blobrepo: optionally verify that provided and computed entry hashes are consistent

Summary:
We know that the hashes for non-root-tree-manifests and filenodes
should always be consistent. Verify that.

Reviewed By: farnz

Differential Revision: D7704087

fbshipit-source-id: 7f6207878c5cd372b272aa6970506dd63b5a3c7c
This commit is contained in:
Siddharth Agarwal 2018-04-20 08:18:15 -07:00 committed by Facebook Github Bot
parent 9b5ee510cb
commit c652345586
7 changed files with 56 additions and 3 deletions

View File

@ -63,4 +63,7 @@ pub enum ErrorKind {
#[fail(display = "DParents failed to complete")] ParentsFailed,
#[fail(display = "Expected {} to be a manifest, found a {} instead", _0, _1)]
NotAManifest(DNodeHash, Type),
#[fail(display = "Inconsistent node hash for entry: path {}, provided: {}, computed: {}", _0,
_1, _2)]
InconsistentEntryHash(RepoPath, HgNodeHash, HgNodeHash),
}

View File

@ -36,7 +36,7 @@ use heads::Heads;
use manifoldblob::ManifoldBlob;
use memblob::EagerMemblob;
use memheads::MemHeads;
use mercurial::{HgNodeHash, HgParents};
use mercurial::{HgBlobNode, HgNodeHash, HgParents};
use mercurial_types::{Changeset, DChangesetId, DFileNodeId, DNodeHash, DParents, Entry, HgBlob,
Manifest, RepoPath, RepositoryId};
use mercurial_types::manifest;
@ -377,7 +377,6 @@ impl BlobRepo {
/// Context for uploading a Mercurial entry.
pub struct UploadHgEntry {
// XXX optionally verify that the hash actually matches the contents
/// This hash is used as the blobstore key, even if it doesn't match the hash of the
/// parents and raw content. This is done because in some cases like root tree manifests
/// in hybrid mode, Mercurial sends fake hashes.
@ -387,6 +386,8 @@ pub struct UploadHgEntry {
pub p1: Option<HgNodeHash>,
pub p2: Option<HgNodeHash>,
pub path: RepoPath,
/// Pass `true` here to verify that the hash actually matches the inputs.
pub check_nodeid: bool,
}
impl UploadHgEntry {
@ -408,13 +409,25 @@ impl UploadHgEntry {
p1,
p2,
path,
check_nodeid,
} = self;
let p1 = p1.as_ref();
let p2 = p2.as_ref();
let raw_content = raw_content.clean();
// XXX verify nodeid here
if check_nodeid {
let computed_nodeid = HgBlobNode::new(raw_content.clone(), p1, p2)
.nodeid()
.expect("raw_content must have data available");
if nodeid != computed_nodeid {
bail_err!(ErrorKind::InconsistentEntryHash(
path,
nodeid,
computed_nodeid
));
}
}
let parents = HgParents::new(p1, p2);

View File

@ -161,6 +161,7 @@ fn upload_hg_entry(
p1,
p2,
path,
check_nodeid: true,
};
upload.upload(repo).unwrap()
}

View File

@ -57,6 +57,7 @@ impl UploadableHgBlob for Filelog {
p1: self.p1,
p2: self.p2,
path: node_key.path.clone(),
check_nodeid: true,
};
upload
.upload(repo)

View File

@ -100,6 +100,9 @@ impl UploadableHgBlob for TreemanifestEntry {
p1: self.p1,
p2: self.p2,
path: node_key.path.clone(),
// The root tree manifest is expected to have the wrong hash in hybrid mode.
// XXX possibly remove this once hybrid mode is gone
check_nodeid: !node_key.path.is_root(),
};
upload.upload(repo).map(move |(_node, value)| {
(

View File

@ -179,6 +179,7 @@ fn upload_entry(
p1: p1.cloned(),
p2: p2.cloned(),
path,
check_nodeid: true,
};
upload.upload(&blobrepo)
})
@ -319,6 +320,10 @@ fn main() {
p1,
p2,
path: RepoPath::root(),
// The root tree manifest is expected to have the wrong hash in hybrid
// mode. This will probably never go away for compatibility with
// old repositories.
check_nodeid: false,
};
upload.upload(&blobrepo)
}

View File

@ -59,6 +59,33 @@ impl RepoPath {
Ok(RepoPath::FilePath(path))
}
/// Whether this path represents the root.
#[inline]
pub fn is_root(&self) -> bool {
match *self {
RepoPath::RootPath => true,
_ => false,
}
}
/// Whether this path represents a directory that isn't the root.
#[inline]
pub fn is_dir(&self) -> bool {
match *self {
RepoPath::DirectoryPath(_) => true,
_ => false,
}
}
/// Whether this path represents a file.
#[inline]
pub fn is_file(&self) -> bool {
match *self {
RepoPath::FilePath(_) => true,
_ => false,
}
}
/// Get the length of this repo path in bytes. `RepoPath::Root` has length 0.
pub fn len(&self) -> usize {
match *self {