1package contenthash 2 3import ( 4 "errors" 5 "os" 6 "path/filepath" 7) 8 9var ( 10 errTooManyLinks = errors.New("too many links") 11) 12 13type onSymlinkFunc func(string, string) error 14 15// rootPath joins a path with a root, evaluating and bounding any 16// symlink to the root directory. 17// This is containerd/continuity/fs RootPath implementation with a callback on 18// resolving the symlink. 19func rootPath(root, path string, cb onSymlinkFunc) (string, error) { 20 if path == "" { 21 return root, nil 22 } 23 var linksWalked int // to protect against cycles 24 for { 25 i := linksWalked 26 newpath, err := walkLinks(root, path, &linksWalked, cb) 27 if err != nil { 28 return "", err 29 } 30 path = newpath 31 if i == linksWalked { 32 newpath = filepath.Join("/", newpath) 33 if path == newpath { 34 return filepath.Join(root, newpath), nil 35 } 36 path = newpath 37 } 38 } 39} 40 41func walkLink(root, path string, linksWalked *int, cb onSymlinkFunc) (newpath string, islink bool, err error) { 42 if *linksWalked > 255 { 43 return "", false, errTooManyLinks 44 } 45 46 path = filepath.Join("/", path) 47 if path == "/" { 48 return path, false, nil 49 } 50 realPath := filepath.Join(root, path) 51 52 fi, err := os.Lstat(realPath) 53 if err != nil { 54 // If path does not yet exist, treat as non-symlink 55 if os.IsNotExist(err) { 56 return path, false, nil 57 } 58 return "", false, err 59 } 60 if fi.Mode()&os.ModeSymlink == 0 { 61 return path, false, nil 62 } 63 newpath, err = os.Readlink(realPath) 64 if err != nil { 65 return "", false, err 66 } 67 if cb != nil { 68 if err := cb(path, newpath); err != nil { 69 return "", false, err 70 } 71 } 72 *linksWalked++ 73 return newpath, true, nil 74} 75 76func walkLinks(root, path string, linksWalked *int, cb onSymlinkFunc) (string, error) { 77 switch dir, file := filepath.Split(path); { 78 case dir == "": 79 newpath, _, err := walkLink(root, file, linksWalked, cb) 80 return newpath, err 81 case file == "": 82 if os.IsPathSeparator(dir[len(dir)-1]) { 83 if dir == "/" { 84 return dir, nil 85 } 86 return walkLinks(root, dir[:len(dir)-1], linksWalked, cb) 87 } 88 newpath, _, err := walkLink(root, dir, linksWalked, cb) 89 return newpath, err 90 default: 91 newdir, err := walkLinks(root, dir, linksWalked, cb) 92 if err != nil { 93 return "", err 94 } 95 newpath, islink, err := walkLink(root, filepath.Join(newdir, file), linksWalked, cb) 96 if err != nil { 97 return "", err 98 } 99 if !islink { 100 return newpath, nil 101 } 102 if filepath.IsAbs(newpath) { 103 return newpath, nil 104 } 105 return filepath.Join(newdir, newpath), nil 106 } 107} 108