Skip to content

Commit

Permalink
skip and blacklist
Browse files Browse the repository at this point in the history
  • Loading branch information
pjdufour committed May 4, 2019
1 parent bd57465 commit 8c89904
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 3 deletions.
24 changes: 24 additions & 0 deletions set.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package godirwalk

import (
"sync"
)

type Set struct {
*sync.Map
}

func (s *Set) Add(k interface{}) {
s.Store(k, struct{}{})
}

func (s *Set) Has(k interface{}) bool {
_, ok := s.Load(k)
return ok
}

func NewSet() *Set {
return &Set{
Map: &sync.Map{},
}
}
56 changes: 53 additions & 3 deletions walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ type Options struct {
// system node it encounters.
Callback WalkFunc

// PostChildrenCallback is an option function that Walk will invoke for
// PostChildrenCallback is an optional function that Walk will invoke for
// every file system directory it encounters after its children have been
// processed.
PostChildrenCallback WalkFunc
Expand All @@ -86,6 +86,24 @@ type Options struct {
// MinimumScratchBufferSize, then a buffer with DefaultScratchBufferSize
// bytes will be created and used once per Walk invocation.
ScratchBuffer []byte

// SkipPathCallback is an optional function that Walk will invoke before it
// stats a path or follows a symbolic link. SkipPathCallback can be used to exclude
// a directory from walking, such as /proc or /sys.
// If SkipPathCallback returns true on a given pathname, then Walk will skip the file.
// If SkipPathCallback returns false, then Walk will walk the file as normal.
// If SkipPathCallback is nil, then Walk will run as normal.
SkipPathCallback func(pathname string) bool

// SkipSymbolicLinkCallback is an optional function that Walk will invoke before it
// follows a symbolic link. SkipTargetCallback can be use to avoid loops,
// such as when a symbolic link target is "..".
// If SkipSymbolicLinkCallback returns true on a given pathname and target, then Walk will skip the symbolic link.
// If SkipSymbolicLinkCallback returns false, then Walk will follow the symbolic link as normal.
// If SkipSymbolicLinkCallback is nil, then Walk will run as normal.
SkipSymbolicLinkCallback func(pathname string, target string) bool

Blacklist *Set
}

// ErrorAction defines a set of actions the Walk function could take based on
Expand Down Expand Up @@ -176,10 +194,14 @@ type WalkFunc func(osPathname string, directoryEntry *Dirent) error
func Walk(pathname string, options *Options) error {
pathname = filepath.Clean(pathname)

if options.SkipPathCallback != nil && options.SkipPathCallback(pathname) {
return nil // skipping root
}

var fi os.FileInfo
var err error

if options.FollowSymbolicLinks {
if options.FollowSymbolicLinks && !options.Blacklist.Has(pathname) {
fi, err = os.Stat(pathname)
if err != nil {
return err
Expand All @@ -201,6 +223,18 @@ func Walk(pathname string, options *Options) error {
modeType: mode & os.ModeType,
}

if dirent.IsSymlink() && options.SkipSymbolicLinkCallback != nil {
// Read the target of the link.
target, err := os.Readlink(pathname)
if err != nil {
return err
}
// If callback returns true that indicates the link should be skipped, then return.
if options.SkipSymbolicLinkCallback(pathname, target) {
return nil
}
}

// If ErrorCallback is nil, set to a default value that halts the walk
// process on all operating system errors. This is done to allow error
// handling to be more succinct in the walk code.
Expand All @@ -227,6 +261,7 @@ func defaultErrorCallback(_ string, _ error) ErrorAction { return Halt }
// walk recursively traverses the file system node specified by pathname and the
// Dirent.
func walk(osPathname string, dirent *Dirent, options *Options) error {

err := options.Callback(osPathname, dirent)
if err != nil {
if err == filepath.SkipDir {
Expand All @@ -243,9 +278,21 @@ func walk(osPathname string, dirent *Dirent, options *Options) error {
// For instance, it could have both the symlink bit and the directory bit
// set indicating it's a symlink to a directory.
if dirent.IsSymlink() {
if !options.FollowSymbolicLinks {
// If not following symbolic links, then return.
if !options.FollowSymbolicLinks || options.Blacklist.Has(osPathname) {
return nil
}
if options.SkipSymbolicLinkCallback != nil {
// Read the target of the link.
target, err := os.Readlink(osPathname)
if err != nil {
return err
}
// If callback returns true that indicates the link should be skipped, then return.
if options.SkipSymbolicLinkCallback(osPathname, target) {
return nil
}
}
skip, err := symlinkDirHelper(osPathname, dirent, options)
if err != nil || skip {
return err
Expand All @@ -271,6 +318,9 @@ func walk(osPathname string, dirent *Dirent, options *Options) error {

for _, deChild := range deChildren {
osChildname := filepath.Join(osPathname, deChild.name)
if options.SkipPathCallback != nil && options.SkipPathCallback(osChildname) {
continue // skip this child without stat or following symbolic link
}
err = walk(osChildname, deChild, options)
if err != nil {
if err != filepath.SkipDir {
Expand Down

0 comments on commit 8c89904

Please sign in to comment.