blob: e3df9ce0b436420a8a8dff75a8b51a70228eca92 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![cfg(test)]
use {
anyhow::Error,
assert_matches::assert_matches,
fdio::{SpawnAction, SpawnOptions},
fidl_fuchsia_io as fio,
fidl_fuchsia_storage_ext4::{MountVmoResult, Server_Marker, ServiceMarker, Success},
fuchsia_runtime::{HandleInfo, HandleType},
fuchsia_zircon::{self as zx, AsHandleRef, DurationNum},
maplit::hashmap,
ramdevice_client::RamdiskClient,
remote_block_device::{BlockClient, RemoteBlockClient},
sha2::{Digest, Sha256},
std::collections::HashMap,
std::io::{self, Read, Seek},
std::{ffi::CString, fs},
test_case::test_case,
};
const RAMDISK_BLOCK_SIZE: u64 = 1024;
const RAMDISK_BLOCK_COUNT: u64 = 16 * 1024;
async fn make_ramdisk() -> (RamdiskClient, RemoteBlockClient) {
let client = RamdiskClient::builder(RAMDISK_BLOCK_SIZE, RAMDISK_BLOCK_COUNT);
let ramdisk = client.build().await.expect("RamdiskClientBuilder.build() failed");
let client_end = ramdisk.open().await.expect("ramdisk.open failed");
let proxy = client_end.into_proxy().expect("into_proxy failed");
let remote_block_device = RemoteBlockClient::new(proxy).await.expect("new failed");
assert_eq!(remote_block_device.block_size(), 1024);
(ramdisk, remote_block_device)
}
#[test_case(
"/pkg/data/extents.img",
hashmap!{
"largefile".to_string() => "de2cf635ae4e0e727f1e412f978001d6a70d2386dc798d4327ec8c77a8e4895d".to_string(),
"smallfile".to_string() => "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03".to_string(),
"sparsefile".to_string() => "3f411e42c1417cd8845d7144679812be3e120318d843c8c6e66d8b2c47a700e9".to_string(),
"a/multi/dir/path/within/this/crowded/extents/test/img/empty".to_string() => "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855".to_string(),
};
"fs with multiple files with multiple extents")]
#[test_case(
"/pkg/data/1file.img",
hashmap!{
"file1".to_string() => "6bc35bfb2ca96c75a1fecde205693c19a827d4b04e90ace330048f3e031487dd".to_string(),
};
"fs with one small file")]
#[test_case(
"/pkg/data/nest.img",
hashmap!{
"file1".to_string() => "6bc35bfb2ca96c75a1fecde205693c19a827d4b04e90ace330048f3e031487dd".to_string(),
"inner/file2".to_string() => "215ca145cbac95c9e2a6f5ff91ca1887c837b18e5f58fd2a7a16e2e5a3901e10".to_string(),
};
"fs with a single directory")]
#[fuchsia::test]
async fn ext4_server_mounts_block_device(
ext4_path: &str,
file_hashes: HashMap<String, String>,
) -> Result<(), Error> {
let mut file_buf = io::BufReader::new(fs::File::open(ext4_path)?);
let size = file_buf.seek(io::SeekFrom::End(0))?;
file_buf.seek(io::SeekFrom::Start(0))?;
let mut temp_buf = vec![0u8; size as usize];
{
file_buf.read_exact(&mut temp_buf)?;
}
let (ramdisk, remote_block_device) = make_ramdisk().await;
remote_block_device.write_at(temp_buf[..].into(), 0).await.expect("write_at failed");
// Close the connection to the block device so the ext4 server can connect.
remote_block_device.close().await.unwrap();
let ext4_binary_path = CString::new("/pkg/bin/ext4_readonly").unwrap();
let (dir_proxy, dir_server) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>()?;
let _process = scoped_task::spawn_etc(
&scoped_task::job_default(),
SpawnOptions::CLONE_ALL,
&ext4_binary_path,
&[&ext4_binary_path],
None,
&mut [
SpawnAction::add_handle(HandleType::DirectoryRequest.into(), dir_server.into()),
SpawnAction::add_handle(
HandleInfo::new(HandleType::User0, 1),
ramdisk.open().await.expect("ramdisk.open").into(),
),
],
)
.unwrap();
for (file_path, expected_hash) in &file_hashes {
let file = fuchsia_fs::directory::open_file_no_describe(
&dir_proxy,
file_path,
fuchsia_fs::OpenFlags::RIGHT_READABLE,
)?;
let mut hasher = Sha256::new();
hasher.update(&fuchsia_fs::file::read(&file).await?);
assert_eq!(*expected_hash, hex::encode(hasher.finalize()));
}
Ok(())
}
#[fuchsia::test]
async fn ext4_server_mounts_block_device_and_dies_on_close() -> Result<(), Error> {
let mut file_buf = io::BufReader::new(fs::File::open("/pkg/data/nest.img")?);
let size = file_buf.seek(io::SeekFrom::End(0))?;
file_buf.seek(io::SeekFrom::Start(0))?;
let mut temp_buf = vec![0u8; size as usize];
file_buf.read_exact(&mut temp_buf)?;
let (ramdisk, remote_block_device) = make_ramdisk().await;
remote_block_device.write_at(temp_buf[..].into(), 0).await.expect("write_at failed");
// Close the connection to the block device so the ext4 server can connect.
remote_block_device.close().await.unwrap();
let ext4_binary_path = CString::new("/pkg/bin/ext4_readonly").unwrap();
let (dir_proxy, dir_server) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>()?;
let process = scoped_task::spawn_etc(
&scoped_task::job_default(),
SpawnOptions::CLONE_ALL,
&ext4_binary_path,
&[&ext4_binary_path],
None,
&mut [
SpawnAction::add_handle(HandleType::DirectoryRequest.into(), dir_server.into()),
SpawnAction::add_handle(
HandleInfo::new(HandleType::User0, 1),
ramdisk.open().await.expect("ramdisk.open").into(),
),
],
)
.unwrap();
std::mem::drop(dir_proxy);
process.wait_handle(zx::Signals::TASK_TERMINATED, zx::Time::after(5.seconds()))?;
Ok(())
}
#[fuchsia::test]
async fn ext4_server_mounts_vmo_one_file() -> Result<(), Error> {
let ext4 = fuchsia_component::client::connect_to_protocol::<Server_Marker>()
.expect("Failed to connect to service");
let mut file_buf = io::BufReader::new(fs::File::open("/pkg/data/1file.img")?);
let size = file_buf.seek(io::SeekFrom::End(0))?;
file_buf.seek(io::SeekFrom::Start(0))?;
let mut temp_buf = vec![0u8; size as usize];
file_buf.read_exact(&mut temp_buf)?;
let vmo = zx::Vmo::create(size)?;
vmo.write(&temp_buf, 0)?;
let (dir_proxy, dir_server) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>()?;
let result = ext4.mount_vmo(vmo, fio::OpenFlags::RIGHT_READABLE, dir_server).await;
assert_matches!(result, Ok(MountVmoResult::Success(Success {})));
let file = fuchsia_fs::directory::open_file_no_describe(
&dir_proxy,
"file1",
fuchsia_fs::OpenFlags::RIGHT_READABLE,
)?;
assert_eq!("file1 contents.\n".to_string(), fuchsia_fs::file::read_to_string(&file).await?);
Ok(())
}
#[fuchsia::test]
async fn ext4_server_mounts_vmo_nested_dirs() -> Result<(), Error> {
let ext4 = fuchsia_component::client::connect_to_protocol::<Server_Marker>()
.expect("Failed to connect to service");
let mut file_buf = io::BufReader::new(fs::File::open("/pkg/data/nest.img")?);
let size = file_buf.seek(io::SeekFrom::End(0))?;
file_buf.seek(io::SeekFrom::Start(0))?;
let mut temp_buf = vec![0u8; size as usize];
file_buf.read_exact(&mut temp_buf)?;
let vmo = zx::Vmo::create(size)?;
vmo.write(&temp_buf, 0)?;
let (dir_proxy, dir_server) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>()?;
let result = ext4.mount_vmo(vmo, fio::OpenFlags::RIGHT_READABLE, dir_server).await;
assert_matches!(result, Ok(MountVmoResult::Success(Success {})));
let file1 = fuchsia_fs::directory::open_file_no_describe(
&dir_proxy,
"file1",
fuchsia_fs::OpenFlags::RIGHT_READABLE,
)?;
assert_eq!("file1 contents.\n".to_string(), fuchsia_fs::file::read_to_string(&file1).await?);
let file2 = fuchsia_fs::directory::open_file_no_describe(
&dir_proxy,
"inner/file2",
fuchsia_fs::OpenFlags::RIGHT_READABLE,
)?;
assert_eq!("file2 contents.\n".to_string(), fuchsia_fs::file::read_to_string(&file2).await?);
Ok(())
}
#[fuchsia::test]
async fn ext4_unified_service_mounts_vmo() -> Result<(), Error> {
let ext4_service = fuchsia_component::client::connect_to_service::<ServiceMarker>()
.expect("Failed to connect to service");
let ext4 = ext4_service.connect_to_server()?;
let mut file_buf = io::BufReader::new(fs::File::open("/pkg/data/nest.img")?);
let size = file_buf.seek(io::SeekFrom::End(0))?;
file_buf.seek(io::SeekFrom::Start(0))?;
let mut temp_buf = vec![0u8; size as usize];
file_buf.read_exact(&mut temp_buf)?;
let vmo = zx::Vmo::create(size)?;
vmo.write(&temp_buf, 0)?;
let (dir_proxy, dir_server) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>()?;
let result = ext4.mount_vmo(vmo, fio::OpenFlags::RIGHT_READABLE, dir_server).await;
assert_matches!(result, Ok(MountVmoResult::Success(Success {})));
let file1 = fuchsia_fs::directory::open_file_no_describe(
&dir_proxy,
"file1",
fuchsia_fs::OpenFlags::RIGHT_READABLE,
)?;
assert_eq!("file1 contents.\n".to_string(), fuchsia_fs::file::read_to_string(&file1).await?);
let file2 = fuchsia_fs::directory::open_file_no_describe(
&dir_proxy,
"inner/file2",
fuchsia_fs::OpenFlags::RIGHT_READABLE,
)?;
assert_eq!("file2 contents.\n".to_string(), fuchsia_fs::file::read_to_string(&file2).await?);
Ok(())
}