• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 use log::error;
18 use std::convert::TryInto;
19 use std::io;
20 use std::path::PathBuf;
21 use std::sync::Mutex;
22 
23 use crate::file::{
24     ChunkBuffer, EagerChunkReader, ReadByChunk, RemoteFileReader, RemoteMerkleTreeReader,
25     VirtFdService,
26 };
27 use crate::fsverity::{merkle_tree_size, VerifiedFileReader};
28 
29 enum FileInfo {
30     ByPathUnderDirFd(i32, PathBuf),
31     ByFd(i32),
32 }
33 
34 type 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.
39 pub 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 
49 impl LazyVerifiedReadonlyFile {
50     /// Prepare the file by a remote path, related to a remote directory FD.
prepare_by_path( service: VirtFdService, remote_dir_fd: i32, remote_path: PathBuf, expected_digest: Vec<u8>, ) -> Self51     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.
prepare_by_fd(service: VirtFdService, remote_fd: i32, expected_digest: Vec<u8>) -> Self66     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 
ensure_init_then<F, T>(&self, callback: F) -> io::Result<T> where F: FnOnce(&Reader) -> io::Result<T>,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 
file_size(&self) -> io::Result<u64>118     pub fn file_size(&self) -> io::Result<u64> {
119         self.ensure_init_then(|reader| Ok(reader.file_size))
120     }
121 }
122 
123 impl ReadByChunk for LazyVerifiedReadonlyFile {
read_chunk(&self, chunk_index: u64, buf: &mut ChunkBuffer) -> io::Result<usize>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 }
128