Print this page
6128 tar should check prefix field when detecting EOT
6129 tar debug output should be available in all builds
Reviewed by: Robert Mustacchi <rm@joyent.com>

@@ -19,11 +19,11 @@
  * CDDL HEADER END
  */
 /*
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2012 Milan Jurik. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 /*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T     */
 /*        All Rights Reserved   */
 

@@ -122,17 +122,10 @@
 #define DEF_FILE "/etc/default/tar"
 
 #define min(a, b)  ((a) < (b) ? (a) : (b))
 #define max(a, b)  ((a) > (b) ? (a) : (b))
 
-/* -DDEBUG      ONLY for debugging */
-#ifdef  DEBUG
-#undef  DEBUG
-#define DEBUG(a, b, c)\
-        (void) fprintf(stderr, "DEBUG - "), (void) fprintf(stderr, a, b, c)
-#endif
-
 #define TBLOCK  512     /* tape block size--should be universal */
 
 #ifdef  BSIZE
 #define SYS_BLOCK BSIZE /* from sys/param.h:  secondary block size */
 #else   /* BSIZE */

@@ -480,11 +473,11 @@
 #endif  /* EUC */
 static int checkupdate(char *arg);
 static int checkw(char c, char *name);
 static int cmp(char *b, char *s, int n);
 static int defset(char *arch);
-static int endtape(void);
+static boolean_t endtape(void);
 static int is_in_table(file_list_t *table[], char *str);
 static int notsame(void);
 static int is_prefix(char *s1, char *s2);
 static int response(void);
 static int build_dblock(const char *, const char *, const char,

@@ -556,10 +549,12 @@
 static char *xz_suffix(void);
 static char *add_suffix();
 static void wait_pid(pid_t);
 static void verify_compress_opt(const char *t);
 static void detect_compress(void);
+static void dlog(const char *, ...);
+static boolean_t should_enable_debug(void);
 
 static  struct stat stbuf;
 
 static  char    *myname;
 static  char    *xtract_chdir = NULL;

@@ -631,11 +626,11 @@
 static  int     dumping;        /* true if writing a tape or other archive */
 static  int     extno;          /* number of extent:  starts at 1 */
 static  int     extotal;        /* total extents in this file */
 static  off_t   extsize;        /* size of current extent during extraction */
 static  ushort_t        Oumask = 0;     /* old umask value */
-static  int is_posix;   /* true if archive we're reading is POSIX-conformant */
+static  boolean_t is_posix;     /* true if archive is POSIX-conformant */
 static  const   char    *magic_type = "ustar";
 static  size_t  xrec_size = 8 * PATH_MAX;       /* extended rec initial size */
 static  char    *xrec_ptr;
 static  off_t   xrec_offset = 0;
 static  int     Xhdrflag;

@@ -643,10 +638,12 @@
 
 static  u_longlong_t    xhdr_flgs;      /* Bits set determine which items */
                                         /*   need to be in extended header. */
 static  pid_t   comp_pid = 0;
 
+static boolean_t debug_output = B_FALSE;
+
 #define _X_DEVMAJOR     0x1
 #define _X_DEVMINOR     0x2
 #define _X_GID          0x4
 #define _X_GNAME        0x8
 #define _X_LINKPATH     0x10

@@ -734,10 +731,12 @@
 #endif
         (void) textdomain(TEXT_DOMAIN);
         if (argc < 2)
                 usage();
 
+        debug_output = should_enable_debug();
+
         tfile = NULL;
         if ((myname = strdup(argv[0])) == NULL) {
                 (void) fprintf(stderr, gettext(
                     "tar: cannot allocate program name\n"));
                 exit(1);

@@ -1191,11 +1190,54 @@
 
         /* Not reached:  keep compiler quiet */
         return (1);
 }
 
+static boolean_t
+should_enable_debug(void)
+{
+        const char *val;
+        const char *truth[] = {
+                "true",
+                "1",
+                "yes",
+                "y",
+                "please",
+                NULL
+        };
+        unsigned int i;
+
+        if ((val = getenv("DEBUG_TAR")) == NULL) {
+                return (B_FALSE);
+        }
+
+        for (i = 0; truth[i] != NULL; i++) {
+                if (strcmp(val, truth[i]) == 0) {
+                        return (B_TRUE);
+                }
+        }
+
+        return (B_FALSE);
+}
+
+/*PRINTFLIKE1*/
 static void
+dlog(const char *format, ...)
+{
+        va_list ap;
+
+        if (!debug_output) {
+                return;
+        }
+
+        va_start(ap, format);
+        (void) fprintf(stderr, "tar: DEBUG: ");
+        (void) vfprintf(stderr, format, ap);
+        va_end(ap);
+}
+
+static void
 usage(void)
 {
         (void) fprintf(stderr, gettext(
 #if defined(O_XATTR)
 #if defined(_PC_SATTR_ENABLED)

@@ -1425,17 +1467,30 @@
  *      wants it backed up must call backtape himself
  *      RETURNS:        0 if not EOT, tape position unaffected
  *                      1 if     EOT, tape position unaffected
  */
 
-static int
+static boolean_t
 endtape(void)
 {
-        if (dblock.dbuf.name[0] == '\0') {      /* null header = EOT */
-                return (1);
-        } else
-                return (0);
+        if (dblock.dbuf.name[0] != '\0') {
+                /*
+                 * The name field is populated.
+                 */
+                return (B_FALSE);
+        }
+
+        if (is_posix && dblock.dbuf.prefix[0] != '\0') {
+                /*
+                 * This is a ustar/POSIX archive, and although the name
+                 * field is empty the prefix field is not.
+                 */
+                return (B_FALSE);
+        }
+
+        dlog("endtape(): found null header; EOT\n");
+        return (B_TRUE);
 }
 
 /*
  *      getdir - get directory entry from tar tape
  *

@@ -1469,13 +1524,15 @@
         (void) sscanf(dblock.dbuf.devminor, "%8lo", &Gen.g_devminor);
 
         is_posix = (strcmp(dblock.dbuf.magic, magic_type) == 0);
 
         sp->st_mode = Gen.g_mode;
-        if (is_posix && (sp->st_mode & S_IFMT) == 0)
+        if (is_posix && (sp->st_mode & S_IFMT) == 0) {
                 switch (dblock.dbuf.typeflag) {
-                case '0': case 0: case _XATTR_HDRTYPE:
+                case '0':
+                case 0:
+                case _XATTR_HDRTYPE:
                         sp->st_mode |= S_IFREG;
                         break;
                 case '1':       /* hard link */
                         break;
                 case '2':

@@ -1496,10 +1553,11 @@
                 default:
                         if (convtoreg(Gen.g_filesz))
                                 sp->st_mode |= S_IFREG;
                         break;
                 }
+        }
 
         if ((dblock.dbuf.typeflag == 'X') || (dblock.dbuf.typeflag == 'L')) {
                 Xhdrflag = 1;   /* Currently processing extended header */
         } else {
                 Xhdrflag = 0;

@@ -1603,20 +1661,32 @@
  *
  *      passtape skips over the next data file on the tape.
  *      The tape directory entry must be in dblock.dbuf. This
  *      routine just eats the number of blocks computed from the
  *      directory size entry; the tape must be (logically) positioned
- *      right after thee directory info.
+ *      right after the directory info.
  */
 
 static void
 passtape(void)
 {
         blkcnt_t blocks;
         char buf[TBLOCK];
 
         /*
+         * Print some debugging information about the directory entry
+         * we are skipping over:
+         */
+        dlog("passtape: typeflag \"%c\"\n", dblock.dbuf.typeflag);
+        if (dblock.dbuf.name[0] != '\0') {
+                dlog("passtape: name \"%s\"\n", dblock.dbuf.name);
+        }
+        if (is_posix && dblock.dbuf.prefix[0] != '\0') {
+                dlog("passtape: prefix \"%s\"\n", dblock.dbuf.prefix);
+        }
+
+        /*
          * Types link(1), sym-link(2), char special(3), blk special(4),
          *  directory(5), and FIFO(6) do not have data blocks associated
          *  with them so just skip reading the data block.
          */
         if (dblock.dbuf.typeflag == '1' || dblock.dbuf.typeflag == '2' ||

@@ -1623,10 +1693,12 @@
             dblock.dbuf.typeflag == '3' || dblock.dbuf.typeflag == '4' ||
             dblock.dbuf.typeflag == '5' || dblock.dbuf.typeflag == '6')
                 return;
         blocks = TBLOCKS(stbuf.st_size);
 
+        dlog("passtape: block count %" FMT_blkcnt_t "\n", blocks);
+
         /* if operating on disk, seek instead of reading */
         if (NotTape)
                 seekdisk(blocks);
         else
                 while (blocks-- > 0)

@@ -2068,30 +2140,29 @@
                             checksum(&dblock));
 
                         (void) writetbuf((char *)&dblock, 1);
                 }
                 if (vflag) {
-#ifdef DEBUG
-                        if (NotTape)
-                                DEBUG("seek = %" FMT_blkcnt_t "K\t", K(tapepos),
-                                    0);
-#endif
+                        if (NotTape) {
+                                dlog("seek = %" FMT_blkcnt_t "K\n", K(tapepos));
+                        }
                         if (filetype == XATTR_FILE && Hiddendir) {
                                 (void) fprintf(vfile,
                                     gettext("a %s attribute %s "),
                                     longname, longattrname);
 
                         } else {
                                 (void) fprintf(vfile, "a %s/ ", longname);
                         }
-                        if (NotTape)
+                        if (NotTape) {
                                 (void) fprintf(vfile, "%" FMT_blkcnt_t "K\n",
                                     K(blocks));
-                        else
+                        } else {
                                 (void) fprintf(vfile, gettext("%" FMT_blkcnt_t
                                     " tape blocks\n"), blocks);
                 }
+                }
 
                 /*
                  * If hidden dir then break now since xattrs_put() will do
                  * the iterating of the directory.
                  *

@@ -2268,24 +2339,20 @@
                                 (void) close(infile);
                                 goto out;
                         }
                         newvol();       /* not worth it--just get new volume */
                 }
-#ifdef DEBUG
-                DEBUG("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
+                dlog("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
                     blocks);
-#endif
                 if (build_dblock(name, tchar, '0', filetype,
                     &stbuf, stbuf.st_dev, prefix) != 0) {
                         goto out;
                 }
                 if (vflag) {
-#ifdef DEBUG
-                        if (NotTape)
-                                DEBUG("seek = %" FMT_blkcnt_t "K\t", K(tapepos),
-                                    0);
-#endif
+                        if (NotTape) {
+                                dlog("seek = %" FMT_blkcnt_t "K\n", K(tapepos));
+                        }
                         (void) fprintf(vfile, "a %s%s%s%s ", longname,
                             rw_sysattr ? gettext(" system") : "",
                             (filetype == XATTR_FILE) ? gettext(
                             " attribute ") : "",
                             (filetype == XATTR_FILE) ?

@@ -2365,28 +2432,24 @@
                                 (void) close(infile);
                                 goto out;
                         }
                         newvol();
                 }
-#ifdef DEBUG
-                DEBUG("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
+                dlog("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
                     blocks);
-#endif
                 if (vflag) {
-#ifdef DEBUG
-                        if (NotTape)
-                                DEBUG("seek = %" FMT_blkcnt_t "K\t", K(tapepos),
-                                    0);
-#endif
-                        if (NotTape)
+                        if (NotTape) {
+                                dlog("seek = %" FMT_blkcnt_t "K\n", K(tapepos));
+
                                 (void) fprintf(vfile, gettext("a %s %"
                                     FMT_blkcnt_t "K\n "), longname, K(blocks));
-                        else
+                        } else {
                                 (void) fprintf(vfile, gettext(
                                     "a %s %" FMT_blkcnt_t " tape blocks\n"),
                                     longname, blocks);
                 }
+                }
                 if (build_dblock(name, tchar, '6', filetype,
                     &stbuf, stbuf.st_dev, prefix) != 0)
                         goto out;
 
                 if (put_extra_attributes(longname, shortname, longattrname,

@@ -2416,28 +2479,24 @@
                                 (void) close(dirfd);
                                 goto out;
                         }
                         newvol();
                 }
-#ifdef DEBUG
-                DEBUG("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
+                dlog("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
                     blocks);
-#endif
                 if (vflag) {
-#ifdef DEBUG
-                        if (NotTape)
-                                DEBUG("seek = %" FMT_blkcnt_t "K\t", K(tapepos),
-                                    0);
-#endif
-                        if (NotTape)
+                        if (NotTape) {
+                                dlog("seek = %" FMT_blkcnt_t "K\t", K(tapepos));
+
                                 (void) fprintf(vfile, gettext("a %s %"
                                     FMT_blkcnt_t "K\n"), longname, K(blocks));
-                        else
+                        } else {
                                 (void) fprintf(vfile, gettext("a %s %"
                                     FMT_blkcnt_t " tape blocks\n"), longname,
                                     blocks);
                 }
+                }
                 if (build_dblock(name, tchar, '3',
                     filetype, &stbuf, stbuf.st_rdev, prefix) != 0)
                         goto out;
 
                 if (put_extra_attributes(longname, shortname, longattrname,

@@ -2467,20 +2526,17 @@
                                 (void) close(dirfd);
                                 goto out;
                         }
                         newvol();
                 }
-#ifdef DEBUG
-                DEBUG("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
+                dlog("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
                     blocks);
-#endif
                 if (vflag) {
-#ifdef DEBUG
-                        if (NotTape)
-                                DEBUG("seek = %" FMT_blkcnt_t "K\t", K(tapepos),
-                                    0);
-#endif
+                        if (NotTape) {
+                                dlog("seek = %" FMT_blkcnt_t "K\n", K(tapepos));
+                        }
+
                         (void) fprintf(vfile, "a %s ", longname);
                         if (NotTape)
                                 (void) fprintf(vfile, "%" FMT_blkcnt_t "K\n",
                                     K(blocks));
                         else

@@ -4788,13 +4844,11 @@
 newvol(void)
 {
         int c;
 
         if (dumping) {
-#ifdef DEBUG
-                DEBUG("newvol called with 'dumping' set\n", 0, 0);
-#endif
+                dlog("newvol called with 'dumping' set\n");
                 putempty((blkcnt_t)2);  /* 2 EOT marks */
                 closevol();
                 flushtape();
                 sync();
                 tapepos = 0;

@@ -4823,14 +4877,12 @@
         if (mt < 0) {
                 (void) fprintf(stderr, gettext(
                     "tar: cannot reopen %s (%s)\n"),
                     dumping ? gettext("output") : gettext("input"), usefile);
 
-#ifdef DEBUG
-                DEBUG("update=%d, usefile=%s ", update, usefile);
-                DEBUG("mt=%d, [%s]\n", mt, strerror(errno));
-#endif
+                dlog("update=%d, usefile=%s ", update, usefile);
+                dlog("mt=%d, [%s]\n", mt, strerror(errno));
 
                 done(2);
         }
 }
 

@@ -5011,13 +5063,11 @@
         /* handle non-multiple of SYS_BLOCK */
         blkcnt_t nxb;   /* # extra blocks */
 #endif
 
         tapepos += blocks;
-#ifdef DEBUG
-        DEBUG("seekdisk(%" FMT_blkcnt_t ") called\n", blocks, 0);
-#endif
+        dlog("seekdisk(%" FMT_blkcnt_t ") called\n", blocks);
         if (recno + blocks <= nblock) {
                 recno += blocks;
                 return;
         }
         if (recno > nblock)

@@ -5024,14 +5074,12 @@
                 recno = nblock;
         seekval = (off_t)blocks - (nblock - recno);
         recno = nblock; /* so readtape() reads next time through */
 #if SYS_BLOCK > TBLOCK
         nxb = (blkcnt_t)(seekval % (off_t)(SYS_BLOCK / TBLOCK));
-#ifdef DEBUG
-        DEBUG("xtrablks=%" FMT_blkcnt_t " seekval=%" FMT_blkcnt_t " blks\n",
+        dlog("xtrablks=%" FMT_blkcnt_t " seekval=%" FMT_blkcnt_t " blks\n",
             nxb, seekval);
-#endif
         if (nxb && nxb > seekval) /* don't seek--we'll read */
                 goto noseek;
         seekval -=  nxb;        /* don't seek quite so far */
 #endif
         if (lseek(mt, (off_t)(TBLOCK * seekval), 1) == (off_t)-1) {

@@ -5041,13 +5089,11 @@
         }
 #if SYS_BLOCK > TBLOCK
         /* read those extra blocks */
 noseek:
         if (nxb) {
-#ifdef DEBUG
-                DEBUG("reading extra blocks\n", 0, 0);
-#endif
+                dlog("reading extra blocks\n", 0, 0);
                 if (read(mt, tbuf, TBLOCK*nblock) < 0) {
                         (void) fprintf(stderr, gettext(
                             "tar: read error while skipping file\n"));
                         done(8);
                 }

@@ -5215,14 +5261,12 @@
 
 static void
 backtape(void)
 {
         struct mtop mtcmd;
-#ifdef DEBUG
-        DEBUG("backtape() called, recno=%" FMT_blkcnt_t " nblock=%d\n", recno,
+        dlog("backtape() called, recno=%" FMT_blkcnt_t " nblock=%d\n", recno,
             nblock);
-#endif
         /*
          * Backup to the position in the archive where the record
          * currently sitting in the tbuf buffer is situated.
          */
 

@@ -5276,13 +5320,11 @@
  */
 
 static void
 flushtape(void)
 {
-#ifdef DEBUG
-        DEBUG("flushtape() called, recno=%" FMT_blkcnt_t "\n", recno, 0);
-#endif
+        dlog("flushtape() called, recno=%" FMT_blkcnt_t "\n", recno);
         if (recno > 0) {        /* anything buffered? */
                 if (NotTape) {
 #if SYS_BLOCK > TBLOCK
                         int i;
 

@@ -5291,24 +5333,20 @@
                          * we are at the end of a volume that is not a tape.
                          * Here we round recno up to an even SYS_BLOCK
                          * boundary.
                          */
                         if ((i = recno % (SYS_BLOCK / TBLOCK)) != 0) {
-#ifdef DEBUG
-                                DEBUG("flushtape() %d rounding blocks\n", i, 0);
-#endif
+                                dlog("flushtape() %d rounding blocks\n", i);
                                 recno += i;     /* round up to even SYS_BLOCK */
                         }
 #endif
                         if (recno > nblock)
                                 recno = nblock;
                 }
-#ifdef DEBUG
-                DEBUG("writing out %" FMT_blkcnt_t " blocks of %" FMT_blkcnt_t
+                dlog("writing out %" FMT_blkcnt_t " blocks of %" FMT_blkcnt_t
                     " bytes\n", (blkcnt_t)(NotTape ? recno : nblock),
                     (blkcnt_t)(NotTape ? recno : nblock) * TBLOCK);
-#endif
                 if (write(mt, tbuf,
                     (size_t)(NotTape ? recno : nblock) * TBLOCK) < 0) {
                         (void) fprintf(stderr, gettext(
                             "tar: tape write error\n"));
                         done(2);

@@ -5434,16 +5472,14 @@
         if ((bp = strtok(NULL, " \t")) != NULL)
                 NotTape = (*bp == 'n' || *bp == 'N');
         else
                 NotTape = (blocklim != 0);
         (void) defopen(NULL);
-#ifdef DEBUG
-        DEBUG("defset: archive='%s'; usefile='%s'\n", arch, usefile);
-        DEBUG("defset: nblock='%d'; blocklim='%" FMT_blkcnt_t "'\n",
+        dlog("defset: archive='%s'; usefile='%s'\n", arch, usefile);
+        dlog("defset: nblock='%d'; blocklim='%" FMT_blkcnt_t "'\n",
             nblock, blocklim);
-        DEBUG("defset: not tape = %d\n", NotTape, 0);
-#endif
+        dlog("defset: not tape = %d\n", NotTape);
         return (TRUE);
 }
 
 
 /*

@@ -5750,22 +5786,28 @@
                 return (0);
         }
 
         if (endtape()) {
                 if (Bflag) {
+                        ssize_t sz;
+                        size_t extra_blocks = 0;
+
                         /*
                          * Logically at EOT - consume any extra blocks
                          * so that write to our stdin won't fail and
                          * emit an error message; otherwise something
                          * like "dd if=foo.tar | (cd bar; tar xvf -)"
                          * will produce a bogus error message from "dd".
                          */
 
-                        while (read(mt, tbuf, TBLOCK*nblock) > 0) {
-                                /* empty body */
+                        while ((sz = read(mt, tbuf, TBLOCK*nblock)) > 0) {
+                                extra_blocks += sz;
                         }
+                        dlog("wantit(): %d bytes of extra blocks\n",
+                            extra_blocks);
                 }
+                dlog("wantit(): at end of tape.\n");
                 return (-1);
         }
 
         gotit = 0;
 

@@ -8014,15 +8056,13 @@
                          * for other hard links since they are the
                          * same file.
                          */
 
                         if (vflag) {
-#ifdef DEBUG
                                 if (NotTape)
-                                        DEBUG("seek = %" FMT_blkcnt_t
-                                            "K\t", K(tapepos), 0);
-#endif
+                                        dlog("seek = %" FMT_blkcnt_t
+                                            "K\n", K(tapepos));
                                 if (filetype == XATTR_FILE) {
                                         (void) fprintf(vfile, gettext(
                                             "a %s attribute %s link to "
                                             "%s attribute %s\n"),
                                             name, component, name,