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
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)
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)
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]
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, )