raise exception when locking the storage fails

Previously it was silently ignored, which is dangerous when multiple instances of Radicale are running.
A configuration option to disable locking was added.
This commit is contained in:
Unrud 2017-06-01 11:21:22 +02:00
parent 17d03be27b
commit a18874fc59
3 changed files with 31 additions and 8 deletions

4
config
View File

@ -101,6 +101,10 @@
# Folder for storing local collections, created if not present
#filesystem_folder = /var/lib/radicale/collections
# Lock the storage. Never start multiple instances of Radicale or edit the
# storage externally while Radicale is running if disabled.
#filesystem_locking = True
# Sync all changes to disk during requests. (This can impair performance.)
# Disabling it increases the risk of data loss, when the system crashes or
# power fails!

View File

@ -139,6 +139,10 @@ INITIAL_CONFIG = OrderedDict([
"value": "True",
"help": "sync all changes to filesystem during requests",
"type": bool}),
("filesystem_locking", {
"value": "True",
"help": "lock the storage while accessing it",
"type": bool}),
("filesystem_close_lock_file", {
"value": "False",
"help": "close the lock file when no more clients are waiting",

View File

@ -751,6 +751,10 @@ class Collection(BaseCollection):
@classmethod
@contextmanager
def acquire_lock(cls, mode, user=None):
if not cls.configuration.getboolean("storage", "filesystem_locking"):
yield
return
def condition():
if mode == "r":
return not cls._writer
@ -786,21 +790,27 @@ class Collection(BaseCollection):
# by arbitrary users
try:
os.chmod(lock_path, stat.S_IWUSR | stat.S_IRUSR)
except OSError:
cls.logger.debug("Failed to set permissions on lock file")
except OSError as e:
cls.logger.info("Failed to set permissions on lock file:"
" %s", e, exc_info=True)
if not cls._lock_file_locked:
if os.name == "nt":
handle = msvcrt.get_osfhandle(cls._lock_file.fileno())
flags = LOCKFILE_EXCLUSIVE_LOCK if mode == "w" else 0
overlapped = Overlapped()
if not lock_file_ex(handle, flags, 0, 1, 0, overlapped):
cls.logger.debug("Locking not supported")
raise RuntimeError("Locking the storage failed: %s" %
ctypes.FormatError())
elif os.name == "posix":
_cmd = fcntl.LOCK_EX if mode == "w" else fcntl.LOCK_SH
try:
fcntl.flock(cls._lock_file.fileno(), _cmd)
except OSError:
cls.logger.debug("Locking not supported")
except OSError as e:
raise RuntimeError("Locking the storage failed: %s" %
e) from e
else:
raise RuntimeError("Locking the storage failed: "
"Unsupported operating system")
cls._lock_file_locked = True
try:
yield
@ -822,12 +832,17 @@ class Collection(BaseCollection):
handle = msvcrt.get_osfhandle(cls._lock_file.fileno())
overlapped = Overlapped()
if not unlock_file_ex(handle, 0, 1, 0, overlapped):
cls.logger.debug("Unlocking not supported")
raise RuntimeError("Unlocking the storage failed: "
"%s" % ctypes.FormatError())
elif os.name == "posix":
try:
fcntl.flock(cls._lock_file.fileno(), fcntl.LOCK_UN)
except OSError:
cls.logger.debug("Unlocking not supported")
except OSError as e:
raise RuntimeError("Unlocking the storage failed: "
"%s" % e) from e
else:
raise RuntimeError("Unlocking the storage failed: "
"Unsupported operating system")
cls._lock_file_locked = False
if cls._waiters:
cls._waiters[0].notify()