Support gnu long filename extensions by default for tar on create, list, and

extract. We now generate GNU tar archives by default ("ustar  ^@" instead of
"ustar^@00"). GNU extensions can be disabled with --strict.

XXX: long symlinks untested.
This commit is contained in:
christos 2002-10-16 03:46:07 +00:00
parent 2da742de79
commit f70dfaaf73
5 changed files with 198 additions and 71 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: ar_io.c,v 1.26 2002/10/13 17:19:33 christos Exp $ */
/* $NetBSD: ar_io.c,v 1.27 2002/10/16 03:46:07 christos Exp $ */
/*-
* Copyright (c) 1992 Keith Muller.
@ -42,7 +42,7 @@
#if 0
static char sccsid[] = "@(#)ar_io.c 8.2 (Berkeley) 4/18/94";
#else
__RCSID("$NetBSD: ar_io.c,v 1.26 2002/10/13 17:19:33 christos Exp $");
__RCSID("$NetBSD: ar_io.c,v 1.27 2002/10/16 03:46:07 christos Exp $");
#endif
#endif /* not lint */
@ -776,7 +776,7 @@ ar_read(char *buf, int cnt)
lstrval = res;
if (res < 0)
syswarn(1, errno, "Failed read on archive volume %d", arvol);
else if (!is_oldgnutar)
else if (!is_gnutar)
tty_warn(0, "End of archive volume %d reached", arvol);
return(res);
}
@ -1395,7 +1395,7 @@ ar_next(void)
if (sigprocmask(SIG_SETMASK, &o_mask, (sigset_t *)NULL) < 0)
syswarn(0, errno, "Unable to restore signal mask");
if (done || !wr_trail || is_oldgnutar || force_one_volume)
if (done || !wr_trail || is_gnutar || force_one_volume)
return(-1);
tty_prnt("\nATTENTION! %s archive volume change required.\n", argv0);

View File

@ -1,4 +1,4 @@
/* $NetBSD: ar_subs.c,v 1.17 2002/10/12 15:39:29 christos Exp $ */
/* $NetBSD: ar_subs.c,v 1.18 2002/10/16 03:46:08 christos Exp $ */
/*-
* Copyright (c) 1992 Keith Muller.
@ -42,7 +42,7 @@
#if 0
static char sccsid[] = "@(#)ar_subs.c 8.2 (Berkeley) 4/18/94";
#else
__RCSID("$NetBSD: ar_subs.c,v 1.17 2002/10/12 15:39:29 christos Exp $");
__RCSID("$NetBSD: ar_subs.c,v 1.18 2002/10/16 03:46:08 christos Exp $");
#endif
#endif /* not lint */
@ -107,6 +107,16 @@ list(void)
* step through the archive until the format says it is done
*/
while (next_head(arcn) == 0) {
if (arcn->type == PAX_GLL || arcn->type == PAX_GLF) {
/*
* we need to read, to get the real filename
*/
off_t cnt;
if (!(*frmt->rd_data)(arcn, -1, &cnt));
(void)rd_skip(cnt + arcn->pad);
continue;
}
if (arcn->name[0] == '/' && !check_Aflag()) {
memmove(arcn->name, arcn->name + 1, strlen(arcn->name));
}
@ -133,7 +143,6 @@ list(void)
if (res == 0)
ls_list(arcn, now, stdout);
}
/*
* skip to next archive format header using values calculated
* by the format header read routine
@ -192,8 +201,14 @@ extract(void)
* says it is done
*/
while (next_head(arcn) == 0) {
int gnu_longlink_hack =
(arcn->type == PAX_GLL || arcn->type == PAX_GLF);
if (arcn->type == PAX_GLL || arcn->type == PAX_GLF) {
/*
* we need to read, to get the real filename
*/
if (!(*frmt->rd_data)(arcn, -1, &cnt));
(void)rd_skip(cnt + arcn->pad);
continue;
}
if (arcn->name[0] == '/' && !check_Aflag()) {
memmove(arcn->name, arcn->name + 1, strlen(arcn->name));
@ -202,19 +217,17 @@ extract(void)
* check for pattern, and user specified options match. When
* all the patterns are matched we are done
*/
if (!gnu_longlink_hack) {
if ((res = pat_match(arcn)) < 0)
break;
if ((res = pat_match(arcn)) < 0)
break;
if ((res > 0) || (sel_chk(arcn) != 0)) {
/*
* file is not selected. skip past any file
* data and padding and go back for the next
* archive member
*/
(void)rd_skip(arcn->skip + arcn->pad);
continue;
}
if ((res > 0) || (sel_chk(arcn) != 0)) {
/*
* file is not selected. skip past any file
* data and padding and go back for the next
* archive member
*/
(void)rd_skip(arcn->skip + arcn->pad);
continue;
}
/*
@ -227,8 +240,7 @@ extract(void)
* file AFTER the name mod. In honesty the pax spec is probably
* flawed in this respect. ignore this for GNU long links.
*/
if ((uflag || Dflag) && ((lstat(arcn->name, &sb) == 0)) &&
!gnu_longlink_hack) {
if ((uflag || Dflag) && ((lstat(arcn->name, &sb) == 0))) {
if (uflag && Dflag) {
if ((arcn->sb.st_mtime <= sb.st_mtime) &&
(arcn->sb.st_ctime <= sb.st_ctime)) {
@ -264,8 +276,7 @@ extract(void)
* Non standard -Y and -Z flag. When the existing file is
* same age or newer skip; ignore this for GNU long links.
*/
if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0)) &&
!gnu_longlink_hack) {
if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0))) {
if (Yflag && Zflag) {
if ((arcn->sb.st_mtime <= sb.st_mtime) &&
(arcn->sb.st_ctime <= sb.st_ctime)) {
@ -302,8 +313,7 @@ extract(void)
/*
* all ok, extract this member based on type
*/
if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG) &&
!gnu_longlink_hack) {
if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) {
/*
* process archive members that are not regular files.
* throw out padding and any data that might follow the
@ -328,9 +338,7 @@ extract(void)
* we have a file with data here. If we can not create it, skip
* over the data and purge the name from hard link table
*/
if (gnu_longlink_hack)
fd = -1; /* this tells the pax internals to DTRT */
else if ((fd = file_creat(arcn)) < 0) {
if ((fd = file_creat(arcn)) < 0) {
(void)rd_skip(arcn->skip + arcn->pad);
purg_lnk(arcn);
continue;
@ -340,8 +348,7 @@ extract(void)
* any unprocessed data
*/
res = (*frmt->rd_data)(arcn, fd, &cnt);
if (!gnu_longlink_hack)
file_close(arcn, fd);
file_close(arcn, fd);
if (vflag && vfpart) {
(void)putc('\n', listf);
vfpart = 0;
@ -1032,7 +1039,7 @@ next_head(ARCHD *arcn)
* storage device, better give the user the bad news.
*/
if ((ret == 0) || (rd_sync() < 0)) {
if (!is_oldgnutar)
if (!is_gnutar)
tty_warn(1,
"Premature end of file on archive read");
return(-1);

View File

@ -1,4 +1,4 @@
/* $NetBSD: extern.h,v 1.33 2002/10/15 16:16:29 christos Exp $ */
/* $NetBSD: extern.h,v 1.34 2002/10/16 03:46:08 christos Exp $ */
/*-
* Copyright (c) 1992 Keith Muller.
@ -295,7 +295,7 @@ u_int st_hash(char *, int, int);
/*
* tar.c
*/
extern int is_oldgnutar;
extern int is_gnutar;
int tar_endwr(void);
off_t tar_endrd(void);
int tar_trail(char *, int, int *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: options.c,v 1.47 2002/10/15 16:16:29 christos Exp $ */
/* $NetBSD: options.c,v 1.48 2002/10/16 03:46:08 christos Exp $ */
/*-
* Copyright (c) 1992 Keith Muller.
@ -42,7 +42,7 @@
#if 0
static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 4/18/94";
#else
__RCSID("$NetBSD: options.c,v 1.47 2002/10/15 16:16:29 christos Exp $");
__RCSID("$NetBSD: options.c,v 1.48 2002/10/16 03:46:08 christos Exp $");
#endif
#endif /* not lint */
@ -116,6 +116,7 @@ static int getline_error;
#define OPT_NORECURSE 12
#define OPT_FORCE_LOCAL 13
#define OPT_INSECURE 14
#define OPT_STRICT 15
/*
* Format specific routine table - MUST BE IN SORTED ORDER BY NAME
@ -698,6 +699,8 @@ struct option tar_longopts[] = {
{ "exclude-from", required_argument, 0, 'X' },
{ "compress", no_argument, 0, 'Z' },
{ "uncompress", no_argument, 0, 'Z' },
{ "strict", no_argument, 0,
OPT_STRICT },
{ "atime-preserve", no_argument, 0,
OPT_ATIME_PRESERVE },
{ "unlink", no_argument, 0,
@ -843,12 +846,12 @@ tar_options(int argc, char **argv)
* this is a create or extract operation.
*/
if (act == ARCHIVE) {
/* GNU tar: write V7 format archives. */
Oflag = 1;
/* 4.2BSD: don't add directory entries. */
if (opt_add("write_opt=nodir") < 0)
tar_usage();
/* GNU tar: write V7 format archives. */
frmt = &(fsub[F_TAR]);
} else {
/* SUS: don't preserve owner/group. */
pids = 0;
@ -1014,6 +1017,10 @@ tar_options(int argc, char **argv)
case OPT_INSECURE:
secure = 0;
break;
case OPT_STRICT:
/* disable gnu extensions */
is_gnutar = 0;
break;
default:
tar_usage();
break;
@ -1685,10 +1692,16 @@ bad_opt(void)
/*
* print all we were given
*/
tty_warn(1,"These format options are not supported");
tty_warn(1," These format options are not supported for %s",
frmt->name);
while ((opt = opt_next()) != NULL)
(void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value);
pax_usage();
if (strcmp(NM_TAR, argv0) == 0)
tar_usage();
else if (strcmp(NM_CPIO, argv0) == 0)
cpio_usage();
else
pax_usage();
return(0);
}
@ -1873,7 +1886,7 @@ void
pax_usage(void)
{
fprintf(stderr,
"usage: pax [-cdnvzO] [-E limit] [-f archive] [-N dbdir] [-s replstr] ...\n"
"Usage: pax [-cdnvzO] [-E limit] [-f archive] [-N dbdir] [-s replstr] ...\n"
" [-U user] ... [-G group] ... [-T [from_date][,to_date]] ...\n"
" [pattern ...]\n");
fprintf(stderr,
@ -1901,7 +1914,7 @@ pax_usage(void)
void
tar_usage(void)
{
(void)fputs("usage: tar -{crtux}[-befhmopqvwzHLOPXZ014578] [archive] "
(void)fputs("Usage: tar -{crtux}[-befhmopqvwzHLOPXZ014578] [archive] "
"[blocksize]\n"
" [-C directory] [-T file] [-s replstr] "
"[file ...]\n", stderr);
@ -1920,23 +1933,23 @@ cpio_usage(void)
#if 1
(void)fputs(
"usage: cpio -i [-BcdfmrStuv] [ -C blksize ] [ -H header ]\n",
"Usage: cpio -i [-BcdfmrStuv] [ -C blksize ] [ -H header ]\n",
stderr);
(void)fputs(" [ -I file ] [ pattern ... ]\n", stderr);
(void)fputs("usage: cpio -o [-aABcLv] [ -C bufsize ] [ -H header ]\n",
(void)fputs("Usage: cpio -o [-aABcLv] [ -C bufsize ] [ -H header ]\n",
stderr);
(void)fputs(" [ -O file ]\n", stderr);
(void)fputs("usage: cpio -p [ adlLmuv ] directory\n", stderr);
(void)fputs("Usage: cpio -p [ adlLmuv ] directory\n", stderr);
#else
/* no E, M, R, V, b, k or s */
(void)fputs("usage: cpio -i [-bBcdfkmrsStuvV] [ -C bufsize ]\n", stderr);
(void)fputs("Usage: cpio -i [-bBcdfkmrsStuvV] [ -C bufsize ]\n", stderr);
(void)fputs(" [ -E file ] [ -H header ] [ -I file [ -M message ] ]\n",
stderr);
(void)fputs(" [ -R id ] [ pattern ... ]\n", stderr);
(void)fputs("usage: cpio -o [-aABcLvV] [ -C bufsize ] [ -H header ]\n",
(void)fputs("Usage: cpio -o [-aABcLvV] [ -C bufsize ] [ -H header ]\n",
stderr);
(void)fputs(" [ -O file [ -M message ] ]\n", stderr);
(void)fputs("usage: cpio -p [ adlLmuvV ] [ -R id ] directory\n", stderr);
(void)fputs("Usage: cpio -p [ adlLmuvV ] [ -R id ] directory\n", stderr);
#endif
exit(1);
/* NOTREACHED */

View File

@ -1,4 +1,4 @@
/* $NetBSD: tar.c,v 1.25 2002/10/13 17:21:50 christos Exp $ */
/* $NetBSD: tar.c,v 1.26 2002/10/16 03:46:09 christos Exp $ */
/*-
* Copyright (c) 1992 Keith Muller.
@ -42,7 +42,7 @@
#if 0
static char sccsid[] = "@(#)tar.c 8.2 (Berkeley) 4/18/94";
#else
__RCSID("$NetBSD: tar.c,v 1.25 2002/10/13 17:21:50 christos Exp $");
__RCSID("$NetBSD: tar.c,v 1.26 2002/10/16 03:46:09 christos Exp $");
#endif
#endif /* not lint */
@ -68,6 +68,7 @@ __RCSID("$NetBSD: tar.c,v 1.25 2002/10/13 17:21:50 christos Exp $");
* Routines for reading, writing and header identify of various versions of tar
*/
static void longlink(ARCHD *);
static u_long tar_chksm(char *, int);
static char *name_split(char *, int);
static int ul_oct(u_long, char *, int, int);
@ -80,8 +81,12 @@ static int ull_oct(unsigned long long, char *, int, int);
*/
static int tar_nodir; /* do not write dirs under old tar */
int is_oldgnutar; /* skip end-ofvolume checks */
int is_gnutar = 1; /* behave like gnu tar; enable gnu
* extensions and skip end-ofvolume
* checks
*/
char *gnu_hack_string; /* ././@LongLink hackery */
int gnu_hack_len; /* len of gnu_hack_string */
/*
* tar_endwr()
@ -729,8 +734,9 @@ ustar_id(char *blk, int size)
return(-1);
if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0)
return(-1);
if (!strncmp(hd->magic, "ustar ", 8))
is_oldgnutar = 1;
/* This is GNU tar */
if (strncmp(hd->magic, "ustar ", 8) != 0)
is_gnutar = 0;
if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT))
return(-1);
return(0);
@ -775,7 +781,14 @@ ustar_rd(ARCHD *arcn, char *buf)
*dest++ = '/';
cnt++;
}
arcn->nlen = strlcpy(dest, hd->name, sizeof(arcn->name) - cnt);
if (gnu_hack_string) {
arcn->nlen = strlcpy(dest, gnu_hack_string,
sizeof(arcn->name) - cnt);
free(gnu_hack_string);
gnu_hack_string = NULL;
} else {
arcn->nlen = strlcpy(dest, hd->name, sizeof(arcn->name) - cnt);
}
/*
* follow the spec to the letter. we should only have mode bits, strip
@ -865,6 +878,27 @@ ustar_rd(ARCHD *arcn, char *buf)
arcn->ln_nlen = strlcpy(arcn->ln_name, hd->linkname,
sizeof(arcn->ln_name));
break;
case LONGLINKTYPE:
if (is_gnutar)
arcn->type = PAX_GLL;
/* FALLTHROUGH */
case LONGNAMETYPE:
if (is_gnutar) {
/*
* GNU long link/file; we tag these here and let the
* pax internals deal with it -- too ugly otherwise.
*/
if (hd->typeflag != LONGLINKTYPE)
arcn->type = PAX_GLF;
arcn->pad = TAR_PAD(arcn->sb.st_size);
arcn->skip = arcn->sb.st_size;
arcn->ln_name[0] = '\0';
arcn->ln_nlen = 0;
} else {
tty_warn(1, "GNU Long %s found in posix ustar archive.",
hd->typeflag == LONGLINKTYPE ? "Link" : "File");
}
break;
case CONTTYPE:
case AREGTYPE:
case REGTYPE:
@ -882,6 +916,37 @@ ustar_rd(ARCHD *arcn, char *buf)
return(0);
}
static void
longlink(ARCHD *arcn)
{
ARCHD larc;
memset(&larc, 0, sizeof(larc));
switch (arcn->type) {
case PAX_SLK:
case PAX_HRG:
case PAX_HLK:
larc.type = PAX_GLL;
larc.ln_nlen = strlcpy(larc.ln_name, "././@LongLink",
sizeof(larc.ln_name));
gnu_hack_string = arcn->ln_name;
gnu_hack_len = arcn->ln_nlen + 1;
break;
default:
larc.nlen = strlcpy(larc.name, "././@LongLink",
sizeof(larc.name));
gnu_hack_string = arcn->name;
gnu_hack_len = arcn->nlen + 1;
larc.type = PAX_GLF;
}
/*
* We need a longlink now.
*/
ustar_wr(&larc);
}
/*
* ustar_wr()
* write a ustar header for the file specified in the ARCHD to the archive
@ -914,9 +979,15 @@ ustar_wr(ARCHD *arcn)
* check the length of the linkname
*/
if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) ||
(arcn->type == PAX_HRG)) && (arcn->ln_nlen >= sizeof(hd->linkname))){
tty_warn(1, "Link name too long for ustar %s", arcn->ln_name);
return(1);
(arcn->type == PAX_HRG)) &&
(arcn->ln_nlen >= sizeof(hd->linkname))){
if (is_gnutar) {
longlink(arcn);
} else {
tty_warn(1, "Link name too long for ustar %s",
arcn->ln_name);
return(1);
}
}
/*
@ -924,8 +995,14 @@ ustar_wr(ARCHD *arcn)
* pt != arcn->name, the name has to be split
*/
if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) {
tty_warn(1, "File name too long for ustar %s", arcn->name);
return(1);
if (is_gnutar) {
longlink(arcn);
pt = arcn->name;
} else {
tty_warn(1, "File name too long for ustar %s",
arcn->name);
return(1);
}
}
/*
@ -981,38 +1058,58 @@ ustar_wr(ARCHD *arcn)
if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
goto out;
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 (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
if (ul_oct((u_long)gnu_hack_len, hd->size,
sizeof(hd->size), 3))
goto out;
break;
case PAX_GLF:
case PAX_REG:
case PAX_CTG:
default:
/*
* file data with this type, set the padding
*/
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);
if (arcn->type == PAX_GLF) {
hd->typeflag = LONGNAMETYPE;
arcn->pad = TAR_PAD(gnu_hack_len);
if (OFFT_OCT((u_long)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);
strncpy(hd->version, TVERSION, TVERSLEN);
if (is_gnutar)
hd->magic[TMAGLEN - 1] = hd->magic[TMAGLEN] = ' ';
else
strncpy(hd->version, TVERSION, TVERSLEN);
/*
* set the remaining fields. Some versions want all 16 bits of mode
@ -1040,6 +1137,16 @@ ustar_wr(ARCHD *arcn)
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);