Skip to content

Commit 1ac1d64

Browse files
authored
fix(mmap): pre-allocate temp file before mmapping (#50)
Fixes: #48 This avoids SIGBUS on memory write in case the temp file is sparse. Implemented for linux only; other target_os cfg values unchanged.
1 parent 58de0b2 commit 1ac1d64

File tree

2 files changed

+36
-10
lines changed

2 files changed

+36
-10
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ tokio = { version = "1.12.0", features = [
3636
tokio-stream = { version = "0.1.7", features = ["io-util"], optional = true }
3737
walkdir = "2.3.2"
3838

39+
[target.'cfg(target_os = "linux")'.dependencies]
40+
libc = { version = "0.2.144", optional = true }
41+
3942
[dev-dependencies]
4043
async-attributes = { version = "1.1.2" }
4144
criterion = "0.4.0"
@@ -54,6 +57,6 @@ harness = false
5457

5558
[features]
5659
default = ["async-std", "mmap"]
57-
mmap = ["memmap2"]
60+
mmap = ["memmap2", "libc"]
5861
link_to = []
5962
tokio-runtime = ["tokio", "tokio-stream"]

src/content/write.rs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -413,21 +413,44 @@ impl AsyncWriter {
413413
#[cfg(feature = "mmap")]
414414
fn make_mmap(tmpfile: &mut NamedTempFile, size: Option<usize>) -> Result<Option<MmapMut>> {
415415
if let Some(size @ 0..=MAX_MMAP_SIZE) = size {
416-
tmpfile
417-
.as_file_mut()
418-
.set_len(size as u64)
419-
.with_context(|| {
420-
format!(
421-
"Failed to configure file length for temp file at {}",
422-
tmpfile.path().display()
423-
)
424-
})?;
416+
allocate_file(tmpfile.as_file(), size).with_context(|| {
417+
format!(
418+
"Failed to configure file length for temp file at {}",
419+
tmpfile.path().display()
420+
)
421+
})?;
425422
Ok(unsafe { MmapMut::map_mut(tmpfile.as_file()).ok() })
426423
} else {
427424
Ok(None)
428425
}
429426
}
430427

428+
#[cfg(feature = "mmap")]
429+
#[cfg(target_os = "linux")]
430+
fn allocate_file(file: &std::fs::File, size: usize) -> std::io::Result<()> {
431+
use std::io::{Error, ErrorKind};
432+
use std::os::fd::AsRawFd;
433+
434+
let fd = file.as_raw_fd();
435+
match unsafe { libc::posix_fallocate64(fd, 0, size as i64) } {
436+
0 => Ok(()),
437+
libc::ENOSPC => Err(Error::new(
438+
ErrorKind::Other, // ErrorKind::StorageFull is unstable
439+
"cannot allocate file: no space left on device",
440+
)),
441+
err => Err(Error::new(
442+
ErrorKind::Other,
443+
format!("posix_fallocate64 failed with code {err}"),
444+
)),
445+
}
446+
}
447+
448+
#[cfg(feature = "mmap")]
449+
#[cfg(not(target_os = "linux"))]
450+
fn allocate_file(file: &std::fs::File, size: usize) -> std::io::Result<()> {
451+
file.set_len(size as u64)
452+
}
453+
431454
#[cfg(not(feature = "mmap"))]
432455
fn make_mmap(_: &mut NamedTempFile, _: Option<usize>) -> Result<Option<MmapMut>> {
433456
Ok(None)

0 commit comments

Comments
 (0)