--- a/hgext/lfs/blobstore.py Mon Dec 04 21:41:04 2017 -0500
+++ b/hgext/lfs/blobstore.py Fri Nov 17 00:06:45 2017 -0500
@@ -7,6 +7,7 @@
from __future__ import absolute_import
+import hashlib
import json
import os
import re
@@ -99,8 +100,11 @@
self.cachevfs = lfsvfs(usercache)
self.ui = repo.ui
- def write(self, oid, data):
+ def write(self, oid, data, verify=True):
"""Write blob to local blobstore."""
+ if verify:
+ _verify(oid, data)
+
with self.vfs(oid, 'wb', atomictemp=True) as fp:
fp.write(data)
@@ -110,14 +114,23 @@
self.ui.note(_('lfs: adding %s to the usercache\n') % oid)
lfutil.link(self.vfs.join(oid), self.cachevfs.join(oid))
- def read(self, oid):
+ def read(self, oid, verify=True):
"""Read blob from local blobstore."""
if not self.vfs.exists(oid):
+ blob = self._read(self.cachevfs, oid, verify)
+ self.ui.note(_('lfs: found %s in the usercache\n') % oid)
lfutil.link(self.cachevfs.join(oid), self.vfs.join(oid))
- self.ui.note(_('lfs: found %s in the usercache\n') % oid)
else:
self.ui.note(_('lfs: found %s in the local lfs store\n') % oid)
- return self.vfs.read(oid)
+ blob = self._read(self.vfs, oid, verify)
+ return blob
+
+ def _read(self, vfs, oid, verify):
+ """Read blob (after verifying) from the given store"""
+ blob = vfs.read(oid)
+ if verify:
+ _verify(oid, blob)
+ return blob
def has(self, oid):
"""Returns True if the local blobstore contains the requested blob,
@@ -233,6 +246,8 @@
request = util.urlreq.request(href)
if action == 'upload':
# If uploading blobs, read data from local blobstore.
+ with localstore.vfs(oid) as fp:
+ _verifyfile(oid, fp)
request.data = filewithprogress(localstore.vfs(oid), None)
request.get_method = lambda: 'PUT'
@@ -253,7 +268,7 @@
if action == 'download':
# If downloading blobs, store downloaded data to local blobstore
- localstore.write(oid, response)
+ localstore.write(oid, response, verify=True)
def _batch(self, pointers, localstore, action):
if action not in ['upload', 'download']:
@@ -324,14 +339,14 @@
def writebatch(self, pointers, fromstore):
for p in pointers:
- content = fromstore.read(p.oid())
+ content = fromstore.read(p.oid(), verify=True)
with self.vfs(p.oid(), 'wb', atomictemp=True) as fp:
fp.write(content)
def readbatch(self, pointers, tostore):
for p in pointers:
content = self.vfs.read(p.oid())
- tostore.write(p.oid(), content)
+ tostore.write(p.oid(), content, verify=True)
class _nullremote(object):
"""Null store storing blobs to /dev/null."""
@@ -368,6 +383,24 @@
None: _promptremote,
}
+def _verify(oid, content):
+ realoid = hashlib.sha256(content).hexdigest()
+ if realoid != oid:
+ raise error.Abort(_('detected corrupt lfs object: %s') % oid,
+ hint=_('run hg verify'))
+
+def _verifyfile(oid, fp):
+ sha256 = hashlib.sha256()
+ while True:
+ data = fp.read(1024 * 1024)
+ if not data:
+ break
+ sha256.update(data)
+ realoid = sha256.hexdigest()
+ if realoid != oid:
+ raise error.Abort(_('detected corrupt lfs object: %s') % oid,
+ hint=_('run hg verify'))
+
def remote(repo):
"""remotestore factory. return a store in _storemap depending on config"""
defaulturl = ''