# HG changeset patch # User Goffi # Date 1556434513 -7200 # Node ID c0f6fd75af5f9b5c624bd6404ca596b180f3706f # Parent 69e4716d626896387e86f561737ba765f08df251 core (memory, memory/sqlite): implemented fileDelete diff -r 69e4716d6268 -r c0f6fd75af5f sat/memory/memory.py --- a/sat/memory/memory.py Sun Apr 28 08:55:13 2019 +0200 +++ b/sat/memory/memory.py Sun Apr 28 08:55:13 2019 +0200 @@ -1541,6 +1541,67 @@ """ return self.storage.fileUpdate(file_id, column, update_cb) + @defer.inlineCallbacks + def _deleteFile(self, client, peer_jid, recursive, files_path, file_data): + """Internal method to delete files/directories recursively + + @param peer_jid(jid.JID): entity requesting the deletion (must be owner of files + to delete) + @param recursive(boolean): True if recursive deletion is needed + @param files_path(unicode): path of the directory containing the actual files + @param file_data(dict): data of the file to delete + """ + if file_data[u'owner'] != peer_jid: + raise exceptions.PermissionError( + u"file {file_name} can't be deleted, {peer_jid} is not the owner" + .format(file_name=file_data[u'name'], peer_jid=peer_jid.full())) + if file_data[u'type'] == C.FILE_TYPE_DIRECTORY: + sub_files = yield self.getFiles(client, peer_jid, parent=file_data[u'id']) + if sub_files and not recursive: + raise exceptions.DataError(_(u"Can't delete directory, it is not empty")) + # we first delete the sub-files + for sub_file_data in sub_files: + yield self._deleteFile(client, peer_jid, recursive, sub_file_data) + # then the directory itself + yield self.storage.fileDelete(file_data[u'id']) + elif file_data[u'type'] == C.FILE_TYPE_FILE: + log.info(_(u"deleting file {name} with hash {file_hash}").format( + name=file_data[u'name'], file_hash=file_data[u'file_hash'])) + yield self.storage.fileDelete(file_data[u'id']) + references = yield self.getFiles( + client, peer_jid, file_hash=file_data[u'file_hash']) + if references: + log.debug(u"there are still references to the file, we keep it") + else: + file_path = os.path.join(files_path, file_data[u'file_hash']) + log.info(_(u"no reference left to {file_path}, deleting").format( + file_path=file_path)) + os.unlink(file_path) + else: + raise exceptions.InternalError(u'Unexpected file type: {file_type}' + .format(file_type=file_data[u'type'])) + + @defer.inlineCallbacks + def fileDelete(self, client, peer_jid, file_id, recursive=False): + """Delete a single file or a directory and all its sub-files + + @param file_id(unicode): id of the file to delete + @param peer_jid(jid.JID): entity requesting the deletion, + must be owner of all files to delete + @param recursive(boolean): must be True to delete a directory and all sub-files + """ + # FIXME: we only allow owner of file to delete files for now, but WRITE access + # should be checked too + files_data = yield self.getFiles(client, peer_jid, file_id) + if not files_data: + raise exceptions.NotFound(u"Can't find the file with id {file_id}".format( + file_id=file_id)) + file_data = files_data[0] + if file_data[u"type"] != C.FILE_TYPE_DIRECTORY and recursive: + raise ValueError(u"recursive can only be set for directories") + files_path = self.host.getLocalPath(None, C.FILES_DIR, profile=False) + yield self._deleteFile(client, peer_jid, recursive, files_path, file_data) + ## Misc ## def isEntityAvailable(self, client, entity_jid): diff -r 69e4716d6268 -r c0f6fd75af5f sat/memory/sqlite.py --- a/sat/memory/sqlite.py Sun Apr 28 08:55:13 2019 +0200 +++ b/sat/memory/sqlite.py Sun Apr 28 08:55:13 2019 +0200 @@ -931,7 +931,7 @@ log.error(_(u"Can't update file table")) def fileUpdate(self, file_id, column, update_cb): - """update a column value using a method to avoid race conditions + """Update a column value using a method to avoid race conditions the older value will be retrieved from database, then update_cb will be applied to update it, and file will be updated checking that older value has not been changed meanwhile @@ -948,6 +948,15 @@ raise exceptions.InternalError('bad column name') return self.dbpool.runInteraction(self._fileUpdate, file_id, column, update_cb) + def fileDelete(self, file_id): + """Delete file metadata from the database + + @param file_id(unicode): id of the file to delete + NOTE: file itself must still be removed, this method only handle metadata in + database + """ + return self.dbpool.runQuery("DELETE FROM files WHERE id = ?", (file_id,)) + ##Helper methods## def __getFirstResult(self, result):