go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/fs/find_files.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package fs 5 6 import ( 7 "errors" 8 "io/fs" 9 "os" 10 "regexp" 11 "strings" 12 ) 13 14 func FindFiles(iofs fs.FS, from string, r *regexp.Regexp, typ string) ([]string, error) { 15 matcher := createFindFilesMatcher(typ, r) 16 matchedPaths := []string{} 17 err := fs.WalkDir(iofs, from, func(p string, d fs.DirEntry, err error) error { 18 if err != nil { 19 if errors.Is(err, os.ErrPermission) { 20 return nil 21 } 22 return err 23 } 24 if matcher.Match(p, d.Type()) { 25 matchedPaths = append(matchedPaths, p) 26 } 27 return nil 28 }) 29 if err != nil { 30 return nil, err 31 } 32 return matchedPaths, nil 33 } 34 35 type findFilesMatcher struct { 36 types []byte 37 r *regexp.Regexp 38 } 39 40 func (m findFilesMatcher) Match(path string, t fs.FileMode) bool { 41 matchesType := m.matchesType(t) 42 matchesRegex := m.matchesRegex(path) 43 44 return matchesType && matchesRegex 45 } 46 47 func (m findFilesMatcher) matchesRegex(path string) bool { 48 if m.r == nil { 49 return true 50 } 51 // We don't use r.Match because we need the entire path to match 52 // if we want to be compatible with find. It would probably be 53 // more efficient add anchors to the regular expression 54 return m.r.FindString(path) == path 55 } 56 57 func (m findFilesMatcher) matchesType(entryType fs.FileMode) bool { 58 if len(m.types) == 0 { 59 return true 60 } 61 for _, at := range m.types { 62 var matches bool 63 switch at { 64 case 'b': 65 matches = (entryType&fs.ModeDevice) != 0 && (entryType&fs.ModeCharDevice) == 0 66 case 'c': 67 matches = (entryType&fs.ModeDevice) != 0 && (entryType&fs.ModeCharDevice) != 0 68 case 'd': 69 matches = entryType.IsDir() 70 case 'p': 71 matches = (entryType & fs.ModeNamedPipe) != 0 72 case 'f': 73 matches = entryType.IsRegular() 74 case 'l': 75 matches = (entryType & fs.ModeSymlink) != 0 76 } 77 if matches { 78 return true 79 } 80 } 81 return false 82 } 83 84 func createFindFilesMatcher(typeStr string, r *regexp.Regexp) findFilesMatcher { 85 allowed := []byte{} 86 types := strings.Split(typeStr, ",") 87 for _, t := range types { 88 if len(t) == 0 { 89 continue 90 } 91 firstChar := t[0] 92 switch firstChar { 93 case 'b', 'c', 'd', 'p', 'f', 'l': 94 allowed = append(allowed, firstChar) 95 default: 96 } 97 } 98 return findFilesMatcher{ 99 types: allowed, 100 r: r, 101 } 102 }