From 7976fa4fe271017ae13f897968306cdd7d1641ea Mon Sep 17 00:00:00 2001 From: owsla Date: Sat, 3 Jan 2009 20:26:52 +0000 Subject: Revert previous patch & hold it for the new development branch git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@992 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109 --- rdiff-backup/CHANGELOG | 1 - rdiff-backup/rdiff_backup/FilenameMapping.py | 7 +- rdiff-backup/rdiff_backup/Main.py | 3 +- rdiff-backup/rdiff_backup/fs_abilities.py | 270 ++++++++++++--------------- 4 files changed, 126 insertions(+), 155 deletions(-) diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG index a52cb27..d49046e 100644 --- a/rdiff-backup/CHANGELOG +++ b/rdiff-backup/CHANGELOG @@ -1,7 +1,6 @@ New in v1.2.5 (????/??/??) --------------------------- -Fix escape DOS devices and escape trailing spaces/periods. (Andrew Ferguson) New in v1.2.4 (2009/01/01) diff --git a/rdiff-backup/rdiff_backup/FilenameMapping.py b/rdiff-backup/rdiff_backup/FilenameMapping.py index 046a4be..8bb3951 100644 --- a/rdiff-backup/rdiff_backup/FilenameMapping.py +++ b/rdiff-backup/rdiff_backup/FilenameMapping.py @@ -85,17 +85,18 @@ def quote(path): """ QuotedPath = chars_to_quote_regexp.sub(quote_single, path) - if not Globals.escape_dos_devices and not Globals.escape_trailing_spaces: + if not Globals.must_escape_dos_devices \ + and not Globals.must_escape_trailing_spaces: return QuotedPath # Escape a trailing space or period (invalid in names on FAT32 under DOS, # Windows and modern Linux) - if Globals.escape_trailing_spaces: + if Globals.must_escape_trailing_spaces: if len(QuotedPath) and (QuotedPath[-1] == ' ' or QuotedPath[-1] == '.'): QuotedPath = QuotedPath[:-1] + \ "%s%03d" % (quoting_char, ord(QuotedPath[-1])) - if not Globals.escape_dos_devices: + if not Globals.must_escape_dos_devices: return QuotedPath # Escape first char of any special DOS device files even if filename has an diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py index 47246f6..d4aa48a 100644 --- a/rdiff-backup/rdiff_backup/Main.py +++ b/rdiff-backup/rdiff_backup/Main.py @@ -860,8 +860,7 @@ def checkdest_need_check(dest_rp): """Return None if no dest dir found, 1 if dest dir needs check, 0 o/w""" if not dest_rp.isdir() or not Globals.rbdir.isdir(): return None for filename in Globals.rbdir.listdir(): - if filename not in ['chars_to_quote', 'special_escapes', - 'backup.log']: break + if filename not in ['chars_to_quote', 'backup.log']: break else: # This may happen the first backup just after we test for quoting return None curmirroot = Globals.rbdir.append("current_mirror") diff --git a/rdiff-backup/rdiff_backup/fs_abilities.py b/rdiff-backup/rdiff_backup/fs_abilities.py index 2cbbec6..d303d7a 100644 --- a/rdiff-backup/rdiff_backup/fs_abilities.py +++ b/rdiff-backup/rdiff_backup/fs_abilities.py @@ -50,8 +50,8 @@ class FSAbilities: high_perms = None # True if suid etc perms are (read/write) supported escape_dos_devices = None # True if dos device files can't be created (e.g., # aux, con, com1, etc) - escape_trailing_spaces = None # True if trailing spaces or periods at the - # end of filenames aren't preserved + escape_trailing_spaces = None # True if files with trailing spaces or + # periods can't be created symlink_perms = None # True if symlink perms are affected by umask def __init__(self, name = None): @@ -131,7 +131,7 @@ class FSAbilities: self.set_carbonfile() self.set_case_sensitive_readonly(rp) self.set_escape_dos_devices(rp) - self.set_escape_trailing_spaces_readonly(rp) + self.set_escape_trailing_spaces(rp) return self def init_readwrite(self, rbdir): @@ -166,7 +166,7 @@ class FSAbilities: self.set_high_perms_readwrite(subdir) self.set_symlink_perms(subdir) self.set_escape_dos_devices(subdir) - self.set_escape_trailing_spaces_readwrite(subdir) + self.set_escape_trailing_spaces(subdir) subdir.delete() return self @@ -554,88 +554,44 @@ class FSAbilities: sym_source.delete() def set_escape_dos_devices(self, subdir): - """Test if DOS device files can be used as filenames. - - This test must detect if the underlying OS is Windows, whehter we are - running under Cygwin or natively. Cygwin allows these special files to - be stat'd from any directory. Native Windows returns OSError (like - non-Cygwin POSIX), but we can check for that using os.name. - - Note that 'con' and 'aux' have some unusual behaviors as shown below. - - os.lstat() | con aux prn - -------------+------------------------------------- - Unix | OSError,2 OSError,2 OSError,2 - Cygwin/NTFS | -success- -success- -success- - Cygwin/FAT32 | -success- -HANGS- - Native Win | WinError,2 WinError,87 WinError,87 - """ - if os.name == "nt": - self.escape_dos_devices = 1 - return - + """If special file con can be stat'd, escape special files""" try: device_rp = subdir.append("con") if device_rp.lstat(): + log.Log("escape_dos_devices required by filesystem at %s" \ + % (subdir.path), 4) self.escape_dos_devices = 1 else: - self.escape_dos_devices = 0 + log.Log("escape_dos_devices not required by filesystem at %s" \ + % (subdir.path), 4) + self.escape_dos_devices = 0 except(OSError): + log.Log("escape_dos_devices required by filesystem at %s" \ + % (subdir.path), 4) self.escape_dos_devices = 1 - def set_escape_trailing_spaces_readwrite(self, testdir): - """Windows and Linux/FAT32 will not preserve trailing spaces or periods. - - Linux/FAT32 behaves inconsistently: It will give an OSError,22 if - os.mkdir() is called on a directory name with a space at the end, but - will give an IOError("invalid mode") if you attempt to create a filename - with a space at the end. However, if a period is placed at the end of - the name, Linux/FAT32 is consistent with Cygwin and Native Windows. - """ - - period_rp = testdir.append("foo.") - assert not period_rp.lstat() + def set_escape_trailing_spaces(self, subdir): + # Disable this for 1.2.4 + self.escape_trailing_spaces = 0 + return - tmp_rp = testdir.append("foo") - tmp_rp.touch() - assert tmp_rp.lstat() - - period_rp.setdata() - if period_rp.lstat(): + """If file with trailing space can't be created, escape such files""" + try: + space_rp = subdir.append("test ") + space_rp.touch() + if space_rp.lstat(): + log.Log("escape_trailing_spaces not required by filesystem " \ + "at %s" % (subdir.path), 4) + self.escape_trailing_spaces = 0 + space_rp.delete() + else: + log.Log("escape_trailing_spaces required by filesystem at %s" \ + % (subdir.path), 4) + self.escape_trailing_spaces = 1 + except (OSError, IOError): + log.Log("escape_trailing_spaces required by filesystem at %s" \ + % (subdir.path), 4) self.escape_trailing_spaces = 1 - else: - self.escape_trailing_spaces = 0 - - tmp_rp.delete() - - def set_escape_trailing_spaces_readonly(self, rp): - """Determine if directory at rp permits filenames with trailing - spaces or periods without writing.""" - - def test_period(dir_rp, dirlist): - """Return 1 if trailing spaces and periods should be escaped""" - filename = dirlist[0] - try: - test_rp = dir_rp.append(filename) - except OSError: - return 0 - assert test_rp.lstat(), test_rp - period = filename + '.' - if period in dirlist: return 0 - - period_rp = dir_rp.append(period) - if period_rp.lstat(): return 1 - return 0 - - dirlist = robust.listrp(rp) - if len(dirlist): - self.escape_trailing_spaces = test_period(rp, dirlist) - else: - log.Log("Warning: could not determine if source directory at\n " - + rp.path + "\npermits trailing spaces or periods in " - "filenames because we can't find any files.\n" - "It will be treated as permitting such files.", 2) - self.escape_trailing_spaces = 0 def get_readonly_fsa(desc_string, rp): @@ -709,6 +665,14 @@ class SetGlobals: SetConnections.UpdateGlobal('symlink_perms', self.dest_fsa.symlink_perms) + def set_escape_dos_devices(self): + SetConnections.UpdateGlobal('escape_dos_devices', \ + self.dest_fsa.escape_dos_devices) + + def set_escape_trailing_spaces(self): + SetConnections.UpdateGlobal('escape_trailing_spaces', \ + self.dest_fsa.escape_trailing_spaces) + class BackupSetGlobals(SetGlobals): """Functions for setting fsa related globals for backup session""" def update_triple(self, src_support, dest_support, attr_triple): @@ -723,50 +687,43 @@ class BackupSetGlobals(SetGlobals): SetConnections.UpdateGlobal(write_attr, 1) self.out_conn.Globals.set_local(conn_attr, 1) - def set_special_escapes(self, rbdir): - """Escaping DOS devices and trailing periods/spaces works like - regular filename escaping. If only the destination requires it, - then we do it. Otherwise, it is not necessary, since the files - couldn't have been created in the first place. We also record - whether we have done it in order to handle the case where a - volume which was escaped is later restored by an OS that does - not require it.""" - - suggested_edd = (self.dest_fsa.escape_dos_devices and not \ - self.src_fsa.escape_dos_devices) - suggested_ets = (self.dest_fsa.escape_trailing_spaces and not \ - self.src_fsa.escape_trailing_spaces) - - se_rp = rbdir.append("special_escapes") - if not se_rp.lstat(): - actual_edd, actual_ets = suggested_edd, suggested_ets - se = "" - if actual_edd: se = se + "escape_dos_devices\n" - if actual_ets: se = se + "escape_trailing_spaces\n" - se_rp.write_string(se) - else: - se = se_rp.get_data().split("\n") - actual_edd = ("escape_dos_devices" in se) - actual_ets = ("escape_trailing_spaces" in se) - - if actual_edd != suggested_edd and not suggested_edd: - log.Log("Warning: System no longer needs DOS devices escaped, " - "but we will retain for backwards compatibility.", 2) - if actual_ets != suggested_ets and not suggested_ets: - log.Log("Warning: System no longer needs trailing spaces or " - "periods escaped, but we will retain for backwards " - "compatibility.", 2) - - SetConnections.UpdateGlobal('escape_dos_devices', actual_edd) - log.Log("Backup: escape_dos_devices = %d" % actual_edd, 4) + def set_must_escape_dos_devices(self, rbdir): + """If local edd or src edd, then must escape """ + try: + device_rp = rbdir.append("con") + if device_rp.lstat(): local_edd = 1 + else: local_edd = 0 + except (OSError): local_edd = 1 + SetConnections.UpdateGlobal('must_escape_dos_devices', \ + self.src_fsa.escape_dos_devices or local_edd) + log.Log("Backup: must_escape_dos_devices = %d" % \ + (self.src_fsa.escape_dos_devices or local_edd), 4) + + def set_must_escape_trailing_spaces(self, rbdir): + """If local ets or src ets, then must escape """ + # Disable this for 1.2.4 + SetConnections.UpdateGlobal('must_escape_trailing_spaces', 0) + return - SetConnections.UpdateGlobal('escape_trailing_spaces', actual_ets) - log.Log("Backup: escape_trailing_spaces = %d" % actual_ets, 4) + try: + space_rp = rbdir.append("test ") + space_rp.touch() + if space_rp.lstat(): + local_ets = 0 + space_rp.delete() + else: + local_ets = 1 + except (OSError, IOError): + local_ets = 1 + SetConnections.UpdateGlobal('must_escape_trailing_spaces', \ + self.src_fsa.escape_trailing_spaces or local_ets) + log.Log("Backup: must_escape_trailing_spaces = %d" % \ + (self.src_fsa.escape_trailing_spaces or local_ets), 4) def set_chars_to_quote(self, rbdir, force): """Set chars_to_quote setting for backup session - Unlike most other options, the chars_to_quote setting also + Unlike the other options, the chars_to_quote setting also depends on the current settings in the rdiff-backup-data directory, not just the current fs features. @@ -861,39 +818,45 @@ class RestoreSetGlobals(SetGlobals): self.out_conn.Globals.set_local(write_attr, 1) if src_support: self.in_conn.Globals.set_local(conn_attr, 1) - def set_special_escapes(self, rbdir): - """Set escape_dos_devices and escape_trailing_spaces from - rdiff-backup-data dir, just like chars_to_quote""" - se_rp = rbdir.append("special_escapes") - if se_rp.lstat(): - se = se_rp.get_data().split("\n") - actual_edd = ("escape_dos_devices" in se) - actual_ets = ("escape_trailing_spaces" in se) + def set_must_escape_dos_devices(self, rbdir): + """If local edd or src edd, then must escape """ + if getattr(self, "src_fsa", None) is not None: + src_edd = self.src_fsa.escape_dos_devices + else: src_edd = 0 + try: + device_rp = rbdir.append("con") + if device_rp.lstat(): local_edd = 1 + else: local_edd = 0 + except (OSError): local_edd = 1 + SetConnections.UpdateGlobal('must_escape_dos_devices', \ + src_edd or local_edd) + log.Log("Restore: must_escape_dos_devices = %d" % \ + (src_edd or local_edd), 4) + + def set_must_escape_trailing_spaces(self, rbdir): + """If local ets or src ets, then must escape """ + # Disable this for 1.2.4 + SetConnections.UpdateGlobal('must_escape_trailing_spaces', 0) + return + + if getattr(self, "src_fsa", None) is not None: + src_ets = self.src_fsa.escape_trailing_spaces else: - log.Log("Warning: special_escapes file not found,\n" - "will assume need to escape DOS devices and trailing " - "spaces based on file systems.", 2) - if getattr(self, "src_fsa", None) is not None: - actual_edd = (self.src_fsa.escape_dos_devices and not - self.dest_fsa.escape_dos_devices) - try: - actual_ets = (self.src_fsa.escape_trailing_spaces and not - self.dest_fsa.escape_trailing_spaces) - except AttributeError: # ETS fixed in version 1.2.5 - actual_ets = 0 + src_ets = 0 + try: + space_rp = rbdir.append("test ") + space_rp.touch() + if space_rp.lstat(): + local_ets = 0 + space_rp.delete() else: - # Single filesystem operation - actual_edd = self.dest_fsa.escape_dos_devices - try: - actual_ets = self.dest_fsa.escape_trailing_spaces - except AttributeError: - actual_ets = 0 - - SetConnections.UpdateGlobal('escape_dos_devices', actual_edd) - log.Log("Backup: escape_dos_devices = %d" % actual_edd, 4) - - SetConnections.UpdateGlobal('escape_trailing_spaces', actual_ets) - log.Log("Backup: escape_trailing_spaces = %d" % actual_ets, 4) + local_ets = 1 + except (OSError, IOError): + local_ets = 1 + SetConnections.UpdateGlobal('must_escape_trailing_spaces', \ + src_ets or local_ets) + log.Log("Restore: must_escape_trailing_spaces = %d" % \ + (src_ets or local_ets), 4) def set_chars_to_quote(self, rbdir): """Set chars_to_quote from rdiff-backup-data dir""" @@ -968,7 +931,10 @@ def backup_set_globals(rpin, force): bsg.set_high_perms() bsg.set_symlink_perms() update_quoting = bsg.set_chars_to_quote(Globals.rbdir, force) - bsg.set_special_escapes(Globals.rbdir) + bsg.set_escape_dos_devices() + bsg.set_escape_trailing_spaces() + bsg.set_must_escape_dos_devices(Globals.rbdir) + bsg.set_must_escape_trailing_spaces(Globals.rbdir) if update_quoting and force: FilenameMapping.update_quoting(Globals.rbdir) @@ -995,7 +961,10 @@ def restore_set_globals(rpout): rsg.set_high_perms() rsg.set_symlink_perms() rsg.set_chars_to_quote(Globals.rbdir) - rsg.set_special_escapes(Globals.rbdir) + rsg.set_escape_dos_devices() + rsg.set_escape_trailing_spaces() + rsg.set_must_escape_dos_devices(Globals.rbdir) + rsg.set_must_escape_trailing_spaces(Globals.rbdir) def single_set_globals(rp, read_only = None): """Set fsa related globals for operation on single filesystem""" @@ -1015,5 +984,8 @@ def single_set_globals(rp, read_only = None): ssg.set_high_perms() ssg.set_symlink_perms() ssg.set_chars_to_quote(Globals.rbdir) - ssg.set_special_escapes(Globals.rbdir) + ssg.set_escape_dos_devices() + ssg.set_escape_trailing_spaces() + ssg.set_must_escape_dos_devices(Globals.rbdir) + ssg.set_must_escape_trailing_spaces(Globals.rbdir) -- cgit v1.2.1