summaryrefslogtreecommitdiff
path: root/archivers/pax/files/tar.c
diff options
context:
space:
mode:
Diffstat (limited to 'archivers/pax/files/tar.c')
-rw-r--r--archivers/pax/files/tar.c93
1 files changed, 61 insertions, 32 deletions
diff --git a/archivers/pax/files/tar.c b/archivers/pax/files/tar.c
index 14cbd7562d5..b02ee64421e 100644
--- a/archivers/pax/files/tar.c
+++ b/archivers/pax/files/tar.c
@@ -1,4 +1,4 @@
-/* $NetBSD: tar.c,v 1.9 2004/08/21 03:28:56 jlam Exp $ */
+/* $NetBSD: tar.c,v 1.10 2005/12/01 03:00:01 minskim Exp $ */
/*-
* Copyright (c) 1992 Keith Muller.
@@ -48,7 +48,7 @@
#if 0
static char sccsid[] = "@(#)tar.c 8.2 (Berkeley) 4/18/94";
#else
-__RCSID("$NetBSD: tar.c,v 1.9 2004/08/21 03:28:56 jlam Exp $");
+__RCSID("$NetBSD: tar.c,v 1.10 2005/12/01 03:00:01 minskim Exp $");
#endif
#endif /* not lint */
@@ -98,7 +98,7 @@ __RCSID("$NetBSD: tar.c,v 1.9 2004/08/21 03:28:56 jlam Exp $");
* Routines for reading, writing and header identify of various versions of tar
*/
-static int expandname(char *, size_t, char **, const char *, size_t);
+static int expandname(char *, size_t, char **, size_t *, const char *, size_t);
static void longlink(ARCHD *, int);
static u_long tar_chksm(char *, int);
static char *name_split(char *, int);
@@ -123,6 +123,8 @@ static char *gnu_hack_string; /* ././@LongLink hackery */
static int gnu_hack_len; /* len of gnu_hack_string */
char *gnu_name_string; /* ././@LongLink hackery name */
char *gnu_link_string; /* ././@LongLink hackery link */
+size_t gnu_name_length; /* ././@LongLink hackery name */
+size_t gnu_link_length; /* ././@LongLink hackery link */
static int gnu_short_trailer; /* gnu short trailer */
static const char LONG_LINK[] = "././@LongLink";
@@ -401,6 +403,7 @@ tar_id(char *blk, int size)
{
HD_TAR *hd;
HD_USTAR *uhd;
+ static int is_ustar = -1;
if (size < BLKMULT)
return(-1);
@@ -416,8 +419,16 @@ tar_id(char *blk, int size)
*/
if (hd->name[0] == '\0')
return(-1);
- if (strncmp(uhd->magic, TMAGIC, TMAGLEN - 1) == 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);
}
@@ -489,9 +500,11 @@ tar_rd(ARCHD *arcn, char *buf)
hd = (HD_TAR *)buf;
if (hd->linkflag != LONGLINKTYPE && hd->linkflag != LONGNAMETYPE) {
arcn->nlen = expandname(arcn->name, sizeof(arcn->name),
- &gnu_name_string, hd->name, sizeof(hd->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, hd->linkname, sizeof(hd->linkname));
+ &gnu_link_string, &gnu_link_length, hd->linkname,
+ sizeof(hd->linkname));
}
arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode,sizeof(hd->mode),OCT) &
0xfff);
@@ -856,10 +869,11 @@ ustar_rd(ARCHD *arcn, char *buf)
if (hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) {
arcn->nlen = expandname(dest, sizeof(arcn->name) - cnt,
- &gnu_name_string, hd->name, sizeof(hd->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, hd->linkname,
- sizeof(hd->linkname));
+ sizeof(arcn->ln_name), &gnu_link_string, &gnu_link_length,
+ hd->linkname, sizeof(hd->linkname));
}
/*
@@ -979,13 +993,14 @@ ustar_rd(ARCHD *arcn, char *buf)
}
static int
-expandname(char *buf, size_t len, char **gnu_name, const char *name,
- size_t nlen)
+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;
@@ -1035,6 +1050,17 @@ longlink(ARCHD *arcn, int type)
* data to write after the header, -1 if archive write failed
*/
+static int
+size_err(const char *what, ARCHD *arcn)
+{
+ /*
+ * header field is out of range
+ */
+ tty_warn(1, "Ustar %s header field is too small for %s",
+ what, arcn->org_name);
+ return 1;
+}
+
int
ustar_wr(ARCHD *arcn)
{
@@ -1121,7 +1147,7 @@ ustar_wr(ARCHD *arcn)
case PAX_DIR:
hd->typeflag = DIRTYPE;
if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
- goto out;
+ return size_err("DIRTYPE", arcn);
break;
case PAX_CHR:
case PAX_BLK:
@@ -1134,12 +1160,12 @@ ustar_wr(ARCHD *arcn)
ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor,
sizeof(hd->devminor), 3) ||
ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
- goto out;
+ return size_err("DEVTYPE", arcn);
break;
case PAX_FIF:
hd->typeflag = FIFOTYPE;
if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3))
- goto out;
+ return size_err("FIFOTYPE", arcn);
break;
case PAX_GLL:
case PAX_SLK:
@@ -1154,7 +1180,7 @@ ustar_wr(ARCHD *arcn)
strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname));
if (ul_oct((u_long)gnu_hack_len, hd->size,
sizeof(hd->size), 3))
- goto out;
+ return size_err("LINKTYPE", arcn);
break;
case PAX_GLF:
case PAX_REG:
@@ -1198,11 +1224,14 @@ ustar_wr(ARCHD *arcn)
* set the remaining fields. Some versions want all 16 bits of mode
* we better humor them (they really do not meet spec though)....
*/
- if (ul_oct((u_long)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 3) ||
- ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 3) ||
- ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 3) ||
- ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),3))
- goto out;
+ if (ul_oct((u_long)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 3))
+ return size_err("MODE", arcn);
+ if (ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 3))
+ return size_err("UID", arcn);
+ if (ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 3))
+ return size_err("GID", arcn);
+ if (ul_oct((u_long)arcn->sb.st_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));
@@ -1215,7 +1244,7 @@ ustar_wr(ARCHD *arcn)
*/
if (ul_oct(tar_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum,
sizeof(hd->chksum), 3))
- goto out;
+ 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)
@@ -1233,13 +1262,6 @@ ustar_wr(ARCHD *arcn)
if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG))
return(0);
return(1);
-
- out:
- /*
- * header field is out of range
- */
- tty_warn(1, "Ustar header field is too small for %s", arcn->org_name);
- return(1);
}
/*
@@ -1265,7 +1287,15 @@ name_split(char *name, int len)
*/
if (len < TNMSZ)
return(name);
- if (len > (TPFSZ + TNMSZ))
+ /*
+ * GNU tar does not honor the prefix+name mode if the magic
+ * is not "ustar\0". So in GNU tar compatibility mode, we don't
+ * split the filename into prefix+name because we are setting
+ * the magic to "ustar " as GNU tar does. This of course will
+ * end up creating a LongLink record in cases where it does not
+ * really need do, but we are behaving like GNU tar after all.
+ */
+ if (is_gnutar || len > (TPFSZ + TNMSZ))
return(NULL);
/*
@@ -1318,11 +1348,10 @@ tar_gnutar_exclude_one(const char *line, size_t len)
char sbuf[MAXPATHLEN * 2 + 1];
/* + / + // + .*""/\/ + \/.* */
char rabuf[MAXPATHLEN * 2 + 1 + 1 + 2 + 4 + 4];
- int i, j;
+ int i, j = 0;
if (line[len - 1] == '\n')
len--;
- strncpy(sbuf, ".*" "\\/", j = 4);
for (i = 0; i < len; i++) {
/*
* convert glob to regexp, escaping everything