Skip to content

Fixup: FileRedirectionFixup

Tim Mangan (MVP) edited this page Aug 2, 2022 · 8 revisions

The FileRedirectionFixup (FRF for short) is a workhorse fixup library that intercepts a wide variety of Windows API calls that involve the Windows file system and may need help under MSIX.

The general causes of issues resulting in needing the FRF include:

  • Files that are part of the package under either the 'VFS\AppData' or 'VFS\LocalAppData' folders are not seen by the application. On Windows 10 versions prior to 2004 (20H1) this would also happen to package files under 'VFS\AppDataCommon'. This issue usually occurs when the application attempts to use the normal path that the application would use when natively installed. Unlike other VFS folders in the package, these folders are not correctly layered and the application will not see the files in the package without FRF assistance.
  • Files in other VFS folders of the package that the application needs to open for modify. The FRF can assist with this.
  • Files in non VFS folders of the package that the application needs to open for modify. In addition to solving this issue by using the FRF, Microsoft introduced a new AppXManifest option to solve this also.
  • Accesses that may work improperly due to other redirection. The FRF will apply Reverse VFS and Reverse Redirection logic when necessary.

When the FRF is used, as files are accessed by the application, an evaluation will be made as to if there is a source for the file inside the package and cause a Copy-on-Access to be made into the MsixWritablePackageRoot folder of the user's LocalAppData folder for this package (if there isn't one already present). It will then complete the call by using redirection to this copy instead.

The FRF intercepts calls to Open and Find folders and files, however, it does not need to intercept the read/write/close associated with that file access and the intercepted call returns a handle to the application which is then used for those operations, ensuring that the activity will also affect the redirected file without additional interceptions.

The FRF currently targets the following Windows API Calls:

API API API API API
CopyFile CopyFileEx CopyFile2 CreateDirectory CreateDirectoryEx
CreateFile CreateFile2 CreateHardLink CreateSymbolicLink DeleteFile
GetFileAttributes GetFileAttributesEx FindNextFile FindFirstFileEx FindNextFile
FindClose GetPrivateProfileInt GetPrivateProfileSection GetPrivateProfileSectionNames GetPrivateProfileString
GetPrivateProfileStruct MoveFile MoveFileEx ReadDirectoryChangesW RemoveDirectory
ReplaceFile SearchPath SetFileAttributes GetCurrentDirectory SetCurrentDirectory
WritePrivateProfileString WritePrivateProfileStruct

Note 1: ReadDirectoryChangesW and ReadDirectoryChangesEx are intercepts that log details only at this time. Applications call these functions with a handle to a directory to monitor changes made under that directory. In most cases, the app would have received a handle to the redirected directory, which is the folder where any changes would be made. There is concern that it is possible for the app to receive a handle from the package, so this logging, which occurs in a minimal form in the release build, will alert you that this function is being used. Testing using the debug build will display the file path associated with this handle. If a handle to a wrong path is discovered, additional work to the fixup may be warranted (Please log an issue to this GitHub repository if you find one).

Note 2: Applications may use posix interfaces to the file system. Each of the functions we have analyzed so far seem to call APIs supported by the FRF. Thus at this time these functions are not being intercepted, If you find an app needing additional support due to posix interfaces, please add an issue to this GitHub repository. The Posix functions under consideration are in ucrtbase.dll, with interfaces such as OpenFile.

When Microsoft originally wrote the FRF, they assumed that an application might have just a single file or two that needs the fixup. Therefore, they created an overly complicated configuration that allows you to target just that one or two things. Our experience with running older applications in MSIX is that they tend to have a lot of different file issues and attempting to identify and target precision configurations is overly tedious as it is a serial process to find each and every process.

So in most cases we instead just configure the FRF to cover all of the general cases that we tend to run into without trying to identify them all individually. Such a blanket remediation technique has a historical precedent in Microsoft App-V where it worked well. While this blanket general configuration will look complicated in the following example, it is fortunately boilerplate text that may be used in just about any application that uses the FRF.

  "processes": [
    {
      "executable": "^PsfLauncher.*",
      "fixups": []
    },
    {
      "executable": ".*",
      "fixups": [
        {
          "dll": "VFS\\ProgramFilesX64\\FileZilla FTP Client\\FileRedirectionFixup.dll",
          "config": {
            "redirectedPaths": {
              "packageRelative": [
                {
                  "base": "",
                  "patterns": [
                    ".*\\.[eE][xX][eE]$",
                    ".*\\.[dD][lL][lL]$",
                    ".*\\.[tT][lL][bB]$",
                    ".*\\.[oO][cC][xX]$",
                    ".*\\.[cC][oO][mM]$",
                    ".*\\.[fF][oO][nN]$",
                    ".*\\.[tT][tT][cC]$",
                    ".*\\.[tT][tT][fF]$",
                    ".*\\.[zZ][iI][pP].*"
                  ],
                  "isExclusion": true
                },
                {
                  "base": "",
                  "patterns": [
                    ".*"
                  ]
                }
              ],
              "packageDriveRelative": [],
              "knownFolders": [
                {
                  "id": "FDD39AD0-238F-46AF-ADB4-6C85480369C7",
                  "relativePaths": [
                    {
                      "base": "",
                      "patterns": [
                        ".*"
                      ],
                      "isExclusion": "true"
                    }
                  ]
                },
                {
                  "id": "LocalAppData",
                  "relativePaths": [
                    {
                      "base": "",
                      "patterns": [
                        ".*"
                      ]
                    }
                  ]
                },
                {
                  "id": "RoamingAppData",
                  "relativePaths": [
                    {
                      "base": "",
                      "patterns": [
                        ".*"
                      ]
                    }
                  ]
                },
                {
                  "id": "ProgramFilesX86",
                  "relativePaths": [
                    {
                      "base": "",
                      "patterns": [
                        ".*"
                      ]
                    }
                  ]
                },
                {
                  "id": "ProgramFilesCommonX86",
                  "relativePaths": [
                    {
                      "base": "",
                      "patterns": [
                        ".*"
                      ]
                    }
                  ]
                },
                {
                  "id": "ProgramFilesX64",
                  "relativePaths": [
                    {
                      "base": "",
                      "patterns": [
                        ".*"
                      ]
                    }
                  ]
                },
                {
                  "id": "ProgramFilesCommonX64",
                  "relativePaths": [
                    {
                      "base": "",
                      "patterns": [
                        ".*"
                      ]
                    }
                  ]
                },
                {
                  "id": "SystemX86",
                  "relativePaths": [
                    {
                      "base": "",
                      "patterns": [
                        ".*"
                      ]
                    }
                  ]
                },
                {
                  "id": "System",
                  "relativePaths": [
                    {
                      "base": "",
                      "patterns": [
                        ".*"
                      ]
                    }
                  ]
                },
                {
                  "id": "Windows",
                  "relativePaths": [
                    {
                      "base": "",
                      "patterns": [
                        ".*"
                      ]
                    }
                  ]
                },
                {
                  "id": "ProgramData",
                  "relativePaths": [
                    {
                      "base": "",
                      "patterns": [
                        ".*"
                      ]
                    }
                  ]
                }
              ]
            }
          }
        }
      ]
    }
  ]

A summary of the processes section of the above example is:

  • A process entry for any PsfLauncher process such that it effectly exempts those processes for fixup dlls.
  • A process entry for any other process running in the container that has the FRF configured for injection.
  • The FRF confiration has a field called redirectedPaths that has three sub-fields that contain arrays of specific configuration. These subfields take effect in calls depending on how the application specified a requested file path and will be processed in this order (no matter which order they appear in the file): > * packageRelative is for calls using relative pathing from the package root folder or a working directory folder. > * packageDriveRelative is for calls using non-package paths from the drive letter (C:) that the package volume is on. This type is rarely used and is currently covered as an unrequested default case after knownFolders matching has been exhausted. > * knownFolders is for calls made using paths of known locations (often documented in developer docs as KnownFolderIDs). These known folders are identified often with the names used in VFS folders, however additional GUIDs are also referenced instead of by name.
  • The packageRelative entry includes an exclusion entry for typical file types that we want to exempt from copy-on-access. These are generally binary components that should not be modified ever. Sometimes we can enter exclusions for folders too.
  • The knownFolders entry includes exclusion entries for folder locations we typically do not want to redirect, such as the Desktop folder or (as in the example above) the user's Documents folder. This entry might be needed if, for example, the package includes a document file that should be seen by the app but any additional files written should go to the real documents folder and not redirected to the WritablePackageRoot.

Known Folders and VFS Paths

The MSIX, and/or the FRF use the following known list of VFS Path Names and KnownFolderIDs. VFS Path Names are used for packageRelative entries and FolderId/extraGuids are used for knownFolder entries:

Known VFS names in MSIX FolderId\extraGuids Typical path on x64 System
AppVSystem32Catroot2 FOLDERID_System\catroot2 C:\Windows\System32\catroot2
AppVSystem32Catroot FOLDERID_System\catroot C:\Windows\SysWOW64\catroot
AppVSystem32DriversEtc FOLDERID_System\drivers\etc C:\Windows\System32\Drivers\etc
AppVSystemreDriverstore FOLDERID_System\driverstore C:\Windows\System32\DriverStore
AppVSystem32Logfiles FOLDERID_System\logfiles C:\Windows\System32\LogFiles
AppVSystem32Spool FOLDERID_System\spool C:\Windows\System32\Spool
SystemX86 FOLDERID_SystemX86 C:\Windows\SysWOW64
ProgramFilesCommonX86 FOLDERID_ProgramFilesCommonX86 C:\Program Files (x86)\Common Files
ProgramFilesX86 FOLDERID_ProgramFilesX86 C:\Program Files (x86)
SystemX64 FOLDERID_System C:\Windows\System32
ProgramFilesCommonX64 FOLDERID_ProgramFilesX64 C:\Program Files\Common Files
System FOLDERID_System C:\Windows\System32
Fonts FOLDERID_Fonts C:\Windows\Fonts
Windows FOLDERID_Windows C:\Windows
Common AppData FOLDERID_ProgramData C:\ProgramData
Local AppData FOLDERID_LocalAppData C:\Users\UserName\AppData\Local
AppData FOLDERID_RoamingAppData C:\Users\UserName\AppData\Roaming
Common Desktop FOLDERID_PublicDesktop C:\ProgramData\Desktop
Common Programs FOLDERID_CommonPrograms C:\ProgramData\Microsoft\Windows\Start Menu\Programs
LOCALAPPDATALOW FOLDERID_LocalAppDataLow C:\Users\UserName\AppData\LocalLow
B4BFCC3A-DB2C-424C-B029-7FE99A87C641 C:\Users\UserName\Desktop
FDD39AD0-238F-46AF-ADB4-6C85480369C7 C:\Users\UserName\Documents
56784854-C6CB-462B-8169-88E350ACB882 C:\Users\UserName\Contacts
374DE290-123F-4565-9164-39C4925E467B C:\Users\UserName\Downloads
1777F761-68AD-4D8A-87BD-30B759FA33DD C:\Users\UserName\Favorites
4BD8D571-6D19-48D3-BE97-422220080E43 C:\Users\UserName\Music
31C0DD25-9439-4F12-BF41-7FF4EDA38722 C:\Users\UserName\3D Objects
33E28130-4E1E-4676-835A-98395C3BC3BB C:\Users\UserName\Pictures
AE50C081-EBD2-438A-8655-8A092E34987A C:\Users\UserName\Recent
A63293E8-664E-48DB-A079-DF759E0509F7 C:\Users\UserName\Templates
18989B1D-99B5-455B-841C-AB7C74E4DDFC C:\Users\UserName\Videos
Other GUIDs Other...
AppVPackageDrive C:\

More Information

More information on the FileRedirectionFixup may be found in the FileRedirectionFixup Developer Documentation page.