Преглед изворни кода

Initial (naive) full repository fuse mount support

Jonas Borgström пре 11 година
родитељ
комит
8e078b5f91
3 измењених фајлова са 52 додато и 19 уклоњено
  1. 8 7
      attic/archiver.py
  2. 21 11
      attic/fuse.py
  3. 23 1
      attic/testsuite/archiver.py

+ 8 - 7
attic/archiver.py

@@ -245,12 +245,13 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
             self.print_error('%s: Mountpoint must be a writable directory' % args.mountpoint)
             return self.exit_code
 
-        repository = self.open_repository(args.archive)
+        repository = self.open_repository(args.src)
         manifest, key = Manifest.load(repository)
-        self.print_verbose("Loading archive metadata...", newline=False)
-        archive = Archive(repository, key, manifest, args.archive.archive)
-        self.print_verbose('done')
-        operations = AtticOperations(key, repository, archive)
+        if args.src.archive:
+            archive = Archive(repository, key, manifest, args.src.archive)
+        else:
+            archive = None
+        operations = AtticOperations(key, repository, manifest, archive)
         self.print_verbose("Mounting filesystem")
         try:
             operations.mount(args.mountpoint, args.options, args.foreground)
@@ -562,8 +563,8 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
         subparser = subparsers.add_parser('mount', parents=[common_parser],
                                           description=self.do_mount.__doc__)
         subparser.set_defaults(func=self.do_mount)
-        subparser.add_argument('archive', metavar='ARCHIVE', type=location_validator(archive=True),
-                               help='archive to mount')
+        subparser.add_argument('src', metavar='REPOSITORY_OR_ARCHIVE', type=location_validator(),
+                               help='repository/archive to mount')
         subparser.add_argument('mountpoint', metavar='MOUNTPOINT', type=str,
                                help='where to mount filesystem')
         subparser.add_argument('-f', '--foreground', dest='foreground',

+ 21 - 11
attic/fuse.py

@@ -4,6 +4,7 @@ import llfuse
 import os
 import stat
 import time
+from attic.archive import Archive
 
 from attic.helpers import daemonize
 # Does this version of llfuse support ns precision?
@@ -13,7 +14,7 @@ have_fuse_mtime_ns = hasattr(llfuse.EntryAttributes, 'st_mtime_ns')
 class AtticOperations(llfuse.Operations):
     """Export Attic archive as a fuse filesystem
     """
-    def __init__(self, key, repository, archive):
+    def __init__(self, key, repository, manifest, archive):
         super(AtticOperations, self).__init__()
         self._inode_count = 0
         self.key = key
@@ -21,22 +22,31 @@ class AtticOperations(llfuse.Operations):
         self.items = {}
         self.parent = {}
         self.contents = defaultdict(dict)
-        default_dir = {b'mode': 0o40755, b'mtime': int(time.time() * 1e9), b'uid': os.getuid(), b'gid': os.getgid()}
-        # Loop through all archive items and assign inode numbers and
-        # extract hierarchy information
+        self.default_dir = {b'mode': 0o40755, b'mtime': int(time.time() * 1e9), b'uid': os.getuid(), b'gid': os.getgid()}
+        if archive:
+            self.process_archive(archive)
+        else:
+            for archive_name in manifest.archives:
+                archive = Archive(repository, key, manifest, archive_name)
+                self.process_archive(archive, [os.fsencode(archive_name)])
+
+    def process_archive(self, archive, prefix=[]):
+        """Build fuse inode hierarcy from archive metadata
+        """
         for item in archive.iter_items():
-            segments = os.fsencode(os.path.normpath(item[b'path'])).split(b'/')
+            segments = prefix + os.fsencode(os.path.normpath(item[b'path'])).split(b'/')
             num_segments = len(segments)
             parent = 1
             for i, segment in enumerate(segments, 1):
                 # Insert a default root inode if needed
                 if self._inode_count == 0 and segment:
-                    self.items[self.allocate_inode()] = default_dir
-                    self.parent[1] = 1
+                    archive_inode = self.allocate_inode()
+                    self.items[archive_inode] = self.default_dir
+                    self.parent[archive_inode] = parent
                 # Leaf segment?
                 if i == num_segments:
                     if b'source' in item and stat.S_ISREG(item[b'mode']):
-                        inode = self._find_inode(item[b'source'])
+                        inode = self._find_inode(item[b'source'], prefix)
                         self.items[inode][b'nlink'] = self.items[inode].get(b'nlink', 1) + 1
                     else:
                         inode = self.allocate_inode()
@@ -48,7 +58,7 @@ class AtticOperations(llfuse.Operations):
                     parent = self.contents[parent][segment]
                 else:
                     inode = self.allocate_inode()
-                    self.items[inode] = default_dir
+                    self.items[inode] = self.default_dir
                     self.parent[inode] = parent
                     if segment:
                         self.contents[parent][segment] = inode
@@ -70,8 +80,8 @@ class AtticOperations(llfuse.Operations):
         stat_.f_favail = 0
         return stat_
 
-    def _find_inode(self, path):
-        segments = os.fsencode(os.path.normpath(path)).split(b'/')
+    def _find_inode(self, path, prefix=[]):
+        segments = prefix + os.fsencode(os.path.normpath(path)).split(b'/')
         inode = 1
         for segment in segments:
             inode = self.contents[inode][segment]

+ 23 - 1
attic/testsuite/archiver.py

@@ -271,7 +271,29 @@ class ArchiverTestCase(ArchiverTestCaseBase):
         self.assert_raises(SystemExit, lambda: self.attic('-h'))
 
     @unittest.skipUnless(has_llfuse, 'llfuse not installed')
-    def test_mount(self):
+    def test_fuse_mount_repository(self):
+        mountpoint = os.path.join(self.tmpdir, 'mountpoint')
+        os.mkdir(mountpoint)
+        self.attic('init', self.repository_location)
+        self.create_test_files()
+        self.attic('create', self.repository_location + '::archive', 'input')
+        self.attic('create', self.repository_location + '::archive2', 'input')
+        try:
+            self.attic('mount', self.repository_location, mountpoint, fork=True)
+            self.wait_for_mount(mountpoint)
+            self.assert_dirs_equal(self.input_path, os.path.join(mountpoint, 'archive', 'input'))
+            self.assert_dirs_equal(self.input_path, os.path.join(mountpoint, 'archive2', 'input'))
+        finally:
+            if sys.platform.startswith('linux'):
+                os.system('fusermount -u ' + mountpoint)
+            else:
+                os.system('umount ' + mountpoint)
+            os.rmdir(mountpoint)
+            # Give the daemon some time to exit
+            time.sleep(.2)
+
+    @unittest.skipUnless(has_llfuse, 'llfuse not installed')
+    def test_fuse_mount_archive(self):
         mountpoint = os.path.join(self.tmpdir, 'mountpoint')
         os.mkdir(mountpoint)
         self.attic('init', self.repository_location)