Files
asterinas/kernel/src/syscall/fallocate.rs
2025-01-09 10:58:16 +08:00

162 lines
5.7 KiB
Rust

// SPDX-License-Identifier: MPL-2.0
use super::SyscallReturn;
use crate::{
fs::{
file_table::{get_file_fast, FileDesc},
utils::FallocMode,
},
prelude::*,
process::ResourceType,
};
pub fn sys_fallocate(
fd: FileDesc,
mode: u64,
offset: i64,
len: i64,
ctx: &Context,
) -> Result<SyscallReturn> {
debug!(
"fd = {}, mode = {}, offset = {}, len = {}",
fd, mode, offset, len
);
check_offset_and_len(offset, len, ctx)?;
let mut file_table = ctx.thread_local.file_table().borrow_mut();
let file = get_file_fast!(&mut file_table, fd);
let falloc_mode = FallocMode::try_from(
RawFallocMode::from_bits(mode as _)
.ok_or_else(|| Error::with_message(Errno::EOPNOTSUPP, "invalid fallocate mode"))?,
)?;
file.fallocate(falloc_mode, offset as usize, len as usize)?;
Ok(SyscallReturn::Return(0))
}
fn check_offset_and_len(offset: i64, len: i64, ctx: &Context) -> Result<()> {
if offset < 0 || len <= 0 {
return_errno_with_message!(
Errno::EINVAL,
"offset is less than 0, or len is less than or equal to 0"
);
}
if offset.checked_add(len).is_none() {
return_errno_with_message!(Errno::EINVAL, "offset+len has overflowed");
}
let max_file_size = {
let resource_limits = ctx.process.resource_limits().lock();
resource_limits
.get_rlimit(ResourceType::RLIMIT_FSIZE)
.get_cur() as usize
};
if (offset + len) as usize > max_file_size {
return_errno_with_message!(Errno::EFBIG, "offset+len exceeds the maximum file size");
}
Ok(())
}
bitflags! {
/// Operation mode flags for fallocate.
///
/// These flags determine the operation to be performed on the given byte range.
struct RawFallocMode: u32 {
/// File size will not be changed when extending the file.
const FALLOC_FL_KEEP_SIZE = 0x01;
/// De-allocates a range (creates a hole).
///
/// Must be OR-ed with `FALLOC_FL_KEEP_SIZE`.
const FALLOC_FL_PUNCH_HOLE = 0x02;
/// Removes a range of a file without leaving a hole.
///
/// The offset and length must be multiples of the filesystem block size.
const FALLOC_FL_COLLAPSE_RANGE = 0x08;
/// Converts a range of a file to zeros.
///
/// Preallocates blocks within the range, converting to unwritten extents.
const FALLOC_FL_ZERO_RANGE = 0x10;
/// Inserts space within the file size without overwriting any existing data.
///
/// The offset and length must be multiples of the filesystem block size.
const FALLOC_FL_INSERT_RANGE = 0x20;
/// Unshares shared blocks within the file size without overwriting any existing data.
///
/// Guarantees that subsequent writes will not fail due to lack of space.
const FALLOC_FL_UNSHARE_RANGE = 0x40;
}
}
impl TryFrom<RawFallocMode> for FallocMode {
type Error = crate::error::Error;
fn try_from(raw_mode: RawFallocMode) -> Result<Self> {
// Check for invalid combinations of flags
if raw_mode.contains(RawFallocMode::FALLOC_FL_PUNCH_HOLE)
&& raw_mode.contains(RawFallocMode::FALLOC_FL_ZERO_RANGE)
{
return_errno_with_message!(
Errno::EOPNOTSUPP,
"PUNCH_HOLE and ZERO_RANGE cannot be used together"
);
}
if raw_mode.contains(RawFallocMode::FALLOC_FL_PUNCH_HOLE)
&& !raw_mode.contains(RawFallocMode::FALLOC_FL_KEEP_SIZE)
{
return_errno_with_message!(
Errno::EOPNOTSUPP,
"PUNCH_HOLE must be combined with KEEP_SIZE"
);
}
if raw_mode.contains(RawFallocMode::FALLOC_FL_COLLAPSE_RANGE)
&& !(raw_mode - RawFallocMode::FALLOC_FL_COLLAPSE_RANGE).is_empty()
{
return_errno_with_message!(
Errno::EINVAL,
"COLLAPSE_RANGE must be used exclusively without any other flags"
);
}
if raw_mode.contains(RawFallocMode::FALLOC_FL_INSERT_RANGE)
&& !(raw_mode - RawFallocMode::FALLOC_FL_INSERT_RANGE).is_empty()
{
return_errno_with_message!(
Errno::EINVAL,
"INSERT_RANGE must be used exclusively without any other flags"
);
}
if raw_mode.contains(RawFallocMode::FALLOC_FL_UNSHARE_RANGE)
&& !(raw_mode
- (RawFallocMode::FALLOC_FL_UNSHARE_RANGE | RawFallocMode::FALLOC_FL_KEEP_SIZE))
.is_empty()
{
return_errno_with_message!(
Errno::EINVAL,
"UNSHARE_RANGE can only be combined with KEEP_SIZE."
);
}
// Transform valid flags into the fallocate mode
let mode = if raw_mode.contains(RawFallocMode::FALLOC_FL_PUNCH_HOLE) {
FallocMode::PunchHoleKeepSize
} else if raw_mode.contains(RawFallocMode::FALLOC_FL_ZERO_RANGE) {
if raw_mode.contains(RawFallocMode::FALLOC_FL_KEEP_SIZE) {
FallocMode::ZeroRangeKeepSize
} else {
FallocMode::ZeroRange
}
} else if raw_mode.contains(RawFallocMode::FALLOC_FL_COLLAPSE_RANGE) {
FallocMode::CollapseRange
} else if raw_mode.contains(RawFallocMode::FALLOC_FL_INSERT_RANGE) {
FallocMode::InsertRange
} else if raw_mode.contains(RawFallocMode::FALLOC_FL_UNSHARE_RANGE) {
FallocMode::AllocateUnshareRange
} else if raw_mode.contains(RawFallocMode::FALLOC_FL_KEEP_SIZE) {
FallocMode::AllocateKeepSize
} else {
FallocMode::Allocate
};
Ok(mode)
}
}