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::InconsistentEncryptionPolicy);
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            (
1760                DirType::Normal | DirType::Casefold | DirType::LegacyCasefold,
1761                DirType::Normal | DirType::Casefold | DirType::LegacyCasefold,
1762            ) => {}
1763            _ => bail!(FxfsError::InconsistentEncryptionPolicy),
1764        }
1765
1766        transaction.add(store_id, Mutation::replace_or_insert_object(src_key, ObjectValue::None));
1767
1768        src_dir.store().update_attributes(transaction, id, None, Some(now)).await?;
1769        if src_dir.object_id() != dst.0.object_id() {
1770            sub_dirs_delta = if descriptor == ObjectDescriptor::Directory { 1 } else { 0 };
1771            src_dir
1772                .update_dir_attributes_internal(
1773                    transaction,
1774                    src_dir.object_id(),
1775                    MutableAttributesInternal {
1776                        sub_dirs: -sub_dirs_delta,
1777                        modification_time: Some(now.as_nanos()),
1778                        change_time: Some(now),
1779                        ..Default::default()
1780                    },
1781                )
1782                .await?;
1783        }
1784        Some((id, descriptor))
1785    } else {
1786        None
1787    };
1788    replace_child_with_object(
1789        transaction,
1790        src,
1791        dst,
1792        sub_dirs_delta,
1793        is_same_dir_casefold_rename,
1794        now,
1795    )
1796    .await
1797}
1798
1799/// Replaces dst.0/dst.1 with the given object, or unlinks if `src` is None.
1800///
1801/// If |dst.0| already has a child |dst.1|, it is removed from dst.0.  For files, if this was their
1802/// last reference, the file is moved to the graveyard.  For directories, the removed directory will
1803/// be moved to the graveyard (and must be empty).  The caller is responsible for tombstoning files
1804/// (when it is no longer open) and directories (immediately after committing the transaction).
1805///
1806/// `sub_dirs_delta` can be used if `src` is a directory and happened to already be a child of
1807/// `dst`.
1808pub async fn replace_child_with_object<'a, S: HandleOwner>(
1809    transaction: &mut Transaction<'a>,
1810    src: Option<(u64, ObjectDescriptor)>,
1811    dst: (&'a Directory<S>, &str),
1812    mut sub_dirs_delta: i64,
1813    is_same_dir_casefold_rename: bool,
1814    timestamp: Timestamp,
1815) -> Result<ReplacedChild, Error> {
1816    let deleted_info =
1817        if is_same_dir_casefold_rename { None } else { dst.0.lookup_ext(dst.1).await? };
1818
1819    let (deleted_id_and_descriptor, dst_key) = match deleted_info {
1820        Some(entry) => (Some((entry.object_id, entry.descriptor.clone())), Some(entry.key)),
1821        None => (None, None),
1822    };
1823    let store_id = dst.0.store().store_object_id();
1824    // There might be optimizations here that allow us to skip the graveyard where we can delete an
1825    // object in a single transaction (which should be the common case).
1826    let result = match deleted_id_and_descriptor {
1827        Some((old_id, ObjectDescriptor::File | ObjectDescriptor::Symlink)) => {
1828            let was_last_ref = dst.0.store().adjust_refs(transaction, old_id, -1).await?;
1829            dst.0.store().update_attributes(transaction, old_id, None, Some(timestamp)).await?;
1830            if was_last_ref {
1831                ReplacedChild::Object(old_id)
1832            } else {
1833                ReplacedChild::ObjectWithRemainingLinks(old_id)
1834            }
1835        }
1836        Some((old_id, ObjectDescriptor::Directory)) => {
1837            let dir = Directory::open(&dst.0.owner(), old_id).await?;
1838            if dir.has_children().await? {
1839                bail!(FxfsError::NotEmpty);
1840            }
1841            // Directories might have extended attributes which might require multiple transactions
1842            // to delete, so we delete directories via the graveyard.
1843            dst.0.store().add_to_graveyard(transaction, old_id);
1844            sub_dirs_delta -= 1;
1845            ReplacedChild::Directory(old_id)
1846        }
1847        Some((_, ObjectDescriptor::Volume)) => {
1848            bail!(anyhow!(FxfsError::Inconsistent).context("Unexpected volume child"))
1849        }
1850        None => {
1851            if src.is_none() {
1852                // Neither src nor dst exist
1853                bail!(FxfsError::NotFound);
1854            }
1855            ReplacedChild::None
1856        }
1857    };
1858    let new_value = match src {
1859        Some((id, descriptor)) => ObjectValue::child(id, descriptor),
1860        None => ObjectValue::None,
1861    };
1862    let new_key = if matches!(new_value, ObjectValue::None) {
1863        None
1864    } else {
1865        if dst.0.dir_type().is_encrypted() {
1866            match dst.0.get_fscrypt_key().await? {
1867                CipherHolder::Cipher(cipher) => {
1868                    let encrypted_dst_name = encrypt_filename(&*cipher, dst.0.object_id(), dst.1)?;
1869                    let dst_hash_code = if dst.0.dir_type().is_casefold() {
1870                        Some(cipher.hash_code_casefold(dst.1))
1871                    } else {
1872                        cipher.hash_code(encrypted_dst_name.as_bytes(), dst.1)
1873                    };
1874                    Some(ObjectKey::encrypted_child(
1875                        dst.0.object_id(),
1876                        encrypted_dst_name,
1877                        dst_hash_code,
1878                    ))
1879                }
1880                CipherHolder::Unavailable => {
1881                    bail!(FxfsError::NoKey);
1882                }
1883            }
1884        } else {
1885            Some(ObjectKey::child(dst.0.object_id(), dst.1, dst.0.dir_type()))
1886        }
1887    };
1888
1889    if let Some(dst_key) = dst_key
1890        && new_key.as_ref() != Some(&dst_key)
1891    {
1892        transaction.add(store_id, Mutation::replace_or_insert_object(dst_key, ObjectValue::None));
1893    }
1894
1895    if let Some(new_key) = new_key {
1896        transaction.add(store_id, Mutation::replace_or_insert_object(new_key, new_value));
1897    }
1898    dst.0
1899        .update_dir_attributes_internal(
1900            transaction,
1901            dst.0.object_id(),
1902            MutableAttributesInternal {
1903                sub_dirs: sub_dirs_delta,
1904                modification_time: Some(timestamp.as_nanos()),
1905                change_time: Some(timestamp),
1906                ..Default::default()
1907            },
1908        )
1909        .await?;
1910    Ok(result)
1911}
1912
1913#[cfg(test)]
1914mod tests {
1915    use super::{ProxyFilename, encrypt_filename, replace_child_with_object};
1916    use crate::errors::FxfsError;
1917    use crate::filesystem::{FxFilesystem, JournalingObject, SyncOptions};
1918    use crate::fsck::{fsck, fsck_volume};
1919    use crate::object_handle::{ObjectHandle, ReadObjectHandle, WriteObjectHandle};
1920    use crate::object_store::directory::{
1921        Directory, MutableAttributesInternal, ReplacedChild, replace_child,
1922    };
1923    use crate::object_store::object_record::{ObjectKey, ObjectValue, Timestamp};
1924    use crate::object_store::transaction::{Options, lock_keys};
1925    use crate::object_store::volume::root_volume;
1926    use crate::object_store::{
1927        AttributeId, HandleOptions, LockKey, NewChildStoreOptions, ObjectDescriptor, ObjectKind,
1928        ObjectStore, SetExtendedAttributeMode, StoreObjectHandle, StoreOptions,
1929    };
1930    use anyhow::Error;
1931    use assert_matches::assert_matches;
1932    use fidl_fuchsia_io as fio;
1933    use fxfs_crypt_common::CryptBase;
1934    use fxfs_crypto::{Cipher, Crypt, WrappingKeyId};
1935    use fxfs_insecure_crypto::new_insecure_crypt;
1936    use std::collections::HashSet;
1937    use std::future::poll_fn;
1938    use std::sync::Arc;
1939    use std::task::Poll;
1940    use storage_device::DeviceHolder;
1941    use storage_device::fake_device::FakeDevice;
1942    use test_case::test_case;
1943
1944    #[fuchsia::test]
1945    fn test_casefold_equality_implies_hash_equality() {
1946        use fxfs_unicode::CasefoldString;
1947
1948        let test_cases = vec![
1949            ("Hello", "hello"),
1950            ("straße", "STRASSE"),
1951            ("e\u{0301}", "\u{00c9}"), // e + acute accent vs E with acute accent
1952            ("hello\u{00ad}", "hello"), // soft hyphen (ignorable)
1953            ("foo\u{200b}bar", "FOOBAR"), // zero width space (ignorable)
1954        ];
1955
1956        for (a, b) in test_cases {
1957            let cf_a = CasefoldString::new(a.to_string());
1958            let cf_b = CasefoldString::new(b.to_string());
1959            assert_eq!(cf_a, cf_b, "Strings {:?} and {:?} should be equal under casefolding", a, b);
1960
1961            let bytes_a: Vec<u8> =
1962                cf_a.casefold_normalized_chars().collect::<String>().into_bytes();
1963            let bytes_b: Vec<u8> =
1964                cf_b.casefold_normalized_chars().collect::<String>().into_bytes();
1965            assert_eq!(
1966                bytes_a, bytes_b,
1967                "Normalized bytes for {:?} and {:?} should be identical",
1968                a, b
1969            );
1970
1971            let hash_a = fscrypt::direntry::tea_hash_filename(bytes_a);
1972            let hash_b = fscrypt::direntry::tea_hash_filename(bytes_b);
1973            assert_eq!(hash_a, hash_b, "Hashes for {:?} and {:?} should be identical", a, b);
1974        }
1975    }
1976
1977    const TEST_DEVICE_BLOCK_SIZE: u32 = 512;
1978    const WRAPPING_KEY_ID: WrappingKeyId = u128::to_le_bytes(2);
1979
1980    /// The synthetic symlink we return when locked is not usable for anything but we still want
1981    /// it to match that returned by fscrypt so we will verify here that we get back the
1982    /// expected ProxyFilename-derived link content.
1983    #[fuchsia::test]
1984    async fn test_reopen_with_different_crypt_shows_proxy_name() -> Result<(), Error> {
1985        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
1986        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
1987        let symlink_object_id;
1988        {
1989            let crypt = Arc::new(new_insecure_crypt());
1990            crypt
1991                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
1992                .expect("add_wrapping_key failed");
1993            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
1994            let store = root_volume
1995                .new_volume(
1996                    "test",
1997                    NewChildStoreOptions {
1998                        options: StoreOptions {
1999                            crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2000                            ..StoreOptions::default()
2001                        },
2002                        ..Default::default()
2003                    },
2004                )
2005                .await
2006                .expect("new_volume failed");
2007            let mut transaction = fs
2008                .root_store()
2009                .new_transaction(
2010                    lock_keys![LockKey::object(
2011                        store.store_object_id(),
2012                        store.root_directory_object_id()
2013                    )],
2014                    Options::default(),
2015                )
2016                .await
2017                .expect("new_transaction failed");
2018            let root_dir = Directory::open(&store, store.root_directory_object_id())
2019                .await
2020                .expect("open failed");
2021            let _ = root_dir.set_wrapping_key(&mut transaction, WRAPPING_KEY_ID).await?;
2022            transaction.commit().await.unwrap();
2023
2024            let mut transaction = fs
2025                .root_store()
2026                .new_transaction(
2027                    lock_keys![LockKey::object(
2028                        store.store_object_id(),
2029                        store.root_directory_object_id()
2030                    )],
2031                    Options::default(),
2032                )
2033                .await
2034                .expect("new_transaction failed");
2035            let root_dir = Directory::open(&store, store.root_directory_object_id())
2036                .await
2037                .expect("open failed");
2038            symlink_object_id = root_dir
2039                .create_symlink(&mut transaction, b"some_link_text", "a")
2040                .await
2041                .expect("create_symlink failed");
2042            transaction.commit().await.expect("commit failed");
2043        };
2044        fs.close().await.expect("close failed");
2045        let device = fs.take_device().await;
2046        device.reopen(false);
2047
2048        let fs = FxFilesystem::open(device).await.expect("open failed");
2049        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2050        // Open the volume without providing the keys.
2051        let store = root_volume
2052            .volume(
2053                "test",
2054                StoreOptions {
2055                    crypt: Some(Arc::new(new_insecure_crypt())),
2056                    ..StoreOptions::default()
2057                },
2058            )
2059            .await
2060            .expect("volume failed");
2061
2062        let item = store
2063            .tree()
2064            .find(&ObjectKey::object(symlink_object_id))
2065            .await
2066            .expect("find failed")
2067            .expect("found record");
2068        let raw_link = match item.value {
2069            ObjectValue::Object { kind: ObjectKind::EncryptedSymlink { link, .. }, .. } => link,
2070            _ => panic!("Unexpected item {item:?}"),
2071        };
2072        let symlink_target = store.read_symlink(symlink_object_id).await?;
2073        // Locked symlinks always have hash_code of zero.
2074        let expected_symlink_target: String =
2075            ProxyFilename::new_with_hash_code(0, &raw_link).into();
2076        assert_eq!(symlink_target, expected_symlink_target.as_bytes());
2077
2078        fs.close().await.expect("Close failed");
2079        Ok(())
2080    }
2081
2082    async fn yield_to_executor() {
2083        let mut done = false;
2084        poll_fn(|cx| {
2085            if done {
2086                Poll::Ready(())
2087            } else {
2088                done = true;
2089                cx.waker().wake_by_ref();
2090                Poll::Pending
2091            }
2092        })
2093        .await;
2094    }
2095
2096    #[fuchsia::test]
2097    async fn test_create_directory() {
2098        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2099        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2100        let object_id = {
2101            let mut transaction = fs
2102                .root_store()
2103                .new_transaction(lock_keys![], Options::default())
2104                .await
2105                .expect("new_transaction failed");
2106            let dir = Directory::create(&mut transaction, &fs.root_store(), None)
2107                .await
2108                .expect("create failed");
2109
2110            let child_dir = dir
2111                .create_child_dir(&mut transaction, "foo")
2112                .await
2113                .expect("create_child_dir failed");
2114            let _child_dir_file = child_dir
2115                .create_child_file(&mut transaction, "bar")
2116                .await
2117                .expect("create_child_file failed");
2118            let _child_file = dir
2119                .create_child_file(&mut transaction, "baz")
2120                .await
2121                .expect("create_child_file failed");
2122            dir.add_child_volume(&mut transaction, "corge", 100)
2123                .await
2124                .expect("add_child_volume failed");
2125            transaction.commit().await.expect("commit failed");
2126            fs.sync(SyncOptions::default()).await.expect("sync failed");
2127            dir.object_id()
2128        };
2129        fs.close().await.expect("Close failed");
2130        let device = fs.take_device().await;
2131        device.reopen(false);
2132        let fs = FxFilesystem::open(device).await.expect("open failed");
2133        {
2134            let dir = Directory::open(&fs.root_store(), object_id).await.expect("open failed");
2135            let (object_id, object_descriptor, _) =
2136                dir.lookup("foo").await.expect("lookup failed").expect("not found");
2137            assert_eq!(object_descriptor, ObjectDescriptor::Directory);
2138            let child_dir =
2139                Directory::open(&fs.root_store(), object_id).await.expect("open failed");
2140            let (object_id, object_descriptor, _) =
2141                child_dir.lookup("bar").await.expect("lookup failed").expect("not found");
2142            assert_eq!(object_descriptor, ObjectDescriptor::File);
2143            let _child_dir_file = ObjectStore::open_object(
2144                &fs.root_store(),
2145                object_id,
2146                HandleOptions::default(),
2147                None,
2148            )
2149            .await
2150            .expect("open object failed");
2151            let (object_id, object_descriptor, _) =
2152                dir.lookup("baz").await.expect("lookup failed").expect("not found");
2153            assert_eq!(object_descriptor, ObjectDescriptor::File);
2154            let _child_file = ObjectStore::open_object(
2155                &fs.root_store(),
2156                object_id,
2157                HandleOptions::default(),
2158                None,
2159            )
2160            .await
2161            .expect("open object failed");
2162            let (object_id, object_descriptor, _) =
2163                dir.lookup("corge").await.expect("lookup failed").expect("not found");
2164            assert_eq!(object_id, 100);
2165            if let ObjectDescriptor::Volume = object_descriptor {
2166            } else {
2167                panic!("wrong ObjectDescriptor");
2168            }
2169
2170            assert_eq!(dir.lookup("qux").await.expect("lookup failed"), None);
2171        }
2172        fs.close().await.expect("Close failed");
2173    }
2174
2175    #[fuchsia::test]
2176    async fn test_set_wrapping_key_does_not_exist() {
2177        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
2178        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2179        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2180        let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
2181        let store = root_volume
2182            .new_volume(
2183                "test",
2184                NewChildStoreOptions {
2185                    options: StoreOptions {
2186                        crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2187                        ..StoreOptions::default()
2188                    },
2189                    ..NewChildStoreOptions::default()
2190                },
2191            )
2192            .await
2193            .expect("new_volume failed");
2194
2195        let mut transaction = fs
2196            .root_store()
2197            .new_transaction(
2198                lock_keys![LockKey::object(
2199                    store.store_object_id(),
2200                    store.root_directory_object_id()
2201                )],
2202                Options::default(),
2203            )
2204            .await
2205            .expect("new transaction failed");
2206        let root_directory =
2207            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
2208        let directory = root_directory
2209            .create_child_dir(&mut transaction, "foo")
2210            .await
2211            .expect("create_child_dir failed");
2212        transaction.commit().await.expect("commit failed");
2213        let mut transaction = fs
2214            .root_store()
2215            .new_transaction(
2216                lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2217                Options::default(),
2218            )
2219            .await
2220            .expect("new transaction failed");
2221        directory
2222            .set_wrapping_key(&mut transaction, WRAPPING_KEY_ID)
2223            .await
2224            .expect_err("wrapping key id 2 has not been added");
2225        transaction.commit().await.expect("commit failed");
2226        crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).expect("add_wrapping_key failed");
2227        let mut transaction = fs
2228            .root_store()
2229            .new_transaction(
2230                lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2231                Options::default(),
2232            )
2233            .await
2234            .expect("new transaction failed");
2235        directory
2236            .set_wrapping_key(&mut transaction, WRAPPING_KEY_ID)
2237            .await
2238            .expect("wrapping key id 2 has been added");
2239        fs.close().await.expect("Close failed");
2240    }
2241
2242    #[fuchsia::test]
2243    async fn test_set_encryption_policy_on_unencrypted_nonempty_dir() {
2244        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
2245        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2246        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2247        let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
2248        let store = root_volume
2249            .new_volume(
2250                "test",
2251                NewChildStoreOptions {
2252                    options: StoreOptions {
2253                        crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2254                        ..StoreOptions::default()
2255                    },
2256                    ..NewChildStoreOptions::default()
2257                },
2258            )
2259            .await
2260            .expect("new_volume failed");
2261
2262        let mut transaction = fs
2263            .root_store()
2264            .new_transaction(
2265                lock_keys![LockKey::object(
2266                    store.store_object_id(),
2267                    store.root_directory_object_id()
2268                )],
2269                Options::default(),
2270            )
2271            .await
2272            .expect("new transaction failed");
2273        let root_directory =
2274            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
2275        let directory = root_directory
2276            .create_child_dir(&mut transaction, "foo")
2277            .await
2278            .expect("create_child_dir failed");
2279        let _file = directory
2280            .create_child_file(&mut transaction, "bar")
2281            .await
2282            .expect("create_child_file failed");
2283        transaction.commit().await.expect("commit failed");
2284        let mut transaction = fs
2285            .root_store()
2286            .new_transaction(
2287                lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2288                Options::default(),
2289            )
2290            .await
2291            .expect("new transaction failed");
2292        directory
2293            .set_wrapping_key(&mut transaction, WRAPPING_KEY_ID)
2294            .await
2295            .expect_err("directory is not empty");
2296        transaction.commit().await.expect("commit failed");
2297        fs.close().await.expect("Close failed");
2298    }
2299
2300    #[fuchsia::test]
2301    async fn test_create_file_or_subdir_in_locked_directory() {
2302        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
2303        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2304        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2305        let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
2306        let store = root_volume
2307            .new_volume(
2308                "test",
2309                NewChildStoreOptions {
2310                    options: StoreOptions {
2311                        crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2312                        ..StoreOptions::default()
2313                    },
2314                    ..NewChildStoreOptions::default()
2315                },
2316            )
2317            .await
2318            .expect("new_volume failed");
2319
2320        let mut transaction = fs
2321            .root_store()
2322            .new_transaction(
2323                lock_keys![LockKey::object(
2324                    store.store_object_id(),
2325                    store.root_directory_object_id()
2326                )],
2327                Options::default(),
2328            )
2329            .await
2330            .expect("new transaction failed");
2331        let root_directory =
2332            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
2333        let directory = root_directory
2334            .create_child_dir(&mut transaction, "foo")
2335            .await
2336            .expect("create_child_dir failed");
2337        transaction.commit().await.expect("commit failed");
2338        crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).expect("add_wrapping_key failed");
2339        let transaction = fs
2340            .root_store()
2341            .new_transaction(
2342                lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2343                Options::default(),
2344            )
2345            .await
2346            .expect("new transaction failed");
2347        directory
2348            .update_attributes(
2349                transaction,
2350                Some(&fio::MutableNodeAttributes {
2351                    wrapping_key_id: Some(WRAPPING_KEY_ID),
2352                    ..Default::default()
2353                }),
2354                0,
2355                None,
2356            )
2357            .await
2358            .expect("update attributes failed");
2359        crypt.forget_wrapping_key(&WRAPPING_KEY_ID).expect("forget wrapping key failed");
2360        let mut transaction = fs
2361            .root_store()
2362            .new_transaction(
2363                lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2364                Options::default(),
2365            )
2366            .await
2367            .expect("new transaction failed");
2368        directory
2369            .create_child_dir(&mut transaction, "bar")
2370            .await
2371            .expect_err("cannot create a dir inside of a locked encrypted directory");
2372        directory
2373            .create_child_file(&mut transaction, "baz")
2374            .await
2375            .map(|_| ())
2376            .expect_err("cannot create a file inside of a locked encrypted directory");
2377        fs.close().await.expect("Close failed");
2378    }
2379
2380    #[fuchsia::test]
2381    async fn test_replace_child_with_object_in_locked_directory() {
2382        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
2383        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2384        let crypt = Arc::new(new_insecure_crypt());
2385
2386        let (parent_oid, src_oid, dst_oid) = {
2387            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2388            let store = root_volume
2389                .new_volume(
2390                    "test",
2391                    NewChildStoreOptions {
2392                        options: StoreOptions {
2393                            crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2394                            ..StoreOptions::default()
2395                        },
2396                        ..Default::default()
2397                    },
2398                )
2399                .await
2400                .expect("new_volume failed");
2401
2402            let mut transaction = fs
2403                .root_store()
2404                .new_transaction(
2405                    lock_keys![LockKey::object(
2406                        store.store_object_id(),
2407                        store.root_directory_object_id()
2408                    )],
2409                    Options::default(),
2410                )
2411                .await
2412                .expect("new transaction failed");
2413            let root_directory = Directory::open(&store, store.root_directory_object_id())
2414                .await
2415                .expect("open failed");
2416            let directory = root_directory
2417                .create_child_dir(&mut transaction, "foo")
2418                .await
2419                .expect("create_child_dir failed");
2420            transaction.commit().await.expect("commit failed");
2421            crypt
2422                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
2423                .expect("add_wrapping_key failed");
2424            let transaction = fs
2425                .root_store()
2426                .new_transaction(
2427                    lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2428                    Options::default(),
2429                )
2430                .await
2431                .expect("new transaction failed");
2432            directory
2433                .update_attributes(
2434                    transaction,
2435                    Some(&fio::MutableNodeAttributes {
2436                        wrapping_key_id: Some(WRAPPING_KEY_ID),
2437                        ..Default::default()
2438                    }),
2439                    0,
2440                    None,
2441                )
2442                .await
2443                .expect("update attributes failed");
2444            let mut transaction = fs
2445                .root_store()
2446                .new_transaction(
2447                    lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
2448                    Options::default(),
2449                )
2450                .await
2451                .expect("new transaction failed");
2452            let src_child = directory
2453                .create_child_dir(&mut transaction, "fee")
2454                .await
2455                .expect("create_child_dir failed");
2456            let dst_child = directory
2457                .create_child_dir(&mut transaction, "faa")
2458                .await
2459                .expect("create_child_dir failed");
2460            transaction.commit().await.expect("commit failed");
2461            crypt.forget_wrapping_key(&WRAPPING_KEY_ID).expect("forget_wrapping_key failed");
2462            (directory.object_id(), src_child.object_id(), dst_child.object_id())
2463        };
2464        fs.close().await.expect("Close failed");
2465        let device = fs.take_device().await;
2466        device.reopen(false);
2467        let fs = FxFilesystem::open(device).await.expect("open failed");
2468        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2469        let store = root_volume
2470            .volume(
2471                "test",
2472                StoreOptions {
2473                    crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2474                    ..StoreOptions::default()
2475                },
2476            )
2477            .await
2478            .expect("volume failed");
2479
2480        {
2481            let parent_directory = Directory::open(&store, parent_oid).await.expect("open failed");
2482            let layer_set = store.tree().layer_set();
2483            let mut merger = layer_set.merger();
2484            let mut encrypted_src_name = None;
2485            let mut encrypted_dst_name = None;
2486            let mut iter = parent_directory.iter(&mut merger).await.expect("iter_from failed");
2487            while let Some((name, object_id, object_descriptor)) = iter.get() {
2488                assert!(matches!(object_descriptor, ObjectDescriptor::Directory));
2489                if object_id == dst_oid {
2490                    encrypted_dst_name = Some(name.to_string());
2491                } else if object_id == src_oid {
2492                    encrypted_src_name = Some(name.to_string());
2493                }
2494                iter.advance().await.expect("iter advance failed");
2495            }
2496
2497            let src_child = parent_directory
2498                .lookup(&encrypted_src_name.expect("src child not found"))
2499                .await
2500                .expect("lookup failed")
2501                .expect("not found");
2502            let mut transaction = fs
2503                .root_store()
2504                .new_transaction(
2505                    lock_keys![LockKey::object(
2506                        store.store_object_id(),
2507                        parent_directory.object_id(),
2508                    )],
2509                    Options::default(),
2510                )
2511                .await
2512                .expect("new transaction failed");
2513            replace_child_with_object(
2514                &mut transaction,
2515                Some((src_child.0, src_child.1)),
2516                (&parent_directory, &encrypted_dst_name.expect("dst child not found")),
2517                0,
2518                false,
2519                Timestamp::now(),
2520            )
2521            .await
2522            .expect_err("renames should fail within a locked directory");
2523        }
2524        fs.close().await.expect("Close failed");
2525    }
2526
2527    #[fuchsia::test]
2528    async fn test_set_encryption_policy_on_unencrypted_file() {
2529        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
2530        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2531        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
2532        let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
2533        let store = root_volume
2534            .new_volume(
2535                "test",
2536                NewChildStoreOptions {
2537                    options: StoreOptions {
2538                        crypt: Some(crypt.clone() as Arc<dyn Crypt>),
2539                        ..StoreOptions::default()
2540                    },
2541                    ..NewChildStoreOptions::default()
2542                },
2543            )
2544            .await
2545            .expect("new_volume failed");
2546
2547        let mut transaction = fs
2548            .root_store()
2549            .new_transaction(
2550                lock_keys![LockKey::object(
2551                    store.store_object_id(),
2552                    store.root_directory_object_id()
2553                )],
2554                Options::default(),
2555            )
2556            .await
2557            .expect("new transaction failed");
2558        let root_directory =
2559            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
2560        let file_handle = root_directory
2561            .create_child_file(&mut transaction, "foo")
2562            .await
2563            .expect("create_child_dir failed");
2564        transaction.commit().await.expect("commit failed");
2565        let mut transaction = fs
2566            .root_store()
2567            .new_transaction(
2568                lock_keys![LockKey::object(store.store_object_id(), file_handle.object_id())],
2569                Options::default(),
2570            )
2571            .await
2572            .expect("new transaction failed");
2573        file_handle
2574            .update_attributes(
2575                &mut transaction,
2576                Some(&fio::MutableNodeAttributes {
2577                    wrapping_key_id: Some(WRAPPING_KEY_ID),
2578                    ..Default::default()
2579                }),
2580                None,
2581            )
2582            .await
2583            .expect_err("Cannot update the wrapping key id of a file");
2584        fs.close().await.expect("Close failed");
2585    }
2586
2587    #[fuchsia::test]
2588    async fn test_delete_child() {
2589        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2590        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2591        let dir;
2592        let child;
2593        let mut transaction = fs
2594            .root_store()
2595            .new_transaction(lock_keys![], Options::default())
2596            .await
2597            .expect("new_transaction failed");
2598        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2599            .await
2600            .expect("create failed");
2601
2602        child =
2603            dir.create_child_file(&mut transaction, "foo").await.expect("create_child_file failed");
2604        transaction.commit().await.expect("commit failed");
2605
2606        transaction = fs
2607            .root_store()
2608            .new_transaction(
2609                lock_keys![
2610                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2611                    LockKey::object(fs.root_store().store_object_id(), child.object_id()),
2612                ],
2613                Options::default(),
2614            )
2615            .await
2616            .expect("new_transaction failed");
2617        assert_matches!(
2618            replace_child(&mut transaction, None, (&dir, "foo"))
2619                .await
2620                .expect("replace_child failed"),
2621            ReplacedChild::Object(..)
2622        );
2623        transaction.commit().await.expect("commit failed");
2624
2625        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
2626        fs.close().await.expect("Close failed");
2627    }
2628
2629    #[fuchsia::test]
2630    async fn test_delete_child_with_children_fails() {
2631        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2632        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2633        let dir;
2634        let child;
2635        let bar;
2636        let mut transaction = fs
2637            .root_store()
2638            .new_transaction(lock_keys![], Options::default())
2639            .await
2640            .expect("new_transaction failed");
2641        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2642            .await
2643            .expect("create failed");
2644
2645        child =
2646            dir.create_child_dir(&mut transaction, "foo").await.expect("create_child_dir failed");
2647        bar = child
2648            .create_child_file(&mut transaction, "bar")
2649            .await
2650            .expect("create_child_file failed");
2651        transaction.commit().await.expect("commit failed");
2652
2653        transaction = fs
2654            .root_store()
2655            .new_transaction(
2656                lock_keys![
2657                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2658                    LockKey::object(fs.root_store().store_object_id(), child.object_id()),
2659                ],
2660                Options::default(),
2661            )
2662            .await
2663            .expect("new_transaction failed");
2664        assert_eq!(
2665            replace_child(&mut transaction, None, (&dir, "foo"))
2666                .await
2667                .expect_err("replace_child succeeded")
2668                .downcast::<FxfsError>()
2669                .expect("wrong error"),
2670            FxfsError::NotEmpty
2671        );
2672        transaction.commit().await.expect("commit failed");
2673
2674        transaction = fs
2675            .root_store()
2676            .new_transaction(
2677                lock_keys![
2678                    LockKey::object(fs.root_store().store_object_id(), child.object_id()),
2679                    LockKey::object(fs.root_store().store_object_id(), bar.object_id()),
2680                ],
2681                Options::default(),
2682            )
2683            .await
2684            .expect("new_transaction failed");
2685        assert_matches!(
2686            replace_child(&mut transaction, None, (&child, "bar"))
2687                .await
2688                .expect("replace_child failed"),
2689            ReplacedChild::Object(..)
2690        );
2691        transaction.commit().await.expect("commit failed");
2692
2693        transaction = fs
2694            .root_store()
2695            .new_transaction(
2696                lock_keys![
2697                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2698                    LockKey::object(fs.root_store().store_object_id(), child.object_id()),
2699                ],
2700                Options::default(),
2701            )
2702            .await
2703            .expect("new_transaction failed");
2704        assert_matches!(
2705            replace_child(&mut transaction, None, (&dir, "foo"))
2706                .await
2707                .expect("replace_child failed"),
2708            ReplacedChild::Directory(..)
2709        );
2710        transaction.commit().await.expect("commit failed");
2711
2712        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
2713        fs.close().await.expect("Close failed");
2714    }
2715
2716    #[fuchsia::test]
2717    async fn test_delete_and_reinsert_child() {
2718        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2719        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2720        let dir;
2721        let child;
2722        let mut transaction = fs
2723            .root_store()
2724            .new_transaction(lock_keys![], Options::default())
2725            .await
2726            .expect("new_transaction failed");
2727        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2728            .await
2729            .expect("create failed");
2730
2731        child =
2732            dir.create_child_file(&mut transaction, "foo").await.expect("create_child_file failed");
2733        transaction.commit().await.expect("commit failed");
2734
2735        transaction = fs
2736            .root_store()
2737            .new_transaction(
2738                lock_keys![
2739                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2740                    LockKey::object(fs.root_store().store_object_id(), child.object_id()),
2741                ],
2742                Options::default(),
2743            )
2744            .await
2745            .expect("new_transaction failed");
2746        assert_matches!(
2747            replace_child(&mut transaction, None, (&dir, "foo"))
2748                .await
2749                .expect("replace_child failed"),
2750            ReplacedChild::Object(..)
2751        );
2752        transaction.commit().await.expect("commit failed");
2753
2754        transaction = fs
2755            .root_store()
2756            .new_transaction(
2757                lock_keys![LockKey::object(fs.root_store().store_object_id(), dir.object_id())],
2758                Options::default(),
2759            )
2760            .await
2761            .expect("new_transaction failed");
2762        dir.create_child_file(&mut transaction, "foo").await.expect("create_child_file failed");
2763        transaction.commit().await.expect("commit failed");
2764
2765        dir.lookup("foo").await.expect("lookup failed");
2766        fs.close().await.expect("Close failed");
2767    }
2768
2769    #[fuchsia::test]
2770    async fn test_delete_child_persists() {
2771        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2772        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2773        let object_id = {
2774            let dir;
2775            let child;
2776            let mut transaction = fs
2777                .root_store()
2778                .new_transaction(lock_keys![], Options::default())
2779                .await
2780                .expect("new_transaction failed");
2781            dir = Directory::create(&mut transaction, &fs.root_store(), None)
2782                .await
2783                .expect("create failed");
2784
2785            child = dir
2786                .create_child_file(&mut transaction, "foo")
2787                .await
2788                .expect("create_child_file failed");
2789            transaction.commit().await.expect("commit failed");
2790            dir.lookup("foo").await.expect("lookup failed");
2791
2792            transaction = fs
2793                .root_store()
2794                .new_transaction(
2795                    lock_keys![
2796                        LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
2797                        LockKey::object(fs.root_store().store_object_id(), child.object_id()),
2798                    ],
2799                    Options::default(),
2800                )
2801                .await
2802                .expect("new_transaction failed");
2803            assert_matches!(
2804                replace_child(&mut transaction, None, (&dir, "foo"))
2805                    .await
2806                    .expect("replace_child failed"),
2807                ReplacedChild::Object(..)
2808            );
2809            transaction.commit().await.expect("commit failed");
2810
2811            fs.sync(SyncOptions::default()).await.expect("sync failed");
2812            dir.object_id()
2813        };
2814
2815        fs.close().await.expect("Close failed");
2816        let device = fs.take_device().await;
2817        device.reopen(false);
2818        let fs = FxFilesystem::open(device).await.expect("open failed");
2819        let dir = Directory::open(&fs.root_store(), object_id).await.expect("open failed");
2820        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
2821        fs.close().await.expect("Close failed");
2822    }
2823
2824    #[fuchsia::test]
2825    async fn test_replace_child() {
2826        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2827        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2828        let dir;
2829        let child_dir1;
2830        let child_dir2;
2831        let mut transaction = fs
2832            .root_store()
2833            .new_transaction(lock_keys![], Options::default())
2834            .await
2835            .expect("new_transaction failed");
2836        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2837            .await
2838            .expect("create failed");
2839
2840        child_dir1 =
2841            dir.create_child_dir(&mut transaction, "dir1").await.expect("create_child_dir failed");
2842        child_dir2 =
2843            dir.create_child_dir(&mut transaction, "dir2").await.expect("create_child_dir failed");
2844        let file = child_dir1
2845            .create_child_file(&mut transaction, "foo")
2846            .await
2847            .expect("create_child_file failed");
2848        transaction.commit().await.expect("commit failed");
2849
2850        transaction = fs
2851            .root_store()
2852            .new_transaction(
2853                lock_keys![
2854                    LockKey::object(fs.root_store().store_object_id(), child_dir1.object_id()),
2855                    LockKey::object(fs.root_store().store_object_id(), child_dir2.object_id()),
2856                    LockKey::object(fs.root_store().store_object_id(), file.object_id()),
2857                ],
2858                Options::default(),
2859            )
2860            .await
2861            .expect("new_transaction failed");
2862        assert_matches!(
2863            replace_child(&mut transaction, Some((&child_dir1, "foo")), (&child_dir2, "bar"))
2864                .await
2865                .expect("replace_child failed"),
2866            ReplacedChild::None
2867        );
2868        transaction.commit().await.expect("commit failed");
2869
2870        assert_eq!(child_dir1.lookup("foo").await.expect("lookup failed"), None);
2871        child_dir2.lookup("bar").await.expect("lookup failed");
2872        fs.close().await.expect("Close failed");
2873    }
2874
2875    #[fuchsia::test]
2876    async fn test_replace_child_overwrites_dst() {
2877        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2878        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2879        let dir;
2880        let child_dir1;
2881        let child_dir2;
2882        let mut transaction = fs
2883            .root_store()
2884            .new_transaction(lock_keys![], Options::default())
2885            .await
2886            .expect("new_transaction failed");
2887        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2888            .await
2889            .expect("create failed");
2890
2891        child_dir1 =
2892            dir.create_child_dir(&mut transaction, "dir1").await.expect("create_child_dir failed");
2893        child_dir2 =
2894            dir.create_child_dir(&mut transaction, "dir2").await.expect("create_child_dir failed");
2895        let foo = child_dir1
2896            .create_child_file(&mut transaction, "foo")
2897            .await
2898            .expect("create_child_file failed");
2899        let bar = child_dir2
2900            .create_child_file(&mut transaction, "bar")
2901            .await
2902            .expect("create_child_file failed");
2903        let foo_oid = foo.object_id();
2904        let bar_oid = bar.object_id();
2905        transaction.commit().await.expect("commit failed");
2906
2907        {
2908            let mut buf = foo.allocate_buffer(TEST_DEVICE_BLOCK_SIZE as usize).await;
2909            buf.fill(0xaa);
2910            foo.write_or_append(Some(0), buf.as_ref()).await.expect("write failed");
2911            buf.fill(0xbb);
2912            bar.write_or_append(Some(0), buf.as_ref()).await.expect("write failed");
2913        }
2914        std::mem::drop(bar);
2915        std::mem::drop(foo);
2916
2917        transaction = fs
2918            .root_store()
2919            .new_transaction(
2920                lock_keys![
2921                    LockKey::object(fs.root_store().store_object_id(), child_dir1.object_id()),
2922                    LockKey::object(fs.root_store().store_object_id(), child_dir2.object_id()),
2923                    LockKey::object(fs.root_store().store_object_id(), foo_oid),
2924                    LockKey::object(fs.root_store().store_object_id(), bar_oid),
2925                ],
2926                Options::default(),
2927            )
2928            .await
2929            .expect("new_transaction failed");
2930        assert_matches!(
2931            replace_child(&mut transaction, Some((&child_dir1, "foo")), (&child_dir2, "bar"))
2932                .await
2933                .expect("replace_child failed"),
2934            ReplacedChild::Object(..)
2935        );
2936        transaction.commit().await.expect("commit failed");
2937
2938        assert_eq!(child_dir1.lookup("foo").await.expect("lookup failed"), None);
2939
2940        // Check the contents to ensure that the file was replaced.
2941        let (oid, object_descriptor, _) =
2942            child_dir2.lookup("bar").await.expect("lookup failed").expect("not found");
2943        assert_eq!(object_descriptor, ObjectDescriptor::File);
2944        let bar =
2945            ObjectStore::open_object(&child_dir2.owner(), oid, HandleOptions::default(), None)
2946                .await
2947                .expect("Open failed");
2948        let mut buf = bar.allocate_buffer(TEST_DEVICE_BLOCK_SIZE as usize).await;
2949        bar.read(0, buf.as_mut()).await.expect("read failed");
2950        assert_eq!(buf.to_vec(), vec![0xaa; TEST_DEVICE_BLOCK_SIZE as usize]);
2951        fs.close().await.expect("Close failed");
2952    }
2953
2954    #[fuchsia::test]
2955    async fn test_replace_child_fails_if_would_overwrite_nonempty_dir() {
2956        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
2957        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
2958        let dir;
2959        let child_dir1;
2960        let child_dir2;
2961        let mut transaction = fs
2962            .root_store()
2963            .new_transaction(lock_keys![], Options::default())
2964            .await
2965            .expect("new_transaction failed");
2966        dir = Directory::create(&mut transaction, &fs.root_store(), None)
2967            .await
2968            .expect("create failed");
2969
2970        child_dir1 =
2971            dir.create_child_dir(&mut transaction, "dir1").await.expect("create_child_dir failed");
2972        child_dir2 =
2973            dir.create_child_dir(&mut transaction, "dir2").await.expect("create_child_dir failed");
2974        let foo = child_dir1
2975            .create_child_file(&mut transaction, "foo")
2976            .await
2977            .expect("create_child_file failed");
2978        let nested_child = child_dir2
2979            .create_child_dir(&mut transaction, "bar")
2980            .await
2981            .expect("create_child_file failed");
2982        nested_child
2983            .create_child_file(&mut transaction, "baz")
2984            .await
2985            .expect("create_child_file failed");
2986        transaction.commit().await.expect("commit failed");
2987
2988        transaction = fs
2989            .root_store()
2990            .new_transaction(
2991                lock_keys![
2992                    LockKey::object(fs.root_store().store_object_id(), child_dir1.object_id()),
2993                    LockKey::object(fs.root_store().store_object_id(), child_dir2.object_id()),
2994                    LockKey::object(fs.root_store().store_object_id(), foo.object_id()),
2995                    LockKey::object(fs.root_store().store_object_id(), nested_child.object_id()),
2996                ],
2997                Options::default(),
2998            )
2999            .await
3000            .expect("new_transaction failed");
3001        assert_eq!(
3002            replace_child(&mut transaction, Some((&child_dir1, "foo")), (&child_dir2, "bar"))
3003                .await
3004                .expect_err("replace_child succeeded")
3005                .downcast::<FxfsError>()
3006                .expect("wrong error"),
3007            FxfsError::NotEmpty
3008        );
3009        fs.close().await.expect("Close failed");
3010    }
3011
3012    #[fuchsia::test]
3013    async fn test_replace_child_within_dir() {
3014        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3015        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3016        let dir;
3017        let mut transaction = fs
3018            .root_store()
3019            .new_transaction(lock_keys![], Options::default())
3020            .await
3021            .expect("new_transaction failed");
3022        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3023            .await
3024            .expect("create failed");
3025        let foo =
3026            dir.create_child_file(&mut transaction, "foo").await.expect("create_child_file failed");
3027        transaction.commit().await.expect("commit failed");
3028
3029        transaction = fs
3030            .root_store()
3031            .new_transaction(
3032                lock_keys![
3033                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3034                    LockKey::object(fs.root_store().store_object_id(), foo.object_id()),
3035                ],
3036                Options::default(),
3037            )
3038            .await
3039            .expect("new_transaction failed");
3040        assert_matches!(
3041            replace_child(&mut transaction, Some((&dir, "foo")), (&dir, "bar"))
3042                .await
3043                .expect("replace_child failed"),
3044            ReplacedChild::None
3045        );
3046        transaction.commit().await.expect("commit failed");
3047
3048        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
3049        dir.lookup("bar").await.expect("lookup new name failed");
3050        fs.close().await.expect("Close failed");
3051    }
3052
3053    #[fuchsia::test]
3054    async fn test_replace_child_normal_into_encrypted_fails() {
3055        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3056        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3057        let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
3058        crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).expect("add_wrapping_key failed");
3059        let store = root_volume(fs.clone())
3060            .await
3061            .expect("root_volume")
3062            .new_volume(
3063                "vol",
3064                NewChildStoreOptions {
3065                    options: StoreOptions { crypt: Some(crypt.clone()), ..Default::default() },
3066                    ..Default::default()
3067                },
3068            )
3069            .await
3070            .expect("new_volume");
3071
3072        let normal_dir;
3073        let encrypted_dir;
3074        let foo;
3075        let mut transaction = fs
3076            .root_store()
3077            .new_transaction(lock_keys![], Options::default())
3078            .await
3079            .expect("new_transaction failed");
3080        normal_dir =
3081            Directory::create(&mut transaction, &store, None).await.expect("create failed");
3082        encrypted_dir = Directory::create(&mut transaction, &store, Some(WRAPPING_KEY_ID))
3083            .await
3084            .expect("create failed");
3085        foo = normal_dir
3086            .create_child_file(&mut transaction, "foo")
3087            .await
3088            .expect("create_child_file failed");
3089        transaction.commit().await.expect("commit failed");
3090
3091        transaction = fs
3092            .root_store()
3093            .new_transaction(
3094                lock_keys![
3095                    LockKey::object(store.store_object_id(), normal_dir.object_id()),
3096                    LockKey::object(store.store_object_id(), encrypted_dir.object_id()),
3097                    LockKey::object(store.store_object_id(), foo.object_id()),
3098                ],
3099                Options::default(),
3100            )
3101            .await
3102            .expect("new_transaction failed");
3103        assert_eq!(
3104            replace_child(&mut transaction, Some((&normal_dir, "foo")), (&encrypted_dir, "foo"))
3105                .await
3106                .expect_err("replace_child succeeded")
3107                .downcast::<FxfsError>()
3108                .expect("wrong error"),
3109            FxfsError::InconsistentEncryptionPolicy
3110        );
3111        fs.close().await.expect("Close failed");
3112    }
3113
3114    #[fuchsia::test]
3115    async fn test_iterate() {
3116        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3117        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3118        let dir;
3119        let mut transaction = fs
3120            .root_store()
3121            .new_transaction(lock_keys![], Options::default())
3122            .await
3123            .expect("new_transaction failed");
3124        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3125            .await
3126            .expect("create failed");
3127        let _cat =
3128            dir.create_child_file(&mut transaction, "cat").await.expect("create_child_file failed");
3129        let _ball = dir
3130            .create_child_file(&mut transaction, "ball")
3131            .await
3132            .expect("create_child_file failed");
3133        let apple = dir
3134            .create_child_file(&mut transaction, "apple")
3135            .await
3136            .expect("create_child_file failed");
3137        let _dog =
3138            dir.create_child_file(&mut transaction, "dog").await.expect("create_child_file failed");
3139        transaction.commit().await.expect("commit failed");
3140        transaction = fs
3141            .root_store()
3142            .new_transaction(
3143                lock_keys![
3144                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3145                    LockKey::object(fs.root_store().store_object_id(), apple.object_id()),
3146                ],
3147                Options::default(),
3148            )
3149            .await
3150            .expect("new_transaction failed");
3151        replace_child(&mut transaction, None, (&dir, "apple")).await.expect("replace_child failed");
3152        transaction.commit().await.expect("commit failed");
3153        let layer_set = dir.store().tree().layer_set();
3154        let mut merger = layer_set.merger();
3155        let mut iter = dir.iter(&mut merger).await.expect("iter failed");
3156        let mut entries = Vec::new();
3157        while let Some((name, _, _)) = iter.get() {
3158            entries.push(name.to_string());
3159            iter.advance().await.expect("advance failed");
3160        }
3161        assert_eq!(&entries, &["ball", "cat", "dog"]);
3162        fs.close().await.expect("Close failed");
3163    }
3164
3165    #[fuchsia::test]
3166    async fn test_sub_dir_count() {
3167        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3168        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3169        let dir;
3170        let child_dir;
3171        let mut transaction = fs
3172            .root_store()
3173            .new_transaction(lock_keys![], Options::default())
3174            .await
3175            .expect("new_transaction failed");
3176        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3177            .await
3178            .expect("create failed");
3179        child_dir =
3180            dir.create_child_dir(&mut transaction, "foo").await.expect("create_child_dir failed");
3181        transaction.commit().await.expect("commit failed");
3182        assert_eq!(dir.get_properties().await.expect("get_properties failed").sub_dirs, 1);
3183
3184        // Moving within the same directory should not change the sub_dir count.
3185        transaction = fs
3186            .root_store()
3187            .new_transaction(
3188                lock_keys![
3189                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3190                    LockKey::object(fs.root_store().store_object_id(), child_dir.object_id()),
3191                ],
3192                Options::default(),
3193            )
3194            .await
3195            .expect("new_transaction failed");
3196        replace_child(&mut transaction, Some((&dir, "foo")), (&dir, "bar"))
3197            .await
3198            .expect("replace_child failed");
3199        transaction.commit().await.expect("commit failed");
3200
3201        assert_eq!(dir.get_properties().await.expect("get_properties failed").sub_dirs, 1);
3202        assert_eq!(child_dir.get_properties().await.expect("get_properties failed").sub_dirs, 0);
3203
3204        // Moving between two different directories should update source and destination.
3205        transaction = fs
3206            .root_store()
3207            .new_transaction(
3208                lock_keys![LockKey::object(
3209                    fs.root_store().store_object_id(),
3210                    child_dir.object_id()
3211                )],
3212                Options::default(),
3213            )
3214            .await
3215            .expect("new_transaction failed");
3216        let second_child = child_dir
3217            .create_child_dir(&mut transaction, "baz")
3218            .await
3219            .expect("create_child_dir failed");
3220        transaction.commit().await.expect("commit failed");
3221
3222        assert_eq!(child_dir.get_properties().await.expect("get_properties failed").sub_dirs, 1);
3223
3224        transaction = fs
3225            .root_store()
3226            .new_transaction(
3227                lock_keys![
3228                    LockKey::object(fs.root_store().store_object_id(), child_dir.object_id()),
3229                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3230                    LockKey::object(fs.root_store().store_object_id(), second_child.object_id()),
3231                ],
3232                Options::default(),
3233            )
3234            .await
3235            .expect("new_transaction failed");
3236        replace_child(&mut transaction, Some((&child_dir, "baz")), (&dir, "foo"))
3237            .await
3238            .expect("replace_child failed");
3239        transaction.commit().await.expect("commit failed");
3240
3241        assert_eq!(dir.get_properties().await.expect("get_properties failed").sub_dirs, 2);
3242        assert_eq!(child_dir.get_properties().await.expect("get_properties failed").sub_dirs, 0);
3243
3244        // Moving over a directory.
3245        transaction = fs
3246            .root_store()
3247            .new_transaction(
3248                lock_keys![
3249                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3250                    LockKey::object(fs.root_store().store_object_id(), second_child.object_id()),
3251                    LockKey::object(fs.root_store().store_object_id(), child_dir.object_id()),
3252                ],
3253                Options::default(),
3254            )
3255            .await
3256            .expect("new_transaction failed");
3257        replace_child(&mut transaction, Some((&dir, "bar")), (&dir, "foo"))
3258            .await
3259            .expect("replace_child failed");
3260        transaction.commit().await.expect("commit failed");
3261
3262        assert_eq!(dir.get_properties().await.expect("get_properties failed").sub_dirs, 1);
3263
3264        // Unlinking a directory.
3265        transaction = fs
3266            .root_store()
3267            .new_transaction(
3268                lock_keys![
3269                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3270                    LockKey::object(fs.root_store().store_object_id(), child_dir.object_id()),
3271                ],
3272                Options::default(),
3273            )
3274            .await
3275            .expect("new_transaction failed");
3276        replace_child(&mut transaction, None, (&dir, "foo")).await.expect("replace_child failed");
3277        transaction.commit().await.expect("commit failed");
3278
3279        assert_eq!(dir.get_properties().await.expect("get_properties failed").sub_dirs, 0);
3280        fs.close().await.expect("Close failed");
3281    }
3282
3283    #[fuchsia::test]
3284    async fn test_deleted_dir() {
3285        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3286        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3287        let dir;
3288        let mut transaction = fs
3289            .root_store()
3290            .new_transaction(lock_keys![], Options::default())
3291            .await
3292            .expect("new_transaction failed");
3293        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3294            .await
3295            .expect("create failed");
3296        let child =
3297            dir.create_child_dir(&mut transaction, "foo").await.expect("create_child_dir failed");
3298        dir.create_child_dir(&mut transaction, "bar").await.expect("create_child_dir failed");
3299        transaction.commit().await.expect("commit failed");
3300
3301        // Flush the tree so that we end up with records in different layers.
3302        dir.store().flush().await.expect("flush failed");
3303
3304        // Unlink the child directory.
3305        transaction = fs
3306            .root_store()
3307            .new_transaction(
3308                lock_keys![
3309                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
3310                    LockKey::object(fs.root_store().store_object_id(), child.object_id()),
3311                ],
3312                Options::default(),
3313            )
3314            .await
3315            .expect("new_transaction failed");
3316        replace_child(&mut transaction, None, (&dir, "foo")).await.expect("replace_child failed");
3317        transaction.commit().await.expect("commit failed");
3318
3319        // Finding the child should fail now.
3320        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
3321
3322        // But finding "bar" should succeed.
3323        assert!(dir.lookup("bar").await.expect("lookup failed").is_some());
3324
3325        // If we mark dir as deleted, any further operations should fail.
3326        dir.set_deleted();
3327
3328        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
3329        assert_eq!(dir.lookup("bar").await.expect("lookup failed"), None);
3330        assert!(!dir.has_children().await.expect("has_children failed"));
3331
3332        transaction = fs
3333            .root_store()
3334            .new_transaction(lock_keys![], Options::default())
3335            .await
3336            .expect("new_transaction failed");
3337
3338        let assert_access_denied = |result| {
3339            if let Err(e) = result {
3340                assert!(FxfsError::Deleted.matches(&e));
3341            } else {
3342                panic!();
3343            }
3344        };
3345        assert_access_denied(dir.create_child_dir(&mut transaction, "baz").await.map(|_| {}));
3346        assert_access_denied(dir.create_child_file(&mut transaction, "baz").await.map(|_| {}));
3347        assert_access_denied(dir.add_child_volume(&mut transaction, "baz", 1).await);
3348        assert_access_denied(
3349            dir.insert_child(&mut transaction, "baz", 1, ObjectDescriptor::File).await,
3350        );
3351        assert_access_denied(
3352            dir.update_dir_attributes_internal(
3353                &mut transaction,
3354                dir.object_id(),
3355                MutableAttributesInternal {
3356                    creation_time: Some(Timestamp::zero().as_nanos()),
3357                    ..Default::default()
3358                },
3359            )
3360            .await,
3361        );
3362        let layer_set = dir.store().tree().layer_set();
3363        let mut merger = layer_set.merger();
3364        assert_access_denied(dir.iter(&mut merger).await.map(|_| {}));
3365    }
3366
3367    #[fuchsia::test]
3368    async fn test_create_symlink() {
3369        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3370        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3371        let (dir_id, symlink_id) = {
3372            let mut transaction = fs
3373                .root_store()
3374                .new_transaction(lock_keys![], Options::default())
3375                .await
3376                .expect("new_transaction failed");
3377            let dir = Directory::create(&mut transaction, &fs.root_store(), None)
3378                .await
3379                .expect("create failed");
3380
3381            let symlink_id = dir
3382                .create_symlink(&mut transaction, b"link", "foo")
3383                .await
3384                .expect("create_symlink failed");
3385            transaction.commit().await.expect("commit failed");
3386
3387            fs.sync(SyncOptions::default()).await.expect("sync failed");
3388            (dir.object_id(), symlink_id)
3389        };
3390        fs.close().await.expect("Close failed");
3391        let device = fs.take_device().await;
3392        device.reopen(false);
3393        let fs = FxFilesystem::open(device).await.expect("open failed");
3394        {
3395            let dir = Directory::open(&fs.root_store(), dir_id).await.expect("open failed");
3396            assert_eq!(
3397                dir.lookup("foo").await.expect("lookup failed").expect("not found"),
3398                (symlink_id, ObjectDescriptor::Symlink, false)
3399            );
3400        }
3401        fs.close().await.expect("Close failed");
3402    }
3403
3404    #[fuchsia::test]
3405    async fn test_read_symlink() {
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 mut transaction = fs
3409            .root_store()
3410            .new_transaction(lock_keys![], Options::default())
3411            .await
3412            .expect("new_transaction failed");
3413        let store = fs.root_store();
3414        let dir = Directory::create(&mut transaction, &store, None).await.expect("create failed");
3415
3416        let symlink_id = dir
3417            .create_symlink(&mut transaction, b"link", "foo")
3418            .await
3419            .expect("create_symlink failed");
3420        transaction.commit().await.expect("commit failed");
3421
3422        let link = store.read_symlink(symlink_id).await.expect("read_symlink failed");
3423        assert_eq!(&link, b"link");
3424        fs.close().await.expect("Close failed");
3425    }
3426
3427    #[fuchsia::test]
3428    async fn test_unlink_symlink() {
3429        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3430        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3431        let dir;
3432        let mut transaction = fs
3433            .root_store()
3434            .new_transaction(lock_keys![], Options::default())
3435            .await
3436            .expect("new_transaction failed");
3437        let store = fs.root_store();
3438        dir = Directory::create(&mut transaction, &store, None).await.expect("create failed");
3439
3440        let symlink_id = dir
3441            .create_symlink(&mut transaction, b"link", "foo")
3442            .await
3443            .expect("create_symlink failed");
3444        transaction.commit().await.expect("commit failed");
3445        transaction = fs
3446            .root_store()
3447            .new_transaction(
3448                lock_keys![
3449                    LockKey::object(store.store_object_id(), dir.object_id()),
3450                    LockKey::object(store.store_object_id(), symlink_id),
3451                ],
3452                Options::default(),
3453            )
3454            .await
3455            .expect("new_transaction failed");
3456        assert_matches!(
3457            replace_child(&mut transaction, None, (&dir, "foo"))
3458                .await
3459                .expect("replace_child failed"),
3460            ReplacedChild::Object(_)
3461        );
3462        transaction.commit().await.expect("commit failed");
3463
3464        assert_eq!(dir.lookup("foo").await.expect("lookup failed"), None);
3465        fs.close().await.expect("Close failed");
3466    }
3467
3468    #[fuchsia::test]
3469    async fn test_get_properties() {
3470        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3471        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3472        let dir;
3473        let mut transaction = fs
3474            .root_store()
3475            .new_transaction(lock_keys![], Options::default())
3476            .await
3477            .expect("new_transaction failed");
3478
3479        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3480            .await
3481            .expect("create failed");
3482        transaction.commit().await.expect("commit failed");
3483
3484        // Check attributes of `dir`
3485        let mut properties = dir.get_properties().await.expect("get_properties failed");
3486        let dir_creation_time = properties.creation_time;
3487        assert_eq!(dir_creation_time, properties.modification_time);
3488        assert_eq!(properties.sub_dirs, 0);
3489        assert!(properties.posix_attributes.is_none());
3490
3491        // Create child directory
3492        transaction = fs
3493            .root_store()
3494            .new_transaction(
3495                lock_keys![LockKey::object(fs.root_store().store_object_id(), dir.object_id())],
3496                Options::default(),
3497            )
3498            .await
3499            .expect("new_transaction failed");
3500        let child_dir =
3501            dir.create_child_dir(&mut transaction, "foo").await.expect("create_child_dir failed");
3502        transaction.commit().await.expect("commit failed");
3503
3504        // Check attributes of `dir` after adding child directory
3505        properties = dir.get_properties().await.expect("get_properties failed");
3506        // The modification time property should have updated
3507        assert_eq!(dir_creation_time, properties.creation_time);
3508        assert!(dir_creation_time < properties.modification_time);
3509        assert_eq!(properties.sub_dirs, 1);
3510        assert!(properties.posix_attributes.is_none());
3511
3512        // Check attributes of `child_dir`
3513        properties = child_dir.get_properties().await.expect("get_properties failed");
3514        assert_eq!(properties.creation_time, properties.modification_time);
3515        assert_eq!(properties.sub_dirs, 0);
3516        assert!(properties.posix_attributes.is_none());
3517
3518        // Create child file with MutableAttributes
3519        transaction = fs
3520            .root_store()
3521            .new_transaction(
3522                lock_keys![LockKey::object(
3523                    fs.root_store().store_object_id(),
3524                    child_dir.object_id()
3525                )],
3526                Options::default(),
3527            )
3528            .await
3529            .expect("new_transaction failed");
3530        let child_dir_file = child_dir
3531            .create_child_file(&mut transaction, "bar")
3532            .await
3533            .expect("create_child_file failed");
3534        child_dir_file
3535            .update_attributes(
3536                &mut transaction,
3537                Some(&fio::MutableNodeAttributes { gid: Some(1), ..Default::default() }),
3538                None,
3539            )
3540            .await
3541            .expect("Updating attributes");
3542        transaction.commit().await.expect("commit failed");
3543
3544        // The modification time property of `child_dir` should have updated
3545        properties = child_dir.get_properties().await.expect("get_properties failed");
3546        assert!(properties.creation_time < properties.modification_time);
3547        assert!(properties.posix_attributes.is_none());
3548
3549        // Check attributes of `child_dir_file`
3550        properties = child_dir_file.get_properties().await.expect("get_properties failed");
3551        assert_eq!(properties.creation_time, properties.modification_time);
3552        assert_eq!(properties.sub_dirs, 0);
3553        assert!(properties.posix_attributes.is_some());
3554        assert_eq!(properties.posix_attributes.unwrap().gid, 1);
3555        // The other POSIX attributes should be set to default values
3556        assert_eq!(properties.posix_attributes.unwrap().uid, 0);
3557        assert_eq!(properties.posix_attributes.unwrap().mode, 0);
3558        assert_eq!(properties.posix_attributes.unwrap().rdev, 0);
3559    }
3560
3561    #[fuchsia::test]
3562    async fn test_update_create_attributes() {
3563        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
3564        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3565        let dir;
3566        let mut transaction = fs
3567            .root_store()
3568            .new_transaction(lock_keys![], Options::default())
3569            .await
3570            .expect("new_transaction failed");
3571
3572        dir = Directory::create(&mut transaction, &fs.root_store(), None)
3573            .await
3574            .expect("create failed");
3575        transaction.commit().await.expect("commit failed");
3576        let mut properties = dir.get_properties().await.expect("get_properties failed");
3577        assert_eq!(properties.sub_dirs, 0);
3578        assert!(properties.posix_attributes.is_none());
3579        let creation_time = properties.creation_time;
3580        let modification_time = properties.modification_time;
3581        assert_eq!(creation_time, modification_time);
3582
3583        // First update: test that
3584        // 1. updating attributes with a POSIX attribute will assign some PosixAttributes to the
3585        //    Object associated with `dir`,
3586        // 2. creation/modification time are only updated if specified in the update,
3587        // 3. any changes will not overwrite other attributes.
3588        transaction = fs
3589            .root_store()
3590            .new_transaction(
3591                lock_keys![LockKey::object(fs.root_store().store_object_id(), dir.object_id())],
3592                Options::default(),
3593            )
3594            .await
3595            .expect("new_transaction failed");
3596        let now = Timestamp::now();
3597        dir.update_attributes(
3598            transaction,
3599            Some(&fio::MutableNodeAttributes {
3600                modification_time: Some(now.as_nanos()),
3601                uid: Some(1),
3602                gid: Some(2),
3603                ..Default::default()
3604            }),
3605            0,
3606            None,
3607        )
3608        .await
3609        .expect("update_attributes failed");
3610        properties = dir.get_properties().await.expect("get_properties failed");
3611        // Check that the properties reflect the updates
3612        assert_eq!(properties.modification_time, now);
3613        assert!(properties.posix_attributes.is_some());
3614        assert_eq!(properties.posix_attributes.unwrap().uid, 1);
3615        assert_eq!(properties.posix_attributes.unwrap().gid, 2);
3616        // The other POSIX attributes should be set to default values
3617        assert_eq!(properties.posix_attributes.unwrap().mode, 0);
3618        assert_eq!(properties.posix_attributes.unwrap().rdev, 0);
3619        // The remaining properties should not have changed
3620        assert_eq!(properties.sub_dirs, 0);
3621        assert_eq!(properties.creation_time, creation_time);
3622
3623        // Second update: test that we can update attributes and that any changes will not overwrite
3624        // other attributes
3625        let transaction = fs
3626            .root_store()
3627            .new_transaction(
3628                lock_keys![LockKey::object(fs.root_store().store_object_id(), dir.object_id())],
3629                Options::default(),
3630            )
3631            .await
3632            .expect("new_transaction failed");
3633        dir.update_attributes(
3634            transaction,
3635            Some(&fio::MutableNodeAttributes {
3636                creation_time: Some(now.as_nanos()),
3637                uid: Some(3),
3638                rdev: Some(10),
3639                ..Default::default()
3640            }),
3641            0,
3642            None,
3643        )
3644        .await
3645        .expect("update_attributes failed");
3646        properties = dir.get_properties().await.expect("get_properties failed");
3647        assert_eq!(properties.creation_time, now);
3648        assert!(properties.posix_attributes.is_some());
3649        assert_eq!(properties.posix_attributes.unwrap().uid, 3);
3650        assert_eq!(properties.posix_attributes.unwrap().rdev, 10);
3651        // The other properties should not have changed
3652        assert_eq!(properties.sub_dirs, 0);
3653        assert_eq!(properties.modification_time, now);
3654        assert_eq!(properties.posix_attributes.unwrap().gid, 2);
3655        assert_eq!(properties.posix_attributes.unwrap().mode, 0);
3656    }
3657
3658    #[fuchsia::test]
3659    async fn write_to_directory_attribute_creates_keys() {
3660        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
3661        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3662        let crypt = Arc::new(new_insecure_crypt());
3663
3664        {
3665            let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
3666            let store = root_volume
3667                .new_volume(
3668                    "vol",
3669                    NewChildStoreOptions {
3670                        options: StoreOptions {
3671                            crypt: Some(crypt.clone()),
3672                            ..StoreOptions::default()
3673                        },
3674                        ..Default::default()
3675                    },
3676                )
3677                .await
3678                .expect("new_volume failed");
3679            let mut transaction = filesystem
3680                .root_store()
3681                .new_transaction(
3682                    lock_keys![LockKey::object(
3683                        store.store_object_id(),
3684                        store.root_directory_object_id()
3685                    )],
3686                    Options::default(),
3687                )
3688                .await
3689                .expect("new transaction failed");
3690            let root_directory = Directory::open(&store, store.root_directory_object_id())
3691                .await
3692                .expect("open failed");
3693            let directory = root_directory
3694                .create_child_dir(&mut transaction, "foo")
3695                .await
3696                .expect("create_child_dir failed");
3697            transaction.commit().await.expect("commit failed");
3698
3699            let mut transaction = filesystem
3700                .root_store()
3701                .new_transaction(
3702                    lock_keys![LockKey::object(store.store_object_id(), directory.object_id())],
3703                    Options::default(),
3704                )
3705                .await
3706                .expect("new transaction failed");
3707            let _ = directory
3708                .handle
3709                .write_attr(&mut transaction, AttributeId::TEST_ID, b"bar")
3710                .await
3711                .expect("write_attr failed");
3712            transaction.commit().await.expect("commit failed");
3713        }
3714
3715        filesystem.close().await.expect("Close failed");
3716        let device = filesystem.take_device().await;
3717        device.reopen(false);
3718        let filesystem = FxFilesystem::open(device).await.expect("open failed");
3719
3720        {
3721            let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
3722            let volume = root_volume
3723                .volume("vol", StoreOptions { crypt: Some(crypt), ..StoreOptions::default() })
3724                .await
3725                .expect("volume failed");
3726            let root_directory = Directory::open(&volume, volume.root_directory_object_id())
3727                .await
3728                .expect("open failed");
3729            let directory = Directory::open(
3730                &volume,
3731                root_directory.lookup("foo").await.expect("lookup failed").expect("not found").0,
3732            )
3733            .await
3734            .expect("open failed");
3735            let mut buf = directory.handle.store().device.allocate_buffer(10).await;
3736            assert_eq!(
3737                directory
3738                    .handle
3739                    .read(AttributeId::TEST_ID, 0, buf.as_mut())
3740                    .await
3741                    .expect("read failed"),
3742                3
3743            );
3744            assert_eq!(buf.subslice(0..3).to_vec(), b"bar");
3745        }
3746
3747        filesystem.close().await.expect("Close failed");
3748    }
3749
3750    #[fuchsia::test]
3751    async fn directory_with_extended_attributes() {
3752        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
3753        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3754        let crypt = Arc::new(new_insecure_crypt());
3755
3756        let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
3757        let store = root_volume
3758            .new_volume(
3759                "vol",
3760                NewChildStoreOptions {
3761                    options: StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
3762                    ..Default::default()
3763                },
3764            )
3765            .await
3766            .expect("new_volume failed");
3767        let directory =
3768            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
3769
3770        let test_small_name = b"security.selinux".to_vec();
3771        let test_small_value = b"foo".to_vec();
3772        let test_large_name = b"large.attribute".to_vec();
3773        let test_large_value = vec![1u8; 500];
3774
3775        directory
3776            .set_extended_attribute(
3777                test_small_name.clone(),
3778                test_small_value.clone(),
3779                SetExtendedAttributeMode::Set,
3780            )
3781            .await
3782            .unwrap();
3783        assert_eq!(
3784            directory.get_extended_attribute(test_small_name.clone()).await.unwrap(),
3785            test_small_value
3786        );
3787
3788        directory
3789            .set_extended_attribute(
3790                test_large_name.clone(),
3791                test_large_value.clone(),
3792                SetExtendedAttributeMode::Set,
3793            )
3794            .await
3795            .unwrap();
3796        assert_eq!(
3797            directory.get_extended_attribute(test_large_name.clone()).await.unwrap(),
3798            test_large_value
3799        );
3800
3801        fsck(filesystem.clone()).await.unwrap();
3802        fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3803            .await
3804            .unwrap();
3805
3806        directory.remove_extended_attribute(test_small_name.clone()).await.unwrap();
3807        directory.remove_extended_attribute(test_large_name.clone()).await.unwrap();
3808
3809        filesystem.close().await.expect("close failed");
3810    }
3811
3812    #[fuchsia::test]
3813    async fn remove_directory_with_extended_attributes() {
3814        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
3815        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3816        let crypt = Arc::new(new_insecure_crypt());
3817
3818        let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
3819        let store = root_volume
3820            .new_volume(
3821                "vol",
3822                NewChildStoreOptions {
3823                    options: StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
3824                    ..Default::default()
3825                },
3826            )
3827            .await
3828            .expect("new_volume failed");
3829        let mut transaction = filesystem
3830            .root_store()
3831            .new_transaction(
3832                lock_keys![LockKey::object(
3833                    store.store_object_id(),
3834                    store.root_directory_object_id()
3835                )],
3836                Options::default(),
3837            )
3838            .await
3839            .expect("new transaction failed");
3840        let root_directory =
3841            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
3842        let directory = root_directory
3843            .create_child_dir(&mut transaction, "foo")
3844            .await
3845            .expect("create_child_dir failed");
3846        transaction.commit().await.expect("commit failed");
3847
3848        fsck(filesystem.clone()).await.unwrap();
3849        fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3850            .await
3851            .unwrap();
3852
3853        let test_small_name = b"security.selinux".to_vec();
3854        let test_small_value = b"foo".to_vec();
3855        let test_large_name = b"large.attribute".to_vec();
3856        let test_large_value = vec![1u8; 500];
3857
3858        directory
3859            .set_extended_attribute(
3860                test_small_name.clone(),
3861                test_small_value.clone(),
3862                SetExtendedAttributeMode::Set,
3863            )
3864            .await
3865            .unwrap();
3866        directory
3867            .set_extended_attribute(
3868                test_large_name.clone(),
3869                test_large_value.clone(),
3870                SetExtendedAttributeMode::Set,
3871            )
3872            .await
3873            .unwrap();
3874
3875        fsck(filesystem.clone()).await.unwrap();
3876        fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3877            .await
3878            .unwrap();
3879
3880        let mut transaction = filesystem
3881            .root_store()
3882            .new_transaction(
3883                lock_keys![
3884                    LockKey::object(store.store_object_id(), root_directory.object_id()),
3885                    LockKey::object(store.store_object_id(), directory.object_id()),
3886                ],
3887                Options::default(),
3888            )
3889            .await
3890            .expect("new_transaction failed");
3891        replace_child(&mut transaction, None, (&root_directory, "foo"))
3892            .await
3893            .expect("replace_child failed");
3894        transaction.commit().await.unwrap();
3895
3896        fsck(filesystem.clone()).await.unwrap();
3897        fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3898            .await
3899            .unwrap();
3900
3901        filesystem.close().await.expect("close failed");
3902    }
3903
3904    #[fuchsia::test]
3905    async fn remove_symlink_with_extended_attributes() {
3906        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
3907        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
3908        let crypt = Arc::new(new_insecure_crypt());
3909
3910        let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
3911        let store = root_volume
3912            .new_volume(
3913                "vol",
3914                NewChildStoreOptions {
3915                    options: StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
3916                    ..Default::default()
3917                },
3918            )
3919            .await
3920            .expect("new_volume failed");
3921        let mut transaction = filesystem
3922            .root_store()
3923            .new_transaction(
3924                lock_keys![LockKey::object(
3925                    store.store_object_id(),
3926                    store.root_directory_object_id()
3927                )],
3928                Options::default(),
3929            )
3930            .await
3931            .expect("new transaction failed");
3932        let root_directory =
3933            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
3934        let symlink_id = root_directory
3935            .create_symlink(&mut transaction, b"somewhere/else", "foo")
3936            .await
3937            .expect("create_symlink failed");
3938        transaction.commit().await.expect("commit failed");
3939
3940        let symlink = StoreObjectHandle::new(
3941            store.clone(),
3942            symlink_id,
3943            false,
3944            HandleOptions::default(),
3945            false,
3946        );
3947
3948        fsck(filesystem.clone()).await.unwrap();
3949        fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3950            .await
3951            .unwrap();
3952
3953        let test_small_name = b"security.selinux".to_vec();
3954        let test_small_value = b"foo".to_vec();
3955        let test_large_name = b"large.attribute".to_vec();
3956        let test_large_value = vec![1u8; 500];
3957
3958        symlink
3959            .set_extended_attribute(
3960                test_small_name.clone(),
3961                test_small_value.clone(),
3962                SetExtendedAttributeMode::Set,
3963            )
3964            .await
3965            .unwrap();
3966        symlink
3967            .set_extended_attribute(
3968                test_large_name.clone(),
3969                test_large_value.clone(),
3970                SetExtendedAttributeMode::Set,
3971            )
3972            .await
3973            .unwrap();
3974
3975        fsck(filesystem.clone()).await.unwrap();
3976        fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3977            .await
3978            .unwrap();
3979
3980        let mut transaction = filesystem
3981            .root_store()
3982            .new_transaction(
3983                lock_keys![
3984                    LockKey::object(store.store_object_id(), root_directory.object_id()),
3985                    LockKey::object(store.store_object_id(), symlink.object_id()),
3986                ],
3987                Options::default(),
3988            )
3989            .await
3990            .expect("new_transaction failed");
3991        replace_child(&mut transaction, None, (&root_directory, "foo"))
3992            .await
3993            .expect("replace_child failed");
3994        transaction.commit().await.unwrap();
3995
3996        fsck(filesystem.clone()).await.unwrap();
3997        fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
3998            .await
3999            .unwrap();
4000
4001        filesystem.close().await.expect("close failed");
4002    }
4003
4004    #[fuchsia::test]
4005    async fn test_update_timestamps() {
4006        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4007        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4008        let dir;
4009        let mut transaction = fs
4010            .root_store()
4011            .new_transaction(lock_keys![], Options::default())
4012            .await
4013            .expect("new_transaction failed");
4014
4015        // Expect that atime, ctime, mtime (and creation time) to be the same when we create a
4016        // directory
4017        dir = Directory::create(&mut transaction, &fs.root_store(), None)
4018            .await
4019            .expect("create failed");
4020        transaction.commit().await.expect("commit failed");
4021        let mut properties = dir.get_properties().await.expect("get_properties failed");
4022        let starting_time = properties.creation_time;
4023        assert_eq!(properties.creation_time, starting_time);
4024        assert_eq!(properties.modification_time, starting_time);
4025        assert_eq!(properties.change_time, starting_time);
4026        assert_eq!(properties.access_time, starting_time);
4027
4028        // Test that we can update the timestamps
4029        transaction = fs
4030            .root_store()
4031            .new_transaction(
4032                lock_keys![LockKey::object(fs.root_store().store_object_id(), dir.object_id())],
4033                Options::default(),
4034            )
4035            .await
4036            .expect("new_transaction failed");
4037        let update1_time = Timestamp::now();
4038        dir.update_attributes(
4039            transaction,
4040            Some(&fio::MutableNodeAttributes {
4041                modification_time: Some(update1_time.as_nanos()),
4042                ..Default::default()
4043            }),
4044            0,
4045            Some(update1_time),
4046        )
4047        .await
4048        .expect("update_attributes failed");
4049        properties = dir.get_properties().await.expect("get_properties failed");
4050        assert_eq!(properties.modification_time, update1_time);
4051        assert_eq!(properties.access_time, starting_time);
4052        assert_eq!(properties.creation_time, starting_time);
4053        assert_eq!(properties.change_time, update1_time);
4054    }
4055
4056    #[fuchsia::test]
4057    async fn test_move_dir_timestamps() {
4058        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4059        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4060        let dir;
4061        let child1;
4062        let child2;
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        child1 = dir
4072            .create_child_dir(&mut transaction, "child1")
4073            .await
4074            .expect("create_child_dir failed");
4075        child2 = dir
4076            .create_child_dir(&mut transaction, "child2")
4077            .await
4078            .expect("create_child_dir failed");
4079        transaction.commit().await.expect("commit failed");
4080        let dir_properties = dir.get_properties().await.expect("get_properties failed");
4081        let child2_properties = child2.get_properties().await.expect("get_properties failed");
4082
4083        // Move dir/child2 to dir/child1/child2
4084        transaction = fs
4085            .root_store()
4086            .new_transaction(
4087                lock_keys![
4088                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
4089                    LockKey::object(fs.root_store().store_object_id(), child1.object_id()),
4090                    LockKey::object(fs.root_store().store_object_id(), child2.object_id()),
4091                ],
4092                Options::default(),
4093            )
4094            .await
4095            .expect("new_transaction failed");
4096        assert_matches!(
4097            replace_child(&mut transaction, Some((&dir, "child2")), (&child1, "child2"))
4098                .await
4099                .expect("replace_child failed"),
4100            ReplacedChild::None
4101        );
4102        transaction.commit().await.expect("commit failed");
4103        // Both mtime and ctime for dir should be updated
4104        let new_dir_properties = dir.get_properties().await.expect("get_properties failed");
4105        let time_of_replacement = new_dir_properties.change_time;
4106        assert!(new_dir_properties.change_time > dir_properties.change_time);
4107        assert_eq!(new_dir_properties.modification_time, time_of_replacement);
4108        // Both mtime and ctime for child1 should be updated
4109        let new_child1_properties = child1.get_properties().await.expect("get_properties failed");
4110        assert_eq!(new_child1_properties.modification_time, time_of_replacement);
4111        assert_eq!(new_child1_properties.change_time, time_of_replacement);
4112        // Only ctime for child2 should be updated
4113        let moved_child2_properties = child2.get_properties().await.expect("get_properties failed");
4114        assert_eq!(moved_child2_properties.change_time, time_of_replacement);
4115        assert_eq!(moved_child2_properties.creation_time, child2_properties.creation_time);
4116        assert_eq!(moved_child2_properties.access_time, child2_properties.access_time);
4117        assert_eq!(moved_child2_properties.modification_time, child2_properties.modification_time);
4118        fs.close().await.expect("Close failed");
4119    }
4120
4121    #[fuchsia::test]
4122    async fn test_unlink_timestamps() {
4123        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4124        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4125        let dir;
4126        let foo;
4127        let mut transaction = fs
4128            .root_store()
4129            .new_transaction(lock_keys![], Options::default())
4130            .await
4131            .expect("new_transaction failed");
4132        dir = Directory::create(&mut transaction, &fs.root_store(), None)
4133            .await
4134            .expect("create failed");
4135        foo =
4136            dir.create_child_file(&mut transaction, "foo").await.expect("create_child_dir failed");
4137
4138        transaction.commit().await.expect("commit failed");
4139        let dir_properties = dir.get_properties().await.expect("get_properties failed");
4140        let foo_properties = foo.get_properties().await.expect("get_properties failed");
4141
4142        transaction = fs
4143            .root_store()
4144            .new_transaction(
4145                lock_keys![
4146                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
4147                    LockKey::object(fs.root_store().store_object_id(), foo.object_id()),
4148                ],
4149                Options::default(),
4150            )
4151            .await
4152            .expect("new_transaction failed");
4153        assert_matches!(
4154            replace_child(&mut transaction, None, (&dir, "foo"))
4155                .await
4156                .expect("replace_child failed"),
4157            ReplacedChild::Object(_)
4158        );
4159        transaction.commit().await.expect("commit failed");
4160        // Both mtime and ctime for dir should be updated
4161        let new_dir_properties = dir.get_properties().await.expect("get_properties failed");
4162        let time_of_replacement = new_dir_properties.change_time;
4163        assert!(new_dir_properties.change_time > dir_properties.change_time);
4164        assert_eq!(new_dir_properties.modification_time, time_of_replacement);
4165        // Only ctime for foo should be updated
4166        let moved_foo_properties = foo.get_properties().await.expect("get_properties failed");
4167        assert_eq!(moved_foo_properties.change_time, time_of_replacement);
4168        assert_eq!(moved_foo_properties.creation_time, foo_properties.creation_time);
4169        assert_eq!(moved_foo_properties.access_time, foo_properties.access_time);
4170        assert_eq!(moved_foo_properties.modification_time, foo_properties.modification_time);
4171        fs.close().await.expect("Close failed");
4172    }
4173
4174    #[fuchsia::test]
4175    async fn test_replace_dir_timestamps() {
4176        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4177        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4178        let dir;
4179        let child_dir1;
4180        let child_dir2;
4181        let foo;
4182        let mut transaction = fs
4183            .root_store()
4184            .new_transaction(lock_keys![], Options::default())
4185            .await
4186            .expect("new_transaction failed");
4187        dir = Directory::create(&mut transaction, &fs.root_store(), None)
4188            .await
4189            .expect("create failed");
4190        child_dir1 =
4191            dir.create_child_dir(&mut transaction, "dir1").await.expect("create_child_dir failed");
4192        child_dir2 =
4193            dir.create_child_dir(&mut transaction, "dir2").await.expect("create_child_dir failed");
4194        foo = child_dir1
4195            .create_child_dir(&mut transaction, "foo")
4196            .await
4197            .expect("create_child_dir failed");
4198        transaction.commit().await.expect("commit failed");
4199        let dir_props = dir.get_properties().await.expect("get_properties failed");
4200        let foo_props = foo.get_properties().await.expect("get_properties failed");
4201
4202        transaction = fs
4203            .root_store()
4204            .new_transaction(
4205                lock_keys![
4206                    LockKey::object(fs.root_store().store_object_id(), dir.object_id()),
4207                    LockKey::object(fs.root_store().store_object_id(), child_dir1.object_id()),
4208                    LockKey::object(fs.root_store().store_object_id(), child_dir2.object_id()),
4209                    LockKey::object(fs.root_store().store_object_id(), foo.object_id()),
4210                ],
4211                Options::default(),
4212            )
4213            .await
4214            .expect("new_transaction failed");
4215        assert_matches!(
4216            replace_child(&mut transaction, Some((&child_dir1, "foo")), (&dir, "dir2"))
4217                .await
4218                .expect("replace_child failed"),
4219            ReplacedChild::Directory(_)
4220        );
4221        transaction.commit().await.expect("commit failed");
4222        // Both mtime and ctime for dir should be updated
4223        let new_dir_props = dir.get_properties().await.expect("get_properties failed");
4224        let time_of_replacement = new_dir_props.change_time;
4225        assert!(new_dir_props.change_time > dir_props.change_time);
4226        assert_eq!(new_dir_props.modification_time, time_of_replacement);
4227        // Both mtime and ctime for dir1 should be updated
4228        let new_dir1_props = child_dir1.get_properties().await.expect("get_properties failed");
4229        let time_of_replacement = new_dir1_props.change_time;
4230        assert_eq!(new_dir1_props.change_time, time_of_replacement);
4231        assert_eq!(new_dir1_props.modification_time, time_of_replacement);
4232        // Only ctime for foo should be updated
4233        let moved_foo_props = foo.get_properties().await.expect("get_properties failed");
4234        assert_eq!(moved_foo_props.change_time, time_of_replacement);
4235        assert_eq!(moved_foo_props.creation_time, foo_props.creation_time);
4236        assert_eq!(moved_foo_props.access_time, foo_props.access_time);
4237        assert_eq!(moved_foo_props.modification_time, foo_props.modification_time);
4238        fs.close().await.expect("Close failed");
4239    }
4240
4241    #[fuchsia::test]
4242    async fn test_create_casefold_directory() {
4243        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4244        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4245        let object_id = {
4246            let mut transaction = fs
4247                .root_store()
4248                .new_transaction(lock_keys![], Options::default())
4249                .await
4250                .expect("new_transaction failed");
4251            let dir = Directory::create(&mut transaction, &fs.root_store(), None)
4252                .await
4253                .expect("create failed");
4254
4255            let child_dir = dir
4256                .create_child_dir(&mut transaction, "foo")
4257                .await
4258                .expect("create_child_dir failed");
4259            let _child_dir_file = child_dir
4260                .create_child_file(&mut transaction, "bAr")
4261                .await
4262                .expect("create_child_file failed");
4263            transaction.commit().await.expect("commit failed");
4264            dir.object_id()
4265        };
4266        fs.close().await.expect("Close failed");
4267        let device = fs.take_device().await;
4268
4269        // We now have foo/bAr which should be case sensitive (casefold not enabled).
4270
4271        device.reopen(false);
4272        let fs = FxFilesystem::open(device).await.expect("open failed");
4273        {
4274            let dir = Directory::open(&fs.root_store(), object_id).await.expect("open failed");
4275            let (object_id, object_descriptor, _) =
4276                dir.lookup("foo").await.expect("lookup failed").expect("not found");
4277            assert_eq!(object_descriptor, ObjectDescriptor::Directory);
4278            let child_dir =
4279                Directory::open(&fs.root_store(), object_id).await.expect("open failed");
4280            assert!(!child_dir.dir_type().is_casefold());
4281            assert!(child_dir.lookup("BAR").await.expect("lookup failed").is_none());
4282            let (object_id, descriptor, _) =
4283                child_dir.lookup("bAr").await.expect("lookup failed").unwrap();
4284            assert_eq!(descriptor, ObjectDescriptor::File);
4285
4286            // We can't set casefold now because the directory isn't empty.
4287            child_dir.set_casefold(true).await.expect_err("not empty");
4288
4289            // Delete the file and subdir and try again.
4290            let mut transaction = fs
4291                .root_store()
4292                .new_transaction(
4293                    lock_keys![
4294                        LockKey::object(fs.root_store().store_object_id(), child_dir.object_id()),
4295                        LockKey::object(fs.root_store().store_object_id(), object_id),
4296                    ],
4297                    Options::default(),
4298                )
4299                .await
4300                .expect("new_transaction failed");
4301            assert_matches!(
4302                replace_child(&mut transaction, None, (&child_dir, "bAr"))
4303                    .await
4304                    .expect("replace_child failed"),
4305                ReplacedChild::Object(..)
4306            );
4307            transaction.commit().await.expect("commit failed");
4308
4309            // This time enabling casefold should succeed.
4310            child_dir.set_casefold(true).await.expect("set casefold");
4311
4312            assert!(child_dir.dir_type().is_casefold());
4313
4314            // Create the file again now that casefold is enabled.
4315            let mut transaction = fs
4316                .root_store()
4317                .new_transaction(
4318                    lock_keys![LockKey::object(
4319                        fs.root_store().store_object_id(),
4320                        child_dir.object_id()
4321                    ),],
4322                    Options::default(),
4323                )
4324                .await
4325                .expect("new_transaction failed");
4326            let _child_dir_file = child_dir
4327                .create_child_file(&mut transaction, "bAr")
4328                .await
4329                .expect("create_child_file failed");
4330            transaction.commit().await.expect("commit failed");
4331
4332            // Check that we can lookup via a case insensitive name.
4333            assert!(child_dir.lookup("BAR").await.expect("lookup failed").is_some());
4334            assert!(child_dir.lookup("bAr").await.expect("lookup failed").is_some());
4335
4336            // Enabling casefold should fail again as the dir is not empty.
4337            child_dir.set_casefold(true).await.expect_err("set casefold");
4338            assert!(child_dir.dir_type().is_casefold());
4339
4340            // Confirm that casefold will affect created subdirectories.
4341            let mut transaction = fs
4342                .root_store()
4343                .new_transaction(
4344                    lock_keys![LockKey::object(
4345                        fs.root_store().store_object_id(),
4346                        child_dir.object_id()
4347                    ),],
4348                    Options::default(),
4349                )
4350                .await
4351                .expect("new_transaction failed");
4352            let sub_dir = child_dir
4353                .create_child_dir(&mut transaction, "sub")
4354                .await
4355                .expect("create_sub_dir failed");
4356            transaction.commit().await.expect("commit failed");
4357            assert!(sub_dir.dir_type().is_casefold());
4358        };
4359        fs.close().await.expect("Close failed");
4360    }
4361
4362    #[fuchsia::test]
4363    async fn test_create_casefold_encrypted_directory() {
4364        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4365        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4366        let proxy_filename: ProxyFilename;
4367        let object_id;
4368        {
4369            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4370            let root_volume = root_volume(fs.clone()).await.unwrap();
4371            let store = root_volume
4372                .new_volume(
4373                    "vol",
4374                    NewChildStoreOptions {
4375                        options: StoreOptions {
4376                            crypt: Some(crypt.clone()),
4377                            ..StoreOptions::default()
4378                        },
4379                        ..Default::default()
4380                    },
4381                )
4382                .await
4383                .unwrap();
4384
4385            // Create a (very weak) key for our encrypted directory.
4386            crypt
4387                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
4388                .expect("add wrapping key failed");
4389
4390            object_id = {
4391                let mut transaction = fs
4392                    .root_store()
4393                    .new_transaction(lock_keys![], Options::default())
4394                    .await
4395                    .expect("new_transaction failed");
4396                let dir = Directory::create(&mut transaction, &store, Some(WRAPPING_KEY_ID))
4397                    .await
4398                    .expect("create failed");
4399
4400                transaction.commit().await.expect("commit");
4401                dir.object_id()
4402            };
4403            let dir = Directory::open(&store, object_id).await.expect("open failed");
4404
4405            dir.set_casefold(true).await.expect("set casefold");
4406            assert!(dir.dir_type().is_casefold());
4407
4408            let mut transaction = fs
4409                .root_store()
4410                .new_transaction(
4411                    lock_keys![LockKey::object(store.store_object_id(), dir.object_id()),],
4412                    Options::default(),
4413                )
4414                .await
4415                .expect("new_transaction failed");
4416            let _file = dir
4417                .create_child_file(&mut transaction, "bAr")
4418                .await
4419                .expect("create_child_file failed");
4420            transaction.commit().await.expect("commit failed");
4421
4422            // Check that we can look up the original name.
4423            assert!(dir.lookup("bAr").await.expect("original lookup failed").is_some());
4424
4425            // Derive the proxy filename now, for use later when operating on the locked volume
4426            // as we won't have the key then.
4427            let key = dir.get_fscrypt_key().await.expect("key").into_cipher().unwrap();
4428            let encrypted_name =
4429                encrypt_filename(&*key, dir.object_id(), "bAr").expect("encrypt_filename");
4430            let hash_code = key.hash_code_casefold("bAr");
4431            proxy_filename = ProxyFilename::new_with_hash_code(hash_code as u64, &encrypted_name);
4432
4433            // Check that we can lookup via a case insensitive name.
4434            assert!(dir.lookup("BAR").await.expect("casefold lookup failed").is_some());
4435
4436            // Check hash values generated are stable across case.
4437            assert_eq!(key.hash_code_casefold("bar"), key.hash_code_casefold("BaR"));
4438
4439            // We can't easily check iteration from here as we only get encrypted entries so
4440            // we just count instead.
4441            let mut count = 0;
4442            let layer_set = dir.store().tree().layer_set();
4443            let mut merger = layer_set.merger();
4444            let mut iter = dir.iter(&mut merger).await.expect("iter");
4445            while let Some(_entry) = iter.get() {
4446                count += 1;
4447                iter.advance().await.expect("advance");
4448            }
4449            assert_eq!(1, count, "unexpected number of entries.");
4450
4451            fs.close().await.expect("Close failed");
4452        }
4453
4454        let device = fs.take_device().await;
4455
4456        // Now try and read the encrypted directory without keys.
4457
4458        device.reopen(false);
4459        let fs = FxFilesystem::open(device).await.expect("open failed");
4460        {
4461            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4462            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4463            let store = root_volume
4464                .volume(
4465                    "vol",
4466                    StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
4467                )
4468                .await
4469                .expect("volume failed");
4470            let dir = Directory::open(&store, object_id).await.expect("open failed");
4471            assert!(dir.dir_type().is_casefold());
4472
4473            // Check that we can NOT look up the original name.
4474            assert!(dir.lookup("bAr").await.expect("lookup failed").is_none());
4475            // We should instead see the proxy filename.
4476            let filename: String = proxy_filename.into();
4477            assert!(dir.lookup(&filename).await.expect("lookup failed").is_some());
4478
4479            let layer_set = dir.store().tree().layer_set();
4480            let mut merger = layer_set.merger();
4481            let mut iter = dir.iter(&mut merger).await.expect("iter");
4482            let item = iter.get().expect("expect item");
4483            let filename: String = proxy_filename.into();
4484            assert_eq!(item.0, &filename);
4485            iter.advance().await.expect("advance");
4486            assert_eq!(None, iter.get());
4487
4488            fsck(fs.clone()).await.unwrap();
4489            fsck_volume(fs.as_ref(), store.store_object_id(), Some(crypt.clone())).await.unwrap();
4490
4491            fs.close().await.expect("Close failed");
4492        }
4493    }
4494
4495    /// Search for a pair of filenames that encode to the same casefold hash and same
4496    /// filename prefix, but different sha256.
4497    /// We are specifically looking for a case where encrypted child of a > encrypted child of b
4498    /// but proxy_filename of a < proxy filename of b or vice versa.
4499    /// This is to fully test the iterator logic for locked directories.
4500    ///
4501    /// Note this is a SLOW process (~12 seconds on my workstation with release build).
4502    /// For that reason, the solution is hard coded and this function is marked as ignored.
4503    ///
4504    /// Returns a pair of filenames on success, None on failure.
4505    #[allow(dead_code)]
4506    fn find_out_of_order_sha256_long_prefix_pair(
4507        object_id: u64,
4508        key: &Arc<dyn Cipher>,
4509    ) -> Option<[String; 2]> {
4510        let mut collision_map: std::collections::HashMap<u32, (usize, ProxyFilename, Vec<u8>)> =
4511            std::collections::HashMap::new();
4512        for i in 0..(1usize << 32) {
4513            let filename = format!("{:0>176}_{i}", 0);
4514            let encrypted_name =
4515                encrypt_filename(&**key, object_id, &filename).expect("encrypt_filename");
4516            let hash_code = key.hash_code_casefold(&filename);
4517            let a = ProxyFilename::new_with_hash_code(hash_code as u64, &encrypted_name);
4518            let hash_code = a.hash_code as u32;
4519            if let Some((j, b, b_encrypted_name)) = collision_map.get(&hash_code) {
4520                assert_eq!(a.filename, b.filename);
4521                if encrypted_name.cmp(b_encrypted_name) != a.sha256.cmp(&b.sha256) {
4522                    return Some([format!("{:0>176}_{i}", 0), format!("{:0>176}_{j}", 0)]);
4523                }
4524            } else {
4525                collision_map.insert(hash_code, (i, a, encrypted_name));
4526            }
4527        }
4528        None
4529    }
4530
4531    #[fuchsia::test]
4532    async fn test_proxy_filename() {
4533        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4534        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4535        let mut filenames = Vec::new();
4536        let object_id;
4537        {
4538            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4539            let root_volume = root_volume(fs.clone()).await.unwrap();
4540            let store = root_volume
4541                .new_volume(
4542                    "vol",
4543                    NewChildStoreOptions {
4544                        options: StoreOptions {
4545                            crypt: Some(crypt.clone()),
4546                            ..StoreOptions::default()
4547                        },
4548                        ..Default::default()
4549                    },
4550                )
4551                .await
4552                .unwrap();
4553
4554            // Create a (very weak) key for our encrypted directory.
4555            crypt
4556                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
4557                .expect("add wrapping key failed");
4558
4559            object_id = {
4560                let mut transaction = fs
4561                    .root_store()
4562                    .new_transaction(lock_keys![], Options::default())
4563                    .await
4564                    .expect("new_transaction failed");
4565                let dir = Directory::create(&mut transaction, &store, Some(WRAPPING_KEY_ID))
4566                    .await
4567                    .expect("create failed");
4568
4569                transaction.commit().await.expect("commit");
4570                dir.object_id()
4571            };
4572
4573            let dir = Directory::open(&store, object_id).await.expect("open failed");
4574
4575            dir.set_casefold(true).await.expect("set casefold");
4576            assert!(dir.dir_type().is_casefold());
4577
4578            let key = dir.get_fscrypt_key().await.expect("key").into_cipher().unwrap();
4579
4580            // Nb: We use a rather expensive brute force search to find two filenames that:
4581            //   1. Have the same hash_code.
4582            //   2. Have the same prefix.
4583            //   3. Have encrypted names and sha256 that sort differently.
4584            // This is to exercise iter_from and lookup() handling scanning of locked directories.
4585            // This search returns stable results so in the interest of cheap tests, this code
4586            // is commented out but should be equivalent to the constants below.
4587            // let collision_pair =
4588            //     find_out_of_order_sha256_long_prefix_pair(dir.object_id(), &key).unwrap();
4589            let collision_pair =
4590                [format!("{:0>176}_{}", 0, 93515), format!("{:0>176}_{}", 0, 15621)];
4591
4592            // Create set of files with a common prefix, long enough to exceed prefix length of 48.
4593            // The first 48 encrypted name bytes will be the same, but the `sha256` will differ.
4594            for filename in (0..64)
4595                .into_iter()
4596                .map(|i| format!("{:0>176}_{i}", 0))
4597                .chain(collision_pair.into_iter())
4598            {
4599                let hash_code = key.hash_code_casefold(&filename);
4600                let encrypted_name =
4601                    encrypt_filename(&*key, dir.object_id(), &filename).expect("encrypt_filename");
4602                let proxy_filename =
4603                    ProxyFilename::new_with_hash_code(hash_code as u64, &encrypted_name);
4604                let mut transaction = fs
4605                    .root_store()
4606                    .new_transaction(
4607                        lock_keys![LockKey::object(store.store_object_id(), dir.object_id()),],
4608                        Options::default(),
4609                    )
4610                    .await
4611                    .expect("new_transaction failed");
4612                let file = dir
4613                    .create_child_file(&mut transaction, &filename)
4614                    .await
4615                    .expect("create_child_file failed");
4616                filenames.push((proxy_filename, file.object_id()));
4617                transaction.commit().await.expect("commit failed");
4618            }
4619
4620            fs.close().await.expect("Close failed");
4621        }
4622
4623        let device = fs.take_device().await;
4624
4625        // Now try and read the encrypted directory without keys.
4626        device.reopen(false);
4627        let fs = FxFilesystem::open(device).await.expect("open failed");
4628        {
4629            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4630            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4631            let store = root_volume
4632                .volume(
4633                    "vol",
4634                    StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
4635                )
4636                .await
4637                .expect("volume failed");
4638            let dir = Directory::open(&store, object_id).await.expect("open failed");
4639            assert!(dir.dir_type().is_casefold());
4640
4641            // Ensure uniqueness of the proxy filenames.
4642            assert_eq!(
4643                filenames.iter().map(|(name, _)| (*name).into()).collect::<HashSet<String>>().len(),
4644                filenames.len()
4645            );
4646
4647            let filename = filenames[0].0.filename.clone();
4648            for (proxy_filename, object_id) in &filenames {
4649                // We used such a long prefix that we expect all files to share it.
4650                assert_eq!(filename, proxy_filename.filename);
4651
4652                let proxy_filename_str: String = (*proxy_filename).into();
4653                let item = dir
4654                    .lookup(&proxy_filename_str)
4655                    .await
4656                    .expect("lookup failed")
4657                    .expect("lookup is not None");
4658                assert_eq!(item.0, *object_id, "Mismatch for filename '{proxy_filename:?}'");
4659            }
4660
4661            fs.close().await.expect("Close failed");
4662        }
4663    }
4664
4665    #[fuchsia::test]
4666    async fn test_replace_directory_and_tombstone_on_remount() {
4667        let device = DeviceHolder::new(FakeDevice::new(8192, 4096));
4668        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4669        let crypt = Arc::new(new_insecure_crypt());
4670        {
4671            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4672            let store = root_volume
4673                .new_volume(
4674                    "test",
4675                    NewChildStoreOptions {
4676                        options: StoreOptions {
4677                            crypt: Some(crypt.clone() as Arc<dyn Crypt>),
4678                            ..StoreOptions::default()
4679                        },
4680                        ..Default::default()
4681                    },
4682                )
4683                .await
4684                .expect("new_volume failed");
4685
4686            let mut transaction = fs
4687                .root_store()
4688                .new_transaction(
4689                    lock_keys![LockKey::object(
4690                        store.store_object_id(),
4691                        store.root_directory_object_id()
4692                    )],
4693                    Options::default(),
4694                )
4695                .await
4696                .expect("new transaction failed");
4697
4698            let root_directory = Directory::open(&store, store.root_directory_object_id())
4699                .await
4700                .expect("open failed");
4701            let _directory = root_directory
4702                .create_child_dir(&mut transaction, "foo")
4703                .await
4704                .expect("create_child_dir failed");
4705            let directory = root_directory
4706                .create_child_dir(&mut transaction, "bar")
4707                .await
4708                .expect("create_child_dir failed");
4709            let oid = directory.object_id();
4710
4711            transaction.commit().await.expect("commit failed");
4712
4713            let mut transaction = fs
4714                .root_store()
4715                .new_transaction(
4716                    lock_keys![LockKey::object(
4717                        store.store_object_id(),
4718                        store.root_directory_object_id()
4719                    )],
4720                    Options::default(),
4721                )
4722                .await
4723                .expect("new transaction failed");
4724
4725            replace_child_with_object(
4726                &mut transaction,
4727                Some((oid, ObjectDescriptor::Directory)),
4728                (&root_directory, "foo"),
4729                0,
4730                false,
4731                Timestamp::now(),
4732            )
4733            .await
4734            .expect("replace_child_with_object failed");
4735
4736            // If replace_child_with_object erroneously were to queue a tombstone, this will allow
4737            // it to run before we've committed, which will cause the test to fail below when we
4738            // remount and try and tombstone the object again.
4739            yield_to_executor().await;
4740
4741            transaction.commit().await.expect("commit failed");
4742
4743            fs.close().await.expect("close failed");
4744        }
4745
4746        let device = fs.take_device().await;
4747        device.reopen(false);
4748        let fs = FxFilesystem::open(device).await.expect("open failed");
4749        let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4750        let _store = root_volume
4751            .volume(
4752                "test",
4753                StoreOptions {
4754                    crypt: Some(crypt.clone() as Arc<dyn Crypt>),
4755                    ..StoreOptions::default()
4756                },
4757            )
4758            .await
4759            .expect("new_volume failed");
4760
4761        // Allow the graveyard to run.
4762        yield_to_executor().await;
4763
4764        fs.close().await.expect("close failed");
4765    }
4766
4767    #[test_case(false; "non_casefold")]
4768    #[test_case(true; "casefold")]
4769    #[fuchsia::test]
4770    async fn test_lookup_long_filename_in_locked_directory(casefold: bool) {
4771        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4772        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4773        let object_id;
4774        let mut filenames = Vec::new();
4775        {
4776            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4777            let root_volume = root_volume(fs.clone()).await.unwrap();
4778            let store = root_volume
4779                .new_volume(
4780                    "vol",
4781                    NewChildStoreOptions {
4782                        options: StoreOptions {
4783                            crypt: Some(crypt.clone()),
4784                            ..StoreOptions::default()
4785                        },
4786                        ..Default::default()
4787                    },
4788                )
4789                .await
4790                .unwrap();
4791
4792            crypt
4793                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
4794                .expect("add_wrapping_key failed");
4795
4796            object_id = {
4797                let mut transaction = fs
4798                    .root_store()
4799                    .new_transaction(lock_keys![], Options::default())
4800                    .await
4801                    .expect("new_transaction failed");
4802                let dir = Directory::create(&mut transaction, &store, Some(WRAPPING_KEY_ID))
4803                    .await
4804                    .expect("create failed");
4805
4806                transaction.commit().await.expect("commit");
4807                dir.object_id()
4808            };
4809            let dir = Directory::open(&store, object_id).await.expect("open failed");
4810            if casefold {
4811                dir.set_casefold(true).await.expect("set casefold");
4812            }
4813
4814            let key = dir.get_fscrypt_key().await.expect("key").into_cipher().unwrap();
4815
4816            for len in [144, 145, 255] {
4817                let filename = "a".repeat(len);
4818                let encrypted_name =
4819                    encrypt_filename(&*key, dir.object_id(), &filename).expect("encrypt_filename");
4820                let proxy_filename = if casefold {
4821                    let hash_code = key.hash_code_casefold(&filename);
4822                    ProxyFilename::new_with_hash_code(hash_code as u64, &encrypted_name)
4823                } else {
4824                    ProxyFilename::new(&encrypted_name)
4825                };
4826                let mut transaction = fs
4827                    .root_store()
4828                    .new_transaction(
4829                        lock_keys![LockKey::object(store.store_object_id(), dir.object_id()),],
4830                        Options::default(),
4831                    )
4832                    .await
4833                    .expect("new_transaction failed");
4834                let file = dir
4835                    .create_child_file(&mut transaction, &filename)
4836                    .await
4837                    .expect("create_child_file failed");
4838                filenames.push((proxy_filename, file.object_id()));
4839                transaction.commit().await.expect("commit failed");
4840            }
4841
4842            fs.close().await.expect("Close failed");
4843        }
4844
4845        let device = fs.take_device().await;
4846        device.reopen(false);
4847        let fs = FxFilesystem::open(device).await.expect("open failed");
4848        {
4849            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4850            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4851            let store = root_volume
4852                .volume(
4853                    "vol",
4854                    StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
4855                )
4856                .await
4857                .expect("volume failed");
4858            let dir = Directory::open(&store, object_id).await.expect("open failed");
4859
4860            // Verify that iteration works.
4861            let layer_set = dir.store().tree().layer_set();
4862            let mut merger = layer_set.merger();
4863            let mut iter = dir.iter(&mut merger).await.expect("iter failed");
4864            let mut entries = Vec::new();
4865            while let Some((name, _, _)) = iter.get() {
4866                entries.push(name.to_string());
4867                iter.advance().await.expect("advance failed");
4868            }
4869            assert_eq!(entries.len(), filenames.len());
4870
4871            for (proxy_filename, object_id) in &filenames {
4872                let proxy_filename_str: String = (*proxy_filename).into();
4873                assert!(entries.contains(&proxy_filename_str));
4874                let item = dir
4875                    .lookup(&proxy_filename_str)
4876                    .await
4877                    .expect("lookup failed")
4878                    .expect("lookup is not None");
4879                assert_eq!(item.0, *object_id, "Mismatch for filename '{proxy_filename:?}'");
4880            }
4881
4882            fs.close().await.expect("Close failed");
4883        }
4884    }
4885
4886    #[fuchsia::test]
4887    async fn test_lookup_cached_entry_after_unlock() {
4888        const FILENAME: &str = "foo";
4889        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4890        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4891        let object_id;
4892        let proxy_filename;
4893        {
4894            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4895            let root_volume = root_volume(fs.clone()).await.unwrap();
4896            let store = root_volume
4897                .new_volume(
4898                    "vol",
4899                    NewChildStoreOptions {
4900                        options: StoreOptions {
4901                            crypt: Some(crypt.clone()),
4902                            ..StoreOptions::default()
4903                        },
4904                        ..Default::default()
4905                    },
4906                )
4907                .await
4908                .unwrap();
4909
4910            crypt
4911                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
4912                .expect("add_wrapping_key failed");
4913
4914            let mut transaction = fs
4915                .root_store()
4916                .new_transaction(lock_keys![], Options::default())
4917                .await
4918                .expect("new_transaction failed");
4919            let dir = Directory::create(&mut transaction, &store, Some(WRAPPING_KEY_ID))
4920                .await
4921                .expect("create failed");
4922            transaction.commit().await.expect("commit");
4923            object_id = dir.object_id();
4924
4925            let key = dir.get_fscrypt_key().await.expect("key").into_cipher().unwrap();
4926            let encrypted_name =
4927                encrypt_filename(&*key, object_id, FILENAME).expect("encrypt_filename");
4928            proxy_filename = ProxyFilename::new(&encrypted_name);
4929
4930            let mut transaction = fs
4931                .root_store()
4932                .new_transaction(
4933                    lock_keys![LockKey::object(store.store_object_id(), object_id),],
4934                    Options::default(),
4935                )
4936                .await
4937                .expect("new_transaction failed");
4938            dir.create_child_file(&mut transaction, FILENAME)
4939                .await
4940                .expect("create_child_file failed");
4941            transaction.commit().await.expect("commit failed");
4942
4943            fs.close().await.expect("Close failed");
4944        }
4945
4946        let device = fs.take_device().await;
4947        device.reopen(false);
4948        let fs = FxFilesystem::open(device).await.expect("open failed");
4949        {
4950            let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
4951            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4952            let store = root_volume
4953                .volume(
4954                    "vol",
4955                    StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
4956                )
4957                .await
4958                .expect("volume failed");
4959            let dir = Directory::open(&store, object_id).await.expect("open failed");
4960
4961            let proxy_filename_str: String = proxy_filename.into();
4962            // This should succeed because the directory is locked.
4963            dir.lookup(&proxy_filename_str)
4964                .await
4965                .expect("lookup failed")
4966                .expect("lookup is not None");
4967
4968            crypt
4969                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
4970                .expect("add_wrapping_key failed");
4971
4972            // This should fail because the directory is now unlocked and we shouldn't be able to
4973            // find the file using its encrypted name.
4974            assert!(dir.lookup(&proxy_filename_str).await.expect("lookup failed").is_none());
4975
4976            fs.close().await.expect("Close failed");
4977        }
4978    }
4979
4980    #[test_case(false, false; "no_encryption_no_casefold")]
4981    #[test_case(false, true; "no_encryption_casefold")]
4982    #[test_case(true, false; "encryption_no_casefold")]
4983    #[test_case(true, true; "encryption_casefold")]
4984    #[fuchsia::test]
4985    async fn test_traversal_position(encrypted: bool, casefold: bool) {
4986        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
4987        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
4988        {
4989            let root_volume = root_volume(fs.clone()).await.expect("root_volume failed");
4990            let crypt = Arc::new(new_insecure_crypt());
4991            crypt
4992                .add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into())
4993                .expect("add_wrapping_key failed");
4994            let store = root_volume
4995                .new_volume(
4996                    "test",
4997                    NewChildStoreOptions {
4998                        options: StoreOptions {
4999                            crypt: Some(crypt.clone() as Arc<dyn Crypt>),
5000                            ..StoreOptions::default()
5001                        },
5002                        ..Default::default()
5003                    },
5004                )
5005                .await
5006                .expect("new_volume failed");
5007            let mut root_dir = Directory::open(&store, store.root_directory_object_id())
5008                .await
5009                .expect("open failed");
5010            if encrypted {
5011                let mut transaction = fs
5012                    .root_store()
5013                    .new_transaction(
5014                        lock_keys![LockKey::object(
5015                            store.store_object_id(),
5016                            store.root_directory_object_id()
5017                        )],
5018                        Options::default(),
5019                    )
5020                    .await
5021                    .expect("new_transaction failed");
5022                root_dir.set_wrapping_key(&mut transaction, WRAPPING_KEY_ID).await.unwrap();
5023                transaction.commit().await.unwrap();
5024
5025                // `set_wrapping_key` doesn't update the in-memory state, so reopen the directory.
5026                root_dir = Directory::open(&store, store.root_directory_object_id())
5027                    .await
5028                    .expect("open failed");
5029            }
5030            if casefold {
5031                root_dir.set_casefold(true).await.unwrap();
5032            }
5033
5034            let mut transaction = fs
5035                .root_store()
5036                .new_transaction(
5037                    lock_keys![LockKey::object(
5038                        store.store_object_id(),
5039                        store.root_directory_object_id()
5040                    )],
5041                    Options::default(),
5042                )
5043                .await
5044                .expect("new_transaction failed");
5045            let _ = root_dir.create_child_file(&mut transaction, "foo").await.unwrap();
5046            transaction.commit().await.unwrap();
5047
5048            let layer_set = store.tree().layer_set();
5049            let mut merger = layer_set.merger();
5050            let iter = root_dir.iter(&mut merger).await.expect("iter failed");
5051            let pos =
5052                iter.traversal_position(|name| name.to_string(), |bytes| format!("{:?}", bytes));
5053            assert!(
5054                pos.is_some(),
5055                "traversal_position returned None for encrypted={}, casefold={}",
5056                encrypted,
5057                casefold
5058            );
5059        }
5060        fs.close().await.expect("close failed");
5061    }
5062
5063    /// Verifies that renaming a file to a casefold-equivalent casing variant within
5064    /// the same directory works.
5065    #[fuchsia::test]
5066    async fn test_casefold_same_dir_rename() -> Result<(), Error> {
5067        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
5068        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
5069
5070        let root_volume = root_volume(fs.clone()).await.unwrap();
5071        let store = root_volume.new_volume("vol", NewChildStoreOptions::default()).await.unwrap();
5072
5073        let dir = {
5074            let mut transaction = fs
5075                .root_store()
5076                .new_transaction(lock_keys![], Options::default())
5077                .await
5078                .expect("new_transaction failed");
5079            let dir =
5080                Directory::create(&mut transaction, &store, None).await.expect("create failed");
5081            transaction.commit().await.expect("commit");
5082            dir
5083        };
5084
5085        // 1. Enable casefolding on the directory
5086        dir.set_casefold(true).await.expect("set casefold");
5087
5088        // 2. Create a child file "FOO" (casing: uppercase)
5089        let file_id = {
5090            let mut transaction = fs
5091                .root_store()
5092                .new_transaction(
5093                    lock_keys![LockKey::object(store.store_object_id(), dir.object_id())],
5094                    Options::default(),
5095                )
5096                .await
5097                .expect("new_transaction failed");
5098            let file =
5099                dir.create_child_file(&mut transaction, "FOO").await.expect("create file failed");
5100            transaction.commit().await.expect("commit failed");
5101            file.object_id()
5102        };
5103
5104        // 3. Confirm target can be looked up under both "FOO" and "foo" (due to case-insensitivity)
5105        let (lookup_id_foo, _, _) = dir.lookup("foo").await.unwrap().unwrap();
5106        assert_eq!(lookup_id_foo, file_id);
5107        let (lookup_id_foo_upper, _, _) = dir.lookup("FOO").await.unwrap().unwrap();
5108        assert_eq!(lookup_id_foo_upper, file_id);
5109
5110        // 4. Execute the same-directory casefolded rename "FOO" -> "foo" (lowercase)
5111        {
5112            let mut transaction = fs
5113                .root_store()
5114                .new_transaction(
5115                    lock_keys![
5116                        LockKey::object(store.store_object_id(), dir.object_id()),
5117                        LockKey::object(store.store_object_id(), file_id),
5118                    ],
5119                    Options::default(),
5120                )
5121                .await
5122                .expect("new_transaction failed");
5123
5124            replace_child(&mut transaction, Some((&dir, "FOO")), (&dir, "foo"))
5125                .await
5126                .expect("same-dir casefold rename failed");
5127
5128            transaction.commit().await.expect("commit failed");
5129        }
5130
5131        // 5. Assert the file was NOT purged (lookup still succeeds!)
5132        let (final_id_foo, _, _) = dir.lookup("foo").await.unwrap().unwrap();
5133        assert_eq!(final_id_foo, file_id);
5134
5135        // Verify survival of the underlying node handle (ObjectStore::open_object)
5136        let file_handle =
5137            ObjectStore::open_object(&dir.owner(), file_id, HandleOptions::default(), None)
5138                .await
5139                .expect("Underlying file object was prematurely tombstoned / graveyarded!");
5140
5141        // Assert reference count is exactly 1.
5142        let properties = file_handle.get_properties().await.unwrap();
5143        assert_eq!(properties.refs, 1);
5144
5145        // Assert the graveyard is completely empty (proves NO graveyard tombstone leak occurred!)
5146        assert_eq!(dir.store().graveyard_count(), 0);
5147
5148        // 6. Verify the internal casing entry was updated to "foo"
5149        let mut count = 0;
5150        let mut found_casing = String::new();
5151        let layer_set = dir.store().tree().layer_set();
5152        let mut merger = layer_set.merger();
5153        let mut iter = dir.iter(&mut merger).await.expect("iter");
5154        while let Some(entry) = iter.get() {
5155            count += 1;
5156            found_casing = entry.0.to_string();
5157            iter.advance().await.expect("advance");
5158        }
5159        assert_eq!(count, 1);
5160        assert_eq!(found_casing, "foo"); // mapping has successfully changed from "FOO" to "foo".
5161
5162        fs.close().await.expect("Close failed");
5163        Ok(())
5164    }
5165
5166    #[fuchsia::test]
5167    async fn test_casefold_rename_mismatched_casing() -> Result<(), Error> {
5168        let device = DeviceHolder::new(FakeDevice::new(8192, TEST_DEVICE_BLOCK_SIZE));
5169        let fs = FxFilesystem::new_empty(device).await.expect("new_empty failed");
5170
5171        let root_volume = root_volume(fs.clone()).await.unwrap();
5172        let store = root_volume.new_volume("vol", NewChildStoreOptions::default()).await.unwrap();
5173
5174        let dir = {
5175            let mut transaction = fs
5176                .root_store()
5177                .new_transaction(lock_keys![], Options::default())
5178                .await
5179                .expect("new_transaction failed");
5180            let dir =
5181                Directory::create(&mut transaction, &store, None).await.expect("create failed");
5182            transaction.commit().await.expect("commit");
5183            dir
5184        };
5185
5186        dir.set_casefold(true).await.expect("set casefold");
5187
5188        // Create "foo" (lowercase)
5189        let file_id = {
5190            let mut transaction = fs
5191                .root_store()
5192                .new_transaction(
5193                    lock_keys![LockKey::object(store.store_object_id(), dir.object_id())],
5194                    Options::default(),
5195                )
5196                .await
5197                .expect("new_transaction failed");
5198            let file =
5199                dir.create_child_file(&mut transaction, "foo").await.expect("create file failed");
5200            transaction.commit().await.expect("commit failed");
5201            file.object_id()
5202        };
5203
5204        // Rename "FOO" (uppercase) to "bar"
5205        {
5206            let mut transaction = fs
5207                .root_store()
5208                .new_transaction(
5209                    lock_keys![
5210                        LockKey::object(store.store_object_id(), dir.object_id()),
5211                        LockKey::object(store.store_object_id(), file_id),
5212                    ],
5213                    Options::default(),
5214                )
5215                .await
5216                .expect("new_transaction failed");
5217
5218            replace_child(&mut transaction, Some((&dir, "FOO")), (&dir, "bar"))
5219                .await
5220                .expect("rename failed");
5221
5222            transaction.commit().await.expect("commit failed");
5223        }
5224
5225        // Check if "foo" (or "FOO") is gone.
5226        let lookup_foo = dir.lookup("foo").await.unwrap();
5227        assert!(
5228            lookup_foo.is_none(),
5229            "Old name 'foo' still exists! Lookup returned: {:?}",
5230            lookup_foo
5231        );
5232
5233        // Check if "bar" exists.
5234        let (lookup_id_bar, _, _) = dir.lookup("bar").await.unwrap().expect("bar not found");
5235        assert_eq!(lookup_id_bar, file_id);
5236
5237        fs.close().await.expect("Close failed");
5238        Ok(())
5239    }
5240
5241    #[fuchsia::test]
5242    async fn test_hard_link_encrypted_symlink_and_fsck_passes() {
5243        let device = DeviceHolder::new(FakeDevice::new(16384, 512));
5244        let filesystem = FxFilesystem::new_empty(device).await.expect("new_empty failed");
5245        let crypt: Arc<CryptBase> = Arc::new(new_insecure_crypt());
5246        crypt.add_wrapping_key(WRAPPING_KEY_ID, [1; 32].into()).expect("add_wrapping_key failed");
5247
5248        let root_volume = root_volume(filesystem.clone()).await.expect("root_volume failed");
5249        let store = root_volume
5250            .new_volume(
5251                "vol",
5252                NewChildStoreOptions {
5253                    options: StoreOptions { crypt: Some(crypt.clone()), ..StoreOptions::default() },
5254                    ..Default::default()
5255                },
5256            )
5257            .await
5258            .expect("new_volume failed");
5259
5260        let root_directory =
5261            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
5262
5263        let mut transaction = filesystem
5264            .root_store()
5265            .new_transaction(
5266                lock_keys![LockKey::object(
5267                    store.store_object_id(),
5268                    store.root_directory_object_id()
5269                )],
5270                Options::default(),
5271            )
5272            .await
5273            .expect("new transaction failed");
5274        root_directory.set_wrapping_key(&mut transaction, WRAPPING_KEY_ID).await.unwrap();
5275        transaction.commit().await.unwrap();
5276
5277        let root_directory =
5278            Directory::open(&store, store.root_directory_object_id()).await.expect("open failed");
5279
5280        let mut transaction = filesystem
5281            .root_store()
5282            .new_transaction(
5283                lock_keys![LockKey::object(
5284                    store.store_object_id(),
5285                    store.root_directory_object_id()
5286                )],
5287                Options::default(),
5288            )
5289            .await
5290            .expect("new transaction failed");
5291        let symlink_id = root_directory
5292            .create_symlink(&mut transaction, b"target_path", "symlink")
5293            .await
5294            .expect("create_symlink failed");
5295        transaction.commit().await.expect("commit failed");
5296
5297        fsck(filesystem.clone()).await.expect("fsck failed");
5298        fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
5299            .await
5300            .expect("fsck_volume failed");
5301
5302        let mut transaction = filesystem
5303            .root_store()
5304            .new_transaction(
5305                lock_keys![
5306                    LockKey::object(store.store_object_id(), root_directory.object_id()),
5307                    LockKey::object(store.store_object_id(), symlink_id)
5308                ],
5309                Options::default(),
5310            )
5311            .await
5312            .expect("new transaction failed");
5313        root_directory
5314            .insert_child(&mut transaction, "symlink_link", symlink_id, ObjectDescriptor::Symlink)
5315            .await
5316            .expect("insert_child failed");
5317        store.adjust_refs(&mut transaction, symlink_id, 1).await.expect("adjust_refs failed");
5318        transaction.commit().await.expect("commit failed");
5319
5320        fsck(filesystem.clone()).await.expect("fsck failed");
5321        fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
5322            .await
5323            .expect("fsck_volume failed");
5324
5325        let mut transaction = filesystem
5326            .root_store()
5327            .new_transaction(
5328                lock_keys![
5329                    LockKey::object(store.store_object_id(), root_directory.object_id()),
5330                    LockKey::object(store.store_object_id(), symlink_id)
5331                ],
5332                Options::default(),
5333            )
5334            .await
5335            .expect("new transaction failed");
5336        replace_child(&mut transaction, None, (&root_directory, "symlink"))
5337            .await
5338            .expect("replace_child failed");
5339        transaction.commit().await.expect("commit failed");
5340
5341        fsck(filesystem.clone()).await.expect("fsck failed");
5342        fsck_volume(filesystem.as_ref(), store.store_object_id(), Some(crypt.clone()))
5343            .await
5344            .expect("fsck_volume failed");
5345
5346        filesystem.close().await.expect("close failed");
5347    }
5348}