Stop rewriting an alias file's own metadata
Writing a volume rebuilds every alias file, because the `alis` record names its target by CNID and CNIDs are assigned afresh on each write. It rebuilt more than the record, though: the file's type and creator were overwritten with ones guessed from the target, the rest of the resource fork was thrown away, and the data fork was cleared. Round-tripping the Apple Menu's Control Panels alias out of a System 7 image therefore turned `fact` into `fdrp`, dropped its `vers` resources, and renamed the `alis` from "Control Panels alias" to "Control Panels". Aliases to applications lost their `adrp` for whatever the target's type happened to be -- `APPC`, `cdev`. Only the `alis` resource is replaced now, in place and under its own name; type and creator are guessed only for an alias built here, which has neither. Reading and rewriting the 29 infinite-mac images machfs can parse touches 73 alias files: type and creator used to change on 10 of them and now change on none, and 28 resource forks used to lose more than the `alis` record and now lose nothing else. (Their data forks were already empty, so clearing those had cost nothing in practice.) For mihaip/infinite-mac#84
This commit is contained in:
1 parent
1f8e7b5288
commit
d32a327c5f
2 files changed
+56
-3
No files matched your search
+22
-3
@@ -474,6 +474,11 @@ class Volume(AbstractFolder):
|
|||||||
aliastarget = (self.name,) + aliastarget # match the convention for this function
|
aliastarget = (self.name,) + aliastarget # match the convention for this function
|
||||||
targetobj = path2wrap[aliastarget].of # probe the target to set some metadata
|
targetobj = path2wrap[aliastarget].of # probe the target to set some metadata
|
||||||
|
|
||||||
|
# An alias read from a volume carries the type and creator the
|
||||||
|
# Finder gave it, which the guesses below do not reproduce:
|
||||||
|
# 'fact' for Control Panels, 'faet' for Automated Tasks. So
|
||||||
|
# only guess for an alias built here, which has neither.
|
||||||
|
if obj.type == b'????' and obj.creator == b'????':
|
||||||
if isinstance(targetobj, Folder):
|
if isinstance(targetobj, Folder):
|
||||||
wrap.creator = b'MACS'
|
wrap.creator = b'MACS'
|
||||||
wrap.type = b'fdrp'
|
wrap.type = b'fdrp'
|
||||||
@@ -490,8 +495,6 @@ class Volume(AbstractFolder):
|
|||||||
else:
|
else:
|
||||||
wrap.type = targetobj.type
|
wrap.type = targetobj.type
|
||||||
|
|
||||||
wrap.data = b''
|
|
||||||
|
|
||||||
userType = b''
|
userType = b''
|
||||||
aliasSize = 9999 # fill this short at offset 4
|
aliasSize = 9999 # fill this short at offset 4
|
||||||
aliasVersion = 2
|
aliasVersion = 2
|
||||||
@@ -514,7 +517,19 @@ class Volume(AbstractFolder):
|
|||||||
# Stress test: find file by name, not CNID
|
# Stress test: find file by name, not CNID
|
||||||
# fileNum = 0
|
# fileNum = 0
|
||||||
|
|
||||||
|
# The record below holds CNIDs, so it has to be rebuilt for this
|
||||||
|
# volume. The rest of the fork belongs to the file: Control
|
||||||
|
# Panels keeps its 'vers', and the 'alis' keeps its own name.
|
||||||
|
resources = list(parse_file(obj.rsrc)) if obj.rsrc else []
|
||||||
|
alisIndex = next((i for i, r in enumerate(resources)
|
||||||
|
if (r.type, r.id) == (b'alis', 0)), None)
|
||||||
|
|
||||||
|
if alisIndex is None:
|
||||||
alis = Resource(b'alis', 0, name=path[-1])
|
alis = Resource(b'alis', 0, name=path[-1])
|
||||||
|
else:
|
||||||
|
oldalis = resources[alisIndex]
|
||||||
|
alis = Resource(b'alis', 0, name=oldalis.name)
|
||||||
|
alis.attribs = oldalis.attribs
|
||||||
alis.data[:] = struct.pack('>4s H hh 28p L 2s hL 64p LL 4s4s HHLh',
|
alis.data[:] = struct.pack('>4s H hh 28p L 2s hL 64p LL 4s4s HHLh',
|
||||||
userType, aliasSize, aliasVersion, \
|
userType, aliasSize, aliasVersion, \
|
||||||
thisAliasKind, volumeName, volumeCrDate, \
|
thisAliasKind, volumeName, volumeCrDate, \
|
||||||
@@ -531,7 +546,11 @@ class Volume(AbstractFolder):
|
|||||||
|
|
||||||
# open('/tmp/creating','wb').write(alis.data)
|
# open('/tmp/creating','wb').write(alis.data)
|
||||||
|
|
||||||
wrap.rsrc = make_file([alis])
|
if alisIndex is None:
|
||||||
|
resources.append(alis)
|
||||||
|
else:
|
||||||
|
resources[alisIndex] = alis
|
||||||
|
wrap.rsrc = make_file(resources)
|
||||||
|
|
||||||
if isinstance(obj, File):
|
if isinstance(obj, File):
|
||||||
wrap.dfrk = wrap.rfrk = (0, 0)
|
wrap.dfrk = wrap.rfrk = (0, 0)
|
||||||
|
|||||||
+34
@@ -1,5 +1,7 @@
|
|||||||
from machfs import *
|
from machfs import *
|
||||||
from machfs import bitmanip, btree
|
from machfs import bitmanip, btree
|
||||||
|
from machfs.main import FinderFlags
|
||||||
|
from macresources import Resource, make_file, parse_file
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
import time
|
import time
|
||||||
@@ -27,6 +29,38 @@ def test_roundtrip():
|
|||||||
assert f.data in copies[-1]
|
assert f.data in copies[-1]
|
||||||
|
|
||||||
|
|
||||||
|
def test_alias_keeps_what_belongs_to_the_file():
|
||||||
|
h = Volume()
|
||||||
|
h.name = 'AliasTest'
|
||||||
|
|
||||||
|
target = File()
|
||||||
|
target.type, target.creator = b'APPC', b'wnkl'
|
||||||
|
target.data = b'target data'
|
||||||
|
h['target'] = target
|
||||||
|
|
||||||
|
# Neither 'fact' nor the 'vers' resource can be derived from the target.
|
||||||
|
vers = Resource(b'vers', 2, data=b'\x01\x00\x80\x00\x00\x00\x041.0')
|
||||||
|
stale = Resource(b'alis', 0, name='alias resource name', data=bytes(150))
|
||||||
|
alias = File()
|
||||||
|
alias.flags |= FinderFlags.kIsAlias
|
||||||
|
alias.type, alias.creator = b'fact', b'MACS'
|
||||||
|
alias.rsrc = make_file([stale, vers])
|
||||||
|
alias.aliastarget = target
|
||||||
|
h['the alias'] = alias
|
||||||
|
|
||||||
|
copy = Volume()
|
||||||
|
copy.read(h.write(10*1024*1024, desktopdb=False, bootable=False))
|
||||||
|
got = copy['the alias']
|
||||||
|
|
||||||
|
assert (got.type, got.creator) == (b'fact', b'MACS')
|
||||||
|
|
||||||
|
resources = {(r.type, r.id): r for r in parse_file(got.rsrc)}
|
||||||
|
assert resources[b'vers', 2].data == vers.data
|
||||||
|
assert resources[b'alis', 0].name == 'alias resource name'
|
||||||
|
# The record itself holds this volume's CNIDs, so it is rebuilt.
|
||||||
|
assert resources[b'alis', 0].data != stale.data
|
||||||
|
|
||||||
|
|
||||||
def test_open_folder():
|
def test_open_folder():
|
||||||
h = Volume()
|
h = Volume()
|
||||||
h.name = 'OpenFolderTest'
|
h.name = 'OpenFolderTest'
|
||||||
|
|||||||
Reference in new issue
Block a user