/* After a file/link/directory creation has failed, see if
it's because some required directory was not present, and if so,
- create all required directories. Return non-zero if a directory
- was created. */
+ 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. */
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 */
- int did_something = 0; /* did we do anything yet? */
- int status;
for (cursor = cursor0; *cursor; cursor++)
{
mode_t mode;
mode_t desired_mode;
+ int status;
if (! ISSLASH (*cursor))
continue;
desired_mode, AT_SYMLINK_NOFOLLOW);
print_for_mkdir (file_name, cursor - file_name, desired_mode);
- did_something = 1;
-
- *cursor = '/';
- continue;
+ *interdir_made = true;
+ }
+ 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);
+ }
}
*cursor = '/';
-
- if (errno == EEXIST)
- continue; /* Directory already exists. */
- else if ((errno == ENOSYS /* Automounted dirs on Solaris return
- this. Reported by Warren Hyde
- <Warren.Hyde@motorola.com> */
- || ERRNO_IS_EACCES) /* Turbo C mkdir gives a funny errno. */
- && faccessat (chdir_fd, file_name, W_OK, AT_EACCESS) == 0)
- continue;
-
- /* Some other error in the mkdir. We return to the caller. */
- break;
+ if (status)
+ return status;
}
- return did_something; /* tell them to retry if we made one */
+ return 0;
}
/* Return true if FILE_NAME (with status *STP, if STP) is not a
case ENOENT:
/* Attempt creating missing intermediate directories. */
- if (make_directories (file_name))
- {
- *interdir_made = true;
- return RECOVER_OK;
- }
+ if (make_directories (file_name, interdir_made) == 0 && *interdir_made)
+ return RECOVER_OK;
break;
default:
if (renameat (chdir_fd, src, chdir_fd, dst) != 0)
{
int e = errno;
+ bool interdir_made;
switch (e)
{
case ENOENT:
- if (make_directories (dst))
+ if (make_directories (dst, &interdir_made) == 0)
{
if (renameat (chdir_fd, src, chdir_fd, dst) == 0)
return true;
--- /dev/null
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+
+# Test suite for GNU tar.
+# Copyright (C) 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# written by Paul Eggert
+
+# Check diagnostic of 'tar -x a/b/c' when b cannot be created.
+
+AT_SETUP([extract parent mkdir failure])
+AT_KEYWORDS([extract extrac15])
+
+AT_TAR_CHECK([
+AT_UNPRIVILEGED_PREREQ
+
+mkdir src src/a src/a/b dest dest/a
+touch src/a/b/c
+chmod a-w dest/a
+
+tar -cf archive.tar -C src a/b/c &&
+if tar -xf archive.tar -C dest a/b/c
+then (exit 1)
+else (exit 0)
+fi
+],
+[0],[],[tar: a/b: Cannot mkdir: Permission denied
+tar: a/b/c: Cannot open: No such file or directory
+tar: Exiting with failure status due to previous errors
+],[],[],[gnu])
+
+AT_CLEANUP