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