Skip to main content

fxfs_platform_testing/fuchsia/
directory.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::fuchsia::device::BlockServer;
6use crate::fuchsia::dirent_cache::DirentCacheKey;
7use crate::fuchsia::errors::map_to_status;
8use crate::fuchsia::file::FxFile;
9use crate::fuchsia::node::{FxNode, GetResult, OpenedNode};
10use crate::fuchsia::symlink::FxSymlink;
11use crate::fuchsia::volume::{FxVolume, RootDir};
12use anyhow::{Error, bail};
13use either::{Left, Right};
14use fidl::endpoints::ServerEnd;
15use fidl_fuchsia_io as fio;
16use fidl_fuchsia_storage_block::BlockMarker;
17use fuchsia_sync::Mutex;
18use futures::future::BoxFuture;
19use fxfs::errors::FxfsError;
20use fxfs::filesystem::SyncOptions;
21use fxfs::log::*;
22use fxfs::object_store::directory::{self, ReplacedChild};
23use fxfs::object_store::transaction::{LockKey, Options, Transaction, lock_keys};
24use fxfs::object_store::{self, Directory, ObjectDescriptor, ObjectStore, Timestamp};
25use fxfs_crypto::WrappingKeyId;
26use fxfs_macros::ToWeakNode;
27use std::any::Any;
28use std::sync::Arc;
29use vfs::directory::dirents_sink::{self, AppendResult};
30use vfs::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
31use vfs::directory::entry_container::{
32    Directory as VfsDirectory, DirectoryWatcher, MutableDirectory,
33};
34use vfs::directory::mutable::connection::MutableConnection;
35use vfs::directory::traversal_position::TraversalPosition;
36use vfs::directory::watchers::Watchers;
37use vfs::directory::watchers::event_producers::SingleNameEventProducer;
38use vfs::execution_scope::ExecutionScope;
39use vfs::path::Path;
40use vfs::{ObjectRequest, ObjectRequestRef, ProtocolsExt, ToObjectRequest, attributes, symlink};
41
42#[derive(ToWeakNode)]
43pub struct FxDirectory {
44    // The root directory is the only directory which has no parent, and its parent can never
45    // change, hence the Option can go on the outside.
46    parent: Option<Mutex<Arc<FxDirectory>>>,
47    directory: object_store::Directory<FxVolume>,
48    watchers: Mutex<Watchers>,
49}
50
51impl RootDir for FxDirectory {
52    fn as_directory_entry(self: Arc<Self>) -> Arc<dyn DirectoryEntry> {
53        self
54    }
55
56    fn serve(self: Arc<Self>, flags: fio::Flags, server_end: ServerEnd<fio::DirectoryMarker>) {
57        let scope = self.volume().scope().clone();
58        vfs::directory::serve_on(self, flags, scope, server_end);
59    }
60
61    fn as_node(self: Arc<Self>) -> Arc<dyn FxNode> {
62        self as Arc<dyn FxNode>
63    }
64}
65
66impl FxDirectory {
67    pub(super) fn new(
68        parent: Option<Arc<FxDirectory>>,
69        directory: object_store::Directory<FxVolume>,
70    ) -> Self {
71        Self {
72            parent: parent.map(|p| Mutex::new(p)),
73            directory,
74            watchers: Mutex::new(Watchers::new()),
75        }
76    }
77
78    pub fn directory(&self) -> &object_store::Directory<FxVolume> {
79        &self.directory
80    }
81
82    pub fn volume(&self) -> &Arc<FxVolume> {
83        self.directory.owner()
84    }
85
86    pub fn store(&self) -> &ObjectStore {
87        self.directory.store()
88    }
89
90    pub fn is_deleted(&self) -> bool {
91        self.directory.is_deleted()
92    }
93
94    pub fn set_deleted(&self) {
95        self.directory.set_deleted();
96        self.watchers.lock().send_event(&mut SingleNameEventProducer::deleted());
97    }
98
99    async fn lookup(
100        self: &Arc<Self>,
101        protocols: &dyn ProtocolsExt,
102        mut path: Path,
103        request: &ObjectRequest,
104    ) -> Result<OpenedNode<dyn FxNode>, Error> {
105        if path.is_empty() {
106            return if protocols.create_unnamed_temporary_in_directory_path() {
107                self.create_unnamed_temporary_file(request.create_attributes()).await
108            } else {
109                Ok(OpenedNode::new(self.clone()))
110            };
111        }
112        let store = self.store();
113        let fs = store.filesystem();
114        let mut current_node = self.clone() as Arc<dyn FxNode>;
115        loop {
116            let last_segment = path.is_single_component();
117            let current_dir =
118                current_node.into_any().downcast::<FxDirectory>().map_err(|_| FxfsError::NotDir)?;
119            let name = path.next().unwrap();
120
121            // Create the transaction here if we might need to create the object so that we have a
122            // lock in place.
123            let keys = lock_keys![LockKey::object(
124                store.store_object_id(),
125                current_dir.directory.object_id()
126            )];
127            let create_object = last_segment
128                && matches!(
129                    protocols.creation_mode(),
130                    vfs::CreationMode::AllowExisting | vfs::CreationMode::Always
131                );
132            let transaction_or_guard = if create_object {
133                Left(store.new_transaction(keys, Options::default()).await?)
134            } else {
135                // When child objects are created, the object is created along with the
136                // directory entry in the same transaction, and so we need to hold a read lock
137                // over the lookup and open calls.
138                Right(fs.lock_manager().read_lock(keys).await)
139            };
140
141            let is_casefold = current_dir.directory.dir_type().is_casefold();
142            let child_descriptor = {
143                match self.directory.owner().dirent_cache().lookup(&(
144                    current_dir.object_id(),
145                    name,
146                    is_casefold,
147                )) {
148                    Some(node) => {
149                        let desc = node.object_descriptor();
150                        Some((node, desc))
151                    }
152                    None => {
153                        if let Some((object_id, object_descriptor, locked)) =
154                            current_dir.directory.lookup(name).await?
155                        {
156                            let child_node = self
157                                .volume()
158                                .get_or_load_node(
159                                    object_id,
160                                    object_descriptor.clone(),
161                                    Some(current_dir.clone()),
162                                )
163                                .await?;
164                            // Do not add a locked encrypted child to the dirent cache. That way, if
165                            // a user opens a locked directory, unlocks the directory, and then
166                            // reopens the directory, fxfs does not return the cached locked node.
167                            if !locked {
168                                self.directory.owner().dirent_cache().insert(
169                                    DirentCacheKey::new(
170                                        current_dir.object_id(),
171                                        name.to_owned(),
172                                        is_casefold,
173                                    ),
174                                    child_node.clone(),
175                                );
176                            }
177                            Some((child_node, object_descriptor))
178                        } else {
179                            None
180                        }
181                    }
182                }
183            };
184
185            match child_descriptor {
186                Some((child_node, object_descriptor)) => {
187                    if transaction_or_guard.is_left()
188                        && protocols.creation_mode() == vfs::CreationMode::Always
189                    {
190                        bail!(FxfsError::AlreadyExists);
191                    }
192                    if last_segment {
193                        if protocols.create_unnamed_temporary_in_directory_path() {
194                            if !matches!(object_descriptor, ObjectDescriptor::Directory) {
195                                bail!(FxfsError::WrongType);
196                            }
197                            let dir = child_node
198                                .into_any()
199                                .downcast::<FxDirectory>()
200                                .map_err(|_| FxfsError::Inconsistent)?;
201                            return dir
202                                .create_unnamed_temporary_file(request.create_attributes())
203                                .await;
204                        }
205
206                        match object_descriptor {
207                            ObjectDescriptor::Directory => {
208                                if !protocols.is_node() && !protocols.is_dir_allowed() {
209                                    if protocols.is_file_allowed() {
210                                        bail!(FxfsError::NotFile)
211                                    } else {
212                                        bail!(FxfsError::WrongType)
213                                    }
214                                }
215                            }
216                            ObjectDescriptor::File => {
217                                if !protocols.is_node() && !protocols.is_file_allowed() {
218                                    if protocols.is_dir_allowed() {
219                                        bail!(FxfsError::NotDir)
220                                    } else {
221                                        bail!(FxfsError::WrongType)
222                                    }
223                                }
224                            }
225                            ObjectDescriptor::Symlink => {
226                                if !protocols.is_node() && !protocols.is_symlink_allowed() {
227                                    bail!(FxfsError::WrongType)
228                                }
229                            }
230                            ObjectDescriptor::Volume => bail!(FxfsError::Inconsistent),
231                        }
232                    }
233                    current_node = child_node;
234                    if last_segment {
235                        // We must make sure to take an open-count whilst we are holding a read
236                        // lock.
237                        return Ok(OpenedNode::new(current_node));
238                    }
239                }
240                None => {
241                    if let Left(mut transaction) = transaction_or_guard {
242                        let new_node = current_dir
243                            .create_child(
244                                &mut transaction,
245                                name,
246                                protocols.create_directory(),
247                                request.create_attributes(),
248                            )
249                            .await?;
250                        if let GetResult::Placeholder(p) =
251                            self.volume().cache().get_or_reserve(new_node.object_id()).await
252                        {
253                            return transaction
254                                .commit_with_callback(|_| {
255                                    p.commit(&new_node);
256                                    current_dir.did_add(name, Some(new_node.clone()));
257                                    // NOTE: We don't take the open count until here in case the
258                                    // transaction fails.
259                                    OpenedNode::new(new_node)
260                                })
261                                .await;
262                        } else {
263                            // We created a node, but the object ID was already used in the cache,
264                            // which suggests a object ID was reused (which would either be a bug or
265                            // corruption).
266                            bail!(FxfsError::Inconsistent);
267                        }
268                    } else {
269                        bail!(FxfsError::NotFound);
270                    }
271                }
272            };
273        }
274    }
275
276    async fn create_child(
277        self: &Arc<Self>,
278        transaction: &mut Transaction<'_>,
279        name: &str,
280        create_dir: bool, // If false, creates a file.
281        create_attributes: Option<&fio::MutableNodeAttributes>,
282    ) -> Result<Arc<dyn FxNode>, Error> {
283        if create_dir {
284            let dir = Arc::new(FxDirectory::new(
285                Some(self.clone()),
286                self.directory.create_child_dir(transaction, name).await?,
287            ));
288            if let Some(attrs) = create_attributes {
289                dir.directory().handle().update_attributes(transaction, Some(&attrs), None).await?;
290            }
291            Ok(dir as Arc<dyn FxNode>)
292        } else {
293            let file = FxFile::new(self.directory.create_child_file(transaction, name).await?);
294            if let Some(attrs) = create_attributes {
295                file.handle()
296                    .uncached_handle()
297                    .update_attributes(transaction, Some(&attrs), None)
298                    .await?;
299            }
300            Ok(file as Arc<dyn FxNode>)
301        }
302    }
303
304    pub(crate) async fn create_unnamed_temporary_file(
305        self: &Arc<Self>,
306        create_attributes: Option<&fio::MutableNodeAttributes>,
307    ) -> Result<OpenedNode<dyn FxNode>, Error> {
308        let store = self.store();
309        let keys = lock_keys![LockKey::object(store.store_object_id(), self.directory.object_id())];
310        let mut transaction = store.new_transaction(keys, Options::default()).await?;
311        let file = FxFile::new(
312            self.directory.create_child_unnamed_temporary_file(&mut transaction).await?,
313        );
314        if let Some(attrs) = create_attributes {
315            file.handle()
316                .uncached_handle()
317                .update_attributes(&mut transaction, Some(&attrs), None)
318                .await?;
319        }
320        let GetResult::Placeholder(p) =
321            self.volume().cache().get_or_reserve(file.object_id()).await
322        else {
323            bail!(FxfsError::Inconsistent);
324        };
325        transaction
326            .commit_with_callback(|_| {
327                let file = file.open_as_temporary();
328                p.commit(&file);
329                file
330            })
331            .await
332    }
333
334    /// Called to indicate a file or directory was removed from this directory.
335    pub(crate) fn did_remove(&self, name: &str) {
336        let is_casefold = self.directory.dir_type().is_casefold();
337        self.directory.owner().dirent_cache().remove(&(
338            self.directory.object_id(),
339            name,
340            is_casefold,
341        ));
342        self.watchers.lock().send_event(&mut SingleNameEventProducer::removed(name));
343    }
344
345    /// Called to indicate a file or directory was added to this directory.
346    pub(crate) fn did_add(&self, name: &str, node: Option<Arc<dyn FxNode>>) {
347        if let Some(node) = node {
348            let is_casefold = self.directory.dir_type().is_casefold();
349            self.directory.owner().dirent_cache().insert(
350                DirentCacheKey::new(self.directory.object_id(), name.to_owned(), is_casefold),
351                node,
352            );
353        }
354        self.watchers.lock().send_event(&mut SingleNameEventProducer::added(name));
355    }
356
357    /// As per fscrypt, a user cannot link an unencrypted file into an encrypted directory nor can
358    /// a user link an encrypted file into a directory encrypted with a different key. Appropriate
359    /// locks must be held by the caller.
360    pub fn check_fscrypt_hard_link_conditions(
361        &self,
362        source_wrapping_key_id: Option<WrappingKeyId>,
363    ) -> Result<(), zx::Status> {
364        if let Some(target_id) = self.directory().dir_type().wrapping_key_id() {
365            if Some(target_id) != source_wrapping_key_id {
366                return Err(zx::Status::BAD_STATE);
367            }
368        }
369        Ok(())
370    }
371
372    pub(crate) async fn link_object(
373        &self,
374        mut transaction: Transaction<'_>,
375        name: &str,
376        source_id: u64,
377        kind: ObjectDescriptor,
378    ) -> Result<(), zx::Status> {
379        let store = self.store();
380        if self.is_deleted() {
381            return Err(zx::Status::ACCESS_DENIED);
382        }
383        if self.directory.lookup(&name).await.map_err(map_to_status)?.is_some() {
384            return Err(zx::Status::ALREADY_EXISTS);
385        }
386        self.directory
387            .insert_child(&mut transaction, &name, source_id, kind.clone())
388            .await
389            .map_err(map_to_status)?;
390        store.adjust_refs(&mut transaction, source_id, 1).await.map_err(map_to_status)?;
391        transaction
392            .commit_with_callback(|_| self.did_add(&name, None))
393            .await
394            .map_err(map_to_status)?;
395        Ok(())
396    }
397
398    // Move graveyard object out from the graveyard and link it to this path. We only expect to do
399    // this when linking an unnamed temporary file for the first time.
400    pub(crate) async fn link_graveyard_object<F>(
401        &self,
402        mut transaction: Transaction<'_>,
403        name: &str,
404        source_id: u64,
405        kind: ObjectDescriptor,
406        transaction_callback: F,
407    ) -> Result<(), zx::Status>
408    where
409        F: FnOnce() + Send,
410    {
411        let store = self.store();
412        if self.is_deleted() {
413            return Err(zx::Status::ACCESS_DENIED);
414        }
415        if self.directory.lookup(&name).await.map_err(map_to_status)?.is_some() {
416            return Err(zx::Status::ALREADY_EXISTS);
417        }
418        // Move object out from the graveyard and place into record. As we are moving the object
419        // from one record to the other, the reference count should stay the same.
420        store.remove_from_graveyard(&mut transaction, source_id);
421        self.directory
422            .insert_child(&mut transaction, &name, source_id, kind.clone())
423            .await
424            .map_err(map_to_status)?;
425        transaction
426            .commit_with_callback(|_| {
427                transaction_callback();
428                self.did_add(&name, None);
429            })
430            .await
431            .map_err(map_to_status)?;
432        Ok(())
433    }
434
435    async fn link_impl(
436        self: Arc<Self>,
437        name: String,
438        source_dir: Arc<dyn Any + Send + Sync>,
439        source_name: &str,
440    ) -> Result<(), zx::Status> {
441        let source_dir = source_dir.downcast::<Self>().unwrap();
442        let store = self.store();
443        let mut source_id =
444            match source_dir.directory.lookup(source_name).await.map_err(map_to_status)? {
445                Some((object_id, ObjectDescriptor::File, _)) => object_id,
446                None => return Err(zx::Status::NOT_FOUND),
447                _ => return Err(zx::Status::NOT_SUPPORTED),
448            };
449        loop {
450            // We don't need a lock on the source directory, as it will be unchanged (unless it is
451            // the same as the destination directory). We just need a lock on the source object to
452            // ensure that it hasn't been simultaneously unlinked. This may race with a rename of
453            // the source file to somewhere else but that shouldn't matter. We need that lock anyway
454            // to update the ref count. Note, fscrypt does not require the source directory to be
455            // locked because a directory's wrapping key cannot change once the directory has
456            // entries.
457            let transaction = store
458                .new_transaction(
459                    lock_keys![
460                        LockKey::object(store.store_object_id(), self.object_id()),
461                        LockKey::object(store.store_object_id(), source_id),
462                    ],
463                    Options::default(),
464                )
465                .await
466                .map_err(map_to_status)?;
467            self.check_fscrypt_hard_link_conditions(source_dir.directory().wrapping_key_id())?;
468            // Ensure under lock that the file still exists there.
469            match source_dir.directory.lookup(source_name).await.map_err(map_to_status)? {
470                Some((new_id, ObjectDescriptor::File, _)) => {
471                    if new_id == source_id {
472                        // We found the object that we got a lock on, it is still valid.
473                        return self
474                            .link_object(transaction, &name, source_id, ObjectDescriptor::File)
475                            .await;
476                    } else {
477                        source_id = new_id
478                    }
479                }
480                None => return Err(zx::Status::NOT_FOUND),
481                _ => return Err(zx::Status::NOT_SUPPORTED),
482            }
483        }
484    }
485
486    async fn rename_impl(
487        self: Arc<Self>,
488        src_dir: Arc<dyn MutableDirectory>,
489        src_name: Path,
490        dst_name: Path,
491    ) -> Result<(), zx::Status> {
492        if !src_name.is_single_component() || !dst_name.is_single_component() {
493            return Err(zx::Status::INVALID_ARGS);
494        }
495        let (src, dst) = (src_name.peek().unwrap(), dst_name.peek().unwrap());
496        let src_dir =
497            src_dir.into_any().downcast::<FxDirectory>().map_err(|_| Err(zx::Status::NOT_DIR))?;
498
499        // Acquire the transaction that locks |src_dir|, |src_name|, |self|, and |dst_name| if they
500        // exist, and also the ID and type of dst and src.
501        let replace_context = self
502            .directory
503            .acquire_context_for_replace(Some((src_dir.directory(), src)), dst, false)
504            .await
505            .map_err(map_to_status)?;
506        let mut transaction = replace_context.transaction;
507
508        if self.is_deleted() {
509            return Err(zx::Status::NOT_FOUND);
510        }
511
512        let (moved_id, moved_descriptor) =
513            replace_context.src_id_and_descriptor.clone().ok_or(zx::Status::NOT_FOUND)?;
514
515        // Make sure the dst path is compatible with the moved node.
516        if let ObjectDescriptor::File = moved_descriptor {
517            if src_name.is_dir() || dst_name.is_dir() {
518                return Err(zx::Status::NOT_DIR);
519            }
520        }
521
522        // Now that we've ensured that the dst path is compatible with the moved node, we can check
523        // for the trivial case.
524        if src_dir.object_id() == self.object_id() && src == dst {
525            return Ok(());
526        }
527
528        if let Some((_, dst_descriptor)) = replace_context.dst_id_and_descriptor.as_ref() {
529            // dst is being overwritten; make sure it's a file iff src is.
530            match (&moved_descriptor, dst_descriptor) {
531                (ObjectDescriptor::Directory, ObjectDescriptor::Directory) => {}
532                (
533                    ObjectDescriptor::File | ObjectDescriptor::Symlink,
534                    ObjectDescriptor::File | ObjectDescriptor::Symlink,
535                ) => {}
536                (ObjectDescriptor::Directory, _) => return Err(zx::Status::NOT_DIR),
537                (ObjectDescriptor::File | ObjectDescriptor::Symlink, _) => {
538                    return Err(zx::Status::NOT_FILE);
539                }
540                _ => return Err(zx::Status::IO_DATA_INTEGRITY),
541            }
542        }
543
544        let moved_node = src_dir
545            .volume()
546            .get_or_load_node(moved_id, moved_descriptor.clone(), Some(src_dir.clone()))
547            .await
548            .map_err(map_to_status)?;
549
550        if let ObjectDescriptor::Directory = moved_descriptor {
551            // Lastly, ensure that self isn't a (transitive) child of the moved node.
552            let mut node_opt = Some(self.clone());
553            while let Some(node) = node_opt {
554                if node.object_id() == moved_node.object_id() {
555                    return Err(zx::Status::INVALID_ARGS);
556                }
557                node_opt = node.parent();
558            }
559        }
560
561        let replace_result = directory::replace_child(
562            &mut transaction,
563            Some((src_dir.directory(), src)),
564            (self.directory(), dst),
565        )
566        .await
567        .map_err(map_to_status)?;
568
569        // Use name from the replace_context if available (which preserves case-folding info).
570        // `src` comes from user supplied name which may have different case.
571        let actual_src_name = replace_context.src_name.as_deref().unwrap_or(src);
572        let actual_dst_name = replace_context.dst_name.as_deref().unwrap_or(dst);
573
574        transaction
575            .commit_with_callback(|_| {
576                moved_node.set_parent(self.clone());
577                src_dir.did_remove(actual_src_name);
578
579                match replace_result {
580                    ReplacedChild::None => {}
581                    ReplacedChild::ObjectWithRemainingLinks(..) | ReplacedChild::Object(_) => {
582                        self.did_remove(actual_dst_name);
583                    }
584                    ReplacedChild::Directory(id) => {
585                        let store = self.store();
586                        store
587                            .filesystem()
588                            .graveyard()
589                            .queue_tombstone_object(store.store_object_id(), id);
590                        self.did_remove(actual_dst_name);
591                        self.volume().mark_directory_deleted(id);
592                    }
593                }
594                self.did_add(dst, Some(moved_node));
595            })
596            .await
597            .map_err(map_to_status)?;
598
599        if let ReplacedChild::Object(id) = replace_result {
600            self.volume().maybe_purge_file(id).await.map_err(map_to_status)?;
601        }
602        Ok(())
603    }
604
605    pub(crate) async fn open_block_file(
606        self: &Arc<Self>,
607        name: &str,
608        server_end: ServerEnd<BlockMarker>,
609    ) {
610        let request = ObjectRequest::new(
611            fio::Flags::empty(),
612            &fio::Options::default(),
613            server_end.into_channel(),
614        );
615        let scope = self.volume().scope().clone();
616        let this = self.clone();
617        request
618            .handle_async(async move |request| {
619                let path = Path::validate_and_split(name).and_then(|p| {
620                    if p.is_single_component() { Ok(p) } else { Err(zx::Status::INVALID_ARGS) }
621                })?;
622                let node = this
623                    .lookup(&fio::Flags::empty(), path, request)
624                    .await
625                    .map_err(map_to_status)?;
626                if node.is::<FxFile>() {
627                    let file = node.downcast::<FxFile>().unwrap_or_else(|_| unreachable!());
628                    if file.is_verified_file() {
629                        log::error!("Tried to expose a verified file as a block device.");
630                        return Err(zx::Status::NOT_SUPPORTED);
631                    }
632                    let server = BlockServer::new(file, request.take().into_channel());
633                    scope.spawn(server.run());
634                    Ok(())
635                } else {
636                    Err(zx::Status::NOT_FILE)
637                }
638            })
639            .await
640    }
641}
642
643impl Drop for FxDirectory {
644    fn drop(&mut self) {
645        self.volume().cache().remove(self);
646    }
647}
648
649impl FxNode for FxDirectory {
650    fn object_id(&self) -> u64 {
651        self.directory.object_id()
652    }
653
654    fn parent(&self) -> Option<Arc<FxDirectory>> {
655        self.parent.as_ref().map(|p| p.lock().clone())
656    }
657
658    fn set_parent(&self, parent: Arc<FxDirectory>) {
659        match &self.parent {
660            Some(p) => *p.lock() = parent,
661            None => panic!("Called set_parent on root node"),
662        }
663    }
664
665    // If these ever do anything, BlobDirectory might need to be fixed.
666    fn open_count_add_one(&self) {}
667    fn open_count_sub_one(self: Arc<Self>) {}
668
669    fn object_descriptor(&self) -> ObjectDescriptor {
670        ObjectDescriptor::Directory
671    }
672}
673
674impl MutableDirectory for FxDirectory {
675    fn link<'a>(
676        self: Arc<Self>,
677        name: String,
678        source_dir: Arc<dyn Any + Send + Sync>,
679        source_name: &'a str,
680    ) -> BoxFuture<'a, Result<(), zx::Status>> {
681        Box::pin(self.link_impl(name, source_dir, source_name))
682    }
683
684    async fn unlink(
685        self: Arc<Self>,
686        name: &str,
687        must_be_directory: bool,
688    ) -> Result<(), zx::Status> {
689        let replace_context = self
690            .directory
691            .acquire_context_for_replace(None, name, true)
692            .await
693            .map_err(map_to_status)?;
694        // Use name from the replace_context if available (which preserves case-folding info).
695        // `name` is user supplied name and may have different case.
696        let actual_dst_name = replace_context.dst_name.as_deref().unwrap_or(name);
697        let mut transaction = replace_context.transaction;
698        let (_, object_descriptor) =
699            replace_context.dst_id_and_descriptor.ok_or(zx::Status::NOT_FOUND)?;
700        if let ObjectDescriptor::Directory = object_descriptor {
701        } else if must_be_directory {
702            return Err(zx::Status::NOT_DIR);
703        }
704        match directory::replace_child(&mut transaction, None, (self.directory(), name))
705            .await
706            .map_err(map_to_status)?
707        {
708            ReplacedChild::None => return Err(zx::Status::NOT_FOUND),
709            ReplacedChild::ObjectWithRemainingLinks(..) => {
710                transaction
711                    .commit_with_callback(|_| self.did_remove(actual_dst_name))
712                    .await
713                    .map_err(map_to_status)?;
714            }
715            ReplacedChild::Object(id) => {
716                transaction
717                    .commit_with_callback(|_| self.did_remove(actual_dst_name))
718                    .await
719                    .map_err(map_to_status)?;
720                // If purging fails, we should still return success, since the file will appear
721                // unlinked at this point anyways.  The file should be cleaned up on a later mount.
722                if let Err(e) = self.volume().maybe_purge_file(id).await {
723                    warn!(error:? = e; "Failed to purge file");
724                }
725            }
726            ReplacedChild::Directory(id) => {
727                transaction
728                    .commit_with_callback(|_| {
729                        let store = self.store();
730                        store
731                            .filesystem()
732                            .graveyard()
733                            .queue_tombstone_object(store.store_object_id(), id);
734                        self.did_remove(actual_dst_name);
735                        self.volume().mark_directory_deleted(id);
736                    })
737                    .await
738                    .map_err(map_to_status)?;
739            }
740        }
741        Ok(())
742    }
743
744    async fn update_attributes(
745        &self,
746        attributes: fio::MutableNodeAttributes,
747    ) -> Result<(), zx::Status> {
748        // TODO(b/365630582): Reconsider doing this as part of the transaction below.
749        if let Some(casefold) = attributes.casefold {
750            self.directory.set_casefold(casefold).await.map_err(map_to_status)?;
751        }
752        let transaction = self
753            .store()
754            .new_transaction(
755                lock_keys![LockKey::object(
756                    self.store().store_object_id(),
757                    self.directory.object_id()
758                )],
759                Options { borrow_metadata_space: true, ..Default::default() },
760            )
761            .await
762            .map_err(map_to_status)?;
763
764        self.directory
765            .update_attributes(transaction, Some(&attributes), 0, Some(Timestamp::now()))
766            .await
767            .map_err(map_to_status)?;
768        Ok(())
769    }
770
771    async fn sync(&self) -> Result<(), zx::Status> {
772        // FDIO implements `syncfs` by calling sync on a directory, so replicate that behaviour.
773        self.volume()
774            .store()
775            .filesystem()
776            .sync(SyncOptions { flush_device: true, ..Default::default() })
777            .await
778            .map_err(map_to_status)
779    }
780
781    fn rename(
782        self: Arc<Self>,
783        src_dir: Arc<dyn MutableDirectory>,
784        src_name: Path,
785        dst_name: Path,
786    ) -> BoxFuture<'static, Result<(), zx::Status>> {
787        Box::pin(self.rename_impl(src_dir, src_name, dst_name))
788    }
789
790    async fn create_symlink(
791        &self,
792        name: String,
793        target: Vec<u8>,
794        connection: Option<ServerEnd<fio::SymlinkMarker>>,
795    ) -> Result<(), zx::Status> {
796        let store = self.store();
797        let dir = &self.directory;
798        let keys = lock_keys![LockKey::object(store.store_object_id(), dir.object_id())];
799        let mut transaction =
800            store.new_transaction(keys, Options::default()).await.map_err(map_to_status)?;
801        if dir.lookup(&name).await.map_err(map_to_status)?.is_some() {
802            return Err(zx::Status::ALREADY_EXISTS);
803        }
804        let object_id =
805            dir.create_symlink(&mut transaction, &target, &name).await.map_err(map_to_status)?;
806        if let Some(connection) = connection {
807            if let GetResult::Placeholder(p) = self.volume().cache().get_or_reserve(object_id).await
808            {
809                transaction
810                    .commit_with_callback(|_| {
811                        let node = Arc::new(FxSymlink::new(self.volume().clone(), object_id));
812                        p.commit(&(node.clone() as Arc<dyn FxNode>));
813                        let scope = self.volume().scope().clone();
814                        let flags =
815                            fio::Flags::PROTOCOL_SYMLINK | fio::PERM_READABLE | fio::PERM_WRITABLE;
816                        // Wrap in OpenedNode to set open_count to 1 for the connection.
817                        let opened_node = OpenedNode::new(node);
818                        // fio::Flags::FLAG_SEND_REPRESENTATION isn't specified so connection
819                        // creation is synchronous.
820                        symlink::Connection::create_sync(
821                            scope,
822                            opened_node.take(),
823                            flags,
824                            flags.to_object_request(connection),
825                        );
826                    })
827                    .await
828            } else {
829                // The node already exists in the cache which could only happen if the filesystem is
830                // corrupt.
831                return Err(zx::Status::IO_DATA_INTEGRITY);
832            }
833        } else {
834            transaction.commit().await.map(|_| ())
835        }
836        .map_err(map_to_status)
837    }
838}
839
840impl DirectoryEntry for FxDirectory {
841    fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), zx::Status> {
842        request.open_dir(self)
843    }
844
845    fn scope(&self) -> Option<ExecutionScope> {
846        Some(self.volume().scope().clone())
847    }
848}
849
850impl GetEntryInfo for FxDirectory {
851    fn entry_info(&self) -> EntryInfo {
852        EntryInfo::new(self.object_id(), fio::DirentType::Directory)
853    }
854}
855
856impl vfs::node::Node for FxDirectory {
857    async fn get_attributes(
858        &self,
859        requested_attributes: fio::NodeAttributesQuery,
860    ) -> Result<fio::NodeAttributes2, zx::Status> {
861        let mut props = self.directory.get_properties().await.map_err(map_to_status)?;
862
863        if requested_attributes.contains(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE) {
864            self.store()
865                .update_access_time(self.directory.object_id(), &mut props, || !self.is_deleted())
866                .await
867                .map_err(map_to_status)?;
868        }
869
870        Ok(attributes!(
871            requested_attributes,
872            Mutable {
873                creation_time: props.creation_time.as_nanos(),
874                modification_time: props.modification_time.as_nanos(),
875                access_time: props.access_time.as_nanos(),
876                mode: props.posix_attributes.map(|a| a.mode),
877                uid: props.posix_attributes.map(|a| a.uid),
878                gid: props.posix_attributes.map(|a| a.gid),
879                rdev: props.posix_attributes.map(|a| a.rdev),
880                casefold: self.directory.dir_type().is_casefold(),
881                selinux_context: self
882                    .directory
883                    .handle()
884                    .get_inline_selinux_context()
885                    .await
886                    .map_err(map_to_status)?,
887                wrapping_key_id: props.dir_type.wrapping_key_id(),
888            },
889            Immutable {
890                protocols: fio::NodeProtocolKinds::DIRECTORY,
891                abilities: fio::Operations::GET_ATTRIBUTES
892                    | fio::Operations::UPDATE_ATTRIBUTES
893                    | fio::Operations::ENUMERATE
894                    | fio::Operations::TRAVERSE
895                    | fio::Operations::MODIFY_DIRECTORY,
896                content_size: props.data_attribute_size,
897                storage_size: props.allocated_size,
898                link_count: props.refs + 1 + props.sub_dirs,
899                id: self.directory.object_id(),
900                change_time: props.change_time.as_nanos(),
901                verity_enabled: false,
902            }
903        ))
904    }
905
906    fn query_filesystem(&self) -> Result<fio::FilesystemInfo, zx::Status> {
907        Ok(self.volume().filesystem_info_for_volume())
908    }
909
910    async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, zx::Status> {
911        self.directory.list_extended_attributes().await.map_err(map_to_status)
912    }
913
914    async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, zx::Status> {
915        self.directory.get_extended_attribute(name).await.map_err(map_to_status)
916    }
917
918    async fn set_extended_attribute(
919        &self,
920        name: Vec<u8>,
921        value: Vec<u8>,
922        mode: fio::SetExtendedAttributeMode,
923    ) -> Result<(), zx::Status> {
924        self.directory.set_extended_attribute(name, value, mode.into()).await.map_err(map_to_status)
925    }
926
927    async fn remove_extended_attribute(&self, name: Vec<u8>) -> Result<(), zx::Status> {
928        self.directory.remove_extended_attribute(name).await.map_err(map_to_status)
929    }
930}
931
932impl VfsDirectory for FxDirectory {
933    fn deprecated_open(
934        self: Arc<Self>,
935        scope: ExecutionScope,
936        flags: fio::OpenFlags,
937        path: Path,
938        server_end: ServerEnd<fio::NodeMarker>,
939    ) {
940        scope.clone().spawn(flags.to_object_request(server_end).handle_async(
941            async move |object_request| {
942                let node =
943                    self.lookup(&flags, path, object_request).await.map_err(map_to_status)?;
944                if node.is::<FxDirectory>() {
945                    let directory =
946                        node.downcast::<FxDirectory>().unwrap_or_else(|_| unreachable!()).take();
947                    object_request
948                        .create_connection::<MutableConnection<_>, _>(scope, directory, flags)
949                        .await
950                } else if node.is::<FxFile>() {
951                    let node = node.downcast::<FxFile>().unwrap_or_else(|_| unreachable!());
952                    if flags.contains(fio::OpenFlags::BLOCK_DEVICE) {
953                        if node.is_verified_file() {
954                            log::error!("Tried to expose a verified file as a block device.");
955                            return Err(zx::Status::NOT_SUPPORTED);
956                        }
957                        if !flags.contains(fio::OpenFlags::RIGHT_READABLE) {
958                            log::error!(
959                                "Opening a file as block device requires at least RIGHT_READABLE."
960                            );
961                            return Err(zx::Status::ACCESS_DENIED);
962                        }
963                        let server = BlockServer::new(node, object_request.take().into_channel());
964                        scope.spawn(server.run());
965                        Ok(())
966                    } else {
967                        FxFile::create_connection_async(node, scope, flags, object_request).await
968                    }
969                } else if node.is::<FxSymlink>() {
970                    let node = node.downcast::<FxSymlink>().unwrap_or_else(|_| unreachable!());
971                    object_request
972                        .create_connection::<symlink::Connection<_>, _>(
973                            scope.clone(),
974                            node.take(),
975                            flags,
976                        )
977                        .await
978                } else {
979                    unreachable!();
980                }
981            },
982        ));
983    }
984
985    fn open(
986        self: Arc<Self>,
987        scope: ExecutionScope,
988        path: Path,
989        flags: fio::Flags,
990        object_request: ObjectRequestRef<'_>,
991    ) -> Result<(), zx::Status> {
992        self.volume().scope().clone().spawn(object_request.take().handle_async(
993            async move |object_request| self.open_async(scope, path, flags, object_request).await,
994        ));
995        Ok(())
996    }
997
998    async fn open_async(
999        self: Arc<Self>,
1000        scope: ExecutionScope,
1001        path: Path,
1002        flags: fio::Flags,
1003        object_request: ObjectRequestRef<'_>,
1004    ) -> Result<(), zx::Status> {
1005        let node = self.lookup(&flags, path, object_request).await.map_err(map_to_status)?;
1006        if node.is::<FxDirectory>() {
1007            let directory =
1008                node.downcast::<FxDirectory>().unwrap_or_else(|_| unreachable!()).take();
1009            object_request
1010                .create_connection::<MutableConnection<_>, _>(scope, directory, flags)
1011                .await
1012        } else if node.is::<FxFile>() {
1013            let file = node.downcast::<FxFile>().unwrap_or_else(|_| unreachable!());
1014            FxFile::create_connection_async(file, scope, flags, object_request).await
1015        } else if node.is::<FxSymlink>() {
1016            let symlink = node.downcast::<FxSymlink>().unwrap_or_else(|_| unreachable!());
1017            object_request
1018                .create_connection::<symlink::Connection<_>, _>(
1019                    scope.clone(),
1020                    symlink.take(),
1021                    flags,
1022                )
1023                .await
1024        } else {
1025            unreachable!();
1026        }
1027    }
1028
1029    async fn read_dirents(
1030        &self,
1031        pos: &TraversalPosition,
1032        mut sink: Box<dyn dirents_sink::Sink>,
1033    ) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), zx::Status> {
1034        if let TraversalPosition::End = pos {
1035            return Ok((TraversalPosition::End, sink.seal()));
1036        } else if let TraversalPosition::Index(_) = pos {
1037            // The VFS should never send this to us, since we never return it here.
1038            return Err(zx::Status::BAD_STATE);
1039        }
1040
1041        let store = self.store();
1042        let fs = store.filesystem();
1043        let _read_guard = fs
1044            .lock_manager()
1045            .read_lock(lock_keys![LockKey::object(store.store_object_id(), self.object_id())])
1046            .await;
1047        if self.is_deleted() {
1048            return Ok((TraversalPosition::End, sink.seal()));
1049        }
1050
1051        let layer_set = self.store().tree().layer_set();
1052        let mut merger = layer_set.merger();
1053        let mut iter = match pos {
1054            TraversalPosition::Start => {
1055                // Synthesize a "." entry if we're at the start of the stream.
1056                match sink
1057                    .append(&EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory), ".")
1058                {
1059                    AppendResult::Ok(new_sink) => sink = new_sink,
1060                    AppendResult::Sealed(sealed) => {
1061                        // Note that the VFS should have yielded an error since the first entry
1062                        // didn't fit. This is defensive in case the VFS' behaviour changes, so that
1063                        // we return a reasonable value.
1064                        return Ok((TraversalPosition::Start, sealed));
1065                    }
1066                }
1067                self.directory.iter(&mut merger).await
1068            }
1069            TraversalPosition::Name(name) => self.directory.iter_from(&mut merger, name).await,
1070            TraversalPosition::Bytes(bytes) => {
1071                self.directory.iter_from_bytes(&mut merger, bytes).await
1072            }
1073            _ => unreachable!(),
1074        }
1075        .map_err(map_to_status)?;
1076        while let Some((name, object_id, object_descriptor)) = iter.get() {
1077            let entry_type = match object_descriptor {
1078                ObjectDescriptor::File => fio::DirentType::File,
1079                ObjectDescriptor::Directory => fio::DirentType::Directory,
1080                ObjectDescriptor::Symlink => fio::DirentType::Symlink,
1081                ObjectDescriptor::Volume => return Err(zx::Status::IO_DATA_INTEGRITY),
1082            };
1083
1084            let info = EntryInfo::new(object_id, entry_type);
1085            match sink.append(&info, &name) {
1086                AppendResult::Ok(new_sink) => sink = new_sink,
1087                AppendResult::Sealed(sealed) => {
1088                    // We did *not* add the current entry to the sink (e.g. because the sink was
1089                    // full), so mark |name| as the next position so that it's the first entry we
1090                    // process on a subsequent call of read_dirents.
1091                    // Note that entries inserted between the previous entry and this entry before
1092                    // the next call to read_dirents would not be included in the results (but
1093                    // there's no requirement to include them anyways).
1094                    return Ok((
1095                        iter.traversal_position(
1096                            |name| TraversalPosition::Name(name.to_string()),
1097                            |bytes| TraversalPosition::Bytes(bytes),
1098                        )
1099                        .unwrap(),
1100                        sealed,
1101                    ));
1102                }
1103            }
1104            iter.advance().await.map_err(map_to_status)?;
1105        }
1106
1107        Ok((TraversalPosition::End, sink.seal()))
1108    }
1109
1110    fn register_watcher(
1111        self: Arc<Self>,
1112        scope: ExecutionScope,
1113        mask: fio::WatchMask,
1114        watcher: DirectoryWatcher,
1115    ) -> Result<(), zx::Status> {
1116        let controller =
1117            self.watchers.lock().add(scope.clone(), self.clone(), mask, watcher).clone();
1118        if mask.contains(fio::WatchMask::EXISTING) && !self.is_deleted() {
1119            scope.spawn(async move {
1120                let layer_set = self.store().tree().layer_set();
1121                let mut merger = layer_set.merger();
1122                let mut iter = match self.directory.iter_from(&mut merger, "").await {
1123                    Ok(iter) => iter,
1124                    Err(e) => {
1125                        error!(error:? = e; "Failed to iterate directory for watch",);
1126                        // TODO(https://fxbug.dev/42178164): This really should close the watcher connection
1127                        // with an epitaph so that the watcher knows.
1128                        return;
1129                    }
1130                };
1131                // TODO(https://fxbug.dev/42178165): It is possible that we'll duplicate entries that are added
1132                // as we iterate over directories.  I suspect fixing this might be non-trivial.
1133                controller.send_event(&mut SingleNameEventProducer::existing("."));
1134                while let Some((name, _, _)) = iter.get() {
1135                    controller.send_event(&mut SingleNameEventProducer::existing(name));
1136                    if let Err(e) = iter.advance().await {
1137                        error!(error:? = e; "Failed to iterate directory for watch",);
1138                        return;
1139                    }
1140                }
1141                controller.send_event(&mut SingleNameEventProducer::idle());
1142            });
1143        }
1144        Ok(())
1145    }
1146
1147    fn unregister_watcher(self: Arc<Self>, key: usize) {
1148        self.watchers.lock().remove(key);
1149    }
1150}
1151
1152impl From<Directory<FxVolume>> for FxDirectory {
1153    fn from(dir: Directory<FxVolume>) -> Self {
1154        Self::new(None, dir)
1155    }
1156}
1157
1158#[cfg(test)]
1159mod tests {
1160    use crate::directory::FxDirectory;
1161    use crate::file::FxFile;
1162    use crate::fuchsia::testing::{
1163        TestFixture, TestFixtureOptions, close_dir_checked, close_file_checked, open_dir,
1164        open_dir_checked, open_file, open_file_checked,
1165    };
1166    use anyhow::bail;
1167    use assert_matches::assert_matches;
1168    use fidl::endpoints::{ClientEnd, Proxy, create_proxy};
1169    use fidl_fuchsia_io as fio;
1170    use fuchsia_async as fasync;
1171    use fuchsia_fs::directory::{DirEntry, DirentKind, WatchEvent, WatchMessage, Watcher};
1172    use fuchsia_fs::file;
1173    use futures::{StreamExt, join};
1174    use fxfs::lsm_tree::Query;
1175    use fxfs::lsm_tree::types::{ItemRef, LayerIterator};
1176    use fxfs::object_store::transaction::{LockKey, lock_keys};
1177    use fxfs::object_store::{ObjectKey, ObjectKeyData, ObjectValue, Timestamp};
1178    use fxfs_crypt_common::CryptBase;
1179    use fxfs_crypto::{FSCRYPT_PADDING, WrappingKeyId};
1180    use std::future::poll_fn;
1181    use std::os::fd::AsRawFd;
1182    use std::sync::Arc;
1183    use std::sync::atomic::{AtomicU64, Ordering};
1184    use std::task::Poll;
1185    use std::time::Duration;
1186    use storage_device::DeviceHolder;
1187    use storage_device::fake_device::FakeDevice;
1188    use vfs::ObjectRequest;
1189    use vfs::node::Node;
1190    use vfs::path::Path;
1191
1192    const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(2);
1193
1194    async fn yield_to_executor() {
1195        let mut done = false;
1196        poll_fn(|cx| {
1197            if done {
1198                Poll::Ready(())
1199            } else {
1200                done = true;
1201                cx.waker().wake_by_ref();
1202                Poll::Pending
1203            }
1204        })
1205        .await;
1206    }
1207
1208    #[fuchsia::test]
1209    async fn test_open_root_dir() {
1210        let fixture = TestFixture::new().await;
1211        let root = fixture.root();
1212        let _: Vec<_> = root.query().await.expect("query failed");
1213        fixture.close().await;
1214    }
1215
1216    #[fuchsia::test]
1217    async fn test_create_dir_persists() {
1218        let mut device = DeviceHolder::new(FakeDevice::new(8192, 512));
1219        for i in 0..2 {
1220            let fixture = TestFixture::open(
1221                device,
1222                TestFixtureOptions { format: i == 0, ..Default::default() },
1223            )
1224            .await;
1225            let root = fixture.root();
1226
1227            let flags = if i == 0 {
1228                fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE
1229            } else {
1230                fio::PERM_READABLE
1231            };
1232            let dir = open_dir_checked(
1233                &root,
1234                "foo",
1235                flags | fio::Flags::PROTOCOL_DIRECTORY,
1236                Default::default(),
1237            )
1238            .await;
1239            close_dir_checked(dir).await;
1240
1241            device = fixture.close().await;
1242        }
1243    }
1244
1245    #[fuchsia::test]
1246    async fn test_open_nonexistent_file() {
1247        let fixture = TestFixture::new().await;
1248        let root = fixture.root();
1249
1250        assert_eq!(
1251            open_file(
1252                &root,
1253                "foo",
1254                fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1255                &Default::default()
1256            )
1257            .await
1258            .expect_err("Open succeeded")
1259            .root_cause()
1260            .downcast_ref::<zx::Status>()
1261            .expect("No status"),
1262            &zx::Status::NOT_FOUND,
1263        );
1264
1265        fixture.close().await;
1266    }
1267
1268    #[fuchsia::test]
1269    async fn test_create_file() {
1270        let fixture = TestFixture::new().await;
1271        let root = fixture.root();
1272
1273        let f = open_file_checked(
1274            &root,
1275            "foo",
1276            fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1277            &Default::default(),
1278        )
1279        .await;
1280        close_file_checked(f).await;
1281
1282        let f = open_file_checked(
1283            &root,
1284            "foo",
1285            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1286            &Default::default(),
1287        )
1288        .await;
1289        close_file_checked(f).await;
1290
1291        fixture.close().await;
1292    }
1293
1294    #[fuchsia::test]
1295    async fn test_create_dir_nested() {
1296        let fixture = TestFixture::new().await;
1297        let root = fixture.root();
1298
1299        let d = open_dir_checked(
1300            &root,
1301            "foo",
1302            fio::Flags::FLAG_MAYBE_CREATE
1303                | fio::PERM_READABLE
1304                | fio::PERM_WRITABLE
1305                | fio::Flags::PROTOCOL_DIRECTORY,
1306            Default::default(),
1307        )
1308        .await;
1309        close_dir_checked(d).await;
1310
1311        let d = open_dir_checked(
1312            &root,
1313            "foo/bar",
1314            fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
1315            Default::default(),
1316        )
1317        .await;
1318        close_dir_checked(d).await;
1319
1320        let d = open_dir_checked(
1321            &root,
1322            "foo/bar",
1323            fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
1324            Default::default(),
1325        )
1326        .await;
1327        close_dir_checked(d).await;
1328
1329        fixture.close().await;
1330    }
1331
1332    #[fuchsia::test]
1333    async fn test_strict_create_file_fails_if_present() {
1334        let fixture = TestFixture::new().await;
1335        let root = fixture.root();
1336
1337        let f = open_file_checked(
1338            &root,
1339            "foo",
1340            fio::Flags::FLAG_MAYBE_CREATE
1341                | fio::Flags::FLAG_MUST_CREATE
1342                | fio::PERM_READABLE
1343                | fio::Flags::PROTOCOL_FILE,
1344            &Default::default(),
1345        )
1346        .await;
1347        close_file_checked(f).await;
1348
1349        assert_eq!(
1350            open_file(
1351                &root,
1352                "foo",
1353                fio::Flags::FLAG_MAYBE_CREATE
1354                    | fio::Flags::FLAG_MUST_CREATE
1355                    | fio::PERM_READABLE
1356                    | fio::Flags::PROTOCOL_FILE,
1357                &Default::default()
1358            )
1359            .await
1360            .expect_err("Open succeeded")
1361            .root_cause()
1362            .downcast_ref::<zx::Status>()
1363            .expect("No status"),
1364            &zx::Status::ALREADY_EXISTS,
1365        );
1366
1367        fixture.close().await;
1368    }
1369
1370    #[fuchsia::test]
1371    async fn test_unlink_file_with_no_refs_immediately_freed() {
1372        let fixture = TestFixture::new().await;
1373        let root = fixture.root();
1374
1375        let file = open_file_checked(
1376            &root,
1377            "foo",
1378            fio::Flags::FLAG_MAYBE_CREATE
1379                | fio::PERM_READABLE
1380                | fio::PERM_WRITABLE
1381                | fio::Flags::PROTOCOL_FILE,
1382            &Default::default(),
1383        )
1384        .await;
1385
1386        // Fill up the file with a lot of data, so we can verify that the extents are freed.
1387        let buf = vec![0xaa as u8; 512];
1388        loop {
1389            match file::write(&file, buf.as_slice()).await {
1390                Ok(_) => {}
1391                Err(e) => {
1392                    if let fuchsia_fs::file::WriteError::WriteError(status) = e {
1393                        if status == zx::Status::NO_SPACE {
1394                            break;
1395                        }
1396                    }
1397                    panic!("Unexpected write error {:?}", e);
1398                }
1399            }
1400        }
1401
1402        close_file_checked(file).await;
1403
1404        root.unlink("foo", &fio::UnlinkOptions::default())
1405            .await
1406            .expect("FIDL call failed")
1407            .expect("unlink failed");
1408
1409        assert_eq!(
1410            open_file(
1411                &root,
1412                "foo",
1413                fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1414                &Default::default()
1415            )
1416            .await
1417            .expect_err("Open succeeded")
1418            .root_cause()
1419            .downcast_ref::<zx::Status>()
1420            .expect("No status"),
1421            &zx::Status::NOT_FOUND,
1422        );
1423
1424        // Create another file so we can verify that the extents were actually freed.
1425        let file = open_file_checked(
1426            &root,
1427            "bar",
1428            fio::Flags::FLAG_MAYBE_CREATE
1429                | fio::PERM_READABLE
1430                | fio::PERM_WRITABLE
1431                | fio::Flags::PROTOCOL_FILE,
1432            &Default::default(),
1433        )
1434        .await;
1435        let buf = vec![0xaa as u8; 8192];
1436        file::write(&file, buf.as_slice()).await.expect("Failed to write new file");
1437        close_file_checked(file).await;
1438
1439        fixture.close().await;
1440    }
1441
1442    #[fuchsia::test]
1443    async fn test_unlink_file() {
1444        let fixture = TestFixture::new().await;
1445        let root = fixture.root();
1446
1447        let file = open_file_checked(
1448            &root,
1449            "foo",
1450            fio::Flags::FLAG_MAYBE_CREATE
1451                | fio::PERM_READABLE
1452                | fio::PERM_WRITABLE
1453                | fio::Flags::PROTOCOL_FILE,
1454            &Default::default(),
1455        )
1456        .await;
1457        close_file_checked(file).await;
1458
1459        root.unlink("foo", &fio::UnlinkOptions::default())
1460            .await
1461            .expect("FIDL call failed")
1462            .expect("unlink failed");
1463
1464        assert_eq!(
1465            open_file(
1466                &root,
1467                "foo",
1468                fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1469                &Default::default()
1470            )
1471            .await
1472            .expect_err("Open succeeded")
1473            .root_cause()
1474            .downcast_ref::<zx::Status>()
1475            .expect("No status"),
1476            &zx::Status::NOT_FOUND,
1477        );
1478
1479        fixture.close().await;
1480    }
1481
1482    #[fuchsia::test]
1483    async fn test_unlink_file_with_active_references() {
1484        let fixture = TestFixture::new().await;
1485        let root = fixture.root();
1486
1487        let file = open_file_checked(
1488            &root,
1489            "foo",
1490            fio::Flags::FLAG_MAYBE_CREATE
1491                | fio::PERM_READABLE
1492                | fio::PERM_WRITABLE
1493                | fio::Flags::PROTOCOL_FILE,
1494            &Default::default(),
1495        )
1496        .await;
1497
1498        let buf = vec![0xaa as u8; 512];
1499        file::write(&file, buf.as_slice()).await.expect("write failed");
1500
1501        root.unlink("foo", &fio::UnlinkOptions::default())
1502            .await
1503            .expect("FIDL call failed")
1504            .expect("unlink failed");
1505
1506        // The child should immediately appear unlinked...
1507        assert_eq!(
1508            open_file(
1509                &root,
1510                "foo",
1511                fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1512                &Default::default()
1513            )
1514            .await
1515            .expect_err("Open succeeded")
1516            .root_cause()
1517            .downcast_ref::<zx::Status>()
1518            .expect("No status"),
1519            &zx::Status::NOT_FOUND,
1520        );
1521
1522        // But its contents should still be readable from the other handle.
1523        file.seek(fio::SeekOrigin::Start, 0)
1524            .await
1525            .expect("seek failed")
1526            .map_err(zx::Status::from_raw)
1527            .expect("seek error");
1528        let rbuf = file::read(&file).await.expect("read failed");
1529        assert_eq!(rbuf, buf);
1530        close_file_checked(file).await;
1531
1532        fixture.close().await;
1533    }
1534
1535    #[fuchsia::test]
1536    async fn test_unlink_dir_with_children_fails() {
1537        let fixture = TestFixture::new().await;
1538        let root = fixture.root();
1539
1540        let dir = open_dir_checked(
1541            &root,
1542            "foo",
1543            fio::Flags::FLAG_MAYBE_CREATE
1544                | fio::PERM_READABLE
1545                | fio::PERM_WRITABLE
1546                | fio::Flags::PROTOCOL_DIRECTORY,
1547            Default::default(),
1548        )
1549        .await;
1550        let f = open_file_checked(
1551            &dir,
1552            "bar",
1553            fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE,
1554            &Default::default(),
1555        )
1556        .await;
1557        close_file_checked(f).await;
1558
1559        assert_eq!(
1560            zx::Status::from_raw(
1561                root.unlink("foo", &fio::UnlinkOptions::default())
1562                    .await
1563                    .expect("FIDL call failed")
1564                    .expect_err("unlink succeeded")
1565            ),
1566            zx::Status::NOT_EMPTY
1567        );
1568
1569        dir.unlink("bar", &fio::UnlinkOptions::default())
1570            .await
1571            .expect("FIDL call failed")
1572            .expect("unlink failed");
1573        root.unlink("foo", &fio::UnlinkOptions::default())
1574            .await
1575            .expect("FIDL call failed")
1576            .expect("unlink failed");
1577
1578        close_dir_checked(dir).await;
1579
1580        fixture.close().await;
1581    }
1582
1583    #[fuchsia::test]
1584    async fn test_unlink_dir_makes_directory_immutable() {
1585        let fixture = TestFixture::new().await;
1586        let root = fixture.root();
1587
1588        let dir = open_dir_checked(
1589            &root,
1590            "foo",
1591            fio::Flags::FLAG_MAYBE_CREATE
1592                | fio::PERM_READABLE
1593                | fio::PERM_WRITABLE
1594                | fio::Flags::PROTOCOL_DIRECTORY,
1595            Default::default(),
1596        )
1597        .await;
1598
1599        root.unlink("foo", &fio::UnlinkOptions::default())
1600            .await
1601            .expect("FIDL call failed")
1602            .expect("unlink failed");
1603
1604        assert_eq!(
1605            open_file(
1606                &dir,
1607                "bar",
1608                fio::PERM_READABLE | fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1609                &Default::default()
1610            )
1611            .await
1612            .expect_err("Create file succeeded")
1613            .root_cause()
1614            .downcast_ref::<zx::Status>()
1615            .expect("No status"),
1616            &zx::Status::ACCESS_DENIED,
1617        );
1618
1619        close_dir_checked(dir).await;
1620
1621        fixture.close().await;
1622    }
1623
1624    #[fuchsia::test(threads = 10)]
1625    async fn test_unlink_directory_with_children_race() {
1626        let fixture = TestFixture::new().await;
1627        let root = fixture.root();
1628
1629        const PARENT: &str = "foo";
1630        const CHILD: &str = "bar";
1631        const GRANDCHILD: &str = "baz";
1632        open_dir_checked(
1633            &root,
1634            PARENT,
1635            fio::Flags::FLAG_MAYBE_CREATE
1636                | fio::PERM_READABLE
1637                | fio::PERM_WRITABLE
1638                | fio::Flags::PROTOCOL_DIRECTORY,
1639            Default::default(),
1640        )
1641        .await;
1642
1643        let open_parent = || async {
1644            open_dir_checked(
1645                &root,
1646                PARENT,
1647                fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1648                Default::default(),
1649            )
1650            .await
1651        };
1652        let parent = open_parent().await;
1653
1654        // Each iteration proceeds as follows:
1655        //  - Initialize a directory foo/bar/. (This might still be around from the previous
1656        //    iteration, which is fine.)
1657        //  - In one task, try to unlink foo/bar/.
1658        //  - In another task, try to add a file foo/bar/baz.
1659        for _ in 0..100 {
1660            let d = open_dir_checked(
1661                &parent,
1662                CHILD,
1663                fio::Flags::FLAG_MAYBE_CREATE
1664                    | fio::PERM_READABLE
1665                    | fio::PERM_WRITABLE
1666                    | fio::Flags::PROTOCOL_DIRECTORY,
1667                Default::default(),
1668            )
1669            .await;
1670            close_dir_checked(d).await;
1671
1672            let parent = open_parent().await;
1673            let deleter = fasync::Task::spawn(async move {
1674                let wait_time = rand::random_range(0..5);
1675                fasync::Timer::new(Duration::from_millis(wait_time)).await;
1676                match parent
1677                    .unlink(CHILD, &fio::UnlinkOptions::default())
1678                    .await
1679                    .expect("FIDL call failed")
1680                    .map_err(zx::Status::from_raw)
1681                {
1682                    Ok(()) => {}
1683                    Err(zx::Status::NOT_EMPTY) => {}
1684                    Err(e) => panic!("Unexpected status from unlink: {:?}", e),
1685                };
1686                close_dir_checked(parent).await;
1687            });
1688
1689            let parent = open_parent().await;
1690            let writer = fasync::Task::spawn(async move {
1691                let child_or = open_dir(
1692                    &parent,
1693                    CHILD,
1694                    fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1695                    &Default::default(),
1696                )
1697                .await;
1698                if let Err(e) = &child_or {
1699                    // The directory was already deleted.
1700                    assert_eq!(
1701                        e.root_cause().downcast_ref::<zx::Status>().expect("No status"),
1702                        &zx::Status::NOT_FOUND
1703                    );
1704                    close_dir_checked(parent).await;
1705                    return;
1706                }
1707                let child = child_or.unwrap();
1708                let _: Vec<_> = child.query().await.expect("query failed");
1709                match open_file(
1710                    &child,
1711                    GRANDCHILD,
1712                    fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
1713                    &Default::default(),
1714                )
1715                .await
1716                {
1717                    Ok(grandchild) => {
1718                        let _: Vec<_> = grandchild.query().await.expect("query failed");
1719                        close_file_checked(grandchild).await;
1720                        // We added the child before the directory was deleted; go ahead and
1721                        // clean up.
1722                        child
1723                            .unlink(GRANDCHILD, &fio::UnlinkOptions::default())
1724                            .await
1725                            .expect("FIDL call failed")
1726                            .expect("unlink failed");
1727                    }
1728                    Err(e) => {
1729                        // The directory started to be deleted before we created a child.
1730                        // Make sure we get the right error.
1731                        assert_eq!(
1732                            e.root_cause().downcast_ref::<zx::Status>().expect("No status"),
1733                            &zx::Status::ACCESS_DENIED,
1734                        );
1735                    }
1736                };
1737                close_dir_checked(child).await;
1738                close_dir_checked(parent).await;
1739            });
1740            writer.await;
1741            deleter.await;
1742        }
1743
1744        close_dir_checked(parent).await;
1745        fixture.close().await;
1746    }
1747
1748    #[fuchsia::test]
1749    async fn test_readdir() {
1750        let fixture = TestFixture::new().await;
1751        let root = fixture.root();
1752
1753        let open_dir = || {
1754            open_dir_checked(
1755                &root,
1756                "foo",
1757                fio::Flags::FLAG_MAYBE_CREATE
1758                    | fio::PERM_READABLE
1759                    | fio::PERM_WRITABLE
1760                    | fio::Flags::PROTOCOL_DIRECTORY,
1761                Default::default(),
1762            )
1763        };
1764        let parent = Arc::new(open_dir().await);
1765
1766        let files = ["eenie", "meenie", "minie", "moe"];
1767        for file in &files {
1768            let file = open_file_checked(
1769                parent.as_ref(),
1770                file,
1771                fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1772                &Default::default(),
1773            )
1774            .await;
1775            close_file_checked(file).await;
1776        }
1777        let dirs = ["fee", "fi", "fo", "fum"];
1778        for dir in &dirs {
1779            let dir = open_dir_checked(
1780                parent.as_ref(),
1781                dir,
1782                fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
1783                Default::default(),
1784            )
1785            .await;
1786            close_dir_checked(dir).await;
1787        }
1788        {
1789            parent
1790                .create_symlink("symlink", b"target", None)
1791                .await
1792                .expect("FIDL call failed")
1793                .expect("create_symlink failed");
1794        }
1795
1796        let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
1797            let status = dir.rewind().await.expect("FIDL call failed");
1798            zx::Status::ok(status).expect("rewind failed");
1799            let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
1800            zx::Status::ok(status).expect("read_dirents failed");
1801            let mut entries = vec![];
1802            for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
1803                entries.push(res.expect("Failed to parse entry"));
1804            }
1805            entries
1806        };
1807
1808        let mut expected_entries =
1809            vec![DirEntry { name: ".".to_owned(), kind: DirentKind::Directory }];
1810        expected_entries.extend(
1811            files.iter().map(|&name| DirEntry { name: name.to_owned(), kind: DirentKind::File }),
1812        );
1813        expected_entries.extend(
1814            dirs.iter()
1815                .map(|&name| DirEntry { name: name.to_owned(), kind: DirentKind::Directory }),
1816        );
1817        expected_entries.push(DirEntry { name: "symlink".to_owned(), kind: DirentKind::Symlink });
1818        expected_entries.sort_unstable();
1819        assert_eq!(expected_entries, readdir(Arc::clone(&parent)).await);
1820
1821        // Remove an entry.
1822        parent
1823            .unlink(&expected_entries.pop().unwrap().name, &fio::UnlinkOptions::default())
1824            .await
1825            .expect("FIDL call failed")
1826            .expect("unlink failed");
1827
1828        assert_eq!(expected_entries, readdir(Arc::clone(&parent)).await);
1829
1830        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
1831        fixture.close().await;
1832    }
1833
1834    #[fuchsia::test]
1835    async fn test_readdir_multiple_calls() {
1836        let fixture = TestFixture::new().await;
1837        let root = fixture.root();
1838
1839        let parent = open_dir_checked(
1840            &root,
1841            "foo",
1842            fio::Flags::FLAG_MAYBE_CREATE
1843                | fio::PERM_READABLE
1844                | fio::PERM_WRITABLE
1845                | fio::Flags::PROTOCOL_DIRECTORY,
1846            Default::default(),
1847        )
1848        .await;
1849
1850        let files = ["a", "b"];
1851        for file in &files {
1852            let file = open_file_checked(
1853                &parent,
1854                file,
1855                fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
1856                &Default::default(),
1857            )
1858            .await;
1859            close_file_checked(file).await;
1860        }
1861
1862        // TODO(https://fxbug.dev/42177353): Magic number; can we get this from fuchsia.io?
1863        const DIRENT_SIZE: u64 = 10; // inode: u64, size: u8, kind: u8
1864        const BUFFER_SIZE: u64 = DIRENT_SIZE + 2; // Enough space for a 2-byte name.
1865
1866        let parse_entries = |buf| {
1867            let mut entries = vec![];
1868            for res in fuchsia_fs::directory::parse_dir_entries(buf) {
1869                entries.push(res.expect("Failed to parse entry"));
1870            }
1871            entries
1872        };
1873
1874        let expected_entries = vec![
1875            DirEntry { name: ".".to_owned(), kind: DirentKind::Directory },
1876            DirEntry { name: "a".to_owned(), kind: DirentKind::File },
1877        ];
1878        let (status, buf) = parent.read_dirents(2 * BUFFER_SIZE).await.expect("FIDL call failed");
1879        zx::Status::ok(status).expect("read_dirents failed");
1880        assert_eq!(expected_entries, parse_entries(&buf));
1881
1882        let expected_entries = vec![DirEntry { name: "b".to_owned(), kind: DirentKind::File }];
1883        let (status, buf) = parent.read_dirents(2 * BUFFER_SIZE).await.expect("FIDL call failed");
1884        zx::Status::ok(status).expect("read_dirents failed");
1885        assert_eq!(expected_entries, parse_entries(&buf));
1886
1887        // Subsequent calls yield nothing.
1888        let expected_entries: Vec<DirEntry> = vec![];
1889        let (status, buf) = parent.read_dirents(2 * BUFFER_SIZE).await.expect("FIDL call failed");
1890        zx::Status::ok(status).expect("read_dirents failed");
1891        assert_eq!(expected_entries, parse_entries(&buf));
1892
1893        close_dir_checked(parent).await;
1894        fixture.close().await;
1895    }
1896
1897    #[fuchsia::test]
1898    async fn test_set_large_extended_attribute_on_encrypted_directory() {
1899        let fixture = TestFixture::new().await;
1900        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
1901        let root = fixture.root();
1902        let open_dir = || {
1903            open_dir_checked(
1904                &root,
1905                "foo",
1906                fio::Flags::FLAG_MAYBE_CREATE
1907                    | fio::PERM_READABLE
1908                    | fio::PERM_WRITABLE
1909                    | fio::Flags::PROTOCOL_DIRECTORY,
1910                Default::default(),
1911            )
1912        };
1913
1914        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
1915        crypt
1916            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
1917            .expect("Failed to add wrapping key");
1918        parent
1919            .update_attributes(&fio::MutableNodeAttributes {
1920                wrapping_key_id: Some(WRAPPING_KEY_ID),
1921                ..Default::default()
1922            })
1923            .await
1924            .expect("FIDL call failed")
1925            .map_err(zx::ok)
1926            .expect("update_attributes failed");
1927        let dir = open_dir_checked(
1928            parent.as_ref(),
1929            "fee",
1930            fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
1931            Default::default(),
1932        )
1933        .await;
1934
1935        let xattr_name = b"xattr_name";
1936        let value_vec = vec![0x3; 300];
1937
1938        dir.set_extended_attribute(
1939            xattr_name,
1940            fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
1941            fio::SetExtendedAttributeMode::Set,
1942        )
1943        .await
1944        .expect("Failed to make FIDL call")
1945        .expect("Failed to set xattr with create");
1946
1947        let subdir = open_dir_checked(
1948            &dir,
1949            "fo",
1950            fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
1951            Default::default(),
1952        )
1953        .await;
1954        close_dir_checked(dir).await;
1955        close_dir_checked(subdir).await;
1956        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
1957        let device = fixture.close().await;
1958        let new_fixture = TestFixture::new_with_device(device).await;
1959        let root = new_fixture.root();
1960        let open_dir = || {
1961            open_dir_checked(
1962                &root,
1963                "foo",
1964                fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
1965                Default::default(),
1966            )
1967        };
1968        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
1969
1970        let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
1971            let status = dir.rewind().await.expect("FIDL call failed");
1972            zx::Status::ok(status).expect("rewind failed");
1973            let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
1974            zx::Status::ok(status).expect("read_dirents failed");
1975            let mut entries = vec![];
1976            for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
1977                entries.push(res.expect("Failed to parse entry"));
1978            }
1979            entries
1980        };
1981
1982        let encrypted_entries = readdir(Arc::clone(&parent)).await;
1983        let mut encrypted_name = String::new();
1984        for entry in encrypted_entries {
1985            if entry.name == ".".to_owned() {
1986                continue;
1987            } else {
1988                assert!(entry.name.len() >= FSCRYPT_PADDING);
1989                encrypted_name = entry.name;
1990                assert!(entry.kind == DirentKind::Directory)
1991            }
1992        }
1993
1994        let encrypted_dir = Arc::new(
1995            open_dir_checked(
1996                parent.as_ref(),
1997                &encrypted_name,
1998                fio::Flags::PROTOCOL_DIRECTORY | fio::PERM_READABLE,
1999                Default::default(),
2000            )
2001            .await,
2002        );
2003
2004        assert_eq!(
2005            encrypted_dir
2006                .get_extended_attribute(xattr_name)
2007                .await
2008                .expect("Failed to make FIDL call")
2009                .expect("Failed to get extended attribute"),
2010            fio::ExtendedAttributeValue::Bytes(value_vec)
2011        );
2012
2013        let encrypted_subdir_entries = readdir(Arc::clone(&encrypted_dir)).await;
2014        for entry in encrypted_subdir_entries {
2015            if entry.name == ".".to_owned() {
2016                continue;
2017            } else {
2018                assert!(entry.name.len() >= FSCRYPT_PADDING);
2019                assert!(entry.kind == DirentKind::Directory)
2020            }
2021        }
2022        close_dir_checked(Arc::try_unwrap(encrypted_dir).unwrap()).await;
2023        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2024        new_fixture.close().await;
2025    }
2026
2027    #[fuchsia::test]
2028    async fn test_set_large_extended_attribute_on_encrypted_file() {
2029        let fixture = TestFixture::new().await;
2030        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2031        let root = fixture.root();
2032        let open_dir = || {
2033            open_dir_checked(
2034                &root,
2035                "foo",
2036                fio::Flags::FLAG_MAYBE_CREATE
2037                    | fio::PERM_READABLE
2038                    | fio::PERM_WRITABLE
2039                    | fio::Flags::PROTOCOL_DIRECTORY,
2040                Default::default(),
2041            )
2042        };
2043
2044        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2045        crypt
2046            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2047            .expect("Failed to add wrapping key");
2048        parent
2049            .update_attributes(&fio::MutableNodeAttributes {
2050                wrapping_key_id: Some(WRAPPING_KEY_ID),
2051                ..Default::default()
2052            })
2053            .await
2054            .expect("FIDL call failed")
2055            .map_err(zx::ok)
2056            .expect("update_attributes failed");
2057        let file = open_file_checked(
2058            parent.as_ref(),
2059            "fee",
2060            fio::Flags::FLAG_MAYBE_CREATE
2061                | fio::PERM_READABLE
2062                | fio::PERM_WRITABLE
2063                | fio::Flags::PROTOCOL_FILE,
2064            &Default::default(),
2065        )
2066        .await;
2067
2068        let buf = vec![0xaa as u8; 512];
2069        file::write(&file, buf.as_slice()).await.expect("write failed");
2070
2071        let xattr_name = b"xattr_name";
2072        let value_vec = vec![0x3; 300];
2073
2074        file.set_extended_attribute(
2075            xattr_name,
2076            fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
2077            fio::SetExtendedAttributeMode::Set,
2078        )
2079        .await
2080        .expect("Failed to make FIDL call")
2081        .expect("Failed to set xattr with create");
2082
2083        close_file_checked(file).await;
2084        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2085        let device = fixture.close().await;
2086        let new_fixture = TestFixture::new_with_device(device).await;
2087        let crypt: Arc<CryptBase> = new_fixture.crypt().unwrap();
2088        let root = new_fixture.root();
2089        let open_dir = || {
2090            open_dir_checked(
2091                &root,
2092                "foo",
2093                fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2094                Default::default(),
2095            )
2096        };
2097        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2098
2099        let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2100            let status = dir.rewind().await.expect("FIDL call failed");
2101            zx::Status::ok(status).expect("rewind failed");
2102            let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2103            zx::Status::ok(status).expect("read_dirents failed");
2104            let mut entries = vec![];
2105            for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2106                entries.push(res.expect("Failed to parse entry"));
2107            }
2108            entries
2109        };
2110
2111        let encrypted_entries = readdir(Arc::clone(&parent)).await;
2112        let mut encrypted_name = String::new();
2113        for entry in encrypted_entries {
2114            if entry.name == ".".to_owned() {
2115                continue;
2116            } else {
2117                assert!(entry.name.len() >= FSCRYPT_PADDING);
2118                encrypted_name = entry.name;
2119                assert!(entry.kind == DirentKind::File)
2120            }
2121        }
2122
2123        let encrypted_file = Arc::new(
2124            open_file_checked(
2125                parent.as_ref(),
2126                &encrypted_name,
2127                fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE,
2128                &Default::default(),
2129            )
2130            .await,
2131        );
2132
2133        assert_eq!(
2134            encrypted_file
2135                .get_extended_attribute(xattr_name)
2136                .await
2137                .expect("Failed to make FIDL call")
2138                .expect("Failed to get extended attribute"),
2139            fio::ExtendedAttributeValue::Bytes(value_vec)
2140        );
2141
2142        close_file_checked(Arc::try_unwrap(encrypted_file).unwrap()).await;
2143
2144        crypt
2145            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2146            .expect("Failed to add wrapping key");
2147
2148        let file = Arc::new(
2149            open_file_checked(
2150                parent.as_ref(),
2151                "fee",
2152                fio::Flags::PROTOCOL_FILE | fio::PERM_READABLE,
2153                &Default::default(),
2154            )
2155            .await,
2156        );
2157
2158        let rbuf = file::read(&file).await.expect("read failed");
2159        assert_eq!(rbuf, buf);
2160
2161        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2162        new_fixture.close().await;
2163    }
2164
2165    #[fuchsia::test]
2166    async fn test_encrypt_directory_in_unencrypted_volume() {
2167        let fixture = TestFixture::new_unencrypted().await;
2168        let root = fixture.root();
2169        let open_dir = || {
2170            open_dir_checked(
2171                &root,
2172                "foo",
2173                fio::Flags::FLAG_MAYBE_CREATE
2174                    | fio::PERM_READABLE
2175                    | fio::PERM_WRITABLE
2176                    | fio::Flags::PROTOCOL_DIRECTORY,
2177                Default::default(),
2178            )
2179        };
2180
2181        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2182        let _ = parent
2183            .update_attributes(&fio::MutableNodeAttributes {
2184                wrapping_key_id: Some(WRAPPING_KEY_ID),
2185                ..Default::default()
2186            })
2187            .await
2188            .expect("FIDL call failed")
2189            .map_err(zx::ok)
2190            .expect_err("encrypting a dir in an unencrypted volume should fail");
2191        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2192        fixture.close().await;
2193    }
2194
2195    #[fuchsia::test]
2196    async fn test_encrypt_directory_with_large_extended_attribute() {
2197        let fixture = TestFixture::new().await;
2198        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2199        let root = fixture.root();
2200        let open_dir = || {
2201            open_dir_checked(
2202                &root,
2203                "foo",
2204                fio::Flags::FLAG_MAYBE_CREATE
2205                    | fio::PERM_READABLE
2206                    | fio::PERM_WRITABLE
2207                    | fio::Flags::PROTOCOL_DIRECTORY,
2208                Default::default(),
2209            )
2210        };
2211
2212        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2213
2214        let xattr_name = b"xattr_name";
2215        let value_vec = vec![0x3; 300];
2216        parent
2217            .set_extended_attribute(
2218                xattr_name,
2219                fio::ExtendedAttributeValue::Bytes(value_vec.clone()),
2220                fio::SetExtendedAttributeMode::Set,
2221            )
2222            .await
2223            .expect("Failed to make FIDL call")
2224            .expect("Failed to set xattr with create");
2225
2226        crypt
2227            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2228            .expect("Failed to add wrapping key");
2229        parent
2230            .update_attributes(&fio::MutableNodeAttributes {
2231                wrapping_key_id: Some(WRAPPING_KEY_ID),
2232                ..Default::default()
2233            })
2234            .await
2235            .expect("FIDL call failed")
2236            .map_err(zx::ok)
2237            .expect("update_attributes failed");
2238        let dir = open_dir_checked(
2239            parent.as_ref(),
2240            "fee",
2241            fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2242            Default::default(),
2243        )
2244        .await;
2245
2246        let subdir = open_dir_checked(
2247            &dir,
2248            "fo",
2249            fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
2250            Default::default(),
2251        )
2252        .await;
2253        close_dir_checked(dir).await;
2254        close_dir_checked(subdir).await;
2255        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2256        let device = fixture.close().await;
2257        let new_fixture = TestFixture::new_with_device(device).await;
2258        let root = new_fixture.root();
2259        let open_dir = || {
2260            open_dir_checked(
2261                &root,
2262                "foo",
2263                fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2264                Default::default(),
2265            )
2266        };
2267        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2268
2269        assert_eq!(
2270            parent
2271                .get_extended_attribute(xattr_name)
2272                .await
2273                .expect("Failed to make FIDL call")
2274                .expect("Failed to get extended attribute"),
2275            fio::ExtendedAttributeValue::Bytes(value_vec)
2276        );
2277
2278        let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2279            let status = dir.rewind().await.expect("FIDL call failed");
2280            zx::Status::ok(status).expect("rewind failed");
2281            let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2282            zx::Status::ok(status).expect("read_dirents failed");
2283            let mut entries = vec![];
2284            for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2285                entries.push(res.expect("Failed to parse entry"));
2286            }
2287            entries
2288        };
2289
2290        let encrypted_entries = readdir(Arc::clone(&parent)).await;
2291        let mut encrypted_name = None;
2292        for entry in encrypted_entries {
2293            if &entry.name == "." {
2294                continue;
2295            } else {
2296                assert!(entry.name.len() >= FSCRYPT_PADDING);
2297                assert!(encrypted_name.replace(entry.name).is_none());
2298                assert!(entry.kind == DirentKind::Directory)
2299            }
2300        }
2301
2302        let encrypted_dir = Arc::new(
2303            open_dir_checked(
2304                parent.as_ref(),
2305                &encrypted_name.as_ref().unwrap(),
2306                fio::Flags::PROTOCOL_DIRECTORY,
2307                Default::default(),
2308            )
2309            .await,
2310        );
2311
2312        let encrypted_subdir_entries = readdir(Arc::clone(&encrypted_dir)).await;
2313        for entry in encrypted_subdir_entries {
2314            if &entry.name == "." {
2315                continue;
2316            } else {
2317                assert!(entry.name.len() >= FSCRYPT_PADDING);
2318                assert!(entry.kind == DirentKind::Directory)
2319            }
2320        }
2321        close_dir_checked(Arc::try_unwrap(encrypted_dir).unwrap()).await;
2322        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2323        new_fixture.close().await;
2324    }
2325
2326    #[fuchsia::test]
2327    async fn test_unlock_directory_during_readdir() {
2328        let fixture = TestFixture::new().await;
2329        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2330        let root = fixture.root();
2331        let open_dir = || {
2332            open_dir_checked(
2333                &root,
2334                "foo",
2335                fio::Flags::FLAG_MAYBE_CREATE
2336                    | fio::PERM_READABLE
2337                    | fio::PERM_WRITABLE
2338                    | fio::Flags::PROTOCOL_DIRECTORY,
2339                Default::default(),
2340            )
2341        };
2342
2343        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2344        crypt
2345            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2346            .expect("Failed to add wrapping key");
2347        parent
2348            .update_attributes(&fio::MutableNodeAttributes {
2349                wrapping_key_id: Some(WRAPPING_KEY_ID),
2350                ..Default::default()
2351            })
2352            .await
2353            .expect("FIDL call failed")
2354            .map_err(zx::ok)
2355            .expect("update_attributes failed");
2356
2357        // Need enough entries such that multiple read_dirents calls are required to drain all the
2358        // entries.
2359        for i in 0..300 {
2360            let dir = open_dir_checked(
2361                parent.as_ref(),
2362                &format!("plaintext_{}", i),
2363                fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2364                Default::default(),
2365            )
2366            .await;
2367            close_dir_checked(dir).await;
2368        }
2369
2370        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2371        let device = fixture.close().await;
2372        let new_fixture = TestFixture::new_with_device(device).await;
2373        let crypt: Arc<CryptBase> = new_fixture.crypt().unwrap();
2374        let root = new_fixture.root();
2375        let open_dir = || {
2376            open_dir_checked(
2377                &root,
2378                "foo",
2379                fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2380                Default::default(),
2381            )
2382        };
2383        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2384
2385        let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2386            let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2387            zx::Status::ok(status).expect("read_dirents failed");
2388            let mut entries = vec![];
2389            for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2390                entries.push(res.expect("Failed to parse entry"));
2391            }
2392            entries
2393        };
2394
2395        let encrypted_entries = readdir(Arc::clone(&parent)).await;
2396        for entry in encrypted_entries {
2397            if entry.name == ".".to_owned() {
2398                continue;
2399            } else {
2400                assert!(entry.name.len() >= FSCRYPT_PADDING);
2401                assert!(!entry.name.starts_with("plaintext_"), "{entry:?} isn't encrypted!");
2402                assert!(entry.kind == DirentKind::Directory)
2403            }
2404        }
2405        crypt
2406            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2407            .expect("Failed to add wrapping key");
2408        let unencrypted_entries = readdir(Arc::clone(&parent)).await;
2409        for entry in unencrypted_entries {
2410            if entry.name == ".".to_owned() {
2411                continue;
2412            } else {
2413                assert!(entry.name.starts_with("plaintext_"), "{entry:?} is still encrypted!");
2414                assert!(entry.kind == DirentKind::Directory)
2415            }
2416        }
2417
2418        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2419        new_fixture.close().await;
2420    }
2421
2422    #[fuchsia::test]
2423    async fn test_readdir_locked_directory() {
2424        let fixture = TestFixture::new().await;
2425        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2426        let root = fixture.root();
2427        let open_dir = || {
2428            open_dir_checked(
2429                &root,
2430                "foo",
2431                fio::Flags::FLAG_MAYBE_CREATE
2432                    | fio::PERM_READABLE
2433                    | fio::PERM_WRITABLE
2434                    | fio::Flags::PROTOCOL_DIRECTORY,
2435                Default::default(),
2436            )
2437        };
2438
2439        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2440        crypt
2441            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2442            .expect("Failed to add wrapping key");
2443        parent
2444            .update_attributes(&fio::MutableNodeAttributes {
2445                wrapping_key_id: Some(WRAPPING_KEY_ID),
2446                ..Default::default()
2447            })
2448            .await
2449            .expect("FIDL call failed")
2450            .map_err(zx::ok)
2451            .expect("update_attributes failed");
2452        let dir = open_dir_checked(
2453            parent.as_ref(),
2454            "fee",
2455            fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2456            Default::default(),
2457        )
2458        .await;
2459
2460        let subdir = open_dir_checked(
2461            &dir,
2462            "fo",
2463            fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_DIRECTORY,
2464            Default::default(),
2465        )
2466        .await;
2467        close_dir_checked(dir).await;
2468        close_dir_checked(subdir).await;
2469
2470        let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2471            let status = dir.rewind().await.expect("FIDL call failed");
2472            zx::Status::ok(status).expect("rewind failed");
2473            let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2474            zx::Status::ok(status).expect("read_dirents failed");
2475            let mut entries = vec![];
2476            for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2477                entries.push(res.expect("Failed to parse entry"));
2478            }
2479            entries
2480        };
2481
2482        let mut expected_entries =
2483            vec![DirEntry { name: ".".to_owned(), kind: DirentKind::Directory }];
2484
2485        expected_entries.push(DirEntry { name: "fee".to_owned(), kind: DirentKind::Directory });
2486        expected_entries.sort_unstable();
2487        assert_eq!(expected_entries, readdir(Arc::clone(&parent)).await);
2488
2489        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2490        let device = fixture.close().await;
2491        let new_fixture = TestFixture::new_with_device(device).await;
2492        let root = new_fixture.root();
2493        let open_dir = || {
2494            open_dir_checked(
2495                &root,
2496                "foo",
2497                fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
2498                Default::default(),
2499            )
2500        };
2501        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
2502
2503        let encrypted_entries = readdir(Arc::clone(&parent)).await;
2504        let mut encrypted_name = String::new();
2505        for entry in encrypted_entries {
2506            if entry.name == ".".to_owned() {
2507                continue;
2508            } else {
2509                assert!(entry.name.len() >= FSCRYPT_PADDING);
2510                encrypted_name = entry.name;
2511                assert!(entry.kind == DirentKind::Directory)
2512            }
2513        }
2514
2515        let encrypted_dir = Arc::new(
2516            open_dir_checked(
2517                parent.as_ref(),
2518                &encrypted_name,
2519                fio::Flags::PROTOCOL_DIRECTORY,
2520                Default::default(),
2521            )
2522            .await,
2523        );
2524
2525        let encrypted_subdir_entries = readdir(Arc::clone(&encrypted_dir)).await;
2526        for entry in encrypted_subdir_entries {
2527            if entry.name == ".".to_owned() {
2528                continue;
2529            } else {
2530                assert!(entry.name.len() >= FSCRYPT_PADDING);
2531                assert!(entry.kind == DirentKind::Directory)
2532            }
2533        }
2534        close_dir_checked(Arc::try_unwrap(encrypted_dir).unwrap()).await;
2535        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
2536        new_fixture.close().await;
2537    }
2538
2539    #[fuchsia::test]
2540    async fn test_link_into_locked_directory_fails() {
2541        let fixture = TestFixture::new().await;
2542        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2543        let root = fixture.root();
2544        let open_dir_1 = || {
2545            open_dir_checked(
2546                &root,
2547                "foo",
2548                fio::Flags::FLAG_MAYBE_CREATE
2549                    | fio::PERM_READABLE
2550                    | fio::PERM_WRITABLE
2551                    | fio::Flags::PROTOCOL_DIRECTORY,
2552                Default::default(),
2553            )
2554        };
2555
2556        let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2557
2558        let open_dir_2 = || {
2559            open_dir_checked(
2560                &root,
2561                "foo_2",
2562                fio::Flags::FLAG_MAYBE_CREATE
2563                    | fio::PERM_READABLE
2564                    | fio::PERM_WRITABLE
2565                    | fio::Flags::PROTOCOL_DIRECTORY,
2566                Default::default(),
2567            )
2568        };
2569        let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2570
2571        crypt
2572            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2573            .expect("Failed to add wrapping key");
2574        parent_1
2575            .update_attributes(&fio::MutableNodeAttributes {
2576                wrapping_key_id: Some(WRAPPING_KEY_ID),
2577                ..Default::default()
2578            })
2579            .await
2580            .expect("FIDL call failed")
2581            .map_err(zx::ok)
2582            .expect("update_attributes failed");
2583        parent_2
2584            .update_attributes(&fio::MutableNodeAttributes {
2585                wrapping_key_id: Some(WRAPPING_KEY_ID),
2586                ..Default::default()
2587            })
2588            .await
2589            .expect("FIDL call failed")
2590            .map_err(zx::ok)
2591            .expect("update_attributes failed");
2592        let file = open_file_checked(
2593            parent_1.as_ref(),
2594            "fee",
2595            fio::Flags::FLAG_MAYBE_CREATE,
2596            &Default::default(),
2597        )
2598        .await;
2599
2600        close_file_checked(file).await;
2601        close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2602        close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2603
2604        let device = fixture.close().await;
2605        let new_fixture = TestFixture::new_with_device(device).await;
2606        let root = new_fixture.root();
2607        let open_dir_1 = || {
2608            open_dir_checked(
2609                &root,
2610                "foo",
2611                fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2612                Default::default(),
2613            )
2614        };
2615        let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2616
2617        let open_dir_2 = || {
2618            open_dir_checked(
2619                &root,
2620                "foo_2",
2621                fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2622                Default::default(),
2623            )
2624        };
2625        let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2626
2627        let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2628            let status = dir.rewind().await.expect("FIDL call failed");
2629            zx::Status::ok(status).expect("rewind failed");
2630            let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2631            zx::Status::ok(status).expect("read_dirents failed");
2632            let mut entries = vec![];
2633            for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2634                entries.push(res.expect("Failed to parse entry"));
2635            }
2636            entries
2637        };
2638
2639        let encrypted_entries = readdir(Arc::clone(&parent_1)).await;
2640        let mut encrypted_name = String::new();
2641        for entry in encrypted_entries {
2642            if entry.name == ".".to_owned() {
2643                continue;
2644            } else {
2645                assert!(entry.name.len() >= FSCRYPT_PADDING);
2646                encrypted_name = entry.name;
2647                assert!(entry.kind == DirentKind::File)
2648            }
2649        }
2650
2651        let (status, parent_2_token) = parent_2.get_token().await.expect("get token failed");
2652        zx::Status::ok(status).unwrap();
2653
2654        assert_eq!(
2655            parent_1
2656                .link(&encrypted_name, parent_2_token.unwrap().into(), "file_2")
2657                .await
2658                .expect("FIDL transport error"),
2659            zx::Status::ACCESS_DENIED.into_raw()
2660        );
2661
2662        close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2663        close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2664        new_fixture.close().await;
2665    }
2666
2667    #[fuchsia::test]
2668    async fn test_rename_in_locked_directory_fails() {
2669        let fixture = TestFixture::new().await;
2670        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2671        let root = fixture.root();
2672        let open_dir_1 = || {
2673            open_dir_checked(
2674                &root,
2675                "foo",
2676                fio::Flags::FLAG_MAYBE_CREATE
2677                    | fio::PERM_READABLE
2678                    | fio::PERM_WRITABLE
2679                    | fio::Flags::PROTOCOL_DIRECTORY,
2680                Default::default(),
2681            )
2682        };
2683
2684        let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2685
2686        let open_dir_2 = || {
2687            open_dir_checked(
2688                &root,
2689                "foo_2",
2690                fio::Flags::FLAG_MAYBE_CREATE
2691                    | fio::PERM_READABLE
2692                    | fio::PERM_WRITABLE
2693                    | fio::Flags::PROTOCOL_DIRECTORY,
2694                Default::default(),
2695            )
2696        };
2697        let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2698
2699        crypt
2700            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2701            .expect("Failed to add wrapping key");
2702        parent_1
2703            .update_attributes(&fio::MutableNodeAttributes {
2704                wrapping_key_id: Some(WRAPPING_KEY_ID),
2705                ..Default::default()
2706            })
2707            .await
2708            .expect("FIDL call failed")
2709            .map_err(zx::ok)
2710            .expect("update_attributes failed");
2711        parent_2
2712            .update_attributes(&fio::MutableNodeAttributes {
2713                wrapping_key_id: Some(WRAPPING_KEY_ID),
2714                ..Default::default()
2715            })
2716            .await
2717            .expect("FIDL call failed")
2718            .map_err(zx::ok)
2719            .expect("update_attributes failed");
2720        let file = open_file_checked(
2721            parent_1.as_ref(),
2722            "fee",
2723            fio::Flags::FLAG_MAYBE_CREATE,
2724            &Default::default(),
2725        )
2726        .await;
2727
2728        close_file_checked(file).await;
2729        close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2730        close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2731
2732        let device = fixture.close().await;
2733        let new_fixture = TestFixture::new_with_device(device).await;
2734        let root = new_fixture.root();
2735        let open_dir_1 = || {
2736            open_dir_checked(
2737                &root,
2738                "foo",
2739                fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2740                Default::default(),
2741            )
2742        };
2743        let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2744
2745        let open_dir_2 = || {
2746            open_dir_checked(
2747                &root,
2748                "foo_2",
2749                fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
2750                Default::default(),
2751            )
2752        };
2753        let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2754
2755        let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
2756            let status = dir.rewind().await.expect("FIDL call failed");
2757            zx::Status::ok(status).expect("rewind failed");
2758            let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
2759            zx::Status::ok(status).expect("read_dirents failed");
2760            let mut entries = vec![];
2761            for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
2762                entries.push(res.expect("Failed to parse entry"));
2763            }
2764            entries
2765        };
2766
2767        let encrypted_entries = readdir(Arc::clone(&parent_1)).await;
2768        let mut encrypted_name = String::new();
2769        for entry in encrypted_entries {
2770            if entry.name == ".".to_owned() {
2771                continue;
2772            } else {
2773                assert!(entry.name.len() >= FSCRYPT_PADDING);
2774                encrypted_name = entry.name;
2775                assert!(entry.kind == DirentKind::File)
2776            }
2777        }
2778
2779        let (status, parent_2_token) = parent_2.get_token().await.expect("get token failed");
2780        zx::Status::ok(status).unwrap();
2781
2782        // Rename cross-directory when locked should fail.
2783        assert_eq!(
2784            parent_1
2785                .rename(&encrypted_name, parent_2_token.unwrap().into(), "file_2")
2786                .await
2787                .expect("FIDL transport error"),
2788            Err(zx::Status::ACCESS_DENIED.into_raw())
2789        );
2790
2791        let (status, parent_1_token) = parent_1.get_token().await.expect("get token failed");
2792        zx::Status::ok(status).unwrap();
2793
2794        // Rename same-directory when locked should fail.
2795        assert_eq!(
2796            parent_1
2797                .rename(&encrypted_name, parent_1_token.unwrap().into(), "file_2")
2798                .await
2799                .expect("FIDL transport error"),
2800            Err(zx::Status::ACCESS_DENIED.into_raw())
2801        );
2802
2803        close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2804        close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2805        new_fixture.close().await;
2806    }
2807
2808    #[fuchsia::test]
2809    async fn test_link_encrypted_file_into_directory_encrypted_with_different_key_fails() {
2810        let fixture = TestFixture::new().await;
2811        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2812        let root = fixture.root();
2813        let open_dir_1 = || {
2814            open_dir_checked(
2815                &root,
2816                "foo",
2817                fio::Flags::FLAG_MAYBE_CREATE
2818                    | fio::PERM_READABLE
2819                    | fio::PERM_WRITABLE
2820                    | fio::Flags::PROTOCOL_DIRECTORY,
2821                Default::default(),
2822            )
2823        };
2824
2825        let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2826
2827        let open_dir_2 = || {
2828            open_dir_checked(
2829                &root,
2830                "foo_2",
2831                fio::Flags::FLAG_MAYBE_CREATE
2832                    | fio::PERM_READABLE
2833                    | fio::PERM_WRITABLE
2834                    | fio::Flags::PROTOCOL_DIRECTORY,
2835                Default::default(),
2836            )
2837        };
2838        let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2839
2840        crypt
2841            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2842            .expect("Failed to add wrapping key");
2843
2844        const WRAPPING_KEY_ID_2: WrappingKeyId = u128::to_le_bytes(3);
2845        crypt
2846            .add_wrapping_key(WRAPPING_KEY_ID_2, [2; 32].into())
2847            .expect("Failed to add wrapping key");
2848
2849        parent_1
2850            .update_attributes(&fio::MutableNodeAttributes {
2851                wrapping_key_id: Some(WRAPPING_KEY_ID),
2852                ..Default::default()
2853            })
2854            .await
2855            .expect("FIDL call failed")
2856            .map_err(zx::ok)
2857            .expect("update_attributes failed");
2858        parent_2
2859            .update_attributes(&fio::MutableNodeAttributes {
2860                wrapping_key_id: Some(WRAPPING_KEY_ID_2),
2861                ..Default::default()
2862            })
2863            .await
2864            .expect("FIDL call failed")
2865            .map_err(zx::ok)
2866            .expect("update_attributes failed");
2867        let file = open_file_checked(
2868            parent_1.as_ref(),
2869            "fee",
2870            fio::Flags::FLAG_MAYBE_CREATE,
2871            &Default::default(),
2872        )
2873        .await;
2874
2875        close_file_checked(file).await;
2876
2877        let (status, parent_2_token) = parent_2.get_token().await.expect("get token failed");
2878        zx::Status::ok(status).unwrap();
2879
2880        assert_eq!(
2881            parent_1
2882                .link("fee", parent_2_token.unwrap().into(), "file_2")
2883                .await
2884                .expect("FIDL transport error"),
2885            zx::Status::BAD_STATE.into_raw()
2886        );
2887        close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2888        close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2889        fixture.close().await;
2890    }
2891
2892    #[fuchsia::test]
2893    async fn test_link_unencrypted_file_into_encrypted_directory_fails() {
2894        let fixture = TestFixture::new().await;
2895        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2896        let root = fixture.root();
2897        let open_dir_1 = || {
2898            open_dir_checked(
2899                &root,
2900                "foo",
2901                fio::Flags::FLAG_MAYBE_CREATE
2902                    | fio::PERM_READABLE
2903                    | fio::PERM_WRITABLE
2904                    | fio::Flags::PROTOCOL_DIRECTORY,
2905                Default::default(),
2906            )
2907        };
2908
2909        let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2910
2911        let open_dir_2 = || {
2912            open_dir_checked(
2913                &root,
2914                "foo_2",
2915                fio::Flags::FLAG_MAYBE_CREATE
2916                    | fio::PERM_READABLE
2917                    | fio::PERM_WRITABLE
2918                    | fio::Flags::PROTOCOL_DIRECTORY,
2919                Default::default(),
2920            )
2921        };
2922        let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2923
2924        crypt
2925            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2926            .expect("Failed to add wrapping key");
2927
2928        parent_1
2929            .update_attributes(&fio::MutableNodeAttributes {
2930                wrapping_key_id: Some(WRAPPING_KEY_ID),
2931                ..Default::default()
2932            })
2933            .await
2934            .expect("FIDL call failed")
2935            .map_err(zx::ok)
2936            .expect("update_attributes failed");
2937
2938        let file = open_file_checked(
2939            parent_2.as_ref(),
2940            "fee",
2941            fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
2942            &Default::default(),
2943        )
2944        .await;
2945
2946        close_file_checked(file).await;
2947
2948        let (status, parent_1_token) = parent_1.get_token().await.expect("get token failed");
2949        zx::Status::ok(status).unwrap();
2950
2951        assert_eq!(
2952            parent_2
2953                .link("fee", parent_1_token.unwrap().into(), "file")
2954                .await
2955                .expect("FIDL transport error"),
2956            zx::Status::BAD_STATE.into_raw()
2957        );
2958        close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
2959        close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
2960        fixture.close().await;
2961    }
2962
2963    #[fuchsia::test]
2964    async fn test_link_locked_directory_into_unencrypted_dir() {
2965        let fixture = TestFixture::new().await;
2966        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
2967        let root = fixture.root();
2968        let open_dir_1 = || {
2969            open_dir_checked(
2970                &root,
2971                "foo",
2972                fio::Flags::FLAG_MAYBE_CREATE
2973                    | fio::PERM_READABLE
2974                    | fio::PERM_WRITABLE
2975                    | fio::Flags::PROTOCOL_DIRECTORY,
2976                Default::default(),
2977            )
2978        };
2979
2980        let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
2981
2982        let open_dir_2 = || {
2983            open_dir_checked(
2984                &root,
2985                "foo_2",
2986                fio::Flags::FLAG_MAYBE_CREATE
2987                    | fio::PERM_READABLE
2988                    | fio::PERM_WRITABLE
2989                    | fio::Flags::PROTOCOL_DIRECTORY,
2990                Default::default(),
2991            )
2992        };
2993        let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
2994
2995        crypt
2996            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2997            .expect("Failed to add wrapping key");
2998        parent_1
2999            .update_attributes(&fio::MutableNodeAttributes {
3000                wrapping_key_id: Some(WRAPPING_KEY_ID),
3001                ..Default::default()
3002            })
3003            .await
3004            .expect("FIDL call failed")
3005            .map_err(zx::ok)
3006            .expect("update_attributes failed");
3007        let file = open_file_checked(
3008            parent_1.as_ref(),
3009            "fee",
3010            fio::Flags::FLAG_MAYBE_CREATE
3011                | fio::PERM_READABLE
3012                | fio::PERM_WRITABLE
3013                | fio::Flags::PROTOCOL_FILE,
3014            &Default::default(),
3015        )
3016        .await;
3017        let _ = file
3018            .write(&[8; 8192])
3019            .await
3020            .expect("FIDL call failed")
3021            .map_err(zx::Status::from_raw)
3022            .expect("write failed");
3023
3024        close_file_checked(file).await;
3025        close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
3026        close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
3027
3028        let device = fixture.close().await;
3029        let new_fixture = TestFixture::new_with_device(device).await;
3030        let root = new_fixture.root();
3031        let open_dir_1 = || {
3032            open_dir_checked(
3033                &root,
3034                "foo",
3035                fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3036                Default::default(),
3037            )
3038        };
3039        let parent_1: Arc<fio::DirectoryProxy> = Arc::new(open_dir_1().await);
3040
3041        let open_dir_2 = || {
3042            open_dir_checked(
3043                &root,
3044                "foo_2",
3045                fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3046                Default::default(),
3047            )
3048        };
3049        let parent_2: Arc<fio::DirectoryProxy> = Arc::new(open_dir_2().await);
3050
3051        let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
3052            let status = dir.rewind().await.expect("FIDL call failed");
3053            zx::Status::ok(status).expect("rewind failed");
3054            let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3055            zx::Status::ok(status).expect("read_dirents failed");
3056            let mut entries = vec![];
3057            for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3058                entries.push(res.expect("Failed to parse entry"));
3059            }
3060            entries
3061        };
3062
3063        let encrypted_entries = readdir(Arc::clone(&parent_1)).await;
3064        let mut encrypted_name = String::new();
3065        for entry in encrypted_entries {
3066            if entry.name == ".".to_owned() {
3067                continue;
3068            } else {
3069                assert!(entry.name.len() >= FSCRYPT_PADDING);
3070                encrypted_name = entry.name;
3071                assert!(entry.kind == DirentKind::File)
3072            }
3073        }
3074
3075        let (status, parent_2_token) = parent_2.get_token().await.expect("get token failed");
3076        zx::Status::ok(status).unwrap();
3077
3078        assert_eq!(
3079            parent_1
3080                .link(&encrypted_name, parent_2_token.unwrap().into(), "file_2")
3081                .await
3082                .expect("FIDL transport error"),
3083            zx::Status::OK.into_raw()
3084        );
3085
3086        let file =
3087            open_file_checked(parent_2.as_ref(), "file_2", fio::PERM_READABLE, &Default::default())
3088                .await;
3089        let (mutable_attributes, _immutable_attributes) = file
3090            .get_attributes(
3091                fio::NodeAttributesQuery::CONTENT_SIZE
3092                    | fio::NodeAttributesQuery::STORAGE_SIZE
3093                    | fio::NodeAttributesQuery::LINK_COUNT
3094                    | fio::NodeAttributesQuery::MODIFICATION_TIME
3095                    | fio::NodeAttributesQuery::CHANGE_TIME
3096                    | fio::NodeAttributesQuery::WRAPPING_KEY_ID,
3097            )
3098            .await
3099            .expect("FIDL call failed")
3100            .map_err(zx::Status::from_raw)
3101            .expect("get_attributes failed");
3102        assert_eq!(mutable_attributes.wrapping_key_id, Some(WRAPPING_KEY_ID));
3103        assert_eq!(
3104            file.read(fio::MAX_BUF)
3105                .await
3106                .expect("FIDL call failed")
3107                .expect_err("reading an encrypted file should fail"),
3108            zx::Status::BAD_STATE.into_raw()
3109        );
3110
3111        close_dir_checked(Arc::try_unwrap(parent_1).unwrap()).await;
3112        close_dir_checked(Arc::try_unwrap(parent_2).unwrap()).await;
3113        new_fixture.close().await;
3114    }
3115
3116    #[fuchsia::test]
3117    async fn test_encrypted_filename_does_not_have_slashes() {
3118        let fixture = TestFixture::new().await;
3119        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3120        let root = fixture.root();
3121        let open_dir = || {
3122            open_dir_checked(
3123                &root,
3124                "foo",
3125                fio::Flags::FLAG_MAYBE_CREATE
3126                    | fio::PERM_READABLE
3127                    | fio::PERM_WRITABLE
3128                    | fio::Flags::PROTOCOL_DIRECTORY,
3129                Default::default(),
3130            )
3131        };
3132
3133        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3134        crypt
3135            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3136            .expect("Failed to add wrapping key");
3137        parent
3138            .update_attributes(&fio::MutableNodeAttributes {
3139                wrapping_key_id: Some(WRAPPING_KEY_ID),
3140                ..Default::default()
3141            })
3142            .await
3143            .expect("FIDL call failed")
3144            .map_err(zx::ok)
3145            .expect("update_attributes failed");
3146        const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
3147        for _ in 0..100 {
3148            let one_char = || CHARSET[rand::random_range(0..CHARSET.len())] as char;
3149            let filename: String = std::iter::repeat_with(one_char).take(100).collect();
3150            let dir = open_dir_checked(
3151                parent.as_ref(),
3152                &filename,
3153                fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3154                Default::default(),
3155            )
3156            .await;
3157            close_dir_checked(dir).await;
3158        }
3159
3160        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3161        let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
3162            let status = dir.rewind().await.expect("FIDL call failed");
3163            zx::Status::ok(status).expect("rewind failed");
3164            let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3165            zx::Status::ok(status).expect("read_dirents failed");
3166            let mut entries = vec![];
3167            for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3168                entries.push(res.expect("Failed to parse entry"));
3169            }
3170            entries
3171        };
3172
3173        let device = fixture.close().await;
3174        let new_fixture = TestFixture::new_with_device(device).await;
3175        let root = new_fixture.root();
3176        let open_dir = || {
3177            open_dir_checked(
3178                &root,
3179                "foo",
3180                fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
3181                Default::default(),
3182            )
3183        };
3184        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3185
3186        let encrypted_entries = readdir(Arc::clone(&parent)).await;
3187        for entry in encrypted_entries {
3188            if entry.name == ".".to_owned() {
3189                continue;
3190            } else {
3191                assert!(entry.name.len() >= FSCRYPT_PADDING);
3192                assert!(!entry.name.contains("/"));
3193                assert!(entry.kind == DirentKind::Directory)
3194            }
3195        }
3196
3197        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3198        new_fixture.close().await;
3199    }
3200
3201    #[fuchsia::test]
3202    async fn test_stat_locked_file() {
3203        let fixture = TestFixture::new().await;
3204        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3205        let root = fixture.root();
3206        let open_dir = || {
3207            open_dir_checked(
3208                &root,
3209                "foo",
3210                fio::Flags::FLAG_MAYBE_CREATE
3211                    | fio::PERM_READABLE
3212                    | fio::PERM_WRITABLE
3213                    | fio::Flags::PROTOCOL_DIRECTORY,
3214                Default::default(),
3215            )
3216        };
3217        let parent = Arc::new(open_dir().await);
3218        crypt
3219            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3220            .expect("Failed to add wrapping key");
3221        parent
3222            .update_attributes(&fio::MutableNodeAttributes {
3223                wrapping_key_id: Some(WRAPPING_KEY_ID),
3224                ..Default::default()
3225            })
3226            .await
3227            .expect("FIDL call failed")
3228            .map_err(zx::ok)
3229            .expect("update_attributes failed");
3230
3231        let file = open_file_checked(
3232            parent.as_ref(),
3233            "file",
3234            fio::Flags::FLAG_MAYBE_CREATE | fio::Flags::PROTOCOL_FILE,
3235            &Default::default(),
3236        )
3237        .await;
3238
3239        close_file_checked(file).await;
3240        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3241
3242        let device = fixture.close().await;
3243        let new_fixture = TestFixture::new_with_device(device).await;
3244        let root = new_fixture.root();
3245        let open_dir = || {
3246            open_dir_checked(
3247                &root,
3248                "foo",
3249                fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
3250                Default::default(),
3251            )
3252        };
3253        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3254        let (status, buf) = parent.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3255        zx::Status::ok(status).expect("read_dirents failed");
3256        let mut encrypted_entries = vec![];
3257        for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3258            encrypted_entries.push(res.expect("Failed to parse entry"));
3259        }
3260        let mut encrypted_name = String::new();
3261        for entry in encrypted_entries {
3262            if entry.name == ".".to_owned() {
3263                continue;
3264            } else {
3265                assert!(entry.name.len() >= FSCRYPT_PADDING);
3266                encrypted_name = entry.name;
3267                assert!(entry.kind == DirentKind::File)
3268            }
3269        }
3270
3271        let file = open_file_checked(
3272            parent.as_ref(),
3273            &encrypted_name,
3274            fio::Flags::PROTOCOL_FILE,
3275            &Default::default(),
3276        )
3277        .await;
3278        let (_mutable_attributes, _immutable_attributes) = file
3279            .get_attributes(
3280                fio::NodeAttributesQuery::CONTENT_SIZE
3281                    | fio::NodeAttributesQuery::STORAGE_SIZE
3282                    | fio::NodeAttributesQuery::LINK_COUNT
3283                    | fio::NodeAttributesQuery::MODIFICATION_TIME
3284                    | fio::NodeAttributesQuery::CHANGE_TIME,
3285            )
3286            .await
3287            .expect("FIDL call failed")
3288            .map_err(zx::Status::from_raw)
3289            .expect("get_attributes failed");
3290        close_file_checked(file).await;
3291        new_fixture.close().await;
3292    }
3293
3294    #[fuchsia::test]
3295    async fn test_unlink_locked_directory() {
3296        let fixture = TestFixture::new().await;
3297        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3298        let root = fixture.root();
3299        let open_dir = || {
3300            open_dir_checked(
3301                &root,
3302                "foo",
3303                fio::Flags::FLAG_MAYBE_CREATE
3304                    | fio::PERM_READABLE
3305                    | fio::PERM_WRITABLE
3306                    | fio::Flags::PROTOCOL_DIRECTORY,
3307                Default::default(),
3308            )
3309        };
3310
3311        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3312        crypt
3313            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3314            .expect("Failed to add wrapping key");
3315        parent
3316            .update_attributes(&fio::MutableNodeAttributes {
3317                wrapping_key_id: Some(WRAPPING_KEY_ID),
3318                ..Default::default()
3319            })
3320            .await
3321            .expect("FIDL call failed")
3322            .map_err(zx::ok)
3323            .expect("update_attributes failed");
3324        let dir = open_dir_checked(
3325            parent.as_ref(),
3326            "fee",
3327            fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3328            Default::default(),
3329        )
3330        .await;
3331
3332        close_dir_checked(dir).await;
3333        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3334        let device = fixture.close().await;
3335        let new_fixture = TestFixture::new_with_device(device).await;
3336        let root = new_fixture.root();
3337        let open_dir = || {
3338            open_dir_checked(
3339                &root,
3340                "foo",
3341                fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3342                Default::default(),
3343            )
3344        };
3345        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3346
3347        let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
3348            let status = dir.rewind().await.expect("FIDL call failed");
3349            zx::Status::ok(status).expect("rewind failed");
3350            let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3351            zx::Status::ok(status).expect("read_dirents failed");
3352            let mut entries = vec![];
3353            for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3354                entries.push(res.expect("Failed to parse entry"));
3355            }
3356            entries
3357        };
3358
3359        let encrypted_entries = readdir(Arc::clone(&parent)).await;
3360        let mut encrypted_name = String::new();
3361        for entry in encrypted_entries {
3362            if entry.name == ".".to_owned() {
3363                continue;
3364            } else {
3365                assert!(entry.name.len() >= FSCRYPT_PADDING);
3366                encrypted_name = entry.name;
3367                assert!(entry.kind == DirentKind::Directory)
3368            }
3369        }
3370
3371        parent
3372            .unlink(&encrypted_name, &fio::UnlinkOptions::default())
3373            .await
3374            .expect("FIDL call failed")
3375            .expect("unlink failed");
3376
3377        let encrypted_entries = readdir(Arc::clone(&parent)).await;
3378        let mut count = 0;
3379        for entry in encrypted_entries {
3380            if entry.name == ".".to_owned() {
3381                continue;
3382            } else {
3383                assert!(entry.name.len() >= FSCRYPT_PADDING);
3384                assert!(entry.kind == DirentKind::Directory)
3385            }
3386            count += 1;
3387        }
3388        assert_eq!(count, 0);
3389        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3390        new_fixture.close().await;
3391    }
3392
3393    #[fuchsia::test]
3394    async fn test_rename_within_locked_encrypted_directory() {
3395        let fixture = TestFixture::new().await;
3396        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3397        let root = fixture.root();
3398        let open_dir = || {
3399            open_dir_checked(
3400                &root,
3401                "foo",
3402                fio::Flags::FLAG_MAYBE_CREATE
3403                    | fio::PERM_READABLE
3404                    | fio::PERM_WRITABLE
3405                    | fio::Flags::PROTOCOL_DIRECTORY,
3406                Default::default(),
3407            )
3408        };
3409
3410        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3411        crypt
3412            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3413            .expect("Failed to add wrapping key");
3414        parent
3415            .update_attributes(&fio::MutableNodeAttributes {
3416                wrapping_key_id: Some(WRAPPING_KEY_ID),
3417                ..Default::default()
3418            })
3419            .await
3420            .expect("FIDL call failed")
3421            .map_err(zx::ok)
3422            .expect("update_attributes failed");
3423        let dir = open_dir_checked(
3424            parent.as_ref(),
3425            "fee",
3426            fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3427            Default::default(),
3428        )
3429        .await;
3430
3431        close_dir_checked(dir).await;
3432        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3433        let device = fixture.close().await;
3434        let new_fixture = TestFixture::new_with_device(device).await;
3435        let crypt: Arc<CryptBase> = new_fixture.crypt().unwrap();
3436        let root = new_fixture.root();
3437        let open_dir = || {
3438            open_dir_checked(
3439                &root,
3440                "foo",
3441                fio::PERM_READABLE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
3442                Default::default(),
3443            )
3444        };
3445        let parent: Arc<fio::DirectoryProxy> = Arc::new(open_dir().await);
3446
3447        let readdir = |dir: Arc<fio::DirectoryProxy>| async move {
3448            let status = dir.rewind().await.expect("FIDL call failed");
3449            zx::Status::ok(status).expect("rewind failed");
3450            let (status, buf) = dir.read_dirents(fio::MAX_BUF).await.expect("FIDL call failed");
3451            zx::Status::ok(status).expect("read_dirents failed");
3452            let mut entries = vec![];
3453            for res in fuchsia_fs::directory::parse_dir_entries(&buf) {
3454                entries.push(res.expect("Failed to parse entry"));
3455            }
3456            entries
3457        };
3458
3459        let encrypted_entries = readdir(Arc::clone(&parent)).await;
3460        let mut encrypted_name = String::new();
3461        for entry in encrypted_entries {
3462            if entry.name == ".".to_owned() {
3463                continue;
3464            } else {
3465                assert!(entry.name.len() >= FSCRYPT_PADDING);
3466                encrypted_name = entry.name;
3467                assert!(entry.kind == DirentKind::Directory)
3468            }
3469        }
3470
3471        let (status, dst_token) = parent.get_token().await.expect("FIDL call failed");
3472        zx::Status::ok(status).expect("get_token failed");
3473        let new_encrypted_name = "aabbcc";
3474        parent
3475            .rename(&encrypted_name, zx::Event::from(dst_token.unwrap()), new_encrypted_name)
3476            .await
3477            .expect("FIDL call failed")
3478            .expect_err("rename should fail on a locked directory");
3479        let (status, dst_token) = parent.get_token().await.expect("FIDL call failed");
3480        zx::Status::ok(status).expect("get_token failed");
3481        crypt
3482            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3483            .expect("Failed to add wrapping key");
3484        parent
3485            .rename("fee", zx::Event::from(dst_token.unwrap()), "new_fee")
3486            .await
3487            .expect("FIDL call failed")
3488            .expect("rename should fail on a locked directory");
3489
3490        let _dir = open_dir_checked(
3491            parent.as_ref(),
3492            "new_fee",
3493            fio::Flags::PROTOCOL_DIRECTORY,
3494            Default::default(),
3495        )
3496        .await;
3497        close_dir_checked(Arc::try_unwrap(parent).unwrap()).await;
3498        new_fixture.close().await;
3499    }
3500
3501    #[fuchsia::test]
3502    async fn test_link_symlink_into_encrypted_directory() {
3503        let fixture = TestFixture::new().await;
3504        let crypt: Arc<CryptBase> = fixture.crypt().unwrap();
3505        let root = fixture.root();
3506        let open_dir = || {
3507            open_dir_checked(
3508                &root,
3509                "foo",
3510                fio::Flags::FLAG_MAYBE_CREATE
3511                    | fio::PERM_READABLE
3512                    | fio::PERM_WRITABLE
3513                    | fio::Flags::PROTOCOL_DIRECTORY,
3514                Default::default(),
3515            )
3516        };
3517        let parent = Arc::new(open_dir().await);
3518        crypt
3519            .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
3520            .expect("Failed to add wrapping key");
3521        parent
3522            .update_attributes(&fio::MutableNodeAttributes {
3523                wrapping_key_id: Some(WRAPPING_KEY_ID),
3524                ..Default::default()
3525            })
3526            .await
3527            .expect("FIDL call failed")
3528            .map_err(zx::ok)
3529            .expect("update_attributes failed");
3530
3531        {
3532            root.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(&root, "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 | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
5108                fio::Options {
5109                    attributes: Some(fio::NodeAttributesQuery::CHANGE_TIME),
5110                    ..Default::default()
5111                },
5112            )
5113            .await;
5114
5115            let (mutable_attributes, immutable_attributes) = dir
5116                .get_attributes(
5117                    fio::NodeAttributesQuery::CHANGE_TIME
5118                        | fio::NodeAttributesQuery::ACCESS_TIME
5119                        | fio::NodeAttributesQuery::MODIFICATION_TIME,
5120                )
5121                .await
5122                .expect("update_attributes FIDL call failed")
5123                .map_err(zx::ok)
5124                .expect("get_attributes failed");
5125            let initial_ctime = immutable_attributes.change_time;
5126            let initial_atime = mutable_attributes.access_time;
5127            // When creating a node, ctime, mtime, and atime are all updated to the current time.
5128            assert_eq!(initial_atime, initial_ctime);
5129            assert_eq!(initial_atime, mutable_attributes.modification_time);
5130
5131            // Client manages atime and they signal to Fxfs that an access has occurred and it may
5132            // require an access time update. They do so by querying with
5133            // `fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE`.
5134            let (mutable_attributes, immutable_attributes) = dir
5135                .get_attributes(
5136                    fio::NodeAttributesQuery::CHANGE_TIME
5137                        | fio::NodeAttributesQuery::ACCESS_TIME
5138                        | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
5139                )
5140                .await
5141                .expect("update_attributes FIDL call failed")
5142                .map_err(zx::ok)
5143                .expect("get_attributes failed");
5144            // atime will be updated as atime <= ctime (or mtime)
5145            assert!(initial_atime < mutable_attributes.access_time);
5146            let updated_atime = mutable_attributes.access_time;
5147            // Calling get_attributes with PENDING_ACCESS_TIME_UPDATE will trigger an update of
5148            // object attributes if access_time needs to be updated. Check that ctime isn't updated.
5149            assert_eq!(initial_ctime, immutable_attributes.change_time);
5150
5151            let (mutable_attributes, _) = dir
5152                .get_attributes(
5153                    fio::NodeAttributesQuery::ACCESS_TIME
5154                        | fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE,
5155                )
5156                .await
5157                .expect("update_attributes FIDL call failed")
5158                .map_err(zx::ok)
5159                .expect("get_attributes failed");
5160            // atime will be not be updated as atime > ctime (or mtime)
5161            assert_eq!(updated_atime, mutable_attributes.access_time);
5162
5163            (fixture.close().await, mutable_attributes.access_time, initial_ctime)
5164        };
5165
5166        let fixture =
5167            TestFixture::open(device, TestFixtureOptions { format: false, ..Default::default() })
5168                .await;
5169        let root = fixture.root();
5170        let dir = open_dir_checked(
5171            &root,
5172            DIR,
5173            fio::PERM_READABLE | fio::Flags::PROTOCOL_DIRECTORY,
5174            Default::default(),
5175        )
5176        .await;
5177
5178        let (mutable_attributes, immutable_attributes) = dir
5179            .get_attributes(
5180                fio::NodeAttributesQuery::CHANGE_TIME | fio::NodeAttributesQuery::ACCESS_TIME,
5181            )
5182            .await
5183            .expect("update_attributesFIDL call failed")
5184            .map_err(zx::ok)
5185            .expect("get_attributes failed");
5186        assert_eq!(immutable_attributes.change_time, expected_ctime);
5187        assert_eq!(mutable_attributes.access_time, expected_atime);
5188        fixture.close().await;
5189    }
5190
5191    #[fuchsia::test]
5192    async fn test_directory_immediately_tombstoned() {
5193        let fixture = TestFixture::new().await;
5194        let root = fixture.root();
5195
5196        let dir = open_dir_checked(
5197            &root,
5198            "foo",
5199            fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
5200            fio::Options::default(),
5201        )
5202        .await;
5203
5204        let (_mutable, immutable) = dir
5205            .get_attributes(fio::NodeAttributesQuery::ID)
5206            .await
5207            .expect("transport error on get_attributes")
5208            .expect("get_attributes failed");
5209        let foo_object_id = immutable.id.unwrap();
5210
5211        let dir = open_dir_checked(
5212            &root,
5213            "bar",
5214            fio::Flags::FLAG_MAYBE_CREATE | fio::PERM_WRITABLE | fio::Flags::PROTOCOL_DIRECTORY,
5215            fio::Options::default(),
5216        )
5217        .await;
5218
5219        let (_mutable, immutable) = dir
5220            .get_attributes(fio::NodeAttributesQuery::ID)
5221            .await
5222            .expect("transport error on get_attributes")
5223            .expect("get_attributes failed");
5224        let bar_object_id = immutable.id.unwrap();
5225
5226        // Check rename.
5227        let (status, dst_token) = root.get_token().await.expect("FIDL call failed");
5228        zx::Status::ok(status).expect("get_token failed");
5229        root.rename("foo", zx::Event::from(dst_token.unwrap()), "bar")
5230            .await
5231            .expect("FIDL call failed")
5232            .expect("rename failed");
5233
5234        // Allow the graveyard to run.
5235        yield_to_executor().await;
5236
5237        // The easiest way to verify the object has been deleted is to scan the LSM tree.
5238        let assert_not_found = async |oid| {
5239            let tree = fixture.volume().volume().store().tree();
5240            let layer_set = tree.layer_set();
5241            let mut merger = layer_set.merger();
5242            let mut iter = merger.query(Query::FullScan).await.unwrap();
5243            while let Some(item) = iter.get() {
5244                match item {
5245                    ItemRef { value: ObjectValue::None, .. } => {}
5246                    ItemRef {
5247                        key: ObjectKey { object_id, data: ObjectKeyData::Object }, ..
5248                    } => {
5249                        assert_ne!(*object_id, oid);
5250                    }
5251                    _ => {}
5252                }
5253                iter.advance().await.unwrap();
5254            }
5255        };
5256
5257        assert_not_found(bar_object_id).await;
5258
5259        // Now check unlink.
5260        root.unlink("bar", &Default::default())
5261            .await
5262            .expect("FIDL call failed")
5263            .expect("unlink failed");
5264
5265        assert_not_found(foo_object_id).await;
5266
5267        fixture.close().await;
5268    }
5269
5270    #[fuchsia::test]
5271    async fn test_failed_create_unnamed_file_transaction() {
5272        let fail = Arc::new(AtomicU64::new(0));
5273        let fail_clone = fail.clone();
5274        let fixture = TestFixture::open(
5275            DeviceHolder::new(FakeDevice::new(16384, 512)),
5276            TestFixtureOptions {
5277                pre_commit_hook: Some(Box::new(move |_| {
5278                    if fail_clone.load(Ordering::Relaxed) > 0 {
5279                        fail_clone.fetch_sub(1, Ordering::Relaxed);
5280                        bail!("Aborted transaction");
5281                    }
5282                    Ok(())
5283                })),
5284                ..Default::default()
5285            },
5286        )
5287        .await;
5288        let root = fixture.root();
5289
5290        fail.fetch_add(1, Ordering::Relaxed);
5291
5292        let _dir = open_file(
5293            &root,
5294            ".",
5295            fio::Flags::FLAG_CREATE_AS_UNNAMED_TEMPORARY,
5296            &fio::Options::default(),
5297        )
5298        .await
5299        .expect_err("Create unexpectedly succeeded");
5300
5301        fixture.close().await;
5302    }
5303
5304    #[fuchsia::test]
5305    async fn test_update_access_time_on_deleted_directory() {
5306        let fixture = TestFixture::new().await;
5307        let root = fixture.root();
5308
5309        let dir = open_dir_checked(
5310            &root,
5311            "foo",
5312            fio::Flags::FLAG_MAYBE_CREATE
5313                | fio::PERM_READABLE
5314                | fio::PERM_WRITABLE
5315                | fio::Flags::PROTOCOL_DIRECTORY,
5316            fio::Options::default(),
5317        )
5318        .await;
5319
5320        root.unlink("foo", &fio::UnlinkOptions::default())
5321            .await
5322            .expect("FIDL call failed")
5323            .expect("unlink failed");
5324
5325        // Requesting PENDING_ACCESS_TIME_UPDATE should not fail even if the directory is deleted.
5326        dir.get_attributes(fio::NodeAttributesQuery::PENDING_ACCESS_TIME_UPDATE)
5327            .await
5328            .expect("FIDL call failed")
5329            .expect("get_attributes failed");
5330
5331        fixture.close().await;
5332    }
5333
5334    async fn enable_casefold(dir: &fio::DirectoryProxy) {
5335        dir.update_attributes(&fio::MutableNodeAttributes {
5336            casefold: Some(true),
5337            ..Default::default()
5338        })
5339        .await
5340        .expect("update_attributes FIDL call failed")
5341        .map_err(zx::ok)
5342        .expect("update_attributes failed");
5343    }
5344
5345    #[fuchsia::test]
5346    async fn test_casefold_cache_lookup_no_duplicates() {
5347        let fixture = TestFixture::new().await;
5348        let root = fixture.root();
5349
5350        let dir = open_dir_checked(
5351            &root,
5352            "dir",
5353            fio::Flags::FLAG_MAYBE_CREATE
5354                | fio::PERM_READABLE
5355                | fio::PERM_WRITABLE
5356                | fio::Flags::PROTOCOL_DIRECTORY,
5357            Default::default(),
5358        )
5359        .await;
5360        enable_casefold(&dir).await;
5361
5362        let file = open_file_checked(
5363            &dir,
5364            "foo",
5365            fio::Flags::FLAG_MAYBE_CREATE
5366                | fio::PERM_READABLE
5367                | fio::PERM_WRITABLE
5368                | fio::Flags::PROTOCOL_FILE,
5369            &Default::default(),
5370        )
5371        .await;
5372        close_file_checked(file).await;
5373
5374        let cache = fixture.volume().volume().dirent_cache();
5375        cache.clear();
5376
5377        let file = open_file_checked(
5378            &dir,
5379            "foo",
5380            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5381            &Default::default(),
5382        )
5383        .await;
5384        close_file_checked(file).await;
5385
5386        let len_after_first_lookup = cache.len();
5387        assert!(len_after_first_lookup >= 1, "Cache should contain at least the looked up file");
5388
5389        let file = open_file_checked(
5390            &dir,
5391            "FOO",
5392            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5393            &Default::default(),
5394        )
5395        .await;
5396        close_file_checked(file).await;
5397
5398        assert_eq!(
5399            cache.len(),
5400            len_after_first_lookup,
5401            "Cache size increased (duplicate entries found)"
5402        );
5403
5404        let file = open_file_checked(
5405            &dir,
5406            "Foo",
5407            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5408            &Default::default(),
5409        )
5410        .await;
5411        close_file_checked(file).await;
5412
5413        assert_eq!(
5414            cache.len(),
5415            len_after_first_lookup,
5416            "Cache size increased (duplicate entries found)"
5417        );
5418
5419        fixture.close().await;
5420    }
5421
5422    #[fuchsia::test]
5423    async fn test_casefold_cache_rename_invalidation() {
5424        let fixture = TestFixture::new().await;
5425        let root = fixture.root();
5426
5427        let dir = open_dir_checked(
5428            &root,
5429            "dir",
5430            fio::Flags::FLAG_MAYBE_CREATE
5431                | fio::PERM_READABLE
5432                | fio::PERM_WRITABLE
5433                | fio::Flags::PROTOCOL_DIRECTORY,
5434            Default::default(),
5435        )
5436        .await;
5437        enable_casefold(&dir).await;
5438
5439        let file = open_file_checked(
5440            &dir,
5441            "foo",
5442            fio::Flags::FLAG_MAYBE_CREATE
5443                | fio::PERM_READABLE
5444                | fio::PERM_WRITABLE
5445                | fio::Flags::PROTOCOL_FILE,
5446            &Default::default(),
5447        )
5448        .await;
5449        close_file_checked(file).await;
5450
5451        let file = open_file_checked(
5452            &dir,
5453            "foo",
5454            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5455            &Default::default(),
5456        )
5457        .await;
5458        close_file_checked(file).await;
5459
5460        let (status, dst_token) = dir.get_token().await.expect("FIDL call failed");
5461        zx::Status::ok(status).expect("get_token failed");
5462        dir.rename("FOO", zx::Event::from(dst_token.unwrap()), "bar")
5463            .await
5464            .expect("Rename FIDL call failed")
5465            .expect("rename failed");
5466
5467        assert_matches!(
5468            open_file(
5469                &dir,
5470                "foo",
5471                fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5472                &Default::default()
5473            )
5474            .await
5475            .expect_err("Open \"foo\" succeeded after rename")
5476            .root_cause()
5477            .downcast_ref::<zx::Status>(),
5478            Some(&zx::Status::NOT_FOUND)
5479        );
5480
5481        let file = open_file_checked(
5482            &dir,
5483            "bar",
5484            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5485            &Default::default(),
5486        )
5487        .await;
5488        close_file_checked(file).await;
5489
5490        fixture.close().await;
5491    }
5492
5493    #[fuchsia::test]
5494    async fn test_casefold_cache_unlink_invalidation() {
5495        let fixture = TestFixture::new().await;
5496        let root = fixture.root();
5497
5498        let dir = open_dir_checked(
5499            &root,
5500            "dir",
5501            fio::Flags::FLAG_MAYBE_CREATE
5502                | fio::PERM_READABLE
5503                | fio::PERM_WRITABLE
5504                | fio::Flags::PROTOCOL_DIRECTORY,
5505            Default::default(),
5506        )
5507        .await;
5508        enable_casefold(&dir).await;
5509
5510        let file = open_file_checked(
5511            &dir,
5512            "foo",
5513            fio::Flags::FLAG_MAYBE_CREATE
5514                | fio::PERM_READABLE
5515                | fio::PERM_WRITABLE
5516                | fio::Flags::PROTOCOL_FILE,
5517            &Default::default(),
5518        )
5519        .await;
5520        close_file_checked(file).await;
5521
5522        let file = open_file_checked(
5523            &dir,
5524            "foo",
5525            fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5526            &Default::default(),
5527        )
5528        .await;
5529        close_file_checked(file).await;
5530
5531        dir.unlink("FOO", &fio::UnlinkOptions::default())
5532            .await
5533            .expect("Unlink FIDL call failed")
5534            .expect("Unlink failed");
5535
5536        assert_matches!(
5537            open_file(
5538                &dir,
5539                "foo",
5540                fio::PERM_READABLE | fio::Flags::PROTOCOL_FILE,
5541                &Default::default()
5542            )
5543            .await
5544            .expect_err("Open \"foo\" succeeded after unlink")
5545            .root_cause()
5546            .downcast_ref::<zx::Status>(),
5547            Some(&zx::Status::NOT_FOUND)
5548        );
5549
5550        fixture.close().await;
5551    }
5552
5553    async fn assert_watch_event(
5554        watcher: &mut Watcher,
5555        expected_event: WatchEvent,
5556        expected_name: &str,
5557    ) {
5558        assert_eq!(
5559            watcher.next().await.unwrap().unwrap(),
5560            WatchMessage { event: expected_event, filename: expected_name.into() }
5561        );
5562    }
5563
5564    async fn set_up_casefold_dir_with_files(
5565        fixture: &TestFixture,
5566        dir_name: &str,
5567        files: &[&str],
5568    ) -> (fio::DirectoryProxy, Watcher) {
5569        let root = fixture.root();
5570        let dir = open_dir_checked(
5571            root,
5572            dir_name,
5573            fio::Flags::FLAG_MAYBE_CREATE
5574                | fio::PERM_READABLE
5575                | fio::PERM_WRITABLE
5576                | fio::Flags::PROTOCOL_DIRECTORY,
5577            Default::default(),
5578        )
5579        .await;
5580        enable_casefold(&dir).await;
5581
5582        for &file_name in files {
5583            let file = open_file_checked(
5584                &dir,
5585                file_name,
5586                fio::Flags::FLAG_MAYBE_CREATE
5587                    | fio::PERM_READABLE
5588                    | fio::PERM_WRITABLE
5589                    | fio::Flags::PROTOCOL_FILE,
5590                &Default::default(),
5591            )
5592            .await;
5593            close_file_checked(file).await;
5594        }
5595
5596        let mut watcher = Watcher::new(&dir).await.unwrap();
5597        assert_watch_event(&mut watcher, WatchEvent::EXISTING, ".").await;
5598
5599        let mut existing_files = files
5600            .iter()
5601            .map(|s| std::path::PathBuf::from(*s))
5602            .collect::<std::collections::HashSet<_>>();
5603        while !existing_files.is_empty() {
5604            let msg = watcher.next().await.unwrap().unwrap();
5605            assert_eq!(msg.event, WatchEvent::EXISTING);
5606            assert!(
5607                existing_files.remove(&msg.filename),
5608                "Unexpected existing file: {:?}",
5609                msg.filename
5610            );
5611        }
5612
5613        assert_watch_event(&mut watcher, WatchEvent::IDLE, "").await;
5614
5615        (dir, watcher)
5616    }
5617
5618    #[fuchsia::test]
5619    async fn test_casefold_rename_watcher_events() {
5620        let fixture = TestFixture::new().await;
5621        let (dir, mut watcher) = set_up_casefold_dir_with_files(&fixture, "dir", &["foo"]).await;
5622
5623        let (status, dst_token) = dir.get_token().await.expect("FIDL call failed");
5624        zx::Status::ok(status).expect("get_token failed");
5625        dir.rename("FOO", zx::Event::from(dst_token.unwrap()), "BAR")
5626            .await
5627            .expect("Rename FIDL call failed")
5628            .expect("rename failed");
5629
5630        assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "foo").await;
5631        assert_watch_event(&mut watcher, WatchEvent::ADD_FILE, "BAR").await;
5632
5633        fixture.close().await;
5634    }
5635
5636    #[fuchsia::test]
5637    async fn test_casefold_rename_overwrite_watcher_events() {
5638        let fixture = TestFixture::new().await;
5639        let (dir, mut watcher) =
5640            set_up_casefold_dir_with_files(&fixture, "dir", &["foo", "bar"]).await;
5641
5642        let (status, dst_token) = dir.get_token().await.expect("FIDL call failed");
5643        zx::Status::ok(status).expect("get_token failed");
5644        dir.rename("FOO", zx::Event::from(dst_token.unwrap()), "BAR")
5645            .await
5646            .expect("Rename FIDL call failed")
5647            .expect("rename failed");
5648
5649        assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "foo").await;
5650        assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "bar").await;
5651        assert_watch_event(&mut watcher, WatchEvent::ADD_FILE, "BAR").await;
5652
5653        fixture.close().await;
5654    }
5655
5656    #[fuchsia::test]
5657    async fn test_casefold_rename_cross_dir_overwrite_watcher_events() {
5658        let fixture = TestFixture::new().await;
5659        let (src_dir, mut src_watcher) =
5660            set_up_casefold_dir_with_files(&fixture, "src_dir", &["foo"]).await;
5661        let (dst_dir, mut dst_watcher) =
5662            set_up_casefold_dir_with_files(&fixture, "dst_dir", &["bar"]).await;
5663
5664        let (status, dst_token) = dst_dir.get_token().await.expect("FIDL call failed");
5665        zx::Status::ok(status).expect("get_token failed");
5666        src_dir
5667            .rename("FOO", zx::Event::from(dst_token.unwrap()), "BAR")
5668            .await
5669            .expect("Rename FIDL call failed")
5670            .expect("rename failed");
5671
5672        assert_watch_event(&mut src_watcher, WatchEvent::REMOVE_FILE, "foo").await;
5673
5674        assert_watch_event(&mut dst_watcher, WatchEvent::REMOVE_FILE, "bar").await;
5675        assert_watch_event(&mut dst_watcher, WatchEvent::ADD_FILE, "BAR").await;
5676
5677        fixture.close().await;
5678    }
5679
5680    #[fuchsia::test]
5681    async fn test_casefold_unlink_watcher_events() {
5682        let fixture = TestFixture::new().await;
5683        let (dir, mut watcher) = set_up_casefold_dir_with_files(&fixture, "dir", &["foo"]).await;
5684
5685        dir.unlink("FOO", &fio::UnlinkOptions::default())
5686            .await
5687            .expect("Unlink FIDL call failed")
5688            .expect("Unlink failed");
5689
5690        assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "foo").await;
5691
5692        fixture.close().await;
5693    }
5694
5695    #[fuchsia::test]
5696    async fn test_casefold_rename_case_only_watcher_events() {
5697        let fixture = TestFixture::new().await;
5698        let (dir, mut watcher) = set_up_casefold_dir_with_files(&fixture, "dir", &["Foo"]).await;
5699
5700        let (status, dst_token) = dir.get_token().await.expect("FIDL call failed");
5701        zx::Status::ok(status).expect("get_token failed");
5702        dir.rename("Foo", zx::Event::from(dst_token.unwrap()), "FOO")
5703            .await
5704            .expect("Rename FIDL call failed")
5705            .expect("rename failed");
5706
5707        assert_watch_event(&mut watcher, WatchEvent::REMOVE_FILE, "Foo").await;
5708        assert_watch_event(&mut watcher, WatchEvent::ADD_FILE, "FOO").await;
5709
5710        fixture.close().await;
5711    }
5712}