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