Skip to content

Commit 6f2b655

Browse files
authored
Merge pull request #1826 from zyuiop/feat/fs-syscalls
feat: implement `cwd`, `umask`, `access`, `faccessat`, `ftruncate`, `fchmod`, `truncate`
2 parents 11432b0 + fa03443 commit 6f2b655

File tree

6 files changed

+464
-19
lines changed

6 files changed

+464
-19
lines changed

src/errno.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,12 @@ impl ToErrno for isize {
751751
}
752752
}
753753

754+
impl ToErrno for Errno {
755+
fn to_errno(&self) -> Option<i32> {
756+
Some(i32::from(*self))
757+
}
758+
}
759+
754760
impl ToErrno for u8 {}
755761
impl ToErrno for u16 {}
756762
impl ToErrno for u32 {}

src/fd/mod.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,52 @@ bitflags! {
6969
}
7070
}
7171

72+
bitflags! {
73+
/// Options for checking file permissions or existence
74+
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)]
75+
pub struct AccessOption: i32 {
76+
/// Test for read permission
77+
const R_OK = 4;
78+
/// Test for write permission
79+
const W_OK = 2;
80+
/// Test for execution permission
81+
const X_OK = 1;
82+
/// Test for existence
83+
const F_OK = 0;
84+
}
85+
}
86+
87+
impl AccessOption {
88+
/// Verifies if the current access options are all valid for the provided file access permissions
89+
pub fn can_access(&self, access_permissions: AccessPermission) -> bool {
90+
if self.contains(AccessOption::R_OK)
91+
&& !access_permissions.contains(AccessPermission::S_IRUSR)
92+
&& !access_permissions.contains(AccessPermission::S_IRGRP)
93+
&& !access_permissions.contains(AccessPermission::S_IROTH)
94+
{
95+
return false;
96+
}
97+
98+
if self.contains(AccessOption::W_OK)
99+
&& !access_permissions.contains(AccessPermission::S_IWUSR)
100+
&& !access_permissions.contains(AccessPermission::S_IWGRP)
101+
&& !access_permissions.contains(AccessPermission::S_IWOTH)
102+
{
103+
return false;
104+
}
105+
106+
if self.contains(AccessOption::X_OK)
107+
&& !access_permissions.contains(AccessPermission::S_IXUSR)
108+
&& !access_permissions.contains(AccessPermission::S_IXGRP)
109+
&& !access_permissions.contains(AccessPermission::S_IXOTH)
110+
{
111+
return false;
112+
}
113+
114+
true
115+
}
116+
}
117+
72118
bitflags! {
73119
/// File status flags.
74120
#[derive(Debug, Copy, Clone, Default)]
@@ -268,6 +314,16 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug {
268314
Err(Errno::Nosys)
269315
}
270316

317+
/// Truncates the file
318+
async fn truncate(&self, _size: usize) -> io::Result<()> {
319+
Err(Errno::Nosys)
320+
}
321+
322+
/// Changes access permissions to the file
323+
async fn chmod(&self, _access_permission: AccessPermission) -> io::Result<()> {
324+
Err(Errno::Nosys)
325+
}
326+
271327
/// `isatty` returns `true` for a terminal device
272328
async fn isatty(&self) -> io::Result<bool> {
273329
Ok(false)
@@ -290,6 +346,12 @@ pub(crate) fn lseek(fd: FileDescriptor, offset: isize, whence: SeekWhence) -> io
290346
block_on(obj.lseek(offset, whence), None)
291347
}
292348

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+
293355
pub(crate) fn write(fd: FileDescriptor, buf: &[u8]) -> io::Result<usize> {
294356
let obj = get_object(fd)?;
295357

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

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+
303370
async fn poll_fds(fds: &mut [PollFd]) -> io::Result<u64> {
304371
future::poll_fn(|cx| {
305372
let mut counter: u64 = 0;

src/fs/fuse.rs

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use crate::drivers::virtio::virtqueue::error::VirtqError;
2424
use crate::errno::Errno;
2525
use crate::executor::block_on;
2626
use crate::fd::PollEvent;
27+
use crate::fs::fuse::ops::SetAttrValidFields;
2728
use crate::fs::{
2829
self, AccessPermission, DirectoryEntry, FileAttr, NodeKind, ObjectInterface, OpenOption,
2930
SeekWhence, VfsNode,
@@ -63,11 +64,12 @@ pub(crate) mod ops {
6364
use alloc::boxed::Box;
6465
use alloc::ffi::CString;
6566

67+
use fuse_abi::linux;
6668
use fuse_abi::linux::*;
6769

6870
use super::Cmd;
6971
use crate::fd::PollEvent;
70-
use crate::fs::SeekWhence;
72+
use crate::fs::{FileAttr, SeekWhence};
7173

7274
#[repr(C)]
7375
#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq)]
@@ -270,6 +272,76 @@ pub(crate) mod ops {
270272
}
271273
}
272274

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

@@ -758,6 +830,23 @@ impl FuseFileHandleInner {
758830
Err(Errno::Io)
759831
}
760832
}
833+
834+
fn set_attr(&mut self, attr: FileAttr, valid: SetAttrValidFields) -> io::Result<FileAttr> {
835+
debug!("FUSE setattr");
836+
if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) {
837+
let (cmd, rsp_payload_len) = ops::Setattr::create(nid, fh, attr, valid);
838+
let rsp = get_filesystem_driver()
839+
.ok_or(Errno::Nosys)?
840+
.lock()
841+
.send_command(cmd, rsp_payload_len)?;
842+
if rsp.headers.out_header.error < 0 {
843+
return Err(Errno::Io);
844+
}
845+
Ok(rsp.headers.op_header.attr.into())
846+
} else {
847+
Err(Errno::Io)
848+
}
849+
}
761850
}
762851

763852
impl Drop for FuseFileHandleInner {
@@ -804,6 +893,32 @@ impl ObjectInterface for FuseFileHandle {
804893
async fn fstat(&self) -> io::Result<FileAttr> {
805894
self.0.lock().await.fstat()
806895
}
896+
897+
async fn truncate(&self, size: usize) -> io::Result<()> {
898+
let attr = FileAttr {
899+
st_size: size.try_into().unwrap(),
900+
..FileAttr::default()
901+
};
902+
903+
self.0
904+
.lock()
905+
.await
906+
.set_attr(attr, SetAttrValidFields::FATTR_SIZE)
907+
.map(|_| ())
908+
}
909+
910+
async fn chmod(&self, access_permission: AccessPermission) -> io::Result<()> {
911+
let attr = FileAttr {
912+
st_mode: access_permission,
913+
..FileAttr::default()
914+
};
915+
916+
self.0
917+
.lock()
918+
.await
919+
.set_attr(attr, SetAttrValidFields::FATTR_MODE)
920+
.map(|_| ())
921+
}
807922
}
808923

809924
impl Clone for FuseFileHandle {

src/fs/mem.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,19 @@ impl ObjectInterface for RamFileInterface {
256256
let guard = self.inner.read().await;
257257
Ok(guard.attr)
258258
}
259+
260+
async fn truncate(&self, size: usize) -> io::Result<()> {
261+
let mut guard = self.inner.write().await;
262+
guard.data.resize(size, 0);
263+
guard.attr.st_size = size as i64;
264+
Ok(())
265+
}
266+
267+
async fn chmod(&self, access_permission: AccessPermission) -> io::Result<()> {
268+
let mut guard = self.inner.write().await;
269+
guard.attr.st_mode = access_permission;
270+
Ok(())
271+
}
259272
}
260273

261274
impl RamFileInterface {

0 commit comments

Comments
 (0)