]> Dogcows Code - chaz/tar/blobdiff - src/extract.c
Fix restoring of directory timestamps from
[chaz/tar] / src / extract.c
index d826371683a62a2f4d4ca5160a0a3f49974b75c4..c89ebd01e74c4a1acb7abffd0b6fabc3f0e83892 100644 (file)
 static bool we_are_root;       /* true if our effective uid == 0 */
 static mode_t newdir_umask;    /* umask when creating new directories */
 static mode_t current_umask;   /* current umask (which is set to 0 if -p) */
+static bool directories_first;  /* Directory members precede non-directory
+                                  ones in the archive. This is detected for
+                                  incremental archives only. This variable
+                                  helps correctly restore directory
+                                  timestamps */
 
 /* Status of the permissions of a file that we are extracting.  */
 enum permstatus
@@ -262,7 +267,10 @@ set_stat (char const *file_name,
          /* FIXME: incremental_option should set ctime too, but how?  */
 
          struct timespec ts[2];
-         ts[0] = incremental_option ? st->atime : start_time;
+         if (incremental_option)
+           ts[0] = st->atime;
+         else
+           ts[0] = start_time;
          ts[1] = st->mtime;
 
          if (utimens (file_name, ts) != 0)
@@ -318,7 +326,19 @@ set_stat (char const *file_name,
    once we stop extracting files into that directory.
    If not restoring permissions, remember to invert the
    INVERT_PERMISSIONS bits from the file's current permissions.
-   PERMSTATUS specifies the status of the file's permissions.  */
+   PERMSTATUS specifies the status of the file's permissions.
+
+   NOTICE: this works only if the archive has usual member order, i.e.
+   directory, then the files in that directory. Incremental archive have
+   somewhat reversed order: first go subdirectories, then all other
+   members. To help cope with this case the variable directories_first
+   is set by prepare_to_extract.
+
+   If an archive was explicitely created so that its member order is
+   reversed, some directory timestamps can be restored incorrectly,
+   e.g.:
+       tar --no-recursion -cf archive dir dir/subdir dir/subdir/file
+*/
 static void
 delay_set_stat (char const *file_name, struct tar_stat_info const *st,
                mode_t invert_permissions, enum permstatus permstatus)
@@ -466,7 +486,7 @@ file_newer_p (const char *file_name, struct tar_stat_info *tar_stat)
       return errno != ENOENT;
     }
   if (!S_ISDIR (st.st_mode)
-      && timespec_cmp (tar_stat->mtime, get_stat_mtime (&st)) <= 0)
+      && tar_timespec_cmp (tar_stat->mtime, get_stat_mtime (&st)) <= 0)
     {
       return true;
     }
@@ -612,7 +632,8 @@ extract_dir (char *file_name, int typeflag)
   else if (typeflag == GNUTYPE_DUMPDIR)
     skip_member ();
 
-  mode = (current_stat_info.stat.st_mode | (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX;
+  mode = (current_stat_info.stat.st_mode |
+          (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX;
 
   while ((status = mkdir (file_name, mode)))
     {
@@ -736,37 +757,33 @@ extract_file (char *file_name, int typeflag)
        }
     }
 
+  mv_begin (&current_stat_info);
   if (current_stat_info.is_sparse)
     sparse_extract_file (fd, &current_stat_info, &size);
   else
     for (size = current_stat_info.stat.st_size; size > 0; )
       {
-       if (multi_volume_option)
-         {
-           assign_string (&save_name, current_stat_info.orig_file_name);
-           save_totsize = current_stat_info.stat.st_size;
-           save_sizeleft = size;
-         }
-
+       mv_size_left (size);
+       
        /* Locate data, determine max length writeable, write it,
           block that we have used the data, then check if the write
           worked.  */
-
+       
        data_block = find_next_block ();
        if (! data_block)
          {
            ERROR ((0, 0, _("Unexpected EOF in archive")));
            break;              /* FIXME: What happens, then?  */
          }
-
+       
        written = available_space_after (data_block);
-
+       
        if (written > size)
          written = size;
        errno = 0;
        count = full_write (fd, data_block->buffer, written);
        size -= written;
-
+       
        set_next_block_after ((union block *)
                              (data_block->buffer + written - 1));
        if (count != written)
@@ -780,9 +797,8 @@ extract_file (char *file_name, int typeflag)
 
   skip_file (size);
 
-  if (multi_volume_option)
-    assign_string (&save_name, 0);
-
+  mv_end ();
+  
   /* If writing to stdout, don't try to do anything to the filename;
      it doesn't exist, or we don't want to touch it anyway.  */
 
@@ -1091,6 +1107,8 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun)
     case DIRTYPE:
     case GNUTYPE_DUMPDIR:
       *fun = extract_dir;
+      if (current_stat_info.dumpdir)
+       directories_first = true;
       break;
 
     case GNUTYPE_VOLHDR:
@@ -1182,7 +1200,8 @@ extract_archive (void)
                                  false, absolute_names_option);
   if (strip_name_components)
     {
-      size_t prefix_len = stripped_prefix_len (file_name, strip_name_components);
+      size_t prefix_len = stripped_prefix_len (file_name,
+                                              strip_name_components);
       if (prefix_len == (size_t) -1)
        {
          skip_member ();
@@ -1191,8 +1210,12 @@ extract_archive (void)
       file_name += prefix_len;
     }
 
-  apply_nonancestor_delayed_set_stat (file_name, 0);
-
+  /* Restore stats for all non-ancestor directories, unless
+     it is an incremental archive.
+     (see NOTICE in the comment to delay_set_stat above) */
+  if (!directories_first)
+    apply_nonancestor_delayed_set_stat (file_name, 0);
+      
   /* Take a safety backup of a previously existing file.  */
 
   if (backup_option)
@@ -1206,7 +1229,6 @@ extract_archive (void)
       }
 
   /* Extract the archive entry according to its type.  */
-
   /* KLUDGE */
   typeflag = sparse_member_p (&current_stat_info) ?
                   GNUTYPE_SPARSE : current_header->header.typeflag;
This page took 0.0314 seconds and 4 git commands to generate.