summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2022-08-26 15:23:23 -0500
committerPaul Eggert <eggert@cs.ucla.edu>2022-08-26 16:39:16 -0500
commit0b74885e81b90d6ab4890b195dce99ca9109fe59 (patch)
tree6d4b3df349fa35a26b8c3bab24d469584791204a /src
parent258d1c44e5ee7c58b28bf0000e9d737df6081885 (diff)
downloadtar-0b74885e81b90d6ab4890b195dce99ca9109fe59.tar.gz
Fix bug with -x --xattr read-only files
Problem reported by Kevin Raymond in: https://bugzilla.redhat.com/show_bug.cgi?id=1886540 * src/extract.c (open_output_file): If we already created the empty file, do not open with O_EXCL, or with O_CREAT or O_TRUNC for that matter. Instead, use only O_NOFOLLOW to avoid some races. When estimating current mode, use openflag & O_EXCL rather than overwriting_old_files. (extract_file): Also invert S_IWUSR if it’s not set. * tests/xattr08.at: New test. * tests/Makefile.am, tests/testsuite.at: Add it.
Diffstat (limited to 'src')
-rw-r--r--src/extract.c31
1 files changed, 15 insertions, 16 deletions
diff --git a/src/extract.c b/src/extract.c
index 2813c961..c1f5731b 100644
--- a/src/extract.c
+++ b/src/extract.c
@@ -1189,14 +1189,12 @@ open_output_file (char const *file_name, int typeflag, mode_t mode,
int fd;
bool overwriting_old_files = old_files_option == OVERWRITE_OLD_FILES;
int openflag = (O_WRONLY | O_BINARY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK
- | O_CREAT
- | (overwriting_old_files
- ? O_TRUNC | (dereference_option ? 0 : O_NOFOLLOW)
- : O_EXCL));
-
- /* File might be created in set_xattr. So clear O_EXCL to avoid open() fail */
- if (file_created)
- openflag = openflag & ~O_EXCL;
+ | (file_created
+ ? O_NOFOLLOW
+ : (O_CREAT
+ | (overwriting_old_files
+ ? O_TRUNC | (dereference_option ? 0 : O_NOFOLLOW)
+ : O_EXCL))));
if (typeflag == CONTTYPE)
{
@@ -1227,7 +1225,12 @@ open_output_file (char const *file_name, int typeflag, mode_t mode,
fd = openat (chdir_fd, file_name, openflag, mode);
if (0 <= fd)
{
- if (overwriting_old_files)
+ if (openflag & O_EXCL)
+ {
+ *current_mode = mode & ~ current_umask;
+ *current_mode_mask = MODE_RWX;
+ }
+ else
{
struct stat st;
if (fstat (fd, &st) != 0)
@@ -1246,11 +1249,6 @@ open_output_file (char const *file_name, int typeflag, mode_t mode,
*current_mode = st.st_mode;
*current_mode_mask = ALL_MODE_BITS;
}
- else
- {
- *current_mode = mode & ~ current_umask;
- *current_mode_mask = MODE_RWX;
- }
}
return fd;
@@ -1268,8 +1266,9 @@ extract_file (char *file_name, int typeflag)
bool interdir_made = false;
mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX
& ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0));
- mode_t invert_permissions = 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO)
- : 0;
+ mode_t invert_permissions
+ = ((0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0)
+ | ((mode & S_IWUSR) ^ S_IWUSR));
mode_t current_mode = 0;
mode_t current_mode_mask = 0;