/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 | } |