dmg-builder 26.5.0 → 26.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/dmgUtil.d.ts +2 -3
- package/out/dmgUtil.js +41 -23
- package/out/dmgUtil.js.map +1 -1
- package/package.json +3 -4
- package/vendor/biplist/__init__.py +0 -977
- package/vendor/dmgbuild/__init__.py +0 -5
- package/vendor/dmgbuild/__main__.py +0 -70
- package/vendor/dmgbuild/badge.py +0 -169
- package/vendor/dmgbuild/colors.py +0 -504
- package/vendor/dmgbuild/core.py +0 -963
- package/vendor/dmgbuild/licensing.py +0 -331
- package/vendor/dmgbuild/resources/builtin-arrow.tiff +0 -0
- package/vendor/ds_store/__init__.py +0 -5
- package/vendor/ds_store/__main__.py +0 -102
- package/vendor/ds_store/buddy.py +0 -486
- package/vendor/ds_store/store.py +0 -1262
- package/vendor/mac_alias/__init__.py +0 -47
- package/vendor/mac_alias/alias.py +0 -772
- package/vendor/mac_alias/bookmark.py +0 -677
- package/vendor/mac_alias/osx.py +0 -1074
- package/vendor/mac_alias/utils.py +0 -20
- package/vendor/run_dmgbuild.py +0 -14
|
@@ -1,772 +0,0 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
import io
|
|
3
|
-
import os
|
|
4
|
-
import os.path
|
|
5
|
-
import struct
|
|
6
|
-
import sys
|
|
7
|
-
from unicodedata import normalize
|
|
8
|
-
|
|
9
|
-
if sys.platform == "darwin":
|
|
10
|
-
from . import osx
|
|
11
|
-
|
|
12
|
-
from .utils import mac_epoch
|
|
13
|
-
|
|
14
|
-
ALIAS_KIND_FILE = 0
|
|
15
|
-
ALIAS_KIND_FOLDER = 1
|
|
16
|
-
|
|
17
|
-
ALIAS_HFS_VOLUME_SIGNATURE = b"H+"
|
|
18
|
-
|
|
19
|
-
ALIAS_FILESYSTEM_UDF = "UDF (CD/DVD)"
|
|
20
|
-
ALIAS_FILESYSTEM_FAT32 = "FAT32"
|
|
21
|
-
ALIAS_FILESYSTEM_EXFAT = "exFAT"
|
|
22
|
-
ALIAS_FILESYSTEM_HFSX = "HFSX"
|
|
23
|
-
ALIAS_FILESYSTEM_HFSPLUS = "HFS+"
|
|
24
|
-
ALIAS_FILESYSTEM_FTP = "FTP"
|
|
25
|
-
ALIAS_FILESYSTEM_NTFS = "NTFS"
|
|
26
|
-
ALIAS_FILESYSTEM_UNKNOWN = "unknown"
|
|
27
|
-
|
|
28
|
-
ALIAS_FIXED_DISK = 0
|
|
29
|
-
ALIAS_NETWORK_DISK = 1
|
|
30
|
-
ALIAS_400KB_FLOPPY_DISK = 2
|
|
31
|
-
ALIAS_800KB_FLOPPY_DISK = 3
|
|
32
|
-
ALIAS_1_44MB_FLOPPY_DISK = 4
|
|
33
|
-
ALIAS_EJECTABLE_DISK = 5
|
|
34
|
-
|
|
35
|
-
ALIAS_NO_CNID = 0xFFFFFFFF
|
|
36
|
-
|
|
37
|
-
ALIAS_FSTYPE_MAP = {
|
|
38
|
-
# Version 2 aliases
|
|
39
|
-
b"HX": ALIAS_FILESYSTEM_HFSX,
|
|
40
|
-
b"H+": ALIAS_FILESYSTEM_HFSPLUS,
|
|
41
|
-
# Version 3 aliases
|
|
42
|
-
b"BDcu": ALIAS_FILESYSTEM_UDF,
|
|
43
|
-
b"BDIS": ALIAS_FILESYSTEM_FAT32,
|
|
44
|
-
b"BDxF": ALIAS_FILESYSTEM_EXFAT,
|
|
45
|
-
b"HX\0\0": ALIAS_FILESYSTEM_HFSX,
|
|
46
|
-
b"H+\0\0": ALIAS_FILESYSTEM_HFSPLUS,
|
|
47
|
-
b"KG\0\0": ALIAS_FILESYSTEM_FTP,
|
|
48
|
-
b"NTcu": ALIAS_FILESYSTEM_NTFS,
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def encode_utf8(s):
|
|
53
|
-
if isinstance(s, bytes):
|
|
54
|
-
return s
|
|
55
|
-
return s.encode("utf-8")
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def decode_utf8(s):
|
|
59
|
-
if isinstance(s, bytes):
|
|
60
|
-
return s.decode("utf-8")
|
|
61
|
-
return s
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
class AppleShareInfo:
|
|
65
|
-
def __init__(self, zone=None, server=None, user=None):
|
|
66
|
-
#: The AppleShare zone
|
|
67
|
-
self.zone = zone
|
|
68
|
-
#: The AFP server
|
|
69
|
-
self.server = server
|
|
70
|
-
#: The username
|
|
71
|
-
self.user = user
|
|
72
|
-
|
|
73
|
-
def __repr__(self):
|
|
74
|
-
return "AppleShareInfo({!r},{!r},{!r})".format(
|
|
75
|
-
self.zone, self.server, self.user
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
class VolumeInfo:
|
|
80
|
-
def __init__(
|
|
81
|
-
self,
|
|
82
|
-
name,
|
|
83
|
-
creation_date,
|
|
84
|
-
fs_type,
|
|
85
|
-
disk_type,
|
|
86
|
-
attribute_flags,
|
|
87
|
-
fs_id,
|
|
88
|
-
appleshare_info=None,
|
|
89
|
-
driver_name=None,
|
|
90
|
-
posix_path=None,
|
|
91
|
-
disk_image_alias=None,
|
|
92
|
-
dialup_info=None,
|
|
93
|
-
network_mount_info=None,
|
|
94
|
-
):
|
|
95
|
-
#: The name of the volume on which the target resides
|
|
96
|
-
self.name = name
|
|
97
|
-
|
|
98
|
-
#: The creation date of the target's volume
|
|
99
|
-
self.creation_date = creation_date
|
|
100
|
-
|
|
101
|
-
#: The filesystem type
|
|
102
|
-
#: (for v2 aliases, this is a 2-character code; for v3 aliases, a
|
|
103
|
-
#: 4-character code).
|
|
104
|
-
self.fs_type = fs_type
|
|
105
|
-
|
|
106
|
-
#: The type of disk; should be one of
|
|
107
|
-
#:
|
|
108
|
-
#: * ALIAS_FIXED_DISK
|
|
109
|
-
#: * ALIAS_NETWORK_DISK
|
|
110
|
-
#: * ALIAS_400KB_FLOPPY_DISK
|
|
111
|
-
#: * ALIAS_800KB_FLOPPY_DISK
|
|
112
|
-
#: * ALIAS_1_44MB_FLOPPY_DISK
|
|
113
|
-
#: * ALIAS_EJECTABLE_DISK
|
|
114
|
-
self.disk_type = disk_type
|
|
115
|
-
|
|
116
|
-
#: Filesystem attribute flags (from HFS volume header)
|
|
117
|
-
self.attribute_flags = attribute_flags
|
|
118
|
-
|
|
119
|
-
#: Filesystem identifier
|
|
120
|
-
self.fs_id = fs_id
|
|
121
|
-
|
|
122
|
-
#: AppleShare information (for automatic remounting of network shares)
|
|
123
|
-
#: *(optional)*
|
|
124
|
-
self.appleshare_info = appleshare_info
|
|
125
|
-
|
|
126
|
-
#: Driver name (*probably* contains a disk driver name on older Macs)
|
|
127
|
-
#: *(optional)*
|
|
128
|
-
self.driver_name = driver_name
|
|
129
|
-
|
|
130
|
-
#: POSIX path of the mount point of the target's volume
|
|
131
|
-
#: *(optional)*
|
|
132
|
-
self.posix_path = posix_path
|
|
133
|
-
|
|
134
|
-
#: :class:`Alias` object pointing at the disk image on which the
|
|
135
|
-
#: target's volume resides *(optional)*
|
|
136
|
-
self.disk_image_alias = disk_image_alias
|
|
137
|
-
|
|
138
|
-
#: Dialup information (for automatic establishment of dialup connections)
|
|
139
|
-
self.dialup_info = dialup_info
|
|
140
|
-
|
|
141
|
-
#: Network mount information (for automatic remounting)
|
|
142
|
-
self.network_mount_info = network_mount_info
|
|
143
|
-
|
|
144
|
-
@property
|
|
145
|
-
def filesystem_type(self):
|
|
146
|
-
return ALIAS_FSTYPE_MAP.get(self.fs_type, ALIAS_FILESYSTEM_UNKNOWN)
|
|
147
|
-
|
|
148
|
-
def __repr__(self):
|
|
149
|
-
args = [
|
|
150
|
-
"name",
|
|
151
|
-
"creation_date",
|
|
152
|
-
"fs_type",
|
|
153
|
-
"disk_type",
|
|
154
|
-
"attribute_flags",
|
|
155
|
-
"fs_id",
|
|
156
|
-
]
|
|
157
|
-
values = []
|
|
158
|
-
for a in args:
|
|
159
|
-
v = getattr(self, a)
|
|
160
|
-
values.append(repr(v))
|
|
161
|
-
|
|
162
|
-
kwargs = [
|
|
163
|
-
"appleshare_info",
|
|
164
|
-
"driver_name",
|
|
165
|
-
"posix_path",
|
|
166
|
-
"disk_image_alias",
|
|
167
|
-
"dialup_info",
|
|
168
|
-
"network_mount_info",
|
|
169
|
-
]
|
|
170
|
-
for a in kwargs:
|
|
171
|
-
v = getattr(self, a)
|
|
172
|
-
if v is not None:
|
|
173
|
-
values.append(f"{a}={v!r}")
|
|
174
|
-
return "VolumeInfo(%s)" % ",".join(values)
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
class TargetInfo:
|
|
178
|
-
def __init__(
|
|
179
|
-
self,
|
|
180
|
-
kind,
|
|
181
|
-
filename,
|
|
182
|
-
folder_cnid,
|
|
183
|
-
cnid,
|
|
184
|
-
creation_date,
|
|
185
|
-
creator_code,
|
|
186
|
-
type_code,
|
|
187
|
-
levels_from=-1,
|
|
188
|
-
levels_to=-1,
|
|
189
|
-
folder_name=None,
|
|
190
|
-
cnid_path=None,
|
|
191
|
-
carbon_path=None,
|
|
192
|
-
posix_path=None,
|
|
193
|
-
user_home_prefix_len=None,
|
|
194
|
-
):
|
|
195
|
-
#: Either ALIAS_KIND_FILE or ALIAS_KIND_FOLDER
|
|
196
|
-
self.kind = kind
|
|
197
|
-
|
|
198
|
-
#: The filename of the target
|
|
199
|
-
self.filename = filename
|
|
200
|
-
|
|
201
|
-
#: The CNID (Catalog Node ID) of the target's containing folder;
|
|
202
|
-
#: CNIDs are similar to but different than traditional UNIX inode
|
|
203
|
-
#: numbers
|
|
204
|
-
self.folder_cnid = folder_cnid
|
|
205
|
-
|
|
206
|
-
#: The CNID (Catalog Node ID) of the target
|
|
207
|
-
self.cnid = cnid
|
|
208
|
-
|
|
209
|
-
#: The target's *creation* date.
|
|
210
|
-
self.creation_date = creation_date
|
|
211
|
-
|
|
212
|
-
#: The target's Mac creator code (a four-character binary string)
|
|
213
|
-
self.creator_code = creator_code
|
|
214
|
-
|
|
215
|
-
#: The target's Mac type code (a four-character binary string)
|
|
216
|
-
self.type_code = type_code
|
|
217
|
-
|
|
218
|
-
#: The depth of the alias? Always seems to be -1 on OS X.
|
|
219
|
-
self.levels_from = levels_from
|
|
220
|
-
|
|
221
|
-
#: The depth of the target? Always seems to be -1 on OS X.
|
|
222
|
-
self.levels_to = levels_to
|
|
223
|
-
|
|
224
|
-
#: The (POSIX) name of the target's containing folder. *(optional)*
|
|
225
|
-
self.folder_name = folder_name
|
|
226
|
-
|
|
227
|
-
#: The path from the volume root as a sequence of CNIDs. *(optional)*
|
|
228
|
-
self.cnid_path = cnid_path
|
|
229
|
-
|
|
230
|
-
#: The Carbon path of the target *(optional)*
|
|
231
|
-
self.carbon_path = carbon_path
|
|
232
|
-
|
|
233
|
-
#: The POSIX path of the target relative to the volume root. Note
|
|
234
|
-
#: that this may or may not have a leading '/' character, but it is
|
|
235
|
-
#: always relative to the containing volume. *(optional)*
|
|
236
|
-
self.posix_path = posix_path
|
|
237
|
-
|
|
238
|
-
#: If the path points into a user's home folder, the number of folders
|
|
239
|
-
#: deep that we go before we get to that home folder. *(optional)*
|
|
240
|
-
self.user_home_prefix_len = user_home_prefix_len
|
|
241
|
-
|
|
242
|
-
def __repr__(self):
|
|
243
|
-
args = [
|
|
244
|
-
"kind",
|
|
245
|
-
"filename",
|
|
246
|
-
"folder_cnid",
|
|
247
|
-
"cnid",
|
|
248
|
-
"creation_date",
|
|
249
|
-
"creator_code",
|
|
250
|
-
"type_code",
|
|
251
|
-
]
|
|
252
|
-
values = []
|
|
253
|
-
for a in args:
|
|
254
|
-
v = getattr(self, a)
|
|
255
|
-
values.append(repr(v))
|
|
256
|
-
|
|
257
|
-
if self.levels_from != -1:
|
|
258
|
-
values.append("levels_from=%r" % self.levels_from)
|
|
259
|
-
if self.levels_to != -1:
|
|
260
|
-
values.append("levels_to=%r" % self.levels_to)
|
|
261
|
-
|
|
262
|
-
kwargs = [
|
|
263
|
-
"folder_name",
|
|
264
|
-
"cnid_path",
|
|
265
|
-
"carbon_path",
|
|
266
|
-
"posix_path",
|
|
267
|
-
"user_home_prefix_len",
|
|
268
|
-
]
|
|
269
|
-
for a in kwargs:
|
|
270
|
-
v = getattr(self, a)
|
|
271
|
-
values.append(f"{a}={v!r}")
|
|
272
|
-
|
|
273
|
-
return "TargetInfo(%s)" % ",".join(values)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
TAG_CARBON_FOLDER_NAME = 0
|
|
277
|
-
TAG_CNID_PATH = 1
|
|
278
|
-
TAG_CARBON_PATH = 2
|
|
279
|
-
TAG_APPLESHARE_ZONE = 3
|
|
280
|
-
TAG_APPLESHARE_SERVER_NAME = 4
|
|
281
|
-
TAG_APPLESHARE_USERNAME = 5
|
|
282
|
-
TAG_DRIVER_NAME = 6
|
|
283
|
-
TAG_NETWORK_MOUNT_INFO = 9
|
|
284
|
-
TAG_DIALUP_INFO = 10
|
|
285
|
-
TAG_UNICODE_FILENAME = 14
|
|
286
|
-
TAG_UNICODE_VOLUME_NAME = 15
|
|
287
|
-
TAG_HIGH_RES_VOLUME_CREATION_DATE = 16
|
|
288
|
-
TAG_HIGH_RES_CREATION_DATE = 17
|
|
289
|
-
TAG_POSIX_PATH = 18
|
|
290
|
-
TAG_POSIX_PATH_TO_MOUNTPOINT = 19
|
|
291
|
-
TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE = 20
|
|
292
|
-
TAG_USER_HOME_LENGTH_PREFIX = 21
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
class Alias:
|
|
296
|
-
def __init__(
|
|
297
|
-
self,
|
|
298
|
-
appinfo=b"\0\0\0\0",
|
|
299
|
-
version=2,
|
|
300
|
-
volume=None,
|
|
301
|
-
target=None,
|
|
302
|
-
extra=[],
|
|
303
|
-
):
|
|
304
|
-
"""Construct a new :class:`Alias` object with the specified
|
|
305
|
-
contents."""
|
|
306
|
-
|
|
307
|
-
#: Application specific information (four byte byte-string)
|
|
308
|
-
self.appinfo = appinfo
|
|
309
|
-
|
|
310
|
-
#: Version (we support versions 2 and 3)
|
|
311
|
-
self.version = version
|
|
312
|
-
|
|
313
|
-
#: A :class:`VolumeInfo` object describing the target's volume
|
|
314
|
-
self.volume = volume
|
|
315
|
-
|
|
316
|
-
#: A :class:`TargetInfo` object describing the target
|
|
317
|
-
self.target = target
|
|
318
|
-
|
|
319
|
-
#: A list of extra `(tag, value)` pairs
|
|
320
|
-
self.extra = list(extra)
|
|
321
|
-
|
|
322
|
-
@classmethod
|
|
323
|
-
def _from_fd(cls, b):
|
|
324
|
-
appinfo, recsize, version = struct.unpack(b">4shh", b.read(8))
|
|
325
|
-
|
|
326
|
-
if recsize < 150:
|
|
327
|
-
raise ValueError("Incorrect alias length")
|
|
328
|
-
|
|
329
|
-
if version not in (2, 3):
|
|
330
|
-
raise ValueError("Unsupported alias version %u" % version)
|
|
331
|
-
|
|
332
|
-
if version == 2:
|
|
333
|
-
(
|
|
334
|
-
kind, # h
|
|
335
|
-
volname, # 28p
|
|
336
|
-
voldate, # I
|
|
337
|
-
fstype, # 2s
|
|
338
|
-
disktype, # h
|
|
339
|
-
folder_cnid, # I
|
|
340
|
-
filename, # 64p
|
|
341
|
-
cnid, # I
|
|
342
|
-
crdate, # I
|
|
343
|
-
creator_code, # 4s
|
|
344
|
-
type_code, # 4s
|
|
345
|
-
levels_from, # h
|
|
346
|
-
levels_to, # h
|
|
347
|
-
volattrs, # I
|
|
348
|
-
volfsid, # 2s
|
|
349
|
-
reserved, # 10s
|
|
350
|
-
) = struct.unpack(b">h28pI2shI64pII4s4shhI2s10s", b.read(142))
|
|
351
|
-
else:
|
|
352
|
-
(
|
|
353
|
-
kind, # h
|
|
354
|
-
voldate_hr, # Q
|
|
355
|
-
fstype, # 4s
|
|
356
|
-
disktype, # h
|
|
357
|
-
folder_cnid, # I
|
|
358
|
-
cnid, # I
|
|
359
|
-
crdate_hr, # Q
|
|
360
|
-
volattrs, # I
|
|
361
|
-
reserved, # 14s
|
|
362
|
-
) = struct.unpack(b">hQ4shIIQI14s", b.read(46))
|
|
363
|
-
|
|
364
|
-
volname = b""
|
|
365
|
-
filename = b""
|
|
366
|
-
creator_code = None
|
|
367
|
-
type_code = None
|
|
368
|
-
voldate = voldate_hr / 65536.0
|
|
369
|
-
crdate = crdate_hr / 65536.0
|
|
370
|
-
|
|
371
|
-
voldate = mac_epoch + datetime.timedelta(seconds=voldate)
|
|
372
|
-
crdate = mac_epoch + datetime.timedelta(seconds=crdate)
|
|
373
|
-
|
|
374
|
-
alias = Alias()
|
|
375
|
-
alias.appinfo = appinfo
|
|
376
|
-
|
|
377
|
-
alias.volume = VolumeInfo(
|
|
378
|
-
volname.decode().replace("/", ":"),
|
|
379
|
-
voldate,
|
|
380
|
-
fstype,
|
|
381
|
-
disktype,
|
|
382
|
-
volattrs,
|
|
383
|
-
volfsid,
|
|
384
|
-
)
|
|
385
|
-
alias.target = TargetInfo(
|
|
386
|
-
kind,
|
|
387
|
-
filename.decode().replace("/", ":"),
|
|
388
|
-
folder_cnid,
|
|
389
|
-
cnid,
|
|
390
|
-
crdate,
|
|
391
|
-
creator_code,
|
|
392
|
-
type_code,
|
|
393
|
-
)
|
|
394
|
-
alias.target.levels_from = levels_from
|
|
395
|
-
alias.target.levels_to = levels_to
|
|
396
|
-
|
|
397
|
-
tag = struct.unpack(b">h", b.read(2))[0]
|
|
398
|
-
|
|
399
|
-
while tag != -1:
|
|
400
|
-
length = struct.unpack(b">h", b.read(2))[0]
|
|
401
|
-
value = b.read(length)
|
|
402
|
-
if length & 1:
|
|
403
|
-
b.read(1)
|
|
404
|
-
|
|
405
|
-
if tag == TAG_CARBON_FOLDER_NAME:
|
|
406
|
-
alias.target.folder_name = value.decode().replace("/", ":")
|
|
407
|
-
elif tag == TAG_CNID_PATH:
|
|
408
|
-
alias.target.cnid_path = struct.unpack(">%uI" % (length // 4), value)
|
|
409
|
-
elif tag == TAG_CARBON_PATH:
|
|
410
|
-
alias.target.carbon_path = value
|
|
411
|
-
elif tag == TAG_APPLESHARE_ZONE:
|
|
412
|
-
if alias.volume.appleshare_info is None:
|
|
413
|
-
alias.volume.appleshare_info = AppleShareInfo()
|
|
414
|
-
alias.volume.appleshare_info.zone = value
|
|
415
|
-
elif tag == TAG_APPLESHARE_SERVER_NAME:
|
|
416
|
-
if alias.volume.appleshare_info is None:
|
|
417
|
-
alias.volume.appleshare_info = AppleShareInfo()
|
|
418
|
-
alias.volume.appleshare_info.server = value
|
|
419
|
-
elif tag == TAG_APPLESHARE_USERNAME:
|
|
420
|
-
if alias.volume.appleshare_info is None:
|
|
421
|
-
alias.volume.appleshare_info = AppleShareInfo()
|
|
422
|
-
alias.volume.appleshare_info.user = value
|
|
423
|
-
elif tag == TAG_DRIVER_NAME:
|
|
424
|
-
alias.volume.driver_name = value
|
|
425
|
-
elif tag == TAG_NETWORK_MOUNT_INFO:
|
|
426
|
-
alias.volume.network_mount_info = value
|
|
427
|
-
elif tag == TAG_DIALUP_INFO:
|
|
428
|
-
alias.volume.dialup_info = value
|
|
429
|
-
elif tag == TAG_UNICODE_FILENAME:
|
|
430
|
-
alias.target.filename = value[2:].decode("utf-16be")
|
|
431
|
-
elif tag == TAG_UNICODE_VOLUME_NAME:
|
|
432
|
-
alias.volume.name = value[2:].decode("utf-16be")
|
|
433
|
-
elif tag == TAG_HIGH_RES_VOLUME_CREATION_DATE:
|
|
434
|
-
seconds = struct.unpack(b">Q", value)[0] / 65536.0
|
|
435
|
-
alias.volume.creation_date = mac_epoch + datetime.timedelta(
|
|
436
|
-
seconds=seconds
|
|
437
|
-
)
|
|
438
|
-
elif tag == TAG_HIGH_RES_CREATION_DATE:
|
|
439
|
-
seconds = struct.unpack(b">Q", value)[0] / 65536.0
|
|
440
|
-
alias.target.creation_date = mac_epoch + datetime.timedelta(
|
|
441
|
-
seconds=seconds
|
|
442
|
-
)
|
|
443
|
-
elif tag == TAG_POSIX_PATH:
|
|
444
|
-
alias.target.posix_path = value.decode()
|
|
445
|
-
elif tag == TAG_POSIX_PATH_TO_MOUNTPOINT:
|
|
446
|
-
alias.volume.posix_path = value.decode()
|
|
447
|
-
elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE:
|
|
448
|
-
alias.volume.disk_image_alias = Alias.from_bytes(value)
|
|
449
|
-
elif tag == TAG_USER_HOME_LENGTH_PREFIX:
|
|
450
|
-
alias.target.user_home_prefix_len = struct.unpack(b">h", value)[0]
|
|
451
|
-
else:
|
|
452
|
-
alias.extra.append((tag, value))
|
|
453
|
-
|
|
454
|
-
tag = struct.unpack(b">h", b.read(2))[0]
|
|
455
|
-
|
|
456
|
-
return alias
|
|
457
|
-
|
|
458
|
-
@classmethod
|
|
459
|
-
def from_bytes(cls, bytes):
|
|
460
|
-
"""Construct an :class:`Alias` object given binary Alias data."""
|
|
461
|
-
with io.BytesIO(bytes) as b:
|
|
462
|
-
return cls._from_fd(b)
|
|
463
|
-
|
|
464
|
-
@classmethod
|
|
465
|
-
def for_file(cls, path):
|
|
466
|
-
"""Create an :class:`Alias` that points at the specified file."""
|
|
467
|
-
if sys.platform != "darwin":
|
|
468
|
-
raise Exception("Not implemented (requires special support)")
|
|
469
|
-
|
|
470
|
-
path = encode_utf8(path)
|
|
471
|
-
|
|
472
|
-
a = Alias()
|
|
473
|
-
|
|
474
|
-
# Find the filesystem
|
|
475
|
-
st = osx.statfs(path)
|
|
476
|
-
vol_path = st.f_mntonname
|
|
477
|
-
|
|
478
|
-
# File and folder names in HFS+ are normalized to a form similar to NFD.
|
|
479
|
-
# Must be normalized (NFD->NFC) before use to avoid unicode string comparison issues.
|
|
480
|
-
vol_path = normalize("NFC", vol_path.decode("utf-8")).encode("utf-8")
|
|
481
|
-
|
|
482
|
-
# Grab its attributes
|
|
483
|
-
attrs = [osx.ATTR_CMN_CRTIME, osx.ATTR_VOL_NAME, 0, 0, 0]
|
|
484
|
-
volinfo = osx.getattrlist(vol_path, attrs, 0)
|
|
485
|
-
|
|
486
|
-
vol_crtime = volinfo[0]
|
|
487
|
-
vol_name = encode_utf8(volinfo[1])
|
|
488
|
-
|
|
489
|
-
# Also grab various attributes of the file
|
|
490
|
-
attrs = [
|
|
491
|
-
osx.ATTR_CMN_OBJTYPE
|
|
492
|
-
| osx.ATTR_CMN_CRTIME
|
|
493
|
-
| osx.ATTR_CMN_FNDRINFO
|
|
494
|
-
| osx.ATTR_CMN_FILEID
|
|
495
|
-
| osx.ATTR_CMN_PARENTID,
|
|
496
|
-
0,
|
|
497
|
-
0,
|
|
498
|
-
0,
|
|
499
|
-
0,
|
|
500
|
-
]
|
|
501
|
-
info = osx.getattrlist(path, attrs, osx.FSOPT_NOFOLLOW)
|
|
502
|
-
|
|
503
|
-
if info[0] == osx.VDIR:
|
|
504
|
-
kind = ALIAS_KIND_FOLDER
|
|
505
|
-
else:
|
|
506
|
-
kind = ALIAS_KIND_FILE
|
|
507
|
-
|
|
508
|
-
cnid = info[3]
|
|
509
|
-
folder_cnid = info[4]
|
|
510
|
-
|
|
511
|
-
dirname, filename = os.path.split(path)
|
|
512
|
-
|
|
513
|
-
if dirname == b"" or dirname == b".":
|
|
514
|
-
dirname = os.getcwd()
|
|
515
|
-
|
|
516
|
-
foldername = os.path.basename(dirname)
|
|
517
|
-
|
|
518
|
-
creation_date = info[1]
|
|
519
|
-
|
|
520
|
-
if kind == ALIAS_KIND_FILE:
|
|
521
|
-
creator_code = struct.pack(b"I", info[2].fileInfo.fileCreator)
|
|
522
|
-
type_code = struct.pack(b"I", info[2].fileInfo.fileType)
|
|
523
|
-
else:
|
|
524
|
-
creator_code = b"\0\0\0\0"
|
|
525
|
-
type_code = b"\0\0\0\0"
|
|
526
|
-
|
|
527
|
-
a.target = TargetInfo(
|
|
528
|
-
kind, filename, folder_cnid, cnid, creation_date, creator_code, type_code
|
|
529
|
-
)
|
|
530
|
-
a.volume = VolumeInfo(vol_name, vol_crtime, b"H+", ALIAS_FIXED_DISK, 0, b"\0\0")
|
|
531
|
-
|
|
532
|
-
a.target.folder_name = foldername
|
|
533
|
-
a.volume.posix_path = vol_path
|
|
534
|
-
|
|
535
|
-
rel_path = os.path.relpath(path, vol_path)
|
|
536
|
-
|
|
537
|
-
# Leave off the initial '/' if vol_path is '/' (no idea why)
|
|
538
|
-
if vol_path == b"/":
|
|
539
|
-
a.target.posix_path = rel_path
|
|
540
|
-
else:
|
|
541
|
-
a.target.posix_path = b"/" + rel_path
|
|
542
|
-
|
|
543
|
-
# Construct the Carbon and CNID paths
|
|
544
|
-
carbon_path = []
|
|
545
|
-
cnid_path = []
|
|
546
|
-
head, tail = os.path.split(rel_path)
|
|
547
|
-
if not tail:
|
|
548
|
-
head, tail = os.path.split(head)
|
|
549
|
-
while head or tail:
|
|
550
|
-
if head:
|
|
551
|
-
attrs = [osx.ATTR_CMN_FILEID, 0, 0, 0, 0]
|
|
552
|
-
info = osx.getattrlist(os.path.join(vol_path, head), attrs, 0)
|
|
553
|
-
cnid_path.append(info[0])
|
|
554
|
-
carbon_tail = tail.replace(b":", b"/")
|
|
555
|
-
carbon_path.insert(0, carbon_tail)
|
|
556
|
-
head, tail = os.path.split(head)
|
|
557
|
-
|
|
558
|
-
carbon_path = vol_name + b":" + b":\0".join(carbon_path)
|
|
559
|
-
|
|
560
|
-
a.target.carbon_path = carbon_path
|
|
561
|
-
a.target.cnid_path = cnid_path
|
|
562
|
-
|
|
563
|
-
return a
|
|
564
|
-
|
|
565
|
-
def _to_fd(self, b):
|
|
566
|
-
# We'll come back and fix the length when we're done
|
|
567
|
-
pos = b.tell()
|
|
568
|
-
b.write(struct.pack(b">4shh", self.appinfo, 0, self.version))
|
|
569
|
-
|
|
570
|
-
carbon_volname = encode_utf8(self.volume.name).replace(b":", b"/")
|
|
571
|
-
carbon_filename = encode_utf8(self.target.filename).replace(b":", b"/")
|
|
572
|
-
voldate = (self.volume.creation_date - mac_epoch).total_seconds()
|
|
573
|
-
crdate = (self.target.creation_date - mac_epoch).total_seconds()
|
|
574
|
-
|
|
575
|
-
if self.version == 2:
|
|
576
|
-
# NOTE: crdate should be in local time, but that's system dependent
|
|
577
|
-
# (so doing so is ridiculous, and nothing could rely on it).
|
|
578
|
-
b.write(
|
|
579
|
-
struct.pack(
|
|
580
|
-
b">h28pI2shI64pII4s4shhI2s10s",
|
|
581
|
-
self.target.kind, # h
|
|
582
|
-
carbon_volname, # 28p
|
|
583
|
-
int(voldate), # I
|
|
584
|
-
self.volume.fs_type, # 2s
|
|
585
|
-
self.volume.disk_type, # h
|
|
586
|
-
self.target.folder_cnid, # I
|
|
587
|
-
carbon_filename, # 64p
|
|
588
|
-
self.target.cnid, # I
|
|
589
|
-
int(crdate), # I
|
|
590
|
-
self.target.creator_code, # 4s
|
|
591
|
-
self.target.type_code, # 4s
|
|
592
|
-
self.target.levels_from, # h
|
|
593
|
-
self.target.levels_to, # h
|
|
594
|
-
self.volume.attribute_flags, # I
|
|
595
|
-
self.volume.fs_id, # 2s
|
|
596
|
-
b"\0" * 10, # 10s
|
|
597
|
-
)
|
|
598
|
-
)
|
|
599
|
-
else:
|
|
600
|
-
b.write(
|
|
601
|
-
struct.pack(
|
|
602
|
-
b">hQ4shIIQI14s",
|
|
603
|
-
self.target.kind, # h
|
|
604
|
-
int(voldate * 65536), # Q
|
|
605
|
-
self.volume.fs_type, # 4s
|
|
606
|
-
self.volume.disk_type, # h
|
|
607
|
-
self.target.folder_cnid, # I
|
|
608
|
-
self.target.cnid, # I
|
|
609
|
-
int(crdate * 65536), # Q
|
|
610
|
-
self.volume.attribute_flags, # I
|
|
611
|
-
b"\0" * 14, # 14s
|
|
612
|
-
)
|
|
613
|
-
)
|
|
614
|
-
|
|
615
|
-
# Excuse the odd order; we're copying Finder
|
|
616
|
-
if self.target.folder_name:
|
|
617
|
-
carbon_foldername = encode_utf8(self.target.folder_name).replace(b":", b"/")
|
|
618
|
-
b.write(struct.pack(b">hh", TAG_CARBON_FOLDER_NAME, len(carbon_foldername)))
|
|
619
|
-
b.write(carbon_foldername)
|
|
620
|
-
if len(carbon_foldername) & 1:
|
|
621
|
-
b.write(b"\0")
|
|
622
|
-
|
|
623
|
-
b.write(
|
|
624
|
-
struct.pack(
|
|
625
|
-
b">hhQhhQ",
|
|
626
|
-
TAG_HIGH_RES_VOLUME_CREATION_DATE,
|
|
627
|
-
8,
|
|
628
|
-
int(voldate * 65536),
|
|
629
|
-
TAG_HIGH_RES_CREATION_DATE,
|
|
630
|
-
8,
|
|
631
|
-
int(crdate * 65536),
|
|
632
|
-
)
|
|
633
|
-
)
|
|
634
|
-
|
|
635
|
-
if self.target.cnid_path:
|
|
636
|
-
cnid_path = struct.pack(
|
|
637
|
-
">%uI" % len(self.target.cnid_path), *self.target.cnid_path
|
|
638
|
-
)
|
|
639
|
-
b.write(struct.pack(b">hh", TAG_CNID_PATH, len(cnid_path)))
|
|
640
|
-
b.write(cnid_path)
|
|
641
|
-
|
|
642
|
-
if self.target.carbon_path:
|
|
643
|
-
carbon_path = encode_utf8(self.target.carbon_path)
|
|
644
|
-
b.write(struct.pack(b">hh", TAG_CARBON_PATH, len(carbon_path)))
|
|
645
|
-
b.write(carbon_path)
|
|
646
|
-
if len(carbon_path) & 1:
|
|
647
|
-
b.write(b"\0")
|
|
648
|
-
|
|
649
|
-
if self.volume.appleshare_info:
|
|
650
|
-
ai = self.volume.appleshare_info
|
|
651
|
-
if ai.zone:
|
|
652
|
-
b.write(struct.pack(b">hh", TAG_APPLESHARE_ZONE, len(ai.zone)))
|
|
653
|
-
b.write(ai.zone)
|
|
654
|
-
if len(ai.zone) & 1:
|
|
655
|
-
b.write(b"\0")
|
|
656
|
-
if ai.server:
|
|
657
|
-
b.write(struct.pack(b">hh", TAG_APPLESHARE_SERVER_NAME, len(ai.server)))
|
|
658
|
-
b.write(ai.server)
|
|
659
|
-
if len(ai.server) & 1:
|
|
660
|
-
b.write(b"\0")
|
|
661
|
-
if ai.username:
|
|
662
|
-
b.write(struct.pack(b">hh", TAG_APPLESHARE_USERNAME, len(ai.username)))
|
|
663
|
-
b.write(ai.username)
|
|
664
|
-
if len(ai.username) & 1:
|
|
665
|
-
b.write(b"\0")
|
|
666
|
-
|
|
667
|
-
if self.volume.driver_name:
|
|
668
|
-
driver_name = encode_utf8(self.volume.driver_name)
|
|
669
|
-
b.write(struct.pack(b">hh", TAG_DRIVER_NAME, len(driver_name)))
|
|
670
|
-
b.write(driver_name)
|
|
671
|
-
if len(driver_name) & 1:
|
|
672
|
-
b.write(b"\0")
|
|
673
|
-
|
|
674
|
-
if self.volume.network_mount_info:
|
|
675
|
-
b.write(
|
|
676
|
-
struct.pack(
|
|
677
|
-
b">hh", TAG_NETWORK_MOUNT_INFO, len(self.volume.network_mount_info)
|
|
678
|
-
)
|
|
679
|
-
)
|
|
680
|
-
b.write(self.volume.network_mount_info)
|
|
681
|
-
if len(self.volume.network_mount_info) & 1:
|
|
682
|
-
b.write(b"\0")
|
|
683
|
-
|
|
684
|
-
if self.volume.dialup_info:
|
|
685
|
-
b.write(
|
|
686
|
-
struct.pack(
|
|
687
|
-
b">hh", TAG_DIALUP_INFO, len(self.volume.network_mount_info)
|
|
688
|
-
)
|
|
689
|
-
)
|
|
690
|
-
b.write(self.volume.network_mount_info)
|
|
691
|
-
if len(self.volume.network_mount_info) & 1:
|
|
692
|
-
b.write(b"\0")
|
|
693
|
-
|
|
694
|
-
utf16 = decode_utf8(self.target.filename).replace(":", "/").encode("utf-16-be")
|
|
695
|
-
b.write(
|
|
696
|
-
struct.pack(b">hhh", TAG_UNICODE_FILENAME, len(utf16) + 2, len(utf16) // 2)
|
|
697
|
-
)
|
|
698
|
-
b.write(utf16)
|
|
699
|
-
|
|
700
|
-
utf16 = decode_utf8(self.volume.name).replace(":", "/").encode("utf-16-be")
|
|
701
|
-
b.write(
|
|
702
|
-
struct.pack(
|
|
703
|
-
b">hhh", TAG_UNICODE_VOLUME_NAME, len(utf16) + 2, len(utf16) // 2
|
|
704
|
-
)
|
|
705
|
-
)
|
|
706
|
-
b.write(utf16)
|
|
707
|
-
|
|
708
|
-
if self.target.posix_path:
|
|
709
|
-
posix_path = encode_utf8(self.target.posix_path)
|
|
710
|
-
b.write(struct.pack(b">hh", TAG_POSIX_PATH, len(posix_path)))
|
|
711
|
-
b.write(posix_path)
|
|
712
|
-
if len(posix_path) & 1:
|
|
713
|
-
b.write(b"\0")
|
|
714
|
-
|
|
715
|
-
if self.volume.posix_path:
|
|
716
|
-
posix_path = encode_utf8(self.volume.posix_path)
|
|
717
|
-
b.write(struct.pack(b">hh", TAG_POSIX_PATH_TO_MOUNTPOINT, len(posix_path)))
|
|
718
|
-
b.write(posix_path)
|
|
719
|
-
if len(posix_path) & 1:
|
|
720
|
-
b.write(b"\0")
|
|
721
|
-
|
|
722
|
-
if self.volume.disk_image_alias:
|
|
723
|
-
d = self.volume.disk_image_alias.to_bytes()
|
|
724
|
-
b.write(struct.pack(b">hh", TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE, len(d)))
|
|
725
|
-
b.write(d)
|
|
726
|
-
if len(d) & 1:
|
|
727
|
-
b.write(b"\0")
|
|
728
|
-
|
|
729
|
-
if self.target.user_home_prefix_len is not None:
|
|
730
|
-
b.write(
|
|
731
|
-
struct.pack(
|
|
732
|
-
b">hhh",
|
|
733
|
-
TAG_USER_HOME_LENGTH_PREFIX,
|
|
734
|
-
2,
|
|
735
|
-
self.target.user_home_prefix_len,
|
|
736
|
-
)
|
|
737
|
-
)
|
|
738
|
-
|
|
739
|
-
for t, v in self.extra:
|
|
740
|
-
b.write(struct.pack(b">hh", t, len(v)))
|
|
741
|
-
b.write(v)
|
|
742
|
-
if len(v) & 1:
|
|
743
|
-
b.write(b"\0")
|
|
744
|
-
|
|
745
|
-
b.write(struct.pack(b">hh", -1, 0))
|
|
746
|
-
|
|
747
|
-
blen = b.tell() - pos
|
|
748
|
-
b.seek(pos + 4, os.SEEK_SET)
|
|
749
|
-
b.write(struct.pack(b">h", blen))
|
|
750
|
-
|
|
751
|
-
def to_bytes(self):
|
|
752
|
-
"""Returns the binary representation for this :class:`Alias`."""
|
|
753
|
-
with io.BytesIO() as b:
|
|
754
|
-
self._to_fd(b)
|
|
755
|
-
return b.getvalue()
|
|
756
|
-
|
|
757
|
-
def __str__(self):
|
|
758
|
-
return "<Alias target=%s>" % self.target.filename
|
|
759
|
-
|
|
760
|
-
def __repr__(self):
|
|
761
|
-
values = []
|
|
762
|
-
if self.appinfo != b"\0\0\0\0":
|
|
763
|
-
values.append("appinfo=%r" % self.appinfo)
|
|
764
|
-
if self.version != 2:
|
|
765
|
-
values.append("version=%r" % self.version)
|
|
766
|
-
if self.volume is not None:
|
|
767
|
-
values.append("volume=%r" % self.volume)
|
|
768
|
-
if self.target is not None:
|
|
769
|
-
values.append("target=%r" % self.target)
|
|
770
|
-
if self.extra:
|
|
771
|
-
values.append("extra=%r" % self.extra)
|
|
772
|
-
return "Alias(%s)" % ",".join(values)
|