fxfs/object_store/
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::errors::FxfsError;
6use crate::lsm_tree::Query;
7use crate::lsm_tree::merge::{Merger, MergerIterator};
8use crate::lsm_tree::types::{Item, ItemRef, LayerIterator};
9use crate::object_handle::{INVALID_OBJECT_ID, ObjectHandle, ObjectProperties};
10use crate::object_store::object_record::{
11    ChildValue, EncryptedCasefoldChild, EncryptedChild, ObjectAttributes, ObjectDescriptor,
12    ObjectItem, ObjectKey, ObjectKeyData, ObjectKind, ObjectValue, Timestamp,
13};
14use crate::object_store::transaction::{
15    LockKey, LockKeys, Mutation, Options, Transaction, lock_keys,
16};
17use crate::object_store::{
18    DataObjectHandle, HandleOptions, HandleOwner, ObjectStore, SetExtendedAttributeMode,
19    StoreObjectHandle,
20};
21use anyhow::{Error, anyhow, bail, ensure};
22use fidl_fuchsia_io as fio;
23use fscrypt::proxy_filename::ProxyFilename;
24use fuchsia_sync::Mutex;
25use fxfs_crypto::{Cipher, CipherHolder, KeyType, ObjectType, WrappingKeyId, key_to_cipher};
26use std::fmt;
27use std::ops::ControlFlow;
28use std::sync::Arc;
29use std::sync::atomic::{AtomicBool, Ordering};
30use zerocopy::IntoBytes;
31
32use super::FSCRYPT_KEY_ID;
33
34type BoxPredicate<'a> = Box<dyn Fn(&ObjectKey) -> ControlFlow<bool> + Send + 'a>;
35
36/// This contains the transaction with the appropriate locks to replace src with dst, and also the
37/// ID and type of the src and dst.
38pub struct ReplaceContext<'a> {
39    pub transaction: Transaction<'a>,
40    pub src_id_and_descriptor: Option<(u64, ObjectDescriptor)>,
41    pub dst_id_and_descriptor: Option<(u64, ObjectDescriptor)>,
42}
43
44/// A directory stores name to child object mappings.
45pub struct Directory<S: HandleOwner> {
46    handle: StoreObjectHandle<S>,
47    /// True if the directory has been deleted and is no longer accessible.
48    is_deleted: AtomicBool,
49    /// True if this directory uses case-insensitive names.
50    casefold: AtomicBool,
51    wrapping_key_id: Mutex<Option<WrappingKeyId>>,
52}
53
54#[derive(Clone, Default)]
55pub struct MutableAttributesInternal {
56    sub_dirs: i64,
57    change_time: Option<Timestamp>,
58    modification_time: Option<u64>,
59    creation_time: Option<u64>,
60}
61
62impl MutableAttributesInternal {
63    pub fn new(
64        sub_dirs: i64,
65        change_time: Option<Timestamp>,
66        modification_time: Option<u64>,
67        creation_time: Option<u64>,
68    ) -> Self {
69        Self { sub_dirs, change_time, modification_time, creation_time }
70    }
71}
72
73/// Encrypts a unicode `name` into a sequence of bytes using the fscrypt key.
74fn encrypt_filename(key: &dyn Cipher, object_id: u64, name: &str) -> Result<Vec<u8>, Error> {
75    let mut name_bytes = name.to_string().into_bytes();
76    key.encrypt_filename(object_id, &mut name_bytes)?;
77    Ok(name_bytes)
78}
79
80/// Decrypts a unicode `name` from a sequence of bytes using the fscrypt key.
81fn decrypt_filename(key: &dyn Cipher, object_id: u64, data: &Vec<u8>) -> Result<String, Error> {
82    let mut raw = data.clone();
83    key.decrypt_filename(object_id, &mut raw)?;
84    Ok(String::from_utf8(raw)?)
85}
86
87#[fxfs_trace::trace]
88impl<S: HandleOwner> Directory<S> {
89    fn new(
90        owner: Arc<S>,
91        object_id: u64,
92        wrapping_key_id: Option<WrappingKeyId>,
93        casefold: bool,
94    ) -> Self {
95        Directory {
96            handle: StoreObjectHandle::new(
97                owner,
98                object_id,
99                /* permanent_keys: */ false,
100                HandleOptions::default(),
101                /* trace: */ false,
102            ),
103            is_deleted: AtomicBool::new(false),
104            casefold: AtomicBool::new(casefold),
105            wrapping_key_id: Mutex::new(wrapping_key_id),
106        }
107    }
108
109    pub fn object_id(&self) -> u64 {
110        self.handle.object_id()
111    }
112
113    pub fn wrapping_key_id(&self) -> Option<WrappingKeyId> {
114        *self.wrapping_key_id.lock()
115    }
116
117    /// Retrieves keys from the key manager or unwraps the wrapped keys in the directory's key
118    /// record.  Returns None if the key is currently unavailable due to the wrapping key being
119    /// unavailable.
120    pub async fn get_fscrypt_key(&self) -> Result<CipherHolder, Error> {
121        let object_id = self.object_id();
122        let store = self.store();
123        store
124            .key_manager()
125            .get_fscrypt_key(object_id, store.crypt().unwrap().as_ref(), async || {
126                store.get_keys(object_id).await
127            })
128            .await
129    }
130
131    pub fn owner(&self) -> &Arc<S> {
132        self.handle.owner()
133    }
134
135    pub fn store(&self) -> &ObjectStore {
136        self.handle.store()
137    }
138
139    pub fn handle(&self) -> &StoreObjectHandle<S> {
140        &self.handle
141    }
142
143    pub fn is_deleted(&self) -> bool {
144        self.is_deleted.load(Ordering::Relaxed)
145    }
146
147    pub fn set_deleted(&self) {
148        self.is_deleted.store(true, Ordering::Relaxed);
149    }
150
151    /// True if this directory is using casefolding (case-insensitive, normalized unicode filenames)
152    pub fn casefold(&self) -> bool {
153        self.casefold.load(Ordering::Relaxed)
154    }
155
156    /// Enables/disables casefolding. This can only be done on an empty directory.
157    pub async fn set_casefold(&self, val: bool) -> Result<(), Error> {
158        let fs = self.store().filesystem();
159        // Nb: We lock the directory to ensure it doesn't change during our check for children.
160        let mut transaction = fs
161            .new_transaction(
162                lock_keys![LockKey::object(self.store().store_object_id(), self.object_id())],
163                Options::default(),
164            )
165            .await?;
166        ensure!(!self.has_children().await?, FxfsError::InvalidArgs);
167        let mut mutation =
168            self.store().txn_get_object_mutation(&transaction, self.object_id()).await?;
169        if let ObjectValue::Object { kind: ObjectKind::Directory { casefold, .. }, .. } =
170            &mut mutation.item.value
171        {
172            *casefold = val;
173        } else {
174            return Err(
175                anyhow!(FxfsError::Inconsistent).context("casefold only applies to directories")
176            );
177        }
178        transaction.add(self.store().store_object_id(), Mutation::ObjectStore(mutation));
179        transaction.commit_with_callback(|_| self.casefold.store(val, Ordering::Relaxed)).await?;
180        Ok(())
181    }
182
183    pub async fn create(
184        transaction: &mut Transaction<'_>,
185        owner: &Arc<S>,
186        wrapping_key_id: Option<WrappingKeyId>,
187    ) -> Result<Directory<S>, Error> {
188        Self::create_with_options(transaction, owner, wrapping_key_id, false).await
189    }
190
191    pub async fn create_with_options(
192        transaction: &mut Transaction<'_>,
193        owner: &Arc<S>,
194        wrapping_key_id: Option<WrappingKeyId>,
195        casefold: bool,
196    ) -> Result<Directory<S>, Error> {
197        let store = owner.as_ref().as_ref();
198        let object_id = store.get_next_object_id(transaction.txn_guard()).await?;
199        let now = Timestamp::now();
200        transaction.add(
201            store.store_object_id(),
202            Mutation::insert_object(
203                ObjectKey::object(object_id),
204                ObjectValue::Object {
205                    kind: ObjectKind::Directory { sub_dirs: 0, casefold, wrapping_key_id },
206                    attributes: ObjectAttributes {
207                        creation_time: now.clone(),
208                        modification_time: now.clone(),
209                        project_id: 0,
210                        posix_attributes: None,
211                        allocated_size: 0,
212                        access_time: now.clone(),
213                        change_time: now,
214                    },
215                },
216            ),
217        );
218        if let Some(wrapping_key_id) = &wrapping_key_id {
219            if let Some(crypt) = store.crypt() {
220                let (key, unwrapped_key) = crypt
221                    .create_key_with_id(object_id, *wrapping_key_id, ObjectType::Directory)
222                    .await?;
223                let cipher = key_to_cipher(&key, &unwrapped_key)?;
224                transaction.add(
225                    store.store_object_id(),
226                    Mutation::insert_object(
227                        ObjectKey::keys(object_id),
228                        ObjectValue::keys(vec![(FSCRYPT_KEY_ID, key)].into()),
229                    ),
230                );
231                // Note that it's possible that this entry gets inserted into the key manager but
232                // this transaction doesn't get committed. This shouldn't be a problem because
233                // unused keys get purged on a standard timeout interval and this key shouldn't
234                // conflict with any other keys.
235                store.key_manager.insert(
236                    object_id,
237                    Arc::new(vec![(FSCRYPT_KEY_ID, CipherHolder::Cipher(cipher))].into()),
238                    false,
239                );
240            } else {
241                return Err(anyhow!("No crypt"));
242            }
243        }
244        Ok(Directory::new(owner.clone(), object_id, wrapping_key_id, casefold))
245    }
246
247    /// Sets the file-based-encryption (FBE) wrapping key for this directory.
248    ///
249    /// This can only be done on empty directories and must NOT be done as part of a transaction
250    /// that creates entries in the same directory. The reason for this is that local state
251    /// (self.wrapping_key_id) is used to control the type of child record written out. If children
252    /// are written to a directory as part of the same transaction that enables FBE, they will be
253    /// written as the wrong child record type.
254    pub async fn set_wrapping_key(
255        &self,
256        transaction: &mut Transaction<'_>,
257        id: WrappingKeyId,
258    ) -> Result<Arc<dyn Cipher>, Error> {
259        let object_id = self.object_id();
260        let store = self.store();
261        if let Some(crypt) = store.crypt() {
262            let (key, unwrapped_key) =
263                crypt.create_key_with_id(object_id, id, ObjectType::Directory).await?;
264            let mut mutation = store.txn_get_object_mutation(transaction, object_id).await?;
265            if let ObjectValue::Object {
266                kind: ObjectKind::Directory { wrapping_key_id, .. }, ..
267            } = &mut mutation.item.value
268            {
269                if wrapping_key_id.is_some() {
270                    return Err(anyhow!("wrapping key id is already set"));
271                }
272                if self.has_children().await? {
273                    return Err(FxfsError::NotEmpty.into());
274                }
275                *wrapping_key_id = Some(id);
276            } else {
277                match mutation.item.value {
278                    ObjectValue::None => bail!(FxfsError::NotFound),
279                    _ => bail!(FxfsError::NotDir),
280                }
281            }
282            transaction.add(store.store_object_id(), Mutation::ObjectStore(mutation));
283
284            let keys_key = ObjectKey::keys(object_id);
285            let item = if let Some(mutation) =
286                transaction.get_object_mutation(store.store_object_id(), keys_key.clone())
287            {
288                Some(mutation.item.clone())
289            } else {
290                store.tree.find(&keys_key).await?
291            };
292
293            let cipher = key_to_cipher(&key, &unwrapped_key)?;
294            match item {
295                None | Some(Item { value: ObjectValue::None, .. }) => {
296                    transaction.add(
297                        store.store_object_id(),
298                        Mutation::insert_object(
299                            ObjectKey::keys(object_id),
300                            ObjectValue::keys(vec![(FSCRYPT_KEY_ID, key)].into()),
301                        ),
302                    );
303                }
304                Some(Item { value: ObjectValue::Keys(mut keys), .. }) => {
305                    keys.insert(FSCRYPT_KEY_ID, key.into());
306                    transaction.add(
307                        store.store_object_id(),
308                        Mutation::replace_or_insert_object(
309                            ObjectKey::keys(object_id),
310                            ObjectValue::keys(keys),
311                        ),
312                    );
313                }
314                Some(item) => bail!("Unexpected item in lookup: {item:?}"),
315            }
316            Ok(cipher)
317        } else {
318            Err(anyhow!("No crypt"))
319        }
320    }
321
322    #[trace]
323    pub async fn open(owner: &Arc<S>, object_id: u64) -> Result<Directory<S>, Error> {
324        let store = owner.as_ref().as_ref();
325        match store.tree.find(&ObjectKey::object(object_id)).await?.ok_or(FxfsError::NotFound)? {
326            ObjectItem {
327                value:
328                    ObjectValue::Object {
329                        kind: ObjectKind::Directory { wrapping_key_id, casefold, .. },
330                        ..
331                    },
332                ..
333            } => Ok(Directory::new(owner.clone(), object_id, wrapping_key_id, casefold)),
334            _ => bail!(FxfsError::NotDir),
335        }
336    }
337
338    /// Opens a directory. The caller is responsible for ensuring that the object exists and is a
339    /// directory.
340    pub fn open_unchecked(
341        owner: Arc<S>,
342        object_id: u64,
343        wrapping_key_id: Option<WrappingKeyId>,
344        casefold: bool,
345    ) -> Self {
346        Self::new(owner, object_id, wrapping_key_id, casefold)
347    }
348
349    /// Acquires the transaction with the appropriate locks to replace |dst| with |src.0|/|src.1|.
350    /// |src| can be None in the case of unlinking |dst| from |self|.
351    /// Returns the transaction, as well as the ID and type of the child and the src. If the child
352    /// doesn't exist, then a transaction is returned with a lock only on the parent and None for
353    /// the target info so that the transaction can be executed with the confidence that the target
354    /// doesn't exist. If the src doesn't exist (in the case of unlinking), None is return for the
355    /// source info.
356    ///
357    /// We need to lock |self|, but also the child if it exists. When it is a directory the lock
358    /// prevents entries being added at the same time. When it is a file needs to be able to
359    /// decrement the reference count.
360    /// If src exists, we also need to lock |src.0| and |src.1|. This is to update their timestamps.
361    pub async fn acquire_context_for_replace(
362        &self,
363        src: Option<(&Directory<S>, &str)>,
364        dst: &str,
365        borrow_metadata_space: bool,
366    ) -> Result<ReplaceContext<'_>, Error> {
367        // Since we don't know the child object ID until we've looked up the child, we need to loop
368        // until we have acquired a lock on a child whose ID is the same as it was in the last
369        // iteration. This also applies for src object ID if |src| is passed in.
370        //
371        // Note that the returned transaction may lock more objects than is necessary (for example,
372        // if the child "foo" was first a directory, then was renamed to "bar" and a file "foo" was
373        // created, we might acquire a lock on both the parent and "bar").
374        //
375        // We can look into not having this loop by adding support to try to add locks in the
376        // transaction. If it fails, we can drop all the locks and start a new transaction.
377        let store = self.store();
378        let mut child_object_id = INVALID_OBJECT_ID;
379        let mut src_object_id = src.map(|_| INVALID_OBJECT_ID);
380        let mut lock_keys = LockKeys::with_capacity(4);
381        lock_keys.push(LockKey::object(store.store_object_id(), self.object_id()));
382        loop {
383            lock_keys.truncate(1);
384            if let Some(src) = src {
385                lock_keys.push(LockKey::object(store.store_object_id(), src.0.object_id()));
386                if let Some(src_object_id) = src_object_id {
387                    if src_object_id != INVALID_OBJECT_ID {
388                        lock_keys.push(LockKey::object(store.store_object_id(), src_object_id));
389                    }
390                }
391            }
392            if child_object_id != INVALID_OBJECT_ID {
393                lock_keys.push(LockKey::object(store.store_object_id(), child_object_id));
394            };
395            let fs = store.filesystem().clone();
396            let transaction = fs
397                .new_transaction(
398                    lock_keys.clone(),
399                    Options { borrow_metadata_space, ..Default::default() },
400                )
401                .await?;
402
403            let mut have_required_locks = true;
404            let mut src_id_and_descriptor = None;
405            if let Some((src_dir, src_name)) = src {
406                match src_dir.lookup(src_name).await? {
407                    Some((object_id, object_descriptor, _)) => match object_descriptor {
408                        ObjectDescriptor::File
409                        | ObjectDescriptor::Directory
410                        | ObjectDescriptor::Symlink => {
411                            if src_object_id != Some(object_id) {
412                                have_required_locks = false;
413                                src_object_id = Some(object_id);
414                            }
415                            src_id_and_descriptor = Some((object_id, object_descriptor));
416                        }
417                        _ => bail!(FxfsError::Inconsistent),
418                    },
419                    None => {
420                        // Can't find src.0/src.1
421                        bail!(FxfsError::NotFound)
422                    }
423                }
424            };
425            let dst_id_and_descriptor = match self.lookup(dst).await? {
426                Some((object_id, object_descriptor, _)) => match object_descriptor {
427                    ObjectDescriptor::File
428                    | ObjectDescriptor::Directory
429                    | ObjectDescriptor::Symlink => {
430                        if child_object_id != object_id {
431                            have_required_locks = false;
432                            child_object_id = object_id
433                        }
434                        Some((object_id, object_descriptor))
435                    }
436                    _ => bail!(FxfsError::Inconsistent),
437                },
438                None => {
439                    if child_object_id != INVALID_OBJECT_ID {
440                        have_required_locks = false;
441                        child_object_id = INVALID_OBJECT_ID;
442                    }
443                    None
444                }
445            };
446            if have_required_locks {
447                return Ok(ReplaceContext {
448                    transaction,
449                    src_id_and_descriptor,
450                    dst_id_and_descriptor,
451                });
452            }
453        }
454    }
455
456    async fn has_children(&self) -> Result<bool, Error> {
457        if self.is_deleted() {
458            return Ok(false);
459        }
460        let layer_set = self.store().tree().layer_set();
461        let mut merger = layer_set.merger();
462        Ok(self.iter(&mut merger).await?.get().is_some())
463    }
464
465    /// Returns the object ID and descriptor for the given child, or None if not found. If found,
466    /// also returns a boolean indicating whether or not the parent directory was locked during the
467    /// lookup.
468    #[trace]
469    pub async fn lookup(&self, name: &str) -> Result<Option<(u64, ObjectDescriptor, bool)>, Error> {
470        if self.is_deleted() {
471            return Ok(None);
472        }
473        let cipher;
474        let proxy_name;
475        // In some cases, we need to iterate over directory entries to find a match.  The code below
476        // finds a starting key and an optional predicate that is used to find a matching entry.
477        // If there is no predicate, we can look for an exact match.
478        let (key, predicate, locked): (_, Option<BoxPredicate<'_>>, _) = if self
479            .wrapping_key_id
480            .lock()
481            .is_some()
482        {
483            cipher = self.get_fscrypt_key().await?;
484            match &cipher {
485                CipherHolder::Cipher(cipher) => {
486                    if self.casefold() {
487                        // We must iterate over all directory entries that have a matching hash code
488                        // until we find a match.
489                        let target_hash_code = cipher.hash_code_casefold(name);
490                        let key = ObjectKey::encrypted_child(
491                            self.object_id(),
492                            vec![],
493                            Some(target_hash_code),
494                        );
495                        (
496                            key,
497                            Some(Box::new(encrypted_casefold_predicate(
498                                cipher.as_ref(),
499                                self.object_id(),
500                                target_hash_code,
501                                name,
502                            ))),
503                            false,
504                        )
505                    } else {
506                        let encrypted_name =
507                            encrypt_filename(cipher.as_ref(), self.object_id(), name)?;
508                        let hash_code = cipher.hash_code(encrypted_name.as_bytes(), name);
509                        (
510                            ObjectKey::encrypted_child(self.object_id(), encrypted_name, hash_code),
511                            None,
512                            false,
513                        )
514                    }
515                }
516                CipherHolder::Unavailable(key_type) => {
517                    proxy_name = match ProxyFilename::try_from(name) {
518                        Ok(name) => name,
519                        Err(_) => return Ok(None),
520                    };
521                    let (key, predicate) =
522                        self.get_key_and_predicate_for_unavailable_cipher(*key_type, &proxy_name);
523                    (key, predicate, true)
524                }
525            }
526        } else {
527            (ObjectKey::child(self.object_id(), name, self.casefold()), None, false)
528        };
529
530        // If the directory is locked, we don't want to use `LMSTree::find` because it caches
531        // results, and if the directory later becomes unlocked, we don't want the cache to yield
532        // entries from when it was locked.
533        if locked || predicate.is_some() {
534            let layer_set = self.store().tree().layer_set();
535            let mut merger = layer_set.merger();
536            let mut iter = merger.query(Query::FullRange(&key)).await?;
537            if let Some(predicate) = predicate {
538                if !self.advance_until(&mut iter, predicate).await? {
539                    return Ok(None);
540                }
541            } else if iter
542                .get()
543                .is_none_or(|item| item.key != &key || matches!(item.value, ObjectValue::None))
544            {
545                return Ok(None);
546            }
547            let item = iter.get().unwrap();
548            match item.value {
549                ObjectValue::Child(ChildValue { object_id, object_descriptor }) => {
550                    Ok(Some((*object_id, object_descriptor.clone(), locked)))
551                }
552                _ => Err(anyhow!(FxfsError::Inconsistent)
553                    .context(format!("Unexpected item in lookup: {item:?}"))),
554            }
555        } else {
556            let item = self.store().tree().find(&key).await?;
557            match item {
558                None => Ok(None),
559                Some(ObjectItem {
560                    value: ObjectValue::Child(ChildValue { object_id, object_descriptor }),
561                    ..
562                }) => Ok(Some((object_id, object_descriptor, false))),
563                _ => Err(anyhow!(FxfsError::Inconsistent)
564                    .context(format!("Unexpected item in lookup: {item:?}",))),
565            }
566        }
567    }
568
569    pub async fn create_child_dir(
570        &self,
571        transaction: &mut Transaction<'_>,
572        name: &str,
573    ) -> Result<Directory<S>, Error> {
574        ensure!(!self.is_deleted(), FxfsError::Deleted);
575
576        let handle = Directory::create_with_options(
577            transaction,
578            self.owner(),
579            self.wrapping_key_id(),
580            self.casefold(),
581        )
582        .await?;
583        if self.wrapping_key_id.lock().is_some() {
584            let fscrypt_key =
585                self.get_fscrypt_key().await?.into_cipher().ok_or(FxfsError::NoKey)?;
586            let encrypted_name =
587                encrypt_filename(&*fscrypt_key, self.object_id(), name).expect("encrypt_filename");
588            let hash_code = if self.casefold() {
589                Some(fscrypt_key.hash_code_casefold(name))
590            } else {
591                fscrypt_key.hash_code(encrypted_name.as_bytes(), name)
592            };
593            transaction.add(
594                self.store().store_object_id(),
595                Mutation::replace_or_insert_object(
596                    ObjectKey::encrypted_child(self.object_id(), encrypted_name, hash_code),
597                    ObjectValue::child(handle.object_id(), ObjectDescriptor::Directory),
598                ),
599            );
600        } else {
601            transaction.add(
602                self.store().store_object_id(),
603                Mutation::replace_or_insert_object(
604                    ObjectKey::child(self.object_id(), &name, self.casefold()),
605                    ObjectValue::child(handle.object_id(), ObjectDescriptor::Directory),
606                ),
607            );
608        }
609        let now = Timestamp::now();
610        self.update_dir_attributes_internal(
611            transaction,
612            self.object_id(),
613            MutableAttributesInternal {
614                sub_dirs: 1,
615                modification_time: Some(now.as_nanos()),
616                change_time: Some(now),
617                ..Default::default()
618            },
619        )
620        .await?;
621        self.copy_project_id_to_object_in_txn(transaction, handle.object_id())?;
622        Ok(handle)
623    }
624
625    pub async fn add_child_file<'a>(
626        &self,
627        transaction: &mut Transaction<'a>,
628        name: &str,
629        handle: &DataObjectHandle<S>,
630    ) -> Result<(), Error> {
631        ensure!(!self.is_deleted(), FxfsError::Deleted);
632        if self.wrapping_key_id.lock().is_some() {
633            let fscrypt_key =
634                self.get_fscrypt_key().await?.into_cipher().ok_or(FxfsError::NoKey)?;
635            let encrypted_name =
636                encrypt_filename(&*fscrypt_key, self.object_id(), name).expect("encrypt_filename");
637            let hash_code = if self.casefold() {
638                Some(fscrypt_key.hash_code_casefold(name))
639            } else {
640                fscrypt_key.hash_code(encrypted_name.as_bytes(), name)
641            };
642            transaction.add(
643                self.store().store_object_id(),
644                Mutation::replace_or_insert_object(
645                    ObjectKey::encrypted_child(self.object_id(), encrypted_name, hash_code),
646                    ObjectValue::child(handle.object_id(), ObjectDescriptor::File),
647                ),
648            );
649        } else {
650            transaction.add(
651                self.store().store_object_id(),
652                Mutation::replace_or_insert_object(
653                    ObjectKey::child(self.object_id(), &name, self.casefold()),
654                    ObjectValue::child(handle.object_id(), ObjectDescriptor::File),
655                ),
656            );
657        }
658        let now = Timestamp::now();
659        self.update_dir_attributes_internal(
660            transaction,
661            self.object_id(),
662            MutableAttributesInternal {
663                modification_time: Some(now.as_nanos()),
664                change_time: Some(now),
665                ..Default::default()
666            },
667        )
668        .await
669    }
670
671    // This applies the project id of this directory (if nonzero) to an object. The method assumes
672    // both this and child objects are already present in the mutations of the provided
673    // transactions and that the child is of of zero size. This is meant for use inside
674    // `create_child_file()` and `create_child_dir()` only, where such assumptions are safe.
675    fn copy_project_id_to_object_in_txn<'a>(
676        &self,
677        transaction: &mut Transaction<'a>,
678        object_id: u64,
679    ) -> Result<(), Error> {
680        let store_id = self.store().store_object_id();
681        // This mutation must already be in here as we've just modified the mtime.
682        let ObjectValue::Object { attributes: ObjectAttributes { project_id, .. }, .. } =
683            transaction
684                .get_object_mutation(store_id, ObjectKey::object(self.object_id()))
685                .unwrap()
686                .item
687                .value
688        else {
689            return Err(anyhow!(FxfsError::Inconsistent));
690        };
691        if project_id > 0 {
692            // This mutation must be present as well since we've just created the object. So this
693            // replaces it.
694            let mut mutation = transaction
695                .get_object_mutation(store_id, ObjectKey::object(object_id))
696                .unwrap()
697                .clone();
698            if let ObjectValue::Object {
699                attributes: ObjectAttributes { project_id: child_project_id, .. },
700                ..
701            } = &mut mutation.item.value
702            {
703                *child_project_id = project_id;
704            } else {
705                return Err(anyhow!(FxfsError::Inconsistent));
706            }
707            transaction.add(store_id, Mutation::ObjectStore(mutation));
708            transaction.add(
709                store_id,
710                Mutation::merge_object(
711                    ObjectKey::project_usage(self.store().root_directory_object_id(), project_id),
712                    ObjectValue::BytesAndNodes { bytes: 0, nodes: 1 },
713                ),
714            );
715        }
716        Ok(())
717    }
718
719    pub async fn create_child_file<'a>(
720        &self,
721        transaction: &mut Transaction<'a>,
722        name: &str,
723    ) -> Result<DataObjectHandle<S>, Error> {
724        self.create_child_file_with_options(transaction, name, HandleOptions::default()).await
725    }
726
727    pub async fn create_child_file_with_options<'a>(
728        &self,
729        transaction: &mut Transaction<'a>,
730        name: &str,
731        options: HandleOptions,
732    ) -> Result<DataObjectHandle<S>, Error> {
733        ensure!(!self.is_deleted(), FxfsError::Deleted);
734        let wrapping_key_id = self.wrapping_key_id.lock().clone();
735        let handle =
736            ObjectStore::create_object(self.owner(), transaction, options, wrapping_key_id).await?;
737        self.add_child_file(transaction, name, &handle).await?;
738        self.copy_project_id_to_object_in_txn(transaction, handle.object_id())?;
739        Ok(handle)
740    }
741
742    pub async fn create_child_unnamed_temporary_file<'a>(
743        &self,
744        transaction: &mut Transaction<'a>,
745    ) -> Result<DataObjectHandle<S>, Error> {
746        ensure!(!self.is_deleted(), FxfsError::Deleted);
747        let wrapping_key_id = self.wrapping_key_id.lock().clone();
748        let handle = ObjectStore::create_object(
749            self.owner(),
750            transaction,
751            HandleOptions::default(),
752            wrapping_key_id,
753        )
754        .await?;
755
756        // Copy project ID from self to the created file object.
757        let ObjectValue::Object { attributes: ObjectAttributes { project_id, .. }, .. } = self
758            .store()
759            .txn_get_object_mutation(&transaction, self.object_id())
760            .await
761            .unwrap()
762            .item
763            .value
764        else {
765            bail!(
766                anyhow!(FxfsError::Inconsistent)
767                    .context("Directory.create_child_file_with_options: expected mutation object")
768            );
769        };
770
771        // Update the object mutation with parent's project ID.
772        let mut child_mutation = transaction
773            .get_object_mutation(
774                self.store().store_object_id(),
775                ObjectKey::object(handle.object_id()),
776            )
777            .unwrap()
778            .clone();
779        if let ObjectValue::Object {
780            attributes: ObjectAttributes { project_id: child_project_id, .. },
781            ..
782        } = &mut child_mutation.item.value
783        {
784            *child_project_id = project_id;
785        } else {
786            bail!(
787                anyhow!(FxfsError::Inconsistent)
788                    .context("Directory.create_child_file_with_options: expected file object")
789            );
790        }
791        transaction.add(self.store().store_object_id(), Mutation::ObjectStore(child_mutation));
792
793        // Add object to graveyard - the object should be removed on remount.
794        self.store().add_to_graveyard(transaction, handle.object_id());
795
796        Ok(handle)
797    }
798
799    pub async fn create_symlink(
800        &self,
801        transaction: &mut Transaction<'_>,
802        link: &[u8],
803        name: &str,
804    ) -> Result<u64, Error> {
805        ensure!(!self.is_deleted(), FxfsError::Deleted);
806        // Limit the length of link that might be too big to put in the tree.
807        // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html.
808        // See _POSIX_SYMLINK_MAX.
809        ensure!(link.len() <= 256, FxfsError::BadPath);
810        let symlink_id = self.store().get_next_object_id(transaction.txn_guard()).await?;
811        let wrapping_key_id = self.wrapping_key_id.lock().clone();
812        let mut link = link.to_vec();
813
814        if let Some(wrapping_key_id) = wrapping_key_id {
815            if let Some(crypt) = self.store().crypt() {
816                let (key, unwrapped_key) = crypt
817                    .create_key_with_id(symlink_id, wrapping_key_id, ObjectType::Symlink)
818                    .await?;
819
820                // Note that it's possible that this entry gets inserted into the key manager but
821                // this transaction doesn't get committed. This shouldn't be a problem because
822                // unused keys get purged on a standard timeout interval and this key shouldn't
823                // conflict with any other keys.
824                let cipher = key_to_cipher(&key, &unwrapped_key)?;
825                self.store().key_manager.insert(
826                    symlink_id,
827                    Arc::new(vec![(FSCRYPT_KEY_ID, CipherHolder::Cipher(cipher.clone()))].into()),
828                    false,
829                );
830
831                let dir_key =
832                    self.get_fscrypt_key().await?.into_cipher().ok_or(FxfsError::NoKey)?;
833                let encrypted_name = encrypt_filename(&*dir_key, self.object_id(), name)?;
834                let hash_code = if self.casefold() {
835                    Some(dir_key.hash_code_casefold(name))
836                } else {
837                    dir_key.hash_code(encrypted_name.as_bytes(), name)
838                };
839                cipher.encrypt_symlink(symlink_id, &mut link)?;
840
841                transaction.add(
842                    self.store().store_object_id(),
843                    Mutation::insert_object(
844                        ObjectKey::object(symlink_id),
845                        ObjectValue::encrypted_symlink(link, Timestamp::now(), Timestamp::now(), 0),
846                    ),
847                );
848                transaction.add(
849                    self.store().store_object_id(),
850                    Mutation::insert_object(
851                        ObjectKey::keys(symlink_id),
852                        ObjectValue::keys(vec![(FSCRYPT_KEY_ID, key)].into()),
853                    ),
854                );
855                transaction.add(
856                    self.store().store_object_id(),
857                    Mutation::replace_or_insert_object(
858                        ObjectKey::encrypted_child(self.object_id(), encrypted_name, hash_code),
859                        ObjectValue::child(symlink_id, ObjectDescriptor::Symlink),
860                    ),
861                );
862            } else {
863                return Err(anyhow!("No crypt"));
864            }
865        } else {
866            transaction.add(
867                self.store().store_object_id(),
868                Mutation::insert_object(
869                    ObjectKey::object(symlink_id),
870                    ObjectValue::symlink(link, Timestamp::now(), Timestamp::now(), 0),
871                ),
872            );
873            transaction.add(
874                self.store().store_object_id(),
875                Mutation::replace_or_insert_object(
876                    ObjectKey::child(self.object_id(), &name, self.casefold()),
877                    ObjectValue::child(symlink_id, ObjectDescriptor::Symlink),
878                ),
879            );
880        };
881
882        let now = Timestamp::now();
883        self.update_dir_attributes_internal(
884            transaction,
885            self.object_id(),
886            MutableAttributesInternal {
887                modification_time: Some(now.as_nanos()),
888                change_time: Some(now),
889                ..Default::default()
890            },
891        )
892        .await?;
893        Ok(symlink_id)
894    }
895
896    pub async fn add_child_volume(
897        &self,
898        transaction: &mut Transaction<'_>,
899        volume_name: &str,
900        store_object_id: u64,
901    ) -> Result<(), Error> {
902        ensure!(!self.is_deleted(), FxfsError::Deleted);
903        transaction.add(
904            self.store().store_object_id(),
905            Mutation::replace_or_insert_object(
906                ObjectKey::child(self.object_id(), volume_name, self.casefold()),
907                ObjectValue::child(store_object_id, ObjectDescriptor::Volume),
908            ),
909        );
910        let now = Timestamp::now();
911        self.update_dir_attributes_internal(
912            transaction,
913            self.object_id(),
914            MutableAttributesInternal {
915                modification_time: Some(now.as_nanos()),
916                change_time: Some(now),
917                ..Default::default()
918            },
919        )
920        .await
921    }
922
923    pub fn delete_child_volume<'a>(
924        &self,
925        transaction: &mut Transaction<'a>,
926        volume_name: &str,
927        store_object_id: u64,
928    ) -> Result<(), Error> {
929        ensure!(!self.is_deleted(), FxfsError::Deleted);
930        transaction.add(
931            self.store().store_object_id(),
932            Mutation::replace_or_insert_object(
933                ObjectKey::child(self.object_id(), volume_name, self.casefold()),
934                ObjectValue::None,
935            ),
936        );
937        // We note in the journal that we've deleted the volume. ObjectManager applies this
938        // mutation by forgetting the store. We do it this way to ensure that the store is removed
939        // during replay where there may be mutations to the store prior to its deletion. Without
940        // this, we will try (and fail) to open the store after replay.
941        transaction.add(store_object_id, Mutation::DeleteVolume);
942        Ok(())
943    }
944
945    /// Inserts a child into the directory.
946    ///
947    /// Requires transaction locks on |self|.
948    pub async fn insert_child<'a>(
949        &self,
950        transaction: &mut Transaction<'a>,
951        name: &str,
952        object_id: u64,
953        descriptor: ObjectDescriptor,
954    ) -> Result<(), Error> {
955        ensure!(!self.is_deleted(), FxfsError::Deleted);
956        let sub_dirs_delta = if descriptor == ObjectDescriptor::Directory { 1 } else { 0 };
957        if self.wrapping_key_id.lock().is_some() {
958            let fscrypt_key =
959                self.get_fscrypt_key().await?.into_cipher().ok_or(FxfsError::NoKey)?;
960            let encrypted_name = encrypt_filename(&*fscrypt_key, self.object_id(), name)?;
961            let hash_code = if self.casefold() {
962                Some(fscrypt_key.hash_code_casefold(name))
963            } else {
964                fscrypt_key.hash_code(encrypted_name.as_bytes(), name)
965            };
966            transaction.add(
967                self.store().store_object_id(),
968                Mutation::replace_or_insert_object(
969                    ObjectKey::encrypted_child(self.object_id(), encrypted_name, hash_code),
970                    ObjectValue::child(object_id, descriptor),
971                ),
972            );
973        } else {
974            transaction.add(
975                self.store().store_object_id(),
976                Mutation::replace_or_insert_object(
977                    ObjectKey::child(self.object_id(), &name, self.casefold()),
978                    ObjectValue::child(object_id, descriptor),
979                ),
980            );
981        }
982        let now = Timestamp::now();
983        self.update_dir_attributes_internal(
984            transaction,
985            self.object_id(),
986            MutableAttributesInternal {
987                sub_dirs: sub_dirs_delta,
988                modification_time: Some(now.as_nanos()),
989                change_time: Some(now),
990                ..Default::default()
991            },
992        )
993        .await
994    }
995
996    /// Updates attributes for the directory.
997    /// Nb: The `casefold` attribute is ignored here. It should be set/cleared via `set_casefold()`.
998    pub async fn update_attributes<'a>(
999        &self,
1000        mut transaction: Transaction<'a>,
1001        node_attributes: Option<&fio::MutableNodeAttributes>,
1002        sub_dirs_delta: i64,
1003        change_time: Option<Timestamp>,
1004    ) -> Result<(), Error> {
1005        ensure!(!self.is_deleted(), FxfsError::Deleted);
1006
1007        if sub_dirs_delta != 0 {
1008            let mut mutation =
1009                self.store().txn_get_object_mutation(&transaction, self.object_id()).await?;
1010            if let ObjectValue::Object { kind: ObjectKind::Directory { sub_dirs, .. }, .. } =
1011                &mut mutation.item.value
1012            {
1013                *sub_dirs = sub_dirs.saturating_add_signed(sub_dirs_delta);
1014            } else {
1015                bail!(
1016                    anyhow!(FxfsError::Inconsistent)
1017                        .context("Directory.update_attributes: expected directory object")
1018                );
1019            };
1020
1021            transaction.add(self.store().store_object_id(), Mutation::ObjectStore(mutation));
1022        }
1023
1024        let wrapping_key =
1025            if let Some(fio::MutableNodeAttributes { wrapping_key_id: Some(id), .. }) =
1026                node_attributes
1027            {
1028                Some((*id, self.set_wrapping_key(&mut transaction, *id).await?))
1029            } else {
1030                None
1031            };
1032
1033        // Delegate to the StoreObjectHandle update_attributes for the rest of the updates.
1034        if node_attributes.is_some() || change_time.is_some() {
1035            self.handle.update_attributes(&mut transaction, node_attributes, change_time).await?;
1036        }
1037        transaction
1038            .commit_with_callback(|_| {
1039                if let Some((wrapping_key_id, cipher)) = wrapping_key {
1040                    *self.wrapping_key_id.lock() = Some(wrapping_key_id);
1041                    self.store().key_manager.merge(self.object_id(), |existing| match existing {
1042                        Some(existing) => {
1043                            let mut cipher_set = (**existing).clone();
1044                            cipher_set.add_key(FSCRYPT_KEY_ID, CipherHolder::Cipher(cipher));
1045                            Arc::new(cipher_set)
1046                        }
1047                        None => {
1048                            Arc::new(vec![(FSCRYPT_KEY_ID, CipherHolder::Cipher(cipher))].into())
1049                        }
1050                    });
1051                }
1052            })
1053            .await?;
1054        Ok(())
1055    }
1056
1057    /// Updates attributes set in `mutable_node_attributes`. MutableAttributesInternal can be
1058    /// extended but should never include wrapping_key_id. Useful for object store Directory
1059    /// methods that only have access to a reference to a transaction.
1060    pub async fn update_dir_attributes_internal<'a>(
1061        &self,
1062        transaction: &mut Transaction<'a>,
1063        object_id: u64,
1064        mutable_node_attributes: MutableAttributesInternal,
1065    ) -> Result<(), Error> {
1066        ensure!(!self.is_deleted(), FxfsError::Deleted);
1067
1068        let mut mutation = self.store().txn_get_object_mutation(transaction, object_id).await?;
1069        if let ObjectValue::Object {
1070            kind: ObjectKind::Directory { sub_dirs, .. },
1071            attributes,
1072            ..
1073        } = &mut mutation.item.value
1074        {
1075            if let Some(time) = mutable_node_attributes.modification_time {
1076                attributes.modification_time = Timestamp::from_nanos(time);
1077            }
1078            if let Some(time) = mutable_node_attributes.change_time {
1079                attributes.change_time = time;
1080            }
1081            if mutable_node_attributes.sub_dirs != 0 {
1082                *sub_dirs = sub_dirs.saturating_add_signed(mutable_node_attributes.sub_dirs);
1083            }
1084            if let Some(time) = mutable_node_attributes.creation_time {
1085                attributes.creation_time = Timestamp::from_nanos(time);
1086            }
1087        } else {
1088            bail!(
1089                anyhow!(FxfsError::Inconsistent)
1090                    .context("Directory.update_attributes: expected directory object")
1091            );
1092        };
1093        transaction.add(self.store().store_object_id(), Mutation::ObjectStore(mutation));
1094        Ok(())
1095    }
1096
1097    pub async fn get_properties(&self) -> Result<ObjectProperties, Error> {
1098        if self.is_deleted() {
1099            return Ok(ObjectProperties {
1100                refs: 0,
1101                allocated_size: 0,
1102                data_attribute_size: 0,
1103                creation_time: Timestamp::zero(),
1104                modification_time: Timestamp::zero(),
1105                access_time: Timestamp::zero(),
1106                change_time: Timestamp::zero(),
1107                sub_dirs: 0,
1108                posix_attributes: None,
1109                casefold: false,
1110                wrapping_key_id: None,
1111            });
1112        }
1113
1114        let item = self
1115            .store()
1116            .tree()
1117            .find(&ObjectKey::object(self.object_id()))
1118            .await?
1119            .ok_or(FxfsError::NotFound)?;
1120        match item.value {
1121            ObjectValue::Object {
1122                kind: ObjectKind::Directory { sub_dirs, casefold, wrapping_key_id },
1123                attributes:
1124                    ObjectAttributes {
1125                        creation_time,
1126                        modification_time,
1127                        posix_attributes,
1128                        access_time,
1129                        change_time,
1130                        ..
1131                    },
1132            } => Ok(ObjectProperties {
1133                refs: 1,
1134                allocated_size: 0,
1135                data_attribute_size: 0,
1136                creation_time,
1137                modification_time,
1138                access_time,
1139                change_time,
1140                sub_dirs,
1141                posix_attributes,
1142                casefold,
1143                wrapping_key_id,
1144            }),
1145            _ => {
1146                bail!(
1147                    anyhow!(FxfsError::Inconsistent)
1148                        .context("get_properties: Expected object value")
1149                )
1150            }
1151        }
1152    }
1153
1154    pub async fn list_extended_attributes(&self) -> Result<Vec<Vec<u8>>, Error> {
1155        ensure!(!self.is_deleted(), FxfsError::Deleted);
1156        self.handle.list_extended_attributes().await
1157    }
1158
1159    pub async fn get_extended_attribute(&self, name: Vec<u8>) -> Result<Vec<u8>, Error> {
1160        ensure!(!self.is_deleted(), FxfsError::Deleted);
1161        self.handle.get_extended_attribute(name).await
1162    }
1163
1164    pub async fn set_extended_attribute(
1165        &self,
1166        name: Vec<u8>,
1167        value: Vec<u8>,
1168        mode: SetExtendedAttributeMode,
1169    ) -> Result<(), Error> {
1170        ensure!(!self.is_deleted(), FxfsError::Deleted);
1171        self.handle.set_extended_attribute(name, value, mode).await
1172    }
1173
1174    pub async fn remove_extended_attribute(&self, name: Vec<u8>) -> Result<(), Error> {
1175        ensure!(!self.is_deleted(), FxfsError::Deleted);
1176        self.handle.remove_extended_attribute(name).await
1177    }
1178
1179    /// Returns an iterator that will return directory entries skipping deleted ones.  Example
1180    /// usage:
1181    ///
1182    ///   let layer_set = dir.store().tree().layer_set();
1183    ///   let mut merger = layer_set.merger();
1184    ///   let mut iter = dir.iter(&mut merger).await?;
1185    ///
1186    pub async fn iter<'a, 'b>(
1187        &self,
1188        merger: &'a mut Merger<'b, ObjectKey, ObjectValue>,
1189    ) -> Result<DirectoryIterator<'a, 'b>, Error> {
1190        // It might be tempting to always use `ObjectKeyData::Child` here knowing that it should
1191        // come earlier than any other directory entries, but directories can have extended
1192        // attributes, and `ObjectKeyData::ExtendedAttribute` sorts after `ObjectKeyData::Child` but
1193        // before `ObjectKeyData::EncryptedChild`.
1194        self.iter_from_key(
1195            merger,
1196            &if self.wrapping_key_id.lock().is_some() {
1197                // This will return ObjectKeyData::EncryptedCasefoldChild which sorts before
1198                // ObjectKeyData::EncryptedChild, so this should work even if not an encrypted
1199                // casefold directory.
1200                ObjectKey::encrypted_child(self.object_id(), Vec::new(), Some(0))
1201            } else {
1202                ObjectKey::child(self.object_id(), "", self.casefold())
1203            },
1204        )
1205        .await
1206    }
1207
1208    /// Like `iter`, but seeks from a specific key.
1209    pub async fn iter_from_key<'a, 'b>(
1210        &self,
1211        merger: &'a mut Merger<'b, ObjectKey, ObjectValue>,
1212        key: &ObjectKey,
1213    ) -> Result<DirectoryIterator<'a, 'b>, Error> {
1214        ensure!(!self.is_deleted(), FxfsError::Deleted);
1215
1216        DirectoryIterator::new(
1217            self.object_id(),
1218            merger.query(Query::FullRange(key)).await?,
1219            if self.wrapping_key_id.lock().is_some() {
1220                self.get_fscrypt_key().await?.into_cipher()
1221            } else {
1222                None
1223            },
1224        )
1225        .await
1226    }
1227
1228    /// Like "iter", but seeks from a specific filename (inclusive).  This should *not* be
1229    /// used for encrypted entries, because it won't decrypt entries (and will panic on
1230    /// a debug build).
1231    ///
1232    /// Example usage:
1233    ///
1234    ///   let layer_set = dir.store().tree().layer_set();
1235    ///   let mut merger = layer_set.merger();
1236    ///   let mut iter = dir.iter_from(&mut merger, "foo").await?;
1237    ///
1238    pub async fn iter_from<'a, 'b>(
1239        &self,
1240        merger: &'a mut Merger<'b, ObjectKey, ObjectValue>,
1241        from: &str,
1242    ) -> Result<DirectoryIterator<'a, 'b>, Error> {
1243        debug_assert!(self.wrapping_key_id.lock().is_none());
1244
1245        self.iter_from_key(merger, &ObjectKey::child(self.object_id(), from, self.casefold())).await
1246    }
1247
1248    /// Like "iter_from", but takes bytes which is expected to be a serialized ObjectKey.  This will
1249    /// decrypt encrypted entries if the key is available.  This should *not* be used for
1250    /// unencrypted directories.
1251    pub async fn iter_from_bytes<'a, 'b>(
1252        &self,
1253        merger: &'a mut Merger<'b, ObjectKey, ObjectValue>,
1254        from: &[u8],
1255    ) -> Result<DirectoryIterator<'a, 'b>, Error> {
1256        debug_assert!(self.wrapping_key_id.lock().is_some());
1257
1258        self.iter_from_key(merger, &bincode::deserialize(&from).unwrap()).await
1259    }
1260
1261    /// Skips over directory entries for this directory until `predicate` returns a match.  Returns
1262    /// `false` if there is no match.
1263    async fn advance_until(
1264        &self,
1265        iter: &mut MergerIterator<'_, '_, ObjectKey, ObjectValue>,
1266        predicate: impl Fn(&ObjectKey) -> ControlFlow<bool>,
1267    ) -> Result<bool, Error> {
1268        while let Some(item) = iter.get()
1269            && matches!(
1270                item,
1271                ItemRef { key: ObjectKey { object_id, .. }, .. }
1272                    if *object_id == self.object_id()
1273            )
1274        {
1275            match item {
1276                // Skip deleted items.
1277                ItemRef { value: ObjectValue::None, .. } => {}
1278                ItemRef { key, .. } => match predicate(key) {
1279                    ControlFlow::Continue(()) => {}
1280                    ControlFlow::Break(result) => return Ok(result),
1281                },
1282            }
1283            iter.advance().await?
1284        }
1285        Ok(false)
1286    }
1287
1288    /// Returns the starting key and an optional predicate (where an iteration is required) to be
1289    /// used when the cipher is unavailable.
1290    fn get_key_and_predicate_for_unavailable_cipher<'a>(
1291        &self,
1292        key_type: KeyType,
1293        proxy_name: &'a ProxyFilename,
1294    ) -> (ObjectKey, Option<BoxPredicate<'a>>) {
1295        if key_type == KeyType::Fxfs || self.casefold() {
1296            (
1297                ObjectKey::encrypted_child(
1298                    self.object_id(),
1299                    proxy_name.raw_filename().to_vec(),
1300                    Some(proxy_name.hash_code as u32),
1301                ),
1302                proxy_name
1303                    .is_truncated()
1304                    .then(|| Box::new(long_proxy_prefix_casefold_predicate(&proxy_name)) as Box<_>),
1305            )
1306        } else {
1307            (
1308                ObjectKey::encrypted_child(
1309                    self.object_id(),
1310                    proxy_name.raw_filename().to_vec(),
1311                    None,
1312                ),
1313                proxy_name
1314                    .is_truncated()
1315                    .then(|| Box::new(long_proxy_prefix_predicate(&proxy_name)) as Box<_>),
1316            )
1317        }
1318    }
1319}
1320
1321/// Used to find an encrypted casefold entry when the cipher is available.
1322fn encrypted_casefold_predicate<'a>(
1323    cipher: &'a dyn Cipher,
1324    object_id: u64,
1325    target_hash_code: u32,
1326    name: &'a str,
1327) -> impl Fn(&ObjectKey) -> ControlFlow<bool> + 'a {
1328    move |key| match key {
1329        ObjectKey {
1330            data:
1331                ObjectKeyData::EncryptedCasefoldChild(EncryptedCasefoldChild {
1332                    hash_code,
1333                    name: encrypted_name,
1334                }),
1335            ..
1336        } if *hash_code == target_hash_code => {
1337            let decrypted_name = decrypt_filename(cipher, object_id, encrypted_name);
1338            match decrypted_name {
1339                Ok(decrypted_name) => {
1340                    if fxfs_unicode::casefold_cmp(name, &decrypted_name)
1341                        == std::cmp::Ordering::Equal
1342                    {
1343                        ControlFlow::Break(true)
1344                    } else {
1345                        ControlFlow::Continue(())
1346                    }
1347                }
1348                Err(_) => ControlFlow::Continue(()),
1349            }
1350        }
1351        _ => ControlFlow::Break(false),
1352    }
1353}
1354
1355/// Used when a long proxy prefix is used with case folding.
1356fn long_proxy_prefix_casefold_predicate(
1357    proxy_name: &ProxyFilename,
1358) -> impl Fn(&ObjectKey) -> ControlFlow<bool> + '_ {
1359    move |key| match key {
1360        ObjectKey {
1361            data: ObjectKeyData::EncryptedCasefoldChild(EncryptedCasefoldChild { hash_code, name }),
1362            ..
1363        } if *hash_code as u64 == proxy_name.hash_code
1364            && name.starts_with(&proxy_name.filename) =>
1365        {
1366            if ProxyFilename::compute_sha256(&name) == proxy_name.sha256 {
1367                ControlFlow::Break(true)
1368            } else {
1369                ControlFlow::Continue(())
1370            }
1371        }
1372        _ => ControlFlow::Break(false),
1373    }
1374}
1375
1376/// Used when a long proxy prefix is used without case folding.
1377fn long_proxy_prefix_predicate(
1378    proxy_name: &ProxyFilename,
1379) -> impl Fn(&ObjectKey) -> ControlFlow<bool> + '_ {
1380    move |key| match key {
1381        ObjectKey { data: ObjectKeyData::EncryptedChild(EncryptedChild(name)), .. }
1382            if name.starts_with(&proxy_name.filename) =>
1383        {
1384            if ProxyFilename::compute_hash_code(name) == proxy_name.hash_code
1385                && ProxyFilename::compute_sha256(name) == proxy_name.sha256
1386            {
1387                ControlFlow::Break(true)
1388            } else {
1389                ControlFlow::Continue(())
1390            }
1391        }
1392        _ => ControlFlow::Break(false),
1393    }
1394}
1395
1396impl<S: HandleOwner> fmt::Debug for Directory<S> {
1397    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1398        f.debug_struct("Directory")
1399            .field("store_id", &self.store().store_object_id())
1400            .field("object_id", &self.object_id())
1401            .finish()
1402    }
1403}
1404
1405pub struct DirectoryIterator<'a, 'b> {
1406    object_id: u64,
1407    iter: MergerIterator<'a, 'b, ObjectKey, ObjectValue>,
1408    cipher: Option<Arc<dyn Cipher>>,
1409    // Holds decrypted or proxy filenames so we can return a reference from get().
1410    filename: Option<String>,
1411}
1412
1413impl<'a, 'b> DirectoryIterator<'a, 'b> {
1414    pub async fn new(
1415        object_id: u64,
1416        iter: MergerIterator<'a, 'b, ObjectKey, ObjectValue>,
1417        cipher: Option<Arc<dyn Cipher>>,
1418    ) -> Result<Self, Error> {
1419        let mut this = DirectoryIterator { object_id, iter, cipher, filename: None };
1420        this.init_item().await?;
1421        Ok(this)
1422    }
1423
1424    pub fn get(&self) -> Option<(&str, u64, &ObjectDescriptor)> {
1425        match self.iter.get() {
1426            Some(ItemRef {
1427                key: ObjectKey { object_id: oid, data: ObjectKeyData::Child { name } },
1428                value: ObjectValue::Child(ChildValue { object_id, object_descriptor }),
1429                ..
1430            }) if *oid == self.object_id => Some((&name, *object_id, object_descriptor)),
1431            Some(ItemRef {
1432                key: ObjectKey { object_id: oid, data: ObjectKeyData::CasefoldChild { name } },
1433                value: ObjectValue::Child(ChildValue { object_id, object_descriptor }),
1434                ..
1435            }) if *oid == self.object_id => Some((&name, *object_id, object_descriptor)),
1436            Some(ItemRef {
1437                key: ObjectKey { object_id: oid, data: ObjectKeyData::EncryptedChild(_) },
1438                value: ObjectValue::Child(ChildValue { object_id, object_descriptor }),
1439                ..
1440            }) if *oid == self.object_id => {
1441                Some((self.filename.as_ref().unwrap(), *object_id, object_descriptor))
1442            }
1443            Some(ItemRef {
1444                key: ObjectKey { object_id: oid, data: ObjectKeyData::EncryptedCasefoldChild(_) },
1445                value: ObjectValue::Child(ChildValue { object_id, object_descriptor }),
1446                ..
1447            }) if *oid == self.object_id => {
1448                Some((self.filename.as_ref().unwrap(), *object_id, object_descriptor))
1449            }
1450            _ => None,
1451        }
1452    }
1453
1454    pub async fn advance(&mut self) -> Result<(), Error> {
1455        self.iter.advance().await?;
1456        self.init_item().await
1457    }
1458
1459    /// Returns a traversal position.
1460    pub fn traversal_position<R>(
1461        &self,
1462        name_visitor: impl FnOnce(&str) -> R,
1463        bytes_visitor: impl FnOnce(Box<[u8]>) -> R,
1464    ) -> Option<R> {
1465        match self.iter.get() {
1466            Some(ItemRef {
1467                key: ObjectKey { object_id: oid, data: ObjectKeyData::Child { name } },
1468                ..
1469            }) if *oid == self.object_id => Some(name_visitor(name)),
1470            Some(ItemRef {
1471                key: ObjectKey { object_id: oid, data: ObjectKeyData::CasefoldChild { name } },
1472                ..
1473            }) if *oid == self.object_id => Some(name_visitor(&name)),
1474            Some(ItemRef {
1475                key:
1476                    key @ ObjectKey {
1477                        object_id: oid,
1478                        data:
1479                            ObjectKeyData::EncryptedChild { .. }
1480                            | ObjectKeyData::EncryptedCasefoldChild { .. },
1481                    },
1482                ..
1483            }) if *oid == self.object_id => {
1484                Some(bytes_visitor(bincode::serialize(key).unwrap().into()))
1485            }
1486            _ => None,
1487        }
1488    }
1489
1490    /// Called to initialize the item after the iterator has moved.
1491    async fn init_item(&mut self) -> Result<(), Error> {
1492        loop {
1493            match self.iter.get() {
1494                Some(ItemRef {
1495                    key: ObjectKey { object_id, .. },
1496                    value: ObjectValue::None,
1497                    ..
1498                }) if *object_id == self.object_id => {}
1499                Some(ItemRef {
1500                    key:
1501                        ObjectKey {
1502                            object_id,
1503                            data:
1504                                ObjectKeyData::EncryptedCasefoldChild(EncryptedCasefoldChild {
1505                                    hash_code,
1506                                    name,
1507                                }),
1508                        },
1509                    value: ObjectValue::Child(_),
1510                    ..
1511                }) if *object_id == self.object_id => {
1512                    // We decrypt filenames on advance. This allows us to return errors on bad data
1513                    // and avoids repeated work if the user calls get() more than once.
1514                    self.update_encrypted_filename(Some(*hash_code), name.clone())?;
1515                    return Ok(());
1516                }
1517                Some(ItemRef {
1518                    key:
1519                        ObjectKey {
1520                            object_id,
1521                            data: ObjectKeyData::EncryptedChild(EncryptedChild(name)),
1522                        },
1523                    value: ObjectValue::Child(_),
1524                    ..
1525                }) if *object_id == self.object_id => {
1526                    // We decrypt filenames on advance. This allows us to return errors on bad data
1527                    // and avoids repeated work if the user calls get() more than once.
1528                    self.update_encrypted_filename(None, name.clone())?;
1529                    return Ok(());
1530                }
1531                _ => return Ok(()),
1532            }
1533            self.iter.advance().await?;
1534        }
1535    }
1536
1537    // For encrypted children, we calculate the filename once and cache it.  This function is called
1538    // to update that cached name.
1539    fn update_encrypted_filename(
1540        &mut self,
1541        hash_code: Option<u32>,
1542        mut name: Vec<u8>,
1543    ) -> Result<(), Error> {
1544        if let Some(cipher) = &self.cipher {
1545            cipher.decrypt_filename(self.object_id, &mut name)?;
1546            self.filename = Some(String::from_utf8(name).map_err(|_| {
1547                anyhow!(FxfsError::Internal).context("Bad UTF-8 encrypted filename")
1548            })?);
1549        } else if let Some(hash_code) = hash_code {
1550            self.filename = Some(ProxyFilename::new_with_hash_code(hash_code as u64, &name).into());
1551        } else {
1552            self.filename = Some(ProxyFilename::new(&name).into());
1553        }
1554        Ok(())
1555    }
1556}
1557
1558/// Return type for |replace_child| describing the object which was replaced. The u64 fields are all
1559/// object_ids.
1560#[derive(Debug)]
1561pub enum ReplacedChild {
1562    None,
1563    // "Object" can be a file or symbolic link, but not a directory.
1564    Object(u64),
1565    ObjectWithRemainingLinks(u64),
1566    Directory(u64),
1567}
1568
1569/// Moves src.0/src.1 to dst.0/dst.1.
1570///
1571/// If |dst.0| already has a child |dst.1|, it is removed from dst.0.  For files, if this was their
1572/// last reference, the file is moved to the graveyard.  For directories, the removed directory will
1573/// be deleted permanently (and must be empty).
1574///
1575/// If |src| is None, this is effectively the same as unlink(dst.0/dst.1).
1576pub async fn replace_child<'a, S: HandleOwner>(
1577    transaction: &mut Transaction<'a>,
1578    src: Option<(&'a Directory<S>, &str)>,
1579    dst: (&'a Directory<S>, &str),
1580) -> Result<ReplacedChild, Error> {
1581    let mut sub_dirs_delta: i64 = 0;
1582    let now = Timestamp::now();
1583
1584    let src = if let Some((src_dir, src_name)) = src {
1585        let store_id = dst.0.store().store_object_id();
1586        assert_eq!(store_id, src_dir.store().store_object_id());
1587        match (src_dir.wrapping_key_id(), dst.0.wrapping_key_id()) {
1588            (Some(src_id), Some(dst_id)) => {
1589                ensure!(src_id == dst_id, FxfsError::NotSupported);
1590                // Renames only work on unlocked encrypted directories. Fail rename if src is
1591                // locked.
1592                let key = src_dir.get_fscrypt_key().await?.into_cipher().ok_or(FxfsError::NoKey)?;
1593                let encrypted_src_name = encrypt_filename(&*key, src_dir.object_id(), src_name)?;
1594                let src_hash_code = if src_dir.casefold() {
1595                    Some(key.hash_code_casefold(src_name))
1596                } else {
1597                    key.hash_code(encrypted_src_name.as_bytes(), src_name)
1598                };
1599                transaction.add(
1600                    store_id,
1601                    Mutation::replace_or_insert_object(
1602                        ObjectKey::encrypted_child(
1603                            src_dir.object_id(),
1604                            encrypted_src_name,
1605                            src_hash_code,
1606                        ),
1607                        ObjectValue::None,
1608                    ),
1609                );
1610            }
1611            (None, None) => {
1612                transaction.add(
1613                    store_id,
1614                    Mutation::replace_or_insert_object(
1615                        ObjectKey::child(src_dir.object_id(), src_name, src_dir.casefold()),
1616                        ObjectValue::None,
1617                    ),
1618                );
1619            }
1620            // TODO: https://fxbug.dev/360172175: Support renames out of encrypted directories.
1621            _ => bail!(FxfsError::NotSupported),
1622        }
1623        let (id, descriptor, _) = src_dir.lookup(src_name).await?.ok_or(FxfsError::NotFound)?;
1624        src_dir.store().update_attributes(transaction, id, None, Some(now)).await?;
1625        if src_dir.object_id() != dst.0.object_id() {
1626            sub_dirs_delta = if descriptor == ObjectDescriptor::Directory { 1 } else { 0 };
1627            src_dir
1628                .update_dir_attributes_internal(
1629                    transaction,
1630                    src_dir.object_id(),
1631                    MutableAttributesInternal {
1632                        sub_dirs: -sub_dirs_delta,
1633                        modification_time: Some(now.as_nanos()),
1634                        change_time: Some(now),
1635                        ..Default::default()
1636                    },
1637                )
1638                .await?;
1639        }
1640        Some((id, descriptor))
1641    } else {
1642        None
1643    };
1644    replace_child_with_object(transaction, src, dst, sub_dirs_delta, now).await
1645}
1646
1647/// Replaces dst.0/dst.1 with the given object, or unlinks if `src` is None.
1648///
1649/// If |dst.0| already has a child |dst.1|, it is removed from dst.0.  For files, if this was their
1650/// last reference, the file is moved to the graveyard.  For directories, the removed directory will
1651/// be moved to the graveyard (and must be empty).  The caller is responsible for tombstoning files
1652/// (when it is no longer open) and directories (immediately after committing the transaction).
1653///
1654/// `sub_dirs_delta` can be used if `src` is a directory and happened to already be a child of
1655/// `dst`.
1656pub async fn replace_child_with_object<'a, S: HandleOwner>(
1657    transaction: &mut Transaction<'a>,
1658    src: Option<(u64, ObjectDescriptor)>,
1659    dst: (&'a Directory<S>, &str),
1660    mut sub_dirs_delta: i64,
1661    timestamp: Timestamp,
1662) -> Result<ReplacedChild, Error> {
1663    let deleted_id_and_descriptor = dst.0.lookup(dst.1).await?;
1664    let store_id = dst.0.store().store_object_id();
1665    // There might be optimizations here that allow us to skip the graveyard where we can delete an
1666    // object in a single transaction (which should be the common case).
1667    let result = match deleted_id_and_descriptor {
1668        Some((old_id, ObjectDescriptor::File | ObjectDescriptor::Symlink, _)) => {
1669            let was_last_ref = dst.0.store().adjust_refs(transaction, old_id, -1).await?;
1670            dst.0.store().update_attributes(transaction, old_id, None, Some(timestamp)).await?;
1671            if was_last_ref {
1672                ReplacedChild::Object(old_id)
1673            } else {
1674                ReplacedChild::ObjectWithRemainingLinks(old_id)
1675            }
1676        }
1677        Some((old_id, ObjectDescriptor::Directory, _)) => {
1678            let dir = Directory::open(&dst.0.owner(), old_id).await?;
1679            if dir.has_children().await? {
1680                bail!(FxfsError::NotEmpty);
1681            }
1682            // Directories might have extended attributes which might require multiple transactions
1683            // to delete, so we delete directories via the graveyard.
1684            dst.0.store().add_to_graveyard(transaction, old_id);
1685            sub_dirs_delta -= 1;
1686            ReplacedChild::Directory(old_id)
1687        }
1688        Some((_, ObjectDescriptor::Volume, _)) => {
1689            bail!(anyhow!(FxfsError::Inconsistent).context("Unexpected volume child"))
1690        }
1691        None => {
1692            if src.is_none() {
1693                // Neither src nor dst exist
1694                bail!(FxfsError::NotFound);
1695            }
1696            ReplacedChild::None
1697        }
1698    };
1699    let new_value = match src {
1700        Some((id, descriptor)) => ObjectValue::child(id, descriptor),
1701        None => ObjectValue::None,
1702    };
1703    if dst.0.wrapping_key_id().is_some() {
1704        match dst.0.get_fscrypt_key().await? {
1705            CipherHolder::Cipher(cipher) => {
1706                let encrypted_dst_name = encrypt_filename(&*cipher, dst.0.object_id(), dst.1)?;
1707                let dst_hash_code = if dst.0.casefold() {
1708                    Some(cipher.hash_code_casefold(dst.1))
1709                } else {
1710                    cipher.hash_code(encrypted_dst_name.as_bytes(), dst.1)
1711                };
1712                transaction.add(
1713                    store_id,
1714                    Mutation::replace_or_insert_object(
1715                        ObjectKey::encrypted_child(
1716                            dst.0.object_id(),
1717                            encrypted_dst_name,
1718                            dst_hash_code,
1719                        ),
1720                        new_value,
1721                    ),
1722                );
1723            }
1724            CipherHolder::Unavailable(key_type) => {
1725                if !matches!(new_value, ObjectValue::None) {
1726                    // unlinks are permitted but renames are not allowed for locked directories.
1727                    bail!(FxfsError::NoKey);
1728                }
1729
1730                let proxy_filename: ProxyFilename = dst.1.try_into().unwrap_or_default();
1731
1732                let layer_set = dst.0.store().tree().layer_set();
1733                let mut merger = layer_set.merger();
1734                let (key, predicate) =
1735                    dst.0.get_key_and_predicate_for_unavailable_cipher(key_type, &proxy_filename);
1736                let mut iter = merger.query(Query::FullRange(&key)).await?;
1737
1738                if let Some(predicate) = predicate {
1739                    if !dst.0.advance_until(&mut iter, predicate).await? {
1740                        bail!(FxfsError::NotFound);
1741                    }
1742                } else if iter
1743                    .get()
1744                    .is_none_or(|item| item.key != &key || item.value == &ObjectValue::None)
1745                {
1746                    bail!(FxfsError::NotFound);
1747                }
1748
1749                transaction.add(
1750                    store_id,
1751                    Mutation::replace_or_insert_object(iter.get().unwrap().key.clone(), new_value),
1752                );
1753            }
1754        }
1755    } else {
1756        transaction.add(
1757            store_id,
1758            Mutation::replace_or_insert_object(
1759                ObjectKey::child(dst.0.object_id(), dst.1, dst.0.casefold()),
1760                new_value,
1761            ),
1762        );
1763    }
1764    dst.0
1765        .update_dir_attributes_internal(
1766            transaction,
1767            dst.0.object_id(),
1768            MutableAttributesInternal {
1769                sub_dirs: sub_dirs_delta,
1770                modification_time: Some(timestamp.as_nanos()),
1771                change_time: Some(timestamp),
1772                ..Default::default()
1773            },
1774        )
1775        .await?;
1776    Ok(result)
1777}
1778
1779#[cfg(test)]
1780mod tests {
1781    use super::{ProxyFilename, encrypt_filename, replace_child_with_object};
1782    use crate::errors::FxfsError;
1783    use crate::filesystem::{FxFilesystem, JournalingObject, SyncOptions};
1784    use crate::object_handle::{ObjectHandle, ReadObjectHandle, WriteObjectHandle};
1785    use crate::object_store::directory::{
1786        Directory, MutableAttributesInternal, ReplacedChild, replace_child,
1787    };
1788    use crate::object_store::object_record::{ObjectKey, ObjectValue, Timestamp};
1789    use crate::object_store::transaction::{Options, lock_keys};
1790    use crate::object_store::volume::root_volume;
1791    use crate::object_store::{
1792        HandleOptions, LockKey, NewChildStoreOptions, ObjectDescriptor, ObjectKind, ObjectStore,
1793        SetExtendedAttributeMode, StoreObjectHandle, StoreOptions,
1794    };
1795    use anyhow::Error;
1796    use assert_matches::assert_matches;
1797    use fidl_fuchsia_io as fio;
1798    use fxfs_crypt_common::CryptBase;
1799    use fxfs_crypto::{Cipher, Crypt, WrappingKeyId};
1800    use fxfs_insecure_crypto::new_insecure_crypt;
1801    use std::collections::{BTreeMap, HashSet};
1802    use std::future::poll_fn;
1803    use std::sync::Arc;
1804    use std::task::Poll;
1805    use storage_device::DeviceHolder;
1806    use storage_device::fake_device::FakeDevice;
1807    use test_case::test_case;
1808
1809    const TEST_DEVICE_BLOCK_SIZE: u32 = 512;
1810    const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(2);
1811
1812    /// The synthetic symlink we return when locked is not usable for anything but we still want
1813    /// it to match that returned by fscrypt so we will verify here that we get back the
1814    /// expected ProxyFilename-derived link content.
1815    #[fuchsia::test]
1816    async fn test_reopen_with_different_crypt_shows_proxy_name() -> Result<(), Error> {
1817        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1818        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1819        let symlink_object_id;
1820        {
1821            let crypt = Arc::new(new_insecure_crypt());
1822            crypt
1823                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
1824                .expect("add_wrapping_key failed");
1825            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
1826            let store = root_volume
1827                .new_volume(
1828                    "test",
1829                    NewChildStoreOptions {
1830                        options: StoreOptions {
1831                            crypt: Some(crypt.clone() as Arc<dyn Crypt>),
1832                            ..StoreOptions::default()
1833                        },
1834                        ..Default::default()
1835                    },
1836                )
1837                .await
1838                .expect("new_volume failed");
1839            let mut transaction = fs
1840                .clone()
1841                .new_transaction(
1842                    lock_keys![LockKey::object(
1843                        store.store_object_id(),
1844                        store.root_directory_object_id()
1845                    )],
1846                    Options::default(),
1847                )
1848                .await
1849                .expect("new_transaction failed");
1850            let root_dir = Directory::open(&store, store.root_directory_object_id())
1851                .await
1852                .expect("open failed");
1853            let _ = root_dir.set_wrapping_key(&mut transaction, WRAPPING_KEY_ID).await?;
1854            transaction.commit().await.unwrap();
1855
1856            let mut transaction = fs
1857                .clone()
1858                .new_transaction(
1859                    lock_keys![LockKey::object(
1860                        store.store_object_id(),
1861                        store.root_directory_object_id()
1862                    )],
1863                    Options::default(),
1864                )
1865                .await
1866                .expect("new_transaction failed");
1867            let root_dir = Directory::open(&store, store.root_directory_object_id())
1868                .await
1869                .expect("open failed");
1870            symlink_object_id = root_dir
1871                .create_symlink(&mut transaction, b"some_link_text", "a")
1872                .await
1873                .expect("create_symlink failed");
1874            transaction.commit().await.expect("commit failed");
1875        };
1876        fs.close().await.expect("close failed");
1877        let device = fs.take_device().await;
1878        device.reopen(false);
1879
1880        let fs = FxFilesystem::open(device).await.expect("open failed");
1881        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
1882        // Open the volume without providing the keys.
1883        let store = root_volume
1884            .volume(
1885                "test",
1886                StoreOptions {
1887                    crypt: Some(Arc::new(new_insecure_crypt())),
1888                    ..StoreOptions::default()
1889                },
1890            )
1891            .await
1892            .expect("volume failed");
1893
1894        let item = store
1895            .tree()
1896            .find(&ObjectKey::object(symlink_object_id))
1897            .await
1898            .expect("find failed")
1899            .expect("found record");
1900        let raw_link = match item.value {
1901            ObjectValue::Object { kind: ObjectKind::EncryptedSymlink { link, .. }, .. } => link,
1902            _ => panic!("Unexpected item {item:?}"),
1903        };
1904        let symlink_target = store.read_symlink(symlink_object_id).await?;
1905        // Locked symlinks always have hash_code of zero.
1906        let expected_symlink_target: String =
1907            ProxyFilename::new_with_hash_code(0, &raw_link).into();
1908        assert_eq!(symlink_target, expected_symlink_target.as_bytes());
1909
1910        fs.close().await.expect("Close failed");
1911        Ok(())
1912    }
1913
1914    async fn yield_to_executor() {
1915        let mut done = false;
1916        poll_fn(|cx| {
1917            if done {
1918                Poll::Ready(())
1919            } else {
1920                done = true;
1921                cx.waker().wake_by_ref();
1922                Poll::Pending
1923            }
1924        })
1925        .await;
1926    }
1927
1928    #[fuchsia::test]
1929    async fn test_create_directory() {
1930        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1931        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1932        let object_id = {
1933            let mut transaction = fs
1934                .clone()
1935                .new_transaction(lock_keys![], Options::default())
1936                .await
1937                .expect("new_transaction failed");
1938            let dir = Directory::create(&mut transaction, &fs.root_store(), None)
1939                .await
1940                .expect("create failed");
1941
1942            let child_dir = dir
1943                .create_child_dir(&mut transaction, "foo")
1944                .await
1945                .expect("create_child_dir failed");
1946            let _child_dir_file = child_dir
1947                .create_child_file(&mut transaction, "bar")
1948                .await
1949                .expect("create_child_file failed");
1950            let _child_file = dir
1951                .create_child_file(&mut transaction, "baz")
1952                .await
1953                .expect("create_child_file failed");
1954            dir.add_child_volume(&mut transaction, "corge", 100)
1955                .await
1956                .expect("add_child_volume failed");
1957            transaction.commit().await.expect("commit failed");
1958            fs.sync(SyncOptions::default()).await.expect("sync failed");
1959            dir.object_id()
1960        };
1961        fs.close().await.expect("Close failed");
1962        let device = fs.take_device().await;
1963        device.reopen(false);
1964        let fs = FxFilesystem::open(device).await.expect("open failed");
1965        {
1966            let dir = Directory::open(&fs.root_store(), object_id).await.expect("open failed");
1967            let (object_id, object_descriptor, _) =
1968                dir.lookup("foo").await.expect("lookup failed").expect("not found");
1969            assert_eq!(object_descriptor, ObjectDescriptor::Directory);
1970            let child_dir =
1971                Directory::open(&fs.root_store(), object_id).await.expect("open failed");
1972            let (object_id, object_descriptor, _) =
1973                child_dir.lookup("bar").await.expect("lookup failed").expect("not found");
1974            assert_eq!(object_descriptor, ObjectDescriptor::File);
1975            let _child_dir_file = ObjectStore::open_object(
1976                &fs.root_store(),
1977                object_id,
1978                HandleOptions::default(),
1979                None,
1980            )
1981            .await
1982            .expect("open object failed");
1983            let (object_id, object_descriptor, _) =
1984                dir.lookup("baz").await.expect("lookup failed").expect("not found");
1985            assert_eq!(object_descriptor, ObjectDescriptor::File);
1986            let _child_file = ObjectStore::open_object(
1987                &fs.root_store(),
1988                object_id,
1989                HandleOptions::default(),
1990                None,
1991            )
1992            .await
1993            .expect("open object failed");
1994            let (object_id, object_descriptor, _) =
1995                dir.lookup("corge").await.expect("lookup failed").expect("not found");
1996            assert_eq!(object_id, 100);
1997            if let ObjectDescriptor::Volume = object_descriptor {
1998            } else {
1999                panic!("wrong ObjectDescriptor");
2000            }
2001
2002            assert_eq!(dir.lookup("qux").await.expect("lookup failed"), None);
2003        }
2004        fs.close().await.expect("Close failed");
2005    }
2006
2007    #[fuchsia::test]
2008    async fn test_set_wrapping_key_does_not_exist() {
2009        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
2010        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2011        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2012        let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
2013        let store = root_volume
2014            .new_volume(
2015                "test",
2016                NewChildStoreOptions {
2017                    options: StoreOptions {
2018                        crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2019                        ..StoreOptions::default()
2020                    },
2021                    ..NewChildStoreOptions::default()
2022                },
2023            )
2024            .await
2025            .expect("new_volume failed");
2026
2027        let mut transaction = fs
2028            .clone()
2029            .new_transaction(
2030                lock_keys![LockKey::object(
2031                    store.store_object_id(),
2032                    store.root_directory_object_id()
2033                )],
2034                Options::default(),
2035            )
2036            .await
2037            .expect("new transaction failed");
2038        let root_directory =
2039            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
2040        let directory = root_directory
2041            .create_child_dir(&mut transaction, "foo")
2042            .await
2043            .expect("create_child_dir failed");
2044        transaction.commit().await.expect("commit failed");
2045        let mut transaction = fs
2046            .clone()
2047            .new_transaction(
2048                lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2049                Options::default(),
2050            )
2051            .await
2052            .expect("new transaction failed");
2053        directory
2054            .set_wrapping_key(&mut transaction, WRAPPING_KEY_ID)
2055            .await
2056            .expect_err("wrapping key id 2 has not been added");
2057        transaction.commit().await.expect("commit failed");
2058        crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).expect("add_wrapping_key failed");
2059        let mut transaction = fs
2060            .clone()
2061            .new_transaction(
2062                lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2063                Options::default(),
2064            )
2065            .await
2066            .expect("new transaction failed");
2067        directory
2068            .set_wrapping_key(&mut transaction, WRAPPING_KEY_ID)
2069            .await
2070            .expect("wrapping key id 2 has been added");
2071        fs.close().await.expect("Close failed");
2072    }
2073
2074    #[fuchsia::test]
2075    async fn test_set_encryption_policy_on_unencrypted_nonempty_dir() {
2076        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
2077        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2078        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2079        let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
2080        let store = root_volume
2081            .new_volume(
2082                "test",
2083                NewChildStoreOptions {
2084                    options: StoreOptions {
2085                        crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2086                        ..StoreOptions::default()
2087                    },
2088                    ..NewChildStoreOptions::default()
2089                },
2090            )
2091            .await
2092            .expect("new_volume failed");
2093
2094        let mut transaction = fs
2095            .clone()
2096            .new_transaction(
2097                lock_keys![LockKey::object(
2098                    store.store_object_id(),
2099                    store.root_directory_object_id()
2100                )],
2101                Options::default(),
2102            )
2103            .await
2104            .expect("new transaction failed");
2105        let root_directory =
2106            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
2107        let directory = root_directory
2108            .create_child_dir(&mut transaction, "foo")
2109            .await
2110            .expect("create_child_dir failed");
2111        let _file = directory
2112            .create_child_file(&mut transaction, "bar")
2113            .await
2114            .expect("create_child_file failed");
2115        transaction.commit().await.expect("commit failed");
2116        let mut transaction = fs
2117            .clone()
2118            .new_transaction(
2119                lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2120                Options::default(),
2121            )
2122            .await
2123            .expect("new transaction failed");
2124        directory
2125            .set_wrapping_key(&mut transaction, WRAPPING_KEY_ID)
2126            .await
2127            .expect_err("directory is not empty");
2128        transaction.commit().await.expect("commit failed");
2129        fs.close().await.expect("Close failed");
2130    }
2131
2132    #[fuchsia::test]
2133    async fn test_create_file_or_subdir_in_locked_directory() {
2134        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
2135        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2136        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2137        let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
2138        let store = root_volume
2139            .new_volume(
2140                "test",
2141                NewChildStoreOptions {
2142                    options: StoreOptions {
2143                        crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2144                        ..StoreOptions::default()
2145                    },
2146                    ..NewChildStoreOptions::default()
2147                },
2148            )
2149            .await
2150            .expect("new_volume failed");
2151
2152        let mut transaction = fs
2153            .clone()
2154            .new_transaction(
2155                lock_keys![LockKey::object(
2156                    store.store_object_id(),
2157                    store.root_directory_object_id()
2158                )],
2159                Options::default(),
2160            )
2161            .await
2162            .expect("new transaction failed");
2163        let root_directory =
2164            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
2165        let directory = root_directory
2166            .create_child_dir(&mut transaction, "foo")
2167            .await
2168            .expect("create_child_dir failed");
2169        transaction.commit().await.expect("commit failed");
2170        crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).expect("add_wrapping_key failed");
2171        let transaction = fs
2172            .clone()
2173            .new_transaction(
2174                lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2175                Options::default(),
2176            )
2177            .await
2178            .expect("new transaction failed");
2179        directory
2180            .update_attributes(
2181                transaction,
2182                Some(&fio::MutableNodeAttributes {
2183                    wrapping_key_id: Some(WRAPPING_KEY_ID),
2184                    ..Default::default()
2185                }),
2186                0,
2187                None,
2188            )
2189            .await
2190            .expect("update attributes failed");
2191        crypt.forget_wrapping_key(&WRAPPING_KEY_ID).expect("forget wrapping key failed");
2192        let mut transaction = fs
2193            .clone()
2194            .new_transaction(
2195                lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2196                Options::default(),
2197            )
2198            .await
2199            .expect("new transaction failed");
2200        directory
2201            .create_child_dir(&mut transaction, "bar")
2202            .await
2203            .expect_err("cannot create a dir inside of a locked encrypted directory");
2204        directory
2205            .create_child_file(&mut transaction, "baz")
2206            .await
2207            .map(|_| ())
2208            .expect_err("cannot create a file inside of a locked encrypted directory");
2209        fs.close().await.expect("Close failed");
2210    }
2211
2212    #[fuchsia::test]
2213    async fn test_replace_child_with_object_in_locked_directory() {
2214        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
2215        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2216        let crypt = Arc::new(new_insecure_crypt());
2217
2218        let (parent_oid, src_oid, dst_oid) = {
2219            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2220            let store = root_volume
2221                .new_volume(
2222                    "test",
2223                    NewChildStoreOptions {
2224                        options: StoreOptions {
2225                            crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2226                            ..StoreOptions::default()
2227                        },
2228                        ..Default::default()
2229                    },
2230                )
2231                .await
2232                .expect("new_volume failed");
2233
2234            let mut transaction = fs
2235                .clone()
2236                .new_transaction(
2237                    lock_keys![LockKey::object(
2238                        store.store_object_id(),
2239                        store.root_directory_object_id()
2240                    )],
2241                    Options::default(),
2242                )
2243                .await
2244                .expect("new transaction failed");
2245            let root_directory = Directory::open(&store, store.root_directory_object_id())
2246                .await
2247                .expect("open failed");
2248            let directory = root_directory
2249                .create_child_dir(&mut transaction, "foo")
2250                .await
2251                .expect("create_child_dir failed");
2252            transaction.commit().await.expect("commit failed");
2253            crypt
2254                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2255                .expect("add_wrapping_key failed");
2256            let transaction = fs
2257                .clone()
2258                .new_transaction(
2259                    lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2260                    Options::default(),
2261                )
2262                .await
2263                .expect("new transaction failed");
2264            directory
2265                .update_attributes(
2266                    transaction,
2267                    Some(&fio::MutableNodeAttributes {
2268                        wrapping_key_id: Some(WRAPPING_KEY_ID),
2269                        ..Default::default()
2270                    }),
2271                    0,
2272                    None,
2273                )
2274                .await
2275                .expect("update attributes failed");
2276            let mut transaction = fs
2277                .clone()
2278                .new_transaction(
2279                    lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2280                    Options::default(),
2281                )
2282                .await
2283                .expect("new transaction failed");
2284            let src_child = directory
2285                .create_child_dir(&mut transaction, "fee")
2286                .await
2287                .expect("create_child_dir failed");
2288            let dst_child = directory
2289                .create_child_dir(&mut transaction, "faa")
2290                .await
2291                .expect("create_child_dir failed");
2292            transaction.commit().await.expect("commit failed");
2293            crypt.forget_wrapping_key(&WRAPPING_KEY_ID).expect("forget_wrapping_key failed");
2294            (directory.object_id(), src_child.object_id(), dst_child.object_id())
2295        };
2296        fs.close().await.expect("Close failed");
2297        let device = fs.take_device().await;
2298        device.reopen(false);
2299        let fs = FxFilesystem::open(device).await.expect("open failed");
2300        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2301        let store = root_volume
2302            .volume(
2303                "test",
2304                StoreOptions {
2305                    crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2306                    ..StoreOptions::default()
2307                },
2308            )
2309            .await
2310            .expect("volume failed");
2311
2312        {
2313            let parent_directory = Directory::open(&store, parent_oid).await.expect("open failed");
2314            let layer_set = store.tree().layer_set();
2315            let mut merger = layer_set.merger();
2316            let mut encrypted_src_name = None;
2317            let mut encrypted_dst_name = None;
2318            let mut iter = parent_directory.iter(&mut merger).await.expect("iter_from failed");
2319            while let Some((name, object_id, object_descriptor)) = iter.get() {
2320                assert!(matches!(object_descriptor, ObjectDescriptor::Directory));
2321                if object_id == dst_oid {
2322                    encrypted_dst_name = Some(name.to_string());
2323                } else if object_id == src_oid {
2324                    encrypted_src_name = Some(name.to_string());
2325                }
2326                iter.advance().await.expect("iter advance failed");
2327            }
2328
2329            let src_child = parent_directory
2330                .lookup(&encrypted_src_name.expect("src child not found"))
2331                .await
2332                .expect("lookup failed")
2333                .expect("not found");
2334            let mut transaction = fs
2335                .clone()
2336                .new_transaction(
2337                    lock_keys![LockKey::object(
2338                        store.store_object_id(),
2339                        parent_directory.object_id(),
2340                    )],
2341                    Options::default(),
2342                )
2343                .await
2344                .expect("new transaction failed");
2345            replace_child_with_object(
2346                &mut transaction,
2347                Some((src_child.0, src_child.1)),
2348                (&parent_directory, &encrypted_dst_name.expect("dst child not found")),
2349                0,
2350                Timestamp::now(),
2351            )
2352            .await
2353            .expect_err("renames should fail within a locked directory");
2354        }
2355        fs.close().await.expect("Close failed");
2356    }
2357
2358    #[fuchsia::test]
2359    async fn test_set_encryption_policy_on_unencrypted_file() {
2360        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
2361        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2362        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2363        let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
2364        let store = root_volume
2365            .new_volume(
2366                "test",
2367                NewChildStoreOptions {
2368                    options: StoreOptions {
2369                        crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2370                        ..StoreOptions::default()
2371                    },
2372                    ..NewChildStoreOptions::default()
2373                },
2374            )
2375            .await
2376            .expect("new_volume failed");
2377
2378        let mut transaction = fs
2379            .clone()
2380            .new_transaction(
2381                lock_keys![LockKey::object(
2382                    store.store_object_id(),
2383                    store.root_directory_object_id()
2384                )],
2385                Options::default(),
2386            )
2387            .await
2388            .expect("new transaction failed");
2389        let root_directory =
2390            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
2391        let file_handle = root_directory
2392            .create_child_file(&mut transaction, "foo")
2393            .await
2394            .expect("create_child_dir failed");
2395        transaction.commit().await.expect("commit failed");
2396        let mut transaction = fs
2397            .clone()
2398            .new_transaction(
2399                lock_keys![LockKey::object(store.store_object_id(), file_handle.object_id())],
2400                Options::default(),
2401            )
2402            .await
2403            .expect("new transaction failed");
2404        file_handle
2405            .update_attributes(
2406                &mut transaction,
2407                Some(&fio::MutableNodeAttributes {
2408                    wrapping_key_id: Some(WRAPPING_KEY_ID),
2409                    ..Default::default()
2410                }),
2411                None,
2412            )
2413            .await
2414            .expect_err("Cannot update the wrapping key id of a file");
2415        fs.close().await.expect("Close failed");
2416    }
2417
2418    #[fuchsia::test]
2419    async fn test_delete_child() {
2420        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2421        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2422        let dir;
2423        let child;
2424        let mut transaction = fs
2425            .clone()
2426            .new_transaction(lock_keys![], Options::default())
2427            .await
2428            .expect("new_transaction failed");
2429        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2430            .await
2431            .expect("create failed");
2432
2433        child =
2434            dir.create_child_file(&mut transaction, "foo").await.expect("create_child_file failed");
2435        transaction.commit().await.expect("commit failed");
2436
2437        transaction = fs
2438            .clone()
2439            .new_transaction(
2440                lock_keys![
2441                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2442                    LockKey::object(fs.root_store().store_object_id(), child.object_id()),
2443                ],
2444                Options::default(),
2445            )
2446            .await
2447            .expect("new_transaction failed");
2448        assert_matches!(
2449            replace_child(&mut transaction, None, (&dir, "foo"))
2450                .await
2451                .expect("replace_child failed"),
2452            ReplacedChild::Object(..)
2453        );
2454        transaction.commit().await.expect("commit failed");
2455
2456        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
2457        fs.close().await.expect("Close failed");
2458    }
2459
2460    #[fuchsia::test]
2461    async fn test_delete_child_with_children_fails() {
2462        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2463        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2464        let dir;
2465        let child;
2466        let bar;
2467        let mut transaction = fs
2468            .clone()
2469            .new_transaction(lock_keys![], Options::default())
2470            .await
2471            .expect("new_transaction failed");
2472        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2473            .await
2474            .expect("create failed");
2475
2476        child =
2477            dir.create_child_dir(&mut transaction, "foo").await.expect("create_child_dir failed");
2478        bar = child
2479            .create_child_file(&mut transaction, "bar")
2480            .await
2481            .expect("create_child_file failed");
2482        transaction.commit().await.expect("commit failed");
2483
2484        transaction = fs
2485            .clone()
2486            .new_transaction(
2487                lock_keys![
2488                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2489                    LockKey::object(fs.root_store().store_object_id(), child.object_id()),
2490                ],
2491                Options::default(),
2492            )
2493            .await
2494            .expect("new_transaction failed");
2495        assert_eq!(
2496            replace_child(&mut transaction, None, (&dir, "foo"))
2497                .await
2498                .expect_err("replace_child succeeded")
2499                .downcast::<FxfsError>()
2500                .expect("wrong error"),
2501            FxfsError::NotEmpty
2502        );
2503        transaction.commit().await.expect("commit failed");
2504
2505        transaction = fs
2506            .clone()
2507            .new_transaction(
2508                lock_keys![
2509                    LockKey::object(fs.root_store().store_object_id(), child.object_id()),
2510                    LockKey::object(fs.root_store().store_object_id(), bar.object_id()),
2511                ],
2512                Options::default(),
2513            )
2514            .await
2515            .expect("new_transaction failed");
2516        assert_matches!(
2517            replace_child(&mut transaction, None, (&child, "bar"))
2518                .await
2519                .expect("replace_child failed"),
2520            ReplacedChild::Object(..)
2521        );
2522        transaction.commit().await.expect("commit failed");
2523
2524        transaction = fs
2525            .clone()
2526            .new_transaction(
2527                lock_keys![
2528                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2529                    LockKey::object(fs.root_store().store_object_id(), child.object_id()),
2530                ],
2531                Options::default(),
2532            )
2533            .await
2534            .expect("new_transaction failed");
2535        assert_matches!(
2536            replace_child(&mut transaction, None, (&dir, "foo"))
2537                .await
2538                .expect("replace_child failed"),
2539            ReplacedChild::Directory(..)
2540        );
2541        transaction.commit().await.expect("commit failed");
2542
2543        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
2544        fs.close().await.expect("Close failed");
2545    }
2546
2547    #[fuchsia::test]
2548    async fn test_delete_and_reinsert_child() {
2549        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2550        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2551        let dir;
2552        let child;
2553        let mut transaction = fs
2554            .clone()
2555            .new_transaction(lock_keys![], Options::default())
2556            .await
2557            .expect("new_transaction failed");
2558        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2559            .await
2560            .expect("create failed");
2561
2562        child =
2563            dir.create_child_file(&mut transaction, "foo").await.expect("create_child_file failed");
2564        transaction.commit().await.expect("commit failed");
2565
2566        transaction = fs
2567            .clone()
2568            .new_transaction(
2569                lock_keys![
2570                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2571                    LockKey::object(fs.root_store().store_object_id(), child.object_id()),
2572                ],
2573                Options::default(),
2574            )
2575            .await
2576            .expect("new_transaction failed");
2577        assert_matches!(
2578            replace_child(&mut transaction, None, (&dir, "foo"))
2579                .await
2580                .expect("replace_child failed"),
2581            ReplacedChild::Object(..)
2582        );
2583        transaction.commit().await.expect("commit failed");
2584
2585        transaction = fs
2586            .clone()
2587            .new_transaction(
2588                lock_keys![LockKey::object(fs.root_store().store_object_id(), dir.object_id())],
2589                Options::default(),
2590            )
2591            .await
2592            .expect("new_transaction failed");
2593        dir.create_child_file(&mut transaction, "foo").await.expect("create_child_file failed");
2594        transaction.commit().await.expect("commit failed");
2595
2596        dir.lookup("foo").await.expect("lookup failed");
2597        fs.close().await.expect("Close failed");
2598    }
2599
2600    #[fuchsia::test]
2601    async fn test_delete_child_persists() {
2602        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2603        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2604        let object_id = {
2605            let dir;
2606            let child;
2607            let mut transaction = fs
2608                .clone()
2609                .new_transaction(lock_keys![], Options::default())
2610                .await
2611                .expect("new_transaction failed");
2612            dir = Directory::create(&mut transaction, &fs.root_store(), None)
2613                .await
2614                .expect("create failed");
2615
2616            child = dir
2617                .create_child_file(&mut transaction, "foo")
2618                .await
2619                .expect("create_child_file failed");
2620            transaction.commit().await.expect("commit failed");
2621            dir.lookup("foo").await.expect("lookup failed");
2622
2623            transaction = fs
2624                .clone()
2625                .new_transaction(
2626                    lock_keys![
2627                        LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2628                        LockKey::object(fs.root_store().store_object_id(), child.object_id()),
2629                    ],
2630                    Options::default(),
2631                )
2632                .await
2633                .expect("new_transaction failed");
2634            assert_matches!(
2635                replace_child(&mut transaction, None, (&dir, "foo"))
2636                    .await
2637                    .expect("replace_child failed"),
2638                ReplacedChild::Object(..)
2639            );
2640            transaction.commit().await.expect("commit failed");
2641
2642            fs.sync(SyncOptions::default()).await.expect("sync failed");
2643            dir.object_id()
2644        };
2645
2646        fs.close().await.expect("Close failed");
2647        let device = fs.take_device().await;
2648        device.reopen(false);
2649        let fs = FxFilesystem::open(device).await.expect("open failed");
2650        let dir = Directory::open(&fs.root_store(), object_id).await.expect("open failed");
2651        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
2652        fs.close().await.expect("Close failed");
2653    }
2654
2655    #[fuchsia::test]
2656    async fn test_replace_child() {
2657        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2658        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2659        let dir;
2660        let child_dir1;
2661        let child_dir2;
2662        let mut transaction = fs
2663            .clone()
2664            .new_transaction(lock_keys![], Options::default())
2665            .await
2666            .expect("new_transaction failed");
2667        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2668            .await
2669            .expect("create failed");
2670
2671        child_dir1 =
2672            dir.create_child_dir(&mut transaction, "dir1").await.expect("create_child_dir failed");
2673        child_dir2 =
2674            dir.create_child_dir(&mut transaction, "dir2").await.expect("create_child_dir failed");
2675        let file = child_dir1
2676            .create_child_file(&mut transaction, "foo")
2677            .await
2678            .expect("create_child_file failed");
2679        transaction.commit().await.expect("commit failed");
2680
2681        transaction = fs
2682            .clone()
2683            .new_transaction(
2684                lock_keys![
2685                    LockKey::object(fs.root_store().store_object_id(), child_dir1.object_id()),
2686                    LockKey::object(fs.root_store().store_object_id(), child_dir2.object_id()),
2687                    LockKey::object(fs.root_store().store_object_id(), file.object_id()),
2688                ],
2689                Options::default(),
2690            )
2691            .await
2692            .expect("new_transaction failed");
2693        assert_matches!(
2694            replace_child(&mut transaction, Some((&child_dir1, "foo")), (&child_dir2, "bar"))
2695                .await
2696                .expect("replace_child failed"),
2697            ReplacedChild::None
2698        );
2699        transaction.commit().await.expect("commit failed");
2700
2701        assert_eq!(child_dir1.lookup("foo").await.expect("lookup failed"), None);
2702        child_dir2.lookup("bar").await.expect("lookup failed");
2703        fs.close().await.expect("Close failed");
2704    }
2705
2706    #[fuchsia::test]
2707    async fn test_replace_child_overwrites_dst() {
2708        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2709        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2710        let dir;
2711        let child_dir1;
2712        let child_dir2;
2713        let mut transaction = fs
2714            .clone()
2715            .new_transaction(lock_keys![], Options::default())
2716            .await
2717            .expect("new_transaction failed");
2718        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2719            .await
2720            .expect("create failed");
2721
2722        child_dir1 =
2723            dir.create_child_dir(&mut transaction, "dir1").await.expect("create_child_dir failed");
2724        child_dir2 =
2725            dir.create_child_dir(&mut transaction, "dir2").await.expect("create_child_dir failed");
2726        let foo = child_dir1
2727            .create_child_file(&mut transaction, "foo")
2728            .await
2729            .expect("create_child_file failed");
2730        let bar = child_dir2
2731            .create_child_file(&mut transaction, "bar")
2732            .await
2733            .expect("create_child_file failed");
2734        let foo_oid = foo.object_id();
2735        let bar_oid = bar.object_id();
2736        transaction.commit().await.expect("commit failed");
2737
2738        {
2739            let mut buf = foo.allocate_buffer(TEST_DEVICE_BLOCK_SIZE as usize).await;
2740            buf.as_mut_slice().fill(0xaa);
2741            foo.write_or_append(Some(0), buf.as_ref()).await.expect("write failed");
2742            buf.as_mut_slice().fill(0xbb);
2743            bar.write_or_append(Some(0), buf.as_ref()).await.expect("write failed");
2744        }
2745        std::mem::drop(bar);
2746        std::mem::drop(foo);
2747
2748        transaction = fs
2749            .clone()
2750            .new_transaction(
2751                lock_keys![
2752                    LockKey::object(fs.root_store().store_object_id(), child_dir1.object_id()),
2753                    LockKey::object(fs.root_store().store_object_id(), child_dir2.object_id()),
2754                    LockKey::object(fs.root_store().store_object_id(), foo_oid),
2755                    LockKey::object(fs.root_store().store_object_id(), bar_oid),
2756                ],
2757                Options::default(),
2758            )
2759            .await
2760            .expect("new_transaction failed");
2761        assert_matches!(
2762            replace_child(&mut transaction, Some((&child_dir1, "foo")), (&child_dir2, "bar"))
2763                .await
2764                .expect("replace_child failed"),
2765            ReplacedChild::Object(..)
2766        );
2767        transaction.commit().await.expect("commit failed");
2768
2769        assert_eq!(child_dir1.lookup("foo").await.expect("lookup failed"), None);
2770
2771        // Check the contents to ensure that the file was replaced.
2772        let (oid, object_descriptor, _) =
2773            child_dir2.lookup("bar").await.expect("lookup failed").expect("not found");
2774        assert_eq!(object_descriptor, ObjectDescriptor::File);
2775        let bar =
2776            ObjectStore::open_object(&child_dir2.owner(), oid, HandleOptions::default(), None)
2777                .await
2778                .expect("Open failed");
2779        let mut buf = bar.allocate_buffer(TEST_DEVICE_BLOCK_SIZE as usize).await;
2780        bar.read(0, buf.as_mut()).await.expect("read failed");
2781        assert_eq!(buf.as_slice(), vec![0xaa; TEST_DEVICE_BLOCK_SIZE as usize]);
2782        fs.close().await.expect("Close failed");
2783    }
2784
2785    #[fuchsia::test]
2786    async fn test_replace_child_fails_if_would_overwrite_nonempty_dir() {
2787        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2788        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2789        let dir;
2790        let child_dir1;
2791        let child_dir2;
2792        let mut transaction = fs
2793            .clone()
2794            .new_transaction(lock_keys![], Options::default())
2795            .await
2796            .expect("new_transaction failed");
2797        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2798            .await
2799            .expect("create failed");
2800
2801        child_dir1 =
2802            dir.create_child_dir(&mut transaction, "dir1").await.expect("create_child_dir failed");
2803        child_dir2 =
2804            dir.create_child_dir(&mut transaction, "dir2").await.expect("create_child_dir failed");
2805        let foo = child_dir1
2806            .create_child_file(&mut transaction, "foo")
2807            .await
2808            .expect("create_child_file failed");
2809        let nested_child = child_dir2
2810            .create_child_dir(&mut transaction, "bar")
2811            .await
2812            .expect("create_child_file failed");
2813        nested_child
2814            .create_child_file(&mut transaction, "baz")
2815            .await
2816            .expect("create_child_file failed");
2817        transaction.commit().await.expect("commit failed");
2818
2819        transaction = fs
2820            .clone()
2821            .new_transaction(
2822                lock_keys![
2823                    LockKey::object(fs.root_store().store_object_id(), child_dir1.object_id()),
2824                    LockKey::object(fs.root_store().store_object_id(), child_dir2.object_id()),
2825                    LockKey::object(fs.root_store().store_object_id(), foo.object_id()),
2826                    LockKey::object(fs.root_store().store_object_id(), nested_child.object_id()),
2827                ],
2828                Options::default(),
2829            )
2830            .await
2831            .expect("new_transaction failed");
2832        assert_eq!(
2833            replace_child(&mut transaction, Some((&child_dir1, "foo")), (&child_dir2, "bar"))
2834                .await
2835                .expect_err("replace_child succeeded")
2836                .downcast::<FxfsError>()
2837                .expect("wrong error"),
2838            FxfsError::NotEmpty
2839        );
2840        fs.close().await.expect("Close failed");
2841    }
2842
2843    #[fuchsia::test]
2844    async fn test_replace_child_within_dir() {
2845        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2846        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2847        let dir;
2848        let mut transaction = fs
2849            .clone()
2850            .new_transaction(lock_keys![], Options::default())
2851            .await
2852            .expect("new_transaction failed");
2853        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2854            .await
2855            .expect("create failed");
2856        let foo =
2857            dir.create_child_file(&mut transaction, "foo").await.expect("create_child_file failed");
2858        transaction.commit().await.expect("commit failed");
2859
2860        transaction = fs
2861            .clone()
2862            .new_transaction(
2863                lock_keys![
2864                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2865                    LockKey::object(fs.root_store().store_object_id(), foo.object_id()),
2866                ],
2867                Options::default(),
2868            )
2869            .await
2870            .expect("new_transaction failed");
2871        assert_matches!(
2872            replace_child(&mut transaction, Some((&dir, "foo")), (&dir, "bar"))
2873                .await
2874                .expect("replace_child failed"),
2875            ReplacedChild::None
2876        );
2877        transaction.commit().await.expect("commit failed");
2878
2879        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
2880        dir.lookup("bar").await.expect("lookup new name failed");
2881        fs.close().await.expect("Close failed");
2882    }
2883
2884    #[fuchsia::test]
2885    async fn test_iterate() {
2886        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2887        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2888        let dir;
2889        let mut transaction = fs
2890            .clone()
2891            .new_transaction(lock_keys![], Options::default())
2892            .await
2893            .expect("new_transaction failed");
2894        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2895            .await
2896            .expect("create failed");
2897        let _cat =
2898            dir.create_child_file(&mut transaction, "cat").await.expect("create_child_file failed");
2899        let _ball = dir
2900            .create_child_file(&mut transaction, "ball")
2901            .await
2902            .expect("create_child_file failed");
2903        let apple = dir
2904            .create_child_file(&mut transaction, "apple")
2905            .await
2906            .expect("create_child_file failed");
2907        let _dog =
2908            dir.create_child_file(&mut transaction, "dog").await.expect("create_child_file failed");
2909        transaction.commit().await.expect("commit failed");
2910        transaction = fs
2911            .clone()
2912            .new_transaction(
2913                lock_keys![
2914                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2915                    LockKey::object(fs.root_store().store_object_id(), apple.object_id()),
2916                ],
2917                Options::default(),
2918            )
2919            .await
2920            .expect("new_transaction failed");
2921        replace_child(&mut transaction, None, (&dir, "apple")).await.expect("replace_child failed");
2922        transaction.commit().await.expect("commit failed");
2923        let layer_set = dir.store().tree().layer_set();
2924        let mut merger = layer_set.merger();
2925        let mut iter = dir.iter(&mut merger).await.expect("iter failed");
2926        let mut entries = Vec::new();
2927        while let Some((name, _, _)) = iter.get() {
2928            entries.push(name.to_string());
2929            iter.advance().await.expect("advance failed");
2930        }
2931        assert_eq!(&entries, &["ball", "cat", "dog"]);
2932        fs.close().await.expect("Close failed");
2933    }
2934
2935    #[fuchsia::test]
2936    async fn test_sub_dir_count() {
2937        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2938        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2939        let dir;
2940        let child_dir;
2941        let mut transaction = fs
2942            .clone()
2943            .new_transaction(lock_keys![], Options::default())
2944            .await
2945            .expect("new_transaction failed");
2946        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2947            .await
2948            .expect("create failed");
2949        child_dir =
2950            dir.create_child_dir(&mut transaction, "foo").await.expect("create_child_dir failed");
2951        transaction.commit().await.expect("commit failed");
2952        assert_eq!(dir.get_properties().await.expect("get_properties failed").sub_dirs, 1);
2953
2954        // Moving within the same directory should not change the sub_dir count.
2955        transaction = fs
2956            .clone()
2957            .new_transaction(
2958                lock_keys![
2959                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2960                    LockKey::object(fs.root_store().store_object_id(), child_dir.object_id()),
2961                ],
2962                Options::default(),
2963            )
2964            .await
2965            .expect("new_transaction failed");
2966        replace_child(&mut transaction, Some((&dir, "foo")), (&dir, "bar"))
2967            .await
2968            .expect("replace_child failed");
2969        transaction.commit().await.expect("commit failed");
2970
2971        assert_eq!(dir.get_properties().await.expect("get_properties failed").sub_dirs, 1);
2972        assert_eq!(child_dir.get_properties().await.expect("get_properties failed").sub_dirs, 0);
2973
2974        // Moving between two different directories should update source and destination.
2975        transaction = fs
2976            .clone()
2977            .new_transaction(
2978                lock_keys![LockKey::object(
2979                    fs.root_store().store_object_id(),
2980                    child_dir.object_id()
2981                )],
2982                Options::default(),
2983            )
2984            .await
2985            .expect("new_transaction failed");
2986        let second_child = child_dir
2987            .create_child_dir(&mut transaction, "baz")
2988            .await
2989            .expect("create_child_dir failed");
2990        transaction.commit().await.expect("commit failed");
2991
2992        assert_eq!(child_dir.get_properties().await.expect("get_properties failed").sub_dirs, 1);
2993
2994        transaction = fs
2995            .clone()
2996            .new_transaction(
2997                lock_keys![
2998                    LockKey::object(fs.root_store().store_object_id(), child_dir.object_id()),
2999                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3000                    LockKey::object(fs.root_store().store_object_id(), second_child.object_id()),
3001                ],
3002                Options::default(),
3003            )
3004            .await
3005            .expect("new_transaction failed");
3006        replace_child(&mut transaction, Some((&child_dir, "baz")), (&dir, "foo"))
3007            .await
3008            .expect("replace_child failed");
3009        transaction.commit().await.expect("commit failed");
3010
3011        assert_eq!(dir.get_properties().await.expect("get_properties failed").sub_dirs, 2);
3012        assert_eq!(child_dir.get_properties().await.expect("get_properties failed").sub_dirs, 0);
3013
3014        // Moving over a directory.
3015        transaction = fs
3016            .clone()
3017            .new_transaction(
3018                lock_keys![
3019                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3020                    LockKey::object(fs.root_store().store_object_id(), second_child.object_id()),
3021                    LockKey::object(fs.root_store().store_object_id(), child_dir.object_id()),
3022                ],
3023                Options::default(),
3024            )
3025            .await
3026            .expect("new_transaction failed");
3027        replace_child(&mut transaction, Some((&dir, "bar")), (&dir, "foo"))
3028            .await
3029            .expect("replace_child failed");
3030        transaction.commit().await.expect("commit failed");
3031
3032        assert_eq!(dir.get_properties().await.expect("get_properties failed").sub_dirs, 1);
3033
3034        // Unlinking a directory.
3035        transaction = fs
3036            .clone()
3037            .new_transaction(
3038                lock_keys![
3039                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3040                    LockKey::object(fs.root_store().store_object_id(), child_dir.object_id()),
3041                ],
3042                Options::default(),
3043            )
3044            .await
3045            .expect("new_transaction failed");
3046        replace_child(&mut transaction, None, (&dir, "foo")).await.expect("replace_child failed");
3047        transaction.commit().await.expect("commit failed");
3048
3049        assert_eq!(dir.get_properties().await.expect("get_properties failed").sub_dirs, 0);
3050        fs.close().await.expect("Close failed");
3051    }
3052
3053    #[fuchsia::test]
3054    async fn test_deleted_dir() {
3055        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3056        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3057        let dir;
3058        let mut transaction = fs
3059            .clone()
3060            .new_transaction(lock_keys![], Options::default())
3061            .await
3062            .expect("new_transaction failed");
3063        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3064            .await
3065            .expect("create failed");
3066        let child =
3067            dir.create_child_dir(&mut transaction, "foo").await.expect("create_child_dir failed");
3068        dir.create_child_dir(&mut transaction, "bar").await.expect("create_child_dir failed");
3069        transaction.commit().await.expect("commit failed");
3070
3071        // Flush the tree so that we end up with records in different layers.
3072        dir.store().flush().await.expect("flush failed");
3073
3074        // Unlink the child directory.
3075        transaction = fs
3076            .clone()
3077            .new_transaction(
3078                lock_keys![
3079                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3080                    LockKey::object(fs.root_store().store_object_id(), child.object_id()),
3081                ],
3082                Options::default(),
3083            )
3084            .await
3085            .expect("new_transaction failed");
3086        replace_child(&mut transaction, None, (&dir, "foo")).await.expect("replace_child failed");
3087        transaction.commit().await.expect("commit failed");
3088
3089        // Finding the child should fail now.
3090        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
3091
3092        // But finding "bar" should succeed.
3093        assert!(dir.lookup("bar").await.expect("lookup failed").is_some());
3094
3095        // If we mark dir as deleted, any further operations should fail.
3096        dir.set_deleted();
3097
3098        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
3099        assert_eq!(dir.lookup("bar").await.expect("lookup failed"), None);
3100        assert!(!dir.has_children().await.expect("has_children failed"));
3101
3102        transaction = fs
3103            .clone()
3104            .new_transaction(lock_keys![], Options::default())
3105            .await
3106            .expect("new_transaction failed");
3107
3108        let assert_access_denied = |result| {
3109            if let Err(e) = result {
3110                assert!(FxfsError::Deleted.matches(&e));
3111            } else {
3112                panic!();
3113            }
3114        };
3115        assert_access_denied(dir.create_child_dir(&mut transaction, "baz").await.map(|_| {}));
3116        assert_access_denied(dir.create_child_file(&mut transaction, "baz").await.map(|_| {}));
3117        assert_access_denied(dir.add_child_volume(&mut transaction, "baz", 1).await);
3118        assert_access_denied(
3119            dir.insert_child(&mut transaction, "baz", 1, ObjectDescriptor::File).await,
3120        );
3121        assert_access_denied(
3122            dir.update_dir_attributes_internal(
3123                &mut transaction,
3124                dir.object_id(),
3125                MutableAttributesInternal {
3126                    creation_time: Some(Timestamp::zero().as_nanos()),
3127                    ..Default::default()
3128                },
3129            )
3130            .await,
3131        );
3132        let layer_set = dir.store().tree().layer_set();
3133        let mut merger = layer_set.merger();
3134        assert_access_denied(dir.iter(&mut merger).await.map(|_| {}));
3135    }
3136
3137    #[fuchsia::test]
3138    async fn test_create_symlink() {
3139        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3140        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3141        let (dir_id, symlink_id) = {
3142            let mut transaction = fs
3143                .clone()
3144                .new_transaction(lock_keys![], Options::default())
3145                .await
3146                .expect("new_transaction failed");
3147            let dir = Directory::create(&mut transaction, &fs.root_store(), None)
3148                .await
3149                .expect("create failed");
3150
3151            let symlink_id = dir
3152                .create_symlink(&mut transaction, b"link", "foo")
3153                .await
3154                .expect("create_symlink failed");
3155            transaction.commit().await.expect("commit failed");
3156
3157            fs.sync(SyncOptions::default()).await.expect("sync failed");
3158            (dir.object_id(), symlink_id)
3159        };
3160        fs.close().await.expect("Close failed");
3161        let device = fs.take_device().await;
3162        device.reopen(false);
3163        let fs = FxFilesystem::open(device).await.expect("open failed");
3164        {
3165            let dir = Directory::open(&fs.root_store(), dir_id).await.expect("open failed");
3166            assert_eq!(
3167                dir.lookup("foo").await.expect("lookup failed").expect("not found"),
3168                (symlink_id, ObjectDescriptor::Symlink, false)
3169            );
3170        }
3171        fs.close().await.expect("Close failed");
3172    }
3173
3174    #[fuchsia::test]
3175    async fn test_read_symlink() {
3176        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3177        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3178        let mut transaction = fs
3179            .clone()
3180            .new_transaction(lock_keys![], Options::default())
3181            .await
3182            .expect("new_transaction failed");
3183        let store = fs.root_store();
3184        let dir = Directory::create(&mut transaction, &store, None).await.expect("create failed");
3185
3186        let symlink_id = dir
3187            .create_symlink(&mut transaction, b"link", "foo")
3188            .await
3189            .expect("create_symlink failed");
3190        transaction.commit().await.expect("commit failed");
3191
3192        let link = store.read_symlink(symlink_id).await.expect("read_symlink failed");
3193        assert_eq!(&link, b"link");
3194        fs.close().await.expect("Close failed");
3195    }
3196
3197    #[fuchsia::test]
3198    async fn test_unlink_symlink() {
3199        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3200        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3201        let dir;
3202        let mut transaction = fs
3203            .clone()
3204            .new_transaction(lock_keys![], Options::default())
3205            .await
3206            .expect("new_transaction failed");
3207        let store = fs.root_store();
3208        dir = Directory::create(&mut transaction, &store, None).await.expect("create failed");
3209
3210        let symlink_id = dir
3211            .create_symlink(&mut transaction, b"link", "foo")
3212            .await
3213            .expect("create_symlink failed");
3214        transaction.commit().await.expect("commit failed");
3215        transaction = fs
3216            .clone()
3217            .new_transaction(
3218                lock_keys![
3219                    LockKey::object(store.store_object_id(), dir.object_id()),
3220                    LockKey::object(store.store_object_id(), symlink_id),
3221                ],
3222                Options::default(),
3223            )
3224            .await
3225            .expect("new_transaction failed");
3226        assert_matches!(
3227            replace_child(&mut transaction, None, (&dir, "foo"))
3228                .await
3229                .expect("replace_child failed"),
3230            ReplacedChild::Object(_)
3231        );
3232        transaction.commit().await.expect("commit failed");
3233
3234        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
3235        fs.close().await.expect("Close failed");
3236    }
3237
3238    #[fuchsia::test]
3239    async fn test_get_properties() {
3240        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3241        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3242        let dir;
3243        let mut transaction = fs
3244            .clone()
3245            .new_transaction(lock_keys![], Options::default())
3246            .await
3247            .expect("new_transaction failed");
3248
3249        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3250            .await
3251            .expect("create failed");
3252        transaction.commit().await.expect("commit failed");
3253
3254        // Check attributes of `dir`
3255        let mut properties = dir.get_properties().await.expect("get_properties failed");
3256        let dir_creation_time = properties.creation_time;
3257        assert_eq!(dir_creation_time, properties.modification_time);
3258        assert_eq!(properties.sub_dirs, 0);
3259        assert!(properties.posix_attributes.is_none());
3260
3261        // Create child directory
3262        transaction = fs
3263            .clone()
3264            .new_transaction(
3265                lock_keys![LockKey::object(fs.root_store().store_object_id(), dir.object_id())],
3266                Options::default(),
3267            )
3268            .await
3269            .expect("new_transaction failed");
3270        let child_dir =
3271            dir.create_child_dir(&mut transaction, "foo").await.expect("create_child_dir failed");
3272        transaction.commit().await.expect("commit failed");
3273
3274        // Check attributes of `dir` after adding child directory
3275        properties = dir.get_properties().await.expect("get_properties failed");
3276        // The modification time property should have updated
3277        assert_eq!(dir_creation_time, properties.creation_time);
3278        assert!(dir_creation_time < properties.modification_time);
3279        assert_eq!(properties.sub_dirs, 1);
3280        assert!(properties.posix_attributes.is_none());
3281
3282        // Check attributes of `child_dir`
3283        properties = child_dir.get_properties().await.expect("get_properties failed");
3284        assert_eq!(properties.creation_time, properties.modification_time);
3285        assert_eq!(properties.sub_dirs, 0);
3286        assert!(properties.posix_attributes.is_none());
3287
3288        // Create child file with MutableAttributes
3289        transaction = fs
3290            .clone()
3291            .new_transaction(
3292                lock_keys![LockKey::object(
3293                    fs.root_store().store_object_id(),
3294                    child_dir.object_id()
3295                )],
3296                Options::default(),
3297            )
3298            .await
3299            .expect("new_transaction failed");
3300        let child_dir_file = child_dir
3301            .create_child_file(&mut transaction, "bar")
3302            .await
3303            .expect("create_child_file failed");
3304        child_dir_file
3305            .update_attributes(
3306                &mut transaction,
3307                Some(&fio::MutableNodeAttributes { gid: Some(1), ..Default::default() }),
3308                None,
3309            )
3310            .await
3311            .expect("Updating attributes");
3312        transaction.commit().await.expect("commit failed");
3313
3314        // The modification time property of `child_dir` should have updated
3315        properties = child_dir.get_properties().await.expect("get_properties failed");
3316        assert!(properties.creation_time < properties.modification_time);
3317        assert!(properties.posix_attributes.is_none());
3318
3319        // Check attributes of `child_dir_file`
3320        properties = child_dir_file.get_properties().await.expect("get_properties failed");
3321        assert_eq!(properties.creation_time, properties.modification_time);
3322        assert_eq!(properties.sub_dirs, 0);
3323        assert!(properties.posix_attributes.is_some());
3324        assert_eq!(properties.posix_attributes.unwrap().gid, 1);
3325        // The other POSIX attributes should be set to default values
3326        assert_eq!(properties.posix_attributes.unwrap().uid, 0);
3327        assert_eq!(properties.posix_attributes.unwrap().mode, 0);
3328        assert_eq!(properties.posix_attributes.unwrap().rdev, 0);
3329    }
3330
3331    #[fuchsia::test]
3332    async fn test_update_create_attributes() {
3333        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3334        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3335        let dir;
3336        let mut transaction = fs
3337            .clone()
3338            .new_transaction(lock_keys![], Options::default())
3339            .await
3340            .expect("new_transaction failed");
3341
3342        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3343            .await
3344            .expect("create failed");
3345        transaction.commit().await.expect("commit failed");
3346        let mut properties = dir.get_properties().await.expect("get_properties failed");
3347        assert_eq!(properties.sub_dirs, 0);
3348        assert!(properties.posix_attributes.is_none());
3349        let creation_time = properties.creation_time;
3350        let modification_time = properties.modification_time;
3351        assert_eq!(creation_time, modification_time);
3352
3353        // First update: test that
3354        // 1. updating attributes with a POSIX attribute will assign some PosixAttributes to the
3355        //    Object associated with `dir`,
3356        // 2. creation/modification time are only updated if specified in the update,
3357        // 3. any changes will not overwrite other attributes.
3358        transaction = fs
3359            .clone()
3360            .new_transaction(
3361                lock_keys![LockKey::object(fs.root_store().store_object_id(), dir.object_id())],
3362                Options::default(),
3363            )
3364            .await
3365            .expect("new_transaction failed");
3366        let now = Timestamp::now();
3367        dir.update_attributes(
3368            transaction,
3369            Some(&fio::MutableNodeAttributes {
3370                modification_time: Some(now.as_nanos()),
3371                uid: Some(1),
3372                gid: Some(2),
3373                ..Default::default()
3374            }),
3375            0,
3376            None,
3377        )
3378        .await
3379        .expect("update_attributes failed");
3380        properties = dir.get_properties().await.expect("get_properties failed");
3381        // Check that the properties reflect the updates
3382        assert_eq!(properties.modification_time, now);
3383        assert!(properties.posix_attributes.is_some());
3384        assert_eq!(properties.posix_attributes.unwrap().uid, 1);
3385        assert_eq!(properties.posix_attributes.unwrap().gid, 2);
3386        // The other POSIX attributes should be set to default values
3387        assert_eq!(properties.posix_attributes.unwrap().mode, 0);
3388        assert_eq!(properties.posix_attributes.unwrap().rdev, 0);
3389        // The remaining properties should not have changed
3390        assert_eq!(properties.sub_dirs, 0);
3391        assert_eq!(properties.creation_time, creation_time);
3392
3393        // Second update: test that we can update attributes and that any changes will not overwrite
3394        // other attributes
3395        let transaction = fs
3396            .clone()
3397            .new_transaction(
3398                lock_keys![LockKey::object(fs.root_store().store_object_id(), dir.object_id())],
3399                Options::default(),
3400            )
3401            .await
3402            .expect("new_transaction failed");
3403        dir.update_attributes(
3404            transaction,
3405            Some(&fio::MutableNodeAttributes {
3406                creation_time: Some(now.as_nanos()),
3407                uid: Some(3),
3408                rdev: Some(10),
3409                ..Default::default()
3410            }),
3411            0,
3412            None,
3413        )
3414        .await
3415        .expect("update_attributes failed");
3416        properties = dir.get_properties().await.expect("get_properties failed");
3417        assert_eq!(properties.creation_time, now);
3418        assert!(properties.posix_attributes.is_some());
3419        assert_eq!(properties.posix_attributes.unwrap().uid, 3);
3420        assert_eq!(properties.posix_attributes.unwrap().rdev, 10);
3421        // The other properties should not have changed
3422        assert_eq!(properties.sub_dirs, 0);
3423        assert_eq!(properties.modification_time, now);
3424        assert_eq!(properties.posix_attributes.unwrap().gid, 2);
3425        assert_eq!(properties.posix_attributes.unwrap().mode, 0);
3426    }
3427
3428    #[fuchsia::test]
3429    async fn write_to_directory_attribute_creates_keys() {
3430        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
3431        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3432        let crypt = Arc::new(new_insecure_crypt());
3433
3434        {
3435            let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
3436            let store = root_volume
3437                .new_volume(
3438                    "vol",
3439                    NewChildStoreOptions {
3440                        options: StoreOptions {
3441                            crypt: Some(crypt.clone()),
3442                            ..StoreOptions::default()
3443                        },
3444                        ..Default::default()
3445                    },
3446                )
3447                .await
3448                .expect("new_volume failed");
3449            let mut transaction = filesystem
3450                .clone()
3451                .new_transaction(
3452                    lock_keys![LockKey::object(
3453                        store.store_object_id(),
3454                        store.root_directory_object_id()
3455                    )],
3456                    Options::default(),
3457                )
3458                .await
3459                .expect("new transaction failed");
3460            let root_directory = Directory::open(&store, store.root_directory_object_id())
3461                .await
3462                .expect("open failed");
3463            let directory = root_directory
3464                .create_child_dir(&mut transaction, "foo")
3465                .await
3466                .expect("create_child_dir failed");
3467            transaction.commit().await.expect("commit failed");
3468
3469            let mut transaction = filesystem
3470                .clone()
3471                .new_transaction(
3472                    lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
3473                    Options::default(),
3474                )
3475                .await
3476                .expect("new transaction failed");
3477            let _ = directory
3478                .handle
3479                .write_attr(&mut transaction, 1, b"bar")
3480                .await
3481                .expect("write_attr failed");
3482            transaction.commit().await.expect("commit failed");
3483        }
3484
3485        filesystem.close().await.expect("Close failed");
3486        let device = filesystem.take_device().await;
3487        device.reopen(false);
3488        let filesystem = FxFilesystem::open(device).await.expect("open failed");
3489
3490        {
3491            let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
3492            let volume = root_volume
3493                .volume("vol", StoreOptions { crypt: Some(crypt), ..StoreOptions::default() })
3494                .await
3495                .expect("volume failed");
3496            let root_directory = Directory::open(&volume, volume.root_directory_object_id())
3497                .await
3498                .expect("open failed");
3499            let directory = Directory::open(
3500                &volume,
3501                root_directory.lookup("foo").await.expect("lookup failed").expect("not found").0,
3502            )
3503            .await
3504            .expect("open failed");
3505            let mut buffer = directory.handle.allocate_buffer(10).await;
3506            assert_eq!(directory.handle.read(1, 0, buffer.as_mut()).await.expect("read failed"), 3);
3507            assert_eq!(&buffer.as_slice()[..3], b"bar");
3508        }
3509
3510        filesystem.close().await.expect("Close failed");
3511    }
3512
3513    #[fuchsia::test]
3514    async fn directory_with_extended_attributes() {
3515        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
3516        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3517        let crypt = Arc::new(new_insecure_crypt());
3518
3519        let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
3520        let store = root_volume
3521            .new_volume(
3522                "vol",
3523                NewChildStoreOptions {
3524                    options: StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
3525                    ..Default::default()
3526                },
3527            )
3528            .await
3529            .expect("new_volume failed");
3530        let directory =
3531            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
3532
3533        let test_small_name = b"security.selinux".to_vec();
3534        let test_small_value = b"foo".to_vec();
3535        let test_large_name = b"large.attribute".to_vec();
3536        let test_large_value = vec![1u8; 500];
3537
3538        directory
3539            .set_extended_attribute(
3540                test_small_name.clone(),
3541                test_small_value.clone(),
3542                SetExtendedAttributeMode::Set,
3543            )
3544            .await
3545            .unwrap();
3546        assert_eq!(
3547            directory.get_extended_attribute(test_small_name.clone()).await.unwrap(),
3548            test_small_value
3549        );
3550
3551        directory
3552            .set_extended_attribute(
3553                test_large_name.clone(),
3554                test_large_value.clone(),
3555                SetExtendedAttributeMode::Set,
3556            )
3557            .await
3558            .unwrap();
3559        assert_eq!(
3560            directory.get_extended_attribute(test_large_name.clone()).await.unwrap(),
3561            test_large_value
3562        );
3563
3564        crate::fsck::fsck(filesystem.clone()).await.unwrap();
3565        crate::fsck::fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3566            .await
3567            .unwrap();
3568
3569        directory.remove_extended_attribute(test_small_name.clone()).await.unwrap();
3570        directory.remove_extended_attribute(test_large_name.clone()).await.unwrap();
3571
3572        filesystem.close().await.expect("close failed");
3573    }
3574
3575    #[fuchsia::test]
3576    async fn remove_directory_with_extended_attributes() {
3577        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
3578        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3579        let crypt = Arc::new(new_insecure_crypt());
3580
3581        let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
3582        let store = root_volume
3583            .new_volume(
3584                "vol",
3585                NewChildStoreOptions {
3586                    options: StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
3587                    ..Default::default()
3588                },
3589            )
3590            .await
3591            .expect("new_volume failed");
3592        let mut transaction = filesystem
3593            .clone()
3594            .new_transaction(
3595                lock_keys![LockKey::object(
3596                    store.store_object_id(),
3597                    store.root_directory_object_id()
3598                )],
3599                Options::default(),
3600            )
3601            .await
3602            .expect("new transaction failed");
3603        let root_directory =
3604            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
3605        let directory = root_directory
3606            .create_child_dir(&mut transaction, "foo")
3607            .await
3608            .expect("create_child_dir failed");
3609        transaction.commit().await.expect("commit failed");
3610
3611        crate::fsck::fsck(filesystem.clone()).await.unwrap();
3612        crate::fsck::fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3613            .await
3614            .unwrap();
3615
3616        let test_small_name = b"security.selinux".to_vec();
3617        let test_small_value = b"foo".to_vec();
3618        let test_large_name = b"large.attribute".to_vec();
3619        let test_large_value = vec![1u8; 500];
3620
3621        directory
3622            .set_extended_attribute(
3623                test_small_name.clone(),
3624                test_small_value.clone(),
3625                SetExtendedAttributeMode::Set,
3626            )
3627            .await
3628            .unwrap();
3629        directory
3630            .set_extended_attribute(
3631                test_large_name.clone(),
3632                test_large_value.clone(),
3633                SetExtendedAttributeMode::Set,
3634            )
3635            .await
3636            .unwrap();
3637
3638        crate::fsck::fsck(filesystem.clone()).await.unwrap();
3639        crate::fsck::fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3640            .await
3641            .unwrap();
3642
3643        let mut transaction = filesystem
3644            .clone()
3645            .new_transaction(
3646                lock_keys![
3647                    LockKey::object(store.store_object_id(), root_directory.object_id()),
3648                    LockKey::object(store.store_object_id(), directory.object_id()),
3649                ],
3650                Options::default(),
3651            )
3652            .await
3653            .expect("new_transaction failed");
3654        replace_child(&mut transaction, None, (&root_directory, "foo"))
3655            .await
3656            .expect("replace_child failed");
3657        transaction.commit().await.unwrap();
3658
3659        crate::fsck::fsck(filesystem.clone()).await.unwrap();
3660        crate::fsck::fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3661            .await
3662            .unwrap();
3663
3664        filesystem.close().await.expect("close failed");
3665    }
3666
3667    #[fuchsia::test]
3668    async fn remove_symlink_with_extended_attributes() {
3669        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
3670        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3671        let crypt = Arc::new(new_insecure_crypt());
3672
3673        let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
3674        let store = root_volume
3675            .new_volume(
3676                "vol",
3677                NewChildStoreOptions {
3678                    options: StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
3679                    ..Default::default()
3680                },
3681            )
3682            .await
3683            .expect("new_volume failed");
3684        let mut transaction = filesystem
3685            .clone()
3686            .new_transaction(
3687                lock_keys![LockKey::object(
3688                    store.store_object_id(),
3689                    store.root_directory_object_id()
3690                )],
3691                Options::default(),
3692            )
3693            .await
3694            .expect("new transaction failed");
3695        let root_directory =
3696            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
3697        let symlink_id = root_directory
3698            .create_symlink(&mut transaction, b"somewhere/else", "foo")
3699            .await
3700            .expect("create_symlink failed");
3701        transaction.commit().await.expect("commit failed");
3702
3703        let symlink = StoreObjectHandle::new(
3704            store.clone(),
3705            symlink_id,
3706            false,
3707            HandleOptions::default(),
3708            false,
3709        );
3710
3711        crate::fsck::fsck(filesystem.clone()).await.unwrap();
3712        crate::fsck::fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3713            .await
3714            .unwrap();
3715
3716        let test_small_name = b"security.selinux".to_vec();
3717        let test_small_value = b"foo".to_vec();
3718        let test_large_name = b"large.attribute".to_vec();
3719        let test_large_value = vec![1u8; 500];
3720
3721        symlink
3722            .set_extended_attribute(
3723                test_small_name.clone(),
3724                test_small_value.clone(),
3725                SetExtendedAttributeMode::Set,
3726            )
3727            .await
3728            .unwrap();
3729        symlink
3730            .set_extended_attribute(
3731                test_large_name.clone(),
3732                test_large_value.clone(),
3733                SetExtendedAttributeMode::Set,
3734            )
3735            .await
3736            .unwrap();
3737
3738        crate::fsck::fsck(filesystem.clone()).await.unwrap();
3739        crate::fsck::fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3740            .await
3741            .unwrap();
3742
3743        let mut transaction = filesystem
3744            .clone()
3745            .new_transaction(
3746                lock_keys![
3747                    LockKey::object(store.store_object_id(), root_directory.object_id()),
3748                    LockKey::object(store.store_object_id(), symlink.object_id()),
3749                ],
3750                Options::default(),
3751            )
3752            .await
3753            .expect("new_transaction failed");
3754        replace_child(&mut transaction, None, (&root_directory, "foo"))
3755            .await
3756            .expect("replace_child failed");
3757        transaction.commit().await.unwrap();
3758
3759        crate::fsck::fsck(filesystem.clone()).await.unwrap();
3760        crate::fsck::fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3761            .await
3762            .unwrap();
3763
3764        filesystem.close().await.expect("close failed");
3765    }
3766
3767    #[fuchsia::test]
3768    async fn test_update_timestamps() {
3769        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3770        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3771        let dir;
3772        let mut transaction = fs
3773            .clone()
3774            .new_transaction(lock_keys![], Options::default())
3775            .await
3776            .expect("new_transaction failed");
3777
3778        // Expect that atime, ctime, mtime (and creation time) to be the same when we create a
3779        // directory
3780        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3781            .await
3782            .expect("create failed");
3783        transaction.commit().await.expect("commit failed");
3784        let mut properties = dir.get_properties().await.expect("get_properties failed");
3785        let starting_time = properties.creation_time;
3786        assert_eq!(properties.creation_time, starting_time);
3787        assert_eq!(properties.modification_time, starting_time);
3788        assert_eq!(properties.change_time, starting_time);
3789        assert_eq!(properties.access_time, starting_time);
3790
3791        // Test that we can update the timestamps
3792        transaction = fs
3793            .clone()
3794            .new_transaction(
3795                lock_keys![LockKey::object(fs.root_store().store_object_id(), dir.object_id())],
3796                Options::default(),
3797            )
3798            .await
3799            .expect("new_transaction failed");
3800        let update1_time = Timestamp::now();
3801        dir.update_attributes(
3802            transaction,
3803            Some(&fio::MutableNodeAttributes {
3804                modification_time: Some(update1_time.as_nanos()),
3805                ..Default::default()
3806            }),
3807            0,
3808            Some(update1_time),
3809        )
3810        .await
3811        .expect("update_attributes failed");
3812        properties = dir.get_properties().await.expect("get_properties failed");
3813        assert_eq!(properties.modification_time, update1_time);
3814        assert_eq!(properties.access_time, starting_time);
3815        assert_eq!(properties.creation_time, starting_time);
3816        assert_eq!(properties.change_time, update1_time);
3817    }
3818
3819    #[fuchsia::test]
3820    async fn test_move_dir_timestamps() {
3821        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3822        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3823        let dir;
3824        let child1;
3825        let child2;
3826        let mut transaction = fs
3827            .clone()
3828            .new_transaction(lock_keys![], Options::default())
3829            .await
3830            .expect("new_transaction failed");
3831        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3832            .await
3833            .expect("create failed");
3834        child1 = dir
3835            .create_child_dir(&mut transaction, "child1")
3836            .await
3837            .expect("create_child_dir failed");
3838        child2 = dir
3839            .create_child_dir(&mut transaction, "child2")
3840            .await
3841            .expect("create_child_dir failed");
3842        transaction.commit().await.expect("commit failed");
3843        let dir_properties = dir.get_properties().await.expect("get_properties failed");
3844        let child2_properties = child2.get_properties().await.expect("get_properties failed");
3845
3846        // Move dir/child2 to dir/child1/child2
3847        transaction = fs
3848            .clone()
3849            .new_transaction(
3850                lock_keys![
3851                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3852                    LockKey::object(fs.root_store().store_object_id(), child1.object_id()),
3853                    LockKey::object(fs.root_store().store_object_id(), child2.object_id()),
3854                ],
3855                Options::default(),
3856            )
3857            .await
3858            .expect("new_transaction failed");
3859        assert_matches!(
3860            replace_child(&mut transaction, Some((&dir, "child2")), (&child1, "child2"))
3861                .await
3862                .expect("replace_child failed"),
3863            ReplacedChild::None
3864        );
3865        transaction.commit().await.expect("commit failed");
3866        // Both mtime and ctime for dir should be updated
3867        let new_dir_properties = dir.get_properties().await.expect("get_properties failed");
3868        let time_of_replacement = new_dir_properties.change_time;
3869        assert!(new_dir_properties.change_time > dir_properties.change_time);
3870        assert_eq!(new_dir_properties.modification_time, time_of_replacement);
3871        // Both mtime and ctime for child1 should be updated
3872        let new_child1_properties = child1.get_properties().await.expect("get_properties failed");
3873        assert_eq!(new_child1_properties.modification_time, time_of_replacement);
3874        assert_eq!(new_child1_properties.change_time, time_of_replacement);
3875        // Only ctime for child2 should be updated
3876        let moved_child2_properties = child2.get_properties().await.expect("get_properties failed");
3877        assert_eq!(moved_child2_properties.change_time, time_of_replacement);
3878        assert_eq!(moved_child2_properties.creation_time, child2_properties.creation_time);
3879        assert_eq!(moved_child2_properties.access_time, child2_properties.access_time);
3880        assert_eq!(moved_child2_properties.modification_time, child2_properties.modification_time);
3881        fs.close().await.expect("Close failed");
3882    }
3883
3884    #[fuchsia::test]
3885    async fn test_unlink_timestamps() {
3886        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3887        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3888        let dir;
3889        let foo;
3890        let mut transaction = fs
3891            .clone()
3892            .new_transaction(lock_keys![], Options::default())
3893            .await
3894            .expect("new_transaction failed");
3895        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3896            .await
3897            .expect("create failed");
3898        foo =
3899            dir.create_child_file(&mut transaction, "foo").await.expect("create_child_dir failed");
3900
3901        transaction.commit().await.expect("commit failed");
3902        let dir_properties = dir.get_properties().await.expect("get_properties failed");
3903        let foo_properties = foo.get_properties().await.expect("get_properties failed");
3904
3905        transaction = fs
3906            .clone()
3907            .new_transaction(
3908                lock_keys![
3909                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3910                    LockKey::object(fs.root_store().store_object_id(), foo.object_id()),
3911                ],
3912                Options::default(),
3913            )
3914            .await
3915            .expect("new_transaction failed");
3916        assert_matches!(
3917            replace_child(&mut transaction, None, (&dir, "foo"))
3918                .await
3919                .expect("replace_child failed"),
3920            ReplacedChild::Object(_)
3921        );
3922        transaction.commit().await.expect("commit failed");
3923        // Both mtime and ctime for dir should be updated
3924        let new_dir_properties = dir.get_properties().await.expect("get_properties failed");
3925        let time_of_replacement = new_dir_properties.change_time;
3926        assert!(new_dir_properties.change_time > dir_properties.change_time);
3927        assert_eq!(new_dir_properties.modification_time, time_of_replacement);
3928        // Only ctime for foo should be updated
3929        let moved_foo_properties = foo.get_properties().await.expect("get_properties failed");
3930        assert_eq!(moved_foo_properties.change_time, time_of_replacement);
3931        assert_eq!(moved_foo_properties.creation_time, foo_properties.creation_time);
3932        assert_eq!(moved_foo_properties.access_time, foo_properties.access_time);
3933        assert_eq!(moved_foo_properties.modification_time, foo_properties.modification_time);
3934        fs.close().await.expect("Close failed");
3935    }
3936
3937    #[fuchsia::test]
3938    async fn test_replace_dir_timestamps() {
3939        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3940        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3941        let dir;
3942        let child_dir1;
3943        let child_dir2;
3944        let foo;
3945        let mut transaction = fs
3946            .clone()
3947            .new_transaction(lock_keys![], Options::default())
3948            .await
3949            .expect("new_transaction failed");
3950        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3951            .await
3952            .expect("create failed");
3953        child_dir1 =
3954            dir.create_child_dir(&mut transaction, "dir1").await.expect("create_child_dir failed");
3955        child_dir2 =
3956            dir.create_child_dir(&mut transaction, "dir2").await.expect("create_child_dir failed");
3957        foo = child_dir1
3958            .create_child_dir(&mut transaction, "foo")
3959            .await
3960            .expect("create_child_dir failed");
3961        transaction.commit().await.expect("commit failed");
3962        let dir_props = dir.get_properties().await.expect("get_properties failed");
3963        let foo_props = foo.get_properties().await.expect("get_properties failed");
3964
3965        transaction = fs
3966            .clone()
3967            .new_transaction(
3968                lock_keys![
3969                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3970                    LockKey::object(fs.root_store().store_object_id(), child_dir1.object_id()),
3971                    LockKey::object(fs.root_store().store_object_id(), child_dir2.object_id()),
3972                    LockKey::object(fs.root_store().store_object_id(), foo.object_id()),
3973                ],
3974                Options::default(),
3975            )
3976            .await
3977            .expect("new_transaction failed");
3978        assert_matches!(
3979            replace_child(&mut transaction, Some((&child_dir1, "foo")), (&dir, "dir2"))
3980                .await
3981                .expect("replace_child failed"),
3982            ReplacedChild::Directory(_)
3983        );
3984        transaction.commit().await.expect("commit failed");
3985        // Both mtime and ctime for dir should be updated
3986        let new_dir_props = dir.get_properties().await.expect("get_properties failed");
3987        let time_of_replacement = new_dir_props.change_time;
3988        assert!(new_dir_props.change_time > dir_props.change_time);
3989        assert_eq!(new_dir_props.modification_time, time_of_replacement);
3990        // Both mtime and ctime for dir1 should be updated
3991        let new_dir1_props = child_dir1.get_properties().await.expect("get_properties failed");
3992        let time_of_replacement = new_dir1_props.change_time;
3993        assert_eq!(new_dir1_props.change_time, time_of_replacement);
3994        assert_eq!(new_dir1_props.modification_time, time_of_replacement);
3995        // Only ctime for foo should be updated
3996        let moved_foo_props = foo.get_properties().await.expect("get_properties failed");
3997        assert_eq!(moved_foo_props.change_time, time_of_replacement);
3998        assert_eq!(moved_foo_props.creation_time, foo_props.creation_time);
3999        assert_eq!(moved_foo_props.access_time, foo_props.access_time);
4000        assert_eq!(moved_foo_props.modification_time, foo_props.modification_time);
4001        fs.close().await.expect("Close failed");
4002    }
4003
4004    #[fuchsia::test]
4005    async fn test_create_casefold_directory() {
4006        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4007        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4008        let object_id = {
4009            let mut transaction = fs
4010                .clone()
4011                .new_transaction(lock_keys![], Options::default())
4012                .await
4013                .expect("new_transaction failed");
4014            let dir = Directory::create(&mut transaction, &fs.root_store(), None)
4015                .await
4016                .expect("create failed");
4017
4018            let child_dir = dir
4019                .create_child_dir(&mut transaction, "foo")
4020                .await
4021                .expect("create_child_dir failed");
4022            let _child_dir_file = child_dir
4023                .create_child_file(&mut transaction, "bAr")
4024                .await
4025                .expect("create_child_file failed");
4026            transaction.commit().await.expect("commit failed");
4027            dir.object_id()
4028        };
4029        fs.close().await.expect("Close failed");
4030        let device = fs.take_device().await;
4031
4032        // We now have foo/bAr which should be case sensitive (casefold not enabled).
4033
4034        device.reopen(false);
4035        let fs = FxFilesystem::open(device).await.expect("open failed");
4036        {
4037            let dir = Directory::open(&fs.root_store(), object_id).await.expect("open failed");
4038            let (object_id, object_descriptor, _) =
4039                dir.lookup("foo").await.expect("lookup failed").expect("not found");
4040            assert_eq!(object_descriptor, ObjectDescriptor::Directory);
4041            let child_dir =
4042                Directory::open(&fs.root_store(), object_id).await.expect("open failed");
4043            assert!(!child_dir.casefold());
4044            assert!(child_dir.lookup("BAR").await.expect("lookup failed").is_none());
4045            let (object_id, descriptor, _) =
4046                child_dir.lookup("bAr").await.expect("lookup failed").unwrap();
4047            assert_eq!(descriptor, ObjectDescriptor::File);
4048
4049            // We can't set casefold now because the directory isn't empty.
4050            child_dir.set_casefold(true).await.expect_err("not empty");
4051
4052            // Delete the file and subdir and try again.
4053            let mut transaction = fs
4054                .clone()
4055                .new_transaction(
4056                    lock_keys![
4057                        LockKey::object(fs.root_store().store_object_id(), child_dir.object_id()),
4058                        LockKey::object(fs.root_store().store_object_id(), object_id),
4059                    ],
4060                    Options::default(),
4061                )
4062                .await
4063                .expect("new_transaction failed");
4064            assert_matches!(
4065                replace_child(&mut transaction, None, (&child_dir, "bAr"))
4066                    .await
4067                    .expect("replace_child failed"),
4068                ReplacedChild::Object(..)
4069            );
4070            transaction.commit().await.expect("commit failed");
4071
4072            // This time enabling casefold should succeed.
4073            child_dir.set_casefold(true).await.expect("set casefold");
4074
4075            assert!(child_dir.casefold());
4076
4077            // Create the file again now that casefold is enabled.
4078            let mut transaction = fs
4079                .clone()
4080                .new_transaction(
4081                    lock_keys![LockKey::object(
4082                        fs.root_store().store_object_id(),
4083                        child_dir.object_id()
4084                    ),],
4085                    Options::default(),
4086                )
4087                .await
4088                .expect("new_transaction failed");
4089            let _child_dir_file = child_dir
4090                .create_child_file(&mut transaction, "bAr")
4091                .await
4092                .expect("create_child_file failed");
4093            transaction.commit().await.expect("commit failed");
4094
4095            // Check that we can lookup via a case insensitive name.
4096            assert!(child_dir.lookup("BAR").await.expect("lookup failed").is_some());
4097            assert!(child_dir.lookup("bAr").await.expect("lookup failed").is_some());
4098
4099            // Enabling casefold should fail again as the dir is not empty.
4100            child_dir.set_casefold(true).await.expect_err("set casefold");
4101            assert!(child_dir.casefold());
4102
4103            // Confirm that casefold will affect created subdirectories.
4104            let mut transaction = fs
4105                .clone()
4106                .new_transaction(
4107                    lock_keys![LockKey::object(
4108                        fs.root_store().store_object_id(),
4109                        child_dir.object_id()
4110                    ),],
4111                    Options::default(),
4112                )
4113                .await
4114                .expect("new_transaction failed");
4115            let sub_dir = child_dir
4116                .create_child_dir(&mut transaction, "sub")
4117                .await
4118                .expect("create_sub_dir failed");
4119            transaction.commit().await.expect("commit failed");
4120            assert!(sub_dir.casefold());
4121        };
4122        fs.close().await.expect("Close failed");
4123    }
4124
4125    #[fuchsia::test]
4126    async fn test_create_casefold_encrypted_directory() {
4127        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4128        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4129        let proxy_filename: ProxyFilename;
4130        let object_id;
4131        {
4132            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4133            let root_volume = root_volume(fs.clone()).await.unwrap();
4134            let store = root_volume
4135                .new_volume(
4136                    "vol",
4137                    NewChildStoreOptions {
4138                        options: StoreOptions {
4139                            crypt: Some(crypt.clone()),
4140                            ..StoreOptions::default()
4141                        },
4142                        ..Default::default()
4143                    },
4144                )
4145                .await
4146                .unwrap();
4147
4148            // Create a (very weak) key for our encrypted directory.
4149            crypt
4150                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
4151                .expect("add wrapping key failed");
4152
4153            object_id = {
4154                let mut transaction = fs
4155                    .clone()
4156                    .new_transaction(
4157                        lock_keys![LockKey::object(
4158                            fs.root_store().store_object_id(),
4159                            store.store_object_id()
4160                        ),],
4161                        Options::default(),
4162                    )
4163                    .await
4164                    .expect("new_transaction failed");
4165                let dir = Directory::create(&mut transaction, &store, Some(WRAPPING_KEY_ID))
4166                    .await
4167                    .expect("create failed");
4168
4169                transaction.commit().await.expect("commit");
4170                dir.object_id()
4171            };
4172            let dir = Directory::open(&store, object_id).await.expect("open failed");
4173
4174            dir.set_casefold(true).await.expect("set casefold");
4175            assert!(dir.casefold());
4176
4177            let mut transaction = fs
4178                .clone()
4179                .new_transaction(
4180                    lock_keys![LockKey::object(store.store_object_id(), dir.object_id()),],
4181                    Options::default(),
4182                )
4183                .await
4184                .expect("new_transaction failed");
4185            let _file = dir
4186                .create_child_file(&mut transaction, "bAr")
4187                .await
4188                .expect("create_child_file failed");
4189            transaction.commit().await.expect("commit failed");
4190
4191            // Check that we can look up the original name.
4192            assert!(dir.lookup("bAr").await.expect("original lookup failed").is_some());
4193
4194            // Derive the proxy filename now, for use later when operating on the locked volume
4195            // as we won't have the key then.
4196            let key = dir.get_fscrypt_key().await.expect("key").into_cipher().unwrap();
4197            let encrypted_name =
4198                encrypt_filename(&*key, dir.object_id(), "bAr").expect("encrypt_filename");
4199            let hash_code = key.hash_code_casefold("bAr");
4200            proxy_filename = ProxyFilename::new_with_hash_code(hash_code as u64, &encrypted_name);
4201
4202            // Check that we can lookup via a case insensitive name.
4203            assert!(dir.lookup("BAR").await.expect("casefold lookup failed").is_some());
4204
4205            // Check hash values generated are stable across case.
4206            assert_eq!(key.hash_code_casefold("bar"), key.hash_code_casefold("BaR"));
4207
4208            // We can't easily check iteration from here as we only get encrypted entries so
4209            // we just count instead.
4210            let mut count = 0;
4211            let layer_set = dir.store().tree().layer_set();
4212            let mut merger = layer_set.merger();
4213            let mut iter = dir.iter(&mut merger).await.expect("iter");
4214            while let Some(_entry) = iter.get() {
4215                count += 1;
4216                iter.advance().await.expect("advance");
4217            }
4218            assert_eq!(1, count, "unexpected number of entries.");
4219
4220            fs.close().await.expect("Close failed");
4221        }
4222
4223        let device = fs.take_device().await;
4224
4225        // Now try and read the encrypted directory without keys.
4226
4227        device.reopen(false);
4228        let fs = FxFilesystem::open(device).await.expect("open failed");
4229        {
4230            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4231            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4232            let store = root_volume
4233                .volume(
4234                    "vol",
4235                    StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
4236                )
4237                .await
4238                .expect("volume failed");
4239            let dir = Directory::open(&store, object_id).await.expect("open failed");
4240            assert!(dir.casefold());
4241
4242            // Check that we can NOT look up the original name.
4243            assert!(dir.lookup("bAr").await.expect("lookup failed").is_none());
4244            // We should instead see the proxy filename.
4245            let filename: String = proxy_filename.into();
4246            assert!(dir.lookup(&filename).await.expect("lookup failed").is_some());
4247
4248            let layer_set = dir.store().tree().layer_set();
4249            let mut merger = layer_set.merger();
4250            let mut iter = dir.iter(&mut merger).await.expect("iter");
4251            let item = iter.get().expect("expect item");
4252            let filename: String = proxy_filename.into();
4253            assert_eq!(item.0, &filename);
4254            iter.advance().await.expect("advance");
4255            assert_eq!(None, iter.get());
4256
4257            crate::fsck::fsck(fs.clone()).await.unwrap();
4258            crate::fsck::fsck_volume(fs.as_ref(), store.store_object_id(), Some(crypt.clone()))
4259                .await
4260                .unwrap();
4261
4262            fs.close().await.expect("Close failed");
4263        }
4264    }
4265
4266    /// Search for a pair of filenames that encode to the same casefold hash and same
4267    /// filename prefix, but different sha256.
4268    /// We are specifically looking for a case where encrypted child of a > encrypted child of b
4269    /// but proxy_filename of a < proxy filename of b or vice versa.
4270    /// This is to fully test the iterator logic for locked directories.
4271    ///
4272    /// Note this is a SLOW process (~12 seconds on my workstation with release build).
4273    /// For that reason, the solution is hard coded and this function is marked as ignored.
4274    ///
4275    /// Returns a pair of filenames on success, None on failure.
4276    #[allow(dead_code)]
4277    fn find_out_of_order_sha256_long_prefix_pair(
4278        object_id: u64,
4279        key: &Arc<dyn Cipher>,
4280    ) -> Option<[String; 2]> {
4281        let mut collision_map: std::collections::HashMap<u32, (usize, ProxyFilename, Vec<u8>)> =
4282            std::collections::HashMap::new();
4283        for i in 0..(1usize << 32) {
4284            let filename = format!("{:0>176}_{i}", 0);
4285            let encrypted_name =
4286                encrypt_filename(&**key, object_id, &filename).expect("encrypt_filename");
4287            let hash_code = key.hash_code_casefold(&filename);
4288            let a = ProxyFilename::new_with_hash_code(hash_code as u64, &encrypted_name);
4289            let hash_code = a.hash_code as u32;
4290            if let Some((j, b, b_encrypted_name)) = collision_map.get(&hash_code) {
4291                assert_eq!(a.filename, b.filename);
4292                if encrypted_name.cmp(b_encrypted_name) != a.sha256.cmp(&b.sha256) {
4293                    return Some([format!("{:0>176}_{i}", 0), format!("{:0>176}_{j}", 0)]);
4294                }
4295            } else {
4296                collision_map.insert(hash_code, (i, a, encrypted_name));
4297            }
4298        }
4299        None
4300    }
4301
4302    #[fuchsia::test]
4303    async fn test_proxy_filename() {
4304        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4305        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4306        let mut filenames = Vec::new();
4307        let object_id;
4308        {
4309            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4310            let root_volume = root_volume(fs.clone()).await.unwrap();
4311            let store = root_volume
4312                .new_volume(
4313                    "vol",
4314                    NewChildStoreOptions {
4315                        options: StoreOptions {
4316                            crypt: Some(crypt.clone()),
4317                            ..StoreOptions::default()
4318                        },
4319                        ..Default::default()
4320                    },
4321                )
4322                .await
4323                .unwrap();
4324
4325            // Create a (very weak) key for our encrypted directory.
4326            crypt
4327                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
4328                .expect("add wrapping key failed");
4329
4330            object_id = {
4331                let mut transaction = fs
4332                    .clone()
4333                    .new_transaction(
4334                        lock_keys![LockKey::object(
4335                            fs.root_store().store_object_id(),
4336                            store.store_object_id()
4337                        ),],
4338                        Options::default(),
4339                    )
4340                    .await
4341                    .expect("new_transaction failed");
4342                let dir = Directory::create(&mut transaction, &store, Some(WRAPPING_KEY_ID))
4343                    .await
4344                    .expect("create failed");
4345
4346                transaction.commit().await.expect("commit");
4347                dir.object_id()
4348            };
4349
4350            let dir = Directory::open(&store, object_id).await.expect("open failed");
4351
4352            dir.set_casefold(true).await.expect("set casefold");
4353            assert!(dir.casefold());
4354
4355            let key = dir.get_fscrypt_key().await.expect("key").into_cipher().unwrap();
4356
4357            // Nb: We use a rather expensive brute force search to find two filenames that:
4358            //   1. Have the same hash_code.
4359            //   2. Have the same prefix.
4360            //   3. Have encrypted names and sha256 that sort differently.
4361            // This is to exercise iter_from and lookup() handling scanning of locked directories.
4362            // This search returns stable results so in the interest of cheap tests, this code
4363            // is commented out but should be equivalent to the constants below.
4364            // let collision_pair =
4365            //     find_out_of_order_sha256_long_prefix_pair(dir.object_id(), &key).unwrap();
4366            let collision_pair =
4367                [format!("{:0>176}_{}", 0, 93515), format!("{:0>176}_{}", 0, 15621)];
4368
4369            // Create set of files with a common prefix, long enough to exceed prefix length of 48.
4370            // The first 48 encrypted name bytes will be the same, but the `sha256` will differ.
4371            for filename in (0..64)
4372                .into_iter()
4373                .map(|i| format!("{:0>176}_{i}", 0))
4374                .chain(collision_pair.into_iter())
4375            {
4376                let hash_code = key.hash_code_casefold(&filename);
4377                let encrypted_name =
4378                    encrypt_filename(&*key, dir.object_id(), &filename).expect("encrypt_filename");
4379                let proxy_filename =
4380                    ProxyFilename::new_with_hash_code(hash_code as u64, &encrypted_name);
4381                let mut transaction = fs
4382                    .clone()
4383                    .new_transaction(
4384                        lock_keys![LockKey::object(store.store_object_id(), dir.object_id()),],
4385                        Options::default(),
4386                    )
4387                    .await
4388                    .expect("new_transaction failed");
4389                let file = dir
4390                    .create_child_file(&mut transaction, &filename)
4391                    .await
4392                    .expect("create_child_file failed");
4393                filenames.push((proxy_filename, file.object_id()));
4394                transaction.commit().await.expect("commit failed");
4395            }
4396
4397            fs.close().await.expect("Close failed");
4398        }
4399
4400        let device = fs.take_device().await;
4401
4402        // Now try and read the encrypted directory without keys.
4403        device.reopen(false);
4404        let fs = FxFilesystem::open(device).await.expect("open failed");
4405        {
4406            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4407            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4408            let store = root_volume
4409                .volume(
4410                    "vol",
4411                    StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
4412                )
4413                .await
4414                .expect("volume failed");
4415            let dir = Directory::open(&store, object_id).await.expect("open failed");
4416            assert!(dir.casefold());
4417
4418            // Ensure uniqueness of the proxy filenames.
4419            assert_eq!(
4420                filenames.iter().map(|(name, _)| (*name).into()).collect::<HashSet<String>>().len(),
4421                filenames.len()
4422            );
4423
4424            let filename = filenames[0].0.filename.clone();
4425            for (proxy_filename, object_id) in &filenames {
4426                // We used such a long prefix that we expect all files to share it.
4427                assert_eq!(filename, proxy_filename.filename);
4428
4429                let proxy_filename_str: String = (*proxy_filename).into();
4430                let item = dir
4431                    .lookup(&proxy_filename_str)
4432                    .await
4433                    .expect("lookup failed")
4434                    .expect("lookup is not None");
4435                assert_eq!(item.0, *object_id, "Mismatch for filename '{proxy_filename:?}'");
4436            }
4437
4438            fs.close().await.expect("Close failed");
4439        }
4440    }
4441
4442    #[fuchsia::test]
4443    async fn test_replace_directory_and_tombstone_on_remount() {
4444        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
4445        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4446        let crypt = Arc::new(new_insecure_crypt());
4447        {
4448            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4449            let store = root_volume
4450                .new_volume(
4451                    "test",
4452                    NewChildStoreOptions {
4453                        options: StoreOptions {
4454                            crypt: Some(crypt.clone() as Arc<dyn Crypt>),
4455                            ..StoreOptions::default()
4456                        },
4457                        ..Default::default()
4458                    },
4459                )
4460                .await
4461                .expect("new_volume failed");
4462
4463            let mut transaction = fs
4464                .clone()
4465                .new_transaction(
4466                    lock_keys![LockKey::object(
4467                        store.store_object_id(),
4468                        store.root_directory_object_id()
4469                    )],
4470                    Options::default(),
4471                )
4472                .await
4473                .expect("new transaction failed");
4474
4475            let root_directory = Directory::open(&store, store.root_directory_object_id())
4476                .await
4477                .expect("open failed");
4478            let _directory = root_directory
4479                .create_child_dir(&mut transaction, "foo")
4480                .await
4481                .expect("create_child_dir failed");
4482            let directory = root_directory
4483                .create_child_dir(&mut transaction, "bar")
4484                .await
4485                .expect("create_child_dir failed");
4486            let oid = directory.object_id();
4487
4488            transaction.commit().await.expect("commit failed");
4489
4490            let mut transaction = fs
4491                .clone()
4492                .new_transaction(
4493                    lock_keys![LockKey::object(
4494                        store.store_object_id(),
4495                        store.root_directory_object_id()
4496                    )],
4497                    Options::default(),
4498                )
4499                .await
4500                .expect("new transaction failed");
4501
4502            replace_child_with_object(
4503                &mut transaction,
4504                Some((oid, ObjectDescriptor::Directory)),
4505                (&root_directory, "foo"),
4506                0,
4507                Timestamp::now(),
4508            )
4509            .await
4510            .expect("replace_child_with_object failed");
4511
4512            // If replace_child_with_object erroneously were to queue a tombstone, this will allow
4513            // it to run before we've committed, which will cause the test to fail below when we
4514            // remount and try and tombstone the object again.
4515            yield_to_executor().await;
4516
4517            transaction.commit().await.expect("commit failed");
4518
4519            fs.close().await.expect("close failed");
4520        }
4521
4522        let device = fs.take_device().await;
4523        device.reopen(false);
4524        let fs = FxFilesystem::open(device).await.expect("open failed");
4525        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4526        let _store = root_volume
4527            .volume(
4528                "test",
4529                StoreOptions {
4530                    crypt: Some(crypt.clone() as Arc<dyn Crypt>),
4531                    ..StoreOptions::default()
4532                },
4533            )
4534            .await
4535            .expect("new_volume failed");
4536
4537        // Allow the graveyard to run.
4538        yield_to_executor().await;
4539
4540        fs.close().await.expect("close failed");
4541    }
4542
4543    // This test ensures that the hash function doesn't inadvertently change on us.
4544    // We have seen this happen with FxHasher when we have bumped the version in the past.
4545    // This test is to catch any future regressions due to unstable hashing.
4546    #[fuchsia::test]
4547    async fn test_hash_code_casefold() {
4548        use crate::lsm_tree::types::LayerIterator;
4549        use crate::object_store::{ItemRef, ObjectKey, ObjectKeyData, Query};
4550
4551        // These hash codes are the ones used by the Fxfs cipher.
4552        const CASEFOLD_NAMES: [(&str, &str, u32); 3] = [
4553            ("Straße", "strasse", 3602031996),
4554            ("FooBar", "foobar", 1029040300),
4555            ("DŽ", "Dž", 1717486654),
4556        ];
4557
4558        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
4559        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4560        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4561        let mut crypt = new_insecure_crypt();
4562        crypt.use_fxfs_keys_for_fscrypt_dirs();
4563        let crypt = Arc::new(crypt);
4564        let store = root_volume
4565            .new_volume(
4566                "test",
4567                NewChildStoreOptions {
4568                    options: StoreOptions {
4569                        crypt: Some(crypt.clone() as Arc<dyn Crypt>),
4570                        ..StoreOptions::default()
4571                    },
4572                    ..NewChildStoreOptions::default()
4573                },
4574            )
4575            .await
4576            .expect("new_volume failed");
4577        let root_directory =
4578            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
4579
4580        // Create an fscrypt encrypted directory.
4581        crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).expect("add wrapping key failed");
4582        let mut transaction = fs
4583            .clone()
4584            .new_transaction(
4585                lock_keys![LockKey::object(
4586                    store.store_object_id(),
4587                    store.root_directory_object_id()
4588                )],
4589                Options::default(),
4590            )
4591            .await
4592            .expect("new transaction failed");
4593        let directory = root_directory
4594            .create_child_dir(&mut transaction, "foo")
4595            .await
4596            .expect("create_child_dir failed");
4597        directory
4598            .set_wrapping_key(&mut transaction, WRAPPING_KEY_ID)
4599            .await
4600            .expect("set wrapping_key");
4601        transaction.commit().await.expect("commit failed");
4602
4603        // Enable casefold.
4604        let directory = Directory::open(&store, directory.object_id()).await.expect("open failed");
4605        directory.set_casefold(true).await.expect("enable_casefold");
4606
4607        // Write some test entries.
4608        let mut transaction = fs
4609            .clone()
4610            .new_transaction(
4611                lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
4612                Options::default(),
4613            )
4614            .await
4615            .expect("new transaction failed");
4616        let directory = Directory::open(&store, directory.object_id()).await.expect("open failed");
4617        for &(name, _, _) in &CASEFOLD_NAMES {
4618            let _ = directory
4619                .create_child_dir(&mut transaction, name)
4620                .await
4621                .expect("create_child_dir failed");
4622        }
4623        transaction.commit().await.expect("commit failed");
4624
4625        // Fetch the casefold hashes for these directly
4626        let layer_set = directory.store().tree().layer_set();
4627        let mut merger = layer_set.merger();
4628        let key = ObjectKey::encrypted_child(directory.object_id(), vec![], Some(0));
4629        let mut iter = merger.query(Query::FullRange(&key)).await.expect("query");
4630        let mut object_id_to_hash_code = BTreeMap::new();
4631        loop {
4632            match iter.get() {
4633                Some(ItemRef {
4634                    key:
4635                        ObjectKey {
4636                            data:
4637                                ObjectKeyData::EncryptedCasefoldChild(
4638                                    crate::object_store::object_record::EncryptedCasefoldChild {
4639                                        hash_code,
4640                                        ..
4641                                    },
4642                                ),
4643                            ..
4644                        },
4645                    value:
4646                        crate::object_store::ObjectValue::Child(
4647                            crate::object_store::object_record::ChildValue { object_id, .. },
4648                        ),
4649                    ..
4650                }) => {
4651                    object_id_to_hash_code.insert(*object_id, *hash_code);
4652                }
4653                _ => {
4654                    break;
4655                }
4656            };
4657            iter.advance().await.expect("advance");
4658        }
4659
4660        // Walk the directory tree, decrypting names, and compare hashes to known constants.
4661        for &(name, casefolded_name, hash_code) in &CASEFOLD_NAMES {
4662            if let Some((object_id, _descriptor, _parent_dir_is_locked)) =
4663                directory.lookup(casefolded_name).await.expect("lookup")
4664            {
4665                let actual_hash = object_id_to_hash_code.get(&object_id);
4666                assert_eq!(
4667                    actual_hash,
4668                    Some(&hash_code),
4669                    "Hash for {name} is actually {actual_hash:?}"
4670                );
4671            } else {
4672                panic!("File not found for {casefolded_name} ({name})");
4673            }
4674        }
4675        fs.close().await.expect("close failed");
4676    }
4677
4678    // FBE uses hash code for non-casefold filenames as well.
4679    // We need to ensure that these hashes also don't change unexpectedly.
4680    #[fuchsia::test]
4681    async fn test_hash_code() {
4682        use crate::lsm_tree::types::LayerIterator;
4683        use crate::object_store::{ItemRef, ObjectKey, ObjectKeyData, Query};
4684
4685        // These hash codes are the ones used by the Fxfs cipher.
4686        const CASEFOLD_NAMES: [(&str, u32); 3] =
4687            [("Straße", 433651741), ("FooBar", 3915573179), ("DŽ", 3078551122)];
4688
4689        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
4690        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4691        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4692        let mut crypt = new_insecure_crypt();
4693        crypt.use_fxfs_keys_for_fscrypt_dirs();
4694        let crypt = Arc::new(crypt);
4695        let store = root_volume
4696            .new_volume(
4697                "test",
4698                NewChildStoreOptions {
4699                    options: StoreOptions {
4700                        crypt: Some(crypt.clone() as Arc<dyn Crypt>),
4701                        ..StoreOptions::default()
4702                    },
4703                    ..NewChildStoreOptions::default()
4704                },
4705            )
4706            .await
4707            .expect("new_volume failed");
4708        let root_directory =
4709            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
4710
4711        // Create an fscrypt encrypted directory.
4712        crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).expect("add_wrapping_key failed");
4713        let mut transaction = fs
4714            .clone()
4715            .new_transaction(
4716                lock_keys![LockKey::object(
4717                    store.store_object_id(),
4718                    store.root_directory_object_id()
4719                )],
4720                Options::default(),
4721            )
4722            .await
4723            .expect("new transaction failed");
4724        let directory = root_directory
4725            .create_child_dir(&mut transaction, "foo")
4726            .await
4727            .expect("create_child_dir failed");
4728        directory
4729            .set_wrapping_key(&mut transaction, WRAPPING_KEY_ID)
4730            .await
4731            .expect("set wrapping_key");
4732        transaction.commit().await.expect("commit failed");
4733
4734        // Write some test entries.
4735        let mut transaction = fs
4736            .clone()
4737            .new_transaction(
4738                lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
4739                Options::default(),
4740            )
4741            .await
4742            .expect("new transaction failed");
4743        let directory = Directory::open(&store, directory.object_id()).await.expect("open failed");
4744        for &(name, _) in &CASEFOLD_NAMES {
4745            let _ = directory
4746                .create_child_dir(&mut transaction, name)
4747                .await
4748                .expect("create_child_dir failed");
4749        }
4750        transaction.commit().await.expect("commit failed");
4751
4752        // Fetch the casefold hashes for these directly
4753        let layer_set = directory.store().tree().layer_set();
4754        let mut merger = layer_set.merger();
4755        let key = ObjectKey::encrypted_child(directory.object_id(), vec![], Some(0));
4756        let mut iter = merger.query(Query::FullRange(&key)).await.expect("query");
4757        let mut object_id_to_hash_code = BTreeMap::new();
4758        loop {
4759            match iter.get() {
4760                Some(ItemRef {
4761                    key:
4762                        ObjectKey {
4763                            data:
4764                                ObjectKeyData::EncryptedCasefoldChild(
4765                                    crate::object_store::object_record::EncryptedCasefoldChild {
4766                                        hash_code,
4767                                        ..
4768                                    },
4769                                ),
4770                            ..
4771                        },
4772                    value:
4773                        crate::object_store::ObjectValue::Child(
4774                            crate::object_store::object_record::ChildValue { object_id, .. },
4775                        ),
4776                    ..
4777                }) => {
4778                    object_id_to_hash_code.insert(*object_id, *hash_code);
4779                }
4780                _ => {
4781                    break;
4782                }
4783            };
4784            iter.advance().await.expect("advance");
4785        }
4786
4787        // Walk the directory tree, decrypting names, and compare hashes to known constants.
4788        for &(name, hash_code) in &CASEFOLD_NAMES {
4789            if let Some((object_id, _descriptor, _parent_dir_is_locked)) =
4790                directory.lookup(name).await.expect("lookup")
4791            {
4792                let actual_hash = object_id_to_hash_code.get(&object_id);
4793                assert_eq!(
4794                    actual_hash,
4795                    Some(&hash_code),
4796                    "Hash for {name} is actually {actual_hash:?}"
4797                );
4798            } else {
4799                panic!("File not found for {name}");
4800            }
4801        }
4802        fs.close().await.expect("close failed");
4803    }
4804
4805    #[test_case(false; "non_casefold")]
4806    #[test_case(true; "casefold")]
4807    #[fuchsia::test]
4808    async fn test_lookup_long_filename_in_locked_directory(casefold: bool) {
4809        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4810        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4811        let object_id;
4812        let mut filenames = Vec::new();
4813        {
4814            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4815            let root_volume = root_volume(fs.clone()).await.unwrap();
4816            let store = root_volume
4817                .new_volume(
4818                    "vol",
4819                    NewChildStoreOptions {
4820                        options: StoreOptions {
4821                            crypt: Some(crypt.clone()),
4822                            ..StoreOptions::default()
4823                        },
4824                        ..Default::default()
4825                    },
4826                )
4827                .await
4828                .unwrap();
4829
4830            crypt
4831                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
4832                .expect("add_wrapping_key failed");
4833
4834            object_id = {
4835                let mut transaction = fs
4836                    .clone()
4837                    .new_transaction(
4838                        lock_keys![LockKey::object(
4839                            fs.root_store().store_object_id(),
4840                            store.store_object_id()
4841                        ),],
4842                        Options::default(),
4843                    )
4844                    .await
4845                    .expect("new_transaction failed");
4846                let dir = Directory::create(&mut transaction, &store, Some(WRAPPING_KEY_ID))
4847                    .await
4848                    .expect("create failed");
4849
4850                transaction.commit().await.expect("commit");
4851                dir.object_id()
4852            };
4853            let dir = Directory::open(&store, object_id).await.expect("open failed");
4854            if casefold {
4855                dir.set_casefold(true).await.expect("set casefold");
4856            }
4857
4858            let key = dir.get_fscrypt_key().await.expect("key").into_cipher().unwrap();
4859
4860            for len in [144, 145, 255] {
4861                let filename = "a".repeat(len);
4862                let encrypted_name =
4863                    encrypt_filename(&*key, dir.object_id(), &filename).expect("encrypt_filename");
4864                let proxy_filename = if casefold {
4865                    let hash_code = key.hash_code_casefold(&filename);
4866                    ProxyFilename::new_with_hash_code(hash_code as u64, &encrypted_name)
4867                } else {
4868                    ProxyFilename::new(&encrypted_name)
4869                };
4870                let mut transaction = fs
4871                    .clone()
4872                    .new_transaction(
4873                        lock_keys![LockKey::object(store.store_object_id(), dir.object_id()),],
4874                        Options::default(),
4875                    )
4876                    .await
4877                    .expect("new_transaction failed");
4878                let file = dir
4879                    .create_child_file(&mut transaction, &filename)
4880                    .await
4881                    .expect("create_child_file failed");
4882                filenames.push((proxy_filename, file.object_id()));
4883                transaction.commit().await.expect("commit failed");
4884            }
4885
4886            fs.close().await.expect("Close failed");
4887        }
4888
4889        let device = fs.take_device().await;
4890        device.reopen(false);
4891        let fs = FxFilesystem::open(device).await.expect("open failed");
4892        {
4893            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4894            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4895            let store = root_volume
4896                .volume(
4897                    "vol",
4898                    StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
4899                )
4900                .await
4901                .expect("volume failed");
4902            let dir = Directory::open(&store, object_id).await.expect("open failed");
4903
4904            // Verify that iteration works.
4905            let layer_set = dir.store().tree().layer_set();
4906            let mut merger = layer_set.merger();
4907            let mut iter = dir.iter(&mut merger).await.expect("iter failed");
4908            let mut entries = Vec::new();
4909            while let Some((name, _, _)) = iter.get() {
4910                entries.push(name.to_string());
4911                iter.advance().await.expect("advance failed");
4912            }
4913            assert_eq!(entries.len(), filenames.len());
4914
4915            for (proxy_filename, object_id) in &filenames {
4916                let proxy_filename_str: String = (*proxy_filename).into();
4917                assert!(entries.contains(&proxy_filename_str));
4918                let item = dir
4919                    .lookup(&proxy_filename_str)
4920                    .await
4921                    .expect("lookup failed")
4922                    .expect("lookup is not None");
4923                assert_eq!(item.0, *object_id, "Mismatch for filename '{proxy_filename:?}'");
4924            }
4925
4926            fs.close().await.expect("Close failed");
4927        }
4928    }
4929
4930    #[fuchsia::test]
4931    async fn test_lookup_cached_entry_after_unlock() {
4932        const FILENAME: &str = "foo";
4933        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4934        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4935        let object_id;
4936        let proxy_filename;
4937        {
4938            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4939            let root_volume = root_volume(fs.clone()).await.unwrap();
4940            let store = root_volume
4941                .new_volume(
4942                    "vol",
4943                    NewChildStoreOptions {
4944                        options: StoreOptions {
4945                            crypt: Some(crypt.clone()),
4946                            ..StoreOptions::default()
4947                        },
4948                        ..Default::default()
4949                    },
4950                )
4951                .await
4952                .unwrap();
4953
4954            crypt
4955                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
4956                .expect("add_wrapping_key failed");
4957
4958            let mut transaction = fs
4959                .clone()
4960                .new_transaction(
4961                    lock_keys![LockKey::object(
4962                        fs.root_store().store_object_id(),
4963                        store.store_object_id()
4964                    ),],
4965                    Options::default(),
4966                )
4967                .await
4968                .expect("new_transaction failed");
4969            let dir = Directory::create(&mut transaction, &store, Some(WRAPPING_KEY_ID))
4970                .await
4971                .expect("create failed");
4972            transaction.commit().await.expect("commit");
4973            object_id = dir.object_id();
4974
4975            let key = dir.get_fscrypt_key().await.expect("key").into_cipher().unwrap();
4976            let encrypted_name =
4977                encrypt_filename(&*key, object_id, FILENAME).expect("encrypt_filename");
4978            proxy_filename = ProxyFilename::new(&encrypted_name);
4979
4980            let mut transaction = fs
4981                .clone()
4982                .new_transaction(
4983                    lock_keys![LockKey::object(store.store_object_id(), object_id),],
4984                    Options::default(),
4985                )
4986                .await
4987                .expect("new_transaction failed");
4988            dir.create_child_file(&mut transaction, FILENAME)
4989                .await
4990                .expect("create_child_file failed");
4991            transaction.commit().await.expect("commit failed");
4992
4993            fs.close().await.expect("Close failed");
4994        }
4995
4996        let device = fs.take_device().await;
4997        device.reopen(false);
4998        let fs = FxFilesystem::open(device).await.expect("open failed");
4999        {
5000            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
5001            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
5002            let store = root_volume
5003                .volume(
5004                    "vol",
5005                    StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
5006                )
5007                .await
5008                .expect("volume failed");
5009            let dir = Directory::open(&store, object_id).await.expect("open failed");
5010
5011            let proxy_filename_str: String = proxy_filename.into();
5012            // This should succeed because the directory is locked.
5013            dir.lookup(&proxy_filename_str)
5014                .await
5015                .expect("lookup failed")
5016                .expect("lookup is not None");
5017
5018            crypt
5019                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
5020                .expect("add_wrapping_key failed");
5021
5022            // This should fail because the directory is now unlocked and we shouldn't be able to
5023            // find the file using its encrypted name.
5024            assert!(dir.lookup(&proxy_filename_str).await.expect("lookup failed").is_none());
5025
5026            fs.close().await.expect("Close failed");
5027        }
5028    }
5029
5030    #[test_case(false, false; "no_encryption_no_casefold")]
5031    #[test_case(false, true; "no_encryption_casefold")]
5032    #[test_case(true, false; "encryption_no_casefold")]
5033    #[test_case(true, true; "encryption_casefold")]
5034    #[fuchsia::test]
5035    async fn test_traversal_position(encrypted: bool, casefold: bool) {
5036        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
5037        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
5038        {
5039            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
5040            let crypt = Arc::new(new_insecure_crypt());
5041            crypt
5042                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
5043                .expect("add_wrapping_key failed");
5044            let store = root_volume
5045                .new_volume(
5046                    "test",
5047                    NewChildStoreOptions {
5048                        options: StoreOptions {
5049                            crypt: Some(crypt.clone() as Arc<dyn Crypt>),
5050                            ..StoreOptions::default()
5051                        },
5052                        ..Default::default()
5053                    },
5054                )
5055                .await
5056                .expect("new_volume failed");
5057            let mut root_dir = Directory::open(&store, store.root_directory_object_id())
5058                .await
5059                .expect("open failed");
5060            if encrypted {
5061                let mut transaction = fs
5062                    .clone()
5063                    .new_transaction(
5064                        lock_keys![LockKey::object(
5065                            store.store_object_id(),
5066                            store.root_directory_object_id()
5067                        )],
5068                        Options::default(),
5069                    )
5070                    .await
5071                    .expect("new_transaction failed");
5072                root_dir.set_wrapping_key(&mut transaction, WRAPPING_KEY_ID).await.unwrap();
5073                transaction.commit().await.unwrap();
5074
5075                // `set_wrapping_key` doesn't update the in-memory state, so reopen the directory.
5076                root_dir = Directory::open(&store, store.root_directory_object_id())
5077                    .await
5078                    .expect("open failed");
5079            }
5080            if casefold {
5081                root_dir.set_casefold(true).await.unwrap();
5082            }
5083
5084            let mut transaction = fs
5085                .clone()
5086                .new_transaction(
5087                    lock_keys![LockKey::object(
5088                        store.store_object_id(),
5089                        store.root_directory_object_id()
5090                    )],
5091                    Options::default(),
5092                )
5093                .await
5094                .expect("new_transaction failed");
5095            let _ = root_dir.create_child_file(&mut transaction, "foo").await.unwrap();
5096            transaction.commit().await.unwrap();
5097
5098            let layer_set = store.tree().layer_set();
5099            let mut merger = layer_set.merger();
5100            let iter = root_dir.iter(&mut merger).await.expect("iter failed");
5101            let pos =
5102                iter.traversal_position(|name| name.to_string(), |bytes| format!("{:?}", bytes));
5103            assert!(
5104                pos.is_some(),
5105                "traversal_position returned None for encrypted={}, casefold={}",
5106                encrypted,
5107                casefold
5108            );
5109        }
5110        fs.close().await.expect("close failed");
5111    }
5112}