objects md5-collisions

This patch identifies md5 collisions on objects and sends a 403
from the object server.

Credits for originating this fix are to Michael Factor.

Change-Id: I4f1b32183e2be6bbea56eaff86b9a4c7f440804a
Fix: Bug #1157454
This commit is contained in:
David Hadas 2013-03-22 09:00:40 +02:00
parent b93b1327f7
commit caa01cd81e
3 changed files with 76 additions and 3 deletions

View File

@ -42,6 +42,10 @@ class DiskFileError(SwiftException):
pass
class DiskFileCollision(SwiftException):
pass
class DiskFileNotExist(SwiftException):
pass

View File

@ -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'

View File

@ -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()