[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
proposed fix for some race conditions in mkdir and install
From: |
Paul Eggert |
Subject: |
proposed fix for some race conditions in mkdir and install |
Date: |
Fri, 15 Sep 2006 15:39:48 -0700 |
User-agent: |
Gnus/5.1008 (Gnus v5.10.8) Emacs/21.4 (gnu/linux) |
Like most utilities, mkdir and install are vulnerable to race
conditions involving file names, e.g., if given a file name that
resolves through a non-sticky directory that is writeable by some
other user. Here's a patch to fix that, just for these two utilities.
It's a bit outre, since it requires forking off a subprocess in some
cases, but it should resist the race condtions better.
I'll attach two patches, one for gnulib and one for coreutils.
Index: ChangeLog
===================================================================
RCS file: /cvsroot/gnulib/gnulib/ChangeLog,v
retrieving revision 1.679
diff -p -u -r1.679 ChangeLog
--- ChangeLog 15 Sep 2006 18:54:47 -0000 1.679
+++ ChangeLog 15 Sep 2006 22:36:56 -0000
@@ -1,5 +1,9 @@
2006-09-15 Paul Eggert <address@hidden>
+ * modules/mkancesdirs (Depends-on): Add fcntl.
+ * modules/savewd: New file.
+ * MODULES.html.sh (File system functions): Add savewd.
+
* modules/configmake (Makefile.am): Add support for the
Automake-supplied PKGLIBDIR, PKGINCLUDEDIR, PKGDATADIR.
Index: MODULES.html.sh
===================================================================
RCS file: /cvsroot/gnulib/gnulib/MODULES.html.sh,v
retrieving revision 1.149
diff -p -u -r1.149 MODULES.html.sh
--- MODULES.html.sh 15 Sep 2006 13:49:12 -0000 1.149
+++ MODULES.html.sh 15 Sep 2006 22:36:56 -0000
@@ -1908,6 +1908,7 @@ func_all_modules ()
func_module same
func_module save-cwd
func_module savedir
+ func_module savewd
func_module stat-time
func_module tmpdir
func_module unlinkdir
Index: lib/ChangeLog
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/ChangeLog,v
retrieving revision 1.1292
diff -p -u -r1.1292 ChangeLog
--- lib/ChangeLog 15 Sep 2006 18:48:09 -0000 1.1292
+++ lib/ChangeLog 15 Sep 2006 22:36:57 -0000
@@ -1,3 +1,29 @@
+2006-09-15 Paul Eggert <address@hidden>
+
+ * dirchownmod.c: Don't include fcntl.h; no longer needed.
+ (dirchownmod): New arg FD. All callers changed.
+ Use FD rather than opening the directory ourself, as opening is
+ now the caller's responsibility.
+ * dirchownmod.h: Likewise.
+ * mkancesdirs.c: Include <sys/types.h>, for portability to older
+ hosts that require <sys/types.h> before <sys/stat.h>. Include
+ fcntl.h, savewd.h, and unistd.h, not dirname.h and stat-macros.h.
+ (test_dir): Remove.
+ (mkancesdirs): Return length of prefix of FILE that has already
+ been made, or -2 if there is a child doing the work. Redo
+ algorithm so that it is O(N) rather than O(N**2). Optimize away
+ ".", and treat ".." specially since it might stray back into
+ already-created areas. Use a subprocess if necessary. New arg
+ WD; all users changed. MAKE_DIR function should now return 1
+ if it creates a directory that is not readable. Return -2 if
+ a child process is spun off.
+ * mkancesdirs.h: Include <stddef.h>, for ptrdiff_t.
+ Adjust signature to match code.
+ * mkdir-p.c: Include dirname.h, for IS_ABSOLUTE_FILE_NAME.
+ (make_dir_parents): Use a subprocess if necessary. New arg WD;
+ all users changed.
+ * savewd.c, savewd.h: New files.
+
2006-09-15 Jim Meyering <address@hidden>
* rename-dest-slash.c (has_trailing_slash): Use
Index: lib/dirchownmod.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/dirchownmod.c,v
retrieving revision 1.4
diff -p -u -r1.4 dirchownmod.c
--- lib/dirchownmod.c 13 Sep 2006 22:38:14 -0000 1.4
+++ lib/dirchownmod.c 15 Sep 2006 22:36:57 -0000
@@ -25,7 +25,6 @@
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <fcntl.h>
#include <unistd.h>
#include "lchmod.h"
@@ -37,7 +36,10 @@
# define fchmod(fd, mode) (-1)
#endif
-/* Change the ownership and mode bits of the directory DIR.
+/* Change the ownership and mode bits of a directory. If FD is
+ nonnegative, it should be a file descriptor associated with the
+ directory; close it before returning. DIR is the name of the
+ directory.
If MKDIR_MODE is not (mode_t) -1, mkdir (DIR, MKDIR_MODE) has just
been executed successfully with umask zero, so DIR should be a
@@ -58,27 +60,12 @@
calls may do the chown but not the chmod. */
int
-dirchownmod (char const *dir, mode_t mkdir_mode,
+dirchownmod (int fd, char const *dir, mode_t mkdir_mode,
uid_t owner, gid_t group,
mode_t mode, mode_t mode_bits)
{
struct stat st;
- int result;
-
- /* Manipulate DIR via a file descriptor if possible, to avoid some races. */
- int open_flags = O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NOFOLLOW | O_NONBLOCK;
- int fd = open (dir, open_flags);
-
- /* Fail if the directory is unreadable, the directory previously
- existed or was created without read permission. Otherwise, get
- the file's status. */
- if (0 <= fd)
- result = fstat (fd, &st);
- else if (errno != EACCES
- || (mkdir_mode != (mode_t) -1 && mkdir_mode & S_IRUSR))
- return fd;
- else
- result = stat (dir, &st);
+ int result = (fd < 0 ? stat (dir, &st) : fstat (fd, &st));
if (result == 0)
{
Index: lib/dirchownmod.h
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/dirchownmod.h,v
retrieving revision 1.1
diff -p -u -r1.1 dirchownmod.h
--- lib/dirchownmod.h 17 Jul 2006 06:06:48 -0000 1.1
+++ lib/dirchownmod.h 15 Sep 2006 22:36:57 -0000
@@ -1,2 +1,2 @@
#include <sys/types.h>
-int dirchownmod (char const *, mode_t, uid_t, gid_t, mode_t, mode_t);
+int dirchownmod (int, char const *, mode_t, uid_t, gid_t, mode_t, mode_t);
Index: lib/mkancesdirs.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/mkancesdirs.c,v
retrieving revision 1.2
diff -p -u -r1.2 mkancesdirs.c
--- lib/mkancesdirs.c 13 Sep 2006 22:38:14 -0000 1.2
+++ lib/mkancesdirs.c 15 Sep 2006 22:36:57 -0000
@@ -22,94 +22,71 @@
#include "mkancesdirs.h"
-#include <errno.h>
+#include <sys/types.h>
#include <sys/stat.h>
+#include <fcntl.h>
-#include "dirname.h"
-#include "stat-macros.h"
-
-/* Return 0 if FILE is a directory, otherwise -1 (setting errno). */
+#include <errno.h>
+#include <unistd.h>
-static int
-test_dir (char const *file)
-{
- struct stat st;
- if (stat (file, &st) == 0)
- {
- if (S_ISDIR (st.st_mode))
- return 0;
- errno = ENOTDIR;
- }
- return -1;
-}
+#include "dirname.h"
+#include "savewd.h"
/* Ensure that the ancestor directories of FILE exist, using an
algorithm that should work even if two processes execute this
- function in parallel. Temporarily modify FILE by storing '\0'
- bytes into it, to access the ancestor directories.
+ function in parallel. Modify FILE as necessary to access the
+ ancestor directories, but restore FILE to an equivalent value
+ if successful.
- Create any ancestor directories that don't already exist, by
- invoking MAKE_DIR (ANCESTOR, MAKE_DIR_ARG). This function should
- return zero if successful, -1 (setting errno) otherwise.
+ WD points to the working directory, using the conventions of
+ savewd.
- If successful, return 0 with FILE set back to its original value;
- otherwise, return -1 (setting errno), storing a '\0' into *FILE so
- that it names the ancestor directory that had problems. */
+ Create any ancestor directories that don't already exist, by
+ invoking MAKE_DIR (COMPONENT, MAKE_DIR_ARG). This function should
+ return 0 if successful and the resulting directory is readable, 1
+ if successful but the resulting directory might not be readable, -1
+ (setting errno) otherwise. If COMPONENT is relative, it is
+ relative to the temporary working directory, which may differ from
+ *WD.
+
+ Ordinarily MAKE_DIR is executed with the working directory changed
+ to reflect the already-made prefix, and mkancesdirs returns with
+ the working directory changed a prefix of FILE. However, if the
+ initial working directory cannot be saved in a file descriptor,
+ MAKE_DIR is invoked in a subprocess and this function returns in
+ both the parent and child process, so the caller should not assume
+ any changed state survives other than the EXITMAX component of WD,
+ and the caller should take care that the parent does not attempt to
+ do the work that the child is doing.
+
+ If successful and if this process can go ahead and create FILE,
+ return the length of the prefix of FILE that has already been made.
+ If successful so far but a child process is doing the actual work,
+ return -2. If unsuccessful, return -1 and set errno. */
-int
-mkancesdirs (char *file,
+ptrdiff_t
+mkancesdirs (char *file, struct savewd *wd,
int (*make_dir) (char const *, void *),
void *make_dir_arg)
{
- /* This algorithm is O(N**2) but in typical practice the fancier
- O(N) algorithms are slower. */
-
/* Address of the previous directory separator that follows an
ordinary byte in a file name in the left-to-right scan, or NULL
if no such separator precedes the current location P. */
char *sep = NULL;
- char const *prefix_end = file + FILE_SYSTEM_PREFIX_LEN (file);
- char *p;
- char c;
-
- /* Search backward through FILE using mkdir to create the
- furthest-away ancestor that is needed. This loop isn't needed
- for correctness, but typically ancestors already exist so this
- loop speeds things up a bit.
-
- This loop runs a bit faster if errno initially contains an error
- number corresponding to a failed access to FILE. However, things
- work correctly regardless of errno's initial value. */
+ /* Address of the leftmost file name component that has not yet
+ been processed. */
+ char *component = file;
- for (p = last_component (file); prefix_end < p; p--)
- if (ISSLASH (*p) && ! ISSLASH (p[-1]))
- {
- *p = '\0';
-
- if (errno == ENOENT && make_dir (file, make_dir_arg) == 0)
- {
- *p = '/';
- break;
- }
-
- if (errno != ENOENT)
- {
- if (test_dir (file) == 0)
- {
- *p = '/';
- break;
- }
- if (errno != ENOENT)
- return -1;
- }
-
- *p = '/';
- }
+ char *p = file + FILE_SYSTEM_PREFIX_LEN (file);
+ char c;
+ bool made_dir = false;
- /* Scan forward through FILE, creating directories along the way.
- Try mkdir before stat, so that the procedure works even when two
- or more processes are executing it in parallel. */
+ /* Scan forward through FILE, creating and chdiring into directories
+ along the way. Try MAKE_DIR before chdir, so that the procedure
+ works even when two or more processes are executing it in
+ parallel. Isolate each file name component by having COMPONENT
+ point to its start and SEP point just after its end. */
while ((c = *p++))
if (ISSLASH (*p))
@@ -119,12 +96,59 @@ mkancesdirs (char *file,
}
else if (ISSLASH (c) && *p && sep)
{
- *sep = '\0';
- if (make_dir (file, make_dir_arg) != 0 && test_dir (file) != 0)
- return -1;
- *sep = '/';
- }
+ /* Don't bother to make or test for "." since it does not
+ affect the algorithm. */
+ if (! (sep - component == 1 && component[0] == '.'))
+ {
+ int make_dir_errno = 0;
+ int savewd_chdir_options = 0;
+ int chdir_result;
+
+ /* Temporarily modify FILE to isolate this file name
+ component. */
+ *sep = '\0';
+
+ /* Invoke MAKE_DIR on this component, except don't bother
+ with ".." since it must exist if its "parent" does. */
+ if (sep - component == 2
+ && component[0] == '.' && component[1] == '.')
+ made_dir = false;
+ else
+ switch (make_dir (component, make_dir_arg))
+ {
+ case -1:
+ make_dir_errno = errno;
+ break;
+
+ case 0:
+ savewd_chdir_options |= SAVEWD_CHDIR_READABLE;
+ /* Fall through. */
+ case 1:
+ made_dir = true;
+ break;
+ }
+
+ if (made_dir)
+ savewd_chdir_options |= SAVEWD_CHDIR_NOFOLLOW;
+
+ chdir_result =
+ savewd_chdir (wd, component, savewd_chdir_options, NULL);
+
+ /* Undo the temporary modification to FILE, unless there
+ was a failure. */
+ if (chdir_result != -1)
+ *sep = '/';
+ if (chdir_result != 0)
+ {
+ if (make_dir_errno != 0 && errno == ENOENT)
+ errno = make_dir_errno;
+ return chdir_result;
+ }
+ }
+
+ component = p;
+ }
- return 0;
+ return component - file;
}
Index: lib/mkancesdirs.h
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/mkancesdirs.h,v
retrieving revision 1.1
diff -p -u -r1.1 mkancesdirs.h
--- lib/mkancesdirs.h 17 Jul 2006 06:06:48 -0000 1.1
+++ lib/mkancesdirs.h 15 Sep 2006 22:36:57 -0000
@@ -1 +1,4 @@
-int mkancesdirs (char *, int (*) (char const *, void *), void *);
+#include <stddef.h>
+struct savewd;
+ptrdiff_t mkancesdirs (char *, struct savewd *,
+ int (*) (char const *, void *), void *);
Index: lib/mkdir-p.c
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/mkdir-p.c,v
retrieving revision 1.7
diff -p -u -r1.7 mkdir-p.c
--- lib/mkdir-p.c 13 Sep 2006 22:38:14 -0000 1.7
+++ lib/mkdir-p.c 15 Sep 2006 22:36:57 -0000
@@ -30,13 +30,17 @@
#define _(msgid) gettext (msgid)
#include "dirchownmod.c"
+#include "dirname.h"
#include "error.h"
#include "quote.h"
#include "mkancesdirs.h"
+#include "savewd.h"
#include "stat-macros.h"
/* Ensure that the directory DIR exists.
+ WD is the working directory, as in savewd.c.
+
If MAKE_ANCESTOR is not null, create any ancestor directories that
don't already exist, by invoking MAKE_ANCESTOR (ANCESTOR, OPTIONS).
This function should return zero if successful, -1 (setting errno)
@@ -46,7 +50,7 @@
created.
Create DIR as a new directory with using mkdir with permissions
- MODE. It is also OK if MAKE_ANCESTOR_DIR is not null and a
+ MODE. It is also OK if MAKE_ANCESTOR is not null and a
directory DIR already exists.
Call ANNOUNCE (DIR, OPTIONS) just after successfully making DIR,
@@ -69,12 +73,15 @@
This implementation assumes the current umask is zero.
Return true if DIR exists as a directory with the proper ownership
- and file mode bits when done. Report a diagnostic and return false
- on failure, storing '\0' into *DIR if an ancestor directory had
- problems. */
+ and file mode bits when done, or if a child process has been
+ dispatched to do the real work (though the child process may not
+ have finished yet -- it is the caller's responsibility to handle
+ this). Report a diagnostic and return false on failure, storing
+ '\0' into *DIR if an ancestor directory had problems. */
bool
make_dir_parents (char *dir,
+ struct savewd *wd,
int (*make_ancestor) (char const *, void *),
void *options,
mode_t mode,
@@ -84,51 +91,101 @@ make_dir_parents (char *dir,
gid_t group,
bool preserve_existing)
{
- bool made_dir = (mkdir (dir, mode) == 0);
+ int mkdir_errno = (IS_ABSOLUTE_FILE_NAME (dir) ? 0 : savewd_errno (wd));
- if (!made_dir && make_ancestor && errno == ENOENT)
+ if (mkdir_errno == 0)
{
- if (mkancesdirs (dir, make_ancestor, options) == 0)
- made_dir = (mkdir (dir, mode) == 0);
- else
+ ptrdiff_t prefix_len = 0;
+ int savewd_chdir_options = (HAVE_FCHMOD ? SAVEWD_CHDIR_SKIP_READABLE :
0);
+
+ if (make_ancestor)
{
- /* mkancestdirs updated DIR for a better-looking
- diagnostic, so don't try to stat DIR below. */
- make_ancestor = NULL;
+ prefix_len = mkancesdirs (dir, wd, make_ancestor, options);
+ if (prefix_len < 0)
+ {
+ if (prefix_len < -1)
+ return true;
+ mkdir_errno = errno;
+ }
}
- }
- if (made_dir)
- {
- announce (dir, options);
- preserve_existing =
- (owner == (uid_t) -1 && group == (gid_t) -1
- && ! ((mode_bits & (S_ISUID | S_ISGID)) | (mode & S_ISVTX)));
- }
- else
- {
- int mkdir_errno = errno;
- struct stat st;
- if (! (make_ancestor && mkdir_errno != ENOENT
- && stat (dir, &st) == 0 && S_ISDIR (st.st_mode)))
+ if (0 <= prefix_len)
{
- error (0, mkdir_errno, _("cannot create directory %s"), quote (dir));
- return false;
+ if (mkdir (dir + prefix_len, mode) == 0)
+ {
+ announce (dir, options);
+ preserve_existing =
+ (owner == (uid_t) -1 && group == (gid_t) -1
+ && ! ((mode_bits & (S_ISUID | S_ISGID)) | (mode & S_ISVTX)));
+ savewd_chdir_options |=
+ (SAVEWD_CHDIR_NOFOLLOW
+ | (mode & S_IRUSR ? SAVEWD_CHDIR_READABLE : 0));
+ }
+ else
+ mkdir_errno = errno;
+
+ if (preserve_existing)
+ {
+ struct stat st;
+ if (mkdir_errno == 0
+ || (mkdir_errno != ENOENT && make_ancestor
+ && stat (dir + prefix_len, &st) == 0
+ && S_ISDIR (st.st_mode)))
+ return true;
+ }
+ else
+ {
+ int open_result[2];
+ int chdir_result =
+ savewd_chdir (wd, dir + prefix_len,
+ savewd_chdir_options, open_result);
+ if (chdir_result < -1)
+ return true;
+ else
+ {
+ bool chdir_ok = (chdir_result == 0);
+ int chdir_errno = errno;
+ int fd = open_result[0];
+ bool chdir_failed_unexpectedly =
+ (mkdir_errno == 0
+ && ((! chdir_ok && (mode & S_IXUSR))
+ || (fd < 0 && (mode & S_IRUSR))));
+
+ if (chdir_failed_unexpectedly)
+ {
+ /* No need to save errno here; it's irrelevant. */
+ if (0 <= fd)
+ close (fd);
+ }
+ else
+ {
+ mode_t mkdir_mode = (mkdir_errno == 0 ? mode : -1);
+ char const *subdir = (chdir_ok ? "." : dir + prefix_len);
+ if (dirchownmod (fd, subdir, mkdir_mode, owner, group,
+ mode, mode_bits)
+ == 0)
+ return true;
+ }
+
+ if (mkdir_errno == 0
+ || (mkdir_errno != ENOENT && make_ancestor
+ && errno != ENOTDIR))
+ {
+ error (0,
+ (! chdir_failed_unexpectedly ? errno
+ : ! chdir_ok && (mode & S_IXUSR) ? chdir_errno
+ : open_result[1]),
+ _(owner == (uid_t) -1 && group == (gid_t) -1
+ ? "cannot change permissions of %s"
+ : "cannot change owner and permissions of %s"),
+ quote (dir));
+ return false;
+ }
+ }
+ }
}
}
- if (! preserve_existing
- && (dirchownmod (dir, (made_dir ? mode : (mode_t) -1),
- owner, group, mode, mode_bits)
- != 0))
- {
- error (0, errno,
- _(owner == (uid_t) -1 && group == (gid_t) -1
- ? "cannot change permissions of %s"
- : "cannot change owner and permissions of %s"),
- quote (dir));
- return false;
- }
-
- return true;
+ error (0, mkdir_errno, _("cannot create directory %s"), quote (dir));
+ return false;
}
Index: lib/mkdir-p.h
===================================================================
RCS file: /cvsroot/gnulib/gnulib/lib/mkdir-p.h,v
retrieving revision 1.3
diff -p -u -r1.3 mkdir-p.h
--- lib/mkdir-p.h 17 Jul 2006 06:06:48 -0000 1.3
+++ lib/mkdir-p.h 15 Sep 2006 22:36:57 -0000
@@ -1,7 +1,7 @@
/* mkdir-p.h -- Ensure that a directory and its parents exist.
- Copyright (C) 1994, 1995, 1996, 1997, 2000, 2003, 2004, 2005 Free
- Software Foundation, Inc.
+ Copyright (C) 1994, 1995, 1996, 1997, 2000, 2003, 2004, 2005, 2006
+ 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
@@ -22,7 +22,9 @@
#include <stdbool.h>
#include <sys/types.h>
+struct savewd;
bool make_dir_parents (char *dir,
+ struct savewd *wd,
int (*make_ancestor) (char const *, void *),
void *options,
mode_t mode,
Index: m4/ChangeLog
===================================================================
RCS file: /cvsroot/gnulib/gnulib/m4/ChangeLog,v
retrieving revision 1.934
diff -p -u -r1.934 ChangeLog
--- m4/ChangeLog 15 Sep 2006 13:49:12 -0000 1.934
+++ m4/ChangeLog 15 Sep 2006 22:36:57 -0000
@@ -1,3 +1,7 @@
+2006-09-15 Paul Eggert <address@hidden>
+
+ * savewd.m4: New file.
+
2006-09-15 Jim Meyering <address@hidden>
* rename-dest-slash.m4 (gl_FUNC_RENAME_TRAILING_DEST_SLASH): New file.
Index: modules/mkancesdirs
===================================================================
RCS file: /cvsroot/gnulib/gnulib/modules/mkancesdirs,v
retrieving revision 1.2
diff -p -u -r1.2 mkancesdirs
--- modules/mkancesdirs 21 Aug 2006 21:46:31 -0000 1.2
+++ modules/mkancesdirs 15 Sep 2006 22:36:57 -0000
@@ -8,6 +8,7 @@ m4/mkancesdirs.m4
Depends-on:
dirname
+fcntl
stat-macros
configure.ac:
--- /dev/null 2005-09-24 22:00:15.000000000 -0700
+++ lib/savewd.c 2006-09-14 15:59:47.000000000 -0700
@@ -0,0 +1,305 @@
+/* Save and restore the working directory, possibly using a child process.
+
+ Copyright (C) 2006 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 2, 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, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Paul Eggert. */
+
+#include <config.h>
+
+#include "savewd.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "exit.h"
+#include "dirname.h"
+#include "fcntl-safer.h"
+
+
+/* Save the working directory into *WD, if it hasn't been saved
+ already. Return true if a child has been forked to do the real
+ work. */
+static bool
+savewd_save (struct savewd *wd)
+{
+ switch (wd->state)
+ {
+ case INITIAL_STATE:
+ /* Save the working directory, or prepare to fall back if possible. */
+ {
+ int fd = open_safer (".", O_RDONLY);
+ if (0 <= fd)
+ {
+ wd->state = FD_STATE;
+ wd->val.fd = fd;
+ break;
+ }
+ if (errno != EACCES)
+ {
+ wd->state = ERROR_STATE;
+ wd->val.errnum = errno;
+ break;
+ }
+ }
+ wd->state = FORKING_STATE;
+ wd->val.child = -1;
+ /* Fall through. */
+ case FORKING_STATE:
+ if (wd->val.child < 0)
+ {
+ /* "Save" the initial working directory by forking a new
+ subprocess that will attempt all the work from the chdir
+ until until the next savewd_restore. */
+ wd->val.child = fork ();
+ if (wd->val.child != 0)
+ {
+ if (0 < wd->val.child)
+ return true;
+ wd->state = ERROR_STATE;
+ wd->val.errnum = errno;
+ }
+ }
+ break;
+
+ case FD_STATE:
+ case FD_POST_CHDIR_STATE:
+ case ERROR_STATE:
+ case FINAL_STATE:
+ break;
+
+ default:
+ assert (false);
+ }
+
+ return false;
+}
+
+int
+savewd_chdir (struct savewd *wd, char const *dir, int options,
+ int open_result[2])
+{
+ int fd = -1;
+ int result = 0;
+
+ /* Open the directory if requested, or if avoiding a race condition
+ is requested and possible. */
+ if (open_result || (options & (O_NOFOLLOW ? SAVEWD_CHDIR_NOFOLLOW : 0)))
+ {
+ fd = open (dir,
+ (O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
+ | (options & SAVEWD_CHDIR_NOFOLLOW ? O_NOFOLLOW : 0)));
+
+ if (open_result)
+ {
+ open_result[0] = fd;
+ open_result[1] = errno;
+ }
+
+ if (fd < 0 && (errno != EACCES || (options & SAVEWD_CHDIR_READABLE)))
+ result = -1;
+ }
+
+ if (result == 0 && ! (0 <= fd && options & SAVEWD_CHDIR_SKIP_READABLE))
+ {
+ if (savewd_save (wd))
+ {
+ open_result = NULL;
+ result = -2;
+ }
+ else
+ {
+ result = (fd < 0 ? chdir (dir) : fchdir (fd));
+
+ if (result == 0)
+ switch (wd->state)
+ {
+ case FD_STATE:
+ wd->state = FD_POST_CHDIR_STATE;
+ break;
+
+ case ERROR_STATE:
+ case FD_POST_CHDIR_STATE:
+ case FINAL_STATE:
+ break;
+
+ case FORKING_STATE:
+ assert (wd->val.child == 0);
+ break;
+
+ default:
+ assert (false);
+ }
+ }
+ }
+
+ if (0 <= fd && ! open_result)
+ {
+ int e = errno;
+ close (fd);
+ errno = e;
+ }
+
+ return result;
+}
+
+int
+savewd_restore (struct savewd *wd, int status)
+{
+ switch (wd->state)
+ {
+ case INITIAL_STATE:
+ case FD_STATE:
+ /* The working directory is the desired directory, so there's no
+ work to do. */
+ break;
+
+ case FD_POST_CHDIR_STATE:
+ /* Restore the working directory using fchdir. */
+ if (fchdir (wd->val.fd) == 0)
+ {
+ wd->state = FD_STATE;
+ break;
+ }
+ else
+ {
+ int chdir_errno = errno;
+ close (wd->val.fd);
+ wd->state = ERROR_STATE;
+ wd->val.errnum = chdir_errno;
+ }
+ /* Fall through. */
+ case ERROR_STATE:
+ /* Report an error if asked to restore the working directory. */
+ errno = wd->val.errnum;
+ return -1;
+
+ case FORKING_STATE:
+ /* "Restore" the working directory by waiting for the subprocess
+ to finish. */
+ {
+ pid_t child = wd->val.child;
+ if (child == 0)
+ _exit (status);
+ if (0 < child)
+ {
+ int status;
+ while (waitpid (child, &status, 0) < 0)
+ assert (errno == EINTR);
+ wd->val.child = -1;
+ if (! WIFEXITED (status))
+ raise (WTERMSIG (status));
+ return WEXITSTATUS (status);
+ }
+ }
+ break;
+
+ default:
+ assert (false);
+ }
+
+ return 0;
+}
+
+void
+savewd_finish (struct savewd *wd)
+{
+ switch (wd->state)
+ {
+ case INITIAL_STATE:
+ case ERROR_STATE:
+ break;
+
+ case FD_STATE:
+ case FD_POST_CHDIR_STATE:
+ close (wd->val.fd);
+ break;
+
+ case FORKING_STATE:
+ assert (wd->val.child < 0);
+ break;
+
+ default:
+ assert (false);
+ }
+
+ wd->state = FINAL_STATE;
+}
+
+/* Return true if the actual work is currently being done by a
+ subprocess.
+
+ A true return means that the caller and the subprocess should
+ resynchronize later with savewd_restore, using only their own
+ memory to decide when to resynchronize; they should not consult the
+ file system to decide, because that might lead to race conditions.
+ This is why savewd_chdir is broken out into another function;
+ savewd_chdir's callers _can_ inspect the file system to decide
+ whether to call savewd_chdir. */
+static inline bool
+savewd_delegating (struct savewd const *wd)
+{
+ return wd->state == FORKING_STATE && 0 < wd->val.child;
+}
+
+int
+savewd_process_files (int n_files, char **file,
+ int (*act) (char *, struct savewd *, void *),
+ void *options)
+{
+ int i = 0;
+ int last_relative;
+ int exit_status = EXIT_SUCCESS;
+ struct savewd wd;
+ savewd_init (&wd);
+
+ for (last_relative = n_files - 1; 0 <= last_relative; last_relative--)
+ if (! IS_ABSOLUTE_FILE_NAME (file[last_relative]))
+ break;
+
+ for (; i < last_relative; i++)
+ {
+ if (! savewd_delegating (&wd))
+ {
+ int s = act (file[i], &wd, options);
+ if (exit_status < s)
+ exit_status = s;
+ }
+
+ if (! IS_ABSOLUTE_FILE_NAME (file[i + 1]))
+ {
+ int r = savewd_restore (&wd, exit_status);
+ if (exit_status < r)
+ exit_status = r;
+ }
+ }
+
+ savewd_finish (&wd);
+
+ for (; i < n_files; i++)
+ {
+ int s = act (file[i], &wd, options);
+ if (exit_status < s)
+ exit_status = s;
+ }
+
+ return exit_status;
+}
--- /dev/null 2005-09-24 22:00:15.000000000 -0700
+++ lib/savewd.h 2006-09-14 15:54:15.000000000 -0700
@@ -0,0 +1,149 @@
+/* Save and restore the working directory, possibly using a subprocess.
+
+ Copyright (C) 2006 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 2, 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, write to the Free Software Foundation,
+ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* Written by Paul Eggert. */
+
+#ifndef SAVEWD_H
+# define SAVEWD_H 1
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+/* A saved working directory. The member names and constants defined
+ by this structure are private to the savewd module. */
+struct savewd
+{
+ /* The state of this object. */
+ enum
+ {
+ /* This object has been created but does not yet represent
+ the working directory. */
+ INITIAL_STATE,
+
+ /* val.fd is the original working directory's file descriptor.
+ It is still the working directory. */
+ FD_STATE,
+
+ /* Like FD_STATE, but the working directory has changed, so
+ restoring it will require a fchdir. */
+ FD_POST_CHDIR_STATE,
+
+ /* Fork and let the subprocess do the work. val.child is 0 in a
+ child, negative in a childless parent, and the child process
+ ID in a parent with a child. */
+ FORKING_STATE,
+
+ /* A serious problem argues against further efforts. val.errnum
+ contains the error number (e.g., EIO). */
+ ERROR_STATE,
+
+ /* savewd_finish has been called, so the application no longer
+ cares whether the working directory is saved, and there is no
+ more work to do. */
+ FINAL_STATE
+ } state;
+
+ /* The object's value. */
+ union
+ {
+ int fd;
+ int errnum;
+ pid_t child;
+ } val;
+};
+
+/* Allocate and return a saved working directory object. */
+static inline void
+savewd_init (struct savewd *wd)
+{
+ wd->state = INITIAL_STATE;
+}
+
+
+/* Options for savewd_chdir. */
+enum
+ {
+ /* Do not follow symbolic links, if supported. */
+ SAVEWD_CHDIR_NOFOLLOW = 1,
+
+ /* The directory should be readable, so fail if it happens to be
+ discovered that the directory is not readable. (Unreadable
+ directories are not necessarily diagnosed, though.) */
+ SAVEWD_CHDIR_READABLE = 2,
+
+ /* Do not chdir if the directory is readable; simply succeed
+ without invoking chdir if the directory was opened. */
+ SAVEWD_CHDIR_SKIP_READABLE = 4
+ };
+
+/* Change the directory, and if successful, record into *WD the fact
+ that the process chdired into DIR. A process using this module
+ should use savewd_chdir rather than chdir or fchdir. Obey the
+ options specified in OPTIONS.
+
+ If OPEN_RESULT is not null, store into OPEN_RESULT[0] a file
+ descriptor that accesses DIR if a file descriptor is successfully
+ obtained. Store -1 otherwise, setting OPEN_RESULT[1] to the error
+ number. Store through OPEN_RESULT regardless of whether the chdir
+ is successful. However, when -2 is returned, the contents of
+ OPEN_RESULT are indeterminate since the file descriptor is closed
+ in the parent.
+
+ Return -2 if a subprocess was spun off to do the real work, -1
+ (setting errno) if unsuccessful, 0 if successful. */
+int savewd_chdir (struct savewd *wd, char const *dir, int options,
+ int open_result[2]);
+
+/* Restore the working directory from *WD. STATUS indicates the exit
+ status corresponding to the work done since the last save; this is
+ used when the caller is in a subprocess. Return 0 if successful,
+ -1 (setting errno) on our failure, a positive subprocess exit
+ status if the working directory was restored in the parent but the
+ subprocess failed. */
+int savewd_restore (struct savewd *wd, int status);
+
+/* Return WD's error number, or 0 if WD is not in an error state. */
+static inline int
+savewd_errno (struct savewd const *wd)
+{
+ return (wd->state == ERROR_STATE ? wd->val.errnum : 0);
+}
+
+/* Deallocate any resources associated with WD. A program that chdirs
+ should restore before finishing. */
+void savewd_finish (struct savewd *wd);
+
+/* Process N_FILES file names, FILE[0] through FILE[N_FILES - 1], in
+ the context of the working directory WD. For each file name F,
+ call ACT (F, WD, OPTIONS); ACT should invoke savewd_chdir as
+ needed, and should return an exit status. WD may be in an
+ error state when ACT is called.
+
+ Save and restore the working directory as needed by the file name
+ vector; assume that ACT does not require access to any relative
+ file names other than its first argument, and that it is OK if the
+ working directory is changed when this function returns. Some
+ actions may be applied in a subprocess.
+
+ Return the maximum exit status that any call to ACT returned, or
+ EXIT_SUCCESS (i.e., 0) if no calls were made. */
+int savewd_process_files (int n_files, char **file,
+ int (*act) (char *, struct savewd *, void *),
+ void *options);
+
+#endif
--- /dev/null 2005-09-24 22:00:15.000000000 -0700
+++ m4/savewd.m4 2006-09-10 11:14:07.000000000 -0700
@@ -0,0 +1,9 @@
+# Save and restore the working directory, possibly using a child process.
+
+dnl Copyright (C) 2004 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_SAVEWD],
+ [AC_REQUIRE([AC_C_INLINE])])
--- /dev/null 2005-09-24 22:00:15.000000000 -0700
+++ modules/savewd 2006-09-10 11:14:36.000000000 -0700
@@ -0,0 +1,29 @@
+Description:
+Save and restore the working directory, possibly using a child process.
+
+Files:
+lib/savewd.h
+lib/savewd.c
+m4/savewd.m4
+
+Depends-on:
+dirname
+exit
+fcntl-safer
+stdbool
+xalloc
+
+configure.ac:
+gl_SAVEWD
+
+Makefile.am:
+lib_SOURCES += savewd.h savewd.c
+
+Include:
+"savewd.h"
+
+License:
+GPL
+
+Maintainer:
+Paul Eggert, Jim Meyering
2006-09-15 Paul Eggert <address@hidden>
* NEWS: Document that mkdir -p and install -d now fork on occasion.
* bootstrap.conf (gnulib_modules): Add savewd.
* src/install.c: Include savewd.h.
(process_dir): New function.
(main, install_file_in_file_parents): Use it, along with the new
savewd module, to avoid some race conditions.
* src/mkdir.c: Include savewd.h.
(struct mkdir_options): New members make_ancestor_function, mode,
mode_bits.
(make_ancestor): Return 1 if the resulting directory is not readable.
(process_dir): New function.
(main): Use it, along with new savewd module, to avoid some
race conditions. Fill in new slots of struct mkdir_options, so
that callees get the values.
* tests/install/basic-1: Test for coreutils 5.97 bug that was
fixed in coreutils 6.0, and which should still be fixed with
this change.
* tests/mkdir/p-3: Likewise.
Index: NEWS
===================================================================
RCS file: /fetish/cu/NEWS,v
retrieving revision 1.418
diff -p -u -r1.418 NEWS
--- NEWS 8 Sep 2006 17:19:51 -0000 1.418
+++ NEWS 15 Sep 2006 22:15:09 -0000
@@ -4,6 +4,11 @@ GNU coreutils NEWS
** Changes in behavior
+ mkdir -p and install -d (or -D) now use a method that forks a child
+ process if the working directory is unreadable and a later argument
+ uses a relative file name. This avoids some race conditions, but it
+ means you may need to kill two processes to stop these programs.
+
rm now rejects attempts to remove the root directory, e.g., `rm -fr /'
now fails without removing anything. Likewise for any file name with
a final `./' or `../' component.
Index: bootstrap.conf
===================================================================
RCS file: /fetish/cu/bootstrap.conf,v
retrieving revision 1.9
diff -p -u -r1.9 bootstrap.conf
--- bootstrap.conf 15 Sep 2006 14:11:39 -0000 1.9
+++ bootstrap.conf 15 Sep 2006 22:15:09 -0000
@@ -55,7 +55,7 @@ gnulib_modules="
quote quotearg raise readlink readtokens readtokens0 readutmp
realloc regex rename-dest-slash rmdir rmdir-errno rpmatch
safe-read same
- save-cwd savedir settime sha1 sig2str ssize_t stat-macros
+ save-cwd savedir savewd settime sha1 sig2str ssize_t stat-macros
stat-time stdbool stdlib-safer stpcpy strcase strftime
strpbrk strtoimax strtoumax strverscmp timespec tzset
unicodeio unistd-safer unlink-busy unlinkdir unlocked-io
Index: src/install.c
===================================================================
RCS file: /fetish/cu/src/install.c,v
retrieving revision 1.194
diff -p -u -r1.194 install.c
--- src/install.c 3 Sep 2006 02:53:16 -0000 1.194
+++ src/install.c 15 Sep 2006 22:15:09 -0000
@@ -35,6 +35,7 @@
#include "mkdir-p.h"
#include "modechange.h"
#include "quote.h"
+#include "savewd.h"
#include "stat-time.h"
#include "utimens.h"
#include "xstrtol.h"
@@ -193,11 +194,23 @@ target_directory_operand (char const *fi
return is_a_dir;
}
+/* Process a command-line file name, for the -d option. */
+static int
+process_dir (char *dir, struct savewd *wd, void *options)
+{
+ return (make_dir_parents (dir, wd,
+ make_ancestor, options,
+ dir_mode, announce_mkdir,
+ dir_mode_bits, owner_id, group_id, false)
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE);
+}
+
int
main (int argc, char **argv)
{
int optc;
- bool ok = true;
+ int exit_status = EXIT_SUCCESS;
const char *specified_mode = NULL;
bool make_backups = false;
char *backup_suffix_string;
@@ -361,13 +374,7 @@ main (int argc, char **argv)
get_ids ();
if (dir_arg)
- {
- int i;
- for (i = 0; i < n_files; i++)
- ok &= make_dir_parents (file[i], make_ancestor, &x,
- dir_mode, announce_mkdir,
- dir_mode_bits, owner_id, group_id, false);
- }
+ exit_status = savewd_process_files (n_files, file, process_dir, &x);
else
{
/* FIXME: it's a little gross that this initialization is
@@ -376,23 +383,22 @@ main (int argc, char **argv)
if (!target_directory)
{
- if (mkdir_and_install)
- ok = install_file_in_file_parents (file[0], file[1], &x);
- else
- ok = install_file_in_file (file[0], file[1], &x);
+ if (! (mkdir_and_install
+ ? install_file_in_file_parents (file[0], file[1], &x)
+ : install_file_in_file (file[0], file[1], &x)))
+ exit_status = EXIT_FAILURE;
}
else
{
int i;
dest_info_init (&x);
for (i = 0; i < n_files; i++)
- {
- ok &= install_file_in_dir (file[i], target_directory, &x);
- }
+ if (! install_file_in_dir (file[i], target_directory, &x))
+ exit_status = EXIT_FAILURE;
}
}
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ exit (exit_status);
}
/* Copy file FROM onto file TO, creating any missing parent directories of TO.
@@ -402,13 +408,36 @@ static bool
install_file_in_file_parents (char const *from, char *to,
struct cp_options *x)
{
- if (mkancesdirs (to, make_ancestor, x) != 0)
+ bool save_working_directory =
+ ! (IS_ABSOLUTE_FILE_NAME (from) && IS_ABSOLUTE_FILE_NAME (to));
+ int status = EXIT_SUCCESS;
+
+ struct savewd wd;
+ savewd_init (&wd);
+ if (! save_working_directory)
+ savewd_finish (&wd);
+
+ if (mkancesdirs (to, &wd, make_ancestor, x) == -1)
{
error (0, errno, _("cannot create directory %s"), to);
- return false;
+ status = EXIT_FAILURE;
+ }
+
+ if (save_working_directory)
+ {
+ int restore_result = savewd_restore (&wd, status);
+ int restore_errno = errno;
+ savewd_finish (&wd);
+ if (EXIT_SUCCESS < restore_result)
+ return false;
+ if (restore_result < 0 && status == EXIT_SUCCESS)
+ {
+ error (0, restore_errno, _("cannot create directory %s"), to);
+ return false;
+ }
}
- return install_file_in_file (from, to, x);
+ return (status == EXIT_SUCCESS && install_file_in_file (from, to, x));
}
/* Copy file FROM onto file TO and give TO the appropriate
Index: src/mkdir.c
===================================================================
RCS file: /fetish/cu/src/mkdir.c,v
retrieving revision 1.105
diff -p -u -r1.105 mkdir.c
--- src/mkdir.c 3 Sep 2006 02:53:16 -0000 1.105
+++ src/mkdir.c 15 Sep 2006 22:15:09 -0000
@@ -28,6 +28,7 @@
#include "mkdir-p.h"
#include "modechange.h"
#include "quote.h"
+#include "savewd.h"
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "mkdir"
@@ -75,12 +76,22 @@ Mandatory arguments to long options are
exit (status);
}
-/* Options for announce_mkdir and make_ancestor. */
+/* Options passed to subsidiary functions. */
struct mkdir_options
{
+ /* Function to make an ancestor, or NULL if ancestors should not be
+ made. */
+ int (*make_ancestor_function) (char const *, void *);
+
/* Mode for ancestor directory. */
mode_t ancestor_mode;
+ /* Mode for directory itself. */
+ mode_t mode;
+
+ /* File mode bits affected by MODE. */
+ mode_t mode_bits;
+
/* If not null, format to use when reporting newly made directories. */
char const *created_directory_format;
};
@@ -94,27 +105,44 @@ announce_mkdir (char const *dir, void *o
error (0, 0, o->created_directory_format, quote (dir));
}
-/* Make ancestor directory DIR, with options OPTIONS. */
+/* Make ancestor directory DIR, with options OPTIONS. Return 0 if
+ successful and the resulting directory is readable, 1 if successful
+ but the resulting directory is not readable, -1 (setting errno)
+ otherwise. */
static int
make_ancestor (char const *dir, void *options)
{
struct mkdir_options const *o = options;
int r = mkdir (dir, o->ancestor_mode);
if (r == 0)
- announce_mkdir (dir, options);
+ {
+ r = ! (o->ancestor_mode & S_IRUSR);
+ announce_mkdir (dir, options);
+ }
return r;
}
+/* Process a command-line file name. */
+static int
+process_dir (char *dir, struct savewd *wd, void *options)
+{
+ struct mkdir_options const *o = options;
+ return (make_dir_parents (dir, wd, o->make_ancestor_function, options,
+ o->mode, announce_mkdir,
+ o->mode_bits, (uid_t) -1, (gid_t) -1, true)
+ ? EXIT_SUCCESS
+ : EXIT_FAILURE);
+}
+
int
main (int argc, char **argv)
{
- mode_t mode = S_IRWXUGO;
- mode_t mode_bits = 0;
- int (*make_ancestor_function) (char const *, void *) = NULL;
const char *specified_mode = NULL;
- int exit_status = EXIT_SUCCESS;
int optc;
struct mkdir_options options;
+ options.make_ancestor_function = NULL;
+ options.mode = S_IRWXUGO;
+ options.mode_bits = 0;
options.created_directory_format = NULL;
initialize_main (&argc, &argv);
@@ -130,7 +158,7 @@ main (int argc, char **argv)
switch (optc)
{
case 'p':
- make_ancestor_function = make_ancestor;
+ options.make_ancestor_function = make_ancestor;
break;
case 'm':
specified_mode = optarg;
@@ -151,7 +179,7 @@ main (int argc, char **argv)
usage (EXIT_FAILURE);
}
- if (make_ancestor_function || specified_mode)
+ if (options.make_ancestor_function || specified_mode)
{
mode_t umask_value = umask (0);
@@ -163,19 +191,14 @@ main (int argc, char **argv)
if (!change)
error (EXIT_FAILURE, 0, _("invalid mode %s"),
quote (specified_mode));
- mode = mode_adjust (S_IRWXUGO, true, umask_value, change,
- &mode_bits);
+ options.mode = mode_adjust (S_IRWXUGO, true, umask_value, change,
+ &options.mode_bits);
free (change);
}
else
- mode &= ~umask_value;
+ options.mode = S_IRWXUGO & ~umask_value;
}
- for (; optind < argc; ++optind)
- if (! make_dir_parents (argv[optind], make_ancestor_function, &options,
- mode, announce_mkdir,
- mode_bits, (uid_t) -1, (gid_t) -1, true))
- exit_status = EXIT_FAILURE;
-
- exit (exit_status);
+ exit (savewd_process_files (argc - optind, argv + optind,
+ process_dir, &options));
}
Index: tests/install/basic-1
===================================================================
RCS file: /fetish/cu/tests/install/basic-1,v
retrieving revision 1.19
diff -p -u -r1.19 basic-1
--- tests/install/basic-1 17 Aug 2006 19:58:28 -0000 1.19
+++ tests/install/basic-1 15 Sep 2006 22:15:09 -0000
@@ -1,7 +1,7 @@
#! /bin/sh
# Basic tests for "install".
-# Copyright (C) 1998, 2000, 2001, 2002, 2004, 2005 Free Software
+# Copyright (C) 1998, 2000, 2001, 2002, 2004, 2005, 2006 Free Software
# Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
@@ -95,16 +95,30 @@ test -d newdir3 || fail=1
# initial working directory ($abs) after creating the first argument, and
# hence cannot do anything meaningful with the following relative-named dirs.
abs=$pwd/$tmp
-mkdir sub && cd sub
-chmod 0 .; ginstall -d $abs/xx/yy rel/sub1 rel/sub2 2> /dev/null && fail=1
-chmod 755 $abs/sub
+mkdir sub || fail=1
+(cd sub && chmod 0 . && ginstall -d $abs/xx/yy rel/sub1 rel/sub2 2> /dev/null)
&& fail=1
+chmod 755 sub
# Ensure that the first argument-dir has been created.
-test -d $abs/xx/yy || fail=1
+test -d xx/yy || fail=1
# Make sure that the `rel' directory was not created...
-test -d $abs/sub/rel && fail=1
+test -d sub/rel && fail=1
# and make sure it was not created in the wrong place.
-test -d $abs/xx/rel && fail=1
+test -d xx/rel && fail=1
+
+# Test that we can install from an unreadable directory with an inaccessible
parent.
+# coreutils 5.97 fails this test.
+mkdir -p sub1/d || fail=1
+(cd sub1/d && chmod a-rx .. && chmod a-r . &&
+ ginstall -d $abs/xx/zz rel/a rel/b 2> /dev/null) || fail=1
+chmod 755 sub1 || fail=1
+chmod 755 sub1/d || fail=1
+test -d xx/zz || fail=1
+test -d sub1/d/rel/a || fail=1
+test -d sub1/d/rel/b || fail=1
+
+# Test whether you can install from a directory that is not readable
+# and whose parent is not accessible. coreutils 5.97 fails this test.
(exit $fail); exit $fail
Index: tests/mkdir/p-3
===================================================================
RCS file: /fetish/cu/tests/mkdir/p-3,v
retrieving revision 1.7
diff -p -u -r1.7 p-3
--- tests/mkdir/p-3 17 Aug 2006 19:58:33 -0000 1.7
+++ tests/mkdir/p-3 15 Sep 2006 22:15:09 -0000
@@ -37,7 +37,7 @@ mkdir -p $tmp || framework_failure=1
cd $tmp || framework_failure=1
mkdir no-access || framework_failure=1
mkdir no-acce2s || framework_failure=1
-mkdir no-acce3s || framework_failure=1
+mkdir -p no-acce3s/d || framework_failure=1
if test $framework_failure = 1; then
echo "$0: failure in testing framework" 1>&2
@@ -45,13 +45,18 @@ if test $framework_failure = 1; then
fi
p=$pwd/$tmp
-(cd no-access; chmod 0 . && mkdir -p $p/a/b u/v) 2> /dev/null && fail=1
+(cd no-access && chmod 0 . && mkdir -p $p/a/b u/v) 2> /dev/null && fail=1
test -d $p/a/b || fail=1
# Same as above, but with a following *absolute* name, it should succeed
-(cd no-acce2s; chmod 0 . && mkdir -p $p/b/b $p/z) || fail=1
+(cd no-acce2s && chmod 0 . && mkdir -p $p/b/b $p/z) || fail=1
+test -d $p/b/b && test -d $p/z || fail=1
-test -d $p/z || fail=1
+# Same as above, but a trailing relative name in an unreadable directory
+# whose parent is inaccessible. coreutils 5.97 fails this test.
+(cd no-acce3s/d && chmod a-rx .. && chmod a-r . && mkdir -p a/b $p/b/c d/e &&
+ test -d a/b && test -d d/e) || fail=1
+test -d $p/b/c || fail=1
b=`ls $p/a|tr -d '\n'`
# With coreutils-5.3.0, this would fail with $b=bu.
- proposed fix for some race conditions in mkdir and install,
Paul Eggert <=