3
3
// found in the LICENSE-BSD-3-Clause file.
4
4
5
5
use std:: cmp:: Ordering ;
6
- use std:: collections:: HashMap ;
7
6
use std:: ffi:: CStr ;
8
7
use std:: fmt:: { Debug , Formatter } ;
9
8
use std:: fs:: File ;
10
9
use std:: io;
11
10
use std:: os:: unix:: io:: { AsRawFd , FromRawFd , RawFd } ;
12
- use std:: sync:: { RwLock , RwLockReadGuard , RwLockWriteGuard } ;
11
+ use std:: sync:: Arc ;
13
12
14
- use vmm_sys_util:: {
15
- fam:: { FamStruct , FamStructWrapper } ,
16
- generate_fam_struct_impl,
17
- } ;
13
+ use vmm_sys_util:: fam:: { FamStruct , FamStructWrapper } ;
18
14
19
- use crate :: passthrough :: PassthroughFs ;
15
+ use super :: mount_fd :: { MountFd , MountFds , MountId } ;
20
16
21
17
/// An arbitrary maximum size for CFileHandle::f_handle.
22
18
///
@@ -64,7 +60,7 @@ pub struct CFileHandleInner {
64
60
pub f_handle : __IncompleteArrayField < libc:: c_char > ,
65
61
}
66
62
67
- generate_fam_struct_impl ! (
63
+ vmm_sys_util :: generate_fam_struct_impl!(
68
64
CFileHandleInner ,
69
65
libc:: c_char,
70
66
f_handle,
@@ -230,46 +226,70 @@ impl FileHandle {
230
226
handle : c_fh,
231
227
} )
232
228
} else {
233
- let e = io:: Error :: last_os_error ( ) ;
234
- Err ( e)
229
+ Err ( io:: Error :: last_os_error ( ) )
235
230
}
236
231
}
232
+ }
233
+
234
+ pub struct OpenableFileHandle {
235
+ handle : Arc < FileHandle > ,
236
+ mount_fd : Arc < MountFd > ,
237
+ }
238
+
239
+ impl Debug for OpenableFileHandle {
240
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
241
+ let fh = self . handle . handle . wrapper . as_fam_struct_ref ( ) ;
242
+ write ! (
243
+ f,
244
+ "Openable file handle: mountfd {}, type {}, len {}" ,
245
+ self . mount_fd. file( ) . as_raw_fd( ) ,
246
+ fh. handle_type,
247
+ fh. handle_bytes
248
+ )
249
+ }
250
+ }
237
251
252
+ impl OpenableFileHandle {
238
253
/// Create a file handle for the given file.
239
254
///
240
255
/// Also ensure that `mount_fds` contains a valid fd for the mount the file is on (so that
241
256
/// `handle.open_with_mount_fds()` will work).
242
257
///
243
258
/// If `path` is empty, `reopen_dir` may be invoked to duplicate `dir` with custom
244
259
/// `libc::open()` flags.
245
- pub fn from_name_at_with_mount_fds < F > (
260
+ pub fn from_name_at < F > (
246
261
dir_fd : RawFd ,
247
262
path : & CStr ,
248
263
mount_fds : & MountFds ,
249
264
reopen_dir : F ,
250
- ) -> io:: Result < Self >
265
+ ) -> io:: Result < OpenableFileHandle >
251
266
where
252
267
F : FnOnce ( RawFd , libc:: c_int , u32 ) -> io:: Result < File > ,
253
268
{
254
- let handle = Self :: from_name_at ( dir_fd, path) . map_err ( |e| {
269
+ let handle = FileHandle :: from_name_at ( dir_fd, path) . map_err ( |e| {
255
270
error ! ( "from_name_at failed error {:?}" , e) ;
256
271
e
257
272
} ) ?;
258
273
259
- mount_fds. ensure_mount_point ( handle. mnt_id , dir_fd, path, reopen_dir) ?;
274
+ let mount_fd = mount_fds
275
+ . get ( handle. mnt_id , reopen_dir)
276
+ . map_err ( |e| e. into_inner ( ) ) ?;
260
277
261
- Ok ( handle)
278
+ Ok ( OpenableFileHandle {
279
+ handle : Arc :: new ( handle) ,
280
+ mount_fd,
281
+ } )
262
282
}
263
283
264
284
/// Open a file handle (low-level wrapper).
265
285
///
266
286
/// `mount_fd` must be an open non-`O_PATH` file descriptor for an inode on the same mount as
267
287
/// the file to be opened, i.e. the mount given by `self.mnt_id`.
268
- fn open ( & self , mount_fd : & impl AsRawFd , flags : libc:: c_int ) -> io:: Result < File > {
288
+ pub fn open ( & self , flags : libc:: c_int ) -> io:: Result < File > {
269
289
let ret = unsafe {
270
290
open_by_handle_at (
271
- mount_fd. as_raw_fd ( ) ,
272
- self . handle . wrapper . as_fam_struct_ptr ( ) ,
291
+ self . mount_fd . file ( ) . as_raw_fd ( ) ,
292
+ self . handle . handle . wrapper . as_fam_struct_ptr ( ) ,
273
293
flags,
274
294
)
275
295
} ;
@@ -284,115 +304,12 @@ impl FileHandle {
284
304
}
285
305
}
286
306
287
- /// Open a file handle, using the given `mount_fds` hash map.
288
- ///
289
- /// Look up `self.mnt_id` in `mount_fds`, and pass the result to `self.open()`.
290
- pub fn open_with_mount_fds (
291
- & self ,
292
- mount_fds : & MountFds ,
293
- flags : libc:: c_int ,
294
- ) -> io:: Result < File > {
295
- let mount_fds_locked = mount_fds. get_map ( ) ;
296
- let mount_file = mount_fds_locked. get ( & self . mnt_id ) . ok_or_else ( || {
297
- error ! (
298
- "open_with_mount_fds: mnt_id {:?} is not found." ,
299
- & self . mnt_id
300
- ) ;
301
- io:: Error :: from_raw_os_error ( libc:: ENODEV )
302
- } ) ?;
303
-
304
- self . open ( mount_file, flags)
305
- }
306
- }
307
-
308
- /// Struct to maintain <mount ID, mountpoint file> mapping for open_by_handle_at().
309
- ///
310
- /// Creating a file handle only returns a mount ID; opening a file handle requires an open fd on the
311
- /// respective mount. This is a type in which we can store fds that we know are associated with a
312
- /// given mount ID, so that when opening a handle we can look it up.
313
- #[ derive( Default ) ]
314
- pub struct MountFds {
315
- pub ( crate ) map : RwLock < HashMap < u64 , File > > ,
316
- }
317
-
318
- impl MountFds {
319
- pub fn new ( ) -> Self {
320
- MountFds :: default ( )
321
- }
322
-
323
- pub fn get_map ( & self ) -> RwLockReadGuard < ' _ , HashMap < u64 , std:: fs:: File > > {
324
- self . map . read ( ) . unwrap ( )
325
- }
326
-
327
- pub fn get_map_mut ( & self ) -> RwLockWriteGuard < ' _ , HashMap < u64 , std:: fs:: File > > {
328
- self . map . write ( ) . unwrap ( )
307
+ pub fn mount_id ( & self ) -> MountId {
308
+ self . handle . mnt_id
329
309
}
330
310
331
- fn ensure_mount_point < F > (
332
- & self ,
333
- mnt_id : u64 ,
334
- dir_fd : RawFd ,
335
- path : & CStr ,
336
- reopen_dir : F ,
337
- ) -> io:: Result < ( ) >
338
- where
339
- F : FnOnce ( RawFd , libc:: c_int , u32 ) -> io:: Result < File > ,
340
- {
341
- if self . get_map ( ) . contains_key ( & mnt_id) {
342
- return Ok ( ( ) ) ;
343
- }
344
-
345
- let ( path_fd, _path_file) = if path. to_bytes ( ) . is_empty ( ) {
346
- // `open_by_handle_at()` needs a non-`O_PATH` fd, and `dir` may be `O_PATH`, so we
347
- // have to open a new fd here
348
- // We do not know whether `dir`/`path` is a special file, though, and we must not open
349
- // special files with anything but `O_PATH`, so we have to get some `O_PATH` fd first
350
- // that we can stat to find out whether it is safe to open.
351
- // (When opening a new fd here, keep a `File` object around so the fd is closed when it
352
- // goes out of scope.)
353
- ( dir_fd, None )
354
- } else {
355
- let f = PassthroughFs :: < ( ) > :: open_file (
356
- dir_fd,
357
- path,
358
- libc:: O_PATH | libc:: O_NOFOLLOW | libc:: O_CLOEXEC ,
359
- 0 ,
360
- )
361
- . map_err ( |e| {
362
- error ! (
363
- "from_name_at_with_mount_fds: open_file on {:?} failed error {:?}" ,
364
- path, e
365
- ) ;
366
- e
367
- } ) ?;
368
- ( f. as_raw_fd ( ) , Some ( f) )
369
- } ;
370
-
371
- // liubo: TODO find mnt id
372
- // Ensure that `file` refers to an inode with the mount ID we need
373
- // if statx(&file, None)?.mnt_id != handle.mnt_id {
374
- // return Err(io::Error::from_raw_os_error(libc::EIO));
375
- // }
376
-
377
- let st = PassthroughFs :: < ( ) > :: stat_fd ( path_fd, None ) ?;
378
- // Ensure that we can safely reopen `path_fd` with `O_RDONLY`
379
- let file_type = st. st_mode & libc:: S_IFMT ;
380
- if file_type != libc:: S_IFREG && file_type != libc:: S_IFDIR {
381
- error ! (
382
- "from_name_at_with_mount_fds: file {:?} is special file" ,
383
- path
384
- ) ;
385
- return Err ( io:: Error :: from_raw_os_error ( libc:: EIO ) ) ;
386
- }
387
-
388
- let file = reopen_dir (
389
- path_fd,
390
- libc:: O_RDONLY | libc:: O_NOFOLLOW | libc:: O_CLOEXEC ,
391
- st. st_mode ,
392
- ) ?;
393
- self . get_map_mut ( ) . insert ( mnt_id, file) ;
394
-
395
- Ok ( ( ) )
311
+ pub fn file_handle ( & self ) -> & Arc < FileHandle > {
312
+ & self . handle
396
313
}
397
314
}
398
315
0 commit comments