fix(mmap): pre-allocate temp file before mmapping (#50)

Fixes: https://github.com/zkat/cacache-rs/issues/48

This avoids SIGBUS on memory write in case the temp file is sparse.
Implemented for linux only; other target_os cfg values unchanged.
This commit is contained in:
Michal Trybus 2023-05-19 18:55:16 +02:00 committed by GitHub
parent 58de0b2d10
commit 1ac1d647e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 10 deletions

View File

@ -36,6 +36,9 @@ tokio = { version = "1.12.0", features = [
tokio-stream = { version = "0.1.7", features = ["io-util"], optional = true } tokio-stream = { version = "0.1.7", features = ["io-util"], optional = true }
walkdir = "2.3.2" walkdir = "2.3.2"
[target.'cfg(target_os = "linux")'.dependencies]
libc = { version = "0.2.144", optional = true }
[dev-dependencies] [dev-dependencies]
async-attributes = { version = "1.1.2" } async-attributes = { version = "1.1.2" }
criterion = "0.4.0" criterion = "0.4.0"
@ -54,6 +57,6 @@ harness = false
[features] [features]
default = ["async-std", "mmap"] default = ["async-std", "mmap"]
mmap = ["memmap2"] mmap = ["memmap2", "libc"]
link_to = [] link_to = []
tokio-runtime = ["tokio", "tokio-stream"] tokio-runtime = ["tokio", "tokio-stream"]

View File

@ -413,21 +413,44 @@ impl AsyncWriter {
#[cfg(feature = "mmap")] #[cfg(feature = "mmap")]
fn make_mmap(tmpfile: &mut NamedTempFile, size: Option<usize>) -> Result<Option<MmapMut>> { fn make_mmap(tmpfile: &mut NamedTempFile, size: Option<usize>) -> Result<Option<MmapMut>> {
if let Some(size @ 0..=MAX_MMAP_SIZE) = size { if let Some(size @ 0..=MAX_MMAP_SIZE) = size {
tmpfile allocate_file(tmpfile.as_file(), size).with_context(|| {
.as_file_mut() format!(
.set_len(size as u64) "Failed to configure file length for temp file at {}",
.with_context(|| { tmpfile.path().display()
format!( )
"Failed to configure file length for temp file at {}", })?;
tmpfile.path().display()
)
})?;
Ok(unsafe { MmapMut::map_mut(tmpfile.as_file()).ok() }) Ok(unsafe { MmapMut::map_mut(tmpfile.as_file()).ok() })
} else { } else {
Ok(None) Ok(None)
} }
} }
#[cfg(feature = "mmap")]
#[cfg(target_os = "linux")]
fn allocate_file(file: &std::fs::File, size: usize) -> std::io::Result<()> {
use std::io::{Error, ErrorKind};
use std::os::fd::AsRawFd;
let fd = file.as_raw_fd();
match unsafe { libc::posix_fallocate64(fd, 0, size as i64) } {
0 => Ok(()),
libc::ENOSPC => Err(Error::new(
ErrorKind::Other, // ErrorKind::StorageFull is unstable
"cannot allocate file: no space left on device",
)),
err => Err(Error::new(
ErrorKind::Other,
format!("posix_fallocate64 failed with code {err}"),
)),
}
}
#[cfg(feature = "mmap")]
#[cfg(not(target_os = "linux"))]
fn allocate_file(file: &std::fs::File, size: usize) -> std::io::Result<()> {
file.set_len(size as u64)
}
#[cfg(not(feature = "mmap"))] #[cfg(not(feature = "mmap"))]
fn make_mmap(_: &mut NamedTempFile, _: Option<usize>) -> Result<Option<MmapMut>> { fn make_mmap(_: &mut NamedTempFile, _: Option<usize>) -> Result<Option<MmapMut>> {
Ok(None) Ok(None)