blob: aac9c4ebff9b9ef045e12a77115c5efe0ef7fddf [file] [log] [blame]
// Copyright 2022 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 {
assert_matches::assert_matches,
fidl::endpoints::create_proxy,
fidl_fuchsia_io as fio, fuchsia_zircon as zx,
io_conformance_util::{test_harness::TestHarness, *},
};
#[fuchsia::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.dir_rights.all());
for dir_flags in harness.dir_rights.valid_combos() {
assert_eq!(dir_flags & fio::OpenFlags::DESCRIBE, fio::OpenFlags::empty());
let (client, server) = create_proxy::<fio::NodeMarker>().expect("Cannot create proxy.");
root_dir
.open(dir_flags | fio::OpenFlags::DIRECTORY, fio::ModeType::empty(), ".", server)
.expect("Cannot open directory");
assert_on_open_not_received(&client).await;
}
}
#[fuchsia::test]
async fn open_file_without_describe_flag() {
let harness = TestHarness::new().await;
for file_flags in harness.file_rights.valid_combos() {
assert_eq!(file_flags & fio::OpenFlags::DESCRIBE, fio::OpenFlags::empty());
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
let (client, server) = create_proxy::<fio::NodeMarker>().expect("Cannot create proxy.");
test_dir
.open(
file_flags | fio::OpenFlags::NOT_DIRECTORY,
fio::ModeType::empty(),
TEST_FILE,
server,
)
.expect("Cannot open file");
assert_on_open_not_received(&client).await;
}
}
/// Checks that open fails with ZX_ERR_BAD_PATH when it should.
#[fuchsia::test]
async fn open_path() {
let harness = TestHarness::new().await;
let root = root_directory(vec![directory("dir", vec![])]);
let root_dir = harness.get_directory(root, harness.dir_rights.all());
// Valid paths:
for path in [".", "/", "/dir/"] {
open_node::<fio::NodeMarker>(&root_dir, fio::OpenFlags::RIGHT_READABLE, path).await;
}
// Invalid paths:
for path in [
"", "//", "///", "////", "./", "/dir//", "//dir//", "/dir//", "/dir/../", "/dir/..",
"/dir/./", "/dir/.", "/./", "./dir",
] {
assert_eq!(
open_node_status::<fio::NodeMarker>(&root_dir, fio::OpenFlags::RIGHT_READABLE, path)
.await
.expect_err("open succeeded"),
zx::Status::INVALID_ARGS,
"path: {}",
path,
);
}
}
/// Check that a trailing flash with OPEN_FLAG_NOT_DIRECTORY returns ZX_ERR_INVALID_ARGS.
#[fuchsia::test]
async fn open_trailing_slash_with_not_directory() {
let harness = TestHarness::new().await;
let root = root_directory(vec![]);
let root_dir = harness.get_directory(root, harness.dir_rights.all());
assert_eq!(
open_node_status::<fio::NodeMarker>(
&root_dir,
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::NOT_DIRECTORY,
"foo/"
)
.await
.expect_err("open succeeded"),
zx::Status::INVALID_ARGS
);
}
// Validate allowed rights for Directory objects.
#[fuchsia::test]
async fn validate_directory_rights() {
let harness = TestHarness::new().await;
// Create a test directory and ensure we can open it with all supported rights.
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let _root_dir = harness.get_directory(
root,
fio::OpenFlags::RIGHT_READABLE
| fio::OpenFlags::RIGHT_WRITABLE
| fio::OpenFlags::RIGHT_EXECUTABLE,
);
}
// Validate allowed rights for File objects (ensures writable files cannot be opened as executable).
#[fuchsia::test]
async fn validate_file_rights() {
let harness = TestHarness::new().await;
// Create a test directory with a single File object, and ensure the directory has all rights.
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let root_dir = harness.get_directory(root, harness.dir_rights.all());
// Opening as READABLE must succeed.
open_node::<fio::NodeMarker>(&root_dir, fio::OpenFlags::RIGHT_READABLE, TEST_FILE).await;
// Opening as WRITABLE must succeed.
open_node::<fio::NodeMarker>(&root_dir, fio::OpenFlags::RIGHT_WRITABLE, TEST_FILE).await;
// Opening as EXECUTABLE must fail (W^X).
open_node_status::<fio::NodeMarker>(&root_dir, fio::OpenFlags::RIGHT_EXECUTABLE, TEST_FILE)
.await
.expect_err("open succeeded");
}
// Validate allowed rights for ExecutableFile objects (ensures cannot be opened as writable).
#[fuchsia::test]
async fn validate_executable_file_rights() {
let harness = TestHarness::new().await;
if !harness.config.supports_executable_file.unwrap_or_default() {
return;
}
// Create a test directory with an ExecutableFile object, and ensure the directory has all rights.
let root = root_directory(vec![executable_file(TEST_FILE)]);
let root_dir = harness.get_directory(root, harness.dir_rights.all());
// Opening with READABLE/EXECUTABLE should succeed.
open_node::<fio::NodeMarker>(
&root_dir,
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_EXECUTABLE,
TEST_FILE,
)
.await;
// Opening with WRITABLE must fail to ensure W^X enforcement.
assert_eq!(
open_node_status::<fio::NodeMarker>(&root_dir, fio::OpenFlags::RIGHT_WRITABLE, TEST_FILE)
.await
.expect_err("open succeeded"),
zx::Status::ACCESS_DENIED
);
}
/// Creates a directory with all rights, and checks it can be opened for all subsets of rights.
#[fuchsia::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.dir_rights.all());
for dir_flags in harness.dir_rights.valid_combos() {
let (client, server) = create_proxy::<fio::NodeMarker>().expect("Cannot create proxy.");
root_dir
.open(
dir_flags | fio::OpenFlags::DESCRIBE | fio::OpenFlags::DIRECTORY,
fio::ModeType::empty(),
".",
server,
)
.expect("Cannot open directory");
assert_eq!(get_open_status(&client).await, zx::Status::OK);
}
}
/// Creates a directory with no rights, and checks opening it with any rights fails.
#[fuchsia::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, fio::OpenFlags::empty());
for dir_flags in harness.dir_rights.valid_combos() {
if dir_flags.is_empty() {
continue;
}
let (client, server) = create_proxy::<fio::NodeMarker>().expect("Cannot create proxy.");
root_dir
.open(
dir_flags | fio::OpenFlags::DESCRIBE | fio::OpenFlags::DIRECTORY,
fio::ModeType::empty(),
".",
server,
)
.expect("Cannot open directory");
assert_eq!(get_open_status(&client).await, zx::Status::ACCESS_DENIED);
}
}
/// Opens a directory, and checks that a child directory can be opened using the same rights.
#[fuchsia::test]
async fn open_child_dir_with_same_rights() {
let harness = TestHarness::new().await;
for dir_flags in harness.dir_rights.valid_combos() {
let root = root_directory(vec![directory("child", vec![])]);
let root_dir = harness.get_directory(root, harness.dir_rights.all());
let parent_dir = open_node::<fio::DirectoryMarker>(
&root_dir,
dir_flags | fio::OpenFlags::DIRECTORY,
".",
)
.await;
// Open child directory with same flags as parent.
let (child_dir_client, child_dir_server) =
create_proxy::<fio::NodeMarker>().expect("Cannot create proxy.");
parent_dir
.open(
dir_flags | fio::OpenFlags::DESCRIBE | fio::OpenFlags::DIRECTORY,
fio::ModeType::empty(),
"child",
child_dir_server,
)
.expect("Cannot open directory");
assert_eq!(get_open_status(&child_dir_client).await, zx::Status::OK);
}
}
/// Opens a directory as readable, and checks that a child directory cannot be opened as writable.
#[fuchsia::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, fio::OpenFlags::RIGHT_READABLE);
// Open parent as readable.
let parent_dir = open_node::<fio::DirectoryMarker>(
&root_dir,
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::DIRECTORY,
".",
)
.await;
// Opening child as writable should fail.
let (child_dir_client, child_dir_server) =
create_proxy::<fio::NodeMarker>().expect("Cannot create proxy.");
parent_dir
.open(
fio::OpenFlags::RIGHT_WRITABLE | fio::OpenFlags::DESCRIBE | fio::OpenFlags::DIRECTORY,
fio::ModeType::empty(),
"child",
child_dir_server,
)
.expect("Cannot open directory");
assert_eq!(get_open_status(&child_dir_client).await, zx::Status::ACCESS_DENIED);
}
/// Creates a child directory and opens it with OPEN_FLAG_POSIX_WRITABLE/EXECUTABLE, ensuring that
/// the requested rights are expanded to only those which the parent directory connection has.
#[fuchsia::test]
async fn open_child_dir_with_posix_flags() {
let harness = TestHarness::new().await;
for dir_flags in harness.dir_rights.valid_combos() {
let root = root_directory(vec![directory("child", vec![])]);
let root_dir = harness.get_directory(root, dir_flags);
let readable = dir_flags & fio::OpenFlags::RIGHT_READABLE;
let parent_dir = open_node::<fio::DirectoryMarker>(
&root_dir,
dir_flags | fio::OpenFlags::DIRECTORY,
".",
)
.await;
let (child_dir_client, child_dir_server) =
create_proxy::<fio::NodeMarker>().expect("Cannot create proxy.");
parent_dir
.open(
readable
| fio::OpenFlags::POSIX_WRITABLE
| fio::OpenFlags::POSIX_EXECUTABLE
| fio::OpenFlags::DESCRIBE
| fio::OpenFlags::DIRECTORY,
fio::ModeType::empty(),
"child",
child_dir_server,
)
.expect("Cannot open directory");
assert_eq!(
get_open_status(&child_dir_client).await,
zx::Status::OK,
"Failed to open directory, flags = {:?}",
dir_flags
);
// Ensure expanded rights do not exceed those of the parent directory connection.
let (status, flags) =
child_dir_client.get_flags().await.expect("Failed to get node flags!");
assert_matches!(zx::Status::ok(status), Ok(()));
assert_eq!(flags & dir_flags, dir_flags);
}
}
/// Ensures that opening a file with more rights than the directory connection fails
/// with Status::ACCESS_DENIED.
#[fuchsia::test]
async fn open_file_with_extra_rights() {
let harness = TestHarness::new().await;
// Combinations to test of the form (directory flags, [file flag combinations]).
// All file flags should have more rights than those of the directory flags.
let test_right_combinations = [
(fio::OpenFlags::empty(), harness.file_rights.valid_combos()),
(
fio::OpenFlags::RIGHT_READABLE,
harness.file_rights.valid_combos_with(fio::OpenFlags::RIGHT_WRITABLE),
),
(
fio::OpenFlags::RIGHT_WRITABLE,
harness.file_rights.valid_combos_with(fio::OpenFlags::RIGHT_READABLE),
),
];
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let root_dir = harness.get_directory(root, harness.dir_rights.all());
for (dir_flags, file_flag_combos) in test_right_combinations.iter() {
let dir_proxy = open_node::<fio::DirectoryMarker>(
&root_dir,
*dir_flags | fio::OpenFlags::DIRECTORY,
".",
)
.await;
for file_flags in file_flag_combos {
if file_flags.is_empty() {
continue; // The rights in file_flags must *exceed* those in dir_flags.
}
// Ensure the combination is valid (e.g. that file_flags is requesting more rights
// than those in dir_flags).
assert!(
(*file_flags & harness.dir_rights.all()) != (*dir_flags & harness.dir_rights.all()),
"Invalid test: file rights must exceed dir! (flags: dir = {:?}, file = {:?})",
*dir_flags,
*file_flags
);
let (client, server) = create_proxy::<fio::NodeMarker>().expect("Cannot create proxy.");
dir_proxy
.open(
*file_flags | fio::OpenFlags::DESCRIBE | fio::OpenFlags::NOT_DIRECTORY,
fio::ModeType::empty(),
TEST_FILE,
server,
)
.expect("Cannot open file");
assert_eq!(
get_open_status(&client).await,
zx::Status::ACCESS_DENIED,
"Opened a file with more rights than the directory! (flags: dir = {:?}, file = {:?})",
*dir_flags,
*file_flags
);
}
}
}
#[fuchsia::test]
async fn open2_directory_unsupported() {
let harness = TestHarness::new().await;
if harness.config.supports_open2.unwrap_or_default() {
return;
}
let root = root_directory(vec![directory("dir", vec![])]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
let dir_proxy = open_dir_with_flags(&test_dir, fio::OpenFlags::RIGHT_READABLE, "dir").await;
// fuchsia.io/Directory.Open2
assert_matches!(
dir_proxy.open2_node::<fio::DirectoryMarker>(".", Default::default()).await,
Err(zx::Status::NOT_SUPPORTED)
);
}
#[fuchsia::test]
async fn open2_rights() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default() {
return;
}
const CONTENT: &[u8] = b"content";
let test_dir = harness.get_directory(
root_directory(vec![file(TEST_FILE, CONTENT.to_vec())]),
fio::OpenFlags::RIGHT_READABLE,
);
// Should fail to open the file if the rights exceed those allowed by the directory.
let status = test_dir
.open2_node::<fio::NodeMarker>(
&TEST_FILE,
fio::NodeOptions { rights: Some(fio::Operations::WRITE_BYTES), ..Default::default() },
)
.await
.expect_err("open should fail if rights exceed those of the parent connection");
assert_eq!(status, zx::Status::ACCESS_DENIED);
// Check that empty rights get copied from the parent if we don't specify any rights.
let proxy = test_dir
.open2_node::<fio::FileMarker>(
&TEST_FILE,
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
file: Some(fio::FileProtocolFlags::default()),
..Default::default()
}),
..Default::default()
},
)
.await
.unwrap();
assert_eq!(
proxy.get_connection_info().await.expect("get_connection_info failed").rights,
test_dir.get_connection_info().await.expect("get_connection_info failed").rights
);
// We should be able to read from the file, but not write.
assert_eq!(&fuchsia_fs::file::read(&proxy).await.expect("read failed"), CONTENT);
assert_matches!(
fuchsia_fs::file::write(&proxy, "data").await,
Err(fuchsia_fs::file::WriteError::WriteError(zx::Status::BAD_HANDLE))
);
}
#[fuchsia::test]
async fn open2_invalid() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default() {
return;
}
let test_dir = harness.get_directory(
root_directory(vec![]),
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
);
// It's an error to specify more than one protocol when trying to create an object.
for mode in [vfs::CreationMode::AllowExisting, vfs::CreationMode::Always] {
let status = test_dir
.open2_node::<fio::NodeMarker>(
"file",
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
file: Some(fio::FileProtocolFlags::default()),
directory: Some(fio::DirectoryProtocolOptions::default()),
..Default::default()
}),
mode: Some(mode.into()),
..Default::default()
},
)
.await
.expect_err("open should fail if multiple protocols are set during object creation");
assert_eq!(status, zx::Status::INVALID_ARGS);
}
// It's an error to specify create attributes when opening an object.
for mode in [None, Some(vfs::CreationMode::Never.into())] {
let status = test_dir
.open2_node::<fio::DirectoryMarker>(
"file",
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
file: Some(fio::FileProtocolFlags::default()),
..Default::default()
}),
mode,
create_attributes: Some(fio::MutableNodeAttributes {
creation_time: Some(1),
..Default::default()
}),
..Default::default()
},
)
.await
.expect_err("open should fail if setting creation_attributes on an existing object");
assert_eq!(status, zx::Status::INVALID_ARGS);
}
}
#[fuchsia::test]
async fn open2_create_dot_fails_with_already_exists() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default() {
return;
}
let test_dir = harness.get_directory(
root_directory(vec![]),
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
);
let status = test_dir
.open2_node::<fio::DirectoryMarker>(
".",
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
directory: Some(fio::DirectoryProtocolOptions::default()),
..Default::default()
}),
mode: Some(vfs::CreationMode::Always.into()),
..Default::default()
},
)
.await
.expect_err("open should fail when trying to create the dot path");
assert_eq!(status, zx::Status::ALREADY_EXISTS);
}
#[fuchsia::test]
async fn open2_open_directory() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default() {
return;
}
let test_dir = harness.get_directory(
root_directory(vec![directory("dir", vec![])]),
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
);
// Should be able to open using the directory protocol.
let (_, representation) = test_dir
.open2_node_get_representation::<fio::DirectoryMarker>(
"dir",
fio::NodeOptions {
flags: Some(fio::NodeFlags::GET_REPRESENTATION),
protocols: Some(fio::NodeProtocols {
directory: Some(fio::DirectoryProtocolOptions::default()),
..Default::default()
}),
..Default::default()
},
)
.await
.expect("open using directory protocol failed");
assert_matches!(representation, fio::Representation::Directory(_));
// Should also be able to open without specifying an exact protocol due to protocol resolution.
let (_, representation) = test_dir
.open2_node_get_representation::<fio::DirectoryMarker>(
"dir",
fio::NodeOptions {
flags: Some(fio::NodeFlags::GET_REPRESENTATION),
..Default::default()
},
)
.await
.expect("open using node protocol resolution failed");
assert_matches!(representation, fio::Representation::Directory(_));
// Attempting to open the directory as a file should fail.
let status = test_dir
.open2_node::<fio::NodeMarker>(
"dir",
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
file: Some(fio::FileProtocolFlags::default()),
..Default::default()
}),
..Default::default()
},
)
.await
.expect_err("opening directory as file should fail");
assert_eq!(status, zx::Status::NOT_FILE);
// Attempting to open the directory as a symbolic link should also fail.
let status = test_dir
.open2_node::<fio::NodeMarker>(
"dir",
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
symlink: Some(fio::SymlinkProtocolFlags::default()),
..Default::default()
}),
..Default::default()
},
)
.await
.expect_err("opening directory as symlink should fail");
assert_eq!(status, zx::Status::WRONG_TYPE);
}
#[fuchsia::test]
async fn open2_open_file() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default() {
return;
}
const CONTENT: &[u8] = b"content";
let test_dir = harness.get_directory(
root_directory(vec![file("file", CONTENT.to_vec())]),
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
);
// Should be able to open the file specifying just the file protocol.
let (_, representation) = test_dir
.open2_node_get_representation::<fio::FileMarker>(
"file",
fio::NodeOptions {
flags: Some(fio::NodeFlags::GET_REPRESENTATION),
protocols: Some(fio::NodeProtocols {
file: Some(fio::FileProtocolFlags::default()),
..Default::default()
}),
..Default::default()
},
)
.await
.expect("failed to open file with file protocol");
assert_matches!(representation, fio::Representation::File(_));
// Should also be able to open without specifying an exact protocol due to protocol resolution.
let (_, representation) = test_dir
.open2_node_get_representation::<fio::FileMarker>(
"file",
fio::NodeOptions {
flags: Some(fio::NodeFlags::GET_REPRESENTATION),
..Default::default()
},
)
.await
.expect("failed to open file with protocol resolution");
assert_matches!(representation, fio::Representation::File(_));
// Attempting to open the file as a directory should fail.
let status = test_dir
.open2_node_get_representation::<fio::NodeMarker>(
"file",
fio::NodeOptions {
flags: Some(fio::NodeFlags::GET_REPRESENTATION),
protocols: Some(fio::NodeProtocols {
directory: Some(fio::DirectoryProtocolOptions::default()),
..Default::default()
}),
..Default::default()
},
)
.await
.expect_err("should fail to open file as directory");
assert_eq!(status, zx::Status::NOT_DIR);
// Attempting to open the file as a symbolic link should fail.
let status = test_dir
.open2_node_get_representation::<fio::NodeMarker>(
"file",
fio::NodeOptions {
flags: Some(fio::NodeFlags::GET_REPRESENTATION),
protocols: Some(fio::NodeProtocols {
symlink: Some(fio::SymlinkProtocolFlags::default()),
..Default::default()
}),
..Default::default()
},
)
.await
.expect_err("should fail to open file as symlink");
assert_eq!(status, zx::Status::WRONG_TYPE);
}
#[fuchsia::test]
async fn open2_file_append() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default()
|| !harness.config.supports_append.unwrap_or_default()
{
return;
}
let test_dir = harness.get_directory(
root_directory(vec![file("file", b"foo".to_vec())]),
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
);
let proxy = test_dir
.open2_node::<fio::FileMarker>(
"file",
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
file: Some(fio::FileProtocolFlags::APPEND),
..Default::default()
}),
..Default::default()
},
)
.await
.unwrap();
// Append to the file.
assert_matches!(fuchsia_fs::file::write(&proxy, " bar").await, Ok(()));
// Read back to check.
proxy.seek(fio::SeekOrigin::Start, 0).await.expect("seek FIDL failed").expect("seek failed");
assert_eq!(fuchsia_fs::file::read(&proxy).await.expect("read failed"), b"foo bar");
}
#[fuchsia::test]
async fn open2_file_truncate_invalid() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default() {
return;
}
let test_dir = harness.get_directory(
root_directory(vec![file("file", b"foo".to_vec())]),
fio::OpenFlags::RIGHT_READABLE,
);
let status = test_dir
.open2_node::<fio::FileMarker>(
"file",
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
file: Some(fio::FileProtocolFlags::TRUNCATE),
..Default::default()
}),
..Default::default()
},
)
.await
.expect_err("open with truncate requires rights to write bytes");
assert_eq!(status, zx::Status::INVALID_ARGS);
}
#[fuchsia::test]
async fn open2_file_truncate() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default() {
return;
}
let test_dir = harness.get_directory(
root_directory(vec![file("file", b"foo".to_vec())]),
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
);
let proxy = test_dir
.open2_node::<fio::FileMarker>(
"file",
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
file: Some(fio::FileProtocolFlags::TRUNCATE),
..Default::default()
}),
..Default::default()
},
)
.await
.unwrap();
assert_eq!(fuchsia_fs::file::read(&proxy).await.expect("read failed"), b"");
}
#[fuchsia::test]
async fn open2_directory_get_representation() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default() {
return;
}
let test_dir = harness.get_directory(root_directory(vec![]), fio::OpenFlags::RIGHT_READABLE);
let (_, representation) = test_dir
.open2_node_get_representation::<fio::DirectoryMarker>(
".",
fio::NodeOptions {
flags: Some(fio::NodeFlags::GET_REPRESENTATION),
attributes: Some(
fio::NodeAttributesQuery::PROTOCOLS | fio::NodeAttributesQuery::ABILITIES,
),
..Default::default()
},
)
.await
.unwrap();
assert_matches!(
representation,
fio::Representation::Directory(fio::DirectoryInfo {
attributes: Some(fio::NodeAttributes2 { mutable_attributes, immutable_attributes }),
..
})
if mutable_attributes == fio::MutableNodeAttributes::default()
&& immutable_attributes
== fio::ImmutableNodeAttributes {
protocols: Some(fio::NodeProtocolKinds::DIRECTORY),
abilities: Some(
fio::Operations::GET_ATTRIBUTES
| fio::Operations::UPDATE_ATTRIBUTES
| fio::Operations::ENUMERATE
| fio::Operations::TRAVERSE
| fio::Operations::MODIFY_DIRECTORY
),
..Default::default()
}
);
}
#[fuchsia::test]
async fn open2_file_get_representation() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default() {
return;
}
let test_dir = harness
.get_directory(root_directory(vec![file("file", vec![])]), fio::OpenFlags::RIGHT_READABLE);
let file_protocols = if harness.config.supports_append.unwrap_or_default() {
Some(fio::FileProtocolFlags::APPEND)
} else {
Some(fio::FileProtocolFlags::default())
};
let (_, representation) = test_dir
.open2_node_get_representation::<fio::FileMarker>(
"file",
fio::NodeOptions {
flags: Some(fio::NodeFlags::GET_REPRESENTATION),
protocols: Some(fio::NodeProtocols { file: file_protocols, ..Default::default() }),
attributes: Some(
fio::NodeAttributesQuery::PROTOCOLS | fio::NodeAttributesQuery::ABILITIES,
),
..Default::default()
},
)
.await
.unwrap();
assert_matches!(
representation,
fio::Representation::File(fio::FileInfo {
is_append,
attributes: Some(fio::NodeAttributes2 { mutable_attributes, immutable_attributes }),
..
})
if mutable_attributes == fio::MutableNodeAttributes::default()
&& immutable_attributes
== fio::ImmutableNodeAttributes {
protocols: Some(fio::NodeProtocolKinds::FILE),
abilities: Some(
fio::Operations::GET_ATTRIBUTES
| fio::Operations::UPDATE_ATTRIBUTES
| fio::Operations::READ_BYTES
| fio::Operations::WRITE_BYTES,
),
..Default::default()
}
&& is_append == Some(file_protocols == Some(fio::FileProtocolFlags::APPEND))
);
}
#[fuchsia::test]
async fn open2_dir_optional_rights() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default() {
return;
}
let test_dir = harness.get_directory(
root_directory(vec![]),
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
);
let proxy = test_dir
.open2_node::<fio::DirectoryMarker>(
".",
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
directory: Some(fio::DirectoryProtocolOptions {
// Optional rights not supported in the parent connection will be removed.
optional_rights: Some(
fio::Operations::WRITE_BYTES | fio::Operations::EXECUTE,
),
..Default::default()
}),
..Default::default()
}),
rights: Some(fio::Operations::READ_BYTES),
..Default::default()
},
)
.await
.unwrap();
assert_eq!(
proxy.get_connection_info().await.expect("get_connection_info failed").rights.unwrap(),
fio::Operations::READ_BYTES | fio::Operations::WRITE_BYTES,
);
}
#[fuchsia::test]
async fn open2_request_attributes_rights_failure() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default() {
return;
}
let test_dir = harness.get_directory(
root_directory(vec![]),
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
);
// Open with no rights.
let proxy = test_dir
.open2_node::<fio::DirectoryMarker>(
".",
fio::NodeOptions { rights: Some(fio::Operations::empty()), ..Default::default() },
)
.await
.unwrap();
// Now open again and request attributes. It should fail.
let options = fio::NodeOptions {
attributes: Some(fio::NodeAttributesQuery::PROTOCOLS),
..Default::default()
};
assert_matches!(
proxy.open2_node::<fio::DirectoryMarker>(".", options).await,
Err(zx::Status::ACCESS_DENIED)
);
}
#[fuchsia::test]
async fn open2_open_existing_directory() {
let harness = TestHarness::new().await;
if !harness.config.supports_open2.unwrap_or_default() {
return;
}
let test_dir = harness.get_directory(
root_directory(vec![directory("dir", vec![])]),
fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::RIGHT_WRITABLE,
);
// Should not be able to open non-existing directory entry with open mode `OpenExisting`.
let status = test_dir
.open2_node::<fio::NodeMarker>(
"foo",
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
directory: Some(fio::DirectoryProtocolOptions::default()),
..Default::default()
}),
mode: Some(vfs::CreationMode::Never.into()),
..Default::default()
},
)
.await
.expect_err("should fail to open non-existing entry when OpenExisting is set");
assert_eq!(status, zx::Status::NOT_FOUND);
// Check that calling open with `OpenExisting` is successful with an existing directory.
test_dir
.open2_node::<fio::NodeMarker>(
"dir",
fio::NodeOptions {
protocols: Some(fio::NodeProtocols {
directory: Some(fio::DirectoryProtocolOptions::default()),
..Default::default()
}),
mode: Some(vfs::CreationMode::Never.into()),
..Default::default()
},
)
.await
.expect("failed to open existing entry");
}
// TODO(https://fxbug.dev/42157659): Add open2 connect tests.