Skip to content

Unnecessary directory read permission required in v4 #103

Description

@t-gebauer

On Linux with a file nested like this: basedir/noreadperm/subdir/file

Where the user only has the execute permission, but not the read permission for the directory: noreadperm.

Then the glob pattern basedir/*/subdir/file causes error open basedir/noreadperm: permission denied.
While v1 (and bash) match the file.

The v4 behavior is surprising, as there should be no need to open the directory noreadperm, because this part of the pattern is static.


I can see there is already a test for "no permissions", but there does not seem to be any test for missing only the read permission on a part of the directory tree.
Here is such a test for v1 and v4 which highlights the problem.

v1 works even without read permissions. v4 fails with error open basedir/noreadperm: permission denied

Test for v1.3.4

Had to increase the go version to use the octal literal.

diff --git i/doublestar_test.go w/doublestar_test.go
index 0051284..21deb8f 100644
--- i/doublestar_test.go
+++ w/doublestar_test.go
@@ -80,7 +80,7 @@ var matchTests = []MatchTest{
 	{"[", "a", false, ErrBadPattern, true},
 	{"[^", "a", false, ErrBadPattern, true},
 	{"[^bc", "a", false, ErrBadPattern, true},
-	{"a[", "a", false, nil, false},
+	// {"a[", "a", false, nil, false},  // fails for some reason at path.Match
 	{"a[", "ab", false, ErrBadPattern, true},
 	{"*x", "xxx", true, nil, true},
 	{"[abc]", "b", true, nil, true},
@@ -335,6 +335,25 @@ func TestGlobSorted(t *testing.T) {
 	}
 }
 
+func TestReadPermissionNotRequiredWithoutGlob(t *testing.T) {
+	expected := []string{"basedir/noreadperm/subdir/file"}
+  matches, err := Glob(joinWithoutClean("test", "basedir", "*", "subdir", "file"))
+  if err != nil {
+    t.Errorf("Unexpected error %v", err)
+  }
+
+	if len(matches) != len(expected) {
+		t.Errorf("Glob returned %#v; expected %#v", matches, expected)
+		return
+	}
+	for idx, match := range matches {
+		if match != joinWithoutClean("test", expected[idx]) {
+			t.Errorf("Glob returned %#v; expected %#v", matches, expected)
+			return
+		}
+	}
+}
+
 func TestMain(m *testing.M) {
 	// create the test directory
 	mkdirp("test", "a", "b", "c")
@@ -373,6 +392,10 @@ func TestMain(m *testing.M) {
 		symlink("../axbxcxdxe/", "test/b/symlink-dir")
 		symlink("/tmp/nonexistant-file-20160902155705", "test/broken-symlink")
 		symlink("a/b", "test/working-symlink")
+
+    mkdirp("test", "basedir", "noreadperm", "subdir")
+    touch("test", "basedir", "noreadperm", "subdir", "file")
+    os.Chmod(joinWithoutClean("test", "basedir", "noreadperm"), 0o100) // only execute
 	}
 
 	os.Exit(m.Run())
diff --git i/go.mod w/go.mod
index ce1688f..6275045 100644
--- i/go.mod
+++ w/go.mod
@@ -1,3 +1,3 @@
 module github.com/bmatcuk/doublestar
 
-go 1.12
+go 1.13
Test for v4.8.1
diff --git i/doublestar_test.go w/doublestar_test.go
index 3962ab5..6ac01c7 100644
--- i/doublestar_test.go
+++ w/doublestar_test.go
@@ -35,7 +35,7 @@ var matchTests = []MatchTest{
 	{"/*", "/debug/", false, false, nil, false, false, true, false, 0, 0},
 	{"/*", "//", false, false, nil, false, false, true, false, 0, 0},
 	{"abc", "abc", true, true, nil, false, false, true, true, 1, 1},
-	{"*", "abc", true, true, nil, false, false, true, true, 23, 18},
+	{"*", "abc", true, true, nil, false, false, true, true, 24, 19},
 	{"*c", "abc", true, true, nil, false, false, true, true, 2, 2},
 	{"*/", "a/", true, true, nil, false, false, true, false, 0, 0},
 	{"a*", "a", true, true, nil, false, false, true, true, 9, 9},
@@ -63,8 +63,8 @@ var matchTests = []MatchTest{
 	{"a[!a]b", "a☺b", true, true, nil, false, false, false, true, 1, 1},
 	{"a???b", "a☺b", false, false, nil, false, false, true, true, 0, 0},
 	{"a[^a][^a][^a]b", "a☺b", false, false, nil, false, false, true, true, 0, 0},
-	{"[a-ζ]*", "α", true, true, nil, false, false, true, true, 20, 17},
-	{"*[a-ζ]", "A", false, false, nil, false, false, true, true, 20, 17},
+	{"[a-ζ]*", "α", true, true, nil, false, false, true, true, 21, 18},
+	{"*[a-ζ]", "A", false, false, nil, false, false, true, true, 21, 18},
 	{"a?b", "a/b", false, false, nil, false, false, true, true, 1, 1},
 	{"a*b", "a/b", false, false, nil, false, false, true, true, 1, 1},
 	{"[\\]a]", "]", true, true, nil, false, false, true, !onWindows, 2, 2},
@@ -100,7 +100,7 @@ var matchTests = []MatchTest{
 	{"[abc]", "b", true, true, nil, false, false, true, true, 3, 3},
 	{"[abc123]", "1", true, true, nil, false, false, true, true, 4, 4},
 	{"[a-z0-9]", "1", true, true, nil, false, false, true, true, 7, 7},
-	{"**", "", true, true, nil, false, false, false, false, 38, 38},
+	{"**", "", true, true, nil, false, false, false, false, 39, 39},
 	{"a/**", "a", true, true, nil, false, false, false, true, 7, 7},
 	{"a/**/", "a", true, true, nil, false, false, false, true, 4, 4},
 	{"a/**", "a/", true, true, nil, false, false, false, false, 7, 7},
@@ -195,6 +195,8 @@ var matchTests = []MatchTest{
 	{"nopermission/*", "nopermission/file", true, false, nil, true, false, true, !onWindows, 0, 0},
 	{"nopermission/dir/", "nopermission/dir", false, false, nil, true, false, true, !onWindows, 0, 0},
 	{"nopermission/file", "nopermission/file", true, false, nil, true, false, true, !onWindows, 0, 0},
+	{"nopermission/file", "nopermission/file", true, false, nil, true, false, true, !onWindows, 0, 0},
+	{"basedir/*/subdir/file", "basedir/noreadperm/subdir/file", true, true, nil, false, false, true, !onWindows, 1, 0},
 }
 
 // Calculate the number of results that we expect
@@ -874,6 +876,10 @@ func TestMain(m *testing.M) {
 			touch("test", "nopermission", "file")
 			os.Chmod(path.Join("test", "nopermission"), 0)
 		}
+
+    mkdirp("test", "basedir", "noreadperm", "subdir")
+    touch("test", "basedir", "noreadperm", "subdir", "file")
+    os.Chmod(path.Join("test", "basedir", "noreadperm"), 0o100) // only execute
 	}
 
 	// initialize numResultsFilesOnly

Background

We encountered this problem when upgrading from grafana promtail version 3.3.2 to 3.4.0 which updates doublestar from v1 to v4.
https://github.com/grafana/loki/releases/tag/v3.4.0

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions