Skip to content

long paths not supported on Windows for openFile, etc #39

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
joeyh opened this issue Jan 21, 2025 · 4 comments
Open

long paths not supported on Windows for openFile, etc #39

joeyh opened this issue Jan 21, 2025 · 4 comments

Comments

@joeyh
Copy link

joeyh commented Jan 21, 2025

openFile from this package appears that it will fail on Windows if a filename is longer than 285 characters or so, which is a default limit in Windows. This is surprising, because openFile from base has for several years avoided that length limitation. So a program that is able to operate on long filenames on windows, if converted from base to file-io, will subtly break.

The openFile implementation here calls directly through to createFile from Win32. Win32 is a direct mapping to the win32 api, which has this filename length limitation.

To avoid the length limitation, base's openFile converts the path to a "UNC-style" path first. That conversion is how ghc is generally supporting long filenames on windows. See create_device_name in ghc's utils/fs/fs.c. And other libraries do too. directory-1.3.9.0 contains furnishPath :: OsPath -> IO WindowsPath which I think could be used in file-io as well to relatively easily fix this.

(Note that I have not actually verified this by running openFile on windows, only by inspection. I suspect other functions like withFile have the same problem too, but have not checked.)

@hasufell
Copy link
Member

hasufell commented Jan 22, 2025

prepending \\?\ does a little more than just circumvent MAX_PATH, so I think this is actually questionable practice. See https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats#path-normalization

Rust discussion: rust-lang/cargo#9770.

Some notable caveats:

  • . and .. are not treated specially (like you would usually expect) when using \\?\
  • / separators are "invalid" and also treated as directory/filenames
  • the current directory is always limited by MAX_PATH unless a manifest file is used

So you end up re-implementing part of the normalization.


On the other hand... deviating from base isn't something we really want.

@joeyh
Copy link
Author

joeyh commented Jan 30, 2025

If you take a look at the implementation in directory, it makes the path absolute, which avoids the issues with "." and "..".

https://github.com/haskell/directory/blob/eb40bbebcaf86153bbc60772fb2e0466d35c95c4/System/Directory/Internal/Windows.hsc#L329

Also openTempFile, if passed a mangled path, will return a filename that also starts with that mangled path. In my experience that can lead to breakage when that file is used in places that don't support paths like this, and so the returned filename needs to be converted to be relative to the original input path instead.

@hasufell
Copy link
Member

If you take a look at the implementation in directory, it makes the path absolute, which avoids the issues with "." and "..".

https://github.com/haskell/directory/blob/eb40bbebcaf86153bbc60772fb2e0466d35c95c4/System/Directory/Internal/Windows.hsc#L329

Yes, I'm aware and it is bad, because it re-implements part of the Win32 path normalization (or: the documented parts, at least).

The main motivation of OsPath is to stop messing around with paths. We're partially subverting that goal here.

I'm thinking to provide a flag enable-long-paths for it that is on by default. Then power users can disable it and handle the matter correctly in their application.

hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
hasufell added a commit that referenced this issue Mar 8, 2025
@hasufell
Copy link
Member

hasufell commented Mar 8, 2025

#40

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants