Line data Source code
1 : /* Make a file's ancestor directories.
2 :
3 : Copyright (C) 2006 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 <http://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 "dirname.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 and the resulting directory is
46 : readable, 1 if successful but the resulting directory might not be
47 : readable, -1 (setting errno) otherwise. If COMPONENT is relative,
48 : it is relative to the temporary working directory, which may differ
49 : from *WD.
50 :
51 : Ordinarily MAKE_DIR is executed with the working directory changed
52 : to reflect the already-made prefix, and mkancesdirs returns with
53 : the working directory changed a prefix of FILE. However, if the
54 : initial working directory cannot be saved in a file descriptor,
55 : MAKE_DIR is invoked in a subprocess and this function returns in
56 : both the parent and child process, so the caller should not assume
57 : any changed state survives other than the EXITMAX component of WD,
58 : and the caller should take care that the parent does not attempt to
59 : do the work that the child is doing.
60 :
61 : If successful and if this process can go ahead and create FILE,
62 : return the length of the prefix of FILE that has already been made.
63 : If successful so far but a child process is doing the actual work,
64 : return -2. If unsuccessful, return -1 and set errno. */
65 :
66 : ptrdiff_t
67 77 : mkancesdirs (char *file, struct savewd *wd,
68 : int (*make_dir) (char const *, char const *, void *),
69 : void *make_dir_arg)
70 : {
71 : /* Address of the previous directory separator that follows an
72 : ordinary byte in a file name in the left-to-right scan, or NULL
73 : if no such separator precedes the current location P. */
74 77 : char *sep = NULL;
75 :
76 : /* Address of the leftmost file name component that has not yet
77 : been processed. */
78 77 : char *component = file;
79 :
80 77 : char *p = file + FILE_SYSTEM_PREFIX_LEN (file);
81 : char c;
82 77 : bool made_dir = false;
83 :
84 : /* Scan forward through FILE, creating and chdiring into directories
85 : along the way. Try MAKE_DIR before chdir, so that the procedure
86 : works even when two or more processes are executing it in
87 : parallel. Isolate each file name component by having COMPONENT
88 : point to its start and SEP point just after its end. */
89 :
90 432 : while ((c = *p++))
91 291 : if (ISSLASH (*p))
92 : {
93 181 : if (! ISSLASH (c))
94 39 : sep = p;
95 : }
96 110 : else if (ISSLASH (c) && *p && sep)
97 : {
98 : /* Don't bother to make or test for "." since it does not
99 : affect the algorithm. */
100 27 : if (! (sep - component == 1 && component[0] == '.'))
101 : {
102 18 : int make_dir_errno = 0;
103 18 : int savewd_chdir_options = 0;
104 : int chdir_result;
105 :
106 : /* Temporarily modify FILE to isolate this file name
107 : component. */
108 18 : *sep = '\0';
109 :
110 : /* Invoke MAKE_DIR on this component, except don't bother
111 : with ".." since it must exist if its "parent" does. */
112 18 : if (sep - component == 2
113 7 : && component[0] == '.' && component[1] == '.')
114 3 : made_dir = false;
115 : else
116 15 : switch (make_dir (file, component, make_dir_arg))
117 : {
118 14 : case -1:
119 14 : make_dir_errno = errno;
120 14 : break;
121 :
122 1 : case 0:
123 1 : savewd_chdir_options |= SAVEWD_CHDIR_READABLE;
124 : /* Fall through. */
125 1 : case 1:
126 1 : made_dir = true;
127 1 : break;
128 : }
129 :
130 18 : if (made_dir)
131 1 : savewd_chdir_options |= SAVEWD_CHDIR_NOFOLLOW;
132 :
133 18 : chdir_result =
134 : savewd_chdir (wd, component, savewd_chdir_options, NULL);
135 :
136 : /* Undo the temporary modification to FILE, unless there
137 : was a failure. */
138 18 : if (chdir_result != -1)
139 5 : *sep = '/';
140 :
141 18 : if (chdir_result != 0)
142 : {
143 13 : if (make_dir_errno != 0 && errno == ENOENT)
144 8 : errno = make_dir_errno;
145 13 : return chdir_result;
146 : }
147 : }
148 :
149 14 : component = p;
150 : }
151 :
152 64 : return component - file;
153 : }
|