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")
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
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 {
|
||||
inode: Arc<INode>,
|
||||
offset: SgxMutex<usize>,
|
||||
options: OpenOptions,
|
||||
}
|
||||
|
||||
#[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,
|
||||
access_mode: AccessMode,
|
||||
status_flags: SgxRwLock<StatusFlags>,
|
||||
}
|
||||
|
||||
impl File for INodeFile {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
if !self.options.read {
|
||||
if !self.access_mode.readable() {
|
||||
return_errno!(EBADF, "File not readable");
|
||||
}
|
||||
let mut offset = self.offset.lock().unwrap();
|
||||
@ -29,11 +22,11 @@ impl File for INodeFile {
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
if !self.options.write {
|
||||
if !self.access_mode.writable() {
|
||||
return_errno!(EBADF, "File not writable");
|
||||
}
|
||||
let mut offset = self.offset.lock().unwrap();
|
||||
if self.options.append {
|
||||
if self.status_flags.read().unwrap().always_append() {
|
||||
let info = self.inode.metadata()?;
|
||||
*offset = info.size;
|
||||
}
|
||||
@ -43,7 +36,7 @@ impl File for INodeFile {
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
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> {
|
||||
if !self.options.write {
|
||||
if !self.access_mode.writable() {
|
||||
return_errno!(EBADF, "File not writable");
|
||||
}
|
||||
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> {
|
||||
if !self.options.read {
|
||||
if !self.access_mode.readable() {
|
||||
return_errno!(EBADF, "File not readable");
|
||||
}
|
||||
let mut offset = self.offset.lock().unwrap();
|
||||
@ -78,11 +71,11 @@ impl File for INodeFile {
|
||||
}
|
||||
|
||||
fn writev(&self, bufs: &[&[u8]]) -> Result<usize> {
|
||||
if !self.options.write {
|
||||
if !self.access_mode.writable() {
|
||||
return_errno!(EBADF, "File not writable");
|
||||
}
|
||||
let mut offset = self.offset.lock().unwrap();
|
||||
if self.options.append {
|
||||
if self.status_flags.read().unwrap().always_append() {
|
||||
let info = self.inode.metadata()?;
|
||||
*offset = info.size;
|
||||
}
|
||||
@ -116,7 +109,7 @@ impl File for INodeFile {
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
self.inode.resize(len as usize)?;
|
||||
@ -134,7 +127,7 @@ impl File for INodeFile {
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
let mut offset = self.offset.lock().unwrap();
|
||||
@ -143,24 +136,48 @@ impl File for INodeFile {
|
||||
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 {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl INodeFile {
|
||||
pub fn open(inode: Arc<INode>, options: OpenOptions) -> Result<Self> {
|
||||
if (options.read && !inode.allow_read()?) {
|
||||
pub fn open(inode: Arc<INode>, flags: u32) -> Result<Self> {
|
||||
let access_mode = AccessMode::from_u32(flags)?;
|
||||
if (access_mode.readable() && !inode.allow_read()?) {
|
||||
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");
|
||||
}
|
||||
|
||||
let status_flags = StatusFlags::from_bits_truncate(flags);
|
||||
Ok(INodeFile {
|
||||
inode,
|
||||
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 {
|
||||
write!(
|
||||
f,
|
||||
"INodeFile {{ inode: ???, pos: {}, options: {:?} }}",
|
||||
"INodeFile {{ inode: ???, pos: {}, access_mode: {:?}, status_flags: {:#o} }}",
|
||||
*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_sgx::DevSgx;
|
||||
use self::dev_zero::DevZero;
|
||||
pub use self::fcntl::FcntlCmd;
|
||||
pub use self::file::{File, FileRef, SgxFile, StdinFile, StdoutFile};
|
||||
pub use self::file_flags::{AccessMode, CreationFlags, StatusFlags};
|
||||
pub use self::file_table::{FileDesc, FileTable};
|
||||
use self::inode_file::OpenOptions;
|
||||
pub use self::inode_file::{INodeExt, INodeFile};
|
||||
pub use self::io_multiplexing::*;
|
||||
pub use self::ioctl::*;
|
||||
@ -30,7 +31,9 @@ mod dev_null;
|
||||
mod dev_random;
|
||||
mod dev_sgx;
|
||||
mod dev_zero;
|
||||
mod fcntl;
|
||||
mod file;
|
||||
mod file_flags;
|
||||
mod file_table;
|
||||
mod hostfs;
|
||||
mod inode_file;
|
||||
@ -42,9 +45,8 @@ mod sgx_impl;
|
||||
mod unix_socket;
|
||||
|
||||
pub fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc> {
|
||||
let flags = OpenFlags::from_bits_truncate(flags);
|
||||
info!(
|
||||
"open: path: {:?}, flags: {:?}, mode: {:#o}",
|
||||
"open: path: {:?}, flags: {:#o}, mode: {:#o}",
|
||||
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 fd = {
|
||||
let close_on_spawn = flags.contains(OpenFlags::CLOEXEC);
|
||||
let creation_flags = CreationFlags::from_bits_truncate(flags);
|
||||
proc.get_files()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.put(file_ref, close_on_spawn)
|
||||
.put(file_ref, creation_flags.must_close_on_spawn())
|
||||
};
|
||||
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]> {
|
||||
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 = current_ref.lock().unwrap();
|
||||
let pipe = Pipe::new()?;
|
||||
|
||||
let file_table_ref = current.get_files();
|
||||
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 writer_fd = file_table.put(Arc::new(Box::new(pipe.writer)), close_on_spawn);
|
||||
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> {
|
||||
let flags = OpenFlags::from_bits_truncate(flags);
|
||||
let creation_flags = CreationFlags::from_bits_truncate(flags);
|
||||
let current_ref = process::get_current();
|
||||
let current = current_ref.lock().unwrap();
|
||||
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 {
|
||||
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, close_on_spawn);
|
||||
file_table.put_at(new_fd, file, creation_flags.must_close_on_spawn());
|
||||
Ok(new_fd)
|
||||
}
|
||||
|
||||
@ -439,7 +440,7 @@ extern "C" {
|
||||
|
||||
impl Process {
|
||||
/// 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" {
|
||||
return Ok(Box::new(DevNull));
|
||||
}
|
||||
@ -452,12 +453,13 @@ impl Process {
|
||||
if path == "/dev/sgx" {
|
||||
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_inode = self.lookup_inode(dir_path)?;
|
||||
match dir_inode.find(file_name) {
|
||||
Ok(file_inode) => {
|
||||
if flags.contains(OpenFlags::EXCLUSIVE) {
|
||||
if creation_flags.is_exclusive() {
|
||||
return_errno!(EEXIST, "file exists");
|
||||
}
|
||||
file_inode
|
||||
@ -473,7 +475,7 @@ impl Process {
|
||||
} else {
|
||||
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
|
||||
@ -504,47 +506,6 @@ fn split_path(path: &str) -> (&str, &str) {
|
||||
(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.
|
||||
pub struct LinuxDirent64 {
|
||||
/// Inode number
|
||||
@ -731,93 +692,13 @@ pub unsafe fn write_cstr(ptr: *mut u8, s: &str) {
|
||||
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> {
|
||||
info!("fcntl: fd: {:?}, cmd: {:?}", &fd, cmd);
|
||||
let current_ref = process::get_current();
|
||||
let mut current = current_ref.lock().unwrap();
|
||||
let files_ref = current.get_files();
|
||||
let mut files = files_ref.lock().unwrap();
|
||||
Ok(match cmd {
|
||||
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
|
||||
}
|
||||
}
|
||||
})
|
||||
let file_table_ref = current.get_files();
|
||||
let ret = fcntl::do_fcntl(file_table_ref, fd, cmd)?;
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn do_readlink(path: &str, buf: &mut [u8]) -> Result<usize> {
|
||||
|
@ -5,7 +5,7 @@ use std::path::Path;
|
||||
use std::sgxfs::SgxFile;
|
||||
|
||||
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::vm::{ProcessVM, ProcessVMBuilder};
|
||||
@ -117,12 +117,10 @@ fn init_files(parent_ref: &ProcessRef, file_actions: &[FileAction]) -> Result<Fi
|
||||
oflag,
|
||||
fd,
|
||||
} => {
|
||||
let flags = OpenFlags::from_bits_truncate(oflag);
|
||||
let file = parent.open_file(path.as_str(), flags, mode)?;
|
||||
let file = parent.open_file(path.as_str(), oflag, mode)?;
|
||||
let file_ref: Arc<Box<File>> = Arc::new(file);
|
||||
|
||||
let close_on_spawn = flags.contains(OpenFlags::CLOEXEC);
|
||||
cloned_file_table.put_at(fd, file_ref, close_on_spawn);
|
||||
let creation_flags = CreationFlags::from_bits_truncate(oflag);
|
||||
cloned_file_table.put_at(fd, file_ref, creation_flags.must_close_on_spawn());
|
||||
}
|
||||
&FileAction::Dup2(old_fd, new_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 \
|
||||
truncate readdir mkdir link tls pthread uname rlimit server \
|
||||
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
|
||||
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