Skip to content

Commit

Permalink
fix(dmg): new version of mac_alias (#5460)
Browse files Browse the repository at this point in the history
  • Loading branch information
elliottkember authored Dec 14, 2020
1 parent a98c895 commit a8acb58
Show file tree
Hide file tree
Showing 3 changed files with 376 additions and 171 deletions.
123 changes: 89 additions & 34 deletions packages/dmg-builder/vendor/mac_alias/alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@

ALIAS_HFS_VOLUME_SIGNATURE = b'H+'

ALIAS_FILESYSTEM_UDF = 'UDF (CD/DVD)'
ALIAS_FILESYSTEM_FAT32 = 'FAT32'
ALIAS_FILESYSTEM_EXFAT = 'exFAT'
ALIAS_FILESYSTEM_HFSX = 'HFSX'
ALIAS_FILESYSTEM_HFSPLUS = 'HFS+'
ALIAS_FILESYSTEM_FTP = 'FTP'
ALIAS_FILESYSTEM_NTFS = 'NTFS'
ALIAS_FILESYSTEM_UNKNOWN = 'unknown'

ALIAS_FIXED_DISK = 0
ALIAS_NETWORK_DISK = 1
ALIAS_400KB_FLOPPY_DISK = 2
Expand All @@ -36,6 +45,21 @@

ALIAS_NO_CNID = 0xffffffff

ALIAS_FSTYPE_MAP = {
# Version 2 aliases
b'HX': ALIAS_FILESYSTEM_HFSX,
b'H+': ALIAS_FILESYSTEM_HFSPLUS,

# Version 3 aliases
b'BDcu': ALIAS_FILESYSTEM_UDF,
b'BDIS': ALIAS_FILESYSTEM_FAT32,
b'BDxF': ALIAS_FILESYSTEM_EXFAT,
b'HX\0\0': ALIAS_FILESYSTEM_HFSX,
b'H+\0\0': ALIAS_FILESYSTEM_HFSPLUS,
b'KG\0\0': ALIAS_FILESYSTEM_FTP,
b'NTcu': ALIAS_FILESYSTEM_NTFS,
}

def encode_utf8(s):
if isinstance(s, bytes):
return s
Expand Down Expand Up @@ -69,7 +93,9 @@ def __init__(self, name, creation_date, fs_type, disk_type,
#: The creation date of the target's volume
self.creation_date = creation_date

#: The filesystem type (a two character code, e.g. ``b'H+'`` for HFS+)
#: The filesystem type
#: (for v2 aliases, this is a 2-character code; for v3 aliases, a
#: 4-character code).
self.fs_type = fs_type

#: The type of disk; should be one of
Expand Down Expand Up @@ -110,6 +136,10 @@ def __init__(self, name, creation_date, fs_type, disk_type,
#: Network mount information (for automatic remounting)
self.network_mount_info = network_mount_info

@property
def filesystem_type(self):
return ALIAS_FSTYPE_MAP.get(self.fs_type, ALIAS_FILESYSTEM_UNKNOWN)

def __repr__(self):
args = ['name', 'creation_date', 'fs_type', 'disk_type',
'attribute_flags', 'fs_id']
Expand Down Expand Up @@ -226,7 +256,7 @@ def __init__(self, appinfo=b'\0\0\0\0', version=2, volume=None,
#: Application specific information (four byte byte-string)
self.appinfo = appinfo

#: Version (we support only version 2)
#: Version (we support versions 2 and 3)
self.version = version

#: A :class:`VolumeInfo` object describing the target's volume
Expand All @@ -245,24 +275,36 @@ def _from_fd(cls, b):
if recsize < 150:
raise ValueError('Incorrect alias length')

if version != 2:
if version not in (2, 3):
raise ValueError('Unsupported alias version %u' % version)

kind, volname, voldate, fstype, disktype, \
folder_cnid, filename, cnid, crdate, creator_code, type_code, \
levels_from, levels_to, volattrs, volfsid, reserved = \
struct.unpack(b'>h28pI2shI64pII4s4shhI2s10s', b.read(142))
if version == 2:
kind, volname, voldate, fstype, disktype, \
folder_cnid, filename, cnid, crdate, creator_code, type_code, \
levels_from, levels_to, volattrs, volfsid, reserved = \
struct.unpack(b'>h28pI2shI64pII4s4shhI2s10s', b.read(142))
else:
kind, voldate_hr, fstype, disktype, folder_cnid, cnid, crdate_hr, \
volattrs, reserved = \
struct.unpack(b'>hQ4shIIQI14s', b.read(46))

volname = b''
filename = b''
creator_code = None
type_code = None
voldate = voldate_hr / 65536.0
crdate = crdate_hr / 65536.0

voldate = mac_epoch + datetime.timedelta(seconds=voldate)
crdate = mac_epoch + datetime.timedelta(seconds=crdate)

alias = Alias()
alias.appinfo = appinfo

alias.volume = VolumeInfo (volname.replace('/',':'),
alias.volume = VolumeInfo (volname.decode().replace('/',':'),
voldate, fstype, disktype,
volattrs, volfsid)
alias.target = TargetInfo (kind, filename.replace('/',':'),
alias.target = TargetInfo (kind, filename.decode().replace('/',':'),
folder_cnid, cnid,
crdate, creator_code, type_code)
alias.target.levels_from = levels_from
Expand All @@ -277,9 +319,9 @@ def _from_fd(cls, b):
b.read(1)

if tag == TAG_CARBON_FOLDER_NAME:
alias.target.folder_name = value.replace('/',':')
alias.target.folder_name = value.decode().replace('/',':')
elif tag == TAG_CNID_PATH:
alias.target.cnid_path = struct.unpack(b'>%uI' % (length // 4),
alias.target.cnid_path = struct.unpack('>%uI' % (length // 4),
value)
elif tag == TAG_CARBON_PATH:
alias.target.carbon_path = value
Expand Down Expand Up @@ -314,9 +356,9 @@ def _from_fd(cls, b):
alias.target.creation_date \
= mac_epoch + datetime.timedelta(seconds=seconds)
elif tag == TAG_POSIX_PATH:
alias.target.posix_path = value
alias.target.posix_path = value.decode()
elif tag == TAG_POSIX_PATH_TO_MOUNTPOINT:
alias.volume.posix_path = value
alias.volume.posix_path = value.decode()
elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE:
alias.volume.disk_image_alias = Alias.from_bytes(value)
elif tag == TAG_USER_HOME_LENGTH_PREFIX:
Expand Down Expand Up @@ -441,24 +483,37 @@ def _to_fd(self, b):
voldate = (self.volume.creation_date - mac_epoch).total_seconds()
crdate = (self.target.creation_date - mac_epoch).total_seconds()

# NOTE: crdate should be in local time, but that's system dependent
# (so doing so is ridiculous, and nothing could rely on it).
b.write(struct.pack(b'>h28pI2shI64pII4s4shhI2s10s',
self.target.kind,
carbon_volname, int(voldate),
self.volume.fs_type,
self.volume.disk_type,
self.target.folder_cnid,
carbon_filename,
self.target.cnid,
int(crdate),
self.target.creator_code,
self.target.type_code,
self.target.levels_from,
self.target.levels_to,
self.volume.attribute_flags,
self.volume.fs_id,
b'\0'*10))
if self.version == 2:
# NOTE: crdate should be in local time, but that's system dependent
# (so doing so is ridiculous, and nothing could rely on it).
b.write(struct.pack(b'>h28pI2shI64pII4s4shhI2s10s',
self.target.kind,
carbon_volname, int(voldate),
self.volume.fs_type,
self.volume.disk_type,
self.target.folder_cnid,
carbon_filename,
self.target.cnid,
int(crdate),
self.target.creator_code,
self.target.type_code,
self.target.levels_from,
self.target.levels_to,
self.volume.attribute_flags,
self.volume.fs_id,
b'\0'*10))
else:
b.write(struct.pack(b'>hQ4shIIQI14s',
self.target.kind,
int(voldate * 65536),
self.volume.fs_type,
self.volume.disk_type,
self.target.folder_cnid,
self.target.cnid,
int(crdate * 65536),
self.volume.attribute_flags,
self.volume.fs_id,
b'\0'*14))

# Excuse the odd order; we're copying Finder
if self.target.folder_name:
Expand All @@ -472,12 +527,12 @@ def _to_fd(self, b):

b.write(struct.pack(b'>hhQhhQ',
TAG_HIGH_RES_VOLUME_CREATION_DATE,
8, long(voldate * 65536),
8, int(voldate * 65536),
TAG_HIGH_RES_CREATION_DATE,
8, long(crdate * 65536)))
8, int(crdate * 65536)))

if self.target.cnid_path:
cnid_path = struct.pack(b'>%uI' % len(self.target.cnid_path),
cnid_path = struct.pack('>%uI' % len(self.target.cnid_path),
*self.target.cnid_path)
b.write(struct.pack(b'>hh', TAG_CNID_PATH,
len(cnid_path)))
Expand Down
19 changes: 13 additions & 6 deletions packages/dmg-builder/vendor/mac_alias/bookmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def iteritems(x):
BMK_URL_ST_RELATIVE = 0x0002

# Bookmark keys
# = 0x1003
kBookmarkURL = 0x1003 # A URL
kBookmarkPath = 0x1004 # Array of path components
kBookmarkCNIDPath = 0x1005 # Array of CNIDs
kBookmarkFileProperties = 0x1010 # (CFURL rp flags,
Expand Down Expand Up @@ -180,10 +180,17 @@ def iteritems(x):
kBookmarkWasFileReference = 0xd001 # True if the URL was a file reference
kBookmarkCreationOptions = 0xd010
kBookmarkURLLengths = 0xe003 # See below
# = 0xf017 # Localized name?
# = 0xf022
kBookmarkSecurityExtension = 0xf080
# = 0xf081
kBookmarkDisplayName = 0xf017
kBookmarkIconData = 0xf020
kBookmarkIconRef = 0xf021
kBookmarkTypeBindingData = 0xf022
kBookmarkCreationTime = 0xf030
kBookmarkSandboxRwExtension = 0xf080
kBookmarkSandboxRoExtension = 0xf081
kBookmarkAliasData = 0xfe00

# Alias for backwards compatibility
kBookmarkSecurityExtension = kBookmarkSandboxRwExtension

# kBookmarkURLLengths is an array that is set if the URL encoded by the
# bookmark had a base URL; in that case, each entry is the length of the
Expand Down Expand Up @@ -322,7 +329,7 @@ def from_bytes(cls, data):

magic,size,dummy,hdrsize = struct.unpack(b'<4sIII', data[0:16])

if magic != b'book':
if magic not in (b'book', b'alis'):
raise ValueError('Not a bookmark file (bad magic) %r' % magic)

if hdrsize < 16:
Expand Down
Loading

0 comments on commit a8acb58

Please sign in to comment.