getPathSpecFromHost builds a pathSpecifier from a host location errors with errDoesNotExist, errIsNotADir, "EvalSymlinks: too many links", or other hard filesystem errors from lstat/stat
(originalPath string)
| 63 | // getPathSpecFromHost builds a pathSpecifier from a host location |
| 64 | // errors with errDoesNotExist, errIsNotADir, "EvalSymlinks: too many links", or other hard filesystem errors from lstat/stat |
| 65 | func getPathSpecFromHost(originalPath string) (*pathSpecifier, error) { |
| 66 | pathSpec := &pathSpecifier{ |
| 67 | originalPath: originalPath, |
| 68 | endsWithSeparator: strings.HasSuffix(originalPath, string(os.PathSeparator)), |
| 69 | endsWithSeparatorDot: filepath.Base(originalPath) == ".", |
| 70 | } |
| 71 | |
| 72 | path := originalPath |
| 73 | |
| 74 | // Path may still be relative at this point. If it is, figure out getwd. |
| 75 | if !filepath.IsAbs(path) { |
| 76 | cwd, err := os.Getwd() |
| 77 | if err != nil { |
| 78 | return nil, errors.Join(errCannotResolvePathNoCwd, err) |
| 79 | } |
| 80 | path = cwd + string(os.PathSeparator) + path |
| 81 | } |
| 82 | |
| 83 | // Try to fully resolve the path |
| 84 | resolvedPath, err := filepath.EvalSymlinks(path) |
| 85 | if err != nil && !errors.Is(err, os.ErrNotExist) { |
| 86 | if errors.Is(err, syscall.ENOTDIR) { |
| 87 | return nil, errors.Join(errIsNotADir, err) |
| 88 | } |
| 89 | |
| 90 | // Other errors: |
| 91 | // - "EvalSymlinks: too many links" |
| 92 | // - any other error coming from lstat |
| 93 | return nil, err |
| 94 | } |
| 95 | |
| 96 | pathSpec.exists = err == nil |
| 97 | |
| 98 | // Ensure the parent exists if the path itself does not |
| 99 | if !pathSpec.exists { |
| 100 | // Try the parent - obtain it by removing any trailing / or /., then the base |
| 101 | cleaned := strings.TrimRight(strings.TrimSuffix(path, string(os.PathSeparator)+"."), string(os.PathSeparator)) |
| 102 | for len(cleaned) < len(path) { |
| 103 | path = cleaned |
| 104 | cleaned = strings.TrimRight(strings.TrimSuffix(path, string(os.PathSeparator)+"."), string(os.PathSeparator)) |
| 105 | } |
| 106 | |
| 107 | base := filepath.Base(path) |
| 108 | path = strings.TrimSuffix(path, string(os.PathSeparator)+base) |
| 109 | |
| 110 | // Resolve it |
| 111 | resolvedPath, err = filepath.EvalSymlinks(path) |
| 112 | if err != nil { |
| 113 | if errors.Is(err, os.ErrNotExist) { |
| 114 | return nil, errors.Join(errDoesNotExist, err) |
| 115 | } else if errors.Is(err, syscall.ENOTDIR) { |
| 116 | return nil, errors.Join(errIsNotADir, err) |
| 117 | } |
| 118 | |
| 119 | return nil, err |
| 120 | } |
| 121 | |
| 122 | resolvedPath = filepath.Join(resolvedPath, base) |