I received a slew of inquiries lately regarding MAX_PATH and the implications it has on various Citrix and non Citrix user profile management systems.  The example case is Citrix Application Streaming combined with Microsoft Roaming Profiles and a customer report that the user profile save/restore craps out on the RadeCache contents which are in their case deeper than 260 characters.   The end guidance is to switch to Citrix Profile Management and this problem will go away.  Here is the long story.

Background

Application Streaming implements isolation via redirection to various layers of glass.

Notice that an isolated application attempt to write to a read-only space will be redirected to the first writable space, which is always on the top.  The writable layer is per-user by definition and is stored beneath %APPDATA%\Citrix\RadeCache.

As an example, an isolated application attempted write to

  • “C:\Windows\Foo.ini” will actually land in
  • C:\users\username\AppData\Local\Citrix\RadeCache\11111111-2222-3333-4444-555555555555_1\Device\C\Windows\Foo.ini

In the above, the repeated numbers are the GUID that identifies the execution target and here, I listed out all of the characters to bring attention to the number of characters in a GUID.

Assuming an 8 character username, the above reference to an 18 character filename ends up being a 112 character reference to space inside the user profile.  This means that the additional characters added because of isolation is 112 – 18 = 106 characters.  When comparing this to 260, that’s a big difference.

Is that a problem?

Going back to the original application streaming development when GUIDs were defined as the mechanism to uniquely identify isolation targets, we were immediately concerned over the length of the string to the per-user layer and the length of the GUID.  We considered GUID vs. “some other identifier” and actually considered using Integer numbers to identify execution targets, partially because they are “shorter”, but ultimately concluded that the requirements to not get isolation spaces mixed up required something reliably unique.

GUIDs are custom built for uniquely identifying things and no matter what, the per-user layer would extend beyond MAX_PATH.

Profile Name was also considered, but it cannot be used for a number of reasons.  In this case, it wouldn’t help either because the profile name string is also long.

History review – MAX_PATH

Back in the days of Windows for Workgroups 3.11, there was a system wide limit to the maximum PATH which could be used by application programs.  This length is defined in the Windows SDK Headers as %define MAX_PATH 260 and it is still with us in Windows 7.

MAX_PATH comes from a FAT16 file system defined limit of 256 characters + the drive identifier, “C:\”.  256 + 3  is 259, then add an extra character for the ASCII nul terminator.  Notice that this really means that the maximum path is more accurately described as 259 characters, while we always title max path as 260 to match the Windows headers and the common practice of defining strings in code to hold filenames.  Example:

  • char szFileName[MAX_PATH]

This would be more accurately coded as #define MAX_PATH 259 and coded the string allocate as

  • char szFileName[MAX_PATH + 1]

The later here would be more “correct”, but 20 years of programming history says that MAX_PATH is 260 and we code it as [MAX_PATH].

As a side note, it is more complicated than MAX_PATH.  There is a limit of the maximum length of a filename vs. the maximum length of a PATH.  Here’s a Microsoft KB that discusses this in some detail.

Another side note is that I believe the stated limit of “256” for FAT and Windows is really 255 as this is the largest number that can be stored in 8-bits.  Let’s get back on topic.

Windows is merely one operating environment of Windows NT

Windows “New Technology” has a much more impressive file system than Windows 3.11.  NTFS can support paths to the limits of UNICODE_STRING in the kernel, which is 32767 double-byte characters.  In kernel, all characters are double-byte and since UNICODE_STRING uses Pascal string style there is no need for a terminating nul.

Microkernel

Windows NT is a microkernel based system.  Much like hypervisors today, the microkernel provides it’s native API and various operating environments can be laid down on top.  “Nt” is the native API.  WINDOWS is merely ONE possible environment.  I understand that Windows 16 was implemented inside of Windows 32, but in concept, Windows 16, Windows 32, Windows 64, Posix and yikes! OS/2, would all be implemented as separate operating environments running on top of the Windows Nt native system.

What’s the point?  The point is that Windows only understands “MAX_PATH” as a length of string.  Read that again carefully – the overloading of names here is designed to confuse people.  The Windows operating environment only understands MAX_PATH, but the Windows NT operating system understands 32767.

We run programs in the Windows environment.  We implement system stuff utilizing the native Windows NT, and these support 32767.

Windows access to long paths

Windows applications running inside the Windows operating environment CAN utilize paths longer than MAX_PATH.  If a programmer prefixes the filename with “\\?\”, then the Win32 API that handles the file access, CreateFile for example, will directly pass the given string to the Nt layer without MAX_PATH dependent filtering.  Similar checks occur for pretty much all of the Win32 APIs.

Back to App Isolation and User Profiles

Given Application Virtualization redirects file operations from a specified location to an isolated location, the “destination” path will always be DEEPER than the source.

The use of GUID exacerbates the problem, but no matter how short, once we put the per-user layer of isolation into per-user space (where it must be), the depth of that directory will be deeper than the original and will be a much longer string.  The string starts with “C:\users\username\appdata\local\citrix\radecache”

Wearing an Application Streaming hat, a few things must be true.

  • The application use of path cannot be restricted as compared to what the application could use when it is locally installed.  The application will be “unaware” that it is really accessing a path deeper than it knows how to handle.
  • The application virtualization system absolutely must be able to handle paths deeper than MAX_PATH.  If you’re auditing code, any time you see static allocation of strings to hold filenames, alarm bells should go off.

Wearing a Profile Management hat, a few things must be true.

  • When sync %USERPROFILE% to/from central store, the APIs used for the copy must include the Nt prefix of “\\?\”.
  • Code used for just in time populate must support NTFS length.

The recent reports say that the failure occurs on syncing the user profile to/from user store and that this failure happens when copying the RadeCache space, which is long filenames.

Investigation revealed that the failing systems are using Windows base operating system Roaming Profiles, which apparently cannot handle filenames or paths beyond MAX_PATH.

Citrix Profile Management

One of the first things I did when we purchased Profile Manager from Sepago was have the Citrix dev and test teams ensure that the system can handle syncing of filenames longer than MAX_PATH. This was a about 3 years ago.  With the recent inquiries, I checked with Citrix Profile Manager team and they confirmed that YES, it happily handles filenames/paths deeper than MAX_PATH.   It is good when things actually work.

What guidance to give

A change to App Streaming to use smaller strings won’t help.  No matter how small, there will always be a string that an application can create that will be close to 260 characters, which when adding path to the user profile to the front will always exceed MAX_PATH.

Changing App Streaming to store the per-user layer of isolation inside a container file such as a VHD or ZIP would conceptually help, but it would create so many other problems, it isn’t a possibility.

Changing MS Roaming Profiles to support long paths would help, but that’s a long time activity and is outside of my world.

The only guidance I can give is to have the customer move from Roaming Profiles to Citrix Profile Manager and the problem will be resolved.  This though is a heavy solution to what is conceptually a small problem.

Joe Nord

Citrix Systems Architect

Profile Manager and Application Streaming