Newsgroups: comp.sources.misc From: uunet!sawmill!prslnk!buhrt (Jeff Buhrt) Subject: v23i033: afio - Manipulate CPIO-format archive and files, Part01/02 Message-ID: X-Md4-Signature: 62361e8d4feb7291fd6adcc3067cc8dc Date: Sun, 29 Sep 1991 03:34:42 GMT Approved: kent@sparky.imd.sterling.com Submitted-by: uunet!sawmill!prslnk!buhrt (Jeff Buhrt) Posting-number: Volume 23, Issue 33 Archive-name: afio/part01 Environment: UNIX This is afio version 2.2. Afio is a public domain version of cpio with many other features. Afio nicely supports floppy disk backups by verifying and allowing restarting the current disk, (un)compressing files as they are passed though, somewhat gracefully handling input data corruption, and many other features (see afio.1). -Jeff Buhrt {sequent,uunet}!sawmill!prslnk!buhrt 812-275-0750 work Proslink, Inc. ---- #!/bin/sh # This is a shell archive (produced by shar 3.49) # To extract the files from this archive, save it to a file, remove # everything above the "!/bin/sh" line above, and type "sh file_name". # # existing files will NOT be overwritten unless -c is specified # # This is part 1 of a multipart archive # do not concatenate these parts, unpack them in order with /bin/sh # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 1932 -r--r--r-- Makefile # 1932 -r--r--r-- Makefile.3b1 # 1979 -rw-r--r-- Makefile.at386 # 1978 -rw-r--r-- Makefile.sco # 687 -r--r--r-- README # 7640 -r--r--r-- afio.1 # 76841 -r--r--r-- afio.c # 809 -r-xr-xr-x dodsk # 137 -rw-rw-rw- patchlevel.h # if test -r _shar_seq_.tmp; then echo 'Must unpack archives in sequence!' echo Please unpack part `cat _shar_seq_.tmp` next exit 1 fi # ============= Makefile ============== if test -f 'Makefile' -a X"$1" != X"-c"; then echo 'x - skipping Makefile (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting Makefile (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Makefile' && SHELL=/bin/sh ## ## I wrote this Makefile, based on comments in the source. -rich $alz. ## Define INDEX to use index() in place of strchr() (v7, BSD). 1 = -DINDEX #1 = -UINDEX ## Define MEMCPY when an efficient memcpy() exists (SysV). #2 = -DMEMCPY 2 = -UMEMCPY ## Define MKDIR when a mkdir() system call is present (4.2BSD, SysVr3). 3 = -DMKDIR #3 = -UMKDIR ## Define NOVOID if your compiler doesn't like void casts. #4 = -DNOVOID 4 = -UNOVOID ## Define SYSTIME to use rather than (4.2BSD). 5 = -DSYSTIME #5 = -USYSTIME ## Define VOIDFIX to allow pointers to functions returning void (non-PCC). #6 = -DVOIDFIX 6 = -UVOIDFIX ## Define CTC3B2 to support AT&T 3B2 streaming cartridge tape. #7 = -DCTC3B2 7 = -UCTC3B2 ## Define HAVEFCNTL if you have 8 = -DHAVEFCNTL #8 = -UHAVEFCNTL ## Define USESHMEM if you have shared memory #9 = -DUSESHMEM 9 = -UUSESHMEM ## Define MYTEMPNAM if you don't have tempnam() a = -DMYTEMPNAM #a = -UMYTEMPNAM ## Define UNIXPC if you are on a 3b1, 7300, etc. ## (problem is you can't write to a floppy from shared memory) #b = -DUNIXPC b = -UUNIXPC ## Define HAVEMEMCMP if you have memcmp otherwise assumes bcmp #c = -DHAVEMEMCMP c = -UHAVEMEMCMP ## Define DEFFMTCMD to being how to format the media you use the most ## This is the DEFault FoRMat CoManD. d = -DDEFFMTCMD='"/bin/false"' #d = -DDEFFMTCMD='"format.sh"' #d = -DDEFFMTCMD='"iv -i /dev/rfp020 /usr/lib/iv/FDnl"' ## Define LONGZFILE if you want .Z to be tagged on the end of a 14 char ## file name (or longer for BSD) in the archive when the file is compressed e = -DLONGZFILE #e = -ULONGZFILE X CC=cc CFLAGS = $1 $2 $3 $4 $5 $6 $7 $8 $9 $a $b $c $d $e -O LDFLAGS = -O X all: afio afio.1 X afio: afio.c X $(CC) $(CFLAGS) -o afio afio.c X shar: README Makefile afio.1 afio.c X shar README Makefile afio.1 afio.c > afio.shar X install: all X @echo copy afio and afio.1 into the appropriate directories. SHAR_EOF chmod 0444 Makefile || echo 'restore of Makefile failed' Wc_c="`wc -c < 'Makefile'`" test 1932 -eq "$Wc_c" || echo 'Makefile: original size 1932, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= Makefile.3b1 ============== if test -f 'Makefile.3b1' -a X"$1" != X"-c"; then echo 'x - skipping Makefile.3b1 (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting Makefile.3b1 (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Makefile.3b1' && CC=ccc SHELL=/bin/sh ## ## I wrote this Makefile, based on comments in the source. -rich $alz. ## Define INDEX to use index() in place of strchr() (v7, BSD). #1 = -DINDEX 1 = -UINDEX ## Define MEMCPY when an efficient memcpy() exists (SysV). 2 = -DMEMCPY #2 = -UMEMCPY ## Define MKDIR when a mkdir() system call is present (4.2BSD, SysVr3). #3 = -DMKDIR 3 = -UMKDIR ## Define NOVOID if your compiler doesn't like void casts. #4 = -DNOVOID 4 = -UNOVOID ## Define SYSTIME to use rather than (4.2BSD). #5 = -DSYSTIME 5 = -USYSTIME ## Define VOIDFIX to allow pointers to functions returning void (non-PCC). #6 = -DVOIDFIX 6 = -UVOIDFIX ## Define CTC3B2 to support AT&T 3B2 streaming cartridge tape. #7 = -DCTC3B2 7 = -UCTC3B2 ## Define HAVEFCNTL if you have 8 = -DHAVEFCNTL #8 = -UHAVEFCNTL ## Define USESHMEM if you have shared memory 9 = -DUSESHMEM #9 = -UUSESHMEM ## Define MYTEMPNAM if you don't have tempnam() #a = -DMYTEMPNAM a = -UMYTEMPNAM ## Define UNIXPC if you are on a 3b1, 7300, etc. ## (problem is you can't write to a floppy from shared memory) b = -DUNIXPC #b = -UUNIXPC ## Define HAVEMEMCMP if you have memcmp otherwise assumes bcmp c = -DHAVEMEMCMP #c = -UHAVEMEMCMP ## Define DEFFMTCMD to being how to format the media you use the most ## This is the DEFault FoRMat CoManD. #d = -DDEFFMTCMD='"/bin/false"' #d = -DDEFFMTCMD='"format.sh"' d = -DDEFFMTCMD=\""iv -i /dev/rfp020 /usr/lib/iv/FDnl"\" ## Define LONGZFILE if you want .Z to be tagged on the end of a 14 char ## file name (or longer for BSD) in the archive when the file is compressed e = -DLONGZFILE #e = -ULONGZFILE X CFLAGS = -O $1 $2 $3 $4 $5 $6 $7 $8 $9 $a $b $c $d $e LDFLAGS = X all: afio afio.1 X afio: afio.c X $(CC) $(CFLAGS) -o afio afio.c X shar: README Makefile afio.1 afio.c X shar README Makefile afio.1 afio.c > afio.shar X install: all X @echo copy afio and afio.1 into the appropriate directories. SHAR_EOF chmod 0444 Makefile.3b1 || echo 'restore of Makefile.3b1 failed' Wc_c="`wc -c < 'Makefile.3b1'`" test 1932 -eq "$Wc_c" || echo 'Makefile.3b1: original size 1932, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= Makefile.at386 ============== if test -f 'Makefile.at386' -a X"$1" != X"-c"; then echo 'x - skipping Makefile.at386 (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting Makefile.at386 (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Makefile.at386' && SHELL=/bin/sh ## ## I wrote this Makefile, based on comments in the source. -rich $alz. ## Define INDEX to use index() in place of strchr() (v7, BSD). #1 = -DINDEX 1 = -UINDEX ## Define MEMCPY when an efficient memcpy() exists (SysV). 2 = -DMEMCPY #2 = -UMEMCPY ## Define MKDIR when a mkdir() system call is present (4.2BSD, SysVr3). 3 = -DMKDIR #3 = -UMKDIR ## Define NOVOID if your compiler doesn't like void casts. #4 = -DNOVOID 4 = -UNOVOID ## Define SYSTIME to use rather than (4.2BSD). #5 = -DSYSTIME 5 = -USYSTIME ## Define VOIDFIX to allow pointers to functions returning void (non-PCC). 6 = -DVOIDFIX #6 = -UVOIDFIX ## Define CTC3B2 to support AT&T 3B2 streaming cartridge tape. #7 = -DCTC3B2 7 = -UCTC3B2 ## Define HAVEFCNTL if you have 8 = -DHAVEFCNTL #8 = -UHAVEFCNTL ## Define USESHMEM if you have shared memory #9 = -DUSESHMEM 9 = -UUSESHMEM ## Define MYTEMPNAM if you don't have tempnam() #a = -DMYTEMPNAM a = -UMYTEMPNAM ## Define UNIXPC if you are on a 3b1, 7300, etc. ## (problem is you can't write to a floppy from shared memory) #b = -DUNIXPC b = -UUNIXPC ## Define HAVEMEMCMP if you have memcmp otherwise assumes bcmp c = -DHAVEMEMCMP #c = -UHAVEMEMCMP ## Define DEFFMTCMD to being how to format the media you use the most ## This is the DEFault FoRMat CoManD. #d = -DDEFFMTCMD='"/bin/false"' d = -DDEFFMTCMD='"format -f /dev/rdsk/f03ht"' #d = -DDEFFMTCMD='"format.sh"' #d = -DDEFFMTCMD='"iv -i /dev/rfp020 /usr/lib/iv/FDnl"' ## Define LONGZFILE if you want .Z to be tagged on the end of a 14 char ## file name (or longer for BSD) in the archive when the file is compressed e = -DLONGZFILE #e = -ULONGZFILE X CC=cc CFLAGS = $1 $2 $3 $4 $5 $6 $7 $8 $9 $a $b $c $d $e -O LDFLAGS = -s X all: afio afio.1 X afio: afio.c X $(CC) $(CFLAGS) -o afio afio.c X shar: README Makefile afio.1 afio.c X shar README Makefile afio.1 afio.c > afio.shar X install: all X @echo copy afio and afio.1 into the appropriate directories. SHAR_EOF chmod 0644 Makefile.at386 || echo 'restore of Makefile.at386 failed' Wc_c="`wc -c < 'Makefile.at386'`" test 1979 -eq "$Wc_c" || echo 'Makefile.at386: original size 1979, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= Makefile.sco ============== if test -f 'Makefile.sco' -a X"$1" != X"-c"; then echo 'x - skipping Makefile.sco (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting Makefile.sco (Text)' sed 's/^X//' << 'SHAR_EOF' > 'Makefile.sco' && SHELL=/bin/sh ## ## I wrote this Makefile, based on comments in the source. -rich $alz. ## Define INDEX to use index() in place of strchr() (v7, BSD). #1 = -DINDEX 1 = -UINDEX ## Define MEMCPY when an efficient memcpy() exists (SysV). 2 = -DMEMCPY #2 = -UMEMCPY ## Define MKDIR when a mkdir() system call is present (4.2BSD, SysVr3). 3 = -DMKDIR #3 = -UMKDIR ## Define NOVOID if your compiler doesn't like void casts. #4 = -DNOVOID 4 = -UNOVOID ## Define SYSTIME to use rather than (4.2BSD). #5 = -DSYSTIME 5 = -USYSTIME ## Define VOIDFIX to allow pointers to functions returning void (non-PCC). 6 = -DVOIDFIX #6 = -UVOIDFIX ## Define CTC3B2 to support AT&T 3B2 streaming cartridge tape. #7 = -DCTC3B2 7 = -UCTC3B2 ## Define HAVEFCNTL if you have 8 = -DHAVEFCNTL #8 = -UHAVEFCNTL ## Define USESHMEM if you have shared memory #9 = -DUSESHMEM 9 = -UUSESHMEM ## Define MYTEMPNAM if you don't have tempnam() #a = -DMYTEMPNAM a = -UMYTEMPNAM ## Define UNIXPC if you are on a 3b1, 7300, etc. ## (problem is you can't write to a floppy from shared memory) #b = -DUNIXPC b = -UUNIXPC ## Define HAVEMEMCMP if you have memcmp otherwise assumes bcmp c = -DHAVEMEMCMP #c = -UHAVEMEMCMP ## Define DEFFMTCMD to being how to format the media you use the most ## This is the DEFault FoRMat CoManD. #d = -DDEFFMTCMD='"/bin/false"' d = -DDEFFMTCMD='"format -f /dev/rfd1"' #d = -DDEFFMTCMD='"format.sh"' #d = -DDEFFMTCMD='"iv -i /dev/rfp020 /usr/lib/iv/FDnl"' ## Define LONGZFILE if you want .Z to be tagged on the end of a 14 char ## file name (or longer for BSD) in the archive when the file is compressed e = -DLONGZFILE #e = -ULONGZFILE X CC=gcc CFLAGS = $1 $2 $3 $4 $5 $6 $7 $8 $9 $a $b $c $d $e -O -g LDFLAGS = -g X all: afio afio.1 X afio: afio.c X $(CC) $(CFLAGS) -o afio afio.c X shar: README Makefile afio.1 afio.c X shar README Makefile afio.1 afio.c > afio.shar X install: all X @echo copy afio and afio.1 into the appropriate directories. SHAR_EOF chmod 0644 Makefile.sco || echo 'restore of Makefile.sco failed' Wc_c="`wc -c < 'Makefile.sco'`" test 1978 -eq "$Wc_c" || echo 'Makefile.sco: original size 1978, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= README ============== if test -f 'README' -a X"$1" != X"-c"; then echo 'x - skipping README (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting README (Text)' sed 's/^X//' << 'SHAR_EOF' > 'README' && X Afio is a better way of dealing with cpio-format archives. It is generally faster than cpio, provides more diverse magnetic tape options and deals somewhat gracefully with input data corruption. X Afio is quite portable. It is now running under 4.2BSD, System V and even one or two UNIX-like operating systems. Please see the beginning of afio.c for a brief description of the compile-time configuration options. X Discussion of features (including those unintended ones commonly referred to as "bugs") may be sent to ..!{ihnp4, sun}!laidbak!mdb. X X Mark Brukhartz X Lachman Associates, Inc. X ..!{ihnp4, sun}!laidbak!mdb X Current maintainer: Jeff Buhrt, uunet!sawmill!prslnk!buhrt SHAR_EOF chmod 0444 README || echo 'restore of README failed' Wc_c="`wc -c < 'README'`" test 687 -eq "$Wc_c" || echo 'README: original size 687, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= afio.1 ============== if test -f 'afio.1' -a X"$1" != X"-c"; then echo 'x - skipping afio.1 (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting afio.1 (Text)' sed 's/^X//' << 'SHAR_EOF' > 'afio.1' && 'br $Header: /u/buhrt/src/afio/RCS/afio.1,v 2.3 1991/09/25 20:08:33 buhrt Exp $ .TH AFIO 1 .SH NAME afio \- manipulate archives and files .SH SYNOPSIS .B "afio \-o" [ .I options ] archive .br .B "afio \-t" [ .I options ] archive .br .B "afio \-i" [ .I options ] archive .br .B "afio \-p" [ .I options ] directory [ ... ] .SH DESCRIPTION .I Afio manipulates groups of files, copying them within the (collective) filesystem or between the filesystem and an .I afio archive. Note that .I afio archives are portable, as they contain only ASCII-formatted header information. They are also compatible with ASCII .IR cpio (1) archives (ala .IR "cpio \-c" ). .PP With .BR \-o , reads pathnames from the standard input and writes an .IR archive . .PP With .BR \-t , reads an .I archive and writes a table-of-contents to the standard output. .PP With .BR \-i , installs the contents of an .I archive relative to the working directory. .PP With .BR \-p , reads pathnames from the standard input and copies the files to each .IR directory . .PP Creates missing directories as necessary, with permissions to match their parents. .PP Generates sparse filesystem blocks (with .IR lseek (2)) when possible. .PP Supports multi-volume archives during interactive operation (i.e., when .I /dev/tty is accessible and .I SIGINT is not being ignored). .PP Options: .TP 13 .BI \-b "\ size" Read or write .IR size -character archive blocks. Suffices of .BR b , .B k and .B m denote multiples of .IR 512 , .I 1024 and .IR 1048576 , respectively. Defaults to .I 5120 for compatibility with .IR cpio (1). .TP .BI \-c "\ count" Buffer .I count archive blocks between I/O operations. A large .I count is recommended with streaming magnetic tape drives. .TP .B \-d Don't create missing directories. .TP .BI \-e "\ bound" Pad the archive to a multiple of .I bound characters. Recognizes the same suffices as .BR \-s . Defaults to .I 1x\^ (the .B \-b block size) for compatibility with .IR cpio (1). .TP .B \-f Spawn a child process to actually write to the archive; provides a clumsy form of double-buffering. Requires .B \-s for multi-volume archive support. .TP .B \-g Change to input file directories. Avoids quadratic filesystem behavior with long similar pathnames. Requires all absolute pathnames, including those for the .B \-o .I archive and the .B \-p .IR directories . .TP .B \-h Follow symbolic links, treating them as ordinary files and directories. .TP .B \-j Don't generate sparse filesystem blocks. .TP .B \-k Skip corrupt data at the .I beginning of an archive (rather than complaining about unrecognizable input). .TP .B \-l With .BR \-o , write file contents with each hard link. .sp With .BR \-t , report hard links. .sp With .BR \-p , attempt to link files rather than copying them. .TP .B \-m Mark output files with a common current timestamp (rather than with input file modification times). .TP .B \-n Protect newer existing files (comparing file modification times). .TP .BI \-s "\ limit" Restrict each portion of a multi-volume archive to .I limit characters. Recognizes the same suffices as .BR \-b . Also, the suffix .B x denotes a multiple of the .B \-b block size (and must follow any .B \-b specification). Useful with finite-length devices which do not return short counts at end of media (sigh); output to magnetic tape typically falls into this category. .TP .B \-u Report files with unseen links. .TP .B \-v Verbose. Report pathnames as they are processed. With .BR \-t , gives an .I "ls \-l" style report (including link information). .TP .B \-x Retain file ownership and setuid/setgid permissions. This is the default for the super-user; he may use .B \-X to override it. .TP .BI \-y "\ prefix" Restrict archive processing to names beginning with .IR prefix . Specify once for each prefix to be recognized. Use .B \-Y to supply prefixes which are .I not to be processed. .TP .B \-z Print execution statistics. This is meant for human consumption; use by other programs is officially discouraged. .PP .TP .B -A Do not turn absolute paths into relative paths. That is don't remove the leading slash. .TP .B -F This is a floppy disk, -s is required. Uses shared memory if compiled in otherwise mallocs as needed (a 3b1 will not be able to malloc the needed memory w/o shared memory), afio assumes either way you can malloc/shmalloc a chunck of memory the size of one disk. Examples: 795k: 3.5" (720k drive), 316k (360k drive) .nf At the end of each disk this message occurs: X Ready for disk [#] on [output] (remove the disk when the light goes out) X Type "go" (or "GO") when ready to proceed (or "quit" to abort): .fi .TP .B -K Verify the output against what is in the memory copy of the disk (-F required). If the writing or verifying fails the following menu pops up (the hidden option "quit" will also exit from the backup at this point). .nf X [Writing/Verify] of disk [disk #] has FAILED (try option #3 first ])! X Enter 1 to RETRY this disk X Enter 2 to REFORMAT this disk X Enter 3 to REFORMAT AND THEN RETRY this disk if the format works .fi .TP .B -L Log_file_path Specify the name of the file to log errors and the finial totals to. .TP .B -R "Disk format command string" This is the command that is run when you enter 2 to reformat the disk. The default (char *formatcmd = "sh /u/store/format.sh") can be changed to a given system's default. .TP .B -Z Compress the files on the way out, in, and passing without links (valid w/ or w/o -F or -K), requires compress and uncompress to be in your path. .PP Special-case archive names: .RS 3 .TP 3 .B o Specify .I \- to read or write the standard input or output, respectively. This disables multi-volume archive handling. .TP .B o Prefix a command string to be executed with an exclamation mark .RI ( ! ). The command is executed once for each archive volume, with its standard input or output piped to .IR afio . It is expected to produce a zero exit code when all is well. .TP .B o Use .I system:file to access an archive in .I file on .IR system . This is really just a special case of pipelining. It requires a 4.2BSD-style remote shell .RI ( rsh (1C)) and a remote copy of .IR afio . .TP .B o Anything else specifies a local file or device. An output file will be created if it does not already exist. .RE .PP Recognizes obsolete binary .IR cpio (1) archives (including those from machines with reversed byte order), but cannot write them. .PP Recovers from archive corruption by searching for a valid magic number. This is rather simplistic, but, much like a disassembler, almost always works. .PP Optimizes pathnames with respect to the current and parent directories. For example, .I ./src/sh/../misc/afio.c becomes .IR src/misc/afio.c . .SH EXAMPLE X AT&T 3b1 (all one line) X find /u/bstore -print | \\ X afio -ovzFZK -L/u/store/BackupLog \\ X -R'/etc/iv -i /dev/rfp020 /usr/lib/iv/FDnl' -s$DISKSIZE /dev/rfp021 .SH BUGS There are too many options. .PP Restricts pathnames to 1023 characters and 255 meaningful elements. .PP There is no sequence information within multi-volume archives. Input sequence errors generally masquerade as data corruption. A solution would probably be mutually exclusive with .IR cpio (1) compatibility. .PP Degenerate uses of symbolic links are mangled by pathname optimization. For example, assuming that "usr.src" is a symbolic link to "/usr/src", the pathname "usr.src/../bin/cu" is mis-optimized into "bin/cu" (rather than "/usr/bin/cu"). .SH "SEE ALSO" cpio(1), find(1), tar(1), tp(1), compress(1). .SH AUTHOR Mark Brukhartz .br .I "..!ihnp4!laidbak!mdb" .br Jeff Buhrt (floppy/compression extensions) .br .I "uunet!sawmill!prslnk!buhrt" SHAR_EOF chmod 0444 afio.1 || echo 'restore of afio.1 failed' Wc_c="`wc -c < 'afio.1'`" test 7640 -eq "$Wc_c" || echo 'afio.1: original size 7640, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= afio.c ============== if test -f 'afio.c' -a X"$1" != X"-c"; then echo 'x - skipping afio.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting afio.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'afio.c' && /* X * afio.c X * X * Manipulate archives and files. X * X * Copyright (c) 1985 Lachman Associates, Inc.. X * X * This software was written by Mark Brukhartz at Lachman Associates, X * Inc.. It may be distributed within the following restrictions: X * (1) It may not be sold at a profit. X * (2) This credit and notice must remain intact. X * This software may be distributed with other software by a commercial X * vendor, provided that it is included at no additional charge. X * X * Please report bugs to "uunet!sawmill!prslnk!buhrt" X * X * Options: X * o Define INDEX to use index() in place of strchr() (v7, BSD). X * o Define MEMCPY when an efficient memcpy() exists (SysV). X * o Define MKDIR when a mkdir() system call is present (4.2BSD, SysVr3). X * o Define NOVOID if your compiler doesn't like void casts. X * o Define SYSTIME to use rather than (4.2BSD). X * o Define VOIDFIX to allow pointers to functions returning void (non-PCC). X * o Define CTC3B2 to support AT&T 3B2 streaming cartridge tape. X * o Define HAVEFCNTL if you have X * o Define USESHMEM if you have shared memory (AT&T 3b1) X * o Define MYTEMPNAM if you don't have tempnam() X * o Define UNIXPC if you are on a 3b1, 7300, etc. X * o Define HAVEMEMCMP if you have memcmp otherwise assumes bcmp X * o Define DEFFMTCMD to being how to format the media you use the most. X * o Define LONGZFILE if you want .Z to be tagged on the end of a 14 char X * X * BUGS: X * Still needs '286 floppy support. X * X * Added by Jeff Buhrt: X * Floppy Verify/format/restart output in the middle of a set, X * compress files on output, extended error messages and logging X * X * NOTE: If you are compressing and the temp directory you pick is X * on a NSF file system, watch out for "stale handle" caused X * by opening the compress output file then unlinking before X * returning an open file descriptor. X */ X static char *ident = "$Header: /u/buhrt/src/afio/RCS/afio.c,v 2.3 1991/09/25 20:08:33 buhrt Exp $"; X #include #include #include #include #include #include #include #include #include "patchlevel.h" X #ifndef major # include #endif /* major */ X #ifdef SYSTIME # include #else /* SYSTIME */ # include #endif /* SYSTIME */ X #ifdef CTC3B2 # include # include #endif /* CTC3B2 */ X #ifdef MYTEMPNAM #include #endif X #ifdef USESHMEM #include #include X #define NUMSHKEYS 20 #define SHMEMSIZE 262144 /* 2^18 (dev3b1) */ #endif X /* done writing to the archive */ #define FALSE 0 #define TRUE 1 #define NOTDONE 0 #define DONE 1 #define NODIE 0 #define DIE 1 X /* X * Address link information base. X */ #define linkhash(ino) \ X (linkbase + (ino) % nel(linkbase)) X /* X * Mininum value. X */ #define min(one, two) \ X (one < two ? one : two) X /* X * Number of array elements. X */ #define nel(a) \ X (sizeof(a) / sizeof(*(a))) X /* X * Remove a file or directory. X */ #define afremove(name, asb) \ X (((asb)->sb_mode & S_IFMT) == S_IFDIR ? rmdir(name) : unlink(name)) X /* X * Swap bytes. X */ #define swab(n) \ X ((((ushort)(n) >> 8) & 0xff) | (((ushort)(n) << 8) & 0xff00)) X /* X * Cast and reduce to unsigned short. X */ #define ush(n) \ X (((ushort) (n)) & 0177777) X /* X * Definitions. X */ #define reg register /* Convenience */ #define uint unsigned int /* Not always in types.h */ #define ushort unsigned short /* Not always in types.h */ #define BLOCK 5120 /* Default archive block size */ #define FSBUF (8*1024) /* Filesystem buffer size */ #define H_COUNT 10 /* Number of items in ASCII header */ #define H_PRINT "%06o%06o%06o%06o%06o%06o%06o%011lo%06o%011lo" #define H_SCAN "%6ho%6ho%6ho%6ho%6ho%6ho%6ho%11lo%6o%11lo" #define H_STRLEN 70 /* ASCII header string length */ #define M_ASCII "070707" /* ASCII magic number */ #define M_BINARY 070707 /* Binary magic number */ #define M_STRLEN 6 /* ASCII magic number length */ #define NULLDEV -1 /* Null device code */ #define NULLINO 0 /* Null inode number */ #define PATHELEM 256 /* Pathname element count limit */ #define PATHSIZE 1024 /* Pathname length limit */ #define S_IFSHF 12 /* File type shift (shb in stat.h) */ #define S_IPERM 07777 /* File permission bits (shb in stat.h) */ #define S_IPEXE 07000 /* Special execution bits (shb in stat.h) */ #define S_IPOPN 0777 /* Open access bits (shb in stat.h) */ #define STDIN 0 /* Standard input file descriptor */ #define STDOUT 1 /* Standard output file descriptor */ #define TTY "/dev/tty" /* For volume-change queries */ X /* X * Some versions of the portable "C" compiler (PCC) can't handle X * pointers to functions returning void. X */ #ifdef VOIDFIX # define VOIDFN void /* Expect "void (*fnptr)()" to work */ #else /* VOIDFIX */ # define VOIDFN int /* Avoid PCC "void (*fnptr)()" bug */ #endif /* VOIDFIX */ X /* X * Trailer pathnames. All must be of the same length. X */ #define TRAILER "TRAILER!!!" /* Archive trailer (cpio compatible) */ #define TRAILZ 11 /* Trailer pathname length (including null) */ X /* X * Open modes; there is no with v7 UNIX. X */ #ifdef HAVEFCNTL #include #else #define O_RDONLY 0 /* Read-only */ #define O_WRONLY 1 /* Write-only */ #define O_RDWR 2 /* Read/write */ #endif /* X * V7 and BSD UNIX use old-fashioned names for a couple of X * string functions. X */ #ifdef INDEX # define strchr index /* Forward character search */ # define strrchr rindex /* Reverse character search */ #endif /* INDEX */ X /* X * Some compilers can't handle void casts. X */ #ifdef NOVOID # define VOID /* Omit void casts */ #else /* NOVOID */ # define VOID (void) /* Quiet lint about ignored return values */ #endif /* NOVOID */ X /* X * Adb is more palatable when static functions and variables are X * declared as globals. Lint gives more useful information when X * statics are truly static. X */ #ifdef lint # define STATIC static /* Declare static variables for lint */ #else /* lint */ # define STATIC /* Make static variables global for adb */ #endif /* lint */ X /* X * Simple types. X */ typedef struct group Group; /* Structure for getgrgid(3) */ typedef struct passwd Passwd; /* Structure for getpwuid(3) */ typedef struct tm Time; /* Structure for localtime(3) */ X #ifdef S_IFLNK X /* X * File status with symbolic links. Kludged to hold symbolic X * link pathname within structure. X */ X typedef struct { X struct stat sb_stat; X char sb_link[PATHSIZE]; X } Stat; # define STAT(name, asb) stat(name, &(asb)->sb_stat) # define FSTAT(fd, asb) fstat(fd, &(asb)->sb_stat) # define LSTAT(name, asb) lstat(name, &(asb)->sb_stat) # define sb_dev sb_stat.st_dev # define sb_ino sb_stat.st_ino # define sb_mode sb_stat.st_mode # define sb_nlink sb_stat.st_nlink # define sb_uid sb_stat.st_uid # define sb_gid sb_stat.st_gid # define sb_rdev sb_stat.st_rdev # define sb_size sb_stat.st_size # define sb_atime sb_stat.st_atime # define sb_mtime sb_stat.st_mtime # define sb_ctime sb_stat.st_ctime # define sb_blksize sb_stat.st_blksize # define sb_blocks sb_stat.st_blocks #else /* S_IFLNK */ X /* X * File status without symbolic links. X */ X typedef struct stat Stat; # define STAT(name, asb) stat(name, asb) # define FSTAT(fd, asb) fstat(fd, asb) # define LSTAT(name, asb) stat(name, asb) # define sb_dev st_dev # define sb_ino st_ino # define sb_mode st_mode # define sb_nlink st_nlink # define sb_uid st_uid # define sb_gid st_gid # define sb_rdev st_rdev # define sb_size st_size # define sb_atime st_atime # define sb_mtime st_mtime # define sb_ctime st_ctime #endif /* S_IFLNK */ X /* X * Binary archive header (obsolete). X */ typedef struct { X short b_dev; /* Device code */ X ushort b_ino; /* Inode number */ X ushort b_mode; /* Type and permissions */ X ushort b_uid; /* Owner */ X ushort b_gid; /* Group */ X short b_nlink; /* Number of links */ X short b_rdev; /* Real device */ X ushort b_mtime[2]; /* Modification time (hi/lo) */ X ushort b_name; /* Length of pathname (with null) */ X ushort b_size[2]; /* Length of data */ } Binary; X /* X * Child process structure. X */ typedef struct child { X struct child *c_forw; /* Forward link */ X int c_pid; /* Process ID */ X int c_flags; /* Flags (CF_) */ X int c_status; /* Exit status */ } Child; X /* X * Child process flags (c_flags). X */ #define CF_EXIT (1<<0) /* Exited */ X /* X * Hard link sources. One or more are chained from each link X * structure. X */ typedef struct name { X struct name *p_forw; /* Forward chain (terminated) */ X struct name *p_back; /* Backward chain (circular) */ X char *p_name; /* Pathname to link from */ } Path; X /* X * File linking information. One entry exists for each unique X * file with with outstanding hard links. X */ typedef struct link { X struct link *l_forw; /* Forward chain (terminated) */ X struct link *l_back; /* Backward chain (terminated) */ X dev_t l_dev; /* Device */ X ino_t l_ino; /* Inode */ X ushort l_nlink; /* Unresolved link count */ X off_t l_size; /* Length */ X Path *l_path; /* Pathname(s) to link from */ } Link; X /* X * Pathnames to (or to not) be processed. X */ typedef struct pattern { X struct pattern *p_forw; /* Forward chain */ X char *p_str; /* String */ X int p_len; /* Length of string */ X int p_not; /* Reverse logic */ } Pattern; X /* X * External functions. X */ void _exit(); void exit(); void free(); char *getenv(); ushort getgid(); Group *getgrgid(); Passwd *getpwuid(); ushort getuid(); Time *localtime(); off_t lseek(); char *malloc(); uint sleep(); char *strcat(); char *strchr(); char *strcpy(); char *strncpy(); char *strrchr(); time_t time(); X X /* X * Internal functions. X */ VOIDFN copyin(); VOIDFN copyout(); void compressfile(); int dirchg(); int dirmake(); int dirneed(); void fatal(); void goodbye(); VOIDFN in(); void inalloc(); int inascii(); int inavail(); int inbinary(); int indata(); int inentry(); int infill(); int inhead(); int inread(); int inskip(); int inswab(); int lineget(); void linkalso(); Link *linkfrom(); void linkleft(); Link *linkto(); char *memcpy(); char *memget(); char *memstr(); int mkdir(); void nameadd(); int namecmp(); int nameopt(); void next(); void nextask(); void nextclos(); int nextopen(); int openin(); int openotty(); int openqtty(); int options(); off_t optsize(); VOIDFN out(); void outalloc(); uint outavail(); int outdata(); void outeof(); void outflush(); void outhead(); void outpad(); void outwait(); void outwrite(); VOIDFN pass(); void passdata(); int passitem(); int pipechld(); int pipeopen(); void pipewait(); void prsize(); int rmdir(); VOIDFN (*signal())(); int fswrite(); #ifdef USESHMEM char *shmemalloc(); void shmemfree(); #endif char *syserr(); #ifdef MYTEMPNAM char *tempnam(); else extern char *tempnam(); #endif VOIDFN toc(); void tocentry(); void tocmode(); void usage(); void verify(); int warn(); int warnarch(); int writedisk(); int xfork(); void xpause(); int xwait(); X extern int atoi(); extern int chdir(); extern int chmod(); extern int chown(); extern int dup(); extern int fork(); extern int isatty(); extern int link(); extern int memcmp(); extern int mknod(); extern int pipe(); extern int read(); extern int stat(); extern int strcmp(); extern int strlen(); extern int strncmp(); extern int umask(); extern int unlink(); extern int utime(); extern int wait(); extern int write(); X /* X * External variables. X */ extern int errno; /* System error code */ extern char *sys_errlist[]; /* System error messages */ extern int sys_nerr; /* Number of sys_errlist entries */ X /* X * Static variables. X */ STATIC short Fflag; /* X * floppy flag (write when buf full) X * set -sdisk_size as well X */ STATIC short Zflag; /* compress the files that we can */ STATIC short verifyflag; /* Verify (floppy) flag */ STATIC short verifycnt; #ifdef CTC3B2 STATIC short Cflag; /* Enable 3B2 CTC streaming (kludge) */ #endif /* CTC3B2 */ STATIC short dflag; /* Don't create missing directories */ STATIC short fflag; /* Fork before writing to archive */ STATIC short gflag; /* Change to input file directories */ STATIC short hflag; /* Follow symbolic links */ STATIC short jflag; /* Don't generate sparse filesystem blocks */ STATIC short kflag; /* Skip initial junk to find a header */ STATIC short lflag; /* Link rather than copying (when possible) */ STATIC short mflag; /* Ignore archived timestamps */ STATIC short nflag; /* Keep newer existing files */ STATIC short uflag; /* Report files with unseen links */ STATIC short vflag; /* Verbose */ STATIC short xflag; /* Retain file ownership */ STATIC short zflag; /* Print final statistics */ STATIC short hidequit; /* show the quit option? */ STATIC short abspaths; /* allow absolute path names? */ STATIC uint arbsize = BLOCK;/* Archive block size */ STATIC short areof; /* End of input volume reached */ STATIC int arfd = -1; /* Archive file descriptor */ STATIC off_t arleft; /* Space remaining within current volume */ STATIC char *arname; /* Expanded archive name */ STATIC uint arpad; /* Final archive block padding boundary */ STATIC char arspec[PATHSIZE];/* Specified archive name */ STATIC off_t aruntil; /* Volume size limit */ STATIC uint arvolume = 1; /* Volume number */ STATIC uint buflen; /* Archive buffer length */ STATIC char *buffer; /* Archive buffer */ STATIC char *bufidx; /* Archive buffer index */ STATIC char *bufend; /* End of data within archive buffer */ STATIC Child *children; /* Child processes */ STATIC char *formatcmd = DEFFMTCMD; /* how to format */ STATIC ushort gid; /* Group ID */ STATIC Link *linkbase[256]; /* Unresolved link information */ STATIC FILE *logfile = NULL;/* log same errors as stderr would */ STATIC ushort mask; /* File creation mask */ STATIC char *myname; /* Arg0 */ extern char *optarg; /* Option argument */ extern int optind; /* Command line index */ STATIC int outpid; /* Process ID of outstanding outflush() */ STATIC Pattern *pattern; /* Pathname matching patterns */ STATIC char pwd[PATHSIZE]; /* Working directory (with "-g") */ STATIC int pipepid; /* Pipeline process ID */ STATIC time_t timenow; /* Current time */ STATIC time_t timewait; /* Time spent awaiting new media */ STATIC off_t total; /* Total number of bytes transferred */ STATIC int ttyf; /* For interactive queries (yuk) */ STATIC ushort uid; /* User ID */ int uncompressrun = 0; /* is uncompress running? its pid if so */ char uncompto[PATHSIZE]; /* name we uncompressed to */ X main(ac, av) int ac; reg char **av; { X reg int c; X reg uint group = 1; X VOIDFN (*fn)() = NULL; X time_t timedone; X auto char remote[PATHSIZE]; X X if (myname = strrchr(*av, '/')) X ++myname; X else X myname = *av; X mask = umask(0); X uid = getuid(); X gid = getgid(); X if (uid == 0) X ++xflag; X VOID signal(SIGPIPE, SIG_IGN); X while (c = options(ac, av, "ioptIOVCb:c:de:fghjklmns:uvxXy:Y:zFKZL:R:qA")) { X switch (c) { X case 'i': X if (fn) X usage(); X fn = in; X break; X case 'o': X if (fn) X usage(); X fn = out; X break; X case 'p': X if (fn) X usage(); X fn = pass; X break; X case 't': X if (fn) X usage(); X fn = toc; X break; X case 'I': X if (fn) X usage(); X fn = copyin; X break; X case 'O': X if (fn) X usage(); X fn = copyout; X break; X case 'V': X VOID printf("%s: Version %s dated %s\n", X myname, VERSION, DATE); X exit(0); #ifdef CTC3B2 X case 'C': X ++Cflag; X arbsize = 31 * 512; X group = 10; X aruntil = 1469 * 31 * 512; X break; #endif /* CTC3B2 */ X case 'b': X if ((arbsize = (uint) optsize(optarg)) == 0) X fatal(optarg, "Bad block size"); X break; X case 'c': X if ((group = (uint) optsize(optarg)) == 0) X fatal(optarg, "Bad buffer count"); X break; X case 'd': X ++dflag; X break; X case 'e': X arpad = (uint) optsize(optarg); X break; X case 'f': X ++fflag; X break; X case 'g': X ++gflag; X break; X case 'h': X ++hflag; X break; X case 'j': X ++jflag; X break; X case 'k': X ++kflag; X break; X case 'l': X ++lflag; X break; X case 'm': X ++mflag; X break; X case 'n': X ++nflag; X break; X case 's': X aruntil = optsize(optarg); X if (aruntil == 0) X usage(); X break; X case 'F': X ++Fflag; X break; X case 'Z': X ++Zflag; X break; X case 'K': X ++verifyflag; X break; X case 'u': X ++uflag; X break; X case 'v': X ++vflag; X break; X case 'x': X ++xflag; X break; X case 'X': X xflag = 0; X break; X case 'y': X nameadd(optarg, 0); X break; X case 'Y': X nameadd(optarg, 1); X break; X case 'z': X ++zflag; X break; X case 'L': X if ((logfile = fopen(optarg, "a")) == (FILE *)0) X { fprintf(stderr, X "Can't open %s to append, get help\n", X optarg); X exit(1); X } X break; X case 'R': X formatcmd = optarg; X break; X case 'q': X hidequit = TRUE; X break; X case 'A': X abspaths = TRUE; X break; X default: X usage(); X } X } X if (fn == NULL || av[optind] == NULL) X usage(); X ttyf = openqtty(); X if (Fflag) X { if ((buflen = aruntil) == 0) X usage(); X } X else X buflen = arbsize * group; X if (aruntil && (aruntil < arbsize)) X { fprintf(stderr, "Media size %d is less than buffer size %d\n", X aruntil, arbsize); X usage(); X } X if (arpad == 0) X arpad = arbsize; X if (fn != pass) { X reg char *colon; X reg char *equal; X reg int isoutput = (fn == out || fn == copyout); X X arname = strcpy(arspec, av[optind++]); X if (colon = strchr(arspec, ':')) { X *colon++ = '\0'; X if (equal = strchr(arspec, '=')) X *equal++ = '\0'; X VOID sprintf(arname = remote, X "!rsh %s %s -%c -b %u -c %u %s", X arspec, equal ? equal : myname, X isoutput ? 'O' : 'I', arbsize, X group, colon); X if (equal) X *--equal = '='; X *--colon = ':'; X } X if (gflag && *arname != '/' && *arname != '!') X fatal(arspec, "Relative pathname"); X VOID signal(SIGINT, goodbye); #ifdef USESHMEM X /* alloc the space as shared if we will be forking a lot */ X if ((*arspec != '!') && (Fflag || fflag) #if ((defined (USESHMEM)) && (defined (UNIXPC))) X /* X * UNIXPC and shared memory read/write bug X * -only a write() is handled correctly below X * in writedisk() X */ X && ((fn == out) || (fn == copyout))) #else X ) #endif X { X if ((buffer = bufidx = bufend = shmemalloc(buflen+BLOCK, arname)) == NULL) X fatal(arspec, "Cannot allocate I/O buffer (shmem)"); X } X else #endif X /* X * +BLOCK is added to make sure we don't overrun buffer on a X * read (internal read(1) length is thus met) X */ X if ((buffer = bufidx = bufend = malloc(buflen+BLOCK)) == NULL) X fatal(arspec, "Cannot allocate I/O buffer"); X X /* X * open a floppy at the last moment (if output), otherwise now X * note we set arleft prematurely so we don't have to open the X * disk now X */ X if (!Fflag || !isoutput) X { if (nextopen(isoutput ? O_WRONLY : O_RDONLY) < 0) X goodbye(1); X } X else X arleft = aruntil; X } X timenow = time((time_t *) NULL); X (*fn)(av + optind); X timedone = time((time_t *) NULL); X if (uflag) X linkleft(); X if (zflag) { X reg FILE *stream; X X stream = fn == toc || arfd == STDOUT ? stderr : stdout; X VOID fprintf(stream, "%s: ", myname); X prsize(stream, total); X VOID fprintf(stream, " bytes %s in %lu seconds. The backup was successfull!\n", X fn == pass X ? "transferred" X : fn == out || fn == copyout X ? "written" X : "read", X timedone - timenow - timewait); X } X if (logfile != (FILE *)0) X { VOID fprintf(logfile, "%s: Successfully backed up ", myname); X prsize(logfile, total); X VOID fprintf(logfile, X " bytes %s in %lu seconds (+waited %d seconds for disk swapping (%u disks)) finished at %s", X fn == pass X ? "transferred" X : fn == out || fn == copyout X ? "written" X : "read", X timedone - timenow - timewait, X timewait, X arvolume, X ctime(&timedone)); X } X nextclos(); X goodbye(0); X /* NOTREACHED */ } X /* X * copyin() X * X * Copy directly from the archive to the standard output. X */ STATIC VOIDFN copyin(av) reg char **av; { X reg int got; X reg uint have; X X if (*av) X fatal(*av, "Extraneous argument"); X while (!areof) { X VOID infill(); X while (have = bufend - bufidx) X if ((got = write(STDOUT, bufidx, have)) < 0) X fatal("", syserr()); X else if (got > 0) X bufidx += got; X else X return; X } } X /* X * copyout() X * X * Copy directly from the standard input to the archive. X */ STATIC VOIDFN copyout(av) reg char **av; { X reg int got; X reg uint want; X X if (*av) X fatal(*av, "Extraneous argument"); X for (;;) { X while ((want = bufend - bufidx) == 0) X outflush(NOTDONE); X if ((got = read(STDIN, bufidx, want)) < 0) X fatal("", syserr()); X else if (got == 0) X break; X else X bufidx += got; X } X outflush(DONE); X if (fflag) X outwait(); } X /* X * dirchg() X * X * Change to the directory containing a given file. X */ STATIC int dirchg(name, local) reg char *name; reg char *local; { X reg char *last; X reg int len; X auto char dir[PATHSIZE]; X X if (*name != '/') X return (warn(name, "Relative pathname")); X for (last = name + strlen(name); last[-1] != '/'; --last) X ; X len = last - name; X strncpy(dir, name, len)[len] = '\0'; X VOID strcpy(local, *last ? last : "."); X if (strcmp(dir, pwd) == 0) X return (0); X if (chdir(dir) < 0) X return (warn(name, syserr())); X VOID strcpy(pwd, dir); X return (0); } X /* X * dirmake() X * X * Make a directory. Returns zero if successful, -1 otherwise. X */ STATIC int dirmake(name, asb) reg char *name; reg Stat *asb; { X if (mkdir(name, asb->sb_mode & S_IPOPN) < 0) X return (-1); X if (asb->sb_mode & S_IPEXE) X VOID chmod(name, asb->sb_mode & S_IPERM); X if (xflag) X VOID chown(name, X uid == 0 ? ush(asb->sb_uid) : uid, X ush(asb->sb_gid)); X return (0); } X /* X * dirneed() X * X * Recursively create missing directories (with the same permissions X * as their first existing parent). Temporarily modifies the 'name' X * argument string. Returns zero if successful, -1 otherwise. X */ STATIC int dirneed(name) char *name; { X reg char *cp; X reg char *last; X reg int ok; X static Stat sb; X X last = NULL; X for (cp = name; *cp; ) X if (*cp++ == '/') X last = cp; X if (last == NULL) X return (STAT(".", &sb)); X *--last = '\0'; X ok = STAT(*name ? name : "/", &sb) == 0 X ? ((sb.sb_mode & S_IFMT) == S_IFDIR) X : (!dflag && dirneed(name) == 0 && dirmake(name, &sb) == 0); X *last = '/'; X return (ok ? 0 : -1); } X /* X * fatal() X * X * Print fatal message and exit. X */ STATIC void fatal(what, why) char *what; char *why; { X VOID warn(what, why); X goodbye(1); } X /* X * in() X * X * Read an archive. X */ STATIC VOIDFN in(av) reg char **av; { X auto Stat sb; X auto char name[PATHSIZE]; X int sel; X X if (*av) X fatal(*av, "Extraneous argument"); X name[0] = '\0'; X while (inhead(name, &sb) == 0) { X if (((sel = namecmp(name)) < 0) || inentry(name, &sb) < 0) X if (inskip(sb.sb_size) < 0) X VOID warn(name, "Skipped file data is corrupt"); X if (vflag && (sel == 0)) X { if (*uncompto) X VOID fprintf(stderr, "%s uncompressed to: %s\n", X name, uncompto); X else X VOID fprintf(stderr, "%s\n", name); X } X } } X /* X * inalloc() X * X * Allocate input buffer space (which was previously indexed X * by inavail()). X */ STATIC void inalloc(len) reg uint len; { X bufidx += len; X total += len; } X /* X * inascii() X * X * Read an ASCII header. Returns zero if successful; X * -1 otherwise. Assumes that the entire magic number X * has been read. X */ STATIC int inascii(magic, name, asb) reg char *magic; reg char *name; reg Stat *asb; { X auto uint namelen; X auto char header[H_STRLEN + 1]; X X if (strncmp(magic, M_ASCII, M_STRLEN) != 0) X return (-1); X if (inread(header, H_STRLEN) < 0) X return (warnarch("Corrupt ASCII header", (off_t) H_STRLEN)); X header[H_STRLEN] = '\0'; X if (sscanf(header, H_SCAN, &asb->sb_dev, X &asb->sb_ino, &asb->sb_mode, &asb->sb_uid, X &asb->sb_gid, &asb->sb_nlink, &asb->sb_rdev, X &asb->sb_mtime, &namelen, &asb->sb_size) != H_COUNT) X return (warnarch("Bad ASCII header", (off_t) H_STRLEN)); X if (namelen == 0 || namelen >= PATHSIZE) X return (warnarch("Bad ASCII pathname length", (off_t) H_STRLEN)); X if (inread(name, namelen) < 0) X return (warnarch("Corrupt ASCII pathname", (off_t) namelen)); X if (name[namelen - 1] != '\0') X return (warnarch("Bad ASCII pathname", (off_t) namelen)); X return (0); } X /* X * inavail() X * X * Index availible input data within the buffer. Stores a pointer X * to the data and its length in given locations. Returns zero with X * valid data, -1 if unreadable portions were replaced with nulls. X */ STATIC int inavail(bufp, lenp) reg char **bufp; uint *lenp; { X reg uint have; X reg int corrupt = 0; X X while ((have = bufend - bufidx) == 0) X corrupt |= infill(); X *bufp = bufidx; X *lenp = have; X return (corrupt); } X /* X * inbinary() X * X * Read a binary header. Returns the number of trailing alignment X * bytes to skip; -1 if unsuccessful. X */ STATIC int inbinary(magic, name, asb) reg char *magic; reg char *name; reg Stat *asb; { X reg uint namefull; X auto Binary binary; X X if ((int)*((ushort *) magic) != M_BINARY) X return (-1); X memcpy((char *) &binary, X magic + sizeof(ushort), X M_STRLEN - sizeof(ushort)); X if (inread((char *) &binary + M_STRLEN - sizeof(ushort), X sizeof(binary) - (M_STRLEN - sizeof(ushort))) < 0) X return (warnarch("Corrupt binary header", X (off_t) sizeof(binary) - (M_STRLEN - sizeof(ushort)))); X asb->sb_dev = binary.b_dev; X asb->sb_ino = binary.b_ino; X asb->sb_mode = binary.b_mode; X asb->sb_uid = binary.b_uid; X asb->sb_gid = binary.b_gid; X asb->sb_nlink = binary.b_nlink; X asb->sb_rdev = binary.b_rdev; X asb->sb_mtime = binary.b_mtime[0] << 16 | binary.b_mtime[1]; X asb->sb_size = binary.b_size[0] << 16 | binary.b_size[1]; X if (binary.b_name == 0 || binary.b_name >= PATHSIZE) X return (warnarch("Bad binary pathname length", X (off_t) sizeof(binary) - (M_STRLEN - sizeof(ushort)))); X if (inread(name, namefull = binary.b_name + binary.b_name % 2) < 0) X return (warnarch("Corrupt binary pathname", (off_t) namefull)); X if (name[binary.b_name - 1] != '\0') X return (warnarch("Bad binary pathname", (off_t) namefull)); X return (asb->sb_size % 2); } X /* X * indata() X * X * Install data from an archive. Returns given file descriptor. X */ STATIC int indata(fd, size, name) int fd; reg off_t size; char *name; { X reg uint chunk; X reg char *oops; X reg int sparse; X reg int corrupt; X auto char *buf; X auto uint avail; X X corrupt = sparse = 0; X oops = NULL; X while (size) { X corrupt |= inavail(&buf, &avail); X size -= (chunk = size < avail ? (uint) size : avail); X if (oops == NULL && (sparse = fswrite(fd, buf, chunk)) < 0) X oops = syserr(); X inalloc(chunk); X } X if (corrupt) X VOID warn(name, "Corrupt archive data"); X if (oops) X VOID warn(name, oops); X else if (sparse > 0 X && (lseek(fd, (off_t) -1, 1) < 0 X || write(fd, "", 1) != 1)) X VOID warn(name, syserr()); X return (fd); } X /* X * inentry() X * X * Install a single archive entry. Returns zero if successful, -1 otherwise. X */ STATIC int inentry(name, asb) char *name; reg Stat *asb; { X reg Link *linkp; X reg int ifd; X reg int ofd; X auto time_t tstamp[2]; X X if ((ofd = openotty(name, asb, linkp = linkfrom(asb), 0, Zflag)) > 0) X { if (asb->sb_size || linkp == NULL || linkp->l_size == 0) X VOID close(indata(ofd, asb->sb_size, name)); X else if ((ifd = open(linkp->l_path->p_name, O_RDONLY)) < 0) X VOID warn(linkp->l_path->p_name, syserr()); X else { X passdata(linkp->l_path->p_name, ifd, name, ofd); X VOID close(ifd); X VOID close(ofd); X } X /* safety */ X if (uncompressrun) X { VOID xwait(uncompressrun, "inentry xwait()", TRUE); X uncompressrun = 0; X } X } X else if (ofd < 0) X return (-1); X else if (inskip(asb->sb_size) < 0) X VOID warn(name, "Redundant file data is corrupt"); X tstamp[0] = tstamp[1] = mflag ? timenow : asb->sb_mtime; X VOID utime(name, tstamp); X return (0); } X /* X * infill() X * X * Fill the archive buffer. Remembers mid-buffer read failures and X * reports them the next time through. Replaces unreadable data with X * null characters. Returns zero with valid data, -1 otherwise. X */ STATIC int infill() { X reg int got; X static int failed; X X bufend = bufidx = buffer; X if (!failed) { X if (areof) X if (total == 0) X fatal(arspec, "No input"); X else X next(O_RDONLY, "Input EOF"); X if (aruntil && arleft < arbsize) X next(O_RDONLY, "Input limit reached"); X while (!failed X && !areof X && (aruntil == 0 || arleft >= arbsize) X && buffer + buflen - bufend >= arbsize) { X if ((got = read(arfd, bufend, arbsize)) > 0) { X bufend += got; X arleft -= got; X } else if (got < 0) X failed = warnarch(syserr(), X (off_t) 0 - (bufend - bufidx)); X else X ++areof; X } X } X if (failed && bufend == buffer) { X failed = 0; X for (got = 0; got < arbsize; ++got) X *bufend++ = '\0'; X return (-1); X } X return (0); } X /* X * inhead() X * X * Read a header. Quietly translates old-fashioned binary cpio headers X * (and arranges to skip the possible alignment byte). Returns zero if X * successful, -1 upon archive trailer. X */ STATIC int inhead(name, asb) reg char *name; reg Stat *asb; { X reg off_t skipped; X auto char magic[M_STRLEN]; X static int align; X X if (align > 0) X VOID inskip((off_t) align); X align = 0; X for (;;) { X VOID inread(magic, M_STRLEN); X skipped = 0; X while ((align = inascii(magic, name, asb)) < 0 X && (align = inbinary(magic, name, asb)) < 0 X && (align = inswab(magic, name, asb)) < 0) { X if (++skipped == 1) { X if (!kflag && total - sizeof(magic) == 0) X fatal(arspec, "Unrecognizable archive"); X VOID warnarch("Bad magic number", X (off_t) sizeof(magic)); X if (name[0]) X VOID warn(name, "May be corrupt"); X } X memcpy(magic, magic + 1, sizeof(magic) - 1); X VOID inread(magic + sizeof(magic) - 1, 1); X } X if (skipped) { X VOID warnarch("Apparently resynchronized", X (off_t) sizeof(magic)); X VOID warn(name, "Continuing"); X } X if (strcmp(name, TRAILER) == 0) X return (-1); X if (nameopt(name) >= 0) X break; X VOID inskip(asb->sb_size + align); X } #ifdef S_IFLNK X if ((asb->sb_mode & S_IFMT) == S_IFLNK) { X if (inread(asb->sb_link, (uint) asb->sb_size) < 0) { X VOID warn(name, "Corrupt symbolic link"); X return (inhead(name, asb)); X } X asb->sb_link[asb->sb_size] = '\0'; X asb->sb_size = 0; X } #endif /* S_IFLNK */ X if ((name[0] == '/') && !abspaths) X if (name[1]) X while (name[0] = name[1]) X ++name; X else X name[0] = '.'; X asb->sb_atime = asb->sb_ctime = asb->sb_mtime; X return (0); } X /* X * inread() X * X * Read a given number of characters from the input archive. Returns X * zero with valid data, -1 if unreadable portions were replaced by X * null characters. X */ STATIC int inread(dst, len) reg char *dst; uint len; { X reg uint have; X reg uint want; X reg int corrupt = 0; X char *endx = dst + len; X X while (want = endx - dst) { X while ((have = bufend - bufidx) == 0) X corrupt |= infill(); X if (have > want) X have = want; X memcpy(dst, bufidx, have); X bufidx += have; X dst += have; X total += have; X } X return (corrupt); } X /* X * inskip() X * X * Skip input archive data. Returns zero under normal circumstances, X * -1 if unreadable data is encountered. X */ STATIC int inskip(len) reg off_t len; { X reg uint chunk; X reg int corrupt = 0; X X while (len) { X while ((chunk = bufend - bufidx) == 0) X corrupt |= infill(); X if (chunk > len) X chunk = len; X bufidx += chunk; X len -= chunk; X total += chunk; X } X return (corrupt); } X /* X * inswab() X * X * Read a reversed byte order binary header. Returns the number X * of trailing alignment bytes to skip; -1 if unsuccessful. X */ STATIC int inswab(magic, name, asb) reg char *magic; reg char *name; reg Stat *asb; { X reg ushort namesize; X reg uint namefull; X auto Binary binary; X X if ((int)*((ushort *) magic) != swab(M_BINARY)) X return (-1); X memcpy((char *) &binary, X magic + sizeof(ushort), X M_STRLEN - sizeof(ushort)); X if (inread((char *) &binary + M_STRLEN - sizeof(ushort), X sizeof(binary) - (M_STRLEN - sizeof(ushort))) < 0) X return (warnarch("Corrupt swapped header", X (off_t) sizeof(binary) - (M_STRLEN - sizeof(ushort)))); X asb->sb_dev = (dev_t) swab(binary.b_dev); X asb->sb_ino = (ino_t) swab(binary.b_ino); X asb->sb_mode = swab(binary.b_mode); X asb->sb_uid = swab(binary.b_uid); X asb->sb_gid = swab(binary.b_gid); X asb->sb_nlink = swab(binary.b_nlink); X asb->sb_rdev = (dev_t) swab(binary.b_rdev); X asb->sb_mtime = swab(binary.b_mtime[0]) << 16 | swab(binary.b_mtime[1]); X asb->sb_size = swab(binary.b_size[0]) << 16 | swab(binary.b_size[1]); X if ((namesize = swab(binary.b_name)) == 0 || namesize >= PATHSIZE) X return (warnarch("Bad swapped pathname length", X (off_t) sizeof(binary) - (M_STRLEN - sizeof(ushort)))); X if (inread(name, namefull = namesize + namesize % 2) < 0) X return (warnarch("Corrupt swapped pathname", (off_t) namefull)); X if (name[namesize - 1] != '\0') X return (warnarch("Bad swapped pathname", (off_t) namefull)); X return (asb->sb_size % 2); } X /* X * lineget() X * X * Get a line from a given stream. Returns 0 if successful, -1 at EOF. X */ STATIC int lineget(stream, buf) reg FILE *stream; reg char *buf; { X reg int c; X X for (;;) { X if ((c = getc(stream)) == EOF) X return (-1); X if (c == '\n') X break; X *buf++ = c; X } X *buf = '\0'; X return (0); } X /* X * linkalso() X * X * Add a destination pathname to an existing chain. Assumes that X * at least one element is present. X */ STATIC void linkalso(linkp, name) reg Link *linkp; char *name; { X reg Path *path; X X if (((path = (Path *) memget(sizeof(Path))) == NULL) X || ((path->p_name = memstr(name)) == NULL)) X return; X path->p_forw = NULL; X path->p_back = linkp->l_path->p_back; X path->p_back->p_forw = path; X linkp->l_path->p_back = path; } X /* X * linkfrom() X * X * Find a file to link from. Returns a pointer to a link X * structure, or NULL if unsuccessful. X */ STATIC Link * linkfrom(asb) reg Stat *asb; { X reg Link *linkp; X reg Link *linknext; X reg Path *path; X reg Path *pathnext; X reg Link **abase; X X for (linkp = *(abase = linkhash(asb->sb_ino)); linkp; linkp = linknext) X if (linkp->l_nlink == 0) { X for (path = linkp->l_path; path; path = pathnext) { X pathnext = path->p_forw; X free(path->p_name); X } X free((char *) linkp->l_path); X if (linknext = linkp->l_forw) X linknext->l_back = linkp->l_back; X if (linkp->l_back) X linkp->l_back->l_forw = linkp->l_forw; SHAR_EOF true || echo 'restore of afio.c failed' fi echo 'End of part 1' echo 'File afio.c is continued in part 2' echo 2 > _shar_seq_.tmp exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.