Skip to main content

fxfs/object_store/
object_record.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5mod legacy;
6
7// TODO(https://fxbug.dev/42178223): need validation after deserialization.
8use crate::checksum::Checksums;
9use crate::log::error;
10use crate::lsm_tree::types::{
11    FuzzyHash, Item, ItemRef, LayerKey, MergeType, OrdLowerBound, OrdUpperBound, SortByU64, Value,
12};
13use crate::object_store::ProjectId;
14use crate::object_store::extent::{Extent, ExtentPartitionIterator};
15use crate::object_store::extent_record::{ExtentValue, ExtentValueV38};
16use crate::serialized_types::Versioned;
17use fprint::TypeFingerprint;
18use fxfs_crypto::{WrappedKey, WrappingKeyId};
19use fxfs_macros::SerializeKey;
20use fxfs_unicode::CasefoldString;
21use serde::{Deserialize, Serialize};
22use std::collections::BTreeMap;
23use std::default::Default;
24use std::hash::Hash;
25use std::time::{Duration, SystemTime, UNIX_EPOCH};
26
27/// ObjectDescriptor is the set of possible records in the object store.
28pub type ObjectDescriptor = ObjectDescriptorV32;
29
30#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, TypeFingerprint)]
31#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
32pub enum ObjectDescriptorV32 {
33    /// A file (in the generic sense; i.e. an object with some attributes).
34    File,
35    /// A directory (in the generic sense; i.e. an object with children).
36    Directory,
37    /// A volume, which is the root of a distinct object store containing Files and Directories.
38    Volume,
39    /// A symbolic link.
40    Symlink,
41}
42
43/// For specifying what property of the project is being addressed.
44pub type ProjectProperty = ProjectPropertyV32;
45
46#[derive(
47    Clone,
48    Debug,
49    Eq,
50    Hash,
51    Ord,
52    PartialEq,
53    PartialOrd,
54    Serialize,
55    Deserialize,
56    TypeFingerprint,
57    SerializeKey,
58)]
59#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
60pub enum ProjectPropertyV32 {
61    /// The configured limit for the project.
62    Limit,
63    /// The currently tracked usage for the project.
64    Usage,
65}
66
67pub type ObjectKeyData = ObjectKeyDataV54;
68
69#[derive(
70    Clone,
71    Debug,
72    Eq,
73    Hash,
74    PartialEq,
75    PartialOrd,
76    Ord,
77    Serialize,
78    Deserialize,
79    TypeFingerprint,
80    SerializeKey,
81)]
82#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
83pub enum ObjectKeyDataV54 {
84    /// A generic, untyped object.  This must come first and sort before all other keys for a given
85    /// object because it's also used as a tombstone and it needs to merge with all following keys.
86    Object,
87    /// Encryption keys for an object.
88    Keys,
89    /// An attribute associated with an object.  It has a 64-bit ID.
90    Attribute(AttributeId, AttributeKeyV32),
91    /// A child of a directory.
92    Child { name: String },
93    /// A graveyard entry for an entire object.
94    GraveyardEntry { object_id: u64 },
95    /// Project ID info. This should only be attached to the volume's root node. Used to address the
96    /// configured limit and the usage tracking which are ordered after the `project_id` to provide
97    /// locality of the two related values.
98    Project { project_id: ProjectId, property: ProjectPropertyV32 },
99    /// An extended attribute associated with an object. It stores the name used for the extended
100    /// attribute, which has a maximum size of 255 bytes enforced by fuchsia.io.
101    ExtendedAttribute {
102        #[serde(with = "crate::zerocopy_serialization")]
103        name: Vec<u8>,
104    },
105    /// A graveyard entry for an attribute.
106    GraveyardAttributeEntry { object_id: u64, attribute_id: AttributeId },
107    /// A child of an encrypted directory.  We store the filename in its encrypted form.  hash_code
108    /// is the hash of the casefolded human-readable name if a directory is also casefolded.  In
109    /// some legacy cases, this is also used in non-casefolded cases, and in some of those cases the
110    /// hash code can be 0.  Going forward, these cases are covered by `EncryptedChild` below.
111    EncryptedCasefoldChild(EncryptedCasefoldChild),
112    /// Case-insensitive child (legacy).
113    LegacyCasefoldChild(CasefoldString),
114    /// An encrypted child that does not use case folding.
115    EncryptedChild(EncryptedChild),
116    /// A child of a directory that uses the casefold feature.
117    /// (i.e. case insensitive, case preserving names)
118    CasefoldChild { hash_code: u32, name: String },
119}
120
121#[derive(
122    Clone,
123    Debug,
124    Eq,
125    Hash,
126    PartialEq,
127    PartialOrd,
128    Ord,
129    Serialize,
130    Deserialize,
131    TypeFingerprint,
132    SerializeKey,
133)]
134#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
135pub struct EncryptedCasefoldChild {
136    pub hash_code: u32,
137    #[serde(with = "crate::zerocopy_serialization")]
138    pub name: Vec<u8>,
139}
140
141#[derive(
142    Clone,
143    Debug,
144    Eq,
145    Hash,
146    PartialEq,
147    PartialOrd,
148    Ord,
149    Serialize,
150    Deserialize,
151    TypeFingerprint,
152    SerializeKey,
153)]
154#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
155pub struct EncryptedChild(#[serde(with = "crate::zerocopy_serialization")] pub Vec<u8>);
156
157pub type AttributeKey = AttributeKeyV32;
158
159#[derive(
160    Clone,
161    Debug,
162    Eq,
163    Hash,
164    Ord,
165    PartialEq,
166    PartialOrd,
167    Serialize,
168    Deserialize,
169    TypeFingerprint,
170    SerializeKey,
171)]
172#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
173pub enum AttributeKeyV32 {
174    // Order here is important: code expects Attribute to precede Extent.
175    Attribute,
176    Extent(Extent),
177}
178
179/// ObjectKey is a key in the object store.
180pub type ObjectKey = ObjectKeyV54;
181
182#[derive(
183    Clone,
184    Debug,
185    Eq,
186    Ord,
187    Hash,
188    PartialEq,
189    PartialOrd,
190    Serialize,
191    Deserialize,
192    SerializeKey,
193    TypeFingerprint,
194    Versioned,
195)]
196#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
197pub struct ObjectKeyV54 {
198    /// The ID of the object referred to.
199    pub object_id: u64,
200    /// The type and data of the key.
201    pub data: ObjectKeyDataV54,
202}
203
204impl SortByU64 for ObjectKey {
205    fn get_leading_u64(&self) -> u64 {
206        self.object_id
207    }
208}
209
210impl ObjectKey {
211    /// Creates a generic ObjectKey.
212    pub fn object(object_id: u64) -> Self {
213        Self { object_id: object_id, data: ObjectKeyData::Object }
214    }
215
216    /// Creates an ObjectKey for encryption keys.
217    pub fn keys(object_id: u64) -> Self {
218        Self { object_id, data: ObjectKeyData::Keys }
219    }
220
221    /// Creates an ObjectKey for an attribute.
222    pub fn attribute(object_id: u64, attribute_id: AttributeId, key: AttributeKey) -> Self {
223        Self { object_id, data: ObjectKeyData::Attribute(attribute_id, key) }
224    }
225
226    /// Creates an ObjectKey for an extent.
227    pub fn extent(object_id: u64, attribute_id: AttributeId, range: std::ops::Range<u64>) -> Self {
228        Self {
229            object_id,
230            data: ObjectKeyData::Attribute(attribute_id, AttributeKey::Extent(Extent(range))),
231        }
232    }
233
234    /// Creates an ObjectKey from an extent.
235    pub fn from_extent(object_id: u64, attribute_id: AttributeId, extent: Extent) -> Self {
236        Self {
237            object_id,
238            data: ObjectKeyData::Attribute(attribute_id, AttributeKey::Extent(extent)),
239        }
240    }
241
242    /// Creates an ObjectKey for a child.
243    pub fn child(object_id: u64, name: &str, dir_type: DirType) -> Self {
244        match dir_type {
245            DirType::Casefold => {
246                let cf = fxfs_unicode::CasefoldStr::new(name);
247                let casefolded = cf.casefold_normalized_chars().flat_map(fxfs_unicode::utf8_bytes);
248                let hash_code = fscrypt::direntry::tea_hash_filename(casefolded);
249                Self {
250                    object_id,
251                    data: ObjectKeyData::CasefoldChild { hash_code, name: name.into() },
252                }
253            }
254            DirType::LegacyCasefold => Self {
255                object_id,
256                data: ObjectKeyData::LegacyCasefoldChild(CasefoldString::new(name.into())),
257            },
258            DirType::Normal => Self { object_id, data: ObjectKeyData::Child { name: name.into() } },
259            DirType::Encrypted(_) | DirType::EncryptedCasefold(_) => {
260                // These shouldn't be used directly; encrypted_child should be used instead.
261                panic!("Encrypted modes require an encrypted name");
262            }
263        }
264    }
265
266    /// Creates an ObjectKey for an encrypted child.
267    ///
268    /// The hash_code is important here -- especially for fscrypt as it affects the
269    /// name of locked files.
270    ///
271    /// For case-insensitive lookups in large encrypted directories, we lose the ability to binary
272    /// search for an entry of interest because encryption breaks our sort order. In these cases
273    /// we prefix records with a 32-bit hash based on the stable *casefolded* name. Hash collisions
274    /// aside, this lets us jump straight to the entry of interest, if it exists.
275    pub fn encrypted_child(object_id: u64, name: Vec<u8>, hash_code: Option<u32>) -> Self {
276        if let Some(hash_code) = hash_code {
277            Self {
278                object_id,
279                data: ObjectKeyData::EncryptedCasefoldChild(EncryptedCasefoldChild {
280                    hash_code,
281                    name,
282                }),
283            }
284        } else {
285            Self { object_id, data: ObjectKeyData::EncryptedChild(EncryptedChild(name)) }
286        }
287    }
288
289    /// Creates a graveyard entry for an object.
290    pub fn graveyard_entry(graveyard_object_id: u64, object_id: u64) -> Self {
291        Self { object_id: graveyard_object_id, data: ObjectKeyData::GraveyardEntry { object_id } }
292    }
293
294    /// Creates a graveyard entry for an attribute.
295    pub fn graveyard_attribute_entry(
296        graveyard_object_id: u64,
297        object_id: u64,
298        attribute_id: AttributeId,
299    ) -> Self {
300        Self {
301            object_id: graveyard_object_id,
302            data: ObjectKeyData::GraveyardAttributeEntry { object_id, attribute_id },
303        }
304    }
305
306    /// Creates an ObjectKey for a ProjectLimit entry.
307    pub fn project_limit(object_id: u64, project_id: ProjectId) -> Self {
308        Self {
309            object_id,
310            data: ObjectKeyData::Project { project_id, property: ProjectProperty::Limit },
311        }
312    }
313
314    /// Creates an ObjectKey for a ProjectUsage entry.
315    pub fn project_usage(object_id: u64, project_id: ProjectId) -> Self {
316        Self {
317            object_id,
318            data: ObjectKeyData::Project { project_id, property: ProjectProperty::Usage },
319        }
320    }
321
322    pub fn extended_attribute(object_id: u64, name: Vec<u8>) -> Self {
323        Self { object_id, data: ObjectKeyData::ExtendedAttribute { name } }
324    }
325
326    /// Returns the merge key for this key; that is, a key which is <= this key and any
327    /// other possibly overlapping key, under Ord. This would be used for the hint in |merge_into|.
328    pub fn key_for_merge_into(&self) -> Self {
329        if let Self {
330            object_id,
331            data: ObjectKeyData::Attribute(attribute_id, AttributeKey::Extent(e)),
332        } = self
333        {
334            Self::attribute(*object_id, *attribute_id, AttributeKey::Extent(e.key_for_merge_into()))
335        } else {
336            self.clone()
337        }
338    }
339}
340
341impl OrdUpperBound for ObjectKey {
342    fn cmp_upper_bound(&self, other: &ObjectKey) -> std::cmp::Ordering {
343        self.object_id.cmp(&other.object_id).then_with(|| match (&self.data, &other.data) {
344            (
345                ObjectKeyData::Attribute(left_attr_id, AttributeKey::Extent(left_extent)),
346                ObjectKeyData::Attribute(right_attr_id, AttributeKey::Extent(right_extent)),
347            ) => left_attr_id.cmp(right_attr_id).then(left_extent.cmp_upper_bound(right_extent)),
348            _ => self.data.cmp(&other.data),
349        })
350    }
351}
352
353impl OrdLowerBound for ObjectKey {
354    fn cmp_lower_bound(&self, other: &ObjectKey) -> std::cmp::Ordering {
355        self.object_id.cmp(&other.object_id).then_with(|| match (&self.data, &other.data) {
356            (
357                ObjectKeyData::Attribute(left_attr_id, AttributeKey::Extent(left_extent)),
358                ObjectKeyData::Attribute(right_attr_id, AttributeKey::Extent(right_extent)),
359            ) => left_attr_id.cmp(right_attr_id).then(left_extent.cmp_lower_bound(right_extent)),
360            _ => self.data.cmp(&other.data),
361        })
362    }
363}
364
365impl LayerKey for ObjectKey {
366    fn merge_type(&self) -> MergeType {
367        // This listing is intentionally exhaustive to force folks to think about how certain
368        // subsets of the keyspace are merged.
369        match self.data {
370            ObjectKeyData::Object
371            | ObjectKeyData::Keys
372            | ObjectKeyData::Attribute(..)
373            | ObjectKeyData::Child { .. }
374            | ObjectKeyData::EncryptedChild(_)
375            | ObjectKeyData::EncryptedCasefoldChild(_)
376            | ObjectKeyData::CasefoldChild { .. }
377            | ObjectKeyData::LegacyCasefoldChild(_)
378            | ObjectKeyData::GraveyardEntry { .. }
379            | ObjectKeyData::GraveyardAttributeEntry { .. }
380            | ObjectKeyData::Project { property: ProjectProperty::Limit, .. }
381            | ObjectKeyData::ExtendedAttribute { .. } => MergeType::OptimizedMerge,
382            ObjectKeyData::Project { property: ProjectProperty::Usage, .. } => MergeType::FullMerge,
383        }
384    }
385
386    fn next_key(&self) -> Option<Self> {
387        match &self.data {
388            ObjectKeyData::Attribute(attr_id, AttributeKey::Extent(extent)) => {
389                // This key comes before (or is equal to) any extent starting at or after the
390                // end of `self`. Searching for its `next_key` finds extents that end after
391                // the end of `self`.
392                Some(ObjectKey {
393                    object_id: self.object_id,
394                    data: ObjectKeyData::Attribute(
395                        *attr_id,
396                        AttributeKey::Extent(Extent::search_key_from_offset(extent.end)),
397                    ),
398                })
399            }
400            _ => None,
401        }
402    }
403
404    fn search_key(&self) -> Option<Self> {
405        if let Self {
406            object_id,
407            data: ObjectKeyData::Attribute(attribute_id, AttributeKey::Extent(e)),
408        } = self
409        {
410            Some(Self::attribute(*object_id, *attribute_id, AttributeKey::Extent(e.search_key())))
411        } else {
412            None
413        }
414    }
415
416    fn is_search_key(&self) -> bool {
417        match self {
418            Self { data: ObjectKeyData::Attribute(_, AttributeKey::Extent(e)), .. } => {
419                e.is_search_key()
420            }
421            _ => true,
422        }
423    }
424
425    fn overlaps(&self, other: &Self) -> bool {
426        if self.object_id != other.object_id {
427            return false;
428        }
429        match (&self.data, &other.data) {
430            (
431                ObjectKeyData::Attribute(left_attr_id, AttributeKey::Extent(left_key)),
432                ObjectKeyData::Attribute(right_attr_id, AttributeKey::Extent(right_key)),
433            ) if *left_attr_id == *right_attr_id => left_key.overlaps(right_key),
434            (a, b) => a == b,
435        }
436    }
437}
438
439pub enum ObjectKeyFuzzyHashIterator {
440    Extent(/* object_id */ u64, AttributeId, ExtentPartitionIterator),
441    NotExtent(/* hash */ Option<u64>),
442}
443
444impl Iterator for ObjectKeyFuzzyHashIterator {
445    type Item = u64;
446
447    fn next(&mut self) -> Option<Self::Item> {
448        match self {
449            Self::Extent(oid, attr_id, extent_keys) => extent_keys.next().map(|range| {
450                let key = ObjectKey::extent(*oid, *attr_id, range);
451                crate::stable_hash::stable_hash(key)
452            }),
453            Self::NotExtent(hash) => hash.take(),
454        }
455    }
456
457    fn size_hint(&self) -> (usize, Option<usize>) {
458        match self {
459            Self::Extent(_, _, extent_keys) => extent_keys.size_hint(),
460            Self::NotExtent(hash) => {
461                let len = if hash.is_some() { 1 } else { 0 };
462                (len, Some(len))
463            }
464        }
465    }
466}
467
468impl ExactSizeIterator for ObjectKeyFuzzyHashIterator {}
469
470impl FuzzyHash for ObjectKey {
471    fn fuzzy_hash(&self) -> impl ExactSizeIterator<Item = u64> {
472        match &self.data {
473            ObjectKeyData::Attribute(attr_id, AttributeKey::Extent(extent)) => {
474                ObjectKeyFuzzyHashIterator::Extent(
475                    self.object_id,
476                    *attr_id,
477                    extent.fuzzy_hash_partition(),
478                )
479            }
480            _ => {
481                let hash = crate::stable_hash::stable_hash(self);
482                ObjectKeyFuzzyHashIterator::NotExtent(Some(hash))
483            }
484        }
485    }
486
487    fn is_range_key(&self) -> bool {
488        match &self.data {
489            ObjectKeyData::Attribute(_, AttributeKey::Extent(_)) => true,
490            _ => false,
491        }
492    }
493}
494
495/// UNIX epoch based timestamp in the UTC timezone.
496pub type Timestamp = TimestampV49;
497
498#[derive(
499    Copy,
500    Clone,
501    Debug,
502    Default,
503    Eq,
504    PartialEq,
505    Ord,
506    PartialOrd,
507    Serialize,
508    Deserialize,
509    TypeFingerprint,
510)]
511#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
512pub struct TimestampV49 {
513    nanos: u64,
514}
515
516impl Timestamp {
517    const NSEC_PER_SEC: u64 = 1_000_000_000;
518
519    pub fn now() -> Self {
520        SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or(Duration::ZERO).into()
521    }
522
523    pub const fn zero() -> Self {
524        Self { nanos: 0 }
525    }
526
527    pub const fn from_nanos(nanos: u64) -> Self {
528        Self { nanos }
529    }
530
531    pub fn from_secs_and_nanos(secs: u64, nanos: u32) -> Self {
532        let Some(secs_in_nanos) = secs.checked_mul(Self::NSEC_PER_SEC) else {
533            error!("Fxfs doesn't support dates past 2554-07-21");
534            return Self { nanos: u64::MAX };
535        };
536        let Some(nanos) = secs_in_nanos.checked_add(nanos as u64) else {
537            error!("Fxfs doesn't support dates past 2554-07-21");
538            return Self { nanos: u64::MAX };
539        };
540        Self { nanos }
541    }
542
543    /// Returns the total number of nanoseconds represented by this `Timestamp` since the Unix
544    /// epoch.
545    pub fn as_nanos(&self) -> u64 {
546        self.nanos
547    }
548
549    /// Returns the fractional nanoseconds represented by this `Timestamp`.
550    pub fn subsec_nanos(&self) -> u32 {
551        (self.nanos % Self::NSEC_PER_SEC) as u32
552    }
553
554    /// Returns the total number of whole seconds represented by this `Timestamp` since the Unix
555    /// epoch.
556    pub fn as_secs(&self) -> u64 {
557        self.nanos / Self::NSEC_PER_SEC
558    }
559}
560
561impl From<std::time::Duration> for Timestamp {
562    fn from(duration: std::time::Duration) -> Self {
563        Self::from_secs_and_nanos(duration.as_secs(), duration.subsec_nanos())
564    }
565}
566
567impl From<Timestamp> for std::time::Duration {
568    fn from(timestamp: Timestamp) -> std::time::Duration {
569        Duration::from_nanos(timestamp.nanos)
570    }
571}
572
573pub type ObjectKind = ObjectKindV54;
574
575#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, TypeFingerprint)]
576#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
577pub enum DirType {
578    Normal,
579    Encrypted(WrappingKeyId),
580    /// Legacy casefolded mode.
581    LegacyCasefold,
582    Casefold,
583    EncryptedCasefold(WrappingKeyId),
584}
585
586impl DirType {
587    pub fn is_casefold(&self) -> bool {
588        matches!(self, DirType::LegacyCasefold | DirType::Casefold | DirType::EncryptedCasefold(_))
589    }
590
591    pub fn is_encrypted(&self) -> bool {
592        matches!(self, DirType::Encrypted(_) | DirType::EncryptedCasefold(_))
593    }
594
595    pub fn with_encryption(self, id: WrappingKeyId) -> Self {
596        match self {
597            DirType::Normal => DirType::Encrypted(id),
598            DirType::Casefold => DirType::EncryptedCasefold(id),
599            _ => self,
600        }
601    }
602
603    pub fn with_casefold(self, val: bool) -> Self {
604        match (val, self) {
605            (true, DirType::Encrypted(id) | DirType::EncryptedCasefold(id)) => {
606                DirType::EncryptedCasefold(id)
607            }
608            (true, _) => DirType::Casefold,
609            (false, DirType::Encrypted(id) | DirType::EncryptedCasefold(id)) => {
610                DirType::Encrypted(id)
611            }
612            (false, _) => DirType::Normal,
613        }
614    }
615
616    pub fn wrapping_key_id(&self) -> Option<WrappingKeyId> {
617        match self {
618            DirType::Encrypted(id) | DirType::EncryptedCasefold(id) => Some(*id),
619            _ => None,
620        }
621    }
622}
623
624impl Default for DirType {
625    fn default() -> Self {
626        DirType::Normal
627    }
628}
629
630#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, TypeFingerprint)]
631#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
632pub enum ObjectKindV54 {
633    File {
634        /// The number of references to this file.
635        refs: u64,
636    },
637    Directory {
638        /// The number of sub-directories in this directory.
639        sub_dirs: u64,
640        /// The type of directory (encryption, casefolding, etc.)
641        dir_type: DirType,
642    },
643    Graveyard,
644    Symlink {
645        /// The number of references to this symbolic link.
646        refs: u64,
647        /// `link` is the target of the link and has no meaning within Fxfs; clients are free to
648        /// interpret it however they like.
649        #[serde(with = "crate::zerocopy_serialization")]
650        link: Box<[u8]>,
651    },
652    EncryptedSymlink {
653        /// The number of references to this symbolic link.
654        refs: u64,
655        /// `link` is the target of the link and has no meaning within Fxfs; clients are free to
656        /// interpret it however they like.
657        /// `link` is stored here in encrypted form, encrypted with the symlink's key using the
658        /// volume's data key.
659        #[serde(with = "crate::zerocopy_serialization")]
660        link: Box<[u8]>,
661    },
662}
663
664#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, TypeFingerprint, Versioned)]
665#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
666pub enum ObjectKindV49 {
667    File {
668        /// The number of references to this file.
669        refs: u64,
670    },
671    Directory {
672        /// The number of sub-directories in this directory.
673        sub_dirs: u64,
674        /// If set, contains the wrapping key id used to encrypt the file contents and filenames in
675        /// this directory.
676        wrapping_key_id: Option<WrappingKeyId>,
677        /// If true, all files and sub-directories created in this directory will support case
678        /// insensitive (but case-preserving) file naming.
679        casefold: bool,
680    },
681    Graveyard,
682    Symlink {
683        /// The number of references to this symbolic link.
684        refs: u64,
685        /// `link` is the target of the link and has no meaning within Fxfs; clients are free to
686        /// interpret it however they like.
687        #[serde(with = "crate::zerocopy_serialization")]
688        link: Box<[u8]>,
689    },
690    EncryptedSymlink {
691        /// The number of references to this symbolic link.
692        refs: u64,
693        /// `link` is the target of the link and has no meaning within Fxfs; clients are free to
694        /// interpret it however they like.
695        /// `link` is stored here in encrypted form, encrypted with the symlink's key using the
696        /// same encryption scheme as the one used to encrypt filenames.
697        #[serde(with = "crate::zerocopy_serialization")]
698        link: Box<[u8]>,
699    },
700}
701
702/// This consists of POSIX attributes that are not used in Fxfs but it may be meaningful to some
703/// clients to have the ability to to set and retrieve these values.
704pub type PosixAttributes = PosixAttributesV32;
705
706#[derive(Clone, Debug, Copy, Default, Serialize, Deserialize, PartialEq, TypeFingerprint)]
707#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
708pub struct PosixAttributesV32 {
709    /// The mode bits associated with this object
710    pub mode: u32,
711    /// User ID of owner
712    pub uid: u32,
713    /// Group ID of owner
714    pub gid: u32,
715    /// Device ID
716    pub rdev: u64,
717}
718
719/// Object-level attributes.  Note that these are not the same as "attributes" in the
720/// ObjectValue::Attribute sense, which refers to an arbitrary data payload associated with an
721/// object.  This naming collision is unfortunate.
722pub type ObjectAttributes = ObjectAttributesV49;
723
724#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, TypeFingerprint)]
725#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
726pub struct ObjectAttributesV49 {
727    /// The timestamp at which the object was created (i.e. crtime).
728    pub creation_time: TimestampV49,
729    /// The timestamp at which the object's data was last modified (i.e. mtime).
730    pub modification_time: TimestampV49,
731    /// The project id to associate this object's resource usage with.
732    #[serde(with = "crate::object_store::project_id::optional_project_id")]
733    pub project_id: Option<ProjectId>,
734    /// Mode, uid, gid, and rdev
735    pub posix_attributes: Option<PosixAttributesV32>,
736    /// The number of bytes allocated to all extents across all attributes for this object.
737    pub allocated_size: u64,
738    /// The timestamp at which the object was last read (i.e. atime).
739    pub access_time: TimestampV49,
740    /// The timestamp at which the object's status was last modified (i.e. ctime).
741    pub change_time: TimestampV49,
742}
743
744pub type ExtendedAttributeValue = ExtendedAttributeValueV32;
745
746#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, TypeFingerprint)]
747#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
748pub enum ExtendedAttributeValueV32 {
749    /// The extended attribute value is stored directly in this object. If the value is above a
750    /// certain size, it should be stored as an attribute with extents instead.
751    Inline(#[serde(with = "crate::zerocopy_serialization")] Vec<u8>),
752    /// The extended attribute value is stored as an attribute with extents. The attribute id
753    /// should be chosen to be within the range of 64-512.
754    AttributeId(AttributeId),
755}
756
757/// Id and descriptor for a child entry.
758pub type ChildValue = ChildValueV32;
759
760#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, TypeFingerprint, Versioned)]
761#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
762pub struct ChildValueV32 {
763    /// The ID of the child object.
764    pub object_id: u64,
765    /// Describes the type of the child.
766    pub object_descriptor: ObjectDescriptorV32,
767}
768
769pub type RootDigest = RootDigestV33;
770
771#[derive(
772    Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, Deserialize, TypeFingerprint,
773)]
774#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
775pub enum RootDigestV33 {
776    Sha256([u8; 32]),
777    Sha512(#[serde(with = "crate::zerocopy_serialization")] Vec<u8>),
778}
779
780pub type FsverityMetadata = FsverityMetadataV50;
781
782#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, TypeFingerprint, Versioned)]
783#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
784pub enum FsverityMetadataV50 {
785    /// The root hash and salt.
786    Internal(RootDigestV33, #[serde(with = "crate::zerocopy_serialization")] Vec<u8>),
787    /// The root hash and salt are in a descriptor inside the merkle attribute.
788    F2fs(std::ops::Range<u64>),
789}
790
791pub type EncryptionKey = EncryptionKeyV56;
792pub type EncryptionKeyV56 = fxfs_crypto::EncryptionKey;
793
794pub type EncryptionKeys = EncryptionKeysV56;
795
796#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize, TypeFingerprint)]
797#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
798pub struct EncryptionKeysV56(Vec<(u64, EncryptionKeyV56)>);
799
800impl EncryptionKeys {
801    pub fn get(&self, id: u64) -> Option<&EncryptionKey> {
802        self.0.iter().find_map(|(i, key)| (*i == id).then_some(key))
803    }
804
805    pub fn insert(&mut self, id: u64, key: EncryptionKey) {
806        self.0.push((id, key))
807    }
808
809    pub fn remove(&mut self, id: u64) -> Option<EncryptionKey> {
810        if let Some(ix) = self.0.iter().position(|(k, _)| *k == id) {
811            Some(self.0.remove(ix).1)
812        } else {
813            None
814        }
815    }
816}
817
818impl From<EncryptionKeys> for BTreeMap<u64, WrappedKey> {
819    fn from(keys: EncryptionKeys) -> Self {
820        keys.0.into_iter().map(|(id, key)| (id, key.into())).collect()
821    }
822}
823
824impl From<Vec<(u64, EncryptionKey)>> for EncryptionKeys {
825    fn from(value: Vec<(u64, EncryptionKey)>) -> Self {
826        Self(value)
827    }
828}
829
830impl std::ops::Deref for EncryptionKeys {
831    type Target = Vec<(u64, EncryptionKey)>;
832    fn deref(&self) -> &Self::Target {
833        &self.0
834    }
835}
836
837/// ObjectValue is the value of an item in the object store.
838/// Note that the tree stores deltas on objects, so these values describe deltas. Unless specified
839/// otherwise, a value indicates an insert/replace mutation.
840pub type ObjectValue = ObjectValueV56;
841impl Value for ObjectValue {
842    const DELETED_MARKER: Self = Self::None;
843}
844
845#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, TypeFingerprint, Versioned)]
846#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
847pub enum ObjectValueV56 {
848    /// Some keys have no value (this often indicates a tombstone of some sort).  Records with this
849    /// value are always filtered when a major compaction is performed, so the meaning must be the
850    /// same as if the item was not present.
851    None,
852    /// Some keys have no value but need to differentiate between a present value and no value
853    /// (None) i.e. their value is really a boolean: None => false, Some => true.
854    Some,
855    /// The value for an ObjectKey::Object record.
856    Object { kind: ObjectKindV54, attributes: ObjectAttributesV49 },
857    /// Specifies encryption keys to use for an object.
858    Keys(EncryptionKeysV56),
859    /// An attribute associated with a file object. |size| is the size of the attribute in bytes.
860    Attribute { size: u64, has_overwrite_extents: bool },
861    /// An extent associated with an object.
862    Extent(ExtentValueV38),
863    /// A child of an object.
864    Child(ChildValue),
865    /// Graveyard entries can contain these entries which will cause a file that has extents beyond
866    /// EOF to be trimmed at mount time.  This is used in cases where shrinking a file can exceed
867    /// the bounds of a single transaction.
868    Trim,
869    /// Added to support tracking Project ID usage and limits.
870    BytesAndNodes { bytes: i64, nodes: i64 },
871    /// A value for an extended attribute. Either inline or a redirection to an attribute with
872    /// extents.
873    ExtendedAttribute(ExtendedAttributeValueV32),
874    /// An attribute associated with a verified file object. |size| is the size of the attribute
875    /// in bytes.
876    VerifiedAttribute { size: u64, fsverity_metadata: FsverityMetadataV50 },
877}
878
879/// Storage for a pair of related byte and node values.
880#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
881pub struct BytesAndNodes {
882    pub bytes: i64,
883    pub nodes: i64,
884}
885
886impl BytesAndNodes {
887    pub fn is_zero(&self) -> bool {
888        self.bytes == 0 && self.nodes == 0
889    }
890}
891
892impl std::ops::Add for BytesAndNodes {
893    type Output = Self;
894    fn add(self, rhs: Self) -> Self {
895        Self { bytes: self.bytes + rhs.bytes, nodes: self.nodes + rhs.nodes }
896    }
897}
898
899impl std::ops::AddAssign for BytesAndNodes {
900    fn add_assign(&mut self, rhs: Self) {
901        self.bytes += rhs.bytes;
902        self.nodes += rhs.nodes;
903    }
904}
905
906impl From<BytesAndNodes> for ObjectValue {
907    fn from(value: BytesAndNodes) -> Self {
908        ObjectValue::BytesAndNodes { bytes: value.bytes, nodes: value.nodes }
909    }
910}
911
912impl From<(i64, i64)> for BytesAndNodes {
913    fn from((bytes, nodes): (i64, i64)) -> Self {
914        Self { bytes, nodes }
915    }
916}
917
918impl From<BytesAndNodes> for (i64, i64) {
919    fn from(value: BytesAndNodes) -> Self {
920        (value.bytes, value.nodes)
921    }
922}
923
924impl ObjectValue {
925    /// Creates an ObjectValue for a file object.
926    pub fn file(
927        refs: u64,
928        allocated_size: u64,
929        creation_time: Timestamp,
930        modification_time: Timestamp,
931        access_time: Timestamp,
932        change_time: Timestamp,
933        project_id: Option<ProjectId>,
934        posix_attributes: Option<PosixAttributes>,
935    ) -> ObjectValue {
936        ObjectValue::Object {
937            kind: ObjectKind::File { refs },
938            attributes: ObjectAttributes {
939                creation_time,
940                modification_time,
941                project_id,
942                posix_attributes,
943                allocated_size,
944                access_time,
945                change_time,
946            },
947        }
948    }
949    pub fn keys(encryption_keys: EncryptionKeys) -> ObjectValue {
950        ObjectValue::Keys(encryption_keys)
951    }
952    /// Creates an ObjectValue for an object attribute.
953    pub fn attribute(size: u64, has_overwrite_extents: bool) -> ObjectValue {
954        ObjectValue::Attribute { size, has_overwrite_extents }
955    }
956    /// Creates an ObjectValue for an object attribute of a verified file.
957    pub fn verified_attribute(size: u64, fsverity_metadata: FsverityMetadata) -> ObjectValue {
958        ObjectValue::VerifiedAttribute { size, fsverity_metadata }
959    }
960    /// Creates an ObjectValue for an insertion/replacement of an object extent.
961    pub fn extent(device_offset: u64, key_id: u64) -> ObjectValue {
962        ObjectValue::Extent(ExtentValue::new_raw(device_offset, key_id))
963    }
964    /// Creates an ObjectValue for an insertion/replacement of an object extent.
965    pub fn extent_with_checksum(
966        device_offset: u64,
967        checksum: Checksums,
968        key_id: u64,
969    ) -> ObjectValue {
970        ObjectValue::Extent(ExtentValue::with_checksum(device_offset, checksum, key_id))
971    }
972    /// Creates an ObjectValue for a deletion of an object extent.
973    pub fn deleted_extent() -> ObjectValue {
974        ObjectValue::Extent(ExtentValue::deleted_extent())
975    }
976    /// Creates an ObjectValue for an object child.
977    pub fn child(object_id: u64, object_descriptor: ObjectDescriptor) -> ObjectValue {
978        ObjectValue::Child(ChildValue { object_id, object_descriptor })
979    }
980    /// Creates an ObjectValue for an object symlink.
981    pub fn symlink(
982        link: impl Into<Box<[u8]>>,
983        creation_time: Timestamp,
984        modification_time: Timestamp,
985        project_id: Option<ProjectId>,
986    ) -> ObjectValue {
987        ObjectValue::Object {
988            kind: ObjectKind::Symlink { refs: 1, link: link.into() },
989            attributes: ObjectAttributes {
990                creation_time,
991                modification_time,
992                project_id,
993                ..Default::default()
994            },
995        }
996    }
997    /// Creates an ObjectValue for an encrypted symlink object.
998    pub fn encrypted_symlink(
999        link: impl Into<Box<[u8]>>,
1000        creation_time: Timestamp,
1001        modification_time: Timestamp,
1002        project_id: Option<ProjectId>,
1003    ) -> ObjectValue {
1004        ObjectValue::Object {
1005            kind: ObjectKind::EncryptedSymlink { refs: 1, link: link.into() },
1006            attributes: ObjectAttributes {
1007                creation_time,
1008                modification_time,
1009                project_id,
1010                ..Default::default()
1011            },
1012        }
1013    }
1014    pub fn inline_extended_attribute(value: impl Into<Vec<u8>>) -> ObjectValue {
1015        ObjectValue::ExtendedAttribute(ExtendedAttributeValue::Inline(value.into()))
1016    }
1017    pub fn extended_attribute(attribute_id: AttributeId) -> ObjectValue {
1018        ObjectValue::ExtendedAttribute(ExtendedAttributeValue::AttributeId(attribute_id))
1019    }
1020}
1021
1022pub type ObjectItem = ObjectItemV56;
1023
1024pub type ObjectItemV56 = Item<ObjectKeyV54, ObjectValueV56>;
1025
1026impl ObjectItem {
1027    pub fn is_tombstone(&self) -> bool {
1028        matches!(
1029            self,
1030            Item {
1031                key: ObjectKey { data: ObjectKeyData::Object, .. },
1032                value: ObjectValue::None,
1033                ..
1034            }
1035        )
1036    }
1037}
1038
1039// If the given item describes an extent, unwraps it and returns the extent key/value.
1040impl<'a> From<ItemRef<'a, ObjectKey, ObjectValue>>
1041    for Option<(/*object-id*/ u64, AttributeId, &'a Extent, &'a ExtentValue)>
1042{
1043    fn from(item: ItemRef<'a, ObjectKey, ObjectValue>) -> Self {
1044        match item {
1045            ItemRef {
1046                key:
1047                    ObjectKey {
1048                        object_id,
1049                        data:
1050                            ObjectKeyData::Attribute(
1051                                attribute_id, //
1052                                AttributeKey::Extent(extent_key),
1053                            ),
1054                    },
1055                value: ObjectValue::Extent(extent_value),
1056                ..
1057            } => Some((*object_id, *attribute_id, extent_key, extent_value)),
1058            _ => None,
1059        }
1060    }
1061}
1062
1063pub type FxfsKey = FxfsKeyV49;
1064pub type FxfsKeyV49 = fxfs_crypto::FxfsKey;
1065
1066#[derive(
1067    Clone,
1068    Copy,
1069    PartialEq,
1070    Eq,
1071    PartialOrd,
1072    Ord,
1073    Debug,
1074    Serialize,
1075    Deserialize,
1076    Hash,
1077    SerializeKey,
1078    TypeFingerprint,
1079)]
1080#[cfg_attr(fuzz, derive(arbitrary::Arbitrary))]
1081#[repr(transparent)]
1082pub struct AttributeId(pub u64);
1083
1084impl AttributeId {
1085    /// The common case for extents which cover the data payload of an object.
1086    pub const DATA: Self = Self(0);
1087
1088    /// Contains a serialized and versioned `BlobMetadata` struct. Use [`BlobMetadata::read_from`]
1089    /// and [`BlobMetadata::write_to`] to access this attribute.
1090    pub const BLOB_METADATA: Self = Self(3);
1091
1092    /// Contains a serialized `BlobMetadataUnversioned` struct. This attribute may still exist on
1093    /// blobs but should no longer be written. Use `AttributeId::BLOB_METADATA` instead.
1094    pub const BLOB_MERKLE: Self = Self(1);
1095
1096    /// For fsverity files in Fxfs, we store the merkle tree of the verified file at a well-known
1097    /// attribute.
1098    pub const FSVERITY_MERKLE: Self = Self(2);
1099
1100    /// For storing an associated profile of paging activity.
1101    pub const PROFILE_RECORDING: Self = Self(4);
1102
1103    /// The range of fxfs attribute IDs which are reserved for extended attribute values. Whenever a
1104    /// new attribute is needed, the first unused ID will be chosen from this range. It's
1105    /// technically safe to change these values, but it has potential consequences - they are only
1106    /// used during ID selection, so any existing extended attributes keep their IDs, which means
1107    /// any past or present selected range here could potentially have used attributes unless they
1108    /// are explicitly migrated, which isn't currently done.
1109    pub const XATTR_RANGE_START: Self = Self(64);
1110    pub const XATTR_RANGE_END: Self = Self(512);
1111
1112    /// A semantic alias for the `0` attribute ID, indicating that it is being used as a starting
1113    /// point to iterate over all attributes rather than specifically looking up the primary data
1114    /// attribute [`AttributeId::DATA`].
1115    pub const SORTED_START: Self = Self(0);
1116
1117    /// An attribute ID to use in tests when no particular ID is necessary.
1118    #[cfg(test)]
1119    pub const TEST_ID: Self = Self(u64::MAX - 1000);
1120
1121    pub const fn raw(self) -> u64 {
1122        self.0
1123    }
1124
1125    /// Returns the current id + 1.
1126    pub const fn next(self) -> Self {
1127        Self(self.0 + 1)
1128    }
1129
1130    /// Returns true if the attribute ID is within the range of extended attributes.
1131    pub const fn is_xattr(self) -> bool {
1132        self.0 >= Self::XATTR_RANGE_START.0 && self.0 < Self::XATTR_RANGE_END.0
1133    }
1134}
1135
1136impl log::kv::ToValue for AttributeId {
1137    fn to_value(&self) -> log::kv::Value<'_> {
1138        log::kv::Value::from(self.0)
1139    }
1140}
1141
1142impl std::fmt::Display for AttributeId {
1143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1144        std::fmt::Display::fmt(&self.0, f)
1145    }
1146}
1147
1148#[cfg(test)]
1149mod tests {
1150    use super::{AttributeId, ObjectKey, ObjectKeyV54, TimestampV49};
1151    use crate::lsm_tree::types::{FuzzyHash as _, LayerKey};
1152    use crate::object_store::extent::MIN_BLOCK_SIZE;
1153    use std::ops::Add;
1154    use std::time::{Duration, SystemTime, UNIX_EPOCH};
1155
1156    // Smoke test to ensure hash stability for Fxfs objects.
1157    // If this test fails, the hash algorithm changed, and that won't do -- Fxfs relies on stable
1158    // hash values, and existing images will appear to be corrupt if they change (see
1159    // https://fxbug.dev/419133532).
1160    #[test]
1161    fn test_hash_stability() {
1162        // Target a specific version of ObjectKey.  If you want to delete ObjectKeyV54, simply
1163        // update this test with a later key version, which will also require re-generating the
1164        // hashes.
1165        assert_eq!(
1166            &ObjectKeyV54::object(100).fuzzy_hash().collect::<Vec<_>>()[..],
1167            &[11885326717398844384]
1168        );
1169        assert_eq!(
1170            &ObjectKeyV54::extent(1, AttributeId::DATA, 0..2 * 1024 * 1024)
1171                .fuzzy_hash()
1172                .collect::<Vec<_>>()[..],
1173            &[11090579907097549012, 2814892992701560424]
1174        );
1175    }
1176
1177    #[test]
1178    fn test_fuzzy_hash_len() {
1179        let key = ObjectKey::object(100);
1180        let mut iter = key.fuzzy_hash();
1181        assert_eq!(iter.len(), 1);
1182        assert_eq!(iter.size_hint(), (1, Some(1)));
1183        assert!(iter.next().is_some());
1184        assert_eq!(iter.len(), 0);
1185        assert_eq!(iter.size_hint(), (0, Some(0)));
1186        assert_eq!(iter.next(), None);
1187
1188        let key = ObjectKey::extent(1, AttributeId::DATA, 0..2 * 1024 * 1024);
1189        let mut iter = key.fuzzy_hash();
1190        assert_eq!(iter.len(), 2);
1191        assert_eq!(iter.size_hint(), (2, Some(2)));
1192        assert!(iter.next().is_some());
1193        assert_eq!(iter.len(), 1);
1194        assert_eq!(iter.size_hint(), (1, Some(1)));
1195        assert!(iter.next().is_some());
1196        assert_eq!(iter.len(), 0);
1197        assert_eq!(iter.size_hint(), (0, Some(0)));
1198        assert_eq!(iter.next(), None);
1199    }
1200
1201    #[test]
1202    fn test_next_key() {
1203        assert_eq!(
1204            ObjectKey::extent(1, AttributeId::TEST_ID, 25 * MIN_BLOCK_SIZE..100 * MIN_BLOCK_SIZE)
1205                .next_key()
1206                .unwrap(),
1207            ObjectKey::extent(1, AttributeId::TEST_ID, 100 * MIN_BLOCK_SIZE..101 * MIN_BLOCK_SIZE)
1208        );
1209        assert_eq!(ObjectKey::object(100).next_key(), None);
1210    }
1211
1212    #[test]
1213    fn test_range_key() {
1214        const ATTR_ID: AttributeId = AttributeId::TEST_ID;
1215        // Make sure we disallow using extent keys with point queries. Other object keys should
1216        // still be allowed with point queries.
1217        assert!(ObjectKey::extent(1, ATTR_ID, 0..2 * 1024 * 1024).is_range_key());
1218        assert!(!ObjectKey::object(100).is_range_key());
1219
1220        assert_eq!(ObjectKey::object(1).overlaps(&ObjectKey::object(1)), true);
1221        assert_eq!(ObjectKey::object(1).overlaps(&ObjectKey::object(2)), false);
1222        assert_eq!(ObjectKey::extent(1, ATTR_ID, 0..100).overlaps(&ObjectKey::object(1)), false);
1223        assert_eq!(ObjectKey::object(1).overlaps(&ObjectKey::extent(1, ATTR_ID, 0..100)), false);
1224        assert_eq!(
1225            ObjectKey::extent(1, ATTR_ID, 0..100).overlaps(&ObjectKey::extent(2, ATTR_ID, 0..100)),
1226            false
1227        );
1228        assert_eq!(
1229            ObjectKey::extent(1, ATTR_ID, 0..100).overlaps(&ObjectKey::extent(
1230                1,
1231                ATTR_ID.next(),
1232                0..100
1233            )),
1234            false
1235        );
1236        assert_eq!(
1237            ObjectKey::extent(1, ATTR_ID, 0..100).overlaps(&ObjectKey::extent(1, ATTR_ID, 0..100)),
1238            true
1239        );
1240
1241        assert_eq!(
1242            ObjectKey::extent(1, ATTR_ID, 0..50).overlaps(&ObjectKey::extent(1, ATTR_ID, 49..100)),
1243            true
1244        );
1245        assert_eq!(
1246            ObjectKey::extent(1, ATTR_ID, 49..100).overlaps(&ObjectKey::extent(1, ATTR_ID, 0..50)),
1247            true
1248        );
1249
1250        assert_eq!(
1251            ObjectKey::extent(1, ATTR_ID, 0..50).overlaps(&ObjectKey::extent(1, ATTR_ID, 50..100)),
1252            false
1253        );
1254        assert_eq!(
1255            ObjectKey::extent(1, ATTR_ID, 50..100).overlaps(&ObjectKey::extent(1, ATTR_ID, 0..50)),
1256            false
1257        );
1258    }
1259
1260    #[test]
1261    fn test_timestamp() {
1262        fn compare_time(std_time: Duration) {
1263            let ts_time: TimestampV49 = std_time.into();
1264            assert_eq!(<TimestampV49 as Into<Duration>>::into(ts_time), std_time);
1265            assert_eq!(ts_time.subsec_nanos(), std_time.subsec_nanos());
1266            assert_eq!(ts_time.as_secs(), std_time.as_secs());
1267            assert_eq!(ts_time.as_nanos() as u128, std_time.as_nanos());
1268        }
1269        compare_time(Duration::from_nanos(0));
1270        compare_time(Duration::from_nanos(u64::MAX));
1271        compare_time(SystemTime::now().duration_since(UNIX_EPOCH).unwrap());
1272
1273        let ts: TimestampV49 = Duration::from_secs(u64::MAX - 1).into();
1274        assert_eq!(ts.nanos, u64::MAX);
1275
1276        let ts: TimestampV49 = (Duration::from_nanos(u64::MAX).add(Duration::from_nanos(1))).into();
1277        assert_eq!(ts.nanos, u64::MAX);
1278    }
1279
1280    #[test]
1281    fn test_legacy_fxfs_key_decoding_fails() {
1282        use fxfs_crypto::{EncryptionKey, FXFS_WRAPPED_KEY_SIZE, FxfsKey, WrappedKeyBytes};
1283
1284        let key = EncryptionKey::LegacyFxfs(FxfsKey {
1285            wrapping_key_id: [1; 16],
1286            key: WrappedKeyBytes([2; FXFS_WRAPPED_KEY_SIZE]),
1287        });
1288        let mut buf = Vec::new();
1289        bincode::serialize_into(&mut buf, &key).expect("serialize succeeds");
1290        assert!(bincode::deserialize::<EncryptionKey>(&buf).is_err());
1291    }
1292}