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  }