summaryrefslogtreecommitdiff
path: root/usr/src/cmd/backup
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/backup
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/backup')
-rw-r--r--usr/src/cmd/backup/Makefile63
-rw-r--r--usr/src/cmd/backup/Makefile.backup96
-rw-r--r--usr/src/cmd/backup/Makefile.targ54
-rw-r--r--usr/src/cmd/backup/dump/Makefile157
-rw-r--r--usr/src/cmd/backup/dump/dump.h483
-rw-r--r--usr/src/cmd/backup/dump/dumpfstab.c345
-rw-r--r--usr/src/cmd/backup/dump/dumpitime.c447
-rw-r--r--usr/src/cmd/backup/dump/dumpmain.c1602
-rw-r--r--usr/src/cmd/backup/dump/dumponline.c403
-rw-r--r--usr/src/cmd/backup/dump/dumpoptr.c476
-rw-r--r--usr/src/cmd/backup/dump/dumptape.c2511
-rw-r--r--usr/src/cmd/backup/dump/dumptraverse.c914
-rw-r--r--usr/src/cmd/backup/dump/dumpusg.h114
-rw-r--r--usr/src/cmd/backup/dump/lftw.c310
-rw-r--r--usr/src/cmd/backup/dump/lint.sed33
-rw-r--r--usr/src/cmd/backup/dump/partial.c224
-rw-r--r--usr/src/cmd/backup/dump/unctime.c95
-rw-r--r--usr/src/cmd/backup/include/byteorder.h73
-rw-r--r--usr/src/cmd/backup/include/memutils.h60
-rw-r--r--usr/src/cmd/backup/include/myrcmd.h63
-rw-r--r--usr/src/cmd/backup/include/rmt.h64
-rw-r--r--usr/src/cmd/backup/lib/Makefile121
-rw-r--r--usr/src/cmd/backup/lib/byteorder.c298
-rw-r--r--usr/src/cmd/backup/lib/getdate.y984
-rw-r--r--usr/src/cmd/backup/lib/lint.sed6
-rw-r--r--usr/src/cmd/backup/lib/memutils.c84
-rw-r--r--usr/src/cmd/backup/lib/myrcmd.c288
-rw-r--r--usr/src/cmd/backup/lib/rmtlib.c563
-rw-r--r--usr/src/cmd/backup/req.flg33
-rw-r--r--usr/src/cmd/backup/restore/Makefile96
-rw-r--r--usr/src/cmd/backup/restore/dirs.c967
-rw-r--r--usr/src/cmd/backup/restore/interactive.c1034
-rw-r--r--usr/src/cmd/backup/restore/lint.sed14
-rw-r--r--usr/src/cmd/backup/restore/main.c583
-rw-r--r--usr/src/cmd/backup/restore/restore.c1049
-rw-r--r--usr/src/cmd/backup/restore/restore.h423
-rw-r--r--usr/src/cmd/backup/restore/symtab.c831
-rw-r--r--usr/src/cmd/backup/restore/tape.c2189
-rw-r--r--usr/src/cmd/backup/restore/utilities.c1188
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 *)&notactive,
+ 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(&current);
+ tm = localtime(&current);
+ memcpy(&ltm, tm, sizeof(ltm));
+ tm = &ltm;
+
+ (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);
+}