Add fcntl's subcommands: F_GETFL and F_SETFL
* Modify fcntl system call to support F_GETFL and F_SETFL * Separate OpenFlags to CreationsFlags, AccessMode and StatusFlags
This commit is contained in:
parent
ebc158fe6c
commit
daed89007a
76
src/libos/src/fs/fcntl.rs
Normal file
76
src/libos/src/fs/fcntl.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use super::*;
|
||||||
|
use process::FileTableRef;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum FcntlCmd {
|
||||||
|
/// Duplicate the file descriptor fd using the lowest-numbered available
|
||||||
|
/// file descriptor greater than or equal to arg.
|
||||||
|
DupFd(FileDesc),
|
||||||
|
/// As for `DupFd`, but additionally set the close-on-exec flag for the
|
||||||
|
/// duplicate file descriptor.
|
||||||
|
DupFdCloexec(FileDesc),
|
||||||
|
/// Return (as the function result) the file descriptor flags
|
||||||
|
GetFd(),
|
||||||
|
/// Set the file descriptor to be close-on-exec or not
|
||||||
|
SetFd(u32),
|
||||||
|
/// Get the file status flags
|
||||||
|
GetFl(),
|
||||||
|
/// Set the file status flags
|
||||||
|
SetFl(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FcntlCmd {
|
||||||
|
#[deny(unreachable_patterns)]
|
||||||
|
pub fn from_raw(cmd: u32, arg: u64) -> Result<FcntlCmd> {
|
||||||
|
Ok(match cmd as c_int {
|
||||||
|
libc::F_DUPFD => FcntlCmd::DupFd(arg as FileDesc),
|
||||||
|
libc::F_DUPFD_CLOEXEC => FcntlCmd::DupFdCloexec(arg as FileDesc),
|
||||||
|
libc::F_GETFD => FcntlCmd::GetFd(),
|
||||||
|
libc::F_SETFD => FcntlCmd::SetFd(arg as u32),
|
||||||
|
libc::F_GETFL => FcntlCmd::GetFl(),
|
||||||
|
libc::F_SETFL => FcntlCmd::SetFl(arg as u32),
|
||||||
|
_ => return_errno!(EINVAL, "unsupported command"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn do_fcntl(file_table_ref: &FileTableRef, fd: FileDesc, cmd: &FcntlCmd) -> Result<isize> {
|
||||||
|
let mut file_table = file_table_ref.lock().unwrap();
|
||||||
|
let ret = match cmd {
|
||||||
|
FcntlCmd::DupFd(min_fd) => {
|
||||||
|
let dup_fd = file_table.dup(fd, *min_fd, false)?;
|
||||||
|
dup_fd as isize
|
||||||
|
}
|
||||||
|
FcntlCmd::DupFdCloexec(min_fd) => {
|
||||||
|
let dup_fd = file_table.dup(fd, *min_fd, true)?;
|
||||||
|
dup_fd as isize
|
||||||
|
}
|
||||||
|
FcntlCmd::GetFd() => {
|
||||||
|
let entry = file_table.get_entry(fd)?;
|
||||||
|
let fd_flags = if entry.is_close_on_spawn() {
|
||||||
|
libc::FD_CLOEXEC
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
fd_flags as isize
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
0
|
||||||
|
}
|
||||||
|
FcntlCmd::GetFl() => {
|
||||||
|
let file = file_table.get(fd)?;
|
||||||
|
let status_flags = file.get_status_flags()?;
|
||||||
|
let access_mode = file.get_access_mode()?;
|
||||||
|
(status_flags.bits() | access_mode as u32) as isize
|
||||||
|
}
|
||||||
|
FcntlCmd::SetFl(flags) => {
|
||||||
|
let file = file_table.get(fd)?;
|
||||||
|
let status_flags = StatusFlags::from_bits_truncate(*flags);
|
||||||
|
file.set_status_flags(status_flags)?;
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(ret)
|
||||||
|
}
|
@ -71,6 +71,18 @@ pub trait File: Debug + Sync + Send + Any {
|
|||||||
return_op_unsupported_error!("ioctl")
|
return_op_unsupported_error!("ioctl")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_access_mode(&self) -> Result<AccessMode> {
|
||||||
|
return_op_unsupported_error!("get_access_mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_status_flags(&self) -> Result<StatusFlags> {
|
||||||
|
return_op_unsupported_error!("get_status_flags")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_status_flags(&self, new_status_flags: StatusFlags) -> Result<()> {
|
||||||
|
return_op_unsupported_error!("set_status_flags")
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &Any;
|
fn as_any(&self) -> &Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
102
src/libos/src/fs/file_flags.rs
Normal file
102
src/libos/src/fs/file_flags.rs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum AccessMode {
|
||||||
|
/// read only
|
||||||
|
O_RDONLY = 0,
|
||||||
|
/// write only
|
||||||
|
O_WRONLY = 1,
|
||||||
|
/// read write
|
||||||
|
O_RDWR = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccessMode {
|
||||||
|
pub fn readable(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
AccessMode::O_RDONLY | AccessMode::O_RDWR => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writable(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
AccessMode::O_WRONLY | AccessMode::O_RDWR => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccessMode {
|
||||||
|
pub fn from_u32(flags: u32) -> Result<Self> {
|
||||||
|
let bits = flags & 0b11;
|
||||||
|
if bits > AccessMode::O_RDWR as u32 {
|
||||||
|
return_errno!(EINVAL, "invalid bits for access mode")
|
||||||
|
}
|
||||||
|
Ok(unsafe { core::mem::transmute(bits as u8) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct CreationFlags: u32 {
|
||||||
|
/// create file if it does not exist
|
||||||
|
const O_CREAT = 1 << 6;
|
||||||
|
/// error if CREATE and the file exists
|
||||||
|
const O_EXCL = 1 << 7;
|
||||||
|
/// not become the process's controlling terminal
|
||||||
|
const O_NOCTTY = 1 << 8;
|
||||||
|
/// truncate file upon open
|
||||||
|
const O_TRUNC = 1 << 9;
|
||||||
|
/// file is a directory
|
||||||
|
const O_DIRECTORY = 1 << 16;
|
||||||
|
/// pathname is not a symbolic link
|
||||||
|
const O_NOFOLLOW = 1 << 17;
|
||||||
|
/// close on exec
|
||||||
|
const O_CLOEXEC = 1 << 19;
|
||||||
|
/// create an unnamed temporary regular file
|
||||||
|
const _O_TMPFILE = 1 << 22;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreationFlags {
|
||||||
|
pub fn must_close_on_spawn(&self) -> bool {
|
||||||
|
self.contains(CreationFlags::O_CLOEXEC)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_create(&self) -> bool {
|
||||||
|
self.contains(CreationFlags::O_CREAT)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_exclusive(&self) -> bool {
|
||||||
|
self.contains(CreationFlags::O_EXCL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct StatusFlags: u32 {
|
||||||
|
/// append on each write
|
||||||
|
const O_APPEND = 1 << 10;
|
||||||
|
/// non block
|
||||||
|
const O_NONBLOCK = 1 << 11;
|
||||||
|
/// synchronized I/O, data
|
||||||
|
const O_DSYNC = 1 << 12;
|
||||||
|
/// signal-driven I/O
|
||||||
|
const O_ASYNC = 1 << 13;
|
||||||
|
/// direct I/O
|
||||||
|
const O_DIRECT = 1 << 14;
|
||||||
|
/// on x86_64, O_LARGEFILE is 0
|
||||||
|
/// not update st_atime
|
||||||
|
const O_NOATIME = 1 << 18;
|
||||||
|
/// synchronized I/O, data and metadata
|
||||||
|
const _O_SYNC = 1 << 20;
|
||||||
|
/// equivalent of POSIX.1's O_EXEC
|
||||||
|
const O_PATH = 1 << 21;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatusFlags {
|
||||||
|
pub fn always_append(&self) -> bool {
|
||||||
|
self.contains(StatusFlags::O_APPEND)
|
||||||
|
}
|
||||||
|
}
|
@ -6,20 +6,13 @@ use std::fmt;
|
|||||||
pub struct INodeFile {
|
pub struct INodeFile {
|
||||||
inode: Arc<INode>,
|
inode: Arc<INode>,
|
||||||
offset: SgxMutex<usize>,
|
offset: SgxMutex<usize>,
|
||||||
options: OpenOptions,
|
access_mode: AccessMode,
|
||||||
}
|
status_flags: SgxRwLock<StatusFlags>,
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct OpenOptions {
|
|
||||||
pub read: bool,
|
|
||||||
pub write: bool,
|
|
||||||
/// Before each write, the file offset is positioned at the end of the file.
|
|
||||||
pub append: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl File for INodeFile {
|
impl File for INodeFile {
|
||||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||||
if !self.options.read {
|
if !self.access_mode.readable() {
|
||||||
return_errno!(EBADF, "File not readable");
|
return_errno!(EBADF, "File not readable");
|
||||||
}
|
}
|
||||||
let mut offset = self.offset.lock().unwrap();
|
let mut offset = self.offset.lock().unwrap();
|
||||||
@ -29,11 +22,11 @@ impl File for INodeFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||||
if !self.options.write {
|
if !self.access_mode.writable() {
|
||||||
return_errno!(EBADF, "File not writable");
|
return_errno!(EBADF, "File not writable");
|
||||||
}
|
}
|
||||||
let mut offset = self.offset.lock().unwrap();
|
let mut offset = self.offset.lock().unwrap();
|
||||||
if self.options.append {
|
if self.status_flags.read().unwrap().always_append() {
|
||||||
let info = self.inode.metadata()?;
|
let info = self.inode.metadata()?;
|
||||||
*offset = info.size;
|
*offset = info.size;
|
||||||
}
|
}
|
||||||
@ -43,7 +36,7 @@ impl File for INodeFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||||
if !self.options.read {
|
if !self.access_mode.readable() {
|
||||||
return_errno!(EBADF, "File not readable");
|
return_errno!(EBADF, "File not readable");
|
||||||
}
|
}
|
||||||
let len = self.inode.read_at(offset, buf)?;
|
let len = self.inode.read_at(offset, buf)?;
|
||||||
@ -51,7 +44,7 @@ impl File for INodeFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
|
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
|
||||||
if !self.options.write {
|
if !self.access_mode.writable() {
|
||||||
return_errno!(EBADF, "File not writable");
|
return_errno!(EBADF, "File not writable");
|
||||||
}
|
}
|
||||||
let len = self.inode.write_at(offset, buf)?;
|
let len = self.inode.write_at(offset, buf)?;
|
||||||
@ -59,7 +52,7 @@ impl File for INodeFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize> {
|
fn readv(&self, bufs: &mut [&mut [u8]]) -> Result<usize> {
|
||||||
if !self.options.read {
|
if !self.access_mode.readable() {
|
||||||
return_errno!(EBADF, "File not readable");
|
return_errno!(EBADF, "File not readable");
|
||||||
}
|
}
|
||||||
let mut offset = self.offset.lock().unwrap();
|
let mut offset = self.offset.lock().unwrap();
|
||||||
@ -78,11 +71,11 @@ impl File for INodeFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn writev(&self, bufs: &[&[u8]]) -> Result<usize> {
|
fn writev(&self, bufs: &[&[u8]]) -> Result<usize> {
|
||||||
if !self.options.write {
|
if !self.access_mode.writable() {
|
||||||
return_errno!(EBADF, "File not writable");
|
return_errno!(EBADF, "File not writable");
|
||||||
}
|
}
|
||||||
let mut offset = self.offset.lock().unwrap();
|
let mut offset = self.offset.lock().unwrap();
|
||||||
if self.options.append {
|
if self.status_flags.read().unwrap().always_append() {
|
||||||
let info = self.inode.metadata()?;
|
let info = self.inode.metadata()?;
|
||||||
*offset = info.size;
|
*offset = info.size;
|
||||||
}
|
}
|
||||||
@ -116,7 +109,7 @@ impl File for INodeFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_len(&self, len: u64) -> Result<()> {
|
fn set_len(&self, len: u64) -> Result<()> {
|
||||||
if !self.options.write {
|
if !self.access_mode.writable() {
|
||||||
return_errno!(EBADF, "File not writable. Can't set len.");
|
return_errno!(EBADF, "File not writable. Can't set len.");
|
||||||
}
|
}
|
||||||
self.inode.resize(len as usize)?;
|
self.inode.resize(len as usize)?;
|
||||||
@ -134,7 +127,7 @@ impl File for INodeFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_entry(&self) -> Result<String> {
|
fn read_entry(&self) -> Result<String> {
|
||||||
if !self.options.read {
|
if !self.access_mode.readable() {
|
||||||
return_errno!(EBADF, "File not readable. Can't read entry.");
|
return_errno!(EBADF, "File not readable. Can't read entry.");
|
||||||
}
|
}
|
||||||
let mut offset = self.offset.lock().unwrap();
|
let mut offset = self.offset.lock().unwrap();
|
||||||
@ -143,24 +136,48 @@ impl File for INodeFile {
|
|||||||
Ok(name)
|
Ok(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_access_mode(&self) -> Result<AccessMode> {
|
||||||
|
Ok(self.access_mode.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_status_flags(&self) -> Result<StatusFlags> {
|
||||||
|
let status_flags = self.status_flags.read().unwrap();
|
||||||
|
Ok(status_flags.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_status_flags(&self, new_status_flags: StatusFlags) -> Result<()> {
|
||||||
|
let mut status_flags = self.status_flags.write().unwrap();
|
||||||
|
// Currently, F_SETFL can change only the O_APPEND,
|
||||||
|
// O_ASYNC, O_NOATIME, and O_NONBLOCK flags
|
||||||
|
let valid_flags_mask = StatusFlags::O_APPEND
|
||||||
|
| StatusFlags::O_ASYNC
|
||||||
|
| StatusFlags::O_NOATIME
|
||||||
|
| StatusFlags::O_NONBLOCK;
|
||||||
|
status_flags.remove(valid_flags_mask);
|
||||||
|
status_flags.insert(new_status_flags & valid_flags_mask);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn as_any(&self) -> &Any {
|
fn as_any(&self) -> &Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl INodeFile {
|
impl INodeFile {
|
||||||
pub fn open(inode: Arc<INode>, options: OpenOptions) -> Result<Self> {
|
pub fn open(inode: Arc<INode>, flags: u32) -> Result<Self> {
|
||||||
if (options.read && !inode.allow_read()?) {
|
let access_mode = AccessMode::from_u32(flags)?;
|
||||||
|
if (access_mode.readable() && !inode.allow_read()?) {
|
||||||
return_errno!(EBADF, "File not readable");
|
return_errno!(EBADF, "File not readable");
|
||||||
}
|
}
|
||||||
if (options.write && !inode.allow_write()?) {
|
if (access_mode.writable() && !inode.allow_write()?) {
|
||||||
return_errno!(EBADF, "File not writable");
|
return_errno!(EBADF, "File not writable");
|
||||||
}
|
}
|
||||||
|
let status_flags = StatusFlags::from_bits_truncate(flags);
|
||||||
Ok(INodeFile {
|
Ok(INodeFile {
|
||||||
inode,
|
inode,
|
||||||
offset: SgxMutex::new(0),
|
offset: SgxMutex::new(0),
|
||||||
options,
|
access_mode,
|
||||||
|
status_flags: SgxRwLock::new(status_flags),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,9 +186,10 @@ impl Debug for INodeFile {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"INodeFile {{ inode: ???, pos: {}, options: {:?} }}",
|
"INodeFile {{ inode: ???, pos: {}, access_mode: {:?}, status_flags: {:#o} }}",
|
||||||
*self.offset.lock().unwrap(),
|
*self.offset.lock().unwrap(),
|
||||||
self.options
|
self.access_mode,
|
||||||
|
*self.status_flags.read().unwrap()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,10 @@ use self::dev_null::DevNull;
|
|||||||
use self::dev_random::DevRandom;
|
use self::dev_random::DevRandom;
|
||||||
use self::dev_sgx::DevSgx;
|
use self::dev_sgx::DevSgx;
|
||||||
use self::dev_zero::DevZero;
|
use self::dev_zero::DevZero;
|
||||||
|
pub use self::fcntl::FcntlCmd;
|
||||||
pub use self::file::{File, FileRef, SgxFile, StdinFile, StdoutFile};
|
pub use self::file::{File, FileRef, SgxFile, StdinFile, StdoutFile};
|
||||||
|
pub use self::file_flags::{AccessMode, CreationFlags, StatusFlags};
|
||||||
pub use self::file_table::{FileDesc, FileTable};
|
pub use self::file_table::{FileDesc, FileTable};
|
||||||
use self::inode_file::OpenOptions;
|
|
||||||
pub use self::inode_file::{INodeExt, INodeFile};
|
pub use self::inode_file::{INodeExt, INodeFile};
|
||||||
pub use self::io_multiplexing::*;
|
pub use self::io_multiplexing::*;
|
||||||
pub use self::ioctl::*;
|
pub use self::ioctl::*;
|
||||||
@ -30,7 +31,9 @@ mod dev_null;
|
|||||||
mod dev_random;
|
mod dev_random;
|
||||||
mod dev_sgx;
|
mod dev_sgx;
|
||||||
mod dev_zero;
|
mod dev_zero;
|
||||||
|
mod fcntl;
|
||||||
mod file;
|
mod file;
|
||||||
|
mod file_flags;
|
||||||
mod file_table;
|
mod file_table;
|
||||||
mod hostfs;
|
mod hostfs;
|
||||||
mod inode_file;
|
mod inode_file;
|
||||||
@ -42,9 +45,8 @@ mod sgx_impl;
|
|||||||
mod unix_socket;
|
mod unix_socket;
|
||||||
|
|
||||||
pub fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc> {
|
pub fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc> {
|
||||||
let flags = OpenFlags::from_bits_truncate(flags);
|
|
||||||
info!(
|
info!(
|
||||||
"open: path: {:?}, flags: {:?}, mode: {:#o}",
|
"open: path: {:?}, flags: {:#o}, mode: {:#o}",
|
||||||
path, flags, mode
|
path, flags, mode
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -55,11 +57,11 @@ pub fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc> {
|
|||||||
let file_ref: Arc<Box<File>> = Arc::new(file);
|
let file_ref: Arc<Box<File>> = Arc::new(file);
|
||||||
|
|
||||||
let fd = {
|
let fd = {
|
||||||
let close_on_spawn = flags.contains(OpenFlags::CLOEXEC);
|
let creation_flags = CreationFlags::from_bits_truncate(flags);
|
||||||
proc.get_files()
|
proc.get_files()
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.put(file_ref, close_on_spawn)
|
.put(file_ref, creation_flags.must_close_on_spawn())
|
||||||
};
|
};
|
||||||
Ok(fd)
|
Ok(fd)
|
||||||
}
|
}
|
||||||
@ -238,14 +240,14 @@ pub fn do_ioctl(fd: FileDesc, cmd: &mut IoctlCmd) -> Result<()> {
|
|||||||
|
|
||||||
pub fn do_pipe2(flags: u32) -> Result<[FileDesc; 2]> {
|
pub fn do_pipe2(flags: u32) -> Result<[FileDesc; 2]> {
|
||||||
info!("pipe2: flags: {:#x}", flags);
|
info!("pipe2: flags: {:#x}", flags);
|
||||||
let flags = OpenFlags::from_bits_truncate(flags);
|
let creation_flags = CreationFlags::from_bits_truncate(flags);
|
||||||
let current_ref = process::get_current();
|
let current_ref = process::get_current();
|
||||||
let current = current_ref.lock().unwrap();
|
let current = current_ref.lock().unwrap();
|
||||||
let pipe = Pipe::new()?;
|
let pipe = Pipe::new()?;
|
||||||
|
|
||||||
let file_table_ref = current.get_files();
|
let file_table_ref = current.get_files();
|
||||||
let mut file_table = file_table_ref.lock().unwrap();
|
let mut file_table = file_table_ref.lock().unwrap();
|
||||||
let close_on_spawn = flags.contains(OpenFlags::CLOEXEC);
|
let close_on_spawn = creation_flags.must_close_on_spawn();
|
||||||
let reader_fd = file_table.put(Arc::new(Box::new(pipe.reader)), close_on_spawn);
|
let reader_fd = file_table.put(Arc::new(Box::new(pipe.reader)), close_on_spawn);
|
||||||
let writer_fd = file_table.put(Arc::new(Box::new(pipe.writer)), close_on_spawn);
|
let writer_fd = file_table.put(Arc::new(Box::new(pipe.writer)), close_on_spawn);
|
||||||
info!("pipe2: reader_fd: {}, writer_fd: {}", reader_fd, writer_fd);
|
info!("pipe2: reader_fd: {}, writer_fd: {}", reader_fd, writer_fd);
|
||||||
@ -275,7 +277,7 @@ pub fn do_dup2(old_fd: FileDesc, new_fd: FileDesc) -> Result<FileDesc> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_dup3(old_fd: FileDesc, new_fd: FileDesc, flags: u32) -> Result<FileDesc> {
|
pub fn do_dup3(old_fd: FileDesc, new_fd: FileDesc, flags: u32) -> Result<FileDesc> {
|
||||||
let flags = OpenFlags::from_bits_truncate(flags);
|
let creation_flags = CreationFlags::from_bits_truncate(flags);
|
||||||
let current_ref = process::get_current();
|
let current_ref = process::get_current();
|
||||||
let current = current_ref.lock().unwrap();
|
let current = current_ref.lock().unwrap();
|
||||||
let file_table_ref = current.get_files();
|
let file_table_ref = current.get_files();
|
||||||
@ -284,8 +286,7 @@ pub fn do_dup3(old_fd: FileDesc, new_fd: FileDesc, flags: u32) -> Result<FileDes
|
|||||||
if old_fd == new_fd {
|
if old_fd == new_fd {
|
||||||
return_errno!(EINVAL, "old_fd must not be equal to new_fd");
|
return_errno!(EINVAL, "old_fd must not be equal to new_fd");
|
||||||
}
|
}
|
||||||
let close_on_spawn = flags.contains(OpenFlags::CLOEXEC);
|
file_table.put_at(new_fd, file, creation_flags.must_close_on_spawn());
|
||||||
file_table.put_at(new_fd, file, close_on_spawn);
|
|
||||||
Ok(new_fd)
|
Ok(new_fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,7 +440,7 @@ extern "C" {
|
|||||||
|
|
||||||
impl Process {
|
impl Process {
|
||||||
/// Open a file on the process. But DO NOT add it to file table.
|
/// Open a file on the process. But DO NOT add it to file table.
|
||||||
pub fn open_file(&self, path: &str, flags: OpenFlags, mode: u32) -> Result<Box<File>> {
|
pub fn open_file(&self, path: &str, flags: u32, mode: u32) -> Result<Box<File>> {
|
||||||
if path == "/dev/null" {
|
if path == "/dev/null" {
|
||||||
return Ok(Box::new(DevNull));
|
return Ok(Box::new(DevNull));
|
||||||
}
|
}
|
||||||
@ -452,12 +453,13 @@ impl Process {
|
|||||||
if path == "/dev/sgx" {
|
if path == "/dev/sgx" {
|
||||||
return Ok(Box::new(DevSgx));
|
return Ok(Box::new(DevSgx));
|
||||||
}
|
}
|
||||||
let inode = if flags.contains(OpenFlags::CREATE) {
|
let creation_flags = CreationFlags::from_bits_truncate(flags);
|
||||||
|
let inode = if creation_flags.can_create() {
|
||||||
let (dir_path, file_name) = split_path(&path);
|
let (dir_path, file_name) = split_path(&path);
|
||||||
let dir_inode = self.lookup_inode(dir_path)?;
|
let dir_inode = self.lookup_inode(dir_path)?;
|
||||||
match dir_inode.find(file_name) {
|
match dir_inode.find(file_name) {
|
||||||
Ok(file_inode) => {
|
Ok(file_inode) => {
|
||||||
if flags.contains(OpenFlags::EXCLUSIVE) {
|
if creation_flags.is_exclusive() {
|
||||||
return_errno!(EEXIST, "file exists");
|
return_errno!(EEXIST, "file exists");
|
||||||
}
|
}
|
||||||
file_inode
|
file_inode
|
||||||
@ -473,7 +475,7 @@ impl Process {
|
|||||||
} else {
|
} else {
|
||||||
self.lookup_inode(&path)?
|
self.lookup_inode(&path)?
|
||||||
};
|
};
|
||||||
Ok(Box::new(INodeFile::open(inode, flags.to_options())?))
|
Ok(Box::new(INodeFile::open(inode, flags)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lookup INode from the cwd of the process
|
/// Lookup INode from the cwd of the process
|
||||||
@ -504,47 +506,6 @@ fn split_path(path: &str) -> (&str, &str) {
|
|||||||
(dir_path, file_name)
|
(dir_path, file_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
pub struct OpenFlags: u32 {
|
|
||||||
/// read only
|
|
||||||
const RDONLY = 0;
|
|
||||||
/// write only
|
|
||||||
const WRONLY = 1;
|
|
||||||
/// read write
|
|
||||||
const RDWR = 2;
|
|
||||||
/// create file if it does not exist
|
|
||||||
const CREATE = 1 << 6;
|
|
||||||
/// error if CREATE and the file exists
|
|
||||||
const EXCLUSIVE = 1 << 7;
|
|
||||||
/// truncate file upon open
|
|
||||||
const TRUNCATE = 1 << 9;
|
|
||||||
/// append on each write
|
|
||||||
const APPEND = 1 << 10;
|
|
||||||
/// non block
|
|
||||||
const NONBLOCK = 1 << 11;
|
|
||||||
/// close on exec
|
|
||||||
const CLOEXEC = 1 << 19;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OpenFlags {
|
|
||||||
fn readable(&self) -> bool {
|
|
||||||
let b = self.bits() & 0b11;
|
|
||||||
b == OpenFlags::RDONLY.bits() || b == OpenFlags::RDWR.bits()
|
|
||||||
}
|
|
||||||
fn writable(&self) -> bool {
|
|
||||||
let b = self.bits() & 0b11;
|
|
||||||
b == OpenFlags::WRONLY.bits() || b == OpenFlags::RDWR.bits()
|
|
||||||
}
|
|
||||||
fn to_options(&self) -> OpenOptions {
|
|
||||||
OpenOptions {
|
|
||||||
read: self.readable(),
|
|
||||||
write: self.writable(),
|
|
||||||
append: self.contains(OpenFlags::APPEND),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(packed)] // Don't use 'C'. Or its size will align up to 8 bytes.
|
#[repr(packed)] // Don't use 'C'. Or its size will align up to 8 bytes.
|
||||||
pub struct LinuxDirent64 {
|
pub struct LinuxDirent64 {
|
||||||
/// Inode number
|
/// Inode number
|
||||||
@ -731,93 +692,13 @@ pub unsafe fn write_cstr(ptr: *mut u8, s: &str) {
|
|||||||
ptr.add(s.len()).write(0);
|
ptr.add(s.len()).write(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum FcntlCmd {
|
|
||||||
/// Duplicate the file descriptor fd using the lowest-numbered available
|
|
||||||
/// file descriptor greater than or equal to arg.
|
|
||||||
DupFd(FileDesc),
|
|
||||||
/// As for `DupFd`, but additionally set the close-on-exec flag for the
|
|
||||||
/// duplicate file descriptor.
|
|
||||||
DupFdCloexec(FileDesc),
|
|
||||||
/// Return (as the function result) the file descriptor flags
|
|
||||||
GetFd(),
|
|
||||||
/// Set the file descriptor to be close-on-exec or not
|
|
||||||
SetFd(u32),
|
|
||||||
/// Get the file status flags
|
|
||||||
GetFl(),
|
|
||||||
/// Set the file status flags
|
|
||||||
SetFl(u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FcntlCmd {
|
|
||||||
#[deny(unreachable_patterns)]
|
|
||||||
pub fn from_raw(cmd: u32, arg: u64) -> Result<FcntlCmd> {
|
|
||||||
Ok(match cmd as c_int {
|
|
||||||
libc::F_DUPFD => FcntlCmd::DupFd(arg as FileDesc),
|
|
||||||
libc::F_DUPFD_CLOEXEC => FcntlCmd::DupFdCloexec(arg as FileDesc),
|
|
||||||
libc::F_GETFD => FcntlCmd::GetFd(),
|
|
||||||
libc::F_SETFD => FcntlCmd::SetFd(arg as u32),
|
|
||||||
libc::F_GETFL => FcntlCmd::GetFl(),
|
|
||||||
libc::F_SETFL => FcntlCmd::SetFl(arg as u32),
|
|
||||||
_ => return_errno!(EINVAL, "invalid command"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn do_fcntl(fd: FileDesc, cmd: &FcntlCmd) -> Result<isize> {
|
pub fn do_fcntl(fd: FileDesc, cmd: &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();
|
||||||
let files_ref = current.get_files();
|
let file_table_ref = current.get_files();
|
||||||
let mut files = files_ref.lock().unwrap();
|
let ret = fcntl::do_fcntl(file_table_ref, fd, cmd)?;
|
||||||
Ok(match cmd {
|
Ok(ret)
|
||||||
FcntlCmd::DupFd(min_fd) => {
|
|
||||||
let dup_fd = files.dup(fd, *min_fd, false)?;
|
|
||||||
dup_fd as isize
|
|
||||||
}
|
|
||||||
FcntlCmd::DupFdCloexec(min_fd) => {
|
|
||||||
let dup_fd = files.dup(fd, *min_fd, true)?;
|
|
||||||
dup_fd as isize
|
|
||||||
}
|
|
||||||
FcntlCmd::GetFd() => {
|
|
||||||
let entry = files.get_entry(fd)?;
|
|
||||||
let fd_flags = if entry.is_close_on_spawn() {
|
|
||||||
libc::FD_CLOEXEC
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
fd_flags as isize
|
|
||||||
}
|
|
||||||
FcntlCmd::SetFd(fd_flags) => {
|
|
||||||
let entry = files.get_entry_mut(fd)?;
|
|
||||||
entry.set_close_on_spawn((fd_flags & libc::FD_CLOEXEC as u32) != 0);
|
|
||||||
0
|
|
||||||
}
|
|
||||||
FcntlCmd::GetFl() => {
|
|
||||||
let file = files.get(fd)?;
|
|
||||||
if let Ok(socket) = file.as_socket() {
|
|
||||||
let ret = try_libc!(libc::ocall::fcntl_arg0(socket.fd(), libc::F_GETFL));
|
|
||||||
ret as isize
|
|
||||||
} else {
|
|
||||||
warn!("fcntl.getfl is unimplemented");
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FcntlCmd::SetFl(flags) => {
|
|
||||||
let file = files.get(fd)?;
|
|
||||||
if let Ok(socket) = file.as_socket() {
|
|
||||||
let ret = try_libc!(libc::ocall::fcntl_arg1(
|
|
||||||
socket.fd(),
|
|
||||||
libc::F_SETFL,
|
|
||||||
*flags as c_int
|
|
||||||
));
|
|
||||||
ret as isize
|
|
||||||
} else {
|
|
||||||
warn!("fcntl.setfl is unimplemented");
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_readlink(path: &str, buf: &mut [u8]) -> Result<usize> {
|
pub fn do_readlink(path: &str, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
@ -5,7 +5,7 @@ use std::path::Path;
|
|||||||
use std::sgxfs::SgxFile;
|
use std::sgxfs::SgxFile;
|
||||||
|
|
||||||
use super::fs::{
|
use super::fs::{
|
||||||
File, FileDesc, FileTable, INodeExt, OpenFlags, StdinFile, StdoutFile, ROOT_INODE,
|
CreationFlags, File, FileDesc, FileTable, INodeExt, StdinFile, StdoutFile, ROOT_INODE,
|
||||||
};
|
};
|
||||||
use super::misc::ResourceLimitsRef;
|
use super::misc::ResourceLimitsRef;
|
||||||
use super::vm::{ProcessVM, ProcessVMBuilder};
|
use super::vm::{ProcessVM, ProcessVMBuilder};
|
||||||
@ -117,12 +117,10 @@ fn init_files(parent_ref: &ProcessRef, file_actions: &[FileAction]) -> Result<Fi
|
|||||||
oflag,
|
oflag,
|
||||||
fd,
|
fd,
|
||||||
} => {
|
} => {
|
||||||
let flags = OpenFlags::from_bits_truncate(oflag);
|
let file = parent.open_file(path.as_str(), oflag, mode)?;
|
||||||
let file = parent.open_file(path.as_str(), flags, mode)?;
|
|
||||||
let file_ref: Arc<Box<File>> = Arc::new(file);
|
let file_ref: Arc<Box<File>> = Arc::new(file);
|
||||||
|
let creation_flags = CreationFlags::from_bits_truncate(oflag);
|
||||||
let close_on_spawn = flags.contains(OpenFlags::CLOEXEC);
|
cloned_file_table.put_at(fd, file_ref, creation_flags.must_close_on_spawn());
|
||||||
cloned_file_table.put_at(fd, file_ref, close_on_spawn);
|
|
||||||
}
|
}
|
||||||
&FileAction::Dup2(old_fd, new_fd) => {
|
&FileAction::Dup2(old_fd, new_fd) => {
|
||||||
let file = cloned_file_table.get(old_fd)?;
|
let file = cloned_file_table.get(old_fd)?;
|
||||||
|
@ -8,7 +8,7 @@ TEST_DEPS := client data_sink
|
|||||||
TESTS := empty env hello_world malloc mmap file fs_perms getpid spawn sched pipe time \
|
TESTS := empty env hello_world malloc mmap file fs_perms getpid spawn sched pipe time \
|
||||||
truncate readdir mkdir link tls pthread uname rlimit server \
|
truncate readdir mkdir link tls pthread uname rlimit server \
|
||||||
server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \
|
server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \
|
||||||
ioctl
|
ioctl fcntl
|
||||||
# Benchmarks: need to be compiled and run by bench-% target
|
# Benchmarks: need to be compiled and run by bench-% target
|
||||||
BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput
|
BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput
|
||||||
|
|
||||||
|
5
test/fcntl/Makefile
Normal file
5
test/fcntl/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
include ../test_common.mk
|
||||||
|
|
||||||
|
EXTRA_C_FLAGS :=
|
||||||
|
EXTRA_LINK_FLAGS :=
|
||||||
|
BIN_ARGS :=
|
88
test/fcntl/main.c
Normal file
88
test/fcntl/main.c
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Helper macro
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
#define CREATION_FLAGS_MASK (O_CLOEXEC | O_CREAT| O_DIRECTORY | O_EXCL | \
|
||||||
|
O_NOCTTY | O_NOFOLLOW | O_TMPFILE | O_TRUNC)
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Test cases for fcntl
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static int __fcntl_getfl(int fd, int open_flags) {
|
||||||
|
int actual_flags;
|
||||||
|
|
||||||
|
actual_flags = fcntl(fd, F_GETFL);
|
||||||
|
open_flags &= ~CREATION_FLAGS_MASK;
|
||||||
|
open_flags |= O_LARGEFILE;
|
||||||
|
if (open_flags != actual_flags) {
|
||||||
|
THROW_ERROR("check getfl failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __fcntl_setfl(int fd, int open_flags) {
|
||||||
|
int ret, actual_flags;
|
||||||
|
|
||||||
|
ret = fcntl(fd, F_SETFL, open_flags & ~O_APPEND);
|
||||||
|
if (ret < 0) {
|
||||||
|
THROW_ERROR("failed to call setfl");
|
||||||
|
}
|
||||||
|
actual_flags = fcntl(fd, F_GETFL);
|
||||||
|
if ((actual_flags & O_APPEND) != 0) {
|
||||||
|
THROW_ERROR("failed to check getfl after setfl");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int(*test_fcntl_func_t)(int fd, int open_flags);
|
||||||
|
|
||||||
|
static int test_fcntl_framework(test_fcntl_func_t fn) {
|
||||||
|
const char *file_path = "/root/test_fcntl_file.txt";
|
||||||
|
int open_flags = O_RDWR | O_CREAT | O_TRUNC | O_APPEND;
|
||||||
|
int mode = 00666;
|
||||||
|
int fd, ret;
|
||||||
|
|
||||||
|
fd = open(file_path, open_flags, mode);
|
||||||
|
if (fd < 0) {
|
||||||
|
THROW_ERROR("failed to open & create file");
|
||||||
|
}
|
||||||
|
if (fn(fd, open_flags) < 0)
|
||||||
|
return -1;
|
||||||
|
close(fd);
|
||||||
|
ret = unlink(file_path);
|
||||||
|
if (ret < 0) {
|
||||||
|
THROW_ERROR("failed to unlink the created file");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_fcntl_getfl() {
|
||||||
|
return test_fcntl_framework(__fcntl_getfl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_fcntl_setfl() {
|
||||||
|
return test_fcntl_framework(__fcntl_setfl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Test suite
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static test_case_t test_cases[] = {
|
||||||
|
TEST_CASE(test_fcntl_getfl),
|
||||||
|
TEST_CASE(test_fcntl_setfl),
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user