Index: head/include/protocols/dumprestore.h =================================================================== --- head/include/protocols/dumprestore.h (revision 359626) +++ head/include/protocols/dumprestore.h (revision 359627) @@ -1,144 +1,144 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)dumprestore.h 8.2 (Berkeley) 1/21/94 * * $FreeBSD$ */ #ifndef _PROTOCOLS_DUMPRESTORE_H_ #define _PROTOCOLS_DUMPRESTORE_H_ /* * TP_BSIZE is the size of file blocks on the dump tapes. * Note that TP_BSIZE must be a multiple of DEV_BSIZE. * * NTREC is the number of TP_BSIZE blocks that are written * in each tape record. HIGHDENSITYTREC is the number of * TP_BSIZE blocks that are written in each tape record on * 6250 BPI or higher density tapes. * * TP_NINDIR is the number of indirect pointers in a TS_INODE * or TS_ADDR record. Note that it must be a power of two. */ #define TP_BSIZE 1024 #define NTREC 10 #define HIGHDENSITYTREC 32 #define TP_NINDIR (TP_BSIZE/2) #define LBLSIZE 16 #define NAMELEN 64 #define OFS_MAGIC (int)60011 #define NFS_MAGIC (int)60012 #ifndef FS_UFS2_MAGIC #define FS_UFS2_MAGIC (int)0x19540119 #endif #define CHECKSUM (int)84446 /* * Since ino_t size is changing to 64-bits, yet we desire this structure to * remain compatible with exiting dump formats, we do NOT use ino_t here, * but rather define a 32-bit type in its place. At some point, it may be * necessary to use some of the c_spare[] in order to fully support 64-bit * inode numbers. */ typedef uint32_t dump_ino_t; -union u_spcl { +extern union u_spcl { char dummy[TP_BSIZE]; struct s_spcl { int32_t c_type; /* record type (see below) */ int32_t c_old_date; /* date of this dump */ int32_t c_old_ddate; /* date of previous dump */ int32_t c_volume; /* dump volume number */ int32_t c_old_tapea; /* logical block of this record */ dump_ino_t c_inumber; /* number of inode */ int32_t c_magic; /* magic number (see above) */ int32_t c_checksum; /* record checksum */ /* * Start old dinode structure, expanded for binary * compatibility with UFS1. */ u_int16_t c_mode; /* file mode */ int16_t c_spare1[3]; /* old nlink, ids */ u_int64_t c_size; /* file byte count */ int32_t c_old_atime; /* old last access time, seconds */ int32_t c_atimensec; /* last access time, nanoseconds */ int32_t c_old_mtime; /* old last modified time, secs */ int32_t c_mtimensec; /* last modified time, nanosecs */ int32_t c_spare2[2]; /* old ctime */ int32_t c_rdev; /* for devices, device number */ int32_t c_birthtimensec; /* creation time, nanosecs */ int64_t c_birthtime; /* creation time, seconds */ int64_t c_atime; /* last access time, seconds */ int64_t c_mtime; /* last modified time, seconds */ int32_t c_extsize; /* external attribute size */ int32_t c_spare4[6]; /* old block pointers */ u_int32_t c_file_flags; /* status flags (chflags) */ int32_t c_spare5[2]; /* old blocks, generation number */ u_int32_t c_uid; /* file owner */ u_int32_t c_gid; /* file group */ int32_t c_spare6[2]; /* previously unused spares */ /* * End old dinode structure. */ int32_t c_count; /* number of valid c_addr entries */ char c_addr[TP_NINDIR]; /* 1 => data; 0 => hole in inode */ char c_label[LBLSIZE]; /* dump label */ int32_t c_level; /* level of this dump */ char c_filesys[NAMELEN]; /* name of dumpped file system */ char c_dev[NAMELEN]; /* name of dumpped device */ char c_host[NAMELEN]; /* name of dumpped host */ int32_t c_flags; /* additional information */ int32_t c_old_firstrec; /* first record on volume */ int64_t c_date; /* date of this dump */ int64_t c_ddate; /* date of previous dump */ int64_t c_tapea; /* logical block of this record */ int64_t c_firstrec; /* first record on volume */ int32_t c_spare[24]; /* reserved for future uses */ } s_spcl; } u_spcl; #define spcl u_spcl.s_spcl /* * special record types */ #define TS_TAPE 1 /* dump tape header */ #define TS_INODE 2 /* beginning of file record */ #define TS_ADDR 4 /* continuation of file record */ #define TS_BITS 3 /* map of inodes on tape */ #define TS_CLRI 6 /* map of inodes deleted since last dump */ #define TS_END 5 /* end of volume marker */ #endif /* !_DUMPRESTORE_H_ */ Index: head/sbin/dump/dump.h =================================================================== --- head/sbin/dump/dump.h (revision 359626) +++ head/sbin/dump/dump.h (revision 359627) @@ -1,184 +1,184 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)dump.h 8.2 (Berkeley) 4/28/95 * * $FreeBSD$ */ /* * Dump maps used to describe what is to be dumped. */ -int mapsize; /* size of the state maps */ -char *usedinomap; /* map of allocated inodes */ -char *dumpdirmap; /* map of directories to be dumped */ -char *dumpinomap; /* map of files to be dumped */ +extern int mapsize; /* size of the state maps */ +extern char *usedinomap; /* map of allocated inodes */ +extern char *dumpdirmap; /* map of directories to be dumped */ +extern char *dumpinomap; /* map of files to be dumped */ /* * Map manipulation macros. */ #define SETINO(ino, map) \ map[(u_int)((ino) - 1) / CHAR_BIT] |= \ 1 << ((u_int)((ino) - 1) % CHAR_BIT) #define CLRINO(ino, map) \ map[(u_int)((ino) - 1) / CHAR_BIT] &= \ ~(1 << ((u_int)((ino) - 1) % CHAR_BIT)) #define TSTINO(ino, map) \ (map[(u_int)((ino) - 1) / CHAR_BIT] & \ (1 << ((u_int)((ino) - 1) % CHAR_BIT))) /* * All calculations done in 0.1" units! */ -char *disk; /* name of the disk file */ -char *tape; /* name of the tape file */ -char *popenout; /* popen(3) per-"tape" command */ -char *dumpdates; /* name of the file containing dump date information*/ -char *temp; /* name of the file for doing rewrite of dumpdates */ -int lastlevel; /* dump level of previous dump */ -int level; /* dump level of this dump */ -int uflag; /* update flag */ -int diskfd; /* disk file descriptor */ -int tapefd; /* tape file descriptor */ -int pipeout; /* true => output to standard output */ -ino_t curino; /* current inumber; used globally */ -int newtape; /* new tape flag */ -int density; /* density in 0.1" units */ -long tapesize; /* estimated tape size, blocks */ -long tsize; /* tape size in 0.1" units */ -long asize; /* number of 0.1" units written on current tape */ -int etapes; /* estimated number of tapes */ -int nonodump; /* if set, do not honor UF_NODUMP user flags */ -int unlimited; /* if set, write to end of medium */ -int cachesize; /* size of block cache in bytes */ -int rsync_friendly; /* be friendly with rsync */ +extern char *disk; /* name of the disk file */ +extern char *tape; /* name of the tape file */ +extern char *popenout; /* popen(3) per-"tape" command */ +extern char *dumpdates; /* name of the file containing dump date info */ +extern int lastlevel; /* dump level of previous dump */ +extern int level; /* dump level of this dump */ +extern int uflag; /* update flag */ +extern int diskfd; /* disk file descriptor */ +extern int pipeout; /* true => output to standard output */ +extern ino_t curino; /* current inumber; used globally */ +extern int newtape; /* new tape flag */ +extern int density; /* density in 0.1" units */ +extern long tapesize; /* estimated tape size, blocks */ +extern long tsize; /* tape size in 0.1" units */ +extern int etapes; /* estimated number of tapes */ +extern int nonodump; /* if set, do not honor UF_NODUMP user flags */ +extern int unlimited; /* if set, write to end of medium */ +extern int cachesize; /* size of block cache in bytes */ +extern int rsync_friendly; /* be friendly with rsync */ +extern int notify; /* notify operator flag */ +extern int blockswritten; /* number of blocks written on current tape */ +extern int tapeno; /* current tape number */ +extern int ntrec; /* blocking factor on tape */ +extern long blocksperfile; /* number of blocks per output file */ +extern int cartridge; /* assume non-cartridge tape */ +extern char *host; /* remote host (if any) */ +extern time_t tstart_writing; /* when started writing the first tape block */ +extern time_t tend_writing; /* after writing the last tape block */ +extern int passno; /* current dump pass number */ +extern struct fs *sblock; /* the file system super block */ +extern long dev_bsize; /* block size of underlying disk device */ +extern int dev_bshift; /* log2(dev_bsize) */ +extern int tp_bshift; /* log2(TP_BSIZE) */ -int notify; /* notify operator flag */ -int blockswritten; /* number of blocks written on current tape */ -int tapeno; /* current tape number */ -time_t tstart_writing; /* when started writing the first tape block */ -time_t tend_writing; /* after writing the last tape block */ -int passno; /* current dump pass number */ -struct fs *sblock; /* the file system super block */ -long dev_bsize; /* block size of underlying disk device */ -int dev_bshift; /* log2(dev_bsize) */ -int tp_bshift; /* log2(TP_BSIZE) */ - /* operator interface functions */ void broadcast(const char *message); void infosch(int); void lastdump(int arg); /* int should be char */ void msg(const char *fmt, ...) __printflike(1, 2); void msgtail(const char *fmt, ...) __printflike(1, 2); int query(const char *question); void quit(const char *fmt, ...) __printflike(1, 2); void timeest(void); time_t unctime(char *str); /* mapping rouintes */ union dinode; int mapfiles(ino_t maxino, long *tapesize); int mapdirs(ino_t maxino, long *tapesize); /* file dumping routines */ void blkread(ufs2_daddr_t blkno, char *buf, int size); ssize_t cread(int fd, void *buf, size_t nbytes, off_t offset); void dumpino(union dinode *dp, ino_t ino); void dumpmap(char *map, int type, ino_t ino); void writeheader(ino_t ino); /* tape writing routines */ int alloctape(void); void close_rewind(void); void dumpblock(ufs2_daddr_t blkno, int size); void startnewtape(int top); void trewind(void); void writerec(char *dp, int isspcl); void Exit(int status) __dead2; void dumpabort(int signo) __dead2; void dump_getfstab(void); char *rawname(char *cp); union dinode *getino(ino_t inum, int *mode); /* rdump routines */ #ifdef RDUMP void rmtclose(void); int rmthost(const char *host); int rmtopen(const char *tape, int mode); int rmtwrite(const char *buf, int count); #endif /* RDUMP */ void interrupt(int signo); /* in case operator bangs on console */ /* * Exit status codes */ #define X_FINOK 0 /* normal exit */ #define X_STARTUP 1 /* startup error */ #define X_REWRITE 2 /* restart writing from the check point */ #define X_ABORT 3 /* abort dump; don't attempt checkpointing */ #define OPGRENT "operator" /* group entry to notify */ struct fstab *fstabsearch(const char *key); /* search fs_file and fs_spec */ #ifndef NAME_MAX #define NAME_MAX 255 #endif /* * The contents of the file _PATH_DUMPDATES is maintained both on * a linked list, and then (eventually) arrayified. */ struct dumpdates { char dd_name[NAME_MAX+3]; int dd_level; time_t dd_ddate; }; -int nddates; /* number of records (might be zero) */ -struct dumpdates **ddatev; /* the arrayfied version */ +extern int nddates; /* number of records (might be zero) */ +extern struct dumpdates **ddatev; /* the arrayfied version */ void initdumptimes(void); void getdumptime(void); void putdumptime(void); #define ITITERATE(i, ddp) \ if (ddatev != NULL) \ for (ddp = ddatev[i = 0]; i < nddates; ddp = ddatev[++i]) #define DUMPFMTLEN 53 /* max device pathname length */ #define DUMPOUTFMT "%-*s %d %s" /* for printf */ /* name, level, ctime(date) */ #define DUMPINFMT "%s %d %[^\n]\n" /* inverse for scanf */ void sig(int signo); #ifndef _PATH_FSTAB #define _PATH_FSTAB "/etc/fstab" #endif Index: head/sbin/dump/dumprmt.c =================================================================== --- head/sbin/dump/dumprmt.c (revision 359626) +++ head/sbin/dump/dumprmt.c (revision 359627) @@ -1,377 +1,376 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)dumprmt.c 8.3 (Berkeley) 4/28/95"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" #include "dump.h" #define TS_CLOSED 0 #define TS_OPEN 1 static int rmtstate = TS_CLOSED; static int rmtape; static char *rmtpeer; static int okname(const char *); static int rmtcall(const char *, const char *); static void rmtconnaborted(int); static int rmtgetb(void); static void rmtgetconn(void); static void rmtgets(char *, int); static int rmtreply(const char *); static int errfd = -1; -extern int ntrec; /* blocking factor on tape */ int rmthost(const char *host) { rmtpeer = strdup(host); if (rmtpeer == NULL) return (0); signal(SIGPIPE, rmtconnaborted); rmtgetconn(); if (rmtape < 0) return (0); return (1); } static void rmtconnaborted(int sig __unused) { msg("Lost connection to remote host.\n"); if (errfd != -1) { fd_set r; struct timeval t; FD_ZERO(&r); FD_SET(errfd, &r); t.tv_sec = 0; t.tv_usec = 0; if (select(errfd + 1, &r, NULL, NULL, &t)) { int i; char buf[2048]; if ((i = read(errfd, buf, sizeof(buf) - 1)) > 0) { buf[i] = '\0'; msg("on %s: %s%s", rmtpeer, buf, buf[i - 1] == '\n' ? "" : "\n"); } } } exit(X_ABORT); } void rmtgetconn(void) { char *cp; const char *rmt; static struct servent *sp = NULL; static struct passwd *pwd = NULL; char *tuser; int size; int throughput; int on; if (sp == NULL) { sp = getservbyname("shell", "tcp"); if (sp == NULL) { msg("shell/tcp: unknown service\n"); exit(X_STARTUP); } pwd = getpwuid(getuid()); if (pwd == NULL) { msg("who are you?\n"); exit(X_STARTUP); } } if ((cp = strchr(rmtpeer, '@')) != NULL) { tuser = rmtpeer; *cp = '\0'; if (!okname(tuser)) exit(X_STARTUP); rmtpeer = ++cp; } else tuser = pwd->pw_name; if ((rmt = getenv("RMT")) == NULL) rmt = _PATH_RMT; msg("%s", ""); rmtape = rcmd(&rmtpeer, (u_short)sp->s_port, pwd->pw_name, tuser, rmt, &errfd); if (rmtape < 0) { msg("login to %s as %s failed.\n", rmtpeer, tuser); return; } (void)fprintf(stderr, "Connection to %s established.\n", rmtpeer); size = ntrec * TP_BSIZE; if (size > 60 * 1024) /* XXX */ size = 60 * 1024; /* Leave some space for rmt request/response protocol */ size += 2 * 1024; while (size > TP_BSIZE && setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0) size -= TP_BSIZE; (void)setsockopt(rmtape, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)); throughput = IPTOS_THROUGHPUT; if (setsockopt(rmtape, IPPROTO_IP, IP_TOS, &throughput, sizeof(throughput)) < 0) perror("IP_TOS:IPTOS_THROUGHPUT setsockopt"); on = 1; if (setsockopt(rmtape, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0) perror("TCP_NODELAY setsockopt"); } static int okname(const char *cp0) { const char *cp; int c; for (cp = cp0; *cp; cp++) { c = *cp; if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) { msg("invalid user name %s\n", cp0); return (0); } } return (1); } int rmtopen(const char *tape, int mode) { char buf[256]; (void)snprintf(buf, sizeof (buf), "O%.226s\n%d\n", tape, mode); rmtstate = TS_OPEN; return (rmtcall(tape, buf)); } void rmtclose(void) { if (rmtstate != TS_OPEN) return; rmtcall("close", "C\n"); rmtstate = TS_CLOSED; } int rmtread(char *buf, int count) { char line[30]; int n, i, cc; (void)snprintf(line, sizeof (line), "R%d\n", count); n = rmtcall("read", line); if (n < 0) /* rmtcall() properly sets errno for us on errors. */ return (n); for (i = 0; i < n; i += cc) { cc = read(rmtape, buf+i, n - i); if (cc <= 0) rmtconnaborted(0); } return (n); } int rmtwrite(const char *buf, int count) { char line[30]; (void)snprintf(line, sizeof (line), "W%d\n", count); write(rmtape, line, strlen(line)); write(rmtape, buf, count); return (rmtreply("write")); } void rmtwrite0(int count) { char line[30]; (void)snprintf(line, sizeof (line), "W%d\n", count); write(rmtape, line, strlen(line)); } void rmtwrite1(const char *buf, int count) { write(rmtape, buf, count); } int rmtwrite2(void) { return (rmtreply("write")); } int rmtseek(int offset, int pos) /* XXX off_t ? */ { char line[80]; (void)snprintf(line, sizeof (line), "L%d\n%d\n", offset, pos); return (rmtcall("seek", line)); } struct mtget mts; struct mtget * rmtstatus(void) { int i; char *cp; if (rmtstate != TS_OPEN) return (NULL); rmtcall("status", "S\n"); for (i = 0, cp = (char *)&mts; i < sizeof(mts); i++) *cp++ = rmtgetb(); return (&mts); } int rmtioctl(int cmd, int count) { char buf[256]; if (count < 0) return (-1); (void)snprintf(buf, sizeof (buf), "I%d\n%d\n", cmd, count); return (rmtcall("ioctl", buf)); } static int rmtcall(const char *cmd, const char *buf) { if (write(rmtape, buf, strlen(buf)) != strlen(buf)) rmtconnaborted(0); return (rmtreply(cmd)); } static int rmtreply(const char *cmd) { char *cp; char code[30], emsg[BUFSIZ]; rmtgets(code, sizeof (code)); if (*code == 'E' || *code == 'F') { rmtgets(emsg, sizeof (emsg)); msg("%s: %s", cmd, emsg); errno = atoi(code + 1); if (*code == 'F') rmtstate = TS_CLOSED; return (-1); } if (*code != 'A') { /* Kill trailing newline */ cp = code + strlen(code); if (cp > code && *--cp == '\n') *cp = '\0'; msg("Protocol to remote tape server botched (code \"%s\").\n", code); rmtconnaborted(0); } return (atoi(code + 1)); } int rmtgetb(void) { char c; if (read(rmtape, &c, 1) != 1) rmtconnaborted(0); return (c); } /* Get a line (guaranteed to have a trailing newline). */ void rmtgets(char *line, int len) { char *cp = line; while (len > 1) { *cp = rmtgetb(); if (*cp == '\n') { cp[1] = '\0'; return; } cp++; len--; } *cp = '\0'; msg("Protocol to remote tape server botched.\n"); msg("(rmtgets got \"%s\").\n", line); rmtconnaborted(0); } Index: head/sbin/dump/itime.c =================================================================== --- head/sbin/dump/itime.c (revision 359626) +++ head/sbin/dump/itime.c (revision 359627) @@ -1,266 +1,268 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)itime.c 8.1 (Berkeley) 6/5/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" struct dumptime { struct dumpdates dt_value; SLIST_ENTRY(dumptime) dt_list; }; SLIST_HEAD(dthead, dumptime) dthead = SLIST_HEAD_INITIALIZER(dthead); -struct dumpdates **ddatev = NULL; -int nddates = 0; +int nddates = 0; /* number of records (might be zero) */ +struct dumpdates **ddatev; /* the arrayfied version */ +char *dumpdates; /* name of the file containing dump date info */ +int lastlevel; /* dump level of previous dump */ static void dumprecout(FILE *, const struct dumpdates *); static int getrecord(FILE *, struct dumpdates *); static int makedumpdate(struct dumpdates *, const char *); static void readdumptimes(FILE *); void initdumptimes(void) { FILE *df; if ((df = fopen(dumpdates, "r")) == NULL) { if (errno != ENOENT) { msg("WARNING: cannot read %s: %s\n", dumpdates, strerror(errno)); return; } /* * Dumpdates does not exist, make an empty one. */ msg("WARNING: no file `%s', making an empty one\n", dumpdates); if ((df = fopen(dumpdates, "w")) == NULL) { msg("WARNING: cannot create %s: %s\n", dumpdates, strerror(errno)); return; } (void) fclose(df); if ((df = fopen(dumpdates, "r")) == NULL) { quit("cannot read %s even after creating it: %s\n", dumpdates, strerror(errno)); /* NOTREACHED */ } } (void) flock(fileno(df), LOCK_SH); readdumptimes(df); (void) fclose(df); } static void readdumptimes(FILE *df) { int i; struct dumptime *dtwalk; for (;;) { dtwalk = (struct dumptime *)calloc(1, sizeof (struct dumptime)); if (getrecord(df, &(dtwalk->dt_value)) < 0) { free(dtwalk); break; } nddates++; SLIST_INSERT_HEAD(&dthead, dtwalk, dt_list); } /* * arrayify the list, leaving enough room for the additional * record that we may have to add to the ddate structure */ ddatev = calloc((unsigned) (nddates + 1), sizeof (struct dumpdates *)); dtwalk = SLIST_FIRST(&dthead); for (i = nddates - 1; i >= 0; i--, dtwalk = SLIST_NEXT(dtwalk, dt_list)) ddatev[i] = &dtwalk->dt_value; } void getdumptime(void) { struct dumpdates *ddp; int i; char *fname; fname = disk; #ifdef FDEBUG msg("Looking for name %s in dumpdates = %s for level = %d\n", fname, dumpdates, level); #endif spcl.c_ddate = 0; lastlevel = 0; initdumptimes(); /* * Go find the entry with the same name for a lower increment * and older date */ ITITERATE(i, ddp) { if (strncmp(fname, ddp->dd_name, sizeof (ddp->dd_name)) != 0) continue; if (ddp->dd_level >= level) continue; if (ddp->dd_ddate <= _time64_to_time(spcl.c_ddate)) continue; spcl.c_ddate = _time_to_time64(ddp->dd_ddate); lastlevel = ddp->dd_level; } } void putdumptime(void) { FILE *df; struct dumpdates *dtwalk; int i; int fd; char *fname; char *tmsg; if(uflag == 0) return; if ((df = fopen(dumpdates, "r+")) == NULL) quit("cannot rewrite %s: %s\n", dumpdates, strerror(errno)); fd = fileno(df); (void) flock(fd, LOCK_EX); fname = disk; free(ddatev); ddatev = NULL; nddates = 0; readdumptimes(df); if (fseek(df, 0L, 0) < 0) quit("fseek: %s\n", strerror(errno)); spcl.c_ddate = 0; ITITERATE(i, dtwalk) { if (strncmp(fname, dtwalk->dd_name, sizeof (dtwalk->dd_name)) != 0) continue; if (dtwalk->dd_level != level) continue; goto found; } /* * construct the new upper bound; * Enough room has been allocated. */ dtwalk = ddatev[nddates] = (struct dumpdates *)calloc(1, sizeof (struct dumpdates)); nddates += 1; found: (void) strncpy(dtwalk->dd_name, fname, sizeof (dtwalk->dd_name)); dtwalk->dd_level = level; dtwalk->dd_ddate = _time64_to_time(spcl.c_date); ITITERATE(i, dtwalk) { dumprecout(df, dtwalk); } if (fflush(df)) quit("%s: %s\n", dumpdates, strerror(errno)); if (ftruncate(fd, ftell(df))) quit("ftruncate (%s): %s\n", dumpdates, strerror(errno)); (void) fclose(df); if (spcl.c_date == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_date); tmsg = ctime(&t); } msg("level %d dump on %s", level, tmsg); } static void dumprecout(FILE *file, const struct dumpdates *what) { if (strlen(what->dd_name) > DUMPFMTLEN) quit("Name '%s' exceeds DUMPFMTLEN (%d) bytes\n", what->dd_name, DUMPFMTLEN); if (fprintf(file, DUMPOUTFMT, DUMPFMTLEN, what->dd_name, what->dd_level, ctime(&what->dd_ddate)) < 0) quit("%s: %s\n", dumpdates, strerror(errno)); } int recno; static int getrecord(FILE *df, struct dumpdates *ddatep) { char tbuf[BUFSIZ]; recno = 0; if ( (fgets(tbuf, sizeof (tbuf), df)) != tbuf) return(-1); recno++; if (makedumpdate(ddatep, tbuf) < 0) msg("Unknown intermediate format in %s, line %d\n", dumpdates, recno); #ifdef FDEBUG msg("getrecord: %s %d %s", ddatep->dd_name, ddatep->dd_level, ddatep->dd_ddate == 0 ? "the epoch\n" : ctime(&ddatep->dd_ddate)); #endif return(0); } static int makedumpdate(struct dumpdates *ddp, const char *tbuf) { char un_buf[128]; (void) sscanf(tbuf, DUMPINFMT, ddp->dd_name, &ddp->dd_level, un_buf); ddp->dd_ddate = unctime(un_buf); if (ddp->dd_ddate < 0) return(-1); return(0); } Index: head/sbin/dump/main.c =================================================================== --- head/sbin/dump/main.c (revision 359626) +++ head/sbin/dump/main.c (revision 359627) @@ -1,773 +1,795 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/1/95"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" #include "pathnames.h" +int mapsize; /* size of the state maps */ +char *usedinomap; /* map of allocated inodes */ +char *dumpdirmap; /* map of directories to be dumped */ +char *dumpinomap; /* map of files to be dumped */ +char *disk; /* name of the disk file */ +char *tape; /* name of the tape file */ +char *popenout; /* popen(3) per-"tape" command */ +int level; /* dump level of this dump */ +int uflag; /* update flag */ +int diskfd; /* disk file descriptor */ +int pipeout; /* true => output to standard output */ +int density = 0; /* density in bytes/0.1" " <- this is for hilit19 */ +long tapesize; /* estimated tape size, blocks */ +long tsize; /* tape size in 0.1" units */ +int etapes; /* estimated number of tapes */ +int nonodump; /* if set, do not honor UF_NODUMP user flags */ +int unlimited; /* if set, write to end of medium */ +int cachesize = 0; /* block cache size (in bytes), defaults to 0 */ +int rsync_friendly; /* be friendly with rsync */ int notify = 0; /* notify operator flag */ -int snapdump = 0; /* dumping live filesystem, so use snapshot */ -int blockswritten = 0; /* number of blocks written on current tape */ +int blockswritten = 0; /* number of blocks written on current tape */ int tapeno = 0; /* current tape number */ -int density = 0; /* density in bytes/0.1" " <- this is for hilit19 */ int ntrec = NTREC; /* # tape blocks in each tape record */ +long blocksperfile; /* number of blocks per output file */ int cartridge = 0; /* Assume non-cartridge tape */ -int cachesize = 0; /* block cache size (in bytes), defaults to 0 */ -long dev_bsize = 1; /* recalculated below */ -long blocksperfile; /* output blocks per file */ char *host = NULL; /* remote host (if any) */ +time_t tstart_writing; /* when started writing the first tape block */ +time_t tend_writing; /* after writing the last tape block */ +int passno; /* current dump pass number */ +struct fs *sblock; /* the file system super block */ +long dev_bsize = 1; /* recalculated below */ +int dev_bshift; /* log2(dev_bsize) */ +int tp_bshift; /* log2(TP_BSIZE) */ +int snapdump = 0; /* dumping live filesystem, so use snapshot */ static char *getmntpt(char *, int *); static long numarg(const char *, long, long); static void obsolete(int *, char **[]); static void usage(void) __dead2; int main(int argc, char *argv[]) { struct stat sb; ino_t ino; int dirty; union dinode *dp; struct fstab *dt; char *map, *mntpt; int ch, mode, mntflags; int i, ret, anydirskipped, bflag = 0, Tflag = 0, honorlevel = 1; int just_estimate = 0; ino_t maxino; char *tmsg; spcl.c_date = _time_to_time64(time(NULL)); tsize = 0; /* Default later, based on 'c' option for cart tapes */ dumpdates = _PATH_DUMPDATES; popenout = NULL; tape = NULL; - temp = _PATH_DTMP; if (TP_BSIZE / DEV_BSIZE == 0 || TP_BSIZE % DEV_BSIZE != 0) quit("TP_BSIZE must be a multiple of DEV_BSIZE\n"); level = 0; rsync_friendly = 0; if (argc < 2) usage(); obsolete(&argc, &argv); while ((ch = getopt(argc, argv, "0123456789aB:b:C:cD:d:f:h:LnP:RrSs:T:uWw")) != -1) switch (ch) { /* dump level */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': level = 10 * level + ch - '0'; break; case 'a': /* `auto-size', Write to EOM. */ unlimited = 1; break; case 'B': /* blocks per output file */ blocksperfile = numarg("number of blocks per file", 1L, 0L); break; case 'b': /* blocks per tape write */ ntrec = numarg("number of blocks per write", 1L, 1000L); break; case 'C': cachesize = numarg("cachesize", 0, 0) * 1024 * 1024; break; case 'c': /* Tape is cart. not 9-track */ cartridge = 1; break; case 'D': dumpdates = optarg; break; case 'd': /* density, in bits per inch */ density = numarg("density", 10L, 327670L) / 10; if (density >= 625 && !bflag) ntrec = HIGHDENSITYTREC; break; case 'f': /* output file */ if (popenout != NULL) errx(X_STARTUP, "You cannot use the P and f " "flags together.\n"); tape = optarg; break; case 'h': honorlevel = numarg("honor level", 0L, 10L); break; case 'L': snapdump = 1; break; case 'n': /* notify operators */ notify = 1; break; case 'P': if (tape != NULL) errx(X_STARTUP, "You cannot use the P and f " "flags together.\n"); popenout = optarg; break; case 'r': /* store slightly less data to be friendly to rsync */ if (rsync_friendly < 1) rsync_friendly = 1; break; case 'R': /* store even less data to be friendlier to rsync */ if (rsync_friendly < 2) rsync_friendly = 2; break; case 'S': /* exit after estimating # of tapes */ just_estimate = 1; break; case 's': /* tape size, feet */ tsize = numarg("tape size", 1L, 0L) * 12 * 10; break; case 'T': /* time of last dump */ spcl.c_ddate = unctime(optarg); if (spcl.c_ddate < 0) { (void)fprintf(stderr, "bad time \"%s\"\n", optarg); exit(X_STARTUP); } Tflag = 1; lastlevel = -1; break; case 'u': /* update /etc/dumpdates */ uflag = 1; break; case 'W': /* what to do */ case 'w': lastdump(ch); exit(X_FINOK); /* do nothing else */ default: usage(); } argc -= optind; argv += optind; if (argc < 1) { (void)fprintf(stderr, "Must specify disk or file system\n"); exit(X_STARTUP); } disk = *argv++; argc--; if (argc >= 1) { (void)fprintf(stderr, "Unknown arguments to dump:"); while (argc--) (void)fprintf(stderr, " %s", *argv++); (void)fprintf(stderr, "\n"); exit(X_STARTUP); } if (rsync_friendly && (level > 0)) { (void)fprintf(stderr, "%s %s\n", "rsync friendly options", "can be used only with level 0 dumps."); exit(X_STARTUP); } if (Tflag && uflag) { (void)fprintf(stderr, "You cannot use the T and u flags together.\n"); exit(X_STARTUP); } if (popenout) { tape = "child pipeline process"; } else if (tape == NULL && (tape = getenv("TAPE")) == NULL) tape = _PATH_DEFTAPE; if (strcmp(tape, "-") == 0) { pipeout++; tape = "standard output"; } if (blocksperfile) blocksperfile = rounddown(blocksperfile, ntrec); else if (!unlimited) { /* * Determine how to default tape size and density * * density tape size * 9-track 1600 bpi (160 bytes/.1") 2300 ft. * 9-track 6250 bpi (625 bytes/.1") 2300 ft. * cartridge 8000 bpi (100 bytes/.1") 1700 ft. * (450*4 - slop) * hilit19 hits again: " */ if (density == 0) density = cartridge ? 100 : 160; if (tsize == 0) tsize = cartridge ? 1700L*120L : 2300L*120L; } if (strchr(tape, ':')) { host = tape; tape = strchr(host, ':'); *tape++ = '\0'; #ifdef RDUMP if (strchr(tape, '\n')) { (void)fprintf(stderr, "invalid characters in tape\n"); exit(X_STARTUP); } if (rmthost(host) == 0) exit(X_STARTUP); #else (void)fprintf(stderr, "remote dump not enabled\n"); exit(X_STARTUP); #endif } (void)setuid(getuid()); /* rmthost() is the only reason to be setuid */ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, sig); if (signal(SIGTRAP, SIG_IGN) != SIG_IGN) signal(SIGTRAP, sig); if (signal(SIGFPE, SIG_IGN) != SIG_IGN) signal(SIGFPE, sig); if (signal(SIGBUS, SIG_IGN) != SIG_IGN) signal(SIGBUS, sig); if (signal(SIGSEGV, SIG_IGN) != SIG_IGN) signal(SIGSEGV, sig); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, sig); if (signal(SIGINT, interrupt) == SIG_IGN) signal(SIGINT, SIG_IGN); dump_getfstab(); /* /etc/fstab snarfed */ /* * disk can be either the full special file name, * the suffix of the special file name, * the special name missing the leading '/', * the file system name with or without the leading '/'. */ dt = fstabsearch(disk); if (dt != NULL) { disk = rawname(dt->fs_spec); if (disk == NULL) errx(X_STARTUP, "%s: unknown file system", dt->fs_spec); (void)strncpy(spcl.c_dev, dt->fs_spec, NAMELEN); (void)strncpy(spcl.c_filesys, dt->fs_file, NAMELEN); } else { (void)strncpy(spcl.c_dev, disk, NAMELEN); (void)strncpy(spcl.c_filesys, "an unlisted file system", NAMELEN); } spcl.c_dev[NAMELEN-1]='\0'; spcl.c_filesys[NAMELEN-1]='\0'; if ((mntpt = getmntpt(disk, &mntflags)) != NULL) { if (mntflags & MNT_RDONLY) { if (snapdump != 0) { msg("WARNING: %s\n", "-L ignored for read-only filesystem."); snapdump = 0; } } else if (snapdump == 0) { msg("WARNING: %s\n", "should use -L when dumping live read-write " "filesystems!"); } else { char snapname[BUFSIZ], snapcmd[BUFSIZ]; snprintf(snapname, sizeof snapname, "%s/.snap", mntpt); if ((stat(snapname, &sb) < 0) || !S_ISDIR(sb.st_mode)) { msg("WARNING: %s %s\n", "-L requested but snapshot location", snapname); msg(" %s: %s\n", "is not a directory", "dump downgraded, -L ignored"); snapdump = 0; } else { snprintf(snapname, sizeof snapname, "%s/.snap/dump_snapshot", mntpt); snprintf(snapcmd, sizeof snapcmd, "%s %s %s", _PATH_MKSNAP_FFS, mntpt, snapname); unlink(snapname); if (system(snapcmd) != 0) errx(X_STARTUP, "Cannot create %s: %s\n", snapname, strerror(errno)); if ((diskfd = open(snapname, O_RDONLY)) < 0) { unlink(snapname); errx(X_STARTUP, "Cannot open %s: %s\n", snapname, strerror(errno)); } unlink(snapname); if (fstat(diskfd, &sb) != 0) err(X_STARTUP, "%s: stat", snapname); spcl.c_date = _time_to_time64(sb.st_mtime); } } } else if (snapdump != 0) { msg("WARNING: Cannot use -L on an unmounted filesystem.\n"); snapdump = 0; } if (snapdump == 0) { if ((diskfd = open(disk, O_RDONLY)) < 0) err(X_STARTUP, "Cannot open %s", disk); if (fstat(diskfd, &sb) != 0) err(X_STARTUP, "%s: stat", disk); if (S_ISDIR(sb.st_mode)) errx(X_STARTUP, "%s: unknown file system", disk); } (void)strcpy(spcl.c_label, "none"); (void)gethostname(spcl.c_host, NAMELEN); spcl.c_level = level; spcl.c_type = TS_TAPE; if (rsync_friendly) { /* don't store real dump times */ spcl.c_date = 0; spcl.c_ddate = 0; } if (spcl.c_date == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_date); tmsg = ctime(&t); } msg("Date of this level %d dump: %s", level, tmsg); if (!Tflag && (!rsync_friendly)) getdumptime(); /* /etc/dumpdates snarfed */ if (spcl.c_ddate == 0) { tmsg = "the epoch\n"; } else { time_t t = _time64_to_time(spcl.c_ddate); tmsg = ctime(&t); } if (lastlevel < 0) msg("Date of last (level unknown) dump: %s", tmsg); else msg("Date of last level %d dump: %s", lastlevel, tmsg); msg("Dumping %s%s ", snapdump ? "snapshot of ": "", disk); if (dt != NULL) msgtail("(%s) ", dt->fs_file); if (host) msgtail("to %s on host %s\n", tape, host); else msgtail("to %s\n", tape); sync(); if ((ret = sbget(diskfd, &sblock, STDSB)) != 0) { switch (ret) { case ENOENT: warn("Cannot find file system superblock"); return (1); default: warn("Unable to read file system superblock"); return (1); } } dev_bsize = sblock->fs_fsize / fsbtodb(sblock, 1); dev_bshift = ffs(dev_bsize) - 1; if (dev_bsize != (1 << dev_bshift)) quit("dev_bsize (%ld) is not a power of 2", dev_bsize); tp_bshift = ffs(TP_BSIZE) - 1; if (TP_BSIZE != (1 << tp_bshift)) quit("TP_BSIZE (%d) is not a power of 2", TP_BSIZE); maxino = sblock->fs_ipg * sblock->fs_ncg; mapsize = roundup(howmany(maxino, CHAR_BIT), TP_BSIZE); usedinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpdirmap = (char *)calloc((unsigned) mapsize, sizeof(char)); dumpinomap = (char *)calloc((unsigned) mapsize, sizeof(char)); tapesize = 3 * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); nonodump = spcl.c_level < honorlevel; passno = 1; setproctitle("%s: pass 1: regular files", disk); msg("mapping (Pass I) [regular files]\n"); anydirskipped = mapfiles(maxino, &tapesize); passno = 2; setproctitle("%s: pass 2: directories", disk); msg("mapping (Pass II) [directories]\n"); while (anydirskipped) { anydirskipped = mapdirs(maxino, &tapesize); } if (pipeout || unlimited) { tapesize += 10; /* 10 trailer blocks */ msg("estimated %ld tape blocks.\n", tapesize); } else { double fetapes; if (blocksperfile) fetapes = (double) tapesize / blocksperfile; else if (cartridge) { /* Estimate number of tapes, assuming streaming stops at the end of each block written, and not in mid-block. Assume no erroneous blocks; this can be compensated for with an artificially low tape size. */ fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes/block */ * (1.0/density) /* 0.1" / byte " */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* streaming-stops per block */ * 15.48 /* 0.1" / streaming-stop " */ ) * (1.0 / tsize ); /* tape / 0.1" " */ } else { /* Estimate number of tapes, for old fashioned 9-track tape */ int tenthsperirg = (density == 625) ? 3 : 7; fetapes = ( (double) tapesize /* blocks */ * TP_BSIZE /* bytes / block */ * (1.0/density) /* 0.1" / byte " */ + (double) tapesize /* blocks */ * (1.0/ntrec) /* IRG's / block */ * tenthsperirg /* 0.1" / IRG " */ ) * (1.0 / tsize ); /* tape / 0.1" " */ } etapes = fetapes; /* truncating assignment */ etapes++; /* count the dumped inodes map on each additional tape */ tapesize += (etapes - 1) * (howmany(mapsize * sizeof(char), TP_BSIZE) + 1); tapesize += etapes + 10; /* headers + 10 trailer blks */ msg("estimated %ld tape blocks on %3.2f tape(s).\n", tapesize, fetapes); } /* * If the user only wants an estimate of the number of * tapes, exit now. */ if (just_estimate) exit(0); /* * Allocate tape buffer. */ if (!alloctape()) quit( "can't allocate tape buffers - try a smaller blocking factor.\n"); startnewtape(1); (void)time((time_t *)&(tstart_writing)); dumpmap(usedinomap, TS_CLRI, maxino - 1); passno = 3; setproctitle("%s: pass 3: directories", disk); msg("dumping (Pass III) [directories]\n"); dirty = 0; /* XXX just to get gcc to shut up */ for (map = dumpdirmap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip directory inodes deleted and maybe reallocated */ dp = getino(ino, &mode); if (mode != IFDIR) continue; (void)dumpino(dp, ino); } passno = 4; setproctitle("%s: pass 4: regular files", disk); msg("dumping (Pass IV) [regular files]\n"); for (map = dumpinomap, ino = 1; ino < maxino; ino++) { if (((ino - 1) % CHAR_BIT) == 0) /* map is offset by 1 */ dirty = *map++; else dirty >>= 1; if ((dirty & 1) == 0) continue; /* * Skip inodes deleted and reallocated as directories. */ dp = getino(ino, &mode); if (mode == IFDIR) continue; (void)dumpino(dp, ino); } (void)time((time_t *)&(tend_writing)); spcl.c_type = TS_END; for (i = 0; i < ntrec; i++) writeheader(maxino - 1); if (pipeout) msg("DUMP: %jd tape blocks\n", (intmax_t)spcl.c_tapea); else msg("DUMP: %jd tape blocks on %d volume%s\n", (intmax_t)spcl.c_tapea, spcl.c_volume, (spcl.c_volume == 1) ? "" : "s"); /* report dump performance, avoid division through zero */ if (tend_writing - tstart_writing == 0) msg("finished in less than a second\n"); else msg("finished in %jd seconds, throughput %jd KBytes/sec\n", (intmax_t)tend_writing - tstart_writing, (intmax_t)(spcl.c_tapea / (tend_writing - tstart_writing))); putdumptime(); trewind(); broadcast("DUMP IS DONE!\a\a\n"); msg("DUMP IS DONE\n"); Exit(X_FINOK); /* NOTREACHED */ } static void usage(void) { fprintf(stderr, "usage: dump [-0123456789acLnSu] [-B records] [-b blocksize] [-C cachesize]\n" " [-D dumpdates] [-d density] [-f file | -P pipecommand] [-h level]\n" " [-s feet] [-T date] filesystem\n" " dump -W | -w\n"); exit(X_STARTUP); } /* * Check to see if a disk is currently mounted. */ static char * getmntpt(char *name, int *mntflagsp) { long mntsize, i; struct statfs *mntbuf; mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); for (i = 0; i < mntsize; i++) { if (!strcmp(mntbuf[i].f_mntfromname, name)) { *mntflagsp = mntbuf[i].f_flags; return (mntbuf[i].f_mntonname); } } return (0); } /* * Pick up a numeric argument. It must be nonnegative and in the given * range (except that a vmax of 0 means unlimited). */ static long numarg(const char *meaning, long vmin, long vmax) { char *p; long val; val = strtol(optarg, &p, 10); if (*p) errx(1, "illegal %s -- %s", meaning, optarg); if (val < vmin || (vmax && val > vmax)) errx(1, "%s must be between %ld and %ld", meaning, vmin, vmax); return (val); } void sig(int signo) { switch(signo) { case SIGALRM: case SIGBUS: case SIGFPE: case SIGHUP: case SIGTERM: case SIGTRAP: if (pipeout) quit("Signal on pipe: cannot recover\n"); msg("Rewriting attempted as response to unknown signal.\n"); (void)fflush(stderr); (void)fflush(stdout); close_rewind(); exit(X_REWRITE); /* NOTREACHED */ case SIGSEGV: msg("SIGSEGV: ABORTING!\n"); (void)signal(SIGSEGV, SIG_DFL); (void)kill(0, SIGSEGV); /* NOTREACHED */ } } char * rawname(char *cp) { struct stat sb; /* * Ensure that the device passed in is a raw device. */ if (stat(cp, &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFCHR) return (cp); /* * Since there's only one device type now, we can't construct any * better name, so we have to return NULL. */ return (NULL); } /* * obsolete -- * Change set of key letters and ordered arguments into something * getopt(3) will like. */ static void obsolete(int *argcp, char **argvp[]) { int argc, flags; char *ap, **argv, *flagsp, **nargv, *p; /* Setup. */ argv = *argvp; argc = *argcp; /* * Return if no arguments or first argument has leading * dash or slash. */ ap = argv[1]; if (argc == 1 || *ap == '-' || *ap == '/') return; /* Allocate space for new arguments. */ if ((*argvp = nargv = malloc((argc + 1) * sizeof(char *))) == NULL || (p = flagsp = malloc(strlen(ap) + 2)) == NULL) err(1, NULL); *nargv++ = *argv; argv += 2; for (flags = 0; *ap; ++ap) { switch (*ap) { case 'B': case 'b': case 'd': case 'f': case 'D': case 'C': case 'h': case 's': case 'T': if (*argv == NULL) { warnx("option requires an argument -- %c", *ap); usage(); } if ((nargv[0] = malloc(strlen(*argv) + 2 + 1)) == NULL) err(1, NULL); nargv[0][0] = '-'; nargv[0][1] = *ap; (void)strcpy(&nargv[0][2], *argv); ++argv; ++nargv; break; default: if (!flags) { *p++ = '-'; flags = 1; } *p++ = *ap; break; } } /* Terminate flags. */ if (flags) { *p = '\0'; *nargv++ = flagsp; } else free(flagsp); /* Copy remaining arguments. */ while ((*nargv++ = *argv++)); /* Update argument count. */ *argcp = nargv - *argvp - 1; } Index: head/sbin/dump/pathnames.h =================================================================== --- head/sbin/dump/pathnames.h (revision 359626) +++ head/sbin/dump/pathnames.h (revision 359627) @@ -1,41 +1,40 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 * $FreeBSD$ */ #include #define _PATH_DEFTAPE "/dev/sa0" -#define _PATH_DTMP "/etc/dtmp" #define _PATH_DUMPDATES "/etc/dumpdates" #define _PATH_LOCK "/tmp/dumplockXXXXXX" #define _PATH_RMT "/etc/rmt" /* path on remote host */ Index: head/sbin/dump/tape.c =================================================================== --- head/sbin/dump/tape.c (revision 359626) +++ head/sbin/dump/tape.c (revision 359627) @@ -1,885 +1,887 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)tape.c 8.4 (Berkeley) 5/1/95"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dump.h" -int writesize; /* size of malloc()ed buffer for tape */ -int64_t lastspclrec = -1; /* tape block number of last written header */ -int trecno = 0; /* next record to write in current block */ -extern long blocksperfile; /* number of blocks per output file */ -long blocksthisvol; /* number of blocks on current output file */ -extern int ntrec; /* blocking factor on tape */ -extern int cartridge; -extern char *host; -char *nexttape; -FILE *popenfp = NULL; +ino_t curino; /* current inumber; used globally */ +int newtape; /* new tape flag */ +union u_spcl u_spcl; /* mapping of variables in a control block */ +static int tapefd; /* tape file descriptor */ +static long asize; /* number of 0.1" units written on cur tape */ +static int writesize; /* size of malloc()ed buffer for tape */ +static int64_t lastspclrec = -1; /* tape block number of last written header */ +static int trecno = 0; /* next record to write in current block */ +static long blocksthisvol; /* number of blocks on current output file */ +static char *nexttape; +static FILE *popenfp = NULL; + static int atomic(ssize_t (*)(), int, char *, int); static void doslave(int, int); static void enslave(void); static void flushtape(void); static void killall(void); static void rollforward(void); /* * Concurrent dump mods (Caltech) - disk block reading and tape writing * are exported to several slave processes. While one slave writes the * tape, the others read disk blocks; they pass control of the tape in * a ring via signals. The parent process traverses the file system and * sends writeheader()'s and lists of daddr's to the slaves via pipes. * The following structure defines the instruction packets sent to slaves. */ struct req { ufs2_daddr_t dblk; int count; }; -int reqsiz; +static int reqsiz; #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ -struct slave { +static struct slave { int64_t tapea; /* header number at start of this chunk */ int64_t firstrec; /* record number of this block */ int count; /* count to next header (used for TS_TAPE */ /* after EOT) */ int inode; /* inode that we are currently dealing with */ int fd; /* FD for this slave */ int pid; /* PID for this slave */ int sent; /* 1 == we've sent this slave requests */ char (*tblock)[TP_BSIZE]; /* buffer for data blocks */ struct req *req; /* buffer for requests */ } slaves[SLAVES+1]; -struct slave *slp; +static struct slave *slp; -char (*nextblock)[TP_BSIZE]; +static char (*nextblock)[TP_BSIZE]; -int master; /* pid of master, for sending error signals */ -int tenths; /* length of tape used per block written */ +static int master; /* pid of master, for sending error signals */ +static int tenths; /* length of tape used per block written */ static volatile sig_atomic_t caught; /* have we caught the signal to proceed? */ static volatile sig_atomic_t ready; /* reached the lock point without having */ /* received the SIGUSR2 signal from the prev slave? */ static jmp_buf jmpbuf; /* where to jump to if we are ready when the */ /* SIGUSR2 arrives from the previous slave */ int alloctape(void) { int pgoff = getpagesize() - 1; char *buf; int i; writesize = ntrec * TP_BSIZE; reqsiz = (ntrec + 1) * sizeof(struct req); /* * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require * repositioning after stopping, i.e, streaming mode, where the gap is * variable, 0.30" to 0.45". The gap is maximal when the tape stops. */ if (blocksperfile == 0 && !unlimited) tenths = writesize / density + (cartridge ? 16 : density == 625 ? 5 : 8); /* * Allocate tape buffer contiguous with the array of instruction * packets, so flushtape() can write them together with one write(). * Align tape buffer on page boundary to speed up tape write(). */ for (i = 0; i <= SLAVES; i++) { buf = (char *) malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE)); if (buf == NULL) return(0); slaves[i].tblock = (char (*)[TP_BSIZE]) (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1; } slp = &slaves[0]; slp->count = 1; slp->tapea = 0; slp->firstrec = 0; nextblock = slp->tblock; return(1); } void writerec(char *dp, int isspcl) { slp->req[trecno].dblk = (ufs2_daddr_t)0; slp->req[trecno].count = 1; /* Can't do a structure assignment due to alignment problems */ bcopy(dp, *(nextblock)++, sizeof (union u_spcl)); if (isspcl) lastspclrec = spcl.c_tapea; trecno++; spcl.c_tapea++; if (trecno >= ntrec) flushtape(); } void dumpblock(ufs2_daddr_t blkno, int size) { int avail, tpblks; ufs2_daddr_t dblkno; dblkno = fsbtodb(sblock, blkno); tpblks = size >> tp_bshift; while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { slp->req[trecno].dblk = dblkno; slp->req[trecno].count = avail; trecno += avail; spcl.c_tapea += avail; if (trecno >= ntrec) flushtape(); dblkno += avail << (tp_bshift - dev_bshift); tpblks -= avail; } } int nogripe = 0; void tperror(int signo __unused) { if (pipeout) { msg("write error on %s\n", tape); quit("Cannot recover\n"); /* NOTREACHED */ } msg("write error %ld blocks into volume %d\n", blocksthisvol, tapeno); broadcast("DUMP WRITE ERROR!\n"); if (!query("Do you want to restart?")) dumpabort(0); msg("Closing this volume. Prepare to restart with new media;\n"); msg("this dump volume will be rewritten.\n"); killall(); nogripe = 1; close_rewind(); Exit(X_REWRITE); } void sigpipe(int signo __unused) { quit("Broken pipe\n"); } static void flushtape(void) { int i, blks, got; int64_t lastfirstrec; int siz = (char *)nextblock - (char *)slp->req; slp->req[trecno].count = 0; /* Sentinel */ if (atomic(write, slp->fd, (char *)slp->req, siz) != siz) quit("error writing command pipe: %s\n", strerror(errno)); slp->sent = 1; /* we sent a request, read the response later */ lastfirstrec = slp->firstrec; if (++slp >= &slaves[SLAVES]) slp = &slaves[0]; /* Read results back from next slave */ if (slp->sent) { if (atomic(read, slp->fd, (char *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } slp->sent = 0; /* Check for end of tape */ if (got < writesize) { msg("End of tape detected\n"); /* * Drain the results, don't care what the values were. * If we read them here then trewind won't... */ for (i = 0; i < SLAVES; i++) { if (slaves[i].sent) { if (atomic(read, slaves[i].fd, (char *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } slaves[i].sent = 0; } } close_rewind(); rollforward(); return; } } blks = 0; if (spcl.c_type != TS_END && spcl.c_type != TS_CLRI && spcl.c_type != TS_BITS) { assert(spcl.c_count <= TP_NINDIR); for (i = 0; i < spcl.c_count; i++) if (spcl.c_addr[i] != 0) blks++; } slp->count = lastspclrec + blks + 1 - spcl.c_tapea; slp->tapea = spcl.c_tapea; slp->firstrec = lastfirstrec + ntrec; slp->inode = curino; nextblock = slp->tblock; trecno = 0; asize += tenths; blockswritten += ntrec; blocksthisvol += ntrec; if (!pipeout && !unlimited && (blocksperfile ? (blocksthisvol >= blocksperfile) : (asize > tsize))) { close_rewind(); startnewtape(0); } timeest(); } void trewind(void) { struct stat sb; int f; int got; for (f = 0; f < SLAVES; f++) { /* * Drain the results, but unlike EOT we DO (or should) care * what the return values were, since if we detect EOT after * we think we've written the last blocks to the tape anyway, * we have to replay those blocks with rollforward. * * fixme: punt for now. */ if (slaves[f].sent) { if (atomic(read, slaves[f].fd, (char *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } slaves[f].sent = 0; if (got != writesize) { msg("EOT detected in last 2 tape records!\n"); msg("Use a longer tape, decrease the size estimate\n"); quit("or use no size estimate at all.\n"); } } (void) close(slaves[f].fd); } while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ /* void */; if (pipeout) return; msg("Closing %s\n", tape); if (popenout) { tapefd = -1; (void)pclose(popenfp); popenfp = NULL; return; } #ifdef RDUMP if (host) { rmtclose(); while (rmtopen(tape, 0) < 0) sleep(10); rmtclose(); return; } #endif if (fstat(tapefd, &sb) == 0 && S_ISFIFO(sb.st_mode)) { (void)close(tapefd); return; } (void) close(tapefd); while ((f = open(tape, 0)) < 0) sleep (10); (void) close(f); } void close_rewind() { time_t tstart_changevol, tend_changevol; trewind(); if (nexttape) return; (void)time((time_t *)&(tstart_changevol)); if (!nogripe) { msg("Change Volumes: Mount volume #%d\n", tapeno+1); broadcast("CHANGE DUMP VOLUMES!\a\a\n"); } while (!query("Is the new volume mounted and ready to go?")) if (query("Do you want to abort?")) { dumpabort(0); /*NOTREACHED*/ } (void)time((time_t *)&(tend_changevol)); if ((tstart_changevol != (time_t)-1) && (tend_changevol != (time_t)-1)) tstart_writing += (tend_changevol - tstart_changevol); } void rollforward(void) { struct req *p, *q, *prev; struct slave *tslp; int i, size, got; int64_t savedtapea; union u_spcl *ntb, *otb; tslp = &slaves[SLAVES]; ntb = (union u_spcl *)tslp->tblock[1]; /* * Each of the N slaves should have requests that need to * be replayed on the next tape. Use the extra slave buffers * (slaves[SLAVES]) to construct request lists to be sent to * each slave in turn. */ for (i = 0; i < SLAVES; i++) { q = &tslp->req[1]; otb = (union u_spcl *)slp->tblock; /* * For each request in the current slave, copy it to tslp. */ prev = NULL; for (p = slp->req; p->count > 0; p += p->count) { *q = *p; if (p->dblk == 0) *ntb++ = *otb++; /* copy the datablock also */ prev = q; q += q->count; } if (prev == NULL) quit("rollforward: protocol botch"); if (prev->dblk != 0) prev->count -= 1; else ntb--; q -= 1; q->count = 0; q = &tslp->req[0]; if (i == 0) { q->dblk = 0; q->count = 1; trecno = 0; nextblock = tslp->tblock; savedtapea = spcl.c_tapea; spcl.c_tapea = slp->tapea; startnewtape(0); spcl.c_tapea = savedtapea; lastspclrec = savedtapea - 1; } size = (char *)ntb - (char *)q; if (atomic(write, slp->fd, (char *)q, size) != size) { perror(" DUMP: error writing command pipe"); dumpabort(0); } slp->sent = 1; if (++slp >= &slaves[SLAVES]) slp = &slaves[0]; q->count = 1; if (prev->dblk != 0) { /* * If the last one was a disk block, make the * first of this one be the last bit of that disk * block... */ q->dblk = prev->dblk + prev->count * (TP_BSIZE / DEV_BSIZE); ntb = (union u_spcl *)tslp->tblock; } else { /* * It wasn't a disk block. Copy the data to its * new location in the buffer. */ q->dblk = 0; *((union u_spcl *)tslp->tblock) = *ntb; ntb = (union u_spcl *)tslp->tblock[1]; } } slp->req[0] = *q; nextblock = slp->tblock; if (q->dblk == 0) nextblock++; trecno = 1; /* * Clear the first slaves' response. One hopes that it * worked ok, otherwise the tape is much too short! */ if (slp->sent) { if (atomic(read, slp->fd, (char *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } slp->sent = 0; if (got != writesize) { quit("EOT detected at start of the tape!\n"); } } } /* * We implement taking and restoring checkpoints on the tape level. * When each tape is opened, a new process is created by forking; this * saves all of the necessary context in the parent. The child * continues the dump; the parent waits around, saving the context. * If the child returns X_REWRITE, then it had problems writing that tape; * this causes the parent to fork again, duplicating the context, and * everything continues as if nothing had happened. */ void startnewtape(int top) { int parentpid; int childpid; int status; char *p; sig_t interrupt_save; interrupt_save = signal(SIGINT, SIG_IGN); parentpid = getpid(); restore_check_point: (void)signal(SIGINT, interrupt_save); /* * All signals are inherited... */ setproctitle(NULL); /* Restore the proctitle. */ childpid = fork(); if (childpid < 0) { msg("Context save fork fails in parent %d\n", parentpid); Exit(X_ABORT); } if (childpid != 0) { /* * PARENT: * save the context by waiting * until the child doing all of the work returns. * don't catch the interrupt */ signal(SIGINT, SIG_IGN); #ifdef TDEBUG msg("Tape: %d; parent process: %d child process %d\n", tapeno+1, parentpid, childpid); #endif /* TDEBUG */ if (waitpid(childpid, &status, 0) == -1) msg("Waiting for child %d: %s\n", childpid, strerror(errno)); if (status & 0xFF) { msg("Child %d returns LOB status %o\n", childpid, status&0xFF); } status = (status >> 8) & 0xFF; #ifdef TDEBUG switch(status) { case X_FINOK: msg("Child %d finishes X_FINOK\n", childpid); break; case X_ABORT: msg("Child %d finishes X_ABORT\n", childpid); break; case X_REWRITE: msg("Child %d finishes X_REWRITE\n", childpid); break; default: msg("Child %d finishes unknown %d\n", childpid, status); break; } #endif /* TDEBUG */ switch(status) { case X_FINOK: Exit(X_FINOK); case X_ABORT: Exit(X_ABORT); case X_REWRITE: goto restore_check_point; default: msg("Bad return code from dump: %d\n", status); Exit(X_ABORT); } /*NOTREACHED*/ } else { /* we are the child; just continue */ #ifdef TDEBUG sleep(4); /* allow time for parent's message to get out */ msg("Child on Tape %d has parent %d, my pid = %d\n", tapeno+1, parentpid, getpid()); #endif /* TDEBUG */ /* * If we have a name like "/dev/rmt0,/dev/rmt1", * use the name before the comma first, and save * the remaining names for subsequent volumes. */ tapeno++; /* current tape sequence */ if (nexttape || strchr(tape, ',')) { if (nexttape && *nexttape) tape = nexttape; if ((p = strchr(tape, ',')) != NULL) { *p = '\0'; nexttape = p + 1; } else nexttape = NULL; msg("Dumping volume %d on %s\n", tapeno, tape); } if (pipeout) { tapefd = STDOUT_FILENO; } else if (popenout) { char volno[sizeof("2147483647")]; (void)sprintf(volno, "%d", spcl.c_volume + 1); if (setenv("DUMP_VOLUME", volno, 1) == -1) { msg("Cannot set $DUMP_VOLUME.\n"); dumpabort(0); } popenfp = popen(popenout, "w"); if (popenfp == NULL) { msg("Cannot open output pipeline \"%s\".\n", popenout); dumpabort(0); } tapefd = fileno(popenfp); } else { #ifdef RDUMP while ((tapefd = (host ? rmtopen(tape, 2) : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) #else while ((tapefd = open(tape, O_WRONLY|O_CREAT, 0666)) < 0) #endif { msg("Cannot open output \"%s\".\n", tape); if (!query("Do you want to retry the open?")) dumpabort(0); } } enslave(); /* Share open tape file descriptor with slaves */ if (popenout) close(tapefd); /* Give up our copy of it. */ signal(SIGINFO, infosch); asize = 0; blocksthisvol = 0; if (top) newtape++; /* new tape signal */ spcl.c_count = slp->count; /* * measure firstrec in TP_BSIZE units since restore doesn't * know the correct ntrec value... */ spcl.c_firstrec = slp->firstrec; spcl.c_volume++; spcl.c_type = TS_TAPE; writeheader((ino_t)slp->inode); if (tapeno > 1) msg("Volume %d begins with blocks from inode %d\n", tapeno, slp->inode); } } void dumpabort(int signo __unused) { if (master != 0 && master != getpid()) /* Signals master to call dumpabort */ (void) kill(master, SIGTERM); else { killall(); msg("The ENTIRE dump is aborted.\n"); } #ifdef RDUMP rmtclose(); #endif Exit(X_ABORT); } void Exit(status) int status; { #ifdef TDEBUG msg("pid = %d exits with status %d\n", getpid(), status); #endif /* TDEBUG */ exit(status); } /* * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. */ void proceed(int signo __unused) { if (ready) longjmp(jmpbuf, 1); caught++; } void enslave(void) { int cmd[2]; int i, j; master = getpid(); signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ signal(SIGPIPE, sigpipe); signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */ for (i = 0; i < SLAVES; i++) { if (i == slp - &slaves[0]) { caught = 1; } else { caught = 0; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || (slaves[i].pid = fork()) < 0) quit("too many slaves, %d (recompile smaller): %s\n", i, strerror(errno)); slaves[i].fd = cmd[1]; slaves[i].sent = 0; if (slaves[i].pid == 0) { /* Slave starts up here */ for (j = 0; j <= i; j++) (void) close(slaves[j].fd); signal(SIGINT, SIG_IGN); /* Master handles this */ doslave(cmd[0], i); Exit(X_FINOK); } } for (i = 0; i < SLAVES; i++) (void) atomic(write, slaves[i].fd, (char *) &slaves[(i + 1) % SLAVES].pid, sizeof slaves[0].pid); master = 0; } void killall(void) { int i; for (i = 0; i < SLAVES; i++) if (slaves[i].pid > 0) { (void) kill(slaves[i].pid, SIGKILL); slaves[i].sent = 0; } } /* * Synchronization - each process has a lockfile, and shares file * descriptors to the following process's lockfile. When our write * completes, we release our lock on the following process's lock- * file, allowing the following process to lock it and proceed. We * get the lock back for the next cycle by swapping descriptors. */ static void doslave(int cmd, int slave_number) { int nread; int nextslave, size, wrote, eot_count; /* * Need our own seek pointer. */ (void) close(diskfd); if ((diskfd = open(disk, O_RDONLY)) < 0) quit("slave couldn't reopen disk: %s\n", strerror(errno)); /* * Need the pid of the next slave in the loop... */ if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave)) != sizeof nextslave) { quit("master/slave protocol botched - didn't get pid of next slave.\n"); } /* * Get list of blocks to dump, read the blocks into tape buffer */ while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) { struct req *p = slp->req; for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { if (p->dblk) { blkread(p->dblk, slp->tblock[trecno], p->count * TP_BSIZE); } else { if (p->count != 1 || atomic(read, cmd, (char *)slp->tblock[trecno], TP_BSIZE) != TP_BSIZE) quit("master/slave protocol botched.\n"); } } if (setjmp(jmpbuf) == 0) { ready = 1; if (!caught) (void) pause(); } ready = 0; caught = 0; /* Try to write the data... */ eot_count = 0; size = 0; wrote = 0; while (eot_count < 10 && size < writesize) { #ifdef RDUMP if (host) wrote = rmtwrite(slp->tblock[0]+size, writesize-size); else #endif wrote = write(tapefd, slp->tblock[0]+size, writesize-size); #ifdef WRITEDEBUG printf("slave %d wrote %d\n", slave_number, wrote); #endif if (wrote < 0) break; if (wrote == 0) eot_count++; size += wrote; } #ifdef WRITEDEBUG if (size != writesize) printf("slave %d only wrote %d out of %d bytes and gave up.\n", slave_number, size, writesize); #endif /* * Handle ENOSPC as an EOT condition. */ if (wrote < 0 && errno == ENOSPC) { wrote = 0; eot_count++; } if (eot_count > 0) size = 0; if (wrote < 0) { (void) kill(master, SIGUSR1); for (;;) (void) sigpause(0); } else { /* * pass size of write back to master * (for EOT handling) */ (void) atomic(write, cmd, (char *)&size, sizeof size); } /* * If partial write, don't want next slave to go. * Also jolts him awake. */ (void) kill(nextslave, SIGUSR2); } if (nread != 0) quit("error reading command pipe: %s\n", strerror(errno)); } /* * Since a read from a pipe may not return all we asked for, * or a write may not write all we ask if we get a signal, * loop until the count is satisfied (or error). */ static int atomic(ssize_t (*func)(), int fd, char *buf, int count) { int got, need = count; while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) buf += got; return (got < 0 ? got : count - need); } Index: head/sbin/restore/restore.h =================================================================== --- head/sbin/restore/restore.h (revision 359626) +++ head/sbin/restore/restore.h (revision 359627) @@ -1,155 +1,155 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)restore.h 8.3 (Berkeley) 9/13/94 * $FreeBSD$ */ /* * Flags */ extern int bflag; /* set input block size */ extern int dflag; /* print out debugging info */ extern int Dflag; /* degraded mode - try hard to get stuff back */ extern int hflag; /* restore hierarchies */ extern int mflag; /* restore by name instead of inode number */ extern int Nflag; /* do not write the disk */ extern int uflag; /* unlink symlink targets */ extern int vflag; /* print out actions taken */ extern int yflag; /* always try to recover from tape errors */ /* * Global variables */ extern char *dumpmap; /* map of inodes on this dump tape */ extern char *usedinomap; /* map of inodes that are in use on this fs */ extern ino_t maxino; /* highest numbered inode in this file system */ extern long dumpnum; /* location of the dump on this tape */ extern long volno; /* current volume being read */ extern long ntrec; /* number of TP_BSIZE records per tape block */ extern time_t dumptime; /* time that this dump begins */ extern time_t dumpdate; /* time that this dump was made */ extern char command; /* opration being performed */ extern FILE *terminal; /* file descriptor for the terminal input */ extern int Bcvt; /* need byte swapping on inodes and dirs */ extern int oldinofmt; /* reading tape with FreeBSD 1 format inodes */ /* * Each file in the file system is described by one of these entries */ struct entry { char *e_name; /* the current name of this entry */ u_char e_namlen; /* length of this name */ char e_type; /* type of this entry, see below */ short e_flags; /* status flags, see below */ ino_t e_ino; /* inode number in previous file sys */ long e_index; /* unique index (for dumpped table) */ struct entry *e_parent; /* pointer to parent directory (..) */ struct entry *e_sibling; /* next element in this directory (.) */ struct entry *e_links; /* hard links to this inode */ struct entry *e_entries; /* for directories, their entries */ struct entry *e_next; /* hash chain list */ }; /* types */ #define LEAF 1 /* non-directory entry */ #define NODE 2 /* directory entry */ #define LINK 4 /* synthesized type, stripped by addentry */ /* flags */ #define EXTRACT 0x0001 /* entry is to be replaced from the tape */ #define NEW 0x0002 /* a new entry to be extracted */ #define KEEP 0x0004 /* entry is not to change */ #define REMOVED 0x0010 /* entry has been removed */ #define TMPNAME 0x0020 /* entry has been given a temporary name */ #define EXISTED 0x0040 /* directory already existed during extract */ /* * Constants associated with entry structs */ #define HARDLINK 1 #define SYMLINK 2 #define TMPHDR "RSTTMP" /* * The entry describes the next file available on the tape */ -struct context { +extern struct context { short action; /* action being taken on this file */ mode_t mode; /* mode of file */ ino_t ino; /* inumber of file */ uid_t uid; /* file owner */ gid_t gid; /* file group */ int file_flags; /* status flags (chflags) */ int rdev; /* device number of file */ time_t atime_sec; /* access time seconds */ time_t mtime_sec; /* modified time seconds */ time_t birthtime_sec; /* creation time seconds */ int atime_nsec; /* access time nanoseconds */ int mtime_nsec; /* modified time nanoseconds */ int birthtime_nsec; /* creation time nanoseconds */ int extsize; /* size of extended attribute data */ off_t size; /* size of file */ char *name; /* name of file */ } curfile; /* actions */ #define USING 1 /* extracting from the tape */ #define SKIP 2 /* skipping */ #define UNKNOWN 3 /* disposition or starting point is unknown */ /* * Definitions for library routines operating on directories. */ typedef struct rstdirdesc RST_DIR; /* * Flags to setdirmodes. */ #define FORCE 0x0001 /* * Useful macros */ #define TSTINO(ino, map) \ (map[(u_int)((ino) - 1) / CHAR_BIT] & \ (1 << ((u_int)((ino) - 1) % CHAR_BIT))) #define SETINO(ino, map) \ map[(u_int)((ino) - 1) / CHAR_BIT] |= \ 1 << ((u_int)((ino) - 1) % CHAR_BIT) #define dprintf if (dflag) fprintf #define vprintf if (vflag) fprintf #define GOOD 1 #define FAIL 0 #define NFS_DR_NEWINODEFMT 0x2 /* Tape uses 4.4 BSD inode format */ Index: head/sbin/restore/tape.c =================================================================== --- head/sbin/restore/tape.c (revision 359626) +++ head/sbin/restore/tape.c (revision 359627) @@ -1,1702 +1,1704 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)tape.c 8.9 (Berkeley) 5/1/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "restore.h" #include "extern.h" static long fssize = MAXBSIZE; static int mt = -1; static int pipein = 0; static int pipecmdin = 0; static FILE *popenfp = NULL; static char *magtape; static int blkcnt; static int numtrec; static char *tapebuf; static union u_spcl endoftapemark; static long byteslide = 0; static long blksread; /* blocks read since last header */ static int64_t tapeaddr = 0; /* current TP_BSIZE tape record */ static long tapesread; static jmp_buf restart; static int gettingfile = 0; /* restart has a valid frame */ static char *host = NULL; static int readmapflag; static int ofile; static char *map; static char lnkbuf[MAXPATHLEN + 1]; static int pathlen; +struct context curfile; /* describes next file available on the tape */ +union u_spcl u_spcl; /* mapping of variables in a control block */ int Bcvt; /* Swap Bytes */ int oldinofmt; /* FreeBSD 1 inode format needs cvt */ #define FLUSHTAPEBUF() blkcnt = ntrec + 1 char *namespace_names[] = EXTATTR_NAMESPACE_NAMES; static void accthdr(struct s_spcl *); static int checksum(int *); static void findinode(struct s_spcl *); static void findtapeblksize(void); static char *setupextattr(int); static void xtrattr(char *, size_t); static void skiphole(void (*)(char *, size_t), size_t *); static int gethead(struct s_spcl *); static void readtape(char *); static void setdumpnum(void); static u_long swabl(u_long); static u_char *swablong(u_char *, int); static u_char *swabshort(u_char *, int); static void terminateinput(void); static void xtrfile(char *, size_t); static void xtrlnkfile(char *, size_t); static void xtrlnkskip(char *, size_t); static void xtrmap(char *, size_t); static void xtrmapskip(char *, size_t); static void xtrskip(char *, size_t); /* * Set up an input source */ void setinput(char *source, int ispipecommand) { FLUSHTAPEBUF(); if (bflag) newtapebuf(ntrec); else newtapebuf(MAX(NTREC, HIGHDENSITYTREC)); terminal = stdin; if (ispipecommand) pipecmdin++; else #ifdef RRESTORE if (strchr(source, ':')) { host = source; source = strchr(host, ':'); *source++ = '\0'; if (rmthost(host) == 0) done(1); } else #endif if (strcmp(source, "-") == 0) { /* * Since input is coming from a pipe we must establish * our own connection to the terminal. */ terminal = fopen(_PATH_TTY, "r"); if (terminal == NULL) { (void)fprintf(stderr, "cannot open %s: %s\n", _PATH_TTY, strerror(errno)); terminal = fopen(_PATH_DEVNULL, "r"); if (terminal == NULL) { (void)fprintf(stderr, "cannot open %s: %s\n", _PATH_DEVNULL, strerror(errno)); done(1); } } pipein++; } /* no longer need or want root privileges */ if (setuid(getuid()) != 0) { fprintf(stderr, "setuid failed\n"); done(1); } magtape = strdup(source); if (magtape == NULL) { fprintf(stderr, "Cannot allocate space for magtape buffer\n"); done(1); } } void newtapebuf(long size) { static int tapebufsize = -1; ntrec = size; if (size <= tapebufsize) return; if (tapebuf != NULL) free(tapebuf - TP_BSIZE); tapebuf = malloc((size+1) * TP_BSIZE); if (tapebuf == NULL) { fprintf(stderr, "Cannot allocate space for tape buffer\n"); done(1); } tapebuf += TP_BSIZE; tapebufsize = size; } /* * Verify that the tape drive can be accessed and * that it actually is a dump tape. */ void setup(void) { int i, j, *ip; struct stat stbuf; vprintf(stdout, "Verify tape and initialize maps\n"); if (pipecmdin) { if (setenv("RESTORE_VOLUME", "1", 1) == -1) { fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n", strerror(errno)); done(1); } popenfp = popen(magtape, "r"); mt = popenfp ? fileno(popenfp) : -1; } else #ifdef RRESTORE if (host) mt = rmtopen(magtape, 0); else #endif if (pipein) mt = 0; else mt = open(magtape, O_RDONLY, 0); if (mt < 0) { fprintf(stderr, "%s: %s\n", magtape, strerror(errno)); done(1); } volno = 1; setdumpnum(); FLUSHTAPEBUF(); if (!pipein && !pipecmdin && !bflag) findtapeblksize(); if (gethead(&spcl) == FAIL) { fprintf(stderr, "Tape is not a dump tape\n"); done(1); } if (pipein) { endoftapemark.s_spcl.c_magic = FS_UFS2_MAGIC; endoftapemark.s_spcl.c_type = TS_END; ip = (int *)&endoftapemark; j = sizeof(union u_spcl) / sizeof(int); i = 0; do i += *ip++; while (--j); endoftapemark.s_spcl.c_checksum = CHECKSUM - i; } if (vflag || command == 't') printdumpinfo(); dumptime = _time64_to_time(spcl.c_ddate); dumpdate = _time64_to_time(spcl.c_date); if (stat(".", &stbuf) < 0) { fprintf(stderr, "cannot stat .: %s\n", strerror(errno)); done(1); } if (stbuf.st_blksize > 0 && stbuf.st_blksize < TP_BSIZE ) fssize = TP_BSIZE; if (stbuf.st_blksize >= TP_BSIZE && stbuf.st_blksize <= MAXBSIZE) fssize = stbuf.st_blksize; if (((TP_BSIZE - 1) & stbuf.st_blksize) != 0) { fprintf(stderr, "Warning: filesystem with non-multiple-of-%d " "blocksize (%d);\n", TP_BSIZE, stbuf.st_blksize); fssize = roundup(fssize, TP_BSIZE); fprintf(stderr, "\twriting using blocksize %ld\n", fssize); } if (spcl.c_volume != 1) { fprintf(stderr, "Tape is not volume 1 of the dump\n"); done(1); } if (gethead(&spcl) == FAIL) { dprintf(stdout, "header read failed at %ld blocks\n", blksread); panic("no header after volume mark!\n"); } findinode(&spcl); if (spcl.c_type != TS_CLRI) { fprintf(stderr, "Cannot find file removal list\n"); done(1); } maxino = (spcl.c_count * TP_BSIZE * NBBY) + 1; dprintf(stdout, "maxino = %ju\n", (uintmax_t)maxino); map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); if (map == NULL) panic("no memory for active inode map\n"); usedinomap = map; curfile.action = USING; getfile(xtrmap, xtrmapskip, xtrmapskip); if (spcl.c_type != TS_BITS) { fprintf(stderr, "Cannot find file dump list\n"); done(1); } map = calloc((unsigned)1, (unsigned)howmany(maxino, NBBY)); if (map == (char *)NULL) panic("no memory for file dump list\n"); dumpmap = map; curfile.action = USING; getfile(xtrmap, xtrmapskip, xtrmapskip); /* * If there may be whiteout entries on the tape, pretend that the * whiteout inode exists, so that the whiteout entries can be * extracted. */ SETINO(UFS_WINO, dumpmap); /* 'r' restores don't call getvol() for tape 1, so mark it as read. */ if (command == 'r') tapesread = 1; } /* * Prompt user to load a new dump volume. * "Nextvol" is the next suggested volume to use. * This suggested volume is enforced when doing full * or incremental restores, but can be overridden by * the user when only extracting a subset of the files. */ void getvol(long nextvol) { int64_t prevtapea; long i, newvol, savecnt; union u_spcl tmpspcl; # define tmpbuf tmpspcl.s_spcl char buf[TP_BSIZE]; if (nextvol == 1) { tapesread = 0; gettingfile = 0; } prevtapea = tapeaddr; savecnt = blksread; if (pipein) { if (nextvol != 1) { panic("Changing volumes on pipe input?\n"); /* Avoid looping if we couldn't ask the user. */ if (yflag || ferror(terminal) || feof(terminal)) done(1); } if (volno == 1) return; newvol = 0; goto gethdr; } again: if (pipein) done(1); /* pipes do not get a second chance */ if (command == 'R' || command == 'r' || curfile.action != SKIP) newvol = nextvol; else newvol = 0; while (newvol <= 0) { if (tapesread == 0) { fprintf(stderr, "%s%s%s%s%s%s%s", "You have not read any tapes yet.\n", "If you are extracting just a few files,", " start with the last volume\n", "and work towards the first; restore", " can quickly skip tapes that\n", "have no further files to extract.", " Otherwise, begin with volume 1.\n"); } else { fprintf(stderr, "You have read volumes"); strcpy(buf, ": "); for (i = 0; i < 32; i++) if (tapesread & (1 << i)) { fprintf(stderr, "%s%ld", buf, i + 1); strcpy(buf, ", "); } fprintf(stderr, "\n"); } do { fprintf(stderr, "Specify next volume #: "); (void) fflush(stderr); if (fgets(buf, BUFSIZ, terminal) == NULL) done(1); } while (buf[0] == '\n'); newvol = atoi(buf); if (newvol <= 0) { fprintf(stderr, "Volume numbers are positive numerics\n"); } } if (newvol == volno) { tapesread |= 1 << (volno - 1); return; } closemt(); fprintf(stderr, "Mount tape volume %ld\n", newvol); fprintf(stderr, "Enter ``none'' if there are no more tapes\n"); fprintf(stderr, "otherwise enter tape name (default: %s) ", magtape); (void) fflush(stderr); if (fgets(buf, BUFSIZ, terminal) == NULL) done(1); if (!strcmp(buf, "none\n")) { terminateinput(); return; } if (buf[0] != '\n') { (void) strcpy(magtape, buf); magtape[strlen(magtape) - 1] = '\0'; } if (pipecmdin) { char volno[sizeof("2147483647")]; (void)sprintf(volno, "%ld", newvol); if (setenv("RESTORE_VOLUME", volno, 1) == -1) { fprintf(stderr, "Cannot set $RESTORE_VOLUME: %s\n", strerror(errno)); done(1); } popenfp = popen(magtape, "r"); mt = popenfp ? fileno(popenfp) : -1; } else #ifdef RRESTORE if (host) mt = rmtopen(magtape, 0); else #endif mt = open(magtape, O_RDONLY, 0); if (mt == -1) { fprintf(stderr, "Cannot open %s\n", magtape); volno = -1; goto again; } gethdr: volno = newvol; setdumpnum(); FLUSHTAPEBUF(); if (gethead(&tmpbuf) == FAIL) { dprintf(stdout, "header read failed at %ld blocks\n", blksread); fprintf(stderr, "tape is not dump tape\n"); volno = 0; goto again; } if (tmpbuf.c_volume != volno) { fprintf(stderr, "Wrong volume (%jd)\n", (intmax_t)tmpbuf.c_volume); volno = 0; goto again; } if (_time64_to_time(tmpbuf.c_date) != dumpdate || _time64_to_time(tmpbuf.c_ddate) != dumptime) { time_t t = _time64_to_time(tmpbuf.c_date); fprintf(stderr, "Wrong dump date\n\tgot: %s", ctime(&t)); fprintf(stderr, "\twanted: %s", ctime(&dumpdate)); volno = 0; goto again; } tapesread |= 1 << (volno - 1); blksread = savecnt; /* * If continuing from the previous volume, skip over any * blocks read already at the end of the previous volume. * * If coming to this volume at random, skip to the beginning * of the next record. */ dprintf(stdout, "last rec %jd, tape starts with %jd\n", (intmax_t)prevtapea, (intmax_t)tmpbuf.c_tapea); if (tmpbuf.c_type == TS_TAPE) { if (curfile.action != USING) { /* * XXX Dump incorrectly sets c_count to 1 in the * volume header of the first tape, so ignore * c_count when volno == 1. */ if (volno != 1) for (i = tmpbuf.c_count; i > 0; i--) readtape(buf); } else if (tmpbuf.c_tapea <= prevtapea) { /* * Normally the value of c_tapea in the volume * header is the record number of the header itself. * However in the volume header following an EOT- * terminated tape, it is the record number of the * first continuation data block (dump bug?). * * The next record we want is `prevtapea + 1'. */ i = prevtapea + 1 - tmpbuf.c_tapea; dprintf(stderr, "Skipping %ld duplicate record%s.\n", i, i > 1 ? "s" : ""); while (--i >= 0) readtape(buf); } } if (curfile.action == USING) { if (volno == 1) panic("active file into volume 1\n"); return; } (void) gethead(&spcl); findinode(&spcl); if (gettingfile) { gettingfile = 0; longjmp(restart, 1); } } /* * Handle unexpected EOF. */ static void terminateinput(void) { if (gettingfile && curfile.action == USING) { printf("Warning: %s %s\n", "End-of-input encountered while extracting", curfile.name); } curfile.name = ""; curfile.action = UNKNOWN; curfile.mode = 0; curfile.ino = maxino; if (gettingfile) { gettingfile = 0; longjmp(restart, 1); } } /* * handle multiple dumps per tape by skipping forward to the * appropriate one. */ static void setdumpnum(void) { struct mtop tcom; if (dumpnum == 1 || volno != 1) return; if (pipein) { fprintf(stderr, "Cannot have multiple dumps on pipe input\n"); done(1); } tcom.mt_op = MTFSF; tcom.mt_count = dumpnum - 1; #ifdef RRESTORE if (host) rmtioctl(MTFSF, dumpnum - 1); else #endif if (!pipecmdin && ioctl(mt, MTIOCTOP, (char *)&tcom) < 0) fprintf(stderr, "ioctl MTFSF: %s\n", strerror(errno)); } void printdumpinfo(void) { time_t t; t = _time64_to_time(spcl.c_date); fprintf(stdout, "Dump date: %s", ctime(&t)); t = _time64_to_time(spcl.c_ddate); fprintf(stdout, "Dumped from: %s", (spcl.c_ddate == 0) ? "the epoch\n" : ctime(&t)); if (spcl.c_host[0] == '\0') return; fprintf(stderr, "Level %jd dump of %s on %s:%s\n", (intmax_t)spcl.c_level, spcl.c_filesys, spcl.c_host, spcl.c_dev); fprintf(stderr, "Label: %s\n", spcl.c_label); } int extractfile(char *name) { u_int flags; uid_t uid; gid_t gid; mode_t mode; int extsize; struct timespec mtimep[2], ctimep[2]; struct entry *ep; char *buf; curfile.name = name; curfile.action = USING; mtimep[0].tv_sec = curfile.atime_sec; mtimep[0].tv_nsec = curfile.atime_nsec; mtimep[1].tv_sec = curfile.mtime_sec; mtimep[1].tv_nsec = curfile.mtime_nsec; ctimep[0].tv_sec = curfile.atime_sec; ctimep[0].tv_nsec = curfile.atime_nsec; ctimep[1].tv_sec = curfile.birthtime_sec; ctimep[1].tv_nsec = curfile.birthtime_nsec; extsize = curfile.extsize; uid = getuid(); if (uid == 0) uid = curfile.uid; gid = curfile.gid; mode = curfile.mode; flags = curfile.file_flags; switch (mode & IFMT) { default: fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode); skipfile(); return (FAIL); case IFSOCK: vprintf(stdout, "skipped socket %s\n", name); skipfile(); return (GOOD); case IFDIR: if (mflag) { ep = lookupname(name); if (ep == NULL || ep->e_flags & EXTRACT) panic("unextracted directory %s\n", name); skipfile(); return (GOOD); } vprintf(stdout, "extract file %s\n", name); return (genliteraldir(name, curfile.ino)); case IFLNK: lnkbuf[0] = '\0'; pathlen = 0; buf = setupextattr(extsize); getfile(xtrlnkfile, xtrattr, xtrlnkskip); if (pathlen == 0) { vprintf(stdout, "%s: zero length symbolic link (ignored)\n", name); return (GOOD); } if (linkit(lnkbuf, name, SYMLINK) == GOOD) { if (extsize > 0) set_extattr(-1, name, buf, extsize, SXA_LINK); (void) lchown(name, uid, gid); (void) lchmod(name, mode); (void) utimensat(AT_FDCWD, name, ctimep, AT_SYMLINK_NOFOLLOW); (void) utimensat(AT_FDCWD, name, mtimep, AT_SYMLINK_NOFOLLOW); (void) lchflags(name, flags); return (GOOD); } return (FAIL); case IFIFO: vprintf(stdout, "extract fifo %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void) unlink(name); if (mkfifo(name, 0600) < 0) { fprintf(stderr, "%s: cannot create fifo: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } if (extsize == 0) { skipfile(); } else { buf = setupextattr(extsize); getfile(xtrnull, xtrattr, xtrnull); set_extattr(-1, name, buf, extsize, SXA_FILE); } (void) chown(name, uid, gid); (void) chmod(name, mode); (void) utimensat(AT_FDCWD, name, ctimep, 0); (void) utimensat(AT_FDCWD, name, mtimep, 0); (void) chflags(name, flags); return (GOOD); case IFCHR: case IFBLK: vprintf(stdout, "extract special file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void) unlink(name); if (mknod(name, (mode & (IFCHR | IFBLK)) | 0600, (int)curfile.rdev) < 0) { fprintf(stderr, "%s: cannot create special file: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } if (extsize == 0) { skipfile(); } else { buf = setupextattr(extsize); getfile(xtrnull, xtrattr, xtrnull); set_extattr(-1, name, buf, extsize, SXA_FILE); } (void) chown(name, uid, gid); (void) chmod(name, mode); (void) utimensat(AT_FDCWD, name, ctimep, 0); (void) utimensat(AT_FDCWD, name, mtimep, 0); (void) chflags(name, flags); return (GOOD); case IFREG: vprintf(stdout, "extract file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void) unlink(name); if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) { fprintf(stderr, "%s: cannot create file: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } buf = setupextattr(extsize); getfile(xtrfile, xtrattr, xtrskip); if (extsize > 0) set_extattr(ofile, name, buf, extsize, SXA_FD); (void) fchown(ofile, uid, gid); (void) fchmod(ofile, mode); (void) futimens(ofile, ctimep); (void) futimens(ofile, mtimep); (void) fchflags(ofile, flags); (void) close(ofile); return (GOOD); } /* NOTREACHED */ } /* * Set attributes on a file descriptor, link, or file. */ void set_extattr(int fd, char *name, void *buf, int size, enum set_extattr_mode mode) { struct extattr *eap, *eaend; const char *method; ssize_t res; int error; char eaname[EXTATTR_MAXNAMELEN + 1]; vprintf(stdout, "Set attributes for %s:", name); eaend = buf + size; for (eap = buf; eap < eaend; eap = EXTATTR_NEXT(eap)) { /* * Make sure this entry is complete. */ if (EXTATTR_NEXT(eap) > eaend || eap->ea_length <= 0) { dprintf(stdout, "\n\t%scorrupted", eap == buf ? "" : "remainder "); break; } if (eap->ea_namespace == EXTATTR_NAMESPACE_EMPTY) continue; snprintf(eaname, sizeof(eaname), "%.*s", (int)eap->ea_namelength, eap->ea_name); vprintf(stdout, "\n\t%s, (%d bytes), %s", namespace_names[eap->ea_namespace], eap->ea_length, eaname); /* * First we try the general attribute setting interface. * However, some attributes can only be set by root or * by using special interfaces (for example, ACLs). */ if (mode == SXA_FD) { res = extattr_set_fd(fd, eap->ea_namespace, eaname, EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)); method = "extattr_set_fd"; } else if (mode == SXA_LINK) { res = extattr_set_link(name, eap->ea_namespace, eaname, EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)); method = "extattr_set_link"; } else if (mode == SXA_FILE) { res = extattr_set_file(name, eap->ea_namespace, eaname, EXTATTR_CONTENT(eap), EXTATTR_CONTENT_SIZE(eap)); method = "extattr_set_file"; } if (res != -1) { dprintf(stdout, " (set using %s)", method); continue; } /* * If the general interface refuses to set the attribute, * then we try all the specialized interfaces that we * know about. */ if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && strcmp(eaname, POSIX1E_ACL_ACCESS_EXTATTR_NAME) == 0) { if (mode == SXA_FD) { error = acl_set_fd(fd, EXTATTR_CONTENT(eap)); method = "acl_set_fd"; } else if (mode == SXA_LINK) { error = acl_set_link_np(name, ACL_TYPE_ACCESS, EXTATTR_CONTENT(eap)); method = "acl_set_link_np"; } else if (mode == SXA_FILE) { error = acl_set_file(name, ACL_TYPE_ACCESS, EXTATTR_CONTENT(eap)); method = "acl_set_file"; } if (error != -1) { dprintf(stdout, " (set using %s)", method); continue; } } if (eap->ea_namespace == EXTATTR_NAMESPACE_SYSTEM && strcmp(eaname, POSIX1E_ACL_DEFAULT_EXTATTR_NAME) == 0) { if (mode == SXA_LINK) { error = acl_set_link_np(name, ACL_TYPE_DEFAULT, EXTATTR_CONTENT(eap)); method = "acl_set_link_np"; } else { error = acl_set_file(name, ACL_TYPE_DEFAULT, EXTATTR_CONTENT(eap)); method = "acl_set_file"; } if (error != -1) { dprintf(stdout, " (set using %s)", method); continue; } } vprintf(stdout, " (unable to set)"); } vprintf(stdout, "\n"); } /* * skip over bit maps on the tape */ void skipmaps(void) { while (spcl.c_type == TS_BITS || spcl.c_type == TS_CLRI) skipfile(); } /* * skip over a file on the tape */ void skipfile(void) { curfile.action = SKIP; getfile(xtrnull, xtrnull, xtrnull); } /* * Skip a hole in an output file */ static void skiphole(void (*skip)(char *, size_t), size_t *seekpos) { char buf[MAXBSIZE]; if (*seekpos > 0) { (*skip)(buf, *seekpos); *seekpos = 0; } } /* * Extract a file from the tape. * When an allocated block is found it is passed to the fill function; * when an unallocated block (hole) is found, a zeroed buffer is passed * to the skip function. */ void getfile(void (*datafill)(char *, size_t), void (*attrfill)(char *, size_t), void (*skip)(char *, size_t)) { int i; volatile off_t size; size_t seekpos; int curblk, attrsize; void (*fillit)(char *, size_t); char buf[MAXBSIZE / TP_BSIZE][TP_BSIZE]; char junk[TP_BSIZE]; curblk = 0; size = spcl.c_size; seekpos = 0; attrsize = spcl.c_extsize; if (spcl.c_type == TS_END) panic("ran off end of tape\n"); if (spcl.c_magic != FS_UFS2_MAGIC) panic("not at beginning of a file\n"); if (!gettingfile && setjmp(restart) != 0) return; gettingfile++; fillit = datafill; if (size == 0 && attrsize > 0) { fillit = attrfill; size = attrsize; attrsize = 0; } loop: for (i = 0; i < spcl.c_count; i++) { if (!readmapflag && i > TP_NINDIR) { if (Dflag) { fprintf(stderr, "spcl.c_count = %jd\n", (intmax_t)spcl.c_count); break; } else panic("spcl.c_count = %jd\n", (intmax_t)spcl.c_count); } if (readmapflag || spcl.c_addr[i]) { readtape(&buf[curblk++][0]); if (curblk == fssize / TP_BSIZE) { skiphole(skip, &seekpos); (*fillit)((char *)buf, (long)(size > TP_BSIZE ? fssize : (curblk - 1) * TP_BSIZE + size)); curblk = 0; } } else { if (curblk > 0) { skiphole(skip, &seekpos); (*fillit)((char *)buf, (long)(size > TP_BSIZE ? curblk * TP_BSIZE : (curblk - 1) * TP_BSIZE + size)); curblk = 0; } /* * We have a block of a hole. Don't skip it * now, because there may be next adjacent * block of the hole in the file. Postpone the * seek until next file write. */ seekpos += (long)MIN(TP_BSIZE, size); } if ((size -= TP_BSIZE) <= 0) { if (size > -TP_BSIZE && curblk > 0) { skiphole(skip, &seekpos); (*fillit)((char *)buf, (long)((curblk * TP_BSIZE) + size)); curblk = 0; } if (attrsize > 0) { fillit = attrfill; size = attrsize; attrsize = 0; continue; } if (spcl.c_count - i > 1) dprintf(stdout, "skipping %d junk block(s)\n", spcl.c_count - i - 1); for (i++; i < spcl.c_count; i++) { if (!readmapflag && i > TP_NINDIR) { if (Dflag) { fprintf(stderr, "spcl.c_count = %jd\n", (intmax_t)spcl.c_count); break; } else panic("spcl.c_count = %jd\n", (intmax_t)spcl.c_count); } if (readmapflag || spcl.c_addr[i]) readtape(junk); } break; } } if (gethead(&spcl) == GOOD && size > 0) { if (spcl.c_type == TS_ADDR) goto loop; dprintf(stdout, "Missing address (header) block for %s at %ld blocks\n", curfile.name, blksread); } if (curblk > 0) panic("getfile: lost data\n"); findinode(&spcl); gettingfile = 0; } /* * These variables are shared between the next two functions. */ static int extbufsize = 0; static char *extbuf; static int extloc; /* * Allocate a buffer into which to extract extended attributes. */ static char * setupextattr(int extsize) { extloc = 0; if (extsize <= extbufsize) return (extbuf); if (extbufsize > 0) free(extbuf); if ((extbuf = malloc(extsize)) != NULL) { extbufsize = extsize; return (extbuf); } extbufsize = 0; extbuf = NULL; fprintf(stderr, "Cannot extract %d bytes %s for inode %ju, name %s\n", extsize, "of extended attributes", (uintmax_t)curfile.ino, curfile.name); return (NULL); } /* * Extract the next block of extended attributes. */ static void xtrattr(char *buf, size_t size) { if (extloc + size > extbufsize) panic("overrun attribute buffer\n"); memmove(&extbuf[extloc], buf, size); extloc += size; } /* * Write out the next block of a file. */ static void xtrfile(char *buf, size_t size) { if (Nflag) return; if (write(ofile, buf, (int) size) == -1) { fprintf(stderr, "write error extracting inode %ju, name %s\nwrite: %s\n", (uintmax_t)curfile.ino, curfile.name, strerror(errno)); } } /* * Skip over a hole in a file. */ /* ARGSUSED */ static void xtrskip(char *buf, size_t size) { if (lseek(ofile, size, SEEK_CUR) == -1) { fprintf(stderr, "seek error extracting inode %ju, name %s\nlseek: %s\n", (uintmax_t)curfile.ino, curfile.name, strerror(errno)); done(1); } } /* * Collect the next block of a symbolic link. */ static void xtrlnkfile(char *buf, size_t size) { pathlen += size; if (pathlen > MAXPATHLEN) { fprintf(stderr, "symbolic link name: %s->%s%s; too long %d\n", curfile.name, lnkbuf, buf, pathlen); done(1); } (void) strcat(lnkbuf, buf); } /* * Skip over a hole in a symbolic link (should never happen). */ /* ARGSUSED */ static void xtrlnkskip(char *buf, size_t size) { fprintf(stderr, "unallocated block in symbolic link %s\n", curfile.name); done(1); } /* * Collect the next block of a bit map. */ static void xtrmap(char *buf, size_t size) { memmove(map, buf, size); map += size; } /* * Skip over a hole in a bit map (should never happen). */ /* ARGSUSED */ static void xtrmapskip(char *buf, size_t size) { panic("hole in map\n"); map += size; } /* * Noop, when an extraction function is not needed. */ /* ARGSUSED */ void xtrnull(char *buf, size_t size) { return; } /* * Read TP_BSIZE blocks from the input. * Handle read errors, and end of media. */ static void readtape(char *buf) { long rd, newvol, i, oldnumtrec; int cnt, seek_failed; if (blkcnt + (byteslide > 0) < numtrec) { memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE) + byteslide], (long)TP_BSIZE); blksread++; tapeaddr++; return; } if (numtrec > 0) memmove(&tapebuf[-TP_BSIZE], &tapebuf[(numtrec-1) * TP_BSIZE], (long)TP_BSIZE); oldnumtrec = numtrec; for (i = 0; i < ntrec; i++) ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; if (numtrec == 0) numtrec = ntrec; cnt = ntrec * TP_BSIZE; rd = 0; getmore: #ifdef RRESTORE if (host) i = rmtread(&tapebuf[rd], cnt); else #endif i = read(mt, &tapebuf[rd], cnt); /* * Check for mid-tape short read error. * If found, skip rest of buffer and start with the next. */ if (!pipein && !pipecmdin && numtrec < ntrec && i > 0) { dprintf(stdout, "mid-media short read error.\n"); numtrec = ntrec; } /* * Handle partial block read. */ if ((pipein || pipecmdin) && i == 0 && rd > 0) i = rd; else if (i > 0 && i != ntrec * TP_BSIZE) { if (pipein || pipecmdin) { rd += i; cnt -= i; if (cnt > 0) goto getmore; i = rd; } else { /* * Short read. Process the blocks read. */ if (i % TP_BSIZE != 0) vprintf(stdout, "partial block read: %ld should be %ld\n", i, ntrec * TP_BSIZE); numtrec = i / TP_BSIZE; } } /* * Handle read error. */ if (i < 0) { fprintf(stderr, "Tape read error while "); switch (curfile.action) { default: fprintf(stderr, "trying to set up tape\n"); break; case UNKNOWN: fprintf(stderr, "trying to resynchronize\n"); break; case USING: fprintf(stderr, "restoring %s\n", curfile.name); break; case SKIP: fprintf(stderr, "skipping over inode %ju\n", (uintmax_t)curfile.ino); break; } if (!yflag && !reply("continue")) done(1); i = ntrec * TP_BSIZE; memset(tapebuf, 0, i); #ifdef RRESTORE if (host) seek_failed = (rmtseek(i, 1) < 0); else #endif seek_failed = (lseek(mt, i, SEEK_CUR) == (off_t)-1); if (seek_failed) { fprintf(stderr, "continuation failed: %s\n", strerror(errno)); done(1); } } /* * Handle end of tape. */ if (i == 0) { vprintf(stdout, "End-of-tape encountered\n"); if (!pipein) { newvol = volno + 1; volno = 0; numtrec = 0; getvol(newvol); readtape(buf); return; } if (rd % TP_BSIZE != 0) panic("partial block read: %ld should be %ld\n", rd, ntrec * TP_BSIZE); terminateinput(); memmove(&tapebuf[rd], &endoftapemark, (long)TP_BSIZE); } if (oldnumtrec == 0) blkcnt = 0; else blkcnt -= oldnumtrec; memmove(buf, &tapebuf[(blkcnt++ * TP_BSIZE) + byteslide], (long)TP_BSIZE); blksread++; tapeaddr++; } static void findtapeblksize(void) { long i; for (i = 0; i < ntrec; i++) ((struct s_spcl *)&tapebuf[i * TP_BSIZE])->c_magic = 0; blkcnt = 0; #ifdef RRESTORE if (host) i = rmtread(tapebuf, ntrec * TP_BSIZE); else #endif i = read(mt, tapebuf, ntrec * TP_BSIZE); if (i <= 0) { fprintf(stderr, "tape read error: %s\n", strerror(errno)); done(1); } if (i % TP_BSIZE != 0) { fprintf(stderr, "Tape block size (%ld) %s (%d)\n", i, "is not a multiple of dump block size", TP_BSIZE); done(1); } ntrec = i / TP_BSIZE; numtrec = ntrec; vprintf(stdout, "Tape block size is %ld\n", ntrec); } void closemt(void) { if (mt < 0) return; if (pipecmdin) { pclose(popenfp); popenfp = NULL; } else #ifdef RRESTORE if (host) rmtclose(); else #endif (void) close(mt); } /* * Read the next block from the tape. * If it is not any valid header, return an error. */ static int gethead(struct s_spcl *buf) { long i; readtape((char *)buf); if (buf->c_magic != FS_UFS2_MAGIC && buf->c_magic != NFS_MAGIC) { if (buf->c_magic == OFS_MAGIC) { fprintf(stderr, "Format of dump tape is too old. Must use\n"); fprintf(stderr, "a version of restore from before 2002.\n"); return (FAIL); } if (swabl(buf->c_magic) != FS_UFS2_MAGIC && swabl(buf->c_magic) != NFS_MAGIC) { if (swabl(buf->c_magic) == OFS_MAGIC) { fprintf(stderr, "Format of dump tape is too old. Must use\n"); fprintf(stderr, "a version of restore from before 2002.\n"); } return (FAIL); } if (!Bcvt) { vprintf(stdout, "Note: Doing Byte swapping\n"); Bcvt = 1; } } if (checksum((int *)buf) == FAIL) return (FAIL); if (Bcvt) { swabst((u_char *)"8l4s1q8l2q17l", (u_char *)buf); swabst((u_char *)"l",(u_char *) &buf->c_level); swabst((u_char *)"2l4q",(u_char *) &buf->c_flags); } readmapflag = 0; switch (buf->c_type) { case TS_CLRI: case TS_BITS: /* * Have to patch up missing information in bit map headers */ buf->c_size = buf->c_count * TP_BSIZE; if (buf->c_count > TP_NINDIR) readmapflag = 1; else for (i = 0; i < buf->c_count; i++) buf->c_addr[i]++; /* FALL THROUGH */ case TS_TAPE: if (buf->c_magic == NFS_MAGIC && (buf->c_flags & NFS_DR_NEWINODEFMT) == 0) oldinofmt = 1; /* FALL THROUGH */ case TS_END: buf->c_inumber = 0; /* FALL THROUGH */ case TS_ADDR: case TS_INODE: /* * For old dump tapes, have to copy up old fields to * new locations. */ if (buf->c_magic == NFS_MAGIC) { buf->c_tapea = buf->c_old_tapea; buf->c_firstrec = buf->c_old_firstrec; buf->c_date = _time32_to_time(buf->c_old_date); buf->c_ddate = _time32_to_time(buf->c_old_ddate); buf->c_atime = _time32_to_time(buf->c_old_atime); buf->c_mtime = _time32_to_time(buf->c_old_mtime); buf->c_birthtime = 0; buf->c_birthtimensec = 0; buf->c_extsize = 0; } break; default: panic("gethead: unknown inode type %d\n", buf->c_type); break; } if (dumpdate != 0 && _time64_to_time(buf->c_date) != dumpdate) fprintf(stderr, "Header with wrong dumpdate.\n"); /* * If we're restoring a filesystem with the old (FreeBSD 1) * format inodes, copy the uid/gid to the new location */ if (oldinofmt) { buf->c_uid = buf->c_spare1[1]; buf->c_gid = buf->c_spare1[2]; } buf->c_magic = FS_UFS2_MAGIC; tapeaddr = buf->c_tapea; if (dflag) accthdr(buf); return(GOOD); } /* * Check that a header is where it belongs and predict the next header */ static void accthdr(struct s_spcl *header) { static ino_t previno = 0x7fffffff; static int prevtype; static long predict; long blks, i; if (header->c_type == TS_TAPE) { fprintf(stderr, "Volume header "); if (header->c_firstrec) fprintf(stderr, "begins with record %jd", (intmax_t)header->c_firstrec); fprintf(stderr, "\n"); previno = 0x7fffffff; return; } if (previno == 0x7fffffff) goto newcalc; switch (prevtype) { case TS_BITS: fprintf(stderr, "Dumped inodes map header"); break; case TS_CLRI: fprintf(stderr, "Used inodes map header"); break; case TS_INODE: fprintf(stderr, "File header, ino %ju", (uintmax_t)previno); break; case TS_ADDR: fprintf(stderr, "File continuation header, ino %ju", (uintmax_t)previno); break; case TS_END: fprintf(stderr, "End of tape header"); break; } if (predict != blksread - 1) fprintf(stderr, "; predicted %ld blocks, got %ld blocks", predict, blksread - 1); fprintf(stderr, "\n"); newcalc: blks = 0; if (header->c_type != TS_END) for (i = 0; i < header->c_count; i++) if (readmapflag || header->c_addr[i] != 0) blks++; predict = blks; blksread = 0; prevtype = header->c_type; previno = header->c_inumber; } /* * Find an inode header. * Complain if had to skip. */ static void findinode(struct s_spcl *header) { static long skipcnt = 0; long i; char buf[TP_BSIZE]; int htype; curfile.name = ""; curfile.action = UNKNOWN; curfile.mode = 0; curfile.ino = 0; do { htype = header->c_type; switch (htype) { case TS_ADDR: /* * Skip up to the beginning of the next record */ for (i = 0; i < header->c_count; i++) if (header->c_addr[i]) readtape(buf); while (gethead(header) == FAIL || _time64_to_time(header->c_date) != dumpdate) { skipcnt++; if (Dflag) { byteslide++; if (byteslide < TP_BSIZE) { blkcnt--; blksread--; } else byteslide = 0; } } break; case TS_INODE: curfile.mode = header->c_mode; curfile.uid = header->c_uid; curfile.gid = header->c_gid; curfile.file_flags = header->c_file_flags; curfile.rdev = header->c_rdev; curfile.atime_sec = header->c_atime; curfile.atime_nsec = header->c_atimensec; curfile.mtime_sec = header->c_mtime; curfile.mtime_nsec = header->c_mtimensec; curfile.birthtime_sec = header->c_birthtime; curfile.birthtime_nsec = header->c_birthtimensec; curfile.extsize = header->c_extsize; curfile.size = header->c_size; curfile.ino = header->c_inumber; break; case TS_END: /* If we missed some tapes, get another volume. */ if (tapesread & (tapesread + 1)) { getvol(0); continue; } curfile.ino = maxino; break; case TS_CLRI: curfile.name = ""; break; case TS_BITS: curfile.name = ""; break; case TS_TAPE: if (Dflag) fprintf(stderr, "unexpected tape header\n"); else panic("unexpected tape header\n"); default: if (Dflag) fprintf(stderr, "unknown tape header type %d\n", spcl.c_type); else panic("unknown tape header type %d\n", spcl.c_type); while (gethead(header) == FAIL || _time64_to_time(header->c_date) != dumpdate) { skipcnt++; if (Dflag) { byteslide++; if (byteslide < TP_BSIZE) { blkcnt--; blksread--; } else byteslide = 0; } } } } while (htype == TS_ADDR); if (skipcnt > 0) fprintf(stderr, "resync restore, skipped %ld %s\n", skipcnt, Dflag ? "bytes" : "blocks"); skipcnt = 0; } static int checksum(int *buf) { int i, j; j = sizeof(union u_spcl) / sizeof(int); i = 0; if (!Bcvt) { do i += *buf++; while (--j); } else { /* What happens if we want to read restore tapes for a 16bit int machine??? */ do i += swabl(*buf++); while (--j); } if (i != CHECKSUM) { fprintf(stderr, "Checksum error %o, inode %ju file %s\n", i, (uintmax_t)curfile.ino, curfile.name); return(FAIL); } return(GOOD); } #ifdef RRESTORE #include void msg(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)vfprintf(stderr, fmt, ap); va_end(ap); } #endif /* RRESTORE */ static u_char * swabshort(u_char *sp, int n) { char c; while (--n >= 0) { c = sp[0]; sp[0] = sp[1]; sp[1] = c; sp += 2; } return (sp); } static u_char * swablong(u_char *sp, int n) { char c; while (--n >= 0) { c = sp[0]; sp[0] = sp[3]; sp[3] = c; c = sp[2]; sp[2] = sp[1]; sp[1] = c; sp += 4; } return (sp); } static u_char * swabquad(u_char *sp, int n) { char c; while (--n >= 0) { c = sp[0]; sp[0] = sp[7]; sp[7] = c; c = sp[1]; sp[1] = sp[6]; sp[6] = c; c = sp[2]; sp[2] = sp[5]; sp[5] = c; c = sp[3]; sp[3] = sp[4]; sp[4] = c; sp += 8; } return (sp); } void swabst(u_char *cp, u_char *sp) { int n = 0; while (*cp) { switch (*cp) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = (n * 10) + (*cp++ - '0'); continue; case 's': case 'w': case 'h': if (n == 0) n = 1; sp = swabshort(sp, n); break; case 'l': if (n == 0) n = 1; sp = swablong(sp, n); break; case 'q': if (n == 0) n = 1; sp = swabquad(sp, n); break; case 'b': if (n == 0) n = 1; sp += n; break; default: fprintf(stderr, "Unknown conversion character: %c\n", *cp); done(0); break; } cp++; n = 0; } } static u_long swabl(u_long x) { swabst((u_char *)"l", (u_char *)&x); return (x); }