This source file includes following definitions.
- savewd_save
- savewd_chdir
- savewd_restore
- savewd_finish
- savewd_delegating
- savewd_process_files
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 #include <config.h>
21
22 #define SAVEWD_INLINE _GL_EXTERN_INLINE
23
24 #include "savewd.h"
25
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <signal.h>
29 #include <stdbool.h>
30 #include <stdlib.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <unistd.h>
34
35 #include "assure.h"
36 #include "attribute.h"
37 #include "fcntl-safer.h"
38 #include "filename.h"
39
40
41
42
43 static bool
44 savewd_save (struct savewd *wd)
45 {
46 switch (wd->state)
47 {
48 case INITIAL_STATE:
49
50 {
51 int fd = open_safer (".", O_SEARCH);
52 if (0 <= fd)
53 {
54 wd->state = FD_STATE;
55 wd->val.fd = fd;
56 break;
57 }
58 if (errno != EACCES && errno != ESTALE)
59 {
60 wd->state = ERROR_STATE;
61 wd->val.errnum = errno;
62 break;
63 }
64 }
65 wd->state = FORKING_STATE;
66 wd->val.child = -1;
67 FALLTHROUGH;
68 case FORKING_STATE:
69 if (wd->val.child < 0)
70 {
71
72
73
74 wd->val.child = fork ();
75 if (wd->val.child != 0)
76 {
77 if (0 < wd->val.child)
78 return true;
79 wd->state = ERROR_STATE;
80 wd->val.errnum = errno;
81 }
82 }
83 break;
84
85 case FD_STATE:
86 case FD_POST_CHDIR_STATE:
87 case ERROR_STATE:
88 case FINAL_STATE:
89 break;
90
91 default:
92 assure (false);
93 }
94
95 return false;
96 }
97
98 int
99 savewd_chdir (struct savewd *wd, char const *dir, int options,
100 int open_result[2])
101 {
102 int fd = -1;
103 int result = 0;
104
105
106
107 if (open_result
108 || (options & (HAVE_WORKING_O_NOFOLLOW ? SAVEWD_CHDIR_NOFOLLOW : 0)))
109 {
110 fd = open (dir,
111 (O_SEARCH | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
112 | (options & SAVEWD_CHDIR_NOFOLLOW ? O_NOFOLLOW : 0)));
113
114 if (open_result)
115 {
116 open_result[0] = fd;
117 open_result[1] = errno;
118 }
119
120 if (fd < 0 && errno != EACCES)
121 result = -1;
122 }
123
124 if (result == 0 && ! (0 <= fd && options & SAVEWD_CHDIR_SKIP_READABLE))
125 {
126 if (savewd_save (wd))
127 {
128 open_result = NULL;
129 result = -2;
130 }
131 else
132 {
133 result = (fd < 0 ? chdir (dir) : fchdir (fd));
134
135 if (result == 0)
136 switch (wd->state)
137 {
138 case FD_STATE:
139 wd->state = FD_POST_CHDIR_STATE;
140 break;
141
142 case ERROR_STATE:
143 case FD_POST_CHDIR_STATE:
144 case FINAL_STATE:
145 break;
146
147 case FORKING_STATE:
148 assure (wd->val.child == 0);
149 break;
150
151 default:
152 assure (false);
153 }
154 }
155 }
156
157 if (0 <= fd && ! open_result)
158 {
159 int e = errno;
160 close (fd);
161 errno = e;
162 }
163
164 return result;
165 }
166
167 int
168 savewd_restore (struct savewd *wd, int status)
169 {
170 switch (wd->state)
171 {
172 case INITIAL_STATE:
173 case FD_STATE:
174
175
176 break;
177
178 case FD_POST_CHDIR_STATE:
179
180 if (fchdir (wd->val.fd) == 0)
181 {
182 wd->state = FD_STATE;
183 break;
184 }
185 else
186 {
187 int chdir_errno = errno;
188 close (wd->val.fd);
189 wd->state = ERROR_STATE;
190 wd->val.errnum = chdir_errno;
191 }
192 FALLTHROUGH;
193 case ERROR_STATE:
194
195 errno = wd->val.errnum;
196 return -1;
197
198 case FORKING_STATE:
199
200
201 {
202 pid_t child = wd->val.child;
203 if (child == 0)
204 _exit (status);
205 if (0 < child)
206 {
207 int child_status;
208 while (waitpid (child, &child_status, 0) < 0)
209 assure (errno == EINTR);
210 wd->val.child = -1;
211 if (! WIFEXITED (child_status))
212 raise (WTERMSIG (child_status));
213 return WEXITSTATUS (child_status);
214 }
215 }
216 break;
217
218 default:
219 assure (false);
220 }
221
222 return 0;
223 }
224
225 void
226 savewd_finish (struct savewd *wd)
227 {
228 switch (wd->state)
229 {
230 case INITIAL_STATE:
231 case ERROR_STATE:
232 break;
233
234 case FD_STATE:
235 case FD_POST_CHDIR_STATE:
236 close (wd->val.fd);
237 break;
238
239 case FORKING_STATE:
240 assure (wd->val.child < 0);
241 break;
242
243 default:
244 assure (false);
245 }
246
247 wd->state = FINAL_STATE;
248 }
249
250
251
252
253
254
255
256
257
258
259
260 static bool
261 savewd_delegating (struct savewd const *wd)
262 {
263 return wd->state == FORKING_STATE && 0 < wd->val.child;
264 }
265
266 int
267 savewd_process_files (int n_files, char **file,
268 int (*act) (char *, struct savewd *, void *),
269 void *options)
270 {
271 int i = 0;
272 int last_relative;
273 int exit_status = EXIT_SUCCESS;
274 struct savewd wd;
275 savewd_init (&wd);
276
277 for (last_relative = n_files - 1; 0 <= last_relative; last_relative--)
278 if (! IS_ABSOLUTE_FILE_NAME (file[last_relative]))
279 break;
280
281 for (; i < last_relative; i++)
282 {
283 if (! savewd_delegating (&wd))
284 {
285 int s = act (file[i], &wd, options);
286 if (exit_status < s)
287 exit_status = s;
288 }
289
290 if (! IS_ABSOLUTE_FILE_NAME (file[i + 1]))
291 {
292 int r = savewd_restore (&wd, exit_status);
293 if (exit_status < r)
294 exit_status = r;
295 }
296 }
297
298 savewd_finish (&wd);
299
300 for (; i < n_files; i++)
301 {
302 int s = act (file[i], &wd, options);
303 if (exit_status < s)
304 exit_status = s;
305 }
306
307 return exit_status;
308 }