blob: 2b4fb58967a5134c62a797bdf0e03018b3c91ae5 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::{
range::Range,
repo_keys,
repository::{
CopyMode, Error, FileSystemRepository, FileSystemRepositoryBuilder, RepoProvider,
RepoStorage,
},
resource::Resource,
},
anyhow::Result,
camino::{Utf8Path, Utf8PathBuf},
delivery_blob::DeliveryBlobType,
fuchsia_merkle::Hash,
futures::{future::BoxFuture, stream::BoxStream, AsyncRead},
std::{collections::BTreeSet, fmt::Debug, time::SystemTime},
tuf::{
metadata::{MetadataPath, MetadataVersion, TargetPath},
pouf::Pouf1,
repository::{
RepositoryProvider as TufRepositoryProvider, RepositoryStorage as TufRepositoryStorage,
},
},
};
#[cfg(not(target_os = "fuchsia"))]
use crate::repository::RepositorySpec;
pub struct PmRepositoryBuilder {
pm_repo_path: Utf8PathBuf,
builder: FileSystemRepositoryBuilder,
}
impl PmRepositoryBuilder {
pub fn new(pm_repo_path: Utf8PathBuf) -> Self {
let metadata_repo_path = pm_repo_path.join("repository");
let blob_repo_path = metadata_repo_path.join("blobs");
PmRepositoryBuilder {
pm_repo_path,
builder: FileSystemRepositoryBuilder::new(metadata_repo_path, blob_repo_path),
}
}
/// Select which [CopyMode] to use when copying files into the repository.
pub fn copy_mode(mut self, copy_mode: CopyMode) -> Self {
self.builder = self.builder.copy_mode(copy_mode);
self
}
/// Alias this repository to this name when this repository is registered on a target.
pub fn alias(mut self, alias: String) -> Self {
self.builder = self.builder.alias(alias);
self
}
/// alias this repository to these names when this repository is registered on a target.
pub fn aliases(mut self, aliases: impl IntoIterator<Item = String>) -> Self {
self.builder = self.builder.aliases(aliases);
self
}
/// Set the type of delivery blob to generate when copying blobs into the repository.
pub fn delivery_blob_type(mut self, delivery_blob_type: DeliveryBlobType) -> Self {
self.builder = self.builder.delivery_blob_type(delivery_blob_type);
self
}
/// Set the path to the blob repo.
pub fn blob_repo_path(mut self, blob_repo_path: Utf8PathBuf) -> Self {
self.builder = self.builder.blob_repo_path(blob_repo_path);
self
}
pub fn build(self) -> PmRepository {
PmRepository { pm_repo_path: self.pm_repo_path, repo: self.builder.build() }
}
}
/// Serve a repository from a local pm repository.
#[derive(Debug)]
pub struct PmRepository {
pm_repo_path: Utf8PathBuf,
repo: FileSystemRepository,
}
impl PmRepository {
pub fn builder(pm_repo_path: Utf8PathBuf) -> PmRepositoryBuilder {
PmRepositoryBuilder::new(pm_repo_path)
}
/// Construct a [PmRepository].
pub fn new(pm_repo_path: Utf8PathBuf) -> Self {
Self::builder(pm_repo_path).build()
}
/// Tries to return the pm repository [RepoKeys]. Returns an empty [RepoKeys] if the `keys/`
/// directory does not exist in the repository.
pub fn repo_keys(&self) -> Result<repo_keys::RepoKeys, repo_keys::ParseError> {
let keys_path = self.pm_repo_path.join("keys");
if keys_path.exists() {
repo_keys::RepoKeys::from_dir(keys_path.as_std_path())
} else {
Ok(repo_keys::RepoKeys::builder().build())
}
}
}
impl RepoProvider for PmRepository {
#[cfg(not(target_os = "fuchsia"))]
fn spec(&self) -> RepositorySpec {
RepositorySpec::Pm { path: self.pm_repo_path.clone(), aliases: self.repo.aliases().clone() }
}
fn aliases(&self) -> &BTreeSet<String> {
self.repo.aliases()
}
fn fetch_metadata_range<'a>(
&'a self,
resource_path: &str,
range: Range,
) -> BoxFuture<'a, Result<Resource, Error>> {
self.repo.fetch_metadata_range(resource_path, range)
}
fn fetch_blob_range<'a>(
&'a self,
resource_path: &str,
range: Range,
) -> BoxFuture<'a, Result<Resource, Error>> {
self.repo.fetch_blob_range(resource_path, range)
}
fn supports_watch(&self) -> bool {
self.repo.supports_watch()
}
fn watch(&self) -> Result<BoxStream<'static, ()>> {
self.repo.watch()
}
fn blob_len<'a>(&'a self, path: &str) -> BoxFuture<'a, Result<u64>> {
self.repo.blob_len(path)
}
fn blob_modification_time<'a>(
&'a self,
path: &str,
) -> BoxFuture<'a, Result<Option<SystemTime>>> {
self.repo.blob_modification_time(path)
}
}
impl TufRepositoryProvider<Pouf1> for PmRepository {
fn fetch_metadata<'a>(
&'a self,
meta_path: &MetadataPath,
version: MetadataVersion,
) -> BoxFuture<'a, tuf::Result<Box<dyn AsyncRead + Send + Unpin + 'a>>> {
self.repo.fetch_metadata(meta_path, version)
}
fn fetch_target<'a>(
&'a self,
target_path: &TargetPath,
) -> BoxFuture<'a, tuf::Result<Box<dyn AsyncRead + Send + Unpin + 'a>>> {
self.repo.fetch_target(target_path)
}
}
impl TufRepositoryStorage<Pouf1> for PmRepository {
fn store_metadata<'a>(
&'a self,
meta_path: &MetadataPath,
version: MetadataVersion,
metadata: &'a mut (dyn AsyncRead + Send + Unpin + 'a),
) -> BoxFuture<'a, tuf::Result<()>> {
self.repo.store_metadata(meta_path, version, metadata)
}
fn store_target<'a>(
&'a self,
target_path: &TargetPath,
target: &'a mut (dyn AsyncRead + Send + Unpin + 'a),
) -> BoxFuture<'a, tuf::Result<()>> {
self.repo.store_target(target_path, target)
}
}
impl RepoStorage for PmRepository {
fn store_blob<'a>(
&'a self,
hash: &Hash,
len: u64,
path: &Utf8Path,
) -> BoxFuture<'a, Result<()>> {
self.repo.store_blob(hash, len, path)
}
}