nativeRelPath verifies that p is a non-empty relative path using either slashes or the buildlet's native path separator, and returns it canonicalized to the native path separator.
(p string)
| 160 | // using either slashes or the buildlet's native path separator, |
| 161 | // and returns it canonicalized to the native path separator. |
| 162 | func nativeRelPath(p string) (string, error) { |
| 163 | if p == "" { |
| 164 | return "", errors.New("path not provided") |
| 165 | } |
| 166 | |
| 167 | if filepath.Separator != '/' && strings.Contains(p, string(filepath.Separator)) { |
| 168 | clean := filepath.Clean(p) |
| 169 | if filepath.IsAbs(clean) { |
| 170 | return "", fmt.Errorf("path %q is not relative", p) |
| 171 | } |
| 172 | if clean == ".." || strings.HasPrefix(clean, ".."+string(filepath.Separator)) { |
| 173 | return "", fmt.Errorf("path %q refers to a parent directory", p) |
| 174 | } |
| 175 | if strings.HasPrefix(p, string(filepath.Separator)) || filepath.VolumeName(clean) != "" { |
| 176 | // On Windows, this catches semi-relative paths like "C:" (meaning “the |
| 177 | // current working directory on volume C:”) and "\windows" (meaning “the |
| 178 | // windows subdirectory of the current drive letter”). |
| 179 | return "", fmt.Errorf("path %q is relative to volume", p) |
| 180 | } |
| 181 | return p, nil |
| 182 | } |
| 183 | |
| 184 | clean := path.Clean(p) |
| 185 | if path.IsAbs(clean) { |
| 186 | return "", fmt.Errorf("path %q is not relative", p) |
| 187 | } |
| 188 | if clean == ".." || strings.HasPrefix(clean, "../") { |
| 189 | return "", fmt.Errorf("path %q refers to a parent directory", p) |
| 190 | } |
| 191 | canon := filepath.FromSlash(p) |
| 192 | if filepath.VolumeName(canon) != "" { |
| 193 | return "", fmt.Errorf("path %q begins with a native volume name", p) |
| 194 | } |
| 195 | return canon, nil |
| 196 | } |