Selaa lähdekoodia

Treat fishy-looking hard links like fishy-looking symlinks.
(struct delayed_set_stat): Rename after_symlinks
member to after_links. All uses changed.
(struct delayed_link): Renamed from struct delayed_symlink.
All uses changed. New member is_symlink.
(delayed_link_head): Renamed from delayed_symlink_head. All uses
changed.
(create_placeholder_file): New function, taken from extract_symlink.
(extract_link): Create placeholders for fishy-looking hard links.
(extract_symlink): Move code into create_placeholder_file.
(apply_delayed_links): Renamed from apply_delayed_symlinks.
All uses changed. Create both hard links and symlinks.

Paul Eggert 19 vuotta sitten
vanhempi
commit
af5d05729a
1 muutettua tiedostoa jossa 126 lisäystä ja 104 poistoa
  1. 126 104
      src/extract.c

+ 126 - 104
src/extract.c

@@ -48,8 +48,8 @@ enum permstatus
 /* List of directories whose statuses we need to extract after we've
 /* List of directories whose statuses we need to extract after we've
    finished extracting their subsidiary files.  If you consider each
    finished extracting their subsidiary files.  If you consider each
    contiguous subsequence of elements of the form [D]?[^D]*, where [D]
    contiguous subsequence of elements of the form [D]?[^D]*, where [D]
-   represents an element where AFTER_SYMLINKS is nonzero and [^D]
-   represents an element where AFTER_SYMLINKS is zero, then the head
+   represents an element where AFTER_LINKS is nonzero and [^D]
+   represents an element where AFTER_LINKS is zero, then the head
    of the subsequence has the longest name, and each non-head element
    of the subsequence has the longest name, and each non-head element
    in the prefix is an ancestor (in the directory hierarchy) of the
    in the prefix is an ancestor (in the directory hierarchy) of the
    preceding element.  */
    preceding element.  */
@@ -61,28 +61,31 @@ struct delayed_set_stat
     size_t file_name_len;
     size_t file_name_len;
     mode_t invert_permissions;
     mode_t invert_permissions;
     enum permstatus permstatus;
     enum permstatus permstatus;
-    bool after_symlinks;
+    bool after_links;
     char file_name[1];
     char file_name[1];
   };
   };
 
 
 static struct delayed_set_stat *delayed_set_stat_head;
 static struct delayed_set_stat *delayed_set_stat_head;
 
 
-/* List of symbolic links whose creation we have delayed.  */
-struct delayed_symlink
+/* List of links whose creation we have delayed.  */
+struct delayed_link
   {
   {
-    /* The next delayed symbolic link in the list.  */
-    struct delayed_symlink *next;
+    /* The next delayed link in the list.  */
+    struct delayed_link *next;
 
 
     /* The device, inode number and last-modified time of the placeholder.  */
     /* The device, inode number and last-modified time of the placeholder.  */
     dev_t dev;
     dev_t dev;
     ino_t ino;
     ino_t ino;
     time_t mtime;
     time_t mtime;
 
 
-    /* The desired owner and group of the symbolic link.  */
+    /* True if the link is symbolic.  */
+    bool is_symlink;
+
+    /* The desired owner and group of the link, if it is a symlink.  */
     uid_t uid;
     uid_t uid;
     gid_t gid;
     gid_t gid;
 
 
-    /* A list of sources for this symlink.  The sources are all to be
+    /* A list of sources for this link.  The sources are all to be
        hard-linked together.  */
        hard-linked together.  */
     struct string_list *sources;
     struct string_list *sources;
 
 
@@ -90,7 +93,7 @@ struct delayed_symlink
     char target[1];
     char target[1];
   };
   };
 
 
-static struct delayed_symlink *delayed_symlink_head;
+static struct delayed_link *delayed_link_head;
 
 
 struct string_list
 struct string_list
   {
   {
@@ -322,7 +325,7 @@ delay_set_stat (char const *file_name, struct stat const *stat_info,
   strcpy (data->file_name, file_name);
   strcpy (data->file_name, file_name);
   data->invert_permissions = invert_permissions;
   data->invert_permissions = invert_permissions;
   data->permstatus = permstatus;
   data->permstatus = permstatus;
-  data->after_symlinks = 0;
+  data->after_links = 0;
   data->stat_info = *stat_info;
   data->stat_info = *stat_info;
   data->next = delayed_set_stat_head;
   data->next = delayed_set_stat_head;
   delayed_set_stat_head = data;
   delayed_set_stat_head = data;
@@ -361,7 +364,7 @@ repair_delayed_set_stat (char const *dir,
 	  quotearg_colon (dir)));
 	  quotearg_colon (dir)));
 }
 }
 
 
-/* After a file/link/symlink/directory creation has failed, see if
+/* After a file/link/directory creation has failed, see if
    it's because some required directory was not present, and if so,
    it's because some required directory was not present, and if so,
    create all required directories.  Return non-zero if a directory
    create all required directories.  Return non-zero if a directory
    was created.  */
    was created.  */
@@ -513,12 +516,12 @@ maybe_recoverable (char *file_name, int *interdir_made)
 }
 }
 
 
 /* Fix the statuses of all directories whose statuses need fixing, and
 /* Fix the statuses of all directories whose statuses need fixing, and
-   which are not ancestors of FILE_NAME.  If AFTER_SYMLINKS is
+   which are not ancestors of FILE_NAME.  If AFTER_LINKS is
    nonzero, do this for all such directories; otherwise, stop at the
    nonzero, do this for all such directories; otherwise, stop at the
    first directory that is marked to be fixed up only after delayed
    first directory that is marked to be fixed up only after delayed
-   symlinks are applied.  */
+   links are applied.  */
 static void
 static void
-apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks)
+apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links)
 {
 {
   size_t file_name_len = strlen (file_name);
   size_t file_name_len = strlen (file_name);
   bool check_for_renamed_directories = 0;
   bool check_for_renamed_directories = 0;
@@ -530,9 +533,9 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks)
       struct stat st;
       struct stat st;
       struct stat const *cur_info = 0;
       struct stat const *cur_info = 0;
 
 
-      check_for_renamed_directories |= data->after_symlinks;
+      check_for_renamed_directories |= data->after_links;
 
 
-      if (after_symlinks < data->after_symlinks
+      if (after_links < data->after_links
 	  || (data->file_name_len < file_name_len
 	  || (data->file_name_len < file_name_len
 	      && file_name[data->file_name_len]
 	      && file_name[data->file_name_len]
 	      && (ISSLASH (file_name[data->file_name_len])
 	      && (ISSLASH (file_name[data->file_name_len])
@@ -776,6 +779,82 @@ extract_file (char *file_name, int typeflag)
   return status;
   return status;
 }
 }
 
 
+/* Create a placeholder file with name FILE_NAME, which will be
+   replaced after other extraction is done by a symbolic link if
+   IS_SYMLINK is true, and by a hard link otherwise.  Set
+   *INTERDIR_MADE if an intermediate directory is made in the
+   process.  */
+
+static int
+create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made)
+{
+  int fd;
+  struct stat st;
+
+  while ((fd = open (file_name, O_WRONLY | O_CREAT | O_EXCL, 0)) < 0)
+    if (! maybe_recoverable (file_name, interdir_made))
+      break;
+
+  if (fd < 0)
+    open_error (file_name);
+  else if (fstat (fd, &st) != 0)
+    {
+      stat_error (file_name);
+      close (fd);
+    }
+  else if (close (fd) != 0)
+    close_error (file_name);
+  else
+    {
+      struct delayed_set_stat *h;
+      struct delayed_link *p =
+	xmalloc (offsetof (struct delayed_link, target)
+		 + strlen (current_stat_info.link_name)
+		 + 1);
+      p->next = delayed_link_head;
+      delayed_link_head = p;
+      p->dev = st.st_dev;
+      p->ino = st.st_ino;
+      p->mtime = st.st_mtime;
+      p->is_symlink = is_symlink;
+      if (is_symlink)
+	{
+	  p->uid = current_stat_info.stat.st_uid;
+	  p->gid = current_stat_info.stat.st_gid;
+	}
+      p->sources = xmalloc (offsetof (struct string_list, string)
+			    + strlen (file_name) + 1);
+      p->sources->next = 0;
+      strcpy (p->sources->string, file_name);
+      strcpy (p->target, current_stat_info.link_name);
+
+      h = delayed_set_stat_head;
+      if (h && ! h->after_links
+	  && strncmp (file_name, h->file_name, h->file_name_len) == 0
+	  && ISSLASH (file_name[h->file_name_len])
+	  && (base_name (file_name) == file_name + h->file_name_len + 1))
+	{
+	  do
+	    {
+	      h->after_links = 1;
+
+	      if (stat (h->file_name, &st) != 0)
+		stat_error (h->file_name);
+	      else
+		{
+		  h->stat_info.st_dev = st.st_dev;
+		  h->stat_info.st_ino = st.st_ino;
+		}
+	    }
+	  while ((h = h->next) && ! h->after_links);
+	}
+
+      return 0;
+    }
+
+  return -1;
+}
+
 static int
 static int
 extract_link (char *file_name, int typeflag)
 extract_link (char *file_name, int typeflag)
 {
 {
@@ -783,6 +862,9 @@ extract_link (char *file_name, int typeflag)
                                              true, absolute_names_option);
                                              true, absolute_names_option);
   int interdir_made = 0;
   int interdir_made = 0;
 
 
+  if (! absolute_names_option && contains_dot_dot (link_name))
+    return create_placeholder_file (file_name, false, &interdir_made);
+
   do
   do
     {
     {
       struct stat st1, st2;
       struct stat st1, st2;
@@ -792,7 +874,7 @@ extract_link (char *file_name, int typeflag)
 
 
       if (status == 0)
       if (status == 0)
 	{
 	{
-	  struct delayed_symlink *ds = delayed_symlink_head;
+	  struct delayed_link *ds = delayed_link_head;
 	  if (ds && lstat (link_name, &st1) == 0)
 	  if (ds && lstat (link_name, &st1) == 0)
 	    for (; ds; ds = ds->next)
 	    for (; ds; ds = ds->next)
 	      if (ds->dev == st1.st_dev
 	      if (ds->dev == st1.st_dev
@@ -831,87 +913,22 @@ static int
 extract_symlink (char *file_name, int typeflag)
 extract_symlink (char *file_name, int typeflag)
 {
 {
 #ifdef HAVE_SYMLINK
 #ifdef HAVE_SYMLINK
-  int status, fd;
+  int status;
   int interdir_made = 0;
   int interdir_made = 0;
 
 
-  if (absolute_names_option
-      || ! (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
-	    || contains_dot_dot (current_stat_info.link_name)))
-    {
-      while ((status = symlink (current_stat_info.link_name, file_name)))
-	if (!maybe_recoverable (file_name, &interdir_made))
-	  break;
-
-      if (status == 0)
-	set_stat (file_name, &current_stat_info.stat, 0, 0, 0, SYMTYPE);
-      else
-	symlink_error (current_stat_info.link_name, file_name);
-    }
-  else
-    {
-      /* This symbolic link is potentially dangerous.  Don't
-	 create it now; instead, create a placeholder file, which
-	 will be replaced after other extraction is done.  */
-      struct stat st;
-
-      while ((fd = open (file_name, O_WRONLY | O_CREAT | O_EXCL, 0)) < 0)
-	if (! maybe_recoverable (file_name, &interdir_made))
-	  break;
-
-      status = -1;
-      if (fd < 0)
-	open_error (file_name);
-      else if (fstat (fd, &st) != 0)
-	{
-	  stat_error (file_name);
-	  close (fd);
-	}
-      else if (close (fd) != 0)
-	close_error (file_name);
-      else
-	{
-	  struct delayed_set_stat *h;
-	  struct delayed_symlink *p = xmalloc (offsetof (struct delayed_symlink, target)
-					       + strlen (current_stat_info.link_name)
-					       + 1);
-	  p->next = delayed_symlink_head;
-	  delayed_symlink_head = p;
-	  p->dev = st.st_dev;
-	  p->ino = st.st_ino;
-	  p->mtime = st.st_mtime;
-	  p->uid = current_stat_info.stat.st_uid;
-	  p->gid = current_stat_info.stat.st_gid;
-	  p->sources = xmalloc (offsetof (struct string_list, string)
-				+ strlen (file_name) + 1);
-	  p->sources->next = 0;
-	  strcpy (p->sources->string, file_name);
-	  strcpy (p->target, current_stat_info.link_name);
-
-	  h = delayed_set_stat_head;
-	  if (h && ! h->after_symlinks
-	      && strncmp (file_name, h->file_name, h->file_name_len) == 0
-	      && ISSLASH (file_name[h->file_name_len])
-	      && (base_name (file_name) == file_name + h->file_name_len + 1))
-	    {
-	      do
-		{
-		  h->after_symlinks = 1;
-
-		  if (stat (h->file_name, &st) != 0)
-		    stat_error (h->file_name);
-		  else
-		    {
-		      h->stat_info.st_dev = st.st_dev;
-		      h->stat_info.st_ino = st.st_ino;
-		    }
-		}
-	      while ((h = h->next) && ! h->after_symlinks);
-	    }
+  if (! absolute_names_option
+      && (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name)
+	  || contains_dot_dot (current_stat_info.link_name)))
+    return create_placeholder_file (file_name, true, &interdir_made);
 
 
-	  status = 0;
-	}
-    }
+  while ((status = symlink (current_stat_info.link_name, file_name)))
+    if (!maybe_recoverable (file_name, &interdir_made))
+      break;
 
 
+  if (status == 0)
+    set_stat (file_name, &current_stat_info.stat, 0, 0, 0, SYMTYPE);
+  else
+    symlink_error (current_stat_info.link_name, file_name);
   return status;
   return status;
 
 
 #else
 #else
@@ -1180,11 +1197,11 @@ extract_archive (void)
 
 
 /* Extract the symbolic links whose final extraction were delayed.  */
 /* Extract the symbolic links whose final extraction were delayed.  */
 static void
 static void
-apply_delayed_symlinks (void)
+apply_delayed_links (void)
 {
 {
-  struct delayed_symlink *ds;
+  struct delayed_link *ds;
 
 
-  for (ds = delayed_symlink_head; ds; )
+  for (ds = delayed_link_head; ds; )
     {
     {
       struct string_list *sources = ds->sources;
       struct string_list *sources = ds->sources;
       char const *valid_source = 0;
       char const *valid_source = 0;
@@ -1195,7 +1212,7 @@ apply_delayed_symlinks (void)
 	  struct stat st;
 	  struct stat st;
 
 
 	  /* Make sure the placeholder file is still there.  If not,
 	  /* Make sure the placeholder file is still there.  If not,
-	     don't create a symlink, as the placeholder was probably
+	     don't create a link, as the placeholder was probably
 	     removed by a later extraction.  */
 	     removed by a later extraction.  */
 	  if (lstat (source, &st) == 0
 	  if (lstat (source, &st) == 0
 	      && st.st_dev == ds->dev
 	      && st.st_dev == ds->dev
@@ -1208,6 +1225,11 @@ apply_delayed_symlinks (void)
 		unlink_error (source);
 		unlink_error (source);
 	      else if (valid_source && link (valid_source, source) == 0)
 	      else if (valid_source && link (valid_source, source) == 0)
 		;
 		;
+	      else if (!ds->is_symlink)
+		{
+		  if (link (ds->target, source) != 0)
+		    link_error (ds->target, source);
+		}
 	      else if (symlink (ds->target, source) != 0)
 	      else if (symlink (ds->target, source) != 0)
 		symlink_error (ds->target, source);
 		symlink_error (ds->target, source);
 	      else
 	      else
@@ -1228,13 +1250,13 @@ apply_delayed_symlinks (void)
 	}
 	}
 
 
       {
       {
-	struct delayed_symlink *next = ds->next;
+	struct delayed_link *next = ds->next;
 	free (ds);
 	free (ds);
 	ds = next;
 	ds = next;
       }
       }
     }
     }
 
 
-  delayed_symlink_head = 0;
+  delayed_link_head = 0;
 }
 }
 
 
 /* Finish the extraction of an archive.  */
 /* Finish the extraction of an archive.  */
@@ -1244,12 +1266,12 @@ extract_finish (void)
   /* First, fix the status of ordinary directories that need fixing.  */
   /* First, fix the status of ordinary directories that need fixing.  */
   apply_nonancestor_delayed_set_stat ("", 0);
   apply_nonancestor_delayed_set_stat ("", 0);
 
 
-  /* Then, apply delayed symlinks, so that they don't affect delayed
+  /* Then, apply delayed links, so that they don't affect delayed
      directory status-setting for ordinary directories.  */
      directory status-setting for ordinary directories.  */
-  apply_delayed_symlinks ();
+  apply_delayed_links ();
 
 
   /* Finally, fix the status of directories that are ancestors
   /* Finally, fix the status of directories that are ancestors
-     of delayed symlinks.  */
+     of delayed links.  */
   apply_nonancestor_delayed_set_stat ("", 1);
   apply_nonancestor_delayed_set_stat ("", 1);
 }
 }