blob: a4e72dee4e2f48d882bdaea5272d2613813f7122 [file] [log] [blame]
Alice Wang0547e862023-04-18 09:32:26 +00001// Copyright 2023, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Functions to process partitions.
16
17use crate::file::clone_file;
18use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
19 PartitionType::PartitionType,
20};
21use binder::{self, ExceptionCode, ParcelFileDescriptor, Status};
22use disk::QcowFile;
23use std::io::{Error, ErrorKind, Write};
24
25/// crosvm requires all partitions to be a multiple of 4KiB.
26const PARTITION_GRANULE_BYTES: u64 = 4096;
27
28/// Initialize an empty partition image of the given size to be used as a writable partition.
29pub fn init_writable_partition(
30 image_fd: &ParcelFileDescriptor,
31 size_bytes: i64,
32 partition_type: PartitionType,
33) -> binder::Result<()> {
34 let size_bytes = size_bytes.try_into().map_err(|e| {
35 Status::new_exception_str(
36 ExceptionCode::ILLEGAL_ARGUMENT,
37 Some(format!("Invalid size {}: {:?}", size_bytes, e)),
38 )
39 })?;
40 let size_bytes = round_up(size_bytes, PARTITION_GRANULE_BYTES);
41 let image = clone_file(image_fd)?;
42 // initialize the file. Any data in the file will be erased.
43 image.set_len(0).map_err(|e| {
44 Status::new_service_specific_error_str(-1, Some(format!("Failed to reset a file: {:?}", e)))
45 })?;
46 let mut part = QcowFile::new(image, size_bytes).map_err(|e| {
47 Status::new_service_specific_error_str(
48 -1,
49 Some(format!("Failed to create QCOW2 image: {:?}", e)),
50 )
51 })?;
52
53 match partition_type {
54 PartitionType::RAW => Ok(()),
55 PartitionType::ANDROID_VM_INSTANCE => format_as_android_vm_instance(&mut part),
56 PartitionType::ENCRYPTEDSTORE => format_as_encryptedstore(&mut part),
57 _ => Err(Error::new(
58 ErrorKind::Unsupported,
59 format!("Unsupported partition type {:?}", partition_type),
60 )),
61 }
62 .map_err(|e| {
63 Status::new_service_specific_error_str(
64 -1,
65 Some(format!("Failed to initialize partition as {:?}: {:?}", partition_type, e)),
66 )
67 })
68}
69
70fn round_up(input: u64, granule: u64) -> u64 {
71 if granule == 0 {
72 return input;
73 }
74 // If the input is absurdly large we round down instead of up; it's going to fail anyway.
75 let result = input.checked_add(granule - 1).unwrap_or(input);
76 (result / granule) * granule
77}
78
79fn format_as_android_vm_instance(part: &mut dyn Write) -> std::io::Result<()> {
80 const ANDROID_VM_INSTANCE_MAGIC: &str = "Android-VM-instance";
81 const ANDROID_VM_INSTANCE_VERSION: u16 = 1;
82
83 part.write_all(ANDROID_VM_INSTANCE_MAGIC.as_bytes())?;
84 part.write_all(&ANDROID_VM_INSTANCE_VERSION.to_le_bytes())?;
85 part.flush()
86}
87
88fn format_as_encryptedstore(part: &mut dyn Write) -> std::io::Result<()> {
89 const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
90
91 part.write_all(UNFORMATTED_STORAGE_MAGIC.as_bytes())?;
92 part.flush()
93}