/* * 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; /// 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, service: VirtFdService, file_info: FileInfo, /// A lazily instantiated reader. reader: Mutex>, } 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, ) -> 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) -> Self { LazyVerifiedReadonlyFile { service, file_info: FileInfo::ByFd(remote_fd), expected_digest, reader: Mutex::new(None), } } fn ensure_init_then(&self, callback: F) -> io::Result where F: FnOnce(&Reader) -> io::Result, { 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 { 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 { self.ensure_init_then(|reader| reader.read_chunk(chunk_index, buf)) } }