Port of freebsd pax.

This commit is contained in:
Ben Gras 2006-01-20 14:45:27 +00:00
parent e04832580d
commit bea0bcc2eb
30 changed files with 14876 additions and 1 deletions

View file

@ -14,7 +14,7 @@ COREUTILS=gnu-coreutils-5.2.1
VIM=vim63
PERL=perl-5.8.7
SMALLPROGRAMS=`arch` aal advent ash autil awk bc byacc cawf cron de dhcpd dis88 elle elvis ftp ftpd ftpd200 httpd ibm indent m4 make mdb mined patch ps reboot rlogind scripts sh simple talk talkd telnet telnetd urlget yap zmodem
SMALLPROGRAMS=`arch` aal advent ash autil awk bc byacc cawf cron de dhcpd dis88 elle elvis ftp ftpd ftpd200 httpd ibm indent m4 make mdb mined patch ps reboot rlogind scripts sh simple talk talkd telnet telnetd urlget yap zmodem pax
usage:
@echo "Usage: make all # Compile all commands" >&2

23
commands/pax/Makefile Normal file
View file

@ -0,0 +1,23 @@
# Makefile for pax
CC = exec cc
CFLAGS = -O -D_POSIX_SOURCE -DNET2_STAT=1 -D_MINIX=1
LDFLAGS= -i
all: pax
OBJ = ar_io.o ar_subs.o buf_subs.o cache.o cpio.o file_subs.o ftree.o \
gen_subs.o getoldopt.o options.o pat_rep.o pax.o sel_subs.o \
tables.o tar.o tty_subs.o fgetln.o
pax: $(OBJ)
$(CC) $(LDFLAGS) -o $@ $(OBJ)
install -S 256k $@
install: /usr/bin/pax
/usr/bin/pax: pax
install -cs -o bin pax $@
clean:
rm -f *.o *.bak core pax

1298
commands/pax/ar_io.c Normal file

File diff suppressed because it is too large Load diff

1237
commands/pax/ar_subs.c Normal file

File diff suppressed because it is too large Load diff

993
commands/pax/buf_subs.c Normal file
View file

@ -0,0 +1,993 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
#if 0
static char sccsid[] = "@(#)buf_subs.c 8.2 (Berkeley) 4/18/94";
#endif
#endif /* not lint */
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pax.h"
#include "extern.h"
/*
* routines which implement archive and file buffering
*/
#define MINFBSZ 512 /* default block size for hole detect */
#define MAXFLT 10 /* default media read error limit */
/*
* Need to change bufmem to dynamic allocation when the upper
* limit on blocking size is removed (though that will violate pax spec)
* MAXBLK define and tests will also need to be updated.
*/
static char bufmem[MAXBLK+BLKMULT]; /* i/o buffer + pushback id space */
static char *buf; /* normal start of i/o buffer */
static char *bufend; /* end or last char in i/o buffer */
static char *bufpt; /* read/write point in i/o buffer */
int blksz = MAXBLK; /* block input/output size in bytes */
int wrblksz; /* user spec output size in bytes */
int maxflt = MAXFLT; /* MAX consecutive media errors */
int rdblksz; /* first read blksize (tapes only) */
off_t wrlimit; /* # of bytes written per archive vol */
off_t wrcnt; /* # of bytes written on current vol */
off_t rdcnt; /* # of bytes read on current vol */
/*
* wr_start()
* set up the buffering system to operate in a write mode
* Return:
* 0 if ok, -1 if the user specified write block size violates pax spec
*/
int
wr_start(void)
{
buf = &(bufmem[BLKMULT]);
/*
* Check to make sure the write block size meets pax specs. If the user
* does not specify a blocksize, we use the format default blocksize.
* We must be picky on writes, so we do not allow the user to create an
* archive that might be hard to read elsewhere. If all ok, we then
* open the first archive volume
*/
if (!wrblksz)
wrblksz = frmt->bsz;
if (wrblksz > MAXBLK) {
paxwarn(1, "Write block size of %d too large, maximum is: %d",
wrblksz, MAXBLK);
return(-1);
}
if (wrblksz % BLKMULT) {
paxwarn(1, "Write block size of %d is not a %d byte multiple",
wrblksz, BLKMULT);
return(-1);
}
if (wrblksz > MAXBLK_POSIX) {
paxwarn(0, "Write block size of %d larger than POSIX max %d, archive may not be portable",
wrblksz, MAXBLK_POSIX);
return(-1);
}
/*
* we only allow wrblksz to be used with all archive operations
*/
blksz = rdblksz = wrblksz;
if ((ar_open(arcname) < 0) && (ar_next() < 0))
return(-1);
wrcnt = 0;
bufend = buf + wrblksz;
bufpt = buf;
return(0);
}
/*
* rd_start()
* set up buffering system to read an archive
* Return:
* 0 if ok, -1 otherwise
*/
int
rd_start(void)
{
/*
* leave space for the header pushback (see get_arc()). If we are
* going to append and user specified a write block size, check it
* right away
*/
buf = &(bufmem[BLKMULT]);
if ((act == APPND) && wrblksz) {
if (wrblksz > MAXBLK) {
paxwarn(1,"Write block size %d too large, maximum is: %d",
wrblksz, MAXBLK);
return(-1);
}
if (wrblksz % BLKMULT) {
paxwarn(1, "Write block size %d is not a %d byte multiple",
wrblksz, BLKMULT);
return(-1);
}
}
/*
* open the archive
*/
if ((ar_open(arcname) < 0) && (ar_next() < 0))
return(-1);
bufend = buf + rdblksz;
bufpt = bufend;
rdcnt = 0;
return(0);
}
/*
* cp_start()
* set up buffer system for copying within the file system
*/
void
cp_start(void)
{
buf = &(bufmem[BLKMULT]);
rdblksz = blksz = MAXBLK;
}
/*
* appnd_start()
* Set up the buffering system to append new members to an archive that
* was just read. The last block(s) of an archive may contain a format
* specific trailer. To append a new member, this trailer has to be
* removed from the archive. The first byte of the trailer is replaced by
* the start of the header of the first file added to the archive. The
* format specific end read function tells us how many bytes to move
* backwards in the archive to be positioned BEFORE the trailer. Two
* different postions have to be adjusted, the O.S. file offset (e.g. the
* position of the tape head) and the write point within the data we have
* stored in the read (soon to become write) buffer. We may have to move
* back several records (the number depends on the size of the archive
* record and the size of the format trailer) to read up the record where
* the first byte of the trailer is recorded. Trailers may span (and
* overlap) record boundries.
* We first calculate which record has the first byte of the trailer. We
* move the OS file offset back to the start of this record and read it
* up. We set the buffer write pointer to be at this byte (the byte where
* the trailer starts). We then move the OS file pointer back to the
* start of this record so a flush of this buffer will replace the record
* in the archive.
* A major problem is rewriting this last record. For archives stored
* on disk files, this is trival. However, many devices are really picky
* about the conditions under which they will allow a write to occur.
* Often devices restrict the conditions where writes can be made writes,
* so it may not be feasable to append archives stored on all types of
* devices.
* Return:
* 0 for success, -1 for failure
*/
int
appnd_start(off_t skcnt)
{
int res;
off_t cnt;
if (exit_val != 0) {
paxwarn(0, "Cannot append to an archive that may have flaws.");
return(-1);
}
/*
* if the user did not specify a write blocksize, inherit the size used
* in the last archive volume read. (If a is set we still use rdblksz
* until next volume, cannot shift sizes within a single volume).
*/
if (!wrblksz)
wrblksz = blksz = rdblksz;
else
blksz = rdblksz;
/*
* make sure that this volume allows appends
*/
if (ar_app_ok() < 0)
return(-1);
/*
* Calculate bytes to move back and move in front of record where we
* need to start writing from. Remember we have to add in any padding
* that might be in the buffer after the trailer in the last block. We
* travel skcnt + padding ROUNDED UP to blksize.
*/
skcnt += bufend - bufpt;
if ((cnt = (skcnt/blksz) * blksz) < skcnt)
cnt += blksz;
if (ar_rev((off_t)cnt) < 0)
goto out;
/*
* We may have gone too far if there is valid data in the block we are
* now in front of, read up the block and position the pointer after
* the valid data.
*/
if ((cnt -= skcnt) > 0) {
/*
* watch out for stupid tape drives. ar_rev() will set rdblksz
* to be real physical blocksize so we must loop until we get
* the old rdblksz (now in blksz). If ar_rev() fouls up the
* determination of the physical block size, we will fail.
*/
bufpt = buf;
bufend = buf + blksz;
while (bufpt < bufend) {
if ((res = ar_read(bufpt, rdblksz)) <= 0)
goto out;
bufpt += res;
}
if (ar_rev((off_t)(bufpt - buf)) < 0)
goto out;
bufpt = buf + cnt;
bufend = buf + blksz;
} else {
/*
* buffer is empty
*/
bufend = buf + blksz;
bufpt = buf;
}
rdblksz = blksz;
rdcnt -= skcnt;
wrcnt = 0;
/*
* At this point we are ready to write. If the device requires special
* handling to write at a point were previously recorded data resides,
* that is handled in ar_set_wr(). From now on we operate under normal
* ARCHIVE mode (write) conditions
*/
if (ar_set_wr() < 0)
return(-1);
act = ARCHIVE;
return(0);
out:
paxwarn(1, "Unable to rewrite archive trailer, cannot append.");
return(-1);
}
/*
* rd_sync()
* A read error occurred on this archive volume. Resync the buffer and
* try to reset the device (if possible) so we can continue to read. Keep
* trying to do this until we get a valid read, or we reach the limit on
* consecutive read faults (at which point we give up). The user can
* adjust the read error limit through a command line option.
* Returns:
* 0 on success, and -1 on failure
*/
int
rd_sync(void)
{
int errcnt = 0;
int res;
/*
* if the user says bail out on first fault, we are out of here...
*/
if (maxflt == 0)
return(-1);
if (act == APPND) {
paxwarn(1, "Unable to append when there are archive read errors.");
return(-1);
}
/*
* poke at device and try to get past media error
*/
if (ar_rdsync() < 0) {
if (ar_next() < 0)
return(-1);
else
rdcnt = 0;
}
for (;;) {
if ((res = ar_read(buf, blksz)) > 0) {
/*
* All right! got some data, fill that buffer
*/
bufpt = buf;
bufend = buf + res;
rdcnt += res;
return(0);
}
/*
* Oh well, yet another failed read...
* if error limit reached, ditch. o.w. poke device to move past
* bad media and try again. if media is badly damaged, we ask
* the poor (and upset user at this point) for the next archive
* volume. remember the goal on reads is to get the most we
* can extract out of the archive.
*/
if ((maxflt > 0) && (++errcnt > maxflt))
paxwarn(0,"Archive read error limit (%d) reached",maxflt);
else if (ar_rdsync() == 0)
continue;
if (ar_next() < 0)
break;
rdcnt = 0;
errcnt = 0;
}
return(-1);
}
/*
* pback()
* push the data used during the archive id phase back into the I/O
* buffer. This is required as we cannot be sure that the header does NOT
* overlap a block boundry (as in the case we are trying to recover a
* flawed archived). This was not designed to be used for any other
* purpose. (What software engineering, HA!)
* WARNING: do not even THINK of pback greater than BLKMULT, unless the
* pback space is increased.
*/
void
pback(char *pt, int cnt)
{
bufpt -= cnt;
memcpy(bufpt, pt, cnt);
return;
}
/*
* rd_skip()
* skip foward in the archive during an archive read. Used to get quickly
* past file data and padding for files the user did NOT select.
* Return:
* 0 if ok, -1 failure, and 1 when EOF on the archive volume was detected.
*/
int
rd_skip(off_t skcnt)
{
off_t res;
off_t cnt;
off_t skipped = 0;
/*
* consume what data we have in the buffer. If we have to move foward
* whole records, we call the low level skip function to see if we can
* move within the archive without doing the expensive reads on data we
* do not want.
*/
if (skcnt == 0)
return(0);
res = MIN((bufend - bufpt), skcnt);
bufpt += res;
skcnt -= res;
/*
* if skcnt is now 0, then no additional i/o is needed
*/
if (skcnt == 0)
return(0);
/*
* We have to read more, calculate complete and partial record reads
* based on rdblksz. we skip over "cnt" complete records
*/
res = skcnt%rdblksz;
cnt = (skcnt/rdblksz) * rdblksz;
/*
* if the skip fails, we will have to resync. ar_fow will tell us
* how much it can skip over. We will have to read the rest.
*/
if (ar_fow(cnt, &skipped) < 0)
return(-1);
res += cnt - skipped;
rdcnt += skipped;
/*
* what is left we have to read (which may be the whole thing if
* ar_fow() told us the device can only read to skip records);
*/
while (res > 0L) {
cnt = bufend - bufpt;
/*
* if the read fails, we will have to resync
*/
if ((cnt <= 0) && ((cnt = buf_fill()) < 0))
return(-1);
if (cnt == 0)
return(1);
cnt = MIN(cnt, res);
bufpt += cnt;
res -= cnt;
}
return(0);
}
/*
* wr_fin()
* flush out any data (and pad if required) the last block. We always pad
* with zero (even though we do not have to). Padding with 0 makes it a
* lot easier to recover if the archive is damaged. zero paddding SHOULD
* BE a requirement....
*/
void
wr_fin(void)
{
if (bufpt > buf) {
memset(bufpt, 0, bufend - bufpt);
bufpt = bufend;
(void)buf_flush(blksz);
}
}
/*
* wr_rdbuf()
* fill the write buffer from data passed to it in a buffer (usually used
* by format specific write routines to pass a file header). On failure we
* punt. We do not allow the user to continue to write flawed archives.
* We assume these headers are not very large (the memory copy we use is
* a bit expensive).
* Return:
* 0 if buffer was filled ok, -1 o.w. (buffer flush failure)
*/
int
wr_rdbuf(char *out, int outcnt)
{
int cnt;
/*
* while there is data to copy copy into the write buffer. when the
* write buffer fills, flush it to the archive and continue
*/
while (outcnt > 0) {
cnt = bufend - bufpt;
if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0))
return(-1);
/*
* only move what we have space for
*/
cnt = MIN(cnt, outcnt);
memcpy(bufpt, out, cnt);
bufpt += cnt;
out += cnt;
outcnt -= cnt;
}
return(0);
}
/*
* rd_wrbuf()
* copy from the read buffer into a supplied buffer a specified number of
* bytes. If the read buffer is empty fill it and continue to copy.
* usually used to obtain a file header for processing by a format
* specific read routine.
* Return
* number of bytes copied to the buffer, 0 indicates EOF on archive volume,
* -1 is a read error
*/
int
rd_wrbuf(char *in, int cpcnt)
{
int res;
int cnt;
int incnt = cpcnt;
/*
* loop until we fill the buffer with the requested number of bytes
*/
while (incnt > 0) {
cnt = bufend - bufpt;
if ((cnt <= 0) && ((cnt = buf_fill()) <= 0)) {
/*
* read error, return what we got (or the error if
* no data was copied). The caller must know that an
* error occured and has the best knowledge what to
* do with it
*/
if ((res = cpcnt - incnt) > 0)
return(res);
return(cnt);
}
/*
* calculate how much data to copy based on whats left and
* state of buffer
*/
cnt = MIN(cnt, incnt);
memcpy(in, bufpt, cnt);
bufpt += cnt;
incnt -= cnt;
in += cnt;
}
return(cpcnt);
}
/*
* wr_skip()
* skip forward during a write. In other words add padding to the file.
* we add zero filled padding as it makes flawed archives much easier to
* recover from. the caller tells us how many bytes of padding to add
* This routine was not designed to add HUGE amount of padding, just small
* amounts (a few 512 byte blocks at most)
* Return:
* 0 if ok, -1 if there was a buf_flush failure
*/
int
wr_skip(off_t skcnt)
{
int cnt;
/*
* loop while there is more padding to add
*/
while (skcnt > 0L) {
cnt = bufend - bufpt;
if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0))
return(-1);
cnt = MIN(cnt, skcnt);
memset(bufpt, 0, cnt);
bufpt += cnt;
skcnt -= cnt;
}
return(0);
}
/*
* wr_rdfile()
* fill write buffer with the contents of a file. We are passed an open
* file descriptor to the file and the archive structure that describes the
* file we are storing. The variable "left" is modified to contain the
* number of bytes of the file we were NOT able to write to the archive.
* it is important that we always write EXACTLY the number of bytes that
* the format specific write routine told us to. The file can also get
* bigger, so reading to the end of file would create an improper archive,
* we just detect this case and warn the user. We never create a bad
* archive if we can avoid it. Of course trying to archive files that are
* active is asking for trouble. It we fail, we pass back how much we
* could NOT copy and let the caller deal with it.
* Return:
* 0 ok, -1 if archive write failure. a short read of the file returns a
* 0, but "left" is set to be greater than zero.
*/
int
wr_rdfile(ARCHD *arcn, int ifd, off_t *left)
{
int cnt;
int res = 0;
off_t size = arcn->sb.st_size;
struct stat sb;
/*
* while there are more bytes to write
*/
while (size > 0L) {
cnt = bufend - bufpt;
if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) {
*left = size;
return(-1);
}
cnt = MIN(cnt, size);
if ((res = read(ifd, bufpt, cnt)) <= 0)
break;
size -= res;
bufpt += res;
}
/*
* better check the file did not change during this operation
* or the file read failed.
*/
if (res < 0)
syswarn(1, errno, "Read fault on %s", arcn->org_name);
else if (size != 0L)
paxwarn(1, "File changed size during read %s", arcn->org_name);
else if (fstat(ifd, &sb) < 0)
syswarn(1, errno, "Failed stat on %s", arcn->org_name);
else if (arcn->sb.st_mtime != sb.st_mtime)
paxwarn(1, "File %s was modified during copy to archive",
arcn->org_name);
*left = size;
return(0);
}
/*
* rd_wrfile()
* extract the contents of a file from the archive. If we are unable to
* extract the entire file (due to failure to write the file) we return
* the numbers of bytes we did NOT process. This way the caller knows how
* many bytes to skip past to find the next archive header. If the failure
* was due to an archive read, we will catch that when we try to skip. If
* the format supplies a file data crc value, we calculate the actual crc
* so that it can be compared to the value stored in the header
* NOTE:
* We call a special function to write the file. This function attempts to
* restore file holes (blocks of zeros) into the file. When files are
* sparse this saves space, and is a LOT faster. For non sparse files
* the performance hit is small. As of this writing, no archive supports
* information on where the file holes are.
* Return:
* 0 ok, -1 if archive read failure. if we cannot write the entire file,
* we return a 0 but "left" is set to be the amount unwritten
*/
int
rd_wrfile(ARCHD *arcn, int ofd, off_t *left)
{
int cnt = 0;
off_t size = arcn->sb.st_size;
int res = 0;
char *fnm = arcn->name;
int isem = 1;
int rem;
int sz = MINFBSZ;
struct stat sb;
u_long crc = 0L;
/*
* pass the blocksize of the file being written to the write routine,
* if the size is zero, use the default MINFBSZ
*/
if (fstat(ofd, &sb) == 0) {
#if 0
/* not under minix */
if (sb.st_blksize > 0)
sz = (int)sb.st_blksize;
#endif
} else
syswarn(0,errno,"Unable to obtain block size for file %s",fnm);
rem = sz;
*left = 0L;
/*
* Copy the archive to the file the number of bytes specified. We have
* to assume that we want to recover file holes as none of the archive
* formats can record the location of file holes.
*/
while (size > 0L) {
cnt = bufend - bufpt;
/*
* if we get a read error, we do not want to skip, as we may
* miss a header, so we do not set left, but if we get a write
* error, we do want to skip over the unprocessed data.
*/
if ((cnt <= 0) && ((cnt = buf_fill()) <= 0))
break;
cnt = MIN(cnt, size);
if ((res = file_write(ofd,bufpt,cnt,&rem,&isem,sz,fnm)) <= 0) {
*left = size;
break;
}
if (docrc) {
/*
* update the actual crc value
*/
cnt = res;
while (--cnt >= 0)
crc += *bufpt++ & 0xff;
} else
bufpt += res;
size -= res;
}
/*
* if the last block has a file hole (all zero), we must make sure this
* gets updated in the file. We force the last block of zeros to be
* written. just closing with the file offset moved forward may not put
* a hole at the end of the file.
*/
if (isem && (arcn->sb.st_size > 0L))
file_flush(ofd, fnm, isem);
/*
* if we failed from archive read, we do not want to skip
*/
if ((size > 0L) && (*left == 0L))
return(-1);
/*
* some formats record a crc on file data. If so, then we compare the
* calculated crc to the crc stored in the archive
*/
if (docrc && (size == 0L) && (arcn->crc != crc))
paxwarn(1,"Actual crc does not match expected crc %s",arcn->name);
return(0);
}
/*
* cp_file()
* copy the contents of one file to another. used during -rw phase of pax
* just as in rd_wrfile() we use a special write function to write the
* destination file so we can properly copy files with holes.
*/
void
cp_file(ARCHD *arcn, int fd1, int fd2)
{
int cnt;
off_t cpcnt = 0L;
int res = 0;
char *fnm = arcn->name;
int no_hole = 0;
int isem = 1;
int rem;
int sz = MINFBSZ;
struct stat sb;
/*
* check for holes in the source file. If none, we will use regular
* write instead of file write.
*/
#if 0
/* not under minix */
if (((off_t)(arcn->sb.st_blocks * BLKMULT)) >= arcn->sb.st_size)
#endif
++no_hole;
/*
* pass the blocksize of the file being written to the write routine,
* if the size is zero, use the default MINFBSZ
*/
if (fstat(fd2, &sb) == 0) {
#if 0
/* not under minix */
if (sb.st_blksize > 0)
sz = sb.st_blksize;
#endif
} else
syswarn(0,errno,"Unable to obtain block size for file %s",fnm);
rem = sz;
/*
* read the source file and copy to destination file until EOF
*/
for(;;) {
if ((cnt = read(fd1, buf, blksz)) <= 0)
break;
if (no_hole)
res = write(fd2, buf, cnt);
else
res = file_write(fd2, buf, cnt, &rem, &isem, sz, fnm);
if (res != cnt)
break;
cpcnt += cnt;
}
/*
* check to make sure the copy is valid.
*/
if (res < 0)
syswarn(1, errno, "Failed write during copy of %s to %s",
arcn->org_name, arcn->name);
else if (cpcnt != arcn->sb.st_size)
paxwarn(1, "File %s changed size during copy to %s",
arcn->org_name, arcn->name);
else if (fstat(fd1, &sb) < 0)
syswarn(1, errno, "Failed stat of %s", arcn->org_name);
else if (arcn->sb.st_mtime != sb.st_mtime)
paxwarn(1, "File %s was modified during copy to %s",
arcn->org_name, arcn->name);
/*
* if the last block has a file hole (all zero), we must make sure this
* gets updated in the file. We force the last block of zeros to be
* written. just closing with the file offset moved forward may not put
* a hole at the end of the file.
*/
if (!no_hole && isem && (arcn->sb.st_size > 0L))
file_flush(fd2, fnm, isem);
return;
}
/*
* buf_fill()
* fill the read buffer with the next record (or what we can get) from
* the archive volume.
* Return:
* Number of bytes of data in the read buffer, -1 for read error, and
* 0 when finished (user specified termination in ar_next()).
*/
int
buf_fill(void)
{
int cnt;
static int fini = 0;
if (fini)
return(0);
for(;;) {
/*
* try to fill the buffer. on error the next archive volume is
* opened and we try again.
*/
if ((cnt = ar_read(buf, blksz)) > 0) {
bufpt = buf;
bufend = buf + cnt;
rdcnt += cnt;
return(cnt);
}
/*
* errors require resync, EOF goes to next archive
*/
if (cnt < 0)
break;
if (ar_next() < 0) {
fini = 1;
return(0);
}
rdcnt = 0;
}
exit_val = 1;
return(-1);
}
/*
* buf_flush()
* force the write buffer to the archive. We are passed the number of
* bytes in the buffer at the point of the flush. When we change archives
* the record size might change. (either larger or smaller).
* Return:
* 0 if all is ok, -1 when a write error occurs.
*/
int
buf_flush(int bufcnt)
{
int cnt;
int push = 0;
int totcnt = 0;
/*
* if we have reached the user specified byte count for each archive
* volume, prompt for the next volume. (The non-standrad -R flag).
* NOTE: If the wrlimit is smaller than wrcnt, we will always write
* at least one record. We always round limit UP to next blocksize.
*/
if ((wrlimit > 0) && (wrcnt > wrlimit)) {
paxwarn(0, "User specified archive volume byte limit reached.");
if (ar_next() < 0) {
wrcnt = 0;
exit_val = 1;
return(-1);
}
wrcnt = 0;
/*
* The new archive volume might have changed the size of the
* write blocksize. if so we figure out if we need to write
* (one or more times), or if there is now free space left in
* the buffer (it is no longer full). bufcnt has the number of
* bytes in the buffer, (the blocksize, at the point we were
* CALLED). Push has the amount of "extra" data in the buffer
* if the block size has shrunk from a volume change.
*/
bufend = buf + blksz;
if (blksz > bufcnt)
return(0);
if (blksz < bufcnt)
push = bufcnt - blksz;
}
/*
* We have enough data to write at least one archive block
*/
for (;;) {
/*
* write a block and check if it all went out ok
*/
cnt = ar_write(buf, blksz);
if (cnt == blksz) {
/*
* the write went ok
*/
wrcnt += cnt;
totcnt += cnt;
if (push > 0) {
/* we have extra data to push to the front.
* check for more than 1 block of push, and if
* so we loop back to write again
*/
memcpy(buf, bufend, push);
bufpt = buf + push;
if (push >= blksz) {
push -= blksz;
continue;
}
} else
bufpt = buf;
return(totcnt);
} else if (cnt > 0) {
/*
* Oh drat we got a partial write!
* if format doesnt care about alignment let it go,
* we warned the user in ar_write().... but this means
* the last record on this volume violates pax spec....
*/
totcnt += cnt;
wrcnt += cnt;
bufpt = buf + cnt;
cnt = bufcnt - cnt;
memcpy(buf, bufpt, cnt);
bufpt = buf + cnt;
if (!frmt->blkalgn || ((cnt % frmt->blkalgn) == 0))
return(totcnt);
break;
}
/*
* All done, go to next archive
*/
wrcnt = 0;
if (ar_next() < 0)
break;
/*
* The new archive volume might also have changed the block
* size. if so, figure out if we have too much or too little
* data for using the new block size
*/
bufend = buf + blksz;
if (blksz > bufcnt)
return(0);
if (blksz < bufcnt)
push = bufcnt - blksz;
}
/*
* write failed, stop pax. we must not create a bad archive!
*/
exit_val = 1;
return(-1);
}

430
commands/pax/cache.c Normal file
View file

@ -0,0 +1,430 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
#if 0
static char sccsid[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93";
#endif
#endif /* not lint */
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <stdlib.h>
#include "pax.h"
#include "cache.h"
#include "extern.h"
/*
* routines that control user, group, uid and gid caches (for the archive
* member print routine).
* IMPORTANT:
* these routines cache BOTH hits and misses, a major performance improvement
*/
static int pwopn = 0; /* is password file open */
static int gropn = 0; /* is group file open */
static UIDC **uidtb = NULL; /* uid to name cache */
static GIDC **gidtb = NULL; /* gid to name cache */
static UIDC **usrtb = NULL; /* user name to uid cache */
static GIDC **grptb = NULL; /* group name to gid cache */
/*
* uidtb_start
* creates an an empty uidtb
* Return:
* 0 if ok, -1 otherwise
*/
int
uidtb_start(void)
{
static int fail = 0;
if (uidtb != NULL)
return(0);
if (fail)
return(-1);
if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
++fail;
paxwarn(1, "Unable to allocate memory for user id cache table");
return(-1);
}
return(0);
}
/*
* gidtb_start
* creates an an empty gidtb
* Return:
* 0 if ok, -1 otherwise
*/
int
gidtb_start(void)
{
static int fail = 0;
if (gidtb != NULL)
return(0);
if (fail)
return(-1);
if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
++fail;
paxwarn(1, "Unable to allocate memory for group id cache table");
return(-1);
}
return(0);
}
/*
* usrtb_start
* creates an an empty usrtb
* Return:
* 0 if ok, -1 otherwise
*/
int
usrtb_start(void)
{
static int fail = 0;
if (usrtb != NULL)
return(0);
if (fail)
return(-1);
if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
++fail;
paxwarn(1, "Unable to allocate memory for user name cache table");
return(-1);
}
return(0);
}
/*
* grptb_start
* creates an an empty grptb
* Return:
* 0 if ok, -1 otherwise
*/
int
grptb_start(void)
{
static int fail = 0;
if (grptb != NULL)
return(0);
if (fail)
return(-1);
if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
++fail;
paxwarn(1,"Unable to allocate memory for group name cache table");
return(-1);
}
return(0);
}
/*
* name_uid()
* caches the name (if any) for the uid. If frc set, we always return the
* the stored name (if valid or invalid match). We use a simple hash table.
* Return
* Pointer to stored name (or an empty string).
*/
const char *
name_uid(uid_t uid, int frc)
{
struct passwd *pw;
UIDC *ptr;
if ((uidtb == NULL) && (uidtb_start() < 0))
return("");
/*
* see if we have this uid cached
*/
ptr = uidtb[uid % UID_SZ];
if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
/*
* have an entry for this uid
*/
if (frc || (ptr->valid == VALID))
return(ptr->name);
return("");
}
/*
* No entry for this uid, we will add it
*/
if (!pwopn) {
setpassent(1);
++pwopn;
}
if (ptr == NULL)
ptr = uidtb[uid % UID_SZ] = (UIDC *)malloc(sizeof(UIDC));
if ((pw = getpwuid(uid)) == NULL) {
/*
* no match for this uid in the local password file
* a string that is the uid in numeric format
*/
if (ptr == NULL)
return("");
ptr->uid = uid;
ptr->valid = INVALID;
# ifdef NET2_STAT
(void)snprintf(ptr->name, sizeof(ptr->name), "%u", uid);
# else
(void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
(unsigned long)uid);
# endif
if (frc == 0)
return("");
} else {
/*
* there is an entry for this uid in the password file
*/
if (ptr == NULL)
return(pw->pw_name);
ptr->uid = uid;
(void)strncpy(ptr->name, pw->pw_name, UNMLEN - 1);
ptr->name[UNMLEN-1] = '\0';
ptr->valid = VALID;
}
return(ptr->name);
}
/*
* name_gid()
* caches the name (if any) for the gid. If frc set, we always return the
* the stored name (if valid or invalid match). We use a simple hash table.
* Return
* Pointer to stored name (or an empty string).
*/
const char *
name_gid(gid_t gid, int frc)
{
struct group *gr;
GIDC *ptr;
if ((gidtb == NULL) && (gidtb_start() < 0))
return("");
/*
* see if we have this gid cached
*/
ptr = gidtb[gid % GID_SZ];
if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
/*
* have an entry for this gid
*/
if (frc || (ptr->valid == VALID))
return(ptr->name);
return("");
}
/*
* No entry for this gid, we will add it
*/
if (!gropn) {
setgroupent(1);
++gropn;
}
if (ptr == NULL)
ptr = gidtb[gid % GID_SZ] = (GIDC *)malloc(sizeof(GIDC));
if ((gr = getgrgid(gid)) == NULL) {
/*
* no match for this gid in the local group file, put in
* a string that is the gid in numeric format
*/
if (ptr == NULL)
return("");
ptr->gid = gid;
ptr->valid = INVALID;
# ifdef NET2_STAT
(void)snprintf(ptr->name, sizeof(ptr->name), "%u", gid);
# else
(void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
(unsigned long)gid);
# endif
if (frc == 0)
return("");
} else {
/*
* there is an entry for this group in the group file
*/
if (ptr == NULL)
return(gr->gr_name);
ptr->gid = gid;
(void)strncpy(ptr->name, gr->gr_name, GNMLEN - 1);
ptr->name[GNMLEN-1] = '\0';
ptr->valid = VALID;
}
return(ptr->name);
}
/*
* uid_name()
* caches the uid for a given user name. We use a simple hash table.
* Return
* the uid (if any) for a user name, or a -1 if no match can be found
*/
int
uid_name(char *name, uid_t *uid)
{
struct passwd *pw;
UIDC *ptr;
int namelen;
/*
* return -1 for mangled names
*/
if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
return(-1);
if ((usrtb == NULL) && (usrtb_start() < 0))
return(-1);
/*
* look up in hash table, if found and valid return the uid,
* if found and invalid, return a -1
*/
ptr = usrtb[st_hash(name, namelen, UNM_SZ)];
if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
if (ptr->valid == INVALID)
return(-1);
*uid = ptr->uid;
return(0);
}
if (!pwopn) {
setpassent(1);
++pwopn;
}
if (ptr == NULL)
ptr = usrtb[st_hash(name, namelen, UNM_SZ)] =
(UIDC *)malloc(sizeof(UIDC));
/*
* no match, look it up, if no match store it as an invalid entry,
* or store the matching uid
*/
if (ptr == NULL) {
if ((pw = getpwnam(name)) == NULL)
return(-1);
*uid = pw->pw_uid;
return(0);
}
(void)strncpy(ptr->name, name, UNMLEN - 1);
ptr->name[UNMLEN-1] = '\0';
if ((pw = getpwnam(name)) == NULL) {
ptr->valid = INVALID;
return(-1);
}
ptr->valid = VALID;
*uid = ptr->uid = pw->pw_uid;
return(0);
}
/*
* gid_name()
* caches the gid for a given group name. We use a simple hash table.
* Return
* the gid (if any) for a group name, or a -1 if no match can be found
*/
int
gid_name(char *name, gid_t *gid)
{
struct group *gr;
GIDC *ptr;
int namelen;
/*
* return -1 for mangled names
*/
if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
return(-1);
if ((grptb == NULL) && (grptb_start() < 0))
return(-1);
/*
* look up in hash table, if found and valid return the uid,
* if found and invalid, return a -1
*/
ptr = grptb[st_hash(name, namelen, GID_SZ)];
if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
if (ptr->valid == INVALID)
return(-1);
*gid = ptr->gid;
return(0);
}
if (!gropn) {
setgroupent(1);
++gropn;
}
if (ptr == NULL)
ptr = grptb[st_hash(name, namelen, GID_SZ)] =
(GIDC *)malloc(sizeof(GIDC));
/*
* no match, look it up, if no match store it as an invalid entry,
* or store the matching gid
*/
if (ptr == NULL) {
if ((gr = getgrnam(name)) == NULL)
return(-1);
*gid = gr->gr_gid;
return(0);
}
(void)strncpy(ptr->name, name, GNMLEN - 1);
ptr->name[GNMLEN-1] = '\0';
if ((gr = getgrnam(name)) == NULL) {
ptr->valid = INVALID;
return(-1);
}
ptr->valid = VALID;
*gid = ptr->gid = gr->gr_gid;
return(0);
}

71
commands/pax/cache.h Normal file
View file

@ -0,0 +1,71 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)cache.h 8.1 (Berkeley) 5/31/93
* $FreeBSD: src/bin/pax/cache.h,v 1.9 2004/04/06 20:06:48 markm Exp $
*/
/*
* Constants and data structures used to implement group and password file
* caches. Traditional passwd/group cache routines perform quite poorly with
* archives. The chances of hitting a valid lookup with an archive is quite a
* bit worse than with files already resident on the file system. These misses
* create a MAJOR performance cost. To address this problem, these routines
* cache both hits and misses.
*
* NOTE: name lengths must be as large as those stored in ANY PROTOCOL and
* as stored in the passwd and group files. CACHE SIZES MUST BE PRIME
*/
#define UNMLEN 32 /* >= user name found in any protocol */
#define GNMLEN 32 /* >= group name found in any protocol */
#define UID_SZ 317 /* size of user_name/uid cache */
#define UNM_SZ 317 /* size of user_name/uid cache */
#define GID_SZ 251 /* size of gid cache */
#define GNM_SZ 317 /* size of group name cache */
#define VALID 1 /* entry and name are valid */
#define INVALID 2 /* entry valid, name NOT valid */
/*
* Node structures used in the user, group, uid, and gid caches.
*/
typedef struct uidc {
int valid; /* is this a valid or a miss entry */
char name[UNMLEN]; /* uid name */
uid_t uid; /* cached uid */
} UIDC;
typedef struct gidc {
int valid; /* is this a valid or a miss entry */
char name[GNMLEN]; /* gid name */
gid_t gid; /* cached gid */
} GIDC;

1152
commands/pax/cpio.c Normal file

File diff suppressed because it is too large Load diff

148
commands/pax/cpio.h Normal file
View file

@ -0,0 +1,148 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)cpio.h 8.1 (Berkeley) 5/31/93
* $FreeBSD: src/bin/pax/cpio.h,v 1.7 2004/04/06 20:06:48 markm Exp $
*/
/*
* Defines common to all versions of cpio
*/
#define TRAILER "TRAILER!!!" /* name in last archive record */
/*
* Header encoding of the different file types
*/
#define C_ISDIR 040000 /* Directory */
#define C_ISFIFO 010000 /* FIFO */
#define C_ISREG 0100000 /* Regular file */
#define C_ISBLK 060000 /* Block special file */
#define C_ISCHR 020000 /* Character special file */
#define C_ISCTG 0110000 /* Reserved for contiguous files */
#define C_ISLNK 0120000 /* Reserved for symbolic links */
#define C_ISOCK 0140000 /* Reserved for sockets */
#define C_IFMT 0170000 /* type of file */
/*
* Data Interchange Format - Extended cpio header format - POSIX 1003.1-1990
*/
typedef struct {
char c_magic[6]; /* magic cookie */
char c_dev[6]; /* device number */
char c_ino[6]; /* inode number */
char c_mode[6]; /* file type/access */
char c_uid[6]; /* owners uid */
char c_gid[6]; /* owners gid */
char c_nlink[6]; /* # of links at archive creation */
char c_rdev[6]; /* block/char major/minor # */
char c_mtime[11]; /* modification time */
char c_namesize[6]; /* length of pathname */
char c_filesize[11]; /* length of file in bytes */
} HD_CPIO;
#define MAGIC 070707 /* transportable archive id */
#ifdef _PAX_
#define AMAGIC "070707" /* ascii equivalent string of MAGIC */
#define CPIO_MASK 0x3ffff /* bits valid in the dev/ino fields */
/* used for dev/inode remaps */
#endif /* _PAX_ */
/*
* Binary cpio header structure
*
* CAUTION! CAUTION! CAUTION!
* Each field really represents a 16 bit short (NOT ASCII). Described as
* an array of chars in an attempt to improve portability!!
*/
typedef struct {
u_char h_magic[2];
u_char h_dev[2];
u_char h_ino[2];
u_char h_mode[2];
u_char h_uid[2];
u_char h_gid[2];
u_char h_nlink[2];
u_char h_rdev[2];
u_char h_mtime_1[2];
u_char h_mtime_2[2];
u_char h_namesize[2];
u_char h_filesize_1[2];
u_char h_filesize_2[2];
} HD_BCPIO;
#ifdef _PAX_
/*
* extraction and creation macros for binary cpio
*/
#define SHRT_EXT(ch) ((((unsigned)(ch)[0])<<8) | (((unsigned)(ch)[1])&0xff))
#define RSHRT_EXT(ch) ((((unsigned)(ch)[1])<<8) | (((unsigned)(ch)[0])&0xff))
#define CHR_WR_0(val) ((char)(((val) >> 24) & 0xff))
#define CHR_WR_1(val) ((char)(((val) >> 16) & 0xff))
#define CHR_WR_2(val) ((char)(((val) >> 8) & 0xff))
#define CHR_WR_3(val) ((char)((val) & 0xff))
/*
* binary cpio masks and pads
*/
#define BCPIO_PAD(x) ((2 - ((x) & 1)) & 1) /* pad to next 2 byte word */
#define BCPIO_MASK 0xffff /* mask for dev/ino fields */
#endif /* _PAX_ */
/*
* System VR4 cpio header structure (with/without file data crc)
*/
typedef struct {
char c_magic[6]; /* magic cookie */
char c_ino[8]; /* inode number */
char c_mode[8]; /* file type/access */
char c_uid[8]; /* owners uid */
char c_gid[8]; /* owners gid */
char c_nlink[8]; /* # of links at archive creation */
char c_mtime[8]; /* modification time */
char c_filesize[8]; /* length of file in bytes */
char c_maj[8]; /* block/char major # */
char c_min[8]; /* block/char minor # */
char c_rmaj[8]; /* special file major # */
char c_rmin[8]; /* special file minor # */
char c_namesize[8]; /* length of pathname */
char c_chksum[8]; /* 0 OR CRC of bytes of FILE data */
} HD_VCPIO;
#define VMAGIC 070701 /* sVr4 new portable archive id */
#define VCMAGIC 070702 /* sVr4 new portable archive id CRC */
#ifdef _PAX_
#define AVMAGIC "070701" /* ascii string of above */
#define AVCMAGIC "070702" /* ascii string of above */
#define VCPIO_PAD(x) ((4 - ((x) & 3)) & 3) /* pad to next 4 byte word */
#define VCPIO_MASK 0xffffffff /* mask for dev/ino fields */
#endif /* _PAX_ */

0
commands/pax/err.h Normal file
View file

293
commands/pax/extern.h Normal file
View file

@ -0,0 +1,293 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)extern.h 8.2 (Berkeley) 4/18/94
* $FreeBSD: src/bin/pax/extern.h,v 1.16 2005/01/12 03:25:55 brian Exp $
*/
/*
* External references from each source file
*/
/*
* ar_io.c
*/
extern const char *arcname;
extern const char *gzip_program;
int ar_open(const char *);
void ar_close(void);
void ar_drain(void);
int ar_set_wr(void);
int ar_app_ok(void);
int ar_read(char *, int);
int ar_write(char *, int);
int ar_rdsync(void);
int ar_fow(off_t, off_t *);
int ar_rev(off_t );
int ar_next(void);
/*
* ar_subs.c
*/
extern u_long flcnt;
void list(void);
void extract(void);
void append(void);
void archive(void);
void copy(void);
/*
* buf_subs.c
*/
extern int blksz;
extern int wrblksz;
extern int maxflt;
extern int rdblksz;
extern off_t wrlimit;
extern off_t rdcnt;
extern off_t wrcnt;
int wr_start(void);
int rd_start(void);
void cp_start(void);
int appnd_start(off_t);
int rd_sync(void);
void pback(char *, int);
int rd_skip(off_t);
void wr_fin(void);
int wr_rdbuf(char *, int);
int rd_wrbuf(char *, int);
int wr_skip(off_t);
int wr_rdfile(ARCHD *, int, off_t *);
int rd_wrfile(ARCHD *, int, off_t *);
void cp_file(ARCHD *, int, int);
int buf_fill(void);
int buf_flush(int);
/*
* cache.c
*/
int uidtb_start(void);
int gidtb_start(void);
int usrtb_start(void);
int grptb_start(void);
const char * name_uid(uid_t, int);
const char * name_gid(gid_t, int);
int uid_name(char *, uid_t *);
int gid_name(char *, gid_t *);
/*
* cpio.c
*/
int cpio_strd(void);
int cpio_trail(ARCHD *);
int cpio_endwr(void);
int cpio_id(char *, int);
int cpio_rd(ARCHD *, char *);
off_t cpio_endrd(void);
int cpio_stwr(void);
int cpio_wr(ARCHD *);
int vcpio_id(char *, int);
int crc_id(char *, int);
int crc_strd(void);
int vcpio_rd(ARCHD *, char *);
off_t vcpio_endrd(void);
int crc_stwr(void);
int vcpio_wr(ARCHD *);
int bcpio_id(char *, int);
int bcpio_rd(ARCHD *, char *);
off_t bcpio_endrd(void);
int bcpio_wr(ARCHD *);
/*
* file_subs.c
*/
int file_creat(ARCHD *);
void file_close(ARCHD *, int);
int lnk_creat(ARCHD *);
int cross_lnk(ARCHD *);
int chk_same(ARCHD *);
int node_creat(ARCHD *);
int unlnk_exist(char *, int);
int chk_path(char *, uid_t, gid_t);
void set_ftime(char *fnm, time_t mtime, time_t atime, int frc);
int set_ids(char *, uid_t, gid_t);
int set_lids(char *, uid_t, gid_t);
void set_pmode(char *, mode_t);
int file_write(int, char *, int, int *, int *, int, char *);
void file_flush(int, char *, int);
void rdfile_close(ARCHD *, int *);
int set_crc(ARCHD *, int);
/*
* ftree.c
*/
int ftree_start(void);
int ftree_add(char *, int);
void ftree_sel(ARCHD *);
void ftree_notsel(void);
void ftree_chk(void);
int next_file(ARCHD *);
/*
* gen_subs.c
*/
void ls_list(ARCHD *, time_t, FILE *);
void ls_tty(ARCHD *);
int l_strncpy(char *, const char *, int);
u_long asc_ul(char *, int, int);
int ul_asc(u_long, char *, int, int);
#ifndef NET2_STAT
u_quad_t asc_uqd(char *, int, int);
int uqd_asc(u_quad_t, char *, int, int);
#endif
/*
* getoldopt.c
*/
int getoldopt(int, char **, const char *);
/*
* options.c
*/
extern FSUB fsub[];
extern int ford[];
void options(int, char **);
OPLIST * opt_next(void);
int opt_add(const char *);
int bad_opt(void);
char *chdname;
/*
* pat_rep.c
*/
int rep_add(char *);
int pat_add(char *, char *);
void pat_chk(void);
int pat_sel(ARCHD *);
int pat_match(ARCHD *);
int mod_name(ARCHD *);
int set_dest(ARCHD *, char *, int);
/*
* pax.c
*/
extern int act;
extern FSUB *frmt;
extern int cflag;
extern int cwdfd;
extern int dflag;
extern int iflag;
extern int kflag;
extern int lflag;
extern int nflag;
extern int tflag;
extern int uflag;
extern int vflag;
extern int Dflag;
extern int Hflag;
extern int Lflag;
extern int Xflag;
extern int Yflag;
extern int Zflag;
extern int vfpart;
extern int patime;
extern int pmtime;
extern int nodirs;
extern int pmode;
extern int pids;
extern int rmleadslash;
extern int exit_val;
extern int docrc;
extern char *dirptr;
extern const char *argv0;
extern FILE *listf;
extern char *tempfile;
extern char *tempbase;
void sig_cleanup(int);
/*
* sel_subs.c
*/
int sel_chk(ARCHD *);
int grp_add(char *);
int usr_add(char *);
int trng_add(char *);
/*
* tables.c
*/
int lnk_start(void);
int chk_lnk(ARCHD *);
void purg_lnk(ARCHD *);
void lnk_end(void);
int ftime_start(void);
int chk_ftime(ARCHD *);
int name_start(void);
int add_name(char *, int, char *);
void sub_name(char *, int *, size_t);
int dev_start(void);
int add_dev(ARCHD *);
int map_dev(ARCHD *, u_long, u_long);
int atdir_start(void);
void atdir_end(void);
void add_atdir(char *, dev_t, ino_t, time_t, time_t);
int get_atdir(dev_t, ino_t, time_t *, time_t *);
int dir_start(void);
void add_dir(char *, int, struct stat *, int);
void proc_dir(void);
u_int st_hash(char *, int, int);
/*
* tar.c
*/
int tar_endwr(void);
off_t tar_endrd(void);
int tar_trail(char *, int, int *);
int tar_id(char *, int);
int tar_opt(void);
int tar_rd(ARCHD *, char *);
int tar_wr(ARCHD *);
int ustar_strd(void);
int ustar_stwr(void);
int ustar_id(char *, int);
int ustar_rd(ARCHD *, char *);
int ustar_wr(ARCHD *);
/*
* tty_subs.c
*/
int tty_init(void);
void tty_prnt(const char *, ...);
int tty_read(char *, int);
void paxwarn(int, const char *, ...);
void syswarn(int, int, const char *, ...);

38
commands/pax/fgetln.c Normal file
View file

@ -0,0 +1,38 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *
fgetln(FILE *fp, size_t *lenp)
{
#define EXTRA 80
char *buf = NULL;
int used = 0, len = 0, remain = 0, final = 0;
while(!final) {
char *b;
int r;
if(remain < EXTRA) {
int newlen;
char *newbuf;
newlen = len + EXTRA;
if(!(newbuf = realloc(buf, newlen))) {
if(buf) free(buf);
return NULL;
}
buf = newbuf;
len = newlen;
remain += EXTRA;
}
buf[used] = '\0';
if(!fgets(buf + used, remain, fp))
break;
r = strlen(buf+used);
used += r;
remain -= r;
len += r;
}
*lenp = len;
return buf;
}

984
commands/pax/file_subs.c Normal file
View file

@ -0,0 +1,984 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
#if 0
static char sccsid[] = "@(#)file_subs.c 8.1 (Berkeley) 5/31/93";
#endif
#endif /* not lint */
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <utime.h>
#include <sys/uio.h>
#include <stdlib.h>
#include "pax.h"
#include "options.h"
#include "extern.h"
static int
mk_link(char *,struct stat *,char *, int);
/*
* routines that deal with file operations such as: creating, removing;
* and setting access modes, uid/gid and times of files
*/
#define FILEBITS (S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
#define SETBITS (S_ISUID | S_ISGID)
#define ABITS (FILEBITS | SETBITS)
/*
* file_creat()
* Create and open a file.
* Return:
* file descriptor or -1 for failure
*/
int
file_creat(ARCHD *arcn)
{
int fd = -1;
mode_t file_mode;
int oerrno;
/*
* assume file doesn't exist, so just try to create it, most times this
* works. We have to take special handling when the file does exist. To
* detect this, we use O_EXCL. For example when trying to create a
* file and a character device or fifo exists with the same name, we
* can accidently open the device by mistake (or block waiting to open)
* If we find that the open has failed, then figure spend the effort to
* figure out why. This strategy was found to have better average
* performance in common use than checking the file (and the path)
* first with lstat.
*/
file_mode = arcn->sb.st_mode & FILEBITS;
if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
file_mode)) >= 0)
return(fd);
/*
* the file seems to exist. First we try to get rid of it (found to be
* the second most common failure when traced). If this fails, only
* then we go to the expense to check and create the path to the file
*/
if (unlnk_exist(arcn->name, arcn->type) != 0)
return(-1);
for (;;) {
/*
* try to open it again, if this fails, check all the nodes in
* the path and give it a final try. if chk_path() finds that
* it cannot fix anything, we will skip the last attempt
*/
if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC,
file_mode)) >= 0)
break;
oerrno = errno;
if (nodirs || chk_path(arcn->name,arcn->sb.st_uid,arcn->sb.st_gid) < 0) {
syswarn(1, oerrno, "Unable to create %s", arcn->name);
return(-1);
}
}
return(fd);
}
/*
* file_close()
* Close file descriptor to a file just created by pax. Sets modes,
* ownership and times as required.
* Return:
* 0 for success, -1 for failure
*/
void
file_close(ARCHD *arcn, int fd)
{
int res = 0;
if (fd < 0)
return;
if (close(fd) < 0)
syswarn(0, errno, "Unable to close file descriptor on %s",
arcn->name);
/*
* set owner/groups first as this may strip off mode bits we want
* then set file permission modes. Then set file access and
* modification times.
*/
if (pids)
res = set_ids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid);
/*
* IMPORTANT SECURITY NOTE:
* if not preserving mode or we cannot set uid/gid, then PROHIBIT
* set uid/gid bits
*/
if (!pmode || res)
arcn->sb.st_mode &= ~(SETBITS);
if (pmode)
set_pmode(arcn->name, arcn->sb.st_mode);
if (patime || pmtime)
set_ftime(arcn->name, arcn->sb.st_mtime, arcn->sb.st_atime, 0);
}
/*
* lnk_creat()
* Create a hard link to arcn->ln_name from arcn->name. arcn->ln_name
* must exist;
* Return:
* 0 if ok, -1 otherwise
*/
int
lnk_creat(ARCHD *arcn)
{
struct stat sb;
/*
* we may be running as root, so we have to be sure that link target
* is not a directory, so we lstat and check
*/
if (lstat(arcn->ln_name, &sb) < 0) {
syswarn(1,errno,"Unable to link to %s from %s", arcn->ln_name,
arcn->name);
return(-1);
}
if (S_ISDIR(sb.st_mode)) {
paxwarn(1, "A hard link to the directory %s is not allowed",
arcn->ln_name);
return(-1);
}
return(mk_link(arcn->ln_name, &sb, arcn->name, 0));
}
/*
* cross_lnk()
* Create a hard link to arcn->org_name from arcn->name. Only used in copy
* with the -l flag. No warning or error if this does not succeed (we will
* then just create the file)
* Return:
* 1 if copy() should try to create this file node
* 0 if cross_lnk() ok, -1 for fatal flaw (like linking to self).
*/
int
cross_lnk(ARCHD *arcn)
{
/*
* try to make a link to original file (-l flag in copy mode). make sure
* we do not try to link to directories in case we are running as root
* (and it might succeed).
*/
if (arcn->type == PAX_DIR)
return(1);
return(mk_link(arcn->org_name, &(arcn->sb), arcn->name, 1));
}
/*
* chk_same()
* In copy mode if we are not trying to make hard links between the src
* and destinations, make sure we are not going to overwrite ourselves by
* accident. This slows things down a little, but we have to protect all
* those people who make typing errors.
* Return:
* 1 the target does not exist, go ahead and copy
* 0 skip it file exists (-k) or may be the same as source file
*/
int
chk_same(ARCHD *arcn)
{
struct stat sb;
/*
* if file does not exist, return. if file exists and -k, skip it
* quietly
*/
if (lstat(arcn->name, &sb) < 0)
return(1);
if (kflag)
return(0);
/*
* better make sure the user does not have src == dest by mistake
*/
if ((arcn->sb.st_dev == sb.st_dev) && (arcn->sb.st_ino == sb.st_ino)) {
paxwarn(1, "Unable to copy %s, file would overwrite itself",
arcn->name);
return(0);
}
return(1);
}
/*
* mk_link()
* try to make a hard link between two files. if ign set, we do not
* complain.
* Return:
* 0 if successful (or we are done with this file but no error, such as
* finding the from file exists and the user has set -k).
* 1 when ign was set to indicates we could not make the link but we
* should try to copy/extract the file as that might work (and is an
* allowed option). -1 an error occurred.
*/
static int
mk_link(char *to, struct stat *to_sb, char *from,
int ign)
{
struct stat sb;
int oerrno;
/*
* if from file exists, it has to be unlinked to make the link. If the
* file exists and -k is set, skip it quietly
*/
if (lstat(from, &sb) == 0) {
if (kflag)
return(0);
/*
* make sure it is not the same file, protect the user
*/
if ((to_sb->st_dev==sb.st_dev)&&(to_sb->st_ino == sb.st_ino)) {
paxwarn(1, "Unable to link file %s to itself", to);
return(-1);;
}
/*
* try to get rid of the file, based on the type
*/
if (S_ISDIR(sb.st_mode)) {
if (rmdir(from) < 0) {
syswarn(1, errno, "Unable to remove %s", from);
return(-1);
}
} else if (unlink(from) < 0) {
if (!ign) {
syswarn(1, errno, "Unable to remove %s", from);
return(-1);
}
return(1);
}
}
/*
* from file is gone (or did not exist), try to make the hard link.
* if it fails, check the path and try it again (if chk_path() says to
* try again)
*/
for (;;) {
if (link(to, from) == 0)
break;
oerrno = errno;
if (!nodirs && chk_path(from, to_sb->st_uid, to_sb->st_gid) == 0)
continue;
if (!ign) {
syswarn(1, oerrno, "Could not link to %s from %s", to,
from);
return(-1);
}
return(1);
}
/*
* all right the link was made
*/
return(0);
}
/*
* node_creat()
* create an entry in the file system (other than a file or hard link).
* If successful, sets uid/gid modes and times as required.
* Return:
* 0 if ok, -1 otherwise
*/
int
node_creat(ARCHD *arcn)
{
int res;
int ign = 0;
int oerrno;
int pass = 0;
mode_t file_mode;
struct stat sb;
/*
* create node based on type, if that fails try to unlink the node and
* try again. finally check the path and try again. As noted in the
* file and link creation routines, this method seems to exhibit the
* best performance in general use workloads.
*/
file_mode = arcn->sb.st_mode & FILEBITS;
for (;;) {
switch(arcn->type) {
case PAX_DIR:
res = mkdir(arcn->name, file_mode);
if (ign)
res = 0;
break;
case PAX_CHR:
file_mode |= S_IFCHR;
res = mknod(arcn->name, file_mode, arcn->sb.st_rdev);
break;
case PAX_BLK:
file_mode |= S_IFBLK;
res = mknod(arcn->name, file_mode, arcn->sb.st_rdev);
break;
case PAX_FIF:
res = mkfifo(arcn->name, file_mode);
break;
case PAX_SCK:
/*
* Skip sockets, operation has no meaning under BSD
*/
paxwarn(0,
"%s skipped. Sockets cannot be copied or extracted",
arcn->name);
return(-1);
case PAX_SLK:
res = symlink(arcn->ln_name, arcn->name);
break;
case PAX_CTG:
case PAX_HLK:
case PAX_HRG:
case PAX_REG:
default:
/*
* we should never get here
*/
paxwarn(0, "%s has an unknown file type, skipping",
arcn->name);
return(-1);
}
/*
* if we were able to create the node break out of the loop,
* otherwise try to unlink the node and try again. if that
* fails check the full path and try a final time.
*/
if (res == 0)
break;
/*
* we failed to make the node
*/
oerrno = errno;
if ((ign = unlnk_exist(arcn->name, arcn->type)) < 0)
return(-1);
if (++pass <= 1)
continue;
if (nodirs || chk_path(arcn->name,arcn->sb.st_uid,arcn->sb.st_gid) < 0) {
syswarn(1, oerrno, "Could not create: %s", arcn->name);
return(-1);
}
}
/*
* we were able to create the node. set uid/gid, modes and times
*/
if (pids)
res = ((arcn->type == PAX_SLK) ?
set_lids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid) :
set_ids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid));
else
res = 0;
/*
* symlinks are done now.
*/
if (arcn->type == PAX_SLK)
return(0);
/*
* IMPORTANT SECURITY NOTE:
* if not preserving mode or we cannot set uid/gid, then PROHIBIT any
* set uid/gid bits
*/
if (!pmode || res)
arcn->sb.st_mode &= ~(SETBITS);
if (pmode)
set_pmode(arcn->name, arcn->sb.st_mode);
if (arcn->type == PAX_DIR && strcmp(NM_CPIO, argv0) != 0) {
/*
* Dirs must be processed again at end of extract to set times
* and modes to agree with those stored in the archive. However
* to allow extract to continue, we may have to also set owner
* rights. This allows nodes in the archive that are children
* of this directory to be extracted without failure. Both time
* and modes will be fixed after the entire archive is read and
* before pax exits.
*/
if (access(arcn->name, R_OK | W_OK | X_OK) < 0) {
if (lstat(arcn->name, &sb) < 0) {
syswarn(0, errno,"Could not access %s (stat)",
arcn->name);
set_pmode(arcn->name,file_mode | S_IRWXU);
} else {
/*
* We have to add rights to the dir, so we make
* sure to restore the mode. The mode must be
* restored AS CREATED and not as stored if
* pmode is not set.
*/
set_pmode(arcn->name,
((sb.st_mode & FILEBITS) | S_IRWXU));
if (!pmode)
arcn->sb.st_mode = sb.st_mode;
}
/*
* we have to force the mode to what was set here,
* since we changed it from the default as created.
*/
add_dir(arcn->name, arcn->nlen, &(arcn->sb), 1);
} else if (pmode || patime || pmtime)
add_dir(arcn->name, arcn->nlen, &(arcn->sb), 0);
}
if (patime || pmtime)
set_ftime(arcn->name, arcn->sb.st_mtime, arcn->sb.st_atime, 0);
return(0);
}
/*
* unlnk_exist()
* Remove node from file system with the specified name. We pass the type
* of the node that is going to replace it. When we try to create a
* directory and find that it already exists, we allow processing to
* continue as proper modes etc will always be set for it later on.
* Return:
* 0 is ok to proceed, no file with the specified name exists
* -1 we were unable to remove the node, or we should not remove it (-k)
* 1 we found a directory and we were going to create a directory.
*/
int
unlnk_exist(char *name, int type)
{
struct stat sb;
/*
* the file does not exist, or -k we are done
*/
if (lstat(name, &sb) < 0)
return(0);
if (kflag)
return(-1);
if (S_ISDIR(sb.st_mode)) {
/*
* try to remove a directory, if it fails and we were going to
* create a directory anyway, tell the caller (return a 1)
*/
if (rmdir(name) < 0) {
if (type == PAX_DIR)
return(1);
syswarn(1,errno,"Unable to remove directory %s", name);
return(-1);
}
return(0);
}
/*
* try to get rid of all non-directory type nodes
*/
if (unlink(name) < 0) {
syswarn(1, errno, "Could not unlink %s", name);
return(-1);
}
return(0);
}
/*
* chk_path()
* We were trying to create some kind of node in the file system and it
* failed. chk_path() makes sure the path up to the node exists and is
* writeable. When we have to create a directory that is missing along the
* path somewhere, the directory we create will be set to the same
* uid/gid as the file has (when uid and gid are being preserved).
* NOTE: this routine is a real performance loss. It is only used as a
* last resort when trying to create entries in the file system.
* Return:
* -1 when it could find nothing it is allowed to fix.
* 0 otherwise
*/
int
chk_path( char *name, uid_t st_uid, gid_t st_gid)
{
char *spt = name;
struct stat sb;
int retval = -1;
/*
* watch out for paths with nodes stored directly in / (e.g. /bozo)
*/
if (*spt == '/')
++spt;
for(;;) {
/*
* work foward from the first / and check each part of the path
*/
spt = strchr(spt, '/');
if (spt == NULL)
break;
*spt = '\0';
/*
* if it exists we assume it is a directory, it is not within
* the spec (at least it seems to read that way) to alter the
* file system for nodes NOT EXPLICITLY stored on the archive.
* If that assumption is changed, you would test the node here
* and figure out how to get rid of it (probably like some
* recursive unlink()) or fix up the directory permissions if
* required (do an access()).
*/
if (lstat(name, &sb) == 0) {
*(spt++) = '/';
continue;
}
/*
* the path fails at this point, see if we can create the
* needed directory and continue on
*/
if (mkdir(name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
*spt = '/';
retval = -1;
break;
}
/*
* we were able to create the directory. We will tell the
* caller that we found something to fix, and it is ok to try
* and create the node again.
*/
retval = 0;
if (pids)
(void)set_ids(name, st_uid, st_gid);
/*
* make sure the user doen't have some strange umask that
* causes this newly created directory to be unusable. We fix
* the modes and restore them back to the creation default at
* the end of pax
*/
if ((access(name, R_OK | W_OK | X_OK) < 0) &&
(lstat(name, &sb) == 0)) {
set_pmode(name, ((sb.st_mode & FILEBITS) | S_IRWXU));
add_dir(name, spt - name, &sb, 1);
}
*(spt++) = '/';
continue;
}
return(retval);
}
/*
* set_ftime()
* Set the access time and modification time for a named file. If frc is
* non-zero we force these times to be set even if the user did not
* request access and/or modification time preservation (this is also
* used by -t to reset access times).
* When ign is zero, only those times the user has asked for are set, the
* other ones are left alone. We do not assume the un-documented feature
* of many utimes() implementations that consider a 0 time value as a do
* not set request.
*/
void
set_ftime(char *fnm, time_t mtime, time_t atime, int frc)
{
#if 0
static struct timeval tv[2] = {{0L, 0L}, {0L, 0L}};
#endif
struct stat sb;
struct utimbuf ut;
#if 0
tv[0].tv_sec = atime;
tv[1].tv_sec = mtime;
#endif
ut.actime = atime;
ut.modtime = mtime;
if (!frc && (!patime || !pmtime)) {
/*
* if we are not forcing, only set those times the user wants
* set. We get the current values of the times if we need them.
*/
if (lstat(fnm, &sb) == 0) {
if (!patime)
ut.actime = sb.st_atime;
if (!pmtime)
ut.modtime = sb.st_mtime;
} else
syswarn(0,errno,"Unable to obtain file stats %s", fnm);
}
/*
* set the times
*/
if (utime(fnm, &ut) < 0)
syswarn(1, errno, "Access/modification time set failed on: %s",
fnm);
return;
}
/*
* set_ids()
* set the uid and gid of a file system node
* Return:
* 0 when set, -1 on failure
*/
int
set_ids(char *fnm, uid_t uid, gid_t gid)
{
if (chown(fnm, uid, gid) < 0) {
/*
* ignore EPERM unless in verbose mode or being run by root.
* if running as pax, POSIX requires a warning.
*/
if (strcmp(NM_PAX, argv0) == 0 || errno != EPERM || vflag ||
geteuid() == 0)
syswarn(1, errno, "Unable to set file uid/gid of %s",
fnm);
return(-1);
}
return(0);
}
/*
* set_lids()
* set the uid and gid of a file system node
* Return:
* 0 when set, -1 on failure
*/
int
set_lids(char *fnm, uid_t uid, gid_t gid)
{
if (chown(fnm, uid, gid) < 0) {
/*
* ignore EPERM unless in verbose mode or being run by root.
* if running as pax, POSIX requires a warning.
*/
if (strcmp(NM_PAX, argv0) == 0 || errno != EPERM || vflag ||
geteuid() == 0)
syswarn(1, errno, "Unable to set file uid/gid of %s",
fnm);
return(-1);
}
return(0);
}
/*
* set_pmode()
* Set file access mode
*/
void
set_pmode(char *fnm, mode_t mode)
{
mode &= ABITS;
if (chmod(fnm, mode) < 0)
syswarn(1, errno, "Could not set permissions on %s", fnm);
return;
}
/*
* file_write()
* Write/copy a file (during copy or archive extract). This routine knows
* how to copy files with lseek holes in it. (Which are read as file
* blocks containing all 0's but do not have any file blocks associated
* with the data). Typical examples of these are files created by dbm
* variants (.pag files). While the file size of these files are huge, the
* actual storage is quite small (the files are sparse). The problem is
* the holes read as all zeros so are probably stored on the archive that
* way (there is no way to determine if the file block is really a hole,
* we only know that a file block of all zero's can be a hole).
* At this writing, no major archive format knows how to archive files
* with holes. However, on extraction (or during copy, -rw) we have to
* deal with these files. Without detecting the holes, the files can
* consume a lot of file space if just written to disk. This replacement
* for write when passed the basic allocation size of a file system block,
* uses lseek whenever it detects the input data is all 0 within that
* file block. In more detail, the strategy is as follows:
* While the input is all zero keep doing an lseek. Keep track of when we
* pass over file block boundries. Only write when we hit a non zero
* input. once we have written a file block, we continue to write it to
* the end (we stop looking at the input). When we reach the start of the
* next file block, start checking for zero blocks again. Working on file
* block boundries significantly reduces the overhead when copying files
* that are NOT very sparse. This overhead (when compared to a write) is
* almost below the measurement resolution on many systems. Without it,
* files with holes cannot be safely copied. It does has a side effect as
* it can put holes into files that did not have them before, but that is
* not a problem since the file contents are unchanged (in fact it saves
* file space). (Except on paging files for diskless clients. But since we
* cannot determine one of those file from here, we ignore them). If this
* ever ends up on a system where CTG files are supported and the holes
* are not desired, just do a conditional test in those routines that
* call file_write() and have it call write() instead. BEFORE CLOSING THE
* FILE, make sure to call file_flush() when the last write finishes with
* an empty block. A lot of file systems will not create an lseek hole at
* the end. In this case we drop a single 0 at the end to force the
* trailing 0's in the file.
* ---Parameters---
* rem: how many bytes left in this file system block
* isempt: have we written to the file block yet (is it empty)
* sz: basic file block allocation size
* cnt: number of bytes on this write
* str: buffer to write
* Return:
* number of bytes written, -1 on write (or lseek) error.
*/
int
file_write(int fd, char *str, int cnt, int *rem, int *isempt, int sz,
char *name)
{
char *pt;
char *end;
int wcnt;
char *st = str;
/*
* while we have data to process
*/
while (cnt) {
if (!*rem) {
/*
* We are now at the start of file system block again
* (or what we think one is...). start looking for
* empty blocks again
*/
*isempt = 1;
*rem = sz;
}
/*
* only examine up to the end of the current file block or
* remaining characters to write, whatever is smaller
*/
wcnt = MIN(cnt, *rem);
cnt -= wcnt;
*rem -= wcnt;
if (*isempt) {
/*
* have not written to this block yet, so we keep
* looking for zero's
*/
pt = st;
end = st + wcnt;
/*
* look for a zero filled buffer
*/
while ((pt < end) && (*pt == '\0'))
++pt;
if (pt == end) {
/*
* skip, buf is empty so far
*/
if (lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) {
syswarn(1,errno,"File seek on %s",
name);
return(-1);
}
st = pt;
continue;
}
/*
* drat, the buf is not zero filled
*/
*isempt = 0;
}
/*
* have non-zero data in this file system block, have to write
*/
if (write(fd, st, wcnt) != wcnt) {
syswarn(1, errno, "Failed write to file %s", name);
return(-1);
}
st += wcnt;
}
return(st - str);
}
/*
* file_flush()
* when the last file block in a file is zero, many file systems will not
* let us create a hole at the end. To get the last block with zeros, we
* write the last BYTE with a zero (back up one byte and write a zero).
*/
void
file_flush(int fd, char *fname, int isempt)
{
static char blnk[] = "\0";
/*
* silly test, but make sure we are only called when the last block is
* filled with all zeros.
*/
if (!isempt)
return;
/*
* move back one byte and write a zero
*/
if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) {
syswarn(1, errno, "Failed seek on file %s", fname);
return;
}
if (write(fd, blnk, 1) < 0)
syswarn(1, errno, "Failed write to file %s", fname);
return;
}
/*
* rdfile_close()
* close a file we have beed reading (to copy or archive). If we have to
* reset access time (tflag) do so (the times are stored in arcn).
*/
void
rdfile_close(ARCHD *arcn, int *fd)
{
/*
* make sure the file is open
*/
if (*fd < 0)
return;
(void)close(*fd);
*fd = -1;
if (!tflag)
return;
/*
* user wants last access time reset
*/
set_ftime(arcn->org_name, arcn->sb.st_mtime, arcn->sb.st_atime, 1);
return;
}
/*
* set_crc()
* read a file to calculate its crc. This is a real drag. Archive formats
* that have this, end up reading the file twice (we have to write the
* header WITH the crc before writing the file contents. Oh well...
* Return:
* 0 if was able to calculate the crc, -1 otherwise
*/
int
set_crc(ARCHD *arcn, int fd)
{
int i;
int res;
off_t cpcnt = 0L;
u_long size;
unsigned long crc = 0L;
char tbuf[FILEBLK];
struct stat sb;
if (fd < 0) {
/*
* hmm, no fd, should never happen. well no crc then.
*/
arcn->crc = 0L;
return(0);
}
#if 0
/* not in minix */
if ((size = (u_long)arcn->sb.st_blksize) > (u_long)sizeof(tbuf))
size = (u_long)sizeof(tbuf);
#endif
/*
* read all the bytes we think that there are in the file. If the user
* is trying to archive an active file, forget this file.
*/
for(;;) {
if ((res = read(fd, tbuf, size)) <= 0)
break;
cpcnt += res;
for (i = 0; i < res; ++i)
crc += (tbuf[i] & 0xff);
}
/*
* safety check. we want to avoid archiving files that are active as
* they can create inconsistant archive copies.
*/
if (cpcnt != arcn->sb.st_size)
paxwarn(1, "File changed size %s", arcn->org_name);
else if (fstat(fd, &sb) < 0)
syswarn(1, errno, "Failed stat on %s", arcn->org_name);
else if (arcn->sb.st_mtime != sb.st_mtime)
paxwarn(1, "File %s was modified during read", arcn->org_name);
else if (lseek(fd, (off_t)0L, SEEK_SET) < 0)
syswarn(1, errno, "File rewind failed on: %s", arcn->org_name);
else {
arcn->crc = crc;
return(0);
}
return(-1);
}

535
commands/pax/ftree.c Normal file
View file

@ -0,0 +1,535 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
#if 0
static char sccsid[] = "@(#)ftree.c 8.2 (Berkeley) 4/18/94";
#endif
#endif /* not lint */
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <fts.h>
#include "pax.h"
#include "ftree.h"
#include "extern.h"
/*
* routines to interface with the fts library function.
*
* file args supplied to pax are stored on a single linked list (of type FTREE)
* and given to fts to be processed one at a time. pax "selects" files from
* the expansion of each arg into the corresponding file tree (if the arg is a
* directory, otherwise the node itself is just passed to pax). The selection
* is modified by the -n and -u flags. The user is informed when a specific
* file arg does not generate any selected files. -n keeps expanding the file
* tree arg until one of its files is selected, then skips to the next file
* arg. when the user does not supply the file trees as command line args to
* pax, they are read from stdin
*/
static FTS *ftsp = NULL; /* current FTS handle */
static int ftsopts; /* options to be used on fts_open */
static char *farray[2]; /* array for passing each arg to fts */
static FTREE *fthead = NULL; /* head of linked list of file args */
static FTREE *fttail = NULL; /* tail of linked list of file args */
static FTREE *ftcur = NULL; /* current file arg being processed */
static FTSENT *ftent = NULL; /* current file tree entry */
static int ftree_skip; /* when set skip to next file arg */
static int ftree_arg(void);
/*
* ftree_start()
* initialize the options passed to fts_open() during this run of pax
* options are based on the selection of pax options by the user
* fts_start() also calls fts_arg() to open the first valid file arg. We
* also attempt to reset directory access times when -t (tflag) is set.
* Return:
* 0 if there is at least one valid file arg to process, -1 otherwise
*/
int
ftree_start(void)
{
/*
* Set up the operation mode of fts, open the first file arg. We must
* use FTS_NOCHDIR, as the user may have to open multiple archives and
* if fts did a chdir off into the boondocks, we may create an archive
* volume in a place where the user did not expect to.
*/
ftsopts = FTS_NOCHDIR;
/*
* optional user flags that effect file traversal
* -H command line symlink follow only (half follow)
* -L follow sylinks (logical)
* -P do not follow sylinks (physical). This is the default.
* -X do not cross over mount points
* -t preserve access times on files read.
* -n select only the first member of a file tree when a match is found
* -d do not extract subtrees rooted at a directory arg.
*/
if (Lflag)
ftsopts |= FTS_LOGICAL;
else
ftsopts |= FTS_PHYSICAL;
if (Hflag)
# ifdef NET2_FTS
paxwarn(0, "The -H flag is not supported on this version");
# else
ftsopts |= FTS_COMFOLLOW;
# endif
if (Xflag)
ftsopts |= FTS_XDEV;
if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) {
paxwarn(1, "Unable to allocate memory for file name buffer");
return(-1);
}
if (ftree_arg() < 0)
return(-1);
if (tflag && (atdir_start() < 0))
return(-1);
return(0);
}
/*
* ftree_add()
* add the arg to the linked list of files to process. Each will be
* processed by fts one at a time
* Return:
* 0 if added to the linked list, -1 if failed
*/
int
ftree_add(char *str, int chflg)
{
FTREE *ft;
int len;
/*
* simple check for bad args
*/
if ((str == NULL) || (*str == '\0')) {
paxwarn(0, "Invalid file name argument");
return(-1);
}
/*
* allocate FTREE node and add to the end of the linked list (args are
* processed in the same order they were passed to pax). Get rid of any
* trailing / the user may pass us. (watch out for / by itself).
*/
if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) {
paxwarn(0, "Unable to allocate memory for filename");
return(-1);
}
if (((len = strlen(str) - 1) > 0) && (str[len] == '/'))
str[len] = '\0';
ft->fname = str;
ft->refcnt = 0;
ft->chflg = chflg;
ft->fow = NULL;
if (fthead == NULL) {
fttail = fthead = ft;
return(0);
}
fttail->fow = ft;
fttail = ft;
return(0);
}
/*
* ftree_sel()
* this entry has been selected by pax. bump up reference count and handle
* -n and -d processing.
*/
void
ftree_sel(ARCHD *arcn)
{
/*
* set reference bit for this pattern. This linked list is only used
* when file trees are supplied pax as args. The list is not used when
* the trees are read from stdin.
*/
if (ftcur != NULL)
ftcur->refcnt = 1;
/*
* if -n we are done with this arg, force a skip to the next arg when
* pax asks for the next file in next_file().
* if -d we tell fts only to match the directory (if the arg is a dir)
* and not the entire file tree rooted at that point.
*/
if (nflag)
ftree_skip = 1;
if (!dflag || (arcn->type != PAX_DIR))
return;
if (ftent != NULL)
(void)fts_set(ftsp, ftent, FTS_SKIP);
}
/*
* ftree_notsel()
* this entry has not been selected by pax.
*/
void
ftree_notsel()
{
if (ftent != NULL)
(void)fts_set(ftsp, ftent, FTS_SKIP);
}
/*
* ftree_chk()
* called at end on pax execution. Prints all those file args that did not
* have a selected member (reference count still 0)
*/
void
ftree_chk(void)
{
FTREE *ft;
int wban = 0;
/*
* make sure all dir access times were reset.
*/
if (tflag)
atdir_end();
/*
* walk down list and check reference count. Print out those members
* that never had a match
*/
for (ft = fthead; ft != NULL; ft = ft->fow) {
if ((ft->refcnt > 0) || ft->chflg)
continue;
if (wban == 0) {
paxwarn(1,"WARNING! These file names were not selected:");
++wban;
}
(void)fprintf(stderr, "%s\n", ft->fname);
}
}
/*
* ftree_arg()
* Get the next file arg for fts to process. Can be from either the linked
* list or read from stdin when the user did not them as args to pax. Each
* arg is processed until the first successful fts_open().
* Return:
* 0 when the next arg is ready to go, -1 if out of file args (or EOF on
* stdin).
*/
static int
ftree_arg(void)
{
char *pt;
/*
* close off the current file tree
*/
if (ftsp != NULL) {
(void)fts_close(ftsp);
ftsp = NULL;
}
/*
* keep looping until we get a valid file tree to process. Stop when we
* reach the end of the list (or get an eof on stdin)
*/
for(;;) {
if (fthead == NULL) {
/*
* the user didn't supply any args, get the file trees
* to process from stdin;
*/
if (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL)
return(-1);
if ((pt = strchr(farray[0], '\n')) != NULL)
*pt = '\0';
} else {
/*
* the user supplied the file args as arguments to pax
*/
if (ftcur == NULL)
ftcur = fthead;
else if ((ftcur = ftcur->fow) == NULL)
return(-1);
if (ftcur->chflg) {
/* First fchdir() back... */
if (fchdir(cwdfd) < 0) {
syswarn(1, errno,
"Can't fchdir to starting directory");
return(-1);
}
if (chdir(ftcur->fname) < 0) {
syswarn(1, errno, "Can't chdir to %s",
ftcur->fname);
return(-1);
}
continue;
} else
farray[0] = ftcur->fname;
}
/*
* Watch it, fts wants the file arg stored in an array of char
* ptrs, with the last one a null. We use a two element array
* and set farray[0] to point at the buffer with the file name
* in it. We cannot pass all the file args to fts at one shot
* as we need to keep a handle on which file arg generates what
* files (the -n and -d flags need this). If the open is
* successful, return a 0.
*/
if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL)
break;
}
return(0);
}
/*
* next_file()
* supplies the next file to process in the supplied archd structure.
* Return:
* 0 when contents of arcn have been set with the next file, -1 when done.
*/
int
next_file(ARCHD *arcn)
{
int cnt;
time_t atime;
time_t mtime;
/*
* ftree_sel() might have set the ftree_skip flag if the user has the
* -n option and a file was selected from this file arg tree. (-n says
* only one member is matched for each pattern) ftree_skip being 1
* forces us to go to the next arg now.
*/
if (ftree_skip) {
/*
* clear and go to next arg
*/
ftree_skip = 0;
if (ftree_arg() < 0)
return(-1);
}
/*
* loop until we get a valid file to process
*/
for(;;) {
if ((ftent = fts_read(ftsp)) == NULL) {
/*
* out of files in this tree, go to next arg, if none
* we are done
*/
if (ftree_arg() < 0)
return(-1);
continue;
}
/*
* handle each type of fts_read() flag
*/
switch(ftent->fts_info) {
case FTS_D:
case FTS_DEFAULT:
case FTS_F:
case FTS_SL:
case FTS_SLNONE:
/*
* these are all ok
*/
break;
case FTS_DP:
/*
* already saw this directory. If the user wants file
* access times reset, we use this to restore the
* access time for this directory since this is the
* last time we will see it in this file subtree
* remember to force the time (this is -t on a read
* directory, not a created directory).
*/
# ifdef NET2_FTS
if (!tflag || (get_atdir(ftent->fts_statb.st_dev,
ftent->fts_statb.st_ino, &mtime, &atime) < 0))
# else
if (!tflag || (get_atdir(ftent->fts_statp->st_dev,
ftent->fts_statp->st_ino, &mtime, &atime) < 0))
# endif
continue;
set_ftime(ftent->fts_path, mtime, atime, 1);
continue;
case FTS_DC:
/*
* fts claims a file system cycle
*/
paxwarn(1,"File system cycle found at %s",ftent->fts_path);
continue;
case FTS_DNR:
# ifdef NET2_FTS
syswarn(1, errno,
# else
syswarn(1, ftent->fts_errno,
# endif
"Unable to read directory %s", ftent->fts_path);
continue;
case FTS_ERR:
# ifdef NET2_FTS
syswarn(1, errno,
# else
syswarn(1, ftent->fts_errno,
# endif
"File system traversal error");
continue;
case FTS_NS:
case FTS_NSOK:
# ifdef NET2_FTS
syswarn(1, errno,
# else
syswarn(1, ftent->fts_errno,
# endif
"Unable to access %s", ftent->fts_path);
continue;
}
/*
* ok got a file tree node to process. copy info into arcn
* structure (initialize as required)
*/
arcn->skip = 0;
arcn->pad = 0;
arcn->ln_nlen = 0;
arcn->ln_name[0] = '\0';
# ifdef NET2_FTS
arcn->sb = ftent->fts_statb;
# else
arcn->sb = *(ftent->fts_statp);
# endif
/*
* file type based set up and copy into the arcn struct
* SIDE NOTE:
* we try to reset the access time on all files and directories
* we may read when the -t flag is specified. files are reset
* when we close them after copying. we reset the directories
* when we are done with their file tree (we also clean up at
* end in case we cut short a file tree traversal). However
* there is no way to reset access times on symlinks.
*/
switch(S_IFMT & arcn->sb.st_mode) {
case S_IFDIR:
arcn->type = PAX_DIR;
if (!tflag)
break;
add_atdir(ftent->fts_path, arcn->sb.st_dev,
arcn->sb.st_ino, arcn->sb.st_mtime,
arcn->sb.st_atime);
break;
case S_IFCHR:
arcn->type = PAX_CHR;
break;
case S_IFBLK:
arcn->type = PAX_BLK;
break;
case S_IFREG:
/*
* only regular files with have data to store on the
* archive. all others will store a zero length skip.
* the skip field is used by pax for actual data it has
* to read (or skip over).
*/
arcn->type = PAX_REG;
arcn->skip = arcn->sb.st_size;
break;
case S_IFLNK:
arcn->type = PAX_SLK;
/*
* have to read the symlink path from the file
*/
if ((cnt = readlink(ftent->fts_path, arcn->ln_name,
PAXPATHLEN - 1)) < 0) {
syswarn(1, errno, "Unable to read symlink %s",
ftent->fts_path);
continue;
}
/*
* set link name length, watch out readlink does not
* always NUL terminate the link path
*/
arcn->ln_name[cnt] = '\0';
arcn->ln_nlen = cnt;
break;
#if 0
case S_IFSOCK:
/*
* under BSD storing a socket is senseless but we will
* let the format specific write function make the
* decision of what to do with it.
*/
arcn->type = PAX_SCK;
break;
#endif
case S_IFIFO:
arcn->type = PAX_FIF;
break;
}
break;
}
/*
* copy file name, set file name length
*/
arcn->nlen = l_strncpy(arcn->name, ftent->fts_path, sizeof(arcn->name) - 1);
arcn->name[arcn->nlen] = '\0';
arcn->org_name = ftent->fts_path;
return(0);
}

48
commands/pax/ftree.h Normal file
View file

@ -0,0 +1,48 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)ftree.h 8.1 (Berkeley) 5/31/93
* $FreeBSD: src/bin/pax/ftree.h,v 1.7 2004/04/06 20:06:48 markm Exp $
*/
/*
* Data structure used by the ftree.c routines to store the file args to be
* handed to fts(). It keeps a reference count of which args generated a
* "selected" member
*/
typedef struct ftree {
char *fname; /* file tree name */
int refcnt; /* has tree had a selected file? */
int chflg; /* change directory flag */
struct ftree *fow; /* pointer to next entry on list */
} FTREE;

414
commands/pax/gen_subs.c Normal file
View file

@ -0,0 +1,414 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
#if 0
static char sccsid[] = "@(#)gen_subs.c 8.1 (Berkeley) 5/31/93";
#endif
#endif /* not lint */
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <stdint.h>
#include <stdio.h>
#include <utmp.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "pax.h"
#include "extern.h"
/*
* a collection of general purpose subroutines used by pax
*/
/*
* constants used by ls_list() when printing out archive members
*/
#define MODELEN 20
#define DATELEN 64
#define SIXMONTHS ((365 / 2) * 86400)
#define CURFRMTM "%b %e %H:%M"
#define OLDFRMTM "%b %e %Y"
#define CURFRMTD "%e %b %H:%M"
#define OLDFRMTD "%e %b %Y"
#ifndef UT_NAMESIZE
#define UT_NAMESIZE 8
#endif
#define UT_GRPSIZE 6
static int d_first = -1;
/*
* ls_list()
* list the members of an archive in ls format
*/
void
ls_list(ARCHD *arcn, time_t now, FILE *fp)
{
struct stat *sbp;
char f_mode[MODELEN];
char f_date[DATELEN];
const char *timefrmt;
/*
* if not verbose, just print the file name
*/
if (!vflag) {
(void)fprintf(fp, "%s\n", arcn->name);
(void)fflush(fp);
return;
}
if (d_first < 0)
d_first = 0;
/*
* user wants long mode
*/
sbp = &(arcn->sb);
#if 0
strmode(sbp->st_mode, f_mode);
#else
strcpy(f_mode, "");
#endif
/*
* time format based on age compared to the time pax was started.
*/
if ((sbp->st_mtime + SIXMONTHS) <= now)
timefrmt = d_first ? OLDFRMTD : OLDFRMTM;
else
timefrmt = d_first ? CURFRMTD : CURFRMTM;
/*
* print file mode, link count, uid, gid and time
*/
#if 0
if (strftime(f_date,DATELEN,timefrmt,localtime(&(sbp->st_mtime))) == 0)
#endif
f_date[0] = '\0';
(void)fprintf(fp, "%s%2u %-*s %-*s ", f_mode, sbp->st_nlink,
UT_NAMESIZE, name_uid(sbp->st_uid, 1), UT_GRPSIZE,
name_gid(sbp->st_gid, 1));
/*
* print device id's for devices, or sizes for other nodes
*/
if ((arcn->type == PAX_CHR) || (arcn->type == PAX_BLK))
# ifdef NET2_STAT
(void)fprintf(fp, "%4u,%4u ", major(sbp->st_rdev),
minor(sbp->st_rdev));
# else
(void)fprintf(fp, "%4lu,%4lu ", (unsigned long)major(sbp->st_rdev),
(unsigned long)minor(sbp->st_rdev));
# endif
else {
# ifdef NET2_STAT
(void)fprintf(fp, "%9lu ", sbp->st_size);
# else
(void)fprintf(fp, "%9ju ", (uintmax_t)sbp->st_size);
# endif
}
/*
* print name and link info for hard and soft links
*/
(void)fprintf(fp, "%s %s", f_date, arcn->name);
if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG))
(void)fprintf(fp, " == %s\n", arcn->ln_name);
else if (arcn->type == PAX_SLK)
(void)fprintf(fp, " => %s\n", arcn->ln_name);
else
(void)putc('\n', fp);
(void)fflush(fp);
return;
}
/*
* tty_ls()
* print a short summary of file to tty.
*/
void
ls_tty(ARCHD *arcn)
{
char f_date[DATELEN];
char f_mode[MODELEN];
const char *timefrmt;
if (d_first < 0)
d_first = 0;
if ((arcn->sb.st_mtime + SIXMONTHS) <= time(NULL))
timefrmt = d_first ? OLDFRMTD : OLDFRMTM;
else
timefrmt = d_first ? CURFRMTD : CURFRMTM;
/*
* convert time to string, and print
*/
#if 0
if (strftime(f_date, DATELEN, timefrmt,
localtime(&(arcn->sb.st_mtime))) == 0)
#endif
f_date[0] = '\0';
#if 0
strmode(arcn->sb.st_mode, f_mode);
#else
strcpy(f_mode, "");
#endif
tty_prnt("%s%s %s\n", f_mode, f_date, arcn->name);
return;
}
/*
* l_strncpy()
* copy src to dest up to len chars (stopping at first '\0').
* when src is shorter than len, pads to len with '\0'.
* Return:
* number of chars copied. (Note this is a real performance win over
* doing a strncpy(), a strlen(), and then a possible memset())
*/
int
l_strncpy(char *dest, const char *src, int len)
{
char *stop;
char *start;
stop = dest + len;
start = dest;
while ((dest < stop) && (*src != '\0'))
*dest++ = *src++;
len = dest - start;
while (dest < stop)
*dest++ = '\0';
return(len);
}
/*
* asc_ul()
* convert hex/octal character string into a u_long. We do not have to
* check for overflow! (the headers in all supported formats are not large
* enough to create an overflow).
* NOTE: strings passed to us are NOT TERMINATED.
* Return:
* unsigned long value
*/
u_long
asc_ul(char *str, int len, int base)
{
char *stop;
u_long tval = 0;
stop = str + len;
/*
* skip over leading blanks and zeros
*/
while ((str < stop) && ((*str == ' ') || (*str == '0')))
++str;
/*
* for each valid digit, shift running value (tval) over to next digit
* and add next digit
*/
if (base == HEX) {
while (str < stop) {
if ((*str >= '0') && (*str <= '9'))
tval = (tval << 4) + (*str++ - '0');
else if ((*str >= 'A') && (*str <= 'F'))
tval = (tval << 4) + 10 + (*str++ - 'A');
else if ((*str >= 'a') && (*str <= 'f'))
tval = (tval << 4) + 10 + (*str++ - 'a');
else
break;
}
} else {
while ((str < stop) && (*str >= '0') && (*str <= '7'))
tval = (tval << 3) + (*str++ - '0');
}
return(tval);
}
/*
* ul_asc()
* convert an unsigned long into an hex/oct ascii string. pads with LEADING
* ascii 0's to fill string completely
* NOTE: the string created is NOT TERMINATED.
*/
int
ul_asc(u_long val, char *str, int len, int base)
{
char *pt;
u_long digit;
/*
* WARNING str is not '\0' terminated by this routine
*/
pt = str + len - 1;
/*
* do a tailwise conversion (start at right most end of string to place
* least significant digit). Keep shifting until conversion value goes
* to zero (all digits were converted)
*/
if (base == HEX) {
while (pt >= str) {
if ((digit = (val & 0xf)) < 10)
*pt-- = '0' + (char)digit;
else
*pt-- = 'a' + (char)(digit - 10);
if ((val = (val >> 4)) == (u_long)0)
break;
}
} else {
while (pt >= str) {
*pt-- = '0' + (char)(val & 0x7);
if ((val = (val >> 3)) == (u_long)0)
break;
}
}
/*
* pad with leading ascii ZEROS. We return -1 if we ran out of space.
*/
while (pt >= str)
*pt-- = '0';
if (val != (u_long)0)
return(-1);
return(0);
}
#ifndef NET2_STAT
/*
* asc_uqd()
* convert hex/octal character string into a u_quad_t. We do not have to
* check for overflow! (the headers in all supported formats are not large
* enough to create an overflow).
* NOTE: strings passed to us are NOT TERMINATED.
* Return:
* u_quad_t value
*/
u_quad_t
asc_uqd(char *str, int len, int base)
{
char *stop;
u_quad_t tval = 0;
stop = str + len;
/*
* skip over leading blanks and zeros
*/
while ((str < stop) && ((*str == ' ') || (*str == '0')))
++str;
/*
* for each valid digit, shift running value (tval) over to next digit
* and add next digit
*/
if (base == HEX) {
while (str < stop) {
if ((*str >= '0') && (*str <= '9'))
tval = (tval << 4) + (*str++ - '0');
else if ((*str >= 'A') && (*str <= 'F'))
tval = (tval << 4) + 10 + (*str++ - 'A');
else if ((*str >= 'a') && (*str <= 'f'))
tval = (tval << 4) + 10 + (*str++ - 'a');
else
break;
}
} else {
while ((str < stop) && (*str >= '0') && (*str <= '7'))
tval = (tval << 3) + (*str++ - '0');
}
return(tval);
}
/*
* uqd_asc()
* convert an u_quad_t into a hex/oct ascii string. pads with LEADING
* ascii 0's to fill string completely
* NOTE: the string created is NOT TERMINATED.
*/
int
uqd_asc(u_quad_t val, char *str, int len, int base)
{
char *pt;
u_quad_t digit;
/*
* WARNING str is not '\0' terminated by this routine
*/
pt = str + len - 1;
/*
* do a tailwise conversion (start at right most end of string to place
* least significant digit). Keep shifting until conversion value goes
* to zero (all digits were converted)
*/
if (base == HEX) {
while (pt >= str) {
if ((digit = (val & 0xf)) < 10)
*pt-- = '0' + (char)digit;
else
*pt-- = 'a' + (char)(digit - 10);
if ((val = (val >> 4)) == (u_quad_t)0)
break;
}
} else {
while (pt >= str) {
*pt-- = '0' + (char)(val & 0x7);
if ((val = (val >> 3)) == (u_quad_t)0)
break;
}
}
/*
* pad with leading ascii ZEROS. We return -1 if we ran out of space.
*/
while (pt >= str)
*pt-- = '0';
if (val != (u_quad_t)0)
return(-1);
return(0);
}
#endif

70
commands/pax/getoldopt.c Normal file
View file

@ -0,0 +1,70 @@
/* $OpenBSD: getoldopt.c,v 1.4 2000/01/22 20:24:51 deraadt Exp $ */
/* $NetBSD: getoldopt.c,v 1.3 1995/03/21 09:07:28 cgd Exp $ */
/*-
* Plug-compatible replacement for getopt() for parsing tar-like
* arguments. If the first argument begins with "-", it uses getopt;
* otherwise, it uses the old rules used by tar, dump, and ps.
*
* Written 25 August 1985 by John Gilmore (ihnp4!hoptoad!gnu) and placed
* in the Pubic Domain for your edification and enjoyment.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "pax.h"
#include "extern.h"
int
getoldopt(int argc, char **argv, const char *optstring)
{
static char *key; /* Points to next keyletter */
static char use_getopt; /* !=0 if argv[1][0] was '-' */
char c;
char *place;
optarg = NULL;
if (key == NULL) { /* First time */
if (argc < 2) return EOF;
key = argv[1];
if (*key == '-')
use_getopt++;
else
optind = 2;
}
if (use_getopt)
return getopt(argc, argv, optstring);
c = *key++;
if (c == '\0') {
key--;
return EOF;
}
place = strchr(optstring, c);
if (place == NULL || c == ':') {
fprintf(stderr, "%s: unknown option %c\n", argv[0], c);
return('?');
}
place++;
if (*place == ':') {
if (optind < argc) {
optarg = argv[optind];
optind++;
} else {
fprintf(stderr, "%s: %c argument missing\n",
argv[0], c);
return('?');
}
}
return(c);
}

1579
commands/pax/options.c Normal file

File diff suppressed because it is too large Load diff

110
commands/pax/options.h Normal file
View file

@ -0,0 +1,110 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)options.h 8.2 (Berkeley) 4/18/94
* $FreeBSD: src/bin/pax/options.h,v 1.6 2004/04/06 20:06:48 markm Exp $
*/
/*
* argv[0] names. Used for tar and cpio emulation
*/
#define NM_TAR "tar"
#define NM_CPIO "cpio"
#define NM_PAX "pax"
/*
* Constants used to specify the legal sets of flags in pax. For each major
* operation mode of pax, a set of illegal flags is defined. If any one of
* those illegal flags are found set, we scream and exit
*/
#define NONE "none"
/*
* flags (one for each option).
*/
#define AF 0x00000001
#define BF 0x00000002
#define CF 0x00000004
#define DF 0x00000008
#define FF 0x00000010
#define IF 0x00000020
#define KF 0x00000040
#define LF 0x00000080
#define NF 0x00000100
#define OF 0x00000200
#define PF 0x00000400
#define RF 0x00000800
#define SF 0x00001000
#define TF 0x00002000
#define UF 0x00004000
#define VF 0x00008000
#define WF 0x00010000
#define XF 0x00020000
#define CBF 0x00040000 /* nonstandard extension */
#define CDF 0x00080000 /* nonstandard extension */
#define CEF 0x00100000 /* nonstandard extension */
#define CGF 0x00200000 /* nonstandard extension */
#define CHF 0x00400000 /* nonstandard extension */
#define CLF 0x00800000 /* nonstandard extension */
#define CPF 0x01000000 /* nonstandard extension */
#define CTF 0x02000000 /* nonstandard extension */
#define CUF 0x04000000 /* nonstandard extension */
#define CXF 0x08000000
#define CYF 0x10000000 /* nonstandard extension */
#define CZF 0x20000000 /* nonstandard extension */
/*
* ascii string indexed by bit position above (alter the above and you must
* alter this string) used to tell the user what flags caused us to complain
*/
#define FLGCH "abcdfiklnoprstuvwxBDEGHLPTUXYZ"
/*
* legal pax operation bit patterns
*/
#define ISLIST(x) (((x) & (RF|WF)) == 0)
#define ISEXTRACT(x) (((x) & (RF|WF)) == RF)
#define ISARCHIVE(x) (((x) & (AF|RF|WF)) == WF)
#define ISAPPND(x) (((x) & (AF|RF|WF)) == (AF|WF))
#define ISCOPY(x) (((x) & (RF|WF)) == (RF|WF))
#define ISWRITE(x) (((x) & (RF|WF)) == WF)
/*
* Illegal option flag subsets based on pax operation
*/
#define BDEXTR (AF|BF|LF|TF|WF|XF|CBF|CHF|CLF|CPF|CXF)
#define BDARCH (CF|KF|LF|NF|PF|RF|CDF|CEF|CYF|CZF)
#define BDCOPY (AF|BF|FF|OF|XF|CBF|CEF)
#define BDLIST (AF|BF|IF|KF|LF|OF|PF|RF|TF|UF|WF|XF|CBF|CDF|CHF|CLF|CPF|CXF|CYF|CZF)

1128
commands/pax/pat_rep.c Normal file

File diff suppressed because it is too large Load diff

51
commands/pax/pat_rep.h Normal file
View file

@ -0,0 +1,51 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)pat_rep.h 8.1 (Berkeley) 5/31/93
* $FreeBSD: src/bin/pax/pat_rep.h,v 1.6 2004/04/06 20:06:48 markm Exp $
*/
/*
* data structure for storing user supplied replacement strings (-s)
*/
typedef struct replace {
char *nstr; /* the new string we will substitute with */
# ifdef NET2_REGEX
regexp *rcmp; /* compiled regular expression used to match */
# else
regex_t rcmp; /* compiled regular expression used to match */
# endif
int flgs; /* print conversions? global in operation? */
#define PRNT 0x1
#define GLOB 0x2
struct replace *fow; /* pointer to next pattern */
} REPLACE;

439
commands/pax/pax.c Normal file
View file

@ -0,0 +1,439 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#if 0
#ifndef lint
static char const copyright[] =
"@(#) Copyright (c) 1992, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)pax.c 8.2 (Berkeley) 4/18/94";
#endif /* not lint */
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <errno.h>
#include <fcntl.h>
#include <locale.h>
#include <minix/paths.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "pax.h"
#include "extern.h"
static int gen_init(void);
/*
* PAX main routines, general globals and some simple start up routines
*/
/*
* Variables that can be accessed by any routine within pax
*/
int act = DEFOP; /* read/write/append/copy */
FSUB *frmt = NULL; /* archive format type */
int cflag; /* match all EXCEPT pattern/file */
int cwdfd; /* starting cwd */
int dflag; /* directory member match only */
int iflag; /* interactive file/archive rename */
int kflag; /* do not overwrite existing files */
int lflag; /* use hard links when possible */
int nflag; /* select first archive member match */
int tflag; /* restore access time after read */
int uflag; /* ignore older modification time files */
int vflag; /* produce verbose output */
int Dflag; /* same as uflag except inode change time */
int Hflag; /* follow command line symlinks (write only) */
int Lflag; /* follow symlinks when writing */
int Xflag; /* archive files with same device id only */
int Yflag; /* same as Dflg except after name mode */
int Zflag; /* same as uflg except after name mode */
int vfpart; /* is partial verbose output in progress */
int patime = 1; /* preserve file access time */
int pmtime = 1; /* preserve file modification times */
int nodirs; /* do not create directories as needed */
int pmode; /* preserve file mode bits */
int pids; /* preserve file uid/gid */
int rmleadslash = 0; /* remove leading '/' from pathnames */
int exit_val; /* exit value */
int docrc; /* check/create file crc */
char *dirptr; /* destination dir in a copy */
const char *argv0; /* root of argv[0] */
sigset_t s_mask; /* signal mask for cleanup critical sect */
FILE *listf; /* file pointer to print file list to */
char *tempfile; /* tempfile to use for mkstemp(3) */
char *tempbase; /* basename of tempfile to use for mkstemp(3) */
/*
* PAX - Portable Archive Interchange
*
* A utility to read, write, and write lists of the members of archive
* files and copy directory hierarchies. A variety of archive formats
* are supported (some are described in POSIX 1003.1 10.1):
*
* ustar - 10.1.1 extended tar interchange format
* cpio - 10.1.2 extended cpio interchange format
* tar - old BSD 4.3 tar format
* binary cpio - old cpio with binary header format
* sysVR4 cpio - with and without CRC
*
* This version is a superset of IEEE Std 1003.2b-d3
*
* Summary of Extensions to the IEEE Standard:
*
* 1 READ ENHANCEMENTS
* 1.1 Operations which read archives will continue to operate even when
* processing archives which may be damaged, truncated, or fail to meet
* format specs in several different ways. Damaged sections of archives
* are detected and avoided if possible. Attempts will be made to resync
* archive read operations even with badly damaged media.
* 1.2 Blocksize requirements are not strictly enforced on archive read.
* Tapes which have variable sized records can be read without errors.
* 1.3 The user can specify via the non-standard option flag -E if error
* resync operation should stop on a media error, try a specified number
* of times to correct, or try to correct forever.
* 1.4 Sparse files (lseek holes) stored on the archive (but stored with blocks
* of all zeros will be restored with holes appropriate for the target
* file system
* 1.5 The user is notified whenever something is found during archive
* read operations which violates spec (but the read will continue).
* 1.6 Multiple archive volumes can be read and may span over different
* archive devices
* 1.7 Rigidly restores all file attributes exactly as they are stored on the
* archive.
* 1.8 Modification change time ranges can be specified via multiple -T
* options. These allow a user to select files whose modification time
* lies within a specific time range.
* 1.9 Files can be selected based on owner (user name or uid) via one or more
* -U options.
* 1.10 Files can be selected based on group (group name or gid) via one o
* more -G options.
* 1.11 File modification time can be checked against existing file after
* name modification (-Z)
*
* 2 WRITE ENHANCEMENTS
* 2.1 Write operation will stop instead of allowing a user to create a flawed
* flawed archive (due to any problem).
* 2.2 Archives written by pax are forced to strictly conform to both the
* archive and pax the specific format specifications.
* 2.3 Blocking size and format is rigidly enforced on writes.
* 2.4 Formats which may exhibit header overflow problems (they have fields
* too small for large file systems, such as inode number storage), use
* routines designed to repair this problem. These techniques still
* conform to both pax and format specifications, but no longer truncate
* these fields. This removes any restrictions on using these archive
* formats on large file systems.
* 2.5 Multiple archive volumes can be written and may span over different
* archive devices
* 2.6 A archive volume record limit allows the user to specify the number
* of bytes stored on an archive volume. When reached the user is
* prompted for the next archive volume. This is specified with the
* non-standard -B flag. The limit is rounded up to the next blocksize.
* 2.7 All archive padding during write use zero filled sections. This makes
* it much easier to pull data out of flawed archive during read
* operations.
* 2.8 Access time reset with the -t applies to all file nodes (including
* directories).
* 2.9 Symbolic links can be followed with -L (optional in the spec).
* 2.10 Modification or inode change time ranges can be specified via
* multiple -T options. These allow a user to select files whose
* modification or inode change time lies within a specific time range.
* 2.11 Files can be selected based on owner (user name or uid) via one or more
* -U options.
* 2.12 Files can be selected based on group (group name or gid) via one o
* more -G options.
* 2.13 Symlinks which appear on the command line can be followed (without
* following other symlinks; -H flag)
*
* 3 COPY ENHANCEMENTS
* 3.1 Sparse files (lseek holes) can be copied without expanding the holes
* into zero filled blocks. The file copy is created with holes which are
* appropriate for the target file system
* 3.2 Access time as well as modification time on copied file trees can be
* preserved with the appropriate -p options.
* 3.3 Access time reset with the -t applies to all file nodes (including
* directories).
* 3.4 Symbolic links can be followed with -L (optional in the spec).
* 3.5 Modification or inode change time ranges can be specified via
* multiple -T options. These allow a user to select files whose
* modification or inode change time lies within a specific time range.
* 3.6 Files can be selected based on owner (user name or uid) via one or more
* -U options.
* 3.7 Files can be selected based on group (group name or gid) via one o
* more -G options.
* 3.8 Symlinks which appear on the command line can be followed (without
* following other symlinks; -H flag)
* 3.9 File inode change time can be checked against existing file before
* name modification (-D)
* 3.10 File inode change time can be checked against existing file after
* name modification (-Y)
* 3.11 File modification time can be checked against existing file after
* name modification (-Z)
*
* 4 GENERAL ENHANCEMENTS
* 4.1 Internal structure is designed to isolate format dependent and
* independent functions. Formats are selected via a format driver table.
* This encourages the addition of new archive formats by only having to
* write those routines which id, read and write the archive header.
*/
/*
* main()
* parse options, set up and operate as specified by the user.
* any operational flaw will set exit_val to non-zero
* Return: 0 if ok, 1 otherwise
*/
int
main(int argc, char *argv[])
{
const char *tmpdir;
size_t tdlen;
(void) setlocale(LC_ALL, "");
listf = stderr;
/*
* Keep a reference to cwd, so we can always come back home.
*/
cwdfd = open(".", O_RDONLY);
if (cwdfd < 0) {
syswarn(0, errno, "Can't open current working directory.");
return(exit_val);
}
/*
* Where should we put temporary files?
*/
if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
tmpdir = _PATH_TMP;
tdlen = strlen(tmpdir);
while(tdlen > 0 && tmpdir[tdlen - 1] == '/')
tdlen--;
tempfile = malloc(tdlen + 1 + sizeof(_TFILE_BASE));
if (tempfile == NULL) {
paxwarn(1, "Cannot allocate memory for temp file name.");
return(exit_val);
}
if (tdlen)
memcpy(tempfile, tmpdir, tdlen);
tempbase = tempfile + tdlen;
*tempbase++ = '/';
/*
* parse options, determine operational mode, general init
*/
options(argc, argv);
if ((gen_init() < 0) || (tty_init() < 0))
return(exit_val);
/*
* select a primary operation mode
*/
switch(act) {
case EXTRACT:
extract();
break;
case ARCHIVE:
archive();
break;
case APPND:
if (gzip_program != NULL)
err(1, "can not gzip while appending");
append();
break;
case COPY:
copy();
break;
default:
case LIST:
list();
break;
}
return(exit_val);
}
/*
* sig_cleanup()
* when interrupted we try to do whatever delayed processing we can.
* This is not critical, but we really ought to limit our damage when we
* are aborted by the user.
* Return:
* never....
*/
void
sig_cleanup(int which_sig)
{
/*
* restore modes and times for any dirs we may have created
* or any dirs we may have read. Set vflag and vfpart so the user
* will clearly see the message on a line by itself.
*/
vflag = vfpart = 1;
#if 0
/* ignore this under minix */
if (which_sig == SIGXCPU)
paxwarn(0, "Cpu time limit reached, cleaning up.");
else
#endif
paxwarn(0, "Signal caught, cleaning up.");
ar_close();
proc_dir();
if (tflag)
atdir_end();
exit(1);
}
/*
* gen_init()
* general setup routines. Not all are required, but they really help
* when dealing with a medium to large sized archives.
*/
static int
gen_init(void)
{
#if 0
struct rlimit reslimit;
#endif
struct sigaction n_hand;
struct sigaction o_hand;
#if 0
/*
* Really needed to handle large archives. We can run out of memory for
* internal tables really fast when we have a whole lot of files...
*/
if (getrlimit(RLIMIT_DATA , &reslimit) == 0){
reslimit.rlim_cur = reslimit.rlim_max;
(void)setrlimit(RLIMIT_DATA , &reslimit);
}
/*
* should file size limits be waived? if the os limits us, this is
* needed if we want to write a large archive
*/
if (getrlimit(RLIMIT_FSIZE , &reslimit) == 0){
reslimit.rlim_cur = reslimit.rlim_max;
(void)setrlimit(RLIMIT_FSIZE , &reslimit);
}
/*
* increase the size the stack can grow to
*/
if (getrlimit(RLIMIT_STACK , &reslimit) == 0){
reslimit.rlim_cur = reslimit.rlim_max;
(void)setrlimit(RLIMIT_STACK , &reslimit);
}
/*
* not really needed, but doesn't hurt
*/
if (getrlimit(RLIMIT_RSS , &reslimit) == 0){
reslimit.rlim_cur = reslimit.rlim_max;
(void)setrlimit(RLIMIT_RSS , &reslimit);
}
#endif
/*
* signal handling to reset stored directory times and modes. Since
* we deal with broken pipes via failed writes we ignore it. We also
* deal with any file size limit thorugh failed writes. Cpu time
* limits are caught and a cleanup is forced.
*/
if ((sigemptyset(&s_mask) < 0) || (sigaddset(&s_mask, SIGTERM) < 0) ||
(sigaddset(&s_mask,SIGINT) < 0)||(sigaddset(&s_mask,SIGHUP) < 0) ||
(sigaddset(&s_mask,SIGPIPE) < 0)||(sigaddset(&s_mask,SIGQUIT)<0)
#if 0
|| (sigaddset(&s_mask,SIGXCPU) < 0)||(sigaddset(&s_mask,SIGXFSZ)<0)
#endif
) {
paxwarn(1, "Unable to set up signal mask");
return(-1);
}
memset(&n_hand, 0, sizeof n_hand);
n_hand.sa_mask = s_mask;
n_hand.sa_flags = 0;
n_hand.sa_handler = sig_cleanup;
if ((sigaction(SIGHUP, &n_hand, &o_hand) < 0) &&
(o_hand.sa_handler == SIG_IGN) &&
(sigaction(SIGHUP, &o_hand, &o_hand) < 0))
goto out;
if ((sigaction(SIGTERM, &n_hand, &o_hand) < 0) &&
(o_hand.sa_handler == SIG_IGN) &&
(sigaction(SIGTERM, &o_hand, &o_hand) < 0))
goto out;
if ((sigaction(SIGINT, &n_hand, &o_hand) < 0) &&
(o_hand.sa_handler == SIG_IGN) &&
(sigaction(SIGINT, &o_hand, &o_hand) < 0))
goto out;
if ((sigaction(SIGQUIT, &n_hand, &o_hand) < 0) &&
(o_hand.sa_handler == SIG_IGN) &&
(sigaction(SIGQUIT, &o_hand, &o_hand) < 0))
goto out;
#if 0
if ((sigaction(SIGXCPU, &n_hand, &o_hand) < 0) &&
(o_hand.sa_handler == SIG_IGN) &&
(sigaction(SIGXCPU, &o_hand, &o_hand) < 0))
goto out;
#endif
n_hand.sa_handler = SIG_IGN;
if ((sigaction(SIGPIPE, &n_hand, &o_hand) < 0)
#if 0
|| (sigaction(SIGXFSZ, &n_hand, &o_hand) < 0)
#endif
)
goto out;
return(0);
out:
syswarn(1, errno, "Unable to set up signal handler");
return(-1);
}

251
commands/pax/pax.h Normal file
View file

@ -0,0 +1,251 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)pax.h 8.2 (Berkeley) 4/18/94
* $FreeBSD: src/bin/pax/pax.h,v 1.18 2004/04/06 20:06:48 markm Exp $
*/
#include <minix/config.h>
#include <minix/const.h>
/*
* BSD PAX global data structures and constants.
*/
#define MAXBLK 64512 /* MAX blocksize supported (posix SPEC) */
/* WARNING: increasing MAXBLK past 32256 */
/* will violate posix spec. */
#define MAXBLK_POSIX 32256 /* MAX blocksize supported as per POSIX */
#define BLKMULT 512 /* blocksize must be even mult of 512 bytes */
/* Don't even think of changing this */
#define DEVBLK 8192 /* default read blksize for devices */
#define FILEBLK 10240 /* default read blksize for files */
#define PAXPATHLEN 3072 /* maximum path length for pax. MUST be */
/* longer than the system PATH_MAX */
/*
* Pax modes of operation
*/
#define LIST 0 /* List the file in an archive */
#define EXTRACT 1 /* extract the files in an archive */
#define ARCHIVE 2 /* write a new archive */
#define APPND 3 /* append to the end of an archive */
#define COPY 4 /* copy files to destination dir */
#define DEFOP LIST /* if no flags default is to LIST */
/*
* Device type of the current archive volume
*/
#define ISREG 0 /* regular file */
#define ISCHR 1 /* character device */
#define ISBLK 2 /* block device */
#define ISTAPE 3 /* tape drive */
#define ISPIPE 4 /* pipe/socket */
typedef struct archd ARCHD;
typedef struct fsub FSUB;
typedef struct oplist OPLIST;
typedef struct pattern PATTERN;
/*
* Format Specific Routine Table
*
* The format specific routine table allows new archive formats to be quickly
* added. Overall pax operation is independent of the actual format used to
* form the archive. Only those routines which deal directly with the archive
* are tailored to the oddities of the specific format. All other routines are
* independent of the archive format. Data flow in and out of the format
* dependent routines pass pointers to ARCHD structure (described below).
*/
struct fsub {
const char *name; /* name of format, this is the name the user */
/* gives to -x option to select it. */
int bsz; /* default block size. used when the user */
/* does not specify a blocksize for writing */
/* Appends continue to with the blocksize */
/* the archive is currently using. */
int hsz; /* Header size in bytes. this is the size of */
/* the smallest header this format supports. */
/* Headers are assumed to fit in a BLKMULT. */
/* If they are bigger, get_head() and */
/* get_arc() must be adjusted */
int udev; /* does append require unique dev/ino? some */
/* formats use the device and inode fields */
/* to specify hard links. when members in */
/* the archive have the same inode/dev they */
/* are assumed to be hard links. During */
/* append we may have to generate unique ids */
/* to avoid creating incorrect hard links */
int hlk; /* does archive store hard links info? if */
/* not, we do not bother to look for them */
/* during archive write operations */
int blkalgn; /* writes must be aligned to blkalgn boundary */
int inhead; /* is the trailer encoded in a valid header? */
/* if not, trailers are assumed to be found */
/* in invalid headers (i.e like tar) */
int (*id)(char *, int); /* checks if a buffer is a valid header */
/* returns 1 if it is, o.w. returns a 0 */
int (*st_rd)(void); /* initialize routine for read. so format */
/* can set up tables etc before it starts */
/* reading an archive */
int (*rd)(ARCHD *, char *);
/* read header routine. passed a pointer to */
/* ARCHD. It must extract the info from the */
/* format and store it in the ARCHD struct. */
/* This routine is expected to fill all the */
/* fields in the ARCHD (including stat buf) */
/* 0 is returned when a valid header is */
/* found. -1 when not valid. This routine */
/* set the skip and pad fields so the format */
/* independent routines know the amount of */
/* padding and the number of bytes of data */
/* which follow the header. This info is */
/* used skip to the next file header */
off_t (*end_rd)(void); /* read cleanup. Allows format to clean up */
/* and MUST RETURN THE LENGTH OF THE TRAILER */
/* RECORD (so append knows how many bytes */
/* to move back to rewrite the trailer) */
int (*st_wr)(void); /* initialize routine for write operations */
int (*wr)(ARCHD *); /* write archive header. Passed an ARCHD */
/* filled with the specs on the next file to */
/* archived. Returns a 1 if no file data is */
/* is to be stored; 0 if file data is to be */
/* added. A -1 is returned if a write */
/* operation to the archive failed. this */
/* function sets the skip and pad fields so */
/* the proper padding can be added after */
/* file data. This routine must NEVER write */
/* a flawed archive header. */
int (*end_wr)(void); /* end write. write the trailer and do any */
/* other format specific functions needed */
/* at the end of an archive write */
int (*trail_cpio)(ARCHD *);
int (*trail_tar)(char *, int, int *);
/* returns 0 if a valid trailer, -1 if not */
/* For formats which encode the trailer */
/* outside of a valid header, a return value */
/* of 1 indicates that the block passed to */
/* it can never contain a valid header (skip */
/* this block, no point in looking at it) */
int (*rd_data)(ARCHD *, int, off_t *);
/* read/process file data from the archive */
int (*wr_data)(ARCHD *, int, off_t *);
/* write/process file data to the archive */
int (*options)(void); /* process format specific options (-o) */
};
/*
* Pattern matching structure
*
* Used to store command line patterns
*/
struct pattern {
char *pstr; /* pattern to match, user supplied */
char *pend; /* end of a prefix match */
char *chdname; /* the dir to change to if not NULL. */
int plen; /* length of pstr */
int flgs; /* processing/state flags */
#define MTCH 0x1 /* pattern has been matched */
#define DIR_MTCH 0x2 /* pattern matched a directory */
struct pattern *fow; /* next pattern */
};
/*
* General Archive Structure (used internal to pax)
*
* This structure is used to pass information about archive members between
* the format independent routines and the format specific routines. When
* new archive formats are added, they must accept requests and supply info
* encoded in a structure of this type. The name fields are declared statically
* here, as there is only ONE of these floating around, size is not a major
* consideration. Eventually converting the name fields to a dynamic length
* may be required if and when the supporting operating system removes all
* restrictions on the length of pathnames it will resolve.
*/
struct archd {
int nlen; /* file name length */
char name[PAXPATHLEN+1]; /* file name */
int ln_nlen; /* link name length */
char ln_name[PAXPATHLEN+1]; /* name to link to (if any) */
char *org_name; /* orig name in file system */
PATTERN *pat; /* ptr to pattern match (if any) */
struct stat sb; /* stat buffer see stat(2) */
off_t pad; /* bytes of padding after file xfer */
off_t skip; /* bytes of real data after header */
/* IMPORTANT. The st_size field does */
/* not always indicate the amount of */
/* data following the header. */
u_long crc; /* file crc */
int type; /* type of file node */
#define PAX_DIR 1 /* directory */
#define PAX_CHR 2 /* character device */
#define PAX_BLK 3 /* block device */
#define PAX_REG 4 /* regular file */
#define PAX_SLK 5 /* symbolic link */
#define PAX_SCK 6 /* socket */
#define PAX_FIF 7 /* fifo */
#define PAX_HLK 8 /* hard link */
#define PAX_HRG 9 /* hard link to a regular file */
#define PAX_CTG 10 /* high performance file */
};
/*
* Format Specific Options List
*
* Used to pass format options to the format options handler
*/
struct oplist {
char *name; /* option variable name e.g. name= */
char *value; /* value for option variable */
struct oplist *fow; /* next option */
};
/*
* General Macros
*/
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
#define TODEV(x, y) makedev((x), (y))
/*
* General Defines
*/
#define HEX 16
#define OCT 8
#define _PAX_ 1
#define _TFILE_BASE "paxXXXXXXXXXX"
#define err(c, str) { perror(str); exit(c); }
#define setpassent(a) setpwent()
#define setgroupent(a) setgrent()

605
commands/pax/sel_subs.c Normal file
View file

@ -0,0 +1,605 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
#if 0
static char sccsid[] = "@(#)sel_subs.c 8.1 (Berkeley) 5/31/93";
#endif
#endif /* not lint */
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <pwd.h>
#include <time.h>
#include <grp.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <stdlib.h>
#include "pax.h"
#include "sel_subs.h"
#include "extern.h"
static int str_sec(char *, time_t *);
static int usr_match(ARCHD *);
static int grp_match(ARCHD *);
static int trng_match(ARCHD *);
static TIME_RNG *trhead = NULL; /* time range list head */
static TIME_RNG *trtail = NULL; /* time range list tail */
static USRT **usrtb = NULL; /* user selection table */
static GRPT **grptb = NULL; /* group selection table */
/*
* Routines for selection of archive members
*/
/*
* sel_chk()
* check if this file matches a specified uid, gid or time range
* Return:
* 0 if this archive member should be processed, 1 if it should be skipped
*/
int
sel_chk(ARCHD *arcn)
{
if (((usrtb != NULL) && usr_match(arcn)) ||
((grptb != NULL) && grp_match(arcn)) ||
((trhead != NULL) && trng_match(arcn)))
return(1);
return(0);
}
/*
* User/group selection routines
*
* Routines to handle user selection of files based on the file uid/gid. To
* add an entry, the user supplies either then name or the uid/gid starting with
* a # on the command line. A \# will escape the #.
*/
/*
* usr_add()
* add a user match to the user match hash table
* Return:
* 0 if added ok, -1 otherwise;
*/
int
usr_add(char *str)
{
u_int indx;
USRT *pt;
struct passwd *pw;
uid_t uid;
/*
* create the table if it doesn't exist
*/
if ((str == NULL) || (*str == '\0'))
return(-1);
if ((usrtb == NULL) &&
((usrtb = (USRT **)calloc(USR_TB_SZ, sizeof(USRT *))) == NULL)) {
paxwarn(1, "Unable to allocate memory for user selection table");
return(-1);
}
/*
* figure out user spec
*/
if (str[0] != '#') {
/*
* it is a user name, \# escapes # as first char in user name
*/
if ((str[0] == '\\') && (str[1] == '#'))
++str;
if ((pw = getpwnam(str)) == NULL) {
paxwarn(1, "Unable to find uid for user: %s", str);
return(-1);
}
uid = (uid_t)pw->pw_uid;
} else
# ifdef NET2_STAT
uid = (uid_t)atoi(str+1);
# else
uid = (uid_t)strtoul(str+1, NULL, 10);
# endif
endpwent();
/*
* hash it and go down the hash chain (if any) looking for it
*/
indx = ((unsigned)uid) % USR_TB_SZ;
if ((pt = usrtb[indx]) != NULL) {
while (pt != NULL) {
if (pt->uid == uid)
return(0);
pt = pt->fow;
}
}
/*
* uid is not yet in the table, add it to the front of the chain
*/
if ((pt = (USRT *)malloc(sizeof(USRT))) != NULL) {
pt->uid = uid;
pt->fow = usrtb[indx];
usrtb[indx] = pt;
return(0);
}
paxwarn(1, "User selection table out of memory");
return(-1);
}
/*
* usr_match()
* check if this files uid matches a selected uid.
* Return:
* 0 if this archive member should be processed, 1 if it should be skipped
*/
static int
usr_match(ARCHD *arcn)
{
USRT *pt;
/*
* hash and look for it in the table
*/
pt = usrtb[((unsigned)arcn->sb.st_uid) % USR_TB_SZ];
while (pt != NULL) {
if (pt->uid == arcn->sb.st_uid)
return(0);
pt = pt->fow;
}
/*
* not found
*/
return(1);
}
/*
* grp_add()
* add a group match to the group match hash table
* Return:
* 0 if added ok, -1 otherwise;
*/
int
grp_add(char *str)
{
u_int indx;
GRPT *pt;
struct group *gr;
gid_t gid;
/*
* create the table if it doesn't exist
*/
if ((str == NULL) || (*str == '\0'))
return(-1);
if ((grptb == NULL) &&
((grptb = (GRPT **)calloc(GRP_TB_SZ, sizeof(GRPT *))) == NULL)) {
paxwarn(1, "Unable to allocate memory fo group selection table");
return(-1);
}
/*
* figure out user spec
*/
if (str[0] != '#') {
/*
* it is a group name, \# escapes # as first char in group name
*/
if ((str[0] == '\\') && (str[1] == '#'))
++str;
if ((gr = getgrnam(str)) == NULL) {
paxwarn(1,"Cannot determine gid for group name: %s", str);
return(-1);
}
gid = gr->gr_gid;
} else
# ifdef NET2_STAT
gid = (gid_t)atoi(str+1);
# else
gid = (gid_t)strtoul(str+1, NULL, 10);
# endif
endgrent();
/*
* hash it and go down the hash chain (if any) looking for it
*/
indx = ((unsigned)gid) % GRP_TB_SZ;
if ((pt = grptb[indx]) != NULL) {
while (pt != NULL) {
if (pt->gid == gid)
return(0);
pt = pt->fow;
}
}
/*
* gid not in the table, add it to the front of the chain
*/
if ((pt = (GRPT *)malloc(sizeof(GRPT))) != NULL) {
pt->gid = gid;
pt->fow = grptb[indx];
grptb[indx] = pt;
return(0);
}
paxwarn(1, "Group selection table out of memory");
return(-1);
}
/*
* grp_match()
* check if this files gid matches a selected gid.
* Return:
* 0 if this archive member should be processed, 1 if it should be skipped
*/
static int
grp_match(ARCHD *arcn)
{
GRPT *pt;
/*
* hash and look for it in the table
*/
pt = grptb[((unsigned)arcn->sb.st_gid) % GRP_TB_SZ];
while (pt != NULL) {
if (pt->gid == arcn->sb.st_gid)
return(0);
pt = pt->fow;
}
/*
* not found
*/
return(1);
}
/*
* Time range selection routines
*
* Routines to handle user selection of files based on the modification and/or
* inode change time falling within a specified time range (the non-standard
* -T flag). The user may specify any number of different file time ranges.
* Time ranges are checked one at a time until a match is found (if at all).
* If the file has a mtime (and/or ctime) which lies within one of the time
* ranges, the file is selected. Time ranges may have a lower and/or an upper
* value. These ranges are inclusive. When no time ranges are supplied to pax
* with the -T option, all members in the archive will be selected by the time
* range routines. When only a lower range is supplied, only files with a
* mtime (and/or ctime) equal to or younger are selected. When only an upper
* range is supplied, only files with a mtime (and/or ctime) equal to or older
* are selected. When the lower time range is equal to the upper time range,
* only files with a mtime (or ctime) of exactly that time are selected.
*/
/*
* trng_add()
* add a time range match to the time range list.
* This is a non-standard pax option. Lower and upper ranges are in the
* format: [yy[mm[dd[hh]]]]mm[.ss] and are comma separated.
* Time ranges are based on current time, so 1234 would specify a time of
* 12:34 today.
* Return:
* 0 if the time range was added to the list, -1 otherwise
*/
int
trng_add(char *str)
{
TIME_RNG *pt;
char *up_pt = NULL;
char *stpt;
char *flgpt;
int dot = 0;
/*
* throw out the badly formed time ranges
*/
if ((str == NULL) || (*str == '\0')) {
paxwarn(1, "Empty time range string");
return(-1);
}
/*
* locate optional flags suffix /{cm}.
*/
if ((flgpt = strrchr(str, '/')) != NULL)
*flgpt++ = '\0';
for (stpt = str; *stpt != '\0'; ++stpt) {
if ((*stpt >= '0') && (*stpt <= '9'))
continue;
if ((*stpt == ',') && (up_pt == NULL)) {
*stpt = '\0';
up_pt = stpt + 1;
dot = 0;
continue;
}
/*
* allow only one dot per range (secs)
*/
if ((*stpt == '.') && (!dot)) {
++dot;
continue;
}
paxwarn(1, "Improperly specified time range: %s", str);
goto out;
}
/*
* allocate space for the time range and store the limits
*/
if ((pt = (TIME_RNG *)malloc(sizeof(TIME_RNG))) == NULL) {
paxwarn(1, "Unable to allocate memory for time range");
return(-1);
}
/*
* by default we only will check file mtime, but usee can specify
* mtime, ctime (inode change time) or both.
*/
if ((flgpt == NULL) || (*flgpt == '\0'))
pt->flgs = CMPMTME;
else {
pt->flgs = 0;
while (*flgpt != '\0') {
switch(*flgpt) {
case 'M':
case 'm':
pt->flgs |= CMPMTME;
break;
case 'C':
case 'c':
pt->flgs |= CMPCTME;
break;
default:
paxwarn(1, "Bad option %c with time range %s",
*flgpt, str);
goto out;
}
++flgpt;
}
}
/*
* start off with the current time
*/
pt->low_time = pt->high_time = time(NULL);
if (*str != '\0') {
/*
* add lower limit
*/
if (str_sec(str, &(pt->low_time)) < 0) {
paxwarn(1, "Illegal lower time range %s", str);
(void)free((char *)pt);
goto out;
}
pt->flgs |= HASLOW;
}
if ((up_pt != NULL) && (*up_pt != '\0')) {
/*
* add upper limit
*/
if (str_sec(up_pt, &(pt->high_time)) < 0) {
paxwarn(1, "Illegal upper time range %s", up_pt);
(void)free((char *)pt);
goto out;
}
pt->flgs |= HASHIGH;
/*
* check that the upper and lower do not overlap
*/
if (pt->flgs & HASLOW) {
if (pt->low_time > pt->high_time) {
paxwarn(1, "Upper %s and lower %s time overlap",
up_pt, str);
(void)free((char *)pt);
return(-1);
}
}
}
pt->fow = NULL;
if (trhead == NULL) {
trtail = trhead = pt;
return(0);
}
trtail->fow = pt;
trtail = pt;
return(0);
out:
paxwarn(1, "Time range format is: [yy[mm[dd[hh]]]]mm[.ss][/[c][m]]");
return(-1);
}
/*
* trng_match()
* check if this files mtime/ctime falls within any supplied time range.
* Return:
* 0 if this archive member should be processed, 1 if it should be skipped
*/
static int
trng_match(ARCHD *arcn)
{
TIME_RNG *pt;
/*
* have to search down the list one at a time looking for a match.
* remember time range limits are inclusive.
*/
pt = trhead;
while (pt != NULL) {
switch(pt->flgs & CMPBOTH) {
case CMPBOTH:
/*
* user wants both mtime and ctime checked for this
* time range
*/
if (((pt->flgs & HASLOW) &&
(arcn->sb.st_mtime < pt->low_time) &&
(arcn->sb.st_ctime < pt->low_time)) ||
((pt->flgs & HASHIGH) &&
(arcn->sb.st_mtime > pt->high_time) &&
(arcn->sb.st_ctime > pt->high_time))) {
pt = pt->fow;
continue;
}
break;
case CMPCTME:
/*
* user wants only ctime checked for this time range
*/
if (((pt->flgs & HASLOW) &&
(arcn->sb.st_ctime < pt->low_time)) ||
((pt->flgs & HASHIGH) &&
(arcn->sb.st_ctime > pt->high_time))) {
pt = pt->fow;
continue;
}
break;
case CMPMTME:
default:
/*
* user wants only mtime checked for this time range
*/
if (((pt->flgs & HASLOW) &&
(arcn->sb.st_mtime < pt->low_time)) ||
((pt->flgs & HASHIGH) &&
(arcn->sb.st_mtime > pt->high_time))) {
pt = pt->fow;
continue;
}
break;
}
break;
}
if (pt == NULL)
return(1);
return(0);
}
/*
* str_sec()
* Convert a time string in the format of [yy[mm[dd[hh]]]]mm[.ss] to gmt
* seconds. Tval already has current time loaded into it at entry.
* Return:
* 0 if converted ok, -1 otherwise
*/
static int
str_sec(char *str, time_t *tval)
{
struct tm *lt;
char *dot = NULL;
lt = localtime(tval);
if ((dot = strchr(str, '.')) != NULL) {
/*
* seconds (.ss)
*/
*dot++ = '\0';
if (strlen(dot) != 2)
return(-1);
if ((lt->tm_sec = ATOI2(dot)) > 61)
return(-1);
} else
lt->tm_sec = 0;
switch (strlen(str)) {
case 10:
/*
* year (yy)
* watch out for year 2000
*/
if ((lt->tm_year = ATOI2(str)) < 69)
lt->tm_year += 100;
str += 2;
/* FALLTHROUGH */
case 8:
/*
* month (mm)
* watch out months are from 0 - 11 internally
*/
if ((lt->tm_mon = ATOI2(str)) > 12)
return(-1);
--lt->tm_mon;
str += 2;
/* FALLTHROUGH */
case 6:
/*
* day (dd)
*/
if ((lt->tm_mday = ATOI2(str)) > 31)
return(-1);
str += 2;
/* FALLTHROUGH */
case 4:
/*
* hour (hh)
*/
if ((lt->tm_hour = ATOI2(str)) > 23)
return(-1);
str += 2;
/* FALLTHROUGH */
case 2:
/*
* minute (mm)
*/
if ((lt->tm_min = ATOI2(str)) > 59)
return(-1);
break;
default:
return(-1);
}
/*
* convert broken-down time to GMT clock time seconds
*/
if ((*tval = mktime(lt)) == -1)
return(-1);
return(0);
}

70
commands/pax/sel_subs.h Normal file
View file

@ -0,0 +1,70 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)sel_subs.h 8.1 (Berkeley) 5/31/93
* $FreeBSD: src/bin/pax/sel_subs.h,v 1.6 2004/04/06 20:06:48 markm Exp $
*/
/*
* data structure for storing uid/grp selects (-U, -G non standard options)
*/
#define USR_TB_SZ 317 /* user selection table size */
#define GRP_TB_SZ 317 /* user selection table size */
typedef struct usrt {
uid_t uid;
struct usrt *fow; /* next uid */
} USRT;
typedef struct grpt {
gid_t gid;
struct grpt *fow; /* next gid */
} GRPT;
/*
* data structure for storing user supplied time ranges (-T option)
*/
#define ATOI2(s) ((((s)[0] - '0') * 10) + ((s)[1] - '0'))
typedef struct time_rng {
time_t low_time; /* lower inclusive time limit */
time_t high_time; /* higher inclusive time limit */
int flgs; /* option flags */
#define HASLOW 0x01 /* has lower time limit */
#define HASHIGH 0x02 /* has higher time limit */
#define CMPMTME 0x04 /* compare file modification time */
#define CMPCTME 0x08 /* compare inode change time */
#define CMPBOTH (CMPMTME|CMPCTME) /* compare inode and mod time */
struct time_rng *fow; /* next pattern */
} TIME_RNG;

1284
commands/pax/tables.c Normal file

File diff suppressed because it is too large Load diff

169
commands/pax/tables.h Normal file
View file

@ -0,0 +1,169 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tables.h 8.1 (Berkeley) 5/31/93
* $FreeBSD: src/bin/pax/tables.h,v 1.10 2004/04/06 20:06:48 markm Exp $
*/
/*
* data structures and constants used by the different databases kept by pax
*/
/*
* Hash Table Sizes MUST BE PRIME, if set too small performance suffers.
* Probably safe to expect 500000 inodes per tape. Assuming good key
* distribution (inodes) chains of under 50 long (worse case) is ok.
*/
#define L_TAB_SZ 2503 /* hard link hash table size */
#define F_TAB_SZ 50503 /* file time hash table size */
#define N_TAB_SZ 541 /* interactive rename hash table */
#define D_TAB_SZ 317 /* unique device mapping table */
#define A_TAB_SZ 317 /* ftree dir access time reset table */
#define MAXKEYLEN 64 /* max number of chars for hash */
/*
* file hard link structure (hashed by dev/ino and chained) used to find the
* hard links in a file system or with some archive formats (cpio)
*/
typedef struct hrdlnk {
char *name; /* name of first file seen with this ino/dev */
dev_t dev; /* files device number */
ino_t ino; /* files inode number */
u_long nlink; /* expected link count */
struct hrdlnk *fow;
} HRDLNK;
/*
* Archive write update file time table (the -u, -C flag), hashed by filename.
* Filenames are stored in a scratch file at seek offset into the file. The
* file time (mod time) and the file name length (for a quick check) are
* stored in a hash table node. We were forced to use a scratch file because
* with -u, the mtime for every node in the archive must always be available
* to compare against (and this data can get REALLY large with big archives).
* By being careful to read only when we have a good chance of a match, the
* performance loss is not measurable (and the size of the archive we can
* handle is greatly increased).
*/
typedef struct ftm {
int namelen; /* file name length */
time_t mtime; /* files last modification time */
off_t seek; /* location in scratch file */
struct ftm *fow;
} FTM;
/*
* Interactive rename table (-i flag), hashed by orig filename.
* We assume this will not be a large table as this mapping data can only be
* obtained through interactive input by the user. Nobody is going to type in
* changes for 500000 files? We use chaining to resolve collisions.
*/
typedef struct namt {
char *oname; /* old name */
char *nname; /* new name typed in by the user */
struct namt *fow;
} NAMT;
/*
* Unique device mapping tables. Some protocols (e.g. cpio) require that the
* <c_dev,c_ino> pair will uniquely identify a file in an archive unless they
* are links to the same file. Appending to archives can break this. For those
* protocols that have this requirement we map c_dev to a unique value not seen
* in the archive when we append. We also try to handle inode truncation with
* this table. (When the inode field in the archive header are too small, we
* remap the dev on writes to remove accidental collisions).
*
* The list is hashed by device number using chain collision resolution. Off of
* each DEVT are linked the various remaps for this device based on those bits
* in the inode which were truncated. For example if we are just remapping to
* avoid a device number during an update append, off the DEVT we would have
* only a single DLIST that has a truncation id of 0 (no inode bits were
* stripped for this device so far). When we spot inode truncation we create
* a new mapping based on the set of bits in the inode which were stripped off.
* so if the top four bits of the inode are stripped and they have a pattern of
* 0110...... (where . are those bits not truncated) we would have a mapping
* assigned for all inodes that has the same 0110.... pattern (with this dev
* number of course). This keeps the mapping sparse and should be able to store
* close to the limit of files which can be represented by the optimal
* combination of dev and inode bits, and without creating a fouled up archive.
* Note we also remap truncated devs in the same way (an exercise for the
* dedicated reader; always wanted to say that...:)
*/
typedef struct devt {
dev_t dev; /* the orig device number we now have to map */
struct devt *fow; /* new device map list */
struct dlist *list; /* map list based on inode truncation bits */
} DEVT;
typedef struct dlist {
ino_t trunc_bits; /* truncation pattern for a specific map */
dev_t dev; /* the new device id we use */
struct dlist *fow;
} DLIST;
/*
* ftree directory access time reset table. When we are done with with a
* subtree we reset the access and mod time of the directory when the tflag is
* set. Not really explicitly specified in the pax spec, but easy and fast to
* do (and this may have even been intended in the spec, it is not clear).
* table is hashed by inode with chaining.
*/
typedef struct atdir {
char *name; /* name of directory to reset */
dev_t dev; /* dev and inode for fast lookup */
ino_t ino;
time_t mtime; /* access and mod time to reset to */
time_t atime;
struct atdir *fow;
} ATDIR;
/*
* created directory time and mode storage entry. After pax is finished during
* extraction or copy, we must reset directory access modes and times that
* may have been modified after creation (they no longer have the specified
* times and/or modes). We must reset time in the reverse order of creation,
* because entries are added from the top of the file tree to the bottom.
* We MUST reset times from leaf to root (it will not work the other
* direction). Entries are recorded into a spool file to make reverse
* reading faster.
*/
typedef struct dirdata {
int nlen; /* length of the directory name (includes \0) */
off_t npos; /* position in file where this dir name starts */
mode_t mode; /* file mode to restore */
time_t mtime; /* mtime to set */
time_t atime; /* atime to set */
int frc_mode; /* do we force mode settings? */
} DIRDATA;

1121
commands/pax/tar.c Normal file

File diff suppressed because it is too large Load diff

145
commands/pax/tar.h Normal file
View file

@ -0,0 +1,145 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)tar.h 8.2 (Berkeley) 4/18/94
* $FreeBSD: src/bin/pax/tar.h,v 1.7 2004/04/06 20:06:49 markm Exp $
*/
/*
* defines and data structures common to all tar formats
*/
#define CHK_LEN 8 /* length of checksum field */
#define TNMSZ 100 /* size of name field */
#ifdef _PAX_
#define NULLCNT 2 /* number of null blocks in trailer */
#define CHK_OFFSET 148 /* start of checksum field */
#define BLNKSUM 256L /* sum of checksum field using ' ' */
#endif /* _PAX_ */
/*
* Values used in typeflag field in all tar formats
* (only REGTYPE, LNKTYPE and SYMTYPE are used in old BSD tar headers)
*/
#define REGTYPE '0' /* Regular File */
#define AREGTYPE '\0' /* Regular File */
#define LNKTYPE '1' /* Link */
#define SYMTYPE '2' /* Symlink */
#define CHRTYPE '3' /* Character Special File */
#define BLKTYPE '4' /* Block Special File */
#define DIRTYPE '5' /* Directory */
#define FIFOTYPE '6' /* FIFO */
#define CONTTYPE '7' /* high perf file */
/*
* Mode field encoding of the different file types - values in octal
*/
#define TSUID 04000 /* Set UID on execution */
#define TSGID 02000 /* Set GID on execution */
#define TSVTX 01000 /* Reserved */
#define TUREAD 00400 /* Read by owner */
#define TUWRITE 00200 /* Write by owner */
#define TUEXEC 00100 /* Execute/Search by owner */
#define TGREAD 00040 /* Read by group */
#define TGWRITE 00020 /* Write by group */
#define TGEXEC 00010 /* Execute/Search by group */
#define TOREAD 00004 /* Read by other */
#define TOWRITE 00002 /* Write by other */
#define TOEXEC 00001 /* Execute/Search by other */
#ifdef _PAX_
/*
* Pad with a bit mask, much faster than doing a mod but only works on powers
* of 2. Macro below is for block of 512 bytes.
*/
#define TAR_PAD(x) ((512 - ((x) & 511)) & 511)
#endif /* _PAX_ */
/*
* structure of an old tar header as it appeared in BSD releases
*/
typedef struct {
char name[TNMSZ]; /* name of entry */
char mode[8]; /* mode */
char uid[8]; /* uid */
char gid[8]; /* gid */
char size[12]; /* size */
char mtime[12]; /* modification time */
char chksum[CHK_LEN]; /* checksum */
char linkflag; /* norm, hard, or sym. */
char linkname[TNMSZ]; /* linked to name */
} HD_TAR;
#ifdef _PAX_
/*
* -o options for BSD tar to not write directories to the archive
*/
#define TAR_NODIR "nodir"
#define TAR_OPTION "write_opt"
/*
* default device names
*/
#define DEV_0 "/dev/rmt0"
#define DEV_1 "/dev/rmt1"
#define DEV_4 "/dev/rmt4"
#define DEV_5 "/dev/rmt5"
#define DEV_7 "/dev/rmt7"
#define DEV_8 "/dev/rmt8"
#endif /* _PAX_ */
/*
* Data Interchange Format - Extended tar header format - POSIX 1003.1-1990
*/
#define TPFSZ 155
#define TMAGIC "ustar" /* ustar and a null */
#define TMAGLEN 6
#define TVERSION "00" /* 00 and no null */
#define TVERSLEN 2
typedef struct {
char name[TNMSZ]; /* name of entry */
char mode[8]; /* mode */
char uid[8]; /* uid */
char gid[8]; /* gid */
char size[12]; /* size */
char mtime[12]; /* modification time */
char chksum[CHK_LEN]; /* checksum */
char typeflag; /* type of file. */
char linkname[TNMSZ]; /* linked to name */
char magic[TMAGLEN]; /* magic cookie */
char version[TVERSLEN]; /* version */
char uname[32]; /* ascii owner name */
char gname[32]; /* ascii group name */
char devmajor[8]; /* major device number */
char devminor[8]; /* minor device number */
char prefix[TPFSZ]; /* linked to name */
} HD_USTAR;

189
commands/pax/tty_subs.c Normal file
View file

@ -0,0 +1,189 @@
/*-
* Copyright (c) 1992 Keith Muller.
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Keith Muller of the University of California, San Diego.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
#if 0
static char sccsid[] = "@(#)tty_subs.c 8.2 (Berkeley) 4/18/94";
#endif
#endif /* not lint */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "pax.h"
#include "extern.h"
#include <stdarg.h>
/*
* routines that deal with I/O to and from the user
*/
#define DEVTTY "/dev/tty" /* device for interactive i/o */
static FILE *ttyoutf = NULL; /* output pointing at control tty */
static FILE *ttyinf = NULL; /* input pointing at control tty */
/*
* tty_init()
* try to open the controlling terminal (if any) for this process. if the
* open fails, future ops that require user input will get an EOF
*/
int
tty_init(void)
{
int ttyfd;
if ((ttyfd = open(DEVTTY, O_RDWR)) >= 0) {
if ((ttyoutf = fdopen(ttyfd, "w")) != NULL) {
if ((ttyinf = fdopen(ttyfd, "r")) != NULL)
return(0);
(void)fclose(ttyoutf);
}
(void)close(ttyfd);
}
if (iflag) {
paxwarn(1, "Fatal error, cannot open %s", DEVTTY);
return(-1);
}
return(0);
}
/*
* tty_prnt()
* print a message using the specified format to the controlling tty
* if there is no controlling terminal, just return.
*/
void
tty_prnt(const char *fmt, ...)
{
va_list ap;
if (ttyoutf == NULL)
return;
va_start(ap, fmt);
(void)vfprintf(ttyoutf, fmt, ap);
va_end(ap);
(void)fflush(ttyoutf);
}
/*
* tty_read()
* read a string from the controlling terminal if it is open into the
* supplied buffer
* Return:
* 0 if data was read, -1 otherwise.
*/
int
tty_read(char *str, int len)
{
char *pt;
if ((--len <= 0) || (ttyinf == NULL) || (fgets(str,len,ttyinf) == NULL))
return(-1);
*(str + len) = '\0';
/*
* strip off that trailing newline
*/
if ((pt = strchr(str, '\n')) != NULL)
*pt = '\0';
return(0);
}
/*
* paxwarn()
* write a warning message to stderr. if "set" the exit value of pax
* will be non-zero.
*/
void
paxwarn(int set, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (set)
exit_val = 1;
/*
* when vflag we better ship out an extra \n to get this message on a
* line by itself
*/
if (vflag && vfpart) {
(void)fflush(listf);
(void)fputc('\n', stderr);
vfpart = 0;
}
(void)fprintf(stderr, "%s: ", argv0);
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
(void)fputc('\n', stderr);
}
/*
* syswarn()
* write a warning message to stderr. if "set" the exit value of pax
* will be non-zero.
*/
void
syswarn(int set, int errnum, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (set)
exit_val = 1;
/*
* when vflag we better ship out an extra \n to get this message on a
* line by itself
*/
if (vflag && vfpart) {
(void)fflush(listf);
(void)fputc('\n', stderr);
vfpart = 0;
}
(void)fprintf(stderr, "%s: ", argv0);
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
/*
* format and print the errno
*/
if (errnum > 0)
(void)fprintf(stderr, " <%s>", strerror(errnum));
(void)fputc('\n', stderr);
}