Extract and rename InstanceFiles.
It seems to be complex enough to merit its own file and not have
everything effectively public.
I renamed it to InstanceStarter, since what it does is start an
instance (possibly creating/re-creating it first). And did a little
related renaming.
This is intended as a pure refactoring; no functionality changes.
Bug: 186126194
Test: adb shell apex/com.android.compos/bin/composd_cmd
Change-Id: Ib42e1c23594d436b845c28bcfe38803e119d1106
diff --git a/compos/composd/src/composd_main.rs b/compos/composd/src/composd_main.rs
index 3bd4121..3f28a81 100644
--- a/compos/composd/src/composd_main.rs
+++ b/compos/composd/src/composd_main.rs
@@ -19,6 +19,7 @@
//! them, and orchestrating trusted compilation.
mod instance_manager;
+mod instance_starter;
mod odrefresh;
mod service;
diff --git a/compos/composd/src/instance_manager.rs b/compos/composd/src/instance_manager.rs
index ad9c28a..6b36ed8 100644
--- a/compos/composd/src/instance_manager.rs
+++ b/compos/composd/src/instance_manager.rs
@@ -14,29 +14,17 @@
* limitations under the License.
*/
-//! Starts and manages instances of the CompOS VM. At most one instance should be running at
-//! a time.
+//! Manages running instances of the CompOS VM. At most one instance should be running at
+//! a time, started on demand.
-use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
- IVirtualizationService::IVirtualizationService, PartitionType::PartitionType,
-};
+use crate::instance_starter::{CompOsInstance, InstanceStarter};
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice;
use anyhow::{bail, Context, Result};
use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
-use compos_aidl_interface::binder::{ParcelFileDescriptor, Strong};
-use compos_common::compos_client::VmInstance;
-use compos_common::{
- COMPOS_DATA_ROOT, CURRENT_DIR, INSTANCE_IMAGE_FILE, PRIVATE_KEY_BLOB_FILE, PUBLIC_KEY_FILE,
-};
-use log::{info, warn};
-use std::fs;
-use std::path::{Path, PathBuf};
+use compos_aidl_interface::binder::Strong;
+use compos_common::CURRENT_DIR;
use std::sync::{Arc, Mutex, Weak};
-
-pub struct CompOsInstance {
- #[allow(dead_code)] // Keeps VirtualizationService & the VM alive
- vm_instance: VmInstance,
- service: Strong<dyn ICompOsService>,
-}
+use virtualizationservice::IVirtualizationService::IVirtualizationService;
pub struct InstanceManager {
service: Strong<dyn IVirtualizationService>,
@@ -51,7 +39,7 @@
pub fn get_running_service(&self) -> Result<Strong<dyn ICompOsService>> {
let mut state = self.state.lock().unwrap();
let instance = state.get_running_instance().context("No running instance")?;
- Ok(instance.service.clone())
+ Ok(instance.get_service())
}
pub fn start_current_instance(&self) -> Result<Arc<CompOsInstance>> {
@@ -72,137 +60,13 @@
}
fn try_start_current_instance(&self) -> Result<Arc<CompOsInstance>> {
- let instance_files = InstanceFiles::new(CURRENT_DIR);
-
- let compos_instance = instance_files.create_or_start_instance(&*self.service)?;
+ let instance_starter = InstanceStarter::new(CURRENT_DIR);
+ let compos_instance = instance_starter.create_or_start_instance(&*self.service)?;
Ok(Arc::new(compos_instance))
}
}
-struct InstanceFiles {
- instance_name: String,
- instance_root: PathBuf,
- instance_image: PathBuf,
- key_blob: PathBuf,
- public_key: PathBuf,
-}
-
-impl InstanceFiles {
- fn new(instance_name: &str) -> Self {
- let instance_root = Path::new(COMPOS_DATA_ROOT).join(instance_name);
- let instant_root_path = instance_root.as_path();
- let instance_image = instant_root_path.join(INSTANCE_IMAGE_FILE);
- let key_blob = instant_root_path.join(PRIVATE_KEY_BLOB_FILE);
- let public_key = instant_root_path.join(PUBLIC_KEY_FILE);
- Self {
- instance_name: instance_name.to_owned(),
- instance_root,
- instance_image,
- key_blob,
- public_key,
- }
- }
-
- fn create_or_start_instance(
- &self,
- service: &dyn IVirtualizationService,
- ) -> Result<CompOsInstance> {
- let compos_instance = self.start_instance();
- match compos_instance {
- Ok(_) => return compos_instance,
- Err(e) => warn!("Failed to start {}: {}", self.instance_name, e),
- }
-
- self.start_new_instance(service)
- }
-
- fn start_instance(&self) -> Result<CompOsInstance> {
- // No point even trying if the files we need aren't there.
- self.check_files_exist()?;
-
- let key_blob = fs::read(&self.key_blob).context("Reading private key blob")?;
- let public_key = fs::read(&self.public_key).context("Reading public key")?;
-
- let vm_instance = VmInstance::start(&self.instance_image).context("Starting VM")?;
- let service = vm_instance.get_service().context("Connecting to CompOS")?;
-
- if !service.verifySigningKey(&key_blob, &public_key).context("Verifying key pair")? {
- bail!("Key pair invalid");
- }
-
- // If we get this far then the instance image is valid in the current context (e.g. the
- // current set of APEXes) and the key blob can be successfully decrypted by the VM. So the
- // files have not been tampered with and we're good to go.
-
- service.initializeSigningKey(&key_blob).context("Loading signing key")?;
-
- Ok(CompOsInstance { vm_instance, service })
- }
-
- fn start_new_instance(
- &self,
- virtualization_service: &dyn IVirtualizationService,
- ) -> Result<CompOsInstance> {
- info!("Creating {} CompOs instance", self.instance_name);
-
- // Ignore failure here - the directory may already exist.
- let _ = fs::create_dir(&self.instance_root);
-
- self.create_instance_image(virtualization_service)?;
-
- let vm_instance = VmInstance::start(&self.instance_image).context("Starting VM")?;
- let service = vm_instance.get_service().context("Connecting to CompOS")?;
-
- let key_data = service.generateSigningKey().context("Generating signing key")?;
- fs::write(&self.key_blob, &key_data.keyBlob).context("Writing key blob")?;
- // TODO: Extract public key from cert
- fs::write(&self.public_key, &key_data.certificate).context("Writing public key")?;
-
- // We don't need to verify the key, since we just generated it and have it in memory.
-
- service.initializeSigningKey(&key_data.keyBlob).context("Loading signing key")?;
-
- Ok(CompOsInstance { vm_instance, service })
- }
-
- fn create_instance_image(
- &self,
- virtualization_service: &dyn IVirtualizationService,
- ) -> Result<()> {
- let instance_image = fs::OpenOptions::new()
- .create(true)
- .read(true)
- .write(true)
- .open(&self.instance_image)
- .context("Creating instance image file")?;
- let instance_image = ParcelFileDescriptor::new(instance_image);
- // TODO: Where does this number come from?
- let size = 10 * 1024 * 1024;
- virtualization_service
- .initializeWritablePartition(&instance_image, size, PartitionType::ANDROID_VM_INSTANCE)
- .context("Writing instance image file")?;
- Ok(())
- }
-
- fn check_files_exist(&self) -> Result<()> {
- if !self.instance_root.is_dir() {
- bail!("Directory {} not found", self.instance_root.display())
- };
- Self::check_file_exists(&self.instance_image)?;
- Self::check_file_exists(&self.key_blob)?;
- Self::check_file_exists(&self.public_key)?;
- Ok(())
- }
-
- fn check_file_exists(file: &Path) -> Result<()> {
- if !file.is_file() {
- bail!("File {} not found", file.display())
- };
- Ok(())
- }
-}
-
// Ensures we only run one instance at a time.
// Valid states:
// Starting: is_starting is true, running_instance is None.
diff --git a/compos/composd/src/instance_starter.rs b/compos/composd/src/instance_starter.rs
new file mode 100644
index 0000000..5352250
--- /dev/null
+++ b/compos/composd/src/instance_starter.rs
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Responsible for validating and starting an existing instance of the CompOS VM, or creating and
+//! starting a new instance if necessary.
+
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+ IVirtualizationService::IVirtualizationService, PartitionType::PartitionType,
+};
+use anyhow::{bail, Context, Result};
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
+use compos_aidl_interface::binder::{ParcelFileDescriptor, Strong};
+use compos_common::compos_client::VmInstance;
+use compos_common::{
+ COMPOS_DATA_ROOT, INSTANCE_IMAGE_FILE, PRIVATE_KEY_BLOB_FILE, PUBLIC_KEY_FILE,
+};
+use log::{info, warn};
+use std::fs;
+use std::path::{Path, PathBuf};
+
+pub struct CompOsInstance {
+ #[allow(dead_code)] // Keeps VirtualizationService & the VM alive
+ vm_instance: VmInstance,
+ service: Strong<dyn ICompOsService>,
+}
+
+impl CompOsInstance {
+ pub fn get_service(&self) -> Strong<dyn ICompOsService> {
+ self.service.clone()
+ }
+}
+
+pub struct InstanceStarter {
+ instance_name: String,
+ instance_root: PathBuf,
+ instance_image: PathBuf,
+ key_blob: PathBuf,
+ public_key: PathBuf,
+}
+
+impl InstanceStarter {
+ pub fn new(instance_name: &str) -> Self {
+ let instance_root = Path::new(COMPOS_DATA_ROOT).join(instance_name);
+ let instant_root_path = instance_root.as_path();
+ let instance_image = instant_root_path.join(INSTANCE_IMAGE_FILE);
+ let key_blob = instant_root_path.join(PRIVATE_KEY_BLOB_FILE);
+ let public_key = instant_root_path.join(PUBLIC_KEY_FILE);
+ Self {
+ instance_name: instance_name.to_owned(),
+ instance_root,
+ instance_image,
+ key_blob,
+ public_key,
+ }
+ }
+
+ pub fn create_or_start_instance(
+ &self,
+ service: &dyn IVirtualizationService,
+ ) -> Result<CompOsInstance> {
+ let compos_instance = self.start_existing_instance();
+ match compos_instance {
+ Ok(_) => return compos_instance,
+ Err(e) => warn!("Failed to start {}: {}", self.instance_name, e),
+ }
+
+ self.start_new_instance(service)
+ }
+
+ fn start_existing_instance(&self) -> Result<CompOsInstance> {
+ // No point even trying if the files we need aren't there.
+ self.check_files_exist()?;
+
+ let key_blob = fs::read(&self.key_blob).context("Reading private key blob")?;
+ let public_key = fs::read(&self.public_key).context("Reading public key")?;
+
+ let vm_instance = VmInstance::start(&self.instance_image).context("Starting VM")?;
+ let service = vm_instance.get_service().context("Connecting to CompOS")?;
+
+ if !service.verifySigningKey(&key_blob, &public_key).context("Verifying key pair")? {
+ bail!("Key pair invalid");
+ }
+
+ // If we get this far then the instance image is valid in the current context (e.g. the
+ // current set of APEXes) and the key blob can be successfully decrypted by the VM. So the
+ // files have not been tampered with and we're good to go.
+
+ service.initializeSigningKey(&key_blob).context("Loading signing key")?;
+
+ Ok(CompOsInstance { vm_instance, service })
+ }
+
+ fn start_new_instance(
+ &self,
+ virtualization_service: &dyn IVirtualizationService,
+ ) -> Result<CompOsInstance> {
+ info!("Creating {} CompOs instance", self.instance_name);
+
+ // Ignore failure here - the directory may already exist.
+ let _ = fs::create_dir(&self.instance_root);
+
+ self.create_instance_image(virtualization_service)?;
+
+ let vm_instance = VmInstance::start(&self.instance_image).context("Starting VM")?;
+ let service = vm_instance.get_service().context("Connecting to CompOS")?;
+
+ let key_data = service.generateSigningKey().context("Generating signing key")?;
+ fs::write(&self.key_blob, &key_data.keyBlob).context("Writing key blob")?;
+ // TODO: Extract public key from cert
+ fs::write(&self.public_key, &key_data.certificate).context("Writing public key")?;
+
+ // We don't need to verify the key, since we just generated it and have it in memory.
+
+ service.initializeSigningKey(&key_data.keyBlob).context("Loading signing key")?;
+
+ Ok(CompOsInstance { vm_instance, service })
+ }
+
+ fn create_instance_image(
+ &self,
+ virtualization_service: &dyn IVirtualizationService,
+ ) -> Result<()> {
+ let instance_image = fs::OpenOptions::new()
+ .create(true)
+ .read(true)
+ .write(true)
+ .open(&self.instance_image)
+ .context("Creating instance image file")?;
+ let instance_image = ParcelFileDescriptor::new(instance_image);
+ // TODO: Where does this number come from?
+ let size = 10 * 1024 * 1024;
+ virtualization_service
+ .initializeWritablePartition(&instance_image, size, PartitionType::ANDROID_VM_INSTANCE)
+ .context("Writing instance image file")?;
+ Ok(())
+ }
+
+ fn check_files_exist(&self) -> Result<()> {
+ if !self.instance_root.is_dir() {
+ bail!("Directory {} not found", self.instance_root.display())
+ };
+ Self::check_file_exists(&self.instance_image)?;
+ Self::check_file_exists(&self.key_blob)?;
+ Self::check_file_exists(&self.public_key)?;
+ Ok(())
+ }
+
+ fn check_file_exists(file: &Path) -> Result<()> {
+ if !file.is_file() {
+ bail!("File {} not found", file.display())
+ };
+ Ok(())
+ }
+}