mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 16:57:49 +03:00
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:
parent
9b5ee510cb
commit
c652345586
@ -63,4 +63,7 @@ pub enum ErrorKind {
|
|||||||
#[fail(display = "DParents failed to complete")] ParentsFailed,
|
#[fail(display = "DParents failed to complete")] ParentsFailed,
|
||||||
#[fail(display = "Expected {} to be a manifest, found a {} instead", _0, _1)]
|
#[fail(display = "Expected {} to be a manifest, found a {} instead", _0, _1)]
|
||||||
NotAManifest(DNodeHash, Type),
|
NotAManifest(DNodeHash, Type),
|
||||||
|
#[fail(display = "Inconsistent node hash for entry: path {}, provided: {}, computed: {}", _0,
|
||||||
|
_1, _2)]
|
||||||
|
InconsistentEntryHash(RepoPath, HgNodeHash, HgNodeHash),
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ use heads::Heads;
|
|||||||
use manifoldblob::ManifoldBlob;
|
use manifoldblob::ManifoldBlob;
|
||||||
use memblob::EagerMemblob;
|
use memblob::EagerMemblob;
|
||||||
use memheads::MemHeads;
|
use memheads::MemHeads;
|
||||||
use mercurial::{HgNodeHash, HgParents};
|
use mercurial::{HgBlobNode, HgNodeHash, HgParents};
|
||||||
use mercurial_types::{Changeset, DChangesetId, DFileNodeId, DNodeHash, DParents, Entry, HgBlob,
|
use mercurial_types::{Changeset, DChangesetId, DFileNodeId, DNodeHash, DParents, Entry, HgBlob,
|
||||||
Manifest, RepoPath, RepositoryId};
|
Manifest, RepoPath, RepositoryId};
|
||||||
use mercurial_types::manifest;
|
use mercurial_types::manifest;
|
||||||
@ -377,7 +377,6 @@ impl BlobRepo {
|
|||||||
|
|
||||||
/// Context for uploading a Mercurial entry.
|
/// Context for uploading a Mercurial entry.
|
||||||
pub struct UploadHgEntry {
|
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
|
/// 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
|
/// parents and raw content. This is done because in some cases like root tree manifests
|
||||||
/// in hybrid mode, Mercurial sends fake hashes.
|
/// in hybrid mode, Mercurial sends fake hashes.
|
||||||
@ -387,6 +386,8 @@ pub struct UploadHgEntry {
|
|||||||
pub p1: Option<HgNodeHash>,
|
pub p1: Option<HgNodeHash>,
|
||||||
pub p2: Option<HgNodeHash>,
|
pub p2: Option<HgNodeHash>,
|
||||||
pub path: RepoPath,
|
pub path: RepoPath,
|
||||||
|
/// Pass `true` here to verify that the hash actually matches the inputs.
|
||||||
|
pub check_nodeid: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UploadHgEntry {
|
impl UploadHgEntry {
|
||||||
@ -408,13 +409,25 @@ impl UploadHgEntry {
|
|||||||
p1,
|
p1,
|
||||||
p2,
|
p2,
|
||||||
path,
|
path,
|
||||||
|
check_nodeid,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let p1 = p1.as_ref();
|
let p1 = p1.as_ref();
|
||||||
let p2 = p2.as_ref();
|
let p2 = p2.as_ref();
|
||||||
let raw_content = raw_content.clean();
|
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);
|
let parents = HgParents::new(p1, p2);
|
||||||
|
|
||||||
|
@ -161,6 +161,7 @@ fn upload_hg_entry(
|
|||||||
p1,
|
p1,
|
||||||
p2,
|
p2,
|
||||||
path,
|
path,
|
||||||
|
check_nodeid: true,
|
||||||
};
|
};
|
||||||
upload.upload(repo).unwrap()
|
upload.upload(repo).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,7 @@ impl UploadableHgBlob for Filelog {
|
|||||||
p1: self.p1,
|
p1: self.p1,
|
||||||
p2: self.p2,
|
p2: self.p2,
|
||||||
path: node_key.path.clone(),
|
path: node_key.path.clone(),
|
||||||
|
check_nodeid: true,
|
||||||
};
|
};
|
||||||
upload
|
upload
|
||||||
.upload(repo)
|
.upload(repo)
|
||||||
|
@ -100,6 +100,9 @@ impl UploadableHgBlob for TreemanifestEntry {
|
|||||||
p1: self.p1,
|
p1: self.p1,
|
||||||
p2: self.p2,
|
p2: self.p2,
|
||||||
path: node_key.path.clone(),
|
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)| {
|
upload.upload(repo).map(move |(_node, value)| {
|
||||||
(
|
(
|
||||||
|
@ -179,6 +179,7 @@ fn upload_entry(
|
|||||||
p1: p1.cloned(),
|
p1: p1.cloned(),
|
||||||
p2: p2.cloned(),
|
p2: p2.cloned(),
|
||||||
path,
|
path,
|
||||||
|
check_nodeid: true,
|
||||||
};
|
};
|
||||||
upload.upload(&blobrepo)
|
upload.upload(&blobrepo)
|
||||||
})
|
})
|
||||||
@ -319,6 +320,10 @@ fn main() {
|
|||||||
p1,
|
p1,
|
||||||
p2,
|
p2,
|
||||||
path: RepoPath::root(),
|
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)
|
upload.upload(&blobrepo)
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,33 @@ impl RepoPath {
|
|||||||
Ok(RepoPath::FilePath(path))
|
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.
|
/// Get the length of this repo path in bytes. `RepoPath::Root` has length 0.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match *self {
|
match *self {
|
||||||
|
Loading…
Reference in New Issue
Block a user