diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/backup | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/backup')
39 files changed, 19338 insertions, 0 deletions
diff --git a/usr/src/cmd/backup/Makefile b/usr/src/cmd/backup/Makefile new file mode 100644 index 0000000000..eb08fe8f4d --- /dev/null +++ b/usr/src/cmd/backup/Makefile @@ -0,0 +1,63 @@ +# +# 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 +# +# +#ident "%Z%%M% %I% %E% SMI" +# +# 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) + +FRC: diff --git a/usr/src/cmd/backup/Makefile.backup b/usr/src/cmd/backup/Makefile.backup new file mode 100644 index 0000000000..2848c8d555 --- /dev/null +++ b/usr/src/cmd/backup/Makefile.backup @@ -0,0 +1,96 @@ +# +# 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 +# +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright (c) 1993,1998 by Sun Microsystems, Inc. +# +# cmd/backup/Makefile.backup +# + +include $(SRC)/cmd/Makefile.cmd + +# xxx should pass -Nlevel=4 (takes 62 minutes of cpu for lib and dump) +LINTFLAGS= -errchk=%all,longptr64 -errhdr=%user -F -m \ + -Ncheck=%all,no%extern -Nlevel=3 -Xtransition=yes \ + -errtags=yes -erroff=E_UNCAL_F,E_ASGN_RESET -s +# -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 + +UFSROOTETC= $(ROOT)/etc +UFSROOTUSR= $(ROOT)/usr +UFSROOTUSRLIB= $(UFSROOTUSR)/lib +UFSROOTUSRLIBFS= $(UFSROOTUSRLIB)/fs +UFSROOTUSRLIBFSTYPE= $(UFSROOTUSRLIBFS)/$(FSTYPE) +UFSROOTUSRSBIN= $(UFSROOTUSR)/sbin + +$(CH)$(UFSROOTETC) := DIRMODE = 755 +$(UFSROOTETC) := OWNER = root +$(UFSROOTETC) := GROUP = sys +$(CH)$(UFSROOTUSR) := DIRMODE = 755 +$(UFSROOTUSR) := OWNER = root +$(UFSROOTUSR) := GROUP = bin +$(CH)$(UFSROOTUSRLIB) := DIRMODE = 755 +$(UFSROOTUSRLIB) := OWNER = root +$(UFSROOTUSRLIB) := GROUP = bin +$(CH)$(UFSROOTUSRLIBFS) := DIRMODE = 755 +$(UFSROOTUSRLIBFS) := OWNER = root +$(UFSROOTUSRLIBFS) := GROUP = sys +$(CH)$(UFSROOTUSRLIBFSFSTYPE):= DIRMODE = 755 +$(UFSROOTUSRLIBFSFSTYPE):= OWNER = root +$(UFSROOTUSRLIBFSFSTYPE):= GROUP = sys +$(CH)$(UFSROOTUSRSBIN) := DIRMODE = 755 +$(UFSROOTUSRSBIN) := OWNER = root +$(UFSROOTUSRSBIN) := GROUP = bin + +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 +lint:= TARGET= lint +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) \ + LINTFLAGS="$(LINTFLAGS)" $(TARGET) +DO_LIBDIR= @cd $(@D); pwd; $(MAKE) LINTFLAGS="$(LINTFLAGS)" $(TARGET) diff --git a/usr/src/cmd/backup/Makefile.targ b/usr/src/cmd/backup/Makefile.targ new file mode 100644 index 0000000000..900b0d4b66 --- /dev/null +++ b/usr/src/cmd/backup/Makefile.targ @@ -0,0 +1,54 @@ +# +# 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 +# +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# cmd/backup/Makefile.targ +# + +# support for debugging +debug:= DUMPLIB= libdump_g.a +debug:= CFLAGS= -g $(XESS) -DDEBUG -DFDEBUG -DTDEBUG ${SBFLAGS} +CFLAGS += $(XSTRCONST) +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 diff --git a/usr/src/cmd/backup/dump/Makefile b/usr/src/cmd/backup/dump/Makefile new file mode 100644 index 0000000000..378dbfacfb --- /dev/null +++ b/usr/src/cmd/backup/dump/Makefile @@ -0,0 +1,157 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# 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 *.ln $(POFILES) + +LOCAL= . +GENERAL= ../include +GLOBAL= ../../../head +CPPFLAGS= -I$(LOCAL) -I$(GENERAL) -I$(GLOBAL) -DPARTIAL \ + -I$(ROLLDIR) $(CPPFLAGS.master) \ + -D_LARGEFILE64_SOURCE=1 +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 +$(UFSROOTDUMPDATES):= OWNER= root +$(UFSROOTDUMPDATES):= GROUP= sys +$(LIBDIR):= DIRMODE= 0755 +$(LIBDIR):= OWNER= root +$(LIBDIR):= GROUP= sys + +FILEMODE= 04555 +OWNER= root + +.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 + +.debug/roll_log.o: + cd $(ROLLDIR) ; pwd ; $(MAKE) CFLAGS=-g + cp $(ROLLOBJS) .debug + 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 diff --git a/usr/src/cmd/backup/dump/dump.h b/usr/src/cmd/backup/dump/dump.h new file mode 100644 index 0000000000..6348824a6f --- /dev/null +++ b/usr/src/cmd/backup/dump/dump.h @@ -0,0 +1,483 @@ +/* + * Copyright 1996, 1998, 2000, 2002-2003 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 + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <locale.h> +#include <sys/types.h> +#include <ctype.h> +#include <string.h> +#include <syslog.h> +#include <errno.h> +#include <fcntl.h> +#include <utmpx.h> +#include <signal.h> +#include <stdlib.h> +#include <time.h> +#include <sys/param.h> /* for MAXBSIZE */ +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <sys/vnode.h> /* needed by inode.h */ +#include <setjmp.h> +#include <sys/mman.h> +#include <assert.h> +#include <dumpusg.h> +#include <kstat.h> +#include <sys/fssnap_if.h> +#include <libgen.h> +#include <limits.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define SUPPORTS_MTB_TAPE_FORMAT +#include <protocols/dumprestore.h> +#include <memutils.h> +#include <note.h> + +#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)) + +uint_t msiz; +uchar_t *clrmap; +uchar_t *dirmap; +uchar_t *filmap; +uchar_t *nodmap; +uchar_t *shamap; +uchar_t *activemap; + +/* + * All calculations done in 0.1" units! + */ + +char *disk; /* name of the disk file */ +char *dname; /* name to put in /etc/dumpdates */ +int disk_dynamic; /* true if disk refers to dynamic storage */ +char *tape; /* name of the tape file */ +char *host; /* name of the remote tape host (may be "user@host") */ +char *dumpdev; /* hostname:device for current volume */ +char *sdumpdev; /* short form of dumpdev (no user name if remote) */ +char *increm; /* name of file containing incremental information */ +char *filesystem; /* name of the file system */ +char *myname; /* argv[0] without leading path components */ +char lastincno; /* increment number of previous dump */ +char incno; /* increment number */ +char *tlabel; /* what goes in tape header c_label field */ +int uflag; /* update flag */ +int fi; /* disk file descriptor */ +int to; /* tape file descriptor */ +int mapfd; /* block disk device descriptor for mmap */ +int pipeout; /* true => output to standard output */ +int tapeout; /* true => output to a tape drive */ +ino_t ino; /* current inumber; used globally */ +off_t pos; /* starting offset within ino; used globally */ +int leftover; /* number of tape recs left over from prev vol */ +int nsubdir; /* counts subdirs, for deciding to dump a dir */ +int newtape; /* new tape flag */ +int nadded; /* number of added sub directories */ +int dadded; /* directory added flag */ +int density; /* density in 0.1" units */ +ulong_t tsize; /* tape size in 0.1" units */ +u_offset_t esize; /* estimated tape size, blocks */ +u_offset_t o_esize; /* number of header blocks (overhead) */ +u_offset_t f_esize; /* number of TP_BSIZE blocks for files/maps */ +uint_t etapes; /* estimated number of tapes */ +uint_t ntrec; /* 1K records per tape block */ +int tenthsperirg; /* 1/10" per tape inter-record gap */ +dev_t partial_dev; /* id of BLOCK device used in partial mode */ +pid_t dumppid; /* process-ID of top-level process */ + +int verify; /* verify each volume */ +int doingverify; /* true => doing a verify pass */ +int active; /* recopy active files */ +int doingactive; /* true => redumping active files */ +int archive; /* true => saving a archive in archivefile */ +char *archivefile; /* name of archivefile */ +int notify; /* notify operator flag */ +int diskette; /* true if dumping to a diskette */ +int cartridge; /* true if dumping to a cartridge tape */ +uint_t tracks; /* number of tracks on a cartridge tape */ +int printsize; /* just print estimated size and exit */ +int offline; /* take tape offline after rewinding */ +int autoload; /* wait for next tape to autoload; implies offline */ +int autoload_tries; /* number of times to check on autoload */ +int autoload_period; /* seconds, tries*period = total wait time */ +int doposition; /* move to specified... */ +daddr32_t filenum; /* position of dump on 1st volume */ +int dumpstate; /* dump output state (see below) */ +int dumptoarchive; /* mark records to be archived */ + +int blockswritten; /* number of blocks written on current tape */ +uint_t tapeno; /* current tape number */ + +struct fs *sblock; /* the file system super block */ +int shortmeta; /* current file has small amount of metadata */ +union u_shadow c_shadow_save[1]; + +time_t *telapsed; /* time spent writing previous tapes */ +time_t *tstart_writing; /* when we started writing the latest tape */ +time_t *tschedule; /* when next to give a remaining-time estimate */ + +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; +}; + +size_t nidates; /* number of records (might be zero) */ +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 */ diff --git a/usr/src/cmd/backup/dump/dumpfstab.c b/usr/src/cmd/backup/dump/dumpfstab.c new file mode 100644 index 0000000000..606ff20773 --- /dev/null +++ b/usr/src/cmd/backup/dump/dumpfstab.c @@ -0,0 +1,345 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#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); +} diff --git a/usr/src/cmd/backup/dump/dumpitime.c b/usr/src/cmd/backup/dump/dumpitime.c new file mode 100644 index 0000000000..41ba0e0d16 --- /dev/null +++ b/usr/src/cmd/backup/dump/dumpitime.c @@ -0,0 +1,447 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#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 + +/* + * 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(d) + 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 +#ifdef __STDC__ +inititimes(void) +#else +inititimes() +#endif +{ + 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(df) + 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 +#ifdef __STDC__ +getitime(void) +#else +getitime() +#endif +{ + 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 +#ifdef __STDC__ +putitime(void) +#else +putitime() +#endif +{ + 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, what) + 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(df, idatep) + 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(ip, buf) + 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(ip) + 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(map) + 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); +} diff --git a/usr/src/cmd/backup/dump/dumpmain.c b/usr/src/cmd/backup/dump/dumpmain.c new file mode 100644 index 0000000000..f6671313f5 --- /dev/null +++ b/usr/src/cmd/backup/dump/dumpmain.c @@ -0,0 +1,1602 @@ +/* + * Copyright 1996-1998, 2000-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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "dump.h" +#include <rmt.h> +#include <sys/mtio.h> +#include <limits.h> +#include <priv_utils.h> +#include "roll_log.h" + +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 + +main(argc, argv) + 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 */ + + 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/<pid>\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 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 ((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 == 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 == 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<<NBBY) - 1); + *clrp = i >> 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); +#ifdef lint + return (0); +#endif +} + +void +sigAbort(sig) + 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(cp) + 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(blks) + 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(sig, act))(int) + int sig; + void (*act)(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(state) + 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(state) + 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) && + (left->st_blksize == right->st_blksize) && + (left->st_blocks == right->st_blocks)) { + 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)); + } +} diff --git a/usr/src/cmd/backup/dump/dumponline.c b/usr/src/cmd/backup/dump/dumponline.c new file mode 100644 index 0000000000..5f5155e721 --- /dev/null +++ b/usr/src/cmd/backup/dump/dumponline.c @@ -0,0 +1,403 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "dump.h" +#include <math.h> +#include <limits.h> + +/* + * 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 */ +}; + +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; + +#ifdef __STDC__ +static void onsigbus(int); +#else +static void onsigbus(); +#endif + +#endif /* ENABLE_MMAP */ + +#ifdef DEBUG +extern int xflag; +#endif + +#ifdef ENABLE_MMAP /* XXX part of mmap support */ +static void +onsigbus(sig) + int sig; +{ + if (!incopy) { + dumpabort(); + /*NOTREACHED*/ + } + incopy = 0; + longjmp(truncate_buf, 1); + /*NOTREACHED*/ +} +#endif /* ENABLE_MMAP */ + +void +#ifdef __STDC__ +allocino(void) +#else +allocino() +#endif +{ + 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 +#ifdef __STDC__ +freeino(void) +#else +freeino() +#endif +{ + 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) + ino_t ino; +{ + last = ilist.id_next; + while (last && last->id_inumber < ino) + last = last->id_next; +} + +char * +unrawname(cp) + 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(devname, dirname) + char *devname; /* name of device (raw or block) */ + char *dirname; /* name of f/s mount point */ +{ + 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(fd, offset, bytes, fetch) + int fd; + off_t offset; /* offset within file */ + off_t bytes; /* number of bytes to map */ + int fetch; /* start faulting in pages */ +{ + /*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 +#ifdef __STDC__ +unmapfile(void) +#else +unmapfile() +#endif +{ + 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 +#ifdef __STDC__ +activepass(void) +#else +activepass() +#endif +{ + 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++; +} diff --git a/usr/src/cmd/backup/dump/dumpoptr.c b/usr/src/cmd/backup/dump/dumpoptr.c new file mode 100644 index 0000000000..dfab108605 --- /dev/null +++ b/usr/src/cmd/backup/dump/dumpoptr.c @@ -0,0 +1,476 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <errno.h> +#include "dump.h" + +static unsigned int timeout; /* current timeout */ +static char *attnmessage, *saveattn; /* attention message */ + +#ifdef __STDC__ +static void alarmcatch(); +static int idatesort(const void *, const void *); +#else /* !__STDC__ */ +static void alarmcatch(); +static int idatesort(); +#endif + +#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(question) + 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(s) + char *s; +{ + int total = 0; + + if (s == (char *)NULL) + return (total); + while (*s) + total += *s++; + return (total); +} + +int +query_once(question, def) + 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(); + 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(); + 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 +#ifdef __STDC__ +alarmcatch(void) +#else +alarmcatch() +#endif +{ + 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(sig) + 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(); + } +} + +/* + * We use wall(1) 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(message) + 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(force, blkswritten) + 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 <stdarg.h> + +/* 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(arg) /* w ==> just what to do; W ==> most recent dumps */ + int arg; +{ + 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(4) */ + 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(v1, v2) +#ifdef __STDC__ + const void *v1; + const void *v2; +#else + void *v1; + void *v2; +#endif +{ + 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); +} diff --git a/usr/src/cmd/backup/dump/dumptape.c b/usr/src/cmd/backup/dump/dumptape.c new file mode 100644 index 0000000000..0d3020fb50 --- /dev/null +++ b/usr/src/cmd/backup/dump/dumptape.c @@ -0,0 +1,2511 @@ +/* + * 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. + */ + +/* 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "dump.h" +#include <rmt.h> +#include <setjmp.h> +#include <sys/fdio.h> +#include <sys/mkdev.h> +#include <assert.h> +#include <limits.h> + +#define SLEEPMS 50 + +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 + +#ifdef __STDC__ +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); +#else +static void cmdwrterr(); +static void cmdrderr(); +static void freetape(); +static void bufclear(); +static pid_t setuparchive(); +static pid_t setupwriter(); +static void nextslave(); +static void tperror(); +static void rollforward(); +static void nap(); +static void alrm(); +static void just_rewind(); +static void killall(); +static void proceed(); +static void die(); +static void enslave(); +static void wait_our_turn(); +static void dumpoffline(); +static void onxfsz(); +static void dowrite(); +static void checkpoint(); +static ssize_t atomic(); +#endif + +static size_t tapesize; + +/* + * Allocate buffers and shared memory variables. Tape buffers are + * allocated on page boundaries for tape write() efficiency. + */ +void +#ifdef __STDC__ +#else +#endif +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 +#ifdef __STDC__ +freetape(void) +#else +freetape() +#endif +{ + 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 +#ifdef __STDC__ +reset(void) +#else +reset() +#endif +{ + 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 +#ifdef __STDC__ +bufclear(void) +#else +bufclear() +#endif +{ + 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 +#ifdef __STDC__ +setuparchive(void) +#else +setuparchive() +#endif +{ + 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*/ + } + + 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 */ +} + +/* + * Start a process to read the output buffers and write the data + * to the output device. + */ +static pid_t +#ifdef __STDC__ +setupwriter(void) +#else +setupwriter() +#endif +{ + 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 */ +} + +void +#ifdef __STDC__ +spclrec(void) +#else +spclrec() +#endif +{ + 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(dp, flags, size) + 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(blkno, size, offset) + 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(sig) + 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(fn, inumber) + 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(inumber) + 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 +#ifdef __STDC__ +nextslave(void) +#else +nextslave() +#endif +{ + if (++rotor >= SLAVES) { + rotor = 0; + } + slp = &slaves[rotor]; +} + +void +#ifdef __STDC__ +flushcmds(void) +#else +flushcmds() +#endif +{ + 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 +#ifdef __STDC__ +flusht(void) +#else +flusht() +#endif +{ + 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(sig) + 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(ms) + 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(sig) + int sig; +{ + longjmp(alrm_buf, 1); + /*NOTREACHED*/ +} + +void +#ifdef __STDC__ +nextdevice(void) +#else +nextdevice() +#endif +{ + 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(f) + int f; /* fd, if local device */ +{ + 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 +#ifdef __STDC__ +just_rewind(void) +#else +just_rewind() +#endif +{ + 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 +#ifdef __STDC__ +trewind(void) +#else +trewind() +#endif +{ + (void) timeclock((time_t)0); + if (offline && (!verify || doingverify)) { + close_rewind(); + } else { + just_rewind(); + if (host) + rmtclose(); + else { + (void) close(to); + to = -1; + } + } +} + +void +#ifdef __STDC__ +close_rewind(void) +#else +close_rewind() +#endif +{ + 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 +#ifdef __STDC__ +changevol(void) +#else +changevol() +#endif +{ + 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(top) + 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 +#ifdef __STDC__ +dumpabort(void) +#else +dumpabort() +#endif +{ + + if (master && master != getpid()) + /* + * signal master to call dumpabort + */ + (void) kill(master, SIGTERM); + else { + killall(); + + if (archivefile) + (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(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 +#ifdef __STDC__ +killall(void) +#else +killall() +#endif +{ + 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(sig) + int sig; +{ + caught++; +} + +/*ARGSUSED*/ +static void +die(sig) + int sig; +{ + Exit(X_FINOK); +} + +static void +#ifdef __STDC__ +enslave(void) +#else +enslave() +#endif +{ + 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 +#ifdef __STDC__ +wait_our_turn(void) +#else +wait_our_turn() +#endif +{ + (void) sighold(SIGUSR1); + + if (!caught) { +#ifdef INSTRUMENT + (*idle)++; +#endif + (void) sigpause(SIGUSR1); + } + caught = 0; + (void) sigrelse(SIGUSR1); +} + +static void +dumpoffline(cmd, next, mynum) + 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(sig) + 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(cmd) + 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(bp, cmd) + 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(func, fd, buf, count) + int (*func)(), fd, count; + char *buf; +{ + 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 +#ifdef __STDC__ +positiontape(char *msgbuf) +#else +positiontape(msgbuf) + char *msgbuf; +#endif +{ + /* 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 +#ifdef __STDC__ +cmdwrterr(void) +#else +cmdwrterr() +#endif +{ + int saverr = errno; + msg(gettext("Error writing command pipe: %s\n"), strerror(saverr)); +} + +static void +#ifdef __STDC__ +cmdrderr(void) +#else +cmdrderr() +#endif +{ + int saverr = errno; + msg(gettext("Error reading command pipe: %s\n"), strerror(saverr)); +} diff --git a/usr/src/cmd/backup/dump/dumptraverse.c b/usr/src/cmd/backup/dump/dumptraverse.c new file mode 100644 index 0000000000..44e72a5879 --- /dev/null +++ b/usr/src/cmd/backup/dump/dumptraverse.c @@ -0,0 +1,914 @@ +/* + * Copyright 1996, 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) 1980 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "dump.h" +#include <sys/file.h> +#include <sys/mman.h> + +#ifdef __STDC__ +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 *); +#else +static void lf_dmpindir(); +static void indir(); +static void lf_blksout(); +static void dsrch(); +void lf_dump(); +#endif + +static char msgbuf[256]; + +void +pass(fn, map) + 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(ip) + 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(ip) + 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(ip) + 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(ip) + 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(ip) + 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(d, n, filesize) + 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(ip) + 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(ip) + struct dinode *ip; +{ + if ((ip->di_shadow == 0) || shortmeta) + return; + + lf_dumpinode(getino((ino_t)(unsigned)(ip->di_shadow))); +} + +int +hasshortmeta(ip) + 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(ip) + 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(ip) + 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(blk, lvl, size) + 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(blkp, bytes) + 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(map, typ) + 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(d, size, filesize) + daddr32_t d; + ulong_t size; /* block 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) + 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)(((unsigned)offset)))*DEV_BSIZE), (whence)) +#endif + +#define BREAD_FAIL(buf, size) { \ + breaderrors += 1; \ + bzero(buf, (size_t)size); \ + } + + + +void +bread(da, ba, cnt) +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; + } +} diff --git a/usr/src/cmd/backup/dump/dumpusg.h b/usr/src/cmd/backup/dump/dumpusg.h new file mode 100644 index 0000000000..9bdfd289e0 --- /dev/null +++ b/usr/src/cmd/backup/dump/dumpusg.h @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#ifndef _DUMPUSG_H +#define _DUMPUSG_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Translate from BSD to System V, where possible. + */ +/* + * System-V specific header files + */ +#include <netdb.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/utsname.h> +#include <sys/statvfs.h> +#include <sys/systeminfo.h> +#include <sys/vfstab.h> +#include <sys/fs/ufs_inode.h> +#include <sys/fs/ufs_fs.h> +#include <sys/fs/ufs_fsdir.h> +#include <sys/fs/ufs_acl.h> + +#include <sys/mnttab.h> +#include <sys/vfstab.h> + +#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 */ +#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 */ diff --git a/usr/src/cmd/backup/dump/lftw.c b/usr/src/cmd/backup/dump/lftw.c new file mode 100644 index 0000000000..b5e8f4477b --- /dev/null +++ b/usr/src/cmd/backup/dump/lftw.c @@ -0,0 +1,310 @@ +/* + * 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. + */ + +/* Copyright (c) 1988 AT&T */ +/* All Rights Reserved */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* 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 <sys/fs/ufs_inode.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <errno.h> +#include <malloc.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <ftw.h> + +#define NULL 0 + +static int pwdfd; + +#ifdef __STDC__ +static int lf_xftw( + const char *, + int (*)(const char *, const struct stat64 *, int), + int, + int (*)(const char *, struct stat64 *)); +#else +static int lf_xftw(); +#endif + +#ifdef __STDC__ +lf_lftw( + const char *path, + int (*fn)(const char *, const struct stat64 *, int), + int depth) +#else +lf_lftw(path, fn, depth) + char *path; + int (*fn)(); + int depth; +#endif +{ + 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(path, fn, depth, statfn) + 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); +} diff --git a/usr/src/cmd/backup/dump/lint.sed b/usr/src/cmd/backup/dump/lint.sed new file mode 100644 index 0000000000..729f57c5ac --- /dev/null +++ b/usr/src/cmd/backup/dump/lint.sed @@ -0,0 +1,33 @@ +/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 diff --git a/usr/src/cmd/backup/dump/partial.c b/usr/src/cmd/backup/dump/partial.c new file mode 100644 index 0000000000..4481a951ad --- /dev/null +++ b/usr/src/cmd/backup/dump/partial.c @@ -0,0 +1,224 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "dump.h" +#include <ftw.h> +#include <ulimit.h> + +static int partial; + +#ifdef __STDC__ +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); +#else +static dev_t devfromopts(); +static int lf_mark_root(); +static int lf_ftw_mark(); +static void markino(); +#endif + +void +#ifdef __STDC__ +partial_check(void) +#else +partial_check() +#endif +{ + 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(mnt) + 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(argc, argv) + 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, path) + 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(name, st, flag) +#ifdef __STDC__ + const char *name; + const struct stat64 *st; +#else + char *name; + struct stat64 *st; +#endif + 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(i) + ino_t i; +{ + struct dinode *dp; + + dp = getino(ino = i); + mark(dp); +} diff --git a/usr/src/cmd/backup/dump/unctime.c b/usr/src/cmd/backup/dump/unctime.c new file mode 100644 index 0000000000..8c252e38c8 --- /dev/null +++ b/usr/src/cmd/backup/dump/unctime.c @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <time.h> +#include <string.h> +#include <stdlib.h> +/* + * 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)); +} diff --git a/usr/src/cmd/backup/include/byteorder.h b/usr/src/cmd/backup/include/byteorder.h new file mode 100644 index 0000000000..d0d72bbb49 --- /dev/null +++ b/usr/src/cmd/backup/include/byteorder.h @@ -0,0 +1,73 @@ +/* + * 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 + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <sys/fs/ufs_fsdir.h> +#include <sys/fs/ufs_acl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define SUPPORTS_MTB_TAPE_FORMAT +#include <protocols/dumprestore.h> +#include <assert.h> + +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 */ diff --git a/usr/src/cmd/backup/include/memutils.h b/usr/src/cmd/backup/include/memutils.h new file mode 100644 index 0000000000..56c7e855ea --- /dev/null +++ b/usr/src/cmd/backup/include/memutils.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef _MEMUTILS_H +#define _MEMUTILS_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <note.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define bcopy(s1, s2, len) (void) memcpy((s2), (s1), (size_t)(len)) +#define bzero(s, len) (void) memset((s), 0, (size_t)(len)) +#define bcmp(s1, s2, len) memcmp((s1), (s2), (size_t)(len)) + +#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 */ diff --git a/usr/src/cmd/backup/include/myrcmd.h b/usr/src/cmd/backup/include/myrcmd.h new file mode 100644 index 0000000000..6ef24c76c9 --- /dev/null +++ b/usr/src/cmd/backup/include/myrcmd.h @@ -0,0 +1,63 @@ +/* + * 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 + +#pragma ident "%Z%%M% %I% %E% SMI" + +#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 */ diff --git a/usr/src/cmd/backup/include/rmt.h b/usr/src/cmd/backup/include/rmt.h new file mode 100644 index 0000000000..f8657f3f18 --- /dev/null +++ b/usr/src/cmd/backup/include/rmt.h @@ -0,0 +1,64 @@ +/* + * 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 + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/mtio.h> + +#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 */ diff --git a/usr/src/cmd/backup/lib/Makefile b/usr/src/cmd/backup/lib/Makefile new file mode 100644 index 0000000000..4c1d3955d8 --- /dev/null +++ b/usr/src/cmd/backup/lib/Makefile @@ -0,0 +1,121 @@ +# +# 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. +# +# ident "%Z%%M% %I% %E% SMI" +# +# cmd/backup/lib/Makefile +# +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 + +# 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= $(YSRC) $(GLIB) *.ln + +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) +LINTOUT= lint.out +CLEANFILES= $(OBJECTS) $(LINTOUT) $(LINTLIB) $(DEBUGS) *.ln \ + $(YSRC) $(LIBRARY) +LINTFLAGS += -y + +# support for -g library +GLIB= libdump_g.a +DEBUGS= $(OBJECTS:%=.debug/%) +$(GLIB):= AROBJS = $(DEBUGS) +$(GLIB):= DIR = .debug +$(GLIB):= CFLAGS= -g $(XESS) -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) > $@ + +# rpcgen produces unused local variables that we can't easily suppress. +# It is also stupid about 32/64 bit integers. Since we don't support +# the RPC subsystem any more, just ignore complaints about it all. +# We have no control over yaccpar, and it has lots of 32/64 complaints. +# Assumes lint run with -s argument +lint: lint.out + sed -f lint.sed lint.out + +lint.out: $(LINTLIB) + +check: $(HDRS) + $(CSTYLE) $(CSTYLEFLAGS) `echo $(SRCS) | sed -e s/getdate.c//` $(HDRS) + $(HDRCHK) $(HDRCHKFLAGS) $(HDRS) + +# include library targets +include ../../../lib/Makefile.targ + +_msg: $(POFILE) diff --git a/usr/src/cmd/backup/lib/byteorder.c b/usr/src/cmd/backup/lib/byteorder.c new file mode 100644 index 0000000000..ef21cbfe55 --- /dev/null +++ b/usr/src/cmd/backup/lib/byteorder.c @@ -0,0 +1,298 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/vnode.h> +#include <locale.h> +#include <stdlib.h> +#include <sys/fs/ufs_inode.h> +#include <sys/fs/ufs_fsdir.h> +#include <sys/fs/ufs_acl.h> +#include <byteorder.h> + +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 */ + } +} diff --git a/usr/src/cmd/backup/lib/getdate.y b/usr/src/cmd/backup/lib/getdate.y new file mode 100644 index 0000000000..187739bdc7 --- /dev/null +++ b/usr/src/cmd/backup/lib/getdate.y @@ -0,0 +1,984 @@ +%{ +/* + * Copyright (c) 1998 by Sun Microsystems, Inc. + * All rights reserved. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* $OrigRevision: 2.1 $ +** +** Originally written by Steven M. Bellovin <smb@research.att.com> while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> 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 <stdio.h> +#include <ctype.h> + +#include <sys/types.h> +#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 <time.h> + +#include <locale.h> +#include <string.h> +#include <stdlib.h> +#include <note.h> +#include <libintl.h> + +#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 <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT +%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE +%type <Meridian> 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 } +}; + + + + +/* ARGSUSED */ +static void +yyerror(s) + char *s; +{ +} + + +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 */ +} + + +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) */ diff --git a/usr/src/cmd/backup/lib/lint.sed b/usr/src/cmd/backup/lib/lint.sed new file mode 100644 index 0000000000..dfd06505f6 --- /dev/null +++ b/usr/src/cmd/backup/lib/lint.sed @@ -0,0 +1,6 @@ +/^"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 diff --git a/usr/src/cmd/backup/lib/memutils.c b/usr/src/cmd/backup/lib/memutils.c new file mode 100644 index 0000000000..e46c33430b --- /dev/null +++ b/usr/src/cmd/backup/lib/memutils.c @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <errno.h> +#include <libintl.h> +#include <string.h> +#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); +} diff --git a/usr/src/cmd/backup/lib/myrcmd.c b/usr/src/cmd/backup/lib/myrcmd.c new file mode 100644 index 0000000000..a2a77c6e5b --- /dev/null +++ b/usr/src/cmd/backup/lib/myrcmd.c @@ -0,0 +1,288 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <myrcmd.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <pwd.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/file.h> +#include <signal.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <netdb.h> +#include <fcntl.h> +#include <libintl.h> + +#include <memutils.h> + +#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); +} diff --git a/usr/src/cmd/backup/lib/rmtlib.c b/usr/src/cmd/backup/lib/rmtlib.c new file mode 100644 index 0000000000..2f63049f87 --- /dev/null +++ b/usr/src/cmd/backup/lib/rmtlib.c @@ -0,0 +1,563 @@ +/*LINTLIBRARY*/ +/*PROTOLIB1*/ +/* + * Copyright 1998, 2002-2003 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. + */ + +/* line below is from UCB 5.4 12/11/85 */ +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <myrcmd.h> +#include <stdio.h> +#include <locale.h> +#include <ctype.h> +#include <pwd.h> +#include <string.h> +#include <signal.h> +#include <sys/mtio.h> +#include <sys/socket.h> +#include <unistd.h> +#include <netdb.h> +#include <locale.h> +#include <stdlib.h> +#include <errno.h> +#include <rmt.h> +#include <libintl.h> + +#define sigvec sigaction +#define sv_handler sa_handler + +#include <netinet/in.h> + +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(errmsg, errexit) + void (*errmsg)(); /* print routine */ + void (*errexit)(); /* exit routine */ +#endif +{ + print = errmsg; + Exit = errexit; +} + +rmthost(host, blocksize) + char *host; + uint_t blocksize; /* in Kbytes per tape block */ +{ + 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(sig) + 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(cp0) + 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); +} + +rmtopen(tape, mode) + 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; +} + +rmtstatus(mt) + 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(mt) + 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); +} + +rmtread(buf, count) + 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); +} + +rmtwrite(buf, count) + 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(buf, count) + 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(offset, pos) + int offset, pos; +{ + char line[80]; + + (void) snprintf(line, sizeof (line), "L%d\n%d\n", offset, pos); + return (rmtcall("seek", line)); +} + +int +rmtioctl(cmd, count) + 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(cmd) + 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(cmd, count) + 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(cmd, buf) + char *cmd, *buf; +{ + if (rmtpush(buf, strlen(buf)) != strlen(buf)) + rmtconnaborted(0); + return (rmtreply(cmd)); +} + +static int +rmtreply(cmd) + 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(cp, len) + 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 <stdarg.h> + +/* 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.h> + +/* VARARGS */ +static void +rmtmsg(va_alist) + va_dcl +{ + va_list args; + char *fmt; + + va_start(args); + fmt = va_arg(args, char *); + (void) vfprintf(stderr, fmt, args); + (void) fflush(stderr); +} +#endif diff --git a/usr/src/cmd/backup/req.flg b/usr/src/cmd/backup/req.flg new file mode 100644 index 0000000000..42e35d869a --- /dev/null +++ b/usr/src/cmd/backup/req.flg @@ -0,0 +1,33 @@ +#!/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 diff --git a/usr/src/cmd/backup/restore/Makefile b/usr/src/cmd/backup/restore/Makefile new file mode 100644 index 0000000000..37a2378b62 --- /dev/null +++ b/usr/src/cmd/backup/restore/Makefile @@ -0,0 +1,96 @@ +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# 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) *.ln $(POFILES) + +LOCAL= . +GENERAL= ../include +GLOBAL= ../../../head +CPPFLAGS= -I$(LOCAL) -I$(GENERAL) -I$(GLOBAL) \ + $(CPPFLAGS.master) -D_LARGEFILE64_SOURCE=1 +LIBDUMP= ../lib/libdump.a +LINTLIBDUMP= ../lib/llib-ldump.ln +LDLIBS += $(BSTATIC) -L../lib -ldump $(BDYNAMIC) -lsocket -lnsl + +UFSROOTLINK= $(UFSROOTUSRSBIN)/$(PROG) +LINKVALUE= ../lib/fs/$(FSTYPE)/$(PROG) + +FILEMODE= 04555 +OWNER= root + +.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 diff --git a/usr/src/cmd/backup/restore/dirs.c b/usr/src/cmd/backup/restore/dirs.c new file mode 100644 index 0000000000..5c39236dd2 --- /dev/null +++ b/usr/src/cmd/backup/restore/dirs.c @@ -0,0 +1,967 @@ +/* + * Copyright (c) 1996-2001 by Sun Microsystems, Inc. + * All rights reserved. + */ + +/* 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "restore.h" +#include <byteorder.h> +#include <stdlib.h> +#include <unistd.h> +#include <utime.h> + +/* + * 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]; +}; + +#ifdef __STDC__ +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); +#else +static ino_t search(); +static void putdir(); +static void putent(); +static void skipmetadata(); +static void flushent(); +static void dcvt(); +static RST_DIR *rst_initdirfile(); +static offset_t rst_telldir(); +static void rst_seekdir(); +static struct inotab *allocinotab(); +static void nodeflush(); +static struct inotab *inotablookup(); +#endif + +/* + * 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(genmode) + 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("<directory file - name unknown>"); + 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(pname, ino, todo) + 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(always, todo) + 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(n) + 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(inum, cp) + 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(buf, size) + 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(dp) + 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 +#ifdef __STDC__ +flushent(void) +#else +flushent() +#endif +{ + + /* 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(odp, ndp) + 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(name) + 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(name) + 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(cdirp) + RST_DIR *cdirp; +{ + if ((cdirp != NULL) && (--(cdirp->dd_refcnt) < 1)) + free(cdirp); +} + +/* + * return a pointer into a directory + */ +static offset_t +rst_telldir(tdirp) + 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(sdirp, loc, base) + RST_DIR *sdirp; + offset_t loc, 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(rdirp) + 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 +#ifdef __STDC__ +setdirmodes(void) +#else +setdirmodes() +#endif +{ + 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(f, size) + 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. + */ +genliteraldir(name, ino) + 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 + */ +inodetype(ino) + 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, dip, seekpt) + 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) + 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(exitcode) + int exitcode; +{ + + closemt(); + if (modefile[0] != '#') + (void) unlink(modefile); + if (dirfile[0] != '#') + (void) unlink(dirfile); + exit(exitcode); +} diff --git a/usr/src/cmd/backup/restore/interactive.c b/usr/src/cmd/backup/restore/interactive.c new file mode 100644 index 0000000000..04a7a8a323 --- /dev/null +++ b/usr/src/cmd/backup/restore/interactive.c @@ -0,0 +1,1034 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <setjmp.h> +#include <euc.h> +#include <widec.h> +#include "restore.h" +#include <ctype.h> +#include <limits.h> +#include <sys/wait.h> + +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 */ + +#ifdef __STDC__ +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 **); +#else +static void getcmd(); +static void expandarg(); +static void printlist(); +static void formatf(); +static char *copynext(); +static int fcmp(); +static char *fmtentry(); +static void setpagercmd(); +static uint_t setpagerargs(); +#endif + +/* + * Read and execute commands from the terminal. + */ +void +#ifdef __STDC__ +runcmdshell(void) +#else +runcmdshell() +#endif +{ + 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(curdir, cmd, cmdsiz, name, namesiz, ap) + char *curdir, *cmd, *name; + size_t cmdsiz, 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(input, output, outsize) + char *input, *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(rawname, canonname, limit) + char *rawname, *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(arg, ap) + 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(name, ino, basename, marked_only) + 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(ap) + 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(f1, f2) + struct afile *f1, *f2; +{ + + return (strcoll(f1->fname, f2->fname)); +} + +/* + * Format a directory entry. + */ +static char * +fmtentry(fp) + 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(sig) + 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 +#ifdef __STDC__ +initpagercmd(void) +#else +initpagercmd() +#endif +{ + 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 +#ifdef __STDC__ +setpagercmd(void) +#else +setpagercmd() +#endif +{ + 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(source) + 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); +} diff --git a/usr/src/cmd/backup/restore/lint.sed b/usr/src/cmd/backup/restore/lint.sed new file mode 100644 index 0000000000..bbe85689be --- /dev/null +++ b/usr/src/cmd/backup/restore/lint.sed @@ -0,0 +1,14 @@ +/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 diff --git a/usr/src/cmd/backup/restore/main.c b/usr/src/cmd/backup/restore/main.c new file mode 100644 index 0000000000..b1dc21fb9d --- /dev/null +++ b/usr/src/cmd/backup/restore/main.c @@ -0,0 +1,583 @@ +/* + * 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) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * 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 <signal.h> +#include <byteorder.h> +#include <priv_utils.h> + +#include <euc.h> +#include <getwidth.h> +#include <sys/mtio.h> +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); + +main(argc, argv) + 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++; + /*FALLTHROUGH*/ + 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 */ + } +#ifdef lint + return (0); +#endif +} + +/* + * 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); + } +} diff --git a/usr/src/cmd/backup/restore/restore.c b/usr/src/cmd/backup/restore/restore.c new file mode 100644 index 0000000000..27c5900530 --- /dev/null +++ b/usr/src/cmd/backup/restore/restore.c @@ -0,0 +1,1049 @@ +/* + * Copyright 1996, 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) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "restore.h" +/* undef MAXNAMLEN to prevent compiler warnings about redef in dirent.h */ +#undef MAXNAMLEN +#include <dirent.h> + +#ifdef __STDC__ +static char *keyval(int); +static void removexattrs(struct entry *); +static void movexattrs(char *, char *); +#else +static char *keyval(); +static void removexattrs(); +static void movexattrs(); +#endif + +/* + * This implements the 't' option. + * List entries on the tape. + */ +long +listfile(name, ino, type) + 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(name, ino, type) + 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(name, ino, type) + 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 +#ifdef __STDC__ +removeoldleaves(void) +#else +removeoldleaves() +#endif +{ + 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(name, ino, type) + 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) { + 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) { + removeleaf(np); + freeentry(np); + } else { + 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: + if (type == LEAF && lookuptype != LINK) { + /* 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(key) + 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 +#ifdef __STDC__ +findunreflinks(void) +#else +findunreflinks() +#endif +{ + 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 +#ifdef __STDC__ +removeoldnodes(void) +#else +removeoldnodes() +#endif +{ + 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(symtabfile) + 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 +#ifdef __STDC__ +createfiles(void) +#else +createfiles() +#endif +{ + 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 +#ifdef __STDC__ +createlinks(void) +#else +createlinks() +#endif +{ + 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 +#ifdef __STDC__ +checkrestore(void) +#else +checkrestore() +#endif +{ + 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(name, ino, type) + 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(ep) + 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(orig, targ) + 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); +} diff --git a/usr/src/cmd/backup/restore/restore.h b/usr/src/cmd/backup/restore/restore.h new file mode 100644 index 0000000000..1151d07939 --- /dev/null +++ b/usr/src/cmd/backup/restore/restore.h @@ -0,0 +1,423 @@ +/* + * 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 1994, 1996, 1998-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _RESTORE_H +#define _RESTORE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <string.h> +#include <malloc.h> +#include <netdb.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/vnode.h> +#include <locale.h> +#include <stdlib.h> +#include <sys/fs/ufs_inode.h> +#include <sys/fs/ufs_fs.h> +#include <sys/fs/ufs_fsdir.h> +#include <note.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define ROOTINO UFSROOTINO +#define SUPPORTS_MTB_TAPE_FORMAT +#include <protocols/dumprestore.h> +#include <memutils.h> +#include <assert.h> + +/* + * 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 + */ +#ifdef __STDC__ +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 *); +#else +extern struct entry *lookupino(); +extern struct entry *lookupname(); +extern struct entry *addentry(); +extern void deleteino(); +extern char *myname(); +extern void freeentry(); +extern void moveentry(); +extern char *savename(); +extern void freename(); +extern void dumpsymtable(); +extern void initsymtable(); +extern void mktempname(); +extern char *gentempname(); +extern void newnode(); +extern void removenode(); +extern void removeleaf(); +extern ino_t lowerbnd(); +extern ino_t upperbnd(); +extern void badentry(); +extern char *flagvalues(); +extern ino_t dirlookup(); +#endif +#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 + */ +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); +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(void); +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 + +#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 DEF_PAGER "/usr/bin/more" + +#ifdef __cplusplus +} +#endif + +#endif /* _RESTORE_H */ diff --git a/usr/src/cmd/backup/restore/symtab.c b/usr/src/cmd/backup/restore/symtab.c new file mode 100644 index 0000000000..e98df9e567 --- /dev/null +++ b/usr/src/cmd/backup/restore/symtab.c @@ -0,0 +1,831 @@ +/* + * 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * 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 <limits.h> + +/* + * 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; + +#ifdef __STDC__ +static void addino(ino_t, struct entry *); +static struct entry *lookupparent(char *); +static void removeentry(struct entry *); +#else +static void addino(); +static struct entry *lookupparent(); +static void removeentry(); +#endif + +/* + * Look up an entry by inode number + */ +struct entry * +lookupino(inum) + 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(inum, np) + 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(inum) + 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(name) + 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(name) + 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: + * + * ./<path>/entry\0<path>/attrentry\0<path>/...\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(ep) + 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(name, inum, type) + 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(ep) + 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(ep, newname) + 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(ep) + 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(name) + 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(name) + 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(filename, checkpt) + 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(filename) + 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 = (struct entry **) + /* LINTED entrytblsize fits in a size_t */ + calloc((size_t)entrytblsize, sizeof (*entry)); + if (entry == (struct entry **)NULL) { + (void) fprintf(stderr, + gettext("no memory for entry table\n")); + done(1); + } + ep = addentry(".", ROOTINO, NODE); + /* 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]; + } +} diff --git a/usr/src/cmd/backup/restore/tape.c b/usr/src/cmd/backup/restore/tape.c new file mode 100644 index 0000000000..9b2746d859 --- /dev/null +++ b/usr/src/cmd/backup/restore/tape.c @@ -0,0 +1,2189 @@ +/* + * 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 1994, 1996, 1998-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <setjmp.h> +#include "restore.h" +#include <byteorder.h> +#include <rmt.h> +#include <sys/mtio.h> +#include <utime.h> +#include <sys/errno.h> +#include <sys/fdio.h> +#include <sys/sysmacros.h> /* for expdev */ +#include <assert.h> +#include <limits.h> +#include <priv_utils.h> + +#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; +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 */ + +#ifdef __STDC__ +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 checktype(struct s_spcl *, int); +static void metaset(char *name); +#else +static int autoload_tape(); +static void setdumpnum(); +static void metacheck(); +static void xtrmeta(); +static void metaskip(); +static void xtrfile(); +static void xtrskip(); +static void xtrlnkfile(); +static void xtrlnkskip(); +static void xtrmap(); +static void xtrmapskip(); +static void readtape(); +static int checkvol(); +static void accthdr(); +static int ishead(); +static checktype(); +static void metaset(); +#endif + +/* + * Set up an input source + */ +void +setinput(source, archive) + 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) + 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 +#ifdef __STDC__ +setup(void) +#else +setup() +#endif +{ + 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; + } + if (((fssize - 1) & fssize) != 0) { + (void) fprintf(stderr, + gettext("bad filesystem block size %d\n"), fssize); + done(1); + } + 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 +#ifdef __STDC__ +setupR(void) +#else +setupR() +#endif +{ + 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; + } + if (((fssize - 1) & fssize) != 0) { + (void) fprintf(stderr, + gettext("bad filesystem block size %d\n"), fssize); + done(1); + } +} + +/* + * Prompt user to load a new dump volume. + * "Nextvol" is the next suggested volume to use. + * This suggested volume is enforced when doing full + * or incremental restores, but can be 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(nextvol) + 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()) { + 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(); + /* + * 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 +#ifdef __STDC__ +setdumpnum(void) +#else +setdumpnum() +#endif +{ + 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 +#ifdef __STDC__ +printdumpinfo(void) +#else +printdumpinfo() +#endif +{ + 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]); + } +} + +extractfile(name) + 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); + ofile = creat64(rname, 0666); + + 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; + } + 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; + } + getfile(xtrfile, xtrskip); + metaset(rname); /* we don't have metadata until after */ + /* getfile() - maybe fchmod(0) then */ + /* fchmod(real) after this? */ + + /* + * 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 +#ifdef __STDC__ +skipmaps(void) +#else +skipmaps() +#endif +{ + continuemap = 1; + while (checktype(&spcl, TS_CLRI) == GOOD || + checktype(&spcl, TS_BITS) == GOOD) + skipfile(); + continuemap = 0; +} + +/* + * skip over a file on the tape + */ +void +#ifdef __STDC__ +skipfile(void) +#else +skipfile() +#endif +{ + curfile.action = SKIP; + getfile(null, null); +} +/* + * Do the file extraction, calling the supplied functions + * with the blocks + */ +void +getfile(f1, f2) + void (*f2)(), (*f1)(); +{ + 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(buf, size) + 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(buf, size) + 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(head) + 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(buf, size) + 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(buf, size) + 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(name) + char *name; +{ + if (metadata == NULL) + return; + if (name != NULL) + metaproc(name, metadata, metasize); + (void) free(metadata); + metadata = NULL; + metasize = 0; +} + +void +metaget(data, size) + char **data; + size_t *size; +{ + *data = metadata; + *size = metasize; +} + +static void +fsd_acl(name, aclp, size) + char *name, *aclp; + unsigned size; +{ + static aclent_t *aclent = NULL; + ufs_acl_t *diskacl; + static int n = 0; + 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; + } + + if (acl(name, SETACL, n, aclent) == -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)); + } + } +} + +static struct fsdtypes { + int type; + void (*function)(); +} fsdtypes[] = { + {FSD_ACL, fsd_acl}, + {FSD_DFACL, fsd_acl}, + {0, NULL} +}; + +void +metaproc(name, mdata, msize) + char *name, *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(buf, size) + 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(buf, size) + char *buf; + size_t size; +{ + (void) fprintf(stderr, + gettext("unallocated block in symbolic link %s\n"), + curfile.name); + done(1); +} + +static void +xtrmap(buf, size) + 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(buf, size) + char *buf; + size_t size; +{ + (void) fprintf(stderr, gettext("hole in map\n")); + done(1); +} + +/*ARGSUSED*/ +void +null(buf, size) + char *buf; + size_t size; +{ +} + +/* + * Do the tape i/o, dealing with volume changes + * etc.. + */ +static void +readtape(b) + 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 +#ifdef __STDC__ +findtapeblksize(int arfile) +#else +findtapeblksize(arfile) +int arfile; +#endif +{ + 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 +#ifdef __STDC__ +flsht(void) +#else +flsht() +#endif +{ + /* LINTED unsigned/signed assignment ok */ + bct = ntrec+1; +} + +void +#ifdef __STDC__ +closemt(void) +#else +closemt() +#endif +{ + static struct mtop mtop = { MTOFFL, 0 }; + + if (mt < 0) + return; + if (offline) + (void) fprintf(stderr, gettext("Rewinding tape\n")); + if (host) { + if (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) + (void) ioctl(mt, MTIOCTOP, &mtop); + (void) ioctl(mt, FDEJECT, 0); + (void) close(mt); + } + mt = -1; +} + +static int +checkvol(b, t) + struct s_spcl *b; + int t; +{ + + if (b->c_volume != t) + return (FAIL); + return (GOOD); +} + +readhdr(b) + 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. + */ +gethead(buf) + 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(header) + 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. + */ +volnumber(inum) + 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(header) + struct s_spcl *header; +{ + long skipcnt = 0; + int i; + char buf[TP_BSIZE_MAX]; + + curfile.name = gettext("<name unknown>"); + 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("<file removal list>"); + curfile.ts = TS_CLRI; + break; + } + if (checktype(header, TS_BITS) == GOOD) { + curfile.name = gettext("<file dump list>"); + 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(buf) + struct s_spcl *buf; +{ + if (buf->c_magic != + ((tp_bsize == TP_BSIZE_MIN) ? NFS_MAGIC : MTB_MAGIC)) + return (FAIL); + return (GOOD); +} + +static +checktype(b, t) + 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 +#ifdef __STDC__ +autoload_tape(void) +#else +autoload_tape() +#endif +{ + 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). + */ + closemt(); + (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); +} diff --git a/usr/src/cmd/backup/restore/utilities.c b/usr/src/cmd/backup/restore/utilities.c new file mode 100644 index 0000000000..c81113001f --- /dev/null +++ b/usr/src/cmd/backup/restore/utilities.c @@ -0,0 +1,1188 @@ +/* + * Copyright 2004 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. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "restore.h" +#include <ctype.h> +#include <errno.h> +#include <syslog.h> +#include <limits.h> +/* LINTED: this file really is necessary */ +#include <euc.h> +#include <widec.h> + +/* + * 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(name) + 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(ep) + 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(ep) + 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(fp, tp) + 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] = NULL; + 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(np) + 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(ep) + 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(ep) + 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). + */ +lf_linkit(existing, new, type) + char *existing, *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(start) + 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(start) + 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(ep, msg) + 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(ep) + 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(name) + 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. + */ +reply(question) + 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... + */ +#ifdef __STDC__ +#include <stdarg.h> + +/* 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); + } +} +#else +#include <varargs.h> + +/* VARARGS1 */ +void +panic(va_alist) + va_dcl +{ + va_list args; + char *msg; + + va_start(args); + msg = va_arg(args, char *); + (void) vfprintf(stderr, msg, args); + va_end(args); + if (reply(gettext("abort")) == GOOD) { + if (reply(gettext("dump core")) == GOOD) + abort(); + done(1); + } +#endif + +/* + * Locale-specific version of ctime + */ +char * +lctime(tp) + 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(name, ino, ap) + 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); +} + +#ifdef __STDC__ +static int gmatch(wchar_t *, wchar_t *); +static int addg(struct direct *, char *, char *, struct arglist *); +#else +static int gmatch(); +static int addg(); +#endif + +/* + * 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(as, rflg, ap) + 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(s, p) + wchar_t *s; /* string to test */ + wchar_t *p; /* pattern to match against */ +{ + 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(dp, as1, as3, ap) + struct direct *dp; /* The directory containing the name */ + char *as1; /* The current directory */ + char *as3; /* The file name in dp */ + struct arglist *ap; /* Where to append the new name */ +{ + 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(path, fd, rpath) + 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(s1, s2, max) + 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); +} |