| // 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. |
| |
| use { |
| fidl::endpoints::{ClientEnd, Proxy, ServerEnd}, |
| fidl_fuchsia_io as fio, fuchsia_async as fasync, |
| isolated_swd::{cache::Cache, omaha, pkgfs::Pkgfs, resolver::Resolver, updater::Updater}, |
| std::sync::Arc, |
| thiserror::Error, |
| }; |
| |
| #[derive(Debug, Error)] |
| pub enum UpdateError { |
| #[error("error launching pkgfs")] |
| PkgfsLaunchError(#[source] anyhow::Error), |
| |
| #[error("error launching pkg-cache")] |
| PkgCacheLaunchError(#[source] anyhow::Error), |
| |
| #[error("error launching pkg-resolver")] |
| PkgResolverLaunchError(#[source] anyhow::Error), |
| |
| #[error("error launching system-updater and installing update")] |
| InstallError(#[source] anyhow::Error), |
| |
| #[error("error setting up resources")] |
| FidlError(#[source] fidl::Error), |
| |
| #[error("IO error occurred")] |
| IoError(#[source] std::io::Error), |
| } |
| |
| pub struct OmahaConfig { |
| /// The app_id to use for Omaha. |
| pub app_id: String, |
| /// The URL of the Omaha server. |
| pub server_url: String, |
| } |
| |
| /// Installs all packages and writes the Fuchsia ZBI from the latest build on the given channel. |
| /// |
| /// The following conditions are expected to be met: |
| /// * The `isolated-swd` package (//src/sys/pkg/lib/isolated-ota:isolated-swd) must be available |
| /// for use - it contains all of the SWD binaries and their manifests. |
| /// * Network services (fuchsia.net.name.Lookup and fuchsia.posix.socket.Provider) are available in |
| /// the /svc/ directory. |
| /// * The pkgsvr binary should be in the current namespace at /pkg/bin/pkgsvr. |
| /// |
| /// If successful, a reboot should be the only thing necessary to boot Fuchsia. |
| /// |
| /// # Arguments |
| /// * `blobfs` - The root directory of the blobfs we are installing to. The blobfs must work, but |
| /// there is no requirement on the state of any blobs (i.e. an empty blobfs, or one with missing or |
| /// corrupt blobs is ok) |
| /// * `paver_connector` - a directory which contains a service file named fuchsia.paver.Paver |
| /// * `repository_config_file` - A folder containing a json-serialized fidl_fuchsia_pkg_ext::RepositoryConfigs file |
| /// * `ssl_cert_dir` - A folder containg the root SSL certificates for use by the package resolver. |
| /// * `channel_name` - The channel to update from. |
| /// * `board_name` - Board name to pass to the system updater. |
| /// * `version` - Version to report as the current installed version. |
| /// * `omaha_cfg` - The |OmahaConfig| to use for Omaha. If None, the update will not use Omaha to |
| /// determine the updater URL. |
| pub async fn download_and_apply_update( |
| blobfs: ClientEnd<fio::DirectoryMarker>, |
| paver_connector: ClientEnd<fio::DirectoryMarker>, |
| repository_config_file: std::fs::File, |
| ssl_cert_dir: std::fs::File, |
| channel_name: &str, |
| board_name: &str, |
| version: &str, |
| omaha_cfg: Option<OmahaConfig>, |
| ) -> Result<(), UpdateError> { |
| let blobfs_proxy = fio::DirectoryProxy::from_channel( |
| fasync::Channel::from_channel(blobfs.into_channel()) |
| .map_err(|e| UpdateError::FidlError(fidl::Error::AsyncChannel(e)))?, |
| ); |
| |
| let pkgfs = |
| Pkgfs::launch(clone_blobfs(&blobfs_proxy)?).map_err(UpdateError::PkgfsLaunchError)?; |
| let cache = Arc::new( |
| Cache::launch(&pkgfs, clone_blobfs(&blobfs_proxy)?) |
| .map_err(UpdateError::PkgCacheLaunchError)?, |
| ); |
| let resolver = Arc::new( |
| Resolver::launch( |
| &pkgfs, |
| Arc::clone(&cache), |
| repository_config_file, |
| channel_name, |
| ssl_cert_dir, |
| ) |
| .map_err(UpdateError::PkgResolverLaunchError)?, |
| ); |
| |
| let blobfs_clone = clone_blobfs(&blobfs_proxy)?; |
| if let Some(cfg) = omaha_cfg { |
| let () = omaha::install_update( |
| blobfs_clone, |
| paver_connector, |
| cache, |
| resolver, |
| board_name.to_owned(), |
| cfg.app_id, |
| cfg.server_url, |
| version.to_owned(), |
| channel_name.to_owned(), |
| ) |
| .await |
| .map_err(UpdateError::InstallError)?; |
| } else { |
| let mut updater = |
| Updater::launch(blobfs_clone, paver_connector, cache, resolver, &board_name) |
| .await |
| .map_err(UpdateError::InstallError)?; |
| |
| let () = updater.install_update(None).await.map_err(UpdateError::InstallError)?; |
| } |
| Ok(()) |
| } |
| |
| fn clone_blobfs( |
| blobfs_proxy: &fio::DirectoryProxy, |
| ) -> Result<ClientEnd<fio::DirectoryMarker>, UpdateError> { |
| let (blobfs_clone, remote) = fidl::endpoints::create_endpoints::<fio::DirectoryMarker>() |
| .map_err(UpdateError::FidlError)?; |
| blobfs_proxy |
| .clone(fio::OpenFlags::CLONE_SAME_RIGHTS, ServerEnd::from(remote.into_channel())) |
| .map_err(UpdateError::FidlError)?; |
| Ok(blobfs_clone) |
| } |