/*
* Copyright 1996-1998, 2002-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "dump.h"
#ifndef LOCK_EX
static struct flock fl;
#define flock(fd, flag) (fl.l_type = (flag), fcntl(fd, F_SETLKW, &fl))
#define LOCK_EX F_WRLCK
#define LOCK_SH F_RDLCK
#define LOCK_UN F_UNLCK
#endif
/*
* Print a date. A date of 0 is the beginning of time (the "epoch").
* If the 2nd argument is non-zero, it is ok to format the date in
* locale-specific form, otherwise we use ctime. We must use ctime
* for dates such as those in the dumpdates file, which must be
* locale-independent.
*/
char *
prdate(d)
time_t d;
{
static char buf[256];
struct tm *tm;
char *p;
if (d == 0)
return (gettext("the epoch"));
tm = localtime(&d);
if (strftime(buf, sizeof (buf), "%c", tm) != 0) {
p = buf;
} else {
/* Wouldn't fit in buf, fall back */
p = ctime(&d);
p[24] = '\0'; /* lose trailing newline */
}
return (p);
}
struct idates **idatev = 0;
size_t nidates = 0;
static int idates_in = 0; /* we have read the increment file */
static int recno;
#ifdef __STDC__
static void readitimes(FILE *);
static void recout(FILE *, struct idates *);
static int getrecord(FILE *, struct idates *);
static int makeidate(struct idates *, char *);
#else
static void readitimes();
static void recout();
static int getrecord();
static int makeidate();
#endif
void
#ifdef __STDC__
inititimes(void)
#else
inititimes()
#endif
{
FILE *df;
int saverr;
if (idates_in)
return;
if (increm == NULL || *increm == '\0') {
msg(gettext("inititimes: No dump record file name defined\n"));
dumpabort();
/*NOTREACHED*/
}
/*
* No need to secure this, as increm is hard-coded to NINCREM,
* and that file is in /etc. If random people have write-permission
* there, then there are more problems than any degree of paranoia
* on our part can fix.
*/
if ((df = fopen(increm, "r")) == NULL) {
saverr = errno;
if (errno == ENOENT)
msg(gettext(
"Warning - dump record file `%s' does not exist\n"),
increm);
else {
msg(gettext("Cannot open dump record file `%s': %s\n"),
increm, strerror(saverr));
dumpabort();
/*NOTREACHED*/
}
return;
}
if (uflag && access(increm, W_OK) < 0) {
msg(gettext("Cannot access dump record file `%s' for update\n"),
increm);
dumpabort();
/*NOTREACHED*/
}
(void) flock(fileno(df), LOCK_SH);
readitimes(df);
(void) fclose(df);
}
static void
readitimes(df)
FILE *df;
{
struct idates *idp;
recno = 0;
for (;;) {
idp = (struct idates *)xcalloc(1, sizeof (*idp));
if (getrecord(df, idp) < 0) {
free((char *)idp);
break;
}
nidates++;
idatev = (struct idates **)xrealloc((void *)idatev,
nidates * (size_t)sizeof (*idatev));
idatev[nidates - 1] = idp;
}
/* LINTED: assigned value is used in inititimes */
idates_in = 1;
}
void
#ifdef __STDC__
getitime(void)
#else
getitime()
#endif
{
struct idates *ip;
int i;
char *fname;
/*
* if an alternate name was specified via the N flag, use it instead
* of the disk name.
*/
if (dname != NULL)
fname = dname;
else
fname = disk;
#ifdef FDEBUG
/* XGETTEXT: #ifdef FDEBUG only */
msg(gettext("Looking for name %s in increm = %s for delta = %c\n"),
fname, increm, (uchar_t)incno);
#endif
spcl.c_ddate = 0;
lastincno = '0';
inititimes();
if (idatev == 0)
return;
/*
* Go find the entry with the same name for a lower increment
* and older date
*/
ITITERATE(i, ip) {
if (strncmp(fname, ip->id_name, sizeof (ip->id_name)) != 0)
continue;
if (ip->id_incno >= incno)
continue;
if (ip->id_ddate <= spcl.c_ddate)
continue;
spcl.c_ddate = ip->id_ddate;
lastincno = ip->id_incno;
}
}
void
#ifdef __STDC__
putitime(void)
#else
putitime()
#endif
{
FILE *df;
struct idates *itwalk;
int i;
int fd, saverr;
char *fname;
if (uflag == 0)
return;
if ((df = safe_fopen(increm, "r+", 0664)) == (FILE *)NULL) {
msg("%s: %s\n", increm, strerror(errno));
(void) unlink(increm);
dumpabort();
/*NOTREACHED*/
}
fd = fileno(df);
(void) flock(fd, LOCK_EX);
/*
* if an alternate name was specified via the N flag, use it instead
* of the disk name.
*/
if (dname != NULL)
fname = dname;
else
fname = disk;
if (idatev != 0) {
for (i = 0; i < nidates && idatev[i] != 0; i++)
free((char *)idatev[i]);
free((char *)idatev);
}
idatev = 0;
nidates = 0;
readitimes(df);
if (fseek(df, 0L, 0) < 0) { /* rewind() was redefined in dumptape.c */
saverr = errno;
msg(gettext("%s: %s error:\n"),
increm, "fseek", strerror(saverr));
dumpabort();
/*NOTREACHED*/
}
spcl.c_ddate = 0;
/* LINTED: won't dereference idatev if it is NULL (see readitimes) */
ITITERATE(i, itwalk) {
if (strncmp(fname, itwalk->id_name,
sizeof (itwalk->id_name)) != 0)
continue;
if (itwalk->id_incno != incno)
continue;
goto found;
}
/*
* Add one more entry to idatev
*/
nidates++;
idatev = (struct idates **)xrealloc((void *)idatev,
nidates * (size_t)sizeof (struct idates *));
itwalk = idatev[nidates - 1] =
(struct idates *)xcalloc(1, sizeof (*itwalk));
found:
(void) strncpy(itwalk->id_name, fname, sizeof (itwalk->id_name));
itwalk->id_name[sizeof (itwalk->id_name) - 1] = '\0';
itwalk->id_incno = incno;
itwalk->id_ddate = spcl.c_date;
ITITERATE(i, itwalk) {
recout(df, itwalk);
}
if (ftruncate64(fd, ftello64(df))) {
saverr = errno;
msg(gettext("%s: %s error:\n"),
increm, "ftruncate64", strerror(saverr));
dumpabort();
/*NOTREACHED*/
}
(void) fclose(df);
msg(gettext("Level %c dump on %s\n"),
(uchar_t)incno, prdate(spcl.c_date));
}
static void
recout(file, what)
FILE *file;
struct idates *what;
{
time_t ddate = what->id_ddate;
/* must use ctime, so we can later use unctime() */
(void) fprintf(file, DUMPOUTFMT,
what->id_name,
(uchar_t)what->id_incno,
ctime(&ddate));
}
static int
getrecord(df, idatep)
FILE *df;
struct idates *idatep;
{
char buf[BUFSIZ];
if ((fgets(buf, BUFSIZ, df)) != buf)
return (-1);
recno++;
if (makeidate(idatep, buf) < 0) {
msg(gettext(
"Malformed entry in dump record file `%s', line %d\n"),
increm, recno);
if (strcmp(increm, NINCREM)) {
msg(gettext("`%s' not a dump record file\n"), increm);
dumpabort();
/*NOTREACHED*/
}
return (-1);
}
#ifdef FDEBUG
msg("getrecord: %s %c %s\n",
idatep->id_name,
(uchar_t)idatep->id_incno,
prdate(idatep->id_ddate));
#endif
return (0);
}
static int
makeidate(ip, buf)
struct idates *ip;
char *buf;
{
char un_buf[128]; /* size must be >= second one in DUMPINFMT */
/*
* MAXNAMLEN has different values in dirent.h and ufs_fsdir.h,
* and we need to ensure that the length in DUMPINFMT matches
* what we allow for. Can't just use MAXNAMLEN in the test,
* because there's no convenient way to substitute it into
* DUMPINFMT.
* XXX There's got to be a better way.
*/
/*LINTED [assertion always true]*/
assert(sizeof (ip->id_name) == (255 + 3));
if (sscanf(buf, DUMPINFMT, ip->id_name, &ip->id_incno, un_buf) != 3)
return (-1);
/* LINTED casting from 64-bit to 32-bit time */
ip->id_ddate = (time32_t)unctime(un_buf);
if (ip->id_ddate < 0)
return (-1);
return (0);
}
/*
* This is an estimation of the number of tp_bsize blocks in the file.
* It estimates the number of blocks in files with holes by assuming
* that all of the blocks accounted for by di_blocks are data blocks
* (when some of the blocks are usually used for indirect pointers);
* hence the estimate may be high.
*/
void
est(ip)
struct dinode *ip;
{
u_offset_t s, t;
/*
* ip->di_size is the size of the file in bytes.
* ip->di_blocks stores the number of sectors actually in the file.
* If there are more sectors than the size would indicate, this just
* means that there are indirect blocks in the file or unused
* sectors in the last file block; we can safely ignore these
* (s = t below).
* If the file is bigger than the number of sectors would indicate,
* then the file has holes in it. In this case we must use the
* block count to estimate the number of data blocks used, but
* we use the actual size for estimating the number of indirect
* dump blocks (t vs. s in the indirect block calculation).
*/
o_esize++;
s = (unsigned)(ip->di_blocks) / (unsigned)(tp_bsize / DEV_BSIZE);
/* LINTED: spurious complaint about sign-extending 32 to 64 bits */
t = d_howmany(ip->di_size, (unsigned)tp_bsize);
if (s > t)
s = t;
if (ip->di_size > (u_offset_t)((unsigned)(sblock->fs_bsize) * NDADDR)) {
/* calculate the number of indirect blocks on the dump tape */
/* LINTED: spurious complaint sign-extending 32 to 64 bits */
s += d_howmany(t -
(unsigned)(NDADDR * sblock->fs_bsize / tp_bsize),
(unsigned)TP_NINDIR);
}
f_esize += s;
}
/*ARGSUSED*/
void
bmapest(map)
uchar_t *map;
{
o_esize++;
/* LINTED: spurious complaint sign-extending 32 to 64 bits */
f_esize += d_howmany(msiz * sizeof (map[0]), (unsigned)tp_bsize);
}
/*
* Check to see if what we are trying to dump is a fs snapshot
* If so, we can use the snapshot's create time to populate
* the dumpdates file, instead of the time of the dump.
*/
time32_t
is_fssnap_dump(char *disk)
{
struct stat st;
char *last;
int snapnum;
kstat_ctl_t *kslib;
kstat_t *ksnum;
kstat_named_t *numval;
last = basename(disk);
if ((strstr(disk, SNAP_NAME) == NULL) || (stat(disk, &st) == -1) ||
(isdigit(last[0]) == 0))
return (0);
snapnum = atoi(last);
if ((kslib = kstat_open()) == NULL)
return (0);
ksnum = kstat_lookup(kslib, SNAP_NAME, snapnum, FSSNAP_KSTAT_NUM);
if (ksnum == NULL) {
(void) kstat_close(kslib);
return (0);
}
if (kstat_read(kslib, ksnum, NULL) == -1) {
(void) kstat_close(kslib);
return (0);
}
numval = kstat_data_lookup(ksnum, FSSNAP_KSTAT_NUM_CREATETIME);
if (numval == NULL) {
(void) kstat_close(kslib);
return (0);
}
(void) kstat_close(kslib);
/* LINTED casting from long to 32-bit time */
return (time32_t)(numval->value.l & INT_MAX);
}