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