resolvePath resolves any symlinks in the existing portion of path while preserving missing trailing components.
(path string)
| 53 | // resolvePath resolves any symlinks in the existing portion of path while |
| 54 | // preserving missing trailing components. |
| 55 | func (api *API) resolvePath(path string) (string, error) { |
| 56 | if !filepath.IsAbs(path) { |
| 57 | return "", xerrors.Errorf("file path must be absolute: %q", path) |
| 58 | } |
| 59 | |
| 60 | path = filepath.Clean(path) |
| 61 | |
| 62 | lstater, hasLstat := api.filesystem.(afero.Lstater) |
| 63 | if !hasLstat { |
| 64 | return path, nil |
| 65 | } |
| 66 | targetReader, hasReadlink := api.filesystem.(afero.LinkReader) |
| 67 | if !hasReadlink { |
| 68 | return path, nil |
| 69 | } |
| 70 | |
| 71 | const maxDepth = 40 |
| 72 | var resolve func(string, int) (string, error) |
| 73 | resolve = func(path string, depth int) (string, error) { |
| 74 | if depth > maxDepth { |
| 75 | return "", xerrors.Errorf("too many levels of symlinks resolving %q", path) |
| 76 | } |
| 77 | |
| 78 | info, _, err := lstater.LstatIfPossible(path) |
| 79 | switch { |
| 80 | case err == nil: |
| 81 | if info.Mode()&os.ModeSymlink == 0 { |
| 82 | dir := filepath.Dir(path) |
| 83 | if dir == path { |
| 84 | return path, nil |
| 85 | } |
| 86 | |
| 87 | resolvedDir, err := resolve(dir, depth) |
| 88 | if err != nil { |
| 89 | return "", err |
| 90 | } |
| 91 | return filepath.Join(resolvedDir, filepath.Base(path)), nil |
| 92 | } |
| 93 | |
| 94 | target, err := targetReader.ReadlinkIfPossible(path) |
| 95 | if err != nil { |
| 96 | return "", err |
| 97 | } |
| 98 | if !filepath.IsAbs(target) { |
| 99 | target = filepath.Join(filepath.Dir(path), target) |
| 100 | } |
| 101 | return resolve(filepath.Clean(target), depth+1) |
| 102 | case errors.Is(err, os.ErrNotExist): |
| 103 | dir := filepath.Dir(path) |
| 104 | if dir == path { |
| 105 | return path, nil |
| 106 | } |
| 107 | |
| 108 | resolvedDir, err := resolve(dir, depth) |
| 109 | if err != nil { |
| 110 | return "", err |
| 111 | } |
| 112 | return filepath.Join(resolvedDir, filepath.Base(path)), nil |
no test coverage detected