Sfoglia il codice sorgente

Merge pull request #462 from Unrud/durabledirs

Durable creation of directories and make sure that the root colleciton exists.
Guillaume Ayoub 9 anni fa
parent
commit
4549d1b2db
1 ha cambiato i file con 21 aggiunte e 5 eliminazioni
  1. 21 5
      radicale/storage.py

+ 21 - 5
radicale/storage.py

@@ -261,6 +261,8 @@ class BaseCollection:
 
         The ``path`` is relative.
 
+        The root collection "/" must always exist.
+
         """
         raise NotImplementedError
 
@@ -425,6 +427,19 @@ class Collection(BaseCollection):
                 return file_name
         raise FileExistsError(errno.EEXIST, "No usable file name found")
 
+    @classmethod
+    def _makedirs_synced(cls, filesystem_path, exist_ok=False):
+        if os.path.isdir(filesystem_path) and exist_ok:
+            return
+        parent_filesystem_path = os.path.dirname(filesystem_path)
+        # Prevent infinite loop
+        if filesystem_path != parent_filesystem_path:
+            # Create parent dirs recursively
+            cls._makedirs_synced(parent_filesystem_path, exist_ok=True)
+        # Possible race!
+        os.makedirs(filesystem_path, exist_ok=exist_ok)
+        sync_directory(parent_filesystem_path)
+
     @classmethod
     def discover(cls, path, depth="0"):
         if path is None:
@@ -437,14 +452,16 @@ class Collection(BaseCollection):
         if not attributes[0]:
             attributes.pop()
 
-        # Try to guess if the path leads to a collection or an item
         folder = cls._get_collection_root_folder()
+        # Create the root collection
+        cls._makedirs_synced(folder, exist_ok=True)
         try:
             filesystem_path = path_to_filesystem(folder, sane_path)
         except ValueError:
             # Path is unsafe
             return
 
+        # Check if the path exists and if it leads to a collection or an item
         if not os.path.isdir(filesystem_path):
             if attributes and os.path.isfile(filesystem_path):
                 href = attributes.pop()
@@ -495,11 +512,11 @@ class Collection(BaseCollection):
         if not props.get("tag") and collection:
             props["tag"] = collection[0].name
         if not props:
-            os.makedirs(filesystem_path, exist_ok=True)
+            cls._makedirs_synced(filesystem_path, exist_ok=True)
             return cls(sane_path, principal=principal)
 
         parent_dir = os.path.dirname(filesystem_path)
-        os.makedirs(parent_dir, exist_ok=True)
+        cls._makedirs_synced(parent_dir, exist_ok=True)
 
         # Create a temporary directory with an unsafe name
         with TemporaryDirectory(
@@ -718,8 +735,7 @@ class Collection(BaseCollection):
             if not cls._lock_file:
                 folder = os.path.expanduser(
                     cls.configuration.get("storage", "filesystem_folder"))
-                if not os.path.exists(folder):
-                    os.makedirs(folder, exist_ok=True)
+                cls._makedirs_synced(folder, exist_ok=True)
                 lock_path = os.path.join(folder, ".Radicale.lock")
                 cls._lock_file = open(lock_path, "w+")
                 # Set access rights to a necessary minimum to prevent locking