diff options
Diffstat (limited to 'archivers/pax/files/tar.c')
-rw-r--r-- | archivers/pax/files/tar.c | 93 |
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 |