blob: 073e0441106fa6c048fdf11ed272f3e27ce3c0ee [file] [log] [blame]
Inseob Kimc0886c22021-12-13 17:41:24 +09001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! Rust bindgen interface for FSVerity Metadata file (.fsv_meta)
18use authfs_fsverity_metadata_bindgen::{
19 fsverity_metadata_header, FSVERITY_SIGNATURE_TYPE_NONE, FSVERITY_SIGNATURE_TYPE_PKCS7,
20 FSVERITY_SIGNATURE_TYPE_RAW,
21};
22
23use std::cmp::min;
24use std::os::unix::fs::MetadataExt;
25
26/// Structure for parsed metadata.
27pub struct FSVerityMetadata {
28 /// Header for the metadata.
29 pub header: fsverity_metadata_header,
30
31 /// Optional signature for the metadata.
32 pub signature: Option<Vec<u8>>,
33
34 metadata_file: File,
35
36 merkle_tree_offset: u64,
37}
38
39impl FSVerityMetadata {
40 /// Read the raw Merkle tree from the metadata, if it exists. The API semantics is similar to a
41 /// regular pread(2), and may not return full requested buffer.
42 pub fn read_merkle_tree(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
Victor Hsieh35dfa1e2022-01-12 17:03:35 -080043 let file_size = self.metadata_file.metadata()?.size();
Inseob Kimc0886c22021-12-13 17:41:24 +090044 let start = self.merkle_tree_offset + offset;
Victor Hsieh35dfa1e2022-01-12 17:03:35 -080045 let end = min(file_size, start + buf.len() as u64);
Inseob Kimc0886c22021-12-13 17:41:24 +090046 let read_size = (end - start) as usize;
47 debug_assert!(read_size <= buf.len());
Victor Hsieh35dfa1e2022-01-12 17:03:35 -080048 if read_size == 0 {
49 Ok(0)
50 } else {
51 self.metadata_file.read_exact_at(&mut buf[..read_size], start)?;
52 Ok(read_size)
53 }
Inseob Kimc0886c22021-12-13 17:41:24 +090054 }
55}
56
57use std::ffi::OsString;
58use std::fs::File;
59use std::io::{self, Read, Seek};
60use std::mem::{size_of, zeroed};
61use std::os::unix::fs::FileExt;
62use std::path::{Path, PathBuf};
63use std::slice::from_raw_parts_mut;
64
65/// Common block and page size in Linux.
66pub const CHUNK_SIZE: u64 = authfs_fsverity_metadata_bindgen::CHUNK_SIZE;
67
68/// Derive a path of metadata for a given path.
69/// e.g. "system/framework/foo.jar" -> "system/framework/foo.jar.fsv_meta"
70pub fn get_fsverity_metadata_path(path: &Path) -> PathBuf {
71 let mut os_string: OsString = path.into();
72 os_string.push(".fsv_meta");
73 os_string.into()
74}
75
76/// Parse metadata from given file, and returns a structure for the metadata.
77pub fn parse_fsverity_metadata(mut metadata_file: File) -> io::Result<Box<FSVerityMetadata>> {
78 let header_size = size_of::<fsverity_metadata_header>();
79
80 // SAFETY: the header doesn't include any pointers
81 let header: fsverity_metadata_header = unsafe {
82 let mut header: fsverity_metadata_header = zeroed();
83 let buffer = from_raw_parts_mut(
84 &mut header as *mut fsverity_metadata_header as *mut u8,
85 header_size,
86 );
87 metadata_file.read_exact(buffer)?;
88
89 // TODO(inseob): This doesn't seem ideal. Maybe we can consider nom?
90 header.version = u32::from_le(header.version);
91 header.descriptor.data_size = u64::from_le(header.descriptor.data_size);
92 header.signature_type = u32::from_le(header.signature_type);
93 header.signature_size = u32::from_le(header.signature_size);
94 header
95 };
96
97 if header.version != 1 {
98 return Err(io::Error::new(io::ErrorKind::Other, "unsupported metadata version"));
99 }
100
101 let signature = match header.signature_type {
102 FSVERITY_SIGNATURE_TYPE_NONE => None,
103 FSVERITY_SIGNATURE_TYPE_PKCS7 | FSVERITY_SIGNATURE_TYPE_RAW => {
104 // TODO: unpad pkcs7?
105 let mut buf = vec![0u8; header.signature_size as usize];
106 metadata_file.read_exact(&mut buf)?;
107 Some(buf)
108 }
109 _ => return Err(io::Error::new(io::ErrorKind::Other, "unknown signature type")),
110 };
111
112 // merkle tree is at the next 4K boundary
113 let merkle_tree_offset =
114 (metadata_file.stream_position()? + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
115
116 Ok(Box::new(FSVerityMetadata { header, signature, metadata_file, merkle_tree_offset }))
117}