blob: 19a6d9f1224925aac27176af1d2d450b301a9109 [file] [log] [blame]
Jooyung Han21e9b922021-06-26 04:14:16 +09001// Copyright 2021, 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//! Payload disk image
16
17use crate::composite::align_to_partition_size;
18
19use anyhow::{Error, Result};
20use microdroid_metadata::{ApexPayload, ApkPayload, Metadata};
21use microdroid_payload_config::ApexConfig;
22use std::fs;
23use std::fs::OpenOptions;
24use std::io::{Seek, SeekFrom, Write};
25use std::path::{Path, PathBuf};
26use std::process::Command;
27use vmconfig::{DiskImage, Partition};
28
29// TODO(b/191601801): look up /apex/apex-info-list.xml
30fn get_path(package_name: &str) -> Result<PathBuf> {
31 let output = Command::new("pm").arg("path").arg(package_name).output()?;
32 let output = String::from_utf8(output.stdout)?;
33 Ok(PathBuf::from(output.strip_prefix("package:").unwrap().trim()))
34}
35
36/// When passing a host APEX file as a block device in a payload disk image,
37/// the size of the original file needs to be stored in the last 4 bytes so that
38/// other programs (e.g. apexd) can read it as a zip.
39fn make_size_filler(size: u64, filler_path: &Path) -> Result<bool> {
40 let partition_size = align_to_partition_size(size + 4);
41 let mut file = OpenOptions::new().create_new(true).write(true).open(filler_path)?;
42 file.set_len(partition_size - size)?;
43 file.seek(SeekFrom::End(-4))?;
44 file.write_all(&(size as i32).to_be_bytes())?;
45 Ok(true)
46}
47
48/// When passing a host APK file as a block device in a payload disk image and it is
49/// mounted via dm-verity, we need to make the device zero-padded up to 4K boundary.
50/// Otherwise, intergrity checks via hashtree will fail.
51fn make_zero_filler(size: u64, filler_path: &Path) -> Result<bool> {
52 let partition_size = align_to_partition_size(size);
53 if partition_size <= size {
54 return Ok(false);
55 }
56 let file = OpenOptions::new().create_new(true).write(true).open(filler_path)?;
57 file.set_len(partition_size - size)?;
58 Ok(true)
59}
60
61/// When passing a host idsig file as a block device, we don't need any filler because it is read
62/// in length-prefixed way.
63fn make_no_filler(_size: u64, _filler_path: &Path) -> Result<bool> {
64 Ok(false)
65}
66
67/// Creates a DiskImage with partitions:
68/// metadata: metadata
69/// microdroid-apex-0: [apex 0, size filler]
70/// microdroid-apex-1: [apex 1, size filler]
71/// ..
72/// microdroid-apk: [apk, zero filler]
73/// microdroid-apk-idsig: idsig
74pub fn make_disk_image(
75 apk_file: PathBuf,
76 idsig_file: PathBuf,
77 config_path: &str,
78 apexes: &[ApexConfig],
79 temporary_directory: &Path,
80) -> Result<DiskImage> {
81 let metadata_path = temporary_directory.join("metadata");
82 let metadata = Metadata {
83 version: 1u32,
84 apexes: apexes
85 .iter()
86 .map(|apex| ApexPayload { name: String::from(&apex.name), ..Default::default() })
87 .collect(),
88 apk: Some(ApkPayload {
89 name: String::from("apk"),
90 payload_partition_name: String::from("microdroid-apk"),
91 idsig_partition_name: String::from("microdroid-apk-idsig"),
92 ..Default::default()
93 })
94 .into(),
95 payload_config_path: format!("/mnt/apk/{}", config_path),
96 ..Default::default()
97 };
98 let mut metadata_file =
99 OpenOptions::new().create_new(true).read(true).write(true).open(&metadata_path)?;
100 microdroid_metadata::write_metadata(&metadata, &mut metadata_file)?;
101
102 // put metadata at the first partition
103 let mut partitions = vec![Partition {
104 label: String::from("metadata"),
105 path: Some(metadata_path),
106 paths: vec![],
107 writable: false,
108 }];
109
110 let mut filler_count = 0;
111 let mut make_partition = |label: String,
112 path: PathBuf,
113 make_filler: &dyn Fn(u64, &Path) -> Result<bool, Error>|
114 -> Result<Partition> {
115 let filler_path = temporary_directory.join(format!("filler-{}", filler_count));
116 let size = fs::metadata(&path)?.len();
117
118 if make_filler(size, &filler_path)? {
119 filler_count += 1;
120 Ok(Partition { label, path: None, paths: vec![path, filler_path], writable: false })
121 } else {
122 Ok(Partition { label, path: Some(path), paths: vec![], writable: false })
123 }
124 };
125 for (i, apex) in apexes.iter().enumerate() {
126 partitions.push(make_partition(
127 format!("microdroid-apex-{}", i),
128 get_path(&apex.name)?,
129 &make_size_filler,
130 )?);
131 }
132 partitions.push(make_partition(String::from("microdroid-apk"), apk_file, &make_zero_filler)?);
133 partitions.push(make_partition(
134 String::from("microdroid-apk-idsig"),
135 idsig_file,
136 &make_no_filler,
137 )?);
138
139 Ok(DiskImage { image: None, partitions, writable: false })
140}