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
// 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.

#![allow(clippy::let_unit_value)]

mod cache_packages;
mod errors;
mod path_hash_mapping;
mod system_image;

pub use crate::{
    cache_packages::CachePackages,
    errors::{
        AllowListError, CachePackagesInitError, PathHashMappingError, StaticPackagesInitError,
    },
    path_hash_mapping::{Bootfs, PathHashMapping, StaticPackages},
    system_image::{ExecutabilityRestrictions, SystemImage},
};

static PKGFS_BOOT_ARG_KEY: &str = "zircon.system.pkgfs.cmd";
static PKGFS_BOOT_ARG_VALUE_PREFIX: &str = "bin/pkgsvr+";

pub async fn get_system_image_hash(
    args: &fidl_fuchsia_boot::ArgumentsProxy,
) -> Result<fuchsia_hash::Hash, SystemImageHashError> {
    let hash = args
        .get_string(PKGFS_BOOT_ARG_KEY)
        .await
        .map_err(SystemImageHashError::Fidl)?
        .ok_or(SystemImageHashError::MissingValue)?;
    let hash = hash
        .strip_prefix(PKGFS_BOOT_ARG_VALUE_PREFIX)
        .ok_or_else(|| SystemImageHashError::BadPrefix(hash.clone()))?;
    hash.parse().map_err(SystemImageHashError::BadHash)
}

#[derive(Debug, thiserror::Error)]
pub enum SystemImageHashError {
    #[error("fidl error calling fuchsia.boot/Arguments.GetString")]
    Fidl(#[source] fidl::Error),

    #[error("boot args have no value for key {}", PKGFS_BOOT_ARG_KEY)]
    MissingValue,

    #[error(
        "boot arg for key {} does not start with {}: {0:?}",
        PKGFS_BOOT_ARG_KEY,
        PKGFS_BOOT_ARG_VALUE_PREFIX
    )]
    BadPrefix(String),

    #[error("boot arg for key {} has invalid hash {0:?}", PKGFS_BOOT_ARG_KEY)]
    BadHash(#[source] fuchsia_hash::ParseHashError),
}

#[cfg(test)]
mod test_get_system_image_hash {
    use {
        super::*, assert_matches::assert_matches, fuchsia_async as fasync,
        mock_boot_arguments::MockBootArgumentsService, std::collections::HashMap, std::sync::Arc,
    };

    #[fasync::run_singlethreaded(test)]
    async fn missing_value() {
        let mock =
            MockBootArgumentsService::new(HashMap::from([(PKGFS_BOOT_ARG_KEY.to_string(), None)]));
        let (proxy, stream) =
            fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_boot::ArgumentsMarker>()
                .unwrap();
        fasync::Task::spawn(Arc::new(mock).handle_request_stream(stream)).detach();

        assert_matches!(
            get_system_image_hash(&proxy).await,
            Err(SystemImageHashError::MissingValue)
        );
    }

    #[fasync::run_singlethreaded(test)]
    async fn bad_prefix() {
        let mock = MockBootArgumentsService::new(HashMap::from([(
            PKGFS_BOOT_ARG_KEY.to_string(),
            Some("bad-prefix".to_string()),
        )]));
        let (proxy, stream) =
            fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_boot::ArgumentsMarker>()
                .unwrap();
        fasync::Task::spawn(Arc::new(mock).handle_request_stream(stream)).detach();

        assert_matches!(
            get_system_image_hash(&proxy).await,
            Err(SystemImageHashError::BadPrefix(prefix)) if prefix == "bad-prefix"
        );
    }

    #[fasync::run_singlethreaded(test)]
    async fn bad_hash() {
        let mock = MockBootArgumentsService::new(HashMap::from([(
            PKGFS_BOOT_ARG_KEY.to_string(),
            Some("bin/pkgsvr+bad-hash".to_string()),
        )]));
        let (proxy, stream) =
            fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_boot::ArgumentsMarker>()
                .unwrap();
        fasync::Task::spawn(Arc::new(mock).handle_request_stream(stream)).detach();

        assert_matches!(get_system_image_hash(&proxy).await, Err(SystemImageHashError::BadHash(_)));
    }

    #[fasync::run_singlethreaded(test)]
    async fn success() {
        let mock = MockBootArgumentsService::new(HashMap::from([(
            PKGFS_BOOT_ARG_KEY.to_string(),
            Some(
                "bin/pkgsvr+0000000000000000000000000000000000000000000000000000000000000000"
                    .to_string(),
            ),
        )]));
        let (proxy, stream) =
            fidl::endpoints::create_proxy_and_stream::<fidl_fuchsia_boot::ArgumentsMarker>()
                .unwrap();
        fasync::Task::spawn(Arc::new(mock).handle_request_stream(stream)).detach();

        assert_eq!(get_system_image_hash(&proxy).await.unwrap(), fuchsia_hash::Hash::from([0; 32]));
    }
}