github.com/koderover/helm@v2.17.0+incompatible/pkg/sympath/walk.go (about) 1 /* 2 Copyright (c) for portions of walk.go are held by The Go Authors, 2009 and are provided under 3 the BSD license. 4 5 https://github.com/golang/go/blob/master/LICENSE 6 7 Copyright The Helm Authors. 8 Licensed under the Apache License, Version 2.0 (the "License"); 9 you may not use this file except in compliance with the License. 10 You may obtain a copy of the License at 11 12 http://www.apache.org/licenses/LICENSE-2.0 13 14 Unless required by applicable law or agreed to in writing, software 15 distributed under the License is distributed on an "AS IS" BASIS, 16 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 See the License for the specific language governing permissions and 18 limitations under the License. 19 */ 20 21 package sympath 22 23 import ( 24 "fmt" 25 "log" 26 "os" 27 "path/filepath" 28 "sort" 29 ) 30 31 // Walk walks the file tree rooted at root, calling walkFn for each file or directory 32 // in the tree, including root. All errors that arise visiting files and directories 33 // are filtered by walkFn. The files are walked in lexical order, which makes the 34 // output deterministic but means that for very large directories Walk can be 35 // inefficient. Walk follows symbolic links. 36 func Walk(root string, walkFn filepath.WalkFunc) error { 37 info, err := os.Lstat(root) 38 if err != nil { 39 err = walkFn(root, nil, err) 40 } else { 41 err = symwalk(root, info, walkFn) 42 } 43 if err == filepath.SkipDir { 44 return nil 45 } 46 return err 47 } 48 49 // readDirNames reads the directory named by dirname and returns 50 // a sorted list of directory entries. 51 func readDirNames(dirname string) ([]string, error) { 52 f, err := os.Open(dirname) 53 if err != nil { 54 return nil, err 55 } 56 names, err := f.Readdirnames(-1) 57 f.Close() 58 if err != nil { 59 return nil, err 60 } 61 sort.Strings(names) 62 return names, nil 63 } 64 65 // symwalk recursively descends path, calling walkFn. 66 func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error { 67 // Recursively walk symlinked directories. 68 if IsSymlink(info) { 69 resolved, err := filepath.EvalSymlinks(path) 70 if err != nil { 71 return fmt.Errorf("error evaluating symlink %s: %s", path, err) 72 } 73 log.Printf("found symbolic link in path: %s resolves to %s", path, resolved) 74 if info, err = os.Lstat(resolved); err != nil { 75 return err 76 } 77 if err := symwalk(path, info, walkFn); err != nil && err != filepath.SkipDir { 78 return err 79 } 80 return nil 81 } 82 83 if err := walkFn(path, info, nil); err != nil { 84 return err 85 } 86 87 if !info.IsDir() { 88 return nil 89 } 90 91 names, err := readDirNames(path) 92 if err != nil { 93 return walkFn(path, info, err) 94 } 95 96 for _, name := range names { 97 filename := filepath.Join(path, name) 98 fileInfo, err := os.Lstat(filename) 99 if err != nil { 100 if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { 101 return err 102 } 103 } else { 104 err = symwalk(filename, fileInfo, walkFn) 105 if err != nil { 106 if (!fileInfo.IsDir() && !IsSymlink(fileInfo)) || err != filepath.SkipDir { 107 return err 108 } 109 } 110 } 111 } 112 return nil 113 } 114 115 // IsSymlink is used to determine if the fileinfo is a symbolic link. 116 func IsSymlink(fi os.FileInfo) bool { 117 return fi.Mode()&os.ModeSymlink != 0 118 }