From daed89007a78e0b0277e637284e5d714df7d4278 Mon Sep 17 00:00:00 2001 From: LI Qing Date: Wed, 4 Dec 2019 08:13:53 +0000 Subject: [PATCH] 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 --- src/libos/src/fs/fcntl.rs | 76 ++++++++++++++ src/libos/src/fs/file.rs | 12 +++ src/libos/src/fs/file_flags.rs | 102 +++++++++++++++++++ src/libos/src/fs/inode_file.rs | 70 ++++++++----- src/libos/src/fs/mod.rs | 157 ++++------------------------- src/libos/src/process/spawn/mod.rs | 10 +- test/Makefile | 2 +- test/fcntl/Makefile | 5 + test/fcntl/main.c | 88 ++++++++++++++++ 9 files changed, 351 insertions(+), 171 deletions(-) create mode 100644 src/libos/src/fs/fcntl.rs create mode 100644 src/libos/src/fs/file_flags.rs create mode 100644 test/fcntl/Makefile create mode 100644 test/fcntl/main.c diff --git a/src/libos/src/fs/fcntl.rs b/src/libos/src/fs/fcntl.rs new file mode 100644 index 00000000..46098d9d --- /dev/null +++ b/src/libos/src/fs/fcntl.rs @@ -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 { + 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 { + 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) +} diff --git a/src/libos/src/fs/file.rs b/src/libos/src/fs/file.rs index 7bc9f53d..36f83dcc 100644 --- a/src/libos/src/fs/file.rs +++ b/src/libos/src/fs/file.rs @@ -71,6 +71,18 @@ pub trait File: Debug + Sync + Send + Any { return_op_unsupported_error!("ioctl") } + fn get_access_mode(&self) -> Result { + return_op_unsupported_error!("get_access_mode") + } + + fn get_status_flags(&self) -> Result { + 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; } diff --git a/src/libos/src/fs/file_flags.rs b/src/libos/src/fs/file_flags.rs new file mode 100644 index 00000000..06a0b96a --- /dev/null +++ b/src/libos/src/fs/file_flags.rs @@ -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 { + 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) + } +} diff --git a/src/libos/src/fs/inode_file.rs b/src/libos/src/fs/inode_file.rs index b932df0e..eb9d1694 100644 --- a/src/libos/src/fs/inode_file.rs +++ b/src/libos/src/fs/inode_file.rs @@ -6,20 +6,13 @@ use std::fmt; pub struct INodeFile { inode: Arc, offset: SgxMutex, - 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, } impl File for INodeFile { fn read(&self, buf: &mut [u8]) -> Result { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { + Ok(self.access_mode.clone()) + } + + fn get_status_flags(&self) -> Result { + 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, options: OpenOptions) -> Result { - if (options.read && !inode.allow_read()?) { + pub fn open(inode: Arc, flags: u32) -> Result { + 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() ) } } diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 791a4f3a..80490f20 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -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 { - 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 { let file_ref: Arc> = 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 { } pub fn do_dup3(old_fd: FileDesc, new_fd: FileDesc, flags: u32) -> Result { - 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 Result> { + pub fn open_file(&self, path: &str, flags: u32, mode: u32) -> Result> { 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 { - 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 { 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 { diff --git a/src/libos/src/process/spawn/mod.rs b/src/libos/src/process/spawn/mod.rs index 139493fc..97c9b4eb 100644 --- a/src/libos/src/process/spawn/mod.rs +++ b/src/libos/src/process/spawn/mod.rs @@ -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 { - 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> = 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)?; diff --git a/test/Makefile b/test/Makefile index 0d7c5b7a..2153bdfa 100644 --- a/test/Makefile +++ b/test/Makefile @@ -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 diff --git a/test/fcntl/Makefile b/test/fcntl/Makefile new file mode 100644 index 00000000..9e1b6dec --- /dev/null +++ b/test/fcntl/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := +BIN_ARGS := diff --git a/test/fcntl/main.c b/test/fcntl/main.c new file mode 100644 index 00000000..5dca0e6b --- /dev/null +++ b/test/fcntl/main.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#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)); +}