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