| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| use log::error; |
| use std::convert::TryInto; |
| use std::io; |
| use std::path::PathBuf; |
| use std::sync::Mutex; |
| |
| use crate::file::{ |
| ChunkBuffer, EagerChunkReader, ReadByChunk, RemoteFileReader, RemoteMerkleTreeReader, |
| VirtFdService, |
| }; |
| use crate::fsverity::{merkle_tree_size, VerifiedFileReader}; |
| |
| enum FileInfo { |
| ByPathUnderDirFd(i32, PathBuf), |
| ByFd(i32), |
| } |
| |
| type Reader = VerifiedFileReader<RemoteFileReader, EagerChunkReader>; |
| |
| /// A lazily created read-only file that is verified against the given fs-verity digest. |
| /// |
| /// The main purpose of this struct is to wrap and construct `VerifiedFileReader` lazily. |
| pub struct LazyVerifiedReadonlyFile { |
| expected_digest: Vec<u8>, |
| |
| service: VirtFdService, |
| file_info: FileInfo, |
| |
| /// A lazily instantiated reader. |
| reader: Mutex<Option<Reader>>, |
| } |
| |
| impl LazyVerifiedReadonlyFile { |
| /// Prepare the file by a remote path, related to a remote directory FD. |
| pub fn prepare_by_path( |
| service: VirtFdService, |
| remote_dir_fd: i32, |
| remote_path: PathBuf, |
| expected_digest: Vec<u8>, |
| ) -> Self { |
| LazyVerifiedReadonlyFile { |
| service, |
| file_info: FileInfo::ByPathUnderDirFd(remote_dir_fd, remote_path), |
| expected_digest, |
| reader: Mutex::new(None), |
| } |
| } |
| |
| /// Prepare the file by a remote file FD. |
| pub fn prepare_by_fd(service: VirtFdService, remote_fd: i32, expected_digest: Vec<u8>) -> Self { |
| LazyVerifiedReadonlyFile { |
| service, |
| file_info: FileInfo::ByFd(remote_fd), |
| expected_digest, |
| reader: Mutex::new(None), |
| } |
| } |
| |
| fn ensure_init_then<F, T>(&self, callback: F) -> io::Result<T> |
| where |
| F: FnOnce(&Reader) -> io::Result<T>, |
| { |
| let mut reader = self.reader.lock().unwrap(); |
| if reader.is_none() { |
| let remote_file = match &self.file_info { |
| FileInfo::ByPathUnderDirFd(dir_fd, related_path) => { |
| RemoteFileReader::new_by_path(self.service.clone(), *dir_fd, related_path)? |
| } |
| FileInfo::ByFd(file_fd) => RemoteFileReader::new(self.service.clone(), *file_fd), |
| }; |
| let remote_fd = remote_file.get_remote_fd(); |
| let file_size = self |
| .service |
| .getFileSize(remote_fd) |
| .map_err(|e| { |
| error!("Failed to get file size of remote fd {}: {}", remote_fd, e); |
| io::Error::from_raw_os_error(libc::EIO) |
| })? |
| .try_into() |
| .map_err(|e| { |
| error!("Failed convert file size: {}", e); |
| io::Error::from_raw_os_error(libc::EIO) |
| })?; |
| let instance = VerifiedFileReader::new( |
| remote_file, |
| file_size, |
| &self.expected_digest, |
| EagerChunkReader::new( |
| RemoteMerkleTreeReader::new(self.service.clone(), remote_fd), |
| merkle_tree_size(file_size), |
| )?, |
| ) |
| .map_err(|e| { |
| error!("Failed instantiate a verified file reader: {}", e); |
| io::Error::from_raw_os_error(libc::EIO) |
| })?; |
| *reader = Some(instance); |
| } |
| callback(reader.as_ref().unwrap()) |
| } |
| |
| pub fn file_size(&self) -> io::Result<u64> { |
| self.ensure_init_then(|reader| Ok(reader.file_size)) |
| } |
| } |
| |
| impl ReadByChunk for LazyVerifiedReadonlyFile { |
| fn read_chunk(&self, chunk_index: u64, buf: &mut ChunkBuffer) -> io::Result<usize> { |
| self.ensure_init_then(|reader| reader.read_chunk(chunk_index, buf)) |
| } |
| } |