blob: 8c02281c2c5fca96ba80f6383c28675210a9204b [file] [log] [blame]
/*
* 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))
}
}