pax_global_header00006660000000000000000000000064134002441000014476gustar00rootroot0000000000000052 comment=33d6e6ad4cec50a45f6cb9241834a39a529a58ee afio-2.5.2/000077500000000000000000000000001340024410000124225ustar00rootroot00000000000000afio-2.5.2/ANNOUNCE-2.5.1000066400000000000000000000022121340024410000142110ustar00rootroot00000000000000Subject: Afio archiver version 2.5.1 Keywords: backup archiver compressed fault tolerant afio cpio * WHAT IS AFIO? Afio is an archiver/backup program that produces cpio-format archives. Like tar, afio can make compressed archives. However, the compressed archives produced by afio are much safer than tar -z archives. If a tar -z archive has a read error, tar is unable to extract the files beyond this error. The compressed archive format used by afio allows it to recover from read errors and go on unpacking the next file. * WHAT IS NEW IN VERSION 2.5.1? This is the first new release since 2.5 in 2003, but actually not that much is new: - various bug fixes - additional options for file name filtering - updates to the regression test scripts to make them more compatible with newer versions of shell tools - additional information on the licensing status - updated example scripts of using gpg with afio, to show a solution that is more compatible with (newer versions of?) gpg. * WHERE DO I GET IT? Version 2.5.1 is at: http://members.brabant.chello.nl/~k.holtman/afio-2.5.1.tgz And at http://freecode.com/projects/afio afio-2.5.2/ANNOUNCE-2.5.2000066400000000000000000000020751340024410000142210ustar00rootroot00000000000000Subject: Afio archiver version 2.5.2 Keywords: backup archiver compressed fault tolerant afio cpio * WHAT IS AFIO? Afio is an archiver/backup program that produces cpio-format archives. Like tar, afio can make compressed archives. However, the compressed archives produced by afio are much safer than tar -z archives. If a tar -z archive has a read error, tar is unable to extract the files beyond this error. The compressed archive format used by afio allows it to recover from read errors and go on unpacking the next file. * WHAT IS NEW IN VERSION 2.5.2? The main changes with respect to version 2.5.1 are: - security improvements for the handling of archives from unstrusted sources - when making multivolume archives, added %V feature to include the volume number in subsequent volume file names - various bug and portability fixes * WHERE DO I GET IT? The afio home page is at: http://members.chello.nl/~k.holtman/afio.html Version 2.5.2 is at: http://members.brabant.chello.nl/~k.holtman/afio-2.5.2.tgz And at http://freecode.com/projects/afio afio-2.5.2/HISTORY000066400000000000000000001273161340024410000135200ustar00rootroot00000000000000 This file contains some historical information on afio. It also contains notes about changes in new versions. ============Very old README file contents============================== 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. 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. [...] Mark Brukhartz Lachman Associates, Inc. ..!{ihnp4, sun}!laidbak!mdb [...] =================Notes on changes in new versions================== Dave Gymer (dpg@cs.nott.ac.uk) (up to version 2.3-dpg-3): This is my port to Linux of afio. See afio.1 and README for a description of what this does. It's based on work I did to port this to MiNT by Eric Smith on the Atari ST, and consists mainly of bugfixes and one important new feature; the use of st_rdev to indicate whether a regular file was compressed by afio or not if it's name in the archive ends in .z. I did this because I didn't want backups of files which were already compressed (like .tar.Z archives and some of the stuff in the Xfree386-1.1 distribution) to be uncompressed if I restored my filesystem. This also means that the -Z option is meaningful when listing the contents of an archive; if given, any .z files are listed without the .z if afio compressed them, or with the .z if they were like that when afio found them. I also ran the code through GNU Indent 1.4. This is the third release of my version; this one knows about gzipped files (.z), uses gzip instead of compress, and handles other files which rarely compress well, like JPEGs. You may want to undefine LONGZFILE if you still use the Minix fs; I use xiafs so it doesn't matter. Even if you do use the Minix fs, you'll only get bitten if you try to restore a compressed archive without uncompressing files. Andrew Stevens (as@prg.oxford.ac.uk) (version 2.3-dpg-4 onwards) I've tidied the code a little to make the double-buffering work (and prompt sensibly) and avoid leakage of file-handles in the compression code. This code really really needs splitting up into multiple source files! I have tested backing up and restoring off floppies fairly thoroughly (various options), but I have no the faintest idea how reliable any of the other stuff is. I have included my favourite backup script as an example of the usage of afio. [This script is in the script1 subdirectory.] This uses the directory /usr/adm/dump to record dumping dates and a file /usr/adm/dump/DONTDUMP (example included) to filter out non-dumpable areas. The afio source as given will not attempt to compress files ending in .Z .z .gz .arc .gif .zip .zoo .lha .jpeg .jpg .tpz .taz .tgz .tzg See also the -E option in the manual page. Koen Holtman (koen@hep.caltech.edu) (version 2.3.5 (for Linux) onwards) Introduced new options -E, -G, -M, -w and -W. On compressing files, afio now temporarily stores the result in memory in stead of creating (potentially huge) /tmp files. This way, there is no danger of the /tmp filesystem getting full during a backup, so that a backup script calling afio can safely use /tmp. Afio won't use more than 2 meg memory for temporary storage (also see the -M option). If it runs out of memory, it calls gzip twice, the first time to determine the length of the result, the second time to get the data itself. The -y and -Y options now are able to handle real shell patterns, and generally work more sensible. Verifying floppy disks now works on Linux. Version 2.3.6 (for Linux): Added -T option. The options -G and -T can be used to optimize the backup speed/compression ratio if you have a slow machine or a fast backup medium. Added -S option. Fixed some bugs in 2.3.5. Version 2.3.6-dpg-1 (for Linux): Bug fix in -F option by Dave Gymer (dpg@Cs.Nott.AC.UK). Version 2.3.7: Deleted shared memory code to reduce code clutter. Removed `buggy gzip workaround' in compfile.c, this caused afio to hang occasionally if the -f option was used. This means that afio will only run with newer gzip versions. (I don't know exactly which version is new enough, though gzip 1.2.3 and above certainly are. Just try it.) Edited -F code to fix `double prompting' bug. Added more warnings to the manual. Added some ifdefs and a few notes to make porting to other unix versions easier. Version 2.4: Added -r option and small bugfix in tty handling, both by Anders Baekgaard (ab@osiris.cpk.auc.dk). Added -B option that prints the byte position of each file in the archive in the -v output. Made -F option do writing in O_SYNC mode under Linux. Thus afio will notice some floppy disk write errors using Linux kernel 1.1.54 or higher. Updated manual page. Updated usage message. Made version number shorter, though number of changes does not warrant a new major version. Version 2.4.1: Fixed bug that sometimes caused unzipping to fail due to a seek on a pipe (bug found and fixed by Keith Owens). Deleted malloc() result check in meminit() to help porting, malloc(0) returns NULL on some machines. Fixed bug caused by incorrect assumptions about write(), triggered under Linux if afio -iZ ... is stopped and restarted. Now exits if a broken pipe occurs on the stderr of afio -i or the stdout of afio -t and afio -rv. Thus afio -t can be used with head to get a table of contents of the beginning of an archive. Fixed bug in setting suid/sgid bits when restoring a file that does not have user=root, group=root. Afio did not take into account that the Linux chown() clears these bits in some cases. Expanded semantics of -s, changed the behavior when afio gets an input eof without there being an -s option given; the old behavior can be gotten back by using -s 0. This change prevents bogus insert next disk messages when the archive ends prematurely. Made control file feature (added -D option). Fixed bug in afio 2.4 that caused symbolic link info to be omitted when creating an archive. Fixed bugs in -r option. Removed empty lines from logfile created with -L option, also fixed bug in logfile creation. Reorganized documentation files, updated manual page. Version 2.4.2: Added code to let subprocesses close archive file descriptor, needed for some tape drivers when changing tapes. Fixed bug in multivolume tape read code. Added some portabilty stuff: - Rewrote archive header scanf to be more portable. - Changed CPPFLAGS into CFLAGS in Makefile. - Changed ssize_t and size_t in writeall() to int. - Added code to clear asb.sb_rdev in the file stat block if not a device: some OSes put (garbage?) info in this field in stead of clearing it and afio wants it cleared. Added -a option to preserve atime on files read when doing -o or -r. Using it will set the ctime for these files to the current time, so this option _cannot_ be used together with most incremental backup schemes, which rely on the ctime being preserved. Fixed bug in -f option when used with -s. Updated manual, added new warning to BUGS section. Fixed tocentry() to print -B byte offset to stdout instead of stderr. Added support for creation of UNIX sockets in restore. Added single qoutes around rsh switches. Added `-s 0' functionality to the -I command. Added -z calculation to -I and -O commands. Changed restore not to do utime() to restore time on symlinks: the timestamp change gets done on the file linked to and that is not very productive. Took -N flag out of LDOPTS in Makefile. No idea what it should have done. Added `f to format' option in floppy handling code (patch by Ulrich Lauther). Note that afio still does not have adequate multivolume floppy support: use the tbackup program as a wrapper around afio for that. Changed default format command in Makefile from /bin/fdformat to fdformat. Changed nice(-40) to nice(-10) for subprocess in -f option. Added options -U, -P, -Q to force compression, specify compression program and pass options to the compression program, based on patch file by karsten.ballueder@stud.uni-karlsruhe.de. Added script3/ directory to distribution. Version 2.4.3: Fixed bug in restore code which sometimes prevents an old file from being overwritten with a hard link. Based on report by Nokubi Takatsugu. Version 2.4.4: Fixeded more bugs in hard link handling, based on same report by Nokubi Takatsugu. The inode field in the archive header only stores the meast significant 16 bits, which is too little for todays filesystems, yet the unpacking code assumes that these 16 bit fields have unique values for each filesystem entry when handling hard links. This can cause problems when unpacking files with hardlinks. Added code to 1) make unpacking od old archives safer by adding more sanitiy checks and 2) ensure that hardlink processing related inode fields in newly produced archives are unique by inventing unique inode numbers if necessary. Added -4 flag to write `extended ascii' format archives with 4-byte inode fields, based on code by Nokubi Takatsugu. See manpage for caveats about -4. Incorporated some patches by Rob W. W. Hooft to speed up buffer memory allocation for -Z option. Rob reported impressive speedup (due to less mallocs all the time) for large files in some cases but I have been unable to duplicate any dramatic speedup myself. Fixed bug reported by Sebastian Wilhelmi: afio -o now correctly exits with a fatal error and 1 exit status if there is a write error to the backup medium. Added -J flag to revert to something like old behavior: with -J, media write errors give a warning and writing goes on, and afio finally exits with status 1. (Actual old behavior was printing warning going on and exiting with status 0. Bad.) Improved -Z reporting using patch by Ed Casas. Made some additional improvements too. Afio will now exit with status 1 if there any warnings were printed during the operation (but some may be missed if -f is used). Added a sleep(2) to lower chance of race condition in rsh processing when reading remote archives with afio ... host:/file. Problems reported/investigated by Rob Browning and Dirk Eddelbuettel. [Note that, in spite of me correcting problems in it, remote file handling is still not officially supported by this maintainer -- KH] Incorporated some patches by Juergen Lock to make porting to (Free)BSD systems easier. This should also solve an IRIX porting problem reported by Mike McDonnell. Incorporated patch by Timo Korvola to allow specification of an alternative remote shell program (like ssh) for remote archives. Added patch by Werner Koch to speficy user for remote file processing. Full syntax now is `filename_without_:' for local files or [user@]host[%rsh][=afio]:file for remote files. (A patch for the same feature was sent by David Atkinson too.) Added some DEC Alpha porting patches by George Brizicky. Incorporated a Solaris porting patch by Erich Focht. Added -H (promptscript) option using patch by Alexander Zangerl. Fixed buggy handling of 0-length files which already have a .z extension. Bug reported by Bob Mitchell. Incorporated patch by Dave Gymer which adds -0 option like GNU cpio, to allow input filenames to be terminated with a '\0' instead of a '\n'. When used with find ... -print0, can be used to ensure that any filename can be handled, even if it contains a newline. (A patch for the same feature was sent by Rob Browning too.) Lots of manpage updates, also updated BUGS section of manpage. Version 2.4.5: Fixed progress reporting and media change code to handle archives and archive volumes above 2GB and 4GB correctly, based on patch by Mike Black. Problems also reported by Maik Musall. Added 'g' (gigabytes) as a possible size denotation in options taking numbers, using patch by Mike Black. Added @ option based on patch by Mike Black. Removed bug in 2.4.4 which would report media change messages as warnings about errors in the final -z count. Added three extensions (.deb, .rpm, .bz2) to the list of those to be excluded from compression by default. Patch by Dirk Eddelbuettel. Fixed bug in code for restoring archived made with -4 option (oops!). Bug found by Chris Thompson. Incorporated patch by Bryan Henderson which fixes bug in floppy prompting if -f option used (every prompt was issued twice). Improved -H (promptscript) option using patch by Raphael Manfredi. Promptscript now has tty on its stdin, and gets one more argument. Included sample promptscript by Raphael Manfredi. When writing archive without specifying -s or -s 0, added code to switch to next volume if current one is full (if write() returns 0 bytes witten or ENOSPC error). Part of code based on patch by Raphael Manfredi. Some updates to documentation. Version 2.4.6: Fixed file closing bug in -r option which prevented tape switches when verifying with some tapes. Bug reported by Tetsuya Makimura and also by Alan J. Wylie. Switched to -Wall in Makefile, modified code to suppress -Wall warnings (at least with my gcc), based on patch by Holger Schurig. Added some extensions to the list of those to be excluded from compression by default. From patch by Holger Schurig. In tests and source code checks, no year 2000 (Y2K) problems have been found with afio. All afio versions back to at least 2.3.6 should have no year 2000 problems, nor should existing archives produced with such versions have year 2000 problems within. Current afio versions do have the usual unix second counter underflow problem, which will manifest itself in 2038. The date-printing afio options (-tv and -L) print 4 digit years. The archive formats output by afio (both ASCII and extended ASCII) use the usual unix seconds-since-1970 counts to represent dates, these counts are encoded as octal numbers in a wide-enough field. Some non-Linux platforms, Sun in particular have a dev_t larger than 16 bits. This caused problems archiving devices on these platforms. We now take this into account: if it is larger than a 16 bit number, the dev_t value is re-encoded in the dev and ino parts of the header. The makedev include is also updated to special-case for Sun. Based on a bug report by by Guenter Steger. Fixed bug in interference between -Z and -l options on writing an archive. Fixed bug in -U, which would not force compression of hard linked files. Documented internal afio limitations connected to -Z on hard links and -U and hard links. Added -1 option and revised its default for better backwards compatibilty with existing backup scripts. Compatibility problem (tob never going on to verify phase if afio exited 1) reported by Denis Sbragion. (I recall that the problem was also reported by someone else but can't find the report anymore.) Same problem, or maybe not, also reported by Florin B Manolache. Added -3 option for better compatibility with pgp. Based on bug reports by Jens Getreu. And many thanks to Jens Getreu for testing the -3 option! This fixes a security hole: --snip-- [SECURITY] afio: security hole in 'afio -P pgp' encrypted archives I. Description Since version 2.4.2, the afio archiver has had an interface, the '-P pgp' command line option, which can be used to pgp-encrypt the file data witten to an afio archive. Following up on some bug reports, I have recently discovered a security problem with this afio-pgp interface: pgp encryption is not always applied in the right way. This makes it possible to crack the encyption on the file data in an 'encrypted' archive produced using afio with the '-P pgp' option. The security of files which were already encrypted _before_ being written to the archive is not affected. II. Impact It is possible to cheaply crack the encryption of at least some of the file data in the 'encrypted' archives produced using 'afio -P pgp'. This includes archives produced using the pgp_write example script included in the [pre-2.4.6] afio distribution. III. Solution _Existing archives_ [made with pre-2.4.6 afio versions] produced with 'afio -P pgp' should really be treated with the same care (against theft etc.) as unencrypted archives. If such existing archives cannot be deleted or safely locked away, then encrypting the _entire_ existing archive file with pgp will protect it. --snip-- Updated scripts in script3/ to use new -3 option. Updated manpage, added some afio command line examples. cdrecord example contributed by Joachim Bergmeyer. Added code to make -y -Y -w -W also work when creating archives. Based on patch by Brian Powell. Fixed bug in -a option, which would not restore the access time on files stored with compression when creating an archive. Based on patch by David Rourke. Made some changes to ease portability to the IRIX native C compiler. See also the PORTING file. Based on patch file by Brian Gunney. Afio currently invokes gzip for compression in the -Z option. Performance experiments by Ted Phelps showed that linking the zlib compression library and invoking it internally will not lead to big performance benefits. Though zlib is faster for very small files, it turns out that invoking gzip is faster for larger files. Switching to zlib would imply a major disturbance in the source. The small to negative performance benefits expected do not seem to justify this, so switching to zlib is not currently considered. Made some changes to ease portability to HPUX with gcc. See also the PORTING file. Based on patch file by Daniel Andersson. Enhanced -s option based on patch by Sebastian Weber, it now can accept a comma-separated list of sizes. Updated manpage BUGS section to say that afio does not restore owner/group information on symlinks. Restoring this info would be possible using the relatively new `lchown' system call, but that call is not supported on older linux systems and some other unix systems. Problem reported by Marko Jahnke. Further reporting by Giuliano P Procida. Added gnupg_* scripts to script3/ dir. Scripts by Jens Getreu. Added script5/ dir and scripts, contributed by Jens Getreu. Updated existing scripts part of SCRIPTS file. Updated /script1 and script2/ to improve/remove /tmp file handling. Based on patches by Marius Tomaschewski. These changes make the scripts more resistant to funny security exploit business with pre-existing /tmp files. Added code to round down volume size to a multiple of the block size in the -s option. Added -9 option to suppress this: pre-2.4.6 versions of afio defaulted to no rounding down. Rounding down is needed for compatibility with some devices, notably ftape. Based on bug report by L. C. Robinson. Changed -H promptscript option to take running as an at or cron job into account. If /dev/tty is not a connected device, afio will now connect /dev/null to the stdin of the promptscript. Based on bug report by Andreas Wehler. Version 2.4.7: Fixed bug that sometimes caused `-- compressed' to be printed twice in verify operation. Has to do with not flushing stdout, stderr before forking. Bug reported by JP Vossen. Added more material on how pattern matching works in the -y option section of the manpage, and added examples of selective restores to manpage. Based on questions by Kjell Palmius and Stojan Rancic. Added text to BUGS section about afio not being able to write into directories for which it has no write permissions, except when running as root. Problem reported by Kagan Kayal. Fixed bug that caused afio -s option values above 4gb to be sometimes treated as a much lower value when reading an archive. This was a truncation-in-casting bug triggered by some combinations of -b and -s values. Bug reported by Michael Zieger. Looked into the case of reading/writing archives to/from regular filesystem files which are bigger than 2 GB. Seem to work on newer linux systems supporting such large files. On the recent linux system I tried (Red Hat 6.2, kernel 2.4.2, GCC 2.95.3, libc.so.6 -> libc-2.1.3.so) a freshly compiled afio can read and write >2GB archive files to the filesystem. HOWEVER I also got reports from others with _the same_ or _newer_ versions of stuff that their compiled afio is not able to do this. I have not found a pattern to this: your best bet is to recompile the afio executable on your platform and try. Based on reports by Grzegorz Wieczorek and Norbert Veber. Looked into the case of >=2GB files being included *in* an archive. With pre-2.4.7 afio versions under Linux, two cases of breakage apply. 1) if afio was compiled without large filesystem support then such files generated "Value too large for defined data type" error, however WITHOUT causing nonzero exit (unless -1 option made `missing files' a cause for nonzero exit). 2) if afio was compiled with large filesysten support such files would be archived. However, such files could then not be verified, listed or unpacked correctly because the afio archive header does not have enough room to correctly represent a >=2GB file size. [Note: If you really really need to restore such a file: by doing some hacks and hand-holding the restore it should be possible to achieve this -- all the data is in the archive, afio just does not know after how many extra increments of 2GB (or 4GB?) blocks it ends]. Added following bugfixes to address all this. No matter how it is compiled under Linux, afio will now issue a warning when encountering a >=2GB file in the set to archive, and not archive that file. The warning will cause nonzero exit (unless -1 option changes this). Also documented 2GB limitations in manpage. Based on bug reports by Mike Black. Fixed bug that mis-calculated compression ratio number (XX%) printed by afio -o -v when compressing very large files. Bug found by Koen Holtman. Did more work on hard links to soft links (some installs actually make these things! Sheesh!) Afio -t output is improved for these, and afio -r does not give incorrect verify errors anymore on veryfiying the second, third, etc. link. Afio -i now correctly restores hardlinked soft links (at least on the linux kernel I tested it on). Updated manpage to change earlier statement that "Some Linux kernels allow one to create a hard link to a symbolic link. afio cannot handle such hard links correctly.". Based on bug report by Thomas Dorner. Should also be cleaner fix to problem reported earlier by Giuliano P Procida. If a fatal error occurs and data was transferred, afio makes sure to print an error message which includes the offset of the fatal error. Based on bug report by Francesco Potorti. Fixed typesetting bug in afio manpage that caused -R option description to exclude the `Disk format command string' argument. Based on a 1998 bug report by Lee Bradshaw, and thanks to Dirk Eddelbuettel for prompting me to look over the Debian bug report database for for afio again -- looks like I lost track of it when I got the report the first time. Added -2 option -- this also means changing the default behavior for compressing very large files. Updated maintainer e-mail addresses. Added some more documentation on sparse file handling to the manpage. Documented some -r option limitations in BUGS section of manpage. Added maximum name length check on reading file names from stdin with afio -i. Version 2.4.7.9beta: [This is a BETA release, meaning that I (the maintainer) am applying less strict quality control than I would do for a normal release. The main reason for the BETA is to get >2GB functionality out to people who need it now and are willing to take some extra risk.] Changes incorporated via Debian 2.4.7-7 (Debian packaging releases maintained by Dirk Eddelbuettel). Corrected typo in manpage, cdrecord was spelled crdrecord. Based on report by Christoph Claus. Switched to large file compile environment (-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE) in order write archives larger than 2GB. Based on suggestions by Norbert Veber. Applied patch by Dieter Schuster to correct an endian issue on 32-bit powerpc systems, trigged by having 64 bit off_t. The bug was in the sprintf formatting in outhead(), asb->sb_size should be (unsigned long int) asb->sb_size. Applied patches by Stephen van Egmond to correct endian issues on 32-bit powerpc systems and fix all -Wall issues. Lots of little changes and fixes of fixes based on the above. Based on discussions and tests with/from Stephen van Egmond, Dirk Eddelbuettel, Dieter Schuster, Complete history of fixing not documented here. Historians, see Debian bug DB numbers #144986 and #153948, and maybe more bug numbers too. Put very elaborate warning flags in compiler options -- need to take out later. [End of list of changes incorporated via Debian 2.4.7-7.] Improved type used for the variable pad in outeof() Removed linkp and some use of it in incheckentry(). This was a bug (dormant?) introduced by maintenance in 2.4.7. Based on reports by several people, including at least Matthias Stolte, Stephen van Egmond, Devin Reade, and Mike Black. Added large ASCII header support for 2G files and 32bit inodes, uid, gid, dev, rdev, and nlink values, as well as 64 bit mtime values (be prepared for if time_t goes higher than 2038). Based on patch from Clark Rawlins. Changed inode type in large ascii header to 64 bits. Added extra space (2 bytes) for flags to second extended ascii header, and variable-length extra header (with 2-byte length indicator). All set to 0 in this release. Updated and improved discussion of archive portability in manpage. Changed installation locations for `make install' to be compatible (I think) with the Linux Filesystem Hierarchy Standard. Version 2.4.7.9beta4: Fixed bug in defines: ushort was defined twice Updated regression test scripts some more. Changed >2gb regression test to use 4.1 gb file: that way we actually use more than 32 bits in the file size. Updated README.FIRST file based on various regression test results. Made ascii art in .h file to decode large ASCII header. Put a few ascii chars in the large ascii header to make it easier to read. Added a lot of casting refinements based on study of elaborate compiler warnings. Version 2.4.7.9beta5: Improved makesparse code in regression test. Version 2.4.8beta1: Added -6 option based on suggestions from, discussions with, and patch from Matthias Stolte. Extended -E option syntax. Created new mechanism to create -E default extensions list from the manpage, because I keep updating it. Added more extensions based on suggestions by Kevin Cosgrove and Matthias Stolte. Made default -E extension matching case-insensitive, and added case sensitiveness configurability to the -E option, based on suggestion by Kevin Cosgrove. Added discussion of using bzip2 to the manpage, prompted by question from Nick Papadonis. Improved the documentation of the -Z option in the manpage. Fixed (largely theoretical) bug in nameaddfile(). Improved description of -A option (and defaults if no -A used) on the manpage. Based on a problem report by Matthew Vernon. Fixed bug in verify command: if the file in the archive is not compressed, and the file on the filesystem has grown larger than the file on the archive, this was not detected before. Based on bug reporting and analysis by Ryan W. Maple and Rainer Koenig, and patch by Ryan W. Maple. Added pointer to the HISTORY file in the authors section of the manpage. Added section on web site and internet resources to manpage. Made some internal buffers used during compression and verify larger. Fixed bug in verify code: verifying a compressed or uncompresses file has a logic bug in that it assumed that the read(f,b,s) system calls would always return with s bytes if not end of file/stream yet. This assumption is incorrect, especially when reading data from a pipe. (Strange that this bug never seemed to cause real problems until I did some testing with a large sparse file and `cat' as the compression program) Removed limitation (introduced in one of the 2.4.7.beta versions) in the -2 option that for files bigger than 2GB, compression is never attempted, unless the -U flag says so. This limitation was introduced at the time because of concerns that gzip would not work on >2GB files. Some testing showed that gzip works fine (and bzip2 also). Added code to use lchown call, so that owner information on symlinks is restored (if the OS does nor have a lchown call, the HAVE_LCHOWN symbol can be commented out in the Makefile). Based on patch file from Matthias Goebl. Added -vv option, and documented stderr/stdout behavior of -v option better. Based on patch file from Matthias Goebl. Added code to set the correct modification times on directories that are restored, even if these directories have additional files restored into them. (In older versions, the mtime was set when creating the directory, but then usually the filesystem updates that mtime to the current time when the directory is filled. Now, the original mtimes of restored directories are remembered, and they are set again just before afio exits.) Based on patch file from Matthias Goebl. Added a sanity check on the stdin at the start of executing -o mode. Output file is only opened (and, if existing, truncated to 0 length) when the stdin data starts looking like a file list. This should prevent deletion of the archive file contents in most cases when people who want to do afio -i myarchive accidentally type afio -o myarchive. The idea is that people who type afio -o accidentally will see the error messages and press ctrl-C in time. Documented archive format in the manpage, and improved documentation of the -k option. Also included a hint about the -k option in the unrecognisable archive error messages. Based on problem reports by Evren Yurtesen and Carter Alvord. Added example of splitting archive into 1GB parts to overcome 2GB filesystem limitations. Based on question by Bob Stewart. Updated maintainer e-mail addresses again. Updated README, INSTALLATION files. Documented archive portability problem in handling major/minor numbers across different OS versions in BUGS section. Fixed bugs in hard link handling when creating an archive (when running without -4 option). (I did some tests on this code with 130K files with hard links, and it turns out that the old code was not correct, though it usually does do the right think when archiving moderate numbers of hard linked files.) The code in linkto() for creating unique 16-bit inode numbers for files with hard links in the archive was buggy. The code would definately fail to produce a correct archive when packing more than 4096 sets of hard linked files in the archive, and could also fail in some cases when 4096 had not been reached. The impact of this bug is somewhat low, I never got any bug reports about it, so I guess that most people who have lots of hard links and care about restoring them correctly were using -4 already. (The -4 option did not suffer from these bugs, and did correctly handle many hard links as advertised.) Added code so that, if number of archived hard links goes over 64K, then large archive headers will be used to store any additional hard linked files. This maximises cpio (and afio backwards) compatibility, while removing the 64K hard link restriction for the normal invocation of afio. Updated manpage accordingly. Fixed bug in *un*packing of archives created with -4 option: hard linked files these archives would not always be unpacked correctly (with afio not printing any error messages about things being incorrect) if the -4 flag was ommitted on the command line when using afio -i. Added printing of warning message for the case `archive will not be fully compatible with cpio or afio versions 2.4.7 and lower', and c and C letters for -1 option. Based in part on discussions with Mike Black. Added code for handling uid/gid values >64K, by using large ASCII header. Based on problem report by Jeffrey Eugene Crawford. Added code for handling time_t values >2^31. Not that Linux supports these yet... Added n letter to -1 flag, based on patch by Keith G. Murphy. Documented use of additional arguments to generic prompt script in -H flag, based on suggestion by Lance Albin. Updated usage message that is printed when afio is run without command line arguments or with invalid arguments. Added M letter to -1 option. Based on suggestion by Devin Reade. Version 2.5: In afio.h ulo and ull macros, added L and LL letters in constants. This is more correct and portable and fixes compiler warnings on newer gcc versions. Documented `long long' issues in PORTING file. Removed ifdeffed typedef ulonglong porting hacks from the afio.h file. Changed mknod() call to mkfifo() call when creating named pipe from archive. Freebsd cannot make named pipes with mknod. Added some extra defines to suppress some warnings on sun platform. Also added some stuff around awk invocations in makefile and regtest to make things easier on sun -- the sun platform I tried it on had a default awk that was too ancient to understand things like gsub. Added some discussion of sun and freebsd to PORTING file. Fix to regression test: no longer complains if two compared directory inodes have a size difference. Bases on bug report by vasudeva. Changed sanity check on the stdin at the start of executing -o mode: now the check only happens if stdin is a tty. This allows scripts to make empty archives, and is needed for compatibility with tob. Based on problem report by Dirk Eddelbuettel. Changed read() to readall() in outdata(). Updated SCRIPTS file. Version 2.5.1: (This version folds in all non-debian specific bug fixes and feature extensions, with respect to 2.5, that were made in the Debian afio package 2.5-6) (This version does not incorporate fixes for all bug reports or feature requests I got since afio 2.5: some of these remain on the todo list) Fixed a file descriptor handling bug that caused gzip subprocess to keep running when afio -oZ is killed or crashes. Based on bug report and patch by Alain Spineux. Fixed bug that could cause afio -oZ to hang, and/or write faulty data without warning, if a large file was modified while being written. The bug fix prevents hanging, and introduces warnings when faulty data might be written. Based on bug report and patch by Alain Spineux. Improved error reporting in -r operation, based on discussions with Alain Spineux. Note that the error message text is all changed now, to be more verbose and explanatory, so scripts that grep the message text have to be rewritten. Verfication reporting via exit code 0/1 is unchanged, unless the -1 r flag is used. Added -1 r flag to ignore certain warnings when verifying the backup of a live filesystem. Based on discussions with Alain Spineux. Changed 'diff -u0' into 'diff -U 0' in regtest.do script in order to be compatible with command line argument parsing of newer gnu diff versions. Also added sort commands to sort find output because when order changes with respect to order in which files where created, this gives false fail on the hardlink check part of regression test. Based on problem reports by Dallas Legan, Shlomi Levi. Added defines to compile under CYGWIN based on patches by Dirk Eddelbuettel and Jari Aalto. Fixed bug in test script, that triggered when find(1) listed files in a different order than the order in which afio unpacked then into the directory. This bug cause the test script to report failure even though there was no failure. Bug reported by Andre Francois, also by Fred Wright and Bruce Bowler. Fixed bugs in test script, having to do with 'bash -c' and 'source' not being ablre to find the dircomp and dircompare commands, that were reported openSuse 10.3 and 11.4. (I have been unable to reproduce the bugs in my environment, so I hope the fixes work.) Bugs reported and fixes proposed by Fred Wright. Added .ogg as an extensions to the list of those to be excluded from compression by default. Proposed by Dirk Eddelbuettel. Fixed logic in in() to correctly report failure with a warning message when uncompression of a commpressed-archived file fails. Also fixes that, potentially valid archive data would be skipped after this error. Based on bug report by Daniel Webb. Closes Debian bug #291364. In next(), changed msg to variable-length string, to avoid possible buffer overflow. Part of the patch by Erik Schanze (downstream Debian). Extended -0 option to influence -t processing and -w and -W file reading too. The -0 option now toggles when used multiple times. Added -7 option to disable/enable globbing in processing -yYwW filename patterns. Based on feature request and patch by Christian Schrader (via Debian). Note that the semantics of the -7 command line options is sligtly different than it was in the Debian 2.5-3 to 2.5-6 branches: in those branches, -7 was non-toggling so it was not necessary to supply the -7 before any -yYwW options it should affect. Replaced several 0 by NULL to avoid compiler warnings. Based on patch by Erik Schanze (downstream Debian). Replaced sys_errlist usage by strerror(3). This avoids (on some systems) compiler warnings about sys_errlist being obsolete. Left in the old code (with #if 0) to ease porting. Based on patch by Erik Schanze (downstream Debian) and report by Mike Black. Fixed bug in gpg usage examples in script3/, and removed script5/ which had the same bug. Bug reported, and bug fix found, by Marcus Williams. Also added info on this to the manpage. In the earlier scripts, the built-in compression feature of gpg was used, but it turns out that this cannot be reliably used with afio: it should be disabled using the -z 0 option to gpg. The reason why gpg built-in compression cannot be used is as follows. When compression is used, and gpg is run twice on the same input file, it can generate differing outputs with different lengths. This is a problem for afio if the output length is larger than the afio -M option value. If the length is larger than the -M value, then afio will call the 'compression' program twice, once to get the 'compressed' file length and once to get the actual file contents and write them to the archive, and if the lenght is bigger in the second run then the data in the archive will be truncated (and therefore corrupted). Afio does emit an error message when this happens, but it might be overlooked. Some caveats: in testing it looks like if -z 0 is used with gpg, it will never produce different-length files for the same input, but I can not find anything in the documentation of gpg suggesting that this is a guaranteed feature. pgp (the non-GNU implementation) might has a similar different-length behavior causing afio to fail occasionally, but I have found no reports on the web about this. (An alternative would be to use openssl as the encryption engine??) Updated legal information for redistributers about the afio license in afio.c and in the .lsm files. Removed earlier text concluding that by Linux Software Map standards, the correct afio license label was LGPL. Added the text file afio_license_issues_v5.txt. All of this was triggerd by a blog entry writen by Tom Callaway, and informed in part by further discussion in the blog comment section and on some Debian mailing lists. Thanks to Mark Brukhartz for going on record to clarify the intent of the original license text. Increase default size for -M option from 2 to 250 megabytes. Version 2.5.1-2: Improved typesetting and ordering of manpage material: patch by Jaari Aalto. Some things to improve git compatibility: removed RCS tags from afio.1 and afio.c, removed trailing whitespace from files. Based om report by Jaari Aalto. Improved some questionable code in incheckdata(). Removed unused variable (causing compiler warning) in outflush() and compressfile(). Created afio home page on the web. Fixed bug in handling buffer size calculations when -s and -c options settings that create a 2GB memory buffer, by upgrading various variables to size_t size. Based on bug report and patch by Yuri D'Elia. Tests have shown that (at least on i386 with 32 bit ints and size_t) this fix does not handle correctly buffer sizes >2GB, probably because size_t is 4 byte, not 8, and I still have doubts about 2GB too. Fixing everything and testing it is just too painful on my current 32 bit Linux install on a box that has only like 700M real memory. Therefore: added code to prevent memory buffer sizes larger than 1.5 GB being used by -s and -c and by -M. For -M, probably a limit of 3.5GB is still OK but I have not tested it so better safe than sorry. On native 64 bit installs, these limits may be over-cautious (especially if the compiler uses sizeof(int)==8, which is NOT the case for Linux amd64), but as I have no way to test this I'd rather get bug reports and patches from 64 bit platform people who run into this limit than have their backups silently fail. Closes(?) Debian bug #578046. Added %V and %S subsitution in input/output file name and !script name generation if -s option used. Based on idea and patch by Gabor Kiss. Closes Debian bug #310806. Changed makefile to use $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) out of environment variables, these are set in some compile environments. Based on report and patch by Jari Aalto. Version 2.5.2: Fixed bug (introduced in 2.5) of afio giving an error message (and nonzero exit) when including a control file in an archive. Bug reported by Ian Campbell. Closes Debian bug #686613. Added d letter for the -1 option. Fixed two compiler errors (as reported by recent gcc) due to discrepancies between function declarations in afio.h and afio.c. Problem reported by Jari Aalto. Added some more size_t and ssize_t. Fixed formatting bug to restore output behavior of afio -o -0 -vv like it was in afio 2.5. Based on bug report by Corey Mann. Closes Debian bug #420654. Improved -r option: if a file with 0 length is in the archive, but the file on the filesystem is now longer, the -r option will now report an error (except for hard linked files). Based on problem report by Christian Schneider. Also updated -r description in manpage. Closes Debian bug #565202. Fixed typos in manual file (contol->control). Contributed by bfontaine on github. Added support in compfile.c for musl libc (http://musl-libc.org) which does not have MAXNAMLEN nor DIRSIZ. Based on patch by doughdemon on github. In savedirstamp and restoredirstamps: fixed a very unlikely memory leak. Changed to keep the head of a list that is never freed reachable, to make Valgrind happy. Contributed by Philipp Gesang. Tweaked regtest script to make it remove test directories left after a failed test before re-testing. Added -Wno-unused-result flag in Makefile to make compilation shut up about unused system call results from calls like dup() and nice(). The fact that the compiler issues these warnings is due to (in my opinion) a declaration bug in glibc, as not checking there results is fully legitimate in many situations. Removed the feature to optimise path names ('Optimizes pathnames with respect to the current and parent directories. For example ./src/sh/../misc/afio.c becomes src/misc/afio.c'). Only optimisation remaining in nameopt() is that multiple leading /// in path name get simplified into one leading /. This change is related to the other security related changes below. Improved security of handling of archives from untrusted sources when they are unpacked. Added the -8 options allowinsecurepaths, allowinsecuresymlinks, and the default behavior described in the manpage when these options are not used. Also added the -1 s option. Updated reregression test input archives to account for new behavior. Based on patch and e-mail input from Philipp Gesang. Added the -8 nosymlinks option and the -1 l option. Based on patch by Philipp Gesang. Fixed buffer overflow risk in inhead() when reading symlink destination value from corrupt or tampered-with archive. Changed -H promptscript argument processing to allow prompt script path names containing spaces or other special characters. This improves compatibility with naming conventions on Windows-centric filesystem. This breaks old behavior where spaces in the prompt script argument could be used to add extra arguments. Based on bug report and suggestion by Philipp Gesang. Added SECURITY CONSIDERATIONS section to the manpage. afio-2.5.2/INSTALLATION000066400000000000000000000111231340024410000142440ustar00rootroot00000000000000 To install afio, follow these steps: STEP 1. Compile. ---------------- Unpack the afio sources, go to the top level source directory, compile the binary by typing: make Side note on compiler warnings: ------------------------------- You may get some compiler warnings -- these do not always indicate a real problem. (The GCC maintainers add new types of warning messages regularly, and afio is usually behind in updating the sources to eliminate new warnings.) If you see warning like: ignoring return value of X, declared with attribute warn_unused_result these are spurious are generated by a bug in glibc: they can be ignored. See the file PORTING for more information on compiling afio on non-Linux machines. STEP 2 (OPTIONAL). Regression tests. ------------------------------------ Optionally, you can run some automatic regression tests to check if the new afio binary works OK. If you have an older version of afio installed, the tests will also check (just to be extra paranoid) if the archive format used by the old binary is still interoperable with the new binary. The formats should be compatible unless there is a serious bug. Sometimes the regression tests fail because they invoke certain unix shell tools, and the tools have changed their command line option syntax. See below for details on running the two automatic regression tests. STEP 3. Install the binary and manual page. ------------------------------------------- Do this manually, or type make install which installs the afio binary in /usr/local/bin, and the manual page in /usr/share/man/man1 -- these are the correct locations for most Linux systems. Notes on using afio ------------------- Afio has far too many options to be used directly from the command line, it is best used as an `archive engine' in a backup script. See the file SCRIPTS for more information on backup scripts that use afio. See the file PORTING for information on compiling afio on non-Linux machines. Details on the two automatic regression tests (step 2). ------------------------------------------------------- Test 1: regtest: file handling and archive portability regression test ---------------------------------------------------------------------- **Note: the test scripts may fail to work on non-GNU platforms that have very old versions of tools like awk, find, and diff. See the PORTING file for more information. If you are going to use afio for system backups, this test is best run from the root account. In that case the test will also try whether afio correctly invokes filesystem operations (like making devices and changing file ownership settings) that normal user accounts are not allowed to do. You can compile and run this test with make regtest This test prints a line with 'OK!' at the end it succeeds. --> this regression test is known to report small permission related problems on several Non-Linux platforms. These mostly have to do (I think) with different approaches the kernels take to handling permissions, so these small problems do not necessarily indicate that the compiled afio is buggy. If you are unsure about interpreting the result of a regression test, please check if any recent information (e.g. in the comments) on the site http://freshmeat.net/projects/afio/ answers your question. If not, or if you think you have found a new bug on your platform, whether Linux or not, feel free to mail the afio maintainer (see the README file). Test 2: regtest2gb: large file handling test -------------------------------------------- This test tries out the large file handling capabilities of afio, and is only applicable to systems with large (>2GB) file support. If you don't know if your system supports large files, you can find that out by running the test. This test requires a) sparse file support in the filesystem (which is present in most Unixes, including Linux, or b) 2.2 GB free space on the filesystem. You can compile and run this test with make regtest2gb This test prints a line with 'OK!' at the end it succeeds. --> this regression test will of course fail on platforms that do not support >2GB files. Note that, if the test fails, this is often not due to a bug in afio, but more likely to a missing feature or configuration problem in the kernel, the filesystem, the compiler, or the libraries. As of Dec 2003, the test is known to report success on - Red Hat Linux 7.3 - Debian Linux 3.0/testing (not 3.0/stable) on most platforms, including i386 - At least some versions of Solaris - FreeBSD 3.5-STABLE afio-2.5.2/Makefile000066400000000000000000000100411340024410000140560ustar00rootroot00000000000000SHELL=/bin/sh ## ## See the INSTALLATION file for quick installation instructions. ## ## I wrote this Makefile, based on comments in the source. -rich $alz. ## Define INDEX to use index() in place of strchr() (v7, BSD). #1 = -UINDEX ## Define MEMCPY when an efficient memcpy() exists (SysV). 2 = -DMEMCPY ## Define MKDIR when a mkdir() system call is present (4.2BSD, SysVr3). 3 = -DMKDIR ## Define NOVOID if your compiler doesn't like void casts. #4 = -UNOVOID ## Define SYSTIME to use rather than (4.2BSD). #5 = -USYSTIME ## Define VOIDFIX to allow pointers to functions returning void (non-PCC). 6 = -DVOIDFIX ## Define CTC3B2 to support AT&T 3B2 streaming cartridge tape. #7 = -UCTC3B2 ## Define HAVEFCNTL if you have 8 = -DHAVEFCNTL ## Define MYTEMPNAM if you don't have tempnam() #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 = -UUNIXPC ## Define HAVEMEMCMP if you have memcmp otherwise assumes bcmp c = -DHAVEMEMCMP ## Define DEFFMTCMD to being how to format the media you use the most ## This is the DEFault FoRMat CoManD. d = -DDEFFMTCMD='"fdformat /dev/fd0H1440"' ## 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 ## Define HAVE_LCHOWN is the system has an lchown call (like chown but does ## not follow symlinks) e2 = -DHAVE_LCHOWN ## Define PRG_COMPRESS to get something other than `gzip'. # you need to edit compfile.c (-G option) if you change this line. f = -DPRG_COMPRESS='"gzip"' ## Define HAVEFNMATCH if you want to use the gnu fnmatch() routine for # -y -Y -w -W matching. # If it is not defined, a primitive replacement match routine is used that # only supports patterns of the form "cccc" and "cccc*". Make sure that # you change the manual page in this case. g = -DHAVEFNMATCH # fnmatch() is in the gnu C library, so it is directly available on # Linux. If your system (e.g. SCO) does not have the gnu C library, # unpack the archive gnu.fnmatch.tar.gz and uncomment the following # two lines: #M = fnmatch.o #I = -I. # Please read the COPYING.LIB file in this archive if you plan to # redistribute afio executables with this library linked in. #uncomment one of the two lines below to #get the normal or large file compile environment # afio will work when compiled in both environments, but on old or non-linux # systems the large file compile environment itself might be buggy or beta. #LARGEFILEFLAGS= LARGEFILEFLAGS=-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE # even more warnings flags.. MW=-Wformat -Werror=format-security -Wall #the set of warning flags below can be uncommented to get lots of places where #code can be reviewed manually #MW=-Wtraditional -Wcast-qual -Wcast-align -Wconversion -pedantic -Wlong-long -Wimplicit -Wuninitialized -W -Wshadow -Wsign-compare -Wstrict-prototypes -Wmissing-declarations CFLAGS1 = -Wall -Wstrict-prototypes -s -O2 -fomit-frame-pointer -Wno-unused-result $(LARGEFILEFLAGS) $(MW) CC=gcc # also using contents of usin CPPFLAGS, CFLAGS, LDFLAGS out of environment # variables, if they exist CFLAGS += $(CFLAGS1) $1 $2 $3 $4 $5 $6 $7 $8 $9 $a $b $c $d $e $(e2) $f $g $I LDFLAGS += CPPFLAGS += afio : afio.o compfile.o exten.o match.o $M $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) \ afio.o compfile.o exten.o match.o $M -o afio clean: rm -f *.o afio rm -f regtest/cmpstat regtest/makesparse rm -f regtest/statsize regtest/statsize64 cd regtest; /bin/sh regtest.clean install: afio cp afio /usr/local/bin cp afio.1 /usr/share/man/man1 # generate default list of -E extensions from manpage # note: on sun, I had to change awk command below to nawk or gawk # to get it to work. exten_default.h : afio.1 awk -f exten_make.awk afio.1 >exten_default.h afio.o : afio.h patchlevel.h compfile.o : afio.h exten.o : afio.h exten_default.h match.o : afio.h regtest: regtestx regtestx: afio cd regtest; make; rm cmpstat regtest2gb: regtest2gbx regtest2gbx: afio cd regtest; make 2gb afio-2.5.2/PORTING000066400000000000000000000160341340024410000134730ustar00rootroot00000000000000 This afio version is based on a Linux port of afio 2.3. Since the original port, significant functionality has been added and some bugs were removed. While primarily intended for use under Linux, this code should be portable to other UNIX versions. As far as I know, there has been no afio development beyond 2.3 outside the Linux community, except for non-Linux patches that people mail back to me. -------------------------------------------------------------------------- The code in this archive compiles under Linux. You may have to tweak the makefile to compile it on other platforms. If you make a `clean' port of this code, meaning that you add ifdefs to the source files and `uncomment this for %s' lines to the makefile, you may want to send diffs to me (see README file for maintainer e-mail address) so that I can merge them with the next afio version. If you encounter any POSIX compatibility problems, please report them. -------------------------------------------------------------------------- UNSIGNED LONG LONG USE New potential porting problems introduced since version 2.4.7.9beta1. Since 2.4.7.9beta1, afio supports >2GB files, and this support depends on the compiler having `unsigned long long' support, and also on having `unsigned long long' format support in the printf and scanf functions. Any non-ancient version of gcc and glibc will have this support. In theory, it is not a huge job to add some ifdefs for platforms without compiler support for long long, ifdefs which drop the 2GB support but otherwise leave a functional afio that can be compiled with old compilers. However I (=the current maintainer) am not doing this yet until I get real problem reports or patches -- I suspect that in practice nobody is using such old compilers anymore. See comments in the afio 2.4.7 afio.h file for the story behind the typedef unsigned long long ulonglong; -------------------------------------------------------------------------- SOME POTENTIAL PORTING PROBLEMS 1) Availability of fnmatch(), see the makefile. 2) Changing the makefile to work with your make and C compiler. 3) Making floppy verify work on your unix flavor. The code in afio.c should be highly portable. However I can not guarantee it to keep working if changes are made to the defines in the makefile. The code in the other .c files was written on a Linux machine for gcc, and assumes a posixy environment. I have not tried it on other machines but on most modern unixes it should compile. I would like to hear about (un)successful ports to other machines. Some of the afio code is a horrible mess, making incremental changes very difficult. A complete rewrite of the floppy disk interfacing code is probably needed if extensions are to be added. At the moment afio is (afaik) the only fault tolerant compressing archiver program available, but it probably has some bugs lurking in it. Because of this afio is not an optimal solution. I hope that afio will be replaced by a compressing version of gnu cpio or by cpio combined with with a fault tolerant compression backend (probably based on a gzip library) in the future. ------------------------------------------------------------------ REGTESSION TEST SCRIPT PORTING The regtession test scripts mentioned in the INSTALL file may fail to work on non-GNU platforms that have very old versions of tools like bash, awk, find, and diff. ----------------- Some notes about regression testing on (some versions of) FreeBSD - FreeBSD has a `bash' exectutable that does not implement full GNU bash. The test scripts have been written to avoid any incompatibilities. ----------------- Some notes about regression testing on (some versions of) sun - if the default awk command is very old, it cannot parse the awk scripts. Change it to `nawk' or `gawk' -- see the comments in the regtest/regtest script. - if the default find command does not support the -print0 option, one test will fail. You can try to subsiture GNU find if it is installed - if some tests fail, then `diff -u' is used to print the differences. The default diff on some sun platforms does not recognise the -u option, You can try to subsiture GNU diff if it is installed. - On at least some version of sun, the default tar command incorrectly restores directory permissions. This will show up as failure of one regression test, but here actually tar is at fault. Example output where this happens: * compare unpacked tar archive with newly unpacked test archive drwxr-xr-x 7 kholtman zh 2048 Dec 1 2002 t2/afiot drwxrwxrwx 7 kholtman zh 2048 Dec 1 2002 t4/afiot cmpstat t4/afiot t2/afiot: mode difference drwxr-xr-x 2 kholtman zh 2048 Dec 2 2002 t2/afiot/ztest drwxrwxrwx 2 kholtman zh 2048 Dec 2 2002 t4/afiot/ztest cmpstat t4/afiot/ztest t2/afiot/ztest: mode difference drwxr-xr-x 3 kholtman zh 2048 Dec 1 2002 t2/afiot/special drwxrwxrwx 3 kholtman zh 2048 Dec 1 2002 t4/afiot/special - The sun kernel (on at least some versions of sun) handles permission bits on symlinks different from the Linux way. This results in a failure of at least one regression test. Example output where this happens: * compare table-of-contents file made by new afio with archived toc t2.arch t2.new differ: char 2048, line 44 --- t2.arch 2003-12-19 22:02:53.000001000 +0100 +++ t2.new 2003-12-19 22:02:53.000001000 +0100 @@ -44,9 +44,6 @@ -lrwxrwxrwx 1 x x DATE afiot/link with spaces S-> name with spaces -lrwxrwxrwx 1 x x DATE afiot/link1 S-> y2k -lrwxrwxrwx 1 x x DATE afiot/linkx S-> /etc/sysconfig/ipchains +lrwxr-xr-x 1 x x DATE afiot/link with spaces S-> name with spaces +lrwxr-xr-x 1 x x DATE afiot/link1 S-> y2k +lrwxr-xr-x 1 x x DATE afiot/linkx S-> /etc/sysconfig/ipchains FAILURE in: compare table-of-contents file made by new afio with archived toc ------------------------------------------------------------------ HISTORICAL PORTING NOTES Historical note: The porting hints below were all written before 1996 as far as I can tell -- they are probably not relevant to recent systems. Note for SCO porters from Karel Kubat, karel@icce.rug.nl: Please read the makefile about the changes needed for fnmatch(). You may want to change the -DDEFFMTCMD value in the makefile. If you encounter any other options which may need changing, please mail me at karel@icce.rug.nl. I'm not the maintainer of afio for SCO platforms, but I'd like to know if you come across anything spectacular. Note for IRIX porters using the SGI native C compiler (cc): Modify the Makefile to use CC=cc, comment out the CFLAGS1 line, and add -Dirix_cc_compatibility to the CFLAGS line. Note for HPUX porters using gcc (maybe some (older?) gcc versions only): These steps seem to be needed to get afio to compile, but I cannot guarantee that they are sufficient to make it work on HPUX: - Comment out '8 = -DHAVEFCNTL' in the Makefile - Remove the '-g' from the CFLAGS1 line in the Makefile. afio-2.5.2/README000066400000000000000000000103141340024410000133010ustar00rootroot00000000000000 This is afio 2.5.2 To find out if this is the latest version, go to http://members.chello.nl/k.holtman/afio.html The current maintainer is Koen Holtman (koen.holtman@ieee.org). (Alternative e-mail address for the maintainer: k.holtman@chello.nl) When mailing the maintainer, please use the word `afio' somewhere in the subject line, this lowers the chance that your mail will get accidentally deleted. Message from the current maintainer, written in 2018: Afio is now in a 'maintenance only' mode, with several years between formal releases. 'Maintenance only' means that I will be happy to answer questions, to handle bug and problem reports, and to incorporate patches that fix bugs, portabiliy problems, and security issues. However, it is very unlikely that I will not work on adding major new features, even if the feature request comes with a patch file implementing the feature. The main concern that drives my maintenance policy is to avoid creating new bugs that would cause the backup process to silently fail. Because of this concern, I do a lot of manual reviewing and testing for every patch I receive. I have concluded that I simply do not have the time (and interest level) anymore to do this for patches adding new features. My interest has declined because since about 2005, hard drives have become so cheap that making fault tolerant compressed archive files for writing to tapes of floppy disks, as afio can do, has become less and less relevant to the needs of average Linux user. I make my personal system backups using rsync on a detachable hard drive. >> See the first lines of afio.c for licensing/redistribution information << -------------------------------------------------------------------------- Afio makes cpio-format archives. It deals somewhat gracefully with input data corruption. Supports multi-volume archives during interactive operation. Afio can make compressed archives that are much safer than compressed tar or cpio archives. Afio is best used as an `archive engine' in a backup script. Since version 2.4.8beta1, afio supports files greater than 2 GB. -------------------------------------------------------------------------- See the file INSTALLATION for installation instructions. See the file SCRIPTS for more information on backup scripts that use afio. See the file PORTING for information on compiling afio on non-Linux machines. --------------------------------------------------------------------------- This afio version is based on a Linux port of afio 2.3. Since the original port, significant functionality has been added and some bugs were removed. While primarily intended for use under Linux, this code should be portable to other UNIX versions. As far as I know, there has been no afio development beyond 2.3 outside the Linux community. Thus, it should be safe to advertise ports of this code to other UNIX versions as ports of afio version 2.4.1. --------------------------------------------------------------------------- Afio has far too many options and features (some of which are not even in the manual page). Anything in afio that doesn't relate to reading or writing an archive from/to a simple file or pipe or backing up and restoring from floppies remains untested. In particular, nobody has verified if the options -p -d -e -g -h -j -l -m -u and -R and the special case archive name `!command' really do what they claim to do. Typical `tested' afio uses are ... | afio -o -v -s 1440k -F -Z /dev/fd0H1440 afio -oZvx /tmp/pipe1 /dev/tty8 >/var/adm/backup WARNING1: the code for -F (and -f and -K) is a complete mess. It will probably work in the normal case, but don't expect it to handle a write/verify error correctly. If you get such an error, best thing is to restart afio completely. WARNING2:The remote archive facilites (host:/file archive names) have not been exhaustively tested. These facilities have seen a lot of real-life use though. However, there may be bugs in the code for error handling and error reporting with remote archives. --------------------------------------------------------------------------- afio-2.5.2/SCRIPTS000066400000000000000000000041531340024410000134770ustar00rootroot00000000000000 Afio has far too many options to be used directly from the command line, it is best used as an `archive engine' in a backup script. 1) Using an existing script. There are a number of backup scripts using afio that I know of. Apart from incorporating the functions of afio, such scripts offer some general `administrative structure' like backup volumes, file exclusion, log files and incremental backup facilities. The available scripts have some large differences in the type of backup device they were primarily designed for (from floppy to network attached tape), the amount of maintenance done on them, and presumably the amount of remaining bugs. A list of scripts that use afio: This list was last updated Dec 2003 -- I have only listed scripts for which I found some evidence that they are (still) maintained. --------------------------------------------------------------------------- Title: tob: Tape Oriented Backup http://tinyplanet.ca/projects/tob/ --------------------------------------------------------------------------- Title: flexbackup: A flexible backup tool. http://www.flexbackup.org/ --------------------------------------------------------------------------- Title: KBackup - Karsten's Backup System http://kbackup.sourceforge.net/ --------------------------------------------------------------------------- 2) Writing your own backup script. (Or adapting an existing one.) Aside from the manual page, the files README.afio, README.linux, and script*/* provide information for script writers. Three sample backup scripts are included with this afio release. The material in script1/ is written by Andrew Stevens, that in script2/ by Dave Gymer. All scripts will probably need some editing to run on your configuration. Sample scripts for backups with pgp encryption are included in script3/. There were contributed by Karsten Ballueder. Sample scripts for backups with GnuPG (gpg) encryption are also included in script3/. There were based on contributions by Jens Getreu. A sample script for use with the -H option is included in script4/. This script was provided by Raphael Manfredi. afio-2.5.2/afio.1000066400000000000000000001462521340024410000134340ustar00rootroot00000000000000'br .TH AFIO 1 .SH NAME afio \- manipulate archives and files .SH SYNOPSIS .B ... | .B afio \-o [ .I options ] archive : write (create) archive .br .B afio \-i [ .I options ] archive : install (unpack) archive .br .B afio \-t [ .I options ] archive : list table-of-contents of archive .br .B afio \-r [ .I options ] archive : verify archive against filesystem .br .B afio \-p [ .I options ] directory [ ... ] : copy files .PP .SH DESCRIPTION .I Afio manipulates groups of files, copying them within the (collective) filesystem or between the filesystem and an .I afio archive. .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 . Cannot be combined with the .B \-Z option. .PP With .BR \-r , reads .IR archive and verifies it against the filesystem. This is useful for verifying tape archives, to ensure they have no bit errors. The verification compares file contents, but not permission bits and non-file filesystem entities, so it cannot be used as a reliable tool to detect every possible change made to a filesystem. .PP Creates missing directories as necessary, with permissions to match their parents. .PP Removes leading slashes from pathnames, making all paths relative to the current directory. This is a safety feature to prevent inadvertent overwriting of system files when doing restores. To suppress this safety feature, the .BR \-A option must be used while writing an archive, but also when reading (installing), verifying, and cataloging an existing archive. .PP Supports compression while archiving, with the .BR \-Z option. Will compress individual files in the archive, not the entire archive datastream, which makes .I afio compressed archives much more robust than .I `tar\ zc' type archives. .PP Supports multi-volume archives during interactive operation (i.e., when .I /dev/tty is accessible and .I SIGINT is not being ignored). .PP .SH OPTIONS .TP 13 .BI "\-@ " address Send email to .I address when a volume change (tape change, floppy change) is needed, and also when the entire operation is complete. Uses .IR sendmail (1) to send the mail. .TP .B \-a Preserve the last access times (atimes) of the files read when making or verifying an archive. .B Warning: if this option is used, .I afio will change the last inode changed times (ctimes) of these files. Thus, this option cannot be used together with an incremental backup scheme that relies on the ctimes being preserved. .TP .BI \-b "\ size" Read or write .IR size -character archive blocks. Suffices of .BR b , .BR k , .B m and .B g denote multiples of .IR 512 , .IR kilobytes , .IR megabytes and .IR gigabytes , respectively. Defaults to .I 5120 for compatibility with .IR cpio (1). In some cases, notably when using .I ftape with some tape drives, .B \-b 10k is needed for compatibility. Note that .B \-b 10k is the default block size used by .IR tar (1), so it is usually a good choice if the tape setup is known to work with .IR tar (1). .TP .BI \-c "\ count" Buffer .I count archive blocks between I/O operations. A large .I count is recommended for efficient use with streaming magnetic tape drives, in order to reduce the number of tape stops and restarts. .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 on restoring files. By default, .I afio creates sparse filesystem blocks (with .IR lseek (2)) when possible when restoring files from an archive, but not if these files were stored in a compressed form. Unless stored in a compressed form, sparse files are not archived efficiently: they will take space equal to the full file length. (The sparse file handling in .I afio does not make much sense except in a historical way.) .TP .B \-k Rather than complaining about unrecognizable input, skip unreadable data (or partial file contents) at the .I beginning of the archive file being read, and search for the next valid archive header. This option is needed to deal with certain types of backup media damage. It is also useful to support quick selective restores from multi-volume archives, or from searchable block devices, if the volume or location of the file to be restored is known in advance (see the .B \-B option). If, for example, a selective restore is done with the fourth volume of a multi-volume afio archive, then the .B \-k option needs to be used, else .I afio will complain about the input not being a well-formed archive. .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 "\ size" Restrict each portion of a multi-volume archive to .I size characters. This option recognizes the same size 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). .I size can be a single size or a comma-seperated list of sizes, for example '2m,5m,8m', to specify different sizes for the subsequent volumes. If there are more volumes than sizes, the last specified size is used for all remaining volumes. If this option is used, the special character sequences .B %V and .B %S in the input/output filename or command string are replaced by the current volume number and volume size. Use .B %% to produce a single % character. The .B \-s option is 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. When an archive is being read or written, using .B \-s causes .I afio to prompt for the next volume if the specified volume length is reached. The .B \-s option will also cause .I afio to prompt if there is a premature EOF while reading the input. The special case .B \-s 0 will activate this prompting for the next volume on premature EOF without setting a volume length. When writing an archive, .I afio will prompt for the next volume on end-of-media, even without .B \-s 0 being supplied, if the device is capable of reporting end-of-media. If the volume .I size specified is not a multiple of the block size set with the .B \-b option, then .I afio(1) will silently round down the volume size to the nearest multiple of the block size. This rounding down can be suppressed using the .B \-9 option: if .B \-9 is used, .I afio(1) will write a small block of data, smaller than the .B \-b size, at the end of the volume to completely fill it to the specified size. Some devices are not able to handle such small block writes. .TP .B \-u Report files with unseen links. .TP .B \-v Verbose. Report pathnames (to stderr) as they are processed. When used with .BR \-t , gives an .I "ls \-l" style report (including link information) to stdout instead. When used twice .RB ( \-vv ) with .BR \-o , gives an .I "ls \-l" style report to stdout while writing the archive. (But this use of .B \-vv will not work if the archive is also being written to stdout.) .TP .BI \-w "\ filename" Treats each line in .I filename as an .B \-y pattern, see .BR \-y . .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 "\ pattern" Restrict processing of files to names matching shell wildcard pattern .IR pattern . Use this flag once for each pattern to be recognized. With the possible exception of the presence of a leading slash, the complete file name as appearing in the archive table-of-contents must match the pattern, for example the file name 'etc/passwd' is matched by the pattern '*passwd' but NOT by the pattern 'passwd'. See .B `man 7 glob' for more information on shell wildcard pattern matching. The only difference with shell wildcard pattern matching is that in .I afio the wildcards will also match '/' characters in file names. For example the pattern '/usr/src/*' will match the file name '/usr/src/linux/Makefile', and any other file name starting with '/usr/src'. Unless the .B \-S option is given, any leading slash in the pattern or the filename is ignored when matching, e.g. .I /etc/passwd will match .IR etc/passwd . Use .B \-Y to supply patterns which are .I not to be processed. .B \-Y overrides .B \-y if a filename matches both. See also .BR \-w\ and\ \-W . See also the .B \-7 option, which can be used to modify the meaning of .BR \-y ", " \-Y ", " \-w ", and " \-W when literal matching without wildcard processing is needed. .B Note: if .I afio was compiled without using the GNU fnmatch library, then the full shell wildcard pattern syntax cannot be used, and matching support is limited to patterns which are a full literal file name and patterns which end in '*'. .TP .B \-z Print execution statistics. This is meant for human consumption; use by other programs is officially discouraged. .TP .B \-A Do not turn absolute paths into relative paths. That is don't remove the leading slash. Applies to the path names written in an archive, but also to the path names read out of an archive during read (install), verify, and cataloging operations. .TP .B \-B If the .B \-v option is used, prints the byte offset of the start of each file in the archive. If your tape drive can start reading at any position in an archive, the output of .B \-B can be useful for doing quick selective restores. .TP .BI \-D "\ controlscript" Set the control script name to .IR controlscript , see the section on .B control files below. .TP .BI \-E "\ [+]filename" " | \-E CS | \-E CI" While creating an archive with compressed files using the .B \-Z option, disable (attempts at) compression for files with particular extensions. This option can be used to speed up the creation of the archive, by making .I afio avoid trying to use .I gzip on files that contain compressed data already. By default, if no specific .B \-E option is given, all files with the extensions 'br the two START_ and END_ comments below are used by the makefile to create 'br the compiled-in defaults for the \-E option. 'br NOTE: the awk script called by in the makefile disregards all 'br FIRST words on each line below, 'br i.e. it disregards the .I typesetting commands and the word and. 'br so BE CAREFUL TO TAKE THIS INTO ACCOUNT if you edit the text below, 'br else the awk script might miss some extensions, or take some 'br common words you add as default extensions. 'br START_EXT_LIST .I .Z .z .gz .bz2 .tgz .I .arc .zip .rar .lzh .lha .I .uc2 .tpz .taz .tgz .rpm .zoo .deb .I .gif .jpeg .jpg .tif .tiff .png .pdf .I .arj .avi .bgb .cab .cpn .hqx .jar .I .mp3 .mpg .mpq .pic .pkz .psn .sit .ogg and .I .smk 'br END_EXT_LIST will not be compressed. Also by default, the file extension matching is case-insensitive (to do the right thing with respect to MS-DOS based filesystems). The .BI \-E "\ filename" form of this option will replace the default list of file extensions by reading a new list of file extensions, separated by whitespace, from .IR filename . .I filename may contain comments preceded by a #. The extensions in .I filename should usually all start with a dot, but they do not need to start with a dot, for example the extension 'tz' will match the file name 'hertz'. The .BI \-E "\ +filename" form (with a + sign in front of .IR filename ) can be used to specify extensions in addition to the built-in default list, instead of replacing the whole default list. To make extension matching case-sensitive, add the special option form .B \-E CS to the command line. The form .B \-E CI invokes the (default) case-insensitive comparison. See also the .B \-6 option, which offers an additional way to suppress compression. .TP .B \-F This is a floppy disk, .B \-s is required. Causes floppy writing in .B O_SYNC mode under Linux. With kernel version 1.1.54 and above, this allows .I afio to detect some floppy errors while writing. 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), .I 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: Ready for disk [#] on [output] (remove the disk when the light goes out) Type "go" (or "GO") when ready to proceed (or "quit" to abort): .fi .TP .BI \-G "\ factor" Specifies the .IR gzip (1) compression speed factor, used when compressing files with the .B \-Z option. Factor 1 is the fastest with least compression, 9 is slowest with best compression. The default value is 6. See also the .IR gzip (1) manual page. If you have a slow machine or a fast backup medium, you may want to specify a low value for .I factor to speed up the backup. On large (>200k) files, .B \-G 1 typically zips twice as fast as .BR "\-G 6" , while still achieving a better result than .IR compress "(1)." The zip speed for small files is mainly determined by the invocation time of .I gzip (1), see the .B \-T option. .TP .BI "\-H " promptscript Specify a script to run, in stead of using the normal prompt, before advancing to the next archive volume. The script will be run with the volume number, archive specification, and the reason for changing to the next volume as arguments. The script should exit with 0 for OK and 1 for abort, other exit codes will be treated as fatal errors. As of .I afio version 2.5.2, the promptscript can be a file name containing spaces or other special characters. .TP .B \-J Try to continue after a media write error when doing a backup (normal behavior is to abort with a fatal error). .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 .nf [Writing/Verify] of disk [disk #] has FAILED! Enter 1 to RETRY this disk Enter 2 to REFORMAT this disk before a RETRY Enter quit to ABORT this backup .fi Currently, .I afio will not process the answers 1 and 2 in the right way. The menu above is only useful in that it signifies that something is wrong. .TP .BI "\-L " Log_file_path Specify the name of the file to log errors and the final totals to. .TP .BI \-M "\ size Specifies the maximum amount of memory to use for the temporary storage of compression results when using the .B \-Z option. The default is .B \-M 250m (250 megabytes). If the compressed version of a file is larger than this (or if .I afio runs out of virtual memory), .IR gzip (1) is run twice of the file, the first time to determine the length of the result, the second time to get the compressed data itself. .TP .BI \-P "\ progname" Use the program .I progname instead of the standard .IR gzip (1) for compression and decompression with the .B \-Z option. For example, use the options .B \-Z \-P bzip2 to write and install archives using .IR bzip2 (1) compression. If .I progname does not have command line options (\-c, \-d, and \-) in the style of .IR gzip (1) then the .B \-Q option can be used to supply the right options. The compression program used must have the property that, if the output file size exceeds the value of the .B \-M option, then when the compression program is run for a second time on the same input, it must produce an output with exactly the same size. (See also the .B \-M option description.) The GnuPG .RB ( gpg ) encryption program does not satisfy this lenght-preserving criterion unless its built-in compression is disabled (see examples in the afio source script3/ directory). See also the .BR \-Q , .B \-U and .B \-3 options. .TP .BI \-Q "\ opt" Pass the option .I "opt" to the compression or decompression program used with the .B \-Z option. For passing multiple options, use .B \-Q multiple times. If no .B \-Q flag is present, the standard options are passed. The standard options are .B \-c \-6 when the program is called for compression and .B \-c \-d when the program is called for decompression. Use the special case .B \-Q "" if no options at all are to be passed to the program. .TP .BI \-R "\ Disk format command string" This is the command that is run when you enter 2 to reformat the disk after a failed verify. The default (fdformat /dev/fd0H1440) can be changed to a given system's default by editing the Makefile. You are also prompted for formatting whenever a disk change is requested. .TP .BI \-S Do not ignore a leading slash in the pattern or the file name when matching .B \-y and .B \-Y patterns. See also .BR \-A . .TP .BI \-T "\ threshold" Only compress a file when using the .B \-Z option if its length is at least .IR threshold . The default is .BR "\-T 0k" . This is useful if you have a slow machine or a fast backup medium. Specifying .B "\-T 3k" typically halves the number of invocations of .IR gzip (1), saving some 30% computation time, while creating an archive that is only 5% longer. The combination .B \-T 8k \-G 1 typically saves 70% computation time and gives a 20% size increase. The latter combination may be a good alternative to not using .B \-Z at all. These figures of course depend heavily on the kind of files in the archive and the processor - i/o speed ratio on your machine. See also the .B \-2 option. .TP .B \-U If used with the .B \-Z option, forces compressed versions to be stored of all files, even if the compressed versions are bigger than the original versions, and disregarding any (default) values of the .B \-T and .B \-2 options. This is useful when the .B \-P and .B \-Q options are used to replace the compression program .I gzip with an encryption program in order to make an archive with encrypted files. Due to internal limitations of .IR afio , use of this flag forces the writing of file content with each hard linked file, rather than only once for every set of hard linked files. .B WARNING: use of the \-U option will also cause compression (or whatever operation the .B \-P option indicates) on files larger than 2 GB, if these are present in the input. Not all compression programs might handle such huge files correctly (recent Linux versions of gzip, bzip2, and gpg have all been tested and seem to work OK). If your setup is obscure, some testing might be warranted. .TP .BI \-W "\ filename" Treats each line in .I filename as an .B \-Y pattern, see .BR \-Y . .TP .BI \-Y "\ pattern" Do .I not process files whose names match shell wildcard pattern .IR pattern . See also .BR "\-y " and " \-W" . .TP .B \-Z Compress the files that go into the archive when creating an archive, or uncompress them again when installing an archive. .I afio \-Z will compress each file in the archive individually, while keeping the archive headers uncompressed. Compared to .I tar zc style archives, .I afio \-Z archives are therefore much more fault-tolerant against read errors on the backup medium. When creating an archive with the .I \-Z option, .I afio will run .I gzip on each file encountered, and, if the result is smaller than the original, store the compressed version of the file. Requires .IR gzip (1) to be in your path. Mainly to speed up .I afio operation, compression is not attempted on a file if: 1) the file is very small (see the .B \-T option), 2) the file is very large (see the .B \-2 option), 3) the file has a certain extension, so it probably contains compressed data already (see the .B \-E option), 4) the file pathname matches a certain pattern, as set by the .B \-6 option, 5) the file has hard links (this due to an internal limitation of afio, but this limitation does not apply if the .B \-l option is also used). Regardless of the above, if the .B \-U option is used then the compression program is always run, and the compressed result is always stored. When installing an archive with compressed files, the .B \-Z option needs to be used in order to make afio automatically uncompress the files that it compressed earlier. The .B \-P option can be used to do the (un)compression with programs other than .IR gzip , see the .B \-P (and .B \-Q and .BR \-3 ) options in this manpage for details. See also the .BR \-G option which provides yet another way to tune the compression process. .TP .B \-0 Use filenames terminated with '\\0' instead of '\\n'. When used as follows: .IR "find ... \-print0 | afio \-o \-0 ..." , it ensures that any input filename can be handled, even a file name containing newlines. When used as .IR "afio \-t \-0 ... | ..." , this allows the table of contents output to be parsed unambiguosly even if the filenames contain newlines. The .B \-0 option also affects the parsing of the files supplied by .B "\-w file" and .B "\-W file" options: if the option .B \-0 precedes them in the command line then the pattern lines contained in the .BR file s should be terminated with '\\0' in stead of '\\n'. A second use of .B \-0 toggles the option. This can be useful when using multiple pattern files or when combining with the .B \-t option. .TP .BI \-1 "\ warnings-to-ignore" Control if .IR afio (1) should exit with a nonzero code after printing certain warning messages, and if certain warning messages should be printed at all. This option is sometimes useful when calling .IR afio (1) from inside a backup script or program. .IR afio (1) will exit with a nonzero code on encountering various 'hard' errors, and also (with the default value of the .B \-1 option) when it has printed certain warning messages during execution. .I warnings-to-ignore is a list of letters which determines the behavior related to warning messages. The default value for this option is .BR "\-1 mc" . For .I afio versions 2.4.3 and earlier, the default was .BR "\-1 a" . For .I afio versions 2.4.4 and 2.4.5, the default was .BR "\-1 ''" . The defined .I warnings-to-ignore letters are as follows. .B a is for for ignoring .IR a ll possible warnings on exit: if this letter is used, the printing of a warning message will never cause a nonzero exit code. .B m is for ignoring in the exit code any warning about .IR m issing files, which will be printed when, on creating an archive, a file whose name was read from the standard input is not found. .B c is for ignoring in the exit code the warning that the archive being created will not be not fully compatible with .IR c pio or afio versions 2.4.7 or lower. .B C is the same as .IR c , but in addition the warning message will not even be printed. .B M will suppress the printing of all warning messages asssociated with .IR M ultivolume archive handling, messages like "Output limit reached" and "Continuing". .B d is for ignoring in the exit code any warnings about changed files, which will be printed when, on creating an archive, a file that is being archived changes while it is being written into the archive, where the changing is detected by examining the file modification time stamp. .B r is for ignoring certain warnings during the verify (\-r) operation. If this letter is used, some verification errors that are very probably due to changes in the filesystem, during or after the backup was made, are ignored in determining the exit code. The two verification errors that are ignored are: 1) a file in the archive is no longer present on the filesystem, and 2) the file contents in the archive and on the filesystem are different, but the file lengths or the file modification times are also different, so the difference in contents is probably due to the file on the file system having been changed. .B s is for ignoring in the exit code the warning printed when the protection code (as described in the section about the .B -8 option) rewrites a suspicious path name for a file or symlink that is being unpacked. .B l is for ignoring in the exit code the warning printed when the .B -8 nosymlinks option is used and a symlink is encountered. .B n is for ignoring in the exit code a particular class of .IR n o-such-file warnings: it ignores these warnings when they happen after the file has already been successfully opened. This unusual warning situation can occur when archiving files on Windows smbfs filesystems -- due to a Windows problem, smbfs files with non-ASCII characters in their names can sometimes be opened but not read. When the .B \-Z option is used, the .I n letter function is (currently) only implemented for files with sizes smaller than indicated by the .B \-T option, so in that case the .B \-T option is also needed for this letter to have any effect. .TP .BI "\-2 " maximum-file-size-to-compress Do not compress any files which are larger than this size when making a compressed archive with the .B \-Z option. The default value is .BR "\-2 200m" (200 Megabytes). This maximum size cutoff lowers the risk that a major portion of a large file will be irrecoverable due to small media errors. If a media error occurs while reading a file that .I afio has stored in a compressed form, then .I afio and .I gzip will not be able to restore the entire remainder of that file. This is usually an acceptable risk for small files. However for very large files the risk of loosing a large amount of data because of this effect will usually be too big. The special case .B "\-2 0" eliminates any maximum size cutoff. .TP .BI "\-3 " filedescriptor-nr Rewind the filedescriptor before invoking the (un)compression program if using the .B \-Z option. This is useful when the .B \-P and .B \-Q options are used to replace the compression program .I gzip with some types of encryption programs in order to make or read an archive with encrypted files. The rewinding is needed to interface correctly with some encryption programs that read their key from an open filedescriptor. If the .B \-P program name matches 'pgp' or 'gpg', then the .B \-3 option .I must be used to avoid .IR afio (1) reporting an error. Use the special case .B "\-3 0" to suppress the error message without rewinding any file descriptor. The .B "\-3 0" option may also be needed to successfully read back encrypted archives made with .I afio version 2.4.5 and older. .TP .B \-4 (Deprecated, the intended effect of this option is now archived by default as long as the .B \-5 option is not used. This option could still be useful for compatibility with machines running an older version of .IR afio .) Write archive with the `extended ASCII' format headers which use 4-byte inode numbers. Archives using the extended ASCII format headers are .B not compatible with any other archiver. This option was useful for reliably creating and restoring sets of files with many internal hard links, for example a news spool. .TP .B \-5 Refuse to create an archive that is incompatible with .IR cpio (1). If this option is used, .I afio will never write any `large ASCII' file headers that are incompatible with .IR cpio (1), but fail with an error code instead. See the ARCHIVE PORTABILITY section above for more information on the use of `large ASCII' file headers. .TP .B \-6 "\ filename" While creating an archive with compressed files using the .B \-Z option, disable (attempts at) compression for files that match particular shell patterns. This option can be used to speed up the creation of the archive, by making .I afio avoid trying to use .I gzip on files that contain compressed data already. Reads shell wildcard patterns from .IR filename , treating each line in the file as a pattern. Files whose names match these patterns are not to be compressed when using the .B \-Z option. Pattern matching is done in exactly the same way as described for the .B \-y option. See also the .B \-E option: the (default) settings of the .B \-E option will further restrict compression attempts. The .B \-E option controls compression attempts based on file extensions; the .B \-6 option is mainly intended as a method for excluding all files in certain subdirectory trees from compression.. .TP .B \-7 Switch between shell wildcard pattern matching and exact name matching (without interpreting any wildcard characters) for the patterns supplied in the .BR \-y ", " \-Y ", " \-w ", and " \-W options. If the .B \-7 option is used in front of any option .BR \-y ", " \-Y ", " \-w ", or " \-W , then the patterns supplied in these options are not intrerpreted as wildcard patterns, but as character strings that must match exactly to the file name, except possibly in leading slashes. This option can be useful for handling the exceptional cases where file names in the archive, or the names of files to be archived, contain wildcard characters themselves. For example, .I find /tmp \-print0 | afio \-ov \-Y '*.jpg' \-7 \-Y '/tmp/a[12]*4' \-0 archive can be used to archive files all files under /tmp, even files with a '\\n' character in the name, except for .jpg files and the file with the exact name .IR /tmp/a[12]*4 . A second use of .B \-7 toggles the matching for subsequently occuring .BR \-y ", " \-Y ", " \-w ", and " \-W back to shell wildcard pattern matching. .TP .BI \-8 "\ directive" Modify various behavior regarding symlinks. The directive .IR nosymlinks applies to both archive creation and archive unpacking. During archive creation, it suppresses the inclusion of any symlink entry in the archive. In unpacking, it suppresses the unpacking of any symlink entry in the archive. This directive does not affect the interpretation of existing symlinks on the filesystem during the path resolution process where afio resolves the directory name components in front of the last / in a path name. The directive .IR allowinsecurepaths applies to the security of archive unpacking. As of version 2.5.2, afio has protection mechanisms that apply to the unpacking of potentially untrusted archives. On unpacking, afio will by default (since version 2.5.2) inspect every pathname in the archive to detect the occurrence of a .. subpath in it. If one or more of these are present this is almost almost certainly due to the archive having been constructed by an attacker. The goal of the attack would be to have the afio unpacking operation over-write system or user files with new contents, via the use of using specially constructed path names like ../../../../../etc/password or ../../../../../home/a_user/.bashrc that resolve to the location of such configuration files. Therefore, if any .. subpaths are detected in a path name in an archive being unpacked, afio issues a warning, and then rewrites every '..' in the path name to 'XX', and the archive entry is unpacked to the rewritten path name instead. The .IR allowinsecurepaths directive disables the above rewriting of likely-insecure path names. Note that afio, while unpacking an archive, will also protect against that archive including potentially insecure path names that start with a leading /, by stripping off the leading / before using the path name is used, which has the effect of the archive entry relative to the current working directory. This stripping behavior can be disabled with the .B \-A option. The directive .IR allowinsecuresymlinks applies to a further the protection mechanism that applies to the unpacking of potentially untrusted archives. On unpacking, afio will by default (since version 2.5.2) inspect every symlink destination in the archive to detect the occurrence of a leading / or a .. subpath in it. If a leading / or .. subpaths are detected in the symlink destination, afio issues a warning, rewrites them to X or XX, and the result is used as the unpacked symlink destination instead. The .IR allowinsecuresymlinks directive disables this protective rewriting behavior. Some further background: an attacking archive with an insecure symlink will typically include, as an entry after the insecure symlink, a file entry with a path that follows the insecure symlink leading to a location in the filesystem where a system or user configuration file can be overwritten. An archive with an insecure symlink may be created most easily an attacker who has the entire archive creation process under their control. However, in another case, the attacker is an untrusted end user on a multi-user system, where a trusted system administrator is creating a backup of a live file system containing directories under control of the untrusted end user. The untrusted end user can potentially exploit race conditions in the backup process, by creating temporary symlinks and files in their own home directory, resulting in in archive contents that would modify system configuration files when later unpacked if the protection mechanism were disabled using the .IR allowinsecuresymlinks directive. The above described protection mechanisms are limited to symlinks. A untrusted archive attack that uses specially constructed hard link entries in the archive is theoretically possible with some archivers, but is not possible with afio, because of the special way that afio represents hard links in an archive. .TP .B \-9 Do not round down any .B \-s volume sizes to the nearest .B \-b block size. See the .B \-s option. .PP .SH ARCHIVE PORTABILITY .I afio archives are portable between different types of UNIX systems, as they contain only ASCII-formatted header information. .PP Except in special cases discussed below, .I afio will create archives with the same format as ASCII .IR cpio (1) archives. Therefore .IR cpio (1) can usually be used to restore an .I afio archive in the case that .I afio is not available on a system. (With most .I cpio versions, to unpack an ASCII format archive, use .IR "cpio \-c" , and for GNU .IR cpio (1) use .IR "cpio \-H odc" .) When unpacking with .IR cpio , any compressed files inside an .I "afio \-Z" archive are not uncompressed by .IR cpio , but will be created on the file system as compressed files with a .z extension. .PP Unfortunately, the ASCII cpio archive format cannot represent some files and file properties that can be present in a modern UNIX filesystem. If afio creates an archive with such things, then it uses an afio-specific 'large ASCII' header for the files concerned. Archives with large ASCII headers cannot be unpacked completely by .I cpio or .I afio versions before 2.4.8. .PP When creating an archive, the `large ASCII' header is used by .I afio to cover the following situations: .RS 3 .TP 3 .B o A file has a size larger than 2 GB .TP .B o The archive contains more than 64K files which have hard links .TP .B o A file, directory, or special file has a UID or GID value larger than 65535. .RE .PP The .BR \-5 option can be used to always preserve .I cpio compatibility, it will cause .I afio to fail rather than produce an incompatible archive in the cases above. .PP Archives made using the (deprecated) .BR \-4 option are also .BR not compatible with .IR cpio , but they are compatible with .I afio versions 2.4.4 and later. .PP .SH ARCHIVE FILE FORMAT An .I afio archive file has a simple format. The archive starts with a file header for the first file, followed by the contents of the first file (which will either be the exact contents byte-for-byte, or the exact contents in some compressed format). The data of the first file is immediately followed by the file header of the second file, and so on. At the end, there is a special `end of archive' header, usually followed by some padding bytes. .PP A multi-volume .I afio archive is simply a normal archive split up into multiple parts. There are no special volume-level data headers. This means that that volumes can be split and merged by external programs, as long as the data stays in the correct order. It also implies that the contents of a single file can cross volume boundaries. Selective restores of files at known volume locations can be done by feeding only the needed volumes to .IR afio , provided that the .B \-k option is used. .PP The contents of hard linked files are (unless the .B \-l option is used) only stored once in the archive. The file headers for the second, third, and later occurrence of a hard linked file have no data after them. This makes selective restores of hard-liked files difficult: if later occurrences are to be restored correctly, the first occurrence always needs to be selected too. .PP .SH NOTES 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 A more elaborate case of the above is .I [user@]host[%rsh][=afio]:file where the optional .I user@ component specifies the user name on the remote host, the optional .I %rsh specifies the (local) name of the remote shell command to use, and the optional .I =afio specifies the name of the remote copy of the afio command. .TP .B o Anything else specifies a local file or device. An output file will be created if it does not already exist. .TP .B o When the .B \-s option is used to invoke multi-volume archive processing, any .B %V in the file/device name or command string is subsisuted by the current volume number, and any .B %S by the current volume size. Use .B %% to produce a single % character. .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 .SH CONTROL FILES .I Afio archives can contain so-called control files. Unlike normal archive entries, a control file in not unpacked to the filesystem. A control file has a .I label and some .IR data . When .I afio encounters a control file in the archive it is reading, it will feed the .I label and .I data to a so-called control script. The control script is supplied by the user. It can perform special actions based on the .I label and .I data it receives from .IR afio . .PP .B Control file labels. The control file mechanism can be used for many things. Examples are putting archive descriptions at the beginning of the archive and embedding lists of files to move before unpacking the rest or the archive. .PP To distinguish between different uses, the .I label of a control file should indicate the program that made the control file and the purpose of the control file data. It should have the form .PP .nf programname.kindofdata .fi .PP where .I programname is the name of the backup program that generated the control file, and .I kindofdata is the meaning of the control file data. Some examples are .PP .nf tbackup.movelist tbackup.updatescript blebberfiler.archivecontents backup_script_of_Joe_User.archivedescription .fi .PP The user-supplied control script should look at the label to decide what to do with the control data. This way, control files with unknown labels can be ignored, and afio archives maintain some degree of portability between different programs that restore or index them. .PP Control file labels that are intended to be portable between different backup programs could be defined in the future. .PP .B Making control files. When making an archive, afio reads a stream containing the names of the files (directories, ...) to put in the archive. This stream may also contain `control file generators', which are lines with the following format: .PP .nf //--sourcename label .fi .PP Here, the //-- sequence signals that a control file is to be made, .I sourcename is the path to a file containing the control file data, and .I label is the control file label. The .I sourcename must be a regular file or a symlink to a regular file. .PP A control file will show up as .PP .nf //--CONTROL_FILE/label .fi .PP in an archive listing, where .I label is the control file label. .PP .B Control scripts. A control script is supplied to afio with the .PP .BI " \-D " controlscript .PP command line option. The .I controlscript must be an executable program. The script is run whenever .I afio encounters a control file while doing a .B \-i \-t or .B \-r operation. Afio will supply the control file .I label as an argument to the script. The script should read the control file .I data from its standard input. If the script exits with a non-zero exit status, .I afio will issue a warning message. .PP If a control file is encountered and no .B \-D option is given, .I afio will issue a warning message. To suppress the warning message and ignore all control scripts, .B \-D "" can be used. .PP An example of a control script is .PP .nf #!/bin/sh if [ $1 = "afio_example.headertext" ]; then #the headertext control file is supposed to be packed as the first #entry of the archive echo Archive header: cat - echo Unpack this archive? y/n #stdout is still connected to the tty, read the reply from stdout read yn <&1 if [ "$yn" = n ]; then #abort kill $PPID fi else echo Ignoring unknown control file. cat - >/dev/null fi .fi .PP .I Afio never compresses the control file data when storing it in an archive, even when the .B \-Z option is used. When a control file is encountered by .I cpio(1) or an .I afio with a version number below 2.4.1, the data will be unpacked to the filesystem, and named .I CONTROL_FILE/label where .I label is the control file label. .SH BUGS There are too many options. .PP Restricts pathnames to 1023 characters, and 255 meaningful elements (where each element is a pathname component separated by a /). .PP Does not use the same default block size as .IR tar (1). .IR tar (1) uses 10 KB, .I afio uses 5 KB by default. Some tape drives only work with a 10 KB block size, in that case the .I afio option .B \-b 10k is needed to make the tape work. .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 The .I afio code for handling floppies .RB ( \-F and .BR \-f " and " \-K options) has buggy error handling. .I afio does not allow one to retry a failed floppy write on a different floppy, and it cannot recover from a verify error. If the floppy handling code is used and write or verify errors do occur, it is best to restart .I afio completely. Making backups to floppies should really be done with a more specialised backup program that wraps .IR afio . .PP The Linux floppy drivers below kernel version 1.1.54 do not allow .I afio to find out about floppy write errors while writing. If you are running a kernel below 1.1.54, .I afio will happily fail to write to (say) a write protected disk and not report anything wrong! The only way to find out about write errors in this case is by watching the kernel messages, or by switching on the verify .RB ( \-K ) option. .PP The remote archive facilites (host:/file archive names) have not been exhaustively tested. These facilities have seen a lot of real-life use though. However, there may be bugs in the code for error handling and error reporting with remote archives. .PP An archive created with a command like .I "'find /usr/src/linux \-print | afio \-o ...'" will not contain the ownership and permissions of the .I /usr and .I /usr/src directories. If these directories are missing when restoring the archive, .I afio will recreate them with some default ownership and permissions. .PP Afio can not restore time stamps on symlinks. Also, on operating systems without an .IR lchown (2) system call, afio can not restore owner/group information on symlinks. (Linux has lchown since kernel version 2.1.86.) .PP Afio tries to restore modification time stamps of directories in the archive correctly. However, if it exits prematurely, then the modification times will not be restored correctly. .PP A restore using decompression will fail if the .I gzip binary used by .I afio is overwritten, by .I afio or by another program, during the restore. The restore will also fail if any shared libraries needed to start .I gzip are overwritten during the restore. .I afio should not normally be used to overwrite the system files on a running system. If it is used in this way, a flag like .I \-Y /bin/gzip can often be added to prevent failure. .PP The .B \-r option verifies the file contents of the files in the archive against the files on the filesystem, but does not cross-check details like permission bits on files, nor does it cross-check that archived directories or other non-file entities still exist on the filesystem. .PP There are several problems with archiving hard links. 1) Due to internal limitations, files with hard links cannot be stored in compressed form, unless the .B \-l or .B \-U options are used which force each hard linked file to be stored separately. 2) Archives which contain hard links and which were made with older (pre-2.4.8) versions of .I afio or with .I cpio can not always be correctly unpacked. This is really a problem in the archives and not in the current version of .IR afio . The risk of incorrect unpacking will be greater if the number of files or hard links in the archives is larger. 3) In a selective restore, if the selection predicates do not select the first copy of a file with archive-internal hard links, then all subsequent copies, if selected, will not be correctly restored. 4) Unless the .B \-4 option is used, the inode number fields in the archive headers for files with hard links of the archive will sometimes not contain the actual (least significant 16 bits of) the inode number of the original file. .PP Some Linux kernels no not allow one to create a hard link to a symbolic link. .I afio will try to re-create such hard links when unpacking an archive, but might fail due to kernel restrictions. .PP Due to internal limitations of .IR afio , the use of the .B \-U option forces the writing of file content with each hard linked file, rather than only once for every set of hard linked files. .PP When it is run without super-user privileges, .I afio is not able to unpack a file into a directory for which it has no write permissions, even if it just created that directory itself. This can be a problem when trying to restore directory structures created by some source code control tools like RCS. .PP When block or character device files are packed into an archive on one operating system (e.g. Linux) and unpacked on another operating system, which uses different sizes for the major and minor device number data types (e.g. Solaris), the major and minor numbers of the device files will not be restored correctly. This can be a problem if the operating systems share a cross-mounted filesystem. A workaround is to use .IR tar (1) for the device files. .PP .SH "SECURITY CONSIDERATIONS" Security considerations arise when unpacking archives from untrusted sources. The recommended technique is to unpack such archives into a temporary, empty destination directory, unaccessible to other system users, while running .I afio as a normal user, so without superuser privileges. As of version 2.5.2, .I afio has security measures, enabled by default, to guard against a class of attacks where specially constructed path names and/or symlink destinations in an archive cause .I afio to to create or modify system or user files outside of the destination directory. See the .B \-8 option for a more detailed description of these attacks and measures. .PP On UNIX multi-user systems with untrusted users, there are several known attacks where, unless the system administrator is very careful, end users can exploit backup and restore activites on the user filesystems to subvert data or operational security. See e.g. the security section of the GNU .I tar manual, at .EX http://www.gnu.org/software/tar/manual/html_node/Security.html .EE for a description of some issues and precautions. .PP An archive from an untrusted source could in theory contain mal-formatted data designed to implement a buffer overflow attack when .I afio reads the archive during a .B \-t or .B \-i operation. While the .I afio archive procesing code is fairly robust, and has passed some automated code checking tools, no formal review has been done to guarantee the absense of buffer overflow attack vulnerabilities. Running .I afio in a sandboxed virtual machine or from inside .IR chroot (8) will improve the security of handling archives from untrusted sources, but the most secure option is to never touch such archives at all. .PP .SH "EXAMPLES" Create an archive with compressed files: .br .I "find .... | afio \-o \-v \-Z /dev/fd0H1440" .PP Install (unpack) an archive with compressed files: .br .I "afio \-i \-v \-Z archive" .PP Install (unpack) an archive with compressed files, protecting newer existing files: .br .I "afio \-i \-v \-Z \-n archive" .PP Create an archive with compressed files on floppy disks: .br .I "find .... | afio \-o \-v \-s 1440k \-F \-Z /dev/fd0H1440" .PP Create an archive with all file contents encrypted by pgp: .br .I "export PGPPASSFD=3" .br .I "find .... | afio \-ovz \-Z \-U \-P pgp \-Q \-fc \-Q +verbose=0 \-3 3 archive 3 rather than (4.2BSD). * o Define VOIDFIX to allow pointers to functions returning void (non-PCC). * o Define CTC3B2 to support AT&T 3B2 streaming cartridge tape. * o Define HAVEFCNTL if you have * o Define MYTEMPNAM if you don't have tempnam() * o Define UNIXPC if you are on a 3b1, 7300, etc. * o Define HAVEMEMCMP if you have memcmp otherwise assumes bcmp * o Define DEFFMTCMD to being how to format the media you use the most. * o Define LONGZFILE if you want .Z to be tagged on the end of a 14 char * * BUGS: * See the manpage. * * Added by Jeff Buhrt: * Floppy Verify/format/restart output in the middle of a set, * compress files on output, extended error messages and logging * * Added by Dave Gymer: * Lotsa bugfixes, Linux support, recognition of .Z files in an archive * that were compressed already (and shouldn't be uncompressed). * Displays compression ratios. * * See the HISTORY file for more revision info. */ #include #include #ifdef sun #include #include #include #include #include #define linux_tstamp 1 #if 0 /* fix SunOS errno.h not declaring what the manpage says it declares bogosity. */ extern int sys_nerr; extern char *sys_errlist[]; #endif #endif #ifdef hpux #if 0 /* Fix that HPUX dosent have sys_nerr or sys_errlist Added by Daniel Andersson, daniel.andersson@sto.sema.se */ extern int sys_nerr; extern char *sys_errlist[]; #endif #endif #include #include #include #ifdef __CYGWIN32__ #include #else #include #endif #include #include #include #include #include #include "patchlevel.h" #ifdef linux #define linux_tstamp 1 #include #include /* for flushing floppy cache */ #include #endif /* compatibility fixes for IRIX native c compiler. */ #ifdef irix_cc_compatibility #define linux_tstamp 1 #include #endif #ifndef major #ifdef sun #include #else #include #endif #endif /* major */ #include "afio.h" /* define 1 to enable file descriptor leak debugging code */ #define FDDEBUG 0 /* * Static variables. */ STATIC short Fflag; /* * floppy flag (write when buf full) * set -sdisk_size as well */ 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 aflag; /* Preserve atime (while munging ctime) */ STATIC short dflag; /* Don't create missing directories */ STATIC short fflag; /* Fork before writing to archive */ STATIC short gflag; /* Change to input file directories */ short hflag; /* Follow symbolic links */ STATIC short jflag; /* Don't generate sparse filesystem blocks */ STATIC short kflag; /* Skip initial junk to find a header */ 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 flag0; /* Input names terminated with '\0' */ STATIC short Jflag=0; /* Keep going after archive write error */ 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 ulonglong 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 char proc_arname[PATHSIZE+10];/* %-processed archive name */ STATIC ulonglong aruntil=0; /* Volume size limit */ STATIC int sflagused=0; /* flag if -s flag was used */ STATIC int roundaruntil=1; /* Round aruntil to multiple of arbsize? */ STATIC ulonglong maxsizetocompress=200L*1024L*1024L; /* ==0, then no max */ STATIC int askfornext=0; /* Ask for next disk on input eof? */ STATIC uint arvolume = 1; /* Volume number */ STATIC off_t 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 gid_t gid; /* Group ID */ STATIC Link *linkbase[1024];/* Unresolved link information */ STATIC unsigned char ino16bitused[256*256/8]; /* bitmap of 16 bit inode numbers */ STATIC ino_t freshino = 0; /* counter to make fresh inos */ 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 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 ulonglong total; /* Total number of bytes transferred */ STATIC int ttyf = -1; /* For interactive queries (yuk) */ STATIC uid_t uid; /* User ID */ int uncompressrun = 0; /* is uncompress running? its pid if so */ char uncompto[PATHSIZE]; /* name we uncompressed to */ STATIC int anycorrupt = 0; /* if any data is corrupted */ STATIC int warnings = 0; /* if any data is corrupted */ int printbytepos = 0; /* print position of each file in archive */ ulonglong bytepos; /* position of first byte of current file */ STATIC char *controlscript=NULL; /* script to pipe control files to */ STATIC char *promptscript=NULL; /* name of disk-change script */ STATIC ushort extfmt = 0; /* Use extended ASCII format */ STATIC ushort cpiocompat = 0; /* Never use large ASCII format headers */ STATIC ushort cpiowarned = 0; /* warning given? */ STATIC char *email=NULL; /* email address to notify of volume change requests */ STATIC int rewindfd = -1; /* file descriptor to rewind before (un)compress invocations */ STATIC char *ignorewarnings="mc"; /* data for -1 option */ STATIC char *aruntil_string=NULL; /* -s option string given by user */ STATIC int extcasesens=0; /* Case sensitive matching in -E option? */ STATIC Dir *DirP=NULL; /* list of directories with their saved timestamps */ STATIC char firstfilename[PATHSIZE]=""; /* for temp storage during -o */ STATIC int useoutmodetoc=0; /* used in tocentry() */ STATIC short noglob=0; /* disable globbing */ STATIC int allowinsecurepaths=0; STATIC int allowinsecuresymlinks=0; STATIC int nosymlinks=0; int main (int ac, char **av) { reg int c; reg uint group = 1; VOIDFN (*fn)(char **) = NULL; time_t timedone; auto char remote[PATHSIZE]; char *exitmsg; int status; Stat tmpstat; #if 0 printf("sizeof off_t = %d\n",sizeof(off_t)); printf("sizeof size_t = %d\n",sizeof(size_t)); #endif if ((myname = strrchr (*av, '/'))) ++myname; else myname = *av; mask = umask (0); uid = getuid (); gid = getgid (); if (uid == 0) xflag = 1; /* ignore SIGPIPE to deal with gzip -d exiting prematurely */ VOID signal (SIGPIPE, SIG_IGN); /* All letters have been used as options, now eating into the numbers.... */ while ((c = options (ac, av, "aioprtIOVCb:c:de:fghjklmns:uvxXy:Y:zFKZL:R:qAE:G:M:w:W:T:SBD:P:Q:U4JH:0@:N:3:1:92:56:78:")) ) { switch (c) { case 'r': if (fn) usage (); fn = readcheck; allowinsecurepaths=allowinsecuresymlinks=1; break; case 'i': if (fn) usage (); fn = in; break; case 'o': if (fn) usage (); fn = out; useoutmodetoc=1; break; case 'p': if (fn) usage (); fn = pass; break; case 't': if (fn) usage (); fn = toc; allowinsecurepaths=allowinsecuresymlinks=1; break; case 'I': if (fn) usage (); fn = copyin; break; case 'O': if (fn) usage (); fn = copyout; break; case 'V': VOID printf ("%s: Version %s dated %s\n", myname, VERSION, DATE); exit (0); #ifdef CTC3B2 case 'C': ++Cflag; arbsize = 31 * 512; group = 10; aruntil = 1469 * 31 * 512; break; #endif /* CTC3B2 */ case 'a': ++aflag; break; case 'b': if ((arbsize = (uint) optsize (optarg)) == 0) fatal (optarg, "Bad block size"); break; case 'c': if ((group = (uint) optsize (optarg)) == 0) fatal (optarg, "Bad buffer count"); break; case 'd': ++dflag; break; case 'e': arpad = (uint) optsize (optarg); break; case 'f': ++fflag; break; case 'g': ++gflag; break; case 'h': ++hflag; break; case 'j': ++jflag; break; case 'k': ++kflag; break; case 'l': ++lflag; break; case 'm': ++mflag; break; case 'n': ++nflag; break; case 's': sflagused = 1; /* Do a 'dry run' to check all values for syntax errors */ aruntil_string = strdup(optarg); while(aruntil_string) update_aruntil(); /* Now the real initialisation */ aruntil_string = optarg; update_aruntil(); if (aruntil == 0) askfornext = 1; break; case 'F': ++Fflag; break; case 'Z': ++Zflag; break; case 'K': ++verifyflag; break; case 'u': ++uflag; break; case 'v': ++vflag; break; case 'x': xflag = 1; break; case 'X': xflag = 0; break; case 'y': nameadd (optarg, PATTYPE_MATCH); break; case 'Y': nameadd (optarg, PATTYPE_NOMATCH); break; case 'z': ++zflag; break; case 'L': if ((logfile = fopen (optarg, "a")) == (FILE *) 0) { fprintf (stderr, "Can't open %s to append, get help\n", optarg); exit (1); } break; case 'R': formatcmd = optarg; break; case 'q': hidequit = TRUE; break; case 'A': abspaths = TRUE; break; case 'E': if(strcmp(optarg,"CS")==0) extcasesens=1; else if(strcmp(optarg,"CI")==0) extcasesens=0; else if(!readcompexts(optarg)) { exit (1); } break; case 'G': gzipfactor=(uint) optsize(optarg); if((gzipfactor <1) || (gzipfactor >9)) { fprintf (stderr, "%s: Illegal gzip speed factor (Must be 1--9)\n", optarg); exit (1); } break; case 'M': maxmem=(off_t) optsize(optarg); break; case 'T': compthreshold=(uint) optsize(optarg); break; case 'w': if(!nameaddfile(optarg,PATTYPE_MATCH,flag0)) { fprintf (stderr, "Can't read configuration file %s\n", optarg); exit (1); } break; case 'W': if(!nameaddfile(optarg,PATTYPE_NOMATCH,flag0)) { fprintf (stderr, "Can't read configuration file %s\n", optarg); exit (1); } break; case '6': if(!nameaddfile(optarg,PATTYPE_EXT,0)) { fprintf (stderr, "Can't read configuration file %s\n", optarg); exit (1); } break; case 'S': ignoreslash=0; break; case 'B': printbytepos=1; break; case 'D': controlscript=optarg; break; case 'Q': compressargs=1; add_arg(optarg); break; case 'P': compressprog=optarg; break; case 'U': /* compress All files */ forceZflag=1; lflag=1; /* Due to internal limitations we need to set this */ break; case '0': flag0 = 1-flag0; break; case '4': /* Use extended ASCII format */ extfmt = 1; break; case '5': cpiocompat = 1; break; case 'J': Jflag = 1; break; case 'H': promptscript=optarg; /* name or definition of the promptscript */ break; case '@': email=optarg; break; case '3': rewindfd=(int)optsize(optarg); break; case '1': ignorewarnings=optarg; break; case '9': roundaruntil=0; break; case '2': maxsizetocompress=optsize(optarg); break; case '7': noglob = 1-noglob; break; case '8': if (strcmp(optarg, "allowinsecurepaths") == 0) allowinsecurepaths=1; else if(strcmp(optarg, "allowinsecuresymlinks") == 0) allowinsecuresymlinks=1; else if (strcmp(optarg, "nosymlinks") == 0) nosymlinks=1; else fatal (optarg, "unknown value for -8 option"); break; default: usage (); } } if (fn == NULL || av[optind] == NULL) usage (); if (extfmt && cpiocompat) { warn(av[0], "Options -4 and -5 cannot be specified at the same time."); usage (); } if(fflag && aruntil==0) { fprintf (stderr,"Fatal: must supply nonzero -s [volsize] to use -f\n"); usage(); } if(compressprog && (rewindfd==-1)) if(strstr(compressprog,"pgp") || strstr(compressprog,"gpg")) { fatal(compressprog,"Must use -3 flag if -P program matches 'pgp' or 'gpg', see the afio manual page."); } if(!compressprog) compressprog = PRG_COMPRESS; compress_arg_list[0] = compressprog; if(maxmem/1024L/1024L > (1024+512)) { /* afio uses uints and ints and size_t at several places that may make it (on architectures with 32 bit ints and size_t) fail to do the right calculations when the buffer size is >2GB. This 1.5 GB limit is somewhat conservative, but better safe than sorry. If you have an os/compile environment witrh 64 bit size_t and int, things might be OK if you disable this test, but I have not tested it. If you disable here see also the code in memwrite(). */ fatal (arspec, "In-memory compression buffer size above 1.5GB not supported"); } if (Fflag) { if ((buflen = (off_t) aruntil) == 0) usage (); } else buflen = (off_t)arbsize * (off_t)group; if( roundaruntil ) { /* round aruntil down to a multiple of arbsize: some devices (like ftape) puke on a smaller-than-blocksize last write to the volume */ aruntil = ( aruntil / (ulonglong) arbsize ); aruntil = aruntil * arbsize; } if (aruntil && (aruntil < arbsize)) { #ifdef hpux /* HPUX gcc dosent like the (ulong) fixed by Daniel Andersson daniel.andersson@sto.sema.se */ fprintf (stderr, "Media size %ld is less than block size %d\n", aruntil, arbsize); #else fprintf (stderr, "Media size %ld is less than block size %d\n", (unsigned long) aruntil, arbsize); #endif usage (); } if (arpad == 0) arpad = arbsize; if (fn != pass) { reg char *colon; reg char *perc; reg char *equal; char *host; reg int isoutput = (fn == out || fn == copyout); arname = strcpy (arspec, av[optind++]); if ((colon = strchr (arspec, ':'))) { *colon++ = '\0'; if ((perc = strchr (arspec, '%'))) *perc++ = '\0'; if ((equal = strchr ((perc ? perc : arspec), '='))) *equal++ = '\0'; if ((host=strchr(arspec,'@'))) *host++ = 0; VOID sprintf (arname = remote, "!%s %s%s %s '%s -%c -b %u -c %u %s'", perc ? perc : "rsh", host ? "-l ":"", host ? arspec : "", host ? host : arspec, equal ? equal : myname, isoutput ? 'O' : 'I', arbsize, group, colon); if( host ) *--host = '@'; if (equal) *--equal = '='; if (perc) *--perc = '%'; *--colon = ':'; } if (gflag && *arname != '/' && *arname != '!') fatal (arspec, "Relative pathname"); VOID signal (SIGINT, goodbye); if(buflen/1024L/1024L > (1024+512)) { /* afio uses uints and ints at several places that make it (on architectures with 32 bit ints and size_t) fail to do the right calculations when the buffer size is >2GB. This 1.5GB limit is somewhat conservative, but better safe than sorry. If you have an os/compile environment witrh 64 bit size_t and int, things might be OK if you disable this test, but I have not tested it. */ fatal (arspec, "In-memory I/O buffer size above 1.5GB not supported"); } /* * +BLOCK is added to make sure we don't overrun buffer on a * read (internal read(1) length is thus met) */ if ((buffer = bufidx = bufend = malloc ((size_t)buflen + BLOCK)) == NULL) fatal (arspec, "Cannot allocate enough memory for I/O buffer"); /* if in -o mode, do a sanity check on the input now. This should prevent deletion of the archive file contents in most cases when people who want to do afio -i accidentally type afio -o. If not reading from a terminal, ignore check -- this allows backup scripts to create empty archives. */ if((fn == out)&&isatty(STDIN)) { while(1) { if (lineget (stdin, firstfilename, PATHSIZE) < 0) { fatal("-o (write archive) mode", "could not read any file names from stdin"); } if(strncmp(firstfilename,"//--",4)==0) break; if (STAT (firstfilename, &tmpstat) < 0) { VOID warn (firstfilename, syserr ()); VOID warn (firstfilename, "-o (write archive) mode needs file names on stdin"); } else break; } /* OK, if there was a good file name, then now firstfilename[] has a non-NULL string, this indicates to the openin function that it needs to use that string, not read from stdin */ } /* * open the archive file * * open a floppy at the last moment (if output), otherwise now * note we set arleft prematurely so we don't have to open the * disk now */ if (!Fflag || !isoutput) { if (nextopen (isoutput ? O_WRONLY : O_RDONLY) < 0) goodbye (1); } else arleft = aruntil; } timenow = time ((time_t *) NULL); (*fn) (av + optind); timedone = time ((time_t *) NULL); if (uflag) linkleft (); if (vflag || (fn == toc)) fflush(stdout); if(cpiowarned) { /* repeat warning message at end */ VOID warn_nocount(arspec,"Warning: Created archive is not fully compatible with cpio or afio versions 2.4.7 and lower."); VOID warn_nocount(arspec,"See the ARCHIVE PORTABILITY section of the manpage."); } exitmsg="The operation was successful."; if(warnings) { exitmsg=malloc(80); if(exitmsg==NULL) exitmsg="The operation HAD WARNINGS ABOUT ERRORS."; else sprintf(exitmsg,"The operation HAD %d WARNING%s ABOUT ERRORS.",warnings,warnings==1?"":"S"); } if(anycorrupt) exitmsg="The operation FAILED."; if (zflag) { reg FILE *stream; stream = fn == toc || fn == copyin || arfd == STDOUT ? stderr : stdout; VOID fprintf (stream, "%s: ", myname); prsize (stream, total); VOID fprintf (stream, " bytes %s in %lu seconds. %s\n", fn == pass ? "transferred" : fn == out || fn == copyout ? "written" : "read", timedone - timenow - timewait, exitmsg); } if (logfile != (FILE *) 0) { VOID fprintf (logfile, "%s: Final count: ", myname); prsize (logfile, total); VOID fprintf (logfile, " bytes %s in %lu seconds (+waited %lu seconds for disk swapping (%u disks)) finished at %s", (fn == pass ? "transferred" : (fn == out || fn == copyout ? "written" : "read")), timedone - timenow - timewait, timewait, arvolume, ctime (&timedone)); VOID fprintf (logfile,"%s\n",exitmsg); } nextclos (); /* Added to lower chance of race condition (?) in rsh processing when reading remote files */ if(fn==copyin) { fflush(stdout); fclose(stdout); sleep(2); } if (email) mail(email,-1,arspec); status=0; if(anycorrupt) status=1; if(warnings) if(index(ignorewarnings,(int)'a')==NULL) status=1; goodbye (status); /* NOTREACHED */ return 0; /* silence gcc -Wall */ } /* * update_aruntil() * * Sets the next aruntil-value out of the options-list */ void update_aruntil() { char *next_aruntil_string = aruntil_string; if(aruntil_string) { aruntil_string = strchr (aruntil_string, ','); if(aruntil_string) *aruntil_string++='\0'; aruntil = optsize (next_aruntil_string); } } /* * mail() * * Mail tape change message */ void mail(char *who,int vol,char *archive) { FILE *fp; char cmd[1024]; char hostname[256]; gethostname(hostname,sizeof(hostname)); sprintf(cmd,"sendmail %s",who); fp = popen(cmd,"w"); if (fp == NULL ) { perror(cmd); return; } fprintf(fp,"From: Afio archiver\n"); fprintf(fp,"Subject: %s %s: %s\n\n",hostname,archive,vol<0?"operation complete":"volume change needed"); fprintf(fp,"Hostname: %s\n",hostname); fprintf(fp,"Archive : %s\n\n",archive); if(vol>=0) fprintf(fp,"Need change to volume #%d.\n\n",vol); else fprintf(fp,"Operation complete.\n\n"); pclose(fp); } /* * copyin() * * Copy directly from the archive to the standard output. */ STATIC VOIDFN copyin (char **av) { reg ssize_t got; reg ssize_t have; if (*av) fatal (*av, "Extraneous argument"); while (!areof || askfornext) { VOID infill (); while ((have = bufend - bufidx)) if ((got = writeall (STDOUT, bufidx, have)) < 0) fatal ("", syserr ()); else { total+=have; if (got > 0) bufidx += got; else return; } } } /* * copyout() * * Copy directly from the standard input to the archive. */ STATIC VOIDFN copyout (char **av) { reg int got; reg uint want; if (*av) fatal (*av, "Extraneous argument"); for (;;) { while ((want = bufend - bufidx) == 0) outflush (NOTDONE); if ((got = read (STDIN, bufidx, want)) < 0) fatal ("", syserr ()); else if (got == 0) break; else { bufidx += got; total += got; /* actually a bit too early for bytes written count */ } } outflush (DONE); if (fflag) outwait (); } /* * dirchg() * * Change to the directory containing a given file. */ STATIC int dirchg (char *name, char *local) { reg char *last; reg int len; auto char dir[PATHSIZE]; if (*name != '/') return (warn (name, "Relative pathname")); for (last = name + strlen (name); last[-1] != '/'; --last) ; len = last - name; strncpy (dir, name, (size_t)len)[len] = '\0'; VOID strcpy (local, *last ? last : "."); if (strcmp (dir, pwd) == 0) return (0); if (chdir (dir) < 0) return (warn (name, syserr ())); VOID strcpy (pwd, dir); return (0); } /* * dirmake() * * Make a directory. Returns zero if successful, -1 otherwise. */ STATIC int dirmake (char *name, Stat *asb) { if (mkdir (name, asb->sb_mode & S_IPOPN) < 0) return (-1); /* First do the chown, then the chmod, because the chown may clear the suid/sgid bits we want to set. */ if (xflag) VOID chown (name, (uid == 0 ? asb->sb_uid : uid), asb->sb_gid); if (asb->sb_mode & S_IPEXE) VOID chmod (name, asb->sb_mode & S_IPERM); return (0); } /* * dirneed() * * Recursively create missing directories (with the same permissions * as their first existing parent). Temporarily modifies the 'name' * argument string. Returns zero if successful, -1 otherwise. */ STATIC int dirneed (char *name) { reg char *cp; reg char *last; reg int ok; static Stat sb; last = NULL; for (cp = name; *cp;) if (*cp++ == '/') last = cp; if (last == NULL) return (STAT (".", &sb)); *--last = '\0'; ok = STAT (*name ? name : "/", &sb) == 0 ? ((sb.sb_mode & S_IFMT) == S_IFDIR) : (!dflag && dirneed (name) == 0 && dirmake (name, &sb) == 0); *last = '/'; return (ok ? 0 : -1); } /* * fatal() * * Print fatal message and exit. */ STATIC void fatal (char *what, char *why) { /* print position in archive if some data was transferred */ if(total>0) VOID warnarch ("Fatal error:",(off_t)0); VOID warn (what, why); goodbye (1); } /* * writeall() * * Write all bytes in buf or return -1. Used to fix invalid assumptions * about write() elsewhere. */ STATIC ssize_t writeall(int fd, const char *buf, size_t count) { ssize_t put; size_t totalput; totalput=0; while(totalput=1) { if(lseek(rewindfd,(off_t)0,SEEK_SET)<0) { fatal("-3 option",syserr()); } } } /* * savedirstamp() * * Remember necessary timestamps for a directory, * so they can be restored afterwards. */ STATIC void savedirstamp (char *name, time_t mtime) { Dir *dirp; /* Note that the cast below is necessary since memget() is pre-C89. */ if((dirp=(Dir *)memget(sizeof(Dir))) != NULL) { if ((dirp->d_name=memstr(name)) == NULL) { free(dirp); } else { dirp->d_mtime=mtime; dirp->d_forw=DirP; DirP=dirp; } } } /* * restoredirstamps() * * Restore timestamps for the saved list of directories. */ STATIC void restoredirstamps (void) { #ifdef linux_tstamp auto struct utimbuf tstamp; #else auto time_t tstamp[2]; #endif Dir *DirP_curr = DirP; /* keep DirP reachable */ Dir *DirP_forw; while(DirP_curr!=NULL) { #ifdef linux_tstamp tstamp.actime = DirP_curr->d_mtime; tstamp.modtime = DirP_curr->d_mtime; /* no error code checking on purpose */ VOID utime (DirP_curr->d_name, &tstamp); #else tstamp[0] = DirP_curr->d_mtime; tstamp[1] = DirP_curr->d_mtime; /* no error code checking on purpose */ VOID utime (DirP_curr->d_name, tstamp); #endif /* We don't call free because we are about to exit anyway. Not calling free should make things a bit more robust if the memory pool is corrupted due to a bug */ /* free(DirP_curr->d_name); */ DirP_forw=DirP_curr->d_forw; /* free(DirP_curr); */ DirP_curr=DirP_forw; } } /* * in() * * Read an archive. */ STATIC VOIDFN in (av) reg char **av; { auto Stat sb; auto char name[PATHSIZE]; int sel,sel2,res; if (*av) fatal (*av, "Extraneous argument"); name[0] = '\0'; while (inhead (name, &sb) == 0) { sel = (namecmp (name,&sb)); if (sel) { if (inskip (sb.sb_size) < 0) VOID warn (name, "Skipped file data is corrupt"); } else { if((sel2 = inentry (name, &sb)) != 0) VOID warn (name, "unpacking error"); else if (vflag) { /* we cast to double and print as floating point because %Lu printing is buggy above 4G (at least with my C library). */ if(printbytepos) fprintf(stderr,"%.0f ",(double)bytepos); if (*uncompto) res = fprintf (stderr, "%s -- uncompressed\n", uncompto); else res = fprintf (stderr, "%s -- okay\n", name); /* check for broken pipe on stderr */ if(res<0) { if(errno == EPIPE) fatal("", syserr()); } } } } restoredirstamps(); } /* * readcheck() * * Read an archive and check contents against existing files */ Stat atime_sb; /* set in openincheck */ int atime_sb_valid; /* set in openincheck */ STATIC VOIDFN readcheck (av) reg char **av; { auto Stat sb; auto char name[PATHSIZE]; auto char local[PATHSIZE]; int sel, res; #ifdef linux_tstamp auto struct utimbuf tstamp; #else auto time_t tstamp[2]; #endif if (*av) fatal (*av, "Extraneous argument"); name[0] = '\0'; while (inhead (name, &sb) == 0) { if ((sel = namecmp (name,&sb)) < 0) { if (inskip (sb.sb_size) < 0) VOID warn (name, "Skipped file data is corrupt"); continue; } if (vflag) { strcpy(local, name); tocentry (local, &sb); } atime_sb_valid=0; if ((res = incheckentry(name, &sb)) < 0) inskip (sb.sb_size); if(aflag && atime_sb_valid && ((sb.sb_mode & S_IFMT)==S_IFREG)) { /* reset access time, this distroys the ctime btw. */ #ifdef linux_tstamp tstamp.actime = atime_sb.sb_atime; tstamp.modtime = atime_sb.sb_mtime; VOID utime (name, &tstamp); #else tstamp[0] = atime_sb.sb_atime; tstamp[1] = atime_sb.sb_mtime; VOID utime (name, tstamp); #endif } } } /* * inalloc() * * Allocate input buffer space (which was previously indexed * by inavail()). */ STATIC void inalloc (uint len) { bufidx += len; total += len; } /* * inascii() * * Read an ASCII header. Returns zero if successful; * -1 otherwise. Assumes that the entire magic number * has been read. */ STATIC int inascii (magic, name, asb) reg char *magic; reg char *name; reg Stat *asb; { auto uint namelen; auto char header[H_STRLEN + 1]; PStat pasb; if (strncmp (magic, M_ASCII, M_STRLEN) != 0) return (-1); if (inread (header, H_STRLEN) < 0) return (warnarch ("Corrupt ASCII header", (off_t) H_STRLEN)); header[H_STRLEN] = '\0'; if (sscanf (header, PH_SCAN, &pasb.PSt_dev, &pasb.PSt_ino, &pasb.PSt_mode, &pasb.PSt_uid, &pasb.PSt_gid, &pasb.PSt_nlink, &pasb.PSt_rdev, &pasb.PSt_mtime, &namelen, &pasb.PSt_size) != H_COUNT) return (warnarch ("Bad ASCII header", (off_t) H_STRLEN)); /* now, we let the compiler cast the info to the right types (field sizes) */ asb->sb_dev = pasb.PSt_dev; asb->sb_ino = pasb.PSt_ino; asb->sb_mode = pasb.PSt_mode; asb->sb_uid = pasb.PSt_uid; asb->sb_gid = pasb.PSt_gid; asb->sb_nlink = pasb.PSt_nlink; asb->sb_rdev = pasb.PSt_rdev; asb->sb_mtime = pasb.PSt_mtime; asb->sb_size = pasb.PSt_size; if (namelen == 0 || namelen >= PATHSIZE) return (warnarch ("Bad ASCII pathname length", (off_t) H_STRLEN)); if (inread (name, namelen) < 0) return (warnarch ("Corrupt ASCII pathname", (off_t) namelen)); if (name[namelen - 1] != '\0') return (warnarch ("Bad ASCII pathname", (off_t) namelen)); return (0); } /* * inascii2() * * Read an ASCII header (new format). Returns zero if successful; * -1 otherwise. Assumes that the entire magic number * has been read. */ STATIC int inascii2 (char *magic, char *name, Stat *asb) { auto uint namelen; auto char header[H_STRLEN2 + 1]; PStat pasb; if (strncmp (magic, M_ASCII2, M_STRLEN) != 0) return (-1); if (inread (header, H_STRLEN2) < 0) return (warnarch ("Corrupt extended ASCII header", (off_t) H_STRLEN2)); header[H_STRLEN2] = '\0'; if (sscanf (header, PH_SCAN2, &pasb.PSt_dev, &pasb.PSt_ino, &pasb.PSt_mode, &pasb.PSt_uid, &pasb.PSt_gid, &pasb.PSt_nlink, &pasb.PSt_rdev, &pasb.PSt_mtime, &namelen, &pasb.PSt_size) != H_COUNT) return (warnarch ("Bad extended ASCII header", (off_t) H_STRLEN)); /* now, we let the compiler cast the info to the right types (field sizes) */ asb->sb_dev = pasb.PSt_dev; asb->sb_ino = pasb.PSt_ino; asb->sb_mode = pasb.PSt_mode; asb->sb_uid = pasb.PSt_uid; asb->sb_gid = pasb.PSt_gid; asb->sb_nlink = pasb.PSt_nlink; asb->sb_rdev = pasb.PSt_rdev; asb->sb_mtime = pasb.PSt_mtime; asb->sb_size = pasb.PSt_size; if (namelen == 0 || namelen >= PATHSIZE) return (warnarch ("Bad ASCII pathname length", (off_t) H_STRLEN)); if (inread (name, namelen) < 0) return (warnarch ("Corrupt ASCII pathname", (off_t) namelen)); if (name[namelen - 1] != '\0') return (warnarch ("Bad ASCII pathname", (off_t) namelen)); return (0); } /* * inascii3() * * Read an ASCII header (large format). Returns zero if successful; * -1 otherwise. Assumes that the entire magic number * has been read. */ STATIC int inascii3 (magic, name, asb) reg char *magic; reg char *name; reg Stat *asb; { auto uint namelen; auto char header[H_STRLEN3 + 1]; PHStat pasb; uint specialflags; uint extraheaderlen; if (strncmp (magic, M_ASCII3, M_STRLEN) != 0) return (-1); if (inread (header, H_STRLEN3) < 0) return (warnarch ("Corrupt extended ASCII3 header", (off_t) H_STRLEN3)); header[H_STRLEN3] = '\0'; if (sscanf (header, PH_SCAN3, &pasb.PSt_dev, &pasb.PSt_ino, &pasb.PSt_mode, &pasb.PSt_uid, &pasb.PSt_gid, &pasb.PSt_nlink, &pasb.PSt_rdev, &pasb.PSt_mtime, &namelen, &specialflags, &extraheaderlen, &pasb.PSt_size) != H_COUNT3) return (warnarch ("Bad extended ASCII3 header", (off_t) H_STRLEN)); /* now, we let the compiler cast the info to the right types (field sizes) */ asb->sb_dev = pasb.PSt_dev; asb->sb_ino = pasb.PSt_ino; asb->sb_mode = pasb.PSt_mode; asb->sb_uid = pasb.PSt_uid; asb->sb_gid = pasb.PSt_gid; asb->sb_nlink = pasb.PSt_nlink; asb->sb_rdev = pasb.PSt_rdev; asb->sb_mtime = pasb.PSt_mtime; asb->sb_size = pasb.PSt_size; if (namelen == 0 || namelen >= PATHSIZE) return (warnarch ("Bad ASCII pathname length", (off_t) H_STRLEN)); if (inread (name, namelen) < 0) return (warnarch ("Corrupt ASCII pathname", (off_t) namelen)); if (name[namelen - 1] != '\0') return (warnarch ("Bad ASCII pathname", (off_t) namelen)); if (inskip ((off_t)extraheaderlen) < 0) return (warnarch ("Corrupt header", (off_t) 0)); return (0); } /* * inavail() * * Index availible input data within the buffer. Stores a pointer * to the data and its length in given locations. Returns zero with * valid data, -1 if unreadable portions were replaced with nulls. */ STATIC int inavail (bufp, lenp) reg char **bufp; uint *lenp; { reg uint have; reg int corrupt = 0; while ((have = bufend - bufidx) == 0) corrupt |= infill (); *bufp = bufidx; *lenp = have; return (corrupt); } /* * inbinary() * * Read a binary header. Returns the number of trailing alignment * bytes to skip; -1 if unsuccessful. */ STATIC int inbinary (magic, name, asb) reg char *magic; reg char *name; reg Stat *asb; { reg uint namefull; auto Binary binary; if ((int) *((ushort *) magic) != M_BINARY) return (-1); memcpy ((char *) &binary, magic + sizeof (ushort), M_STRLEN - sizeof (ushort)); if (inread ((char *) &binary + M_STRLEN - sizeof (ushort), sizeof (binary) - (M_STRLEN - sizeof (ushort))) < 0) return (warnarch ("Corrupt binary header", (off_t) sizeof (binary) - (M_STRLEN - sizeof (ushort)))); asb->sb_dev = binary.b_dev; asb->sb_ino = binary.b_ino; asb->sb_mode = binary.b_mode; asb->sb_uid = binary.b_uid; asb->sb_gid = binary.b_gid; asb->sb_nlink = binary.b_nlink; asb->sb_rdev = binary.b_rdev; asb->sb_mtime = binary.b_mtime[0] << 16 | binary.b_mtime[1]; asb->sb_size = binary.b_size[0] << 16 | binary.b_size[1]; if (binary.b_name == 0 || binary.b_name >= PATHSIZE) return (warnarch ("Bad binary pathname length", (off_t) sizeof (binary) - (M_STRLEN - sizeof (ushort)))); if (inread (name, namefull = binary.b_name + binary.b_name % 2) < 0) return (warnarch ("Corrupt binary pathname", (off_t) namefull)); if (name[binary.b_name - 1] != '\0') return (warnarch ("Bad binary pathname", (off_t) namefull)); return (asb->sb_size % 2); } /* * indata() * * Install data from an archive. Returns given file descriptor. */ STATIC int indata (fd, size, name) int fd; reg off_t size; char *name; { reg size_t chunk; reg char *oops; reg ssize_t sparse; reg int corrupt; auto char *buf; auto uint avail; corrupt = sparse = 0; oops = NULL; while (size) { corrupt |= inavail (&buf, &avail); size -= (chunk = size < (off_t)avail ? (uint) size : avail); if (oops == NULL && (sparse = fswrite (fd, buf, chunk)) < 0) oops = syserr (); inalloc (chunk); } if (corrupt) VOID warn (name, "Corrupt archive data"); if (oops) VOID warn (name, oops); else if (sparse > 0 && (lseek (fd, (off_t) - 1, 1) < 0 || write (fd, "", 1) != 1)) VOID warn (name, syserr ()); return (fd); } /* * incheckdata() * * Check data from an archive. Returns given file descriptor. */ STATIC int incheckdata (int fd, off_t size, char *name, Stat *asb, int comp) { reg uint chunk; reg char *oops; reg int corrupt, warned = 0; auto char *buf; auto uint avail; auto int pfd[2], pfdc[2]; auto int pid, comppid; corrupt = 0; oops = NULL; if (comp) { /* if compressed, we process data with the following setup: - child 1 is the comparator - child 2 is the gunzip (gzip -c -d) - the main process reads data from the archive and writes it to child 2 to be uncompressed - child 2 writes the uncompressed data to child 1 - child 1 reads from the file in the filesystem and from child 2, compares, and exits nonzero if is a discrepancy. */ if (pipe(pfd) < 0) { perror("pipe"); exit(1); } switch ((pid = xfork("incheckdata, decompressing", NODIE))) { case -1: perror("fork"); exit(1); /* child 1 start ======================== */ case 0: /* child */ VOID close (pfd[1]); if (arfd != STDIN && arfd != STDOUT) VOID close (arfd); if (pipe(pfdc) < 0) { perror("pipe"); exit(1); } switch ((comppid = xfork ("incheckdata, decomp", NODIE))) { case -1: perror("fork"); exit(1); /* child 2 start ======================== */ case 0: /* child */ VOID close (pfdc[0]); VOID close (fileno (stdin)); VOID dup (pfd[0]); VOID close (pfd[0]); VOID close (fileno (stdout)); if (dup (pfdc[1]) < 0) exit(1); close(pfdc[1]); mayberewind(); if(compressargs) execvp (compressprog, compress_arg_list); else execlp (compressprog, compressprog, "-d", "-c", NULL); fprintf (stderr, "Could not uncompress, errno %d\n", errno); exit(1); break; /* child 2 end ======================== */ /* child 1 continued ======================== */ default: { char buff1[40960]; char buff2[40960]; size_t n; ssize_t n1, n2; close(pfdc[1]); n1 = n2 = 1; while ((n1 > 0) && (n2 > 0)) { n = sizeof(buff1); n1 = readall(fd, buff1, n); n2 = readall(pfdc[0], buff2, n); size -= n1; if (n1 < 0 || n2 < 0 || n1 != n2) { VOID warn_nocount (name, "File in archive has different length than file on filesystem"); corrupt = 1; break; } else { if (memcmp(buff1, buff2, (size_t)n1) != 0) { if (!warned++) VOID warn_nocount (name, "File in archive has different data than file on filesystem"); corrupt = 1; } } } if (corrupt) kill(comppid, SIGTERM); close(pfdc[0]); close(fd); if (xwait (comppid, "incheckentry xwait() gunzip", FALSE) != 0) { warn(name, "uncompressing failure"); corrupt = 1; } exit(corrupt ? 2 : 0); } } /* child 2 end ======================== */ default: /* main process continue ======================== */ close(pfd[0]); uncompressrun = pid; while (asb->sb_size) { corrupt |= inavail (&buf, &avail); if (corrupt) { break; } asb->sb_size -= (chunk = asb->sb_size < (off_t)avail ? (uint) asb->sb_size : avail); if (writeall (pfd[1], buf, chunk) < 0) oops = syserr(); inalloc(chunk); } close(pfd[1]); corrupt |= (xwait (pid, "incheckentry xwait()", FALSE) != 0); break; } } else { /* not compressed */ char buff1[40960]; ssize_t n1, n2; ssize_t n; while (asb->sb_size && !corrupt) { if (!(corrupt |= inavail (&buf, &avail))) { /* cast of asb->sb_size (which can be 8 byte) only happens when the value fits in size_t (which is usually 4 byte) */ n2 = asb->sb_size < (off_t)avail ? (size_t) asb->sb_size : avail; n = (n2 < (ssize_t)sizeof(buff1)) ? n2 : (ssize_t)sizeof(buff1); n1 = readall(fd, buff1, n); asb->sb_size -= n; if (n1 < 0 || n2 < 0 || n1 != n) { if(n1!=n) VOID warn_nocount (name, "File in archive has different length than file on filesystem"); corrupt = 1; } else { if( memcmp(buff1, buf, n) != 0 ) { VOID warn_nocount (name, "File in archive has different data than file on filesystem"); corrupt = 1; } } inalloc((uint)n); } } /* See if the file is _longer_ then our backup. */ if (read(fd, buff1, 1) > 0) { VOID warn_nocount (name, "File in archive is shorter than file on filesystem"); corrupt = 1; } } close(fd); if (oops) VOID warn (name, oops); if (corrupt) { /* file : Stat atime_sb archive : Stat *asb */ if ( (atime_sb.sb_mtime==asb->sb_mtime) && (atime_sb.sb_size==asb->sb_size)) { /* file has same mod time and length --> should have verified OK, so this is very probably a real error in the backup medium. */ VOID warn (name, "File data in archive is corrupt"); anycorrupt=1; } else { VOID warn (name, "Archive headers indicate that file on filesystem was modified during or after archive was made"); if(index(ignorewarnings,(int)'r')) { warn_nocount(name, "Not counting this as a verification error"); warnings--; } else anycorrupt=1; } return -1; } return 0; } /* * inentry() * * Install a single archive entry. Returns zero if successful, -1 otherwise. */ STATIC int inentry (name, asb) char *name; reg Stat *asb; { reg Link *linkp; reg int ifd; reg int ofd; #ifdef linux_tstamp auto struct utimbuf tstamp; #else auto time_t tstamp[2]; #endif int result; result=0; if ((ofd = openotty (name, asb, linkp = linkfrom (asb,1), 0, Zflag)) > 0) { if (asb->sb_size || linkp == NULL || linkp->l_size == 0) VOID close (indata (ofd, asb->sb_size, name)); else if ((ifd = open (linkp->l_path->p_name, O_RDONLY)) < 0) VOID warn (linkp->l_path->p_name, syserr ()); else { passdata (linkp->l_path->p_name, ifd, name, ofd); VOID close (ifd); VOID close (ofd); } /* safety */ if (uncompressrun) { result = xwait (uncompressrun, "inentry xwait()", TRUE); uncompressrun = 0; if(result!=0) result=-1; } } else if (ofd < 0) return (-1); else if (inskip (asb->sb_size) < 0) VOID warn (name, "Redundant file data is corrupt"); /* Cannot set utime on symlink (at least not under Linux) */ if((asb->sb_mode & S_IFMT) != S_IFLNK) { #ifdef linux_tstamp tstamp.actime = tstamp.modtime = mflag ? timenow : asb->sb_mtime; VOID utime (name, &tstamp); #else tstamp[0] = tstamp[1] = mflag ? timenow : asb->sb_mtime; VOID utime (name, tstamp); #endif } return (result); } /* * incheckentry() * * Check a single archive entry. Returns zero if successful, -1 otherwise. */ STATIC int incheckentry (name, asb) char *name; reg Stat *asb; { reg int ifd; auto int compression; if (ISCONTROL(asb)) { /* process control file */ if(inentry (name, asb) < 0) if (inskip (asb->sb_size) < 0) VOID warn (name, "Skipped file data is corrupt"); /* do not verify the control file, return success */ return(0); } uncompressrun = 0; /* The exact logic below here, and inside openincheck and incheckdata, is questionable -- it will not catch all faulty situations as being a definate error. It will work in 99.9% of the error cases though, so I am not touching it... -- KH */ if ((ifd = openincheck (name, asb, &compression, Zflag)) > 0) { if (asb->sb_size) return incheckdata(ifd, asb->sb_size, name, asb, compression); } else if (ifd < 0) /* some strangeness occured in in archive file */ { anycorrupt = -1; return (-1); } else if (inskip (asb->sb_size) < 0) VOID warn (name, "Redundant file data is corrupt"); if (ifd > 0) close(ifd); return (0); } /* * infill() * * Fill the archive buffer. Remembers mid-buffer read failures and * reports them the next time through. Replaces unreadable data with * null characters. Returns zero with valid data, -1 otherwise. */ STATIC int infill () { reg int got; static int failed; ulonglong ullreadsize; size_t readsize; bufend = bufidx = buffer; if (!failed) { if (areof) { if (total == 0) { fatal (arspec, "No input"); } else { if((aruntil!=0) || askfornext) { next (O_RDONLY, "Input EOF"); } else { fatal (arspec, "Premature input EOF"); } } } #if 0 fprintf(stderr,"aruntil=%lld arleft=%lld arbsize=%d\n",aruntil,arleft,arbsize); #endif if (aruntil && (arleft == 0)) next (O_RDONLY, "Input limit reached"); if(aruntil) ullreadsize=arleft; else ullreadsize=buffer + buflen - bufend; if(ullreadsize>arbsize) ullreadsize=arbsize; readsize=(size_t)ullreadsize; while (!failed && !areof && (aruntil == 0 || arleft >= (ulonglong)readsize) && (off_t)(buffer - bufend) + buflen >= (off_t)readsize) { if ((got = read (arfd, bufend, readsize)) > 0) { bufend += got; arleft -= got; } else if (got < 0) failed = warnarch (syserr (), (off_t) 0 - (bufend - bufidx)); else ++areof; } } if (failed && bufend == buffer) { failed = 0; for (got = 0; (uint)got < arbsize; ++got) *bufend++ = '\0'; return (-1); } return (0); } /* * fixinsecure() * * Rewrite .. subpath components in path names into XX, * making them more secure for restoring. For more info see the -8 option * in the manpage. */ STATIC int fixinsecure (name) char *name; { int changed=0; int l=strlen(name); int i; if(strcmp("..",name)==0) { changed=1; name[0]=name[1]='X'; } if(strncmp("../",name,3)==0) { changed=1; name[0]=name[1]='X'; } for(i=0; i=3) if(strncmp("/..",name+l-3,3)==0) { changed=1; name[l-2]=name[l-1]='X'; } return changed; } /* * inhead() * * Read a header. Quietly translates old-fashioned binary cpio headers * (and arranges to skip the possible alignment byte). Returns zero if * successful, -1 upon archive trailer. */ STATIC int inhead (name, asb) reg char *name; reg Stat *asb; { reg off_t skipped; auto char magic[M_STRLEN]; static int align; char *name2; int rewritten; #if FDDEBUG /* debug code added by KH. Are we leaking file descriptors? */ { int i; i=dup(0); close(i); fprintf(stderr,"%d",i); } #endif if (align > 0) VOID inskip ((off_t) align); align = 0; bytepos=total; /* position of current file */ for (;;) { VOID inread (magic, M_STRLEN); skipped = 0; while ( ((align = inascii (magic, name, asb)) < 0) && ((align = inascii2 (magic, name, asb)) < 0) && ((align = inascii3 (magic, name, asb)) < 0) && ((align = inbinary (magic, name, asb)) < 0) && ((align = inswab (magic, name, asb)) < 0)) { if (++skipped == 1) { if (!kflag && total - sizeof (magic) == 0) { warn(arspec,"Input does not start with valid archive header."); warn(arspec,"Use the -k option to start reading in the middle of an archive."); fatal (arspec, "Unrecognizable archive"); } VOID warnarch ("Bad magic number", (off_t) sizeof (magic)); if (name[0]) VOID warn (name, "May be corrupt"); } memcpy (magic, magic + 1, sizeof (magic) - 1); VOID inread (magic + sizeof (magic) - 1, 1); } if (skipped) { VOID warnarch ("Apparently resynchronized", (off_t) sizeof (magic)); VOID warn (name, "Continuing"); } if (strcmp (name, TRAILER) == 0) return (-1); if (nameopt (name) >= 0) break; VOID inskip (asb->sb_size + align); } #ifdef S_IFLNK if ((asb->sb_mode & S_IFMT) == S_IFLNK) { /* buffer overflow protection */ if((uint) asb->sb_size > sizeof(asb->sb_link)-1) { VOID warn (name, "Corrupt symbolic link"); return (inhead (name, asb)); } if (inread (asb->sb_link, (uint) asb->sb_size) < 0) { VOID warn (name, "Corrupt symbolic link"); return (inhead (name, asb)); } asb->sb_link[asb->sb_size] = '\0'; asb->sb_size = 0; rewritten=0; if(!allowinsecuresymlinks) { if(asb->sb_link[0]=='/') { asb->sb_link[0]='X'; rewritten=1; } if(fixinsecure (asb->sb_link)) rewritten=1; if(rewritten) { VOID warn_nocount (name,"potentially insecure symlink destination path, rewriting:");; VOID warn(asb->sb_link,"is the rewritten destination value used"); if(index(ignorewarnings,(int)'s')) warnings--; } } } #endif /* S_IFLNK */ if ((name[0] == '/') && !abspaths) { /* if there was more than 1 leading /, nameopt() has already removed all leading / but one */ if (name[1]) { name2=name; while ((name2[0] = name2[1])) ++name2; } else { name[0] = '.'; } } if(!allowinsecurepaths) { if(fixinsecure(name)) { VOID warn (name,"potentially insecure destination path rewritten"); if(index(ignorewarnings,(int)'s')) warnings--; } } asb->sb_atime = asb->sb_ctime = asb->sb_mtime; return (0); } /* * inread() * * Read a given number of characters from the input archive. Returns * zero with valid data, -1 if unreadable portions were replaced by * null characters. */ STATIC int inread (dst, len) reg char *dst; uint len; { reg uint have; reg uint want; reg int corrupt = 0; char *endx = dst + len; while ((want = endx - dst)) { while ((have = bufend - bufidx) == 0) corrupt |= infill (); if (have > want) have = want; memcpy (dst, bufidx, have); bufidx += have; dst += have; total += have; } return (corrupt); } /* * inskip() * * Skip input archive data. Returns zero under normal circumstances, * -1 if unreadable data is encountered. */ STATIC int inskip (len) reg off_t len; { reg uint chunk; reg int corrupt = 0; while (len) { while ((chunk = bufend - bufidx) == 0) corrupt |= infill (); if ((off_t)chunk > len) chunk = len; bufidx += chunk; len -= chunk; total += chunk; } return (corrupt); } /* * inswab() * * Read a reversed byte order binary header. Returns the number * of trailing alignment bytes to skip; -1 if unsuccessful. */ STATIC int inswab (magic, name, asb) reg char *magic; reg char *name; reg Stat *asb; { reg ushort namesize; reg uint namefull; auto Binary binary; if ((int) *((ushort *) magic) != swab (M_BINARY)) return (-1); memcpy ((char *) &binary, magic + sizeof (ushort), M_STRLEN - sizeof (ushort)); if (inread ((char *) &binary + M_STRLEN - sizeof (ushort), sizeof (binary) - (M_STRLEN - sizeof (ushort))) < 0) return (warnarch ("Corrupt swapped header", (off_t) sizeof (binary) - (M_STRLEN - sizeof (ushort)))); asb->sb_dev = (dev_t) swab (binary.b_dev); asb->sb_ino = (ino_t) swab (binary.b_ino); asb->sb_mode = swab (binary.b_mode); asb->sb_uid = swab (binary.b_uid); asb->sb_gid = swab (binary.b_gid); asb->sb_nlink = swab (binary.b_nlink); asb->sb_rdev = (dev_t) swab (binary.b_rdev); asb->sb_mtime = swab (binary.b_mtime[0]) << 16 | swab (binary.b_mtime[1]); asb->sb_size = swab (binary.b_size[0]) << 16 | swab (binary.b_size[1]); if ((namesize = swab (binary.b_name)) == 0 || namesize >= PATHSIZE) return (warnarch ("Bad swapped pathname length", (off_t) sizeof (binary) - (M_STRLEN - sizeof (ushort)))); if (inread (name, namefull = namesize + namesize % 2) < 0) return (warnarch ("Corrupt swapped pathname", (off_t) namefull)); if (name[namesize - 1] != '\0') return (warnarch ("Bad swapped pathname", (off_t) namefull)); return (asb->sb_size % 2); } /* * lineget() * * Get a line from a given stream. Returns 0 if successful, -1 at EOF. */ STATIC int lineget (stream, buf, bufsize) reg FILE *stream; reg char *buf; int bufsize; { reg int c; int size; char *bufstart; size=0; bufstart=buf; for (;;) { if ((c = getc (stream)) == EOF) return (-1); if ((!flag0 && c == '\n') || (flag0 && c == '\0')) break; if(sizebufsize-1) warn(bufstart,"Path name too long, truncated"); return (0); } /* * linkalso() * * Add a destination pathname to an existing chain. Assumes that * at least one element is present. */ STATIC void linkalso (linkp, name) reg Link *linkp; char *name; { reg Path *path; if (((path = (Path *) memget (sizeof (Path))) == NULL) || ((path->p_name = memstr (name)) == NULL)) return; path->p_forw = NULL; path->p_back = linkp->l_path->p_back; path->p_back->p_forw = path; linkp->l_path->p_back = path; } /* * linkfrom() * * Find a file to link from. Returns a pointer to a link * structure, or NULL if unsuccessful. */ /* In v2.4.4: added some checks which would be redundant if not for the fact that the standard archive format only stores the lower 16 bits of the inode number. The checks reduce the chance of false matches when, reading archives produced by older afio versions and by cpio in the old ascii format. */ STATIC Link * linkfrom (asb, installing) reg Stat *asb; int installing; { reg Link *linkp; reg Link *linknext; reg Link **abase; /* check: could file be hard-linked at all? */ if(asb->sb_nlink<=1) return NULL; /* check: if is a directory, then cannot be hard-linked to something */ if((asb->sb_mode & S_IFMT) == S_IFDIR) return NULL; /* check: sb_size==0 for entry in archive which should produce hardlink */ if(installing && (asb->sb_size!=0)) return NULL; for (linkp = *(abase = linkhash (asb->sb_ino)); linkp; linkp = linknext) { if (linkp->l_ino == asb->sb_ino && linkp->l_dev == asb->sb_dev /* check: do mtimes also match? */ && linkp->l_mtime == asb->sb_mtime /* check: do modes also match? */ && linkp->l_mode == asb->sb_mode ) { --linkp->l_nlink; /* reference count for -u option */ /* in older versions of afio, if l_nlink drops to 0, then the link entry is removed from the hash table. We don't do this anymore because that would break things if the same hard linked directory entry was included multiple times in the archive. Also it allows us to call linkfrom as often as we want for each file */ return (linkp); } linknext = linkp->l_forw; } return (NULL); } /* * linkleft() * * Complain about files with unseen links. */ STATIC void linkleft () { reg Link *lp; reg Link **base; for (base = linkbase; base < linkbase + nel (linkbase); ++base) for (lp = *base; lp; lp = lp->l_forw) { /* printf("%s ino=%ld nl=%d\n",lp->l_path->p_name,(long int)lp->l_ino,(int)lp->l_nlink); */ if (lp->l_nlink) VOID warn (lp->l_path->p_name, "Unseen link(s)"); } } /* * linkto() * * Remember a file with outstanding links. Returns a * pointer to the associated link structure, or NULL * when linking is not possible. * * except in extfmt mode, * updates asb->sb_ino to create an (inode number,device number) pair that * is unique for each set of hard linked files in the archive, * while keeping the inode number <16bits as long as possible. * before calling linkto, asb->sb_ino should contain the inode number * from the filesystem, as obtained with a stat call. */ STATIC Link * linkto (name, asb) char *name; reg Stat *asb; { reg Link *linkp; reg Path *path; reg Link **abase; ino_t ino; int ino16; if ((asb->sb_mode & S_IFMT) == S_IFDIR) return (NULL); if(!extfmt) { /* the old ASCII format only stores the least significant 16 bits of the inode. We need to avoid duplicate ino numbers for different files in the archive or else the restore can have all kinds of horrible failure modes */ /* first, try the inode that is the inode from the fs, truncated */ ino = asb->sb_ino & 0x0ffff; /* check if that has been used, if so choose other one */ while(1) { ino16=ino&0xffff; /* if the bit for ino16 is 0, then this value has not been used yet in the archive, so we can break out of the loop and use it */ if((ino16bitused[ino16/8]&((unsigned char)(1)<<(ino16&0x7)))==0) break; /* choose other one */ ino=freshino++; if(freshino>0xffff) { /* break out of loop, no need to check if has been used */ break; } } } else { ino = asb->sb_ino; } /* add this number to the table of used 16-bit inode numbers */ /* this operation is unneccessary in some cases of the control flow of the above if statements, but it is nice to put it here */ ino16=ino&0xffff; ino16bitused[ino16/8]=ino16bitused[ino16/8]|((unsigned char)(1)<<(ino16&0x7)); /* add this inode to table of hard links (under filesystem inode number) */ if( ((linkp = (Link *) memget (sizeof (Link))) == NULL) || ((path = (Path *) memget (sizeof (Path))) == NULL) || ((path->p_name = memstr (name)) == NULL)) return (NULL); linkp->l_dev = asb->sb_dev; linkp->l_ino = asb->sb_ino; /* original ino from fs */ linkp->l_nlink = asb->sb_nlink - 1; linkp->l_size = asb->sb_size; linkp->l_mtime = asb->sb_mtime; linkp->l_mode = asb->sb_mode; linkp->l_path = path; path->p_forw = NULL; path->p_back = path; linkp->l_ino_ar = asb->sb_ino = ino; if ((linkp->l_forw = *(abase = linkhash (linkp->l_ino)))) linkp->l_forw->l_back = linkp; linkp->l_back = NULL; return (*abase = linkp); } #ifndef MEMCPY /* * memcpy() * * A simple block move. */ STATIC char * memcpy (to, from, len) reg char *to; reg char *from; uint len; { reg char *toend; for (toend = to + len; to < toend; *to++ = *from++) ; return (to); } #endif /* MEMCPY */ /* * memget() * * Allocate memory. */ STATIC char * memget (len) uint len; { reg char *mem; static short outofmem; if ((mem = malloc (len)) == NULL && !outofmem) outofmem = warn ("memget()", "Out of memory"); return (mem); } /* * memstr() * * Duplicate a string into dynamic memory. */ STATIC char * memstr (str) reg char *str; { reg char *mem; if ((mem = memget ((uint) strlen (str) + 1))) VOID strcpy (mem, str); return (mem); } #ifndef MKDIR /* * mkdir() * * Make a directory via "/bin/mkdir". Sets errno to a * questionably sane value upon failure. */ STATIC int mkdir (name, mode) reg char *name; reg ushort mode; { reg int pid; if ((pid = xfork ("mkdir()", DIE)) == 0) { VOID close (fileno (stdin)); VOID close (fileno (stdout)); VOID close (fileno (stderr)); VOID open ("/dev/null", O_RDWR); VOID dup (fileno (stdin)); VOID dup (fileno (stdin)); VOID umask (~mode); VOID execl ("/bin/mkdir", "mkdir", name, (char *) NULL); exit (1); } if (xwait (pid, "mkdir()", TRUE) == 0) return (0); errno = EACCES; return (-1); } #endif /* MKDIR */ /* * nameopt() * * Used to optimize a pathname in a complicated way, trying to remove * redundant ../ components, now just simplifies leading //// into one * leading /. Returns 1 always. */ STATIC int nameopt (name) char *name; { char *name2; while((name[0]=='/')&&(name[1]=='/')) { name2=name; while ((name2[0] = name2[1])) ++name2; } return 1; } /* * next() * * Advance to the next archive volume. * */ STATIC void next (mode, why) reg int mode; reg char *why; { reg time_t began; char *msg; int msgsize; char answer[20]; msgsize = 200 + strlen(myname) * 2 + strlen(arspec); msg = memget (msgsize); began = time ((time_t *) NULL); nextclos (); if(!index(ignorewarnings,(int)'M')) { VOID warnarch (why, (off_t) 0); warnings--; /* above warnach call is not an error condition */ } if (arfd == STDIN || arfd == STDOUT) goodbye (1); ++arvolume; /* change disk # here */ if (email) mail(email,(int)arvolume,arspec); if(promptscript) { for (;;) { auto int result; result=runpromptscript(why); if (result==1) fatal(arspec,"Aborted"); if (result!=0) fatal(arspec,"Promptscript failed"); if (nextopen(mode)==0) break; } } else { if(Fflag) { VOID sprintf (msg, "\ %s: Ready for disk %u on %s\n\ %s: \"quit\" to abort,\"f\" to format, anything else to proceed. > \07", myname, arvolume, arspec, myname); } else { VOID sprintf (msg, "\ %s: Ready for volume %u on %s\n\ %s: \"quit\" to abort, anything else to proceed. > \07", myname, arvolume, arspec, myname); } for (;;) { nextask (msg, answer, sizeof (answer)); if (strcmp (answer, "quit") == 0) fatal (arspec, "Aborted"); else { while (Fflag && strcmp(answer,"f") == 0) { fprintf (stderr, "formatting using %s ...\n",formatcmd); if (system (formatcmd) != 0) { strcpy(msg,"\n"); strcat(msg,myname); strcat(msg,": Format failed! Try again (y/n)? > "); nextask (msg, answer, sizeof (answer)); if (answer[0] == 'y') answer[0] = 'f'; else exit(1); } else break; } if (nextopen (mode) == 0) break; } } } if(!index(ignorewarnings,(int)'M')) { VOID warnarch ("Continuing", (off_t) 0); warnings--; /* above warnach call is not an error condition */ } timewait += time ((time_t *) NULL) - began; free(msg); } /* * runpromptscript() * * Utility function to run the promptscript * */ STATIC int runpromptscript (why) char *why; { int pid,result; char vol[100]; /* run info-script with volume number and archive spec, and reason for calling it as arguments. the script should return 0 for ok and 1 for abort, other return codes will be treated as errors. */ /* connect prompt script to /dev/tty if it can be opened, else connect it to /dev/null (for crontab/at job use) */ sprintf(vol,"%d",arvolume); if ((pid = xfork (promptscript, DIE)) == 0) { VOID close (fileno (stdin)); VOID close (fileno (stdout)); VOID close (fileno (stderr)); if ((open (TTY, O_RDWR)) < 0) { VOID open ("/dev/null", O_RDWR); } VOID dup (fileno (stdin)); VOID dup (fileno (stdin)); execlp(promptscript,promptscript,vol,arspec,why,(char *)NULL); exit (1); } result=xwait (pid, "promptscript", TRUE); return(result); } /* * nextask() * * Ask a question and get a response. Ignores spaces and tabs. */ STATIC void nextask (msg, answer, limit) reg char *msg; reg char *answer; reg int limit; { reg int idx; reg int got; auto char c; if (ttyf < 0) ttyf = openqtty (); if (ttyf < 0) fatal (TTY, "Unavailable"); VOID writeall (ttyf, msg, (uint) strlen (msg)); idx = 0; while ((got = read (ttyf, &c, 1)) == 1) if (c == '\04' || c == '\n') break; else if (c == ' ' || c == '\t') continue; else if (idx < limit - 1) answer[idx++] = c; if (got < 0) fatal (TTY, syserr ()); answer[idx] = '\0'; } /* * nextclos() * * Close an archive. */ STATIC void nextclos () { if (arfd != STDIN && arfd != STDOUT && !fflag) { VOID close (arfd); arfd = -1; } areof = 0; if (arname && *arname == '!') pipewait (); /* the volume is complete, need to adjust the volume limit for the * following volume. */ if (aruntil != 0) { update_aruntil(); if (aruntil == 0) askfornext = 1; if( roundaruntil ) { /* round aruntil down to a multiple of arbsize: some devices (like ftape) puke on a smaller-than-blocksize last write to the volume */ aruntil = ( aruntil / (ulonglong) arbsize ); aruntil = aruntil * arbsize; } /* error check... */ if (aruntil && (aruntil < arbsize)) { fatal(arspec,"-s option value for this volume smaller than block (-b) size"); } } } /* * nextopen() * * Open an archive. Returns 0 if successful, -1 otherwise. */ STATIC int nextopen (mode) int mode; { #if 1 if (arfd != -1) close (arfd); #endif if (*arname == '!') arfd = pipeopen (mode); else if (strcmp (arname, "-") == 0) arfd = mode ? STDOUT : STDIN; else { process_arname(arname); #ifdef CTC3B2 if (Cflag) { reg int oops; reg int fd; oops = ((fd = open (proc_arname, O_RDWR | O_CTSPECIAL)) < 0 || ioctl (fd, STREAMON) < 0); VOID close (fd); if (oops) return (warnarch (syserr (), (off_t) 0)); } #endif /* CTC3B2 */ #ifdef linux /* Do O_SYNC writing to the floppy drive */ if(Fflag && mode) arfd = open (proc_arname, mode | O_CREAT | O_TRUNC | O_SYNC, 0666 & ~mask); else arfd = mode ? creat (proc_arname, (mode_t)(0666 & ~mask)) : open (proc_arname, mode); #else arfd = mode ? creat (proc_arname, (mode_t)(0666 & ~mask)) : open (proc_arname, mode); #endif /* linux */ } if (arfd < 0) return (warnarch (syserr (), (off_t) 0)); arleft = aruntil; return (0); } /* * openin() * * Open the next input file. Returns a file descriptor, 0 if no data * exists, or -1 at EOF. This kludge works because standard input is * in use, preventing open() from returning zero. * * if fsname is non-NULL, copy filename on local filesystem into it, * if not a control file, for later use by -a option. */ STATIC int openin (name, fsname, asb, cratio) char *name,*fsname; reg Stat *asb; int *cratio; { int fd; auto char local[PATHSIZE]; char *label; if(fsname) *fsname='\0'; if (cratio) *cratio = 100; for (;;) { if(firstfilename[0]!=0) { /* special case to suport input sanity check code in main() */ strcpy(name,firstfilename); firstfilename[0]=0; } else { if (lineget (stdin, name, PATHSIZE) < 0) return (-1); } /* control file syntax is //--SOURCEFILE[ LABEL] */ if(strncmp(name,"//--",4)==0) { strcpy(local,&name[4]); label=index(local,(int)' '); if(label!=NULL) { *label='\000'; /* separate filename and label */ label++; } else label=NOLABEL; if(*label=='\000') label=NOLABEL; sprintf(name,"%s/%s",CONTROLNAME,label); /* Now, local is the filename, label is the label, and name is the filename in the archive, with the label embedded. */ if (STAT (local, asb) < 0) { VOID warn (local, syserr ()); continue; } /* Sun machines put trash in sb_rdev in stead of setting it to 0 for regular files. Afio wants a 0 */ if(! (S_ISBLK(asb->sb_mode)||S_ISCHR(asb->sb_mode)) ) asb->sb_rdev=0; if ((asb->sb_mode & S_IFMT) != S_IFREG) { VOID warn (local, "Cannot read control file from this source."); continue; } /* Flag that this is a control file. An archive entry is a control file if it is a regular file and if the ISCONTROL flag is set. The filename is not important. */ asb->sb_rdev |= RDEV_ISCONTROL; if (asb->sb_size == 0) return (0); if ((fd = open (local, O_RDONLY)) >= 0) return (fd); VOID warn (local, syserr ()); continue; } /* not a control file, make a normal archive entry */ if (namecmp (name,NULL) < 0) continue; if (nameopt (name) < 0) continue; if (!gflag) VOID strcpy (local, name); else if (dirchg (name, local) < 0) continue; if(fsname) strcpy(fsname,local); /* for use by utime() in -a option */ if ((hflag ? STAT (local, asb) : LSTAT (local, asb)) < 0) { VOID warn (name, syserr ()); if(index(ignorewarnings,(int)'m')) warnings--; #ifdef EOVERFLOW /* try to catch (in a portable way we hope) the case of statting a >2GB file with a regular 4-byte off_t stat call, this gives an EOVERFLOW on Linux. We do not want to count this case as a missing file, so print extra warning so that warnings counter gets increased properly. */ if(errno==EOVERFLOW) VOID warn (name, "Can't archive file with size >= 2GB"); #endif continue; } /* Sun machines put trash in sb_rdev in stead of setting it to 0 for regular files. Afio wants a 0 */ if(! (S_ISBLK(asb->sb_mode)||S_ISCHR(asb->sb_mode)) ) asb->sb_rdev=0; switch (asb->sb_mode & S_IFMT) { case S_IFDIR: asb->sb_nlink = 1; asb->sb_size = 0; return (0); #ifdef S_IFLNK case S_IFLNK: if ((asb->sb_size = readlink (local, asb->sb_link, sizeof (asb->sb_link) - 1)) < 0) { VOID warn (name, syserr ()); continue; } asb->sb_link[asb->sb_size] = '\0'; return (0); #endif /* S_IFLNK */ case S_IFREG: if (asb->sb_size == 0) { /* indicate that file is not suitable for compression */ asb->sb_rdev |= RDEV_NOTCOMPR; return (0); } if (cpiocompat && (asb->sb_size != (asb->sb_size&0x7fffffff))) { /* >=2GB file. Can't handle this and remain cpio compatible */ VOID fatal (name, "Archiving file with size >= 2GB not allowed by -5 option"); continue; } if ((fd = open (local, O_RDONLY)) >= 0) { if (Zflag) compressfile (&fd, name, asb, cratio); return (fd); } VOID warn (name, syserr ()); break; default: asb->sb_size = 0; return (0); } } } /* * openincheck() * * Open the input file. Returns a file descriptor, 0 if no data * exists, or -1 at EOF. This kludge works because standard input is * in use, preventing open() from returning zero. */ STATIC int openincheck (name, asb, comp, dozflag) char *name; reg Stat *asb; int *comp; int dozflag; { int fd; auto char local[PATHSIZE]; auto char archlink[PATHSIZE]; char* namedot; *uncompto = '\0'; *comp = 0; if (dozflag && ((asb->sb_mode & S_IFMT) == S_IFREG) && ((namedot = strrchr (name, '.')) != NULL) && (asb->sb_rdev & RDEV_NOTCOMPR) == 0 && (asb->sb_size > 0) && (strcmp (namedot, ".z") == 0)) { *namedot = '\0'; strcpy(uncompto, name); *comp = 1; } else namedot = NULL; /* not uncompressing */ if (nameopt (name) < 0) return -1; if (!gflag) VOID strcpy (local, name); else if (dirchg (name, local) < 0) return 0; switch (asb->sb_mode & S_IFMT) { case S_IFDIR: asb->sb_nlink = 1; asb->sb_size = 0; /* do not check if directory exists */ /* do not check directory permissions and access times */ return (0); #ifdef S_IFLNK case S_IFLNK: /* if sb_size==0 then it is a hardlink to a symlink earlier in the archive, so don't check. */ if (asb->sb_size == 0) return (0); strcpy(archlink,asb->sb_link); if ((asb->sb_size = readlink (local, asb->sb_link, sizeof (asb->sb_link) - 1)) < 0) { VOID warn (name, syserr ()); asb->sb_size = 0; return 0; } asb->sb_link[asb->sb_size] = '\0'; if(strcmp(archlink,asb->sb_link)!=0) VOID warn (name, "Difference in symlink destination filename"); asb->sb_size = 0; return (0); #endif /* S_IFLNK */ case S_IFREG: /* does not check file permissions and access times */ /* get last access time if we want to restore it */ if(aflag) { if (STAT (local, &atime_sb) < 0) { /* silently fail, the open below will report an error */ } else { atime_sb_valid=1; } } if ((fd = open (local, O_RDONLY)) >= 0) { /* check for larger file on filesystem if file has 0 size */ /* special handling of hard links: non-first archive entries for hardlinked files, which have 0 size, are not checked. If the hardlinked file has 0 size originally, the first archive entry for it also has 0 size, which means that the code below does not then check if the file on the filesystem grew larger. Fixing this would be too much work, we have to figure out correct coding of hard link inode/file name administration (see e.g. code in tocentry()) in order to detect whether the entry represents a first entry. */ if ((asb->sb_size == 0)&&(asb->sb_nlink == 1)) { if (STAT (local, &atime_sb) >= 0) { if (asb->sb_size != atime_sb.sb_size) { VOID warn (name, "File in archive has different length than file on filesystem"); if(index(ignorewarnings,(int)'r')) { warn_nocount(name, "Not counting this as a verification error"); warnings--; } } } } return (fd); } VOID warn_nocount (name, syserr ()); VOID warn (name, "File on filesystem could not be opened for verify"); if(index(ignorewarnings,(int)'r')) { warn_nocount(name, "Not counting this as a verification error"); warnings--; } return 0; default: asb->sb_size = 0; return (0); } } /* * opencontrolscript() * * Start a control script. Returns the stdin of the script, * -1 if unsuccessful. * If no control script option given, return file handle of /dev/null. */ STATIC int opencontrolscript (char *name) { auto int pfd[2]; int comppid; char *label; *uncompto='\000'; /* for -v output on restore, verify */ if(controlscript == NULL ) { /* ignore data */ warnarch("No -D option given, ignoring control file.",(off_t)0); return open("/dev/null",O_WRONLY); } if(strcmp(controlscript,"")==0) { /* silently ignore data */ return open("/dev/null",O_WRONLY); } label=index(name,(int)'/'); if(label==NULL) label=NOLABEL; else label++; if (pipe (pfd) < 0) { warn("Control script",syserr()); return -1; } if ((comppid = xfork ("opencontrolscript(in), running control script", NODIE)) == 0) { /* Ignoring SIGPIPE may cause strange results in some shell scripts */ VOID signal (SIGPIPE, SIG_DFL); if (arfd != STDIN && arfd != STDOUT) VOID close (arfd); VOID close (pfd[1]); /* child */ VOID close (fileno (stdin)); VOID dup (pfd[0]); VOID close (pfd[0]); execlp (controlscript, controlscript, label, NULL); warnarch("Problems running control script:",(off_t)0); warn(controlscript,syserr()); exit (1); } /*parent*/ if (comppid < 0) { warn("Control script",syserr()); return -1; } close (pfd[0]); /* this enables some safety checks */ uncompressrun = comppid; return pfd[1]; } /* * openotty() * * Open an output file. Returns the output file descriptor, * 0 if no data is required or -1 if unsuccessful. Note that * UNIX open() will never return 0 because the standard input * is in use. */ STATIC int openotty (name, asb, linkp, ispass, dozflag) char *name; reg Stat *asb; Link *linkp; reg int ispass; int dozflag; { reg int exists; reg int fd; reg ushort perm; ushort operm=0; /* this one and next two initialised to */ uid_t ouid=9999; /* suppress compiler warnings */ gid_t ogid=9999; Path *path; auto Stat osb; #ifdef S_IFLNK reg int ssize; auto char sname[PATHSIZE]; #endif /* S_IFLNK */ char *namedot; auto int pfd[2]; int comppid; if(ISCONTROL(asb)) return opencontrolscript(name); *uncompto = '\0'; /* * -iZ try to uncompress a compress'd regular file */ if (dozflag && !linkp && ((asb->sb_mode & S_IFMT) == S_IFREG) && ((namedot = strrchr (name, '.')) != NULL) && (asb->sb_rdev & RDEV_NOTCOMPR) == 0 && (asb->sb_size > 0) && (strcmp (namedot, ".z") == 0)) { *namedot = '\0'; } else namedot = NULL; /* not uncompressing */ if ((exists = (LSTAT (name, &osb) == 0))) { /* The asb.sb_ino here has not been truncated to 16 bits, so the check is safe and may even add some protection. */ if (ispass && osb.sb_ino == asb->sb_ino && osb.sb_dev == asb->sb_dev) return (warn (name, "Same file")); else if ((osb.sb_mode & S_IFMT) == (asb->sb_mode & S_IFMT)) operm = osb.sb_mode & (xflag ? S_IPERM : S_IPOPN); else if (afremove (name, &osb) < 0) return (warn (name, syserr ())); else exists = 0; } if (linkp) { if (exists) { #if 0 /* Bogus check commented out. This check is bogus and dangerous because we only get the least significant 16 bits of the ino of the archived file from the archive header. So the file this check would prevent overwriting (replacing with a link) if true will probably be a different file altogether, which we wanted overwritten, not preserved. Also, the check is bogus anyway when installing the files on a different computer system. Ho hum. */ printf("asb->sb_ino == osb.sb_ino %d %d\n",asb->sb_ino, osb.sb_ino); if (asb->sb_ino == osb.sb_ino && asb->sb_dev == osb.sb_dev) return (0); else #endif if (unlink (name) < 0) return (warn (name, syserr ())); else exists = 0; } for (path = linkp->l_path; path; path = path->p_forw) if (link (path->p_name, name) == 0 || (errno == ENOENT && dirneed (name) == 0 && link (path->p_name, name) == 0)) return (0); else if (errno != EXDEV) return (warn (name, syserr ())); VOID warn (name, "Link broken"); linkalso (linkp, name); } perm = asb->sb_mode & (xflag ? S_IPERM : S_IPOPN); if (exists) { ouid = osb.sb_uid; ogid = osb.sb_gid; } switch (asb->sb_mode & S_IFMT) { case S_IFBLK: case S_IFCHR: fd = 0; /* rdev==0 means that dev_t value does not fit in 16 bits, and is encoded in dev and ino instead see out(). */ if(asb->sb_rdev==0) asb->sb_rdev=(asb->sb_dev << 16) + asb->sb_ino; if (exists) { if (asb->sb_rdev == osb.sb_rdev) if (perm != operm && chmod (name, perm) < 0) return (warn (name, syserr ())); else break; else if (afremove (name, &osb) < 0) return (warn (name, syserr ())); else exists = 0; } if (mknod (name, asb->sb_mode, asb->sb_rdev) < 0 && (errno != ENOENT || dirneed (name) < 0 || mknod (name, asb->sb_mode, asb->sb_rdev) < 0)) return (warn (name, syserr ())); break; case S_IFDIR: if (exists) { if (xflag && (asb->sb_uid != ouid || asb->sb_gid != ogid) && chown (name, asb->sb_uid, asb->sb_gid) < 0) return (warn (name, syserr ())); else if (perm != operm && chmod (name, perm) < 0) return (warn (name, syserr ())); else { ; } if(osb.sb_mtime <= (mflag ? timenow : asb->sb_mtime)) savedirstamp(name, mflag ? timenow : asb->sb_mtime); } else { savedirstamp(name, mflag ? timenow : asb->sb_mtime); if (dirneed (name) < 0 || dirmake (name, asb) < 0) return (warn (name, syserr ())); } return (0); #ifdef S_IFIFO case S_IFIFO: fd = 0; if (exists) if (perm != operm && chmod (name, perm) < 0) return (warn (name, syserr ())); else { ; } else if (mkfifo (name, asb->sb_mode) < 0 && (errno != ENOENT || dirneed (name) < 0 || mkfifo (name, asb->sb_mode) < 0)) return (warn (name, syserr ())); break; #endif /* S_IFIFO */ #ifdef S_IFSOCK case S_IFSOCK: fd = 0; if (exists) if (perm != operm && chmod (name, perm) < 0) return (warn (name, syserr ())); else { ; } else if (mknod (name, asb->sb_mode, (dev_t) 0) < 0 && (errno != ENOENT || dirneed (name) < 0 || mknod (name, asb->sb_mode, (dev_t) 0) < 0)) return (warn (name, syserr ())); break; #endif /* S_IFSOCK */ #ifdef S_IFLNK case S_IFLNK: fd = 0; if (nosymlinks) { warn (name, "Not unpacking this symlink because of -8 nosymlinks"); if(index(ignorewarnings,(int)'l')) warnings--; break; } if (exists) { if ((ssize = readlink (name, sname, sizeof (sname))) < 0) return (warn (name, syserr ())); else if (strncmp (sname, asb->sb_link, (size_t)ssize) == 0) break; else if (afremove (name, &osb) < 0) return (warn (name, syserr ())); else exists = 0; } if (symlink (asb->sb_link, name) < 0 && (errno != ENOENT || dirneed (name) < 0 || symlink (asb->sb_link, name) < 0)) return (warn (name, syserr ())); break; #endif /* S_IFLNK */ case S_IFREG: if (exists) { if (nflag && osb.sb_mtime > asb->sb_mtime) return (warn_nocount (name, "Newer file exists")); else if (unlink (name) < 0) return (warn (name, syserr ())); else exists = 0; } if ((fd = creat (name, perm)) < 0 && (errno != ENOENT || dirneed (name) < 0 || (fd = creat (name, perm)) < 0)) return (warn (name, syserr ())); if (dozflag && !linkp && namedot) { if (pipe (pfd) >= 0) { if ((comppid = xfork ("openotty(in), compressing", NODIE)) == 0) { if (arfd != STDIN && arfd != STDOUT) VOID close (arfd); VOID close (pfd[1]); /* child */ VOID close (fileno (stdin)); VOID dup (pfd[0]); VOID close (pfd[0]); VOID close (fileno (stdout)); if (dup (fd) < 0) exit (1); VOID close (fd); mayberewind(); if(compressargs) execvp (compressprog, compress_arg_list); else execlp (compressprog, compressprog, "-d", "-c", NULL); fprintf (stderr, "Could not uncompress, errno %d\n", errno); exit (1); } else /* life seems ok */ if (comppid > 0) { close (fd); fd = pfd[1]; close (pfd[0]); uncompressrun = comppid; strcpy (uncompto, name); #if 0 *namedot = '.'; #endif break; } } /* we failed try again, not uncompressing the file */ unlink (name); *namedot = '.'; return (openotty (name, asb, linkp, ispass, FALSE)); } break; default: return (warn (name, "Unknown filetype")); } /* Can't chown()/chmod() a symbolic link, but for all others... */ if((asb->sb_mode & S_IFMT) != S_IFLNK) { if (xflag && (!exists || asb->sb_uid != osb.sb_uid || asb->sb_gid != osb.sb_gid)) { if (chown (name, (uid == 0 ? asb->sb_uid : uid), asb->sb_gid)) perror (name); else /* chown may have cleared suid/sgid flags; restore them */ if(perm&S_IPEXE) if(chmod (name, perm) < 0) return (warn (name, syserr ())); } } else { /* but if we have an lchown call, we _can_ chown a symbolic link.. */ /* note: the lchown call is like chown, except that lchown does not follow a symlink. lchown is not present in all unix systems. Therefore, an ifdef is used below, the symbol is defined in the makefile */ #ifdef HAVE_LCHOWN if (xflag && (!exists || asb->sb_uid != osb.sb_uid || asb->sb_gid != osb.sb_gid)) { if (lchown (name, (uid == 0 ? asb->sb_uid : uid), asb->sb_gid)) perror (name); } #endif } if (linkp == NULL && asb->sb_nlink > 1) VOID linkto (name, asb); return (fd); } /* * openqtty() * * Open the terminal for interactive queries (sigh). Assumes that * background processes ignore interrupts and that the open() or * the isatty() will fail for processes which are not attached to * terminals. Returns a file descriptor (-1 if unsuccessful). */ int openqtty (void) { reg VOIDFN (*intr) (int); int fd; fd = -1; if (!Fflag) { if ((intr = signal (SIGINT, SIG_IGN)) == SIG_IGN) return (fd); VOID signal (SIGINT, intr); } if ((fd = open (TTY, O_RDWR)) < 0) { VOID warn (TTY, syserr ()); } else if (!isatty (fd)) VOID warn (TTY, "Is not a tty"); return (fd); } /* * options() * * Decode most reasonable forms of UNIX option syntax. Takes main()- * style argument indices (argc/argv) and a string of valid option * letters. Letters denoting options with arguments must be followed * by colons. With valid options, returns the option letter and points * "optarg" at the associated argument (if any). Returns '?' for bad * options and missing arguments. Returns zero when no options remain, * leaving "optind" indexing the first remaining argument. */ STATIC int options (ac, av, proto) int ac; register char **av; char *proto; { register int c; register char *idx; static int optsub; if (optind == 0) { optind = 1; optsub = 0; } optarg = NULL; if (optind >= ac) return (0); if (optsub == 0 && (av[optind][0] != '-' || av[optind][1] == '\0')) return (0); switch (c = av[optind][++optsub]) { case '\0': ++optind; optsub = 0; return (options (ac, av, proto)); case '-': ++optind; optsub = 0; return (0); case ':': return ('?'); } if ((idx = strchr (proto, c)) == NULL) return ('?'); if (idx[1] != ':') return (c); optarg = &av[optind][++optsub]; ++optind; optsub = 0; if (*optarg) return (c); if (optind >= ac) return ('?'); optarg = av[optind++]; return (c); } /* * optsize() * * Interpret a "size" argument. Recognizes suffices for blocks * (512-byte), kilobytes, megabytes, gigabytes and blocksize. Returns * the size in bytes. */ STATIC ulonglong optsize (str) char *str; { reg char *idx; ulonglong number; ulonglong result; result = 0; idx = str; for (;;) { number = 0; while (*idx >= '0' && *idx <= '9') number = number * 10 + *idx++ - '0'; switch (*idx++) { case 'b': result += number * (ulonglong)512; continue; case 'k': result += number * (ulonglong)1024; continue; case 'm': result += number * (ulonglong)(1024L * 1024L); continue; case 'g': result += number * (ulonglong)(1024L * 1024L * 1024L); continue; case 'x': result += number * (ulonglong)arbsize; continue; case '+': result += number; continue; case '\0': result += number; break; default: break; } break; } if (*--idx) fatal (str, "Unrecognizable value"); return (result); } /* * out() * * Write an archive. */ STATIC VOIDFN out (av) char **av; { reg int fd; auto Stat sb; auto char name[PATHSIZE]; auto char fsname[PATHSIZE]; auto int compression; #ifdef linux_tstamp auto struct utimbuf tstamp; #else auto time_t tstamp[2]; #endif int wantlarge; Link *linkp; if (*av) fatal (*av, "Extraneous argument"); while ((fd = openin (name, fsname, &sb, &compression)) >= 0) { bytepos=total; /* offset of this file in archive */ sb.ino_orig=sb.sb_ino; if (!lflag && sb.sb_nlink > 1) { if ((linkp = linkfrom (&sb,0))) { /* this file is a had link to a file we stored before */ sb.sb_size = 0; sb.sb_ino=linkp->l_ino_ar; } else { /* linkto also updates sb.sb_ino to equal l_ino_ar */ VOID linkto (name, &sb); } } /* Some systems have a dev_t larger than 16 bits. If the dev_t value is larger, then encode it in the dev and ino fields of the archive header, and set rdev to 0 to signify special case. */ /* for symmetry, and to reduce the amount of conditional logic needed in afio, this re-encoding measure is also used if we produce an outhead3 large ascii header, even though the outhead3 header has more room in its rdev field. */ if( (S_ISBLK(sb.sb_mode)||S_ISCHR(sb.sb_mode))&& ( ((sb.sb_rdev|0xffff)!=0xffff) || (sb.sb_rdev==0)) ) { sb.sb_dev=sb.sb_rdev>>16; sb.sb_ino=sb.sb_rdev&0xffff; sb.sb_rdev=(dev_t)0; } /* calculate if we want to use a large ASCII header. * Use them only when the data won't fit into the * default header. */ wantlarge=(sb.sb_size != (sb.sb_size&0x7FFFFFFF)); /* also need large archive header to correctly store hard linked files (if inode numbers assigned by linkto() go above 16 bits) */ if (!lflag && (sb.sb_nlink > 1) && !extfmt) wantlarge = wantlarge || ((sb.sb_ino&0x0ffff)!=sb.sb_ino); /* also need it for big uid/gid values */ wantlarge=wantlarge || ((sb.sb_uid&0x0ffff)!=sb.sb_uid); wantlarge=wantlarge || ((sb.sb_gid&0x0ffff)!=sb.sb_gid); /* When on a system with sizeof(time_t)>4bytes, and if the time on the file is >2038, also use large archive header (Linux right now has a 4-byte time_t, but we want to be future-proof.) */ wantlarge=wantlarge || ((sb.sb_mtime&0x7fffffff)!=sb.sb_mtime); if(cpiocompat && wantlarge) { /* if this was the >2GB case we already had a fatal elsewhere.. */ VOID fatal (name, "Cannot create cpio-compatible file header for this input, aborting because of -5 option"); } if(wantlarge && !cpiowarned && !index(ignorewarnings,(int)'C')) { VOID warn_nocount(name, "Cannot create cpio-compatible file header for this input"); VOID warnarch("Continuing, archive will not be fully compatible with cpio or afio versions 2.4.7 and lower",(off_t)0); if(index(ignorewarnings,(int)'c')) warnings--; cpiowarned=1; } #ifdef S_IFLNK if ((sb.sb_mode & S_IFMT) == S_IFLNK) if (nosymlinks) { warn (fsname, "Not including symlink because of -8 nosymlinks"); if(index(ignorewarnings,(int)'l')) warnings--; continue; } #endif /* S_IFLNK */ if(wantlarge) { outhead3 (name, &sb); } else { if(extfmt) outhead2 (name, &sb); else outhead (name, &sb); } if (fd) { if (fd==ZIPFD) { outdatazip(zipfdfd,name,sb.sb_size); close(zipfdfd); } else if (fd==MEMFD) { outdatamem(name,sb.sb_size); } else VOID close (outdata (fd, name, sb.sb_size)); } if ((vflag>1)&&((arfd!=STDOUT))) { /* You can use -vv for full ls-style listings on stdout. */ char toc_name[PATHSIZE]; Stat toc_sb; /* Copy data structures first, because tocentry modifies them */ strncpy(toc_name,name,PATHSIZE); memcpy(&toc_sb,&sb,sizeof(Stat)); tocentry (toc_name, &toc_sb); } else if (vflag) { if(printbytepos) fprintf(stderr,"%.0f ",(double)bytepos); if ((name[0] == '/') && !abspaths && (name[1]!=0)) fprintf (stderr, "%s -- ", &name[1]); else fprintf (stderr, "%s -- ", name); /* We do not check for a broken pipe on stderr, we wouldn't want to stop making the archive if this happens. */ if ((fd==ZIPFD)||(fd==MEMFD)) VOID fprintf (stderr, "(%02d%%)\n", compression); else VOID fputs ("okay\n", stderr); } /* ASX check if file changed between the begining and end of the backup */ /* if *fsname==0, it was a control file, so do not check then */ if (*fsname!=0) { struct stat st; /* I must check fsname ! but error must be reported as fsname or name ????? I chosed to use fsname */ if ((hflag ? stat(fsname, &st) : lstat(fsname, &st))<0) { warn (fsname, syserr()); } else { if (st.st_mtime!=sb.sb_mtime) { warn (fsname, "File was modified during its backup"); if(index(ignorewarnings,(int)'d')) warnings--; } } } if(aflag && *fsname && ((sb.sb_mode & S_IFMT)==S_IFREG)) { /* reset access time, this distroys the ctime btw. */ #ifdef linux_tstamp tstamp.actime = sb.sb_atime; tstamp.modtime = sb.sb_mtime; VOID utime (fsname, &tstamp); #else tstamp[0] = sb.sb_atime; tstamp[1] = sb.sb_mtime; VOID utime (fsname, tstamp); #endif } } outeof (TRAILER, TRAILZ); } /* * outalloc() * * Allocate buffer space previously referenced by outavail(). */ STATIC void outalloc (len) reg size_t len; { bufidx += len; total += len; } /* * outavail() * * Index buffer space for archive output. Stores a buffer pointer * at a given location. Returns the number of bytes available. */ STATIC size_t outavail (bufp) reg char **bufp; { reg size_t have; while ((have = bufend - bufidx) == 0) { outflush (NOTDONE); } *bufp = bufidx; return (have); } /* * outdata() * * Write archive data. Continues after file read errors, padding with * null characters if neccessary. Always returns the given input file * descriptor. */ STATIC int outdata (fd, name, size) int fd; char *name; reg off_t size; { reg size_t chunk; reg ssize_t got; reg int oops; reg size_t avail; auto char *buf; oops = got = 0; while (size) { avail = outavail (&buf); size -= (chunk = size < (off_t)avail ? size : avail); if (oops == 0 && (got = readall (fd, buf, chunk)) < 0) { oops = warn (name, syserr ()); if(index(ignorewarnings,(int)'n')) warnings--; got = 0; } if ((size_t)got < chunk) { if (oops == 0) oops = warn (name, "Early EOF"); while ((size_t)got < chunk) buf[got++] = '\0'; } outalloc (chunk); } return (fd); } /* * outdatazip() * * Write archive data. Continues after file read errors, padding with * null characters if neccessary. * * Special version for reading from gzip through a pipe. */ void outdatazip (fd, name, size) int fd; char *name; reg off_t size; { reg size_t chunk; reg ssize_t got; reg size_t avail; auto char *buf; char localbuf[4096]; int overflow=0; while (size>0) { avail = outavail (&buf); chunk = (size < (off_t)avail ? size : avail); got = read (fd, buf, chunk); if(got<=0) break; outalloc (got); size-=got; } /* read the end of the stream, if the data changed on the second gzip */ overflow=0; while (read (fd, localbuf, sizeof(localbuf))>0) overflow=1; waitforgzip(); /* wait for child to exit */ if(size>0 || overflow) warn (name, "Error zipping file, written damaged copy to archive."); /* pad with zeros, if necessary */ while (size>0) { avail = outavail (&buf); size -= (chunk = size < (off_t)avail ? size : avail); got=0; while (got < chunk) buf[got++] = '\0'; outalloc (chunk); } } /* * outdatamem() * * Write archive data. * * Special version for reading from internal memory buffer. */ void outdatamem (name, size) char *name; reg off_t size; { reg uint chunk; reg uint got; reg uint avail; auto char *buf; /* read from memory */ memreset(); while (size) { avail = outavail (&buf); chunk = (size < (off_t)avail ? (uint) size : avail); got=memread (buf, (int)chunk); if (got==0) break; outalloc (got); size-=got; } memfree(); } /* * outeof() * * Write an archive trailer. */ STATIC void outeof (name, namelen) char *name; reg uint namelen; { ulonglong pad; auto char header[M_STRLEN + H_STRLEN + 1]; if ((pad = (total + M_STRLEN + H_STRLEN + namelen) % arpad)) pad = arpad - pad; VOID strcpy (header, M_ASCII); VOID sprintf (header + M_STRLEN, H_PRINT, 0, 0, 0, 0, 0, 1, 0, ulo(0) , namelen, (long unsigned int)pad); outwrite (header, M_STRLEN + H_STRLEN); outwrite (name, namelen); outpad ((off_t)pad); outflush (DONE); if (fflag) outwait (); } /* * outflush() * * Flush the output buffer. Optionally fork()s to allow the * parent to refill the buffer while the child waits for the * write() to complete. */ STATIC void outflush (done) int done; { /* * in this case we are a floppy and want to write the floppy from one * buffer (to have a copy of the data to verify against) */ bufend = buffer + ((aruntil!=0) ? min ((ulonglong)buflen, arleft) : (ulonglong)buflen); if (Fflag && (done == NOTDONE) && ((bufend - bufidx) > 0)) { return; } /* * Otherwise open up the next volume once we've run out of space */ if ( !Fflag && aruntil && arleft == 0) next (O_WRONLY, "Output limit reached"); /* writedisk will report errors, no need to check return code */ VOID writedisk (1); bufend = (bufidx = buffer) + ((aruntil!=0) ? min ((ulonglong)buflen, arleft) : (ulonglong)buflen); } /* * outhead() * * Write an archive header. */ STATIC void outhead (name, asb) reg char *name; reg Stat *asb; { reg uint namelen; auto char header[M_STRLEN + H_STRLEN + 1]; if ((name[0] == '/') && !abspaths) { if (name[1]) ++name; else name = "."; } namelen = (uint) strlen (name) + 1; VOID strcpy (header, M_ASCII); VOID sprintf (header + M_STRLEN, H_PRINT, ush (asb->sb_dev), ush (asb->sb_ino), ush (asb->sb_mode), ush (asb->sb_uid), ush (asb->sb_gid), ush (asb->sb_nlink), ush (asb->sb_rdev), ulo(mflag ? timenow : asb->sb_mtime), namelen, (long unsigned int) asb->sb_size); outwrite (header, M_STRLEN + H_STRLEN); outwrite (name, namelen); #ifdef S_IFLNK if ((asb->sb_mode & S_IFMT) == S_IFLNK) outwrite (asb->sb_link, (uint) asb->sb_size); #endif /* S_IFLNK */ } /* * outhead2() * * Write an archive header. */ STATIC void outhead2 (name, asb) reg char *name; reg Stat *asb; { reg uint namelen; auto char header[M_STRLEN + H_STRLEN2 + 1]; if ((name[0] == '/') && !abspaths) { if (name[1]) ++name; else name = "."; } namelen = (uint) strlen (name) + 1; VOID strcpy (header, M_ASCII2); VOID sprintf (header + M_STRLEN, H_PRINT2, ush (asb->sb_dev), (long unsigned int)(asb->sb_ino), ush (asb->sb_mode), ush (asb->sb_uid), ush (asb->sb_gid), ush (asb->sb_nlink), ush (asb->sb_rdev), ulo(mflag ? timenow : asb->sb_mtime), namelen, (long unsigned int)(asb->sb_size)); outwrite (header, M_STRLEN + H_STRLEN2); outwrite (name, namelen); #ifdef S_IFLNK if ((asb->sb_mode & S_IFMT) == S_IFLNK) outwrite (asb->sb_link, (uint) asb->sb_size); #endif /* S_IFLNK */ } /* * outhead3() * * Write a large ASCII archive header. */ STATIC void outhead3 (name, asb) reg char *name; reg Stat *asb; { reg uint namelen; auto char header[M_STRLEN + H_STRLEN3 + 1]; if ((name[0] == '/') && !abspaths) { if (name[1]) ++name; else name = "."; } namelen = (uint) strlen (name) + 1; VOID strcpy (header, M_ASCII3); VOID sprintf (header + M_STRLEN, H_PRINT3, ulo (asb->sb_dev), ull (asb->sb_ino), ulo (asb->sb_mode), ulo (asb->sb_uid), ulo (asb->sb_gid), ulo (asb->sb_nlink), ulo (asb->sb_rdev), mflag ? ull (timenow) : ull (asb->sb_mtime), namelen, 0, 0, ull (asb->sb_size)); outwrite (header, M_STRLEN + H_STRLEN3); outwrite (name, namelen); #ifdef S_IFLNK if ((asb->sb_mode & S_IFMT) == S_IFLNK) outwrite (asb->sb_link, (uint) asb->sb_size); #endif /* S_IFLNK */ } /* * outpad() * * Pad the archive. */ STATIC void outpad (pad) reg off_t pad; { reg int idx; reg int len; while (pad) { if ((len = bufend - bufidx) > pad) len = pad; for (idx = 0; idx < len; ++idx) *bufidx++ = '\0'; total += len; outflush (NOTDONE); pad -= len; } } /* * outwait() * * Wait for the last background outflush() process (if any). The child * exit value is zero if successful, 255 if a write() returned zero or * the value of errno if a write() was unsuccessful. */ STATIC void outwait () { auto int status; if (outpid == 0) return; status = xwait (outpid, "outwait()", TRUE); outpid = 0; if (status) fatal (arspec, "Child error"); } /* * outwrite() * * Write archive data. */ STATIC void outwrite (idx, len) reg char *idx; uint len; { reg uint have; reg uint want; reg char *endx = idx + len; while ((want = endx - idx)) { while ((have = bufend - bufidx) == 0) outflush (NOTDONE); if (have > want) have = want; memcpy (bufidx, idx, have); bufidx += have; idx += have; total += have; } } /* * pass() * * Copy within the filesystem. */ STATIC VOIDFN pass (av) reg char **av; { reg int fd; reg char **avx; auto Stat sb; auto char name[PATHSIZE]; for (avx = av; *avx; ++avx) { if (gflag && **avx != '/') fatal (*avx, "Relative pathname"); if (STAT (*avx, &sb) < 0) fatal (*avx, syserr ()); if ((sb.sb_mode & S_IFMT) != S_IFDIR) fatal (*avx, "Not a directory"); } while ((fd = openin (name, NULL, &sb, (int *) 0)) >= 0) { if (passitem (name, &sb, fd, av)) VOID close (fd); if (vflag) { if (*uncompto) VOID fprintf (stderr, "%s -- uncompressed\n", uncompto); else VOID fprintf (stderr, "%s -- okay\n", name); } } } /* * passdata() * * Copy data to one file. Doesn't believe in input file * descriptor zero (see description of kludge in openin() * comments). Closes the provided output file descriptor. */ STATIC void passdata (from, ifd, to, ofd) char *from; reg int ifd; char *to; reg int ofd; { reg ssize_t got; reg ssize_t sparse; auto char block[FSBUF]; if (ifd) { VOID lseek (ifd, (off_t) 0, 0); sparse = 0; while ((got = read (ifd, block, sizeof (block))) > 0 && (sparse = fswrite (ofd, block, (uint) got)) >= 0) total += got; if (got) VOID warn (got < 0 ? from : to, syserr ()); else if (sparse > 0 && (lseek (ofd, (off_t) - sparse, 1) < 0 || writeall (ofd, block, sparse) != sparse)) VOID warn (to, syserr ()); } VOID close (ofd); } /* * passitem() * * Copy one file. Returns given input file descriptor. */ STATIC int passitem (from, asb, ifd, dir) char *from; Stat *asb; reg int ifd; reg char **dir; { reg int ofd; #ifdef linux_tstamp auto struct utimbuf tstamp; #else auto time_t tstamp[2]; #endif auto char to[PATHSIZE]; while (*dir) { if (nameopt (strcat (strcat (strcpy (to, *dir++), "/"), from)) < 0) continue; if ((ofd = openotty (to, asb, lflag ? linkto (from, asb) : linkfrom (asb,0), 1, Zflag)) < 0) continue; if (ofd > 0) passdata (from, ifd, to, ofd); #ifdef linux_tstamp tstamp.actime = tstamp.modtime = mflag ? timenow : asb->sb_mtime; VOID utime (to, &tstamp); #else tstamp[0] = tstamp[1] = mflag ? timenow : asb->sb_mtime; VOID utime (to, tstamp); #endif /* safety */ if (uncompressrun) { VOID xwait (uncompressrun, "passitem xwait()", TRUE); uncompressrun = 0; } } return (ifd); } STATIC VOIDFN process_arname (template) char *template; { int remain, len; char *sptr, *dptr; sptr=template; dptr=proc_arname; if(!sflagused) { strcpy(dptr,sptr); return; } remain = sizeof(proc_arname)-10; for (; remain>=0; sptr++) { *dptr = '\0'; if (*sptr == '%') { switch (*++sptr) { case 'V': /* volume number */ if (remain < 40) break; len = sprintf(dptr,"%u", arvolume); dptr += len; remain -= len; break; case 'S': /* volume size */ if (remain < 40) break; len = sprintf(dptr,"%llu", (unsigned long long) aruntil); dptr += len; remain -= len; break; default: *dptr++ = *sptr; remain--; break; } } else { *dptr++ = *sptr; remain--; } if (*sptr == '\0') break; } } /* * pipechld() * * Child portion of pipeline fork. */ STATIC int pipechld (mode, pfd) int mode; reg int *pfd; { reg char **av; auto char *arg[32]; av = arg; if ((*av = getenv ("SHELL")) && **av) ++av; else *av++ = "/bin/sh"; *av++ = "-c"; process_arname(arname + 1); *av++ = proc_arname; *av = NULL; if (mode) { VOID close (pfd[1]); VOID close (STDIN); VOID dup (pfd[0]); VOID close (pfd[0]); VOID close (STDOUT); VOID open ("/dev/null", O_WRONLY); } else { VOID close (STDIN); VOID open ("/dev/null", O_RDONLY); VOID close (pfd[0]); VOID close (STDOUT); VOID dup (pfd[1]); VOID close (pfd[1]); } if (ttyf >= 0) VOID close (ttyf); VOID execvp (arg[0], arg); VOID warn (arg[0], syserr ()); _exit (1); return 0; /* to avoid compiler warning */ } /* * pipeopen() * * Open an archive via a pipeline. Returns a file * descriptor, or -1 if unsuccessful. */ STATIC int pipeopen (mode) reg int mode; { auto int pfd[2]; if (pipe (pfd) < 0) return (-1); if ((pipepid = xfork ("pipeopen()", DIE)) == 0) pipechld (mode, pfd); if (mode) { VOID close (pfd[0]); return (pfd[1]); } else { VOID close (pfd[1]); return (pfd[0]); } } /* * pipewait() * * Await a pipeline. */ STATIC void pipewait () { reg int status; if (pipepid == 0) return; status = xwait (pipepid, "pipewait()", TRUE); pipepid = 0; if (status) fatal (arspec, "Pipeline error"); } /* * prsize() * * Print a file offset. */ STATIC void prsize (stream, size) FILE *stream; reg ulonglong size; { reg ulonglong n; if ((n = (size / (1024L * 1024L)))) { VOID fprintf (stream, "%lum+", (unsigned long)n); size -= n * 1024 * 1024; } if ((n = (size / 1024))) { VOID fprintf (stream, "%luk+", (unsigned long)n); size -= n * 1024; } VOID fprintf (stream, "%lu", (unsigned long) size); } #ifndef MKDIR /* * rmdir() * * Remove a directory via "/bin/rmdir". Sets errno to a * questionably sane value upon failure. */ STATIC int rmdir (name) reg char *name; { reg int pid; if ((pid = xfork ("rmdir()", DIE)) == 0) { VOID close (fileno (stdin)); VOID close (fileno (stdout)); VOID close (fileno (stderr)); VOID open ("/dev/null", O_RDWR); VOID dup (fileno (stdin)); VOID dup (fileno (stdin)); VOID execl ("/bin/rmdir", "rmdir", name, (char *) NULL); exit (1); } if (xwait (pid, "rmdir()", TRUE) == 0) return (0); errno = EACCES; return (-1); } #endif /* MKDIR */ /* * fswrite() * * Write a filesystem block. Seeks past sparse blocks. Returns * 0 if the block was written, the given length for a sparse * block or -1 if unsuccessful. */ STATIC ssize_t fswrite (fd, buf, len) int fd; char *buf; size_t len; { reg char *bidx; reg char *bend; /* * if -j (no sparse blocks) or compressrun (piping to uncompress utility) * then do not bother looking for empty buffers, just write the data. */ if (jflag || uncompressrun) return (writeall (fd, buf, len) == (int)len ? 0 : -1); bend = (bidx = buf) + len; while (bidx < bend) if (*bidx++) return (writeall (fd, buf, len) == (int)len ? 0 : -1); return (lseek (fd, (off_t) len, 1) < 0 ? -1 : (int)len); } /* * syserr() * * Return pointer to appropriate system error message. */ STATIC char * syserr () { static char msg[40]; #if 0 /* older version */ if (errno > 0 && errno < sys_nerr) return ((char *) sys_errlist[errno]); VOID sprintf (msg, "Unknown error (errno %d)", errno); return (msg); #else /* newer version, should be posix compliant and eliminate some compiler warnings */ char *pTmp = NULL; if (! (pTmp = strerror(errno))) { VOID sprintf (msg, "Unknown error (errno %d)", errno); pTmp = msg; } return (pTmp); #endif } /* * toc() * * Print archive table of contents. */ STATIC VOIDFN toc (av) reg char **av; { auto Stat sb; auto char name[PATHSIZE]; if (*av) fatal (*av, "Extraneous argument"); name[0] = '\0'; while (inhead (name, &sb) == 0) { if (ISCONTROL(&sb)) { /* list it */ if (namecmp (name,&sb) == 0) tocentry (name, &sb); /* process control file */ if(inentry (name, &sb) < 0) if (inskip (sb.sb_size) < 0) VOID warn (name, "Skipped file data is corrupt"); continue; } if (namecmp (name,&sb) == 0) tocentry (name, &sb); if (inskip (sb.sb_size) < 0) VOID warn (name, "File data is corrupt"); } } /* * tocentry() * * Print a single table-of-contents entry. */ STATIC void tocentry (name, asb) char *name; reg Stat *asb; { reg Time *atm; reg Link *from; reg Passwd *pwp; reg Group *grp; static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; int res; Link *linkp; Link *linknext; if(printbytepos) printf("%.0f ",(double)bytepos); if (vflag) { tocmode (asb->sb_mode); VOID printf (" %2d", (int)asb->sb_nlink); atm = localtime (&asb->sb_mtime); if ((pwp = getpwuid (asb->sb_uid))) VOID printf (" %-8s", pwp->pw_name); else VOID printf (" %-8u", (unsigned int)(asb->sb_uid)); if ((grp = getgrgid (asb->sb_gid))) VOID printf (" %-8s", grp->gr_name); else VOID printf (" %-8u", (unsigned int)(asb->sb_gid)); switch (asb->sb_mode & S_IFMT) { case S_IFBLK: case S_IFCHR: /* rdev==0 means that rdev is encoded in dev and ino, see out(). */ if(asb->sb_rdev==0) asb->sb_rdev=(asb->sb_dev<<16) + asb->sb_ino; VOID printf (" %3d, %3d", (int)major (asb->sb_rdev), (int)minor (asb->sb_rdev)); break; case S_IFREG: VOID printf (" %8lld", (long long int)(asb->sb_size)); /* VOID printf (" %8ld", (long unsigned int)(asb->sb_size)); */ break; default: VOID printf (" "); } VOID printf (" %3s %2d %02d:%02d:%02d %4d ", month[atm->tm_mon], atm->tm_mday, atm->tm_hour, atm->tm_min, atm->tm_sec, atm->tm_year + 1900); } { char *namedot = strrchr (name, '.'); if (Zflag && (asb->sb_mode & S_IFMT) == S_IFREG && (asb->sb_rdev & RDEV_NOTCOMPR) == 0 && (asb->sb_size > 0) && namedot && namedot[1] == 'z' && !namedot[2]) *namedot = '\0'; else namedot = 0; if (ISCONTROL(asb)) res = printf("//--"); if ((!useoutmodetoc) && flag0) res = printf ("%s%c", name, 0); else res = printf ("%s", name); /* to find out about broken pipe as early as possible */ if(res > 0) res = fflush(stdout); /* check for broken pipe on stdout, this ends the listing */ if(res<0) { if(errno == EPIPE) fatal("", syserr()); } if (vflag && namedot) VOID printf (" -- compressed"); } if (!flag0 && (vflag || lflag)) { from=NULL; if (asb->sb_nlink > 1) { if(!useoutmodetoc) { if ((from = linkfrom (asb,1))) VOID printf (" -> %s", from->l_path->p_name); else VOID linkto (name, asb); } else { /* need special logic to resolve hard link info when in -o mode: because of side effects of linkfrom and linkto, which were already called for the file entry, we cannot use them here again */ if((asb->sb_mode & S_IFMT) != S_IFDIR) { for (linkp = *(linkhash (asb->ino_orig)); linkp; linkp = linknext) { if (linkp->l_ino == asb->ino_orig && linkp->l_dev == asb->sb_dev && strcmp(linkp->l_path->p_name,name) ) { VOID printf (" -> %s", linkp->l_path->p_name); break; } linknext = linkp->l_forw; } } } } #ifdef S_IFLNK if (((asb->sb_mode & S_IFMT) == S_IFLNK)&&(from==NULL)) VOID printf (" S-> %s", asb->sb_link); #endif /* S_IFLNK */ } if ((!flag0)||useoutmodetoc) putchar ('\n'); } /* * tocmode() * * Fancy file mode display. */ STATIC void tocmode (mode) reg mode_t mode; { switch (mode & S_IFMT) { case S_IFREG: putchar ('-'); break; case S_IFDIR: putchar ('d'); break; #ifdef S_IFLNK case S_IFLNK: putchar ('l'); break; #endif /* S_IFLNK */ #ifdef S_IFSOCK case S_IFSOCK: putchar ('s'); break; #endif /* S_IFSOCK */ case S_IFBLK: putchar ('b'); break; case S_IFCHR: putchar ('c'); break; #ifdef S_IFIFO case S_IFIFO: putchar ('p'); break; #endif /* S_IFIFO */ default: VOID printf ("[%o]", (uint)(mode >> S_IFSHF)); } putchar (mode & 0400 ? 'r' : '-'); putchar (mode & 0200 ? 'w' : '-'); putchar (mode & 0100 ? mode & 04000 ? 's' : 'x' : mode & 04000 ? 'S' : '-'); putchar (mode & 0040 ? 'r' : '-'); putchar (mode & 0020 ? 'w' : '-'); putchar (mode & 0010 ? mode & 02000 ? 's' : 'x' : mode & 02000 ? 'S' : '-'); putchar (mode & 0004 ? 'r' : '-'); putchar (mode & 0002 ? 'w' : '-'); putchar (mode & 0001 ? mode & 01000 ? 't' : 'x' : mode & 01000 ? 'T' : '-'); } /* * usage() * * Print a helpful message and exit. */ STATIC void usage () { VOID fprintf (stderr, "\n\ Usage: [filename generator] | %s -o [options] archive : write archive\n\ %s -i [options] archive : install archive\n\ %s -t [options] archive : list table-of-contents of archive\n\ %s -r [options] archive : verify archive against filesystem\n\ Frequently used options:\n\ General: -v : verbose\n\ -Z : with -o: gzip files when writing them to the archive,\n\ with -i/t/r: handle archive written with -Z option\n\ -5 : abort instead of creating archive incompatible with cpio\n\ Tape: -s [volsize] : size of volume, can have suffix k or m or g\n\ -b [blocksize] : block size (default is 5120)\n\ -c [count] : buffer count blocks between doing I/O\n\ Install: -n : protect newer files -k : skip corrupt data at beginning\n\ Select: -y [pattern] : only process files matching pattern\n\ -Y [pattern] : do not process files matching pattern\n", myname, myname, myname, myname); VOID fprintf (stderr,"Version %s dated %s\n", VERSION, DATE); exit (1); } /* * warn() * * Print a warning message. Always returns -1. */ STATIC int warn (what, why) char *what; char *why; { time_t dietime; warnings++; dietime = time ((time_t *) NULL); VOID fprintf (stderr, "%s: \"%s\": %s\n", myname, what, why); /* ctime() provides the \n for the fprintf. */ if (logfile != (FILE *) 0) VOID fprintf (logfile, "%s: \"%s\": %s (disk %u) at %s", myname, what, why, arvolume, ctime (&dietime)); return (-1); } STATIC int warn_nocount (what, why) char *what; char *why; { warnings--; return warn(what,why); } /* * warnarch() * * Print an archive-related warning message, including * an adjusted file offset. Always returns -1. */ STATIC int warnarch (msg, adjust) char *msg; off_t adjust; { warnings++; VOID fprintf (stderr, "%s: \"%s\" [offset ", myname, arspec); prsize (stderr, total - adjust); VOID fprintf (stderr, "]: %s\n", msg); return (-1); } /* * xfork() * * Create a child. */ STATIC int xfork (what, die) reg char *what; int die; { reg int pid; reg Child *cp; reg int idx; static uint delay[] = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144}; /* flush error logfile before fork */ if(logfile != (FILE *)0) fflush(logfile); /* also flush stdout and stderr */ fflush(stdout); fflush(stderr); #ifdef SIGCHLD VOID signal (SIGCHLD, SIG_DFL); /* SysV mostly... */ #else VOID signal (SIGCLD, SIG_DFL); /* some SysV's... */ #endif for (idx = 0; (pid = fork ()) < 0; ++idx) { if (idx == sizeof (delay)) { if (die) fatal (arspec, syserr ()); else return (-1); } VOID warn_nocount (what, "Trouble forking..."); if (Fflag && !die) /* give up and go on... */ return (-1); sleep (delay[idx]); } if (idx) VOID warn_nocount (what, "...successful fork"); cp = (Child *) memget (sizeof (*cp)); cp->c_pid = pid; cp->c_flags = 0; cp->c_status = 0; cp->c_forw = children; children = cp; return (pid); } /* * xpause() * * Await a child. */ STATIC void xpause () { reg Child *cp; reg int pid; auto int status; do { while ((pid = wait (&status)) < 0) ; for (cp = children; cp && cp->c_pid != pid; cp = cp->c_forw) ; } while (cp == NULL); cp->c_flags |= CF_EXIT; cp->c_status = status; } /* * xwait() * * Find the status of a child. */ STATIC int xwait (pid, what, compstat2) reg int pid; char *what; int compstat2; { reg int status; reg Child *cp; reg Child **acp; auto char why[100]; for (acp = &children; (cp = *acp); acp = &cp->c_forw) if (cp->c_pid == pid) break; if (cp == NULL) fatal (what, "Lost child"); while ((cp->c_flags & CF_EXIT) == 0) xpause (); status = cp->c_status; *acp = cp->c_forw; free ((char *) cp); if (status == 0) return (0); if (status & 0377) VOID sprintf (why, "Killed by signal %d%s", status & 0177, status & 0200 ? " -- core dumped" : ""); else VOID sprintf (why, "Exit %d", (status >> 8) & 0377); if ((!compstat2 && (((status >> 8) & 0377) != 2)) || compstat2) return (warn (what, why)); else return ((status >> 8) & 0377); } /* right now we verify the whole disk */ void verify (error) int error; { char *verbuf; char *buf; reg time_t began; int got, len; uint readamt; auto char msg[200]; auto char answer[20]; if (*arname == '!') { VOID warn ("Can't verify a piped command", ""); return; } if (!error) { if ((verbuf = malloc (arbsize)) == NULL) fatal (arspec, "Cannot allocate Verify I/O buffer"); /* * close as O_WRONLY and reopen as O_RDONLY to verify (on a * disk this is a good thing) */ /* fprintf(stderr,"closing..\n"); */ nextclos (); verifycnt++; if (nextopen (O_RDONLY) < 0) { VOID warn ("re-open for verify failed", ""); error = 1; } else { #ifdef linux /* flush the floppy cache. (added by KH) */ if(Fflag) { /* fprintf(stderr,"flushing..\n"); */ if (ioctl(arfd,FDFLUSH,NULL) < 0) warn(arname,"can't flush device cache."); } #endif fprintf (stderr, "Verifying disk %u...\n", arvolume ); for (buf = buffer; (len = bufidx - buf);) { readamt = min ((uint)len, arbsize); if ((uint)(got = read (arfd, verbuf, readamt)) == readamt) { #ifdef HAVEMEMCMP if (memcmp (verbuf, buf, (size_t)got) != 0) #else if (bcmp (verbuf, buf, got) != 0) #endif { VOID warn ("Verify failed", ""); error = 1; break; } else buf += got; } else { VOID warn ("Read returned short", ""); fprintf (stderr, "Read %d wanted %d bytes\n", got, readamt); error = 1; break; } } } free (verbuf); } if (error) { int answernum = 0; nextclos (); for (;;) { began = time ((time_t *) NULL); VOID sprintf (msg, "\ %s: %s of disk %u has FAILED!\07\n\ \tEnter 1 to RETRY this disk\n\ \tEnter 2 to REFORMAT this disk before a RETRY\n\07\n%s", myname, ((error && (verifycnt == 0)) ? "Writing" : "Verify"), arvolume, hidequit ? "" : "\tEnter \"quit\" to ABORT the backup\n\07"); nextask (msg, answer, sizeof (answer)); timewait += time ((time_t *) NULL) - began; answernum = atoi (answer); if (answernum == 1) /* note: recursive here... */ { /* if we can't open, try again */ if (nextopen (O_WRONLY) < 0) continue; } else if ((answernum == 2) ) { if (system (formatcmd) != 0) { fprintf (stderr, "Format failed!\n"); answernum = 0;/* error, don't autowrite */ } else { fprintf (stderr, "Format successful!\n"); /* if we can't open, try again */ if (nextopen (O_WRONLY) < 0) continue; return; } } else if (strcmp (answer, "quit") == 0) fatal (arspec, "Quitting during a verify"); } } } int writedisk (realwrite) int realwrite; { reg char *buf; reg int got; reg uint len; int wrstat; static int firsttime = 1; /* * If we're double buffering wait for any pending writes to * complete before starting next writing */ if (fflag) outwait (); /* * if we are a floppy open the disk at the last moment * call verify w/ an error if the disk can't be opened */ if (Fflag) { /* There's something funny in the program in that writedisk() gets called when there is nothing in the buffer to write; it apparently gets called twice for each buffer when using the -s double buffer option. I can't figure out the complex and undocumented definitions of bufidx and bufend well enough to stop this double calling, but the only harm done by the second call is if we call for another disk to be loaded. So we make sure we only call for a disk if there's actually data to write on it (i.e. bufidx > buffer). - Bryan Henderson 98.08.12 */ if( ! firsttime && bufidx > buffer ) next( O_WRONLY, "Next disk needed"); else while( nextopen (O_WRONLY) < 0) { verifycnt = 0; verify (1); } firsttime = 0; } /* * If we're double buffering spawn a child to do the * actual writing, and return immediately */ if( fflag ) { outpid = xfork ("outflush()", DIE); if( outpid != 0 ) { /* what does this do? It seems to be needed to let -s -f work */ arleft -= bufidx - buffer; nextclos(); /* added by KH to make verify work */ return(0); } else VOID nice (-10); } do { wrstat = 0 ; for (buf = buffer; (len = bufidx - buf);) { if ((got = write (arfd, buf, *arname == '!' ? len : min (len, arbsize))) > 0) { buf += got; if (realwrite) arleft -= got; } else if (fflag) { VOID warn (arspec, got < 0 ? syserr () : "Apparently full -- archive will be incomplete"); _exit (1); } else if (got < 0) { if(errno==EPIPE) fatal(arspec, syserr()); if(errno==ENOSPC) { /* For DAT/DDS -- RAM, 1998/05/19 */ next (O_WRONLY, "No space left on device"); continue; /* Redo failed write on new volume */ } if(!Jflag) VOID fatal (arspec, syserr ()); else { VOID warn (arspec, syserr ()); anycorrupt=1; } wrstat = 1; break; } else { if (Fflag && verifyflag) { /* can't recover from this one, break out of loop and let verify detect the mess */ VOID warn (arspec, "Apparently full already"); wrstat = 1; break; } next (O_WRONLY, "Apparently full"); /* added continue, this creates correct behavior (no unwritten data) when changing to next volume on write()=0 -- KH */ continue; } } if (Fflag && verifyflag) { verifycnt = 0; verify ( wrstat ); } /* Added KH: bug fix for double prompting bug if no -K and -f given */ /* It took me far too long to find this. IMO it was a big mistake to use arleft, which usually denotes the space left in the buffer, to denote the space left on the device in this routine. Talking about spaghetti variables. I'd rather not think about what will happen if the write/verify error handling stuff gets used. */ if (Fflag && !verifyflag) arleft = aruntil; } while( wrstat && Fflag && verifyflag ); if( fflag ) _exit( wrstat ); else return ( wrstat ); return 0; /* to avoid compiler warning */ } void goodbye (status) int status; { /* log that we died */ if (status && (logfile != (FILE *) 0)) { time_t dietime; dietime = time ((time_t *) NULL); VOID fprintf (logfile, "%s: the backup has failed (status %d), died at %s", myname, status, ctime (&dietime)); } exit (status); } #ifdef MYTEMPNAM /* author: Monty Walls * written: 4/17/89 * Copyright: Copyright (c) 1989 by Monty Walls. * Not derived from licensed software. * * Permission to copy and/or distribute granted under the * following conditions: * * 1). This notice must remain intact. * 2). The author is not responsible for the consequences of use * this software, no matter how awful, even if they * arise from defects in it. * 3). Altered version must not be represented as being the * original software. */ #define MAXPREFIX 5 #define TMPNAME "tmp" #ifndef P_tmpdir #define P_tmpdir "/tmp" #define L_tmpnam 14 #endif extern char *mktemp (); extern char *strcat (); extern char *strcpy (); extern char *getenv (); char * tempnam (dir, name) char *dir; char *name; { char *buf, *tmpdir; /* * This is kind of like the chicken & the egg. * Do we use the users preference or the programmers? */ #ifdef USE_ENV_VAR if ((tmpdir = getenv ("TMPDIR")) == (char *) NULL) { if ((tmpdir = dir) == (char *) NULL) tmpdir = P_tmpdir; } #else if ((tmpdir = dir) == (char *) NULL) { if ((tmpdir = getenv ("TMPDIR")) == (char *) NULL) tmpdir = P_tmpdir; } #endif /* now lets check and see if we can work there */ if (access (tmpdir, R_OK + W_OK + X_OK) < 0) return ((char *) NULL); if (name == (char *) NULL) name = TMPNAME; else if (strlen (name) > MAXPREFIX) name[5] = '\0'; /* this is according to SYS5 */ /* the magic value 2 is for '\0' & '/' */ if ((buf = (char *) malloc (L_tmpnam + strlen (tmpdir) + 2)) == (char *) NULL) return ((char *) NULL); strcpy (buf, tmpdir); strcat (buf, "/"); strcat (buf, name); strcat (buf, ".XXXXXX"); /* pass our completed pattern to mktemp */ return (mktemp (buf)); } #endif /* MYTEMPNAM */ afio-2.5.2/afio.h000066400000000000000000000342231340024410000135150ustar00rootroot00000000000000/* afio.h defines for afio. */ #ifdef SYSTIME #include #else /* SYSTIME */ #include #endif /* SYSTIME */ #ifdef CTC3B2 #include #include #endif /* CTC3B2 */ #ifdef MYTEMPNAM #include #endif #ifdef USESHMEM #include #include #define NUMSHKEYS 20 #define SHMEMSIZE 262144 /* 2^18 (dev3b1) */ #endif /* done writing to the archive */ #define FALSE 0 #define TRUE 1 #define NOTDONE 0 #define DONE 1 #define NODIE 0 #define DIE 1 /* Note for porters: see the PORTING file if your compiler does not support unsigned long long. */ typedef unsigned long long ulonglong; /* KH */ #define MEMFD 10000 #define ZIPFD 10001 extern int zipfdfd; /* flags for the st_rdev field of regular files */ /* file is not compressed, ignore any .z extension */ #define RDEV_NOTCOMPR 1 /* file is a control file */ #define RDEV_ISCONTROL 2 /* An archive entry is a control file if it is a regular file and if the ISCONTROL flag is set. The filename is not important. */ #define ISCONTROL(sb) ((((sb)->sb_mode & S_IFMT) == S_IFREG)&&((sb)->sb_rdev & RDEV_ISCONTROL)) /* pseudo filename for control files */ #define CONTROLNAME "CONTROL_FILE" /* label value if no label given */ #define NOLABEL "no_label" /* * Address link information base. */ #define linkhash(ino) \ (linkbase + ((ino) & 0xffff) % nel(linkbase)) /* * Mininum value. */ #define min(one, two) \ (one < two ? one : two) /* * Number of array elements. */ #define nel(a) \ (sizeof(a) / sizeof(*(a))) /* * Remove a file or directory. */ #define afremove(name, asb) \ (((asb)->sb_mode & S_IFMT) == S_IFDIR ? rmdir(name) : unlink(name)) /* * Swap bytes. */ #define swab(n) \ ((((unsigned short)(n) >> 8) & 0xff) | (((unsigned short)(n) << 8) & 0xff00)) /* * Cast and reduce to unsigned short. */ #define ush(n) \ (((unsigned short) (n)) & 0177777) #define ulo(n) \ (((unsigned long) (n)) & 0xFFFFFFFFL) #define ull(n) \ (((unsigned long long) (n)) & 0xFFFFFFFFFFFFFFFFLL) /* 1234567890123456 */ /* * Definitions. */ #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 */ /* binary format */ #define M_BINARY 070707 /* Binary magic number */ #define M_STRLEN 6 /* ASCII magic number length */ /* (old) ASCII format */ #define H_STRLEN 70 /* old ASCII header string length */ #define M_ASCII "070707" /* old ASCII magic number */ #define H_PRINT "%06o%06o%06o%06o%06o%06o%06o%011lo%06o%011lo" /* H_SCAN is obsolete, replaced by PH_SCAN to be more portable. */ #define H_SCAN "%6ho%6ho%6ho%6ho%6ho%6ho%6ho%11lo%6o%11lo" #define PH_SCAN "%6lo%6lo%6lo%6lo%6lo%6lo%6lo%11lo%6o%11lo" /* Below is a handy piece of ASCII art which can be used to decode old ASCII format headers. |23456|23456|23456|23456|23456|23456|23456|23456|23456789ab|23456|23456789ab|- | hdr| dev| ino| mode| uid| gid|nlink| rdev| mtime|nmlen| size|n */ /* extended ASCII format */ #define H_STRLEN2 75 /* extended ASCII header string length */ #define M_ASCII2 "070717" /* extended ASCII magic number */ #define H_PRINT2 "%06o%011lo%06o%06o%06o%06o%06o%011lo%06o%011lo" #define PH_SCAN2 "%6lo%11lo%6lo%6lo%6lo%6lo%6lo%11lo%6o%11lo" /* Below is a handy piece of ASCII art which can be used to decode large ASCII format headers. |23456|2345678|234567890123456m|23456|2345678|2345678|2345678|2345678|234567890123456n|234|234|234s|234567890123456:|- | hdr| dev| inoM| mod| uid| gid| nlink| rdev| mtimeN|nml|flg|xszS| size:|n */ /* large ASCII format */ #define H_STRLEN3 110 /* large ASCII string length */ #define M_ASCII3 "070727" /* large ASCII magic number */ #define H_COUNT3 12 #define H_PRINT3 "%08lX%016llXm%06lo%08lX%08lX%08lX%08lX%016llXn%04X%04X%04Xs%016llX:" #define PH_SCAN3 "%8lX%16llXm%6lo%8lX%8lX%8lX%8llX%16llXn%4X%4X%4Xs%16llX:" typedef struct { long unsigned int PSt_dev; long unsigned int PSt_ino; long unsigned int PSt_mode; long unsigned int PSt_uid; long unsigned int PSt_gid; long unsigned int PSt_nlink; long unsigned int PSt_rdev; long unsigned int PSt_mtime; long unsigned int PSt_size; } PStat; typedef struct { long unsigned int PSt_dev; unsigned long long PSt_ino; long unsigned int PSt_mode; long unsigned int PSt_uid; long unsigned int PSt_gid; long unsigned int PSt_nlink; unsigned long long PSt_rdev; unsigned long long PSt_mtime; unsigned long long PSt_size; } PHStat; #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 */ /* the three flags below are used in the match.c code, they allow us to store 3 types of patterns in a single `all patterns' linked list. (the use of a single list is for historical reasons, it is not the most ovbious design for the current needs) */ #define PATTYPE_MATCH 0 #define PATTYPE_NOMATCH 1 #define PATTYPE_EXT 2 #ifndef PRG_COMPRESS #define PRG_COMPRESS "compress" #endif /* * Some versions of the portable "C" compiler (PCC) can't handle * pointers to functions returning void. */ #ifdef VOIDFIX #define VOIDFN void /* Expect "void (*fnptr)()" to work */ #else /* VOIDFIX */ #define VOIDFN int /* Avoid PCC "void (*fnptr)()" bug */ #endif /* VOIDFIX */ /* * Trailer pathnames. All must be of the same length. */ #define TRAILER "TRAILER!!!" /* Archive trailer (cpio compatible) */ #define TRAILZ 11 /* Trailer pathname length (including null) */ /* * Open modes; there is no with v7 UNIX. */ #ifdef HAVEFCNTL #include #else #define O_RDONLY 0 /* Read-only */ #define O_WRONLY 1 /* Write-only */ #define O_RDWR 2 /* Read/write */ #endif /* * V7 and BSD UNIX use old-fashioned names for a couple of * string functions. */ #ifdef INDEX #define strchr index /* Forward character search */ #define strrchr rindex /* Reverse character search */ #endif /* INDEX */ /* * Some compilers can't handle void casts. */ #ifdef NOVOID #define VOID /* Omit void casts */ #else /* NOVOID */ #define VOID (void) /* Quiet lint about ignored return values */ #endif /* NOVOID */ /* * Adb is more palatable when static functions and variables are * declared as globals. Lint gives more useful information when * statics are truly static. */ #ifdef lint #define STATIC static /* Declare static variables for lint */ #else /* lint */ #define STATIC /* Make static variables global for adb */ #endif /* lint */ /* * Simple types. */ typedef struct group Group; /* Structure for getgrgid(3) */ typedef struct passwd Passwd; /* Structure for getpwuid(3) */ typedef struct tm Time; /* Structure for localtime(3) */ #ifdef S_IFLNK /* * File status with symbolic links. Kludged to hold symbolic * link pathname within structure. */ typedef struct { struct stat sb_stat; char sb_link[PATHSIZE]; ino_t ino_orig; /* used in -o mode to support -ovv */ } 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 */ /* * File status without symbolic links. */ 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 */ /* * Binary archive header (obsolete). */ typedef struct { short b_dev; /* Device code */ ushort b_ino; /* Inode number */ ushort b_mode; /* Type and permissions */ ushort b_uid; /* Owner */ ushort b_gid; /* Group */ short b_nlink; /* Number of links */ short b_rdev; /* Real device */ ushort b_mtime[2]; /* Modification time (hi/lo) */ ushort b_name; /* Length of pathname (with null) */ ushort b_size[2]; /* Length of data */ } Binary; /* * Child process structure. */ typedef struct child { struct child *c_forw; /* Forward link */ int c_pid; /* Process ID */ int c_flags; /* Flags (CF_) */ int c_status; /* Exit status */ } Child; /* * Child process flags (c_flags). */ #define CF_EXIT (1<<0) /* Exited */ /* * Hard link sources. One or more are chained from each link * structure. */ typedef struct name { struct name *p_forw; /* Forward chain (terminated) */ struct name *p_back; /* Backward chain (circular) */ char *p_name; /* Pathname to link from */ } Path; /* * File linking information. One entry exists for each unique * file with with outstanding hard links. */ typedef struct link { struct link *l_forw; /* Forward chain (terminated) */ struct link *l_back; /* Backward chain (terminated) */ dev_t l_dev; /* Device */ ino_t l_ino; /* Inode */ ino_t l_ino_ar; /* Inode nr we will put in the archive */ ushort l_nlink; /* Unresolved link count */ time_t l_mtime; /* Modification time */ ushort l_mode; /* mode */ off_t l_size; /* Length */ Path *l_path; /* Pathname(s) to link from */ } Link; /* * Directory information. One entry exists for each directory * in order to update it with the correct timestamps. */ typedef struct dir { struct dir *d_forw; /* Forward chain (terminated) */ time_t d_mtime; /* Modification time */ char *d_name; /* Pathname of directory */ } Dir; /* * Internal functions. */ VOIDFN copyin (char **); VOIDFN copyout (char **); void compressfile (int *, char *, Stat *, int *); int dirchg (char *, char *); int dirmake (char *, Stat *); int dirneed (char *); void fatal (char *,char *); void goodbye (int); VOIDFN in (char **); void inalloc (uint); int inascii (char *, char *, Stat *); int inascii2 (char *, char *, Stat *); int inascii3 (char *, char *, Stat *); int inavail (char **, uint*); int inbinary (char *, char *, Stat *); int indata (int, off_t, char*); int inentry (char *, Stat *); int infill (void); int inhead (char *, Stat *); int inread (char *, uint); int inskip (off_t); int inswab (char *, char *, Stat *); int lineget (FILE *, char *, int); void linkalso (Link *, char * ); Link *linkfrom (Stat *, int); void linkleft (void); Link *linkto (char *, Stat *); #ifndef MEMCPY char *memcpy (char *, char *, uint); #endif char *memget (uint); char *memstr (char *); #ifndef MKDIR int mkdir (char *, ushort); #endif void nameadd (char *, int); int namecmp (char *, Stat *); int namecmp_ext (char *); int nameopt (char *); void next (int, char *); int runpromptscript (char *); void nextask (char *, char *, int); void nextclos (void ); int nextopen (int); int openin (char *, char *, Stat *, int *); int openotty (char *, Stat *, Link *, int, int); int openqtty (void); int options (int, char **, char *); VOIDFN out (char **); void outalloc (size_t); size_t outavail (char **); int outdata (int, char*, off_t); void outdatazip (int, char*, off_t); /* added KH */ void waitforgzip(void); /* added KH */ void outdatamem (char *, off_t); /* added KH */ void memreset(void); /* added KH */ int memread(char *buf,int count); /* added KH */ void memfree(void); /* added KH */ int nameaddfile(char *, int, int); /* added KH */ void outeof (char *, uint); void outflush (int); void outhead (char *, Stat *); void outhead2 (char *, Stat *); void outhead3 (char *, Stat *); void outpad (off_t); void outwait (void); void outwrite (char *,uint); VOIDFN pass (char **); void passdata (char *, int, char*, int); int passitem (char *, Stat *,int, char**); int pipechld (int, int *); int pipeopen (int ); void pipewait (void); void prsize (FILE *, ulonglong); VOIDFN readcheck (char **); VOIDFN process_arname (char *); #ifndef MKDIR int rmdir (char *); #endif #if !defined (linux) && !defined(__FreeBSD__) && !defined(sun) && !defined(__CYGWIN32__) VOIDFN (*signal ())(); #endif ssize_t fswrite (int, char*, size_t); char *syserr (void); VOIDFN toc (char **); void tocentry (char *, Stat *); void tocmode (mode_t); void usage (void); void verify (int); int warn (char *, char *); int warn_nocount (char*, char *); int warnarch (char *, off_t); int writedisk (int); int xfork (char *, int); void xpause (void); int xwait (int, char *, int); void mail(char *who,int vol,char *archive); ssize_t writeall(int, const char*, size_t); int incheckentry(char *, Stat *); int incheckdata (int fd, off_t size, char *name, Stat *asb, int comp); Link * linkinode16 (dev_t dev, ino_t ino); int opencontrolscript (char *name); int openincheck(char *, Stat *, int *,int); void mayberewind(void); int fixinsecure (char *); extern void add_arg(char *arg); extern char *compress_arg_list[]; extern short lflag; extern short hflag; extern int gzipfactor; extern off_t maxmem; extern long compthreshold; extern int ignoreslash; extern short Zflag; extern int arfd; extern int extcasesens; extern int forceZflag; extern char *compressprog; extern int compressargs; extern int rewindfd; extern char *ignorewarnings; extern int readcompexts(char*); ulonglong optsize (char *); void update_aruntil(void); extern ulonglong maxsizetocompress; extern short noglob; extern short flag0; afio-2.5.2/afio.lsm000066400000000000000000000013571340024410000140630ustar00rootroot00000000000000Begin4 Title: AFIO Version: 2.5.2 Entered-date: 2018-11-30 Description: Archiver & backup program with builtin compression Afio makes cpio-format archives. Afio can make compressed archives that are much safer than compressed tar or cpio archives. Afio is best used as an `archive engine' in a backup script. Supports files greater than 2 GB. Author: Mark Brukhartz, Koen Holtman, et al. Maintained-by: koen.holtman@ieee.org (Koen Holtman) Primary-site: http://members.chello.nl/~k.holtman/afio-2.5.2.tgz Alternate-site: http://members.chello.nl/~k.holtman/afio.html Alternate-site: http://freecode.com/projects/afio/ Platforms: gcc, gzip Copying-policy: free Keywords: backup archive compression gzip afio cpio fault tolerant End afio-2.5.2/afio_license_issues_v5.txt000066400000000000000000001076201340024410000176160ustar00rootroot00000000000000 Issues with the afio license text identified in 2008 ==================================================== About afio ========== Afio is a fault-tolerant archiver/backup tool for Unix systems. Afio was created in 1985 by Mark Brukhartz. Since then, many contributers and maintainers have added features and bug fixes. Afio is similar to Unix tools like tar, cpio, star, and pax. However, as a feature that these other tools lack: afio has the ability to make compressed archive files that are very fault tolerant against byte errors. This fault tolerant compression has attracted a user base that has been sufficiently large to keep afio alive as a maintained piece of software. Afio project information and link to sources: http://freshmeat.net/projects/afio/ About this note =============== In 2008, several people have raised the question if afio can be considered 'free' software by modern standards, see for example https://bugzilla.redhat.com/show_bug.cgi?id=449037 http://spot.livejournal.com/303000.html http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=509287 http://www.mail-archive.com/debian-legal@lists.debian.org/index.html#39478 A number of separate issues were raised in these discussions, this note tries to identify and address all of them. The answer to the question if afio is free depends partly on the definition of free. This note will not try to define the true meaning of free. The main goal of this note is to help the reader to determine if afio is 'free software' or 'open source' or 'freely distributable' by the definition chosen by the reader. To meet that goal, various valid but sometimes contradictory lines of reasoning about 'free' will be described and discussed. This note was written by Koen Holtman (the current afio maintainer) in January/February 2009, based on a review of the discussions on the web and further e-mail discussions with a number of people. In this note, the term 'FOSS' is used to refer to the broad class of free/open/etc software in general. The term 'Linux distro' is used to refer to any GNU/Linux distribution. Disclaimer: the author of this note is not a lawyer, nor does he play one on TV. Full afio license text ====================== The full afio license text (for afio 2.4.6 and later) is reproduced in this section. * afio.c * * Manipulate archives and files. * * This software was written by Mark Brukhartz at Lachman Associates, * Inc.. Additional code was written by a large cast of people. * * Licensing and (re)distribution * ------------------------------ * * THE SUMMARY INFORMATION BELOW WAS WRITTEN FOR THE BENEFIT OF * SOFTWARE DISTRIBUTORS * * Because of historical reasons, different parts of this software * package are covered by different licenses. However: * * A) This software package as a whole may be re-distributed by any * method that satisfies the conditions of both the Perl "Artistic * License" and the GNU Library General Public License. * * B) According to the theory.html file of the Sunsite Archive * Maintainers, this implies that the correct LSM template field * is: * * Copying-policy: LGPL * * C) This software package can also be re-distributed under * particular conditions that are _weaker_ than the Perl "Artistic * License" combined with the GNU Library General Public License. * Redistribution need only satisfy all four license notices below. * * Disclaimer: I am not a lawyer, and neither are the Sunsite Archive * Maintainers. * * END OF SUMMARY INFORMATION * * ------------------------------------------------------------------ * * License notice 1, covering part of this software package. * * [Covers the original 1985 afio code] * * Copyright (c) 1985 Lachman Associates, Inc.. * * This software was written by Mark Brukhartz at Lachman Associates, * Inc.. It may be distributed within the following restrictions: * (1) It may not be sold at a profit. * (2) This credit and notice must remain intact. * This software may be distributed with other software by a commercial * vendor, provided that it is included at no additional charge. * * * [Note: it is believed that condition 5 of the Perl "Artistic * License" implies the intent of restriction (1) above.] * * -------- * * ** License notice 2, covering part of this software package. * * [Covers the tempnam function] * * Copyright: Copyright (c) 1989 by Monty Walls. * Not derived from licensed software. * * Permission to copy and/or distribute granted under the * following conditions: * * 1). This notice must remain intact. * 2). The author is not responsible for the consequences of use * this software, no matter how awful, even if they * arise from defects in it. * 3). Altered version must not be represented as being the * original software. * * -------- * * ** License notice 3, covering part of this software package. * * [Covers the contents of the gnu.fnmatch.tar.gz file] * * Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; see the file COPYING.LIB. If * not, write to the Free Software Foundation, Inc., 675 Mass Ave, * Cambridge, MA 02139, USA. * * -------- * * ** License notice 4, covering part of this software package. * * [Covers the remainder of this software package] * * Additional code was written by a large cast of people. * * All additional code may be redistributed under the conditions of * license notice 1. * * Note that the authors of the additional code retain the right to * allow for the re-distribution of their code under weaker (and less * exotic) conditions. * * -------- * * ** WARRANTY NOTICE * * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * [End of licensing and redistribution section] The remaining sections of this note address issues raised with this license. The most important issue is issue number 2. Issue number 1 is about the question: why bother at all? Issue 1. Why bother distributing afio if there are alternatives like tar and cpio with a standard OSI/FSF approved free license? ================================================================ - Issue 1 description The afio license is not a standard OSI/FSF approved free license, in fact it includes text written in 1985 that is widely considered to be problematic. There are several tools, with arguably better licenses, that are similar to afio, e.g. gnu tar (GPL v3) gnu cpio (GPL v3) star (CDDL+GPL, though there seem to be some disputes about license compatibility) heirloom cpio/pax (free license) pax from Keith Muller So would it not be a good thing to remove afio from a Linux distro? Removal would make a) the license situation of the distro more easy/pure while b) not removing any functions people need. - Response to issue 1 1) Utility argument The above mentioned alternatives to afio do not provide everything that afio provides, so removing it from a Linux distro would decrease the utility of the distro. Some users of the distro will miss it if it is taken out. afio has one unique feature (fault tolerant compressed archives) that all of the above lack. Also, it has several specialized options that the other tools lack, and there are deployed backup scripts that rely on these specialized options. Afio is also very mature (bug-free) and portable because of its age and user base. The user base includes power users backing up large and complex filesystems, and these power users have historically found and fixed very obscure bugs. This maturity of afio is a big advantage in a backup tool -- a written-from-scratch replacement would take a long time to be equally mature. Several FOSS backup packages support afio as an archive engine, and some were designed specifically around afio. 2) Community/historical argument Afio is a FOSS project that started in 1985 and has grown 4-fold in code size and features since. Including afio into a distro shows to potential FOSS contributers just how long such software can remain useful, and celebrates the continuity between the current Web based FOSS community with the historical UNIX/USENET news based FOSS community. Issue 2. License places limits on redistribution by some parties ================================================================ - Issue 2 description Several definitions of 'free' software include the requirement that any party should be able to re-distribute it. For example. Debian Free Software Guideline 1 (DFSG#1) states: The license of a Debian component may not restrict any party from selling or giving away the software as a component of an aggregate software distribution containing programs from several different sources. [...] (See: http://www.debian.org/social_contract.en.html ) The following part of the afio license can be read to contradict this: * This software was written by Mark Brukhartz at Lachman Associates, * Inc.. It may be distributed within the following restrictions: * (1) It may not be sold at a profit. * (2) [...] * This software may be distributed with other software by a commercial * vendor, provided that it is included at no additional charge. The problem is that the last two lines of the above text do allow the 'selling or giving away the software as a component of an aggregate software distribution', but they ONLY allow this, when the selling is at a profit, if you are a 'commercial vendor'. So for example a private individual, who cannot be considered a commercial vendor, would be prevented by this license from burning and re-selling a CD including afio at a profit. Therefore the license restricts some parties from re-distribution, violating DFSG#1 and also DFSG#5, making afio non-free. - Response to issue 2 The above interpretation of the license text is a possible one, but it is not the only possible interpretation. If one wants to make a case for afio satisfying DFSG#1, then one has to argue that the designation 'commercial vendor' designates very broadly ANY party that is engaged in 'selling at a profit'. A private individual re-selling a CD including afio at a profit could claim to be acting as a 'commercial vendor' as far as the meaning of the license text is concerned. Is such a claim credible? I believe it is. Below are the two arguments why 'commercial vendor' covers any party selling at a profit. 1) Argument by authority of the author We managed to contact Mark Brukhartz, and after discussion of the issues he provided a statement on the meaning of the license text that he wrote. The text is: * This software was written by Mark Brukhartz at Lachman Associates, * Inc.. It may be distributed within the following restrictions: * (1) It may not be sold at a profit. * (2) [...] * This software may be distributed with other software by a commercial * vendor, provided that it is included at no additional charge. Here is the statement from Mark on this license text: It's my opinion, as the sole author of the license, that it does not restrict any party from selling or giving away the software as a component of an aggregate software distribution containing programs from several different sources. Clause (1) allows the giving away of the software and the selling at no profit. The text 'This software may be distributed with other software by a commercial vendor, provided that it is included at no additional charge' should be read as granting the additional right to sell at profit the software as part of an aggregate distribution -- the term 'commercial vendor' was written to mean, and should be read to mean, 'any seller who aims to make a profit'. If should be pretty clear that Mark believes that the license satisfies DFSG#1 and DFSG#5. I should caution the reader that this statement by Mark does not fully resolve the interpretation issue: theoretically a judge might disagree with Mark, and a judge is allowed to have the final say. Also, Mark is not the legal owner of the license: Lachman Associates owned the copyright and therefore the license, but Lachman and its intellectual property was later sold to another company, and it is in fact not clear which company owns the copyright to Mark's code right now, see this link: http://spot.livejournal.com/303000.html Also, after Mark wrote and licensed the original code, the afio code base has grown by a factor of 4, so some other contributers (who retain ownership of the copyrights to their own code) could potentially speak up to object to Mark's opinion. Nevertheless, as Mark was the sole author of the license text in question, his opinion on the interpretation of the text does carry significant weight. 2) Argument that a broad interpretation of the meaning of 'commercial vendor' is possible and most likely If we look up 'commercial vendor' in the dictionary (http://www.thefreedictionary.com/), we find: vendor [..] 1. One that sells or vends: a street vendor; a vendor of software products on the Web. 2. [...] [...] commercial [...] [...] 3. Having profit as a chief aim [...] So it is perfectly valid to read 'commercial vendor' as broadly 'one that sells with profit as a chief aim', meaning 'anybody who sells at a profit'. It is not necessary to narrowly read 'commercial vendor' as 'a real company as opposed to a private individual'. Also speaking for the broad interpretation is the fact that the words 'it may not be sold at a profit' were used a few lines earlier in the license text. It is very plausible that the term 'commercial vendor' was used by the author as a short-hand to designate anyone doing the opposite of 'it may not be sold at a profit'. - Cautionary statement about the above two arguments It should be stressed that the above arguments about the broad interpretation of 'commercial vendor' are not 100.00% 'safe' in a legal sense. Any trained lawyer will immediately see that there is some ambiguity in the afio license text, and that this ambiguity leaves enough room for a judge to disagree with the above arguments on why 'commercial vendor' should be interpreted broadly. Therefore, anybody acting as if these arguments were true runs the risk of facing a judge who might not agree. One of the jobs of a trained lawyer is to identify such legal ambiguities and to deal with them -- by having their client stop taking the risk, or by seeking out the license owner to obtain a clarification that removes the ambiguity. Unfortunately, contacting the license owner is not really possible in the case of afio, see issue #4 below. So we are left with the following cautionary statement: even though I believe that the arguments above are very convincing, you will be taking a legal risk if you re-distribute afio. I believe it is a very small risk. I believe that, if you are a distributer even a very 'pure' version of GNU/Linux, you are already engaged in taking comparatively larger legal risks. Somewhat credible parties have stated that the Linux kernel infringes on patents that have never been licensed for general use. In most countries, any distributer of the Linux kernel therefore runs the legal risk of being sued for patent infringement. Issue 3. License does not permit modification ============================================= - Issue 3 description Debian Free Software Guideline 3 (DFSG#3) states: The license must allow modifications and derived works, and must allow them to be distributed under the same terms as the license of the original software. The afio license notices (except for notice 3 which is the LGPL) do not contain any explicit statement allowing modification, or re-distribution of modified works. So afio fails the test of DFSG#3 and cannot be called free software. - Response to issue 3 Under most versions of copyright law, the default situation is that original author has the sole right of making copies, and also the sole right of making modified copies. So the absence of an explicit statement in which the author also grants this right to others is indeed cause for concern. So we have to ask ourselves: did the copyright holders of afio in fact grant the right to modify their work to others? I believe they did, so afio does satisfy DFSG#3. I have 2 arguments why they did. 1) Argument by the contents of the license notices If we look at the 4 license notices, we see that - notice 1 contains the statement It may be distributed within the following restrictions: [...] (2) This credit and notice must remain intact. - notice 2 contains the statement Permission to copy and/or distribute granted under the following conditions: 1). This notice must remain intact. [...] - notice 3 is the LGPL, which explicitly allows modification - notice 4 refers to the conditions of notice 1. So in fact notice 1, 2, and 4 all contain the clause 'this notice must remain intact'. Such a clause can be read to imply that 'it is not a condition that the parts of this work outside of this notice remain intact'. By explicitly forbidding the modification of the notice, they license owners are implicitly allowing the modification of other parts of the work. Had they wished to forbid such modifications of the rest of the work, they would have written different license notices. 2) Argument by implied licensing Implied licensing is a legal principle by which a copyright owner can be said to have granted a license for a certain type of use implicitly, by their actions, as opposed to explicitly, by writing a clause in a license text. See for example: http://en.wikipedia.org/wiki/Implied_license http://www.iplawobserver.com/2008/09/implied-license-to-use-custom-created.html The principle of implied licensing contradicts, to some extent, the principle in international copyright law that all rights about which an author remains silent are automatically assigned to the author only, see http://en.wikipedia.org/wiki/Copyrights#Berne_Convention_for_the_Protection_of_Literary_and_Artistic_Works There is a growing body of (US) case law supporting implied licensing. The most interesting part of case law for afio is the court opinion in Field v. Google, linked from these pages: http://www.iplawobserver.com/2006/03/googles-cache-was-fair-use-according.html http://en.wikipedia.org/wiki/Field_v._Google#Implied_License In this case, Field created a public web page and then objected to this web page being available in the Google cache. Google argued, among other things, that they had an implied license to make the page available via the Google cache. The court described implied license as follows: A license is a defense to a claim of copyright infringement. [...] A copyright owner may grant a nonexclusive license expressly or impliedly through conduct. [...] An implied license can be found where the copyright holder engages in conduct from which [the] other [party] may properly infer that the owner consents to his use. [...] Consent to use the copyrighted work need not be manifested verbally and may be inferred based on silence where the copyright holder knows of the use and encourages it. The court found it significant that Field knew in advance that Google would be caching his page unless he took some actions in labeling his web site to prevent it, and that he chose not to take those actions -- Field remained silent thereby granting implied license. If we look at the case of afio, we can build an argument for the granting of implied license as follows. A. Mark Brukhartz, an employee of the license holder at the time, posted the afio source code, including an explicit license statement, to comp.sources.unix in 1987. Link: http://groups.google.com/group/comp.sources.unix/browse_thread/thread/ce3312137ad92a37/ec49f37f3e59a267?lnk=gst&q=afio#ec49f37f3e59a267 B. The explicit license text was silent on limiting the right to modify the code. To show that there was an implicit license of to modify the code, we have to show that modification was one of the uses that the license holder could have expected after posting the code to comp.sources.unix. C. The comp.sources community was an early FOSS community, and people extending other people's code was one of the things that could be expected in that community. Indeed the creation of such extensions happened almost immediately in the case of afio -- see D. and E. below. D. The above newsgroup archive link shows that after Mark submitted the sources of afio to the newsgroup, Rich Salz, the moderator of the newsgroup, added a Makefile to the sources before forwarding them to the group, thereby in fact distributing a modified version of afio. It was common practice for Rich Salz to add a Makefile when submitted sources did not have them; the license holder would probably have known this -- and took no steps to forbid it. E. Mark did not explicitly object when modifications to afio were posted. For example, three days after the afio post to comp.sources.unix, Karl Denniger posted an improvement for afio to comp.sources.d (an unmoderated companion newsgroup to comp.sources.unix), with the description: These are context diffs to the 'afio' program, a cpio replacement, that was posted recently. The changes here take care of what I saw as a possible 1gaffe in the '-y' and '-Y' options. Link: http://groups.google.com/group/comp.sources.d/browse_thread/thread/381df257b583954e F. The license holder knew that it was common practice to modify code posted to comp.sources.unix. To illustrate that Lachman Associates would have been well aware of the practice of extending software tools in the context of the comp.sources newsgroups: in 1989, two Lachman employees greatly extended a terminal emulator program written by someone else in 1986, and posted their extended version to comp.sources.atari.st, see: http://groups.google.com/group/comp.sources.atari.st/msg/95d006760c056af1 Given all of the above, it can be argued that the actions of Mark (while he identified himself as working for the license owner Lachman Associates) constituted the granting of an implied license to modify afio. The current version of afio contains many contributions by other people than Mark, and these contributors typically did not add any license texts of their own. For these contributions, similar arguments for the granting of implied license to modify can be made. Note that the principle of implied licensing has been developed mostly through recent US case law; as far as I know it is still absent from international treaties. So in some locations, invoking this principle will be legally more risky than in other locations. A trained legal person would prefer to avoid a reliance on implied licensing whenever possible. Issue 4. Afio should be re-licensed under a better license ========================================================== - Issue 4 description Various software packages which had problematic licenses have now been re-licensed under better licenses. Often they have been re-licensed under OSI/FSF approved licenses. The same should be done with afio. - Response to issue 4 It would be nice if re-licensing of afio were possible, but it is not possible in practice. Afio, in the current 2.5 version, is not the in-house product of a single company. In the 22 years since Mark Brukhartz posted afio to comp.sources.unix, the FOSS community has added many features which have made the package grow by a factor of 4. Several authors have contributed major pieces of code to afio, and many more contributers (an estimated 40-100 people) provided patches, ideas, and example scripts. The afio sources do not contain complete log files containing the names of all contributers, so contacting all of them, which would be required by copyright law in order to re-license afio, is a virtually impossible task. Furthermore, as mentioned above, Mark Brukhartz is not the owner of the copyright of the original afio code, Lachman Associates owned this copyright, and it is unclear which company owns it now (see http://spot.livejournal.com/303000.html). That company would have to be identified and would have to be willing to re-license. As an alternative to re-licensing ALL afio code, it would be possible to try to find a subset of the contributers and ask them to re-license their own contributed code. Depending on the success in finding the contributers, this could (according an the estimate of the current maintainer) bring about 30-60% of the afio code base under a new license. However, but such an action would not satisfy those seeking legal clarity on the status of afio as a whole. Some software projects have managed to improve their license situation by re-writing from scratch those parts of the code that had questionable or unknown licenses. However, for afio this would mean rewriting 30-70% of the code. Any such re-write would introduce a lot of new bugs, which is not desirable in a mature backup tool. Issue 5. What about the Copying-policy: LGPL thing in the license text? ======================================================================= - Issue 5 description The license text includes at the start a summary * THE SUMMARY INFORMATION BELOW WAS WRITTEN FOR THE BENEFIT OF * SOFTWARE DISTRIBUTORS and this summary text explains how * Copying-policy: LGPL is the right LSM template value for afio. The writing of this summary information has been interpreted by some sources as an attempt to re-license afio under the LGPL, see e.g. http://spot.livejournal.com/303000.html. So one might ask: is this summary information an attempt a re-licensing, and if not, why is it there? - Response to issue 5 (Response written by Koen Holtman, author of the SUMMARY INFORMATION in question, partly based on personal recollections) The summary information is definitely not an attempt at relicensing. A close reading should make this clear. The summary information was added to afio in November 1999, it was prompted by the fact that the sunsite/ibiblio/metalab FTP site robots at that time stopped accepting random strings Copying-policy field of the .lsm file. (.lsm is a metadata file format for describing FOSS software packages on FTP sites) The FTP site robots only accepted certain fixed strings like 'LGPL'. See the following web page: http://web.archive.org/web/20001117112300/http://www.ibiblio.org/pub/Linux/LICENSES/theory.html The LGPL label was chosen by Koen Holtman among the possible fixed strings based on the contents of the web page above. Note that the Perl Artistic license referred to at the time was the 'original' Perl artistic license of 1997 which contains the following text: You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. Several people have since argued that part of the Artistic license text has problems, and a new version of the Artistic license (v2.0) was written that excludes this text. In an interesting twist, the site freshmeat.net at one point seems to have imported the computer-readable .lsm file of afio, using the Copying-policy line to create a 'license' line on the web site: [License] OSI Approved :: GNU Lesser General Public License (LGPL) (see http://web.archive.org/web/20070930211041/http://freshmeat.net/projects/afio/ ) So the interaction between the sunsite and freshmeat automatic robots seems to have effectively 'laundered' the complex license status of afio. Then, the FSF seems to have copied the data from Freshmeat: http://web.archive.org/web/20080109010049/http://directory.fsf.org/project/afio/ Both these directories have since been corrected. Issue 6. Several people working for/with FOSS related organizations have called afio not-free. ======================================================= - Issue 6 description In his blog post at http://spot.livejournal.com/303000.html , Tom Callaway, who works on Fedora legal issues, quoted the 1985 part of the afio license and wrote: Now, this license isn't free. Tom then goes on point out that the main problem with the license is with the 'It may not be sold at a profit.' clause, i.e. issue 2 above. At https://bugzilla.redhat.com/show_bug.cgi?id=449037, similar conclusions are drawn, and the issue of including afio in Fedora is closed as 'cannot fix'. In response to issues raised by Tom Callaway, the FSF reviewed the afio license and removed afio from it's Free Software directory. (See a note by Brett Smith in the comment track at http://spot.livejournal.com/303000.html.) This removal means that the FSF determined that the afio license is non-free by FSF standards. So it seems like expert opinion is going against the idea that afio is free. - Response to issue 6 Are the experts above wrong? I believe that they have made no logical errors in their reasoning -- it looks like they correctly applied a set of rules to determine freeness. So if we are to make a case for afio being 'free' by some definitions, we are down to examining the rules that were applied, and showing that at least one of them is not included in all possible definitions of 'free'. In the discussions in the links above, we find that the people involved, in so far as they explain their reasoning, are referring to the same definitions of 'free' I have used in this note, e.g. Debian Free Software Guideline #1. However, the conclusions about freeness drawn are generally more negative than my conclusions in this note. Why? I believe that the people in the links above are all using the following rule when applying guidelines for freeness: worst-case-rule: If the license text is ambiguous, in a way that would leave enough room for a judge to disagree that the license meets our written definition of 'free', then the license should be treated as non-free. In my own analysis of the legal issues above, I have avoided applying this worst-case-rule by default. I have however tried to identify all possible places where this worst-case-rule could be applied. It is very common for trained lawyers to apply this worst-case-rule, to go by the worst possible interpretation of an ambiguous legal text. In fact, in a multidisciplinary business team, one of the key skills that a lawyer is expected to bring to the table is the skill to find the legal ambiguities and worst-cases. In the FOSS context, the worst-case-rule has often been used when a large company (e.g. Netscape, Sun, Microsoft) created a specialized license to go with a specific piece of software that the company wanted to share with or contribute to the FOSS community. Arguably, it is a good strategy in such a case for the FOSS community to assume the worst possible interpretation of the license text concerned. The use of the worst-case-rule has sometimes led to companies improving their license texts from a FOSS community point of view. However, I would argue that applying this worst-case-rule to afio, a historical product of the FOSS community for which the license text cannot be changed anymore, is like throwing out the baby with the bathwater. There is no need to treat afio as if it might be a carefully constructed Trojan horse. So I feel that applying the worst-case-rule is fine in come cases, but destructive in others. Does this mean that I am arguing for a double standard as far as the legal analysis of FOSS licenses is concerned? I am not -- in the end, a legal analysis is a determination of the risk of getting sued and of losing in court when one is sued. This risk cannot be determined correctly by doing a narrow analysis of the license text under the worst-case assumption that the other party is out to get you. Other factors, like those considered for afio in this note, should also play a role in legal risk determination. I believe that a strict application of the worst-case-rule, when judging a software package against e.g. the Debian Free Software Guidelines or the four kinds of software freedoms defined by the FSF, will lead to results that are counter-productive for the FOSS community: - The worst-case-rule will lead to a favoring of software packages released in one go by a single company over almost all software packages that were grown over many years by volunteer contributors using the Internet. Most volunteer software will look worse through the lens of the worst-case-rule, because of the way copyright law works. In a worst-case legal analysis, copyright law requires that all volunteer contributers have made explicit statements placing their contributions under a free license. If one such statement is missing, then there exists an ambiguity in that author's wishers, that has to be interpreted by the worst-case-rule as a lack of permission to copy or modify. - Living with the worst-case-rule in a FOSS project will raise barriers of entry for contributers to FOSS projects, because these contributers would have to be asked to make legal statements licensing their contributions, instead of relying on the principle of implicit license. - The worst-case-rule will favor software projects that were started recently over some longer-running software projects, because only the recent projects could start with license texts that have been constructed to be unambiguous according to the most recent legal insights. In conclusion, I believe that the reason why the experts in the links above found afio to be non-free is that they all implicitly or explicitly applied the worst-case-rule. I do not want to argue that the worst-case-rule should never be applied. In fact, a Linux distro based on the strict application of the worst-case-rule could be considered valuable by some, and some distributions that are looking to be '100% free' seem to be applying this rule. Note that '100% free' according to the worst-case-rule does not mean '100% elimination of all legal risks to the user'. No Linux distro can be 100% pure in this way: the kernel alone comes with patent risks. I believe that the worst-case-rule should not be used by more general Linux distros, unless it is combined with a moderating principle. Without a moderating principle, the worst-case-rule has negative effects on the community aspects of FOSS. Conclusion ========== This note has addressed several questions that have been raised on the status of afio as 'free' software. The best argument for afio being 'non-free' is that the afio license text that was written in 1985 fails the test of the worst-case-rule: the text is ambiguous in a way that would leave enough room for a judge to disagree that the license meets e.g. Debian Free Software Guideline 1. The best argument for afio being 'free' is that the worst-case rule should not be applied in the case of afio, because it is the product of a long-running FOSS effort, and because the ambiguities in the 1985 license text, when examined in context, do not create any practical barriers against exercising the freedoms that a modern FOSS user or distributer expects to have. I believe that legally speaking, if a user, programmer, or distributer treats afio as 'free' software, the risk of having a court conclude that they are in violation of the afio license is very low, low enough to be lost in the background noise. It is not always morally right to treat copyrighted works in a 'free' way, just because the legal risks of doing so are low. But in the case of afio I believe treating it as 'free' it is definitely morally right, because this what the authors intended. afio-2.5.2/compfile.c000066400000000000000000000227731340024410000143770ustar00rootroot00000000000000/* afio file compresssion code */ #include #include #include #include #include #include #include #include #include #include "patchlevel.h" #include "afio.h" int matchcompext(char *); void meminit(void); void memwrite(char *buf, int count); int setupgzip(char *name); int zipfdfd; /* default value vor gzip 1.2.3 */ int gzipfactor=6; /* compress all files? */ int forceZflag = 0; /* this can be set to use something else but gzip: */ char *compressprog = NULL; int compressargs = 0; /* max. (virtual) memory usage for -Z option */ off_t maxmem=250*1024*1024; /* files whose length is below this length won't be compressed */ long compthreshold=0; /* stuff to build argument list for compression/decompression : */ #define MAX_ARGS 101 char * compress_arg_list[MAX_ARGS+1]; /* MAX_ARGS+1 because we need to leave room for a NULL to mark the end */ int compress_arg_no = 1; void add_arg(char *arg) { if((compress_arg_no == 1)&&(*arg=='\0')) { /* special case: -Q "" means no arguments. */ return; } if(compress_arg_no < MAX_ARGS ) compress_arg_list[compress_arg_no++] = arg; else { fprintf (stderr, "afio: Fatal: maximium number of -Q arguments exceeded.\n"); exit(1); } } /* * meminit, memwrite, memreset, memread, memfree. */ int memerror; char *membank=NULL; size_t allocsize=0; size_t memsize,membytesread,membytesleft; void meminit(void) { memerror=0; memsize=0; /* RWWH: Initialize only the first time */ if (!allocsize) { allocsize=100000; membank=(char *)malloc(allocsize); if(membank==NULL) { warn_nocount("Memory","Low on virtual memory, zipping twice instead."); memerror=1; allocsize=0; } } } void memwrite(char *buf, int count) { char *oldbank; if(memerror) return; oldbank=membank; /* RWWH: don't increase by linear amount! grow exponentially! */ if (memsize+count>=allocsize) { allocsize=1.25*(memsize+count); if(allocsize/1024L/1024L > 2000) { /* safety: to avoid possible errors due to use of 32 bit int and size_t types here and there, prevent membank going over 2GB if compression programm actually expands filesize. Note that -M is capped at 1.5GB anyway. */ membank=NULL; /* simulate realloc running out of memory */ } else { membank=(char *)realloc(membank,allocsize); } if(membank==NULL) { warn_nocount("Memory","Low on virtual memory, zipping twice instead."); /* The free() both works and is necessary. I can't recall what manpage documents this. */ free(oldbank); memerror=1; allocsize=0; return; } } memcpy(membank+memsize,buf,(size_t)count); memsize+=count; } void memreset() { membytesread=0; membytesleft=memsize; } int memread(char *buf,int count) { if(membytesleft==0) return 0; if ((size_t)count > membytesleft) count=membytesleft; memcpy(buf,membank+membytesread,(size_t)count); membytesread+=count; membytesleft-=count; return count; } void memfree() { /* We do not free anymore, this should speed things up. */ #if 0 if(memerror==0) free(membank); allocsize=0; #endif } /*******/ /* * Fork a gzip zipping the file name. The zipped version is output through a * pipe, the pipe handle is returned. * Returns -1 on failure. */ int comppid; int setupgzip(char *name) { int pipedes[2]; char farg[3]; farg[0]='-'; farg[1]=gzipfactor+'0'; farg[2]='\0'; if(pipe(pipedes)==-1) { perror("pipe"); return -1; } mayberewind(); if ((comppid = xfork ("out(), compressing", NODIE)) == 0) { if (arfd != STDIN && arfd != STDOUT) VOID close (arfd); dup2(pipedes[1],fileno(stdout)); close(pipedes[1]); close(pipedes[0]); VOID close (fileno (stdin)); if (open (name, O_RDONLY) >= 0) { if(! compressargs) execlp (compressprog, compressprog, "-c", farg, NULL); else execvp (compressprog, compress_arg_list); } exit (1); } close(pipedes[1]); /* fprintf(stderr,"pid %d init, pipedes=%d.\n",comppid,pipedes[0]); */ return pipedes[0]; } #include #include void waitforgzip() { /* fprintf(stderr,"wait for gzip %d.\n",comppid); */ xwait (comppid, "out(), wait for gzip child", FALSE); } /* * compress "name" if we can * If we compress we change the statbuf size, the file name, close * the old pointer to the file and return the pointer to the compressed * version; */ #if ( defined(sun) && defined(__svr4__) ) || defined(__CYGWIN32__) #include #else #include #endif void compressfile (int *fdp, char *name, reg Stat *asb, int *cratio) { int compout; char *tmpcomp; Stat asb2; int zipfd; ssize_t len; char buf[40960]; off_t ziplen; int usemem; if (cratio) *cratio = 100; /* We need to mark the file as being suitable or not for decompression on reading the archive. This is done by adding a .z extension. There is a problem with this: a file that is not suitable for compression may already have such an extension. Since this is a regular file, we can use the device number to indicate something weird going on here. We look at the bit (asb->sb_rdev)&RDEV_NOTCOMPR: If it is 1, it means that the file wasn't suitable for compression, and thus that any .z present was there on the original name. If it is 0, this does NOT imply that the file was compressed by afio. Note that it being 0 doesn't mean `decompress me', it merely indicates that a .z extender, _if_ _present_, was added by afio. (However, as of version 2.3.6, false does mean that the file was compressed by afio and a .z extender added. In older archives one may see filenames with the bit 0 and not ending in .z. Such files have a compressed version larger than the original.) In pre-2.4.4 versions, files with size 0 do not have the bit set to 1. */ /* indicate unsuitable for compression, may change it later */ asb->sb_rdev |= RDEV_NOTCOMPR; /* Hard link handling code will break if we do compression on the file, so try to compress only if no links or hard link handling. Note that if forceZflag==1 than also lflag==1 was set, so this test will never return false. Also only try to compress if not already compressed, or length is >= threshold, or <= maxsizetocompress, but always try if we force compression */ if ( (lflag || (asb->sb_nlink == 1)) && ( ( !matchcompext(name) && (asb->sb_size >= compthreshold) && ((maxsizetocompress==0) || ((ulonglong)(asb->sb_size) <= maxsizetocompress)) ) || forceZflag ) ) { /* make sure compress could put on the .Z */ if ((tmpcomp = strrchr (name, '/')) != NULL) tmpcomp++; else tmpcomp = name; #ifdef NAME_MAX if (strlen (tmpcomp) + 2 > NAME_MAX) #else #ifdef MAXNAMLEN /* BSD otherwise should be sysV (FFS on sysV?) */ if (strlen (tmpcomp) + 2 > MAXNAMLEN) #else if (strlen (tmpcomp) + 2 > DIRSIZ) #endif /* MAXNAMLEN */ #endif /* NAME_MAX */ { #ifndef LONGZFILE VOID warn (name, " is too long to tack on .z"); return; #endif } /* fprintf(stderr,"---nam: %s, len: %d ziplen: ",name,asb->sb_size); */ usemem=1; if((zipfd=setupgzip(name))!=-1) { ziplen=0; if(usemem) meminit(); while((len=read(zipfd,buf,sizeof(buf)))!=0) { if(len<0) { fprintf(stderr, "Trouble zipping file, storing uncompressed\n"); /* read error on pipe, do not use gzip on this file */ ziplen=0; break; } if(usemem) memwrite(buf,len); ziplen+=len; /* too much memory used? */ if(usemem && ziplen>maxmem) { /* prepare to zip twice and free memory */ usemem=0; if (!memerror) memfree(); /* fprintf(stderr,"zipping twice."); */ } } close(zipfd); /* wait for child to exit */ if(xwait (comppid, "out(), wait for gzip child", FALSE)!= 0) { fprintf(stderr, "Trouble zipping file, storing uncompressed\n"); ziplen=0; } if(memerror) usemem=0; /* fprintf(stderr,"%ld\n",ziplen); */ asb2.sb_size=ziplen; if (ziplen>0) { /* if compressed file is smaller than original, of if forceZflag is set, use compressed data */ if (asb2.sb_size < asb->sb_size || forceZflag ) { if(usemem) { close (*fdp); strcat (name, ".z"); /* was suitable for compression */ asb->sb_rdev &= ~RDEV_NOTCOMPR; if (cratio) *cratio = (int)((asb2.sb_size * 100.0) / asb->sb_size); asb->sb_size = asb2.sb_size; *fdp = MEMFD; } else if ((compout = setupgzip(name)) >= 0) { zipfdfd=compout; compout=ZIPFD; close (*fdp); strcat (name, ".z"); /* was suitable for compression */ asb->sb_rdev &= ~RDEV_NOTCOMPR; if (cratio) *cratio = (int)((asb2.sb_size * 100.0) / asb->sb_size); asb->sb_size = asb2.sb_size; *fdp = compout; } } else { if(usemem) memfree(); } } } } } afio-2.5.2/exten.c000066400000000000000000000032761340024410000137210ustar00rootroot00000000000000 #include #include #include #include #include #include #include "patchlevel.h" #include "afio.h" struct extnode { char *ext; struct extnode *next; }; /* merge in auto-generated list of default extensions mentioned in the manpage */ #include "exten_default.h" /* Read file extensions of files that are not to be compressed * from compextsfile. * Extenstions in the file are seperated by whitespace. * a # begins a comment that lasts till the end of the line. */ int readcompexts(char *compextsfile) { FILE *infile; char ex[81]; int c; struct extnode *tmp; if(compextsfile[0]=='+') compextsfile++; else compexts=NULL; infile=fopen(compextsfile,"r"); if(infile==0) { fprintf (stderr, "Can't read configuration file %s\n", compextsfile); return 0; } while(fscanf(infile,"%80s",ex)!=EOF) { if(ex[0]=='#') { /* throw away comment. */ do{ c=fgetc(infile); if(c==EOF) { fclose(infile); return 1; } }while(c!='\n'); continue; } tmp=(struct extnode *)malloc(sizeof(struct extnode)); if(tmp==NULL) break; if((tmp->ext=strdup(ex))==NULL) break; tmp->next=compexts; compexts=tmp; } fclose(infile); return 1; } int matchcompext(char *s) { struct extnode *p; size_t sl; p=compexts; sl=strlen(s); while(p!=NULL) { if(sl >= strlen(p->ext)) { if(extcasesens) { if(strcmp(s+sl-strlen(p->ext),p->ext)==0) return 1; } else { if(strcasecmp(s+sl-strlen(p->ext),p->ext)==0) return 1; } } p=p->next; } if(namecmp_ext(s)==0) return 1; return 0; } afio-2.5.2/exten_default.h000066400000000000000000000031631340024410000154250ustar00rootroot00000000000000/* This file was auto-generated by the Makefile, from the afio.1 manpage file */ struct extnode de1={ ".Z", NULL }; struct extnode de2={ ".z", &de1 }; struct extnode de3={ ".gz", &de2 }; struct extnode de4={ ".bz2", &de3 }; struct extnode de5={ ".tgz", &de4 }; struct extnode de6={ ".arc", &de5 }; struct extnode de7={ ".zip", &de6 }; struct extnode de8={ ".rar", &de7 }; struct extnode de9={ ".lzh", &de8 }; struct extnode de10={ ".lha", &de9 }; struct extnode de11={ ".uc2", &de10 }; struct extnode de12={ ".tpz", &de11 }; struct extnode de13={ ".taz", &de12 }; struct extnode de14={ ".tgz", &de13 }; struct extnode de15={ ".rpm", &de14 }; struct extnode de16={ ".zoo", &de15 }; struct extnode de17={ ".deb", &de16 }; struct extnode de18={ ".gif", &de17 }; struct extnode de19={ ".jpeg", &de18 }; struct extnode de20={ ".jpg", &de19 }; struct extnode de21={ ".tif", &de20 }; struct extnode de22={ ".tiff", &de21 }; struct extnode de23={ ".png", &de22 }; struct extnode de24={ ".pdf", &de23 }; struct extnode de25={ ".arj", &de24 }; struct extnode de26={ ".avi", &de25 }; struct extnode de27={ ".bgb", &de26 }; struct extnode de28={ ".cab", &de27 }; struct extnode de29={ ".cpn", &de28 }; struct extnode de30={ ".hqx", &de29 }; struct extnode de31={ ".jar", &de30 }; struct extnode de32={ ".mp3", &de31 }; struct extnode de33={ ".mpg", &de32 }; struct extnode de34={ ".mpq", &de33 }; struct extnode de35={ ".pic", &de34 }; struct extnode de36={ ".pkz", &de35 }; struct extnode de37={ ".psn", &de36 }; struct extnode de38={ ".sit", &de37 }; struct extnode de39={ ".ogg", &de38 }; struct extnode de40={ ".smk", &de39 }; struct extnode *compexts=&de40; afio-2.5.2/exten_make.awk000066400000000000000000000020221340024410000152420ustar00rootroot00000000000000# Generates default list of -E extensions from manpage. # Does this by looking for the special comments # 'br START_EXT_LIST # and # 'br END_EXT_LIST # in the manpage, and for all lines in between, recording every # word on each line, except for the first word each line, as an extension, # where a word is a whitespace-separated string of characters. # See also the manpage comments surrounding the extension list. $0 ~ "'br END_EXT_LIST" { p=0; } p==1 { for(i=2; i<=NF; i++) { ex[nx++]=$i; } } $0 ~ "'br START_EXT_LIST" { p=1; } END { print "/* This file was auto-generated by the Makefile, from the afio.1 manpage file */" for(i=0; i=3- NrՅO2.qo&_~RO=RR[ GOz~K[Od'7Cnu , Dt s6P9|qkfesu|3S̓ƒa 4IO% 샷D Aָ1) L֘r别r7lOĄ CIko$*-e bH"\]Fzgr]iwq<8~wv;kW_7ON^GWWVw_cx'qVڻaO5 }~T ]BV*l̲wBeX=nzd_ĬG @}pyޠ1#Tzӄr:Էʯ{o-z4a X}&~PgSmdo_}^Q_φ]*L3a!< +SjVp ^[WwUk?l>ʞ&Nd/2I8YV>F+I׵ъ"uh dF_Nv GV^ ^^,!5_[E~cG*_@VV.? Ngݓh(I';V |)7v׉MP C֐]{ Kw'$k]w[CZ(Yn[U6xI鴴%)S vmslx Bgցk䬿64 Տ7@ymɛ{1++ r>TsbfQ ЂbN8*`=E7D[l}66(a;+9}xГwo_l;;ɏNBL 9[RDnZ2BCxv_(Te t]q)Y.•`+ 9JVEYIQ," <' ِ(2cR(9+o2NVR ٱ/"޼M/i\PU Z &',ZK A&HaʭtlQ777x;7cP*u dymއe{pAMѲA\3niq]޿Bܟ:4ni|mCUb^ȦK> gѡ5S*ڻOrvS&qY]u:*˲aR`e.OB}r_ſm?}G?wV8^9Cr*rmʶ%1`[ >}^puD_6QQ/&f}:W>޻Gf088SC۔Bo:\o5;D)⹷>~J6 |+N9KjsYaT9NyZBo&E7L:(pCcݠitAuԃ gg%޽l(RVٙ(]?aOaM'ۋ פV/wv@<=8+}_M\@}\ݠt EjNuBSPN mZώnfDRr M˄Aw#>f,C=|.}j8+Cy$1WPX G]Xf:ҔV"  $uGLj1~J!`Hq?nX\6٤GNDZZNX8FE]r~97,6ibW`\MݲX)76K N Qݐ+1i42A> +۫gT[f6QFcl4hRd8B,H_T4@p\!wHE,0O[)K[b_twC#xtrȬα!/f4*4c7hx85PYʫ k0bO.4j\_\)x~wEKVGE~ ^g5wc"\:_~:rEy6DrWrC"@R@xfFC,`>H`%b\ȭRi:6Sy aԨ>`6Xov zp+*}A;$cIi. 5n\”s\:~yѦXĔ b@3X)UḴ.7ԡWWdbFҀiJijb9rط /Ҹ4"_Lu.dҨĵQlXUS=.228~ VLHmhRZ)}tqtx'ƈ!#|^U%ֹmfc^nԞB fxf+DWj-.-9i&E!A YSn>KEbKG1:Лx[Ѓrϸw)[*aZ2QF8Z F0"GJ6F?v#$mg(;[*/:pK|'G-C3l`28tsxr穂O:س1$jB 'jp\ Ym=:ynuk7؜HBJ}DFLD=Y'<|eo B& ~iDi\ʶ)1vS&qcQ4V`.IhiPӨUĢU)Ԫש Iy.''֯m5? kM`&4-z7QO0(QF>wV9B]FU+UJZMSn|'.o/Ց 5,k4,K$G+*$LWZ㫧SHge:4R⨻NHc! ~^FzL|@ԐU,D/Eo GW {r .ѫՐMf!J掩$lᇨai,ݽb_|'\JkWw\G QhE_++EGG%\Q?쬆dɴ_ "TfVݾ85DimP1v$?S6Hg;M3#<Zzj,ڬmQ8(y;mvg22ITIDVsCIepA77?ȃ9M3I 6@V iqꢃy:}7'Jg X3RMgO Ý4k}$Y4(ra!'0s*Ld}:m<dNXn<Φ]nG]h=(Y1@0ZґaF5DI/1 )o>³y! m=z ѝZ}dUߊl[`KD֕N݃NlIƪRMM;sͲЕTוvUF@sen;G%*wĐg|-0:Oe60Jڠ|YM%6z{ 6u' ۇE&W)CNp3l]JYljtolbp,` Bp_6r% B$gi*vR=|q噚wb5)K]a|^ϙ- dJ6CU0`F Moq',IU2uG}2q[B2@qwgd=:t`9й5ADU fK?\:M)lߗ(騝q}j" OQ&S;K@ Qi{qǰ2lo}x؅Vqʢ+df=ݔΓ,:V,3gD?ے/+~0_!0aw-mI>K*( I QLH@ o=/8$̣g_͡dV  .y}]j*!gE9X\8h X r-mZ<P!wf5PxNs|*;vVR%eE0Oh/[ щ/\]g_^GRZaf-~gkSA"JtaYIS@&5#?[5GxDΓ;]τ3{gu" RXch}F"ud+{92)۸ZT: WǢX֎ӏaH9}s 9vFFt5tRgVW+ͧ؍cH{`?l$TXTﻰБ LlND%{V5Dgf'ƒ,-EBo3͜:y!akYbo8!M3e%0o)o}70{|`yFقZ*7YH"<!6l lv7gn) AW6t_AY`&wVOb-nW C ZSny߰+m󐳣>m$/aOVwL]X>U2##p2D =t.}k;#x .rrǴMox|~dj iE x[0 F7p9Y8q>Y M5ldd[YLA n<%p.ƅ%$ć$VkZ_>ڷclC z̫-/DYpQ%%J{$hM n%xECxHK25l+9۝,VY6P ;$悤6xDĐǵ˻9PkIVN.HRiPXm-]jI`Juscdvs漆lV! siyϳ3|d}WcOJo;x#@}(΅Tip)cpkӟ3]!UBO2Q Y@/7t[wM:Wc;)+8~_ۤi:FׄBZ1^8P3p 1D0h3CiS¯C ( g9`N陦 FNc)z-`'-MN"tv* ְ] w嬕K1bz^"N$tz |}$i@5䈚E9kv x@o ݼo\L eXLR ͻŭd#0A| `w<΃m & T;hµ$Pkk.VwP8Yjwj8<+E2f[nRX>r+TYR\'ޅl^b-8?~P ?>L=cɣS;g3)bK?AY"QnDdI@qW2BAhs٣O2F"d Zm,4T%IF6%r$Ehgm3|b#1" Rne;x˹BvƩTuАk ejn!& hC[`B$]U*$'7;WYZ?dI-*~rjz\6PNxPᕁ[n[bV⋫*ժ_W[msoh'FiEIGؘHx]mgklqoQ CƗdȈMȼVx}_pςK =3(ѾBkjʜ*&H_C3<`$N9nb{'edӯD"la'I*S4|8Hlj*wa6zm{jXYyO*qW Dv)IX lV ܾt+'f˾Jh41.Oz&ؓBdJFzi󽎪nYsw9=N8~֒]48'^OlC' B묫"Q͈"tnML zZоt, q̞$rsPOf8C)J5&6ʒy O=~ a80MO hjk$ׁB2h"+B̎nS q7^aa&/|ˣ;?``M~KF1 @柄uZꤪK.G8etf+(LC+ˍ!owuI_G# ʘ@cJ[Z$hr(H9V9IB^YDw:-@~W1x'̨ cw;.J<) Am[2Rmeݖ_Z3(y@5>*j煑R\]A= Es Ŗa>@Tƣ:SDN@&*xC߆OiufO$e0.on ~}Ă?Ԥ/^7iB* 9xvSVl{0o4 ߎ5 vS> i`V8 !*KBk֭K 7kb^}P7vh>RZqzy?:tC5Yn P5e IGUd d͒[rv WG0!9 ̴rGsǒ79uW}8Dw[慈('y*m;Yy%ru*Y+XYSUV7Oo=@ 3ɜGIYM߾"= ѿ[ `ݽng$["4/\pS.GeAMT] [ڼ7USJgw7%kg/e@~, .Ke%AIF eF@Z#Y2jZH\_ORE,~'z&UINme$Mb0y$mqzk?,J'vz %>7g,x1Z8Lg?%'ja=b ]̒%nDʥ4? 9Li/ܥ n4:t _M)OkAR Zk1S軶u oƀc0: 5B &4ox?:a:b yD*MӷlVޤnS#Θ@(=i:05v?s\\BChHm#ۃu;]A$sp: Ng bDw4ܵ%ӫliZ_٘k"BIhP, b-r}_r9f; f^N 451p$ <;~zb <&Ik~A3{ 5 ]=4.-iJ ? PiBB Fط5\أgGTX_@H@<#V IG\+(0xJ=DWfWHl>. ҵל#s,.WMuk f,[ WhxNZF+IMIޡ3Nl[:d;Q1b\@+]gw(7e܎&)+pmz1{ Qasǹ^lCʡESD^0UӇM1K I,C j,*"Sֶ߈Mi[%ldSJ\ [77}խ7]Ĩ&ً/hE˚6q.mB_qS˛_K.cПy[h2I” 1]a:InlUYtWL^;?ϼrZr{mKVhW'̮L m- J3cI^8yg,Bkgq?U%+3"pS}[ B#)"+gor qO`(gQã.1$"hI.şZA!lOª,WtW5ng M@[ >l-d`_Ws|9>s|9>s|9><afio-2.5.2/match.c000066400000000000000000000115351340024410000136670ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include "patchlevel.h" #ifdef HAVEFNMATCH #include #else /* A limited fnmatch replacement. Only accepts patterns in the form "ccccc" or "ccccc*". (These were the patterns accepted by older afio versions.) */ int fnmatch(const char *p, const char *s, int flag) { if(p[strlen(p)-1]=='*') return strncmp(p,s,strlen(p)-1); else return strcmp(p,s); } #endif #ifdef linux #include #endif #include "afio.h" STATIC int ignoreslash=1; /* * Pathnames to (or not to) be processed. */ typedef struct pattern { struct pattern *p_forw; /* Forward chain */ char *p_str; /* String */ int p_len; /* Length of string */ int p_ptype; /* Pattern type */ /* types are: PATTYPE_MATCH, _NOMATCH, _EXT. */ int p_noglob; /* influenced by used of -7 flag */ } Pattern; STATIC Pattern *pattern; /* Pathname matching patterns */ /* * nameadd() * * Add a name to the pattern list. */ STATIC void nameadd (name, ptype) reg char *name; int ptype; { reg Pattern *px; px = (Pattern *) memget (sizeof (Pattern)); px->p_forw = pattern; px->p_str = strdup(name); if(px->p_str==NULL) fatal(name,"out of memory."); px->p_len = strlen (name); px->p_ptype = ptype; px->p_noglob = noglob; pattern = px; /* fprintf(stderr,"--%s added\n",name); */ } /* * nameaddfile() * * Add a file of patterns to the pattern list. * Returns 0 on failure, 1 on success. */ STATIC int nameaddfile(fname,ptype,parsewith0) char *fname; int ptype; int parsewith0; { FILE *infile; char pat[PATHSIZE+1]; int c,i; infile=fopen(fname,"r"); if(infile==0) return 0; if(parsewith0) { i=0; while((c = fgetc(infile))!=EOF) { if(i > sizeof(pat)) fatal(pat,"Path name too long"); pat[i]=c; if(c == '\0') { if(i>0) nameadd(pat,ptype); i=0; continue; } i++; } /* handle case where the last entry in the file does not end in \0 */ if(i>0 && isb_rdev); */ if(asb && ISCONTROL(asb)) return 0; strcpy(namec,name); namedot = strrchr (namec, '.'); if (Zflag && asb && (asb->sb_mode & S_IFMT) == S_IFREG && asb->sb_rdev == 0 && namedot && namedot[1] == 'z' && !namedot[2] ) *namedot = '\0'; else namedot = 0; n=namec; if(ignoreslash && (n[0]=='/')) n++; for(px=pattern; px; px=px->p_forw) if((px->p_ptype)==PATTYPE_NOMATCH) { p=px->p_str; if(ignoreslash && (p[0]=='/')) p++; if (px->p_noglob) { if(strcmp(p,n)==0) return -1; } else { if(fnmatch(p,n,0)==0) return -1; } } existpospat=0; for(px=pattern; px; px=px->p_forw) if((px->p_ptype)==PATTYPE_MATCH) { existpospat=1; p=px->p_str; if(ignoreslash && (p[0]=='/')) p++; if (px->p_noglob) { if(strcmp(p,n)==0) return 0; } else { if(fnmatch(p,n,0)==0) return 0; } } if(!existpospat) return 0; else return -1; } /* * namecmp_ext() * * Compare a pathname with the extensions list. Returns 0 for * a match, -1 otherwise. * */ STATIC int namecmp_ext (name) reg char *name; { reg Pattern *px; char *p,*n; n=name; if(ignoreslash && (n[0]=='/')) n++; for(px=pattern; px; px=px->p_forw) if((px->p_ptype)==PATTYPE_EXT) { p=px->p_str; if(ignoreslash && (p[0]=='/')) p++; if(fnmatch(p,n,0)==0) return 0; } return -1; } afio-2.5.2/patchlevel.h000066400000000000000000000026441340024410000147300ustar00rootroot00000000000000#define VERSION "2.5.2" #define DATE "30 Nov 2018" /* * Version 1.68 1985 Mark Brukhartz * Version 2.3 25 Sep 1991 comp.sources.misc * Version 2.3-dpg-3 07 Mar 1993 Dave Gymer * Version 2.3-dpg-4 14 Mar 1993 Andrew Stevens * Version 2.3.5 (for Linux) 11 Dec 1993 Koen Holtman * Version 2.3.6 (for Linux) 31 Dec 1993 Koen Holtman * Version 2.3.6-dpg-1 " 12 Jan 1994 Dave Gymer * Version 2.3.7 11 Mar 1994 Koen Holtman * Version 2.4 26 Nov 1994 Anders Baekgaard, Koen Holtman * Version 2.4.1 08 Feb 1995 Koen Holtman * Version 2.4.2 21 Jan 1996 Koen Holtman * Version 2.4.3 15 May 1998 Koen Holtman * Version 2.4.4 21 Jun 1998 Koen Holtman * Version 2.4.5 28 Sep 1998 Koen Holtman * Version 2.4.6 25 Nov 1999 Koen Holtman * Version 2.4.7 09 Oct 2001 Koen Holtman * Version 2.4.7.9beta 01 Dec 2002 Koen Holtman * Version 2.4.7.9beta4 04 Dec 2002 Koen Holtman * Version 2.4.8.beta1 14 Dec 2003 Koen Holtman * Version 2.5 21 Dec 2003 Koen Holtman * Version 2.5.1 05 Feb 2012 Koen Holtman * Version 2.5.1-2 09 Aug 2012 Koen Holtman * Version 2.5.2 30 Nov 2018 Koen Holtman */ afio-2.5.2/regtest/000077500000000000000000000000001340024410000140775ustar00rootroot00000000000000afio-2.5.2/regtest/Makefile000066400000000000000000000006271340024410000155440ustar00rootroot00000000000000CC=gcc all: cmpstat bash ./regtest cmpstat: cmpstat.c gcc -o cmpstat cmpstat.c 2gb: gcc -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE makesparse.c -o makesparse bash ./regtest2gb sizeof: gcc statsize.c -o statsize gcc -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE statsize.c -o statsize64 echo normal compile env: ./statsize echo large file compile env: ./statsize64 rm statsize statsize64 afio-2.5.2/regtest/afiotsmall.af000066400000000000000000000620011340024410000165410ustar00rootroot000000000000000707070014031262100407550000000000000000010000000757255213700000600000000000afiot0707070014031671330407550000000000000000010000000757254440600001400000000000afiot/ztest0707070014031671341006440000000000000000010000010676666335300001600000000002afiot/ztest/aa 0707070014031671351006440000000000000000010000010676666335600002000000000032afiot/ztest/b.zf7bKJ*W0707070014031671361006440000000000000000010000010676666335300002000000000026afiot/ztest/a.zf7K0707070014031671371006440000000000000000010000010676666335600002200000000056afiot/ztest/b.z.zf7xvۜ9#&Z, 0707070014031262201006440000000000000000010000000757255630500003100000000167afiot/name with spaces.z=UK0 C>QRHszgy,ݝͻA2:(VLTRmdP)s|VTZk +ʽ#Fi: !Ϙ0707070014031262211207770000000000000000010000000757255004700002700000000020afiot/link with spacesname with spaces0707070014031262221207770000000000000000010000000757255010300001400000000027afiot/linkxYetc/sysconfig/ipchains0707070014031262271207770000000000000000010000000757255011200001400000000025afiot/linkyYetc/sysconfig/ipcins0707070014031621510407550000000000000000010000000757254776100001600000000000afiot/special0707070014030301130200050000110000000000010024050226351376000002300000000000afiot/special/blex0707070014031621531006440000000000000000010000010571622725100002700000000753afiot/special/afio.lsm.9/+.9/Ao1 $RhQhDV8Nvږ=&$T`ɒe|WA|7dq 1^^ɅsY+ݬV &MRXEݙ_ J9@jl,!b:6àzL񳍏"PGV \z浦p+]C$a|.HEg9bjLpS·ѻU%?sj{YeZabNEOYIGizS"Y?5|&9A,Cg;oxm:LQNڪ$Q/܁xrڠg|Ő+ ^,@o7o?z{#;,#sɜz<@'YEN;󐐬t ]ȘwBs E't!W e'C%bTPkZ;Dh,Nr( mLM܊9`m8 $U kEڔШS̘7^y-QE%HɊJagx\I'lxcpK,%1R-v!ŬtR^ %IJj$覉)_!Ʊ~YEPV8W6ݍGu i\gPqCe5k{DgU;~7ïjo\0707070014031621551006440000000000000000010000000604723531300003200000000322afiot/special/spec.cpio.z:0;^\֑xA1+P\p(_ asB,`y RWʮQ R7G?)'lV+{T H 7#6*tewbB,, 5`ծ}UBP B䞜<u4!A>A }w0707070014031621561006440000000000000000010000010666634255400002500000000076afiot/special/afio.cl6`9zӌX󤖦)pVk+#_]\pWC;l)0707070014030301121000070000110000000000010000010610044472600002200000000005afiot/special/rttrrtt 0707070014031621520206440000000000000000010177500757254707700002300000000000afiot/special/dev10707070014031621601007550000000000000000010000000604714237600002500000000117afiot/special/test.zĜ0KMWHLWЭ*)KV-V0P(*)*N-QЭ*JMLJN,*؁d01L B\ n0707070014031621570606440000000000000000010177500757254710200002300000000000afiot/special/dev20707070014031621611006440000000000000000030000010652637504700003000000001255afiot/special/hardlink1total 27 drwxr-xr-x 4 root root 1024 May 13 21:53 . drwxr-xr-x 23 root root 1024 Mar 24 1996 .. -rw-r--r-- 1 root root 16450 Jan 30 1995 afio.1 -rw-r--r-- 1 root root 759 Feb 8 1995 afio.lsm -rw-r--r-- 1 root root 1884 Mar 1 1994 exten.c -rw-r--r-- 1 root root 0 May 13 21:53 hardlink1 prw-r--r-- 1 root root 0 Sep 15 1995 rtt drwxr-xr-x 3 root root 1024 Nov 29 1996 spec -rw-r--r-- 1 root root 512 Nov 5 1995 spec.cpio drwxr-xr-x 3 root root 1024 Nov 29 1996 specorg -rwxr-xr-x 1 root root 110 Nov 5 1995 test 0707070014031621611006440000000000000000030000010652637504700003000000000000afiot/special/hardlink20707070014031621611006440000000000000000030000010652637504700003000000000000afiot/special/hardlink30707070014030301140600050000110000000000010050050226351376000002400000000000afiot/special/blex20707070014030301201207770000000000000000010000000757253736300002500000000004afiot/special/blexlnblex0707070014030301150100070000110000000000010000000226351376000002400000000000afiot/special/blexp0707070014030301171400070000110000000000010000000226351376000002200000000000afiot/special/log0707070014031621631207770000000000000000010000000757253736300003300000000004afiot/special/testsoftlinktest0707070014031274100400070000110000000000010000000757254414100002300000000000afiot/special/vivi0707070014031274111006440000000000000000010000000757254414100003200000000145afiot/special/vivi/blex.za==1 } Q-O r!<_b\d<\P2)o<ݳ k@ҁG%7>sRс +c+xz0707070014030301161000070000110000000000010000010000000000000002100000000000afiot/special/X00707070014031621620206440000000000000000010274020757254713700002300000000000afiot/special/dev30707070014031621640606440000000000000000011676360757254711700002300000000000afiot/special/dev40707070014031621650206440000000000000000010274020757254716600002300000000000afiot/special/dev50707070014031621660106440000000000000000010000000757254720500002400000000000afiot/special/pipe10707070014031621670101230000000000000000010000000757254732500002400000000000afiot/special/fifo10707070014031621701066440000000000000000010000010757254740500002000000000000afiot/special/a0707070014031621711016440000000000000000010000010757254740500002000000000000afiot/special/b0707070014031621721006440000000000000000010000010757254740500002000000000000afiot/special/c0707070014031621731074210000000000000000010000010757254776100002000000000000afiot/special/d0707070014030664350407550000000000000000010000000757254463700001400000000000afiot/files0707070014031262131006440000070000100000010000010757254447500001700000000003afiot/files/a1/* 0707070014031262111006440000110000020000010000000757254446400002200000000266afiot/files/a10.z4=}A @ нKg;Aw^ I8SԢwY% [P+ 2AzyrD+>2&2L*C][~r5"IQaPl:jK9Inf}ۀH">|~|I=H %U!0I.ЂP)d@;F\Z:s;avHPυq@8$/ZH m%V`tR+Q@gla _e"TVOa*3r]qP-j ˛4ZA"ެe>DT9}Bi#֠Z>2Рr%,}9V$bM!(Σkq\Y8=:|Ұ.v.JQ)zB^%\u 'm&.|TBZ?FSȐIҴ n05\BC\Wk*mY7<(IIN{ozkSrW vˊ8AçB*Go4Xۢ)jj1r WHC i[\HokVSL.'lk'4+ '0#dBiar;h '\S)dүA?YhUԁ ĕ+r5z׶I!dIDV~w5Bn <}a|aB+E-L21a&z#6o`#YNA.2$ ڨϱkaL.uK7(I&S 5:n͎Ð$j5h-+R}%uxR+$2 Qy@j"% S F)4=Qޚy#};gY#KZj Qe]'=\!kbVwhHJS;VL+W-i^05dFJ]^*\iJ?jc:\#{t8h&zcoG gq6l)\LzӮ>UXW>BLR.>K\OFR<:)fƤwBiEpRQLD(0Ngs!mٿQ SF07[^݈tJJ0H=BS76or}ثj]kpO|RժhLI=}]_k{aB>- 8 ey-;5 6KE0u2hxiN':>Ym߬ хxfs~d5$XКj!|aJ Q01󌖞'"җe(`+>eIn";~4x)۷*z |6.E(LYReN{sNX9堓Pj$$yp_;`Q;|W9~,,I*vCUTMs/̓CD|$xJyS%%qbE$^uap6tj]!AfHyԼOFO2R\BmId@L$ kf`_6Ʋzc8E"3Mj`IA$>qDQT,2DNF0"άtG ";Vì=iVĝU9uȚ^##X\$ "-R9Vos{"Q7%{ҁF՜ /$AqoMIŴ&"Mg8&=21Cu CYiT *U \+Q&OXo!Ü`dL]o~(7(#ml8![ yg`~ pg;pIw׹j}vE-;2WEN~D#Qc+4[lz-kNo0UB'B=n.W3,*F+~ҶR(>:V0JY?ɪO1JNE!D$αJzrzB)E[PiO,gFsus^D$]׳=* U>kؿYSǣ\X#g#0+PrJm'(cV$U S)#aU+؃wExO5'ŹӟE*EE]*`"/̒9QwR ;gG8ÿ A1 Mp`>rb'0 MM%'DaӑVզl ǭg%oZ>4 |֊n*$*3eIrpH  ׇEdh񜅢#xq l3~#Ď6]q]tzeɾ2¶C!I 5|mTJˢ{7.F9 ҌS(TXf㪽>{͠o *9 $tVuoC3D4'F5]6Uv ]]Ss,B*Q-3wBJP49\6I)+nPGVELJ]}^sdrr5s7T~h|4;j v+GV1 DfZLlu?gA45&Ϋ rKվHp8R<3ݠà$~#sG9tm8CX`"e,BIM*/=аd>rFGSqvuIKBmW5Q97uwjZe<3_ZJzq",7P՚X4O}K*zA\8eYKi6F%+KcPrRNV~Oƺ,AQuOv^yzU!p* BM8BTƉj-Ѹ}*"m2eSqXK#fN~Jel:)u*MGTam*p|mDBP.JnAOb$]zS= bcUt׸Ţw yn2RףW?|<}|X;fچ5c5B.\|l3[2dk*5a.;~4s*nXB(D/F䌯=|uB丄E,W9 n!Jt*aa OO "4DA#*+M14v͂TC$̐#sWV_+>Kt+rRx{{dF|gC+(T0i6ўm2)f" 6ƻ4J-p-+ p~.BؼJwfz{J9CKNZk2-RSFZ^ٖd<ǽYnܧ6*CSoVJNr 3k-:=SgǴw 뱱;IEd\I9SUY7l z#Ox^NJZ2z@QG,Œ-K5C Om6\;9 wbupg^B>]uIҽ#Nk=/kM/bhzTdoCXK|i\, /ru& H zw;0t$'{o:Y"}ӂ!K>.o/S6i:L鷡M.5Tl; U zU8uBŶ-/w3j-GB[/|j[ukzg筻_uMuz}zmFw[AbuU}f/V̜BstyOr*]4O)xϢѳPQU-*g!= W*o^}ZoZ4RkVXw"VfA;x=$~<5{vǹ}1>9>;/@ /x %`s71ZH`NA uQ4&ʰ@\JYhWlYNiݨ.m?JܺUnGw6]jlf)(sI09`L79  anP‚Ѳ^_% U_|pRY l{)X:dQĜ߷Lw$9|ݱ)~=㻳KH?^B4Y?C_<\ˍs 7 _tؿVM3 5| Wq5vPz,n:ZͺߔڂJ/اz׵htZ_ _奿wNUF o2 FRn ޶#4 GdD];S\0dȦ Zqr3/K"ܝݝ}@uy]Zkc 97guf+On+$C1"rYo]w{Afzځ} Qg$kgi40YNL%X Ii. mq!}3b7ZzTlZq~L»?}t Bp9 "ztb N}:p;Kc}@R?i ;Nο-+qrI?pN%?2Og {-*W0WRJpo~+yzK|^|.Ҋ5뷒FTb?M+ Iui.^v3WŅÛ%;N3˵5n iL2O?vksΚퟱ3-[Zȓ120>Ucu}qT7:tz+DSFŸa[. k>oKD.QEFe.92rIE yY ~}߉Jݚ%}:MlߟYdnGi=,uRm ~(c'(~a^^lLRFI)O9X J3PVi.gD CZbU90L0X@In5A9=SX3{̏WU}DgZkIaG _IuJTBf3978*'Ow8[{ c=lz}`ym3Fate}~J VN-B+tи~^ثsƁsondy:ʷFQ~m-|.%ͩNjJcPz(čg2Kn<[=YjQ]pl:]^Tgrg($[cW:%),^RHZ!vYig%7~.F"a0y9o8#Wx[0xma ="."7 [oc/.G.Pc\ÒQ+'wR6a8ŏ["jRDԄM d5ϥҰ= %IK܎tQ(^:`1%2ӆ/! m7I?8T.ZG36i {#2W"-rxnKHU#)9D s~|:11oEQB"٪M?PZD#OdvD;Y߈>\u2GX]LA**Ɉ'(vUk޺jx8!6]M<8AWkuPKr5X#c$GV2c!p(;a? OMֵq_MSH~Dd?pg:^x$c{FYJ?a2:d0}O99 xn>y[wߵ]Q;nl}z{ 8qE  ` ?%nXatz=4}*+AJ}$yv'#ާ3n˫7<<9 Hk[!Q)-ew 'd6͝nȋ4}=x:Ip8na~u7lxkg1eA'"Ö] v!0@85DqBX ;W_9V\T- v\0 EdߏB1yg]@c;:}Õnqwq2) 7%+)oiOL"=kq~ `6S̝_ ymBE)6׸bd>;jrI;ϋ Z@/:D~vzpӃw3m5q&'SVt)Q$vYf0707070014031262301006440000000000000000010000010677123010700002200000000000afiot/files/archx0707070014031262151006440000000000000000010000000757254452700002100000000315afiot/files/b1.zW=A0 =xKA9`LRTKvn'HVj҄6?N94t`V[:wbH0s9_ ǑBwY 2m=W94 Q\JP!^QZ\ӿ.G MoͥOs0G׳VP*BE"|@DI - 0707070014031262161006440000000000000000010000000757254453700002100000000443afiot/files/b2.z_=AN0@}O1K =C!N3j#NOC{eK;wtam76ͣ$Z`9Joc>>-Z,Tk-e9z:_KIY%ؚC׉cf%(_9Q%IPoRzRƴr[Pc4o&<                  co?hI0707070014031262171006440000000000000000010000000757254455500002100000003615afiot/files/b3.zm=1N1@ўSL B0Xv#!ӓtHϕ-F"#mʲ)eRϑ-Eؔ)yy[Ze8Zzs| -mj1 4Ǻe(t9UaXce2ʥR}1c./)wy,C[_ּj,?EO; @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ c0707070014031262341006440000000000000000010000010757255213300002200000000205afiot/exttest.gifexttest.gif files from jan2000 jan2000-2 js link1 link with spaces linkx linky llt logfile name with spaces special t1 to1 y2k ztest 0707070014031262351006440000000000000000010000010757255213700002200000000221afiot/exttest.lhaexttest.gif exttest.lha files from jan2000 jan2000-2 js link1 link with spaces linkx linky llt logfile name with spaces special t1 to1 y2k ztest 0707070014031262231006440000000000000000010000010703324167400001600000000000afiot/jan20000707070014031262241006440000000000000000010000000703323257500001400000000200afiot/y2k.z}5m81 1FJ 3l*/KȄ)R# MԢ7lZ=ez{MݴF?U:r<_NK)evx0707070014031262251006440000000000000000010000011606722507400002000000000000afiot/jan2000-20707070014031262261006660000000000000000010000000703323257500002000000000451afiot/logfile.z}5m8͒Kk0CNib\!'-תd&JBS EͰTiQjQXo\f*:z2 kJF4H;UUGuڼ#O&\}rX{7 q$Ldlzr `_m{1SbP2y~bawuj?;^[D{.99>R?y C)(UR{/9[ ?@90aqw_S8Q?"oa0707070014031262361006440000000000000000010000000701732456500001400000000472afiot/llt.zu=8Mn0]1,"R h⴨#MJDH5={f쌫[pxi.HN9m y%89X\'Ȳ$cj!cdXR>4&#hS<Prw+i^HCn:(Xېx}roOaWxmwr `El//؏M紽Ē~l dzAĹֹ8/]:͏31zP Bcl7sc_/O|fN.izTT\Fv0t3i}CMU0707070000000000000000000000000000000000010000000000000000000001300000004257TRAILER!!! afio-2.5.2/regtest/afiotsmall.af.toc000066400000000000000000000123521340024410000173310ustar00rootroot00000000000000drwxr-xr-x 1 root root Dec 2 04:32:47 2002 afiot drwxr-xr-x 1 root root Dec 2 03:44:22 2002 afiot/ztest -rw-r--r-- 1 root root 2 Sep 12 10:40:11 1999 afiot/ztest/a -rw-r--r-- 1 root root 26 Sep 12 10:40:14 1999 afiot/ztest/b.z -rw-r--r-- 1 root root 22 Sep 12 10:40:11 1999 afiot/ztest/a.z -rw-r--r-- 1 root root 46 Sep 12 10:40:14 1999 afiot/ztest/b.z.z -rw-r--r-- 1 root root 119 Dec 2 05:08:37 2002 afiot/name with spaces -- compressed lrwxrwxrwx 1 root root Dec 2 04:14:47 2002 afiot/link with spaces S-> name with spaces lrwxrwxrwx 1 root root Dec 2 04:15:15 2002 afiot/linkx S-> Yetc/sysconfig/ipchains lrwxrwxrwx 1 root root Dec 2 04:15:22 2002 afiot/linky S-> Yetc/sysconfig/ipcins drwxr-xr-x 1 root root Dec 2 04:13:53 2002 afiot/special c------r-x 1 news root 5, 5 Jan 1 00:00:00 1980 afiot/special/blex -rw-r--r-- 1 root root 491 Feb 8 21:50:49 1995 afiot/special/afio.lsm -rw-r--r-- 1 root root 774 Mar 1 22:51:43 1994 afiot/special/exten.c -- compressed -rw-r--r-- 1 root root 210 Nov 5 23:29:31 1995 afiot/special/spec.cpio -- compressed -rw-r--r-- 1 root root 62 Feb 28 23:38:36 1999 afiot/special/afio.c -------rwx 1 news root 5 Jan 21 15:12:38 1996 afiot/special/rtt crw-r--r-- 1 root root 31, 232 Dec 2 04:06:39 2002 afiot/special/dev1 -rwxr-xr-x 1 root root 79 Nov 5 15:06:54 1995 afiot/special/test -- compressed brw-r--r-- 1 root root 31, 232 Dec 2 04:06:42 2002 afiot/special/dev2 -rw-r--r-- 3 root root 685 May 13 21:53:11 1998 afiot/special/hardlink1 -rw-r--r-- 3 root root 0 May 13 21:53:11 1998 afiot/special/hardlink2 -> afiot/special/hardlink1 -rw-r--r-- 3 root root 0 May 13 21:53:11 1998 afiot/special/hardlink3 -> afiot/special/hardlink1 b------r-x 1 news root 10, 5 Jan 1 00:00:00 1980 afiot/special/blex2 lrwxrwxrwx 1 root root Dec 2 03:01:23 2002 afiot/special/blexln S-> blex p------rwx 1 news root Jan 1 00:00:00 1980 afiot/special/blexp s------rwx 1 news root Jan 1 00:00:00 1980 afiot/special/log lrwxrwxrwx 1 root root Dec 2 03:01:23 2002 afiot/special/testsoftlink S-> test d------rwx 1 news root Dec 2 03:41:37 2002 afiot/special/vivi -rw-r--r-- 1 root root 101 Dec 2 03:41:37 2002 afiot/special/vivi/blex -- compressed -------rwx 1 news root 0 Jan 1 01:00:00 1970 afiot/special/X0 crw-r--r-- 1 root root 47, 2 Dec 2 04:07:11 2002 afiot/special/dev3 brw-r--r-- 1 root root 239, 158 Dec 2 04:06:55 2002 afiot/special/dev4 crw-r--r-- 1 root root 47, 2 Dec 2 04:07:34 2002 afiot/special/dev5 prw-r--r-- 1 root root Dec 2 04:07:49 2002 afiot/special/pipe1 p--x-w--wx 1 root root Dec 2 04:09:09 2002 afiot/special/fifo1 -rwSr-Sr-- 1 root root 0 Dec 2 04:09:57 2002 afiot/special/a -rw-r--r-T 1 root root 0 Dec 2 04:09:57 2002 afiot/special/b -rw-r--r-- 1 root root 0 Dec 2 04:09:57 2002 afiot/special/c -r-S-wS--t 1 root root 0 Dec 2 04:13:53 2002 afiot/special/d drwxr-xr-x 1 root root Dec 2 03:46:55 2002 afiot/files -rw-r--r-- 1 halt mem 3 Dec 2 03:45:17 2002 afiot/files/a1 -rw-r--r-- 1 news daemon 182 Dec 2 03:45:08 2002 afiot/files/a10 -- compressed -rw-r--r-- 1 root root 1490 Dec 2 03:45:13 2002 afiot/files/a100 -- compressed -rw-r--r-- 1 root root 9244 Dec 2 03:45:21 2002 afiot/files/a1000 -- compressed -rw-r--r-- 1 root root 0 Sep 19 20:49:43 1999 afiot/files/archx -rw-r--r-- 1 root root 205 Dec 2 03:45:43 2002 afiot/files/b1 -- compressed -rw-r--r-- 1 root root 291 Dec 2 03:45:51 2002 afiot/files/b2 -- compressed -rw-r--r-- 1 root root 1933 Dec 2 03:46:05 2002 afiot/files/b3 -- compressed -rw-r--r-- 1 root root 133 Dec 2 04:32:43 2002 afiot/exttest.gif -rw-r--r-- 1 root root 145 Dec 2 04:32:47 2002 afiot/exttest.lha -rw-r--r-- 1 root root 0 Jan 1 01:01:00 2000 afiot/jan2000 -rw-r--r-- 1 root root 128 Jan 1 00:00:13 2000 afiot/y2k -- compressed -rw-r--r-- 1 root root 0 Jan 2 01:01:00 2030 afiot/jan2000-2 -rw-rw-rw- 1 root root 297 Jan 1 00:00:13 2000 afiot/logfile -- compressed drwxr-xr-x 1 root root Dec 2 03:01:23 2002 afiot/from -rw-r--r-- 1 root root 2 Sep 12 10:40:11 1999 afiot/from/a -rw-r--r-- 1 root root 26 Sep 12 10:40:14 1999 afiot/from/b.z drwxr-xr-x 1 root root Dec 2 03:44:22 2002 afiot/to1 lrwxrwxrwx 1 root root Dec 2 03:01:23 2002 afiot/link1 S-> y2k -rw-r--r-- 1 root root 119 Sep 12 11:15:10 1999 afiot/t1 -- compressed -rwxr-xr-x 1 root root 196 Sep 17 22:09:53 1999 afiot/js -- compressed -rw-r--r-- 1 root root 314 Nov 25 22:26:13 1999 afiot/llt -- compressed afio-2.5.2/regtest/afiotsmall.tgz000066400000000000000000000410451340024410000167640ustar00rootroot00000000000000!== tuWҔɳ FkVٟe#?;Վ5;3JZZ49|Bi¡!$i844Ir(i& @46=Idckgg{y+^R"x4 ?q<pHc!:Hlq(Zda꺽R׏Qmj H$P[ZR"E/vh/$"~jx׿o9`%'-cIW?Xvn?r?w4qOb}k{JAf_)c$sWĎUl߉+͒m%`I8{@ݯf~=~7~5/8;Qh,~8.z[SJyɗSTjr^4~]OUivb-TR2H|.JⴏMBrD{(!ڰ8ZǑ?/*Y唩u!%Ekxw_jYoA}OLlܢ;Gp/EC'Uڂi2|,E1B60 7_d??~ V},Uq7b1@`_īxw]I'<eoVqWB?G8Cwv{ _>8ik.Jۑ}<[|Ǯ_gjU/zrS|*_;?w~ktkWze;OE<%]t/ؒ<7멛{'w ocE{_g>w>w7{<Ǭv=M>ÏK}t'pőykΞL+xS~_'}ρXijg0yA5OvC3?`t{yW:/gk~to!uz'/u̍/8q{r3;FauƝWOX`mҷM;Z.y9tz?C'vt<'FOl&ZÑp4DMVYJ.,D5LE=W˪$2$[Ϋt|^iY,Htm ɦn1T*9n-adjlBz>`(lVn!`ZifY*&]dx"-@<(a2 q" G2en儔G4Dq#E"9sXC IK)!ن3 $$9H &.Nq,!`9H ?WrCTTmqYn V |T.XW3 ksEdɤDmbd$eRˢYYKp-ԱM&)R4ByHɢ5%,ټbS Ibb a8ٜUl`GQUF CYu>MN7wj>2J'6ѹk ;^vު/T뮦2>]45¦eC Js,in@׻ׇK } e2ҋ/spC7Y"J%WjAB1ܗtLW:XH:'Eˈp:UnW]zvi=YlE+|]/aARU]y.] fBIt ^Zgo0|Zq.\'_E>SYi똝l~cb`Twoȵ ve+aW i>`jdH)n2KE 8&*80ɔG_0ܾ3(>v E"> Žݐ8N'B>]A[׋U7w!ydv}iPf;%ęM!i8"q. UJz/b`U7( gS+8;B cwrWG`ppPBW$Z~9z2 "kKnwsݜ+ۿ j i 4M%TPDvq[;`/0?6My[翝M#' ּP hz8_4_K86#h,ؚZJL_gO!׀>. qNO|ʫb|Gd}>w٫~U^+(qwT^-b6ӊm̷#--w~X#b?ׂ.!/;In .GtWÎp[Jf >"_!76@\YEںs甜 bxY3EO퀺Zr?_Du-/xo1XO+uK#uD\l ˅mjK-x 6W~/_󒺨~ ɻ"=`ZF ,s??X.u'+тGVq71~/iIS*ٸ WfE$o}fb;;J5ۦUi,N%Ӟ'M$9_4,2MZ`D6hJdDTɜD,23n9 )2nǦM16M*sd"FϢpq@dUR `$E3|D wd#<6TN36ba@ :> []VGt= ~Bz*;(yt~ hڂPmh> šdf$݂mJfKS C'!)67.@@%a#:32Lؾ&13CF(30s,`CtD8#9fmiƮ@uhDشCY GqUɕHI:v]`X'@ 4Wneй@{]"!&0k mDlNPApe2GXU:&S%ʚV.7t΢)2< 0噄7@5@MTMw Ea,(t8TMg;j@(Jd*7&,EZ2cfq0Z10XhؔJO0 }Nf2,NxPbn$!Pۑ¢"G,&n=&@ P^Ao) F0KZ NUFb9 ldt"598cIqAW؄L,vq(aڶ,\RaTS` B7@fګWca!,hhxW{W{W{W{W{W{WGƫyD8\+סZy6KK)biuKJC}ٻ;5ќ}dtɦ<T辜brEC5tYW~:gITRȶ@r |q1t1GRPO%3`zC[zCtjeV;͐B%/UTnZSۛEyBRoGUiIA8Q@r:/s~ P-ʬߔ˛p{& 2`-t2?:7xLD4Sdӽ-F Z9ʱ;S-h}@jiL YJ$kH3N*@#A$dI lI `=7$Z:KqYa]2Zg&2t9WJ` bJl.cIz.)w)C^N=-'GݚSoU4 B[b|8s*Xz!EPbK%-9?<>dVWjk11L'.rVGɱ&Nmu8cǨ*Óá䆶܀'4h Sh0: :bn8]Ka 98O(E)`6"͂ ޽mm -> n(ɦ[v8tHbDl!ٙYs=0U$AN?:mI`P'@'cyA\HMtTomp %e]0#%*^Lu#k2}%WGd8ϻT׿u|CHVGil1#:WonbڶYs/w}á6#$xn3MWgnmUԥnu.O_L-~ upcXPO7VVo CTU!LjI0Vu)t{Piԥx6y6Z}iTanID*r-Ue9KG|)G:cMdذ8|z`n8v k>@ꕨ@Yop"+טt>҆S-Xz{+,OR]\͚_㖴Obh_HC~A\ݮxѾJ /݂[[5FjŨ A{5}FRE[R:Ӛ*Jz%F!p4ҷRJu&ٸXWLxfMH: .>B# o?O*miݧo1:ytm[o˵ IfMΎ^KǙSnt"}g8ˢ9M9: 唥wjAHĪGMcVTkiJ%2'6f$ x^{͛&]KΡnu^,-&mBخ c63%5)p؎^2;p cu" 7)sQhUrZt齂eX6*;B~ТCѥwCVT^]W~yB[5{ppk/DwFMb )tE?^RK$ɪ~Wdw:sէ>MN[Mc2ٚjpu nrJG/pci?(91-}(31Z},/u뷄Op ͽ+r,gէ:%qlë{Rf7~xݗG/?xO$ntZ!c0:]wv$,0C+{ZZg3=1sMzgK 1ZwZ.]u v raHNIk_DOSk*n?'Kܲ{?d]zx.;[#m}tti'r,:.Ȣ4/I+ {u5zkwTcOϭ Εnn,qWg:aOs/gᬞ5q=nߓ$Zcgchy.%Z^r˖,Ռ\?](bdm~߶"g?zWvJgN)}1N^m}&׵B4:kBٍEY*-z 4ToRUeoDoK~fk lE m1 BuBL2|"fJ2zeƇg m:JS*|CC_g!Q&4i$ ^em_[wypmZwN`-3t}ξE`u5°Kj.F֤{w598vnj|:8$׷h!,)°t|3JĽcI?Rgj2+60ҔU+txEr4춴E6O jĵ62%il>6$LL0Y@+:,oŎV^ zs.2F$ TV*)DZg>"&O|y}2ns$ފxhHRl[]o [vW:Do,jR &;֔](߅-Y ^1:~n,_b}_O!xI4%.]Lcb.rAܔ./$peQ ^e ɀM/LQRϻFvisPIL}Z@=c{sM[hILEWiTŠ_НCvp-dιvaí ۃrϏ/  'BUkH>O/eHCwYZֺ7MmῘ:|m:?]/*p\ *=ʮ_rxm) A.W}8*ң)Y|{A ,Td6s#n#PFy1|)Y"\L:IoL/WnL^ʎri(7W [)v;Wcxk1ۙ=/Ԍfx[ͮJc?[AG n~"vvx&J8augżZyGG=ٍuZ!|#ʟWƔz&|,S>8Midd hg̍1eEmf :V\^*.hf]So7lRMxᮏ# 3qW =vml֤ݧapl|oCf*QDU~]C}eK6w+<:O:^(G?4ٳuyo#maXh?j=nxבΥa~%eyJjG ?[4a\_UPz0T=[eA{2eugBYĎuMkT)=EDUV%յy,{Kqg'nvũXʄ/><}=6cc-?ج{lh>S0!T|ۆn0u͌%J95D_Es)jQ?L+#"-} ƧYL:3Jn*oH4fdƝ Q!UL9M&!PMw5+? a)3s;ٴvn;]8S{r؞LJ7vN^nʨ2)O k*?  /fä́0:II_uZor {s1VznXm'd z:d?T%m?Jܝi A6ygHN&4Yi;m ?PՍ5wW%z?6rZ0tpGN[y/_[~*N~`3;ԩ/a~T2ڔ>3G@([H;;FϑhQ6NF^>ͅscmrFn"s9U+otC>ȧ칏7Lr$b}ToKZzַ9VN?&W^bIO&NyM^,c^H"ٍh72pwݣepYK?1j7h|W+͇gkQj0$곊Wi?U|+hmEHҦH2K"r&̀k6C?ΐ~oe 5nj.KHDȏɸSZz]2됄J0Dga9S‡6a v{(/'_Co'Q(Ck b I=Z8㼔GyCL*.;O^:xʴ3mwϰWSC2MW흞um ~t0ϲd*2-*{͍|&`n`6cxCT~={^wC0<-XhiR9ffE>"}Xa^ %G^Z."oskFS p+= BnO:7w^F%dQgTx32+[H̍4tMRnHϽwF*{I:T[$lRK*Lڴ̚m2LX ;!&t{+*6DP-xi"k?~cBt{WP^ 0מgsu/;6ivUGiz(JGdN)Hq<(T_"7m޺Re7\\376S *s^S1=f#b &12u[kzSͻÃ'O੏NFd~VI[rb.Ο+u4sg&%@EoOƥM]=Z[|\3![/A^}^nz|ϩ%fId7;td͞S a -Ne"}occj\ #>O?H~}x J:deYcWM'-H16>.KƝFaq{ ]p9EW vӑEBe?}d_p8ڟ|t 7צ6>c^5o(7}zK_1plևً6yUbO7 Mdrli][zO|ǰp692˗5a'+_Y\/)ЉN馧$uغ^+2EP_1rA]aؕRl--;ZԏMקoYCw 5i5 (]vrb'w07VyQl]MsUᾷeS?tXb*u}G8Ec'=Wo<+?/gB-5 I!&kI! iwLz4{L?>4_S잾6h]á^Re= [MK}PMBױ+X~SZ5rL]7d`z\wq&XsjŮ'f0cFS3ePd_pŸ IFf` :Yd~DZlZ+c+}J~L$\͕N|[ r|~'׎ptY:>Z|/ߩi').oa7|}gwRݔ~SK6+kk{{_UMjus U*@@!ڍ[Xm'Mߪ 5                  ki2ul;]).NJ(@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ί(h#MFNQ;[[wnYww榄,oo݉ʷp,s8ՇQtg wALʵ܏^UR&,Us?&nwZ$JFV_ڿWnwSW?&X.nnTɍU4W,3~ V2?Œ3dq׿O[M׿[ܬnUvv%\]Knqק:IfyʕVYU]*u:YPY>NygZ.8qi7.k7Oia;|ݻwm\MsK!ŝe]ljZeS,yM|FhA9:œ0^T5̪V5;i\S؊XZ^'9QnG_{Q0VkGݨѣܻ8a]dhIw_0+_/8̼b|1ע} b";aWQR\nE _>yU~E7߾?7NǃDZu9fT+o4Nd7>?b>=UìDrMlFvÜGz}_ivD۷vD?mL;ilnnkKP΄%st;^;a'Y{?|ӝy_~bDG5VYʖ4 h}ŏl[ۇvww?"w+wDz^ڴ뿲M,ss5wZllˊfUtu+v&+EMܮn1U^܇]wgswrYST+$˿86\entzH(767acɰ\|9Gώƺl1I7%4o>9:zl3z["Ǎl \nz5*u>*7Z4&*[TJftWo*џr+&{FvYmT+U}[Yvy'YFQmmËaIh+ӍtIѥRnE_RmoE^yT ՝h}=W^%OC,rgwgn#Yej<>mG=֣m=}MBߊQܛ}uwnWQe6ԇM歿1tK"ܵd84)/:QQRHszgy,ݝͻA2:(VLTRmdP)s|VTZk +ʽ#Fi: !Ϙ0707070014030722231207770000000000000000010000000757250364100002700000000020afiot/link with spacesname with spaces0707070014030722241207770000000000000000010000000757250364100001400000000027afiot/linkxYetc/sysconfig/ipchains0707070014030722251207770000000000000000010000000757250364100001400000000025afiot/linkyYetc/sysconfig/ipcins0707070014030722260407550000000000000000010000000757250504700001600000000000afiot/special0707070014030722271006440000000000000000010000010571622725100002700000000753afiot/special/afio.lsm.9/+.9/Ao1 $RhQhDV8Nvږ=&$T`ɒe|WA|7dq 1^^ɅsY+ݬV &MRXEݙ_ J9@jl,!b:6àzL񳍏"PGV \z浦p+]C$a|.HEg9bjLpS·ѻU%?sj{YeZabNEOYIGizS"Y?5|&9A,Cg;oxm:LQNڪ$Q/܁xrڠg|Ő+ ^,@o7o?z{#;,#sɜz<@'YEN;󐐬t ]ȘwBs E't!W e'C%bTPkZ;Dh,Nr( mLM܊9`m8 $U kEڔШS̘7^y-QE%HɊJagx\I'lxcpK,%1R-v!ŬtR^ %IJj$覉)_!Ʊ~YEPV8W6ݍGu i\gPqCe5k{DgU;~7ïjo\0707070014030722311006440000000000000000010000000604723531300003200000000322afiot/special/spec.cpio.z:0;^\֑xA1+P\p(_ asB,`y RWʮQ R7G?)'lV+{T H 7#6*tewbB,, 5`ծ}UBP B䞜<u4!A>A }w0707070014030722441006440000000000000000010000010666634255400002500000000076afiot/special/afio.cl6`9zӌX󤖦)pVk+#_]\pWC;l)0707070014030722471007550000000000000000010000000604714237600002500000000117afiot/special/test.zĜ0KMWHLWЭ*)KV-V0P(*)*N-QЭ*JMLJN,*؁d01L B\ n0707070014030722501006440000000000000000030000010652637504700003000000001255afiot/special/hardlink1total 27 drwxr-xr-x 4 root root 1024 May 13 21:53 . drwxr-xr-x 23 root root 1024 Mar 24 1996 .. -rw-r--r-- 1 root root 16450 Jan 30 1995 afio.1 -rw-r--r-- 1 root root 759 Feb 8 1995 afio.lsm -rw-r--r-- 1 root root 1884 Mar 1 1994 exten.c -rw-r--r-- 1 root root 0 May 13 21:53 hardlink1 prw-r--r-- 1 root root 0 Sep 15 1995 rtt drwxr-xr-x 3 root root 1024 Nov 29 1996 spec -rw-r--r-- 1 root root 512 Nov 5 1995 spec.cpio drwxr-xr-x 3 root root 1024 Nov 29 1996 specorg -rwxr-xr-x 1 root root 110 Nov 5 1995 test 0707070014030722501006440000000000000000030000010652637504700003000000000000afiot/special/hardlink20707070014030722501006440000000000000000030000010652637504700003000000000000afiot/special/hardlink30707070014030722511207770000000000000000010000000757250364100002500000000004afiot/special/blexlnblex0707070014030722520100050000000000000000010000000226351376000002400000000000afiot/special/blexp0707070014030722531207770000000000000000010000000757250364100003300000000004afiot/special/testsoftlinktest0707070014031030140407000000000000000000010000000757254414100002300000000000afiot/special/vivi0707070014031030151006440000000000000000010000000757254414100003200000000145afiot/special/vivi/blex.za==1 } Q-O r!<_b\d<\P2)o<ݳ k@ҁG%7>sRс +c+xz0707070014030722550106440000000000000000010000000757254720500002400000000000afiot/special/pipe10707070014030722560101210000000000000000010000000757254732500002400000000000afiot/special/fifo10707070014030722571006440000000000000000010000010757254740500002000000000000afiot/special/a0707070014030722601006440000000000000000010000010757254740500002000000000000afiot/special/b0707070014030722611006440000000000000000010000010757254740500002000000000000afiot/special/c0707070014030722621004210000000000000000010000010757254776100002000000000000afiot/special/d0707070014031030160407550000000000000000010000000757254463700001400000000000afiot/files0707070014031030171006440000000000000000010000010757254447500001700000000003afiot/files/a1/* 0707070014031030411006440000000000000000010000000757254446400002200000000266afiot/files/a10.z4=}A @ нKg;Aw^ I8SԢwY% [P+ 2AzyrD+>2&2L*C][~r5"IQaPl:jK9Inf}ۀH">|~|I=H %U!0I.ЂP)d@;F\Z:s;avHPυq@8$/ZH m%V`tR+Q@gla _e"TVOa*3r]qP-j ˛4ZA"ެe>DT9}Bi#֠Z>2Рr%,}9V$bM!(Σkq\Y8=:|Ұ.v.JQ)zB^%\u 'm&.|TBZ?FSȐIҴ n05\BC\Wk*mY7<(IIN{ozkSrW vˊ8AçB*Go4Xۢ)jj1r WHC i[\HokVSL.'lk'4+ '0#dBiar;h '\S)dүA?YhUԁ ĕ+r5z׶I!dIDV~w5Bn <}a|aB+E-L21a&z#6o`#YNA.2$ ڨϱkaL.uK7(I&S 5:n͎Ð$j5h-+R}%uxR+$2 Qy@j"% S F)4=Qޚy#};gY#KZj Qe]'=\!kbVwhHJS;VL+W-i^05dFJ]^*\iJ?jc:\#{t8h&zcoG gq6l)\LzӮ>UXW>BLR.>K\OFR<:)fƤwBiEpRQLD(0Ngs!mٿQ SF07[^݈tJJ0H=BS76or}ثj]kpO|RժhLI=}]_k{aB>- 8 ey-;5 6KE0u2hxiN':>Ym߬ хxfs~d5$XКj!|aJ Q01󌖞'"җe(`+>eIn";~4x)۷*z |6.E(LYReN{sNX9堓Pj$$yp_;`Q;|W9~,,I*vCUTMs/̓CD|$xJyS%%qbE$^uap6tj]!AfHyԼOFO2R\BmId@L$ kf`_6Ʋzc8E"3Mj`IA$>qDQT,2DNF0"άtG ";Vì=iVĝU9uȚ^##X\$ "-R9Vos{"Q7%{ҁF՜ /$AqoMIŴ&"Mg8&=21Cu CYiT *U \+Q&OXo!Ü`dL]o~(7(#ml8![ yg`~ pg;pIw׹j}vE-;2WEN~D#Qc+4[lz-kNo0UB'B=n.W3,*F+~ҶR(>:V0JY?ɪO1JNE!D$αJzrzB)E[PiO,gFsus^D$]׳=* U>kؿYSǣ\X#g#0+PrJm'(cV$U S)#aU+؃wExO5'ŹӟE*EE]*`"/̒9QwR ;gG8ÿ A1 Mp`>rb'0 MM%'DaӑVզl ǭg%oZ>4 |֊n*$*3eIrpH  ׇEdh񜅢#xq l3~#Ď6]q]tzeɾ2¶C!I 5|mTJˢ{7.F9 ҌS(TXf㪽>{͠o *9 $tVuoC3D4'F5]6Uv ]]Ss,B*Q-3wBJP49\6I)+nPGVELJ]}^sdrr5s7T~h|4;j v+GV1 DfZLlu?gA45&Ϋ rKվHp8R<3ݠà$~#sG9tm8CX`"e,BIM*/=аd>rFGSqvuIKBmW5Q97uwjZe<3_ZJzq",7P՚X4O}K*zA\8eYKi6F%+KcPrRNV~Oƺ,AQuOv^yzU!p* BM8BTƉj-Ѹ}*"m2eSqXK#fN~Jel:)u*MGTam*p|mDBP.JnAOb$]zS= bcUt׸Ţw yn2RףW?|<}|X;fچ5c5B.\|l3[2dk*5a.;~4s*nXB(D/F䌯=|uB丄E,W9 n!Jt*aa OO "4DA#*+M14v͂TC$̐#sWV_+>Kt+rRx{{dF|gC+(T0i6ўm2)f" 6ƻ4J-p-+ p~.BؼJwfz{J9CKNZk2-RSFZ^ٖd<ǽYnܧ6*CSoVJNr 3k-:=SgǴw 뱱;IEd\I9SUY7l z#Ox^NJZ2z@QG,Œ-K5C Om6\;9 wbupg^B>]uIҽ#Nk=/kM/bhzTdoCXK|i\, /ru& H zw;0t$'{o:Y"}ӂ!K>.o/S6i:L鷡M.5Tl; U zU8uBŶ-/w3j-GB[/|j[ukzg筻_uMuz}zmFw[AbuU}f/V̜BstyOr*]4O)xϢѳPQU-*g!= W*o^}ZoZ4RkVXw"VfA;x=$~<5{vǹ}1>9>;/@ /x %`s71ZH`NA uQ4&ʰ@\JYhWlYNiݨ.m?JܺUnGw6]jlf)(sI09`L79  anP‚Ѳ^_% U_|pRY l{)X:dQĜ߷Lw$9|ݱ)~=㻳KH?^B4Y?C_<\ˍs 7 _tؿVM3 5| Wq5vPz,n:ZͺߔڂJ/اz׵htZ_ _奿wNUF o2 FRn ޶#4 GdD];S\0dȦ Zqr3/K"ܝݝ}@uy]Zkc 97guf+On+$C1"rYo]w{Afzځ} Qg$kgi40YNL%X Ii. mq!}3b7ZzTlZq~L»?}t Bp9 "ztb N}:p;Kc}@R?i ;Nο-+qrI?pN%?2Og {-*W0WRJpo~+yzK|^|.Ҋ5뷒FTb?M+ Iui.^v3WŅÛ%;N3˵5n iL2O?vksΚퟱ3-[Zȓ120>Ucu}qT7:tz+DSFŸa[. k>oKD.QEFe.92rIE yY ~}߉Jݚ%}:MlߟYdnGi=,uRm ~(c'(~a^^lLRFI)O9X J3PVi.gD CZbU90L0X@In5A9=SX3{̏WU}DgZkIaG _IuJTBf3978*'Ow8[{ c=lz}`ym3Fate}~J VN-B+tи~^ثsƁsondy:ʷFQ~m-|.%ͩNjJcPz(čg2Kn<[=YjQ]pl:]^Tgrg($[cW:%),^RHZ!vYig%7~.F"a0y9o8#Wx[0xma ="."7 [oc/.G.Pc\ÒQ+'wR6a8ŏ["jRDԄM d5ϥҰ= %IK܎tQ(^:`1%2ӆ/! m7I?8T.ZG36i {#2W"-rxnKHU#)9D s~|:11oEQB"٪M?PZD#OdvD;Y߈>\u2GX]LA**Ɉ'(vUk޺jx8!6]M<8AWkuPKr5X#c$GV2c!p(;a? OMֵq_MSH~Dd?pg:^x$c{FYJ?a2:d0}O99 xn>y[wߵ]Q;nl}z{ 8qE  ` ?%nXatz=4}*+AJ}$yv'#ާ3n˫7<<9 Hk[!Q)-ew 'd6͝nȋ4}=x:Ip8na~u7lxkg1eA'"Ö] v!0@85DqBX ;W_9V\T- v\0 EdߏB1yg]@c;:}Õnqwq2) 7%+)oiOL"=kq~ `6S̝_ ymBE)6׸bd>;jrI;ϋ Z@/:D~vzpӃw3m5q&'SVt)Q$vYf0707070014031030441006440000000000000000010000010677123010700002200000000000afiot/files/archx0707070014031030451006440000000000000000010000000757254452700002100000000315afiot/files/b1.zW=A0 =xKA9`LRTKvn'HVj҄6?N94t`V[:wbH0s9_ ǑBwY 2m=W94 Q\JP!^QZ\ӿ.G MoͥOs0G׳VP*BE"|@DI - 0707070014031030461006440000000000000000010000000757254453700002100000000443afiot/files/b2.z_=AN0@}O1K =C!N3j#NOC{eK;wtam76ͣ$Z`9Joc>>-Z,Tk-e9z:_KIY%ؚC׉cf%(_9Q%IPoRzRƴr[Pc4o&<                  co?hI0707070014031030501006440000000000000000010000000757254455500002100000003615afiot/files/b3.zm=1N1@ўSL B0Xv#!ӓtHϕ-F"#mʲ)eRϑ-Eؔ)yy[Ze8Zzs| -mj1 4Ǻe(t9UaXce2ʥR}1c./)wy,C[_ּj,?EO; @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ c0707070014030722631006440000000000000000010000010757255213300002200000000205afiot/exttest.gifexttest.gif files from jan2000 jan2000-2 js link1 link with spaces linkx linky llt logfile name with spaces special t1 to1 y2k ztest 0707070014030722641006440000000000000000010000010757255213700002200000000221afiot/exttest.lhaexttest.gif exttest.lha files from jan2000 jan2000-2 js link1 link with spaces linkx linky llt logfile name with spaces special t1 to1 y2k ztest 0707070014030722651006440000000000000000010000010703324167400001600000000000afiot/jan20000707070014030722661006440000000000000000010000000703323257500001400000000200afiot/y2k.z}5m81 1FJ 3l*/KȄ)R# MԢ7lZ=ez{MݴF?U:r<_NK)evx0707070014030722671006640000000000000000010000000703323257500002000000000451afiot/logfile.z}5m8͒Kk0CNib\!'-תd&JBS EͰTiQjQXo\f*:z2 kJF4H;UUGuڼ#O&\}rX{7 q$Ldlzr `_m{1SbP2y~bawuj?;^[D{.99>R?y C)(UR{/9[ ?@90aqw_S8Q?"oa0707070014030722731006440000000000000000010000000701732456500001400000000472afiot/llt.zu=8Mn0]1,"R h⴨#MJDH5={f쌫[pxi.HN9m y%89X\'Ȳ$cj!cdXR>4&#hS<Prw+i^HCn:(Xېx}roOaWxmwr `El//؏M紽Ē~l dzAĹֹ8/]:͏31zP Bcl7sc_/O|fN.izTT\Fv0t3i}CMU0707070000000000000000000000000000000000010000000000000000000001300000006303TRAILER!!! afio-2.5.2/regtest/afiotsmallnoroot.af.toc000066400000000000000000000105651340024410000205760ustar00rootroot00000000000000drwxr-xr-x 1 root root Dec 1 23:05:21 2002 afiot drwxr-xr-x 1 root root Dec 2 03:44:22 2002 afiot/ztest -rw-r--r-- 1 root root 2 Sep 12 10:40:11 1999 afiot/ztest/a -rw-r--r-- 1 root root 26 Sep 12 10:40:14 1999 afiot/ztest/b.z -rw-r--r-- 1 root root 22 Sep 12 10:40:11 1999 afiot/ztest/a.z -rw-r--r-- 1 root root 46 Sep 12 10:40:14 1999 afiot/ztest/b.z.z -rw-r--r-- 1 root root 119 Dec 2 05:08:37 2002 afiot/name with spaces -- compressed lrwxrwxrwx 1 root root Dec 1 23:05:21 2002 afiot/link with spaces S-> name with spaces lrwxrwxrwx 1 root root Dec 1 23:05:21 2002 afiot/linkx S-> Yetc/sysconfig/ipchains lrwxrwxrwx 1 root root Dec 1 23:05:21 2002 afiot/linky S-> Yetc/sysconfig/ipcins drwxr-xr-x 1 root root Dec 1 23:16:07 2002 afiot/special -rw-r--r-- 1 root root 491 Feb 8 21:50:49 1995 afiot/special/afio.lsm -rw-r--r-- 1 root root 774 Mar 1 22:51:43 1994 afiot/special/exten.c -- compressed -rw-r--r-- 1 root root 210 Nov 5 23:29:31 1995 afiot/special/spec.cpio -- compressed -rw-r--r-- 1 root root 62 Feb 28 23:38:36 1999 afiot/special/afio.c -rwxr-xr-x 1 root root 79 Nov 5 15:06:54 1995 afiot/special/test -- compressed -rw-r--r-- 3 root root 685 May 13 21:53:11 1998 afiot/special/hardlink1 -rw-r--r-- 3 root root 0 May 13 21:53:11 1998 afiot/special/hardlink2 -> afiot/special/hardlink1 -rw-r--r-- 3 root root 0 May 13 21:53:11 1998 afiot/special/hardlink3 -> afiot/special/hardlink1 lrwxrwxrwx 1 root root Dec 1 23:05:21 2002 afiot/special/blexln S-> blex p------r-x 1 root root Jan 1 00:00:00 1980 afiot/special/blexp lrwxrwxrwx 1 root root Dec 1 23:05:21 2002 afiot/special/testsoftlink S-> test drwx------ 1 root root Dec 2 03:41:37 2002 afiot/special/vivi -rw-r--r-- 1 root root 101 Dec 2 03:41:37 2002 afiot/special/vivi/blex -- compressed prw-r--r-- 1 root root Dec 2 04:07:49 2002 afiot/special/pipe1 p--x-w---x 1 root root Dec 2 04:09:09 2002 afiot/special/fifo1 -rw-r--r-- 1 root root 0 Dec 2 04:09:57 2002 afiot/special/a -rw-r--r-- 1 root root 0 Dec 2 04:09:57 2002 afiot/special/b -rw-r--r-- 1 root root 0 Dec 2 04:09:57 2002 afiot/special/c -r---w---x 1 root root 0 Dec 2 04:13:53 2002 afiot/special/d drwxr-xr-x 1 root root Dec 2 03:46:55 2002 afiot/files -rw-r--r-- 1 root root 3 Dec 2 03:45:17 2002 afiot/files/a1 -rw-r--r-- 1 root root 182 Dec 2 03:45:08 2002 afiot/files/a10 -- compressed -rw-r--r-- 1 root root 1490 Dec 2 03:45:13 2002 afiot/files/a100 -- compressed -rw-r--r-- 1 root root 9244 Dec 2 03:45:21 2002 afiot/files/a1000 -- compressed -rw-r--r-- 1 root root 0 Sep 19 20:49:43 1999 afiot/files/archx -rw-r--r-- 1 root root 205 Dec 2 03:45:43 2002 afiot/files/b1 -- compressed -rw-r--r-- 1 root root 291 Dec 2 03:45:51 2002 afiot/files/b2 -- compressed -rw-r--r-- 1 root root 1933 Dec 2 03:46:05 2002 afiot/files/b3 -- compressed -rw-r--r-- 1 root root 133 Dec 2 04:32:43 2002 afiot/exttest.gif -rw-r--r-- 1 root root 145 Dec 2 04:32:47 2002 afiot/exttest.lha -rw-r--r-- 1 root root 0 Jan 1 01:01:00 2000 afiot/jan2000 -rw-r--r-- 1 root root 128 Jan 1 00:00:13 2000 afiot/y2k -- compressed -rw-rw-r-- 1 root root 297 Jan 1 00:00:13 2000 afiot/logfile -- compressed drwxr-xr-x 1 root root Dec 2 03:01:23 2002 afiot/from -rw-r--r-- 1 root root 2 Sep 12 10:40:11 1999 afiot/from/a -rw-r--r-- 1 root root 26 Sep 12 10:40:14 1999 afiot/from/b.z drwxr-xr-x 1 root root Dec 2 03:44:22 2002 afiot/to1 lrwxrwxrwx 1 root root Dec 1 23:05:21 2002 afiot/link1 S-> y2k -rw-r--r-- 1 root root 119 Sep 12 11:15:10 1999 afiot/t1 -- compressed -rwxr-xr-x 1 root root 196 Sep 17 22:09:53 1999 afiot/js -- compressed -rw-r--r-- 1 root root 314 Nov 25 22:26:13 1999 afiot/llt -- compressed afio-2.5.2/regtest/afiotsmallnoroot.tgz000066400000000000000000000402651340024410000202300ustar00rootroot00000000000000+== tuWҔɳ FkVٟe#lhX3̬q&O(- PS849 &iB%9mӤ!H'8iR{ߛٟ֖:[wۙRNގUP(Gʠ񕿏Gb4E$lq(Yda}4hSk`D"_ ECVi P,9@8 PV:8;xp,g2Ʋ#wzꩯ8i]yĎ{o;c͖mXk\#2G'vy'O<,yFhXW%,Xt|՗~p?/^?فO?cǚUVkRy.ːdjv_g' ^ 6`^jAZ}$M-=oSmV`k5,e>U#%_,ʊlg낯,"mG7걵c~n<ql"%ǹWjcqrUd])^Ő <;/oB04YW7o4_;^)Z֎\=h\"؀4q ϞpՏݑ]2q{?зv}wwN}7'ng~/ zkx߿.O=pں&'s_8+/޶Ɨ/8ogٸW-{?~߼}G_~'1>~Ss%OH $?2c̮?shߋgg7mS|wxCzza}}4oǡ|T3g>vFϏuL_*?d^ xC:}Fo8tǺ}%;.qsz|׻ٸӷAs5]_~棏gcq碯|}D,uc:<~>~Ooztc]:+v'|,>[[qz-.ӇgnC>p{N(gwOc GCHb[0\x8bvbER-; ѿpE]hf#^VUfeH\PU.Ӳ4Yvfm߲9^QMbdUrw=ad}o-UD!=0Wm6pe7&pmZlZ-Kݤ++@FFFi Pe7E 3#6ʼn0ɔ9R)RRr))R mHΑbM60$ qxS>d64E8 5EaH XRS,rf"exH9.:|Ka `v\ *e f&-E,XEdɤDmbd$eRˢYYOp/ԱO[h)P|dQ Δ|A)a$r1 z"l΃*Y6*΄L[X^&LeN-w~nN$zEÆM>`t NDdxm]4@`պk).?u;8!?cLkEWiwΒ%z熐 ɱa \v_z~1S'ҼTvD8($﮼syA!* r?IdUhѝA>ymhЃ Vw F r,R=p7̀gWR;[2@`~3\Պs$o1re" N_,fk4TvxKEU+_5c-gj0OK%WU#CMht[YjO=jy5QH2 r@}APL'1EH#{>q|9O Qvm_/% -XC|^%w)Q5\sCqD>BrY#6`\n:oNSbVq/5P<oppPBW%Z,}2ԕ&+Jm))›~'2 ʱ,c,bQ1;E1;mI%bܗ5ltY[y IKe"(lI>zG b$=fC=X$"WJ X(VAaE G0!dCmo*a08_pxN^+&GtuTsDsD^Bw[\78~P޽17TDht"t7v@ͬ/?DkAF['G(CXE4O ?1̶n7>+vT=x?~mK?,Fc&o\'Ëw_[슠ǓAbmEV ~>w\-`u{Cmyo"6@sJN_ g<X1V?_D fk ~1xS<]1P=[qs,̞C֧ qH(뽍uY2`f i/&#\(JIXMRIir0HH"UXI%2>@N"d)%gY *~/#jyfIY\)!Q9*WX t樖耙9Oa,`h1v5H5TW$GLM7:Ӓzh=t1C4`KS|+։1P'͕4ƀ;j*7v.1>fff6 A;;AF⚂a8Q"[$TT,lz* T:ҩIQX tC ՞7:SI~nZWۛEyBho * F% D'CЪQPr;:/ ~ P-ʬ3 &4vd 3SH'ӃP|BDs9EV0+Ңl']P(LںUC 3)f*[#π?:E` @n$5:u,5Y[Ou7$Z:Kq[a9]2zg3t9J` bJl-cnM#pcFs:[ӵFjZ%!%&MlPCy,[2h-G0|Ÿ<'kRRdz|4v:Juqn}A @BrluL ^+,JE,Prx8=9?QRlEE'$F5Ztnedlt+ғ/l)g?B!!J'w;gjsU%QS+;@` }X Xʕ+r{DbkX6 qBuQfƥ];0L>~IM@v!y76\.Ͷݶ$ȕ9ekE+-FCk68 ResJԬp ؎N'Hb"ԡ{<<͚y@$9'cI`P'@9޾>kͧQ|޵@kx)JۜhKtGzΒ(/u\gku ^gGQusZ[G[ݨZֶ#&:F?_K$~]2׵H>R+nJkCE} (ǃ 鎯ܷhR\%i8z_f!e4?Kprӯ!y!gIśo/v1xq&ݸ=Eh3ek6ao`36ڦGQnGSW8Y\4KBkΛ];;h8bɗQizꔿZL?ߏ%~)N+3'k#htH,I{D3WەPFS2O7}l[o*ۓYa1<:/HhuΡ;^o6İ-7tWIKk#>w$oM^^uEk߰q-Zqucٺ'g"Ǯa~ϟH .[s%ֹ\?Gڰ}A/ڽFl&lTWGw-^ڋi/q/a_ia@[ts(\_4h^sͦ%ATіδ ho-#TGz ~Ot6$֕q8[)3j?N0/SJZi[LN^u'q;֛r풽mٻ5zcCq;6D!'H>{hNhNC9l9e8Z{P:5f:|϶~\"Abc+,e޼i[ѵ_0br&+Y$0 k9~A_ًX*.3;ן;V'Bt2Vm7`E+Xe#'-:tH|/5zyJX={ppk/DtMb )tE?YQK$ɪAOdw: Oԧ1:R}g_Y&}|xuPܵ{.{>ޭMN-c2jpu ^vJG~~,%4'#e.&ZkaT ax͊Yzaj鞥<}vcLn]E,-z3.KGlzV.*Ĥ٤7}e\YRC֭WzSH[8K3Dr*}M&~U:~YOJUY'w=d]x!; cm}li';r,:.ȲV.I+ {uFkwLcOϭM.nn,yWg:as/% Y=w_kFP{Xۗ3iYYw2TkKxQQ/۲XFkJvV3r4vtutȩY*ǞR:ѶŴ;ya=d_ e7er泫y:@3I)#W_3/* _+68;!m/bd< e M2hT}+wvs+Ⱦv`(Mi|cEtƲ>6t:l Ȕ!iz|L21dl갨^z+4&z^ӭPO",`]drHBJk 4KM׭/UR|RE.sMr={Rd҉"Hba&\/ضV%^AV)tY/ MNw)1P ;][&bziufdYz~]뛅]ϟ-,WkKTUUkwNi) [R[“ĵ 7K./؅oۗIw/?%ձ, Pi5W07&:HJ|03eI.Jg;>K:†T6I(ZTvٶ y%;Voon nnBUȑ}ӱwmoD[EuJ[q;]n&~v`Rh˪N<_ VuUzR"C4Yu(:zoa+Z*O.jƓQ]E醏bCzWŰ ,ЋvFӏ'?Il('Wb]D[q0\elnnڰf}6{^{Sm۝/S{UۮUj|ko,[kJK4Zrk.$믒rzGlkj~7OmoCbl:M%dm;l$ɒtluYK6/?߹-_k?*%5Rn-[%Kȭ.ڍwG6ӷnzvv@n8WlW_:-@>bˊGn&?e~ǍFYfazH-̋zيQ1vC(Y쬙Y8ѿzWj];kSo8dv"NY$8YNCz!@oElE~_ ql<5F(W./V~|"W!@bI5Yˮ?\Yp?83t={(i]QSp#;ŴsPIL}@=c4{kC[hIEWiTŠНCvp-d΅raí ۃ%rϏ/   ᧗B]kH>O/eHv&#wYZ׺7Mm>sm7tx>[/+p\ O*=ʮ_rx) A.W}8*ңY|VFv&29AKXc,?n&C{GoƤHNmiʍ?KQz8euEYK~ZZRawYpm17LѠM]Ѿo掷+UuWoV :/w?{Sc7ߵU2G;;~e1z8@g?~jlztvvރbF°|!՚{gHN&4Yi;m ?P5w%z?6rZ0tpGN[y/_݈:A*N~`3;i/a~T2ٔ>3G@([H;;FϑjQ6IF^>z¹_zܠ:\crAGzPgns|#{c?ɢp_#<ےV lUϵ:}6?F/bI3w^=8pHlvc6ڭ ]khx7\ҏu f.;|?ȕs(5sYŏ+ kך?*~G K$ iS$%]\ tf iOڡLo _gHƷ2nTig.|2iԲwqB:$ QYxX0MX)F]~ G!ׇGnS(!%1kȤm#EK^y(;"vYU˯z2LeF3,UALU{oF^>דxY"3]Eqo15҄Mfo޻gOMpH%;m9-X*l0hugU"kӫ^˥tZM|pMhnܝAo͝ץcu aԕꌀʶ{%/á=s#M ]w ^?Ԓ 6=f'p61ӄ+nHi ; o@˻6]pG1P- n)}󟸪Sit$W8 E?O2Az_ZįS{- uܦyBruḱ=MeO{V6ѵ Ux8 %f11|x(*H(}ՃqFNCʌskkIm]65k6LSwwhBǍ 1Jw~RIW}ifQ”~oCr; XAyˆ7? m.BCXu2R.E]NC7O$z4oJCf7:iJY21lDvSKL690,L&H(9:Tktҧ3}ɯ/߼~jAI9K}rzdpO%)& f/geɤJ!c} "v/r66sK.Q a:M?QG_?:x[$uB>u~icTϴ|~ׯ}%v?}%Ǖ 鿳;O*r  ~-,36kTh @@@@@@@@@@@@@@@@@EjyUvսJ_.`} '%                                                                                                                                                                                                                                                                                                                                                                                              pZ}_ʉ&N} (W彝;e,W˻wje{[owwD[8e(3 ׅK&svuڣYF*)~\?ؿEw|%F3v/߫\7)o3a,^/7ƃJzIwe~wҸ}xW+]\c^\@YO'-~/ooWw*{R-sť\~ӿRvuwWGENy,*/:K(E^ge_}8K40>}ݻwi\MsK!ŽjueE/nˤ?E8΀rAD8neVMN14BV$bɉo8rҏQZ+?GGޞ`:_f$f3NY*au^GIhx0 v\?t{.S}WGO?{QKFt2NݐoEƓDNzCv.S## 9JT.vdo8n{@7{vwgN{kGY(C[n_SޓV XL^2Og=|_wϗ_KGZ?TnvvvzWvI@#,~Gg>=_۩v%tζ]]eY.ruoGg[V0ҧ[7])"mnu,,,,,,,,,,,,,,,,,,>t?{6_ޭ.JZgc,vwJNrIlkپCQxmb[G%g}}wrw_]UTٶF'Q˽yo:-*/@Jnni;7xUv} uЏdr3L[ Dafs-;:+_{zw=َ#{$vl[} i;XXXXXXXXXXXXXXXX afio-2.5.2/regtest/cmpstat.c000066400000000000000000000027621340024410000157250ustar00rootroot00000000000000 /* compare status of files argv[1] and argv[2], testing if afio restored argv[2] right. if we have an argv[3], then do not compare uid/gid. returns 1 if the test is not OK or other things are not OK. prints reason for that to stdout. */ #include #include #include #include #include #include char ** av; void fatal(const char *s) { printf("cmpstat %s %s: %s\n",av[1],av[2],s); exit(1); } void fatalls(const char *s) { char buf[1000]; printf("cmpstat %s %s: %s\n",av[1],av[2],s); sprintf(buf,"ls -ld \"%s\" \"%s\"",av[1],av[2]); system(buf); exit(1); } int main(int argc, char ** argv) { struct stat s1,s2; av=argv; if(argc>4) { printf("cmpstat: wrong number of args\n"); return 1; } if(lstat(argv[1],&s1)) fatal("file 1 not found"); if(lstat(argv[2],&s2)) fatal("file 2 not found"); if(s1.st_nlink != s2.st_nlink) fatalls("nlink difference"); if(argc!=4) { if(s1.st_uid != s2.st_uid) fatalls("uid difference"); if(s1.st_gid != s2.st_gid) fatalls("gid difference"); } if(!(S_ISDIR(s1.st_mode) || S_ISLNK(s1.st_mode))) if(s1.st_mtime != s2.st_mtime) fatalls("mtime difference"); if(S_ISCHR(s1.st_mode) || S_ISBLK(s1.st_mode)) { if(s1.st_rdev != s2.st_rdev) fatalls("device number difference"); } if(s1.st_mode != s2.st_mode) fatalls("mode difference"); if(!(S_ISDIR(s1.st_mode))) if(s1.st_size != s2.st_size) fatalls("size difference"); return 0; } afio-2.5.2/regtest/dircompare000077500000000000000000000013011340024410000161450ustar00rootroot00000000000000 #the way of calling this script is strange to preserve compatibility #with the horrid approximation of a bash that I found on a freebsd system. #dircompare compares contents of dirs $d1 and $d2 carefully #prints problems found to stdout #exits with 1 on error. fail=0; #compare file contents find $d1 -type f | $AWK '{ n=$0; gsub("^"d1,d2,n); print "if ! cmp \""$0"\" \""n"\"; then fail=1; fi"; }' d1=$d1 d2=$d2 - >dircompare.tmp #compare file stat blocks source ./dircompare.tmp find $d1 | $AWK '{ n=$0; gsub("^"d1,d2,n); print "if ! ./cmpstat \""$0"\" \""n"\" "cmpflag"; then fail=1; fi"; }' d1=$d1 d2=$d2 cmpflag=$cmpflag - >dircompare.tmp source ./dircompare.tmp rm dircompare.tmp exit $fail afio-2.5.2/regtest/makesparse.c000066400000000000000000000021241340024410000163750ustar00rootroot00000000000000 /* test large file compile environment and make a large sparse file */ #include #include #include #include #include #include #include #include #include void fatal(const char *s) { printf("makesparse: %s\n",s); exit(1); } int main(int argc, char ** argv) { struct stat s; int f; unsigned long long pos; char *bla="bla bla bla\n"; printf("sizeof st_size = %d\n",sizeof(s.st_size)); if(sizeof(s.st_size)<8) fatal("Large file compile environment not present!"); if(argc!=2) fatal("Wrong number of args"); f=open(argv[1],O_RDWR); if(!f) fatal("Open failed"); pos=(unsigned long long)(4LL*1024LL*1024LL*1024LL); if(lseek(f,(off_t)pos,SEEK_SET)==((off_t)-1)) { perror("lseek"); fatal("lseek failed"); } if(!write(f,bla,strlen(bla))) { perror("write"); fatal("write failed"); } close(f); if(lstat(argv[1],&s)) { perror("stat"); fatal("stat failed"); } if(s.st_size!=pos+strlen(bla)) fatal("created file does not have the expected length"); return 0; } afio-2.5.2/regtest/regtest000066400000000000000000000034321340024410000155010ustar00rootroot00000000000000 #set -x export -n BASH_ENV # ancient versions of awk won't work with these scripts, # but will give syntax errors trying to parse their program. # On sun, may have to change this to `nawk' or `gawk'. export AWK=awk echo " * Testing on: `uname -a`" echo " * OS version: `cat -v /etc/issue`" echo " * gcc version: `gcc -v 2>&1`" echo " * Afio shared libraries: `ldd ../afio`" #if 1, inject some errors to self-test the regression test. ERRORINJECT=0 #for security, regresson test will make #some devices and suid scripts (solve more elegantly later) chmod 700 . #binary of new afio NEWAF=`pwd`/../afio #try to find an old afio binary OLDAF=`type -p afio` if [ "$OLDAF" = "" ]; then echo "Can't" find an old afio executable, will not do parts of regression testing... fi #clean up any old regtest directories source ./regtest.clean #---------------- failed=0; #1) run batch of tests with included test archive #OLDTESTD=/root/afio/afiot OLDTESTD=/i/n/v/a/l/i/d TESTA=`pwd`/afiotsmall.af TESTTAR=`pwd`/afiotsmall.tgz TESTTOC=$TESTA.toc if [ "`whoami`" != root ]; then echo Not running as root, regression test will not cover special files like devices TESTA=`pwd`/afiotsmallnoroot.af TESTTAR=`pwd`/afiotsmallnoroot.tgz TESTTOC=$TESTA.toc #umask 000 needed to make tar restore all permissions, as afio does umask 000 fi source ./regtest.do if [ $failed = 1 ]; then exit 1 fi #2) are we on Koen's machine? Yes, then run a second batch of tests #(only on Koen's machine) location of existing unpacked test directory OLDTESTD=/oldlin/root/src/afiot if [ -d $OLDTESTD ]; then if [ "`whoami`" = root ]; then TESTTAR=/i/n/v/a/l/i/d TESTA=/oldlin/root/src/afiot.af TESTTOC=$TESTA.toc source ./regtest.do fi fi if [ $failed = 0 ]; then echo All regression tests OK\!\! source ./regtest.clean fi afio-2.5.2/regtest/regtest.clean000066400000000000000000000002431340024410000165570ustar00rootroot00000000000000 #need to chmod first, or else we can't remove if we are a normal user chmod -R 777 ./t[0-9] 2>/dev/null rm -rf ./t[1-9] 2>/dev/null rm -f ./t[1-5].* 2>/dev/null afio-2.5.2/regtest/regtest.do000066400000000000000000000127751340024410000161140ustar00rootroot00000000000000 echo Doing regression tests with test archive $TESTA.... function printop { op=$1; echo " * $op"; }; function failure { echo "FAILURE in: $op"; failed=1; }; comploc=`pwd`/dircompare function dircomp { export d1=$1; export d2=$2; export cmpflag=$3; bash -c $comploc; }; # do actual regression test wd=`pwd` source regtest.clean if [ "$OLDAF" != "" ]; then printop "unpack test archive to t1/ with old afio" mkdir t1 cd t1 if ! $OLDAF -iZ $TESTA; then failure fi cd $wd if [ -d $OLDTESTD ]; then printop "compare test archive with unpacked old test archive" if ! dircomp $OLDTESTD t1/afiot; then failure fi fi fi printop "unpack test archive to t2/ with new afio" mkdir t2 cd t2 if ! $NEWAF -iZ $TESTA; then failure fi cd $wd if [ $ERRORINJECT = 1 ]; then echo adding some errors to self-test the regression test echo > t1/afiot/jbtdfj echo > t2/afiot/ergergeg echo >>t2/afiot/files/b2 chmod 222 t2/afiot/files/b3 fi if [ "$OLDAF" != "" ]; then printop "compare test archive trees that were unpacked by new and old afio" if ! dircomp t1 t2; then failure fi fi if [ -f $TESTTAR ]; then printop "untar tarred version of test archive into t4/ (the tarred version does not contain future-dated file and socket file)" mkdir t4 cd t4 if ! gunzip -c $TESTTAR | tar xf -; then failure fi printop "compare unpacked tar archive with newly unpacked test archive" cd $wd if ! dircomp t4/afiot t2/afiot nouid; then failure fi fi printop "verify unpacked test archive with new afio"; cd t2 if ! $NEWAF -rZ $TESTA; then failure fi cd $wd # --- now we switch to packing experiments on the test tree # (with the new dates on the dirs etc) printop "pack test tree with new afio again" cd t2 if ! $NEWAF -Zt $TESTA | sort | $NEWAF -oZ ../t2.afn; then failure fi cd $wd if [ "$OLDAF" != "" ]; then printop "pack test tree with old afio again" cd t2 if ! $NEWAF -Zt $TESTA | sort | $OLDAF -oZ ../t2.afo; then failure fi cd $wd printop "check if archive files produced by new and old afio are identical" if ! cmp t2.afo t2.afn; then failure fi fi printop "pack test tree with new afio using -f and some other strange options" cd t2 if ! $NEWAF -Zt $TESTA | sort | tr '\n' '\0' | $NEWAF -oZ -f -s 10m -0 ../t2.afnb; then failure fi cd $wd printop "check if archive file produced using -f and other strange options is same" if ! cmp t2.afn t2.afnb; then failure fi if [ "$OLDAF" != "" ]; then printop "verify newly packed archive with old afio" cd t2 if ! $OLDAF -rvZ ../t2.afn >../t2.afov; then failure fi cd $wd fi printop "verify newly packed archive with new afio" cd t2 if ! $NEWAF -rvZ ../t2.afn >../t2.afnv; then failure fi cd $wd if [ "$OLDAF" != "" ]; then printop "compare verify operation -v outputs of new and old afio" if ! cmp t2.afov t2.afnv; then diff -U 0 t2.afov t2.afnv failure fi fi printop "try to install newly packed archive into t3/ with new afio" mkdir t3 cd t3 if ! $NEWAF -iZ ../t2.afn; then failure fi cd $wd printop "check if tree is identical after packing and unpacking with new afio" if ! dircomp t2 t3; then failure fi if [ "$OLDAF" != "" ]; then printop "list table-of-contents of test archive with old afio" if ! $OLDAF -tvzZ t2.afn >t2.afot 2>t2.afot2; then cat t2.afot2 failure fi fi printop "list table-of-contents of test archive with new afio" if ! $NEWAF -tvzZ t2.afn >t2.afnt 2>t2.afnt2; then failure fi if [ "$OLDAF" != "" ]; then printop "compare table-of-contents files generated by new and old afio" if ! cmp t2.afot t2.afnt; then diff -U 0 t2.afot t2.afnt failure fi fi printop "compare table-of-contents file made by new afio with archived toc" #filter out date/time from tocs to account for timezone differences and #changed time/date stamps on dirs and symlinks $AWK '{gsub("... .. ..:..:.. ....","DATE"); print;}' $TESTTOC >t2.x $AWK '{gsub("... .. ..:..:.. ....","DATE"); print;}' t2.afnt >t2.y if [ "`whoami`" = root ]; then #also, uid/gid to name mappings may also have changed, so filter out non-root files grep -v "^[dl]" $TESTTOC | grep "root *root" t2.xx grep -v "^[dl]" t2.afnt | grep "root *root" t2.yy else #filter out date/time from tocs to account for timezone differences and #changed time/date stamps on dirs and symlinks #also, archived toc uses user root, so blank out user and group fields. $AWK '{$3="x"; $4="x"; print; }' t2.xx $AWK '{$3="x"; $4="x"; print; }' t2.yy fi sort -t @ < t2.xx >t2.arch sort -t @ < t2.yy >t2.new if ! cmp t2.arch t2.new; then diff -U 0 t2.arch t2.new failure fi if [ "$OLDAF" != "" ]; then printop "compare table-of-contents operation -z stderr generated by new and old afio" $AWK '{gsub("[0-9]* second","XX second"); print}' t2.afot2x $AWK '{gsub("[0-9]* second","XX second"); print}' t2.afnt2x if ! cmp t2.afot2x t2.afnt2x; then failure diff -U 0 t2.afot2x t2.afnt2x fi fi printop "check if (the local version of) cpio can list the toc of an afio archive" #gnu cpio needs -H odc, if we do not detect gnu cpio then try -c flag. flags="-itv -c" if cpio --version >t5.cv 2>/dev/null; then if grep GNU t5.cv >/dev/null; then flags="-tv -H odc" fi fi if [ "x$flags" != "x-tv -H odc" ]; then echo ..no GNU cpio found, trying the installed cpio with $flags flags. fi if ! cpio $flags t2.afn.c1 2>t2.afn.c2; then cat t2.afn.c2 failure else #gnu cpio does not seem to return error status on some errors, so #check if stderr had anything... if grep -v blocks t2.afn.c2 >/dev/null; then cat t2.afn.c2 failure fi fi if [ $failed = 1 ]; then echo " -------------- " echo THERE WERE REGRESSION TEST FAILURES fi afio-2.5.2/regtest/regtest2gb000066400000000000000000000016601340024410000160750ustar00rootroot00000000000000#!/bin/sh #set -x echo "Doing >2GB file support regression test" echo " * Testing on: `uname -a`" echo " * OS version: `cat -v /etc/issue`" echo " * gcc version: `gcc -v 2>&1`" echo " * Afio shared libraries: `ldd ../afio`" echo " * Test is done by making a 4.000001GB sparse file and trying to pack+verify it." rm -rf t mkdir t cd t echo bla bla bla >bla cp bla 4.1gb if ! ../makesparse 4.1gb; then cd .. ls -l t/* echo "2GB regression test FAILED, looks like no >2GB support in compiler and/or kernel" rm -rf t exit 1 fi echo wox wox wox > wox echo wuxta wuxta wuxta >wuxta cd .. ls -l t/* echo "running afio -o | afio -r" if ! find t | ../afio -ozZf -L t.log -1C -s50g - | ../afio -rvzZ - ; then echo ">2GB regression test FAILED!" rm -rf t t.log exit 1 else if grep ERROR t.log >/dev/null; then echo ">2GB regression test FAILED!" rm -rf t t.log exit 1 else echo ">2GB regression test OK!" rm -rf t t.log fi fi afio-2.5.2/regtest/statsize.c000066400000000000000000000016721340024410000161170ustar00rootroot00000000000000 /* print sizeof values of struct stat fields useful for compatibility testing */ #include #include #include #include #include int main(int argc, char ** argv) { struct stat s; printf("sizeof st_dev = %d\n",sizeof(s.st_dev)); printf("sizeof st_ino = %d\n",sizeof(s.st_ino)); printf("sizeof st_mode = %d\n",sizeof(s.st_mode)); printf("sizeof st_nlink = %d\n",sizeof(s.st_nlink)); printf("sizeof st_uid = %d\n",sizeof(s.st_uid)); printf("sizeof st_gid = %d\n",sizeof(s.st_gid)); printf("sizeof st_rdev = %d\n",sizeof(s.st_rdev)); printf("sizeof st_size = %d\n",sizeof(s.st_size)); printf("sizeof st_blksize = %d\n",sizeof(s.st_blksize)); printf("sizeof st_blocks = %d\n",sizeof(s.st_blocks)); printf("sizeof st_atime = %d\n",sizeof(s.st_atime)); printf("sizeof st_mtime = %d\n",sizeof(s.st_mtime)); printf("sizeof st_ctime = %d\n",sizeof(s.st_ctime)); return 0; } afio-2.5.2/script1/000077500000000000000000000000001340024410000140075ustar00rootroot00000000000000afio-2.5.2/script1/DONTDUMP000066400000000000000000000000651340024410000151650ustar00rootroot00000000000000^/usr/local/lib/tex/fonts/pk\|^/tmp\|^/usr/local/src afio-2.5.2/script1/backup000066400000000000000000000024501340024410000152000ustar00rootroot00000000000000level=$1 # # Construct basename for dump records FS=`echo $2 | sed -e '1,$s?/?:?g'` echo Dump root = $2 Level = $level echo -n Finding files to dump... DUMPFILES=/tmp/backup$$ #Remove any existing DUMPFILES to avoid possible security exploits if [ -e ${DUMPFILES} ]; then /bin/rm -f ${DUMPFILES}; fi #for cleanup trap "rm -f ${DUMPFILES}; exit 1" 1 2 3 4 5 7 9 10 12 15 date +"%b %d %H:%M" > /usr/adm/dump/newdump$$ # # If it is a level 0 dump simply dump everything... if [ $level = 0 ] then find $2 | grep -v -f /usr/adm/dump/DONTDUMP > ${DUMPFILES} echo done cat ${DUMPFILES} | afio -o -v -f -b 1024 -s 1440x -F -Z /dev/fd0H1440 else # # Otherwise dump only stuff newer... # # Get the date of the most recent dump with the highest level <= $level] prevdump=`ls --reverse /usr/adm/dump/${FS}.[0-${level}] | head --lines=1` if [ "x${prevdump}" = x ] then echo failed echo backup: No lower level dump - cannot do level $level dump exit 1 fi find $2 -cnewer ${prevdump} | grep -v -f /usr/adm/dump/DONTDUMP > ${DUMPFILES} echo done fi cat ${DUMPFILES} | afio -o -v -f -b 1024 -s 1440x -F -Z /dev/fd0H1440 /bin/rm -f ${DUMPFILES} # # All higher level dumps are now invalidated /bin/rm -f /usr/adm/dump/${FS}.[${level}-9] # # Record date of dump future reference mv /usr/adm/dump/newdump$$ /usr/adm/dump/${FS}.${level} afio-2.5.2/script2/000077500000000000000000000000001340024410000140105ustar00rootroot00000000000000afio-2.5.2/script2/README000066400000000000000000000021001340024410000146610ustar00rootroot00000000000000These are the scripts I use to make backups and to restore them afterwards using afio for Linux. They're pretty simple. I put them in /etc/backup on my backup filesystem (with a symlink from /etc/backup on the root FS), and do /etc/backup/backup as root to back the system up. The x.* files are regular expression which cause matching names to be excluded from backups. A trailing slash is used on some to ensure that the directory is saved in the backup but its contents aren't. To restore, I boot of a root/boot/lilo floppy, remake any necessary filesystems, mount the normal root FS on /mnt/root, restore /, then reboot with the normal root filesystem and restore the rest. You could restore everything in one go, although you have to be careful to create any directories required in /mnt/root to mount other filesystems on. (My /home/ftp/pub is on a separate filesystem; if you just have one big FS like I used to then everything can be restored in one fell swoop.) I hereby place this material in the public domain. David P Gymer, 1 February, 1994. -- Dave dpg@cs.nott.ac.uk afio-2.5.2/script2/backup000077500000000000000000000027461340024410000152140ustar00rootroot00000000000000#!/bin/bash # DON'T use the Zsh to run this, IT WON'T WORK RIGHT. # Required: # bash, afio (2.3.6-dpg-1 or higher), gzip, find, sort # Optional: # cat (if non-filtered backups required) # egrep, perl (if filtered backups required) # Some defaults # Gzip compression factor opt_G=6 # Gzip threshold opt_T=1k # Function to ask user if a backup is to be performed, and to do it if they say okay. AFIO () { local excludecmd local foo echo -n "$1 backup on $3:$2, \"go\" to continue, other to skip: " read foo if test "$foo" = "go"; then if test $# = 3; then excludecmd=cat else excludecmd="egrep -v `perl -p -e 'if ($eols) {print "|";} else {$eols=1} s/\n$//;' < $4`" fi # You might want to lose the `sort' if you have a very big filesystem. # Note that though 512 is the floppy sector size, we do a -b 1024 because # the Linux floppy driver handles floppies in 1024 byte blocks internally. find $1 | $excludecmd | sort | afio -ovzFKZx -G $opt_G -T $opt_T -s $2 -b 1024 $3 fi } cd / opt_G=5 AFIO "home" 720k /dev/fd0 /etc/backup/x.home AFIO "*" 1440k /dev/fd0 /etc/backup/x.dot opt_G=9 AFIO "home/ftp/pub" 1440k /dev/fd0 opt_G=9 AFIO "/usr/X386 /usr/local/X386 /usr/TeX" 720k /dev/fd0 opt_G=9 AFIO "/usr/man /usr/local/man /usr/info" 720k /dev/fd0 opt_G=9 AFIO "/usr/lib/emacs" 720k /dev/fd0 # This is where I mount my backup fs from the normal root fs. cd /mnt/backup echo -n "./mnt2.1/"; AFIO . 360k /dev/fd1 echo -n "./mnt2.2/"; AFIO . 720k /dev/fd0 afio-2.5.2/script2/restore000077500000000000000000000015101340024410000154160ustar00rootroot00000000000000#!/bin/sh # This DOES work with the Zsh (which is the only shell # on my emergancy boot/root disk). # Required: # afio (2.3.6-dpg-1 or higher), gzip # Optional: # test (required if it's not a shell builtin) # Function to prompt for possible restore and maybe do so AFIO () { local foo echo -n "$1 ($2 in $3) restore, \"go\" to continue, other to skip: " read foo if test "$foo" = "go"; then # If the archive is damaged, you should add the -k flag to afio. # You could tell afio they're floppies, but that's much slower! # afio -ivxFZ -b 1024 -s $2 $3 afio -ivxZ -b 1024 $3 fi } # This is where I mount my normal root fs from the backup fs. cd /mnt/root AFIO home 720k /dev/fd0 AFIO . 1440k /dev/fd0 AFIO home/ftp/pub 1440k /dev/fd0 AFIO "X11/TeX" 720k /dev/fd0 AFIO "man/info" 720k /dev/fd0 AFIO "emacs" 720k /dev/fd0 afio-2.5.2/script2/x.dot000066400000000000000000000002451340024410000147700ustar00rootroot00000000000000^(usr/)?tmp/ ^usr/spool/(news/.*/[0-9]*$|pmnt/in.coming/news/) ^home/ ^(mnt/backup/|proc) ^usr/(local/)?X386/ ^usr/TeX ^usr/(local/)?man/ ^usr/info/ ^usr/lib/emacs/ afio-2.5.2/script2/x.home000066400000000000000000000000401340024410000151230ustar00rootroot00000000000000^home/ftp/pub/ ^home/ftp/dos-./ afio-2.5.2/script3/000077500000000000000000000000001340024410000140115ustar00rootroot00000000000000afio-2.5.2/script3/afio_pack_gpg000077500000000000000000000032231340024410000165100ustar00rootroot00000000000000#!/bin/bash # # This example shows how to configure afio to write GnuPG encrypted archives. # GnuPG is a complete and free replacement for PGP. Because it does not use # IDEA or RSA it can be used without any restrictions. GnuPG is a RFC2440 # (OpenPGP) compliant application. # This example uses a pass phrase in a file, for increased security. # the file permissions of this file should be set to -rw-------- # (group and world unreadable) to keep the pass phrase secure dir_to_backup=/usr/include/linux passphrasefile=my_passphrasefile # gpg has built-in compression but this feature cannot be used with # afio (it should be disabled using the -z 0 to gpg, which can be set using # -Q -z -Q 0 in afio). find $dir_to_backup |afio -ovz -Z -U -P gpg -Q --symmetric -Q --passphrase-fd=3 -Q --no-verbose -Q --batch -Q --no-options -Q -z -Q 0 -3 3 my_archive_file 3<$passphrasefile # The reason why gpg built-in compression cannot be used is as # follows. When compression is used, and gpg is run twice on the same # input file, it can generate differing outputs with different # lengths. This is a problem for afio if the output length is larger # than the afio -M option value. If the length is larger than the -M # value, then afio will call the 'compression' program twice, once to # get the 'compressed' file length and once to get the actual file # contents and write them to the archive, and if the lenght is bigger # in the second run then the data in the archive will be truncated # (and therefore corrupted). Afio does emit an error message when # this happens, but it might be overlooked. # the archive written with this script can be unpacked with afio_unpack_gpg afio-2.5.2/script3/afio_pack_gpg_zip000077500000000000000000000033051340024410000173730ustar00rootroot00000000000000#!/bin/bash # # This example shows how to configure afio to write GnuPG encrypted archives. # GnuPG is a complete and free replacement for PGP. Because it does not use # IDEA or RSA it can be used without any restrictions. GnuPG is a RFC2440 # (OpenPGP) compliant application. # This example uses a pass phrase in a file, for increased security. # the file permissions of this file should be set to -rw-------- # (group and world unreadable) to keep the pass phrase secure dir_to_backup=/usr/include/linux passphrasefile=my_passphrasefile # gpg has built-in compression but this feature cannot be used with # afio (it should be disabled using the -z 0 to gpg). In this example # we compress the files in the archive anyway by first passing them # through gzip, then through gpg, using the auxillary script # gpg_zip_encrypt. find $dir_to_backup | afio -ovz -Z -U -P ./gpg_zip_encrypt -3 3 my_archive_file 3<$passphrasefile # The reason why gpg built-in compression cannot be used is as # follows. When compression is used, and gpg is run twice on the same # input file, it can generate differing outputs with different # lengths. This is a problem for afio if the output length is larger # than the afio -M option value. If the length is larger than the -M # value, then afio will call the 'compression' program twice, once to # get the 'compressed' file length and once to get the actual file # contents and write them to the archive, and if the lenght is bigger # in the second run then the data in the archive will be truncated # (and therefore corrupted). Afio does emit an error message when # this happens, but it might be overlooked. # the archive written with this script can be unpacked with # afio_unpack_gpg_zip afio-2.5.2/script3/afio_unpack_gpg000077500000000000000000000005201340024410000170500ustar00rootroot00000000000000#!/bin/sh # # This example shows how to configure afio to unpack GnuPG encrypted archives. # it is a companion script to afio_pack_gpg, see the comments there. passphrasefile=my_passphrasefile afio -ivzZ -3 3 -P gpg -3 3 -Q --decrypt -Q --no-options -Q --batch -Q --passphrase-fd=3 -Q --no-verbose my_archive_file 3<$passphrasefile afio-2.5.2/script3/afio_unpack_gpg_zip000077500000000000000000000004241340024410000177350ustar00rootroot00000000000000#!/bin/sh # # This example shows how to configure afio to unpack GnuPG encrypted archives. # it is a companion script to afio_pack_gpg_zip, see the comments there. passphrasefile=my_passphrasefile afio -ivzZ -3 3 -P ./gpg_decrypt_unzip my_archive_file 3. vol="$1" device="$2" message="$3" # debugging -- show error message to ensure it was a "no space left" case "$message" in '') ;; *) echo "Got error '$message'";; esac case "$device" in /dev/tape|/dev/ntape) medium=tape echo -n "Ejecting tape..." mt -f$device offline echo "done." ;; /dev/floppy) medium="floppy disk" ;; *) echo "Unrecognized medium device!" medium="backup medium" ;; esac cont=true while $cont; do echo "Please insert $medium for volume #$vol..." echo -n "Press \"return\" when $medium is ready, or enter \"q\" to quit: ^G" read ans case "$ans" in q*) exit 1;; esac case "$medium" in tape) mt -f$device status >/dev/null 2>&1 case "$?" in 0) cont=false;; *) echo "Tape status error.";; esac ;; *) cont=false esac done