Add fstatat and openat system calls
This commit is contained in:
parent
66dec604e4
commit
23817fc659
@ -27,10 +27,8 @@ impl AccessibilityCheckFlags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const AT_FDCWD: i32 = -100;
|
|
||||||
|
|
||||||
pub fn do_faccessat(
|
pub fn do_faccessat(
|
||||||
dirfd: Option<FileDesc>,
|
dirfd: DirFd,
|
||||||
path: &str,
|
path: &str,
|
||||||
mode: AccessibilityCheckMode,
|
mode: AccessibilityCheckMode,
|
||||||
flags: AccessibilityCheckFlags,
|
flags: AccessibilityCheckFlags,
|
||||||
@ -41,8 +39,8 @@ pub fn do_faccessat(
|
|||||||
);
|
);
|
||||||
match dirfd {
|
match dirfd {
|
||||||
// TODO: handle dirfd
|
// TODO: handle dirfd
|
||||||
Some(dirfd) => return_errno!(ENOSYS, "cannot accept dirfd"),
|
DirFd::Fd(dirfd) => return_errno!(ENOSYS, "cannot accept dirfd"),
|
||||||
None => do_access(path, mode),
|
DirFd::Cwd => do_access(path, mode),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
40
src/libos/src/fs/file_ops/dirfd.rs
Normal file
40
src/libos/src/fs/file_ops/dirfd.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub const AT_FDCWD: i32 = -100;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DirFd {
|
||||||
|
Cwd,
|
||||||
|
Fd(FileDesc),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirFd {
|
||||||
|
pub fn from_i32(fd: i32) -> Result<DirFd> {
|
||||||
|
let dirfd = if fd >= 0 {
|
||||||
|
DirFd::Fd(fd as FileDesc)
|
||||||
|
} else if fd == AT_FDCWD {
|
||||||
|
DirFd::Cwd
|
||||||
|
} else {
|
||||||
|
return_errno!(EINVAL, "invalid dirfd");
|
||||||
|
};
|
||||||
|
Ok(dirfd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the absolute path of directory
|
||||||
|
pub fn get_dir_path(dirfd: FileDesc) -> Result<String> {
|
||||||
|
let dir_path = {
|
||||||
|
let current_ref = process::get_current();
|
||||||
|
let proc = current_ref.lock().unwrap();
|
||||||
|
let file_ref = proc.get_files().lock().unwrap().get(dirfd)?;
|
||||||
|
if let Ok(inode_file) = file_ref.as_inode_file() {
|
||||||
|
if inode_file.metadata()?.type_ != FileType::Dir {
|
||||||
|
return_errno!(ENOTDIR, "not a directory");
|
||||||
|
}
|
||||||
|
inode_file.get_abs_path().to_owned()
|
||||||
|
} else {
|
||||||
|
return_errno!(EBADF, "not an inode file");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(dir_path)
|
||||||
|
}
|
@ -2,12 +2,11 @@ use super::dev_fs::{DevNull, DevRandom, DevSgx, DevZero};
|
|||||||
use super::*;
|
use super::*;
|
||||||
use process::Process;
|
use process::Process;
|
||||||
|
|
||||||
pub use self::access::{
|
pub use self::access::{do_access, do_faccessat, AccessibilityCheckFlags, AccessibilityCheckMode};
|
||||||
do_access, do_faccessat, AccessibilityCheckFlags, AccessibilityCheckMode, AT_FDCWD,
|
|
||||||
};
|
|
||||||
pub use self::chdir::do_chdir;
|
pub use self::chdir::do_chdir;
|
||||||
pub use self::close::do_close;
|
pub use self::close::do_close;
|
||||||
pub use self::dirent::do_getdents64;
|
pub use self::dirent::do_getdents64;
|
||||||
|
pub use self::dirfd::{get_dir_path, DirFd};
|
||||||
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};
|
||||||
@ -17,12 +16,12 @@ pub use self::ioctl::{do_ioctl, IoctlCmd, StructuredIoctlArgType, StructuredIoct
|
|||||||
pub use self::link::do_link;
|
pub use self::link::do_link;
|
||||||
pub use self::lseek::do_lseek;
|
pub use self::lseek::do_lseek;
|
||||||
pub use self::mkdir::do_mkdir;
|
pub use self::mkdir::do_mkdir;
|
||||||
pub use self::open::do_open;
|
pub use self::open::do_openat;
|
||||||
pub use self::read::{do_pread, do_read, do_readv};
|
pub use self::read::{do_pread, do_read, do_readv};
|
||||||
pub use self::rename::do_rename;
|
pub use self::rename::do_rename;
|
||||||
pub use self::rmdir::do_rmdir;
|
pub use self::rmdir::do_rmdir;
|
||||||
pub use self::sendfile::do_sendfile;
|
pub use self::sendfile::do_sendfile;
|
||||||
pub use self::stat::{do_fstat, do_lstat, do_stat, Stat};
|
pub use self::stat::{do_fstat, do_fstatat, do_lstat, Stat, StatFlags};
|
||||||
pub use self::symlink::do_readlink;
|
pub use self::symlink::do_readlink;
|
||||||
pub use self::truncate::{do_ftruncate, do_truncate};
|
pub use self::truncate::{do_ftruncate, do_truncate};
|
||||||
pub use self::unlink::do_unlink;
|
pub use self::unlink::do_unlink;
|
||||||
@ -32,6 +31,7 @@ mod access;
|
|||||||
mod chdir;
|
mod chdir;
|
||||||
mod close;
|
mod close;
|
||||||
mod dirent;
|
mod dirent;
|
||||||
|
mod dirfd;
|
||||||
mod dup;
|
mod dup;
|
||||||
mod fcntl;
|
mod fcntl;
|
||||||
mod file_flags;
|
mod file_flags;
|
||||||
|
@ -1,11 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc> {
|
fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc> {
|
||||||
info!(
|
|
||||||
"open: path: {:?}, flags: {:#o}, mode: {:#o}",
|
|
||||||
path, flags, mode
|
|
||||||
);
|
|
||||||
|
|
||||||
let current_ref = process::get_current();
|
let current_ref = process::get_current();
|
||||||
let mut proc = current_ref.lock().unwrap();
|
let mut proc = current_ref.lock().unwrap();
|
||||||
|
|
||||||
@ -21,3 +16,22 @@ pub fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc> {
|
|||||||
};
|
};
|
||||||
Ok(fd)
|
Ok(fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn do_openat(dirfd: DirFd, path: &str, flags: u32, mode: u32) -> Result<FileDesc> {
|
||||||
|
info!(
|
||||||
|
"openat: dirfd: {:?}, path: {:?}, flags: {:#o}, mode: {:#o}",
|
||||||
|
dirfd, path, flags, mode
|
||||||
|
);
|
||||||
|
if Path::new(path).is_absolute() {
|
||||||
|
// Path is absolute, so dirfd is ignored
|
||||||
|
return Ok(do_open(path, flags, mode)?);
|
||||||
|
}
|
||||||
|
let path = match dirfd {
|
||||||
|
DirFd::Fd(dirfd) => {
|
||||||
|
let dir_path = get_dir_path(dirfd)?;
|
||||||
|
dir_path + "/" + path
|
||||||
|
}
|
||||||
|
DirFd::Cwd => path.to_owned(),
|
||||||
|
};
|
||||||
|
do_open(&path, flags, mode)
|
||||||
|
}
|
||||||
|
@ -105,6 +105,14 @@ impl StatMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct StatFlags: u32 {
|
||||||
|
const AT_EMPTY_PATH = 1 << 12;
|
||||||
|
const AT_NO_AUTOMOUNT = 1 << 11;
|
||||||
|
const AT_SYMLINK_NOFOLLOW = 1 << 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Metadata> for Stat {
|
impl From<Metadata> for Stat {
|
||||||
fn from(info: Metadata) -> Self {
|
fn from(info: Metadata) -> Self {
|
||||||
Stat {
|
Stat {
|
||||||
@ -126,7 +134,7 @@ impl From<Metadata> for Stat {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_stat(path: &str) -> Result<Stat> {
|
fn do_stat(path: &str) -> Result<Stat> {
|
||||||
warn!("stat is partial implemented as lstat");
|
warn!("stat is partial implemented as lstat");
|
||||||
do_lstat(path)
|
do_lstat(path)
|
||||||
}
|
}
|
||||||
@ -149,3 +157,31 @@ pub fn do_lstat(path: &str) -> Result<Stat> {
|
|||||||
let stat = Stat::from(inode.metadata()?);
|
let stat = Stat::from(inode.metadata()?);
|
||||||
Ok(stat)
|
Ok(stat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn do_fstatat(dirfd: DirFd, path: &str, flags: StatFlags) -> Result<Stat> {
|
||||||
|
info!(
|
||||||
|
"fstatat: dirfd: {:?}, path: {:?}, flags: {:?}",
|
||||||
|
dirfd, path, flags
|
||||||
|
);
|
||||||
|
if path.len() == 0 && !flags.contains(StatFlags::AT_EMPTY_PATH) {
|
||||||
|
return_errno!(ENOENT, "path is an empty string");
|
||||||
|
}
|
||||||
|
if Path::new(path).is_absolute() {
|
||||||
|
// Path is absolute, so dirfd is ignored
|
||||||
|
return Ok(do_stat(path)?);
|
||||||
|
}
|
||||||
|
match dirfd {
|
||||||
|
DirFd::Fd(dirfd) => {
|
||||||
|
if path.len() == 0 {
|
||||||
|
// Path is an empty string, and the flags contiains AT_EMPTY_PATH,
|
||||||
|
// so the behavior of fstatat() is similar to that of fstat().
|
||||||
|
do_fstat(dirfd)
|
||||||
|
} else {
|
||||||
|
let dir_path = get_dir_path(dirfd)?;
|
||||||
|
let path = dir_path + "/" + path;
|
||||||
|
do_stat(&path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DirFd::Cwd => do_stat(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ use std::any::Any;
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
pub use self::dev_fs::AsDevRandom;
|
pub use self::dev_fs::AsDevRandom;
|
||||||
pub use self::file::{File, FileRef};
|
pub use self::file::{File, FileRef};
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use super::file_ops;
|
use super::file_ops;
|
||||||
use super::file_ops::{AccessibilityCheckFlags, AccessibilityCheckMode, FcntlCmd, AT_FDCWD};
|
use super::file_ops::{
|
||||||
|
AccessibilityCheckFlags, AccessibilityCheckMode, DirFd, FcntlCmd, StatFlags,
|
||||||
|
};
|
||||||
use super::fs_ops;
|
use super::fs_ops;
|
||||||
use super::*;
|
use super::*;
|
||||||
use util::mem_util::from_user;
|
use util::mem_util::from_user;
|
||||||
@ -14,7 +16,16 @@ pub fn do_open(path: *const i8, flags: u32, mode: u32) -> Result<isize> {
|
|||||||
let path = from_user::clone_cstring_safely(path)?
|
let path = from_user::clone_cstring_safely(path)?
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.into_owned();
|
.into_owned();
|
||||||
let fd = file_ops::do_open(&path, flags, mode)?;
|
let fd = file_ops::do_openat(DirFd::Cwd, &path, flags, mode)?;
|
||||||
|
Ok(fd as isize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn do_openat(dirfd: i32, path: *const i8, flags: u32, mode: u32) -> Result<isize> {
|
||||||
|
let dirfd = DirFd::from_i32(dirfd)?;
|
||||||
|
let path = from_user::clone_cstring_safely(path)?
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
let fd = file_ops::do_openat(dirfd, &path, flags, mode)?;
|
||||||
Ok(fd as isize)
|
Ok(fd as isize)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +126,7 @@ pub fn do_stat(path: *const i8, stat_buf: *mut Stat) -> Result<isize> {
|
|||||||
.into_owned();
|
.into_owned();
|
||||||
from_user::check_mut_ptr(stat_buf)?;
|
from_user::check_mut_ptr(stat_buf)?;
|
||||||
|
|
||||||
let stat = file_ops::do_stat(&path)?;
|
let stat = file_ops::do_fstatat(DirFd::Cwd, &path, StatFlags::empty())?;
|
||||||
unsafe {
|
unsafe {
|
||||||
stat_buf.write(stat);
|
stat_buf.write(stat);
|
||||||
}
|
}
|
||||||
@ -145,6 +156,20 @@ pub fn do_lstat(path: *const i8, stat_buf: *mut Stat) -> Result<isize> {
|
|||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn do_fstatat(dirfd: i32, path: *const i8, stat_buf: *mut Stat, flags: u32) -> Result<isize> {
|
||||||
|
let dirfd = DirFd::from_i32(dirfd)?;
|
||||||
|
let path = from_user::clone_cstring_safely(path)?
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned();
|
||||||
|
from_user::check_mut_ptr(stat_buf)?;
|
||||||
|
let flags = StatFlags::from_bits_truncate(flags);
|
||||||
|
let stat = file_ops::do_fstatat(dirfd, &path, flags)?;
|
||||||
|
unsafe {
|
||||||
|
stat_buf.write(stat);
|
||||||
|
}
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn do_access(path: *const i8, mode: u32) -> Result<isize> {
|
pub fn do_access(path: *const i8, mode: u32) -> Result<isize> {
|
||||||
let path = from_user::clone_cstring_safely(path)?
|
let path = from_user::clone_cstring_safely(path)?
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
@ -154,13 +179,7 @@ pub fn do_access(path: *const i8, mode: u32) -> Result<isize> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32) -> Result<isize> {
|
pub fn do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32) -> Result<isize> {
|
||||||
let dirfd = if dirfd >= 0 {
|
let dirfd = DirFd::from_i32(dirfd)?;
|
||||||
Some(dirfd as FileDesc)
|
|
||||||
} else if dirfd == AT_FDCWD {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
return_errno!(EINVAL, "invalid dirfd");
|
|
||||||
};
|
|
||||||
let path = from_user::clone_cstring_safely(path)?
|
let path = from_user::clone_cstring_safely(path)?
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.into_owned();
|
.into_owned();
|
||||||
|
@ -60,6 +60,7 @@ pub extern "C" fn dispatch_syscall(
|
|||||||
let ret = match syscall_num {
|
let ret = match syscall_num {
|
||||||
// file
|
// file
|
||||||
SysOpen => fs::do_open(arg0 as *const i8, arg1 as u32, arg2 as u32),
|
SysOpen => fs::do_open(arg0 as *const i8, arg1 as u32, arg2 as u32),
|
||||||
|
SysOpenat => fs::do_openat(arg0 as i32, arg1 as *const i8, arg2 as u32, arg3 as u32),
|
||||||
SysClose => fs::do_close(arg0 as FileDesc),
|
SysClose => fs::do_close(arg0 as FileDesc),
|
||||||
SysRead => fs::do_read(arg0 as FileDesc, arg1 as *mut u8, arg2 as usize),
|
SysRead => fs::do_read(arg0 as FileDesc, arg1 as *mut u8, arg2 as usize),
|
||||||
SysWrite => fs::do_write(arg0 as FileDesc, arg1 as *const u8, arg2 as usize),
|
SysWrite => fs::do_write(arg0 as FileDesc, arg1 as *const u8, arg2 as usize),
|
||||||
@ -80,6 +81,12 @@ pub extern "C" fn dispatch_syscall(
|
|||||||
SysStat => fs::do_stat(arg0 as *const i8, arg1 as *mut Stat),
|
SysStat => fs::do_stat(arg0 as *const i8, arg1 as *mut Stat),
|
||||||
SysFstat => fs::do_fstat(arg0 as FileDesc, arg1 as *mut Stat),
|
SysFstat => fs::do_fstat(arg0 as FileDesc, arg1 as *mut Stat),
|
||||||
SysLstat => fs::do_lstat(arg0 as *const i8, arg1 as *mut Stat),
|
SysLstat => fs::do_lstat(arg0 as *const i8, arg1 as *mut Stat),
|
||||||
|
SysNewfstatat => fs::do_fstatat(
|
||||||
|
arg0 as i32,
|
||||||
|
arg1 as *const i8,
|
||||||
|
arg2 as *mut Stat,
|
||||||
|
arg3 as u32,
|
||||||
|
),
|
||||||
SysAccess => fs::do_access(arg0 as *const i8, arg1 as u32),
|
SysAccess => fs::do_access(arg0 as *const i8, arg1 as u32),
|
||||||
SysFaccessat => fs::do_faccessat(arg0 as i32, arg1 as *const i8, arg2 as u32, arg3 as u32),
|
SysFaccessat => fs::do_faccessat(arg0 as i32, arg1 as *const i8, arg2 as u32, arg3 as u32),
|
||||||
SysLseek => fs::do_lseek(arg0 as FileDesc, arg1 as off_t, arg2 as i32),
|
SysLseek => fs::do_lseek(arg0 as FileDesc, arg1 as off_t, arg2 as i32),
|
||||||
|
@ -11,8 +11,8 @@ endif
|
|||||||
# Dependencies: need to be compiled but not to run by any Makefile target
|
# Dependencies: need to be compiled but not to run by any Makefile target
|
||||||
TEST_DEPS := client data_sink
|
TEST_DEPS := client data_sink
|
||||||
# Tests: need to be compiled and run by test-% target
|
# Tests: need to be compiled and run by test-% target
|
||||||
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 symlink tls pthread uname rlimit server \
|
truncate readdir mkdir open stat link symlink 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 fcntl
|
ioctl fcntl
|
||||||
# Benchmarks: need to be compiled and run by bench-% target
|
# Benchmarks: need to be compiled and run by bench-% target
|
||||||
|
5
test/open/Makefile
Normal file
5
test/open/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
include ../test_common.mk
|
||||||
|
|
||||||
|
EXTRA_C_FLAGS :=
|
||||||
|
EXTRA_LINK_FLAGS :=
|
||||||
|
BIN_ARGS :=
|
111
test/open/main.c
Normal file
111
test/open/main.c
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
#include <fcntl.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Helper function
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static int remove_file(const char *file_path) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = unlink(file_path);
|
||||||
|
if (ret < 0) {
|
||||||
|
THROW_ERROR("failed to unlink the created file");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Test cases for open
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static int __test_open(const char *file_path, int flags, int mode) {
|
||||||
|
int fd = open(file_path, flags, mode);
|
||||||
|
if (fd < 0) {
|
||||||
|
THROW_ERROR("failed to open a file");
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __test_openat_with_abs_path(const char *file_path, int flags, int mode) {
|
||||||
|
int fd = openat(AT_FDCWD, file_path, flags, mode);
|
||||||
|
if (fd < 0) {
|
||||||
|
THROW_ERROR("failed to openat a file with abs path");
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __test_openat_with_dirfd(const char *file_path, int flags, int mode) {
|
||||||
|
char dir_buf[128] = { 0 };
|
||||||
|
char base_buf[128] = { 0 };
|
||||||
|
char *dir_name, *file_name;
|
||||||
|
int dirfd, fd, ret;
|
||||||
|
|
||||||
|
ret = snprintf(dir_buf, sizeof(dir_buf), "%s", file_path);
|
||||||
|
if (ret >= sizeof(dir_buf) || ret < 0) {
|
||||||
|
THROW_ERROR("failed to copy file path to the dir buffer");
|
||||||
|
}
|
||||||
|
ret = snprintf(base_buf, sizeof(base_buf), "%s", file_path);
|
||||||
|
if (ret >= sizeof(base_buf) || ret < 0) {
|
||||||
|
THROW_ERROR("failed to copy file path to the base buffer");
|
||||||
|
}
|
||||||
|
dir_name = dirname(dir_buf);
|
||||||
|
file_name = basename(base_buf);
|
||||||
|
dirfd = open(dir_name, O_RDONLY);
|
||||||
|
if (dirfd < 0) {
|
||||||
|
THROW_ERROR("failed to open dir");
|
||||||
|
}
|
||||||
|
fd = openat(dirfd, file_name, flags, mode);
|
||||||
|
if (fd < 0) {
|
||||||
|
close(dirfd);
|
||||||
|
THROW_ERROR("failed to openat a file with dirfd");
|
||||||
|
}
|
||||||
|
close(dirfd);
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int(*test_open_func_t)(const char *, int, int);
|
||||||
|
|
||||||
|
static int test_open_framework(test_open_func_t fn) {
|
||||||
|
const char *file_path = "/root/test_filesystem_open.txt";
|
||||||
|
int flags = O_RDONLY | O_CREAT| O_TRUNC;
|
||||||
|
int mode = 00666;
|
||||||
|
|
||||||
|
if (fn(file_path, flags, mode) < 0)
|
||||||
|
return -1;
|
||||||
|
if (remove_file(file_path) < 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_open() {
|
||||||
|
return test_open_framework(__test_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_openat_with_abs_path() {
|
||||||
|
return test_open_framework(__test_openat_with_abs_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_openat_with_dirfd() {
|
||||||
|
return test_open_framework(__test_openat_with_dirfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Test suite main
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static test_case_t test_cases[] = {
|
||||||
|
TEST_CASE(test_open),
|
||||||
|
TEST_CASE(test_openat_with_abs_path),
|
||||||
|
TEST_CASE(test_openat_with_dirfd),
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, const char *argv[]) {
|
||||||
|
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
|
||||||
|
}
|
5
test/stat/Makefile
Normal file
5
test/stat/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
include ../test_common.mk
|
||||||
|
|
||||||
|
EXTRA_C_FLAGS :=
|
||||||
|
EXTRA_LINK_FLAGS :=
|
||||||
|
BIN_ARGS :=
|
196
test/stat/main.c
Normal file
196
test/stat/main.c
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
#include <sys/stat.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Helper function
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static int create_file(const char *file_path) {
|
||||||
|
int fd;
|
||||||
|
int flags = O_RDONLY | O_CREAT| O_TRUNC;
|
||||||
|
int mode = 00666;
|
||||||
|
|
||||||
|
fd = open(file_path, flags, mode);
|
||||||
|
if (fd < 0) {
|
||||||
|
THROW_ERROR("failed to create a file");
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int remove_file(const char *file_path) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = unlink(file_path);
|
||||||
|
if (ret < 0) {
|
||||||
|
THROW_ERROR("failed to unlink the created file");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Test cases for stat
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static int __test_stat(const char *file_path) {
|
||||||
|
struct stat stat_buf;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = stat(file_path, &stat_buf);
|
||||||
|
if (ret < 0) {
|
||||||
|
THROW_ERROR("failed to stat file");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __test_fstat(const char *file_path) {
|
||||||
|
struct stat stat_buf;
|
||||||
|
int fd, ret;
|
||||||
|
int flags = O_RDONLY;
|
||||||
|
|
||||||
|
fd = open(file_path, flags);
|
||||||
|
if (fd < 0) {
|
||||||
|
THROW_ERROR("failed to open file");
|
||||||
|
}
|
||||||
|
ret = fstat(fd, &stat_buf);
|
||||||
|
if (ret < 0) {
|
||||||
|
close(fd);
|
||||||
|
THROW_ERROR("failed to fstat file");
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __test_lstat(const char *file_path) {
|
||||||
|
struct stat stat_buf;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = lstat(file_path, &stat_buf);
|
||||||
|
if (ret < 0) {
|
||||||
|
THROW_ERROR("failed to lstat file");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __test_fstatat_with_abs_path(const char *file_path) {
|
||||||
|
struct stat stat_buf;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = fstatat(AT_FDCWD, file_path, &stat_buf, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
THROW_ERROR("failed to fstatat file with abs path");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __test_fstatat_with_empty_path(const char *file_path) {
|
||||||
|
struct stat stat_buf;
|
||||||
|
int fd, ret;
|
||||||
|
|
||||||
|
ret = fstatat(AT_FDCWD, "", &stat_buf, 0);
|
||||||
|
if (!(ret < 0 && errno == ENOENT)) {
|
||||||
|
THROW_ERROR("fstatat with empty path should return ENOENT");
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(file_path, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
THROW_ERROR("failed to open file");
|
||||||
|
}
|
||||||
|
ret = fstatat(fd, "", &stat_buf, AT_EMPTY_PATH);
|
||||||
|
if (ret < 0) {
|
||||||
|
close(fd);
|
||||||
|
THROW_ERROR("failed to fstatat empty path with AT_EMPTY_PATH flags");
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __test_fstatat_with_dirfd(const char *file_path) {
|
||||||
|
struct stat stat_buf;
|
||||||
|
char dir_buf[128] = { 0 };
|
||||||
|
char base_buf[128] = { 0 };
|
||||||
|
char *dir_name, *file_name;
|
||||||
|
int dirfd, ret;
|
||||||
|
|
||||||
|
ret = snprintf(dir_buf, sizeof(dir_buf), "%s", file_path);
|
||||||
|
if (ret >= sizeof(dir_buf) || ret < 0) {
|
||||||
|
THROW_ERROR("failed to copy file path to the dir buffer");
|
||||||
|
}
|
||||||
|
ret = snprintf(base_buf, sizeof(base_buf), "%s", file_path);
|
||||||
|
if (ret >= sizeof(base_buf) || ret < 0) {
|
||||||
|
THROW_ERROR("failed to copy file path to the base buffer");
|
||||||
|
}
|
||||||
|
dir_name = dirname(dir_buf);
|
||||||
|
file_name = basename(base_buf);
|
||||||
|
dirfd = open(dir_name, O_RDONLY);
|
||||||
|
if (dirfd < 0) {
|
||||||
|
THROW_ERROR("failed to open dir");
|
||||||
|
}
|
||||||
|
ret = fstatat(dirfd, file_name, &stat_buf, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
close(dirfd);
|
||||||
|
THROW_ERROR("failed to fstatat file with dirfd");
|
||||||
|
}
|
||||||
|
close(dirfd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int(*test_stat_func_t)(const char *);
|
||||||
|
|
||||||
|
static int test_stat_framework(test_stat_func_t fn) {
|
||||||
|
const char *file_path = "/root/test_filesystem_stat.txt";
|
||||||
|
|
||||||
|
if (create_file(file_path) < 0)
|
||||||
|
return -1;
|
||||||
|
if (fn(file_path) < 0)
|
||||||
|
return -1;
|
||||||
|
if (remove_file(file_path) < 0)
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_stat() {
|
||||||
|
return test_stat_framework(__test_stat);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_fstat() {
|
||||||
|
return test_stat_framework(__test_fstat);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_lstat() {
|
||||||
|
return test_stat_framework(__test_lstat);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_fstatat_with_abs_path() {
|
||||||
|
return test_stat_framework(__test_fstatat_with_abs_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_fstatat_with_empty_path() {
|
||||||
|
return test_stat_framework(__test_fstatat_with_empty_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_fstatat_with_dirfd() {
|
||||||
|
return test_stat_framework(__test_fstatat_with_dirfd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Test suite main
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static test_case_t test_cases[] = {
|
||||||
|
TEST_CASE(test_stat),
|
||||||
|
TEST_CASE(test_fstat),
|
||||||
|
TEST_CASE(test_lstat),
|
||||||
|
TEST_CASE(test_fstatat_with_abs_path),
|
||||||
|
TEST_CASE(test_fstatat_with_empty_path),
|
||||||
|
TEST_CASE(test_fstatat_with_dirfd),
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, const char *argv[]) {
|
||||||
|
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user