bug-findutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 2/4] Exec predicates now store which directory they want to run i


From: James Youngman
Subject: [PATCH 2/4] Exec predicates now store which directory they want to run in.
Date: Sat, 10 Apr 2010 21:33:04 +0100

* 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 <address@hidden>
---
 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  <address@hidden>
 
+        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





reply via email to

[Prev in Thread] Current Thread [Next in Thread]