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.
This commit is contained in:
LI Qing 2020-02-13 04:54:14 +00:00 committed by tate.thl
parent a6e55881b9
commit 551fb8f9d8
8 changed files with 197 additions and 7 deletions

@ -79,6 +79,14 @@ pub trait File: Debug + Sync + Send + Any {
return_op_unsupported_error!("set_status_flags") 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; fn as_any(&self) -> &dyn Any;
} }

@ -1,7 +1,9 @@
use super::flock::flock;
use super::*; use super::*;
use util::mem_util::from_user;
#[derive(Debug)] #[derive(Debug)]
pub enum FcntlCmd { pub enum FcntlCmd<'a> {
/// Duplicate the file descriptor fd using the lowest-numbered available /// Duplicate the file descriptor fd using the lowest-numbered available
/// file descriptor greater than or equal to arg. /// file descriptor greater than or equal to arg.
DupFd(FileDesc), DupFd(FileDesc),
@ -16,11 +18,15 @@ pub enum FcntlCmd {
GetFl(), GetFl(),
/// Set the file status flags /// Set the file status flags
SetFl(u32), 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)] #[deny(unreachable_patterns)]
pub fn from_raw(cmd: u32, arg: u64) -> Result<FcntlCmd> { pub fn from_raw(cmd: u32, arg: u64) -> Result<FcntlCmd<'a>> {
Ok(match cmd as c_int { Ok(match cmd as c_int {
libc::F_DUPFD => FcntlCmd::DupFd(arg as FileDesc), libc::F_DUPFD => FcntlCmd::DupFd(arg as FileDesc),
libc::F_DUPFD_CLOEXEC => FcntlCmd::DupFdCloexec(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_SETFD => FcntlCmd::SetFd(arg as u32),
libc::F_GETFL => FcntlCmd::GetFl(), libc::F_GETFL => FcntlCmd::GetFl(),
libc::F_SETFL => FcntlCmd::SetFl(arg as u32), 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"), _ => return_errno!(EINVAL, "unsupported command"),
}) })
} }
} }
pub fn do_fcntl(fd: FileDesc, cmd: &FcntlCmd) -> Result<isize> { pub fn do_fcntl(fd: FileDesc, cmd: &mut FcntlCmd) -> Result<isize> {
info!("fcntl: fd: {:?}, cmd: {:?}", &fd, cmd); info!("fcntl: fd: {:?}, cmd: {:?}", &fd, cmd);
let current_ref = process::get_current(); let current_ref = process::get_current();
let mut current = current_ref.lock().unwrap(); let mut current = current_ref.lock().unwrap();
@ -59,7 +77,7 @@ pub fn do_fcntl(fd: FileDesc, cmd: &FcntlCmd) -> Result<isize> {
} }
FcntlCmd::SetFd(fd_flags) => { FcntlCmd::SetFd(fd_flags) => {
let entry = file_table.get_entry_mut(fd)?; 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 0
} }
FcntlCmd::GetFl() => { FcntlCmd::GetFl() => {
@ -74,6 +92,22 @@ pub fn do_fcntl(fd: FileDesc, cmd: &FcntlCmd) -> Result<isize> {
file.set_status_flags(status_flags)?; file.set_status_flags(status_flags)?;
0 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) Ok(ret)
} }

@ -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<Self> {
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<Self> {
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<Self> {
Ok(match whence {
0 => FlockWhence::SEEK_SET,
1 => FlockWhence::SEEK_CUR,
2 => FlockWhence::SEEK_END,
_ => return_errno!(EINVAL, "Invalid whence"),
})
}
}

@ -11,6 +11,7 @@ pub use self::dirent::do_getdents64;
pub use self::dup::{do_dup, do_dup2, do_dup3}; pub use self::dup::{do_dup, do_dup2, do_dup3};
pub use self::fcntl::{do_fcntl, FcntlCmd}; pub use self::fcntl::{do_fcntl, FcntlCmd};
pub use self::file_flags::{AccessMode, CreationFlags, StatusFlags}; 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::fsync::{do_fdatasync, do_fsync};
pub use self::ioctl::{do_ioctl, IoctlCmd, StructuredIoctlArgType, StructuredIoctlNum}; pub use self::ioctl::{do_ioctl, IoctlCmd, StructuredIoctlArgType, StructuredIoctlNum};
pub use self::link::do_link; pub use self::link::do_link;
@ -34,6 +35,7 @@ mod dirent;
mod dup; mod dup;
mod fcntl; mod fcntl;
mod file_flags; mod file_flags;
mod flock;
mod fsync; mod fsync;
mod ioctl; mod ioctl;
mod link; mod link;

@ -166,6 +166,32 @@ impl File for INodeFile {
Ok(()) 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 { fn as_any(&self) -> &dyn Any {
self self
} }

@ -10,6 +10,7 @@ use std::mem::MaybeUninit;
pub use self::file::{File, FileRef}; pub use self::file::{File, FileRef};
pub use self::file_ops::{AccessMode, CreationFlags, Stat, StatusFlags}; 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_ops::{IoctlCmd, StructuredIoctlArgType, StructuredIoctlNum};
pub use self::file_table::{FileDesc, FileTable}; pub use self::file_table::{FileDesc, FileTable};
pub use self::inode_file::{AsINodeFile, INodeExt, INodeFile}; pub use self::inode_file::{AsINodeFile, INodeExt, INodeFile};

@ -347,8 +347,8 @@ pub fn do_sendfile(
} }
pub fn do_fcntl(fd: FileDesc, cmd: u32, arg: u64) -> Result<isize> { pub fn do_fcntl(fd: FileDesc, cmd: u32, arg: u64) -> Result<isize> {
let cmd = FcntlCmd::from_raw(cmd, arg)?; let mut cmd = FcntlCmd::from_raw(cmd, arg)?;
file_ops::do_fcntl(fd, &cmd) file_ops::do_fcntl(fd, &mut cmd)
} }
pub fn do_ioctl(fd: FileDesc, cmd: u32, argp: *mut u8) -> Result<isize> { pub fn do_ioctl(fd: FileDesc, cmd: u32, argp: *mut u8) -> Result<isize> {

@ -43,6 +43,32 @@ static int __fcntl_setfl(int fd, int open_flags) {
return 0; 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); typedef int(*test_fcntl_func_t)(int fd, int open_flags);
static int test_fcntl_framework(test_fcntl_func_t fn) { 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); return test_fcntl_framework(__fcntl_setfl);
} }
static int test_getlk_and_setlk() {
return test_fcntl_framework(__fcntl_getlk_and_setlk);
}
// ============================================================================ // ============================================================================
// Test suite // Test suite
// ============================================================================ // ============================================================================
@ -81,6 +111,7 @@ static int test_fcntl_setfl() {
static test_case_t test_cases[] = { static test_case_t test_cases[] = {
TEST_CASE(test_fcntl_getfl), TEST_CASE(test_fcntl_getfl),
TEST_CASE(test_fcntl_setfl), TEST_CASE(test_fcntl_setfl),
TEST_CASE(test_getlk_and_setlk),
}; };
int main() { int main() {