1 /* Save and restore the working directory, possibly using a child process.
3 Copyright (C) 2006-2007, 2009-2020 Free Software Foundation, Inc.
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.
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.
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/>. */
18 /* Written by Paul Eggert. */
22 #define SAVEWD_INLINE _GL_EXTERN_INLINE
31 #include <sys/types.h>
36 #include "attribute.h"
37 #include "fcntl-safer.h"
40 /* Save the working directory into *WD, if it hasn't been saved
41 already. Return true if a child has been forked to do the real
44 savewd_save (struct savewd
*wd
)
49 /* Save the working directory, or prepare to fall back if possible. */
51 int fd
= open_safer (".", O_SEARCH
);
58 if (errno
!= EACCES
&& errno
!= ESTALE
)
60 wd
->state
= ERROR_STATE
;
61 wd
->val
.errnum
= errno
;
65 wd
->state
= FORKING_STATE
;
69 if (wd
->val
.child
< 0)
71 /* "Save" the initial working directory by forking a new
72 subprocess that will attempt all the work from the chdir
73 until the next savewd_restore. */
74 wd
->val
.child
= fork ();
75 if (wd
->val
.child
!= 0)
77 if (0 < wd
->val
.child
)
79 wd
->state
= ERROR_STATE
;
80 wd
->val
.errnum
= errno
;
86 case FD_POST_CHDIR_STATE
:
99 savewd_chdir (struct savewd
*wd
, char const *dir
, int options
,
105 /* Open the directory if requested, or if avoiding a race condition
106 is requested and possible. */
108 || (options
& (HAVE_WORKING_O_NOFOLLOW
? SAVEWD_CHDIR_NOFOLLOW
: 0)))
111 (O_SEARCH
| O_DIRECTORY
| O_NOCTTY
| O_NONBLOCK
112 | (options
& SAVEWD_CHDIR_NOFOLLOW
? O_NOFOLLOW
: 0)));
117 open_result
[1] = errno
;
120 if (fd
< 0 && errno
!= EACCES
)
124 if (result
== 0 && ! (0 <= fd
&& options
& SAVEWD_CHDIR_SKIP_READABLE
))
126 if (savewd_save (wd
))
133 result
= (fd
< 0 ? chdir (dir
) : fchdir (fd
));
139 wd
->state
= FD_POST_CHDIR_STATE
;
143 case FD_POST_CHDIR_STATE
:
148 assure (wd
->val
.child
== 0);
157 if (0 <= fd
&& ! open_result
)
168 savewd_restore (struct savewd
*wd
, int status
)
174 /* The working directory is the desired directory, so there's no
178 case FD_POST_CHDIR_STATE
:
179 /* Restore the working directory using fchdir. */
180 if (fchdir (wd
->val
.fd
) == 0)
182 wd
->state
= FD_STATE
;
187 int chdir_errno
= errno
;
189 wd
->state
= ERROR_STATE
;
190 wd
->val
.errnum
= chdir_errno
;
194 /* Report an error if asked to restore the working directory. */
195 errno
= wd
->val
.errnum
;
199 /* "Restore" the working directory by waiting for the subprocess
202 pid_t child
= wd
->val
.child
;
208 while (waitpid (child
, &child_status
, 0) < 0)
209 assure (errno
== EINTR
);
211 if (! WIFEXITED (child_status
))
212 raise (WTERMSIG (child_status
));
213 return WEXITSTATUS (child_status
);
226 savewd_finish (struct savewd
*wd
)
235 case FD_POST_CHDIR_STATE
:
240 assure (wd
->val
.child
< 0);
247 wd
->state
= FINAL_STATE
;
250 /* Return true if the actual work is currently being done by a
253 A true return means that the caller and the subprocess should
254 resynchronize later with savewd_restore, using only their own
255 memory to decide when to resynchronize; they should not consult the
256 file system to decide, because that might lead to race conditions.
257 This is why savewd_chdir is broken out into another function;
258 savewd_chdir's callers _can_ inspect the file system to decide
259 whether to call savewd_chdir. */
261 savewd_delegating (struct savewd
const *wd
)
263 return wd
->state
== FORKING_STATE
&& 0 < wd
->val
.child
;
267 savewd_process_files (int n_files
, char **file
,
268 int (*act
) (char *, struct savewd
*, void *),
273 int exit_status
= EXIT_SUCCESS
;
277 for (last_relative
= n_files
- 1; 0 <= last_relative
; last_relative
--)
278 if (! IS_ABSOLUTE_FILE_NAME (file
[last_relative
]))
281 for (; i
< last_relative
; i
++)
283 if (! savewd_delegating (&wd
))
285 int s
= act (file
[i
], &wd
, options
);
290 if (! IS_ABSOLUTE_FILE_NAME (file
[i
+ 1]))
292 int r
= savewd_restore (&wd
, exit_status
);
300 for (; i
< n_files
; i
++)
302 int s
= act (file
[i
], &wd
, options
);