#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif
#include <sys/cdefs.h>
#if !defined(lint)
#if 0
static char sccsid[] = "@(#)tar.c 8.2 (Berkeley) 4/18/94";
#else
__RCSID("$NetBSD: tar.c,v 1.76 2024/08/05 13:37:27 riastradh Exp $");
#endif
#endif
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <ctype.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "pax.h"
#include "extern.h"
#include "tar.h"
extern struct stat tst;
static int expandname(char *, size_t, char **, size_t *, const char *, size_t);
static void longlink(ARCHD *, int);
static uint32_t tar_chksm(char *, int);
static char *name_split(char *, int);
static int u32_oct(uintmax_t, char *, int, int);
static int umax_oct(uintmax_t, char *, int, int);
static int tar_gnutar_exclude_one(const char *, size_t);
static int check_sum(char *, size_t, char *, size_t, int);
static int tar_nodir;
int is_gnutar;
static int seen_gnu_warning;
static char *gnu_hack_string;
static int gnu_hack_len;
char *gnu_name_string;
char *gnu_link_string;
size_t gnu_name_length;
size_t gnu_link_length;
static int gnu_short_trailer;
static const char LONG_LINK[] = "././@LongLink";
#ifdef _PAX_
char DEV_0[] = "/dev/rst0";
char DEV_1[] = "/dev/rst1";
char DEV_4[] = "/dev/rst4";
char DEV_5[] = "/dev/rst5";
char DEV_7[] = "/dev/rst7";
char DEV_8[] = "/dev/rst8";
#endif
static int
check_sum(char *hd, size_t hdlen, char *bl, size_t bllen, int quiet)
{
uint32_t hdck, blck;
hdck = asc_u32(hd, hdlen, OCT);
blck = tar_chksm(bl, bllen);
if (hdck != blck) {
if (!quiet)
tty_warn(0, "Header checksum %o does not match %o",
hdck, blck);
return -1;
}
return 0;
}
int
tar_endwr(void)
{
return wr_skip((off_t)(NULLCNT * BLKMULT));
}
off_t
tar_endrd(void)
{
return (off_t)((gnu_short_trailer ? 1 : NULLCNT) * BLKMULT);
}
int
tar_trail(char *buf, int in_resync, int *cnt)
{
int i;
gnu_short_trailer = 0;
for (i = 0; i < BLKMULT; ++i) {
if (buf[i] != '\0')
break;
}
if (i != BLKMULT)
return -1;
if (!in_resync) {
++*cnt;
if (is_gnutar) {
gnu_short_trailer = 1;
++*cnt;
}
if (*cnt >= NULLCNT)
return 0;
}
return 1;
}
static int
u32_oct(uintmax_t val, char *str, int len, int term)
{
char *pt;
uint64_t p;
p = val & TOP_HALF;
if (p && p != TOP_HALF)
return -1;
val &= BOTTOM_HALF;
pt = str + len - 1;
switch(term) {
case 3:
*pt-- = '\0';
break;
case 2:
*pt-- = ' ';
*pt-- = '\0';
break;
case 1:
*pt-- = ' ';
break;
case 0:
default:
*pt-- = '\0';
*pt-- = ' ';
break;
}
while (pt >= str) {
*pt-- = '0' + (char)(val & 0x7);
if ((val = val >> 3) == 0)
break;
}
while (pt >= str)
*pt-- = '0';
if (val != 0)
return -1;
return 0;
}
static int
umax_oct(uintmax_t val, char *str, int len, int term)
{
char *pt;
pt = str + len - 1;
switch(term) {
case 3:
*pt-- = '\0';
break;
case 2:
*pt-- = ' ';
*pt-- = '\0';
break;
case 1:
*pt-- = ' ';
break;
case 0:
default:
*pt-- = '\0';
*pt-- = ' ';
break;
}
while (pt >= str) {
*pt-- = '0' + (char)(val & 0x7);
if ((val = val >> 3) == 0)
break;
}
while (pt >= str)
*pt-- = '0';
if (val != 0)
return -1;
return 0;
}
static uint32_t
tar_chksm(char *blk, int len)
{
char *stop;
char *pt;
uint32_t chksm = BLNKSUM;
pt = blk;
stop = blk + CHK_OFFSET;
while (pt < stop)
chksm += (uint32_t)(*pt++ & 0xff);
pt += CHK_LEN;
stop = blk + len;
while (pt < stop)
chksm += (uint32_t)(*pt++ & 0xff);
return chksm;
}
int
tar_id(char *blk, int size)
{
HD_TAR *hd;
HD_USTAR *uhd;
static int is_ustar = -1;
if (size < BLKMULT)
return -1;
hd = (HD_TAR *)blk;
uhd = (HD_USTAR *)blk;
if (hd->name[0] == '\0')
return -1;
if (strncmp(uhd->magic, TMAGIC, TMAGLEN - 1) == 0) {
if (is_ustar == -1) {
is_ustar = 1;
return -1;
} else
tty_warn(0,
"Busted tar archive: has both ustar and old tar "
"records");
} else
is_ustar = 0;
return check_sum(hd->chksum, sizeof(hd->chksum), blk, BLKMULT, 1);
}
int
tar_opt(void)
{
OPLIST *opt;
while ((opt = opt_next()) != NULL) {
if (strcmp(opt->name, TAR_OPTION) ||
strcmp(opt->value, TAR_NODIR)) {
tty_warn(1,
"Unknown tar format -o option/value pair %s=%s",
opt->name, opt->value);
tty_warn(1,
"%s=%s is the only supported tar format option",
TAR_OPTION, TAR_NODIR);
return -1;
}
if ((act != APPND) && (act != ARCHIVE)) {
tty_warn(1, "%s=%s is only supported when writing.",
opt->name, opt->value);
return -1;
}
tar_nodir = 1;
}
return 0;
}
int
tar_rd(ARCHD *arcn, char *buf)
{
HD_TAR *hd;
char *pt;
if (tar_id(buf, BLKMULT) < 0)
return -1;
memset(arcn, 0, sizeof(*arcn));
arcn->org_name = arcn->name;
arcn->pat = NULL;
arcn->sb.st_nlink = 1;
hd = (HD_TAR *)buf;
if (hd->linkflag != LONGLINKTYPE && hd->linkflag != LONGNAMETYPE) {
arcn->nlen = expandname(arcn->name, sizeof(arcn->name),
&gnu_name_string, &gnu_name_length, hd->name,
sizeof(hd->name));
arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name),
&gnu_link_string, &gnu_link_length, hd->linkname,
sizeof(hd->linkname));
}
arcn->sb.st_mode = (mode_t)(asc_u32(hd->mode,sizeof(hd->mode),OCT) &
0xfff);
arcn->sb.st_uid = (uid_t)asc_u32(hd->uid, sizeof(hd->uid), OCT);
arcn->sb.st_gid = (gid_t)asc_u32(hd->gid, sizeof(hd->gid), OCT);
arcn->sb.st_size = (off_t)ASC_OFFT(hd->size, sizeof(hd->size), OCT);
if (arcn->sb.st_size == -1)
return -1;
arcn->sb.st_mtime = (time_t)(int32_t)asc_u32(hd->mtime, sizeof(hd->mtime), OCT);
arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
pt = &(arcn->name[arcn->nlen - 1]);
arcn->pad = 0;
arcn->skip = 0;
switch(hd->linkflag) {
case SYMTYPE:
arcn->type = PAX_SLK;
arcn->sb.st_mode |= S_IFLNK;
break;
case LNKTYPE:
arcn->type = PAX_HLK;
arcn->sb.st_nlink = 2;
arcn->sb.st_mode |= S_IFREG;
break;
case LONGLINKTYPE:
case LONGNAMETYPE:
if (hd->linkflag != LONGLINKTYPE)
arcn->type = PAX_GLF;
else
arcn->type = PAX_GLL;
arcn->pad = TAR_PAD(arcn->sb.st_size);
arcn->skip = arcn->sb.st_size;
break;
case AREGTYPE:
case REGTYPE:
case DIRTYPE:
default:
if (*pt == '/' || hd->linkflag == DIRTYPE) {
arcn->type = PAX_DIR;
arcn->sb.st_mode |= S_IFDIR;
arcn->sb.st_nlink = 2;
} else {
arcn->type = PAX_REG;
arcn->sb.st_mode |= S_IFREG;
arcn->pad = TAR_PAD(arcn->sb.st_size);
arcn->skip = arcn->sb.st_size;
}
break;
}
if (*pt == '/') {
*pt = '\0';
--arcn->nlen;
}
return 0;
}
int
tar_wr(ARCHD *arcn)
{
HD_TAR *hd;
int len;
uintmax_t mtime;
char hdblk[sizeof(HD_TAR)];
switch(arcn->type) {
case PAX_DIR:
if (tar_nodir)
return 1;
break;
case PAX_CHR:
tty_warn(1, "Tar cannot archive a character device %s",
arcn->org_name);
return 1;
case PAX_BLK:
tty_warn(1,
"Tar cannot archive a block device %s", arcn->org_name);
return 1;
case PAX_SCK:
tty_warn(1, "Tar cannot archive a socket %s", arcn->org_name);
return 1;
case PAX_FIF:
tty_warn(1, "Tar cannot archive a fifo %s", arcn->org_name);
return 1;
case PAX_SLK:
case PAX_HLK:
case PAX_HRG:
if (arcn->ln_nlen > (int)sizeof(hd->linkname)) {
tty_warn(1,"Link name too long for tar %s",
arcn->ln_name);
return 1;
}
break;
case PAX_REG:
case PAX_CTG:
default:
break;
}
len = arcn->nlen;
if (arcn->type == PAX_DIR)
++len;
if (len >= (int)sizeof(hd->name)) {
tty_warn(1, "File name too long for tar %s", arcn->name);
return 1;
}
memset(hdblk, 0, sizeof(hdblk));
hd = (HD_TAR *)hdblk;
strlcpy(hd->name, arcn->name, sizeof(hd->name));
arcn->pad = 0;
if (arcn->type == PAX_DIR) {
hd->linkflag = AREGTYPE;
hd->name[len-1] = '/';
if (u32_oct((uintmax_t)0L, hd->size, sizeof(hd->size), 1))
goto out;
} else if (arcn->type == PAX_SLK) {
hd->linkflag = SYMTYPE;
strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname));
if (u32_oct((uintmax_t)0L, hd->size, sizeof(hd->size), 1))
goto out;
} else if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) {
hd->linkflag = LNKTYPE;
strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname));
if (u32_oct((uintmax_t)0L, hd->size, sizeof(hd->size), 1))
goto out;
} else {
hd->linkflag = AREGTYPE;
if (OFFT_OCT(arcn->sb.st_size, hd->size, sizeof(hd->size), 1)) {
tty_warn(1,"File is too large for tar %s",
arcn->org_name);
return 1;
}
arcn->pad = TAR_PAD(arcn->sb.st_size);
}
mtime = tst.st_ino ? tst.st_mtime : arcn->sb.st_mtime;
if (u32_oct((uintmax_t)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 0) ||
u32_oct((uintmax_t)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 0) ||
u32_oct((uintmax_t)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 0) ||
u32_oct(mtime, hd->mtime, sizeof(hd->mtime), 1))
goto out;
if (u32_oct(tar_chksm(hdblk, sizeof(HD_TAR)), hd->chksum,
sizeof(hd->chksum), 3))
goto out;
if (wr_rdbuf(hdblk, sizeof(HD_TAR)) < 0)
return -1;
if (wr_skip((off_t)(BLKMULT - sizeof(HD_TAR))) < 0)
return -1;
if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG))
return 0;
return 1;
out:
tty_warn(1, "Tar header field is too small for %s", arcn->org_name);
return 1;
}
int
ustar_strd(void)
{
return 0;
}
int
ustar_stwr(void)
{
return 0;
}
int
ustar_id(char *blk, int size)
{
HD_USTAR *hd;
if (size < BLKMULT)
return -1;
hd = (HD_USTAR *)blk;
if (hd->name[0] == '\0')
return -1;
if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0)
return -1;
if (strncmp(hd->magic, "ustar ", 8) == 0 && !is_gnutar &&
!seen_gnu_warning) {
seen_gnu_warning = 1;
tty_warn(0,
"Trying to read GNU tar archive with GNU extensions and end-of-volume checks off");
}
return check_sum(hd->chksum, sizeof(hd->chksum), blk, BLKMULT, 0);
}
int
ustar_rd(ARCHD *arcn, char *buf)
{
HD_USTAR *hd;
char *dest;
int cnt;
dev_t devmajor;
dev_t devminor;
if (ustar_id(buf, BLKMULT) < 0)
return -1;
memset(arcn, 0, sizeof(*arcn));
arcn->org_name = arcn->name;
arcn->pat = NULL;
arcn->sb.st_nlink = 1;
hd = (HD_USTAR *)buf;
dest = arcn->name;
if (*(hd->prefix) != '\0') {
cnt = strlcpy(arcn->name, hd->prefix, sizeof(arcn->name));
dest += cnt;
*dest++ = '/';
cnt++;
} else {
cnt = 0;
}
if (hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) {
arcn->nlen = expandname(dest, sizeof(arcn->name) - cnt,
&gnu_name_string, &gnu_name_length, hd->name,
sizeof(hd->name)) + cnt;
arcn->ln_nlen = expandname(arcn->ln_name,
sizeof(arcn->ln_name), &gnu_link_string, &gnu_link_length,
hd->linkname, sizeof(hd->linkname));
}
arcn->sb.st_mode = (mode_t)(asc_u32(hd->mode, sizeof(hd->mode), OCT) &
0xfff);
arcn->sb.st_size = (off_t)ASC_OFFT(hd->size, sizeof(hd->size), OCT);
if (arcn->sb.st_size == -1)
return -1;
arcn->sb.st_mtime = (time_t)(int32_t)asc_u32(hd->mtime, sizeof(hd->mtime), OCT);
arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
hd->gname[sizeof(hd->gname) - 1] = '\0';
if (gid_from_group(hd->gname, &(arcn->sb.st_gid)) < 0)
arcn->sb.st_gid = (gid_t)asc_u32(hd->gid, sizeof(hd->gid), OCT);
hd->uname[sizeof(hd->uname) - 1] = '\0';
if (uid_from_user(hd->uname, &(arcn->sb.st_uid)) < 0)
arcn->sb.st_uid = (uid_t)asc_u32(hd->uid, sizeof(hd->uid), OCT);
arcn->pad = 0;
arcn->skip = 0;
arcn->sb.st_rdev = (dev_t)0;
switch(hd->typeflag) {
case FIFOTYPE:
arcn->type = PAX_FIF;
arcn->sb.st_mode |= S_IFIFO;
break;
case DIRTYPE:
arcn->type = PAX_DIR;
arcn->sb.st_mode |= S_IFDIR;
arcn->sb.st_nlink = 2;
if (arcn->name[arcn->nlen - 1] == '/')
arcn->name[--arcn->nlen] = '\0';
break;
case BLKTYPE:
case CHRTYPE:
if (hd->typeflag == BLKTYPE) {
arcn->type = PAX_BLK;
arcn->sb.st_mode |= S_IFBLK;
} else {
arcn->type = PAX_CHR;
arcn->sb.st_mode |= S_IFCHR;
}
devmajor = (dev_t)asc_u32(hd->devmajor,sizeof(hd->devmajor),OCT);
devminor = (dev_t)asc_u32(hd->devminor,sizeof(hd->devminor),OCT);
arcn->sb.st_rdev = TODEV(devmajor, devminor);
break;
case SYMTYPE:
case LNKTYPE:
if (hd->typeflag == SYMTYPE) {
arcn->type = PAX_SLK;
arcn->sb.st_mode |= S_IFLNK;
} else {
arcn->type = PAX_HLK;
arcn->sb.st_mode |= S_IFREG;
arcn->sb.st_nlink = 2;
}
break;
case LONGLINKTYPE:
case LONGNAMETYPE:
if (is_gnutar) {
if (hd->typeflag != LONGLINKTYPE)
arcn->type = PAX_GLF;
else
arcn->type = PAX_GLL;
arcn->pad = TAR_PAD(arcn->sb.st_size);
arcn->skip = arcn->sb.st_size;
} else {
tty_warn(1, "GNU Long %s found in posix ustar archive.",
hd->typeflag == LONGLINKTYPE ? "Link" : "File");
}
break;
case FILEXTYPE:
case GLOBXTYPE:
tty_warn(0, "%s extended headers posix ustar archive."
" Extracting as plain files. Following files might be"
" in the wrong directory or have wrong attributes.",
hd->typeflag == FILEXTYPE ? "File" : "Global");
case CONTTYPE:
case AREGTYPE:
case REGTYPE:
default:
arcn->type = PAX_REG;
arcn->pad = TAR_PAD(arcn->sb.st_size);
arcn->skip = arcn->sb.st_size;
arcn->sb.st_mode |= S_IFREG;
break;
}
return 0;
}
static int
expandname(char *buf, size_t len, char **gnu_name, size_t *gnu_length,
const char *name, size_t nlen)
{
if (*gnu_name) {
len = strlcpy(buf, *gnu_name, len);
free(*gnu_name);
*gnu_name = NULL;
*gnu_length = 0;
} else {
if (len > ++nlen)
len = nlen;
len = strlcpy(buf, name, len);
}
return len;
}
static void
longlink(ARCHD *arcn, int type)
{
ARCHD larc;
(void)memset(&larc, 0, sizeof(larc));
larc.type = type;
larc.nlen = strlcpy(larc.name, LONG_LINK, sizeof(larc.name));
switch (type) {
case PAX_GLL:
gnu_hack_string = arcn->ln_name;
gnu_hack_len = arcn->ln_nlen + 1;
break;
case PAX_GLF:
gnu_hack_string = arcn->name;
gnu_hack_len = arcn->nlen + 1;
break;
default:
errx(1, "Invalid type in GNU longlink %d", type);
}
ustar_wr(&larc);
}
static int
size_err(const char *what, ARCHD *arcn)
{
tty_warn(1, "Ustar %s header field is too small for %s",
what, arcn->org_name);
return 1;
}
int
ustar_wr(ARCHD *arcn)
{
HD_USTAR *hd;
char *pt;
uintmax_t mtime;
char hdblk[sizeof(HD_USTAR)];
const char *user, *group;
switch (arcn->type) {
case PAX_SCK:
if (!is_gnutar)
tty_warn(1, "Ustar cannot archive a socket %s",
arcn->org_name);
return 1;
case PAX_SLK:
case PAX_HLK:
case PAX_HRG:
if (arcn->ln_nlen >= (int)sizeof(hd->linkname)) {
if (is_gnutar) {
longlink(arcn, PAX_GLL);
} else {
tty_warn(1, "Link name too long for ustar %s",
arcn->ln_name);
return 1;
}
}
break;
default:
break;
}
if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) {
if (is_gnutar) {
longlink(arcn, PAX_GLF);
pt = arcn->name;
} else {
tty_warn(1, "File name too long for ustar %s",
arcn->name);
return 1;
}
}
memset(hdblk, 0, sizeof(hdblk));
hd = (HD_USTAR *)hdblk;
arcn->pad = 0L;
if (pt != arcn->name) {
*pt = '\0';
strlcpy(hd->prefix, arcn->name, sizeof(hd->prefix));
*pt++ = '/';
}
strlcpy(hd->name, pt, sizeof(hd->name));
switch(arcn->type) {
case PAX_DIR:
hd->typeflag = DIRTYPE;
if (u32_oct((uintmax_t)0L, hd->size, sizeof(hd->size), 3))
return size_err("DIRTYPE", arcn);
break;
case PAX_CHR:
case PAX_BLK:
if (arcn->type == PAX_CHR)
hd->typeflag = CHRTYPE;
else
hd->typeflag = BLKTYPE;
if (u32_oct((uintmax_t)MAJOR(arcn->sb.st_rdev), hd->devmajor,
sizeof(hd->devmajor), 3) ||
u32_oct((uintmax_t)MINOR(arcn->sb.st_rdev), hd->devminor,
sizeof(hd->devminor), 3) ||
u32_oct((uintmax_t)0L, hd->size, sizeof(hd->size), 3))
return size_err("DEVTYPE", arcn);
break;
case PAX_FIF:
hd->typeflag = FIFOTYPE;
if (u32_oct((uintmax_t)0L, hd->size, sizeof(hd->size), 3))
return size_err("FIFOTYPE", arcn);
break;
case PAX_GLL:
case PAX_SLK:
case PAX_HLK:
case PAX_HRG:
if (arcn->type == PAX_SLK)
hd->typeflag = SYMTYPE;
else if (arcn->type == PAX_GLL)
hd->typeflag = LONGLINKTYPE;
else
hd->typeflag = LNKTYPE;
strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname));
if (u32_oct((uintmax_t)gnu_hack_len, hd->size,
sizeof(hd->size), 3))
return size_err("LINKTYPE", arcn);
break;
case PAX_GLF:
case PAX_REG:
case PAX_CTG:
default:
if (arcn->type == PAX_GLF) {
hd->typeflag = LONGNAMETYPE;
arcn->pad = TAR_PAD(gnu_hack_len);
if (OFFT_OCT((uint32_t)gnu_hack_len, hd->size,
sizeof(hd->size), 3)) {
tty_warn(1,"File is too long for ustar %s",
arcn->org_name);
return 1;
}
} else {
if (arcn->type == PAX_CTG)
hd->typeflag = CONTTYPE;
else
hd->typeflag = REGTYPE;
arcn->pad = TAR_PAD(arcn->sb.st_size);
if (OFFT_OCT(arcn->sb.st_size, hd->size,
sizeof(hd->size), 3)) {
tty_warn(1,"File is too long for ustar %s",
arcn->org_name);
return 1;
}
}
break;
}
strncpy(hd->magic, TMAGIC, TMAGLEN);
if (is_gnutar)
hd->magic[TMAGLEN - 1] = hd->version[0] = ' ';
else
strncpy(hd->version, TVERSION, TVERSLEN);
if (u32_oct((uintmax_t)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 3))
return size_err("MODE", arcn);
if (u32_oct((uintmax_t)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 3))
return size_err("UID", arcn);
if (u32_oct((uintmax_t)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 3))
return size_err("GID", arcn);
mtime = tst.st_ino ? tst.st_mtime : arcn->sb.st_mtime;
if (u32_oct(mtime, hd->mtime, sizeof(hd->mtime), 3))
return size_err("MTIME", arcn);
user = user_from_uid(arcn->sb.st_uid, 1);
group = group_from_gid(arcn->sb.st_gid, 1);
strncpy(hd->uname, user ? user : "", sizeof(hd->uname));
strncpy(hd->gname, group ? group : "", sizeof(hd->gname));
if (u32_oct(tar_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum,
sizeof(hd->chksum), 3))
return size_err("CHKSUM", arcn);
if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0)
return -1;
if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0)
return -1;
if (gnu_hack_string) {
int res = wr_rdbuf(gnu_hack_string, gnu_hack_len);
int pad = gnu_hack_len;
gnu_hack_string = NULL;
gnu_hack_len = 0;
if (res < 0)
return -1;
if (wr_skip((off_t)(BLKMULT - (pad % BLKMULT))) < 0)
return -1;
}
if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG))
return 0;
return 1;
}
static char *
name_split(char *name, int len)
{
char *start;
if (len < TNMSZ)
return name;
if (is_gnutar || len > (TPFSZ + TNMSZ))
return NULL;
start = name + len - TNMSZ;
while ((*start != '\0') && (*start != '/'))
++start;
if (*start == '\0')
return NULL;
len = start - name;
if ((len >= TPFSZ) || (len == 0))
return NULL;
return start;
}
static int
tar_gnutar_exclude_one(const char *line, size_t len)
{
char sbuf[MAXPATHLEN * 2 + 1];
char rabuf[MAXPATHLEN * 2 + 1 + 1 + 2 + 4 + 4];
size_t i;
int j = 0;
if (line[len - 1] == '\n')
len--;
for (i = 0; i < len; i++) {
if (line[i] == '*')
sbuf[j++] = '.';
else if (line[i] == '?') {
sbuf[j++] = '.';
continue;
} else if (!isalnum((unsigned char)line[i]) &&
!isblank((unsigned char)line[i]))
sbuf[j++] = '\\';
sbuf[j++] = line[i];
}
sbuf[j] = '\0';
if (line[0] != '/') {
(void)snprintf(rabuf, sizeof rabuf, "/.*\\/%s$//", sbuf);
if (rep_add(rabuf) < 0)
return (-1);
(void)snprintf(rabuf, sizeof rabuf, "/.*\\/%s\\/.*//", sbuf);
if (rep_add(rabuf) < 0)
return (-1);
}
(void)snprintf(rabuf, sizeof rabuf, "/^%s$//", sbuf);
if (rep_add(rabuf) < 0)
return (-1);
(void)snprintf(rabuf, sizeof rabuf, "/^%s\\/.*//", sbuf);
if (rep_add(rabuf) < 0)
return (-1);
return (0);
}
int
tar_gnutar_minus_minus_exclude(const char *path)
{
size_t len = strlen(path);
if (len > MAXPATHLEN)
tty_warn(0, "pathname too long: %s", path);
return (tar_gnutar_exclude_one(path, len));
}
int
tar_gnutar_X_compat(const char *path)
{
char *line;
FILE *fp;
int lineno = 0;
size_t len;
if (path[0] == '-' && path[1] == '\0')
fp = stdin;
else {
fp = fopen(path, "r");
if (fp == NULL) {
tty_warn(1, "cannot open %s: %s", path,
strerror(errno));
return -1;
}
}
while ((line = fgetln(fp, &len))) {
lineno++;
if (len > MAXPATHLEN) {
tty_warn(0, "pathname too long, line %d of %s",
lineno, path);
}
if (tar_gnutar_exclude_one(line, len))
return -1;
}
if (fp != stdin)
fclose(fp);
return 0;
}