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