Coverage Report

Created: 2024-10-16 07:58

/rust/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.6.2/src/unix.rs
Line
Count
Source (jump to first uncovered line)
1
extern crate libc;
2
3
use std::fs::File;
4
use std::mem::ManuallyDrop;
5
use std::os::unix::io::{FromRawFd, RawFd};
6
use std::sync::atomic::{AtomicUsize, Ordering};
7
use std::{io, ptr};
8
9
use crate::advice::Advice;
10
11
#[cfg(any(
12
    all(target_os = "linux", not(target_arch = "mips")),
13
    target_os = "freebsd",
14
    target_os = "android"
15
))]
16
const MAP_STACK: libc::c_int = libc::MAP_STACK;
17
18
#[cfg(not(any(
19
    all(target_os = "linux", not(target_arch = "mips")),
20
    target_os = "freebsd",
21
    target_os = "android"
22
)))]
23
const MAP_STACK: libc::c_int = 0;
24
25
#[cfg(any(target_os = "linux", target_os = "android"))]
26
const MAP_POPULATE: libc::c_int = libc::MAP_POPULATE;
27
28
#[cfg(not(any(target_os = "linux", target_os = "android")))]
29
const MAP_POPULATE: libc::c_int = 0;
30
31
pub struct MmapInner {
32
    ptr: *mut libc::c_void,
33
    len: usize,
34
}
35
36
impl MmapInner {
37
    /// Creates a new `MmapInner`.
38
    ///
39
    /// This is a thin wrapper around the `mmap` sytem call.
40
0
    fn new(
41
0
        len: usize,
42
0
        prot: libc::c_int,
43
0
        flags: libc::c_int,
44
0
        file: RawFd,
45
0
        offset: u64,
46
0
    ) -> io::Result<MmapInner> {
47
0
        let alignment = offset % page_size() as u64;
48
0
        let aligned_offset = offset - alignment;
49
0
        let aligned_len = len + alignment as usize;
50
0
51
0
        // `libc::mmap` does not support zero-size mappings. POSIX defines:
52
0
        //
53
0
        // https://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html
54
0
        // > If `len` is zero, `mmap()` shall fail and no mapping shall be established.
55
0
        //
56
0
        // So if we would create such a mapping, crate a one-byte mapping instead:
57
0
        let aligned_len = aligned_len.max(1);
58
0
59
0
        // Note that in that case `MmapInner::len` is still set to zero,
60
0
        // and `Mmap` will still dereferences to an empty slice.
61
0
        //
62
0
        // If this mapping is backed by an empty file, we create a mapping larger than the file.
63
0
        // This is unusual but well-defined. On the same man page, POSIX further defines:
64
0
        //
65
0
        // > The `mmap()` function can be used to map a region of memory that is larger
66
0
        // > than the current size of the object.
67
0
        //
68
0
        // (The object here is the file.)
69
0
        //
70
0
        // > Memory access within the mapping but beyond the current end of the underlying
71
0
        // > objects may result in SIGBUS signals being sent to the process. The reason for this
72
0
        // > is that the size of the object can be manipulated by other processes and can change
73
0
        // > at any moment. The implementation should tell the application that a memory reference
74
0
        // > is outside the object where this can be detected; otherwise, written data may be lost
75
0
        // > and read data may not reflect actual data in the object.
76
0
        //
77
0
        // Because `MmapInner::len` is not incremented, this increment of `aligned_len`
78
0
        // will not allow accesses past the end of the file and will not cause SIGBUS.
79
0
        //
80
0
        // (SIGBUS is still possible by mapping a non-empty file and then truncating it
81
0
        // to a shorter size, but that is unrelated to this handling of empty files.)
82
0
83
0
        unsafe {
84
0
            let ptr = libc::mmap(
85
0
                ptr::null_mut(),
86
0
                aligned_len as libc::size_t,
87
0
                prot,
88
0
                flags,
89
0
                file,
90
0
                aligned_offset as libc::off_t,
91
0
            );
92
0
93
0
            if ptr == libc::MAP_FAILED {
94
0
                Err(io::Error::last_os_error())
95
            } else {
96
0
                Ok(MmapInner {
97
0
                    ptr: ptr.offset(alignment as isize),
98
0
                    len,
99
0
                })
100
            }
101
        }
102
0
    }
103
104
0
    pub fn map(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result<MmapInner> {
105
0
        let populate = if populate { MAP_POPULATE } else { 0 };
106
0
        MmapInner::new(
107
0
            len,
108
0
            libc::PROT_READ,
109
0
            libc::MAP_SHARED | populate,
110
0
            file,
111
0
            offset,
112
0
        )
113
0
    }
114
115
0
    pub fn map_exec(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result<MmapInner> {
116
0
        let populate = if populate { MAP_POPULATE } else { 0 };
117
0
        MmapInner::new(
118
0
            len,
119
0
            libc::PROT_READ | libc::PROT_EXEC,
120
0
            libc::MAP_SHARED | populate,
121
0
            file,
122
0
            offset,
123
0
        )
124
0
    }
125
126
0
    pub fn map_mut(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result<MmapInner> {
127
0
        let populate = if populate { MAP_POPULATE } else { 0 };
128
0
        MmapInner::new(
129
0
            len,
130
0
            libc::PROT_READ | libc::PROT_WRITE,
131
0
            libc::MAP_SHARED | populate,
132
0
            file,
133
0
            offset,
134
0
        )
135
0
    }
136
137
0
    pub fn map_copy(len: usize, file: RawFd, offset: u64, populate: bool) -> io::Result<MmapInner> {
138
0
        let populate = if populate { MAP_POPULATE } else { 0 };
139
0
        MmapInner::new(
140
0
            len,
141
0
            libc::PROT_READ | libc::PROT_WRITE,
142
0
            libc::MAP_PRIVATE | populate,
143
0
            file,
144
0
            offset,
145
0
        )
146
0
    }
147
148
0
    pub fn map_copy_read_only(
149
0
        len: usize,
150
0
        file: RawFd,
151
0
        offset: u64,
152
0
        populate: bool,
153
0
    ) -> io::Result<MmapInner> {
154
0
        let populate = if populate { MAP_POPULATE } else { 0 };
155
0
        MmapInner::new(
156
0
            len,
157
0
            libc::PROT_READ,
158
0
            libc::MAP_PRIVATE | populate,
159
0
            file,
160
0
            offset,
161
0
        )
162
0
    }
163
164
    /// Open an anonymous memory map.
165
0
    pub fn map_anon(len: usize, stack: bool, populate: bool) -> io::Result<MmapInner> {
166
0
        let stack = if stack { MAP_STACK } else { 0 };
167
0
        let populate = if populate { MAP_POPULATE } else { 0 };
168
0
        MmapInner::new(
169
0
            len,
170
0
            libc::PROT_READ | libc::PROT_WRITE,
171
0
            libc::MAP_PRIVATE | libc::MAP_ANON | stack | populate,
172
0
            -1,
173
0
            0,
174
0
        )
175
0
    }
176
177
0
    pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> {
178
0
        let alignment = (self.ptr as usize + offset) % page_size();
179
0
        let offset = offset as isize - alignment as isize;
180
0
        let len = len + alignment;
181
0
        let result =
182
0
            unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_SYNC) };
183
0
        if result == 0 {
184
0
            Ok(())
185
        } else {
186
0
            Err(io::Error::last_os_error())
187
        }
188
0
    }
189
190
0
    pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> {
191
0
        let alignment = (self.ptr as usize + offset) % page_size();
192
0
        let offset = offset as isize - alignment as isize;
193
0
        let len = len + alignment;
194
0
        let result =
195
0
            unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_ASYNC) };
196
0
        if result == 0 {
197
0
            Ok(())
198
        } else {
199
0
            Err(io::Error::last_os_error())
200
        }
201
0
    }
202
203
0
    fn mprotect(&mut self, prot: libc::c_int) -> io::Result<()> {
204
0
        unsafe {
205
0
            let alignment = self.ptr as usize % page_size();
206
0
            let ptr = self.ptr.offset(-(alignment as isize));
207
0
            let len = self.len + alignment;
208
0
            let len = len.max(1);
209
0
            if libc::mprotect(ptr, len, prot) == 0 {
210
0
                Ok(())
211
            } else {
212
0
                Err(io::Error::last_os_error())
213
            }
214
        }
215
0
    }
216
217
0
    pub fn make_read_only(&mut self) -> io::Result<()> {
218
0
        self.mprotect(libc::PROT_READ)
219
0
    }
220
221
0
    pub fn make_exec(&mut self) -> io::Result<()> {
222
0
        self.mprotect(libc::PROT_READ | libc::PROT_EXEC)
223
0
    }
224
225
0
    pub fn make_mut(&mut self) -> io::Result<()> {
226
0
        self.mprotect(libc::PROT_READ | libc::PROT_WRITE)
227
0
    }
228
229
    #[inline]
230
0
    pub fn ptr(&self) -> *const u8 {
231
0
        self.ptr as *const u8
232
0
    }
Unexecuted instantiation: <memmap2::os::MmapInner>::ptr
Unexecuted instantiation: <memmap2::os::MmapInner>::ptr
Unexecuted instantiation: <memmap2::os::MmapInner>::ptr
Unexecuted instantiation: <memmap2::os::MmapInner>::ptr
233
234
    #[inline]
235
0
    pub fn mut_ptr(&mut self) -> *mut u8 {
236
0
        self.ptr as *mut u8
237
0
    }
238
239
    #[inline]
240
0
    pub fn len(&self) -> usize {
241
0
        self.len
242
0
    }
Unexecuted instantiation: <memmap2::os::MmapInner>::len
Unexecuted instantiation: <memmap2::os::MmapInner>::len
Unexecuted instantiation: <memmap2::os::MmapInner>::len
Unexecuted instantiation: <memmap2::os::MmapInner>::len
243
244
0
    pub fn advise(&self, advice: Advice, offset: usize, len: usize) -> io::Result<()> {
245
0
        let alignment = (self.ptr as usize + offset) % page_size();
246
0
        let offset = offset as isize - alignment as isize;
247
0
        let len = len + alignment;
248
0
        unsafe {
249
0
            if libc::madvise(self.ptr.offset(offset), len, advice as i32) != 0 {
250
0
                Err(io::Error::last_os_error())
251
            } else {
252
0
                Ok(())
253
            }
254
        }
255
0
    }
256
257
0
    pub fn lock(&self) -> io::Result<()> {
258
0
        unsafe {
259
0
            if libc::mlock(self.ptr, self.len) != 0 {
260
0
                Err(io::Error::last_os_error())
261
            } else {
262
0
                Ok(())
263
            }
264
        }
265
0
    }
266
267
0
    pub fn unlock(&self) -> io::Result<()> {
268
0
        unsafe {
269
0
            if libc::munlock(self.ptr, self.len) != 0 {
270
0
                Err(io::Error::last_os_error())
271
            } else {
272
0
                Ok(())
273
            }
274
        }
275
0
    }
276
}
277
278
impl Drop for MmapInner {
279
0
    fn drop(&mut self) {
280
0
        let alignment = self.ptr as usize % page_size();
281
0
        let len = self.len + alignment;
282
0
        let len = len.max(1);
283
0
        // Any errors during unmapping/closing are ignored as the only way
284
0
        // to report them would be through panicking which is highly discouraged
285
0
        // in Drop impls, c.f. https://github.com/rust-lang/lang-team/issues/97
286
0
        unsafe {
287
0
            let ptr = self.ptr.offset(-(alignment as isize));
288
0
            libc::munmap(ptr, len as libc::size_t);
289
0
        }
290
0
    }
291
}
292
293
unsafe impl Sync for MmapInner {}
294
unsafe impl Send for MmapInner {}
295
296
0
fn page_size() -> usize {
297
0
    static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
298
0
299
0
    match PAGE_SIZE.load(Ordering::Relaxed) {
300
        0 => {
301
0
            let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
302
0
303
0
            PAGE_SIZE.store(page_size, Ordering::Relaxed);
304
0
305
0
            page_size
306
        }
307
0
        page_size => page_size,
308
    }
309
0
}
310
311
0
pub fn file_len(file: RawFd) -> io::Result<u64> {
312
0
    // SAFETY: We must not close the passed-in fd by dropping the File we create,
313
0
    // we ensure this by immediately wrapping it in a ManuallyDrop.
314
0
    unsafe {
315
0
        let file = ManuallyDrop::new(File::from_raw_fd(file));
316
0
        Ok(file.metadata()?.len())
317
    }
318
0
}