Skip to main content

fxfs_platform/fuchsia/
directory.rs

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