changeset 4269:64a85ce8be70 default tip @

memory (file): avoid race condition when creating parent path directories.
author Goffi <goffi@goffi.org>
date Tue, 18 Jun 2024 12:06:45 +0200
parents 51d004e50786
children
files libervia/backend/memory/memory.py
diffstat 1 files changed, 30 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/libervia/backend/memory/memory.py	Thu Jun 13 13:22:41 2024 +0200
+++ b/libervia/backend/memory/memory.py	Tue Jun 18 12:06:45 2024 +0200
@@ -251,6 +251,7 @@
         self._cache_path = Path(self.config_get("", "local_dir"), C.CACHE_DIR)
         self.admins = self.config_get("", "admins_list", [])
         self.admin_jids = set()
+        self._file_path_lock = defer.DeferredLock()
 
 
     async def initialise(self):
@@ -1706,28 +1707,36 @@
         owner = self.get_files_owner(client, owner, peer_jid, file_id, parent)
 
         if path is not None:
-            path = str(path)
-            # _get_parent_dir will check permissions if peer_jid is set, so we use owner
-            parent, remaining_path_elts = await self._get_parent_dir(
-                client, path, parent, namespace, owner, owner, perms_to_check
-            )
-            # if remaining directories don't exist, we have to create them
-            for new_dir in remaining_path_elts:
-                new_dir_id = shortuuid.uuid()
-                await self.storage.set_file(
-                    client,
-                    name=new_dir,
-                    file_id=new_dir_id,
-                    version="",
-                    parent=parent,
-                    type_=C.FILE_TYPE_DIRECTORY,
-                    namespace=namespace,
-                    created=time.time(),
-                    owner=owner,
-                    access=access,
-                    extra={},
+            # We use a lock to avoid race condition leading to duplicate directories with
+            # same name and parent.
+            await self._file_path_lock.acquire()
+            try:
+
+                path = str(path)
+                # _get_parent_dir will check permissions if peer_jid is set, so we use owner
+                parent, remaining_path_elts = await self._get_parent_dir(
+                    client, path, parent, namespace, owner, owner, perms_to_check
                 )
-                parent = new_dir_id
+                # if remaining directories don't exist, we have to create them
+                for new_dir in remaining_path_elts:
+                    new_dir_id = shortuuid.uuid()
+                    await self.storage.set_file(
+                        client,
+                        name=new_dir,
+                        file_id=new_dir_id,
+                        version="",
+                        parent=parent,
+                        type_=C.FILE_TYPE_DIRECTORY,
+                        namespace=namespace,
+                        created=time.time(),
+                        owner=owner,
+                        access=access,
+                        extra={},
+                    )
+                    parent = new_dir_id
+            finally:
+                self._file_path_lock.release()
+
         elif parent is None:
             parent = ""