Skip to main content

fuchsia_repo/repository/
pm.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::range::Range;
6use crate::repo_keys;
7use crate::repository::{
8    CopyMode, Error, FileSystemRepository, FileSystemRepositoryBuilder, RepoProvider, RepoStorage,
9};
10use crate::resource::Resource;
11use anyhow::Result;
12use camino::{Utf8Path, Utf8PathBuf};
13use delivery_blob::DeliveryBlobType;
14use fuchsia_merkle::Hash;
15use futures::AsyncRead;
16use futures::future::BoxFuture;
17use futures::stream::BoxStream;
18use std::collections::BTreeSet;
19use std::fmt::Debug;
20use std::time::SystemTime;
21use tuf::metadata::{MetadataPath, MetadataVersion, TargetPath};
22use tuf::pouf::Pouf1;
23use tuf::repository::{
24    RepositoryProvider as TufRepositoryProvider, RepositoryStorage as TufRepositoryStorage,
25};
26
27#[cfg(not(target_os = "fuchsia"))]
28use crate::repository::RepositorySpec;
29
30pub struct PmRepositoryBuilder {
31    pm_repo_path: Utf8PathBuf,
32    builder: FileSystemRepositoryBuilder,
33}
34
35impl PmRepositoryBuilder {
36    pub fn new(pm_repo_path: Utf8PathBuf) -> Self {
37        let metadata_repo_path = pm_repo_path.join("repository");
38        let blob_repo_path = metadata_repo_path.join("blobs");
39
40        PmRepositoryBuilder {
41            pm_repo_path,
42            builder: FileSystemRepositoryBuilder::new(metadata_repo_path, blob_repo_path),
43        }
44    }
45
46    /// Select which [CopyMode] to use when copying files into the repository.
47    pub fn copy_mode(mut self, copy_mode: CopyMode) -> Self {
48        self.builder = self.builder.copy_mode(copy_mode);
49        self
50    }
51
52    /// Alias this repository to this name when this repository is registered on a target.
53    pub fn alias(mut self, alias: String) -> Self {
54        self.builder = self.builder.alias(alias);
55        self
56    }
57
58    /// alias this repository to these names when this repository is registered on a target.
59    pub fn aliases(mut self, aliases: impl IntoIterator<Item = String>) -> Self {
60        self.builder = self.builder.aliases(aliases);
61        self
62    }
63
64    /// Set the type of delivery blob to generate when copying blobs into the repository.
65    pub fn delivery_blob_type(mut self, delivery_blob_type: DeliveryBlobType) -> Self {
66        self.builder = self.builder.delivery_blob_type(delivery_blob_type);
67        self
68    }
69
70    /// Set the path to the blob repo.
71    pub fn blob_repo_path(mut self, blob_repo_path: Utf8PathBuf) -> Self {
72        self.builder = self.builder.blob_repo_path(blob_repo_path);
73        self
74    }
75
76    pub fn build(self) -> PmRepository {
77        PmRepository { pm_repo_path: self.pm_repo_path, repo: self.builder.build() }
78    }
79}
80
81/// Serve a repository from a local pm repository.
82#[derive(Debug)]
83pub struct PmRepository {
84    pm_repo_path: Utf8PathBuf,
85    repo: FileSystemRepository,
86}
87
88impl PmRepository {
89    pub fn builder(pm_repo_path: Utf8PathBuf) -> PmRepositoryBuilder {
90        PmRepositoryBuilder::new(pm_repo_path)
91    }
92
93    /// Construct a [PmRepository].
94    pub fn new(pm_repo_path: Utf8PathBuf) -> Self {
95        Self::builder(pm_repo_path).build()
96    }
97
98    /// Tries to return the pm repository [RepoKeys]. Returns an empty [RepoKeys] if the `keys/`
99    /// directory does not exist in the repository.
100    pub fn repo_keys(&self) -> Result<repo_keys::RepoKeys, repo_keys::ParseError> {
101        let keys_path = self.pm_repo_path.join("keys");
102        if keys_path.exists() {
103            repo_keys::RepoKeys::from_dir(keys_path.as_std_path())
104        } else {
105            Ok(repo_keys::RepoKeys::builder().build())
106        }
107    }
108}
109
110impl RepoProvider for PmRepository {
111    #[cfg(not(target_os = "fuchsia"))]
112    fn spec(&self) -> RepositorySpec {
113        RepositorySpec::Pm { path: self.pm_repo_path.clone(), aliases: self.repo.aliases().clone() }
114    }
115
116    fn aliases(&self) -> &BTreeSet<String> {
117        self.repo.aliases()
118    }
119
120    fn fetch_metadata_range<'a>(
121        &'a self,
122        resource_path: &str,
123        range: Range,
124    ) -> BoxFuture<'a, Result<Resource, Error>> {
125        self.repo.fetch_metadata_range(resource_path, range)
126    }
127
128    fn fetch_blob_range<'a>(
129        &'a self,
130        resource_path: &str,
131        range: Range,
132    ) -> BoxFuture<'a, Result<Resource, Error>> {
133        self.repo.fetch_blob_range(resource_path, range)
134    }
135
136    fn supports_watch(&self) -> bool {
137        self.repo.supports_watch()
138    }
139
140    fn watch(&self) -> Result<BoxStream<'static, ()>> {
141        self.repo.watch()
142    }
143
144    fn blob_modification_time<'a>(
145        &'a self,
146        path: &str,
147    ) -> BoxFuture<'a, Result<Option<SystemTime>>> {
148        self.repo.blob_modification_time(path)
149    }
150
151    fn blob_type(&self) -> DeliveryBlobType {
152        self.repo.blob_type()
153    }
154}
155
156impl TufRepositoryProvider<Pouf1> for PmRepository {
157    fn fetch_metadata<'a>(
158        &'a self,
159        meta_path: &MetadataPath,
160        version: MetadataVersion,
161    ) -> BoxFuture<'a, tuf::Result<Box<dyn AsyncRead + Send + Unpin + 'a>>> {
162        self.repo.fetch_metadata(meta_path, version)
163    }
164
165    fn fetch_target<'a>(
166        &'a self,
167        target_path: &TargetPath,
168    ) -> BoxFuture<'a, tuf::Result<Box<dyn AsyncRead + Send + Unpin + 'a>>> {
169        self.repo.fetch_target(target_path)
170    }
171}
172
173impl TufRepositoryStorage<Pouf1> for PmRepository {
174    fn store_metadata<'a>(
175        &'a self,
176        meta_path: &MetadataPath,
177        version: MetadataVersion,
178        metadata: &'a mut (dyn AsyncRead + Send + Unpin + 'a),
179    ) -> BoxFuture<'a, tuf::Result<()>> {
180        self.repo.store_metadata(meta_path, version, metadata)
181    }
182
183    fn store_target<'a>(
184        &'a self,
185        target_path: &TargetPath,
186        target: &'a mut (dyn AsyncRead + Send + Unpin + 'a),
187    ) -> BoxFuture<'a, tuf::Result<()>> {
188        self.repo.store_target(target_path, target)
189    }
190}
191
192impl RepoStorage for PmRepository {
193    fn store_blob<'a>(
194        &'a self,
195        hash: &Hash,
196        len: u64,
197        path: &Utf8Path,
198    ) -> BoxFuture<'a, Result<()>> {
199        self.repo.store_blob(hash, len, path)
200    }
201    fn store_delivery_blob<'a>(
202        &'a self,
203        hash: &Hash,
204        path: &Utf8Path,
205        delivery_blob_type: DeliveryBlobType,
206    ) -> BoxFuture<'a, Result<()>> {
207        self.repo.store_delivery_blob(hash, path, delivery_blob_type)
208    }
209}