diff options
Diffstat (limited to 'rdiff-backup')
-rw-r--r-- | rdiff-backup/CHANGELOG | 10 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/Globals.py | 5 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/Main.py | 4 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/fs_abilities.py | 23 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/metadata.py | 34 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/rpath.py | 55 | ||||
-rw-r--r-- | rdiff-backup/testing/fs_abilitiestest.py | 8 |
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') |