@@ -6,7 +6,8 @@ use std::{
66 ffi:: OsStr ,
77 fs:: { self , File , OpenOptions } ,
88 io,
9- os:: unix:: fs:: OpenOptionsExt as _,
9+ num:: NonZeroUsize ,
10+ os:: unix:: { ffi:: OsStrExt as _, fs:: OpenOptionsExt as _, io:: IntoRawFd as _} ,
1011 path:: PathBuf ,
1112} ;
1213
@@ -29,8 +30,8 @@ pub struct ShmKeeper {
2930/// [`map`](Self::map) can be called more than once; every call returns another
3031/// view of the same bytes. Drop the handle once the mappings exist.
3132pub struct ShmHandle {
32- file : File ,
33- size : usize ,
33+ file : fspy_nostd :: OwnedFd ,
34+ size : NonZeroUsize ,
3435}
3536
3637/// The mapped shared bytes.
@@ -53,13 +54,10 @@ pub struct Mapping {
5354///
5455/// Returns an error if the shared memory cannot be created or sized.
5556pub fn create ( size : usize ) -> io:: Result < ( ShmKeeper , ShmHandle ) > {
56- if size == 0 {
57- return Err ( io:: Error :: new (
58- io:: ErrorKind :: InvalidInput ,
59- "shared-memory size must be nonzero" ,
60- ) ) ;
61- }
62- let size_u64 = u64:: try_from ( size) . map_err ( |_| {
57+ let size = NonZeroUsize :: new ( size) . ok_or_else ( || {
58+ io:: Error :: new ( io:: ErrorKind :: InvalidInput , "shared-memory size must be nonzero" )
59+ } ) ?;
60+ let size_u64 = u64:: try_from ( size. get ( ) ) . map_err ( |_| {
6361 io:: Error :: new ( io:: ErrorKind :: InvalidInput , "shared-memory size exceeds u64" )
6462 } ) ?;
6563
@@ -81,6 +79,7 @@ pub fn create(size: usize) -> io::Result<(ShmKeeper, ShmHandle)> {
8179
8280 // Every byte reads as zero because the file is all holes.
8381 file. set_len ( size_u64) ?;
82+ let file = into_nostd_fd ( file) ;
8483
8584 Ok ( ( keeper, ShmHandle { file, size } ) )
8685}
@@ -95,20 +94,61 @@ pub fn create(size: usize) -> io::Result<(ShmKeeper, ShmHandle)> {
9594/// Returns an error if the shared memory is unavailable, which is the common
9695/// case once its keeper has been dropped.
9796pub fn open ( id : & OsStr ) -> io:: Result < ShmHandle > {
98- // Rust opens are `O_CLOEXEC`, so a traced process never leaks this
99- // descriptor.
100- let file = OpenOptions :: new ( ) . read ( true ) . write ( true ) . open ( id) ?;
97+ let file = open_file ( id) ?;
10198 // If another process shrinks the file before `map`, mapping fails. If it
10299 // resizes afterwards, nothing here touches the mapped pages. A concurrent
103100 // resize cannot make a mapping access invalid memory.
104- let size = usize:: try_from ( file. metadata ( ) ?. len ( ) )
101+ let size = usize:: try_from ( fspy_nostd :: fs :: fstat ( & file) . map_err ( error_to_io ) ?. st_size )
105102 . map_err ( |_| io:: Error :: new ( io:: ErrorKind :: InvalidData , "invalid shared-memory size" ) ) ?;
106- if size == 0 {
107- return Err ( io:: Error :: new ( io:: ErrorKind :: InvalidData , "shared-memory size is zero" ) ) ;
108- }
103+ let size = NonZeroUsize :: new ( size)
104+ . ok_or_else ( || io:: Error :: new ( io:: ErrorKind :: InvalidData , "shared-memory size is zero" ) ) ?;
109105 Ok ( ShmHandle { file, size } )
110106}
111107
108+ fn open_file ( path : & OsStr ) -> io:: Result < fspy_nostd:: OwnedFd > {
109+ let mut buf = [ 0_u8 ; fspy_nostd:: fs:: PATH_MAX ] ;
110+ let path = copy_path ( path, & mut buf) ?;
111+ fspy_nostd:: fs:: openat (
112+ fspy_nostd:: CWD ,
113+ path,
114+ fspy_nostd:: fs:: OFlags :: RDWR | fspy_nostd:: fs:: OFlags :: CLOEXEC ,
115+ fspy_nostd:: fs:: Mode :: empty ( ) ,
116+ )
117+ . map_err ( error_to_io)
118+ }
119+
120+ fn copy_path < ' buf > (
121+ path : & OsStr ,
122+ buf : & ' buf mut [ u8 ; fspy_nostd:: fs:: PATH_MAX ] ,
123+ ) -> io:: Result < fspy_nostd:: CStr < ' buf , fspy_nostd:: Fat > > {
124+ let bytes = path. as_bytes ( ) ;
125+ if bytes. contains ( & 0 ) {
126+ return Err ( io:: Error :: new ( io:: ErrorKind :: InvalidInput , "path contains NUL" ) ) ;
127+ }
128+
129+ let len_with_nul = bytes. len ( ) . checked_add ( 1 ) . ok_or ( io:: ErrorKind :: InvalidInput ) ?;
130+ let initialized = buf. get_mut ( ..len_with_nul) . ok_or_else ( || {
131+ io:: Error :: from_raw_os_error ( fspy_nostd:: Error :: NAMETOOLONG . raw_os_error ( ) )
132+ } ) ?;
133+ initialized[ ..bytes. len ( ) ] . copy_from_slice ( bytes) ;
134+ initialized[ bytes. len ( ) ] = 0 ;
135+
136+ // SAFETY: the copied path contains no NUL, followed by the terminator set
137+ // above, and the returned view borrows the initialized buffer prefix.
138+ Ok ( unsafe { fspy_nostd:: CStr :: from_units_with_nul_unchecked ( initialized) } )
139+ }
140+
141+ fn error_to_io ( error : fspy_nostd:: Error ) -> io:: Error {
142+ io:: Error :: from_raw_os_error ( error. raw_os_error ( ) )
143+ }
144+
145+ fn into_nostd_fd ( file : File ) -> fspy_nostd:: OwnedFd {
146+ let fd = file. into_raw_fd ( ) ;
147+ // SAFETY: ownership of `file`'s descriptor transfers without closing or
148+ // duplicating it.
149+ unsafe { fspy_nostd:: FromRawFd :: from_raw_fd ( fd) }
150+ }
151+
112152impl Drop for ShmKeeper {
113153 fn drop ( & mut self ) {
114154 let _ = fs:: remove_file ( & self . path ) ;
@@ -131,7 +171,8 @@ impl ShmHandle {
131171 ///
132172 /// Returns an error if the mapping cannot be established.
133173 pub fn map ( & self ) -> io:: Result < Mapping > {
134- Ok ( Mapping { raw : MmapOptions :: new ( ) . len ( self . size ) . map_raw ( & self . file ) ? } )
174+ let file = fspy_nostd:: AsRawFd :: as_raw_fd ( & self . file ) ;
175+ Ok ( Mapping { raw : MmapOptions :: new ( ) . len ( self . size . get ( ) ) . map_raw ( file) ? } )
135176 }
136177}
137178
0 commit comments