Skip to main content

fxfs_platform/fuchsia/
directory.rs

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