Import upstream version 1.29
[debian/tar] / src / extract.c
index ca25603a45b5570b284f976d02addaf32c98427c..f982433d77fff9aff5e30a8f28fa62b767b63b99 100644 (file)
@@ -1,7 +1,7 @@
 /* Extract files from a tar archive.
 
-   Copyright 1988, 1992-1994, 1996-2001, 2003-2007, 2010, 2012-2014 Free
-   Software Foundation, Inc.
+   Copyright 1988, 1992-1994, 1996-2001, 2003-2007, 2010, 2012-2014,
+   2016 Free Software Foundation, Inc.
 
    This file is part of GNU tar.
 
@@ -109,7 +109,7 @@ struct delayed_set_stat
     struct xattr_array *xattr_map;
     /* Length and contents of name.  */
     size_t file_name_len;
-    char file_name[1];
+    char *file_name;
   };
 
 static struct delayed_set_stat *delayed_set_stat_head;
@@ -441,9 +441,7 @@ delay_set_stat (char const *file_name, struct tar_stat_info const *st,
                mode_t mode, int atflag)
 {
   size_t file_name_len = strlen (file_name);
-  struct delayed_set_stat *data =
-    xmalloc (offsetof (struct delayed_set_stat, file_name)
-            + file_name_len + 1);
+  struct delayed_set_stat *data = xmalloc (sizeof (*data));
   data->next = delayed_set_stat_head;
   data->mode = mode;
   if (st)
@@ -456,6 +454,7 @@ delay_set_stat (char const *file_name, struct tar_stat_info const *st,
       data->mtime = st->mtime;
     }
   data->file_name_len = file_name_len;
+  data->file_name = xstrdup (file_name);
   data->current_mode = current_mode;
   data->current_mode_mask = current_mode_mask;
   data->interdir = ! st;
@@ -537,6 +536,56 @@ repair_delayed_set_stat (char const *dir,
          quotearg_colon (dir)));
 }
 
+static void
+free_delayed_set_stat (struct delayed_set_stat *data)
+{
+  free (data->file_name);
+  xheader_xattr_free (data->xattr_map, data->xattr_map_size);
+  free (data->cntx_name);
+  free (data->acls_a_ptr);
+  free (data->acls_d_ptr);
+  free (data);
+}
+
+void
+remove_delayed_set_stat (const char *fname)
+{
+  struct delayed_set_stat *data, *next, *prev = NULL;
+  for (data = delayed_set_stat_head; data; data = next)
+    {
+      next = data->next;
+      if (chdir_current == data->change_dir
+         && strcmp (data->file_name, fname) == 0)
+       {
+         free_delayed_set_stat (data);
+         if (prev)
+           prev->next = next;
+         else
+           delayed_set_stat_head = next;
+         return;
+       }
+      else
+       prev = data;
+    }
+}
+
+static void
+fixup_delayed_set_stat (char const *src, char const *dst)
+{
+  struct delayed_set_stat *data;
+  for (data = delayed_set_stat_head; data; data = data->next)
+    {
+      if (chdir_current == data->change_dir
+         && strcmp (data->file_name, src) == 0)
+       {
+         free (data->file_name);
+         data->file_name = xstrdup (dst);
+         data->file_name_len = strlen (dst);
+         return;
+       }
+    }
+}
+
 /* After a file/link/directory creation has failed, see if
    it's because some required directory was not present, and if so,
    create all required directories.  Return zero if all the required
@@ -846,11 +895,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
        }
 
       delayed_set_stat_head = data->next;
-      xheader_xattr_free (data->xattr_map, data->xattr_map_size);
-      free (data->cntx_name);
-      free (data->acls_a_ptr);
-      free (data->acls_d_ptr);
-      free (data);
+      free_delayed_set_stat (data);
     }
 }
 
@@ -1741,7 +1786,9 @@ extract_finish (void)
 bool
 rename_directory (char *src, char *dst)
 {
-  if (renameat (chdir_fd, src, chdir_fd, dst) != 0)
+  if (renameat (chdir_fd, src, chdir_fd, dst) == 0)
+    fixup_delayed_set_stat (src, dst);
+  else
     {
       int e = errno;
       bool interdir_made;