summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2022-06-13 17:02:54 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2022-06-13 17:03:49 -0700
commitd935dc7d1c150b3425dd43dc13a4dd2e2b712c26 (patch)
tree6f724e6af4a347e6e81aca69f30d14142f4a0509
parent9f0e54ab2fcae945eef441ebb0d217bbccc6c75b (diff)
downloadtar-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.c28
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;