Mercurial > hg > octave-nkf > gnulib-hg
changeset 7312:796e1837ac66
* lib/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.
* lib/dirchownmod.h: Likewise.
* lib/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.
* lib/mkancesdirs.h: Include <stddef.h>, for ptrdiff_t.
Adjust signature to match code.
* lib/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.
* lib/savewd.c, lib/savewd.h: New files.
* m4/savewd.m4: New file.
* modules/mkancesdirs (Depends-on): Add fcntl.
* modules/savewd: New file.
* MODULES.html.sh (File system functions): Add savewd.
author | Paul Eggert <eggert@cs.ucla.edu> |
---|---|
date | Sat, 16 Sep 2006 19:58:25 +0000 |
parents | dc0ecc0fbaf2 |
children | 38e8d472e939 |
files | ChangeLog MODULES.html.sh lib/ChangeLog lib/dirchownmod.c lib/dirchownmod.h lib/mkancesdirs.c lib/mkancesdirs.h lib/mkdir-p.c lib/mkdir-p.h m4/ChangeLog modules/mkancesdirs |
diffstat | 11 files changed, 251 insertions(+), 142 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2006-09-15 Paul Eggert <eggert@cs.ucla.edu> + * 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.
--- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -1908,6 +1908,7 @@ func_module same func_module save-cwd func_module savedir + func_module savewd func_module stat-time func_module tmpdir func_module unlinkdir
--- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,29 @@ +2006-09-15 Paul Eggert <eggert@cs.ucla.edu> + + * 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 <jim@meyering.net> * rename-dest-slash.c (has_trailing_slash): Use
--- a/lib/dirchownmod.c +++ b/lib/dirchownmod.c @@ -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) {
--- a/lib/dirchownmod.h +++ b/lib/dirchownmod.h @@ -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);
--- a/lib/mkancesdirs.c +++ b/lib/mkancesdirs.c @@ -22,94 +22,71 @@ #include "mkancesdirs.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + #include <errno.h> -#include <sys/stat.h> +#include <unistd.h> #include "dirname.h" -#include "stat-macros.h" - -/* Return 0 if FILE is a directory, otherwise -1 (setting errno). */ - -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 "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. + + WD points to the working directory, using the conventions of + savewd. 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. + 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. - 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. */ + 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. -int -mkancesdirs (char *file, + 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. */ + +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. */ - - for (p = last_component (file); prefix_end < p; p--) - if (ISSLASH (*p) && ! ISSLASH (p[-1])) - { - *p = '\0'; + /* Address of the leftmost file name component that has not yet + been processed. */ + char *component = file; - if (errno == ENOENT && make_dir (file, make_dir_arg) == 0) - { - *p = '/'; - break; - } + char *p = file + FILE_SYSTEM_PREFIX_LEN (file); + char c; + bool made_dir = false; - if (errno != ENOENT) - { - if (test_dir (file) == 0) - { - *p = '/'; - break; - } - if (errno != ENOENT) - return -1; - } - - *p = '/'; - } - - /* 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 @@ } 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; }
--- a/lib/mkancesdirs.h +++ b/lib/mkancesdirs.h @@ -1,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 *);
--- a/lib/mkdir-p.c +++ b/lib/mkdir-p.c @@ -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 @@ 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) + { + prefix_len = mkancesdirs (dir, wd, make_ancestor, options); + if (prefix_len < 0) + { + if (prefix_len < -1) + return true; + mkdir_errno = errno; + } + } + + if (0 <= prefix_len) { - /* mkancestdirs updated DIR for a better-looking - diagnostic, so don't try to stat DIR below. */ - make_ancestor = NULL; + 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 (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))) - { - error (0, mkdir_errno, _("cannot create directory %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; }
--- a/lib/mkdir-p.h +++ b/lib/mkdir-p.h @@ -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,