1// Copyright ©2015 The Go Authors 2// Copyright ©2015 Steve Francia <spf@spf13.com> 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15 16package afero 17 18import ( 19 "os" 20 "path/filepath" 21 "sort" 22) 23 24// readDirNames reads the directory named by dirname and returns 25// a sorted list of directory entries. 26// adapted from https://golang.org/src/path/filepath/path.go 27func readDirNames(fs Fs, dirname string) ([]string, error) { 28 f, err := fs.Open(dirname) 29 if err != nil { 30 return nil, err 31 } 32 names, err := f.Readdirnames(-1) 33 f.Close() 34 if err != nil { 35 return nil, err 36 } 37 sort.Strings(names) 38 return names, nil 39} 40 41// walk recursively descends path, calling walkFn 42// adapted from https://golang.org/src/path/filepath/path.go 43func walk(fs Fs, path string, info os.FileInfo, walkFn filepath.WalkFunc) error { 44 err := walkFn(path, info, nil) 45 if err != nil { 46 if info.IsDir() && err == filepath.SkipDir { 47 return nil 48 } 49 return err 50 } 51 52 if !info.IsDir() { 53 return nil 54 } 55 56 names, err := readDirNames(fs, path) 57 if err != nil { 58 return walkFn(path, info, err) 59 } 60 61 for _, name := range names { 62 filename := filepath.Join(path, name) 63 fileInfo, err := lstatIfPossible(fs, filename) 64 if err != nil { 65 if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { 66 return err 67 } 68 } else { 69 err = walk(fs, filename, fileInfo, walkFn) 70 if err != nil { 71 if !fileInfo.IsDir() || err != filepath.SkipDir { 72 return err 73 } 74 } 75 } 76 } 77 return nil 78} 79 80// if the filesystem supports it, use Lstat, else use fs.Stat 81func lstatIfPossible(fs Fs, path string) (os.FileInfo, error) { 82 if lfs, ok := fs.(Lstater); ok { 83 fi, _, err := lfs.LstatIfPossible(path) 84 return fi, err 85 } 86 return fs.Stat(path) 87} 88 89// Walk walks the file tree rooted at root, calling walkFn for each file or 90// directory in the tree, including root. All errors that arise visiting files 91// and directories are filtered by walkFn. The files are walked in lexical 92// order, which makes the output deterministic but means that for very 93// large directories Walk can be inefficient. 94// Walk does not follow symbolic links. 95 96func (a Afero) Walk(root string, walkFn filepath.WalkFunc) error { 97 return Walk(a.Fs, root, walkFn) 98} 99 100func Walk(fs Fs, root string, walkFn filepath.WalkFunc) error { 101 info, err := lstatIfPossible(fs, root) 102 if err != nil { 103 return walkFn(root, nil, err) 104 } 105 return walk(fs, root, info, walkFn) 106} 107