diff options
author | Paul Eggert <eggert@cs.ucla.edu> | 2022-06-13 17:02:54 -0700 |
---|---|---|
committer | Paul Eggert <eggert@cs.ucla.edu> | 2022-06-13 17:03:49 -0700 |
commit | d935dc7d1c150b3425dd43dc13a4dd2e2b712c26 (patch) | |
tree | 6f724e6af4a347e6e81aca69f30d14142f4a0509 | |
parent | 9f0e54ab2fcae945eef441ebb0d217bbccc6c75b (diff) | |
download | tar-d935dc7d1c150b3425dd43dc13a4dd2e2b712c26.tar.gz |
Avoid EOVERFLOW problems in some symlink tests
* src/extract.c (is_directory_link): New arg ST. Caller changed.
(is_directory_link, open_output_file):
Use readlinkat, not fstatat, to determine whether a string
names a symlink. This avoids EOVERFLOW issues.
(extract_dir): Avoid duplicate calls to fstatat when
keep_directory_symlink_option && fstatat_flags == 0
and the file is a symlink to an existing file.
-rw-r--r-- | src/extract.c | 28 |
1 files changed, 12 insertions, 16 deletions
diff --git a/src/extract.c b/src/extract.c index fda4617d..6d2543f0 100644 --- a/src/extract.c +++ b/src/extract.c @@ -982,18 +982,12 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links) static bool -is_directory_link (const char *file_name) +is_directory_link (char const *file_name, struct stat *st) { - struct stat st; - int e = errno; - int res; - - res = (fstatat (chdir_fd, file_name, &st, AT_SYMLINK_NOFOLLOW) == 0 && - S_ISLNK (st.st_mode) && - fstatat (chdir_fd, file_name, &st, 0) == 0 && - S_ISDIR (st.st_mode)); - errno = e; - return res; + char buf[1]; + return (0 <= readlinkat (chdir_fd, file_name, buf, sizeof buf) + && fstatat (chdir_fd, file_name, st, 0) == 0 + && S_ISDIR (st->st_mode)); } /* Given struct stat of a directory (or directory member) whose ownership @@ -1066,11 +1060,14 @@ extract_dir (char *file_name, int typeflag) || old_files_option == OVERWRITE_OLD_FILES)) { struct stat st; + st.st_mode = 0; - if (keep_directory_symlink_option && is_directory_link (file_name)) + if (keep_directory_symlink_option + && is_directory_link (file_name, &st)) return 0; - if (deref_stat (file_name, &st) == 0) + if ((st.st_mode != 0 && fstatat_flags == 0) + || deref_stat (file_name, &st) == 0) { current_mode = st.st_mode; current_mode_mask = ALL_MODE_BITS; @@ -1178,9 +1175,8 @@ open_output_file (char const *file_name, int typeflag, mode_t mode, if (! HAVE_WORKING_O_NOFOLLOW && overwriting_old_files && ! dereference_option) { - struct stat st; - if (fstatat (chdir_fd, file_name, &st, AT_SYMLINK_NOFOLLOW) == 0 - && S_ISLNK (st.st_mode)) + char buf[1]; + if (0 <= readlinkat (chdir_fd, file_name, buf, sizeof buf)) { errno = ELOOP; return -1; |