github.com/koderover/helm@v2.17.0+incompatible/internal/third_party/dep/fs/fs.go (about)

     1  /*
     2  Copyright (c) for portions of fs.go are held by The Go Authors, 2016 and are provided under
     3  the BSD license.
     4  
     5  Redistribution and use in source and binary forms, with or without
     6  modification, are permitted provided that the following conditions are
     7  met:
     8  
     9     * Redistributions of source code must retain the above copyright
    10  notice, this list of conditions and the following disclaimer.
    11     * Redistributions in binary form must reproduce the above
    12  copyright notice, this list of conditions and the following disclaimer
    13  in the documentation and/or other materials provided with the
    14  distribution.
    15     * Neither the name of Google Inc. nor the names of its
    16  contributors may be used to endorse or promote products derived from
    17  this software without specific prior written permission.
    18  
    19  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    20  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    21  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    22  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    23  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    24  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    25  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    26  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    27  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    28  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    29  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    30  */
    31  
    32  package fs
    33  
    34  import (
    35  	"io"
    36  	"io/ioutil"
    37  	"os"
    38  	"path/filepath"
    39  	"runtime"
    40  	"syscall"
    41  
    42  	"github.com/pkg/errors"
    43  )
    44  
    45  // fs contains a copy of a few functions from dep tool code to avoid a dependency on golang/dep.
    46  // This code is copied from https://github.com/golang/dep/blob/37d6c560cdf407be7b6cd035b23dba89df9275cf/internal/fs/fs.go
    47  // No changes to the code were made other than removing some unused functions
    48  
    49  // RenameWithFallback attempts to rename a file or directory, but falls back to
    50  // copying in the event of a cross-device link error. If the fallback copy
    51  // succeeds, src is still removed, emulating normal rename behavior.
    52  func RenameWithFallback(src, dst string) error {
    53  	_, err := os.Stat(src)
    54  	if err != nil {
    55  		return errors.Wrapf(err, "cannot stat %s", src)
    56  	}
    57  
    58  	err = os.Rename(src, dst)
    59  	if err == nil {
    60  		return nil
    61  	}
    62  
    63  	return renameFallback(err, src, dst)
    64  }
    65  
    66  // renameByCopy attempts to rename a file or directory by copying it to the
    67  // destination and then removing the src thus emulating the rename behavior.
    68  func renameByCopy(src, dst string) error {
    69  	var cerr error
    70  	if dir, _ := IsDir(src); dir {
    71  		cerr = CopyDir(src, dst)
    72  		if cerr != nil {
    73  			cerr = errors.Wrap(cerr, "copying directory failed")
    74  		}
    75  	} else {
    76  		cerr = copyFile(src, dst)
    77  		if cerr != nil {
    78  			cerr = errors.Wrap(cerr, "copying file failed")
    79  		}
    80  	}
    81  
    82  	if cerr != nil {
    83  		return errors.Wrapf(cerr, "rename fallback failed: cannot rename %s to %s", src, dst)
    84  	}
    85  
    86  	return errors.Wrapf(os.RemoveAll(src), "cannot delete %s", src)
    87  }
    88  
    89  var (
    90  	errSrcNotDir = errors.New("source is not a directory")
    91  	errDstExist  = errors.New("destination already exists")
    92  )
    93  
    94  // CopyDir recursively copies a directory tree, attempting to preserve permissions.
    95  // Source directory must exist, destination directory must *not* exist.
    96  func CopyDir(src, dst string) error {
    97  	src = filepath.Clean(src)
    98  	dst = filepath.Clean(dst)
    99  
   100  	// We use os.Lstat() here to ensure we don't fall in a loop where a symlink
   101  	// actually links to a one of its parent directories.
   102  	fi, err := os.Lstat(src)
   103  	if err != nil {
   104  		return err
   105  	}
   106  	if !fi.IsDir() {
   107  		return errSrcNotDir
   108  	}
   109  
   110  	_, err = os.Stat(dst)
   111  	if err != nil && !os.IsNotExist(err) {
   112  		return err
   113  	}
   114  	if err == nil {
   115  		return errDstExist
   116  	}
   117  
   118  	if err = os.MkdirAll(dst, fi.Mode()); err != nil {
   119  		return errors.Wrapf(err, "cannot mkdir %s", dst)
   120  	}
   121  
   122  	entries, err := ioutil.ReadDir(src)
   123  	if err != nil {
   124  		return errors.Wrapf(err, "cannot read directory %s", dst)
   125  	}
   126  
   127  	for _, entry := range entries {
   128  		srcPath := filepath.Join(src, entry.Name())
   129  		dstPath := filepath.Join(dst, entry.Name())
   130  
   131  		if entry.IsDir() {
   132  			if err = CopyDir(srcPath, dstPath); err != nil {
   133  				return errors.Wrap(err, "copying directory failed")
   134  			}
   135  		} else {
   136  			// This will include symlinks, which is what we want when
   137  			// copying things.
   138  			if err = copyFile(srcPath, dstPath); err != nil {
   139  				return errors.Wrap(err, "copying file failed")
   140  			}
   141  		}
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  // copyFile copies the contents of the file named src to the file named
   148  // by dst. The file will be created if it does not already exist. If the
   149  // destination file exists, all its contents will be replaced by the contents
   150  // of the source file. The file mode will be copied from the source.
   151  func copyFile(src, dst string) (err error) {
   152  	if sym, err := IsSymlink(src); err != nil {
   153  		return errors.Wrap(err, "symlink check failed")
   154  	} else if sym {
   155  		if err := cloneSymlink(src, dst); err != nil {
   156  			if runtime.GOOS == "windows" {
   157  				// If cloning the symlink fails on Windows because the user
   158  				// does not have the required privileges, ignore the error and
   159  				// fall back to copying the file contents.
   160  				//
   161  				// ERROR_PRIVILEGE_NOT_HELD is 1314 (0x522):
   162  				// https://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx
   163  				if lerr, ok := err.(*os.LinkError); ok && lerr.Err != syscall.Errno(1314) {
   164  					return err
   165  				}
   166  			} else {
   167  				return err
   168  			}
   169  		} else {
   170  			return nil
   171  		}
   172  	}
   173  
   174  	in, err := os.Open(src)
   175  	if err != nil {
   176  		return
   177  	}
   178  	defer in.Close()
   179  
   180  	out, err := os.Create(dst)
   181  	if err != nil {
   182  		return
   183  	}
   184  
   185  	if _, err = io.Copy(out, in); err != nil {
   186  		out.Close()
   187  		return
   188  	}
   189  
   190  	// Check for write errors on Close
   191  	if err = out.Close(); err != nil {
   192  		return
   193  	}
   194  
   195  	si, err := os.Stat(src)
   196  	if err != nil {
   197  		return
   198  	}
   199  
   200  	// Temporary fix for Go < 1.9
   201  	//
   202  	// See: https://github.com/golang/dep/issues/774
   203  	// and https://github.com/golang/go/issues/20829
   204  	if runtime.GOOS == "windows" {
   205  		dst = fixLongPath(dst)
   206  	}
   207  	err = os.Chmod(dst, si.Mode())
   208  
   209  	return
   210  }
   211  
   212  // cloneSymlink will create a new symlink that points to the resolved path of sl.
   213  // If sl is a relative symlink, dst will also be a relative symlink.
   214  func cloneSymlink(sl, dst string) error {
   215  	resolved, err := os.Readlink(sl)
   216  	if err != nil {
   217  		return err
   218  	}
   219  
   220  	return os.Symlink(resolved, dst)
   221  }
   222  
   223  // IsDir determines is the path given is a directory or not.
   224  func IsDir(name string) (bool, error) {
   225  	fi, err := os.Stat(name)
   226  	if err != nil {
   227  		return false, err
   228  	}
   229  	if !fi.IsDir() {
   230  		return false, errors.Errorf("%q is not a directory", name)
   231  	}
   232  	return true, nil
   233  }
   234  
   235  // IsSymlink determines if the given path is a symbolic link.
   236  func IsSymlink(path string) (bool, error) {
   237  	l, err := os.Lstat(path)
   238  	if err != nil {
   239  		return false, err
   240  	}
   241  
   242  	return l.Mode()&os.ModeSymlink == os.ModeSymlink, nil
   243  }
   244  
   245  // fixLongPath returns the extended-length (\\?\-prefixed) form of
   246  // path when needed, in order to avoid the default 260 character file
   247  // path limit imposed by Windows. If path is not easily converted to
   248  // the extended-length form (for example, if path is a relative path
   249  // or contains .. elements), or is short enough, fixLongPath returns
   250  // path unmodified.
   251  //
   252  // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
   253  func fixLongPath(path string) string {
   254  	// Do nothing (and don't allocate) if the path is "short".
   255  	// Empirically (at least on the Windows Server 2013 builder),
   256  	// the kernel is arbitrarily okay with < 248 bytes. That
   257  	// matches what the docs above say:
   258  	// "When using an API to create a directory, the specified
   259  	// path cannot be so long that you cannot append an 8.3 file
   260  	// name (that is, the directory name cannot exceed MAX_PATH
   261  	// minus 12)." Since MAX_PATH is 260, 260 - 12 = 248.
   262  	//
   263  	// The MSDN docs appear to say that a normal path that is 248 bytes long
   264  	// will work; empirically the path must be less then 248 bytes long.
   265  	if len(path) < 248 {
   266  		// Don't fix. (This is how Go 1.7 and earlier worked,
   267  		// not automatically generating the \\?\ form)
   268  		return path
   269  	}
   270  
   271  	// The extended form begins with \\?\, as in
   272  	// \\?\c:\windows\foo.txt or \\?\UNC\server\share\foo.txt.
   273  	// The extended form disables evaluation of . and .. path
   274  	// elements and disables the interpretation of / as equivalent
   275  	// to \. The conversion here rewrites / to \ and elides
   276  	// . elements as well as trailing or duplicate separators. For
   277  	// simplicity it avoids the conversion entirely for relative
   278  	// paths or paths containing .. elements. For now,
   279  	// \\server\share paths are not converted to
   280  	// \\?\UNC\server\share paths because the rules for doing so
   281  	// are less well-specified.
   282  	if len(path) >= 2 && path[:2] == `\\` {
   283  		// Don't canonicalize UNC paths.
   284  		return path
   285  	}
   286  	if !isAbs(path) {
   287  		// Relative path
   288  		return path
   289  	}
   290  
   291  	const prefix = `\\?`
   292  
   293  	pathbuf := make([]byte, len(prefix)+len(path)+len(`\`))
   294  	copy(pathbuf, prefix)
   295  	n := len(path)
   296  	r, w := 0, len(prefix)
   297  	for r < n {
   298  		switch {
   299  		case os.IsPathSeparator(path[r]):
   300  			// empty block
   301  			r++
   302  		case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
   303  			// /./
   304  			r++
   305  		case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
   306  			// /../ is currently unhandled
   307  			return path
   308  		default:
   309  			pathbuf[w] = '\\'
   310  			w++
   311  			for ; r < n && !os.IsPathSeparator(path[r]); r++ {
   312  				pathbuf[w] = path[r]
   313  				w++
   314  			}
   315  		}
   316  	}
   317  	// A drive's root directory needs a trailing \
   318  	if w == len(`\\?\c:`) {
   319  		pathbuf[w] = '\\'
   320  		w++
   321  	}
   322  	return string(pathbuf[:w])
   323  }
   324  
   325  func isAbs(path string) (b bool) {
   326  	v := volumeName(path)
   327  	if v == "" {
   328  		return false
   329  	}
   330  	path = path[len(v):]
   331  	if path == "" {
   332  		return false
   333  	}
   334  	return os.IsPathSeparator(path[0])
   335  }
   336  
   337  func volumeName(path string) (v string) {
   338  	if len(path) < 2 {
   339  		return ""
   340  	}
   341  	// with drive letter
   342  	c := path[0]
   343  	if path[1] == ':' &&
   344  		('0' <= c && c <= '9' || 'a' <= c && c <= 'z' ||
   345  			'A' <= c && c <= 'Z') {
   346  		return path[:2]
   347  	}
   348  	// is it UNC
   349  	if l := len(path); l >= 5 && os.IsPathSeparator(path[0]) && os.IsPathSeparator(path[1]) &&
   350  		!os.IsPathSeparator(path[2]) && path[2] != '.' {
   351  		// first, leading `\\` and next shouldn't be `\`. its server name.
   352  		for n := 3; n < l-1; n++ {
   353  			// second, next '\' shouldn't be repeated.
   354  			if os.IsPathSeparator(path[n]) {
   355  				n++
   356  				// third, following something characters. its share name.
   357  				if !os.IsPathSeparator(path[n]) {
   358  					if path[n] == '.' {
   359  						break
   360  					}
   361  					for ; n < l; n++ {
   362  						if os.IsPathSeparator(path[n]) {
   363  							break
   364  						}
   365  					}
   366  					return path[:n]
   367  				}
   368  				break
   369  			}
   370  		}
   371  	}
   372  	return ""
   373  }