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