Browse Source

Added support for backup and restore of bsdflags (OS X and FreeBSD)

This improves our Backup-Bouncer score (#56)
Jonas Borgström 11 years ago
parent
commit
8bf3bb1ca3
3 changed files with 17 additions and 0 deletions
  1. 9 0
      attic/archive.py
  2. 4 0
      attic/testsuite/__init__.py
  3. 4 0
      attic/testsuite/archiver.py

+ 9 - 0
attic/archive.py

@@ -27,6 +27,7 @@ CHUNK_MASK = 0xffff
 utime_supports_fd = os.utime in getattr(os, 'supports_fd', {})
 utime_supports_fd = os.utime in getattr(os, 'supports_fd', {})
 has_mtime_ns = sys.version >= '3.3'
 has_mtime_ns = sys.version >= '3.3'
 has_lchmod = hasattr(os, 'lchmod')
 has_lchmod = hasattr(os, 'lchmod')
+has_lchflags = hasattr(os, 'lchflags')
 
 
 
 
 class DownloadPipeline:
 class DownloadPipeline:
@@ -315,6 +316,12 @@ class Archive:
             os.utime(path, None, ns=(item[b'mtime'], item[b'mtime']), follow_symlinks=False)
             os.utime(path, None, ns=(item[b'mtime'], item[b'mtime']), follow_symlinks=False)
         elif not symlink:
         elif not symlink:
             os.utime(path, (item[b'mtime'] / 10**9, item[b'mtime'] / 10**9))
             os.utime(path, (item[b'mtime'] / 10**9, item[b'mtime'] / 10**9))
+        # Only available on OS X and FreeBSD
+        if has_lchflags and b'bsdflags' in item:
+            try:
+                os.lchflags(path, item[b'bsdflags'])
+            except OSError:
+                pass
 
 
     def delete(self, stats):
     def delete(self, stats):
         unpacker = msgpack.Unpacker(use_list=False)
         unpacker = msgpack.Unpacker(use_list=False)
@@ -341,6 +348,8 @@ class Archive:
         xattrs = xattr.get_all(path, follow_symlinks=False)
         xattrs = xattr.get_all(path, follow_symlinks=False)
         if xattrs:
         if xattrs:
             item[b'xattrs'] = StableDict(xattrs)
             item[b'xattrs'] = StableDict(xattrs)
+        if has_lchflags and st.st_flags:
+            item[b'bsdflags'] = st.st_flags
         return item
         return item
 
 
     def process_item(self, path, st):
     def process_item(self, path, st):

+ 4 - 0
attic/testsuite/__init__.py

@@ -15,6 +15,8 @@ try:
 except ImportError:
 except ImportError:
     have_fuse_mtime_ns = False
     have_fuse_mtime_ns = False
 
 
+has_lchflags = hasattr(os, 'lchflags')
+
 
 
 # The mtime get/set precison varies on different OS and Python versions
 # The mtime get/set precison varies on different OS and Python versions
 if 'HAVE_FUTIMENS' in getattr(posix, '_have_functions', []):
 if 'HAVE_FUTIMENS' in getattr(posix, '_have_functions', []):
@@ -56,6 +58,8 @@ class AtticTestCase(unittest.TestCase):
             # Assume path2 is on FUSE if st_dev is different
             # Assume path2 is on FUSE if st_dev is different
             fuse = s1.st_dev != s2.st_dev
             fuse = s1.st_dev != s2.st_dev
             attrs = ['st_mode', 'st_uid', 'st_gid', 'st_rdev']
             attrs = ['st_mode', 'st_uid', 'st_gid', 'st_rdev']
+            if has_lchflags:
+                attrs.append('st_flags')
             if not fuse or not os.path.isdir(path1):
             if not fuse or not os.path.isdir(path1):
                 # dir nlink is always 1 on our fuse fileystem
                 # dir nlink is always 1 on our fuse fileystem
                 attrs.append('st_nlink')
                 attrs.append('st_nlink')

+ 4 - 0
attic/testsuite/archiver.py

@@ -24,6 +24,8 @@ try:
 except ImportError:
 except ImportError:
     has_llfuse = False
     has_llfuse = False
 
 
+has_lchflags = hasattr(os, 'lchflags')
+
 src_dir = os.path.join(os.getcwd(), os.path.dirname(__file__), '..')
 src_dir = os.path.join(os.getcwd(), os.path.dirname(__file__), '..')
 
 
 
 
@@ -138,6 +140,8 @@ class ArchiverTestCase(ArchiverTestCaseBase):
             xattr.setxattr(os.path.join(self.input_path, 'link1'), 'user.foo_symlink', b'bar_symlink', follow_symlinks=False)
             xattr.setxattr(os.path.join(self.input_path, 'link1'), 'user.foo_symlink', b'bar_symlink', follow_symlinks=False)
         # FIFO node
         # FIFO node
         os.mkfifo(os.path.join(self.input_path, 'fifo1'))
         os.mkfifo(os.path.join(self.input_path, 'fifo1'))
+        if has_lchflags:
+            os.lchflags(os.path.join(self.input_path, 'file1'), stat.UF_NODUMP)
 
 
     def test_basic_functionality(self):
     def test_basic_functionality(self):
         self.create_test_files()
         self.create_test_files()