4
4
use std:: collections:: BTreeMap ;
5
5
use std:: sync:: Arc ;
6
6
7
+ #[ cfg( target_os = "linux" ) ]
7
8
use super :: file_handle:: FileHandle ;
9
+ #[ cfg( target_os = "macos" ) ]
10
+ use super :: stat:: Stat ;
11
+ #[ cfg( target_os = "linux" ) ]
8
12
use super :: statx:: StatExt ;
9
- use super :: { Inode , InodeData , InodeHandle } ;
13
+ use super :: { InoT , Inode , InodeData , InodeHandle } ;
10
14
11
15
#[ derive( Clone , Copy , Default , PartialOrd , Ord , PartialEq , Eq , Debug ) ]
12
16
/// Identify an inode in `PassthroughFs` by `InodeId`.
13
17
pub struct InodeId {
14
- pub ino : libc :: ino64_t ,
18
+ pub ino : InoT ,
15
19
pub dev : libc:: dev_t ,
20
+ #[ cfg( target_os = "linux" ) ]
16
21
pub mnt : u64 ,
17
22
}
18
23
19
24
impl InodeId {
25
+ #[ cfg( target_os = "linux" ) ]
20
26
#[ inline]
21
27
pub ( super ) fn from_stat ( st : & StatExt ) -> Self {
22
28
InodeId {
@@ -25,12 +31,22 @@ impl InodeId {
25
31
mnt : st. mnt_id ,
26
32
}
27
33
}
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
+ }
28
43
}
29
44
30
45
#[ derive( Default ) ]
31
46
pub struct InodeStore {
32
47
data : BTreeMap < Inode , Arc < InodeData > > ,
33
48
by_id : BTreeMap < InodeId , Inode > ,
49
+ #[ cfg( target_os = "linux" ) ]
34
50
by_handle : BTreeMap < Arc < FileHandle > , Inode > ,
35
51
}
36
52
@@ -41,6 +57,7 @@ impl InodeStore {
41
57
/// will get lost.
42
58
pub fn insert ( & mut self , data : Arc < InodeData > ) {
43
59
self . by_id . insert ( data. id , data. inode ) ;
60
+ #[ cfg( target_os = "linux" ) ]
44
61
if let InodeHandle :: Handle ( handle) = & data. handle {
45
62
self . by_handle
46
63
. insert ( handle. file_handle ( ) . clone ( ) , data. inode ) ;
@@ -59,6 +76,7 @@ impl InodeStore {
59
76
}
60
77
61
78
if let Some ( data) = data. as_ref ( ) {
79
+ #[ cfg( target_os = "linux" ) ]
62
80
if let InodeHandle :: Handle ( handle) = & data. handle {
63
81
self . by_handle . remove ( handle. file_handle ( ) ) ;
64
82
}
@@ -69,6 +87,7 @@ impl InodeStore {
69
87
70
88
pub fn clear ( & mut self ) {
71
89
self . data . clear ( ) ;
90
+ #[ cfg( target_os = "linux" ) ]
72
91
self . by_handle . clear ( ) ;
73
92
self . by_id . clear ( ) ;
74
93
}
@@ -82,6 +101,7 @@ impl InodeStore {
82
101
self . get ( inode)
83
102
}
84
103
104
+ #[ cfg( target_os = "linux" ) ]
85
105
pub fn get_by_handle ( & self , handle : & FileHandle ) -> Option < & Arc < InodeData > > {
86
106
let inode = self . inode_by_handle ( handle) ?;
87
107
self . get ( inode)
@@ -91,6 +111,7 @@ impl InodeStore {
91
111
self . by_id . get ( id)
92
112
}
93
113
114
+ #[ cfg( target_os = "linux" ) ]
94
115
pub fn inode_by_handle ( & self , handle : & FileHandle ) -> Option < & Inode > {
95
116
self . by_handle . get ( handle)
96
117
}
@@ -105,8 +126,13 @@ mod test {
105
126
use std:: mem:: MaybeUninit ;
106
127
use std:: os:: unix:: io:: AsRawFd ;
107
128
use std:: sync:: atomic:: Ordering ;
129
+ #[ cfg( target_os = "macos" ) ]
130
+ use tempfile:: Builder ;
131
+ #[ cfg( target_os = "linux" ) ]
108
132
use vmm_sys_util:: tempfile:: TempFile ;
109
133
134
+ use stat:: stat;
135
+
110
136
impl PartialEq for InodeData {
111
137
fn eq ( & self , other : & Self ) -> bool {
112
138
if self . inode != other. inode
@@ -117,16 +143,26 @@ mod test {
117
143
return false ;
118
144
}
119
145
146
+ #[ cfg( target_os = "linux" ) ]
120
147
match ( & self . handle , & other. handle ) {
121
148
( InodeHandle :: File ( f1) , InodeHandle :: File ( f2) ) => f1. as_raw_fd ( ) == f2. as_raw_fd ( ) ,
122
149
( InodeHandle :: Handle ( h1) , InodeHandle :: Handle ( h2) ) => {
123
150
h1. file_handle ( ) == h2. file_handle ( )
124
151
}
125
152
_ => false ,
126
153
}
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
+ }
127
162
}
128
163
}
129
164
165
+ #[ cfg( target_os = "linux" ) ]
130
166
fn stat_fd ( fd : & impl AsRawFd ) -> io:: Result < libc:: stat64 > {
131
167
let mut st = MaybeUninit :: < libc:: stat64 > :: zeroed ( ) ;
132
168
let null_path = unsafe { CStr :: from_bytes_with_nul_unchecked ( b"\0 " ) } ;
@@ -148,6 +184,7 @@ mod test {
148
184
}
149
185
}
150
186
187
+ #[ cfg( target_os = "linux" ) ]
151
188
#[ test]
152
189
fn test_inode_store ( ) {
153
190
let mut m = InodeStore :: default ( ) ;
@@ -214,4 +251,65 @@ mod test {
214
251
assert ! ( m. get( & inode2) . is_none( ) ) ;
215
252
assert ! ( m. get_by_id( & id2) . is_none( ) ) ;
216
253
}
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
+ }
217
315
}
0 commit comments