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 |
|
|
|
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 |
|
|
|
129 | The corrupted blob is not added to the usercache or local store | |
|
133 | 130 | |
|
134 |
$ |
|
|
135 | sha256=fa82ca222fc9813afad3559637960bf311170cdd80ed35287f4623eb2320a660 | |
|
136 |
$ |
|
|
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: |
|
|
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 |
|
|
|
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 |
$ |
|
|
716 | sha256=40f67c7e91d554db4bc500f8f62c2e40f9f61daa5b62388e577bbae26f5396ff | |
|
717 |
$ |
|
|
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 |
|
|
|
727 | 2 integrity errors encountered! | |
|
730 | 728 | (first damaged changeset appears to be 0) |
|
731 | 729 | [1] |
|
732 | 730 | |
|
733 |
|
|
|
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 |
|
|
|
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