From a2f7afcd9d4942323ca46cc2c3c048188b4d8092 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 18 May 2010 12:58:34 +0200 Subject: [PATCH 2/5] 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 function 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 function; true for predicates -execdir and -okdir. * find/pred.c: Include 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 declaration of global variable. (starting_desc): Remove declaration of global variable. * import-gnulib.config (modules): Import module save-cwd. --- ChangeLog | 83 +++++++++++++++++++++++++++++++++++++++++++++ find/defs.h | 14 ++++---- find/find.c | 69 +++++-------------------------------- find/finddata.c | 12 +----- find/ftsfind.c | 49 ++------------------------- find/parser.c | 35 ++++++------------ find/pred.c | 92 +++++++++++++++++++++++++++++++++----------------- find/util.c | 89 +++++++++++++++++++++++++++++++++++++++--------- import-gnulib.config | 1 + lib/dircallback.c | 36 +++++++++++++++++++- lib/dircallback.h | 5 ++- lib/listfile.c | 2 +- 12 files changed, 291 insertions(+), 196 deletions(-) diff --git a/ChangeLog b/ChangeLog index 460ec1c..e0a4573 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,88 @@ 2010-04-10 James Youngman + 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 function 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 function; true for predicates -execdir + and -okdir. + * find/pred.c: Include 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 declaration of global variable. + (starting_desc): Remove declaration 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 working diff --git a/find/defs.h b/find/defs.h index 4539fd9..6115b3c 100644 --- a/find/defs.h +++ b/find/defs.h @@ -195,9 +195,8 @@ struct exec_val struct buildcmd_state state; char **replace_vec; /* Command arguments (for ";" style) */ int num_args; - boolean use_current_dir; /* If nonzero, don't chdir to start dir */ boolean 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. */ }; /* The format string for a -printf or -fprintf is chopped into one or @@ -335,8 +334,6 @@ struct predicate /* find.c, ftsfind.c */ boolean is_fts_enabled(int *ftsoptions); -int get_start_dirfd(void); -int get_current_dirfd(void); /* find library function declarations. */ @@ -493,8 +490,11 @@ struct predicate *insert_primary_withpred PARAMS((const struct parser_table *ent void usage PARAMS((FILE *fp, int status, char *msg)); extern boolean 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); +boolean is_exec_in_local_dir(const PRED_FUNC pred_func); + void fatal_file_error(const char *name) ATTRIBUTE_NORETURN; void nonfatal_file_error(const char *name); @@ -654,10 +654,10 @@ struct state }; /* finddata.c */ +extern struct options options; extern struct state state; -extern char const *starting_dir; -extern int starting_desc; extern char *program_name; +extern struct saved_cwd *initial_wd; #endif diff --git a/find/find.c b/find/find.c index 171988f..badcf3c 100644 --- a/find/find.c +++ b/find/find.c @@ -52,6 +52,7 @@ #include "quotearg.h" #include "xgetcwd.h" #include "error.h" +#include "save-cwd.h" #ifdef HAVE_LOCALE_H #include @@ -131,6 +132,8 @@ main (int argc, char **argv) program_name = argv[0]; state.exit_status = 0; + record_initial_cwd (); + /* Set the option defaults before we do the locale * initialisation as check_nofollow() needs to be executed in the * POSIX locale. @@ -183,23 +186,6 @@ main (int argc, char **argv) } - starting_desc = open (".", 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 (1, errno, _("cannot get current directory")); - } set_stat_placeholders(&starting_stat_buf); if ((*options.xstat) (".", &starting_stat_buf) != 0) error (1, errno, _("cannot stat current directory")); @@ -876,7 +862,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; @@ -911,45 +897,10 @@ safely_chdir(const char *dest, static void chdir_back (void) { - struct stat stat_buf; - boolean 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_file_error(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()\n"); + if (options.debug_options & DebugSearch) + fprintf (stderr, "chdir_back(): chdir to start point\n"); - if (fchdir (starting_desc) != 0) - { - fatal_file_error(starting_dir); - } - } + restore_cwd (initial_wd); } /* Move to the parent of a given directory and then call a function, @@ -1038,7 +989,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 do_process_predicate(char *pathname, @@ -1326,7 +1277,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, ".")) { @@ -1464,7 +1415,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 373eb38..dcc8261 100644 --- a/find/finddata.c +++ b/find/finddata.c @@ -19,6 +19,7 @@ #include #include "defs.h" +#include "save-cwd.h" /* Name this program was run with. */ @@ -26,13 +27,4 @@ char *program_name; 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 b59d896..cc54c9f 100644 --- a/find/ftsfind.c +++ b/find/ftsfind.c @@ -98,24 +98,6 @@ static int ftsoptions = FTS_NOSTAT|FTS_TIGHT_CYCLE_CHECK; static int prev_depth = INT_MIN; /* fts_level can be < 0 */ static int curr_fd = -1; -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) { if (ftsoptions & FTS_CWDFD) @@ -324,15 +306,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) { @@ -560,7 +533,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) @@ -664,6 +637,8 @@ main (int argc, char **argv) state.execdirs_outstanding = false; state.cwd_dir_fd = AT_FDCWD; + record_initial_cwd (); + /* Set the option defaults before we do the locale initialisation as * check_nofollow() needs to be executed in the POSIX locale. */ @@ -713,24 +688,6 @@ main (int argc, char **argv) } - starting_desc = open (".", 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 (1, errno, _("cannot get current directory")); - } - - process_all_startpoints(argc-end_of_leading_options, argv+end_of_leading_options); /* If "-exec ... {} +" has been used, there may be some diff --git a/find/parser.c b/find/parser.c index 08758ee..4344c56 100644 --- a/find/parser.c +++ b/find/parser.c @@ -177,7 +177,6 @@ static struct segment **make_segment PARAMS((struct segment **segment, struct predicate *pred)); static boolean insert_exec_ok PARAMS((const char *action, const struct parser_table *entry, - int dir_fd, char *argv[], int *arg_ptr)); static boolean get_comp_type PARAMS((const char **str, @@ -844,13 +843,13 @@ parse_empty (const struct parser_table* entry, char **argv, int *arg_ptr) static boolean 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 boolean 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 boolean @@ -1734,13 +1733,13 @@ parse_nowarn (const struct parser_table* entry, char **argv, int *arg_ptr) static boolean 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 boolean 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); } boolean @@ -3083,11 +3082,10 @@ check_path_safety(const char *action, char **argv) /* handles both exec and ok predicate */ static boolean -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 */ @@ -3108,6 +3106,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)) { @@ -3127,13 +3126,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; @@ -3286,17 +3286,6 @@ new_insert_exec_ok (const char *action, -static boolean -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. diff --git a/find/pred.c b/find/pred.c index b1f48a0..3d74f7a 100644 --- a/find/pred.c +++ b/find/pred.c @@ -47,6 +47,7 @@ #include "dircallback.h" #include "error.h" #include "verify.h" +#include "save-cwd.h" #if ENABLE_NLS # include @@ -499,8 +500,30 @@ pred_empty (const char *pathname, struct stat *stat_buf, struct predicate *pred_ return (false); } + static boolean -new_impl_pred_exec (int dir_fd, const char *pathname, +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; + } + return true; +} + + +static boolean +new_impl_pred_exec (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr, const char *prefix, size_t pfxlen) @@ -509,7 +532,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 parser 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. @@ -555,8 +603,7 @@ new_impl_pred_exec (int dir_fd, const char *pathname, boolean 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); } boolean @@ -564,8 +611,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)); } @@ -1433,8 +1479,7 @@ boolean 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; } @@ -1444,8 +1489,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; @@ -1546,7 +1590,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); } @@ -1865,7 +1909,7 @@ pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ static boolean -prep_child_for_exec (boolean close_stdin, int dir_fd) +prep_child_for_exec (boolean close_stdin, const struct saved_cwd *wd) { boolean ok = true; if (close_stdin) @@ -1901,17 +1945,10 @@ prep_child_for_exec (boolean 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; } @@ -1927,13 +1964,6 @@ launch (const struct buildcmd_control *ctl, static int first_time = 1; const struct exec_val *execp = buildstate->usercontext; - if (!execp->use_current_dir) - { - assert (starting_desc >= 0); - assert (execp->dir_fd == starting_desc); - } - - /* Null terminate the arg list. */ bc_push_arg (ctl, buildstate, (char *) NULL, 0, NULL, 0, false); @@ -1954,8 +1984,8 @@ launch (const struct buildcmd_control *ctl, 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 cc9a3eb..56bf488 100644 --- a/find/util.c +++ b/find/util.c @@ -36,6 +36,9 @@ #include "error.h" #include "verify.h" #include "openat.h" +#include "dircallback.h" +#include "xalloc.h" +#include "save-cwd.h" #if ENABLE_NLS # include @@ -290,6 +293,26 @@ check_nofollow(void) #endif +static int +exec_cb (void *context) +{ + struct exec_val *execp = context; + launch (&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 @@ -297,14 +320,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)) { @@ -319,25 +342,24 @@ do_complete_pending_execdirs(struct predicate *p, int dir_fd) if (execp->state.todo) { /* There are not-yet-executed arguments. */ - launch (&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; } } - /* Examine the predicate list for instances of -exec which have been * terminated with '+' (build argument list) rather than ';' (singles @@ -373,6 +395,37 @@ 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 + { + /* since we may already be in atexit, die with _exit(). */ + error (0, errno, + _("failed to restore initial working directory")); + _exit (EXIT_FAILURE); + } +} + + static void traverse_tree(struct predicate *tree, @@ -432,9 +485,11 @@ cleanup(void) if (eval_tree) { traverse_tree(eval_tree, complete_pending_execs); - complete_pending_execdirs(get_current_dirfd()); + complete_pending_execdirs (); traverse_tree(eval_tree, flush_and_close_output_files); } + + cleanup_initial_cwd (); } /* Savannah bug #16378 manifests as an assertion failure in pred_type() @@ -970,15 +1025,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 * */ @@ -1004,6 +1050,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 f2e8998..b1f0851 100644 --- a/import-gnulib.config +++ b/import-gnulib.config @@ -67,6 +67,7 @@ quotearg realloc regex rpmatch +save-cwd savedir stat-macros stat-time diff --git a/lib/dircallback.c b/lib/dircallback.c index 5dbf3b3..f96fccc 100644 --- a/lib/dircallback.c +++ b/lib/dircallback.c @@ -54,7 +54,41 @@ 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 = -1; + int saved_errno = 0; + 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); + } + + if (restore_cwd (&here) != 0) + 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 41ea282..3234113 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 diff --git a/lib/listfile.c b/lib/listfile.c index ca9eae2..b5bee54 100644 --- a/lib/listfile.c +++ b/lib/listfile.c @@ -424,7 +424,7 @@ get_link_name_at (const char *name, int dir_fd, char *relname) args.result = NULL; args.name = name; args.relname = relname; - if (0 == run_in_dir(dir_fd, get_link_name_cb, &args)) + if (0 == run_in_dirfd (dir_fd, get_link_name_cb, &args)) return args.result; else return NULL; -- 1.6.6.1