in tough/src/editor/signed.rs [640:731]
fn target_path(
&self,
input: &Path,
outdir: &Path,
target_filename: Option<&TargetName>,
) -> Result<TargetPath> {
let outdir =
std::fs::canonicalize(outdir).context(error::AbsolutePathSnafu { path: outdir })?;
// If the caller requested a specific target filename, use that, otherwise use the filename
// component of the input path.
let target_name = if let Some(target_filename) = target_filename {
Cow::Borrowed(target_filename)
} else {
Cow::Owned(TargetName::new(
input
.file_name()
.context(error::NoFileNameSnafu { path: input })?
.to_str()
.context(error::PathUtf8Snafu { path: input })?,
)?)
};
// create a Target object using the input path.
let target_from_path =
Target::from_path(input).context(error::TargetFromPathSnafu { path: input })?;
// Use the file name to see if a target exists in the repo
// with that name. If so...
let repo_targets = &self.targets();
let repo_target = repo_targets
.get(&target_name)
.context(error::PathIsNotTargetSnafu { path: input })?;
// compare the hashes of the target from the repo and the target we just created. They
// should match, or we alert the caller; if target replacement is intended, it should
// happen earlier, in RepositoryEditor.
ensure!(
target_from_path.hashes.sha256 == repo_target.hashes.sha256,
error::HashMismatchSnafu {
context: "target",
calculated: hex::encode(target_from_path.hashes.sha256),
expected: hex::encode(&repo_target.hashes.sha256),
}
);
let dest = if self.consistent_snapshot() {
outdir.join(format!(
"{}.{}",
hex::encode(&target_from_path.hashes.sha256),
target_name.resolved()
))
} else {
outdir.join(target_name.resolved())
};
// Return the target path, using the `TargetPath` enum that represents the type of file
// that already exists at that path (if any)
if !dest.exists() {
return Ok(TargetPath::New { path: dest });
}
// If we're using consistent snapshots, filenames include the checksum, so we know they're
// unique; if we're not, then there could be a target from another repo with the same name
// but different checksum. We can't assume such conflicts are OK, so we fail.
if !self.consistent_snapshot() {
// Use DigestAdapter to get a streaming checksum of the file without needing to hold
// its contents.
let f = fs::File::open(&dest).context(error::FileOpenSnafu { path: &dest })?;
let mut reader = DigestAdapter::sha256(
Box::new(f),
&repo_target.hashes.sha256,
Url::from_file_path(&dest)
.ok() // dump unhelpful `()` error
.context(error::FileUrlSnafu { path: &dest })?,
);
let mut dev_null = std::io::sink();
// The act of reading with the DigestAdapter verifies the checksum, assuming the read
// succeeds.
std::io::copy(&mut reader, &mut dev_null)
.context(error::FileReadSnafu { path: &dest })?;
}
let metadata =
fs::symlink_metadata(&dest).context(error::FileMetadataSnafu { path: &dest })?;
if metadata.file_type().is_file() {
Ok(TargetPath::File { path: dest })
} else if metadata.file_type().is_symlink() {
Ok(TargetPath::Symlink { path: dest })
} else {
error::InvalidFileTypeSnafu { path: dest }.fail()
}
}