Skip to main content

fxfs/object_store/
directory.rs

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