github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/pkg/fileutil/symlink.go (about) 1 // Copyright 2016 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fileutil 16 17 import ( 18 "errors" 19 "os" 20 "path/filepath" 21 ) 22 23 // The following code was taken from "src/path/filepath/symlink.go" from Go 1.7.4. 24 // Modifications include: 25 // - remove Windows specific code 26 // - continue evaluatig paths, even if they don't exist 27 28 // Original copyright notice: 29 // 30 // Copyright 2012 The Go Authors. All rights reserved. 31 // Use of this source code is governed by a BSD-style 32 // license that can be found in the LICENSE file. 33 34 func isRoot(path string) bool { 35 return path == "/" 36 } 37 38 func walkLink(path string, linksWalked *int) (newpath string, islink bool, err error) { 39 if *linksWalked > 255 { 40 return "", false, errors.New("EvalSymlinks: too many links") 41 } 42 fi, err := os.Lstat(path) 43 if os.IsNotExist(err) { 44 return path, false, nil 45 } 46 if err != nil { 47 return "", false, err 48 } 49 if fi.Mode()&os.ModeSymlink == 0 { 50 return path, false, nil 51 } 52 newpath, err = os.Readlink(path) 53 if err != nil { 54 return "", false, err 55 } 56 *linksWalked++ 57 return newpath, true, nil 58 } 59 60 func walkLinks(path string, linksWalked *int) (string, error) { 61 dir, file := filepath.Split(path) 62 63 switch { 64 case dir == "": 65 newpath, _, err := walkLink(file, linksWalked) 66 return newpath, err 67 case file == "": 68 if os.IsPathSeparator(dir[len(dir)-1]) { 69 if isRoot(dir) { 70 return dir, nil 71 } 72 return walkLinks(dir[:len(dir)-1], linksWalked) 73 } 74 newpath, _, err := walkLink(dir, linksWalked) 75 return newpath, err 76 default: 77 newdir, err := walkLinks(dir, linksWalked) 78 if err != nil { 79 return "", err 80 } 81 newpath, islink, err := walkLink(filepath.Join(newdir, file), linksWalked) 82 if err != nil { 83 return "", err 84 } 85 if !islink { 86 return newpath, nil 87 } 88 if filepath.IsAbs(newpath) || os.IsPathSeparator(newpath[0]) { 89 return newpath, nil 90 } 91 return filepath.Join(newdir, newpath), nil 92 } 93 } 94 95 func walkSymlinks(path string) (string, error) { 96 if path == "" { 97 return path, nil 98 } 99 var linksWalked int // to protect against cycles 100 for { 101 i := linksWalked 102 newpath, err := walkLinks(path, &linksWalked) 103 if err != nil { 104 return "", err 105 } 106 if i == linksWalked { 107 return filepath.Clean(newpath), nil 108 } 109 path = newpath 110 } 111 } 112 113 // EvalSymlinksAlways behaves like filepath.EvalSymlinks 114 // but does not require that all path components must exist. 115 // 116 // While filepath.EvalSymlink behaves like `readlink -e` 117 // this function behaves like `readlink -m`. 118 // 119 // Unlike `readlink` EvalSymlinksAlways might return a relative path. 120 func EvalSymlinksAlways(path string) (string, error) { 121 return walkSymlinks(path) 122 }