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