blob: daca00acef9484312f54dc71be16b57f4e1a8223 [file] [log] [blame]
// Copyright 2021 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 {
crate::{
errors::FxfsError,
object_handle::ObjectHandle,
object_store::{
record::{ObjectItem, ObjectKey, ObjectValue},
transaction::{Mutation, Transaction},
HandleOptions, ObjectStore, StoreObjectHandle,
},
},
anyhow::{bail, Error},
std::sync::Arc,
};
// ObjectDescriptor is exposed in Directory::lookup.
pub use crate::object_store::record::ObjectDescriptor;
/// A directory stores name to child object mappings.
pub struct Directory {
store: Arc<ObjectStore>,
object_id: u64,
}
impl Directory {
pub fn new(store: Arc<ObjectStore>, object_id: u64) -> Self {
Directory { store, object_id }
}
pub fn object_store(&self) -> Arc<ObjectStore> {
self.store.clone()
}
pub fn object_id(&self) -> u64 {
return self.object_id;
}
/// Returns the object ID and descriptor for the given child, or FxfsError::NotFound if not
/// found.
pub async fn lookup(&self, name: &str) -> Result<(u64, ObjectDescriptor), Error> {
let item = self
.store
.tree()
.find(&ObjectKey::child(self.object_id, name))
.await?
.ok_or(FxfsError::NotFound)?;
if let ObjectValue::Child { object_id, object_descriptor } = item.value {
Ok((object_id, object_descriptor))
} else {
bail!(FxfsError::Inconsistent);
}
}
pub async fn create_child_dir(
&self,
transaction: &mut Transaction,
name: &str,
) -> Result<Directory, Error> {
let handle = self.store.create_directory(transaction).await?;
transaction.add(
self.store.store_object_id(),
Mutation::Insert {
item: ObjectItem {
key: ObjectKey::child(self.object_id, name),
value: ObjectValue::child(handle.object_id(), ObjectDescriptor::Directory),
},
},
);
Ok(handle)
}
pub async fn create_child_file(
&self,
transaction: &mut Transaction,
name: &str,
) -> Result<StoreObjectHandle, Error> {
let handle = self.store.create_object(transaction, HandleOptions::default()).await?;
transaction.add(
self.store.store_object_id(),
Mutation::Insert {
item: ObjectItem {
key: ObjectKey::child(self.object_id, name),
value: ObjectValue::child(handle.object_id(), ObjectDescriptor::File),
},
},
);
Ok(handle)
}
pub fn add_child_volume(
&self,
transaction: &mut Transaction,
volume_name: &str,
store_object_id: u64,
volume_info_object_id: u64,
) {
transaction.add(
self.store.store_object_id(),
Mutation::Insert {
item: ObjectItem {
key: ObjectKey::child(self.object_id, volume_name),
value: ObjectValue::child(
store_object_id,
ObjectDescriptor::Volume(volume_info_object_id),
),
},
},
);
}
}
impl ObjectStore {
pub async fn create_directory(
self: &Arc<Self>,
transaction: &mut Transaction,
) -> Result<Directory, Error> {
self.ensure_open().await?;
let object_id = self.get_next_object_id();
transaction.add(
self.store_object_id,
Mutation::Insert {
item: ObjectItem {
key: ObjectKey::object(object_id),
value: ObjectValue::object(ObjectDescriptor::Directory, 1),
},
},
);
Ok(Directory::new(self.clone(), object_id))
}
pub async fn open_directory(self: &Arc<Self>, object_id: u64) -> Result<Directory, Error> {
let item =
self.tree.find(&ObjectKey::object(object_id)).await?.ok_or(FxfsError::NotFound)?;
match item.value {
ObjectValue::Object { object_descriptor: ObjectDescriptor::Directory, refs } => {
if refs == 0 {
bail!(FxfsError::NotFound);
} else {
Ok(Directory::new(self.clone(), object_id))
}
}
ObjectValue::Object { object_descriptor, .. } => {
log::debug!("Expected directory, found: {:?}", object_descriptor);
bail!(FxfsError::NotDir);
}
_ => bail!(FxfsError::Inconsistent),
}
}
}
#[cfg(test)]
mod tests {
use {
crate::{
errors::FxfsError,
object_store::{
filesystem::{Filesystem, FxFilesystem, SyncOptions},
transaction::Transaction,
HandleOptions, ObjectDescriptor,
},
testing::fake_device::FakeDevice,
},
fuchsia_async as fasync,
std::sync::Arc,
};
const TEST_DEVICE_BLOCK_SIZE: u32 = 512;
#[fasync::run_singlethreaded(test)]
async fn test_create_directory() {
let device = Arc::new(FakeDevice::new(2048, TEST_DEVICE_BLOCK_SIZE));
let object_id = {
let fs = FxFilesystem::new_empty(device.clone()).await.expect("new_empty failed");
let mut transaction = Transaction::new();
let dir = fs
.root_store()
.create_directory(&mut transaction)
.await
.expect("create_directory failed");
let child_dir = dir
.create_child_dir(&mut transaction, "foo")
.await
.expect("create_child_dir failed");
let _child_dir_file = child_dir
.create_child_file(&mut transaction, "bar")
.await
.expect("create_child_file failed");
let _child_file = dir
.create_child_file(&mut transaction, "baz")
.await
.expect("create_child_file failed");
dir.add_child_volume(&mut transaction, "corge", 100, 101);
fs.commit_transaction(transaction).await;
fs.sync(SyncOptions::default()).await.expect("sync failed");
dir.object_id()
};
{
let fs = FxFilesystem::open(device).await.expect("open failed");
let dir =
fs.root_store().open_directory(object_id).await.expect("open_directory failed");
let (object_id, object_descriptor) = dir.lookup("foo").await.expect("lookup failed");
assert_eq!(object_descriptor, ObjectDescriptor::Directory);
let child_dir =
fs.root_store().open_directory(object_id).await.expect("open_directory failed");
let (object_id, object_descriptor) =
child_dir.lookup("bar").await.expect("lookup failed");
assert_eq!(object_descriptor, ObjectDescriptor::File);
let _child_dir_file = fs
.root_store()
.open_object(object_id, HandleOptions::default())
.await
.expect("open object failed");
let (object_id, object_descriptor) = dir.lookup("baz").await.expect("lookup failed");
assert_eq!(object_descriptor, ObjectDescriptor::File);
let _child_file = fs
.root_store()
.open_object(object_id, HandleOptions::default())
.await
.expect("open object failed");
let (object_id, object_descriptor) = dir.lookup("corge").await.expect("lookup failed");
assert_eq!(object_id, 100);
if let ObjectDescriptor::Volume(volume_info_object_id) = object_descriptor {
assert_eq!(volume_info_object_id, 101);
} else {
panic!("wrong ObjectDescriptor");
}
assert_eq!(
dir.lookup("qux")
.await
.expect_err("lookup succeeded")
.downcast::<FxfsError>()
.expect("wrong error"),
FxfsError::NotFound
);
}
}
}