#ident "@(#)dumpadd.c 1.14 93/06/25" /* * Copyright (c) 1990,1991,1992 by Sun Microsystems, Inc. */ /* * Add database entries from existing dumps */ #include "defs.h" #include #include #include #include #include static int newvolume, fulltape; static struct byteorder_ctx *byteorder = NULL; static daddr_t tapestart; extern int t_stdio_remote; #ifdef __STDC__ static void hostprompt(char **); static void devprompt(int, char **); static void alloc_tapebuf(int); static void free_tapebuf(void); static void open_tape(const char *); static void close_tape(void); static int rewind_tape(void); static void space_tape(void); static void free_tapedata(void); static void dumptape(const char *, const char *, int); static void error_exit(void); static void nextvol(void); static void read_header(int, int); static char *prompt(int); static void read_data(FILE *); static void garbage(void); static void dirdata(struct s_spcl *, FILE *); static void linkdata(struct s_spcl *, FILE *); static int addentries(FILE *); static void taperead(char *); static int dnodedata(struct s_spcl *, FILE *); static void dnode_span(FILE *); static void outputtapes(FILE *); #else static void hostprompt(); static void devprompt(); static void alloc_tapebuf(); static void free_tapebuf(); static void open_tape(); static void close_tape(); static int rewind_tape(); static void space_tape(); static void free_tapedata(); static void dumptape(); static void error_exit(); static void nextvol(); static void read_header(); static char *prompt(); static void read_data(); static void garbage(); static void dirdata(); static void linkdata(); static int addentries(); static void taperead(); static int dnodedata(); static void dnode_span(); static void outputtapes(); #endif /* * add an entire tape's worth of dumps to the database. */ void #ifdef __STDC__ tapeadd(char *dbhost, char *dumpdev, const char *tdir, int tpbsize) #else tapeadd(dbhost, dumpdev, tdir, tpbsize) char *dbhost; char *dumpdev; char *tdir; int tpbsize; #endif { int num; char buf[256]; char *yes = gettext("yY"); if (dbhost == NULL) { hostprompt(&dbhost); } devprompt(1, &dumpdev); alloc_tapebuf(tpbsize); open_tape(dumpdev); if (rewind_tape()) exit(1); fulltape = 1; num = 1; /*CONSTCOND*/ while (1) { /* XXX: while dumps on tape */ (void) printf(gettext("File %d:\n"), num); dumptape(dbhost, tdir, num); if (newvolume) { newvolume = 0; num = 1; (void) fprintf(stderr, gettext( "Continue adding dumps from new volume? ")); if (gets(buf) == NULL || (buf[0] != yes[0] && buf[0] != yes[1])) break; } space_tape(); num++; } close_tape(); free_tapebuf(); } void #ifdef __STDC__ dumpadd(char *dbhost, char *dumpdev, const char *tdir, int tpbsize) #else dumpadd(dbhost, dumpdev, tdir, tpbsize) char *dbhost; char *dumpdev; char *tdir; int tpbsize; #endif { if (dbhost == NULL) { hostprompt(&dbhost); } if (dumpdev == NULL) { devprompt(0, &dumpdev); } alloc_tapebuf(tpbsize); open_tape(dumpdev); dumptape(dbhost, tdir, -1); space_tape(); close_tape(); free_tapebuf(); } static void hostprompt(host) char **host; { static char dbhost[256]; for (;;) { (void) fputs(gettext( "Name of database server where dump data will be added: "), stderr); if (gets(dbhost) == NULL) exit(1); if (gethostbyname(dbhost)) break; (void) fprintf(stderr, gettext("Unknown host: `%s'\n"), dbhost); } *host = dbhost; } static void devprompt(fulltape, dev) int fulltape; char **dev; { static char dumpfile[256]; struct stat stbuf; again: if (*dev == NULL) { (void) fputs(gettext( "Device from which to read dump: "), stderr); if (gets(dumpfile) == NULL) exit(1); *dev = dumpfile; } if (strchr(*dev, ':')) /* t_stdio_remote not set yet! */ return; else if (fulltape) { if (stat(*dev, &stbuf) == -1) { perror(*dev); *dev = NULL; goto again; } if ((!S_ISBLK(stbuf.st_mode) && !S_ISCHR(stbuf.st_mode)) || (minor(stbuf.st_rdev) & MT_NOREWIND) == 0) { (void) fprintf(stderr, gettext( "You must specify a no-rewind tape device\n")); *dev = NULL; goto again; } } } /* * rebuild database entries for a dump. A dump has the following format: * * header * maps * directory inode * directory data * directory data * . * . * directory inode * directory data * . * . * . * file inode * file data * file inode * . * . * . * * We take this data and turn it into a batch_update file as defined * in "batchfile.h". This file is then processed by the database * server... */ /* * XXX: make sure everything gets re-initialized for multiple * invocations. */ static struct dheader dumphead; static struct tape_data { struct bu_tape bu_tape; struct tape_data *nxt; } *tapes, *curtape; static int dnodecnt, namecnt, tapecnt; static int firstinode = -1; static u_long volnum; /* * stream pointer to the dump or tape file to be * added to the database. Reading an Exabyte * drive using 5.0 stdio package fail because * the read gets done using sub-record size reads. * All operations on tfp must be done using * stdio routines prepended with t_ found in the * file t_stdio.c. This package is a simple * buffering package that maintains semantics with * stdio, but is by no means complete. */ static FILE *tfp; FILE *t_fopen(const char *, const char *); int t_setvbuf(FILE *, char *, int, size_t); int t_fclose(FILE *); int t_fileno(FILE *); void t_rewind(FILE *); int t_feof(FILE *); size_t t_fread(void *, size_t, size_t, FILE *); static char *prompt(); static char *tapedev; static char *outdir = "/tmp"; static char *tfilename = "taperebuild"; static char outfile[MAXPATHLEN]; /* * buffer that we read into. This is allocated by alloc_tapebuf(). * User may over-ride the 63KB default size. * Note that 1/4" cartridge tapes may not behave correctly * when a read size of > 63K is specified. */ #define DEFBSIZE 63 static int bufsize; static char *mybuf; static int volchange; #define MNTPOINT 1 #define TAPELABEL 2 static void alloc_tapebuf(bsiz) int bsiz; { if (!bsiz) bsiz = DEFBSIZE; bufsize = bsiz*TP_BSIZE; mybuf = malloc((unsigned)bufsize); if (mybuf == NULL) { (void) fprintf(stderr, gettext("%s: out of memory\n"), "alloc_tapebuf"); exit(1); } if ((byteorder == NULL) && ((byteorder = byteorder_create()) == NULL)) { (void) fprintf(stderr, gettext("Cannot allocate byte order status\n")); exit(1); } } static void #ifdef __STDC__ free_tapebuf(void) #else free_tapebuf() #endif { free(mybuf); byteorder_destroy(byteorder); byteorder = NULL; } static void #ifdef __STDC__ open_tape(const char *dev) #else open_tape(dev) char *dev; #endif { if ((tfp = t_fopen(dev, "r")) == NULL) { (void) fprintf(stderr, gettext("cannot open `%s'\n"), dev); exit(1); } tapedev = (char *)dev; if (t_setvbuf(tfp, mybuf, _IOFBF, bufsize) != 0) { (void) fprintf(stderr, gettext("cannot setvbuf `%s'\n"), dev); exit(1); } } static void #ifdef __STDC__ close_tape(void) #else close_tape() #endif { if (fulltape) (void) rewind_tape(); (void) t_fclose(tfp); } static int #ifdef __STDC__ rewind_tape(void) #else rewind_tape() #endif { struct mtop tcom; if (t_stdio_remote) { if (rmtioctl(MTREW, 1) != 1) { perror("rewind"); return (1); } } else { tcom.mt_op = MTREW; tcom.mt_count = 0; if (ioctl(t_fileno(tfp), (int)MTIOCTOP, (char *)&tcom) < 0) { perror("rewind"); return (1); } } return (0); } static void #ifdef __STDC__ space_tape(void) #else space_tape() #endif { struct mtop tcom; if (t_stdio_remote) { if (rmtioctl(MTFSF, 1) != 1) { perror("ioctl MTFSF"); exit(1); } } else { tcom.mt_op = MTFSF; tcom.mt_count = 1; if (ioctl(t_fileno(tfp), (int)MTIOCTOP, (char *)&tcom) < 0) { perror("ioctl MTFSF"); exit(1); } } t_rewind(tfp); } static void #ifdef __STDC__ free_tapedata(void) #else free_tapedata() #endif { register struct tape_data *t, *tt; t = tapes; while (t) { tt = t; t = t->nxt; free((char *)tt); } tapes = curtape = (struct tape_data *)0; } static void #ifdef __STDC__ dumptape(const char *dbhost, const char *tdir, int filenum) #else dumptape(dbhost, tdir, filenum) char *dbhost; char *tdir; int filenum; #endif { FILE *outfp; struct bu_header buh; int tapepos = 1; int handle; char response[256]; unsigned sleeptime; int doprompts = 0; volchange = dnodecnt = namecnt = tapecnt = volnum = 0; firstinode = -1; free_tapedata(); if (filenum == -1) { doprompts = 1; (void) fprintf(stderr, gettext( "Defaulting to file 1 of the tape. OK? (yes or no) ")); if (gets(response) == NULL) exit(1); else if (strcmp(response, gettext("yes"))) { for (;;) { (void) fprintf(stderr, gettext( "Enter desired file number: ")); if (gets(response) == NULL) exit(1); if (sscanf(response, "%d", &tapepos) != 1) exit(1); if (tapepos >= 1 && tapepos < 500) { (void) fprintf(stderr, gettext( "Using file number %d\n"), tapepos); break; } else { (void) fprintf(stderr, gettext( "ridiculous file number %d\n"), tapepos); } } } } else { tapepos = filenum; } if (tdir) outdir = (char *)tdir; (void) sprintf(outfile, "%s/%s.%lu", outdir, tfilename, (u_long)getpid()); if ((outfp = fopen(outfile, "w")) == NULL) { (void) fprintf(stderr, gettext("cannot open `%s'\n"), outfile); exit(1); } read_header(tapepos, doprompts); buh.name_cnt = 0; buh.dnode_cnt = 0; buh.tape_cnt = 0; /* * write dummy batchfile header - we update this later when * we know what the values should be. */ if (fwrite((char *)&buh, sizeof (struct bu_header), 1, outfp) != 1) { (void) fprintf(stderr, gettext("cannot write update file header\n")); (void) fclose(outfp); (void) unlink(outfile); exit(1); } /* write dump header */ if (fwrite((char *)&dumphead, sizeof (struct dheader), 1, outfp) != 1) { (void) fprintf(stderr, gettext("cannot write dump header\n")); (void) fclose(outfp); (void) unlink(outfile); exit(1); } /* write filename and dnode data */ read_data(outfp); /* write tape data */ outputtapes(outfp); /* write updated batchfile header at the end */ buh.name_cnt = namecnt; buh.dnode_cnt = dnodecnt; buh.tape_cnt = tapecnt; if (namecnt == 0 || dnodecnt == 0 || tapecnt == 0) { #if 0 (void) fprintf(stderr, gettext("no data read?\n")); (void) fclose(outfp); (void) unlink(outfile); exit(1); #else (void) fprintf(stderr, gettext("empty dump\n")); #endif } if (fwrite((char *)&buh, sizeof (struct bu_header), 1, outfp) != 1) { (void) fprintf(stderr, gettext("cannot write update file header\n")); (void) fclose(outfp); (void) unlink(outfile); exit(1); } /* and rewrite the one at the beginning */ rewind(outfp); if (fwrite((char *)&buh, sizeof (struct bu_header), 1, outfp) != 1) { (void) fprintf(stderr, gettext("cannot write update file header\n")); exit(1); } (void) fclose(outfp); /* * send update file to database. */ sleeptime = 0; /*CONSTCOND*/ while (1) { if (sleeptime) { (void) sleep(sleeptime); (void) fprintf(stderr, gettext("retrying database update at `%s'\n"), dbhost); } if ((handle = update_start(dbhost, dumphead.dh_host)) == -1) { (void) fprintf(stderr, gettext( "cannot start update at database server `%s'\n"), dbhost); } else if (update_data(handle, outfile)) { (void) fprintf(stderr, gettext( "cannot send update file to database server `%s'\n"), dbhost); } else if (update_process(handle)) { (void) fprintf(stderr, gettext( "database server `%s' cannot process update\n"), dbhost); } else { (void) fprintf(stderr, gettext( "update file transmitted to database server `%s'\n"), dbhost); break; } sleeptime += 5; } (void) unlink(outfile); } static void #ifdef __STDC__ error_exit(void) #else error_exit() #endif { (void) unlink(outfile); close_tape(); exit(1); } static void #ifdef __STDC__ nextvol(void) #else nextvol() #endif { char buffer[TP_BSIZE]; char response[100]; struct s_spcl *p; struct tape_data *tp; char *pp; close_tape(); (void) fprintf(stderr, gettext("Mount next volume (yes or no): ")); if (gets(response) == NULL) error_exit(); else if (strcmp(response, gettext("yes"))) error_exit(); open_tape(tapedev); if (t_fread(buffer, TP_BSIZE, 1, tfp) != 1) { (void) fprintf(stderr, gettext("%s: cannot read header\n"), "nextvol"); error_exit(); } /*LINTED [buffer properly aligned]*/ p = (struct s_spcl *)buffer; if ((normspcl(byteorder, p, (int *) p, NFS_MAGIC) != 0) || (p->c_type != TS_TAPE)) { (void) fprintf(stderr, gettext("%s: not in dump format!\n"), "nextvol"); error_exit(); } volnum++; if (p->c_volume != volnum) { (void) fprintf(stderr, gettext( "expected volume %lu, got %lu\n"), volnum, p->c_volume); error_exit(); } if (p->c_date != dumphead.dh_time) { (void) fprintf(stderr, gettext("first volume had dumpdate %s"), lctime(&dumphead.dh_time)); (void) fprintf(stderr, gettext("this volume has dumpdate %s"), lctime(&p->c_date)); error_exit(); } tp = (struct tape_data *)malloc(sizeof (struct tape_data)); if (tp == (struct tape_data *)0) { (void) fprintf(stderr, gettext("%s: out of memory\n"), "nextvol"); error_exit(); } tapestart = p->c_tapea; tp->nxt = (struct tape_data *)0; tp->bu_tape.first_inode = tp->bu_tape.last_inode = 0; tp->bu_tape.filenum = 1; /* XXX */ curtape->nxt = tp; if (strcmp(p->c_label, "none") == 0) { pp = prompt(TAPELABEL); if (pp) (void) bcopy(pp, tp->bu_tape.label, LBLSIZE); else (void) bcopy(p->c_label, tp->bu_tape.label, LBLSIZE); } else { (void) bcopy(p->c_label, tp->bu_tape.label, LBLSIZE); } tapecnt++; curtape = tp; volchange = 1; newvolume = 1; } static void read_header(tapepos, doprompt) int tapepos; int doprompt; { char buffer[TP_BSIZE]; struct s_spcl *p; struct hostent *hp; char *tp; if (t_fread(buffer, TP_BSIZE, 1, tfp) != 1) { if (t_feof(tfp)) { (void) fprintf(stderr, gettext("no more dumps on this media\n")); } else { (void) fprintf(stderr, gettext("cannot read dump header\n")); } error_exit(); } /*LINTED [buffer properly aligned]*/ p = (struct s_spcl *)buffer; if ((normspcl(byteorder, p, (int *) p, NFS_MAGIC) != 0) || (p->c_type != TS_TAPE)) { (void) fprintf(stderr, gettext("%s: not in dump format!\n"), "read_header"); error_exit(); } if (p->c_volume != 1) { (void) fprintf(stderr, gettext( "%s: not first volume of dump\n"), "read_header"); error_exit(); } volnum = p->c_volume; (void) strcpy(dumphead.dh_host, p->c_host); if ((hp = gethostbyname(p->c_host)) == NULL) { (void) fprintf(stderr, gettext("cannot get host `%s'\n"), p->c_host); error_exit(); } dumphead.dh_netid = **(u_long **)(hp->h_addr_list); (void) strcpy(dumphead.dh_dev, p->c_dev); dumphead.dh_time = p->c_date; dumphead.dh_prvdumptime = p->c_ddate; dumphead.dh_level = p->c_level; dumphead.dh_flags = 0; if (p->c_flags & DR_REDUMP) dumphead.dh_flags |= DH_ACTIVE; if (p->c_flags & DR_TRUEINC) dumphead.dh_flags |= DH_TRUEINC; if (strstr(p->c_filesys, "a partial")) dumphead.dh_flags |= DH_PARTIAL; dumphead.dh_position = tapepos; dumphead.dh_ntapes = 1; (void) bcopy(p->c_label, dumphead.dh_label[0], LBLSIZE); if (strstr(p->c_filesys, "file system")) { (void) strcpy(dumphead.dh_mnt, "/"); } else { (void) strcpy(dumphead.dh_mnt, p->c_filesys); } if (doprompt || (strcmp(p->c_label, "none") == 0)) { tp = prompt(MNTPOINT); if (tp) (void) strcpy(dumphead.dh_mnt, tp); else (void) strcpy(dumphead.dh_mnt, "/"); } /* * first tape record */ tapes = (struct tape_data *)malloc(sizeof (struct tape_data)); if (tapes == (struct tape_data *)0) { (void) fprintf(stderr, gettext("%s: out of memory\n"), "read_header"); error_exit(); } tapes->nxt = (struct tape_data *)0; tapes->bu_tape.first_inode = tapes->bu_tape.last_inode = 0; tapes->bu_tape.filenum = tapepos; if (strcmp(p->c_label, "none") == 0) { tp = prompt(TAPELABEL); if (tp) (void) bcopy(tp, tapes->bu_tape.label, LBLSIZE); else (void) bcopy(p->c_label, tapes->bu_tape.label, LBLSIZE); } else { (void) bcopy(p->c_label, tapes->bu_tape.label, LBLSIZE); } tapecnt = 1; curtape = tapes; (void) printf(gettext("level %lu dump of %s:%s on %s"), dumphead.dh_level, dumphead.dh_host, dumphead.dh_mnt, lctime(&dumphead.dh_time)); } static char * prompt(msg) int msg; { static char buffer[256]; for (;;) { switch (msg) { case MNTPOINT: (void) fprintf(stderr, gettext( "Specify the `real' mount point of this\n\ \tfilesystem -- no symlinks! (default `%s'): "), dumphead.dh_mnt); break; case TAPELABEL: (void) bzero(buffer, LBLSIZE); (void) fprintf(stderr, gettext( "Specify tape label for tape mount prompts: ")); break; } if (gets(buffer) == NULL) { exit(1); } else if (buffer[0] == '\0') { if (msg == MNTPOINT) return (dumphead.dh_mnt); } else if (msg == MNTPOINT && buffer[0] != '/') { (void) fprintf(stderr, gettext( "mount point must begin with '/'\n\n")); } else { return (buffer); } } } static void read_data(fp) FILE *fp; { char buffer[TP_BSIZE]; struct s_spcl *p; mode_t mode; int doingdir; register int i; doingdir = 0; /*LINTED [buffer properly aligned]*/ p = (struct s_spcl *)buffer; p->c_type = TS_BITS; /* anything but end */ while (p->c_type != TS_END) { int skipping = 0; taperead(buffer); if (normspcl(byteorder, p, (int *) p, NFS_MAGIC) != 0) { if (! skipping) (void) fprintf(stderr, gettext("Dump format error: Attempting to resync: skipping...")); (void) fflush(stderr); skipping = 1; continue; } else if (skipping) (void) fprintf(stderr, gettext(" done.\n")); skipping = 0; switch (p->c_type) { case TS_INODE: volchange = 0; if (firstinode == -1) { firstinode = p->c_inumber; curtape->bu_tape.first_inode = firstinode; } curtape->bu_tape.last_inode = p->c_inumber; doingdir = 0; mode = p->c_dinode.di_mode; if (S_ISDIR(mode)) { doingdir = 1; dirdata(p, fp); } else if (S_ISLNK(mode)) { linkdata(p, fp); } else { (void) dnodedata(p, fp); for (i = 0; i < p->c_count; i++) if (p->c_addr[i]) garbage(); if (volchange) dnode_span(fp); } break; case TS_ADDR: volchange = 0; for (i = 0; i < p->c_count; i++) { if (p->c_addr[i]) { if (doingdir) (void) addentries(fp); else garbage(); } } if (volchange) { curtape->bu_tape.first_inode = p->c_inumber; dnode_span(fp); } curtape->bu_tape.last_inode = p->c_inumber; break; case TS_BITS: case TS_CLRI: doingdir = 0; for (i = 0; i < p->c_count; i++) /* * XXX: does c_addr map these?? */ garbage(); break; case TS_END: doingdir = 0; break; default: (void) fprintf(stderr, gettext("unknown record type %ld\n"), p->c_type); break; } } } static void #ifdef __STDC__ garbage(void) #else garbage() #endif { char buffer[TP_BSIZE]; taperead(buffer); } static void dirdata(p, fp) struct s_spcl *p; /* already passed through normspcl() */ FILE *fp; { struct bu_name bun; register int i; bun.inode = p->c_inumber; bun.type = DIRECTORY; bun.namelen = 0; if (fwrite((char *)&bun, sizeof (struct bu_name), 1, fp) != 1) { (void) fprintf(stderr, gettext("%s: cannot write directory\n"), "dirdata"); error_exit(); } (void) dnodedata(p, fp); namecnt++; for (i = 0; i < p->c_count; i++) { if (p->c_addr[i]) (void) addentries(fp); } } static int linklen; /* used in linkdata() and dnodedata() */ void linkdata(p, fp) struct s_spcl *p; /* already passed through normspcl() */ FILE *fp; { char buffer[TP_BSIZE]; register int i; /* * A zero-length symlink does not put any link data into the database * temporary file, for compatibility with the XDR routines and the * "dump" command. Nor does it bother to read any data from the tape. */ if (p->c_dinode.di_size <= 0) { linklen = 1; (void) dnodedata(p, fp); return; } if (p->c_addr[0] == 0) { (void) fprintf(stderr, gettext("%s: c_addr error?\n"), "linkdata"); } taperead(buffer); linklen = strlen(buffer)+1; (void) dnodedata(p, fp); if (fwrite((char *)buffer, linklen, 1, fp) != 1) { (void) fprintf(stderr, gettext("cannot write link data!\n")); error_exit(); } for (i = 1; i < p->c_count; i++) { if (p->c_addr[i]) garbage(); } } addentries(fp) FILE *fp; { struct bu_name bun; struct direct *d, *end; char buffer[TP_BSIZE]; /* * next block read from tape should contain directory data */ taperead(buffer); /*LINTED [buffer properly aligned]*/ d = (struct direct *)buffer; /*LINTED [buffer properly aligned]*/ end = (struct direct *)(buffer+TP_BSIZE); while (d < end) { normdirect(byteorder, d); if (d->d_reclen == 0) break; if (d->d_ino && strcmp(d->d_name, ".") && strcmp(d->d_name, "..")) { bun.inode = d->d_ino; bun.namelen = d->d_namlen+1; bun.type = 0; if (fwrite((char *)&bun, sizeof (struct bu_name), 1, fp) != 1) { (void) fprintf(stderr, gettext("%s: %s error\n"), "addentries", "fwrite1"); return (-1); } if (fwrite((char *)d->d_name, (int)bun.namelen, 1, fp) != 1) { (void) fprintf(stderr, gettext("%s: %s error\n"), "addentries", "fwrite2"); return (-1); } namecnt++; } d = (struct direct *)((unsigned)d+d->d_reclen); } return (0); } void taperead(buffer) char *buffer; { if (t_fread(buffer, TP_BSIZE, 1, tfp) != 1) { if (t_feof(tfp)) { nextvol(); if (t_fread(buffer, TP_BSIZE, 1, tfp) != 1) { (void) fprintf(stderr, gettext( "%s: %s error\n"), "taperead", "fread"); error_exit(); } firstinode = -1; } else { (void) fprintf(stderr, gettext("%s: %s error\n"), "taperead", "fread"); error_exit(); } } } static struct dnode lastdnode; dnodedata(p, fp) struct s_spcl *p; /* already passed through normspcl() */ FILE *fp; { struct dinode *ip; mode_t mode; ip = &p->c_dinode; lastdnode.dn_mode = ip->di_mode; lastdnode.dn_uid = ip->di_uid; lastdnode.dn_gid = ip->di_gid; mode = p->c_dinode.di_mode; if (S_ISBLK(mode) || S_ISCHR(mode)) { lastdnode.dn_size = p->c_dinode.di_rdev; } else { lastdnode.dn_size = p->c_dinode.di_size; } lastdnode.dn_atime = ip->di_atime; lastdnode.dn_mtime = ip->di_mtime; lastdnode.dn_ctime = ip->di_ctime; if (S_ISLNK(mode)) lastdnode.dn_symlink = linklen; else lastdnode.dn_blocks = ip->di_blocks; /* XXX: no way to get active file data back from here? */ lastdnode.dn_flags = DN_OFFSET; lastdnode.dn_vol_position = p->c_tapea - tapestart; lastdnode.dn_inode = p->c_inumber; if (fwrite((char *)&lastdnode, sizeof (struct dnode), 1, fp) != 1) { (void) fprintf(stderr, gettext("%s: %s error\n"), "dnodedata", "write"); return (-1); } dnodecnt++; return (0); } void dnode_span(fp) FILE *fp; { long curpos; /* * re-write the last dnode to indicate that its file * spans tapes. */ lastdnode.dn_flags |= DN_MULTITAPE; curpos = ftell(fp); if (fseek(fp, curpos - sizeof (struct dnode), 0) == -1) { (void) fprintf(stderr, gettext("%s: %s error\n"), "dnode_span", "fseek"); return; } if (fwrite((char *)&lastdnode, sizeof (struct dnode), 1, fp) != 1) { (void) fprintf(stderr, gettext("%s: cannot rewrite dnode\n"), "dnode_span"); return; } } void outputtapes(fp) FILE *fp; { struct tape_data *tp; int cnt; for (cnt = 0, tp = tapes; tp; cnt++, tp = tp->nxt) { if (cnt > tapecnt) { (void) fprintf(stderr, gettext("tape count error\n")); exit(1); } if (fwrite((char *)&tp->bu_tape, sizeof (struct bu_tape), 1, fp) != 1) { (void) fprintf(stderr, gettext("tape write error\n")); exit(1); } } }