diff options
author | Kiyoshi Aman <kiyoshi.aman+adelie@gmail.com> | 2019-03-17 16:35:06 -0500 |
---|---|---|
committer | Kiyoshi Aman <kiyoshi.aman+adelie@gmail.com> | 2019-03-17 16:35:06 -0500 |
commit | ec3eeac15118f74309c4657804dd852c87011402 (patch) | |
tree | f23e18bf9985a7922192f0bdc52d02ea6b3a03ac /bin/pax/ar_io.c | |
parent | d94c253b07d75ab5c368dbd7a0cc3035b019ccfd (diff) | |
download | userland-ec3eeac15118f74309c4657804dd852c87011402.tar.gz userland-ec3eeac15118f74309c4657804dd852c87011402.tar.bz2 userland-ec3eeac15118f74309c4657804dd852c87011402.tar.xz userland-ec3eeac15118f74309c4657804dd852c87011402.zip |
rm bin/pax: utility covered by heirloom-pax
Diffstat (limited to 'bin/pax/ar_io.c')
-rw-r--r-- | bin/pax/ar_io.c | 1725 |
1 files changed, 0 insertions, 1725 deletions
diff --git a/bin/pax/ar_io.c b/bin/pax/ar_io.c deleted file mode 100644 index d839fa0..0000000 --- a/bin/pax/ar_io.c +++ /dev/null @@ -1,1725 +0,0 @@ -/* $NetBSD: ar_io.c,v 1.58 2017/10/02 21:57:59 joerg 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 <sys/cdefs.h> -#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.58 2017/10/02 21:57:59 joerg Exp $"); -#endif -#endif /* not lint */ - -#include <sys/param.h> -#include <sys/ioctl.h> -#ifdef HAVE_SYS_MTIO_H -#include <sys/mtio.h> -#endif -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/wait.h> -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#ifdef SUPPORT_RMT -#define __RMTLIB_PRIVATE -#include <rmt.h> -#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[] = "<STDOUT>"; /* pseudo name for stdout */ -static char STDN[] = "<STDIN>"; /* pseudo name for stdin */ -static char NONE[] = "<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 */ - -#ifdef HAVE_SYS_MTIO_H -static int get_phys(void); -#endif -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, const char *); -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) -{ -#ifdef HAVE_SYS_MTIO_H - struct mtget mb; -#endif - - 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; - } - if (!isrmt(arfd)) { - rmtclose(arfd); - tty_warn(0, "Not a remote file: %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 (dochdir(chdname) == -1) - 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)) { -#ifdef HAVE_SYS_MTIO_H - artyp = ioctl(arfd, MTIOCGET, &mb) ? ISCHR : ISTAPE; -#else - tty_warn(1, "System does not have tape support"); - artyp = ISREG; -#endif - } 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 beyond any doubt that we can unlink only 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 || 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 || 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 && !Vflag) { - flcnt = 0; - return; - } - - /* - * Print out a summary of I/O for this archive volume. - */ - if (vfpart) { - (void)putc('\n', listf); - vfpart = 0; - } - - /* mimic cpio's block count first */ - if (frmt && 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; -#ifdef HAVE_SYS_MTIO_H - struct mtop mb; -#endif - - /* - * 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: -#ifdef HAVE_SYS_MTIO_H - /* - * 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; -#else - tty_warn(1, "System does not have tape support"); -#endif - 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; -#ifdef HAVE_SYS_MTIO_H - int phyblk; - struct mtop mb; -#endif - - /* - * 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 */ -#ifdef HAVE_SYS_MTIO_H - /* - * 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; - } -#else - tty_warn(1, "System does not have tape support"); -#endif - break; - } - lstrval = 1; - return 0; -} - -#ifdef HAVE_SYS_MTIO_H -/* - * 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; -} -#endif - -/* - * 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, 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(char *buf, size_t size, off_t sz, time_t tm, const char *unitstr) -{ - (void)snprintf(buf, size, "%lu secs (" OFFT_F " %s/sec)", - (unsigned long)tm, (OFFT_T)(sz / tm), unitstr); - return buf; -} - -static const char * -sizefmt(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; - char buf[BUFSIZ]; - char tbuf[MAXPATHLEN/4]; /* XXX silly size! */ - char s1buf[MAXPATHLEN/8]; /* XXX very silly size! */ - char s2buf[MAXPATHLEN/8]; /* XXX very silly size! */ - 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 && act != COPY) { - snprintf(buf, sizeof(buf), - "unknown format, %s skipped in %s\n", - sizefmt(s1buf, sizeof(s1buf), rdcnt), - timefmt(tbuf, sizeof(tbuf), rdcnt, secs, "bytes")); - if (n == 0) - (void)fprintf(outf, "%s: %s", argv0, buf); - else - (void)write(STDERR_FILENO, buf, strlen(buf)); - return; - } - - - if (n != 0 && *archd.name) { - snprintf(buf, sizeof(buf), "Working on `%s' (%s)\n", - archd.name, sizefmt(s1buf, sizeof(s1buf), archd.sb.st_size)); - (void)write(STDERR_FILENO, buf, strlen(buf)); - } - - - if (act == COPY) { - snprintf(buf, sizeof(buf), - "%lu files in %s\n", - (unsigned long)flcnt, - timefmt(tbuf, sizeof(tbuf), flcnt, secs, "files")); - } else { - 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, "bytes")); - } - 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(const char *name) -{ - /* First fdochdir() back... */ - if (fdochdir(cwdfd) == -1) - return -1; - if (dochdir(name) == -1) - return -1; - return 0; -} |