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_policy_equivalence(
360 &self,
361 source_wrapping_key_id: Option<WrappingKeyId>,
362 ) -> Result<(), zx::Status> {
363 if let Some(target_id) = self.directory().dir_type().wrapping_key_id() {
364 if Some(target_id) != source_wrapping_key_id {
365 return Err(zx::Status::BAD_STATE);
366 }
367 }
368 Ok(())
369 }
370
371 pub(crate) async fn link_object(
372 &self,
373 mut transaction: Transaction<'_>,
374 name: &str,
375 source_id: u64,
376 kind: ObjectDescriptor,
377 ) -> Result<(), zx::Status> {
378 let store = self.store();
379 if self.is_deleted() {
380 return Err(zx::Status::ACCESS_DENIED);
381 }
382 if self.directory.lookup(&name).await.map_err(map_to_status)?.is_some() {
383 return Err(zx::Status::ALREADY_EXISTS);
384 }
385 self.directory
386 .insert_child(&mut transaction, &name, source_id, kind.clone())
387 .await
388 .map_err(map_to_status)?;
389 store.adjust_refs(&mut transaction, source_id, 1).await.map_err(map_to_status)?;
390 transaction
391 .commit_with_callback(|_| self.did_add(&name, None))
392 .await
393 .map_err(map_to_status)?;
394 Ok(())
395 }
396
397 pub(crate) async fn link_graveyard_object<F>(
400 &self,
401 mut transaction: Transaction<'_>,
402 name: &str,
403 source_id: u64,
404 kind: ObjectDescriptor,
405 transaction_callback: F,
406 ) -> Result<(), zx::Status>
407 where
408 F: FnOnce() + Send,
409 {
410 let store = self.store();
411 if self.is_deleted() {
412 return Err(zx::Status::ACCESS_DENIED);
413 }
414 if self.directory.lookup(&name).await.map_err(map_to_status)?.is_some() {
415 return Err(zx::Status::ALREADY_EXISTS);
416 }
417 store.remove_from_graveyard(&mut transaction, source_id);
420 self.directory
421 .insert_child(&mut transaction, &name, source_id, kind.clone())
422 .await
423 .map_err(map_to_status)?;
424 transaction
425 .commit_with_callback(|_| {
426 transaction_callback();
427 self.did_add(&name, None);
428 })
429 .await
430 .map_err(map_to_status)?;
431 Ok(())
432 }
433
434 async fn link_impl(
435 self: Arc<Self>,
436 name: String,
437 source_dir: Arc<dyn Any + Send + Sync>,
438 source_name: &str,
439 ) -> Result<(), zx::Status> {
440 let source_dir = source_dir.downcast::<Self>().unwrap();
441 let store = self.store();
442 let mut source_id =
443 match source_dir.directory.lookup(source_name).await.map_err(map_to_status)? {
444 Some((object_id, ObjectDescriptor::File, _)) => object_id,
445 None => return Err(zx::Status::NOT_FOUND),
446 _ => return Err(zx::Status::NOT_SUPPORTED),
447 };
448 loop {
449 let transaction = store
457 .new_transaction(
458 lock_keys![
459 LockKey::object(store.store_object_id(), self.object_id()),
460 LockKey::object(store.store_object_id(), source_id),
461 ],
462 Options::default(),
463 )
464 .await
465 .map_err(map_to_status)?;
466 self.check_fscrypt_policy_equivalence(source_dir.directory().wrapping_key_id())?;
467 match source_dir.directory.lookup(source_name).await.map_err(map_to_status)? {
469 Some((new_id, ObjectDescriptor::File, _)) => {
470 if new_id == source_id {
471 return self
473 .link_object(transaction, &name, source_id, ObjectDescriptor::File)
474 .await;
475 } else {
476 source_id = new_id
477 }
478 }
479 None => return Err(zx::Status::NOT_FOUND),
480 _ => return Err(zx::Status::NOT_SUPPORTED),
481 }
482 }
483 }
484
485 async fn rename_impl(
486 self: Arc<Self>,
487 src_dir: Arc<dyn MutableDirectory>,
488 src_name: Path,
489 dst_name: Path,
490 ) -> Result<(), zx::Status> {
491 if !src_name.is_single_component() || !dst_name.is_single_component() {
492 return Err(zx::Status::INVALID_ARGS);
493 }
494 let (src, dst) = (src_name.peek().unwrap(), dst_name.peek().unwrap());
495 let src_dir =
496 src_dir.into_any().downcast::<FxDirectory>().map_err(|_| Err(zx::Status::NOT_DIR))?;
497
498 let replace_context = self
501 .directory
502 .acquire_context_for_replace(Some((src_dir.directory(), src)), dst, false)
503 .await
504 .map_err(map_to_status)?;
505 let mut transaction = replace_context.transaction;
506
507 if self.is_deleted() {
508 return Err(zx::Status::NOT_FOUND);
509 }
510
511 let (moved_id, moved_descriptor) =
512 replace_context.src_id_and_descriptor.clone().ok_or(zx::Status::NOT_FOUND)?;
513
514 if let ObjectDescriptor::File = moved_descriptor {
516 if src_name.is_dir() || dst_name.is_dir() {
517 return Err(zx::Status::NOT_DIR);
518 }
519 }
520
521 if src_dir.object_id() == self.object_id() && src == dst {
524 return Ok(());
525 }
526
527 if let Some((_, dst_descriptor)) = replace_context.dst_id_and_descriptor.as_ref() {
528 match (&moved_descriptor, dst_descriptor) {
530 (ObjectDescriptor::Directory, ObjectDescriptor::Directory) => {}
531 (
532 ObjectDescriptor::File | ObjectDescriptor::Symlink,
533 ObjectDescriptor::File | ObjectDescriptor::Symlink,
534 ) => {}
535 (ObjectDescriptor::Directory, _) => return Err(zx::Status::NOT_DIR),
536 (ObjectDescriptor::File | ObjectDescriptor::Symlink, _) => {
537 return Err(zx::Status::NOT_FILE);
538 }
539 _ => return Err(zx::Status::IO_DATA_INTEGRITY),
540 }
541 }
542
543 let moved_node = src_dir
544 .volume()
545 .get_or_load_node(moved_id, moved_descriptor.clone(), Some(src_dir.clone()))
546 .await
547 .map_err(map_to_status)?;
548
549 if let ObjectDescriptor::Directory = moved_descriptor {
550 let mut node_opt = Some(self.clone());
552 while let Some(node) = node_opt {
553 if node.object_id() == moved_node.object_id() {
554 return Err(zx::Status::INVALID_ARGS);
555 }
556 node_opt = node.parent();
557 }
558 }
559
560 let replace_result = directory::replace_child(
561 &mut transaction,
562 Some((src_dir.directory(), src)),
563 (self.directory(), dst),
564 )
565 .await
566 .map_err(map_to_status)?;
567
568 let actual_src_name = replace_context.src_name.as_deref().unwrap_or(src);
571 let actual_dst_name = replace_context.dst_name.as_deref().unwrap_or(dst);
572
573 transaction
574 .commit_with_callback(|_| {
575 moved_node.set_parent(self.clone());
576 src_dir.did_remove(actual_src_name);
577
578 match replace_result {
579 ReplacedChild::None => {}
580 ReplacedChild::ObjectWithRemainingLinks(..) | ReplacedChild::Object(_) => {
581 self.did_remove(actual_dst_name);
582 }
583 ReplacedChild::Directory(id) => {
584 let store = self.store();
585 store
586 .filesystem()
587 .graveyard()
588 .queue_tombstone_object(store.store_object_id(), id);
589 self.did_remove(actual_dst_name);
590 self.volume().mark_directory_deleted(id);
591 }
592 }
593 self.did_add(dst, Some(moved_node));
594 })
595 .await
596 .map_err(map_to_status)?;
597
598 if let ReplacedChild::Object(id) = replace_result {
599 self.volume().maybe_purge_file(id).await.map_err(map_to_status)?;
600 }
601 Ok(())
602 }
603
604 pub(crate) async fn open_block_file(
605 self: &Arc<Self>,
606 name: &str,
607 server_end: ServerEnd<BlockMarker>,
608 ) {
609 let request = ObjectRequest::new(
610 fio::Flags::empty(),
611 &fio::Options::default(),
612 server_end.into_channel(),
613 );
614 let scope = self.volume().scope().clone();
615 let this = self.clone();
616 request
617 .handle_async(async move |request| {
618 let path = Path::validate_and_split(name).and_then(|p| {
619 if p.is_single_component() { Ok(p) } else { Err(zx::Status::INVALID_ARGS) }
620 })?;
621 let node = this
622 .lookup(&fio::Flags::empty(), path, request)
623 .await
624 .map_err(map_to_status)?;
625 if node.is::<FxFile>() {
626 let file = node.downcast::<FxFile>().unwrap_or_else(|_| unreachable!());
627 if file.is_verified_file() {
628 log::error!("Tried to expose a verified file as a block device.");
629 return Err(zx::Status::NOT_SUPPORTED);
630 }
631 let server = BlockServer::new(file, request.take().into_channel());
632 scope.spawn(server.run());
633 Ok(())
634 } else {
635 Err(zx::Status::NOT_FILE)
636 }
637 })
638 .await
639 }
640}
641
642impl Drop for FxDirectory {
643 fn drop(&mut self) {
644 self.volume().cache().remove(self);
645 }
646}
647
648impl FxNode for FxDirectory {
649 fn object_id(&self) -> u64 {
650 self.directory.object_id()
651 }
652
653 fn parent(&self) -> Option<Arc<FxDirectory>> {
654 self.parent.as_ref().map(|p| p.lock().clone())
655 }
656
657 fn set_parent(&self, parent: Arc<FxDirectory>) {
658 match &self.parent {
659 Some(p) => *p.lock() = parent,
660 None => panic!("Called set_parent on root node"),
661 }
662 }
663
664 fn open_count_add_one(&self) {}
666 fn open_count_sub_one(self: Arc<Self>) {}
667
668 fn object_descriptor(&self) -> ObjectDescriptor {
669 ObjectDescriptor::Directory
670 }
671}
672
673impl MutableDirectory for FxDirectory {
674 fn link<'a>(
675 self: Arc<Self>,
676 name: String,
677 source_dir: Arc<dyn Any + Send + Sync>,
678 source_name: &'a str,
679 ) -> BoxFuture<'a, Result<(), zx::Status>> {
680 Box::pin(self.link_impl(name, source_dir, source_name))
681 }
682
683 async fn unlink(
684 self: Arc<Self>,
685 name: &str,
686 must_be_directory: bool,
687 ) -> Result<(), zx::Status> {
688 let replace_context = self
689 .directory
690 .acquire_context_for_replace(None, name, true)
691 .await
692 .map_err(map_to_status)?;
693 let actual_dst_name = replace_context.dst_name.as_deref().unwrap_or(name);
696 let mut transaction = replace_context.transaction;
697 let (_, object_descriptor) =
698 replace_context.dst_id_and_descriptor.ok_or(zx::Status::NOT_FOUND)?;
699 if let ObjectDescriptor::Directory = object_descriptor {
700 } else if must_be_directory {
701 return Err(zx::Status::NOT_DIR);
702 }
703 match directory::replace_child(&mut transaction, None, (self.directory(), name))
704 .await
705 .map_err(map_to_status)?
706 {
707 ReplacedChild::None => return Err(zx::Status::NOT_FOUND),
708 ReplacedChild::ObjectWithRemainingLinks(..) => {
709 transaction
710 .commit_with_callback(|_| self.did_remove(actual_dst_name))
711 .await
712 .map_err(map_to_status)?;
713 }
714 ReplacedChild::Object(id) => {
715 transaction
716 .commit_with_callback(|_| self.did_remove(actual_dst_name))
717 .await
718 .map_err(map_to_status)?;
719 if let Err(e) = self.volume().maybe_purge_file(id).await {
722 warn!(error:? = e; "Failed to purge file");
723 }
724 }
725 ReplacedChild::Directory(id) => {
726 transaction
727 .commit_with_callback(|_| {
728 let store = self.store();
729 store
730 .filesystem()
731 .graveyard()
732 .queue_tombstone_object(store.store_object_id(), id);
733 self.did_remove(actual_dst_name);
734 self.volume().mark_directory_deleted(id);
735 })
736 .await
737 .map_err(map_to_status)?;
738 }
739 }
740 Ok(())
741 }
742
743 async fn update_attributes(
744 &self,
745 attributes: fio::MutableNodeAttributes,
746 ) -> Result<(), zx::Status> {
747 if let Some(casefold) = attributes.casefold {
749 self.directory.set_casefold(casefold).await.map_err(map_to_status)?;
750 }
751 let transaction = self
752 .store()
753 .new_transaction(
754 lock_keys![LockKey::object(
755 self.store().store_object_id(),
756 self.directory.object_id()
757 )],
758 Options { borrow_metadata_space: true, ..Default::default() },
759 )
760 .await
761 .map_err(map_to_status)?;
762
763 self.directory
764 .update_attributes(transaction, Some(&attributes), 0, Some(Timestamp::now()))
765 .await
766 .map_err(map_to_status)?;
767 Ok(())
768 }
769
770 async fn sync(&self) -> Result<(), zx::Status> {
771 self.volume()
773 .store()
774 .filesystem()
775 .sync(SyncOptions { flush_device: true, ..Default::default() })
776 .await
777 .map_err(map_to_status)
778 }
779
780 fn rename(
781 self: Arc<Self>,
782 src_dir: Arc<dyn MutableDirectory>,
783 src_name: Path,
784 dst_name: Path,
785 ) -> BoxFuture<'static, Result<(), zx::Status>> {
786 Box::pin(self.rename_impl(src_dir, src_name, dst_name))
787 }
788
789 async fn create_symlink(
790 &self,
791 name: String,
792 target: Vec<u8>,
793 connection: Option<ServerEnd<fio::SymlinkMarker>>,
794 ) -> Result<(), zx::Status> {
795 let store = self.store();
796 let dir = &self.directory;
797 let keys = lock_keys![LockKey::object(store.store_object_id(), dir.object_id())];
798 let mut transaction =
799 store.new_transaction(keys, Options::default()).await.map_err(map_to_status)?;
800 if dir.lookup(&name).await.map_err(map_to_status)?.is_some() {
801 return Err(zx::Status::ALREADY_EXISTS);
802 }
803 let object_id =
804 dir.create_symlink(&mut transaction, &target, &name).await.map_err(map_to_status)?;
805 if let Some(connection) = connection {
806 if let GetResult::Placeholder(p) = self.volume().cache().get_or_reserve(object_id).await
807 {
808 transaction
809 .commit_with_callback(|_| {
810 let node = Arc::new(FxSymlink::new(self.volume().clone(), object_id));
811 p.commit(&(node.clone() as Arc<dyn FxNode>));
812 let scope = self.volume().scope().clone();
813 let flags =
814 fio::Flags::PROTOCOL_SYMLINK | fio::PERM_READABLE | fio::PERM_WRITABLE;
815 let opened_node = OpenedNode::new(node);
817 symlink::Connection::create_sync(
820 scope,
821 opened_node.take(),
822 flags,
823 flags.to_object_request(connection),
824 );
825 })
826 .await
827 } else {
828 return Err(zx::Status::IO_DATA_INTEGRITY);
831 }
832 } else {
833 transaction.commit().await.map(|_| ())
834 }
835 .map_err(map_to_status)
836 }
837}
838
839impl DirectoryEntry for FxDirectory {
840 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), zx::Status> {
841 request.open_dir(self)
842 }
843
844 fn scope(&self) -> Option<ExecutionScope> {
845 Some(self.volume().scope().clone())
846 }
847}
848
849impl GetEntryInfo for FxDirectory {
850 fn entry_info(&self) -> EntryInfo {
851 EntryInfo::new(self.object_id(), fio::DirentType::Directory)
852 }
853}
854
855impl vfs::node::Node for FxDirectory {
856 async fn get_attributes(
857 &self,
858 requested_attributes: fio::NodeAttributesQuery,
859 ) -> Result<fio::NodeAttributes2, zx::Status> {
860 let mut props = self.directory.get_properties().await.map_err(map_to_status)?;
861
862 if requested_attributes.contains(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE) {
863 self.store()
864 .update_access_time(self.directory.object_id(), &mut props, || !self.is_deleted())
865 .await
866 .map_err(map_to_status)?;
867 }
868
869 Ok(attributes!(
870 requested_attributes,
871 Mutable {
872 creation_time: props.creation_time.as_nanos(),
873 modification_time: props.modification_time.as_nanos(),
874 access_time: props.access_time.as_nanos(),
875 mode: props.posix_attributes.map(|a| a.mode),
876 uid: props.posix_attributes.map(|a| a.uid),
877 gid: props.posix_attributes.map(|a| a.gid),
878 rdev: props.posix_attributes.map(|a| a.rdev),
879 casefold: self.directory.dir_type().is_casefold(),
880 selinux_context: self
881 .directory
882 .handle()
883 .get_inline_selinux_context()
884 .await
885 .map_err(map_to_status)?,
886 wrapping_key_id: props.dir_type.wrapping_key_id(),
887 },
888 Immutable {
889 protocols: fio::NodeProtocolKinds::DIRECTORY,
890 abilities: fio::Operations::GET_ATTRIBUTES
891 | fio::Operations::UPDATE_ATTRIBUTES
892 | fio::Operations::ENUMERATE
893 | fio::Operations::TRAVERSE
894 | fio::Operations::MODIFY_DIRECTORY,
895 content_size: props.data_attribute_size,
896 storage_size: props.allocated_size,
897 link_count: props.refs + 1 + props.sub_dirs,
898 id: self.directory.object_id(),
899 change_time: props.change_time.as_nanos(),
900 verity_enabled: false,
901 }
902 ))
903 }
904
905 fn query_filesystem(&self) -> Result<fio::FilesystemInfo, zx::Status> {
906 Ok(self.volume().filesystem_info_for_volume())
907 }
908
909 async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, zx::Status> {
910 self.directory.list_extended_attributes().await.map_err(map_to_status)
911 }
912
913 async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, zx::Status> {
914 self.directory.get_extended_attribute(name).await.map_err(map_to_status)
915 }
916
917 async fn set_extended_attribute(
918 &self,
919 name: Vec<u8>,
920 value: Vec<u8>,
921 mode: fio::SetExtendedAttributeMode,
922 ) -> Result<(), zx::Status> {
923 self.directory.set_extended_attribute(name, value, mode.into()).await.map_err(map_to_status)
924 }
925
926 async fn remove_extended_attribute(&self, name: Vec<u8>) -> Result<(), zx::Status> {
927 self.directory.remove_extended_attribute(name).await.map_err(map_to_status)
928 }
929}
930
931impl VfsDirectory for FxDirectory {
932 fn deprecated_open(
933 self: Arc<Self>,
934 scope: ExecutionScope,
935 flags: fio::OpenFlags,
936 path: Path,
937 server_end: ServerEnd<fio::NodeMarker>,
938 ) {
939 scope.clone().spawn(flags.to_object_request(server_end).handle_async(
940 async move |object_request| {
941 let node =
942 self.lookup(&flags, path, object_request).await.map_err(map_to_status)?;
943 if node.is::<FxDirectory>() {
944 let directory =
945 node.downcast::<FxDirectory>().unwrap_or_else(|_| unreachable!()).take();
946 object_request
947 .create_connection::<MutableConnection<_>, _>(scope, directory, flags)
948 .await
949 } else if node.is::<FxFile>() {
950 let node = node.downcast::<FxFile>().unwrap_or_else(|_| unreachable!());
951 if flags.contains(fio::OpenFlags::BLOCK_DEVICE) {
952 if node.is_verified_file() {
953 log::error!("Tried to expose a verified file as a block device.");
954 return Err(zx::Status::NOT_SUPPORTED);
955 }
956 if !flags.contains(fio::OpenFlags::RIGHT_READABLE) {
957 log::error!(
958 "Opening a file as block device requires at least RIGHT_READABLE."
959 );
960 return Err(zx::Status::ACCESS_DENIED);
961 }
962 let server = BlockServer::new(node, object_request.take().into_channel());
963 scope.spawn(server.run());
964 Ok(())
965 } else {
966 FxFile::create_connection_async(node, scope, flags, object_request).await
967 }
968 } else if node.is::<FxSymlink>() {
969 let node = node.downcast::<FxSymlink>().unwrap_or_else(|_| unreachable!());
970 object_request
971 .create_connection::<symlink::Connection<_>, _>(
972 scope.clone(),
973 node.take(),
974 flags,
975 )
976 .await
977 } else {
978 unreachable!();
979 }
980 },
981 ));
982 }
983
984 fn open(
985 self: Arc<Self>,
986 scope: ExecutionScope,
987 path: Path,
988 flags: fio::Flags,
989 object_request: ObjectRequestRef<'_>,
990 ) -> Result<(), zx::Status> {
991 self.volume().scope().clone().spawn(object_request.take().handle_async(
992 async move |object_request| self.open_async(scope, path, flags, object_request).await,
993 ));
994 Ok(())
995 }
996
997 async fn open_async(
998 self: Arc<Self>,
999 scope: ExecutionScope,
1000 path: Path,
1001 flags: fio::Flags,
1002 object_request: ObjectRequestRef<'_>,
1003 ) -> Result<(), zx::Status> {
1004 let node = self.lookup(&flags, path, object_request).await.map_err(map_to_status)?;
1005 if node.is::<FxDirectory>() {
1006 let directory =
1007 node.downcast::<FxDirectory>().unwrap_or_else(|_| unreachable!()).take();
1008 object_request
1009 .create_connection::<MutableConnection<_>, _>(scope, directory, flags)
1010 .await
1011 } else if node.is::<FxFile>() {
1012 let file = node.downcast::<FxFile>().unwrap_or_else(|_| unreachable!());
1013 FxFile::create_connection_async(file, scope, flags, object_request).await
1014 } else if node.is::<FxSymlink>() {
1015 let symlink = node.downcast::<FxSymlink>().unwrap_or_else(|_| unreachable!());
1016 object_request
1017 .create_connection::<symlink::Connection<_>, _>(
1018 scope.clone(),
1019 symlink.take(),
1020 flags,
1021 )
1022 .await
1023 } else {
1024 unreachable!();
1025 }
1026 }
1027
1028 async fn read_dirents(
1029 &self,
1030 pos: &TraversalPosition,
1031 mut sink: Box<dyn dirents_sink::Sink>,
1032 ) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), zx::Status> {
1033 if let TraversalPosition::End = pos {
1034 return Ok((TraversalPosition::End, sink.seal()));
1035 } else if let TraversalPosition::Index(_) = pos {
1036 return Err(zx::Status::BAD_STATE);
1038 }
1039
1040 let store = self.store();
1041 let fs = store.filesystem();
1042 let _read_guard = fs
1043 .lock_manager()
1044 .read_lock(lock_keys![LockKey::object(store.store_object_id(), self.object_id())])
1045 .await;
1046 if self.is_deleted() {
1047 return Ok((TraversalPosition::End, sink.seal()));
1048 }
1049
1050 let layer_set = self.store().tree().layer_set();
1051 let mut merger = layer_set.merger();
1052 let mut iter = match pos {
1053 TraversalPosition::Start => {
1054 match sink
1056 .append(&EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), ".")
1057 {
1058 AppendResult::Ok(new_sink) => sink = new_sink,
1059 AppendResult::Sealed(sealed) => {
1060 return Ok((TraversalPosition::Start, sealed));
1064 }
1065 }
1066 self.directory.iter(&mut merger).await
1067 }
1068 TraversalPosition::Name(name) => self.directory.iter_from(&mut merger, name).await,
1069 TraversalPosition::Bytes(bytes) => {
1070 self.directory.iter_from_bytes(&mut merger, bytes).await
1071 }
1072 _ => unreachable!(),
1073 }
1074 .map_err(map_to_status)?;
1075 while let Some((name, object_id, object_descriptor)) = iter.get() {
1076 let entry_type = match object_descriptor {
1077 ObjectDescriptor::File => fio::DirentType::File,
1078 ObjectDescriptor::Directory => fio::DirentType::Directory,
1079 ObjectDescriptor::Symlink => fio::DirentType::Symlink,
1080 ObjectDescriptor::Volume => return Err(zx::Status::IO_DATA_INTEGRITY),
1081 };
1082
1083 let info = EntryInfo::new(object_id, entry_type);
1084 match sink.append(&info, &name) {
1085 AppendResult::Ok(new_sink) => sink = new_sink,
1086 AppendResult::Sealed(sealed) => {
1087 return Ok((
1094 iter.traversal_position(
1095 |name| TraversalPosition::Name(name.to_string()),
1096 |bytes| TraversalPosition::Bytes(bytes),
1097 )
1098 .unwrap(),
1099 sealed,
1100 ));
1101 }
1102 }
1103 iter.advance().await.map_err(map_to_status)?;
1104 }
1105
1106 Ok((TraversalPosition::End, sink.seal()))
1107 }
1108
1109 fn register_watcher(
1110 self: Arc<Self>,
1111 scope: ExecutionScope,
1112 mask: fio::WatchMask,
1113 watcher: DirectoryWatcher,
1114 ) -> Result<(), zx::Status> {
1115 let controller =
1116 self.watchers.lock().add(scope.clone(), self.clone(), mask, watcher).clone();
1117 if mask.contains(fio::WatchMask::EXISTING) && !self.is_deleted() {
1118 scope.spawn(async move {
1119 let layer_set = self.store().tree().layer_set();
1120 let mut merger = layer_set.merger();
1121 let mut iter = match self.directory.iter_from(&mut merger, "").await {
1122 Ok(iter) => iter,
1123 Err(e) => {
1124 error!(error:? = e; "Failed to iterate directory for watch",);
1125 return;
1128 }
1129 };
1130 controller.send_event(&mut SingleNameEventProducer::existing("."));
1133 while let Some((name, _, _)) = iter.get() {
1134 controller.send_event(&mut SingleNameEventProducer::existing(name));
1135 if let Err(e) = iter.advance().await {
1136 error!(error:? = e; "Failed to iterate directory for watch",);
1137 return;
1138 }
1139 }
1140 controller.send_event(&mut SingleNameEventProducer::idle());
1141 });
1142 }
1143 Ok(())
1144 }
1145
1146 fn unregister_watcher(self: Arc<Self>, key: usize) {
1147 self.watchers.lock().remove(key);
1148 }
1149}
1150
1151impl From<Directory<FxVolume>> for FxDirectory {
1152 fn from(dir: Directory<FxVolume>) -> Self {
1153 Self::new(None, dir)
1154 }
1155}
1156
1157#[cfg(test)]
1158mod tests {
1159 use crate::directory::FxDirectory;
1160 use crate::file::FxFile;
1161 use crate::fuchsia::testing::{
1162 TestFixture, TestFixtureOptions, close_dir_checked, close_file_checked, open_dir,
1163 open_dir_checked, open_file, open_file_checked,
1164 };
1165 use anyhow::bail;
1166 use assert_matches::assert_matches;
1167 use fidl::endpoints::{ClientEnd, Proxy, create_proxy};
1168 use fidl_fuchsia_io as fio;
1169 use fuchsia_async as fasync;
1170 use fuchsia_fs::directory::{DirEntry, DirentKind, WatchEvent, WatchMessage, Watcher};
1171 use fuchsia_fs::file;
1172 use futures::{StreamExt, join};
1173 use fxfs::lsm_tree::Query;
1174 use fxfs::lsm_tree::types::{ItemRef, LayerIterator};
1175 use fxfs::object_store::transaction::{LockKey, lock_keys};
1176 use fxfs::object_store::{ObjectKey, ObjectKeyData, ObjectValue, Timestamp};
1177 use fxfs_crypt_common::CryptBase;
1178 use fxfs_crypto::{FSCRYPT_PADDING, WrappingKeyId};
1179 use std::future::poll_fn;
1180 use std::os::fd::AsRawFd;
1181 use std::sync::Arc;
1182 use std::sync::atomic::{AtomicU64, Ordering};
1183 use std::task::Poll;
1184 use std::time::Duration;
1185 use storage_device::DeviceHolder;
1186 use storage_device::fake_device::FakeDevice;
1187 use vfs::ObjectRequest;
1188 use vfs::node::Node;
1189 use vfs::path::Path;
1190
1191 const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(2);
1192
1193 async fn yield_to_executor() {
1194 let mut done = false;
1195 poll_fn(|cx| {
1196 if done {
1197 Poll::Ready(())
1198 } else {
1199 done = true;
1200 cx.waker().wake_by_ref();
1201 Poll::Pending
1202 }
1203 })
1204 .await;
1205 }
1206
1207 #[fuchsia::test]
1208 async fn test_open_root_dir() {
1209 let fixture = TestFixture::new().await;
1210 let root = fixture.root();
1211 let _: Vec<_> = root.query().await.expect("query failed");
1212 fixture.close().await;
1213 }
1214
1215 #[fuchsia::test]
1216 async fn test_create_dir_persists() {
1217 let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1218 for i in 0..2 {
1219 let fixture = TestFixture::open(
1220 device,
1221 TestFixtureOptions { format: i == 0, ..Default::default() },
1222 )
1223 .await;
1224 let root = fixture.root();
1225
1226 let flags = if i == 0 {
1227 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE
1228 } else {
1229 fio::PERM_READABLE
1230 };
1231 let dir = open_dir_checked(
1232 &root,
1233 "foo",
1234 flags | fio::Flags::PROTOCOL_DIRECTORY,
1235 Default::default(),
1236 )
1237 .await;
1238 close_dir_checked(dir).await;
1239
1240 device = fixture.close().await;
1241 }
1242 }
1243
1244 #[fuchsia::test]
1245 async fn test_open_nonexistent_file() {
1246 let fixture = TestFixture::new().await;
1247 let root = fixture.root();
1248
1249 assert_eq!(
1250 open_file(
1251 &root,
1252 "foo",
1253 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1254 &Default::default()
1255 )
1256 .await
1257 .expect_err("Open succeeded")
1258 .root_cause()
1259 .downcast_ref::<zx::Status>()
1260 .expect("No status"),
1261 &zx::Status::NOT_FOUND,
1262 );
1263
1264 fixture.close().await;
1265 }
1266
1267 #[fuchsia::test]
1268 async fn test_create_file() {
1269 let fixture = TestFixture::new().await;
1270 let root = fixture.root();
1271
1272 let f = open_file_checked(
1273 &root,
1274 "foo",
1275 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1276 &Default::default(),
1277 )
1278 .await;
1279 close_file_checked(f).await;
1280
1281 let f = open_file_checked(
1282 &root,
1283 "foo",
1284 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1285 &Default::default(),
1286 )
1287 .await;
1288 close_file_checked(f).await;
1289
1290 fixture.close().await;
1291 }
1292
1293 #[fuchsia::test]
1294 async fn test_create_dir_nested() {
1295 let fixture = TestFixture::new().await;
1296 let root = fixture.root();
1297
1298 let d = open_dir_checked(
1299 &root,
1300 "foo",
1301 fio::Flags::FLAG_MAYBE_CREATE
1302 | fio::PERM_READABLE
1303 | fio::PERM_WRITABLE
1304 | fio::Flags::PROTOCOL_DIRECTORY,
1305 Default::default(),
1306 )
1307 .await;
1308 close_dir_checked(d).await;
1309
1310 let d = open_dir_checked(
1311 &root,
1312 "foo/bar",
1313 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
1314 Default::default(),
1315 )
1316 .await;
1317 close_dir_checked(d).await;
1318
1319 let d = open_dir_checked(
1320 &root,
1321 "foo/bar",
1322 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
1323 Default::default(),
1324 )
1325 .await;
1326 close_dir_checked(d).await;
1327
1328 fixture.close().await;
1329 }
1330
1331 #[fuchsia::test]
1332 async fn test_strict_create_file_fails_if_present() {
1333 let fixture = TestFixture::new().await;
1334 let root = fixture.root();
1335
1336 let f = open_file_checked(
1337 &root,
1338 "foo",
1339 fio::Flags::FLAG_MAYBE_CREATE
1340 | fio::Flags::FLAG_MUST_CREATE
1341 | fio::PERM_READABLE
1342 | fio::Flags::PROTOCOL_FILE,
1343 &Default::default(),
1344 )
1345 .await;
1346 close_file_checked(f).await;
1347
1348 assert_eq!(
1349 open_file(
1350 &root,
1351 "foo",
1352 fio::Flags::FLAG_MAYBE_CREATE
1353 | fio::Flags::FLAG_MUST_CREATE
1354 | fio::PERM_READABLE
1355 | fio::Flags::PROTOCOL_FILE,
1356 &Default::default()
1357 )
1358 .await
1359 .expect_err("Open succeeded")
1360 .root_cause()
1361 .downcast_ref::<zx::Status>()
1362 .expect("No status"),
1363 &zx::Status::ALREADY_EXISTS,
1364 );
1365
1366 fixture.close().await;
1367 }
1368
1369 #[fuchsia::test]
1370 async fn test_unlink_file_with_no_refs_immediately_freed() {
1371 let fixture = TestFixture::new().await;
1372 let root = fixture.root();
1373
1374 let file = open_file_checked(
1375 &root,
1376 "foo",
1377 fio::Flags::FLAG_MAYBE_CREATE
1378 | fio::PERM_READABLE
1379 | fio::PERM_WRITABLE
1380 | fio::Flags::PROTOCOL_FILE,
1381 &Default::default(),
1382 )
1383 .await;
1384
1385 let buf = vec![0xaa as u8; 512];
1387 loop {
1388 match file::write(&file, buf.as_slice()).await {
1389 Ok(_) => {}
1390 Err(e) => {
1391 if let fuchsia_fs::file::WriteError::WriteError(status) = e {
1392 if status == zx::Status::NO_SPACE {
1393 break;
1394 }
1395 }
1396 panic!("Unexpected write error {:?}", e);
1397 }
1398 }
1399 }
1400
1401 close_file_checked(file).await;
1402
1403 root.unlink("foo", &fio::UnlinkOptions::default())
1404 .await
1405 .expect("FIDL call failed")
1406 .expect("unlink failed");
1407
1408 assert_eq!(
1409 open_file(
1410 &root,
1411 "foo",
1412 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1413 &Default::default()
1414 )
1415 .await
1416 .expect_err("Open succeeded")
1417 .root_cause()
1418 .downcast_ref::<zx::Status>()
1419 .expect("No status"),
1420 &zx::Status::NOT_FOUND,
1421 );
1422
1423 let file = open_file_checked(
1425 &root,
1426 "bar",
1427 fio::Flags::FLAG_MAYBE_CREATE
1428 | fio::PERM_READABLE
1429 | fio::PERM_WRITABLE
1430 | fio::Flags::PROTOCOL_FILE,
1431 &Default::default(),
1432 )
1433 .await;
1434 let buf = vec![0xaa as u8; 8192];
1435 file::write(&file, buf.as_slice()).await.expect("Failed to write new file");
1436 close_file_checked(file).await;
1437
1438 fixture.close().await;
1439 }
1440
1441 #[fuchsia::test]
1442 async fn test_unlink_file() {
1443 let fixture = TestFixture::new().await;
1444 let root = fixture.root();
1445
1446 let file = open_file_checked(
1447 &root,
1448 "foo",
1449 fio::Flags::FLAG_MAYBE_CREATE
1450 | fio::PERM_READABLE
1451 | fio::PERM_WRITABLE
1452 | fio::Flags::PROTOCOL_FILE,
1453 &Default::default(),
1454 )
1455 .await;
1456 close_file_checked(file).await;
1457
1458 root.unlink("foo", &fio::UnlinkOptions::default())
1459 .await
1460 .expect("FIDL call failed")
1461 .expect("unlink failed");
1462
1463 assert_eq!(
1464 open_file(
1465 &root,
1466 "foo",
1467 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1468 &Default::default()
1469 )
1470 .await
1471 .expect_err("Open succeeded")
1472 .root_cause()
1473 .downcast_ref::<zx::Status>()
1474 .expect("No status"),
1475 &zx::Status::NOT_FOUND,
1476 );
1477
1478 fixture.close().await;
1479 }
1480
1481 #[fuchsia::test]
1482 async fn test_unlink_file_with_active_references() {
1483 let fixture = TestFixture::new().await;
1484 let root = fixture.root();
1485
1486 let file = open_file_checked(
1487 &root,
1488 "foo",
1489 fio::Flags::FLAG_MAYBE_CREATE
1490 | fio::PERM_READABLE
1491 | fio::PERM_WRITABLE
1492 | fio::Flags::PROTOCOL_FILE,
1493 &Default::default(),
1494 )
1495 .await;
1496
1497 let buf = vec![0xaa as u8; 512];
1498 file::write(&file, buf.as_slice()).await.expect("write failed");
1499
1500 root.unlink("foo", &fio::UnlinkOptions::default())
1501 .await
1502 .expect("FIDL call failed")
1503 .expect("unlink failed");
1504
1505 assert_eq!(
1507 open_file(
1508 &root,
1509 "foo",
1510 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1511 &Default::default()
1512 )
1513 .await
1514 .expect_err("Open succeeded")
1515 .root_cause()
1516 .downcast_ref::<zx::Status>()
1517 .expect("No status"),
1518 &zx::Status::NOT_FOUND,
1519 );
1520
1521 file.seek(fio::SeekOrigin::Start, 0)
1523 .await
1524 .expect("seek failed")
1525 .map_err(zx::Status::from_raw)
1526 .expect("seek error");
1527 let rbuf = file::read(&file).await.expect("read failed");
1528 assert_eq!(rbuf, buf);
1529 close_file_checked(file).await;
1530
1531 fixture.close().await;
1532 }
1533
1534 #[fuchsia::test]
1535 async fn test_unlink_dir_with_children_fails() {
1536 let fixture = TestFixture::new().await;
1537 let root = fixture.root();
1538
1539 let dir = open_dir_checked(
1540 &root,
1541 "foo",
1542 fio::Flags::FLAG_MAYBE_CREATE
1543 | fio::PERM_READABLE
1544 | fio::PERM_WRITABLE
1545 | fio::Flags::PROTOCOL_DIRECTORY,
1546 Default::default(),
1547 )
1548 .await;
1549 let f = open_file_checked(
1550 &dir,
1551 "bar",
1552 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE,
1553 &Default::default(),
1554 )
1555 .await;
1556 close_file_checked(f).await;
1557
1558 assert_eq!(
1559 zx::Status::from_raw(
1560 root.unlink("foo", &fio::UnlinkOptions::default())
1561 .await
1562 .expect("FIDL call failed")
1563 .expect_err("unlink succeeded")
1564 ),
1565 zx::Status::NOT_EMPTY
1566 );
1567
1568 dir.unlink("bar", &fio::UnlinkOptions::default())
1569 .await
1570 .expect("FIDL call failed")
1571 .expect("unlink failed");
1572 root.unlink("foo", &fio::UnlinkOptions::default())
1573 .await
1574 .expect("FIDL call failed")
1575 .expect("unlink failed");
1576
1577 close_dir_checked(dir).await;
1578
1579 fixture.close().await;
1580 }
1581
1582 #[fuchsia::test]
1583 async fn test_unlink_dir_makes_directory_immutable() {
1584 let fixture = TestFixture::new().await;
1585 let root = fixture.root();
1586
1587 let dir = open_dir_checked(
1588 &root,
1589 "foo",
1590 fio::Flags::FLAG_MAYBE_CREATE
1591 | fio::PERM_READABLE
1592 | fio::PERM_WRITABLE
1593 | fio::Flags::PROTOCOL_DIRECTORY,
1594 Default::default(),
1595 )
1596 .await;
1597
1598 root.unlink("foo", &fio::UnlinkOptions::default())
1599 .await
1600 .expect("FIDL call failed")
1601 .expect("unlink failed");
1602
1603 assert_eq!(
1604 open_file(
1605 &dir,
1606 "bar",
1607 fio::PERM_READABLE | fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1608 &Default::default()
1609 )
1610 .await
1611 .expect_err("Create file succeeded")
1612 .root_cause()
1613 .downcast_ref::<zx::Status>()
1614 .expect("No status"),
1615 &zx::Status::ACCESS_DENIED,
1616 );
1617
1618 close_dir_checked(dir).await;
1619
1620 fixture.close().await;
1621 }
1622
1623 #[fuchsia::test(threads = 10)]
1624 async fn test_unlink_directory_with_children_race() {
1625 let fixture = TestFixture::new().await;
1626 let root = fixture.root();
1627
1628 const PARENT: &str = "foo";
1629 const CHILD: &str = "bar";
1630 const GRANDCHILD: &str = "baz";
1631 open_dir_checked(
1632 &root,
1633 PARENT,
1634 fio::Flags::FLAG_MAYBE_CREATE
1635 | fio::PERM_READABLE
1636 | fio::PERM_WRITABLE
1637 | fio::Flags::PROTOCOL_DIRECTORY,
1638 Default::default(),
1639 )
1640 .await;
1641
1642 let open_parent = || async {
1643 open_dir_checked(
1644 &root,
1645 PARENT,
1646 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1647 Default::default(),
1648 )
1649 .await
1650 };
1651 let parent = open_parent().await;
1652
1653 for _ in 0..100 {
1659 let d = open_dir_checked(
1660 &parent,
1661 CHILD,
1662 fio::Flags::FLAG_MAYBE_CREATE
1663 | fio::PERM_READABLE
1664 | fio::PERM_WRITABLE
1665 | fio::Flags::PROTOCOL_DIRECTORY,
1666 Default::default(),
1667 )
1668 .await;
1669 close_dir_checked(d).await;
1670
1671 let parent = open_parent().await;
1672 let deleter = fasync::Task::spawn(async move {
1673 let wait_time = rand::random_range(0..5);
1674 fasync::Timer::new(Duration::from_millis(wait_time)).await;
1675 match parent
1676 .unlink(CHILD, &fio::UnlinkOptions::default())
1677 .await
1678 .expect("FIDL call failed")
1679 .map_err(zx::Status::from_raw)
1680 {
1681 Ok(()) => {}
1682 Err(zx::Status::NOT_EMPTY) => {}
1683 Err(e) => panic!("Unexpected status from unlink: {:?}", e),
1684 };
1685 close_dir_checked(parent).await;
1686 });
1687
1688 let parent = open_parent().await;
1689 let writer = fasync::Task::spawn(async move {
1690 let child_or = open_dir(
1691 &parent,
1692 CHILD,
1693 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1694 &Default::default(),
1695 )
1696 .await;
1697 if let Err(e) = &child_or {
1698 assert_eq!(
1700 e.root_cause().downcast_ref::<zx::Status>().expect("No status"),
1701 &zx::Status::NOT_FOUND
1702 );
1703 close_dir_checked(parent).await;
1704 return;
1705 }
1706 let child = child_or.unwrap();
1707 let _: Vec<_> = child.query().await.expect("query failed");
1708 match open_file(
1709 &child,
1710 GRANDCHILD,
1711 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1712 &Default::default(),
1713 )
1714 .await
1715 {
1716 Ok(grandchild) => {
1717 let _: Vec<_> = grandchild.query().await.expect("query failed");
1718 close_file_checked(grandchild).await;
1719 child
1722 .unlink(GRANDCHILD, &fio::UnlinkOptions::default())
1723 .await
1724 .expect("FIDL call failed")
1725 .expect("unlink failed");
1726 }
1727 Err(e) => {
1728 assert_eq!(
1731 e.root_cause().downcast_ref::<zx::Status>().expect("No status"),
1732 &zx::Status::ACCESS_DENIED,
1733 );
1734 }
1735 };
1736 close_dir_checked(child).await;
1737 close_dir_checked(parent).await;
1738 });
1739 writer.await;
1740 deleter.await;
1741 }
1742
1743 close_dir_checked(parent).await;
1744 fixture.close().await;
1745 }
1746
1747 #[fuchsia::test]
1748 async fn test_readdir() {
1749 let fixture = TestFixture::new().await;
1750 let root = fixture.root();
1751
1752 let open_dir = || {
1753 open_dir_checked(
1754 &root,
1755 "foo",
1756 fio::Flags::FLAG_MAYBE_CREATE
1757 | fio::PERM_READABLE
1758 | fio::PERM_WRITABLE
1759 | fio::Flags::PROTOCOL_DIRECTORY,
1760 Default::default(),
1761 )
1762 };
1763 let parent = Arc::new(open_dir().await);
1764
1765 let files = ["eenie", "meenie", "minie", "moe"];
1766 for file in &files {
1767 let file = open_file_checked(
1768 parent.as_ref(),
1769 file,
1770 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1771 &Default::default(),
1772 )
1773 .await;
1774 close_file_checked(file).await;
1775 }
1776 let dirs = ["fee", "fi", "fo", "fum"];
1777 for dir in &dirs {
1778 let dir = open_dir_checked(
1779 parent.as_ref(),
1780 dir,
1781 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
1782 Default::default(),
1783 )
1784 .await;
1785 close_dir_checked(dir).await;
1786 }
1787 {
1788 parent
1789 .create_symlink("symlink", b"target", None)
1790 .await
1791 .expect("FIDL call failed")
1792 .expect("create_symlink failed");
1793 }
1794
1795 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
1796 let status = dir.rewind().await.expect("FIDL call failed");
1797 zx::Status::ok(status).expect("rewind failed");
1798 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
1799 zx::Status::ok(status).expect("read_dirents failed");
1800 let mut entries = vec![];
1801 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
1802 entries.push(res.expect("Failed to parse entry"));
1803 }
1804 entries
1805 };
1806
1807 let mut expected_entries =
1808 vec![DirEntry { name: ".".to_owned(), kind: DirentKind::Directory }];
1809 expected_entries.extend(
1810 files.iter().map(|&name| DirEntry { name: name.to_owned(), kind: DirentKind::File }),
1811 );
1812 expected_entries.extend(
1813 dirs.iter()
1814 .map(|&name| DirEntry { name: name.to_owned(), kind: DirentKind::Directory }),
1815 );
1816 expected_entries.push(DirEntry { name: "symlink".to_owned(), kind: DirentKind::Symlink });
1817 expected_entries.sort_unstable();
1818 assert_eq!(expected_entries, readdir(Arc::clone(&parent)).await);
1819
1820 parent
1822 .unlink(&expected_entries.pop().unwrap().name, &fio::UnlinkOptions::default())
1823 .await
1824 .expect("FIDL call failed")
1825 .expect("unlink failed");
1826
1827 assert_eq!(expected_entries, readdir(Arc::clone(&parent)).await);
1828
1829 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
1830 fixture.close().await;
1831 }
1832
1833 #[fuchsia::test]
1834 async fn test_readdir_multiple_calls() {
1835 let fixture = TestFixture::new().await;
1836 let root = fixture.root();
1837
1838 let parent = open_dir_checked(
1839 &root,
1840 "foo",
1841 fio::Flags::FLAG_MAYBE_CREATE
1842 | fio::PERM_READABLE
1843 | fio::PERM_WRITABLE
1844 | fio::Flags::PROTOCOL_DIRECTORY,
1845 Default::default(),
1846 )
1847 .await;
1848
1849 let files = ["a", "b"];
1850 for file in &files {
1851 let file = open_file_checked(
1852 &parent,
1853 file,
1854 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1855 &Default::default(),
1856 )
1857 .await;
1858 close_file_checked(file).await;
1859 }
1860
1861 const DIRENT_SIZE: u64 = 10; const BUFFER_SIZE: u64 = DIRENT_SIZE + 2; let parse_entries = |buf| {
1866 let mut entries = vec![];
1867 for res in fuchsia_fs::directory::parse_dir_entries(buf) {
1868 entries.push(res.expect("Failed to parse entry"));
1869 }
1870 entries
1871 };
1872
1873 let expected_entries = vec![
1874 DirEntry { name: ".".to_owned(), kind: DirentKind::Directory },
1875 DirEntry { name: "a".to_owned(), kind: DirentKind::File },
1876 ];
1877 let (status, buf) = parent.read_dirents(2 * BUFFER_SIZE).await.expect("FIDL call failed");
1878 zx::Status::ok(status).expect("read_dirents failed");
1879 assert_eq!(expected_entries, parse_entries(&buf));
1880
1881 let expected_entries = vec![DirEntry { name: "b".to_owned(), kind: DirentKind::File }];
1882 let (status, buf) = parent.read_dirents(2 * BUFFER_SIZE).await.expect("FIDL call failed");
1883 zx::Status::ok(status).expect("read_dirents failed");
1884 assert_eq!(expected_entries, parse_entries(&buf));
1885
1886 let expected_entries: Vec<DirEntry> = vec![];
1888 let (status, buf) = parent.read_dirents(2 * BUFFER_SIZE).await.expect("FIDL call failed");
1889 zx::Status::ok(status).expect("read_dirents failed");
1890 assert_eq!(expected_entries, parse_entries(&buf));
1891
1892 close_dir_checked(parent).await;
1893 fixture.close().await;
1894 }
1895
1896 #[fuchsia::test]
1897 async fn test_set_large_extended_attribute_on_encrypted_directory() {
1898 let fixture = TestFixture::new().await;
1899 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
1900 let root = fixture.root();
1901 let open_dir = || {
1902 open_dir_checked(
1903 &root,
1904 "foo",
1905 fio::Flags::FLAG_MAYBE_CREATE
1906 | fio::PERM_READABLE
1907 | fio::PERM_WRITABLE
1908 | fio::Flags::PROTOCOL_DIRECTORY,
1909 Default::default(),
1910 )
1911 };
1912
1913 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
1914 crypt
1915 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
1916 .expect("Failed to add wrapping key");
1917 parent
1918 .update_attributes(&fio::MutableNodeAttributes {
1919 wrapping_key_id: Some(WRAPPING_KEY_ID),
1920 ..Default::default()
1921 })
1922 .await
1923 .expect("FIDL call failed")
1924 .map_err(zx::ok)
1925 .expect("update_attributes failed");
1926 let dir = open_dir_checked(
1927 parent.as_ref(),
1928 "fee",
1929 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1930 Default::default(),
1931 )
1932 .await;
1933
1934 let xattr_name = b"xattr_name";
1935 let value_vec = vec![0x3; 300];
1936
1937 dir.set_extended_attribute(
1938 xattr_name,
1939 fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
1940 fio::SetExtendedAttributeMode::Set,
1941 )
1942 .await
1943 .expect("Failed to make FIDL call")
1944 .expect("Failed to set xattr with create");
1945
1946 let subdir = open_dir_checked(
1947 &dir,
1948 "fo",
1949 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
1950 Default::default(),
1951 )
1952 .await;
1953 close_dir_checked(dir).await;
1954 close_dir_checked(subdir).await;
1955 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
1956 let device = fixture.close().await;
1957 let new_fixture = TestFixture::new_with_device(device).await;
1958 let root = new_fixture.root();
1959 let open_dir = || {
1960 open_dir_checked(
1961 &root,
1962 "foo",
1963 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
1964 Default::default(),
1965 )
1966 };
1967 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
1968
1969 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
1970 let status = dir.rewind().await.expect("FIDL call failed");
1971 zx::Status::ok(status).expect("rewind failed");
1972 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
1973 zx::Status::ok(status).expect("read_dirents failed");
1974 let mut entries = vec![];
1975 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
1976 entries.push(res.expect("Failed to parse entry"));
1977 }
1978 entries
1979 };
1980
1981 let encrypted_entries = readdir(Arc::clone(&parent)).await;
1982 let mut encrypted_name = String::new();
1983 for entry in encrypted_entries {
1984 if entry.name == ".".to_owned() {
1985 continue;
1986 } else {
1987 assert!(entry.name.len() >= FSCRYPT_PADDING);
1988 encrypted_name = entry.name;
1989 assert!(entry.kind == DirentKind::Directory)
1990 }
1991 }
1992
1993 let encrypted_dir = Arc::new(
1994 open_dir_checked(
1995 parent.as_ref(),
1996 &encrypted_name,
1997 fio::Flags::PROTOCOL_DIRECTORY | fio::PERM_READABLE,
1998 Default::default(),
1999 )
2000 .await,
2001 );
2002
2003 assert_eq!(
2004 encrypted_dir
2005 .get_extended_attribute(xattr_name)
2006 .await
2007 .expect("Failed to make FIDL call")
2008 .expect("Failed to get extended attribute"),
2009 fio::ExtendedAttributeValue::Bytes(value_vec)
2010 );
2011
2012 let encrypted_subdir_entries = readdir(Arc::clone(&encrypted_dir)).await;
2013 for entry in encrypted_subdir_entries {
2014 if entry.name == ".".to_owned() {
2015 continue;
2016 } else {
2017 assert!(entry.name.len() >= FSCRYPT_PADDING);
2018 assert!(entry.kind == DirentKind::Directory)
2019 }
2020 }
2021 close_dir_checked(Arc::try_unwrap(encrypted_dir).unwrap()).await;
2022 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2023 new_fixture.close().await;
2024 }
2025
2026 #[fuchsia::test]
2027 async fn test_set_large_extended_attribute_on_encrypted_file() {
2028 let fixture = TestFixture::new().await;
2029 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2030 let root = fixture.root();
2031 let open_dir = || {
2032 open_dir_checked(
2033 &root,
2034 "foo",
2035 fio::Flags::FLAG_MAYBE_CREATE
2036 | fio::PERM_READABLE
2037 | fio::PERM_WRITABLE
2038 | fio::Flags::PROTOCOL_DIRECTORY,
2039 Default::default(),
2040 )
2041 };
2042
2043 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2044 crypt
2045 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2046 .expect("Failed to add wrapping key");
2047 parent
2048 .update_attributes(&fio::MutableNodeAttributes {
2049 wrapping_key_id: Some(WRAPPING_KEY_ID),
2050 ..Default::default()
2051 })
2052 .await
2053 .expect("FIDL call failed")
2054 .map_err(zx::ok)
2055 .expect("update_attributes failed");
2056 let file = open_file_checked(
2057 parent.as_ref(),
2058 "fee",
2059 fio::Flags::FLAG_MAYBE_CREATE
2060 | fio::PERM_READABLE
2061 | fio::PERM_WRITABLE
2062 | fio::Flags::PROTOCOL_FILE,
2063 &Default::default(),
2064 )
2065 .await;
2066
2067 let buf = vec![0xaa as u8; 512];
2068 file::write(&file, buf.as_slice()).await.expect("write failed");
2069
2070 let xattr_name = b"xattr_name";
2071 let value_vec = vec![0x3; 300];
2072
2073 file.set_extended_attribute(
2074 xattr_name,
2075 fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
2076 fio::SetExtendedAttributeMode::Set,
2077 )
2078 .await
2079 .expect("Failed to make FIDL call")
2080 .expect("Failed to set xattr with create");
2081
2082 close_file_checked(file).await;
2083 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2084 let device = fixture.close().await;
2085 let new_fixture = TestFixture::new_with_device(device).await;
2086 let crypt: Arc<CryptBase> = new_fixture.crypt().unwrap();
2087 let root = new_fixture.root();
2088 let open_dir = || {
2089 open_dir_checked(
2090 &root,
2091 "foo",
2092 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2093 Default::default(),
2094 )
2095 };
2096 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2097
2098 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2099 let status = dir.rewind().await.expect("FIDL call failed");
2100 zx::Status::ok(status).expect("rewind failed");
2101 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2102 zx::Status::ok(status).expect("read_dirents failed");
2103 let mut entries = vec![];
2104 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2105 entries.push(res.expect("Failed to parse entry"));
2106 }
2107 entries
2108 };
2109
2110 let encrypted_entries = readdir(Arc::clone(&parent)).await;
2111 let mut encrypted_name = String::new();
2112 for entry in encrypted_entries {
2113 if entry.name == ".".to_owned() {
2114 continue;
2115 } else {
2116 assert!(entry.name.len() >= FSCRYPT_PADDING);
2117 encrypted_name = entry.name;
2118 assert!(entry.kind == DirentKind::File)
2119 }
2120 }
2121
2122 let encrypted_file = Arc::new(
2123 open_file_checked(
2124 parent.as_ref(),
2125 &encrypted_name,
2126 fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE,
2127 &Default::default(),
2128 )
2129 .await,
2130 );
2131
2132 assert_eq!(
2133 encrypted_file
2134 .get_extended_attribute(xattr_name)
2135 .await
2136 .expect("Failed to make FIDL call")
2137 .expect("Failed to get extended attribute"),
2138 fio::ExtendedAttributeValue::Bytes(value_vec)
2139 );
2140
2141 close_file_checked(Arc::try_unwrap(encrypted_file).unwrap()).await;
2142
2143 crypt
2144 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2145 .expect("Failed to add wrapping key");
2146
2147 let file = Arc::new(
2148 open_file_checked(
2149 parent.as_ref(),
2150 "fee",
2151 fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE,
2152 &Default::default(),
2153 )
2154 .await,
2155 );
2156
2157 let rbuf = file::read(&file).await.expect("read failed");
2158 assert_eq!(rbuf, buf);
2159
2160 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2161 new_fixture.close().await;
2162 }
2163
2164 #[fuchsia::test]
2165 async fn test_encrypt_directory_in_unencrypted_volume() {
2166 let fixture = TestFixture::new_unencrypted().await;
2167 let root = fixture.root();
2168 let open_dir = || {
2169 open_dir_checked(
2170 &root,
2171 "foo",
2172 fio::Flags::FLAG_MAYBE_CREATE
2173 | fio::PERM_READABLE
2174 | fio::PERM_WRITABLE
2175 | fio::Flags::PROTOCOL_DIRECTORY,
2176 Default::default(),
2177 )
2178 };
2179
2180 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2181 let _ = parent
2182 .update_attributes(&fio::MutableNodeAttributes {
2183 wrapping_key_id: Some(WRAPPING_KEY_ID),
2184 ..Default::default()
2185 })
2186 .await
2187 .expect("FIDL call failed")
2188 .map_err(zx::ok)
2189 .expect_err("encrypting a dir in an unencrypted volume should fail");
2190 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2191 fixture.close().await;
2192 }
2193
2194 #[fuchsia::test]
2195 async fn test_encrypt_directory_with_large_extended_attribute() {
2196 let fixture = TestFixture::new().await;
2197 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2198 let root = fixture.root();
2199 let open_dir = || {
2200 open_dir_checked(
2201 &root,
2202 "foo",
2203 fio::Flags::FLAG_MAYBE_CREATE
2204 | fio::PERM_READABLE
2205 | fio::PERM_WRITABLE
2206 | fio::Flags::PROTOCOL_DIRECTORY,
2207 Default::default(),
2208 )
2209 };
2210
2211 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2212
2213 let xattr_name = b"xattr_name";
2214 let value_vec = vec![0x3; 300];
2215 parent
2216 .set_extended_attribute(
2217 xattr_name,
2218 fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
2219 fio::SetExtendedAttributeMode::Set,
2220 )
2221 .await
2222 .expect("Failed to make FIDL call")
2223 .expect("Failed to set xattr with create");
2224
2225 crypt
2226 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2227 .expect("Failed to add wrapping key");
2228 parent
2229 .update_attributes(&fio::MutableNodeAttributes {
2230 wrapping_key_id: Some(WRAPPING_KEY_ID),
2231 ..Default::default()
2232 })
2233 .await
2234 .expect("FIDL call failed")
2235 .map_err(zx::ok)
2236 .expect("update_attributes failed");
2237 let dir = open_dir_checked(
2238 parent.as_ref(),
2239 "fee",
2240 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2241 Default::default(),
2242 )
2243 .await;
2244
2245 let subdir = open_dir_checked(
2246 &dir,
2247 "fo",
2248 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
2249 Default::default(),
2250 )
2251 .await;
2252 close_dir_checked(dir).await;
2253 close_dir_checked(subdir).await;
2254 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2255 let device = fixture.close().await;
2256 let new_fixture = TestFixture::new_with_device(device).await;
2257 let root = new_fixture.root();
2258 let open_dir = || {
2259 open_dir_checked(
2260 &root,
2261 "foo",
2262 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2263 Default::default(),
2264 )
2265 };
2266 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2267
2268 assert_eq!(
2269 parent
2270 .get_extended_attribute(xattr_name)
2271 .await
2272 .expect("Failed to make FIDL call")
2273 .expect("Failed to get extended attribute"),
2274 fio::ExtendedAttributeValue::Bytes(value_vec)
2275 );
2276
2277 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2278 let status = dir.rewind().await.expect("FIDL call failed");
2279 zx::Status::ok(status).expect("rewind failed");
2280 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2281 zx::Status::ok(status).expect("read_dirents failed");
2282 let mut entries = vec![];
2283 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2284 entries.push(res.expect("Failed to parse entry"));
2285 }
2286 entries
2287 };
2288
2289 let encrypted_entries = readdir(Arc::clone(&parent)).await;
2290 let mut encrypted_name = None;
2291 for entry in encrypted_entries {
2292 if &entry.name == "." {
2293 continue;
2294 } else {
2295 assert!(entry.name.len() >= FSCRYPT_PADDING);
2296 assert!(encrypted_name.replace(entry.name).is_none());
2297 assert!(entry.kind == DirentKind::Directory)
2298 }
2299 }
2300
2301 let encrypted_dir = Arc::new(
2302 open_dir_checked(
2303 parent.as_ref(),
2304 &encrypted_name.as_ref().unwrap(),
2305 fio::Flags::PROTOCOL_DIRECTORY,
2306 Default::default(),
2307 )
2308 .await,
2309 );
2310
2311 let encrypted_subdir_entries = readdir(Arc::clone(&encrypted_dir)).await;
2312 for entry in encrypted_subdir_entries {
2313 if &entry.name == "." {
2314 continue;
2315 } else {
2316 assert!(entry.name.len() >= FSCRYPT_PADDING);
2317 assert!(entry.kind == DirentKind::Directory)
2318 }
2319 }
2320 close_dir_checked(Arc::try_unwrap(encrypted_dir).unwrap()).await;
2321 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2322 new_fixture.close().await;
2323 }
2324
2325 #[fuchsia::test]
2326 async fn test_unlock_directory_during_readdir() {
2327 let fixture = TestFixture::new().await;
2328 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2329 let root = fixture.root();
2330 let open_dir = || {
2331 open_dir_checked(
2332 &root,
2333 "foo",
2334 fio::Flags::FLAG_MAYBE_CREATE
2335 | fio::PERM_READABLE
2336 | fio::PERM_WRITABLE
2337 | fio::Flags::PROTOCOL_DIRECTORY,
2338 Default::default(),
2339 )
2340 };
2341
2342 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2343 crypt
2344 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2345 .expect("Failed to add wrapping key");
2346 parent
2347 .update_attributes(&fio::MutableNodeAttributes {
2348 wrapping_key_id: Some(WRAPPING_KEY_ID),
2349 ..Default::default()
2350 })
2351 .await
2352 .expect("FIDL call failed")
2353 .map_err(zx::ok)
2354 .expect("update_attributes failed");
2355
2356 for i in 0..300 {
2359 let dir = open_dir_checked(
2360 parent.as_ref(),
2361 &format!("plaintext_{}", i),
2362 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2363 Default::default(),
2364 )
2365 .await;
2366 close_dir_checked(dir).await;
2367 }
2368
2369 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2370 let device = fixture.close().await;
2371 let new_fixture = TestFixture::new_with_device(device).await;
2372 let crypt: Arc<CryptBase> = new_fixture.crypt().unwrap();
2373 let root = new_fixture.root();
2374 let open_dir = || {
2375 open_dir_checked(
2376 &root,
2377 "foo",
2378 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2379 Default::default(),
2380 )
2381 };
2382 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2383
2384 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2385 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2386 zx::Status::ok(status).expect("read_dirents failed");
2387 let mut entries = vec![];
2388 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2389 entries.push(res.expect("Failed to parse entry"));
2390 }
2391 entries
2392 };
2393
2394 let encrypted_entries = readdir(Arc::clone(&parent)).await;
2395 for entry in encrypted_entries {
2396 if entry.name == ".".to_owned() {
2397 continue;
2398 } else {
2399 assert!(entry.name.len() >= FSCRYPT_PADDING);
2400 assert!(!entry.name.starts_with("plaintext_"), "{entry:?} isn't encrypted!");
2401 assert!(entry.kind == DirentKind::Directory)
2402 }
2403 }
2404 crypt
2405 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2406 .expect("Failed to add wrapping key");
2407 let unencrypted_entries = readdir(Arc::clone(&parent)).await;
2408 for entry in unencrypted_entries {
2409 if entry.name == ".".to_owned() {
2410 continue;
2411 } else {
2412 assert!(entry.name.starts_with("plaintext_"), "{entry:?} is still encrypted!");
2413 assert!(entry.kind == DirentKind::Directory)
2414 }
2415 }
2416
2417 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2418 new_fixture.close().await;
2419 }
2420
2421 #[fuchsia::test]
2422 async fn test_readdir_locked_directory() {
2423 let fixture = TestFixture::new().await;
2424 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2425 let root = fixture.root();
2426 let open_dir = || {
2427 open_dir_checked(
2428 &root,
2429 "foo",
2430 fio::Flags::FLAG_MAYBE_CREATE
2431 | fio::PERM_READABLE
2432 | fio::PERM_WRITABLE
2433 | fio::Flags::PROTOCOL_DIRECTORY,
2434 Default::default(),
2435 )
2436 };
2437
2438 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2439 crypt
2440 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2441 .expect("Failed to add wrapping key");
2442 parent
2443 .update_attributes(&fio::MutableNodeAttributes {
2444 wrapping_key_id: Some(WRAPPING_KEY_ID),
2445 ..Default::default()
2446 })
2447 .await
2448 .expect("FIDL call failed")
2449 .map_err(zx::ok)
2450 .expect("update_attributes failed");
2451 let dir = open_dir_checked(
2452 parent.as_ref(),
2453 "fee",
2454 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2455 Default::default(),
2456 )
2457 .await;
2458
2459 let subdir = open_dir_checked(
2460 &dir,
2461 "fo",
2462 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
2463 Default::default(),
2464 )
2465 .await;
2466 close_dir_checked(dir).await;
2467 close_dir_checked(subdir).await;
2468
2469 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2470 let status = dir.rewind().await.expect("FIDL call failed");
2471 zx::Status::ok(status).expect("rewind failed");
2472 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2473 zx::Status::ok(status).expect("read_dirents failed");
2474 let mut entries = vec![];
2475 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2476 entries.push(res.expect("Failed to parse entry"));
2477 }
2478 entries
2479 };
2480
2481 let mut expected_entries =
2482 vec![DirEntry { name: ".".to_owned(), kind: DirentKind::Directory }];
2483
2484 expected_entries.push(DirEntry { name: "fee".to_owned(), kind: DirentKind::Directory });
2485 expected_entries.sort_unstable();
2486 assert_eq!(expected_entries, readdir(Arc::clone(&parent)).await);
2487
2488 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2489 let device = fixture.close().await;
2490 let new_fixture = TestFixture::new_with_device(device).await;
2491 let root = new_fixture.root();
2492 let open_dir = || {
2493 open_dir_checked(
2494 &root,
2495 "foo",
2496 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2497 Default::default(),
2498 )
2499 };
2500 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2501
2502 let encrypted_entries = readdir(Arc::clone(&parent)).await;
2503 let mut encrypted_name = String::new();
2504 for entry in encrypted_entries {
2505 if entry.name == ".".to_owned() {
2506 continue;
2507 } else {
2508 assert!(entry.name.len() >= FSCRYPT_PADDING);
2509 encrypted_name = entry.name;
2510 assert!(entry.kind == DirentKind::Directory)
2511 }
2512 }
2513
2514 let encrypted_dir = Arc::new(
2515 open_dir_checked(
2516 parent.as_ref(),
2517 &encrypted_name,
2518 fio::Flags::PROTOCOL_DIRECTORY,
2519 Default::default(),
2520 )
2521 .await,
2522 );
2523
2524 let encrypted_subdir_entries = readdir(Arc::clone(&encrypted_dir)).await;
2525 for entry in encrypted_subdir_entries {
2526 if entry.name == ".".to_owned() {
2527 continue;
2528 } else {
2529 assert!(entry.name.len() >= FSCRYPT_PADDING);
2530 assert!(entry.kind == DirentKind::Directory)
2531 }
2532 }
2533 close_dir_checked(Arc::try_unwrap(encrypted_dir).unwrap()).await;
2534 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2535 new_fixture.close().await;
2536 }
2537
2538 #[fuchsia::test]
2539 async fn test_link_into_locked_directory_fails() {
2540 let fixture = TestFixture::new().await;
2541 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2542 let root = fixture.root();
2543 let open_dir_1 = || {
2544 open_dir_checked(
2545 &root,
2546 "foo",
2547 fio::Flags::FLAG_MAYBE_CREATE
2548 | fio::PERM_READABLE
2549 | fio::PERM_WRITABLE
2550 | fio::Flags::PROTOCOL_DIRECTORY,
2551 Default::default(),
2552 )
2553 };
2554
2555 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2556
2557 let open_dir_2 = || {
2558 open_dir_checked(
2559 &root,
2560 "foo_2",
2561 fio::Flags::FLAG_MAYBE_CREATE
2562 | fio::PERM_READABLE
2563 | fio::PERM_WRITABLE
2564 | fio::Flags::PROTOCOL_DIRECTORY,
2565 Default::default(),
2566 )
2567 };
2568 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2569
2570 crypt
2571 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2572 .expect("Failed to add wrapping key");
2573 parent_1
2574 .update_attributes(&fio::MutableNodeAttributes {
2575 wrapping_key_id: Some(WRAPPING_KEY_ID),
2576 ..Default::default()
2577 })
2578 .await
2579 .expect("FIDL call failed")
2580 .map_err(zx::ok)
2581 .expect("update_attributes failed");
2582 parent_2
2583 .update_attributes(&fio::MutableNodeAttributes {
2584 wrapping_key_id: Some(WRAPPING_KEY_ID),
2585 ..Default::default()
2586 })
2587 .await
2588 .expect("FIDL call failed")
2589 .map_err(zx::ok)
2590 .expect("update_attributes failed");
2591 let file = open_file_checked(
2592 parent_1.as_ref(),
2593 "fee",
2594 fio::Flags::FLAG_MAYBE_CREATE,
2595 &Default::default(),
2596 )
2597 .await;
2598
2599 close_file_checked(file).await;
2600 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2601 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2602
2603 let device = fixture.close().await;
2604 let new_fixture = TestFixture::new_with_device(device).await;
2605 let root = new_fixture.root();
2606 let open_dir_1 = || {
2607 open_dir_checked(
2608 &root,
2609 "foo",
2610 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2611 Default::default(),
2612 )
2613 };
2614 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2615
2616 let open_dir_2 = || {
2617 open_dir_checked(
2618 &root,
2619 "foo_2",
2620 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2621 Default::default(),
2622 )
2623 };
2624 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2625
2626 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2627 let status = dir.rewind().await.expect("FIDL call failed");
2628 zx::Status::ok(status).expect("rewind failed");
2629 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2630 zx::Status::ok(status).expect("read_dirents failed");
2631 let mut entries = vec![];
2632 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2633 entries.push(res.expect("Failed to parse entry"));
2634 }
2635 entries
2636 };
2637
2638 let encrypted_entries = readdir(Arc::clone(&parent_1)).await;
2639 let mut encrypted_name = String::new();
2640 for entry in encrypted_entries {
2641 if entry.name == ".".to_owned() {
2642 continue;
2643 } else {
2644 assert!(entry.name.len() >= FSCRYPT_PADDING);
2645 encrypted_name = entry.name;
2646 assert!(entry.kind == DirentKind::File)
2647 }
2648 }
2649
2650 let (status, parent_2_token) = parent_2.get_token().await.expect("get token failed");
2651 zx::Status::ok(status).unwrap();
2652
2653 assert_eq!(
2654 parent_1
2655 .link(&encrypted_name, parent_2_token.unwrap().into(), "file_2")
2656 .await
2657 .expect("FIDL transport error"),
2658 zx::Status::ACCESS_DENIED.into_raw()
2659 );
2660
2661 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2662 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2663 new_fixture.close().await;
2664 }
2665
2666 #[fuchsia::test]
2667 async fn test_rename_in_locked_directory_fails() {
2668 let fixture = TestFixture::new().await;
2669 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2670 let root = fixture.root();
2671 let open_dir_1 = || {
2672 open_dir_checked(
2673 &root,
2674 "foo",
2675 fio::Flags::FLAG_MAYBE_CREATE
2676 | fio::PERM_READABLE
2677 | fio::PERM_WRITABLE
2678 | fio::Flags::PROTOCOL_DIRECTORY,
2679 Default::default(),
2680 )
2681 };
2682
2683 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2684
2685 let open_dir_2 = || {
2686 open_dir_checked(
2687 &root,
2688 "foo_2",
2689 fio::Flags::FLAG_MAYBE_CREATE
2690 | fio::PERM_READABLE
2691 | fio::PERM_WRITABLE
2692 | fio::Flags::PROTOCOL_DIRECTORY,
2693 Default::default(),
2694 )
2695 };
2696 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2697
2698 crypt
2699 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2700 .expect("Failed to add wrapping key");
2701 parent_1
2702 .update_attributes(&fio::MutableNodeAttributes {
2703 wrapping_key_id: Some(WRAPPING_KEY_ID),
2704 ..Default::default()
2705 })
2706 .await
2707 .expect("FIDL call failed")
2708 .map_err(zx::ok)
2709 .expect("update_attributes failed");
2710 parent_2
2711 .update_attributes(&fio::MutableNodeAttributes {
2712 wrapping_key_id: Some(WRAPPING_KEY_ID),
2713 ..Default::default()
2714 })
2715 .await
2716 .expect("FIDL call failed")
2717 .map_err(zx::ok)
2718 .expect("update_attributes failed");
2719 let file = open_file_checked(
2720 parent_1.as_ref(),
2721 "fee",
2722 fio::Flags::FLAG_MAYBE_CREATE,
2723 &Default::default(),
2724 )
2725 .await;
2726
2727 close_file_checked(file).await;
2728 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2729 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2730
2731 let device = fixture.close().await;
2732 let new_fixture = TestFixture::new_with_device(device).await;
2733 let root = new_fixture.root();
2734 let open_dir_1 = || {
2735 open_dir_checked(
2736 &root,
2737 "foo",
2738 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2739 Default::default(),
2740 )
2741 };
2742 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2743
2744 let open_dir_2 = || {
2745 open_dir_checked(
2746 &root,
2747 "foo_2",
2748 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2749 Default::default(),
2750 )
2751 };
2752 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2753
2754 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2755 let status = dir.rewind().await.expect("FIDL call failed");
2756 zx::Status::ok(status).expect("rewind failed");
2757 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2758 zx::Status::ok(status).expect("read_dirents failed");
2759 let mut entries = vec![];
2760 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2761 entries.push(res.expect("Failed to parse entry"));
2762 }
2763 entries
2764 };
2765
2766 let encrypted_entries = readdir(Arc::clone(&parent_1)).await;
2767 let mut encrypted_name = String::new();
2768 for entry in encrypted_entries {
2769 if entry.name == ".".to_owned() {
2770 continue;
2771 } else {
2772 assert!(entry.name.len() >= FSCRYPT_PADDING);
2773 encrypted_name = entry.name;
2774 assert!(entry.kind == DirentKind::File)
2775 }
2776 }
2777
2778 let (status, parent_2_token) = parent_2.get_token().await.expect("get token failed");
2779 zx::Status::ok(status).unwrap();
2780
2781 assert_eq!(
2783 parent_1
2784 .rename(&encrypted_name, parent_2_token.unwrap().into(), "file_2")
2785 .await
2786 .expect("FIDL transport error"),
2787 Err(zx::Status::ACCESS_DENIED.into_raw())
2788 );
2789
2790 let (status, parent_1_token) = parent_1.get_token().await.expect("get token failed");
2791 zx::Status::ok(status).unwrap();
2792
2793 assert_eq!(
2795 parent_1
2796 .rename(&encrypted_name, parent_1_token.unwrap().into(), "file_2")
2797 .await
2798 .expect("FIDL transport error"),
2799 Err(zx::Status::ACCESS_DENIED.into_raw())
2800 );
2801
2802 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2803 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2804 new_fixture.close().await;
2805 }
2806
2807 #[fuchsia::test]
2808 async fn test_link_encrypted_file_into_directory_encrypted_with_different_key_fails() {
2809 let fixture = TestFixture::new().await;
2810 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2811 let root = fixture.root();
2812 let open_dir_1 = || {
2813 open_dir_checked(
2814 &root,
2815 "foo",
2816 fio::Flags::FLAG_MAYBE_CREATE
2817 | fio::PERM_READABLE
2818 | fio::PERM_WRITABLE
2819 | fio::Flags::PROTOCOL_DIRECTORY,
2820 Default::default(),
2821 )
2822 };
2823
2824 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2825
2826 let open_dir_2 = || {
2827 open_dir_checked(
2828 &root,
2829 "foo_2",
2830 fio::Flags::FLAG_MAYBE_CREATE
2831 | fio::PERM_READABLE
2832 | fio::PERM_WRITABLE
2833 | fio::Flags::PROTOCOL_DIRECTORY,
2834 Default::default(),
2835 )
2836 };
2837 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2838
2839 crypt
2840 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2841 .expect("Failed to add wrapping key");
2842
2843 const WRAPPING_KEY_ID_2: WrappingKeyId = u128::to_le_bytes(3);
2844 crypt
2845 .add_wrapping_key(WRAPPING_KEY_ID_2, [2; 32].into())
2846 .expect("Failed to add wrapping key");
2847
2848 parent_1
2849 .update_attributes(&fio::MutableNodeAttributes {
2850 wrapping_key_id: Some(WRAPPING_KEY_ID),
2851 ..Default::default()
2852 })
2853 .await
2854 .expect("FIDL call failed")
2855 .map_err(zx::ok)
2856 .expect("update_attributes failed");
2857 parent_2
2858 .update_attributes(&fio::MutableNodeAttributes {
2859 wrapping_key_id: Some(WRAPPING_KEY_ID_2),
2860 ..Default::default()
2861 })
2862 .await
2863 .expect("FIDL call failed")
2864 .map_err(zx::ok)
2865 .expect("update_attributes failed");
2866 let file = open_file_checked(
2867 parent_1.as_ref(),
2868 "fee",
2869 fio::Flags::FLAG_MAYBE_CREATE,
2870 &Default::default(),
2871 )
2872 .await;
2873
2874 close_file_checked(file).await;
2875
2876 let (status, parent_2_token) = parent_2.get_token().await.expect("get token failed");
2877 zx::Status::ok(status).unwrap();
2878
2879 assert_eq!(
2880 parent_1
2881 .link("fee", parent_2_token.unwrap().into(), "file_2")
2882 .await
2883 .expect("FIDL transport error"),
2884 zx::Status::BAD_STATE.into_raw()
2885 );
2886 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2887 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2888 fixture.close().await;
2889 }
2890
2891 #[fuchsia::test]
2892 async fn test_link_unencrypted_file_into_encrypted_directory_fails() {
2893 let fixture = TestFixture::new().await;
2894 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2895 let root = fixture.root();
2896 let open_dir_1 = || {
2897 open_dir_checked(
2898 &root,
2899 "foo",
2900 fio::Flags::FLAG_MAYBE_CREATE
2901 | fio::PERM_READABLE
2902 | fio::PERM_WRITABLE
2903 | fio::Flags::PROTOCOL_DIRECTORY,
2904 Default::default(),
2905 )
2906 };
2907
2908 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2909
2910 let open_dir_2 = || {
2911 open_dir_checked(
2912 &root,
2913 "foo_2",
2914 fio::Flags::FLAG_MAYBE_CREATE
2915 | fio::PERM_READABLE
2916 | fio::PERM_WRITABLE
2917 | fio::Flags::PROTOCOL_DIRECTORY,
2918 Default::default(),
2919 )
2920 };
2921 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2922
2923 crypt
2924 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2925 .expect("Failed to add wrapping key");
2926
2927 parent_1
2928 .update_attributes(&fio::MutableNodeAttributes {
2929 wrapping_key_id: Some(WRAPPING_KEY_ID),
2930 ..Default::default()
2931 })
2932 .await
2933 .expect("FIDL call failed")
2934 .map_err(zx::ok)
2935 .expect("update_attributes failed");
2936
2937 let file = open_file_checked(
2938 parent_2.as_ref(),
2939 "fee",
2940 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
2941 &Default::default(),
2942 )
2943 .await;
2944
2945 close_file_checked(file).await;
2946
2947 let (status, parent_1_token) = parent_1.get_token().await.expect("get token failed");
2948 zx::Status::ok(status).unwrap();
2949
2950 assert_eq!(
2951 parent_2
2952 .link("fee", parent_1_token.unwrap().into(), "file")
2953 .await
2954 .expect("FIDL transport error"),
2955 zx::Status::BAD_STATE.into_raw()
2956 );
2957 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2958 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2959 fixture.close().await;
2960 }
2961
2962 #[fuchsia::test]
2963 async fn test_link_locked_directory_into_unencrypted_dir() {
2964 let fixture = TestFixture::new().await;
2965 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2966 let root = fixture.root();
2967 let open_dir_1 = || {
2968 open_dir_checked(
2969 &root,
2970 "foo",
2971 fio::Flags::FLAG_MAYBE_CREATE
2972 | fio::PERM_READABLE
2973 | fio::PERM_WRITABLE
2974 | fio::Flags::PROTOCOL_DIRECTORY,
2975 Default::default(),
2976 )
2977 };
2978
2979 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2980
2981 let open_dir_2 = || {
2982 open_dir_checked(
2983 &root,
2984 "foo_2",
2985 fio::Flags::FLAG_MAYBE_CREATE
2986 | fio::PERM_READABLE
2987 | fio::PERM_WRITABLE
2988 | fio::Flags::PROTOCOL_DIRECTORY,
2989 Default::default(),
2990 )
2991 };
2992 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2993
2994 crypt
2995 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2996 .expect("Failed to add wrapping key");
2997 parent_1
2998 .update_attributes(&fio::MutableNodeAttributes {
2999 wrapping_key_id: Some(WRAPPING_KEY_ID),
3000 ..Default::default()
3001 })
3002 .await
3003 .expect("FIDL call failed")
3004 .map_err(zx::ok)
3005 .expect("update_attributes failed");
3006 let file = open_file_checked(
3007 parent_1.as_ref(),
3008 "fee",
3009 fio::Flags::FLAG_MAYBE_CREATE
3010 | fio::PERM_READABLE
3011 | fio::PERM_WRITABLE
3012 | fio::Flags::PROTOCOL_FILE,
3013 &Default::default(),
3014 )
3015 .await;
3016 let _ = file
3017 .write(&[8; 8192])
3018 .await
3019 .expect("FIDL call failed")
3020 .map_err(zx::Status::from_raw)
3021 .expect("write failed");
3022
3023 close_file_checked(file).await;
3024 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
3025 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
3026
3027 let device = fixture.close().await;
3028 let new_fixture = TestFixture::new_with_device(device).await;
3029 let root = new_fixture.root();
3030 let open_dir_1 = || {
3031 open_dir_checked(
3032 &root,
3033 "foo",
3034 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3035 Default::default(),
3036 )
3037 };
3038 let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
3039
3040 let open_dir_2 = || {
3041 open_dir_checked(
3042 &root,
3043 "foo_2",
3044 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3045 Default::default(),
3046 )
3047 };
3048 let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
3049
3050 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
3051 let status = dir.rewind().await.expect("FIDL call failed");
3052 zx::Status::ok(status).expect("rewind failed");
3053 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3054 zx::Status::ok(status).expect("read_dirents failed");
3055 let mut entries = vec![];
3056 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3057 entries.push(res.expect("Failed to parse entry"));
3058 }
3059 entries
3060 };
3061
3062 let encrypted_entries = readdir(Arc::clone(&parent_1)).await;
3063 let mut encrypted_name = String::new();
3064 for entry in encrypted_entries {
3065 if entry.name == ".".to_owned() {
3066 continue;
3067 } else {
3068 assert!(entry.name.len() >= FSCRYPT_PADDING);
3069 encrypted_name = entry.name;
3070 assert!(entry.kind == DirentKind::File)
3071 }
3072 }
3073
3074 let (status, parent_2_token) = parent_2.get_token().await.expect("get token failed");
3075 zx::Status::ok(status).unwrap();
3076
3077 assert_eq!(
3078 parent_1
3079 .link(&encrypted_name, parent_2_token.unwrap().into(), "file_2")
3080 .await
3081 .expect("FIDL transport error"),
3082 zx::Status::OK.into_raw()
3083 );
3084
3085 let file =
3086 open_file_checked(parent_2.as_ref(), "file_2", fio::PERM_READABLE, &Default::default())
3087 .await;
3088 let (mutable_attributes, _immutable_attributes) = file
3089 .get_attributes(
3090 fio::NodeAttributesQuery::CONTENT_SIZE
3091 | fio::NodeAttributesQuery::STORAGE_SIZE
3092 | fio::NodeAttributesQuery::LINK_COUNT
3093 | fio::NodeAttributesQuery::MODIFICATION_TIME
3094 | fio::NodeAttributesQuery::CHANGE_TIME
3095 | fio::NodeAttributesQuery::WRAPPING_KEY_ID,
3096 )
3097 .await
3098 .expect("FIDL call failed")
3099 .map_err(zx::Status::from_raw)
3100 .expect("get_attributes failed");
3101 assert_eq!(mutable_attributes.wrapping_key_id, Some(WRAPPING_KEY_ID));
3102 assert_eq!(
3103 file.read(fio::MAX_BUF)
3104 .await
3105 .expect("FIDL call failed")
3106 .expect_err("reading an encrypted file should fail"),
3107 zx::Status::BAD_STATE.into_raw()
3108 );
3109
3110 close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
3111 close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
3112 new_fixture.close().await;
3113 }
3114
3115 #[fuchsia::test]
3116 async fn test_encrypted_filename_does_not_have_slashes() {
3117 let fixture = TestFixture::new().await;
3118 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3119 let root = fixture.root();
3120 let open_dir = || {
3121 open_dir_checked(
3122 &root,
3123 "foo",
3124 fio::Flags::FLAG_MAYBE_CREATE
3125 | fio::PERM_READABLE
3126 | fio::PERM_WRITABLE
3127 | fio::Flags::PROTOCOL_DIRECTORY,
3128 Default::default(),
3129 )
3130 };
3131
3132 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3133 crypt
3134 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3135 .expect("Failed to add wrapping key");
3136 parent
3137 .update_attributes(&fio::MutableNodeAttributes {
3138 wrapping_key_id: Some(WRAPPING_KEY_ID),
3139 ..Default::default()
3140 })
3141 .await
3142 .expect("FIDL call failed")
3143 .map_err(zx::ok)
3144 .expect("update_attributes failed");
3145 const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
3146 for _ in 0..100 {
3147 let one_char = || CHARSET[rand::random_range(0..CHARSET.len())] as char;
3148 let filename: String = std::iter::repeat_with(one_char).take(100).collect();
3149 let dir = open_dir_checked(
3150 parent.as_ref(),
3151 &filename,
3152 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3153 Default::default(),
3154 )
3155 .await;
3156 close_dir_checked(dir).await;
3157 }
3158
3159 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3160 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
3161 let status = dir.rewind().await.expect("FIDL call failed");
3162 zx::Status::ok(status).expect("rewind failed");
3163 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3164 zx::Status::ok(status).expect("read_dirents failed");
3165 let mut entries = vec![];
3166 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3167 entries.push(res.expect("Failed to parse entry"));
3168 }
3169 entries
3170 };
3171
3172 let device = fixture.close().await;
3173 let new_fixture = TestFixture::new_with_device(device).await;
3174 let root = new_fixture.root();
3175 let open_dir = || {
3176 open_dir_checked(
3177 &root,
3178 "foo",
3179 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
3180 Default::default(),
3181 )
3182 };
3183 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3184
3185 let encrypted_entries = readdir(Arc::clone(&parent)).await;
3186 for entry in encrypted_entries {
3187 if entry.name == ".".to_owned() {
3188 continue;
3189 } else {
3190 assert!(entry.name.len() >= FSCRYPT_PADDING);
3191 assert!(!entry.name.contains("/"));
3192 assert!(entry.kind == DirentKind::Directory)
3193 }
3194 }
3195
3196 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3197 new_fixture.close().await;
3198 }
3199
3200 #[fuchsia::test]
3201 async fn test_stat_locked_file() {
3202 let fixture = TestFixture::new().await;
3203 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3204 let root = fixture.root();
3205 let open_dir = || {
3206 open_dir_checked(
3207 &root,
3208 "foo",
3209 fio::Flags::FLAG_MAYBE_CREATE
3210 | fio::PERM_READABLE
3211 | fio::PERM_WRITABLE
3212 | fio::Flags::PROTOCOL_DIRECTORY,
3213 Default::default(),
3214 )
3215 };
3216 let parent = Arc::new(open_dir().await);
3217 crypt
3218 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3219 .expect("Failed to add wrapping key");
3220 parent
3221 .update_attributes(&fio::MutableNodeAttributes {
3222 wrapping_key_id: Some(WRAPPING_KEY_ID),
3223 ..Default::default()
3224 })
3225 .await
3226 .expect("FIDL call failed")
3227 .map_err(zx::ok)
3228 .expect("update_attributes failed");
3229
3230 let file = open_file_checked(
3231 parent.as_ref(),
3232 "file",
3233 fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
3234 &Default::default(),
3235 )
3236 .await;
3237
3238 close_file_checked(file).await;
3239 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3240
3241 let device = fixture.close().await;
3242 let new_fixture = TestFixture::new_with_device(device).await;
3243 let root = new_fixture.root();
3244 let open_dir = || {
3245 open_dir_checked(
3246 &root,
3247 "foo",
3248 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
3249 Default::default(),
3250 )
3251 };
3252 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3253 let (status, buf) = parent.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3254 zx::Status::ok(status).expect("read_dirents failed");
3255 let mut encrypted_entries = vec![];
3256 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3257 encrypted_entries.push(res.expect("Failed to parse entry"));
3258 }
3259 let mut encrypted_name = String::new();
3260 for entry in encrypted_entries {
3261 if entry.name == ".".to_owned() {
3262 continue;
3263 } else {
3264 assert!(entry.name.len() >= FSCRYPT_PADDING);
3265 encrypted_name = entry.name;
3266 assert!(entry.kind == DirentKind::File)
3267 }
3268 }
3269
3270 let file = open_file_checked(
3271 parent.as_ref(),
3272 &encrypted_name,
3273 fio::Flags::PROTOCOL_FILE | fio::Flags::PERM_GET_ATTRIBUTES,
3274 &Default::default(),
3275 )
3276 .await;
3277 let (_mutable_attributes, _immutable_attributes) = file
3278 .get_attributes(
3279 fio::NodeAttributesQuery::CONTENT_SIZE
3280 | fio::NodeAttributesQuery::STORAGE_SIZE
3281 | fio::NodeAttributesQuery::LINK_COUNT
3282 | fio::NodeAttributesQuery::MODIFICATION_TIME
3283 | fio::NodeAttributesQuery::CHANGE_TIME,
3284 )
3285 .await
3286 .expect("FIDL call failed")
3287 .map_err(zx::Status::from_raw)
3288 .expect("get_attributes failed");
3289 close_file_checked(file).await;
3290 new_fixture.close().await;
3291 }
3292
3293 #[fuchsia::test]
3294 async fn test_unlink_locked_directory() {
3295 let fixture = TestFixture::new().await;
3296 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3297 let root = fixture.root();
3298 let open_dir = || {
3299 open_dir_checked(
3300 &root,
3301 "foo",
3302 fio::Flags::FLAG_MAYBE_CREATE
3303 | fio::PERM_READABLE
3304 | fio::PERM_WRITABLE
3305 | fio::Flags::PROTOCOL_DIRECTORY,
3306 Default::default(),
3307 )
3308 };
3309
3310 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3311 crypt
3312 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3313 .expect("Failed to add wrapping key");
3314 parent
3315 .update_attributes(&fio::MutableNodeAttributes {
3316 wrapping_key_id: Some(WRAPPING_KEY_ID),
3317 ..Default::default()
3318 })
3319 .await
3320 .expect("FIDL call failed")
3321 .map_err(zx::ok)
3322 .expect("update_attributes failed");
3323 let dir = open_dir_checked(
3324 parent.as_ref(),
3325 "fee",
3326 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3327 Default::default(),
3328 )
3329 .await;
3330
3331 close_dir_checked(dir).await;
3332 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3333 let device = fixture.close().await;
3334 let new_fixture = TestFixture::new_with_device(device).await;
3335 let root = new_fixture.root();
3336 let open_dir = || {
3337 open_dir_checked(
3338 &root,
3339 "foo",
3340 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3341 Default::default(),
3342 )
3343 };
3344 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3345
3346 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
3347 let status = dir.rewind().await.expect("FIDL call failed");
3348 zx::Status::ok(status).expect("rewind failed");
3349 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3350 zx::Status::ok(status).expect("read_dirents failed");
3351 let mut entries = vec![];
3352 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3353 entries.push(res.expect("Failed to parse entry"));
3354 }
3355 entries
3356 };
3357
3358 let encrypted_entries = readdir(Arc::clone(&parent)).await;
3359 let mut encrypted_name = String::new();
3360 for entry in encrypted_entries {
3361 if entry.name == ".".to_owned() {
3362 continue;
3363 } else {
3364 assert!(entry.name.len() >= FSCRYPT_PADDING);
3365 encrypted_name = entry.name;
3366 assert!(entry.kind == DirentKind::Directory)
3367 }
3368 }
3369
3370 parent
3371 .unlink(&encrypted_name, &fio::UnlinkOptions::default())
3372 .await
3373 .expect("FIDL call failed")
3374 .expect("unlink failed");
3375
3376 let encrypted_entries = readdir(Arc::clone(&parent)).await;
3377 let mut count = 0;
3378 for entry in encrypted_entries {
3379 if entry.name == ".".to_owned() {
3380 continue;
3381 } else {
3382 assert!(entry.name.len() >= FSCRYPT_PADDING);
3383 assert!(entry.kind == DirentKind::Directory)
3384 }
3385 count += 1;
3386 }
3387 assert_eq!(count, 0);
3388 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3389 new_fixture.close().await;
3390 }
3391
3392 #[fuchsia::test]
3393 async fn test_rename_within_locked_encrypted_directory() {
3394 let fixture = TestFixture::new().await;
3395 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3396 let root = fixture.root();
3397 let open_dir = || {
3398 open_dir_checked(
3399 &root,
3400 "foo",
3401 fio::Flags::FLAG_MAYBE_CREATE
3402 | fio::PERM_READABLE
3403 | fio::PERM_WRITABLE
3404 | fio::Flags::PROTOCOL_DIRECTORY,
3405 Default::default(),
3406 )
3407 };
3408
3409 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3410 crypt
3411 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3412 .expect("Failed to add wrapping key");
3413 parent
3414 .update_attributes(&fio::MutableNodeAttributes {
3415 wrapping_key_id: Some(WRAPPING_KEY_ID),
3416 ..Default::default()
3417 })
3418 .await
3419 .expect("FIDL call failed")
3420 .map_err(zx::ok)
3421 .expect("update_attributes failed");
3422 let dir = open_dir_checked(
3423 parent.as_ref(),
3424 "fee",
3425 fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3426 Default::default(),
3427 )
3428 .await;
3429
3430 close_dir_checked(dir).await;
3431 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3432 let device = fixture.close().await;
3433 let new_fixture = TestFixture::new_with_device(device).await;
3434 let crypt: Arc<CryptBase> = new_fixture.crypt().unwrap();
3435 let root = new_fixture.root();
3436 let open_dir = || {
3437 open_dir_checked(
3438 &root,
3439 "foo",
3440 fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3441 Default::default(),
3442 )
3443 };
3444 let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3445
3446 let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
3447 let status = dir.rewind().await.expect("FIDL call failed");
3448 zx::Status::ok(status).expect("rewind failed");
3449 let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3450 zx::Status::ok(status).expect("read_dirents failed");
3451 let mut entries = vec![];
3452 for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3453 entries.push(res.expect("Failed to parse entry"));
3454 }
3455 entries
3456 };
3457
3458 let encrypted_entries = readdir(Arc::clone(&parent)).await;
3459 let mut encrypted_name = String::new();
3460 for entry in encrypted_entries {
3461 if entry.name == ".".to_owned() {
3462 continue;
3463 } else {
3464 assert!(entry.name.len() >= FSCRYPT_PADDING);
3465 encrypted_name = entry.name;
3466 assert!(entry.kind == DirentKind::Directory)
3467 }
3468 }
3469
3470 let (status, dst_token) = parent.get_token().await.expect("FIDL call failed");
3471 zx::Status::ok(status).expect("get_token failed");
3472 let new_encrypted_name = "aabbcc";
3473 parent
3474 .rename(&encrypted_name, zx::Event::from(dst_token.unwrap()), new_encrypted_name)
3475 .await
3476 .expect("FIDL call failed")
3477 .expect_err("rename should fail on a locked directory");
3478 let (status, dst_token) = parent.get_token().await.expect("FIDL call failed");
3479 zx::Status::ok(status).expect("get_token failed");
3480 crypt
3481 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3482 .expect("Failed to add wrapping key");
3483 parent
3484 .rename("fee", zx::Event::from(dst_token.unwrap()), "new_fee")
3485 .await
3486 .expect("FIDL call failed")
3487 .expect("rename should fail on a locked directory");
3488
3489 let _dir = open_dir_checked(
3490 parent.as_ref(),
3491 "new_fee",
3492 fio::Flags::PROTOCOL_DIRECTORY,
3493 Default::default(),
3494 )
3495 .await;
3496 close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3497 new_fixture.close().await;
3498 }
3499
3500 #[fuchsia::test]
3501 async fn test_link_symlink_into_encrypted_directory() {
3502 let fixture = TestFixture::new().await;
3503 let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3504 let root = fixture.root();
3505 let open_dir = || {
3506 open_dir_checked(
3507 &root,
3508 "foo",
3509 fio::Flags::FLAG_MAYBE_CREATE
3510 | fio::PERM_READABLE
3511 | fio::PERM_WRITABLE
3512 | fio::Flags::PROTOCOL_DIRECTORY,
3513 Default::default(),
3514 )
3515 };
3516 let parent = Arc::new(open_dir().await);
3517 crypt
3518 .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3519 .expect("Failed to add wrapping key");
3520 parent
3521 .update_attributes(&fio::MutableNodeAttributes {
3522 wrapping_key_id: Some(WRAPPING_KEY_ID),
3523 ..Default::default()
3524 })
3525 .await
3526 .expect("FIDL call failed")
3527 .map_err(zx::ok)
3528 .expect("update_attributes failed");
3529
3530 {
3531 parent
3532 .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(&parent, "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
5108 | fio::PERM_WRITABLE
5109 | fio::Flags::PROTOCOL_DIRECTORY
5110 | fio::Flags::PERM_GET_ATTRIBUTES,
5111 fio::Options {
5112 attributes: Some(fio::NodeAttributesQuery::CHANGE_TIME),
5113 ..Default::default()
5114 },
5115 )
5116 .await;
5117
5118 let (mutable_attributes, immutable_attributes) = dir
5119 .get_attributes(
5120 fio::NodeAttributesQuery::CHANGE_TIME
5121 | fio::NodeAttributesQuery::ACCESS_TIME
5122 | fio::NodeAttributesQuery::MODIFICATION_TIME,
5123 )
5124 .await
5125 .expect("update_attributes FIDL call failed")
5126 .map_err(zx::ok)
5127 .expect("get_attributes failed");
5128 let initial_ctime = immutable_attributes.change_time;
5129 let initial_atime = mutable_attributes.access_time;
5130 assert_eq!(initial_atime, initial_ctime);
5132 assert_eq!(initial_atime, mutable_attributes.modification_time);
5133
5134 let (mutable_attributes, immutable_attributes) = dir
5138 .get_attributes(
5139 fio::NodeAttributesQuery::CHANGE_TIME
5140 | fio::NodeAttributesQuery::ACCESS_TIME
5141 | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
5142 )
5143 .await
5144 .expect("update_attributes FIDL call failed")
5145 .map_err(zx::ok)
5146 .expect("get_attributes failed");
5147 assert!(initial_atime < mutable_attributes.access_time);
5149 let updated_atime = mutable_attributes.access_time;
5150 assert_eq!(initial_ctime, immutable_attributes.change_time);
5153
5154 let (mutable_attributes, _) = dir
5155 .get_attributes(
5156 fio::NodeAttributesQuery::ACCESS_TIME
5157 | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
5158 )
5159 .await
5160 .expect("update_attributes FIDL call failed")
5161 .map_err(zx::ok)
5162 .expect("get_attributes failed");
5163 assert_eq!(updated_atime, mutable_attributes.access_time);
5165
5166 (fixture.close().await, mutable_attributes.access_time, initial_ctime)
5167 };
5168
5169 let fixture =
5170 TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
5171 .await;
5172 let root = fixture.root();
5173 let dir = open_dir_checked(
5174 &root,
5175 DIR,
5176 fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
5177 Default::default(),
5178 )
5179 .await;
5180
5181 let (mutable_attributes, immutable_attributes) = dir
5182 .get_attributes(
5183 fio::NodeAttributesQuery::CHANGE_TIME | fio::NodeAttributesQuery::ACCESS_TIME,
5184 )
5185 .await
5186 .expect("update_attributesFIDL call failed")
5187 .map_err(zx::ok)
5188 .expect("get_attributes failed");
5189 assert_eq!(immutable_attributes.change_time, expected_ctime);
5190 assert_eq!(mutable_attributes.access_time, expected_atime);
5191 fixture.close().await;
5192 }
5193
5194 #[fuchsia::test]
5195 async fn test_directory_immediately_tombstoned() {
5196 let fixture = TestFixture::new().await;
5197 let root = fixture.root();
5198
5199 let dir = open_dir_checked(
5200 &root,
5201 "foo",
5202 fio::Flags::FLAG_MAYBE_CREATE
5203 | fio::PERM_WRITABLE
5204 | fio::Flags::PROTOCOL_DIRECTORY
5205 | fio::Flags::PERM_GET_ATTRIBUTES,
5206 fio::Options::default(),
5207 )
5208 .await;
5209
5210 let (_mutable, immutable) = dir
5211 .get_attributes(fio::NodeAttributesQuery::ID)
5212 .await
5213 .expect("transport error on get_attributes")
5214 .expect("get_attributes failed");
5215 let foo_object_id = immutable.id.unwrap();
5216
5217 let dir = open_dir_checked(
5218 &root,
5219 "bar",
5220 fio::Flags::FLAG_MAYBE_CREATE
5221 | fio::PERM_WRITABLE
5222 | fio::Flags::PROTOCOL_DIRECTORY
5223 | fio::Flags::PERM_GET_ATTRIBUTES,
5224 fio::Options::default(),
5225 )
5226 .await;
5227
5228 let (_mutable, immutable) = dir
5229 .get_attributes(fio::NodeAttributesQuery::ID)
5230 .await
5231 .expect("transport error on get_attributes")
5232 .expect("get_attributes failed");
5233 let bar_object_id = immutable.id.unwrap();
5234
5235 let (status, dst_token) = root.get_token().await.expect("FIDL call failed");
5237 zx::Status::ok(status).expect("get_token failed");
5238 root.rename("foo", zx::Event::from(dst_token.unwrap()), "bar")
5239 .await
5240 .expect("FIDL call failed")
5241 .expect("rename failed");
5242
5243 yield_to_executor().await;
5245
5246 let assert_not_found = async |oid| {
5248 let tree = fixture.volume().volume().store().tree();
5249 let layer_set = tree.layer_set();
5250 let mut merger = layer_set.merger();
5251 let mut iter = merger.query(Query::FullScan).await.unwrap();
5252 while let Some(item) = iter.get() {
5253 match item {
5254 ItemRef { value: ObjectValue::None, .. } => {}
5255 ItemRef {
5256 key: ObjectKey { object_id, data: ObjectKeyData::Object }, ..
5257 } => {
5258 assert_ne!(*object_id, oid);
5259 }
5260 _ => {}
5261 }
5262 iter.advance().await.unwrap();
5263 }
5264 };
5265
5266 assert_not_found(bar_object_id).await;
5267
5268 root.unlink("bar", &Default::default())
5270 .await
5271 .expect("FIDL call failed")
5272 .expect("unlink failed");
5273
5274 assert_not_found(foo_object_id).await;
5275
5276 fixture.close().await;
5277 }
5278
5279 #[fuchsia::test]
5280 async fn test_failed_create_unnamed_file_transaction() {
5281 let fail = AtomicU64::new(0);
5282 let (mut hooks, fs_hooks) = fxfs::hooks::Hooks::new();
5283 hooks.set_pre_commit(|_| {
5284 if fail.load(Ordering::Relaxed) > 0 {
5285 fail.fetch_sub(1, Ordering::Relaxed);
5286 bail!("Aborted transaction");
5287 }
5288 Ok(())
5289 });
5290 let fixture = TestFixture::open(
5291 DeviceHolder::new(FakeDevice::new(16384, 512)),
5292 TestFixtureOptions { hooks: Some(fs_hooks), ..Default::default() },
5293 )
5294 .await;
5295 let root = fixture.root();
5296
5297 fail.fetch_add(1, Ordering::Relaxed);
5298
5299 let _dir = open_file(
5300 &root,
5301 ".",
5302 fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY,
5303 &fio::Options::default(),
5304 )
5305 .await
5306 .expect_err("Create unexpectedly succeeded");
5307
5308 fixture.close().await;
5309 }
5310
5311 #[fuchsia::test]
5312 async fn test_update_access_time_on_deleted_directory() {
5313 let fixture = TestFixture::new().await;
5314 let root = fixture.root();
5315
5316 let dir = open_dir_checked(
5317 &root,
5318 "foo",
5319 fio::Flags::FLAG_MAYBE_CREATE
5320 | fio::PERM_READABLE
5321 | fio::PERM_WRITABLE
5322 | fio::Flags::PROTOCOL_DIRECTORY,
5323 fio::Options::default(),
5324 )
5325 .await;
5326
5327 root.unlink("foo", &fio::UnlinkOptions::default())
5328 .await
5329 .expect("FIDL call failed")
5330 .expect("unlink failed");
5331
5332 dir.get_attributes(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE)
5334 .await
5335 .expect("FIDL call failed")
5336 .expect("get_attributes failed");
5337
5338 fixture.close().await;
5339 }
5340
5341 async fn enable_casefold(dir: &fio::DirectoryProxy) {
5342 dir.update_attributes(&fio::MutableNodeAttributes {
5343 casefold: Some(true),
5344 ..Default::default()
5345 })
5346 .await
5347 .expect("update_attributes FIDL call failed")
5348 .map_err(zx::ok)
5349 .expect("update_attributes failed");
5350 }
5351
5352 #[fuchsia::test]
5353 async fn test_casefold_cache_lookup_no_duplicates() {
5354 let fixture = TestFixture::new().await;
5355 let root = fixture.root();
5356
5357 let dir = open_dir_checked(
5358 &root,
5359 "dir",
5360 fio::Flags::FLAG_MAYBE_CREATE
5361 | fio::PERM_READABLE
5362 | fio::PERM_WRITABLE
5363 | fio::Flags::PROTOCOL_DIRECTORY,
5364 Default::default(),
5365 )
5366 .await;
5367 enable_casefold(&dir).await;
5368
5369 let file = open_file_checked(
5370 &dir,
5371 "foo",
5372 fio::Flags::FLAG_MAYBE_CREATE
5373 | fio::PERM_READABLE
5374 | fio::PERM_WRITABLE
5375 | fio::Flags::PROTOCOL_FILE,
5376 &Default::default(),
5377 )
5378 .await;
5379 close_file_checked(file).await;
5380
5381 let cache = fixture.volume().volume().dirent_cache();
5382 cache.clear();
5383
5384 let file = open_file_checked(
5385 &dir,
5386 "foo",
5387 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5388 &Default::default(),
5389 )
5390 .await;
5391 close_file_checked(file).await;
5392
5393 let len_after_first_lookup = cache.len();
5394 assert!(len_after_first_lookup >= 1, "Cache should contain at least the looked up file");
5395
5396 let file = open_file_checked(
5397 &dir,
5398 "FOO",
5399 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5400 &Default::default(),
5401 )
5402 .await;
5403 close_file_checked(file).await;
5404
5405 assert_eq!(
5406 cache.len(),
5407 len_after_first_lookup,
5408 "Cache size increased (duplicate entries found)"
5409 );
5410
5411 let file = open_file_checked(
5412 &dir,
5413 "Foo",
5414 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5415 &Default::default(),
5416 )
5417 .await;
5418 close_file_checked(file).await;
5419
5420 assert_eq!(
5421 cache.len(),
5422 len_after_first_lookup,
5423 "Cache size increased (duplicate entries found)"
5424 );
5425
5426 fixture.close().await;
5427 }
5428
5429 #[fuchsia::test]
5430 async fn test_casefold_cache_rename_invalidation() {
5431 let fixture = TestFixture::new().await;
5432 let root = fixture.root();
5433
5434 let dir = open_dir_checked(
5435 &root,
5436 "dir",
5437 fio::Flags::FLAG_MAYBE_CREATE
5438 | fio::PERM_READABLE
5439 | fio::PERM_WRITABLE
5440 | fio::Flags::PROTOCOL_DIRECTORY,
5441 Default::default(),
5442 )
5443 .await;
5444 enable_casefold(&dir).await;
5445
5446 let file = open_file_checked(
5447 &dir,
5448 "foo",
5449 fio::Flags::FLAG_MAYBE_CREATE
5450 | fio::PERM_READABLE
5451 | fio::PERM_WRITABLE
5452 | fio::Flags::PROTOCOL_FILE,
5453 &Default::default(),
5454 )
5455 .await;
5456 close_file_checked(file).await;
5457
5458 let file = open_file_checked(
5459 &dir,
5460 "foo",
5461 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5462 &Default::default(),
5463 )
5464 .await;
5465 close_file_checked(file).await;
5466
5467 let (status, dst_token) = dir.get_token().await.expect("FIDL call failed");
5468 zx::Status::ok(status).expect("get_token failed");
5469 dir.rename("FOO", zx::Event::from(dst_token.unwrap()), "bar")
5470 .await
5471 .expect("Rename FIDL call failed")
5472 .expect("rename failed");
5473
5474 assert_matches!(
5475 open_file(
5476 &dir,
5477 "foo",
5478 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5479 &Default::default()
5480 )
5481 .await
5482 .expect_err("Open \"foo\" succeeded after rename")
5483 .root_cause()
5484 .downcast_ref::<zx::Status>(),
5485 Some(&zx::Status::NOT_FOUND)
5486 );
5487
5488 let file = open_file_checked(
5489 &dir,
5490 "bar",
5491 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5492 &Default::default(),
5493 )
5494 .await;
5495 close_file_checked(file).await;
5496
5497 fixture.close().await;
5498 }
5499
5500 #[fuchsia::test]
5501 async fn test_casefold_cache_unlink_invalidation() {
5502 let fixture = TestFixture::new().await;
5503 let root = fixture.root();
5504
5505 let dir = open_dir_checked(
5506 &root,
5507 "dir",
5508 fio::Flags::FLAG_MAYBE_CREATE
5509 | fio::PERM_READABLE
5510 | fio::PERM_WRITABLE
5511 | fio::Flags::PROTOCOL_DIRECTORY,
5512 Default::default(),
5513 )
5514 .await;
5515 enable_casefold(&dir).await;
5516
5517 let file = open_file_checked(
5518 &dir,
5519 "foo",
5520 fio::Flags::FLAG_MAYBE_CREATE
5521 | fio::PERM_READABLE
5522 | fio::PERM_WRITABLE
5523 | fio::Flags::PROTOCOL_FILE,
5524 &Default::default(),
5525 )
5526 .await;
5527 close_file_checked(file).await;
5528
5529 let file = open_file_checked(
5530 &dir,
5531 "foo",
5532 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5533 &Default::default(),
5534 )
5535 .await;
5536 close_file_checked(file).await;
5537
5538 dir.unlink("FOO", &fio::UnlinkOptions::default())
5539 .await
5540 .expect("Unlink FIDL call failed")
5541 .expect("Unlink failed");
5542
5543 assert_matches!(
5544 open_file(
5545 &dir,
5546 "foo",
5547 fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5548 &Default::default()
5549 )
5550 .await
5551 .expect_err("Open \"foo\" succeeded after unlink")
5552 .root_cause()
5553 .downcast_ref::<zx::Status>(),
5554 Some(&zx::Status::NOT_FOUND)
5555 );
5556
5557 fixture.close().await;
5558 }
5559
5560 async fn assert_watch_event(
5561 watcher: &mut Watcher,
5562 expected_event: WatchEvent,
5563 expected_name: &str,
5564 ) {
5565 assert_eq!(
5566 watcher.next().await.unwrap().unwrap(),
5567 WatchMessage { event: expected_event, filename: expected_name.into() }
5568 );
5569 }
5570
5571 async fn set_up_casefold_dir_with_files(
5572 fixture: &TestFixture,
5573 dir_name: &str,
5574 files: &[&str],
5575 ) -> (fio::DirectoryProxy, Watcher) {
5576 let root = fixture.root();
5577 let dir = open_dir_checked(
5578 root,
5579 dir_name,
5580 fio::Flags::FLAG_MAYBE_CREATE
5581 | fio::PERM_READABLE
5582 | fio::PERM_WRITABLE
5583 | fio::Flags::PROTOCOL_DIRECTORY,
5584 Default::default(),
5585 )
5586 .await;
5587 enable_casefold(&dir).await;
5588
5589 for &file_name in files {
5590 let file = open_file_checked(
5591 &dir,
5592 file_name,
5593 fio::Flags::FLAG_MAYBE_CREATE
5594 | fio::PERM_READABLE
5595 | fio::PERM_WRITABLE
5596 | fio::Flags::PROTOCOL_FILE,
5597 &Default::default(),
5598 )
5599 .await;
5600 close_file_checked(file).await;
5601 }
5602
5603 let mut watcher = Watcher::new(&dir).await.unwrap();
5604 assert_watch_event(&mut watcher, WatchEvent::EXISTING, ".").await;
5605
5606 let mut existing_files = files
5607 .iter()
5608 .map(|s| std::path::PathBuf::from(*s))
5609 .collect::<std::collections::HashSet<_>>();
5610 while !existing_files.is_empty() {
5611 let msg = watcher.next().await.unwrap().unwrap();
5612 assert_eq!(msg.event, WatchEvent::EXISTING);
5613 assert!(
5614 existing_files.remove(&msg.filename),
5615 "Unexpected existing file: {:?}",
5616 msg.filename
5617 );
5618 }
5619
5620 assert_watch_event(&mut watcher, WatchEvent::IDLE, "").await;
5621
5622 (dir, watcher)
5623 }
5624
5625 #[fuchsia::test]
5626 async fn test_casefold_rename_watcher_events() {
5627 let fixture = TestFixture::new().await;
5628 let (dir, mut watcher) = set_up_casefold_dir_with_files(&fixture, "dir", &["foo"]).await;
5629
5630 let (status, dst_token) = dir.get_token().await.expect("FIDL call failed");
5631 zx::Status::ok(status).expect("get_token failed");
5632 dir.rename("FOO", zx::Event::from(dst_token.unwrap()), "BAR")
5633 .await
5634 .expect("Rename FIDL call failed")
5635 .expect("rename failed");
5636
5637 assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "foo").await;
5638 assert_watch_event(&mut watcher, WatchEvent::ADD_FILE, "BAR").await;
5639
5640 fixture.close().await;
5641 }
5642
5643 #[fuchsia::test]
5644 async fn test_casefold_rename_overwrite_watcher_events() {
5645 let fixture = TestFixture::new().await;
5646 let (dir, mut watcher) =
5647 set_up_casefold_dir_with_files(&fixture, "dir", &["foo", "bar"]).await;
5648
5649 let (status, dst_token) = dir.get_token().await.expect("FIDL call failed");
5650 zx::Status::ok(status).expect("get_token failed");
5651 dir.rename("FOO", zx::Event::from(dst_token.unwrap()), "BAR")
5652 .await
5653 .expect("Rename FIDL call failed")
5654 .expect("rename failed");
5655
5656 assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "foo").await;
5657 assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "bar").await;
5658 assert_watch_event(&mut watcher, WatchEvent::ADD_FILE, "BAR").await;
5659
5660 fixture.close().await;
5661 }
5662
5663 #[fuchsia::test]
5664 async fn test_casefold_rename_cross_dir_overwrite_watcher_events() {
5665 let fixture = TestFixture::new().await;
5666 let (src_dir, mut src_watcher) =
5667 set_up_casefold_dir_with_files(&fixture, "src_dir", &["foo"]).await;
5668 let (dst_dir, mut dst_watcher) =
5669 set_up_casefold_dir_with_files(&fixture, "dst_dir", &["bar"]).await;
5670
5671 let (status, dst_token) = dst_dir.get_token().await.expect("FIDL call failed");
5672 zx::Status::ok(status).expect("get_token failed");
5673 src_dir
5674 .rename("FOO", zx::Event::from(dst_token.unwrap()), "BAR")
5675 .await
5676 .expect("Rename FIDL call failed")
5677 .expect("rename failed");
5678
5679 assert_watch_event(&mut src_watcher, WatchEvent::REMOVE_FILE, "foo").await;
5680
5681 assert_watch_event(&mut dst_watcher, WatchEvent::REMOVE_FILE, "bar").await;
5682 assert_watch_event(&mut dst_watcher, WatchEvent::ADD_FILE, "BAR").await;
5683
5684 fixture.close().await;
5685 }
5686
5687 #[fuchsia::test]
5688 async fn test_casefold_unlink_watcher_events() {
5689 let fixture = TestFixture::new().await;
5690 let (dir, mut watcher) = set_up_casefold_dir_with_files(&fixture, "dir", &["foo"]).await;
5691
5692 dir.unlink("FOO", &fio::UnlinkOptions::default())
5693 .await
5694 .expect("Unlink FIDL call failed")
5695 .expect("Unlink failed");
5696
5697 assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "foo").await;
5698
5699 fixture.close().await;
5700 }
5701
5702 #[fuchsia::test]
5703 async fn test_casefold_rename_case_only_watcher_events() {
5704 let fixture = TestFixture::new().await;
5705 let (dir, mut watcher) = set_up_casefold_dir_with_files(&fixture, "dir", &["Foo"]).await;
5706
5707 let (status, dst_token) = dir.get_token().await.expect("FIDL call failed");
5708 zx::Status::ok(status).expect("get_token failed");
5709 dir.rename("Foo", zx::Event::from(dst_token.unwrap()), "FOO")
5710 .await
5711 .expect("Rename FIDL call failed")
5712 .expect("rename failed");
5713
5714 assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "Foo").await;
5715 assert_watch_event(&mut watcher, WatchEvent::ADD_FILE, "FOO").await;
5716
5717 fixture.close().await;
5718 }
5719}