summaryrefslogtreecommitdiffstats
path: root/gluster/swift/obj/diskfile.py
diff options
context:
space:
mode:
Diffstat (limited to 'gluster/swift/obj/diskfile.py')
-rw-r--r--gluster/swift/obj/diskfile.py94
1 files changed, 53 insertions, 41 deletions
diff --git a/gluster/swift/obj/diskfile.py b/gluster/swift/obj/diskfile.py
index 47cde89..eb180a2 100644
--- a/gluster/swift/obj/diskfile.py
+++ b/gluster/swift/obj/diskfile.py
@@ -447,9 +447,9 @@ class DiskFileWriter(object):
df._threadpool.force_run_in_thread(self._finalize_put, metadata)
- # Avoid the unlink() system call as part of the mkstemp context
- # cleanup
- self.tmppath = None
+ # Avoid the unlink() system call as part of the DiskFile.create()
+ # context cleanup
+ self._tmppath = None
class DiskFileReader(object):
@@ -509,7 +509,7 @@ class DiskFileReader(object):
bytes_read += len(chunk)
diff = bytes_read - dropped_cache
if diff > (1024 * 1024):
- self._drop_cache(self._fd, dropped_cache, diff)
+ self._drop_cache(dropped_cache, diff)
dropped_cache = bytes_read
yield chunk
if self._iter_hook:
@@ -604,6 +604,7 @@ class DiskFile(object):
self._logger = mgr.logger
self._metadata = None
self._fd = None
+ self._stat = None
# Don't store a value for data_file until we know it exists.
self._data_file = None
@@ -644,37 +645,49 @@ class DiskFile(object):
"""
# Writes are always performed to a temporary file
try:
- fd = do_open(self._data_file, os.O_RDONLY | O_CLOEXEC)
+ self._fd = do_open(self._data_file, os.O_RDONLY | O_CLOEXEC)
except GlusterFileSystemOSError as err:
if err.errno in (errno.ENOENT, errno.ENOTDIR):
# If the file does exist, or some part of the path does not
# exist, raise the expected DiskFileNotExist
raise DiskFileNotExist
raise
- else:
- stats = do_fstat(fd)
- if not stats:
- return
- self._is_dir = stat.S_ISDIR(stats.st_mode)
- obj_size = stats.st_size
-
- self._metadata = read_metadata(fd)
- if not validate_object(self._metadata):
- create_object_metadata(fd)
- self._metadata = read_metadata(fd)
- assert self._metadata is not None
- self._filter_metadata()
-
- if self._is_dir:
- do_close(fd)
- obj_size = 0
- self._fd = -1
- else:
- if self._is_object_expired(self._metadata):
- raise DiskFileExpired(metadata=self._metadata)
- self._fd = fd
-
- self._obj_size = obj_size
+ try:
+ self._stat = do_fstat(self._fd)
+ self._is_dir = stat.S_ISDIR(self._stat.st_mode)
+ obj_size = self._stat.st_size
+
+ self._metadata = read_metadata(self._fd)
+ if not validate_object(self._metadata, self._stat):
+ create_object_metadata(self._fd)
+ self._metadata = read_metadata(self._fd)
+ assert self._metadata is not None
+ self._filter_metadata()
+
+ if self._is_dir:
+ do_close(self._fd)
+ obj_size = 0
+ self._fd = -1
+ else:
+ if self._is_object_expired(self._metadata):
+ raise DiskFileExpired(metadata=self._metadata)
+ self._obj_size = obj_size
+ except (OSError, IOError, DiskFileExpired) as err:
+ # Something went wrong. Context manager will not call
+ # __exit__. So we close the fd manually here.
+ self._close_fd()
+ if hasattr(err, 'errno') and \
+ err.errno in (errno.ENOENT, errno.ESTALE):
+ # Handle races: ENOENT/ESTALE can be raised by read_metadata()
+ # call in GlusterFS if file gets deleted by another
+ # client after do_open() succeeds
+ logging.warn("open(%s) succeeded but one of the subsequent "
+ "syscalls failed with ENOENT/ESTALE. Raising "
+ "DiskFileNotExist." % (self._data_file))
+ raise DiskFileNotExist
+ else:
+ # Re-raise the original exception after fd has been closed
+ raise err
return self
def _is_object_expired(self, metadata):
@@ -712,6 +725,12 @@ class DiskFile(object):
raise DiskFileNotOpen()
return self
+ def _close_fd(self):
+ if self._fd is not None:
+ fd, self._fd = self._fd, None
+ if fd > -1:
+ do_close(fd)
+
def __exit__(self, t, v, tb):
"""
Context exit.
@@ -723,10 +742,7 @@ class DiskFile(object):
responsibility of the implementation to properly handle that.
"""
self._metadata = None
- if self._fd is not None:
- fd, self._fd = self._fd, None
- if fd > -1:
- do_close(fd)
+ self._close_fd()
def get_metadata(self):
"""
@@ -833,10 +849,6 @@ class DiskFile(object):
" on subpath: %s" % (full_path, cur_path))
child = stack.pop() if stack else None
return True, newmd
- # Exists, but as a file
- #raise DiskFileError('DiskFile.put(): directory creation failed'
- # ' since the target, %s, already exists as'
- # ' a file' % df._data_file)
@contextmanager
def create(self, size=None):
@@ -893,7 +905,7 @@ class DiskFile(object):
if attempts >= MAX_OPEN_ATTEMPTS:
# We failed after N attempts to create the temporary
# file.
- raise DiskFileError('DiskFile.mkstemp(): failed to'
+ raise DiskFileError('DiskFile.create(): failed to'
' successfully create a temporary file'
' without running into a name conflict'
' after %d of %d attempts for: %s' % (
@@ -906,7 +918,7 @@ class DiskFile(object):
# FIXME: Possible FUSE issue or race condition, let's
# sleep on it and retry the operation.
_random_sleep()
- logging.warn("DiskFile.mkstemp(): %s ... retrying in"
+ logging.warn("DiskFile.create(): %s ... retrying in"
" 0.1 secs", gerr)
attempts += 1
elif not self._obj_path:
@@ -915,7 +927,7 @@ class DiskFile(object):
# could be a FUSE issue or some race condition, so let's
# sleep a bit and retry.
_random_sleep()
- logging.warn("DiskFile.mkstemp(): %s ... retrying in"
+ logging.warn("DiskFile.create(): %s ... retrying in"
" 0.1 secs", gerr)
attempts += 1
elif attempts > 1:
@@ -923,7 +935,7 @@ class DiskFile(object):
# also be a FUSE issue or some race condition, nap and
# retry.
_random_sleep()
- logging.warn("DiskFile.mkstemp(): %s ... retrying in"
+ logging.warn("DiskFile.create(): %s ... retrying in"
" 0.1 secs" % gerr)
attempts += 1
else: