Skip to main content

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