Skip to content
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

Filenames and directories truncated to 30 characters #2706

Closed
avengerx opened this issue Jun 11, 2022 · 9 comments
Closed

Filenames and directories truncated to 30 characters #2706

avengerx opened this issue Jun 11, 2022 · 9 comments
Labels
Bug Decompiler The decompiler engine itself

Comments

@avengerx
Copy link

avengerx commented Jun 11, 2022

Input code

Just make a simple project with an embedded resource file name with more than 30 characters, example:
mylongfilenameherebuthereandherealso.txt

Erroneous output

The file will be output as:
mylongfilenameherebuthereandhe.txt (if I counted right, 30 characters)

Details

  • Product in use: ILSpyCmd (dotnet tool),
  • Version in use: 8.0.0.7047 (prerelease, pulled from latest master build workflow)

This was also reproduced in 7.2.0.0.rc version of the Our Linux/Mac/Windows ILSpy UI based on [Avalonia](http://www.avaloniaui.net/) - check out https://github.com/icsharpcode/AvaloniaILSpy (found in README.md

Also reproduced in the windows GUI I downloaded, version: ILSpy version 7.2.1.6856. (sorry, I'm not sure, was it from MS Store?.. not sure... it works better than AvalonUI one).

Besides embedded resource filenames, for instance, if we don't --nested-directories, and the namespace goes beyond 30 characters, the same happens to the output directory name: truncated at 30 characters. The actual namespace in the output file is complete.

While all links in the end project works well, in case the project has a reference or path to that embedded resource (if using string/reflection for instance), that would break the decompiled code (thus I believe this is a bug).

@avengerx avengerx added Bug Decompiler The decompiler engine itself labels Jun 11, 2022
@avengerx
Copy link
Author

I see... I ... think... I see.. :P

But if HKLM/System/CurrentControlSet/Control/FileSystem/LongPathsEnabled is 0 on windows (95+?) this just means file name length is limited to 260 characters, not just 30. When this setting is 1, it means Windows FS will support filenames with over 260 chars. That's what I read here: LongPathsEnabled in Windows docs Q&A site

For the long-term record, a summary of the question:

"Is the registry setting [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem] LongPathsEnabled supported in Windows Server 2019? Is it supposed to work?"

And the answer:

"By setting the registry key LongPathsEnabled to 1, most of the common Win32 file and directory functions will be able to use file paths exceeding 260 characters.

However, with the option enabled, Explorer still fails to use long file paths. Explorer is not long filepath aware. This is a by design behavior, as there are a lot of legacy APIs and DLLs which win32 applications depend on."

So I guess we should be good to use 255 always, when PlatformID.Win32NT.

Here's the actual documentation page on the registry key: Maximum Path Length Limitation

And according to that doc page, when the registry key is enabled, the path length limitation is raised to 32,767.

avengerx pushed a commit to avengerx/ilspy that referenced this issue Jun 11, 2022
On Windows, perhaps since Windows 95, the maximum length of file names
was 260 characters. For windows' special LongPaths setting, the limit
ranks up to 32,767 characters (according to Microsoft documentation,
untested).

By looking up the original commit and issue (icsharpcode#2038),
that introduced the 30 characters limit, it was not clear what reason
was behind the limit.

Related GitHub issue: icsharpcode#2706.
avengerx pushed a commit to avengerx/ilspy that referenced this issue Jun 11, 2022
On Windows, perhaps since Windows 95, the maximum length of file names
was 260 characters. For windows' special LongPaths setting, the limit
ranks up to 32,767 characters (according to Microsoft documentation,
untested).

By looking up the original commit and issue (icsharpcode#2038),
that introduced the 30 characters limit, it was not clear what reason
was behind the limit.

Related GitHub issue: icsharpcode#2706.
@siegfriedpammer
Copy link
Member

siegfriedpammer commented Jun 12, 2022

But if HKLM/System/CurrentControlSet/Control/FileSystem/LongPathsEnabled is 0 on windows (95+?) this just means file name length is limited to 260 characters, not just 30. When this setting is 1, it means Windows FS will support filenames with over 260 chars.

First of all this feature was added recently to Windows 10, not Windows 95 and later. Windows 10 Version 1607 (Anniversary Update), internal version 10.0.14393, released in 2016.

I think you are confusing two things here: "path length" and "file/directory name length", or as it is called by Jeremy Kuhne: "segment length" (see also https://docs.microsoft.com/en-us/archive/blogs/jeremykuhne/net-4-6-2-and-long-paths-on-windows-10)

image

Before the update a path was limited to 260 characters. After the update this limit was raised, I believe to 2^15 (~32 000), however ILSpy treats it as "infinite" hence uses int.MaxValue.

However, the segment length is a filesystem limitation where a directory name or file name (NOT path) can only be up to 255 characters:

See also #2567 (comment)

Sorry, for sounding harsh and rude, however, you are not the first person to report the file name truncation as being buggy and wrong, when in fact there is no good alternative, and I am a bit fed up with having to state the same facts and limitations over and over again. Sorry!

@dgrunwald
Copy link
Member

There are multiple types of long path support in Windows.
The ancient variant (afaik since Windows NT, but not on Windows 9x) requires the program to escape the paths using kernel path syntax "\\?\c:\...". Note that the kernel path syntax also deactivates some other forms of normalization normally performed by Win32; which means the program will have to reimplement those. Also I believe there were some restrictions in .NET framework that prevented the use of kernel path syntax in .NET, but those might no longer apply to current .NET versions.

The new variant (since Windows 10 Version 1607) only requires setting the registry key, nothing else. The program can just use normal paths.

Since ILSpy doesn't use kernel path syntax, you will need to set the registry key to enable long path support. We didn't add the registry check for fun; users without the registry key set were reporting crashes due to the total path being too long, e.g. even on Windows 11: #2622

@avengerx
Copy link
Author

Ok, I got mixed up with the "segment" and "path" lengths. But still, 30 is too little for my use case. Windows 10, without
LongPathsEnabled. There must be a more reasonable value for that limitation. In my case, the longest file name with extension was 47 characters, and it works like a charm.

I don't know how 30 was decided upon but it simply is not realistic. Perhaps you should, instead limit the file name length to 255 or 260 -and- also check if the full path exceeds the limit before truncating it?

@avengerx
Copy link
Author

This statement from the very docs on the long path feature hints on a bit more limitation:

"When using an API to create a directory, the specified path cannot be so long that you cannot append an 8.3 file name (that is, the directory name cannot exceed MAX_PATH minus 12)."

That could have been another reason 255 (neither 260) worked on Windows. It should instead be 260 - 12 = 248? I guess I can create a simple project and check that out.

@dgrunwald
Copy link
Member

The problem is that we only limit individual segments, but would need to also limit the full path.
But that gets complicated quickly (we can't just truncate the full path, we'd need to distribute the available space in the path to among the different segments).

But I think the upgrade to .NET 6 might save us here: Unlike .NET framework 4.x, the new .NET seems to internally convert normal paths to kernel paths: dotnet/corefx#3001
Thus the registry key is no longer relevant and we could always use the long path support.

avengerx pushed a commit to avengerx/ilspy that referenced this issue Jun 12, 2022
It will now use 258 as maximum path length in windows, and also
258 - 12 (246) for file name (12 stands for the 8.3 file name
space reservation required).

For now, we'll throw an exception as truncating file names should
be avoided, and decide if specific settings to enable truncation
should take place.

Changes the fallover maximums to a smaller value yet being generous
about the limits.

Related GitHub issue: icsharpcode#2706.
avengerx pushed a commit to avengerx/ilspy that referenced this issue Jun 12, 2022
This changes the ProjectWriter class so that files created by it are
validated using DirectoryInfo and/or FileInfo classes.

Also adjusts hardcoded path lengths to:
- use actual maximum windows' LongPaths feature allows (32767),
- specify exact path part character limit (258-12), which reserves
  characters for the 8.3 legacy file name format,
- increases unsupported platforms' limit to 200 characters instead of 30

Now WholeProjectDecompiler class will always check for full path while
trying to create files to properly validate maximum path length exceeds.

The hardcoded maxPathLength value (258) was tested to its edge cases, in
which Visual Studio 2022 would allow a value of up to 259, whereas Notepad++
and WordPad won't open files with paths that long.

Arbitrary values would also work (as far as DirInfo doesn't throw an
exception, but most Windows applications wouldn't be able to handle the
files.

Related GitHub issue: icsharpcode#2706.
@avengerx
Copy link
Author

Okay, I gave it another try, changing it to validate the full path, and expand the output path to absolute when the class is instanced. I have tested its edge cases on Windows without long path support and it worked like a charm. This approach would help me split the embedded resources also in paths, albeit this is very prone to errors, if it proves problematic... I'll create an issue for the embedded resources if I can't find an already open one.

avengerx pushed a commit to avengerx/ilspy that referenced this issue Jun 14, 2022
This changes the ProjectWriter class so that files created by it are
validated using DirectoryInfo and/or FileInfo classes.

Also adjusts hardcoded path lengths to:
- use actual maximum windows' LongPaths feature allows (32767),
- specify exact path part character limit (258-12), which reserves
  characters for the 8.3 legacy file name format,
- increases unsupported platforms' limit to 200 characters instead of 30

Now WholeProjectDecompiler class will always check for full path while
trying to create files to properly validate maximum path length exceeds.

The hardcoded maxPathLength value (258) was tested to its edge cases, in
which Visual Studio 2022 would allow a value of up to 259, whereas Notepad++
and WordPad won't open files with paths that long.

Arbitrary values would also work (as far as DirInfo doesn't throw an
exception, but most Windows applications wouldn't be able to handle the
files.

Related GitHub issue: icsharpcode#2706.
avengerx pushed a commit to avengerx/ilspy that referenced this issue Jun 14, 2022
This removes the 30 character long file names truncating limitation by
improving Windows checks against the path and adding another layer of
validation to ensure the full path would produce reachable files on
Windows.

- Expands 'TargetDirectory' to absolute path using System.IO.DirectoryInfo
- Replaces file writing calls with path validating helpers:
 - File.Copy => CopyFile
 - new FileStream => MakeFileStream
 - new StreamWriter => MakeStreamWriter
 - File.WriteAllBytes => WriteBytesTo
 - Directory.CreateDirectory => CreateDir
  This should avoid future changes from forgetting to validate the path.
- Implements Validation that:
 - Requires rooted paths
 - Parses path using System.IO.FileSystemInfo
 - Computes Windows' 8.3 path space reservation while validating
   directories
 - Checks parsed path against hardcoded path limits from
   GetLongPathSupport()
 - Ensures the parsed path is within TargetDirectory (protects against
   '../' paths and Path.Combine with rooted paths)

Related GitHub issue: icsharpcode#2706
@avengerx
Copy link
Author

But I think the upgrade to .NET 6 might save us here: Unlike .NET framework 4.x, the new .NET seems to internally convert normal paths to kernel paths: dotnet/corefx#3001 Thus the registry key is no longer relevant and we could always use the long path support.

This update might be working, yes, I get it to make longer paths without registry settings... The problem is that almost no other application can open those paths... So perhaps this should be something configurable.

Regardless of that, just having the limitation up to ~250 characters, and no longer trimming file names with just 30 characters is a huge improvement that should work in most cases!

@avengerx
Copy link
Author

https://github.com/dotnet/runtime/blob/44b44501c76c46bd79ee52b7d9a9d8a4957fc85f/src/libraries/System.IO.FileSystem/tests/FileStream/ctor_options.Windows.cs#L89

This could be an interesting approach to get specific file system's path limit, once we already have the TargetDirectory resolved to a disk/mount point. (windows-specific). But this would still leave the issue with long paths not being able to be opened by the overall applications on the system.

clin1234 pushed a commit to clin1234/ILSpy that referenced this issue Jun 26, 2022
…cters.

With the built-in support for long paths in .NET 6.0, we no longer need to check for the registry key. The only limitation that remains is maxSegmentLength, which seems to be 255 on all commonly used file systems/all platforms. Also there is no need to differentiate between Windows and other platforms.

- Windows Explorer in Windows 10 seems to be fine with files generated by ILSpy that have names longer than 260 characters.
- Notepad++ and other applications seem to use 8.3 path syntax to access the file.
- Visual Studio 2022 does not like the long path names, affected users should raise an issue with MS. ILSpy generates proper paths.
clin1234 pushed a commit to clin1234/ILSpy that referenced this issue Jun 26, 2022
…cters.

With the built-in support for long paths in .NET 6.0, we no longer need to check for the registry key. The only limitation that remains is maxSegmentLength, which seems to be 255 on all commonly used file systems/all platforms. Also there is no need to differentiate between Windows and other platforms.

- Windows Explorer in Windows 10 seems to be fine with files generated by ILSpy that have names longer than 260 characters.
- Notepad++ and other applications seem to use 8.3 path syntax to access the file.
- Visual Studio 2022 does not like the long path names, affected users should raise an issue with MS. ILSpy generates proper paths.
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 14, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Bug Decompiler The decompiler engine itself
Projects
None yet
Development

No branches or pull requests

3 participants