summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbescoto <bescoto@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109>2004-01-28 06:54:53 +0000
committerbescoto <bescoto@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109>2004-01-28 06:54:53 +0000
commit06d65d2d192b904e81fa17828c3b8b7dd273bb02 (patch)
treebb560713170517416384be01aedcfc57359b1e6e
parenta41f18f654feaea3dfab6955df177e9956823ea2 (diff)
downloadrdiff-backup-06d65d2d192b904e81fa17828c3b8b7dd273bb02.tar.gz
Checked in John Goerzen's carbonfile patch
git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@508 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
-rw-r--r--rdiff-backup/CHANGELOG10
-rw-r--r--rdiff-backup/rdiff_backup/Globals.py5
-rw-r--r--rdiff-backup/rdiff_backup/Main.py4
-rw-r--r--rdiff-backup/rdiff_backup/fs_abilities.py23
-rw-r--r--rdiff-backup/rdiff_backup/metadata.py34
-rw-r--r--rdiff-backup/rdiff_backup/rpath.py55
-rw-r--r--rdiff-backup/testing/fs_abilitiestest.py8
7 files changed, 134 insertions, 5 deletions
diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG
index 911ddc2..8871415 100644
--- a/rdiff-backup/CHANGELOG
+++ b/rdiff-backup/CHANGELOG
@@ -1,6 +1,16 @@
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).
diff --git a/rdiff-backup/rdiff_backup/Globals.py b/rdiff-backup/rdiff_backup/Globals.py
index 6a486ad..cfb0e1a 100644
--- a/rdiff-backup/rdiff_backup/Globals.py
+++ b/rdiff-backup/rdiff_backup/Globals.py
@@ -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
diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py
index 70c7aac..e417013 100644
--- a/rdiff-backup/rdiff_backup/Main.py
+++ b/rdiff-backup/rdiff_backup/Main.py
@@ -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")
diff --git a/rdiff-backup/rdiff_backup/fs_abilities.py b/rdiff-backup/rdiff_backup/fs_abilities.py
index 39d4de9..2fb314a 100644
--- a/rdiff-backup/rdiff_backup/fs_abilities.py
+++ b/rdiff-backup/rdiff_backup/fs_abilities.py
@@ -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
diff --git a/rdiff-backup/rdiff_backup/metadata.py b/rdiff-backup/rdiff_backup/metadata.py
index b7ff395..95ccdfc 100644
--- a/rdiff-backup/rdiff_backup/metadata.py
+++ b/rdiff-backup/rdiff_backup/metadata.py
@@ -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)
diff --git a/rdiff-backup/rdiff_backup/rpath.py b/rdiff-backup/rdiff_backup/rpath.py
index a549973..ef38402 100644
--- a/rdiff-backup/rdiff_backup/rpath.py
+++ b/rdiff-backup/rdiff_backup/rpath.py
@@ -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.
diff --git a/rdiff-backup/testing/fs_abilitiestest.py b/rdiff-backup/testing/fs_abilitiestest.py
index 93f17c4..61e80c1 100644
--- a/rdiff-backup/testing/fs_abilitiestest.py
+++ b/rdiff-backup/testing/fs_abilitiestest.py
@@ -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')