##// END OF EJS Templates
lfs: gracefully handle aborts on the server when corrupt blobs are detected...
Matt Harbison -
r37710:10e5bb96 default
parent child Browse files
Show More
@@ -152,7 +152,8 b' class local(object):'
152
152
153 realoid = sha256.hexdigest()
153 realoid = sha256.hexdigest()
154 if realoid != oid:
154 if realoid != oid:
155 raise error.Abort(_('corrupt remote lfs object: %s') % oid)
155 raise LfsCorruptionError(_('corrupt remote lfs object: %s')
156 % oid)
156
157
157 self._linktousercache(oid)
158 self._linktousercache(oid)
158
159
@@ -526,8 +527,8 b' def _deduplicate(pointers):'
526 def _verify(oid, content):
527 def _verify(oid, content):
527 realoid = hashlib.sha256(content).hexdigest()
528 realoid = hashlib.sha256(content).hexdigest()
528 if realoid != oid:
529 if realoid != oid:
529 raise error.Abort(_('detected corrupt lfs object: %s') % oid,
530 raise LfsCorruptionError(_('detected corrupt lfs object: %s') % oid,
530 hint=_('run hg verify'))
531 hint=_('run hg verify'))
531
532
532 def remote(repo, remote=None):
533 def remote(repo, remote=None):
533 """remotestore factory. return a store in _storemap depending on config
534 """remotestore factory. return a store in _storemap depending on config
@@ -573,3 +574,8 b' def remote(repo, remote=None):'
573
574
574 class LfsRemoteError(error.RevlogError):
575 class LfsRemoteError(error.RevlogError):
575 pass
576 pass
577
578 class LfsCorruptionError(error.Abort):
579 """Raised when a corrupt blob is detected, aborting an operation
580
581 It exists to allow specialized handling on the server side."""
@@ -20,6 +20,8 b' from mercurial import ('
20 pycompat,
20 pycompat,
21 )
21 )
22
22
23 from . import blobstore
24
23 HTTP_OK = hgwebcommon.HTTP_OK
25 HTTP_OK = hgwebcommon.HTTP_OK
24 HTTP_CREATED = hgwebcommon.HTTP_CREATED
26 HTTP_CREATED = hgwebcommon.HTTP_CREATED
25 HTTP_BAD_REQUEST = hgwebcommon.HTTP_BAD_REQUEST
27 HTTP_BAD_REQUEST = hgwebcommon.HTTP_BAD_REQUEST
@@ -276,13 +278,15 b' def _processbasictransfer(repo, req, res'
276 # Content-Length, but what happens if a client sends less than it
278 # Content-Length, but what happens if a client sends less than it
277 # says it will?
279 # says it will?
278
280
279 # TODO: download() will abort if the checksum fails. It should raise
281 statusmessage = hgwebcommon.statusmessage
280 # something checksum specific that can be caught here, and turned
282 try:
281 # into an http code.
283 localstore.download(oid, req.bodyfh)
282 localstore.download(oid, req.bodyfh)
284 res.status = statusmessage(HTTP_OK if existed else HTTP_CREATED)
285 except blobstore.LfsCorruptionError:
286 _logexception(req)
283
287
284 statusmessage = hgwebcommon.statusmessage
288 # XXX: Is this the right code?
285 res.status = statusmessage(HTTP_OK if existed else HTTP_CREATED)
289 res.status = statusmessage(422, b'corrupt blob')
286
290
287 # There's no payload here, but this is the header that lfs-test-server
291 # There's no payload here, but this is the header that lfs-test-server
288 # sends back. This eliminates some gratuitous test output conditionals.
292 # sends back. This eliminates some gratuitous test output conditionals.
@@ -296,9 +300,18 b' def _processbasictransfer(repo, req, res'
296 res.status = hgwebcommon.statusmessage(HTTP_OK)
300 res.status = hgwebcommon.statusmessage(HTTP_OK)
297 res.headers[b'Content-Type'] = b'application/octet-stream'
301 res.headers[b'Content-Type'] = b'application/octet-stream'
298
302
299 # TODO: figure out how to send back the file in chunks, instead of
303 try:
300 # reading the whole thing.
304 # TODO: figure out how to send back the file in chunks, instead of
301 res.setbodybytes(localstore.read(oid))
305 # reading the whole thing. (Also figure out how to send back
306 # an error status if an IOError occurs after a partial write
307 # in that case. Here, everything is read before starting.)
308 res.setbodybytes(localstore.read(oid))
309 except blobstore.LfsCorruptionError:
310 _logexception(req)
311
312 # XXX: Is this the right code?
313 res.status = hgwebcommon.statusmessage(422, b'corrupt blob')
314 res.setbodybytes(b'')
302
315
303 return True
316 return True
304 else:
317 else:
@@ -236,7 +236,7 b' Test a bad checksum sent by the client i'
236 $ hg -R client push http://localhost:$HGPORT1
236 $ hg -R client push http://localhost:$HGPORT1
237 pushing to http://localhost:$HGPORT1/
237 pushing to http://localhost:$HGPORT1/
238 searching for changes
238 searching for changes
239 abort: HTTP error: HTTP Error 500: Internal Server Error (oid=b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c, action=upload)!
239 abort: HTTP error: HTTP Error 422: corrupt blob (oid=b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c, action=upload)!
240 [255]
240 [255]
241
241
242 $ echo 'test lfs file' > server/lfs3.bin
242 $ echo 'test lfs file' > server/lfs3.bin
@@ -255,7 +255,7 b' Test a checksum failure during the proce'
255
255
256 $ hg --config lfs.url=http://localhost:$HGPORT1/.git/info/lfs \
256 $ hg --config lfs.url=http://localhost:$HGPORT1/.git/info/lfs \
257 > -R client update -r tip
257 > -R client update -r tip
258 abort: HTTP error: HTTP Error 500: Internal Server Error (oid=276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d, action=download)!
258 abort: HTTP error: HTTP Error 422: corrupt blob (oid=276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d, action=download)!
259 [255]
259 [255]
260
260
261 $ $PYTHON $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
261 $ $PYTHON $RUNTESTDIR/killdaemons.py $DAEMON_PIDS
@@ -279,14 +279,14 b' Test a checksum failure during the proce'
279 $LOCALIP - - [$LOGDATE$] "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
279 $LOCALIP - - [$LOGDATE$] "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
280 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
280 $LOCALIP - - [$LOGDATE$] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
281 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
281 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
282 $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c HTTP/1.1" 500 - (glob)
282 $LOCALIP - - [$LOGDATE$] "PUT /.hg/lfs/objects/b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c HTTP/1.1" 422 - (glob)
283 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
283 $LOCALIP - - [$LOGDATE$] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
284 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D392c05922088bacf8e68a6939b480017afbf245d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
284 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D392c05922088bacf8e68a6939b480017afbf245d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
285 $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=525251863cad618e55d483555f3d00a2ca99597e&heads=506bf3d83f78c54b89e81c6411adee19fdf02156+525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
285 $LOCALIP - - [$LOGDATE$] "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&bundlecaps=HG20%2Cbundle2%3DHG20%250Abookmarks%250Achangegroup%253D01%252C02%252C03%250Adigests%253Dmd5%252Csha1%252Csha512%250Aerror%253Dabort%252Cunsupportedcontent%252Cpushraced%252Cpushkey%250Ahgtagsfnodes%250Alistkeys%250Aphases%253Dheads%250Apushkey%250Aremote-changegroup%253Dhttp%252Chttps%250Arev-branch-cache%250Astream%253Dv2&cg=1&common=525251863cad618e55d483555f3d00a2ca99597e&heads=506bf3d83f78c54b89e81c6411adee19fdf02156+525251863cad618e55d483555f3d00a2ca99597e&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull (glob)
286 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
286 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
287 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 500 - (glob)
287 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 500 - (glob)
288 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
288 $LOCALIP - - [$LOGDATE$] "POST /.git/info/lfs/objects/batch HTTP/1.1" 200 - (glob)
289 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 500 - (glob)
289 $LOCALIP - - [$LOGDATE$] "GET /.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d HTTP/1.1" 422 - (glob)
290
290
291 $ grep -v ' File "' $TESTTMP/errors.log
291 $ grep -v ' File "' $TESTTMP/errors.log
292 $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.git/info/lfs/objects/batch': (glob)
292 $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.git/info/lfs/objects/batch': (glob)
@@ -301,20 +301,13 b' Test a checksum failure during the proce'
301 $LOCALIP - - [$ERRDATE$] HG error: raise IOError(errno.EIO, '%s: I/O error' % oid) (glob)
301 $LOCALIP - - [$ERRDATE$] HG error: raise IOError(errno.EIO, '%s: I/O error' % oid) (glob)
302 $LOCALIP - - [$ERRDATE$] HG error: IOError: [Errno 5] b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c: I/O error (glob)
302 $LOCALIP - - [$ERRDATE$] HG error: IOError: [Errno 5] b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c: I/O error (glob)
303 $LOCALIP - - [$ERRDATE$] HG error: (glob)
303 $LOCALIP - - [$ERRDATE$] HG error: (glob)
304 $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/.hg/lfs/objects/b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c': (glob)
304 $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.hg/lfs/objects/b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c': (glob)
305 Traceback (most recent call last):
305 $LOCALIP - - [$ERRDATE$] HG error: Traceback (most recent call last): (glob)
306 self.do_write()
306 $LOCALIP - - [$ERRDATE$] HG error: localstore.download(oid, req.bodyfh) (glob)
307 self.do_hgweb()
307 $LOCALIP - - [$ERRDATE$] HG error: super(badstore, self).download(oid, src) (glob)
308 for chunk in self.server.application(env, self._start_response):
308 $LOCALIP - - [$ERRDATE$] HG error: % oid) (glob)
309 for r in self._runwsgi(req, res, repo):
309 $LOCALIP - - [$ERRDATE$] HG error: LfsCorruptionError: corrupt remote lfs object: b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c (glob)
310 rctx, req, res, self.check_perm)
310 $LOCALIP - - [$ERRDATE$] HG error: (glob)
311 return func(*(args + a), **kw)
312 lambda perm:
313 localstore.download(oid, req.bodyfh)
314 super(badstore, self).download(oid, src)
315 raise error.Abort(_('corrupt remote lfs object: %s') % oid)
316 Abort: corrupt remote lfs object: b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c
317
318 $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d': (glob)
311 $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d': (glob)
319 Traceback (most recent call last):
312 Traceback (most recent call last):
320 self.do_write()
313 self.do_write()
@@ -329,18 +322,11 b' Test a checksum failure during the proce'
329 raise IOError(errno.EIO, '%s: I/O error' % oid)
322 raise IOError(errno.EIO, '%s: I/O error' % oid)
330 IOError: [Errno 5] 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d: I/O error
323 IOError: [Errno 5] 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d: I/O error
331
324
332 $LOCALIP - - [$ERRDATE$] Exception happened during processing request '/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d': (glob)
325 $LOCALIP - - [$ERRDATE$] HG error: Exception happened while processing request '/.hg/lfs/objects/276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d': (glob)
333 Traceback (most recent call last):
326 $LOCALIP - - [$ERRDATE$] HG error: Traceback (most recent call last): (glob)
334 self.do_write()
327 $LOCALIP - - [$ERRDATE$] HG error: res.setbodybytes(localstore.read(oid)) (glob)
335 self.do_hgweb()
328 $LOCALIP - - [$ERRDATE$] HG error: blob = self._read(self.vfs, oid, verify) (glob)
336 for chunk in self.server.application(env, self._start_response):
329 $LOCALIP - - [$ERRDATE$] HG error: blobstore._verify(oid, 'dummy content') (glob)
337 for r in self._runwsgi(req, res, repo):
330 $LOCALIP - - [$ERRDATE$] HG error: hint=_('run hg verify')) (glob)
338 rctx, req, res, self.check_perm)
331 $LOCALIP - - [$ERRDATE$] HG error: LfsCorruptionError: detected corrupt lfs object: 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d (glob)
339 return func(*(args + a), **kw)
332 $LOCALIP - - [$ERRDATE$] HG error: (glob)
340 lambda perm:
341 res.setbodybytes(localstore.read(oid))
342 blob = self._read(self.vfs, oid, verify)
343 blobstore._verify(oid, 'dummy content')
344 hint=_('run hg verify'))
345 Abort: detected corrupt lfs object: 276f73cfd75f9fb519810df5f5d96d6594ca2521abd86cbcd92122f7d51a1f3d
346
General Comments 0
You need to be logged in to leave comments. Login now