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  }