Commit 0e93ca10 authored by bescoto's avatar bescoto

Checked in John Goerzen's carbonfile patch


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@508 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent e8a09645
New in v0.13.4 (??????????)
---------------------------
Checked in patch by John Goerzen to support Mac OS X Finder
information. As John says:
> Specifically, it adds storage of:
>
> * 4-byte creator
> * 4-byte type
> * integer flags
> * dual integer location
Much thanks to John for adding this useful feature all by himself!
Regressing and restoring should now take less memory when processing
large directories (noticed by Luke Mewburn and others).
......
......@@ -85,6 +85,11 @@ resource_forks_active = None
resource_forks_write = None
resource_forks_conn = None
# Like the above, but applies to MacOS Carbon Finder creator/type info.
carbonfile_active = None
carbonfile_write = None
carbonfile_conn = None
# This will be set as soon as the LocalConnection class loads
local_connection = None
......
......@@ -385,6 +385,8 @@ def backup_set_fs_globals(rpin, rpout):
update_triple(src_fsa.resource_forks, dest_fsa.resource_forks,
('resource_forks_active', 'resource_forks_write',
'resource_forks_conn'))
update_triple(src_fsa.carbonfile, dest_fsa.carbonfile,
('carbonfile_active', 'carbonfile_write', 'carbonfile_conn'))
if Globals.never_drop_acls and not Globals.acls_active:
Log.FatalError("--never-drop-acls specified, but ACL support\n"
"disabled on destination filesystem")
......@@ -489,6 +491,8 @@ def restore_set_fs_globals(target):
update_triple(mirror_fsa.resource_forks, target_fsa.resource_forks,
('resource_forks_active', 'resource_forks_write',
'resource_forks_conn'))
update_triple(mirror_fsa.carbonfile, target_fsa.carbonfile,
('carbonfile_active', 'carbonfile_write', 'carbonfile_conn'))
if Globals.never_drop_acls and not Globals.acls_active:
Log.FatalError("--never-drop-acls specified, but ACL support\n"
"disabled on destination filesystem")
......
......@@ -40,6 +40,7 @@ class FSAbilities:
fsync_dirs = None # True if directories can be fsync'd
dir_inc_perms = None # True if regular files can have full permissions
resource_forks = None # True if regular_file/rsrc holds resource fork
carbonfile = None # True if Mac Carbon file data is supported.
name = None # Short string, not used for any technical purpose
read_only = None # True if capabilities were determined non-destructively
......@@ -92,7 +93,8 @@ class FSAbilities:
add_boolean_list([('Access control lists', self.acls),
('Extended attributes', self.eas),
('Mac OS X style resource forks',
self.resource_forks)])
self.resource_forks),
('Mac OS X Finder information', self.carbonfile)])
s.append(s[0])
return '\n'.join(s)
......@@ -112,6 +114,7 @@ class FSAbilities:
self.set_eas(rp, 0)
self.set_acls(rp)
self.set_resource_fork_readonly(rp)
self.set_carbonfile()
return self
def init_readwrite(self, rbdir, use_ctq_file = 1,
......@@ -145,6 +148,7 @@ class FSAbilities:
self.set_acls(subdir)
self.set_dir_inc_perms(subdir)
self.set_resource_fork_readwrite(subdir)
self.set_carbonfile()
if override_chars_to_quote is None: self.set_chars_to_quote(subdir)
else: self.chars_to_quote = override_chars_to_quote
if use_ctq_file: self.compare_chars_to_quote(rbdir)
......@@ -326,6 +330,23 @@ rdiff-backup-data/chars_to_quote.
else: self.dir_inc_perms = 0
test_rp.delete()
def set_carbonfile(self):
"""Test for support of the Mac Carbon library. This library
can be used to obtain Finder info (creator/type)."""
try:
import Carbon.File
import MacOS
except:
self.carbonfile = 0
return
try: x = Carbon.File.FSSpec('.')
except:
self.carbonfile = 0
return
self.carbonfile = 1
def set_resource_fork_readwrite(self, dir_rp):
"""Test for resource forks by writing to regular_file/rsrc"""
assert dir_rp.conn is Globals.local_connection
......
......@@ -62,6 +62,31 @@ class ParsingError(Exception):
"""This is raised when bad or unparsable data is received"""
pass
def carbonfile2string(cfile):
"""Convert CarbonFile data to a string suitable for storing."""
retvalparts = []
retvalparts.append('creator:%s' % binascii.hexlify(cfile['creator']))
retvalparts.append('type:%s' % binascii.hexlify(cfile['type']))
retvalparts.append('location:%d,%d' % cfile['location'])
retvalparts.append('flags:%d' % cfile['flags'])
return '|'.join(retvalparts)
def string2carbonfile(data):
"""Re-constitute CarbonFile data from a string stored by
carbonfile2string."""
retval = {}
for component in data.split('|'):
key, value = component.split(':')
if key == 'creator':
retval['creator'] = binascii.unhexlify(value)
elif key == 'type':
retval['type'] = binascii.unhexlify(value)
elif key == 'location':
a, b = value.split(',')
retval['location'] = (int(a), int(b))
elif key == 'flags':
retval['flags'] = int(value)
return retval
def RORP2Record(rorpath):
"""From RORPath, return text record of file's metadata"""
......@@ -79,6 +104,12 @@ def RORP2Record(rorpath):
if not rorpath.get_resource_fork(): rf = "None"
else: rf = binascii.hexlify(rorpath.get_resource_fork())
str_list.append(" ResourceFork %s\n" % (rf,))
# If there is Carbon data, save it.
if rorpath.has_carbonfile():
if not rorpath.get_carbonfile(): cfile = "None"
else: cfile = carbonfile2string(rorpath.get_carbonfile())
str_list.append(" CarbonFile %s\n" % (cfile,))
# If file is hardlinked, add that information
if Globals.preserve_hardlinks:
......@@ -132,6 +163,9 @@ def Record2RORP(record_string):
elif field == "ResourceFork":
if data == "None": data_dict['resourcefork'] = ""
else: data_dict['resourcefork'] = binascii.unhexlify(data)
elif field == "CarbonFile":
if data == "None": data_dict['carbonfile'] = None
else: data_dict['carbonfile'] = string2carbonfile(data)
elif field == "NumHardLinks": data_dict['nlink'] = int(data)
elif field == "Inode": data_dict['inode'] = long(data)
elif field == "DeviceLoc": data_dict['devloc'] = long(data)
......
......@@ -155,6 +155,8 @@ def copy_attribs(rpin, rpout):
if rpin.issym(): return # symlinks have no valid attributes
if Globals.resource_forks_write and rpin.isreg():
rpout.write_resource_fork(rpin.get_resource_fork())
if Globals.carbonfile_write and rpin.isreg():
rpout.write_carbonfile(rpin.get_carbonfile())
if Globals.eas_write: rpout.write_ea(rpin.get_ea())
if Globals.change_ownership: rpout.chown(*user_group.map_rpath(rpin))
rpout.chmod(rpin.getperms())
......@@ -174,6 +176,8 @@ def copy_attribs_inc(rpin, rpout):
if rpin.issym(): return # symlinks have no valid attributes
if Globals.resource_forks_write and rpin.isreg() and rpout.isreg():
rpout.write_resource_fork(rpin.get_resource_fork())
if Globals.carbonfile_write and rpin.isreg() and rpout.isreg():
rpout.write_carbonfile(rpin.get_carbonfile())
if Globals.eas_write: rpout.write_ea(rpin.get_ea())
if Globals.change_ownership: apply(rpout.chown, rpin.getuidgid())
if rpin.isdir() and not rpout.isdir():
......@@ -294,6 +298,7 @@ class RORPath:
elif key == 'size' and not self.isreg(): pass
elif key == 'ea' and not Globals.eas_active: pass
elif key == 'acl' and not Globals.acls_active: pass
elif key == 'carbonfile' and not Globals.carbonfile_active: pass
elif key == 'resourcefork' and not Globals.resource_forks_active:
pass
elif key == 'uname' or key == 'gname':
......@@ -333,6 +338,7 @@ class RORPath:
elif key == 'inode': pass
elif key == 'ea' and not Globals.eas_write: pass
elif key == 'acl' and not Globals.acls_write: pass
elif key == 'carbonfile' and not Globals.carbonfile_write: pass
elif key == 'resourcefork' and not Globals.resource_forks_write:
pass
elif (not other.data.has_key(key) or
......@@ -589,6 +595,18 @@ class RORPath:
"""Return extended attributes object"""
return self.data['ea']
def has_carbonfile(self):
"""True if rpath has a carbonfile parameter"""
return self.data.has_key('carbonfile')
def get_carbonfile(self):
"""Returns the carbonfile data"""
return self.data['carbonfile']
def set_carbonfile(self, cfile):
"""Record carbonfile data in dictionary. Does not write."""
self.data['carbonfile'] = cfile
def has_resource_fork(self):
"""True if rpath has a resourcefork parameter"""
return self.data.has_key('resourcefork')
......@@ -1059,6 +1077,41 @@ class RPath(RORPath):
ea.write_to_rp(self)
self.data['ea'] = ea
def get_carbonfile(self):
"""Return resource fork data, loading from filesystem if
necessary."""
from Carbon.File import FSSpec
import MacOS
try: return self.data['cfile']
except KeyError: pass
try:
fsobj = FSSpec(self.path)
finderinfo = fsobj.FSpGetFInfo()
cfile = {'creator': finderinfo.Creator,
'type': finderinfo.Type,
'location': finderinfo.Location,
'flags': finderinfo.Flags}
self.data['carbonfile'] = cfile
return cfile
except MacOS.Error:
self.data['carbonfile'] = None
return self.data['carbonfile']
def write_carbonfile(self, cfile):
"""Write new carbon data to self."""
log.Log("Writing carbon data to %s" % (self.index,), 7)
from Carbon.File import FSSpec
import MacOS
fsobj = FSSpec(self.path)
finderinfo = fsobj.FSpGetFInfo()
finderinfo.Creator = cfile['creator']
finderinfo.Type = cfile['type']
finderinfo.Location = cfile['location']
finderinfo.Flags = cfile['flags']
fsobj.FSpSetFInfo(finderinfo)
self.set_carbonfile(cfile)
def get_resource_fork(self):
"""Return resource fork data, setting if necessary"""
assert self.isreg()
......@@ -1112,7 +1165,7 @@ def setdata_local(rpath):
if Globals.acls_conn: rpath.data['acl'] = acl_get(rpath)
if Globals.resource_forks_conn and rpath.isreg():
rpath.get_resource_fork()
if Globals.carbonfile_conn and rpath.isreg(): rpath.get_carbonfile()
# These two are overwritten by the eas_acls.py module. We can't
# import that module directly because of circular dependency problems.
......
......@@ -19,8 +19,7 @@ class FSAbilitiesTest(unittest.TestCase):
hardlinks = fsync_dirs = 1
dir_inc_perms = 1
resource_forks = 0
carbonfile = 0
# Describes MS-Windows style file system
#dir_to_test = "/mnt/fat"
......@@ -30,6 +29,7 @@ class FSAbilitiesTest(unittest.TestCase):
#fsync_dirs = 1
#dir_inc_perms = 0
#resource_forks = 0
#carbonfile = 0
def testReadOnly(self):
"""Test basic querying read only"""
......@@ -40,6 +40,7 @@ class FSAbilitiesTest(unittest.TestCase):
assert fsa.eas == self.eas, fsa.eas
assert fsa.acls == self.acls, fsa.acls
assert fsa.resource_forks == self.resource_forks, fsa.resource_forks
assert fsa.carbonfile == self.carbonfile, fsa.carbonfile
def testReadWrite(self):
"""Test basic querying read/write"""
......@@ -61,7 +62,8 @@ class FSAbilitiesTest(unittest.TestCase):
assert fsa.fsync_dirs == self.fsync_dirs, fsa.fsync_dirs
assert fsa.dir_inc_perms == self.dir_inc_perms, fsa.dir_inc_perms
assert fsa.resource_forks == self.resource_forks, fsa.resource_forks
assert fsa.carbonfile == self.carbonfile, fsa.carbonfile
ctq_rp = new_dir.append("chars_to_quote")
assert ctq_rp.lstat()
fp = ctq_rp.open('rb')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment