mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-18 12:06:43 +00:00
162 lines
5.7 KiB
Rust
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)
|
|
}
|
|
}
|