1701 lines
39 KiB
C
Executable File
1701 lines
39 KiB
C
Executable File
#ident "@(#)batch_update.c 1.30 93/07/07"
|
|
|
|
/*
|
|
* Copyright (c) 1990,1991,1992 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#include "defs.h"
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/vfs.h>
|
|
#include <sys/resource.h>
|
|
#include "dboper.h"
|
|
|
|
/*
|
|
* internal error codes
|
|
*/
|
|
#define ERR_GENERIC -1
|
|
#define ERR_EMPTYFILE -2
|
|
|
|
static int hashsize;
|
|
#define HASH(a) (a & (hashsize-1))
|
|
static struct bucket {
|
|
struct node *nxt;
|
|
} *buckets;
|
|
|
|
#define ROOT_INODE 2
|
|
|
|
struct node {
|
|
struct dnode *dn;
|
|
char *name;
|
|
int namelen;
|
|
int flags;
|
|
u_long dnode_num;
|
|
struct node *sibling;
|
|
struct node *child;
|
|
struct node *hashnxt;
|
|
};
|
|
/* node flags */
|
|
#define NODE_ISDIR 1
|
|
|
|
#define STDIO_BUFSIZE 32768
|
|
static char *stdiobuf1;
|
|
static char *stdiobuf2;
|
|
|
|
static struct node *root;
|
|
static struct node *alloc_node();
|
|
|
|
static struct dheader dumphead;
|
|
static struct tape_data {
|
|
struct bu_tape bu_tape;
|
|
u_long maxdirinode;
|
|
struct tape_data *nxt;
|
|
} *tapehead;
|
|
static struct tape_data *alloc_tape();
|
|
|
|
#define NULLNODE (struct node *)0
|
|
|
|
static struct dnode *alloc_dnode();
|
|
static char *alloc_name();
|
|
|
|
static int ndnodes;
|
|
static int pathoffset;
|
|
static int linkoffset;
|
|
|
|
#ifdef __STDC__
|
|
static void handler(int);
|
|
static int empty_update(const char *, u_long);
|
|
static int build_updatefile(const char *, char *, u_long);
|
|
static int buildtree(FILE *, FILE *);
|
|
static int getdirs(FILE *, int, int *);
|
|
static int order_tree(struct node *);
|
|
static struct node *hash_lookup(u_long);
|
|
static struct node *link_lookup(u_long);
|
|
static struct node *hash_exchange(struct node *);
|
|
static int add_dnodes(FILE *, int, FILE *);
|
|
static void makelink(struct dnode *, const char *, FILE *);
|
|
static int process_tapes(FILE *, int, int);
|
|
static void locate_tape(struct dnode *);
|
|
static int build_root(const char *);
|
|
static void dump_dnodes(struct node *, u_long, FILE *, FILE *);
|
|
static int output_dumpheader(char *, const char *, u_long);
|
|
static void dump_dir_and_instance(struct node *, u_long, u_long);
|
|
static int update_activetapes(const char *, u_long);
|
|
static int getmemory(long filesize, const struct bu_header *);
|
|
static void freemem1(void);
|
|
static void freemem2(void);
|
|
static void alloc_iobuffers(void);
|
|
static struct dnode *alloc_dnode(void);
|
|
static struct node *alloc_node(void);
|
|
static struct tape_data *alloc_tape(void);
|
|
static char *alloc_name(int);
|
|
static void start_msg(void);
|
|
static void success_msg(const char *);
|
|
static void update_stats(const char *);
|
|
static void fail_msg(void);
|
|
#else
|
|
static void handler();
|
|
static int empty_update();
|
|
static int build_updatefile();
|
|
static int buildtree();
|
|
static int getdirs();
|
|
static int order_tree();
|
|
static struct node *hash_lookup();
|
|
static struct node *link_lookup();
|
|
static struct node *hash_exchange();
|
|
static int add_dnodes();
|
|
static void makelink();
|
|
static int process_tapes();
|
|
static void locate_tape();
|
|
static int build_root();
|
|
static void dump_dnodes();
|
|
static int output_dumpheader();
|
|
static void dump_dir_and_instance();
|
|
static int update_activetapes();
|
|
static int getmemory();
|
|
static void freemem1();
|
|
static void freemem2();
|
|
static void alloc_iobuffers();
|
|
static struct dnode *alloc_dnode();
|
|
static struct node *alloc_node();
|
|
static struct tape_data *alloc_tape();
|
|
static char *alloc_name();
|
|
static void start_msg();
|
|
static void success_msg();
|
|
static void update_stats();
|
|
static void fail_msg();
|
|
#endif
|
|
|
|
/*ARGSUSED*/
|
|
static void
|
|
handler(sig)
|
|
int sig;
|
|
{
|
|
fail_msg();
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef __STDC__
|
|
batch_update(const char *host,
|
|
const char *file)
|
|
#else
|
|
batch_update(host, file)
|
|
char *host;
|
|
char *file;
|
|
#endif
|
|
{
|
|
FILE *fp, *pcfp, *lnkfp;
|
|
char path[256];
|
|
u_long dumpid;
|
|
char dnode_name[256], pcomp_name[256], header_name[256], lnk_name[256],
|
|
updatefile[256], newname[256];
|
|
struct sigvec vec;
|
|
struct stat statbuf;
|
|
int rc;
|
|
|
|
startupreg(file);
|
|
vec.sv_handler = handler;
|
|
#ifdef USG
|
|
vec.sa_flags = SA_RESTART;
|
|
(void) sigemptyset(&vec.sa_mask);
|
|
#else
|
|
vec.sv_flags = 0;
|
|
vec.sv_mask = 0;
|
|
#endif
|
|
(void) sigvec(SIGHUP, &vec, (struct sigvec *)NULL);
|
|
(void) sigvec(SIGINT, &vec, (struct sigvec *)NULL);
|
|
(void) sigvec(SIGQUIT, &vec, (struct sigvec *)NULL);
|
|
(void) sigvec(SIGBUS, &vec, (struct sigvec *)NULL);
|
|
(void) sigvec(SIGSEGV, &vec, (struct sigvec *)NULL);
|
|
(void) sigvec(SIGTERM, &vec, (struct sigvec *)NULL);
|
|
|
|
dumphead.dh_host[0] = 0;
|
|
root = NULLNODE;
|
|
tapehead = (struct tape_data *)0;
|
|
linkoffset = pathoffset = ndnodes = 0;
|
|
(void) sprintf(path, "%s/%s", host, UPDATE_FILE);
|
|
(void) strcat(path, ".%lu");
|
|
if (sscanf(file, path, &dumpid) != 1) {
|
|
(void) fprintf(stderr, gettext(
|
|
"unknown update file name format `%s'\n"), file);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* open the file created by dump. The structure of this file
|
|
* is defined in "batchfile.h"
|
|
*/
|
|
if ((fp = fopen(file, "r")) == NULL) {
|
|
perror(file);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
alloc_iobuffers();
|
|
(void) setvbuf(fp, stdiobuf1, _IOFBF, STDIO_BUFSIZE);
|
|
|
|
/*
|
|
* build the file that indicates an update is in progress for
|
|
* the given dumpid. This causes the startup code to retry this
|
|
* update if we crash somewhere in the middle of it.
|
|
*/
|
|
if (build_updatefile(host, updatefile, dumpid)) {
|
|
(void) fprintf(stderr, gettext("cannot create update file\n"));
|
|
(void) fclose(fp);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
|
|
(void) sprintf(lnk_name,
|
|
"%s/%s%s.%lu", host, TEMP_PREFIX, LINKFILE, dumpid);
|
|
if ((lnkfp = fopen(lnk_name, "w")) == NULL) {
|
|
(void) fprintf(stderr,
|
|
gettext("cannot open link file `%s'\n"), lnk_name);
|
|
(void) fclose(fp);
|
|
fail_msg();
|
|
return (-1);
|
|
} else if (fchmod(fileno(lnkfp), 0600) == -1) {
|
|
(void) fprintf(stderr, gettext(
|
|
"cannot chmod link file `%s'\n"), lnk_name);
|
|
(void) fclose(fp);
|
|
(void) fclose(lnkfp);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
|
|
if (rc = buildtree(fp, lnkfp)) {
|
|
(void) fclose(fp);
|
|
(void) fclose(lnkfp);
|
|
(void) unlink(file);
|
|
(void) unlink(lnk_name);
|
|
if (rc == ERR_EMPTYFILE) {
|
|
return (empty_update(host, dumpid));
|
|
} else {
|
|
(void) fprintf(stderr, gettext("%s error\n"),
|
|
"build_tree");
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
}
|
|
(void) fclose(fp);
|
|
if (fflush(lnkfp) != 0) {
|
|
(void) fprintf(stderr, gettext("link file %s failure\n"),
|
|
"fflush");
|
|
(void) fclose(lnkfp);
|
|
(void) unlink(lnk_name);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
(void) fsync(fileno(lnkfp));
|
|
if (fstat(fileno(lnkfp), &statbuf) == -1 || statbuf.st_size == 0) {
|
|
(void) unlink(lnk_name);
|
|
lnk_name[0] = 0;
|
|
}
|
|
(void) fclose(lnkfp);
|
|
|
|
if (root == NULLNODE) {
|
|
(void) fprintf(stderr, gettext("input file `%s' is empty!\n"),
|
|
file);
|
|
(void) unlink(lnk_name);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* produce dnode and pathcomponent files from our in-memory
|
|
* representation of the dump file.
|
|
*/
|
|
(void) sprintf(dnode_name,
|
|
"%s/%s%s.%lu", host, TEMP_PREFIX, DNODEFILE, dumpid);
|
|
if ((fp = fopen(dnode_name, "w")) == NULL) {
|
|
(void) fprintf(stderr, gettext("cannot create `%s'\n"),
|
|
dnode_name);
|
|
(void) unlink(lnk_name);
|
|
fail_msg();
|
|
return (-1);
|
|
} else if (fchmod(fileno(fp), 0600) == -1) {
|
|
perror("fchmod");
|
|
(void) fprintf(stderr, gettext("cannot chmod `%s'\n"),
|
|
dnode_name);
|
|
(void) fclose(fp);
|
|
(void) unlink(lnk_name);
|
|
(void) unlink(dnode_name);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
(void) setvbuf(fp, stdiobuf1, _IOFBF, STDIO_BUFSIZE);
|
|
|
|
(void) sprintf(pcomp_name,
|
|
"%s/%s%s.%lu", host, TEMP_PREFIX, PATHFILE, dumpid);
|
|
if ((pcfp = fopen(pcomp_name, "w")) == NULL) {
|
|
(void) fprintf(stderr, gettext("cannot create `%s'\n"),
|
|
pcomp_name);
|
|
(void) fclose(fp);
|
|
(void) unlink(dnode_name);
|
|
(void) unlink(lnk_name);
|
|
fail_msg();
|
|
return (-1);
|
|
} else if (fchmod(fileno(pcfp), 0600) == -1) {
|
|
perror("fchmod");
|
|
(void) fprintf(stderr, gettext("cannot chmod `%s'\n"),
|
|
pcomp_name);
|
|
(void) fclose(fp);
|
|
(void) fclose(pcfp);
|
|
(void) unlink(lnk_name);
|
|
(void) unlink(dnode_name);
|
|
(void) unlink(pcomp_name);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
(void) setvbuf(pcfp, stdiobuf2, _IOFBF, STDIO_BUFSIZE);
|
|
|
|
dump_dnodes(root, NONEXISTENT_BLOCK, fp, pcfp);
|
|
|
|
/*
|
|
* make sure that all dnode and pathcomponent data is flushed
|
|
* to disk. Note that these files continue to have temporary
|
|
* names until the `dir' and `instance' transactions have
|
|
* all been processed.
|
|
*/
|
|
if (fflush(fp) != 0 || fflush(pcfp) != 0) {
|
|
(void) fprintf(stderr, gettext("cannot %s %s\n"),
|
|
"fflush", "dnode/pathcomponent");
|
|
(void) fclose(fp);
|
|
(void) fclose(pcfp);
|
|
(void) unlink(lnk_name);
|
|
(void) unlink(dnode_name);
|
|
(void) unlink(pcomp_name);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
(void) fsync(fileno(fp));
|
|
(void) fclose(fp);
|
|
(void) fsync(fileno(pcfp));
|
|
(void) fclose(pcfp);
|
|
|
|
freemem1();
|
|
|
|
/*
|
|
* create a dump header file
|
|
*/
|
|
if (output_dumpheader(header_name, host, dumpid)) {
|
|
(void) unlink(lnk_name);
|
|
(void) unlink(dnode_name);
|
|
(void) unlink(pcomp_name);
|
|
(void) unlink(header_name);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* now traverse our in-memory representation once more to
|
|
* update the directory and instance files.
|
|
*/
|
|
if (instance_open(host)) {
|
|
(void) fprintf(stderr,
|
|
gettext("cannot open instance for `%s'\n"), host);
|
|
(void) unlink(lnk_name);
|
|
(void) unlink(dnode_name);
|
|
(void) unlink(pcomp_name);
|
|
(void) unlink(header_name);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
if (dir_open(host)) {
|
|
(void) fprintf(stderr,
|
|
gettext("cannot open dir for `%s'\n"), host);
|
|
(void) unlink(lnk_name);
|
|
(void) unlink(dnode_name);
|
|
(void) unlink(pcomp_name);
|
|
(void) unlink(header_name);
|
|
instance_close(host);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
dump_dir_and_instance(root, DIR_ROOTBLK, dumpid);
|
|
instance_close(host);
|
|
dir_close(host);
|
|
|
|
freemem2();
|
|
|
|
(void) update_activetapes(host, dumpid);
|
|
|
|
/*
|
|
* Here we have safely acquired all the data. Now we can
|
|
* rename the `update.inprogress' file, remove the original
|
|
* update data file, rename temporary files and
|
|
* apply transaction files.
|
|
*/
|
|
(void) sprintf(newname, "%s/%s.%lu", host, UPDATE_DONE, dumpid);
|
|
if (rename(updatefile, newname) == -1) {
|
|
perror("rename");
|
|
(void) fprintf(stderr, gettext("cannot rename `%s'\n"),
|
|
updatefile);
|
|
/* XXX: what do we do now??? */
|
|
}
|
|
startupreg(NULL);
|
|
(void) strcpy(updatefile, newname);
|
|
(void) sprintf(newname, "%s/%s.%lu", host, DNODEFILE, dumpid);
|
|
if (rename(dnode_name, newname) == -1) {
|
|
perror("rename");
|
|
(void) fprintf(stderr, gettext("cannot rename `%s'\n"),
|
|
dnode_name);
|
|
/* XXX ??? */
|
|
}
|
|
(void) sprintf(newname, "%s/%s.%lu", host, PATHFILE, dumpid);
|
|
if (rename(pcomp_name, newname) == -1) {
|
|
perror("rename");
|
|
(void) fprintf(stderr, gettext("cannot rename `%s'\n"),
|
|
pcomp_name);
|
|
/* XXX ??? */
|
|
}
|
|
(void) sprintf(newname, "%s/%s.%lu", host, HEADERFILE, dumpid);
|
|
if (rename(header_name, newname) == -1) {
|
|
perror("rename");
|
|
(void) fprintf(stderr, gettext("cannot rename `%s'\n"),
|
|
header_name);
|
|
}
|
|
if (lnk_name[0]) {
|
|
(void) sprintf(newname, "%s/%s.%lu", host, LINKFILE, dumpid);
|
|
if (rename(lnk_name, newname) == -1) {
|
|
perror("rename");
|
|
(void) fprintf(stderr, gettext("cannot rename `%s'\n"),
|
|
lnk_name);
|
|
}
|
|
}
|
|
if (unlink(file) == -1) {
|
|
perror("unlink");
|
|
(void) fprintf(stderr,
|
|
gettext("cannot remove update file `%s'\n"), file);
|
|
}
|
|
if (dir_trans(host) != 0) {
|
|
(void) fprintf(stderr, gettext("%s failure\n"), "dir_trans");
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
if (instance_trans(host) != 0) {
|
|
(void) fprintf(stderr, gettext("%s failure\n"),
|
|
"instance_trans");
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
if (tape_trans(host) != 0) {
|
|
(void) fprintf(stderr, gettext("%s failure\n"), "tape_trans");
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
if (unlink(updatefile) == -1) {
|
|
perror("unlink");
|
|
(void) fprintf(stderr, gettext("cannot remove `%s'\n"),
|
|
updatefile);
|
|
}
|
|
success_msg(host);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
#ifdef __STDC__
|
|
empty_update(const char *host,
|
|
u_long dumpid)
|
|
#else
|
|
empty_update(host, dumpid)
|
|
char *host;
|
|
u_long dumpid;
|
|
#endif
|
|
{
|
|
char header_name[256], update_name[256], newname[256];
|
|
|
|
if (output_dumpheader(header_name, host, dumpid)) {
|
|
(void) fprintf(stderr, gettext("empty dump/header write\n"));
|
|
(void) unlink(header_name);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
if (build_updatefile(host, update_name, dumpid)) {
|
|
(void) unlink(header_name);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
if (update_activetapes(host, dumpid)) {
|
|
(void) unlink(header_name);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
(void) sprintf(newname, "%s/%s.%lu", host, UPDATE_DONE, dumpid);
|
|
if (rename(update_name, newname) == -1) {
|
|
perror("rename");
|
|
(void) unlink(header_name);
|
|
(void) unlink(update_name);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
(void) strcpy(update_name, newname);
|
|
(void) sprintf(newname, "%s/%s.%lu", host, HEADERFILE, dumpid);
|
|
if (rename(header_name, newname) == -1) {
|
|
perror("rename");
|
|
(void) unlink(header_name);
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
if (tape_trans(host) != 0) {
|
|
(void) fprintf(stderr, gettext("%s failure\n"), "tape_trans");
|
|
fail_msg();
|
|
return (-1);
|
|
}
|
|
(void) unlink(update_name);
|
|
success_msg(host);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
#ifdef __STDC__
|
|
build_updatefile(const char *host,
|
|
char *ufile,
|
|
u_long dumpid)
|
|
#else
|
|
build_updatefile(host, ufile, dumpid)
|
|
char *host;
|
|
char *ufile;
|
|
u_long dumpid;
|
|
#endif
|
|
{
|
|
char name[256];
|
|
struct stat stbuf;
|
|
int filesizes[3];
|
|
int fd;
|
|
|
|
(void) sprintf(name, "%s/%s", host, DIRFILE);
|
|
if (stat(name, &stbuf) == -1) {
|
|
stbuf.st_size = 0;
|
|
}
|
|
filesizes[0] = stbuf.st_size;
|
|
(void) sprintf(name, "%s/%s", host, INSTANCEFILE);
|
|
if (stat(name, &stbuf) == -1) {
|
|
stbuf.st_size = 0;
|
|
}
|
|
filesizes[1] = stbuf.st_size;
|
|
(void) sprintf(name, "%s", TAPEFILE);
|
|
if (stat(name, &stbuf) == -1) {
|
|
stbuf.st_size = 0;
|
|
}
|
|
filesizes[2] = stbuf.st_size;
|
|
(void) sprintf(ufile, "%s/%s.%lu", host, UPDATE_INPROGRESS, dumpid);
|
|
if ((fd = open(ufile, O_RDWR|O_CREAT, 0600)) == -1) {
|
|
perror("open");
|
|
(void) fprintf(stderr, gettext("cannot create `%s'\n"), ufile);
|
|
return (-1);
|
|
}
|
|
if (write(fd, (char *)filesizes, 3*sizeof (int)) != 3*sizeof (int)) {
|
|
perror("write");
|
|
(void) fprintf(stderr, gettext("cannot write file sizes\n"));
|
|
(void) close(fd);
|
|
(void) unlink(ufile);
|
|
return (-1);
|
|
}
|
|
(void) fsync(fd);
|
|
(void) close(fd);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
buildtree(fp, lnkfp)
|
|
FILE *fp;
|
|
FILE *lnkfp;
|
|
{
|
|
struct bu_header buh, buh1;
|
|
struct stat stbuf;
|
|
int dnodecnt;
|
|
int rc = 0;
|
|
|
|
if (fstat(fileno(fp), &stbuf) == -1) {
|
|
perror("fstat");
|
|
(void) fprintf(stderr,
|
|
gettext("%s: %s error\n"), "buildtree", "fstat");
|
|
return (-1);
|
|
}
|
|
|
|
|
|
/* read the update file header */
|
|
if (fread((char *)&buh, sizeof (struct bu_header), 1, fp) != 1) {
|
|
(void) fprintf(stderr, gettext("%s: cannot read file header\n"),
|
|
"buildtree");
|
|
return (-1);
|
|
}
|
|
|
|
if (getmemory(stbuf.st_size, &buh))
|
|
return (-1);
|
|
|
|
/* read the dump header */
|
|
if (fread((char *)&dumphead, sizeof (struct dheader), 1, fp) != 1) {
|
|
(void) fprintf(stderr,
|
|
gettext("%s: cannot read dump header\n"), "buildtree");
|
|
return (-1);
|
|
}
|
|
dumphead.dh_ntapes = buh.tape_cnt;
|
|
|
|
start_msg();
|
|
if (buh.name_cnt == 0 || buh.dnode_cnt == 0) {
|
|
dumphead.dh_flags |= DH_EMPTY;
|
|
rc = ERR_EMPTYFILE;
|
|
goto gettapes;
|
|
}
|
|
|
|
/*
|
|
* build up directory information
|
|
*/
|
|
dnodecnt = buh.dnode_cnt;
|
|
if (getdirs(fp, (int)buh.name_cnt, &dnodecnt))
|
|
return (-1);
|
|
root = hash_lookup((u_long)ROOT_INODE);
|
|
if (order_tree(root))
|
|
return (-1);
|
|
|
|
/*
|
|
* now add dnode info to our tree
|
|
*/
|
|
if (add_dnodes(fp, dnodecnt, lnkfp))
|
|
return (-1);
|
|
|
|
/*
|
|
* build name for root node. Note that there may be multiple
|
|
* levels depending on the mount point...
|
|
*/
|
|
if (build_root(dumphead.dh_mnt))
|
|
return (-1);
|
|
|
|
/*
|
|
* get tape data
|
|
*/
|
|
gettapes:
|
|
if (process_tapes(fp, (int)buh.tape_cnt, rc))
|
|
return (-1);
|
|
|
|
|
|
/*
|
|
* and finally the file header again...
|
|
*/
|
|
if (fread((char *)&buh1, sizeof (struct bu_header), 1, fp) != 1) {
|
|
(void) fprintf(stderr, gettext("%s: cannot read file header\n"),
|
|
"buildtree");
|
|
return (-1);
|
|
}
|
|
|
|
if (bcmp((char *)&buh1, (char *)&buh, sizeof (struct bu_header))) {
|
|
(void) fprintf(stderr,
|
|
gettext("bad update file: header mismatch\n"));
|
|
return (-1);
|
|
}
|
|
|
|
return (rc);
|
|
}
|
|
|
|
static int
|
|
getdirs(fp, num, dncnt)
|
|
FILE *fp;
|
|
int num;
|
|
int *dncnt;
|
|
{
|
|
struct bu_name bun;
|
|
int cnt = 0;
|
|
char name[256];
|
|
struct node *n, *parent;
|
|
struct bucket *b;
|
|
|
|
while (cnt < num) {
|
|
if (fread((char *)&bun, sizeof (struct bu_name), 1, fp) != 1) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: cannot read name struct\n"), "getdirs");
|
|
return (-1);
|
|
}
|
|
n = alloc_node();
|
|
n->flags = 0;
|
|
n->dn = NULL_DNODE;
|
|
n->name = NULL;
|
|
n->sibling = n->child = n->hashnxt = NULLNODE;
|
|
n->dnode_num = bun.inode;
|
|
if (bun.type == DIRECTORY) {
|
|
parent = n;
|
|
n->child = NULLNODE;
|
|
n->name = NULL;
|
|
/* add dir to hash list */
|
|
b = &buckets[HASH(bun.inode)];
|
|
n->hashnxt = b->nxt;
|
|
b->nxt = n;
|
|
|
|
/*
|
|
* directories carry their dnodes right
|
|
* with them.
|
|
*/
|
|
n->dn = alloc_dnode();
|
|
if (fread((char *)n->dn,
|
|
sizeof (struct dnode), 1, fp) != 1) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: bad dnode fread\n"), "getdirs");
|
|
return (-1);
|
|
}
|
|
(*dncnt)--;
|
|
} else {
|
|
if ((int)bun.namelen < 0 ||
|
|
(int)bun.namelen > MAXNAMLEN+1) {
|
|
(void) fprintf(stderr,
|
|
gettext("%s: bad namelen %d\n"),
|
|
"getdirs", bun.namelen);
|
|
return (-1);
|
|
}
|
|
if (fread((char *)name, (int)bun.namelen, 1, fp) != 1) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: cannot read name\n"), "getdirs");
|
|
return (-1);
|
|
}
|
|
n->name = alloc_name((int)bun.namelen);
|
|
(void) strcpy(n->name, name);
|
|
n->namelen = bun.namelen-1;
|
|
n->sibling = parent->child;
|
|
parent->child = n;
|
|
}
|
|
cnt++;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
order_tree(troot)
|
|
struct node *troot;
|
|
{
|
|
register struct node *n, *c;
|
|
int rc;
|
|
struct bucket *b;
|
|
|
|
if (troot == NULLNODE) {
|
|
(void) fprintf(stderr, gettext("%s: bad root\n"), "order_tree");
|
|
return (-1);
|
|
}
|
|
rc = 0;
|
|
for (n = troot->child; n; n = n->sibling) {
|
|
if (c = hash_exchange(n)) {
|
|
n->child = c->child;
|
|
n->dn = c->dn;
|
|
if (rc = order_tree(n))
|
|
break;
|
|
} else {
|
|
b = &buckets[HASH(n->dnode_num)];
|
|
n->hashnxt = b->nxt;
|
|
b->nxt = n;
|
|
}
|
|
}
|
|
return (rc);
|
|
}
|
|
|
|
static struct node *
|
|
hash_lookup(num)
|
|
u_long num;
|
|
{
|
|
struct bucket *b;
|
|
register struct node *p;
|
|
|
|
b = &buckets[HASH(num)];
|
|
for (p = b->nxt; p != (struct node *)b; p = p->hashnxt)
|
|
if (p->dnode_num == num) {
|
|
return (p);
|
|
}
|
|
return (NULLNODE);
|
|
}
|
|
|
|
static struct node *
|
|
link_lookup(num)
|
|
u_long num;
|
|
{
|
|
struct bucket *b;
|
|
register struct node *p;
|
|
|
|
/*
|
|
* caller has a inode number (from a directory)
|
|
* that has no dnode. See if there is another name (hard link)
|
|
* for this inode.
|
|
*/
|
|
b = &buckets[HASH(num)];
|
|
for (p = b->nxt; p != (struct node *)b; p = p->hashnxt)
|
|
if (p->dn && p->dn->dn_inode == num) {
|
|
return (p);
|
|
}
|
|
return (NULLNODE);
|
|
}
|
|
|
|
static struct node *
|
|
hash_exchange(n)
|
|
struct node *n;
|
|
{
|
|
struct bucket *b;
|
|
register struct node *p, *p1;
|
|
|
|
b = &buckets[HASH(n->dnode_num)];
|
|
p1 = NULLNODE;
|
|
for (p = b->nxt; p != (struct node *)b; p = p->hashnxt) {
|
|
if (p->dnode_num == n->dnode_num) {
|
|
n->hashnxt = p->hashnxt;
|
|
if (p1)
|
|
p1->hashnxt = n;
|
|
else
|
|
b->nxt = n;
|
|
return (p);
|
|
}
|
|
p1 = p;
|
|
}
|
|
return (NULLNODE);
|
|
}
|
|
|
|
static int
|
|
add_dnodes(fp, num, lnkfp)
|
|
FILE *fp;
|
|
int num;
|
|
FILE *lnkfp;
|
|
{
|
|
int cnt = 0;
|
|
struct dnode *dnp;
|
|
struct node *p;
|
|
char linkbuf[MAXPATHLEN];
|
|
|
|
dnp = NULL_DNODE;
|
|
while (cnt < num) {
|
|
if (dnp == NULL_DNODE) {
|
|
dnp = alloc_dnode();
|
|
}
|
|
if (fread((char *)dnp, sizeof (struct dnode), 1, fp) != 1) {
|
|
(void) fprintf(stderr, gettext("%s: %s error\n"),
|
|
"add_dnodes", "fread");
|
|
return (-1);
|
|
}
|
|
cnt++;
|
|
if (S_ISLNK(dnp->dn_mode)) {
|
|
if (dnp->dn_symlink < 0 ||
|
|
dnp->dn_symlink > MAXPATHLEN) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: bad symlink\n"), "add_dnodes");
|
|
return (-1);
|
|
}
|
|
if (fread((char *)linkbuf,
|
|
(int)dnp->dn_symlink, 1, fp) != 1) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: %s error\n"), "add_dnodes", "fread");
|
|
return (-1);
|
|
}
|
|
}
|
|
if ((p = hash_lookup(dnp->dn_inode)) == NULLNODE) {
|
|
(void) fprintf(stderr,
|
|
gettext("%s: cannot find %lu\n"),
|
|
"add_dnodes", dnp->dn_inode);
|
|
/* return (-1); */
|
|
continue;
|
|
}
|
|
if (S_ISLNK(dnp->dn_mode)) {
|
|
makelink(dnp, linkbuf, lnkfp);
|
|
}
|
|
p->dn = dnp;
|
|
dnp = NULL_DNODE;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
#ifdef __STDC__
|
|
makelink(struct dnode *dnp,
|
|
const char *name,
|
|
FILE *lnkfp)
|
|
#else
|
|
makelink(dnp, name, lnkfp)
|
|
struct dnode *dnp;
|
|
char *name;
|
|
FILE *lnkfp;
|
|
#endif
|
|
{
|
|
int len;
|
|
|
|
len = dnp->dn_symlink;
|
|
dnp->dn_symlink = linkoffset;
|
|
(void) fputs(name, lnkfp);
|
|
(void) putc('\0', lnkfp);
|
|
linkoffset += len;
|
|
}
|
|
|
|
static int
|
|
process_tapes(fp, num, empty)
|
|
FILE *fp;
|
|
int num;
|
|
int empty;
|
|
{
|
|
int cnt = 0;
|
|
struct tape_data *t, *last;
|
|
struct node *n;
|
|
|
|
last = (struct tape_data *)0;
|
|
while (cnt < num) {
|
|
t = alloc_tape();
|
|
t->nxt = (struct tape_data *)0;
|
|
if (fread((char *)&t->bu_tape,
|
|
sizeof (struct bu_tape), 1, fp) != 1) {
|
|
(void) fprintf(stderr, gettext("%s: %s error\n"),
|
|
"process_tapes", "fread");
|
|
return (-1);
|
|
}
|
|
|
|
if (empty) {
|
|
/*
|
|
* XXX: empty dumps cannot span tapes, right?
|
|
*/
|
|
tapehead = t;
|
|
(void) memcpy(&dumphead.dh_label[0][0],
|
|
t->bu_tape.label, LBLSIZE);
|
|
dumphead.dh_position = t->bu_tape.filenum;
|
|
if (t->bu_tape.filenum == 1) {
|
|
/*
|
|
* If the first file on tape was (re)written,
|
|
* we scratch any previous database
|
|
* information for this tape.
|
|
*/
|
|
(void) scratch_tape(t->bu_tape.label,
|
|
dumphead.dh_time);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* use link_lookup() rather than hash_lookup() since we
|
|
* need to be sure we get a dnode here
|
|
*/
|
|
if ((n = link_lookup(t->bu_tape.first_inode)) == NULLNODE) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: cannot lookup node\n"), "process_tapes");
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* there can be a break in the inode number sequence
|
|
* between the dumping of dirs and the dumping of
|
|
* regular files. We don't want to be tricked into
|
|
* thinking that a dir appears on a later tape than
|
|
* it actually does...
|
|
*/
|
|
t->maxdirinode = 0;
|
|
if (S_ISDIR(n->dn->dn_mode)) {
|
|
/*
|
|
* first inode on tape is a directory. Check
|
|
* the last one. Use link_lookup() rather than
|
|
* hash_lookup() since we need to be sure that
|
|
* we get a dnode here.
|
|
*/
|
|
n = link_lookup(t->bu_tape.last_inode);
|
|
if (n == NULLNODE) {
|
|
(void) fprintf(stderr,
|
|
gettext("%s: %s error\n"),
|
|
"process_tapes", "link_lookup");
|
|
return (-1);
|
|
}
|
|
if (S_ISDIR(n->dn->dn_mode)) {
|
|
/* last one is also a directory */
|
|
t->maxdirinode = t->bu_tape.last_inode;
|
|
} else {
|
|
/* all directories are on this tape */
|
|
t->maxdirinode = 0x7fffffff;
|
|
}
|
|
}
|
|
if (last) {
|
|
last->nxt = t;
|
|
if (t->bu_tape.filenum != 1) {
|
|
(void) fprintf(stderr, gettext(
|
|
"dump not first file on volume `%s'\n"),
|
|
t->bu_tape.label);
|
|
}
|
|
} else {
|
|
tapehead = t;
|
|
(void) memcpy(&dumphead.dh_label[0][0],
|
|
t->bu_tape.label, LBLSIZE);
|
|
dumphead.dh_position = t->bu_tape.filenum;
|
|
}
|
|
last = t;
|
|
cnt++;
|
|
|
|
if (t->bu_tape.filenum == 1) {
|
|
/*
|
|
* If the first file on tape was (re)written,
|
|
* we scratch any previous database information
|
|
* for this tape.
|
|
*/
|
|
(void) scratch_tape(t->bu_tape.label, dumphead.dh_time);
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
locate_tape(dnp)
|
|
struct dnode *dnp;
|
|
{
|
|
register struct tape_data *t;
|
|
register int count, first, last;
|
|
|
|
for (t = tapehead, count = 0; t; t = t->nxt, count++) {
|
|
first = t->bu_tape.first_inode;
|
|
last = t->bu_tape.last_inode;
|
|
if (S_ISDIR(dnp->dn_mode)) {
|
|
if (t->maxdirinode == 0) {
|
|
(void) fprintf(stderr, gettext(
|
|
"cannot find tape for directory inode\n"));
|
|
return;
|
|
}
|
|
if (t->maxdirinode >= dnp->dn_inode) {
|
|
dnp->dn_volid = count;
|
|
if (last == dnp->dn_inode && t->nxt &&
|
|
t->nxt->bu_tape.first_inode ==
|
|
dnp->dn_inode) {
|
|
dnp->dn_flags |= DN_MULTITAPE;
|
|
}
|
|
return;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
if (first <= dnp->dn_inode && last >= dnp->dn_inode) {
|
|
dnp->dn_volid = count;
|
|
#if 0
|
|
/*
|
|
* dn_vol_position will be filled in by dump. It is likely to
|
|
* be unused as long as we maintain media compatibility with the
|
|
* old dump program.
|
|
*/
|
|
dnp->dn_vol_position = t->bu_tape.filenum;
|
|
#endif
|
|
|
|
if (last == dnp->dn_inode && t->nxt &&
|
|
t->nxt->bu_tape.first_inode == dnp->dn_inode)
|
|
dnp->dn_flags |= DN_MULTITAPE;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
#ifdef __STDC__
|
|
build_root(const char *path)
|
|
#else
|
|
build_root(path)
|
|
char *path;
|
|
#endif
|
|
{
|
|
char *p;
|
|
struct node *n;
|
|
char rootpath[MAXPATHLEN];
|
|
|
|
(void) strcpy(rootpath, path);
|
|
p = strrchr(rootpath, '/');
|
|
if (!p) {
|
|
(void) fprintf(stderr,
|
|
gettext("%s: bad mount point\n"), "dir_root");
|
|
return (-1);
|
|
}
|
|
root->namelen = strlen(p+1);
|
|
if (root->namelen == 0) {
|
|
root->namelen = 1;
|
|
root->name = alloc_name(2);
|
|
root->name[0] = '.';
|
|
root->name[1] = '\0';
|
|
return (0);
|
|
}
|
|
root->name = alloc_name(root->namelen+1);
|
|
(void) strcpy(root->name, p+1);
|
|
while (p != rootpath) {
|
|
*p = '\0';
|
|
p = strrchr(rootpath, '/');
|
|
n = alloc_node();
|
|
n->flags = 0;
|
|
n->dn = NULL_DNODE;
|
|
n->sibling = n->child = n->hashnxt = NULLNODE;
|
|
n->namelen = strlen(p+1);
|
|
n->name = alloc_name(n->namelen+1);
|
|
(void) strcpy(n->name, p+1);
|
|
n->child = root;
|
|
root = n;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
dump_dnodes(troot, parent_block, fp, pcfp)
|
|
struct node *troot;
|
|
u_long parent_block;
|
|
FILE *fp;
|
|
FILE *pcfp;
|
|
{
|
|
register struct node *p;
|
|
static struct dnode dummy_dnode;
|
|
struct dnode *dnp;
|
|
|
|
for (p = troot; p; p = p->sibling) {
|
|
if (p->dn == NULL_DNODE) {
|
|
register struct node *linkp;
|
|
/*
|
|
* check for a hard link.
|
|
*/
|
|
linkp = link_lookup(p->dnode_num);
|
|
if (linkp != (struct node *)0 && linkp != p) {
|
|
bcopy((char *)linkp->dn, (char *)&dummy_dnode,
|
|
sizeof (struct dnode));
|
|
/* make sure it gets in instance file too */
|
|
p->dn = dnp = &dummy_dnode;
|
|
locate_tape(p->dn);
|
|
} else {
|
|
/*
|
|
* we write a empty dnode for files in the
|
|
* tree which were not actually dumped so that
|
|
* we maintain hierarchy information for
|
|
* rebuild purposes.
|
|
*
|
|
* XXX: can this even happen?
|
|
*/
|
|
(void) bzero((char *)&dummy_dnode,
|
|
sizeof (struct dnode));
|
|
dnp = &dummy_dnode;
|
|
}
|
|
} else {
|
|
locate_tape(p->dn);
|
|
dnp = p->dn;
|
|
}
|
|
dnp->dn_filename = (u_long) pathoffset;
|
|
dnp->dn_parent = parent_block;
|
|
if (fwrite((char *)dnp, sizeof (struct dnode), 1, fp) != 1) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: %s error\n"), "dump_dnodes", "fwrite");
|
|
}
|
|
p->dnode_num = ndnodes++;
|
|
(void) fputs(p->name, pcfp);
|
|
(void) putc('\0', pcfp);
|
|
pathoffset += p->namelen+1;
|
|
if (S_ISDIR(dnp->dn_mode))
|
|
p->flags |= NODE_ISDIR;
|
|
}
|
|
|
|
for (p = troot; p; p = p->sibling)
|
|
dump_dnodes(p->child, p->dnode_num, fp, pcfp);
|
|
}
|
|
|
|
static int
|
|
#ifdef __STDC__
|
|
output_dumpheader(char *header_name,
|
|
const char *host,
|
|
u_long dumpid)
|
|
#else
|
|
output_dumpheader(header_name, host, dumpid)
|
|
char *header_name;
|
|
char *host;
|
|
u_long dumpid;
|
|
#endif
|
|
{
|
|
FILE *fp;
|
|
struct tape_data *t;
|
|
|
|
(void) sprintf(header_name, "%s/%s%s.%lu", host, TEMP_PREFIX,
|
|
HEADERFILE, dumpid);
|
|
if ((fp = fopen(header_name, "w")) == NULL) {
|
|
(void) fprintf(stderr,
|
|
gettext("cannot create `%s'\n"), header_name);
|
|
return (-1);
|
|
}
|
|
if (fchmod(fileno(fp), 0600) == -1) {
|
|
(void) fprintf(stderr,
|
|
gettext("cannot chmod `%s'\n"), header_name);
|
|
(void) fclose(fp);
|
|
return (-1);
|
|
}
|
|
if (fwrite((char *)&dumphead, sizeof (struct dheader), 1, fp) != 1) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: %s error\n"), "output_dumpheader", "fwrite");
|
|
(void) fclose(fp);
|
|
return (-1);
|
|
}
|
|
if (dumphead.dh_ntapes > 1) {
|
|
for (t = tapehead->nxt; t; t = t->nxt) {
|
|
if (fwrite(t->bu_tape.label, LBLSIZE, 1, fp) != 1) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: %s volume label error\n"),
|
|
"output_dumpheader", "fwrite");
|
|
(void) fclose(fp);
|
|
return (-1);
|
|
}
|
|
}
|
|
}
|
|
if (fflush(fp) != 0) {
|
|
(void) fprintf(stderr, gettext("%s: %s failure\n"),
|
|
"output_header", "fflush");
|
|
return (-1);
|
|
}
|
|
(void) fsync(fileno(fp));
|
|
(void) fclose(fp);
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
dump_dir_and_instance(troot, dirblock, dfile_id)
|
|
struct node *troot;
|
|
u_long dirblock;
|
|
u_long dfile_id;
|
|
{
|
|
struct dir_entry *ep;
|
|
struct dir_block *bp;
|
|
struct node *p;
|
|
u_long instancerec;
|
|
u_long curdirblk;
|
|
int dummyinstance;
|
|
|
|
/*
|
|
* NOTE:
|
|
*
|
|
* We cannot reference dnode structures here because they have
|
|
* already been freed. We can check the dnode pointer to see
|
|
* if one existed for this file, but we cannot dereference the
|
|
* pointer.
|
|
*/
|
|
curdirblk = dirblock;
|
|
for (p = troot; p; p = p->sibling) {
|
|
dummyinstance = 0;
|
|
instancerec = NONEXISTENT_BLOCK;
|
|
curdirblk = dirblock;
|
|
ep = dir_name_getblock(&curdirblk, &bp, p->name, p->namelen);
|
|
if (ep == NULL_DIRENTRY) {
|
|
if (p->dn != NULL_DNODE || p->child) {
|
|
instancerec =
|
|
instance_newrec(NONEXISTENT_BLOCK);
|
|
if (p->dn == NULL_DNODE)
|
|
dummyinstance = 1;
|
|
}
|
|
curdirblk = dirblock;
|
|
ep = dir_addent(&curdirblk, &bp, p->name,
|
|
p->namelen, instancerec);
|
|
if (ep == NULL_DIRENTRY) {
|
|
(void) fprintf(stderr, gettext(
|
|
"cannot add name `%s'\n"),
|
|
p->name);
|
|
exit(1);
|
|
}
|
|
|
|
} else {
|
|
instancerec = ep->de_instances;
|
|
if ((p->dn != NULL_DNODE || p->child) &&
|
|
instancerec == NONEXISTENT_BLOCK) {
|
|
instancerec =
|
|
instance_newrec(NONEXISTENT_BLOCK);
|
|
if (p->dn == NULL_DNODE)
|
|
dummyinstance = 1;
|
|
if (dir_add_instance(curdirblk,
|
|
ep, instancerec)) {
|
|
(void) fprintf(stderr, gettext(
|
|
"cannot add instance\n"));
|
|
}
|
|
}
|
|
}
|
|
if (p->dn != NULL_DNODE && instancerec != NONEXISTENT_BLOCK) {
|
|
(void) instance_addent(instancerec,
|
|
dfile_id, p->dnode_num);
|
|
} else if (dummyinstance) {
|
|
/*
|
|
* we add an empty instance record for any
|
|
* non-dumped directory that has descendents
|
|
* (since we believe it could be a mount point
|
|
* and we will later get a dump that contains
|
|
* the mounted-on directory). By having the
|
|
* instance record already allocated, we don't
|
|
* have to worry about updating the `.' and `..'
|
|
* directory entries later when the full
|
|
* hierarchy is in place.
|
|
*/
|
|
(void) instance_addent(instancerec,
|
|
(u_long)0, (u_long)0);
|
|
}
|
|
|
|
if ((p->dn != NULL_DNODE && (p->flags & NODE_ISDIR)) ||
|
|
p->child) {
|
|
/*
|
|
* make a subdir for any entry that had a dnode
|
|
* and was a directory (even if it may be empty)
|
|
*/
|
|
if (ep->de_directory == NONEXISTENT_BLOCK) {
|
|
if (dir_newsubdir(curdirblk, ep) == 0) {
|
|
(void) fprintf(stderr, gettext(
|
|
"cannot make sub-directory\n"));
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p->child) {
|
|
dump_dir_and_instance(p->child,
|
|
ep->de_directory, dfile_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
#ifdef __STDC__
|
|
update_activetapes(const char *host,
|
|
u_long dumpid)
|
|
#else
|
|
update_activetapes(host, dumpid)
|
|
char *host;
|
|
u_long dumpid;
|
|
#endif
|
|
{
|
|
struct tape_data *t;
|
|
u_long recnum, hostnum, inet_addr();
|
|
char *p;
|
|
|
|
/* extract host internet number from the passed string */
|
|
if ((p = strchr(host, '.')) == NULL) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: cannot get hostnum\n"), "update_activetapes");
|
|
return (-1);
|
|
}
|
|
hostnum = inet_addr(++p);
|
|
#ifdef NOTYET
|
|
if (hostnum != dumphead.dh_netid) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: hostnum discrepancy\n"), "update_activetapes");
|
|
return (-1);
|
|
}
|
|
#endif
|
|
|
|
if (tape_open(host)) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: %s error\n"), "update_activetapes", "tape_open");
|
|
return (-1);
|
|
}
|
|
|
|
for (t = tapehead; t; t = t->nxt) {
|
|
if (tape_lookup(t->bu_tape.label, &recnum) == NULL_TREC) {
|
|
recnum = tape_newrec(t->bu_tape.label,
|
|
NONEXISTENT_BLOCK);
|
|
}
|
|
(void) tape_addent(recnum, hostnum, dumpid, t->bu_tape.filenum);
|
|
}
|
|
|
|
tape_close(host);
|
|
return (0);
|
|
}
|
|
|
|
static caddr_t pool1;
|
|
#ifdef notdef
|
|
static caddr_t pool1end;
|
|
#endif
|
|
static caddr_t pool2;
|
|
static caddr_t pool2end;
|
|
|
|
#ifdef PERFSTATS
|
|
static int filecnt;
|
|
#endif
|
|
|
|
static struct dnode *dnodebase, *nextdnode;
|
|
static struct node *nodebase, *nextnode;
|
|
static struct tape_data *tapebase, *nexttape;
|
|
static caddr_t namebase, nextname;
|
|
|
|
static int
|
|
#ifdef __STDC__
|
|
getmemory(long filesize, /* size of data file */
|
|
const struct bu_header *hd) /* data file header */
|
|
#else
|
|
getmemory(filesize, hd)
|
|
long filesize; /* size of data file */
|
|
struct bu_header *hd; /* data file header */
|
|
#endif
|
|
{
|
|
int pool1size, pool2size;
|
|
int nodecnt;
|
|
unsigned int highbit = 0x80000000U;
|
|
register int i;
|
|
|
|
/*
|
|
* allocate space for:
|
|
*
|
|
* names + dnodes + tape info (based on size of data file)
|
|
* stdio buffers (fixed size)
|
|
* tree data structures (based on # of entries in data file)
|
|
*
|
|
* We allocate dnodes and hash buckets together since they may
|
|
* be freed before the update of dir and instance files.
|
|
*
|
|
* Names, tree structures and tape structures are allocated in
|
|
* a separate pool since they may not be freed until all is
|
|
* complete...
|
|
*/
|
|
dnodebase = nextdnode = (struct dnode *)0;
|
|
nodebase = nextnode = (struct node *)0;
|
|
tapebase = nexttape = (struct tape_data *)0;
|
|
namebase = nextname = (caddr_t)0;
|
|
|
|
pool1size = (hd->dnode_cnt * sizeof (struct dnode));
|
|
|
|
/*
|
|
* nearest power of 2 which is less than the number of files
|
|
* we're processing. This insures a max of two entries per
|
|
* bucket...
|
|
*/
|
|
for (i = 0; i < 31; i++) {
|
|
hashsize = highbit >> i;
|
|
if (hd->name_cnt & hashsize)
|
|
break;
|
|
}
|
|
if (hashsize & (hashsize-1)) {
|
|
(void) fprintf(stderr, gettext("bad hash size\n"));
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* XXX: should we fix an upper bound on number of buckets?
|
|
* Note that buckets are only 4 bytes each...
|
|
*/
|
|
|
|
#ifdef PERFSTATS
|
|
filecnt = hd->name_cnt;
|
|
#endif
|
|
|
|
pool1size += (hashsize * sizeof (struct bucket));
|
|
if ((pool1 = (caddr_t)malloc((unsigned)pool1size)) == NULL) {
|
|
(void) fprintf(stderr,
|
|
gettext("%s: out of memory\n"), "getmemory");
|
|
exit(1);
|
|
}
|
|
/*LINTED [pool1 was malloc'ed]*/
|
|
dnodebase = (struct dnode *)pool1;
|
|
buckets = (struct bucket *)(pool1 +
|
|
/*LINTED [pool1 was malloc'ed]*/
|
|
(hd->dnode_cnt * sizeof (struct dnode)));
|
|
#ifdef notdef
|
|
pool1end = pool1 + pool1size;
|
|
#endif
|
|
|
|
for (i = 0; i < hashsize; i++)
|
|
buckets[i].nxt = (struct node *)&buckets[i];
|
|
|
|
/* allocate some extra nodes in case `build_root' needs them */
|
|
nodecnt = hd->name_cnt + 10;
|
|
pool2size = sizeof (struct node) * nodecnt;
|
|
pool2size += (filesize - (hd->dnode_cnt * sizeof (struct dnode)));
|
|
|
|
pool2 = (caddr_t) malloc((unsigned)pool2size);
|
|
if (pool2 == NULL) {
|
|
(void) fprintf(stderr,
|
|
gettext("%s: out of memory\n"), "getmemory");
|
|
exit(1);
|
|
}
|
|
|
|
/*LINTED [pool2 was malloc'ed]*/
|
|
nodebase = (struct node *)pool2;
|
|
tapebase = (struct tape_data *)((char *)nodebase +
|
|
/*LINTED [pool2 was malloc'ed]*/
|
|
(nodecnt * sizeof (struct node)));
|
|
namebase = (char *)tapebase +
|
|
(hd->tape_cnt * sizeof (struct tape_data));
|
|
pool2end = pool2 + pool2size;
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
#ifdef __STDC__
|
|
freemem1(void)
|
|
#else
|
|
freemem1()
|
|
#endif
|
|
{
|
|
free(stdiobuf1);
|
|
free(pool1);
|
|
}
|
|
|
|
static void
|
|
#ifdef __STDC__
|
|
freemem2(void)
|
|
#else
|
|
freemem2()
|
|
#endif
|
|
{
|
|
free(pool2);
|
|
}
|
|
|
|
static void
|
|
#ifdef __STDC__
|
|
alloc_iobuffers(void)
|
|
#else
|
|
alloc_iobuffers()
|
|
#endif
|
|
{
|
|
stdiobuf1 = (char *)malloc(2*STDIO_BUFSIZE);
|
|
if (stdiobuf1 == NULL) {
|
|
(void) fprintf(stderr, gettext(
|
|
"%s: cannot allocate io buffers\n"), "alloc_iobuffers");
|
|
exit(1);
|
|
}
|
|
stdiobuf2 = stdiobuf1 + STDIO_BUFSIZE;
|
|
}
|
|
|
|
static struct dnode *
|
|
#ifdef __STDC__
|
|
alloc_dnode(void)
|
|
#else
|
|
alloc_dnode()
|
|
#endif
|
|
{
|
|
|
|
if (!nextdnode)
|
|
nextdnode = dnodebase;
|
|
|
|
#if 1 /* DEBUG */
|
|
if (((char *)nextdnode + sizeof (struct dnode)) > (char *)buckets) {
|
|
(void) fprintf(stderr, gettext("%s: too many dnodes\n"),
|
|
"alloc_dnode");
|
|
exit(1);
|
|
}
|
|
#endif
|
|
return (nextdnode++);
|
|
}
|
|
|
|
static struct node *
|
|
#ifdef __STDC__
|
|
alloc_node(void)
|
|
#else
|
|
alloc_node()
|
|
#endif
|
|
{
|
|
|
|
if (!nextnode)
|
|
nextnode = nodebase;
|
|
|
|
#if 1 /* DEBUG */
|
|
if (((char *)nextnode + sizeof (struct node)) > (char *)tapebase) {
|
|
(void) fprintf(stderr, gettext("%s: too many nodes\n"),
|
|
"alloc_dnode");
|
|
exit(1);
|
|
}
|
|
#endif
|
|
return (nextnode++);
|
|
}
|
|
|
|
static struct tape_data *
|
|
#ifdef __STDC__
|
|
alloc_tape(void)
|
|
#else
|
|
alloc_tape()
|
|
#endif
|
|
{
|
|
|
|
if (!nexttape)
|
|
nexttape = tapebase;
|
|
|
|
#if 1 /* DEBUG */
|
|
if (((char *)nexttape + sizeof (struct tape_data)) > namebase) {
|
|
(void) fprintf(stderr, gettext("%s: too many tapes\n"),
|
|
"alloc_tape");
|
|
exit(1);
|
|
}
|
|
#endif
|
|
return (nexttape++);
|
|
}
|
|
|
|
static char *
|
|
alloc_name(size)
|
|
int size;
|
|
{
|
|
caddr_t ret;
|
|
|
|
if (!nextname)
|
|
nextname = namebase;
|
|
|
|
#if 1 /* DEBUG */
|
|
if ((nextname + size) > pool2end) {
|
|
(void) fprintf(stderr, gettext("%s: too many names\n"),
|
|
"alloc_name");
|
|
exit(1);
|
|
}
|
|
#endif
|
|
ret = nextname;
|
|
nextname += size;
|
|
return (ret);
|
|
}
|
|
|
|
#ifdef PERFSTATS
|
|
static time_t starttime;
|
|
#endif
|
|
|
|
static void
|
|
#ifdef __STDC__
|
|
start_msg(void)
|
|
#else
|
|
start_msg()
|
|
#endif
|
|
{
|
|
char msg[MAXMSGLEN];
|
|
|
|
#ifdef PERFSTATS
|
|
starttime = time(0);
|
|
#endif
|
|
(void) sprintf(msg, gettext(
|
|
"Started database update for level %lu dump of %s:%s"),
|
|
dumphead.dh_level, dumphead.dh_host, dumphead.dh_mnt);
|
|
(void) oper_send(DBOPER_TTL, LOG_NOTICE, DBOPER_FLAGS, msg);
|
|
(void) fprintf(stderr, "%s\n", msg);
|
|
}
|
|
|
|
static void
|
|
#ifdef __STDC__
|
|
success_msg(const char *host)
|
|
#else
|
|
success_msg(host)
|
|
char *host;
|
|
#endif
|
|
{
|
|
char msg[MAXMSGLEN];
|
|
|
|
update_stats(host);
|
|
(void) sprintf(msg, gettext(
|
|
"Completed database update for level %lu dump of %s:%s"),
|
|
dumphead.dh_level, dumphead.dh_host, dumphead.dh_mnt);
|
|
(void) oper_send(DBOPER_TTL, LOG_NOTICE, DBOPER_FLAGS, msg);
|
|
(void) fprintf(stderr, "%s\n", msg);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
static void
|
|
#ifdef __STDC__
|
|
update_stats(const char *host)
|
|
#else
|
|
update_stats(host)
|
|
char *host;
|
|
#endif
|
|
{
|
|
#ifdef PERFSTATS
|
|
char statfile[256];
|
|
struct rusage r;
|
|
FILE *fp;
|
|
time_t now;
|
|
|
|
(void) strcpy(statfile, host);
|
|
strcat(statfile, "/.updatestats");
|
|
if (getrusage(RUSAGE_SELF, &r) == -1) {
|
|
perror("getrusage");
|
|
return;
|
|
}
|
|
|
|
if ((fp = fopen(statfile, "a")) == NULL) {
|
|
(void) fprintf(stderr,
|
|
gettext("cannot open/append `%s'\n"), statfile);
|
|
return;
|
|
}
|
|
now = time(0);
|
|
(void) fprintf(fp,
|
|
"-----------------------------------------------------\n");
|
|
(void) fprintf(fp, gettext("Start: %s"), lctime(&starttime));
|
|
(void) fprintf(fp, gettext("Finish: %s"), lctime(&now));
|
|
(void) fprintf(fp, gettext("Elapsed Time: %d seconds\n"),
|
|
now - starttime);
|
|
(void) fprintf(fp, gettext("Number of files processed: %d\n"), filecnt);
|
|
(void) fprintf(fp, gettext("User time: %d seconds\n"),
|
|
r.ru_utime.tv_sec);
|
|
(void) fprintf(fp, gettext("System time: %d seconds\n"),
|
|
r.ru_stime.tv_sec);
|
|
(void) fprintf(fp, gettext("Voluntary Context Switches: %d\n"),
|
|
r.ru_nvcsw);
|
|
(void) fprintf(fp, gettext("Involuntary Context Switches: %d\n"),
|
|
r.ru_nivcsw);
|
|
(void) fprintf(fp, gettext("Major Page Faults: %d\n"), r.ru_majflt);
|
|
(void) fprintf(fp,
|
|
"-----------------------------------------------------\n");
|
|
(void) fclose(fp);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
#ifdef __STDC__
|
|
fail_msg(void)
|
|
#else
|
|
fail_msg()
|
|
#endif
|
|
{
|
|
char msg[MAXMSGLEN];
|
|
struct statfs buf;
|
|
|
|
if (dumphead.dh_host[0])
|
|
(void) sprintf(msg, gettext(
|
|
"Database update failed for level %lu dump of %s:%s"),
|
|
dumphead.dh_level, dumphead.dh_host, dumphead.dh_mnt);
|
|
else
|
|
(void) sprintf(msg, gettext("Database update failed"));
|
|
(void) oper_send(DBOPER_TTL, LOG_WARNING, DBOPER_FLAGS, msg);
|
|
(void) fprintf(stderr, "%s\n", msg);
|
|
|
|
if (statfs(".", &buf) != -1) {
|
|
if (buf.f_bfree == 0 || buf.f_bavail == 0) {
|
|
(void) fprintf(stderr, gettext(
|
|
"Database file system is full\n"));
|
|
(void) oper_send(DBOPER_TTL, LOG_ALERT, DBOPER_FLAGS,
|
|
gettext("Database file system is full"));
|
|
}
|
|
}
|
|
}
|