Skip to content

Commit

Permalink
[file_utils] Use stat64 explicitly to handle large files on 32-bit sy…
Browse files Browse the repository at this point in the history
…stems

The Rust libc lacks support for 64-bit file offset on 32-bit systems
(i.e., the _FILE_OFFSET_BITS=64 definition is not available and
libc::off_t is always 4-byte width on those systems). This means
libc::stat returns EOVERFLOW when it is called on files greater than
2GB. To avoid this issue, explicitly use libc::stat64 instead.
  • Loading branch information
mingnus committed Apr 22, 2023
1 parent 175f1ad commit 775c332
Showing 1 changed file with 31 additions and 22 deletions.
53 changes: 31 additions & 22 deletions src/file_utils.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,42 @@
use nix::sys::stat;
use nix::sys::stat::{FileStat, SFlag};
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Seek, Write};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::io::AsRawFd;
use std::path::Path;

//---------------------------------------

fn test_bit(mode: u32, flag: SFlag) -> bool {
SFlag::from_bits_truncate(mode).contains(flag)
fn test_bit(mode: u32, flag: u32) -> bool {
(mode & libc::S_IFMT) == flag
}

fn is_file_or_blk_(info: FileStat) -> bool {
test_bit(info.st_mode, SFlag::S_IFBLK) || test_bit(info.st_mode, SFlag::S_IFREG)
fn is_file_or_blk_(info: &libc::stat64) -> bool {
test_bit(info.st_mode, libc::S_IFBLK) || test_bit(info.st_mode, libc::S_IFREG)
}

// wrapper of libc::stat64
fn libc_stat64(path: &Path) -> io::Result<libc::stat64> {
let c_path = std::ffi::CString::new(path.as_os_str().as_bytes())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e.to_string()))?;

unsafe {
let mut st: libc::stat64 = std::mem::zeroed();
let r = libc::stat64(c_path.as_ptr(), &mut st);
if r == 0 {
Ok(st)
} else {
Err(io::Error::last_os_error())
}
}
}

pub fn is_file_or_blk(path: &Path) -> io::Result<bool> {
stat::stat(path).map_or_else(|e| Err(e.into()), |info| Ok(is_file_or_blk_(info)))
libc_stat64(path).map(|info| is_file_or_blk_(&info))
}

pub fn is_file(path: &Path) -> io::Result<bool> {
stat::stat(path).map_or_else(
|e| Err(e.into()),
|info| Ok(test_bit(info.st_mode, SFlag::S_IFREG)),
)
libc_stat64(path).map(|info| test_bit(info.st_mode, libc::S_IFREG))
}

//---------------------------------------
Expand All @@ -51,18 +63,15 @@ fn get_device_size<P: AsRef<Path>>(path: P) -> io::Result<u64> {
}

pub fn file_size<P: AsRef<Path>>(path: P) -> io::Result<u64> {
match stat::stat(path.as_ref()) {
Ok(info) => {
if test_bit(info.st_mode, SFlag::S_IFREG) {
Ok(info.st_size as u64)
} else if test_bit(info.st_mode, SFlag::S_IFBLK) {
get_device_size(path)
} else {
fail("Not a block device or regular file")
}
libc_stat64(path.as_ref()).and_then(|info| {
if test_bit(info.st_mode, libc::S_IFREG) {
Ok(info.st_size as u64)
} else if test_bit(info.st_mode, libc::S_IFBLK) {
get_device_size(path)
} else {
fail("Not a block device or regular file")
}
Err(e) => Err(e.into()),
}
})
}

//---------------------------------------
Expand Down

0 comments on commit 775c332

Please sign in to comment.