mapped_file/
lib.rs

1
2#[macro_use] extern crate lazy_static;
3
4use libc::{
5    mmap,
6    MAP_FAILED,
7};
8
9use std::{
10    os::unix::prelude::*,
11    ops,
12    mem,
13    ptr::{
14	self,
15	NonNull,
16    },
17    io,
18    fmt, error,
19    
20    borrow::{
21	Borrow, BorrowMut,
22    }
23};
24
25mod ffi;
26use ffi::c_try;
27
28
29pub mod hugetlb;
30//#[cfg(feature="file")]
31pub mod file;
32
33pub mod ring; //TODO
34use ring::buffer;
35
36mod ext; use ext::*;
37
38use hugetlb::{
39    HugePage,
40    MapHugeFlag,
41};
42
43mod uniq;
44use uniq::UniqueSlice;
45
46mod flags;
47pub use flags::*;
48
49pub mod err;
50use err::{
51    os_error,
52    opaque,
53};
54
55
56#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
57#[repr(transparent)]
58struct MappedSlice(UniqueSlice<u8>);
59
60unsafe impl Send for MappedSlice{}
61unsafe impl Sync for MappedSlice{}
62
63impl ops::Drop for MappedSlice
64{
65    #[inline]
66    fn drop(&mut self) 
67    {
68	unsafe {
69            libc::munmap(self.0.as_mut_ptr() as *mut _, self.0.len());
70	}
71    }
72}
73
74/// A memory mapping over file `T`.
75#[derive(Debug, PartialEq, Eq, Hash)]
76pub struct MappedFile<T>
77{
78    file: T,
79    map: MappedSlice,
80}
81#[inline(never)]
82#[cold]
83fn _panic_invalid_address() -> !
84{
85    panic!("Invalid/unsupported address returned from mmap()")
86}
87
88/// Get the current system page size
89pub fn get_page_size() -> usize
90{
91    use libc::c_int;
92    extern "C" {
93	fn getpagesize() -> c_int;
94    }
95    lazy_static! {
96	static ref PAGESZ: c_int = unsafe {
97	    getpagesize()
98	};
99    }
100    let v: c_int = *PAGESZ;
101    v as usize
102}
103
104impl<T> MappedFile<T> {
105    /// A reference to the mapped backing file
106    #[inline]
107    pub fn inner(&self) -> &T
108    {
109	&self.file
110    }
111    /// A mutable reference to the mapped backing file
112    ///
113    /// # Note
114    /// Behaviour of faulted or unfaulted pages is not specified if the backing file is modified another way. Likewise, if the backing file is read from another way, the data mapped is not guaranteed to have been synced unless a `flush()` has completed. Be careful with this.
115    #[inline]
116    pub fn inner_mut(&mut self) -> &mut T
117    {
118	&mut self.file
119    }
120
121    /// Unmap the memory contained in `T` and return it.
122    ///
123    /// # Warning
124    /// If the map is shared, or refers to a persistent file on disk, you should call `flush()`
125    /// first or use `into_inner_synced()`
126    #[inline] 
127    pub fn into_inner(self) -> T
128    {
129        drop(self.map);
130        self.file
131    }
132}
133
134impl<T: AsRawFd> MappedFile<T> {
135    /// Map the file `file` to `len` bytes with memory protection as provided by `perm`, and mapping flags provided by `flags`.
136    /// # Mapping flags
137    /// The trait `MapFlags` is used to allow user-defined configurations of `mmap()`, but the `Flags` enum should usually be used for this, or `()`, which behaves the same as `Flags::default()`.
138    ///
139    /// # Returns
140    /// If `mmap()` fails, then the current `errno` is returned alongside the `file` that was passed in, otherwise, a new mapping is
141    /// constructed over `file`, and that is returned.
142    ///
143    /// # Panics
144    /// If `mmap()` succeeds, but returns an invalid address (e.g. 0)
145    pub fn try_new(file: T, len: usize, perm: Perm, flags: impl flags::MapFlags) -> Result<Self, TryNewError<T>>
146    {
147	
148	const NULL: *mut libc::c_void = ptr::null_mut();
149        let fd = file.as_raw_fd();
150        let slice = match unsafe {
151            mmap(ptr::null_mut(), len, perm.get_prot(), flags.get_mmap_flags(), fd, 0)
152        } {
153            MAP_FAILED => return Err(TryNewError::wrap_last_error(file)),
154            NULL => _panic_invalid_address(),
155            ptr => unsafe {
156                UniqueSlice {
157                    mem: NonNull::new_unchecked(ptr as *mut u8),
158                    end: match NonNull::new((ptr as *mut u8).add(len)) {
159			Some(n) => n,
160			_ => _panic_invalid_address(),
161		    },
162                }
163            },
164        };
165        Ok(Self {
166            file,
167            map: MappedSlice(slice)
168        })
169    }
170
171    
172
173    /// Returns a dual mapping `(tx, rx)`, into the same file.
174    ///
175    /// This essentially creates s "sender" `tx`, and "receiver" `rx` mapping over the same data.
176    /// The sender is *write only*, and the receiver is *read only*.
177    ///
178    /// When **both** of the mappings have been uunmapped (when each `MappedFile<T>` has been dropped,) the inner value `file` is then dropped.
179    ///
180    /// # Sharing modes
181    /// `B` is used for the counter over the file handle `T`. Currently it can be
182    /// * `buffer::Shared` - A `Send`able mapping, use this for concurrent processing.
183    /// * `buffer::Private` - A `!Send` mapping, use this for when both returned maps are only used on the same thread that this function was called from.
184    ///
185    /// # Note
186    /// `len` **must** be a multiple of the used page size (see `get_page_size()`) (or hugepage size, if `flags` is set to use one) for this to work.
187    ///
188    /// # Panics
189    /// If the initial mapping fails, the file descriptor cannot be mapped, `len` was not a multiple of the correct page size, or if the fixed mappings fail. (see `try_shared()`.)
190    pub fn shared<B: buffer::TwoBufferProvider<T>>(file: T, len: usize, flags: impl flags::MapFlags) -> (MappedFile<B>, MappedFile<B>)
191    {
192	#[cold]
193	#[inline(never)]
194	fn _panic_failed_with(error: Box<io::Error>) -> !
195	{
196	    Err(error).expect("Failed to create shared mapping")
197	}
198	Self::try_shared(file, len, flags).unwrap_or_else(|e| {
199	    _panic_failed_with(e.error)
200	})
201    }
202    /// Returns a dual mapping `(tx, rx)`, into the same file.
203    ///
204    /// This essentially creates s "sender" `tx`, and "receiver" `rx` mapping over the same data.
205    /// The sender is *write only*, and the receiver is *read only*.
206    ///
207    /// When **both** of the mappings have been uunmapped (when each `MappedFile<T>` has been dropped,) the inner value `file` is then dropped.
208    ///
209    /// # Sharing modes
210    /// `B` is used for the counter over the file handle `T`. Currently it can be
211    /// * `buffer::Shared` - A `Send`able mapping, use this for concurrent processing.
212    /// * `buffer::Private` - A `!Send` mapping, use this for when both returned maps are only used on the same thread that this function was called from.
213    ///
214    /// # Note
215    /// `len` **must** be a multiple of the used page size (see `get_page_size()`) (or hugepage size, if `flags` is set to use one) for this to work.
216    pub fn try_shared<B: buffer::TwoBufferProvider<T>>(file: T, len: usize, flags: impl flags::MapFlags) -> Result<(MappedFile<B>, MappedFile<B>), TryNewError<T>>
217    {
218	Self::try_new_buffer_raw::<B>(file, len, None, false, flags)
219    }
220    //TODO: XXX: Test this when we have implemented memfd.
221    #[inline] 
222    pub(crate) fn try_new_buffer_raw<B: buffer::TwoBufferProvider<T>>(file: T, len: usize, rings: impl Into<Option<std::num::NonZeroUsize>>, allow_unsafe_writes: bool, flags: impl flags::MapFlags) -> Result<(MappedFile<B>, MappedFile<B>), TryNewError<T>>
223    {
224	const NULL: *mut libc::c_void = ptr::null_mut();
225
226	macro_rules! try_map_or {
227	    ($($tt:tt)*) => {
228		match unsafe {
229		    mmap($($tt)*)
230		} {
231		    MAP_FAILED => Err(io::Error::last_os_error()),
232		    NULL => _panic_invalid_address(),
233		    ptr => Ok(unsafe {
234			
235			UniqueSlice {
236			    mem: NonNull::new_unchecked(ptr as *mut u8),
237			    end: match NonNull::new((ptr as *mut u8).add(len)) {
238				Some(n) => n,
239				_ => _panic_invalid_address(),
240			    }
241			}
242		    })
243		}.map(MappedSlice)
244	    };
245	}
246	macro_rules! try_map {
247	    ($($tt:tt)*) => {
248		MappedSlice(match unsafe {
249		    mmap($($tt)*)
250		} {
251		    MAP_FAILED => return Err(TryNewError::wrap_last_error(file)),
252		    NULL => _panic_invalid_address(),
253		    ptr => unsafe {
254			
255			UniqueSlice {
256			    mem: NonNull::new_unchecked(ptr as *mut u8),
257			    end: match NonNull::new((ptr as *mut u8).add(len)) {
258				Some(n) => n,
259				_ => _panic_invalid_address(),
260			    }
261			}
262		    }
263		})
264	    };
265	}
266
267	macro_rules! unwrap {
268	    ($err:expr) => {
269		match $err {
270		    Ok(v) => v,
271		    Err(e) => return Err(TryNewError::wrap((e, file))),
272		}
273	    };
274	}
275	
276	let (prot_w, prot_r) = if allow_unsafe_writes {
277	    let p = Perm::ReadWrite.get_prot();
278	    (p, p)
279	} else {
280	    (Perm::Writeonly.get_prot(), Perm::Readonly.get_prot())
281	};
282	// Move into dual buffer
283
284	let (tx, rx) = match rings.into() {
285	    None => {
286		// No rings, just create two mappings at same addr.
287		let flags = flags.get_mmap_flags();
288		let mut root = try_map!(NULL, len * 2, libc::PROT_NONE, (flags & !libc::MAP_SHARED) | libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, -1, 0);
289		let rawfd = file.as_raw_fd();
290		
291		let rm = try_map!(root.0.as_mut_ptr().add(len) as *mut _, len, prot_r, flags | libc::MAP_FIXED, rawfd, 0); // Map reader at offset `len` from `root`.
292		let tm = try_map!(root.0.as_mut_ptr() as *mut _, len, prot_w, flags | libc::MAP_FIXED, rawfd, 0);  // Map writer at `root`, unmapping the anonymous map used to reserve the pages.
293
294		let tf = B::from_value(file);
295		let rf = B::from_wrapper(tf.as_wrapper());
296		(MappedFile {
297		    file: tf,
298		    map: tm
299		}, MappedFile {
300		    file: rf,
301		    map: rm,
302		})
303	    },
304	    Some(pages) => {
305		// Create anon mapping at full length
306		let full_len = unwrap!((len * 2)
307				       .checked_mul(pages.get())
308				       .ok_or_else(|| io::Error::new(io::ErrorKind::OutOfMemory,
309								     format!("Could not map {} pages of size {len}. Value would overflow", pages.get()))));
310		let flags = flags.get_mmap_flags();
311		let mut root = try_map!(NULL, full_len, libc::PROT_NONE, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, -1, 0);
312		let pivots = {
313		    let rawfd = file.as_raw_fd();
314		    let pivots: io::Result<Vec<_>> = std::iter::successors(unsafe { Some(root.0.as_mut_ptr().add(full_len - (len * 2))) }, |&x| unsafe { Some(x.sub(len * 2)) }) // Map in reverse, from end of `root`, and overwrite the `root` mapping last.
315			.take(pages.get())
316			.map(|base| {
317			    let rm = try_map_or!(base.add(len) as *mut _, len, prot_r, flags | libc::MAP_FIXED,rawfd, 0 )?;
318			    let tm = try_map_or!(base as *mut _, len, prot_w, flags | libc::MAP_FIXED, rawfd, 0)?;
319
320			    Ok((tm, rm))
321			})
322			.collect();
323		    unwrap!(pivots)
324		};
325		
326		    todo!("We can't carry `pivots` over to return from this function; the data is needed for unmapping the ring...");
327		todo!("The mapping we'd be using is `root`. But we need to unmap `pivots` in reverse order when the returned `MappedFile` is dropped...")
328	    }
329	};
330	Ok((tx, rx))
331    }    
332    /// Map the file `file` to `len` bytes with memory protection as provided by `perm`, and
333    /// mapping flags provided by `flags`.
334    ///
335    /// # Returns
336    /// If `mmap()` fails, then the current `errno` set by `mmap()` is returned, otherwise, a new mapping is
337    /// constructed over `file`, and that is returned.
338    /// If `mmap()` fails, `file` is dropped. To retain `file`, use `try_new()`.
339    ///
340    /// # Panics
341    /// If `mmap()` succeeds, but returns an invalid address (e.g. 0)
342    #[inline] 
343    pub fn new(file: T, len: usize, perm: Perm, flags: impl MapFlags) -> io::Result<Self>
344    {
345	Self::try_new(file, len, perm, flags).map_err(Into::into)
346    }
347
348    /// Sync the mapped memory to the backing file store via `msync()`.
349    ///
350    /// If this is a private mapping, or is mapped over a private file descriptor that does not refer to on-disk persistent storage, syncing the data is usually pointless.
351    ///
352    /// # Returns
353    /// If `msync()` fails.
354    pub fn flush(&mut self, flush: Flush) -> io::Result<()>
355    {
356        use libc::msync;
357        match unsafe {
358	    msync(self.map.0.as_mut_ptr() as *mut _, self.map.0.len(), flush.get_ms())
359	} {
360	    0 => Ok(()),
361	    _ => Err(io::Error::last_os_error())
362        }
363    }
364
365    /// Replace the mapped file object with another that aliases the same file descriptor.
366    ///
367    /// # Warning
368    /// * The old file object is *not* dropped to prevent the file descriptor being closed. (see `replace_inner_unchecked()`).
369    ///  If `T` contains other resources, this can cause a memory leak.
370    ///
371    /// # Panics
372    /// If `other`'s `AsRawFd` impl *does not* alias the already contained `T`'s.
373    pub fn replace_inner<U: AsRawFd>(self, other: U) -> MappedFile<U>
374    {
375        assert_eq!(self.file.as_raw_fd(), other.as_raw_fd(), "File descriptors must alias");
376	unsafe {
377	    let (this, file) = self.replace_inner_unchecked(other);
378	    mem::forget(file);
379	    this
380	}
381    }
382    
383    /// Unmap the memory contained in `T` and return it.
384    /// Before the memory is unmapped, it is `msync()`'d according to `flush`.
385    ///
386    /// # Panics
387    /// If `msync()` fails.
388    #[inline]
389    pub fn into_inner_synced(mut self, flush: Flush) -> T
390    {
391        self.flush(flush).expect("Failed to sync data");
392        drop(self.map);
393        self.file
394    }
395}
396
397impl<T> MappedFile<T> {
398    #[inline(always)]
399    fn raw_parts(&self) -> (*mut u8, usize)
400    {
401        (self.map.0.mem.as_ptr(), self.map.0.len())
402    } 
403
404    /// Set advise according to `adv`, and optionally advise the kernel on if the memory will be needed or not.
405    pub fn advise(&mut self, adv: Advice, needed: Option<bool>) -> io::Result<()>
406    {
407        use libc::{
408	    madvise,
409	    MADV_WILLNEED,
410	    MADV_DONTNEED
411        };
412        let (addr, len) = self.raw_parts();
413        match unsafe { madvise(addr as *mut _, len, adv.get_madv() | needed.map(|n| n.then(|| MADV_WILLNEED).unwrap_or(MADV_DONTNEED)).unwrap_or(0)) } {
414	    0 => Ok(()),
415	    _ => Err(io::Error::last_os_error())
416        }
417    }
418
419    /// With advice, used as a builder-pattern alternative for `advise()`.
420    ///
421    /// # Returns
422    /// If `madvise()` fails, then the `io::Error` along with the previous instance is returned.
423    #[inline(always)] 
424    pub fn try_with_advice(mut self, adv: Advice, needed: Option<bool>) -> Result<Self, TryNewError<Self>>
425    {
426        match self.advise(adv, needed) {
427	    Ok(_) => Ok(self),
428	    Err(error) => Err(TryNewError {
429		error: Box::new(error),
430		value: self,
431	    })
432	}
433    }
434
435    /// With advice, used as a builder-pattern alternative for `advise()`.
436    ///
437    /// # Returns
438    /// If `madvise()` fails, then the mapping is dropped and the error is returned. To keep the previous instance if the call failes, use `try_with_advice()`.
439    #[inline] 
440    pub fn with_advice(self, adv: Advice, needed: Option<bool>) -> io::Result<Self>
441    {
442	self.try_with_advice(adv, needed).map_err(Into::into)
443    }
444    
445    /// Replace the inner file with another without checking static or dynamic bounding.
446    /// This function is extremely unsafe if the following conditions are not met in entirity.
447    ///
448    /// # Safety
449    /// * `U` and `T` **must** have an `AsRawFd::as_raw_fd()` impl that returns the same `RawFd` value unconditionally.
450    /// * The returned `T` in the tuple **must not** attempt close the file descriptor while the returned `MappedFile<U>` in the tuple is alive.
451    /// * The returned values **should not** *both* attempt to close the file descriptor when dropped. To prevent the `MappedFile<U>` from attempting to close the file descriptor, use `MappedFile::into_inner()` and ensure `U` does not close the file descriptor while `T` is alive. Alternatively, use a mechanism of `T` to prevent it from closing the file descriptor while `U` is alive.
452    #[inline(always)]
453    pub unsafe fn replace_inner_unchecked<U>(self, other: U) -> (MappedFile<U>, T)
454    {
455        let MappedFile{ file, map } = self;
456        (MappedFile {
457	    file: other,
458	    map
459        }, file)
460    }
461
462    /// The size of the mapped memory
463    #[inline]
464    pub fn len(&self) -> usize
465    {
466        self.map.0.len()
467    }
468
469    /// Get a slice of the mapped memory
470    #[inline]
471    pub fn as_slice(&self) -> &[u8]
472    {
473        &self.map.0[..]
474    }
475
476    /// Get a mutable slice of the mapped memory
477    #[inline]
478    pub fn as_slice_mut(&mut self) -> &mut [u8]
479    {
480        &mut self.map.0[..]
481    }
482
483    /// Get a raw slice of the mapped memory
484    #[inline] 
485    pub fn as_raw_slice(&self) -> *const [u8]
486    {
487	self.map.0.as_raw_slice()
488    }
489
490    /// Get a raw mutable slice of the mapped memory
491    #[inline]
492    pub fn as_raw_slice_mut(&mut self) -> *mut [u8]
493    {
494	self.map.0.as_raw_slice_mut()
495    }
496
497    /// Checks if the mapping dangles (i.e. `len() == 0`.)
498    #[inline]
499    pub fn is_empty(&self) -> bool
500    {
501        self.map.0.is_empty()
502    }
503}
504
505/// Error returned when mapping operation fails.
506///
507/// Also returns the value passed in.
508pub struct TryNewError<T: ?Sized>
509{
510    error: Box<io::Error>,
511    value: T
512}
513
514impl<T:?Sized> error::Error for TryNewError<T>
515{
516    #[inline] 
517    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
518	Some(self.error.as_ref())
519    }
520}
521
522impl<T:?Sized> fmt::Display for TryNewError<T>
523{
524    #[inline] 
525    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
526    {
527	write!(f, "error in mapping of type {}", std::any::type_name::<T>())
528    }
529}
530
531impl<T:?Sized> fmt::Debug for TryNewError<T>
532{
533    #[inline]
534    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
535    {
536	f.debug_struct("TryNewError")
537	    .field("error", &self.error)
538	    .finish_non_exhaustive()
539    }
540}
541
542impl<T: ?Sized> TryNewError<T>
543{
544    /// A reference to the value
545    #[inline] 
546    pub fn value(&self) -> &T
547    {
548	&self.value
549    }
550    /// A mutable reference to the value
551    #[inline] 
552    pub fn value_mut(&mut self) -> &mut T
553    {
554	&mut self.value
555    }
556    /// A reference to the IO error
557    #[inline] 
558    pub fn error(&self) -> &io::Error
559    {
560	&self.error
561    }
562    /// Consume a boxed instance and return the boxed IO error.
563    #[inline] 
564    pub fn into_error_box(self: Box<Self>) -> Box<io::Error>
565    {
566	self.error
567    }
568}
569
570impl<T> TryNewError<T>
571{
572    #[inline] 
573    fn wrap_last_error(value: T) -> Self
574    {
575	Self {
576	    error: Box::new(io::Error::last_os_error()),
577	    value,
578	}
579    }
580
581    #[inline] 
582    fn wrap((error, value): (impl Into<io::Error>, T)) -> Self
583    {
584	Self {
585	    error: Box::new(error.into()),
586	    value
587	}
588    }
589    /// Consume into the contained value 
590    #[inline] 
591    pub fn into_inner(self) -> T
592    {
593	self.value
594    }
595
596    /// Consume into the IO error
597    #[inline] 
598    pub fn into_error(self) -> io::Error
599    {
600	*self.error
601    }
602    /// Consume into the value and the error.
603    #[inline] 
604    pub fn into_parts(self) -> (T, io::Error)
605    {
606	(self.value, *self.error)
607    }
608}
609
610impl<T: ?Sized> From<Box<TryNewError<T>>> for io::Error
611{
612    #[inline] 
613    fn from(from: Box<TryNewError<T>>) -> Self
614    {
615	*from.error
616    }
617}
618
619impl<T> From<TryNewError<T>> for io::Error
620{
621    #[inline] 
622    fn from(from: TryNewError<T>) -> Self
623    {
624	from.into_error()
625    }
626}
627
628impl<T: AsRawFd> Borrow<T> for MappedFile<T>
629{
630    #[inline]
631    fn borrow(&self) -> &T
632    {
633        &self.file
634    }
635}
636
637impl<T> Borrow<[u8]> for MappedFile<T>
638{
639    #[inline]
640    fn borrow(&self) -> &[u8]
641    {
642        self.as_slice()
643    }
644}
645
646impl<T> BorrowMut<[u8]> for MappedFile<T>
647{
648    #[inline]
649    fn borrow_mut(&mut self) -> &mut [u8]
650    {
651        self.as_slice_mut()
652    }
653}
654
655impl<T> AsRef<[u8]> for MappedFile<T>
656{
657    #[inline]
658    fn as_ref(&self) -> &[u8]
659    {
660	self.as_slice()
661    }
662}
663
664impl<T> AsMut<[u8]> for MappedFile<T>
665{
666    #[inline]
667    fn as_mut(&mut self) -> &mut [u8]
668    {
669	self.as_slice_mut()
670    }
671}
672
673impl<T> ops::Deref for MappedFile<T>
674{
675    type Target=  [u8];
676    #[inline]
677    fn deref(&self) -> &Self::Target
678    {
679        self.as_slice()
680    }
681}
682impl<T> ops::DerefMut for MappedFile<T>
683{
684    #[inline]
685    fn deref_mut(&mut self) -> &mut Self::Target
686    {
687        self.as_slice_mut()
688    }
689}
690
691/// Used for anonymous mappings with `MappedFile`.
692///
693/// # Safety
694/// The `AsRawFd` impl of this structure always returns `-1`. It should only be used with `MappedFile`, as this is an invlalid file descriptor in all other contexts.
695#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
696pub struct Anonymous;
697
698impl AsRawFd for Anonymous
699{
700    #[inline(always)] 
701    fn as_raw_fd(&self) -> RawFd {
702	-1
703    }
704}
705
706//TODO: Continue copying from `utf8encode` at the //TODO (cont.) line
707