# # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # cmd/backup/dump/Makefile # # Copyright (c) 1980 Regents of the University of California. # All rights reserved. The Berkeley software License Agreement # specifies the terms and conditions for redistribution. # # dump.h header file # dumpfstab.c reads fstab/vfstab, mtab/mnttab # dumpitime.c reads /etc/dumpdates # dumpmain.c driver # dumponline.c online dump support # dumpoptr.c operator interface # dumptape.c handles the mag tape and opening/closing # dumptraverse.c traverses the file system # lftw.c fixed version of ftw(3) # partial.c partial filesystem dump support # unctime.c undo ctime # # CPPFLAGS: # DEBUG use local directory to find ddate and dumpdates # PARTIAL enable partial filesystem dump support # TDEBUG trace out the process forking # # CAUTION: FSTYPE must be defined before including ../Makefile.backup, # otherwise UFSROOTPKGUSRLIBFSTYPE doesn't get expanded # properly and the magic doesn't happen. FSTYPE= ufs include ../Makefile.backup #CFLAGS += -DTDEBUG UFSDUMP= ufsdump PROG= $(UFSDUMP) PRODUCT= $(PROG) ROLLDIR= ../../fs.d/ufs/roll_log ROLLOBJS= $(ROLLDIR)/$(MACH)/roll_log.o ROLLSRCS= $(ROLLDIR)/roll_log.c LOBJECTS= dumponline.o dumpfstab.o dumpitime.o dumpmain.o \ dumpoptr.o dumptape.o dumptraverse.o unctime.o \ partial.o lftw.o OBJECTS= $(LOBJECTS) roll_log.o SRCS= $(LOBJECTS:.o=.c) POFILES= $(OBJECTS:.o=.po) ../lib/libdump.po POFILE= ufsdump.po ROLLPOFILES= roll_log.po # XXX This is only needed for the check: target. It would be nice to # XXX automatically generate the list when needed. HEADERS= ../../../head/protocols/dumprestore.h \ ../../fs.d/ufs/roll_log/roll_log.h ../include/byteorder.h \ ../include/memutils.h \ ../include/rmt.h dump.h dumpusg.h CLOBBERFILES += $(PRODUCT) $(DEBUGPRODUCTS) dumpdates LOCAL= . GENERAL= ../include GLOBAL= ../../../head CPPFLAGS= -I$(LOCAL) -I$(GENERAL) -I$(GLOBAL) -DPARTIAL \ -I$(ROLLDIR) $(CPPFLAGS.master) \ -D_LARGEFILE64_SOURCE=1 CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-clobbered CERRWARN += -Wno-extra LIBDUMP= ../lib/libdump.a LINTLIBDUMP= ../lib/llib-ldump.ln -lkstat LDLIBS += $(BSTATIC) -L../lib -ldump $(BDYNAMIC) \ -lsocket -lnsl -ladm -lm -lkstat UFSROOTDUMPDATES= $(UFSROOTETC)/dumpdates UFSROOTLINK= $(UFSROOTUSRSBIN)/$(PROG) LINKVALUE= ../lib/fs/$(FSTYPE)/$(PROG) LIBDIR= $(UFSROOTUSRLIBFS)/$(FSTYPE) $(UFSROOTDUMPDATES): FILEMODE= 0664 $(LIBDIR): DIRMODE= 0755 FILEMODE= 04555 .KEEP_STATE: all: $(PRODUCT) $(PROG): $(OBJECTS) $$(LIBDUMP) $(LINK.c) -o $@ $(OBJECTS) $(LDLIBS) $(POST_PROCESS) $(LIBDUMP): FRC $(DO_LIBDIR) $(POFILE): $(POFILES) $(RM) $@; cat $(POFILES) > $@ FRC: install: all $(LIBDIR) $(UFSROOTPKGUSRLIBFSTYPE) dumpdates \ $(UFSROOTDUMPDATES) $(UFSROOTLINK) dumpdates: cp /dev/null dumpdates #XXX Should actually note return value from close(2), particularly regarding #XXX tape descriptors. Some drives don't actually force anything to media #XXX except when they are writing a file mark.... # grep is looking for non-space/tab lint: $(SRCS) $(ROLLSRCS) $$(LINTLIBDUMP) $(LINT.c) $(SRCS) $(ROLLSRCS) $(LINTLIBDUMP) 2>&1 \ | sed -f lint.sed | grep '^[^ ]' $(LINTLIBDUMP): FRC cd ../lib; pwd; $(MAKE) lint pwd check: FRC $(CSTYLE) $(CSTYLEFLAGS) $(SRCS) $(ROLLSRCS) $(HEADERS) $(HDRCHK) $(HDRCHKFLAGS) $(HEADERS) clean: $(RM) $(OBJECTS) $(ROLLOBJS) $(DEBUGOBJS) *.ln $(LIBDIR): $(INS.dir) $(UFSROOTLINK): -$(RM) $@; $(SYMLINK) $(LINKVALUE) $(UFSROOTLINK) roll_log.o: cd $(ROLLDIR) ; pwd ; $(MAKE) cp $(ROLLOBJS) . pwd $(ROLLPOFILES): $(ROLLSRCS) cd $(ROLLDIR) ; pwd ; $(MAKE) `echo $(ROLLPOFILES) | sed -e 's@$(ROLLDIR)/@@g'` cp $(ROLLDIR)/*.po . pwd ../lib/libdump.po: cd ../lib ; pwd ; $(MAKE) libdump.po include ../Makefile.targ Copyright (c) 1980 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. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the University of California, Berkeley and its contributors. 4. 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 COPYRIGHT OWNER 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. PORTIONS OF BACKUP COMMAND FUNCTIONALITY /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #ifndef _DUMP_H #define _DUMP_H #include #include #include #include #include #include #include #include #include #include #include #include #include /* for MAXBSIZE */ #include #include #include #include /* needed by inode.h */ #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define SUPPORTS_MTB_TAPE_FORMAT #include #include #include #define NI 16 #define MAXINOPB (MAXBSIZE / sizeof (struct dinode)) #define MAXNINDIR (MAXBSIZE / sizeof (daddr32_t)) #ifndef roundup #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) #endif #ifndef MIN #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif #ifndef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif /* * Define an overflow-free version of howmany so that we don't * run into trouble with large files. */ #define d_howmany(x, y) ((x) / (y) + ((x) % (y) != 0)) #define MWORD(m, i) (m[(ino_t)(i-1)/NBBY]) #define MBIT(i) ((1<<((ino_t)(i-1)%NBBY))&0xff) #define BIS(i, w) (MWORD(w, i) |= MBIT(i)) #define BIC(i, w) (MWORD(w, i) &= ~MBIT(i)) #define BIT(i, w) (MWORD(w, i) & MBIT(i)) extern uint_t msiz; extern uchar_t *clrmap; extern uchar_t *dirmap; extern uchar_t *filmap; extern uchar_t *nodmap; extern uchar_t *shamap; extern uchar_t *activemap; /* * All calculations done in 0.1" units! */ extern char *disk; /* name of the disk file */ extern char *dname; /* name to put in /etc/dumpdates */ extern int disk_dynamic; /* true if disk refers to dynamic storage */ extern char *tape; /* name of the tape file */ extern char *host; /* name of the remote tape host (may be "user@host") */ extern char *dumpdev; /* hostname:device for current volume */ extern char *sdumpdev; /* short form of dumpdev (no user name if remote) */ extern char *increm; /* name of file containing incremental information */ extern char *filesystem; /* name of the file system */ extern char *myname; /* argv[0] without leading path components */ extern char lastincno; /* increment number of previous dump */ extern char incno; /* increment number */ extern char *tlabel; /* what goes in tape header c_label field */ extern int uflag; /* update flag */ extern int fi; /* disk file descriptor */ extern int to; /* tape file descriptor */ extern int mapfd; /* block disk device descriptor for mmap */ extern int pipeout; /* true => output to standard output */ extern int tapeout; /* true => output to a tape drive */ extern ino_t ino; /* current inumber; used globally */ extern off_t pos; /* starting offset within ino; used globally */ extern int leftover; /* number of tape recs left over from prev vol */ extern int nsubdir; /* counts subdirs, for deciding to dump a dir */ extern int newtape; /* new tape flag */ extern int nadded; /* number of added sub directories */ extern int dadded; /* directory added flag */ extern int density; /* density in 0.1" units */ extern ulong_t tsize; /* tape size in 0.1" units */ extern u_offset_t esize; /* estimated tape size, blocks */ extern u_offset_t o_esize; /* number of header blocks (overhead) */ extern u_offset_t f_esize; /* number of TP_BSIZE blocks for files/maps */ extern uint_t etapes; /* estimated number of tapes */ extern uint_t ntrec; /* 1K records per tape block */ extern int tenthsperirg; /* 1/10" per tape inter-record gap */ extern dev_t partial_dev; /* id of BLOCK device used in partial mode */ extern pid_t dumppid; /* process-ID of top-level process */ extern int verify; /* verify each volume */ extern int doingverify; /* true => doing a verify pass */ extern int active; /* recopy active files */ extern int doingactive; /* true => redumping active files */ extern int archive; /* true => saving a archive in archivefile */ extern char *archivefile; /* name of archivefile */ extern int archive_opened; /* have opened/created the archivefile */ extern int notify; /* notify operator flag */ extern int diskette; /* true if dumping to a diskette */ extern int cartridge; /* true if dumping to a cartridge tape */ extern uint_t tracks; /* number of tracks on a cartridge tape */ extern int printsize; /* just print estimated size and exit */ extern int offline; /* take tape offline after rewinding */ extern int autoload; /* wait for next tape to autoload; implies offline */ extern int autoload_tries; /* number of times to check on autoload */ extern int autoload_period; /* seconds, tries*period = total wait time */ extern int doposition; /* move to specified... */ extern daddr32_t filenum; /* position of dump on 1st volume */ extern int dumpstate; /* dump output state (see below) */ extern int dumptoarchive; /* mark records to be archived */ extern int blockswritten; /* number of blocks written on current tape */ extern uint_t tapeno; /* current tape number */ extern struct fs *sblock; /* the file system super block */ extern int shortmeta; /* current file has small amount of metadata */ extern union u_shadow c_shadow_save[1]; extern time_t *telapsed; /* time spent writing previous tapes */ extern time_t *tstart_writing; /* when we started writing the latest tape */ extern time_t *tschedule; /* when next to give a remaining-time estimate */ extern char *debug_chdir; /* non-NULL means to mkdir this/pid, */ /* and chdir there, once for each separate child */ /* * Defines for the msec part of * inode-based times, since we're * not part of the kernel. */ #define di_atspare di_ic.ic_atspare #define di_mtspare di_ic.ic_mtspare #define di_ctspare di_ic.ic_ctspare #define HOUR (60L*60L) #define DAY (24L*HOUR) #define YEAR (365L*DAY) /* * Dump output states */ #define DS_INIT 0 #define DS_START 1 #define DS_CLRI 2 #define DS_BITS 3 #define DS_DIRS 4 #define DS_FILES 5 #define DS_END 6 #define DS_DONE 7 /* * Exit status codes */ #define X_FINOK 0 /* normal exit */ #define X_REWRITE 2 /* restart writing from the check point */ #define X_ABORT 3 /* abort all of dump; no checkpoint restart */ #define X_VERIFY 4 /* verify the reel just written */ #define X_RESTART 5 /* abort all progress so far; attempt restart */ #define NINCREM "/etc/dumpdates" /* new format incremental info */ #define TAPE "/dev/rmt/0b" /* default tape device */ #define OPGRENT "sys" /* group entry to notify */ #define DIALUP "ttyd" /* prefix for dialups */ #define DISKETTE "/dev/rfd0c" #define NBUF 64 /* number of output buffers */ #define MAXNTREC 256 /* max tape blocking factor (in Kb) */ /* * The contents of the file NINCREM are maintained both on * a linked list and then (eventually) arrayified. */ struct idates { char id_name[MAXNAMLEN+3]; char id_incno; time32_t id_ddate; }; extern size_t nidates; /* number of records (might be zero) */ extern struct idates **idatev; /* the arrayfied version */ #define ITITERATE(i, ip) \ for (i = 0; i < nidates && (ip = idatev[i]) != NULL; i++) /* * Function declarations */ #ifdef __STDC__ /* * dumpfstab.c */ extern void mnttabread(void); extern struct mntent *mnttabsearch(char *, int); extern void setmnttab(void); extern struct mntent *getmnttab(void); /* * dumpitime.c */ extern char *prdate(time_t); extern void inititimes(void); extern void getitime(void); extern void putitime(void); extern void est(struct dinode *); extern time32_t is_fssnap_dump(char *); extern void bmapest(uchar_t *); /* * dumplabel.c */ extern void getlabel(void); /* * dumpmain.c */ extern void child_chdir(void); extern char *unrawname(char *); extern void sigAbort(int); extern char *rawname(char *); extern char *lf_rawname(char *); extern time32_t timeclock(time32_t); #ifdef signal extern void (*nsignal(int, void (*)(int)))(int); #endif extern int safe_file_open(const char *file, int mode, int perms); extern int safe_device_open(const char *file, int mode, int perms); extern FILE *safe_fopen(const char *filename, const char *smode, int perms); /* * dumponline.c */ extern void allocino(void); extern void freeino(void); extern void saveino(ino_t, struct dinode *); extern void resetino(ino_t); extern long getigen(ino_t); extern int lf_ismounted(char *, char *); extern int isoperator(uid_t, gid_t); extern int lockfs(char *, char *); extern int openi(ino_t, long, char *); extern caddr_t mapfile(int, off_t, off_t, int); extern void unmapfile(void); extern void stattoi(struct stat *, struct dinode *); extern void dumpfile(int, caddr_t, off_t, off_t, off_t, int, int); extern void activepass(void); /* * dumpoptr.c */ extern int query(char *); extern int query_once(char *, int); extern void interrupt(int); extern void broadcast(char *); extern void timeest(int, int); /*PRINTFLIKE1*/ extern void msg(const char *, ...); /*PRINTFLIKE1*/ extern void msgtail(const char *, ...); extern void lastdump(int); extern char *getresponse(char *, char *); /* * dumptape.c */ extern void alloctape(void); extern void reset(void); extern void spclrec(void); extern void taprec(uchar_t *, int, int); extern void dmpblk(daddr32_t, size_t, off_t); extern void toslave(void (*)(ino_t), ino_t); extern void doinode(ino_t); extern void dospcl(ino_t); extern void flushcmds(void); extern void flusht(void); extern void nextdevice(void); extern int isrewind(int); extern void trewind(void); extern void close_rewind(void); extern void changevol(void); extern void otape(int); extern void dumpabort(void); extern void dumpailing(void); extern void Exit(int); extern void positiontape(char *); /* * dumptraverse.c */ extern void pass(void (*)(struct dinode *), uchar_t *); extern void mark(struct dinode *); extern void active_mark(struct dinode *); extern void markshad(struct dinode *); extern void estshad(struct dinode *); extern void freeshad(); extern void add(struct dinode *); extern void dirdump(struct dinode *); extern void dump(struct dinode *); extern void lf_dump(struct dinode *); extern void dumpblocks(ino_t); extern void bitmap(uchar_t *, int); extern struct dinode *getino(ino_t); extern void bread(diskaddr_t, uchar_t *, size_t); extern int hasshortmeta(struct dinode **ip); /* * lftw.c */ extern int lftw(const char *, int (*)(const char *, const struct stat *, int), int); extern int lf_lftw(const char *, int (*)(const char *, const struct stat64 *, int), int); /* * partial.c */ extern void partial_check(void); extern void lf_partial_check(void); extern int partial_mark(int, char **); /* * unctime.c */ extern time_t unctime(char *); #else /* !STDC */ /* * dumpfstab.c */ extern void mnttabread(); extern struct mntent *mnttabsearch(); extern void setmnttab(); extern struct mntent *getmnttab(); /* * dumpitime.c */ extern char *prdate(); extern void inititimes(); extern void getitime(); extern void putitime(); extern void est(); extern time32_t is_fssnap_dump(); extern void bmapest(); /* * dumplabel.c */ extern void getlabel(); /* * dumpmain.c */ extern void child_chdir(); extern char *unrawname(); extern void sigAbort(); extern char *rawname(); extern char *lf_rawname(); extern time_t timeclock(); #ifdef signal extern void nsignal(); #endif extern int safe_file_open(); extern int safe_device_open(); extern FILE *safe_fopen(); /* * dumponline.c */ extern void allocino(); extern void freeino(); extern void saveino(); extern void resetino(); extern long getigen(); extern int lf_ismounted(); extern int isoperator(); extern ulong_t lockfs(); extern int openi(); extern caddr_t mapfile(); extern void unmapfile(); extern void stattoi(); extern void dumpfile(); extern void activepass(); /* * dumpoptr.c */ extern int query(); extern int query_once(); extern void interrupt(); extern void broadcast(); extern void timeest(); extern void msg(); extern void msgtail(); extern void lastdump(); extern char *getresponse(); /* * dumptape.c */ extern void alloctape(); extern void reset(); extern void spclrec(); extern void taprec(); extern void dmpblk(); extern void toslave(); extern void doinode(); extern void dospcl(); extern void flushcmds(); extern void flusht(); extern void nextdevice(); extern int isrewind(); extern void trewind(); extern void close_rewind(); extern void changevol(); extern void otape(); extern void dumpabort(); extern void dumpailing(); extern void Exit(); extern void positiontape(); /* * dumptraverse.c */ extern void pass(); extern void mark(); extern void active_mark(); extern void markshad(); extern void estshad(); extern void freeshad(); extern void add(); extern void dirdump(); extern void dump(); extern void lf_dump(); extern void dumpblocks(); extern void bitmap(); extern struct dinode *getino(); extern void bread(); extern int hasshortmeta(); /* * lftw.c */ extern int lftw(); extern int lf_lftw(); /* * partial.c */ extern void partial_check(); extern void lf_partial_check(); extern int partial_mark(); /* * unctime.c */ extern time_t unctime(); #endif /* __STDC__ */ /* Insufficiently-featureful system header files... */ NOTE(ALIGNMENT(mmap, 8)) #ifdef __cplusplus } #endif #endif /* _DUMP_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1996,1998 by Sun Microsystems, Inc. * All rights reserved. */ #include "dump.h" /* * File system mount table input routines. We handle a * a combination of BSD and SVR4 formats by coding functions * to explicitly read the SVR4 vfstab file and using * #define's to build a routine to read both BSD files * (fstab and mtab) and SVR4's mnttab file. Internally * we keep everything in the common (mtab/mnttab) format. */ static struct pmntent { struct mntent *pm_mnt; struct pmntent *pm_next; } *mnttable; /* Note that nothing is ever free()'d, so this is safe */ #define mntstrdup(s) ((s) ? strdup((s)) : "") #ifdef __STDC__ static struct mntent *mygetmntent(FILE *, char *); static struct pmntent *addmtab(char *, struct pmntent *); static struct mntent *allocmntent(struct mntent *); #else /* !__STDC__ */ static struct pmntent *addmtab(); static struct mntent *mygetmntent(); static struct mntent *allocmntent(); static int idatesort(); #endif static struct mntent * mygetmntent(f, name) FILE *f; char *name; { static struct mntent mt; int status; if ((status = getmntent(f, &mt)) == 0) return (&mt); switch (status) { case EOF: break; /* normal exit condition */ case MNT_TOOLONG: msg(gettext("%s has a line that is too long\n"), name); break; case MNT_TOOMANY: msg(gettext("%s has a line with too many entries\n"), name); break; case MNT_TOOFEW: msg(gettext("%s has a line with too few entries\n"), name); break; default: msg(gettext( "Unknown return code, %d, from getmntent() on %s\n"), status, name); break; } return (NULL); } /* * Read in SVR4 vfstab-format table. */ static struct pmntent * addvfstab(tablename, pm) char *tablename; struct pmntent *pm; { struct mnttab *mnt; struct vfstab vfs; FILE *tp; int status; assert(((mnttable == NULL) && (pm == NULL)) || (pm != NULL)); /* * No need to secure this, as tablename is hard-coded to VFSTAB, * and that file is in /etc. If random people have write-permission * there, then there are more problems than any degree of paranoia * on our part can fix. */ tp = fopen(tablename, "r"); if (tp == (FILE *)0) { msg(gettext("Cannot open %s for dump table information.\n"), tablename); return ((struct pmntent *)0); } while ((status = getvfsent(tp, &vfs)) == 0) { if (vfs.vfs_fstype == (char *)0 || strcmp(vfs.vfs_fstype, MNTTYPE_42) != 0) continue; mnt = (struct mnttab *)xmalloc(sizeof (*mnt)); mnt->mnt_fsname = mntstrdup(vfs.vfs_special); mnt->mnt_dir = mntstrdup(vfs.vfs_mountp); mnt->mnt_type = mntstrdup(vfs.vfs_fstype); mnt->mnt_opts = mntstrdup(vfs.vfs_mntopts); if (mnttable == (struct pmntent *)0) /* * Guaranteed by caller that pm will also be NULL, * so no memory leak to worry about. */ mnttable = pm = (struct pmntent *)xmalloc(sizeof (*pm)); else { /* Guaranteed pm not NULL by caller and local logic */ pm->pm_next = (struct pmntent *)xmalloc(sizeof (*pm)); pm = pm->pm_next; } pm->pm_mnt = mnt; pm->pm_next = (struct pmntent *)0; } switch (status) { case EOF: break; /* normal exit condition */ case VFS_TOOLONG: msg(gettext("%s has a line that is too long\n"), tablename); break; case VFS_TOOMANY: msg(gettext("%s has a line with too many entries\n"), tablename); break; case VFS_TOOFEW: msg(gettext("%s has a line with too few entries\n"), tablename); break; default: msg(gettext( "Unknown return code, %d, from getvfsent() on %s\n"), status, tablename); break; } (void) fclose(tp); return (pm); } static struct mntent * allocmntent(mnt) struct mntent *mnt; { struct mntent *new; new = (struct mntent *)xmalloc(sizeof (*mnt)); new->mnt_fsname = mntstrdup(mnt->mnt_fsname); /* mnt_special */ new->mnt_dir = mntstrdup(mnt->mnt_dir); /* mnt_mountp */ new->mnt_type = mntstrdup(mnt->mnt_type); /* mnt_fstype */ new->mnt_opts = mntstrdup(mnt->mnt_opts); /* mnt_mntopts */ return (new); } void mnttabread() { struct pmntent *pm = (struct pmntent *)0; if (mnttable != (struct pmntent *)0) return; /* * Read in the file system mount tables. Order * is important as the first matched entry is used * if the target device/filesystem is not mounted. * We try fstab or vfstab first, then mtab or mnttab. */ pm = addvfstab(VFSTAB, pm); (void) addmtab(MOUNTED, pm); } static struct pmntent * addmtab(tablename, pm) char *tablename; struct pmntent *pm; { struct mntent *mnt; FILE *tp; tp = setmntent(tablename, "r"); if (tp == (FILE *)0) { msg(gettext("Cannot open %s for dump table information.\n"), tablename); return ((struct pmntent *)0); } while (mnt = mygetmntent(tp, tablename)) { if (mnt->mnt_type == (char *)0 || strcmp(mnt->mnt_type, MNTTYPE_42) != 0) continue; mnt = allocmntent(mnt); if (mnttable == (struct pmntent *)0) /* * Guaranteed by caller that pm will also be NULL, * so no memory leak to worry about. */ mnttable = pm = (struct pmntent *)xmalloc(sizeof (*pm)); else { /* Guaranteed pm not NULL by caller and local logic */ pm->pm_next = (struct pmntent *)xmalloc(sizeof (*pm)); pm = pm->pm_next; } pm->pm_mnt = mnt; pm->pm_next = (struct pmntent *)0; } (void) endmntent(tp); return (pm); } /* * Search in fstab and potentially mtab for a file name. * If "mounted" is non-zero, the target file system must * be mounted in order for the search to succeed. * This file name can be either the special or the path file name. * * The entries in either fstab or mtab are the BLOCK special names, * not the character special names. * The caller of mnttabsearch assures that the character device * is dumped (that is much faster) * * The file name can omit the leading '/'. */ struct mntent * mnttabsearch(key, mounted) char *key; int mounted; { struct pmntent *pm; struct mntent *mnt; struct mntent *first = (struct mntent *)0; char *s; char *gotreal; char path[MAXPATHLEN]; for (pm = mnttable; pm; pm = pm->pm_next) { s = NULL; mnt = pm->pm_mnt; if (strcmp(mnt->mnt_dir, key) == 0) goto found; if (strcmp(mnt->mnt_fsname, key) == 0) goto found; if ((s = rawname(mnt->mnt_fsname)) != NULL && strcmp(s, key) == 0) goto found; gotreal = realpath(mnt->mnt_dir, path); if (gotreal && strcmp(path, key) == 0) goto found; if (key[0] != '/') { if (*mnt->mnt_fsname == '/' && strcmp(mnt->mnt_fsname + 1, key) == 0) goto found; if (*mnt->mnt_dir == '/' && strcmp(mnt->mnt_dir + 1, key) == 0) goto found; if (gotreal && *path == '/' && strcmp(path + 1, key) == 0) goto found; } if (s != NULL && s != mnt->mnt_fsname) free(s); continue; found: /* Pointer comparison, not string comparison */ if (s != NULL && s != mnt->mnt_fsname) free(s); /* * Found a match; return immediately if * it is mounted (valid), otherwise just * record if it's the first matched entry. */ if (lf_ismounted(mnt->mnt_fsname, mnt->mnt_dir) > 0) return (mnt); else if (first == (struct mntent *)0) first = mnt; } /* * If we get here, there were either * no matches, or no matched entries * were mounted. Return failure if * we were supposed to find a mounted * entry, otherwise return the first * matched entry (or null). */ if (mounted) return ((struct mntent *)0); return (first); } static struct pmntent *current; static int set; void #ifdef __STDC__ setmnttab(void) #else setmnttab() #endif { current = mnttable; set = 1; } struct mntent * #ifdef __STDC__ getmnttab(void) #else getmnttab() #endif { struct pmntent *pm; if (!set) setmnttab(); pm = current; if (current) { current = current->pm_next; return (pm->pm_mnt); } return ((struct mntent *)0); } /* * Copyright 1996-1998, 2002-2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #include "dump.h" #ifndef LOCK_EX static struct flock fl; #define flock(fd, flag) (fl.l_type = (flag), fcntl(fd, F_SETLKW, &fl)) #define LOCK_EX F_WRLCK #define LOCK_SH F_RDLCK #define LOCK_UN F_UNLCK #endif char *disk; char *dname; char *increm; char incno; char lastincno; int uflag; uint_t msiz; u_offset_t o_esize; u_offset_t f_esize; /* * Print a date. A date of 0 is the beginning of time (the "epoch"). * If the 2nd argument is non-zero, it is ok to format the date in * locale-specific form, otherwise we use ctime. We must use ctime * for dates such as those in the dumpdates file, which must be * locale-independent. */ char * prdate(time_t d) { static char buf[256]; struct tm *tm; char *p; if (d == 0) return (gettext("the epoch")); tm = localtime(&d); if (strftime(buf, sizeof (buf), "%c", tm) != 0) { p = buf; } else { /* Wouldn't fit in buf, fall back */ p = ctime(&d); p[24] = '\0'; /* lose trailing newline */ } return (p); } struct idates **idatev = 0; size_t nidates = 0; static int idates_in = 0; /* we have read the increment file */ static int recno; #ifdef __STDC__ static void readitimes(FILE *); static void recout(FILE *, struct idates *); static int getrecord(FILE *, struct idates *); static int makeidate(struct idates *, char *); #else static void readitimes(); static void recout(); static int getrecord(); static int makeidate(); #endif void inititimes(void) { FILE *df; int saverr; if (idates_in) return; if (increm == NULL || *increm == '\0') { msg(gettext("inititimes: No dump record file name defined\n")); dumpabort(); /*NOTREACHED*/ } /* * No need to secure this, as increm is hard-coded to NINCREM, * and that file is in /etc. If random people have write-permission * there, then there are more problems than any degree of paranoia * on our part can fix. */ if ((df = fopen(increm, "r")) == NULL) { saverr = errno; if (errno == ENOENT) msg(gettext( "Warning - dump record file `%s' does not exist\n"), increm); else { msg(gettext("Cannot open dump record file `%s': %s\n"), increm, strerror(saverr)); dumpabort(); /*NOTREACHED*/ } return; } if (uflag && access(increm, W_OK) < 0) { msg(gettext("Cannot access dump record file `%s' for update\n"), increm); dumpabort(); /*NOTREACHED*/ } (void) flock(fileno(df), LOCK_SH); readitimes(df); (void) fclose(df); } static void readitimes(FILE *df) { struct idates *idp; recno = 0; for (;;) { idp = (struct idates *)xcalloc(1, sizeof (*idp)); if (getrecord(df, idp) < 0) { free((char *)idp); break; } nidates++; idatev = (struct idates **)xrealloc((void *)idatev, nidates * (size_t)sizeof (*idatev)); idatev[nidates - 1] = idp; } /* LINTED: assigned value is used in inititimes */ idates_in = 1; } void getitime(void) { struct idates *ip; int i; char *fname; /* * if an alternate name was specified via the N flag, use it instead * of the disk name. */ if (dname != NULL) fname = dname; else fname = disk; #ifdef FDEBUG /* XGETTEXT: #ifdef FDEBUG only */ msg(gettext("Looking for name %s in increm = %s for delta = %c\n"), fname, increm, (uchar_t)incno); #endif spcl.c_ddate = 0; lastincno = '0'; inititimes(); if (idatev == 0) return; /* * Go find the entry with the same name for a lower increment * and older date */ ITITERATE(i, ip) { if (strncmp(fname, ip->id_name, sizeof (ip->id_name)) != 0) continue; if (ip->id_incno >= incno) continue; if (ip->id_ddate <= spcl.c_ddate) continue; spcl.c_ddate = ip->id_ddate; lastincno = ip->id_incno; } } void putitime(void) { FILE *df; struct idates *itwalk; int i; int fd, saverr; char *fname; if (uflag == 0) return; if ((df = safe_fopen(increm, "r+", 0664)) == (FILE *)NULL) { msg("%s: %s\n", increm, strerror(errno)); (void) unlink(increm); dumpabort(); /*NOTREACHED*/ } fd = fileno(df); (void) flock(fd, LOCK_EX); /* * if an alternate name was specified via the N flag, use it instead * of the disk name. */ if (dname != NULL) fname = dname; else fname = disk; if (idatev != 0) { for (i = 0; i < nidates && idatev[i] != 0; i++) free((char *)idatev[i]); free((char *)idatev); } idatev = 0; nidates = 0; readitimes(df); if (fseek(df, 0L, 0) < 0) { /* rewind() was redefined in dumptape.c */ saverr = errno; msg(gettext("%s: %s error:\n"), increm, "fseek", strerror(saverr)); dumpabort(); /*NOTREACHED*/ } spcl.c_ddate = 0; /* LINTED: won't dereference idatev if it is NULL (see readitimes) */ ITITERATE(i, itwalk) { if (strncmp(fname, itwalk->id_name, sizeof (itwalk->id_name)) != 0) continue; if (itwalk->id_incno != incno) continue; goto found; } /* * Add one more entry to idatev */ nidates++; idatev = (struct idates **)xrealloc((void *)idatev, nidates * (size_t)sizeof (struct idates *)); itwalk = idatev[nidates - 1] = (struct idates *)xcalloc(1, sizeof (*itwalk)); found: (void) strncpy(itwalk->id_name, fname, sizeof (itwalk->id_name)); itwalk->id_name[sizeof (itwalk->id_name) - 1] = '\0'; itwalk->id_incno = incno; itwalk->id_ddate = spcl.c_date; ITITERATE(i, itwalk) { recout(df, itwalk); } if (ftruncate64(fd, ftello64(df))) { saverr = errno; msg(gettext("%s: %s error:\n"), increm, "ftruncate64", strerror(saverr)); dumpabort(); /*NOTREACHED*/ } (void) fclose(df); msg(gettext("Level %c dump on %s\n"), (uchar_t)incno, prdate(spcl.c_date)); } static void recout(FILE *file, struct idates *what) { time_t ddate = what->id_ddate; /* must use ctime, so we can later use unctime() */ (void) fprintf(file, DUMPOUTFMT, what->id_name, (uchar_t)what->id_incno, ctime(&ddate)); } static int getrecord(FILE *df, struct idates *idatep) { char buf[BUFSIZ]; if ((fgets(buf, BUFSIZ, df)) != buf) return (-1); recno++; if (makeidate(idatep, buf) < 0) { msg(gettext( "Malformed entry in dump record file `%s', line %d\n"), increm, recno); if (strcmp(increm, NINCREM)) { msg(gettext("`%s' not a dump record file\n"), increm); dumpabort(); /*NOTREACHED*/ } return (-1); } #ifdef FDEBUG msg("getrecord: %s %c %s\n", idatep->id_name, (uchar_t)idatep->id_incno, prdate(idatep->id_ddate)); #endif return (0); } static int makeidate(struct idates *ip, char *buf) { char un_buf[128]; /* size must be >= second one in DUMPINFMT */ /* * MAXNAMLEN has different values in dirent.h and ufs_fsdir.h, * and we need to ensure that the length in DUMPINFMT matches * what we allow for. Can't just use MAXNAMLEN in the test, * because there's no convenient way to substitute it into * DUMPINFMT. * XXX There's got to be a better way. */ /*LINTED [assertion always true]*/ assert(sizeof (ip->id_name) == (255 + 3)); if (sscanf(buf, DUMPINFMT, ip->id_name, &ip->id_incno, un_buf) != 3) return (-1); /* LINTED casting from 64-bit to 32-bit time */ ip->id_ddate = (time32_t)unctime(un_buf); if (ip->id_ddate < 0) return (-1); return (0); } /* * This is an estimation of the number of tp_bsize blocks in the file. * It estimates the number of blocks in files with holes by assuming * that all of the blocks accounted for by di_blocks are data blocks * (when some of the blocks are usually used for indirect pointers); * hence the estimate may be high. */ void est(struct dinode *ip) { u_offset_t s, t; /* * ip->di_size is the size of the file in bytes. * ip->di_blocks stores the number of sectors actually in the file. * If there are more sectors than the size would indicate, this just * means that there are indirect blocks in the file or unused * sectors in the last file block; we can safely ignore these * (s = t below). * If the file is bigger than the number of sectors would indicate, * then the file has holes in it. In this case we must use the * block count to estimate the number of data blocks used, but * we use the actual size for estimating the number of indirect * dump blocks (t vs. s in the indirect block calculation). */ o_esize++; s = (unsigned)(ip->di_blocks) / (unsigned)(tp_bsize / DEV_BSIZE); /* LINTED: spurious complaint about sign-extending 32 to 64 bits */ t = d_howmany(ip->di_size, (unsigned)tp_bsize); if (s > t) s = t; if (ip->di_size > (u_offset_t)((unsigned)(sblock->fs_bsize) * NDADDR)) { /* calculate the number of indirect blocks on the dump tape */ /* LINTED: spurious complaint sign-extending 32 to 64 bits */ s += d_howmany(t - (unsigned)(NDADDR * sblock->fs_bsize / tp_bsize), (unsigned)TP_NINDIR); } f_esize += s; } /*ARGSUSED*/ void bmapest(uchar_t *map) { o_esize++; /* LINTED: spurious complaint sign-extending 32 to 64 bits */ f_esize += d_howmany(msiz * sizeof (map[0]), (unsigned)tp_bsize); } /* * Check to see if what we are trying to dump is a fs snapshot * If so, we can use the snapshot's create time to populate * the dumpdates file, instead of the time of the dump. */ time32_t is_fssnap_dump(char *disk) { struct stat st; char *last; int snapnum; kstat_ctl_t *kslib; kstat_t *ksnum; kstat_named_t *numval; last = basename(disk); if ((strstr(disk, SNAP_NAME) == NULL) || (stat(disk, &st) == -1) || (isdigit(last[0]) == 0)) return (0); snapnum = atoi(last); if ((kslib = kstat_open()) == NULL) return (0); ksnum = kstat_lookup(kslib, SNAP_NAME, snapnum, FSSNAP_KSTAT_NUM); if (ksnum == NULL) { (void) kstat_close(kslib); return (0); } if (kstat_read(kslib, ksnum, NULL) == -1) { (void) kstat_close(kslib); return (0); } numval = kstat_data_lookup(ksnum, FSSNAP_KSTAT_NUM_CREATETIME); if (numval == NULL) { (void) kstat_close(kslib); return (0); } (void) kstat_close(kslib); /* LINTED casting from long to 32-bit time */ return (time32_t)(numval->value.l & INT_MAX); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #include "dump.h" #include #include #include #include #include "roll_log.h" #include char *filesystem; char *host; char *debug_chdir; char *dumpdev; char *sdumpdev; char *tlabel; time_t *telapsed; time_t *tstart_writing; uchar_t *clrmap; uchar_t *dirmap; uchar_t *filmap; uchar_t *nodmap; uchar_t *shamap; uchar_t *activemap; int archive; int autoload; int autoload_period; int autoload_tries; int archive_opened; int doingverify; int disk_dynamic; int dumpstate; int dumptoarchive; int fi; int leftover; int nadded; int offline; int verify; uint_t etapes; ulong_t tsize; ino_t ino; off_t pos; pid_t dumppid; u_offset_t esize; union u_shadow c_shadow_save[1]; int notify = 0; /* notify operator flag */ int blockswritten = 0; /* number of blocks written on current tape */ uint_t tapeno = 0; /* current tape number */ daddr32_t filenum = 0; /* current file number on tape */ int density = 0; /* density in bytes/0.1" */ int tenthsperirg; /* inter-record-gap in 0.1"'s */ uint_t ntrec = 0; /* # tape blocks in each tape record */ uint_t saved_ntrec = 0; /* saved value of ntrec */ uint_t forceflag = 0; /* forced to change tp_bsize */ int cartridge = 0; /* assume non-cartridge tape */ uint_t tracks; /* # tracks on a cartridge tape */ int diskette = 0; /* assume not dumping to a diskette */ int printsize = 0; /* just print estimated size and exit */ int mapfd = -1; /* if >= 0, file descriptor for mmap */ int32_t tp_bsize = TP_BSIZE_MIN; /* tape block record size (frag size) */ #ifdef DEBUG int xflag; /* debugging switch */ #endif char *myname; /* * This should be struct fs, but there are trailing bits on disk * that we also need to read in as part of it. It's an array of * longs instead of char to force proper alignment. */ static long sblock_buf[SBSIZE/sizeof (long)]; #ifdef __STDC__ static char *mb(u_offset_t); static void nextstate(int); #else static char *mb(); static void nextstate(); #endif extern jmp_buf checkpoint_buf; /* context for return from checkpoint */ #define FUDGE_FACTOR 0x2000000 int main(int argc, char *argv[]) { char *arg; int bflag = 0, i, error = 0, saverr; double fetapes = 0.0; struct mnttab *dt; char msgbuf[3000], *msgp; char kbsbuf[BUFSIZ]; u_offset_t esize_shift = 0; int32_t new_mult = 0; time32_t snapdate; host = NULL; if (myname = strrchr(argv[0], '/')) myname++; else myname = argv[0]; if (strcmp("hsmdump", myname) == 0) { msg(gettext("hsmdump emulation is no longer supported.\n")); Exit(X_ABORT); } tape = DEFTAPE; autoload_period = 12; autoload_tries = 12; /* traditional default of ~2.5 minutes */ (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif /* TEXT_DOMAIN */ (void) textdomain(TEXT_DOMAIN); /* * If someone strips the set-uid bit, dump will still work for local * tapes. Fail when we try to access a remote tape. */ (void) __init_suid_priv(0, PRIV_NET_PRIVADDR, (char *)NULL); if (sysinfo(SI_HOSTNAME, spcl.c_host, sizeof (spcl.c_host)) < 0) { saverr = errno; msg(gettext("Could not get host name: %s\n"), strerror(saverr)); bzero(spcl.c_host, sizeof (spcl.c_host)); } dumppid = getpid(); tsize = 0; /* no default size, detect EOT dynamically */ archive_opened = 0; disk = NULL; dname = NULL; disk_dynamic = 0; increm = NINCREM; incno = '9'; uflag = 0; arg = "u"; tlabel = "none"; if (argc > 1) { argv++; argc--; arg = *argv; if (*arg == '-') arg++; } while (*arg) switch (*arg++) { /* BE CAUTIOUS OF FALLTHROUGHS */ case 'M': /* * This undocumented option causes each process to * mkdir debug_chdir/getpid(), and chdir to it. This is * to ease the collection of profiling information and * core dumps. */ if (argc > 1) { argv++; argc--; debug_chdir = *argv; msg(gettext( "Each process shall try to chdir to %s/\n"), debug_chdir); child_chdir(); } else { msg(gettext("Missing move-to-dir (M) name\n")); dumpabort(); /*NOTREACHED*/ } break; case 'w': lastdump('w'); /* tell us only what has to be done */ exit(0); break; case 'W': /* what to do */ lastdump('W'); /* tell state of what has been done */ exit(0); /* do nothing else */ break; case 'T': if (argc > 1) { int count; int multiplier; char units; argv++; argc--; count = atoi(*argv); if (count < 1) { msg(gettext( "Unreasonable autoload timeout period\n")); dumpabort(); /*NOTREACHED*/ } units = *(*argv + strlen(*argv) - 1); switch (units) { case 's': multiplier = 1; break; case 'h': multiplier = 3600; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'm': multiplier = 60; break; default: msg(gettext( "Unknown timeout units indicator `%c'\n"), units); dumpabort(); /*NOTREACHED*/ } autoload_tries = 1 + ((count * multiplier) / autoload_period); } else { msg(gettext("Missing autoload timeout period\n")); dumpabort(); /*NOTREACHED*/ } break; case 'f': /* output file */ if (argc > 1) { argv++; argc--; tape = *argv; if (*tape == '\0') { msg(gettext("Bad output device name\n")); dumpabort(); /*NOTREACHED*/ } } else { msg(gettext("Missing output device name\n")); dumpabort(); /*NOTREACHED*/ } if (strcmp(tape, "-") == 0 && verify) { msg(gettext( "Cannot verify when dumping to standard out.\n")); dumpabort(); /*NOTREACHED*/ } break; case 'd': /* density, in bits per inch */ if (argc > 1) { argv++; argc--; density = atoi(*argv) / 10; if (density <= 0) { msg(gettext( "Density must be a positive integer\n")); dumpabort(); /*NOTREACHED*/ } } else { msg(gettext("Missing density\n")); dumpabort(); /*NOTREACHED*/ } break; case 's': /* tape size, feet */ if (argc > 1) { argv++; argc--; tsize = atol(*argv); if ((*argv[0] == '-') || (tsize == 0)) { msg(gettext( "Tape size must be a positive integer\n")); dumpabort(); /*NOTREACHED*/ } } else { msg(gettext("Missing tape size\n")); dumpabort(); /*NOTREACHED*/ } break; case 't': /* tracks */ if (argc > 1) { argv++; argc--; tracks = atoi(*argv); } else { msg(gettext("Missing track count\n")); dumpabort(); /*NOTREACHED*/ } break; case 'b': /* blocks per tape write */ if (argc > 1) { argv++; argc--; bflag++; /* * We save the ntrec in case we need to change * tp_bsize later, we will have to recalculate * it. */ saved_ntrec = ntrec = atoi(*argv); if (ntrec == 0 || (ntrec&1) || ntrec > (MAXNTREC*2)) { msg(gettext( "Block size must be a positive, even integer <= %d\n"), MAXNTREC*2); dumpabort(); /*NOTREACHED*/ } ntrec /= (tp_bsize/DEV_BSIZE); } else { msg(gettext("Missing blocking factor\n")); dumpabort(); /*NOTREACHED*/ } break; case 'c': /* Tape is cart. not 9-track */ case 'C': /* 'C' to be consistent with 'D' */ cartridge++; break; case '0': /* dump level */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': incno = arg[-1]; break; case 'u': /* update /etc/dumpdates */ uflag++; break; case 'n': /* notify operators */ notify++; break; case 'a': /* create archive file */ archive = 1; if (argc > 1) { argv++; argc--; if (**argv == '\0') { msg(gettext("Bad archive file name\n")); dumpabort(); /*NOTREACHED*/ } archivefile = strdup(*argv); if (archivefile == NULL) { saverr = errno; msg(gettext("Cannot allocate memory: %s\n"), strerror(saverr)); dumpabort(); /*NOTREACHED*/ } } else { msg(gettext("Missing archive file name\n")); dumpabort(); /*NOTREACHED*/ } break; case 'v': verify++; doingverify++; if (strcmp(tape, "-") == 0) { msg(gettext( "Cannot verify when dumping to standard out.\n")); dumpabort(); /*NOTREACHED*/ } break; case 'D': diskette++; break; case 'N': if (argc > 1) { argv++; argc--; if (**argv == '\0') { msg(gettext("Missing name for dumpdates " "entry.\n")); dumpabort(); /*NOTREACHED*/ } dname = *argv; if (strlen(dname) > MAXNAMLEN + 2) { msg(gettext("Dumpdates entry name too " "long.\n")); dumpabort(); /*NOTREACHED*/ } for (i = 0; i < strlen(dname); i++) { if (isspace(*(dname+i))) { msg(gettext("Dumpdates entry name may " "not contain white space.\n")); dumpabort(); /*NOTREACHED*/ } } } else { msg(gettext("Missing name for dumpdates entry.\n")); dumpabort(); /*NOTREACHED*/ } break; case 'L': if (argc > 1) { argv++; argc--; if (**argv == '\0') { msg(gettext("Missing tape label name\n")); dumpabort(); /*NOTREACHED*/ } tlabel = *argv; if (strlen(tlabel) > (sizeof (spcl.c_label) - 1)) { tlabel[sizeof (spcl.c_label) - 1] = '\0'; msg(gettext( "Truncating label to maximum supported length: `%s'\n"), tlabel); } } else { msg(gettext("Missing tape label name\n")); dumpabort(); /*NOTREACHED*/ } break; case 'l': autoload++; break; case 'o': offline++; break; case 'S': printsize++; break; #ifdef DEBUG case 'z': xflag++; break; #endif default: msg(gettext("Bad option `%c'\n"), arg[-1]); dumpabort(); /*NOTREACHED*/ } if (argc > 1) { argv++; argc--; if (**argv == '\0') { msg(gettext("Bad disk name\n")); dumpabort(); /*NOTREACHED*/ } disk = *argv; disk_dynamic = 0; } if (disk == NULL) { (void) fprintf(stderr, gettext( "Usage: %s [0123456789fustdWwnNDCcbavloS [argument]] filesystem\n"), myname); Exit(X_ABORT); } if (!filenum) filenum = 1; if (signal(SIGINT, interrupt) == SIG_IGN) (void) signal(SIGINT, SIG_IGN); if (strcmp(tape, "-") == 0) { pipeout++; tape = gettext("standard output"); dumpdev = sdumpdev = strdup(tape); if (dumpdev == NULL) { saverr = errno; msg(gettext("Cannot allocate memory: %s\n"), strerror(saverr)); dumpabort(); /*NOTREACHED*/ } /*CONSTANTCONDITION*/ assert(sizeof (spcl.c_label) > 5); (void) strcpy(spcl.c_label, "none"); } else if (*tape == '+') { nextdevice(); (void) strcpy(spcl.c_label, tlabel); } else { /* if not already set, set diskette to default */ if (diskette && strcmp(tape, DEFTAPE) == 0) tape = DISKETTE; nextdevice(); (void) strcpy(spcl.c_label, tlabel); } if (cartridge && diskette) { error = 1; msg(gettext("Cannot select both cartridge and diskette\n")); } if (density && diskette) { error = 1; msg(gettext("Cannot select density of diskette\n")); } if (tracks && diskette) { error = 1; msg(gettext("Cannot select number of tracks of diskette\n")); } if (error) { dumpabort(); /*NOTREACHED*/ } /* * 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. * * Most Sun-2's came with 4 track (20MB) cartridge tape drives, * while most other machines (Sun-3's and non-Sun's) come with * 9 track (45MB) cartridge tape drives. Some Sun-2's came with * 9 track drives, but there is no way for the software to detect * which drive type is installed. Sigh... We make the gross * assumption that #ifdef mc68010 will test for a Sun-2. * * cartridge 8000 bpi (100 bytes/.1") 425 * tracks ft. */ if (density == 0) density = cartridge ? 100 : 625; if (tracks == 0) tracks = 9; if (!bflag) { if (cartridge) ntrec = CARTRIDGETREC; else if (diskette) ntrec = NTREC; else if (density >= 625) ntrec = HIGHDENSITYTREC; else ntrec = NTREC; /* * save ntrec in case we have to change tp_bsize later. */ saved_ntrec = (ntrec * (tp_bsize/DEV_BSIZE)); } if (!diskette) { tsize *= 12L*10L; if (cartridge) tsize *= tracks; } rmtinit(msg, Exit); if (host) { char *cp = strchr(host, '@'); if (cp == (char *)0) cp = host; else cp++; if (rmthost(host, ntrec) == 0) { msg(gettext("Cannot connect to tape host `%s'\n"), cp); dumpabort(); /*NOTREACHED*/ } } if (signal(SIGHUP, sigAbort) == SIG_IGN) (void) signal(SIGHUP, SIG_IGN); if (signal(SIGTRAP, sigAbort) == SIG_IGN) (void) signal(SIGTRAP, SIG_IGN); if (signal(SIGFPE, sigAbort) == SIG_IGN) (void) signal(SIGFPE, SIG_IGN); if (signal(SIGBUS, sigAbort) == SIG_IGN) (void) signal(SIGBUS, SIG_IGN); if (signal(SIGSEGV, sigAbort) == SIG_IGN) (void) signal(SIGSEGV, SIG_IGN); if (signal(SIGTERM, sigAbort) == SIG_IGN) (void) signal(SIGTERM, SIG_IGN); if (signal(SIGUSR1, sigAbort) == SIG_IGN) (void) signal(SIGUSR1, SIG_IGN); if (signal(SIGPIPE, sigAbort) == SIG_IGN) (void) signal(SIGPIPE, SIG_IGN); mnttabread(); /* /etc/fstab, /etc/mtab 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 '/'. * NB: we attempt to avoid dumping the block device * (using rawname) because specfs and the vm system * are not necessarily in sync. */ /* * Attempt to roll the log if its root user before doing the dump. * There's nothing the user can do if we are unable to roll the log, * so we'll silently ignore failures. */ if (getuid() == 0 && rl_roll_log(disk) != RL_SUCCESS && disk[0] != '/') { /* Try it again with leading '/'. */ char *slashed; slashed = (char *)malloc(strlen(disk) + 2); if (slashed != (char *)NULL) { (void) sprintf(slashed, "%c%s", '/', disk); (void) rl_roll_log(slashed); free(slashed); } } dt = mnttabsearch(disk, 0); if (dt != 0) { filesystem = dt->mnt_mountp; if (disk_dynamic) { /* LINTED: disk is not NULL */ free(disk); } disk = rawname(dt->mnt_special); disk_dynamic = (disk != dt->mnt_special); (void) strncpy(spcl.c_dev, dt->mnt_special, sizeof (spcl.c_dev)); spcl.c_dev[sizeof (spcl.c_dev) - 1] = '\0'; (void) strncpy(spcl.c_filesys, dt->mnt_mountp, sizeof (spcl.c_filesys)); spcl.c_filesys[sizeof (spcl.c_filesys) - 1] = '\0'; } else { (void) strncpy(spcl.c_dev, disk, sizeof (spcl.c_dev)); spcl.c_dev[sizeof (spcl.c_dev) - 1] = '\0'; #ifdef PARTIAL /* check for partial filesystem dump */ partial_check(); dt = mnttabsearch(disk, 1); if (dt != 0) { filesystem = dt->mnt_mountp; if (disk_dynamic) free(disk); disk = rawname(dt->mnt_special); disk_dynamic = (disk != dt->mnt_special); (void) strncpy(spcl.c_filesys, "a partial file system", sizeof (spcl.c_filesys)); spcl.c_filesys[sizeof (spcl.c_filesys) - 1] = '\0'; } else #endif /* PARTIAL */ { char *old_disk = disk; (void) strncpy(spcl.c_filesys, "an unlisted file system", sizeof (spcl.c_filesys)); spcl.c_filesys[sizeof (spcl.c_filesys) - 1] = '\0'; disk = rawname(old_disk); if (disk != old_disk) { if (disk_dynamic) free(old_disk); disk_dynamic = 1; } /* * If disk == old_disk, then disk_dynamic's state * does not change. */ } } fi = open64(disk, O_RDONLY); if (fi < 0) { saverr = errno; msg(gettext("Cannot open dump device `%s': %s\n"), disk, strerror(saverr)); Exit(X_ABORT); } if (sscanf(&incno, "%1d", &spcl.c_level) != 1) { msg(gettext("Bad dump level `%c' specified\n"), incno); dumpabort(); /*NOTREACHED*/ } getitime(); /* /etc/dumpdates snarfed */ sblock = (struct fs *)&sblock_buf; sync(); bread((diskaddr_t)SBLOCK, (uchar_t *)sblock, (long)SBSIZE); if ((sblock->fs_magic != FS_MAGIC) && (sblock->fs_magic != MTB_UFS_MAGIC)) { msg(gettext( "Warning - super-block on device `%s' is corrupt - run fsck\n"), disk); dumpabort(); /*NOTREACHED*/ } if (sblock->fs_magic == FS_MAGIC && (sblock->fs_version != UFS_EFISTYLE4NONEFI_VERSION_2 && sblock->fs_version != UFS_VERSION_MIN)) { msg(gettext("Unrecognized UFS version: %d\n"), sblock->fs_version); dumpabort(); /*NOTREACHED*/ } if (sblock->fs_magic == MTB_UFS_MAGIC && (sblock->fs_version < MTB_UFS_VERSION_MIN || sblock->fs_version > MTB_UFS_VERSION_1)) { msg(gettext("Unrecognized UFS version: %d\n"), sblock->fs_version); dumpabort(); /*NOTREACHED*/ } /* * Try to set up for using mmap(2). It only works on the block * device, but if we can use it, things go somewhat faster. If * we can't open it, we'll silently fall back to the old method * (read/memcpy). We also only try this if it's been cleanly * unmounted. Dumping a live filesystem this way runs into * buffer consistency problems. Of course, we don't support * running dump on a mounted filesystem, but some people do it * anyway. */ if (sblock->fs_clean == FSCLEAN) { char *block = unrawname(disk); if (block != NULL) { mapfd = open(block, O_RDONLY, 0); free(block); } } restart: bread((diskaddr_t)SBLOCK, (uchar_t *)sblock, (long)SBSIZE); if ((sblock->fs_magic != FS_MAGIC) && (sblock->fs_magic != MTB_UFS_MAGIC)) { /* paranoia */ msg(gettext("bad super-block magic number, run fsck\n")); dumpabort(); /*NOTREACHED*/ } if (sblock->fs_magic == FS_MAGIC && (sblock->fs_version != UFS_EFISTYLE4NONEFI_VERSION_2 && sblock->fs_version != UFS_VERSION_MIN)) { msg(gettext("Unrecognized UFS version: %d\n"), sblock->fs_version); dumpabort(); /*NOTREACHED*/ } if (sblock->fs_magic == MTB_UFS_MAGIC && (sblock->fs_version < MTB_UFS_VERSION_MIN || sblock->fs_version > MTB_UFS_VERSION_1)) { msg(gettext("Unrecognized UFS version: %d\n"), sblock->fs_version); dumpabort(); /*NOTREACHED*/ } if (!doingactive) allocino(); /* XXX should sanity-check the super block before trusting/using it */ /* LINTED XXX time truncated - tolerate until tape format changes */ spcl.c_date = (time32_t)time((time_t *)NULL); bcopy(&(spcl.c_shadow), c_shadow_save, sizeof (c_shadow_save)); snapdate = is_fssnap_dump(disk); if (snapdate) spcl.c_date = snapdate; if (!printsize) { msg(gettext("Date of this level %c dump: %s\n"), incno, prdate(spcl.c_date)); msg(gettext("Date of last level %c dump: %s\n"), (uchar_t)lastincno, prdate(spcl.c_ddate)); msg(gettext("Dumping %s "), disk); if (filesystem != 0) msgtail("(%.*s:%s) ", /* LINTED unsigned -> signed cast ok */ (int)sizeof (spcl.c_host), spcl.c_host, filesystem); msgtail(gettext("to %s.\n"), sdumpdev); } esize = f_esize = o_esize = 0; msiz = roundup(d_howmany(sblock->fs_ipg * sblock->fs_ncg, NBBY), TP_BSIZE_MAX); if (!doingactive) { clrmap = (uchar_t *)xcalloc(msiz, sizeof (*clrmap)); filmap = (uchar_t *)xcalloc(msiz, sizeof (*filmap)); dirmap = (uchar_t *)xcalloc(msiz, sizeof (*dirmap)); nodmap = (uchar_t *)xcalloc(msiz, sizeof (*nodmap)); shamap = (uchar_t *)xcalloc(msiz, sizeof (*shamap)); activemap = (uchar_t *)xcalloc(msiz, sizeof (*activemap)); } else { if (clrmap == NULL || filmap == NULL || dirmap == NULL || nodmap == NULL || shamap == NULL || activemap == NULL) { msg(gettext( "Internal error: NULL map pointer while re-dumping active files")); dumpabort(); /*NOTREACHED*/ } bzero(clrmap, msiz); bzero(filmap, msiz); bzero(dirmap, msiz); bzero(nodmap, msiz); bzero(shamap, msiz); /* retain active map */ } dumpstate = DS_INIT; dumptoarchive = 1; /* * Read cylinder group inode-used bitmaps to avoid reading clear inodes. */ { uchar_t *clrp = clrmap; struct cg *cgp = (struct cg *)xcalloc((uint_t)sblock->fs_cgsize, 1); for (i = 0; i < sblock->fs_ncg; i++) { bread(fsbtodb(sblock, cgtod(sblock, i)), (uchar_t *)cgp, sblock->fs_cgsize); bcopy(cg_inosused(cgp), clrp, (int)sblock->fs_ipg / NBBY); clrp += sblock->fs_ipg / NBBY; } free((char *)cgp); /* XXX right-shift clrmap one bit. why? */ for (i = 0; clrp > clrmap; i <<= NBBY) { i |= *--clrp & ((1<> 1; } } if (!printsize) { msgp = gettext("Mapping (Pass I) [regular files]\n"); msg(msgp); } ino = 0; #ifdef PARTIAL if (partial_mark(argc, argv)) { #endif /* PARTIAL */ if (!doingactive) pass(mark, clrmap); /* mark updates 'x'_esize */ else pass(active_mark, clrmap); /* updates 'x'_esize */ #ifdef PARTIAL } #endif /* PARTIAL */ do { if (!printsize) { msgp = gettext("Mapping (Pass II) [directories]\n"); msg(msgp); } nadded = 0; ino = 0; pass(add, dirmap); } while (nadded); ino = 0; /* adjust estimated size for shadow inodes */ pass(markshad, nodmap); ino = 0; pass(estshad, shamap); freeshad(); bmapest(clrmap); bmapest(nodmap); esize = o_esize + f_esize; if (diskette) { /* estimate number of floppies */ if (tsize != 0) fetapes = (double)(esize + ntrec) / (double)tsize; } 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. */ tenthsperirg = 16; /* actually 15.48, says Archive */ if (tsize != 0) fetapes = ((double)esize /* blocks */ * (tp_bsize /* bytes/block */ * (1.0/density)) /* 0.1" / byte */ + (double)esize /* blocks */ * (1.0/ntrec) /* streaming-stops per block */ * tenthsperirg) /* 0.1" / streaming-stop */ * (1.0 / tsize); /* tape / 0.1" */ } else { /* Estimate number of tapes, for old fashioned 9-track tape */ #ifdef sun /* sun has long irg's */ tenthsperirg = (density == 625) ? 6 : 12; #else tenthsperirg = (density == 625) ? 5 : 8; #endif if (tsize != 0) fetapes = ((double)esize /* blocks */ * (tp_bsize /* bytes / block */ * (1.0/density)) /* 0.1" / byte */ + (double)esize /* blocks */ * (1.0/ntrec) /* IRG's / block */ * tenthsperirg) /* 0.1" / IRG */ * (1.0 / tsize); /* tape / 0.1" */ } etapes = fetapes; /* truncating assignment */ etapes++; /* count the nodemap on each additional tape */ for (i = 1; i < etapes; i++) bmapest(nodmap); /* * If the above bmapest is called, it changes o_esize and f_esize. * So we will recalculate esize here anyway to make sure. * Also, add tape headers and trailer records. */ esize = o_esize + f_esize + etapes + ntrec; /* * If the estimated number of tp_bsize tape blocks is greater than * INT_MAX we have to adjust tp_bsize and ntrec to handle * the larger dump. esize is an estimate, so we 'fudge' * INT_MAX a little. If tp_bsize is adjusted, it will be adjusted * to the size needed for this dump (2048, 4096, 8192, ...) */ if (esize > (INT_MAX - FUDGE_FACTOR)) { /* esize is too big */ forceflag++; esize_shift = ((esize + (INT_MAX - FUDGE_FACTOR) - 1)/ ((u_offset_t)(INT_MAX - FUDGE_FACTOR))) - 1; if ((esize_shift > ESIZE_SHIFT_MAX) || (ntrec == 0)) { msgp = gettext( "Block factor %d ('b' flag) is too small for this size dump."); msg(msgp, saved_ntrec); dumpabort(); /*NOTREACHED*/ } /* * recalculate esize from: * o_esize - header tape records * (f_esize + (num_mult -1)) >> esize_shift - new non-header * tape records for files/maps * etapes - TS_TAPE records * ntrec - TS_END records * * ntrec is adjusted so a tape record is still 'b' flag * number of DEV_BSIZE (512) in size */ new_mult = (tp_bsize << esize_shift)/tp_bsize; tp_bsize = (tp_bsize << esize_shift); esize = o_esize + ((f_esize + (new_mult - 1)) >> esize_shift) + etapes + ntrec; ntrec = (saved_ntrec/(tp_bsize/DEV_BSIZE)); } if (forceflag != 0) { msgp = gettext( "Forcing larger tape block size (%d).\n"); msg(msgp, tp_bsize); } alloctape(); /* allocate tape buffers */ assert((tp_bsize / DEV_BSIZE != 0) && (tp_bsize % DEV_BSIZE == 0)); /* * If all we wanted was the size estimate, * just print it out and exit. */ if (printsize) { (void) printf("%llu\n", esize * tp_bsize); Exit(0); } if (tsize != 0) { if (diskette) msgp = gettext( "Estimated %lld blocks (%s) on %3.2f diskettes.\n"); else msgp = gettext( "Estimated %lld blocks (%s) on %3.2f tapes.\n"); msg(msgp, (esize*(tp_bsize/DEV_BSIZE)), mb(esize), fetapes); } else { msgp = gettext("Estimated %lld blocks (%s).\n"); msg(msgp, (esize*(tp_bsize/DEV_BSIZE)), mb(esize)); } dumpstate = DS_CLRI; otape(1); /* bitmap is the first to tape write */ *telapsed = 0; (void) time(tstart_writing); /* filmap indicates all non-directory inodes */ { uchar_t *np, *fp, *dp; np = nodmap; dp = dirmap; fp = filmap; for (i = 0; i < msiz; i++) *fp++ = *np++ ^ *dp++; } while (dumpstate != DS_DONE) { /* * When we receive EOT notification from * the writer, the signal handler calls * rollforward and then jumps here. */ (void) setjmp(checkpoint_buf); switch (dumpstate) { case DS_INIT: /* * We get here if a tape error occurred * after releasing the name lock but before * the volume containing the last of the * dir info was completed. We have to start * all over in this case. */ { char *rmsg = gettext( "Warning - output error occurred after releasing name lock\n\ \tThe dump will restart\n"); msg(rmsg); goto restart; } /* NOTREACHED */ case DS_START: case DS_CLRI: ino = UFSROOTINO; dumptoarchive = 1; bitmap(clrmap, TS_CLRI); nextstate(DS_BITS); /* FALLTHROUGH */ case DS_BITS: ino = UFSROOTINO; dumptoarchive = 1; if (BIT(UFSROOTINO, nodmap)) /* empty dump check */ bitmap(nodmap, TS_BITS); nextstate(DS_DIRS); if (!doingverify) { msgp = gettext( "Dumping (Pass III) [directories]\n"); msg(msgp); } /* FALLTHROUGH */ case DS_DIRS: dumptoarchive = 1; pass(dirdump, dirmap); nextstate(DS_FILES); if (!doingverify) { msgp = gettext( "Dumping (Pass IV) [regular files]\n"); msg(msgp); } /* FALLTHROUGH */ case DS_FILES: dumptoarchive = 0; pass(lf_dump, filmap); flushcmds(); dumpstate = DS_END; /* don't reset ino */ /* FALLTHROUGH */ case DS_END: dumptoarchive = 1; spcl.c_type = TS_END; for (i = 0; i < ntrec; i++) { spclrec(); } flusht(); break; case DS_DONE: break; default: msg(gettext("Internal state error\n")); dumpabort(); /*NOTREACHED*/ } } if ((! doingactive) && (! active)) trewind(); if (verify && !doingverify) { msgp = gettext("Finished writing last dump volume\n"); msg(msgp); Exit(X_VERIFY); } if (spcl.c_volume > 1) (void) snprintf(msgbuf, sizeof (msgbuf), gettext("%lld blocks (%s) on %ld volumes"), ((uint64_t)spcl.c_tapea*(tp_bsize/DEV_BSIZE)), mb((u_offset_t)(unsigned)(spcl.c_tapea)), spcl.c_volume); else (void) snprintf(msgbuf, sizeof (msgbuf), gettext("%lld blocks (%s) on 1 volume"), ((uint64_t)spcl.c_tapea*(tp_bsize/DEV_BSIZE)), mb((u_offset_t)(unsigned)(spcl.c_tapea))); if (timeclock((time_t)0) != (time_t)0) { (void) snprintf(kbsbuf, sizeof (kbsbuf), gettext(" at %ld KB/sec"), (long)(((float)spcl.c_tapea / (float)timeclock((time_t)0)) * 1000.0)); (void) strcat(msgbuf, kbsbuf); } (void) strcat(msgbuf, "\n"); msg(msgbuf); (void) timeclock((time_t)-1); if (archive) msg(gettext("Archiving dump to `%s'\n"), archivefile); if (active && !verify) { nextstate(DS_INIT); activepass(); goto restart; } msgp = gettext("DUMP IS DONE\n"); msg(msgp); broadcast(msgp); if (! doingactive) putitime(); Exit(X_FINOK); /*NOTREACHED*/ return (0); } void sigAbort(int sig) { char *sigtype; switch (sig) { case SIGHUP: sigtype = "SIGHUP"; break; case SIGTRAP: sigtype = "SIGTRAP"; break; case SIGFPE: sigtype = "SIGFPE"; break; case SIGBUS: msg(gettext("%s ABORTING!\n"), "SIGBUS()"); (void) signal(SIGUSR2, SIG_DFL); abort(); /*NOTREACHED*/ case SIGSEGV: msg(gettext("%s ABORTING!\n"), "SIGSEGV()"); (void) signal(SIGUSR2, SIG_DFL); abort(); /*NOTREACHED*/ case SIGALRM: sigtype = "SIGALRM"; break; case SIGTERM: sigtype = "SIGTERM"; break; case SIGPIPE: msg(gettext("Broken pipe\n")); dumpabort(); /*NOTREACHED*/ default: sigtype = "SIGNAL"; break; } msg(gettext("%s() try rewriting\n"), sigtype); if (pipeout) { msg(gettext("Unknown signal, Cannot recover\n")); dumpabort(); /*NOTREACHED*/ } msg(gettext("Rewriting attempted as response to unknown signal.\n")); (void) fflush(stderr); (void) fflush(stdout); close_rewind(); Exit(X_REWRITE); } /* Note that returned value is malloc'd if != cp && != NULL */ char * rawname(char *cp) { struct stat64 st; char *dp; extern char *getfullrawname(); if (stat64(cp, &st) < 0 || (st.st_mode & S_IFMT) != S_IFBLK) return (cp); dp = getfullrawname(cp); if (dp == 0) return (0); if (*dp == '\0') { free(dp); return (0); } if (stat64(dp, &st) < 0 || (st.st_mode & S_IFMT) != S_IFCHR) { free(dp); return (cp); } return (dp); } static char * mb(u_offset_t blks) { static char buf[16]; if (blks < 1024) (void) snprintf(buf, sizeof (buf), "%lldKB", blks); else (void) snprintf(buf, sizeof (buf), "%.2fMB", ((double)(blks*tp_bsize)) / (double)(1024*1024)); return (buf); } #ifdef signal void (*nsignal(int sig, void (*act)(int)))(int) { struct sigaction sa, osa; sa.sa_handler = act; (void) sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; if (sigaction(sig, &sa, &osa) < 0) return ((void (*)(int))-1); return (osa.sa_handler); } #endif static void nextstate(int state) { /* LINTED assigned value never used - kept for documentary purposes */ dumpstate = state; /* LINTED assigned value never used - kept for documentary purposes */ ino = 0; /* LINTED assigned value never used - kept for documentary purposes */ pos = 0; leftover = 0; } /* * timeclock() function, for keeping track of how much time we've spent * writing to the tape device. it always returns the amount of time * already spent, in milliseconds. if you pass it a positive, then that's * telling it that we're writing, so the time counts. if you pass it a * zero, then that's telling it we're not writing; perhaps we're waiting * for user input. * * a state of -1 resets everything. */ time32_t timeclock(time32_t state) { static int *currentState = NULL; static struct timeval *clockstart; static time32_t *emilli; struct timeval current[1]; int fd, saverr; #ifdef DEBUG fprintf(stderr, "pid=%d timeclock ", getpid()); if (state == (time32_t)-1) fprintf(stderr, "cleared\n"); else if (state > 0) fprintf(stderr, "ticking\n"); else fprintf(stderr, "paused\n"); #endif /* DEBUG */ /* if we haven't setup the shared memory, init */ if (currentState == (int *)NULL) { if ((fd = open("/dev/zero", O_RDWR)) < 0) { saverr = errno; msg(gettext("Cannot open `%s': %s\n"), "/dev/zero", strerror(saverr)); dumpabort(); /*NOTREACHED*/ } /*LINTED [mmap always returns an aligned value]*/ currentState = (int *)mmap((char *)0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, (off_t)0); if (currentState == (int *)-1) { saverr = errno; msg(gettext( "Cannot memory map monitor variables: %s\n"), strerror(saverr)); dumpabort(); /*NOTREACHED*/ } (void) close(fd); /* LINTED currentState is sufficiently aligned */ clockstart = (struct timeval *)(currentState + 1); emilli = (time32_t *)(clockstart + 1); /* Note everything is initialized to zero via /dev/zero */ } if (state == (time32_t)-1) { bzero(clockstart, sizeof (*clockstart)); *currentState = 0; *emilli = (time32_t)0; return (0); } (void) gettimeofday(current, NULL); if (*currentState != 0) { current->tv_usec += 1000000; current->tv_sec--; /* LINTED: result will fit in a time32_t */ *emilli += (current->tv_sec - clockstart->tv_sec) * 1000; /* LINTED: result will fit in a time32_t */ *emilli += (current->tv_usec - clockstart->tv_usec) / 1000; } if (state != 0) bcopy(current, clockstart, sizeof (current)); *currentState = state; return (*emilli); } static int statcmp(const struct stat64 *left, const struct stat64 *right) { int result = 1; if ((left->st_dev == right->st_dev) && (left->st_ino == right->st_ino) && (left->st_mode == right->st_mode) && (left->st_nlink == right->st_nlink) && (left->st_uid == right->st_uid) && (left->st_gid == right->st_gid) && (left->st_rdev == right->st_rdev) && (left->st_ctim.tv_sec == right->st_ctim.tv_sec) && (left->st_ctim.tv_nsec == right->st_ctim.tv_nsec) && (left->st_mtim.tv_sec == right->st_mtim.tv_sec) && (left->st_mtim.tv_nsec == right->st_mtim.tv_nsec)) { /* * Unlike in the ufsrestore version * st_blocks and st_blksiz are not * compared. The reason for this is * problems with zfs dump files. Zfs * changes it's statistics in those * fields. */ result = 0; } return (result); } /* * Safely open a file or device. */ static int safe_open_common(const char *filename, int mode, int perms, int device) { int fd; int working_mode; int saverr; char *errtext; struct stat64 pre_stat, pre_lstat; struct stat64 post_stat, post_lstat; /* * Don't want to be spoofed into trashing something we * shouldn't, thus the following rigamarole. If it doesn't * exist, we create it and proceed. Otherwise, require that * what's there be a real file with no extraneous links and * owned by whoever ran us. * * The silliness with using both lstat() and fstat() is to avoid * race-condition games with someone replacing the file with a * symlink after we've opened it. If there was an flstat(), * we wouldn't need the fstat(). * * The initial open with the hard-coded flags is ok even if we * are intending to open only for reading. If it succeeds, * then the file did not exist, and we'll synthesize an appropriate * complaint below. Otherwise, it does exist, so we won't be * truncating it with the open. */ if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_LARGEFILE, perms)) < 0) { if (errno == EEXIST) { if (lstat64(filename, &pre_lstat) < 0) { return (-1); } if (stat64(filename, &pre_stat) < 0) { return (-1); } working_mode = mode & (O_WRONLY|O_RDWR|O_RDONLY); working_mode |= O_LARGEFILE; if ((fd = open(filename, working_mode)) < 0) { if (errno == ENOENT) { errtext = gettext( "Unexpected condition detected: %s used to exist, but doesn't any longer\n"); msg(errtext, filename); syslog(LOG_WARNING, errtext, filename); errno = ENOENT; } return (-1); } if (lstat64(filename, &post_lstat) < 0) { saverr = errno; (void) close(fd); errno = saverr; return (-1); } if (fstat64(fd, &post_stat) < 0) { saverr = errno; (void) close(fd); errno = saverr; return (-1); } /* * Can't just use memcmp(3C), because the access * time is updated by open(2). */ if (statcmp(&pre_lstat, &post_lstat) != 0) { errtext = gettext("Unexpected change detected: " "%s's lstat(2) information changed\n"); msg(errtext, filename); syslog(LOG_WARNING, errtext, filename); errno = EPERM; return (-1); } if (statcmp(&pre_stat, &post_stat) != 0) { errtext = gettext("Unexpected change detected: " "%s's stat(2) information changed\n"), msg(errtext, filename); syslog(LOG_WARNING, errtext, filename); errno = EPERM; return (-1); } /* * If inode, device, or type are wrong, bail out. * Note using post_stat instead of post_lstat for the * S_ISCHR() test. This is to allow the /dev -> * /devices bit to work, as long as the final target * is a character device (i.e., raw disk or tape). */ if (device && !(S_ISCHR(post_stat.st_mode)) && !(S_ISFIFO(post_stat.st_mode)) && !(S_ISREG(post_lstat.st_mode))) { errtext = gettext("Unexpected condition " "detected: %s is not a supported device\n"), msg(errtext, filename); syslog(LOG_WARNING, errtext, filename); (void) close(fd); errno = EPERM; return (-1); } else if (!device && (!S_ISREG(post_lstat.st_mode) || (post_stat.st_ino != post_lstat.st_ino) || (post_stat.st_dev != post_lstat.st_dev))) { errtext = gettext("Unexpected condition " "detected: %s is not a regular file\n"), msg(errtext, filename); syslog(LOG_WARNING, errtext, filename); (void) close(fd); errno = EPERM; return (-1); } /* * Bad link count implies someone's linked our * target to something else, which we probably * shouldn't step on. */ if (post_lstat.st_nlink != 1) { errtext = gettext("Unexpected condition " "detected: %s must have exactly one " "link\n"), msg(errtext, filename); syslog(LOG_WARNING, errtext, filename); (void) close(fd); errno = EPERM; return (-1); } /* * Root might make a file, but non-root might * need to open it. If the permissions let us * get this far, then let it through. */ if (post_lstat.st_uid != getuid() && post_lstat.st_uid != 0) { errtext = gettext("Unsupported " "condition detected: %s " "must be owned by uid %ld or 0\n"), msg(errtext, filename, (long)getuid()); syslog(LOG_WARNING, errtext, filename, (long)getuid()); (void) close(fd); errno = EPERM; return (-1); } if (mode & O_TRUNC) { if (ftruncate(fd, (off_t)0) < 0) { msg("ftruncate(%s): %s\n", filename, strerror(errno)); (void) close(fd); return (-1); } } } else { /* * Didn't exist, but couldn't open it. */ return (-1); } } else { /* * If truncating open succeeded for a read-only open, * bail out, as we really shouldn't have succeeded. */ if (mode & O_RDONLY) { /* Undo the O_CREAT */ (void) unlink(filename); msg("open(%s): %s\n", filename, strerror(ENOENT)); (void) close(fd); errno = ENOENT; return (-1); } } return (fd); } /* * Safely open a file. */ int safe_file_open(const char *filename, int mode, int perms) { return (safe_open_common(filename, mode, perms, 0)); } /* * Safely open a device. */ int safe_device_open(const char *filename, int mode, int perms) { return (safe_open_common(filename, mode, perms, 1)); } /* * STDIO version of safe_open */ FILE * safe_fopen(const char *filename, const char *smode, int perms) { int fd; int bmode; /* * accepts only modes "r", "r+", and "w" */ if (smode[0] == 'r') { if (smode[1] == '\0') { bmode = O_RDONLY; } else if ((smode[1] == '+') && (smode[2] == '\0')) { bmode = O_RDWR; } } else if ((smode[0] == 'w') && (smode[1] == '\0')) { bmode = O_WRONLY; } else { msg(gettext("internal error: safe_fopen: invalid mode `%s'\n"), smode); return (NULL); } fd = safe_file_open(filename, bmode, perms); /* * caller is expected to report error. */ if (fd >= 0) return (fdopen(fd, smode)); return ((FILE *)NULL); } void child_chdir(void) { char name[MAXPATHLEN]; if (debug_chdir != NULL) { snprintf(name, sizeof (name), "%s/%ld", debug_chdir, (long)getpid()); if (mkdir(name, 0755) < 0) msg("mkdir(%s): %s", name, strerror(errno)); if (chdir(name) < 0) msg("chdir(%s): %s", name, strerror(errno)); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1991,1996,1998 by Sun Microsystems, Inc. * All rights reserved. */ #include "dump.h" #include #include /* * Uncomment if using mmap'ing of files for pre-fetch. * #define ENABLE_MMAP 1 */ struct inodesc { ino_t id_inumber; /* inode number */ long id_gen; /* generation number */ struct inodesc *id_next; /* next on linked list */ }; char *archivefile; char *tape; int active; int doingactive; int doposition; int pipeout; int tapeout; int to; struct fs *sblock; union u_spcl u_spcl; static struct inodesc ilist; /* list of used inodesc structs */ static struct inodesc *last; /* last inodesc init'd or matched */ static struct inodesc *freeinodesc; /* free list of inodesc structs */ static struct inodesc **ialloc; /* allocated chunks, for freeing */ static int nchunks; /* number of allocations */ #ifdef ENABLE_MMAP /* XXX part of mmap support */ /* * If an mmap'ed file is truncated as it is being dumped or * faulted in, we are delivered a SIGBUS. */ static jmp_buf truncate_buf; static void (*savebus)(); static int incopy; static void onsigbus(int); #endif /* ENABLE_MMAP */ #ifdef DEBUG extern int xflag; #endif #ifdef ENABLE_MMAP /* XXX part of mmap support */ static void onsigbus(int sig) { if (!incopy) { dumpabort(); /*NOTREACHED*/ } incopy = 0; longjmp(truncate_buf, 1); /*NOTREACHED*/ } #endif /* ENABLE_MMAP */ void allocino(void) { ino_t maxino; size_t nused; maxino = (unsigned)(sblock->fs_ipg * sblock->fs_ncg); if (maxino > ULONG_MAX) { msg(gettext("allocino: filesystem too large\n")); dumpabort(); /*NOTREACHED*/ } /* LINTED maxino guaranteed to fit into a size_t by above test */ nused = maxino - sblock->fs_cstotal.cs_nifree; freeinodesc = (struct inodesc *)xcalloc(nused, sizeof (*freeinodesc)); if (freeinodesc == (struct inodesc *)0) { msg(gettext("%s: out of memory\n"), "allocino"); dumpabort(); /*NOTREACHED*/ } last = &ilist; ialloc = (struct inodesc **)xmalloc(2*sizeof (*ialloc)); ialloc[0] = freeinodesc; ialloc[1] = (struct inodesc *)0; nchunks = 1; } void freeino(void) { int i; if (ialloc == (struct inodesc **)0) return; for (i = 0; i < nchunks; i++) if (ialloc[i] != 0) free(ialloc[i]); free(ialloc); ialloc = (struct inodesc **)0; } void resetino(ino_t ino) { last = ilist.id_next; while (last && last->id_inumber < ino) last = last->id_next; } char * unrawname(char *cp) { char *dp; extern char *getfullblkname(); dp = getfullblkname(cp); if (dp == 0) return (0); if (*dp == '\0') { free(dp); return (0); } if (dp == cp) /* caller wants to always free() dp */ dp = strdup(cp); return (dp); } /* * Determine if specified device is mounted at * specified mount point. Returns 1 if mounted, * 0 if not mounted, -1 on error. */ int lf_ismounted(char *devname, char *dirname) { struct stat64 st; char *blockname; /* name of block device */ dev_t dev; int saverr; if ((blockname = unrawname(devname)) == NULL) { msg(gettext("Cannot obtain block name from `%s'\n"), devname); return (-1); } if (stat64(blockname, &st) < 0) { saverr = errno; msg(gettext("Cannot obtain status of device `%s': %s\n"), blockname, strerror(saverr)); free(blockname); return (-1); } free(blockname); dev = st.st_rdev; if (stat64(dirname, &st) < 0) { saverr = errno; msg(gettext("Cannot obtain status of device `%s': %s\n"), dirname, strerror(saverr)); return (-1); } if (dev == st.st_dev) return (1); return (0); } #ifdef ENABLE_MMAP /* XXX mapped-file support */ #define MINMAPSIZE 1024*1024 #define MAXMAPSIZE 1024*1024*32 static caddr_t mapbase; /* base of mapped data */ static caddr_t mapend; /* last byte of mapped data */ static size_t mapsize; /* amount of mapped data */ /* * Map a file prior to dumping and start faulting in its * pages. Stop if we catch a signal indicating our turn * to dump has arrived. If the file is truncated out from * under us, immediately return. * NB: the base of the mapped data may not coincide * exactly to the requested offset, due to alignment * constraints. */ caddr_t mapfile(int fd, off_t offset, off_t bytes, int fetch) { /*LINTED [c used during pre-fetch faulting]*/ volatile char c, *p; int stride = (int)sysconf(_SC_PAGESIZE); extern int caught; /* pre-fetch until set */ caddr_t mapstart; /* beginning of file's mapped data */ off_t mapoffset; /* page-aligned offset */ int saverr; mapbase = mapend = (caddr_t)0; if (bytes == 0) return ((caddr_t)0); /* * mmap the file for reading */ mapoffset = offset & ~(stride - 1); /* LINTED: "bytes" will always fit into a size_t */ mapsize = bytes + (offset - mapoffset); if (mapsize > MAXMAPSIZE) mapsize = MAXMAPSIZE; while ((mapbase = mmap((caddr_t)0, mapsize, PROT_READ, MAP_SHARED, fd, mapoffset)) == (caddr_t)-1 && errno == ENOMEM && mapsize >= MINMAPSIZE) { /* * Due to address space limitations, we * may not be able to map as much as we want. */ mapsize /= 2; /* exponential back-off */ } if (mapbase == (caddr_t)-1) { saverr = errno; msg(gettext("Cannot map file at inode `%lu' into memory: %s\n"), ino, strerror(saverr)); /* XXX why not call dumpailing() here? */ if (!query(gettext( "Do you want to attempt to continue? (\"yes\" or \"no\") "))) { dumpabort(); /*NOTREACHED*/ } mapbase = (caddr_t)0; return ((caddr_t)0); } (void) madvise(mapbase, mapsize, MADV_SEQUENTIAL); mapstart = mapbase + (offset - mapoffset); mapend = mapbase + (mapsize - 1); if (!fetch) return (mapstart); if (setjmp(truncate_buf) == 0) { savebus = signal(SIGBUS, onsigbus); /* * Touch each page to pre-fetch by faulting. At least * one of c or *p must be declared volatile, lest the * optimizer eliminate the assignment in the loop. */ incopy = 1; for (p = mapbase; !caught && p <= mapend; p += stride) { /* LINTED: c is used for its side-effects */ c = *p; } incopy = 0; } #ifdef DEBUG else /* XGETTEXT: #ifdef DEBUG only */ msg(gettext( "FILE TRUNCATED (fault): Interrupting pre-fetch\n")); #endif (void) signal(SIGBUS, savebus); return (mapstart); } void unmapfile(void) { if (mapbase) { /* XXX we're unmapping it, so what does this gain us? */ (void) msync(mapbase, mapsize, MS_ASYNC|MS_INVALIDATE); (void) munmap(mapbase, mapsize); mapbase = (caddr_t)0; } } #endif /* ENABLE_MMAP */ void activepass(void) { static int passno = 1; /* active file pass number */ char *ext, *old; char buf[3000]; static char defext[] = ".retry"; if (pipeout) { msg(gettext("Cannot re-dump active files to `%s'\n"), tape); dumpabort(); /*NOTREACHED*/ } if (active > 1) (void) snprintf(buf, sizeof (buf), gettext( "%d files were active and will be re-dumped\n"), active); else (void) snprintf(buf, sizeof (buf), gettext( "1 file was active and will be re-dumped\n")); msg(buf); doingactive++; active = 0; reset(); /* reset tape params */ spcl.c_ddate = spcl.c_date; /* chain with last dump/pass */ /* * If archiving, create a new * archive file. */ if (archivefile) { old = archivefile; ext = strstr(old, defext); if (ext != (char *)NULL) *ext = '\0'; /* just want the base name */ /* The two is for the trailing \0 and rounding up log10() */ archivefile = xmalloc(strlen(old) + strlen(defext) + (int)log10((double)passno) + 2); /* Always fits */ (void) sprintf(archivefile, "%s%s%d", old, defext, passno); free(old); } if (tapeout) { if (isrewind(to)) { /* * A "rewind" tape device. When we do * the close, we will lose our position. * Be nice and switch volumes. */ (void) snprintf(buf, sizeof (buf), gettext( "Warning - cannot dump active files to rewind " "device `%s'\n"), tape); msg(buf); close_rewind(); changevol(); } else { trewind(); doposition = 0; filenum++; } } else { /* * Not a tape. Do a volume switch. * This will advance to the next file * if using a sequence of files, next * diskette if using diskettes, or * let the user move the old file out * of the way. */ close_rewind(); changevol(); /* switch files */ } (void) snprintf(buf, sizeof (buf), gettext( "Dumping active files (retry pass %d) to `%s'\n"), passno, tape); msg(buf); passno++; } /* * Copyright (c) 1998 by Sun Microsystems, Inc. * All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #include #include "dump.h" time_t *tschedule; static unsigned int timeout; /* current timeout */ static char *attnmessage, *saveattn; /* attention message */ static void alarmcatch(int); static int idatesort(const void *, const void *); #ifdef DEBUG extern int xflag; #endif /* * Query the operator; This fascist piece of code requires * an exact response. * It is intended to protect dump aborting by inquisitive * people banging on the console terminal to see what is * happening which might cause dump to croak, destroying * a large number of hours of work. * * Every time += 2 minutes we reprint the message, alerting others * that dump needs attention. */ int query(char *question) { int def = -1; while (def == -1) def = query_once(question, -1); return (def); } static int in_query_once; static jmp_buf sjalarmbuf; /* real simple check-sum */ static int addem(char *s) { int total = 0; if (s == (char *)NULL) return (total); while (*s) total += *s++; return (total); } int query_once(char *question, int def) { static char *lastmsg; static int lastmsgsum; int msgsum; char replybuffer[BUFSIZ]; int back; time32_t timeclockstate; pollfd_t pollset; struct sigvec sv; /* special hook to flush timeout cache */ if (question == NULL) { lastmsg = (char *)NULL; lastmsgsum = 0; return (0); } attnmessage = question; /* * Only reset the state if the message changed somehow */ msgsum = addem(question); if (lastmsg != question || lastmsgsum != msgsum) { timeout = 0; if (telapsed && tstart_writing) *telapsed += time((time_t *)0) - *tstart_writing; lastmsg = question; lastmsgsum = msgsum; } timeclockstate = timeclock((time_t)0); if (setjmp(sjalarmbuf) != 0) { if (def != -1) { if (def) msgtail(gettext("YES\n")); else msgtail(gettext("NO\n")); } back = def; goto done; } alarmcatch(SIGALRM); in_query_once = 1; pollset.fd = -1; pollset.events = 0; pollset.revents = 0; if (isatty(fileno(stdin))) { pollset.fd = fileno(stdin); pollset.events = POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND; } else { dumpabort(); /*NOTREACHED*/ } for (;;) { if (poll(&pollset, 1, -1) < 0) { if (errno == EINTR) continue; perror("poll(stdin)"); dumpabort(); /*NOTREACHED*/ } if (pollset.revents == 0) continue; /* sanity check */ if (fgets(replybuffer, sizeof (replybuffer), stdin) == NULL) { if (ferror(stdin)) { clearerr(stdin); continue; } else { dumpabort(); /*NOTREACHED*/ } } timeout = 0; if (strcasecmp(replybuffer, gettext("yes\n")) == 0) { back = 1; lastmsg = (char *)NULL; lastmsgsum = 0; goto done; } else if (strcasecmp(replybuffer, gettext("no\n")) == 0) { back = 0; lastmsg = (char *)NULL; lastmsgsum = 0; goto done; } else { msg(gettext("\"yes\" or \"no\"?\n")); in_query_once = 0; alarmcatch(SIGALRM); in_query_once = 1; } } done: /* * Turn off the alarm, and reset the signal to trap out.. */ (void) alarm(0); attnmessage = NULL; sv.sv_handler = sigAbort; sv.sv_flags = SA_RESTART; (void) sigemptyset(&sv.sa_mask); (void) sigvec(SIGALRM, &sv, (struct sigvec *)0); if (tstart_writing) (void) time(tstart_writing); (void) timeclock(timeclockstate); in_query_once = 0; return (back); } /* * Alert the console operator, and enable the alarm clock to * sleep for time += 2 minutes in case nobody comes to satisfy dump * If the alarm goes off while in the query_once for loop, we just * longjmp back there and return the default answer. */ static void alarmcatch(int signal __unused) { struct sigvec sv; if (in_query_once) { longjmp(sjalarmbuf, 1); } if (timeout) { msgtail("\n"); } timeout += 120; msg(gettext("NEEDS ATTENTION: %s"), attnmessage); sv.sv_handler = alarmcatch; sv.sv_flags = SA_RESTART; (void) sigemptyset(&sv.sa_mask); (void) sigvec(SIGALRM, &sv, (struct sigvec *)0); (void) alarm(timeout); } /* * Here if an inquisitive operator interrupts the dump program */ /*ARGSUSED*/ void interrupt(int sig) { if (!saveattn) { saveattn = attnmessage; } msg(gettext("Interrupt received.\n")); if (query(gettext( "Do you want to abort dump?: (\"yes\" or \"no\") "))) { dumpabort(); /*NOTREACHED*/ } if (saveattn) { attnmessage = saveattn; saveattn = NULL; alarmcatch(SIGALRM); } } /* * We use wall(8) to do the actual broadcasting, so * that we don't have to worry about duplicated code * only getting fixed in one place. This also saves * us from having to worry about process groups, * controlling terminals, and the like. */ void broadcast(char *message) { time_t clock; pid_t pid; int saverr; int fildes[2]; FILE *wall; struct tm *localclock; if (!notify) return; if (pipe(fildes) < 0) { saverr = errno; msg(gettext("pipe: %s\n"), strerror(saverr)); return; } switch (pid = fork()) { case -1: return; case 0: close(fildes[0]); if (dup2(fildes[1], 0) < 0) { saverr = errno; msg(gettext("dup2: %s\n"), strerror(saverr)); exit(1); } execl("/usr/sbin/wall", "wall", "-g", OPGRENT, (char *)NULL); saverr = errno; msg(gettext("execl: %s\n"), strerror(saverr)); exit(1); default: break; /* parent */ } close(fildes[1]); wall = fdopen(fildes[0], "r+"); if (wall == (FILE *)NULL) { saverr = errno; msg(gettext("fdopen: %s\n"), strerror(saverr)); return; } clock = time((time_t *)0); localclock = localtime(&clock); (void) fprintf(wall, gettext( "\n\007\007\007Message from the dump program to all operators at \ %d:%02d ...\n\n%s"), localclock->tm_hour, localclock->tm_min, message); fclose(wall); while (wait((int *)0) != pid) { continue; /*LINTED [empty loop body]*/ } } /* * print out an estimate of the amount of time left to do the dump */ #define EST_SEC 600 /* every 10 minutes */ void timeest(int force, int blkswritten) { time_t tnow, deltat; char *msgp; if (tschedule == NULL) return; if (*tschedule == 0) *tschedule = time((time_t *)0) + EST_SEC; (void) time(&tnow); if ((force || tnow >= *tschedule) && blkswritten) { *tschedule = tnow + EST_SEC; if (!force && blkswritten < 50 * ntrec) return; deltat = (*telapsed + (tnow - *tstart_writing)) * ((double)esize / blkswritten - 1.0); msgp = gettext("%3.2f%% done, finished in %d:%02d\n"); msg(msgp, (blkswritten*100.0)/esize, deltat/3600, (deltat%3600)/60); } } #include /* VARARGS1 */ void msg(const char *fmt, ...) { char buf[1024], *cp; size_t size; va_list args; va_start(args, fmt); (void) strcpy(buf, " DUMP: "); cp = &buf[strlen(buf)]; #ifdef TDEBUG (void) sprintf(cp, "pid=%d ", getpid()); cp = &buf[strlen(buf)]; #endif /* don't need -1, vsnprintf does it right */ /* LINTED pointer arithmetic result fits in size_t */ size = ((size_t)sizeof (buf)) - (size_t)(cp - buf); (void) vsnprintf(cp, size, fmt, args); (void) fputs(buf, stderr); (void) fflush(stdout); (void) fflush(stderr); va_end(args); } /* VARARGS1 */ void msgtail(const char *fmt, ...) { va_list args; va_start(args, fmt); (void) vfprintf(stderr, fmt, args); va_end(args); } #define MINUTES(x) ((x) * 60) /* * Tell the operator what has to be done; * we don't actually do it */ void lastdump(int arg) /* w ==> just what to do; W ==> most recent dumps */ { char *lastname; char *date; int i; time_t tnow, ddate; struct mntent *dt; int dumpme = 0; struct idates *itwalk; (void) time(&tnow); mnttabread(); /* /etc/fstab input */ inititimes(); /* /etc/dumpdates input */ /* Don't use msg(), this isn't a tell-the-world kind of thing */ if (arg == 'w') (void) fprintf(stdout, gettext("Dump these file systems:\n")); else (void) fprintf(stdout, gettext( "Last dump(s) done (Dump '>' file systems):\n")); if (idatev != NULL) { qsort((char *)idatev, nidates, sizeof (*idatev), idatesort); lastname = "??"; ITITERATE(i, itwalk) { if (strncmp(lastname, itwalk->id_name, sizeof (itwalk->id_name)) == 0) continue; /* must be ctime(), per ufsdump(5) */ ddate = itwalk->id_ddate; date = (char *)ctime(&ddate); date[16] = '\0'; /* blow away seconds and year */ lastname = itwalk->id_name; dt = mnttabsearch(itwalk->id_name, 0); if ((time_t)(itwalk->id_ddate) < (tnow - DAY)) { dumpme = 1; } if ((arg == 'w') && dumpme) { /* * Handle the w option: print out file systems * which haven't been backed up within a day. */ (void) printf(gettext("%8s\t(%6s)\n"), itwalk->id_name, dt ? dt->mnt_dir : ""); } if (arg == 'W') { /* * Handle the W option: print out ALL * filesystems including recent dump dates and * dump levels. Mark the backup-needing * filesystems with a >. */ (void) printf(gettext( "%c %8s\t(%6s) Last dump: Level %c, Date %s\n"), dumpme ? '>' : ' ', itwalk->id_name, dt ? dt->mnt_dir : "", (uchar_t)itwalk->id_incno, date); } dumpme = 0; } } } static int idatesort(const void *v1, const void *v2) { struct idates **p1 = (struct idates **)v1; struct idates **p2 = (struct idates **)v2; int diff; diff = strcoll((*p1)->id_name, (*p2)->id_name); if (diff == 0) { /* * Time may eventually become unsigned, so can't * rely on subtraction to give a useful result. * Note that we are sorting dates into reverse * order, so that we will report based on the * most-recent record for a particular filesystem. */ if ((*p1)->id_ddate > (*p2)->id_ddate) diff = -1; else if ((*p1)->id_ddate < (*p2)->id_ddate) diff = 1; } return (diff); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Portions of this source code were derived from Berkeley 4.3 BSD * under license from the Regents of the University of California. */ #include "dump.h" #include #include #include #include #include #include #define SLEEPMS 50 int newtape; static uint_t writesize; /* size of malloc()ed buffer for tape */ static ino_t inos[TP_NINOS]; /* starting inodes on each tape */ /* * The req structure is used to pass commands from the parent * process through the pipes to the slave processes. It comes * in two flavors, depending on which mode dump is operating under: * an inode request (on-line mode) and a disk block request ("old" mode). */ /* * The inode request structure is used during on-line mode. * The master passes inode numbers and starting offsets to * the slaves. The tape writer passes out the current inode, * offset, and number of tape records written after completing a volume. */ struct ireq { ino_t inumber; /* inode number to open/dump */ long igen; /* inode generation number */ off_t offset; /* starting offset in inode */ int count; /* count for 1st spclrec */ }; /* * The block request structure is used in off-line mode to pass * commands to dump disk blocks from the parent process through * the pipes to the slave processes. */ struct breq { diskaddr_t dblk; /* disk address to read */ size_t size; /* number of bytes to read from disk */ ulong_t spclrec[1]; /* actually longer */ }; struct req { short aflag; /* write data to archive process as well */ short tflag; /* begin new tape */ union reqdata { struct ireq ino; /* used for on-line mode */ struct breq blks; /* used for off-line mode */ } data; }; #define ir_inumber data.ino.inumber #define ir_igen data.ino.igen #define ir_offset data.ino.offset #define ir_count data.ino.count #define br_dblk data.blks.dblk #define br_size data.blks.size #define br_spcl data.blks.spclrec static int reqsiz = 0; /* alloctape will initialize */ #define SLAVES 3 struct slaves { int sl_slavefd; /* pipe from master to slave */ pid_t sl_slavepid; /* slave pid; used by killall() */ ino_t sl_inos; /* inos, if this record starts tape */ int sl_offset; /* logical blocks written for object */ int sl_count; /* logical blocks left in spclrec */ int sl_tapea; /* header number, if starting tape */ int sl_firstrec; /* number of first block on tape */ int sl_state; /* dump output state */ struct req *sl_req; /* instruction packet to slave */ }; static struct slaves slaves[SLAVES]; /* one per slave */ static struct slaves *slp; /* pointer to current slave */ static struct slaves chkpt; /* checkpointed data */ struct bdesc { char *b_data; /* pointer to buffer data */ int b_flags; /* flags (see below) */ }; /* * The following variables are in shared memory, and must be * explicitly checkpointed and/or reset. */ static caddr_t shared; /* pointer to block of shared memory */ static struct bdesc *bufp; /* buffer descriptors */ static struct bdesc **current; /* output buffer to fill */ static int *tapea; /* logical record count */ #ifdef INSTRUMENT static int *readmissp; /* number of times writer was idle */ static int *idle; /* number of times slaves were idle */ #endif /* INSTRUMENT */ /* * Buffer flags */ #define BUF_EMPTY 0x0 /* nothing in buffer */ #define BUF_FULL 0x1 /* data in buffer */ #define BUF_SPCLREC 0x2 /* contains special record */ #define BUF_ARCHIVE 0x4 /* dump to archive */ static int recsout; /* number of req's sent to slaves */ static int totalrecsout; /* total number of req's sent to slaves */ static int rotor; /* next slave to be instructed */ static pid_t master; /* pid of master, for sending error signals */ static int writer = -1; /* fd of tape writer */ static pid_t writepid; /* pid of tape writer */ static int arch; /* fd of output archiver */ static pid_t archivepid; /* pid of output archiver */ static int archivefd; /* fd of archive file (proper) */ static offset_t lf_archoffset; /* checkpointed offset into archive file */ int caught; /* caught signal -- imported by mapfile() */ #ifdef DEBUG extern int xflag; #endif static void cmdwrterr(void); static void cmdrderr(void); static void freetape(void); static void bufclear(void); static pid_t setuparchive(void); static pid_t setupwriter(void); static void nextslave(void); static void tperror(int); static void rollforward(int); static void nap(int); static void alrm(int); static void just_rewind(void); static void killall(void); static void proceed(int); static void die(int); static void enslave(void); static void wait_our_turn(void); static void dumpoffline(int, pid_t, int); static void onxfsz(int); static void dowrite(int); static void checkpoint(struct bdesc *, int); static ssize_t atomic(int (*)(), int, char *, int); static size_t tapesize; /* * Allocate buffers and shared memory variables. Tape buffers are * allocated on page boundaries for tape write() efficiency. */ void alloctape(void) { struct slaves *slavep; ulong_t pgoff = (unsigned)(getpagesize() - 1); /* 2**n - 1 */ int mapfd; char *obuf; int saverr; int i, j; writesize = ntrec * tp_bsize; if (!printsize) msg(gettext("Writing %d Kilobyte records\n"), writesize / TP_BSIZE_MIN); /* * set up shared memory seg for here and child */ mapfd = open("/dev/zero", O_RDWR); if (mapfd == -1) { saverr = errno; msg(gettext("Cannot open `%s': %s\n"), "/dev/zero", strerror(saverr)); dumpabort(); /*NOTREACHED*/ } /* * Allocate space such that buffers are page-aligned and * pointers are aligned on 4-byte boundaries (for SPARC). * This code assumes that (NBUF * writesize) is a multiple * of the page size and that pages are aligned on 4-byte * boundaries. Space is allocated as follows: * * (NBUF * writesize) for the actual buffers * (pagesize - 1) for padding so the buffers are page-aligned * (NBUF * ntrec * sizeof (struct bdesc)) for each buffer * (n * sizeof (int)) for [n] debugging variables/pointers * (n * sizeof (int)) for [n] miscellaneous variables/pointers */ tapesize = (NBUF * writesize) /* output buffers */ /* LINTED: pgoff fits into a size_t */ + (size_t)pgoff /* page alignment */ /* buffer descriptors */ + (((size_t)sizeof (struct bdesc)) * NBUF * ntrec) #ifdef INSTRUMENT + (2 * (size_t)sizeof (int *)) /* instrumentation */ #endif /* shared variables */ + (size_t)sizeof (struct bdesc **) + (size_t)sizeof (int *) + (3 * (size_t)sizeof (time_t)); shared = mmap((char *)0, tapesize, PROT_READ|PROT_WRITE, MAP_SHARED, mapfd, (off_t)0); if (shared == (caddr_t)-1) { saverr = errno; msg(gettext("Cannot memory map output buffers: %s\n"), strerror(saverr)); dumpabort(); /*NOTREACHED*/ } (void) close(mapfd); /* * Buffers and buffer headers */ obuf = (char *)(((ulong_t)shared + pgoff) & ~pgoff); /* LINTED obuf and writesize are aligned */ bufp = (struct bdesc *)(obuf + NBUF*writesize); /* * Shared memory variables */ current = (struct bdesc **)&bufp[NBUF*ntrec]; tapea = (int *)(current + 1); /* LINTED pointer alignment ok */ telapsed = (time_t *)(tapea + 1); tstart_writing = telapsed + 1; tschedule = tstart_writing + 1; #ifdef INSTRUMENT /* * Debugging and instrumentation variables */ readmissp = (int *)(tschedule + 1); idle = readmissp + 1; #endif for (i = 0, j = 0; i < NBUF * ntrec; i++, j += tp_bsize) { bufp[i].b_data = &obuf[j]; } reqsiz = sizeof (struct req) + tp_bsize - sizeof (long); for (slavep = slaves; slavep < &slaves[SLAVES]; slavep++) slavep->sl_req = (struct req *)xmalloc(reqsiz); chkpt.sl_offset = 0; /* start at offset 0 */ chkpt.sl_count = 0; chkpt.sl_inos = UFSROOTINO; /* in root inode */ chkpt.sl_firstrec = 1; chkpt.sl_tapea = 0; } static void freetape(void) { if (shared == NULL) return; (void) timeclock((time_t)0); (void) munmap(shared, tapesize); shared = NULL; } /* * Reset tape state variables -- called * before a pass to dump active files. */ void reset(void) { bufclear(); #ifdef INSTRUMENT (*readmissp) = 0; (*idle) = 0; #endif spcl.c_flags = 0; spcl.c_volume = 0; tapeno = 0; chkpt.sl_offset = 0; /* start at offset 0 */ chkpt.sl_count = 0; chkpt.sl_inos = UFSROOTINO; /* in root inode */ chkpt.sl_firstrec = 1; chkpt.sl_tapea = 0; } static void bufclear(void) { struct bdesc *bp; int i; for (i = 0, bp = bufp; i < NBUF * ntrec; i++, bp++) bp->b_flags = BUF_EMPTY; if ((caddr_t)current < shared || (caddr_t)current > (shared + tapesize)) { msg(gettext( "bufclear: current pointer out of range of shared memory\n")); dumpabort(); /*NOTREACHED*/ } if ((*current != NULL) && (*current < &bufp[0] || *current > &bufp[NBUF*ntrec])) { /* ANSI string catenation, to shut cstyle up */ msg(gettext("bufclear: current buffer pointer (0x%x) " "out of range of buffer\naddresses (0x%x - 0x%x)\n"), *current, &bufp[0], &bufp[NBUF*ntrec]); dumpabort(); /*NOTREACHED*/ } *current = bufp; } /* * Start a process to collect information describing the dump. * This data takes two forms: * the bitmap and directory information being written to * the front of the tape (the "archive" file) * information describing each directory and inode (to * be included in the database tmp file) * Write the data to the files as it is received so huge file * systems don't cause dump to consume large amounts of memory. */ static pid_t setuparchive(void) { struct slaves *slavep; int cmd[2]; pid_t pid; ssize_t size; char *data; char *errmsg; int flags, saverr; int punt = 0; /* * Both the archive and database tmp files are * checkpointed by taking their current offsets * (sizes) after completing each volume. Restoring * from a checkpoint involves truncating to the * checkpointed size. */ if (archive && !doingactive) { /* It's allowed/expected to exist, so can't use O_EXCL */ archivefd = safe_file_open(archivefile, O_WRONLY, 0600); if (archivefd < 0) { saverr = errno; msg(gettext("Cannot open archive file `%s': %s\n"), archivefile, strerror(saverr)); dumpabort(); /*NOTREACHED*/ } archive_opened = 1; if (lseek64(archivefd, lf_archoffset, 0) < 0) { saverr = errno; msg(gettext( "Cannot position archive file `%s' : %s\n"), archivefile, strerror(saverr)); dumpabort(); /*NOTREACHED*/ } if (ftruncate64(archivefd, lf_archoffset) < 0) { saverr = errno; msg(gettext( "Cannot truncate archive file `%s' : %s\n"), archivefile, strerror(saverr)); dumpabort(); /*NOTREACHED*/ } } if (pipe(cmd) < 0) { saverr = errno; msg(gettext("%s: %s error: %s\n"), "setuparchive", "pipe", strerror(saverr)); return (0); } sighold(SIGINT); if ((pid = fork()) < 0) { saverr = errno; msg(gettext("%s: %s error: %s\n"), "setuparchive", "fork", strerror(saverr)); return (0); } if (pid > 0) { sigrelse(SIGINT); /* parent process */ (void) close(cmd[0]); arch = cmd[1]; return (pid); } /* * child process */ (void) signal(SIGINT, SIG_IGN); /* master handles this */ #ifdef TDEBUG (void) sleep(4); /* allow time for parent's message to get out */ /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext("Archiver has pid = %ld\n"), (long)getpid()); #endif freeino(); /* release unneeded resources */ freetape(); for (slavep = &slaves[0]; slavep < &slaves[SLAVES]; slavep++) { if (slavep->sl_slavefd != -1) { (void) close(slavep->sl_slavefd); slavep->sl_slavefd = -1; } } (void) close(to); (void) close(fi); to = fi = -1; (void) close(cmd[1]); data = xmalloc(tp_bsize); for (;;) { size = atomic((int(*)())read, cmd[0], (char *)&flags, sizeof (flags)); if ((unsigned)size != sizeof (flags)) break; size = atomic((int(*)())read, cmd[0], data, tp_bsize); if (size == tp_bsize) { if (archive && flags & BUF_ARCHIVE && !punt && (size = write(archivefd, data, tp_bsize)) != tp_bsize) { struct stat64 stats; if (size != -1) { errmsg = strdup(gettext( "Output truncated")); if (errmsg == NULL) errmsg = ""; } else { errmsg = strerror(errno); } if (fstat64(archivefd, &stats) < 0) stats.st_size = -1; /* cast to keep lint&printf happy */ msg(gettext( "Cannot write archive file `%s' at offset %lld: %s\n"), archivefile, (longlong_t)stats.st_size, errmsg); msg(gettext( "Archive file will be deleted, dump will continue\n")); punt++; if ((size != -1) && (*errmsg != '\0')) { free(errmsg); } } } else { break; } } (void) close(cmd[0]); if (archive) { (void) close(archivefd); archivefd = -1; } if (punt) { (void) unlink(archivefile); Exit(X_ABORT); } Exit(X_FINOK); /* NOTREACHED */ return (0); } /* * Start a process to read the output buffers and write the data * to the output device. */ static pid_t setupwriter(void) { struct slaves *slavep; int cmd[2]; pid_t pid; int saverr; caught = 0; if (pipe(cmd) < 0) { saverr = errno; msg(gettext("%s: %s error: %s\n"), "setupwriter", "pipe", strerror(saverr)); return (0); } sighold(SIGINT); if ((pid = fork()) < 0) { saverr = errno; msg(gettext("%s: %s error: %s\n"), "setupwriter", "fork", strerror(saverr)); return (0); } if (pid > 0) { /* * Parent process */ sigrelse(SIGINT); (void) close(cmd[0]); writer = cmd[1]; return (pid); } /* * Child (writer) process */ (void) signal(SIGINT, SIG_IGN); /* master handles this */ #ifdef TDEBUG (void) sleep(4); /* allow time for parent's message to get out */ /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext("Writer has pid = %ld\n"), (long)getpid()); #endif child_chdir(); freeino(); /* release unneeded resources */ for (slavep = &slaves[0]; slavep < &slaves[SLAVES]; slavep++) { if (slavep->sl_slavefd != -1) { (void) close(slavep->sl_slavefd); slavep->sl_slavefd = -1; } } (void) close(fi); fi = -1; (void) close(cmd[1]); dowrite(cmd[0]); if (arch >= 0) { (void) close(arch); arch = -1; } (void) close(cmd[0]); Exit(X_FINOK); /* NOTREACHED */ return (0); } void spclrec(void) { int s, i; int32_t *ip; int flags = BUF_SPCLREC; if ((BIT(ino, shamap)) && (spcl.c_type == TS_INODE)) { spcl.c_type = TS_ADDR; /* LINTED: result fits in a short */ spcl.c_dinode.di_mode &= ~S_IFMT; /* LINTED: result fits in a short */ spcl.c_dinode.di_mode |= IFSHAD; } /* * Only TS_INODEs should have short metadata, if this * isn't such a spclrec, clear the metadata flag and * the c_shadow contents. */ if (!(spcl.c_type == TS_INODE && (spcl.c_flags & DR_HASMETA))) { spcl.c_flags &= ~DR_HASMETA; bcopy(c_shadow_save, &(spcl.c_shadow), sizeof (spcl.c_shadow)); } if (spcl.c_type == TS_END) { spcl.c_count = 1; spcl.c_flags |= DR_INODEINFO; bcopy((char *)inos, (char *)spcl.c_inos, sizeof (inos)); } else if (spcl.c_type == TS_TAPE) { spcl.c_flags |= DR_NEWHEADER; if (doingactive) spcl.c_flags |= DR_REDUMP; } else if (spcl.c_type != TS_INODE) flags = BUF_SPCLREC; spcl.c_tapea = *tapea; /* LINTED for now, max inode # is 2**31 (ufs max size is 4TB) */ spcl.c_inumber = (ino32_t)ino; spcl.c_magic = (tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC; spcl.c_checksum = 0; ip = (int32_t *)&spcl; s = CHECKSUM; assert((tp_bsize % sizeof (*ip)) == 0); i = tp_bsize / sizeof (*ip); assert((i%8) == 0); i /= 8; do { s -= *ip++; s -= *ip++; s -= *ip++; s -= *ip++; s -= *ip++; s -= *ip++; s -= *ip++; s -= *ip++; } while (--i > 0); spcl.c_checksum = s; taprec((uchar_t *)&spcl, flags, sizeof (spcl)); if (spcl.c_type == TS_END) spcl.c_flags &= ~DR_INODEINFO; else if (spcl.c_type == TS_TAPE) spcl.c_flags &= ~(DR_NEWHEADER|DR_REDUMP|DR_TRUEINC); } /* * Fill appropriate buffer */ void taprec(uchar_t *dp, int flags, int size) { if (size > tp_bsize) { msg(gettext( "taprec: Unexpected buffer size, expected %d, got %d.\n"), tp_bsize, size); dumpabort(); /*NOTREACHED*/ } while ((*current)->b_flags & BUF_FULL) nap(10); bcopy(dp, (*current)->b_data, (size_t)size); if (size < tp_bsize) { bzero((*current)->b_data + size, tp_bsize - size); } if (dumptoarchive) flags |= BUF_ARCHIVE; /* no locking as we assume only one reader and one writer active */ (*current)->b_flags = (flags | BUF_FULL); if (++*current >= &bufp[NBUF*ntrec]) (*current) = &bufp[0]; (*tapea)++; } void dmpblk(daddr32_t blkno, size_t size, off_t offset) { diskaddr_t dblkno; assert((offset >> DEV_BSHIFT) <= INT32_MAX); dblkno = fsbtodb(sblock, blkno) + (offset >> DEV_BSHIFT); size = (size + DEV_BSIZE-1) & ~(DEV_BSIZE-1); slp->sl_req->br_dblk = dblkno; slp->sl_req->br_size = size; if (dumptoarchive) { /* LINTED: result fits in a short */ slp->sl_req->aflag |= BUF_ARCHIVE; } toslave((void(*)())0, ino); } /*ARGSUSED*/ static void tperror(int sig) { char buf[3000]; if (pipeout) { msg(gettext("Write error on %s\n"), tape); msg(gettext("Cannot recover\n")); dumpabort(); /* NOTREACHED */ } if (!doingverify) { broadcast(gettext("WRITE ERROR!\n")); (void) snprintf(buf, sizeof (buf), gettext("Do you want to restart?: (\"yes\" or \"no\") ")); if (!query(buf)) { dumpabort(); /*NOTREACHED*/ } if (tapeout && (isrewind(to) || offline)) { /* ANSI string catenation, to shut cstyle up */ msg(gettext("This tape will rewind. After " "it is rewound,\nreplace the faulty tape " "with a new one;\nthis dump volume will " "be rewritten.\n")); } } else { broadcast(gettext("TAPE VERIFICATION ERROR!\n")); (void) snprintf(buf, sizeof (buf), gettext( "Do you want to rewrite?: (\"yes\" or \"no\") ")); if (!query(buf)) { dumpabort(); /*NOTREACHED*/ } msg(gettext( "This tape will be rewritten and then verified\n")); } killall(); trewind(); Exit(X_REWRITE); } /* * Called by master from pass() to send a request to dump files/blocks * to one of the slaves. Slaves return whether the file was active * when it was being dumped. The tape writer process sends checkpoint * info when it completes a volume. */ void toslave(void (*fn)(), ino_t inumber) { int wasactive; if (recsout >= SLAVES) { if ((unsigned)atomic((int(*)())read, slp->sl_slavefd, (char *)&wasactive, sizeof (wasactive)) != sizeof (wasactive)) { cmdrderr(); dumpabort(); /*NOTREACHED*/ } if (wasactive) { active++; msg(gettext( "The file at inode `%lu' was active and will " "be recopied\n"), slp->sl_req->ir_inumber); /* LINTED: 32-bit to 8-bit assignment ok */ BIS(slp->sl_req->ir_inumber, activemap); } } slp->sl_req->aflag = 0; if (dumptoarchive) { /* LINTED: result fits in a short */ slp->sl_req->aflag |= BUF_ARCHIVE; } if (fn) (*fn)(inumber); if (atomic((int(*)())write, slp->sl_slavefd, (char *)slp->sl_req, reqsiz) != reqsiz) { cmdwrterr(); dumpabort(); /*NOTREACHED*/ } ++recsout; nextslave(); } void dospcl(ino_t inumber) { /* LINTED for now, max inode # is 2**31 (ufs max size is 1TB) */ spcl.c_inumber = (ino32_t)inumber; slp->sl_req->br_dblk = 0; bcopy((char *)&spcl, (char *)slp->sl_req->br_spcl, tp_bsize); } static void nextslave(void) { if (++rotor >= SLAVES) { rotor = 0; } slp = &slaves[rotor]; } void flushcmds(void) { int i; int wasactive; /* * Retrieve all slave status */ if (recsout < SLAVES) { slp = slaves; rotor = 0; } for (i = 0; i < (recsout < SLAVES ? recsout : SLAVES); i++) { if ((unsigned)atomic((int(*)())read, slp->sl_slavefd, (char *)&wasactive, sizeof (wasactive)) != sizeof (wasactive)) { cmdrderr(); dumpabort(); /*NOTREACHED*/ } if (wasactive) { active++; msg(gettext( "inode %d was active and will be recopied\n"), slp->sl_req->ir_inumber); /* LINTED: 32-bit to 8-bit assignment ok */ BIS(slp->sl_req->ir_inumber, activemap); } nextslave(); } } void flusht(void) { sigset_t block_set, oset; /* hold SIGUSR1 and atomically sleep */ (void) sigemptyset(&block_set); (void) sigaddset(&block_set, SIGUSR1); (void) sigprocmask(SIG_BLOCK, &block_set, &oset); (void) kill(writepid, SIGUSR1); /* tell writer to flush */ (void) sigpause(SIGUSR1); /* wait for SIGUSR1 from writer */ /*NOTREACHED*/ } jmp_buf checkpoint_buf; /* * Roll forward to the next volume after receiving * an EOT signal from writer. Get checkpoint data * from writer and return if done, otherwise fork * a new process and jump back to main state loop * to begin the next volume. Installed as the master's * signal handler for SIGUSR1. */ /*ARGSUSED*/ static void rollforward(int sig) { int status; (void) sighold(SIGUSR1); /* * Writer sends us checkpoint information after * each volume. A returned state of DS_DONE with no * unwritten (left-over) records differentiates a * clean flush from one in which EOT was encountered. */ if ((unsigned)atomic((int(*)())read, writer, (char *)&chkpt, sizeof (struct slaves)) != sizeof (struct slaves)) { cmdrderr(); dumpabort(); /*NOTREACHED*/ } if (atomic((int(*)())read, writer, (char *)&spcl, TP_BSIZE_MIN) != TP_BSIZE_MIN) { cmdrderr(); dumpabort(); /*NOTREACHED*/ } ino = chkpt.sl_inos - 1; pos = chkpt.sl_offset; leftover = chkpt.sl_count; dumpstate = chkpt.sl_state; blockswritten = ++chkpt.sl_tapea; if (dumpstate == DS_DONE) { if (archivepid) { /* * If archiving (either archive or * database), signal the archiver * to finish up. This must happen * before the writer exits in order * to avoid a race. */ (void) kill(archivepid, SIGUSR1); } (void) signal(SIGUSR1, SIG_IGN); (void) sigrelse(SIGUSR1); (void) kill(writepid, SIGUSR1); /* tell writer to exit */ lf_archoffset = 0LL; longjmp(checkpoint_buf, 1); /*NOTREACHED*/ } if (leftover) { (void) memmove(spcl.c_addr, &spcl.c_addr[spcl.c_count-leftover], leftover); bzero(&spcl.c_addr[leftover], TP_NINDIR-leftover); } if (writepid) { (void) kill(writepid, SIGUSR1); /* tell writer to exit */ (void) close(writer); writer = -1; } if (archivepid) { (void) waitpid(archivepid, &status, 0); /* wait for archiver */ #ifdef TDEBUG /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext("Archiver %ld returns with status %d\n"), (long)archivepid, status); #endif archivepid = 0; } /* * Checkpoint archive file */ if (!doingverify && archive) { lf_archoffset = lseek64(archivefd, (off64_t)0, 2); if (lf_archoffset < 0) { int saverr = errno; msg(gettext("Cannot position archive file `%s': %s\n"), archivefile, strerror(saverr)); dumpabort(); /*NOTREACHED*/ } (void) close(archivefd); archivefd = -1; } resetino(ino); if (dumpstate == DS_START) { msg(gettext( "Tape too short: changing volumes and restarting\n")); reset(); } if (!pipeout) { if (verify && !doingverify) trewind(); else { close_rewind(); changevol(); } } (void) sigrelse(SIGUSR1); otape(0); longjmp(checkpoint_buf, 1); /*NOTREACHED*/ } static void nap(int ms) { struct timeval tv; tv.tv_sec = ms / 1000; tv.tv_usec = (ms - tv.tv_sec * 1000) * 1000; (void) select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv); } static jmp_buf alrm_buf; /*ARGSUSED*/ static void alrm(int sig) { longjmp(alrm_buf, 1); /*NOTREACHED*/ } void nextdevice(void) { char *cp; if (host != NULL) /* we set the host only once in ufsdump */ return; host = NULL; if (strchr(tape, ':')) { if (diskette) { msg(gettext("Cannot do remote dump to diskette\n")); Exit(X_ABORT); } host = tape; tape = strchr(host, ':'); *tape++ = 0; cp = strchr(host, '@'); /* user@host? */ if (cp != (char *)0) cp++; else cp = host; } else cp = spcl.c_host; /* * dumpdev is provided for use in prompts and is of * the form: * hostname:device * sdumpdev is of the form: * hostname:device * for remote devices, and simply: * device * for local devices. */ if (dumpdev != (char *)NULL) { /* LINTED: dumpdev is not NULL */ free(dumpdev); } /*LINTED [cast to smaller integer]*/ dumpdev = xmalloc((size_t)((sizeof (spcl.c_host) + strlen(tape) + 2))); /* LINTED unsigned -> signed cast ok */ (void) sprintf(dumpdev, "%.*s:%s", (int)sizeof (spcl.c_host), cp, tape); if (cp == spcl.c_host) sdumpdev = strchr(dumpdev, ':') + 1; else sdumpdev = dumpdev; } /* * Gross hack due to misfeature of mt tape driver that causes * the device to rewind if we generate any signals. Guess * whether tape is rewind device or not -- for local devices * we can just look at the minor number. For rmt devices, * make an educated guess. */ int isrewind(int f) { struct stat64 sbuf; char *c; int unit; int rewind; if (host) { c = strrchr(tape, '/'); if (c == NULL) c = tape; else c++; /* * If the last component begins or ends with an 'n', it is * assumed to be a non-rewind device. */ if (c[0] == 'n' || c[strlen(c)-1] == 'n') rewind = 0; else if ((strstr(tape, "mt") || strstr(tape, "st")) && sscanf(tape, "%*[a-zA-Z/]%d", &unit) == 1 && (unit & MT_NOREWIND)) rewind = 0; else rewind = 1; } else { if (fstat64(f, &sbuf) < 0) { msg(gettext( "Cannot obtain status of output device `%s'\n"), tape); dumpabort(); /*NOTREACHED*/ } rewind = minor(sbuf.st_rdev) & MT_NOREWIND ? 0 : 1; } return (rewind); } static void just_rewind(void) { struct slaves *slavep; char *rewinding = gettext("Tape rewinding\n"); for (slavep = &slaves[0]; slavep < &slaves[SLAVES]; slavep++) { if (slavep->sl_slavepid > 0) /* signal normal exit */ (void) kill(slavep->sl_slavepid, SIGTERM); if (slavep->sl_slavefd >= 0) { (void) close(slavep->sl_slavefd); slavep->sl_slavefd = -1; } } /* wait for any signals from slaves */ while (waitpid(0, (int *)0, 0) >= 0) /*LINTED [empty body]*/ continue; if (pipeout) return; if (doingverify) { /* * Space to the end of the tape. * Backup first in case we already read the EOF. */ if (host) { (void) rmtioctl(MTBSR, 1); if (rmtioctl(MTEOM, 1) < 0) (void) rmtioctl(MTFSF, 1); } else { static struct mtop bsr = { MTBSR, 1 }; static struct mtop eom = { MTEOM, 1 }; static struct mtop fsf = { MTFSF, 1 }; (void) ioctl(to, MTIOCTOP, &bsr); if (ioctl(to, MTIOCTOP, &eom) < 0) (void) ioctl(to, MTIOCTOP, &fsf); } } /* * Guess whether the tape is rewinding so we can tell * the operator if it's going to take a long time. */ if (tapeout && isrewind(to)) { /* tape is probably rewinding */ msg(rewinding); } } void trewind(void) { (void) timeclock((time_t)0); if (offline && (!verify || doingverify)) { close_rewind(); } else { just_rewind(); if (host) rmtclose(); else { (void) close(to); to = -1; } } } void close_rewind(void) { char *rewinding = gettext("Tape rewinding\n"); (void) timeclock((time_t)0); just_rewind(); /* * The check in just_rewind won't catch the case in * which the current volume is being taken off-line * and is not mounted on a no-rewind device (and is * not the last volume, which is not taken off-line). */ if (tapeout && !isrewind(to) && offline) { /* tape is probably rewinding */ msg(rewinding); } if (host) { if (offline || autoload) (void) rmtioctl(MTOFFL, 0); rmtclose(); } else { if (offline || autoload) { static struct mtop offl = { MTOFFL, 0 }; (void) ioctl(to, MTIOCTOP, &offl); if (diskette) (void) ioctl(to, FDEJECT, 0); } (void) close(to); to = -1; } } void changevol(void) { char buf1[3000], buf2[3000]; char volname[LBLSIZE+1]; /*CONSTANTCONDITION*/ assert(sizeof (spcl.c_label) < sizeof (volname)); filenum = 1; nextdevice(); (void) strcpy(spcl.c_label, tlabel); if (host) { char *rhost = host; char *cp = strchr(host, '@'); if (cp == (char *)0) cp = host; else cp++; if (rmthost(rhost, ntrec) == 0) { msg(gettext("Cannot connect to tape host `%s'\n"), cp); dumpabort(); /*NOTREACHED*/ } if (rhost != host) free(rhost); } /* * Make volume switching as automatic as possible * while avoiding overwriting volumes. We will * switch automatically under the following condition: * 1) The user specified autoloading from the * command line. * At one time, we (in the guise of hsmdump) had the * concept of a sequence of devices to rotate through, * but that's never been a ufsdump feature. */ if (autoload) { int tries; /* * Stop the clock for throughput calculations. */ if ((telapsed != NULL) && (tstart_writing != NULL)) { *telapsed += time((time_t *)NULL) - *tstart_writing; } (void) snprintf(volname, sizeof (volname), "#%d", tapeno+1); (void) snprintf(buf1, sizeof (buf1), gettext( "Mounting volume %s on %s\n"), volname, dumpdev); msg(buf1); broadcast(buf1); /* * Wait for the tape to autoload. Note that the delay * period doesn't take into account however long it takes * for the open to fail (measured at 21 seconds for an * Exabyte 8200 under 2.7 on an Ultra 2). */ for (tries = 0; tries < autoload_tries; tries++) { if (host) { if (rmtopen(tape, O_RDONLY) >= 0) { rmtclose(); return; } } else { int f, m; m = (access(tape, F_OK) == 0) ? 0 : O_CREAT; if ((f = doingverify ? safe_device_open(tape, O_RDONLY, 0600) : safe_device_open(tape, O_RDONLY|m, 0600)) >= 0) { (void) close(f); return; } } (void) sleep(autoload_period); } /* * Autoload timed out, ask the operator to do it. * Note that query() will update *telapsed, and we * shouldn't charge for the autoload time. So, since * we updated *telapsed ourselves above, we just set * tstart_writing to the current time, and query() * will end up making a null-effect change. This, * of course, assumes that our caller will be resetting * *tstart_writing. This is currently the case. * If tstart_writing is NULL (should never happen), * we're ok, since time(2) will accept a NULL pointer. */ (void) time(tstart_writing); } if (strncmp(spcl.c_label, "none", 5)) { (void) strncpy(volname, spcl.c_label, sizeof (spcl.c_label)); volname[sizeof (spcl.c_label)] = '\0'; } else (void) snprintf(volname, sizeof (volname), "#%d", tapeno+1); timeest(1, spcl.c_tapea); (void) snprintf(buf1, sizeof (buf1), gettext( "Change Volumes: Mount volume `%s' on `%s'\n"), volname, dumpdev); msg(buf1); broadcast(gettext("CHANGE VOLUMES!\7\7\n")); (void) snprintf(buf1, sizeof (buf1), gettext( "Is the new volume (%s) mounted on `%s' and ready to go?: %s"), volname, dumpdev, gettext("(\"yes\" or \"no\") ")); while (!query(buf1)) { (void) snprintf(buf2, sizeof (buf2), gettext( "Do you want to abort dump?: (\"yes\" or \"no\") ")); if (query(buf2)) { dumpabort(); /*NOTREACHED*/ } } } /* * 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 otape(int top) { static struct mtget mt; char buf[3000]; pid_t parentpid; pid_t childpid; pid_t waitproc; int status; struct sigvec sv, osv; sv.sv_flags = SA_RESTART; (void) sigemptyset(&sv.sa_mask); sv.sv_handler = SIG_IGN; (void) sigvec(SIGINT, &sv, (struct sigvec *)0); parentpid = getpid(); if (verify) { if (doingverify) doingverify = 0; else Exit(X_VERIFY); } restore_check_point: sv.sv_handler = interrupt; (void) sigvec(SIGINT, &sv, (struct sigvec *)0); (void) fflush(stderr); /* * All signals are inherited... */ sighold(SIGINT); childpid = fork(); if (childpid < 0) { msg(gettext( "Context-saving fork failed in parent %ld\n"), (long)parentpid); Exit(X_ABORT); } if (childpid != 0) { /* * PARENT: * save the context by waiting * until the child doing all of the work returns. * let the child catch user interrupts */ sv.sv_handler = SIG_IGN; (void) sigvec(SIGINT, &sv, (struct sigvec *)0); sigrelse(SIGINT); #ifdef TDEBUG /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext( "Volume: %d; parent process: %ld child process %ld\n"), tapeno+1, (long)parentpid, (long)childpid); #endif /* TDEBUG */ for (;;) { waitproc = waitpid(0, &status, 0); if (waitproc == childpid) break; msg(gettext( "Parent %ld waiting for child %ld had another child %ld return\n"), (long)parentpid, (long)childpid, (long)waitproc); } if (WIFSIGNALED(status)) { msg(gettext("Process %ld killed by signal %d: %s\n"), (long)childpid, WTERMSIG(status), strsignal(WTERMSIG(status))); status = X_ABORT; } else status = WEXITSTATUS(status); #ifdef TDEBUG switch (status) { case X_FINOK: /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext( "Child %ld finishes X_FINOK\n"), (long)childpid); break; case X_ABORT: /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext( "Child %ld finishes X_ABORT\n"), (long)childpid); break; case X_REWRITE: /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext( "Child %ld finishes X_REWRITE\n"), (long)childpid); break; case X_RESTART: /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext( "Child %ld finishes X_RESTART\n"), (long)childpid); break; case X_VERIFY: /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext( "Child %ld finishes X_VERIFY\n"), (long)childpid); break; default: /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext("Child %ld finishes unknown %d\n"), (long)childpid, status); break; } #endif /* TDEBUG */ switch (status) { case X_FINOK: /* wait for children */ while (waitpid(0, (int *)0, 0) >= 0) /*LINTED [empty body]*/ continue; Exit(X_FINOK); /*NOTREACHED*/ case X_ABORT: Exit(X_ABORT); /*NOTREACHED*/ case X_VERIFY: doingverify++; goto restore_check_point; /*NOTREACHED*/ case X_REWRITE: doingverify = 0; changevol(); goto restore_check_point; /* NOTREACHED */ case X_RESTART: doingverify = 0; if (!top) { Exit(X_RESTART); } if (!offline) autoload = 0; changevol(); sv.sv_handler = interrupt; (void) sigvec(SIGINT, &sv, (struct sigvec *)0); return; /* NOTREACHED */ default: msg(gettext("Bad return code from dump: %d\n"), status); Exit(X_ABORT); /*NOTREACHED*/ } /*NOTREACHED*/ } else { /* we are the child; just continue */ child_chdir(); sigrelse(SIGINT); #ifdef TDEBUG (void) sleep(4); /* time for parent's message to get out */ /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext( "Child on Volume %d has parent %ld, my pid = %ld\n"), tapeno+1, (long)parentpid, (long)getpid()); #endif (void) snprintf(buf, sizeof (buf), gettext( "Cannot open `%s'. Do you want to retry the open?: (\"yes\" or \"no\") "), dumpdev); if (doingverify) { /* 1 for stdout */ while ((to = host ? rmtopen(tape, O_RDONLY) : pipeout ? 1 : safe_device_open(tape, O_RDONLY, 0600)) < 0) { perror(tape); if (autoload) { if (!query_once(buf, 1)) { dumpabort(); /*NOTREACHED*/ } } else { if (!query(buf)) { dumpabort(); /*NOTREACHED*/ } } } /* * If we're using the non-rewinding tape device, * the tape will be left positioned after the * EOF mark. We need to back up to the beginning * of this tape file (cross two tape marks in the * reverse direction and one in the forward * direction) before the verify pass. */ if (host) { if (rmtioctl(MTBSF, 2) >= 0) (void) rmtioctl(MTFSF, 1); else (void) rmtioctl(MTNBSF, 1); } else { static struct mtop bsf = { MTBSF, 2 }; static struct mtop fsf = { MTFSF, 1 }; static struct mtop nbsf = { MTNBSF, 1 }; if (ioctl(to, MTIOCTOP, &bsf) >= 0) (void) ioctl(to, MTIOCTOP, &fsf); else (void) ioctl(to, MTIOCTOP, &nbsf); } } else { /* * XXX Add logic to test for "tape" being a * XXX device or a non-existent file. * Current behaviour is that it must exist, * and we over-write whatever's there. * This can be bad if tape == "/etc/passwd". */ if (!pipeout && doposition && (tapeno == 0)) { positiontape(buf); if (setjmp(alrm_buf)) { /* * The tape is rewinding; * we're screwed. */ msg(gettext( "Cannot position tape using " "rewind device!\n")); dumpabort(); /*NOTREACHED*/ } else { sv.sv_handler = alrm; (void) sigvec(SIGALRM, &sv, &osv); (void) alarm(15); } while ((to = host ? rmtopen(tape, O_WRONLY) : safe_device_open(tape, O_WRONLY, 0600)) < 0) (void) sleep(10); (void) alarm(0); (void) sigvec(SIGALRM, &osv, (struct sigvec *)0); } else { int m; m = (access(tape, F_OK) == 0) ? 0 : O_CREAT; /* * Only verify the tape label if label * verification is on and we are at BOT */ if (pipeout) to = 1; else while ((to = host ? rmtopen(tape, O_WRONLY) : safe_device_open(tape, O_WRONLY|m, 0600)) < 0) if (!query_once(buf, 1)) { dumpabort(); /*NOTREACHED*/ } } } if (!pipeout) { tapeout = host ? rmtstatus(&mt) >= 0 : ioctl(to, MTIOCGET, &mt) >= 0; /* set state */ /* * Make sure the tape is positioned * where it is supposed to be */ if (tapeout && (tapeno > 0) && (mt.mt_fileno != (filenum-1))) { (void) snprintf(buf, sizeof (buf), gettext( "Warning - tape positioning error!\n\ \t%s current file %ld, should be %ld\n"), tape, mt.mt_fileno+1, filenum); msg(buf); dumpailing(); } } tapeno++; /* current tape sequence */ if (tapeno < TP_NINOS) inos[tapeno] = chkpt.sl_inos; spcl.c_firstrec = chkpt.sl_firstrec; spcl.c_tapea = (*tapea) = chkpt.sl_tapea; spcl.c_volume++; enslave(); /* Share tape buffers with slaves */ #ifdef DEBUG if (xflag) { /* XGETTEXT: #ifdef DEBUG only */ msg(gettext("Checkpoint state:\n")); msg(" blockswritten %u\n", blockswritten); msg(" ino %u\n", ino); msg(" pos %u\n", pos); msg(" left %u\n", leftover); msg(" tapea %u\n", (*tapea)); msg(" state %d\n", dumpstate); } #endif spcl.c_type = TS_TAPE; spcl.c_tpbsize = tp_bsize; if (leftover == 0) { spcl.c_count = 0; spclrec(); newtape = 0; } else newtape++; /* new volume indication */ if (doingverify) { msg(gettext("Starting verify pass\n")); } else if (tapeno > 1) { msg(gettext( "Volume %d begins with blocks from inode %lu\n"), tapeno, chkpt.sl_inos); } (void) timeclock((time_t)1); (void) time(tstart_writing); timeest(0, spcl.c_tapea); } } void dumpabort(void) { if (master && master != getpid()) /* * signal master to call dumpabort */ (void) kill(master, SIGTERM); else { killall(); if (archivefile && archive_opened) (void) unlink(archivefile); msg(gettext("The ENTIRE dump is aborted.\n")); } Exit(X_ABORT); } void dumpailing(void) { broadcast(gettext("DUMP IS AILING!\n")); if (!query(gettext( "Do you want to attempt to continue? (\"yes\" or \"no\") "))) { dumpabort(); /*NOTREACHED*/ } } void Exit(int status) { /* * Clean up message system */ #ifdef TDEBUG /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext("pid = %ld exits with status %d\n"), (long)getpid(), status); #endif /* TDEBUG */ exit(status); } static void killall(void) { struct slaves *slavep; for (slavep = &slaves[0]; slavep < &slaves[SLAVES]; slavep++) if (slavep->sl_slavepid > 0) { (void) kill(slavep->sl_slavepid, SIGKILL); #ifdef TDEBUG /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext("Slave child %ld killed\n"), (long)slavep->sl_slavepid); #endif } if (writepid) { (void) kill(writepid, SIGKILL); #ifdef TDEBUG /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext("Writer child %ld killed\n"), (long)writepid); #endif } if (archivepid) { (void) kill(archivepid, SIGKILL); #ifdef TDEBUG /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext("Archiver child %ld killed\n"), (long)archivepid); #endif } } /*ARGSUSED*/ static void proceed(int sig) { caught++; } /*ARGSUSED*/ static void die(int sig) { Exit(X_FINOK); } static void enslave(void) { int cmd[2]; /* file descriptors */ int i; struct sigvec sv; struct slaves *slavep; int saverr; sv.sv_flags = SA_RESTART; (void) sigemptyset(&sv.sa_mask); master = getpid(); /* * slave sends SIGTERM on dumpabort */ sv.sv_handler = (void(*)(int))dumpabort; (void) sigvec(SIGTERM, &sv, (struct sigvec *)0); sv.sv_handler = tperror; (void) sigvec(SIGUSR2, &sv, (struct sigvec *)0); sv.sv_handler = proceed; (void) sigvec(SIGUSR1, &sv, (struct sigvec *)0); totalrecsout += recsout; caught = 0; recsout = 0; rotor = 0; bufclear(); for (slavep = &slaves[0]; slavep < &slaves[SLAVES]; slavep++) slavep->sl_slavefd = -1; archivefd = arch = writer = -1; for (i = 0; i < SLAVES; i++) { if (pipe(cmd) < 0) { saverr = errno; msg(gettext( "Cannot create pipe for slave process: %s\n"), strerror(saverr)); dumpabort(); /*NOTREACHED*/ } sighold(SIGUSR2); sighold(SIGINT); sighold(SIGTERM); if ((slaves[i].sl_slavepid = fork()) < 0) { saverr = errno; msg(gettext("Cannot create slave process: %s\n"), strerror(saverr)); dumpabort(); /*NOTREACHED*/ } slaves[i].sl_slavefd = cmd[1]; if (slaves[i].sl_slavepid == 0) { /* Slave starts up here */ pid_t next; /* pid of neighbor */ sv.sv_handler = SIG_DFL; (void) sigvec(SIGUSR2, &sv, (struct sigvec *)0); sv.sv_handler = SIG_IGN; /* master handler INT */ (void) sigvec(SIGINT, &sv, (struct sigvec *)0); sv.sv_handler = die; /* normal slave exit */ (void) sigvec(SIGTERM, &sv, (struct sigvec *)0); child_chdir(); sigrelse(SIGUSR2); sigrelse(SIGINT); sigrelse(SIGTERM); freeino(); /* release unneeded resources */ #ifdef TDEBUG (void) sleep(4); /* time for parent's message to get out */ /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext("Neighbor has pid = %ld\n"), (long)getpid()); #endif /* Closes cmd[1] as a side-effect */ for (slavep = &slaves[0]; slavep < &slaves[SLAVES]; slavep++) if (slavep->sl_slavefd >= 0) { (void) close(slavep->sl_slavefd); slavep->sl_slavefd = -1; } (void) close(to); (void) close(fi); /* Need our own seek ptr */ to = -1; fi = open(disk, O_RDONLY); if (fi < 0) { saverr = errno; msg(gettext( "Cannot open dump device `%s': %s\n"), disk, strerror(saverr)); dumpabort(); /*NOTREACHED*/ } if ((unsigned)atomic((int(*)())read, cmd[0], (char *)&next, sizeof (next)) != sizeof (next)) { cmdrderr(); dumpabort(); /*NOTREACHED*/ } dumpoffline(cmd[0], next, i); Exit(X_FINOK); } /* Parent continues here */ sigrelse(SIGUSR2); sigrelse(SIGINT); sigrelse(SIGTERM); (void) close(cmd[0]); } if (archive) { archivepid = setuparchive(); if (!archivepid) { dumpabort(); /*NOTREACHED*/ } } writepid = setupwriter(); if (!writepid) { dumpabort(); /*NOTREACHED*/ } if (arch >= 0) { (void) close(arch); /* only writer has this open */ arch = -1; } /* Tell each slave who follows it */ for (i = 0; i < SLAVES; i++) { if ((unsigned)atomic((int(*)())write, slaves[i].sl_slavefd, (char *)&(slaves[(i + 1) % SLAVES].sl_slavepid), sizeof (int)) != sizeof (int)) { cmdwrterr(); dumpabort(); /*NOTREACHED*/ } } sv.sv_handler = rollforward; /* rcvd from writer on EOT */ (void) sigvec(SIGUSR1, &sv, (struct sigvec *)0); slp = slaves; (void) kill(slp->sl_slavepid, SIGUSR1); master = 0; } static void wait_our_turn(void) { (void) sighold(SIGUSR1); if (!caught) { #ifdef INSTRUMENT (*idle)++; #endif (void) sigpause(SIGUSR1); } caught = 0; (void) sigrelse(SIGUSR1); } static void dumpoffline(int cmd, pid_t next, int mynum) { struct req *p = slaves[mynum].sl_req; ulong_t i; uchar_t *cp; uchar_t *blkbuf; int notactive = 0; blkbuf = xmalloc(sblock->fs_bsize); /*CONSTANTCONDITION*/ assert(sizeof (spcl) == TP_BSIZE_MIN); while (atomic((int(*)())read, cmd, (char *)p, reqsiz) == reqsiz) { if (p->br_dblk) { bread(p->br_dblk, (uchar_t *)blkbuf, p->br_size); } else { bcopy((char *)p->br_spcl, (char *)&spcl, sizeof (spcl)); ino = spcl.c_inumber; } dumptoarchive = p->aflag & BUF_ARCHIVE; wait_our_turn(); if (p->br_dblk) { for (i = p->br_size, cp = blkbuf; i > 0; /* LINTED character pointers aren't signed */ cp += i > tp_bsize ? tp_bsize : i, i -= i > tp_bsize ? tp_bsize : i) { /* LINTED unsigned to signed conversion ok */ taprec(cp, 0, i > tp_bsize ? tp_bsize : (int)i); } } else spclrec(); (void) kill(next, SIGUSR1); /* Next slave's turn */ /* * Note that we lie about file activity since we don't * check for it. */ if ((unsigned)atomic((int(*)())write, cmd, (char *)¬active, sizeof (notactive)) != sizeof (notactive)) { cmdwrterr(); dumpabort(); /*NOTREACHED*/ } } free(blkbuf); } static int count; /* tape blocks written since last spclrec */ /*ARGSUSED*/ static void onxfsz(int sig) { msg(gettext("File size limit exceeded writing output volume %d\n"), tapeno); (void) kill(master, SIGUSR2); Exit(X_REWRITE); } static long lastnonaddr; /* last DS_{INODE,CLRI,BITS} written */ static long lastnonaddrm; /* and the mode thereof */ /* * dowrite -- the main body of the output writer process */ static void dowrite(int cmd) { struct bdesc *last = &bufp[(NBUF*ntrec)-1]; /* last buffer in pool */ struct bdesc *bp = bufp; /* current buf in tape block */ struct bdesc *begin = bufp; /* first buf of tape block */ struct bdesc *end = bufp + (ntrec-1); /* last buf of tape block */ int siz; /* bytes written (block) */ int trecs; /* records written (block) */ long asize = 0; /* number of 0.1" units... */ /* ...written on current tape */ char *tp, *rbuf = NULL; char *recmap = spcl.c_addr; /* current tape record map */ char *endmp; /* end of valid map data */ char *mp; /* current map entry */ union u_spcl *sp; (void) signal(SIGXFSZ, onxfsz); bzero((char *)&spcl, sizeof (spcl)); count = 0; if (doingverify) { rbuf = (char *)malloc((uint_t)writesize); if (rbuf == 0) { /* Restart from checkpoint */ (void) kill(master, SIGUSR2); Exit(X_REWRITE); } } for (;;) { /* START: wait until all buffers in tape block are full */ if ((bp->b_flags & BUF_FULL) == 0) { if (caught) { /* master signalled flush */ (void) sighold(SIGUSR1); caught = 0; /* signal ready */ (void) kill(master, SIGUSR1); chkpt.sl_count = 0; /* signal not at EOT */ checkpoint(bp-1, cmd); /* send data */ (void) sigpause(SIGUSR1); break; } #ifdef INSTRUMENT (*readmissp)++; #endif nap(50); continue; } if (bp < end) { bp++; continue; } /* END: wait until all buffers in tape block are full */ tp = begin->b_data; (void) sighold(SIGUSR1); if (host) { if (!doingverify) siz = rmtwrite(tp, writesize); else if ((siz = rmtread(rbuf, writesize)) == writesize && bcmp(rbuf, tp, writesize)) siz = -1; } else { if (!doingverify) siz = write(to, tp, writesize); else if ((siz = read(to, rbuf, writesize)) == writesize && bcmp(rbuf, tp, writesize)) siz = -1; if (siz < 0 && diskette && errno == ENOSPC) siz = 0; /* really EOF */ } (void) sigrelse(SIGUSR1); if (siz < 0 || (pipeout && siz != writesize)) { char buf[3000]; /* * Isn't i18n wonderful? */ if (doingverify) { if (diskette) (void) snprintf(buf, sizeof (buf), gettext( "Verification error %ld blocks into diskette %d\n"), asize * 2, tapeno); else if (tapeout) (void) snprintf(buf, sizeof (buf), gettext( "Verification error %ld feet into tape %d\n"), (cartridge ? asize/tracks : asize)/120L, tapeno); else (void) snprintf(buf, sizeof (buf), gettext( "Verification error %ld blocks into volume %d\n"), asize * 2, tapeno); } else { if (diskette) (void) snprintf(buf, sizeof (buf), gettext( "Write error %ld blocks into diskette %d\n"), asize * 2, tapeno); else if (tapeout) (void) snprintf(buf, sizeof (buf), gettext( "Write error %ld feet into tape %d\n"), (cartridge ? asize/tracks : asize)/120L, tapeno); else (void) snprintf(buf, sizeof (buf), gettext( "Write error %ld blocks into volume %d\n"), asize * 2, tapeno); } msg(buf); /* Restart from checkpoint */ #ifdef TDEBUG /* XGETTEXT: #ifdef TDEBUG only */ msg(gettext("sending SIGUSR2 to pid %ld\n"), master); #endif (void) kill(master, SIGUSR2); Exit(X_REWRITE); } trecs = siz / tp_bsize; if (diskette) asize += trecs; /* asize == blocks written */ else asize += (siz/density + tenthsperirg); if (trecs) chkpt.sl_firstrec++; for (bp = begin; bp < begin + trecs; bp++) { if ((arch >= 0) && (bp->b_flags & BUF_ARCHIVE)) { if ((unsigned)atomic((int(*)())write, arch, (char *)&bp->b_flags, sizeof (bp->b_flags)) != sizeof (bp->b_flags)) { cmdwrterr(); dumpabort(); /*NOTREACHED*/ } if (atomic((int(*)())write, arch, bp->b_data, tp_bsize) != tp_bsize) { cmdwrterr(); dumpabort(); /*NOTREACHED*/ } } if (bp->b_flags & BUF_SPCLREC) { /*LINTED [bp->b_data is aligned]*/ sp = (union u_spcl *)bp->b_data; if (sp->s_spcl.c_type != TS_ADDR) { lastnonaddr = sp->s_spcl.c_type; lastnonaddrm = sp->s_spcl.c_dinode.di_mode; if (sp->s_spcl.c_type != TS_TAPE) chkpt.sl_offset = 0; } chkpt.sl_count = sp->s_spcl.c_count; bcopy((char *)sp, (char *)&spcl, sizeof (spcl)); mp = recmap; endmp = &recmap[spcl.c_count]; count = 0; } else { chkpt.sl_offset++; chkpt.sl_count--; count++; mp++; } /* * Adjust for contiguous hole */ for (; mp < endmp; mp++) { if (*mp) break; chkpt.sl_offset++; chkpt.sl_count--; } } /* * Check for end of tape */ if (trecs < ntrec || (!pipeout && tsize > 0 && asize > tsize)) { if (tapeout) msg(gettext("End-of-tape detected\n")); else msg(gettext("End-of-file detected\n")); (void) sighold(SIGUSR1); caught = 0; (void) kill(master, SIGUSR1); /* signal EOT */ checkpoint(--bp, cmd); /* send checkpoint data */ (void) sigpause(SIGUSR1); break; } for (bp = begin; bp <= end; bp++) bp->b_flags = BUF_EMPTY; if (end + ntrec > last) { bp = begin = bufp; timeest(0, spcl.c_tapea); } else bp = begin = end+1; end = begin + (ntrec-1); } if (rbuf != NULL) free(rbuf); } /* * Send checkpoint info back to master. This information * consists of the current inode number, number of logical * blocks written for that inode (or bitmap), the last logical * block number written, the number of logical blocks written * to this volume, the current dump state, and the current * special record map. */ static void checkpoint(struct bdesc *bp, int cmd) { int state, type; ino_t ino; if (++bp >= &bufp[NBUF*ntrec]) bp = bufp; /* * If we are dumping files and the record following * the last written to tape is a special record, use * it to get an accurate indication of current state. */ if ((bp->b_flags & BUF_SPCLREC) && (bp->b_flags & BUF_FULL) && lastnonaddr == TS_INODE) { /*LINTED [bp->b_data is aligned]*/ union u_spcl *nextspcl = (union u_spcl *)bp->b_data; if (nextspcl->s_spcl.c_type == TS_INODE) { chkpt.sl_offset = 0; chkpt.sl_count = 0; } else if (nextspcl->s_spcl.c_type == TS_END) { chkpt.sl_offset = 0; chkpt.sl_count = 1; /* EOT indicator */ } ino = nextspcl->s_spcl.c_inumber; type = nextspcl->s_spcl.c_type; } else { /* * If not, use what we have. */ ino = spcl.c_inumber; type = spcl.c_type; } switch (type) { /* set output state */ case TS_ADDR: switch (lastnonaddr) { case TS_INODE: case TS_TAPE: if ((lastnonaddrm & IFMT) == IFDIR || (lastnonaddrm & IFMT) == IFATTRDIR) state = DS_DIRS; else state = DS_FILES; break; case TS_CLRI: state = DS_CLRI; break; case TS_BITS: state = DS_BITS; break; } break; case TS_INODE: if ((spcl.c_dinode.di_mode & IFMT) == IFDIR || (spcl.c_dinode.di_mode & IFMT) == IFATTRDIR) state = DS_DIRS; else state = DS_FILES; break; case 0: /* EOT on 1st record */ case TS_TAPE: state = DS_START; ino = UFSROOTINO; break; case TS_CLRI: state = DS_CLRI; break; case TS_BITS: state = DS_BITS; break; case TS_END: if (spcl.c_type == TS_END) state = DS_DONE; else state = DS_END; break; } /* * Checkpoint info to be processed by rollforward(): * The inode with which the next volume should begin * The last inode number on this volume * The last logical block number on this volume * The current output state * The offset within the current inode (already in sl_offset) * The number of records left from last spclrec (in sl_count) * The physical block the next vol begins with (in sl_firstrec) */ chkpt.sl_inos = ino; chkpt.sl_tapea = spcl.c_tapea + count; chkpt.sl_state = state; if ((unsigned)atomic((int(*)())write, cmd, (char *)&chkpt, sizeof (chkpt)) != sizeof (chkpt)) { cmdwrterr(); dumpabort(); /*NOTREACHED*/ } if ((unsigned)atomic((int(*)())write, cmd, (char *)&spcl, sizeof (spcl)) != sizeof (spcl)) { cmdwrterr(); dumpabort(); /*NOTREACHED*/ } #ifdef DEBUG if (xflag) { /* XGETTEXT: #ifdef DEBUG only */ msg(gettext("sent chkpt to master:\n")); msg(" ino %u\n", chkpt.sl_inos); msg(" 1strec %u\n", chkpt.sl_firstrec); msg(" lastrec %u\n", chkpt.sl_tapea); msg(" written %u\n", chkpt.sl_offset); msg(" left %u\n", chkpt.sl_count); msg(" state %d\n", chkpt.sl_state); } #endif } /* * 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 ssize_t atomic(int (*func)(), int fd, char *buf, int count) { ssize_t got = 0, need = count; /* don't inherit random value if immediately get zero back from func */ errno = 0; while (need > 0) { got = (*func)(fd, buf, MIN(need, 4096)); if (got < 0 && errno == EINTR) continue; if (got <= 0) break; buf += got; need -= got; } /* if we got what was asked for, return count, else failure (got) */ return ((need != 0) ? got : count); } void positiontape(char *msgbuf) { /* Static as never change, no need to waste stack space */ static struct mtget mt; static struct mtop rew = { MTREW, 1 }; static struct mtop fsf = { MTFSF, 1 }; char *info = strdup(gettext("Positioning `%s' to file %ld\n")); char *fail = strdup(gettext("Cannot position tape to file %d\n")); int m; /* gettext()'s return value is volatile, hence the strdup()s */ m = (access(tape, F_OK) == 0) ? 0 : O_CREAT; /* * To avoid writing tape marks at inappropriate places, we open the * device read-only, position it, close it, and reopen it for writing. */ while ((to = host ? rmtopen(tape, O_RDONLY) : safe_device_open(tape, O_RDONLY|m, 0600)) < 0) { if (autoload) { if (!query_once(msgbuf, 1)) { dumpabort(); /*NOTREACHED*/ } } else { if (!query(msgbuf)) { dumpabort(); /*NOTREACHED*/ } } } if (host) { if (rmtstatus(&mt) >= 0 && rmtioctl(MTREW, 1) >= 0 && filenum > 1) { msg(info, dumpdev, filenum); if (rmtioctl(MTFSF, filenum-1) < 0) { msg(fail, filenum); dumpabort(); /*NOTREACHED*/ } } rmtclose(); } else { if (ioctl(to, MTIOCGET, &mt) >= 0 && ioctl(to, MTIOCTOP, &rew) >= 0 && filenum > 1) { msg(info, dumpdev, filenum); fsf.mt_count = filenum - 1; if (ioctl(to, MTIOCTOP, &fsf) < 0) { msg(fail, filenum); dumpabort(); /*NOTREACHED*/ } } (void) close(to); to = -1; } free(info); free(fail); } static void cmdwrterr(void) { int saverr = errno; msg(gettext("Error writing command pipe: %s\n"), strerror(saverr)); } static void cmdrderr(void) { int saverr = errno; msg(gettext("Error reading command pipe: %s\n"), strerror(saverr)); } /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #include "dump.h" #include #include static void lf_dmpindir(daddr32_t, int, u_offset_t *); static void indir(daddr32_t, int, u_offset_t *); static void lf_blksout(daddr32_t *, u_offset_t); static void lf_dumpinode(struct dinode *); static void dsrch(daddr32_t, ulong_t, u_offset_t); void lf_dump(struct dinode *); int dadded; int nsubdir; int shortmeta; static char msgbuf[256]; void pass(void (*fn)(struct dinode *), uchar_t *map) { int bits; ino_t maxino; maxino = (unsigned)(sblock->fs_ipg * sblock->fs_ncg - 1); /* * Handle pass restarts. We don't check for UFSROOTINO just in * case we need to restart on the root inode. */ if (ino != 0) { bits = ~0; if (map != NULL) { /* LINTED: lint seems to think map is signed */ map += (ino / NBBY); bits = *map++; } bits >>= (ino % NBBY); resetino(ino); goto restart; } while (ino < maxino) { if ((ino % NBBY) == 0) { bits = ~0; if (map != NULL) bits = *map++; } restart: ino++; /* * Ignore any inode less than UFSROOTINO and inodes that * we have already done on a previous pass. */ if ((ino >= UFSROOTINO) && (bits & 1)) { /* * The following test is merely an optimization * for common case where "add" will just return. */ if (!(fn == add && BIT(ino, nodmap))) (*fn)(getino(ino)); } bits >>= 1; } } void mark(struct dinode *ip) { int f; f = ip->di_mode & IFMT; if (f == 0 || ip->di_nlink <= 0) { /* LINTED: 32-bit to 8-bit assignment ok */ BIC(ino, clrmap); return; } /* LINTED: 32-bit to 8-bit assignment ok */ BIS(ino, clrmap); if (f == IFDIR || f == IFATTRDIR) { /* LINTED: 32-bit to 8-bit assignment ok */ BIS(ino, dirmap); } if (ip->di_ctime >= spcl.c_ddate) { if (f == IFSHAD) return; /* LINTED: 32-bit to 8-bit assignment ok */ BIS(ino, nodmap); /* attribute changes impact the root */ if (f == IFATTRDIR) BIS(UFSROOTINO, nodmap); if (f != IFREG && f != IFDIR && f != IFATTRDIR && f != IFLNK) { o_esize += 1; return; } est(ip); } } void active_mark(struct dinode *ip) { int f; f = ip->di_mode & IFMT; if (f == 0 || ip->di_nlink <= 0) { /* LINTED: 32-bit to 8-bit assignment ok */ BIC(ino, clrmap); return; } /* LINTED: 32-bit to 8-bit assignment ok */ BIS(ino, clrmap); if (f == IFDIR || f == IFATTRDIR) { /* LINTED: 32-bit to 8-bit assignment ok */ BIS(ino, dirmap); } if (BIT(ino, activemap)) { /* LINTED: 32-bit to 8-bit assignment ok */ BIS(ino, nodmap); /* attribute changes impact the root */ if (f == IFATTRDIR) BIS(UFSROOTINO, nodmap); if (f != IFREG && f != IFDIR && f != IFATTRDIR && f != IFLNK) { o_esize += 1; return; } est(ip); } } static struct shcount { struct shcount *higher, *lower; ino_t ino; unsigned long count; } shcounts = { NULL, NULL, 0, 0 }; static struct shcount *shc = NULL; void markshad(struct dinode *ip) { ino_t shadow; if (ip->di_shadow == 0) return; if (shc == NULL) shc = &shcounts; shadow = (ino_t)(unsigned)(ip->di_shadow); while ((shadow > shc->ino) && (shc->higher)) shc = shc->higher; while ((shadow < shc->ino) && (shc->lower)) shc = shc->lower; if (shadow != shc->ino) { struct shcount *new; new = (struct shcount *)xcalloc(1, sizeof (*new)); new->higher = shc->higher; if (shc->higher != NULL) shc->higher->lower = new; shc->higher = new; new->lower = shc; shc = new; shc->ino = shadow; } /* LINTED: 32-bit to 8-bit assignment ok */ BIS(shadow, shamap); shc->count++; } void estshad(struct dinode *ip) { u_offset_t esizeprime; u_offset_t tmpesize; if (ip->di_size <= sizeof (union u_shadow)) return; while ((ino > shc->ino) && (shc->higher)) shc = shc->higher; while ((ino < shc->ino) && (shc->lower)) shc = shc->lower; if (ino != shc->ino) return; /* xxx panic? complain? */ tmpesize = (o_esize + f_esize); esizeprime = tmpesize; est(ip); esizeprime = tmpesize - esizeprime; esizeprime *= shc->count - 1; f_esize += esizeprime; } void freeshad() { if (shc == NULL) return; while (shc->higher) shc = shc->higher; while (shc->lower) { shc = shc->lower; if (shc->higher) /* else panic? */ (void) free(shc->higher); } /* * This should be unnecessary, but do it just to be safe. * Note that shc might be malloc'd or static, so can't free(). */ bzero(shc, sizeof (*shc)); } void add(struct dinode *ip) { int i; u_offset_t filesize; if (BIT(ino, nodmap)) return; if ((ip->di_mode & IFMT) != IFDIR && (ip->di_mode & IFMT) != IFATTRDIR) { (void) snprintf(msgbuf, sizeof (msgbuf), gettext( "Warning - directory at inode `%lu' vanished!\n"), ino); msg(msgbuf); /* LINTED: 32-bit to 8-bit assignment ok */ BIC(ino, dirmap); return; } nsubdir = 0; dadded = 0; filesize = ip->di_size; for (i = 0; i < NDADDR; i++) { if (ip->di_db[i] != 0) /* LINTED dblksize/blkoff does a safe cast here */ dsrch(ip->di_db[i], (ulong_t)dblksize(sblock, ip, i), filesize); filesize -= (unsigned)(sblock->fs_bsize); } for (i = 0; i < NIADDR; i++) { if (ip->di_ib[i] != 0) indir(ip->di_ib[i], i, &filesize); } if (dadded) { nadded++; if (!BIT(ino, nodmap)) { /* LINTED: 32-bit to 8-bit assignment ok */ BIS(ino, nodmap); if ((ip->di_mode & IFMT) == IFATTRDIR) { /* attribute changes "auto-percolate" to root */ BIS(UFSROOTINO, nodmap); } est(ip); } } if (nsubdir == 0) { if (!BIT(ino, nodmap)) { /* LINTED: 32-bit to 8-bit assignment ok */ BIC(ino, dirmap); } } } static void indir(daddr32_t d, int n, u_offset_t *filesize) { int i; daddr32_t idblk[MAXNINDIR]; if ((unsigned)(sblock->fs_bsize) > sizeof (idblk)) { msg(gettext( "Inconsistency detected: filesystem block size larger than valid maximum.\n")); dumpabort(); /*NOTREACHED*/ } if ((unsigned)NINDIR(sblock) > MAXNINDIR) { /*CSTYLED*/ msg(gettext( "Inconsistency detected: inode has more indirect \ blocks than valid maximum.\n")); dumpabort(); /*NOTREACHED*/ } if (dadded || *filesize == 0) return; #ifdef lint idblk[0] = '\0'; #endif /* lint */ /* xxx sanity check sblock contents before trusting them */ bread(fsbtodb(sblock, d), (uchar_t *)idblk, (size_t)sblock->fs_bsize); if (n <= 0) { for (i = 0; i < NINDIR(sblock); i++) { d = idblk[i]; if (d != 0) dsrch(d, (ulong_t)(uint32_t)sblock->fs_bsize, *filesize); *filesize -= (unsigned)(sblock->fs_bsize); } } else { n--; for (i = 0; i < NINDIR(sblock); i++) { d = idblk[i]; if (d != 0) indir(d, n, filesize); } } } void dirdump(struct dinode *ip) { /* watchout for dir inodes deleted and maybe reallocated */ if (((ip->di_mode & IFMT) != IFDIR && (ip->di_mode & IFMT) != IFATTRDIR) || ip->di_nlink < 2) { (void) snprintf(msgbuf, sizeof (msgbuf), gettext( "Warning - directory at inode `%lu' vanished!\n"), ino); msg(msgbuf); return; } lf_dump(ip); } static u_offset_t loffset; /* current offset in file (ufsdump) */ static void lf_dumpmeta(struct dinode *ip) { if ((ip->di_shadow == 0) || shortmeta) return; lf_dumpinode(getino((ino_t)(unsigned)(ip->di_shadow))); } int hasshortmeta(struct dinode **ip) { ino_t savino; int rc; if ((*ip)->di_shadow == 0) return (0); savino = ino; *ip = getino((ino_t)(unsigned)((*ip)->di_shadow)); rc = ((*ip)->di_size <= sizeof (union u_shadow)); *ip = getino(ino = savino); return (rc); } void lf_dumpinode(struct dinode *ip) { int i; u_offset_t size; i = ip->di_mode & IFMT; if (i == 0 || ip->di_nlink <= 0) return; spcl.c_dinode = *ip; spcl.c_count = 0; if ((i != IFDIR && i != IFATTRDIR && i != IFREG && i != IFLNK && i != IFSHAD) || ip->di_size == 0) { toslave(dospcl, ino); return; } size = NDADDR * (unsigned)(sblock->fs_bsize); if (size > ip->di_size) size = ip->di_size; lf_blksout(&ip->di_db[0], size); size = ip->di_size - size; if (size > 0) { for (i = 0; i < NIADDR; i++) { lf_dmpindir(ip->di_ib[i], i, &size); if (size == 0) break; } } } void lf_dump(struct dinode *ip) { if ((!BIT(ino, nodmap)) && (!BIT(ino, shamap))) return; shortmeta = hasshortmeta(&ip); if (shortmeta) { ip = getino((ino_t)(unsigned)(ip->di_shadow)); /* assume spcl.c_shadow is smaller than 1 block */ bread(fsbtodb(sblock, ip->di_db[0]), (uchar_t *)spcl.c_shadow.c_shadow, sizeof (spcl.c_shadow)); spcl.c_flags |= DR_HASMETA; } else { spcl.c_flags &= ~DR_HASMETA; } ip = getino(ino); loffset = 0; if (newtape) { spcl.c_type = TS_TAPE; } else if (pos) spcl.c_type = TS_ADDR; else spcl.c_type = TS_INODE; newtape = 0; lf_dumpinode(ip); lf_dumpmeta(ip); pos = 0; } static void lf_dmpindir(daddr32_t blk, int lvl, u_offset_t *size) { int i; u_offset_t cnt; daddr32_t idblk[MAXNINDIR]; if ((unsigned)(sblock->fs_bsize) > sizeof (idblk)) { msg(gettext( "Inconsistency detected: filesystem block size larger than valid maximum.\n")); dumpabort(); /*NOTREACHED*/ } if ((unsigned)NINDIR(sblock) > MAXNINDIR) { msg(gettext( "Inconsistency detected: inode has more indirect \ blocks than valid maximum.\n")); dumpabort(); /*NOTREACHED*/ } if (blk != 0) bread(fsbtodb(sblock, blk), (uchar_t *)idblk, (size_t)sblock->fs_bsize); else bzero((char *)idblk, (size_t)sblock->fs_bsize); if (lvl <= 0) { cnt = (u_offset_t)(unsigned)NINDIR(sblock) * (u_offset_t)(unsigned)(sblock->fs_bsize); if (cnt > *size) cnt = *size; *size -= cnt; lf_blksout(&idblk[0], cnt); return; } lvl--; for (i = 0; i < NINDIR(sblock); i++) { lf_dmpindir(idblk[i], lvl, size); if (*size == 0) return; } } static void lf_blksout(daddr32_t *blkp, u_offset_t bytes) { u_offset_t i; u_offset_t tbperfsb = (unsigned)(sblock->fs_bsize / tp_bsize); u_offset_t j, k, count; u_offset_t bytepos, diff; u_offset_t bytecnt = 0; off_t byteoff = 0; /* bytes to skip within first f/s block */ off_t fragoff = 0; /* frags to skip within first f/s block */ u_offset_t tpblkoff = 0; /* tape blocks to skip in first f/s block */ u_offset_t tpblkskip = 0; /* total tape blocks to skip */ u_offset_t skip; /* tape blocks to skip this pass */ if (pos) { /* * We get here if a slave throws a signal to the * master indicating a partially dumped file. * Begin by figuring out what was undone. */ bytepos = (offset_t)pos * tp_bsize; if ((loffset + bytes) <= bytepos) { /* This stuff was dumped already, forget it. */ loffset += (u_offset_t)tp_bsize * /* LINTED: spurious complaint on sign-extending */ d_howmany(bytes, (u_offset_t)tp_bsize); return; } if (loffset < bytepos) { /* * Some of this was dumped, some wasn't. * Figure out what was done and skip it. */ diff = bytepos - loffset; /* LINTED: spurious complaint on sign-extending */ tpblkskip = d_howmany(diff, (u_offset_t)tp_bsize); /* LINTED room after EOT is only a few MB */ blkp += (int)(diff / sblock->fs_bsize); bytecnt = diff % (unsigned)(sblock->fs_bsize); /* LINTED: result fits, due to modulus */ byteoff = bytecnt % (off_t)(sblock->fs_fsize); /* LINTED: spurious complaint on sign-extending */ tpblkoff = d_howmany(bytecnt, (u_offset_t)(unsigned)tp_bsize); /* LINTED: result fits, due to modulus */ fragoff = bytecnt / (off_t)(sblock->fs_fsize); bytecnt = (unsigned)(sblock->fs_bsize) - bytecnt; } } loffset += bytes; while (bytes > 0) { if (bytes < TP_NINDIR*tp_bsize) /* LINTED: spurious complaint on sign-extending */ count = d_howmany(bytes, (u_offset_t)tp_bsize); else count = TP_NINDIR; if (tpblkskip) { if (tpblkskip < TP_NINDIR) { bytes -= (tpblkskip * (u_offset_t)tp_bsize); skip = tpblkskip; tpblkskip = 0; } else { bytes -= (offset_t)TP_NINDIR*tp_bsize; tpblkskip -= TP_NINDIR; continue; } } else skip = 0; assert(tbperfsb >= tpblkoff); assert((count - skip) <= TP_NINDIR); for (j = 0, k = 0; j < count - skip; j++, k++) { spcl.c_addr[j] = (blkp[k] != 0); for (i = tbperfsb - tpblkoff; --i > 0; j++) spcl.c_addr[j+1] = spcl.c_addr[j]; tpblkoff = 0; } /* LINTED (count - skip) will always fit into an int32_t */ spcl.c_count = count - skip; toslave(dospcl, ino); bytecnt = MIN(bytes, bytecnt ? bytecnt : (unsigned)(sblock->fs_bsize)); j = 0; while (j < count - skip) { if (*blkp != 0) { /* LINTED: fragoff fits into 32 bits */ dmpblk(*blkp+(int32_t)fragoff, /* LINTED: bytecnt fits into 32 bits */ (size_t)bytecnt, byteoff); } blkp++; bytes -= bytecnt; /* LINTED: spurious complaint on sign-extending */ j += d_howmany(bytecnt, (u_offset_t)tp_bsize); bytecnt = MIN(bytes, (unsigned)(sblock->fs_bsize)); byteoff = 0; fragoff = 0; } spcl.c_type = TS_ADDR; bytecnt = 0; } pos = 0; } void bitmap(uchar_t *map, int typ) { int i; u_offset_t count; uchar_t *cp; if (!newtape) spcl.c_type = typ; else newtape = 0; for (i = 0; i < TP_NINDIR; i++) spcl.c_addr[i] = 1; /* LINTED: spurious complaint on sign-extending */ count = d_howmany(msiz * sizeof (map[0]), tp_bsize) - pos; for (cp = &map[pos * tp_bsize]; count > 0; count -= (u_offset_t)(unsigned)spcl.c_count) { if (leftover) { spcl.c_count = leftover; leftover = 0; } else { /* LINTED value always less than INT32_MAX */ spcl.c_count = count > TP_NINDIR ? TP_NINDIR : count; } spclrec(); for (i = 0; i < spcl.c_count; i++, cp += tp_bsize) taprec(cp, 0, tp_bsize); spcl.c_type = TS_ADDR; } } static void dsrch(daddr32_t d, ulong_t size, u_offset_t filesize) { struct direct *dp; struct dinode *ip; ulong_t loc; char dblk[MAXBSIZE]; if (dadded || filesize == 0) return; if (filesize > (u_offset_t)size) filesize = (u_offset_t)size; if (sizeof (dblk) < roundup(filesize, DEV_BSIZE)) { msg(gettext( "Inconsistency detected: filesystem block size larger than valid maximum.\n")); dumpabort(); /*NOTREACHED*/ } #ifdef lint dblk[0] = '\0'; #endif /* lint */ /* LINTED ufs disk addresses always fit into 32 bits */ bread(fsbtodb(sblock, d), (uchar_t *)dblk, /* LINTED from sizeof check above, roundup() <= max(size_t) */ (size_t)(roundup(filesize, DEV_BSIZE))); loc = 0; while ((u_offset_t)loc < filesize) { /*LINTED [dblk is char[], loc (dp->d_reclen) % 4 == 0]*/ dp = (struct direct *)(dblk + loc); if (dp->d_reclen == 0) { (void) snprintf(msgbuf, sizeof (msgbuf), gettext( "Warning - directory at inode `%lu' is " "corrupted\n"), ino); msg(msgbuf); break; } loc += dp->d_reclen; if (dp->d_ino == 0) continue; if (dp->d_name[0] == '.') { if (dp->d_name[1] == '\0') { if ((ino_t)(dp->d_ino) != ino) { (void) snprintf(msgbuf, sizeof (msgbuf), gettext( "Warning - directory at inode `%lu' is corrupted:\n\ \t\".\" points to inode `%lu' - run fsck\n"), ino, dp->d_ino); msg(msgbuf); } continue; } if (dp->d_name[1] == '.' && dp->d_name[2] == '\0') { if (!BIT(dp->d_ino, dirmap) && ((ip = getino(ino)) == NULL || (ip->di_mode & IFMT) != IFATTRDIR)) { (void) snprintf(msgbuf, sizeof (msgbuf), gettext( "Warning - directory at inode `%lu' is corrupted:\n\ \t\"..\" points to non-directory inode `%lu' - run fsck\n"), ino, dp->d_ino); msg(msgbuf); } continue; } } if (BIT(dp->d_ino, nodmap)) { dadded++; return; } if (BIT(dp->d_ino, dirmap)) nsubdir++; } } #define CACHESIZE 32 struct dinode * getino(ino_t ino) { static ino_t minino, maxino; static struct dinode itab[MAXINOPB]; static struct dinode icache[CACHESIZE]; static ino_t icacheval[CACHESIZE], lasti = 0; static int cacheoff = 0; int i; if (ino >= minino && ino < maxino) { lasti = ino; return (&itab[ino - minino]); } /* before we do major i/o, check for a secondary cache hit */ for (i = 0; i < CACHESIZE; i++) if (icacheval[i] == ino) return (icache + i); /* we need to do major i/o. throw the last inode retrieved into */ /* the cache. note: this copies garbage the first time it is */ /* used, but no harm done. */ icacheval[cacheoff] = lasti; bcopy(itab + (lasti - minino), icache + cacheoff, sizeof (itab[0])); lasti = ino; if (++cacheoff >= CACHESIZE) cacheoff = 0; #define INOPERDB (DEV_BSIZE / sizeof (struct dinode)) minino = ino &~ (INOPERDB - 1); maxino = ((itog(sblock, ino) + 1) * (unsigned)(sblock->fs_ipg)); if (maxino > minino + MAXINOPB) maxino = minino + MAXINOPB; bread( /* LINTED: can't make up for broken system macros here */ (fsbtodb(sblock, itod(sblock, ino)) + itoo(sblock, ino) / INOPERDB), /* LINTED: (max - min) * size fits into a size_t */ (uchar_t *)itab, (size_t)((maxino - minino) * sizeof (*itab))); return (&itab[ino - minino]); } #define BREADEMAX 32 #ifdef NO__LONGLONG__ #define DEV_LSEEK(fd, offset, whence) \ lseek((fd), (((off_t)(offset))*DEV_BSIZE), (whence)) #else #define DEV_LSEEK(fd, offset, whence) \ llseek((fd), (((offset_t)((offset)))*DEV_BSIZE), (whence)) #endif #define BREAD_FAIL(buf, size) { \ breaderrors += 1; \ bzero(buf, (size_t)size); \ } void bread(diskaddr_t da, uchar_t *ba, size_t cnt) { caddr_t maddr; uchar_t *dest; int saverr; int n; size_t len; off64_t filoff; off64_t mapoff; off64_t displacement; static size_t pagesize = 0; static int breaderrors = 0; /* mechanics for caching small bread requests. these are */ /* often small ACLs that are used over and over. */ static uchar_t bcache[DEV_BSIZE * CACHESIZE]; static diskaddr_t bcacheval[CACHESIZE]; static int cacheoff = 0; int i; if ((cnt >= DEV_BSIZE) && (mapfd != -1)) { if (pagesize == 0) pagesize = getpagesize(); /* * We depend on mmap(2)'s guarantee that mapping a * partial page will cause the remainder of the page * to be zero-filled. */ filoff = ((off64_t)da) * DEV_BSIZE; displacement = filoff & (pagesize - 1); mapoff = filoff - displacement; /* LINTED offset will fit into 32 bits */ len = (size_t)roundup(cnt + (filoff - mapoff), pagesize); maddr = mmap64(NULL, len, PROT_READ, MAP_SHARED, mapfd, mapoff); if (maddr != MAP_FAILED) { (void) memcpy(ba, maddr + displacement, cnt); (void) munmap(maddr, len); return; } } if (DEV_LSEEK(fi, da, L_SET) < 0) { saverr = errno; msg(gettext("bread: dev_seek error: %s\n"), strerror(saverr)); /* Don't know where we are, return the least-harmful data */ BREAD_FAIL(ba, cnt); return; } if (read(fi, ba, (size_t)cnt) == (size_t)cnt) return; while (cnt != 0) { if (da >= fsbtodb(sblock, sblock->fs_size)) { msg(gettext( "Warning - block %llu is beyond the end of `%s'\n"), da, disk); BREAD_FAIL(ba, cnt); break; } if (DEV_LSEEK(fi, da, L_SET) < 0) { msg(gettext("%s: %s error\n"), "bread", "DEV_LSEEK2"); BREAD_FAIL(ba, cnt); break; } if (cnt < DEV_BSIZE) { /* small read. check for cache hit. */ for (i = 0; i < CACHESIZE; i++) if (bcacheval[i] == da) { bcopy(bcache + (i * DEV_BSIZE), ba, cnt); return; } /* no cache hit; throw this one into the cache... */ len = cnt; dest = bcache + (cacheoff * DEV_BSIZE); bcacheval[cacheoff] = da; if (++cacheoff >= CACHESIZE) cacheoff = 0; } else { len = DEV_BSIZE; dest = ba; } n = read(fi, dest, DEV_BSIZE); if (n != DEV_BSIZE) { n = MAX(n, 0); bzero(dest+n, DEV_BSIZE-n); breaderrors += 1; msg(gettext( "Warning - cannot read sector %llu of `%s'\n"), da, disk); } if (dest != ba) bcopy(dest, ba, len); da++; /* LINTED character pointers aren't signed */ ba += len; cnt -= len; } if (breaderrors > BREADEMAX) { msg(gettext( "More than %d block read errors from dump device `%s'\n"), BREADEMAX, disk); dumpailing(); breaderrors = 0; } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _DUMPUSG_H #define _DUMPUSG_H /* * Translate from BSD to System V, where possible. */ /* * System-V specific header files */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* * make mnttab look like mtab */ #define MOUNTED MNTTAB #define mntent mnttab #define mnt_fsname mnt_special #define mnt_dir mnt_mountp #define mnt_type mnt_fstype #define mnt_opts mnt_mntopts #define MNTTYPE_42 "ufs" #define MNTINFO_DEV "dev" #define setmntent fopen #define endmntent fclose /* * Function translations */ #define gethostname(name, len) \ ((sysinfo(SI_HOSTNAME, (name), (len)) < 0) ? -1 : 0) #define signal nsignal /* defined in dumpmain.c */ #define sigvec sigaction /* both struct and func */ #define sv_flags sa_flags #define sv_handler sa_handler #define sv_mask sa_mask #define sigmask(x) x #define setreuid(r, e) seteuid(e) #define statfs statvfs /* both struct and func */ #undef setjmp #define setjmp(b) sigsetjmp((b), 1) #define longjmp siglongjmp #define jmp_buf sigjmp_buf #if !__STDC__ extern int seteuid(); #endif /* * Inode related translations */ #define ROOTINO UFSROOTINO #define di_rdev di_ordev /* * For stat-inode translation. * Don't forget the translation from * nanosecs to usecs (or vica versa) */ #define st_spare1 st_atim.tv_nsec #define st_spare2 st_mtim.tv_nsec #define st_spare3 st_ctim.tv_nsec #define TMCONV 1000 #ifdef __cplusplus } #endif #endif /* _DUMPUSG_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ /* LINTLIBRARY */ /* * ftw - file tree walk * * int ftw (path, fn, depth) char *path; int (*fn)(); int depth; * * Given a path name, ftw starts from the file given by that path * name and visits each file and directory in the tree beneath * that file. If a single file has multiple links within the * structure, it will be visited once for each such link. * For each object visited, fn is called with three arguments. * The first contains the path name of the object, the second * contains a pointer to a stat buffer which will usually hold * appropriate information for the object and the third will * contain an integer value giving additional information: * * FTW_F The object is a file for which stat was * successful. It does not guarantee that the * file can actually be read. * * FTW_D The object is a directory for which stat and * open for read were both successful. * * FTW_DNR The object is a directory for which stat * succeeded, but which cannot be read. Because * the directory cannot be read, fn will not be * called for any descendants of this directory. * * FTW_NS Stat failed on the object because of lack of * appropriate permission. This indication will * be given, for example, for each file in a * directory with read but no execute permission. * Because stat failed, it is not possible to * determine whether this object is a file or a * directory. The stat buffer passed to fn will * contain garbage. Stat failure for any reason * other than lack of permission will be * considered an error and will cause ftw to stop * and return -1 to its caller. * * If fn returns nonzero, ftw stops and returns the same value * to its caller. If ftw gets into other trouble along the way, * it returns -1 and leaves an indication of the cause in errno. * * The third argument to ftw does not limit the depth to which * ftw will go. Rather, it limits the depth to which ftw will * go before it starts recycling file descriptors. In general, * it is necessary to use a file descriptor for each level of the * tree, but they can be recycled for deep trees by saving the * position, closing, re-opening, and seeking. It is possible * to start recycling file descriptors by sensing when we have * run out, but in general this will not be terribly useful if * fn expects to be able to open files. We could also figure out * how many file descriptors are available and guarantee a certain * number to fn, but we would not know how many to guarantee, * and we do not want to impose the extra overhead on a caller who * knows how many are available without having to figure it out. * * It is possible for ftw to die with a memory fault in the event * of a file system so deeply nested that the stack overflows. */ #include #include #include #include #include #include #include #include #include #include static int pwdfd; static int lf_xftw( const char *, int (*)(const char *, const struct stat64 *, int), int, int (*)(const char *, struct stat64 *)); int lf_lftw( const char *path, int (*fn)(const char *, const struct stat64 *, int), int depth) { int rc; if ((pwdfd = open(".", O_RDONLY)) < 0) { return (-1); } else { rc = (lf_xftw(path, fn, depth, lstat64)); (void) close(pwdfd); return (rc); } } static int #ifdef __STDC__ lf_xftw( const char *path, int (*fn)(const char *, const struct stat64 *, int), int depth, int (*statfn)(const char *, struct stat64 *)) #else lf_xftw(char *path, int (*fn)(), int depth, int (*statfn)()) #endif { int n; int rc, sublen, saverr, attrfd; DIR *dirp; char *subpath, *component; struct stat64 sb; struct dirent *dp; extern dev_t partial_dev; /* * Try to get file status. * If unsuccessful, errno will say why. */ if ((*statfn)(path, &sb) < 0) return (errno == EACCES? (*fn)(path, &sb, FTW_NS): -1); /* * The stat succeeded, so we know the object exists. * Make sure it is not a mount point for another filesystem. * The following check must be made here because: * * + namefs can be mounted on anything, but a directory * + all other filesystems must be mounted on a directory */ if (sb.st_dev != partial_dev) { return (0); } /* * Check for presence of attributes on file */ if (pathconf(path, _PC_XATTR_EXISTS) == 1) { attrfd = attropen64(path, ".", O_RDONLY|O_NONBLOCK); } else { attrfd = -1; } /* * If not a directory, call the user function and return. */ if ((sb.st_mode & S_IFMT) != S_IFDIR && (sb.st_mode & IFMT) != IFATTRDIR) { rc = (*fn)(path, &sb, FTW_F); if (rc == 0 && attrfd != -1) { (void) fchdir(attrfd); rc = lf_xftw(".", fn, depth-1, statfn); (void) fchdir(pwdfd); (void) close(attrfd); } return (rc); } /* * The object was a directory and not a mount point. * * Open a file to read the directory */ dirp = opendir(path); /* * Call the user function, telling it whether * the directory can be read. If it can't be read * call the user function or indicate an error, * depending on the reason it couldn't be read. */ if (dirp == NULL) rc = (errno == EACCES? (*fn)(path, &sb, FTW_DNR): -1); else rc = (*fn)(path, &sb, FTW_D); /* * If the directory has attributes, process the * attributes before processing the directory contents. */ if (rc == 0 && attrfd != -1) { (void) fchdir(attrfd); rc = lf_xftw(".", fn, depth-1, statfn); (void) fchdir(pwdfd); (void) close(attrfd); } if (rc != 0 || dirp == NULL) return (rc); /* Allocate a buffer to hold generated pathnames. */ /* LINTED: the length will fit into a signed integer */ n = (int)strlen(path); sublen = n + MAXNAMLEN + 1; /* +1 for appended / */ subpath = malloc((unsigned)(sublen+1)); /* +1 for NUL */ if (subpath == NULL) { saverr = errno; (void) closedir(dirp); errno = saverr; return (-1); } /* Create a prefix to which we will append component names */ (void) strcpy(subpath, path); if (subpath[0] != '\0' && subpath[n-1] != '/') subpath[n++] = '/'; component = &subpath[n]; /* LINTED: result will fit into a 32-bit int */ sublen -= component - subpath; /* * Read the directory one component at a time. * We must ignore "." and "..", but other than that, * just create a path name and call self to check it out. */ while ((dp = readdir(dirp)) != NULL) { if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) { long here; /* Append component name to the working path */ (void) strncpy(component, dp->d_name, sublen); component[sublen - 1] = '\0'; /* * If we are about to exceed our depth, * remember where we are and close a file. */ if (depth <= 1) { here = telldir(dirp); (void) closedir(dirp); } /* * Do a recursive call to process the file. * (watch this, sports fans) */ rc = lf_xftw(subpath, fn, depth-1, statfn); if (rc != 0) { free(subpath); if (depth > 1) (void) closedir(dirp); return (rc); } /* * If we closed the file, try to reopen it. */ if (depth <= 1) { dirp = opendir(path); if (dirp == NULL) { free(subpath); return (-1); } seekdir(dirp, here); } } } /* * We got out of the subdirectory loop. The return from * the final readdir is in dp. Clean up. */ free(subpath); (void) closedir(dirp); return (0); } /stdio.h",.*always ignored: fclose/d /stdio.h",.*always ignored: fprintf/d /stdio.h",.*always ignored: printf/d /stdio.h",.*always ignored: snprintf/d /time.h",.*sometimes ignored: time/d /string.h",.*sometimes ignored: memcpy/d /signal.h",.*always ignored: sighold/d /signal.h",.*always ignored: sigrelse/d /unistd.h",.*always ignored: close/d /unistd.h",.*always ignored: execl/d /unistd.h",.*always ignored: sleep/d /myrcmd.c",.*assigned value never used: retval .*(22[0-9])/d /dumptape.c",.*use of a pointer.*questionable/d /dumponline.c",.*name used but not defined: log10 /d /dumponline.c",.*name used but not defined: getfullblkname /d /dumpmain.c",.*name used but not defined: getfullrawname /d /^ obuf defined/d /roll_log.c",.*include file.*unnecessary:/d /partial.c",.*name used but not defined: ulimit/d /dirent.h",.*different definitions of macro: MAXNAMLEN/d /limits.h",.*different definitions of macro: SYS_NMLN/d /lint suppression directive not used/d /memutils.c",.*deallocating NULL pointer/d /memutils.c",.*assigned value never used: allocated at/d /dumpmain.c",.*assigned value never used:/d /dumptape.c",.*deallocating NULL pointer/d /dumpmain.c",.*deallocating NULL pointer/d /partial.c",.*deallocating NULL pointer/d /dumpitime.c",.*reference to deallocated memory/d /dumpitime.c",.*reference using a NULL pointer/d /assigned value never used: idates_in/d /roll_log.c",.*assigned value never used/d /dumptape.c",.*statement not reached/d /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1998,2001 by Sun Microsystems, Inc. * All rights reserved. */ #include "dump.h" #include #include dev_t partial_dev; static int partial; static dev_t devfromopts(struct mntent *); static int lf_mark_root(dev_t, char *); static int lf_ftw_mark(const char *, const struct stat64 *, int); static void markino(ino_t); void partial_check(void) { struct mntent *mnt; struct stat64 st; if (stat64(disk, &st) < 0 || (st.st_mode & S_IFMT) == S_IFCHR || (st.st_mode & S_IFMT) == S_IFBLK) return; partial_dev = st.st_dev; setmnttab(); while (mnt = getmnttab()) { st.st_dev = devfromopts(mnt); if (st.st_dev == NODEV && stat64(mnt->mnt_dir, &st) < 0) continue; if (partial_dev == st.st_dev) { if (disk_dynamic) { /* LINTED: disk is not NULL */ free(disk); } disk = rawname(mnt->mnt_fsname); disk_dynamic = (disk != mnt->mnt_fsname); partial = 1; incno = '0'; uflag = 0; return; } } msg(gettext("`%s' is not on a locally mounted filesystem\n"), disk); dumpabort(); /*NOTREACHED*/ } /* * The device id for the mount should be available in * the mount option string as "dev=%04x". If it's there * extract the device id and avoid having to stat. */ static dev_t devfromopts(struct mntent *mnt) { char *str; str = hasmntopt(mnt, MNTINFO_DEV); if (str != NULL && (str = strchr(str, '='))) return ((dev_t)strtol(str + 1, (char **)NULL, 16)); return (NODEV); } int partial_mark(int argc, char **argv) { char *path; struct stat64 st; if (partial == 0) return (1); while (--argc >= 0) { path = *argv++; if (stat64(path, &st) < 0 || st.st_dev != partial_dev) { msg(gettext("`%s' is not on dump device `%s'\n"), path, disk); dumpabort(); /*NOTREACHED*/ } if (lf_mark_root(partial_dev, path)) { msg(gettext( "Cannot find filesystem mount point for `%s'\n"), path); dumpabort(); /*NOTREACHED*/ } /* LINTED this ulimit will always be < INT_MAX */ if (lf_lftw(path, lf_ftw_mark, (int)ulimit(UL_GDESLIM, 0) / 2) < 0) { int saverr = errno; msg(gettext("Error in %s (%s)\n"), "ftw", strerror(saverr)); dumpabort(); /*NOTREACHED*/ } } return (0); } /* mark directories between target and root */ static int lf_mark_root(dev_t dev, char *path) { struct stat64 st; char dotdot[MAXPATHLEN + 16]; char *slash; if (strlen(path) > sizeof (dotdot)) return (1); (void) strcpy(dotdot, path); if (stat64(dotdot, &st) < 0) return (1); /* if target is a regular file, find directory */ if ((st.st_mode & S_IFMT) != S_IFDIR) if (slash = strrchr(dotdot, '/')) /* "/file" -> "/" */ if (slash == dotdot) slash[1] = 0; /* "dir/file" -> "dir" */ else slash[0] = 0; else /* "file" -> "." */ (void) strcpy(dotdot, "."); /* keep marking parent until we hit mount point */ do { if (stat64(dotdot, &st) < 0 || (st.st_mode & S_IFMT) != S_IFDIR || st.st_dev != dev) return (1); markino(st.st_ino); if (strlen(dotdot) > (sizeof (dotdot) - 4)) return (1); (void) strcat(dotdot, "/.."); } while (st.st_ino != 2); return (0); } /*ARGSUSED*/ static int lf_ftw_mark(const char *name, const struct stat64 *st, int flag) { if (flag != FTW_NS) { /* LINTED ufs only uses the lower 32 bits */ markino((ino_t)st->st_ino); } return (0); } static void markino(ino_t i) { struct dinode *dp; dp = getino(ino = i); mark(dp); } /* * Copyright (c) 1998,2001 by Sun Microsystems, Inc. * All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #include #include #include #include /* * Convert a ctime(3) format string into a system format date. * Return the date thus calculated. * * Return -1 if the string is not in ctime format. */ /* * Offsets into the ctime string to various parts. */ #define E_MONTH 4 #define E_DAY 8 #define E_HOUR 11 #define E_MINUTE 14 #define E_SECOND 17 #define E_YEAR 20 #ifdef __STDC__ static int lookup(char *); static time_t emitl(struct tm *); #else static int lookup(); static time_t emitl(); #endif time_t unctime(str) char *str; { struct tm then; char dbuf[30]; /* Definition of ctime(3) is 24 characters + newline + NUL */ (void) strncpy(dbuf, str, 24); dbuf[24] = '\0'; dbuf[E_MONTH+3] = '\0'; then.tm_mon = lookup(&dbuf[E_MONTH]); if (then.tm_mon < 0) { return (-1); } then.tm_mday = atoi(&dbuf[E_DAY]); then.tm_hour = atoi(&dbuf[E_HOUR]); then.tm_min = atoi(&dbuf[E_MINUTE]); then.tm_sec = atoi(&dbuf[E_SECOND]); then.tm_year = atoi(&dbuf[E_YEAR]) - 1900; return (emitl(&then)); } static char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; static int lookup(str) char *str; { char *cp, *cp2; for (cp = months, cp2 = str; *cp != 0; cp += 3) if (strncmp(cp, cp2, 3) == 0) /* LINTED ptr arith will give < INT_MAX result */ return (((int)(cp-months)) / 3); return (-1); } /* * Routine to convert a localtime(3) format date back into * a system format date. */ static time_t emitl(dp) struct tm *dp; { dp->tm_isdst = -1; return (mktime(dp)); }