/* $NetBSD: ar_io.c,v 1.42 2004/06/21 13:38:51 christos Exp $ */ /*- * 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 HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if !defined(lint) #if 0 static char sccsid[] = "@(#)ar_io.c 8.2 (Berkeley) 4/18/94"; #else __RCSID("$NetBSD: ar_io.c,v 1.42 2004/06/21 13:38:51 christos Exp $"); #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SUPPORT_RMT #define __RMTLIB_PRIVATE #include #endif /* SUPPORT_RMT */ #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 STDO[] = ""; /* pseudo name for stdout */ static char STDN[] = ""; /* pseudo name for stdin */ static char NONE[] = ""; /* pseudo name for none */ 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 */ time_t starttime; /* time the run started */ int force_one_volume; /* 1 if we ignore volume changes */ static int get_phys(void); extern sigset_t s_mask; static void ar_start_gzip(int, const char *, int); static const char *timefmt(char *, size_t, off_t, time_t); static const char *sizefmt(char *, size_t, off_t); #ifdef SUPPORT_RMT #ifdef SYS_NO_RESTART static int rmtread_with_restart(int, void *, int); static int rmtwrite_with_restart(int, void *, int); #else #define rmtread_with_restart(a, b, c) rmtread((a), (b), (c)) #define rmtwrite_with_restart(a, b, c) rmtwrite((a), (b), (c)) #endif #endif /* SUPPORT_RMT */ /* * 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; #ifdef SUPPORT_RMT if (name && strchr(name, ':') != NULL && !forcelocal) { artyp = ISRMT; if ((arfd = rmtopen(name, O_RDWR, DMOD)) == -1) { syswarn(0, errno, "Failed open on %s", name); return -1; } blksz = rdblksz = 8192; lstrval = 1; return 0; } #endif /* SUPPORT_RMT */ /* * 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)) { tty_warn(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; /* * Special handling for empty files. */ if (artyp == ISREG && arsb.st_size == 0) { switch (act) { case LIST: case EXTRACT: return -1; case APPND: act = -ARCHIVE; return -1; case ARCHIVE: break; } } /* * 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, otherwize we will continue to use rdblksz). We * must 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, worst 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); #ifdef SUPPORT_RMT if (artyp == ISRMT) (void)rmtclose(arfd); else #endif /* SUPPORT_RMT */ (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) { (void)fprintf(listf, "%s: unknown format, " OFFT_F " bytes skipped.\n", argv0, rdcnt); (void)fflush(listf); flcnt = 0; return; } if (strcmp(NM_CPIO, argv0) == 0) { (void)fprintf(listf, OFFT_F " blocks\n", (rdcnt ? rdcnt : wrcnt) / 5120); } ar_summary(0); (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 */ #ifdef SUPPORT_RMT if (artyp == ISRMT) { while ((res = rmtread_with_restart(arfd, drbuf, sizeof(drbuf))) > 0) continue; } else { #endif /* SUPPORT_RMT */ while ((res = read_with_restart(arfd, drbuf, sizeof(drbuf))) > 0) continue; #ifdef SUPPORT_RMT } #endif /* SUPPORT_RMT */ 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) { tty_warn(1, "Cannot append to an archive obtained from a pipe."); return(-1); } if (!invld_rec) return(0); tty_warn(1, "Cannot append, device record size %d does not support %s spec", rdblksz, argv0); return(-1); } #ifdef SYS_NO_RESTART /* * read_with_restart() * Equivalent to read() but does retry on signals. * This function is not needed on 4.2BSD and later. * Return: * Number of bytes written. -1 indicates an error. */ int read_with_restart(int fd, void *buf, int bsz) { int r; while (((r = read(fd, buf, bsz)) < 0) && errno == EINTR) continue; return(r); } /* * rmtread_with_restart() * Equivalent to rmtread() but does retry on signals. * This function is not needed on 4.2BSD and later. * Return: * Number of bytes written. -1 indicates an error. */ static int rmtread_with_restart(int fd, void *buf, int bsz) { int r; while (((r = rmtread(fd, buf, bsz)) < 0) && errno == EINTR) continue; return(r); } #endif /* * xread() * Equivalent to read() but does retry on partial read, which may occur * on signals. * Return: * Number of bytes read. 0 for end of file, -1 for an error. */ int xread(int fd, void *buf, int bsz) { char *b = buf; int nread = 0; int r; do { #ifdef SUPPORT_RMT if ((r = rmtread_with_restart(fd, b, bsz)) <= 0) break; #else if ((r = read_with_restart(fd, b, bsz)) <= 0) break; #endif /* SUPPORT_RMT */ b += r; bsz -= r; nread += r; } while (bsz > 0); return(nread ? nread : r); } #ifdef SYS_NO_RESTART /* * write_with_restart() * Equivalent to write() but does retry on signals. * This function is not needed on 4.2BSD and later. * Return: * Number of bytes written. -1 indicates an error. */ int write_with_restart(int fd, void *buf, int bsz) { int r; while (((r = write(fd, buf, bsz)) < 0) && errno == EINTR) ; return(r); } /* * rmtwrite_with_restart() * Equivalent to write() but does retry on signals. * This function is not needed on 4.2BSD and later. * Return: * Number of bytes written. -1 indicates an error. */ static int rmtwrite_with_restart(int fd, void *buf, int bsz) { int r; while (((r = rmtwrite(fd, buf, bsz)) < 0) && errno == EINTR) ; return(r); } #endif /* * xwrite() * Equivalent to write() but does retry on partial write, which may occur * on signals. * Return: * Number of bytes written. -1 indicates an error. */ int xwrite(int fd, void *buf, int bsz) { char *b = buf; int written = 0; int r; do { #ifdef SUPPORT_RMT if ((r = rmtwrite_with_restart(fd, b, bsz)) <= 0) break; #else if ((r = write_with_restart(fd, b, bsz)) <= 0) break; #endif /* SUPPORT_RMT */ b += r; bsz -= r; written += r; } while (bsz > 0); return(written ? written : r); } /* * 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) { #ifdef SUPPORT_RMT case ISRMT: if ((res = rmtread_with_restart(arfd, buf, cnt)) > 0) { io_ok = 1; return res; } break; #endif /* SUPPORT_RMT */ case ISTAPE: if ((res = read_with_restart(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 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_with_restart(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 tty_warn(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 = xwrite(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)) res = lstrval = 0; #ifdef EDQUOT if (errno == EDQUOT) res = lstrval = 0; #endif break; case ISTAPE: case ISCHR: case ISBLK: #ifdef SUPPORT_RMT case ISRMT: #endif /* SUPPORT_RMT */ if (res >= 0) break; if (errno == EACCES) { tty_warn(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)) { tty_warn(1, "Unable to append, trailer re-write failed. Quitting."); return(res); } if (res == 0) tty_warn(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)) tty_warn(0, "WARNING: partial archive write. Archive MAY BE FLAWED"); else tty_warn(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 if this is going to be * an update/append to a 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)) { tty_warn(1, "Cannot allow updates to an archive with flaws."); return(-1); } if (io_ok) did_io = 1; switch(artyp) { #ifdef SUPPORT_RMT case ISRMT: #endif /* SUPPORT_RMT */ 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; #ifdef SUPPORT_RMT if (artyp == ISRMT) { if (rmtioctl(arfd, MTIOCTOP, &mb) < 0) break; } else { #endif /* SUPPORT_RMT */ if (ioctl(arfd, MTIOCTOP, &mb) < 0) break; #ifdef SUPPORT_RMT } #endif /* SUPPORT_RMT */ 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) { tty_warn(1, "Unable to recover from an archive read failure."); return(-1); } tty_warn(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 == ISTAPE || artyp == ISPIPE #ifdef SUPPORT_RMT || artyp == ISRMT #endif /* SUPPORT_RMT */ ) 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) */ mpos = cpos + sksz; if (artyp == ISREG && mpos > arsb.st_size) mpos = arsb.st_size; if ((mpos = lseek(arfd, mpos, SEEK_SET)) >= 0) { *skipped = mpos - cpos; return(0); } } else { if (artyp != ISREG) return(0); /* non-seekable device */ } 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 */ tty_warn(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 */ tty_warn(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: #ifdef SUPPORT_RMT case ISRMT: #endif /* SUPPORT_RMT */ /* * 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 handle 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) { tty_warn(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 ( #ifdef SUPPORT_RMT rmtioctl(arfd, MTIOCTOP, &mb) #else ioctl(arfd, MTIOCTOP, &mb) #endif /* SUPPORT_RMT */ < 0) { syswarn(1, errno, "Unable to backspace tape %ld blocks.", (long) 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() */ #ifdef SUPPORT_RMT while ((res = rmtread_with_restart(arfd, scbuf, sizeof(scbuf))) > 0) #else while ((res = read_with_restart(arfd, scbuf, sizeof(scbuf))) > 0) #endif /* SUPPORT_RMT */ 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 ( #ifdef SUPPORT_RMT rmtioctl(arfd, MTIOCTOP, &mb) #else ioctl(arfd, MTIOCTOP, &mb) #endif /* SUPPORT_RMT */ < 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 ( #ifdef SUPPORT_RMT rmtioctl(arfd, MTIOCTOP, &mb) #else ioctl(arfd, MTIOCTOP, &mb) #endif /* SUPPORT_RMT */ < 0) { syswarn(1, errno, "Unable to backspace over last tape block."); return(-1); } if ((phyblk = #ifdef SUPPORT_RMT rmtread_with_restart(arfd, scbuf, sizeof(scbuf)) #else read_with_restart(arfd, scbuf, sizeof(scbuf)) #endif /* SUPPORT_RMT */ ) <= 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 = #ifdef SUPPORT_RMT rmtread_with_restart(arfd, scbuf, sizeof(scbuf)) #else read_with_restart(arfd, scbuf, sizeof(scbuf)) #endif /* SUPPORT_RMT */ ) > 0) ; if (res < 0) { syswarn(1, errno, "Unable to locate tape filemark."); return(-1); } mb.mt_op = MTBSF; mb.mt_count = 1; if ( #ifdef SUPPORT_RMT rmtioctl(arfd, MTIOCTOP, &mb) #else ioctl(arfd, MTIOCTOP, &mb) #endif /* SUPPORT_RMT */ < 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) { tty_warn(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 ( #ifdef SUPPORT_RMT rmtioctl(arfd, MTIOCTOP, &mb) #else ioctl(arfd, MTIOCTOP, &mb) #endif /* SUPPORT_RMT */ < 0) { syswarn(1, errno, "Unable to backspace tape over %ld pad blocks", (long)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) { char buf[PAXPATHLEN+2]; static char *arcfree = NULL; 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, (sigset_t *)NULL) < 0) syswarn(0, errno, "Unable to restore signal mask"); if (done || !wr_trail || force_one_volume) return(-1); if (!is_gnutar) 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 #ifdef SUPPORT_RMT || artyp == ISRMT #endif /* SUPPORT_RMT */ ) { 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 { if (is_gnutar) { tty_warn(1, "Unexpected EOF on archive file"); return -1; } 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) { if (arcfree) { (void)free(arcfree); arcfree = NULL; } if ((arcfree = strdup(buf)) == NULL) { done = 1; lstrval = -1; tty_warn(0, "Cannot save archive name."); return(-1); } arcname = arcfree; break; } tty_prnt("Cannot open %s, try again\n", buf); continue; } return(0); } /* * ar_start_gzip() * starts the compression/decompression process as a child, using magic * to keep the fd the same in the calling function (parent). possible * programs are GZIP_CMD, BZIP2_CMD, and COMPRESS_CMD. */ void ar_start_gzip(int fd, const char *gzp, 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(gzp, gzp, gzip_flags, NULL) < 0) err(1, "could not exec"); /* NOTREACHED */ } } static const char * timefmt(buf, size, sz, tm) char *buf; size_t size; off_t sz; time_t tm; { (void)snprintf(buf, size, "%lu secs (" OFFT_F " bytes/sec)", (unsigned long)tm, (OFFT_T)(sz / tm)); return buf; } static const char * sizefmt(buf, size, sz) char *buf; size_t size; off_t sz; { (void)snprintf(buf, size, OFFT_F " bytes", (OFFT_T)sz); return buf; } void ar_summary(int n) { time_t secs; int len; char buf[MAXPATHLEN]; char tbuf[MAXPATHLEN/4]; char s1buf[MAXPATHLEN/8]; char s2buf[MAXPATHLEN/8]; FILE *outf; if (act == LIST) outf = stdout; else outf = stderr; /* * If we are called from a signal (n != 0), use snprintf(3) so that we * don't reenter stdio(3). */ (void)time(&secs); if ((secs -= starttime) == 0) secs = 1; /* * 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) { len = snprintf(buf, sizeof(buf), "unknown format, %s skipped in %s\n", sizefmt(s1buf, sizeof(s1buf), rdcnt), timefmt(tbuf, sizeof(tbuf), rdcnt, secs)); if (n == 0) (void)fprintf(outf, "%s: %s", argv0, buf); else (void)write(STDERR_FILENO, buf, len); return; } if (n != 0) { len = snprintf(buf, sizeof(buf), "Working on `%s' (%s)\n", archd.name, sizefmt(s1buf, sizeof(s1buf), archd.sb.st_size)); (void)write(STDERR_FILENO, buf, len); } len = snprintf(buf, sizeof(buf), "%s vol %d, %lu files, %s read, %s written in %s\n", frmt->name, arvol-1, (unsigned long)flcnt, sizefmt(s1buf, sizeof(s1buf), rdcnt), sizefmt(s2buf, sizeof(s2buf), wrcnt), timefmt(tbuf, sizeof(tbuf), rdcnt + wrcnt, secs)); if (n == 0) (void)fprintf(outf, "%s: %s", argv0, buf); else (void)write(STDERR_FILENO, buf, strlen(buf)); } /* * ar_dochdir(name) * change directory to name, and remember where we came from and * where we change to (for ar_open). * * Maybe we could try to be smart and only do the actual chdir * when necessary to write a file read from the archive, but this * is not easy to get right given the pax code structure. * * Be sure to not leak descriptors! * * We are called N * M times when extracting, and N times when * writing archives, where * N: number of -C options * M: number of files in archive * * Returns 0 if all went well, else -1. */ int ar_dochdir(char *name) { /* First fchdir() back... */ if (fchdir(cwdfd) < 0) { syswarn(1, errno, "Can't fchdir to starting directory"); return(-1); } if (chdir(name) < 0) { syswarn(1, errno, "Can't chdir to %s", name); return(-1); } return (0); }