blob: 935db2b35ccc92f523d6034922d48f980be01156 [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;
use io_conformance_util::test_harness::TestHarness;
use io_conformance_util::*;
use {fidl_fuchsia_io as fio, fuchsia_zircon as zx};
#[fuchsia::test]
async fn set_attr_file_with_sufficient_rights() {
let harness = TestHarness::new().await;
if !harness.supports_mutable_attrs() {
return;
}
for dir_flags in harness.file_rights.valid_combos_with(fio::OpenFlags::RIGHT_WRITABLE) {
let root = root_directory(vec![file("file", vec![])]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
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!(zx::Status::from_raw(status), zx::Status::OK);
// Set CREATION_TIME flag, but not MODIFICATION_TIME.
let status = file
.set_attr(
fio::NodeAttributeFlags::CREATION_TIME,
&fio::NodeAttributes {
creation_time: 111,
modification_time: 222,
..EMPTY_NODE_ATTRS
},
)
.await
.expect("set_attr failed");
assert_eq!(zx::Status::from_raw(status), zx::Status::OK);
let (status, new_attr) = file.get_attr().await.expect("get_attr failed");
assert_eq!(zx::Status::from_raw(status), zx::Status::OK);
// Check that only creation_time was updated.
let expected = fio::NodeAttributes { creation_time: 111, ..old_attr };
assert_eq!(new_attr, expected);
}
}
#[fuchsia::test]
async fn set_attr_file_with_insufficient_rights() {
let harness = TestHarness::new().await;
if !harness.supports_mutable_attrs() {
return;
}
for dir_flags in harness.file_rights.valid_combos_without(fio::OpenFlags::RIGHT_WRITABLE) {
let root = root_directory(vec![file("file", vec![])]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
let file = open_file_with_flags(&test_dir, dir_flags, "file").await;
let status = file
.set_attr(
fio::NodeAttributeFlags::CREATION_TIME,
&fio::NodeAttributes {
creation_time: 111,
modification_time: 222,
..EMPTY_NODE_ATTRS
},
)
.await
.expect("set_attr failed");
assert_eq!(zx::Status::from_raw(status), zx::Status::BAD_HANDLE);
}
}
#[fuchsia::test]
async fn set_attr_directory_with_sufficient_rights() {
let harness = TestHarness::new().await;
if !harness.supports_mutable_attrs() {
return;
}
for dir_flags in harness.file_rights.valid_combos_with(fio::OpenFlags::RIGHT_WRITABLE) {
let root = root_directory(vec![directory("dir", vec![])]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
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!(zx::Status::from_raw(status), zx::Status::OK);
// Set CREATION_TIME flag, but not MODIFICATION_TIME.
let status = dir
.set_attr(
fio::NodeAttributeFlags::CREATION_TIME,
&fio::NodeAttributes {
creation_time: 111,
modification_time: 222,
..EMPTY_NODE_ATTRS
},
)
.await
.expect("set_attr failed");
assert_eq!(zx::Status::from_raw(status), zx::Status::OK);
let (status, new_attr) = dir.get_attr().await.expect("get_attr failed");
assert_eq!(zx::Status::from_raw(status), zx::Status::OK);
// Check that only creation_time was updated.
let expected = fio::NodeAttributes { creation_time: 111, ..old_attr };
assert_eq!(new_attr, expected);
}
}
#[fuchsia::test]
async fn set_attr_directory_with_insufficient_rights() {
let harness = TestHarness::new().await;
if !harness.supports_mutable_attrs() {
return;
}
for dir_flags in harness.file_rights.valid_combos_without(fio::OpenFlags::RIGHT_WRITABLE) {
let root = root_directory(vec![directory("dir", vec![])]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
let dir = open_dir_with_flags(&test_dir, dir_flags, "dir").await;
let status = dir
.set_attr(
fio::NodeAttributeFlags::CREATION_TIME,
&fio::NodeAttributes {
creation_time: 111,
modification_time: 222,
..EMPTY_NODE_ATTRS
},
)
.await
.expect("set_attr failed");
assert_eq!(zx::Status::from_raw(status), zx::Status::BAD_HANDLE);
}
}
#[fuchsia::test]
async fn get_attributes_query_none() {
let harness = TestHarness::new().await;
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
let file_proxy =
open_file_with_flags(&test_dir, fio::OpenFlags::RIGHT_READABLE, TEST_FILE).await;
// fuchsia.io/Node.GetAttributes
// Node attributes that were not requested should return None
let attributes = file_proxy
.get_attributes(fio::NodeAttributesQuery::empty())
.await
.unwrap()
.expect("get_attributes failed");
assert_eq!(attributes, Default::default());
}
#[fuchsia::test]
async fn get_attributes_file_query_all() {
let harness = TestHarness::new().await;
let supported_attrs = harness.config.supported_attributes;
const FILE_CONTENTS: &'static [u8] = b"test-file-contents";
let root = root_directory(vec![file(TEST_FILE, FILE_CONTENTS.to_owned())]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
let file_proxy =
open_file_with_flags(&test_dir, fio::OpenFlags::RIGHT_READABLE, TEST_FILE).await;
// fuchsia.io/Node.GetAttributes
// All of the attributes are requested. Filesystems are allowed to return None for attributes
// they don't support.
let (mutable_attrs, immutable_attrs) = file_proxy
.get_attributes(fio::NodeAttributesQuery::all())
.await
.unwrap()
.expect("get_attributes failed");
// If ctime and mtime are supported then they shouldn't be 0.
if supported_attrs.contains(fio::NodeAttributesQuery::CREATION_TIME) {
assert_matches!(mutable_attrs.creation_time, Some(1..));
} else {
assert_matches!(mutable_attrs.creation_time, None);
}
if supported_attrs.contains(fio::NodeAttributesQuery::MODIFICATION_TIME) {
assert_matches!(mutable_attrs.modification_time, Some(1..));
} else {
assert_matches!(mutable_attrs.modification_time, None);
}
if supported_attrs.contains(fio::NodeAttributesQuery::ACCESS_TIME) {
assert_matches!(mutable_attrs.access_time, Some(1..));
} else {
assert_matches!(mutable_attrs.access_time, None);
}
// The posix attributes weren't set so they should all be None.
assert_matches!(mutable_attrs.mode, None);
assert_matches!(mutable_attrs.uid, None);
assert_matches!(mutable_attrs.gid, None);
assert_matches!(mutable_attrs.rdev, None);
// All node types must report at least protocols and abilities.
assert_matches!(immutable_attrs.protocols, Some(fio::NodeProtocolKinds::FILE));
assert!(immutable_attrs.abilities.is_some());
// Other attributes have conditional support.
if supported_attrs.contains(fio::NodeAttributesQuery::CONTENT_SIZE) {
assert_matches!(immutable_attrs.content_size, Some(x) if x == FILE_CONTENTS.len() as u64);
} else {
assert_matches!(immutable_attrs.content_size, None);
}
if supported_attrs.contains(fio::NodeAttributesQuery::STORAGE_SIZE) {
assert_matches!(immutable_attrs.storage_size, Some(..));
} else {
assert_matches!(immutable_attrs.storage_size, None);
}
if supported_attrs.contains(fio::NodeAttributesQuery::LINK_COUNT) {
assert_matches!(immutable_attrs.link_count, Some(..));
} else {
assert_matches!(immutable_attrs.link_count, None);
}
if supported_attrs.contains(fio::NodeAttributesQuery::ID) {
assert_matches!(immutable_attrs.id, Some(..));
} else {
assert_matches!(immutable_attrs.id, None);
}
}
#[fuchsia::test]
async fn get_attributes_directory_query_all() {
let harness = TestHarness::new().await;
let supported_attrs = harness.config.supported_attributes;
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/Node.GetAttributes
// All of the attributes are requested. Filesystems are allowed to return None for attributes
// they don't support.
let (mutable_attrs, immutable_attrs) = dir_proxy
.get_attributes(fio::NodeAttributesQuery::all())
.await
.unwrap()
.expect("get_attributes failed");
// If timestamps are supported then they shouldn't be 0.
if supported_attrs.contains(fio::NodeAttributesQuery::CREATION_TIME) {
assert_matches!(mutable_attrs.creation_time, Some(1..));
} else {
assert_matches!(mutable_attrs.creation_time, None);
}
if supported_attrs.contains(fio::NodeAttributesQuery::MODIFICATION_TIME) {
assert_matches!(mutable_attrs.modification_time, Some(1..));
} else {
assert_matches!(mutable_attrs.modification_time, None);
}
if supported_attrs.contains(fio::NodeAttributesQuery::ACCESS_TIME) {
assert_matches!(mutable_attrs.access_time, Some(1..));
} else {
assert_matches!(mutable_attrs.access_time, None);
}
// The posix attributes weren't set so they should all be None.
assert_matches!(mutable_attrs.mode, None);
assert_matches!(mutable_attrs.uid, None);
assert_matches!(mutable_attrs.gid, None);
assert_matches!(mutable_attrs.rdev, None);
// All node types must report at least protocols and abilities.
assert_matches!(immutable_attrs.protocols, Some(fio::NodeProtocolKinds::DIRECTORY));
assert!(immutable_attrs.abilities.is_some());
// Other attributes have conditional support.
if supported_attrs.contains(fio::NodeAttributesQuery::CONTENT_SIZE) {
assert_matches!(immutable_attrs.content_size, Some(..));
} else {
assert_matches!(immutable_attrs.content_size, None);
}
if supported_attrs.contains(fio::NodeAttributesQuery::STORAGE_SIZE) {
assert_matches!(immutable_attrs.storage_size, Some(..));
} else {
assert_matches!(immutable_attrs.storage_size, None);
}
if supported_attrs.contains(fio::NodeAttributesQuery::LINK_COUNT) {
assert_matches!(immutable_attrs.link_count, Some(..));
} else {
assert_matches!(immutable_attrs.link_count, None);
}
if supported_attrs.contains(fio::NodeAttributesQuery::ID) {
assert_matches!(immutable_attrs.id, Some(..));
} else {
assert_matches!(immutable_attrs.id, None);
}
}
#[fuchsia::test]
async fn update_attributes_file_unsupported() {
let harness = TestHarness::new().await;
if harness.supports_mutable_attrs() {
return;
}
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
let file_proxy =
open_file_with_flags(&test_dir, fio::OpenFlags::RIGHT_WRITABLE, TEST_FILE).await;
// fuchsia.io/Node.UpdateAttributes
assert_eq!(
file_proxy.update_attributes(&fio::MutableNodeAttributes::default()).await.unwrap(),
Err(zx::Status::NOT_SUPPORTED.into_raw())
);
}
#[fuchsia::test]
async fn update_attributes_file_with_insufficient_rights() {
let harness = TestHarness::new().await;
if !harness.supports_mutable_attrs() {
return;
}
let root = root_directory(vec![file(TEST_FILE, TEST_FILE_CONTENTS.to_vec())]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
let file_proxy =
open_file_with_flags(&test_dir, fio::OpenFlags::RIGHT_READABLE, TEST_FILE).await;
let status = file_proxy
.update_attributes(&fio::MutableNodeAttributes {
modification_time: Some(111),
..Default::default()
})
.await
.expect("FIDL call failed")
.map_err(zx::Status::from_raw);
assert_eq!(status, Err(zx::Status::BAD_HANDLE));
}
#[fuchsia::test]
async fn update_attributes_file_with_sufficient_rights() {
let harness = TestHarness::new().await;
if !harness.supports_mutable_attrs() {
return;
}
let supported_attrs = harness.config.supported_attributes;
let root = root_directory(vec![file(TEST_FILE, TEST_FILE_CONTENTS.to_vec())]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
let file_proxy =
open_file_with_flags(&test_dir, fio::OpenFlags::RIGHT_WRITABLE, TEST_FILE).await;
let new_attrs = fio::MutableNodeAttributes {
creation_time: supported_attrs
.contains(fio::NodeAttributesQuery::CREATION_TIME)
.then_some(111),
modification_time: supported_attrs
.contains(fio::NodeAttributesQuery::MODIFICATION_TIME)
.then_some(222),
mode: supported_attrs.contains(fio::NodeAttributesQuery::MODE).then_some(333),
uid: supported_attrs.contains(fio::NodeAttributesQuery::UID).then_some(444),
gid: supported_attrs.contains(fio::NodeAttributesQuery::GID).then_some(555),
rdev: supported_attrs.contains(fio::NodeAttributesQuery::RDEV).then_some(666),
access_time: supported_attrs.contains(fio::NodeAttributesQuery::ACCESS_TIME).then_some(777),
..Default::default()
};
let _ = file_proxy
.update_attributes(&new_attrs)
.await
.expect("FIDL call failed")
.map_err(zx::Status::from_raw)
.expect("update_attributes failed");
let (mutable_attrs, _) = file_proxy
.get_attributes(supported_attrs)
.await
.expect("FIDL call failed")
.map_err(zx::Status::from_raw)
.expect("get_attributes failed");
assert_eq!(mutable_attrs, new_attrs);
}
#[fuchsia::test]
async fn get_attributes_file_node_reference() {
let harness = TestHarness::new().await;
let root = root_directory(vec![file(TEST_FILE, TEST_FILE_CONTENTS.to_vec())]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
let file_proxy =
open_file_with_flags(&test_dir, fio::OpenFlags::NODE_REFERENCE, TEST_FILE).await;
// fuchsia.io/Node.GetAttributes
let (_mutable_attributes, immutable_attributes) = file_proxy
.get_attributes(fio::NodeAttributesQuery::PROTOCOLS)
.await
.unwrap()
.expect("get_attributes failed");
assert_eq!(immutable_attributes.protocols.unwrap(), fio::NodeProtocolKinds::FILE);
}
#[fuchsia::test]
async fn update_attributes_file_node_reference_not_allowed() {
let harness = TestHarness::new().await;
let root = root_directory(vec![file(TEST_FILE, vec![])]);
let test_dir = harness.get_directory(root, harness.dir_rights.all());
let file_proxy =
open_file_with_flags(&test_dir, fio::OpenFlags::NODE_REFERENCE, TEST_FILE).await;
// Node references does not support fuchsia.io/Node.UpdateAttributes
assert_eq!(
file_proxy.update_attributes(&fio::MutableNodeAttributes::default()).await.unwrap(),
Err(zx::Status::BAD_HANDLE.into_raw())
);
}
#[fuchsia::test]
async fn get_attributes_directory() {
let harness = TestHarness::new().await;
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;
let (_mutable_attributes, immutable_attributes) = dir_proxy
.get_attributes(fio::NodeAttributesQuery::PROTOCOLS)
.await
.unwrap()
.expect("get_attributes failed");
assert_eq!(immutable_attributes.protocols.unwrap(), fio::NodeProtocolKinds::DIRECTORY);
}
#[fuchsia::test]
async fn update_attributes_directory_unsupported() {
let harness = TestHarness::new().await;
if harness.supports_mutable_attrs() {
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_WRITABLE, "dir").await;
// fuchsia.io/Node.UpdateAttributes
assert_eq!(
dir_proxy.update_attributes(&fio::MutableNodeAttributes::default()).await.unwrap(),
Err(zx::Status::NOT_SUPPORTED.into_raw())
);
}
#[fuchsia::test]
async fn update_attributes_directory_with_insufficient_rights() {
let harness = TestHarness::new().await;
if !harness.supports_mutable_attrs() {
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;
let status = dir_proxy
.update_attributes(&fio::MutableNodeAttributes {
modification_time: Some(111),
..Default::default()
})
.await
.expect("FIDL call failed")
.map_err(zx::Status::from_raw);
assert_eq!(status, Err(zx::Status::BAD_HANDLE));
}
#[fuchsia::test]
async fn update_attributes_directory_with_sufficient_rights() {
let harness = TestHarness::new().await;
if !harness.supports_mutable_attrs() {
return;
}
let supported_attrs = harness.config.supported_attributes;
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_WRITABLE, "dir").await;
let new_attrs = fio::MutableNodeAttributes {
creation_time: supported_attrs
.contains(fio::NodeAttributesQuery::CREATION_TIME)
.then_some(111),
modification_time: supported_attrs
.contains(fio::NodeAttributesQuery::MODIFICATION_TIME)
.then_some(222),
mode: supported_attrs.contains(fio::NodeAttributesQuery::MODE).then_some(333),
uid: supported_attrs.contains(fio::NodeAttributesQuery::UID).then_some(444),
gid: supported_attrs.contains(fio::NodeAttributesQuery::GID).then_some(555),
rdev: supported_attrs.contains(fio::NodeAttributesQuery::RDEV).then_some(666),
access_time: supported_attrs.contains(fio::NodeAttributesQuery::ACCESS_TIME).then_some(777),
..Default::default()
};
let _ = dir_proxy
.update_attributes(&new_attrs)
.await
.expect("FIDL call failed")
.map_err(zx::Status::from_raw)
.expect("update_attributes failed");
let (mutable_attrs, _) = dir_proxy
.get_attributes(fio::NodeAttributesQuery::all())
.await
.expect("FIDL call failed")
.map_err(zx::Status::from_raw)
.expect("get_attributes failed");
assert_eq!(mutable_attrs, new_attrs);
}
#[fuchsia::test]
async fn get_attributes_directory_node_reference() {
let harness = TestHarness::new().await;
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::NODE_REFERENCE, "dir").await;
// fuchsia.io/Node.GetAttributes
let (_mutable_attributes, immutable_attributes) = dir_proxy
.get_attributes(fio::NodeAttributesQuery::PROTOCOLS)
.await
.unwrap()
.expect("get_attributes failed");
assert_eq!(immutable_attributes.protocols.unwrap(), fio::NodeProtocolKinds::DIRECTORY);
}
#[fuchsia::test]
async fn update_attributes_directory_node_reference_not_allowed() {
let harness = TestHarness::new().await;
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::NODE_REFERENCE, "dir").await;
// Node reference doesn't allow for updating attributes
assert_eq!(
dir_proxy.update_attributes(&fio::MutableNodeAttributes::default()).await.unwrap(),
Err(zx::Status::BAD_HANDLE.into_raw())
);
}