Example #1
0
    async def file_delete(self, path: FsPath) -> EntryID:
        # Check write rights
        self.check_write_rights(path)

        # Fetch and lock
        async with self._lock_parent_manifest_from_path(path) as (parent,
                                                                  child):

            # Entry doesn't exist
            if child is None:
                raise FSFileNotFoundError(filename=path)

            # Not a file
            if not isinstance(child, LocalFileManifest):
                raise FSIsADirectoryError(filename=path)

            # Create new manifest
            new_parent = parent.evolve_children_and_mark_updated(
                {path.name: None},
                prevent_sync_pattern=self.local_storage.
                get_prevent_sync_pattern(),
            )

            # Atomic change
            await self.local_storage.set_manifest(parent.id, new_parent)

        # Send event
        self._send_event(CoreEvent.FS_ENTRY_UPDATED, id=parent.id)

        # Return the entry id of the deleted file
        return child.id
Example #2
0
    async def file_resize(self, path: FsPath, length: int) -> EntryID:
        # Check write rights
        self.check_write_rights(path)

        # Lock manifest
        async with self._lock_manifest_from_path(path) as manifest:

            # Not a file
            if not isinstance(manifest, LocalFileManifest):
                raise FSIsADirectoryError(filename=path)

            # Perform resize
            await self._manifest_resize(manifest, length)

            # Return entry id
            return manifest.id
    async def file_open(self, path: FsPath, write_mode: bool) -> Tuple[EntryID, FileDescriptor]:
        # Check read and write rights
        if write_mode:
            self.check_write_rights(path)
        else:
            self.check_read_rights(path)

        # Lock path in read mode
        async with self._lock_manifest_from_path(path) as manifest:

            # Not a file
            if not isinstance(manifest, LocalFileManifest):
                raise FSIsADirectoryError(filename=path)

            # Return the entry id of the open file and the file descriptor
            return manifest.id, self.local_storage.create_file_descriptor(manifest)
Example #4
0
    async def file_reshape(self, entry_id: EntryID) -> None:

        # Loop over attemps
        while True:

            # Fetch and lock
            async with self.local_storage.lock_manifest(entry_id) as manifest:

                # Not a file manifest
                if not isinstance(manifest, LocalFileManifest):
                    raise FSIsADirectoryError(entry_id)

                # Normalize
                missing = await self._manifest_reshape(manifest)

            # Done
            if not missing:
                return

            # Load missing blocks
            await self.remote_loader.load_blocks(missing)
Example #5
0
    async def entry_rename(self,
                           source: FsPath,
                           destination: FsPath,
                           overwrite: bool = True) -> Optional[EntryID]:
        # Check write rights
        self.check_write_rights(source)

        # Source is root
        if source.is_root():
            raise FSPermissionError(filename=source)

        # Destination is root
        if destination.is_root():
            raise FSPermissionError(filename=destination)

        # Cross-directory renaming is not supported
        if source.parent != destination.parent:
            raise FSCrossDeviceError(filename=source, filename2=destination)

        # Pre-fetch the source if necessary
        if overwrite:
            await self._get_manifest_from_path(source)

        # Fetch and lock
        async with self._lock_parent_manifest_from_path(destination) as (
                parent, child):

            # Source does not exist
            if source.name not in parent.children:
                raise FSFileNotFoundError(filename=source)
            source_entry_id = parent.children[source.name]

            # Source and destination are the same
            if source.name == destination.name:
                return None

            # Destination already exists
            if not overwrite and child is not None:
                raise FSFileExistsError(filename=destination)

            # Overwrite logic
            if overwrite and child is not None:
                source_manifest = await self._get_manifest(source_entry_id)

                # Overwrite a file
                if isinstance(source_manifest, LocalFileManifest):

                    # Destination is a folder
                    if isinstance(child, LocalFolderManifest):
                        raise FSIsADirectoryError(filename=destination)

                # Overwrite a folder
                if isinstance(source_manifest, LocalFolderManifest):

                    # Destination is not a folder
                    if not isinstance(child, LocalFolderManifest):
                        raise FSNotADirectoryError(filename=destination)

                    # Destination is not empty
                    if child.children:
                        raise FSDirectoryNotEmptyError(filename=destination)

            # Create new manifest
            new_parent = parent.evolve_children_and_mark_updated(
                {
                    destination.name: source_entry_id,
                    source.name: None
                },
                prevent_sync_pattern=self.local_storage.
                get_prevent_sync_pattern(),
            )

            # Atomic change
            await self.local_storage.set_manifest(parent.id, new_parent)

        # Send event
        self._send_event(CoreEvent.FS_ENTRY_UPDATED, id=parent.id)

        # Return the entry id of the renamed entry
        return parent.children[source.name]
Example #6
0
    async def file_conflict(
        self,
        entry_id: EntryID,
        local_manifest: Union[LocalFolderManifest, LocalFileManifest],
        remote_manifest: BaseRemoteManifest,
    ) -> None:
        # This is the only transaction that affects more than one manifests
        # That's because the local version of the file has to be registered in the
        # parent as a new child while the remote version has to be set as the actual
        # version. In practice, this should not be an issue.

        # Lock parent then child
        parent_id = local_manifest.parent
        async with self.local_storage.lock_manifest(
                parent_id) as parent_manifest:

            # Not a folderish manifest
            if not isinstance(parent_manifest,
                              (LocalFolderManifest, LocalWorkspaceManifest)):
                raise FSNotADirectoryError(parent_id)

            async with self.local_storage.lock_manifest(
                    entry_id) as current_manifest:

                # Not a file manifest
                if not isinstance(current_manifest, LocalFileManifest):
                    raise FSIsADirectoryError(entry_id)

                # Make sure the file still exists
                filename = get_filename(parent_manifest, entry_id)
                if filename is None:
                    return

                # Copy blocks
                new_blocks = []
                for chunks in current_manifest.blocks:
                    new_chunks = []
                    for chunk in chunks:
                        data = await self.local_storage.get_chunk(chunk.id)
                        new_chunk = Chunk.new(chunk.start, chunk.stop)
                        await self.local_storage.set_chunk(new_chunk.id, data)
                        if len(chunks) == 1:
                            new_chunk = new_chunk.evolve_as_block(data)
                        new_chunks.append(chunk)
                    new_blocks.append(tuple(new_chunks))

                # Prepare
                prevent_sync_pattern = self.local_storage.get_prevent_sync_pattern(
                )
                new_name = get_conflict_filename(
                    filename, list(parent_manifest.children),
                    remote_manifest.author)
                new_manifest = LocalFileManifest.new_placeholder(
                    self.local_author,
                    parent=parent_id).evolve(size=current_manifest.size,
                                             blocks=tuple(new_blocks))
                new_parent_manifest = parent_manifest.evolve_children_and_mark_updated(
                    {new_name: new_manifest.id},
                    prevent_sync_pattern=prevent_sync_pattern)
                other_manifest = BaseLocalManifest.from_remote(
                    remote_manifest, prevent_sync_pattern=prevent_sync_pattern)

                # Set manifests
                await self.local_storage.set_manifest(new_manifest.id,
                                                      new_manifest,
                                                      check_lock_status=False)
                await self.local_storage.set_manifest(parent_id,
                                                      new_parent_manifest)
                await self.local_storage.set_manifest(entry_id, other_manifest)

                self._send_event(CoreEvent.FS_ENTRY_UPDATED,
                                 id=new_manifest.id)
                self._send_event(CoreEvent.FS_ENTRY_UPDATED, id=parent_id)
                self._send_event(
                    CoreEvent.FS_ENTRY_FILE_CONFLICT_RESOLVED,
                    id=entry_id,
                    backup_id=new_manifest.id,
                )