...

Source file src/io/fs/glob.go

Documentation: io/fs

		 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  }
	 130  

View as plain text