update_package/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Copyright 2020 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.

#![deny(missing_docs)]

//! Typesafe wrappers around an "update" package.

mod board;
mod epoch;
mod hash;
mod image;
pub mod images;
mod name;
mod packages;
mod update_mode;
mod version;

pub use crate::board::VerifyBoardError;
pub use crate::epoch::ParseEpochError;
pub use crate::hash::HashError;
pub use crate::image::OpenImageError;
pub use crate::images::{
    parse_image_packages_json, ImageMetadata, ImageMetadataError, ImagePackagesError,
    ImagePackagesManifest, ImagePackagesManifestBuilder, ImagesMetadata, ResolveImagesError,
    VerifyError, VersionedImagePackagesManifest, ZbiAndOptionalVbmetaMetadata,
};
pub use crate::name::VerifyNameError;
pub use crate::packages::{
    parse_packages_json, serialize_packages_json, ParsePackageError, SerializePackageError,
};
pub use crate::update_mode::{ParseUpdateModeError, UpdateMode};
pub use crate::version::{ReadVersionError, SystemVersion};

use fidl_fuchsia_io as fio;
use fuchsia_hash::Hash;
use fuchsia_url::PinnedAbsolutePackageUrl;

/// An open handle to an image package.
#[cfg(target_os = "fuchsia")]
pub struct UpdateImagePackage {
    proxy: fio::DirectoryProxy,
}

#[cfg(target_os = "fuchsia")]
impl UpdateImagePackage {
    /// Creates a new [`UpdateImagePackage`] with a given proxy.
    pub fn new(proxy: fio::DirectoryProxy) -> Self {
        Self { proxy }
    }

    /// Opens the image at given `path` as a resizable VMO buffer.
    pub async fn open_image(&self, path: &str) -> Result<fidl_fuchsia_mem::Buffer, OpenImageError> {
        image::open_from_path(&self.proxy, path).await
    }
}

/// An open handle to an "update" package.
#[derive(Debug)]
pub struct UpdatePackage {
    proxy: fio::DirectoryProxy,
}

impl UpdatePackage {
    /// Creates a new [`UpdatePackage`] with the given proxy.
    pub fn new(proxy: fio::DirectoryProxy) -> Self {
        Self { proxy }
    }

    /// Verifies that the package's name/variant is "update/0".
    pub async fn verify_name(&self) -> Result<(), VerifyNameError> {
        name::verify(&self.proxy).await
    }

    /// Loads the image packages manifest, or determines that it is not present.
    pub async fn images_metadata(&self) -> Result<ImagesMetadata, ImagePackagesError> {
        images::images_metadata(&self.proxy).await
    }

    /// Verifies the board file has the given `contents`.
    pub async fn verify_board(&self, contents: &str) -> Result<(), VerifyBoardError> {
        board::verify_board(&self.proxy, contents).await
    }

    /// Parses the update-mode file to obtain update mode. Returns `Ok(None)` if the update-mode
    /// file is not present in the update package.
    pub async fn update_mode(&self) -> Result<Option<UpdateMode>, ParseUpdateModeError> {
        update_mode::update_mode(&self.proxy).await
    }

    /// Returns the list of package urls that go in the universe of this update package.
    pub async fn packages(&self) -> Result<Vec<PinnedAbsolutePackageUrl>, ParsePackageError> {
        packages::packages(&self.proxy).await
    }

    /// Returns the package hash of this update package.
    pub async fn hash(&self) -> Result<Hash, HashError> {
        hash::hash(&self.proxy).await
    }

    /// Returns the version of this update package.
    pub async fn version(&self) -> Result<SystemVersion, ReadVersionError> {
        version::read_version(&self.proxy).await
    }

    /// Parses the epoch.json file to obtain the epoch. Returns `Ok(None)` if the epoch.json file
    /// is not present in the update package.
    pub async fn epoch(&self) -> Result<Option<u64>, ParseEpochError> {
        epoch::epoch(&self.proxy).await
    }
}

#[cfg(test)]
struct TestUpdatePackage {
    update_pkg: UpdatePackage,
    temp_dir: tempfile::TempDir,
}

#[cfg(test)]
impl TestUpdatePackage {
    #[cfg(not(target_os = "fuchsia"))]
    compile_error!("Building tests for non-fuchsia targets requires a library to serve a temp dir using the fidl_fuchsia_io::Directory protocol");

    fn new() -> Self {
        let temp_dir = tempfile::tempdir().expect("/tmp to exist");
        let update_pkg_proxy = fuchsia_fs::directory::open_in_namespace(
            temp_dir.path().to_str().unwrap(),
            fio::PERM_READABLE,
        )
        .expect("temp dir to open");
        Self { temp_dir, update_pkg: UpdatePackage::new(update_pkg_proxy) }
    }

    fn proxy(&self) -> &fio::DirectoryProxy {
        &self.update_pkg.proxy
    }

    async fn add_file(self, path: impl AsRef<std::path::Path>, contents: impl AsRef<[u8]>) -> Self {
        let path = path.as_ref();
        match path.parent() {
            Some(empty) if empty == std::path::Path::new("") => {}
            None => {}
            Some(parent) => std::fs::create_dir_all(self.temp_dir.path().join(parent)).unwrap(),
        }
        fuchsia_fs::file::write_in_namespace(
            self.temp_dir.path().join(path).to_str().unwrap(),
            contents,
        )
        .await
        .expect("create test update package file");
        self
    }
}

#[cfg(test)]
impl std::ops::Deref for TestUpdatePackage {
    type Target = UpdatePackage;

    fn deref(&self) -> &Self::Target {
        &self.update_pkg
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[fuchsia_async::run_singlethreaded(test)]
    async fn lifecycle() {
        let (proxy, _server_end) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
        UpdatePackage::new(proxy);
    }
}