Merge pull request #462 from Unrud/durabledirs

Durable creation of directories and make sure that the root colleciton exists.
This commit is contained in:
Guillaume Ayoub 2016-08-06 14:02:41 +02:00 committed by GitHub
commit 4549d1b2db

View File

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