App-V and Legacy NTFS Junctions

I was having problems recently sequencing an application named SolarWinds Toolset; it appeared to be ignoring the data stored under C:\ProgramData captured during sequencing and instead reading/writing files to this location outside of the virtual environment.

A bit of Procmon analysis showed that the application was trying to read from C:\ProgramData\Application Data. On Windows Vista and 7, this is not an actual folder but an NTFS junction; a reparse point so that (badly written) legacy apps designed for Windows XP can be redirected to write to the correct location. NTFS junctions are similar to file/folder shortcuts that you may see on your desktop, but they are put in place all over Windows 7 to aid application compatibility after Microsoft changed the folder structure since Vista. You can easily view just how many of these there are on your system by running the following command:

dir /s /al c:\

Applications are supposed to query Windows APIs to determine exactly where the per-user and per-machine app data locations are. But often developers use alternate methods, such as %ALLUSERSPROFILE%\Application Data and %USERPROFILE%\Application Data. On XP these folders would equate to C:\Documents and Settings\All Users\Application Data and C:\Documents and Settings\USERNAME\Application Data. After changing the folder structure since Vista, NTFS junctions are used to redirect attempt to write to these location to the correct place.

For example, say an application tries to write to: %ALLUSERSPROFILE%\Application Data on Windows 7, which actually would prefer you to store per-machine appdata to C:\ProgramData. To achieve this, %ALLUSERSPROFILE% is set to C:\ProgramData, and in there is an NTFS junction shortcut Application Data, which also points to C:\ProgramData. All of this redirection goes on transparently behind the scenes and it all works wonderfully.

Except if you try to run this application inside App-V!

You can try the following test for yourself on either App-V 4 or 5. Here is a simple batch file that tries to read and write to these junction points:

@echo off

echo Program data:
type "%ALLUSERSPROFILE%\Application Data\Test\config.txt"

echo User data:
type "%USERPROFILE%\Application Data\Test\config.txt"

echo.
echo Updating data files...
echo.

IF NOT EXIST "%ALLUSERSPROFILE%\Application Data\Test\config.txt" (
md "%ALLUSERSPROFILE%\Application Data\Test"
echo This file stores program data >"%ALLUSERSPROFILE%\Application Data\Test\config.txt" )

IF NOT EXIST "%USERPROFILE%\Application Data\Test\config.txt" (
md "%USERPROFILE%\Application Data\Test"
echo This file stores user data >"%USERPROFILE%\Application Data\Test\config.txt" )

echo Program data:
type "%ALLUSERSPROFILE%\Application Data\Test\config.txt"

echo User data:
type "%USERPROFILE%\Application Data\Test\config.txt"

pause

This simply does the following:

  1. Tries to read 2 config files using the %ALLUSERSPROFILE%\Application Data and %USERPROFILE%\Application Data locations.
  2. If they do not exist they are created.
  3. Then it tries to read the config files again.

This mimics simple application behaviour where it will try to read its config files and create default ones if none exist.  Running this locally, we see this on the first run as expected:

firstrun

Then on each successive run the script can find the files both times:

secondrun

Now, you can sequence this batch file and create a shortcut to it.  On App-V 5, you will also have to craft your shortcut to run cmd.exe /c <path to batch file>, since shortcuts directly to batch files run outside of the virtual environment by default (see here for a previous post detailing this).  You can launch the app as many times as you like during sequencing and the same result as above will be displayed.  However, on first launch on the App-V client, the result from the first window will be displayed; the script cannot find the files in the requested location.  It then creates them and can read them again, but it appears that this redirection has pointed to the folders outside of the virtual environment and the files are created on the local file system instead.

I believe the reason for this is as follows. App-V only virtualises subfolders under the common VFS folders that are created or written to during sequencing. So if your app creates %APPDATA%\Test during sequencing, it will be captured and virtualised, and all reads and writes to this location will be redirected to the user’s PKG file in App-V 4, or the user locations under their profile in App-V 5. However, if in later use on the client the application tries to create a folder %APPDATA%\Test2, App-V will allow these file operations to take place on the local file system since that folder was not captured during sequencing. Since the subfolder Application Data was not captured during sequencing as the location does not really exist, App-V does not attempt to virtualise or redirect access to this location.

Hopefully this will not affect too many applications since most should be using the ‘proper’ methods for determing the folder locations.  But the potential undesired impacts of this are:

  • If you configure an application during sequencing, it may not be able to find the configuration on client launch.  This could either cause the application to crash, or for it to recreate default settings files.
  • If you have two conflicting applications, such as two different version of the same product, they could both be trying to write to the same location with unintended results.
  • For multi-user systems or terminal servers, one user can change the application configuration which will also affect other users.

I have found a workaround  for this problem, which could help resolve these issues if they are affecting you.  On your sequencer, before monitoring, delete any of the affected NTFS junctions (you will need to enable the viewing of hidden and system files in Explorer) and recreate them as real folders.  Then in the example above, the sequence would actually capture the folder C:\Programdata\Application Data\Test, and this would then read and write correctly on the client.

This fix didn’t work for the application I was working on originally since it contained many components, some of which use the proper methods for determining the folder locations, and some which didn’t – resulting in the data being split across two locations, which it did not like.  Microsoft’s Application Compatibility Toolkit may be a solution in these dire cases where you need to force the application to redirect to an alternate location!

Leave a Reply