]> Dogcows Code - chaz/tar/blobdiff - src/create.c
Updated
[chaz/tar] / src / create.c
index 1c5a204dc4cbd6e77fd0601cd38fa5bb684e0a5e..6808fba926869e09f7d07726ddd292e640fe8964 100644 (file)
 
 #include "system.h"
 
-#if !MSDOS
-# include <pwd.h>
-# include <grp.h>
-#endif
-
 #if HAVE_UTIME_H
 # include <utime.h>
 #else
@@ -41,11 +36,6 @@ struct utimbuf
 #include "common.h"
 #include <hash.h>
 
-#ifndef MSDOS
-extern dev_t ar_dev;
-extern ino_t ar_ino;
-#endif
-
 struct link
   {
     dev_t dev;
@@ -330,6 +320,14 @@ uintmax_to_chars (uintmax_t v, char *p, size_t s)
 {
   to_chars (0, v, sizeof v, 0, p, s, "uintmax_t");
 }
+
+void
+string_to_chars (char *str, char *p, size_t s)
+{
+  strncpy (p, str, s);
+  p[s-1] = 0;
+}
+
 \f
 /* Writing routines.  */
 
@@ -355,24 +353,54 @@ write_eot (void)
   set_next_block_after (pointer);
 }
 
-/* Write a GNUTYPE_LONGLINK or GNUTYPE_LONGNAME block.  */
+/* Write a "private" header */
+static union block *
+start_private_header (const char *name, size_t size)
+{
+  time_t t;
+  union block *header = find_next_block ();
+  
+  memset (header->buffer, 0, sizeof (union block));
 
-/* FIXME: Cross recursion between start_header and write_long!  */
+  strncpy (header->header.name, name, NAME_FIELD_SIZE);
+  header->header.name[NAME_FIELD_SIZE - 1] = '\0';
+  OFF_TO_CHARS (size, header->header.size);
 
-static union block *start_header (const char *, struct tar_stat_info *);
+  time (&t);
+  TIME_TO_CHARS (t, header->header.mtime);
+  MODE_TO_CHARS (S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, header->header.mode);
+  UID_TO_CHARS (getuid (), header->header.uid);
+  GID_TO_CHARS (getgid (), header->header.gid);
+  MAJOR_TO_CHARS (0, header->header.devmajor);
+  MAJOR_TO_CHARS (0, header->header.devminor);
+  strncpy (header->header.magic, TMAGIC, TMAGLEN);
+  strncpy (header->header.version, TVERSION, TVERSLEN);
+  return header;
+}
+
+/* Create a new header and store there at most NAME_FIELD_SIZE bytes of
+   the file name */
 
+static union block * 
+write_short_name (struct tar_stat_info *st)
+{
+  union block *header = find_next_block ();
+  memset (header->buffer, 0, sizeof (union block));
+  
+  strncpy (header->header.name, st->file_name, NAME_FIELD_SIZE);
+  header->header.name[NAME_FIELD_SIZE - 1] = '\0';
+  return header;
+}
+
+/* Write a GNUTYPE_LONGLINK or GNUTYPE_LONGNAME block.  */
 static void
-write_long (const char *p, char type)
+write_gnu_long_link (const char *p, char type)
 {
   size_t size = strlen (p) + 1;
   size_t bufsize;
   union block *header;
-  struct tar_stat_info foo;
-
-  memset (&foo, 0, sizeof foo);
-  foo.stat.st_size = size;
 
-  header = start_header ("././@LongLink", &foo);
+  header = start_private_header ("././@LongLink", size);
   header->header.typeflag = type;
   finish_header (header, -1);
 
@@ -393,6 +421,161 @@ write_long (const char *p, char type)
   memset (header->buffer + size, 0, bufsize - size);
   set_next_block_after (header + (size - 1) / BLOCKSIZE);
 }
+
+static size_t
+split_long_name (const char *name, size_t length)
+{
+  size_t i;
+
+  if (length > PREFIX_FIELD_SIZE)
+    length = PREFIX_FIELD_SIZE+2;
+  for (i = length - 1; i > 0; i--)
+    if (ISSLASH (name[i]))
+      break;
+  return i;
+}
+
+static union block *
+write_ustar_long_name (const char *name)
+{
+  size_t length = strlen (name);
+  size_t i;
+  union block *header;
+
+  if (length > PREFIX_FIELD_SIZE + NAME_FIELD_SIZE + 1)
+    {
+      WARN ((0, 0, _("%s: file name is too long (max %d); not dumped"),
+            quotearg_colon (name),
+            PREFIX_FIELD_SIZE + NAME_FIELD_SIZE + 1));
+      return NULL;
+    }
+  
+  i = split_long_name (name, length);
+  if (i == 0)
+    {
+      WARN ((0, 0,
+            _("%s: file name is too long (cannot be split); not dumped"),
+            quotearg_colon (name),
+            PREFIX_FIELD_SIZE + NAME_FIELD_SIZE + 1));
+      return NULL;
+    }
+
+  header = find_next_block ();
+  memset (header->buffer, 0, sizeof (header->buffer));
+  memcpy (header->header.prefix, name, i);
+  memcpy (header->header.name, name + i + 1, length - i);
+  
+  return header;
+}
+
+/* Write a long link name, depending on the current archive format */
+static void
+write_long_link (struct tar_stat_info *st)
+{
+  switch (archive_format)
+    {
+    case POSIX_FORMAT:
+      xheader_store ("linkpath", st);
+      break;
+
+    case V7_FORMAT:                    /* old V7 tar format */
+    case USTAR_FORMAT:
+    case STAR_FORMAT:
+      WARN ((0, 0,
+            _("%s: link name is too long; not dumped"),
+            quotearg_colon (st->link_name)));
+           break;
+      
+    case OLDGNU_FORMAT:
+    case GNU_FORMAT:
+      write_gnu_long_link (st->link_name, GNUTYPE_LONGLINK);
+      break;
+
+    default:
+      abort(); /*FIXME*/
+    }
+}
+
+static union block *
+write_long_name (struct tar_stat_info *st)
+{
+  switch (archive_format)
+    {
+    case POSIX_FORMAT:
+      xheader_store ("path", st);
+      break;
+
+    case V7_FORMAT:                  
+    case USTAR_FORMAT:
+    case STAR_FORMAT:
+      return write_ustar_long_name (st->file_name);
+      
+    case OLDGNU_FORMAT:
+    case GNU_FORMAT:
+      write_gnu_long_link (st->file_name, GNUTYPE_LONGNAME);
+      break;
+
+    default:
+      abort(); /*FIXME*/
+    }
+  return write_short_name (st);
+}
+
+static union block *
+write_extended (union block *old_header, char type)
+{
+  union block *header, hp;
+  struct tar_stat_info foo;
+  size_t size;
+  char *p;
+
+  if (extended_header.buffer || extended_header.stk == NULL)
+    return old_header; 
+  
+  xheader_finish (&extended_header);
+  size = extended_header.size;
+
+  memcpy (hp.buffer, old_header, sizeof (hp));
+  
+  header = start_private_header ("././@PaxHeader", size);
+  header->header.typeflag = type;
+
+  finish_header (header, -1);
+
+  p = extended_header.buffer;
+
+  do
+    {
+      size_t len;
+      
+      header = find_next_block ();
+      len = BLOCKSIZE;
+      if (len > size)
+       len = size;
+      memcpy (header->buffer, p, len);
+      if (len < BLOCKSIZE)
+       memset (header->buffer + len, 0, BLOCKSIZE - len);
+      p += len;
+      size -= len;
+      set_next_block_after (header);
+    }
+  while (size > 0);
+  
+  xheader_destroy (&extended_header);
+  header = find_next_block ();
+  memcpy (header, &hp.buffer, sizeof (hp.buffer));
+  return header;
+}
+
+static union block * 
+write_header_name (struct tar_stat_info *st)
+{
+  if (NAME_FIELD_SIZE <= strlen (st->file_name))
+    return write_long_name (st);
+  else
+    return write_short_name (st);
+}
+
 \f
 /* Header handling.  */
 
@@ -405,17 +588,14 @@ start_header (const char *name, struct tar_stat_info *st)
   union block *header;
 
   name = safer_name_suffix (name, 0);
+  assign_string (&st->file_name, name);
 
-  if (sizeof header->header.name <= strlen (name))
-    write_long (name, GNUTYPE_LONGNAME);
-  header = find_next_block ();
-  memset (header->buffer, 0, sizeof (union block));
+  header = write_header_name (st);
+  if (!header)
+    return NULL;
 
   assign_string (&current_stat_info.file_name, name);
 
-  strncpy (header->header.name, name, NAME_FIELD_SIZE);
-  header->header.name[NAME_FIELD_SIZE - 1] = '\0';
-
   /* Override some stat fields, if requested to do so.  */
 
   if (owner_option != (uid_t) -1)
@@ -449,19 +629,57 @@ start_header (const char *name, struct tar_stat_info *st)
      above, thus making GNU tar both a universal donor and a universal
      acceptor for Paul's test.  */
 
-  if (archive_format == V7_FORMAT)
+  if (archive_format == V7_FORMAT || archive_format == USTAR_FORMAT)
     MODE_TO_CHARS (st->stat.st_mode & MODE_ALL, header->header.mode);
   else
     MODE_TO_CHARS (st->stat.st_mode, header->header.mode);
 
-  UID_TO_CHARS (st->stat.st_uid, header->header.uid);
-  GID_TO_CHARS (st->stat.st_gid, header->header.gid);
-  OFF_TO_CHARS (st->stat.st_size, header->header.size);
+  if (st->stat.st_uid > MAXOCTAL7 && archive_format == POSIX_FORMAT)
+    xheader_store ("uid", st);
+  else
+    UID_TO_CHARS (st->stat.st_uid, header->header.uid);
+  
+  if (st->stat.st_gid > MAXOCTAL7 && archive_format == POSIX_FORMAT)
+    xheader_store ("gid", st);
+  else
+    GID_TO_CHARS (st->stat.st_gid, header->header.gid);
+
+  if (st->stat.st_size > MAXOCTAL11 && archive_format == POSIX_FORMAT)
+    xheader_store ("size", st);
+  else
+    OFF_TO_CHARS (st->stat.st_size, header->header.size);
+
   TIME_TO_CHARS (st->stat.st_mtime, header->header.mtime);
-  MAJOR_TO_CHARS (0, header->header.devmajor);
-  MINOR_TO_CHARS (0, header->header.devminor);
 
-  if (incremental_option)
+  /* FIXME */
+  if (S_ISCHR (st->stat.st_mode)
+      || S_ISBLK (st->stat.st_mode))
+    {
+      st->devmajor = major (st->stat.st_rdev);
+      st->devminor = minor (st->stat.st_rdev);
+
+      if (st->devmajor > MAXOCTAL7 && archive_format == POSIX_FORMAT)
+       xheader_store ("devmajor", st);
+      else
+       MAJOR_TO_CHARS (st->devmajor, header->header.devmajor);
+
+      if (st->devminor > MAXOCTAL7 && archive_format == POSIX_FORMAT)
+       xheader_store ("devminor", st);
+      else
+       MAJOR_TO_CHARS (st->devminor, header->header.devminor);
+    }
+  else
+    {
+      MAJOR_TO_CHARS (0, header->header.devmajor);
+      MINOR_TO_CHARS (0, header->header.devminor);
+    }
+  
+  if (archive_format == POSIX_FORMAT)
+    {
+      xheader_store ("atime", st);
+      xheader_store ("ctime", st);
+    }
+  else if (incremental_option)
     if (archive_format == OLDGNU_FORMAT)
       {
        TIME_TO_CHARS (st->stat.st_atime, header->oldgnu_header.atime);
@@ -481,7 +699,8 @@ start_header (const char *name, struct tar_stat_info *st)
       break;
 
     case POSIX_FORMAT:
-    case GNU_FORMAT:
+    case USTAR_FORMAT:
+    case GNU_FORMAT:   /*FIXME?*/
       strncpy (header->header.magic, TMAGIC, TMAGLEN);
       strncpy (header->header.version, TVERSION, TVERSLEN);
       break;
@@ -496,8 +715,20 @@ start_header (const char *name, struct tar_stat_info *st)
     }
   else
     {
-      uid_to_uname (st->stat.st_uid, header->header.uname);
-      gid_to_gname (st->stat.st_gid, header->header.gname);
+      uid_to_uname (st->stat.st_uid, &st->uname);
+      gid_to_gname (st->stat.st_gid, &st->gname);
+      
+      if (archive_format == POSIX_FORMAT
+         && strlen (st->uname) > UNAME_FIELD_SIZE)
+       xheader_store ("uname", st);
+      else
+       UNAME_TO_CHARS (st->uname, header->header.uname);
+
+      if (archive_format == POSIX_FORMAT
+         && strlen (st->gname) > GNAME_FIELD_SIZE)
+       xheader_store ("gname", st);
+      else
+       GNAME_TO_CHARS (st->gname, header->header.gname);
     }
 
   return header;
@@ -514,6 +745,24 @@ finish_header (union block *header, off_t block_ordinal)
   int sum;
   char *p;
 
+  /* Note: It is important to do this before the call to write_extended(),
+     so that the actual ustar header is printed */
+  if (verbose_option
+      && header->header.typeflag != GNUTYPE_LONGLINK
+      && header->header.typeflag != GNUTYPE_LONGNAME
+      && header->header.typeflag != XHDTYPE
+      && header->header.typeflag != XGLTYPE)
+    {
+      /* These globals are parameters to print_header, sigh.  */
+
+      current_header = header;
+      /* current_stat_info is already set up.  */
+      current_format = archive_format;
+      print_header (block_ordinal);
+    }
+
+  header = write_extended (header, XHDTYPE);
+  
   memcpy (header->header.chksum, CHKBLANKS, sizeof header->header.chksum);
 
   sum = 0;
@@ -534,18 +783,6 @@ finish_header (union block *header, off_t block_ordinal)
 
   uintmax_to_chars ((uintmax_t) sum, header->header.chksum, 7);
 
-  if (verbose_option
-      && header->header.typeflag != GNUTYPE_LONGLINK
-      && header->header.typeflag != GNUTYPE_LONGNAME)
-    {
-      /* These globals are parameters to print_header, sigh.  */
-
-      current_header = header;
-      /* current_stat_info is already set up.  */
-      current_format = archive_format;
-      print_header (block_ordinal);
-    }
-
   set_next_block_after (header);
 }
 \f
@@ -848,6 +1085,15 @@ compare_links (void const *entry1, void const *entry2)
   return ((link1->dev ^ link2->dev) | (link1->ino ^ link2->ino)) == 0;
 }
 
+/* Copy at most LEN bytes from SRC to DST. Terminate with NUL unless
+   SRC is LEN characters long */
+static void
+tar_copy_str (char *dst, const char *src, size_t len)
+{
+  dst[len-1] = 0;
+  strncpy (dst, src, len);
+}
+
 /* Table of all non-directories that we've written so far.  Any time
    we see another, we check the table and avoid dumping the data
    again if we've done it once already.  */
@@ -922,16 +1168,13 @@ dump_file (char *p, int top_level, dev_t parent_device)
       return;
     }
 
-#if !MSDOS
   /* See if we are trying to dump the archive.  */
-
-  if (ar_dev && current_stat_info.stat.st_dev == ar_dev && current_stat_info.stat.st_ino == ar_ino)
+  if (sys_file_is_archive (&current_stat_info))
     {
       WARN ((0, 0, _("%s: file is the archive; not dumped"),
             quotearg_colon (p)));
       return;
     }
-#endif
 
   if (S_ISDIR (current_stat_info.stat.st_mode))
     {
@@ -981,17 +1224,10 @@ dump_file (char *p, int top_level, dev_t parent_device)
          block_ordinal = current_block_ordinal ();
          current_stat_info.stat.st_size = 0;   /* force 0 size on dir */
 
-         /* FIXME: If people could really read standard archives, this
-            should be:
-
-            header
-              = start_header (standard_option ? p : namebuf, &current_stat_info);
-
-            but since they'd interpret DIRTYPE blocks as regular
-            files, we'd better put the / on the name.  */
-
          header = start_header (namebuf, &current_stat_info);
-
+         if (!header)
+           return;
+         
          if (incremental_option)
            header->header.typeflag = GNUTYPE_DUMPDIR;
          else /* if (standard_option) */
@@ -1119,16 +1355,16 @@ dump_file (char *p, int top_level, dev_t parent_device)
              dup->nlink--;
              
              block_ordinal = current_block_ordinal ();
-             if (NAME_FIELD_SIZE <= strlen (link_name))
-               write_long (link_name, GNUTYPE_LONGLINK);
              assign_string (&current_stat_info.link_name, link_name);
+             if (NAME_FIELD_SIZE < strlen (link_name))
+               write_long_link (&current_stat_info);
 
              current_stat_info.stat.st_size = 0;
              header = start_header (p, &current_stat_info);
-             strncpy (header->header.linkname, link_name, NAME_FIELD_SIZE);
-
-             /* Force null termination.  */
-             header->header.linkname[NAME_FIELD_SIZE - 1] = 0;
+             if (!header)
+               return;
+             tar_copy_str (header->header.linkname, link_name,
+                           NAME_FIELD_SIZE);
 
              header->header.typeflag = LNKTYPE;
              finish_header (header, block_ordinal);
@@ -1201,6 +1437,8 @@ dump_file (char *p, int top_level, dev_t parent_device)
 
                  block_ordinal = current_block_ordinal ();
                  header = start_header (p, &current_stat_info);
+                 if (!header)
+                   return;
                  header->header.typeflag = GNUTYPE_SPARSE;
                  header_moved = 1;
 
@@ -1245,8 +1483,8 @@ dump_file (char *p, int top_level, dev_t parent_device)
 
          sizeleft = current_stat_info.stat.st_size;
 
-         /* Don't bother opening empty, world readable files.  Also do not open
-            files when archive is meant for /dev/null.  */
+         /* Don't bother opening empty, world readable files.  Also do not
+            open files when archive is meant for /dev/null.  */
 
          if (dev_null_output
              || (sizeleft == 0
@@ -1272,14 +1510,21 @@ dump_file (char *p, int top_level, dev_t parent_device)
            {
              block_ordinal = current_block_ordinal ();
              header = start_header (p, &current_stat_info);
+             if (!header)
+               return;
            }
 
          /* Mark contiguous files, if we support them.  */
 
-         if (archive_format != V7_FORMAT && S_ISCTG (current_stat_info.stat.st_mode))
+         if (archive_format != V7_FORMAT
+             && S_ISCTG (current_stat_info.stat.st_mode))
            header->header.typeflag = CONTTYPE;
 
-         isextended = header->oldgnu_header.isextended;
+         if (archive_format == GNU_FORMAT || archive_format == OLDGNU_FORMAT)
+           isextended = header->oldgnu_header.isextended;
+         else
+           isextended = 0;
+
          save_typeflag = header->header.typeflag;
          finish_header (header, block_ordinal);
          if (isextended)
@@ -1361,7 +1606,9 @@ dump_file (char *p, int top_level, dev_t parent_device)
                    char buf[UINTMAX_STRSIZE_BOUND];
                    memset (start->buffer + count, 0, bufsize - count);
                    WARN ((0, 0,
-                          _("%s: File shrank by %s bytes; padding with zeros"),
+                          ngettext ("%s: File shrank by %s byte; padding with zeros",
+                                    "%s: File shrank by %s bytes; padding with zeros",
+                                    sizeleft),
                           quotearg_colon (p),
                           STRINGIFY_BIGINT (sizeleft, buf)));
                    if (! ignore_failed_read_option)
@@ -1446,15 +1693,16 @@ dump_file (char *p, int top_level, dev_t parent_device)
              return;
            }
          buffer[size] = '\0';
-         if (size >= NAME_FIELD_SIZE)
-           write_long (buffer, GNUTYPE_LONGLINK);
          assign_string (&current_stat_info.link_name, buffer);
+         if (size > NAME_FIELD_SIZE)
+           write_long_link (&current_stat_info);
 
          block_ordinal = current_block_ordinal ();
          current_stat_info.stat.st_size = 0;   /* force 0 size on symlink */
          header = start_header (p, &current_stat_info);
-         strncpy (header->header.linkname, buffer, NAME_FIELD_SIZE);
-         header->header.linkname[NAME_FIELD_SIZE - 1] = '\0';
+         if (!header)
+           return;
+         tar_copy_str (header->header.linkname, buffer, NAME_FIELD_SIZE);
          header->header.typeflag = SYMTYPE;
          finish_header (header, block_ordinal);
          /* nothing more to do to it */
@@ -1493,6 +1741,8 @@ dump_file (char *p, int top_level, dev_t parent_device)
   block_ordinal = current_block_ordinal ();
   current_stat_info.stat.st_size = 0;  /* force 0 size */
   header = start_header (p, &current_stat_info);
+  if (!header)
+    return;
   header->header.typeflag = type;
 
   if (type != FIFOTYPE)
This page took 0.035127 seconds and 4 git commands to generate.