From f0cb8a4520ec758b0cbaf5515b9c63fd6d7b4a9d Mon Sep 17 00:00:00 2001 From: Michal Trybus Date: Fri, 19 May 2023 00:09:53 +0200 Subject: [PATCH] fix(mmap): pre-allocate temp file before mmapping 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 | 1 + src/content/write.rs | 41 ++++++++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index db34ec5..a4ff6d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ digest = "0.10.6" either = "1.6.1" futures = "0.3.17" hex = "0.4.3" +libc = "0.2.144" memmap2 = { version = "0.5.8", optional = true } miette = "5.7.0" reflink = "0.1.3" 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)