##// END OF EJS Templates
lfs: verify lfs object content when transferring to and from the remote store...
Matt Harbison -
r35492:417e8e04 default
parent child Browse files
Show More
@@ -7,6 +7,7 b''
7 7
8 8 from __future__ import absolute_import
9 9
10 import hashlib
10 11 import json
11 12 import os
12 13 import re
@@ -99,8 +100,11 b' class local(object):'
99 100 self.cachevfs = lfsvfs(usercache)
100 101 self.ui = repo.ui
101 102
102 def write(self, oid, data):
103 def write(self, oid, data, verify=True):
103 104 """Write blob to local blobstore."""
105 if verify:
106 _verify(oid, data)
107
104 108 with self.vfs(oid, 'wb', atomictemp=True) as fp:
105 109 fp.write(data)
106 110
@@ -110,14 +114,23 b' class local(object):'
110 114 self.ui.note(_('lfs: adding %s to the usercache\n') % oid)
111 115 lfutil.link(self.vfs.join(oid), self.cachevfs.join(oid))
112 116
113 def read(self, oid):
117 def read(self, oid, verify=True):
114 118 """Read blob from local blobstore."""
115 119 if not self.vfs.exists(oid):
120 blob = self._read(self.cachevfs, oid, verify)
121 self.ui.note(_('lfs: found %s in the usercache\n') % oid)
116 122 lfutil.link(self.cachevfs.join(oid), self.vfs.join(oid))
117 self.ui.note(_('lfs: found %s in the usercache\n') % oid)
118 123 else:
119 124 self.ui.note(_('lfs: found %s in the local lfs store\n') % oid)
120 return self.vfs.read(oid)
125 blob = self._read(self.vfs, oid, verify)
126 return blob
127
128 def _read(self, vfs, oid, verify):
129 """Read blob (after verifying) from the given store"""
130 blob = vfs.read(oid)
131 if verify:
132 _verify(oid, blob)
133 return blob
121 134
122 135 def has(self, oid):
123 136 """Returns True if the local blobstore contains the requested blob,
@@ -233,6 +246,8 b' class _gitlfsremote(object):'
233 246 request = util.urlreq.request(href)
234 247 if action == 'upload':
235 248 # If uploading blobs, read data from local blobstore.
249 with localstore.vfs(oid) as fp:
250 _verifyfile(oid, fp)
236 251 request.data = filewithprogress(localstore.vfs(oid), None)
237 252 request.get_method = lambda: 'PUT'
238 253
@@ -253,7 +268,7 b' class _gitlfsremote(object):'
253 268
254 269 if action == 'download':
255 270 # If downloading blobs, store downloaded data to local blobstore
256 localstore.write(oid, response)
271 localstore.write(oid, response, verify=True)
257 272
258 273 def _batch(self, pointers, localstore, action):
259 274 if action not in ['upload', 'download']:
@@ -324,14 +339,14 b' class _dummyremote(object):'
324 339
325 340 def writebatch(self, pointers, fromstore):
326 341 for p in pointers:
327 content = fromstore.read(p.oid())
342 content = fromstore.read(p.oid(), verify=True)
328 343 with self.vfs(p.oid(), 'wb', atomictemp=True) as fp:
329 344 fp.write(content)
330 345
331 346 def readbatch(self, pointers, tostore):
332 347 for p in pointers:
333 348 content = self.vfs.read(p.oid())
334 tostore.write(p.oid(), content)
349 tostore.write(p.oid(), content, verify=True)
335 350
336 351 class _nullremote(object):
337 352 """Null store storing blobs to /dev/null."""
@@ -368,6 +383,24 b' class _promptremote(object):'
368 383 None: _promptremote,
369 384 }
370 385
386 def _verify(oid, content):
387 realoid = hashlib.sha256(content).hexdigest()
388 if realoid != oid:
389 raise error.Abort(_('detected corrupt lfs object: %s') % oid,
390 hint=_('run hg verify'))
391
392 def _verifyfile(oid, fp):
393 sha256 = hashlib.sha256()
394 while True:
395 data = fp.read(1024 * 1024)
396 if not data:
397 break
398 sha256.update(data)
399 realoid = sha256.hexdigest()
400 if realoid != oid:
401 raise error.Abort(_('detected corrupt lfs object: %s') % oid,
402 hint=_('run hg verify'))
403
371 404 def remote(repo):
372 405 """remotestore factory. return a store in _storemap depending on config"""
373 406 defaulturl = ''
@@ -54,7 +54,9 b' def readfromstore(self, text):'
54 54 if not store.has(oid):
55 55 p.filename = getattr(self, 'indexfile', None)
56 56 self.opener.lfsremoteblobstore.readbatch([p], store)
57 text = store.read(oid)
57
58 # The caller will validate the content
59 text = store.read(oid, verify=False)
58 60
59 61 # pack hg filelog metadata
60 62 hgmeta = {}
@@ -76,7 +78,7 b' def writetostore(self, text):'
76 78
77 79 # git-lfs only supports sha256
78 80 oid = hashlib.sha256(text).hexdigest()
79 self.opener.lfslocalblobstore.write(oid, text)
81 self.opener.lfslocalblobstore.write(oid, text, verify=False)
80 82
81 83 # replace contents with metadata
82 84 longoid = 'sha256:%s' % oid
@@ -105,16 +105,15 b' Clear the cache to force a download'
105 105 lfs: found 37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 in the local lfs store
106 106 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
107 107
108 Test a corrupt file download, but clear the cache first to force a download
109
110 XXX: ideally, the validation would occur before polluting the usercache and
111 local store, with a clearer error message.
108 Test a corrupt file download, but clear the cache first to force a download.
112 109
113 110 $ rm -rf `hg config lfs.usercache`
114 111 $ cp $TESTTMP/lfs-content/d1/1e/1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 blob
115 112 $ echo 'damage' > $TESTTMP/lfs-content/d1/1e/1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
116 113 $ rm ../repo1/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
117 114 $ rm ../repo1/*
115
116 XXX: suggesting `hg verify` won't help with a corrupt file on the lfs server.
118 117 $ hg --repo ../repo1 update -C tip -v
119 118 resolving manifests
120 119 getting a
@@ -123,18 +122,16 b' local store, with a clearer error messag'
123 122 lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b in the local lfs store
124 123 getting c
125 124 lfs: downloading d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 (19 bytes)
126 lfs: adding d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 to the usercache
127 lfs: processed: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
128 lfs: found d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 in the local lfs store
129 abort: integrity check failed on data/c.i:0!
125 abort: detected corrupt lfs object: d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
126 (run hg verify)
130 127 [255]
131 128
132 BUG: the corrupted blob was added to the usercache and local store
129 The corrupted blob is not added to the usercache or local store
133 130
134 $ cat ../repo1/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 | $TESTDIR/f --sha256
135 sha256=fa82ca222fc9813afad3559637960bf311170cdd80ed35287f4623eb2320a660
136 $ cat `hg config lfs.usercache`/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998 | $TESTDIR/f --sha256
137 sha256=fa82ca222fc9813afad3559637960bf311170cdd80ed35287f4623eb2320a660
131 $ test -f ../repo1/.hg/store/lfs/objects/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
132 [1]
133 $ test -f `hg config lfs.usercache`/d1/1e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
134 [1]
138 135 $ cp blob $TESTTMP/lfs-content/d1/1e/1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
139 136
140 137 Test a corrupted file upload
@@ -146,7 +143,8 b' Test a corrupted file upload'
146 143 pushing to ../repo1
147 144 searching for changes
148 145 lfs: uploading e659058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0 (17 bytes)
149 abort: HTTP error: HTTP Error 500: Internal Server Error (oid=e659058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0, action=upload)!
146 abort: detected corrupt lfs object: e659058e26b07b39d2a9c7145b3f99b41f797b6621c8076600e9cb7ee88291f0
147 (run hg verify)
150 148 [255]
151 149
152 150 Check error message when the remote missed a blob:
@@ -704,18 +704,17 b' Damaging a file required by the update d'
704 704 updating to branch default
705 705 resolving manifests
706 706 getting l
707 lfs: adding 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b to the usercache
708 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
709 abort: integrity check failed on data/l.i:3!
707 abort: detected corrupt lfs object: 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
708 (run hg verify)
710 709 [255]
711 710
712 BUG: A corrupted lfs blob either shouldn't be created after a transfer from a
713 file://remotestore, or it shouldn't be left behind.
711 A corrupted lfs blob is not transferred from a file://remotestore to the
712 usercache or local store.
714 713
715 $ cat emptycache/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
716 sha256=40f67c7e91d554db4bc500f8f62c2e40f9f61daa5b62388e577bbae26f5396ff
717 $ cat fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
718 sha256=40f67c7e91d554db4bc500f8f62c2e40f9f61daa5b62388e577bbae26f5396ff
714 $ test -f emptycache/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
715 [1]
716 $ test -f fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
717 [1]
719 718
720 719 $ hg -R fromcorrupt2 verify
721 720 checking changesets
@@ -723,14 +722,13 b" file://remotestore, or it shouldn't be l"
723 722 crosschecking files in changesets and manifests
724 723 checking files
725 724 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
726 l@4: unpacking 6f1ff1f39c11: integrity check failed on data/l.i:3
727 725 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
728 726 4 files, 5 changesets, 10 total revisions
729 3 integrity errors encountered!
727 2 integrity errors encountered!
730 728 (first damaged changeset appears to be 0)
731 729 [1]
732 730
733 BUG: push will happily send corrupt files upstream. (The alternate dummy remote
731 Corrupt local files are not sent upstream. (The alternate dummy remote
734 732 avoids the corrupt lfs object in the original remote.)
735 733
736 734 $ mkdir $TESTTMP/dummy-remote2
@@ -742,18 +740,9 b' avoids the corrupt lfs object in the ori'
742 740 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
743 741 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
744 742 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
745 5 changesets found
746 uncompressed size of bundle content:
747 997 (changelog)
748 1032 (manifests)
749 841 l
750 272 large
751 788 s
752 139 small
753 adding changesets
754 adding manifests
755 adding file changes
756 added 5 changesets with 10 changes to 4 files
743 abort: detected corrupt lfs object: 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
744 (run hg verify)
745 [255]
757 746
758 747 $ hg -R fromcorrupt2 --config lfs.url=file:///$TESTTMP/dummy-remote2 verify -v
759 748 repository uses revlog format 1
@@ -764,27 +753,21 b' avoids the corrupt lfs object in the ori'
764 753 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
765 754 l@1: unpacking 46a2f24864bc: integrity check failed on data/l.i:0
766 755 lfs: found 22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b in the local lfs store
767 l@4: unpacking 6f1ff1f39c11: integrity check failed on data/l.i:3
768 756 lfs: found 66100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e in the local lfs store
769 757 large@0: unpacking 2c531e0992ff: integrity check failed on data/large.i:0
770 758 lfs: found 89b6070915a3d573ff3599d1cda305bc5e38549b15c4847ab034169da66e1ca8 in the local lfs store
771 759 lfs: found b1a6ea88da0017a0e77db139a54618986e9a2489bee24af9fe596de9daac498c in the local lfs store
772 760 4 files, 5 changesets, 10 total revisions
773 3 integrity errors encountered!
761 2 integrity errors encountered!
774 762 (first damaged changeset appears to be 0)
775 763 [1]
776 764
777 765 $ cat $TESTTMP/dummy-remote2/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
778 sha256=40f67c7e91d554db4bc500f8f62c2e40f9f61daa5b62388e577bbae26f5396ff
766 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
779 767 $ cat fromcorrupt2/.hg/store/lfs/objects/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b | $TESTDIR/f --sha256
780 sha256=40f67c7e91d554db4bc500f8f62c2e40f9f61daa5b62388e577bbae26f5396ff
781
782 $ cat $TESTTMP/dummy-remote2/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
783 LONGER-THAN-TEN-BYTES-WILL-TRIGGER-LFS
784 damage
785 $ cat $TESTTMP/dummy-remote2/22/f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
786 RESTORE-TO-BE-LARGE
787 damage
768 sha256=22f66a3fc0b9bf3f012c814303995ec07099b3a9ce02a7af84b5970811074a3b
769 $ test -f $TESTTMP/dummy-remote2/66/100b384bf761271b407d79fc30cdd0554f3b2c5d944836e936d584b88ce88e
770 [1]
788 771
789 772 Accessing a corrupt file will complain
790 773
General Comments 0
You need to be logged in to leave comments. Login now