In this new “mini-series” I will take a close look into a Virtual Filesystem (VFS) feature found in App-V, and some of the things that I feel should be understood by everyone doing VFS sequences and especially when trying to troubleshoot them.
This article will cover the basics and gives an introduction to VFS as a technology piece in App-V. In the upcoming parts we’ll look the data handling for VFS directories at the client and discuss current limitations and known bugs in VFS implementation.
Like with lot of the articles on this site, please keep in mind that matters discussed in this series are mostly based on personal findings and experience with working with App-V packages both as packager and as implementor of underlying technology in code; and so some of the things fall into “not documented by MS” -category. Your mileage for results may vary, since different versions or hotfixes of App-V Client can make a difference; however I have tried to verify all of this using the latest 4.6 SP1 Client and Sequencer.
While not being documented, exposing the unexposed may help you understand just why you might have issues with some of your App-V packages and maybe how to work around those issues (if possible).
So what is the Virtual File System, to be exact?
VFS denotes a mechanism that App-V, or indeed any application virtualization technology for that matter, uses to overlay files and directories that are stored in our virtual application package over the physical filesystem that exists on the client. The purpose here is of course to show to an application running from the package that its “installed” resources are where they are expected to be; be it application’s private directory structure over at Program Files or some additional files placed in common, shared, directory like C:\Windows\System32.
Generally speaking, in App-V, we can package our virtual applications by using so-called mount sequencing which means that we direct application installation to go directly to secondary (Q:) partition on the Sequencing machine. This method of sequencing has always been recommended as default and is stated to have lower overhead of virtualization than alternative method, VFS-based virtualization.
In the so-called VFS-sequencing (the main topic of this series), we install the application to where ever it by default wants to go (usually C:\Program Files\Application Name) and leave it to Virtual Filesystem –feature to handle representation of all application’s files. Sometimes this type of sequencing is even required by a kind of application that does not work from anywhere else besides the default installation directory; or it has backend-related dependencies of certain install directory.
And even if we do the recommended Q: -based packaging instead of VFS -sequencing, we almost always end up having at least few files being put over at our system directories because application shortcuts (.LNK files) and other “residual” files are stored there.
Therefore App-V needs to be able to provide virtualized view to the file system on a client for all virtual applications i.e. display directories and files on your C: drive (and why not other partitions as well, if existing) that are not really there but come from an information stored in App-V package’s virtual environment. What actually happens in the background is that App-V intercepts file I/O operations coming from processes running in virtual environment, and modify those calls based on the VFS mapping information stored in the package.
So, if we for instance have a file in the package at the package path \mypackage\VFS\CSIDL_WINDOWS\file.dll, it will be shown to the virtual application in a location defined by the mapping information stored for that file. In this example case it is very likely that the mapped path happens to be %CSIDL_WINDOWS%\file.dll (App-V by default uses directory naming that basically mirrors the actual mapping path when it stores VFS –related files and directories), which when the mapping variables has been expanded, will be C:\Windows\file.dll if C:\Windows is currently resolved path for %CSIDL_WINDOWS%. You can find more information about mapping variables in my earlier post about mixing 32-bit and 64-bit packages/systems.
When application tries to open or otherwise access file in this very path, App-V will modify the I/O call so that the access on a file system level actually happens against Q:\mypackage\VFS\CSIDL_WINDOWS\file.dll unbeknownst to application itself.
(App-V stores all VFS’ed files and directories always under VFS –subdirectory directly under package root, although there’s no hard technical requirement to do so best to my knowledge. There’s, btw, also no technical limitation on how many target paths one file or directory can be mapped to; usually it’s one-to-one relationship but App-V is capable of doing one-to-many mappings which sometimes happens to some files when original installer had deleted or moved them around.)
This is sufficient to fool virtual application into thinking that it’s precious directories and/or files that it expects to found from certain path are indeed in that path. Virtual application can also, if it decides to look, see those same files in the VFS\… directory structure on a virtual drive as that’s where they really are.
At this point it needs to be said that sometimes – fortunately very rarely – application may also decide to take a second look of the file handle that it received from Windows after opening a file from VFS mapped target path, and see that the real path is actually the one going into Q:\… . This can cause issues if the path is not “expected” by the application.
Variation of such an issue has actually happened to me at least once with Excel plugin (XLA file), which kept seeing itself in the Q: and went ahead to modify certain paths in the worksheet’s cells using the actual virtual drive path; the intended behavior was to fool it to think it is installed on subdirectory on root of C:\ as that’s where previously – physically – installed version of the plugin was located.
The “solution” was to deploy only XLA file to the real subdirectory on C: drive, bring rest of the assets virtualized in App-V package and launch local XLA file into the package’s bubble; as it was determined impossible to make the XLA to see anything besides its actual file system path (there’s likely a function in Excel that allows add-on to query its installation path, and that in turn received path to Q: from the process information or some other Windows structure).
Reasons for the above problem is that App-V cannot fake the path in all possible cases since the file system –related functionality goes deep into kernel level, and Windows itself needs to see files by their original, real, paths. The fact that Windows “sees” the paths using the original path, and not VFS virtualized mapped path, can also be seen in that with Windows Firewall (and anti-malware etc.) all the rules which relate to files has to be specified using the actual package path to the file instead of VFS path. So VFS is only effective for processes running from the package itself, and even then for the common case (file access I/O).
Two modes of virtualized file system entry
Now, having discussed what the role of VFS is, and how App-V implements it, let’s next look at the two modes of VFS mapped entries: fully virtualized and “half-virtualized”, or override and merged states, respectively.
Fully virtualized means that anything and everything in that virtual directory will completely mask underlying directory which happens to be in the same path, whereas half virtualized state only merges the contents of virtualized directory with that of the local one.
To illustrate this difference, let’s consider a local directory for an installed application, in this case C:\Program Files\7-Zip. This directory contains application’s files (and folders):
Let’s deploy virtual application to the same system, which has the exact same path defined inside VFS configuration for that package and which has the directory set to fully virtualized (maybe, I don’t know, because we sequenced a 7-Zip program!):
Now, what the virtualized 7-Zip will see on that C:\Program Files\7-Zip, is seemingly the same as we have on that local directory examined earlier:
However, since this now fully virtualized VFS directory for that package, any changes to the local directory won’t be reflected at what our virtual application will see. To demonstrate this fact, let’s remove all the .TXT files from the local directory. And regardless of how much we refresh the view inside virtual 7-Zip after the fact, it keeps seeing those text files because all of the files come from the package:
The opposite is also true in that if we now remove or add something to the package’s understanding of said directory, local directory won’t see a thing of it. This is both true for files/directories added during sequencing to that VFS folder as well as changes that happen at runtime (assuming directory is writable for normal users).
To illustrate how merged folders behave, let’s modify our virtual package so that the 7-Zip folder is changed to merged, contents of it are removed and extra file added:
After updating the package in the client’s cache and starting our 7-Zip from the local copy (but into package’s context) we can see it sees “through” to the local C:\Program Files\7-Zip but also sees that one additional file in it:
So setting directory to fully virtualized or overridden mode isolates and ensures the version from a package is the one that our virtual application will always see independent on what changes are happening on the client. But if we only want to selectively add to something to an existing directory then merged state is desired.
When App-V Sequencer itself maps all VFS –related files and directories after monitoring has been completed, i.e. it will copy all those files and directories from the other partitions to under that VFS subdirectory in your package directory, it also assigns a mode to each copied file and folder. While all VFS files always have fully virtualized mode (because how could you have file that is merged with equivalent local file?), state of the directory in VFS will either be overridden or merged depending if the directory was created anew during the monitoring or not.
Basic idea and assumption here is that if the directory was not in the system before monitoring started, it is very likely a private to that application and thus can safely be marked as fully virtualized and there’s nothing that needs to be seen from local directory at the same path, if it even exists. But if it existed prior to monitoring, it’s assumed that the directory belongs to somebody other than the application being virtualized as so it should see possibly local directory of the same name, along with additions that the application installer made to it.
After sequencing, you can look at the Virtual File System –tab in the App-V Sequencer, and see that directories marked as fully virtualized are in yellow and directories set to merged state are shown in gray. There’s possibility to change the state using the context menu:
Usually you don’t need to change any of the virtualization levels on VFS entries, but when you do it’s good to know the implications. The need for changes in VFS mode is most frequently associated with trying to run same applications virtualized that are already installed locally on the client systems, or possibly when trying to run newer/older version of the same application than installed locally. Another clear case for modifying virtualization state is when trying to integrate application with another one, especially in plug-in scenarios (and now I’m not really talking about DSC here, as that particular functionality has its own share of VFS oddities as documented by Tim in his whitepaper and Nicke with his shortname issue, which I believe relates to VFS in particular)
Now you see it, now you don’t
In addition of having variable virtualization level available for VFS directories (remember, VFS files only have override state), there’s another – much lesser recognized – feature of VFS available for us. This feature is about marking files or folder deleted instead of actually deleting then from the package.
The best way to describe this semantic difference is that when package has [VFS -based] file or directory actually deleted from it, it won’t be displayed on the client anymore (understandably) but it will not prevent local file from showing if the containing directory is set to merged state.
But if the file or directory is only marked as deleted then the package will retain the file inside it, but the target of entry’s VFS mapping not only will prevented from showing to the process (i.e. application) running inside virtual environment but it also continue masking local entry as well.
Logical deletion flag for VFS mapped files and directories is in reality property of the VFS mapping entry, and if file has multiple mapping entries to it (rare, but possible) then it could be that one of the VFS target paths is virtualized normally and another one is virtualized as deleted!
Main issue with logical deletion in VFS is that the Sequencer does not support showing that information anywhere in the GUI; it will pick up deletion of VFS file or directory if you do it during monitoring, and marks the VFS mapping as such, but you cannot distinguish deleted entry from the Sequencer’s Virtual File System -tab from the other entries. Here’s excerpt from the VFS mappings of our earlier 7-Zip App-V package, as shown in Sequencer:
As we can see, most everything looks good, right?
Well, what I actually did after adding those 7-Zip files back to the sequence was to change one of the files to logically deleted state. Starting the virtual 7-Zip from the package again, we can see it cannot see 7-zip.chm file although it does exists on the local directory:
What makes this whole thing bit problematic for a packager is that you couldn’t see file being set to deleted; after all, Sequencer showed the file amongst others in the VFS mapping list!
But if we look the same package using AVE, we can see it’s now in logically deleted state:
Used correctly, deleted VFS files and directories is a another powerful tool in making sure that virtual application integrates correctly in “hostile” client environment where there’s potential for conflicts when virtual application has to use merged VFS directories. With fully virtualized folders, marking files or directories inside it does not have much sense as the containing directory is already masking out all the local file system entries.
In the next installment of the series, we will look at where file data is stored when using VFS directories and discuss a special limitation of using merged folders.