]> git.ipfire.org Git - thirdparty/tar.git/commitdiff
Avoid need to append "/." for make_directories
authorPaul Eggert <eggert@cs.ucla.edu>
Sat, 18 Jul 2026 20:24:40 +0000 (13:24 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Thu, 30 Jul 2026 06:12:46 +0000 (23:12 -0700)
* src/extract.c (make_directories): New arg JUST_PARENT.
All callers changed.
* src/misc.c (struct wd.name, add_wd, chdir_arg):
Use char *, not char const *, to let make_directories
temporarily alter slashes.  All uses changed.
(chdir_do): Do not create a new name with trailing "/."
because make_directories no longer needs this.

src/common.h
src/extract.c
src/misc.c
src/names.c

index 586288f4b01a1053a281837ccd230246fb8dec27..51aac6a88ba006ef29313c5747c33f718454fcc9 100644 (file)
@@ -558,7 +558,7 @@ void verify_volume (void);
 extern dev_t root_device;
 
 void extr_init (void);
-int make_directories (char *file_name);
+int make_directories (char *file_name, bool just_parent);
 void extract_archive (void);
 void extract_finish (void);
 bool rename_directory (char *src, char *dst);
@@ -782,7 +782,7 @@ idx_t blocking_write (int fd, void const *buf, idx_t count);
 enum { BADFD = AT_FDCWD == -1 ? -2 : -1 };
 
 extern idx_t chdir_current;
-idx_t chdir_arg (char const *dir);
+idx_t chdir_arg (char *dir);
 void chdir_do (idx_t dir, bool create);
 struct chdir_id { int err; dev_t st_dev; ino_t st_ino; } chdir_id (void);
 struct fdbase fdbase (char const *);
index 98785e447d0f7cf89ff8c2036f537b974d3009e6..7286225090689a422adf77608b40db98f753f601 100644 (file)
@@ -770,8 +770,8 @@ fixup_delayed_set_stat (char const *src, char const *dst)
     }
 }
 
-/* Ensure that FILE_NAME's parent is a directory by creating the
-   directory and ancestors as needed.
+/* Ensure that FILE_NAME is a directory by creating it and ancestors as needed.
+   If JUST_PARENT do so for FILE_NAME's parent directory, not FILE_NAME itself.
    Do not overwrite existing files.
    Possibly temporarily modify FILE_NAME if it contains slashes,
    but restore FILE_NAME before returning.
@@ -779,7 +779,7 @@ fixup_delayed_set_stat (char const *src, char const *dst)
    some other process), zero if a directory is already there, and
    negative (issuing a diagnostic) otherwise.  */
 int
-make_directories (char *file_name)
+make_directories (char *file_name, bool just_parent)
 {
   char *cursor0 = file_name + FILE_SYSTEM_PREFIX_LEN (file_name);
   char *cursor;                        /* points into the file name */
@@ -787,29 +787,34 @@ make_directories (char *file_name)
   int parent_errno;
   int result = 0;
 
-  for (cursor = cursor0; *cursor; cursor++)
+  for (cursor = cursor0; ; cursor++)
     {
       mode_t mode;
       mode_t desired_mode;
       int status;
 
-      if (! ISSLASH (*cursor))
-       continue;
-
-      /* Avoid mkdir of empty string, if leading or double '/'.  */
-
-      if (cursor == cursor0 || ISSLASH (cursor[-1]))
-       continue;
-
-      /* Avoid mkdir where last part of file name is "." or "..".  */
+      char c = *cursor;
+      if (! ISSLASH (c))
+       {
+         if (c)
+           continue;
+         if (just_parent)
+           break;
+       }
 
-      if (cursor[-1] == '.'
-         && (cursor == cursor0 + 1 || ISSLASH (cursor[-2])
-             || (cursor[-2] == '.'
-                 && (cursor == cursor0 + 2 || ISSLASH (cursor[-3])))))
-       continue;
+      /* Avoid mkdir of empty string (if leading or double slash),
+        or where last part of file name is "." or "..".  */
+      if (cursor == cursor0 || ISSLASH (cursor[-1])
+         || (cursor[-1] == '.'
+             && (cursor == cursor0 + 1 || ISSLASH (cursor[-2])
+                 || (cursor[-2] == '.'
+                     && (cursor == cursor0 + 2 || ISSLASH (cursor[-3]))))))
+       {
+         if (c)
+           continue;
+         break;
+       }
 
-      char c = *cursor;
       *cursor = '\0';          /* truncate the name there */
       desired_mode = MODE_RWX & ~ newdir_umask;
       mode = desired_mode | (we_are_root ? 0 : MODE_WXUSR);
@@ -846,6 +851,8 @@ make_directories (char *file_name)
            break;
          }
 
+      if (!c)
+       break;
       *cursor = c;
     }
 
@@ -982,7 +989,7 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made)
 
     case ENOENT:
       /* Attempt creating missing intermediate directories. */
-      if (0 < make_directories (file_name))
+      if (0 < make_directories (file_name, true))
        return RECOVER_OK;
       break;
 
@@ -2086,7 +2093,7 @@ rename_directory (char *src, char *dst)
       switch (e)
        {
        case ENOENT:
-         if (0 <= make_directories (dst))
+         if (0 <= make_directories (dst, true))
            {
              f = fdbase (dst);
              if (f.fd != BADFD
index 35e28e99a0b782a4f4c6608f59fd6b5a48ad565a..d8fbea47a8c48b1e0d631bbb4a7794653676b20f 100644 (file)
@@ -959,7 +959,7 @@ set_file_atime (int fd, int parentfd, char const *file, struct timespec atime)
 struct wd
 {
   /* The directory's name.  */
-  char const *name;
+  char *name;
   /* "Absolute" path representing this directory; in the contrast to
      the real absolute pathname, it can contain /../ components (see
      normalize_filename_x for the reason of it).  It is NULL if the
@@ -1020,7 +1020,7 @@ chdir_count (void)
    DFD is either AT_FDWD for the initial "." entry,
    or 0 meaning the file descriptor is not open yet.  */
 static void
-add_wd (char const *dir, int dfd)
+add_wd (char *dir, int dfd)
 {
   wd[wd_count].name = dir;
   wd[wd_count].abspath = NULL;
@@ -1051,7 +1051,7 @@ ensure_wd (void)
       int n_incr_min = 4;
 
       wd = xpalloc (NULL, &wd_alloc, n_incr_min, -1, sizeof *wd);
-      add_wd (".", AT_FDCWD);
+      add_wd ((char *) ".", AT_FDCWD);
     }
 }
 
@@ -1060,7 +1060,7 @@ ensure_wd (void)
    two targets to the vector.  However, if DIR is "." or an equivalent,
    just reuse the last item in the vector.  */
 idx_t
-chdir_arg (char const *dir)
+chdir_arg (char *dir)
 {
   ensure_wd ();
 
@@ -1124,27 +1124,16 @@ chdir_do (idx_t i, bool create)
            {
              if (create)
                {
-                 char *dir_with_dot;
                  struct open_how saved_open_searchdir_how = open_searchdir_how;
                  /* Don't use O_BENEATH during creation of the
                     directory. The one-top-level directory is
                     allowed to be given as an absolute path.  */
                  open_searchdir_how.resolve = 0;
-                 /* Append a dot. make_directories creates
-                    directories up to and excluding the last
-                    component of the path. So, in order to create
-                    "a/b", we need to pass "a/b/." to it. */
-                 {
-                   namebuf_t nbuf = namebuf_create (curr->name);
-                   namebuf_add_dir (nbuf, ".");
-                   dir_with_dot = namebuf_finish (nbuf);
-                 }
-                 if (0 <= make_directories (dir_with_dot))
+                 if (0 <= make_directories (curr->name, false))
                    /* Directory created, retry */
                    fd = openat (chdir_fd, curr->name,
                                 open_searchdir_how.flags & ~O_NOFOLLOW);
                  open_searchdir_how = saved_open_searchdir_how;
-                 free (dir_with_dot);
                  /* Either the creation or open failed */
                  if (fd < 0)
                    open_fatal (curr->name);
index b26c546f79edd5ec35cfdf2aab55e67d5033e5f7..58dd0711e48e4312a08bd20fb998b772ba7d6515 100644 (file)
@@ -875,7 +875,7 @@ static idx_t name_buffer_length; /* allocated length of name_buffer */
 void
 name_init (void)
 {
-  chdir_do (chdir_arg ("."), false);
+  chdir_do (chdir_arg ((char *) "."), false);
   name_list_adjust ();
 }
 \f