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