1 /* Make a file's ancestor directories. 2 3 Copyright (C) 2006, 2009-2021 Free Software Foundation, Inc. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <https://www.gnu.org/licenses/>. */ 17 18 /* Written by Paul Eggert. */ 19 20 #include <config.h> 21 22 #include "mkancesdirs.h" 23 24 #include <sys/types.h> 25 #include <sys/stat.h> 26 #include <fcntl.h> 27 28 #include <errno.h> 29 #include <unistd.h> 30 31 #include "filename.h" 32 #include "savewd.h" 33 34 /* Ensure that the ancestor directories of FILE exist, using an 35 algorithm that should work even if two processes execute this 36 function in parallel. Modify FILE as necessary to access the 37 ancestor directories, but restore FILE to an equivalent value 38 if successful. 39 40 WD points to the working directory, using the conventions of 41 savewd. 42 43 Create any ancestor directories that don't already exist, by 44 invoking MAKE_DIR (FILE, COMPONENT, MAKE_DIR_ARG). This function 45 should return 0 if successful, -1 (setting errno) otherwise. If 46 COMPONENT is relative, it is relative to the temporary working 47 directory, which may differ from *WD. 48 49 Ordinarily MAKE_DIR is executed with the working directory changed 50 to reflect the already-made prefix, and mkancesdirs returns with 51 the working directory changed a prefix of FILE. However, if the 52 initial working directory cannot be saved in a file descriptor, 53 MAKE_DIR is invoked in a subprocess and this function returns in 54 both the parent and child process, so the caller should not assume 55 any changed state survives other than the EXITMAX component of WD, 56 and the caller should take care that the parent does not attempt to 57 do the work that the child is doing. 58 59 If successful and if this process can go ahead and create FILE, 60 return the length of the prefix of FILE that has already been made. 61 If successful so far but a child process is doing the actual work, 62 return -2. If unsuccessful, return -1 and set errno. */ 63 64 ptrdiff_t 65 mkancesdirs (char *file, struct savewd *wd, /* */ 66 int (*make_dir) (char const *, char const *, void *), 67 void *make_dir_arg) 68 { 69 /* Address of the previous directory separator that follows an 70 ordinary byte in a file name in the left-to-right scan, or NULL 71 if no such separator precedes the current location P. */ 72 char *sep = NULL; 73 74 /* Address of the leftmost file name component that has not yet 75 been processed. */ 76 char *component = file; 77 78 char *p = file + FILE_SYSTEM_PREFIX_LEN (file); 79 char c; 80 bool made_dir = false; 81 82 /* Scan forward through FILE, creating and chdiring into directories 83 along the way. Try MAKE_DIR before chdir, so that the procedure 84 works even when two or more processes are executing it in 85 parallel. Isolate each file name component by having COMPONENT 86 point to its start and SEP point just after its end. */ 87 88 while ((c = *p++)) 89 if (ISSLASH (*p)) 90 { 91 if (! ISSLASH (c)) 92 sep = p; 93 } 94 else if (ISSLASH (c) && *p && sep) 95 { 96 /* Don't bother to make or test for "." since it does not 97 affect the algorithm. */ 98 if (! (sep - component == 1 && component[0] == '.')) 99 { 100 int make_dir_errno = 0; 101 int savewd_chdir_options = 0; 102 int chdir_result; 103 104 /* Temporarily modify FILE to isolate this file name 105 component. */ 106 *sep = '\0'; 107 108 /* Invoke MAKE_DIR on this component, except don't bother 109 with ".." since it must exist if its "parent" does. */ 110 if (sep - component == 2 111 && component[0] == '.' && component[1] == '.') 112 made_dir = false; 113 else if (make_dir (file, component, make_dir_arg) < 0) 114 make_dir_errno = errno; 115 else 116 made_dir = true; 117 118 if (made_dir) 119 savewd_chdir_options |= SAVEWD_CHDIR_NOFOLLOW; 120 121 chdir_result = 122 savewd_chdir (wd, component, savewd_chdir_options, NULL); 123 124 /* Undo the temporary modification to FILE, unless there 125 was a failure. */ 126 if (chdir_result != -1) 127 *sep = '/'; 128 129 if (chdir_result != 0) 130 { 131 if (make_dir_errno != 0 && errno == ENOENT) 132 errno = make_dir_errno; 133 return chdir_result; 134 } 135 } 136 137 component = p; 138 } 139 140 return component - file; 141 }