Skip to content

Commit 9477828

Browse files
committed
feat: add ftruncate/fchmod/truncate syscalls
1 parent 141d352 commit 9477828

File tree

5 files changed

+190
-5
lines changed

5 files changed

+190
-5
lines changed

src/fd/mod.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,16 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug {
314314
Err(io::Error::ENOSYS)
315315
}
316316

317+
/// Truncates the file
318+
async fn truncate(&self, _size: usize) -> io::Result<()> {
319+
Err(io::Error::ENOSYS)
320+
}
321+
322+
/// Changes access permissions to the file
323+
async fn chmod(&self, _access_permission: AccessPermission) -> io::Result<()> {
324+
Err(io::Error::ENOSYS)
325+
}
326+
317327
/// `isatty` returns `true` for a terminal device
318328
async fn isatty(&self) -> io::Result<bool> {
319329
Ok(false)
@@ -336,6 +346,12 @@ pub(crate) fn lseek(fd: FileDescriptor, offset: isize, whence: SeekWhence) -> io
336346
block_on(obj.lseek(offset, whence), None)
337347
}
338348

349+
pub(crate) fn chmod(fd: FileDescriptor, mode: AccessPermission) -> io::Result<()> {
350+
let obj = get_object(fd)?;
351+
352+
block_on(obj.chmod(mode), None)
353+
}
354+
339355
pub(crate) fn write(fd: FileDescriptor, buf: &[u8]) -> io::Result<usize> {
340356
let obj = get_object(fd)?;
341357

@@ -346,6 +362,11 @@ pub(crate) fn write(fd: FileDescriptor, buf: &[u8]) -> io::Result<usize> {
346362
block_on(obj.write(buf), None)
347363
}
348364

365+
pub(crate) fn truncate(fd: FileDescriptor, length: usize) -> io::Result<()> {
366+
let obj = get_object(fd)?;
367+
block_on(obj.truncate(length), None)
368+
}
369+
349370
async fn poll_fds(fds: &mut [PollFd]) -> io::Result<u64> {
350371
future::poll_fn(|cx| {
351372
let mut counter: u64 = 0;

src/fs/fuse.rs

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ use crate::drivers::pci::get_filesystem_driver;
2222
use crate::drivers::virtio::virtqueue::error::VirtqError;
2323
use crate::executor::block_on;
2424
use crate::fd::PollEvent;
25+
use crate::fs::fuse::ops::SetAttrValidFields;
2526
use crate::fs::{
2627
self, AccessPermission, DirectoryEntry, FileAttr, NodeKind, ObjectInterface, OpenOption,
2728
SeekWhence, VfsNode,
2829
};
2930
use crate::mm::device_alloc::DeviceAlloc;
3031
use crate::time::{time_t, timespec};
3132
use crate::{arch, io};
32-
3333
// response out layout eg @ https://github.com/zargony/fuse-rs/blob/bf6d1cf03f3277e35b580f3c7b9999255d72ecf3/src/ll/request.rs#L44
3434
// op in/out sizes/layout: https://github.com/hanwen/go-fuse/blob/204b45dba899dfa147235c255908236d5fde2d32/fuse/opcode.go#L439
3535
// possible responses for command: qemu/tools/virtiofsd/fuse_lowlevel.h
@@ -64,7 +64,7 @@ pub(crate) mod ops {
6464

6565
use super::Cmd;
6666
use crate::fd::PollEvent;
67-
use crate::fs::SeekWhence;
67+
use crate::fs::{FileAttr, SeekWhence};
6868

6969
#[repr(C)]
7070
#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)]
@@ -267,6 +267,76 @@ pub(crate) mod ops {
267267
}
268268
}
269269

270+
#[derive(Debug)]
271+
pub(crate) struct Setattr;
272+
273+
impl Op for Setattr {
274+
const OP_CODE: fuse_opcode = fuse_opcode::FUSE_SETATTR;
275+
type InStruct = fuse_setattr_in;
276+
type InPayload = ();
277+
type OutStruct = fuse_attr_out;
278+
type OutPayload = ();
279+
}
280+
281+
bitflags! {
282+
#[derive(Debug, Copy, Clone, Default)]
283+
pub struct SetAttrValidFields: u32 {
284+
const FATTR_MODE = 1 << 0;
285+
const FATTR_UID = 1 << 1;
286+
const FATTR_GID = 1 << 2;
287+
const FATTR_SIZE = 1 << 3;
288+
const FATTR_ATIME = 1 << 4;
289+
const FATTR_MTIME = 1 << 5;
290+
const FATTR_FH = 1 << 6;
291+
const FATTR_ATIME_NOW = 1 << 7;
292+
const FATTR_MTIME_NOW = 1 << 8;
293+
const FATTR_LOCKOWNER = 1 << 9;
294+
const FATTR_CTIME = 1 << 10;
295+
const FATTR_KILL_SUIDGID = 1 << 11;
296+
}
297+
}
298+
299+
impl Setattr {
300+
pub(crate) fn create(
301+
nid: u64,
302+
fh: u64,
303+
attr: FileAttr,
304+
valid_attr: SetAttrValidFields,
305+
) -> (Cmd<Self>, u32) {
306+
let cmd = Cmd::new(
307+
nid,
308+
fuse_setattr_in {
309+
valid: valid_attr
310+
.difference(
311+
// Remove unsupported attributes
312+
SetAttrValidFields::FATTR_LOCKOWNER,
313+
)
314+
.bits(),
315+
padding: 0,
316+
fh,
317+
318+
// Fuse attributes mapping: https://github.com/libfuse/libfuse/blob/fc1c8da0cf8a18d222cb1feed0057ba44ea4d18f/lib/fuse_lowlevel.c#L105
319+
size: attr.st_size as u64,
320+
atime: attr.st_atim.tv_sec as u64,
321+
atimensec: attr.st_atim.tv_nsec as u32,
322+
mtime: attr.st_ctim.tv_sec as u64,
323+
mtimensec: attr.st_ctim.tv_nsec as u32,
324+
ctime: attr.st_ctim.tv_sec as u64,
325+
ctimensec: attr.st_ctim.tv_nsec as u32,
326+
mode: attr.st_mode.bits(),
327+
unused4: 0,
328+
uid: attr.st_uid,
329+
gid: attr.st_gid,
330+
unused5: 0,
331+
332+
lock_owner: 0, // unsupported
333+
},
334+
);
335+
336+
(cmd, 0)
337+
}
338+
}
339+
270340
#[derive(Debug)]
271341
pub(crate) struct Readlink;
272342

@@ -755,6 +825,23 @@ impl FuseFileHandleInner {
755825
Err(io::Error::EIO)
756826
}
757827
}
828+
829+
fn set_attr(&mut self, attr: FileAttr, valid: SetAttrValidFields) -> io::Result<FileAttr> {
830+
debug!("FUSE setattr");
831+
if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
832+
let (cmd, rsp_payload_len) = ops::Setattr::create(nid, fh, attr, valid);
833+
let rsp = get_filesystem_driver()
834+
.ok_or(io::Error::ENOSYS)?
835+
.lock()
836+
.send_command(cmd, rsp_payload_len)?;
837+
if rsp.headers.out_header.error < 0 {
838+
return Err(io::Error::EIO);
839+
}
840+
Ok(rsp.headers.op_header.attr.into())
841+
} else {
842+
Err(io::Error::EIO)
843+
}
844+
}
758845
}
759846

760847
impl Drop for FuseFileHandleInner {
@@ -801,6 +888,32 @@ impl ObjectInterface for FuseFileHandle {
801888
async fn fstat(&self) -> io::Result<FileAttr> {
802889
self.0.lock().await.fstat()
803890
}
891+
892+
async fn truncate(&self, size: usize) -> io::Result<()> {
893+
let attr = FileAttr {
894+
st_size: size.try_into().unwrap(),
895+
..FileAttr::default()
896+
};
897+
898+
self.0
899+
.lock()
900+
.await
901+
.set_attr(attr, SetAttrValidFields::FATTR_SIZE)
902+
.map(|_| ())
903+
}
904+
905+
async fn chmod(&self, access_permission: AccessPermission) -> io::Result<()> {
906+
let attr = FileAttr {
907+
st_mode: access_permission,
908+
..FileAttr::default()
909+
};
910+
911+
self.0
912+
.lock()
913+
.await
914+
.set_attr(attr, SetAttrValidFields::FATTR_MODE)
915+
.map(|_| ())
916+
}
804917
}
805918

806919
impl Clone for FuseFileHandle {

src/fs/mem.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,19 @@ impl ObjectInterface for RamFileInterface {
252252
let guard = self.inner.read().await;
253253
Ok(guard.attr)
254254
}
255+
256+
async fn truncate(&self, size: usize) -> io::Result<()> {
257+
let mut guard = self.inner.write().await;
258+
guard.data.resize(size, 0);
259+
guard.attr.st_size = size as i64;
260+
Ok(())
261+
}
262+
263+
async fn chmod(&self, access_permission: AccessPermission) -> io::Result<()> {
264+
let mut guard = self.inner.write().await;
265+
guard.attr.st_mode = access_permission;
266+
Ok(())
267+
}
255268
}
256269

257270
impl RamFileInterface {

src/fs/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use hermit_sync::{InterruptSpinMutex, OnceCell};
1414
use mem::MemDirectory;
1515
use num_enum::{IntoPrimitive, TryFromPrimitive};
1616

17+
use crate::executor::block_on;
1718
use crate::fd::{AccessPermission, ObjectInterface, OpenOption, insert_object, remove_object};
1819
use crate::io;
1920
use crate::io::Write;
@@ -440,6 +441,17 @@ where
440441
}
441442
}
442443

444+
pub fn truncate(name: &str, size: usize) -> io::Result<()> {
445+
with_relative_filename(name, |name| {
446+
let fs = FILESYSTEM.get().ok_or(io::Error::EINVAL)?;
447+
if let Ok(file) = fs.open(name, OpenOption::O_TRUNC, AccessPermission::empty()) {
448+
block_on(file.truncate(size), None)
449+
} else {
450+
Err(io::Error::EBADF)
451+
}
452+
})
453+
}
454+
443455
pub fn open(name: &str, flags: OpenOption, mode: AccessPermission) -> io::Result<FileDescriptor> {
444456
// mode is 0x777 (0b0111_0111_0111), when flags | O_CREAT, else 0
445457
// flags is bitmask of O_DEC_* defined above.

src/syscalls/mod.rs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -436,9 +436,6 @@ pub unsafe extern "C" fn sys_faccessat(
436436
#[hermit_macro::system]
437437
#[unsafe(no_mangle)]
438438
pub unsafe extern "C" fn sys_access(name: *const c_char, flags: i32) -> i32 {
439-
// This is an implementation of libc's access, not of linux's faccessat
440-
// See https://linux.die.net/man/2/faccessat for differences
441-
442439
let access_option = AccessOption::from_bits_truncate(flags);
443440
if access_option.bits() != flags {
444441
return -crate::errno::EINVAL;
@@ -466,6 +463,19 @@ pub unsafe extern "C" fn sys_access(name: *const c_char, flags: i32) -> i32 {
466463
}
467464
}
468465

466+
#[hermit_macro::system]
467+
#[unsafe(no_mangle)]
468+
pub unsafe extern "C" fn sys_fchmod(fd: FileDescriptor, mode: u32) -> i32 {
469+
let access_permission = AccessPermission::from_bits_truncate(mode);
470+
if access_permission.bits() != mode {
471+
return -crate::errno::EINVAL;
472+
}
473+
474+
crate::fd::chmod(fd, access_permission)
475+
.map(|()| 0)
476+
.unwrap_or_else(|e| -i32::from(e))
477+
}
478+
469479
#[hermit_macro::system]
470480
#[unsafe(no_mangle)]
471481
pub extern "C" fn sys_close(fd: FileDescriptor) -> i32 {
@@ -546,6 +556,22 @@ pub unsafe extern "C" fn sys_write(fd: FileDescriptor, buf: *const u8, len: usiz
546556
unsafe { write(fd, buf, len) }
547557
}
548558

559+
#[hermit_macro::system]
560+
#[unsafe(no_mangle)]
561+
pub unsafe extern "C" fn sys_ftruncate(fd: FileDescriptor, size: usize) -> i32 {
562+
crate::fd::truncate(fd, size).map_or_else(|e| -i32::from(e), |()| 0)
563+
}
564+
565+
#[hermit_macro::system]
566+
#[unsafe(no_mangle)]
567+
pub unsafe extern "C" fn sys_truncate(path: *const c_char, size: usize) -> i32 {
568+
let Ok(path) = unsafe { CStr::from_ptr(path) }.to_str() else {
569+
return -crate::errno::EINVAL;
570+
};
571+
572+
crate::fs::truncate(path, size).map_or_else(|e| -i32::from(e), |()| 0)
573+
}
574+
549575
/// `write()` attempts to write `nbyte` of data to the object referenced by the
550576
/// descriptor `fd` from a buffer. `writev()` performs the same
551577
/// action, but gathers the output data from the `iovcnt` buffers specified by the

0 commit comments

Comments
 (0)