The curious case of the missing file attributes
Like many Mac admins, I am a big fan of AutoPkg (https://github.com/autopkg/autopkg), an open-source tool for automating the creation of software packages for distribution. Its power comes from the use of modular processors to build a recipe - each software package will have its own recipe, but often built up from the same processors. A processor performs a single step, such as downloading a .zip archive, or copying a file to a specific location in the package structure.
I'm also an active member of the MacAdmins Slack workspace (https://macadmins.slack.com), a supportive network of IT professionals from around the globe. We each face many of the same issues, and it's great to be able to get advice from others with more experience in particular areas, and to be able to contribute and help others.
The problem
Back in May, Slack member @sdagley commented that VMware Fusion 11.5.5 packages created in AutoPkg were no longer working correctly. Trying to start a VM would generate the error:
Could not open /dev/vmmon: Broken pipe.
VMware had actually highlighted the issue in https://kb.vmware.com/s/article/2058680 - code signatures stored as metadata in some of the files within the app were being lost, causing Fusion to fail to load kernel extensions and launch services correctly. However, the workaround they provide generates a .mpkg package which isn't suitable for distribution.
The most popular set of AutoPkg recipes for VMware Fusion 11 is curated by my friends at dataJar (https://github.com/autopkg/dataJAR-recipes/tree/master/VMware%20Fusion%2011), although the problem was not limited to those recipes. It would also affect packages created by hand using the Jamf Composer tool, for example.
The solution - part 1
@sdagley's post triggered an extensive discussion on Slack, with lots of work being done in particular by @rtrouton.
The AutoPkg recipes first use a custom processor to query an XML file at https://softwareupdate.vmware.com/cds/vmw-desktop/fusion.xml, and obtain the URL for a zipped archive of the latest app version. The archive is downloaded from that URL, unzipped, and the .app directory copied across into a folder structure which is then used to build the installer package. The required metadata was being lost as the files were copied across.
@rtrouton edited the pkg recipe to use the FileMover processor rather than the Copier - moving the file preserves the metadata. However, this wasn't enough to completely fix the issue - the metadata was lost again when the package was installed. And so @rtrouton created a Feature Request (https://github.com/autopkg/autopkg/issues/636) to ask that AutoPkg have the ability to preserve extended attributes.
The solution - part 2
The Feature Request references another FOSS tool (munki-pkg) which is also commonly used to create software packages for macOS. It was noted that munki-pkg can create packages preserving extended attributes, and this got me thinking. In the case of munki-pkg, a parameter preserve_xattr is included in the build file, and results in a working package. So, I started digging through the code to find out exactly what this parameter does.
It turns out that munki-pkg doesn't really do anything with the preserve_xattr parameter. Some parameters can be provided which trigger munki-pkg to act differently, but preserve_xattr isn't one of them. Rather, it is one of a small collection of parameters which are simply collected together, and passed into the PackageInfo file inside the package when it is created.
The PackageInfo file is a requirement of the installer package format, and tells macOS how to deal with the packaged software. (There is some useful, although unofficial and not quite complete, documentation at http://s.sudre.free.fr/Stuff/Ivanhoe/FLAT.html.) You don't actually need to supply a PackageInfo file when building a package, since the macOS pkgbuild binary will create it for you. Of course, if you don't supply one, then you only get the default options, which don't include the preserve_xattr setting.
A bit more digging later, and I found that AutoPkg does actually have the ability to specify a PackageInfo file when creating a package. There is a little-used processor by the name of PkgInfoCreator, which can take a template file and use that as the basis for a PackageInfo file. If you include the preserve_xattr setting in the template, then it makes it through to the final package. When the package is installed, the component files will all retain the extended attributes they had when the package was created.
This solves the issue! Using @rtroutons solution to move, rather than copy, the files, and then making use of PkgInfoCreator, I was able to create a recipe which would install VMware Fusion 11 with all the required file attributes. I've submitted a request to dataJar to update their recipe.
The solution - part 1 (again)
There was one more issue I needed to fix. The custom processor used to get the download URL for Fusion is not working for the current version (11.5.5) - the version number provided in the XML file is incorrect.
VMware actually provide a fixed URL which can always be used to download the latest version of the app, https://www.vmware.com/go/getfusion. However, the process is a little different - instead of a zipped file, this link provides a read-only .dmg disk image containing the .app. And while this solution is a little more elegant in one aspect, it has the problem that the read-only nature means you can't move the files out, you have to copy them. As we saw above, copying the files using python means that the extended attributes are lost.
I decided to try and fix this from both directions. Firstly, I forked the custom processor used in the original recipe, and modified it to avoid the incorrect version number. With this modification in place, the updated dataJar recipe works great.
Secondly, I took a fork of the Copier processor and modified it so that it uses the macOS binary ditto, instead of the native python function, to copy the files. Since macOS 10.5 (released 11 years ago), ditto has preserved extended attributes by default when copying files. With this in place, I was able to use my alternative recipe to download the disk image, copy the files out, and create a working package.
There are advantages and disadvantages to both approaches, so I'm submitting both sets of recipes. They can be found in my GitHub repo at https://github.com/mikedowler/dataJAR-recipes, and hopefully one or both will make it into dataJar's parent.