changeset 2928:c0f6fd75af5f

core (memory, memory/sqlite): implemented fileDelete
author Goffi <goffi@goffi.org>
date Sun, 28 Apr 2019 08:55:13 +0200
parents 69e4716d6268
children e0429ff7f6b6
files sat/memory/memory.py sat/memory/sqlite.py
diffstat 2 files changed, 71 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- 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):
--- 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):