#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.60 2023/07/20 20:10:59 lukem Exp $");
#endif
#endif
#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
#include "pax.h"
#include "options.h"
#include "extern.h"
#define DMOD 0666
#define EXT_MODE O_RDONLY
#define AR_MODE (O_WRONLY | O_CREAT | O_TRUNC)
#define APP_MODE O_RDWR
static char STDO[] = "<STDOUT>";
static char STDN[] = "<STDIN>";
static char NONE[] = "<NONE>";
static int arfd = -1;
static int artyp = ISREG;
static int arvol = 1;
static int lstrval = -1;
static int io_ok;
static int did_io;
static int done;
static struct stat arsb;
static int invld_rec;
static int wr_trail = 1;
static int can_unlnk = 0;
const char *arcname;
const char *gzip_program;
static pid_t zpid = -1;
time_t starttime;
int force_one_volume;
#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
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
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:
arcname = NONE;
lstrval = 1;
return 0;
}
if (arfd < 0)
return -1;
if (chdname != NULL)
if (dochdir(chdname) == -1)
return -1;
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;
if (artyp == ISREG && arsb.st_size == 0) {
switch (act) {
case LIST:
case EXTRACT:
return -1;
case APPND:
act = -ARCHIVE;
return -1;
case ARCHIVE:
break;
}
}
if (artyp != ISREG)
can_unlnk = 0;
if (act == ARCHIVE) {
blksz = rdblksz = wrblksz;
lstrval = 1;
return 0;
}
switch(artyp) {
case ISTAPE:
blksz = rdblksz = MAXBLK;
break;
case ISPIPE:
case ISBLK:
case ISCHR:
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;
if ((act == APPND) || (artyp == ISCHR))
blksz = rdblksz;
else
blksz = MAXBLK;
break;
case ISREG:
if ((act == APPND) && wrblksz && ((arsb.st_size%wrblksz)==0)){
blksz = rdblksz = wrblksz;
break;
}
for (rdblksz = MAXBLK; rdblksz > 0; rdblksz -= BLKMULT)
if ((arsb.st_size % rdblksz) == 0)
break;
if (rdblksz <= 0)
rdblksz = FILEBLK;
if (act == APPND)
blksz = rdblksz;
else
blksz = MAXBLK;
break;
default:
blksz = rdblksz = BLKMULT;
break;
}
lstrval = 1;
return 0;
}
void
ar_close(void)
{
int status;
if (arfd < 0) {
did_io = io_ok = flcnt = 0;
return;
}
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 (can_unlnk && (fstat(arfd, &arsb) == 0) && (S_ISREG(arsb.st_mode)) &&
(arsb.st_size == 0)) {
(void)unlink(arcname);
can_unlnk = 0;
}
if ((act == LIST || act == EXTRACT) && nflag && zpid > 0)
kill(zpid, SIGINT);
#ifdef SUPPORT_RMT
if (artyp == ISRMT)
(void)rmtclose(arfd);
else
#endif
(void)close(arfd);
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;
if (frmt != NULL)
++arvol;
if (!vflag && !Vflag) {
flcnt = 0;
return;
}
if (vfpart) {
(void)putc('\n', listf);
vfpart = 0;
}
if (frmt && strcmp(NM_CPIO, argv0) == 0) {
(void)fprintf(listf, OFFT_F " blocks\n",
(OFFT_T)((rdcnt ? rdcnt : wrcnt) / 5120));
}
ar_summary(0);
(void)fflush(listf);
flcnt = 0;
}
void
ar_drain(void)
{
int res;
char drbuf[MAXBLK];
if ((artyp != ISPIPE) || (lstrval <= 0))
return;
#ifdef SUPPORT_RMT
if (artyp == ISRMT) {
while ((res = rmtread_with_restart(arfd,
drbuf, sizeof(drbuf))) > 0)
continue;
} else {
#endif
while ((res = read_with_restart(arfd,
drbuf, sizeof(drbuf))) > 0)
continue;
#ifdef SUPPORT_RMT
}
#endif
lstrval = res;
}
int
ar_set_wr(void)
{
off_t cpos;
wr_trail = 0;
if (artyp != ISREG)
return 0;
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;
}
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
int
read_with_restart(int fd, void *buf, int bsz)
{
int r;
while (((r = read(fd, buf, bsz)) < 0) && errno == EINTR)
continue;
return r;
}
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
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
b += r;
bsz -= r;
nread += r;
} while (bsz > 0);
return nread ? nread : r;
}
#ifdef SYS_NO_RESTART
int
write_with_restart(int fd, void *buf, int bsz)
{
int r;
while (((r = write(fd, buf, bsz)) < 0) && errno == EINTR)
;
return r;
}
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
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
b += r;
bsz -= r;
written += r;
} while (bsz > 0);
return written ? written : r;
}
int
ar_read(char *buf, int cnt)
{
int res = 0;
if (lstrval <= 0)
return lstrval;
switch (artyp) {
#ifdef SUPPORT_RMT
case ISRMT:
if ((res = rmtread_with_restart(arfd, buf, cnt)) > 0) {
io_ok = 1;
return res;
}
break;
#endif
case ISTAPE:
if ((res = read_with_restart(arfd, buf, cnt)) > 0) {
io_ok = 1;
if (res != rdblksz) {
rdblksz = res;
if (rdblksz % BLKMULT)
invld_rec = 1;
}
return res;
}
break;
case ISREG:
case ISBLK:
case ISCHR:
case ISPIPE:
default:
if ((res = read_with_restart(arfd, buf, cnt)) > 0) {
io_ok = 1;
return res;
}
break;
}
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;
}
int
ar_write(char *buf, int bsz)
{
int res;
off_t cpos;
if (lstrval <= 0)
return lstrval;
if ((res = xwrite(arfd, buf, bsz)) == bsz) {
wr_trail = 1;
io_ok = 1;
return bsz;
}
if (res < 0)
lstrval = res;
else
lstrval = 0;
switch (artyp) {
case ISREG:
if ((res > 0) && (res % BLKMULT)) {
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 ((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
if (res >= 0)
break;
if (errno == EACCES) {
tty_warn(0,
"Write failed, archive is write protected.");
res = lstrval = 0;
return 0;
}
if ((errno == ENOSPC) || (errno == EIO) || (errno == ENXIO))
res = lstrval = 0;
break;
case ISPIPE:
default:
break;
}
if (res >= 0) {
if (res > 0)
wr_trail = 1;
io_ok = 1;
}
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;
}
int
ar_rdsync(void)
{
long fsbz;
off_t cpos;
off_t mpos;
#ifdef HAVE_SYS_MTIO_H
struct mtop mb;
#endif
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
case ISTAPE:
#ifdef HAVE_SYS_MTIO_H
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
if (ioctl(arfd, MTIOCTOP, &mb) < 0)
break;
#ifdef SUPPORT_RMT
}
#endif
lstrval = 1;
#else
tty_warn(1, "System does not have tape support");
#endif
break;
case ISREG:
case ISCHR:
case ISBLK:
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:
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;
}
int
ar_fow(off_t sksz, off_t *skipped)
{
off_t cpos;
off_t mpos;
*skipped = 0;
if (sksz <= 0)
return 0;
if (lstrval <= 0)
return lstrval;
if (artyp == ISTAPE || artyp == ISPIPE
#ifdef SUPPORT_RMT
|| artyp == ISRMT
#endif
)
return 0;
if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) >= 0) {
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;
}
syswarn(1, errno, "Forward positioning operation on archive failed");
lstrval = -1;
return -1;
}
int
ar_rev(off_t sksz)
{
off_t cpos;
#ifdef HAVE_SYS_MTIO_H
int phyblk;
struct mtop mb;
#endif
if (lstrval < 0)
return lstrval;
switch(artyp) {
case ISPIPE:
if (sksz <= 0)
break;
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;
if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) {
syswarn(1, errno,
"Unable to obtain current archive byte offset");
lstrval = -1;
return -1;
}
if ((cpos -= sksz) < (off_t)0L) {
if (arvol > 1) {
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
#ifdef HAVE_SYS_MTIO_H
if ((phyblk = get_phys()) <= 0) {
lstrval = -1;
return -1;
}
rdblksz = phyblk;
if (sksz <= 0)
break;
if (sksz % phyblk) {
tty_warn(1,
"Tape drive unable to backspace requested amount");
lstrval = -1;
return -1;
}
mb.mt_op = MTBSR;
mb.mt_count = sksz/phyblk;
if (
#ifdef SUPPORT_RMT
rmtioctl(arfd, MTIOCTOP, &mb)
#else
ioctl(arfd, MTIOCTOP, &mb)
#endif
< 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
static int
get_phys(void)
{
int padsz = 0;
int res;
int phyblk;
struct mtop mb;
char scbuf[MAXBLK];
if (lstrval == 1) {
#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
padsz += res;
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
< 0) {
syswarn(1, errno, "Unable to backspace over tape filemark.");
return -1;
}
mb.mt_op = MTBSR;
mb.mt_count = 1;
if (
#ifdef SUPPORT_RMT
rmtioctl(arfd, MTIOCTOP, &mb)
#else
ioctl(arfd, MTIOCTOP, &mb)
#endif
< 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
) <= 0) {
syswarn(1, errno, "Cannot determine archive tape blocksize.");
return -1;
}
while ((res =
#ifdef SUPPORT_RMT
rmtread_with_restart(arfd, scbuf, sizeof(scbuf))
#else
read_with_restart(arfd, scbuf, sizeof(scbuf))
#endif
) > 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
< 0) {
syswarn(1, errno, "Unable to backspace over tape filemark.");
return -1;
}
lstrval = 1;
if (padsz == 0)
return phyblk;
if (padsz % phyblk) {
tty_warn(1, "Tape drive unable to backspace requested amount");
return -1;
}
mb.mt_op = MTBSR;
mb.mt_count = padsz/phyblk;
if (
#ifdef SUPPORT_RMT
rmtioctl(arfd, MTIOCTOP, &mb)
#else
ioctl(arfd, MTIOCTOP, &mb)
#endif
< 0) {
syswarn(1, errno,
"Unable to backspace tape over %ld pad blocks",
(long)mb.mt_count);
return -1;
}
return phyblk;
}
#endif
int
ar_next(void)
{
char buf[PAXPATHLEN+2];
static char *arcfree = NULL;
sigset_t o_mask;
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 (strcmp(arcname, STDO) && strcmp(arcname, STDN) && (artyp != ISREG)
&& (artyp != ISPIPE)) {
if (artyp == ISTAPE
#ifdef SUPPORT_RMT
|| artyp == ISRMT
#endif
) {
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':
if (ar_open(arcname) >= 0)
return 0;
tty_prnt("Cannot re-open %s, try again\n",
arcname);
continue;
case 's':
case 'S':
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);
}
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;
}
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;
}
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");
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");
}
}
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[4096];
char tbuf[MAXPATHLEN/4];
char s1buf[MAXPATHLEN/8];
char s2buf[MAXPATHLEN/8];
FILE *outf;
if (act == LIST)
outf = stdout;
else
outf = stderr;
(void)time(&secs);
if ((secs -= starttime) == 0)
secs = 1;
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));
}
int
ar_dochdir(const char *name)
{
if (fdochdir(cwdfd) == -1)
return -1;
if (dochdir(name) == -1)
return -1;
return 0;
}