buhrt@cs.purdue.edu (Jeffery A Buhrt) (02/11/91)
Submitted-by: sawmill!buhrt@cs.purdue.edu (Jeffery A Buhrt) Posting-number: Volume 1, Issue 1 Archive-name: afio/part01 [You might post this to alt.sources as well. -Jeff] Afio is a cpio clone that is much more intelligent about backups than most cpios. If I am using floppies for a backup I will not use anything else. It verifies the floppies, allows a restart if one fails, and can compress each file as it is backed up. I have been unable to contact Mark Brukhartz at Lachman Associates, and I am posting this verses patches. Please see the top of afio.c for distribution rights. I make no warranties on this software other than it seems to work great for me. I have added a few neat new features to afio. 1) Compress files as they are passing though. 2) If we are a floppy (on output) a) store the entire floppy into core before writing b) write the floppy from the in-core copy (1) if the write fails, just rewrite that disk! (2) verify the data written, BYTE-for-BYTE c) a cute little menu pops up when there are problems 3) If the machine has shared memory (and does a copy on fork vs exec) the 1.2m or 720k (etc) malloced buffer is shmemalloc()'d -The data then is not copied when afio forks to compress a file This version is running on 40+ 3b1's of ours, a few '386's, and 100+ other 3b1's at another company. Bugs (features to add): a) There is no support for malloc(795k,1.2m) on a '286 w/o a huge model -sorry b) On a UNIXPC you CAN not read(from_floppy, to_shmem, #) or write(to_floppy, from_shmem, #) without hanging the process in kernal mode (note the #ifdef UNIXPC that uses a malloced buffer). -This was fixed in UNIX3.51m+ (dK+) c) If you are compressing and the temp directory you pick is on a NSF file system, watch out for "stale handle" caused by opening the compress output file then unlinking before returning an open file descriptor. - not sure what to do, ideas welcome d) What about listing files to extract like cpio has? ex: afio -iv /dev/rfp021 /etc/passwd /etc/group I have sent this version to a few people who have not reported any problems and have had a few requests to post. Please send any comments or new code that you have. dodsk is an example script to write a simple backup. ex: find dir -print | dodsk -Jeff Buhrt 317-477-6000 {sequent, tippy.cs.purdue.edu}!sawmill!buhrt #!/bin/sh # This is a shell archive (shar 3.20) # made 01/28/1991 14:29 UTC by buhrt@sawmill # Source directory /users/buhrt/src/afio # # existing files WILL be overwritten # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 591 -rwxr-xr-x dodsk # 1777 -rw-r--r-- Makefile # 1777 -rw-rw-r-- Makefile.3b1 # 7476 -r--r--r-- afio.1 # 72522 -r--r--r-- afio.c # if touch 2>&1 | fgrep '[-amc]' > /dev/null then TOUCH=touch else TOUCH=true fi # ============= dodsk ============== echo "x - extracting dodsk (Text)" sed 's/^X//' << 'SHAR_EOF' > dodsk && X# X# for a 3b1 (UNIXPC) X# 1) Put a formatted floppy in the drive X# 2) /etc/iv -tv /dev/rfp020 X# 3) Partition 1 (/dev/rfp021) insert that number into the -s flag X# valid numbers are formatted as ###[bmk] X# b=512, k=1024, m=1048576 (ex: 1m5k105) X# X# 3b1 (UNIXPC) 3.5" 720k floppy, high density format (part 0 is 5k) X#DISKSIZE=795k X#FORMAT='iv -iv /dev/rfp020 /usr/lib/iv/FDnl' X#DEVICE=/dev/rfp021 X X# 1.2M (whole disk) drive under Unix SysV 3.2.2 X#DISKSIZE=1440k XDISKSIZE=1200k XFORMAT=format\ -v\ /dev/rdsk/f05ht XDEVICE=/dev/rdsk/f05ht Xafio -ovzFKZ -L./BackupLog -s$DISKSIZE -R$FORMAT $DEVICE SHAR_EOF $TOUCH -am 0524182390 dodsk && chmod 0755 dodsk || echo "restore of dodsk failed" set `wc -c dodsk`;Wc_c=$1 if test "$Wc_c" != "591"; then echo original size 591, current size $Wc_c fi # ============= Makefile ============== echo "x - extracting Makefile (Text)" sed 's/^X//' << 'SHAR_EOF' > Makefile && XSHELL=/bin/sh X## X## I wrote this Makefile, based on comments in the source. -rich $alz. X## Define INDEX to use index() in place of strchr() (v7, BSD). X1 = -DINDEX X#1 = -UINDEX X## Define MEMCPY when an efficient memcpy() exists (SysV). X#2 = -DMEMCPY X2 = -UMEMCPY X## Define MKDIR when a mkdir() system call is present (4.2BSD, SysVr3). X3 = -DMKDIR X#3 = -UMKDIR X## Define NOVOID if your compiler doesn't like void casts. X#4 = -DNOVOID X4 = -UNOVOID X## Define SYSTIME to use <sys/time.h> rather than <time.h> (4.2BSD). X5 = -DSYSTIME X#5 = -USYSTIME X## Define VOIDFIX to allow pointers to functions returning void (non-PCC). X#6 = -DVOIDFIX X6 = -UVOIDFIX X## Define CTC3B2 to support AT&T 3B2 streaming cartridge tape. X#7 = -DCTC3B2 X7 = -UCTC3B2 X## Define HAVEFCNTL if you have <fcntl.h> X8 = -DHAVEFCNTL X#8 = -UHAVEFCNTL X## Define USESHMEM if you have shared memory X#9 = -DUSESHMEM X9 = -UUSESHMEM X## Define MYTEMPNAM if you don't have tempnam() Xa = -DMYTEMPNAM X#a = -UMYTEMPNAM X## Define UNIXPC if you are on a 3b1, 7300, or other stupid AT&T no-work-alike X## (problem is you can't write to a floppy from shared memory) X#b = -DUNIXPC Xb = -UUNIXPC X## Define HAVEMEMCMP if you have memcmp otherwise assumes bcmp X#c = -DHAVEMEMCMP Xc = -UHAVEMEMCMP X## Define DEFFMTCMD to being how to format the media you use the most X## This is the DEFault FoRMat CoManD. Xd = -DDEFFMTCMD='"/bin/false"' X#d = -DDEFFMTCMD='"format.sh"' X#d = -DDEFFMTCMD='"iv -i /dev/rfp020 /usr/lib/iv/FDnl"' X XCC=atscc XCFLAGS = $1 $2 $3 $4 $5 $6 $7 $8 $9 $a $b $c $d -O XLDFLAGS = X Xall: afio afio.1 X Xafio: afio.c X $(CC) $(CFLAGS) -o afio afio.c X Xshar: README Makefile afio.1 afio.c X shar README Makefile afio.1 afio.c > afio.shar X Xinstall: all X @echo copy afio and afio.1 into the appropriate directories. SHAR_EOF $TOUCH -am 0128092791 Makefile && chmod 0644 Makefile || echo "restore of Makefile failed" set `wc -c Makefile`;Wc_c=$1 if test "$Wc_c" != "1777"; then echo original size 1777, current size $Wc_c fi # ============= Makefile.3b1 ============== echo "x - extracting Makefile.3b1 (Text)" sed 's/^X//' << 'SHAR_EOF' > Makefile.3b1 && XCC=ccc XSHELL=/bin/sh X## X## I wrote this Makefile, based on comments in the source. -rich $alz. X## Define INDEX to use index() in place of strchr() (v7, BSD). X#1 = -DINDEX X1 = -UINDEX X## Define MEMCPY when an efficient memcpy() exists (SysV). X2 = -DMEMCPY X#2 = -UMEMCPY X## Define MKDIR when a mkdir() system call is present (4.2BSD, SysVr3). X#3 = -DMKDIR X3 = -UMKDIR X## Define NOVOID if your compiler doesn't like void casts. X#4 = -DNOVOID X4 = -UNOVOID X## Define SYSTIME to use <sys/time.h> rather than <time.h> (4.2BSD). X#5 = -DSYSTIME X5 = -USYSTIME X## Define VOIDFIX to allow pointers to functions returning void (non-PCC). X#6 = -DVOIDFIX X6 = -UVOIDFIX X## Define CTC3B2 to support AT&T 3B2 streaming cartridge tape. X#7 = -DCTC3B2 X7 = -UCTC3B2 X## Define HAVEFCNTL if you have <fcntl.h> X8 = -DHAVEFCNTL X#8 = -UHAVEFCNTL X## Define USESHMEM if you have shared memory X9 = -DUSESHMEM X#9 = -UUSESHMEM X## Define MYTEMPNAM if you don't have tempnam() X#a = -DMYTEMPNAM Xa = -UMYTEMPNAM X## Define UNIXPC if you are on a 3b1, 7300, or other stupid AT&T no-work-alike X## (problem is you can't write to a floppy from shared memory) Xb = -DUNIXPC X#b = -UUNIXPC X## Define HAVEMEMCMP if you have memcmp otherwise assumes bcmp Xc = -DHAVEMEMCMP X#c = -UHAVEMEMCMP X## Define DEFFMTCMD to being how to format the media you use the most X## This is the DEFault FoRMat CoManD. X#d = -DDEFFMTCMD='"/bin/false"' X#d = -DDEFFMTCMD='"format.sh"' Xd = -DDEFFMTCMD=\""iv -i /dev/rfp020 /usr/lib/iv/FDnl"\" X XCFLAGS = -O $1 $2 $3 $4 $5 $6 $7 $8 $9 $a $b $c $d XLDFLAGS = X Xall: afio afio.1 X Xafio: afio.c X $(CC) $(CFLAGS) -o afio afio.c X Xshar: README Makefile afio.1 afio.c X shar README Makefile afio.1 afio.c > afio.shar X Xinstall: all X @echo copy afio and afio.1 into the appropriate directories. SHAR_EOF $TOUCH -am 0128092891 Makefile.3b1 && chmod 0664 Makefile.3b1 || echo "restore of Makefile.3b1 failed" set `wc -c Makefile.3b1`;Wc_c=$1 if test "$Wc_c" != "1777"; then echo original size 1777, current size $Wc_c fi # ============= afio.1 ============== echo "x - extracting afio.1 (Text)" sed 's/^X//' << 'SHAR_EOF' > afio.1 && X'br $Header: /users/buhrt/src/afio/afio.1,v 1.2 90/10/31 10:55:24 buhrt Exp $ X.TH AFIO 1 X.SH NAME Xafio \- manipulate archives and files X.SH SYNOPSIS X.B "afio \-o" X[ X.I options X] archive X.br X.B "afio \-t" X[ X.I options X] archive X.br X.B "afio \-i" X[ X.I options X] archive X.br X.B "afio \-p" X[ X.I options X] directory [ ... ] X.SH DESCRIPTION X.I Afio Xmanipulates groups of files, copying them within the (collective) Xfilesystem or between the filesystem and an X.I afio Xarchive. Note that X.I afio Xarchives are portable, as they contain only ASCII-formatted Xheader information. They are also compatible with ASCII X.IR cpio (1) Xarchives (ala X.IR "cpio \-c" ). X.PP XWith X.BR \-o , Xreads pathnames from the standard input Xand writes an X.IR archive . X.PP XWith X.BR \-t , Xreads an X.I archive Xand writes a table-of-contents to the standard output. X.PP XWith X.BR \-i , Xinstalls the contents of an X.I archive Xrelative to the working directory. X.PP XWith X.BR \-p , Xreads pathnames from the standard input Xand copies the files to each X.IR directory . X.PP XCreates missing directories as necessary, with permissions Xto match their parents. X.PP XGenerates sparse filesystem blocks (with X.IR lseek (2)) Xwhen possible. X.PP XSupports multi-volume archives during interactive operation X(i.e., when X.I /dev/tty Xis accessible and X.I SIGINT Xis not being ignored). X.PP XOptions: X.TP 13 X.BI \-b "\ size" XRead or write X.IR size -character Xarchive blocks. XSuffices of X.BR b , X.B k Xand X.B m Xdenote multiples of X.IR 512 , X.I 1024 Xand X.IR 1048576 , Xrespectively. XDefaults to X.I 5120 Xfor compatibility with X.IR cpio (1). X.TP X.BI \-c "\ count" XBuffer X.I count Xarchive blocks between I/O operations. A large X.I count Xis recommended with streaming magnetic tape drives. X.TP X.B \-d XDon't create missing directories. X.TP X.BI \-e "\ bound" XPad the archive to a multiple of X.I bound Xcharacters. XRecognizes the same suffices as X.BR \-s . XDefaults to X.I 1x\^ X(the X.B \-b Xblock size) Xfor compatibility with X.IR cpio (1). X.TP X.B \-f XSpawn a child process to actually write to the archive; provides Xa clumsy form of double-buffering. XRequires X.B \-s Xfor multi-volume archive support. X.TP X.B \-g XChange to input file directories. Avoids quadratic filesystem Xbehavior with long similar pathnames. Requires all absolute Xpathnames, including those for the X.B \-o X.I archive Xand the X.B \-p X.IR directories . X.TP X.B \-h XFollow symbolic links, treating them as ordinary files and directories. X.TP X.B \-j XDon't generate sparse filesystem blocks. X.TP X.B \-k XSkip corrupt data at the X.I beginning Xof an archive (rather Xthan complaining about unrecognizable input). X.TP X.B \-l XWith X.BR \-o , Xwrite file contents with each hard link. X.sp XWith X.BR \-t , Xreport hard links. X.sp XWith X.BR \-p , Xattempt to link files rather than copying them. X.TP X.B \-m XMark output files with a common current timestamp X(rather than with input file modification times). X.TP X.B \-n XProtect newer existing files (comparing file modification times). X.TP X.BI \-s "\ limit" XRestrict each portion of a multi-volume archive to X.I limit Xcharacters. XRecognizes the same suffices as X.BR \-b . XAlso, the suffix X.B x Xdenotes a multiple of the X.B \-b Xblock size (and must follow any X.B \-b Xspecification). XUseful with finite-length devices which do not return short Xcounts at end of media (sigh); output to magnetic tape typically Xfalls into this category. X.TP X.B \-u XReport files with unseen links. X.TP X.B \-v XVerbose. Report pathnames as they are processed. With X.BR \-t , Xgives an X.I "ls \-l" Xstyle report (including link information). X.TP X.B \-x XRetain file ownership and setuid/setgid permissions. XThis is the default for the super-user; he may use X.B \-X Xto override it. X.TP X.BI \-y "\ prefix" XRestrict archive processing to names beginning with X.IR prefix . XSpecify once for each prefix to be recognized. XUse X.B \-Y Xto supply prefixes which are X.I not Xto be processed. X.TP X.B \-z XPrint execution statistics. This is meant for human consumption; Xuse by other programs is officially discouraged. X.PP XSpecial-case archive names: X.RS 3 X.TP 3 X.B o XSpecify X.I \- Xto read or write the standard input or output, respectively. XThis disables multi-volume archive handling. X.TP X.B o XPrefix a command string to be executed with an exclamation mark X.RI ( ! ). XThe command is executed once for each archive volume, Xwith its standard input or output piped to X.IR afio . XIt is expected to produce a zero exit code when all is well. X.TP X.B o XUse X.I system:file Xto access an archive in X.I file Xon X.IR system . XThis is really just a special case of pipelining. XIt requires a 4.2BSD-style remote shell X.RI ( rsh (1C)) Xand a remote copy of X.IR afio . X.TP X.B o XAnything else specifies a local file or device. XAn output file will be created if it does not already exist. X.RE X.PP XSawmill localizisms (Floppy and compression support): X.TP X.B -F XThis is a floppy disk, -s is required. XUses shared memory if compiled in otherwise mallocs as needed (a 3b1 Xwill not be able to malloc the needed memory w/o shared memory), Xafio assumes either way you can malloc/shmalloc a chunck of memory Xthe size of one disk. Examples: 795k: 3.5" (720k drive), 316k (360k drive) X.nf XAt 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): X.fi X.TP X.B -K XVerify the output against what is in the memory copy of the disk (-F required). XIf the writing or verifying fails the following menu pops up X(the hidden option "quit" will also exit from the backup at this point). X.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 X.fi X.TP X.B -L Log_file_path XSpecify the name of the file to log errors and the finial totals to. X.TP X.B -R "Disk format command string" XThis is the command that is run when you enter 2 to reformat the disk. XThe default (char *formatcmd = "sh /u/store/format.sh") can be changed Xto a given system's default. X.TP X.B -Z XCompress the files on the way out (valid w/ or w/o -F or -K). X.PP XRecognizes obsolete binary X.IR cpio (1) Xarchives (including those from machines with reversed byte order), Xbut cannot write them. X.PP XRecovers from archive corruption by searching for a valid magic Xnumber. This is rather simplistic, but, much like a disassembler, Xalmost always works. X.PP XOptimizes pathnames with respect to the current and parent Xdirectories. For example, X.I ./src/sh/../misc/afio.c Xbecomes X.IR src/misc/afio.c . X.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 X.SH BUGS XThere are too many options. X.PP XRestricts pathnames to 1023 characters and 255 meaningful elements. X.PP XThere is no sequence information within multi-volume archives. XInput sequence errors generally masquerade as data corruption. XA solution would probably be mutually exclusive with X.IR cpio (1) Xcompatibility. X.PP XDegenerate uses of symbolic links are mangled by pathname optimization. XFor example, assuming that "usr.src" is a symbolic link to "/usr/src", Xthe pathname "usr.src/../bin/cu" is mis-optimized into "bin/cu" (rather Xthan "/usr/bin/cu"). X.SH "SEE ALSO" Xcpio(1), find(1), tar(1), tp(1). X.SH AUTHOR XMark Brukhartz X.br X.I "..!ihnp4!laidbak!mdb" XJeff Buhrt (floppy extensions) X.br X.I "sequent!sawmill!buhrt" SHAR_EOF $TOUCH -am 1031105590 afio.1 && chmod 0444 afio.1 || echo "restore of afio.1 failed" set `wc -c afio.1`;Wc_c=$1 if test "$Wc_c" != "7476"; then echo original size 7476, current size $Wc_c fi # ============= afio.c ============== echo "x - extracting afio.c (Text)" sed 's/^X//' << 'SHAR_EOF' > afio.c && X/* 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 "..!ihnp4!laidbak!mdb". 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 <sys/time.h> rather than <time.h> (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 * 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 Xstatic char *ident = "$Header: /users/buhrt/src/afio/myafio.c,v 1.23 1991/01/23 19:50:13 buhrt Exp $"; X X#include <stdio.h> X#include <errno.h> X#include <sys/signal.h> X#include <sys/types.h> X#include <sys/ioctl.h> X#include <sys/stat.h> X#include <pwd.h> X#include <grp.h> X X#ifndef major X# include <sys/sysmacros.h> X#endif /* major */ X X#ifdef SYSTIME X# include <sys/time.h> X#else /* SYSTIME */ X# include <time.h> X#endif /* SYSTIME */ X X#ifdef CTC3B2 X# include <sys/vtoc.h> X# include <sys/ct.h> X#endif /* CTC3B2 */ X X#ifdef MYTEMPNAM X#include <sys/file.h> X#endif X X#ifdef USESHMEM X#include <sys/ipc.h> X#include <sys/shm.h> X X#define NUMSHKEYS 20 X#define SHMEMSIZE 262144 /* 2^18 (dev3b1) */ X#endif X X/* done writing to the archive */ X#define FALSE 0 X#define TRUE 1 X#define NOTDONE 0 X#define DONE 1 X#define NODIE 0 X#define DIE 1 X X/* X * Address link information base. X */ X#define linkhash(ino) \ X (linkbase + (ino) % nel(linkbase)) X X/* X * Mininum value. X */ X#define min(one, two) \ X (one < two ? one : two) X X/* X * Number of array elements. X */ X#define nel(a) \ X (sizeof(a) / sizeof(*(a))) X X/* X * Remove a file or directory. X */ X#define remove(name, asb) \ X (((asb)->sb_mode & S_IFMT) == S_IFDIR ? rmdir(name) : unlink(name)) X X/* X * Swap bytes. X */ X#define swab(n) \ X ((((ushort)(n) >> 8) & 0xff) | (((ushort)(n) << 8) & 0xff00)) X X/* X * Cast and reduce to unsigned short. X */ X#define ush(n) \ X (((ushort) (n)) & 0177777) X X/* X * Definitions. X */ X#define reg register /* Convenience */ X#define uint unsigned int /* Not always in types.h */ X#define ushort unsigned short /* Not always in types.h */ X#define BLOCK 5120 /* Default archive block size */ X#define FSBUF (8*1024) /* Filesystem buffer size */ X#define H_COUNT 10 /* Number of items in ASCII header */ X#define H_PRINT "%06o%06o%06o%06o%06o%06o%06o%011lo%06o%011lo" X#define H_SCAN "%6ho%6ho%6ho%6ho%6ho%6ho%6ho%11lo%6o%11lo" X#define H_STRLEN 70 /* ASCII header string length */ X#define M_ASCII "070707" /* ASCII magic number */ X#define M_BINARY 070707 /* Binary magic number */ X#define M_STRLEN 6 /* ASCII magic number length */ X#define NULLDEV -1 /* Null device code */ X#define NULLINO 0 /* Null inode number */ X#define PATHELEM 256 /* Pathname element count limit */ X#define PATHSIZE 1024 /* Pathname length limit */ X#define S_IFSHF 12 /* File type shift (shb in stat.h) */ X#define S_IPERM 07777 /* File permission bits (shb in stat.h) */ X#define S_IPEXE 07000 /* Special execution bits (shb in stat.h) */ X#define S_IPOPN 0777 /* Open access bits (shb in stat.h) */ X#define STDIN 0 /* Standard input file descriptor */ X#define STDOUT 1 /* Standard output file descriptor */ X#define TTY "/dev/tty" /* For volume-change queries */ X X/* X * Some versions of the portable "C" compiler (PCC) can't handle X * pointers to functions returning void. X */ X#ifdef VOIDFIX X# define VOIDFN void /* Expect "void (*fnptr)()" to work */ X#else /* VOIDFIX */ X# define VOIDFN int /* Avoid PCC "void (*fnptr)()" bug */ X#endif /* VOIDFIX */ X X/* X * Trailer pathnames. All must be of the same length. X */ X#define TRAILER "TRAILER!!!" /* Archive trailer (cpio compatible) */ X#define TRAILZ 11 /* Trailer pathname length (including null) */ X X/* X * Open modes; there is no <fcntl.h> with v7 UNIX. X */ X#ifdef HAVEFCNTL X#include <fcntl.h> X#else X#define O_RDONLY 0 /* Read-only */ X#define O_WRONLY 1 /* Write-only */ X#define O_RDWR 2 /* Read/write */ X#endif X/* X * V7 and BSD UNIX use old-fashioned names for a couple of X * string functions. X */ X#ifdef INDEX X# define strchr index /* Forward character search */ X# define strrchr rindex /* Reverse character search */ X#endif /* INDEX */ X X/* X * Some compilers can't handle void casts. X */ X#ifdef NOVOID X# define VOID /* Omit void casts */ X#else /* NOVOID */ X# define VOID (void) /* Quiet lint about ignored return values */ X#endif /* NOVOID */ X 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 */ X#ifdef lint X# define STATIC static /* Declare static variables for lint */ X#else /* lint */ X# define STATIC /* Make static variables global for adb */ X#endif /* lint */ X X/* X * Simple types. X */ Xtypedef struct group Group; /* Structure for getgrgid(3) */ Xtypedef struct passwd Passwd; /* Structure for getpwuid(3) */ Xtypedef struct tm Time; /* Structure for localtime(3) */ X 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; X# define STAT(name, asb) stat(name, &(asb)->sb_stat) X# define FSTAT(fd, asb) fstat(fd, &(asb)->sb_stat) X# define LSTAT(name, asb) lstat(name, &(asb)->sb_stat) X# define sb_dev sb_stat.st_dev X# define sb_ino sb_stat.st_ino X# define sb_mode sb_stat.st_mode X# define sb_nlink sb_stat.st_nlink X# define sb_uid sb_stat.st_uid X# define sb_gid sb_stat.st_gid X# define sb_rdev sb_stat.st_rdev X# define sb_size sb_stat.st_size X# define sb_atime sb_stat.st_atime X# define sb_mtime sb_stat.st_mtime X# define sb_ctime sb_stat.st_ctime X# define sb_blksize sb_stat.st_blksize X# define sb_blocks sb_stat.st_blocks X#else /* S_IFLNK */ X /* X * File status without symbolic links. X */ X typedef struct stat Stat; X# define STAT(name, asb) stat(name, asb) X# define FSTAT(fd, asb) fstat(fd, asb) X# define LSTAT(name, asb) stat(name, asb) X# define sb_dev st_dev X# define sb_ino st_ino X# define sb_mode st_mode X# define sb_nlink st_nlink X# define sb_uid st_uid X# define sb_gid st_gid X# define sb_rdev st_rdev X# define sb_size st_size X# define sb_atime st_atime X# define sb_mtime st_mtime X# define sb_ctime st_ctime X#endif /* S_IFLNK */ X X/* X * Binary archive header (obsolete). X */ Xtypedef 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 */ X} Binary; X X/* X * Child process structure. X */ Xtypedef 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 */ X} Child; X X/* X * Child process flags (c_flags). X */ X#define CF_EXIT (1<<0) /* Exited */ X X/* X * Hard link sources. One or more are chained from each link X * structure. X */ Xtypedef 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 */ X} Path; X X/* X * File linking information. One entry exists for each unique X * file with with outstanding hard links. X */ Xtypedef 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 */ X} Link; X X/* X * Pathnames to (or to not) be processed. X */ Xtypedef 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 */ X} Pattern; X X/* X * External functions. X */ Xvoid _exit(); Xvoid exit(); Xvoid free(); Xchar *getenv(); Xushort getgid(); XGroup *getgrgid(); XPasswd *getpwuid(); Xushort getuid(); XTime *localtime(); Xoff_t lseek(); Xchar *malloc(); Xuint sleep(); Xchar *strcat(); Xchar *strchr(); Xchar *strcpy(); Xchar *strncpy(); Xchar *strrchr(); Xtime_t time(); Xushort umask(); X X X/* X * Internal functions. X */ XVOIDFN copyin(); XVOIDFN copyout(); Xint dirchg(); Xint dirmake(); Xint dirneed(); Xvoid fatal(); XVOIDFN in(); Xvoid inalloc(); Xint inascii(); Xint inavail(); Xint inbinary(); Xint indata(); Xint inentry(); Xint infill(); Xint inhead(); Xint inread(); Xint inskip(); Xint inswab(); Xint lineget(); Xvoid linkalso(); XLink *linkfrom(); Xvoid linkleft(); XLink *linkto(); Xvoid memcpy(); Xchar *memget(); Xchar *memstr(); Xint mkdir(); Xvoid nameadd(); Xint namecmp(); Xint nameopt(); Xvoid next(); Xvoid nextask(); Xvoid nextclos(); Xint nextopen(); Xint openin(); Xint openo(); XVOIDFN openq(); Xint options(); Xoff_t optsize(); XVOIDFN out(); Xvoid outalloc(); Xuint outavail(); Xint outdata(); Xvoid outeof(); Xvoid outflush(); Xvoid outhead(); Xvoid outpad(); Xvoid outwait(); Xvoid outwrite(); XVOIDFN pass(); Xvoid passdata(); Xint passitem(); Xint pipechld(); Xint pipeopen(); Xvoid pipewait(); Xvoid prsize(); Xint rmdir(); XVOIDFN (*signal())(); Xint swrite(); X#ifdef USESHMEM Xchar *shmemalloc(); Xvoid shmemfree(); X#endif Xchar *syserr(); X#ifdef MYTEMPNAM Xchar *tempnam(); X#endif XVOIDFN toc(); Xvoid tocentry(); Xvoid tocmode(); Xvoid usage(); Xint warn(); Xint warnarch(); Xint xfork(); Xvoid xpause(); Xint xwait(); X X/* X * External variables. X */ Xextern int errno; /* System error code */ Xextern char *sys_errlist[]; /* System error messages */ Xextern int sys_nerr; /* Number of sys_errlist entries */ X X/* X * Static variables. X */ XSTATIC short Fflag; /* X * floppy flag (write when buf full) X * set -sdisk_size as well X */ XSTATIC short Zflag; /* compress the files that we can */ XSTATIC short verifyflag; /* Verify (floppy) flag */ XSTATIC short verifycnt; X#ifdef CTC3B2 XSTATIC short Cflag; /* Enable 3B2 CTC streaming (kludge) */ X#endif /* CTC3B2 */ XSTATIC short dflag; /* Don't create missing directories */ XSTATIC short fflag; /* Fork before writing to archive */ XSTATIC short gflag; /* Change to input file directories */ XSTATIC short hflag; /* Follow symbolic links */ XSTATIC short jflag; /* Don't generate sparse filesystem blocks */ XSTATIC short kflag; /* Skip initial junk to find a header */ XSTATIC short lflag; /* Link rather than copying (when possible) */ XSTATIC short mflag; /* Ignore archived timestamps */ XSTATIC short nflag; /* Keep newer existing files */ XSTATIC short uflag; /* Report files with unseen links */ XSTATIC short vflag; /* Verbose */ XSTATIC short xflag; /* Retain file ownership */ XSTATIC short zflag; /* Print final statistics */ XSTATIC short hidequit; /* show the quit option? */ XSTATIC uint arbsize = BLOCK;/* Archive block size */ XSTATIC short areof; /* End of input volume reached */ XSTATIC int arfd = -1; /* Archive file descriptor */ XSTATIC off_t arleft; /* Space remaining within current volume */ XSTATIC char *arname; /* Expanded archive name */ XSTATIC uint arpad; /* Final archive block padding boundary */ XSTATIC char arspec[PATHSIZE];/* Specified archive name */ XSTATIC off_t aruntil; /* Volume size limit */ XSTATIC uint arvolume = 1; /* Volume number */ XSTATIC uint buflen; /* Archive buffer length */ XSTATIC char *buffer; /* Archive buffer */ XSTATIC char *bufidx; /* Archive buffer index */ XSTATIC char *bufend; /* End of data within archive buffer */ XSTATIC Child *children; /* Child processes */ XSTATIC char *formatcmd = DEFFMTCMD; /* how to format */ XSTATIC ushort gid; /* Group ID */ XSTATIC Link *linkbase[256]; /* Unresolved link information */ XSTATIC FILE *logfile = NULL;/* log same errors as stderr would */ XSTATIC ushort mask; /* File creation mask */ XSTATIC char *myname; /* Arg0 */ XSTATIC char *optarg; /* Option argument */ XSTATIC int optind; /* Command line index */ XSTATIC int outpid; /* Process ID of outstanding outflush() */ XSTATIC Pattern *pattern; /* Pathname matching patterns */ XSTATIC char pwd[PATHSIZE]; /* Working directory (with "-g") */ XSTATIC int pipepid; /* Pipeline process ID */ XSTATIC time_t timenow; /* Current time */ XSTATIC time_t timewait; /* Time spent awaiting new media */ XSTATIC off_t total; /* Total number of bytes transferred */ XSTATIC int ttyf; /* For interactive queries (yuk) */ XSTATIC ushort uid; /* User ID */ X Xmain(ac, av) Xint ac; Xreg char **av; X{ X reg int c; X reg uint group = 1; X reg 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:q")) { 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\n", ident); X exit(0); X#ifdef CTC3B2 X case 'C': X ++Cflag; X arbsize = 31 * 512; X group = 10; X aruntil = 1469 * 31 * 512; X break; X#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 default: X usage(); X } X } X if (fn == NULL || av[optind] == NULL) X usage(); X ttyf = openq(); 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 int goodbye(); 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); X#ifdef USESHMEM X /* alloc the space as shared if we will be forking a lot */ X if ((*arspec != '!') && (Fflag || fflag) X#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))) X#else X ) X#endif X { X if ((buffer = bufidx = bufend = shmemalloc(buflen, arname)) == NULL) X fatal(arspec, "Cannot allocate I/O buffer (shmem)"); X } X else X#endif X if ((buffer = bufidx = bufend = malloc(buflen)) == 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 X/* X * copyin() X * X * Copy directly from the archive to the standard output. X */ XSTATIC VOIDFN Xcopyin(av) Xreg char **av; X{ 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("<stdout>", syserr()); X else if (got > 0) X bufidx += got; X else X return; X } X} X X/* X * copyout() X * X * Copy directly from the standard input to the archive. X */ XSTATIC VOIDFN Xcopyout(av) Xreg char **av; X{ 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("<stdin>", syserr()); X else if (got == 0) X break; X else X bufidx += got; X } X outflush(DONE); X if (fflag) X outwait(); X} X X/* X * dirchg() X * X * Change to the directory containing a given file. X */ XSTATIC int Xdirchg(name, local) Xreg char *name; Xreg char *local; X{ 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 X/* X * dirmake() X * X * Make a directory. Returns zero if successful, -1 otherwise. X */ XSTATIC int Xdirmake(name, asb) Xreg char *name; Xreg Stat *asb; X{ 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 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 */ XSTATIC int Xdirneed(name) Xchar *name; X{ 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 X/* X * fatal() X * X * Print fatal message and exit. X */ XSTATIC void Xfatal(what, why) Xchar *what; Xchar *why; X{ X VOID warn(what, why); X goodbye(1); X} X X/* X * in() X * X * Read an archive. X */ XSTATIC VOIDFN Xin(av) Xreg char **av; X{ X auto Stat sb; X auto char name[PATHSIZE]; X X if (*av) X fatal(*av, "Extraneous argument"); X name[0] = '\0'; X while (inhead(name, &sb) == 0) { X if (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) X VOID fprintf(stderr, "%s\n", name); X } X} X X/* X * inalloc() X * X * Allocate input buffer space (which was previously indexed X * by inavail()). X */ XSTATIC void Xinalloc(len) Xreg uint len; X{ X bufidx += len; X total += len; X} X 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 */ XSTATIC int Xinascii(magic, name, asb) Xreg char *magic; Xreg char *name; Xreg Stat *asb; X{ 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 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 */ XSTATIC int Xinavail(bufp, lenp) Xreg char **bufp; Xuint *lenp; X{ 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 X/* X * inbinary() X * X * Read a binary header. Returns the number of trailing alignment X * bytes to skip; -1 if unsuccessful. X */ XSTATIC int Xinbinary(magic, name, asb) Xreg char *magic; Xreg char *name; Xreg Stat *asb; X{ X reg uint namefull; X auto Binary binary; X X if (*((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 X/* X * indata() X * X * Install data from an archive. Returns given file descriptor. X */ XSTATIC int Xindata(fd, size, name) Xint fd; Xreg off_t size; Xchar *name; X{ 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 = swrite(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 X/* X * inentry() X * X * Install a single archive entry. Returns zero if successful, -1 otherwise. X */ XSTATIC int Xinentry(name, asb) Xchar *name; Xreg Stat *asb; X{ X reg Link *linkp; X reg int ifd; X reg int ofd; X auto time_t tstamp[2]; X X if ((ofd = openo(name, asb, linkp = linkfrom(asb), 0)) > 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 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 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 */ XSTATIC int Xinfill() X{ 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 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 */ XSTATIC int Xinhead(name, asb) Xreg char *name; Xreg Stat *asb; X{ 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 } 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 } X#endif /* S_IFLNK */ X if (name[0] == '/') 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 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 */ XSTATIC int Xinread(dst, len) Xreg char *dst; Xuint len; X{ 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 X/* X * inskip() X * X * Skip input archive data. Returns zero under normal circumstances, X * -1 if unreadable data is encountered. X */ XSTATIC int Xinskip(len) Xreg off_t len; X{ 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 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 */ XSTATIC int Xinswab(magic, name, asb) Xreg char *magic; Xreg char *name; Xreg Stat *asb; X{ X reg ushort namesize; X reg uint namefull; X auto Binary binary; X X if (*((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 X/* X * lineget() X * X * Get a line from a given stream. Returns 0 if successful, -1 at EOF. X */ XSTATIC int Xlineget(stream, buf) Xreg FILE *stream; Xreg char *buf; X{ 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 X/* X * linkalso() X * X * Add a destination pathname to an existing chain. Assumes that X * at least one element is present. X */ XSTATIC void Xlinkalso(linkp, name) Xreg Link *linkp; Xchar *name; X{ 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 X/* X * linkfrom() X * X * Find a file to link from. Returns a pointer to a link X * structure, or NULL if unsuccessful. X */ XSTATIC Link * Xlinkfrom(asb) Xreg Stat *asb; X{ 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; X else X *abase = linkp->l_forw; X free((char *) linkp); X } else if (linkp->l_ino == asb->sb_ino X && linkp->l_dev == asb->sb_dev) { X --linkp->l_nlink; X return (linkp); X } else X linknext = linkp->l_forw; X return (NULL); X} X X X/* X * linkleft() X * X * Complain about files with unseen links. X */ XSTATIC void Xlinkleft() X{ X reg Link *lp; X reg Link **base; X X for (base = linkbase; base < linkbase + nel(linkbase); ++base) X for (lp = *base; lp; lp = lp->l_forw) X if (lp->l_nlink) X VOID warn(lp->l_path->p_name, "Unseen link(s)"); X} X X/* X * linkto() X * X * Remember a file with outstanding links. Returns a X * pointer to the associated link structure, or NULL X * when linking is not possible. X */ XSTATIC Link * Xlinkto(name, asb) Xchar *name; Xreg Stat *asb; X{ X reg Link *linkp; X reg Path *path; X reg Link **abase; X X if ((asb->sb_mode & S_IFMT) == S_IFDIR X || (linkp = (Link *) memget(sizeof(Link))) == NULL X || (path = (Path *) memget(sizeof(Path))) == NULL X || (path->p_name = memstr(name)) == NULL) X return (NULL); X linkp->l_dev = asb->sb_dev; X linkp->l_ino = asb->sb_ino; X linkp->l_nlink = asb->sb_nlink - 1; X linkp->l_size = asb->sb_size; X linkp->l_path = path; X path->p_forw = NULL; X path->p_back = path; X if (linkp->l_forw = *(abase = linkhash(asb->sb_ino))) X linkp->l_forw->l_back = linkp; X linkp->l_back = NULL; X return (*abase = linkp); X} X X#ifndef MEMCPY X X/* X * memcpy() X * X * A simple block move. X */ XSTATIC void Xmemcpy(to, from, len) Xreg char *to; Xreg char *from; Xuint len; X{ X reg char *toend; X X for (toend = to + len; to < toend; *to++ = *from++) X ; X} X X#endif /* MEMCPY */ X X/* X * memget() X * X * Allocate memory. X */ XSTATIC char * Xmemget(len) Xuint len; X{ X reg char *mem; X static short outofmem; X X if ((mem = malloc(len)) == NULL && !outofmem) X outofmem = warn("memget()", "Out of memory"); X return (mem); X} X X/* X * memstr() X * X * Duplicate a string into dynamic memory. X */ XSTATIC char * Xmemstr(str) Xreg char *str; X{ X reg char *mem; X X if (mem = memget((uint) strlen(str) + 1)) X VOID strcpy(mem, str); X return (mem); X} X X#ifndef MKDIR X X/* X * mkdir() X * X * Make a directory via "/bin/mkdir". Sets errno to a X * questionably sane value upon failure. X */ XSTATIC int Xmkdir(name, mode) Xreg char *name; Xreg ushort mode; X{ X reg int pid; X X if ((pid = xfork("mkdir()", DIE)) == 0) { X VOID close(fileno(stdin)); X VOID close(fileno(stdout)); X VOID close(fileno(stderr)); X VOID open("/dev/null", O_RDWR); X VOID dup(fileno(stdin)); X VOID dup(fileno(stdin)); X VOID umask(~mode); X VOID execl("/bin/mkdir", "mkdir", name, (char *) NULL); X exit(1); X } X if (xwait(pid, "mkdir()", TRUE) == 0) X return (0); X errno = EACCES; X return (-1); X} X X#endif /* MKDIR */ X X/* X * nameadd() X * X * Add a name to the pattern list. X */ XSTATIC void Xnameadd(name, not) Xreg char *name; Xint not; X{ X reg Pattern *px; X X px = (Pattern *) memget(sizeof(Pattern)); X px->p_forw = pattern; X px->p_str = name; X px->p_len = strlen(name); X px->p_not = not; X pattern = px; X} X X/* X * namecmp() X * X * Compare a pathname with the pattern list. Returns 0 for X * a match, -1 otherwise. X */ XSTATIC int Xnamecmp(name) Xreg char *name; X{ X reg Pattern *px; X reg int positive; X reg int match; X int x; X int wild = 0; X int comp; X X positive = match = 0; X if (pattern == NULL) return(0); /* Ok if nothing spec'd */ X for (px = pattern; px; px = px->p_forw) { X if (!px->p_not) X ++positive; X x = px->p_len; X if (px->p_str[px->p_len - 1] == '*') { X x--; /* If wild, back up one */ X wild++; X } X comp = strncmp(name, px->p_str, x); X if (px->p_not) { X if (wild == 0) { /* If not wild */ X if ((name[x] != '\0') || (comp != 0)) X ++match; X else X return(-1); /* OUT NOW */ X } else X if (comp != 0) X ++match; X else X return(-1); /* OUT NOW */ X } else { X if (wild == 0) { /* If not wild */ X if ((name[x] == '\0') && (comp == 0)) X ++match; X } else X if (comp == 0) X ++match; X } X } X if (match > 0) X return(0); X return(-1); X} X X/* X * nameopt() X * X * Optimize a pathname. Confused by "<symlink>/.." twistiness. X * Returns the number of final pathname elements (zero for "/" X * or ".") or -1 if unsuccessful. X */ XSTATIC int Xnameopt(begin) Xchar *begin; X{ X reg char *name; X reg char *item; X reg int idx; X int absolute; X auto char *element[PATHELEM]; X X absolute = (*(name = begin) == '/'); X idx = 0; X for (;;) { X if (idx == PATHELEM) X return (warn(begin, "Too many elements")); X while (*name == '/') X ++name; X if (*name == '\0') X break; X element[idx] = item = name; X while (*name && *name != '/') X ++name; X if (*name) X *name++ = '\0'; X if (strcmp(item, "..") == 0) X if (idx == 0) X if (absolute) X ; X else X ++idx; X else if (strcmp(element[idx - 1], "..") == 0) X ++idx; X else X --idx; X else if (strcmp(item, ".") != 0) X ++idx; X } X if (idx == 0) X element[idx++] = absolute ? "" : "."; X element[idx] = NULL; X name = begin; X if (absolute) X *name++ = '/'; X for (idx = 0; item = element[idx]; ++idx, *name++ = '/') X while (*item) X *name++ = *item++; X *--name = '\0'; X return (idx); X} X X/* X * next() X * X * Advance to the next archive volume. X */ XSTATIC void Xnext(mode, why) Xreg int mode; Xreg char *why; X{ X reg time_t began; X auto char msg[200]; X auto char answer[20]; X X began = time((time_t *) NULL); X nextclos(); X VOID warnarch(why, (off_t) 0); X if (arfd == STDIN || arfd == STDOUT) X goodbye(1); X ++arvolume; /* change disk # here */ X VOID sprintf(msg, "\ X%s: Ready for disk %u on \"%s\" (remove the disk when the light goes out)\n\ X%s: Type \"go\" (or \"GO\") when ready to proceed %s: \07", X myname, arvolume, arspec, myname, X hidequit ? "" : "(or \"quit\" to abort )"); X for (;;) { X nextask(msg, answer, sizeof(answer)); X if (strcmp(answer, "quit") == 0) X fatal(arspec, "Aborted"); X if ((strcmp(answer, "go") == 0) || X (strcmp(answer, "GO") == 0)) X { X /* only open the disk now if we it it right now */ X if (!Fflag || (mode == O_RDONLY)) X { if (nextopen(mode) == 0) X break; X } X else X break; X } X } X VOID warnarch("Continuing", (off_t) 0); X timewait += time((time_t *) NULL) - began; X} X X/* X * nextask() X * X * Ask a question and get a response. Ignores spaces and tabs. X */ XSTATIC void Xnextask(msg, answer, limit) Xreg char *msg; Xreg char *answer; Xreg int limit; X{ X reg int idx; X reg int got; X auto char c; X X if (ttyf < 0) X fatal(TTY, "Unavailable"); X VOID write(ttyf, msg, (uint) strlen(msg)); X idx = 0; X while ((got = read(ttyf, &c, 1)) == 1) X if (c == '\04' || c == '\n') X break; X else if (c == ' ' || c == '\t') X continue; X else if (idx < limit - 1) X answer[idx++] = c; X if (got < 0) X fatal(TTY, syserr()); X answer[idx] = '\0'; X} X X/* X * nextclos() X * X * Close an archive. X */ XSTATIC void Xnextclos() X{ X if (arfd != STDIN && arfd != STDOUT) X VOID close(arfd); X areof = 0; X if (arname && *arname == '!') X pipewait(); X} X X/* X * nextopen() X * X * Open an archive. Returns 0 if successful, -1 otherwise. X */ XSTATIC int Xnextopen(mode) Xint mode; X{ X if (*arname == '!') X arfd = pipeopen(mode); X else if (strcmp(arname, "-") == 0) X arfd = mode ? STDOUT : STDIN; X else { X#ifdef CTC3B2 X if (Cflag) { X reg int oops; X reg int fd; X X oops = ((fd = open(arname, O_RDWR | O_CTSPECIAL)) < 0 X || ioctl(fd, STREAMON) < 0); X VOID close(fd); X if (oops) X return (warnarch(syserr(), (off_t) 0)); X } X#endif /* CTC3B2 */ X arfd = mode ? creat(arname, 0666 & ~mask) : open(arname, mode); X } X if (arfd < 0) X return (warnarch(syserr(), (off_t) 0)); X arleft = aruntil; X return (0); X} X X/* X * openin() X * X * Open the next input file. Returns a file descriptor, 0 if no data X * exists, or -1 at EOF. This kludge works because standard input is X * in use, preventing open() from returning zero. X */ XSTATIC int Xopenin(name, asb) Xchar *name; Xreg Stat *asb; X{ X int fd; X auto char local[PATHSIZE]; X X for (;;) { X if (lineget(stdin, name) < 0) X return (-1); X if (nameopt(name) < 0) X continue; X if (!gflag) X VOID strcpy(local, name); X else if (dirchg(name, local) < 0) X continue; X if ((hflag ? STAT(local, asb) : LSTAT(local, asb)) < 0) { X VOID warn(name, syserr()); X continue; X } X switch (asb->sb_mode & S_IFMT) { X case S_IFDIR: X asb->sb_nlink = 1; X asb->sb_size = 0; X return (0); X#ifdef S_IFLNK X case S_IFLNK: X if ((asb->sb_size = readlink(local, X asb->sb_link, sizeof(asb->sb_link) - 1)) < 0) { X VOID warn(name, syserr()); X continue; X } X asb->sb_link[asb->sb_size] = '\0'; X return (0); X#endif /* S_IFLNK */ X case S_IFREG: X if (asb->sb_size == 0) X return (0); X if ((fd = open(local, O_RDONLY)) >= 0) X { if (Zflag) X compressfile(&fd, name, asb); X X return (fd); X } X VOID warn(name, syserr()); X break; X default: X asb->sb_size = 0; X return (0); X } X } X} X X/* X * openo() X * X * Open an output file. Returns the output file descriptor, X * 0 if no data is required or -1 if unsuccessful. Note that X * UNIX open() will never return 0 because the standard input X * is in use. X */ XSTATIC int Xopeno(name, asb, linkp, ispass) Xchar *name; Xreg Stat *asb; XLink *linkp; Xreg int ispass; X{ X reg int exists; X reg int fd; X reg ushort perm; X ushort operm; X ushort ouid; X ushort ogid; X Path *path; X auto Stat osb; X#ifdef S_IFLNK X reg int ssize; X auto char sname[PATHSIZE]; X#endif /* S_IFLNK */ X X if (exists = (LSTAT(name, &osb) == 0)) X if (ispass X && osb.sb_ino == asb->sb_ino X && osb.sb_dev == asb->sb_dev) X return (warn(name, "Same file")); X else if ((osb.sb_mode & S_IFMT) == (asb->sb_mode & S_IFMT)) X operm = osb.sb_mode & (xflag ? S_IPERM : S_IPOPN); X else if (remove(name, &osb) < 0) X return (warn(name, syserr())); X else X exists = 0; X if (linkp) { X if (exists) X if (asb->sb_ino == osb.sb_ino X && asb->sb_dev == osb.sb_dev) X return (0); X else if (unlink(name) < 0) X return (warn(name, syserr())); X else X exists = 0; X for (path = linkp->l_path; path; path = path->p_forw) X if (link(path->p_name, name) == 0 X || (errno == ENOENT X && dirneed(name) == 0 X && link(path->p_name, name) == 0)) X return (0); X else if (errno != EXDEV) X return (warn(name, syserr())); X VOID warn(name, "Link broken"); X linkalso(linkp, name); X } X perm = asb->sb_mode & (xflag ? S_IPERM : S_IPOPN); X if (exists){ X ouid = osb.sb_uid; X ogid = osb.sb_gid; X } X switch (asb->sb_mode & S_IFMT) { X case S_IFBLK: X case S_IFCHR: X fd = 0; X if (exists) X if (asb->sb_rdev == osb.sb_rdev) X if (perm != operm && chmod(name, perm) < 0) X return (warn(name, syserr())); X else X break; X else if (remove(name, &osb) < 0) X return (warn(name, syserr())); X else X exists = 0; X if (mknod(name, asb->sb_mode, asb->sb_rdev) < 0 X && (errno != ENOENT X || dirneed(name) < 0 X || mknod(name, asb->sb_mode, asb->sb_rdev) < 0)) X return (warn(name, syserr())); X break; X case S_IFDIR: X if (exists) X if (perm != operm && chmod(name, perm) < 0) X return (warn(name, syserr())); X else if (xflag && (asb->sb_uid != ouid || asb->sb_gid != ogid) && X chown(name, asb->sb_uid, asb->sb_gid) < 0) X return (warn(name, syserr())); X else X ; X else if (dirneed(name) < 0 || dirmake(name, asb) < 0) X return (warn(name, syserr())); X return (0); X#ifdef S_IFIFO X case S_IFIFO: X fd = 0; X if (exists) X if (perm != operm && chmod(name, perm) < 0) X return (warn(name, syserr())); X else X ; X else if (mknod(name, asb->sb_mode, (dev_t) 0) < 0 X && (errno != ENOENT X || dirneed(name) < 0 X || mknod(name, asb->sb_mode, (dev_t) 0) < 0)) X return (warn(name, syserr())); X break; X#endif /* S_IFIFO */ X#ifdef S_IFLNK X case S_IFLNK: X if (exists) X if ((ssize = readlink(name, sname, sizeof(sname))) < 0) X return (warn(name, syserr())); X else if (strncmp(sname, asb->sb_link, ssize) == 0) X return (0); X else if (remove(name, &osb) < 0) X return (warn(name, syserr())); X else X exists = 0; X if (symlink(asb->sb_link, name) < 0 X && (errno != ENOENT X || dirneed(name) < 0 X || symlink(asb->sb_link, name) < 0)) X return (warn(name, syserr())); X return (0); /* Can't chown()/chmod() a symbolic link */ X#endif /* S_IFLNK */ X case S_IFREG: X if (exists) X if (nflag && osb.sb_mtime > asb->sb_mtime) X return (warn(name, "Newer file exists")); X else if (unlink(name) < 0) X return (warn(name, syserr())); X else X exists = 0; X if ((fd = creat(name, perm)) < 0 X && (errno != ENOENT X || dirneed(name) < 0 X || (fd = creat(name, perm)) < 0)) X return (warn(name, syserr())); X break; X default: X return (warn(name, "Unknown filetype")); X } X if (xflag X && (!exists X || asb->sb_uid != osb.sb_uid X || asb->sb_gid != osb.sb_gid)) X VOID chown(name, X uid == 0 ? ush(asb->sb_uid) : uid, X ush(asb->sb_gid)); X if (linkp == NULL && asb->sb_nlink > 1) X VOID linkto(name, asb); X return (fd); X} X X/* X * openq() X * X * Open the terminal for interactive queries (sigh). Assumes that X * background processes ignore interrupts and that the open() or X * the isatty() will fail for processes which are not attached to X * terminals. Returns a file descriptor (-1 if unsuccessful). X */ Xint Xopenq() X{ X reg VOIDFN (*intr)(); X int fd; X X fd = -1; X if (!Fflag) X { X if ((intr = signal(SIGINT, SIG_IGN)) == SIG_IGN) X return; X VOID signal(SIGINT, intr); X } X X if ((fd = open(TTY, O_RDWR)) < 0) X { warn(TTY, syserr()); X } X else X if (!isatty(fd)) X warn(TTY, "Is not a tty"); X} X X/* X * options() X * X * Decode most reasonable forms of UNIX option syntax. Takes main()- X * style argument indices (argc/argv) and a string of valid option X * letters. Letters denoting options with arguments must be followed X * by colons. With valid options, returns the option letter and points X * "optarg" at the associated argument (if any). Returns '?' for bad X * options and missing arguments. Returns zero when no options remain, X * leaving "optind" indexing the first remaining argument. X */ XSTATIC int Xoptions(ac, av, proto) Xint ac; Xregister char **av; Xchar *proto; X{ X register int c; X register char *idx; X static int optsub; X X if (optind == 0) { X optind = 1; X optsub = 0; X } X optarg = NULL; X if (optind >= ac) X return (0); X if (optsub == 0 && (av[optind][0] != '-' || av[optind][1] == '\0')) X return (0); X switch (c = av[optind][++optsub]) { X case '\0': X ++optind; X optsub = 0; X return (options(ac, av, proto)); X case '-': X ++optind; X optsub = 0; X return (0); X case ':': X return ('?'); X } X if ((idx = strchr(proto, c)) == NULL) X return ('?'); X if (idx[1] != ':') X return (c); X optarg = &av[optind][++optsub]; X ++optind; X optsub = 0; X if (*optarg) X return (c); X if (optind >= ac) X return ('?'); X optarg = av[optind++]; X return (c); X} X X/* X * optsize() X * X * Interpret a "size" argument. Recognizes suffices for blocks X * (512-byte), kilobytes and megabytes and blocksize. Returns X * the size in bytes. X */ XSTATIC off_t Xoptsize(str) Xchar *str; X{ X reg char *idx; X reg off_t number; X reg off_t result; X X result = 0; X idx = str; X for (;;) { X number = 0; X while (*idx >= '0' && *idx <= '9') X number = number * 10 + *idx++ - '0'; X switch (*idx++) { X case 'b': X result += number * 512; X continue; X case 'k': X result += number * 1024; X continue; X case 'm': X result += number * 1024L * 1024L; X continue; X case 'x': X result += number * arbsize; X continue; X case '+': X result += number; X continue; X case '\0': X result += number; X break; X default: X break; X } X break; X } X if (*--idx) X fatal(str, "Unrecognizable value"); X return (result); X} X X/* X * out() X * X * Write an archive. X */ XSTATIC VOIDFN Xout(av) Xchar **av; X{ X reg int fd; X auto Stat sb; X auto char name[PATHSIZE]; X X if (*av) X fatal(*av, "Extraneous argument"); X while ((fd = openin(name, &sb)) >= 0) { X if (!lflag && sb.sb_nlink > 1) X { if (linkfrom(&sb)) X sb.sb_size = 0; X else X VOID linkto(name, &sb); X } X X outhead(name, &sb); X if (fd) X VOID close(outdata(fd, name, sb.sb_size)); X if (vflag) X VOID fprintf(stderr, "%s\n", name); X } X outeof(TRAILER, TRAILZ); X} X X/* X * outalloc() X * X * Allocate buffer space previously referenced by outavail(). X */ XSTATIC void Xoutalloc(len) Xreg uint len; X{ X bufidx += len; X total += len; X} X X/* X * outavail() X * X * Index buffer space for archive output. Stores a buffer pointer X * at a given location. Returns the number of bytes available. X */ XSTATIC uint Xoutavail(bufp) Xreg char **bufp; X{ X reg uint have; X X while ((have = bufend - bufidx) == 0) X outflush(NOTDONE); X *bufp = bufidx; X return (have); X} X X/* X * outdata() X * X * Write archive data. Continues after file read errors, padding with X * null characters if neccessary. Always returns the given input file X * descriptor. X */ XSTATIC int Xoutdata(fd, name, size) Xint fd; Xchar *name; Xreg off_t size; X{ X reg uint chunk; X reg int got; X reg int oops; X reg uint avail; X auto char *buf; X X oops = got = 0; X while (size) { X avail = outavail(&buf); X size -= (chunk = size < avail ? (uint) size : avail); X if (oops == 0 && (got = read(fd, buf, chunk)) < 0) { X oops = warn(name, syserr()); X got = 0; X } X if (got < chunk) { X if (oops == 0) X oops = warn(name, "Early EOF"); X while (got < chunk) X buf[got++] = '\0'; X } X outalloc(chunk); X } X return (fd); X} X X/* X * outeof() X * X * Write an archive trailer. X */ XSTATIC void Xouteof(name, namelen) Xchar *name; Xreg uint namelen; X{ X reg off_t pad; X auto char header[M_STRLEN + H_STRLEN + 1]; X X if (pad = (total + M_STRLEN + H_STRLEN + namelen) % arpad) X pad = arpad - pad; X VOID strcpy(header, M_ASCII); X VOID sprintf(header + M_STRLEN, H_PRINT, 0, 0, X 0, 0, 0, 1, 0, (time_t) 0, namelen, pad); X outwrite(header, M_STRLEN + H_STRLEN); X outwrite(name, namelen); X outpad(pad); X outflush(DONE); X if (fflag) X outwait(); X} X X/* X * outflush() X * X * Flush the output buffer. Optionally fork()s to allow the X * parent to refill the buffer while the child waits for the X * write() to complete. X */ XSTATIC void Xoutflush(done) Xint done; X{ X int wrstat; X X /* X * in this case we are a floppy and want to write the floppy from one X * buffer (to have a copy of the data to verify against) X */ X bufend = buffer + (aruntil ? min(buflen, arleft) : buflen); X if (Fflag && (done == NOTDONE) && ((bufend - bufidx) > 0)) X { return; X } X X if (aruntil && arleft == 0) X next(O_WRONLY, "Output limit reached"); X X /* X * if we are a floppy open the disk at the last moment X * call verify w/ an error if the disk can't be opened X */ X if (Fflag) X { X if (nextopen(O_WRONLY) < 0) X { X verifycnt = 0; X verify(1); X if (done == NOTDONE) X next(O_WRONLY, "Disk's output limit reached (disk one)"); X bufend = (bufidx = buffer) + (aruntil ? min(buflen, arleft) : buflen); X return; X } X } X if (fflag) { X outwait(); X if ((outpid = xfork("outflush()", DIE)) == 0) X VOID nice(-40); X } X X if (!fflag || outpid == 0) { X wrstat = writedisk(1); X if (Fflag && verifyflag) X { verifycnt = 0; X verify(wrstat); X if (done == NOTDONE) X next(O_WRONLY, "Disk's output limit reached"); X } X X } X if (fflag) { X if (outpid == 0) X _exit(0); X else X arleft -= bufidx - buffer; X } X bufend = (bufidx = buffer) + (aruntil ? min(buflen, arleft) : buflen); X} X X/* X * outhead() X * X * Write an archive header. X */ XSTATIC void Xouthead(name, asb) Xreg char *name; Xreg Stat *asb; X{ X reg uint namelen; X auto char header[M_STRLEN + H_STRLEN + 1]; X X if (name[0] == '/') X if (name[1]) X ++name; X else X name = "."; X namelen = (uint) strlen(name) + 1; X VOID strcpy(header, M_ASCII); X VOID sprintf(header + M_STRLEN, H_PRINT, ush(asb->sb_dev), X ush(asb->sb_ino), ush(asb->sb_mode), ush(asb->sb_uid), X ush(asb->sb_gid), ush(asb->sb_nlink), ush(asb->sb_rdev), X mflag ? timenow : asb->sb_mtime, namelen, asb->sb_size); X outwrite(header, M_STRLEN + H_STRLEN); X outwrite(name, namelen); X#ifdef S_IFLNK X if ((asb->sb_mode & S_IFMT) == S_IFLNK) X outwrite(asb->sb_link, (uint) asb->sb_size); X#endif /* S_IFLNK */ X} X X/* X * outpad() X * X * Pad the archive. X */ XSTATIC void Xoutpad(pad) Xreg off_t pad; X{ X reg int idx; X reg int len; X X while (pad) { X if ((len = bufend - bufidx) > pad) X len = pad; X for (idx = 0; idx < len; ++idx) X *bufidx++ = '\0'; X total += len; X outflush(NOTDONE); X pad -= len; X } X} X X/* X * outwait() X * X * Wait for the last background outflush() process (if any). The child X * exit value is zero if successful, 255 if a write() returned zero or X * the value of errno if a write() was unsuccessful. X */ XSTATIC void Xoutwait() X{ X auto int status; X X if (outpid == 0) X return; X status = xwait(outpid, "outwait()", TRUE); X outpid = 0; X if (status) X fatal(arspec, "Child error"); X} X X/* X * outwrite() X * X * Write archive data. X */ XSTATIC void Xoutwrite(idx, len) Xreg char *idx; Xuint len; X{ X reg uint have; X reg uint want; X reg char *endx = idx + len; X X while (want = endx - idx) { X while ((have = bufend - bufidx) == 0) X outflush(NOTDONE); X if (have > want) X have = want; X memcpy(bufidx, idx, have); X bufidx += have; X idx += have; X total += have; X } X} X X/* X * pass() X * X * Copy within the filesystem. X */ XSTATIC VOIDFN Xpass(av) Xreg char **av; X{ X reg int fd; X reg char **avx; X auto Stat sb; X auto char name[PATHSIZE]; X X for (avx = av; *avx; ++avx) { X if (gflag && **avx != '/') X fatal(*avx, "Relative pathname"); X if (STAT(*avx, &sb) < 0) X fatal(*avx, syserr()); X if ((sb.sb_mode & S_IFMT) != S_IFDIR) X fatal(*avx, "Not a directory"); X } X while ((fd = openin(name, &sb)) >= 0) { X if (passitem(name, &sb, fd, av)) X VOID close(fd); X if (vflag) X VOID fprintf(stderr, "%s\n", name); X } X} X X/* X * passdata() X * X * Copy data to one file. Doesn't believe in input file X * descriptor zero (see description of kludge in openin() X * comments). Closes the provided output file descriptor. X */ XSTATIC void Xpassdata(from, ifd, to, ofd) Xchar *from; Xreg int ifd; Xchar *to; Xreg int ofd; X{ X reg int got; X reg int sparse; X auto char block[FSBUF]; X X if (ifd) { X VOID lseek(ifd, (off_t) 0, 0); X sparse = 0; X while ((got = read(ifd, block, sizeof(block))) > 0 X && (sparse = swrite(ofd, block, (uint) got)) >= 0) X total += got; X if (got) X VOID warn(got < 0 ? from : to, syserr()); X else if (sparse > 0 X && (lseek(ofd, (off_t) -sparse, 1) < 0 X || write(ofd, block, (uint) sparse) != sparse)) X VOID warn(to, syserr()); X } X VOID close(ofd); X} X X/* X * passitem() X * X * Copy one file. Returns given input file descriptor. X */ XSTATIC int Xpassitem(from, asb, ifd, dir) Xchar *from; XStat *asb; Xreg int ifd; Xreg char **dir; X{ X reg int ofd; X auto time_t tstamp[2]; X auto char to[PATHSIZE]; X X while (*dir) { X if (nameopt(strcat(strcat(strcpy(to, *dir++), "/"), from)) < 0) X continue; X if ((ofd = openo(to, asb, X lflag ? linkto(from, asb) : linkfrom(asb), 1)) < 0) X continue; X if (ofd > 0) X passdata(from, ifd, to, ofd); X tstamp[0] = tstamp[1] = mflag ? timenow : asb->sb_mtime; X VOID utime(to, tstamp); X } X return (ifd); X} X X/* X * pipechld() X * X * Child portion of pipeline fork. X */ XSTATIC int Xpipechld(mode, pfd) Xint mode; Xreg int *pfd; X{ X reg char **av; X auto char *arg[32]; X X av = arg; X if ((*av = getenv("SHELL")) && **av) X ++av; X else X *av++ = "/bin/sh"; X *av++ = "-c"; X *av++ = arname + 1; X *av = NULL; X if (mode) { X VOID close(pfd[1]); X VOID close(STDIN); X VOID dup(pfd[0]); X VOID close(pfd[0]); X VOID close(STDOUT); X VOID open("/dev/null", O_WRONLY); X } else { X VOID close(STDIN); X VOID open("/dev/null", O_RDONLY); X VOID close(pfd[0]); X VOID close(STDOUT); X VOID dup(pfd[1]); X VOID close(pfd[1]); X } X if (ttyf >= 0) X VOID close(ttyf); X VOID execvp(arg[0], arg); X VOID warn(arg[0], syserr()); X _exit(1); X} X X/* X * pipeopen() X * X * Open an archive via a pipeline. Returns a file X * descriptor, or -1 if unsuccessful. X */ XSTATIC int Xpipeopen(mode) Xreg int mode; X{ X auto int pfd[2]; X X if (pipe(pfd) < 0) X return (-1); X if ((pipepid = xfork("pipeopen()", DIE)) == 0) X pipechld(mode, pfd); X if (mode) { X VOID close(pfd[0]); X return (pfd[1]); X } else { X VOID close(pfd[1]); X return (pfd[0]); X } X} X X/* X * pipewait() X * X * Await a pipeline. X */ XSTATIC void Xpipewait() X{ X reg int status; X X if (pipepid == 0) X return; X status = xwait(pipepid, "pipewait()", TRUE); X pipepid = 0; X if (status) X fatal(arspec, "Pipeline error"); X} X X/* X * prsize() X * X * Print a file offset. X */ XSTATIC void Xprsize(stream, size) XFILE *stream; Xreg off_t size; X{ X reg off_t n; X X if (n = (size / (1024L * 1024L))) { X VOID fprintf(stream, "%ldm+", n); X size -= n * 1024 * 1024; X } X if (n = (size / 1024)) { X VOID fprintf(stream, "%ldk+", n); X size -= n * 1024; X } X VOID fprintf(stream, "%ld", size); X} X X#ifndef MKDIR X X/* X * rmdir() X * X * Remove a directory via "/bin/rmdir". Sets errno to a X * questionably sane value upon failure. X */ XSTATIC int Xrmdir(name) Xreg char *name; X{ X reg int pid; X X if ((pid = xfork("rmdir()", DIE)) == 0) { X VOID close(fileno(stdin)); X VOID close(fileno(stdout)); X VOID close(fileno(stderr)); X VOID open("/dev/null", O_RDWR); X VOID dup(fileno(stdin)); X VOID dup(fileno(stdin)); X VOID execl("/bin/rmdir", "rmdir", name, (char *) NULL); X exit(1); X } X if (xwait(pid, "rmdir()", TRUE) == 0) X return (0); X errno = EACCES; X return (-1); X} X X#endif /* MKDIR */ X X/* X * swrite() X * X * Write a filesystem block. Seeks past sparse blocks. Returns X * 0 if the block was written, the given length for a sparse X * block or -1 if unsuccessful. X */ XSTATIC int Xswrite(fd, buf, len) Xint fd; Xchar *buf; Xuint len; X{ X reg char *bidx; X reg char *bend; X X if (jflag) X return (write(fd, buf, len) == len ? 0 : -1); X bend = (bidx = buf) + len; X while (bidx < bend) X if (*bidx++) X return (write(fd, buf, len) == len ? 0 : -1); X return (lseek(fd, (off_t) len, 1) < 0 ? -1 : len); X} X X/* X * syserr() X * X * Return pointer to appropriate system error message. X */ XSTATIC char * Xsyserr() X{ X static char msg[40]; X X if (errno > 0 && errno < sys_nerr) X return (sys_errlist[errno]); X VOID sprintf(msg, "Unknown error (errno %d)", errno); X return (msg); X} X X/* X * toc() X * X * Print archive table of contents. X */ XSTATIC VOIDFN Xtoc(av) Xreg char **av; X{ X auto Stat sb; X auto char name[PATHSIZE]; X X if (*av) X fatal(*av, "Extraneous argument"); X name[0] = '\0'; X while (inhead(name, &sb) == 0) { X if (namecmp(name) == 0) X tocentry(name, &sb); X if (inskip(sb.sb_size) < 0) X VOID warn(name, "File data is corrupt"); X } X} X X/* X * tocentry() X * X * Print a single table-of-contents entry. X */ XSTATIC void Xtocentry(name, asb) Xchar *name; Xreg Stat *asb; X{ X reg Time *atm; X reg Link *from; X reg Passwd *pwp; X reg Group *grp; X static char *month[] = { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" X }; X X if (vflag) { X tocmode(asb->sb_mode); X VOID printf(" %2d", asb->sb_nlink); X atm = localtime(&asb->sb_mtime); X if (pwp = getpwuid(ush(asb->sb_uid))) X VOID printf(" %-8s", pwp->pw_name); X else X VOID printf(" %-8u", ush(asb->sb_uid)); X if (grp = getgrgid(ush(asb->sb_gid))) X VOID printf(" %-8s", grp->gr_name); X else X VOID printf(" %-8u", ush(asb->sb_gid)); X switch (asb->sb_mode & S_IFMT) { X case S_IFBLK: X case S_IFCHR: X VOID printf(" %3d, %3d", X major(asb->sb_rdev), minor(asb->sb_rdev)); X break; X case S_IFREG: X VOID printf(" %8ld", asb->sb_size); X break; X default: X VOID printf(" "); X } X VOID printf(" %3s %2d %02d:%02d:%02d %4d ", X month[atm->tm_mon], atm->tm_mday, atm->tm_hour, X atm->tm_min, atm->tm_sec, atm->tm_year + 1900); X } X VOID printf("%s", name); X if (vflag || lflag) { X if (asb->sb_nlink > 1) X if (from = linkfrom(asb)) X VOID printf(" -> %s", X from->l_path->p_name); X else X VOID linkto(name, asb); X#ifdef S_IFLNK X if ((asb->sb_mode & S_IFMT) == S_IFLNK) X VOID printf(" S-> %s", asb->sb_link); X#endif /* S_IFLNK */ X } X putchar('\n'); X} X X/* X * tocmode() X * X * Fancy file mode display. X */ XSTATIC void Xtocmode(mode) Xreg ushort mode; X{ X switch (mode & S_IFMT) { X case S_IFREG: putchar('-'); break; X case S_IFDIR: putchar('d'); break; X#ifdef S_IFLNK X case S_IFLNK: putchar('l'); break; X#endif /* S_IFLNK */ X case S_IFBLK: putchar('b'); break; X case S_IFCHR: putchar('c'); break; X#ifdef S_IFIFO X case S_IFIFO: putchar('p'); break; X#endif /* S_IFIFO */ X default: X VOID printf("[%o]", mode >> S_IFSHF); X } X putchar(mode & 0400 ? 'r' : '-'); X putchar(mode & 0200 ? 'w' : '-'); X putchar(mode & 0100 X ? mode & 04000 ? 's' : 'x' X : mode & 04000 ? 'S' : '-'); X putchar(mode & 0040 ? 'r' : '-'); X putchar(mode & 0020 ? 'w' : '-'); X putchar(mode & 0010 X ? mode & 02000 ? 's' : 'x' X : mode & 02000 ? 'S' : '-'); X putchar(mode & 0004 ? 'r' : '-'); X putchar(mode & 0002 ? 'w' : '-'); X putchar(mode & 0001 X ? mode & 01000 ? 't' : 'x' X : mode & 01000 ? 'T' : '-'); X} X X/* X * usage() X * X * Print a helpful message and exit. X */ XSTATIC void Xusage() X{ X VOID fprintf(stderr, "\ XUsage: %s -o [ -fghlmuvzq ] [ -(bces) n ] [-F[KZL status_file][R format_command]s n] archive\n\ X %s -i [ -djkmnuvxz ] [ -(bcs) n ] [ -y prefix ] archive\n\ X %s -t [ -kuvz ] [ -(bcs) n ] [ -y prefix ] archive\n\ X %s -p [ -dghjlmnuvxz ] dir [ ... ]\n", X myname, myname, myname, myname); X exit(1); X} X X/* X * warn() X * X * Print a warning message. Always returns -1. X */ XSTATIC int Xwarn(what, why) Xchar *what; Xchar *why; X{ X long dietime; X dietime = time((time_t *) NULL); X X VOID fprintf(stderr, X "%s: \"%s\": %s\n", X myname, what, why); X if (logfile != (FILE *)0) X VOID fprintf(logfile, "%s: \"%s\": %s (disk %u) at %s\n", X myname, what, why, arvolume, ctime(&dietime)); X return (-1); X} X X/* X * warnarch() X * X * Print an archive-related warning message, including X * an adjusted file offset. Always returns -1. X */ XSTATIC int Xwarnarch(msg, adjust) Xchar *msg; Xoff_t adjust; X{ X VOID fprintf(stderr, "%s: \"%s\" [offset ", myname, arspec); X prsize(stderr, total - adjust); X VOID fprintf(stderr, "]: %s\n", msg); X return (-1); X} X X/* X * xfork() X * X * Create a child. X */ XSTATIC int Xxfork(what, die) Xreg char *what; Xint die; X{ X reg int pid; X reg Child *cp; X reg int idx; X static uint delay[] = { 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144 }; X X VOID signal(SIGCHLD, SIG_DFL); /* SysV mostly... */ X for (idx = 0; (pid = fork()) < 0; ++idx) { X if (idx == sizeof(delay)) X if (die) X fatal(arspec, syserr()); X else X return(-1); X VOID warn(what, "Trouble forking..."); X if (Fflag && !die) /* give up and go on... */ X return(-1); X sleep(delay[idx]); X } X if (idx) X VOID warn(what, "...successful fork"); X cp = (Child *) memget(sizeof(*cp)); X cp->c_pid = pid; X cp->c_flags = 0; X cp->c_status = 0; X cp->c_forw = children; X children = cp; X return (pid); X} X X/* X * xpause() X * X * Await a child. X */ XSTATIC void Xxpause() X{ X reg Child *cp; X reg int pid; X auto int status; X X do { X while ((pid = wait(&status)) < 0) X ; X for (cp = children; cp && cp->c_pid != pid; cp = cp->c_forw) X ; X } while (cp == NULL); X cp->c_flags |= CF_EXIT; X cp->c_status = status; X} X X/* X * xwait() X * X * Find the status of a child. X */ XSTATIC int Xxwait(pid, what, compstat2) Xreg int pid; Xchar *what; Xint compstat2; X{ X reg int status; X reg Child *cp; X reg Child **acp; X auto char why[100]; X X for (acp = &children; cp = *acp; acp = &cp->c_forw) X if (cp->c_pid == pid) X break; X if (cp == NULL) X fatal(what, "Lost child"); X while ((cp->c_flags & CF_EXIT) == 0) X xpause(); X status = cp->c_status; X *acp = cp->c_forw; X free((char *) cp); X if (status == 0) X return (0); X if (status & 0377) X VOID sprintf(why, "Killed by signal %d%s", X status & 0177, status & 0200 ? " -- core dumped" : ""); X else X VOID sprintf(why, "Exit %d", (status >> 8) & 0377); X X if ((!compstat2 && ((status >> 8) & 0377 != 2)) || compstat2) X return (warn(what, why)); X else X return ((status >> 8) & 0377); X} X X/* right now we verify the whole disk */ Xverify(error) Xint error; X{ char *verbuf; X char *buf; X reg time_t began; X int got, readamt, len; X int wrstat; X auto char msg[200]; X auto char answer[20]; X X if (*arname == '!') X { warn("Can't verify a piped command", ""); X return; X } X if (!error) X { if ((verbuf = malloc(arbsize)) == NULL) X fatal(arspec, "Cannot allocate Verify I/O buffer"); X X /* X * close as O_WRONLY and reopen as O_RDONLY to verify (on a X * disk this is a good thing) X */ X nextclos(); X nextopen(O_RDONLY); X fprintf(stderr, "Verifying...\n"); X verifycnt++; X for (buf = buffer; len = bufidx - buf; ) X { X readamt = min(len, arbsize); X if ((got = read(arfd, verbuf, readamt)) == readamt) X { X#ifdef HAVEMEMCMP X if (memcmp(verbuf, buf, got) != 0) X#else X if (bcmp(verbuf, buf, got) != 0) X#endif X { warn("Verify failed", ""); X error = 1; X break; X } X else X buf += got; X } X else X { warn("Read returned short", ""); X fprintf(stderr, "Read %d wanted %d bytes\n", got, X readamt); X error = 1; X break; X } X } X free(verbuf); X } X if (error) X { int answernum = 0; X X for (;;) X { X /* X * answernum == 3 here means the format worked and we want X * to try to write the disk (answernum == 1) X */ X if (answernum == 3) X answernum = 1; X else X { X /* don't count time waiting for the user */ X began = time((time_t *) NULL); X VOID sprintf(msg, "\ X%s: %s of disk %u has FAILED (try option #3 first)!\07\n\ X\tEnter 1 to RETRY this disk\n\ X\tEnter 2 to REFORMAT this disk\n\07\ X\tEnter 3 to REFORMAT AND THEN RETRY this disk if the format works\n%s", X myname, X ((error && (verifycnt == 0)) ? "Writing" : X "Verify"), X arvolume, hidequit ? "" : X "\tEnter \"quit\" to ABORT the backup\n\07"); X nextask(msg, answer, sizeof(answer)); X timewait += time((time_t *) NULL) - began; X answernum = atoi(answer); X } X X if (answernum == 1) /* note: recursive here... */ X { nextclos(); X /* if we can't open, try again */ X if (nextopen(O_WRONLY) < 0) X continue; X wrstat = writedisk(0); X /* if we failed or we are verifing */ X if (wrstat || (Fflag && verifyflag)) X verify(wrstat); X break; X } X else X if ((answernum == 2) || (answernum == 3)) X { if (system(formatcmd) != 0) X { fprintf(stderr, "Format failed!\n"); X answernum = 0; /* error, don't autowrite */ X } X else X fprintf(stderr, "Format successful!\n"); X } X else X if (strcmp(answer, "quit") == 0) X fatal(arspec, "Quiting during a verify"); X } X } X} X Xwritedisk(realwrite) Xint realwrite; X{ X reg char *buf; X reg int got; X reg uint len; X char *tbuf; X X if (Fflag) X fprintf(stderr, "Writing the disk...\n"); X#if ((defined (USESHMEM)) && (defined (UNIXPC))) X /* X * stupid 3b1 will block in a write to a floppy from shared memory X * (tested on Unix3.51[ ac]) X */ X if ((tbuf = malloc(arbsize)) == (char *)0) X { fatal("writedisk malloc failed", syserr()); X } X#endif X for (buf = buffer; len = bufidx - buf; ) { X#if ((defined (USESHMEM)) && (defined (UNIXPC))) X memcpy(tbuf, buf, min(len, arbsize)); X if ((got = write(arfd, tbuf, X#else X if ((got = write(arfd, buf, X#endif X *arname == '!' ? len : min(len, arbsize))) > 0) { X buf += got; X if (realwrite) X arleft -= got; X } else if (fflag) { X VOID warn(arspec, got < 0 X ? syserr() X : "Apparently full"); X _exit(1); X } else if (got < 0) { X /*fatal(arspec, syserr());*/ X warn(arspec, syserr()); X return(1); X } X else X { X/* check here if verifying... */ X next(O_WRONLY, "Apparently full"); X return(1); X } X } X#if ((defined (USESHMEM)) && (defined (UNIXPC))) X free(tbuf); X#endif X return(0); X} X X/* X * compress "name" if we can X * If we compress we change the statbuf size, the file name, close X * the old pointer to the file and return the pointer to the compressed X * version; X */ X#include <sys/dir.h> Xcompressfile(fdp, name, asb) Xint *fdp; Xchar *name; Xreg Stat *asb; X{ X int compout; X char *tmpcomp; X Stat asb2; X int comppid; X X /* compress only if no links */ X if (!lflag && (asb->sb_nlink == 1) && X (strncmp(name+strlen(name)-2, ".Z", 2) != 0)) X { X /* make sure compress could put on the .Z */ X if ((tmpcomp = strrchr(name, '/')) != NULL) X tmpcomp++; X else X tmpcomp = name; X#ifdef MAXNAMLEN /* BSD otherwise should be sysV (FFS on sysV?) */ X if (strlen(tmpcomp)+2 > MAXNAMLEN) X#else X if (strlen(tmpcomp)+2 > DIRSIZ) X#endif X { warn(name, " is too long to tack on .Z"); X return; X } X X tmpcomp = tempnam(NULL, "afiot"); X VOID unlink(tmpcomp); X X if ((comppid = xfork("out(), compressing", NODIE)) == 0) X { VOID close(fileno(stdout)); X if (open(tmpcomp, O_WRONLY|O_CREAT|O_TRUNC, 0600) >= 0) X { execlp("compress", "compress", "-c", X name, 0); X close(tmpcomp); X } X else X warn("open compress output", syserr()); X exit(1); X } X if ((comppid > 0) && X (xwait(comppid, "out(), wait for child", FALSE) == 0)) X { X if ((hflag ? STAT(tmpcomp, &asb2) : X LSTAT(tmpcomp, &asb2)) >= 0) X { X if ((compout = open(tmpcomp, O_RDONLY, 0400)) > 0) X { X if (asb2.sb_size < asb->sb_size) X { close(*fdp); X strcat(name, ".Z"); X asb->sb_size = asb2.sb_size; X *fdp = compout; X } X } X } X } X X /* X * assume the open descriptor above will still access the file X */ X if (tmpcomp) X unlink(tmpcomp); X } X} X X#ifdef USESHMEM Xextern char *shmat(); Xint memids[NUMSHKEYS]; Xchar *memlocs[NUMSHKEYS]; Xint indx = 0; X X/* in this malloc we assume the memory segments will be allocated in order */ Xchar * Xshmemalloc(buflen, arname) Xuint buflen; /* Archive buffer length */ Xchar *arname; X{ X int size; X key_t shkey; X X do { X if (indx == NUMSHKEYS) X fatal("Too many shared memory keys", ""); X shkey = ftok(arname, (char)indx); X size = min(SHMEMSIZE, buflen); X if ((memids[indx] = shmget(shkey, size, IPC_CREAT|0666)) < 0) { X fatal("shmget failed", syserr()); X } X if ((memlocs[indx] = shmat(memids[indx], 0, 0)) == (char *)-1) { X fatal("shmat failed", syserr()); X } X indx++; X buflen -= size; X } while (buflen > 0); X return(memlocs[0]); X} X Xvoid Xshmemfree() X{ for(--indx; indx >= 0; indx--) X if (shmctl(memids[indx], IPC_RMID, NULL) < 0) { X warn("shmctl failed (shmemfree)\n", syserr()); X } X} X#endif /* USESHMEM */ X Xgoodbye(stat) Xint stat; X{ X#ifdef USESHMEM X shmemfree(); X#endif X /* log that we died */ X if (stat && (logfile != (FILE *)0)) X { long dietime; X dietime = time((time_t *) NULL); X X VOID fprintf(logfile, "%s: the backup has failed (stat %d), died at %s", X myname, stat, ctime(&dietime)); X X } X X exit(stat); X} X X#ifdef MYTEMPNAM X/* author: Monty Walls X * written: 4/17/89 X * Copyright: Copyright (c) 1989 by Monty Walls. X * Not derived from licensed software. X * X * Permission to copy and/or distribute granted under the X * following conditions: X * X * 1). This notice must remain intact. X * 2). The author is not responsible for the consequences of use X * this software, no matter how awful, even if they X * arise from defects in it. X * 3). Altered version must not be represented as being the X * original software. X */ X X#define MAXPREFIX 5 X#define TMPNAME "tmp" X#ifndef P_tmpdir X#define P_tmpdir "/tmp" X#define L_tmpnam 14 X#endif X Xextern char *mktemp(); Xextern char *strcat(); Xextern char *strcpy(); Xextern char *getenv(); X Xchar * Xtempnam(dir, name) Xchar *dir; Xchar *name; X{ X char *buf, *tmpdir; X X /* X * This is kind of like the chicken & the egg. X * Do we use the users preference or the programmers? X */ X#ifdef USE_ENV_VAR X if ((tmpdir = getenv("TMPDIR")) == (char *)NULL) { X if ((tmpdir = dir) == (char *)NULL) X tmpdir = P_tmpdir; X } X#else X if ((tmpdir = dir) == (char *)NULL) { X if ((tmpdir = getenv("TMPDIR")) == (char *)NULL) X tmpdir = P_tmpdir; X } X#endif X /* now lets check and see if we can work there */ X if (access(tmpdir, R_OK+W_OK+X_OK) < 0) X return ((char *)NULL); X X if (name == (char *)NULL) X name = TMPNAME; X else if (strlen(name) > MAXPREFIX) X name[5] = '\0'; /* this is according to SYS5 */ X X /* the magic value 2 is for '\0' & '/' */ X if ((buf = (char *)malloc(L_tmpnam + strlen(tmpdir) + 2)) == (char *)NULL) X return ((char *)NULL); X X strcpy(buf, tmpdir); X strcat(buf, "/"); X strcat(buf, name); X strcat(buf, ".XXXXXX"); X X /* pass our completed pattern to mktemp */ X return (mktemp(buf)); X} X#endif /* MYTEMPNAM */ SHAR_EOF $TOUCH -am 0123145091 afio.c && chmod 0444 afio.c || echo "restore of afio.c failed" set `wc -c afio.c`;Wc_c=$1 if test "$Wc_c" != "72522"; then echo original size 72522, current size $Wc_c fi exit 0 -- David H. Brierley Home: dave@galaxia.newport.ri.us; Work: dhb@quahog.ssd.ray.com Send comp.sources.3b1 submissions to comp-sources-3b1@galaxia.newport.ri.us %% Can I be excused, my brain is full. **