fxfs/object_store/
directory.rs

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