diff --git a/bin/pax/Makefile b/bin/pax/Makefile index d0772ee49766..500b6f1f7fd0 100644 --- a/bin/pax/Makefile +++ b/bin/pax/Makefile @@ -1,39 +1,15 @@ # @(#)Makefile 8.1 (Berkeley) 5/31/93 # $FreeBSD$ .include -# To install on versions prior to BSD 4.4 the following may have to be -# defined with CFLAGS += -# -# -DNET2_STAT Use NET2 or older stat structure. The version of the -# stat structure is easily determined by looking at the -# basic type of an off_t (often defined in the file: -# /usr/include/sys/types.h). If off_t is a long (and is -# NOT A quad) then you must define NET2_STAT. -# This define is important, as if you do have a quad_t -# off_t and define NET2_STAT, pax will compile but will -# NOT RUN PROPERLY. -# -# -DNET2_FTS Use the older NET2 fts. To identify the version, -# examine the file: /usr/include/fts.h. If FTS_COMFOLLOW -# is not defined then you must define NET2_FTS. -# Pax may not compile if this not (un)defined properly. -# -# -DNET2_REGEX Use the older regexp.h not regex.h. The regex version -# is determined by looking at the value returned by -# regexec() (man 3 regexec). If regexec return a 1 for -# success (and NOT a 0 for success) you have the older -# regex routines and must define NET2_REGEX. -# Pax may not compile if this not (un)defined properly. - PACKAGE=runtime PROG= pax SRCS= ar_io.c ar_subs.c buf_subs.c cache.c cpio.c file_subs.c ftree.c \ gen_subs.c getoldopt.c options.c pat_rep.c pax.c sel_subs.c \ tables.c tar.c tty_subs.c HAS_TESTS= SUBDIR.${MK_TESTS}+= tests .include diff --git a/bin/pax/ar_io.c b/bin/pax/ar_io.c index 6271944703ff..e00d6f0457f6 100644 --- a/bin/pax/ar_io.c +++ b/bin/pax/ar_io.c @@ -1,1290 +1,1280 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. 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[] = "@(#)ar_io.c 8.2 (Berkeley) 4/18/94"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pax.h" #include "options.h" #include "extern.h" /* * Routines which deal directly with the archive I/O device/file. */ #define DMOD 0666 /* default mode of created archives */ #define EXT_MODE O_RDONLY /* open mode for list/extract */ #define AR_MODE (O_WRONLY | O_CREAT | O_TRUNC) /* mode for archive */ #define APP_MODE O_RDWR /* mode for append */ static char none[] = ""; /* pseudo name for no file */ static char stdo[] = ""; /* pseudo name for stdout */ static char stdn[] = ""; /* pseudo name for stdin */ static int arfd = -1; /* archive file descriptor */ static int artyp = ISREG; /* archive type: file/FIFO/tape */ static int arvol = 1; /* archive volume number */ static int lstrval = -1; /* return value from last i/o */ static int io_ok; /* i/o worked on volume after resync */ static int did_io; /* did i/o ever occur on volume? */ static int done; /* set via tty termination */ static struct stat arsb; /* stat of archive device at open */ static int invld_rec; /* tape has out of spec record size */ static int wr_trail = 1; /* trailer was rewritten in append */ static int can_unlnk = 0; /* do we unlink null archives? */ const char *arcname; /* printable name of archive */ const char *gzip_program; /* name of gzip program */ static pid_t zpid = -1; /* pid of child process */ static int get_phys(void); static void ar_start_gzip(int, const char *, int); /* * ar_open() * Opens the next archive volume. Determines the type of the device and * sets up block sizes as required by the archive device and the format. * Note: we may be called with name == NULL on the first open only. * Return: * -1 on failure, 0 otherwise */ int ar_open(const char *name) { struct mtget mb; if (arfd != -1) (void)close(arfd); arfd = -1; can_unlnk = did_io = io_ok = invld_rec = 0; artyp = ISREG; flcnt = 0; /* * open based on overall operation mode */ switch (act) { case LIST: case EXTRACT: if (name == NULL) { arfd = STDIN_FILENO; arcname = stdn; } else if ((arfd = open(name, EXT_MODE, DMOD)) < 0) syswarn(0, errno, "Failed open to read on %s", name); if (arfd != -1 && gzip_program != NULL) ar_start_gzip(arfd, gzip_program, 0); break; case ARCHIVE: if (name == NULL) { arfd = STDOUT_FILENO; arcname = stdo; } else if ((arfd = open(name, AR_MODE, DMOD)) < 0) syswarn(0, errno, "Failed open to write on %s", name); else can_unlnk = 1; if (arfd != -1 && gzip_program != NULL) ar_start_gzip(arfd, gzip_program, 1); break; case APPND: if (name == NULL) { arfd = STDOUT_FILENO; arcname = stdo; } else if ((arfd = open(name, APP_MODE, DMOD)) < 0) syswarn(0, errno, "Failed open to read/write on %s", name); break; case COPY: /* * arfd not used in COPY mode */ arcname = none; lstrval = 1; return(0); } if (arfd < 0) return(-1); if (chdname != NULL) if (chdir(chdname) != 0) { syswarn(1, errno, "Failed chdir to %s", chdname); return(-1); } /* * set up is based on device type */ if (fstat(arfd, &arsb) < 0) { syswarn(0, errno, "Failed stat on %s", arcname); (void)close(arfd); arfd = -1; can_unlnk = 0; return(-1); } if (S_ISDIR(arsb.st_mode)) { paxwarn(0, "Cannot write an archive on top of a directory %s", arcname); (void)close(arfd); arfd = -1; can_unlnk = 0; return(-1); } if (S_ISCHR(arsb.st_mode)) artyp = ioctl(arfd, MTIOCGET, &mb) ? ISCHR : ISTAPE; else if (S_ISBLK(arsb.st_mode)) artyp = ISBLK; else if ((lseek(arfd, (off_t)0L, SEEK_CUR) == -1) && (errno == ESPIPE)) artyp = ISPIPE; else artyp = ISREG; /* * make sure we beyond any doubt that we only can unlink regular files * we created */ if (artyp != ISREG) can_unlnk = 0; /* * if we are writing, we are done */ if (act == ARCHIVE) { blksz = rdblksz = wrblksz; lstrval = 1; return(0); } /* * set default blksz on read. APPNDs writes rdblksz on the last volume * On all new archive volumes, we shift to wrblksz (if the user * specified one, otherwise we will continue to use rdblksz). We * must to set blocksize based on what kind of device the archive is * stored. */ switch(artyp) { case ISTAPE: /* * Tape drives come in at least two flavors. Those that support * variable sized records and those that have fixed sized * records. They must be treated differently. For tape drives * that support variable sized records, we must make large * reads to make sure we get the entire record, otherwise we * will just get the first part of the record (up to size we * asked). Tapes with fixed sized records may or may not return * multiple records in a single read. We really do not care * what the physical record size is UNLESS we are going to * append. (We will need the physical block size to rewrite * the trailer). Only when we are appending do we go to the * effort to figure out the true PHYSICAL record size. */ blksz = rdblksz = MAXBLK; break; case ISPIPE: case ISBLK: case ISCHR: /* * Blocksize is not a major issue with these devices (but must * be kept a multiple of 512). If the user specified a write * block size, we use that to read. Under append, we must * always keep blksz == rdblksz. Otherwise we go ahead and use * the device optimal blocksize as (and if) returned by stat * and if it is within pax specs. */ if ((act == APPND) && wrblksz) { blksz = rdblksz = wrblksz; break; } if ((arsb.st_blksize > 0) && (arsb.st_blksize < MAXBLK) && ((arsb.st_blksize % BLKMULT) == 0)) rdblksz = arsb.st_blksize; else rdblksz = DEVBLK; /* * For performance go for large reads when we can without harm */ if ((act == APPND) || (artyp == ISCHR)) blksz = rdblksz; else blksz = MAXBLK; break; case ISREG: /* * if the user specified wrblksz works, use it. Under appends * we must always keep blksz == rdblksz */ if ((act == APPND) && wrblksz && ((arsb.st_size%wrblksz)==0)){ blksz = rdblksz = wrblksz; break; } /* * See if we can find the blocking factor from the file size */ for (rdblksz = MAXBLK; rdblksz > 0; rdblksz -= BLKMULT) if ((arsb.st_size % rdblksz) == 0) break; /* * When we cannot find a match, we may have a flawed archive. */ if (rdblksz <= 0) rdblksz = FILEBLK; /* * for performance go for large reads when we can */ if (act == APPND) blksz = rdblksz; else blksz = MAXBLK; break; default: /* * should never happen, worse case, slow... */ blksz = rdblksz = BLKMULT; break; } lstrval = 1; return(0); } /* * ar_close() * closes archive device, increments volume number, and prints i/o summary */ void ar_close(void) { int status; if (arfd < 0) { did_io = io_ok = flcnt = 0; return; } /* * Close archive file. This may take a LONG while on tapes (we may be * forced to wait for the rewind to complete) so tell the user what is * going on (this avoids the user hitting control-c thinking pax is * broken). */ if (vflag && (artyp == ISTAPE)) { if (vfpart) (void)putc('\n', listf); (void)fprintf(listf, "%s: Waiting for tape drive close to complete...", argv0); (void)fflush(listf); } /* * if nothing was written to the archive (and we created it), we remove * it */ if (can_unlnk && (fstat(arfd, &arsb) == 0) && (S_ISREG(arsb.st_mode)) && (arsb.st_size == 0)) { (void)unlink(arcname); can_unlnk = 0; } /* * for a quick extract/list, pax frequently exits before the child * process is done */ if ((act == LIST || act == EXTRACT) && nflag && zpid > 0) kill(zpid, SIGINT); (void)close(arfd); /* Do not exit before child to ensure data integrity */ if (zpid > 0) waitpid(zpid, &status, 0); if (vflag && (artyp == ISTAPE)) { (void)fputs("done.\n", listf); vfpart = 0; (void)fflush(listf); } arfd = -1; if (!io_ok && !did_io) { flcnt = 0; return; } did_io = io_ok = 0; /* * The volume number is only increased when the last device has data * and we have already determined the archive format. */ if (frmt != NULL) ++arvol; if (!vflag) { flcnt = 0; return; } /* * Print out a summary of I/O for this archive volume. */ if (vfpart) { (void)putc('\n', listf); vfpart = 0; } /* * If we have not determined the format yet, we just say how many bytes * we have skipped over looking for a header to id. There is no way we * could have written anything yet. */ if (frmt == NULL) { -# ifdef NET2_STAT - (void)fprintf(listf, "%s: unknown format, %lu bytes skipped.\n", - argv0, rdcnt); -# else (void)fprintf(listf, "%s: unknown format, %ju bytes skipped.\n", argv0, (uintmax_t)rdcnt); -# endif (void)fflush(listf); flcnt = 0; return; } if (strcmp(NM_CPIO, argv0) == 0) (void)fprintf(listf, "%llu blocks\n", (unsigned long long)((rdcnt ? rdcnt : wrcnt) / 5120)); else if (strcmp(NM_TAR, argv0) != 0) (void)fprintf(listf, -# ifdef NET2_STAT - "%s: %s vol %d, %lu files, %lu bytes read, %lu bytes written.\n", - argv0, frmt->name, arvol-1, flcnt, rdcnt, wrcnt); -# else "%s: %s vol %d, %ju files, %ju bytes read, %ju bytes written.\n", argv0, frmt->name, arvol-1, (uintmax_t)flcnt, (uintmax_t)rdcnt, (uintmax_t)wrcnt); -# endif (void)fflush(listf); flcnt = 0; } /* * ar_drain() * drain any archive format independent padding from an archive read * from a socket or a pipe. This is to prevent the process on the * other side of the pipe from getting a SIGPIPE (pax will stop * reading an archive once a format dependent trailer is detected). */ void ar_drain(void) { int res; char drbuf[MAXBLK]; /* * we only drain from a pipe/socket. Other devices can be closed * without reading up to end of file. We sure hope that pipe is closed * on the other side so we will get an EOF. */ if ((artyp != ISPIPE) || (lstrval <= 0)) return; /* * keep reading until pipe is drained */ while ((res = read(arfd, drbuf, sizeof(drbuf))) > 0) ; lstrval = res; } /* * ar_set_wr() * Set up device right before switching from read to write in an append. * device dependent code (if required) to do this should be added here. * For all archive devices we are already positioned at the place we want * to start writing when this routine is called. * Return: * 0 if all ready to write, -1 otherwise */ int ar_set_wr(void) { off_t cpos; /* * we must make sure the trailer is rewritten on append, ar_next() * will stop us if the archive containing the trailer was not written */ wr_trail = 0; /* * Add any device dependent code as required here */ if (artyp != ISREG) return(0); /* * Ok we have an archive in a regular file. If we were rewriting a * file, we must get rid of all the stuff after the current offset * (it was not written by pax). */ if (((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) || (ftruncate(arfd, cpos) < 0)) { syswarn(1, errno, "Unable to truncate archive file"); return(-1); } return(0); } /* * ar_app_ok() * check if the last volume in the archive allows appends. We cannot check * this until we are ready to write since there is no spec that says all * volumes in a single archive have to be of the same type... * Return: * 0 if we can append, -1 otherwise. */ int ar_app_ok(void) { if (artyp == ISPIPE) { paxwarn(1, "Cannot append to an archive obtained from a pipe."); return(-1); } if (!invld_rec) return(0); paxwarn(1,"Cannot append, device record size %d does not support %s spec", rdblksz, argv0); return(-1); } /* * ar_read() * read up to a specified number of bytes from the archive into the * supplied buffer. When dealing with tapes we may not always be able to * read what we want. * Return: * Number of bytes in buffer. 0 for end of file, -1 for a read error. */ int ar_read(char *buf, int cnt) { int res = 0; /* * if last i/o was in error, no more reads until reset or new volume */ if (lstrval <= 0) return(lstrval); /* * how we read must be based on device type */ switch (artyp) { case ISTAPE: if ((res = read(arfd, buf, cnt)) > 0) { /* * CAUTION: tape systems may not always return the same * sized records so we leave blksz == MAXBLK. The * physical record size that a tape drive supports is * very hard to determine in a uniform and portable * manner. */ io_ok = 1; if (res != rdblksz) { /* * Record size changed. If this is happens on * any record after the first, we probably have * a tape drive which has a fixed record size * we are getting multiple records in a single * read). Watch out for record blocking that * violates pax spec (must be a multiple of * BLKMULT). */ rdblksz = res; if (rdblksz % BLKMULT) invld_rec = 1; } return(res); } break; case ISREG: case ISBLK: case ISCHR: case ISPIPE: default: /* * Files are so easy to deal with. These other things cannot * be trusted at all. So when we are dealing with character * devices and pipes we just take what they have ready for us * and return. Trying to do anything else with them runs the * risk of failure. */ if ((res = read(arfd, buf, cnt)) > 0) { io_ok = 1; return(res); } break; } /* * We are in trouble at this point, something is broken... */ lstrval = res; if (res < 0) syswarn(1, errno, "Failed read on archive volume %d", arvol); else paxwarn(0, "End of archive volume %d reached", arvol); return(res); } /* * ar_write() * Write a specified number of bytes in supplied buffer to the archive * device so it appears as a single "block". Deals with errors and tries * to recover when faced with short writes. * Return: * Number of bytes written. 0 indicates end of volume reached and with no * flaws (as best that can be detected). A -1 indicates an unrecoverable * error in the archive occurred. */ int ar_write(char *buf, int bsz) { int res; off_t cpos; /* * do not allow pax to create a "bad" archive. Once a write fails on * an archive volume prevent further writes to it. */ if (lstrval <= 0) return(lstrval); if ((res = write(arfd, buf, bsz)) == bsz) { wr_trail = 1; io_ok = 1; return(bsz); } /* * write broke, see what we can do with it. We try to send any partial * writes that may violate pax spec to the next archive volume. */ if (res < 0) lstrval = res; else lstrval = 0; switch (artyp) { case ISREG: if ((res > 0) && (res % BLKMULT)) { /* * try to fix up partial writes which are not BLKMULT * in size by forcing the runt record to next archive * volume */ if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) break; cpos -= (off_t)res; if (ftruncate(arfd, cpos) < 0) break; res = lstrval = 0; break; } if (res >= 0) break; /* * if file is out of space, handle it like a return of 0 */ if ((errno == ENOSPC) || (errno == EFBIG) || (errno == EDQUOT)) res = lstrval = 0; break; case ISTAPE: case ISCHR: case ISBLK: if (res >= 0) break; if (errno == EACCES) { paxwarn(0, "Write failed, archive is write protected."); res = lstrval = 0; return(0); } /* * see if we reached the end of media, if so force a change to * the next volume */ if ((errno == ENOSPC) || (errno == EIO) || (errno == ENXIO)) res = lstrval = 0; break; case ISPIPE: default: /* * we cannot fix errors to these devices */ break; } /* * Better tell the user the bad news... * if this is a block aligned archive format, we may have a bad archive * if the format wants the header to start at a BLKMULT boundary. While * we can deal with the mis-aligned data, it violates spec and other * archive readers will likely fail. If the format is not block * aligned, the user may be lucky (and the archive is ok). */ if (res >= 0) { if (res > 0) wr_trail = 1; io_ok = 1; } /* * If we were trying to rewrite the trailer and it didn't work, we * must quit right away. */ if (!wr_trail && (res <= 0)) { paxwarn(1,"Unable to append, trailer re-write failed. Quitting."); return(res); } if (res == 0) paxwarn(0, "End of archive volume %d reached", arvol); else if (res < 0) syswarn(1, errno, "Failed write to archive volume: %d", arvol); else if (!frmt->blkalgn || ((res % frmt->blkalgn) == 0)) paxwarn(0,"WARNING: partial archive write. Archive MAY BE FLAWED"); else paxwarn(1,"WARNING: partial archive write. Archive IS FLAWED"); return(res); } /* * ar_rdsync() * Try to move past a bad spot on a flawed archive as needed to continue * I/O. Clears error flags to allow I/O to continue. * Return: * 0 when ok to try i/o again, -1 otherwise. */ int ar_rdsync(void) { long fsbz; off_t cpos; off_t mpos; struct mtop mb; /* * Fail resync attempts at user request (done) or this is going to be * an update/append to an existing archive. If last i/o hit media end, * we need to go to the next volume not try a resync. */ if ((done > 0) || (lstrval == 0)) return(-1); if ((act == APPND) || (act == ARCHIVE)) { paxwarn(1, "Cannot allow updates to an archive with flaws."); return(-1); } if (io_ok) did_io = 1; switch(artyp) { case ISTAPE: /* * if the last i/o was a successful data transfer, we assume * the fault is just a bad record on the tape that we are now * past. If we did not get any data since the last resync try * to move the tape forward one PHYSICAL record past any * damaged tape section. Some tape drives are stubborn and need * to be pushed. */ if (io_ok) { io_ok = 0; lstrval = 1; break; } mb.mt_op = MTFSR; mb.mt_count = 1; if (ioctl(arfd, MTIOCTOP, &mb) < 0) break; lstrval = 1; break; case ISREG: case ISCHR: case ISBLK: /* * try to step over the bad part of the device. */ io_ok = 0; if (((fsbz = arsb.st_blksize) <= 0) || (artyp != ISREG)) fsbz = BLKMULT; if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) break; mpos = fsbz - (cpos % (off_t)fsbz); if (lseek(arfd, mpos, SEEK_CUR) < 0) break; lstrval = 1; break; case ISPIPE: default: /* * cannot recover on these archive device types */ io_ok = 0; break; } if (lstrval <= 0) { paxwarn(1, "Unable to recover from an archive read failure."); return(-1); } paxwarn(0, "Attempting to recover from an archive read failure."); return(0); } /* * ar_fow() * Move the I/O position within the archive forward the specified number of * bytes as supported by the device. If we cannot move the requested * number of bytes, return the actual number of bytes moved in skipped. * Return: * 0 if moved the requested distance, -1 on complete failure, 1 on * partial move (the amount moved is in skipped) */ int ar_fow(off_t sksz, off_t *skipped) { off_t cpos; off_t mpos; *skipped = 0; if (sksz <= 0) return(0); /* * we cannot move forward at EOF or error */ if (lstrval <= 0) return(lstrval); /* * Safer to read forward on devices where it is hard to find the end of * the media without reading to it. With tapes we cannot be sure of the * number of physical blocks to skip (we do not know physical block * size at this point), so we must only read forward on tapes! */ if (artyp != ISREG) return(0); /* * figure out where we are in the archive */ if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) >= 0) { /* * we can be asked to move farther than there are bytes in this * volume, if so, just go to file end and let normal buf_fill() * deal with the end of file (it will go to next volume by * itself) */ if ((mpos = cpos + sksz) > arsb.st_size) { *skipped = arsb.st_size - cpos; mpos = arsb.st_size; } else *skipped = sksz; if (lseek(arfd, mpos, SEEK_SET) >= 0) return(0); } syswarn(1, errno, "Forward positioning operation on archive failed"); lstrval = -1; return(-1); } /* * ar_rev() * move the i/o position within the archive backwards the specified byte * count as supported by the device. With tapes drives we RESET rdblksz to * the PHYSICAL blocksize. * NOTE: We should only be called to move backwards so we can rewrite the * last records (the trailer) of an archive (APPEND). * Return: * 0 if moved the requested distance, -1 on complete failure */ int ar_rev(off_t sksz) { off_t cpos; struct mtop mb; int phyblk; /* * make sure we do not have try to reverse on a flawed archive */ if (lstrval < 0) return(lstrval); switch(artyp) { case ISPIPE: if (sksz <= 0) break; /* * cannot go backwards on these critters */ paxwarn(1, "Reverse positioning on pipes is not supported."); lstrval = -1; return(-1); case ISREG: case ISBLK: case ISCHR: default: if (sksz <= 0) break; /* * For things other than files, backwards movement has a very * high probability of failure as we really do not know the * true attributes of the device we are talking to (the device * may not even have the ability to lseek() in any direction). * First we figure out where we are in the archive. */ if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) { syswarn(1, errno, "Unable to obtain current archive byte offset"); lstrval = -1; return(-1); } /* * we may try to go backwards past the start when the archive * is only a single record. If this happens and we are on a * multi volume archive, we need to go to the end of the * previous volume and continue our movement backwards from * there. */ if ((cpos -= sksz) < (off_t)0L) { if (arvol > 1) { /* * this should never happen */ paxwarn(1,"Reverse position on previous volume."); lstrval = -1; return(-1); } cpos = (off_t)0L; } if (lseek(arfd, cpos, SEEK_SET) < 0) { syswarn(1, errno, "Unable to seek archive backwards"); lstrval = -1; return(-1); } break; case ISTAPE: /* * Calculate and move the proper number of PHYSICAL tape * blocks. If the sksz is not an even multiple of the physical * tape size, we cannot do the move (this should never happen). * (We also cannot handler trailers spread over two vols). * get_phys() also makes sure we are in front of the filemark. */ if ((phyblk = get_phys()) <= 0) { lstrval = -1; return(-1); } /* * make sure future tape reads only go by physical tape block * size (set rdblksz to the real size). */ rdblksz = phyblk; /* * if no movement is required, just return (we must be after * get_phys() so the physical blocksize is properly set) */ if (sksz <= 0) break; /* * ok we have to move. Make sure the tape drive can do it. */ if (sksz % phyblk) { paxwarn(1, "Tape drive unable to backspace requested amount"); lstrval = -1; return(-1); } /* * move backwards the requested number of bytes */ mb.mt_op = MTBSR; mb.mt_count = sksz/phyblk; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1,errno, "Unable to backspace tape %d blocks.", mb.mt_count); lstrval = -1; return(-1); } break; } lstrval = 1; return(0); } /* * get_phys() * Determine the physical block size on a tape drive. We need the physical * block size so we know how many bytes we skip over when we move with * mtio commands. We also make sure we are BEFORE THE TAPE FILEMARK when * return. * This is one really SLOW routine... * Return: * physical block size if ok (ok > 0), -1 otherwise */ static int get_phys(void) { int padsz = 0; int res; int phyblk; struct mtop mb; char scbuf[MAXBLK]; /* * move to the file mark, and then back up one record and read it. * this should tell us the physical record size the tape is using. */ if (lstrval == 1) { /* * we know we are at file mark when we get back a 0 from * read() */ while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0) padsz += res; if (res < 0) { syswarn(1, errno, "Unable to locate tape filemark."); return(-1); } } /* * move backwards over the file mark so we are at the end of the * last record. */ mb.mt_op = MTBSF; mb.mt_count = 1; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1, errno, "Unable to backspace over tape filemark."); return(-1); } /* * move backwards so we are in front of the last record and read it to * get physical tape blocksize. */ mb.mt_op = MTBSR; mb.mt_count = 1; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1, errno, "Unable to backspace over last tape block."); return(-1); } if ((phyblk = read(arfd, scbuf, sizeof(scbuf))) <= 0) { syswarn(1, errno, "Cannot determine archive tape blocksize."); return(-1); } /* * read forward to the file mark, then back up in front of the filemark * (this is a bit paranoid, but should be safe to do). */ while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0) ; if (res < 0) { syswarn(1, errno, "Unable to locate tape filemark."); return(-1); } mb.mt_op = MTBSF; mb.mt_count = 1; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1, errno, "Unable to backspace over tape filemark."); return(-1); } /* * set lstrval so we know that the filemark has not been seen */ lstrval = 1; /* * return if there was no padding */ if (padsz == 0) return(phyblk); /* * make sure we can move backwards over the padding. (this should * never fail). */ if (padsz % phyblk) { paxwarn(1, "Tape drive unable to backspace requested amount"); return(-1); } /* * move backwards over the padding so the head is where it was when * we were first called (if required). */ mb.mt_op = MTBSR; mb.mt_count = padsz/phyblk; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1,errno,"Unable to backspace tape over %d pad blocks", mb.mt_count); return(-1); } return(phyblk); } /* * ar_next() * prompts the user for the next volume in this archive. For some devices * we may allow the media to be changed. Otherwise a new archive is * prompted for. By pax spec, if there is no controlling tty or an eof is * read on tty input, we must quit pax. * Return: * 0 when ready to continue, -1 when all done */ int ar_next(void) { static char *arcbuf; char buf[PAXPATHLEN+2]; sigset_t o_mask; /* * WE MUST CLOSE THE DEVICE. A lot of devices must see last close, (so * things like writing EOF etc will be done) (Watch out ar_close() can * also be called via a signal handler, so we must prevent a race. */ if (sigprocmask(SIG_BLOCK, &s_mask, &o_mask) < 0) syswarn(0, errno, "Unable to set signal mask"); ar_close(); if (sigprocmask(SIG_SETMASK, &o_mask, NULL) < 0) syswarn(0, errno, "Unable to restore signal mask"); if (done || !wr_trail || Oflag || strcmp(NM_TAR, argv0) == 0) return(-1); tty_prnt("\nATTENTION! %s archive volume change required.\n", argv0); /* * if i/o is on stdin or stdout, we cannot reopen it (we do not know * the name), the user will be forced to type it in. */ if (strcmp(arcname, stdo) && strcmp(arcname, stdn) && (artyp != ISREG) && (artyp != ISPIPE)) { if (artyp == ISTAPE) { tty_prnt("%s ready for archive tape volume: %d\n", arcname, arvol); tty_prnt("Load the NEXT TAPE on the tape drive"); } else { tty_prnt("%s ready for archive volume: %d\n", arcname, arvol); tty_prnt("Load the NEXT STORAGE MEDIA (if required)"); } if ((act == ARCHIVE) || (act == APPND)) tty_prnt(" and make sure it is WRITE ENABLED.\n"); else tty_prnt("\n"); for(;;) { tty_prnt("Type \"y\" to continue, \".\" to quit %s,", argv0); tty_prnt(" or \"s\" to switch to new device.\nIf you"); tty_prnt(" cannot change storage media, type \"s\"\n"); tty_prnt("Is the device ready and online? > "); if ((tty_read(buf,sizeof(buf))<0) || !strcmp(buf,".")){ done = 1; lstrval = -1; tty_prnt("Quitting %s!\n", argv0); vfpart = 0; return(-1); } if ((buf[0] == '\0') || (buf[1] != '\0')) { tty_prnt("%s unknown command, try again\n",buf); continue; } switch (buf[0]) { case 'y': case 'Y': /* * we are to continue with the same device */ if (ar_open(arcname) >= 0) return(0); tty_prnt("Cannot re-open %s, try again\n", arcname); continue; case 's': case 'S': /* * user wants to open a different device */ tty_prnt("Switching to a different archive\n"); break; default: tty_prnt("%s unknown command, try again\n",buf); continue; } break; } } else tty_prnt("Ready for archive volume: %d\n", arvol); /* * have to go to a different archive */ for (;;) { tty_prnt("Input archive name or \".\" to quit %s.\n", argv0); tty_prnt("Archive name > "); if ((tty_read(buf, sizeof(buf)) < 0) || !strcmp(buf, ".")) { done = 1; lstrval = -1; tty_prnt("Quitting %s!\n", argv0); vfpart = 0; return(-1); } if (buf[0] == '\0') { tty_prnt("Empty file name, try again\n"); continue; } if (!strcmp(buf, "..")) { tty_prnt("Illegal file name: .. try again\n"); continue; } if (strlen(buf) > PAXPATHLEN) { tty_prnt("File name too long, try again\n"); continue; } /* * try to open new archive */ if (ar_open(buf) >= 0) { free(arcbuf); if ((arcbuf = strdup(buf)) == NULL) { done = 1; lstrval = -1; paxwarn(0, "Cannot save archive name."); return(-1); } arcname = arcbuf; break; } tty_prnt("Cannot open %s, try again\n", buf); continue; } return(0); } /* * ar_start_gzip() * starts the gzip compression/decompression process as a child, using magic * to keep the fd the same in the calling function (parent). */ void ar_start_gzip(int fd, const char *gzip_prog, int wr) { int fds[2]; const char *gzip_flags; if (pipe(fds) < 0) err(1, "could not pipe"); zpid = fork(); if (zpid < 0) err(1, "could not fork"); /* parent */ if (zpid) { if (wr) dup2(fds[1], fd); else dup2(fds[0], fd); close(fds[0]); close(fds[1]); } else { if (wr) { dup2(fds[0], STDIN_FILENO); dup2(fd, STDOUT_FILENO); gzip_flags = "-c"; } else { dup2(fds[1], STDOUT_FILENO); dup2(fd, STDIN_FILENO); gzip_flags = "-dc"; } close(fds[0]); close(fds[1]); if (execlp(gzip_prog, gzip_prog, gzip_flags, (char *)NULL) < 0) err(1, "could not exec"); /* NOTREACHED */ } } diff --git a/bin/pax/cache.c b/bin/pax/cache.c index 980d7bccc217..5e55e70be851 100644 --- a/bin/pax/cache.c +++ b/bin/pax/cache.c @@ -1,433 +1,425 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #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 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 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 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 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); } diff --git a/bin/pax/cpio.c b/bin/pax/cpio.c index a47b7fd7a823..f16162efffe7 100644 --- a/bin/pax/cpio.c +++ b/bin/pax/cpio.c @@ -1,1154 +1,1120 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. 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[] = "@(#)cpio.c 8.1 (Berkeley) 5/31/93"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include "pax.h" #include "cpio.h" #include "extern.h" static int rd_nm(ARCHD *, int); static int rd_ln_nm(ARCHD *); static int com_rd(ARCHD *); /* * Routines which support the different cpio versions */ static int swp_head; /* binary cpio header byte swap */ /* * Routines common to all versions of cpio */ /* * cpio_strd() * Fire up the hard link detection code * Return: * 0 if ok -1 otherwise (the return values of lnk_start()) */ int cpio_strd(void) { return(lnk_start()); } /* * cpio_trail() * Called to determine if a header block is a valid trailer. We are * passed the block, the in_sync flag (which tells us we are in resync * mode; looking for a valid header), and cnt (which starts at zero) * which is used to count the number of empty blocks we have seen so far. * Return: * 0 if a valid trailer, -1 if not a valid trailer, */ int cpio_trail(ARCHD *arcn) { /* * look for trailer id in file we are about to process */ if ((strcmp(arcn->name, TRAILER) == 0) && (arcn->sb.st_size == 0)) return(0); return(-1); } /* * com_rd() * operations common to all cpio read functions. * Return: * 0 */ static int com_rd(ARCHD *arcn) { arcn->skip = 0; arcn->pat = NULL; arcn->org_name = arcn->name; switch(arcn->sb.st_mode & C_IFMT) { case C_ISFIFO: arcn->type = PAX_FIF; break; case C_ISDIR: arcn->type = PAX_DIR; break; case C_ISBLK: arcn->type = PAX_BLK; break; case C_ISCHR: arcn->type = PAX_CHR; break; case C_ISLNK: arcn->type = PAX_SLK; break; case C_ISOCK: arcn->type = PAX_SCK; break; case C_ISCTG: case C_ISREG: default: /* * we have file data, set up skip (pad is set in the format * specific sections) */ arcn->sb.st_mode = (arcn->sb.st_mode & 0xfff) | C_ISREG; arcn->type = PAX_REG; arcn->skip = arcn->sb.st_size; break; } if (chk_lnk(arcn) < 0) return(-1); return(0); } /* * cpio_end_wr() * write the special file with the name trailer in the proper format * Return: * result of the write of the trailer from the cpio specific write func */ int cpio_endwr(void) { ARCHD last; /* * create a trailer request and call the proper format write function */ memset(&last, 0, sizeof(last)); last.nlen = sizeof(TRAILER) - 1; last.type = PAX_REG; last.sb.st_nlink = 1; (void)strcpy(last.name, TRAILER); return((*frmt->wr)(&last)); } /* * rd_nam() * read in the file name which follows the cpio header * Return: * 0 if ok, -1 otherwise */ static int rd_nm(ARCHD *arcn, int nsz) { /* * do not even try bogus values */ if ((nsz == 0) || (nsz > (int)sizeof(arcn->name))) { paxwarn(1, "Cpio file name length %d is out of range", nsz); return(-1); } /* * read the name and make sure it is not empty and is \0 terminated */ if ((rd_wrbuf(arcn->name,nsz) != nsz) || (arcn->name[nsz-1] != '\0') || (arcn->name[0] == '\0')) { paxwarn(1, "Cpio file name in header is corrupted"); return(-1); } return(0); } /* * rd_ln_nm() * read in the link name for a file with links. The link name is stored * like file data (and is NOT \0 terminated!) * Return: * 0 if ok, -1 otherwise */ static int rd_ln_nm(ARCHD *arcn) { /* * check the length specified for bogus values */ if ((arcn->sb.st_size == 0) || ((size_t)arcn->sb.st_size >= sizeof(arcn->ln_name))) { -# ifdef NET2_STAT - paxwarn(1, "Cpio link name length is invalid: %lu", - arcn->sb.st_size); -# else paxwarn(1, "Cpio link name length is invalid: %ju", (uintmax_t)arcn->sb.st_size); -# endif return(-1); } /* * read in the link name and \0 terminate it */ if (rd_wrbuf(arcn->ln_name, (int)arcn->sb.st_size) != (int)arcn->sb.st_size) { paxwarn(1, "Cpio link name read error"); return(-1); } arcn->ln_nlen = arcn->sb.st_size; arcn->ln_name[arcn->ln_nlen] = '\0'; /* * watch out for those empty link names */ if (arcn->ln_name[0] == '\0') { paxwarn(1, "Cpio link name is corrupt"); return(-1); } return(0); } /* * Routines common to the extended byte oriented cpio format */ /* * cpio_id() * determine if a block given to us is a valid extended byte oriented * cpio header * Return: * 0 if a valid header, -1 otherwise */ int cpio_id(char *blk, int size) { if ((size < (int)sizeof(HD_CPIO)) || (strncmp(blk, AMAGIC, sizeof(AMAGIC) - 1) != 0)) return(-1); return(0); } /* * cpio_rd() * determine if a buffer is a byte oriented extended cpio archive entry. * convert and store the values in the ARCHD parameter. * Return: * 0 if a valid header, -1 otherwise. */ int cpio_rd(ARCHD *arcn, char *buf) { int nsz; HD_CPIO *hd; /* * check that this is a valid header, if not return -1 */ if (cpio_id(buf, sizeof(HD_CPIO)) < 0) return(-1); hd = (HD_CPIO *)buf; /* * byte oriented cpio (posix) does not have padding! extract the octal * ascii fields from the header */ arcn->pad = 0L; arcn->sb.st_dev = (dev_t)asc_ul(hd->c_dev, sizeof(hd->c_dev), OCT); arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), OCT); arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), OCT); arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), OCT); arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), OCT); arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink), OCT); arcn->sb.st_rdev = (dev_t)asc_ul(hd->c_rdev, sizeof(hd->c_rdev), OCT); -#ifdef NET2_STAT - arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime, sizeof(hd->c_mtime), - OCT); -#else arcn->sb.st_mtime = (time_t)asc_uqd(hd->c_mtime, sizeof(hd->c_mtime), OCT); -#endif arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; -#ifdef NET2_STAT - arcn->sb.st_size = (off_t)asc_ul(hd->c_filesize,sizeof(hd->c_filesize), - OCT); -#else arcn->sb.st_size = (off_t)asc_uqd(hd->c_filesize,sizeof(hd->c_filesize), OCT); -#endif /* * check name size and if valid, read in the name of this entry (name * follows header in the archive) */ if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),OCT)) < 2) return(-1); arcn->nlen = nsz - 1; if (rd_nm(arcn, nsz) < 0) return(-1); if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) { /* * no link name to read for this file */ arcn->ln_nlen = 0; arcn->ln_name[0] = '\0'; return(com_rd(arcn)); } /* * check link name size and read in the link name. Link names are * stored like file data. */ if (rd_ln_nm(arcn) < 0) return(-1); /* * we have a valid header (with a link) */ return(com_rd(arcn)); } /* * cpio_endrd() * no cleanup needed here, just return size of the trailer (for append) * Return: * size of trailer header in this format */ off_t cpio_endrd(void) { return((off_t)(sizeof(HD_CPIO) + sizeof(TRAILER))); } /* * cpio_stwr() * start up the device mapping table * Return: * 0 if ok, -1 otherwise (what dev_start() returns) */ int cpio_stwr(void) { return(dev_start()); } /* * cpio_wr() * copy the data in the ARCHD to buffer in extended byte oriented cpio * format. * Return * 0 if file has data to be written after the header, 1 if file has NO * data to write after the header, -1 if archive write failed */ int cpio_wr(ARCHD *arcn) { HD_CPIO *hd; int nsz; HD_CPIO hdblk; /* * check and repair truncated device and inode fields in the header */ if (map_dev(arcn, (u_long)CPIO_MASK, (u_long)CPIO_MASK) < 0) return(-1); arcn->pad = 0L; nsz = arcn->nlen + 1; hd = &hdblk; if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) arcn->sb.st_rdev = 0; switch(arcn->type) { case PAX_CTG: case PAX_REG: case PAX_HRG: /* * set data size for file data */ -# ifdef NET2_STAT - if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize, - sizeof(hd->c_filesize), OCT)) { -# else if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize, sizeof(hd->c_filesize), OCT)) { -# endif paxwarn(1,"File is too large for cpio format %s", arcn->org_name); return(1); } break; case PAX_SLK: /* * set data size to hold link name */ if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize, sizeof(hd->c_filesize), OCT)) goto out; break; default: /* * all other file types have no file data */ if (ul_asc((u_long)0, hd->c_filesize, sizeof(hd->c_filesize), OCT)) goto out; break; } /* * copy the values to the header using octal ascii */ if (ul_asc((u_long)MAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) || ul_asc((u_long)arcn->sb.st_dev, hd->c_dev, sizeof(hd->c_dev), OCT) || ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino), OCT) || ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode), OCT) || ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid), OCT) || ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid), OCT) || ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink), OCT) || ul_asc((u_long)arcn->sb.st_rdev, hd->c_rdev, sizeof(hd->c_rdev), OCT) || ul_asc((u_long)arcn->sb.st_mtime,hd->c_mtime,sizeof(hd->c_mtime), OCT) || ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), OCT)) goto out; /* * write the file name to the archive */ if ((wr_rdbuf((char *)&hdblk, (int)sizeof(HD_CPIO)) < 0) || (wr_rdbuf(arcn->name, nsz) < 0)) { paxwarn(1, "Unable to write cpio header for %s", arcn->org_name); return(-1); } /* * if this file has data, we are done. The caller will write the file * data, if we are link tell caller we are done, go to next file */ if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || (arcn->type == PAX_HRG)) return(0); if (arcn->type != PAX_SLK) return(1); /* * write the link name to the archive, tell the caller to go to the * next file as we are done. */ if (wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) { paxwarn(1,"Unable to write cpio link name for %s",arcn->org_name); return(-1); } return(1); out: /* * header field is out of range */ paxwarn(1, "Cpio header field is too small to store file %s", arcn->org_name); return(1); } /* * Routines common to the system VR4 version of cpio (with/without file CRC) */ /* * vcpio_id() * determine if a block given to us is a valid system VR4 cpio header * WITHOUT crc. WATCH it the magic cookies are in OCTAL, the header * uses HEX * Return: * 0 if a valid header, -1 otherwise */ int vcpio_id(char *blk, int size) { if ((size < (int)sizeof(HD_VCPIO)) || (strncmp(blk, AVMAGIC, sizeof(AVMAGIC) - 1) != 0)) return(-1); return(0); } /* * crc_id() * determine if a block given to us is a valid system VR4 cpio header * WITH crc. WATCH it the magic cookies are in OCTAL the header uses HEX * Return: * 0 if a valid header, -1 otherwise */ int crc_id(char *blk, int size) { if ((size < (int)sizeof(HD_VCPIO)) || (strncmp(blk, AVCMAGIC, (int)sizeof(AVCMAGIC) - 1) != 0)) return(-1); return(0); } /* * crc_strd() w set file data CRC calculations. Fire up the hard link detection code * Return: * 0 if ok -1 otherwise (the return values of lnk_start()) */ int crc_strd(void) { docrc = 1; return(lnk_start()); } /* * vcpio_rd() * determine if a buffer is a system VR4 archive entry. (with/without CRC) * convert and store the values in the ARCHD parameter. * Return: * 0 if a valid header, -1 otherwise. */ int vcpio_rd(ARCHD *arcn, char *buf) { HD_VCPIO *hd; dev_t devminor; dev_t devmajor; int nsz; /* * during the id phase it was determined if we were using CRC, use the * proper id routine. */ if (docrc) { if (crc_id(buf, sizeof(HD_VCPIO)) < 0) return(-1); } else { if (vcpio_id(buf, sizeof(HD_VCPIO)) < 0) return(-1); } hd = (HD_VCPIO *)buf; arcn->pad = 0L; /* * extract the hex ascii fields from the header */ arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), HEX); arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), HEX); arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), HEX); arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), HEX); -#ifdef NET2_STAT - arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime,sizeof(hd->c_mtime),HEX); -#else arcn->sb.st_mtime = (time_t)asc_uqd(hd->c_mtime,sizeof(hd->c_mtime),HEX); -#endif arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; -#ifdef NET2_STAT - arcn->sb.st_size = (off_t)asc_ul(hd->c_filesize, - sizeof(hd->c_filesize), HEX); -#else arcn->sb.st_size = (off_t)asc_uqd(hd->c_filesize, sizeof(hd->c_filesize), HEX); -#endif arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink), HEX); devmajor = (dev_t)asc_ul(hd->c_maj, sizeof(hd->c_maj), HEX); devminor = (dev_t)asc_ul(hd->c_min, sizeof(hd->c_min), HEX); arcn->sb.st_dev = TODEV(devmajor, devminor); devmajor = (dev_t)asc_ul(hd->c_rmaj, sizeof(hd->c_maj), HEX); devminor = (dev_t)asc_ul(hd->c_rmin, sizeof(hd->c_min), HEX); arcn->sb.st_rdev = TODEV(devmajor, devminor); arcn->crc = asc_ul(hd->c_chksum, sizeof(hd->c_chksum), HEX); /* * check the length of the file name, if ok read it in, return -1 if * bogus */ if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),HEX)) < 2) return(-1); arcn->nlen = nsz - 1; if (rd_nm(arcn, nsz) < 0) return(-1); /* * skip padding. header + filename is aligned to 4 byte boundaries */ if (rd_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0) return(-1); /* * if not a link (or a file with no data), calculate pad size (for * padding which follows the file data), clear the link name and return */ if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) { /* * we have a valid header (not a link) */ arcn->ln_nlen = 0; arcn->ln_name[0] = '\0'; arcn->pad = VCPIO_PAD(arcn->sb.st_size); return(com_rd(arcn)); } /* * read in the link name and skip over the padding */ if ((rd_ln_nm(arcn) < 0) || (rd_skip((off_t)(VCPIO_PAD(arcn->sb.st_size))) < 0)) return(-1); /* * we have a valid header (with a link) */ return(com_rd(arcn)); } /* * vcpio_endrd() * no cleanup needed here, just return size of the trailer (for append) * Return: * size of trailer header in this format */ off_t vcpio_endrd(void) { return((off_t)(sizeof(HD_VCPIO) + sizeof(TRAILER) + (VCPIO_PAD(sizeof(HD_VCPIO) + sizeof(TRAILER))))); } /* * crc_stwr() * start up the device mapping table, enable crc file calculation * Return: * 0 if ok, -1 otherwise (what dev_start() returns) */ int crc_stwr(void) { docrc = 1; return(dev_start()); } /* * vcpio_wr() * copy the data in the ARCHD to buffer in system VR4 cpio * (with/without crc) format. * Return * 0 if file has data to be written after the header, 1 if file has * NO data to write after the header, -1 if archive write failed */ int vcpio_wr(ARCHD *arcn) { HD_VCPIO *hd; unsigned int nsz; HD_VCPIO hdblk; /* * check and repair truncated device and inode fields in the cpio * header */ if (map_dev(arcn, (u_long)VCPIO_MASK, (u_long)VCPIO_MASK) < 0) return(-1); nsz = arcn->nlen + 1; hd = &hdblk; if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) arcn->sb.st_rdev = 0; /* * add the proper magic value depending whether we were asked for * file data crc's, and the crc if needed. */ if (docrc) { if (ul_asc((u_long)VCMAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) || ul_asc((u_long)arcn->crc,hd->c_chksum,sizeof(hd->c_chksum), HEX)) goto out; } else { if (ul_asc((u_long)VMAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) || ul_asc((u_long)0L, hd->c_chksum, sizeof(hd->c_chksum),HEX)) goto out; } switch(arcn->type) { case PAX_CTG: case PAX_REG: case PAX_HRG: /* * caller will copy file data to the archive. tell him how * much to pad. */ arcn->pad = VCPIO_PAD(arcn->sb.st_size); -# ifdef NET2_STAT - if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize, - sizeof(hd->c_filesize), HEX)) { -# else if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize, sizeof(hd->c_filesize), HEX)) { -# endif paxwarn(1,"File is too large for sv4cpio format %s", arcn->org_name); return(1); } break; case PAX_SLK: /* * no file data for the caller to process, the file data has * the size of the link */ arcn->pad = 0L; if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize, sizeof(hd->c_filesize), HEX)) goto out; break; default: /* * no file data for the caller to process */ arcn->pad = 0L; if (ul_asc((u_long)0L, hd->c_filesize, sizeof(hd->c_filesize), HEX)) goto out; break; } /* * set the other fields in the header */ if (ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino), HEX) || ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode), HEX) || ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid), HEX) || ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid), HEX) || ul_asc((u_long)arcn->sb.st_mtime, hd->c_mtime, sizeof(hd->c_mtime), HEX) || ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink), HEX) || ul_asc((u_long)MAJOR(arcn->sb.st_dev),hd->c_maj, sizeof(hd->c_maj), HEX) || ul_asc((u_long)MINOR(arcn->sb.st_dev),hd->c_min, sizeof(hd->c_min), HEX) || ul_asc((u_long)MAJOR(arcn->sb.st_rdev),hd->c_rmaj,sizeof(hd->c_maj), HEX) || ul_asc((u_long)MINOR(arcn->sb.st_rdev),hd->c_rmin,sizeof(hd->c_min), HEX) || ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), HEX)) goto out; /* * write the header, the file name and padding as required. */ if ((wr_rdbuf((char *)&hdblk, (int)sizeof(HD_VCPIO)) < 0) || (wr_rdbuf(arcn->name, (int)nsz) < 0) || (wr_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0)) { paxwarn(1,"Could not write sv4cpio header for %s",arcn->org_name); return(-1); } /* * if we have file data, tell the caller we are done, copy the file */ if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || (arcn->type == PAX_HRG)) return(0); /* * if we are not a link, tell the caller we are done, go to next file */ if (arcn->type != PAX_SLK) return(1); /* * write the link name, tell the caller we are done. */ if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) || (wr_skip((off_t)(VCPIO_PAD(arcn->ln_nlen))) < 0)) { paxwarn(1,"Could not write sv4cpio link name for %s", arcn->org_name); return(-1); } return(1); out: /* * header field is out of range */ paxwarn(1,"Sv4cpio header field is too small for file %s",arcn->org_name); return(1); } /* * Routines common to the old binary header cpio */ /* * bcpio_id() * determine if a block given to us is an old binary cpio header * (with/without header byte swapping) * Return: * 0 if a valid header, -1 otherwise */ int bcpio_id(char *blk, int size) { if (size < (int)sizeof(HD_BCPIO)) return(-1); /* * check both normal and byte swapped magic cookies */ if (((u_short)SHRT_EXT(blk)) == MAGIC) return(0); if (((u_short)RSHRT_EXT(blk)) == MAGIC) { if (!swp_head) ++swp_head; return(0); } return(-1); } /* * bcpio_rd() * determine if a buffer is an old binary archive entry. (It may have byte * swapped header) convert and store the values in the ARCHD parameter. * This is a very old header format and should not really be used. * Return: * 0 if a valid header, -1 otherwise. */ int bcpio_rd(ARCHD *arcn, char *buf) { HD_BCPIO *hd; int nsz; /* * check the header */ if (bcpio_id(buf, sizeof(HD_BCPIO)) < 0) return(-1); arcn->pad = 0L; hd = (HD_BCPIO *)buf; if (swp_head) { /* * header has swapped bytes on 16 bit boundaries */ arcn->sb.st_dev = (dev_t)(RSHRT_EXT(hd->h_dev)); arcn->sb.st_ino = (ino_t)(RSHRT_EXT(hd->h_ino)); arcn->sb.st_mode = (mode_t)(RSHRT_EXT(hd->h_mode)); arcn->sb.st_uid = (uid_t)(RSHRT_EXT(hd->h_uid)); arcn->sb.st_gid = (gid_t)(RSHRT_EXT(hd->h_gid)); arcn->sb.st_nlink = (nlink_t)(RSHRT_EXT(hd->h_nlink)); arcn->sb.st_rdev = (dev_t)(RSHRT_EXT(hd->h_rdev)); arcn->sb.st_mtime = (time_t)(RSHRT_EXT(hd->h_mtime_1)); arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) | ((time_t)(RSHRT_EXT(hd->h_mtime_2))); arcn->sb.st_size = (off_t)(RSHRT_EXT(hd->h_filesize_1)); arcn->sb.st_size = (arcn->sb.st_size << 16) | ((off_t)(RSHRT_EXT(hd->h_filesize_2))); nsz = (int)(RSHRT_EXT(hd->h_namesize)); } else { arcn->sb.st_dev = (dev_t)(SHRT_EXT(hd->h_dev)); arcn->sb.st_ino = (ino_t)(SHRT_EXT(hd->h_ino)); arcn->sb.st_mode = (mode_t)(SHRT_EXT(hd->h_mode)); arcn->sb.st_uid = (uid_t)(SHRT_EXT(hd->h_uid)); arcn->sb.st_gid = (gid_t)(SHRT_EXT(hd->h_gid)); arcn->sb.st_nlink = (nlink_t)(SHRT_EXT(hd->h_nlink)); arcn->sb.st_rdev = (dev_t)(SHRT_EXT(hd->h_rdev)); arcn->sb.st_mtime = (time_t)(SHRT_EXT(hd->h_mtime_1)); arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) | ((time_t)(SHRT_EXT(hd->h_mtime_2))); arcn->sb.st_size = (off_t)(SHRT_EXT(hd->h_filesize_1)); arcn->sb.st_size = (arcn->sb.st_size << 16) | ((off_t)(SHRT_EXT(hd->h_filesize_2))); nsz = (int)(SHRT_EXT(hd->h_namesize)); } arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; /* * check the file name size, if bogus give up. otherwise read the file * name */ if (nsz < 2) return(-1); arcn->nlen = nsz - 1; if (rd_nm(arcn, nsz) < 0) return(-1); /* * header + file name are aligned to 2 byte boundaries, skip if needed */ if (rd_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0) return(-1); /* * if not a link (or a file with no data), calculate pad size (for * padding which follows the file data), clear the link name and return */ if (((arcn->sb.st_mode & C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)){ /* * we have a valid header (not a link) */ arcn->ln_nlen = 0; arcn->ln_name[0] = '\0'; arcn->pad = BCPIO_PAD(arcn->sb.st_size); return(com_rd(arcn)); } if ((rd_ln_nm(arcn) < 0) || (rd_skip((off_t)(BCPIO_PAD(arcn->sb.st_size))) < 0)) return(-1); /* * we have a valid header (with a link) */ return(com_rd(arcn)); } /* * bcpio_endrd() * no cleanup needed here, just return size of the trailer (for append) * Return: * size of trailer header in this format */ off_t bcpio_endrd(void) { return((off_t)(sizeof(HD_BCPIO) + sizeof(TRAILER) + (BCPIO_PAD(sizeof(HD_BCPIO) + sizeof(TRAILER))))); } /* * bcpio_wr() * copy the data in the ARCHD to buffer in old binary cpio format * There is a real chance of field overflow with this critter. So we * always check that the conversion is ok. nobody in their right mind * should write an archive in this format... * Return * 0 if file has data to be written after the header, 1 if file has NO * data to write after the header, -1 if archive write failed */ int bcpio_wr(ARCHD *arcn) { HD_BCPIO *hd; int nsz; HD_BCPIO hdblk; off_t t_offt; int t_int; time_t t_timet; /* * check and repair truncated device and inode fields in the cpio * header */ if (map_dev(arcn, (u_long)BCPIO_MASK, (u_long)BCPIO_MASK) < 0) return(-1); if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) arcn->sb.st_rdev = 0; hd = &hdblk; switch(arcn->type) { case PAX_CTG: case PAX_REG: case PAX_HRG: /* * caller will copy file data to the archive. tell him how * much to pad. */ arcn->pad = BCPIO_PAD(arcn->sb.st_size); hd->h_filesize_1[0] = CHR_WR_0(arcn->sb.st_size); hd->h_filesize_1[1] = CHR_WR_1(arcn->sb.st_size); hd->h_filesize_2[0] = CHR_WR_2(arcn->sb.st_size); hd->h_filesize_2[1] = CHR_WR_3(arcn->sb.st_size); t_offt = (off_t)(SHRT_EXT(hd->h_filesize_1)); t_offt = (t_offt<<16) | ((off_t)(SHRT_EXT(hd->h_filesize_2))); if (arcn->sb.st_size != t_offt) { paxwarn(1,"File is too large for bcpio format %s", arcn->org_name); return(1); } break; case PAX_SLK: /* * no file data for the caller to process, the file data has * the size of the link */ arcn->pad = 0L; hd->h_filesize_1[0] = CHR_WR_0(arcn->ln_nlen); hd->h_filesize_1[1] = CHR_WR_1(arcn->ln_nlen); hd->h_filesize_2[0] = CHR_WR_2(arcn->ln_nlen); hd->h_filesize_2[1] = CHR_WR_3(arcn->ln_nlen); t_int = (int)(SHRT_EXT(hd->h_filesize_1)); t_int = (t_int << 16) | ((int)(SHRT_EXT(hd->h_filesize_2))); if (arcn->ln_nlen != t_int) goto out; break; default: /* * no file data for the caller to process */ arcn->pad = 0L; hd->h_filesize_1[0] = (char)0; hd->h_filesize_1[1] = (char)0; hd->h_filesize_2[0] = (char)0; hd->h_filesize_2[1] = (char)0; break; } /* * build up the rest of the fields */ hd->h_magic[0] = CHR_WR_2(MAGIC); hd->h_magic[1] = CHR_WR_3(MAGIC); hd->h_dev[0] = CHR_WR_2(arcn->sb.st_dev); hd->h_dev[1] = CHR_WR_3(arcn->sb.st_dev); if (arcn->sb.st_dev != (dev_t)(SHRT_EXT(hd->h_dev))) goto out; hd->h_ino[0] = CHR_WR_2(arcn->sb.st_ino); hd->h_ino[1] = CHR_WR_3(arcn->sb.st_ino); if (arcn->sb.st_ino != (ino_t)(SHRT_EXT(hd->h_ino))) goto out; hd->h_mode[0] = CHR_WR_2(arcn->sb.st_mode); hd->h_mode[1] = CHR_WR_3(arcn->sb.st_mode); if (arcn->sb.st_mode != (mode_t)(SHRT_EXT(hd->h_mode))) goto out; hd->h_uid[0] = CHR_WR_2(arcn->sb.st_uid); hd->h_uid[1] = CHR_WR_3(arcn->sb.st_uid); if (arcn->sb.st_uid != (uid_t)(SHRT_EXT(hd->h_uid))) goto out; hd->h_gid[0] = CHR_WR_2(arcn->sb.st_gid); hd->h_gid[1] = CHR_WR_3(arcn->sb.st_gid); if (arcn->sb.st_gid != (gid_t)(SHRT_EXT(hd->h_gid))) goto out; hd->h_nlink[0] = CHR_WR_2(arcn->sb.st_nlink); hd->h_nlink[1] = CHR_WR_3(arcn->sb.st_nlink); if (arcn->sb.st_nlink != (nlink_t)(SHRT_EXT(hd->h_nlink))) goto out; hd->h_rdev[0] = CHR_WR_2(arcn->sb.st_rdev); hd->h_rdev[1] = CHR_WR_3(arcn->sb.st_rdev); if (arcn->sb.st_rdev != (dev_t)(SHRT_EXT(hd->h_rdev))) goto out; hd->h_mtime_1[0] = CHR_WR_0(arcn->sb.st_mtime); hd->h_mtime_1[1] = CHR_WR_1(arcn->sb.st_mtime); hd->h_mtime_2[0] = CHR_WR_2(arcn->sb.st_mtime); hd->h_mtime_2[1] = CHR_WR_3(arcn->sb.st_mtime); t_timet = (time_t)(SHRT_EXT(hd->h_mtime_1)); t_timet = (t_timet << 16) | ((time_t)(SHRT_EXT(hd->h_mtime_2))); if (arcn->sb.st_mtime != t_timet) goto out; nsz = arcn->nlen + 1; hd->h_namesize[0] = CHR_WR_2(nsz); hd->h_namesize[1] = CHR_WR_3(nsz); if (nsz != (int)(SHRT_EXT(hd->h_namesize))) goto out; /* * write the header, the file name and padding as required. */ if ((wr_rdbuf((char *)&hdblk, (int)sizeof(HD_BCPIO)) < 0) || (wr_rdbuf(arcn->name, nsz) < 0) || (wr_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0)) { paxwarn(1, "Could not write bcpio header for %s", arcn->org_name); return(-1); } /* * if we have file data, tell the caller we are done */ if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || (arcn->type == PAX_HRG)) return(0); /* * if we are not a link, tell the caller we are done, go to next file */ if (arcn->type != PAX_SLK) return(1); /* * write the link name, tell the caller we are done. */ if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) || (wr_skip((off_t)(BCPIO_PAD(arcn->ln_nlen))) < 0)) { paxwarn(1,"Could not write bcpio link name for %s",arcn->org_name); return(-1); } return(1); out: /* * header field is out of range */ paxwarn(1,"Bcpio header field is too small for file %s", arcn->org_name); return(1); } diff --git a/bin/pax/extern.h b/bin/pax/extern.h index ec171f2d177c..12b7fe162a6e 100644 --- a/bin/pax/extern.h +++ b/bin/pax/extern.h @@ -1,299 +1,297 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. 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$ */ /* * External references from each source file */ #include /* * 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); extern 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 Oflag; 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 sigset_t s_mask; 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 *, ...) __printflike(1, 2); int tty_read(char *, int); void paxwarn(int, const char *, ...) __printflike(2, 3); void syswarn(int, int, const char *, ...) __printflike(3, 4); diff --git a/bin/pax/ftree.c b/bin/pax/ftree.c index 26df8164bc2d..da0d72998975 100644 --- a/bin/pax/ftree.c +++ b/bin/pax/ftree.c @@ -1,537 +1,512 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #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 symlinks (logical) * -P do not follow symlinks (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(void) { 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; 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; 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); } diff --git a/bin/pax/gen_subs.c b/bin/pax/gen_subs.c index f91aaff7b809..b8cb437405a7 100644 --- a/bin/pax/gen_subs.c +++ b/bin/pax/gen_subs.c @@ -1,399 +1,388 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #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" 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 = (*nl_langinfo(D_MD_ORDER) == 'd'); /* * user wants long mode */ sbp = &(arcn->sb); strmode(sbp->st_mode, f_mode); /* * 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 (strftime(f_date,DATELEN,timefrmt,localtime(&(sbp->st_mtime))) == 0) f_date[0] = '\0'; (void)fprintf(fp, "%s%2ju %-12s %-12s ", f_mode, (uintmax_t)sbp->st_nlink, name_uid(sbp->st_uid, 1), 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 = (*nl_langinfo(D_MD_ORDER) == 'd'); 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 (strftime(f_date, DATELEN, timefrmt, localtime(&(arcn->sb.st_mtime))) == 0) f_date[0] = '\0'; strmode(arcn->sb.st_mode, f_mode); 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 diff --git a/bin/pax/options.c b/bin/pax/options.c index 4ec02d80d55c..280a52a86d6c 100644 --- a/bin/pax/options.c +++ b/bin/pax/options.c @@ -1,1592 +1,1587 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. 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 sccsid[] = "@(#)options.c 8.2 (Berkeley) 4/18/94"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "pax.h" #include "options.h" #include "cpio.h" #include "tar.h" #include "extern.h" /* * Routines which handle command line options */ static char flgch[] = FLGCH; /* list of all possible flags */ static OPLIST *ophead = NULL; /* head for format specific options -x */ static OPLIST *optail = NULL; /* option tail */ static int no_op(void); static void printflg(unsigned int); static int c_frmt(const void *, const void *); static off_t str_offt(char *); static char *get_line(FILE *fp); static void pax_options(int, char **); static void pax_usage(void); static void tar_options(int, char **); static void tar_usage(void); static void cpio_options(int, char **); static void cpio_usage(void); /* errors from get_line */ #define GETLINE_FILE_CORRUPT 1 #define GETLINE_OUT_OF_MEM 2 static int get_line_error; char *chdname; #define GZIP_CMD "gzip" /* command to run as gzip */ #define COMPRESS_CMD "compress" /* command to run as compress */ #define BZIP2_CMD "bzip2" /* command to run as gzip */ /* * Format specific routine table - MUST BE IN SORTED ORDER BY NAME * (see pax.h for description of each function) * * name, blksz, hdsz, udev, hlk, blkagn, inhead, id, st_read, * read, end_read, st_write, write, end_write, trail, * rd_data, wr_data, options */ FSUB fsub[] = { /* 0: OLD BINARY CPIO */ {"bcpio", 5120, sizeof(HD_BCPIO), 1, 0, 0, 1, bcpio_id, cpio_strd, bcpio_rd, bcpio_endrd, cpio_stwr, bcpio_wr, cpio_endwr, cpio_trail, NULL, rd_wrfile, wr_rdfile, bad_opt}, /* 1: OLD OCTAL CHARACTER CPIO */ {"cpio", 5120, sizeof(HD_CPIO), 1, 0, 0, 1, cpio_id, cpio_strd, cpio_rd, cpio_endrd, cpio_stwr, cpio_wr, cpio_endwr, cpio_trail, NULL, rd_wrfile, wr_rdfile, bad_opt}, /* 2: SVR4 HEX CPIO */ {"sv4cpio", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, vcpio_id, cpio_strd, vcpio_rd, vcpio_endrd, cpio_stwr, vcpio_wr, cpio_endwr, cpio_trail, NULL, rd_wrfile, wr_rdfile, bad_opt}, /* 3: SVR4 HEX CPIO WITH CRC */ {"sv4crc", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, crc_id, crc_strd, vcpio_rd, vcpio_endrd, crc_stwr, vcpio_wr, cpio_endwr, cpio_trail, NULL, rd_wrfile, wr_rdfile, bad_opt}, /* 4: OLD TAR */ {"tar", 10240, BLKMULT, 0, 1, BLKMULT, 0, tar_id, no_op, tar_rd, tar_endrd, no_op, tar_wr, tar_endwr, NULL, tar_trail, rd_wrfile, wr_rdfile, tar_opt}, /* 5: POSIX USTAR */ {"ustar", 10240, BLKMULT, 0, 1, BLKMULT, 0, ustar_id, ustar_strd, ustar_rd, tar_endrd, ustar_stwr, ustar_wr, tar_endwr, NULL, tar_trail, rd_wrfile, wr_rdfile, bad_opt}, }; #define F_OCPIO 0 /* format when called as cpio -6 */ #define F_ACPIO 1 /* format when called as cpio -c */ #define F_CPIO 3 /* format when called as cpio */ #define F_OTAR 4 /* format when called as tar -o */ #define F_TAR 5 /* format when called as tar */ #define DEFLT 5 /* default write format from list above */ /* * ford is the archive search order used by get_arc() to determine what kind * of archive we are dealing with. This helps to properly id archive formats * some formats may be subsets of others.... */ int ford[] = {5, 4, 3, 2, 1, 0, -1 }; /* * options() * figure out if we are pax, tar or cpio. Call the appropriate options * parser */ void options(int argc, char **argv) { /* * Are we acting like pax, tar or cpio (based on argv[0]) */ if ((argv0 = strrchr(argv[0], '/')) != NULL) argv0++; else argv0 = argv[0]; if (strcmp(NM_TAR, argv0) == 0) { tar_options(argc, argv); return; } else if (strcmp(NM_CPIO, argv0) == 0) { cpio_options(argc, argv); return; } /* * assume pax as the default */ argv0 = NM_PAX; pax_options(argc, argv); return; } /* * pax_options() * look at the user specified flags. set globals as required and check if * the user specified a legal set of flags. If not, complain and exit */ static void pax_options(int argc, char **argv) { int c; size_t i; unsigned int flg = 0; unsigned int bflg = 0; char *pt; FSUB tmp; /* * process option flags */ while ((c=getopt(argc,argv,"ab:cdf:iklno:p:rs:tuvwx:zB:DE:G:HLOPT:U:XYZ")) != -1) { switch (c) { case 'a': /* * append */ flg |= AF; break; case 'b': /* * specify blocksize */ flg |= BF; if ((wrblksz = (int)str_offt(optarg)) <= 0) { paxwarn(1, "Invalid block size %s", optarg); pax_usage(); } break; case 'c': /* * inverse match on patterns */ cflag = 1; flg |= CF; break; case 'd': /* * match only dir on extract, not the subtree at dir */ dflag = 1; flg |= DF; break; case 'f': /* * filename where the archive is stored */ arcname = optarg; flg |= FF; break; case 'i': /* * interactive file rename */ iflag = 1; flg |= IF; break; case 'k': /* * do not clobber files that exist */ kflag = 1; flg |= KF; break; case 'l': /* * try to link src to dest with copy (-rw) */ lflag = 1; flg |= LF; break; case 'n': /* * select first match for a pattern only */ nflag = 1; flg |= NF; break; case 'o': /* * pass format specific options */ flg |= OF; if (opt_add(optarg) < 0) pax_usage(); break; case 'p': /* * specify file characteristic options */ for (pt = optarg; *pt != '\0'; ++pt) { switch(*pt) { case 'a': /* * do not preserve access time */ patime = 0; break; case 'e': /* * preserve user id, group id, file * mode, access/modification times */ pids = 1; pmode = 1; patime = 1; pmtime = 1; break; case 'm': /* * do not preserve modification time */ pmtime = 0; break; case 'o': /* * preserve uid/gid */ pids = 1; break; case 'p': /* * preserver file mode bits */ pmode = 1; break; default: paxwarn(1, "Invalid -p string: %c", *pt); pax_usage(); break; } } flg |= PF; break; case 'r': /* * read the archive */ flg |= RF; break; case 's': /* * file name substitution name pattern */ if (rep_add(optarg) < 0) { pax_usage(); break; } flg |= SF; break; case 't': /* * preserve access time on file system nodes we read */ tflag = 1; flg |= TF; break; case 'u': /* * ignore those older files */ uflag = 1; flg |= UF; break; case 'v': /* * verbose operation mode */ vflag = 1; flg |= VF; break; case 'w': /* * write an archive */ flg |= WF; break; case 'x': /* * specify an archive format on write */ tmp.name = optarg; if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub, sizeof(fsub)/sizeof(FSUB), sizeof(FSUB), c_frmt)) != NULL) { flg |= XF; break; } paxwarn(1, "Unknown -x format: %s", optarg); (void)fputs("pax: Known -x formats are:", stderr); for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i) (void)fprintf(stderr, " %s", fsub[i].name); (void)fputs("\n\n", stderr); pax_usage(); break; case 'z': /* * use gzip. Non standard option. */ gzip_program = GZIP_CMD; break; case 'B': /* * non-standard option on number of bytes written on a * single archive volume. */ if ((wrlimit = str_offt(optarg)) <= 0) { paxwarn(1, "Invalid write limit %s", optarg); pax_usage(); } if (wrlimit % BLKMULT) { paxwarn(1, "Write limit is not a %d byte multiple", BLKMULT); pax_usage(); } flg |= CBF; break; case 'D': /* * On extraction check file inode change time before the * modification of the file name. Non standard option. */ Dflag = 1; flg |= CDF; break; case 'E': /* * non-standard limit on read faults * 0 indicates stop after first error, values * indicate a limit, "NONE" try forever */ flg |= CEF; if (strcmp(NONE, optarg) == 0) maxflt = -1; else if ((maxflt = atoi(optarg)) < 0) { paxwarn(1, "Error count value must be positive"); pax_usage(); } break; case 'G': /* * non-standard option for selecting files within an * archive by group (gid or name) */ if (grp_add(optarg) < 0) { pax_usage(); break; } flg |= CGF; break; case 'H': /* * follow command line symlinks only */ Hflag = 1; flg |= CHF; break; case 'L': /* * follow symlinks */ Lflag = 1; flg |= CLF; break; case 'O': /* * Force one volume. Non standard option. */ Oflag = 1; break; case 'P': /* * do NOT follow symlinks (default) */ Lflag = 0; flg |= CPF; break; case 'T': /* * non-standard option for selecting files within an * archive by modification time range (lower,upper) */ if (trng_add(optarg) < 0) { pax_usage(); break; } flg |= CTF; break; case 'U': /* * non-standard option for selecting files within an * archive by user (uid or name) */ if (usr_add(optarg) < 0) { pax_usage(); break; } flg |= CUF; break; case 'X': /* * do not pass over mount points in the file system */ Xflag = 1; flg |= CXF; break; case 'Y': /* * On extraction check file inode change time after the * modification of the file name. Non standard option. */ Yflag = 1; flg |= CYF; break; case 'Z': /* * On extraction check modification time after the * modification of the file name. Non standard option. */ Zflag = 1; flg |= CZF; break; default: pax_usage(); break; } } /* * figure out the operation mode of pax read,write,extract,copy,append * or list. check that we have not been given a bogus set of flags * for the operation mode. */ if (ISLIST(flg)) { act = LIST; listf = stdout; bflg = flg & BDLIST; } else if (ISEXTRACT(flg)) { act = EXTRACT; bflg = flg & BDEXTR; } else if (ISARCHIVE(flg)) { act = ARCHIVE; bflg = flg & BDARCH; } else if (ISAPPND(flg)) { act = APPND; bflg = flg & BDARCH; } else if (ISCOPY(flg)) { act = COPY; bflg = flg & BDCOPY; } else pax_usage(); if (bflg) { printflg(flg); pax_usage(); } /* * if we are writing (ARCHIVE) we use the default format if the user * did not specify a format. when we write during an APPEND, we will * adopt the format of the existing archive if none was supplied. */ if (!(flg & XF) && (act == ARCHIVE)) frmt = &(fsub[DEFLT]); /* * process the args as they are interpreted by the operation mode */ switch (act) { case LIST: case EXTRACT: for (; optind < argc; optind++) if (pat_add(argv[optind], NULL) < 0) pax_usage(); break; case COPY: if (optind >= argc) { paxwarn(0, "Destination directory was not supplied"); pax_usage(); } --argc; dirptr = argv[argc]; /* FALLTHROUGH */ case ARCHIVE: case APPND: for (; optind < argc; optind++) if (ftree_add(argv[optind], 0) < 0) pax_usage(); /* * no read errors allowed on updates/append operation! */ maxflt = 0; break; } } /* * tar_options() * look at the user specified flags. set globals as required and check if * the user specified a legal set of flags. If not, complain and exit */ static void tar_options(int argc, char **argv) { int c; int fstdin = 0; int tar_Oflag = 0; int nincfiles = 0; int incfiles_max = 0; struct incfile { char *file; char *dir; }; struct incfile *incfiles = NULL; /* * Set default values. */ rmleadslash = 1; /* * process option flags */ while ((c = getoldopt(argc, argv, "b:cef:hjmopqruts:vwxyzBC:HI:LOPXZ014578")) != -1) { switch(c) { case 'b': /* * specify blocksize in 512-byte blocks */ if ((wrblksz = (int)str_offt(optarg)) <= 0) { paxwarn(1, "Invalid block size %s", optarg); tar_usage(); } wrblksz *= 512; /* XXX - check for int oflow */ break; case 'c': /* * create an archive */ act = ARCHIVE; break; case 'e': /* * stop after first error */ maxflt = 0; break; case 'f': /* * filename where the archive is stored */ if ((optarg[0] == '-') && (optarg[1]== '\0')) { /* * treat a - as stdin */ fstdin = 1; arcname = NULL; break; } fstdin = 0; arcname = optarg; break; case 'h': /* * follow symlinks */ Lflag = 1; break; case 'j': case 'y': /* * use bzip2. Non standard option. */ gzip_program = BZIP2_CMD; break; case 'm': /* * do not preserve modification time */ pmtime = 0; break; case 'o': if (opt_add("write_opt=nodir") < 0) tar_usage(); case 'O': tar_Oflag = 1; break; case 'p': /* * preserve uid/gid and file mode, regardless of umask */ pmode = 1; pids = 1; break; case 'q': /* * select first match for a pattern only */ nflag = 1; break; case 'r': case 'u': /* * append to the archive */ act = APPND; break; case 's': /* * file name substitution name pattern */ if (rep_add(optarg) < 0) { tar_usage(); break; } break; case 't': /* * list contents of the tape */ act = LIST; break; case 'v': /* * verbose operation mode */ vflag++; break; case 'w': /* * interactive file rename */ iflag = 1; break; case 'x': /* * extract an archive, preserving mode, * and mtime if possible. */ act = EXTRACT; pmtime = 1; break; case 'z': /* * use gzip. Non standard option. */ gzip_program = GZIP_CMD; break; case 'B': /* * Nothing to do here, this is pax default */ break; case 'C': chdname = optarg; break; case 'H': /* * follow command line symlinks only */ Hflag = 1; break; case 'I': if (++nincfiles > incfiles_max) { incfiles_max = nincfiles + 3; incfiles = realloc(incfiles, sizeof(*incfiles) * incfiles_max); if (incfiles == NULL) { paxwarn(0, "Unable to allocate space " "for option list"); exit(1); } } incfiles[nincfiles - 1].file = optarg; incfiles[nincfiles - 1].dir = chdname; break; case 'L': /* * follow symlinks */ Lflag = 1; break; case 'P': /* * do not remove leading '/' from pathnames */ rmleadslash = 0; break; case 'X': /* * do not pass over mount points in the file system */ Xflag = 1; break; case 'Z': /* * use compress. */ gzip_program = COMPRESS_CMD; break; case '0': arcname = DEV_0; break; case '1': arcname = DEV_1; break; case '4': arcname = DEV_4; break; case '5': arcname = DEV_5; break; case '7': arcname = DEV_7; break; case '8': arcname = DEV_8; break; default: tar_usage(); break; } } argc -= optind; argv += optind; /* Traditional tar behaviour (pax uses stderr unless in list mode) */ if (fstdin == 1 && act == ARCHIVE) listf = stderr; else listf = stdout; /* Traditional tar behaviour (pax wants to read file list from stdin) */ if ((act == ARCHIVE || act == APPND) && argc == 0 && nincfiles == 0) exit(0); /* * if we are writing (ARCHIVE) specify tar, otherwise run like pax * (unless -o specified) */ if (act == ARCHIVE || act == APPND) frmt = &(fsub[tar_Oflag ? F_OTAR : F_TAR]); else if (tar_Oflag) { paxwarn(1, "The -O/-o options are only valid when writing an archive"); tar_usage(); /* only valid when writing */ } /* * process the args as they are interpreted by the operation mode */ switch (act) { case LIST: case EXTRACT: default: { int sawpat = 0; char *file, *dir = NULL; while (nincfiles || *argv != NULL) { /* * If we queued up any include files, * pull them in now. Otherwise, check * for -I and -C positional flags. * Anything else must be a file to * extract. */ if (nincfiles) { file = incfiles->file; dir = incfiles->dir; incfiles++; nincfiles--; } else if (strcmp(*argv, "-I") == 0) { if (*++argv == NULL) break; file = *argv++; dir = chdname; } else file = NULL; if (file != NULL) { FILE *fp; char *str; if (strcmp(file, "-") == 0) fp = stdin; else if ((fp = fopen(file, "r")) == NULL) { paxwarn(1, "Unable to open file '%s' for read", file); tar_usage(); } while ((str = get_line(fp)) != NULL) { if (pat_add(str, dir) < 0) tar_usage(); sawpat = 1; } if (strcmp(file, "-") != 0) fclose(fp); if (get_line_error) { paxwarn(1, "Problem with file '%s'", file); tar_usage(); } } else if (strcmp(*argv, "-C") == 0) { if (*++argv == NULL) break; chdname = *argv++; } else if (pat_add(*argv++, chdname) < 0) tar_usage(); else sawpat = 1; } /* * if patterns were added, we are doing chdir() * on a file-by-file basis, else, just one * global chdir (if any) after opening input. */ if (sawpat > 0) chdname = NULL; } break; case ARCHIVE: case APPND: if (chdname != NULL) { /* initial chdir() */ if (ftree_add(chdname, 1) < 0) tar_usage(); } while (nincfiles || *argv != NULL) { char *file, *dir = NULL; /* * If we queued up any include files, pull them in * now. Otherwise, check for -I and -C positional * flags. Anything else must be a file to include * in the archive. */ if (nincfiles) { file = incfiles->file; dir = incfiles->dir; incfiles++; nincfiles--; } else if (strcmp(*argv, "-I") == 0) { if (*++argv == NULL) break; file = *argv++; dir = NULL; } else file = NULL; if (file != NULL) { FILE *fp; char *str; /* Set directory if needed */ if (dir) { if (ftree_add(dir, 1) < 0) tar_usage(); } if (strcmp(file, "-") == 0) fp = stdin; else if ((fp = fopen(file, "r")) == NULL) { paxwarn(1, "Unable to open file '%s' for read", file); tar_usage(); } while ((str = get_line(fp)) != NULL) { if (ftree_add(str, 0) < 0) tar_usage(); } if (strcmp(file, "-") != 0) fclose(fp); if (get_line_error) { paxwarn(1, "Problem with file '%s'", file); tar_usage(); } } else if (strcmp(*argv, "-C") == 0) { if (*++argv == NULL) break; if (ftree_add(*argv++, 1) < 0) tar_usage(); } else if (ftree_add(*argv++, 0) < 0) tar_usage(); } /* * no read errors allowed on updates/append operation! */ maxflt = 0; break; } if (!fstdin && ((arcname == NULL) || (*arcname == '\0'))) { arcname = getenv("TAPE"); if ((arcname == NULL) || (*arcname == '\0')) arcname = _PATH_DEFTAPE; } } static int mkpath(char *path) { struct stat sb; char *slash; int done = 0; slash = path; while (!done) { slash += strspn(slash, "/"); slash += strcspn(slash, "/"); done = (*slash == '\0'); *slash = '\0'; if (stat(path, &sb)) { if (errno != ENOENT || mkdir(path, 0777)) { paxwarn(1, "%s", path); return (-1); } } else if (!S_ISDIR(sb.st_mode)) { syswarn(1, ENOTDIR, "%s", path); return (-1); } if (!done) *slash = '/'; } return (0); } /* * cpio_options() * look at the user specified flags. set globals as required and check if * the user specified a legal set of flags. If not, complain and exit */ static void cpio_options(int argc, char **argv) { int c; size_t i; char *str; FSUB tmp; FILE *fp; kflag = 1; pids = 1; pmode = 1; pmtime = 0; arcname = NULL; dflag = 1; act = -1; nodirs = 1; while ((c=getopt(argc,argv,"abcdfiklmoprstuvzABC:E:F:H:I:LO:SZ6")) != -1) switch (c) { case 'a': /* * preserve access time on files read */ tflag = 1; break; case 'b': /* * swap bytes and half-words when reading data */ break; case 'c': /* * ASCII cpio header */ frmt = &(fsub[F_ACPIO]); break; case 'd': /* * create directories as needed */ nodirs = 0; break; case 'f': /* * invert meaning of pattern list */ cflag = 1; break; case 'i': /* * restore an archive */ act = EXTRACT; break; case 'k': break; case 'l': /* * use links instead of copies when possible */ lflag = 1; break; case 'm': /* * preserve modification time */ pmtime = 1; break; case 'o': /* * create an archive */ act = ARCHIVE; frmt = &(fsub[F_CPIO]); break; case 'p': /* * copy-pass mode */ act = COPY; break; case 'r': /* * interactively rename files */ iflag = 1; break; case 's': /* * swap bytes after reading data */ break; case 't': /* * list contents of archive */ act = LIST; listf = stdout; break; case 'u': /* * replace newer files */ kflag = 0; break; case 'v': /* * verbose operation mode */ vflag = 1; break; case 'z': /* * use gzip. Non standard option. */ gzip_program = GZIP_CMD; break; case 'A': /* * append mode */ act = APPND; break; case 'B': /* * Use 5120 byte block size */ wrblksz = 5120; break; case 'C': /* * set block size in bytes */ wrblksz = atoi(optarg); break; case 'E': /* * file with patterns to extract or list */ if ((fp = fopen(optarg, "r")) == NULL) { paxwarn(1, "Unable to open file '%s' for read", optarg); cpio_usage(); } while ((str = get_line(fp)) != NULL) { pat_add(str, NULL); } fclose(fp); if (get_line_error) { paxwarn(1, "Problem with file '%s'", optarg); cpio_usage(); } break; case 'F': case 'I': case 'O': /* * filename where the archive is stored */ if ((optarg[0] == '-') && (optarg[1]== '\0')) { /* * treat a - as stdin */ arcname = NULL; break; } arcname = optarg; break; case 'H': /* * specify an archive format on write */ tmp.name = optarg; if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub, sizeof(fsub)/sizeof(FSUB), sizeof(FSUB), c_frmt)) != NULL) break; paxwarn(1, "Unknown -H format: %s", optarg); (void)fputs("cpio: Known -H formats are:", stderr); for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i) (void)fprintf(stderr, " %s", fsub[i].name); (void)fputs("\n\n", stderr); cpio_usage(); break; case 'L': /* * follow symbolic links */ Lflag = 1; break; case 'S': /* * swap halfwords after reading data */ break; case 'Z': /* * use compress. Non standard option. */ gzip_program = COMPRESS_CMD; break; case '6': /* * process Version 6 cpio format */ frmt = &(fsub[F_OCPIO]); break; case '?': default: cpio_usage(); break; } argc -= optind; argv += optind; /* * process the args as they are interpreted by the operation mode */ switch (act) { case LIST: case EXTRACT: while (*argv != NULL) if (pat_add(*argv++, NULL) < 0) cpio_usage(); break; case COPY: if (*argv == NULL) { paxwarn(0, "Destination directory was not supplied"); cpio_usage(); } dirptr = *argv; if (mkpath(dirptr) < 0) cpio_usage(); --argc; ++argv; /* FALLTHROUGH */ case ARCHIVE: case APPND: if (*argv != NULL) cpio_usage(); /* * no read errors allowed on updates/append operation! */ maxflt = 0; while ((str = get_line(stdin)) != NULL) { ftree_add(str, 0); } if (get_line_error) { paxwarn(1, "Problem while reading stdin"); cpio_usage(); } break; default: cpio_usage(); break; } } /* * printflg() * print out those invalid flag sets found to the user */ static void printflg(unsigned int flg) { int nxt; int pos = 0; (void)fprintf(stderr,"%s: Invalid combination of options:", argv0); while ((nxt = ffs(flg)) != 0) { flg = flg >> nxt; pos += nxt; (void)fprintf(stderr, " -%c", flgch[pos-1]); } (void)putc('\n', stderr); } /* * c_frmt() * comparison routine used by bsearch to find the format specified * by the user */ static int c_frmt(const void *a, const void *b) { return(strcmp(((const FSUB *)a)->name, ((const FSUB *)b)->name)); } /* * opt_next() * called by format specific options routines to get each format specific * flag and value specified with -o * Return: * pointer to next OPLIST entry or NULL (end of list). */ OPLIST * opt_next(void) { OPLIST *opt; if ((opt = ophead) != NULL) ophead = ophead->fow; return(opt); } /* * bad_opt() * generic routine used to complain about a format specific options * when the format does not support options. */ int bad_opt(void) { OPLIST *opt; if (ophead == NULL) return(0); /* * print all we were given */ paxwarn(1,"These format options are not supported"); while ((opt = opt_next()) != NULL) (void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value); pax_usage(); return(0); } /* * opt_add() * breaks the value supplied to -o into an option name and value. Options * are given to -o in the form -o name-value,name=value * multiple -o may be specified. * Return: * 0 if format in name=value format, -1 if -o is passed junk. */ int opt_add(const char *str) { OPLIST *opt; char *frpt; char *pt; char *endpt; char *lstr; if ((str == NULL) || (*str == '\0')) { paxwarn(0, "Invalid option name"); return(-1); } if ((lstr = strdup(str)) == NULL) { paxwarn(0, "Unable to allocate space for option list"); return(-1); } frpt = endpt = lstr; /* * break into name and values pieces and stuff each one into a * OPLIST structure. When we know the format, the format specific * option function will go through this list */ while ((frpt != NULL) && (*frpt != '\0')) { if ((endpt = strchr(frpt, ',')) != NULL) *endpt = '\0'; if ((pt = strchr(frpt, '=')) == NULL) { paxwarn(0, "Invalid options format"); free(lstr); return(-1); } if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) { paxwarn(0, "Unable to allocate space for option list"); free(lstr); return(-1); } lstr = NULL; /* parts of string going onto the OPLIST */ *pt++ = '\0'; opt->name = frpt; opt->value = pt; opt->fow = NULL; if (endpt != NULL) frpt = endpt + 1; else frpt = NULL; if (ophead == NULL) { optail = ophead = opt; continue; } optail->fow = opt; optail = opt; } free(lstr); return(0); } /* * str_offt() * Convert an expression of the following forms to an off_t > 0. * 1) A positive decimal number. * 2) A positive decimal number followed by a b (mult by 512). * 3) A positive decimal number followed by a k (mult by 1024). * 4) A positive decimal number followed by a m (mult by 512). * 5) A positive decimal number followed by a w (mult by sizeof int) * 6) Two or more positive decimal numbers (with/without k,b or w). * separated by x (also * for backwards compatibility), specifying * the product of the indicated values. * Return: * 0 for an error, a positive value o.w. */ static off_t str_offt(char *val) { char *expr; off_t num, t; -# ifdef NET2_STAT - num = strtol(val, &expr, 0); - if ((num == LONG_MAX) || (num <= 0) || (expr == val)) -# else num = strtoq(val, &expr, 0); if ((num == QUAD_MAX) || (num <= 0) || (expr == val)) -# endif return(0); switch(*expr) { case 'b': t = num; num *= 512; if (t > num) return(0); ++expr; break; case 'k': t = num; num *= 1024; if (t > num) return(0); ++expr; break; case 'm': t = num; num *= 1048576; if (t > num) return(0); ++expr; break; case 'w': t = num; num *= sizeof(int); if (t > num) return(0); ++expr; break; } switch(*expr) { case '\0': break; case '*': case 'x': t = num; num *= str_offt(expr + 1); if (t > num) return(0); break; default: return(0); } return(num); } char * get_line(FILE *f) { char *name, *temp; size_t len; name = fgetln(f, &len); if (!name) { get_line_error = ferror(f) ? GETLINE_FILE_CORRUPT : 0; return(0); } if (name[len-1] != '\n') len++; temp = malloc(len); if (!temp) { get_line_error = GETLINE_OUT_OF_MEM; return(0); } memcpy(temp, name, len-1); temp[len-1] = 0; return(temp); } /* * no_op() * for those option functions where the archive format has nothing to do. * Return: * 0 */ static int no_op(void) { return(0); } /* * pax_usage() * print the usage summary to the user */ void pax_usage(void) { (void)fputs("usage: pax [-cdnOvz] [-E limit] [-f archive] ", stderr); (void)fputs("[-s replstr] ... [-U user] ...", stderr); (void)fputs("\n [-G group] ... ", stderr); (void)fputs("[-T [from_date][,to_date]] ... ", stderr); (void)fputs("[pattern ...]\n", stderr); (void)fputs(" pax -r [-cdiknOuvzDYZ] [-E limit] ", stderr); (void)fputs("[-f archive] [-o options] ... \n", stderr); (void)fputs(" [-p string] ... [-s replstr] ... ", stderr); (void)fputs("[-U user] ... [-G group] ...\n ", stderr); (void)fputs("[-T [from_date][,to_date]] ... ", stderr); (void)fputs(" [pattern ...]\n", stderr); (void)fputs(" pax -w [-dituvzHLOPX] [-b blocksize] ", stderr); (void)fputs("[ [-a] [-f archive] ] [-x format] \n", stderr); (void)fputs(" [-B bytes] [-s replstr] ... ", stderr); (void)fputs("[-o options] ... [-U user] ...", stderr); (void)fputs("\n [-G group] ... ", stderr); (void)fputs("[-T [from_date][,to_date][/[c][m]]] ... ", stderr); (void)fputs("[file ...]\n", stderr); (void)fputs(" pax -r -w [-diklntuvDHLOPXYZ] ", stderr); (void)fputs("[-p string] ... [-s replstr] ...", stderr); (void)fputs("\n [-U user] ... [-G group] ... ", stderr); (void)fputs("[-T [from_date][,to_date][/[c][m]]] ... ", stderr); (void)fputs("\n [file ...] directory\n", stderr); exit(1); } /* * tar_usage() * print the usage summary to the user */ void tar_usage(void) { (void)fputs("usage: tar [-]{crtux}[-befhjmopqsvwyzHLOPXZ014578] [blocksize] ", stderr); (void)fputs("[archive] [replstr] [-C directory] [-I file] [file ...]\n", stderr); exit(1); } /* * cpio_usage() * print the usage summary to the user */ void cpio_usage(void) { (void)fputs("usage: cpio -o [-aABcLvVzZ] [-C bytes] [-H format] [-O archive]\n", stderr); (void)fputs(" [-F archive] < name-list [> archive]\n", stderr); (void)fputs(" cpio -i [-bBcdfmnrsStuvVzZ6] [-C bytes] [-E file] [-H format]\n", stderr); (void)fputs(" [-I archive] [-F archive] [pattern...] [< archive]\n", stderr); (void)fputs(" cpio -p [-adlLmuvV] destination-directory < name-list\n", stderr); exit(1); } diff --git a/bin/pax/pat_rep.c b/bin/pax/pat_rep.c index 0dfa630050ea..ee683341d099 100644 --- a/bin/pax/pat_rep.c +++ b/bin/pax/pat_rep.c @@ -1,1130 +1,1040 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. 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[] = "@(#)pat_rep.c 8.2 (Berkeley) 4/18/94"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include -#ifdef NET2_REGEX -#include -#else #include -#endif #include "pax.h" #include "pat_rep.h" #include "extern.h" /* * routines to handle pattern matching, name modification (regular expression * substitution and interactive renames), and destination name modification for * copy (-rw). Both file name and link names are adjusted as required in these * routines. */ #define MAXSUBEXP 10 /* max subexpressions, DO NOT CHANGE */ static PATTERN *pathead = NULL; /* file pattern match list head */ static PATTERN *pattail = NULL; /* file pattern match list tail */ static REPLACE *rephead = NULL; /* replacement string list head */ static REPLACE *reptail = NULL; /* replacement string list tail */ static int rep_name(char *, int *, int); static int tty_rename(ARCHD *); static int fix_path(char *, int *, char *, int); static int fn_match(char *, char *, char **); static char * range_match(char *, int); -#ifdef NET2_REGEX -static int resub(regexp *, char *, char *, char *); -#else static int resub(regex_t *, regmatch_t *, char *, char *, char *, char *); -#endif /* * rep_add() * parses the -s replacement string; compiles the regular expression * and stores the compiled value and it's replacement string together in * replacement string list. Input to this function is of the form: * /old/new/pg * The first char in the string specifies the delimiter used by this * replacement string. "Old" is a regular expression in "ed" format which * is compiled by regcomp() and is applied to filenames. "new" is the * substitution string; p and g are options flags for printing and global * replacement (over the single filename) * Return: * 0 if a proper replacement string and regular expression was added to * the list of replacement patterns; -1 otherwise. */ int rep_add(char *str) { char *pt1; char *pt2; REPLACE *rep; -# ifndef NET2_REGEX int res; char rebuf[BUFSIZ]; -# endif /* * throw out the bad parameters */ if ((str == NULL) || (*str == '\0')) { paxwarn(1, "Empty replacement string"); return(-1); } /* * first character in the string specifies what the delimiter is for * this expression */ if ((pt1 = strchr(str+1, *str)) == NULL) { paxwarn(1, "Invalid replacement string %s", str); return(-1); } /* * allocate space for the node that handles this replacement pattern * and split out the regular expression and try to compile it */ if ((rep = (REPLACE *)malloc(sizeof(REPLACE))) == NULL) { paxwarn(1, "Unable to allocate memory for replacement string"); return(-1); } *pt1 = '\0'; -# ifdef NET2_REGEX - if ((rep->rcmp = regcomp(str+1)) == NULL) { -# else if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) { regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf)); paxwarn(1, "%s while compiling regular expression %s", rebuf, str); -# endif free(rep); return(-1); } /* * put the delimiter back in case we need an error message and * locate the delimiter at the end of the replacement string * we then point the node at the new substitution string */ *pt1++ = *str; if ((pt2 = strchr(pt1, *str)) == NULL) { -# ifdef NET2_REGEX - free(rep->rcmp); -# else regfree(&rep->rcmp); -# endif free(rep); paxwarn(1, "Invalid replacement string %s", str); return(-1); } *pt2 = '\0'; rep->nstr = pt1; pt1 = pt2++; rep->flgs = 0; /* * set the options if any */ while (*pt2 != '\0') { switch(*pt2) { case 'g': case 'G': rep->flgs |= GLOB; break; case 'p': case 'P': rep->flgs |= PRNT; break; default: -# ifdef NET2_REGEX - free(rep->rcmp); -# else regfree(&rep->rcmp); -# endif free(rep); *pt1 = *str; paxwarn(1, "Invalid replacement string option %s", str); return(-1); } ++pt2; } /* * all done, link it in at the end */ rep->fow = NULL; if (rephead == NULL) { reptail = rephead = rep; return(0); } reptail->fow = rep; reptail = rep; return(0); } /* * pat_add() * add a pattern match to the pattern match list. Pattern matches are used * to select which archive members are extracted. (They appear as * arguments to pax in the list and read modes). If no patterns are * supplied to pax, all members in the archive will be selected (and the * pattern match list is empty). * Return: * 0 if the pattern was added to the list, -1 otherwise */ int pat_add(char *str, char *chdnam) { PATTERN *pt; /* * throw out the junk */ if ((str == NULL) || (*str == '\0')) { paxwarn(1, "Empty pattern string"); return(-1); } /* * allocate space for the pattern and store the pattern. the pattern is * part of argv so do not bother to copy it, just point at it. Add the * node to the end of the pattern list */ if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) { paxwarn(1, "Unable to allocate memory for pattern string"); return(-1); } pt->pstr = str; pt->pend = NULL; pt->plen = strlen(str); pt->fow = NULL; pt->flgs = 0; pt->chdname = chdnam; if (pathead == NULL) { pattail = pathead = pt; return(0); } pattail->fow = pt; pattail = pt; return(0); } /* * pat_chk() * complain if any the user supplied pattern did not result in a match to * a selected archive member. */ void pat_chk(void) { PATTERN *pt; int wban = 0; /* * walk down the list checking the flags to make sure MTCH was set, * if not complain */ for (pt = pathead; pt != NULL; pt = pt->fow) { if (pt->flgs & MTCH) continue; if (!wban) { paxwarn(1, "WARNING! These patterns were not matched:"); ++wban; } (void)fprintf(stderr, "%s\n", pt->pstr); } } /* * pat_sel() * the archive member which matches a pattern was selected. Mark the * pattern as having selected an archive member. arcn->pat points at the * pattern that was matched. arcn->pat is set in pat_match() * * NOTE: When the -c option is used, we are called when there was no match * by pat_match() (that means we did match before the inverted sense of * the logic). Now this seems really strange at first, but with -c we * need to keep track of those patterns that cause an archive member to NOT * be selected (it found an archive member with a specified pattern) * Return: * 0 if the pattern pointed at by arcn->pat was tagged as creating a * match, -1 otherwise. */ int pat_sel(ARCHD *arcn) { PATTERN *pt; PATTERN **ppt; int len; /* * if no patterns just return */ if ((pathead == NULL) || ((pt = arcn->pat) == NULL)) return(0); /* * when we are NOT limited to a single match per pattern mark the * pattern and return */ if (!nflag) { pt->flgs |= MTCH; return(0); } /* * we reach this point only when we allow a single selected match per * pattern, if the pattern matches a directory and we do not have -d * (dflag) we are done with this pattern. We may also be handed a file * in the subtree of a directory. in that case when we are operating * with -d, this pattern was already selected and we are done */ if (pt->flgs & DIR_MTCH) return(0); if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) { /* * ok we matched a directory and we are allowing * subtree matches but because of the -n only its children will * match. This is tagged as a DIR_MTCH type. * WATCH IT, the code assumes that pt->pend points * into arcn->name and arcn->name has not been modified. * If not we will have a big mess. Yup this is another kludge */ /* * if this was a prefix match, remove trailing part of path * so we can copy it. Future matches will be exact prefix match */ if (pt->pend != NULL) *pt->pend = '\0'; if ((pt->pstr = strdup(arcn->name)) == NULL) { paxwarn(1, "Pattern select out of memory"); if (pt->pend != NULL) *pt->pend = '/'; pt->pend = NULL; return(-1); } /* * put the trailing / back in the source string */ if (pt->pend != NULL) { *pt->pend = '/'; pt->pend = NULL; } pt->plen = strlen(pt->pstr); /* * strip off any trailing /, this should really never happen */ len = pt->plen - 1; if (*(pt->pstr + len) == '/') { *(pt->pstr + len) = '\0'; pt->plen = len; } pt->flgs = DIR_MTCH | MTCH; arcn->pat = pt; return(0); } /* * we are then done with this pattern, so we delete it from the list * because it can never be used for another match. * Seems kind of strange to do for a -c, but the pax spec is really * vague on the interaction of -c -n and -d. We assume that when -c * and the pattern rejects a member (i.e. it matched it) it is done. * In effect we place the order of the flags as having -c last. */ pt = pathead; ppt = &pathead; while ((pt != NULL) && (pt != arcn->pat)) { ppt = &(pt->fow); pt = pt->fow; } if (pt == NULL) { /* * should never happen.... */ paxwarn(1, "Pattern list inconsistent"); return(-1); } *ppt = pt->fow; free(pt); arcn->pat = NULL; return(0); } /* * pat_match() * see if this archive member matches any supplied pattern, if a match * is found, arcn->pat is set to point at the potential pattern. Later if * this archive member is "selected" we process and mark the pattern as * one which matched a selected archive member (see pat_sel()) * Return: * 0 if this archive member should be processed, 1 if it should be * skipped and -1 if we are done with all patterns (and pax should quit * looking for more members) */ int pat_match(ARCHD *arcn) { PATTERN *pt; arcn->pat = NULL; /* * if there are no more patterns and we have -n (and not -c) we are * done. otherwise with no patterns to match, matches all */ if (pathead == NULL) { if (nflag && !cflag) return(-1); return(0); } /* * have to search down the list one at a time looking for a match. */ pt = pathead; while (pt != NULL) { /* * check for a file name match unless we have DIR_MTCH set in * this pattern then we want a prefix match */ if (pt->flgs & DIR_MTCH) { /* * this pattern was matched before to a directory * as we must have -n set for this (but not -d). We can * only match CHILDREN of that directory so we must use * an exact prefix match (no wildcards). */ if ((arcn->name[pt->plen] == '/') && (strncmp(pt->pstr, arcn->name, pt->plen) == 0)) break; } else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0) break; pt = pt->fow; } /* * return the result, remember that cflag (-c) inverts the sense of a * match */ if (pt == NULL) return(cflag ? 0 : 1); /* * We had a match, now when we invert the sense (-c) we reject this * member. However we have to tag the pattern a being successful, (in a * match, not in selecting an archive member) so we call pat_sel() here. */ arcn->pat = pt; if (!cflag) return(0); if (pat_sel(arcn) < 0) return(-1); arcn->pat = NULL; return(1); } /* * fn_match() * Return: * 0 if this archive member should be processed, 1 if it should be * skipped and -1 if we are done with all patterns (and pax should quit * looking for more members) * Note: *pend may be changed to show where the prefix ends. */ static int fn_match(char *pattern, char *string, char **pend) { char c; char test; *pend = NULL; for (;;) { switch (c = *pattern++) { case '\0': /* * Ok we found an exact match */ if (*string == '\0') return(0); /* * Check if it is a prefix match */ if ((dflag == 1) || (*string != '/')) return(-1); /* * It is a prefix match, remember where the trailing * / is located */ *pend = string; return(0); case '?': if ((test = *string++) == '\0') return (-1); break; case '*': c = *pattern; /* * Collapse multiple *'s. */ while (c == '*') c = *++pattern; /* * Optimized hack for pattern with a * at the end */ if (c == '\0') return (0); /* * General case, use recursion. */ while ((test = *string) != '\0') { if (!fn_match(pattern, string, pend)) return (0); ++string; } return (-1); case '[': /* * range match */ if (((test = *string++) == '\0') || ((pattern = range_match(pattern, test)) == NULL)) return (-1); break; case '\\': default: if (c != *string++) return (-1); break; } } /* NOTREACHED */ } static char * range_match(char *pattern, int test) { char c; char c2; int negate; int ok = 0; if ((negate = (*pattern == '!')) != 0) ++pattern; while ((c = *pattern++) != ']') { /* * Illegal pattern */ if (c == '\0') return (NULL); if ((*pattern == '-') && ((c2 = pattern[1]) != '\0') && (c2 != ']')) { if ((c <= test) && (test <= c2)) ok = 1; pattern += 2; } else if (c == test) ok = 1; } return (ok == negate ? NULL : pattern); } /* * mod_name() * modify a selected file name. first attempt to apply replacement string * expressions, then apply interactive file rename. We apply replacement * string expressions to both filenames and file links (if we didn't the * links would point to the wrong place, and we could never be able to * move an archive that has a file link in it). When we rename files * interactively, we store that mapping (old name to user input name) so * if we spot any file links to the old file name in the future, we will * know exactly how to fix the file link. * Return: * 0 continue to process file, 1 skip this file, -1 pax is finished */ int mod_name(ARCHD *arcn) { int res = 0; /* * Strip off leading '/' if appropriate. * Currently, this option is only set for the tar format. */ if (rmleadslash && arcn->name[0] == '/') { if (arcn->name[1] == '\0') { arcn->name[0] = '.'; } else { (void)memmove(arcn->name, &arcn->name[1], strlen(arcn->name)); arcn->nlen--; } if (rmleadslash < 2) { rmleadslash = 2; paxwarn(0, "Removing leading / from absolute path names in the archive"); } } if (rmleadslash && arcn->ln_name[0] == '/' && (arcn->type == PAX_HLK || arcn->type == PAX_HRG)) { if (arcn->ln_name[1] == '\0') { arcn->ln_name[0] = '.'; } else { (void)memmove(arcn->ln_name, &arcn->ln_name[1], strlen(arcn->ln_name)); arcn->ln_nlen--; } if (rmleadslash < 2) { rmleadslash = 2; paxwarn(0, "Removing leading / from absolute path names in the archive"); } } /* * IMPORTANT: We have a problem. what do we do with symlinks? * Modifying a hard link name makes sense, as we know the file it * points at should have been seen already in the archive (and if it * wasn't seen because of a read error or a bad archive, we lose * anyway). But there are no such requirements for symlinks. On one * hand the symlink that refers to a file in the archive will have to * be modified to so it will still work at its new location in the * file system. On the other hand a symlink that points elsewhere (and * should continue to do so) should not be modified. There is clearly * no perfect solution here. So we handle them like hardlinks. Clearly * a replacement made by the interactive rename mapping is very likely * to be correct since it applies to a single file and is an exact * match. The regular expression replacements are a little harder to * justify though. We claim that the symlink name is only likely * to be replaced when it points within the file tree being moved and * in that case it should be modified. what we really need to do is to * call an oracle here. :) */ if (rephead != NULL) { /* * we have replacement strings, modify the name and the link * name if any. */ if ((res = rep_name(arcn->name, &(arcn->nlen), 1)) != 0) return(res); if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) && ((res = rep_name(arcn->ln_name, &(arcn->ln_nlen), 0)) != 0)) return(res); } if (iflag) { /* * perform interactive file rename, then map the link if any */ if ((res = tty_rename(arcn)) != 0) return(res); if ((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) sub_name(arcn->ln_name, &(arcn->ln_nlen), sizeof(arcn->ln_name)); } return(res); } /* * tty_rename() * Prompt the user for a replacement file name. A "." keeps the old name, * a empty line skips the file, and an EOF on reading the tty, will cause * pax to stop processing and exit. Otherwise the file name input, replaces * the old one. * Return: * 0 process this file, 1 skip this file, -1 we need to exit pax */ static int tty_rename(ARCHD *arcn) { char tmpname[PAXPATHLEN+2]; int res; /* * prompt user for the replacement name for a file, keep trying until * we get some reasonable input. Archives may have more than one file * on them with the same name (from updates etc). We print verbose info * on the file so the user knows what is up. */ tty_prnt("\nATTENTION: %s interactive file rename operation.\n", argv0); for (;;) { ls_tty(arcn); tty_prnt("Input new name, or a \".\" to keep the old name, "); tty_prnt("or a \"return\" to skip this file.\n"); tty_prnt("Input > "); if (tty_read(tmpname, sizeof(tmpname)) < 0) return(-1); if (strcmp(tmpname, "..") == 0) { tty_prnt("Try again, illegal file name: ..\n"); continue; } if (strlen(tmpname) > PAXPATHLEN) { tty_prnt("Try again, file name too long\n"); continue; } break; } /* * empty file name, skips this file. a "." leaves it alone */ if (tmpname[0] == '\0') { tty_prnt("Skipping file.\n"); return(1); } if ((tmpname[0] == '.') && (tmpname[1] == '\0')) { tty_prnt("Processing continues, name unchanged.\n"); return(0); } /* * ok the name changed. We may run into links that point at this * file later. we have to remember where the user sent the file * in order to repair any links. */ tty_prnt("Processing continues, name changed to: %s\n", tmpname); res = add_name(arcn->name, arcn->nlen, tmpname); arcn->nlen = l_strncpy(arcn->name, tmpname, sizeof(arcn->name) - 1); arcn->name[arcn->nlen] = '\0'; if (res < 0) return(-1); return(0); } /* * set_dest() * fix up the file name and the link name (if any) so this file will land * in the destination directory (used during copy() -rw). * Return: * 0 if ok, -1 if failure (name too long) */ int set_dest(ARCHD *arcn, char *dest_dir, int dir_len) { if (fix_path(arcn->name, &(arcn->nlen), dest_dir, dir_len) < 0) return(-1); /* * It is really hard to deal with symlinks here, we cannot be sure * if the name they point was moved (or will be moved). It is best to * leave them alone. */ if ((arcn->type != PAX_HLK) && (arcn->type != PAX_HRG)) return(0); if (fix_path(arcn->ln_name, &(arcn->ln_nlen), dest_dir, dir_len) < 0) return(-1); return(0); } /* * fix_path * concatenate dir_name and or_name and store the result in or_name (if * it fits). This is one ugly function. * Return: * 0 if ok, -1 if the final name is too long */ static int fix_path( char *or_name, int *or_len, char *dir_name, int dir_len) { char *src; char *dest; char *start; int len; /* * we shift the or_name to the right enough to tack in the dir_name * at the front. We make sure we have enough space for it all before * we start. since dest always ends in a slash, we skip of or_name * if it also starts with one. */ start = or_name; src = start + *or_len; dest = src + dir_len; if (*start == '/') { ++start; --dest; } if ((len = dest - or_name) > PAXPATHLEN) { paxwarn(1, "File name %s/%s, too long", dir_name, start); return(-1); } *or_len = len; /* * enough space, shift */ while (src >= start) *dest-- = *src--; src = dir_name + dir_len - 1; /* * splice in the destination directory name */ while (src >= dir_name) *dest-- = *src--; *(or_name + len) = '\0'; return(0); } /* * rep_name() * walk down the list of replacement strings applying each one in order. * when we find one with a successful substitution, we modify the name * as specified. if required, we print the results. if the resulting name * is empty, we will skip this archive member. We use the regexp(3) * routines (regexp() ought to win a prize as having the most cryptic * library function manual page). * --Parameters-- * name is the file name we are going to apply the regular expressions to * (and may be modified) * nlen is the length of this name (and is modified to hold the length of * the final string). * prnt is a flag that says whether to print the final result. * Return: * 0 if substitution was successful, 1 if we are to skip the file (the name * ended up empty) */ static int rep_name(char *name, int *nlen, int prnt) { REPLACE *pt; char *inpt; char *outpt; char *endpt; char *rpt; int found = 0; int res; -# ifndef NET2_REGEX regmatch_t pm[MAXSUBEXP]; -# endif char nname[PAXPATHLEN+1]; /* final result of all replacements */ char buf1[PAXPATHLEN+1]; /* where we work on the name */ /* * copy the name into buf1, where we will work on it. We need to keep * the orig string around so we can print out the result of the final * replacement. We build up the final result in nname. inpt points at * the string we apply the regular expression to. prnt is used to * suppress printing when we handle replacements on the link field * (the user already saw that substitution go by) */ pt = rephead; (void)strlcpy(buf1, name, sizeof(buf1)); inpt = buf1; outpt = nname; endpt = outpt + PAXPATHLEN; /* * try each replacement string in order */ while (pt != NULL) { do { /* * check for a successful substitution, if not go to * the next pattern, or cleanup if we were global */ -# ifdef NET2_REGEX - if (regexec(pt->rcmp, inpt) == 0) -# else if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0) -# endif break; /* * ok we found one. We have three parts, the prefix * which did not match, the section that did and the * tail (that also did not match). Copy the prefix to * the final output buffer (watching to make sure we * do not create a string too long). */ found = 1; -# ifdef NET2_REGEX - rpt = pt->rcmp->startp[0]; -# else rpt = inpt + pm[0].rm_so; -# endif while ((inpt < rpt) && (outpt < endpt)) *outpt++ = *inpt++; if (outpt == endpt) break; /* * for the second part (which matched the regular * expression) apply the substitution using the * replacement string and place it the prefix in the * final output. If we have problems, skip it. */ -# ifdef NET2_REGEX - if ((res = resub(pt->rcmp,pt->nstr,outpt,endpt)) < 0) { -# else if ((res = resub(&(pt->rcmp),pm,inpt,pt->nstr,outpt,endpt)) < 0) { -# endif if (prnt) paxwarn(1, "Replacement name error %s", name); return(1); } outpt += res; /* * we set up to look again starting at the first * character in the tail (of the input string right * after the last character matched by the regular * expression (inpt always points at the first char in * the string to process). If we are not doing a global * substitution, we will use inpt to copy the tail to * the final result. Make sure we do not overrun the * output buffer */ -# ifdef NET2_REGEX - inpt = pt->rcmp->endp[0]; -# else inpt += pm[0].rm_eo - pm[0].rm_so; -# endif if ((outpt == endpt) || (*inpt == '\0')) break; /* * if the user wants global we keep trying to * substitute until it fails, then we are done. */ } while (pt->flgs & GLOB); if (found) break; /* * a successful substitution did NOT occur, try the next one */ pt = pt->fow; } if (found) { /* * we had a substitution, copy the last tail piece (if there is * room) to the final result */ while ((outpt < endpt) && (*inpt != '\0')) *outpt++ = *inpt++; *outpt = '\0'; if ((outpt == endpt) && (*inpt != '\0')) { if (prnt) paxwarn(1,"Replacement name too long %s >> %s", name, nname); return(1); } /* * inform the user of the result if wanted */ if (prnt && (pt->flgs & PRNT)) { if (*nname == '\0') (void)fprintf(stderr,"%s >> \n", name); else (void)fprintf(stderr,"%s >> %s\n", name, nname); } /* * if empty inform the caller this file is to be skipped * otherwise copy the new name over the orig name and return */ if (*nname == '\0') return(1); *nlen = l_strncpy(name, nname, PAXPATHLEN + 1); name[PAXPATHLEN] = '\0'; } return(0); } -#ifdef NET2_REGEX -/* - * resub() - * apply the replacement to the matched expression. expand out the old - * style ed(1) subexpression expansion. - * Return: - * -1 if error, or the number of characters added to the destination. - */ - -static int -resub(regexp *prog, char *src, char *dest, char *destend) -{ - char *spt; - char *dpt; - char c; - int no; - int len; - - spt = src; - dpt = dest; - while ((dpt < destend) && ((c = *spt++) != '\0')) { - if (c == '&') - no = 0; - else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) - no = *spt++ - '0'; - else { - if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) - c = *spt++; - *dpt++ = c; - continue; - } - if ((prog->startp[no] == NULL) || (prog->endp[no] == NULL) || - ((len = prog->endp[no] - prog->startp[no]) <= 0)) - continue; - - /* - * copy the subexpression to the destination. - * fail if we run out of space or the match string is damaged - */ - if (len > (destend - dpt)) - len = destend - dpt; - if (l_strncpy(dpt, prog->startp[no], len) != len) - return(-1); - dpt += len; - } - return(dpt - dest); -} - -#else /* * resub() * apply the replacement to the matched expression. expand out the old * style ed(1) subexpression expansion. * Return: * -1 if error, or the number of characters added to the destination. */ static int resub(regex_t *rp, regmatch_t *pm, char *orig, char *src, char *dest, char *destend) { char *spt; char *dpt; char c; regmatch_t *pmpt; int len; int subexcnt; spt = src; dpt = dest; subexcnt = rp->re_nsub; while ((dpt < destend) && ((c = *spt++) != '\0')) { /* * see if we just have an ordinary replacement character * or we refer to a subexpression. */ if (c == '&') { pmpt = pm; } else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) { /* * make sure there is a subexpression as specified */ if ((len = *spt++ - '0') > subexcnt) return(-1); pmpt = pm + len; } else { /* * Ordinary character, just copy it */ if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) c = *spt++; *dpt++ = c; continue; } /* * continue if the subexpression is bogus */ if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) || ((len = pmpt->rm_eo - pmpt->rm_so) <= 0)) continue; /* * copy the subexpression to the destination. * fail if we run out of space or the match string is damaged */ if (len > (destend - dpt)) len = destend - dpt; if (l_strncpy(dpt, orig + pmpt->rm_so, len) != len) return(-1); dpt += len; } return(dpt - dest); } -#endif diff --git a/bin/pax/pat_rep.h b/bin/pax/pat_rep.h index 7def28d36e30..e28064e6a08e 100644 --- a/bin/pax/pat_rep.h +++ b/bin/pax/pat_rep.h @@ -1,53 +1,49 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. 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$ */ /* * 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; diff --git a/bin/pax/sel_subs.c b/bin/pax/sel_subs.c index 4c0d09e4eb7f..f1e644a55ac9 100644 --- a/bin/pax/sel_subs.c +++ b/bin/pax/sel_subs.c @@ -1,608 +1,600 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #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 the user 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); free(pt); 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); free(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); free(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); free(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); } diff --git a/bin/pax/tar.c b/bin/pax/tar.c index 3a458223bddd..41cdb1f0d2e7 100644 --- a/bin/pax/tar.c +++ b/bin/pax/tar.c @@ -1,1123 +1,1099 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * 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. * 3. 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[] = "@(#)tar.c 8.2 (Berkeley) 4/18/94"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include "pax.h" #include "extern.h" #include "tar.h" /* * Routines for reading, writing and header identify of various versions of tar */ static u_long tar_chksm(char *, int); static char *name_split(char *, int); static int ul_oct(u_long, char *, int, int); -#ifndef NET2_STAT static int uqd_oct(u_quad_t, char *, int, int); -#endif /* * Routines common to all versions of tar */ static int tar_nodir; /* do not write dirs under old tar */ /* * tar_endwr() * add the tar trailer of two null blocks * Return: * 0 if ok, -1 otherwise (what wr_skip returns) */ int tar_endwr(void) { return(wr_skip((off_t)(NULLCNT*BLKMULT))); } /* * tar_endrd() * no cleanup needed here, just return size of trailer (for append) * Return: * size of trailer (2 * BLKMULT) */ off_t tar_endrd(void) { return((off_t)(NULLCNT*BLKMULT)); } /* * tar_trail() * Called to determine if a header block is a valid trailer. We are passed * the block, the in_sync flag (which tells us we are in resync mode; * looking for a valid header), and cnt (which starts at zero) which is * used to count the number of empty blocks we have seen so far. * Return: * 0 if a valid trailer, -1 if not a valid trailer, or 1 if the block * could never contain a header. */ int tar_trail(char *buf, int in_resync, int *cnt) { int i; /* * look for all zero, trailer is two consecutive blocks of zero */ for (i = 0; i < BLKMULT; ++i) { if (buf[i] != '\0') break; } /* * if not all zero it is not a trailer, but MIGHT be a header. */ if (i != BLKMULT) return(-1); /* * When given a zero block, we must be careful! * If we are not in resync mode, check for the trailer. Have to watch * out that we do not mis-identify file data as the trailer, so we do * NOT try to id a trailer during resync mode. During resync mode we * might as well throw this block out since a valid header can NEVER be * a block of all 0 (we must have a valid file name). */ if (!in_resync && (++*cnt >= NULLCNT)) return(0); return(1); } /* * ul_oct() * convert an unsigned long to an octal string. many oddball field * termination characters are used by the various versions of tar in the * different fields. term selects which kind to use. str is '0' padded * at the front to len. we are unable to use only one format as many old * tar readers are very cranky about this. * Return: * 0 if the number fit into the string, -1 otherwise */ static int ul_oct(u_long val, char *str, int len, int term) { char *pt; /* * term selects the appropriate character(s) for the end of the string */ pt = str + len - 1; switch(term) { case 3: *pt-- = '\0'; break; case 2: *pt-- = ' '; *pt-- = '\0'; break; case 1: *pt-- = ' '; break; case 0: default: *pt-- = '\0'; *pt-- = ' '; break; } /* * convert and blank pad if there is space */ while (pt >= str) { *pt-- = '0' + (char)(val & 0x7); if ((val = val >> 3) == (u_long)0) break; } while (pt >= str) *pt-- = '0'; if (val != (u_long)0) return(-1); return(0); } -#ifndef NET2_STAT /* * uqd_oct() * convert an u_quad_t to an octal string. one of many oddball field * termination characters are used by the various versions of tar in the * different fields. term selects which kind to use. str is '0' padded * at the front to len. we are unable to use only one format as many old * tar readers are very cranky about this. * Return: * 0 if the number fit into the string, -1 otherwise */ static int uqd_oct(u_quad_t val, char *str, int len, int term) { char *pt; /* * term selects the appropriate character(s) for the end of the string */ pt = str + len - 1; switch(term) { case 3: *pt-- = '\0'; break; case 2: *pt-- = ' '; *pt-- = '\0'; break; case 1: *pt-- = ' '; break; case 0: default: *pt-- = '\0'; *pt-- = ' '; break; } /* * convert and blank pad if there is space */ while (pt >= str) { *pt-- = '0' + (char)(val & 0x7); if ((val = val >> 3) == 0) break; } while (pt >= str) *pt-- = '0'; if (val != (u_quad_t)0) return(-1); return(0); } -#endif /* * tar_chksm() * calculate the checksum for a tar block counting the checksum field as * all blanks (BLNKSUM is that value pre-calculated, the sum of 8 blanks). * NOTE: we use len to short circuit summing 0's on write since we ALWAYS * pad headers with 0. * Return: * unsigned long checksum */ static u_long tar_chksm(char *blk, int len) { char *stop; char *pt; u_long chksm = BLNKSUM; /* initial value is checksum field sum */ /* * add the part of the block before the checksum field */ pt = blk; stop = blk + CHK_OFFSET; while (pt < stop) chksm += (u_long)(*pt++ & 0xff); /* * move past the checksum field and keep going, spec counts the * checksum field as the sum of 8 blanks (which is pre-computed as * BLNKSUM). * ASSUMED: len is greater than CHK_OFFSET. (len is where our 0 padding * starts, no point in summing zero's) */ pt += CHK_LEN; stop = blk + len; while (pt < stop) chksm += (u_long)(*pt++ & 0xff); return(chksm); } /* * Routines for old BSD style tar (also made portable to sysV tar) */ /* * tar_id() * determine if a block given to us is a valid tar header (and not a USTAR * header). We have to be on the lookout for those pesky blocks of all * zero's. * Return: * 0 if a tar header, -1 otherwise */ int tar_id(char *blk, int size) { HD_TAR *hd; HD_USTAR *uhd; if (size < BLKMULT) return(-1); hd = (HD_TAR *)blk; uhd = (HD_USTAR *)blk; /* * check for block of zero's first, a simple and fast test, then make * sure this is not a ustar header by looking for the ustar magic * cookie. We should use TMAGLEN, but some USTAR archive programs are * wrong and create archives missing the \0. Last we check the * checksum. If this is ok we have to assume it is a valid header. */ if (hd->name[0] == '\0') return(-1); if (strncmp(uhd->magic, TMAGIC, TMAGLEN - 1) == 0) return(-1); if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT)) return(-1); return(0); } /* * tar_opt() * handle tar format specific -o options * Return: * 0 if ok -1 otherwise */ int tar_opt(void) { OPLIST *opt; while ((opt = opt_next()) != NULL) { if (strcmp(opt->name, TAR_OPTION) || strcmp(opt->value, TAR_NODIR)) { paxwarn(1, "Unknown tar format -o option/value pair %s=%s", opt->name, opt->value); paxwarn(1,"%s=%s is the only supported tar format option", TAR_OPTION, TAR_NODIR); return(-1); } /* * we only support one option, and only when writing */ if ((act != APPND) && (act != ARCHIVE)) { paxwarn(1, "%s=%s is only supported when writing.", opt->name, opt->value); return(-1); } tar_nodir = 1; } return(0); } /* * tar_rd() * extract the values out of block already determined to be a tar header. * store the values in the ARCHD parameter. * Return: * 0 */ int tar_rd(ARCHD *arcn, char *buf) { HD_TAR *hd; char *pt; /* * we only get proper sized buffers passed to us */ if (tar_id(buf, BLKMULT) < 0) return(-1); arcn->org_name = arcn->name; arcn->sb.st_nlink = 1; arcn->pat = NULL; /* * copy out the name and values in the stat buffer */ hd = (HD_TAR *)buf; /* * old tar format specifies the name always be null-terminated, * but let's be robust to broken archives. * the same applies to handling links below. */ arcn->nlen = l_strncpy(arcn->name, hd->name, MIN(sizeof(hd->name), sizeof(arcn->name)) - 1); arcn->name[arcn->nlen] = '\0'; arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode,sizeof(hd->mode),OCT) & 0xfff); arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); -#ifdef NET2_STAT - arcn->sb.st_size = (off_t)asc_ul(hd->size, sizeof(hd->size), OCT); - arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT); -#else arcn->sb.st_size = (off_t)asc_uqd(hd->size, sizeof(hd->size), OCT); arcn->sb.st_mtime = (time_t)asc_uqd(hd->mtime, sizeof(hd->mtime), OCT); -#endif arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; /* * have to look at the last character, it may be a '/' and that is used * to encode this as a directory */ pt = &(arcn->name[arcn->nlen - 1]); arcn->pad = 0; arcn->skip = 0; switch(hd->linkflag) { case SYMTYPE: /* * symbolic link, need to get the link name and set the type in * the st_mode so -v printing will look correct. */ arcn->type = PAX_SLK; arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname, MIN(sizeof(hd->linkname), sizeof(arcn->ln_name)) - 1); arcn->ln_name[arcn->ln_nlen] = '\0'; arcn->sb.st_mode |= S_IFLNK; break; case LNKTYPE: /* * hard link, need to get the link name, set the type in the * st_mode and st_nlink so -v printing will look better. */ arcn->type = PAX_HLK; arcn->sb.st_nlink = 2; arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname, MIN(sizeof(hd->linkname), sizeof(arcn->ln_name)) - 1); arcn->ln_name[arcn->ln_nlen] = '\0'; /* * no idea of what type this thing really points at, but * we set something for printing only. */ arcn->sb.st_mode |= S_IFREG; break; case DIRTYPE: /* * It is a directory, set the mode for -v printing */ arcn->type = PAX_DIR; arcn->sb.st_mode |= S_IFDIR; arcn->sb.st_nlink = 2; arcn->ln_name[0] = '\0'; arcn->ln_nlen = 0; break; case AREGTYPE: case REGTYPE: default: /* * If we have a trailing / this is a directory and NOT a file. */ arcn->ln_name[0] = '\0'; arcn->ln_nlen = 0; if (*pt == '/') { /* * it is a directory, set the mode for -v printing */ arcn->type = PAX_DIR; arcn->sb.st_mode |= S_IFDIR; arcn->sb.st_nlink = 2; } else { /* * have a file that will be followed by data. Set the * skip value to the size field and calculate the size * of the padding. */ arcn->type = PAX_REG; arcn->sb.st_mode |= S_IFREG; arcn->pad = TAR_PAD(arcn->sb.st_size); arcn->skip = arcn->sb.st_size; } break; } /* * strip off any trailing slash. */ if (*pt == '/') { *pt = '\0'; --arcn->nlen; } return(0); } /* * tar_wr() * write a tar header for the file specified in the ARCHD to the archive. * Have to check for file types that cannot be stored and file names that * are too long. Be careful of the term (last arg) to ul_oct, each field * of tar has it own spec for the termination character(s). * ASSUMED: space after header in header block is zero filled * Return: * 0 if file has data to be written after the header, 1 if file has NO * data to write after the header, -1 if archive write failed */ int tar_wr(ARCHD *arcn) { HD_TAR *hd; int len; HD_TAR hdblk; /* * check for those file system types which tar cannot store */ switch(arcn->type) { case PAX_DIR: /* * user asked that dirs not be written to the archive */ if (tar_nodir) return(1); break; case PAX_CHR: paxwarn(1, "Tar cannot archive a character device %s", arcn->org_name); return(1); case PAX_BLK: paxwarn(1, "Tar cannot archive a block device %s", arcn->org_name); return(1); case PAX_SCK: paxwarn(1, "Tar cannot archive a socket %s", arcn->org_name); return(1); case PAX_FIF: paxwarn(1, "Tar cannot archive a fifo %s", arcn->org_name); return(1); case PAX_SLK: case PAX_HLK: case PAX_HRG: if (arcn->ln_nlen >= (int)sizeof(hd->linkname)) { paxwarn(1,"Link name too long for tar %s", arcn->ln_name); return(1); } break; case PAX_REG: case PAX_CTG: default: break; } /* * check file name len, remember extra char for dirs (the / at the end) */ len = arcn->nlen; if (arcn->type == PAX_DIR) ++len; if (len >= (int)sizeof(hd->name)) { paxwarn(1, "File name too long for tar %s", arcn->name); return(1); } /* * copy the data out of the ARCHD into the tar header based on the type * of the file. Remember many tar readers want the unused fields to be * padded with zero. We set the linkflag field (type), the linkname * (or zero if not used),the size, and set the padding (if any) to be * added after the file data (0 for all other types, as they only have * a header) */ hd = &hdblk; l_strncpy(hd->name, arcn->name, sizeof(hd->name) - 1); hd->name[sizeof(hd->name) - 1] = '\0'; arcn->pad = 0; if (arcn->type == PAX_DIR) { /* * directories are the same as files, except have a filename * that ends with a /, we add the slash here. No data follows, * dirs, so no pad. */ hd->linkflag = AREGTYPE; memset(hd->linkname, 0, sizeof(hd->linkname)); hd->name[len-1] = '/'; if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1)) goto out; } else if (arcn->type == PAX_SLK) { /* * no data follows this file, so no pad */ hd->linkflag = SYMTYPE; l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname) - 1); hd->linkname[sizeof(hd->linkname) - 1] = '\0'; if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1)) goto out; } else if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) { /* * no data follows this file, so no pad */ hd->linkflag = LNKTYPE; l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname) - 1); hd->linkname[sizeof(hd->linkname) - 1] = '\0'; if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1)) goto out; } else { /* * data follows this file, so set the pad */ hd->linkflag = AREGTYPE; memset(hd->linkname, 0, sizeof(hd->linkname)); -# ifdef NET2_STAT - if (ul_oct((u_long)arcn->sb.st_size, hd->size, - sizeof(hd->size), 1)) { -# else if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size, sizeof(hd->size), 1)) { -# endif paxwarn(1,"File is too large for tar %s", arcn->org_name); return(1); } arcn->pad = TAR_PAD(arcn->sb.st_size); } /* * copy those fields that are independent of the type */ if (ul_oct((u_long)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 0) || ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 0) || ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 0) || ul_oct((u_long)arcn->sb.st_mtime, hd->mtime, sizeof(hd->mtime), 1)) goto out; /* * calculate and add the checksum, then write the header. A return of * 0 tells the caller to now write the file data, 1 says no data needs * to be written */ if (ul_oct(tar_chksm((char *)&hdblk, sizeof(HD_TAR)), hd->chksum, sizeof(hd->chksum), 3)) goto out; if (wr_rdbuf((char *)&hdblk, sizeof(HD_TAR)) < 0) return(-1); if (wr_skip((off_t)(BLKMULT - sizeof(HD_TAR))) < 0) return(-1); if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG)) return(0); return(1); out: /* * header field is out of range */ paxwarn(1, "Tar header field is too small for %s", arcn->org_name); return(1); } /* * Routines for POSIX ustar */ /* * ustar_strd() * initialization for ustar read * Return: * 0 if ok, -1 otherwise */ int ustar_strd(void) { if ((usrtb_start() < 0) || (grptb_start() < 0)) return(-1); return(0); } /* * ustar_stwr() * initialization for ustar write * Return: * 0 if ok, -1 otherwise */ int ustar_stwr(void) { if ((uidtb_start() < 0) || (gidtb_start() < 0)) return(-1); return(0); } /* * ustar_id() * determine if a block given to us is a valid ustar header. We have to * be on the lookout for those pesky blocks of all zero's * Return: * 0 if a ustar header, -1 otherwise */ int ustar_id(char *blk, int size) { HD_USTAR *hd; if (size < BLKMULT) return(-1); hd = (HD_USTAR *)blk; /* * check for block of zero's first, a simple and fast test then check * ustar magic cookie. We should use TMAGLEN, but some USTAR archive * programs are fouled up and create archives missing the \0. Last we * check the checksum. If ok we have to assume it is a valid header. */ if (hd->name[0] == '\0') return(-1); if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0) return(-1); if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT)) return(-1); return(0); } /* * ustar_rd() * extract the values out of block already determined to be a ustar header. * store the values in the ARCHD parameter. * Return: * 0 */ int ustar_rd(ARCHD *arcn, char *buf) { HD_USTAR *hd; char *dest; int cnt = 0; dev_t devmajor; dev_t devminor; /* * we only get proper sized buffers */ if (ustar_id(buf, BLKMULT) < 0) return(-1); arcn->org_name = arcn->name; arcn->sb.st_nlink = 1; arcn->pat = NULL; arcn->nlen = 0; hd = (HD_USTAR *)buf; /* * see if the filename is split into two parts. if, so joint the parts. * we copy the prefix first and add a / between the prefix and name. */ dest = arcn->name; if (*(hd->prefix) != '\0') { cnt = l_strncpy(dest, hd->prefix, MIN(sizeof(hd->prefix), sizeof(arcn->name) - 2)); dest += cnt; *dest++ = '/'; cnt++; } /* * ustar format specifies the name may be unterminated * if it fills the entire field. this also applies to * the prefix and the linkname. */ arcn->nlen = cnt + l_strncpy(dest, hd->name, MIN(sizeof(hd->name), sizeof(arcn->name) - cnt - 1)); arcn->name[arcn->nlen] = '\0'; /* * follow the spec to the letter. we should only have mode bits, strip * off all other crud we may be passed. */ arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) & 0xfff); -#ifdef NET2_STAT - arcn->sb.st_size = (off_t)asc_ul(hd->size, sizeof(hd->size), OCT); - arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT); -#else arcn->sb.st_size = (off_t)asc_uqd(hd->size, sizeof(hd->size), OCT); arcn->sb.st_mtime = (time_t)asc_uqd(hd->mtime, sizeof(hd->mtime), OCT); -#endif arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; /* * If we can find the ascii names for gname and uname in the password * and group files we will use the uid's and gid they bind. Otherwise * we use the uid and gid values stored in the header. (This is what * the POSIX spec wants). */ hd->gname[sizeof(hd->gname) - 1] = '\0'; if (gid_name(hd->gname, &(arcn->sb.st_gid)) < 0) arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); hd->uname[sizeof(hd->uname) - 1] = '\0'; if (uid_name(hd->uname, &(arcn->sb.st_uid)) < 0) arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); /* * set the defaults, these may be changed depending on the file type */ arcn->ln_name[0] = '\0'; arcn->ln_nlen = 0; arcn->pad = 0; arcn->skip = 0; arcn->sb.st_rdev = (dev_t)0; /* * set the mode and PAX type according to the typeflag in the header */ switch(hd->typeflag) { case FIFOTYPE: arcn->type = PAX_FIF; arcn->sb.st_mode |= S_IFIFO; break; case DIRTYPE: arcn->type = PAX_DIR; arcn->sb.st_mode |= S_IFDIR; arcn->sb.st_nlink = 2; /* * Some programs that create ustar archives append a '/' * to the pathname for directories. This clearly violates * ustar specs, but we will silently strip it off anyway. */ if (arcn->name[arcn->nlen - 1] == '/') arcn->name[--arcn->nlen] = '\0'; break; case BLKTYPE: case CHRTYPE: /* * this type requires the rdev field to be set. */ if (hd->typeflag == BLKTYPE) { arcn->type = PAX_BLK; arcn->sb.st_mode |= S_IFBLK; } else { arcn->type = PAX_CHR; arcn->sb.st_mode |= S_IFCHR; } devmajor = (dev_t)asc_ul(hd->devmajor,sizeof(hd->devmajor),OCT); devminor = (dev_t)asc_ul(hd->devminor,sizeof(hd->devminor),OCT); arcn->sb.st_rdev = TODEV(devmajor, devminor); break; case SYMTYPE: case LNKTYPE: if (hd->typeflag == SYMTYPE) { arcn->type = PAX_SLK; arcn->sb.st_mode |= S_IFLNK; } else { arcn->type = PAX_HLK; /* * so printing looks better */ arcn->sb.st_mode |= S_IFREG; arcn->sb.st_nlink = 2; } /* * copy the link name */ arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname, MIN(sizeof(hd->linkname), sizeof(arcn->ln_name) - 1)); arcn->ln_name[arcn->ln_nlen] = '\0'; break; case CONTTYPE: case AREGTYPE: case REGTYPE: default: /* * these types have file data that follows. Set the skip and * pad fields. */ arcn->type = PAX_REG; arcn->pad = TAR_PAD(arcn->sb.st_size); arcn->skip = arcn->sb.st_size; arcn->sb.st_mode |= S_IFREG; break; } return(0); } /* * ustar_wr() * write a ustar header for the file specified in the ARCHD to the archive * Have to check for file types that cannot be stored and file names that * are too long. Be careful of the term (last arg) to ul_oct, we only use * '\0' for the termination character (this is different than picky tar) * ASSUMED: space after header in header block is zero filled * Return: * 0 if file has data to be written after the header, 1 if file has NO * data to write after the header, -1 if archive write failed */ int ustar_wr(ARCHD *arcn) { HD_USTAR *hd; char *pt; HD_USTAR hdblk; /* * check for those file system types ustar cannot store */ if (arcn->type == PAX_SCK) { paxwarn(1, "Ustar cannot archive a socket %s", arcn->org_name); return(1); } /* * check the length of the linkname */ if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) && (arcn->ln_nlen > (int)sizeof(hd->linkname))) { paxwarn(1, "Link name too long for ustar %s", arcn->ln_name); return(1); } /* * split the path name into prefix and name fields (if needed). if * pt != arcn->name, the name has to be split */ if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) { paxwarn(1, "File name too long for ustar %s", arcn->name); return(1); } hd = &hdblk; arcn->pad = 0L; /* * split the name, or zero out the prefix */ if (pt != arcn->name) { /* * name was split, pt points at the / where the split is to * occur, we remove the / and copy the first part to the prefix */ *pt = '\0'; l_strncpy(hd->prefix, arcn->name, sizeof(hd->prefix)); *pt++ = '/'; } else memset(hd->prefix, 0, sizeof(hd->prefix)); /* * copy the name part. this may be the whole path or the part after * the prefix. both the name and prefix may fill the entire field. */ l_strncpy(hd->name, pt, sizeof(hd->name)); /* * set the fields in the header that are type dependent */ switch(arcn->type) { case PAX_DIR: hd->typeflag = DIRTYPE; memset(hd->linkname, 0, sizeof(hd->linkname)); memset(hd->devmajor, 0, sizeof(hd->devmajor)); memset(hd->devminor, 0, sizeof(hd->devminor)); if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) goto out; break; case PAX_CHR: case PAX_BLK: if (arcn->type == PAX_CHR) hd->typeflag = CHRTYPE; else hd->typeflag = BLKTYPE; memset(hd->linkname, 0, sizeof(hd->linkname)); if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor, sizeof(hd->devmajor), 3) || ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor, sizeof(hd->devminor), 3) || ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) goto out; break; case PAX_FIF: hd->typeflag = FIFOTYPE; memset(hd->linkname, 0, sizeof(hd->linkname)); memset(hd->devmajor, 0, sizeof(hd->devmajor)); memset(hd->devminor, 0, sizeof(hd->devminor)); if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) goto out; break; case PAX_SLK: case PAX_HLK: case PAX_HRG: if (arcn->type == PAX_SLK) hd->typeflag = SYMTYPE; else hd->typeflag = LNKTYPE; /* the link name may occupy the entire field in ustar */ l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname)); memset(hd->devmajor, 0, sizeof(hd->devmajor)); memset(hd->devminor, 0, sizeof(hd->devminor)); if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) goto out; break; case PAX_REG: case PAX_CTG: default: /* * file data with this type, set the padding */ if (arcn->type == PAX_CTG) hd->typeflag = CONTTYPE; else hd->typeflag = REGTYPE; memset(hd->linkname, 0, sizeof(hd->linkname)); memset(hd->devmajor, 0, sizeof(hd->devmajor)); memset(hd->devminor, 0, sizeof(hd->devminor)); arcn->pad = TAR_PAD(arcn->sb.st_size); -# ifdef NET2_STAT - if (ul_oct((u_long)arcn->sb.st_size, hd->size, - sizeof(hd->size), 3)) { -# else if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size, sizeof(hd->size), 3)) { -# endif paxwarn(1,"File is too long for ustar %s",arcn->org_name); return(1); } break; } l_strncpy(hd->magic, TMAGIC, TMAGLEN); l_strncpy(hd->version, TVERSION, TVERSLEN); /* * set the remaining fields. Some versions want all 16 bits of mode * we better humor them (they really do not meet spec though).... */ if (ul_oct((u_long)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 3) || ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 3) || ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 3) || ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),3)) goto out; l_strncpy(hd->uname,name_uid(arcn->sb.st_uid, 0),sizeof(hd->uname)); l_strncpy(hd->gname,name_gid(arcn->sb.st_gid, 0),sizeof(hd->gname)); /* * calculate and store the checksum write the header to the archive * return 0 tells the caller to now write the file data, 1 says no data * needs to be written */ if (ul_oct(tar_chksm((char *)&hdblk, sizeof(HD_USTAR)), hd->chksum, sizeof(hd->chksum), 3)) goto out; if (wr_rdbuf((char *)&hdblk, sizeof(HD_USTAR)) < 0) return(-1); if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0) return(-1); if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG)) return(0); return(1); out: /* * header field is out of range */ paxwarn(1, "Ustar header field is too small for %s", arcn->org_name); return(1); } /* * name_split() * see if the name has to be split for storage in a ustar header. We try * to fit the entire name in the name field without splitting if we can. * The split point is always at a / * Return * character pointer to split point (always the / that is to be removed * if the split is not needed, the points is set to the start of the file * name (it would violate the spec to split there). A NULL is returned if * the file name is too long */ static char * name_split(char *name, int len) { char *start; /* * check to see if the file name is small enough to fit in the name * field. if so just return a pointer to the name. */ if (len <= TNMSZ) return(name); if (len > TPFSZ + TNMSZ) return(NULL); /* * we start looking at the biggest sized piece that fits in the name * field. We walk forward looking for a slash to split at. The idea is * to find the biggest piece to fit in the name field (or the smallest * prefix we can find) */ start = name + len - TNMSZ; while ((*start != '\0') && (*start != '/')) ++start; /* * if we hit the end of the string, this name cannot be split, so we * cannot store this file. */ if (*start == '\0') return(NULL); len = start - name; /* * NOTE: /str where the length of str == TNMSZ can not be stored under * the p1003.1-1990 spec for ustar. We could force a prefix of / and * the file would then expand on extract to //str. The len == 0 below * makes this special case follow the spec to the letter. */ if ((len > TPFSZ) || (len == 0)) return(NULL); /* * ok have a split point, return it to the caller */ return(start); }