1 /* Save and restore the working directory, possibly using a child process.
3 Copyright (C) 2006-2007, 2009-2017 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 <http://www.gnu.org/licenses/>. */
18 /* Written by Paul Eggert. */
22 #define SAVEWD_INLINE _GL_EXTERN_INLINE
31 #include <sys/types.h>
37 #include "fcntl-safer.h"
39 /* Save the working directory into *WD, if it hasn't been saved
40 already. Return true if a child has been forked to do the real
43 savewd_save (struct savewd
*wd
)
48 /* Save the working directory, or prepare to fall back if possible. */
50 int fd
= open_safer (".", O_SEARCH
);
57 if (errno
!= EACCES
&& errno
!= ESTALE
)
59 wd
->state
= ERROR_STATE
;
60 wd
->val
.errnum
= errno
;
64 wd
->state
= FORKING_STATE
;
68 if (wd
->val
.child
< 0)
70 /* "Save" the initial working directory by forking a new
71 subprocess that will attempt all the work from the chdir
72 until until the next savewd_restore. */
73 wd
->val
.child
= fork ();
74 if (wd
->val
.child
!= 0)
76 if (0 < wd
->val
.child
)
78 wd
->state
= ERROR_STATE
;
79 wd
->val
.errnum
= errno
;
85 case FD_POST_CHDIR_STATE
:
98 savewd_chdir (struct savewd
*wd
, char const *dir
, int options
,
104 /* Open the directory if requested, or if avoiding a race condition
105 is requested and possible. */
107 || (options
& (HAVE_WORKING_O_NOFOLLOW
? SAVEWD_CHDIR_NOFOLLOW
: 0)))
110 (O_SEARCH
| O_DIRECTORY
| O_NOCTTY
| O_NONBLOCK
111 | (options
& SAVEWD_CHDIR_NOFOLLOW
? O_NOFOLLOW
: 0)));
116 open_result
[1] = errno
;
119 if (fd
< 0 && errno
!= EACCES
)
123 if (result
== 0 && ! (0 <= fd
&& options
& SAVEWD_CHDIR_SKIP_READABLE
))
125 if (savewd_save (wd
))
132 result
= (fd
< 0 ? chdir (dir
) : fchdir (fd
));
138 wd
->state
= FD_POST_CHDIR_STATE
;
142 case FD_POST_CHDIR_STATE
:
147 assure (wd
->val
.child
== 0);
156 if (0 <= fd
&& ! open_result
)
167 savewd_restore (struct savewd
*wd
, int status
)
173 /* The working directory is the desired directory, so there's no
177 case FD_POST_CHDIR_STATE
:
178 /* Restore the working directory using fchdir. */
179 if (fchdir (wd
->val
.fd
) == 0)
181 wd
->state
= FD_STATE
;
186 int chdir_errno
= errno
;
188 wd
->state
= ERROR_STATE
;
189 wd
->val
.errnum
= chdir_errno
;
193 /* Report an error if asked to restore the working directory. */
194 errno
= wd
->val
.errnum
;
198 /* "Restore" the working directory by waiting for the subprocess
201 pid_t child
= wd
->val
.child
;
207 while (waitpid (child
, &child_status
, 0) < 0)
208 assure (errno
== EINTR
);
210 if (! WIFEXITED (child_status
))
211 raise (WTERMSIG (child_status
));
212 return WEXITSTATUS (child_status
);
225 savewd_finish (struct savewd
*wd
)
234 case FD_POST_CHDIR_STATE
:
239 assure (wd
->val
.child
< 0);
246 wd
->state
= FINAL_STATE
;
249 /* Return true if the actual work is currently being done by a
252 A true return means that the caller and the subprocess should
253 resynchronize later with savewd_restore, using only their own
254 memory to decide when to resynchronize; they should not consult the
255 file system to decide, because that might lead to race conditions.
256 This is why savewd_chdir is broken out into another function;
257 savewd_chdir's callers _can_ inspect the file system to decide
258 whether to call savewd_chdir. */
260 savewd_delegating (struct savewd
const *wd
)
262 return wd
->state
== FORKING_STATE
&& 0 < wd
->val
.child
;
266 savewd_process_files (int n_files
, char **file
,
267 int (*act
) (char *, struct savewd
*, void *),
272 int exit_status
= EXIT_SUCCESS
;
276 for (last_relative
= n_files
- 1; 0 <= last_relative
; last_relative
--)
277 if (! IS_ABSOLUTE_FILE_NAME (file
[last_relative
]))
280 for (; i
< last_relative
; i
++)
282 if (! savewd_delegating (&wd
))
284 int s
= act (file
[i
], &wd
, options
);
289 if (! IS_ABSOLUTE_FILE_NAME (file
[i
+ 1]))
291 int r
= savewd_restore (&wd
, exit_status
);
299 for (; i
< n_files
; i
++)
301 int s
= act (file
[i
], &wd
, options
);