def mkdirs(path):
    """
    Ensures the path is a directory or makes it if not. Errors if the path
    exists but is a file or on permissions failure.

    :param path: path to create
    """
    if not os.path.isdir(path):
        try:
            os.makedirs(path)
        except OSError, err:
            if err.errno != errno.EEXIST or not os.path.isdir(path):
                raise def normalize_timestamp(timestamp):
    """
    Format a timestamp (string or numeric) into a standardized
    xxxxxxxxxx.xxxxx format.

    :param timestamp: unix timestamp
    :returns: normalized timestamp as a string
    """
    return "%016.05f" % (float(timestamp)) def public(func):
    """
    Decorator to declare which methods are publicly accessible as HTTP
    requests

    :param func: function to make public
    """
    func.publicly_accessible = True

    @functools.wraps(func)
    def wrapped(*a, **kw):
        return func(*a, **kw)
    return wrapped def storage_directory(datadir, partition, hash):
    """
    Get the storage directory

    :param datadir: Base data directory
    :param partition: Partition
    :param hash: Account, container or object hash
    :returns: Storage directory
    """
    return os.path.join(datadir, str(partition), hash[-3:], hash) def hash_path(account, container=None, object=None, raw_digest=False):
    """
    Get the connonical hash for an account/container/object

    :param account: Account
    :param container: Container
    :param object: Object
    :param raw_digest: If True, return the raw version rather than a hex digest
    :returns: hash string
    """
    if object and not container:
        raise ValueError('container is required if object is provided')
    paths = [account]
    if container:
        paths.append(container)
    if object:
        paths.append(object)
    if raw_digest:
        return md5('/' + '/'.join(paths) + HASH_PATH_SUFFIX).digest()
    else:
        return md5('/' + '/'.join(paths) + HASH_PATH_SUFFIX).hexdigest() def renamer(old, new):
    """
    Attempt to fix / hide race conditions like empty object directories
    being removed by backend processes during uploads, by retrying.

    :param old: old path to be renamed
    :param new: new path to be renamed to
    """
    try:
        mkdirs(os.path.dirname(new))
        os.rename(old, new)
    except OSError:
        mkdirs(os.path.dirname(new))
        os.rename(old, new) def fallocate(fd, size):
    """
    Pre-allocate disk space for a file.

    :param fd: file descriptor
    :param size: size to allocate (in bytes)
    """
    global _sys_fallocate
    if _sys_fallocate is None:
        _sys_fallocate = FallocateWrapper()
    if size < 0:
        size = 0
    # 1 means "FALLOC_FL_KEEP_SIZE", which means it pre-allocates invisibly
    ret = _sys_fallocate(fd, 1, 0, ctypes.c_uint64(size))
    err = ctypes.get_errno()
    if ret and err not in (0, errno.ENOSYS, errno.EOPNOTSUPP,
                           errno.EINVAL):
        raise OSError(err, 'Unable to fallocate(%s)' % size) def fsync(fd):
    """
    Sync modified file data and metadata to disk.

    :param fd: file descriptor
    """
    if hasattr(fcntl, 'F_FULLSYNC'):
        try:
            fcntl.fcntl(fd, fcntl.F_FULLSYNC)
        except IOError as e:
            raise OSError(e.errno, 'Unable to F_FULLSYNC(%s)' % fd)
    else:
        os.fsync(fd) def fdatasync(fd):
    """
    Sync modified file data to disk.

    :param fd: file descriptor
    """
    try:
        os.fdatasync(fd)
    except AttributeError:
        fsync(fd) def split_path(path, minsegs=1, maxsegs=None, rest_with_last=False):
    """
    Validate and split the given HTTP request path.

    **Examples**::

        ['a'] = split_path('/a')
        ['a', None] = split_path('/a', 1, 2)
        ['a', 'c'] = split_path('/a/c', 1, 2)
        ['a', 'c', 'o/r'] = split_path('/a/c/o/r', 1, 3, True)

    :param path: HTTP Request path to be split
    :param minsegs: Minimum number of segments to be extracted
    :param maxsegs: Maximum number of segments to be extracted
    :param rest_with_last: If True, trailing data will be returned as part
                           of last segment.  If False, and there is
                           trailing data, raises ValueError.
    :returns: list of segments with a length of maxsegs (non-existant
              segments will return as None)
    :raises: ValueError if given an invalid path
    """
    if not maxsegs:
        maxsegs = minsegs
    if minsegs > maxsegs:
        raise ValueError('minsegs > maxsegs: %d > %d' % (minsegs, maxsegs))
    if rest_with_last:
        segs = path.split('/', maxsegs)
        minsegs += 1
        maxsegs += 1
        count = len(segs)
        if (segs[0] or count < minsegs or count > maxsegs or
                '' in segs[1:minsegs]):
            raise ValueError('Invalid path: %s' % quote(path))
    else:
        minsegs += 1
        maxsegs += 1
        segs = path.split('/', maxsegs)
        count = len(segs)
        if (segs[0] or count < minsegs or count > maxsegs + 1 or
                '' in segs[1:minsegs] or
                (count == maxsegs + 1 and segs[maxsegs])):
            raise ValueError('Invalid path: %s' % quote(path))
    segs = segs[1:maxsegs]
    segs.extend([None] * (maxsegs - 1 - len(segs)))
    return segs def drop_buffer_cache(fd, offset, length):
    """
    Drop 'buffer' cache for the given range of the given file.

    :param fd: file descriptor
    :param offset: start offset
    :param length: length
    """
    global _posix_fadvise
    if _posix_fadvise is None:
        _posix_fadvise = load_libc_function('posix_fadvise64')
    # 4 means "POSIX_FADV_DONTNEED"
    ret = _posix_fadvise(fd, ctypes.c_uint64(offset),
                         ctypes.c_uint64(length), 4)
    if ret != 0:
        logging.warn("posix_fadvise64(%s, %s, %s, 4) -> %s"
                     % (fd, offset, length, ret)) def get_logger(conf, name=None, log_to_console=False, log_route=None,
               fmt="%(server)s %(message)s"):
    """
    Get the current system logger using config settings.

    **Log config and defaults**::

        log_facility = LOG_LOCAL0
        log_level = INFO
        log_name = swift
        log_udp_host = (disabled)
        log_udp_port = logging.handlers.SYSLOG_UDP_PORT
        log_address = /dev/log
        log_statsd_host = (disabled)
        log_statsd_port = 8125
        log_statsd_default_sample_rate = 1.0
        log_statsd_sample_rate_factor = 1.0
        log_statsd_metric_prefix = (empty-string)

    :param conf: Configuration dict to read settings from
    :param name: Name of the logger
    :param log_to_console: Add handler which writes to console on stderr
    :param log_route: Route for the logging, not emitted to the log, just used
                      to separate logging configurations
    :param fmt: Override log format
    """
    if not conf:
        conf = {}
    if name is None:
        name = conf.get('log_name', 'swift')
    if not log_route:
        log_route = name
    logger = logging.getLogger(log_route)
    logger.propagate = False
    # all new handlers will get the same formatter
    formatter = SwiftLogFormatter(fmt)

    # get_logger will only ever add one SysLog Handler to a logger
    if not hasattr(get_logger, 'handler4logger'):
        get_logger.handler4logger = {}
    if logger in get_logger.handler4logger:
        logger.removeHandler(get_logger.handler4logger[logger])

    # facility for this logger will be set by last call wins
    facility = getattr(SysLogHandler, conf.get('log_facility', 'LOG_LOCAL0'),
                       SysLogHandler.LOG_LOCAL0)
    udp_host = conf.get('log_udp_host')
    if udp_host:
        udp_port = int(conf.get('log_udp_port',
                                logging.handlers.SYSLOG_UDP_PORT))
        handler = SysLogHandler(address=(udp_host, udp_port),
                                facility=facility)
    else:
        log_address = conf.get('log_address', '/dev/log')
        try:
            handler = SysLogHandler(address=log_address, facility=facility)
        except socket.error, e:
            # Either /dev/log isn't a UNIX socket or it does not exist at all
            if e.errno not in [errno.ENOTSOCK, errno.ENOENT]:
                raise e
            handler = SysLogHandler(facility=facility)
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    get_logger.handler4logger[logger] = handler

    # setup console logging
    if log_to_console or hasattr(get_logger, 'console_handler4logger'):
        # remove pre-existing console handler for this logger
        if not hasattr(get_logger, 'console_handler4logger'):
            get_logger.console_handler4logger = {}
        if logger in get_logger.console_handler4logger:
            logger.removeHandler(get_logger.console_handler4logger[logger])

        console_handler = logging.StreamHandler(sys.__stderr__)
        console_handler.setFormatter(formatter)
        logger.addHandler(console_handler)
        get_logger.console_handler4logger[logger] = console_handler

    # set the level for the logger
    logger.setLevel(
        getattr(logging, conf.get('log_level', 'INFO').upper(), logging.INFO))

    # Setup logger with a StatsD client if so configured
    statsd_host = conf.get('log_statsd_host')
    if statsd_host:
        statsd_port = int(conf.get('log_statsd_port', 8125))
        base_prefix = conf.get('log_statsd_metric_prefix', '')
        default_sample_rate = float(conf.get(
            'log_statsd_default_sample_rate', 1))
        sample_rate_factor = float(conf.get(
            'log_statsd_sample_rate_factor', 1))
        statsd_client = StatsdClient(statsd_host, statsd_port, base_prefix,
                                     name, default_sample_rate,
                                     sample_rate_factor)
        logger.statsd_client = statsd_client
    else:
        logger.statsd_client = None

    adapted_logger = LogAdapter(logger, name)
    other_handlers = conf.get('log_custom_handlers', None)
    if other_handlers:
        log_custom_handlers = [s.strip() for s in other_handlers.split(',')
                               if s.strip()]
        for hook in log_custom_handlers:
            try:
                mod, fnc = hook.rsplit('.', 1)
                logger_hook = getattr(__import__(mod, fromlist=[fnc]), fnc)
                logger_hook(conf, name, log_to_console, log_route, fmt,
                            logger, adapted_logger)
            except (AttributeError, ImportError):
                print >>sys.stderr, 'Error calling custom handler [%s]' % hook
            except ValueError:
                print >>sys.stderr, 'Invalid custom handler format [%s]' % hook
    return adapted_logger def write_pickle(obj, dest, tmp=None, pickle_protocol=0):
    """
    Ensure that a pickle file gets written to disk.  The file
    is first written to a tmp location, ensure it is synced to disk, then
    perform a move to its final location

    :param obj: python object to be pickled
    :param dest: path of final destination file
    :param tmp: path to tmp to use, defaults to None
    :param pickle_protocol: protocol to pickle the obj with, defaults to 0
    """
    if tmp is None:
        tmp = os.path.dirname(dest)
    fd, tmppath = mkstemp(dir=tmp, suffix='.tmp')
    with os.fdopen(fd, 'wb') as fo:
        pickle.dump(obj, fo, pickle_protocol)
        fo.flush()
        os.fsync(fd)
        renamer(tmppath, dest) def config_true_value(value):
    """
    Returns True if the value is either True or a string in TRUE_VALUES.
    Returns False otherwise.
    """
    return value is True or \
        (isinstance(value, basestring) and value.lower() in TRUE_VALUES) def validate_device_partition(device, partition):
    """
    Validate that a device and a partition are valid and won't lead to
    directory traversal when used.

    :param device: device to validate
    :param partition: partition to validate
    :raises: ValueError if given an invalid device or partition
    """
    invalid_device = False
    invalid_partition = False
    if not device or '/' in device or device in ['.', '..']:
        invalid_device = True
    if not partition or '/' in partition or partition in ['.', '..']:
        invalid_partition = True

    if invalid_device:
        raise ValueError('Invalid device: %s' % quote(device or ''))
    elif invalid_partition:
        raise ValueError('Invalid partition: %s' % quote(partition or '')) def timing_stats(**dec_kwargs):
    """
    Returns a decorator that logs timing events or errors for public methods in
    swift's wsgi server controllers, based on response code.
    """
    def decorating_func(func):
        method = func.func_name

        @functools.wraps(func)
        def _timing_stats(ctrl, *args, **kwargs):
            start_time = time.time()
            resp = func(ctrl, *args, **kwargs)
            if is_success(resp.status_int) or \
                    is_redirection(resp.status_int) or \
                    resp.status_int == HTTP_NOT_FOUND:
                ctrl.logger.timing_since(method + '.timing',
                                         start_time, **dec_kwargs)
            else:
                ctrl.logger.timing_since(method + '.errors.timing',
                                         start_time, **dec_kwargs)
            return resp

        return _timing_stats
    return decorating_func def create_instance(import_str, base_class, *args, **kwargs):
    """
    Returns instance of class which imported by import path.

    :param import_str: import path of class
    :param base_class: to check the imported class is a subclass of base class
    :param \*args: positional arguments for new instance
    :param \*\*kwargs: keyword arguments for new instance
    :returns: instance of imported class which instantiated with
              arguments *args and **kwargs
    """
    object_class = import_class(import_str)
    if not issubclass(object_class, base_class):
        subclass_path = '.'.join((base_class.__module__, base_class.__name__))
        raise ValueError("Class was imported by %r is not subclass of %r." %
                         (import_str, subclass_path))
    instance = object_class(*args, **kwargs)
    return instance RESPONSE_REASONS = {
    100: ('Continue', ''),
    200: ('OK', ''),
    201: ('Created', ''),
    202: ('Accepted', 'The request is accepted for processing.'),
    204: ('No Content', ''),
    206: ('Partial Content', ''),
    301: ('Moved Permanently', 'The resource has moved permanently.'),
    302: ('Found', ''),
    304: ('Not Modified', ''),
    307: ('Temporary Redirect', 'The resource has moved temporarily.'),
    400: ('Bad Request', 'The server could not comply with the request since '
          'it is either malformed or otherwise incorrect.'),
    401: ('Unauthorized', 'This server could not verify that you are '
          'authorized to access the document you requested.'),
    402: ('Payment Required', 'Access was denied for financial reasons.'),
    403: ('Forbidden', 'Access was denied to this resource.'),
    404: ('Not Found', 'The resource could not be found.'),
    405: ('Method Not Allowed', 'The method is not allowed for this '
          'resource.'),
    406: ('Not Acceptable', 'The resource is not available in a format '
          'acceptable to your browser.'),
    408: ('Request Timeout', 'The server has waited too long for the request '
          'to be sent by the client.'),
    409: ('Conflict', 'There was a conflict when trying to complete '
          'your request.'),
    410: ('Gone', 'This resource is no longer available.'),
    411: ('Length Required', 'Content-Length header required.'),
    412: ('Precondition Failed', 'A precondition for this request was not '
          'met.'),
    413: ('Request Entity Too Large', 'The body of your request was too '
          'large for this server.'),
    414: ('Request URI Too Long', 'The request URI was too long for this '
          'server.'),
    415: ('Unsupported Media Type', 'The request media type is not '
          'supported by this server.'),
    416: ('Requested Range Not Satisfiable', 'The Range requested is not '
          'available.'),
    417: ('Expectation Failed', 'Expectation failed.'),
    422: ('Unprocessable Entity', 'Unable to process the contained '
          'instructions'),
    499: ('Client Disconnect', 'The client was disconnected during request.'),
    500: ('Internal Error', 'The server has either erred or is incapable of '
          'performing the requested operation.'),
    501: ('Not Implemented', 'The requested method is not implemented by '
          'this server.'),
    502: ('Bad Gateway', 'Bad gateway.'),
    503: ('Service Unavailable', 'The server is currently unavailable. '
          'Please try again at a later time.'),
    504: ('Gateway Timeout', 'A timeout has occurred speaking to a '
          'backend server.'),
    507: ('Insufficient Storage', 'There was not enough space to save the '
          'resource. Drive: %(drive)s'),
}
UTC = _UTC()
class _UTC(tzinfo):
class HeaderEnvironProxy(UserDict.DictMixin):
class HeaderKeyDict(dict):
class Range(object):
class Match(object):
class Accept(object):
class Request(object):
class Response(object):
class HTTPException(Response, Exception):
class StatusMap(object):
    def dst(self, dt):
    def tzname(self, dt):
def _datetime_property(header):
    def getter(self):
    def setter(self, value):
def _header_property(header):
    def getter(self):
    def setter(self, value):
def _header_int_property(header):
    def getter(self):
    def setter(self, value):
    def __init__(self, environ):
    def _normalize(self, key):
    def __getitem__(self, key):
    def __setitem__(self, key, value):
    def __contains__(self, key):
    def __delitem__(self, key):
    def keys(self):
    def __init__(self, *args, **kwargs):
    def update(self, other):
    def __getitem__(self, key):
    def __setitem__(self, key, value):
    def __contains__(self, key):
    def __delitem__(self, key):
    def get(self, key, default=None):
def _resp_status_property():
    def getter(self):
    def setter(self, value):
def _resp_body_property():
    def getter(self):
    def setter(self, value):
def _resp_etag_property():
    def getter(self):
    def setter(self, value):
def _resp_content_type_property():
    def getter(self):
    def setter(self, value):
def _resp_charset_property():
    def getter(self):
    def setter(self, value):
def _resp_app_iter_property():
    def getter(self):
    def setter(self, value):
def _req_fancy_property(cls, header, even_if_nonexistent=False):
    def getter(self):
    def setter(self, value):
    def __init__(self, headerval):
    def __str__(self):
    def ranges_for_length(self, length):
    def __init__(self, headerval):
    def __contains__(self, val):
    def __init__(self, headerval):
    def _get_types(self):
    def best_match(self, options):
    def __repr__(self):
def _req_environ_property(environ_field):
    def getter(self):
    def setter(self, value):
def _req_body_property():
    def getter(self):
    def setter(self, value):
def _host_url_property():
    def getter(self):
    def __init__(self, environ):
    def blank(cls, path, environ=None, headers=None, body=None):
    def params(self):
    def path_qs(self):
    def path(self):
    def url(self):
    def path_info_pop(self):
    def copy_get(self):
    def call_application(self, application):
        def start_response(status, headers, exc_info=None):
    def get_response(self, application):
    def split_path(self, minsegs=1, maxsegs=None, rest_with_last=False):
def content_range_header_value(start, stop, size):
def content_range_header(start, stop, size):
def multi_range_iterator(ranges, content_type, boundary, size, sub_iter_gen):
    def __init__(self, body=None, status=200, headers={}, app_iter=None,
                 request=None, conditional_response=False, **kw):
    def _prepare_for_ranges(self, ranges):
    def _response_iter(self, app_iter, body):
                            def _body_slicer(start, stop):
    def absolute_location(self):
    def __call__(self, env, start_response):
    def __init__(self, *args, **kwargs):
def wsgify(func):
        def _wsgify_self(self, env, start_response):
        def _wsgify_bare(env, start_response):
    def __getitem__(self, key): class KeyDriver(object):
    """Parent class for differetn key manager interfaces"""

    def __init__(self, conf):
        self.conf = conf

    def get_key(self, key_id):
        """
        Empty method get_key

        :param key_id: number of key in database
        :raise NotImplementedError: If driver don't have this method
        """
        raise NotImplementedError("Not implemented get_key function. "
                                  "Maybe incorrect driver")

    def get_key_id(self, account):
        """
        Empty method get_key_id

        :param account: string is name of account
        :raise NotImplementedError: If driver don't have this method
        """
        raise NotImplementedError("Not implemented get_key_id function. "
                                  "Maybe incorrect driver")

    def sync(self):
        """
        Some synchronization operations can be achieved in cases of changing
        a structure of data storage.
        """
        raise NotImplementedError()

    @staticmethod
    def validate_key_id(key_id):
        """
        Checks key_id to be a number

        :raise ValueError: if key_id isn't converted to positive number
        """
        if key_id:
            try:
                k_id = long(key_id)
            except ValueError:
                    raise ValueError("Incorrect value %r. String must include "
                                     "only digits chars." % (key_id,))
            if k_id < 0:
                    raise ValueError("Incorrect value %r. Only positive "
                                     "numbers are allowed." % (key_id,)) class CryptoDriver(object):
    """
    Base driver class that implements the functionality of encryption
    and decryption of data blocks of objects.

    :param conf: application configuration
    :param key_manager: instance of
                        swift.common.key_manager.base.KeyDriver which
                        store encryption keys
    """

    def __init__(self, conf, key_manager):
        self.conf = conf
        self.key_manager = key_manager

    def encrypted_chunk_size(self, context, original_size):
        """
        Calculates the size of the encrypted data block based on the
        size of the original data block.

        :param cotext: encryption context
        :param original_size: length of original string
        :returns: length of crypted string
        """
        chunk = " " * original_size
        return len(str(self.encrypt(context, chunk)))

    def encrypt(self, context, chunk):
        """
        Returns the encrypted data block.

        :param context: encryption context
        :param chunk: data block to encrypt
        :returns: encrypted data block
        """
        raise NotImplementedError

    def decrypt(self, context, chunk):
        """
        Returns the decrypted data block.

        :param context: encryption context
        :param chunk: data block to decrypt
        :returns: decrypted data block
        """
        raise NotImplementedError

    def encryption_context(self, key_id):
        """
        Returns the context which needed to encrypt or decrypt the
        data block.

        :param key_id: unique key identifier
        :returns: encryption context
        """
        context = {'key_id': key_id}
        return context
import cPickle as pickle
import errno
import os
import time
import traceback
from datetime import datetime
from hashlib import md5
from tempfile import mkstemp
from urllib import unquote
from contextlib import contextmanager
from xattr import getxattr, setxattr
from eventlet import sleep, Timeout, tpool
from swift.common.utils import mkdirs, normalize_timestamp, public, \
    storage_directory, hash_path, renamer, fallocate, fsync, fdatasync, \
    split_path, drop_buffer_cache, get_logger, write_pickle, \
    config_true_value, validate_device_partition, timing_stats, \
    create_instance
from swift.common.bufferedhttp import http_connect
from swift.common.constraints import check_object_creation, check_mount, \
    check_float, check_utf8
from swift.common.exceptions import ConnectionTimeout, DiskFileError, \
    DiskFileNotExist
from swift.obj.replicator import tpool_reraise, invalidate_hash, \
    quarantine_renamer, get_hashes
from swift.common.http import is_success
from swift.common.swob import HTTPAccepted, HTTPBadRequest, HTTPCreated, \
    HTTPInternalServerError, HTTPNoContent, HTTPNotFound, HTTPNotModified, \
    HTTPPreconditionFailed, HTTPRequestTimeout, HTTPUnprocessableEntity, \
    HTTPClientDisconnect, HTTPMethodNotAllowed, Request, Response, UTC, \
    HTTPInsufficientStorage, multi_range_iterator
from swift.common.key_manager.drivers.base import KeyDriver
from swift.obj.encryptor import CryptoDriver
            if file.metadata['Original-Etag'] in request.if_none_match:
                resp = HTTPNotModified(request=request)
                resp.etag = file.metadata['Original-Etag']
                file.close()
                return resp
        try:
            if_unmodified_since = request.if_unmodified_since
        except (OverflowError, ValueError):
            # catches timestamps before the epoch
            return HTTPPreconditionFailed(request=request)
        if if_unmodified_since and \
                datetime.fromtimestamp(
                    float(file.metadata['X-Timestamp']), UTC) > \
                if_unmodified_since:
            file.close()
            return HTTPPreconditionFailed(request=request)
        try:
            if_modified_since = request.if_modified_since
        except (OverflowError, ValueError):
            # catches timestamps before the epoch
            return HTTPPreconditionFailed(request=request)
        if if_modified_since and \
                datetime.fromtimestamp(
                    float(file.metadata['X-Timestamp']), UTC) < \
                if_modified_since:
            file.close()
            return HTTPNotModified(request=request)
        response = Response(app_iter=file,
                            request=request, conditional_response=True)
        response.headers['Content-Type'] = file.metadata.get(
            'Content-Type', 'application/octet-stream')
        for key, value in file.metadata.iteritems():
            if key.lower().startswith('x-object-meta-') or \
                    key.lower() in self.allowed_headers:
                response.headers[key] = value
        response.etag = file.metadata['Original-Etag']
        response.last_modified = float(file.metadata['X-Timestamp'])
        response.content_length = file_size
        if response.content_length < self.keep_cache_size and \
                (self.keep_cache_private or
                 ('X-Auth-Token' not in request.headers and
                  'X-Storage-Token' not in request.headers)):
            file.keep_cache = True
        if 'Content-Encoding' in file.metadata:
            response.content_encoding = file.metadata['Content-Encoding']
        response.headers['X-Timestamp'] = file.metadata['X-Timestamp']
        return request.get_response(response)

    @public
    @timing_stats(sample_rate=0.8)
    def HEAD(self, request):
        """Handle HTTP HEAD requests for the Swift Object Server."""
        try:
            device, partition, account, container, obj = \
                split_path(unquote(request.path), 5, 5, True)
            validate_device_partition(device, partition)
        except ValueError, err:
            resp = HTTPBadRequest(request=request)
            resp.content_type = 'text/plain'
            resp.body = str(err)
            return resp
        if self.mount_check and not check_mount(self.devices, device):
            return HTTPInsufficientStorage(drive=device, request=request)
        file = DiskFile(self.devices, device, partition, account, container,
                        obj, self.logger, disk_chunk_size=self.disk_chunk_size,
                        origin_disk_chunk_size=self.origin_disk_chunk_size,
                        crypto_driver=self.crypto_driver)
        if file.is_deleted() or file.is_expired():
            return HTTPNotFound(request=request)
        try:
            file_size = file.get_data_file_size()
        except (DiskFileError, DiskFileNotExist):
            file.quarantine()
            return HTTPNotFound(request=request)
        response = Response(request=request, conditional_response=True)
        response.headers['Content-Type'] = file.metadata.get(
            'Content-Type', 'application/octet-stream')
        for key, value in file.metadata.iteritems():
            if key.lower().startswith('x-object-meta-') or \
                    key.lower() in self.allowed_headers:
                response.headers[key] = value
        response.etag = file.metadata['Original-Etag']
        response.last_modified = float(file.metadata['X-Timestamp'])
        # Needed for container sync feature
        response.headers['X-Timestamp'] = file.metadata['X-Timestamp']
        response.content_length = file_size
        if 'Content-Encoding' in file.metadata:
            response.content_encoding = file.metadata['Content-Encoding']
        return response

    @public
    @timing_stats()
    def DELETE(self, request):
        """Handle HTTP DELETE requests for the Swift Object Server."""
        try:
            device, partition, account, container, obj = \
                split_path(unquote(request.path), 5, 5, True)
            validate_device_partition(device, partition)
        except ValueError, e:
            return HTTPBadRequest(body=str(e), request=request,
                                  content_type='text/plain')
        if 'x-timestamp' not in request.headers or \
                not check_float(request.headers['x-timestamp']):
            return HTTPBadRequest(body='Missing timestamp', request=request,
                                  content_type='text/plain')
        if self.mount_check and not check_mount(self.devices, device):
            return HTTPInsufficientStorage(drive=device, request=request)
        response_class = HTTPNoContent
        file = DiskFile(self.devices, device, partition, account, container,
                        obj, self.logger, disk_chunk_size=self.disk_chunk_size,
                        origin_disk_chunk_size=self.origin_disk_chunk_size,
                        crypto_driver=self.crypto_driver)
        if 'x-if-delete-at' in request.headers and \
                int(request.headers['x-if-delete-at']) != \
                int(file.metadata.get('X-Delete-At') or 0):
            return HTTPPreconditionFailed(
                request=request,
                body='X-If-Delete-At and X-Delete-At do not match')
        orig_timestamp = file.metadata.get('X-Timestamp')
        if file.is_deleted() or file.is_expired():

            response_class = HTTPNotFound