diff options
Diffstat (limited to 'fs/contrib/archivefs.py')
-rw-r--r-- | fs/contrib/archivefs.py | 40 |
1 files changed, 39 insertions, 1 deletions
diff --git a/fs/contrib/archivefs.py b/fs/contrib/archivefs.py index cb3b4af..428c680 100644 --- a/fs/contrib/archivefs.py +++ b/fs/contrib/archivefs.py @@ -70,7 +70,7 @@ class ArchiveFS(FS): self.root_path = getattr(f, 'name', None) self.contents = PathMap() self.archive = libarchive.SeekableArchive(f, format=format, mode=mode) - if mode == 'r': + if 'r' in mode: for item in self.archive: for part in recursepath(item.pathname)[1:]: part = relpath(part) @@ -198,6 +198,22 @@ class ArchiveMountFS(mountfs.MountFS): return False return isinstance(object, mountfs.MountFS.DirMount) + def unmount(self, path): + """Unmounts a path. + + :param path: Path to unmount + + """ + # This might raise a KeyError, but that is what MountFS will do, so + # shall we. + fs = self.mount_tree.pop(path) + # TODO: it may be necessary to remember what paths were auto-mounted, + # so we can close those here. It may not be safe to close a file system + # that the user provided. However, it is definitely NOT safe to leave + # one open. + if callable(getattr(fs, 'close', None)): + fs.close() + def _delegate(self, path, auto_mount=True): """A _delegate() override that will automatically mount archives that are encountered in the path. For example, the path /foo/bar.zip/baz.txt contains @@ -356,6 +372,27 @@ class ArchiveMountFS(mountfs.MountFS): self.copy(src, dst, overwrite=overwrite, chunk_size=chunk_size) self.remove(src) + def rename(self, src, dst): + """An rename() implementation that ensures the rename does not span + file systems. It also ensures that an archive can be renamed (without + trying to mount either the src or destination paths).""" + src_is_archive = libarchive.is_archive_name(src) + # If src path is a mounted archive, unmount it. + if src_is_archive and self.ismount(src): + self.unmount(src) + # Now delegate the path, if the path is an archive, don't remount it. + srcfs, _ignored, src = self._delegate(src, auto_mount=(not src_is_archive)) + # Follow the same steps for dst. + dst_is_archive = libarchive.is_archive_name(dst) + if dst_is_archive and self.ismount(dst): + self.unmount(dst) + dstfs, _ignored, dst = self._delegate(dst, auto_mount=(not dst_is_archive)) + # srcfs, src and dstfs, dst are now the file system and path for our src and dst. + if srcfs is dstfs and srcfs is not self: + # Both src and dst are on the same fs, let it do the copy. + return srcfs.rename(src, dst) + raise OperationFailedError("rename resource", path=src) + def walk(self, path="/", wildcard=None, @@ -466,6 +503,7 @@ class ArchiveMountFS(mountfs.MountFS): def main(): ArchiveFS() + if __name__ == '__main__': main() |