X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fmkancesdirs.c;h=0c60a93cdab933ab471cdfde309a21dfe950257f;hb=fa1db0dd22768f09a507674a30beb5b8a87bb35f;hp=4737742366806f6a13741467f6dd5202213e2c39;hpb=5f531596a92e1205a700218dedfe7a525eef21c8;p=gnulib.git diff --git a/lib/mkancesdirs.c b/lib/mkancesdirs.c index 473774236..0c60a93cd 100644 --- a/lib/mkancesdirs.c +++ b/lib/mkancesdirs.c @@ -1,11 +1,11 @@ /* Make a file's ancestor directories. - Copyright (C) 2006 Free Software Foundation, Inc. + Copyright (C) 2006, 2009-2013 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or modify + 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. + the Free Software Foundation; either version 3 of the License, 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 @@ -13,120 +13,141 @@ 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. */ + along with this program. If not, see . */ /* Written by Paul Eggert. */ -#ifdef HAVE_CONFIG_H -# include -#endif +#include #include "mkancesdirs.h" -#include +#include #include +#include -#include "dirname.h" -#include "stat-macros.h" - -/* Return 0 if FILE is a directory, otherwise -1 (setting errno). */ +#include +#include -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. - - 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. + function in parallel. Modify FILE as necessary to access the + ancestor directories, but restore FILE to an equivalent value + if successful. - 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. */ + WD points to the working directory, using the conventions of + savewd. -int -mkancesdirs (char *file, - int (*make_dir) (char const *, void *), - void *make_dir_arg) + Create any ancestor directories that don't already exist, by + invoking MAKE_DIR (FILE, 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. */ + +ptrdiff_t +mkancesdirs (char *file, struct savewd *wd, + int (*make_dir) (char const *, 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)) { - if (! ISSLASH (c)) - sep = p; + if (! ISSLASH (c)) + sep = p; } 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 (file, 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; }