github.com/charlievieth/fastwalk@v1.0.3/entry_filter_unix.go (about)

     1  //go:build (linux || darwin || freebsd || openbsd || netbsd || !windows) && !appengine
     2  // +build linux darwin freebsd openbsd netbsd !windows
     3  // +build !appengine
     4  
     5  package fastwalk
     6  
     7  import (
     8  	"io/fs"
     9  	"sync"
    10  	"syscall"
    11  )
    12  
    13  type fileKey struct {
    14  	Dev uint64
    15  	Ino uint64
    16  }
    17  
    18  type entryMap struct {
    19  	mu   sync.Mutex
    20  	keys map[fileKey]struct{}
    21  }
    22  
    23  // An EntryFilter keeps track of visited directory entries and can be used to
    24  // detect and avoid symlink loops or processing the same file twice.
    25  type EntryFilter struct {
    26  	// Use an array of 8 to reduce lock contention. The entryMap is
    27  	// picked via the inode number. We don't take the device number
    28  	// into account because: we don't expect to see many of them and
    29  	// uniformly spreading the load isn't terribly beneficial here.
    30  	ents [8]entryMap
    31  }
    32  
    33  // NewEntryFilter returns a new EntryFilter
    34  func NewEntryFilter() *EntryFilter {
    35  	return new(EntryFilter)
    36  }
    37  
    38  func (e *EntryFilter) seen(dev, ino uint64) (seen bool) {
    39  	m := &e.ents[ino%uint64(len(e.ents))]
    40  	m.mu.Lock()
    41  	if _, seen = m.keys[fileKey{dev, ino}]; !seen {
    42  		if m.keys == nil {
    43  			m.keys = make(map[fileKey]struct{})
    44  		}
    45  		m.keys[fileKey{dev, ino}] = struct{}{}
    46  	}
    47  	m.mu.Unlock()
    48  	return seen
    49  }
    50  
    51  // TODO: this name is confusing and should be fixed
    52  
    53  // Entry returns if path and fs.DirEntry have been seen before.
    54  func (e *EntryFilter) Entry(path string, de fs.DirEntry) (seen bool) {
    55  	fi, err := StatDirEntry(path, de)
    56  	if err != nil {
    57  		return true // treat errors as duplicate files
    58  	}
    59  	stat := fi.Sys().(*syscall.Stat_t)
    60  	return e.seen(uint64(stat.Dev), uint64(stat.Ino))
    61  }