1 /* Save and restore the working directory, possibly using a child process.
3 Copyright (C) 2006-2007, 2009-2019 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>
37 #include "fcntl-safer.h"
41 # define FALLTHROUGH ((void) 0)
43 # define FALLTHROUGH __attribute__ ((__fallthrough__))
47 /* Save the working directory into *WD, if it hasn't been saved
48 already. Return true if a child has been forked to do the real
51 savewd_save (struct savewd
*wd
)
56 /* Save the working directory, or prepare to fall back if possible. */
58 int fd
= open_safer (".", O_SEARCH
);
65 if (errno
!= EACCES
&& errno
!= ESTALE
)
67 wd
->state
= ERROR_STATE
;
68 wd
->val
.errnum
= errno
;
72 wd
->state
= FORKING_STATE
;
76 if (wd
->val
.child
< 0)
78 /* "Save" the initial working directory by forking a new
79 subprocess that will attempt all the work from the chdir
80 until the next savewd_restore. */
81 wd
->val
.child
= fork ();
82 if (wd
->val
.child
!= 0)
84 if (0 < wd
->val
.child
)
86 wd
->state
= ERROR_STATE
;
87 wd
->val
.errnum
= errno
;
93 case FD_POST_CHDIR_STATE
:
106 savewd_chdir (struct savewd
*wd
, char const *dir
, int options
,
112 /* Open the directory if requested, or if avoiding a race condition
113 is requested and possible. */
115 || (options
& (HAVE_WORKING_O_NOFOLLOW
? SAVEWD_CHDIR_NOFOLLOW
: 0)))
118 (O_SEARCH
| O_DIRECTORY
| O_NOCTTY
| O_NONBLOCK
119 | (options
& SAVEWD_CHDIR_NOFOLLOW
? O_NOFOLLOW
: 0)));
124 open_result
[1] = errno
;
127 if (fd
< 0 && errno
!= EACCES
)
131 if (result
== 0 && ! (0 <= fd
&& options
& SAVEWD_CHDIR_SKIP_READABLE
))
133 if (savewd_save (wd
))
140 result
= (fd
< 0 ? chdir (dir
) : fchdir (fd
));
146 wd
->state
= FD_POST_CHDIR_STATE
;
150 case FD_POST_CHDIR_STATE
:
155 assure (wd
->val
.child
== 0);
164 if (0 <= fd
&& ! open_result
)
175 savewd_restore (struct savewd
*wd
, int status
)
181 /* The working directory is the desired directory, so there's no
185 case FD_POST_CHDIR_STATE
:
186 /* Restore the working directory using fchdir. */
187 if (fchdir (wd
->val
.fd
) == 0)
189 wd
->state
= FD_STATE
;
194 int chdir_errno
= errno
;
196 wd
->state
= ERROR_STATE
;
197 wd
->val
.errnum
= chdir_errno
;
201 /* Report an error if asked to restore the working directory. */
202 errno
= wd
->val
.errnum
;
206 /* "Restore" the working directory by waiting for the subprocess
209 pid_t child
= wd
->val
.child
;
215 while (waitpid (child
, &child_status
, 0) < 0)
216 assure (errno
== EINTR
);
218 if (! WIFEXITED (child_status
))
219 raise (WTERMSIG (child_status
));
220 return WEXITSTATUS (child_status
);
233 savewd_finish (struct savewd
*wd
)
242 case FD_POST_CHDIR_STATE
:
247 assure (wd
->val
.child
< 0);
254 wd
->state
= FINAL_STATE
;
257 /* Return true if the actual work is currently being done by a
260 A true return means that the caller and the subprocess should
261 resynchronize later with savewd_restore, using only their own
262 memory to decide when to resynchronize; they should not consult the
263 file system to decide, because that might lead to race conditions.
264 This is why savewd_chdir is broken out into another function;
265 savewd_chdir's callers _can_ inspect the file system to decide
266 whether to call savewd_chdir. */
268 savewd_delegating (struct savewd
const *wd
)
270 return wd
->state
== FORKING_STATE
&& 0 < wd
->val
.child
;
274 savewd_process_files (int n_files
, char **file
,
275 int (*act
) (char *, struct savewd
*, void *),
280 int exit_status
= EXIT_SUCCESS
;
284 for (last_relative
= n_files
- 1; 0 <= last_relative
; last_relative
--)
285 if (! IS_ABSOLUTE_FILE_NAME (file
[last_relative
]))
288 for (; i
< last_relative
; i
++)
290 if (! savewd_delegating (&wd
))
292 int s
= act (file
[i
], &wd
, options
);
297 if (! IS_ABSOLUTE_FILE_NAME (file
[i
+ 1]))
299 int r
= savewd_restore (&wd
, exit_status
);
307 for (; i
< n_files
; i
++)
309 int s
= act (file
[i
], &wd
, options
);