* lib/dircallback.c (run_in_dirfd): New name for old run_in_dir function. (run_in_dir): Like the old funciton of the same name, but now takes an argument const struct saved_cwd *. * lib/dircallback.h: Update declarations of run_in_dirfd and run_in_dir. * find/util.c: Include dircallback.h, xalloc.h, save-cwd.h. (do_complete_pending_execdirs): Remove dir_fd parameter, since the per-predicate data structures now indicate what directory they need to be run in. Instead of calling bc_do_exec directly, use a callback 'exec_cb' that uses run_in_dir (which now takes a saved_cwd* parameter instead of a file descriptor). (do_exec): Called by do_complete_pending_execdirs, and simply uses run_in_dir to call exec_cb, restoring the working directory afterward. (record_initial_cwd): New function, initialises the global variable initial_wd. (cleanup_initial_cwd): New function, cleans up the global variable initial_wd. (cleanup): Call cleanup_initial_cwd. (get_start_dirfd): Remove. (is_exec_in_local_dir): New funciton; true for predicates -execdir and -okdir. * find/pred.c: Include cloexec.h and save-cwd.h. (record_exec_dir): New function, sets the value of execp->wd_for_exec if needed. (new_impl_pred_exec): Remove the obsolete dir_fd parameter. Call record_exec_dir. (pred_exec): Don't pass the dir_fd parameter. (pred_execdir): Likewise. (pred_ok): Likewise. (pred_okdir): Likewise. (can_access): Call run_in_dirfd rather than run_in_dir (the function was renamed). (prep_child_for_exec): Remove dir_fd parameter; don't fchdir to that. Call restore_cwd instead (passing a saved_cwd* parameter which replaced dir_fd). (launch): Remove references to execp->use_current_dir. (launch): Change references to execp->dir_fd to execp->wd_for_exec. * find/parser.c: Correct indentiation of declaration of insert_exec_ok and remove the obsolete dir_fd parameter. (parse_exec): Don't pass the dir_fd parameter to insert_exec_ok. (parse_execdir): Likewise. (parse_ok): Likewise. (parse_okdir): Likewise. (insert_exec_ok): Remove obsolete dir_fd paramter. Initialise execp->wd_for_exec, either to NULL (for -*dir) or to the initial_wd. * find/ftsfind.c: Remove get_current_dirfd. Remove complete_execdirs_cb. (consider_visiting): Call complete_pending_execdirs directly. (main): Call record_initial_cwd to record the initial working directory, early on. Don't initialise starting_dir or starting_desc, they have been removed. * find/finddata.c: Include save-cwd.h. Remove starting_dir and starting_desc. Add new global variable initial_wd. It is a struct saved_wd* and represents find's initial working directory. * find/find.c: Include save-cwd.h. (main): Call record_initial_cwd in order to initialise the global variable initial_wd Don't set starting_desc and starting_dir, since those variables have been removed. (safely_chdir): Don't pass an fd to complete_pending_execdirs. (chdir_back): Remove the safety check (since we are using fchdir and in any case no longer have all the data that the existing wd_sanity_check function wants). (do_process_top_dir): Don't pass an fd to complete_pending_execdirs. (process_dir): Likewise. * find/defs.h (struct exec_val): Remove use_current_dir and dir_fd. Replace with wd_for_exec, which is a struct saved_wd*. (get_start_dirfd): Remove prototype. (get_current_dirfd): Remove prototype. (complete_pending_execdirs): No longer takes dir_fd parameter. (record_initial_cwd): Add prototype. (is_exec_in_local_dir): Add prototype. (options): Declare. (initial_wd): Add declaration. It is a struct saved_wd* and represents find's initial working directory. (starting_dir): Remove devlaration of global variable. (starting_desc): Remove devlaration of global variable. * import-gnulib.config (modules): Import module save-cwd.
Signed-off-by: James Youngman <[email protected]> --- ChangeLog | 83 ++++++++++++++++++++++++++++++++++++++++++++ find/defs.h | 13 +++---- find/find.c | 70 +++++-------------------------------- find/finddata.c | 10 +---- find/ftsfind.c | 48 ++------------------------ find/parser.c | 42 ++++++++-------------- find/pred.c | 93 ++++++++++++++++++++++++++++++++++---------------- find/util.c | 88 ++++++++++++++++++++++++++++++++++++++--------- import-gnulib.config | 1 + lib/dircallback.c | 31 ++++++++++++++++- lib/dircallback.h | 5 ++- 11 files changed, 288 insertions(+), 196 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0249307..e167e7a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,88 @@ 2010-04-10 James Youngman <[email protected]> + Exec predicates now store which directory they want to run in. + * lib/dircallback.c (run_in_dirfd): New name for old run_in_dir + function. + (run_in_dir): Like the old funciton of the same name, but now + takes an argument const struct saved_cwd *. + * lib/dircallback.h: Update declarations of run_in_dirfd and + run_in_dir. + * find/util.c: Include dircallback.h, xalloc.h, save-cwd.h. + (do_complete_pending_execdirs): Remove dir_fd parameter, since the + per-predicate data structures now indicate what directory they + need to be run in. Instead of calling bc_do_exec directly, use a + callback 'exec_cb' that uses run_in_dir (which now takes a + saved_cwd* parameter instead of a file descriptor). + (do_exec): Called by do_complete_pending_execdirs, and simply uses + run_in_dir to call exec_cb, restoring the working directory + afterward. + (record_initial_cwd): New function, initialises the global + variable initial_wd. + (cleanup_initial_cwd): New function, cleans up the global variable + initial_wd. + (cleanup): Call cleanup_initial_cwd. + (get_start_dirfd): Remove. + (is_exec_in_local_dir): New funciton; true for predicates -execdir + and -okdir. + * find/pred.c: Include cloexec.h and save-cwd.h. + (record_exec_dir): New function, sets the value of + execp->wd_for_exec if needed. + (new_impl_pred_exec): Remove the obsolete dir_fd parameter. Call + record_exec_dir. + (pred_exec): Don't pass the dir_fd parameter. + (pred_execdir): Likewise. + (pred_ok): Likewise. + (pred_okdir): Likewise. + (can_access): Call run_in_dirfd rather than run_in_dir (the + function was renamed). + (prep_child_for_exec): Remove dir_fd parameter; don't fchdir to + that. Call restore_cwd instead (passing a saved_cwd* parameter + which replaced dir_fd). + (launch): Remove references to execp->use_current_dir. + (launch): Change references to execp->dir_fd to execp->wd_for_exec. + * find/parser.c: Correct indentiation of declaration of + insert_exec_ok and remove the obsolete dir_fd parameter. + (parse_exec): Don't pass the dir_fd parameter to insert_exec_ok. + (parse_execdir): Likewise. + (parse_ok): Likewise. + (parse_okdir): Likewise. + (insert_exec_ok): Remove obsolete dir_fd paramter. Initialise + execp->wd_for_exec, either to NULL (for -*dir) or to the + initial_wd. + * find/ftsfind.c: Remove get_current_dirfd. Remove + complete_execdirs_cb. + (consider_visiting): Call complete_pending_execdirs directly. + (main): Call record_initial_cwd to record the initial working + directory, early on. Don't initialise starting_dir or + starting_desc, they have been removed. + * find/finddata.c: Include save-cwd.h. Remove starting_dir and + starting_desc. Add new global variable initial_wd. It is a struct + saved_wd* and represents find's initial working directory. + * find/find.c: Include save-cwd.h. + (main): Call record_initial_cwd in order to initialise the + global variable initial_wd Don't set starting_desc and + starting_dir, since those variables have been removed. + (safely_chdir): Don't pass an fd to complete_pending_execdirs. + (chdir_back): Remove the safety check (since we are using fchdir + and in any case no longer have all the data that the existing + wd_sanity_check function wants). + (do_process_top_dir): Don't pass an fd to + complete_pending_execdirs. + (process_dir): Likewise. + * find/defs.h (struct exec_val): Remove use_current_dir and + dir_fd. Replace with wd_for_exec, which is a struct saved_wd*. + (get_start_dirfd): Remove prototype. + (get_current_dirfd): Remove prototype. + (complete_pending_execdirs): No longer takes dir_fd parameter. + (record_initial_cwd): Add prototype. + (is_exec_in_local_dir): Add prototype. + (options): Declare. + (initial_wd): Add declaration. It is a struct saved_wd* and + represents find's initial working directory. + (starting_dir): Remove devlaration of global variable. + (starting_desc): Remove devlaration of global variable. + * import-gnulib.config (modules): Import module save-cwd. + Add a test which checks $CWD for find -execdir {} +/; * find/testsuite/find.gnu/execdir-multiple.exp: New test; verifies that for -execdir +, all the execs occur with the correct workikng diff --git a/find/defs.h b/find/defs.h index 2be9da5..0e39959 100644 --- a/find/defs.h +++ b/find/defs.h @@ -194,9 +194,8 @@ struct exec_val struct buildcmd_state state; char **replace_vec; /* Command arguments (for ";" style) */ int num_args; - bool use_current_dir; /* If nonzero, don't chdir to start dir */ bool close_stdin; /* If true, close stdin in the child. */ - int dir_fd; /* The directory to do the exec in. */ + struct saved_cwd *wd_for_exec; /* What directory to perform the exec in. */ int last_child_status; /* Status of the most recent child. */ }; @@ -340,8 +339,6 @@ struct predicate /* find.c, ftsfind.c */ bool is_fts_enabled(int *ftsoptions); -int get_start_dirfd(void); -int get_current_dirfd(void); /* find library function declarations. */ @@ -500,8 +497,10 @@ struct predicate *insert_primary_withpred PARAMS((const struct parser_table *ent void usage PARAMS((FILE *fp, int status, char *msg)); extern bool check_nofollow(void); void complete_pending_execs(struct predicate *p); -void complete_pending_execdirs(int dir_fd); /* Passing dir_fd is an unpleasant CodeSmell. */ +void complete_pending_execdirs (void); const char *safely_quote_err_filename (int n, char const *arg); +void record_initial_cwd (void); +bool is_exec_in_local_dir(const PRED_FUNC pred_func); void fatal_target_file_error (int errno_value, const char *name) ATTRIBUTE_NORETURN; void fatal_nontarget_file_error (int errno_value, const char *name) ATTRIBUTE_NORETURN; @@ -675,8 +674,8 @@ struct state }; /* finddata.c */ +extern struct options options; extern struct state state; -extern char const *starting_dir; -extern int starting_desc; +extern struct saved_cwd *initial_wd; #endif diff --git a/find/find.c b/find/find.c index 30a2c34..3fa36ba 100644 --- a/find/find.c +++ b/find/find.c @@ -50,6 +50,7 @@ #include "error.h" #include "fdleak.h" #include "progname.h" +#include "save-cwd.h" #ifdef HAVE_LOCALE_H #include <locale.h> @@ -136,6 +137,8 @@ main (int argc, char **argv) remember_non_cloexec_fds (); } + record_initial_cwd (); + state.already_issued_stat_error_msg = false; state.shared_files = sharefile_init ("w"); if (NULL == state.shared_files) @@ -196,24 +199,6 @@ main (int argc, char **argv) } - starting_desc = open_cloexec (".", O_RDONLY -#if defined O_LARGEFILE - |O_LARGEFILE -#endif - ); - if (0 <= starting_desc && fchdir (starting_desc) != 0) - { - close (starting_desc); - starting_desc = -1; - } - - if (starting_desc < 0) - { - starting_dir = xgetcwd (); - if (! starting_dir) - error (EXIT_FAILURE, errno, _("cannot get current directory")); - } - set_stat_placeholders (&starting_stat_buf); if ((*options.xstat) (".", &starting_stat_buf) != 0) error (EXIT_FAILURE, errno, _("cannot stat current directory")); @@ -893,7 +878,7 @@ safely_chdir (const char *dest, * processed, do them now because they must be done in the same * directory. */ - complete_pending_execdirs (get_current_dirfd ()); + complete_pending_execdirs (); #if !defined(O_NOFOLLOW) options.open_nofollow_available = false; @@ -928,45 +913,10 @@ safely_chdir (const char *dest, static void chdir_back (void) { - struct stat stat_buf; - bool dummy; - - if (starting_desc < 0) - { - if (options.debug_options & DebugSearch) - fprintf (stderr, "chdir_back(): chdir(\"%s\")\n", starting_dir); - -#ifdef STAT_MOUNTPOINTS - /* We will need the mounted device list. Get it now if we don't - * already have it. - */ - if (NULL == mounted_devices) - init_mounted_dev_list (1); -#endif - - if (chdir (starting_dir) != 0) - fatal_nontarget_file_error (errno, starting_dir); - - wd_sanity_check (starting_dir, - program_name, - starting_dir, - starting_stat_buf.st_dev, - starting_stat_buf.st_ino, - &stat_buf, 0, __LINE__, - TraversingUp, - FATAL_IF_SANITY_CHECK_FAILS, - &dummy); - } - else - { - if (options.debug_options & DebugSearch) - fprintf (stderr, "chdir_back(): chdir(<starting-point>)\n"); + if (options.debug_options & DebugSearch) + fprintf (stderr, "chdir_back(): chdir to start point\n"); - if (fchdir (starting_desc) != 0) - { - fatal_nontarget_file_error (errno, starting_dir); - } - } + restore_cwd (initial_wd); } /* Move to the parent of a given directory and then call a function, @@ -1055,7 +1005,7 @@ static void do_process_top_dir (char *pathname, (void) pstat; process_path (pathname, base, false, ".", mode); - complete_pending_execdirs (get_current_dirfd ()); + complete_pending_execdirs (); } static void @@ -1345,7 +1295,7 @@ process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, * yet been processed, do them now because they must be done in * the same directory. */ - complete_pending_execdirs (get_current_dirfd ()); + complete_pending_execdirs (); if (strcmp (name, ".")) { @@ -1482,7 +1432,7 @@ process_dir (char *pathname, char *name, int pathlen, const struct stat *statp, * yet been processed, do them now because they must be done in * the same directory. */ - complete_pending_execdirs (get_current_dirfd ()); + complete_pending_execdirs (); if (strcmp (name, ".")) { diff --git a/find/finddata.c b/find/finddata.c index eb2fdeb..d37a678 100644 --- a/find/finddata.c +++ b/find/finddata.c @@ -19,17 +19,11 @@ #include <config.h> #include "defs.h" +#include "save-cwd.h" struct options options; struct state state; -/* The full path of the initial working directory, or "." if - STARTING_DESC is nonnegative. */ -char const *starting_dir = "."; - -/* A file descriptor open to the initial working directory. - Doing it this way allows us to work when the i.w.d. has - unreadable parents. */ -int starting_desc; +struct saved_cwd *initial_wd = NULL; diff --git a/find/ftsfind.c b/find/ftsfind.c index 7f9f0a8..49aef9b 100644 --- a/find/ftsfind.c +++ b/find/ftsfind.c @@ -89,24 +89,6 @@ static bool find (char *arg) __attribute_warn_unused_result__; static bool process_all_startpoints (int argc, char *argv[]) __attribute_warn_unused_result__; -int -get_current_dirfd (void) -{ - if (ftsoptions & FTS_CWDFD) - { - assert (curr_fd != -1); - assert ( (AT_FDCWD == curr_fd) || (curr_fd >= 0) ); - - if (AT_FDCWD == curr_fd) - return starting_desc; - else - return curr_fd; - } - else - { - return AT_FDCWD; - } -} static void left_dir (void) @@ -299,15 +281,6 @@ symlink_loop (const char *name) } -static int -complete_execdirs_cb (void *context) -{ - (void) context; - /* By the tme this callback is called, the current directory is correct. */ - complete_pending_execdirs (AT_FDCWD); - return 0; -} - static void show_outstanding_execdirs (FILE *fp) { @@ -555,7 +528,7 @@ consider_visiting (FTS *p, FTSENT *ent) if (state.execdirs_outstanding) { show_outstanding_execdirs (stderr); - run_in_dir (p->fts_cwd_fd, complete_execdirs_cb, NULL); + complete_pending_execdirs (); } if (ent->fts_info == FTS_DP) @@ -674,6 +647,8 @@ main (int argc, char **argv) else set_program_name ("find"); + record_initial_cwd (); + state.already_issued_stat_error_msg = false; state.exit_status = 0; state.execdirs_outstanding = false; @@ -740,23 +715,6 @@ main (int argc, char **argv) } - starting_desc = open_cloexec (".", O_RDONLY -#if defined O_LARGEFILE - |O_LARGEFILE -#endif - ); - if (0 <= starting_desc && fchdir (starting_desc) != 0) - { - close (starting_desc); - starting_desc = -1; - } - if (starting_desc < 0) - { - starting_dir = xgetcwd (); - if (! starting_dir) - error (EXIT_FAILURE, errno, _("cannot get current directory")); - } - /* process_all_startpoints processes the starting points named on * the command line. A false return value from it means that we * failed to restore the original context. That means it would not diff --git a/find/parser.c b/find/parser.c index fb6c753..55bc63c 100644 --- a/find/parser.c +++ b/find/parser.c @@ -181,10 +181,9 @@ static struct segment **make_segment PARAMS((struct segment **segment, char aux_format_char, struct predicate *pred)); static bool insert_exec_ok PARAMS((const char *action, - const struct parser_table *entry, - int dir_fd, - char *argv[], - int *arg_ptr)); + const struct parser_table *entry, + char *argv[], + int *arg_ptr)); static bool get_comp_type PARAMS((const char **str, enum comparison_type *comp_type)); static bool get_relative_timestamp PARAMS((const char *str, @@ -934,13 +933,13 @@ parse_empty (const struct parser_table* entry, char **argv, int *arg_ptr) static bool parse_exec (const struct parser_table* entry, char **argv, int *arg_ptr) { - return insert_exec_ok ("-exec", entry, get_start_dirfd (), argv, arg_ptr); + return insert_exec_ok ("-exec", entry, argv, arg_ptr); } static bool parse_execdir (const struct parser_table* entry, char **argv, int *arg_ptr) { - return insert_exec_ok ("-execdir", entry, -1, argv, arg_ptr); + return insert_exec_ok ("-execdir", entry, argv, arg_ptr); } static bool @@ -1814,13 +1813,13 @@ parse_nowarn (const struct parser_table* entry, char **argv, int *arg_ptr) static bool parse_ok (const struct parser_table* entry, char **argv, int *arg_ptr) { - return insert_exec_ok ("-ok", entry, get_start_dirfd (), argv, arg_ptr); + return insert_exec_ok ("-ok", entry, argv, arg_ptr); } static bool parse_okdir (const struct parser_table* entry, char **argv, int *arg_ptr) { - return insert_exec_ok ("-okdir", entry, -1, argv, arg_ptr); + return insert_exec_ok ("-okdir", entry, argv, arg_ptr); } bool @@ -3278,11 +3277,10 @@ check_path_safety (const char *action, char **argv) /* handles both exec and ok predicate */ static bool -new_insert_exec_ok (const char *action, - const struct parser_table *entry, - int dir_fd, - char **argv, - int *arg_ptr) +insert_exec_ok (const char *action, + const struct parser_table *entry, + char **argv, + int *arg_ptr) { int start, end; /* Indexes in ARGV of start & end of cmd. */ int i; /* Index into cmd args */ @@ -3303,6 +3301,7 @@ new_insert_exec_ok (const char *action, our_pred->need_type = our_pred->need_stat = false; execp = &our_pred->args.exec_vec; + execp->wd_for_exec = NULL; if ((func != pred_okdir) && (func != pred_ok)) { @@ -3322,13 +3321,14 @@ new_insert_exec_ok (const char *action, if ((func == pred_execdir) || (func == pred_okdir)) { + execp->wd_for_exec = NULL; options.ignore_readdir_race = false; check_path_safety (action, argv); - execp->use_current_dir = true; } else { - execp->use_current_dir = false; + assert (NULL != initial_wd); + execp->wd_for_exec = initial_wd; } our_pred->args.exec_vec.multiple = 0; @@ -3482,18 +3482,6 @@ new_insert_exec_ok (const char *action, -static bool -insert_exec_ok (const char *action, - const struct parser_table *entry, - int dir_fd, - char **argv, - int *arg_ptr) -{ - return new_insert_exec_ok (action, entry, dir_fd, argv, arg_ptr); -} - - - /* Get a timestamp and comparison type. STR is the ASCII representation. diff --git a/find/pred.c b/find/pred.c index 1e0e891..4cd70ee 100644 --- a/find/pred.c +++ b/find/pred.c @@ -48,6 +48,8 @@ #include "verify.h" #include "fdleak.h" #include "areadlink.h" +#include "cloexec.h" +#include "save-cwd.h" #include <selinux/selinux.h> @@ -504,8 +506,31 @@ pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ return (false); } + +static bool +record_exec_dir (struct exec_val *execp) +{ + if (!execp->wd_for_exec) + { + /* working directory not already known, so must be a *dir variant, + and this must be the first arg we added. However, this may + be -execdir foo {} \; (i.e. not multiple). */ + assert (!execp->state.todo); + + /* Record the WD. */ + execp->wd_for_exec = xmalloc (sizeof (*execp->wd_for_exec)); + execp->wd_for_exec->name = NULL; + execp->wd_for_exec->desc = openat (state.cwd_dir_fd, ".", O_RDONLY); + if (execp->wd_for_exec->desc < 0) + return false; + set_cloexec_flag (execp->wd_for_exec->desc, true); + } + return true; +} + + static bool -new_impl_pred_exec (int dir_fd, const char *pathname, +new_impl_pred_exec (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, const char *prefix, size_t pfxlen) @@ -514,7 +539,32 @@ new_impl_pred_exec (int dir_fd, const char *pathname, size_t len = strlen (pathname); (void) stat_buf; - execp->dir_fd = dir_fd; + + if (is_exec_in_local_dir (pred_ptr->pred_func)) + { + /* For -execdir/-okdir predicates, the parser did not fill in + the wd_for_exec member of sturct exec_val. So for those + predicates, we do so now. + */ + if (!record_exec_dir (execp)) + { + error (EXIT_FAILURE, errno, + _("Failed to save working directory in order to " + "run a command on %s"), + safely_quote_err_filename (0, pathname)); + /*NOTREACHED*/ + } + } + else + { + /* For the others (-exec, -ok), the parder should + have set wd_for_exec to initial_wd, indicating + that the exec should take place from find's initial + working directory. + */ + assert (execp->wd_for_exec == initial_wd); + } + if (execp->multiple) { /* Push the argument onto the current list. @@ -570,8 +620,7 @@ new_impl_pred_exec (int dir_fd, const char *pathname, bool pred_exec (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) { - return new_impl_pred_exec (get_start_dirfd(), - pathname, stat_buf, pred_ptr, NULL, 0); + return new_impl_pred_exec (pathname, stat_buf, pred_ptr, NULL, 0); } bool @@ -579,8 +628,7 @@ pred_execdir (const char *pathname, struct stat *stat_buf, struct predicate *pre { const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./"; (void) &pathname; - return new_impl_pred_exec (get_current_dirfd (), - state.rel_pathname, stat_buf, pred_ptr, + return new_impl_pred_exec (state.rel_pathname, stat_buf, pred_ptr, prefix, (prefix ? 2 : 0)); } @@ -1482,8 +1530,7 @@ bool pred_ok (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) { if (is_ok (pred_ptr->args.exec_vec.replace_vec[0], pathname)) - return new_impl_pred_exec (get_start_dirfd (), - pathname, stat_buf, pred_ptr, NULL, 0); + return new_impl_pred_exec (pathname, stat_buf, pred_ptr, NULL, 0); else return false; } @@ -1493,8 +1540,7 @@ pred_okdir (const char *pathname, struct stat *stat_buf, struct predicate *pred_ { const char *prefix = (state.rel_pathname[0] == '/') ? NULL : "./"; if (is_ok (pred_ptr->args.exec_vec.replace_vec[0], pathname)) - return new_impl_pred_exec (get_current_dirfd (), - state.rel_pathname, stat_buf, pred_ptr, + return new_impl_pred_exec (state.rel_pathname, stat_buf, pred_ptr, prefix, (prefix ? 2 : 0)); else return false; @@ -1595,7 +1641,7 @@ can_access (int access_type) args.filename = state.rel_pathname; args.access_type = access_type; args.cb_errno = 0; - return 0 == run_in_dir (state.cwd_dir_fd, access_callback, &args); + return 0 == run_in_dirfd (state.cwd_dir_fd, access_callback, &args); } @@ -1955,7 +2001,7 @@ pred_context (const char *pathname, struct stat *stat_buf, static bool -prep_child_for_exec (bool close_stdin, int dir_fd) +prep_child_for_exec (bool close_stdin, const struct saved_cwd *wd) { bool ok = true; if (close_stdin) @@ -1991,17 +2037,10 @@ prep_child_for_exec (bool close_stdin, int dir_fd) * announcement of a call to stat() anyway, as we're about to exec * something. */ - if (dir_fd != AT_FDCWD) + if (0 != restore_cwd (wd)) { - assert (dir_fd >= 0); - if (0 != fchdir (dir_fd)) - { - /* If we cannot execute our command in the correct directory, - * we should not execute it at all. - */ - error (0, errno, _("Failed to change directory")); - ok = false; - } + error (0, errno, _("Failed to change directory")); + ok = false; } return ok; } @@ -2018,12 +2057,6 @@ launch (struct buildcmd_control *ctl, void *usercontext, int argc, char **argv) static int first_time = 1; struct exec_val *execp = usercontext; - if (!execp->use_current_dir) - { - assert (starting_desc >= 0); - assert (execp->dir_fd == starting_desc); - } - /* Make sure output of command doesn't get mixed with find output. */ fflush (stdout); fflush (stderr); @@ -2041,8 +2074,8 @@ launch (struct buildcmd_control *ctl, void *usercontext, int argc, char **argv) if (child_pid == 0) { /* We are the child. */ - assert (starting_desc >= 0); - if (!prep_child_for_exec (execp->close_stdin, execp->dir_fd)) + assert (NULL != execp->wd_for_exec); + if (!prep_child_for_exec (execp->close_stdin, execp->wd_for_exec)) { _exit (1); } diff --git a/find/util.c b/find/util.c index 880e99a..8e9e55f 100644 --- a/find/util.c +++ b/find/util.c @@ -37,6 +37,9 @@ #include "error.h" #include "verify.h" #include "fdleak.h" +#include "dircallback.h" +#include "xalloc.h" +#include "save-cwd.h" #if ENABLE_NLS @@ -334,6 +337,26 @@ check_nofollow (void) #endif +static int +exec_cb (void *context) +{ + struct exec_val *execp = context; + bc_do_exec (&execp->ctl, &execp->state); + return 0; +} + +static void +do_exec (struct exec_val *execp) +{ + run_in_dir (execp->wd_for_exec, exec_cb, execp); + if (execp->wd_for_exec != initial_wd) + { + free_cwd (execp->wd_for_exec); + free (execp->wd_for_exec); + execp->wd_for_exec = NULL; + } +} + /* Examine the predicate list for instances of -execdir or -okdir * which have been terminated with '+' (build argument list) rather @@ -341,14 +364,14 @@ check_nofollow (void) * have no effect if there are no arguments waiting). */ static void -do_complete_pending_execdirs (struct predicate *p, int dir_fd) +do_complete_pending_execdirs (struct predicate *p) { if (NULL == p) return; assert (state.execdirs_outstanding); - do_complete_pending_execdirs (p->pred_left, dir_fd); + do_complete_pending_execdirs (p->pred_left); if (pred_is (p, pred_execdir) || pred_is(p, pred_okdir)) { @@ -363,20 +386,20 @@ do_complete_pending_execdirs (struct predicate *p, int dir_fd) if (execp->state.todo) { /* There are not-yet-executed arguments. */ - bc_do_exec (&execp->ctl, &execp->state); + do_exec (execp); } } } - do_complete_pending_execdirs (p->pred_right, dir_fd); + do_complete_pending_execdirs (p->pred_right); } void -complete_pending_execdirs (int dir_fd) +complete_pending_execdirs (void) { if (state.execdirs_outstanding) { - do_complete_pending_execdirs (get_eval_tree(), dir_fd); + do_complete_pending_execdirs (get_eval_tree()); state.execdirs_outstanding = false; } } @@ -418,6 +441,36 @@ complete_pending_execs (struct predicate *p) complete_pending_execs (p->pred_right); } +void +record_initial_cwd (void) +{ + initial_wd = xmalloc (sizeof (*initial_wd)); + if (0 != save_cwd (initial_wd)) + { + error (EXIT_FAILURE, errno, + _("failed to save initial working directory")); + } +} + +static void +cleanup_initial_cwd (void) +{ + if (0 == restore_cwd (initial_wd)) + { + free_cwd (initial_wd); + free (initial_wd); + initial_wd = NULL; + } + else + { + /* Dont try to exit on failure, since we may already be in + atexit. */ + error (0, errno, + _("failed to restore initial working directory")); + } +} + + static void traverse_tree (struct predicate *tree, void (*callback)(struct predicate*)) @@ -470,7 +523,7 @@ cleanup (void) if (eval_tree) { traverse_tree (eval_tree, complete_pending_execs); - complete_pending_execdirs (get_current_dirfd()); + complete_pending_execdirs (); } /* Close ouptut files and NULL out references to them. */ @@ -478,6 +531,8 @@ cleanup (void) if (eval_tree) traverse_tree (eval_tree, undangle_file_pointers); + cleanup_initial_cwd (); + if (fd_leak_check_is_enabled ()) { complain_about_leaky_fds (); @@ -1028,16 +1083,6 @@ set_option_defaults (struct options *p) } -/* get_start_dirfd - * - * Returns the fd for the directory we started in. - */ -int -get_start_dirfd (void) -{ - return starting_desc; -} - /* apply_predicate * */ @@ -1064,6 +1109,15 @@ apply_predicate(const char *pathname, struct stat *stat_buf, struct predicate *p } +/* is_exec_in_local_dir + * + */ +bool +is_exec_in_local_dir (const PRED_FUNC pred_func) +{ + return pred_execdir == pred_func || pred_okdir == pred_func; +} + /* safely_quote_err_filename * */ diff --git a/import-gnulib.config b/import-gnulib.config index e0010bb..9307145 100644 --- a/import-gnulib.config +++ b/import-gnulib.config @@ -112,6 +112,7 @@ quotearg realloc regex rpmatch +save-cwd savedir selinux-at snprintf diff --git a/lib/dircallback.c b/lib/dircallback.c index dda89fc..e889ed0 100644 --- a/lib/dircallback.c +++ b/lib/dircallback.c @@ -34,7 +34,36 @@ int -run_in_dir (int dir_fd, int (*callback)(void*), void *usercontext) +run_in_dir (const struct saved_cwd *there, + int (*callback)(void*), void *usercontext) +{ + int err, saved_errno; + struct saved_cwd here; + if (0 == save_cwd (&here)) + { + if (0 == restore_cwd (there)) + { + err = (*callback)(usercontext); + saved_errno = (err < 0 ? errno : 0); + } + else + { + openat_restore_fail (errno); + } + free_cwd (&here); + } + else + { + openat_save_fail (errno); + } + if (saved_errno) + errno = saved_errno; + return err; +} + + +int +run_in_dirfd (int dir_fd, int (*callback)(void*), void *usercontext) { if (dir_fd == AT_FDCWD) { diff --git a/lib/dircallback.h b/lib/dircallback.h index b9c3dee..d2f74c4 100644 --- a/lib/dircallback.h +++ b/lib/dircallback.h @@ -19,6 +19,9 @@ #if !defined DIRCALLBACK_H # define DIRCALLBACK_H -int run_in_dir (int dir_fd, int (*callback)(void*), void *usercontext); +struct saved_cwd; + +int run_in_dirfd (int fd, int (*callback)(void*), void *usercontext); +int run_in_dir (struct saved_cwd*, int (*callback)(void*), void *usercontext); #endif -- 1.7.0
