update_package/
lib.rs

1// Copyright 2020 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
5#![deny(missing_docs)]
6
7//! Typesafe wrappers around an "update" package.
8
9mod board;
10mod epoch;
11mod hash;
12mod image;
13pub mod images;
14pub mod manifest;
15mod name;
16mod packages;
17mod update_mode;
18mod version;
19
20pub use crate::board::VerifyBoardError;
21pub use crate::epoch::ParseEpochError;
22pub use crate::hash::HashError;
23pub use crate::image::OpenImageError;
24pub use crate::images::{
25    ImageMetadata, ImageMetadataError, ImagePackagesError, ImagePackagesManifest,
26    ImagePackagesManifestBuilder, ImagesMetadata, ResolveImagesError, VerifyError,
27    VersionedImagePackagesManifest, ZbiAndOptionalVbmetaMetadata, parse_image_packages_json,
28};
29pub use crate::name::VerifyNameError;
30pub use crate::packages::{
31    ParsePackageError, SerializePackageError, parse_packages_json, serialize_packages_json,
32};
33pub use crate::update_mode::{ParseUpdateModeError, UpdateMode};
34pub use crate::version::{ReadVersionError, SystemVersion};
35
36use fidl_fuchsia_io as fio;
37use fuchsia_hash::Hash;
38use fuchsia_url::PinnedAbsolutePackageUrl;
39
40/// An open handle to an image package.
41#[cfg(target_os = "fuchsia")]
42pub struct UpdateImagePackage {
43    proxy: fio::DirectoryProxy,
44}
45
46#[cfg(target_os = "fuchsia")]
47impl UpdateImagePackage {
48    /// Creates a new [`UpdateImagePackage`] with a given proxy.
49    pub fn new(proxy: fio::DirectoryProxy) -> Self {
50        Self { proxy }
51    }
52
53    /// Opens the image at given `path` as a resizable VMO buffer.
54    pub async fn open_image(&self, path: &str) -> Result<fidl_fuchsia_mem::Buffer, OpenImageError> {
55        image::open_from_path(&self.proxy, path).await
56    }
57}
58
59/// An open handle to an "update" package.
60#[derive(Debug)]
61pub struct UpdatePackage {
62    proxy: fio::DirectoryProxy,
63}
64
65impl UpdatePackage {
66    /// Creates a new [`UpdatePackage`] with the given proxy.
67    pub fn new(proxy: fio::DirectoryProxy) -> Self {
68        Self { proxy }
69    }
70
71    /// Verifies that the package's name/variant is "update/0".
72    pub async fn verify_name(&self) -> Result<(), VerifyNameError> {
73        name::verify(&self.proxy).await
74    }
75
76    /// Loads the image packages manifest, or determines that it is not present.
77    pub async fn images_metadata(&self) -> Result<ImagesMetadata, ImagePackagesError> {
78        images::images_metadata(&self.proxy).await
79    }
80
81    /// Verifies the board file has the given `contents`.
82    pub async fn verify_board(&self, contents: &str) -> Result<(), VerifyBoardError> {
83        board::verify_board(&self.proxy, contents).await
84    }
85
86    /// Parses the update-mode file to obtain update mode. Returns `Ok(None)` if the update-mode
87    /// file is not present in the update package.
88    pub async fn update_mode(&self) -> Result<Option<UpdateMode>, ParseUpdateModeError> {
89        update_mode::update_mode(&self.proxy).await
90    }
91
92    /// Returns the list of package urls that go in the universe of this update package.
93    pub async fn packages(&self) -> Result<Vec<PinnedAbsolutePackageUrl>, ParsePackageError> {
94        packages::packages(&self.proxy).await
95    }
96
97    /// Returns the package hash of this update package.
98    pub async fn hash(&self) -> Result<Hash, HashError> {
99        hash::hash(&self.proxy).await
100    }
101
102    /// Returns the version of this update package.
103    pub async fn version(&self) -> Result<SystemVersion, ReadVersionError> {
104        version::read_version(&self.proxy).await
105    }
106
107    /// Parses the epoch.json file to obtain the epoch. Returns `Ok(None)` if the epoch.json file
108    /// is not present in the update package.
109    pub async fn epoch(&self) -> Result<Option<u64>, ParseEpochError> {
110        epoch::epoch(&self.proxy).await
111    }
112}
113
114#[cfg(test)]
115struct TestUpdatePackage {
116    update_pkg: UpdatePackage,
117    temp_dir: tempfile::TempDir,
118}
119
120#[cfg(test)]
121impl TestUpdatePackage {
122    #[cfg(not(target_os = "fuchsia"))]
123    compile_error!(
124        "Building tests for non-fuchsia targets requires a library to serve a temp dir using the fidl_fuchsia_io::Directory protocol"
125    );
126
127    fn new() -> Self {
128        let temp_dir = tempfile::tempdir().expect("/tmp to exist");
129        let update_pkg_proxy = fuchsia_fs::directory::open_in_namespace(
130            temp_dir.path().to_str().unwrap(),
131            fio::PERM_READABLE,
132        )
133        .expect("temp dir to open");
134        Self { temp_dir, update_pkg: UpdatePackage::new(update_pkg_proxy) }
135    }
136
137    fn proxy(&self) -> &fio::DirectoryProxy {
138        &self.update_pkg.proxy
139    }
140
141    async fn add_file(self, path: impl AsRef<std::path::Path>, contents: impl AsRef<[u8]>) -> Self {
142        let path = path.as_ref();
143        match path.parent() {
144            Some(empty) if empty == std::path::Path::new("") => {}
145            None => {}
146            Some(parent) => std::fs::create_dir_all(self.temp_dir.path().join(parent)).unwrap(),
147        }
148        fuchsia_fs::file::write_in_namespace(
149            self.temp_dir.path().join(path).to_str().unwrap(),
150            contents,
151        )
152        .await
153        .expect("create test update package file");
154        self
155    }
156}
157
158#[cfg(test)]
159impl std::ops::Deref for TestUpdatePackage {
160    type Target = UpdatePackage;
161
162    fn deref(&self) -> &Self::Target {
163        &self.update_pkg
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170
171    #[fuchsia_async::run_singlethreaded(test)]
172    async fn lifecycle() {
173        let (proxy, _server_end) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
174        UpdatePackage::new(proxy);
175    }
176}