github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/io/fs/glob.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package fs 6 7 import ( 8 "path" 9 ) 10 11 // A GlobFS is a file system with a Glob method. 12 type GlobFS interface { 13 FS 14 15 // Glob returns the names of all files matching pattern, 16 // providing an implementation of the top-level 17 // Glob function. 18 Glob(pattern string) ([]string, error) 19 } 20 21 // Glob returns the names of all files matching pattern or nil 22 // if there is no matching file. The syntax of patterns is the same 23 // as in [path.Match]. The pattern may describe hierarchical names such as 24 // usr/*/bin/ed. 25 // 26 // Glob ignores file system errors such as I/O errors reading directories. 27 // The only possible returned error is [path.ErrBadPattern], reporting that 28 // the pattern is malformed. 29 // 30 // If fs implements [GlobFS], Glob calls fs.Glob. 31 // Otherwise, Glob uses [ReadDir] to traverse the directory tree 32 // and look for matches for the pattern. 33 func Glob(fsys FS, pattern string) (matches []string, err error) { 34 return globWithLimit(fsys, pattern, 0) 35 } 36 37 func globWithLimit(fsys FS, pattern string, depth int) (matches []string, err error) { 38 // This limit is added to prevent stack exhaustion issues. See 39 // CVE-2022-30630. 40 const pathSeparatorsLimit = 10000 41 if depth > pathSeparatorsLimit { 42 return nil, path.ErrBadPattern 43 } 44 if fsys, ok := fsys.(GlobFS); ok { 45 return fsys.Glob(pattern) 46 } 47 48 // Check pattern is well-formed. 49 if _, err := path.Match(pattern, ""); err != nil { 50 return nil, err 51 } 52 if !hasMeta(pattern) { 53 if _, err = Stat(fsys, pattern); err != nil { 54 return nil, nil 55 } 56 return []string{pattern}, nil 57 } 58 59 dir, file := path.Split(pattern) 60 dir = cleanGlobPath(dir) 61 62 if !hasMeta(dir) { 63 return glob(fsys, dir, file, nil) 64 } 65 66 // Prevent infinite recursion. See issue 15879. 67 if dir == pattern { 68 return nil, path.ErrBadPattern 69 } 70 71 var m []string 72 m, err = globWithLimit(fsys, dir, depth+1) 73 if err != nil { 74 return nil, err 75 } 76 for _, d := range m { 77 matches, err = glob(fsys, d, file, matches) 78 if err != nil { 79 return 80 } 81 } 82 return 83 } 84 85 // cleanGlobPath prepares path for glob matching. 86 func cleanGlobPath(path string) string { 87 switch path { 88 case "": 89 return "." 90 default: 91 return path[0 : len(path)-1] // chop off trailing separator 92 } 93 } 94 95 // glob searches for files matching pattern in the directory dir 96 // and appends them to matches, returning the updated slice. 97 // If the directory cannot be opened, glob returns the existing matches. 98 // New matches are added in lexicographical order. 99 func glob(fs FS, dir, pattern string, matches []string) (m []string, e error) { 100 m = matches 101 infos, err := ReadDir(fs, dir) 102 if err != nil { 103 return // ignore I/O error 104 } 105 106 for _, info := range infos { 107 n := info.Name() 108 matched, err := path.Match(pattern, n) 109 if err != nil { 110 return m, err 111 } 112 if matched { 113 m = append(m, path.Join(dir, n)) 114 } 115 } 116 return 117 } 118 119 // hasMeta reports whether path contains any of the magic characters 120 // recognized by path.Match. 121 func hasMeta(path string) bool { 122 for i := 0; i < len(path); i++ { 123 switch path[i] { 124 case '*', '?', '[', '\\': 125 return true 126 } 127 } 128 return false 129 }