# # 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 1993,1998,2002 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # cmd/backup/Makefile # include Makefile.backup COMMONPRODSUBDIRS= dump restore UFSPRODSUBDIRS= LIBSUBDIRS= lib LIBDONE= $(LIBSUBDIRS)/.target_done SUBDIRS= $(LIBSUBDIRS) $(COMMONPRODSUBDIRS) $(UFSPRODSUBDIRS) UFSSUBDIRS= $(LIBSUBDIRS) $(COMMONPRODSUBDIRS) $(UFSPRODSUBDIRS) UFSINSDIRS= $(UFSROOTETC) $(UFSROOTUSR) $(UFSROOTUSRLIB) \ $(UFSROOTUSRLIBFS) $(UFSROOTUSRSBIN) PODIRS= dump restore .KEEP_STATE: .DONE: @rm -f $(LIBDONE) all debug lint check: $(COMMONPRODSUBDIRS) $(UFSPRODSUBDIRS) _msg: $(PODIRS) clean clobber: $(SUBDIRS) .DONE install: $(UFSINSDIRS) $(UFSSUBDIRS) $(UFSINSDIRS): $(INS.dir) $(COMMONPRODSUBDIRS) $(UFSPRODSUBDIRS): $(LIBSUBDIRS) FRC $(DO_SUBDIR) $(LIBSUBDIRS): FRC $(DO_SUBDIR) FRC: # # 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. # # cmd/backup/Makefile.backup # # Copyright (c) 2018, Joyent, Inc. include $(SRC)/cmd/Makefile.cmd # -c: continuation line indentation (broken for multi-line continuations) # -h: heuristic checks (sometimes wrong) # -p: extra-picky # -v: verbose # -C: ignore header block comments # -P: check for non-POSIX types #CSTYLEFLAGS= -c -h -p -v -P CSTYLEFLAGS= -h -p -P CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-clobbered # not linted SMATCH=off UFSROOTETC= $(ROOT)/etc UFSROOTUSR= $(ROOT)/usr UFSROOTUSRLIB= $(UFSROOTUSR)/lib UFSROOTUSRLIBFS= $(UFSROOTUSRLIB)/fs UFSROOTUSRLIBFSTYPE= $(UFSROOTUSRLIBFS)/$(FSTYPE) UFSROOTUSRSBIN= $(UFSROOTUSR)/sbin UFSROOTPKGUSRLIBFSTYPE= $(PROG:%=$(UFSROOTUSRLIBFSTYPE)/%) UFSROOTPKGETC= $(PROT:%=$(UFSROOTETC)/%) $(UFSROOTETC)/%: % | $(UFSROOTETC) $(INS.file) $(UFSROOTUSRLIBFSTYPE)/%: % | $(UFSROOTUSRLIBFSTYPE) $(INS.file) # set up TARGET macro for all Makefile here all: TARGET= all install: TARGET= install clean: TARGET= clean clobber: TARGET= clobber debug: TARGET= debug check: TARGET= check _msg: TARGET= _msg # default dump library is the -O one DUMPLIB= libdump.a # define the DO_SUBDIR macro, so that it can be changed here for all Makefiles DO_SUBDIR= cd $@; pwd; $(MAKE) $(TARGET) DO_LIBDIR= @cd $(@D); pwd; $(MAKE) $(TARGET) # # 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 2004 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # support for debugging debug: DUMPLIB= libdump_g.a debug: CFLAGS= $(CCGDEBUG) -DDEBUG -DFDEBUG -DTDEBUG $(SBFLAGS) DEBUGDIR= .debug DEBUGOBJS= $(OBJECTS:%=$(DEBUGDIR)/%) DEBUGPRODUCTS= $(UFSDEBUGPRODUCT) UFSDEBUGPRODUCT= $(PRODUCT:%=%_g) UFSDEBUGPROG= $(PROG:%=%_g) debug: $(UFSDEBUGPROG) $(UFSDEBUGPROG): $(DEBUGDIR) $$(DEBUGOBJS) $$(LIBDUMP) $(LINK.c) -o $@ $(DEBUGOBJS) $(LDLIBS) $(POST_PROCESS) $(DEBUGDIR)/%.o: %.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) $(DEBUGDIR): -@mkdir -p $@ include $(SRC)/cmd/Makefile.targ # # 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)); } /* * 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 1990-1998, 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _BYTEORDER_H #define _BYTEORDER_H #include #include #include #ifdef __cplusplus extern "C" { #endif #define SUPPORTS_MTB_TAPE_FORMAT #include #include struct byteorder_ctx { int initialized; int Bcvt; }; #ifdef __STDC__ extern struct byteorder_ctx *byteorder_create(void); extern void byteorder_destroy(struct byteorder_ctx *); extern void byteorder_banner(struct byteorder_ctx *, FILE *); extern void swabst(char *, uchar_t *); extern uint32_t swabl(uint32_t); extern int normspcl(struct byteorder_ctx *, struct s_spcl *, int *, int, int); extern void normdirect(struct byteorder_ctx *, struct direct *); extern void normacls(struct byteorder_ctx *, ufs_acl_t *, int); #else /* __STDC__ */ extern struct byteorder_ctx *byteorder_create(); extern void byteorder_destroy(); extern void byteorder_banner(); extern void swabst(); extern uint32_t swabl(); extern int normspcl(); extern void normdirect(); extern void normacls(); #endif /* __STDC__ */ #ifdef __cplusplus } #endif #endif /* _BYTEORDER_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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _MEMUTILS_H #define _MEMUTILS_H #include #ifdef __cplusplus extern "C" { #endif #if defined(__STDC__) extern void *xmalloc(size_t); extern void *xcalloc(size_t, size_t); extern void *xrealloc(void *, size_t); #else extern void *xmalloc(); extern void *xcalloc(); extern void *xrealloc(); #endif /* __STDC__ */ NOTE(ALIGNMENT(xmalloc, 8)) NOTE(ALIGNMENT(xcalloc, 8)) NOTE(ALIGNMENT(xrealloc, 8)) #ifdef __cplusplus } #endif #endif /* _MEMUTILS_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) 1998 by Sun Microsystems, Inc. * All rights reserved. * * Internal definitions for the myrcmd.c rcmd(3) replacement module. */ #ifndef _MYRCMD_H #define _MYRCMD_H #ifdef __cplusplus extern "C" { #endif /* Failure return values */ #define MYRCMD_EBAD -1 #define MYRCMD_NOHOST -2 #define MYRCMD_ENOPORT -3 #define MYRCMD_ENOSOCK -4 #define MYRCMD_ENOCONNECT -5 /* * On a failure, the output that would have normally gone to stderr is * now placed in the global string "myrcmd_stderr". Callers should check * to see if there is anything in the string before trying to print it. */ extern char myrcmd_stderr[]; #ifdef __STDC__ extern int myrcmd(char **ahost, unsigned short rport, char *locuser, char *remuser, char *cmd); #else extern int myrcmd(); #endif /* __STDC__ */ #ifdef __cplusplus } #endif #endif /* _MYRCMD_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) 1991,1998 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _RMT_H #define _RMT_H #include #ifdef __cplusplus extern "C" { #endif #ifdef __STDC__ extern void rmtinit(void (*)(const char *, ...), void (*)(int)); extern int rmthost(char *, uint_t); extern int rmtopen(char *, int); extern void rmtclose(void); extern int rmtstatus(struct mtget *); extern int rmtread(char *, uint_t); extern int rmtwrite(char *, uint_t); extern int rmtseek(int, int); extern int rmtioctl(int, long); #else extern void rmtinit(); extern int rmthost(); extern int rmtopen(); extern void rmtclose(); extern int rmtstatus(); extern int rmtread(); extern int rmtwrite(); extern int rmtseek(); extern int rmtioctl(); #endif #ifdef __cplusplus } #endif #endif /* _RMT_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 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # LIBRARY= libdump.a # Has to be before include of Makefile.backup # This should be POFILE=libdump.po, but that causes make to # fall over due to some seriously weird interactions in the # various indirectly-included makefiles. So, since this works # and is otherwise harmless, we fake it. PROG= libdump # Include library definitions, then backup definitions, as in general # we want the flags and such from our tree. include ../../../lib/Makefile.lib include ../Makefile.backup SRCDIR = . # Specifically request the construction of a static library. # This library is not installed in the proto area. LIBS= $(LIBRARY) HDRS= ../include/byteorder.h \ ../include/memutils.h ../include/myrcmd.h \ ../../../head/protocols/dumprestore.h \ ../include/rmt.h YFILE= getdate.y YSRC= getdate.c CLOBBERFILES += $(LIBS) $(GLIB) LOBJS= rmtlib.o myrcmd.o \ $(YSRC:%.c=%.o) \ byteorder.o memutils.o $(RPC_CLNT:%.c=%.o) $(RPC_XDR:%.c=%.o) OBJECTS= $(LOBJS) POFILES= $(OBJECTS:.o=.po) GENERAL= ../include GLOBAL= ../../../head CPPFLAGS= -I$(GENERAL) -I$(GLOBAL) $(CPPFLAGS.master) CERRWARN += -Wno-implicit-function-declaration CERRWARN += -Wno-parentheses CERRWARN += -Wno-unused-variable CLEANFILES= $(OBJECTS) $(DEBUGS) $(YSRC) $(LIBRARY) # because of labels from yacc objs/getdate.o : CERRWARN += -Wno-unused-label # support for -g library GLIB= libdump_g.a DEBUGS= $(OBJECTS:%=.debug/%) $(GLIB): AROBJS = $(DEBUGS) $(GLIB): DIR = .debug $(GLIB): CFLAGS= $(CCGDEBUG) -DDEBUG -DYYDEBUG $(SBFLAGS) .KEEP_STATE: all: $(LIBS) debug: $(LIBS) $(GLIB) .debug: -@mkdir -p $@ .debug/%.o: %.c $(COMPILE.c) -o $@ $< $(POST_PROCESS_O) $(GLIB): .debug $$(DEBUGS) $(BUILD.AR) $(POST_PROCESS_A) $(OBJECTS): $(HDRS) install: all $(POFILE): $(POFILES) $(RM) $@; cat $(POFILES) > $@ check: $(HDRS) $(CSTYLE) $(CSTYLEFLAGS) `echo $(SRCS) | sed -e s/getdate.c//` $(HDRS) $(HDRCHK) $(HDRCHKFLAGS) $(HDRS) # include library targets include ../../../lib/Makefile.targ _msg: $(POFILE) /* * 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) 1983 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 #include #include #include #include #include #include struct byteorder_ctx * byteorder_create(void) { struct byteorder_ctx *rc; /* LINTED: assignment value is used */ if ((rc = (struct byteorder_ctx *)calloc(1, sizeof (*rc))) == NULL) return (NULL); return (rc); } void byteorder_destroy(struct byteorder_ctx *ctx) { if (ctx != NULL) (void) free((char *)ctx); } void byteorder_banner(struct byteorder_ctx *ctx, FILE *filep) { if ((! ctx->initialized) || (filep == NULL)) return; if (ctx->Bcvt) (void) fprintf(filep, gettext("Note: doing byte swapping\n")); } /* * Control string (cp) is a sequence of optional numeric repeat counts * and format specifiers. s/w/h indicate a 16-bit quantity is to be * byte-swapped, l indicates a 32-bit quantity. A repeat count is * identical in effect to having the following format character appear * N times (e.g., "3h" is equivalent to "hhh"). * * The byte-swapping is performed in-place, in the buffer sp. */ void swabst(char *cp, uchar_t *sp) { int n = 0; uchar_t c; while (*cp) { switch (*cp) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = (n * 10) + (*cp++ - '0'); continue; case 's': case 'w': case 'h': /* LINTED: type punning ok here */ c = sp[0]; sp[0] = sp[1]; sp[1] = c; sp++; break; case 'l': c = sp[0]; sp[0] = sp[3]; sp[3] = c; c = sp[2]; sp[2] = sp[1]; sp[1] = c; sp += 3; } /* Any other character, like 'b' counts as byte. */ sp++; if (n <= 1) { n = 0; cp++; } else n--; } } uint32_t swabl(uint32_t x) { uint32_t l = x; swabst("l", (uchar_t *)&l); /* LINTED: type punning ok here */ return (l); } static int checksum(struct byteorder_ctx *ctx, int *b, int size) { uint_t i, j; if (! ctx->initialized) return (-1); /* * We should only be called on to checksum u_spcl's, so make * sure that's what we got. */ if ((unsigned)size < tp_bsize) return (-1); j = tp_bsize / sizeof (int); i = 0; if (!ctx->Bcvt) { do i += (uint_t)*b++; while (--j); } else { /* * What happens if we want to read restore tapes * for a 16bit int machine??? */ do i += swabl(*b++); while (--j); } return (i != CHECKSUM); } /* * normspcl() checks that a spclrec is valid. it does byte/quad * swapping if necessary, and checks the checksum. it does NOT convert * from the old filesystem format; gethead() in tape.c does that. * * ctx is the context for this package * sp is a pointer to a current-format spclrec, that may need to be * byteswapped. * cs is a pointer to the thing we want to checksum. if we're * converting from the old filesystem format, it might be different * from sp. * css is the size of the thing we want to checksum. * magic is the magic number we compare against. */ int normspcl(struct byteorder_ctx *ctx, struct s_spcl *sp, int *cs, int css, int magic) { u_offset_t sv; if ((! ctx->initialized) && (sp->c_magic != magic)) { if (swabl(sp->c_magic) != (uint32_t)magic) return (-1); ctx->Bcvt = 1; } ctx->initialized = 1; if (checksum(ctx, cs, css)) return (-1); /* * Unless our caller is actively trying to break us, a * successful checksum() means that *sp is at least as * big as what we think it should be as far as byte * swapping goes. Therefore, we don't need to do any * more size checks here. */ /* handle byte swapping */ if (ctx->Bcvt) { /* * byteswap * c_type, c_date, c_ddate, c_volume, c_tapea, c_inumber, * c_magic, c_checksum, * all of c_dinode, and c_count. */ swabst("8l4s31l", (uchar_t *)sp); /* * byteswap * c_flags, c_firstrec, and c_spare. */ swabst("34l", (uchar_t *)&(sp->c_flags)); /* byteswap the inodes if necessary. */ #ifndef lint /* lint won't shut up about sprintf below */ if (sp->c_flags & DR_INODEINFO) { char buffy[BUFSIZ]; /* Can't overflow, max len is %d format (20)+`l'+\0 */ /* LINTED lint can't tell diff between %ld and %dl */ (void) sprintf(buffy, "%dl", TP_NINOS); swabst(buffy, (uchar_t *)sp->c_data.s_inos); } #endif /* lint */ /* if no metadata, byteswap the level */ if (! (sp->c_flags & DR_HASMETA)) swabst("1l", (uchar_t *)&(sp->c_level)); } /* handle quad swapping (note -- we no longer perform this check */ /* we now do quad swapping iff we're doing byte swapping.) */ /* * the following code is being changed during the large file * project. This code needed to be changed because ic_size * is no longer a quad, it has been changed to ic_lsize, which is * an offset_t, and the field "val" doesn't exist anymore. */ /* * This is the old code. (before large file project.) * * sv = sp->c_dinode.di_ic.ic_size.val; * * if (ctx->Bcvt) { * long foo; * * foo = sv[1]; * sv[1] = sv[0]; * sv[0] = foo; * } */ /* swap the upper 32 bits of ic_lsize with the lower 32 bits */ if (ctx->Bcvt) { sv = sp->c_dinode.di_ic.ic_lsize; sv = (sv << 32) | (sv >> 32); sp->c_dinode.di_ic.ic_lsize = sv; } if (sp->c_magic != magic) return (-1); return (0); } void normdirect(ctx, d) struct byteorder_ctx *ctx; struct direct *d; { assert(ctx->initialized); if (ctx->Bcvt) swabst("l2s", (uchar_t *)d); } void normacls(struct byteorder_ctx *ctx, ufs_acl_t *acl, int n) { static int complained = 0; int i; uid32_t uid; assert(ctx->initialized); if (! ctx->Bcvt) return; for (i = 0; i < n; i++) { swabst("1s", (uchar_t *)&(acl[i].acl_tag)); /* u_short */ swabst("1s", (uchar_t *)&(acl[i].acl_perm)); /* o_mode_t */ /* LINTED explicitly checking for truncation below */ uid = (uid32_t)(acl[i].acl_who); if (!complained && ((uid_t)uid) != acl[i].acl_who) { /* * The problem is that acl_who is a uid_t, * and we know that the on-tape version is * definitely 32 bits. To avoid getting * burned if/when uid_t becomes bigger * than that, we need to do the explicit * conversion and check. */ (void) fprintf(stderr, "Some ACL uids have been truncated\n"); complained = 1; } swabst("1l", (uchar_t *)&(uid)); /* uid32_t */ } } %{ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* $OrigRevision: 2.1 $ ** ** Originally written by Steven M. Bellovin while ** at the University of North Carolina at Chapel Hill. Later tweaked by ** a couple of people on Usenet. Completely overhauled by Rich $alz ** and Jim Berets in August, 1990; ** send any email to Rich. ** ** This grammar has eight shift/reduce conflicts. ** ** This code is in the public domain and has no copyright. */ /* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */ /* SUPPRESS 288 on yyerrlab *//* Label unused */ #include #include #include #define NEED_TZSET struct timeb { time_t time; /* Seconds since the epoch */ unsigned short millitm; /* Field not used */ short timezone; short dstflag; /* Field not used */ }; #include #include #include #include #include #include #if !defined(lint) && !defined(SABER) static char RCS[] = "$Header: /home/laramie/berliner/ws/backup/usr/src/cmd/backup/lib/getdate.y,v 1.5 1992/06/09 21:46:21 sam Exp $"; #endif /* !defined(lint) && !defined(SABER) */ #define EPOCH 1970 #define HOURN(x) (x * 60) #define SECSPERDAY (24L * 60L * 60L) #define CHECK_TM(y) (((y) % 100) < 70 ? (y) + 2000 : (y) + 1900) /* ** An entry in the lexical lookup table. */ typedef struct _TABLE { char *name; int type; time_t value; } TABLE; /* ** Daylight-savings mode: on, off, or not yet known. */ typedef enum _DSTMODE { DSTon, DSToff, DSTmaybe } DSTMODE; /* ** Meridian: am, pm, or 24-hour style. */ typedef enum _MERIDIAN { MERam, MERpm, MER24 } MERIDIAN; /* ** Global variables. We could get rid of most of these by using a good ** union as the yacc stack. (This routine was originally written before ** yacc had the %union construct.) Maybe someday; right now we only use ** the %union very rarely. */ static char *yyInput; static DSTMODE yyDSTmode; static time_t yyDayOrdinal; static time_t yyDayNumber; static int yyHaveDate; static int yyHaveDay; static int yyHaveRel; static int yyHaveTime; static int yyHaveZone; static time_t yyTimezone; static time_t yyDay; static time_t yyHour; static time_t yyMinutes; static time_t yyMonth; static time_t yySeconds; static time_t yyYear; static MERIDIAN yyMeridian; static time_t yyRelMonth; static time_t yyRelSeconds; static char *domainname = "hsm_libdump"; /* for dgettext() */ #define yylex 1 /* suppress yacc's definition */ %} %union { time_t Number; enum _MERIDIAN Meridian; } %token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT %token tSEC_UNIT tSNUMBER tUNUMBER tZONE %type tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT %type tSEC_UNIT tSNUMBER tUNUMBER tZONE %type tMERIDIAN o_merid %% spec : /* NULL */ | spec item ; item : time { yyHaveTime++; } | zone { yyHaveZone++; } | date { yyHaveDate++; } | day { yyHaveDay++; } | rel { yyHaveRel++; } | number ; time : tUNUMBER tMERIDIAN { yyHour = $1; yyMinutes = 0; yySeconds = 0; yyMeridian = $2; } | tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; yyMinutes = $3; yySeconds = 0; yyMeridian = $4; } | tUNUMBER ':' tUNUMBER tSNUMBER { yyHour = $1; yyMinutes = $3; yyMeridian = MER24; yyDSTmode = DSToff; yyTimezone = - ($4 % 100 + ($4 / 100) * 60); } | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { yyHour = $1; yyMinutes = $3; yySeconds = $5; yyMeridian = $6; } | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { yyHour = $1; yyMinutes = $3; yySeconds = $5; yyMeridian = MER24; yyDSTmode = DSToff; yyTimezone = - ($6 % 100 + ($6 / 100) * 60); } ; zone : tZONE { yyTimezone = $1; yyDSTmode = DSToff; } | tDAYZONE { yyTimezone = $1; yyDSTmode = DSTon; } ; day : tDAY { yyDayOrdinal = 1; yyDayNumber = $1; } | tDAY ',' { yyDayOrdinal = 1; yyDayNumber = $1; } | tUNUMBER tDAY { yyDayOrdinal = $1; yyDayNumber = $2; } ; date : tUNUMBER '/' tUNUMBER { yyMonth = $1; yyDay = $3; } | tUNUMBER '/' tUNUMBER '/' tUNUMBER { yyMonth = $1; yyDay = $3; yyYear = $5; } | tMONTH tUNUMBER { yyMonth = $1; yyDay = $2; } | tMONTH tUNUMBER ',' tUNUMBER { yyMonth = $1; yyDay = $2; yyYear = $4; } | tUNUMBER tMONTH { yyMonth = $2; yyDay = $1; } | tUNUMBER tMONTH tUNUMBER { yyMonth = $2; yyDay = $1; yyYear = $3; } ; rel : relunit tAGO { yyRelSeconds = -yyRelSeconds; yyRelMonth = -yyRelMonth; } | relunit ; relunit : tUNUMBER tMINUTE_UNIT { yyRelSeconds += $1 * $2 * 60L; } | tSNUMBER tMINUTE_UNIT { yyRelSeconds += $1 * $2 * 60L; } | tMINUTE_UNIT { yyRelSeconds += $1 * 60L; } | tSNUMBER tSEC_UNIT { yyRelSeconds += $1; } | tUNUMBER tSEC_UNIT { yyRelSeconds += $1; } | tSEC_UNIT { yyRelSeconds++; } | tSNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } | tUNUMBER tMONTH_UNIT { yyRelMonth += $1 * $2; } | tMONTH_UNIT { yyRelMonth += $1; } ; number : tUNUMBER { if (yyHaveTime && yyHaveDate && !yyHaveRel) yyYear = $1; else { yyHaveTime++; if ($1 < 100) { yyHour = $1; yyMinutes = 0; } else { yyHour = $1 / 100; yyMinutes = $1 % 100; } yySeconds = 0; yyMeridian = MER24; } } ; o_merid : /* NULL */ { $$ = MER24; } | tMERIDIAN { $$ = $1; } ; %% /* Month and day table. */ static TABLE MonthDayTable[] = { { "january", tMONTH, 1 }, { "february", tMONTH, 2 }, { "march", tMONTH, 3 }, { "april", tMONTH, 4 }, { "may", tMONTH, 5 }, { "june", tMONTH, 6 }, { "july", tMONTH, 7 }, { "august", tMONTH, 8 }, { "september", tMONTH, 9 }, { "sept", tMONTH, 9 }, { "october", tMONTH, 10 }, { "november", tMONTH, 11 }, { "december", tMONTH, 12 }, { "sunday", tDAY, 0 }, { "monday", tDAY, 1 }, { "tuesday", tDAY, 2 }, { "tues", tDAY, 2 }, { "wednesday", tDAY, 3 }, { "wednes", tDAY, 3 }, { "thursday", tDAY, 4 }, { "thur", tDAY, 4 }, { "thurs", tDAY, 4 }, { "friday", tDAY, 5 }, { "saturday", tDAY, 6 }, { NULL } }; /* Time units table. */ static TABLE UnitsTable[] = { { "year", tMONTH_UNIT, 12 }, { "month", tMONTH_UNIT, 1 }, { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, { "week", tMINUTE_UNIT, 7 * 24 * 60 }, { "day", tMINUTE_UNIT, 1 * 24 * 60 }, { "hour", tMINUTE_UNIT, 60 }, { "minute", tMINUTE_UNIT, 1 }, { "min", tMINUTE_UNIT, 1 }, { "second", tSEC_UNIT, 1 }, { "sec", tSEC_UNIT, 1 }, { NULL } }; /* Assorted relative-time words. */ static TABLE OtherTable[] = { { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, { "today", tMINUTE_UNIT, 0 }, { "now", tMINUTE_UNIT, 0 }, { "last", tUNUMBER, -1 }, { "this", tMINUTE_UNIT, 0 }, { "next", tUNUMBER, 2 }, { "first", tUNUMBER, 1 }, /* { "second", tUNUMBER, 2 }, */ { "third", tUNUMBER, 3 }, { "fourth", tUNUMBER, 4 }, { "fifth", tUNUMBER, 5 }, { "sixth", tUNUMBER, 6 }, { "seventh", tUNUMBER, 7 }, { "eighth", tUNUMBER, 8 }, { "ninth", tUNUMBER, 9 }, { "tenth", tUNUMBER, 10 }, { "eleventh", tUNUMBER, 11 }, { "twelfth", tUNUMBER, 12 }, { "ago", tAGO, 1 }, { NULL } }; /* The timezone table. */ static TABLE TimezoneTable[] = { { "gmt", tZONE, HOURN( 0) }, /* Greenwich Mean */ { "ut", tZONE, HOURN( 0) }, /* Universal (Coordinated) */ { "utc", tZONE, HOURN( 0) }, { "wet", tZONE, HOURN( 0) }, /* Western European */ { "bst", tDAYZONE, HOURN( 0) }, /* British Summer */ { "wat", tZONE, HOURN( 1) }, /* West Africa */ { "at", tZONE, HOURN( 2) }, /* Azores */ #if 0 /* For completeness. BST is also British Summer, and GST is * also Guam Standard. */ { "bst", tZONE, HOURN( 3) }, /* Brazil Standard */ { "gst", tZONE, HOURN( 3) }, /* Greenland Standard */ #endif { "nft", tZONE, HOURN(3.5) }, /* Newfoundland */ { "nst", tZONE, HOURN(3.5) }, /* Newfoundland Standard */ { "ndt", tDAYZONE, HOURN(3.5) }, /* Newfoundland Daylight */ { "ast", tZONE, HOURN( 4) }, /* Atlantic Standard */ { "adt", tDAYZONE, HOURN( 4) }, /* Atlantic Daylight */ { "est", tZONE, HOURN( 5) }, /* Eastern Standard */ { "edt", tDAYZONE, HOURN( 5) }, /* Eastern Daylight */ { "cst", tZONE, HOURN( 6) }, /* Central Standard */ { "cdt", tDAYZONE, HOURN( 6) }, /* Central Daylight */ { "mst", tZONE, HOURN( 7) }, /* Mountain Standard */ { "mdt", tDAYZONE, HOURN( 7) }, /* Mountain Daylight */ { "pst", tZONE, HOURN( 8) }, /* Pacific Standard */ { "pdt", tDAYZONE, HOURN( 8) }, /* Pacific Daylight */ { "yst", tZONE, HOURN( 9) }, /* Yukon Standard */ { "ydt", tDAYZONE, HOURN( 9) }, /* Yukon Daylight */ { "hst", tZONE, HOURN(10) }, /* Hawaii Standard */ { "hdt", tDAYZONE, HOURN(10) }, /* Hawaii Daylight */ { "cat", tZONE, HOURN(10) }, /* Central Alaska */ { "ahst", tZONE, HOURN(10) }, /* Alaska-Hawaii Standard */ { "nt", tZONE, HOURN(11) }, /* Nome */ { "idlw", tZONE, HOURN(12) }, /* International Date Line West */ { "cet", tZONE, -HOURN(1) }, /* Central European */ { "met", tZONE, -HOURN(1) }, /* Middle European */ { "mewt", tZONE, -HOURN(1) }, /* Middle European Winter */ { "mest", tDAYZONE, -HOURN(1) }, /* Middle European Summer */ { "swt", tZONE, -HOURN(1) }, /* Swedish Winter */ { "sst", tDAYZONE, -HOURN(1) }, /* Swedish Summer */ { "fwt", tZONE, -HOURN(1) }, /* French Winter */ { "fst", tDAYZONE, -HOURN(1) }, /* French Summer */ { "eet", tZONE, -HOURN(2) }, /* Eastern Europe, USSR Zone 1 */ { "bt", tZONE, -HOURN(3) }, /* Baghdad, USSR Zone 2 */ { "it", tZONE, -HOURN(3.5) },/* Iran */ { "zp4", tZONE, -HOURN(4) }, /* USSR Zone 3 */ { "zp5", tZONE, -HOURN(5) }, /* USSR Zone 4 */ { "ist", tZONE, -HOURN(5.5) },/* Indian Standard */ { "zp6", tZONE, -HOURN(6) }, /* USSR Zone 5 */ #if 0 /* For completeness. NST is also Newfoundland Stanard, nad SST is * also Swedish Summer. */ { "nst", tZONE, -HOURN(6.5) },/* North Sumatra */ { "sst", tZONE, -HOURN(7) }, /* South Sumatra, USSR Zone 6 */ #endif /* 0 */ { "wast", tZONE, -HOURN(7) }, /* West Australian Standard */ { "wadt", tDAYZONE, -HOURN(7) }, /* West Australian Daylight */ { "jt", tZONE, -HOURN(7.5) },/* Java (3pm in Cronusland!) */ { "cct", tZONE, -HOURN(8) }, /* China Coast, USSR Zone 7 */ { "jst", tZONE, -HOURN(9) }, /* Japan Standard, USSR Zone 8 */ { "cast", tZONE, -HOURN(9.5) },/* Central Australian Standard */ { "cadt", tDAYZONE, -HOURN(9.5) },/* Central Australian Daylight */ { "east", tZONE, -HOURN(10) }, /* Eastern Australian Standard */ { "eadt", tDAYZONE, -HOURN(10) }, /* Eastern Australian Daylight */ { "gst", tZONE, -HOURN(10) }, /* Guam Standard, USSR Zone 9 */ { "nzt", tZONE, -HOURN(12) }, /* New Zealand */ { "nzst", tZONE, -HOURN(12) }, /* New Zealand Standard */ { "nzdt", tDAYZONE, -HOURN(12) }, /* New Zealand Daylight */ { "idle", tZONE, -HOURN(12) }, /* International Date Line East */ { NULL } }; /* Military timezone table. */ static TABLE MilitaryTable[] = { { "a", tZONE, HOURN( 1) }, { "b", tZONE, HOURN( 2) }, { "c", tZONE, HOURN( 3) }, { "d", tZONE, HOURN( 4) }, { "e", tZONE, HOURN( 5) }, { "f", tZONE, HOURN( 6) }, { "g", tZONE, HOURN( 7) }, { "h", tZONE, HOURN( 8) }, { "i", tZONE, HOURN( 9) }, { "k", tZONE, HOURN( 10) }, { "l", tZONE, HOURN( 11) }, { "m", tZONE, HOURN( 12) }, { "n", tZONE, HOURN(- 1) }, { "o", tZONE, HOURN(- 2) }, { "p", tZONE, HOURN(- 3) }, { "q", tZONE, HOURN(- 4) }, { "r", tZONE, HOURN(- 5) }, { "s", tZONE, HOURN(- 6) }, { "t", tZONE, HOURN(- 7) }, { "u", tZONE, HOURN(- 8) }, { "v", tZONE, HOURN(- 9) }, { "w", tZONE, HOURN(-10) }, { "x", tZONE, HOURN(-11) }, { "y", tZONE, HOURN(-12) }, { "z", tZONE, HOURN( 0) }, { NULL } }; static int yyerror(const char *s __unused) { return (0); } static time_t ToSeconds(Hours, Minutes, Seconds, Meridian) time_t Hours; time_t Minutes; time_t Seconds; MERIDIAN Meridian; { if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) return -1; switch (Meridian) { case MER24: if (Hours < 0 || Hours > 23) return -1; return (Hours * 60L + Minutes) * 60L + Seconds; case MERam: if (Hours < 1 || Hours > 12) return -1; if (Hours != 12) return (Hours * 60L + Minutes) * 60L + Seconds; else return Minutes * 60L + Seconds; case MERpm: if (Hours < 1 || Hours > 12) return -1; if (Hours != 12) return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; else return (720L + Minutes) * 60L + Seconds; } /* NOTREACHED */ return (-1); } static time_t Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode) time_t Month; time_t Day; time_t Year; time_t Hours; time_t Minutes; time_t Seconds; MERIDIAN Meridian; DSTMODE DSTmode; { static int DaysInMonth[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; time_t tod; time_t Julian; time_t i; if (Year < 0) Year = -Year; if (Year < 138) Year += 1900; DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 29 : 28; if (Year < EPOCH || Year > 2037 || Month < 1 || Month > 12 /* LINTED Month is a time_t so intermediate results aren't truncated */ || Day < 1 || Day > DaysInMonth[(int)--Month]) return -1; for (Julian = Day - 1, i = 0; i < Month; i++) Julian += DaysInMonth[i]; for (i = EPOCH; i < Year; i++) Julian += 365 + (i % 4 == 0); Julian *= SECSPERDAY; Julian += yyTimezone * 60L; if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) return -1; Julian += tod; if (DSTmode == DSTon || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) Julian -= 60 * 60; return Julian; } static time_t DSTcorrect(Start, Future) time_t Start; time_t Future; { time_t StartDay; time_t FutureDay; StartDay = (localtime(&Start)->tm_hour + 1) % 24; FutureDay = (localtime(&Future)->tm_hour + 1) % 24; return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; } static time_t RelativeDate(Start, DayOrdinal, DayNumber) time_t Start; time_t DayOrdinal; time_t DayNumber; { struct tm *tm; time_t now; now = Start; tm = localtime(&now); now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); return DSTcorrect(Start, now); } static time_t RelativeMonth(Start, RelMonth) time_t Start; time_t RelMonth; { struct tm *tm; time_t Month; time_t Year; if (RelMonth == 0) return 0; tm = localtime(&Start); Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; Year = Month / 12; Month = Month % 12 + 1; return DSTcorrect(Start, Convert(Month, (time_t)tm->tm_mday, Year, (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, MER24, DSTmaybe)); } static int LookupWord(buff) char *buff; { char *p; char *q; TABLE *tp; uint_t i; int abbrev; /* Make it lowercase. */ for (p = buff; *p; p++) if (isupper((u_char)*p)) *p = tolower(*p); if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { yylval.Meridian = MERam; return tMERIDIAN; } if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { yylval.Meridian = MERpm; return tMERIDIAN; } /* See if we have an abbreviation for a month. */ if (strlen(buff) == 3) abbrev = 1; else if (strlen(buff) == 4 && buff[3] == '.') { abbrev = 1; buff[3] = '\0'; } else abbrev = 0; for (tp = MonthDayTable; tp->name; tp++) { if (abbrev) { if (strncmp(buff, tp->name, 3) == 0) { yylval.Number = tp->value; return tp->type; } } else if (strcmp(buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } } for (tp = TimezoneTable; tp->name; tp++) if (strcmp(buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } for (tp = UnitsTable; tp->name; tp++) if (strcmp(buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Strip off any plural and try the units table again. */ i = strlen(buff) - 1; if (buff[i] == 's') { buff[i] = '\0'; for (tp = UnitsTable; tp->name; tp++) if (strcmp(buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } } for (tp = OtherTable; tp->name; tp++) if (strcmp(buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } /* Military timezones. */ if (buff[1] == '\0' && isalpha((u_char)*buff)) { for (tp = MilitaryTable; tp->name; tp++) if (strcmp(buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } } /* Drop out any periods and try the timezone table again. */ for (i = 0, p = q = buff; *q; q++) if (*q != '.') *p++ = *q; else i++; *p = '\0'; if (i) for (tp = TimezoneTable; tp->name; tp++) if (strcmp(buff, tp->name) == 0) { yylval.Number = tp->value; return tp->type; } return tID; } void pdateerr(p) char *p; { char *name = "DATEMSK"; /* env variable for date format */ char *value; char fmt[256], line[256]; FILE *fp; time_t now; struct tm *tm; value = getenv(name); if (value == (char *)0) { fprintf(stderr, dgettext(domainname, "%s: Environment variable %s not set\n"), p, name); return; } switch (getdate_err) { case 0: default: fprintf(stderr, dgettext(domainname, "%s: Unkown getdate() error\n"), p); break; case 1: fprintf(stderr, dgettext(domainname, "%s: %s null or undefined\n"), p, name); break; case 2: fprintf(stderr, dgettext(domainname, "%s: Cannot read template file %s\n"), p, value); break; case 3: fprintf(stderr, dgettext(domainname, "%s: Failed to get file status information\n"), p); break; case 4: fprintf(stderr, dgettext(domainname, "%s: Template file %s not a regular file\n"), p, value); break; case 5: fprintf(stderr, dgettext(domainname, "%s: Error reading template file %s\n"), p, value); break; case 6: fprintf(stderr, dgettext(domainname, "%s: %s failed\n"), p, "malloc()"); break; case 7: fprintf(stderr, dgettext(domainname, "%s: Bad date/time format\n"), p); fp = fopen(value, "r"); if (fp == (FILE *)0) break; now = time((time_t *)0); tm = localtime(&now); fprintf(stderr, dgettext(domainname, "The following are examples of valid formats:\n")); while (fgets(fmt, sizeof (fmt), fp)) { if (strchr(fmt, '%') == (char *)0) continue; fprintf(stderr, " "); (void) strftime(line, sizeof (line), fmt, tm); fprintf(stderr, "%s", line); } (void) fclose(fp); break; case 8: (void) fprintf(stderr, dgettext(domainname, "%s: Invalid date specification\n"), p); break; } } #undef yylex static int yylex() { char c; char *p; char buff[20]; int Count; int sign; for ( ; ; ) { while (isspace((u_char)*yyInput)) yyInput++; if (isdigit((u_char)(c = *yyInput)) || c == '-' || c == '+') { if (c == '-' || c == '+') { sign = c == '-' ? -1 : 1; if (!isdigit((u_char)*++yyInput)) /* skip the '-' sign */ continue; } else sign = 0; yylval.Number = 0; while (isdigit((u_char)(c = *yyInput++))) { int n; char digit = c; (void) sscanf(&digit, "%1d", &n); yylval.Number = 10 * yylval.Number + n; } yyInput--; if (sign < 0) yylval.Number = -yylval.Number; return sign ? tSNUMBER : tUNUMBER; } if (isalpha((u_char)c)) { for (p = buff; isalpha((u_char)(c = *yyInput++)) || c == '.'; ) if (p < &buff[sizeof (buff) - 1]) *p++ = c; *p = '\0'; yyInput--; return LookupWord(buff); } if (c != '(') return *yyInput++; Count = 0; do { c = *yyInput++; if (c == '\0') return c; if (c == '(') Count++; else if (c == ')') Count--; } while (Count > 0); } } time_t getreldate(p, now) char *p; struct timeb *now; { struct tm *tm; struct timeb ftz; time_t Start; time_t tod; if (strcmp(setlocale(LC_TIME, NULL), "C")) { static char localedate[24]; struct tm ltm; tm = getdate(p); if (getdate_err == 1 /* NODATEMASK */) { char buffy[BUFSIZ]; time_t current; printf(gettext("environment variable %s not set\n"), "DATEMSK"); do { time(¤t); tm = localtime(¤t); memcpy(<m, tm, sizeof(ltm)); tm = <m; (void) fputs(gettext("Enter date as mmddhhmm[yy]: "), stdout); (void) fflush(stdout); if (fgets(buffy, sizeof (buffy), stdin) == NULL) { (void) printf(gettext("Encountered EOF on stdin\n")); return(-1); } } while (sscanf(buffy, "%2d%2d%2d%2d%2d", &(tm->tm_mon), &(tm->tm_mday), &(tm->tm_hour), &(tm->tm_min), &(tm->tm_year)) < 4); (tm->tm_mon)--; } else if (tm == NULL) return(-1); (void)sprintf(localedate, "%d:%2.2d %d/%d %d", tm->tm_hour, tm->tm_min, tm->tm_mon + 1, tm->tm_mday, CHECK_TM(tm->tm_year)); p = localedate; } yyInput = p; if (now == NULL) { now = &ftz; (void) time(&ftz.time); /* Set the timezone global. */ tzset(); /* LINTED timezone is time_t so intermediate results aren't truncated */ ftz.timezone = (int) timezone / 60; } tm = localtime(&now->time); yyYear = tm->tm_year; yyMonth = tm->tm_mon + 1; yyDay = tm->tm_mday; yyTimezone = now->timezone; yyDSTmode = DSTmaybe; yyHour = tm->tm_hour; yyMinutes = tm->tm_min; yySeconds = tm->tm_sec; yyMeridian = MER24; yyRelSeconds = 0; yyRelMonth = 0; yyHaveDate = 0; yyHaveDay = 0; yyHaveRel = 0; yyHaveTime = 0; yyHaveZone = 0; if (yyparse() || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) return -1; if (yyHaveDate || yyHaveTime || yyHaveDay) { Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, yyMeridian, yyDSTmode); if (Start < 0) return -1; } else { Start = now->time; if (!yyHaveRel) Start -= ((tm->tm_hour * 60L) + tm->tm_min * 60L) + tm->tm_sec; } Start += yyRelSeconds; Start += RelativeMonth(Start, yyRelMonth); if (yyHaveDay && !yyHaveDate) { tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); Start += tod; } /* Have to do *something* with a legitimate -1 so it's distinguishable * from the error return value. (Alternately could set errno on error.) */ return Start == -1 ? 0 : Start; } #if defined(TEST) /* ARGSUSED */ main(ac, av) int ac; char *av[]; { char buff[128]; time_t d; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); (void) printf(gettext("Enter date, or blank line to exit.\n\t> ")); (void) fflush(stdout); while (gets(buff) && buff[0]) { d = getreldate(buff, (struct timeb *)NULL); if (d == -1) (void) printf(gettext("Bad format - couldn't convert.\n")); else { (void) cftime(buff, "%c\n", &d); (void) printf("%s", buff); } (void) printf("\t> "); (void) fflush(stdout); } exit(0); /* NOTREACHED */ } #endif /* defined(TEST) */ /^"operator_xdr.c.*variable unused in function/d /yaccpar/d /getdate.y.*include file.*unnecessary/d /different definitions.*CLK_TCK/d /lint suppression directive not used/d /no corresponding .h file/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 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include #include "memutils.h" extern void msg(const char *, ...); extern void dumpabort(void); void * xmalloc(bytes) size_t bytes; { void *cp; cp = malloc(bytes); if (cp == NULL) { int saverr = errno; msg(gettext("Cannot allocate memory: %s\n"), strerror(saverr)); dumpabort(); } return (cp); } void * xcalloc(nelem, size) size_t nelem; size_t size; { void *cp; cp = calloc(nelem, size); if (cp == NULL) { int saverr = errno; msg(gettext("Cannot allocate memory: %s\n"), strerror(saverr)); dumpabort(); } return (cp); } void * xrealloc(allocated, newsize) void *allocated; size_t newsize; { void *cp; /* LINTED realloc knows what to do with a NULL pointer */ cp = realloc(allocated, newsize); if (cp == NULL) { int saverr = errno; msg(gettext("Cannot allocate memory: %s\n"), strerror(saverr)); dumpabort(); } return (cp); } /* * 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 1999 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define index(s, c) strchr(s, c) char *strchr(); char *inet_ntoa(); char myrcmd_stderr[1024]; int myrcmd(char **ahost, unsigned short rport, char *locuser, char *remuser, char *cmd) { uint_t loclen, remlen, cmdlen; int s, timo, retval; int tries = 0; pid_t pid; struct sockaddr_in sin; char c; int lport; int saverr; struct hostent *hp; sigset_t oldmask; sigset_t newmask; struct sigaction oldaction; struct sigaction newaction; static struct hostent numhp; static char numhostname[32]; /* big enough for "255.255.255.255" */ struct in_addr numaddr; struct in_addr *numaddrlist[2]; myrcmd_stderr[0] = '\0'; /* empty error string */ pid = getpid(); hp = gethostbyname(*ahost); if (hp == 0) { char *straddr; bzero((char *)numaddrlist, sizeof (numaddrlist)); if ((numaddr.s_addr = inet_addr(*ahost)) == (in_addr_t)-1) { (void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr), gettext("%s: unknown host\n"), *ahost); return (MYRCMD_NOHOST); } else { bzero((char *)&numhp, sizeof (numhp)); bzero(numhostname, sizeof (numhostname)); if ((straddr = inet_ntoa(numaddr)) == (char *)0) { (void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr), gettext("%s: unknown host\n"), *ahost); return (MYRCMD_NOHOST); } (void) strncpy(numhostname, straddr, sizeof (numhostname)); numhostname[sizeof (numhostname) - 1] = '\0'; numhp.h_name = numhostname; numhp.h_addrtype = AF_INET; numhp.h_length = sizeof (numaddr); numaddrlist[0] = &numaddr; numaddrlist[1] = NULL; numhp.h_addr_list = (char **)numaddrlist; hp = &numhp; } } *ahost = hp->h_name; /* This provides a bounds-test for the bcopy()s below. */ if ((unsigned)(hp->h_length) > sizeof (sin.sin_addr)) { (void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr), gettext("rcmd: address size: %d larger than limit %d\n"), hp->h_length, sizeof (sin.sin_addr)); return (MYRCMD_EBAD); } /* ignore SIGPIPE */ bzero((char *)&newaction, sizeof (newaction)); newaction.sa_handler = SIG_IGN; newaction.sa_flags = SA_ONSTACK; (void) sigaction(SIGPIPE, &newaction, &oldaction); /* block SIGURG */ bzero((char *)&newmask, sizeof (newmask)); (void) sigaddset(&newmask, SIGURG); (void) sigprocmask(SIG_BLOCK, &newmask, &oldmask); again: timo = 1; /* * Use 0 as lport means that rresvport() will bind to a port in * the anonymous priviledged port range. */ lport = 0; for (;;) { s = rresvport(&lport); if (s < 0) { int err; if (errno == EAGAIN) { (void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr), gettext("socket: All ports in use\n")); err = MYRCMD_ENOPORT; } else { saverr = errno; (void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr), gettext("rcmd: socket: %s\n"), strerror(saverr)); err = MYRCMD_ENOSOCK; } /* restore original SIGPIPE handler */ (void) sigaction(SIGPIPE, &oldaction, (struct sigaction *)0); /* restore original signal mask */ (void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0); return (err); } /* Can't fail, according to fcntl(2) */ (void) fcntl(s, F_SETOWN, pid); sin.sin_family = hp->h_addrtype; bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr, hp->h_length); sin.sin_port = rport; if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) >= 0) break; saverr = errno; (void) close(s); if (saverr == EADDRINUSE) { continue; } if (saverr == ECONNREFUSED && timo <= 16) { sleep(timo); timo *= 2; continue; } if (hp->h_addr_list[1] != NULL) { saverr = errno; fprintf(stderr, gettext("connect to address %s: "), inet_ntoa(sin.sin_addr)); errno = saverr; perror(0); hp->h_addr_list++; bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr, hp->h_length); fprintf(stderr, gettext("Trying %s...\n"), inet_ntoa(sin.sin_addr)); continue; } (void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr), "%s: %s\n", hp->h_name, strerror(saverr)); /* restore original SIGPIPE handler */ (void) sigaction(SIGPIPE, &oldaction, (struct sigaction *)0); /* restore original signal mask */ (void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0); return (MYRCMD_ENOCONNECT); } if (write(s, "", 1) < 0) { (void) close(s); return (MYRCMD_ENOCONNECT); } loclen = strlen(locuser) + 1; remlen = strlen(remuser) + 1; cmdlen = strlen(cmd) + 1; if (((retval = write(s, locuser, loclen)) != loclen) || ((retval = write(s, remuser, remlen)) != remlen) || ((retval = write(s, cmd, cmdlen)) != cmdlen)) { if (retval == -1) (void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr), "write: %s\n", strerror(errno)); else (void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr), gettext("write unexpectedly truncated\n")); goto bad; } retval = read(s, &c, 1); if (retval != 1) { if (retval == 0) { /* * Solaris 2.0 bug alert. Sometimes, if the * tapehost is a Solaris 2.0 system, the connection * will be dropped at this point. Let's try again, * three times, before we throw in the towel. */ if (++tries < 3) { (void) close(s); goto again; } (void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr), gettext("Protocol error, %s closed connection\n"), *ahost); } else if (retval < 0) { (void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr), "%s: %s\n", *ahost, strerror(errno)); } else { (void) snprintf(myrcmd_stderr, sizeof (myrcmd_stderr), gettext("Protocol error, %s sent %d bytes\n"), *ahost, retval); } goto bad; } if (c != 0) { char *cp = myrcmd_stderr; char *ecp = &myrcmd_stderr[sizeof (myrcmd_stderr) - 1]; while (read(s, &c, 1) == 1) { *cp++ = c; if (c == '\n' || cp >= ecp) break; } *cp = '\0'; goto bad; } /* restore original SIGPIPE handler */ (void) sigaction(SIGPIPE, &oldaction, (struct sigaction *)0); /* restore original signal mask */ (void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0); return (s); bad: (void) close(s); /* restore original SIGPIPE handler */ (void) sigaction(SIGPIPE, &oldaction, (struct sigaction *)0); /* restore original signal mask */ (void) sigprocmask(SIG_SETMASK, &oldmask, (sigset_t *)0); return (MYRCMD_EBAD); } /*LINTLIBRARY*/ /*PROTOLIB1*/ /* * Copyright 2005 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define sigvec sigaction #define sv_handler sa_handler #include extern int32_t tp_bsize; #define TS_CLOSED 0 #define TS_OPEN 1 static int rmtstate = TS_CLOSED; static int rmtape = -1; static int rmtversion = 0; static char *rmtpeer, *rmtpeer_malloc; static uint_t ntrec; /* blocking factor on tape */ static char *domainname = "hsm_libdump"; /* for dgettext() */ #ifdef __STDC__ static void rmtmsg(const char *, ...); /* package print routine */ static void rmtconnaborted(int); static void rmtgetconn(void); static int rmtstatus_extended(struct mtget *); static int rmtioctl_extended(int, long); static int map_extended_ioctl(int); static int okname(char *); static int rmtcall(char *, char *); static int rmtreply(char *); static int rmtpush(char *, uint_t); static void rmtgets(char *, int); static void (*print)(const char *, ...); /* print routine */ static void (*Exit)(int); /* exit routine */ #else static void rmtmsg(); static void rmtconnaborted(); static void rmtgetconn(); static int okname(); static int rmtstatus_extended(); static int rmtioctl_extended(); static int map_extended_ioctl(); static int rmtcall(); static int rmtreply(); static int rmtpush(); static void rmtgets(); static void (*print)(); static void (*Exit)(); #endif /* * Get a program-specific print and exit routine into * the package. This is primarily for dump's benefit. * This routine is optional -- if not called the two * default to fprintf(stderr) and exit. */ #ifdef __STDC__ void rmtinit( void (*errmsg)(const char *, ...), /* print routine */ void (*errexit)(int)) /* exit routine */ #else void rmtinit(void (*errmsg)(), void (*errexit)()) #endif { print = errmsg; Exit = errexit; } int rmthost(char *host, uint_t blocksize) { struct sigvec sv; #ifdef __STDC__ if (print == (void (*)(const char *, ...))0) #else if (print == (void (*)())0) #endif print = rmtmsg; #ifdef __STDC__ if (Exit == (void (*)(int))0) #else if (Exit == (void (*)())0) #endif Exit = exit; if (rmtape >= 0 && rmtstate != TS_OPEN) { (void) close(rmtape); rmtape = -1; } if (rmtpeer_malloc) (void) free(rmtpeer_malloc); rmtpeer = rmtpeer_malloc = strdup(host); if (rmtpeer == (char *)0) return (0); ntrec = blocksize; sv.sa_flags = SA_RESTART; (void) sigemptyset(&sv.sa_mask); sv.sv_handler = rmtconnaborted; (void) sigvec(SIGPIPE, &sv, (struct sigvec *)0); rmtgetconn(); if (rmtape < 0) return (0); return (1); } /*ARGSUSED*/ static void rmtconnaborted(int sig) { print(dgettext(domainname, "Lost connection to remote host.\n")); Exit(1); } static void #ifdef __STDC__ rmtgetconn(void) #else rmtgetconn() #endif { static struct servent *sp = 0; static struct passwd *pwd = 0; char *tuser, *host, *device; uint_t size; if (sp == 0) { sp = getservbyname("shell", "tcp"); if (sp == 0) { print(dgettext(domainname, "shell/tcp: unknown service\n")); Exit(1); } pwd = getpwuid(getuid()); if (pwd == 0) { print(dgettext(domainname, "Cannot find password entry for uid %d\n"), getuid()); Exit(1); } } /* Was strrchr(), be consistent with dump */ host = strchr(rmtpeer, '@'); if (host) { tuser = rmtpeer; *host++ = 0; rmtpeer = host; if (!okname(tuser)) Exit(1); } else { host = rmtpeer; tuser = pwd->pw_name; } /* Was strrchr() - be consistent with dump and restore */ device = strchr(host, ':'); if (device) *device = 0; /* throw away device name */ /* * myrcmd() replaces the contents of rmtpeer with a pointer * to a static copy of the canonical host name. However, * since we never refer to rmtpeer again (other than to * overwrite it in the next rmthost() invocation), we don't * really care. */ /* LINTED sp->s_port is an int, even though port numbers are 1..65535 */ rmtape = myrcmd(&rmtpeer, (ushort_t)sp->s_port, pwd->pw_name, tuser, "/etc/rmt"); if (rmtape < 0) { if (*myrcmd_stderr) print("%s", myrcmd_stderr); } else { size = ntrec * tp_bsize; while (size > tp_bsize && setsockopt(rmtape, SOL_SOCKET, SO_SNDBUF, (char *)&size, sizeof (size)) < 0) size -= tp_bsize; } } static int okname(char *cp0) { char *cp; uchar_t c; for (cp = cp0; *cp; cp++) { c = (uchar_t)*cp; if (!isascii(c) || !(isalnum(c) || c == '_' || c == '-')) { print(dgettext(domainname, "invalid user name %s\n"), cp0); return (0); } } return (1); } int rmtopen(char *tape, int mode) { struct mtget mt; char buf[256]; int fd; (void) snprintf(buf, sizeof (buf), "O%s\n%d\n", tape, mode); rmtstate = TS_OPEN; fd = rmtcall(tape, buf); if (fd != -1) { /* see if the rmt server supports the extended protocol */ rmtversion = rmtioctl(-1, 0); /* * Some rmt daemons apparently close the connection * when they get a bogus ioctl. See 1210852 (ignore * the evaluation). Make sure we can still talk to * the device, re-opening it if necessary. */ if (rmtversion < 1) { if (rmtstatus(&mt) < 0) { rmtclose(); rmtgetconn(); rmtversion = 0; } } } return (fd); } void #ifdef __STDC__ rmtclose(void) #else rmtclose() #endif { if (rmtstate != TS_OPEN) return; (void) rmtcall("close", "C\n"); rmtstate = TS_CLOSED; } int rmtstatus(struct mtget *mt) { char *buf = (char *)mt; int n, i, cc; if (rmtversion > 0) return (rmtstatus_extended(mt)); n = rmtcall("status", "S"); if (n < 0) { return (-1); } if ((unsigned)n > sizeof (*mt)) { print(dgettext(domainname, "rmtstatus: expected response size %d, got %d\n"), sizeof (struct mtget), n); print(dgettext(domainname, "This means the remote rmt daemon is not compatible.\n")); rmtconnaborted(0); } i = 0; while (i < n) { cc = read(rmtape, buf+i, n - i); if (cc <= 0) rmtconnaborted(0); i += cc; } return (n); } static int rmtstatus_extended(struct mtget *mt) { if ((mt->mt_type = rmtcall("status", "sT")) == -1) return (-1); mt->mt_dsreg = rmtcall("status", "sD"); mt->mt_erreg = rmtcall("status", "sE"); mt->mt_resid = rmtcall("status", "sR"); mt->mt_fileno = rmtcall("status", "sF"); mt->mt_blkno = rmtcall("status", "sB"); mt->mt_flags = rmtcall("status", "sf"); mt->mt_bf = rmtcall("status", "sb"); return (0); } int rmtread(char *buf, uint_t count) { char line[30]; int n, i, cc; (void) snprintf(line, sizeof (line), "R%d\n", count); n = rmtcall("read", line); if (n < 0) { return (-1); } if (n > count) { print(dgettext(domainname, "rmtread: expected response size %d, got %d\n"), count, n); print(dgettext(domainname, "This means the remote rmt daemon is not compatible.\n")); rmtconnaborted(0); } i = 0; while (i < n) { cc = read(rmtape, buf+i, n - i); if (cc <= 0) rmtconnaborted(0); i += cc; } return (n); } int rmtwrite(char *buf, uint_t count) { int retval; char line[64]; /* numbers can get big */ (void) snprintf(line, sizeof (line), "W%d\n", count); retval = rmtpush(line, strlen(line)); if (retval <= 0) return (-1); retval = rmtpush(buf, count); if (retval <= 0) return (-1); return (rmtreply("write")); } int rmtpush(char *buf, uint_t count) { int retval; do { retval = write(rmtape, buf, count); buf += retval; count -= retval; } while (count && retval > 0); return (retval); } int rmtseek(int offset, int pos) { char line[80]; (void) snprintf(line, sizeof (line), "L%d\n%d\n", offset, pos); return (rmtcall("seek", line)); } int rmtioctl(int cmd, long count) { char buf[256]; int xcmd; if (count < 0) return (-1); if ((xcmd = map_extended_ioctl(cmd)) != -1) return (rmtioctl_extended(xcmd, count)); (void) snprintf(buf, sizeof (buf), "I%d\n%ld\n", cmd, count); return (rmtcall("ioctl", buf)); } /* * Map from the standard Sun ioctl commands into the extended version, * if possible. */ static int map_extended_ioctl(int cmd) { int xcmd; if (rmtversion <= 0) return (-1); /* extended protocol not supported */ switch (cmd) { case MTRETEN: xcmd = 2; break; case MTERASE: xcmd = 3; break; case MTEOM: xcmd = 4; break; case MTNBSF: xcmd = 5; break; default: xcmd = -1; /* not supported */ break; } return (xcmd); } static int rmtioctl_extended(int cmd, long count) { char buf[256]; (void) snprintf(buf, sizeof (buf), "i%d\n%ld\n", cmd, count); return (rmtcall("ioctl", buf)); } static int rmtcall(char *cmd, char *buf) { if (rmtpush(buf, strlen(buf)) != strlen(buf)) rmtconnaborted(0); return (rmtreply(cmd)); } static int rmtreply(char *cmd) { char code[30], emsg[BUFSIZ]; rmtgets(code, sizeof (code)); if (*code == 'E' || *code == 'F') { rmtgets(emsg, sizeof (emsg)); /* * don't print error message for ioctl or status; * or if we are opening up a full path (i.e. device) * and the tape is not loaded (EIO error) */ if (strcmp(cmd, "ioctl") != 0 && strcmp(cmd, "status") != 0 && !(cmd[0] == '/' && atoi(code + 1) == EIO)) print("%s: %s\n", cmd, emsg); errno = atoi(code + 1); if (*code == 'F') { rmtstate = TS_CLOSED; return (-1); } return (-1); } if (*code != 'A') { print(dgettext(domainname, "Protocol to remote tape server botched (code %s?).\n"), code); rmtconnaborted(0); } return (atoi(code + 1)); } static void rmtgets(char *cp, int len) { int i, n; n = recv(rmtape, cp, len-1, MSG_PEEK); for (i = 0; i < n; i++) if (cp[i] == '\n') break; n = i + 1; /* characters to read at once */ for (i = 0; i < len; i += n, n = 1) { n = read(rmtape, cp, n); if (n <= 0) rmtconnaborted(0); cp += n; if (cp[-1] == '\n') { cp[-1] = '\0'; return; } } print(dgettext(domainname, "Protocol to remote tape server botched (in rmtgets).\n")); rmtconnaborted(0); } #ifdef __STDC__ #include /* VARARGS1 */ static void rmtmsg(const char *fmt, ...) { va_list args; va_start(args, fmt); (void) vfprintf(stderr, fmt, args); (void) fflush(stderr); } #else #include /* VARARGS */ static void rmtmsg(va_dcl) { va_list args; char *fmt; va_start(args); fmt = va_arg(args, char *); (void) vfprintf(stderr, fmt, args); (void) fflush(stderr); } #endif #!/bin/sh # # 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 2003 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "%Z%%M% %I% %E% SMI" echo_file usr/src/lib/Makefile.lib echo_file usr/src/lib/Makefile.targ echo_file usr/src/head/protocols/dumprestore.h echo_file usr/src/cmd/fs.d/Makefile.fstype find_files "s.*" usr/src/cmd/fs.d/ufs/roll_log # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # cmd/backup/restore/Makefile # # Copyright (c) 1983 Regents of the University of California. # All rights reserved. The Berkeley software License Agreement # specifies the terms and conditions for redistribution. # # CAUTION: FSTYPE must be defined before including ../Makefile.backup, # otherwise UTFROOTPKGUSRLIBFSTYPE doesn't get expanded # properly and the magic doesn't happen. FSTYPE= ufs UFSRESTORE= ufsrestore PROG= $(UFSRESTORE) ROOTFS_PROG= $(PROG) PRODUCT= $(PROG) include ../Makefile.backup OBJECTS= dirs.o interactive.o main.o restore.o \ symtab.o tape.o utilities.o SRCS= $(OBJECTS:%.o=%.c) POFILES= $(OBJECTS:%.o=%.po) ../lib/libdump.po POFILE= ufsrestore.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 \ ../include/byteorder.h ../include/memutils.h \ ../include/rmt.h restore.h CLOBBERFILES += $(PRODUCT) $(DEBUGPRODUCTS) LOCAL= . GENERAL= ../include GLOBAL= ../../../head CPPFLAGS= -I$(LOCAL) -I$(GENERAL) -I$(GLOBAL) \ $(CPPFLAGS.master) -D_LARGEFILE64_SOURCE=1 # Hammerhead: Suppress pointer-related warnings in legacy restore code CERRWARN += -Wno-incompatible-pointer-types CERRWARN += -Wno-int-to-pointer-cast LIBDUMP= ../lib/libdump.a LINTLIBDUMP= ../lib/llib-ldump.ln LDLIBS += $(BSTATIC) -L../lib -ldump $(BDYNAMIC) -lsocket -lnsl -lsec UFSROOTLINK= $(UFSROOTUSRSBIN)/$(PROG) LINKVALUE= ../lib/fs/$(FSTYPE)/$(PROG) FILEMODE= 04555 .KEEP_STATE: all: $(ROOTFS_PROG) $(PROG): $(OBJECTS) $$(LIBDUMP) $(LINK.c) -o $@ $(OBJECTS) $(LDLIBS) $(POST_PROCESS) $(LIBDUMP): FRC $(DO_LIBDIR) FRC: install: all $(UFSROOTPKGUSRLIBFSTYPE) $(UFSROOTLINK) lint: $(SRCS) $(LINTLIBDUMP) $(LINT.c) $(SRCS) $(LINTLIBDUMP) 2>&1 \ | sed -f lint.sed | grep -v '^[ ]' $(LINTLIBDUMP): FRC cd ../lib; pwd; $(MAKE) lint pwd check: $(CSTYLE) $(CSTYLEFLAGS) $(SRCS) $(HEADERS) $(HDRCHK) $(HDRCHKFLAGS) $(HEADERS) clean: $(RM) $(OBJECTS) $(DEBUGOBJS) *.ln $(UFSROOTLINK): -$(RM) $@; $(SYMLINK) $(LINKVALUE) $(UFSROOTLINK) $(POFILE): $(POFILES) $(RM) $@; cat $(POFILES) > $@ ../lib/libdump.po: cd ../lib ; pwd ; $(MAKE) libdump.po include ../Makefile.targ /* * Copyright 2005 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) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #include "restore.h" #include #include #include #include struct context curfile; /* * Symbol table of directories read from tape. */ #define HASHSIZE 1000 #define INOHASH(val) (val % HASHSIZE) struct inotab { struct inotab *t_next; ino_t t_ino; offset_t t_seekpt; offset_t t_size; struct inotab *t_xattr; }; static struct inotab *inotab[HASHSIZE]; static struct inotab *xattrlist = NULL; /* * Information retained about directories. */ static struct modeinfo { ino_t ino; time_t timep[2]; mode_t mode; uid_t uid; gid_t gid; size_t metasize; } node; /* * Global variables for this file. */ static off64_t g_seekpt; /* some people have a local seekpt */ static FILE *df, *mf; static char dirfile[MAXPATHLEN] = "#"; /* No file */ static char modefile[MAXPATHLEN] = "#"; /* No file */ static RST_DIR *dirp; #define INIT_TEMPFILE(name, type) \ if (name[0] == '#') { \ if (tmpdir == (char *)NULL) /* can't happen; be paranoid */ \ tmpdir = "/tmp"; \ (void) snprintf(name, sizeof (name), \ "%s/rst" type "%ld.XXXXXX", tmpdir, dumpdate); \ (void) mktemp(name); \ } #define INIT_DIRFILE() INIT_TEMPFILE(dirfile, "dir") #define INIT_MODEFILE() INIT_TEMPFILE(modefile, "mode") /* * Format of old style directories. */ #define ODIRSIZ 14 struct odirect { ushort_t d_ino; char d_name[ODIRSIZ]; }; static ino_t search(ino_t, char *); static void putdir(char *, size_t); static void putent(struct direct *); static void skipmetadata(FILE *, size_t); static void flushent(void); static void dcvt(struct odirect *, struct direct *); static RST_DIR *rst_initdirfile(char *); static offset_t rst_telldir(RST_DIR *); static void rst_seekdir(RST_DIR *, offset_t, offset_t); static struct inotab *allocinotab(ino_t, struct dinode *, off64_t); static void nodeflush(void); static struct inotab *inotablookup(ino_t); /* * Extract directory contents, building up a directory structure * on disk for extraction by name. * If genmode is requested, save mode, owner, and times for all * directories on the tape. */ void extractdirs(int genmode) { int ts; struct dinode *ip; int saverr; struct inotab *itp; struct direct nulldir; static char dotname[] = "."; /* dirlookup/psearch writes to its arg */ vprintf(stdout, gettext("Extract directories from tape\n")); INIT_DIRFILE(); if ((df = safe_fopen(dirfile, "w", 0600)) == (FILE *)NULL) { saverr = errno; (void) fprintf(stderr, gettext("%s: %s - cannot create directory temporary\n"), progname, dirfile); errno = saverr; perror("fopen"); done(1); } if (genmode != 0) { INIT_MODEFILE(); if ((mf = safe_fopen(modefile, "w", 0600)) == (FILE *)NULL) { saverr = errno; (void) fprintf(stderr, gettext("%s: %s - cannot create modefile \n"), progname, modefile); errno = saverr; perror("fopen"); done(1); } } nulldir.d_ino = 0; nulldir.d_namlen = 1; (void) strcpy(nulldir.d_name, "/"); /* LINTED DIRSIZ will always fit into a ushort_t */ nulldir.d_reclen = (ushort_t)DIRSIZ(&nulldir); /* LINTED sign extension ok in assert */ assert(DIRSIZ(&nulldir) == (ulong_t)nulldir.d_reclen); for (;;) { curfile.name = gettext(""); curfile.action = USING; ip = curfile.dip; ts = curfile.ts; if (ts != TS_END && ts != TS_INODE) { getfile(null, null); continue; } if (ts == TS_INODE && ip == NULL) { (void) fprintf(stderr, gettext( "%s: extractdirs: Failed internal consistency check, curfile.dip is NULL\n"), progname); done(1); } if ((ts == TS_INODE && (ip->di_mode & IFMT) != IFDIR && (ip->di_mode & IFMT) != IFATTRDIR) || (ts == TS_END)) { (void) fflush(df); /* XXX Legitimate error, bad complaint string */ if (ferror(df)) panic("%s: %s\n", dirfile, strerror(errno)); (void) fclose(df); rst_closedir(dirp); dirp = rst_initdirfile(dirfile); if (dirp == NULL) perror("initdirfile"); if (mf != NULL) { (void) fflush(mf); /* XXX Legitimate error, bad complaint string */ if (ferror(mf)) panic("%s: %s\n", modefile, strerror(errno)); (void) fclose(mf); } if (dirlookup(dotname) == 0) { (void) fprintf(stderr, gettext( "Root directory is not on tape\n")); done(1); } return; } itp = allocinotab(curfile.ino, ip, g_seekpt); getfile(putdir, null); if (mf != NULL) nodeflush(); putent(&nulldir); flushent(); itp->t_size = g_seekpt - itp->t_seekpt; } } /* * skip over all the directories on the tape */ void skipdirs() { while (curfile.dip != NULL && ((curfile.dip->di_mode & IFMT) == IFDIR || (curfile.dip->di_mode & IFMT) == IFATTRDIR)) { skipfile(); } } /* * Recursively find names and inumbers of all files in subtree * pname and pass them off to be processed. */ void treescan(char *pname, ino_t ino, long (*todo)()) { struct inotab *itp; struct direct *dp; uint_t loclen; offset_t bpt; char locname[MAXCOMPLEXLEN]; itp = inotablookup(ino); if (itp == NULL) { /* * Pname is name of a simple file or an unchanged directory. */ (void) (*todo)(pname, ino, LEAF); return; } /* * Pname is a dumped directory name. */ if ((*todo)(pname, ino, NODE) == FAIL) return; /* * begin search through the directory * skipping over "." and ".." */ loclen = complexcpy(locname, pname, MAXCOMPLEXLEN); locname[loclen-1] = '/'; rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); dp = rst_readdir(dirp); /* "." */ if (dp != NULL && strcmp(dp->d_name, ".") == 0) dp = rst_readdir(dirp); /* ".." */ else (void) fprintf(stderr, gettext("Warning: `.' missing from directory %s\n"), pname); if (dp != NULL && strcmp(dp->d_name, "..") == 0) dp = rst_readdir(dirp); /* first real entry */ else (void) fprintf(stderr, gettext("Warning: `..' missing from directory %s\n"), pname); bpt = rst_telldir(dirp); /* * a zero inode signals end of directory */ while (dp != NULL && dp->d_ino != 0) { locname[loclen] = '\0'; if ((loclen + dp->d_namlen) >= (sizeof (locname) - 2)) { (void) fprintf(stderr, gettext( "%s%s: ignoring name that exceeds %d char\n"), locname, dp->d_name, MAXCOMPLEXLEN); } else { /* Always fits by if() condition */ (void) strcpy(locname + loclen, dp->d_name); /* put a double null on string for lookupname() */ locname[loclen+dp->d_namlen+1] = '\0'; treescan(locname, dp->d_ino, todo); rst_seekdir(dirp, bpt, itp->t_seekpt); } dp = rst_readdir(dirp); bpt = rst_telldir(dirp); } if (dp == NULL) (void) fprintf(stderr, gettext("corrupted directory: %s.\n"), locname); } /* * Scan the directory table looking for extended attribute trees. * Recursively find names and inumbers in each tree and pass them * off to be processed. If the always parameter is not set, only * process the attribute tree if the attribute tree parent is to * be extracted. */ void attrscan(int always, long (*todo)()) { struct inotab *itp; struct entry *ep, *parent; struct direct *dp; char name[MAXCOMPLEXLEN]; int len; for (itp = xattrlist; itp != NULL; itp = itp->t_xattr) { rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); if ((dp = rst_readdir(dirp)) != NULL && /* "." */ (dp = rst_readdir(dirp)) != NULL && /* ".." */ strcmp(dp->d_name, "..") == 0) { if ((parent = lookupino(dp->d_ino)) != NULL) { if (!always && (parent->e_flags & (NEW|EXTRACT)) == 0) continue; len = complexcpy(name, myname(parent), MAXCOMPLEXLEN - 3); name[len] = '.'; name[len+1] = '\0'; name[len+2] = '\0'; inattrspace = 1; if ((ep = lookupino(itp->t_ino)) == NULL) { ep = addentry(name, itp->t_ino, NODE|ROOT); } ep->e_flags |= XATTRROOT; treescan(name, itp->t_ino, todo); inattrspace = 0; } else { (void) fprintf(stderr, gettext("Warning: orphaned attribute " "directory\n")); } } else { (void) fprintf(stderr, gettext("Warning: `..' missing " "from attribute directory\n")); } } } /* * Search the directory tree rooted at inode ROOTINO * for the path pointed at by n. Note that n must be * modifiable, although it is returned in the same * condition it was given to us in. */ ino_t psearch(char *n) { char *cp, *cp1; ino_t ino; char c; ino = ROOTINO; if (*(cp = n) == '/') cp++; next: cp1 = cp + 1; while (*cp1 != '/' && *cp1) cp1++; c = *cp1; *cp1 = 0; ino = search(ino, cp); if (ino == 0) { *cp1 = c; return (0); } *cp1 = c; if (c == '/') { cp = cp1+1; goto next; } return (ino); } /* * search the directory inode ino * looking for entry cp */ static ino_t search(ino_t inum, char *cp) { struct direct *dp; struct inotab *itp; uint_t len; itp = inotablookup(inum); if (itp == NULL) return (0); rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); len = strlen(cp); do { dp = rst_readdir(dirp); if (dp == NULL || dp->d_ino == 0) return (0); } while (dp->d_namlen != len || strncmp(dp->d_name, cp, len) != 0); return (dp->d_ino); } /* * Put the directory entries in the directory file */ static void putdir(char *buf, size_t size) { struct direct cvtbuf; struct odirect *odp; struct odirect *eodp; struct direct *dp; size_t loc, i; if (cvtflag) { /*LINTED [buf is char[] in getfile, size % fs_fsize == 0]*/ eodp = (struct odirect *)&buf[size]; /*LINTED [buf is char[] in getfile]*/ for (odp = (struct odirect *)buf; odp < eodp; odp++) if (odp->d_ino != 0) { dcvt(odp, &cvtbuf); putent(&cvtbuf); } } else { loc = 0; while (loc < size) { /*LINTED [buf is char[] in getfile, loc % 4 == 0]*/ dp = (struct direct *)(buf + loc); normdirect(byteorder, dp); i = DIRBLKSIZ - (loc & (DIRBLKSIZ - 1)); if (dp->d_reclen == 0 || (long)dp->d_reclen > i) { loc += i; continue; } loc += dp->d_reclen; if (dp->d_ino != 0) { putent(dp); } } } } /* * These variables are "local" to the following two functions. */ static char dirbuf[DIRBLKSIZ]; static int32_t dirloc = 0; static int32_t prev = 0; /* * add a new directory entry to a file. */ static void putent(struct direct *dp) { /* LINTED DIRSIZ will always fit in a ushort_t */ dp->d_reclen = (ushort_t)DIRSIZ(dp); /* LINTED sign extension ok in assert */ assert(DIRSIZ(dp) == (ulong_t)dp->d_reclen); if (dirloc + (long)dp->d_reclen > DIRBLKSIZ) { /*LINTED [prev += dp->d_reclen, prev % 4 == 0]*/ ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; (void) fwrite(dirbuf, 1, DIRBLKSIZ, df); if (ferror(df)) panic("%s: %s\n", dirfile, strerror(errno)); dirloc = 0; } bcopy((char *)dp, dirbuf + dirloc, (size_t)dp->d_reclen); prev = dirloc; dirloc += dp->d_reclen; } /* * flush out a directory that is finished. */ static void flushent(void) { /* LINTED prev += dp->d_reclen, prev % 4 == 0 */ ((struct direct *)(dirbuf + prev))->d_reclen = DIRBLKSIZ - prev; (void) fwrite(dirbuf, (size_t)dirloc, 1, df); if (ferror(df)) panic("%s: %s\n", dirfile, strerror(errno)); g_seekpt = ftello64(df); dirloc = 0; } static void dcvt(struct odirect *odp, struct direct *ndp) { (void) bzero((char *)ndp, sizeof (*ndp)); ndp->d_ino = odp->d_ino; /* Note that odp->d_name may not be null-terminated */ /* LINTED assertion always true */ assert(sizeof (ndp->d_name) > sizeof (odp->d_name)); (void) strncpy(ndp->d_name, odp->d_name, sizeof (odp->d_name)); ndp->d_name[sizeof (odp->d_name)] = '\0'; /* LINTED: strlen will fit into d_namlen */ ndp->d_namlen = strlen(ndp->d_name); /* LINTED sign extension ok in assert */ assert(DIRSIZ(ndp) == (ulong_t)ndp->d_reclen); /* LINTED DIRSIZ always fits in ushort_t */ ndp->d_reclen = (ushort_t)DIRSIZ(ndp); } /* * Initialize the directory file */ static RST_DIR * rst_initdirfile(char *name) { RST_DIR *dp; int fd; if ((fd = open(name, O_RDONLY | O_LARGEFILE)) == -1) return ((RST_DIR *)0); if ((dp = (RST_DIR *)malloc(sizeof (*dp))) == NULL) { (void) close(fd); return ((RST_DIR *)0); } dp->dd_fd = fd; dp->dd_loc = 0; dp->dd_refcnt = 1; return (dp); } /* * Simulate the opening of a directory */ RST_DIR * rst_opendir(char *name) { struct inotab *itp; ino_t ino; if ((ino = dirlookup(name)) > 0 && (itp = inotablookup(ino)) != NULL) { rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); dirp->dd_refcnt++; return (dirp); } return ((RST_DIR *)0); } /* * Releases the hidden state created by rst_opendir(). * Specifically, the dirp it provided to the caller is malloc'd. */ void rst_closedir(RST_DIR *cdirp) { if ((cdirp != NULL) && (--(cdirp->dd_refcnt) < 1)) free(cdirp); } /* * return a pointer into a directory */ static offset_t rst_telldir(RST_DIR *tdirp) { offset_t pos = llseek(tdirp->dd_fd, (offset_t)0, SEEK_CUR); if (pos == (offset_t)-1) { perror("Could not determine position in directory file"); done(1); } return ((pos - tdirp->dd_size) + tdirp->dd_loc); } /* * Seek to an entry in a directory. * Only values returned by ``rst_telldir'' should be passed to rst_seekdir. * This routine handles many directories in a single file. * It takes the base of the directory in the file, plus * the desired seek offset into it. */ static void rst_seekdir(RST_DIR *sdirp, offset_t loc, offset_t base) { if (loc == rst_telldir(sdirp)) return; loc -= base; if (loc < 0) (void) fprintf(stderr, gettext("bad seek pointer to rst_seekdir %d\n"), loc); (void) llseek(sdirp->dd_fd, base + (loc & ~(DIRBLKSIZ - 1)), 0); sdirp->dd_loc = loc & (DIRBLKSIZ - 1); if (sdirp->dd_loc != 0) sdirp->dd_size = read(sdirp->dd_fd, sdirp->dd_buf, DIRBLKSIZ); } /* * get next entry in a directory. */ struct direct * rst_readdir(RST_DIR *rdirp) { struct direct *dp; for (;;) { if (rdirp->dd_loc == 0) { rdirp->dd_size = read(rdirp->dd_fd, rdirp->dd_buf, DIRBLKSIZ); if (rdirp->dd_size <= 0) { dprintf(stderr, gettext("error reading directory\n")); return ((struct direct *)0); } } if (rdirp->dd_loc >= rdirp->dd_size) { rdirp->dd_loc = 0; continue; } /*LINTED [rvalue will be aligned on int boundary]*/ dp = (struct direct *)(rdirp->dd_buf + rdirp->dd_loc); if (dp->d_reclen == 0 || (long)dp->d_reclen > (DIRBLKSIZ + 1 - rdirp->dd_loc)) { dprintf(stderr, gettext("corrupted directory: bad reclen %d\n"), dp->d_reclen); return ((struct direct *)0); } rdirp->dd_loc += dp->d_reclen; if (dp->d_ino == 0 && strcmp(dp->d_name, "/") != 0) continue; if ((ino_t)(dp->d_ino) >= maxino) { dprintf(stderr, gettext("corrupted directory: bad inum %lu\n"), dp->d_ino); continue; } return (dp); } } /* * Set the mode, owner, and times for all new or changed directories */ void setdirmodes(void) { FILE *smf; struct entry *ep; char *cp, *metadata = NULL; size_t metasize = 0; int override = -1; int saverr; static int complained_chown = 0; static int complained_chmod = 0; int dfd; vprintf(stdout, gettext("Set directory mode, owner, and times.\n")); /* XXX if modefile[0] == '#', shouldn't we just bail here? */ /* XXX why isn't it set already? */ INIT_MODEFILE(); smf = fopen64(modefile, "r"); if (smf == NULL) { perror("fopen"); (void) fprintf(stderr, gettext("cannot open mode file %s\n"), modefile); (void) fprintf(stderr, gettext("directory mode, owner, and times not set\n")); return; } clearerr(smf); for (;;) { (void) fread((char *)&node, 1, sizeof (node), smf); if (feof(smf)) break; ep = lookupino(node.ino); if (command == 'i' || command == 'x') { if (ep == NIL) { skipmetadata(smf, node.metasize); continue; } if (ep->e_flags & EXISTED) { if (override < 0) { if (reply(gettext( "Directories already exist, set modes anyway")) == FAIL) override = 0; else override = 1; } if (override == 0) { /* LINTED: result fits into short */ ep->e_flags &= ~NEW; skipmetadata(smf, node.metasize); continue; } } if (node.ino == ROOTINO && reply(gettext("set owner/mode for '.'")) == FAIL) { skipmetadata(smf, node.metasize); continue; } } if (ep == NIL) { panic(gettext("cannot find directory inode %d\n"), node.ino); skipmetadata(smf, node.metasize); continue; } cp = myname(ep); resolve(myname(ep), &dfd, &cp); if (dfd != AT_FDCWD) { if (fchdir(dfd) < 0) { saverr = errno; (void) fprintf(stderr, gettext("Can not set attribute context: %s\n"), strerror(saverr)); (void) close(dfd); continue; } } if (chmod(cp, node.mode) < 0 && !complained_chmod) { saverr = errno; (void) fprintf(stderr, gettext("Can not set directory permissions: %s\n"), strerror(saverr)); complained_chmod = 1; } if (node.metasize != 0) { if (node.metasize > metasize) metadata = realloc(metadata, metasize = node.metasize); if (metadata == NULL) { (void) fprintf(stderr, gettext("Cannot malloc metadata\n")); done(1); } (void) fread(metadata, 1, node.metasize, smf); metaproc(cp, metadata, node.metasize); } /* * BUG 4302943 * Since the ACLs must be set before fixing the ownership, * chown should be called only after metaproc */ if (chown(cp, node.uid, node.gid) < 0 && !complained_chown) { saverr = errno; (void) fprintf(stderr, gettext("Can not set directory ownership: %s\n"), strerror(saverr)); complained_chown = 1; } utime(cp, (struct utimbuf *)node.timep); /* LINTED: result fits into short */ ep->e_flags &= ~NEW; if (dfd != AT_FDCWD) { fchdir(savepwd); (void) close(dfd); } } if (ferror(smf)) panic(gettext("error setting directory modes\n")); if (metadata != NULL) (void) free(metadata); (void) fclose(smf); } void skipmetadata(FILE *f, size_t size) { /* XXX should we bail if this doesn't work? */ /* LINTED unsigned -> signed conversion ok here */ (void) fseeko(f, (off_t)size, SEEK_CUR); } /* * Generate a literal copy of a directory. */ int genliteraldir(char *name, ino_t ino) { struct inotab *itp; int ofile, dp; off64_t i; size_t size; char buf[BUFSIZ]; itp = inotablookup(ino); if (itp == NULL) { (void) fprintf(stderr, gettext("Cannot find directory inode %d named %s\n"), ino, name); return (FAIL); } if ((ofile = creat(name, 0666)) < 0) { (void) fprintf(stderr, "%s: ", name); (void) fflush(stderr); perror(gettext("cannot create file")); return (FAIL); } rst_seekdir(dirp, itp->t_seekpt, itp->t_seekpt); dp = dup(dirp->dd_fd); if (dp < 0) { perror(gettext("dup(2) failed")); (void) close(ofile); (void) unlink(name); return (FAIL); } for (i = itp->t_size; i != 0; i -= size) { /* LINTED cast is safe due to comparison */ size = i < BUFSIZ ? (size_t)i : BUFSIZ; /* XXX instead of done(), clean up and return FAIL? */ if (read(dp, buf, size) == -1) { (void) fprintf(stderr, gettext( "read error extracting inode %d, name %s\n"), curfile.ino, curfile.name); perror("read"); done(1); } if (write(ofile, buf, size) == -1) { (void) fprintf(stderr, gettext( "write error extracting inode %d, name %s\n"), curfile.ino, curfile.name); perror("write"); done(1); } } (void) close(dp); (void) close(ofile); return (GOOD); } /* * Determine the type of an inode */ int inodetype(ino_t ino) { struct inotab *itp; itp = inotablookup(ino); if (itp == NULL) return (LEAF); return (NODE); } /* * Allocate and initialize a directory inode entry. * If requested, save its pertinent mode, owner, and time info. */ static struct inotab * allocinotab(ino_t ino, struct dinode *dip, off64_t seekpt) { struct inotab *itp; itp = (struct inotab *)calloc(1, sizeof (*itp)); if (itp == 0) { (void) fprintf(stderr, gettext("no memory for directory table\n")); done(1); } itp->t_next = inotab[INOHASH(ino)]; inotab[INOHASH(ino)] = itp; itp->t_ino = ino; itp->t_seekpt = seekpt; if ((dip->di_mode & IFMT) == IFATTRDIR) { itp->t_xattr = xattrlist; xattrlist = itp; } if (mf == NULL) return (itp); node.ino = ino; node.timep[0] = dip->di_atime; node.timep[1] = dip->di_mtime; node.mode = dip->di_mode; node.uid = dip->di_suid == UID_LONG ? dip->di_uid : (uid_t)dip->di_suid; node.gid = dip->di_sgid == GID_LONG ? dip->di_gid : (gid_t)dip->di_sgid; return (itp); } void nodeflush() { char *metadata; if (mf == NULL) { (void) fprintf(stderr, gettext( "Inconsistency detected: modefile pointer is NULL\n")); done(1); } metaget(&metadata, &(node.metasize)); (void) fwrite((char *)&node, 1, sizeof (node), mf); if (node.metasize != 0) (void) fwrite(metadata, 1, node.metasize, mf); if (ferror(mf)) panic("%s: %s\n", modefile, strerror(errno)); } /* * Look up an inode in the table of directories */ static struct inotab * inotablookup(ino_t ino) { struct inotab *itp; for (itp = inotab[INOHASH(ino)]; itp != NULL; itp = itp->t_next) if (itp->t_ino == ino) return (itp); return ((struct inotab *)0); } /* * Clean up and exit */ void done(int exitcode) { closemt(ALLOW_OFFLINE); /* don't force offline on exit */ if (modefile[0] != '#') (void) unlink(modefile); if (dirfile[0] != '#') (void) unlink(dirfile); exit(exitcode); } /* * Copyright 1998,2001-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) 1985 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 "restore.h" #include #include #include extern eucwidth_t wp; #define round(a, b) ((((a) + (b) - 1) / (b)) * (b)) /* * Things to handle interruptions. */ static jmp_buf reset; static int reset_OK; static char *nextarg = NULL; static int dontexpand; /* co-routine state set in getnext, used in expandarg */ static void getcmd(char *, char *, size_t, char *, size_t, struct arglist *); static void expandarg(char *, struct arglist *); static void printlist(char *, ino_t, char *, int); static void formatf(struct arglist *); static char *copynext(char *, char *, size_t); static int fcmp(struct afile *, struct afile *); static char *fmtentry(struct afile *); static void setpagercmd(void); static uint_t setpagerargs(char **); /* * Read and execute commands from the terminal. */ void runcmdshell(void) { struct entry *np; ino_t ino; static struct arglist alist = { 0, 0, 0, 0, 0 }; char curdir[MAXCOMPLEXLEN]; char name[MAXCOMPLEXLEN]; char cmd[BUFSIZ]; #ifdef lint curdir[0] = '\0'; #endif /* lint */ canon("/", curdir, sizeof (curdir)); loop: if (setjmp(reset) != 0) { for (; alist.head < alist.last; alist.head++) freename(alist.head->fname); nextarg = NULL; volno = 0; goto loop; /* make sure jmpbuf is up-to-date */ } reset_OK = 1; getcmd(curdir, cmd, sizeof (cmd), name, sizeof (name), &alist); /* * Using strncmp() to catch unique prefixes. */ switch (cmd[0]) { /* * Add elements to the extraction list. */ case 'a': if (strncmp(cmd, "add", strlen(cmd)) != 0) goto bad; if (name[0] == '\0') break; ino = dirlookup(name); if (ino == 0) break; if (mflag) pathcheck(name); treescan(name, ino, addfile); break; /* * Change working directory. */ case 'c': if (strncmp(cmd, "cd", strlen(cmd)) != 0) goto bad; if (name[0] == '\0') break; ino = dirlookup(name); if (ino == 0) break; if (inodetype(ino) == LEAF) { (void) fprintf(stderr, gettext("%s: not a directory\n"), name); break; } /* No need to canon(name), getcmd() did it for us */ (void) strncpy(curdir, name, sizeof (curdir)); curdir[sizeof (curdir) - 1] = '\0'; break; /* * Delete elements from the extraction list. */ case 'd': if (strncmp(cmd, "delete", strlen(cmd)) != 0) goto bad; if (name[0] == '\0') break; np = lookupname(name); if (np == NIL || (np->e_flags & NEW) == 0) { (void) fprintf(stderr, gettext("%s: not on extraction list\n"), name); break; } treescan(name, np->e_ino, deletefile); break; /* * Extract the requested list. */ case 'e': if (strncmp(cmd, "extract", strlen(cmd)) != 0) goto bad; attrscan(0, addfile); createfiles(); createlinks(); setdirmodes(); if (dflag) checkrestore(); volno = 0; break; /* * List available commands. */ case 'h': if (strncmp(cmd, "help", strlen(cmd)) != 0) goto bad; /*FALLTHROUGH*/ case '?': /* ANSI string catenation, to shut cstyle up */ (void) fprintf(stderr, "%s", gettext("Available commands are:\n" "\tls [arg] - list directory\n" "\tmarked [arg] - list items marked for extraction from directory\n" "\tcd arg - change directory\n" "\tpwd - print current directory\n" "\tadd [arg] - add `arg' to list of files to be extracted\n" "\tdelete [arg] - delete `arg' from list of files to be extracted\n" "\textract - extract requested files\n" "\tsetmodes - set modes of requested directories\n" "\tquit - immediately exit program\n" "\twhat - list dump header information\n" "\tverbose - toggle verbose flag (useful with ``ls'')\n" "\tpaginate - toggle pagination flag (affects ``ls'' and ``marked'')\n" "\tsetpager - set pagination command and arguments\n" "\thelp or `?' - print this list\n" "If no `arg' is supplied, the current directory is used\n")); break; /* * List a directory. */ case 'l': case 'm': if ((strncmp(cmd, "ls", strlen(cmd)) != 0) && (strncmp(cmd, "marked", strlen(cmd)) != 0)) goto bad; if (name[0] == '\0') break; ino = dirlookup(name); if (ino == 0) break; printlist(name, ino, curdir, *cmd == 'm'); break; /* * Print current directory or enable pagination. */ case 'p': if (strlen(cmd) < 2) goto ambiguous; if (strncmp(cmd, "pwd", strlen(cmd)) == 0) { if (curdir[1] == '\0') { (void) fprintf(stderr, "/\n"); } else { (void) fprintf(stderr, "%s\n", &curdir[1]); } } else if (strncmp(cmd, "paginate", strlen(cmd)) == 0) { if (paginating) { (void) fprintf(stderr, gettext("paging disabled\n")); paginating = 0; break; } if (vflag) { (void) fprintf(stderr, gettext("paging enabled (%s)\n"), pager_catenated); } else { (void) fprintf(stderr, gettext("paging enabled\n")); } if (dflag) { int index = 0; while (index < pager_len) { (void) fprintf(stderr, ">>>pager_vector[%d] = `%s'\n", index, pager_vector[index] ? pager_vector[index] : "(null)"); index += 1; } } paginating = 1; } else { goto bad; } break; /* * Quit. */ case 'q': if (strncmp(cmd, "quit", strlen(cmd)) != 0) goto bad; reset_OK = 0; return; case 'x': if (strncmp(cmd, "xit", strlen(cmd)) != 0) goto bad; reset_OK = 0; return; /* * Toggle verbose mode. */ case 'v': if (strncmp(cmd, "verbose", strlen(cmd)) != 0) goto bad; if (vflag) { (void) fprintf(stderr, gettext("verbose mode off\n")); vflag = 0; break; } (void) fprintf(stderr, gettext("verbose mode on\n")); vflag = 1; break; /* * Just restore requested directory modes, or set pagination command. */ case 's': if (strlen(cmd) < 4) goto ambiguous; if (strncmp(cmd, "setmodes", strlen(cmd)) == 0) { setdirmodes(); } else if (strncmp(cmd, "setpager", strlen(cmd)) == 0) { setpagercmd(); } else { goto bad; } break; /* * Print out dump header information. */ case 'w': if (strncmp(cmd, "what", strlen(cmd)) != 0) goto bad; printdumpinfo(); break; /* * Turn on debugging. */ case 'D': if (strncmp(cmd, "Debug", strlen(cmd)) != 0) goto bad; if (dflag) { (void) fprintf(stderr, gettext("debugging mode off\n")); dflag = 0; break; } (void) fprintf(stderr, gettext("debugging mode on\n")); dflag++; break; /* * Unknown command. */ default: bad: (void) fprintf(stderr, gettext("%s: unknown command; type ? for help\n"), cmd); break; ambiguous: (void) fprintf(stderr, gettext("%s: ambiguous command; type ? for help\n"), cmd); break; } goto loop; } static char input[MAXCOMPLEXLEN]; /* shared by getcmd() and setpagercmd() */ #define rawname input /* save space by reusing input buffer */ /* * Read and parse an interactive command. * The first word on the line is assigned to "cmd". If * there are no arguments on the command line, then "curdir" * is returned as the argument. If there are arguments * on the line they are returned one at a time on each * successive call to getcmd. Each argument is first assigned * to "name". If it does not start with "/" the pathname in * "curdir" is prepended to it. Finally "canon" is called to * eliminate any embedded ".." components. */ /* ARGSUSED */ static void getcmd(char *curdir, char *cmd, size_t cmdsiz, char *name, size_t namesiz, struct arglist *ap) { char *cp; char output[MAXCOMPLEXLEN]; /* * Check to see if still processing arguments. */ if (ap->head != ap->last) { (void) strncpy(name, ap->head->fname, namesiz); name[namesiz - 1] = '\0'; /* double null terminate string */ if ((strlen(name) + 2) > namesiz) { fprintf(stderr, gettext("name is too long, ignoring")); memset(name, 0, namesiz); } else { name[strlen(name) + 1] = '\0'; } freename(ap->head->fname); ap->head++; return; } if (nextarg != NULL) goto getnext; /* * Read a command line and trim off trailing white space. */ readagain: do { (void) fprintf(stderr, "%s > ", progname); (void) fflush(stderr); (void) fgets(input, sizeof (input), terminal); } while (!feof(terminal) && input[0] == '\n'); if (feof(terminal)) { (void) strncpy(cmd, "quit", cmdsiz); return; } /* trim off trailing white space and newline */ for (cp = &input[strlen(input) - 2]; cp >= &input[0] && isspace((uchar_t)*cp); cp--) { continue; /*LINTED [empty loop body]*/ } *++cp = '\0'; if ((strlen(input) + 2) > MAXCOMPLEXLEN) { fprintf(stderr, gettext("command is too long\n")); goto readagain; } else { /* double null terminate string */ *(cp + 1) = '\0'; } if (cp == &input[0]) goto readagain; /* * Copy the command into "cmd". */ cp = copynext(input, cmd, cmdsiz); ap->cmd = cmd; /* * If no argument, use curdir as the default. */ if (*cp == '\0') { (void) strncpy(name, curdir, namesiz); name[namesiz - 1] = '\0'; /* double null terminate string */ if ((strlen(name) + 2) > namesiz) { fprintf(stderr, gettext("name is too long, ignoring")); memset(name, 0, namesiz); } else { name[strlen(name) + 1] = '\0'; } return; } nextarg = cp; /* * Find the next argument. */ getnext: cp = copynext(nextarg, rawname, sizeof (rawname)); if (*cp == '\0') nextarg = NULL; else nextarg = cp; /* * If it an absolute pathname, canonicalize it and return it. */ if (rawname[0] == '/') { canon(rawname, name, namesiz); } else { /* * For relative pathnames, prepend the current directory to * it then canonicalize and return it. */ (void) snprintf(output, sizeof (output), "%s/%s", curdir, rawname); canon(output, name, namesiz); } expandarg(name, ap); /* * ap->head->fname guaranteed to be double null-terminated and * no more than MAXCOMPLEXLEN characters long. */ assert(namesiz >= (MAXCOMPLEXLEN)); (void) strcpy(name, ap->head->fname); /* double null terminate string */ name[strlen(name) + 1] = '\0'; freename(ap->head->fname); ap->head++; #undef rawname } /* * Strip off the next token of the input. */ static char * copynext(char *input, char *output, size_t outsize) { char *cp, *bp, *limit; char quote; dontexpand = 0; /* skip to argument */ for (cp = input; *cp != '\0' && isspace((uchar_t)*cp); cp++) { continue; /*LINTED [empty loop body]*/ } bp = output; limit = output + outsize - 1; /* -1 for the trailing \0 */ while (!isspace((uchar_t)*cp) && *cp != '\0' && bp < limit) { /* * Handle back slashes. */ if (*cp == '\\') { if (*++cp == '\0') { (void) fprintf(stderr, gettext( "command lines cannot be continued\n")); continue; } *bp++ = *cp++; continue; } /* * The usual unquoted case. */ if (*cp != '\'' && *cp != '"') { *bp++ = *cp++; continue; } /* * Handle single and double quotes. */ quote = *cp++; dontexpand = 1; while (*cp != quote && *cp != '\0' && bp < limit) *bp++ = *cp++; if (*cp++ == '\0') { (void) fprintf(stderr, gettext("missing %c\n"), (uchar_t)quote); cp--; continue; } } *bp = '\0'; if ((strlen(output) + 2) > outsize) { fprintf(stderr, gettext( "name is too long, ignoring")); memset(output, 0, outsize); } else { /* double null terminate string */ *(bp + 1) = '\0'; } return (cp); } /* * Canonicalize file names to always start with ``./'' and * remove any imbedded "." and ".." components. * * The pathname "canonname" is returned double null terminated. */ void canon(char *rawname, char *canonname, size_t limit) { char *cp, *np, *prefix; uint_t len; assert(limit > 3); if (strcmp(rawname, ".") == 0 || strncmp(rawname, "./", 2) == 0) prefix = ""; else if (rawname[0] == '/') prefix = "."; else prefix = "./"; (void) snprintf(canonname, limit, "%s%s", prefix, rawname); /* * Eliminate multiple and trailing '/'s */ for (cp = np = canonname; *np != '\0'; cp++) { *cp = *np++; while (*cp == '/' && *np == '/') np++; } *cp = '\0'; if ((strlen(canonname) + 2) > limit) { fprintf(stderr, gettext("canonical name is too long, ignoring name\n")); memset(canonname, 0, limit); } else { /* double null terminate string */ *(cp + 1) = '\0'; } if (*--cp == '/') *cp = '\0'; /* * Eliminate extraneous "." and ".." from pathnames. Uses * memmove(), as strcpy() might do the wrong thing for these * small overlaps. */ np = canonname; while (*np != '\0') { np++; cp = np; while (*np != '/' && *np != '\0') np++; if (np - cp == 1 && *cp == '.') { cp--; len = strlen(np); (void) memmove(cp, np, len); *(cp + len) = '\0'; /* double null terminate string */ *(cp + len + 1) = '\0'; np = cp; } if (np - cp == 2 && strncmp(cp, "..", 2) == 0) { cp--; /* find beginning of name */ while (cp > &canonname[1] && *--cp != '/') { continue; /*LINTED [empty loop body]*/ } len = strlen(np); (void) memmove(cp, np, len); *(cp + len) = '\0'; /* double null terminate string */ *(cp + len + 1) = '\0'; np = cp; } } } /* * globals (file name generation) * * "*" in params matches r.e ".*" * "?" in params matches r.e. "." * "[...]" in params matches character class * "[...a-z...]" in params matches a through z. */ static void expandarg(char *arg, struct arglist *ap) { static struct afile single; int size; ap->head = ap->last = (struct afile *)0; if (dontexpand) size = 0; else size = expand(arg, 0, ap); if (size == 0) { struct entry *ep; ep = lookupname(arg); single.fnum = ep ? ep->e_ino : 0; single.fname = savename(arg); ap->head = &single; ap->last = ap->head + 1; return; } if ((ap->last - ap->head) > ULONG_MAX) { (void) fprintf(stderr, gettext("Argument expansion too large to sort\n")); } else { /* LINTED pointer arith just range-checked */ qsort((char *)ap->head, (size_t)(ap->last - ap->head), sizeof (*ap->head), (int (*)(const void *, const void *)) fcmp); } } /* * Do an "ls" style listing of a directory */ static void printlist(char *name, ino_t ino, char *basename, int marked_only) { struct afile *fp; struct direct *dp; static struct arglist alist = { 0, 0, 0, 0, "ls" }; struct afile single; struct entry *np; RST_DIR *dirp; int list_entry; if ((dirp = rst_opendir(name)) == NULL) { single.fnum = ino; if (strncmp(name, basename, strlen(basename)) == 0) single.fname = savename(name + strlen(basename) + 1); else single.fname = savename(name); alist.head = &single; alist.last = alist.head + 1; if (alist.base != NULL) { free(alist.base); alist.base = NULL; } } else { alist.head = (struct afile *)0; (void) fprintf(stderr, "%s:\n", name); while (dp = rst_readdir(dirp)) { if (dp == NULL || dp->d_ino == 0) { rst_closedir(dirp); dirp = NULL; break; } if (!dflag && BIT(dp->d_ino, dumpmap) == 0) continue; if (vflag == 0 && (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)) continue; list_entry = 1; if (marked_only) { np = lookupino(dp->d_ino); if ((np == NIL) || ((np->e_flags & NEW) == 0)) list_entry = 0; } if (list_entry) { if (!mkentry(dp->d_name, dp->d_ino, &alist)) { rst_closedir(dirp); return; } } } } if (alist.head != 0) { if ((alist.last - alist.head) > ULONG_MAX) { (void) fprintf(stderr, gettext("Directory too large to sort\n")); } else { qsort((char *)alist.head, /* LINTED range-checked */ (size_t)(alist.last - alist.head), sizeof (*alist.head), (int (*)(const void *, const void *)) fcmp); } formatf(&alist); for (fp = alist.head; fp < alist.last; fp++) freename(fp->fname); alist.head = NULL; /* * Don't free alist.base, as we'll probably be called * again, and might as well re-use what we've got. */ } if (dirp != NULL) { (void) fprintf(stderr, "\n"); rst_closedir(dirp); } } /* * Print out a pretty listing of a directory */ static void formatf(struct arglist *ap) { struct afile *fp; struct entry *np; /* LINTED: result fits into an int */ int nentry = (int)(ap->last - ap->head); int i, j; uint_t len, w, width = 0, columns, lines; char *cp; FILE *output = stderr; if (ap->head == ap->last) return; if (paginating) { int fds[2]; if (pipe(fds) < 0) { perror(gettext("could not create pipe")); goto no_page; } switch (fork()) { case -1: perror(gettext("could not fork")); goto no_page; case 0: /* * Make sure final output still ends up in * the same place. */ (void) dup2(fileno(stderr), fileno(stdout)); (void) close(fds[0]); (void) dup2(fds[1], fileno(stdin)); execvp(pager_vector[0], pager_vector); perror(gettext("execvp of pager failed")); exit(1); /*NOTREACHED*/ default: (void) close(fds[1]); output = fdopen(fds[0], "w"); if (output != (FILE *)NULL) { break; } perror(gettext("could not open pipe to pager")); output = stderr; no_page: (void) fprintf(stderr, gettext("pagination disabled\n")); paginating = 0; } } for (fp = ap->head; fp < ap->last; fp++) { fp->ftype = inodetype(fp->fnum); np = lookupino(fp->fnum); if (np != NIL) fp->fflags = np->e_flags; else fp->fflags = 0; len = strlen(fmtentry(fp)); if (len > width) width = len; } width += 2; columns = 80 / width; if (columns == 0) columns = 1; lines = (nentry + columns - 1) / columns; for (i = 0; i < lines && !ferror(output); i++) { for (j = 0; j < columns && !ferror(output); j++) { fp = ap->head + j * lines + i; cp = fmtentry(fp); (void) fprintf(output, "%s", cp); if (fp + lines >= ap->last) { (void) fprintf(output, "\n"); break; } w = strlen(cp); while (w < width) { w++; if (fprintf(output, " ") < 0) break; } } } if (paginating) { (void) fclose(output); (void) wait((int *)NULL); } } /* * Comparison routine for qsort. */ static int fcmp(struct afile *f1, struct afile *f2) { return (strcoll(f1->fname, f2->fname)); } /* * Format a directory entry. */ static char * fmtentry(struct afile *fp) { static char fmtres[MAXCOMPLEXLEN]; static int precision = 0; ino_t i; char *cp, *dp, *limit; if (!vflag) { /* MAXCOMPLEXLEN assumed to be >= 1 */ fmtres[0] = '\0'; } else { if (precision == 0) { for (i = maxino; i != 0; i /= 10) precision++; if (sizeof (fmtres) < (unsigned)(precision + 2)) { (void) fprintf(stderr, gettext( "\nInternal check failed, minimum width %d exceeds available size %d\n"), (precision + 2), sizeof (fmtres)); done(1); } } (void) snprintf(fmtres, sizeof (fmtres), "%*ld ", precision, fp->fnum); } dp = &fmtres[strlen(fmtres)]; limit = fmtres + sizeof (fmtres) - 1; if (dflag && BIT(fp->fnum, dumpmap) == 0) *dp++ = '^'; else if ((fp->fflags & NEW) != 0) *dp++ = '*'; else *dp++ = ' '; for (cp = fp->fname; *cp && dp < limit; cp++) /* LINTED: precedence ok, can't fix system macro */ if (!vflag && (!ISPRINT(*cp, wp))) *dp++ = '?'; else *dp++ = *cp; if (fp->ftype == NODE && dp < limit) *dp++ = '/'; *dp++ = 0; return (fmtres); } /* * respond to interrupts */ /* ARGSUSED */ void onintr(int sig) { char buf[300]; if (command == 'i' && reset_OK) longjmp(reset, 1); (void) snprintf(buf, sizeof (buf), gettext("%s interrupted, continue"), progname); if (reply(buf) == FAIL) done(1); } /* * Set up pager_catenated and pager_vector. */ void initpagercmd(void) { char *cp; cp = getenv("PAGER"); if (cp != NULL) pager_catenated = strdup(cp); if ((pager_catenated == NULL) || (*pager_catenated == '\0')) { if (pager_catenated != NULL) free(pager_catenated); pager_catenated = strdup(DEF_PAGER); } if (pager_catenated == NULL) { (void) fprintf(stderr, gettext("out of memory\n")); done(1); } pager_vector = (char **)malloc(sizeof (char *)); if (pager_vector == NULL) { (void) fprintf(stderr, gettext("out of memory\n")); done(1); } pager_len = 1; cp = pager_catenated; (void) setpagerargs(&cp); } /* * Resets pager_catenated and pager_vector from user input. */ void setpagercmd(void) { uint_t catenate_length; int index; /* * We'll get called immediately after setting a pager, due to * our interaction with getcmd()'s internal state. Don't do * anything when that happens. */ if (*input == '\0') return; if (pager_len > 0) { for (index = 0; pager_vector[index] != (char *)NULL; index += 1) free(pager_vector[index]); free(pager_vector); free(pager_catenated); } pager_vector = (char **)malloc(2 * sizeof (char *)); if (pager_vector == NULL) { (void) fprintf(stderr, gettext("out of memory\n")); done(1); } pager_len = 2; pager_vector[0] = strdup(input); if (pager_vector[0] == NULL) { (void) fprintf(stderr, gettext("out of memory\n")); done(1); } if (dflag) (void) fprintf(stderr, gettext("got command `%s'\n"), input); catenate_length = setpagerargs(&nextarg) + strlen(pager_vector[0]) + 1; pager_catenated = (char *)malloc(catenate_length * (size_t)sizeof (char)); if (pager_catenated == (char *)NULL) { (void) fprintf(stderr, gettext("out of memory\n")); done(1); } for (index = 0; pager_vector[index] != (char *)NULL; index += 1) { if (index > 0) (void) strcat(pager_catenated, " "); (void) strcat(pager_catenated, pager_vector[index]); } } /* * Extract arguments for the pager command from getcmd()'s input buffer. */ static uint_t setpagerargs(char **source) { char word[MAXCOMPLEXLEN]; char *cp = *source; uint_t length = 0; while ((cp != (char *)NULL) && (*cp != '\0')) { cp = copynext(cp, word, sizeof (word)); if (dflag) fprintf(stderr, gettext("got word `%s'\n"), word); pager_vector = (char **)realloc(pager_vector, (size_t)sizeof (char *) * (pager_len + 1)); if (pager_vector == (char **)NULL) { (void) fprintf(stderr, gettext("out of memory\n")); done(1); } pager_vector[pager_len - 1] = strdup(word); if (pager_vector[pager_len - 1] == (char *)NULL) { (void) fprintf(stderr, gettext("out of memory\n")); done(1); } length += strlen(word) + 1; pager_len += 1; } pager_vector[pager_len - 1] = (char *)NULL; *source = cp; return (length); } /stdio.h".*sometimes ignored: fprintf/d /stdio.h".*always ignored: snprintf/d /stdio.h".*always ignored: printf/d /string.h".*sometimes ignored: memcpy/d /time.h".*sometimes ignored: time/d /myrcmd.c",.*assigned value never used: retval .*(22[0-9])/d /tape.c",.*assigned value never used: mt .*(22[0-9])/d /main.c",.*modification using a NULL pointer/d /dirs.c",.*name used but not defined: utime /d /lint suppression directive/d /byteorder.c",.*inconsistent use of a value type/d /byteorder.c",.*assigned value never used: allocated at.*byteorder.c/d /unistd.h".*always ignored: sleep/d /unistd.h".*always ignored: execvp/d /* * Copyright 2005 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) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* * Modified to recursively extract all files within a subtree * (supressed by the h option) and recreate the heirarchical * structure of that subtree and move extracted files to their * proper homes (supressed by the m option). * Includes the s (skip files) option for use with multiple * dumps on a single tape. * 8/29/80 by Mike Litzkow * * Modified to work on the new file system and to recover from * tape read errors. * 1/19/82 by Kirk McKusick * * Full incremental restore running entirely in user code and * interactive tape browser. * 1/19/83 by Kirk McKusick */ #include "restore.h" #include #include #include #include #include #include eucwidth_t wp; int bflag = 0, dflag = 0, vflag = 0, yflag = 0; int hflag = 1, mflag = 1, paginating = 0, offline = 0, autoload = 0; int autoload_tries; int autoload_period; int cvtflag = 0; /* Converting from old dump format */ char command = '\0'; long dumpnum = 1; int volno = 0; uint_t ntrec; /* blocking factor, in KB */ uint_t saved_ntrec; /* saved blocking factor, in KB */ ssize_t tape_rec_size = 0; /* tape record size (ntrec * tp_bsize) */ size_t newtapebuf_size = 0; /* save size of last call to newtapebuf */ char *progname; char *dumpmap; char *clrimap; char *c_label; /* if non-NULL, we must see this tape label */ ino_t maxino; time_t dumptime; time_t dumpdate; FILE *terminal; char *tmpdir; char *pager_catenated; char **pager_vector; int pager_len; int inattrspace = 0; int savepwd; int32_t tp_bsize = TP_BSIZE_MIN; struct byteorder_ctx *byteorder; static void set_tmpdir(void); int main(int argc, char *argv[]) { static struct arglist alist = { 0, 0, 0, 0, 0 }; int count; char *cp; char *fname; ino_t ino; char *inputdev; char *archivefile = 0; char *symtbl = RESTORESYMTABLE; char name[MAXPATHLEN]; int fflag = 0; struct sigaction sa, osa; int multiplier; char units; if ((progname = strrchr(argv[0], '/')) != NULL) progname++; else progname = argv[0]; if (strcmp("hsmrestore", progname) == 0) { (void) fprintf(stderr, gettext("hsmrestore emulation is no longer supported.\n")); done(1); } /* * Convert the effective uid of 0 to the single privilege * we really want. When running with all privileges, this * is a no-op. When the set-uid bit is stripped restore * still works for local tapes. Fail when trying to access * a remote tape in that case and not immediately. */ (void) __init_suid_priv(0, PRIV_NET_PRIVADDR, (char *)NULL); inputdev = DEFTAPE; /* * This doesn't work because ufsrestore is statically linked: * (void) setlocale(LC_ALL, ""); * The problem seems to be with LC_COLLATE, so set all the * others explicitly. Bug 1157128 was created against the I18N * library. When that bug is fixed this should go back to the way * it was. * XXX 1157128 was closed as a dup of 1099747. That bug was fixed by * disallowing setlocale() to anything other than "C". "" is * allowed, but only if none of the envars LC_ALL, LC_COLLATE, or LANG * select anything other than "C". */ (void) setlocale(LC_CTYPE, ""); (void) setlocale(LC_NUMERIC, ""); (void) setlocale(LC_TIME, ""); (void) setlocale(LC_MONETARY, ""); (void) setlocale(LC_MESSAGES, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); getwidth(&wp); if ((byteorder = byteorder_create()) == NULL) { (void) fprintf(stderr, gettext("Cannot create byteorder context\n")); done(1); } if ((savepwd = open(".", O_RDONLY)) < 0) { (void) fprintf(stderr, gettext("Cannot save current directory context\n")); done(1); } set_tmpdir(); autoload_period = 12; autoload_tries = 12; /* traditional default of ~2.5 minutes */ sa.sa_handler = onintr; sa.sa_flags = SA_RESTART; (void) sigemptyset(&sa.sa_mask); (void) sigaction(SIGINT, &sa, &osa); if (osa.sa_handler == SIG_IGN) (void) sigaction(SIGINT, &osa, (struct sigaction *)0); (void) sigaction(SIGTERM, &sa, &osa); if (osa.sa_handler == SIG_IGN) (void) sigaction(SIGTERM, &osa, (struct sigaction *)0); if (argc < 2) { usage: (void) fprintf(stderr, gettext("Usage:\n\ \t%s tabcdfhsvyLloT [file file ...]\n\ \t%s xabcdfhmsvyLloT [file file ...]\n\ \t%s iabcdfhmsvyLloT\n\ \t%s rabcdfsvyLloT\n\ \t%s RabcdfsvyLloT\n\n\ a requires an archive file name\n\ b requires a blocking factor\n\ f requires a dump file\n\ s requires a file number\n\ L requires a tape label\n\ If set, the envar TMPDIR selects where temporary files are kept\n"), progname, progname, progname, progname, progname); done(1); } argv++; /* the bag-of-options */ argc -= 2; /* count of parameters to the options */ command = '\0'; c_label = (char *)NULL; /* any tape's acceptable */ for (cp = *argv++; *cp; cp++) { switch (*cp) { /* BE CAUTIOUS OF FALLTHROUGHS */ case 'T': if (argc < 1) { (void) fprintf(stderr, gettext( "Missing autoload timeout period\n")); done(1); } count = atoi(*argv); if (count < 1) { (void) fprintf(stderr, gettext( "Unreasonable autoload timeout period `%s'\n"), *argv); done(1); } 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: (void) fprintf(stderr, gettext( "Unknown timeout units indicator `%c'\n"), units); done(1); } autoload_tries = 1 + ((count * multiplier) / autoload_period); argv++; argc--; break; case 'l': autoload++; break; case 'o': offline++; break; case '-': break; case 'a': if (argc < 1) { (void) fprintf(stderr, gettext("missing archive file name\n")); done(1); } archivefile = *argv++; if (*archivefile == '\0') { (void) fprintf(stderr, gettext("empty archive file name\n")); done(1); } argc--; break; case 'c': cvtflag++; break; case 'd': dflag++; break; case 'D': /* * This used to be the Dflag, but it doesn't * hurt to always check, so was removed. This * case is here for backward compatability. */ break; case 'h': hflag = 0; break; case 'm': mflag = 0; break; case 'v': vflag++; break; case 'y': yflag++; break; case 'f': if (argc < 1) { (void) fprintf(stderr, gettext("missing device specifier\n")); done(1); } inputdev = *argv++; if (*inputdev == '\0') { (void) fprintf(stderr, gettext("empty device specifier\n")); done(1); } fflag++; argc--; break; case 'b': /* * change default tape blocksize */ bflag++; if (argc < 1) { (void) fprintf(stderr, gettext("missing block size\n")); done(1); } saved_ntrec = ntrec = atoi(*argv++); if (ntrec == 0 || (ntrec&1)) { (void) fprintf(stderr, gettext( "Block size must be a positive, even integer\n")); done(1); } ntrec /= (tp_bsize/DEV_BSIZE); argc--; break; case 's': /* * dumpnum (skip to) for multifile dump tapes */ if (argc < 1) { (void) fprintf(stderr, gettext("missing dump number\n")); done(1); } dumpnum = atoi(*argv++); if (dumpnum <= 0) { (void) fprintf(stderr, gettext( "Dump number must be a positive integer\n")); done(1); } argc--; break; case 't': case 'R': case 'r': case 'x': case 'i': if (command != '\0') { (void) fprintf(stderr, gettext( "%c and %c are mutually exclusive\n"), (uchar_t)*cp, (uchar_t)command); goto usage; } command = *cp; break; case 'L': if (argc < 1 || **argv == '\0') { (void) fprintf(stderr, gettext("Missing tape label name\n")); done(1); } c_label = *argv++; /* must get tape with this label */ if (strlen(c_label) > (sizeof (spcl.c_label) - 1)) { c_label[sizeof (spcl.c_label) - 1] = '\0'; (void) fprintf(stderr, gettext( "Truncating label to maximum supported length: `%s'\n"), c_label); } argc--; break; default: (void) fprintf(stderr, gettext("Bad key character %c\n"), (uchar_t)*cp); goto usage; } } if (command == '\0') { (void) fprintf(stderr, gettext("must specify i, t, r, R, or x\n")); goto usage; } setinput(inputdev, archivefile); if (argc == 0) { /* re-use last argv slot for default */ argc = 1; *--argv = mflag ? "." : "2"; } switch (command) { /* * Interactive mode. */ case 'i': setup(); extractdirs(1); initsymtable((char *)0); initpagercmd(); runcmdshell(); done(0); /* NOTREACHED */ /* * Incremental restoration of a file system. */ case 'r': setup(); if (dumptime > 0) { /* * This is an incremental dump tape. */ vprintf(stdout, gettext("Begin incremental restore\n")); initsymtable(symtbl); extractdirs(1); removeoldleaves(); vprintf(stdout, gettext("Calculate node updates.\n")); strcpy(name, "."); name[2] = '\0'; treescan(name, ROOTINO, nodeupdates); attrscan(1, nodeupdates); findunreflinks(); removeoldnodes(); } else { /* * This is a level zero dump tape. */ vprintf(stdout, gettext("Begin level 0 restore\n")); initsymtable((char *)0); extractdirs(1); vprintf(stdout, gettext("Calculate extraction list.\n")); strcpy(name, "."); name[2] = '\0'; treescan(name, ROOTINO, nodeupdates); attrscan(1, nodeupdates); } createleaves(symtbl); createlinks(); setdirmodes(); checkrestore(); if (dflag) { vprintf(stdout, gettext("Verify the directory structure\n")); strcpy(name, "."); name[2] = '\0'; treescan(name, ROOTINO, verifyfile); } dumpsymtable(symtbl, (long)1); done(0); /* NOTREACHED */ /* * Resume an incremental file system restoration. */ case 'R': setupR(); initsymtable(symtbl); skipmaps(); skipdirs(); createleaves(symtbl); createlinks(); setdirmodes(); checkrestore(); dumpsymtable(symtbl, (long)1); done(0); /* NOTREACHED */ /* * List contents of tape. */ case 't': setup(); extractdirs(0); initsymtable((char *)0); if (vflag) printdumpinfo(); while (argc--) { canon(*argv++, name, sizeof (name)); name[strlen(name)+1] = '\0'; ino = dirlookup(name); if (ino == 0) continue; treescan(name, ino, listfile); } done(0); /* NOTREACHED */ /* * Batch extraction of tape contents. */ case 'x': setup(); extractdirs(1); initsymtable((char *)0); while (argc--) { if (mflag) { canon(*argv++, name, sizeof (name)); if (expand(name, 0, &alist) == 0) { /* no meta-characters to expand */ ino = dirlookup(name); if (ino == 0) continue; pathcheck(name); } else { /* add each of the expansions */ while ((alist.last - alist.head) > 0) { fname = alist.head->fname; ino = dirlookup(fname); if (ino != 0) { pathcheck(fname); treescan(fname, ino, addfile); } freename(fname); alist.head++; } alist.head = (struct afile *)NULL; continue; /* argc loop */ } } else { ino = (ino_t)atol(*argv); if ((*(*argv++) == '-') || ino < ROOTINO) { (void) fprintf(stderr, gettext( "bad inode number: %ld\n"), ino); done(1); } name[0] = '\0'; } treescan(name, ino, addfile); attrscan(0, addfile); } createfiles(); createlinks(); setdirmodes(); if (dflag) checkrestore(); done(0); /* NOTREACHED */ } return (0); } /* * Determine where the user wants us to put our temporary files, * and make sure we can actually do so. Bail out if there's a problem. */ void set_tmpdir(void) { int fd; char name[MAXPATHLEN]; tmpdir = getenv("TMPDIR"); if ((tmpdir == (char *)NULL) || (*tmpdir == '\0')) tmpdir = "/tmp"; if (*tmpdir != '/') { (void) fprintf(stderr, gettext("TMPDIR is not an absolute path (`%s').\n"), tmpdir); done(1); } /* * The actual use of tmpdir is in dirs.c, and is of the form * tmpdir + "/rst" + type (three characters) + "%ld.XXXXXX" + * a trailing NUL, where %ld is an arbitrary time_t. * * Thus, the magic 31 is strlen(itoa(MAX_TIME_T)) + "/rst" + * ".XXXXXX" + '\0'. A time_t is 64 bits, so MAX_TIME_T is * LONG_MAX - nineteen digits. In theory, so many things in * ufsrestore will break once time_t's value goes beyond 32 * bits that it's not worth worrying about this particular * instance at this time, but we've got to start somewhere. * * Note that the use of a pid below is just for testing the * validity of the named directory. */ if (strlen(tmpdir) > (MAXPATHLEN - 31)) { (void) fprintf(stderr, gettext("TMPDIR too long\n")); done(1); } /* Guaranteed to fit by above test (sizeof(time_t) >= sizeof(pid_t)) */ (void) snprintf(name, sizeof (name), "%s/rstdir.%ld", tmpdir, getpid()); /* * This is effectively a stripped-down version of safe_open(), * because if the file exists, we want to fail. */ fd = open(name, O_CREAT|O_EXCL|O_RDWR, 0600); if (fd < 0) { perror(gettext("Can not create temporary file")); done(1); } (void) close(fd); if (unlink(name) < 0) { perror(gettext("Can not delete temporary file")); done(1); } } /* * 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) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #include "restore.h" /* undef MAXNAMLEN to prevent compiler warnings about redef in dirent.h */ #undef MAXNAMLEN #include static char *keyval(int); static void removexattrs(struct entry *); static void movexattrs(char *, char *); /* * This implements the 't' option. * List entries on the tape. */ long listfile(char *name, ino_t ino, int type) { long descend = hflag ? GOOD : FAIL; if (BIT(ino, dumpmap) == 0) { return (descend); } vprintf(stdout, "%s", type == LEAF ? gettext("leaf") : gettext("dir ")); (void) fprintf(stdout, "%10lu\t%s\n", ino, name); return (descend); } /* * This implements the 'x' option. * Request that new entries be extracted. */ long addfile(char *name, ino_t ino, int type) { struct entry *ep; long descend = hflag ? GOOD : FAIL; char buf[100]; /* Don't know if ino_t is long or long long, so be safe w/ *printf() */ if (BIT(ino, dumpmap) == 0) { if (mflag) { dprintf(stdout, gettext( "%s: not on the volume\n"), name); } else { dprintf(stdout, gettext( "inode %llu: not on the volume\n"), (u_longlong_t)ino); } return (descend); } if (!mflag) { (void) snprintf(buf, sizeof (buf), "./%llu", (u_longlong_t)ino); buf[sizeof (buf) - 1] = '\0'; name = buf; if (type == NODE) { (void) genliteraldir(name, ino); return (descend); } } ep = lookupino(ino); if (ep != NIL) { if (strcmp(name, myname(ep)) == 0) { /* LINTED: result fits into a short */ ep->e_flags |= NEW; return (descend); } type |= LINK; } ep = addentry(name, ino, type); if (type == NODE) newnode(ep); /* LINTED: result fits into a short */ ep->e_flags |= NEW; return (descend); } /* * This is used by the 'i' option to undo previous requests made by addfile. * Delete entries from the request queue. */ /* ARGSUSED */ long deletefile(char *name, ino_t ino, int type) { long descend = hflag ? GOOD : FAIL; struct entry *ep; if (BIT(ino, dumpmap) == 0) { return (descend); } ep = lookupino(ino); if (ep != NIL) { /* LINTED: result fits into a short */ ep->e_flags &= ~NEW; } return (descend); } /* * The following four routines implement the incremental * restore algorithm. The first removes old entries, the second * does renames and calculates the extraction list, the third * cleans up link names missed by the first two, and the final * one deletes old directories. * * Directories cannot be immediately deleted, as they may have * other files in them which need to be moved out first. As * directories to be deleted are found, they are put on the * following deletion list. After all deletions and renames * are done, this list is actually deleted. */ static struct entry *removelist; /* * Remove unneeded leaves from the old tree. * Remove directories from the lookup chains. */ void removeoldleaves(void) { struct entry *ep; ino_t i; vprintf(stdout, gettext("Mark entries to be removed.\n")); for (i = ROOTINO + 1; i < maxino; i++) { if (BIT(i, clrimap)) continue; ep = lookupino(i); if (ep == NIL) continue; while (ep != NIL) { dprintf(stdout, gettext("%s: REMOVE\n"), myname(ep)); removexattrs(ep->e_xattrs); if (ep->e_type == LEAF) { removeleaf(ep); freeentry(ep); } else { mktempname(ep); deleteino(ep->e_ino); /* * once the inode is deleted from the symbol * table, the e_next field is reusable */ ep->e_next = removelist; removelist = ep; } ep = ep->e_links; } } } /* * For each directory entry on the incremental tape, determine which * category it falls into as follows: * KEEP - entries that are to be left alone. * NEW - new entries to be added. * EXTRACT - files that must be updated with new contents. * LINK - new links to be added. * Renames are done at the same time. */ long nodeupdates(char *name, ino_t ino, int type) { struct entry *ep, *np, *ip; long descend = GOOD; int lookuptype = 0; int key = 0; /* key values */ #define ONTAPE 0x1 /* inode is on the tape */ #define INOFND 0x2 /* inode already exists */ #define NAMEFND 0x4 /* name already exists */ #define MODECHG 0x8 /* mode of inode changed */ /* * This routine is called once for each element in the * directory hierarchy, with a full path name. * The "type" value is incorrectly specified as LEAF for * directories that are not on the dump tape. * * Check to see if the file is on the tape. */ if (BIT(ino, dumpmap)) key |= ONTAPE; /* * Check to see if the name exists, and if the name is a link. */ np = lookupname(name); if (np != NIL) { key |= NAMEFND; ip = lookupino(np->e_ino); if (ip == NULL) { (void) fprintf(stderr, gettext("corrupted symbol table\n")); done(1); } if (ip != np) lookuptype = LINK; } /* * Check to see if the inode exists, and if one of its links * corresponds to the name (if one was found). */ ip = lookupino(ino); if (ip != NIL) { key |= INOFND; for (ep = ip->e_links; ep != NIL; ep = ep->e_links) { if (ep == np) { /* * Need to set the NEW flag on the hard link * so it gets created because we extract the * "parent". If the NAMEFND key is set, remove * the leaf. */ if (ip->e_flags & EXTRACT) { if (key & NAMEFND) { removeleaf(np); freeentry(np); np = NIL; key &= ~NAMEFND; } ep->e_flags |= NEW; } else { ip = ep; } break; } } } /* * If both a name and an inode are found, but they do not * correspond to the same file, then both the inode that has * been found and the inode corresponding to the name that * has been found need to be renamed. The current pathname * is the new name for the inode that has been found. Since * all files to be deleted have already been removed, the * named file is either a now-unneeded link, or it must live * under a new name in this dump level. If it is a link, it * can be removed. If it is not a link, it is given a * temporary name in anticipation that it will be renamed * when it is later found by inode number. */ if (((key & (INOFND|NAMEFND)) == (INOFND|NAMEFND)) && ip != np) { if (lookuptype == LINK) { removeleaf(np); freeentry(np); } else { dprintf(stdout, gettext("name/inode conflict, mktempname %s\n"), myname(np)); mktempname(np); } np = NIL; key &= ~NAMEFND; } if ((key & ONTAPE) && (((key & INOFND) && ip->e_type != type) || ((key & NAMEFND) && np->e_type != type))) key |= MODECHG; /* * Decide on the disposition of the file based on its flags. * Note that we have already handled the case in which * a name and inode are found that correspond to different files. * Thus if both NAMEFND and INOFND are set then ip == np. */ switch (key) { /* * A previously existing file has been found. * Mark it as KEEP so that other links to the inode can be * detected, and so that it will not be reclaimed by the search * for unreferenced names. */ case INOFND|NAMEFND: /* LINTED: result fits into a short */ ip->e_flags |= KEEP; dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, flagvalues(ip)); break; /* * A file on the tape has a name which is the same as a name * corresponding to a different file in the previous dump. * Since all files to be deleted have already been removed, * this file is either a now-unneeded link, or it must live * under a new name in this dump level. If it is a link, it * can simply be removed. If it is not a link, it is given a * temporary name in anticipation that it will be renamed * when it is later found by inode number (see INOFND case * below). The entry is then treated as a new file. */ case ONTAPE|NAMEFND: case ONTAPE|NAMEFND|MODECHG: if (lookuptype == LINK || key == (ONTAPE|NAMEFND)) { removeleaf(np); freeentry(np); } else { /* * Create a temporary node only if MODECHG. */ mktempname(np); } /*FALLTHROUGH*/ /* * A previously non-existent file. * Add it to the file system, and request its extraction. * If it is a directory, create it immediately. * (Since the name is unused there can be no conflict) */ case ONTAPE: ep = addentry(name, ino, type); if (type == NODE) newnode(ep); /* LINTED: result fits into a short */ ep->e_flags |= NEW|KEEP; dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, flagvalues(ep)); break; /* * A file with the same inode number, but a different * name has been found. If the other name has not already * been found (indicated by the KEEP flag, see above) then * this must be a new name for the file, and it is renamed. * If the other name has been found then this must be a * link to the file. Hard links to directories are not * permitted, and are either deleted or converted to * symbolic links. Finally, if the file is on the tape, * a request is made to extract it. */ case ONTAPE|INOFND: if (type == LEAF && (ip->e_flags & KEEP) == 0) { /* LINTED: result fits into a short */ ip->e_flags |= EXTRACT; } /*FALLTHROUGH*/ case INOFND: if ((ip->e_flags & KEEP) == 0) { renameit(myname(ip), name); moveentry(ip, name); /* LINTED: result fits into a short */ ip->e_flags |= KEEP; dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, flagvalues(ip)); break; } if (ip->e_type == NODE) { descend = FAIL; (void) fprintf(stderr, gettext( "deleted hard link %s to directory %s\n"), name, myname(ip)); break; } ep = addentry(name, ino, type|LINK); /* LINTED: result fits into a short */ ep->e_flags |= NEW; dprintf(stdout, "[%s] %s: %s|LINK\n", keyval(key), name, flagvalues(ep)); break; /* * A previously known file which is to be updated. */ case ONTAPE|INOFND|NAMEFND: /* * Extract leaf nodes. */ if (type == LEAF) { /* LINTED: result fits into a short */ np->e_flags |= EXTRACT; } /* LINTED: result fits into a short */ np->e_flags |= KEEP; dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, flagvalues(np)); break; /* * An inode is being reused in a completely different way. * Normally an extract can simply do an "unlink" followed * by a "creat". Here we must do effectively the same * thing. The complications arise because we cannot really * delete a directory since it may still contain files * that we need to rename, so we delete it from the symbol * table, and put it on the list to be deleted eventually. * Conversely if a directory is to be created, it must be * done immediately, rather than waiting until the * extraction phase. */ case ONTAPE|INOFND|MODECHG: case ONTAPE|INOFND|NAMEFND|MODECHG: if (ip->e_flags & KEEP) { badentry(ip, gettext("cannot KEEP and change modes")); break; } if (ip->e_type == LEAF) { /* changing from leaf to node */ removeleaf(ip); freeentry(ip); ip = addentry(name, ino, type); newnode(ip); } else { /* changing from node to leaf */ if ((ip->e_flags & TMPNAME) == 0) mktempname(ip); deleteino(ip->e_ino); ip->e_next = removelist; removelist = ip; ip = addentry(name, ino, type); } /* LINTED: result fits into a short */ ip->e_flags |= NEW|KEEP; dprintf(stdout, "[%s] %s: %s\n", keyval(key), name, flagvalues(ip)); break; /* * A hard link to a directory that has been removed. * Ignore it. */ case NAMEFND: dprintf(stdout, gettext("[%s] %s: Extraneous name\n"), keyval(key), name); descend = FAIL; break; /* * If we find a directory entry for a file that is not on * the tape, then we must have found a file that was created * while the dump was in progress. Since we have no contents * for it, we discard the name knowing that it will be on the * next incremental tape. */ case 0: (void) fprintf(stderr, gettext("%s: (inode %lu) not found on volume\n"), name, ino); break; /* * If any of these arise, something is grievously wrong with * the current state of the symbol table. */ case INOFND|NAMEFND|MODECHG: case NAMEFND|MODECHG: case INOFND|MODECHG: (void) fprintf(stderr, "[%s] %s: %s\n", keyval(key), name, gettext("inconsistent state")); done(1); /* NOTREACHED */ /* * These states "cannot" arise for any state of the symbol table. */ case ONTAPE|MODECHG: case MODECHG: default: (void) fprintf(stderr, "[%s] %s: %s\n", keyval(key), name, gettext("impossible state")); done(1); /* NOTREACHED */ } return (descend); } /* * Calculate the active flags in a key. */ static char * keyval(int key) { static char keybuf[32]; /* Note longest case is everything except |NIL */ (void) strcpy(keybuf, "|NIL"); keybuf[0] = '\0'; if (key & ONTAPE) (void) strcat(keybuf, "|ONTAPE"); if (key & INOFND) (void) strcat(keybuf, "|INOFND"); if (key & NAMEFND) (void) strcat(keybuf, "|NAMEFND"); if (key & MODECHG) (void) strcat(keybuf, "|MODECHG"); return (&keybuf[1]); } /* * Find unreferenced link names. */ void findunreflinks(void) { struct entry *ep, *np; ino_t i; vprintf(stdout, gettext("Find unreferenced names.\n")); for (i = ROOTINO; i < maxino; i++) { ep = lookupino(i); if (ep == NIL || ep->e_type == LEAF || BIT(i, dumpmap) == 0) continue; for (np = ep->e_entries; np != NIL; np = np->e_sibling) { if (np->e_flags == 0) { dprintf(stdout, gettext( "%s: remove unreferenced name\n"), myname(np)); removeleaf(np); freeentry(np); } } } /* * Any leaves remaining in removed directories are unreferenced. */ for (ep = removelist; ep != NIL; ep = ep->e_next) { for (np = ep->e_entries; np != NIL; np = np->e_sibling) { if (np->e_type == LEAF) { if (np->e_flags != 0) badentry(np, gettext( "unreferenced with flags")); dprintf(stdout, gettext( "%s: remove unreferenced name\n"), myname(np)); removeleaf(np); freeentry(np); } } } } /* * Remove old nodes (directories). * Note that this routine runs in O(N*D) where: * N is the number of directory entries to be removed. * D is the maximum depth of the tree. * If N == D this can be quite slow. If the list were * topologically sorted, the deletion could be done in * time O(N). */ void removeoldnodes(void) { struct entry *ep, **prev; long change; vprintf(stdout, gettext("Remove old nodes (directories).\n")); do { change = 0; prev = &removelist; for (ep = removelist; ep != NIL; ep = *prev) { if (ep->e_entries != NIL) { prev = &ep->e_next; continue; } *prev = ep->e_next; removenode(ep); freeentry(ep); change++; } } while (change); for (ep = removelist; ep != NIL; ep = ep->e_next) badentry(ep, gettext("cannot remove, non-empty")); } /* * This is the routine used to extract files for the 'r' command. * Extract new leaves. */ void createleaves(char *symtabfile) { struct entry *ep; char name[MAXCOMPLEXLEN]; ino_t first; int curvol; if (command == 'R') { vprintf(stdout, gettext("Continue extraction of new leaves\n")); } else { vprintf(stdout, gettext("Extract new leaves.\n")); dumpsymtable(symtabfile, volno); } first = lowerbnd(ROOTINO); curvol = volno; while (curfile.ino < maxino) { first = lowerbnd(first); /* * If the next available file is not the one which we * expect then we have missed one or more files. Since * we do not request files that were not on the tape, * the lost files must have been due to a tape read error, * or a file that was removed while the dump was in progress. * * The loop will terminate with first == maxino, if not * sooner. Due to the e_flags manipulation, lowerbnd() * will never return its argument. */ while (first < curfile.ino) { ep = lookupino(first); if (ep == NIL) { (void) fprintf(stderr, gettext("%d: bad first\n"), first); done(1); } (void) fprintf(stderr, gettext("%s: not found on volume\n"), myname(ep)); /* LINTED: result fits into a short */ ep->e_flags &= ~(NEW|EXTRACT); first = lowerbnd(first); } /* * If we find files on the tape that have no corresponding * directory entries, then we must have found a file that * was created while the dump was in progress. Since we have * no name for it, we discard it knowing that it will be * on the next incremental tape. */ if (first != curfile.ino) { (void) fprintf(stderr, gettext("expected next file %d, got %d\n"), first, curfile.ino); skipfile(); goto next; } ep = lookupino(curfile.ino); if (ep == NIL) { (void) fprintf(stderr, gettext("unknown file on volume\n")); done(1); } if ((ep->e_flags & (NEW|EXTRACT)) == 0) badentry(ep, gettext("unexpected file on volume")); /* * If the file is to be extracted, then the old file must * be removed since its type may change from one leaf type * to another (eg "file" to "character special"). But we * also need to preserve any existing extended attributes; * so first rename the file, then move its attributes, then * remove it. */ if ((ep->e_flags & EXTRACT) != 0) { char *sname = savename(ep->e_name); complexcpy(name, myname(ep), MAXCOMPLEXLEN); mktempname(ep); (void) extractfile(name); movexattrs(myname(ep), name); removeleaf(ep); freename(ep->e_name); ep->e_name = sname; ep->e_namlen = strlen(ep->e_name); /* LINTED: result fits into a short */ ep->e_flags &= ~REMOVED; } else { (void) extractfile(myname(ep)); } /* LINTED: result fits into a short */ ep->e_flags &= ~(NEW|EXTRACT); /* * We checkpoint the restore after every tape reel, so * as to simplify the amount of work required by the * 'R' command. */ next: if (curvol != volno) { dumpsymtable(symtabfile, volno); skipmaps(); curvol = volno; } } } /* * This is the routine used to extract files for the 'x' and 'i' commands. * Efficiently extract a subset of the files on a tape. */ void createfiles(void) { ino_t first, next, last; struct entry *ep; int curvol, nextvol; vprintf(stdout, gettext("Extract requested files\n")); first = lowerbnd(ROOTINO); last = upperbnd(maxino - 1); nextvol = volnumber(first); if (nextvol == 0) { curfile.action = SKIP; getvol(1); skipmaps(); skipdirs(); } for (;;) { first = lowerbnd(first); last = upperbnd(last); /* * Check to see if any files remain to be extracted */ if (first > last) return; /* * If a map of inode numbers to tape volumes is * available, then select the next volume to be read. */ if (nextvol > 0) { nextvol = volnumber(first); if (nextvol != volno) { curfile.action = UNKNOWN; getvol(nextvol); skipmaps(); } } /* * Reject any volumes with inodes greater than * the last one needed. This will only be true * if the above code has not selected a volume. */ while (curfile.ino > last) { curfile.action = SKIP; getvol(0); skipmaps(); skipdirs(); } /* * Decide on the next inode needed. * Skip across the inodes until it is found * or an out of order volume change is encountered */ next = lowerbnd(curfile.ino); do { curvol = volno; while (next > curfile.ino && volno == curvol) skipfile(); skipmaps(); skipdirs(); } while (volno == curvol + 1); /* * If volume change out of order occurred the * current state must be recalculated */ if (volno != curvol) continue; /* * If the current inode is greater than the one we were * looking for then we missed the one we were looking for. * Since we only attempt to extract files listed in the * dump map, the lost files must have been due to a tape * read error, or a file that was removed while the dump * was in progress. Thus we report all requested files * between the one we were looking for, and the one we * found as missing, and delete their request flags. */ while (next < curfile.ino) { ep = lookupino(next); if (ep == NIL) { (void) fprintf(stderr, gettext("corrupted symbol table\n")); done(1); } (void) fprintf(stderr, gettext("%s: not found on volume\n"), myname(ep)); /* LINTED: result fits into a short */ ep->e_flags &= ~NEW; next = lowerbnd(next); } /* * The current inode is the one that we are looking for, * so extract it per its requested name. */ if (next == curfile.ino && next <= last) { ep = lookupino(next); if (ep == NIL) { (void) fprintf(stderr, gettext("corrupted symbol table\n")); done(1); } (void) extractfile(myname(ep)); /* LINTED: result fits into a short */ ep->e_flags &= ~NEW; if (volno != curvol) skipmaps(); } } } /* * Add links. */ void createlinks(void) { struct entry *np, *ep; ino_t i; int dfd; char *to, *from; int saverr; vprintf(stdout, gettext("Add links\n")); for (i = ROOTINO; i < maxino; i++) { ep = lookupino(i); if (ep == NIL) continue; to = savename(myname(ep)); for (np = ep->e_links; np != NIL; np = np->e_links) { if ((np->e_flags & NEW) == 0) continue; resolve(myname(np), &dfd, &from); if (dfd != AT_FDCWD) { if (fchdir(dfd) < 0) { saverr = errno; (void) fprintf(stderr, gettext( "%s->%s: link failed: %s\n"), from, to, strerror(saverr)); (void) close(dfd); continue; } } if (ep->e_type == NODE) { (void) lf_linkit(to, from, SYMLINK); } else { (void) lf_linkit(to, from, HARDLINK); } /* LINTED: result fits into a short */ np->e_flags &= ~NEW; if (dfd != AT_FDCWD) { fchdir(savepwd); (void) close(dfd); } } freename(to); } } /* * Check the symbol table. * We do this to insure that all the requested work was done, and * that no temporary names remain. */ void checkrestore(void) { struct entry *ep; ino_t i; vprintf(stdout, gettext("Check the symbol table.\n")); for (i = ROOTINO; i < maxino; i++) { for (ep = lookupino(i); ep != NIL; ep = ep->e_links) { /* LINTED: result fits into a short */ ep->e_flags &= ~KEEP; if (ep->e_type == NODE) { /* LINTED: result fits into a short */ ep->e_flags &= ~(NEW|EXISTED); } if ((ep->e_flags & ~(XATTR|XATTRROOT)) != 0) badentry(ep, gettext("incomplete operations")); } } } /* * Compare with the directory structure on the tape * A paranoid check that things are as they should be. */ long verifyfile(char *name, ino_t ino, int type) { struct entry *np, *ep; long descend = GOOD; ep = lookupname(name); if (ep == NIL) { (void) fprintf(stderr, gettext("Warning: missing name %s\n"), name); return (FAIL); } np = lookupino(ino); if (np != ep) descend = FAIL; for (; np != NIL; np = np->e_links) if (np == ep) break; if (np == NIL) { (void) fprintf(stderr, gettext("missing inumber %d\n"), ino); done(1); } if (ep->e_type == LEAF && type != LEAF) badentry(ep, gettext("type should be LEAF")); return (descend); } /* * This routine does not actually remove any attribute files, it * just removes entries from the symbol table. The attribute files * themselves are assumed to be removed automatically when the * parent file is removed. */ static void removexattrs(struct entry *ep) { struct entry *np = ep; if (ep == NIL) return; for (np = ep->e_entries; np != NIL; np = np->e_sibling) { if (np->e_type == NODE) { removexattrs(np); } else { np->e_flags |= REMOVED; freeentry(np); } } ep->e_flags |= REMOVED; freeentry(ep); } /* * Move all the extended attributes associated with orig to * the file named by the second argument (targ). */ static void movexattrs(char *orig, char *targ) { char *to, *from; int fromfd, fromdir, tofd, todir, tfd; DIR *dirp = NULL; struct dirent *dp = NULL; fromfd = tofd = fromdir = todir = tfd = -1; resolve(orig, &tfd, &from); if (tfd == AT_FDCWD && pathconf(orig, _PC_XATTR_EXISTS) != 1) { /* no attributes to move */ return; } if ((fromfd = openat64(tfd, from, O_RDONLY|O_NONBLOCK)) == -1) { fprintf(stderr, gettext("%s: cannot move attributes: "), from); perror(""); if (tfd != AT_FDCWD) (void) close(tfd); goto out; } if (fpathconf(fromfd, _PC_XATTR_EXISTS) != 1) { /* no attributes to move */ if (tfd != AT_FDCWD) (void) close(tfd); goto out; } if ((fromdir = openat64(fromfd, ".", O_RDONLY|O_NONBLOCK|O_XATTR)) == -1) { fprintf(stderr, gettext("%s: cannot access attributes: "), from); perror(""); if (tfd != AT_FDCWD) (void) close(tfd); goto out; } if (tfd != AT_FDCWD) (void) close(tfd); resolve(targ, &tfd, &to); if ((tofd = openat64(tfd, to, O_RDONLY|O_NONBLOCK)) == -1 || (todir = openat64(tofd, ".", O_RDONLY|O_NONBLOCK|O_XATTR)) == -1) { fprintf(stderr, gettext("%s: cannot create attributes: "), to); perror(""); goto out; } if (tfd != AT_FDCWD) (void) close(tfd); (void) close(tofd); if ((tfd = dup(fromdir)) == -1 || (dirp = fdopendir(tfd)) == NULL) { fprintf(stderr, gettext("%s: cannot allocate DIR structure to attribute " "directory: "), from); perror(""); if (tfd != -1) (void) close(tfd); goto out; } while ((dp = readdir(dirp)) != NULL) { if ((dp->d_name[0] == '.' && dp->d_name[1] == '\0') || (dp->d_name[0] == '.' && dp->d_name[1] == '.' && dp->d_name[2] == '\0')) continue; if ((renameat(fromdir, dp->d_name, todir, dp->d_name)) == -1) { fprintf(stderr, gettext("%s: cannot move attribute %s: "), from, dp->d_name); goto out; } } out: if (fromfd != -1) (void) close(fromfd); if (tofd != -1) (void) close(tofd); if (dirp != NULL) (void) closedir(dirp); if (fromdir != -1) (void) close(fromdir); if (todir != -1) (void) close(todir); } /* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _RESTORE_H #define _RESTORE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define ROOTINO UFSROOTINO #define SUPPORTS_MTB_TAPE_FORMAT #include #include #include /* * Flags */ extern int cvtflag; /* convert from old to new tape format */ extern int bflag; /* set input block size */ extern int dflag; /* print out debugging info */ extern int hflag; /* restore heirarchies */ extern int mflag; /* restore by name instead of inode number */ extern int vflag; /* print out actions taken */ extern int yflag; /* always try to recover from tape errors */ extern int paginating; /* paginate bulk interactive output */ extern int offline; /* take tape offline when closing */ extern int autoload; /* wait for tape to autoload; implies offline */ /* * Global variables */ extern int autoload_tries; /* number of times to check on autoload */ extern int autoload_period; /* seconds, tries*period = total wait time */ extern struct byteorder_ctx *byteorder; extern char *progname; /* our name */ extern char *dumpmap; /* map of inodes on this dump tape */ extern char *clrimap; /* map of inodes to be deleted */ extern char *c_label; /* label we expect to see on the tape */ extern ino_t maxino; /* highest numbered inode in this file system */ extern long dumpnum; /* location of the dump on this tape */ extern int volno; /* current volume being read */ extern uint_t ntrec; /* number of tp_bsize records per tape block */ extern uint_t saved_ntrec; /* number of tp_bsize records per tape block */ extern ssize_t tape_rec_size; /* tape record size (tp_bsize * ntrec) */ extern time_t dumptime; /* time that this dump begins */ extern time_t dumpdate; /* time that this dump was made */ extern char command; /* opration being performed */ extern FILE *terminal; /* file descriptor for the terminal input */ extern char *tmpdir; /* where to put the rst{dir,mode}... files */ extern char *pager_catenated; /* pager command and args */ extern char **pager_vector; /* pager_catenated split up for execve() */ extern int pager_len; /* # elements in pager_vector; includes NULL */ extern int inattrspace; /* true if currently scanning attribute space */ extern int savepwd; /* this is where restore is running from */ /* * Each file in the file system is described by one of these entries * Note that the e_next field is used by the symbol table hash lists * and then reused by the remove code after the entry is removed from * the symbol table. */ struct entry { char *e_name; /* the current name of this entry */ ushort_t e_namlen; /* length of this name */ char e_type; /* type of this entry, see below */ short e_flags; /* status flags, see below */ ino_t e_ino; /* inode number in previous file sys */ long e_index; /* unique index (for dumpped table) */ struct entry *e_parent; /* pointer to parent directory (..) */ struct entry *e_sibling; /* next element in this directory (.) */ struct entry *e_links; /* hard links to this inode */ struct entry *e_entries; /* for directories, their entries */ struct entry *e_xattrs; /* pointer to extended attribute root */ struct entry *e_next; /* hash chain list and removelist */ }; /* types */ #define LEAF 1 /* non-directory entry */ #define NODE 2 /* directory entry */ #define LINK 4 /* synthesized type, stripped by addentry */ #define ROOT 8 /* synthesized type, stripped by addentry */ /* flags */ #define EXTRACT 0x0001 /* entry is to be replaced from the tape */ #define NEW 0x0002 /* a new entry to be extracted */ #define KEEP 0x0004 /* entry is not to change */ #define REMOVED 0x0010 /* entry has been removed */ #define TMPNAME 0x0020 /* entry has been given a temporary name */ #define EXISTED 0x0040 /* directory already existed during extract */ #define XATTR 0x0080 /* file belongs in an attribute tree */ #define XATTRROOT 0x0100 /* directory is root of an attribute tree */ /* * functions defined on entry structs */ extern struct entry *lookupino(ino_t); extern struct entry *lookupname(char *); extern struct entry *addentry(char *, ino_t, int); extern void deleteino(ino_t); extern char *myname(struct entry *); extern void freeentry(struct entry *); extern void moveentry(struct entry *, char *); extern char *savename(char *); extern void freename(char *); extern void dumpsymtable(char *, int); extern void initsymtable(char *); extern void mktempname(struct entry *); extern char *gentempname(struct entry *); extern void newnode(struct entry *); extern void removenode(struct entry *); extern void removeleaf(struct entry *); extern ino_t lowerbnd(ino_t); extern ino_t upperbnd(ino_t); extern void badentry(struct entry *, char *); extern char *flagvalues(struct entry *); extern ino_t dirlookup(char *); #define NIL ((struct entry *)(0)) /* * Definitions for library routines operating on directories. * These definitions are used only for reading fake directory * entries from restore's temporary file "restoresymtable" * These have little to do with real directory entries. */ #if !defined(DEV_BSIZE) #define DEV_BSIZE 512 #endif #define DIRBLKSIZ DEV_BSIZE typedef struct _rstdirdesc { int dd_fd; int dd_refcnt; /* so rst_{open,close}dir() avoid leaking memory */ off64_t dd_loc; off64_t dd_size; char dd_buf[DIRBLKSIZ]; } RST_DIR; /* * Constants associated with entry structs */ #define HARDLINK 1 #define SYMLINK 2 #define TMPHDR "RSTTMP" /* * The entry describes the next file available on the tape */ extern struct context { char *name; /* name of file */ ino_t ino; /* inumber of file */ struct dinode *dip; /* pointer to inode */ int action; /* action being taken on this file */ int ts; /* TS_* type of tape record */ } curfile; /* actions */ #define USING 1 /* extracting from the tape */ #define SKIP 2 /* skipping */ #define UNKNOWN 3 /* disposition or starting point is unknown */ /* * Structure and routines associated with listing directories * and expanding meta-characters in pathnames. */ struct afile { ino_t fnum; /* inode number of file */ char *fname; /* file name */ short fflags; /* extraction flags, if any */ char ftype; /* file type, e.g. LEAF or NODE */ }; struct arglist { struct afile *head; /* start of argument list */ struct afile *last; /* end of argument list */ struct afile *base; /* current list arena */ int nent; /* maximum size of list */ char *cmd; /* the current command */ }; /* * Other exported routines */ #ifdef __STDC__ extern int mkentry(char *, ino_t, struct arglist *); extern int expand(char *, int, struct arglist *); extern ino_t psearch(char *); extern void metaget(char **data, size_t *size); extern void metaproc(char *, char *, size_t); extern long listfile(char *, ino_t, int); extern long addfile(char *, ino_t, int); extern long deletefile(char *, ino_t, int); extern long nodeupdates(char *, ino_t, int); extern long verifyfile(char *, ino_t, int); extern void extractdirs(int genmode); extern void skipdirs(void); extern void treescan(char *, ino_t, long (*)(char *, ino_t, int)); extern RST_DIR *rst_opendir(char *); extern void rst_closedir(RST_DIR *); extern struct direct *rst_readdir(RST_DIR *); extern void setdirmodes(void); extern int genliteraldir(char *, ino_t); extern int inodetype(ino_t); extern void done(int) __NORETURN; extern void runcmdshell(void); extern void canon(char *, char *, size_t); extern void onintr(int); extern void removeoldleaves(void); extern void findunreflinks(void); extern void removeoldnodes(void); extern void createleaves(char *); extern void createfiles(void); extern void createlinks(void); extern void checkrestore(void); extern void setinput(char *, char *); extern void newtapebuf(size_t); extern void setup(void); extern void setupR(void); extern void getvol(int); extern void printdumpinfo(void); extern int extractfile(char *); extern void skipmaps(void); extern void skipfile(void); extern void getfile(void (*)(char *, size_t), void (*)(char *, size_t)); extern void null(char *, size_t); extern void findtapeblksize(int); extern void flsht(void); extern void closemt(int); extern int readhdr(struct s_spcl *); extern int gethead(struct s_spcl *); extern int volnumber(ino_t); extern void findinode(struct s_spcl *); extern void pathcheck(char *); extern void renameit(char *, char *); extern int linkit(char *, char *, int); extern int lf_linkit(char *, char *, int); extern int reply(char *); /*PRINTFLIKE1*/ extern void panic(const char *, ...); extern char *lctime(time_t *); extern int safe_open(int, const char *file, int mode, int perms); extern FILE *safe_fopen(const char *filename, const char *smode, int perms); extern void reset_dump(void); extern void get_next_device(void); extern void initpagercmd(void); extern void resolve(char *, int *, char **); extern int complexcopy(char *, char *, int); #else /* !STDC */ extern int mkentry(); extern int expand(); extern ino_t psearch(); extern void metaget(); extern void metaproc(); extern long listfile(); extern long addfile(); extern long deletefile(); extern long nodeupdates(); extern long verifyfile(); extern void extractdirs(); extern void skipdirs(); extern void treescan(); extern RST_DIR *rst_opendir(); extern void rst_closedir(); extern struct direct *rst_readdir(); extern void setdirmodes(); extern int genliteraldir(); extern int inodetype(); extern void done(); extern void runcmdshell(); extern void canon(); extern void onintr(); extern void removeoldleaves(); extern void findunreflinks(); extern void removeoldnodes(); extern void createleaves(); extern void createfiles(); extern void createlinks(); extern void checkrestore(); extern void setinput(); extern void newtapebuf(); extern void setup(); extern void setupR(); extern void getvol(); extern void printdumpinfo(); extern int extractfile(); extern void skipmaps(); extern void skipfile(); extern void getfile(); extern void null(); extern void findtapeblksize(); extern void flsht(); extern void closemt(); extern int readhdr(); extern int gethead(); extern int volnumber(); extern void findinode(); extern void pathcheck(); extern void renameit(); extern int linkit(); extern int lf_linkit(); extern int reply(); extern void panic(); extern char *lctime(); extern int safe_open(); extern FILE *safe_fopen(); extern void reset_dump(); extern void get_next_device(); extern void initpagercmd(); extern void resolve(); extern int complexcopy(); #endif /* STDC */ /* * Useful macros */ #define MWORD(m, i) ((m)[(ino_t)((i)-1)/NBBY]) #define MBIT(i) (1<<((ino_t)((i)-1)%NBBY)) #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)) /* * Macro used to get to the last segment of a complex string */ #define LASTPART(s) {int len = strlen(s)+1;\ while (s[len] != '\0')\ {s += len; len = strlen(s)+1; }\ } /* * Define maximum length of complex string. For now we use * MAXPATHLEN * 2 since recursion is not (yet) supported. * (add 3 for the 3 NULL characters in a two-part path) * Note that each component of a complex string is still * limited to MAXPATHLEN length. */ #define MAXCOMPLEXLEN (MAXPATHLEN*2 + 3) /* * 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)) /* * Defines used by findtapeblksize() */ #define TAPE_FILE 0 #define ARCHIVE_FILE 1 #undef setjmp #define setjmp(b) sigsetjmp((b), 1) #define longjmp siglongjmp #define jmp_buf sigjmp_buf #define chown lchown /* * Defaults */ #define TAPE "/dev/rmt/0b" /* default tape device */ #define RESTORESYMTABLE "./restoresymtable" #define dprintf if (dflag) (void) fprintf #define vprintf if (vflag) (void) fprintf #define GOOD 1 #define FAIL 0 #define ALLOW_OFFLINE 0 #define FORCE_OFFLINE 1 /* offline drive for autoload */ #define DEF_PAGER "/usr/bin/more" #ifdef __cplusplus } #endif #endif /* _RESTORE_H */ /* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright (c) 1996,1998,2001 by Sun Microsystems, Inc. * All rights reserved. */ /* * These routines maintain the symbol table which tracks the state * of the file system being restored. They provide lookup by either * name or inode number. They also provide for creation, deletion, * and renaming of entries. Because of the dynamic nature of pathnames, * names should not be saved, but always constructed just before they * are needed, by calling "myname". */ #include "restore.h" #include /* * The following variables define the inode symbol table. * The primary hash table is dynamically allocated based on * the number of inodes in the file system (maxino), scaled by * HASHFACTOR. The variable "entry" points to the hash table; * the variable "entrytblsize" indicates its size (in entries). */ #define HASHFACTOR 5 static struct entry **entry; static uint_t entrytblsize; static void addino(ino_t, struct entry *); static struct entry *lookupparent(char *); static void removeentry(struct entry *); /* * Look up an entry by inode number */ struct entry * lookupino(ino_t inum) { struct entry *ep; if (inum < ROOTINO || inum >= maxino) return (NIL); for (ep = entry[inum % entrytblsize]; ep != NIL; ep = ep->e_next) if (ep->e_ino == inum) return (ep); return (NIL); } /* * We now ignore inodes that are out of range. This * allows us to attempt to proceed in the face of * a corrupted archive, albeit with future complaints * about failed inode lookups. We only complain once * about range problems, to avoid irritating the user * without providing any useful information. Failed * lookups have the bogus name, which is useful, so * they always happen. */ static int complained_about_range = 0; /* * Add an entry into the entry table */ static void addino(ino_t inum, struct entry *np) { struct entry **epp; if (inum < ROOTINO || inum >= maxino) { if (!complained_about_range) { panic(gettext("%s: out of range %d\n"), "addino", inum); complained_about_range = 1; } return; } epp = &entry[inum % entrytblsize]; np->e_ino = inum; np->e_next = *epp; *epp = np; if (dflag) for (np = np->e_next; np != NIL; np = np->e_next) if (np->e_ino == inum) badentry(np, gettext("duplicate inum")); } /* * Delete an entry from the entry table. We assume our caller * arranges for the necessary memory reclamation, if needed. */ void deleteino(ino_t inum) { struct entry *next; struct entry **prev; if (inum < ROOTINO || inum >= maxino) { if (!complained_about_range) { panic(gettext("%s: out of range %d\n"), "deleteino", inum); complained_about_range = 1; } return; } prev = &entry[inum % entrytblsize]; for (next = *prev; next != NIL; next = next->e_next) { if (next->e_ino == inum) { next->e_ino = 0; *prev = next->e_next; return; } prev = &next->e_next; } } /* * Look up an entry by name. * NOTE: this function handles "complex" pathnames (as returned * by myname()) for extended file attributes. The name string * provided to this function should be terminated with *two* * NULL characters. */ struct entry * lookupname(char *name) { struct entry *ep; char *np, *cp; char buf[MAXPATHLEN]; if (strlen(name) > (sizeof (buf) - 1)) { (void) fprintf(stderr, gettext("%s: ignoring too-long name\n"), "lookupname"); return (NIL); } cp = name; for (ep = lookupino(ROOTINO); ep != NIL; ep = ep->e_entries) { np = buf; while (*cp != '/' && *cp != '\0') *np++ = *cp++; *np = '\0'; for (; ep != NIL; ep = ep->e_sibling) if (strcmp(ep->e_name, buf) == 0) break; if (*cp++ == '\0') { if (*cp != '\0') { ep = ep->e_xattrs; /* * skip over the "./" prefix on all * extended attribute paths */ cp += 2; } if (*cp == '\0') return (ep); } if (ep == NIL) break; } return (NIL); } /* * Look up the parent of a pathname. This routine accepts complex * names so the provided name argument must terminate with two NULLs. */ static struct entry * lookupparent(char *name) { struct entry *ep; char *tailindex, savechar, *lastpart; int xattrparent = 0; /* find the last component of the complex name */ lastpart = name; LASTPART(lastpart); tailindex = strrchr(lastpart, '/'); if (tailindex == 0) { if (lastpart == name) return (NIL); /* * tailindex normaly points to the '/' character * dividing the path, but in the case of an extended * attribute transition it will point to the NULL * separator in front of the attribute path. */ tailindex = lastpart - 1; xattrparent = 1; } else { *tailindex = '\0'; } savechar = *(tailindex+1); *(tailindex+1) = '\0'; ep = lookupname(name); if (ep != NIL && !xattrparent && ep->e_type != NODE) panic(gettext("%s is not a directory\n"), name); if (!xattrparent) *tailindex = '/'; *(tailindex+1) = savechar; return (ep); } /* * Determine the current pathname of a node or leaf. * The returned pathname will be multiple strings with NULL separators: * * .//entry\0/attrentry\0/...\0\0 * ^ ^ ^ ^ * return pntr entry attr recursive attr terminator * * Guaranteed to return a name that fits within MAXCOMPLEXLEN and is * terminated with two NULLs. */ char * myname(struct entry *ep) { char *cp; struct entry *root = lookupino(ROOTINO); static char namebuf[MAXCOMPLEXLEN]; cp = &namebuf[MAXCOMPLEXLEN - 3]; *(cp + 1) = '\0'; *(cp + 2) = '\0'; while (cp > &namebuf[ep->e_namlen]) { cp -= ep->e_namlen; bcopy(ep->e_name, cp, (size_t)ep->e_namlen); if (ep == root) return (cp); if (ep->e_flags & XATTRROOT) *(--cp) = '\0'; else *(--cp) = '/'; ep = ep->e_parent; } panic(gettext("%s%s: pathname too long\n"), "...", cp); return (cp); } /* * Unused symbol table entries are linked together on a freelist * headed by the following pointer. */ static struct entry *freelist = NIL; /* * add an entry to the symbol table */ struct entry * addentry(char *name, ino_t inum, int type) { struct entry *np, *ep; char *cp; if (freelist != NIL) { np = freelist; freelist = np->e_next; (void) bzero((char *)np, (size_t)sizeof (*np)); } else { np = (struct entry *)calloc(1, sizeof (*np)); if (np == NIL) { (void) fprintf(stderr, gettext("no memory to extend symbol table\n")); done(1); } } np->e_type = type & ~(LINK|ROOT); if (inattrspace) np->e_flags |= XATTR; ep = lookupparent(name); if (ep == NIL) { if (inum != ROOTINO || lookupino(ROOTINO) != NIL) { (void) fprintf(stderr, gettext( "%s: bad name %s\n"), "addentry", name); assert(0); done(1); } np->e_name = savename(name); /* LINTED: savename guarantees that strlen fits in e_namlen */ np->e_namlen = strlen(name); np->e_parent = np; addino(ROOTINO, np); return (np); } if (np->e_flags & XATTR) { /* * skip to the last part of the complex string: it * containes the extended attribute file name. */ LASTPART(name); } cp = strrchr(name, '/'); if (cp == NULL) cp = name; else cp++; np->e_name = savename(cp); /* LINTED: savename guarantees that strlen will fit */ np->e_namlen = strlen(np->e_name); np->e_parent = ep; /* * Extended attribute root directories must be linked to their * "parents" via the e_xattrs field. Other entries are simply * added to their parent directories e_entries list. */ if ((type & ROOT) && (np->e_flags & XATTR)) { /* link this extended attribute root dir to its "parent" */ ep->e_xattrs = np; } else { /* add this entry to the entry list of the parent dir */ np->e_sibling = ep->e_entries; ep->e_entries = np; } if (type & LINK) { ep = lookupino(inum); if (ep == NIL) { /* XXX just bail on this one and continue? */ (void) fprintf(stderr, gettext("link to non-existent name\n")); done(1); } np->e_ino = inum; np->e_links = ep->e_links; ep->e_links = np; } else if (inum != 0) { ep = lookupino(inum); if (ep != NIL) panic(gettext("duplicate entry\n")); else addino(inum, np); } return (np); } /* * delete an entry from the symbol table */ void freeentry(struct entry *ep) { struct entry *np; ino_t inum; if ((ep->e_flags & REMOVED) == 0) badentry(ep, gettext("not marked REMOVED")); if (ep->e_type == NODE) { if (ep->e_links != NIL) badentry(ep, gettext("freeing referenced directory")); if (ep->e_entries != NIL) badentry(ep, gettext("freeing non-empty directory")); } if (ep->e_ino != 0) { np = lookupino(ep->e_ino); if (np == NIL) badentry(ep, gettext("lookupino failed")); if (np == ep) { inum = ep->e_ino; deleteino(inum); if (ep->e_links != NIL) addino(inum, ep->e_links); } else { for (; np != NIL; np = np->e_links) { if (np->e_links == ep) { np->e_links = ep->e_links; break; } } if (np == NIL) badentry(ep, gettext("link not found")); } } removeentry(ep); freename(ep->e_name); ep->e_next = freelist; freelist = ep; } /* * Relocate an entry in the tree structure */ void moveentry(struct entry *ep, char *newname) { struct entry *np; char *cp; np = lookupparent(newname); if (np == NIL) badentry(ep, gettext("cannot move ROOT")); if (np != ep->e_parent) { removeentry(ep); ep->e_parent = np; ep->e_sibling = np->e_entries; np->e_entries = ep; } /* find the last component of the complex name */ LASTPART(newname); cp = strrchr(newname, '/') + 1; if (cp == (char *)1) cp = newname; freename(ep->e_name); ep->e_name = savename(cp); /* LINTED: savename guarantees that strlen will fit */ ep->e_namlen = strlen(cp); if (strcmp(gentempname(ep), ep->e_name) == 0) { /* LINTED: result fits in a short */ ep->e_flags |= TMPNAME; } else { /* LINTED: result fits in a short */ ep->e_flags &= ~TMPNAME; } } /* * Remove an entry in the tree structure */ static void removeentry(struct entry *ep) { struct entry *np; np = ep->e_parent; if (ep->e_flags & XATTRROOT) { if (np->e_xattrs == ep) np->e_xattrs = NIL; else badentry(ep, gettext( "parent does not reference this xattr tree")); } else if (np->e_entries == ep) { np->e_entries = ep->e_sibling; } else { for (np = np->e_entries; np != NIL; np = np->e_sibling) { if (np->e_sibling == ep) { np->e_sibling = ep->e_sibling; break; } } if (np == NIL) badentry(ep, gettext( "cannot find entry in parent list")); } } /* * Table of unused string entries, sorted by length. * * Entries are allocated in STRTBLINCR sized pieces so that names * of similar lengths can use the same entry. The value of STRTBLINCR * is chosen so that every entry has at least enough space to hold * a "struct strtbl" header. Thus every entry can be linked onto an * apprpriate free list. * * NB. The macro "allocsize" below assumes that "struct strhdr" * has a size that is a power of two. Also, an extra byte is * allocated for the string to provide space for the two NULL * string terminator required for extended attribute paths. */ struct strhdr { struct strhdr *next; }; #define STRTBLINCR ((size_t)sizeof (struct strhdr)) #define allocsize(size) (((size) + 2 + STRTBLINCR - 1) & ~(STRTBLINCR - 1)) static struct strhdr strtblhdr[allocsize(MAXCOMPLEXLEN) / STRTBLINCR]; /* * Allocate space for a name. It first looks to see if it already * has an appropriate sized entry, and if not allocates a new one. */ char * savename(char *name) { struct strhdr *np; size_t len, as; char *cp; if (name == NULL) { (void) fprintf(stderr, gettext("bad name\n")); done(1); } len = strlen(name); if (len > MAXPATHLEN) { (void) fprintf(stderr, gettext("name too long\n")); done(1); } as = allocsize(len); np = strtblhdr[as / STRTBLINCR].next; if (np != NULL) { strtblhdr[as / STRTBLINCR].next = np->next; cp = (char *)np; } else { /* Note that allocsize() adds 2 for the trailing \0s */ cp = malloc(as); if (cp == NULL) { (void) fprintf(stderr, gettext("no space for string table\n")); done(1); } } (void) strcpy(cp, name); /* add an extra null for complex (attribute) name support */ cp[len+1] = '\0'; return (cp); } /* * Free space for a name. The resulting entry is linked onto the * appropriate free list. */ void freename(char *name) { struct strhdr *tp, *np; /* NULL case should never happen, but might as well be careful */ if (name != NULL) { tp = &strtblhdr[allocsize(strlen(name)) / STRTBLINCR]; /*LINTED [name points to at least sizeof (struct strhdr)]*/ np = (struct strhdr *)name; np->next = tp->next; tp->next = np; } } /* * Useful quantities placed at the end of a dumped symbol table. */ struct symtableheader { int volno; uint_t stringsize; uint_t entrytblsize; time_t dumptime; time_t dumpdate; ino_t maxino; uint_t ntrec; }; /* * dump a snapshot of the symbol table */ void dumpsymtable(char *filename, int checkpt) { struct entry *ep, *tep; ino_t i; struct entry temp, *tentry; int mynum = 1; uint_t stroff; FILE *fp; struct symtableheader hdr; vprintf(stdout, gettext("Check pointing the restore\n")); if ((fp = safe_fopen(filename, "w", 0600)) == (FILE *)NULL) { perror("fopen"); (void) fprintf(stderr, gettext("cannot create save file %s for symbol table\n"), filename); done(1); } clearerr(fp); /* * Assign an index to each entry * Write out the string entries */ for (i = ROOTINO; i < maxino; i++) { for (ep = lookupino(i); ep != NIL; ep = ep->e_links) { ep->e_index = mynum++; (void) fwrite(ep->e_name, sizeof (ep->e_name[0]), (size_t)allocsize(ep->e_namlen), fp); } } /* * Convert e_name pointers to offsets, other pointers * to indices, and output */ tep = &temp; stroff = 0; for (i = ROOTINO; !ferror(fp) && i < maxino; i++) { for (ep = lookupino(i); !ferror(fp) && ep != NIL; ep = ep->e_links) { bcopy((char *)ep, (char *)tep, sizeof (*tep)); /* LINTED: type pun ok */ tep->e_name = (char *)stroff; stroff += allocsize(ep->e_namlen); tep->e_parent = (struct entry *)ep->e_parent->e_index; if (ep->e_links != NIL) tep->e_links = (struct entry *)ep->e_links->e_index; if (ep->e_sibling != NIL) tep->e_sibling = (struct entry *)ep->e_sibling->e_index; if (ep->e_entries != NIL) tep->e_entries = (struct entry *)ep->e_entries->e_index; if (ep->e_xattrs != NIL) tep->e_xattrs = (struct entry *)ep->e_xattrs->e_index; if (ep->e_next != NIL) tep->e_next = (struct entry *)ep->e_next->e_index; (void) fwrite((char *)tep, sizeof (*tep), 1, fp); } } /* * Convert entry pointers to indices, and output */ for (i = 0; !ferror(fp) && i < (ino_t)entrytblsize; i++) { if (entry[i] == NIL) tentry = NIL; else tentry = (struct entry *)entry[i]->e_index; (void) fwrite((char *)&tentry, sizeof (tentry), 1, fp); } if (!ferror(fp)) { /* Ought to have a checksum or magic number */ hdr.volno = checkpt; hdr.maxino = maxino; hdr.entrytblsize = entrytblsize; hdr.stringsize = stroff; hdr.dumptime = dumptime; hdr.dumpdate = dumpdate; hdr.ntrec = ntrec; (void) fwrite((char *)&hdr, sizeof (hdr), 1, fp); } if (ferror(fp)) { perror("fwrite"); panic(gettext("output error to file %s writing symbol table\n"), filename); } (void) fclose(fp); } /* * Initialize a symbol table from a file */ void initsymtable(char *filename) { char *base; off64_t tblsize; struct entry *ep; struct entry *baseep, *lep; struct symtableheader hdr; struct stat64 stbuf; uint_t i; int fd; vprintf(stdout, gettext("Initialize symbol table.\n")); if (filename == NULL) { if ((maxino / HASHFACTOR) > UINT_MAX) { (void) fprintf(stderr, gettext("file system too large\n")); done(1); } /* LINTED: result fits in entrytblsize */ entrytblsize = maxino / HASHFACTOR; entry = calloc((size_t)entrytblsize, sizeof (*entry)); if (entry == NULL) { (void) fprintf(stderr, gettext("no memory for entry table\n")); done(1); } ep = calloc(1, sizeof (*ep)); if (ep == NULL) { (void) fprintf(stderr, gettext("no memory for entry\n")); done(1); } ep->e_type = NODE; ep->e_name = savename("."); ep->e_namlen = 1; ep->e_parent = ep; addino(ROOTINO, ep); /* LINTED: result fits in a short */ ep->e_flags |= NEW; return; } if ((fd = open(filename, O_RDONLY|O_LARGEFILE)) < 0) { perror("open"); (void) fprintf(stderr, gettext("cannot open symbol table file %s\n"), filename); done(1); } if (fstat64(fd, &stbuf) < 0) { perror("stat"); (void) fprintf(stderr, gettext("cannot stat symbol table file %s\n"), filename); (void) close(fd); done(1); } /* * The symbol table file is too small so say we can't read it. */ if (stbuf.st_size < sizeof (hdr)) { (void) fprintf(stderr, gettext("cannot read symbol table file %s\n"), filename); (void) close(fd); done(1); } tblsize = stbuf.st_size - sizeof (hdr); if (tblsize > ULONG_MAX) { (void) fprintf(stderr, gettext("symbol table file too large\n")); (void) close(fd); done(1); } /* LINTED tblsize fits in a size_t */ base = calloc((size_t)sizeof (char), (size_t)tblsize); if (base == NULL) { (void) fprintf(stderr, gettext("cannot allocate space for symbol table\n")); (void) close(fd); done(1); } /* LINTED tblsize fits in a size_t */ if (read(fd, base, (size_t)tblsize) < 0 || read(fd, (char *)&hdr, sizeof (hdr)) < 0) { perror("read"); (void) fprintf(stderr, gettext("cannot read symbol table file %s\n"), filename); (void) close(fd); done(1); } (void) close(fd); switch (command) { case 'r': case 'M': /* * For normal continuation, insure that we are using * the next incremental tape */ if (hdr.dumpdate != dumptime) { if (hdr.dumpdate < dumptime) (void) fprintf(stderr, gettext( "Incremental volume too low\n")); else (void) fprintf(stderr, gettext( "Incremental volume too high\n")); done(1); } break; case 'R': /* * For restart, insure that we are using the same tape */ curfile.action = SKIP; dumptime = hdr.dumptime; dumpdate = hdr.dumpdate; if (!bflag) newtapebuf(hdr.ntrec); getvol(hdr.volno); break; default: (void) fprintf(stderr, gettext("initsymtable called from command %c\n"), (uchar_t)command); done(1); /*NOTREACHED*/ } maxino = hdr.maxino; entrytblsize = hdr.entrytblsize; /*LINTED [pointer cast alignment]*/ entry = (struct entry **) (base + tblsize - (entrytblsize * sizeof (*entry))); if (((ulong_t)entry % 4) != 0) { (void) fprintf(stderr, gettext("Symbol table file corrupted\n")); done(1); } /*LINTED [rvalue % 4 == 0] */ baseep = (struct entry *) (base + hdr.stringsize - sizeof (*baseep)); if (((ulong_t)baseep % 4) != 0) { (void) fprintf(stderr, gettext("Symbol table file corrupted\n")); done(1); } lep = (struct entry *)entry; for (i = 0; i < entrytblsize; i++) { if (entry[i] == NIL) continue; entry[i] = &baseep[(long)entry[i]]; } for (ep = &baseep[1]; ep < lep; ep++) { ep->e_name = base + (long)ep->e_name; ep->e_parent = &baseep[(long)ep->e_parent]; if (ep->e_sibling != NIL) ep->e_sibling = &baseep[(long)ep->e_sibling]; if (ep->e_links != NIL) ep->e_links = &baseep[(long)ep->e_links]; if (ep->e_entries != NIL) ep->e_entries = &baseep[(long)ep->e_entries]; if (ep->e_xattrs != NIL) ep->e_xattrs = &baseep[(long)ep->e_xattrs]; if (ep->e_next != NIL) ep->e_next = &baseep[(long)ep->e_next]; } } /* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include "restore.h" #include #include #include #include #include #include #include /* for expdev */ #include #include #include #include #define MAXINO 65535 /* KLUDGE */ #define MAXTAPES 128 static size_t fssize = MAXBSIZE; /* preferred size of writes to filesystem */ int mt = -1; static int continuemap = 0; char magtape[BUFSIZ]; int pipein = 0; char *host; /* used in dumprmt.c */ daddr32_t rec_position; static char *archivefile; /* used in metamucil.c */ static int bct; /* block # index into tape record buffer */ static int numtrec; /* # of logical blocks in current tape record */ static char *tbf = NULL; static size_t tbfsize = 0; static int recsread; union u_spcl u_spcl; static union u_spcl endoftapemark; static struct s_spcl dumpinfo; static long blksread; /* # of logical blocks actually read/touched */ static long tapea; /* current logical block # on tape */ static uchar_t tapesread[MAXTAPES]; static jmp_buf restart; static int gettingfile = 0; /* restart has a valid frame */ static int ofile; static char *map, *beginmap; static char *endmap; static char lnkbuf[MAXPATHLEN + 2]; static int pathlen; static int inodeinfo; /* Have starting volume information */ static int hostinfo; /* Have dump host information */ static int autoload_tape(void); static void setdumpnum(void); static void metacheck(struct s_spcl *); static void xtrmeta(char *, size_t); static void metaskip(char *, size_t); static void xtrfile(char *, size_t); static void xtrskip(char *, size_t); static void xtrlnkfile(char *, size_t); static void xtrlnkskip(char *, size_t); static void xtrmap(char *, size_t); static void xtrmapskip(char *, size_t); static void readtape(char *); static int checkvol(struct s_spcl *, int); static void accthdr(struct s_spcl *); static int ishead(struct s_spcl *); static int checktype(struct s_spcl *, int); static void metaset(char *name); /* * Set up an input source */ void setinput(char *source, char *archive) { flsht(); archivefile = archive; if (bflag == 0) { ntrec = ((CARTRIDGETREC > HIGHDENSITYTREC) ? (NTREC > CARTRIDGETREC ? NTREC : CARTRIDGETREC) : (NTREC > HIGHDENSITYTREC ? NTREC : HIGHDENSITYTREC)); saved_ntrec = (ntrec * (tp_bsize/DEV_BSIZE)); } newtapebuf(ntrec); terminal = stdin; if (source == NULL) { /* A can't-happen */ (void) fprintf(stderr, gettext("Internal consistency check failed.\n")); done(1); } if (strchr(source, ':')) { char *tape; host = source; tape = strchr(host, ':'); *tape++ = '\0'; if (strlen(tape) > (sizeof (magtape) - 1)) { (void) fprintf(stderr, gettext("Tape name too long\n")); done(1); } (void) strcpy(magtape, tape); if (rmthost(host, ntrec) == 0) done(1); } else { if (strlen(source) > (sizeof (magtape) - 1)) { (void) fprintf(stderr, gettext("Tape name too long\n")); done(1); } /* Not remote, no need for privileges */ __priv_relinquish(); host = NULL; if (strcmp(source, "-") == 0) { /* * Since input is coming from a pipe we must establish * our own connection to the terminal. */ terminal = fopen("/dev/tty", "r"); if (terminal == NULL) { int saverr = errno; char *msg = gettext("Cannot open(\"/dev/tty\")"); errno = saverr; perror(msg); terminal = fopen("/dev/null", "r"); if (terminal == NULL) { saverr = errno; msg = gettext( "Cannot open(\"/dev/null\")"); errno = saverr; perror(msg); done(1); } } pipein++; if (archive) { (void) fprintf(stderr, gettext( "Cannot specify an archive file when reading from a pipe\n")); done(1); } } (void) strcpy(magtape, source); } } void newtapebuf(size_t size) { size_t nsize; nsize = size * tp_bsize; ntrec = size; if (nsize <= tbfsize) return; if (tbf != NULL) free(tbf); tbf = (char *)malloc(nsize); if (tbf == NULL) { (void) fprintf(stderr, gettext("Cannot allocate space for buffer\n")); done(1); } tbfsize = nsize; } /* * Verify that the tape drive can be accessed and * that it actually is a dump tape. */ void setup(void) { int i, j; int32_t *ip; struct stat stbuf; size_t mapsize; char *syment = RESTORESYMTABLE; vprintf(stdout, gettext("Verify volume and initialize maps\n")); if (archivefile) { mt = open(archivefile, O_RDONLY|O_LARGEFILE); if (mt < 0) { perror(archivefile); done(1); } volno = 0; } else if (host) { if ((mt = rmtopen(magtape, O_RDONLY)) < 0) { perror(magtape); done(1); } volno = 1; } else { if (pipein) mt = 0; else if ((mt = open(magtape, O_RDONLY|O_LARGEFILE)) < 0) { perror(magtape); done(1); } volno = 1; } setdumpnum(); flsht(); if (!pipein && !bflag) if (archivefile) findtapeblksize(ARCHIVE_FILE); else findtapeblksize(TAPE_FILE); if (bflag == 1) { tape_rec_size = saved_ntrec * DEV_BSIZE; } /* * Get the first header. If c_magic is NOT NFS_MAGIC or if * the checksum is in error, it will fail. The magic could then * be either OFS_MAGIC or MTB_MAGIC. If OFS_MAGIC, assume we * have an old dump, and try to convert it. If it is MTB_MAGIC, we * procees this after. */ if ((gethead(&spcl) == FAIL) && (spcl.c_magic != MTB_MAGIC)) { bct--; /* push back this block */ blksread--; tapea--; cvtflag++; if (gethead(&spcl) == FAIL) { (void) fprintf(stderr, gettext("Volume is not in dump format\n")); done(1); } (void) fprintf(stderr, gettext("Converting to new file system format.\n")); } /* * The above gethead will have failed if the magic is * MTB_MAGIC. If that is true, we need to adjust tp_bsize. * We have assumed to this time that tp_bsize was 1024, if * this is a newer dump, get the real tp_bsize from the header, * and recalculate ntrec, numtrec. */ if (spcl.c_magic == MTB_MAGIC) { tp_bsize = spcl.c_tpbsize; if ((tp_bsize % TP_BSIZE_MIN != 0) || (tp_bsize > TP_BSIZE_MAX)) { (void) fprintf(stderr, gettext("Volume is not in dump format\n")); done(1); } ntrec = (tape_rec_size/tp_bsize); numtrec = ntrec; newtapebuf(ntrec); bct--; /* push back this block */ blksread--; tapea--; /* we have to re-do this in case checksum is wrong */ if (gethead(&spcl) == FAIL) { (void) fprintf(stderr, gettext("Volume is not in dump format\n")); done(1); } } if (vflag) byteorder_banner(byteorder, stdout); if (pipein) { endoftapemark.s_spcl.c_magic = cvtflag ? OFS_MAGIC : ((tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC); endoftapemark.s_spcl.c_type = TS_END; /* * include this since the `resync' loop in findinode * expects to find a header with the c_date field * filled in. */ endoftapemark.s_spcl.c_date = spcl.c_date; ip = (int32_t *)&endoftapemark; /*LINTED [assertion always true]*/ assert((sizeof (endoftapemark) % sizeof (int32_t)) == 0); j = sizeof (endoftapemark) / sizeof (int32_t); i = 0; do i += *ip++; while (--j) ; endoftapemark.s_spcl.c_checksum = CHECKSUM - i; } if (vflag && command != 't') printdumpinfo(); dumptime = spcl.c_ddate; dumpdate = spcl.c_date; if (stat(".", &stbuf) < 0) { perror(gettext("cannot stat .")); done(1); } if (stbuf.st_blksize >= tp_bsize && stbuf.st_blksize <= MAXBSIZE) { /* LINTED: value fits in a size_t */ fssize = stbuf.st_blksize; } else { fssize = MAXBSIZE; } if (checkvol(&spcl, 1) == FAIL) { (void) fprintf(stderr, gettext("This is not volume 1 of the dump\n")); done(1); } if (readhdr(&spcl) == FAIL) panic(gettext("no header after volume mark!\n")); findinode(&spcl); /* sets curfile, resyncs the tape if need be */ if (checktype(&spcl, TS_CLRI) == FAIL) { (void) fprintf(stderr, gettext("Cannot find file removal list\n")); done(1); } maxino = (unsigned)((spcl.c_count * tp_bsize * NBBY) + 1); dprintf(stdout, "maxino = %lu\n", maxino); /* * Allocate space for at least MAXINO inodes to allow us * to restore partial dump tapes written before dump was * fixed to write out the entire inode map. */ if (maxino > ULONG_MAX) { (void) fprintf(stderr, gettext("file system too large\n")); done(1); } /* LINTED maxino size-checked above */ mapsize = (size_t)d_howmany(maxino > MAXINO ? maxino : MAXINO, NBBY); beginmap = map = calloc((size_t)1, mapsize); if (map == (char *)NIL) { (void) fprintf(stderr, gettext("no memory for file removal list\n")); done(1); } endmap = map + mapsize; clrimap = map; curfile.action = USING; continuemap = 1; getfile(xtrmap, xtrmapskip); if (MAXINO > maxino) maxino = MAXINO; if (checktype(&spcl, TS_BITS) == FAIL) { /* if we have TS_CLRI then no TS_BITS then a TS_END */ /* then we have an empty dump file */ if (gethead(&spcl) == GOOD && checktype(&spcl, TS_END) == GOOD) { if ((command == 'r') || (command == 'R')) { initsymtable(syment); dumpsymtable(syment, volno); } done(0); } /* otherwise we have an error */ (void) fprintf(stderr, gettext("Cannot find file dump list\n")); done(1); } /* LINTED maxino size-checked above */ mapsize = (size_t)d_howmany(maxino, NBBY); beginmap = map = calloc((size_t)1, mapsize); if (map == (char *)NULL) { (void) fprintf(stderr, gettext("no memory for file dump list\n")); done(1); } endmap = map + mapsize; dumpmap = map; curfile.action = USING; continuemap = 1; getfile(xtrmap, xtrmapskip); continuemap = 0; } /* * Initialize fssize variable for 'R' command to work. */ void setupR(void) { struct stat stbuf; if (stat(".", &stbuf) < 0) { perror(gettext("cannot stat .")); done(1); } if (stbuf.st_blksize >= tp_bsize && stbuf.st_blksize <= MAXBSIZE) { /* LINTED: value fits in a size_t */ fssize = stbuf.st_blksize; } else { fssize = MAXBSIZE; } } /* * Prompt user to load a new dump volume. * "Nextvol" is the next suggested volume to use. * This suggested volume is enforced when doing full * or incremental restores, but can be overrridden by * the user when only extracting a subset of the files. * * first_time is used with archive files and can have 1 of 3 states: * FT_STATE_1 Tape has not been read yet * FT_STATE_2 Tape has been read but not positioned past directory * information * FT_STATE_3 Tape has been read and is reading file information */ #define FT_STATE_1 1 #define FT_STATE_2 2 #define FT_STATE_3 3 void getvol(int nextvol) { int newvol; long savecnt, savetapea, wantnext; long i; union u_spcl tmpspcl; #define tmpbuf tmpspcl.s_spcl char buf[TP_BSIZE_MAX]; static int first_time = FT_STATE_1; if (tbf == NULL) { (void) fprintf(stderr, gettext( "Internal consistency failure in getvol: tbf is NULL\n")); done(1); } if (nextvol == 1) { for (i = 0; i < MAXTAPES; i++) tapesread[i] = 0; gettingfile = 0; } if (pipein) { if (nextvol != 1) panic(gettext("changing volumes on pipe input\n")); if (volno == 1) return; goto gethdr; } savecnt = blksread; /* ignore volume verification tape i/o */ savetapea = tapea; again: if (pipein) done(1); /* pipes do not get a second chance */ if (command == 'R' || command == 'r' || curfile.action != SKIP) { wantnext = 1; newvol = nextvol; } else { wantnext = 0; newvol = 0; } if (autoload) { if ((volno == 1) && (nextvol == 1)) { tapesread[volno-1]++; return; } if (autoload_tape()) { wantnext = 1; newvol = nextvol; goto gethdr; } } while (newvol <= 0) { int n = 0; for (i = 0; i < MAXTAPES; i++) if (tapesread[i]) n++; if (n == 0) { (void) fprintf(stderr, "%s", gettext( "You have not read any volumes yet.\n\ Unless you know which volume your file(s) are on you should start\n\ with the last volume and work towards the first.\n")); } else { (void) fprintf(stderr, gettext("You have read volumes")); (void) strcpy(tbf, ": "); for (i = 0; i < MAXTAPES; i++) if (tapesread[i]) { (void) fprintf(stderr, "%s%ld", tbf, i+1); (void) strcpy(tbf, ", "); } (void) fprintf(stderr, "\n"); } do { (void) fprintf(stderr, gettext("Specify next volume #: ")); (void) fflush(stderr); /* LINTED tbfsize is limited to a few MB */ (void) fgets(tbf, (int)tbfsize, terminal); } while (!feof(terminal) && tbf[0] == '\n'); if (feof(terminal)) done(1); newvol = atoi(tbf); if (newvol <= 0) { (void) fprintf(stderr, gettext( "Volume numbers are positive numerics\n")); } if (newvol > MAXTAPES) { (void) fprintf(stderr, gettext( "This program can only deal with %d volumes\n"), MAXTAPES); newvol = 0; } } if (newvol == volno) { tapesread[volno-1]++; return; } closemt(ALLOW_OFFLINE); /* * XXX: if we are switching devices, we should probably try * the device once without prompting to enable unattended * operation. */ if (host) (void) fprintf(stderr, gettext( "Mount volume %d\nthen enter volume name on host %s (default: %s) "), newvol, host, magtape); else (void) fprintf(stderr, gettext( "Mount volume %d\nthen enter volume name (default: %s) "), newvol, magtape); (void) fflush(stderr); /* LINTED tbfsize is limited to a few MB */ (void) fgets(tbf, (int)tbfsize, terminal); if (feof(terminal)) done(1); /* * XXX We don't allow rotating among tape hosts, just drives. */ if (tbf[0] != '\n') { (void) strncpy(magtape, tbf, sizeof (magtape)); magtape[sizeof (magtape) - 1] = '\0'; /* LINTED unsigned -> signed conversion ok */ i = (int)strlen(magtape); if (magtape[i - 1] == '\n') magtape[i - 1] = '\0'; } if ((host != NULL && (mt = rmtopen(magtape, O_RDONLY)) == -1) || (host == NULL && (mt = open(magtape, O_RDONLY|O_LARGEFILE)) == -1)) { int error = errno; (void) fprintf(stderr, gettext("Cannot open %s: %s\n"), magtape, strerror(error)); volno = -1; goto again; } gethdr: volno = newvol; setdumpnum(); flsht(); if (!pipein && !bflag && archivefile && (first_time == FT_STATE_1)) { first_time = FT_STATE_2; findtapeblksize(TAPE_FILE); } if (readhdr(&tmpbuf) == FAIL) { (void) fprintf(stderr, gettext("volume is not in dump format\n")); volno = 0; goto again; } if (checkvol(&tmpbuf, volno) == FAIL) { (void) fprintf(stderr, gettext("Wrong volume (%d)\n"), tmpbuf.c_volume); volno = 0; goto again; } if (((time_t)(tmpbuf.c_date) != dumpdate) || ((time_t)(tmpbuf.c_ddate) != dumptime)) { char *tmp_ct; time_t lc_date = (time_t)tmpbuf.c_date; /* * This is used to save the return value from lctime(), * since that's volatile across lctime() invocations. */ tmp_ct = strdup(lctime(&lc_date)); if (tmp_ct == (char *)0) { (void) fprintf(stderr, gettext( "Cannot allocate space for time string\n")); done(1); } (void) fprintf(stderr, gettext("Wrong dump date\n\tgot: %s\twanted: %s"), tmp_ct, lctime(&dumpdate)); volno = 0; free(tmp_ct); goto again; } tapesread[volno-1]++; blksread = savecnt; tapea = savetapea; /* * If continuing from the previous volume, skip over any * blocks read already at the end of the previous volume. * * If coming to this volume at random, skip to the beginning * of the next record. */ if (tmpbuf.c_type == TS_TAPE && (tmpbuf.c_flags & DR_NEWHEADER)) { if (!wantnext) { if (archivefile && first_time == FT_STATE_2) { first_time = FT_STATE_3; } recsread = tmpbuf.c_firstrec; tapea = tmpbuf.c_tapea; dprintf(stdout, "restore skipping %d records\n", tmpbuf.c_count); for (i = tmpbuf.c_count; i > 0; i--) readtape(buf); } else if (tmpbuf.c_firstrec != 0) { savecnt = blksread; savetapea = tapea; if (archivefile && first_time == FT_STATE_2) { /* * subtract 2, 1 for archive file's TS_END * and 1 for tape's TS_TAPE */ first_time = FT_STATE_3; i = tapea - tmpbuf.c_tapea - 2; } else { i = tapea - tmpbuf.c_tapea; } if (i > 0) dprintf(stdout, gettext( "restore skipping %d duplicate records\n"), i); else if (i < 0) dprintf(stdout, gettext( "restore duplicate record botch (%d)\n"), i); while (--i >= 0) readtape(buf); blksread = savecnt; tapea = savetapea + 1; /* <= (void) gethead() below */ } } if (curfile.action == USING) { if (volno == 1) panic(gettext("active file into volume 1\n")); return; } (void) gethead(&spcl); findinode(&spcl); /* do we always restart files in full? */ if (gettingfile) { /* i.e. will we lose metadata? */ gettingfile = 0; longjmp(restart, 1); /* will this set f1 & f2? */ } } /* * handle multiple dumps per tape by skipping forward to the * appropriate one. Note we don't use absolute positioning, * as that may take a very long time. */ static void setdumpnum(void) { struct mtop tcom; int retval; if (dumpnum == 1 || volno != 1) return; if (pipein) { (void) fprintf(stderr, gettext("Cannot have multiple dumps on pipe input\n")); done(1); } tcom.mt_op = MTFSF; tcom.mt_count = dumpnum - 1; if (host) retval = rmtioctl(MTFSF, dumpnum - 1); else retval = ioctl(mt, (int)MTIOCTOP, (char *)&tcom); if (retval < 0) perror("ioctl MTFSF"); } void printdumpinfo(void) { int i; time_t date; static char *epoch = NULL; if (epoch == NULL) { epoch = strdup(gettext("the epoch\n")); if (epoch == NULL) { (void) fprintf(stderr, gettext("Out of memory\n")); return; } } date = (time_t)dumpinfo.c_date; (void) fprintf(stdout, gettext("Dump date: %s"), lctime(&date)); date = (time_t)dumpinfo.c_ddate; (void) fprintf(stdout, gettext("Dumped from: %s"), (dumpinfo.c_ddate == 0) ? epoch : lctime(&date)); if (hostinfo) { (void) fprintf(stdout, gettext("Level %d dump of %s on %.*s:%s\n"), dumpinfo.c_level, dumpinfo.c_filesys, sizeof (dumpinfo.c_host), dumpinfo.c_host, dumpinfo.c_dev); (void) fprintf(stdout, gettext("Label: %.*s\n"), sizeof (dumpinfo.c_label), dumpinfo.c_label); } if (inodeinfo) { (void) fprintf(stdout, gettext("Starting inode numbers by volume:\n")); for (i = 1; i <= dumpinfo.c_volume; i++) (void) fprintf(stdout, gettext("\tVolume %d: %6d\n"), i, dumpinfo.c_inos[i]); } } int extractfile(char *name) { static int complained_chown = 0; static int complained_lchown = 0; static int complained_chmod = 0; static int complained_utime = 0; static int complained_mknod = 0; mode_t mode; time_t timep[2]; struct entry *ep; uid_t uid; gid_t gid; char *errmsg; int result, saverr; dev_t full_dev; int dfd; char *rname; curfile.name = name; curfile.action = USING; timep[0] = (time_t)curfile.dip->di_atime; timep[1] = (time_t)curfile.dip->di_mtime; mode = curfile.dip->di_mode; uid = curfile.dip->di_suid == UID_LONG ? curfile.dip->di_uid : (uid_t)curfile.dip->di_suid; gid = curfile.dip->di_sgid == GID_LONG ? curfile.dip->di_gid : (gid_t)curfile.dip->di_sgid; resolve(name, &dfd, &rname); if (dfd != AT_FDCWD) { if (fchdir(dfd) < 0) { saverr = errno; (void) fprintf(stderr, gettext( "%s: unable to set attribute context: %s\n"), rname, strerror(saverr)); skipfile(); (void) close(dfd); return (FAIL); } } switch (mode & IFMT) { default: (void) fprintf(stderr, gettext("%s: unknown file mode 0%lo\n"), rname, (ulong_t)(mode&IFMT)); skipfile(); result = FAIL; break; case IFSOCK: vprintf(stdout, gettext("skipped socket %s\n"), rname); skipfile(); result = GOOD; break; case IFDIR: if (mflag) { ep = lookupname(name); if (ep == NIL || ep->e_flags & EXTRACT) { panic(gettext( "directory %s was not restored\n"), rname); skipfile(); result = FAIL; break; } skipfile(); result = GOOD; break; } vprintf(stdout, gettext("extract file %s\n"), rname); result = genliteraldir(rname, curfile.ino); break; case IFLNK: lnkbuf[0] = '\0'; pathlen = 0; getfile(xtrlnkfile, xtrlnkskip); if (pathlen == 0) { vprintf(stdout, gettext( "%s: zero length symbolic link (ignored)\n"), rname); result = GOOD; break; } if ((result = lf_linkit(lnkbuf, rname, SYMLINK)) != GOOD) break; /* 1254700: set uid/gid (previously missing) */ if (lchown(rname, uid, gid) < 0 && !complained_lchown) { /* Just a warning */ saverr = errno; errmsg = gettext( "Unable to restore ownership of symlink %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_lchown = 1; } metaset(rname); result = GOOD; break; case IFCHR: case IFBLK: case IFIFO: vprintf(stdout, gettext("extract special file %s\n"), rname); /* put device rdev into dev_t expanded format */ /* XXX does this always do the right thing? */ /* XXX does dump do the right thing? */ if (((curfile.dip->di_ordev & 0xFFFF0000) == 0) || ((curfile.dip->di_ordev & 0xFFFF0000) == 0xFFFF0000)) { full_dev = expdev((unsigned)(curfile.dip->di_ordev)); } else { /* LINTED sign extension ok */ full_dev = (unsigned)(curfile.dip->di_ordev); } if (mknod(rname, mode, full_dev) < 0) { struct stat64 s[1]; saverr = errno; if ((stat64(rname, s)) || ((s->st_mode & S_IFMT) != (mode & S_IFMT)) || (s->st_rdev != full_dev)) { if (saverr != EPERM || !complained_mknod) { (void) fprintf(stderr, "%s: ", rname); (void) fflush(stderr); errno = saverr; perror(gettext( "cannot create special file")); if (saverr == EPERM) { (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_mknod = 1; } } skipfile(); result = FAIL; break; } } if (chown(rname, uid, gid) < 0 && !complained_chown) { /* Just a warning */ saverr = errno; errmsg = gettext( "Unable to restore ownership of %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_chown = 1; } if (chmod(rname, mode) < 0 && !complained_chmod) { saverr = errno; errmsg = gettext( "Unable to restore permissions on %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_chmod = 1; } skipfile(); metaset(rname); /* skipfile() got the metadata, if any */ if (utime(rname, (struct utimbuf *)timep) < 0 && !complained_utime) { saverr = errno; errmsg = gettext( "Unable to restore times on %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_utime = 1; } result = GOOD; break; case IFREG: vprintf(stdout, gettext("extract file %s\n"), rname); /* * perform a restrictive creat(2) initally, we'll * fchmod(2) according to the archive later after * we've written the blocks. */ ofile = creat64(rname, 0600); if (ofile < 0) { saverr = errno; errmsg = gettext("cannot create file"); (void) fprintf(stderr, "%s: ", rname); (void) fflush(stderr); errno = saverr; perror(errmsg); skipfile(); result = FAIL; break; } if (fchown(ofile, uid, gid) < 0 && !complained_chown) { /* Just a warning */ saverr = errno; errmsg = gettext( "Unable to restore ownership of %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_chown = 1; } getfile(xtrfile, xtrskip); metaset(rname); /* * the fchmod(2) has to come after getfile() as some POSIX * implementations clear the S_ISUID and S_ISGID bits of the * file after every write(2). */ if (fchmod(ofile, mode) < 0 && !complained_chmod) { saverr = errno; errmsg = gettext( "Unable to restore permissions on %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_chmod = 1; } /* * Some errors don't get reported until we close(2), so * check for them. * XXX unlink the file if an error is reported? */ if (close(ofile) < 0) { saverr = errno; errmsg = gettext("error closing file"); (void) fprintf(stderr, "%s: ", rname); (void) fflush(stderr); errno = saverr; perror(errmsg); result = FAIL; break; } if (utime(rname, (struct utimbuf *)timep) < 0 && !complained_utime) { saverr = errno; errmsg = gettext( "Unable to restore times on %s: %s\n"); (void) fprintf(stderr, errmsg, rname, strerror(saverr)); (void) fprintf(stderr, gettext( "Additional such failures will be ignored.\n")); complained_utime = 1; } result = GOOD; break; } if (dfd != AT_FDCWD) { fchdir(savepwd); (void) close(dfd); } return (result); } /* * skip over bit maps on the tape */ void skipmaps(void) { continuemap = 1; while (checktype(&spcl, TS_CLRI) == GOOD || checktype(&spcl, TS_BITS) == GOOD) skipfile(); continuemap = 0; } /* * skip over a file on the tape */ void skipfile(void) { curfile.action = SKIP; getfile(null, null); } /* * Do the file extraction, calling the supplied functions * with the blocks */ void getfile(void (*f1)(), void (*f2)()) { int i; size_t curblk = 0; offset_t size = (offset_t)spcl.c_dinode.di_size; static char clearedbuf[MAXBSIZE]; char buf[TP_BSIZE_MAX]; char *bufptr; char junk[TP_BSIZE_MAX]; assert(MAXBSIZE >= tp_bsize); metaset(NULL); /* flush old metadata */ if (checktype(&spcl, TS_END) == GOOD) { panic(gettext("ran off end of volume\n")); return; } if (ishead(&spcl) == FAIL) { panic(gettext("not at beginning of a file\n")); return; } metacheck(&spcl); /* check for metadata in header */ if (!gettingfile && setjmp(restart) != 0) { gettingfile = 0; /* paranoia; longjmp'er should do */ return; } gettingfile++; loop: if ((spcl.c_dinode.di_mode & IFMT) == IFSHAD) { f1 = xtrmeta; f2 = metaskip; } for (i = 0, bufptr = buf; i < spcl.c_count; i++) { if ((i >= TP_NINDIR) || (spcl.c_addr[i])) { readtape(bufptr); bufptr += tp_bsize; curblk++; if (curblk == (fssize / tp_bsize)) { (*f1)(buf, size > tp_bsize ? (size_t)(fssize) : /* LINTED size <= tp_bsize */ (curblk - 1) * tp_bsize + (size_t)size); curblk = 0; bufptr = buf; } } else { if (curblk > 0) { (*f1)(buf, size > tp_bsize ? (size_t)(curblk * tp_bsize) : /* LINTED size <= tp_bsize */ (curblk - 1) * tp_bsize + (size_t)size); curblk = 0; bufptr = buf; } (*f2)(clearedbuf, size > tp_bsize ? /* LINTED size <= tp_bsize */ (long)tp_bsize : (size_t)size); } if ((size -= tp_bsize) <= 0) { for (i++; i < spcl.c_count; i++) if ((i >= TP_NINDIR) || (spcl.c_addr[i])) readtape(junk); break; } } if (curblk > 0) { /* * Ok to cast size to size_t here. The above for loop reads * data into the buffer then writes it to the output file. The * call to f1 here is to write out the data that's in the * buffer that has not yet been written to the file. * This will be less than N-KB of data, since the * above loop writes to the file in filesystem- * blocksize chunks. */ /* LINTED: size fits into a size_t at this point */ (*f1)(buf, (curblk * tp_bsize) + (size_t)size); curblk = 0; bufptr = buf; } if ((readhdr(&spcl) == GOOD) && (checktype(&spcl, TS_ADDR) == GOOD)) { if (continuemap) size = (offset_t)spcl.c_count * tp_bsize; /* big bitmap */ else if ((size <= 0) && ((spcl.c_dinode.di_mode & IFMT) == IFSHAD)) { /* LINTED unsigned to signed conversion ok */ size = spcl.c_dinode.di_size; } if (size > 0) goto loop; } if (size > 0) dprintf(stdout, gettext("Missing address (header) block for %s\n"), curfile.name); findinode(&spcl); gettingfile = 0; } /* * The next routines are called during file extraction to * put the data into the right form and place. */ static void xtrfile(char *buf, size_t size) { if (write(ofile, buf, (size_t)size) == -1) { int saverr = errno; (void) fprintf(stderr, gettext("write error extracting inode %d, name %s\n"), curfile.ino, curfile.name); errno = saverr; perror("write"); done(1); } } /* * Even though size is a size_t, it's seeking to a relative * offset. Thus, the seek could go beyond 2 GB, so lseek64 is needed. */ /*ARGSUSED*/ static void xtrskip(char *buf, size_t size) { if (lseek64(ofile, (offset_t)size, 1) == -1) { int saverr = errno; (void) fprintf(stderr, gettext("seek error extracting inode %d, name %s\n"), curfile.ino, curfile.name); errno = saverr; perror("lseek64"); done(1); } } /* these are local to the next five functions */ static char *metadata = NULL; static size_t metasize = 0; static void metacheck(struct s_spcl *head) { if (! (head->c_flags & DR_HASMETA)) return; if ((metadata = malloc(metasize = (size_t)sizeof (head->c_shadow))) == NULL) { (void) fprintf(stderr, gettext("Cannot malloc for metadata\n")); done(1); } bcopy(&(head->c_shadow), metadata, metasize); } static void xtrmeta(char *buf, size_t size) { if ((metadata == NULL) && ((spcl.c_dinode.di_mode & IFMT) != IFSHAD)) return; if ((metadata = realloc(metadata, metasize + size)) == NULL) { (void) fprintf(stderr, gettext("Cannot malloc for metadata\n")); done(1); } bcopy(buf, metadata + metasize, size); metasize += size; } /* ARGSUSED */ static void metaskip(char *buf, size_t size) { if (metadata == NULL) return; if ((metadata = realloc(metadata, metasize + size)) == NULL) { (void) fprintf(stderr, gettext("Cannot malloc for metadata\n")); done(1); } bzero(metadata + metasize, size); metasize += size; } static void metaset(char *name) { if (metadata == NULL) return; if (name != NULL) metaproc(name, metadata, metasize); (void) free(metadata); metadata = NULL; metasize = 0; } void metaget(char **data, size_t *size) { *data = metadata; *size = metasize; } static void fsd_acl(char *name, char *aclp, unsigned size) { static aclent_t *aclent = NULL; ufs_acl_t *diskacl; static int n = 0; acl_t *set_aclp; uint_t i; int saverr, j; if (aclp == NULL) { if (aclent != NULL) free(aclent); aclent = NULL; n = 0; return; } /*LINTED [aclp is malloc'd]*/ diskacl = (ufs_acl_t *)aclp; /* LINTED: result fits in an int */ j = size / sizeof (*diskacl); normacls(byteorder, diskacl, j); i = n; n += j; aclent = realloc(aclent, n * (size_t)sizeof (*aclent)); if (aclent == NULL) { (void) fprintf(stderr, gettext("Cannot malloc acl list\n")); done(1); } j = 0; while (i < n) { aclent[i].a_type = diskacl[j].acl_tag; aclent[i].a_id = diskacl[j].acl_who; aclent[i].a_perm = diskacl[j].acl_perm; ++i; ++j; } set_aclp = acl_to_aclp(ACLENT_T, aclent, n); if (set_aclp == NULL) { (void) fprintf(stderr, gettext("Cannot build acl_t\n")); done(1); } if (acl_set(name, set_aclp) == -1) { static int once = 0; /* * Treat some errors from the acl subsystem specially to * avoid being too noisy: * * ENOSYS - ACLs not supported on this file system * EPERM - not the owner or not privileged * * The following is also supported for backwards compat. * since acl(2) used to return the wrong errno: * * EINVAL - not the owner of the object */ if (errno == ENOSYS || errno == EPERM || errno == EINVAL) { if (once == 0) { saverr = errno; ++once; fprintf(stderr, gettext("setacl failed: %s\n"), strerror(saverr)); } } else { saverr = errno; fprintf(stderr, gettext("setacl on %s failed: %s\n"), name, strerror(saverr)); } } acl_free(set_aclp); } static struct fsdtypes { int type; void (*function)(); } fsdtypes[] = { {FSD_ACL, fsd_acl}, {FSD_DFACL, fsd_acl}, {0, NULL} }; void metaproc(char *name, char *mdata, size_t msize) { struct fsdtypes *fsdtype; ufs_fsd_t *fsd; char *c; /* * for the whole shadow inode, dispatch each piece * to the appropriate function. */ c = mdata; /* LINTED (c - mdata) fits into a size_t */ while ((size_t)(c - mdata) < msize) { /*LINTED [mdata is malloc'd]*/ fsd = (ufs_fsd_t *)c; assert((fsd->fsd_size % 4) == 0); /* LINTED: lint thinks pointers are signed */ c += FSD_RECSZ(fsd, fsd->fsd_size); if ((fsd->fsd_type == FSD_FREE) || ((unsigned)(fsd->fsd_size) <= sizeof (ufs_fsd_t)) || (c > (mdata + msize))) break; for (fsdtype = fsdtypes; fsdtype->type; fsdtype++) if (fsdtype->type == fsd->fsd_type) (*fsdtype->function)(name, fsd->fsd_data, (unsigned)(fsd->fsd_size) - sizeof (fsd->fsd_type) - sizeof (fsd->fsd_size)); /* ^^^ be sure to change if fsd ever changes ^^^ */ } /* reset the state of all the functions */ for (fsdtype = fsdtypes; fsdtype->type; fsdtype++) (*fsdtype->function)(NULL, NULL, 0); } static void xtrlnkfile(char *buf, size_t size) { /* LINTED: signed/unsigned mix ok */ pathlen += size; if (pathlen > MAXPATHLEN) { (void) fprintf(stderr, gettext("symbolic link name: %s->%s%s; too long %d\n"), curfile.name, lnkbuf, buf, pathlen); done(1); } buf[size] = '\0'; (void) strcat(lnkbuf, buf); /* add an extra NULL to make this a legal complex string */ lnkbuf[pathlen+1] = '\0'; } /*ARGSUSED*/ static void xtrlnkskip(char *buf, size_t size) { (void) fprintf(stderr, gettext("unallocated block in symbolic link %s\n"), curfile.name); done(1); } static void xtrmap(char *buf, size_t size) { if ((map+size) > endmap) { int64_t mapsize, increment; int64_t diff; if (spcl.c_type != TS_ADDR) { (void) fprintf(stderr, gettext("xtrmap: current record not TS_ADDR\n")); done(1); } if ((spcl.c_count < 0) || (spcl.c_count > TP_NINDIR)) { (void) fprintf(stderr, gettext("xtrmap: illegal c_count field (%d)\n"), spcl.c_count); done(1); } increment = d_howmany( ((spcl.c_count * tp_bsize * NBBY) + 1), NBBY); mapsize = endmap - beginmap + increment; if (mapsize > UINT_MAX) { (void) fprintf(stderr, gettext("xtrmap: maximum bitmap size exceeded")); done(1); } diff = map - beginmap; /* LINTED mapsize checked above */ beginmap = realloc(beginmap, (size_t)mapsize); if (beginmap == NULL) { (void) fprintf(stderr, gettext("xtrmap: realloc failed\n")); done(1); } map = beginmap + diff; endmap = beginmap + mapsize; /* LINTED endmap - map cannot exceed 32 bits */ bzero(map, (size_t)(endmap - map)); maxino = NBBY * mapsize + 1; } bcopy(buf, map, size); /* LINTED character pointers aren't signed */ map += size; } /*ARGSUSED*/ static void xtrmapskip(char *buf, size_t size) { (void) fprintf(stderr, gettext("hole in map\n")); done(1); } /*ARGSUSED*/ void null(char *buf, size_t size) { } /* * Do the tape i/o, dealing with volume changes * etc.. */ static void readtape(char *b) { int i; int rd, newvol; int cnt; struct s_spcl *sp; int32_t expected_magic; if (tbf == NULL) { (void) fprintf(stderr, gettext( "Internal consistency failure in readtape: tbf is NULL\n")); done(1); } expected_magic = ((tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC); top: if (bct < numtrec) { /* * check for old-dump floppy EOM -- it may appear in * the middle of a buffer. The Dflag used to be used for * this, but since it doesn't hurt to always do this we * got rid of the Dflag. */ /*LINTED [tbf = malloc()]*/ sp = &((union u_spcl *)&tbf[bct*tp_bsize])->s_spcl; if (sp->c_magic == expected_magic && sp->c_type == TS_EOM && (time_t)(sp->c_date) == dumpdate && (time_t)(sp->c_ddate) == dumptime) { for (i = 0; i < ntrec; i++) /*LINTED [tbf = malloc()]*/ ((struct s_spcl *) &tbf[i*tp_bsize])->c_magic = 0; bct = 0; rd = 0; i = 0; goto nextvol; } bcopy(&tbf[(bct++*tp_bsize)], b, (size_t)tp_bsize); blksread++; tapea++; return; } /*LINTED [assertion always true]*/ assert(sizeof (union u_spcl) == TP_BSIZE_MAX); for (i = 0; i < ntrec; i++) /*LINTED [tbf = malloc()]*/ ((struct s_spcl *)&tbf[i*sizeof (struct s_spcl)])->c_magic = 0; if (numtrec == 0) { /* LINTED unsigned/signed assignment ok */ numtrec = ntrec; } /* LINTED unsigned/signed assignment ok */ cnt = ntrec*tp_bsize; rd = 0; getmore: if (host) i = rmtread(&tbf[rd], cnt); else i = read(mt, &tbf[rd], cnt); /* * Check for mid-tape short read error. * If found, return rest of buffer. */ if (numtrec < ntrec && i != 0) { /* LINTED unsigned/signed assignment ok */ numtrec = ntrec; goto top; } /* * Handle partial block read. */ if (i > 0 && i != ntrec*tp_bsize) { if (pipein) { rd += i; cnt -= i; if (cnt > 0) goto getmore; i = rd; } else { if (i % tp_bsize != 0) panic(gettext( "partial block read: %d should be %d\n"), i, ntrec * tp_bsize); numtrec = i / tp_bsize; if (numtrec == 0) /* * it's possible to read only 512 bytes * from a QIC device... */ i = 0; } } /* * Handle read error. */ if (i < 0) { switch (curfile.action) { default: (void) fprintf(stderr, gettext( "Read error while trying to set up volume\n")); break; case UNKNOWN: (void) fprintf(stderr, gettext( "Read error while trying to resynchronize\n")); break; case USING: (void) fprintf(stderr, gettext( "Read error while restoring %s\n"), curfile.name); break; case SKIP: (void) fprintf(stderr, gettext( "Read error while skipping over inode %d\n"), curfile.ino); break; } if (!yflag && !reply(gettext("continue"))) done(1); /* LINTED: unsigned->signed conversion ok */ i = (int)(ntrec*tp_bsize); bzero(tbf, (size_t)i); if ((host != 0 && rmtseek(i, 1) < 0) || (host == 0 && (lseek64(mt, (offset_t)i, 1) == (off64_t)-1))) { perror(gettext("continuation failed")); done(1); } } /* * Handle end of tape. The Dflag used to be used, but since it doesn't * hurt to always check we got rid if it. */ /* * if the first record in the buffer just read is EOM, * change volumes. */ /*LINTED [tbf = malloc()]*/ sp = &((union u_spcl *)tbf)->s_spcl; if (i != 0 && sp->c_magic == expected_magic && sp->c_type == TS_EOM && (time_t)(sp->c_date) == dumpdate && (time_t)(sp->c_ddate) == dumptime) { i = 0; } nextvol: if (i == 0) { if (!pipein) { newvol = volno + 1; volno = 0; numtrec = 0; getvol(newvol); readtape(b); /* XXX tail recursion, not goto top? */ return; } /* XXX if panic returns, should we round rd up? */ /* XXX if we do, then we should zero the intervening space */ if (rd % tp_bsize != 0) panic(gettext("partial block read: %d should be %d\n"), rd, ntrec * tp_bsize); bcopy((char *)&endoftapemark, &tbf[rd], (size_t)tp_bsize); } bct = 0; bcopy(&tbf[(bct++*tp_bsize)], b, (size_t)tp_bsize); blksread++; recsread++; tapea++; rec_position++; } void findtapeblksize(int arfile) { int i; if (tbf == NULL) { (void) fprintf(stderr, gettext( "Internal consistency failure in findtapeblksize: " "tbf is NULL\n")); assert(tbf != NULL); done(1); } for (i = 0; i < ntrec; i++) /*LINTED [tbf = malloc()]*/ ((struct s_spcl *)&tbf[i * tp_bsize])->c_magic = 0; bct = 0; if (host && arfile == TAPE_FILE) tape_rec_size = rmtread(tbf, ntrec * tp_bsize); else tape_rec_size = read(mt, tbf, ntrec * tp_bsize); recsread++; rec_position++; if (tape_rec_size == (ssize_t)-1) { int saverr = errno; char *errmsg = gettext("Media read error"); errno = saverr; perror(errmsg); done(1); } if (tape_rec_size % tp_bsize != 0) { (void) fprintf(stderr, gettext( "Record size (%d) is not a multiple of dump block size (%d)\n"), tape_rec_size, tp_bsize); done(1); } ntrec = (int)tape_rec_size / tp_bsize; /* LINTED unsigned/signed assignment ok */ numtrec = ntrec; vprintf(stdout, gettext("Media block size is %d\n"), ntrec*2); } void flsht(void) { /* LINTED unsigned/signed assignment ok */ bct = ntrec+1; } void closemt(int mode) { /* * If mode == FORCE_OFFLINE then we're not done but * we need to change tape. So, rewind and unload current * tape before loading the new one. */ static struct mtop mtop = { MTOFFL, 0 }; if (mt < 0) return; if (offline || mode == FORCE_OFFLINE) (void) fprintf(stderr, gettext("Rewinding tape\n")); if (host) { if (offline || mode == FORCE_OFFLINE) (void) rmtioctl(MTOFFL, 1); rmtclose(); } else if (pipein) { char buffy[MAXBSIZE]; while (read(mt, buffy, sizeof (buffy)) > 0) { continue; /*LINTED [assertion always true]*/ } (void) close(mt); } else { /* * Only way to tell if this is a floppy is to issue an ioctl * but why waste one - if the eject fails, tough! */ if (offline || mode == FORCE_OFFLINE) (void) ioctl(mt, MTIOCTOP, &mtop); (void) ioctl(mt, FDEJECT, 0); (void) close(mt); } mt = -1; } static int checkvol(struct s_spcl *b, int t) { if (b->c_volume != t) return (FAIL); return (GOOD); } int readhdr(struct s_spcl *b) { if (gethead(b) == FAIL) { dprintf(stdout, gettext("readhdr fails at %ld blocks\n"), blksread); return (FAIL); } return (GOOD); } /* * read the tape into buf, then return whether or * or not it is a header block. */ int gethead(struct s_spcl *buf) { int i; union u_ospcl { char dummy[TP_BSIZE_MIN]; struct s_ospcl { int32_t c_type; int32_t c_date; int32_t c_ddate; int32_t c_volume; int32_t c_tapea; ushort_t c_inumber; int32_t c_magic; int32_t c_checksum; struct odinode { unsigned short odi_mode; ushort_t odi_nlink; ushort_t odi_uid; ushort_t odi_gid; int32_t odi_size; int32_t odi_rdev; char odi_addr[36]; int32_t odi_atime; int32_t odi_mtime; int32_t odi_ctime; } c_dinode; int32_t c_count; char c_baddr[256]; } s_ospcl; } u_ospcl; if (cvtflag) { readtape((char *)(&u_ospcl.s_ospcl)); bzero((char *)buf, (size_t)TP_BSIZE_MIN); buf->c_type = u_ospcl.s_ospcl.c_type; buf->c_date = u_ospcl.s_ospcl.c_date; buf->c_ddate = u_ospcl.s_ospcl.c_ddate; buf->c_volume = u_ospcl.s_ospcl.c_volume; buf->c_tapea = u_ospcl.s_ospcl.c_tapea; buf->c_inumber = u_ospcl.s_ospcl.c_inumber; buf->c_checksum = u_ospcl.s_ospcl.c_checksum; buf->c_magic = u_ospcl.s_ospcl.c_magic; buf->c_dinode.di_mode = u_ospcl.s_ospcl.c_dinode.odi_mode; /* LINTED: unsigned/signed combination ok */ buf->c_dinode.di_nlink = u_ospcl.s_ospcl.c_dinode.odi_nlink; buf->c_dinode.di_size = (unsigned)(u_ospcl.s_ospcl.c_dinode.odi_size); buf->c_dinode.di_uid = u_ospcl.s_ospcl.c_dinode.odi_uid; buf->c_dinode.di_gid = u_ospcl.s_ospcl.c_dinode.odi_gid; buf->c_dinode.di_suid = UID_LONG; buf->c_dinode.di_sgid = GID_LONG; buf->c_dinode.di_ordev = u_ospcl.s_ospcl.c_dinode.odi_rdev; buf->c_dinode.di_atime = u_ospcl.s_ospcl.c_dinode.odi_atime; buf->c_dinode.di_mtime = u_ospcl.s_ospcl.c_dinode.odi_mtime; buf->c_dinode.di_ctime = u_ospcl.s_ospcl.c_dinode.odi_ctime; buf->c_count = u_ospcl.s_ospcl.c_count; bcopy(u_ospcl.s_ospcl.c_baddr, buf->c_addr, sizeof (u_ospcl.s_ospcl.c_baddr)); /*CONSTANTCONDITION*/ assert(sizeof (u_ospcl.s_ospcl) < sizeof (union u_spcl)); /* we byte-swap the new spclrec, but checksum the old */ /* (see comments in normspcl()) */ if (normspcl(byteorder, buf, (int *)(&u_ospcl.s_ospcl), sizeof (u_ospcl.s_ospcl), OFS_MAGIC)) return (FAIL); buf->c_magic = ((tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC); } else { readtape((char *)buf); if (normspcl(byteorder, buf, (int *)buf, tp_bsize, ((tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC))) return (FAIL); } switch (buf->c_type) { case TS_CLRI: case TS_BITS: /* * Have to patch up missing information in bit map headers */ buf->c_inumber = 0; buf->c_dinode.di_size = (offset_t)buf->c_count * tp_bsize; for (i = 0; i < buf->c_count && i < TP_NINDIR; i++) buf->c_addr[i] = 1; break; case TS_TAPE: case TS_END: if (dumpinfo.c_date == 0) { dumpinfo.c_date = spcl.c_date; dumpinfo.c_ddate = spcl.c_ddate; } if (!hostinfo && spcl.c_host[0] != '\0') { bcopy(spcl.c_label, dumpinfo.c_label, sizeof (spcl.c_label)); bcopy(spcl.c_filesys, dumpinfo.c_filesys, sizeof (spcl.c_filesys)); bcopy(spcl.c_dev, dumpinfo.c_dev, sizeof (spcl.c_dev)); bcopy(spcl.c_host, dumpinfo.c_host, sizeof (spcl.c_host)); dumpinfo.c_level = spcl.c_level; hostinfo++; if (c_label != NULL && strncmp(c_label, spcl.c_label, sizeof (spcl.c_label)) != 0) { (void) fprintf(stderr, gettext( "Incorrect tape label. Expected `%s', got `%.*s'\n"), c_label, sizeof (spcl.c_label), spcl.c_label); done(1); } } if (!inodeinfo && (spcl.c_flags & DR_INODEINFO)) { dumpinfo.c_volume = spcl.c_volume; bcopy(spcl.c_inos, dumpinfo.c_inos, sizeof (spcl.c_inos)); inodeinfo++; } buf->c_inumber = 0; break; case TS_INODE: case TS_ADDR: break; default: panic(gettext("%s: unknown inode type %d\n"), "gethead", buf->c_type); return (FAIL); } if (dflag) accthdr(buf); return (GOOD); } /* * Check that a header is where it belongs and predict the next header */ static void accthdr(struct s_spcl *header) { static ino_t previno = (ino_t)(unsigned)-1; static int prevtype; static long predict; int blks, i; if (header->c_type == TS_TAPE) { if (header->c_firstrec) (void) fprintf(stderr, gettext("Volume header begins with record %d"), header->c_firstrec); else (void) fprintf(stderr, gettext("Volume header")); (void) fprintf(stderr, "\n"); previno = (ino_t)(unsigned)-1; return; } if (previno == (ino_t)(unsigned)-1) goto newcalc; switch (prevtype) { case TS_BITS: (void) fprintf(stderr, gettext("Dump mask header")); break; case TS_CLRI: (void) fprintf(stderr, gettext("Remove mask header")); break; case TS_INODE: (void) fprintf(stderr, gettext("File header, ino %d at record %d"), previno, rec_position); break; case TS_ADDR: (void) fprintf(stderr, gettext("File continuation header, ino %d"), previno); break; case TS_END: (void) fprintf(stderr, gettext("End of media header")); break; } if (predict != blksread - 1) (void) fprintf(stderr, gettext("; predicted %ld blocks, got %ld blocks"), predict, blksread - 1); (void) fprintf(stderr, "\n"); newcalc: blks = 0; if (header->c_type != TS_END) for (i = 0; i < header->c_count; i++) if ((i >= TP_NINDIR) || (header->c_addr[i] != 0)) blks++; predict = blks; blksread = 0; prevtype = header->c_type; previno = header->c_inumber; } /* * Try to determine which volume a file resides on. */ int volnumber(ino_t inum) { int i; if (inodeinfo == 0) return (0); for (i = 1; i <= dumpinfo.c_volume; i++) if (inum < (ino_t)(unsigned)(dumpinfo.c_inos[i])) break; return (i - 1); } /* * Find an inode header. * Note that *header must be stable storage, as curfile will end up with * pointers into it. */ void findinode(struct s_spcl *header) { long skipcnt = 0; int i; char buf[TP_BSIZE_MAX]; curfile.name = gettext(""); curfile.action = UNKNOWN; curfile.dip = (struct dinode *)NULL; curfile.ino = 0; curfile.ts = 0; if (ishead(header) == FAIL) { skipcnt++; while (gethead(header) == FAIL || (time_t)(header->c_date) != dumpdate) skipcnt++; } for (;;) { if (checktype(header, TS_ADDR) == GOOD) { /* * Skip up to the beginning of the next record */ for (i = 0; i < header->c_count; i++) if ((i >= TP_NINDIR) || (header->c_addr[i])) readtape(buf); (void) gethead(header); continue; } if (checktype(header, TS_INODE) == GOOD) { curfile.dip = &header->c_dinode; if (curfile.dip->di_suid != UID_LONG) curfile.dip->di_uid = curfile.dip->di_suid; if (curfile.dip->di_sgid != GID_LONG) curfile.dip->di_gid = curfile.dip->di_sgid; curfile.ino = header->c_inumber; curfile.ts = TS_INODE; break; } if (checktype(header, TS_END) == GOOD) { curfile.ino = maxino; curfile.ts = TS_END; break; } if (checktype(header, TS_CLRI) == GOOD) { curfile.name = gettext(""); curfile.ts = TS_CLRI; break; } if (checktype(header, TS_BITS) == GOOD) { curfile.name = gettext(""); curfile.ts = TS_BITS; break; } while (gethead(header) == FAIL) skipcnt++; } if (skipcnt > 0) (void) fprintf(stderr, gettext("resync restore, skipped %d blocks\n"), skipcnt); } /* * return whether or not the buffer contains a header block */ static int ishead(struct s_spcl *buf) { if (buf->c_magic != ((tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC)) return (FAIL); return (GOOD); } static int checktype(struct s_spcl *b, int t) { if (b->c_type != t) return (FAIL); return (GOOD); } /* * If autoloading is enabled, attempt to do it. If we succeed, * return non-zero. */ static int autoload_tape(void) { int result = 0; /* assume failure */ int tries; int fd; if (autoload) { /* * 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). */ /* rewind tape and offline drive before loading new tape */ closemt(FORCE_OFFLINE); (void) fprintf(stderr, gettext("Attempting to autoload next volume\n")); for (tries = 0; tries < autoload_tries; tries++) { if (host) { if (rmtopen(magtape, O_RDONLY) >= 0) { rmtclose(); result = 1; break; } } else { if ((fd = open(magtape, O_RDONLY|O_LARGEFILE, 0600)) >= 0) { (void) close(fd); result = 1; break; } } (void) sleep(autoload_period); } if (result == 0) { /* Assume caller will deal with manual change-over */ (void) fprintf(stderr, gettext("Autoload timed out\n")); } else { if ((host != NULL && (mt = rmtopen(magtape, O_RDONLY)) == -1) || (host == NULL && (mt = open(magtape, O_RDONLY|O_LARGEFILE)) == -1)) { (void) fprintf(stderr, gettext( "Autoload could not re-open tape\n")); result = 0; } else { (void) fprintf(stderr, gettext( "Tape loaded\n")); } } } return (result); } /* * Copyright 2005 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) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #include "restore.h" #include #include #include #include /* LINTED: this file really is necessary */ #include #include /* * Insure that all the components of a pathname exist. Note that * lookupname() and addentry() both expect complex names as * input arguments, so a double NULL needs to be added to each name. */ void pathcheck(char *name) { char *cp, save; struct entry *ep; char *start; start = strchr(name, '/'); if (start == 0) return; for (cp = start; *cp != '\0'; cp++) { if (*cp != '/') continue; *cp = '\0'; save = *(cp+1); *(cp+1) = '\0'; ep = lookupname(name); if (ep == NIL) { ep = addentry(name, psearch(name), NODE); newnode(ep); } /* LINTED: result fits in a short */ ep->e_flags |= NEW|KEEP; *cp = '/'; *(cp+1) = save; } } /* * Change a name to a unique temporary name. */ void mktempname(struct entry *ep) { char *newname; if (ep->e_flags & TMPNAME) badentry(ep, gettext("mktempname: called with TMPNAME")); /* LINTED: result fits in a short */ ep->e_flags |= TMPNAME; newname = savename(gentempname(ep)); renameit(myname(ep), newname); freename(ep->e_name); ep->e_name = newname; /* LINTED: savename guarantees strlen will fit */ ep->e_namlen = strlen(ep->e_name); } /* * Generate a temporary name for an entry. */ char * gentempname(struct entry *ep) { static char name[MAXPATHLEN]; struct entry *np; long i = 0; for (np = lookupino(ep->e_ino); np != NIL && np != ep; np = np->e_links) i++; if (np == NIL) badentry(ep, gettext("not on ino list")); (void) snprintf(name, sizeof (name), "%s%ld%lu", TMPHDR, i, ep->e_ino); return (name); } /* * Rename a file or directory. */ void renameit(char *fp, char *tp) { int fromfd, tofd; char *from, *to; char tobuf[MAXPATHLEN]; char *pathend; resolve(fp, &fromfd, &from); /* * The to pointer argument is assumed to be either a fully * specified path (starting with "./") or a simple temporary * file name (starting with TMPHDR). If passed a simple temp * file name, we need to set up the descriptors explicitly. */ if (strncmp(tp, TMPHDR, sizeof (TMPHDR) - 1) == 0) { tofd = fromfd; if ((pathend = strrchr(from, '/')) != NULL) { strncpy(tobuf, from, pathend - from + 1); tobuf[pathend - from + 1] = '\0'; strlcat(tobuf, tp, sizeof (tobuf)); to = tobuf; } else { to = tp; } } else resolve(tp, &tofd, &to); if (renameat(fromfd, from, tofd, to) < 0) { int saverr = errno; (void) fprintf(stderr, gettext("Warning: cannot rename %s to %s: %s\n"), from, to, strerror(saverr)); (void) fflush(stderr); } else { vprintf(stdout, gettext("rename %s to %s\n"), from, to); } if (fromfd != AT_FDCWD) (void) close(fromfd); if (tofd != AT_FDCWD) (void) close(tofd); } /* * Create a new node (directory). Note that, because we have no * mkdirat() function, fchdir() must be used set up the appropriate * name space context prior to the call to mkdir() if we are * operating in attribute space. */ void newnode(struct entry *np) { char *cp; int dfd; if (np->e_type != NODE) badentry(np, gettext("newnode: not a node")); resolve(myname(np), &dfd, &cp); if (dfd != AT_FDCWD) { if (fchdir(dfd) < 0) { int saverr = errno; (void) fprintf(stderr, gettext("Warning: cannot create %s: %s"), cp, strerror(saverr)); (void) fflush(stderr); (void) close(dfd); return; } } if (mkdir(cp, 0777) < 0) { int saverr = errno; /* LINTED: result fits in a short */ np->e_flags |= EXISTED; (void) fprintf(stderr, gettext("Warning: ")); (void) fflush(stderr); (void) fprintf(stderr, "%s: %s\n", cp, strerror(saverr)); } else { vprintf(stdout, gettext("Make node %s\n"), cp); } if (dfd != AT_FDCWD) { fchdir(savepwd); (void) close(dfd); } } /* * Remove an old node (directory). See comment above on newnode() * for explanation of fchdir() use below. */ void removenode(struct entry *ep) { char *cp; int dfd; if (ep->e_type != NODE) badentry(ep, gettext("removenode: not a node")); if (ep->e_entries != NIL) badentry(ep, gettext("removenode: non-empty directory")); /* LINTED: result fits in a short */ ep->e_flags |= REMOVED; /* LINTED: result fits in a short */ ep->e_flags &= ~TMPNAME; resolve(myname(ep), &dfd, &cp); if (dfd != AT_FDCWD) { if (fchdir(dfd) < 0) { int saverr = errno; (void) fprintf(stderr, gettext("Warning: cannot remove %s: %s"), cp, strerror(saverr)); (void) fflush(stderr); (void) close(dfd); return; } } if (rmdir(cp) < 0) { /* NOTE: could use unlinkat (..,REMOVEDIR) */ int saverr = errno; (void) fprintf(stderr, gettext("Warning: %s: %s\n"), cp, strerror(saverr)); (void) fflush(stderr); } else { vprintf(stdout, gettext("Remove node %s\n"), cp); } if (dfd != AT_FDCWD) { (void) fchdir(savepwd); (void) close(dfd); } } /* * Remove a leaf. */ void removeleaf(struct entry *ep) { char *cp; int dfd; if (ep->e_type != LEAF) badentry(ep, gettext("removeleaf: not a leaf")); /* LINTED: result fits in a short */ ep->e_flags |= REMOVED; /* LINTED: result fits in a short */ ep->e_flags &= ~TMPNAME; resolve(myname(ep), &dfd, &cp); if (unlinkat(dfd, cp, 0) < 0) { int saverr = errno; (void) fprintf(stderr, gettext("Warning: %s: %s\n"), cp, strerror(saverr)); (void) fflush(stderr); } else { vprintf(stdout, gettext("Remove leaf %s\n"), cp); } if (dfd != AT_FDCWD) (void) close(dfd); } /* * Create a link. * This function assumes that the context has already been set * for the link file to be created (i.e., we have "fchdir-ed" * into attribute space already if this is an attribute link). */ int lf_linkit(char *existing, char *new, int type) { char linkbuf[MAXPATHLEN]; struct stat64 s1[1], s2[1]; char *name; int dfd, l, result; resolve(existing, &dfd, &name); if (dfd == -1) { (void) fprintf(stderr, gettext( "Warning: cannot restore %s link %s->%s\n"), (type == SYMLINK ? "symbolic" : "hard"), new, existing); result = FAIL; goto out; } if (type == SYMLINK) { if (symlink(name, new) < 0) { /* No trailing \0 from readlink(2) */ if (((l = readlink(new, linkbuf, sizeof (linkbuf))) > 0) && (l == strlen(name)) && (strncmp(linkbuf, name, l) == 0)) { vprintf(stdout, gettext("Symbolic link %s->%s ok\n"), new, name); result = GOOD; goto out; } else { int saverr = errno; (void) fprintf(stderr, gettext( "Warning: cannot create symbolic link %s->%s: %s"), new, name, strerror(saverr)); (void) fflush(stderr); result = FAIL; goto out; } } } else if (type == HARDLINK) { if (link(name, new) < 0) { int saverr = errno; if ((stat64(name, s1) == 0) && (stat64(new, s2) == 0) && (s1->st_dev == s2->st_dev) && (s1->st_ino == s2->st_ino)) { vprintf(stdout, gettext("Hard link %s->%s ok\n"), new, name); result = GOOD; goto out; } else { (void) fprintf(stderr, gettext( "Warning: cannot create hard link %s->%s: %s\n"), new, name, strerror(saverr)); (void) fflush(stderr); result = FAIL; goto out; } } } else { panic(gettext("%s: unknown type %d\n"), "linkit", type); result = FAIL; goto out; } result = GOOD; if (type == SYMLINK) vprintf(stdout, gettext("Create symbolic link %s->%s\n"), new, name); else vprintf(stdout, gettext("Create hard link %s->%s\n"), new, name); out: if (dfd != AT_FDCWD) { (void) close(dfd); } return (result); } /* * Find lowest-numbered inode (above "start") that needs to be extracted. * Caller knows that a return value of maxino means there's nothing left. */ ino_t lowerbnd(ino_t start) { struct entry *ep; for (; start < maxino; start++) { ep = lookupino(start); if (ep == NIL || ep->e_type == NODE) continue; if (ep->e_flags & (NEW|EXTRACT)) return (start); } return (start); } /* * Find highest-numbered inode (below "start") that needs to be extracted. */ ino_t upperbnd(ino_t start) { struct entry *ep; for (; start > ROOTINO; start--) { ep = lookupino(start); if (ep == NIL || ep->e_type == NODE) continue; if (ep->e_flags & (NEW|EXTRACT)) return (start); } return (start); } /* * report on a badly formed entry */ void badentry(struct entry *ep, char *msg) { (void) fprintf(stderr, gettext("bad entry: %s\n"), msg); (void) fprintf(stderr, gettext("name: %s\n"), myname(ep)); (void) fprintf(stderr, gettext("parent name %s\n"), myname(ep->e_parent)); if (ep->e_sibling != NIL) (void) fprintf(stderr, gettext("sibling name: %s\n"), myname(ep->e_sibling)); if (ep->e_entries != NIL) (void) fprintf(stderr, gettext("next entry name: %s\n"), myname(ep->e_entries)); if (ep->e_links != NIL) (void) fprintf(stderr, gettext("next link name: %s\n"), myname(ep->e_links)); if (ep->e_xattrs != NIL) (void) fprintf(stderr, gettext("attribute root name: %s\n"), myname(ep->e_xattrs)); if (ep->e_next != NIL) (void) fprintf(stderr, gettext("next hashchain name: %s\n"), myname(ep->e_next)); (void) fprintf(stderr, gettext("entry type: %s\n"), ep->e_type == NODE ? gettext("NODE") : gettext("LEAF")); (void) fprintf(stderr, gettext("inode number: %lu\n"), ep->e_ino); panic(gettext("flags: %s\n"), flagvalues(ep)); /* Our callers are expected to handle our returning. */ } /* * Construct a string indicating the active flag bits of an entry. */ char * flagvalues(struct entry *ep) { static char flagbuf[BUFSIZ]; (void) strlcpy(flagbuf, gettext("|NIL"), sizeof (flagbuf)); flagbuf[0] = '\0'; if (ep->e_flags & REMOVED) (void) strlcat(flagbuf, gettext("|REMOVED"), sizeof (flagbuf)); if (ep->e_flags & TMPNAME) (void) strlcat(flagbuf, gettext("|TMPNAME"), sizeof (flagbuf)); if (ep->e_flags & EXTRACT) (void) strlcat(flagbuf, gettext("|EXTRACT"), sizeof (flagbuf)); if (ep->e_flags & NEW) (void) strlcat(flagbuf, gettext("|NEW"), sizeof (flagbuf)); if (ep->e_flags & KEEP) (void) strlcat(flagbuf, gettext("|KEEP"), sizeof (flagbuf)); if (ep->e_flags & EXISTED) (void) strlcat(flagbuf, gettext("|EXISTED"), sizeof (flagbuf)); if (ep->e_flags & XATTR) (void) strlcat(flagbuf, gettext("|XATTR"), sizeof (flagbuf)); if (ep->e_flags & XATTRROOT) (void) strlcat(flagbuf, gettext("|XATTRROOT"), sizeof (flagbuf)); return (&flagbuf[1]); } /* * Check to see if a name is on a dump tape. */ ino_t dirlookup(char *name) { ino_t ino; ino = psearch(name); if (ino == 0 || BIT(ino, dumpmap) == 0) (void) fprintf(stderr, gettext("%s is not on volume\n"), name); return (ino); } /* * Elicit a reply. */ int reply(char *question) { char *yesorno = gettext("yn"); /* must be two characters, "yes" first */ int c; do { (void) fprintf(stderr, "%s? [%s] ", question, yesorno); (void) fflush(stderr); c = getc(terminal); while (c != '\n' && getc(terminal) != '\n') { if (ferror(terminal)) { (void) fprintf(stderr, gettext( "Error reading response\n")); (void) fflush(stderr); return (FAIL); } if (feof(terminal)) return (FAIL); } if (isupper(c)) c = tolower(c); } while (c != yesorno[0] && c != yesorno[1]); if (c == yesorno[0]) return (GOOD); return (FAIL); } /* * handle unexpected inconsistencies */ /* * Note that a panic w/ EOF on the tty means all panics will return... */ #include /* VARARGS1 */ void panic(const char *msg, ...) { va_list args; va_start(args, msg); (void) vfprintf(stderr, msg, args); va_end(args); if (reply(gettext("abort")) == GOOD) { if (reply(gettext("dump core")) == GOOD) abort(); done(1); } } /* * Locale-specific version of ctime */ char * lctime(time_t *tp) { static char buf[256]; struct tm *tm; tm = localtime(tp); (void) strftime(buf, sizeof (buf), "%c\n", tm); return (buf); } static int statcmp(const struct stat *left, const struct stat *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) && (left->st_blksize == right->st_blksize) && (left->st_blocks == right->st_blocks)) { result = 0; } return (result); } /* * Safely open a file. */ int safe_open(int dfd, const char *filename, int mode, int perms) { static int init_syslog = 1; int fd; int working_mode; int saverr; char *errtext; struct stat pre_stat, pre_lstat; struct stat post_stat, post_lstat; if (init_syslog) { openlog(progname, LOG_CONS, LOG_DAEMON); init_syslog = 0; } /* * 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 = openat(dfd, filename, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_LARGEFILE, perms)) < 0) { if (errno == EEXIST) { if (fstatat(dfd, filename, &pre_lstat, AT_SYMLINK_NOFOLLOW) < 0) { saverr = errno; (void) close(fd); errno = saverr; return (-1); } if (fstatat(dfd, filename, &pre_stat, 0) < 0) { saverr = errno; (void) close(fd); errno = saverr; return (-1); } working_mode = mode & (O_WRONLY|O_RDWR|O_RDONLY); working_mode |= O_LARGEFILE; if ((fd = openat(dfd, filename, working_mode)) < 0) { if (errno == ENOENT) { errtext = gettext( "Unexpected condition detected: %s used to exist, but doesn't any longer\n"); (void) fprintf(stderr, errtext, filename); syslog(LOG_WARNING, errtext, filename); errno = ENOENT; } return (-1); } if (fstatat(fd, NULL, &post_lstat, AT_SYMLINK_NOFOLLOW) < 0) { saverr = errno; (void) close(fd); errno = saverr; return (-1); } if (fstatat(fd, NULL, &post_stat, 0) < 0) { saverr = errno; (void) close(fd); errno = saverr; return (-1); } if (statcmp(&pre_lstat, &post_lstat) != 0) { errtext = gettext( "Unexpected condition detected: %s's lstat(2) information changed\n"); (void) fprintf(stderr, errtext, filename); syslog(LOG_WARNING, errtext, filename); errno = EPERM; return (-1); } if (statcmp(&pre_stat, &post_stat) != 0) { errtext = gettext( "Unexpected condition detected: %s's stat(2) information changed\n"); (void) fprintf(stderr, errtext, filename); syslog(LOG_WARNING, errtext, filename); errno = EPERM; return (-1); } /* * If inode, device, or type are wrong, bail out. */ if ((!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"); (void) fprintf(stderr, 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"); (void) fprintf(stderr, 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"); (void) fprintf(stderr, errtext, filename, (long)getuid()); syslog(LOG_WARNING, errtext, filename, (long)getuid()); (void) close(fd); errno = EPERM; return (-1); } if (mode & (O_WRONLY|O_TRUNC)) { if (ftruncate(fd, (off_t)0) < 0) { (void) fprintf(stderr, "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) unlinkat(dfd, filename, 0); (void) fprintf(stderr, "open(%s): %s\n", filename, strerror(ENOENT)); (void) close(fd); errno = ENOENT; return (-1); } } return (fd); } /* * STDIO version of safe_open. Equivalent to fopen64(...). */ 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 { (void) fprintf(stderr, gettext("internal error: safe_fopen: invalid mode `%s'\n"), smode); return (NULL); } fd = safe_open(AT_FDCWD, filename, bmode, perms); /* * caller is expected to report error. */ if (fd >= 0) return (fdopen(fd, smode)); return ((FILE *)NULL); } /* * Read the contents of a directory. */ int mkentry(char *name, ino_t ino, struct arglist *ap) { struct afile *fp; if (ap->base == NULL) { ap->nent = 20; ap->base = (struct afile *)calloc((unsigned)ap->nent, sizeof (*(ap->base))); if (ap->base == NULL) { (void) fprintf(stderr, gettext("%s: out of memory\n"), ap->cmd); return (FAIL); } } if (ap->head == NULL) ap->head = ap->last = ap->base; fp = ap->last; fp->fnum = ino; fp->fname = savename(name); fp++; if (fp == ap->head + ap->nent) { ap->base = (struct afile *)realloc((char *)ap->base, (size_t)(2 * ap->nent * (size_t)sizeof (*(ap->base)))); if (ap->base == NULL) { (void) fprintf(stderr, gettext("%s: out of memory\n"), ap->cmd); return (FAIL); } ap->head = ap->base; fp = ap->head + ap->nent; ap->nent *= 2; } ap->last = fp; return (GOOD); } static int gmatch(wchar_t *, wchar_t *); static int addg(struct direct *, char *, char *, struct arglist *); /* * XXX This value is ASCII (but not language) dependent. In * ASCII, it is the DEL character (unlikely to appear in paths). * If you are compiling on an EBCDIC-based machine, re-define * this (0x7f is '"') to be something like 0x7 (DEL). It's * either this hack or re-write the expand() algorithm... */ #define DELIMCHAR ((char)0x7f) /* * Expand a file name. * "as" is the pattern to expand. * "rflg" non-zero indicates that we're recursing. * "ap" is where to put the results of the expansion. * * Our caller guarantees that "as" is at least the string ".". */ int expand(char *as, int rflg, struct arglist *ap) { int count, size; char dir = 0; char *rescan = 0; RST_DIR *dirp; char *s, *cs; int sindex, rindexa, lindex; struct direct *dp; char slash; char *rs; char c; wchar_t w_fname[PATH_MAX+1]; wchar_t w_pname[PATH_MAX+1]; /* * check for meta chars */ s = cs = as; slash = 0; while (*cs != '*' && *cs != '?' && *cs != '[') { if (*cs++ == 0) { if (rflg && slash) break; else return (0); } else if (*cs == '/') { slash++; } } for (;;) { if (cs == s) { s = ""; break; } else if (*--cs == '/') { *cs = 0; if (s == cs) s = "/"; break; } } if ((dirp = rst_opendir(s)) != NULL) dir++; count = 0; if (*cs == 0) *cs++ = DELIMCHAR; if (dir) { /* * check for rescan */ rs = cs; do { if (*rs == '/') { rescan = rs; *rs = 0; } } while (*rs++); /* LINTED: result fits into an int */ sindex = (int)(ap->last - ap->head); (void) mbstowcs(w_pname, cs, PATH_MAX); w_pname[PATH_MAX - 1] = 0; while ((dp = rst_readdir(dirp)) != NULL && dp->d_ino != 0) { if (!dflag && BIT(dp->d_ino, dumpmap) == 0) continue; if ((*dp->d_name == '.' && *cs != '.')) continue; (void) mbstowcs(w_fname, dp->d_name, PATH_MAX); w_fname[PATH_MAX - 1] = 0; if (gmatch(w_fname, w_pname)) { if (addg(dp, s, rescan, ap) < 0) { rst_closedir(dirp); return (-1); } count++; } } if (rescan) { rindexa = sindex; /* LINTED: result fits into an int */ lindex = (int)(ap->last - ap->head); if (count) { count = 0; while (rindexa < lindex) { size = expand(ap->head[rindexa].fname, 1, ap); if (size < 0) { rst_closedir(dirp); return (size); } count += size; rindexa++; } } /* LINTED: lint is confused about pointer size/type */ bcopy((void *)(&ap->head[lindex]), (void *)(&ap->head[sindex]), (size_t)((ap->last - &ap->head[rindexa])) * sizeof (*ap->head)); ap->last -= lindex - sindex; *rescan = '/'; } rst_closedir(dirp); } s = as; while ((c = *s) != '\0') *s++ = (c != DELIMCHAR ? c : '/'); return (count); } /* * Check for a name match */ static int gmatch(wchar_t *s, wchar_t *p) { long scc; /* source character to text */ wchar_t c; /* pattern character to match */ char ok; /* [x-y] range match status */ long lc; /* left character of [x-y] range */ scc = *s++; switch (c = *p++) { case '[': ok = 0; lc = -1; while (c = *p++) { if (c == ']') { return (ok ? gmatch(s, p) : 0); } else if (c == '-') { wchar_t rc = *p++; /* * Check both ends must belong to * the same codeset. */ if (wcsetno(lc) != wcsetno(rc)) { /* * If not, ignore the '-' * operator and [x-y] is * treated as if it were * [xy]. */ if (scc == lc) ok++; if (scc == (lc = rc)) ok++; } else if (lc <= scc && scc <= rc) ok++; } else { lc = c; if (scc == lc) ok++; } } /* No closing bracket => failure */ return (0); default: if (c != scc) return (0); /*FALLTHROUGH*/ case '?': return (scc ? gmatch(s, p) : 0); case '*': if (*p == 0) return (1); s--; while (*s) { if (gmatch(s++, p)) return (1); } return (0); case 0: return (scc == 0); } } /* * Construct a matched name. */ static int addg(struct direct *dp, char *as1, char *as3, struct arglist *ap) { char *s1, *s2, *limit; int c; char buf[MAXPATHLEN + 1]; s2 = buf; limit = buf + sizeof (buf) - 1; s1 = as1; while ((c = *s1++) != '\0' && s2 < limit) { if (c == DELIMCHAR) { *s2++ = '/'; break; } /* LINTED narrowing cast */ *s2++ = (char)c; } s1 = dp->d_name; while ((*s2 = *s1++) != '\0' && s2 < limit) s2++; s1 = as3; if (s1 != NULL && s2 < limit) { *s2++ = '/'; while ((*s2++ = *++s1) != '\0' && s2 < limit) { continue; /*LINTED [empty loop body]*/ } } *s2 = '\0'; if (mkentry(buf, dp->d_ino, ap) == FAIL) return (-1); return (0); } /* * Resolve a "complex" pathname (as generated by myname()) into * a file descriptor and a relative path. The file descriptor * will reference the hidden directory containing the attribute * named by the relative path. If the provided path is not * complex, the returned file descriptor will be AT_FDCWD and rpath * will equal path. * * This function is intended to be used to transform a complex * pathname into a pair of handles that can be used to actually * manipulate the named file. Since extended attributes have * an independant name space, a file descriptor for a directory * in the attribute name space is necessary to actually manipulate * the attribute file (via the path-relative xxxat() system calls * or a call to fchdir()). * * In the event of an error, the returned file descriptor will be * -1. It is expected that callers will either check for this * condition directly, or attempt to use the descriptor, fail, and * generate an appropriate context-specific error message. * * This function is pretty much a no-op for "simple" (non-attribute) * paths. */ void resolve(char *path, int *fd, char **rpath) { int tfd; *fd = tfd = AT_FDCWD; *rpath = path; path = *rpath + strlen(*rpath) +1; while (*path != '\0' && (*fd = openat64(tfd, *rpath, O_RDONLY)) > 0) { if (tfd != AT_FDCWD) (void) close(tfd); tfd = *fd; *rpath = path; path = *rpath + strlen(*rpath) +1; } if (*fd == AT_FDCWD) return; if (*fd < 0 || (*fd = openat64(tfd, ".", O_RDONLY|O_XATTR)) < 0) { int saverr = errno; (void) fprintf(stderr, gettext("Warning: cannot fully resolve %s: %s"), path, strerror(saverr)); (void) fflush(stderr); } if (tfd != AT_FDCWD) (void) close(tfd); } /* * Copy a complex pathname to another string. Note that the * length returned by this function is the number of characters * up to (but not including) the final NULL. */ int complexcpy(char *s1, char *s2, int max) { int nullseen = 0; int len = 0; while (len++ < max) { *s1++ = *s2; if (*s2++ == '\0') { if (nullseen) return (len-1); else nullseen = 1; } else { nullseen = 0; } } *s1 = '\0'; if (nullseen == 0) *--s1 = '\0'; fprintf(stderr, gettext("Warning: unterminated source string in complexcpy\n")); return (max-1); }