Long Paths in .NET, Part 3 of 3 [Kim Hamilton]
Updated 6/10/08 2:20pm: clarified details of proposed solution
Here it is, Part 3 of the long path series, which started over a year ago. I apologize for leaving you hanging; the BCL team has been busy lighting up the web. Because of the delay, I’ll summarize the compatibility concerns as context for the proposed solution.
Summary of Compat Concerns
Recall from Part 1 that one way to bypass the MAX_PATH limit with Win32 File APIs is to prepend \\?\ to the file name. This allows you to create paths that are only subject to NTFS restrictions, and the length limit is 32K. However, \\?\ has another side-effect -- it bypasses all Win32 file name canonicalization.
BCL gets a lot of requests for long path support. Some specifically request that we allow the \\?\ prefix. This brings up the question: is \\?\ requested because it allows longer paths, or do users also want to create paths that don’t conform to Win32 naming conventions? Our investigation indicates that, while there are specialized areas where non-canonical file names are useful, the overwhelming majority of users just want longer paths.
Why is this distinction important? If you just want longer paths, you don’t necessarily want the side effect of turning off all Win32 file naming conventions. For example:
- ‘/’ will no longer be converted to ‘\’ ; instead it’s part of the file name
- Trailing white spaces will no longer be removed; they’re also part of the file name
- You can create file names with reserved device names.
File names like this are problematic for other apps (independent of the path length: using \\?\ you can create a file name shorter than MAX_PATH that doesn’t adhere to Win32 naming conventions), to the extent that (I expect) a great majority of users will want the framework to enforce canonicalization, at least as the default behavior.
Note that the above statements are a commentary on unbridled use of \\?\. The problem could still be resolved as follows: behind the scenes we first canonicalize the path using GetFullPathName (since GetFullPathName isn’t subject to the MAX_PATH restriction) and then prefix\\?\. Perhaps non-canonical names could be allowed on an opt-in basis.
Either way, suppose .NET lets you create paths up to 32K in length. Now you have a new problem: you have a file that, most likely, no other app on your system can use. It would have to support the \\?\ syntax. Furthermore, many .NET APIs won’t even be able to work with this file: recall from Part 1 that this syntax only works with the Win32 file-related APIs, but not for general Win32 functions that accept paths (e.g. LoadLibrary).
This blog series has focused fairly heavily on nuances of the \\?\ prefix, simply because it’s commonly viewed as the workaround to the MAX_PATH limitation. Let’s switch focus to some reasonable goals in the absence of a unified solution (exposed, for example, by Win32 APIs).
- Reduce the incidence of hitting path length limit for opening existing files in a compatible way; i.e. the path can only be modified into a form that Win32 APIs accept as legal.
- For users that need consistent access to long paths, even if that involves bypassing Win32 restrictions, allow access to longer/non-canonical (tbd) file names.
Because of the compat concerns with Goal 2, we don’t want users to “accidentally” use this solution.
Fortunately, the Vista shell has provided a precedent of allowing longer path names in a compatible way. It’s called auto-path shrinking and it attempts to squeeze a file name into MAX_PATH by shrinking the long file names into the short name equivalents piecewise behind the scenes. Before describing that, note that the proposed solution is a hybrid approach:
1. Try to squeeze the file name into MAX_PATH characters using auto-path shrinking. Only used for existing files, and paths that don't have the file:///?\ prefix (see below)
2. Allow use of the file:///?\ prefix for creating as well as opening (in general allow this for every operation corresponding to a Win32 file API that supports this). We will not attempt to add the file:///?\ prefix behind the scenes; at most we'll provide a helper to perform such as AddLongPathPrefix. In any case, the user must intentionally request this and not stumble into using file:///?\ by accident. This part is TBD: we think it makes sense to expose as an option whether we should always enforce other Win32 file name restrictions other than length, and enforcing file naming rules would be the default.
Let's describe auto-path shrinking a bit more. If you pass in a file name that exceeds MAX_PATH:
It will try to shrink it under the MAX_PATH limit by using the short name equivalents:
This solution may seem odd at first (beyond the ironic spin that we’re coming full circle to short file names). But it’s very compelling for adoption in the framework since paths of this form are acceptable to Win32 API (it’s a valid Win32 file name).
Some important clarifications:
- Note that this solution wouldn’t require you to actually use short file names. When you create a file on an NT-based system, a short name is generated and associated with it*. You would be able to pass “normal” (long file name) file names to .NET APIs, and the short name conversion would happen being the scenes. (Just wanted to make it clear that we’re not reverting to the pre-Windows 95 days.)
- Right now, you can’t use short names to .NET APIs to get around the MAX_PATH restriction. During canonicalization, we expand short names to long names, and throw a PathTooLongException if the long name exceeds MAX_PATH.
* This brings up two questions. One is that users can turn off short file name generation via a registry value. This is discussed below. Also, you’ll notice this solution is NT-focused, but Silverlight can run on Macs. We also intend to handle platform-specific path limits with long path efforts, instead of enforcing Windows MAX_PATH (as we do currently).
Allowing use of file:///?\ will likely require a permission demand greater than FileIOPermission, perhaps even full demand for full trust. However, for many apps that need to work with long paths, this isn't a problem. We should investigate ways to relax this demand for partial trust scenarios like isolated storage.
Let’s look at some pros and cons of auto-shrinking:
- Doesn't work if user has turned off short file names. (But this is uncommon.)
- Given that shrinking is happening behind the scenes, it could make path length issues even more confusing to users. \\?\ allows a consistent higher limit of 32K. Examples and explanations are given below.
- A path name that can be shrunk into MAX_PATH as above is accepted by any Win32 API (clarification: assuming the name is given to the Win32 API in shrunk form. Win32 APIs do not perform auto-shrinking)
- This is an appealing "try to make it work" solution (if the user hasn't provided file:///?\). Until there’s a standard solution in the Win32 APIs, it’s better from a maintenance and usability perspective if we’re all using a similar approach.
We’re curious to hear your feedback about this approach.