1- use super :: { HardlinkList , Value } ;
2- use crate :: { hardlink:: LinkPathListReflection , inode:: InodeNumber } ;
1+ use super :: { HardlinkList , InodeKey , Value } ;
2+ use crate :: { device :: DeviceNumber , hardlink:: LinkPathListReflection , inode:: InodeNumber } ;
33use dashmap:: DashMap ;
44use derive_more:: { Display , Error , Into , IntoIterator } ;
55use into_sorted:: IntoSortedUnstable ;
@@ -12,8 +12,8 @@ use serde::{Deserialize, Serialize};
1212/// internal content.
1313///
1414/// **Guarantees:**
15- /// * Every inode number is unique.
16- /// * The internal list is always sorted by inode numbers .
15+ /// * Every pair of an inode number and a device number is unique.
16+ /// * The internal list is always sorted by pairs of an inode number and a device number .
1717///
1818/// **Equality:** `Reflection` implements `PartialEq` and `Eq` traits.
1919///
@@ -50,6 +50,8 @@ impl<Size> Reflection<Size> {
5050pub struct ReflectionEntry < Size > {
5151 /// The inode number of the file.
5252 pub ino : InodeNumber ,
53+ /// Device number of the filesystem the inode belongs to.
54+ pub dev : DeviceNumber ,
5355 /// Size of the file.
5456 pub size : Size ,
5557 /// Total number of links of the file, both listed (in [`Self::paths`]) and unlisted.
@@ -61,42 +63,55 @@ pub struct ReflectionEntry<Size> {
6163impl < Size > ReflectionEntry < Size > {
6264 /// Create a new entry.
6365 #[ inline]
64- fn new ( ino : InodeNumber , Value { size, links, paths } : Value < Size > ) -> Self {
66+ fn new ( InodeKey { ino, dev } : InodeKey , Value { size, links, paths } : Value < Size > ) -> Self {
6567 let paths = paths. into ( ) ;
6668 ReflectionEntry {
6769 ino,
70+ dev,
6871 size,
6972 links,
7073 paths,
7174 }
7275 }
7376
74- /// Dissolve [`ReflectionEntry`] into a pair of [`InodeNumber `] and [`Value`].
77+ /// Dissolve [`ReflectionEntry`] into a pair of [`InodeKey `] and [`Value`].
7578 #[ inline]
76- fn dissolve ( self ) -> ( InodeNumber , Value < Size > ) {
79+ fn dissolve ( self ) -> ( InodeKey , Value < Size > ) {
7780 let ReflectionEntry {
7881 ino,
82+ dev,
7983 size,
8084 links,
8185 paths,
8286 } = self ;
8387 let paths = paths. into ( ) ;
84- ( ino, Value { size, links, paths } )
88+ ( InodeKey { ino, dev } , Value { size, links, paths } )
89+ }
90+
91+ /// Sorting key to be used in the "sort by key" family of functions.
92+ ///
93+ /// Sort by the inode number first, then by the device number.
94+ ///
95+ /// This function returns a pair of 2 `u64`s instead of a pair of 2 wrapper
96+ /// types because we prefer them not to have to implement `Ord`.
97+ #[ inline]
98+ fn sorting_key ( & self ) -> ( u64 , u64 ) {
99+ ( u64:: from ( self . ino ) , u64:: from ( self . dev ) )
85100 }
86101}
87102
88103impl < Size > From < Vec < ReflectionEntry < Size > > > for Reflection < Size > {
89- /// Sort the list by inode numbers, then create the reflection.
104+ /// Sort the list by inode numbers and device numbers , then create the reflection.
90105 fn from ( list : Vec < ReflectionEntry < Size > > ) -> Self {
91- list. into_sorted_unstable_by_key ( |entry| u64 :: from ( entry . ino ) )
106+ list. into_sorted_unstable_by_key ( ReflectionEntry :: sorting_key )
92107 . pipe ( Reflection )
93108 }
94109}
95110
96111impl < Size > From < HardlinkList < Size > > for Reflection < Size > {
97112 fn from ( HardlinkList ( list) : HardlinkList < Size > ) -> Self {
98113 list. into_iter ( )
99- . map ( |( ino , value) | ReflectionEntry :: new ( ino , value) )
114+ . map ( |( key , value) | ReflectionEntry :: new ( key , value) )
100115 . collect :: < Vec < _ > > ( )
101116 . pipe ( Reflection :: from)
102117 }
@@ -107,9 +122,19 @@ impl<Size> From<HardlinkList<Size>> for Reflection<Size> {
107122#[ derive( Debug , Display , Error , Clone , Copy , PartialEq , Eq ) ]
108123#[ non_exhaustive]
109124pub enum ConversionError {
110- /// When the source has duplicated inode numbers.
111- #[ display( "Inode number {_0} is duplicated" ) ]
112- DuplicatedInode ( #[ error( not( source) ) ] InodeNumber ) ,
125+ /// When the source has a duplicated `(inode, device)` pair.
126+ #[ display( "Inode {_0} on device {_1} is duplicated" ) ]
127+ DuplicatedInode ( InodeNumber , DeviceNumber ) ,
128+ }
129+
130+ impl ConversionError {
131+ /// Convenient function to convert an [`InodeKey`] into a [`ConversionError::DuplicatedInode`].
132+ ///
133+ /// We don't embed [`InodeKey`] directly into [`ConversionError::DuplicatedInode`] because of
134+ /// their difference in visibility: One is private, the other public.
135+ fn duplicated_inode ( InodeKey { ino, dev } : InodeKey ) -> Self {
136+ ConversionError :: DuplicatedInode ( ino, dev)
137+ }
113138}
114139
115140impl < Size > TryFrom < Reflection < Size > > for HardlinkList < Size > {
@@ -118,9 +143,9 @@ impl<Size> TryFrom<Reflection<Size>> for HardlinkList<Size> {
118143 let map = DashMap :: with_capacity ( entries. len ( ) ) ;
119144
120145 for entry in entries {
121- let ( ino , value) = entry. dissolve ( ) ;
122- if map. insert ( ino , value) . is_some ( ) {
123- return ino . pipe ( ConversionError :: DuplicatedInode ) . pipe ( Err ) ;
146+ let ( key , value) = entry. dissolve ( ) ;
147+ if map. insert ( key , value) . is_some ( ) {
148+ return key . pipe ( ConversionError :: duplicated_inode ) . pipe ( Err ) ;
124149 }
125150 }
126151
0 commit comments