Beispiel #1
0
 def test_fchown(self):
     tmpdir = mkdtemp()
     try:
         fd, tmpfile = mkstemp(dir=tmpdir)
         buf = os.stat(tmpfile)
         if buf.st_uid == 0:
             raise SkipTest
         else:
             try:
                 fs.do_fchown(fd, 20000, 20000)
             except OSError as ex:
                 if ex.errno != errno.EPERM:
                     self.fail("Expected OSError")
             else:
                     self.fail("Expected OSError")
     finally:
         os.close(fd)
         shutil.rmtree(tmpdir)
 def test_fchown_err(self):
     tmpdir = mkdtemp()
     try:
         fd, tmpfile = mkstemp(dir=tmpdir)
         fd_rd = os.open(tmpfile, os.O_RDONLY)
         buf = os.stat(tmpfile)
         if buf.st_uid == 0:
             raise SkipTest
         else:
             try:
                 fs.do_fchown(fd_rd, 20000, 20000)
             except GlusterFileSystemOSError as ex:
                 if ex.errno != errno.EPERM:
                     self.fail(
                         "Expected GlusterFileSystemOSError(errno=EPERM)")
             else:
                 self.fail("Expected GlusterFileSystemOSError")
     finally:
         os.close(fd_rd)
         os.close(fd)
         shutil.rmtree(tmpdir)
    def create(self, size=None):
        """
        Context manager to create a file. We create a temporary file first, and
        then return a DiskFileWriter object to encapsulate the state.

        For Gluster, we first optimistically create the temporary file using
        the "rsync-friendly" .NAME.random naming. If we find that some path to
        the file does not exist, we then create that path and then create the
        temporary file again. If we get file name conflict, we'll retry using
        different random suffixes 1,000 times before giving up.

        .. note::

            An implementation is not required to perform on-disk
            preallocations even if the parameter is specified. But if it does
            and it fails, it must raise a `DiskFileNoSpace` exception.

        :param size: optional initial size of file to explicitly allocate on
                     disk
        :raises DiskFileNoSpace: if a size is specified and allocation fails
        :raises AlreadyExistsAsFile: if path or part of a path is not a \
                                     directory
        """
        data_file = os.path.join(self._put_datadir, self._obj)

        # Assume the full directory path exists to the file already, and
        # construct the proper name for the temporary file.
        attempts = 1
        cur_thread = str(getcurrent())
        while True:
            postfix = md5(self._obj + _cur_host + _cur_pid + cur_thread
                          + str(random.random())).hexdigest()
            tmpfile = '.' + self._obj + '.' + postfix
            tmppath = os.path.join(self._put_datadir, tmpfile)
            try:
                fd = do_open(tmppath,
                             os.O_WRONLY | os.O_CREAT | os.O_EXCL | O_CLOEXEC)
            except GlusterFileSystemOSError as gerr:
                if gerr.errno in (errno.ENOSPC, errno.EDQUOT):
                    # Raise DiskFileNoSpace to be handled by upper layers when
                    # there is no space on disk OR when quota is exceeded
                    raise DiskFileNoSpace()
                if gerr.errno == errno.ENOTDIR:
                    raise AlreadyExistsAsFile('do_open(): failed on %s,'
                                              '  path or part of a'
                                              ' path is not a directory'
                                              % (tmppath))

                if gerr.errno not in (errno.ENOENT, errno.EEXIST, errno.EIO):
                    # FIXME: Other cases we should handle?
                    raise
                if attempts >= MAX_OPEN_ATTEMPTS:
                    # We failed after N attempts to create the temporary
                    # file.
                    raise DiskFileError('DiskFile.mkstemp(): failed to'
                                        ' successfully create a temporary file'
                                        ' without running into a name conflict'
                                        ' after %d of %d attempts for: %s' % (
                                            attempts, MAX_OPEN_ATTEMPTS,
                                            data_file))
                if gerr.errno == errno.EEXIST:
                    # Retry with a different random number.
                    attempts += 1
                elif gerr.errno == errno.EIO:
                    # 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"
                                 " 0.1 secs", gerr)
                    attempts += 1
                elif not self._obj_path:
                    # No directory hierarchy and the create failed telling us
                    # the container or volume directory does not exist. This
                    # 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"
                                 " 0.1 secs", gerr)
                    attempts += 1
                elif attempts > 1:
                    # Got ENOENT after previously making the path. This could
                    # also be a FUSE issue or some race condition, nap and
                    # retry.
                    _random_sleep()
                    logging.warn("DiskFile.mkstemp(): %s ... retrying in"
                                 " 0.1 secs" % gerr)
                    attempts += 1
                else:
                    # It looks like the path to the object does not already
                    # exist; don't count this as an attempt, though, since
                    # we perform the open() system call optimistically.
                    self._create_dir_object(self._obj_path)
            else:
                break
        dw = None
        try:
            # Ensure it is properly owned before we make it available.
            do_fchown(fd, self._uid, self._gid)
            # NOTE: we do not perform the fallocate() call at all. We ignore
            # it completely since at the time of this writing FUSE does not
            # support it.
            dw = DiskFileWriter(fd, tmppath, self)
            yield dw
        finally:
            dw.close()
            if dw._tmppath:
                do_unlink(dw._tmppath)
Beispiel #4
0
    def mkstemp(self, size=None):
        """
        Contextmanager to make a temporary file, optionally of a specified
        initial size.

        For Gluster, we first optimistically create the temporary file using
        the "rsync-friendly" .NAME.random naming. If we find that some path to
        the file does not exist, we then create that path and then create the
        temporary file again. If we get file name conflict, we'll retry using
        different random suffixes 1,000 times before giving up.
        """
        data_file = os.path.join(self.put_datadir, self._obj)

        # Assume the full directory path exists to the file already, and
        # construct the proper name for the temporary file.
        for i in range(0, 1000):
            tmpfile = '.' + self._obj + '.' + md5(self._obj +
                      str(random.random())).hexdigest()
            tmppath = os.path.join(self.put_datadir, tmpfile)
            try:
                fd = do_open(tmppath,
                             os.O_WRONLY | os.O_CREAT | os.O_EXCL | O_CLOEXEC)
            except GlusterFileSystemOSError as gerr:
                if gerr.errno == errno.EEXIST:
                    # Retry with a different random number.
                    continue
                if gerr.errno == errno.EIO:
                    # 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"
                                 " 0.1 secs", gerr)
                    continue
                if gerr.errno != errno.ENOENT:
                    # FIXME: Other cases we should handle?
                    raise
                if not self._obj_path:
                    # No directory hierarchy and the create failed telling us
                    # the container or volume directory does not exist. This
                    # 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"
                                 " 0.1 secs", gerr)
                    continue
                if i != 0:
                    # Got ENOENT after previously making the path. This could
                    # also be a FUSE issue or some race condition, nap and
                    # retry.
                    _random_sleep()
                    logging.warn("DiskFile.mkstemp(): %s ... retrying in"
                                 " 0.1 secs" % gerr)
                    continue
                # It looks like the path to the object does not already exist
                self._create_dir_object(self._obj_path)
                continue
            else:
                break
        else:
            # We failed after 1,000 attempts to create the temporary file.
            raise DiskFileError('DiskFile.mkstemp(): failed to successfully'
                                ' create a temporary file without running'
                                ' into a name conflict after 1,000 attempts'
                                ' for: %s' % (data_file,))

        self.tmppath = tmppath

        try:
            # Ensure it is properly owned before we make it available.
            do_fchown(fd, self.uid, self.gid)
            if _preallocate and size:
                # For XFS, fallocate() turns off speculative pre-allocation
                # until a write is issued either to the last block of the file
                # before the EOF or beyond the EOF. This means that we are
                # less likely to fragment free space with pre-allocated
                # extents that get truncated back to the known file size.
                # However, this call also turns holes into allocated but
                # unwritten extents, so that allocation occurs before the
                # write, not during XFS writeback. This effectively defeats
                # any allocation optimizations the filesystem can make at
                # writeback time.
                fallocate(fd, size)
            yield fd
        finally:
            try:
                do_close(fd)
            except OSError:
                pass
            if self.tmppath:
                tmppath, self.tmppath = self.tmppath, None
                do_unlink(tmppath)
Beispiel #5
0
    def writer(self, size=None):
        """
        Contextmanager to make a temporary file, optionally of a specified
        initial size.

        For Gluster, we first optimistically create the temporary file using
        the "rsync-friendly" .NAME.random naming. If we find that some path to
        the file does not exist, we then create that path and then create the
        temporary file again. If we get file name conflict, we'll retry using
        different random suffixes 1,000 times before giving up.
        """
        data_file = os.path.join(self.put_datadir, self._obj)

        # Assume the full directory path exists to the file already, and
        # construct the proper name for the temporary file.
        attempts = 1
        cur_thread = str(getcurrent())
        while True:
            postfix = md5(self._obj + _cur_host + _cur_pid + cur_thread
                          + str(random.random())).hexdigest()
            tmpfile = '.' + self._obj + '.' + postfix
            tmppath = os.path.join(self.put_datadir, tmpfile)
            try:
                fd = do_open(tmppath,
                             os.O_WRONLY | os.O_CREAT | os.O_EXCL | O_CLOEXEC)
            except GlusterFileSystemOSError as gerr:
                if gerr.errno == errno.ENOSPC:
                    # Raise DiskFileNoSpace to be handled by upper layers
                    raise DiskFileNoSpace()
                if gerr.errno not in (errno.ENOENT, errno.EEXIST, errno.EIO):
                    # FIXME: Other cases we should handle?
                    raise
                if attempts >= MAX_OPEN_ATTEMPTS:
                    # We failed after N attempts to create the temporary
                    # file.
                    raise DiskFileError('DiskFile.mkstemp(): failed to'
                                        ' successfully create a temporary file'
                                        ' without running into a name conflict'
                                        ' after %d of %d attempts for: %s' % (
                                            attempts, MAX_OPEN_ATTEMPTS,
                                            data_file))
                if gerr.errno == errno.EEXIST:
                    # Retry with a different random number.
                    attempts += 1
                elif gerr.errno == errno.EIO:
                    # 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"
                                 " 0.1 secs", gerr)
                    attempts += 1
                elif not self._obj_path:
                    # No directory hierarchy and the create failed telling us
                    # the container or volume directory does not exist. This
                    # 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"
                                 " 0.1 secs", gerr)
                    attempts += 1
                elif attempts > 1:
                    # Got ENOENT after previously making the path. This could
                    # also be a FUSE issue or some race condition, nap and
                    # retry.
                    _random_sleep()
                    logging.warn("DiskFile.mkstemp(): %s ... retrying in"
                                 " 0.1 secs" % gerr)
                    attempts += 1
                else:
                    # It looks like the path to the object does not already
                    # exist; don't count this as an attempt, though, since
                    # we perform the open() system call optimistically.
                    self._create_dir_object(self._obj_path)
            else:
                break
        dw = None
        try:
            # Ensure it is properly owned before we make it available.
            do_fchown(fd, self.uid, self.gid)
            # NOTE: we do not perform the fallocate() call at all. We ignore
            # it completely.
            dw = DiskWriter(self, fd, tmppath, self.threadpool)
            yield dw
        finally:
            try:
                if dw.fd:
                    do_close(dw.fd)
            except OSError:
                pass
            if dw.tmppath:
                do_unlink(dw.tmppath)
Beispiel #6
0
    def create(self, size=None):
        """
        Context manager to create a file. We create a temporary file first, and
        then return a DiskFileWriter object to encapsulate the state.

        For Gluster, we first optimistically create the temporary file using
        the "rsync-friendly" .NAME.random naming. If we find that some path to
        the file does not exist, we then create that path and then create the
        temporary file again. If we get file name conflict, we'll retry using
        different random suffixes 1,000 times before giving up.

        .. note::

            An implementation is not required to perform on-disk
            preallocations even if the parameter is specified. But if it does
            and it fails, it must raise a `DiskFileNoSpace` exception.

        :param size: optional initial size of file to explicitly allocate on
                     disk
        :raises DiskFileNoSpace: if a size is specified and allocation fails
        :raises AlreadyExistsAsFile: if path or part of a path is not a \
                                     directory
        """
        data_file = os.path.join(self._put_datadir, self._obj)

        # Assume the full directory path exists to the file already, and
        # construct the proper name for the temporary file.
        attempts = 1
        cur_thread = str(getcurrent())
        while True:
            postfix = md5(self._obj + _cur_host + _cur_pid + cur_thread +
                          str(random.random())).hexdigest()
            tmpfile = '.' + self._obj + '.' + postfix
            tmppath = os.path.join(self._put_datadir, tmpfile)
            try:
                fd = do_open(tmppath,
                             os.O_WRONLY | os.O_CREAT | os.O_EXCL | O_CLOEXEC)
            except GlusterFileSystemOSError as gerr:
                if gerr.errno in (errno.ENOSPC, errno.EDQUOT):
                    # Raise DiskFileNoSpace to be handled by upper layers when
                    # there is no space on disk OR when quota is exceeded
                    raise DiskFileNoSpace()
                if gerr.errno == errno.ENOTDIR:
                    raise AlreadyExistsAsFile('do_open(): failed on %s,'
                                              '  path or part of a'
                                              ' path is not a directory' %
                                              (tmppath))

                if gerr.errno not in (errno.ENOENT, errno.EEXIST, errno.EIO):
                    # FIXME: Other cases we should handle?
                    raise
                if attempts >= MAX_OPEN_ATTEMPTS:
                    # We failed after N attempts to create the temporary
                    # file.
                    raise DiskFileError(
                        'DiskFile.mkstemp(): failed to'
                        ' successfully create a temporary file'
                        ' without running into a name conflict'
                        ' after %d of %d attempts for: %s' %
                        (attempts, MAX_OPEN_ATTEMPTS, data_file))
                if gerr.errno == errno.EEXIST:
                    # Retry with a different random number.
                    attempts += 1
                elif gerr.errno == errno.EIO:
                    # 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"
                        " 0.1 secs", gerr)
                    attempts += 1
                elif not self._obj_path:
                    # No directory hierarchy and the create failed telling us
                    # the container or volume directory does not exist. This
                    # 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"
                        " 0.1 secs", gerr)
                    attempts += 1
                elif attempts > 1:
                    # Got ENOENT after previously making the path. This could
                    # also be a FUSE issue or some race condition, nap and
                    # retry.
                    _random_sleep()
                    logging.warn("DiskFile.mkstemp(): %s ... retrying in"
                                 " 0.1 secs" % gerr)
                    attempts += 1
                else:
                    # It looks like the path to the object does not already
                    # exist; don't count this as an attempt, though, since
                    # we perform the open() system call optimistically.
                    self._create_dir_object(self._obj_path)
            else:
                break
        dw = None
        try:
            # Ensure it is properly owned before we make it available.
            do_fchown(fd, self._uid, self._gid)
            # NOTE: we do not perform the fallocate() call at all. We ignore
            # it completely since at the time of this writing FUSE does not
            # support it.
            dw = DiskFileWriter(fd, tmppath, self)
            yield dw
        finally:
            dw.close()
            if dw._tmppath:
                do_unlink(dw._tmppath)
Beispiel #7
0
    def create(self, size=None):
        """
        Context manager to create a file. We create a temporary file first, and
        then return a DiskFileWriter object to encapsulate the state.

        For Gluster, we first optimistically create the temporary file using
        the "rsync-friendly" .NAME.random naming. If we find that some path to
        the file does not exist, we then create that path and then create the
        temporary file again. If we get file name conflict, we'll retry using
        different random suffixes 1,000 times before giving up.

        .. note::

            An implementation is not required to perform on-disk
            preallocations even if the parameter is specified. But if it does
            and it fails, it must raise a `DiskFileNoSpace` exception.

        :param size: optional initial size of file to explicitly allocate on
                     disk
        :raises DiskFileNoSpace: if a size is specified and allocation fails
        :raises AlreadyExistsAsFile: if path or part of a path is not a \
                                     directory
        """

        data_file = os.path.join(self._put_datadir, self._obj)

        # Assume the full directory path exists to the file already, and
        # construct the proper name for the temporary file.
        fd = None
        attempts = 1
        while True:
            tmpfile = '.' + self._obj + '.' + uuid4().hex
            tmppath = os.path.join(self._put_datadir, tmpfile)
            try:
                fd = do_open(tmppath,
                             os.O_WRONLY | os.O_CREAT | os.O_EXCL | O_CLOEXEC)
            except GlusterFileSystemOSError as gerr:
                if gerr.errno in (errno.ENOSPC, errno.EDQUOT):
                    # Raise DiskFileNoSpace to be handled by upper layers when
                    # there is no space on disk OR when quota is exceeded
                    raise DiskFileNoSpace()
                if gerr.errno == errno.ENOTDIR:
                    raise AlreadyExistsAsFile('do_open(): failed on %s,'
                                              '  path or part of a'
                                              ' path is not a directory'
                                              % (tmppath))

                if gerr.errno not in (errno.ENOENT, errno.EEXIST, errno.EIO):
                    # FIXME: Other cases we should handle?
                    raise
                if attempts >= MAX_OPEN_ATTEMPTS:
                    # We failed after N attempts to create the temporary
                    # file.
                    raise DiskFileError('DiskFile.create(): failed to'
                                        ' successfully create a temporary file'
                                        ' without running into a name conflict'
                                        ' after %d of %d attempts for: %s' % (
                                            attempts, MAX_OPEN_ATTEMPTS,
                                            data_file))
                if gerr.errno == errno.EEXIST:
                    # Retry with a different random number.
                    attempts += 1
                elif gerr.errno == errno.EIO:
                    # FIXME: Possible FUSE issue or race condition, let's
                    # sleep on it and retry the operation.
                    _random_sleep()
                    logging.warn("DiskFile.create(): %s ... retrying in"
                                 " 0.1 secs", gerr)
                    attempts += 1
                elif not self._obj_path:
                    # ENOENT
                    # No directory hierarchy and the create failed telling us
                    # the container or volume directory does not exist. This
                    # could be a FUSE issue or some race condition, so let's
                    # sleep a bit and retry.
                    # Handle race:
                    # This can be the issue when memcache has cached that the
                    # container exists. If someone removes the container dir
                    # from filesystem, it's not reflected in memcache. So
                    # swift reports that the container exists and this code
                    # tries to create a file in a directory that does not
                    # exist. However, it's wrong to create the container here.
                    _random_sleep()
                    logging.warn("DiskFile.create(): %s ... retrying in"
                                 " 0.1 secs", gerr)
                    attempts += 1
                    if attempts > 2:
                        # Ideally we would want to return 404 indicating that
                        # the container itself does not exist. Can't be done
                        # though as the caller won't catch DiskFileNotExist.
                        # We raise an exception with a meaningful name for
                        # correctness.
                        logging.warn("Container dir %s does not exist",
                                     self._container_path)
                        raise DiskFileContainerDoesNotExist
                elif attempts > 1:
                    # Got ENOENT after previously making the path. This could
                    # also be a FUSE issue or some race condition, nap and
                    # retry.
                    _random_sleep()
                    logging.warn("DiskFile.create(): %s ... retrying in"
                                 " 0.1 secs" % gerr)
                    attempts += 1
                else:
                    # It looks like the path to the object does not already
                    # exist; don't count this as an attempt, though, since
                    # we perform the open() system call optimistically.
                    self._create_dir_object(self._obj_path)
            else:
                break
        dw = None
        try:
            # Ensure it is properly owned before we make it available.
            if not ((self._uid == DEFAULT_UID) and (self._gid == DEFAULT_GID)):
                # If both UID and GID is -1 (default values), it has no effect.
                # So don't do a fchown.
                # Further, at the time of this writing, UID and GID information
                # is not passed to DiskFile.
                do_fchown(fd, self._uid, self._gid)
            # NOTE: we do not perform the fallocate() call at all. We ignore
            # it completely since at the time of this writing FUSE does not
            # support it.
            dw = DiskFileWriter(fd, tmppath, self)
            # It's now the responsibility of DiskFileWriter to close this fd.
            fd = None
            yield dw
        finally:
            if dw:
                dw.close()
                if dw._tmppath:
                    do_unlink(dw._tmppath)
Beispiel #8
0
    def create(self, size=None):
        """
        Context manager to create a file. We create a temporary file first, and
        then return a DiskFileWriter object to encapsulate the state.

        For Gluster, we first optimistically create the temporary file using
        the "rsync-friendly" .NAME.random naming. If we find that some path to
        the file does not exist, we then create that path and then create the
        temporary file again. If we get file name conflict, we'll retry using
        different random suffixes 1,000 times before giving up.

        .. note::

            An implementation is not required to perform on-disk
            preallocations even if the parameter is specified. But if it does
            and it fails, it must raise a `DiskFileNoSpace` exception.

        :param size: optional initial size of file to explicitly allocate on
                     disk
        :raises DiskFileNoSpace: if a size is specified and allocation fails
        :raises AlreadyExistsAsFile: if path or part of a path is not a \
                                     directory
        """
        data_file = os.path.join(self._put_datadir, self._obj)

        # Assume the full directory path exists to the file already, and
        # construct the proper name for the temporary file.
        fd = None
        attempts = 1
        while True:
            tmpfile = '.' + self._obj + '.' + uuid4().hex
            tmppath = os.path.join(self._put_datadir, tmpfile)
            try:
                fd = do_open(tmppath,
                             os.O_WRONLY | os.O_CREAT | os.O_EXCL | O_CLOEXEC)
            except GlusterFileSystemOSError as gerr:
                if gerr.errno in (errno.ENOSPC, errno.EDQUOT):
                    # Raise DiskFileNoSpace to be handled by upper layers when
                    # there is no space on disk OR when quota is exceeded
                    raise DiskFileNoSpace()
                if gerr.errno == errno.ENOTDIR:
                    raise AlreadyExistsAsFile('do_open(): failed on %s,'
                                              '  path or part of a'
                                              ' path is not a directory'
                                              % (tmppath))

                if gerr.errno not in (errno.ENOENT, errno.EEXIST, errno.EIO):
                    # FIXME: Other cases we should handle?
                    raise
                if attempts >= MAX_OPEN_ATTEMPTS:
                    # We failed after N attempts to create the temporary
                    # file.
                    raise DiskFileError('DiskFile.create(): failed to'
                                        ' successfully create a temporary file'
                                        ' without running into a name conflict'
                                        ' after %d of %d attempts for: %s' % (
                                            attempts, MAX_OPEN_ATTEMPTS,
                                            data_file))
                if gerr.errno == errno.EEXIST:
                    # Retry with a different random number.
                    attempts += 1
                elif gerr.errno == errno.EIO:
                    # FIXME: Possible FUSE issue or race condition, let's
                    # sleep on it and retry the operation.
                    _random_sleep()
                    logging.warn("DiskFile.create(): %s ... retrying in"
                                 " 0.1 secs", gerr)
                    attempts += 1
                elif not self._obj_path:
                    # ENOENT
                    # No directory hierarchy and the create failed telling us
                    # the container or volume directory does not exist. This
                    # could be a FUSE issue or some race condition, so let's
                    # sleep a bit and retry.
                    # Handle race:
                    # This can be the issue when memcache has cached that the
                    # container exists. If someone removes the container dir
                    # from filesystem, it's not reflected in memcache. So
                    # swift reports that the container exists and this code
                    # tries to create a file in a directory that does not
                    # exist. However, it's wrong to create the container here.
                    _random_sleep()
                    logging.warn("DiskFile.create(): %s ... retrying in"
                                 " 0.1 secs", gerr)
                    attempts += 1
                    if attempts > 2:
                        # Ideally we would want to return 404 indicating that
                        # the container itself does not exist. Can't be done
                        # though as the caller won't catch DiskFileNotExist.
                        # We raise an exception with a meaningful name for
                        # correctness.
                        logging.warn("Container dir %s does not exist",
                                     self._container_path)
                        raise DiskFileContainerDoesNotExist
                elif attempts > 1:
                    # Got ENOENT after previously making the path. This could
                    # also be a FUSE issue or some race condition, nap and
                    # retry.
                    _random_sleep()
                    logging.warn("DiskFile.create(): %s ... retrying in"
                                 " 0.1 secs" % gerr)
                    attempts += 1
                else:
                    # It looks like the path to the object does not already
                    # exist; don't count this as an attempt, though, since
                    # we perform the open() system call optimistically.
                    self._create_dir_object(self._obj_path)
            else:
                break
        dw = None
        try:
            # Ensure it is properly owned before we make it available.
            if not ((self._uid == DEFAULT_UID) and (self._gid == DEFAULT_GID)):
                # If both UID and GID is -1 (default values), it has no effect.
                # So don't do a fchown.
                # Further, at the time of this writing, UID and GID information
                # is not passed to DiskFile.
                do_fchown(fd, self._uid, self._gid)
            # NOTE: we do not perform the fallocate() call at all. We ignore
            # it completely since at the time of this writing FUSE does not
            # support it.
            dw = DiskFileWriter(fd, tmppath, self)
            # It's now the responsibility of DiskFileWriter to close this fd.
            fd = None
            yield dw
        finally:
            if dw:
                dw.close()
                if dw._tmppath:
                    do_unlink(dw._tmppath)