Patchwork posix: if glob has a trailing slash match directories only.

login
register
mail settings
Submitter Dmitry Goncharov
Date Nov. 30, 2017, 5:01 a.m.
Message ID <20171130050118.GA21476@madrid>
Download mbox | patch
Permalink /patch/24625/
State New
Headers show

Comments

Dmitry Goncharov - Nov. 30, 2017, 5:01 a.m.
On Tue, Nov 28, 2017 at 02:04:53PM -0800, Jonathan Nieder wrote:
> 
> What should happen in the DT_LNK case?  Should the same logic trip for
> it as well so we can distinguish between a symlink to a directory and
> other symlinks?
> 

The filesystems that i tested on all return DT_UNKNOWN for a symlink or
hardlink. Even when they return proper type for a file or directory.
However, if the filesystem ever sets type to DT_LNK glob needs to treat it like
DT_UNKNOWN.

regards, Dmitry

Patch

diff --git a/ChangeLog b/ChangeLog
index ffeb208132..fb76fc1415 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@ 
+2017-11-28  Dmitry Goncharov  <dgoncharov@users.sf.net>
+
+	[BZ #22513]
+	* posix/glob.c (glob_in_dir): Make pattern with a trailing slash
+	match directores only.
+	* posix/globtest.sh: Add tests.
+
 2017-11-13  Florian Weimer  <fweimer@redhat.com>
 
 	* support/next_to_fault.h, support/next_to_fault.c: New files.
diff --git a/posix/glob.c b/posix/glob.c
index cb39779d07..47e67907cd 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -1342,7 +1342,33 @@  glob_in_dir (const char *pattern, const char *directory, int flags,
 	      if (flags & GLOB_ONLYDIR)
 		switch (readdir_result_type (d))
 		  {
-		  case DT_DIR: case DT_LNK: case DT_UNKNOWN: break;
+		  case DT_DIR: break;
+		  case DT_LNK: case DT_UNKNOWN:
+		  {
+		    int dir;
+		    size_t namlen = strlen (d.name);
+		    size_t fullsize;
+		    bool alloca_fullname
+		      = (! size_add_wrapv (dirlen + 1, namlen + 1, &fullsize)
+			 && glob_use_alloca (alloca_used, fullsize));
+		    char *fullname;
+		    if (alloca_fullname)
+		      fullname = alloca_account (fullsize, alloca_used);
+		    else
+		      {
+			fullname = malloc (fullsize);
+			if (fullname == NULL)
+			  return GLOB_NOSPACE;
+		      }
+		    mempcpy (mempcpy (mempcpy (fullname, directory, dirlen),
+				      "/", 1),
+			     d.name, namlen + 1);
+		    dir = is_dir (fullname, flags, pglob);
+		    if (__glibc_unlikely (!alloca_fullname))
+		      free (fullname);
+		    if (dir)
+		      break;
+		  }
 		  default: continue;
 		  }
 
diff --git a/posix/globtest.sh b/posix/globtest.sh
index 73f7ae31cc..4a062ea507 100755
--- a/posix/globtest.sh
+++ b/posix/globtest.sh
@@ -43,13 +43,22 @@  export LC_ALL
 
 # Create the arena
 testdir=${common_objpfx}posix/globtest-dir
+testdir2=${common_objpfx}posix/globtest-dir2
 testout=${common_objpfx}posix/globtest-out
 rm -rf $testdir $testout
 mkdir $testdir
+mkdir $testdir2
+mkdir $testdir2/hello1d
+ln -s $testdir2/hello1d $testdir2/hello1ds
+mkdir $testdir2/hello2d
+echo 1 > $testdir2/hello1f
+ln -s $testdir2/hello1f $testdir2/hello1fs
+echo 2 > $testdir2/hello2f
+ln $testdir2/hello2f $testdir2/hello2fl
 
 cleanup() {
     chmod 777 $testdir/noread
-    rm -fr $testdir $testout
+    rm -fr $testdir $testdir2 $testout
 }
 
 trap cleanup 0 HUP INT QUIT TERM
@@ -815,6 +824,45 @@  if test $failed -ne 0; then
   result=1
 fi
 
+# Test that if the specified glob ends with a slash then only directories are
+# matched.
+# First run with the glob that has no slash to demonstrate presence of a slash
+# makes a difference.
+failed=0
+${test_program_prefix} \
+${common_objpfx}posix/globtest "$testdir2" "hello*" |
+sort > $testout
+cat <<"EOF" | $CMP - $testout >> $logfile || failed=1
+`hello1d'
+`hello1ds'
+`hello1f'
+`hello1fs'
+`hello2d'
+`hello2f'
+`hello2fl'
+EOF
+
+if test $failed -ne 0; then
+  echo "pattern+meta test failed" >> $logfile
+  result=1
+fi
+
+# The same pattern+meta test with a slash this time.
+failed=0
+${test_program_prefix} \
+${common_objpfx}posix/globtest "$testdir2" "hello*/" |
+sort > $testout
+cat <<"EOF" | $CMP - $testout >> $logfile || failed=1
+`hello1d/'
+`hello1ds/'
+`hello2d/'
+EOF
+
+if test $failed -ne 0; then
+  echo "pattern+meta+slash test failed" >> $logfile
+  result=1
+fi
+
 if test $result -eq 0; then
     echo "All OK." > $logfile
 fi