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