swift-foundation
swift-foundation copied to clipboard
`URL(filePath:)` should resolve Windows drive-relative paths
When a URL is initialized with a Windows drive-relative path like URL(filePath: "S:relative/path"), we need to check the current working directory of the S: drive (not necessarily the cwd of the process) and use that as the base URL. Then, we should strip the S: from the relative path so that it resolves correctly against the base path.
E.g. if the current working directory of the S: drive is /current/directory/, we would get
let url = URL(filePath: "S:relative/path")
print(url.relativePath) // "relative/path"
print(url.baseURL?.path) // "S:/current/directory"
print(url.path) // "S:/current/directory/relative/path"
@swift-ci please test
@compnerd I updated the behavior to use PathIsRelativeW. On Windows, URL(filePath:) will now behave as follows for the following inputs:
/path - Assume this is a POSIX path for compatibility, and do not immediately resolve it to its current drive. (This was the behavior in the SCL-F implementation, too.) If a user passes URL.path to Windows file system APIs, it will then be resolved against the current drive.
\path - PathIsRelativeW returns TRUE. Call GetFullPathNameW to resolve this to an absolute path from the current drive.
path - PathIsRelativeW returns TRUE. Store this as a relative path. If no base URL was provided, set the base URL to the current directory.
C:path - PathIsRelativeW returns FALSE. Call GetFullPathNameW to resolve this to an absolute path from C:\.
C:\path - PathIsRelativeW returns FALSE.
\\?\C:\path - PathIsRelativeW returns FALSE.
A relative path like path is the only case where we'll explicitly store a relative path and not call GetFullPathNameW to resolve to an absolute path. Otherwise, we'll always have a resolved absolute path after URL initialization. Do you have any feedback on this approach? Thanks!
I think that the approach seems right. The one thing that I do wonder about is the behaviour with symlinks/junctions. What happens on Linux there? Do we need to peer through any symlinks in the path?
The one thing that I do wonder about is the behaviour with symlinks/junctions. What happens on Linux there? Do we need to peer through any symlinks in the path?
On macOS/Linux we don't resolve any symlinks in the path before storing it (we do expand ~ to the user's home directory), but we'll follow symlinks to check if a file exists at that path if the .checkFileSystem flag is passed. GetFullPathNameW does resolve symlinks, correct? So there'd be a bit of a difference between OSes, but it might be reasonable?
No, I don't think that GetFullPathNameW resolves all symlinks and junctions or substs. I think that you need to use GetFinalPathNameByHandleW for that.
Oh, gotcha, then I think we should be good and that behavior will be the same on Windows/MacOS/Linux
@swift-ci please test