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