Wednesday, June 6, 2012

Mac OS X pkg, bom files, package installation and utilities

List all installed packages:
pkgutil --pkgs --volume /
What files does this package install?
pkgutil --files com.vmware.fusion
Verify file permissions and owner/group is the same as listed in the package BOM in /private/var/db/receipts/ (actually calls repair_packages). See the end of this post for how to verify the file content.
pkgutil --verify com.vmware.fusion
There isn't any uninstall functionality on OS X, this command just drops the metadata out of the receipts directory, which is handy if you want to re-install:
pkgutil --forget com.vmware.fusion
pkgutil has a code signing check feature, but it looks like it is broken or doesn't work how I expected it to:
$ pkgutil --check-signature com.apple.pkg.Safari
Package does not exist: com.apple.pkg.Safari
$ pkgutil --check-signature /Applications/Safari.app/
Package "Safari":
   Status: no signature
$ codesign -d -vvv /Applications/Safari.app/
Executable=/Applications/Safari.app/Contents/MacOS/Safari
Identifier=com.apple.Safari
Format=bundle with Mach-O universal (i386 x86_64)
CodeDirectory v=20100 size=185 flags=0x0(none) hashes=3+3 location=embedded
Hash type=sha1 size=20
CDHash=66615ae53cb89ac254e4efc6d3eb2f93fa6a4a85
Signature size=4064
Authority=Software Signing
Authority=Apple Code Signing Certification Authority
Authority=Apple Root CA
Info.plist entries=37
Sealed Resources rules=11 files=393
Internal requirements count=1 size=108

PKG files


PKG files are just XAR files that contain a bunch of metadata files and the app itself inside a zipped cpio.

To extract the files on OS X, use (note tempdir will be created and can't exist prior to running the command):
pkgutil --expand my.pkg tempdir
Here is a manual unpacking sequence (if you just want metadata use the pkgutil commands to avoid all this unpacking). tar understands xar on OS X but on linux you'll need to install the xar package and use 'xar -xf helloworld.pkg':
$ tar zxvf helloworld.pkg 
x com.totallylegit.helloworld.pkg
x com.totallylegit.helloworld.pkg/Bom
x com.totallylegit.helloworld.pkg/Payload
x com.totallylegit.helloworld.pkg/PackageInfo
x Distribution

$ cd com.totallylegit.helloworld.pkg/
$ ls
Bom  PackageInfo Payload
$ mv Payload Payload.gz
$ gunzip Payload
$ cpio -iv < Payload
.
./helloworld.app
./helloworld.app/Contents
./helloworld.app/Contents/Info.plist
./helloworld.app/Contents/MacOS
./helloworld.app/Contents/MacOS/helloworld
./helloworld.app/Contents/PkgInfo
./helloworld.app/Contents/Resources
./helloworld.app/Contents/Resources/en.lproj
./helloworld.app/Contents/Resources/en.lproj/Credits.rtf
./helloworld.app/Contents/Resources/en.lproj/InfoPlist.strings
./helloworld.app/Contents/Resources/en.lproj/MainMenu.nib
87 blocks

BOM files


Use lsbom to inspect bill of materials files (note apple says "The files and directories where receipts are stored are subject to change. Always use pkgutil to query or modify them"). These options are more readable than the defaults:
$ lsbom -p MUGsf /private/var/db/receipts/com.vmware.fusion.bom
drwxr-xr-x  root admin  .
drwxrwxr-x  root admin  ./Applications
-rwxr-xr-x  root admin 82 ./Applications/._VMware Fusion.app
[snip]
While pkgutil/repair_packages could check file content hasn't changed by calculating the CRC32 and comparing that with the one in the BOM, it doesn't. It would be fairly simple to do. The BOM content for a simple package looks like this (path, octal mode, UID/GID, size, CRC32):
$ lsbom Bom 
. 0 0/0
./helloworld.app 40755 0/0
./helloworld.app/Contents 40755 0/0
./helloworld.app/Contents/Info.plist 100644 0/0 1429 3178638052
./helloworld.app/Contents/MacOS 40755 0/0
./helloworld.app/Contents/MacOS/helloworld 100755 0/0 13488 1649029420
./helloworld.app/Contents/PkgInfo 100644 0/0 8 742937289
./helloworld.app/Contents/Resources 40755 0/0
./helloworld.app/Contents/Resources/en.lproj 40755 0/0
./helloworld.app/Contents/Resources/en.lproj/Credits.rtf 100644 0/0 436 2072274354
./helloworld.app/Contents/Resources/en.lproj/InfoPlist.strings 100644 0/0 92 2769355950
./helloworld.app/Contents/Resources/en.lproj/MainMenu.nib 100644 0/0 27463 4224082203
and the checksum can be calculated with cksum which prints the size in bytes and the CRC32:
$ cksum helloworld 
1649029420 13488 helloworld

Installation


On installation the package app directory is copied to the installation dir (usually /Applications/) and the BOM is written with a plist of installation metadata to the Receipts directory:
$ ls -1 /private/var/db/receipts/com.vmware*
/private/var/db/receipts/com.vmware.fusion.bom
/private/var/db/receipts/com.vmware.fusion.plist

4 comments:

Anonymous said...

Hi. Thanks for putting this online. I used the "list all installed packages info" to *finally* successfully install two versions of R. (Knew I needed to forget what I had, but couldn't figure out what that was.)

Anonymous said...

thx a lot
about unpack PKG files can add:
pkgutil --expand PKG DIR

Unknown said...

Excellent read, thank you.

Unknown said...

hi,
how to repackage back , i tried with pkgutil --flatten, but the package show error on opening afterwards.