| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 | from collections import defaultdictimport errnoimport llfuseimport osimport statimport timefrom attic.helpers import daemonize# Does this version of llfuse support ns precision?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):        super(AtticOperations, self).__init__()        self._inode_count = 0        self.key = key        self.repository = repository        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        for item in archive.iter_items():            segments = 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                # 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'])                        self.items[inode][b'nlink'] = self.items[inode].get(b'nlink', 1) + 1                    else:                        inode = self.allocate_inode()                        self.items[inode] = item                    self.parent[inode] = parent                    if segment:                        self.contents[parent][segment] = inode                elif segment in self.contents[parent]:                    parent = self.contents[parent][segment]                else:                    inode = self.allocate_inode()                    self.items[inode] = default_dir                    self.parent[inode] = parent                    if segment:                        self.contents[parent][segment] = inode                    parent = inode    def allocate_inode(self):        self._inode_count += 1        return self._inode_count    def statfs(self):        stat_ = llfuse.StatvfsData()        stat_.f_bsize = 512        stat_.f_frsize = 512        stat_.f_blocks = 0        stat_.f_bfree = 0        stat_.f_bavail = 0        stat_.f_files = 0        stat_.f_ffree = 0        stat_.f_favail = 0        return stat_    def _find_inode(self, path):        segments = os.fsencode(os.path.normpath(path)).split(b'/')        inode = 1        for segment in segments:            inode = self.contents[inode][segment]        return inode    def getattr(self, inode):        item = self.items[inode]        size = 0        try:            size = sum(size for _, size, _ in item[b'chunks'])        except KeyError:            pass        entry = llfuse.EntryAttributes()        entry.st_ino = inode        entry.generation = 0        entry.entry_timeout = 300        entry.attr_timeout = 300        entry.st_mode = item[b'mode']        entry.st_nlink = item.get(b'nlink', 1)        entry.st_uid = item[b'uid']        entry.st_gid = item[b'gid']        entry.st_rdev = item.get(b'rdev', 0)        entry.st_size = size        entry.st_blksize = 512        entry.st_blocks = 1        if have_fuse_mtime_ns:            entry.st_atime_ns = item[b'mtime']            entry.st_mtime_ns = item[b'mtime']            entry.st_ctime_ns = item[b'mtime']        else:            entry.st_atime = item[b'mtime'] / 1e9            entry.st_mtime = item[b'mtime'] / 1e9            entry.st_ctime = item[b'mtime'] / 1e9        return entry    def listxattr(self, inode):        item = self.items[inode]        return item.get(b'xattrs', {}).keys()    def getxattr(self, inode, name):        item = self.items[inode]        try:            return item.get(b'xattrs', {})[name]        except KeyError:            raise llfuse.FUSEError(errno.ENODATA)    def lookup(self, parent_inode, name):        if name == b'.':            inode = parent_inode        elif name == b'..':            inode = self.parent[parent_inode]        else:            inode = self.contents[parent_inode].get(name)            if not inode:                raise llfuse.FUSEError(errno.ENOENT)        return self.getattr(inode)    def open(self, inode, flags):        return inode    def opendir(self, inode):        return inode    def read(self, fh, offset, size):        parts = []        item = self.items[fh]        for id, s, csize in item[b'chunks']:            if s < offset:                offset -= s                continue            n = min(size, s - offset)            chunk = self.key.decrypt(id, self.repository.get(id))            parts.append(chunk[offset:offset+n])            offset = 0            size -= n            if not size:                break        return b''.join(parts)    def readdir(self, fh, off):        entries = [(b'.', fh), (b'..', self.parent[fh])]        entries.extend(self.contents[fh].items())        for i, (name, inode) in enumerate(entries[off:], off):            yield name, self.getattr(inode), i + 1    def readlink(self, inode):        return os.fsencode(self.items[inode][b'source'])    def mount(self, mountpoint, extra_options, foreground=False):        options = ['fsname=atticfs', 'ro']        if extra_options:            options.extend(extra_options.split(','))        llfuse.init(self, mountpoint, options)        if not foreground:            daemonize()        try:            llfuse.main(single=True)        except:            llfuse.close()            raise        llfuse.close()
 |