Files
Arquivotheca.Solaris-2.5/cmd/backup/dump/dumpdatabase.c
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

1017 lines
25 KiB
C
Executable File

/*
* Copyright (c) 1991 by Sun Microsystems, Inc.
*/
#pragma ident "@(#)dumpdatabase.c 1.58 94/08/10 SMI"
#include "dump.h"
#include <config.h>
#include <database/batchfile.h>
#include <database/header.h>
#include <database/dnode.h>
#if defined(USG) && defined(MAXNAMLEN)
#undef MAXNAMLEN /* yecch */
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <unistd.h>
#include <dirent.h>
/* "dump." + 5 char PID + "." + 8 char hostid + "." + mmddhhmmss + \0 */
#define DBFILENAMELEN 31
#ifdef DEBUG
#define DBFILEMODE 0644
#else
#define DBFILEMODE 0600
#endif
/*
* For error processing...
*/
enum dbfop {
dbf_create = 0,
dbf_open = 1,
dbf_write = 2,
dbf_read = 3,
dbf_seek = 4,
dbf_trunc = 5
};
#define SENDOPER 1
#define DONTSEND 0
/*
* This structure contains the information
* necessary to checkpoint/restart the
* database data file.
*/
struct db_chkpt {
ino_t db_lastinumber; /* current inode number */
struct dnode db_lastdnode; /* current dnode data */
off_t db_written; /* symlink bytes written */
off_t db_offset; /* offset within data file */
struct bu_header db_hdr; /* file header */
struct bu_tape db_tapelist[1]; /* tapes; can be longer */
};
static struct db_chkpt *dbchkpt;
static struct bu_tape *tapelist[TP_NINDIR]; /* tape descriptor list */
static struct bu_header filehdr; /* temp file header record */
static ino_t lastino; /* last inode number */
static struct dnode dn; /* current dnode */
static int written; /* symlink bytes written */
static char *tmpdir; /* where tmp file goes */
static u_long dirsiz; /* number of TP_BSIZE blocks needed for dirs */
static u_long files; /* number of files in this dumpset */
#ifdef __STDC__
static void dbferror(enum dbfop, int);
static int iscomplete(int);
static int isdbtmp(char *);
static void recover_mode(void);
static void updatedump(int);
static void writetapes(int);
#ifdef USG
static long gethostid(void);
#endif
#else
static void dbferror();
static int iscomplete();
static int isdbtmp();
static void recover_mode();
static void updatedump();
static void writetapes();
#endif
static void
#ifdef __STDC__
dbferror(operation, dosend)
#else
dbferror()
#endif
enum dbfop operation;
int dosend; /* msg goes to operator msg system if == 1 */
{
char *text;
int save_errno = errno;
int badop = 0;
switch ((int)operation) {
case dbf_create:
text = gettext(
"Cannot create database data file `%s': %s\n");
break;
case dbf_open:
text = gettext(
"Cannot open database data file `%s': %s\n");
break;
case dbf_write:
text = gettext(
"Cannot write to database data file `%s': %s\n");
break;
case dbf_read:
text = gettext(
"Cannot read from database data file `%s': %s\n");
break;
case dbf_seek:
text = gettext(
"Cannot seek in database data file `%s': %s\n");
break;
case dbf_trunc:
text = gettext(
"Cannot truncate database data file `%s': %s\n");
break;
default:
text = gettext(
"Unknown operation on database data file `%s': `%d'\n");
badop++;
break;
}
if (badop == 0)
msg(text, dbtmpfile, strerror(save_errno));
else
msg(text, dbtmpfile, operation);
if (dosend) {
if (badop == 0)
(void) opermes(LOG_INFO, text,
dbtmpfile, strerror(save_errno));
else
(void) opermes(LOG_INFO, text, dbtmpfile, operation);
}
}
/*
* doupdate -- update a dump database server with info
* from a dump. Called after completing the copying
* associated with a dump, or in recovery mode (dump -R)
* at system boot). To update with data from a specific
* dump, "name" contains the name of the appropriate tmp
* file; in recovery mode, "name" is NULL indicating the
* appropriate temporary directory should be searched
* for dump data files.
*/
void
doupdate(name)
char *name; /* name of tmp file */
{
int maxfds = (int)sysconf(_SC_OPEN_MAX);
register int fd;
#ifndef DEBUG
int childpid;
(void) signal(SIGHUP, SIG_IGN);
/*
* Fork process to do the work
*/
if ((childpid = fork()) < 0) {
msg(gettext("Database update fork fails in parent %d\n"),
getpid());
Exit(X_ABORT);
}
/*
* Parent -- exits immediately, backgrounding child.
*/
if (childpid > 0)
Exit(X_FINOK);
#endif
/*
* Child starts here -- hangs around to do the actual update(s).
*/
if (setsid() < 0)
(void) opermes(LOG_INFO, gettext(
"Warning - %s cannot exit until the database update completes\n"),
myname);
if (name == NULL)
recover_mode(); /* recover mode */
else
updatedump(0); /* leave incomplete files alone */
Exit(X_FINOK);
}
/*
* Recover mode. The current temporary directory
* is searched for valid database tmp files -- any
* that are found are sent to the appropriate
* server.
*/
static void
recover_mode()
{
DIR *dirp;
struct dirent *dp;
char *tmpdir;
(void) gethostname(spcl.c_host, NAMELEN);
while (tmpdir = gettmpdir()) {
(void) opermes(LOG_INFO, gettext(
"Recovering database files from directory `%s'\n"), tmpdir);
if ((dirp = opendir(tmpdir)) == NULL) {
(void) opermes(LOG_INFO, gettext(
"Cannot open temporary directory `%s': %s\n"),
tmpdir, strerror(errno));
continue;
}
if (chdir(tmpdir) < 0) {
(void) opermes(LOG_INFO, gettext(
"Cannot chdir to temporary directory `%s': %s\n"),
tmpdir, strerror(errno));
(void) closedir(dirp);
continue;
}
while (dp = readdir(dirp)) {
if (!isdbtmp(dp->d_name))
continue;
dbtmpfile = dp->d_name;
updatedump(1); /* remove incomplete files */
}
(void) closedir(dirp);
}
(void) opermes(LOG_INFO, gettext("Database file recovery complete\n"));
}
static void
updatedump(remove)
int remove; /* =1 if incomplete files are to be removed */
{
struct dheader dh; /* dump descriptor record */
struct stat sbuf;
int rc, handle, interval;
int success = 0;
int fd;
fd = open(dbtmpfile, O_RDONLY);
if (fd < 0) {
dbferror(dbf_open, SENDOPER);
if (remove)
(void) opermes(LOG_INFO,
gettext("Removing database data file `%s'\n"),
dbtmpfile);
else
(void) opermes(LOG_INFO,
gettext("Ignoring database data file `%s'\n"),
dbtmpfile);
goto done;
}
/*
* Check for file completeness
*/
if (!iscomplete(fd)) {
if (remove)
(void) opermes(LOG_INFO, gettext(
"Removing incomplete database data file `%s'\n"),
dbtmpfile);
else
(void) opermes(LOG_INFO, gettext(
"Ignoring incomplete database data file `%s'\n"),
dbtmpfile);
goto done;
}
if (read(fd, (char *)&filehdr, sizeof (filehdr)) != sizeof (filehdr)) {
dbferror(dbf_read, SENDOPER);
goto done;
}
if (read(fd, (char *)&dh, sizeof (dh)) != sizeof (dh)) {
dbferror(dbf_read, SENDOPER);
goto done;
}
if (strncmp(dh.dh_host, spcl.c_host, NAMELEN))
goto done; /* only do this host's files */
disk = dh.dh_dev;
filesystem = dh.dh_mnt;
if (lseek(fd, 0L, 0) < 0) {
dbferror(dbf_seek, SENDOPER);
goto done;
}
/*
* Check ownership - file must be
* owned by someone with read
* permission for dumped device
*/
if (fstat(fd, &sbuf) < 0 ||
isoperator((int)sbuf.st_uid, (int)sbuf.st_gid) <= 0) {
if (remove)
(void) opermes(LOG_INFO, gettext(
"Removing database data file `%s' owned by non-operator\n"),
dbtmpfile);
else
(void) opermes(LOG_INFO, gettext(
"Ignoring database data file `%s' owned by non-operator\n"),
dbtmpfile);
goto done;
}
interval = 0;
if (setreuid(-1, 0) < 0) {
(void) opermes(LOG_INFO,
gettext("Cannot become super-user: %s\n"), strerror(errno));
Exit(X_ABORT);
}
while (!success) {
interval += 60; /* linear back-off */
handle = update_start(filehdr.dbhost, spcl.c_host);
if (handle < 0) {
/*
* wait for server to come up
*/
(void) opermes(LOG_INFO, gettext(
"Database server `%s' not responding still trying\n"),
filehdr.dbhost);
/*
* Release our hold on the operator daemon
* while we sleep.
*/
msgend();
(void) sleep((unsigned)interval);
msginit();
continue;
}
(void) opermes(LOG_INFO, gettext(
"Transfer of data for `%s:%s' to server `%s' initiated\n"),
spcl.c_host, filesystem, filehdr.dbhost);
if ((rc = update_data(handle, dbtmpfile)) ||
update_process(handle)) {
if (rc == -2) {
(void) opermes(LOG_INFO, gettext(
"Data for `%s:%s' on server `%s' already processed\n"),
spcl.c_host, filesystem, filehdr.dbhost);
break;
}
(void) opermes(LOG_INFO, gettext(
"Transfer of data for `%s:%s' to server `%s' interrupted\n"),
spcl.c_host, filesystem, filehdr.dbhost);
/*
* Release our hold on the operator daemon
* while we sleep.
*/
msgend();
(void) sleep((unsigned)interval);
msginit();
continue;
}
(void) opermes(LOG_INFO, gettext(
"Transfer of data for `%s:%s' to server `%s' completed\n"),
spcl.c_host, filesystem, filehdr.dbhost);
success = 1;
}
(void) setreuid(-1, getuid());
remove = 1;
done:
(void) close(fd);
if (remove)
(void) unlink(dbtmpfile);
}
/*
* A database tmp file is owned by root and looks like:
* bu_header
* ...data...
* bu_header
* Returns true (non-zero) if the file is owned by root
* and is complete, false (zero) otherwise. The file is
* left positioned at its first byte.
*/
static int
iscomplete(fd)
int fd;
{
struct bu_header trailer;
off_t offset = -((long) sizeof (trailer));
if (lseek(fd, (off_t)0, 0) < 0) /* struct at beginning */
return (0);
if (read(fd, (char *)&filehdr, sizeof (filehdr)) != sizeof (filehdr))
return (0);
if (lseek(fd, offset, 2) < 0) /* struct at end */
return (0);
if (read(fd, (char *)&trailer, sizeof (trailer)) != sizeof (trailer))
return (0);
/*
* leave file positioned at beginning of data
*/
if (lseek(fd, 0L, 0) < 0) {
dbferror(dbf_seek, SENDOPER);
dumpabort();
}
if (bcmp((char *)&filehdr, (char *)&trailer, sizeof (filehdr)) == 0)
return (1);
return (0);
}
void
creatdbtmp(time)
time_t time;
{
struct statfs statb;
struct tm *tm;
u_long freesz;
u_long dbsize = /* estimate database tmp file size */
(dirsiz +
howmany((files * sizeof (struct dnode)), TP_BSIZE)) *
(TP_BSIZE/DEV_BSIZE); /* tape blocks to device blocks */
if (tmpdir == NULL) {
/*
* Walk the list of directories in "tmpdir" and find
* the first one suitable for use during this dump.
* The directory cannot reside on a file system that
* will be locked at any time during the dump and if
* database update is to be performed, its free space
* must exceed the estimated amount of space required
* to store the tmp file.
*/
while (tmpdir = gettmpdir()) {
if (!okwrite(tmpdir, 0)) {
msg(gettext(
"Skipping temporary directory `%s': write-locked\n"),
tmpdir);
continue;
}
if (statfs(tmpdir, &statb) < 0) {
msg(gettext(
"Skipping temporary directory `%s': %s\n"),
tmpdir, strerror(errno));
continue;
}
freesz = statb.f_bfree * (statb.f_bsize / DEV_BSIZE);
if (dbsize > freesz)
msg(gettext(
"Skipping temporary directory `%s': \
not enough space on f/s (%lu Kb < %lu Kb)\n"),
tmpdir,
(freesz * DEV_BSIZE) / 1024,
(dbsize * DEV_BSIZE) / 1024);
else
break;
}
if (tmpdir == NULL) {
msg(gettext(
"Cannot find suitable temporary directory\n"));
dumpabort();
}
}
dbtmpfile = xmalloc((DBFILENAMELEN+strlen(tmpdir)+1));
tm = localtime(&time);
(void) sprintf(dbtmpfile, "%s/dump.%05d.%08lx.%02d%02d%02d%02d%02d",
tmpdir,
(int)getpid(),
(u_long)gethostid(),
tm->tm_mon+1, /* XXX */
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
}
int
#ifdef __STDC__
initdbtmp(void)
#else
initdbtmp()
#endif
{
struct dheader dh; /* dump descriptor record */
char path[MAXPATHLEN];
int fd;
fd = open(dbtmpfile, O_RDWR|O_CREAT|O_TRUNC, DBFILEMODE);
if (fd < 0) {
dbferror(dbf_create, DONTSEND);
dumpabort();
}
/*
* The bu_header record will be used to propagate
* name and dnode counts across volumes
*/
(void) bzero((char *)&filehdr, sizeof (filehdr));
(void) getdbserver(filehdr.dbhost, BCHOSTNAMELEN);
errno = ENOSPC;
if (write(fd, (char *)&filehdr, sizeof (filehdr)) != sizeof (filehdr)) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
/*
* Make dump header and write it out
*/
(void) bzero((char *)&dh, sizeof (dh));
(void) strncpy(dh.dh_host, spcl.c_host, NAMELEN);
dh.dh_netid = 0;
(void) strcpy(dh.dh_dev, spcl.c_dev);
if (realpath(filesystem, path))
(void) strncpy(dh.dh_mnt, path, sizeof (dh.dh_mnt));
else
(void) strncpy(dh.dh_mnt, filesystem, sizeof (dh.dh_mnt));
dh.dh_time = spcl.c_date;
dh.dh_prvdumptime = spcl.c_ddate;
dh.dh_level = spcl.c_level;
dh.dh_flags =
(doingactive ? DH_ACTIVE : 0) | (trueinc ? DH_TRUEINC : 0);
#ifdef PARTIAL
if (strstr(spcl.c_filesys, "partial"))
dh.dh_flags |= DH_PARTIAL;
#endif
dh.dh_position = filenum;
dh.dh_ntapes = 0;
errno = ENOSPC;
if (write(fd, (char *)&dh, sizeof (dh)) != sizeof (dh)) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
lastino = ROOTINO;
return (fd);
}
/*
* Shared parent/child routine: Open database file, initialize
* state variables from the checkpointed state data. The state
* data is handed in as a caddr_t because the master treats it
* as opaque data, i.e., a "magic cookie".
*/
int
opendbtmp(state)
caddr_t state;
{
int fd;
off_t offset;
register int i;
fd = open(dbtmpfile, O_RDWR);
if (fd < 0) {
dbferror(dbf_open, DONTSEND);
dumpabort();
}
/*LINTED [state = malloc() and therefore aligned]*/
dbchkpt = (struct db_chkpt *)state;
lastino = dbchkpt->db_lastinumber;
dn = dbchkpt->db_lastdnode;
written = dbchkpt->db_written;
offset = dbchkpt->db_offset;
filehdr = dbchkpt->db_hdr;
for (i = 0; i < filehdr.tape_cnt; i++)
tapelist[i] = &dbchkpt->db_tapelist[i];
if (lseek(fd, offset, 0) < 0) {
dbferror(dbf_seek, DONTSEND);
dumpabort();
}
if (ftruncate(fd, offset) < 0) {
dbferror(dbf_trunc, DONTSEND);
dumpabort();
}
return (fd);
}
/*
* Child routine: Checkpoint database file state
* and place this information at the end of the
* data file, followed by the size of the info.
* Called by archiver, just before exiting.
*/
void
savedbtmp(fd)
int fd;
{
register int i;
size_t nbytes;
nbytes = sizeof (struct bu_tape) * (filehdr.tape_cnt - 1);
nbytes += sizeof (struct db_chkpt);
/*LINTED [rvalue = malloc() and therefore aligned]*/
dbchkpt = (struct db_chkpt *)xmalloc(nbytes);
dbchkpt->db_lastinumber = lastino;
dbchkpt->db_lastdnode = dn;
dbchkpt->db_written = written;
dbchkpt->db_offset = lseek(fd, 0L, 2);
if (dbchkpt->db_offset < 0) {
dbferror(dbf_seek, DONTSEND);
dumpabort();
}
dbchkpt->db_hdr = filehdr;
for (i = 0; i < filehdr.tape_cnt; i++)
(void) bcopy((char *)tapelist[i],
(char *)&dbchkpt->db_tapelist[i], sizeof (struct bu_tape));
errno = ENOSPC;
if (write(fd, (char *)dbchkpt, nbytes) != nbytes) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
errno = ENOSPC;
if (write(fd, (char *)&nbytes, sizeof (nbytes)) != sizeof (nbytes)) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
free(dbchkpt);
dbchkpt = (struct db_chkpt *)0;
(void) close(fd);
}
/*
* Parent routine: retrieve and hold on to
* the state info at the end of the data file.
* This becomes the checkpointed state. Called
* by rollforward, just before forking the next
* volume master.
*/
caddr_t
statdbtmp(fd)
int fd;
{
size_t nbytes;
if (lseek(fd, -sizeof (nbytes), 2) < 0) {
dbferror(dbf_seek, DONTSEND);
dumpabort();
}
if (read(fd, (char *)&nbytes, sizeof (nbytes)) != sizeof (nbytes)) {
dbferror(dbf_read, DONTSEND);
dumpabort();
}
if (lseek(fd, -(nbytes+sizeof (nbytes)), 2) < 0) {
dbferror(dbf_seek, DONTSEND);
dumpabort();
}
/*LINTED [rvalue = malloc() and therefore aligned]*/
dbchkpt = (struct db_chkpt *)xmalloc(nbytes);
if (read(fd, (char *)dbchkpt, nbytes) != nbytes) {
dbferror(dbf_read, DONTSEND);
dumpabort();
}
(void) close(fd);
return ((caddr_t)dbchkpt);
}
void
closedbtmp(fd)
int fd;
{
writetapes(fd);
/*
* Now that we know how many
* records of each type there are,
* re-write the bu_header record
* at the front of the file.
*/
if (lseek(fd, 0L, 0) < 0) { /* struct at beginning */
dbferror(dbf_seek, DONTSEND);
dumpabort();
}
errno = ENOSPC;
if (write(fd, (char *)&filehdr, sizeof (filehdr)) != sizeof (filehdr)) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
/*
* Now write out trailer
* record to mark file complete
*/
if (lseek(fd, 0L, 2) < 0) { /* end of file */
dbferror(dbf_seek, DONTSEND);
dumpabort();
}
errno = ENOSPC;
if (write(fd, (char *)&filehdr, sizeof (filehdr)) != sizeof (filehdr)) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
(void) close(fd);
}
/*
* The bulk of the work involved in translating archive data
* into the form required by the database is done here.
*/
void
writedbtmp(fd, data, isspcl)
int fd;
char *data;
int isspcl;
{
#undef d_ino /* <dirent.h> does this to us */
register int loc;
register struct direct *dp, *ndp;
static long dirsize;
/*LINTED [data = malloc() and therefore aligned]*/
struct s_spcl *spclrec = (struct s_spcl *)data;
struct dinode *ip = &spclrec->c_dinode;
static struct bu_tape *tp;
struct bu_name name;
if (isspcl) {
/*
* special record -- make a tape entry
* if beginning of new volume
*/
if (spclrec->c_type == TS_TAPE) {
filehdr.tape_cnt = spclrec->c_volume - 1;
tapelist[filehdr.tape_cnt] = (struct bu_tape *)
/*LINTED [lvalue = malloc() and therefore aligned]*/
xmalloc(sizeof (struct bu_tape));
tp = tapelist[filehdr.tape_cnt];
(void) bzero((char *)tp, sizeof (struct bu_tape));
(void) strncpy(tp->label, spclrec->c_label, LBLSIZE);
if (spclrec->c_volume == 1)
tp->filenum = filenum;
else
tp->filenum = 1;
filehdr.tape_cnt++;
/*
* multi-volume check
*/
if (spclrec->c_inumber == lastino)
dn.dn_flags |= DN_MULTITAPE;
tp->first_inode = tp->last_inode = lastino;
} else if (spclrec->c_type != TS_ADDR) {
if (lastino > ROOTINO &&
!S_ISLNK(dn.dn_mode) &&
!S_ISDIR(dn.dn_mode) &&
(spclrec->c_inumber != lastino ||
spclrec->c_type == TS_END)) {
/*
* Different inode -- check for file activity,
* write out the dnode, initialize w/new inode
* XXX doesn't work cuz activemap is local
*/
dn.dn_flags |=
BIT(dn.dn_inode, activemap) ? DN_ACTIVE : 0;
errno = ENOSPC;
if (write(fd, (char *)&dn, sizeof (dn))
!= sizeof (dn)) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
filehdr.dnode_cnt++;
}
if (spclrec->c_type == TS_END) {
/*
* stop after 1st TS_END
*/
lastino = ROOTINO;
return;
}
if (!tp->first_inode)
tp->first_inode = spclrec->c_inumber;
/*
* initialize a dnode
*/
dn.dn_inode = lastino = spclrec->c_inumber;
dn.dn_mode = ip->di_mode;
dn.dn_uid = ip->di_suid == UID_LONG ?
ip->di_uid : ip->di_suid;
dn.dn_gid = ip->di_sgid == GID_LONG ?
ip->di_gid : ip->di_sgid;
if ((ip->di_mode & IFMT) == IFCHR ||
(ip->di_mode & IFMT) == IFBLK)
dn.dn_size = ip->di_rdev;
else
dirsize = dn.dn_size = ip->di_size;
if ((ip->di_mode & IFMT) == IFLNK) {
dn.dn_symlink = dn.dn_size+1;
/*
* Symbolic link data -- although dump
* will dump an arbitrarily large link,
* the database can only handle one
* MAXPATHLEN bytes long.
*/
if (dn.dn_symlink > TP_BSIZE)
dn.dn_symlink = TP_BSIZE;
written = 0;
} else
dn.dn_blocks = ip->di_blocks;
dn.dn_atime = ip->di_atime;
dn.dn_mtime = ip->di_mtime;
dn.dn_ctime = ip->di_ctime;
dn.dn_filename = NULL;
dn.dn_parent = NULL;
dn.dn_volid = NULL;
/*
* dn_vol_position is in TP_BSIZE units.
* We set DN_OFFSET to tell recover that the value
* in dn_vol_position is valid.
*/
dn.dn_vol_position =
((u_long) spclrec->c_tapea - blockswritten);
dn.dn_flags = DN_OFFSET;
if (ip->di_shadow != 0)
dn.dn_flags |= DN_ACL;
tp->last_inode = spclrec->c_inumber;
/*
* The dnode for a directory immediately follows
* its name record, so the data file can be created
* in one pass and in one piece. The dnode is
* therefore written before the directory data.
* This has the side effect of disabling the checks
* for multi-volume or active directories. We
* don't care about active directories since we
* are locking the operations we really care about
* (rename, unlink) and it's *highly* unlikely
* that directories will span volumes.
*/
if (S_ISDIR(ip->di_mode) &&
spclrec->c_type != TS_ADDR) {
name.inode = dn.dn_inode;
name.type = DIRECTORY;
name.namelen = 0;
errno = ENOSPC;
if (write(fd, (char *)&name, sizeof (name))
!= sizeof (name)) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
errno = ENOSPC;
if (write(fd, (char *)&dn, sizeof (dn))
!= sizeof (dn)) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
filehdr.name_cnt++;
filehdr.dnode_cnt++;
} else if (S_ISLNK(ip->di_mode) &&
spclrec->c_type != TS_ADDR) {
errno = ENOSPC;
if (write(fd, (char *)&dn, sizeof (dn))
!= sizeof (dn)) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
filehdr.dnode_cnt++;
}
}
} else if ((dn.dn_mode & IFMT) == IFLNK && written <= MAXPATHLEN) {
int nbytes = dn.dn_symlink;
/*
* XXX - a bug in UFS allows the creation of a symlink
* with null bytes, but a length > 0. Work-around this
* by checking here and re-writing the dnode if necessary.
*/
if (data[0] == '\0') {
dn.dn_symlink = nbytes = 1;
errno = ENOSPC;
if (lseek(fd, (off_t)0 - sizeof (dn), SEEK_CUR) < 0 ||
write(fd, (char *)&dn, sizeof (dn))
!= sizeof (dn)) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
}
data[nbytes-1] = '\0';
errno = ENOSPC;
if (write(fd, data, nbytes) != nbytes) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
written += nbytes;
} else {
/*
* Directory data
*/
/*LINTED [data = malloc() and therefore aligned]*/
dp = ndp = (struct direct *)data;
for (loc = 0; loc < TP_BSIZE && dirsize > 0; dp = ndp) {
if (dp->d_reclen == 0) {
break;
}
loc += dp->d_reclen;
dirsize -= dp->d_reclen;
ndp = (struct direct *)((u_long)ndp + dp->d_reclen);
if (dp->d_ino == 0 ||
strcmp(dp->d_name, ".") == 0 ||
strcmp(dp->d_name, "..") == 0)
continue;
name.inode = dp->d_ino;
name.type = 0;
name.namelen = dp->d_namlen+1;
errno = ENOSPC;
if (write(fd, (char *)&name, sizeof (name))
!= sizeof (name)) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
errno = ENOSPC;
if (write(fd, (char *)dp->d_name, (int)name.namelen)
!= name.namelen) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
filehdr.name_cnt++;
}
}
}
/*
* Write the tape descriptor records to the end of
* the database tmp file.
*/
static void
writetapes(fd)
int fd;
{
register int i;
for (i = 0; i < filehdr.tape_cnt; i++) {
errno = ENOSPC;
if (write(fd, (char *)tapelist[i], (int)sizeof (struct bu_tape))
!= sizeof (struct bu_tape)) {
dbferror(dbf_write, DONTSEND);
dumpabort();
}
}
}
/*
* isdbtmp -- if the argument file name is a valid database
* temporary file name return true (1), false (0) otherwise.
* Database temp files have names in the form:
* "dump.xxxxx.yyyyyyyy.mmddhhmmss"
* where the x's are the PID and the y's are the hex digits of the hostid.
*/
static int
isdbtmp(name)
char *name;
{
struct tm tm;
register int i;
int pid;
long id;
register char *cp;
if (strncmp("dump.", name, 5))
return (0);
cp = &name[5];
for (i = 0; i < 5; i++)
if (!isdigit((u_char)*cp++))
return (0);
if (*cp++ != '.')
return (0);
for (i = 0; i < 8; i++)
if (!isxdigit((u_char)*cp++))
return (0);
if (*cp++ != '.')
return (0);
if (sscanf(&name[5], "%5d", &pid) != 1 || pid <= 3 || pid > MAXPID)
return (0);
if (sscanf(&name[11], "%8lx", (u_long *)&id) != 1 || id != gethostid())
return (0);
i = sscanf(cp, "%2d%2d%2d%2d%2d",
&tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
if (i != 5 ||
(tm.tm_mon < 1 || tm.tm_mon) > 12 || /* XXX */
(tm.tm_mday < 1 || tm.tm_mday) > 31 ||
(tm.tm_hour < 0 || tm.tm_hour) > 24 ||
(tm.tm_min < 0 || tm.tm_min) > 59 ||
(tm.tm_sec < 0 || tm.tm_sec) > 59)
return (0);
return (1);
}
/*
* Compute a running estimate on the size of the
* database tmp file. Called from est().
*/
void
dbtmpest(ip, tpblks)
struct dinode *ip;
long tpblks; /* from est() -- number of tape blocks */
{
if (S_ISDIR(ip->di_mode))
dirsiz += tpblks;
files++;
}
#ifdef USG
#define HOSTIDLEN 40
static long
#ifdef __STDC__
gethostid(void)
#else
gethostid()
#endif
{
char name[HOSTIDLEN], *end;
long hostid;
int error;
error = sysinfo(SI_HW_SERIAL, name, HOSTIDLEN);
/*
* error > 0 ==> number of bytes to hold name
* and is discarded since gethostid only
* cares if it succeeded or failed
*/
if (error == -1)
return (-1);
else {
hostid = strtoul(name, &end, 10);
if (hostid == 0 && end == name) {
return (-1);
}
return (hostid);
}
}
#endif