From 3db581bc7941adfdb0732b18fdfb651b8ab104af Mon Sep 17 00:00:00 2001
From: James Youngman <jay@gnu.org>
Date: Sun, 11 Apr 2010 14:38:51 +0100
Subject: [PATCH] Fix Savannah bug 27563 (-L breaks -execdir).
To: findutils-patches@gnu.org

* import-gnulib.config (modules): Import the cloexec modile.
* find/pred.c: Include "cloexec.h".
(new_impl_pred_exec): Split the relative pathname into a directory
and a base part.  If the predicate is -execdir or -okdir, then
open the directory name and use the resulting fd to set
execp->dir_fd if it's not already set.  Pass the base part
as the target filename.  On the other hand for -exec and -ok, use
starting_desc and pathname.
* find/parser.c (new_insert_exec_ok): Initialise execp->dir_fd.
* find/testsuite/Makefile.am (EXTRA_DIST_EXP): Correct test script
name: find.gnu/sv-bug-27563-execdir.exp, not
find.gnu/sv-bug-25359-execdir.exp.
* find/testsuite/Makefile.am (EXTRA_DIST_EXP): Add
find.gnu/sv-bug-27563-execdir.exp and
find.posix/sv-bug-27563-exec.exp.
(EXTRA_DIST_XO): Add find.gnu/sv-bug-27563-execdir.xo and
find.posix/sv-bug-27563-exec.xo.
* find/testsuite/find.gnu/sv-bug-27563-execdir.exp: New test.
* find/testsuite/find.posix/sv-bug-27563-exec.exp: New test.
* find/testsuite/find.gnu/sv-bug-27563-execdir.xo: Expected output.
* find/testsuite/find.posix/sv-bug-27563-exec.xo: Expected output.
* NEWS: Mention the bugfix.

Signed-off-by: James Youngman <jay@gnu.org>
---
 ChangeLog                                        |   26 ++++++++++++
 NEWS                                             |    3 +
 find/parser.c                                    |    1 +
 find/pred.c                                      |   48 ++++++++++++++++++----
 find/testsuite/Makefile.am                       |    4 ++
 find/testsuite/find.gnu/sv-bug-27563-execdir.exp |    6 +++
 find/testsuite/find.gnu/sv-bug-27563-execdir.xo  |    1 +
 find/testsuite/find.posix/sv-bug-27563-exec.exp  |    6 +++
 find/testsuite/find.posix/sv-bug-27563-exec.xo   |    1 +
 import-gnulib.config                             |    1 +
 10 files changed, 89 insertions(+), 8 deletions(-)
 create mode 100644 find/testsuite/find.gnu/sv-bug-27563-execdir.exp
 create mode 100644 find/testsuite/find.gnu/sv-bug-27563-execdir.xo
 create mode 100644 find/testsuite/find.posix/sv-bug-27563-exec.exp
 create mode 100644 find/testsuite/find.posix/sv-bug-27563-exec.xo

diff --git a/ChangeLog b/ChangeLog
index 3c9f62e..e8259b1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2010-04-11  James Youngman  <jay@gnu.org>
+
+	Fix Savannah bug 27563 (-L breaks -execdir).
+	* import-gnulib.config (modules): Import the cloexec modile.
+	* find/pred.c: Include "cloexec.h".
+	(new_impl_pred_exec): Split the relative pathname into a directory
+	and a base part.  If the predicate is -execdir or -okdir, then
+	open the directory name and use the resulting fd to set
+	execp->dir_fd if it's not already set.  Pass the base part
+	as the target filename.  On the other hand for -exec and -ok, use
+	starting_desc and pathname.
+	* find/parser.c (new_insert_exec_ok): Initialise execp->dir_fd.
+	* find/testsuite/Makefile.am (EXTRA_DIST_EXP): Correct test script
+	name: find.gnu/sv-bug-27563-execdir.exp, not
+	find.gnu/sv-bug-25359-execdir.exp.
+	* find/testsuite/Makefile.am (EXTRA_DIST_EXP): Add
+	find.gnu/sv-bug-27563-execdir.exp and
+	find.posix/sv-bug-27563-exec.exp.
+	(EXTRA_DIST_XO): Add find.gnu/sv-bug-27563-execdir.xo and
+	find.posix/sv-bug-27563-exec.xo.
+	* find/testsuite/find.gnu/sv-bug-27563-execdir.exp: New test.
+	* find/testsuite/find.posix/sv-bug-27563-exec.exp: New test.
+	* find/testsuite/find.gnu/sv-bug-27563-execdir.xo: Expected output.
+	* find/testsuite/find.posix/sv-bug-27563-exec.xo: Expected output.
+	* NEWS: Mention the bugfix.
+
 2010-04-06  James Youngman  <jay@gnu.org>
 
 	* po/nl.po: Updated Dutch translation.
diff --git a/NEWS b/NEWS
index 853fade..f3010b2 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,9 @@ GNU findutils NEWS - User visible changes.	-*- outline -*- (allout)
 #28824: Corrected error message for "-ctime x".
         Likewise for -gid, -inum, -links, -mmin, -cmin, -amin,
 	-uid, -used, -atime, -mtime, -ctime.
+
+#27563: -L breaks -execdir
+
 #27017: find -D opt / -fstype ext3 -print , -quit coredumps
 
 ** Translations
diff --git a/find/parser.c b/find/parser.c
index 08758ee..057a338 100644
--- a/find/parser.c
+++ b/find/parser.c
@@ -3109,6 +3109,7 @@ new_insert_exec_ok (const char *action,
 
   execp = &our_pred->args.exec_vec;
 
+  execp->dir_fd = -1;
   if ((func != pred_okdir) && (func != pred_ok))
     {
       allow_plus = true;
diff --git a/find/pred.c b/find/pred.c
index b1f48a0..8888004 100644
--- a/find/pred.c
+++ b/find/pred.c
@@ -47,6 +47,7 @@
 #include "dircallback.h"
 #include "error.h"
 #include "verify.h"
+#include "cloexec.h"
 
 #if ENABLE_NLS
 # include <libintl.h>
@@ -505,11 +506,41 @@ new_impl_pred_exec (int dir_fd, const char *pathname,
 		    struct predicate *pred_ptr,
 		    const char *prefix, size_t pfxlen)
 {
+  size_t len;
   struct exec_val *execp = &pred_ptr->args.exec_vec;
-  size_t len = strlen(pathname);
-
+  const char *target=pathname;
+  char *base = NULL;
+  boolean result;
+  const bool local = (pred_ptr->pred_func == pred_execdir
+		      || pred_ptr->pred_func == pred_okdir);
   (void) stat_buf;
-  execp->dir_fd = dir_fd;
+  if (execp->dir_fd < 0)
+    {
+      if (local)
+	{
+	  char *dir = dir_name (state.rel_pathname);
+	  execp->dir_fd = openat (state.cwd_dir_fd, dir, O_RDONLY
+#if defined O_LARGEFILE
+				  |O_LARGEFILE
+#endif
+				  );
+	  if (execp->dir_fd < 0)
+	    error (1, errno, _("Failed to open directory %s"), dir);
+	  set_cloexec_flag (execp->dir_fd, true);
+	  free (dir);
+	}
+      else
+	{
+	  execp->dir_fd = starting_desc;
+	}
+    }
+
+  if (local)
+    target = base = base_name (state.rel_pathname);
+  else
+    target = pathname;
+  len = strlen (target);
+
   if (execp->multiple)
     {
       /* Push the argument onto the current list.
@@ -518,7 +549,7 @@ new_impl_pred_exec (int dir_fd, const char *pathname,
        */
       bc_push_arg(&execp->ctl,
 		  &execp->state,
-		  pathname, len+1,
+		  target, len+1,
 		  prefix, pfxlen,
 		  0);
 
@@ -528,7 +559,7 @@ new_impl_pred_exec (int dir_fd, const char *pathname,
       /* POSIX: If the primary expression is punctuated by a plus
        * sign, the primary shall always evaluate as true
        */
-      return true;
+      result = true;
     }
   else
     {
@@ -541,14 +572,16 @@ new_impl_pred_exec (int dir_fd, const char *pathname,
 		       execp->replace_vec[i],
 		       strlen(execp->replace_vec[i]),
 		       prefix, pfxlen,
-		       pathname, len,
+		       target, len,
 		       0);
 	}
 
       /* Actually invoke the command. */
-      return  execp->ctl.exec_callback(&execp->ctl,
+      result = execp->ctl.exec_callback(&execp->ctl,
 					&execp->state);
     }
+  free (base);
+  return result;
 }
 
 
@@ -1930,7 +1963,6 @@ launch (const struct buildcmd_control *ctl,
   if (!execp->use_current_dir)
     {
       assert (starting_desc >= 0);
-      assert (execp->dir_fd == starting_desc);
     }
 
 
diff --git a/find/testsuite/Makefile.am b/find/testsuite/Makefile.am
index dca30e6..c48965e 100644
--- a/find/testsuite/Makefile.am
+++ b/find/testsuite/Makefile.am
@@ -62,6 +62,7 @@ find.gnu/samefile-same.xo \
 find.gnu/samefile-symlink.xo \
 find.gnu/sv-bug-17782.xo \
 find.gnu/sv-bug-18222.xo \
+find.gnu/sv-bug-27563-execdir.xo \
 find.gnu/true.xo \
 find.gnu/wholename.xo \
 find.gnu/xtype-symlink.xo \
@@ -77,6 +78,7 @@ find.posix/grouping.xo \
 find.posix/links.xo \
 find.posix/sv-bug-11175.xo \
 find.posix/sv-bug-12181.xo \
+find.posix/sv-bug-27563-exec.xo \
 find.posix/depth1.xo \
 find.posix/mtime0.xo \
 find.posix/sizes.xo \
@@ -176,6 +178,7 @@ find.gnu/sv-bug-17490.exp \
 find.gnu/sv-bug-17782.exp \
 find.gnu/sv-bug-18222.exp \
 find.gnu/sv-bug-24169.exp \
+find.gnu/sv-bug-27563-execdir.exp \
 find.gnu/quit.exp \
 find.gnu/used-invarg.exp \
 find.gnu/used-missing.exp \
@@ -196,6 +199,7 @@ find.posix/links.exp \
 find.posix/mtime0.exp \
 find.posix/sv-bug-11175.exp \
 find.posix/sv-bug-12181.exp \
+find.posix/sv-bug-27563-exec.exp \
 find.posix/depth1.exp \
 find.posix/sizes.exp \
 find.posix/name.exp \
diff --git a/find/testsuite/find.gnu/sv-bug-27563-execdir.exp b/find/testsuite/find.gnu/sv-bug-27563-execdir.exp
new file mode 100644
index 0000000..c67fc88
--- /dev/null
+++ b/find/testsuite/find.gnu/sv-bug-27563-execdir.exp
@@ -0,0 +1,6 @@
+# tests for Savannah bug 27563 (result of find -L -exec ls {} \;)
+exec rm -rf tmp
+exec mkdir tmp
+exec touch tmp/yyyy
+find_start p {-L tmp -name yyyy -execdir ls \{\} \; }
+exec rm -rf tmp
diff --git a/find/testsuite/find.gnu/sv-bug-27563-execdir.xo b/find/testsuite/find.gnu/sv-bug-27563-execdir.xo
new file mode 100644
index 0000000..285260b
--- /dev/null
+++ b/find/testsuite/find.gnu/sv-bug-27563-execdir.xo
@@ -0,0 +1 @@
+./yyyy
diff --git a/find/testsuite/find.posix/sv-bug-27563-exec.exp b/find/testsuite/find.posix/sv-bug-27563-exec.exp
new file mode 100644
index 0000000..d18b0c1
--- /dev/null
+++ b/find/testsuite/find.posix/sv-bug-27563-exec.exp
@@ -0,0 +1,6 @@
+# tests for Savannah bug 27563 (result of find -L -exec ls {} \;)
+exec rm -rf tmp
+exec mkdir tmp
+exec touch tmp/yyyy
+find_start p {-L tmp -name yyyy -exec ls \{\} \; }
+exec rm -rf tmp
diff --git a/find/testsuite/find.posix/sv-bug-27563-exec.xo b/find/testsuite/find.posix/sv-bug-27563-exec.xo
new file mode 100644
index 0000000..cd491dd
--- /dev/null
+++ b/find/testsuite/find.posix/sv-bug-27563-exec.xo
@@ -0,0 +1 @@
+tmp/yyyy
diff --git a/import-gnulib.config b/import-gnulib.config
index f2e8998..f032eca 100644
--- a/import-gnulib.config
+++ b/import-gnulib.config
@@ -32,6 +32,7 @@ argmatch
 assert
 c-strstr
 canonicalize
+cloexec
 closein
 closeout
 dirname
-- 
1.7.0

