blob: b6e9c043e4ef3da90ca794b92d96aea985f84d00 [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
Jooyung Han35edb8f2021-07-01 16:17:16 +090019use anyhow::Result;
Jooyung Han21e9b922021-06-26 04:14:16 +090020use 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.
Jooyung Han35edb8f2021-07-01 16:17:16 +090039/// Returns true always since the filler is created.
Jooyung Han21e9b922021-06-26 04:14:16 +090040fn make_size_filler(size: u64, filler_path: &Path) -> Result<bool> {
41 let partition_size = align_to_partition_size(size + 4);
42 let mut file = OpenOptions::new().create_new(true).write(true).open(filler_path)?;
43 file.set_len(partition_size - size)?;
44 file.seek(SeekFrom::End(-4))?;
45 file.write_all(&(size as i32).to_be_bytes())?;
46 Ok(true)
47}
48
49/// When passing a host APK file as a block device in a payload disk image and it is
50/// mounted via dm-verity, we need to make the device zero-padded up to 4K boundary.
Jooyung Han35edb8f2021-07-01 16:17:16 +090051/// Otherwise, integrity checks via hashtree will fail.
52/// Returns true if filler is created.
Jooyung Han21e9b922021-06-26 04:14:16 +090053fn make_zero_filler(size: u64, filler_path: &Path) -> Result<bool> {
54 let partition_size = align_to_partition_size(size);
55 if partition_size <= size {
56 return Ok(false);
57 }
58 let file = OpenOptions::new().create_new(true).write(true).open(filler_path)?;
59 file.set_len(partition_size - size)?;
60 Ok(true)
61}
62
63/// When passing a host idsig file as a block device, we don't need any filler because it is read
64/// in length-prefixed way.
Jooyung Han35edb8f2021-07-01 16:17:16 +090065/// Returns false always because the filler is not created.
Jooyung Han21e9b922021-06-26 04:14:16 +090066fn make_no_filler(_size: u64, _filler_path: &Path) -> Result<bool> {
67 Ok(false)
68}
69
70/// Creates a DiskImage with partitions:
71/// metadata: metadata
72/// microdroid-apex-0: [apex 0, size filler]
73/// microdroid-apex-1: [apex 1, size filler]
74/// ..
75/// microdroid-apk: [apk, zero filler]
76/// microdroid-apk-idsig: idsig
77pub fn make_disk_image(
78 apk_file: PathBuf,
79 idsig_file: PathBuf,
80 config_path: &str,
81 apexes: &[ApexConfig],
82 temporary_directory: &Path,
83) -> Result<DiskImage> {
84 let metadata_path = temporary_directory.join("metadata");
85 let metadata = Metadata {
86 version: 1u32,
87 apexes: apexes
88 .iter()
Jooyung Han35edb8f2021-07-01 16:17:16 +090089 .map(|apex| ApexPayload { name: apex.name.clone(), ..Default::default() })
Jooyung Han21e9b922021-06-26 04:14:16 +090090 .collect(),
91 apk: Some(ApkPayload {
Jooyung Han35edb8f2021-07-01 16:17:16 +090092 name: "apk".to_owned(),
93 payload_partition_name: "microdroid-apk".to_owned(),
94 idsig_partition_name: "microdroid-apk-idsig".to_owned(),
Jooyung Han21e9b922021-06-26 04:14:16 +090095 ..Default::default()
96 })
97 .into(),
98 payload_config_path: format!("/mnt/apk/{}", config_path),
99 ..Default::default()
100 };
101 let mut metadata_file =
102 OpenOptions::new().create_new(true).read(true).write(true).open(&metadata_path)?;
103 microdroid_metadata::write_metadata(&metadata, &mut metadata_file)?;
104
105 // put metadata at the first partition
106 let mut partitions = vec![Partition {
Jooyung Han35edb8f2021-07-01 16:17:16 +0900107 label: "metadata".to_owned(),
Jooyung Han54f422f2021-07-01 00:37:19 +0900108 paths: vec![metadata_path],
Jooyung Han21e9b922021-06-26 04:14:16 +0900109 writable: false,
110 }];
111
112 let mut filler_count = 0;
Jooyung Han21e9b922021-06-26 04:14:16 +0900113 for (i, apex) in apexes.iter().enumerate() {
114 partitions.push(make_partition(
115 format!("microdroid-apex-{}", i),
116 get_path(&apex.name)?,
Jooyung Han35edb8f2021-07-01 16:17:16 +0900117 temporary_directory,
118 &mut filler_count,
Jooyung Han21e9b922021-06-26 04:14:16 +0900119 &make_size_filler,
120 )?);
121 }
Jooyung Han21e9b922021-06-26 04:14:16 +0900122 partitions.push(make_partition(
Jooyung Han35edb8f2021-07-01 16:17:16 +0900123 "microdroid-apk".to_owned(),
124 apk_file,
125 temporary_directory,
126 &mut filler_count,
127 &make_zero_filler,
128 )?);
129 partitions.push(make_partition(
130 "microdroid-apk-idsig".to_owned(),
Jooyung Han21e9b922021-06-26 04:14:16 +0900131 idsig_file,
Jooyung Han35edb8f2021-07-01 16:17:16 +0900132 temporary_directory,
133 &mut filler_count,
Jooyung Han21e9b922021-06-26 04:14:16 +0900134 &make_no_filler,
135 )?);
136
137 Ok(DiskImage { image: None, partitions, writable: false })
138}
Jooyung Han35edb8f2021-07-01 16:17:16 +0900139
140fn make_partition(
141 label: String,
142 path: PathBuf,
143 temporary_directory: &Path,
144 filler_count: &mut u32,
145 make_filler: &dyn Fn(u64, &Path) -> Result<bool>,
146) -> Result<Partition> {
147 let filler_path = temporary_directory.join(format!("filler-{}", filler_count));
148 let size = fs::metadata(&path)?.len();
149
150 if make_filler(size, &filler_path)? {
151 *filler_count += 1;
152 Ok(Partition { label, paths: vec![path, filler_path], writable: false })
153 } else {
154 Ok(Partition { label, paths: vec![path], writable: false })
155 }
156}