swift-foundation icon indicating copy to clipboard operation
swift-foundation copied to clipboard

`URL(filePath:)` should resolve Windows drive-relative paths

Open jrflat opened this issue 1 year ago • 7 comments
trafficstars

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"

jrflat avatar Nov 12 '24 23:11 jrflat

@swift-ci please test

jrflat avatar Nov 12 '24 23:11 jrflat

@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!

jrflat avatar Dec 18 '24 20:12 jrflat

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?

compnerd avatar Dec 18 '24 20:12 compnerd

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?

jrflat avatar Dec 18 '24 20:12 jrflat

No, I don't think that GetFullPathNameW resolves all symlinks and junctions or substs. I think that you need to use GetFinalPathNameByHandleW for that.

compnerd avatar Dec 18 '24 21:12 compnerd

Oh, gotcha, then I think we should be good and that behavior will be the same on Windows/MacOS/Linux

jrflat avatar Dec 18 '24 21:12 jrflat

@swift-ci please test

jrflat avatar Dec 18 '24 21:12 jrflat