| /* Make a file's ancestor directories. | |
| Copyright (C) 2006, 2009-2026 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 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 | |
| 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 <https://www.gnu.org/licenses/>. */ | |
| /* Written by Paul Eggert. */ | |
| #include <config.h> | |
| #include "mkancesdirs.h" | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <fcntl.h> | |
| #include <errno.h> | |
| #include <unistd.h> | |
| #include "filename.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. 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 (FILE, COMPONENT, MAKE_DIR_ARG). This function | |
| should return 0 if successful, -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) | |
| { | |
| /* 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; | |
| /* Address of the leftmost file name component that has not yet | |
| been processed. */ | |
| char *component = file; | |
| char *p = file + FILE_SYSTEM_PREFIX_LEN (file); | |
| char c; | |
| bool made_dir = false; | |
| /* 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; | |
| } | |
| else if (ISSLASH (c) && *p && sep) | |
| { | |
| /* Don't bother to make or test for "." since it does not | |
| affect the algorithm. */ | |
| if (! (sep - component == 1 && component[0] == '.')) | |
| { | |
| /* 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. */ | |
| int make_dir_errno = 0; | |
| if (sep - component == 2 | |
| && component[0] == '.' && component[1] == '.') | |
| made_dir = false; | |
| else if (make_dir (file, component, make_dir_arg) < 0) | |
| make_dir_errno = errno; | |
| else | |
| made_dir = true; | |
| int savewd_chdir_options = 0; | |
| if (made_dir) | |
| savewd_chdir_options |= SAVEWD_CHDIR_NOFOLLOW; | |
| int 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 component - file; | |
| } |