How to relocate the Package Cache

Heath Stewart

Visual Studio can require a lot of space on the system drive. Based on years of data collected from customers’ installations from the Customer Experience Improvement Program, we took advantage of this feature in Burn – the Windows Installer XML (WiX) chainer – to eliminate most errors during repair, servicing, and even uninstall. This was not a popular decision with some customers. For years even I pushed against caching Visual Studio deployment packages because of the impact to drive space as well, but as HDD space increased market studies showed little reason not to cache for the increased reliability of deployment.

We understand, however, that many customers have smaller SSDs and while size will increase and cost decrease over time, some customers are blocked from installing Visual Studio or have little space left over after a successful installation.

I have submitted a feature proposal to WiX to allow control over the Package Cache location, but until then there is a workaround that really highlights some of the virtualization features in Windows.

Disclaimer

This practice has received some testing and has been running under load for a reasonable amount of time. While it uses documented features of Windows, this is not an officially supported practice and may leave your machine in a corrupted state should you disconnected the secondary drive or fail to properly secure the folder mount point and its contents.

Workaround

You can move the contents of the Package Cache to a partition on another drive – copying the ACL and owner, which is very important for both security and because some programs may not trust any ACL or owner that is different than what is expected – and then mount that partition into an empty folder. But rather than dedicate an entire partition on a physical disk – which isn’t always as easy to reconfigure on the fly – we will create a virtual disk (VHD) on another drive.

By creating an expandable virtual disk we can declare a maximum size that can be much larger than necessary – even larger than the host drive itself – but takes up only as much room as necessary and will grow as the content grows. This way, should you ever need to allocate more space you can simply dismount and move the VHD to another disk, then remount it. No need to recopy files.

Mounting a VHD into an empty directory also maintains the mount across reboots – something not currently supported when mounting a VHD for drive access.

System requirements

Support for creating and mounting VHDs was built into Windows Vista with support for VHDs larger than 2TB added in Windows 8 using the newer VHDX format.

Manual walkthrough

To show more in depth how this works – and how you might adapt it for your own use – I will use a couple of built-in programs: diskpart.exe and mountvol.exe. We can also do all this in PowerShell with the right Windows Features enabled but I will cover that in the scripted section.

  1. Open an elevated command prompt.
  2. Run diskpart.exe to start the disk partitioning utility: diskpart
  3. Create a large (ex: 1TB), expandable VHD on whatever secondary disk (ex: X:) you prefer with security matching the source directory’s security: create vdisk file="X:Cache.vhd" type=expandable maximum=1048576 sd="O:BAG:DUD:P(A;;FA;;;BA)(A;;FA;;;SY)(A;;FRFX;;;BU)(A;;FRFX;;;WD)"
  4. Select the VHD and create a partition using all available space: select vdisk file=”X:Cache.vhd” attach vdisk create partition primary
  5. Format the volume that was created automatically and temporarily assign a drive letter (ex: P:): format fs=ntfs label="Package Cache" quick assign letter=P exit
  6. After exiting diskpart.exe, move any existing per-machine payloads from the Package Cache with security: robocopy "%ProgramData%Package Cache" P: /e /copyall /move /zb
  7. Recreate the Package Cache directory and set up the ACL and owner as before: mkdir "%ProgramData%Package Cache" echo y | cacls foo /s:"O:BAG:DUD:PAI(A;OICIID;FA;;;BA)(A;OICIID;FA;;;SY)(A;OICIID;FRFX;;;BU)(A;OICIID;FRFX;;;WD)"
  8. Run mountvol.exe without any parameters first and look for the volume name that has the drive letter you assigned to the VHD, then use that with mountvol.exe again to mount that volume into the empty Package Cache directory. mountvol mountvol "%ProgramData%Package Cache" \?Volume{a525b826-8a0c-11e3-be94-00249b0716f5}
  9. Run diskpart.exe again and remove the drive letter assignment from the volume (should be in partition 1 of the VHD): select vdisk file="X:Cache.vhd" select partition 1 remove letter=P exit
  10. Non-boot VHDs are not automatically mounted, so before you reboot you need to make sure the VHD is mounted again whenever the machine is started. Write a simple script for diskpart.exe to execute on startup. If you’re doing this on a laptop, you should edit the scheduled task afterward to allow it to run on batteries. echo select vdisk file=X:Cache.vhd > X:Cache.txt echo attach vdisk >> X:Cache.txt schtasks /create /ru system /sc onstart /rl highest /tn "Attach Package Cache" /tr "%SystemRoot%System32diskpart.exe /s X:Cache.txt"

After exiting diskpart.exe you now have mapped a VHD on another drive into the new Package Cache directory. The VHD will be remounted into the directory whenever the machine is rebooted. If you look at the mount point, you will also see its icon, description, and size are different.

The size reported in Windows Explorer is merely the maximum and not how much space is consumed within the virtual disk. In fact, you probably would care less about that than how much space the VHD itself is consuming. Browse to the folder were you created the VHD (ex: X:Cache.vhd) and you will see the actual file size.

When bundles have been uninstalled and packages removed from the Package Cache, you can attempt to compact the VHD to reclaim space on the host disk.

  1. Open an elevated command prompt.
  2. Run diskpart.exe: diskpart
  3. Select the VHD and attempt to compact it: select vdisk file="X:Cache.vhd compact vdisk exit

Should you ever need to move the file, you can use mountvol.exe to dismount the VHD, copy it to another attached drive, and remount the VHD.

Scripted solution

Once the Hyper-V Module for Windows PowerShell is installed on supported Windows platforms, you can easily script this and execute it on remote machines running an elevated WinRM endpoint. PowerShell is a powerful object-oriented shell that provides for the same compositional techniques of any modern programming language.

I have created an example PowerShell script you can use locally on any Windows 7 machine or newer, or run on remote machines using the Invoke-Command cmdlet.

When bundles have been uninstalled and packages removed from the Package Cache, you can attempt to compact the VHD to reclaim space on the host disk.

  1. Open an elevated PowerShell prompt.
  2. Run the following command to get and optimize (compact) the VHD you passed as a parameter to Move-WixPackageCache.ps1 (ex: X:Cache.vhd): get-vhd X:Cache.vhd | dismount-vhd -passthru | optimize-vhd -passthru | mount-vhd

Summary

Mounting virtual disks hosted on other attached disks can be an effective workaround to reducing space on the system drive. We will continue to explore options for locating more data on the chosen installation drive but there will always be some components that need to be installed to the Windows or Program Files directories. There are some known issues when redirecting those and other directories to another drive other than where Windows is installed, so leaving plenty of space on your system drive is always recommended.

0 comments

Discussion is closed.

Feedback usabilla icon