WIP: skip reading dirs if we can
diff --git a/doublestar_test.go b/doublestar_test.go
index dfce33d..c13fd52 100644
--- a/doublestar_test.go
+++ b/doublestar_test.go
@@ -36,7 +36,7 @@
 	{"/*", "/debug/", false, false, false, nil, false, false, true, false, 0, 0},
 	{"/*", "//", false, false, false, nil, false, false, true, false, 0, 0},
 	{"abc", "abc", true, true, false, nil, false, false, true, true, 1, 1},
-	{"*", "abc", true, true, false, nil, false, false, true, true, 26, 21},
+	{"*", "abc", true, true, false, nil, false, false, true, true, 27, 21},
 	{"*c", "abc", true, true, false, nil, false, false, true, true, 2, 2},
 	{"*/", "a/", true, true, false, nil, false, false, true, false, 0, 0},
 	{"a*", "a", true, true, false, nil, false, false, true, true, 9, 9},
@@ -64,8 +64,8 @@
 	{"a[!a]b", "a☺b", true, true, false, nil, false, false, false, true, 1, 1},
 	{"a???b", "a☺b", false, false, false, nil, false, false, true, true, 0, 0},
 	{"a[^a][^a][^a]b", "a☺b", false, false, false, nil, false, false, true, true, 0, 0},
-	{"[a-ζ]*", "α", true, true, false, nil, false, false, true, true, 23, 20},
-	{"*[a-ζ]", "A", false, false, false, nil, false, false, true, true, 23, 20},
+	{"[a-ζ]*", "α", true, true, false, nil, false, false, true, true, 24, 20},
+	{"*[a-ζ]", "A", false, false, false, nil, false, false, true, true, 24, 20},
 	{"a?b", "a/b", false, false, false, nil, false, false, true, true, 1, 1},
 	{"a*b", "a/b", false, false, false, nil, false, false, true, true, 1, 1},
 	{"[\\]a]", "]", true, true, false, nil, false, false, true, !onWindows, 2, 2},
@@ -210,6 +210,8 @@
 	{"hidden-tests/**", "hidden-tests/hidden-dir/hidden-file", true, true, false, nil, false, false, false, onWindows, 6, 6},
 	{"hidden-tests/.hidden-dir/*", "hidden-tests/.hidden-dir/.hidden-file", true, true, false, nil, false, false, true, !onWindows, 2, 2},
 	{"hidden-tests/hidden-dir/*", "hidden-tests/hidden-dir/hidden-file", true, true, false, nil, false, false, true, onWindows, 2, 2},
+	// this pattern is technically "standard", but path.Glob will fail to match
+	{"*/dir/file", "noreaddirpermission/dir/file", true, true, false, nil, true, false, false, !onWindows, 1, 1},
 }
 
 // True if the file system supports case-sensitive filenames
@@ -950,11 +952,19 @@
 		symlink("/tmp/nonexistant-file-20160902155705", "testdata/broken-symlink")
 		symlink("a/b", "testdata/working-symlink")
 
+		// no permisions at all
 		if !exists("testdata", "nopermission") {
 			mkdirp("testdata", "nopermission", "dir")
 			touch("testdata", "nopermission", "file")
 			os.Chmod(path.Join("testdata", "nopermission"), 0)
 		}
+
+		// no permission to read dir
+		if !exists("testdata", "noreaddirpermission") {
+			mkdirp("testdata", "noreaddirpermission", "dir")
+			touch("testdata", "noreaddirpermission", "dir", "file")
+			os.Chmod(path.Join("testdata", "noreaddirpermission"), 0o111)
+		}
 	}
 
 	// initialize numResultsFilesOnly
diff --git a/glob.go b/glob.go
index f3ef039..0c4394c 100644
--- a/glob.go
+++ b/glob.go
@@ -109,11 +109,26 @@
 		return g.globDir(fsys, unescapeMeta(dir), pattern, matches, firstSegment, beforeMeta)
 	}
 
+	// recurse on dir
 	var dirs []string
 	dirs, err = g.doGlob(fsys, dir, matches, false, beforeMeta)
 	if err != nil {
 		return
 	}
+
+	// if the pattern does not contain any meta, we can skip globbing
+	if indexMeta(pattern) == -1 {
+		for _, d := range dirs {
+			matches, err = g.globDirNoMeta(fsys, d, pattern, matches, firstSegment, false)
+			if err != nil {
+				return
+			}
+		}
+
+		return
+	}
+
+	// for each dir from above, glob
 	for _, d := range dirs {
 		matches, err = g.globDir(fsys, d, pattern, matches, firstSegment, false)
 		if err != nil {
@@ -193,19 +208,7 @@
 	m = matches
 
 	if pattern == "" {
-		if !canMatchFiles || !g.filesOnly {
-			// pattern can be an empty string if the original pattern ended in a
-			// slash, in which case, we should just return dir, but only if it
-			// actually exists and it's a directory (or a symlink to a directory)
-			_, isDir, err := g.isPathDir(fsys, dir, beforeMeta)
-			if err != nil {
-				return nil, err
-			}
-			if isDir {
-				m = append(m, dir)
-			}
-		}
-		return
+		return g.globDirEmptyPattern(fsys, dir, m, canMatchFiles, beforeMeta)
 	}
 
 	if pattern == "**" {
@@ -310,6 +313,61 @@
 	return matches, nil
 }
 
+// find files/subdirectories in the given `dir` that match `pattern`,
+// assuming `pattern` has no meta characters
+func (g *glob) globDirNoMeta(fsys fs.FS, dir, pattern string, matches []string, canMatchFiles, beforeMeta bool) (m []string, e error) {
+	m = matches
+
+	if pattern == "" {
+		return g.globDirEmptyPattern(fsys, dir, m, canMatchFiles, beforeMeta)
+	}
+
+	fullPath := path.Join(dir, pattern)
+
+	var info fs.FileInfo
+	var matched bool
+	info, matched, e = g.exists(fsys, fullPath, beforeMeta)
+	if e != nil {
+		return
+	}
+	if matched {
+		matched = canMatchFiles
+		if !matched || g.filesOnly {
+			matched = info.IsDir()
+			if canMatchFiles {
+				// if we're here, it's because g.filesOnly
+				// is set and we don't want directories
+				matched = !matched
+			}
+		}
+		if matched {
+			m = append(m, fullPath)
+		}
+	}
+
+	return
+}
+
+// handle an empty pattern while globbing a directory
+func (g *glob) globDirEmptyPattern(fsys fs.FS, dir string, matches []string, canMatchFiles, beforeMeta bool) (m []string, e error) {
+	m = matches
+
+	if !canMatchFiles || !g.filesOnly {
+		// pattern can be an empty string if the original pattern ended in a
+		// slash, in which case, we should just return dir, but only if it
+		// actually exists and it's a directory (or a symlink to a directory)
+		_, isDir, err := g.isPathDir(fsys, dir, beforeMeta)
+		if err != nil {
+			return nil, err
+		}
+		if isDir {
+			m = append(m, dir)
+		}
+	}
+
+	return
+}
+
 // Returns true if the pattern has a doublestar in the middle of the pattern.
 // In this case, GlobWalk is faster because it can get away with less
 // allocations. However, Glob has a _very_ slight edge if the pattern ends in
diff --git a/globwalk.go b/globwalk.go
index 9516cef..8a55865 100644
--- a/globwalk.go
+++ b/globwalk.go
@@ -114,8 +114,13 @@
 		return g.globDirWalk(fsys, unescapeMeta(dir), pattern, firstSegment, beforeMeta, fn)
 	}
 
+	patternHasNoMeta := indexMeta(pattern) == -1
 	return g.doGlobWalk(fsys, dir, false, beforeMeta, func(p string, d fs.DirEntry) error {
-		if err := g.globDirWalk(fsys, p, pattern, firstSegment, false, fn); err != nil {
+		if patternHasNoMeta {
+			if err := g.globDirNoMetaWalk(fsys, p, pattern, firstSegment, false, fn); err != nil {
+				return err
+			}
+		} else if err := g.globDirWalk(fsys, p, pattern, firstSegment, false, fn); err != nil {
 			return err
 		}
 		return nil
@@ -240,22 +245,7 @@
 
 func (g *glob) globDirWalk(fsys fs.FS, dir, pattern string, canMatchFiles, beforeMeta bool, fn GlobWalkFunc) (e error) {
 	if pattern == "" {
-		if !canMatchFiles || !g.filesOnly {
-			// pattern can be an empty string if the original pattern ended in a
-			// slash, in which case, we should just return dir, but only if it
-			// actually exists and it's a directory (or a symlink to a directory)
-			info, isDir, err := g.isPathDir(fsys, dir, beforeMeta)
-			if err != nil {
-				return err
-			}
-			if isDir {
-				e = fn(dir, dirEntryFromFileInfo(info))
-				if e == SkipDir {
-					e = nil
-				}
-			}
-		}
-		return
+		return g.globDirEmptyPatternWalk(fsys, dir, canMatchFiles, beforeMeta, fn)
 	}
 
 	if pattern == "**" {
@@ -393,6 +383,61 @@
 	return
 }
 
+func (g *glob) globDirNoMetaWalk(fsys fs.FS, dir, pattern string, canMatchFiles, beforeMeta bool, fn GlobWalkFunc) (e error) {
+	if pattern == "" {
+		return g.globDirEmptyPatternWalk(fsys, dir, canMatchFiles, beforeMeta, fn)
+	}
+
+	fullPath := path.Join(dir, pattern)
+
+	var info fs.FileInfo
+	var matched bool
+	info, matched, e = g.exists(fsys, fullPath, beforeMeta)
+	if e != nil {
+		return
+	}
+	if matched {
+		matched = canMatchFiles
+		if !matched || g.filesOnly {
+			matched = info.IsDir()
+			if canMatchFiles {
+				// if we're here, it's because g.filesOnly
+				// is set and we don't want directories
+				matched = !matched
+			}
+		}
+		if matched {
+			if e = fn(fullPath, dirEntryFromFileInfo(info)); e != nil {
+				if e == SkipDir {
+					e = nil
+				}
+			}
+		}
+	}
+
+	return
+}
+
+func (g *glob) globDirEmptyPatternWalk(fsys fs.FS, dir string, canMatchFiles, beforeMeta bool, fn GlobWalkFunc) (e error) {
+	if !canMatchFiles || !g.filesOnly {
+		// pattern can be an empty string if the original pattern ended in a
+		// slash, in which case, we should just return dir, but only if it
+		// actually exists and it's a directory (or a symlink to a directory)
+		info, isDir, err := g.isPathDir(fsys, dir, beforeMeta)
+		if err != nil {
+			return err
+		}
+		if isDir {
+			e = fn(dir, dirEntryFromFileInfo(info))
+			if e == SkipDir {
+				e = nil
+			}
+		}
+	}
+
+	return
+}
+
 type DirEntryFromFileInfo struct {
 	fi fs.FileInfo
 }