From 1ac1d647e0e25e1230bca376f9f294336dd57943 Mon Sep 17 00:00:00 2001 From: Michal Trybus Date: Fri, 19 May 2023 18:55:16 +0200 Subject: [PATCH] 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. --- Cargo.toml | 5 ++++- src/content/write.rs | 41 ++++++++++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index db34ec5..702a559 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,9 @@ tokio = { version = "1.12.0", features = [ tokio-stream = { version = "0.1.7", features = ["io-util"], optional = true } walkdir = "2.3.2" +[target.'cfg(target_os = "linux")'.dependencies] +libc = { version = "0.2.144", optional = true } + [dev-dependencies] async-attributes = { version = "1.1.2" } criterion = "0.4.0" @@ -54,6 +57,6 @@ harness = false [features] default = ["async-std", "mmap"] -mmap = ["memmap2"] +mmap = ["memmap2", "libc"] link_to = [] tokio-runtime = ["tokio", "tokio-stream"] diff --git a/src/content/write.rs b/src/content/write.rs index 41b7db3..595c269 100644 --- a/src/content/write.rs +++ b/src/content/write.rs @@ -413,21 +413,44 @@ impl AsyncWriter { #[cfg(feature = "mmap")] fn make_mmap(tmpfile: &mut NamedTempFile, size: Option) -> Result> { if let Some(size @ 0..=MAX_MMAP_SIZE) = size { - tmpfile - .as_file_mut() - .set_len(size as u64) - .with_context(|| { - format!( - "Failed to configure file length for temp file at {}", - tmpfile.path().display() - ) - })?; + allocate_file(tmpfile.as_file(), size).with_context(|| { + format!( + "Failed to configure file length for temp file at {}", + tmpfile.path().display() + ) + })?; Ok(unsafe { MmapMut::map_mut(tmpfile.as_file()).ok() }) } else { 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"))] fn make_mmap(_: &mut NamedTempFile, _: Option) -> Result> { Ok(None)