From 551fb8f9d8384daeeab3518f8643b61f0537b814 Mon Sep 17 00:00:00 2001 From: LI Qing Date: Thu, 13 Feb 2020 04:54:14 +0000 Subject: [PATCH] Add a dummy implementation for file advisory locks This commits is a dummy implementation of file advisory locks. Specifically, for regular files, fcntl `F_SETLK` (i.e., acquiring or releasing locks) always succeeds and fcntl `F_GETLK` (i.e., testing locks) always returns no locks. --- src/libos/src/fs/file.rs | 8 +++ src/libos/src/fs/file_ops/fcntl.rs | 44 +++++++++++++-- src/libos/src/fs/file_ops/flock.rs | 88 ++++++++++++++++++++++++++++++ src/libos/src/fs/file_ops/mod.rs | 2 + src/libos/src/fs/inode_file.rs | 26 +++++++++ src/libos/src/fs/mod.rs | 1 + src/libos/src/fs/syscalls.rs | 4 +- test/fcntl/main.c | 31 +++++++++++ 8 files changed, 197 insertions(+), 7 deletions(-) create mode 100644 src/libos/src/fs/file_ops/flock.rs diff --git a/src/libos/src/fs/file.rs b/src/libos/src/fs/file.rs index 8fd69c6f..29b2e3c5 100644 --- a/src/libos/src/fs/file.rs +++ b/src/libos/src/fs/file.rs @@ -79,6 +79,14 @@ pub trait File: Debug + Sync + Send + Any { return_op_unsupported_error!("set_status_flags") } + fn test_advisory_lock(&self, lock: &mut Flock) -> Result<()> { + return_op_unsupported_error!("test_advisory_lock") + } + + fn set_advisory_lock(&self, lock: &Flock) -> Result<()> { + return_op_unsupported_error!("set_advisory_lock") + } + fn as_any(&self) -> &dyn Any; } diff --git a/src/libos/src/fs/file_ops/fcntl.rs b/src/libos/src/fs/file_ops/fcntl.rs index f9898764..dc619d15 100644 --- a/src/libos/src/fs/file_ops/fcntl.rs +++ b/src/libos/src/fs/file_ops/fcntl.rs @@ -1,7 +1,9 @@ +use super::flock::flock; use super::*; +use util::mem_util::from_user; #[derive(Debug)] -pub enum FcntlCmd { +pub enum FcntlCmd<'a> { /// Duplicate the file descriptor fd using the lowest-numbered available /// file descriptor greater than or equal to arg. DupFd(FileDesc), @@ -16,11 +18,15 @@ pub enum FcntlCmd { GetFl(), /// Set the file status flags SetFl(u32), + /// Test a file lock + GetLk(&'a mut flock), + /// Acquire or release a file lock + SetLk(&'a flock), } -impl FcntlCmd { +impl<'a> FcntlCmd<'a> { #[deny(unreachable_patterns)] - pub fn from_raw(cmd: u32, arg: u64) -> Result { + pub fn from_raw(cmd: u32, arg: u64) -> Result> { Ok(match cmd as c_int { libc::F_DUPFD => FcntlCmd::DupFd(arg as FileDesc), libc::F_DUPFD_CLOEXEC => FcntlCmd::DupFdCloexec(arg as FileDesc), @@ -28,12 +34,24 @@ impl FcntlCmd { libc::F_SETFD => FcntlCmd::SetFd(arg as u32), libc::F_GETFL => FcntlCmd::GetFl(), libc::F_SETFL => FcntlCmd::SetFl(arg as u32), + libc::F_GETLK => { + let flock_mut_ptr = arg as *mut flock; + from_user::check_mut_ptr(flock_mut_ptr)?; + let flock_mut_c = unsafe { &mut *flock_mut_ptr }; + FcntlCmd::GetLk(flock_mut_c) + } + libc::F_SETLK => { + let flock_ptr = arg as *const flock; + from_user::check_ptr(flock_ptr)?; + let flock_c = unsafe { &*flock_ptr }; + FcntlCmd::SetLk(flock_c) + } _ => return_errno!(EINVAL, "unsupported command"), }) } } -pub fn do_fcntl(fd: FileDesc, cmd: &FcntlCmd) -> Result { +pub fn do_fcntl(fd: FileDesc, cmd: &mut FcntlCmd) -> Result { info!("fcntl: fd: {:?}, cmd: {:?}", &fd, cmd); let current_ref = process::get_current(); let mut current = current_ref.lock().unwrap(); @@ -59,7 +77,7 @@ pub fn do_fcntl(fd: FileDesc, cmd: &FcntlCmd) -> Result { } FcntlCmd::SetFd(fd_flags) => { let entry = file_table.get_entry_mut(fd)?; - entry.set_close_on_spawn((fd_flags & libc::FD_CLOEXEC as u32) != 0); + entry.set_close_on_spawn((*fd_flags & libc::FD_CLOEXEC as u32) != 0); 0 } FcntlCmd::GetFl() => { @@ -74,6 +92,22 @@ pub fn do_fcntl(fd: FileDesc, cmd: &FcntlCmd) -> Result { file.set_status_flags(status_flags)?; 0 } + FcntlCmd::GetLk(flock_mut_c) => { + let file = file_table.get(fd)?; + let mut lock = Flock::from_c(*flock_mut_c)?; + if let FlockType::F_UNLCK = lock.l_type { + return_errno!(EINVAL, "invalid flock type for getlk"); + } + file.test_advisory_lock(&mut lock)?; + (*flock_mut_c).copy_from_safe(&lock); + 0 + } + FcntlCmd::SetLk(flock_c) => { + let file = file_table.get(fd)?; + let lock = Flock::from_c(*flock_c)?; + file.set_advisory_lock(&lock)?; + 0 + } }; Ok(ret) } diff --git a/src/libos/src/fs/file_ops/flock.rs b/src/libos/src/fs/file_ops/flock.rs new file mode 100644 index 00000000..74b28cb7 --- /dev/null +++ b/src/libos/src/fs/file_ops/flock.rs @@ -0,0 +1,88 @@ +/// File POSIX advisory lock +use super::*; +use process::pid_t; + +/// C struct for a lock +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct flock { + pub l_type: u16, + pub l_whence: u16, + pub l_start: off_t, + pub l_len: off_t, + pub l_pid: pid_t, +} + +impl flock { + pub fn copy_from_safe(&mut self, lock: &Flock) { + self.l_type = lock.l_type as u16; + self.l_whence = lock.l_whence as u16; + self.l_start = lock.l_start; + self.l_len = lock.l_len; + self.l_pid = lock.l_pid; + } +} + +/// Type safe representation of flock +#[derive(Debug, Copy, Clone)] +pub struct Flock { + pub l_type: FlockType, + pub l_whence: FlockWhence, + pub l_start: off_t, + pub l_len: off_t, + pub l_pid: pid_t, +} + +impl Flock { + pub fn from_c(flock_c: &flock) -> Result { + let l_type = FlockType::from_u16(flock_c.l_type)?; + let l_whence = FlockWhence::from_u16(flock_c.l_whence)?; + Ok(Self { + l_type: l_type, + l_whence: l_whence, + l_start: flock_c.l_start, + l_len: flock_c.l_len, + l_pid: flock_c.l_pid, + }) + } +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone)] +#[repr(u16)] +pub enum FlockType { + F_RDLCK = 0, + F_WRLCK = 1, + F_UNLCK = 2, +} + +impl FlockType { + pub fn from_u16(_type: u16) -> Result { + Ok(match _type { + 0 => FlockType::F_RDLCK, + 1 => FlockType::F_WRLCK, + 2 => FlockType::F_UNLCK, + _ => return_errno!(EINVAL, "invalid flock type"), + }) + } +} + +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone)] +#[repr(u16)] +pub enum FlockWhence { + SEEK_SET = 0, + SEEK_CUR = 1, + SEEK_END = 2, +} + +impl FlockWhence { + pub fn from_u16(whence: u16) -> Result { + Ok(match whence { + 0 => FlockWhence::SEEK_SET, + 1 => FlockWhence::SEEK_CUR, + 2 => FlockWhence::SEEK_END, + _ => return_errno!(EINVAL, "Invalid whence"), + }) + } +} diff --git a/src/libos/src/fs/file_ops/mod.rs b/src/libos/src/fs/file_ops/mod.rs index 18d53619..aaad1bb2 100644 --- a/src/libos/src/fs/file_ops/mod.rs +++ b/src/libos/src/fs/file_ops/mod.rs @@ -11,6 +11,7 @@ pub use self::dirent::do_getdents64; pub use self::dup::{do_dup, do_dup2, do_dup3}; pub use self::fcntl::{do_fcntl, FcntlCmd}; pub use self::file_flags::{AccessMode, CreationFlags, StatusFlags}; +pub use self::flock::{Flock, FlockType}; pub use self::fsync::{do_fdatasync, do_fsync}; pub use self::ioctl::{do_ioctl, IoctlCmd, StructuredIoctlArgType, StructuredIoctlNum}; pub use self::link::do_link; @@ -34,6 +35,7 @@ mod dirent; mod dup; mod fcntl; mod file_flags; +mod flock; mod fsync; mod ioctl; mod link; diff --git a/src/libos/src/fs/inode_file.rs b/src/libos/src/fs/inode_file.rs index b3aa8c42..1464fb72 100644 --- a/src/libos/src/fs/inode_file.rs +++ b/src/libos/src/fs/inode_file.rs @@ -166,6 +166,32 @@ impl File for INodeFile { Ok(()) } + fn test_advisory_lock(&self, lock: &mut Flock) -> Result<()> { + // Let the advisory lock could be placed + // TODO: Implement the real advisory lock + lock.l_type = FlockType::F_UNLCK; + Ok(()) + } + + fn set_advisory_lock(&self, lock: &Flock) -> Result<()> { + match lock.l_type { + FlockType::F_RDLCK => { + if !self.access_mode.readable() { + return_errno!(EBADF, "File not readable"); + } + } + FlockType::F_WRLCK => { + if !self.access_mode.writable() { + return_errno!(EBADF, "File not writable"); + } + } + _ => (), + } + // Let the advisory lock could be acquired or released + // TODO: Implement the real advisory lock + Ok(()) + } + fn as_any(&self) -> &dyn Any { self } diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 50116b28..c509d49b 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -10,6 +10,7 @@ use std::mem::MaybeUninit; pub use self::file::{File, FileRef}; pub use self::file_ops::{AccessMode, CreationFlags, Stat, StatusFlags}; +pub use self::file_ops::{Flock, FlockType}; pub use self::file_ops::{IoctlCmd, StructuredIoctlArgType, StructuredIoctlNum}; pub use self::file_table::{FileDesc, FileTable}; pub use self::inode_file::{AsINodeFile, INodeExt, INodeFile}; diff --git a/src/libos/src/fs/syscalls.rs b/src/libos/src/fs/syscalls.rs index a0ba8159..6fce9fdb 100644 --- a/src/libos/src/fs/syscalls.rs +++ b/src/libos/src/fs/syscalls.rs @@ -347,8 +347,8 @@ pub fn do_sendfile( } pub fn do_fcntl(fd: FileDesc, cmd: u32, arg: u64) -> Result { - let cmd = FcntlCmd::from_raw(cmd, arg)?; - file_ops::do_fcntl(fd, &cmd) + let mut cmd = FcntlCmd::from_raw(cmd, arg)?; + file_ops::do_fcntl(fd, &mut cmd) } pub fn do_ioctl(fd: FileDesc, cmd: u32, argp: *mut u8) -> Result { diff --git a/test/fcntl/main.c b/test/fcntl/main.c index 5dca0e6b..6c01bd6a 100644 --- a/test/fcntl/main.c +++ b/test/fcntl/main.c @@ -43,6 +43,32 @@ static int __fcntl_setfl(int fd, int open_flags) { return 0; } +static int __fcntl_getlk_and_setlk(int fd, int open_flags) { + int ret; + struct flock fl = { F_WRLCK, SEEK_SET, 0, 0, 0 }; + + // getlk + ret = fcntl(fd, F_GETLK, &fl); + if (ret < 0) { + THROW_ERROR("failed to call getlk"); + } + if (fl.l_type != F_UNLCK) { + THROW_ERROR("failed to get correct fl type"); + } + + // setlk + if ((open_flags & O_WRONLY) || (open_flags & O_RDWR)) + fl.l_type = F_WRLCK; + else + fl.l_type = F_RDLCK; + ret = fcntl(fd, F_SETLK, &fl); + if (ret < 0) { + THROW_ERROR("failed to call setlk"); + } + + return 0; +} + typedef int(*test_fcntl_func_t)(int fd, int open_flags); static int test_fcntl_framework(test_fcntl_func_t fn) { @@ -74,6 +100,10 @@ static int test_fcntl_setfl() { return test_fcntl_framework(__fcntl_setfl); } +static int test_getlk_and_setlk() { + return test_fcntl_framework(__fcntl_getlk_and_setlk); +} + // ============================================================================ // Test suite // ============================================================================ @@ -81,6 +111,7 @@ static int test_fcntl_setfl() { static test_case_t test_cases[] = { TEST_CASE(test_fcntl_getfl), TEST_CASE(test_fcntl_setfl), + TEST_CASE(test_getlk_and_setlk), }; int main() {