From: Paul Eggert Date: Sat, 18 Jul 2026 20:24:40 +0000 (-0700) Subject: Avoid need to append "/." for make_directories X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b7704c8d989cbc63d681bc7d3a59764f1adb4e99;p=thirdparty%2Ftar.git Avoid need to append "/." for make_directories * 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. --- diff --git a/src/common.h b/src/common.h index 586288f4..51aac6a8 100644 --- a/src/common.h +++ b/src/common.h @@ -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 *); diff --git a/src/extract.c b/src/extract.c index 98785e44..72862250 100644 --- a/src/extract.c +++ b/src/extract.c @@ -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 diff --git a/src/misc.c b/src/misc.c index 35e28e99..d8fbea47 100644 --- a/src/misc.c +++ b/src/misc.c @@ -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); diff --git a/src/names.c b/src/names.c index b26c546f..58dd0711 100644 --- a/src/names.c +++ b/src/names.c @@ -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 (); }