blob: 8c02281c2c5fca96ba80f6383c28675210a9204b [file] [log] [blame]
Victor Hsiehe8137e32022-02-11 22:14:12 +00001/*
2 * Copyright (C) 2022 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
17use log::error;
18use std::convert::TryInto;
19use std::io;
20use std::path::PathBuf;
21use std::sync::Mutex;
22
23use crate::file::{
24 ChunkBuffer, EagerChunkReader, ReadByChunk, RemoteFileReader, RemoteMerkleTreeReader,
25 VirtFdService,
26};
27use crate::fsverity::{merkle_tree_size, VerifiedFileReader};
28
29enum FileInfo {
30 ByPathUnderDirFd(i32, PathBuf),
31 ByFd(i32),
32}
33
34type Reader = VerifiedFileReader<RemoteFileReader, EagerChunkReader>;
35
36/// A lazily created read-only file that is verified against the given fs-verity digest.
37///
38/// The main purpose of this struct is to wrap and construct `VerifiedFileReader` lazily.
39pub struct LazyVerifiedReadonlyFile {
40 expected_digest: Vec<u8>,
41
42 service: VirtFdService,
43 file_info: FileInfo,
44
45 /// A lazily instantiated reader.
46 reader: Mutex<Option<Reader>>,
47}
48
49impl LazyVerifiedReadonlyFile {
50 /// Prepare the file by a remote path, related to a remote directory FD.
51 pub fn prepare_by_path(
52 service: VirtFdService,
53 remote_dir_fd: i32,
54 remote_path: PathBuf,
55 expected_digest: Vec<u8>,
56 ) -> Self {
57 LazyVerifiedReadonlyFile {
58 service,
59 file_info: FileInfo::ByPathUnderDirFd(remote_dir_fd, remote_path),
60 expected_digest,
61 reader: Mutex::new(None),
62 }
63 }
64
65 /// Prepare the file by a remote file FD.
66 pub fn prepare_by_fd(service: VirtFdService, remote_fd: i32, expected_digest: Vec<u8>) -> Self {
67 LazyVerifiedReadonlyFile {
68 service,
69 file_info: FileInfo::ByFd(remote_fd),
70 expected_digest,
71 reader: Mutex::new(None),
72 }
73 }
74
75 fn ensure_init_then<F, T>(&self, callback: F) -> io::Result<T>
76 where
77 F: FnOnce(&Reader) -> io::Result<T>,
78 {
79 let mut reader = self.reader.lock().unwrap();
80 if reader.is_none() {
81 let remote_file = match &self.file_info {
82 FileInfo::ByPathUnderDirFd(dir_fd, related_path) => {
83 RemoteFileReader::new_by_path(self.service.clone(), *dir_fd, related_path)?
84 }
85 FileInfo::ByFd(file_fd) => RemoteFileReader::new(self.service.clone(), *file_fd),
86 };
87 let remote_fd = remote_file.get_remote_fd();
88 let file_size = self
89 .service
90 .getFileSize(remote_fd)
91 .map_err(|e| {
92 error!("Failed to get file size of remote fd {}: {}", remote_fd, e);
93 io::Error::from_raw_os_error(libc::EIO)
94 })?
95 .try_into()
96 .map_err(|e| {
97 error!("Failed convert file size: {}", e);
98 io::Error::from_raw_os_error(libc::EIO)
99 })?;
100 let instance = VerifiedFileReader::new(
101 remote_file,
102 file_size,
103 &self.expected_digest,
104 EagerChunkReader::new(
105 RemoteMerkleTreeReader::new(self.service.clone(), remote_fd),
106 merkle_tree_size(file_size),
107 )?,
108 )
109 .map_err(|e| {
110 error!("Failed instantiate a verified file reader: {}", e);
111 io::Error::from_raw_os_error(libc::EIO)
112 })?;
113 *reader = Some(instance);
114 }
115 callback(reader.as_ref().unwrap())
116 }
117
118 pub fn file_size(&self) -> io::Result<u64> {
119 self.ensure_init_then(|reader| Ok(reader.file_size))
120 }
121}
122
123impl ReadByChunk for LazyVerifiedReadonlyFile {
124 fn read_chunk(&self, chunk_index: u64, buf: &mut ChunkBuffer) -> io::Result<usize> {
125 self.ensure_init_then(|reader| reader.read_chunk(chunk_index, buf))
126 }
127}