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