Merge pull request #616 from Unrud/locking
raise exception when locking the storage fails
This commit is contained in:
commit
91e49986ee
4
config
4
config
@ -101,6 +101,10 @@
|
|||||||
# Folder for storing local collections, created if not present
|
# Folder for storing local collections, created if not present
|
||||||
#filesystem_folder = /var/lib/radicale/collections
|
#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.)
|
# Sync all changes to disk during requests. (This can impair performance.)
|
||||||
# Disabling it increases the risk of data loss, when the system crashes or
|
# Disabling it increases the risk of data loss, when the system crashes or
|
||||||
# power fails!
|
# power fails!
|
||||||
|
@ -32,9 +32,8 @@ import ssl
|
|||||||
import sys
|
import sys
|
||||||
from wsgiref.simple_server import make_server
|
from wsgiref.simple_server import make_server
|
||||||
|
|
||||||
from . import (
|
from . import (VERSION, Application, RequestHandler, ThreadedHTTPServer,
|
||||||
VERSION, Application, RequestHandler, ThreadedHTTPServer,
|
ThreadedHTTPSServer, config, log)
|
||||||
ThreadedHTTPSServer, config, log)
|
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def run():
|
||||||
|
@ -139,6 +139,10 @@ INITIAL_CONFIG = OrderedDict([
|
|||||||
"value": "True",
|
"value": "True",
|
||||||
"help": "sync all changes to filesystem during requests",
|
"help": "sync all changes to filesystem during requests",
|
||||||
"type": bool}),
|
"type": bool}),
|
||||||
|
("filesystem_locking", {
|
||||||
|
"value": "True",
|
||||||
|
"help": "lock the storage while accessing it",
|
||||||
|
"type": bool}),
|
||||||
("filesystem_close_lock_file", {
|
("filesystem_close_lock_file", {
|
||||||
"value": "False",
|
"value": "False",
|
||||||
"help": "close the lock file when no more clients are waiting",
|
"help": "close the lock file when no more clients are waiting",
|
||||||
|
@ -751,6 +751,10 @@ class Collection(BaseCollection):
|
|||||||
@classmethod
|
@classmethod
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def acquire_lock(cls, mode, user=None):
|
def acquire_lock(cls, mode, user=None):
|
||||||
|
if not cls.configuration.getboolean("storage", "filesystem_locking"):
|
||||||
|
yield
|
||||||
|
return
|
||||||
|
|
||||||
def condition():
|
def condition():
|
||||||
if mode == "r":
|
if mode == "r":
|
||||||
return not cls._writer
|
return not cls._writer
|
||||||
@ -786,21 +790,27 @@ class Collection(BaseCollection):
|
|||||||
# by arbitrary users
|
# by arbitrary users
|
||||||
try:
|
try:
|
||||||
os.chmod(lock_path, stat.S_IWUSR | stat.S_IRUSR)
|
os.chmod(lock_path, stat.S_IWUSR | stat.S_IRUSR)
|
||||||
except OSError:
|
except OSError as e:
|
||||||
cls.logger.debug("Failed to set permissions on lock file")
|
cls.logger.info("Failed to set permissions on lock file:"
|
||||||
|
" %s", e, exc_info=True)
|
||||||
if not cls._lock_file_locked:
|
if not cls._lock_file_locked:
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
handle = msvcrt.get_osfhandle(cls._lock_file.fileno())
|
handle = msvcrt.get_osfhandle(cls._lock_file.fileno())
|
||||||
flags = LOCKFILE_EXCLUSIVE_LOCK if mode == "w" else 0
|
flags = LOCKFILE_EXCLUSIVE_LOCK if mode == "w" else 0
|
||||||
overlapped = Overlapped()
|
overlapped = Overlapped()
|
||||||
if not lock_file_ex(handle, flags, 0, 1, 0, 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":
|
elif os.name == "posix":
|
||||||
_cmd = fcntl.LOCK_EX if mode == "w" else fcntl.LOCK_SH
|
_cmd = fcntl.LOCK_EX if mode == "w" else fcntl.LOCK_SH
|
||||||
try:
|
try:
|
||||||
fcntl.flock(cls._lock_file.fileno(), _cmd)
|
fcntl.flock(cls._lock_file.fileno(), _cmd)
|
||||||
except OSError:
|
except OSError as e:
|
||||||
cls.logger.debug("Locking not supported")
|
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
|
cls._lock_file_locked = True
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
@ -822,12 +832,17 @@ class Collection(BaseCollection):
|
|||||||
handle = msvcrt.get_osfhandle(cls._lock_file.fileno())
|
handle = msvcrt.get_osfhandle(cls._lock_file.fileno())
|
||||||
overlapped = Overlapped()
|
overlapped = Overlapped()
|
||||||
if not unlock_file_ex(handle, 0, 1, 0, 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":
|
elif os.name == "posix":
|
||||||
try:
|
try:
|
||||||
fcntl.flock(cls._lock_file.fileno(), fcntl.LOCK_UN)
|
fcntl.flock(cls._lock_file.fileno(), fcntl.LOCK_UN)
|
||||||
except OSError:
|
except OSError as e:
|
||||||
cls.logger.debug("Unlocking not supported")
|
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
|
cls._lock_file_locked = False
|
||||||
if cls._waiters:
|
if cls._waiters:
|
||||||
cls._waiters[0].notify()
|
cls._waiters[0].notify()
|
||||||
|
@ -26,6 +26,7 @@ import shutil
|
|||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from radicale import Application, config
|
from radicale import Application, config
|
||||||
|
|
||||||
from .test_base import BaseTest
|
from .test_base import BaseTest
|
||||||
|
@ -26,6 +26,7 @@ import shutil
|
|||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from radicale import Application, config
|
from radicale import Application, config
|
||||||
|
|
||||||
from . import BaseTest
|
from . import BaseTest
|
||||||
|
Loading…
Reference in New Issue
Block a user