diff --git a/swift/common/exceptions.py b/swift/common/exceptions.py index 625d6dd217..219b03de75 100644 --- a/swift/common/exceptions.py +++ b/swift/common/exceptions.py @@ -42,6 +42,10 @@ class DiskFileError(SwiftException): pass +class DiskFileCollision(SwiftException): + pass + + class DiskFileNotExist(SwiftException): pass diff --git a/swift/obj/server.py b/swift/obj/server.py index fc23ea2f80..03fdcff53d 100644 --- a/swift/obj/server.py +++ b/swift/obj/server.py @@ -38,7 +38,7 @@ 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 + DiskFileNotExist, DiskFileCollision from swift.obj.replicator import tpool_reraise, invalidate_hash, \ quarantine_renamer, get_hashes from swift.common.http import is_success @@ -46,7 +46,7 @@ from swift.common.swob import HTTPAccepted, HTTPBadRequest, HTTPCreated, \ HTTPInternalServerError, HTTPNoContent, HTTPNotFound, HTTPNotModified, \ HTTPPreconditionFailed, HTTPRequestTimeout, HTTPUnprocessableEntity, \ HTTPClientDisconnect, HTTPMethodNotAllowed, Request, Response, UTC, \ - HTTPInsufficientStorage, multi_range_iterator + HTTPInsufficientStorage, HTTPForbidden, multi_range_iterator DATADIR = 'objects' @@ -105,6 +105,7 @@ class DiskFile(object): :param keep_data_fp: if True, don't close the fp, otherwise close it :param disk_chunk_size: size of chunks on file reads :param iter_hook: called when __iter__ returns a chunk + :raises DiskFileCollision: on md5 collision """ def __init__(self, path, device, partition, account, container, obj, @@ -155,6 +156,14 @@ class DiskFile(object): if key.lower() not in DISALLOWED_HEADERS: del self.metadata[key] self.metadata.update(read_metadata(mfp)) + if 'name' in self.metadata: + if self.metadata['name'] != self.name: + self.logger.error(_('Client path %(client)s does not match ' + 'path stored in object metadata %(meta)s'), + {'client': self.name, + 'meta': self.metadata['name']}) + raise DiskFileCollision('Client path does not match path ' + 'stored in object metadata') def __iter__(self): """Returns an iterator over the data file.""" @@ -926,6 +935,8 @@ class ObjectController(object): res = HTTPMethodNotAllowed() else: res = method(req) + except DiskFileCollision: + res = HTTPForbidden(request=req) except (Exception, Timeout): self.logger.exception(_( 'ERROR __call__ error with %(method)s' diff --git a/test/unit/obj/test_server.py b/test/unit/obj/test_server.py index 1584aa3f4b..7c924198cd 100755 --- a/test/unit/obj/test_server.py +++ b/test/unit/obj/test_server.py @@ -153,7 +153,6 @@ class TestDiskFile(unittest.TestCase): hook_call_count[0] += 1 df = self._get_data_file(fsize=65, csize=8, iter_hook=hook) - print repr(df.__dict__) for _ in df: pass @@ -1416,6 +1415,65 @@ class TestObjectController(unittest.TestCase): self.assertEquals(errbuf.getvalue(), '') self.assertEquals(outbuf.getvalue()[:4], '405 ') + def my_check(*args): + return False + def my_storage_directory(*args): + return self.testdir+'/collide' + _storage_directory = object_server.storage_directory + _check = object_server.check_object_creation + try: + object_server.storage_directory = my_storage_directory + object_server.check_object_creation = my_check + inbuf = StringIO() + errbuf = StringIO() + outbuf = StringIO() + self.object_controller.__call__({'REQUEST_METHOD': 'PUT', + 'SCRIPT_NAME': '', + 'PATH_INFO': '/sda1/p/a/c/o', + 'SERVER_NAME': '127.0.0.1', + 'SERVER_PORT': '8080', + 'SERVER_PROTOCOL': 'HTTP/1.0', + 'CONTENT_LENGTH': '0', + 'CONTENT_TYPE': 'text/html', + 'HTTP_X_TIMESTAMP': 1.2, + 'wsgi.version': (1, 0), + 'wsgi.url_scheme': 'http', + 'wsgi.input': inbuf, + 'wsgi.errors': errbuf, + 'wsgi.multithread': False, + 'wsgi.multiprocess': False, + 'wsgi.run_once': False}, + start_response) + self.assertEquals(errbuf.getvalue(), '') + self.assertEquals(outbuf.getvalue()[:4], '201 ') + + inbuf = StringIO() + errbuf = StringIO() + outbuf = StringIO() + self.object_controller.__call__({'REQUEST_METHOD': 'PUT', + 'SCRIPT_NAME': '', + 'PATH_INFO': '/sda1/q/b/d/x', + 'SERVER_NAME': '127.0.0.1', + 'SERVER_PORT': '8080', + 'SERVER_PROTOCOL': 'HTTP/1.0', + 'CONTENT_LENGTH': '0', + 'CONTENT_TYPE': 'text/html', + 'HTTP_X_TIMESTAMP': 1.3, + 'wsgi.version': (1, 0), + 'wsgi.url_scheme': 'http', + 'wsgi.input': inbuf, + 'wsgi.errors': errbuf, + 'wsgi.multithread': False, + 'wsgi.multiprocess': False, + 'wsgi.run_once': False}, + start_response) + self.assertEquals(errbuf.getvalue(), '') + self.assertEquals(outbuf.getvalue()[:4], '403 ') + + finally: + object_server.storage_directory = _storage_directory + object_server.check_object_creation = _check + def test_invalid_method_doesnt_exist(self): errbuf = StringIO() outbuf = StringIO()