blob: 15fb0f6d06402d17fb106fe1de00a1a8f9c02e48 [file] [log] [blame]
// Copyright 2020 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.
use {
fidl::endpoints::{create_endpoints, create_proxy, Proxy, ServiceMarker},
fidl_fuchsia_io as io, fidl_fuchsia_io_test as io_test, fidl_fuchsia_mem,
fuchsia_async::{self as fasync, DurationExt, TimeoutExt},
fuchsia_zircon as zx,
fuchsia_zircon::Status,
futures::StreamExt,
io_conformance_util::io1_request_logger_factory::Io1RequestLoggerFactory,
io_conformance_util::{flags::build_flag_combinations, test_harness::TestHarness},
};
const TEST_FILE: &str = "testing.txt";
const EMPTY_NODE_ATTRS: io::NodeAttributes = io::NodeAttributes {
mode: 0,
id: 0,
content_size: 0,
storage_size: 0,
link_count: 0,
creation_time: 0,
modification_time: 0,
};
/// Listens for the `OnOpen` event and returns its [Status].
async fn get_open_status(node_proxy: &io::NodeProxy) -> Status {
let mut events = node_proxy.take_event_stream();
let io::NodeEvent::OnOpen_ { s, info: _ } =
events.next().await.expect("OnOpen event not received").expect("FIDL error");
Status::from_raw(s)
}
async fn assert_on_open_not_received(node_proxy: &io::NodeProxy) {
let mut events = node_proxy.take_event_stream();
// Wait at most 200ms for an OnOpen event to appear.
let event =
events.next().on_timeout(zx::Duration::from_millis(200).after_now(), || Option::None).await;
assert!(event.is_none(), "Unexpected OnOpen event received");
}
/// Converts a generic `NodeProxy` to either a file or directory proxy.
fn convert_node_proxy<T: ServiceMarker>(proxy: io::NodeProxy) -> T::Proxy {
T::Proxy::from_channel(proxy.into_channel().expect("Cannot convert node proxy to channel"))
}
/// Helper function to open the desired node in the root folder. Only use this
/// if testing something other than the open call directly.
async fn open_node<T: ServiceMarker>(
dir: &io::DirectoryProxy,
flags: u32,
mode: u32,
path: &str,
) -> T::Proxy {
let flags = flags | io::OPEN_FLAG_DESCRIBE;
let (node_proxy, node_server) = create_proxy::<io::NodeMarker>().expect("Cannot create proxy");
dir.open(flags, mode, path, node_server).expect("Cannot open node");
assert_eq!(get_open_status(&node_proxy).await, Status::OK);
convert_node_proxy::<T>(node_proxy)
}
/// Helper function to open a file with the given flags. Only use this if testing something other
/// than the open call directly.
async fn open_file_with_flags(
parent_dir: &io::DirectoryProxy,
flags: u32,
path: &str,
) -> io::FileProxy {
open_node::<io::FileMarker>(&parent_dir, flags, io::MODE_TYPE_FILE, path).await
}
/// Helper function to open a sub-directory with the given flags. Only use this if testing
/// something other than the open call directly.
async fn open_dir_with_flags(
parent_dir: &io::DirectoryProxy,
flags: u32,
path: &str,
) -> io::DirectoryProxy {
open_node::<io::DirectoryMarker>(&parent_dir, flags, io::MODE_TYPE_DIRECTORY, path).await
}
/// Helper function to open a sub-directory as readable and writable. Only use this if testing
/// something other than the open call directly.
async fn open_rw_dir(parent_dir: &io::DirectoryProxy, path: &str) -> io::DirectoryProxy {
open_dir_with_flags(parent_dir, io::OPEN_RIGHT_READABLE | io::OPEN_RIGHT_WRITABLE, path).await
}
/// Helper function to call `get_token` on a directory. Only use this if testing something
/// other than the `get_token` call directly.
async fn get_token(dir: &io::DirectoryProxy) -> fidl::Handle {
let (status, token) = dir.get_token().await.expect("get_token failed");
assert_eq!(Status::from_raw(status), Status::OK);
token.expect("handle missing")
}
/// Helper function to read a file and return its contents. Only use this if testing something other
/// than the read call directly.
async fn read_file(dir: &io::DirectoryProxy, path: &str) -> Vec<u8> {
let file =
open_node::<io::FileMarker>(dir, io::OPEN_RIGHT_READABLE, io::MODE_TYPE_FILE, path).await;
let (status, data) = file.read(100).await.expect("read failed");
assert_eq!(Status::from_raw(status), Status::OK);
data
}
/// Attempts to open the given file, and checks the status is `NOT_FOUND`.
async fn assert_file_not_found(dir: &io::DirectoryProxy, path: &str) {
let (file_proxy, file_server) = create_proxy::<io::NodeMarker>().expect("Cannot create proxy");
dir.open(
io::OPEN_RIGHT_READABLE | io::OPEN_FLAG_DESCRIBE,
io::MODE_TYPE_FILE,
path,
file_server,
)
.expect("Cannot open file");
assert_eq!(get_open_status(&file_proxy).await, Status::NOT_FOUND);
}
fn root_directory(entries: Vec<io_test::DirectoryEntry>) -> io_test::Directory {
// Convert the simple vector of entries into the convoluted FIDL field type.
let entries: Vec<Option<Box<io_test::DirectoryEntry>>> =
entries.into_iter().map(|e| Some(Box::new(e))).collect();
io_test::Directory { name: None, entries: Some(entries), ..io_test::Directory::EMPTY }
}
fn directory(name: &str, entries: Vec<io_test::DirectoryEntry>) -> io_test::DirectoryEntry {
let mut dir = root_directory(entries);
dir.name = Some(name.to_string());
io_test::DirectoryEntry::Directory(dir)
}
fn file(name: &str, contents: Vec<u8>) -> io_test::DirectoryEntry {
io_test::DirectoryEntry::File(io_test::File {
name: Some(name.to_string()),
contents: Some(contents),
..io_test::File::EMPTY
})
}
fn vmo_file(name: &str, contents: &[u8]) -> io_test::DirectoryEntry {
let size = contents.len() as u64;
let vmo = zx::Vmo::create(size).expect("Cannot create VMO");
vmo.write(contents, 0).expect("Cannot write to VMO");
let range = fidl_fuchsia_mem::Range { vmo, offset: 0, size };
io_test::DirectoryEntry::VmoFile(io_test::VmoFile {
name: Some(name.to_string()),
buffer: Some(range),
..io_test::VmoFile::EMPTY
})
}
// Example test to start up a v2 component harness to test when opening a path that goes through a
// remote mount point, the server forwards the request to the remote correctly.
#[fasync::run_singlethreaded(test)]
async fn open_remote_directory_test() {
let harness = TestHarness::new().await;
if harness.config.no_remote_dir.unwrap_or_default() {
return;
}
let (remote_dir_client, remote_dir_server) =
create_endpoints::<io::DirectoryMarker>().expect("Cannot create endpoints");
let remote_name = "remote_directory";
// Request an extra directory connection from the harness to use as the remote,
// and interpose the requests from the server under test to this remote.
let (logger, mut rx) = Io1RequestLoggerFactory::new();
let remote_dir_server =
logger.get_logged_directory(remote_name.to_string(), remote_dir_server).await;
let root = root_directory(vec![]);
harness
.proxy
.get_directory(root, io::OPEN_RIGHT_READABLE | io::OPEN_RIGHT_WRITABLE, remote_dir_server)
.expect("Cannot get empty remote directory");
let (test_dir_proxy, test_dir_server) =
create_proxy::<io::DirectoryMarker>().expect("Cannot create proxy");
harness
.proxy
.get_directory_with_remote_directory(
remote_dir_client,
remote_name,
io::OPEN_RIGHT_READABLE | io::OPEN_RIGHT_WRITABLE,
test_dir_server,
)
.expect("Cannot get test harness directory");
let (_remote_dir_proxy, remote_dir_server) =
create_proxy::<io::NodeMarker>().expect("Cannot create proxy");
test_dir_proxy
.open(io::OPEN_RIGHT_READABLE, io::MODE_TYPE_DIRECTORY, remote_name, remote_dir_server)
.expect("Cannot open remote directory");
// Wait on an open call to the interposed remote directory.
let open_request_string = rx.next().await.expect("Local tx/rx channel was closed");
// TODO(fxbug.dev/45613):: Bare-metal testing against returned request string. We need
// to find a more ergonomic return format.
assert_eq!(open_request_string, "remote_directory flags:1, mode:16384, path:.");
}
/// Creates a directory with all rights, and checks it can be opened for all subsets of rights.
#[fasync::run_singlethreaded(test)]
async fn open_dir_with_sufficient_rights() {
let harness = TestHarness::new().await;
let root = root_directory(vec![]);
let root_dir = harness.get_directory(root, harness.all_rights);
for dir_flags in harness.all_flag_combos() {
let (client, server) = create_proxy::<io::NodeMarker>().expect("Cannot create proxy.");
root_dir
.open(dir_flags | io::OPEN_FLAG_DESCRIBE, io::MODE_TYPE_DIRECTORY, ".", server)
.expect("Cannot open directory");
assert_eq!(get_open_status(&client).await, Status::OK);
}
}
/// Creates a directory with no rights, and checks opening it with any rights fails.
#[fasync::run_singlethreaded(test)]
async fn open_dir_with_insufficient_rights() {
let harness = TestHarness::new().await;
let root = root_directory(vec![]);
let root_dir = harness.get_directory(root, 0);
for dir_flags in harness.all_flag_combos() {
let (client, server) = create_proxy::<io::NodeMarker>().expect("Cannot create proxy.");
root_dir
.open(dir_flags | io::OPEN_FLAG_DESCRIBE, io::MODE_TYPE_DIRECTORY, ".", server)
.expect("Cannot open directory");
assert_eq!(get_open_status(&client).await, Status::ACCESS_DENIED);
}
}
/// Opens a directory, and checks that a child directory can be opened using the same rights.
#[fasync::run_singlethreaded(test)]
async fn open_child_dir_with_same_rights() {
let harness = TestHarness::new().await;
for dir_flags in harness.all_flag_combos() {
let root = root_directory(vec![directory("child", vec![])]);
let root_dir = harness.get_directory(root, harness.all_rights);
let parent_dir =
open_node::<io::DirectoryMarker>(&root_dir, dir_flags, io::MODE_TYPE_DIRECTORY, ".")
.await;
// Open child directory with same flags as parent.
let (child_dir_client, child_dir_server) =
create_proxy::<io::NodeMarker>().expect("Cannot create proxy.");
parent_dir
.open(
dir_flags | io::OPEN_FLAG_DESCRIBE,
io::MODE_TYPE_DIRECTORY,
"child",
child_dir_server,
)
.expect("Cannot open directory");
assert_eq!(get_open_status(&child_dir_client).await, Status::OK);
}
}
/// Opens a directory as readable, and checks that a child directory cannot be opened as writable.
#[fasync::run_singlethreaded(test)]
async fn open_child_dir_with_extra_rights() {
let harness = TestHarness::new().await;
let root = root_directory(vec![directory("child", vec![])]);
let root_dir = harness.get_directory(root, io::OPEN_RIGHT_READABLE);
// Open parent as readable.
let parent_dir = open_node::<io::DirectoryMarker>(
&root_dir,
io::OPEN_RIGHT_READABLE,
io::MODE_TYPE_DIRECTORY,
".",
)
.await;
// Opening child as writable should fail.
let (child_dir_client, child_dir_server) =
create_proxy::<io::NodeMarker>().expect("Cannot create proxy.");
parent_dir
.open(
io::OPEN_RIGHT_WRITABLE | io::OPEN_FLAG_DESCRIBE,
io::MODE_TYPE_DIRECTORY,
"child",
child_dir_server,
)
.expect("Cannot open directory");
assert_eq!(get_open_status(&child_dir_client).await, Status::ACCESS_DENIED);
}
#[fasync::run_singlethreaded(test)]
async fn open_dir_without_describe_flag() {
let harness = TestHarness::new().await;
let root = root_directory(vec![]);
let root_dir = harness.get_directory(root, harness.all_rights);
for dir_flags in harness.all_flag_combos() {
assert_eq!(dir_flags & io::OPEN_FLAG_DESCRIBE, 0);
let (client, server) = create_proxy::<io::NodeMarker>().expect("Cannot create proxy.");
root_dir
.open(dir_flags, io::MODE_TYPE_DIRECTORY, ".", server)
.expect("Cannot open directory");
assert_on_open_not_received(&client).await;
}
}
#[fasync::run_singlethreaded(test)]
async fn open_file_without_describe_flag() {
let harness = TestHarness::new().await;
for file_flags in harness.readable_flag_combos() {
assert_eq!(file_flags & io::OPEN_FLAG_DESCRIBE, 0);
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let (client, server) = create_proxy::<io::NodeMarker>().expect("Cannot create proxy.");
test_dir.open(file_flags, io::MODE_TYPE_FILE, TEST_FILE, server).expect("Cannot open file");
assert_on_open_not_received(&client).await;
}
}
#[fasync::run_singlethreaded(test)]
async fn create_file_with_sufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.immutable_dir.unwrap_or_default() {
return;
}
for dir_flags in harness.writable_flag_combos() {
let root = root_directory(vec![]);
let test_dir = harness.get_directory(root, harness.all_rights);
// Re-open directory with the flags being tested.
let dir = open_dir_with_flags(&test_dir, dir_flags, ".").await;
let (client, server) = create_proxy::<io::NodeMarker>().expect("Cannot create proxy.");
dir.open(
dir_flags | io::OPEN_FLAG_CREATE | io::OPEN_FLAG_DESCRIBE,
io::MODE_TYPE_FILE,
TEST_FILE,
server,
)
.expect("Cannot open file");
assert_eq!(get_open_status(&client).await, Status::OK);
assert_eq!(read_file(&test_dir, TEST_FILE).await, &[]);
}
}
#[fasync::run_singlethreaded(test)]
async fn create_file_with_insufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.immutable_dir.unwrap_or_default() {
return;
}
for dir_flags in harness.non_writable_flag_combos() {
let root = root_directory(vec![]);
let test_dir = harness.get_directory(root, harness.all_rights);
// Re-open directory with the flags being tested.
let dir = open_dir_with_flags(&test_dir, dir_flags, ".").await;
let (client, server) = create_proxy::<io::NodeMarker>().expect("Cannot create proxy.");
dir.open(
dir_flags | io::OPEN_FLAG_CREATE | io::OPEN_FLAG_DESCRIBE,
io::MODE_TYPE_FILE,
TEST_FILE,
server,
)
.expect("Cannot open file");
assert_eq!(get_open_status(&client).await, Status::ACCESS_DENIED);
assert_file_not_found(&test_dir, TEST_FILE).await;
}
}
#[fasync::run_singlethreaded(test)]
async fn file_read_with_sufficient_rights() {
let harness = TestHarness::new().await;
for file_flags in harness.readable_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let (status, _data) = file.read(0).await.expect("read failed");
assert_eq!(Status::from_raw(status), Status::OK);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_read_with_insufficient_rights() {
let harness = TestHarness::new().await;
for file_flags in harness.non_readable_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let (status, _data) = file.read(0).await.expect("read failed");
assert_eq!(Status::from_raw(status), Status::BAD_HANDLE);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_read_at_with_sufficient_rights() {
let harness = TestHarness::new().await;
for file_flags in harness.readable_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let (status, _data) = file.read_at(0, 0).await.expect("read_at failed");
assert_eq!(Status::from_raw(status), Status::OK);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_read_at_with_insufficient_rights() {
let harness = TestHarness::new().await;
for file_flags in harness.non_readable_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let (status, _data) = file.read_at(0, 0).await.expect("read_at failed");
assert_eq!(Status::from_raw(status), Status::BAD_HANDLE);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_write_with_sufficient_rights() {
let harness = TestHarness::new().await;
for file_flags in harness.writable_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let (status, _actual) = file.write("".as_bytes()).await.expect("write failed");
assert_eq!(Status::from_raw(status), Status::OK);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_write_with_insufficient_rights() {
let harness = TestHarness::new().await;
for file_flags in harness.non_writable_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let (status, _actual) = file.write("".as_bytes()).await.expect("write failed");
assert_eq!(Status::from_raw(status), Status::BAD_HANDLE);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_write_at_with_sufficient_rights() {
let harness = TestHarness::new().await;
for file_flags in harness.writable_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let (status, _actual) = file.write_at("".as_bytes(), 0).await.expect("write_at failed");
assert_eq!(Status::from_raw(status), Status::OK);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_write_at_with_insufficient_rights() {
let harness = TestHarness::new().await;
for file_flags in harness.non_writable_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let (status, _actual) = file.write_at("".as_bytes(), 0).await.expect("write_at failed");
assert_eq!(Status::from_raw(status), Status::BAD_HANDLE);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_truncate_with_sufficient_rights() {
let harness = TestHarness::new().await;
for file_flags in harness.writable_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let status = file.truncate(0).await.expect("truncate failed");
assert_eq!(Status::from_raw(status), Status::OK);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_truncate_with_insufficient_rights() {
let harness = TestHarness::new().await;
for file_flags in harness.non_writable_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let status = file.truncate(0).await.expect("truncate failed");
assert_eq!(Status::from_raw(status), Status::BAD_HANDLE);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_read_in_subdirectory() {
let harness = TestHarness::new().await;
for file_flags in harness.readable_flag_combos() {
let root = root_directory(vec![directory("subdir", vec![file("testing.txt", vec![])])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file = open_node::<io::FileMarker>(
&test_dir,
file_flags,
io::MODE_TYPE_FILE,
"subdir/testing.txt",
)
.await;
let (status, _data) = file.read(0).await.expect("Read failed");
assert_eq!(Status::from_raw(status), Status::OK);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_get_readable_buffer_with_sufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.no_get_buffer.unwrap_or_default() {
return;
}
let contents = "abcdef".as_bytes();
for file_flags in harness.readable_flag_combos() {
let root = root_directory(vec![vmo_file(TEST_FILE, contents)]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let (status, buffer) = file.get_buffer(io::VMO_FLAG_READ).await.expect("get_buffer failed");
assert_eq!(Status::from_raw(status), Status::OK);
// Check contents of buffer.
let buffer = *buffer.expect("buffer is missing");
let mut data = vec![0; buffer.size as usize];
buffer.vmo.read(&mut data, 0).expect("vmo read failed");
assert_eq!(&data, contents);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_get_readable_buffer_with_insufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.no_get_buffer.unwrap_or_default() {
return;
}
for file_flags in harness.non_readable_flag_combos() {
let root = root_directory(vec![vmo_file(TEST_FILE, "abcdef".as_bytes())]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let (status, _buffer) =
file.get_buffer(io::VMO_FLAG_READ).await.expect("get_buffer failed");
assert_eq!(Status::from_raw(status), Status::ACCESS_DENIED);
}
}
#[fasync::run_singlethreaded(test)]
async fn file_get_writable_buffer_with_sufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.no_get_buffer.unwrap_or_default() {
return;
}
for file_flags in harness.writable_flag_combos() {
let root = root_directory(vec![vmo_file(TEST_FILE, "aaaaa".as_bytes())]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
// Get writable buffer.
let (status, buffer) =
file.get_buffer(io::VMO_FLAG_WRITE).await.expect("get_buffer failed");
assert_eq!(Status::from_raw(status), Status::OK);
// Try to write to buffer.
let buffer = *buffer.expect("buffer is missing");
buffer.vmo.write("bbbbb".as_bytes(), 0).expect("vmo write failed");
}
}
#[fasync::run_singlethreaded(test)]
async fn file_get_writable_buffer_with_insufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.no_get_buffer.unwrap_or_default() {
return;
}
for file_flags in harness.non_writable_flag_combos() {
let root = root_directory(vec![vmo_file(TEST_FILE, "abcdef".as_bytes())]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file =
open_node::<io::FileMarker>(&test_dir, file_flags, io::MODE_TYPE_FILE, TEST_FILE).await;
let (status, _buffer) =
file.get_buffer(io::VMO_FLAG_WRITE).await.expect("get_buffer failed");
assert_eq!(Status::from_raw(status), Status::ACCESS_DENIED);
}
}
#[fasync::run_singlethreaded(test)]
async fn directory_describe() {
let harness = TestHarness::new().await;
let root = root_directory(vec![]);
let test_dir = harness.get_directory(root, 0);
let node_info = test_dir.describe().await.expect("describe failed");
assert!(matches!(node_info, io::NodeInfo::Directory { .. }));
}
#[fasync::run_singlethreaded(test)]
async fn file_describe() {
let harness = TestHarness::new().await;
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, io::OPEN_RIGHT_READABLE);
let file = open_node::<io::FileMarker>(
&test_dir,
io::OPEN_RIGHT_READABLE,
io::MODE_TYPE_FILE,
TEST_FILE,
)
.await;
let node_info = file.describe().await.expect("describe failed");
// The node_info can be either File or Vmofile type.
assert!(matches!(node_info, io::NodeInfo::File { .. } | io::NodeInfo::Vmofile { .. }));
}
#[fasync::run_singlethreaded(test)]
async fn vmo_file_describe() {
let harness = TestHarness::new().await;
if harness.config.no_vmofile.unwrap_or_default() {
return;
}
let root = root_directory(vec![vmo_file(TEST_FILE, &[])]);
let test_dir = harness.get_directory(root, io::OPEN_RIGHT_READABLE);
let file = open_node::<io::FileMarker>(
&test_dir,
io::OPEN_RIGHT_READABLE,
io::MODE_TYPE_FILE,
TEST_FILE,
)
.await;
let node_info = file.describe().await.expect("describe failed");
assert!(
matches!(node_info, io::NodeInfo::File { .. } | io::NodeInfo::Vmofile { .. }),
"Expected File, instead got {:?}",
node_info
);
}
#[fasync::run_singlethreaded(test)]
async fn get_token_with_sufficient_rights() {
let harness = TestHarness::new().await;
for dir_flags in harness.writable_flag_combos() {
let root = root_directory(vec![]);
let test_dir = harness.get_directory(root, dir_flags);
let (status, _handle) = test_dir.get_token().await.expect("get_token failed");
assert_eq!(Status::from_raw(status), Status::OK);
// Handle is tested in other test cases.
}
}
#[fasync::run_singlethreaded(test)]
async fn get_token_with_insufficient_rights() {
let harness = TestHarness::new().await;
for dir_flags in harness.non_writable_flag_combos() {
let root = root_directory(vec![]);
let test_dir = harness.get_directory(root, dir_flags);
let (status, _handle) = test_dir.get_token().await.expect("get_token failed");
assert_eq!(Status::from_raw(status), Status::BAD_HANDLE);
}
}
#[fasync::run_singlethreaded(test)]
async fn rename_with_sufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.no_rename.unwrap_or_default() {
return;
}
let contents = "abcdef".as_bytes();
for dir_flags in harness.writable_flag_combos() {
let root = root_directory(vec![
directory("src", vec![file("old.txt", contents.to_vec())]),
directory("dest", vec![]),
]);
let test_dir = harness.get_directory(root, harness.all_rights);
let src_dir = open_dir_with_flags(&test_dir, dir_flags, "src").await;
let dest_dir = open_rw_dir(&test_dir, "dest").await;
let dest_token = get_token(&dest_dir).await;
// Rename src/old.txt -> dest/new.txt.
let status = src_dir.rename("old.txt", dest_token, "new.txt").await.expect("rename failed");
assert_eq!(Status::from_raw(status), Status::OK);
// Check dest/new.txt was created and has correct contents.
assert_eq!(read_file(&test_dir, "dest/new.txt").await, contents);
// Check src/old.txt no longer exists.
assert_file_not_found(&test_dir, "src/old.txt").await;
}
}
#[fasync::run_singlethreaded(test)]
async fn rename_with_insufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.no_rename.unwrap_or_default() {
return;
}
let contents = "abcdef".as_bytes();
for dir_flags in harness.non_writable_flag_combos() {
let root = root_directory(vec![
directory("src", vec![file("old.txt", contents.to_vec())]),
directory("dest", vec![]),
]);
let test_dir = harness.get_directory(root, harness.all_rights);
let src_dir = open_dir_with_flags(&test_dir, dir_flags, "src").await;
let dest_dir = open_rw_dir(&test_dir, "dest").await;
let dest_token = get_token(&dest_dir).await;
// Try renaming src/old.txt -> dest/new.txt.
let status = src_dir.rename("old.txt", dest_token, "new.txt").await.expect("rename failed");
assert_eq!(Status::from_raw(status), Status::BAD_HANDLE);
}
}
#[fasync::run_singlethreaded(test)]
async fn link_with_sufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.no_link.unwrap_or_default() {
return;
}
let contents = "abcdef".as_bytes();
for dir_flags in harness.writable_flag_combos() {
let root = root_directory(vec![
directory("src", vec![file("old.txt", contents.to_vec())]),
directory("dest", vec![]),
]);
let test_dir = harness.get_directory(root, harness.all_rights);
let src_dir = open_dir_with_flags(&test_dir, dir_flags, "src").await;
let dest_dir = open_rw_dir(&test_dir, "dest").await;
let dest_token = get_token(&dest_dir).await;
// Link src/old.txt -> dest/new.txt.
let status = src_dir.link("old.txt", dest_token, "new.txt").await.expect("link failed");
assert_eq!(Status::from_raw(status), Status::OK);
// Check dest/new.txt was created and has correct contents.
assert_eq!(read_file(&test_dir, "dest/new.txt").await, contents);
// Check src/old.txt still exists.
assert_eq!(read_file(&test_dir, "src/old.txt").await, contents);
}
}
#[fasync::run_singlethreaded(test)]
async fn link_with_insufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.no_link.unwrap_or_default() {
return;
}
let contents = "abcdef".as_bytes();
for dir_flags in harness.non_writable_flag_combos() {
let root = root_directory(vec![
directory("src", vec![file("old.txt", contents.to_vec())]),
directory("dest", vec![]),
]);
let test_dir = harness.get_directory(root, harness.all_rights);
let src_dir = open_dir_with_flags(&test_dir, dir_flags, "src").await;
let dest_dir = open_rw_dir(&test_dir, "dest").await;
let dest_token = get_token(&dest_dir).await;
// Link src/old.txt -> dest/new.txt.
let status = src_dir.link("old.txt", dest_token, "new.txt").await.expect("link failed");
assert_eq!(Status::from_raw(status), Status::BAD_HANDLE);
// Check dest/new.txt was not created.
assert_file_not_found(&test_dir, "dest/new.txt").await;
// Check src/old.txt still exists.
assert_eq!(read_file(&test_dir, "src/old.txt").await, contents);
}
}
#[fasync::run_singlethreaded(test)]
async fn clone_file_with_same_or_fewer_rights() {
let harness = TestHarness::new().await;
for file_flags in harness.all_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file = open_file_with_flags(&test_dir, file_flags, TEST_FILE).await;
// Clone using every subset of flags.
for clone_flags in build_flag_combinations(0, file_flags) {
let (proxy, server) = create_proxy::<io::NodeMarker>().expect("create_proxy failed");
file.clone(clone_flags | io::OPEN_FLAG_DESCRIBE, server).expect("clone failed");
let status = get_open_status(&proxy).await;
assert_eq!(status, Status::OK);
// Check flags of cloned connection are correct.
let proxy = convert_node_proxy::<io::FileMarker>(proxy);
// TODO(fxbug.dev/33880): Add support for NodeGetFlags to rustvfs and then switch to
// using that here instead of GetFlags.
let (status, flags) = proxy.get_flags().await.expect("get_flags failed");
assert_eq!(Status::from_raw(status), Status::OK);
assert_eq!(flags, clone_flags);
}
}
}
#[fasync::run_singlethreaded(test)]
async fn clone_file_with_same_rights_flag() {
let harness = TestHarness::new().await;
for file_flags in harness.all_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file = open_file_with_flags(&test_dir, file_flags, TEST_FILE).await;
// Clone using CLONE_FLAG_SAME_RIGHTS.
let (proxy, server) = create_proxy::<io::NodeMarker>().expect("create_proxy failed");
file.clone(io::CLONE_FLAG_SAME_RIGHTS | io::OPEN_FLAG_DESCRIBE, server)
.expect("clone failed");
let status = get_open_status(&proxy).await;
assert_eq!(status, Status::OK);
// Check flags of cloned connection are correct.
let proxy = convert_node_proxy::<io::FileMarker>(proxy);
// TODO(fxbug.dev/33880): Add support for NodeGetFlags to rustvfs and then switch to using
// that here instead of GetFlags.
let (status, flags) = proxy.get_flags().await.expect("get_flags failed");
assert_eq!(Status::from_raw(status), Status::OK);
assert_eq!(flags, file_flags);
}
}
#[fasync::run_singlethreaded(test)]
async fn clone_file_with_additional_rights() {
let harness = TestHarness::new().await;
for file_flags in harness.all_flag_combos() {
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file = open_file_with_flags(&test_dir, file_flags, TEST_FILE).await;
// Clone using every superset of flags, should fail.
for clone_flags in build_flag_combinations(file_flags, harness.all_rights) {
if clone_flags == file_flags {
continue;
}
let (proxy, server) = create_proxy::<io::NodeMarker>().expect("create_proxy failed");
file.clone(clone_flags | io::OPEN_FLAG_DESCRIBE, server).expect("clone failed");
let status = get_open_status(&proxy).await;
assert_eq!(status, Status::ACCESS_DENIED);
}
}
}
#[fasync::run_singlethreaded(test)]
async fn clone_directory_with_same_or_fewer_rights() {
let harness = TestHarness::new().await;
for dir_flags in harness.all_flag_combos() {
let root = root_directory(vec![directory("dir", vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let dir = open_dir_with_flags(&test_dir, dir_flags, "dir").await;
// Clone using every subset of flags.
for clone_flags in build_flag_combinations(0, dir_flags) {
let (proxy, server) = create_proxy::<io::NodeMarker>().expect("create_proxy failed");
dir.clone(clone_flags | io::OPEN_FLAG_DESCRIBE, server).expect("clone failed");
let status = get_open_status(&proxy).await;
assert_eq!(status, Status::OK);
// Check flags of cloned connection are correct.
let (status, flags) = proxy.node_get_flags().await.expect("node_get_flags failed");
assert_eq!(Status::from_raw(status), Status::OK);
assert_eq!(flags, clone_flags);
}
}
}
#[fasync::run_singlethreaded(test)]
async fn clone_directory_with_same_rights_flag() {
let harness = TestHarness::new().await;
for dir_flags in harness.all_flag_combos() {
let root = root_directory(vec![directory("dir", vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let dir = open_dir_with_flags(&test_dir, dir_flags, "dir").await;
// Clone using CLONE_FLAG_SAME_RIGHTS.
let (proxy, server) = create_proxy::<io::NodeMarker>().expect("create_proxy failed");
dir.clone(io::CLONE_FLAG_SAME_RIGHTS | io::OPEN_FLAG_DESCRIBE, server)
.expect("clone failed");
let status = get_open_status(&proxy).await;
assert_eq!(status, Status::OK);
// Check flags of cloned connection are correct.
let proxy = convert_node_proxy::<io::FileMarker>(proxy);
let (status, flags) = proxy.node_get_flags().await.expect("node_get_flags failed");
assert_eq!(Status::from_raw(status), Status::OK);
assert_eq!(flags, dir_flags);
}
}
#[fasync::run_singlethreaded(test)]
async fn clone_directory_with_additional_rights() {
let harness = TestHarness::new().await;
for dir_flags in harness.all_flag_combos() {
let root = root_directory(vec![directory("dir", vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let dir = open_dir_with_flags(&test_dir, dir_flags, "dir").await;
// Clone using every superset of flags, should fail.
for clone_flags in build_flag_combinations(dir_flags, harness.all_rights) {
if clone_flags == dir_flags {
continue;
}
let (proxy, server) = create_proxy::<io::NodeMarker>().expect("create_proxy failed");
dir.clone(clone_flags | io::OPEN_FLAG_DESCRIBE, server).expect("clone failed");
let status = get_open_status(&proxy).await;
assert_eq!(status, Status::ACCESS_DENIED);
}
}
}
#[fasync::run_singlethreaded(test)]
async fn set_attr_file_with_sufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.no_set_attr.unwrap_or_default() {
return;
}
for dir_flags in harness.writable_flag_combos() {
let root = root_directory(vec![file("file", vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file = open_file_with_flags(&test_dir, dir_flags, "file").await;
let (status, old_attr) = file.get_attr().await.expect("get_attr failed");
assert_eq!(Status::from_raw(status), Status::OK);
// Set CREATION_TIME flag, but not MODIFICATION_TIME.
let status = file
.set_attr(
io::NODE_ATTRIBUTE_FLAG_CREATION_TIME,
&mut io::NodeAttributes {
creation_time: 111,
modification_time: 222,
..EMPTY_NODE_ATTRS
},
)
.await
.expect("set_attr failed");
assert_eq!(Status::from_raw(status), Status::OK);
let (status, new_attr) = file.get_attr().await.expect("get_attr failed");
assert_eq!(Status::from_raw(status), Status::OK);
// Check that only creation_time was updated.
let expected = io::NodeAttributes { creation_time: 111, ..old_attr };
assert_eq!(new_attr, expected);
}
}
#[fasync::run_singlethreaded(test)]
async fn set_attr_file_with_insufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.no_set_attr.unwrap_or_default() {
return;
}
for dir_flags in harness.non_writable_flag_combos() {
let root = root_directory(vec![file("file", vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let file = open_file_with_flags(&test_dir, dir_flags, "file").await;
let status = file
.set_attr(
io::NODE_ATTRIBUTE_FLAG_CREATION_TIME,
&mut io::NodeAttributes {
creation_time: 111,
modification_time: 222,
..EMPTY_NODE_ATTRS
},
)
.await
.expect("set_attr failed");
assert_eq!(Status::from_raw(status), Status::BAD_HANDLE);
}
}
#[fasync::run_singlethreaded(test)]
async fn set_attr_directory_with_sufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.no_set_attr.unwrap_or_default() {
return;
}
for dir_flags in harness.writable_flag_combos() {
let root = root_directory(vec![directory("dir", vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let dir = open_dir_with_flags(&test_dir, dir_flags, "dir").await;
let (status, old_attr) = dir.get_attr().await.expect("get_attr failed");
assert_eq!(Status::from_raw(status), Status::OK);
// Set CREATION_TIME flag, but not MODIFICATION_TIME.
let status = dir
.set_attr(
io::NODE_ATTRIBUTE_FLAG_CREATION_TIME,
&mut io::NodeAttributes {
creation_time: 111,
modification_time: 222,
..EMPTY_NODE_ATTRS
},
)
.await
.expect("set_attr failed");
assert_eq!(Status::from_raw(status), Status::OK);
let (status, new_attr) = dir.get_attr().await.expect("get_attr failed");
assert_eq!(Status::from_raw(status), Status::OK);
// Check that only creation_time was updated.
let expected = io::NodeAttributes { creation_time: 111, ..old_attr };
assert_eq!(new_attr, expected);
}
}
#[fasync::run_singlethreaded(test)]
async fn set_attr_directory_with_insufficient_rights() {
let harness = TestHarness::new().await;
if harness.config.no_set_attr.unwrap_or_default() {
return;
}
for dir_flags in harness.non_writable_flag_combos() {
let root = root_directory(vec![directory("dir", vec![])]);
let test_dir = harness.get_directory(root, harness.all_rights);
let dir = open_dir_with_flags(&test_dir, dir_flags, "dir").await;
let status = dir
.set_attr(
io::NODE_ATTRIBUTE_FLAG_CREATION_TIME,
&mut io::NodeAttributes {
creation_time: 111,
modification_time: 222,
..EMPTY_NODE_ATTRS
},
)
.await
.expect("set_attr failed");
assert_eq!(Status::from_raw(status), Status::BAD_HANDLE);
}
}