Add detection of file-system having no support for link creation (e.g. Unraid shares).

This commit is contained in:
JackDandy 2015-02-12 23:41:13 +00:00
parent 3eab0595e7
commit 77ed796092
2 changed files with 20 additions and 16 deletions

View file

@ -50,6 +50,7 @@
* Add percentage of episodes downloaded to footer and remove double spaces in text * Add percentage of episodes downloaded to footer and remove double spaces in text
* Fix SSL authentication on Synology stations * Fix SSL authentication on Synology stations
* Change IPT urls to reduce 301 redirection * Change IPT urls to reduce 301 redirection
* Add detection of file-system having no support for link creation (e.g. Unraid shares)
[develop changelog] [develop changelog]
* Change uT params from unicode to str.format as magnet URLs worked but sending files in POST bodies failed * Change uT params from unicode to str.format as magnet URLs worked but sending files in POST bodies failed

View file

@ -2,22 +2,25 @@ from __future__ import absolute_import
import time import time
import os import os
import errno
from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout, from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout,
AlreadyLocked) AlreadyLocked)
class LinkLockFile(LockBase):
"""Lock access to a file using atomic property of link(2).
>>> lock = LinkLockFile('somefile') class LinkLockFile(LockBase):
>>> lock = LinkLockFile('somefile', threaded=False) """
Lock access to a file using atomic property of link(2).
lock = LinkLockFile('somefile'[, threaded=False[, timeout=None]])
""" """
# noinspection PyTypeChecker
def acquire(self, timeout=None): def acquire(self, timeout=None):
try: try:
open(self.unique_name, "wb").close() open(self.unique_name, 'wb').close()
except IOError: except IOError:
raise LockFailed("failed to create %s" % self.unique_name) raise LockFailed('failed to create %s' % self.unique_name)
timeout = timeout is not None and timeout or self.timeout timeout = timeout is not None and timeout or self.timeout
end_time = time.time() end_time = time.time()
@ -28,7 +31,10 @@ class LinkLockFile(LockBase):
# Try and create a hard link to it. # Try and create a hard link to it.
try: try:
os.link(self.unique_name, self.lock_file) os.link(self.unique_name, self.lock_file)
except OSError: except OSError as e:
if errno.ENOSYS == e.errno:
raise LockFailed('%s' % e.strerror)
# Link creation failed. Maybe we've double-locked? # Link creation failed. Maybe we've double-locked?
nlinks = os.stat(self.unique_name).st_nlink nlinks = os.stat(self.unique_name).st_nlink
if nlinks == 2: if nlinks == 2:
@ -40,22 +46,20 @@ class LinkLockFile(LockBase):
if timeout is not None and time.time() > end_time: if timeout is not None and time.time() > end_time:
os.unlink(self.unique_name) os.unlink(self.unique_name)
if timeout > 0: if timeout > 0:
raise LockTimeout("Timeout waiting to acquire" raise LockTimeout('Timeout waiting to acquire lock for %s' % self.path)
" lock for %s" %
self.path)
else: else:
raise AlreadyLocked("%s is already locked" % raise AlreadyLocked('%s is already locked' % self.path)
self.path)
time.sleep(timeout is not None and timeout/10 or 0.1) time.sleep(timeout is not None and (timeout / 10) or 0.1)
else: else:
# Link creation succeeded. We're good to go. # Link creation succeeded. We're good to go.
return return
def release(self): def release(self):
if not self.is_locked(): if not self.is_locked():
raise NotLocked("%s is not locked" % self.path) raise NotLocked('%s is not locked' % self.path)
elif not os.path.exists(self.unique_name): elif not os.path.exists(self.unique_name):
raise NotMyLock("%s is locked, but not by me" % self.path) raise NotMyLock('%s is locked, but not by me' % self.path)
os.unlink(self.unique_name) os.unlink(self.unique_name)
os.unlink(self.lock_file) os.unlink(self.lock_file)
@ -70,4 +74,3 @@ class LinkLockFile(LockBase):
def break_lock(self): def break_lock(self):
if os.path.exists(self.lock_file): if os.path.exists(self.lock_file):
os.unlink(self.lock_file) os.unlink(self.lock_file)