1use crate::fuchsia::device::BlockServer;
6use crate::fuchsia::dirent_cache::DirentCacheKey;
7use crate::fuchsia::errors::map_to_status;
8use crate::fuchsia::file::{FlushType, FxFile};
9use crate::fuchsia::node::{FxNode, GetResult, OpenedNode};
10use crate::fuchsia::symlink::FxSymlink;
11use crate::fuchsia::volume::{FxVolume, RootDir};
12use anyhow::{Error, bail};
13use either::{Left, Right};
14use fidl::endpoints::ServerEnd;
15use fidl_fuchsia_io as fio;
16use fidl_fuchsia_storage_block::BlockMarker;
17use fuchsia_sync::Mutex;
18use futures::future::BoxFuture;
19use fxfs::errors::FxfsError;
20use fxfs::filesystem::{SyncOptions, TruncateGuard};
21use fxfs::log::*;
22use fxfs::object_store::directory::{self, ReplacedChild};
23use fxfs::object_store::transaction::{
24 LockKey, Options, ReservationOptions, Transaction, lock_keys,
25};
26use fxfs::object_store::{self, Directory, ObjectDescriptor, ObjectStore, Timestamp};
27use fxfs_crypto::WrappingKeyId;
28use fxfs_macros::ToWeakNode;
29use std::any::Any;
30use std::sync::Arc;
31use vfs::directory::dirents_sink::{self, AppendResult};
32use vfs::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
33use vfs::directory::entry_container::{
34 Directory as VfsDirectory, DirectoryWatcher, MutableDirectory,
35};
36use vfs::directory::mutable::connection::MutableConnection;
37use vfs::directory::traversal_position::TraversalPosition;
38use vfs::directory::watchers::Watchers;
39use vfs::directory::watchers::event_producers::SingleNameEventProducer;
40use vfs::execution_scope::ExecutionScope;
41use vfs::path::Path;
42use vfs::{ObjectRequest, ObjectRequestRef, ProtocolsExt, ToObjectRequest, attributes, symlink};
43
44struct ReplaceWithPurgeResult<'a> {
45 transaction: Transaction<'a>,
46 truncate_guard: Option<TruncateGuard<'static>>,
47 replace_result: ReplacedChild,
48 moved_node: Option<Arc<dyn FxNode>>,
49 actual_src_name: String,
50 actual_dst_name: String,
51}
52
53#[derive(ToWeakNode)]
54pub struct FxDirectory {
55 parent: Option<Mutex<Arc<FxDirectory>>>,
58 directory: object_store::Directory<FxVolume>,
59 watchers: Mutex<Watchers>,
60}
61
62impl RootDir for FxDirectory {
63 fn as_directory_entry(self: Arc<Self>) -> Arc<dyn DirectoryEntry> {
64 self
65 }
66
67 fn serve(self: Arc<Self>, flags: fio::Flags, server_end: ServerEnd<fio::DirectoryMarker>) {
68 let scope = self.volume().scope().clone();
69 vfs::directory::serve_on(self, flags, scope, server_end);
70 }
71
72 fn as_node(self: Arc<Self>) -> Arc<dyn FxNode> {
73 self as Arc<dyn FxNode>
74 }
75}
76
77impl FxDirectory {
78 pub(super) fn new(
79 parent: Option<Arc<FxDirectory>>,
80 directory: object_store::Directory<FxVolume>,
81 ) -> Self {
82 Self {
83 parent: parent.map(|p| Mutex::new(p)),
84 directory,
85 watchers: Mutex::new(Watchers::new()),
86 }
87 }
88
89 pub fn directory(&self) -> &object_store::Directory<FxVolume> {
90 &self.directory
91 }
92
93 pub fn volume(&self) -> &Arc<FxVolume> {
94 self.directory.owner()
95 }
96
97 pub fn store(&self) -> &ObjectStore {
98 self.directory.store()
99 }
100
101 pub fn is_deleted(&self) -> bool {
102 self.directory.is_deleted()
103 }
104
105 pub fn set_deleted(&self) {
106 self.directory.set_deleted();
107 self.watchers.lock().send_event(&mut SingleNameEventProducer::deleted());
108 }
109
110 async fn lookup(
111 self: &Arc<Self>,
112 protocols: &dyn ProtocolsExt,
113 mut path: Path,
114 request: &ObjectRequest,
115 ) -> Result<OpenedNode<dyn FxNode>, Error> {
116 if path.is_empty() {
117 return if protocols.create_unnamed_temporary_in_directory_path() {
118 self.create_unnamed_temporary_file(request.create_attributes()).await
119 } else {
120 Ok(OpenedNode::new(self.clone()))
121 };
122 }
123 let store = self.store();
124 let fs = store.filesystem();
125 let mut current_node = self.clone() as Arc<dyn FxNode>;
126 loop {
127 let last_segment = path.is_single_component();
128 let current_dir =
129 current_node.into_any().downcast::<FxDirectory>().map_err(|_| FxfsError::NotDir)?;
130 let name = path.next().unwrap();
131
132 let keys = lock_keys![LockKey::object(
135 store.store_object_id(),
136 current_dir.directory.object_id()
137 )];
138 let create_object = last_segment
139 && matches!(
140 protocols.creation_mode(),
141 vfs::CreationMode::AllowExisting | vfs::CreationMode::Always
142 );
143 let transaction_or_guard = if create_object {
144 Left(store.new_transaction(keys, Options::default()).await?)
145 } else {
146 Right(fs.lock_manager().read_lock(keys).await)
150 };
151
152 let is_casefold = current_dir.directory.dir_type().is_casefold();
153 let child_descriptor = {
154 match self.directory.owner().dirent_cache().lookup(&(
155 current_dir.object_id(),
156 name,
157 is_casefold,
158 )) {
159 Some(node) => {
160 let desc = node.object_descriptor();
161 Some((node, desc))
162 }
163 None => {
164 if let Some((object_id, object_descriptor, locked)) =
165 current_dir.directory.lookup(name).await?
166 {
167 let child_node = self
168 .volume()
169 .get_or_load_node(
170 object_id,
171 object_descriptor.clone(),
172 Some(current_dir.clone()),
173 )
174 .await?;
175 if !locked {
179 self.directory.owner().dirent_cache().insert(
180 DirentCacheKey::new(
181 current_dir.object_id(),
182 name.to_owned(),
183 is_casefold,
184 ),
185 child_node.clone(),
186 );
187 }
188 Some((child_node, object_descriptor))
189 } else {
190 None
191 }
192 }
193 }
194 };
195
196 match child_descriptor {
197 Some((child_node, object_descriptor)) => {
198 if transaction_or_guard.is_left()
199 && protocols.creation_mode() == vfs::CreationMode::Always
200 {
201 bail!(FxfsError::AlreadyExists);
202 }
203 if last_segment {
204 if protocols.create_unnamed_temporary_in_directory_path() {
205 if !matches!(object_descriptor, ObjectDescriptor::Directory) {
206 bail!(FxfsError::WrongType);
207 }
208 let dir = child_node
209 .into_any()
210 .downcast::<FxDirectory>()
211 .map_err(|_| FxfsError::Inconsistent)?;
212 return dir
213 .create_unnamed_temporary_file(request.create_attributes())
214 .await;
215 }
216
217 match object_descriptor {
218 ObjectDescriptor::Directory => {
219 if !protocols.is_node() && !protocols.is_dir_allowed() {
220 if protocols.is_file_allowed() {
221 bail!(FxfsError::NotFile)
222 } else {
223 bail!(FxfsError::WrongType)
224 }
225 }
226 }
227 ObjectDescriptor::File => {
228 if !protocols.is_node() && !protocols.is_file_allowed() {
229 if protocols.is_dir_allowed() {
230 bail!(FxfsError::NotDir)
231 } else {
232 bail!(FxfsError::WrongType)
233 }
234 }
235 }
236 ObjectDescriptor::Symlink => {
237 if !protocols.is_node() && !protocols.is_symlink_allowed() {
238 bail!(FxfsError::WrongType)
239 }
240 }
241 ObjectDescriptor::Volume => bail!(FxfsError::Inconsistent),
242 }
243 }
244 current_node = child_node;
245 if last_segment {
246 return Ok(OpenedNode::new(current_node));
249 }
250 }
251 None => {
252 if let Left(mut transaction) = transaction_or_guard {
253 let new_node = current_dir
254 .create_child(
255 &mut transaction,
256 name,
257 protocols.create_directory(),
258 request.create_attributes(),
259 )
260 .await?;
261 if let GetResult::Placeholder(p) =
262 self.volume().cache().get_or_reserve(new_node.object_id()).await
263 {
264 return transaction
265 .commit_with_callback(|_| {
266 p.commit(&new_node);
267 current_dir.did_add(name, Some(new_node.clone()));
268 OpenedNode::new(new_node)
271 })
272 .await;
273 } else {
274 bail!(FxfsError::Inconsistent);
278 }
279 } else {
280 bail!(FxfsError::NotFound);
281 }
282 }
283 };
284 }
285 }
286
287 async fn create_child(
288 self: &Arc<Self>,
289 transaction: &mut Transaction<'_>,
290 name: &str,
291 create_dir: bool, create_attributes: Option<&fio::MutableNodeAttributes>,
293 ) -> Result<Arc<dyn FxNode>, Error> {
294 if create_dir {
295 let dir = Arc::new(FxDirectory::new(
296 Some(self.clone()),
297 self.directory.create_child_dir(transaction, name).await?,
298 ));
299 if let Some(attrs) = create_attributes {
300 dir.directory().handle().update_attributes(transaction, Some(&attrs), None).await?;
301 }
302 Ok(dir as Arc<dyn FxNode>)
303 } else {
304 let file = FxFile::new(self.directory.create_child_file(transaction, name).await?);
305 if let Some(attrs) = create_attributes {
306 file.handle()
307 .uncached_handle()
308 .update_attributes(transaction, Some(&attrs), None)
309 .await?;
310 }
311 Ok(file as Arc<dyn FxNode>)
312 }
313 }
314
315 pub(crate) async fn create_unnamed_temporary_file(
316 self: &Arc<Self>,
317 create_attributes: Option<&fio::MutableNodeAttributes>,
318 ) -> Result<OpenedNode<dyn FxNode>, Error> {
319 let store = self.store();
320 let keys = lock_keys![LockKey::object(store.store_object_id(), self.directory.object_id())];
321 let mut transaction = store.new_transaction(keys, Options::default()).await?;
322 let file = FxFile::new(
323 self.directory.create_child_unnamed_temporary_file(&mut transaction).await?,
324 );
325 if let Some(attrs) = create_attributes {
326 file.handle()
327 .uncached_handle()
328 .update_attributes(&mut transaction, Some(&attrs), None)
329 .await?;
330 }
331 let GetResult::Placeholder(p) =
332 self.volume().cache().get_or_reserve(file.object_id()).await
333 else {
334 bail!(FxfsError::Inconsistent);
335 };
336 transaction
337 .commit_with_callback(|_| {
338 let file = file.open_as_temporary();
339 p.commit(&file);
340 file
341 })
342 .await
343 }
344
345 fn remove_from_dirent_cache(&self, name: &str) {
346 let is_casefold = self.directory.dir_type().is_casefold();
347 self.directory.owner().dirent_cache().remove(&(
348 self.directory.object_id(),
349 name,
350 is_casefold,
351 ));
352 }
353
354 pub(crate) fn did_remove(&self, name: &str) {
356 self.remove_from_dirent_cache(name);
357 self.watchers.lock().send_event(&mut SingleNameEventProducer::removed(name));
358 }
359
360 pub(crate) fn did_add(&self, name: &str, node: Option<Arc<dyn FxNode>>) {
362 if let Some(node) = node {
363 let is_casefold = self.directory.dir_type().is_casefold();
364 self.directory.owner().dirent_cache().insert(
365 DirentCacheKey::new(self.directory.object_id(), name.to_owned(), is_casefold),
366 node,
367 );
368 }
369 self.watchers.lock().send_event(&mut SingleNameEventProducer::added(name));
370 }
371
372 pub fn check_fscrypt_policy_equivalence(
375 &self,
376 source_wrapping_key_id: Option<WrappingKeyId>,
377 ) -> Result<(), zx::Status> {
378 if let Some(target_id) = self.directory().dir_type().wrapping_key_id() {
379 if Some(target_id) != source_wrapping_key_id {
380 return Err(zx::Status::BAD_STATE);
381 }
382 }
383 Ok(())
384 }
385
386 pub(crate) async fn link_object(
387 &self,
388 mut transaction: Transaction<'_>,
389 name: &str,
390 source_id: u64,
391 kind: ObjectDescriptor,
392 ) -> Result<(), zx::Status> {
393 let store = self.store();
394 if self.is_deleted() {
395 return Err(zx::Status::ACCESS_DENIED);
396 }
397 if self.directory.lookup(&name).await.map_err(map_to_status)?.is_some() {
398 return Err(zx::Status::ALREADY_EXISTS);
399 }
400 self.directory
401 .insert_child(&mut transaction, &name, source_id, kind.clone())
402 .await
403 .map_err(map_to_status)?;
404 store.adjust_refs(&mut transaction, source_id, 1).await.map_err(map_to_status)?;
405 transaction
406 .commit_with_callback(|_| self.did_add(&name, None))
407 .await
408 .map_err(map_to_status)?;
409 Ok(())
410 }
411
412 pub(crate) async fn link_graveyard_object<F>(
415 &self,
416 mut transaction: Transaction<'_>,
417 name: &str,
418 source_id: u64,
419 kind: ObjectDescriptor,
420 transaction_callback: F,
421 ) -> Result<(), zx::Status>
422 where
423 F: FnOnce() + Send,
424 {
425 let store = self.store();
426 if self.is_deleted() {
427 return Err(zx::Status::ACCESS_DENIED);
428 }
429 if self.directory.lookup(&name).await.map_err(map_to_status)?.is_some() {
430 return Err(zx::Status::ALREADY_EXISTS);
431 }
432 store.remove_from_graveyard(&mut transaction, source_id);
435 self.directory
436 .insert_child(&mut transaction, &name, source_id, kind.clone())
437 .await
438 .map_err(map_to_status)?;
439 transaction
440 .commit_with_callback(|_| {
441 transaction_callback();
442 self.did_add(&name, None);
443 })
444 .await
445 .map_err(map_to_status)?;
446 Ok(())
447 }
448
449 async fn link_impl(
450 self: Arc<Self>,
451 name: String,
452 source_dir: Arc<dyn Any + Send + Sync>,
453 source_name: &str,
454 ) -> Result<(), zx::Status> {
455 let source_dir = source_dir.downcast::<Self>().unwrap();
456 let store = self.store();
457 let mut source_id =
458 match source_dir.directory.lookup(source_name).await.map_err(map_to_status)? {
459 Some((object_id, ObjectDescriptor::File, _)) => object_id,
460 None => return Err(zx::Status::NOT_FOUND),
461 _ => return Err(zx::Status::NOT_SUPPORTED),
462 };
463 loop {
464 let transaction = store
472 .new_transaction(
473 lock_keys![
474 LockKey::object(store.store_object_id(), self.object_id()),
475 LockKey::object(store.store_object_id(), source_id),
476 ],
477 Options::default(),
478 )
479 .await
480 .map_err(map_to_status)?;
481 self.check_fscrypt_policy_equivalence(source_dir.directory().wrapping_key_id())?;
482 match source_dir.directory.lookup(source_name).await.map_err(map_to_status)? {
484 Some((new_id, ObjectDescriptor::File, _)) => {
485 if new_id == source_id {
486 return self
488 .link_object(transaction, &name, source_id, ObjectDescriptor::File)
489 .await;
490 } else {
491 source_id = new_id
492 }
493 }
494 None => return Err(zx::Status::NOT_FOUND),
495 _ => return Err(zx::Status::NOT_SUPPORTED),
496 }
497 }
498 }
499
500 async fn replace_child_with_purge<'a>(
509 self: &'a Arc<Self>,
510 src: Option<(&'a Arc<FxDirectory>, &'a str)>,
511 dst: &'a str,
512 must_be_directory: bool,
513 ) -> Result<Option<ReplaceWithPurgeResult<'a>>, zx::Status> {
514 let mut allow_purge = !self.store().filesystem().is_remote();
519 loop {
520 let borrow_metadata_space = src.is_none();
521 let replace_context = self
524 .directory
525 .acquire_context_for_replace(
526 src.map(|(dir, name)| (dir.directory(), name)),
527 dst,
528 borrow_metadata_space,
529 )
530 .await
531 .map_err(map_to_status)?;
532 let mut transaction = replace_context.transaction;
533
534 let (moved_node, actual_src_name) = if let Some((src_dir, src_name)) = src {
535 if self.is_deleted() {
536 return Err(zx::Status::NOT_FOUND);
537 }
538
539 let (moved_id, moved_descriptor) =
540 replace_context.src_id_and_descriptor.clone().ok_or(zx::Status::NOT_FOUND)?;
541
542 if let ObjectDescriptor::File = moved_descriptor {
544 if must_be_directory {
545 return Err(zx::Status::NOT_DIR);
546 }
547 }
548
549 if src_dir.object_id() == self.object_id() && src_name == dst {
552 return Ok(None);
553 }
554
555 if let Some((_, dst_descriptor)) = replace_context.dst_id_and_descriptor.as_ref() {
556 match (&moved_descriptor, dst_descriptor) {
558 (ObjectDescriptor::Directory, ObjectDescriptor::Directory) => {}
559 (
560 ObjectDescriptor::File | ObjectDescriptor::Symlink,
561 ObjectDescriptor::File | ObjectDescriptor::Symlink,
562 ) => {}
563 (ObjectDescriptor::Directory, _) => return Err(zx::Status::NOT_DIR),
564 (ObjectDescriptor::File | ObjectDescriptor::Symlink, _) => {
565 return Err(zx::Status::NOT_FILE);
566 }
567 _ => return Err(zx::Status::IO_DATA_INTEGRITY),
568 }
569 }
570
571 let moved_node = src_dir
572 .volume()
573 .get_or_load_node(moved_id, moved_descriptor.clone(), Some(src_dir.clone()))
574 .await
575 .map_err(map_to_status)?;
576
577 if let ObjectDescriptor::Directory = moved_descriptor {
578 let mut node_opt = Some(self.clone());
580 while let Some(node) = node_opt {
581 if node.object_id() == moved_node.object_id() {
582 return Err(zx::Status::INVALID_ARGS);
583 }
584 node_opt = node.parent();
585 }
586 }
587
588 let actual_src_name =
591 replace_context.src_name.as_deref().unwrap_or(src_name).to_owned();
592 (Some(moved_node), actual_src_name)
593 } else {
594 let (_child_id, object_descriptor) =
595 replace_context.dst_id_and_descriptor.clone().ok_or(zx::Status::NOT_FOUND)?;
596 if let ObjectDescriptor::Directory = object_descriptor {
597 } else if must_be_directory {
598 return Err(zx::Status::NOT_DIR);
599 }
600 (None, String::new())
601 };
602
603 let actual_dst_name = replace_context.dst_name.as_deref().unwrap_or(dst).to_owned();
606
607 if allow_purge && replace_context.dst_id_and_descriptor.is_some() {
611 self.remove_from_dirent_cache(&actual_dst_name);
612 }
613 let can_purge = allow_purge
614 && match replace_context.dst_id_and_descriptor.as_ref() {
615 Some((dst_id, ObjectDescriptor::File | ObjectDescriptor::Symlink)) => {
616 !self.volume().cache().contains_key(*dst_id)
617 }
618 _ => false,
619 };
620
621 let replace_result = directory::replace_child_with_purge(
622 &mut transaction,
623 src.map(|(dir, name)| (dir.directory(), name)),
624 (self.directory(), dst),
625 can_purge,
626 )
627 .await
628 .map_err(map_to_status)?;
629
630 if let ReplacedChild::Purged(id) = replace_result {
631 transaction.commit_prepare().await;
637 if self.volume().cache().contains_key(id) {
638 allow_purge = false;
639 continue;
640 }
641 }
642 return Ok(Some(ReplaceWithPurgeResult {
643 transaction,
644 truncate_guard: replace_context.truncate_guard,
645 replace_result,
646 moved_node,
647 actual_src_name,
648 actual_dst_name,
649 }));
650 }
651 }
652
653 async fn rename_impl(
654 self: Arc<Self>,
655 src_dir: Arc<dyn MutableDirectory>,
656 src_name: Path,
657 dst_name: Path,
658 ) -> Result<(), zx::Status> {
659 if !src_name.is_single_component() || !dst_name.is_single_component() {
660 return Err(zx::Status::INVALID_ARGS);
661 }
662 let (src, dst) = (src_name.peek().unwrap(), dst_name.peek().unwrap());
663 let src_dir =
664 src_dir.into_any().downcast::<FxDirectory>().map_err(|_| zx::Status::NOT_DIR)?;
665
666 let Some(ReplaceWithPurgeResult {
667 transaction,
668 truncate_guard,
669 replace_result,
670 moved_node,
671 actual_src_name,
672 actual_dst_name,
673 }) = self
674 .replace_child_with_purge(
675 Some((&src_dir, src)),
676 dst,
677 src_name.is_dir() || dst_name.is_dir(),
678 )
679 .await?
680 else {
681 return Ok(());
682 };
683 let moved_node = moved_node.unwrap();
684
685 transaction
686 .commit_with_callback(|_| {
687 moved_node.set_parent(self.clone());
688 src_dir.did_remove(&actual_src_name);
689
690 match replace_result {
691 ReplacedChild::None => {}
692 ReplacedChild::ObjectWithRemainingLinks(..)
693 | ReplacedChild::Object(_)
694 | ReplacedChild::Purged(_) => {
695 self.did_remove(&actual_dst_name);
696 }
697 ReplacedChild::Directory(id) => {
698 let store = self.store();
699 store.filesystem().queue_tombstone_object(store.store_object_id(), id);
700 self.did_remove(&actual_dst_name);
701 self.volume().mark_directory_deleted(id);
702 }
703 }
704 self.did_add(dst, Some(moved_node));
705 })
706 .await
707 .map_err(map_to_status)?;
708
709 if let ReplacedChild::Object(id) = replace_result {
710 self.volume()
711 .maybe_purge_file(id, truncate_guard.as_ref())
712 .await
713 .map_err(map_to_status)?;
714 }
715 Ok(())
716 }
717
718 pub(crate) async fn open_block_file(
719 self: &Arc<Self>,
720 name: &str,
721 server_end: ServerEnd<BlockMarker>,
722 ) {
723 let request = ObjectRequest::new(
724 fio::Flags::empty(),
725 &fio::Options::default(),
726 server_end.into_channel(),
727 );
728 let scope = self.volume().scope().clone();
729 let this = self.clone();
730 request
731 .handle_async(async move |request| {
732 let path = Path::validate_and_split(name).and_then(|p| {
733 if p.is_single_component() { Ok(p) } else { Err(zx::Status::INVALID_ARGS) }
734 })?;
735 let node = this
736 .lookup(&fio::Flags::empty(), path, request)
737 .await
738 .map_err(map_to_status)?;
739 if node.is::<FxFile>() {
740 let file = node.downcast::<FxFile>().unwrap_or_else(|_| unreachable!());
741 if file.is_verified_file() {
742 log::error!("Tried to expose a verified file as a block device.");
743 return Err(zx::Status::NOT_SUPPORTED);
744 }
745 let server = BlockServer::new(file, request.take().into_channel());
746 scope.spawn(server.run());
747 Ok(())
748 } else {
749 Err(zx::Status::NOT_FILE)
750 }
751 })
752 .await
753 }
754}
755
756impl Drop for FxDirectory {
757 fn drop(&mut self) {
758 self.volume().cache().remove(self);
759 }
760}
761
762impl FxNode for FxDirectory {
763 fn object_id(&self) -> u64 {
764 self.directory.object_id()
765 }
766
767 fn parent(&self) -> Option<Arc<FxDirectory>> {
768 self.parent.as_ref().map(|p| p.lock().clone())
769 }
770
771 fn set_parent(&self, parent: Arc<FxDirectory>) {
772 match &self.parent {
773 Some(p) => *p.lock() = parent,
774 None => panic!("Called set_parent on root node"),
775 }
776 }
777
778 fn open_count_add_one(&self) {}
780 fn open_count_sub_one(self: Arc<Self>) {}
781
782 fn object_descriptor(&self) -> ObjectDescriptor {
783 ObjectDescriptor::Directory
784 }
785}
786
787impl MutableDirectory for FxDirectory {
788 fn link<'a>(
789 self: Arc<Self>,
790 name: String,
791 source_dir: Arc<dyn Any + Send + Sync>,
792 source_name: &'a str,
793 ) -> BoxFuture<'a, Result<(), zx::Status>> {
794 Box::pin(self.link_impl(name, source_dir, source_name))
795 }
796
797 async fn unlink(
798 self: Arc<Self>,
799 name: &str,
800 must_be_directory: bool,
801 ) -> Result<(), zx::Status> {
802 let ReplaceWithPurgeResult {
803 transaction,
804 truncate_guard,
805 replace_result,
806 actual_dst_name,
807 ..
808 } = self
809 .replace_child_with_purge(None, name, must_be_directory)
810 .await?
811 .expect("unlink cannot be a trivial rename");
812
813 match replace_result {
814 ReplacedChild::None => return Err(zx::Status::NOT_FOUND),
815 ReplacedChild::ObjectWithRemainingLinks(..) | ReplacedChild::Purged(..) => {
816 transaction
817 .commit_with_callback(|_| self.did_remove(&actual_dst_name))
818 .await
819 .map_err(map_to_status)?;
820 }
821 ReplacedChild::Object(id) => {
822 transaction
823 .commit_with_callback(|_| self.did_remove(&actual_dst_name))
824 .await
825 .map_err(map_to_status)?;
826
827 if let Err(e) = self.volume().maybe_purge_file(id, truncate_guard.as_ref()).await {
830 warn!(error:? = e; "Failed to purge file");
831 }
832 }
833 ReplacedChild::Directory(id) => {
834 transaction
835 .commit_with_callback(|_| {
836 let store = self.store();
837 store.filesystem().queue_tombstone_object(store.store_object_id(), id);
838 self.did_remove(&actual_dst_name);
839 self.volume().mark_directory_deleted(id);
840 })
841 .await
842 .map_err(map_to_status)?;
843 }
844 }
845 Ok(())
846 }
847
848 async fn update_attributes(
849 &self,
850 attributes: fio::MutableNodeAttributes,
851 ) -> Result<(), zx::Status> {
852 if let Some(casefold) = attributes.casefold {
854 self.directory.set_casefold(casefold).await.map_err(map_to_status)?;
855 }
856 let transaction = self
857 .store()
858 .new_transaction(
859 lock_keys![LockKey::object(
860 self.store().store_object_id(),
861 self.directory.object_id()
862 )],
863 Options { reservation: ReservationOptions::Borrowed, ..Default::default() },
864 )
865 .await
866 .map_err(map_to_status)?;
867
868 self.directory
869 .update_attributes(transaction, Some(&attributes), 0, Some(Timestamp::now()))
870 .await
871 .map_err(map_to_status)?;
872 Ok(())
873 }
874
875 async fn sync(&self) -> Result<(), zx::Status> {
876 self.volume().flush_all_files(FlushType::Sync).await;
878 self.volume()
879 .store()
880 .filesystem()
881 .sync(SyncOptions { flush_device: true, ..Default::default() })
882 .await
883 .map_err(map_to_status)
884 }
885
886 fn rename(
887 self: Arc<Self>,
888 src_dir: Arc<dyn MutableDirectory>,
889 src_name: Path,
890 dst_name: Path,
891 ) -> BoxFuture<'static, Result<(), zx::Status>> {
892 Box::pin(self.rename_impl(src_dir, src_name, dst_name))
893 }
894
895 async fn create_symlink(
896 &self,
897 name: String,
898 target: Vec<u8>,
899 connection: Option<ServerEnd<fio::SymlinkMarker>>,
900 ) -> Result<(), zx::Status> {
901 let store = self.store();
902 let dir = &self.directory;
903 let keys = lock_keys![LockKey::object(store.store_object_id(), dir.object_id())];
904 let mut transaction =
905 store.new_transaction(keys, Options::default()).await.map_err(map_to_status)?;
906 if dir.lookup(&name).await.map_err(map_to_status)?.is_some() {
907 return Err(zx::Status::ALREADY_EXISTS);
908 }
909 let object_id =
910 dir.create_symlink(&mut transaction, &target, &name).await.map_err(map_to_status)?;
911 if let Some(connection) = connection {
912 if let GetResult::Placeholder(p) = self.volume().cache().get_or_reserve(object_id).await
913 {
914 transaction
915 .commit_with_callback(|_| {
916 let node = Arc::new(FxSymlink::new(self.volume().clone(), object_id));
917 p.commit(&(node.clone() as Arc<dyn FxNode>));
918 let scope = self.volume().scope().clone();
919 let flags =
920 fio::Flags::PROTOCOL_SYMLINK | fio::PERM_READABLE | fio::PERM_WRITABLE;
921 let opened_node = OpenedNode::new(node);
923 symlink::Connection::create_sync(
926 scope,
927 opened_node.take(),
928 flags,
929 flags.to_object_request(connection),
930 );
931 })
932 .await
933 } else {
934 return Err(zx::Status::IO_DATA_INTEGRITY);
937 }
938 } else {
939 transaction.commit().await.map(|_| ())
940 }
941 .map_err(map_to_status)
942 }
943}
944
945impl DirectoryEntry for FxDirectory {
946 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), zx::Status> {
947 request.open_dir(self)
948 }
949
950 fn scope(&self) -> Option<ExecutionScope> {
951 Some(self.volume().scope().clone())
952 }
953}
954
955impl GetEntryInfo for FxDirectory {
956 fn entry_info(&self) -> EntryInfo {
957 EntryInfo::new(self.object_id(), fio::DirentType::Directory)
958 }
959}
960
961impl vfs::node::Node for FxDirectory {
962 async fn get_attributes(
963 &self,
964 requested_attributes: fio::NodeAttributesQuery,
965 ) -> Result<fio::NodeAttributes2, zx::Status> {
966 let mut props = self.directory.get_properties().await.map_err(map_to_status)?;
967
968 if requested_attributes.contains(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE) {
969 self.store()
970 .update_access_time(self.directory.object_id(), &mut props, || !self.is_deleted())
971 .await
972 .map_err(map_to_status)?;
973 }
974
975 Ok(attributes!(
976 requested_attributes,
977 Mutable {
978 creation_time: props.creation_time.as_nanos(),
979 modification_time: props.modification_time.as_nanos(),
980 access_time: props.access_time.as_nanos(),
981 mode: props.posix_attributes.map(|a| a.mode),
982 uid: props.posix_attributes.map(|a| a.uid),
983 gid: props.posix_attributes.map(|a| a.gid),
984 rdev: props.posix_attributes.map(|a| a.rdev),
985 casefold: self.directory.dir_type().is_casefold(),
986 selinux_context: self
987 .directory
988 .handle()
989 .get_inline_selinux_context()
990 .await
991 .map_err(map_to_status)?,
992 encryption_policy: props.dir_type.fscrypt_policy(),
993 },
994 Immutable {
995 protocols: fio::NodeProtocolKinds::DIRECTORY,
996 abilities: fio::Operations::GET_ATTRIBUTES
997 | fio::Operations::UPDATE_ATTRIBUTES
998 | fio::Operations::ENUMERATE
999 | fio::Operations::TRAVERSE
1000 | fio::Operations::MODIFY_DIRECTORY,
1001 content_size: props.data_attribute_size,
1002 storage_size: props.allocated_size,
1003 link_count: props.refs + 1 + props.sub_dirs,
1004 id: self.directory.object_id(),
1005 change_time: props.change_time.as_nanos(),
1006 verity_enabled: false,
1007 }
1008 ))
1009 }
1010
1011 fn query_filesystem(&self) -> Result<fio::FilesystemInfo, zx::Status> {
1012 Ok(self.volume().filesystem_info_for_volume())
1013 }
1014
1015 async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, zx::Status> {
1016 self.directory.list_extended_attributes().await.map_err(map_to_status)
1017 }
1018
1019 async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, zx::Status> {
1020 self.directory.get_extended_attribute(name).await.map_err(map_to_status)
1021 }
1022
1023 async fn set_extended_attribute(
1024 &self,
1025 name: Vec<u8>,
1026 value: Vec<u8>,
1027 mode: fio::SetExtendedAttributeMode,
1028 ) -> Result<(), zx::Status> {
1029 self.directory.set_extended_attribute(name, value, mode.into()).await.map_err(map_to_status)
1030 }
1031
1032 async fn remove_extended_attribute(&self, name: Vec<u8>) -> Result<(), zx::Status> {
1033 self.directory.remove_extended_attribute(name).await.map_err(map_to_status)
1034 }
1035}
1036
1037impl VfsDirectory for FxDirectory {
1038 fn deprecated_open(
1039 self: Arc<Self>,
1040 scope: ExecutionScope,
1041 flags: fio::OpenFlags,
1042 path: Path,
1043 server_end: ServerEnd<fio::NodeMarker>,
1044 ) {
1045 scope.clone().spawn(flags.to_object_request(server_end).handle_async(
1046 async move |object_request| {
1047 let node =
1048 self.lookup(&flags, path, object_request).await.map_err(map_to_status)?;
1049 if node.is::<FxDirectory>() {
1050 let directory =
1051 node.downcast::<FxDirectory>().unwrap_or_else(|_| unreachable!()).take();
1052 object_request
1053 .create_connection::<MutableConnection<_>, _>(scope, directory, flags)
1054 .await
1055 } else if node.is::<FxFile>() {
1056 let node = node.downcast::<FxFile>().unwrap_or_else(|_| unreachable!());
1057 if flags.contains(fio::OpenFlags::BLOCK_DEVICE) {
1058 if node.is_verified_file() {
1059 log::error!("Tried to expose a verified file as a block device.");
1060 return Err(zx::Status::NOT_SUPPORTED);
1061 }
1062 if !flags.contains(fio::OpenFlags::RIGHT_READABLE) {
1063 log::error!(
1064 "Opening a file as block device requires at least RIGHT_READABLE."
1065 );
1066 return Err(zx::Status::ACCESS_DENIED);
1067 }
1068 let server = BlockServer::new(node, object_request.take().into_channel());
1069 scope.spawn(server.run());
1070 Ok(())
1071 } else {
1072 FxFile::create_connection_async(node, scope, flags, object_request).await
1073 }
1074 } else if node.is::<FxSymlink>() {
1075 let node = node.downcast::<FxSymlink>().unwrap_or_else(|_| unreachable!());
1076 object_request
1077 .create_connection::<symlink::Connection<_>, _>(
1078 scope.clone(),
1079 node.take(),
1080 flags,
1081 )
1082 .await
1083 } else {
1084 unreachable!();
1085 }
1086 },
1087 ));
1088 }
1089
1090 fn open(
1091 self: Arc<Self>,
1092 scope: ExecutionScope,
1093 path: Path,
1094 flags: fio::Flags,
1095 object_request: ObjectRequestRef<'_>,
1096 ) -> Result<(), zx::Status> {
1097 self.volume().scope().clone().spawn(object_request.take().handle_async(
1098 async move |object_request| self.open_async(scope, path, flags, object_request).await,
1099 ));
1100 Ok(())
1101 }
1102
1103 async fn open_async(
1104 self: Arc<Self>,
1105 scope: ExecutionScope,
1106 path: Path,
1107 flags: fio::Flags,
1108 object_request: ObjectRequestRef<'_>,
1109 ) -> Result<(), zx::Status> {
1110 let node = self.lookup(&flags, path, object_request).await.map_err(map_to_status)?;
1111 if node.is::<FxDirectory>() {
1112 let directory =
1113 node.downcast::<FxDirectory>().unwrap_or_else(|_| unreachable!()).take();
1114 object_request
1115 .create_connection::<MutableConnection<_>, _>(scope, directory, flags)
1116 .await
1117 } else if node.is::<FxFile>() {
1118 let file = node.downcast::<FxFile>().unwrap_or_else(|_| unreachable!());
1119 FxFile::create_connection_async(file, scope, flags, object_request).await
1120 } else if node.is::<FxSymlink>() {
1121 let symlink = node.downcast::<FxSymlink>().unwrap_or_else(|_| unreachable!());
1122 object_request
1123 .create_connection::<symlink::Connection<_>, _>(
1124 scope.clone(),
1125 symlink.take(),
1126 flags,
1127 )
1128 .await
1129 } else {
1130 unreachable!();
1131 }
1132 }
1133
1134 async fn read_dirents(
1135 &self,
1136 pos: &TraversalPosition,
1137 mut sink: Box<dyn dirents_sink::Sink>,
1138 ) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), zx::Status> {
1139 if let TraversalPosition::End = pos {
1140 return Ok((TraversalPosition::End, sink.seal()));
1141 } else if let TraversalPosition::Index(_) = pos {
1142 return Err(zx::Status::BAD_STATE);
1144 }
1145
1146 let store = self.store();
1147 let fs = store.filesystem();
1148 let _read_guard = fs
1149 .lock_manager()
1150 .read_lock(lock_keys![LockKey::object(store.store_object_id(), self.object_id())])
1151 .await;
1152 if self.is_deleted() {
1153 return Ok((TraversalPosition::End, sink.seal()));
1154 }
1155
1156 let layer_set = self.store().tree().layer_set();
1157 let mut merger = layer_set.merger();
1158 let mut iter = match pos {
1159 TraversalPosition::Start => {
1160 match sink
1162 .append(&EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), ".")
1163 {
1164 AppendResult::Ok(new_sink) => sink = new_sink,
1165 AppendResult::Sealed(sealed) => {
1166 return Ok((TraversalPosition::Start, sealed));
1170 }
1171 }
1172 self.directory.iter(&mut merger).await
1173 }
1174 TraversalPosition::Name(name) => self.directory.iter_from(&mut merger, name).await,
1175 TraversalPosition::Bytes(bytes) => {
1176 self.directory.iter_from_bytes(&mut merger, bytes).await
1177 }
1178 _ => unreachable!(),
1179 }
1180 .map_err(map_to_status)?;
1181 while let Some((name, object_id, object_descriptor)) = iter.get() {
1182 let entry_type = match object_descriptor {
1183 ObjectDescriptor::File => fio::DirentType::File,
1184 ObjectDescriptor::Directory => fio::DirentType::Directory,
1185 ObjectDescriptor::Symlink => fio::DirentType::Symlink,
1186 ObjectDescriptor::Volume => return Err(zx::Status::IO_DATA_INTEGRITY),
1187 };
1188
1189 let info = EntryInfo::new(object_id, entry_type);
1190 match sink.append(&info, &name) {
1191 AppendResult::Ok(new_sink) => sink = new_sink,
1192 AppendResult::Sealed(sealed) => {
1193 return Ok((
1200 iter.traversal_position(
1201 |name| TraversalPosition::Name(name.to_string()),
1202 |bytes| TraversalPosition::Bytes(bytes),
1203 )
1204 .unwrap(),
1205 sealed,
1206 ));
1207 }
1208 }
1209 iter.advance().await.map_err(map_to_status)?;
1210 }
1211
1212 Ok((TraversalPosition::End, sink.seal()))
1213 }
1214
1215 fn register_watcher(
1216 self: Arc<Self>,
1217 scope: ExecutionScope,
1218 mask: fio::WatchMask,
1219 watcher: DirectoryWatcher,
1220 ) -> Result<(), zx::Status> {
1221 let controller =
1222 self.watchers.lock().add(scope.clone(), self.clone(), mask, watcher).clone();
1223 if mask.contains(fio::WatchMask::EXISTING) && !self.is_deleted() {
1224 scope.spawn(async move {
1225 let layer_set = self.store().tree().layer_set();
1226 let mut merger = layer_set.merger();
1227 let mut iter = match self.directory.iter_from(&mut merger, "").await {
1228 Ok(iter) => iter,
1229 Err(e) => {
1230 error!(error:? = e; "Failed to iterate directory for watch",);
1231 return;
1234 }
1235 };
1236 controller.send_event(&mut SingleNameEventProducer::existing("."));
1239 while let Some((name, _, _)) = iter.get() {
1240 controller.send_event(&mut SingleNameEventProducer::existing(name));
1241 if let Err(e) = iter.advance().await {
1242 error!(error:? = e; "Failed to iterate directory for watch",);
1243 return;
1244 }
1245 }
1246 controller.send_event(&mut SingleNameEventProducer::idle());
1247 });
1248 }
1249 Ok(())
1250 }
1251
1252 fn unregister_watcher(self: Arc<Self>, key: usize) {
1253 self.watchers.lock().remove(key);
1254 }
1255}
1256
1257impl From<Directory<FxVolume>> for FxDirectory {
1258 fn from(dir: Directory<FxVolume>) -> Self {
1259 Self::new(None, dir)
1260 }
1261}
1262
1263#[cfg(test)]
1264mod tests {
1265 use crate::directory::FxDirectory;
1266 use crate::file::FxFile;
1267 use crate::fuchsia::testing::{
1268 TestFixture, TestFixtureOptions, close_dir_checked, close_file_checked, open_dir,
1269 open_dir_checked, open_file, open_file_checked,
1270 };
1271 use anyhow::bail;
1272 use assert_matches::assert_matches;
1273 use fidl::endpoints::{ClientEnd, Proxy, create_proxy};
1274 use fidl_fuchsia_io as fio;
1275 use fuchsia_async as fasync;
1276 use fuchsia_fs::directory::{DirEntry, DirentKind, WatchEvent, WatchMessage, Watcher};
1277 use fuchsia_fs::file;
1278 use futures::{StreamExt, join};
1279 use fxfs::lsm_tree::Query;
1280 use fxfs::lsm_tree::types::{ItemRef, LayerIterator};
1281 use fxfs::object_store::transaction::{LockKey, lock_keys};
1282 use fxfs::object_store::{ObjectKey, ObjectKeyData, ObjectValue, Timestamp};
1283 use fxfs_crypt_common::CryptBase;
1284 use fxfs_crypto::{FSCRYPT_PADDING, WrappingKeyId};
1285 use std::future::poll_fn;
1286 use std::os::fd::AsRawFd;
1287 use std::sync::Arc;
1288 use std::sync::atomic::{AtomicU64, Ordering};
1289 use std::task::Poll;
1290 use std::time::Duration;
1291 use storage_device::DeviceHolder;
1292 use storage_device::fake_device::FakeDevice;
1293 use vfs::ObjectRequest;
1294 use vfs::node::Node;
1295 use vfs::path::Path;
1296
1297 const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(2);
1298
1299 async fn yield_to_executor() {
1300 let mut done = false;
1301 poll_fn(|cx| {
1302 if done {
1303 Poll::Ready(())
1304 } else {
1305 done = true;
1306 cx.waker().wake_by_ref();
1307 Poll::Pending
1308 }
1309 })
1310 .await;
1311 }
1312
1313 #[fuchsia::test]
1314 async fn test_open_root_dir() {
1315 let fixture = TestFixture::new().await;
1316 let root = fixture.root();
1317 let _: Vec<_> = root.query().await.expect("query failed");
1318 fixture.close().await;
1319 }
1320
1321 #[fuchsia::test]
1322 async fn test_create_dir_persists() {
1323 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1324 for i in 0..2 {
1325 let fixture = TestFixture::open(
1326 device,
1327 TestFixtureOptions { format: i == 0, ..Default::default() },
1328 )
1329 .await;
1330 let root = fixture.root();
1331
1332 let flags = if i == 0 {
1333 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE
1334 } else {
1335 fio::PERM_READABLE
1336 };
1337 let dir = open_dir_checked(
1338 &root,
1339 "foo",
1340 flags | fio::Flags::PROTOCOL_DIRECTORY,
1341 Default::default(),
1342 )
1343 .await;
1344 close_dir_checked(dir).await;
1345
1346 device = fixture.close().await;
1347 }
1348 }
1349
1350 #[fuchsia::test]
1351 async fn test_open_nonexistent_file() {
1352 let fixture = TestFixture::new().await;
1353 let root = fixture.root();
1354
1355 assert_eq!(
1356 open_file(
1357 &root,
1358 "foo",
1359 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1360 &Default::default()
1361 )
1362 .await
1363 .expect_err("Open succeeded")
1364 .root_cause()
1365 .downcast_ref::<zx::Status>()
1366 .expect("No status"),
1367 &zx::Status::NOT_FOUND,
1368 );
1369
1370 fixture.close().await;
1371 }
1372
1373 #[fuchsia::test]
1374 async fn test_create_file() {
1375 let fixture = TestFixture::new().await;
1376 let root = fixture.root();
1377
1378 let f = open_file_checked(
1379 &root,
1380 "foo",
1381 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1382 &Default::default(),
1383 )
1384 .await;
1385 close_file_checked(f).await;
1386
1387 let f = open_file_checked(
1388 &root,
1389 "foo",
1390 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1391 &Default::default(),
1392 )
1393 .await;
1394 close_file_checked(f).await;
1395
1396 fixture.close().await;
1397 }
1398
1399 #[fuchsia::test]
1400 async fn test_create_dir_nested() {
1401 let fixture = TestFixture::new().await;
1402 let root = fixture.root();
1403
1404 let d = open_dir_checked(
1405 &root,
1406 "foo",
1407 fio::Flags::FLAG_MAYBE_CREATE
1408 | fio::PERM_READABLE
1409 | fio::PERM_WRITABLE
1410 | fio::Flags::PROTOCOL_DIRECTORY,
1411 Default::default(),
1412 )
1413 .await;
1414 close_dir_checked(d).await;
1415
1416 let d = open_dir_checked(
1417 &root,
1418 "foo/bar",
1419 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
1420 Default::default(),
1421 )
1422 .await;
1423 close_dir_checked(d).await;
1424
1425 let d = open_dir_checked(
1426 &root,
1427 "foo/bar",
1428 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
1429 Default::default(),
1430 )
1431 .await;
1432 close_dir_checked(d).await;
1433
1434 fixture.close().await;
1435 }
1436
1437 #[fuchsia::test]
1438 async fn test_strict_create_file_fails_if_present() {
1439 let fixture = TestFixture::new().await;
1440 let root = fixture.root();
1441
1442 let f = open_file_checked(
1443 &root,
1444 "foo",
1445 fio::Flags::FLAG_MAYBE_CREATE
1446 | fio::Flags::FLAG_MUST_CREATE
1447 | fio::PERM_READABLE
1448 | fio::Flags::PROTOCOL_FILE,
1449 &Default::default(),
1450 )
1451 .await;
1452 close_file_checked(f).await;
1453
1454 assert_eq!(
1455 open_file(
1456 &root,
1457 "foo",
1458 fio::Flags::FLAG_MAYBE_CREATE
1459 | fio::Flags::FLAG_MUST_CREATE
1460 | fio::PERM_READABLE
1461 | fio::Flags::PROTOCOL_FILE,
1462 &Default::default()
1463 )
1464 .await
1465 .expect_err("Open succeeded")
1466 .root_cause()
1467 .downcast_ref::<zx::Status>()
1468 .expect("No status"),
1469 &zx::Status::ALREADY_EXISTS,
1470 );
1471
1472 fixture.close().await;
1473 }
1474
1475 #[fuchsia::test]
1476 async fn test_unlink_file_with_no_refs_immediately_freed() {
1477 let fixture = TestFixture::new().await;
1478 let root = fixture.root();
1479
1480 let file = open_file_checked(
1481 &root,
1482 "foo",
1483 fio::Flags::FLAG_MAYBE_CREATE
1484 | fio::PERM_READABLE
1485 | fio::PERM_WRITABLE
1486 | fio::Flags::PROTOCOL_FILE,
1487 &Default::default(),
1488 )
1489 .await;
1490
1491 let buf = vec![0xaa as u8; 512];
1493 loop {
1494 match file::write(&file, buf.as_slice()).await {
1495 Ok(_) => {}
1496 Err(e) => {
1497 if let fuchsia_fs::file::WriteError::WriteError(status) = e {
1498 if status == zx::Status::NO_SPACE {
1499 break;
1500 }
1501 }
1502 panic!("Unexpected write error {:?}", e);
1503 }
1504 }
1505 }
1506
1507 close_file_checked(file).await;
1508
1509 root.unlink("foo", &fio::UnlinkOptions::default())
1510 .await
1511 .expect("FIDL call failed")
1512 .expect("unlink failed");
1513
1514 assert_eq!(
1515 open_file(
1516 &root,
1517 "foo",
1518 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1519 &Default::default()
1520 )
1521 .await
1522 .expect_err("Open succeeded")
1523 .root_cause()
1524 .downcast_ref::<zx::Status>()
1525 .expect("No status"),
1526 &zx::Status::NOT_FOUND,
1527 );
1528
1529 let file = open_file_checked(
1531 &root,
1532 "bar",
1533 fio::Flags::FLAG_MAYBE_CREATE
1534 | fio::PERM_READABLE
1535 | fio::PERM_WRITABLE
1536 | fio::Flags::PROTOCOL_FILE,
1537 &Default::default(),
1538 )
1539 .await;
1540 let buf = vec![0xaa as u8; 8192];
1541 file::write(&file, buf.as_slice()).await.expect("Failed to write new file");
1542 close_file_checked(file).await;
1543
1544 fixture.close().await;
1545 }
1546
1547 #[fuchsia::test]
1548 async fn test_unlink_file() {
1549 let fixture = TestFixture::new().await;
1550 let root = fixture.root();
1551
1552 let file = open_file_checked(
1553 &root,
1554 "foo",
1555 fio::Flags::FLAG_MAYBE_CREATE
1556 | fio::PERM_READABLE
1557 | fio::PERM_WRITABLE
1558 | fio::Flags::PROTOCOL_FILE,
1559 &Default::default(),
1560 )
1561 .await;
1562 close_file_checked(file).await;
1563
1564 root.unlink("foo", &fio::UnlinkOptions::default())
1565 .await
1566 .expect("FIDL call failed")
1567 .expect("unlink failed");
1568
1569 assert_eq!(
1570 open_file(
1571 &root,
1572 "foo",
1573 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1574 &Default::default()
1575 )
1576 .await
1577 .expect_err("Open succeeded")
1578 .root_cause()
1579 .downcast_ref::<zx::Status>()
1580 .expect("No status"),
1581 &zx::Status::NOT_FOUND,
1582 );
1583
1584 fixture.close().await;
1585 }
1586
1587 #[fuchsia::test]
1588 async fn test_unlink_file_with_active_references() {
1589 let fixture = TestFixture::new().await;
1590 let root = fixture.root();
1591
1592 let file = open_file_checked(
1593 &root,
1594 "foo",
1595 fio::Flags::FLAG_MAYBE_CREATE
1596 | fio::PERM_READABLE
1597 | fio::PERM_WRITABLE
1598 | fio::Flags::PROTOCOL_FILE,
1599 &Default::default(),
1600 )
1601 .await;
1602
1603 let buf = vec![0xaa as u8; 512];
1604 file::write(&file, buf.as_slice()).await.expect("write failed");
1605
1606 root.unlink("foo", &fio::UnlinkOptions::default())
1607 .await
1608 .expect("FIDL call failed")
1609 .expect("unlink failed");
1610
1611 assert_eq!(
1613 open_file(
1614 &root,
1615 "foo",
1616 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1617 &Default::default()
1618 )
1619 .await
1620 .expect_err("Open succeeded")
1621 .root_cause()
1622 .downcast_ref::<zx::Status>()
1623 .expect("No status"),
1624 &zx::Status::NOT_FOUND,
1625 );
1626
1627 file.seek(fio::SeekOrigin::Start, 0)
1629 .await
1630 .expect("seek failed")
1631 .map_err(zx::Status::err_from_raw)
1632 .expect("seek error");
1633 let rbuf = file::read(&file).await.expect("read failed");
1634 assert_eq!(rbuf, buf);
1635 close_file_checked(file).await;
1636
1637 fixture.close().await;
1638 }
1639
1640 #[fuchsia::test]
1641 async fn test_unlink_dir_with_children_fails() {
1642 let fixture = TestFixture::new().await;
1643 let root = fixture.root();
1644
1645 let dir = open_dir_checked(
1646 &root,
1647 "foo",
1648 fio::Flags::FLAG_MAYBE_CREATE
1649 | fio::PERM_READABLE
1650 | fio::PERM_WRITABLE
1651 | fio::Flags::PROTOCOL_DIRECTORY,
1652 Default::default(),
1653 )
1654 .await;
1655 let f = open_file_checked(
1656 &dir,
1657 "bar",
1658 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE,
1659 &Default::default(),
1660 )
1661 .await;
1662 close_file_checked(f).await;
1663
1664 assert_eq!(
1665 zx::Status::ok(
1666 root.unlink("foo", &fio::UnlinkOptions::default())
1667 .await
1668 .expect("FIDL call failed")
1669 .expect_err("unlink succeeded")
1670 ),
1671 Err(zx::Status::NOT_EMPTY)
1672 );
1673
1674 dir.unlink("bar", &fio::UnlinkOptions::default())
1675 .await
1676 .expect("FIDL call failed")
1677 .expect("unlink failed");
1678 root.unlink("foo", &fio::UnlinkOptions::default())
1679 .await
1680 .expect("FIDL call failed")
1681 .expect("unlink failed");
1682
1683 close_dir_checked(dir).await;
1684
1685 fixture.close().await;
1686 }
1687
1688 #[fuchsia::test]
1689 async fn test_unlink_dir_makes_directory_immutable() {
1690 let fixture = TestFixture::new().await;
1691 let root = fixture.root();
1692
1693 let dir = open_dir_checked(
1694 &root,
1695 "foo",
1696 fio::Flags::FLAG_MAYBE_CREATE
1697 | fio::PERM_READABLE
1698 | fio::PERM_WRITABLE
1699 | fio::Flags::PROTOCOL_DIRECTORY,
1700 Default::default(),
1701 )
1702 .await;
1703
1704 root.unlink("foo", &fio::UnlinkOptions::default())
1705 .await
1706 .expect("FIDL call failed")
1707 .expect("unlink failed");
1708
1709 assert_eq!(
1710 open_file(
1711 &dir,
1712 "bar",
1713 fio::PERM_READABLE | fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1714 &Default::default()
1715 )
1716 .await
1717 .expect_err("Create file succeeded")
1718 .root_cause()
1719 .downcast_ref::<zx::Status>()
1720 .expect("No status"),
1721 &zx::Status::ACCESS_DENIED,
1722 );
1723
1724 close_dir_checked(dir).await;
1725
1726 fixture.close().await;
1727 }
1728
1729 #[fuchsia::test(threads = 10)]
1730 async fn test_unlink_directory_with_children_race() {
1731 let fixture = TestFixture::new().await;
1732 let root = fixture.root();
1733
1734 const PARENT: &str = "foo";
1735 const CHILD: &str = "bar";
1736 const GRANDCHILD: &str = "baz";
1737 open_dir_checked(
1738 &root,
1739 PARENT,
1740 fio::Flags::FLAG_MAYBE_CREATE
1741 | fio::PERM_READABLE
1742 | fio::PERM_WRITABLE
1743 | fio::Flags::PROTOCOL_DIRECTORY,
1744 Default::default(),
1745 )
1746 .await;
1747
1748 let open_parent = || async {
1749 open_dir_checked(
1750 &root,
1751 PARENT,
1752 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1753 Default::default(),
1754 )
1755 .await
1756 };
1757 let parent = open_parent().await;
1758
1759 for _ in 0..100 {
1765 let d = open_dir_checked(
1766 &parent,
1767 CHILD,
1768 fio::Flags::FLAG_MAYBE_CREATE
1769 | fio::PERM_READABLE
1770 | fio::PERM_WRITABLE
1771 | fio::Flags::PROTOCOL_DIRECTORY,
1772 Default::default(),
1773 )
1774 .await;
1775 close_dir_checked(d).await;
1776
1777 let parent = open_parent().await;
1778 let deleter = fasync::Task::spawn(async move {
1779 let wait_time = rand::random_range(0..5);
1780 fasync::Timer::new(Duration::from_millis(wait_time)).await;
1781 match parent
1782 .unlink(CHILD, &fio::UnlinkOptions::default())
1783 .await
1784 .expect("FIDL call failed")
1785 .map_err(zx::Status::err_from_raw)
1786 {
1787 Ok(()) => {}
1788 Err(zx::Status::NOT_EMPTY) => {}
1789 Err(e) => panic!("Unexpected status from unlink: {:?}", e),
1790 };
1791 close_dir_checked(parent).await;
1792 });
1793
1794 let parent = open_parent().await;
1795 let writer = fasync::Task::spawn(async move {
1796 let child_or = open_dir(
1797 &parent,
1798 CHILD,
1799 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1800 &Default::default(),
1801 )
1802 .await;
1803 if let Err(e) = &child_or {
1804 assert_eq!(
1806 e.root_cause().downcast_ref::<zx::Status>().expect("No status"),
1807 &zx::Status::NOT_FOUND
1808 );
1809 close_dir_checked(parent).await;
1810 return;
1811 }
1812 let child = child_or.unwrap();
1813 let _: Vec<_> = child.query().await.expect("query failed");
1814 match open_file(
1815 &child,
1816 GRANDCHILD,
1817 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1818 &Default::default(),
1819 )
1820 .await
1821 {
1822 Ok(grandchild) => {
1823 let _: Vec<_> = grandchild.query().await.expect("query failed");
1824 close_file_checked(grandchild).await;
1825 child
1828 .unlink(GRANDCHILD, &fio::UnlinkOptions::default())
1829 .await
1830 .expect("FIDL call failed")
1831 .expect("unlink failed");
1832 }
1833 Err(e) => {
1834 assert_eq!(
1837 e.root_cause().downcast_ref::<zx::Status>().expect("No status"),
1838 &zx::Status::ACCESS_DENIED,
1839 );
1840 }
1841 };
1842 close_dir_checked(child).await;
1843 close_dir_checked(parent).await;
1844 });
1845 writer.await;
1846 deleter.await;
1847 }
1848
1849 close_dir_checked(parent).await;
1850 fixture.close().await;
1851 }
1852
1853 #[fuchsia::test]
1854 async fn test_readdir() {
1855 let fixture = TestFixture::new().await;
1856 let root = fixture.root();
1857
1858 let open_dir = || {
1859 open_dir_checked(
1860 &root,
1861 "foo",
1862 fio::Flags::FLAG_MAYBE_CREATE
1863 | fio::PERM_READABLE
1864 | fio::PERM_WRITABLE
1865 | fio::Flags::PROTOCOL_DIRECTORY,
1866 Default::default(),
1867 )
1868 };
1869 let parent = Arc::new(open_dir().await);
1870
1871 let files = ["eenie", "meenie", "minie", "moe"];
1872 for file in &files {
1873 let file = open_file_checked(
1874 parent.as_ref(),
1875 file,
1876 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1877 &Default::default(),
1878 )
1879 .await;
1880 close_file_checked(file).await;
1881 }
1882 let dirs = ["fee", "fi", "fo", "fum"];
1883 for dir in &dirs {
1884 let dir = open_dir_checked(
1885 parent.as_ref(),
1886 dir,
1887 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
1888 Default::default(),
1889 )
1890 .await;
1891 close_dir_checked(dir).await;
1892 }
1893 {
1894 parent
1895 .create_symlink("symlink", b"target", None)
1896 .await
1897 .expect("FIDL call failed")
1898 .expect("create_symlink failed");
1899 }
1900
1901 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
1902 let status = dir.rewind().await.expect("FIDL call failed");
1903 zx::Status::ok(status).expect("rewind failed");
1904 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
1905 zx::Status::ok(status).expect("read_dirents failed");
1906 let mut entries = vec![];
1907 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
1908 entries.push(res.expect("Failed to parse entry"));
1909 }
1910 entries
1911 };
1912
1913 let mut expected_entries =
1914 vec![DirEntry { name: ".".to_owned(), kind: DirentKind::Directory }];
1915 expected_entries.extend(
1916 files.iter().map(|&name| DirEntry { name: name.to_owned(), kind: DirentKind::File }),
1917 );
1918 expected_entries.extend(
1919 dirs.iter()
1920 .map(|&name| DirEntry { name: name.to_owned(), kind: DirentKind::Directory }),
1921 );
1922 expected_entries.push(DirEntry { name: "symlink".to_owned(), kind: DirentKind::Symlink });
1923 expected_entries.sort_unstable();
1924 assert_eq!(expected_entries, readdir(Arc::clone(&parent)).await);
1925
1926 parent
1928 .unlink(&expected_entries.pop().unwrap().name, &fio::UnlinkOptions::default())
1929 .await
1930 .expect("FIDL call failed")
1931 .expect("unlink failed");
1932
1933 assert_eq!(expected_entries, readdir(Arc::clone(&parent)).await);
1934
1935 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
1936 fixture.close().await;
1937 }
1938
1939 #[fuchsia::test]
1940 async fn test_readdir_multiple_calls() {
1941 let fixture = TestFixture::new().await;
1942 let root = fixture.root();
1943
1944 let parent = open_dir_checked(
1945 &root,
1946 "foo",
1947 fio::Flags::FLAG_MAYBE_CREATE
1948 | fio::PERM_READABLE
1949 | fio::PERM_WRITABLE
1950 | fio::Flags::PROTOCOL_DIRECTORY,
1951 Default::default(),
1952 )
1953 .await;
1954
1955 let files = ["a", "b"];
1956 for file in &files {
1957 let file = open_file_checked(
1958 &parent,
1959 file,
1960 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1961 &Default::default(),
1962 )
1963 .await;
1964 close_file_checked(file).await;
1965 }
1966
1967 const DIRENT_SIZE: u64 = 10; const BUFFER_SIZE: u64 = DIRENT_SIZE + 2; let parse_entries = |buf| {
1972 let mut entries = vec![];
1973 for res in fuchsia_fs::directory::parse_dir_entries(buf) {
1974 entries.push(res.expect("Failed to parse entry"));
1975 }
1976 entries
1977 };
1978
1979 let expected_entries = vec![
1980 DirEntry { name: ".".to_owned(), kind: DirentKind::Directory },
1981 DirEntry { name: "a".to_owned(), kind: DirentKind::File },
1982 ];
1983 let (status, buf) = parent.read_dirents(2 * BUFFER_SIZE).await.expect("FIDL call failed");
1984 zx::Status::ok(status).expect("read_dirents failed");
1985 assert_eq!(expected_entries, parse_entries(&buf));
1986
1987 let expected_entries = vec![DirEntry { name: "b".to_owned(), kind: DirentKind::File }];
1988 let (status, buf) = parent.read_dirents(2 * BUFFER_SIZE).await.expect("FIDL call failed");
1989 zx::Status::ok(status).expect("read_dirents failed");
1990 assert_eq!(expected_entries, parse_entries(&buf));
1991
1992 let expected_entries: Vec<DirEntry> = vec![];
1994 let (status, buf) = parent.read_dirents(2 * BUFFER_SIZE).await.expect("FIDL call failed");
1995 zx::Status::ok(status).expect("read_dirents failed");
1996 assert_eq!(expected_entries, parse_entries(&buf));
1997
1998 close_dir_checked(parent).await;
1999 fixture.close().await;
2000 }
2001
2002 #[fuchsia::test]
2003 async fn test_set_large_extended_attribute_on_encrypted_directory() {
2004 let fixture = TestFixture::new().await;
2005 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2006 let root = fixture.root();
2007 let open_dir = || {
2008 open_dir_checked(
2009 &root,
2010 "foo",
2011 fio::Flags::FLAG_MAYBE_CREATE
2012 | fio::PERM_READABLE
2013 | fio::PERM_WRITABLE
2014 | fio::Flags::PROTOCOL_DIRECTORY,
2015 Default::default(),
2016 )
2017 };
2018
2019 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2020 crypt
2021 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2022 .expect("Failed to add wrapping key");
2023 parent
2024 .update_attributes(&fio::MutableNodeAttributes {
2025 encryption_policy: Some(fio::FscryptPolicy {
2026 key_identifier: WRAPPING_KEY_ID,
2027 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2028 }),
2029 ..Default::default()
2030 })
2031 .await
2032 .expect("FIDL call failed")
2033 .map_err(zx::ok)
2034 .expect("update_attributes failed");
2035 let dir = open_dir_checked(
2036 parent.as_ref(),
2037 "fee",
2038 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2039 Default::default(),
2040 )
2041 .await;
2042
2043 let xattr_name = b"xattr_name";
2044 let value_vec = vec![0x3; 300];
2045
2046 dir.set_extended_attribute(
2047 xattr_name,
2048 fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
2049 fio::SetExtendedAttributeMode::Set,
2050 )
2051 .await
2052 .expect("Failed to make FIDL call")
2053 .expect("Failed to set xattr with create");
2054
2055 let subdir = open_dir_checked(
2056 &dir,
2057 "fo",
2058 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
2059 Default::default(),
2060 )
2061 .await;
2062 close_dir_checked(dir).await;
2063 close_dir_checked(subdir).await;
2064 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2065 let device = fixture.close().await;
2066 let new_fixture = TestFixture::new_with_device(device).await;
2067 let root = new_fixture.root();
2068 let open_dir = || {
2069 open_dir_checked(
2070 &root,
2071 "foo",
2072 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2073 Default::default(),
2074 )
2075 };
2076 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2077
2078 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2079 let status = dir.rewind().await.expect("FIDL call failed");
2080 zx::Status::ok(status).expect("rewind failed");
2081 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2082 zx::Status::ok(status).expect("read_dirents failed");
2083 let mut entries = vec![];
2084 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2085 entries.push(res.expect("Failed to parse entry"));
2086 }
2087 entries
2088 };
2089
2090 let encrypted_entries = readdir(Arc::clone(&parent)).await;
2091 let mut encrypted_name = String::new();
2092 for entry in encrypted_entries {
2093 if entry.name == ".".to_owned() {
2094 continue;
2095 } else {
2096 assert!(entry.name.len() >= FSCRYPT_PADDING);
2097 encrypted_name = entry.name;
2098 assert!(entry.kind == DirentKind::Directory)
2099 }
2100 }
2101
2102 let encrypted_dir = Arc::new(
2103 open_dir_checked(
2104 parent.as_ref(),
2105 &encrypted_name,
2106 fio::Flags::PROTOCOL_DIRECTORY | fio::PERM_READABLE,
2107 Default::default(),
2108 )
2109 .await,
2110 );
2111
2112 assert_eq!(
2113 encrypted_dir
2114 .get_extended_attribute(xattr_name)
2115 .await
2116 .expect("Failed to make FIDL call")
2117 .expect("Failed to get extended attribute"),
2118 fio::ExtendedAttributeValue::Bytes(value_vec)
2119 );
2120
2121 let encrypted_subdir_entries = readdir(Arc::clone(&encrypted_dir)).await;
2122 for entry in encrypted_subdir_entries {
2123 if entry.name == ".".to_owned() {
2124 continue;
2125 } else {
2126 assert!(entry.name.len() >= FSCRYPT_PADDING);
2127 assert!(entry.kind == DirentKind::Directory)
2128 }
2129 }
2130 close_dir_checked(Arc::try_unwrap(encrypted_dir).unwrap()).await;
2131 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2132 new_fixture.close().await;
2133 }
2134
2135 #[fuchsia::test]
2136 async fn test_set_large_extended_attribute_on_encrypted_file() {
2137 let fixture = TestFixture::new().await;
2138 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2139 let root = fixture.root();
2140 let open_dir = || {
2141 open_dir_checked(
2142 &root,
2143 "foo",
2144 fio::Flags::FLAG_MAYBE_CREATE
2145 | fio::PERM_READABLE
2146 | fio::PERM_WRITABLE
2147 | fio::Flags::PROTOCOL_DIRECTORY,
2148 Default::default(),
2149 )
2150 };
2151
2152 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2153 crypt
2154 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2155 .expect("Failed to add wrapping key");
2156 parent
2157 .update_attributes(&fio::MutableNodeAttributes {
2158 encryption_policy: Some(fio::FscryptPolicy {
2159 key_identifier: WRAPPING_KEY_ID,
2160 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2161 }),
2162 ..Default::default()
2163 })
2164 .await
2165 .expect("FIDL call failed")
2166 .map_err(zx::ok)
2167 .expect("update_attributes failed");
2168 let file = open_file_checked(
2169 parent.as_ref(),
2170 "fee",
2171 fio::Flags::FLAG_MAYBE_CREATE
2172 | fio::PERM_READABLE
2173 | fio::PERM_WRITABLE
2174 | fio::Flags::PROTOCOL_FILE,
2175 &Default::default(),
2176 )
2177 .await;
2178
2179 let buf = vec![0xaa as u8; 512];
2180 file::write(&file, buf.as_slice()).await.expect("write failed");
2181
2182 let xattr_name = b"xattr_name";
2183 let value_vec = vec![0x3; 300];
2184
2185 file.set_extended_attribute(
2186 xattr_name,
2187 fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
2188 fio::SetExtendedAttributeMode::Set,
2189 )
2190 .await
2191 .expect("Failed to make FIDL call")
2192 .expect("Failed to set xattr with create");
2193
2194 close_file_checked(file).await;
2195 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2196 let device = fixture.close().await;
2197 let new_fixture = TestFixture::new_with_device(device).await;
2198 let crypt: Arc<CryptBase> = new_fixture.crypt().unwrap();
2199 let root = new_fixture.root();
2200 let open_dir = || {
2201 open_dir_checked(
2202 &root,
2203 "foo",
2204 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2205 Default::default(),
2206 )
2207 };
2208 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2209
2210 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2211 let status = dir.rewind().await.expect("FIDL call failed");
2212 zx::Status::ok(status).expect("rewind failed");
2213 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2214 zx::Status::ok(status).expect("read_dirents failed");
2215 let mut entries = vec![];
2216 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2217 entries.push(res.expect("Failed to parse entry"));
2218 }
2219 entries
2220 };
2221
2222 let encrypted_entries = readdir(Arc::clone(&parent)).await;
2223 let mut encrypted_name = String::new();
2224 for entry in encrypted_entries {
2225 if entry.name == ".".to_owned() {
2226 continue;
2227 } else {
2228 assert!(entry.name.len() >= FSCRYPT_PADDING);
2229 encrypted_name = entry.name;
2230 assert!(entry.kind == DirentKind::File)
2231 }
2232 }
2233
2234 let encrypted_file = Arc::new(
2235 open_file_checked(
2236 parent.as_ref(),
2237 &encrypted_name,
2238 fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE,
2239 &Default::default(),
2240 )
2241 .await,
2242 );
2243
2244 assert_eq!(
2245 encrypted_file
2246 .get_extended_attribute(xattr_name)
2247 .await
2248 .expect("Failed to make FIDL call")
2249 .expect("Failed to get extended attribute"),
2250 fio::ExtendedAttributeValue::Bytes(value_vec)
2251 );
2252
2253 close_file_checked(Arc::try_unwrap(encrypted_file).unwrap()).await;
2254
2255 crypt
2256 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2257 .expect("Failed to add wrapping key");
2258
2259 let file = Arc::new(
2260 open_file_checked(
2261 parent.as_ref(),
2262 "fee",
2263 fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE,
2264 &Default::default(),
2265 )
2266 .await,
2267 );
2268
2269 let rbuf = file::read(&file).await.expect("read failed");
2270 assert_eq!(rbuf, buf);
2271
2272 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2273 new_fixture.close().await;
2274 }
2275
2276 #[fuchsia::test]
2277 async fn test_encrypt_directory_in_unencrypted_volume() {
2278 let fixture = TestFixture::new_unencrypted().await;
2279 let root = fixture.root();
2280 let open_dir = || {
2281 open_dir_checked(
2282 &root,
2283 "foo",
2284 fio::Flags::FLAG_MAYBE_CREATE
2285 | fio::PERM_READABLE
2286 | fio::PERM_WRITABLE
2287 | fio::Flags::PROTOCOL_DIRECTORY,
2288 Default::default(),
2289 )
2290 };
2291
2292 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2293 let _ = parent
2294 .update_attributes(&fio::MutableNodeAttributes {
2295 encryption_policy: Some(fio::FscryptPolicy {
2296 key_identifier: WRAPPING_KEY_ID,
2297 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2298 }),
2299 ..Default::default()
2300 })
2301 .await
2302 .expect("FIDL call failed")
2303 .map_err(zx::ok)
2304 .expect_err("encrypting a dir in an unencrypted volume should fail");
2305 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2306 fixture.close().await;
2307 }
2308
2309 #[fuchsia::test]
2310 async fn test_encrypt_directory_with_large_extended_attribute() {
2311 let fixture = TestFixture::new().await;
2312 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2313 let root = fixture.root();
2314 let open_dir = || {
2315 open_dir_checked(
2316 &root,
2317 "foo",
2318 fio::Flags::FLAG_MAYBE_CREATE
2319 | fio::PERM_READABLE
2320 | fio::PERM_WRITABLE
2321 | fio::Flags::PROTOCOL_DIRECTORY,
2322 Default::default(),
2323 )
2324 };
2325
2326 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2327
2328 let xattr_name = b"xattr_name";
2329 let value_vec = vec![0x3; 300];
2330 parent
2331 .set_extended_attribute(
2332 xattr_name,
2333 fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
2334 fio::SetExtendedAttributeMode::Set,
2335 )
2336 .await
2337 .expect("Failed to make FIDL call")
2338 .expect("Failed to set xattr with create");
2339
2340 crypt
2341 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2342 .expect("Failed to add wrapping key");
2343 parent
2344 .update_attributes(&fio::MutableNodeAttributes {
2345 encryption_policy: Some(fio::FscryptPolicy {
2346 key_identifier: WRAPPING_KEY_ID,
2347 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2348 }),
2349 ..Default::default()
2350 })
2351 .await
2352 .expect("FIDL call failed")
2353 .map_err(zx::ok)
2354 .expect("update_attributes failed");
2355 let dir = open_dir_checked(
2356 parent.as_ref(),
2357 "fee",
2358 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2359 Default::default(),
2360 )
2361 .await;
2362
2363 let subdir = open_dir_checked(
2364 &dir,
2365 "fo",
2366 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
2367 Default::default(),
2368 )
2369 .await;
2370 close_dir_checked(dir).await;
2371 close_dir_checked(subdir).await;
2372 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2373 let device = fixture.close().await;
2374 let new_fixture = TestFixture::new_with_device(device).await;
2375 let root = new_fixture.root();
2376 let open_dir = || {
2377 open_dir_checked(
2378 &root,
2379 "foo",
2380 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2381 Default::default(),
2382 )
2383 };
2384 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2385
2386 assert_eq!(
2387 parent
2388 .get_extended_attribute(xattr_name)
2389 .await
2390 .expect("Failed to make FIDL call")
2391 .expect("Failed to get extended attribute"),
2392 fio::ExtendedAttributeValue::Bytes(value_vec)
2393 );
2394
2395 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2396 let status = dir.rewind().await.expect("FIDL call failed");
2397 zx::Status::ok(status).expect("rewind failed");
2398 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2399 zx::Status::ok(status).expect("read_dirents failed");
2400 let mut entries = vec![];
2401 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2402 entries.push(res.expect("Failed to parse entry"));
2403 }
2404 entries
2405 };
2406
2407 let encrypted_entries = readdir(Arc::clone(&parent)).await;
2408 let mut encrypted_name = None;
2409 for entry in encrypted_entries {
2410 if &entry.name == "." {
2411 continue;
2412 } else {
2413 assert!(entry.name.len() >= FSCRYPT_PADDING);
2414 assert!(encrypted_name.replace(entry.name).is_none());
2415 assert!(entry.kind == DirentKind::Directory)
2416 }
2417 }
2418
2419 let encrypted_dir = Arc::new(
2420 open_dir_checked(
2421 parent.as_ref(),
2422 &encrypted_name.as_ref().unwrap(),
2423 fio::Flags::PROTOCOL_DIRECTORY | fio::Flags::PERM_ENUMERATE,
2424 Default::default(),
2425 )
2426 .await,
2427 );
2428
2429 let encrypted_subdir_entries = readdir(Arc::clone(&encrypted_dir)).await;
2430 for entry in encrypted_subdir_entries {
2431 if &entry.name == "." {
2432 continue;
2433 } else {
2434 assert!(entry.name.len() >= FSCRYPT_PADDING);
2435 assert!(entry.kind == DirentKind::Directory)
2436 }
2437 }
2438 close_dir_checked(Arc::try_unwrap(encrypted_dir).unwrap()).await;
2439 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2440 new_fixture.close().await;
2441 }
2442
2443 #[fuchsia::test]
2444 async fn test_unlock_directory_during_readdir() {
2445 let fixture = TestFixture::new().await;
2446 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2447 let root = fixture.root();
2448 let open_dir = || {
2449 open_dir_checked(
2450 &root,
2451 "foo",
2452 fio::Flags::FLAG_MAYBE_CREATE
2453 | fio::PERM_READABLE
2454 | fio::PERM_WRITABLE
2455 | fio::Flags::PROTOCOL_DIRECTORY,
2456 Default::default(),
2457 )
2458 };
2459
2460 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2461 crypt
2462 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2463 .expect("Failed to add wrapping key");
2464 parent
2465 .update_attributes(&fio::MutableNodeAttributes {
2466 encryption_policy: Some(fio::FscryptPolicy {
2467 key_identifier: WRAPPING_KEY_ID,
2468 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2469 }),
2470 ..Default::default()
2471 })
2472 .await
2473 .expect("FIDL call failed")
2474 .map_err(zx::ok)
2475 .expect("update_attributes failed");
2476
2477 for i in 0..300 {
2480 let dir = open_dir_checked(
2481 parent.as_ref(),
2482 &format!("plaintext_{}", i),
2483 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2484 Default::default(),
2485 )
2486 .await;
2487 close_dir_checked(dir).await;
2488 }
2489
2490 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2491 let device = fixture.close().await;
2492 let new_fixture = TestFixture::new_with_device(device).await;
2493 let crypt: Arc<CryptBase> = new_fixture.crypt().unwrap();
2494 let root = new_fixture.root();
2495 let open_dir = || {
2496 open_dir_checked(
2497 &root,
2498 "foo",
2499 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2500 Default::default(),
2501 )
2502 };
2503 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2504
2505 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2506 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2507 zx::Status::ok(status).expect("read_dirents failed");
2508 let mut entries = vec![];
2509 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2510 entries.push(res.expect("Failed to parse entry"));
2511 }
2512 entries
2513 };
2514
2515 let encrypted_entries = readdir(Arc::clone(&parent)).await;
2516 for entry in encrypted_entries {
2517 if entry.name == ".".to_owned() {
2518 continue;
2519 } else {
2520 assert!(entry.name.len() >= FSCRYPT_PADDING);
2521 assert!(!entry.name.starts_with("plaintext_"), "{entry:?} isn't encrypted!");
2522 assert!(entry.kind == DirentKind::Directory)
2523 }
2524 }
2525 crypt
2526 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2527 .expect("Failed to add wrapping key");
2528 let unencrypted_entries = readdir(Arc::clone(&parent)).await;
2529 for entry in unencrypted_entries {
2530 if entry.name == ".".to_owned() {
2531 continue;
2532 } else {
2533 assert!(entry.name.starts_with("plaintext_"), "{entry:?} is still encrypted!");
2534 assert!(entry.kind == DirentKind::Directory)
2535 }
2536 }
2537
2538 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2539 new_fixture.close().await;
2540 }
2541
2542 #[fuchsia::test]
2543 async fn test_readdir_locked_directory() {
2544 let fixture = TestFixture::new().await;
2545 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2546 let root = fixture.root();
2547 let open_dir = || {
2548 open_dir_checked(
2549 &root,
2550 "foo",
2551 fio::Flags::FLAG_MAYBE_CREATE
2552 | fio::PERM_READABLE
2553 | fio::PERM_WRITABLE
2554 | fio::Flags::PROTOCOL_DIRECTORY,
2555 Default::default(),
2556 )
2557 };
2558
2559 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2560 crypt
2561 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2562 .expect("Failed to add wrapping key");
2563 parent
2564 .update_attributes(&fio::MutableNodeAttributes {
2565 encryption_policy: Some(fio::FscryptPolicy {
2566 key_identifier: WRAPPING_KEY_ID,
2567 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2568 }),
2569 ..Default::default()
2570 })
2571 .await
2572 .expect("FIDL call failed")
2573 .map_err(zx::ok)
2574 .expect("update_attributes failed");
2575 let dir = open_dir_checked(
2576 parent.as_ref(),
2577 "fee",
2578 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2579 Default::default(),
2580 )
2581 .await;
2582
2583 let subdir = open_dir_checked(
2584 &dir,
2585 "fo",
2586 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
2587 Default::default(),
2588 )
2589 .await;
2590 close_dir_checked(dir).await;
2591 close_dir_checked(subdir).await;
2592
2593 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2594 let status = dir.rewind().await.expect("FIDL call failed");
2595 zx::Status::ok(status).expect("rewind failed");
2596 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2597 zx::Status::ok(status).expect("read_dirents failed");
2598 let mut entries = vec![];
2599 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2600 entries.push(res.expect("Failed to parse entry"));
2601 }
2602 entries
2603 };
2604
2605 let mut expected_entries =
2606 vec![DirEntry { name: ".".to_owned(), kind: DirentKind::Directory }];
2607
2608 expected_entries.push(DirEntry { name: "fee".to_owned(), kind: DirentKind::Directory });
2609 expected_entries.sort_unstable();
2610 assert_eq!(expected_entries, readdir(Arc::clone(&parent)).await);
2611
2612 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2613 let device = fixture.close().await;
2614 let new_fixture = TestFixture::new_with_device(device).await;
2615 let root = new_fixture.root();
2616 let open_dir = || {
2617 open_dir_checked(
2618 &root,
2619 "foo",
2620 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2621 Default::default(),
2622 )
2623 };
2624 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2625
2626 let encrypted_entries = readdir(Arc::clone(&parent)).await;
2627 let mut encrypted_name = String::new();
2628 for entry in encrypted_entries {
2629 if entry.name == ".".to_owned() {
2630 continue;
2631 } else {
2632 assert!(entry.name.len() >= FSCRYPT_PADDING);
2633 encrypted_name = entry.name;
2634 assert!(entry.kind == DirentKind::Directory)
2635 }
2636 }
2637
2638 let encrypted_dir = Arc::new(
2639 open_dir_checked(
2640 parent.as_ref(),
2641 &encrypted_name,
2642 fio::Flags::PROTOCOL_DIRECTORY | fio::Flags::PERM_ENUMERATE,
2643 Default::default(),
2644 )
2645 .await,
2646 );
2647
2648 let encrypted_subdir_entries = readdir(Arc::clone(&encrypted_dir)).await;
2649 for entry in encrypted_subdir_entries {
2650 if entry.name == ".".to_owned() {
2651 continue;
2652 } else {
2653 assert!(entry.name.len() >= FSCRYPT_PADDING);
2654 assert!(entry.kind == DirentKind::Directory)
2655 }
2656 }
2657 close_dir_checked(Arc::try_unwrap(encrypted_dir).unwrap()).await;
2658 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2659 new_fixture.close().await;
2660 }
2661
2662 #[fuchsia::test]
2663 async fn test_link_into_locked_directory_fails() {
2664 let fixture = TestFixture::new().await;
2665 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2666 let root = fixture.root();
2667 let open_dir_1 = || {
2668 open_dir_checked(
2669 &root,
2670 "foo",
2671 fio::Flags::FLAG_MAYBE_CREATE
2672 | fio::PERM_READABLE
2673 | fio::PERM_WRITABLE
2674 | fio::Flags::PROTOCOL_DIRECTORY,
2675 Default::default(),
2676 )
2677 };
2678
2679 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2680
2681 let open_dir_2 = || {
2682 open_dir_checked(
2683 &root,
2684 "foo_2",
2685 fio::Flags::FLAG_MAYBE_CREATE
2686 | fio::PERM_READABLE
2687 | fio::PERM_WRITABLE
2688 | fio::Flags::PROTOCOL_DIRECTORY,
2689 Default::default(),
2690 )
2691 };
2692 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2693
2694 crypt
2695 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2696 .expect("Failed to add wrapping key");
2697 parent_1
2698 .update_attributes(&fio::MutableNodeAttributes {
2699 encryption_policy: Some(fio::FscryptPolicy {
2700 key_identifier: WRAPPING_KEY_ID,
2701 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2702 }),
2703 ..Default::default()
2704 })
2705 .await
2706 .expect("FIDL call failed")
2707 .map_err(zx::ok)
2708 .expect("update_attributes failed");
2709 parent_2
2710 .update_attributes(&fio::MutableNodeAttributes {
2711 encryption_policy: Some(fio::FscryptPolicy {
2712 key_identifier: WRAPPING_KEY_ID,
2713 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2714 }),
2715 ..Default::default()
2716 })
2717 .await
2718 .expect("FIDL call failed")
2719 .map_err(zx::ok)
2720 .expect("update_attributes failed");
2721 let file = open_file_checked(
2722 parent_1.as_ref(),
2723 "fee",
2724 fio::Flags::FLAG_MAYBE_CREATE,
2725 &Default::default(),
2726 )
2727 .await;
2728
2729 close_file_checked(file).await;
2730 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2731 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2732
2733 let device = fixture.close().await;
2734 let new_fixture = TestFixture::new_with_device(device).await;
2735 let root = new_fixture.root();
2736 let open_dir_1 = || {
2737 open_dir_checked(
2738 &root,
2739 "foo",
2740 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2741 Default::default(),
2742 )
2743 };
2744 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2745
2746 let open_dir_2 = || {
2747 open_dir_checked(
2748 &root,
2749 "foo_2",
2750 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2751 Default::default(),
2752 )
2753 };
2754 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2755
2756 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2757 let status = dir.rewind().await.expect("FIDL call failed");
2758 zx::Status::ok(status).expect("rewind failed");
2759 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2760 zx::Status::ok(status).expect("read_dirents failed");
2761 let mut entries = vec![];
2762 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2763 entries.push(res.expect("Failed to parse entry"));
2764 }
2765 entries
2766 };
2767
2768 let encrypted_entries = readdir(Arc::clone(&parent_1)).await;
2769 let mut encrypted_name = String::new();
2770 for entry in encrypted_entries {
2771 if entry.name == ".".to_owned() {
2772 continue;
2773 } else {
2774 assert!(entry.name.len() >= FSCRYPT_PADDING);
2775 encrypted_name = entry.name;
2776 assert!(entry.kind == DirentKind::File)
2777 }
2778 }
2779
2780 let (status, parent_2_token) =
2781 parent_2.deprecated_get_token().await.expect("get token failed");
2782 zx::Status::ok(status).unwrap();
2783
2784 assert_eq!(
2785 parent_1
2786 .link(&encrypted_name, parent_2_token.unwrap().into(), "file_2")
2787 .await
2788 .expect("FIDL transport error"),
2789 zx::Status::ACCESS_DENIED.into_raw()
2790 );
2791
2792 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2793 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2794 new_fixture.close().await;
2795 }
2796
2797 #[fuchsia::test]
2798 async fn test_rename_in_locked_directory_fails() {
2799 let fixture = TestFixture::new().await;
2800 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2801 let root = fixture.root();
2802 let open_dir_1 = || {
2803 open_dir_checked(
2804 &root,
2805 "foo",
2806 fio::Flags::FLAG_MAYBE_CREATE
2807 | fio::PERM_READABLE
2808 | fio::PERM_WRITABLE
2809 | fio::Flags::PROTOCOL_DIRECTORY,
2810 Default::default(),
2811 )
2812 };
2813
2814 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2815
2816 let open_dir_2 = || {
2817 open_dir_checked(
2818 &root,
2819 "foo_2",
2820 fio::Flags::FLAG_MAYBE_CREATE
2821 | fio::PERM_READABLE
2822 | fio::PERM_WRITABLE
2823 | fio::Flags::PROTOCOL_DIRECTORY,
2824 Default::default(),
2825 )
2826 };
2827 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2828
2829 crypt
2830 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2831 .expect("Failed to add wrapping key");
2832 parent_1
2833 .update_attributes(&fio::MutableNodeAttributes {
2834 encryption_policy: Some(fio::FscryptPolicy {
2835 key_identifier: WRAPPING_KEY_ID,
2836 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2837 }),
2838 ..Default::default()
2839 })
2840 .await
2841 .expect("FIDL call failed")
2842 .map_err(zx::ok)
2843 .expect("update_attributes failed");
2844 parent_2
2845 .update_attributes(&fio::MutableNodeAttributes {
2846 encryption_policy: Some(fio::FscryptPolicy {
2847 key_identifier: WRAPPING_KEY_ID,
2848 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2849 }),
2850 ..Default::default()
2851 })
2852 .await
2853 .expect("FIDL call failed")
2854 .map_err(zx::ok)
2855 .expect("update_attributes failed");
2856 let file = open_file_checked(
2857 parent_1.as_ref(),
2858 "fee",
2859 fio::Flags::FLAG_MAYBE_CREATE,
2860 &Default::default(),
2861 )
2862 .await;
2863
2864 close_file_checked(file).await;
2865 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2866 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2867
2868 let device = fixture.close().await;
2869 let new_fixture = TestFixture::new_with_device(device).await;
2870 let root = new_fixture.root();
2871 let open_dir_1 = || {
2872 open_dir_checked(
2873 &root,
2874 "foo",
2875 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2876 Default::default(),
2877 )
2878 };
2879 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2880
2881 let open_dir_2 = || {
2882 open_dir_checked(
2883 &root,
2884 "foo_2",
2885 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2886 Default::default(),
2887 )
2888 };
2889 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2890
2891 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2892 let status = dir.rewind().await.expect("FIDL call failed");
2893 zx::Status::ok(status).expect("rewind failed");
2894 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2895 zx::Status::ok(status).expect("read_dirents failed");
2896 let mut entries = vec![];
2897 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2898 entries.push(res.expect("Failed to parse entry"));
2899 }
2900 entries
2901 };
2902
2903 let encrypted_entries = readdir(Arc::clone(&parent_1)).await;
2904 let mut encrypted_name = String::new();
2905 for entry in encrypted_entries {
2906 if entry.name == ".".to_owned() {
2907 continue;
2908 } else {
2909 assert!(entry.name.len() >= FSCRYPT_PADDING);
2910 encrypted_name = entry.name;
2911 assert!(entry.kind == DirentKind::File)
2912 }
2913 }
2914
2915 let parent_2_token =
2916 parent_2.get_token().await.expect("get token failed").expect("get_token failed");
2917
2918 assert_eq!(
2920 parent_1
2921 .rename(&encrypted_name, parent_2_token, "file_2")
2922 .await
2923 .expect("FIDL transport error"),
2924 Err(zx::Status::ACCESS_DENIED.into_raw())
2925 );
2926
2927 let parent_1_token =
2928 parent_1.get_token().await.expect("get token failed").expect("get_token failed");
2929
2930 assert_eq!(
2932 parent_1
2933 .rename(&encrypted_name, parent_1_token, "file_2")
2934 .await
2935 .expect("FIDL transport error"),
2936 Err(zx::Status::ACCESS_DENIED.into_raw())
2937 );
2938
2939 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2940 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2941 new_fixture.close().await;
2942 }
2943
2944 #[fuchsia::test]
2945 async fn test_link_encrypted_file_into_directory_encrypted_with_different_key_fails() {
2946 let fixture = TestFixture::new().await;
2947 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2948 let root = fixture.root();
2949 let open_dir_1 = || {
2950 open_dir_checked(
2951 &root,
2952 "foo",
2953 fio::Flags::FLAG_MAYBE_CREATE
2954 | fio::PERM_READABLE
2955 | fio::PERM_WRITABLE
2956 | fio::Flags::PROTOCOL_DIRECTORY,
2957 Default::default(),
2958 )
2959 };
2960
2961 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2962
2963 let open_dir_2 = || {
2964 open_dir_checked(
2965 &root,
2966 "foo_2",
2967 fio::Flags::FLAG_MAYBE_CREATE
2968 | fio::PERM_READABLE
2969 | fio::PERM_WRITABLE
2970 | fio::Flags::PROTOCOL_DIRECTORY,
2971 Default::default(),
2972 )
2973 };
2974 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2975
2976 crypt
2977 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2978 .expect("Failed to add wrapping key");
2979
2980 const WRAPPING_KEY_ID_2: WrappingKeyId = u128::to_le_bytes(3);
2981 crypt
2982 .add_wrapping_key(WRAPPING_KEY_ID_2, [2; 32].into())
2983 .expect("Failed to add wrapping key");
2984
2985 parent_1
2986 .update_attributes(&fio::MutableNodeAttributes {
2987 encryption_policy: Some(fio::FscryptPolicy {
2988 key_identifier: WRAPPING_KEY_ID,
2989 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
2990 }),
2991 ..Default::default()
2992 })
2993 .await
2994 .expect("FIDL call failed")
2995 .map_err(zx::ok)
2996 .expect("update_attributes failed");
2997 parent_2
2998 .update_attributes(&fio::MutableNodeAttributes {
2999 encryption_policy: Some(fio::FscryptPolicy {
3000 key_identifier: WRAPPING_KEY_ID_2,
3001 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
3002 }),
3003 ..Default::default()
3004 })
3005 .await
3006 .expect("FIDL call failed")
3007 .map_err(zx::ok)
3008 .expect("update_attributes failed");
3009 let file = open_file_checked(
3010 parent_1.as_ref(),
3011 "fee",
3012 fio::Flags::FLAG_MAYBE_CREATE,
3013 &Default::default(),
3014 )
3015 .await;
3016
3017 close_file_checked(file).await;
3018
3019 let (status, parent_2_token) =
3020 parent_2.deprecated_get_token().await.expect("get token failed");
3021 zx::Status::ok(status).unwrap();
3022
3023 assert_eq!(
3024 parent_1
3025 .link("fee", parent_2_token.unwrap().into(), "file_2")
3026 .await
3027 .expect("FIDL transport error"),
3028 zx::Status::BAD_STATE.into_raw()
3029 );
3030 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
3031 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
3032 fixture.close().await;
3033 }
3034
3035 #[fuchsia::test]
3036 async fn test_link_unencrypted_file_into_encrypted_directory_fails() {
3037 let fixture = TestFixture::new().await;
3038 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3039 let root = fixture.root();
3040 let open_dir_1 = || {
3041 open_dir_checked(
3042 &root,
3043 "foo",
3044 fio::Flags::FLAG_MAYBE_CREATE
3045 | fio::PERM_READABLE
3046 | fio::PERM_WRITABLE
3047 | fio::Flags::PROTOCOL_DIRECTORY,
3048 Default::default(),
3049 )
3050 };
3051
3052 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
3053
3054 let open_dir_2 = || {
3055 open_dir_checked(
3056 &root,
3057 "foo_2",
3058 fio::Flags::FLAG_MAYBE_CREATE
3059 | fio::PERM_READABLE
3060 | fio::PERM_WRITABLE
3061 | fio::Flags::PROTOCOL_DIRECTORY,
3062 Default::default(),
3063 )
3064 };
3065 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
3066
3067 crypt
3068 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3069 .expect("Failed to add wrapping key");
3070
3071 parent_1
3072 .update_attributes(&fio::MutableNodeAttributes {
3073 encryption_policy: Some(fio::FscryptPolicy {
3074 key_identifier: WRAPPING_KEY_ID,
3075 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
3076 }),
3077 ..Default::default()
3078 })
3079 .await
3080 .expect("FIDL call failed")
3081 .map_err(zx::ok)
3082 .expect("update_attributes failed");
3083
3084 let file = open_file_checked(
3085 parent_2.as_ref(),
3086 "fee",
3087 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
3088 &Default::default(),
3089 )
3090 .await;
3091
3092 close_file_checked(file).await;
3093
3094 let (status, parent_1_token) =
3095 parent_1.deprecated_get_token().await.expect("get token failed");
3096 zx::Status::ok(status).unwrap();
3097
3098 assert_eq!(
3099 parent_2
3100 .link("fee", parent_1_token.unwrap().into(), "file")
3101 .await
3102 .expect("FIDL transport error"),
3103 zx::Status::BAD_STATE.into_raw()
3104 );
3105 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
3106 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
3107 fixture.close().await;
3108 }
3109
3110 #[fuchsia::test]
3111 async fn test_link_locked_directory_into_unencrypted_dir() {
3112 let fixture = TestFixture::new().await;
3113 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3114 let root = fixture.root();
3115 let open_dir_1 = || {
3116 open_dir_checked(
3117 &root,
3118 "foo",
3119 fio::Flags::FLAG_MAYBE_CREATE
3120 | fio::PERM_READABLE
3121 | fio::PERM_WRITABLE
3122 | fio::Flags::PROTOCOL_DIRECTORY,
3123 Default::default(),
3124 )
3125 };
3126
3127 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
3128
3129 let open_dir_2 = || {
3130 open_dir_checked(
3131 &root,
3132 "foo_2",
3133 fio::Flags::FLAG_MAYBE_CREATE
3134 | fio::PERM_READABLE
3135 | fio::PERM_WRITABLE
3136 | fio::Flags::PROTOCOL_DIRECTORY,
3137 Default::default(),
3138 )
3139 };
3140 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
3141
3142 crypt
3143 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3144 .expect("Failed to add wrapping key");
3145 parent_1
3146 .update_attributes(&fio::MutableNodeAttributes {
3147 encryption_policy: Some(fio::FscryptPolicy {
3148 key_identifier: WRAPPING_KEY_ID,
3149 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
3150 }),
3151 ..Default::default()
3152 })
3153 .await
3154 .expect("FIDL call failed")
3155 .map_err(zx::ok)
3156 .expect("update_attributes failed");
3157 let file = open_file_checked(
3158 parent_1.as_ref(),
3159 "fee",
3160 fio::Flags::FLAG_MAYBE_CREATE
3161 | fio::PERM_READABLE
3162 | fio::PERM_WRITABLE
3163 | fio::Flags::PROTOCOL_FILE,
3164 &Default::default(),
3165 )
3166 .await;
3167 let _ = file
3168 .write(&[8; 8192])
3169 .await
3170 .expect("FIDL call failed")
3171 .map_err(zx::Status::err_from_raw)
3172 .expect("write failed");
3173
3174 close_file_checked(file).await;
3175 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
3176 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
3177
3178 let device = fixture.close().await;
3179 let new_fixture = TestFixture::new_with_device(device).await;
3180 let root = new_fixture.root();
3181 let open_dir_1 = || {
3182 open_dir_checked(
3183 &root,
3184 "foo",
3185 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3186 Default::default(),
3187 )
3188 };
3189 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
3190
3191 let open_dir_2 = || {
3192 open_dir_checked(
3193 &root,
3194 "foo_2",
3195 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3196 Default::default(),
3197 )
3198 };
3199 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
3200
3201 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
3202 let status = dir.rewind().await.expect("FIDL call failed");
3203 zx::Status::ok(status).expect("rewind failed");
3204 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3205 zx::Status::ok(status).expect("read_dirents failed");
3206 let mut entries = vec![];
3207 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3208 entries.push(res.expect("Failed to parse entry"));
3209 }
3210 entries
3211 };
3212
3213 let encrypted_entries = readdir(Arc::clone(&parent_1)).await;
3214 let mut encrypted_name = String::new();
3215 for entry in encrypted_entries {
3216 if entry.name == ".".to_owned() {
3217 continue;
3218 } else {
3219 assert!(entry.name.len() >= FSCRYPT_PADDING);
3220 encrypted_name = entry.name;
3221 assert!(entry.kind == DirentKind::File)
3222 }
3223 }
3224
3225 let (status, parent_2_token) =
3226 parent_2.deprecated_get_token().await.expect("get token failed");
3227 zx::Status::ok(status).unwrap();
3228
3229 assert_eq!(
3230 parent_1
3231 .link(&encrypted_name, parent_2_token.unwrap().into(), "file_2")
3232 .await
3233 .expect("FIDL transport error"),
3234 zx::sys::ZX_OK
3235 );
3236
3237 let file =
3238 open_file_checked(parent_2.as_ref(), "file_2", fio::PERM_READABLE, &Default::default())
3239 .await;
3240 let (mutable_attributes, _immutable_attributes) = file
3241 .get_attributes(
3242 fio::NodeAttributesQuery::CONTENT_SIZE
3243 | fio::NodeAttributesQuery::STORAGE_SIZE
3244 | fio::NodeAttributesQuery::LINK_COUNT
3245 | fio::NodeAttributesQuery::MODIFICATION_TIME
3246 | fio::NodeAttributesQuery::CHANGE_TIME
3247 | fio::NodeAttributesQuery::ENCRYPTION_POLICY,
3248 )
3249 .await
3250 .expect("FIDL call failed")
3251 .map_err(zx::Status::err_from_raw)
3252 .expect("get_attributes failed");
3253 assert_eq!(
3254 mutable_attributes.encryption_policy.map(|p| p.key_identifier),
3255 Some(WRAPPING_KEY_ID)
3256 );
3257 assert_eq!(
3258 file.read(fio::MAX_BUF)
3259 .await
3260 .expect("FIDL call failed")
3261 .expect_err("reading an encrypted file should fail"),
3262 zx::Status::BAD_STATE.into_raw()
3263 );
3264
3265 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
3266 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
3267 new_fixture.close().await;
3268 }
3269
3270 #[fuchsia::test]
3271 async fn test_encrypted_filename_does_not_have_slashes() {
3272 let fixture = TestFixture::new().await;
3273 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3274 let root = fixture.root();
3275 let open_dir = || {
3276 open_dir_checked(
3277 &root,
3278 "foo",
3279 fio::Flags::FLAG_MAYBE_CREATE
3280 | fio::PERM_READABLE
3281 | fio::PERM_WRITABLE
3282 | fio::Flags::PROTOCOL_DIRECTORY,
3283 Default::default(),
3284 )
3285 };
3286
3287 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3288 crypt
3289 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3290 .expect("Failed to add wrapping key");
3291 parent
3292 .update_attributes(&fio::MutableNodeAttributes {
3293 encryption_policy: Some(fio::FscryptPolicy {
3294 key_identifier: WRAPPING_KEY_ID,
3295 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
3296 }),
3297 ..Default::default()
3298 })
3299 .await
3300 .expect("FIDL call failed")
3301 .map_err(zx::ok)
3302 .expect("update_attributes failed");
3303 const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
3304 for _ in 0..100 {
3305 let one_char = || CHARSET[rand::random_range(0..CHARSET.len())] as char;
3306 let filename: String = std::iter::repeat_with(one_char).take(100).collect();
3307 let dir = open_dir_checked(
3308 parent.as_ref(),
3309 &filename,
3310 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3311 Default::default(),
3312 )
3313 .await;
3314 close_dir_checked(dir).await;
3315 }
3316
3317 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3318 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
3319 let status = dir.rewind().await.expect("FIDL call failed");
3320 zx::Status::ok(status).expect("rewind failed");
3321 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3322 zx::Status::ok(status).expect("read_dirents failed");
3323 let mut entries = vec![];
3324 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3325 entries.push(res.expect("Failed to parse entry"));
3326 }
3327 entries
3328 };
3329
3330 let device = fixture.close().await;
3331 let new_fixture = TestFixture::new_with_device(device).await;
3332 let root = new_fixture.root();
3333 let open_dir = || {
3334 open_dir_checked(
3335 &root,
3336 "foo",
3337 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
3338 Default::default(),
3339 )
3340 };
3341 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3342
3343 let encrypted_entries = readdir(Arc::clone(&parent)).await;
3344 for entry in encrypted_entries {
3345 if entry.name == ".".to_owned() {
3346 continue;
3347 } else {
3348 assert!(entry.name.len() >= FSCRYPT_PADDING);
3349 assert!(!entry.name.contains("/"));
3350 assert!(entry.kind == DirentKind::Directory)
3351 }
3352 }
3353
3354 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3355 new_fixture.close().await;
3356 }
3357
3358 #[fuchsia::test]
3359 async fn test_stat_locked_file() {
3360 let fixture = TestFixture::new().await;
3361 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3362 let root = fixture.root();
3363 let open_dir = || {
3364 open_dir_checked(
3365 &root,
3366 "foo",
3367 fio::Flags::FLAG_MAYBE_CREATE
3368 | fio::PERM_READABLE
3369 | fio::PERM_WRITABLE
3370 | fio::Flags::PROTOCOL_DIRECTORY,
3371 Default::default(),
3372 )
3373 };
3374 let parent = Arc::new(open_dir().await);
3375 crypt
3376 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3377 .expect("Failed to add wrapping key");
3378 parent
3379 .update_attributes(&fio::MutableNodeAttributes {
3380 encryption_policy: Some(fio::FscryptPolicy {
3381 key_identifier: WRAPPING_KEY_ID,
3382 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
3383 }),
3384 ..Default::default()
3385 })
3386 .await
3387 .expect("FIDL call failed")
3388 .map_err(zx::ok)
3389 .expect("update_attributes failed");
3390
3391 let file = open_file_checked(
3392 parent.as_ref(),
3393 "file",
3394 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
3395 &Default::default(),
3396 )
3397 .await;
3398
3399 close_file_checked(file).await;
3400 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3401
3402 let device = fixture.close().await;
3403 let new_fixture = TestFixture::new_with_device(device).await;
3404 let root = new_fixture.root();
3405 let open_dir = || {
3406 open_dir_checked(
3407 &root,
3408 "foo",
3409 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
3410 Default::default(),
3411 )
3412 };
3413 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3414 let (status, buf) = parent.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3415 zx::Status::ok(status).expect("read_dirents failed");
3416 let mut encrypted_entries = vec![];
3417 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3418 encrypted_entries.push(res.expect("Failed to parse entry"));
3419 }
3420 let mut encrypted_name = String::new();
3421 for entry in encrypted_entries {
3422 if entry.name == ".".to_owned() {
3423 continue;
3424 } else {
3425 assert!(entry.name.len() >= FSCRYPT_PADDING);
3426 encrypted_name = entry.name;
3427 assert!(entry.kind == DirentKind::File)
3428 }
3429 }
3430
3431 let file = open_file_checked(
3432 parent.as_ref(),
3433 &encrypted_name,
3434 fio::Flags::PROTOCOL_FILE | fio::Flags::PERM_GET_ATTRIBUTES,
3435 &Default::default(),
3436 )
3437 .await;
3438 let (_mutable_attributes, _immutable_attributes) = file
3439 .get_attributes(
3440 fio::NodeAttributesQuery::CONTENT_SIZE
3441 | fio::NodeAttributesQuery::STORAGE_SIZE
3442 | fio::NodeAttributesQuery::LINK_COUNT
3443 | fio::NodeAttributesQuery::MODIFICATION_TIME
3444 | fio::NodeAttributesQuery::CHANGE_TIME,
3445 )
3446 .await
3447 .expect("FIDL call failed")
3448 .map_err(zx::Status::err_from_raw)
3449 .expect("get_attributes failed");
3450 close_file_checked(file).await;
3451 new_fixture.close().await;
3452 }
3453
3454 #[fuchsia::test]
3455 async fn test_unlink_locked_directory() {
3456 let fixture = TestFixture::new().await;
3457 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3458 let root = fixture.root();
3459 let open_dir = || {
3460 open_dir_checked(
3461 &root,
3462 "foo",
3463 fio::Flags::FLAG_MAYBE_CREATE
3464 | fio::PERM_READABLE
3465 | fio::PERM_WRITABLE
3466 | fio::Flags::PROTOCOL_DIRECTORY,
3467 Default::default(),
3468 )
3469 };
3470
3471 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3472 crypt
3473 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3474 .expect("Failed to add wrapping key");
3475 parent
3476 .update_attributes(&fio::MutableNodeAttributes {
3477 encryption_policy: Some(fio::FscryptPolicy {
3478 key_identifier: WRAPPING_KEY_ID,
3479 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
3480 }),
3481 ..Default::default()
3482 })
3483 .await
3484 .expect("FIDL call failed")
3485 .map_err(zx::ok)
3486 .expect("update_attributes failed");
3487 let dir = open_dir_checked(
3488 parent.as_ref(),
3489 "fee",
3490 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3491 Default::default(),
3492 )
3493 .await;
3494
3495 close_dir_checked(dir).await;
3496 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3497 let device = fixture.close().await;
3498 let new_fixture = TestFixture::new_with_device(device).await;
3499 let root = new_fixture.root();
3500 let open_dir = || {
3501 open_dir_checked(
3502 &root,
3503 "foo",
3504 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3505 Default::default(),
3506 )
3507 };
3508 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3509
3510 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
3511 let status = dir.rewind().await.expect("FIDL call failed");
3512 zx::Status::ok(status).expect("rewind failed");
3513 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3514 zx::Status::ok(status).expect("read_dirents failed");
3515 let mut entries = vec![];
3516 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3517 entries.push(res.expect("Failed to parse entry"));
3518 }
3519 entries
3520 };
3521
3522 let encrypted_entries = readdir(Arc::clone(&parent)).await;
3523 let mut encrypted_name = String::new();
3524 for entry in encrypted_entries {
3525 if entry.name == ".".to_owned() {
3526 continue;
3527 } else {
3528 assert!(entry.name.len() >= FSCRYPT_PADDING);
3529 encrypted_name = entry.name;
3530 assert!(entry.kind == DirentKind::Directory)
3531 }
3532 }
3533
3534 parent
3535 .unlink(&encrypted_name, &fio::UnlinkOptions::default())
3536 .await
3537 .expect("FIDL call failed")
3538 .expect("unlink failed");
3539
3540 let encrypted_entries = readdir(Arc::clone(&parent)).await;
3541 let mut count = 0;
3542 for entry in encrypted_entries {
3543 if entry.name == ".".to_owned() {
3544 continue;
3545 } else {
3546 assert!(entry.name.len() >= FSCRYPT_PADDING);
3547 assert!(entry.kind == DirentKind::Directory)
3548 }
3549 count += 1;
3550 }
3551 assert_eq!(count, 0);
3552 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3553 new_fixture.close().await;
3554 }
3555
3556 #[fuchsia::test]
3557 async fn test_rename_within_locked_encrypted_directory() {
3558 let fixture = TestFixture::new().await;
3559 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3560 let root = fixture.root();
3561 let open_dir = || {
3562 open_dir_checked(
3563 &root,
3564 "foo",
3565 fio::Flags::FLAG_MAYBE_CREATE
3566 | fio::PERM_READABLE
3567 | fio::PERM_WRITABLE
3568 | fio::Flags::PROTOCOL_DIRECTORY,
3569 Default::default(),
3570 )
3571 };
3572
3573 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3574 crypt
3575 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3576 .expect("Failed to add wrapping key");
3577 parent
3578 .update_attributes(&fio::MutableNodeAttributes {
3579 encryption_policy: Some(fio::FscryptPolicy {
3580 key_identifier: WRAPPING_KEY_ID,
3581 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
3582 }),
3583 ..Default::default()
3584 })
3585 .await
3586 .expect("FIDL call failed")
3587 .map_err(zx::ok)
3588 .expect("update_attributes failed");
3589 let dir = open_dir_checked(
3590 parent.as_ref(),
3591 "fee",
3592 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3593 Default::default(),
3594 )
3595 .await;
3596
3597 close_dir_checked(dir).await;
3598 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3599 let device = fixture.close().await;
3600 let new_fixture = TestFixture::new_with_device(device).await;
3601 let crypt: Arc<CryptBase> = new_fixture.crypt().unwrap();
3602 let root = new_fixture.root();
3603 let open_dir = || {
3604 open_dir_checked(
3605 &root,
3606 "foo",
3607 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3608 Default::default(),
3609 )
3610 };
3611 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3612
3613 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
3614 let status = dir.rewind().await.expect("FIDL call failed");
3615 zx::Status::ok(status).expect("rewind failed");
3616 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3617 zx::Status::ok(status).expect("read_dirents failed");
3618 let mut entries = vec![];
3619 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3620 entries.push(res.expect("Failed to parse entry"));
3621 }
3622 entries
3623 };
3624
3625 let encrypted_entries = readdir(Arc::clone(&parent)).await;
3626 let mut encrypted_name = String::new();
3627 for entry in encrypted_entries {
3628 if entry.name == ".".to_owned() {
3629 continue;
3630 } else {
3631 assert!(entry.name.len() >= FSCRYPT_PADDING);
3632 encrypted_name = entry.name;
3633 assert!(entry.kind == DirentKind::Directory)
3634 }
3635 }
3636
3637 let (status, dst_token) = parent.deprecated_get_token().await.expect("FIDL call failed");
3638 zx::Status::ok(status).expect("get_token failed");
3639 let new_encrypted_name = "aabbcc";
3640 parent
3641 .rename(&encrypted_name, zx::Event::from(dst_token.unwrap()), new_encrypted_name)
3642 .await
3643 .expect("FIDL call failed")
3644 .expect_err("rename should fail on a locked directory");
3645 let (status, dst_token) = parent.deprecated_get_token().await.expect("FIDL call failed");
3646 zx::Status::ok(status).expect("get_token failed");
3647 crypt
3648 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3649 .expect("Failed to add wrapping key");
3650 parent
3651 .rename("fee", zx::Event::from(dst_token.unwrap()), "new_fee")
3652 .await
3653 .expect("FIDL call failed")
3654 .expect("rename should fail on a locked directory");
3655
3656 let _dir = open_dir_checked(
3657 parent.as_ref(),
3658 "new_fee",
3659 fio::Flags::PROTOCOL_DIRECTORY,
3660 Default::default(),
3661 )
3662 .await;
3663 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3664 new_fixture.close().await;
3665 }
3666
3667 #[fuchsia::test]
3668 async fn test_link_symlink_into_encrypted_directory() {
3669 let fixture = TestFixture::new().await;
3670 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3671 let root = fixture.root();
3672 let open_dir = || {
3673 open_dir_checked(
3674 &root,
3675 "foo",
3676 fio::Flags::FLAG_MAYBE_CREATE
3677 | fio::PERM_READABLE
3678 | fio::PERM_WRITABLE
3679 | fio::Flags::PROTOCOL_DIRECTORY,
3680 Default::default(),
3681 )
3682 };
3683 let parent = Arc::new(open_dir().await);
3684 crypt
3685 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3686 .expect("Failed to add wrapping key");
3687 parent
3688 .update_attributes(&fio::MutableNodeAttributes {
3689 encryption_policy: Some(fio::FscryptPolicy {
3690 key_identifier: WRAPPING_KEY_ID,
3691 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
3692 }),
3693 ..Default::default()
3694 })
3695 .await
3696 .expect("FIDL call failed")
3697 .map_err(zx::ok)
3698 .expect("update_attributes failed");
3699
3700 {
3701 parent
3702 .create_symlink("symlink", b"target", None)
3703 .await
3704 .expect("FIDL call failed")
3705 .expect("create_symlink failed");
3706
3707 async fn open_symlink(root: &fio::DirectoryProxy, path: &str) -> fio::SymlinkProxy {
3708 let (proxy, server_end) = create_proxy::<fio::SymlinkMarker>();
3709 root.open(
3710 path,
3711 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::FLAG_SEND_REPRESENTATION,
3712 &Default::default(),
3713 server_end.into_channel(),
3714 )
3715 .expect("open failed");
3716
3717 let representation = proxy
3718 .take_event_stream()
3719 .next()
3720 .await
3721 .expect("missing Symlink event")
3722 .expect("failed to read Symlink event")
3723 .into_on_representation()
3724 .expect("failed to decode OnRepresentation");
3725
3726 assert_matches!(representation,
3727 fio::Representation::Symlink(fio::SymlinkInfo{
3728 target: Some(target), ..
3729 }) if target == b"target"
3730 );
3731
3732 proxy
3733 }
3734
3735 let proxy = open_symlink(&parent, "symlink").await;
3736
3737 let (status, dst_token) =
3738 parent.deprecated_get_token().await.expect("FIDL call failed");
3739 zx::Status::ok(status).expect("get_token failed");
3740 proxy
3741 .link_into(zx::Event::from(dst_token.unwrap()), "symlink2")
3742 .await
3743 .expect("link_into (FIDL) failed")
3744 .expect("link_into failed");
3745
3746 open_symlink(&parent, "symlink2").await;
3747 }
3748
3749 fixture.close().await;
3750 }
3751
3752 #[fuchsia::test]
3753 async fn test_link_symlink_into_locked_directory_fails() {
3754 let fixture = TestFixture::new().await;
3755 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3756 let root = fixture.root();
3757 let open_dir = || {
3758 open_dir_checked(
3759 &root,
3760 "foo",
3761 fio::Flags::FLAG_MAYBE_CREATE
3762 | fio::PERM_READABLE
3763 | fio::PERM_WRITABLE
3764 | fio::Flags::PROTOCOL_DIRECTORY,
3765 Default::default(),
3766 )
3767 };
3768 let parent = Arc::new(open_dir().await);
3769 crypt
3770 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3771 .expect("Failed to add wrapping key");
3772 parent
3773 .update_attributes(&fio::MutableNodeAttributes {
3774 encryption_policy: Some(fio::FscryptPolicy {
3775 key_identifier: WRAPPING_KEY_ID,
3776 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
3777 }),
3778 ..Default::default()
3779 })
3780 .await
3781 .expect("FIDL call failed")
3782 .map_err(zx::ok)
3783 .expect("update_attributes failed");
3784 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3785
3786 let device = fixture.close().await;
3787 let new_fixture = TestFixture::new_with_device(device).await;
3788 let root = new_fixture.root();
3789 let open_dir = || {
3790 open_dir_checked(
3791 &root,
3792 "foo",
3793 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3794 Default::default(),
3795 )
3796 };
3797 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3798 {
3799 root.create_symlink("symlink", b"target", None)
3800 .await
3801 .expect("FIDL call failed")
3802 .expect("create_symlink failed");
3803
3804 async fn open_symlink(root: &fio::DirectoryProxy, path: &str) -> fio::SymlinkProxy {
3805 let (proxy, server_end) = create_proxy::<fio::SymlinkMarker>();
3806 root.open(
3807 path,
3808 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::FLAG_SEND_REPRESENTATION,
3809 &Default::default(),
3810 server_end.into_channel(),
3811 )
3812 .expect("open failed");
3813
3814 let representation = proxy
3815 .take_event_stream()
3816 .next()
3817 .await
3818 .expect("missing Symlink event")
3819 .expect("failed to read Symlink event")
3820 .into_on_representation()
3821 .expect("failed to decode OnRepresentation");
3822
3823 assert_matches!(representation,
3824 fio::Representation::Symlink(fio::SymlinkInfo{
3825 target: Some(target), ..
3826 }) if target == b"target"
3827 );
3828
3829 proxy
3830 }
3831
3832 let proxy = open_symlink(&root, "symlink").await;
3833
3834 let (status, dst_token) =
3835 parent.deprecated_get_token().await.expect("FIDL call failed");
3836 zx::Status::ok(status).expect("get_token failed");
3837 proxy
3838 .link_into(zx::Event::from(dst_token.unwrap()), "symlink2")
3839 .await
3840 .expect("link_into (FIDL) failed")
3841 .expect_err("linking into a locked directory should fail");
3842 }
3843
3844 new_fixture.close().await;
3845 }
3846
3847 #[fuchsia::test]
3848 async fn test_stat_locked_symlink() {
3849 let fixture = TestFixture::new().await;
3850 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3851 let root = fixture.root();
3852 let open_dir = || {
3853 open_dir_checked(
3854 &root,
3855 "foo",
3856 fio::Flags::FLAG_MAYBE_CREATE
3857 | fio::PERM_READABLE
3858 | fio::PERM_WRITABLE
3859 | fio::Flags::PROTOCOL_DIRECTORY,
3860 Default::default(),
3861 )
3862 };
3863 let parent = Arc::new(open_dir().await);
3864 crypt
3865 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3866 .expect("Failed to add wrapping key");
3867 parent
3868 .update_attributes(&fio::MutableNodeAttributes {
3869 encryption_policy: Some(fio::FscryptPolicy {
3870 key_identifier: WRAPPING_KEY_ID,
3871 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
3872 }),
3873 ..Default::default()
3874 })
3875 .await
3876 .expect("FIDL call failed")
3877 .map_err(zx::ok)
3878 .expect("update_attributes failed");
3879
3880 parent
3882 .create_symlink("symlink", b"target", None)
3883 .await
3884 .expect("FIDL call failed")
3885 .expect("create_symlink failed");
3886
3887 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3888
3889 let device = fixture.close().await;
3890 let new_fixture = TestFixture::new_with_device(device).await;
3891 let root = new_fixture.root();
3892 let open_dir = || {
3893 open_dir_checked(
3894 &root,
3895 "foo",
3896 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
3897 Default::default(),
3898 )
3899 };
3900 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3901 let (status, buf) = parent.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3902 zx::Status::ok(status).expect("read_dirents failed");
3903 let mut encrypted_entries = vec![];
3904 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3905 encrypted_entries.push(res.expect("Failed to parse entry"));
3906 }
3907 let mut encrypted_name = String::new();
3908 for entry in encrypted_entries {
3909 if entry.name == ".".to_owned() {
3910 continue;
3911 } else {
3912 assert!(entry.name.len() >= FSCRYPT_PADDING);
3913 encrypted_name = entry.name;
3914 assert!(entry.kind == DirentKind::Symlink)
3915 }
3916 }
3917 {
3918 let (symlink, server_end) = create_proxy::<fio::SymlinkMarker>();
3919 parent
3920 .open(
3921 &encrypted_name,
3922 fio::PERM_READABLE | fio::Flags::FLAG_SEND_REPRESENTATION,
3923 &Default::default(),
3924 server_end.into_channel(),
3925 )
3926 .expect("open failed");
3927
3928 let representation = symlink
3929 .take_event_stream()
3930 .next()
3931 .await
3932 .expect("missing Symlink event")
3933 .expect("failed to read Symlink event")
3934 .into_on_representation()
3935 .expect("failed to decode OnRepresentation");
3936 let mut encrypted_target = None;
3937 if let fio::Representation::Symlink(fio::SymlinkInfo { target: Some(target), .. }) =
3938 representation
3939 {
3940 encrypted_target = Some(target)
3941 };
3942
3943 let (_mutable, immutable) = symlink
3944 .get_attributes(fio::NodeAttributesQuery::CONTENT_SIZE)
3945 .await
3946 .expect("transport error on get_attributes")
3947 .expect("failed to get attributes on a locked symlink");
3948
3949 assert_eq!(immutable.content_size, encrypted_target.map(|x| x.len() as u64));
3950 }
3951
3952 new_fixture.close().await;
3953 }
3954
3955 #[fuchsia::test]
3956 async fn test_create_symlink_in_locked_directory() {
3957 let fixture = TestFixture::new().await;
3958 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3959 let root = fixture.root();
3960 let open_dir = || {
3961 open_dir_checked(
3962 &root,
3963 "foo",
3964 fio::Flags::FLAG_MAYBE_CREATE
3965 | fio::PERM_READABLE
3966 | fio::PERM_WRITABLE
3967 | fio::Flags::PROTOCOL_DIRECTORY,
3968 Default::default(),
3969 )
3970 };
3971 let parent = Arc::new(open_dir().await);
3972 crypt
3973 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3974 .expect("Failed to add wrapping key");
3975 parent
3976 .update_attributes(&fio::MutableNodeAttributes {
3977 encryption_policy: Some(fio::FscryptPolicy {
3978 key_identifier: WRAPPING_KEY_ID,
3979 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
3980 }),
3981 ..Default::default()
3982 })
3983 .await
3984 .expect("FIDL call failed")
3985 .map_err(zx::ok)
3986 .expect("update_attributes failed");
3987
3988 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3989
3990 let device = fixture.close().await;
3991 let new_fixture = TestFixture::new_with_device(device).await;
3992 let root = new_fixture.root();
3993 let open_dir = || {
3994 open_dir_checked(
3995 &root,
3996 "foo",
3997 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
3998 Default::default(),
3999 )
4000 };
4001 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
4002 parent
4003 .create_symlink("symlink", b"target", None)
4004 .await
4005 .expect("FIDL call failed")
4006 .expect_err("creating a symlink in a locked directory should fail");
4007
4008 new_fixture.close().await;
4009 }
4010
4011 #[fuchsia::test]
4012 async fn test_get_encryption_policy_on_locked_directory() {
4013 let fixture = TestFixture::new().await;
4014 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
4015 let root = fixture.root();
4016 let parent = open_dir_checked(
4017 &root,
4018 "foo",
4019 fio::Flags::FLAG_MAYBE_CREATE
4020 | fio::PERM_READABLE
4021 | fio::PERM_WRITABLE
4022 | fio::Flags::PROTOCOL_DIRECTORY,
4023 Default::default(),
4024 )
4025 .await;
4026 crypt
4027 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
4028 .expect("Failed to add wrapping key");
4029 let expected_policy = fio::FscryptPolicy {
4030 key_identifier: WRAPPING_KEY_ID,
4031 flags: fio::FscryptPolicyFlags::PAD_16 | fio::FscryptPolicyFlags::IV_INO_LBLK_64,
4032 };
4033 parent
4034 .update_attributes(&fio::MutableNodeAttributes {
4035 encryption_policy: Some(expected_policy.clone()),
4036 ..Default::default()
4037 })
4038 .await
4039 .expect("FIDL call failed")
4040 .map_err(zx::ok)
4041 .expect("update_attributes failed");
4042
4043 let child = open_dir_checked(
4044 &parent,
4045 "child_dir",
4046 fio::Flags::FLAG_MUST_CREATE
4047 | fio::PERM_READABLE
4048 | fio::PERM_WRITABLE
4049 | fio::Flags::PROTOCOL_DIRECTORY,
4050 Default::default(),
4051 )
4052 .await;
4053 close_dir_checked(child).await;
4054 close_dir_checked(parent).await;
4055
4056 let device = fixture.close().await;
4057 let new_fixture = TestFixture::new_with_device(device).await;
4059 let root = new_fixture.root();
4060 let locked_parent = open_dir_checked(
4061 &root,
4062 "foo",
4063 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
4064 Default::default(),
4065 )
4066 .await;
4067 let (mutable_attrs, _) = locked_parent
4068 .get_attributes(fio::NodeAttributesQuery::ENCRYPTION_POLICY)
4069 .await
4070 .expect("FIDL call failed")
4071 .map_err(zx::Status::err_from_raw)
4072 .expect("get_attributes failed on locked parent directory");
4073 assert_eq!(mutable_attrs.encryption_policy, Some(expected_policy.clone()));
4074
4075 let entries = fuchsia_fs::directory::readdir(&locked_parent).await.expect("readdir failed");
4076 assert_eq!(entries.len(), 1);
4077 let locked_child = open_dir_checked(
4078 &locked_parent,
4079 &entries[0].name,
4080 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
4081 Default::default(),
4082 )
4083 .await;
4084 let (child_mutable_attrs, _) = locked_child
4085 .get_attributes(fio::NodeAttributesQuery::ENCRYPTION_POLICY)
4086 .await
4087 .expect("FIDL call failed")
4088 .map_err(zx::Status::err_from_raw)
4089 .expect("get_attributes failed on locked child directory");
4090 assert_eq!(child_mutable_attrs.encryption_policy, Some(expected_policy));
4091
4092 close_dir_checked(locked_child).await;
4093 close_dir_checked(locked_parent).await;
4094 new_fixture.close().await;
4095 }
4096
4097 #[fuchsia::test]
4098 async fn test_symlink() {
4099 let fixture = TestFixture::new().await;
4100
4101 {
4102 let root = fixture.root();
4103
4104 root.create_symlink("symlink", b"target", None)
4105 .await
4106 .expect("FIDL call failed")
4107 .expect("create_symlink failed");
4108
4109 let (proxy, server_end) = create_proxy::<fio::SymlinkMarker>();
4110 root.open(
4111 "symlink",
4112 fio::PERM_READABLE | fio::Flags::FLAG_SEND_REPRESENTATION,
4113 &Default::default(),
4114 server_end.into_channel(),
4115 )
4116 .expect("open failed");
4117
4118 let representation = proxy
4119 .take_event_stream()
4120 .next()
4121 .await
4122 .expect("missing Symlink event")
4123 .expect("failed to read Symlink event")
4124 .into_on_representation()
4125 .expect("failed to decode OnRepresentation");
4126
4127 assert_matches!(representation,
4128 fio::Representation::Symlink(fio::SymlinkInfo{
4129 target: Some(target), ..
4130 }) if target == b"target"
4131 );
4132
4133 let (proxy, server_end) = create_proxy::<fio::SymlinkMarker>();
4134 root.create_symlink("symlink2", b"target2", Some(server_end))
4135 .await
4136 .expect("FIDL call failed")
4137 .expect("create_symlink failed");
4138
4139 let node_info = proxy.describe().await.expect("FIDL call failed");
4140 assert_matches!(
4141 node_info,
4142 fio::SymlinkInfo { target: Some(target), .. } if target == b"target2"
4143 );
4144
4145 root.unlink("symlink2", &fio::UnlinkOptions::default())
4147 .await
4148 .expect("FIDL call failed")
4149 .expect("unlink failed");
4150
4151 open_file_checked(
4153 &root,
4154 "target",
4155 fio::Flags::FLAG_MAYBE_CREATE
4156 | fio::PERM_READABLE
4157 | fio::PERM_WRITABLE
4158 | fio::Flags::PROTOCOL_FILE,
4159 &Default::default(),
4160 )
4161 .await;
4162 let (status, dst_token) = root.deprecated_get_token().await.expect("FIDL call failed");
4163 zx::Status::ok(status).expect("get_token failed");
4164 root.rename("target", zx::Event::from(dst_token.unwrap()), "symlink")
4165 .await
4166 .expect("FIDL call failed")
4167 .expect("rename failed");
4168
4169 proxy
4170 .get_attributes(fio::NodeAttributesQuery::empty())
4171 .await
4172 .expect("FIDL call failed")
4173 .expect("get_attributes failed");
4174 let node_info = proxy.describe().await.expect("FIDL call failed");
4175 assert_matches!(
4176 node_info,
4177 fio::SymlinkInfo { target: Some(target), .. } if target == b"target2"
4178 );
4179 }
4180
4181 fixture.close().await;
4182 }
4183
4184 #[fuchsia::test]
4185 async fn test_symlink_link_into_unlinked_fails() {
4186 let fixture = TestFixture::new().await;
4187 {
4188 let root = fixture.root();
4189
4190 root.create_symlink("symlink", b"target", None)
4192 .await
4193 .expect("FIDL call failed")
4194 .expect("create_symlink failed");
4195
4196 let (proxy, server_end) = create_proxy::<fio::SymlinkMarker>();
4198 root.open(
4199 "symlink",
4200 fio::PERM_READABLE | fio::PERM_WRITABLE,
4201 &Default::default(),
4202 server_end.into_channel(),
4203 )
4204 .expect("open failed");
4205
4206 root.unlink("symlink", &fio::UnlinkOptions::default())
4208 .await
4209 .expect("FIDL call failed")
4210 .expect("unlink failed");
4211
4212 let dst_token =
4214 root.get_token().await.expect("FIDL call failed").expect("get_token failed");
4215
4216 let link_result = proxy.link_into(dst_token, "symlink_new").await;
4217 assert_matches!(
4218 link_result,
4219 Ok(Err(status)) if status == zx::Status::NOT_FOUND.into_raw()
4220 );
4221
4222 let (proxy_new, server_end_new) = create_proxy::<fio::SymlinkMarker>();
4224 root.open(
4225 "symlink_new",
4226 fio::PERM_READABLE,
4227 &Default::default(),
4228 server_end_new.into_channel(),
4229 )
4230 .expect("open failed");
4231
4232 let describe_result = proxy_new.describe().await;
4233 assert_matches!(describe_result, Err(_));
4234 }
4235 fixture.close().await;
4236 }
4237
4238 #[fuchsia::test]
4243 async fn test_race_hard_link_with_unlink() {
4244 let fixture = TestFixture::new().await;
4245 {
4246 let root = fixture.root();
4247
4248 let inner = open_dir_checked(
4249 root,
4250 "bar",
4251 fio::Flags::FLAG_MAYBE_CREATE
4252 | fio::PERM_READABLE
4253 | fio::PERM_WRITABLE
4254 | fio::Flags::PROTOCOL_DIRECTORY,
4255 Default::default(),
4256 )
4257 .await;
4258 let inner2 = open_dir_checked(
4259 &inner,
4260 ".",
4261 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
4262 Default::default(),
4263 )
4264 .await;
4265
4266 let file = open_file_checked(
4267 &inner,
4268 "foo",
4269 fio::Flags::FLAG_MAYBE_CREATE
4270 | fio::PERM_READABLE
4271 | fio::PERM_WRITABLE
4272 | fio::Flags::PROTOCOL_FILE,
4273 &Default::default(),
4274 )
4275 .await;
4276 assert_eq!(
4277 file.write("valid".as_bytes()).await.expect("FIDL failed").expect("Write success"),
4278 5
4279 );
4280 close_file_checked(file).await;
4281
4282 let file = open_file_checked(
4283 &inner,
4284 "foo2",
4285 fio::Flags::FLAG_MAYBE_CREATE
4286 | fio::PERM_READABLE
4287 | fio::PERM_WRITABLE
4288 | fio::Flags::PROTOCOL_FILE,
4289 &Default::default(),
4290 )
4291 .await;
4292 assert_eq!(
4293 file.write("valid".as_bytes()).await.expect("FIDL failed").expect("Write success"),
4294 5
4295 );
4296 close_file_checked(file).await;
4297
4298 let inner_token = inner
4299 .deprecated_get_token()
4300 .await
4301 .expect("fidl failed")
4302 .1
4303 .expect("get_token returned no handle");
4304 let root_token = root
4305 .deprecated_get_token()
4306 .await
4307 .expect("fidl failed")
4308 .1
4309 .expect("get_token returned no handle");
4310
4311 let write_lock = fixture
4317 .fs()
4318 .lock_manager()
4319 .write_lock(lock_keys![LockKey::object(
4320 fixture.volume().volume().store().store_object_id(),
4321 fixture.volume().root_dir().directory().object_id()
4322 )])
4323 .await;
4324
4325 join!(
4326 async move {
4327 fasync::Timer::new(Duration::from_millis(50)).await;
4330 let _lock = write_lock;
4331 },
4332 async move {
4333 fasync::Timer::new(Duration::from_millis(25)).await;
4336 inner
4337 .rename("foo2", inner_token.into(), "foo")
4338 .await
4339 .expect("FIDL call failed")
4340 .expect("Rename failed");
4341 },
4342 async move {
4343 assert_eq!(
4346 inner2.link("foo", root_token, "baz").await.expect("Fidl call"),
4347 zx::sys::ZX_OK
4348 );
4349 }
4350 );
4351 }
4352
4353 let file = open_file_checked(
4356 fixture.root(),
4357 "baz",
4358 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_FILE,
4359 &Default::default(),
4360 )
4361 .await;
4362 let buff = file.read(5).await.expect("FIDL failed").expect("Read failed");
4363 close_file_checked(file).await;
4364 assert_eq!(buff.as_slice(), "valid".as_bytes());
4365 fixture.close().await;
4366 }
4367
4368 #[fuchsia::test]
4369 async fn test_hard_link_to_symlink() {
4370 let fixture = TestFixture::new().await;
4371
4372 {
4373 let root = fixture.root();
4374
4375 root.create_symlink("symlink", b"target", None)
4376 .await
4377 .expect("FIDL call failed")
4378 .expect("create_symlink failed");
4379
4380 async fn open_symlink(root: &fio::DirectoryProxy, path: &str) -> fio::SymlinkProxy {
4381 let (proxy, server_end) = create_proxy::<fio::SymlinkMarker>();
4382 root.open(
4383 path,
4384 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::FLAG_SEND_REPRESENTATION,
4385 &Default::default(),
4386 server_end.into_channel(),
4387 )
4388 .expect("open failed");
4389
4390 let representation = proxy
4391 .take_event_stream()
4392 .next()
4393 .await
4394 .expect("missing Symlink event")
4395 .expect("failed to read Symlink event")
4396 .into_on_representation()
4397 .expect("failed to decode OnRepresentation");
4398
4399 assert_matches!(representation,
4400 fio::Representation::Symlink(fio::SymlinkInfo{
4401 target: Some(target), ..
4402 }) if target == b"target"
4403 );
4404
4405 proxy
4406 }
4407
4408 let proxy = open_symlink(&root, "symlink").await;
4409
4410 let (status, dst_token) = root.deprecated_get_token().await.expect("FIDL call failed");
4411 zx::Status::ok(status).expect("get_token failed");
4412 proxy
4413 .link_into(zx::Event::from(dst_token.unwrap()), "symlink2")
4414 .await
4415 .expect("link_into (FIDL) failed")
4416 .expect("link_into failed");
4417
4418 open_symlink(&root, "symlink2").await;
4419 }
4420
4421 fixture.close().await;
4422 }
4423
4424 #[fuchsia::test]
4425 async fn test_symlink_stat() {
4426 let fixture = TestFixture::new().await;
4427
4428 {
4429 let root = fixture.root();
4430
4431 root.create_symlink("symlink", b"target", None)
4432 .await
4433 .expect("FIDL call failed")
4434 .expect("create_symlink failed");
4435
4436 let root = fuchsia_fs::directory::clone(root).expect("clone failed");
4437
4438 fasync::unblock(|| {
4439 let root: std::os::fd::OwnedFd =
4440 fdio::create_fd(root.into_channel().unwrap().into_zx_channel().into())
4441 .expect("create_fd failed");
4442
4443 let mut stat = std::mem::MaybeUninit::<libc::stat>::uninit();
4444 let name = std::ffi::CString::new("symlink").expect("CString::new failed");
4445 assert_eq!(
4446 unsafe { libc::fstatat(root.as_raw_fd(), name.as_ptr(), stat.as_mut_ptr(), 0) },
4451 0
4452 );
4453 })
4454 .await;
4455 }
4456
4457 fixture.close().await;
4458 }
4459
4460 #[fuchsia::test]
4461 async fn test_remove_dir_all_with_symlink() {
4462 let fixture = TestFixture::new().await;
4467
4468 {
4469 let root = fixture.root();
4470
4471 let dir = open_dir_checked(
4472 &root,
4473 "dir",
4474 fio::Flags::FLAG_MAYBE_CREATE
4475 | fio::PERM_READABLE
4476 | fio::PERM_WRITABLE
4477 | fio::Flags::PROTOCOL_DIRECTORY,
4478 Default::default(),
4479 )
4480 .await;
4481
4482 dir.create_symlink("symlink", b"target", None)
4483 .await
4484 .expect("FIDL call failed")
4485 .expect("create_symlink failed");
4486
4487 let namespace = fdio::Namespace::installed().expect("Unable to get namespace");
4488 static COUNTER: AtomicU64 = AtomicU64::new(0);
4489 let path = format!("/test_symlink_stat.{}", COUNTER.fetch_add(1, Ordering::Relaxed));
4490 let root = fuchsia_fs::directory::clone(root).expect("clone failed");
4491 namespace
4492 .bind(&path, ClientEnd::new(root.into_channel().unwrap().into_zx_channel()))
4493 .expect("bind failed");
4494 let path_copy = path.clone();
4495 scopeguard::defer!({
4496 let _ = namespace.unbind(&path_copy);
4497 });
4498
4499 fasync::unblock(move || {
4500 assert_matches!(std::fs::remove_dir_all(&format!("{path}/dir")), Ok(()));
4501 })
4502 .await;
4503 }
4504
4505 fixture.close().await;
4506 }
4507
4508 #[fuchsia::test]
4509 async fn extended_attributes() {
4510 let fixture = TestFixture::new().await;
4511 let root = fixture.root();
4512
4513 let file = open_dir_checked(
4514 &root,
4515 "foo",
4516 fio::Flags::FLAG_MAYBE_CREATE
4517 | fio::PERM_READABLE
4518 | fio::PERM_WRITABLE
4519 | fio::Flags::PROTOCOL_DIRECTORY,
4520 Default::default(),
4521 )
4522 .await;
4523
4524 let name = b"security.selinux";
4525 let value_vec = b"bar".to_vec();
4526
4527 {
4528 let (iterator_client, iterator_server) =
4529 fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
4530 file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
4531 let (chunk, last) = iterator_client
4532 .get_next()
4533 .await
4534 .expect("Failed to make FIDL call")
4535 .expect("Failed to get next iterator chunk");
4536 assert!(last);
4537 assert_eq!(chunk, Vec::<Vec<u8>>::new());
4538 }
4539 assert_eq!(
4540 file.get_extended_attribute(name)
4541 .await
4542 .expect("Failed to make FIDL call")
4543 .expect_err("Got successful message back for missing attribute"),
4544 zx::Status::NOT_FOUND.into_raw(),
4545 );
4546
4547 file.set_extended_attribute(
4548 name,
4549 fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
4550 fio::SetExtendedAttributeMode::Set,
4551 )
4552 .await
4553 .expect("Failed to make FIDL call")
4554 .expect("Failed to set extended attribute");
4555
4556 {
4557 let (iterator_client, iterator_server) =
4558 fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
4559 file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
4560 let (chunk, last) = iterator_client
4561 .get_next()
4562 .await
4563 .expect("Failed to make FIDL call")
4564 .expect("Failed to get next iterator chunk");
4565 assert!(last);
4566 assert_eq!(chunk, vec![name]);
4567 }
4568 assert_eq!(
4569 file.get_extended_attribute(name)
4570 .await
4571 .expect("Failed to make FIDL call")
4572 .expect("Failed to get extended attribute"),
4573 fio::ExtendedAttributeValue::Bytes(value_vec)
4574 );
4575
4576 file.remove_extended_attribute(name)
4577 .await
4578 .expect("Failed to make FIDL call")
4579 .expect("Failed to remove extended attribute");
4580
4581 {
4582 let (iterator_client, iterator_server) =
4583 fidl::endpoints::create_proxy::<fio::ExtendedAttributeIteratorMarker>();
4584 file.list_extended_attributes(iterator_server).expect("Failed to make FIDL call");
4585 let (chunk, last) = iterator_client
4586 .get_next()
4587 .await
4588 .expect("Failed to make FIDL call")
4589 .expect("Failed to get next iterator chunk");
4590 assert!(last);
4591 assert_eq!(chunk, Vec::<Vec<u8>>::new());
4592 }
4593 assert_eq!(
4594 file.get_extended_attribute(name)
4595 .await
4596 .expect("Failed to make FIDL call")
4597 .expect_err("Got successful message back for missing attribute"),
4598 zx::Status::NOT_FOUND.into_raw(),
4599 );
4600
4601 close_dir_checked(file).await;
4602 fixture.close().await;
4603 }
4604
4605 #[fuchsia::test]
4606 async fn extended_attribute_set_modes() {
4607 let fixture = TestFixture::new().await;
4608 let root = fixture.root();
4609
4610 let dir = open_dir_checked(
4611 &root,
4612 "foo",
4613 fio::Flags::FLAG_MAYBE_CREATE
4614 | fio::PERM_READABLE
4615 | fio::PERM_WRITABLE
4616 | fio::Flags::PROTOCOL_DIRECTORY,
4617 Default::default(),
4618 )
4619 .await;
4620
4621 let name = b"security.selinux";
4622 let value_vec = b"bar".to_vec();
4623 let value2_vec = b"new value".to_vec();
4624
4625 assert_eq!(
4627 dir.set_extended_attribute(
4628 name,
4629 fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
4630 fio::SetExtendedAttributeMode::Replace
4631 )
4632 .await
4633 .expect("Failed to make FIDL call")
4634 .expect_err("Got successful message back from replacing a nonexistent attribute"),
4635 zx::Status::NOT_FOUND.into_raw()
4636 );
4637
4638 dir.set_extended_attribute(
4640 name,
4641 fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
4642 fio::SetExtendedAttributeMode::Create,
4643 )
4644 .await
4645 .expect("Failed to make FIDL call")
4646 .expect("Failed to set xattr with create");
4647
4648 assert_eq!(
4650 dir.set_extended_attribute(
4651 name,
4652 fio::ExtendedAttributeValue::Bytes(value2_vec.clone()),
4653 fio::SetExtendedAttributeMode::Create
4654 )
4655 .await
4656 .expect("Failed to make FIDL call")
4657 .expect_err("Got successful message back from replacing a nonexistent attribute"),
4658 zx::Status::ALREADY_EXISTS.into_raw()
4659 );
4660
4661 dir.set_extended_attribute(
4663 name,
4664 fio::ExtendedAttributeValue::Bytes(value2_vec.clone()),
4665 fio::SetExtendedAttributeMode::Replace,
4666 )
4667 .await
4668 .expect("Failed to make FIDL call")
4669 .expect("Failed to set xattr with create");
4670
4671 close_dir_checked(dir).await;
4672 fixture.close().await;
4673 }
4674
4675 #[fuchsia::test]
4676 async fn test_remove_large_xattr() {
4677 let fixture = TestFixture::new().await;
4678 {
4679 let root = fixture.root();
4680 let dir = open_dir_checked(
4681 &root,
4682 "foo",
4683 fio::Flags::FLAG_MAYBE_CREATE
4684 | fio::PERM_READABLE
4685 | fio::PERM_WRITABLE
4686 | fio::Flags::PROTOCOL_DIRECTORY,
4687 Default::default(),
4688 )
4689 .await;
4690
4691 dir.set_extended_attribute(
4692 "name".as_bytes(),
4693 fio::ExtendedAttributeValue::Bytes(vec![17u8; 300]),
4694 fio::SetExtendedAttributeMode::Create,
4695 )
4696 .await
4697 .expect("FIDL call failed")
4698 .expect("Set xattr failed");
4699
4700 dir.remove_extended_attribute("name".as_bytes())
4701 .await
4702 .expect("FIDL call failed")
4703 .expect("Set xattr failed");
4704 }
4705 fixture.close().await;
4706 }
4707
4708 #[fuchsia::test]
4709 async fn test_create_dir_with_mutable_node_attributes() {
4710 let fixture = TestFixture::new().await;
4711 {
4712 let root_dir = fixture.volume().root_dir();
4713
4714 let path_str = "foo";
4715 let path = Path::validate_and_split(path_str).unwrap();
4716
4717 let (_proxy, server_end) = create_proxy::<fio::DirectoryMarker>();
4718 let mode: u32 = 0o123;
4719 let flags = fio::Flags::PROTOCOL_DIRECTORY | fio::Flags::FLAG_MAYBE_CREATE;
4720 let options = fio::Options {
4721 create_attributes: Some(fio::MutableNodeAttributes {
4722 mode: Some(mode),
4723 ..Default::default()
4724 }),
4725 ..Default::default()
4726 };
4727
4728 let request = ObjectRequest::new(flags, &options, server_end.into_channel());
4729 let dir = root_dir.lookup(&flags, path, &request).await.expect("lookup failed");
4730
4731 let attrs = dir
4732 .clone()
4733 .into_any()
4734 .downcast::<FxDirectory>()
4735 .expect("Not a directory")
4736 .get_attributes(
4737 fio::NodeAttributesQuery::MODE
4738 | fio::NodeAttributesQuery::UID
4739 | fio::NodeAttributesQuery::ACCESS_TIME,
4740 )
4741 .await
4742 .expect("FIDL call failed");
4743 assert_eq!(attrs.mutable_attributes.mode.unwrap(), mode);
4744 assert_eq!(attrs.mutable_attributes.uid.unwrap(), 0);
4747 assert!(attrs.mutable_attributes.gid.is_none());
4749 assert!(attrs.mutable_attributes.rdev.is_none());
4750 assert!(attrs.mutable_attributes.access_time.is_some());
4751 }
4752 fixture.close().await;
4753 }
4754
4755 #[fuchsia::test]
4756 async fn test_create_dir_with_default_mutable_node_attributes() {
4757 let fixture = TestFixture::new().await;
4758 {
4759 let root_dir = fixture.volume().root_dir();
4760
4761 let path_str = "foo";
4762 let path = Path::validate_and_split(path_str).unwrap();
4763
4764 let (_proxy, server_end) = create_proxy::<fio::DirectoryMarker>();
4765 let flags = fio::Flags::PROTOCOL_DIRECTORY | fio::Flags::FLAG_MAYBE_CREATE;
4766 let options = fio::Options {
4767 create_attributes: Some(fio::MutableNodeAttributes { ..Default::default() }),
4768 ..Default::default()
4769 };
4770
4771 let request = ObjectRequest::new(flags, &options, server_end.into_channel());
4772 let dir = root_dir.lookup(&flags, path, &request).await.expect("lookup failed");
4773
4774 let attrs = dir
4775 .clone()
4776 .into_any()
4777 .downcast::<FxDirectory>()
4778 .expect("Not a directory")
4779 .get_attributes(fio::NodeAttributesQuery::MODE)
4780 .await
4781 .expect("FIDL call failed");
4782 assert!(attrs.mutable_attributes.mode.is_none());
4785 assert!(attrs.mutable_attributes.uid.is_none());
4787 assert!(attrs.mutable_attributes.gid.is_none());
4788 assert!(attrs.mutable_attributes.rdev.is_none());
4789 assert!(attrs.mutable_attributes.creation_time.is_none());
4790 assert!(attrs.mutable_attributes.modification_time.is_none());
4791 assert!(attrs.mutable_attributes.access_time.is_none());
4792 }
4793 fixture.close().await;
4794 }
4795
4796 #[fuchsia::test]
4797 async fn test_create_dir_using_flags_and_options() {
4798 let fixture = TestFixture::new().await;
4799 {
4800 let root_dir = fixture.volume().root_dir();
4801
4802 let path_str = "foo";
4803 let path = Path::validate_and_split(path_str).unwrap();
4804
4805 let (_proxy, server_end) = create_proxy::<fio::DirectoryMarker>();
4806 let mode: u32 = 0o123;
4807 let flags = fio::Flags::PROTOCOL_DIRECTORY | fio::Flags::FLAG_MAYBE_CREATE;
4808 let options = fio::Options {
4809 create_attributes: Some(fio::MutableNodeAttributes {
4810 mode: Some(mode),
4811 ..Default::default()
4812 }),
4813 ..Default::default()
4814 };
4815
4816 let request = ObjectRequest::new(flags, &options, server_end.into());
4818 let dir = root_dir.lookup(&flags, path, &request).await.expect("lookup failed");
4819
4820 let attrs = dir
4822 .clone()
4823 .into_any()
4824 .downcast::<FxDirectory>()
4825 .expect("Not a directory")
4826 .get_attributes(
4827 fio::NodeAttributesQuery::MODE
4828 | fio::NodeAttributesQuery::UID
4829 | fio::NodeAttributesQuery::ACCESS_TIME,
4830 )
4831 .await
4832 .expect("FIDL call failed");
4833 assert_eq!(attrs.mutable_attributes.mode.unwrap(), mode);
4834 assert_eq!(attrs.mutable_attributes.uid.unwrap(), 0);
4837 assert!(attrs.mutable_attributes.gid.is_none());
4839 assert!(attrs.mutable_attributes.rdev.is_none());
4840 assert!(attrs.mutable_attributes.access_time.is_some());
4841 }
4842 fixture.close().await;
4843 }
4844
4845 #[fuchsia::test]
4846 async fn test_create_file_with_mutable_node_attributes() {
4847 let fixture = TestFixture::new().await;
4848 {
4849 let root_dir = fixture.volume().root_dir();
4850
4851 let path_str = "foo";
4852 let path = Path::validate_and_split(path_str).unwrap();
4853
4854 let (_proxy, server_end) = create_proxy::<fio::FileMarker>();
4855 let mode: u32 = 0o123;
4856 let uid = 1;
4857 let gid = 2;
4858 let rdev = 3;
4859 let modification_time = Timestamp::now().as_nanos();
4860
4861 let flags = fio::Flags::PROTOCOL_FILE | fio::Flags::FLAG_MAYBE_CREATE;
4862 let options = fio::Options {
4863 create_attributes: Some(fio::MutableNodeAttributes {
4864 modification_time: Some(modification_time),
4865 mode: Some(mode),
4866 uid: Some(uid),
4867 gid: Some(gid),
4868 rdev: Some(rdev),
4869 ..Default::default()
4870 }),
4871 ..Default::default()
4872 };
4873
4874 let request = ObjectRequest::new(flags, &options, server_end.into_channel());
4875 let file = root_dir.lookup(&flags, path, &request).await.expect("lookup failed");
4876
4877 let attributes = file
4878 .clone()
4879 .into_any()
4880 .downcast::<FxFile>()
4881 .expect("Not a file")
4882 .get_attributes(
4883 fio::NodeAttributesQuery::CREATION_TIME
4884 | fio::NodeAttributesQuery::MODIFICATION_TIME
4885 | fio::NodeAttributesQuery::CHANGE_TIME
4886 | fio::NodeAttributesQuery::MODE
4887 | fio::NodeAttributesQuery::UID
4888 | fio::NodeAttributesQuery::GID
4889 | fio::NodeAttributesQuery::RDEV,
4890 )
4891 .await
4892 .expect("FIDL call failed");
4893 assert_eq!(mode, attributes.mutable_attributes.mode.unwrap());
4894 assert_eq!(uid, attributes.mutable_attributes.uid.unwrap());
4895 assert_eq!(gid, attributes.mutable_attributes.gid.unwrap());
4896 assert_eq!(rdev, attributes.mutable_attributes.rdev.unwrap());
4897 assert_eq!(modification_time, attributes.mutable_attributes.modification_time.unwrap());
4898 assert!(attributes.mutable_attributes.creation_time.is_some());
4899 assert!(attributes.immutable_attributes.change_time.is_some());
4900 }
4901 fixture.close().await;
4902 }
4903
4904 #[fuchsia::test]
4905 async fn test_create_file_with_default_mutable_node_attributes() {
4906 let fixture = TestFixture::new().await;
4907 {
4908 let root_dir = fixture.volume().root_dir();
4909
4910 let path_str = "foo";
4911 let path = Path::validate_and_split(path_str).unwrap();
4912
4913 let (_proxy, server_end) = create_proxy::<fio::FileMarker>();
4914
4915 let flags = fio::Flags::PROTOCOL_FILE | fio::Flags::FLAG_MAYBE_CREATE;
4916 let options = Default::default();
4917
4918 let request = ObjectRequest::new(flags, &options, server_end.into_channel());
4919 let file = root_dir.lookup(&flags, path, &request).await.expect("lookup failed");
4920
4921 let attrs = file
4922 .clone()
4923 .into_any()
4924 .downcast::<FxFile>()
4925 .expect("Not a directory")
4926 .get_attributes(fio::NodeAttributesQuery::MODE)
4927 .await
4928 .expect("FIDL call failed");
4929 assert!(attrs.mutable_attributes.mode.is_none());
4932 assert!(attrs.mutable_attributes.uid.is_none());
4934 assert!(attrs.mutable_attributes.gid.is_none());
4935 assert!(attrs.mutable_attributes.rdev.is_none());
4936 assert!(attrs.mutable_attributes.creation_time.is_none());
4937 assert!(attrs.mutable_attributes.modification_time.is_none());
4938 }
4939 fixture.close().await;
4940 }
4941
4942 #[fuchsia::test]
4943 async fn test_create_file_using_flags_and_options() {
4944 let fixture = TestFixture::new().await;
4945 {
4946 let root_dir = fixture.volume().root_dir();
4947
4948 let path_str = "foo";
4949 let path = Path::validate_and_split(path_str).unwrap();
4950
4951 let (_proxy, server_end) = create_proxy::<fio::DirectoryMarker>();
4952 let mode: u32 = 0o123;
4953 let uid = 1;
4954 let gid = 2;
4955 let rdev = 3;
4956 let modification_time = Timestamp::now().as_nanos();
4957 let flags = fio::Flags::PROTOCOL_FILE | fio::Flags::FLAG_MAYBE_CREATE;
4958 let options = fio::Options {
4959 create_attributes: Some(fio::MutableNodeAttributes {
4960 modification_time: Some(modification_time),
4961 mode: Some(mode),
4962 uid: Some(uid),
4963 gid: Some(gid),
4964 rdev: Some(rdev),
4965 ..Default::default()
4966 }),
4967 ..Default::default()
4968 };
4969
4970 let request = ObjectRequest::new(flags, &options, server_end.into());
4972 let file = root_dir.lookup(&flags, path, &request).await.expect("lookup failed");
4973
4974 let attributes = file
4976 .clone()
4977 .into_any()
4978 .downcast::<FxFile>()
4979 .expect("Not a file")
4980 .get_attributes(
4981 fio::NodeAttributesQuery::CREATION_TIME
4982 | fio::NodeAttributesQuery::MODIFICATION_TIME
4983 | fio::NodeAttributesQuery::CHANGE_TIME
4984 | fio::NodeAttributesQuery::MODE
4985 | fio::NodeAttributesQuery::UID
4986 | fio::NodeAttributesQuery::GID
4987 | fio::NodeAttributesQuery::RDEV,
4988 )
4989 .await
4990 .expect("FIDL call failed");
4991 assert_eq!(mode, attributes.mutable_attributes.mode.unwrap());
4992 assert_eq!(uid, attributes.mutable_attributes.uid.unwrap());
4993 assert_eq!(gid, attributes.mutable_attributes.gid.unwrap());
4994 assert_eq!(rdev, attributes.mutable_attributes.rdev.unwrap());
4995 assert_eq!(modification_time, attributes.mutable_attributes.modification_time.unwrap());
4996 assert!(attributes.mutable_attributes.creation_time.is_some());
4997 assert!(attributes.immutable_attributes.change_time.is_some());
4998 }
4999 fixture.close().await;
5000 }
5001
5002 #[fuchsia::test]
5003 async fn test_update_attributes_also_updates_ctime() {
5004 let fixture = TestFixture::new().await;
5005 let root = fixture.root();
5006
5007 let dir = open_dir_checked(
5008 &root,
5009 "foo",
5010 fio::Flags::FLAG_MAYBE_CREATE
5011 | fio::PERM_READABLE
5012 | fio::PERM_WRITABLE
5013 | fio::Flags::PROTOCOL_DIRECTORY,
5014 Default::default(),
5015 )
5016 .await;
5017
5018 let (_mutable_attributes, immutable_attributes) = dir
5019 .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
5020 .await
5021 .expect("FIDL call failed")
5022 .map_err(zx::ok)
5023 .expect("get_attributes failed");
5024
5025 dir.update_attributes(&fio::MutableNodeAttributes {
5026 modification_time: Some(Timestamp::now().as_nanos()),
5027 mode: Some(111),
5028 gid: Some(222),
5029 ..Default::default()
5030 })
5031 .await
5032 .expect("FIDL call failed")
5033 .map_err(zx::ok)
5034 .expect("update_attributes failed");
5035
5036 let (_mutable_attributes, immutable_attributes_after_update) = dir
5037 .get_attributes(fio::NodeAttributesQuery::CHANGE_TIME)
5038 .await
5039 .expect("FIDL call failed")
5040 .map_err(zx::ok)
5041 .expect("get_attributes failed");
5042 assert!(immutable_attributes_after_update.change_time > immutable_attributes.change_time);
5043 fixture.close().await;
5044 }
5045
5046 async fn open_to_get_selinux_context(
5047 root_dir: &fio::DirectoryProxy,
5048 path: &str,
5049 protocol: fio::Flags,
5050 ) -> Option<fio::SelinuxContext> {
5051 let flags =
5053 protocol | fio::Flags::FLAG_SEND_REPRESENTATION | fio::Flags::PERM_GET_ATTRIBUTES;
5054 let options = fio::Options {
5055 attributes: Some(fio::NodeAttributesQuery::SELINUX_CONTEXT),
5056 ..Default::default()
5057 };
5058 let (node, server_end) = create_proxy::<fio::NodeMarker>();
5059 root_dir.open(path, flags, &options, server_end.into_channel()).expect("Reopening node");
5060 let repr = node
5061 .take_event_stream()
5062 .next()
5063 .await
5064 .expect("Need representation")
5065 .expect("Failed to read")
5066 .into_on_representation()
5067 .unwrap();
5068 match repr {
5069 fio::Representation::Directory(fio::DirectoryInfo {
5070 attributes: Some(attr), ..
5071 })
5072 | fio::Representation::File(fio::FileInfo { attributes: Some(attr), .. })
5073 | fio::Representation::Symlink(fio::SymlinkInfo { attributes: Some(attr), .. }) => {
5074 attr.mutable_attributes.selinux_context
5075 }
5076 _ => panic!("Wrong type returned."),
5077 }
5078 }
5079
5080 #[fuchsia::test]
5081 async fn test_selinux_context_via_open() {
5082 const CONTEXT: &str = "valid";
5083 const CONTEXT2: &str = "also_valid";
5084 let node_info: Vec<(&str, fio::Flags)> =
5085 vec![("dir", fio::Flags::PROTOCOL_DIRECTORY), ("file", fio::Flags::PROTOCOL_FILE)];
5086 let fixture = TestFixture::new().await;
5087 {
5088 let root_dir = fixture.root();
5089
5090 for (path, protocol) in node_info {
5091 let flags = protocol
5093 | fio::Flags::FLAG_SEND_REPRESENTATION
5094 | fio::Flags::FLAG_MAYBE_CREATE
5095 | fio::PERM_READABLE
5096 | fio::PERM_WRITABLE;
5097 let options = fio::Options {
5098 create_attributes: Some(fio::MutableNodeAttributes {
5099 selinux_context: Some(fio::SelinuxContext::Data(CONTEXT.into())),
5100 ..Default::default()
5101 }),
5102 ..Default::default()
5103 };
5104 let (node, server_end) = create_proxy::<fio::NodeMarker>();
5105 root_dir
5106 .open(path, flags, &options, server_end.into_channel())
5107 .expect("Creating node");
5108 assert!(
5110 node.take_event_stream()
5111 .next()
5112 .await
5113 .expect("Need representation")
5114 .expect("Failed to read")
5115 .into_on_representation()
5116 .is_some()
5117 );
5118
5119 assert_eq!(
5121 open_to_get_selinux_context(&root_dir, &path, protocol).await,
5122 Some(fio::SelinuxContext::Data(CONTEXT.into()))
5123 );
5124
5125 node.set_extended_attribute(
5127 fio::SELINUX_CONTEXT_NAME.as_bytes(),
5128 fio::ExtendedAttributeValue::Bytes(CONTEXT2.into()),
5129 fio::SetExtendedAttributeMode::Replace,
5130 )
5131 .await
5132 .unwrap()
5133 .expect("Updating xattr");
5134 assert_eq!(
5135 open_to_get_selinux_context(&root_dir, &path, protocol).await,
5136 Some(fio::SelinuxContext::Data(CONTEXT2.into()))
5137 );
5138
5139 let vmo = zx::Vmo::create(4000).expect("Creating VMO");
5141 node.set_extended_attribute(
5142 fio::SELINUX_CONTEXT_NAME.as_bytes(),
5143 fio::ExtendedAttributeValue::Buffer(vmo),
5144 fio::SetExtendedAttributeMode::Replace,
5145 )
5146 .await
5147 .unwrap()
5148 .expect("Updating xattr");
5149 assert_matches!(
5150 open_to_get_selinux_context(&root_dir, &path, protocol).await,
5151 Some(fio::SelinuxContext::UseExtendedAttributes(fio::EmptyStruct {}))
5152 );
5153
5154 node.remove_extended_attribute(fio::SELINUX_CONTEXT_NAME.as_bytes())
5155 .await
5156 .unwrap()
5157 .expect("Deleting xattr");
5158 assert_matches!(
5159 open_to_get_selinux_context(&root_dir, &path, protocol).await,
5160 None
5161 );
5162 }
5163 }
5164 fixture.close().await;
5165 }
5166
5167 #[fuchsia::test]
5168 async fn test_selinux_context_via_open_symlink() {
5169 const CONTEXT: &str = "valid";
5170 let fixture = TestFixture::new().await;
5171 {
5172 let path = "symlink";
5173 let root_dir = fixture.root();
5174 let (node, server_end) = create_proxy::<fio::SymlinkMarker>();
5176 root_dir
5177 .create_symlink(path, ".".as_bytes(), Some(server_end))
5178 .await
5179 .expect("Fidl query")
5180 .expect("Create symlink");
5181
5182 node.set_extended_attribute(
5183 fio::SELINUX_CONTEXT_NAME.as_bytes(),
5184 fio::ExtendedAttributeValue::Bytes(CONTEXT.into()),
5185 fio::SetExtendedAttributeMode::Create,
5186 )
5187 .await
5188 .unwrap()
5189 .expect("Updating xattr");
5190
5191 assert_eq!(
5193 open_to_get_selinux_context(&root_dir, &path, fio::Flags::PROTOCOL_SYMLINK).await,
5194 Some(fio::SelinuxContext::Data(CONTEXT.into()))
5195 );
5196
5197 let vmo = zx::Vmo::create(4000).expect("Creating VMO");
5199 node.set_extended_attribute(
5200 fio::SELINUX_CONTEXT_NAME.as_bytes(),
5201 fio::ExtendedAttributeValue::Buffer(vmo),
5202 fio::SetExtendedAttributeMode::Replace,
5203 )
5204 .await
5205 .unwrap()
5206 .expect("Updating xattr");
5207 assert_matches!(
5208 open_to_get_selinux_context(&root_dir, &path, fio::Flags::PROTOCOL_SYMLINK).await,
5209 Some(fio::SelinuxContext::UseExtendedAttributes(fio::EmptyStruct {}))
5210 );
5211
5212 node.remove_extended_attribute(fio::SELINUX_CONTEXT_NAME.as_bytes())
5214 .await
5215 .unwrap()
5216 .expect("Deleting xattr");
5217 assert_matches!(
5218 open_to_get_selinux_context(&root_dir, &path, fio::Flags::PROTOCOL_SYMLINK).await,
5219 None
5220 );
5221 }
5222 fixture.close().await;
5223 }
5224
5225 #[fuchsia::test]
5226 async fn test_open_deleted_self() {
5227 let fixture = TestFixture::new().await;
5228 let root = fixture.root();
5229
5230 let dir = open_dir_checked(
5231 &root,
5232 "foo",
5233 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
5234 Default::default(),
5235 )
5236 .await;
5237
5238 root.unlink("foo", &fio::UnlinkOptions::default())
5239 .await
5240 .expect("FIDL call failed")
5241 .expect("unlink failed");
5242
5243 assert_eq!(
5244 open_dir(&root, "foo", fio::Flags::PROTOCOL_DIRECTORY, &Default::default())
5245 .await
5246 .expect_err("Open succeeded")
5247 .root_cause()
5248 .downcast_ref::<zx::Status>()
5249 .expect("No status"),
5250 &zx::Status::NOT_FOUND,
5251 );
5252
5253 open_dir_checked(&dir, ".", fio::Flags::PROTOCOL_DIRECTORY, Default::default()).await;
5254
5255 fixture.close().await;
5256 }
5257
5258 #[fuchsia::test]
5259 async fn test_open3_deleted_self() {
5260 let fixture = TestFixture::new().await;
5261 let root = fixture.root();
5262
5263 const PATH: &str = "foo";
5264
5265 let dir = open_dir_checked(
5266 &root,
5267 PATH,
5268 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
5269 Default::default(),
5270 )
5271 .await;
5272
5273 root.unlink(PATH, &fio::UnlinkOptions::default())
5274 .await
5275 .expect("FIDL call failed")
5276 .expect("unlink failed");
5277
5278 assert_eq!(
5279 open_dir(
5280 &root,
5281 PATH,
5282 fio::Flags::PROTOCOL_DIRECTORY | fio::Flags::FLAG_SEND_REPRESENTATION,
5283 &fio::Options::default()
5284 )
5285 .await
5286 .expect_err("Open succeeded unexpectedly")
5287 .root_cause()
5288 .downcast_ref::<zx::Status>()
5289 .expect("No status"),
5290 &zx::Status::NOT_FOUND,
5291 );
5292
5293 open_dir_checked(
5294 &dir,
5295 ".",
5296 fio::Flags::PROTOCOL_DIRECTORY | fio::Flags::FLAG_SEND_REPRESENTATION,
5297 fio::Options::default(),
5298 )
5299 .await;
5300
5301 fixture.close().await;
5302 }
5303
5304 #[fuchsia::test]
5305 async fn test_update_attributes_persists() {
5306 const DIR: &str = "foo";
5307 let mtime = Some(Timestamp::now().as_nanos());
5308 let atime = Some(Timestamp::now().as_nanos());
5309 let mode = Some(111);
5310
5311 let device = {
5312 let fixture = TestFixture::new().await;
5313 let root = fixture.root();
5314
5315 let dir = open_dir_checked(
5316 &root,
5317 DIR,
5318 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
5319 Default::default(),
5320 )
5321 .await;
5322
5323 dir.update_attributes(&fio::MutableNodeAttributes {
5324 modification_time: mtime,
5325 access_time: atime,
5326 mode: mode,
5327 ..Default::default()
5328 })
5329 .await
5330 .expect("update_attributes FIDL call failed")
5331 .map_err(zx::ok)
5332 .expect("update_attributes failed");
5333
5334 fixture.close().await
5336 };
5337
5338 let fixture =
5339 TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
5340 .await;
5341 let root = fixture.root();
5342 let dir = open_dir_checked(
5343 &root,
5344 DIR,
5345 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
5346 Default::default(),
5347 )
5348 .await;
5349
5350 let (mutable_attributes, _immutable_attributes) = dir
5351 .get_attributes(
5352 fio::NodeAttributesQuery::MODIFICATION_TIME
5353 | fio::NodeAttributesQuery::ACCESS_TIME
5354 | fio::NodeAttributesQuery::MODE,
5355 )
5356 .await
5357 .expect("update_attributesFIDL call failed")
5358 .map_err(zx::ok)
5359 .expect("get_attributes failed");
5360 assert_eq!(mutable_attributes.modification_time, mtime);
5361 assert_eq!(mutable_attributes.access_time, atime);
5362 assert_eq!(mutable_attributes.mode, mode);
5363 fixture.close().await;
5364 }
5365
5366 #[fuchsia::test]
5367 async fn test_atime_from_pending_access_time_update_request() {
5368 const DIR: &str = "foo";
5369
5370 let (device, expected_atime, expected_ctime) = {
5371 let fixture = TestFixture::new().await;
5372 let root = fixture.root();
5373
5374 let dir = open_dir_checked(
5375 &root,
5376 DIR,
5377 fio::Flags::FLAG_MAYBE_CREATE
5378 | fio::PERM_WRITABLE
5379 | fio::Flags::PROTOCOL_DIRECTORY
5380 | fio::Flags::PERM_GET_ATTRIBUTES,
5381 fio::Options {
5382 attributes: Some(fio::NodeAttributesQuery::CHANGE_TIME),
5383 ..Default::default()
5384 },
5385 )
5386 .await;
5387
5388 let (mutable_attributes, immutable_attributes) = dir
5389 .get_attributes(
5390 fio::NodeAttributesQuery::CHANGE_TIME
5391 | fio::NodeAttributesQuery::ACCESS_TIME
5392 | fio::NodeAttributesQuery::MODIFICATION_TIME,
5393 )
5394 .await
5395 .expect("update_attributes FIDL call failed")
5396 .map_err(zx::ok)
5397 .expect("get_attributes failed");
5398 let initial_ctime = immutable_attributes.change_time;
5399 let initial_atime = mutable_attributes.access_time;
5400 assert_eq!(initial_atime, initial_ctime);
5402 assert_eq!(initial_atime, mutable_attributes.modification_time);
5403
5404 let (mutable_attributes, immutable_attributes) = dir
5408 .get_attributes(
5409 fio::NodeAttributesQuery::CHANGE_TIME
5410 | fio::NodeAttributesQuery::ACCESS_TIME
5411 | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
5412 )
5413 .await
5414 .expect("update_attributes FIDL call failed")
5415 .map_err(zx::ok)
5416 .expect("get_attributes failed");
5417 assert!(initial_atime < mutable_attributes.access_time);
5419 let updated_atime = mutable_attributes.access_time;
5420 assert_eq!(initial_ctime, immutable_attributes.change_time);
5423
5424 let (mutable_attributes, _) = dir
5425 .get_attributes(
5426 fio::NodeAttributesQuery::ACCESS_TIME
5427 | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
5428 )
5429 .await
5430 .expect("update_attributes FIDL call failed")
5431 .map_err(zx::ok)
5432 .expect("get_attributes failed");
5433 assert_eq!(updated_atime, mutable_attributes.access_time);
5435
5436 (fixture.close().await, mutable_attributes.access_time, initial_ctime)
5437 };
5438
5439 let fixture =
5440 TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
5441 .await;
5442 let root = fixture.root();
5443 let dir = open_dir_checked(
5444 &root,
5445 DIR,
5446 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
5447 Default::default(),
5448 )
5449 .await;
5450
5451 let (mutable_attributes, immutable_attributes) = dir
5452 .get_attributes(
5453 fio::NodeAttributesQuery::CHANGE_TIME | fio::NodeAttributesQuery::ACCESS_TIME,
5454 )
5455 .await
5456 .expect("update_attributesFIDL call failed")
5457 .map_err(zx::ok)
5458 .expect("get_attributes failed");
5459 assert_eq!(immutable_attributes.change_time, expected_ctime);
5460 assert_eq!(mutable_attributes.access_time, expected_atime);
5461 fixture.close().await;
5462 }
5463
5464 #[fuchsia::test]
5465 async fn test_directory_immediately_tombstoned() {
5466 let fixture = TestFixture::new().await;
5467 let root = fixture.root();
5468
5469 let dir = open_dir_checked(
5470 &root,
5471 "foo",
5472 fio::Flags::FLAG_MAYBE_CREATE
5473 | fio::PERM_WRITABLE
5474 | fio::Flags::PROTOCOL_DIRECTORY
5475 | fio::Flags::PERM_GET_ATTRIBUTES,
5476 fio::Options::default(),
5477 )
5478 .await;
5479
5480 let (_mutable, immutable) = dir
5481 .get_attributes(fio::NodeAttributesQuery::ID)
5482 .await
5483 .expect("transport error on get_attributes")
5484 .expect("get_attributes failed");
5485 let foo_object_id = immutable.id.unwrap();
5486
5487 let dir = open_dir_checked(
5488 &root,
5489 "bar",
5490 fio::Flags::FLAG_MAYBE_CREATE
5491 | fio::PERM_WRITABLE
5492 | fio::Flags::PROTOCOL_DIRECTORY
5493 | fio::Flags::PERM_GET_ATTRIBUTES,
5494 fio::Options::default(),
5495 )
5496 .await;
5497
5498 let (_mutable, immutable) = dir
5499 .get_attributes(fio::NodeAttributesQuery::ID)
5500 .await
5501 .expect("transport error on get_attributes")
5502 .expect("get_attributes failed");
5503 let bar_object_id = immutable.id.unwrap();
5504
5505 let (status, dst_token) = root.deprecated_get_token().await.expect("FIDL call failed");
5507 zx::Status::ok(status).expect("get_token failed");
5508 root.rename("foo", zx::Event::from(dst_token.unwrap()), "bar")
5509 .await
5510 .expect("FIDL call failed")
5511 .expect("rename failed");
5512
5513 yield_to_executor().await;
5515
5516 let assert_not_found = async |oid| {
5518 let tree = fixture.volume().volume().store().tree();
5519 let layer_set = tree.layer_set();
5520 let mut merger = layer_set.merger();
5521 let mut iter = merger.query(Query::FullScan).await.unwrap();
5522 while let Some(item) = iter.get() {
5523 match item {
5524 ItemRef { value: ObjectValue::None, .. } => {}
5525 ItemRef {
5526 key: ObjectKey { object_id, data: ObjectKeyData::Object }, ..
5527 } => {
5528 assert_ne!(*object_id, oid);
5529 }
5530 _ => {}
5531 }
5532 iter.advance().await.unwrap();
5533 }
5534 };
5535
5536 assert_not_found(bar_object_id).await;
5537
5538 root.unlink("bar", &Default::default())
5540 .await
5541 .expect("FIDL call failed")
5542 .expect("unlink failed");
5543
5544 assert_not_found(foo_object_id).await;
5545
5546 fixture.close().await;
5547 }
5548
5549 #[fuchsia::test]
5550 async fn test_failed_create_unnamed_file_transaction() {
5551 let fail = AtomicU64::new(0);
5552 let (mut hooks, fs_hooks) = fxfs::hooks::Hooks::new();
5553 hooks.set_pre_commit(|_| {
5554 if fail.load(Ordering::Relaxed) > 0 {
5555 fail.fetch_sub(1, Ordering::Relaxed);
5556 bail!("Aborted transaction");
5557 }
5558 Ok(())
5559 });
5560 let fixture = TestFixture::open(
5561 DeviceHolder::new(FakeDevice::new(16384, 512)),
5562 TestFixtureOptions { hooks: Some(fs_hooks), ..Default::default() },
5563 )
5564 .await;
5565 let root = fixture.root();
5566
5567 fail.fetch_add(1, Ordering::Relaxed);
5568
5569 let _dir = open_file(
5570 &root,
5571 ".",
5572 fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY,
5573 &fio::Options::default(),
5574 )
5575 .await
5576 .expect_err("Create unexpectedly succeeded");
5577
5578 fixture.close().await;
5579 }
5580
5581 #[fuchsia::test]
5582 async fn test_update_access_time_on_deleted_directory() {
5583 let fixture = TestFixture::new().await;
5584 let root = fixture.root();
5585
5586 let dir = open_dir_checked(
5587 &root,
5588 "foo",
5589 fio::Flags::FLAG_MAYBE_CREATE
5590 | fio::PERM_READABLE
5591 | fio::PERM_WRITABLE
5592 | fio::Flags::PROTOCOL_DIRECTORY,
5593 fio::Options::default(),
5594 )
5595 .await;
5596
5597 root.unlink("foo", &fio::UnlinkOptions::default())
5598 .await
5599 .expect("FIDL call failed")
5600 .expect("unlink failed");
5601
5602 dir.get_attributes(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE)
5604 .await
5605 .expect("FIDL call failed")
5606 .expect("get_attributes failed");
5607
5608 fixture.close().await;
5609 }
5610
5611 async fn enable_casefold(dir: &fio::DirectoryProxy) {
5612 dir.update_attributes(&fio::MutableNodeAttributes {
5613 casefold: Some(true),
5614 ..Default::default()
5615 })
5616 .await
5617 .expect("update_attributes FIDL call failed")
5618 .map_err(zx::ok)
5619 .expect("update_attributes failed");
5620 }
5621
5622 #[fuchsia::test]
5623 async fn test_casefold_cache_lookup_no_duplicates() {
5624 let fixture = TestFixture::new().await;
5625 let root = fixture.root();
5626
5627 let dir = open_dir_checked(
5628 &root,
5629 "dir",
5630 fio::Flags::FLAG_MAYBE_CREATE
5631 | fio::PERM_READABLE
5632 | fio::PERM_WRITABLE
5633 | fio::Flags::PROTOCOL_DIRECTORY,
5634 Default::default(),
5635 )
5636 .await;
5637 enable_casefold(&dir).await;
5638
5639 let file = open_file_checked(
5640 &dir,
5641 "foo",
5642 fio::Flags::FLAG_MAYBE_CREATE
5643 | fio::PERM_READABLE
5644 | fio::PERM_WRITABLE
5645 | fio::Flags::PROTOCOL_FILE,
5646 &Default::default(),
5647 )
5648 .await;
5649 close_file_checked(file).await;
5650
5651 let cache = fixture.volume().volume().dirent_cache();
5652 cache.clear();
5653
5654 let file = open_file_checked(
5655 &dir,
5656 "foo",
5657 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5658 &Default::default(),
5659 )
5660 .await;
5661 close_file_checked(file).await;
5662
5663 let len_after_first_lookup = cache.len();
5664 assert!(len_after_first_lookup >= 1, "Cache should contain at least the looked up file");
5665
5666 let file = open_file_checked(
5667 &dir,
5668 "FOO",
5669 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5670 &Default::default(),
5671 )
5672 .await;
5673 close_file_checked(file).await;
5674
5675 assert_eq!(
5676 cache.len(),
5677 len_after_first_lookup,
5678 "Cache size increased (duplicate entries found)"
5679 );
5680
5681 let file = open_file_checked(
5682 &dir,
5683 "Foo",
5684 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5685 &Default::default(),
5686 )
5687 .await;
5688 close_file_checked(file).await;
5689
5690 assert_eq!(
5691 cache.len(),
5692 len_after_first_lookup,
5693 "Cache size increased (duplicate entries found)"
5694 );
5695
5696 fixture.close().await;
5697 }
5698
5699 #[fuchsia::test]
5700 async fn test_casefold_cache_rename_invalidation() {
5701 let fixture = TestFixture::new().await;
5702 let root = fixture.root();
5703
5704 let dir = open_dir_checked(
5705 &root,
5706 "dir",
5707 fio::Flags::FLAG_MAYBE_CREATE
5708 | fio::PERM_READABLE
5709 | fio::PERM_WRITABLE
5710 | fio::Flags::PROTOCOL_DIRECTORY,
5711 Default::default(),
5712 )
5713 .await;
5714 enable_casefold(&dir).await;
5715
5716 let file = open_file_checked(
5717 &dir,
5718 "foo",
5719 fio::Flags::FLAG_MAYBE_CREATE
5720 | fio::PERM_READABLE
5721 | fio::PERM_WRITABLE
5722 | fio::Flags::PROTOCOL_FILE,
5723 &Default::default(),
5724 )
5725 .await;
5726 close_file_checked(file).await;
5727
5728 let file = open_file_checked(
5729 &dir,
5730 "foo",
5731 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5732 &Default::default(),
5733 )
5734 .await;
5735 close_file_checked(file).await;
5736
5737 let dst_token = dir.get_token().await.expect("FIDL call failed").expect("get_token failed");
5738 dir.rename("FOO", dst_token, "bar")
5739 .await
5740 .expect("Rename FIDL call failed")
5741 .expect("rename failed");
5742
5743 assert_matches!(
5744 open_file(
5745 &dir,
5746 "foo",
5747 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5748 &Default::default()
5749 )
5750 .await
5751 .expect_err("Open \"foo\" succeeded after rename")
5752 .root_cause()
5753 .downcast_ref::<zx::Status>(),
5754 Some(&zx::Status::NOT_FOUND)
5755 );
5756
5757 let file = open_file_checked(
5758 &dir,
5759 "bar",
5760 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5761 &Default::default(),
5762 )
5763 .await;
5764 close_file_checked(file).await;
5765
5766 fixture.close().await;
5767 }
5768
5769 #[fuchsia::test]
5770 async fn test_casefold_cache_unlink_invalidation() {
5771 let fixture = TestFixture::new().await;
5772 let root = fixture.root();
5773
5774 let dir = open_dir_checked(
5775 &root,
5776 "dir",
5777 fio::Flags::FLAG_MAYBE_CREATE
5778 | fio::PERM_READABLE
5779 | fio::PERM_WRITABLE
5780 | fio::Flags::PROTOCOL_DIRECTORY,
5781 Default::default(),
5782 )
5783 .await;
5784 enable_casefold(&dir).await;
5785
5786 let file = open_file_checked(
5787 &dir,
5788 "foo",
5789 fio::Flags::FLAG_MAYBE_CREATE
5790 | fio::PERM_READABLE
5791 | fio::PERM_WRITABLE
5792 | fio::Flags::PROTOCOL_FILE,
5793 &Default::default(),
5794 )
5795 .await;
5796 close_file_checked(file).await;
5797
5798 let file = open_file_checked(
5799 &dir,
5800 "foo",
5801 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5802 &Default::default(),
5803 )
5804 .await;
5805 close_file_checked(file).await;
5806
5807 dir.unlink("FOO", &fio::UnlinkOptions::default())
5808 .await
5809 .expect("Unlink FIDL call failed")
5810 .expect("Unlink failed");
5811
5812 assert_matches!(
5813 open_file(
5814 &dir,
5815 "foo",
5816 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5817 &Default::default()
5818 )
5819 .await
5820 .expect_err("Open \"foo\" succeeded after unlink")
5821 .root_cause()
5822 .downcast_ref::<zx::Status>(),
5823 Some(&zx::Status::NOT_FOUND)
5824 );
5825
5826 fixture.close().await;
5827 }
5828
5829 async fn assert_watch_event(
5830 watcher: &mut Watcher,
5831 expected_event: WatchEvent,
5832 expected_name: &str,
5833 ) {
5834 assert_eq!(
5835 watcher.next().await.unwrap().unwrap(),
5836 WatchMessage { event: expected_event, filename: expected_name.into() }
5837 );
5838 }
5839
5840 async fn set_up_casefold_dir_with_files(
5841 fixture: &TestFixture,
5842 dir_name: &str,
5843 files: &[&str],
5844 ) -> (fio::DirectoryProxy, Watcher) {
5845 let root = fixture.root();
5846 let dir = open_dir_checked(
5847 root,
5848 dir_name,
5849 fio::Flags::FLAG_MAYBE_CREATE
5850 | fio::PERM_READABLE
5851 | fio::PERM_WRITABLE
5852 | fio::Flags::PROTOCOL_DIRECTORY,
5853 Default::default(),
5854 )
5855 .await;
5856 enable_casefold(&dir).await;
5857
5858 for &file_name in files {
5859 let file = open_file_checked(
5860 &dir,
5861 file_name,
5862 fio::Flags::FLAG_MAYBE_CREATE
5863 | fio::PERM_READABLE
5864 | fio::PERM_WRITABLE
5865 | fio::Flags::PROTOCOL_FILE,
5866 &Default::default(),
5867 )
5868 .await;
5869 close_file_checked(file).await;
5870 }
5871
5872 let mut watcher = Watcher::new(&dir).await.unwrap();
5873 assert_watch_event(&mut watcher, WatchEvent::EXISTING, ".").await;
5874
5875 let mut existing_files = files
5876 .iter()
5877 .map(|s| std::path::PathBuf::from(*s))
5878 .collect::<std::collections::HashSet<_>>();
5879 while !existing_files.is_empty() {
5880 let msg = watcher.next().await.unwrap().unwrap();
5881 assert_eq!(msg.event, WatchEvent::EXISTING);
5882 assert!(
5883 existing_files.remove(&msg.filename),
5884 "Unexpected existing file: {:?}",
5885 msg.filename
5886 );
5887 }
5888
5889 assert_watch_event(&mut watcher, WatchEvent::IDLE, "").await;
5890
5891 (dir, watcher)
5892 }
5893
5894 #[fuchsia::test]
5895 async fn test_casefold_rename_watcher_events() {
5896 let fixture = TestFixture::new().await;
5897 let (dir, mut watcher) = set_up_casefold_dir_with_files(&fixture, "dir", &["foo"]).await;
5898
5899 let dst_token = dir.get_token().await.expect("FIDL call failed").expect("get_token failed");
5900 dir.rename("FOO", dst_token, "BAR")
5901 .await
5902 .expect("Rename FIDL call failed")
5903 .expect("rename failed");
5904
5905 assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "foo").await;
5906 assert_watch_event(&mut watcher, WatchEvent::ADD_FILE, "BAR").await;
5907
5908 fixture.close().await;
5909 }
5910
5911 #[fuchsia::test]
5912 async fn test_casefold_rename_overwrite_watcher_events() {
5913 let fixture = TestFixture::new().await;
5914 let (dir, mut watcher) =
5915 set_up_casefold_dir_with_files(&fixture, "dir", &["foo", "bar"]).await;
5916
5917 let dst_token = dir.get_token().await.expect("FIDL call failed").expect("get_token failed");
5918 dir.rename("FOO", dst_token, "BAR")
5919 .await
5920 .expect("Rename FIDL call failed")
5921 .expect("rename failed");
5922
5923 assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "foo").await;
5924 assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "bar").await;
5925 assert_watch_event(&mut watcher, WatchEvent::ADD_FILE, "BAR").await;
5926
5927 fixture.close().await;
5928 }
5929
5930 #[fuchsia::test]
5931 async fn test_casefold_rename_cross_dir_overwrite_watcher_events() {
5932 let fixture = TestFixture::new().await;
5933 let (src_dir, mut src_watcher) =
5934 set_up_casefold_dir_with_files(&fixture, "src_dir", &["foo"]).await;
5935 let (dst_dir, mut dst_watcher) =
5936 set_up_casefold_dir_with_files(&fixture, "dst_dir", &["bar"]).await;
5937
5938 let dst_token =
5939 dst_dir.get_token().await.expect("FIDL call failed").expect("get_token failed");
5940 src_dir
5941 .rename("FOO", dst_token, "BAR")
5942 .await
5943 .expect("Rename FIDL call failed")
5944 .expect("rename failed");
5945
5946 assert_watch_event(&mut src_watcher, WatchEvent::REMOVE_FILE, "foo").await;
5947
5948 assert_watch_event(&mut dst_watcher, WatchEvent::REMOVE_FILE, "bar").await;
5949 assert_watch_event(&mut dst_watcher, WatchEvent::ADD_FILE, "BAR").await;
5950
5951 fixture.close().await;
5952 }
5953
5954 #[fuchsia::test]
5955 async fn test_casefold_unlink_watcher_events() {
5956 let fixture = TestFixture::new().await;
5957 let (dir, mut watcher) = set_up_casefold_dir_with_files(&fixture, "dir", &["foo"]).await;
5958
5959 dir.unlink("FOO", &fio::UnlinkOptions::default())
5960 .await
5961 .expect("Unlink FIDL call failed")
5962 .expect("Unlink failed");
5963
5964 assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "foo").await;
5965
5966 fixture.close().await;
5967 }
5968
5969 #[fuchsia::test]
5970 async fn test_casefold_rename_case_only_watcher_events() {
5971 let fixture = TestFixture::new().await;
5972 let (dir, mut watcher) = set_up_casefold_dir_with_files(&fixture, "dir", &["Foo"]).await;
5973
5974 let dst_token = dir.get_token().await.expect("FIDL call failed").expect("get_token failed");
5975 dir.rename("Foo", dst_token, "FOO")
5976 .await
5977 .expect("Rename FIDL call failed")
5978 .expect("rename failed");
5979
5980 assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "Foo").await;
5981 assert_watch_event(&mut watcher, WatchEvent::ADD_FILE, "FOO").await;
5982
5983 fixture.close().await;
5984 }
5985
5986 #[fuchsia::test]
5987 async fn test_unlink_purges_in_single_transaction_when_closed() {
5988 let fixture = TestFixture::new().await;
5989 let root = fixture.root();
5990 let volume = fixture.volume().volume();
5991 let store = volume.store();
5992 let root_dir = fixture
5993 .volume()
5994 .root()
5995 .clone()
5996 .into_any()
5997 .downcast::<FxDirectory>()
5998 .expect("Not a directory");
5999
6000 let file = open_file_checked(
6003 root,
6004 "cached_file",
6005 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
6006 &fio::Options::default(),
6007 )
6008 .await;
6009 let (_mutable, immutable) = file
6010 .get_attributes(fio::NodeAttributesQuery::ID)
6011 .await
6012 .expect("transport error on get_attributes")
6013 .expect("get_attributes failed");
6014 let cached_id = immutable.id.unwrap();
6015 file::write(&file, b"hello").await.expect("write failed");
6016 close_file_checked(file).await;
6017
6018 assert!(volume.cache().contains_key(cached_id));
6021 root_dir.remove_from_dirent_cache("cached_file");
6022 assert!(!volume.cache().contains_key(cached_id));
6023
6024 let closed_file = open_file_checked(
6029 root,
6030 "closed_file",
6031 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
6032 &fio::Options::default(),
6033 )
6034 .await;
6035 let (_mutable, immutable) = closed_file
6036 .get_attributes(fio::NodeAttributesQuery::ID)
6037 .await
6038 .expect("transport error on get_attributes")
6039 .expect("get_attributes failed");
6040 let closed_id = immutable.id.unwrap();
6041 file::write(&closed_file, b"hello").await.expect("write failed");
6042 close_file_checked(closed_file).await;
6043
6044 assert!(volume.cache().contains_key(closed_id));
6045 root.unlink("closed_file", &fio::UnlinkOptions::default())
6046 .await
6047 .expect("FIDL call failed")
6048 .expect("unlink failed");
6049 assert!(!volume.cache().contains_key(closed_id));
6050 assert!(
6052 store
6053 .tree()
6054 .find(&ObjectKey::graveyard_entry(store.graveyard_directory_object_id(), closed_id))
6055 .await
6056 .expect("find failed")
6057 .is_none()
6058 );
6059
6060 let open_file = open_file_checked(
6064 root,
6065 "open_file",
6066 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
6067 &fio::Options::default(),
6068 )
6069 .await;
6070 let (_mutable, immutable) = open_file
6071 .get_attributes(fio::NodeAttributesQuery::ID)
6072 .await
6073 .expect("transport error on get_attributes")
6074 .expect("get_attributes failed");
6075 let open_id = immutable.id.unwrap();
6076 file::write(&open_file, b"hello").await.expect("write failed");
6077
6078 root.unlink("open_file", &fio::UnlinkOptions::default())
6079 .await
6080 .expect("FIDL call failed")
6081 .expect("unlink failed");
6082 assert!(volume.cache().contains_key(open_id));
6083 assert!(
6084 store
6085 .tree()
6086 .find(&ObjectKey::graveyard_entry(store.graveyard_directory_object_id(), open_id))
6087 .await
6088 .expect("find failed")
6089 .is_some()
6090 );
6091 close_file_checked(open_file).await;
6092
6093 drop(root_dir);
6094 fixture.close().await;
6095 }
6096
6097 #[fuchsia::test]
6098 async fn test_symlink_removed_from_node_cache_on_drop() {
6099 let fixture = TestFixture::new().await;
6100 let root = fixture.root();
6101 let volume = fixture.volume().volume();
6102
6103 let (proxy, server_end) = create_proxy::<fio::SymlinkMarker>();
6104 root.create_symlink("test_symlink", b"target", Some(server_end))
6105 .await
6106 .expect("FIDL call failed")
6107 .expect("create_symlink failed");
6108
6109 let (_mutable, immutable) = proxy
6110 .get_attributes(fio::NodeAttributesQuery::ID)
6111 .await
6112 .expect("transport error on get_attributes")
6113 .expect("get_attributes failed");
6114 let symlink_id = immutable.id.unwrap();
6115
6116 assert!(volume.cache().contains_key(symlink_id));
6118
6119 proxy
6120 .close()
6121 .await
6122 .expect("FIDL call failed")
6123 .map_err(zx::Status::err_from_raw)
6124 .expect("close failed");
6125
6126 assert!(!volume.cache().contains_key(symlink_id));
6129
6130 fixture.close().await;
6131 }
6132
6133 #[fuchsia::test]
6134 async fn test_sync_flushes_file_data() {
6135 let fixture = TestFixture::new().await;
6136 let root = fixture.root();
6137 let volume = fixture.volume().volume();
6138
6139 let file = open_file_checked(
6140 root,
6141 "test_file",
6142 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::PERM_WRITABLE,
6143 &fio::Options::default(),
6144 )
6145 .await;
6146
6147 let (_mutable, immutable) = file
6148 .get_attributes(fio::NodeAttributesQuery::ID)
6149 .await
6150 .expect("transport error on get_attributes")
6151 .expect("get_attributes failed");
6152 let file_id = immutable.id.unwrap();
6153
6154 let node = volume
6155 .cache()
6156 .get(file_id)
6157 .expect("Node should be cached")
6158 .into_any()
6159 .downcast::<FxFile>()
6160 .unwrap();
6161
6162 file::write(&file, b"hello world").await.expect("write failed");
6163
6164 assert!(node.handle().needs_flush());
6165
6166 root.sync()
6167 .await
6168 .expect("FIDL call failed")
6169 .map_err(zx::Status::err_from_raw)
6170 .expect("sync failed");
6171
6172 assert!(!node.handle().needs_flush());
6173
6174 close_file_checked(file).await;
6175 drop(node);
6176 fixture.close().await;
6177 }
6178
6179 #[fuchsia::test]
6180 async fn test_large_symlink_attributes_and_target() {
6181 let fixture = TestFixture::new().await;
6182 let root = fixture.root();
6183 let block_size = fixture.fs().block_size();
6184
6185 assert_eq!(
6186 root.create_symlink("too_big", &vec![b'a'; 4094], None)
6187 .await
6188 .expect("FIDL call failed")
6189 .expect_err("too big symlink target should fail"),
6190 zx::Status::BAD_PATH.into_raw(),
6191 );
6192
6193 let inline_target = vec![b'i'; 256];
6194 let spilled_target = vec![b's'; 4093];
6195
6196 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
6197 crypt
6198 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
6199 .expect("Failed to add wrapping key");
6200 let fscrypt_dir = open_dir_checked(
6201 &root,
6202 "fscrypt_dir",
6203 fio::Flags::FLAG_MAYBE_CREATE
6204 | fio::PERM_READABLE
6205 | fio::PERM_WRITABLE
6206 | fio::Flags::PROTOCOL_DIRECTORY,
6207 Default::default(),
6208 )
6209 .await;
6210 fscrypt_dir
6211 .update_attributes(&fio::MutableNodeAttributes {
6212 encryption_policy: Some(fio::FscryptPolicy {
6213 key_identifier: WRAPPING_KEY_ID,
6214 flags: fxfs::object_store::LEGACY_FSCRYPT_FLAGS,
6215 }),
6216 ..Default::default()
6217 })
6218 .await
6219 .expect("FIDL call failed")
6220 .map_err(zx::ok)
6221 .expect("update_attributes failed");
6222
6223 for dir in [&root, &fscrypt_dir] {
6224 let (inline_proxy, inline_server) = create_proxy::<fio::SymlinkMarker>();
6225 dir.create_symlink("inline_symlink", &inline_target, Some(inline_server))
6226 .await
6227 .expect("FIDL call failed")
6228 .expect("create_symlink failed");
6229
6230 let (spilled_proxy, spilled_server) = create_proxy::<fio::SymlinkMarker>();
6231 dir.create_symlink("spilled_symlink", &spilled_target, Some(spilled_server))
6232 .await
6233 .expect("FIDL call failed")
6234 .expect("create_symlink failed");
6235
6236 let inline_info = inline_proxy.describe().await.expect("describe failed");
6237 assert_eq!(inline_info.target.as_deref(), Some(inline_target.as_slice()));
6238 let (_, inline_immutable) = inline_proxy
6239 .get_attributes(
6240 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
6241 )
6242 .await
6243 .expect("FIDL call failed")
6244 .expect("get_attributes failed");
6245 assert_eq!(inline_immutable.content_size, Some(256));
6246 assert_eq!(inline_immutable.storage_size, Some(0));
6247
6248 let spilled_info = spilled_proxy.describe().await.expect("describe failed");
6249 assert_eq!(spilled_info.target.as_deref(), Some(spilled_target.as_slice()));
6250 let (_, spilled_immutable) = spilled_proxy
6251 .get_attributes(
6252 fio::NodeAttributesQuery::CONTENT_SIZE | fio::NodeAttributesQuery::STORAGE_SIZE,
6253 )
6254 .await
6255 .expect("FIDL call failed")
6256 .expect("get_attributes failed");
6257 assert_eq!(spilled_immutable.content_size, Some(4093));
6258 assert_eq!(spilled_immutable.storage_size, Some(block_size.get()));
6259 }
6260
6261 fixture.close().await;
6262 }
6263}