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