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