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 }