Skip to main content

fxfs_platform_testing/fuchsia/
directory.rs

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