SHA256
3
0
forked from pool/tar

Accepting request 1032467 from Base:System

OBS-URL: https://build.opensuse.org/request/show/1032467
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/tar?expand=0&rev=78
This commit is contained in:
Dominique Leuenberger 2022-11-02 11:46:18 +00:00 committed by Git OBS Bridge
commit 04be33ef31
6 changed files with 433 additions and 113 deletions

View File

@ -1,49 +1,48 @@
From 49b9f54ff66d126c4c0d58ccfbc2b85f96f845fc Mon Sep 17 00:00:00 2001 From 79d1ac38c19faad64f0e993180bf1ad27f217072 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu> From: James Abbatiello <abbeyj@gmail.com>
Date: Thu, 31 Mar 2022 18:26:03 -0700 Date: Fri, 10 Jun 2022 18:25:13 -0700
Subject: [PATCH] Retry file creation more aggressively Subject: tar: fix race condition
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* src/extract.c (maybe_recoverable): When deciding whether to Problem reported in:
retry file creation, dont insist on our making intermediate
subdirectories; its OK if some other process made them.
Problem reported by James Abbatiello in:
https://lists.gnu.org/r/bug-tar/2022-03/msg00000.html https://lists.gnu.org/r/bug-tar/2022-03/msg00000.html
* src/extract.c (make_directories): Retry the file creation as
long as the directory exists, regardless of whether tar itself
created the directory.
Copyright-paperwork-exempt: Yes
--- ---
src/extract.c | 2 +- src/extract.c | 16 ++++++++--------
1 file changed, 1 insertion(+), 1 deletion(-) 1 file changed, 8 insertions(+), 8 deletions(-)
Index: tar-1.34/src/extract.c diff --git a/src/extract.c b/src/extract.c
=================================================================== index 0753dec..fda4617 100644
--- tar-1.34.orig/src/extract.c --- a/src/extract.c
+++ tar-1.34/src/extract.c +++ b/src/extract.c
@@ -645,9 +645,9 @@ fixup_delayed_set_stat (char const *src, @@ -638,10 +638,9 @@ fixup_delayed_set_stat (char const *src, char const *dst)
it's because some required directory was not present, and if so,
/* After a file/link/directory creation has failed due to ENOENT,
create all required directories. Return zero if all the required create all required directories. Return zero if all the required
directories were created, nonzero (issuing a diagnostic) otherwise. - directories were created, nonzero (issuing a diagnostic) otherwise.
- Set *INTERDIR_MADE if at least one directory was created. */ - Set *INTERDIR_MADE if at least one directory was created. */
+ */ + directories were created, nonzero (issuing a diagnostic) otherwise. */
static int static int
-make_directories (char *file_name, bool *interdir_made) -make_directories (char *file_name, bool *interdir_made)
+make_directories (char *file_name) +make_directories (char *file_name)
{ {
char *cursor0 = file_name + FILE_SYSTEM_PREFIX_LEN (file_name); char *cursor0 = file_name + FILE_SYSTEM_PREFIX_LEN (file_name);
char *cursor; /* points into the file name */ char *cursor; /* points into the file name */
@@ -689,7 +689,6 @@ make_directories (char *file_name, bool @@ -685,7 +684,6 @@ make_directories (char *file_name, bool *interdir_made)
desired_mode, AT_SYMLINK_NOFOLLOW); desired_mode, AT_SYMLINK_NOFOLLOW);
print_for_mkdir (file_name, cursor - file_name, desired_mode); print_for_mkdir (file_name, cursor - file_name, desired_mode);
- *interdir_made = true; - *interdir_made = true;
parent_end = NULL;
} }
else if (errno == EEXIST) else
status = 0; @@ -841,8 +839,11 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made)
@@ -829,8 +828,11 @@ maybe_recoverable (char *file_name, bool
case ENOENT: case ENOENT:
/* Attempt creating missing intermediate directories. */ /* Attempt creating missing intermediate directories. */
- if (make_directories (file_name, interdir_made) == 0 && *interdir_made) - if (make_directories (file_name, interdir_made) == 0)
- return RECOVER_OK; - return RECOVER_OK;
+ if (make_directories (file_name) == 0) + if (make_directories (file_name) == 0)
+ { + {
@ -53,87 +52,7 @@ Index: tar-1.34/src/extract.c
break; break;
default: default:
@@ -1329,7 +1331,7 @@ extract_file (char *file_name, int typef @@ -1944,12 +1945,11 @@ rename_directory (char *src, char *dst)
first. If it doesn't exist, there is no matching entry in the list.
Otherwise, look for the entry in list which has the matching dev
and ino numbers.
-
+
This approach avoids scanning the singly-linked list in obvious cases
and does not rely on comparing file names, which may differ for
various reasons (e.g. relative vs. absolute file names).
@@ -1342,14 +1344,14 @@ find_delayed_link_source (char const *na
if (!delayed_link_head)
return NULL;
-
+
if (fstatat (chdir_fd, name, &st, AT_SYMLINK_NOFOLLOW))
{
if (errno != ENOENT)
stat_error (name);
return NULL;
}
-
+
for (dl = delayed_link_head; dl; dl = dl->next)
{
if (dl->dev == st.st_dev && dl->ino == st.st_ino)
@@ -1357,7 +1359,7 @@ find_delayed_link_source (char const *na
}
return dl;
}
-
+
/* Create a placeholder file with name FILE_NAME, which will be
replaced after other extraction is done by a symbolic link if
IS_SYMLINK is true, and by a hard link otherwise. Set
@@ -1385,7 +1387,7 @@ create_placeholder_file (char *file_name
*/
return 0;
}
-
+
switch (maybe_recoverable (file_name, false, interdir_made))
{
case RECOVER_OK:
@@ -1467,7 +1469,7 @@ extract_link (char *file_name, int typef
char const *link_name;
int rc;
struct delayed_link *dl;
-
+
link_name = current_stat_info.link_name;
if (! absolute_names_option && contains_dot_dot (link_name))
@@ -1475,7 +1477,7 @@ extract_link (char *file_name, int typef
dl = find_delayed_link_source (link_name);
if (dl)
return create_placeholder_file (file_name, false, &interdir_made, dl);
-
+
do
{
struct stat st1, st2;
@@ -1697,7 +1699,7 @@ prepare_to_extract (char const *file_nam
case GNUTYPE_VOLHDR:
return false;
-
+
case GNUTYPE_MULTIVOL:
ERROR ((0, 0,
_("%s: Cannot extract -- file is continued from another volume"),
@@ -1753,7 +1755,7 @@ prepare_to_extract (char const *file_nam
}
}
*fun = extractor;
-
+
return true;
}
@@ -1934,12 +1936,11 @@ rename_directory (char *src, char *dst)
else else
{ {
int e = errno; int e = errno;
@ -147,3 +66,6 @@ Index: tar-1.34/src/extract.c
{ {
if (renameat (chdir_fd, src, chdir_fd, dst) == 0) if (renameat (chdir_fd, src, chdir_fd, dst) == 0)
return true; return true;
--
cgit v1.1

View File

@ -0,0 +1,76 @@
From d935dc7d1c150b3425dd43dc13a4dd2e2b712c26 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Mon, 13 Jun 2022 17:02:54 -0700
Subject: 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.
---
src/extract.c | 28 ++++++++++++----------------
1 file changed, 12 insertions(+), 16 deletions(-)
diff --git a/src/extract.c b/src/extract.c
index fda4617..6d2543f 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;
--
cgit v1.1

View File

@ -0,0 +1,187 @@
From 17debecd7300e94f590b8ce167a8c0735cb6d57d Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org>
Date: Sat, 22 Oct 2022 12:06:45 +0300
Subject: Fix savannah bug #63123
The bug was introduced by commit 79d1ac38c1, which didn't take into
account all the consequences of returning RECOVER_OK on EEXIST, in
particular interactions with the delayed_set_stat logic.
The commit 79d1ac38c1 is reverted (the bug it was intended to fix
was actually fixed by 79a442d7b0). Instead:
* src/extract.c (maybe_recoverable): Don't call maybe_recoverable
if EEXIST is reported when UNLINK_FIRST_OLD_FILES option is set.
---
src/extract.c | 108 +++++++++++++++++++++++++++++++---------------------------
1 file changed, 58 insertions(+), 50 deletions(-)
diff --git a/src/extract.c b/src/extract.c
index 78de47f..37ab295 100644
--- a/src/extract.c
+++ b/src/extract.c
@@ -679,9 +679,10 @@ fixup_delayed_set_stat (char const *src, char const *dst)
/* After a file/link/directory creation has failed due to ENOENT,
create all required directories. Return zero if all the required
- directories were created, nonzero (issuing a diagnostic) otherwise. */
+ directories were created, nonzero (issuing a diagnostic) otherwise.
+ Set *INTERDIR_MADE if at least one directory was created. */
static int
-make_directories (char *file_name)
+make_directories (char *file_name, bool *interdir_made)
{
char *cursor0 = file_name + FILE_SYSTEM_PREFIX_LEN (file_name);
char *cursor; /* points into the file name */
@@ -725,6 +726,7 @@ make_directories (char *file_name)
desired_mode, AT_SYMLINK_NOFOLLOW);
print_for_mkdir (file_name, cursor - file_name, desired_mode);
+ *interdir_made = true;
parent_end = NULL;
}
else
@@ -879,12 +881,9 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made)
FALLTHROUGH;
case ENOENT:
- /* Attempt creating missing intermediate directories. */
- if (make_directories (file_name) == 0)
- {
- *interdir_made = true;
- return RECOVER_OK;
- }
+ /* Attempt creating missing intermediate directories. */
+ if (make_directories (file_name, interdir_made) == 0)
+ return RECOVER_OK;
break;
default:
@@ -1072,61 +1071,69 @@ extract_dir (char *file_name, int typeflag)
break;
}
- if (errno == EEXIST
- && (interdir_made
+ if (errno == EEXIST)
+ {
+ if (interdir_made
|| keep_directory_symlink_option
|| old_files_option == NO_OVERWRITE_DIR_OLD_FILES
|| old_files_option == DEFAULT_OLD_FILES
- || old_files_option == OVERWRITE_OLD_FILES))
- {
- struct stat st;
- st.st_mode = 0;
-
- if (keep_directory_symlink_option
- && is_directory_link (file_name, &st))
- return 0;
-
- if ((st.st_mode != 0 && fstatat_flags == 0)
- || deref_stat (file_name, &st) == 0)
+ || old_files_option == OVERWRITE_OLD_FILES)
{
- current_mode = st.st_mode;
- current_mode_mask = ALL_MODE_BITS;
+ struct stat st;
+ st.st_mode = 0;
+
+ if (keep_directory_symlink_option
+ && is_directory_link (file_name, &st))
+ return 0;
- if (S_ISDIR (current_mode))
+ if ((st.st_mode != 0 && fstatat_flags == 0)
+ || deref_stat (file_name, &st) == 0)
{
- if (interdir_made)
- {
- repair_delayed_set_stat (file_name, &st);
- return 0;
- }
- else if (old_files_option == NO_OVERWRITE_DIR_OLD_FILES)
+ current_mode = st.st_mode;
+ current_mode_mask = ALL_MODE_BITS;
+
+ if (S_ISDIR (current_mode))
{
- /* Temporarily change the directory mode to a safe
- value, to be able to create files in it, should
- the need be.
- */
- mode = safe_dir_mode (&st);
- status = fd_chmod(-1, file_name, mode,
- AT_SYMLINK_NOFOLLOW, DIRTYPE);
- if (status == 0)
+ if (interdir_made)
{
- /* Store the actual directory mode, to be restored
- later.
- */
- current_stat_info.stat = st;
- current_mode = mode & ~ current_umask;
- current_mode_mask = MODE_RWX;
- atflag = AT_SYMLINK_NOFOLLOW;
- break;
+ repair_delayed_set_stat (file_name, &st);
+ return 0;
}
- else
+ else if (old_files_option == NO_OVERWRITE_DIR_OLD_FILES)
{
- chmod_error_details (file_name, mode);
+ /* Temporarily change the directory mode to a safe
+ value, to be able to create files in it, should
+ the need be.
+ */
+ mode = safe_dir_mode (&st);
+ status = fd_chmod (-1, file_name, mode,
+ AT_SYMLINK_NOFOLLOW, DIRTYPE);
+ if (status == 0)
+ {
+ /* Store the actual directory mode, to be restored
+ later.
+ */
+ current_stat_info.stat = st;
+ current_mode = mode & ~ current_umask;
+ current_mode_mask = MODE_RWX;
+ atflag = AT_SYMLINK_NOFOLLOW;
+ break;
+ }
+ else
+ {
+ chmod_error_details (file_name, mode);
+ }
}
+ break;
}
- break;
}
}
+ else if (old_files_option == UNLINK_FIRST_OLD_FILES)
+ {
+ status = 0;
+ break;
+ }
+
errno = EEXIST;
}
@@ -1978,11 +1985,12 @@ rename_directory (char *src, char *dst)
else
{
int e = errno;
+ bool interdir_made;
switch (e)
{
case ENOENT:
- if (make_directories (dst) == 0)
+ if (make_directories (dst, &interdir_made) == 0)
{
if (renameat (chdir_fd, src, chdir_fd, dst) == 0)
return true;
--
cgit v1.1

View File

@ -0,0 +1,117 @@
From 79a442d7b0e92622794bfa41dee18a28e450a0dc Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Thu, 9 Jun 2022 22:09:34 -0700
Subject: tar: fix race condition
Problem reported by James Abbatiello in:
https://lists.gnu.org/r/bug-tar/2022-03/msg00000.html
* src/extract.c (make_directories): Do not assume that when
mkdirat fails with errno == EEXIST that there is an existing file
that can be statted. It could be a dangling symlink. Instead,
wait until the end and stat it.
---
src/extract.c | 61 ++++++++++++++++++++++++++++++++++++++---------------------
2 files changed, 43 insertions(+), 23 deletions(-)
diff --git a/src/extract.c b/src/extract.c
index e7be463..0753dec 100644
--- a/src/extract.c
+++ b/src/extract.c
@@ -636,8 +636,7 @@ fixup_delayed_set_stat (char const *src, char const *dst)
}
}
-/* After a file/link/directory creation has failed, see if
- it's because some required directory was not present, and if so,
+/* After a file/link/directory creation has failed due to ENOENT,
create all required directories. Return zero if all the required
directories were created, nonzero (issuing a diagnostic) otherwise.
Set *INTERDIR_MADE if at least one directory was created. */
@@ -646,6 +645,8 @@ make_directories (char *file_name, bool *interdir_made)
{
char *cursor0 = file_name + FILE_SYSTEM_PREFIX_LEN (file_name);
char *cursor; /* points into the file name */
+ char *parent_end = NULL;
+ int parent_errno;
for (cursor = cursor0; *cursor; cursor++)
{
@@ -685,31 +686,47 @@ make_directories (char *file_name, bool *interdir_made)
print_for_mkdir (file_name, cursor - file_name, desired_mode);
*interdir_made = true;
+ parent_end = NULL;
}
- else if (errno == EEXIST)
- status = 0;
else
- {
- /* Check whether the desired file exists. Even when the
- file exists, mkdir can fail with some errno value E other
- than EEXIST, so long as E describes an error condition
- that also applies. */
- int e = errno;
- struct stat st;
- status = fstatat (chdir_fd, file_name, &st, 0);
- if (status)
- {
- errno = e;
- mkdir_error (file_name);
- }
- }
+ switch (errno)
+ {
+ case ELOOP: case ENAMETOOLONG: case ENOENT: case ENOTDIR:
+ /* FILE_NAME doesn't exist and couldn't be created; fail now. */
+ mkdir_error (file_name);
+ *cursor = '/';
+ return status;
+
+ default:
+ /* FILE_NAME may be an existing directory so do not fail now.
+ Instead, arrange to check at loop exit, assuming this is
+ the last loop iteration. */
+ parent_end = cursor;
+ parent_errno = errno;
+ break;
+ }
*cursor = '/';
- if (status)
- return status;
}
- return 0;
+ if (!parent_end)
+ return 0;
+
+ /* Although we did not create the parent directory, some other
+ process may have created it, so check whether it exists now. */
+ *parent_end = '\0';
+ struct stat st;
+ int stat_status = fstatat (chdir_fd, file_name, &st, 0);
+ if (!stat_status && !S_ISDIR (st.st_mode))
+ stat_status = -1;
+ if (stat_status)
+ {
+ errno = parent_errno;
+ mkdir_error (file_name);
+ }
+ *parent_end = '/';
+
+ return stat_status;
}
/* Return true if FILE_NAME (with status *STP, if STP) is not a
@@ -824,7 +841,7 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made)
case ENOENT:
/* Attempt creating missing intermediate directories. */
- if (make_directories (file_name, interdir_made) == 0 && *interdir_made)
+ if (make_directories (file_name, interdir_made) == 0)
return RECOVER_OK;
break;
--
cgit v1.1

View File

@ -1,3 +1,13 @@
-------------------------------------------------------------------
Thu Oct 27 13:55:03 UTC 2022 - Danilo Spinella <danilo.spinella@suse.com>
- Fix unexpected inconsistency when making directory, bsc#1203600
* tar-avoid-overflow-in-symlinks-tests.patch
* tar-fix-extract-unlink.patch
- Update race condition fix, bsc#1200657
* tar-fix-race-condition.patch
- Refresh bsc1200657.patch
------------------------------------------------------------------- -------------------------------------------------------------------
Sat Aug 20 06:23:22 UTC 2022 - Dirk Müller <dmueller@suse.com> Sat Aug 20 06:23:22 UTC 2022 - Dirk Müller <dmueller@suse.com>

View File

@ -39,9 +39,14 @@ Patch3: %{name}-ignore_lone_zero_blocks.patch
Patch5: add_readme-tests.patch Patch5: add_readme-tests.patch
Patch6: tar-PIE.patch Patch6: tar-PIE.patch
Patch7: tests-skip-time01-on-32bit-time_t.patch Patch7: tests-skip-time01-on-32bit-time_t.patch
# PATCH-FIX-OPENSUSE danilo.spinella@suse.com bsc#1200657 # PATCH-FIX-UPSTREAM danilo.spinella@suse.com bsc#1200657
# fix race condition while creating intermediate subdirectories # fix race condition while creating intermediate subdirectories
Patch8: bsc1200657.patch Patch8: tar-fix-race-condition.patch
# PATCH-FIX-UPSTREAM danilo.spinella@suse.com bsc#1203600
# Unexpected inconsistency when making directory
Patch9: tar-avoid-overflow-in-symlinks-tests.patch
Patch10: bsc1200657.patch
Patch11: tar-fix-extract-unlink.patch
BuildRequires: automake >= 1.15 BuildRequires: automake >= 1.15
BuildRequires: libacl-devel BuildRequires: libacl-devel
BuildRequires: libselinux-devel BuildRequires: libselinux-devel
@ -113,6 +118,9 @@ it may as well access remote devices or files.
%patch6 -p1 %patch6 -p1
%patch7 -p1 %patch7 -p1
%patch8 -p1 %patch8 -p1
%patch9 -p1
%patch10 -p1
%patch11 -p1
%build %build
%define my_cflags -W -Wall -Wpointer-arith -Wstrict-prototypes -Wformat-security -Wno-unused-parameter -fPIE %define my_cflags -W -Wall -Wpointer-arith -Wstrict-prototypes -Wformat-security -Wno-unused-parameter -fPIE