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