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