44use std:: collections:: BTreeMap ;
55use std:: sync:: Arc ;
66
7+ #[ cfg( target_os = "linux" ) ]
78use super :: file_handle:: FileHandle ;
9+ #[ cfg( target_os = "macos" ) ]
10+ use super :: stat:: Stat ;
11+ #[ cfg( target_os = "linux" ) ]
812use super :: statx:: StatExt ;
9- use super :: { Inode , InodeData , InodeHandle } ;
13+ use super :: { InoT , Inode , InodeData , InodeHandle } ;
1014
1115#[ derive( Clone , Copy , Default , PartialOrd , Ord , PartialEq , Eq , Debug ) ]
1216/// Identify an inode in `PassthroughFs` by `InodeId`.
1317pub struct InodeId {
14- pub ino : libc :: ino64_t ,
18+ pub ino : InoT ,
1519 pub dev : libc:: dev_t ,
20+ #[ cfg( target_os = "linux" ) ]
1621 pub mnt : u64 ,
1722}
1823
1924impl InodeId {
25+ #[ cfg( target_os = "linux" ) ]
2026 #[ inline]
2127 pub ( super ) fn from_stat ( st : & StatExt ) -> Self {
2228 InodeId {
@@ -25,12 +31,22 @@ impl InodeId {
2531 mnt : st. mnt_id ,
2632 }
2733 }
34+
35+ #[ cfg( target_os = "macos" ) ]
36+ #[ inline]
37+ pub ( super ) fn from_stat ( st : & Stat ) -> Self {
38+ InodeId {
39+ ino : st. st . st_ino ,
40+ dev : st. st . st_dev ,
41+ }
42+ }
2843}
2944
3045#[ derive( Default ) ]
3146pub struct InodeStore {
3247 data : BTreeMap < Inode , Arc < InodeData > > ,
3348 by_id : BTreeMap < InodeId , Inode > ,
49+ #[ cfg( target_os = "linux" ) ]
3450 by_handle : BTreeMap < Arc < FileHandle > , Inode > ,
3551}
3652
@@ -41,6 +57,7 @@ impl InodeStore {
4157 /// will get lost.
4258 pub fn insert ( & mut self , data : Arc < InodeData > ) {
4359 self . by_id . insert ( data. id , data. inode ) ;
60+ #[ cfg( target_os = "linux" ) ]
4461 if let InodeHandle :: Handle ( handle) = & data. handle {
4562 self . by_handle
4663 . insert ( handle. file_handle ( ) . clone ( ) , data. inode ) ;
@@ -59,6 +76,7 @@ impl InodeStore {
5976 }
6077
6178 if let Some ( data) = data. as_ref ( ) {
79+ #[ cfg( target_os = "linux" ) ]
6280 if let InodeHandle :: Handle ( handle) = & data. handle {
6381 self . by_handle . remove ( handle. file_handle ( ) ) ;
6482 }
@@ -69,6 +87,7 @@ impl InodeStore {
6987
7088 pub fn clear ( & mut self ) {
7189 self . data . clear ( ) ;
90+ #[ cfg( target_os = "linux" ) ]
7291 self . by_handle . clear ( ) ;
7392 self . by_id . clear ( ) ;
7493 }
@@ -82,6 +101,7 @@ impl InodeStore {
82101 self . get ( inode)
83102 }
84103
104+ #[ cfg( target_os = "linux" ) ]
85105 pub fn get_by_handle ( & self , handle : & FileHandle ) -> Option < & Arc < InodeData > > {
86106 let inode = self . inode_by_handle ( handle) ?;
87107 self . get ( inode)
@@ -91,6 +111,7 @@ impl InodeStore {
91111 self . by_id . get ( id)
92112 }
93113
114+ #[ cfg( target_os = "linux" ) ]
94115 pub fn inode_by_handle ( & self , handle : & FileHandle ) -> Option < & Inode > {
95116 self . by_handle . get ( handle)
96117 }
@@ -105,8 +126,13 @@ mod test {
105126 use std:: mem:: MaybeUninit ;
106127 use std:: os:: unix:: io:: AsRawFd ;
107128 use std:: sync:: atomic:: Ordering ;
129+ #[ cfg( target_os = "macos" ) ]
130+ use tempfile:: Builder ;
131+ #[ cfg( target_os = "linux" ) ]
108132 use vmm_sys_util:: tempfile:: TempFile ;
109133
134+ use stat:: stat;
135+
110136 impl PartialEq for InodeData {
111137 fn eq ( & self , other : & Self ) -> bool {
112138 if self . inode != other. inode
@@ -117,16 +143,26 @@ mod test {
117143 return false ;
118144 }
119145
146+ #[ cfg( target_os = "linux" ) ]
120147 match ( & self . handle , & other. handle ) {
121148 ( InodeHandle :: File ( f1) , InodeHandle :: File ( f2) ) => f1. as_raw_fd ( ) == f2. as_raw_fd ( ) ,
122149 ( InodeHandle :: Handle ( h1) , InodeHandle :: Handle ( h2) ) => {
123150 h1. file_handle ( ) == h2. file_handle ( )
124151 }
125152 _ => false ,
126153 }
154+
155+ #[ cfg( target_os = "macos" ) ]
156+ match ( & self . handle , & other. handle ) {
157+ ( InodeHandle :: File ( f1, _) , InodeHandle :: File ( f2, _) ) => {
158+ f1. as_raw_fd ( ) == f2. as_raw_fd ( )
159+ }
160+ _ => false ,
161+ }
127162 }
128163 }
129164
165+ #[ cfg( target_os = "linux" ) ]
130166 fn stat_fd ( fd : & impl AsRawFd ) -> io:: Result < libc:: stat64 > {
131167 let mut st = MaybeUninit :: < libc:: stat64 > :: zeroed ( ) ;
132168 let null_path = unsafe { CStr :: from_bytes_with_nul_unchecked ( b"\0 " ) } ;
@@ -148,6 +184,7 @@ mod test {
148184 }
149185 }
150186
187+ #[ cfg( target_os = "linux" ) ]
151188 #[ test]
152189 fn test_inode_store ( ) {
153190 let mut m = InodeStore :: default ( ) ;
@@ -214,4 +251,65 @@ mod test {
214251 assert ! ( m. get( & inode2) . is_none( ) ) ;
215252 assert ! ( m. get_by_id( & id2) . is_none( ) ) ;
216253 }
254+
255+ #[ cfg( target_os = "macos" ) ]
256+ #[ test]
257+ fn test_inode_store ( ) {
258+ let mut m = InodeStore :: default ( ) ;
259+ let tmpfile1 = Builder :: new ( ) . tempfile ( ) . unwrap ( ) ;
260+ let tmpfile2 = Builder :: new ( ) . tempfile ( ) . unwrap ( ) ;
261+
262+ let inode1: Inode = 3 ;
263+ let inode2: Inode = 4 ;
264+ let inode_stat1 = stat ( tmpfile1. as_file ( ) ) . unwrap ( ) ;
265+ let inode_stat2 = stat ( tmpfile2. as_file ( ) ) . unwrap ( ) ;
266+ let id1 = InodeId :: from_stat ( & inode_stat1) ;
267+ let id2 = InodeId :: from_stat ( & inode_stat2) ;
268+ let cstr1 = CString :: new ( tmpfile1. path ( ) . to_string_lossy ( ) . to_string ( ) ) . unwrap ( ) ;
269+ let cstr2 = CString :: new ( tmpfile2. path ( ) . to_string_lossy ( ) . to_string ( ) ) . unwrap ( ) ;
270+ let file_or_handle1 = InodeHandle :: File ( tmpfile1. into_file ( ) , cstr1) ;
271+ let file_or_handle2 = InodeHandle :: File ( tmpfile2. into_file ( ) , cstr2) ;
272+ let data1 = InodeData :: new ( inode1, file_or_handle1, 2 , id1, inode_stat1. st . st_mode ) ;
273+ let data2 = InodeData :: new ( inode2, file_or_handle2, 2 , id2, inode_stat2. st . st_mode ) ;
274+ let data1 = Arc :: new ( data1) ;
275+ let data2 = Arc :: new ( data2) ;
276+
277+ m. insert ( data1. clone ( ) ) ;
278+
279+ // get not present key, expect none
280+ assert ! ( m. get( & 1 ) . is_none( ) ) ;
281+
282+ // get just inserted value by key, by id, by handle
283+ assert ! ( m. get_by_id( & InodeId :: default ( ) ) . is_none( ) ) ;
284+ assert_eq ! ( m. get( & inode1) . unwrap( ) , & data1) ;
285+ assert_eq ! ( m. get_by_id( & id1) . unwrap( ) , & data1) ;
286+
287+ // insert another value, and check again
288+ m. insert ( data2. clone ( ) ) ;
289+ assert ! ( m. get( & 1 ) . is_none( ) ) ;
290+ assert ! ( m. get_by_id( & InodeId :: default ( ) ) . is_none( ) ) ;
291+ assert_eq ! ( m. get( & inode1) . unwrap( ) , & data1) ;
292+ assert_eq ! ( m. get_by_id( & id1) . unwrap( ) , & data1) ;
293+ assert_eq ! ( m. get( & inode2) . unwrap( ) , & data2) ;
294+ assert_eq ! ( m. get_by_id( & id2) . unwrap( ) , & data2) ;
295+
296+ // remove non-present key
297+ assert ! ( m. remove( & 1 , false ) . is_none( ) ) ;
298+
299+ // remove present key, return its value
300+ assert_eq ! ( m. remove( & inode1, false ) . unwrap( ) , data1. clone( ) ) ;
301+ assert ! ( m. get( & inode1) . is_none( ) ) ;
302+ assert ! ( m. get_by_id( & id1) . is_none( ) ) ;
303+ assert_eq ! ( m. get( & inode2) . unwrap( ) , & data2) ;
304+ assert_eq ! ( m. get_by_id( & id2) . unwrap( ) , & data2) ;
305+
306+ // clear the map
307+ m. clear ( ) ;
308+ assert ! ( m. get( & 1 ) . is_none( ) ) ;
309+ assert ! ( m. get_by_id( & InodeId :: default ( ) ) . is_none( ) ) ;
310+ assert ! ( m. get( & inode1) . is_none( ) ) ;
311+ assert ! ( m. get_by_id( & id1) . is_none( ) ) ;
312+ assert ! ( m. get( & inode2) . is_none( ) ) ;
313+ assert ! ( m. get_by_id( & id2) . is_none( ) ) ;
314+ }
217315}
0 commit comments