Add "*at()" system calls

The syscalls implemented in this submission are as follows:
fchmodat, fchownat, linkat, mkdirat, renameat, readlinkat, unlinkat
This commit is contained in:
LI Qing 2020-08-11 11:21:04 +08:00 committed by Tate, Hongliang Tian
parent 9c390923a5
commit 292fc28340
33 changed files with 1225 additions and 562 deletions

@ -38,37 +38,23 @@ impl AccessibilityCheckFlags {
} }
pub fn do_faccessat( pub fn do_faccessat(
dirfd: DirFd, fs_path: &FsPath,
path: &str,
mode: AccessibilityCheckMode, mode: AccessibilityCheckMode,
flags: AccessibilityCheckFlags, flags: AccessibilityCheckFlags,
) -> Result<()> { ) -> Result<()> {
debug!( debug!(
"faccessat: dirfd: {:?}, path: {:?}, mode: {:?}, flags: {:?}", "faccessat: fs_path: {:?}, mode: {:?}, flags: {:?}",
dirfd, path, mode, flags fs_path, mode, flags
); );
let path = match dirfd {
DirFd::Fd(dirfd) => {
let dir_path = get_dir_path(dirfd)?;
dir_path + "/" + path
}
DirFd::Cwd => path.to_owned(),
};
do_access(&path, mode, flags)
}
fn do_access(
path: &str,
mode: AccessibilityCheckMode,
flags: AccessibilityCheckFlags,
) -> Result<()> {
let inode = { let inode = {
let path = fs_path.to_abs_path()?;
let current = current!(); let current = current!();
let fs = current.fs().lock().unwrap(); let fs = current.fs().lock().unwrap();
if flags.contains(AccessibilityCheckFlags::AT_SYMLINK_NOFOLLOW) { if flags.contains(AccessibilityCheckFlags::AT_SYMLINK_NOFOLLOW) {
fs.lookup_inode_no_follow(path)? fs.lookup_inode_no_follow(&path)?
} else { } else {
fs.lookup_inode(path)? fs.lookup_inode(&path)?
} }
}; };
if mode.test_for_exist() { if mode.test_for_exist() {

@ -55,12 +55,27 @@ impl FileMode {
} }
} }
pub fn do_chmod(path: &str, mode: FileMode) -> Result<()> { bitflags! {
debug!("chmod: path: {:?}, mode: {:?}", path, mode); pub struct ChmodFlags: i32 {
const AT_SYMLINK_NOFOLLOW = 0x100;
}
}
pub fn do_fchmodat(fs_path: &FsPath, mode: FileMode, flags: ChmodFlags) -> Result<()> {
debug!(
"fchmodat: fs_path: {:?}, mode: {:#o}, flags: {:?}",
fs_path, mode, flags
);
let inode = { let inode = {
let path = fs_path.to_abs_path()?;
let current = current!(); let current = current!();
let fs = current.fs().lock().unwrap(); let fs = current.fs().lock().unwrap();
fs.lookup_inode(path)? if flags.contains(ChmodFlags::AT_SYMLINK_NOFOLLOW) {
fs.lookup_inode_no_follow(&path)?
} else {
fs.lookup_inode(&path)?
}
}; };
let mut info = inode.metadata()?; let mut info = inode.metadata()?;
info.mode = mode.bits(); info.mode = mode.bits();
@ -69,7 +84,8 @@ pub fn do_chmod(path: &str, mode: FileMode) -> Result<()> {
} }
pub fn do_fchmod(fd: FileDesc, mode: FileMode) -> Result<()> { pub fn do_fchmod(fd: FileDesc, mode: FileMode) -> Result<()> {
debug!("fchmod: fd: {}, mode: {:?}", fd, mode); debug!("fchmod: fd: {}, mode: {:#o}", fd, mode);
let file_ref = current!().file(fd)?; let file_ref = current!().file(fd)?;
let mut info = file_ref.metadata()?; let mut info = file_ref.metadata()?;
info.mode = mode.bits(); info.mode = mode.bits();

@ -1,11 +1,27 @@
use super::*; use super::*;
pub fn do_chown(path: &str, uid: u32, gid: u32) -> Result<()> { bitflags! {
debug!("chown: path: {:?}, uid: {}, gid: {}", path, uid, gid); pub struct ChownFlags: i32 {
const AT_EMPTY_PATH = 0x1000;
const AT_SYMLINK_NOFOLLOW = 0x100;
}
}
pub fn do_fchownat(fs_path: &FsPath, uid: u32, gid: u32, flags: ChownFlags) -> Result<()> {
debug!(
"fchownat: fs_path: {:?}, uid: {}, gid: {}, flags: {:?}",
fs_path, uid, gid, flags
);
let inode = { let inode = {
let path = fs_path.to_abs_path()?;
let current = current!(); let current = current!();
let fs = current.fs().lock().unwrap(); let fs = current.fs().lock().unwrap();
fs.lookup_inode(path)? if flags.contains(ChownFlags::AT_SYMLINK_NOFOLLOW) {
fs.lookup_inode_no_follow(&path)?
} else {
fs.lookup_inode(&path)?
}
}; };
let mut info = inode.metadata()?; let mut info = inode.metadata()?;
info.uid = uid as usize; info.uid = uid as usize;
@ -16,6 +32,7 @@ pub fn do_chown(path: &str, uid: u32, gid: u32) -> Result<()> {
pub fn do_fchown(fd: FileDesc, uid: u32, gid: u32) -> Result<()> { pub fn do_fchown(fd: FileDesc, uid: u32, gid: u32) -> Result<()> {
debug!("fchown: fd: {}, uid: {}, gid: {}", fd, uid, gid); debug!("fchown: fd: {}, uid: {}, gid: {}", fd, uid, gid);
let file_ref = current!().file(fd)?; let file_ref = current!().file(fd)?;
let mut info = file_ref.metadata()?; let mut info = file_ref.metadata()?;
info.uid = uid as usize; info.uid = uid as usize;
@ -23,17 +40,3 @@ pub fn do_fchown(fd: FileDesc, uid: u32, gid: u32) -> Result<()> {
file_ref.set_metadata(&info)?; file_ref.set_metadata(&info)?;
Ok(()) Ok(())
} }
pub fn do_lchown(path: &str, uid: u32, gid: u32) -> Result<()> {
debug!("lchown: path: {:?}, uid: {}, gid: {}", path, uid, gid);
let inode = {
let current = current!();
let fs = current.fs().lock().unwrap();
fs.lookup_inode_no_follow(path)?
};
let mut info = inode.metadata()?;
info.uid = uid as usize;
info.gid = gid as usize;
inode.set_metadata(&info)?;
Ok(())
}

@ -1,38 +0,0 @@
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 file_ref = current!().file(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)
}

@ -0,0 +1,107 @@
use super::*;
pub const AT_FDCWD: i32 = -100;
pub struct FsPath<'a> {
inner: FsPathInner<'a>,
}
#[derive(Debug)]
enum FsPathInner<'a> {
// absolute path
Absolute(&'a str),
// path is relative to Cwd
CwdRelative(&'a str),
// Cwd
Cwd,
// path is relative to DirFd
FdRelative(&'a str, FileDesc),
// Fd
Fd(FileDesc),
}
impl<'a> FsPath<'a> {
/// Construct a FsPath
pub fn new(path: &'a str, fd: i32, allow_empty_path: bool) -> Result<Self> {
let fs_path_inner = if Path::new(path).is_absolute() {
FsPathInner::Absolute(path)
} else if fd >= 0 {
if path.is_empty() {
if !allow_empty_path {
return_errno!(ENOENT, "path is an empty string");
}
FsPathInner::Fd(fd as FileDesc)
} else {
let file_ref = current!().file(fd as FileDesc)?;
let inode_file = file_ref
.as_inode_file()
.map_err(|_| errno!(EBADF, "dirfd is not an inode file"))?;
if inode_file.metadata()?.type_ != FileType::Dir {
return_errno!(ENOTDIR, "dirfd is not a directory");
}
FsPathInner::FdRelative(path, fd as FileDesc)
}
} else if fd == AT_FDCWD {
if path.is_empty() {
if !allow_empty_path {
return_errno!(ENOENT, "path is an empty string");
}
FsPathInner::Cwd
} else {
FsPathInner::CwdRelative(path)
}
} else {
return_errno!(EINVAL, "invalid dirfd number");
};
Ok(FsPath {
inner: fs_path_inner,
})
}
/// Convert to absolute path
pub fn to_abs_path(&self) -> Result<String> {
let abs_path = match &self.inner {
FsPathInner::Absolute(path) => (*path).to_owned(),
FsPathInner::FdRelative(path, dirfd) => {
let dir_path = get_abs_path_by_fd(*dirfd)?;
if dir_path.ends_with("/") {
dir_path + path
} else {
dir_path + "/" + path
}
}
FsPathInner::Fd(fd) => get_abs_path_by_fd(*fd)?,
FsPathInner::CwdRelative(path) => {
let current = current!();
let fs = current.fs().lock().unwrap();
fs.convert_to_abs_path(path)
}
FsPathInner::Cwd => {
let current = current!();
let fs = current.fs().lock().unwrap();
fs.cwd().to_owned()
}
};
Ok(abs_path)
}
}
impl<'a> Debug for FsPath<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "FsPath {{ {:?} }}", self.inner)
}
}
/// Get the absolute path by file descriptor
fn get_abs_path_by_fd(fd: FileDesc) -> Result<String> {
let path = {
let file_ref = current!().file(fd)?;
if let Ok(inode_file) = file_ref.as_inode_file() {
inode_file.get_abs_path().to_owned()
} else {
return_errno!(EBADF, "not an inode file");
}
};
Ok(path)
}

@ -1,13 +1,29 @@
use super::*; use super::*;
pub fn do_link(oldpath: &str, newpath: &str) -> Result<()> { bitflags! {
debug!("link: oldpath: {:?}, newpath: {:?}", oldpath, newpath); pub struct LinkFlags: i32 {
const AT_EMPTY_PATH = 0x1000;
const AT_SYMLINK_FOLLOW = 0x400;
}
}
pub fn do_linkat(old_fs_path: &FsPath, new_fs_path: &FsPath, flags: LinkFlags) -> Result<()> {
debug!(
"linkat: old_fs_path: {:?}, new_fs_path: {:?}, flags:{:?}",
old_fs_path, new_fs_path, flags
);
let newpath = new_fs_path.to_abs_path()?;
let (new_dir_path, new_file_name) = split_path(&newpath); let (new_dir_path, new_file_name) = split_path(&newpath);
let (inode, new_dir_inode) = { let (inode, new_dir_inode) = {
let oldpath = old_fs_path.to_abs_path()?;
let current = current!(); let current = current!();
let fs = current.fs().lock().unwrap(); let fs = current.fs().lock().unwrap();
let inode = fs.lookup_inode_no_follow(&oldpath)?; let inode = if flags.contains(LinkFlags::AT_SYMLINK_FOLLOW) {
fs.lookup_inode(&oldpath)?
} else {
fs.lookup_inode_no_follow(&oldpath)?
};
let new_dir_inode = fs.lookup_inode(new_dir_path)?; let new_dir_inode = fs.lookup_inode(new_dir_path)?;
(inode, new_dir_inode) (inode, new_dir_inode)
}; };

@ -1,9 +1,9 @@
use super::*; use super::*;
pub fn do_mkdir(path: &str, mode: usize) -> Result<()> { pub fn do_mkdirat(fs_path: &FsPath, mode: usize) -> Result<()> {
// TODO: check pathname debug!("mkdirat: fs_path: {:?}, mode: {:#o}", fs_path, mode);
debug!("mkdir: path: {:?}, mode: {:#o}", path, mode);
let path = fs_path.to_abs_path()?;
let (dir_path, file_name) = split_path(&path); let (dir_path, file_name) = split_path(&path);
let inode = { let inode = {
let current = current!(); let current = current!();

@ -3,32 +3,32 @@ use super::*;
use process::Process; use process::Process;
pub use self::access::{do_faccessat, AccessibilityCheckFlags, AccessibilityCheckMode}; pub use self::access::{do_faccessat, AccessibilityCheckFlags, AccessibilityCheckMode};
pub use self::chmod::{do_chmod, do_fchmod, FileMode}; pub use self::chmod::{do_fchmod, do_fchmodat, ChmodFlags, FileMode};
pub use self::chown::{do_chown, do_fchown, do_lchown}; pub use self::chown::{do_fchown, do_fchownat, ChownFlags};
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};
pub use self::flock::{Flock, FlockType}; pub use self::flock::{Flock, FlockType};
pub use self::fspath::{FsPath, AT_FDCWD};
pub use self::fsync::{do_fdatasync, do_fsync}; pub use self::fsync::{do_fdatasync, do_fsync};
pub use self::ioctl::{ pub use self::ioctl::{
do_ioctl, occlum_ocall_ioctl, BuiltinIoctlNum, IfConf, IoctlCmd, StructuredIoctlArgType, do_ioctl, occlum_ocall_ioctl, BuiltinIoctlNum, IfConf, IoctlCmd, StructuredIoctlArgType,
StructuredIoctlNum, StructuredIoctlNum,
}; };
pub use self::link::do_link; pub use self::link::{do_linkat, LinkFlags};
pub use self::lseek::do_lseek; pub use self::lseek::do_lseek;
pub use self::mkdir::do_mkdir; pub use self::mkdir::do_mkdirat;
pub use self::open::do_openat; 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_renameat;
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_fstatat, do_lstat, Stat, StatFlags}; pub use self::stat::{do_fstat, do_fstatat, Stat, StatFlags};
pub use self::symlink::{do_readlink, do_symlinkat}; pub use self::symlink::{do_readlinkat, do_symlinkat};
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_unlinkat, UnlinkFlags};
pub use self::write::{do_pwrite, do_write, do_writev}; pub use self::write::{do_pwrite, do_write, do_writev};
mod access; mod access;
@ -36,11 +36,11 @@ mod chmod;
mod chown; mod chown;
mod close; mod close;
mod dirent; mod dirent;
mod dirfd;
mod dup; mod dup;
mod fcntl; mod fcntl;
mod file_flags; mod file_flags;
mod flock; mod flock;
mod fspath;
mod fsync; mod fsync;
mod ioctl; mod ioctl;
mod link; mod link;
@ -56,14 +56,3 @@ mod symlink;
mod truncate; mod truncate;
mod unlink; mod unlink;
mod write; mod write;
/// Split a `path` str to `(base_path, file_name)`
pub fn split_path(path: &str) -> (&str, &str) {
let mut split = path.trim_end_matches('/').rsplitn(2, '/');
let file_name = split.next().unwrap();
let mut dir_path = split.next().unwrap_or(".");
if dir_path == "" {
dir_path = "/";
}
(dir_path, file_name)
}

@ -1,10 +1,16 @@
use super::*; use super::*;
fn do_open(path: &str, flags: u32, mode: u32) -> Result<FileDesc> { pub fn do_openat(fs_path: &FsPath, flags: u32, mode: u32) -> Result<FileDesc> {
debug!(
"openat: fs_path: {:?}, flags: {:#o}, mode: {:#o}",
fs_path, flags, mode
);
let path = fs_path.to_abs_path()?;
let current = current!(); let current = current!();
let fs = current.fs().lock().unwrap(); let fs = current.fs().lock().unwrap();
let file = fs.open_file(path, flags, mode)?; let file = fs.open_file(&path, flags, mode)?;
let file_ref: Arc<Box<dyn File>> = Arc::new(file); let file_ref: Arc<Box<dyn File>> = Arc::new(file);
let fd = { let fd = {
@ -13,18 +19,3 @@ 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> {
debug!(
"openat: dirfd: {:?}, path: {:?}, flags: {:#o}, mode: {:#o}",
dirfd, 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)
}

@ -1,8 +1,13 @@
use super::*; use super::*;
pub fn do_rename(oldpath: &str, newpath: &str) -> Result<()> { pub fn do_renameat(old_fs_path: &FsPath, new_fs_path: &FsPath) -> Result<()> {
debug!("rename: oldpath: {:?}, newpath: {:?}", oldpath, newpath); debug!(
"renameat: old_fs_path: {:?}, new_fs_path: {:?}",
old_fs_path, new_fs_path
);
let oldpath = old_fs_path.to_abs_path()?;
let newpath = new_fs_path.to_abs_path()?;
let current = current!(); let current = current!();
let fs = current.fs().lock().unwrap(); let fs = current.fs().lock().unwrap();

@ -134,17 +134,6 @@ impl From<Metadata> for Stat {
} }
} }
fn do_stat(path: &str) -> Result<Stat> {
debug!("stat: path: {}", path);
let inode = {
let current = current!();
let fs = current.fs().lock().unwrap();
fs.lookup_inode(&path)?
};
let stat = Stat::from(inode.metadata()?);
Ok(stat)
}
pub fn do_fstat(fd: u32) -> Result<Stat> { pub fn do_fstat(fd: u32) -> Result<Stat> {
debug!("fstat: fd: {}", fd); debug!("fstat: fd: {}", fd);
let file_ref = current!().file(fd as FileDesc)?; let file_ref = current!().file(fd as FileDesc)?;
@ -152,42 +141,19 @@ pub fn do_fstat(fd: u32) -> Result<Stat> {
Ok(stat) Ok(stat)
} }
pub fn do_lstat(path: &str) -> Result<Stat> { pub fn do_fstatat(fs_path: &FsPath, flags: StatFlags) -> Result<Stat> {
debug!("lstat: path: {}", path); debug!("fstatat: fs_path: {:?}, flags: {:?}", fs_path, flags);
let inode = { let inode = {
let path = fs_path.to_abs_path()?;
let current = current!(); let current = current!();
let fs = current.fs().lock().unwrap(); let fs = current.fs().lock().unwrap();
fs.lookup_inode_no_follow(&path)? if flags.contains(StatFlags::AT_SYMLINK_NOFOLLOW) {
fs.lookup_inode_no_follow(&path)?
} else {
fs.lookup_inode(&path)?
}
}; };
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> {
debug!(
"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");
}
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;
if !flags.contains(StatFlags::AT_SYMLINK_NOFOLLOW) {
do_stat(&path)
} else {
do_lstat(&path)
}
}
}
DirFd::Cwd if !flags.contains(StatFlags::AT_SYMLINK_NOFOLLOW) => do_stat(path),
DirFd::Cwd => do_lstat(path),
}
}

@ -1,15 +1,17 @@
use super::*; use super::*;
pub fn do_readlink(path: &str, buf: &mut [u8]) -> Result<usize> { pub fn do_readlinkat(fs_path: &FsPath, buf: &mut [u8]) -> Result<usize> {
debug!("readlink: path: {:?}", path); debug!("readlinkat: fs_path: {:?}", fs_path);
let path = fs_path.to_abs_path()?;
let file_path = { let file_path = {
if path == "/proc/self/exe" { if path.as_str() == "/proc/self/exe" {
current!().process().exec_path().to_owned() current!().process().exec_path().to_owned()
} else if path.starts_with("/proc/self/fd") { } else if path.starts_with("/proc/self/fd") {
let fd = path let fd = path
.trim_start_matches("/proc/self/fd/") .trim_start_matches("/proc/self/fd/")
.parse::<FileDesc>() .parse::<FileDesc>()
.map_err(|e| errno!(EBADF, "Invalid file descriptor"))?; .map_err(|_| errno!(EBADF, "Invalid file descriptor"))?;
let file_ref = current!().file(fd)?; let file_ref = current!().file(fd)?;
if let Ok(inode_file) = file_ref.as_inode_file() { if let Ok(inode_file) = file_ref.as_inode_file() {
inode_file.get_abs_path().to_owned() inode_file.get_abs_path().to_owned()
@ -21,7 +23,7 @@ pub fn do_readlink(path: &str, buf: &mut [u8]) -> Result<usize> {
let inode = { let inode = {
let current = current!(); let current = current!();
let fs = current.fs().lock().unwrap(); let fs = current.fs().lock().unwrap();
fs.lookup_inode_no_follow(path)? fs.lookup_inode_no_follow(&path)?
}; };
if inode.metadata()?.type_ != FileType::SymLink { if inode.metadata()?.type_ != FileType::SymLink {
return_errno!(EINVAL, "not a symbolic link"); return_errno!(EINVAL, "not a symbolic link");
@ -38,7 +40,17 @@ pub fn do_readlink(path: &str, buf: &mut [u8]) -> Result<usize> {
Ok(len) Ok(len)
} }
fn do_symlink(target: &str, link_path: &str) -> Result<usize> { pub fn do_symlinkat(target: &str, link_path: &FsPath) -> Result<usize> {
debug!("symlinkat: target: {}, link_path: {:?}", target, link_path);
if target.is_empty() {
return_errno!(ENOENT, "target is an empty string");
}
if target.len() > PATH_MAX {
return_errno!(ENAMETOOLONG, "target is too long");
}
let link_path = link_path.to_abs_path()?;
let (dir_path, link_name) = split_path(&link_path); let (dir_path, link_name) = split_path(&link_path);
let dir_inode = { let dir_inode = {
let current = current!(); let current = current!();
@ -54,24 +66,3 @@ fn do_symlink(target: &str, link_path: &str) -> Result<usize> {
link_inode.write_at(0, data)?; link_inode.write_at(0, data)?;
Ok(0) Ok(0)
} }
pub fn do_symlinkat(target: &str, new_dirfd: DirFd, link_path: &str) -> Result<usize> {
debug!(
"symlinkat: target: {:?}, new_dirfd: {:?}, link_path: {:?}",
target, new_dirfd, link_path
);
if target.is_empty() || link_path.is_empty() {
return_errno!(ENOENT, "target or linkpath is an empty string");
}
if target.len() > PATH_MAX || link_path.len() > PATH_MAX {
return_errno!(ENAMETOOLONG, "target or linkpath is too long");
}
let link_path = match new_dirfd {
DirFd::Fd(dirfd) => {
let dir_path = get_dir_path(dirfd)?;
dir_path + "/" + link_path
}
DirFd::Cwd => link_path.to_owned(),
};
do_symlink(target, &link_path)
}

@ -1,8 +1,12 @@
use super::*; use super::*;
pub fn do_unlink(path: &str) -> Result<()> { bitflags! {
debug!("unlink: path: {:?}", path); pub struct UnlinkFlags: i32 {
const AT_REMOVEDIR = 0x200;
}
}
fn do_unlink(path: &str) -> Result<()> {
let (dir_path, file_name) = split_path(&path); let (dir_path, file_name) = split_path(&path);
let dir_inode = { let dir_inode = {
let current = current!(); let current = current!();
@ -21,3 +25,14 @@ pub fn do_unlink(path: &str) -> Result<()> {
dir_inode.unlink(file_name)?; dir_inode.unlink(file_name)?;
Ok(()) Ok(())
} }
pub fn do_unlinkat(fs_path: &FsPath, flags: UnlinkFlags) -> Result<()> {
debug!("unlinkat: fs_path: {:?}, flags: {:?}", fs_path, flags);
let abs_path = fs_path.to_abs_path()?;
if flags.contains(UnlinkFlags::AT_REMOVEDIR) {
super::do_rmdir(&abs_path)
} else {
do_unlink(&abs_path)
}
}

@ -1,7 +1,8 @@
use super::event_file::EventCreationFlags; use super::event_file::EventCreationFlags;
use super::file_ops; use super::file_ops;
use super::file_ops::{ use super::file_ops::{
AccessibilityCheckFlags, AccessibilityCheckMode, DirFd, FcntlCmd, StatFlags, AccessibilityCheckFlags, AccessibilityCheckMode, ChmodFlags, ChownFlags, FcntlCmd, FsPath,
LinkFlags, StatFlags, UnlinkFlags, AT_FDCWD,
}; };
use super::fs_ops; use super::fs_ops;
use super::*; use super::*;
@ -35,24 +36,15 @@ pub fn do_eventfd2(init_val: u32, flags: i32) -> Result<isize> {
} }
pub fn do_open(path: *const i8, flags: u32, mode: u32) -> Result<isize> { pub fn do_open(path: *const i8, flags: u32, mode: u32) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)? self::do_openat(AT_FDCWD, path, flags, mode)
.to_string_lossy()
.into_owned();
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> { pub fn do_openat(dirfd: i32, 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 dirfd = if Path::new(&path).is_absolute() { let fs_path = FsPath::new(&path, dirfd, false)?;
// Path is absolute, dirfd is treated as Cwd let fd = file_ops::do_openat(&fs_path, flags, mode)?;
DirFd::Cwd
} else {
DirFd::from_i32(dirfd)?
};
let fd = file_ops::do_openat(dirfd, &path, flags, mode)?;
Ok(fd as isize) Ok(fd as isize)
} }
@ -147,19 +139,6 @@ pub fn do_pwrite(fd: FileDesc, buf: *const u8, size: usize, offset: off_t) -> Re
Ok(len as isize) Ok(len as isize)
} }
pub fn do_stat(path: *const i8, stat_buf: *mut Stat) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)?
.to_string_lossy()
.into_owned();
from_user::check_mut_ptr(stat_buf)?;
let stat = file_ops::do_fstatat(DirFd::Cwd, &path, StatFlags::empty())?;
unsafe {
stat_buf.write(stat);
}
Ok(0)
}
pub fn do_fstat(fd: FileDesc, stat_buf: *mut Stat) -> Result<isize> { pub fn do_fstat(fd: FileDesc, stat_buf: *mut Stat) -> Result<isize> {
from_user::check_mut_ptr(stat_buf)?; from_user::check_mut_ptr(stat_buf)?;
@ -170,32 +149,27 @@ pub fn do_fstat(fd: FileDesc, stat_buf: *mut Stat) -> Result<isize> {
Ok(0) Ok(0)
} }
pub fn do_lstat(path: *const i8, stat_buf: *mut Stat) -> Result<isize> { pub fn do_stat(path: *const i8, stat_buf: *mut Stat) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)? self::do_fstatat(AT_FDCWD, path, stat_buf, 0)
.to_string_lossy() }
.into_owned();
from_user::check_mut_ptr(stat_buf)?;
let stat = file_ops::do_lstat(&path)?; pub fn do_lstat(path: *const i8, stat_buf: *mut Stat) -> Result<isize> {
unsafe { self::do_fstatat(
stat_buf.write(stat); AT_FDCWD,
} path,
Ok(0) stat_buf,
StatFlags::AT_SYMLINK_NOFOLLOW.bits(),
)
} }
pub fn do_fstatat(dirfd: i32, path: *const i8, stat_buf: *mut Stat, flags: u32) -> Result<isize> { pub fn do_fstatat(dirfd: i32, path: *const i8, stat_buf: *mut Stat, flags: 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 dirfd = if Path::new(&path).is_absolute() { let flags = StatFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
// Path is absolute, dirfd is treated as Cwd let fs_path = FsPath::new(&path, dirfd, flags.contains(StatFlags::AT_EMPTY_PATH))?;
DirFd::Cwd
} else {
DirFd::from_i32(dirfd)?
};
from_user::check_mut_ptr(stat_buf)?; from_user::check_mut_ptr(stat_buf)?;
let flags = StatFlags::from_bits_truncate(flags); let stat = file_ops::do_fstatat(&fs_path, flags)?;
let stat = file_ops::do_fstatat(dirfd, &path, flags)?;
unsafe { unsafe {
stat_buf.write(stat); stat_buf.write(stat);
} }
@ -203,27 +177,17 @@ pub fn do_fstatat(dirfd: i32, path: *const i8, stat_buf: *mut Stat, flags: u32)
} }
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)? self::do_faccessat(AT_FDCWD, path, mode, 0)
.to_string_lossy()
.into_owned();
let mode = AccessibilityCheckMode::from_u32(mode)?;
let flags = AccessibilityCheckFlags::empty();
file_ops::do_faccessat(DirFd::Cwd, &path, mode, flags).map(|_| 0)
} }
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 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 dirfd = if Path::new(&path).is_absolute() { let fs_path = FsPath::new(&path, dirfd, false)?;
// Path is absolute, dirfd is treated as Cwd
DirFd::Cwd
} else {
DirFd::from_i32(dirfd)?
};
let mode = AccessibilityCheckMode::from_u32(mode)?; let mode = AccessibilityCheckMode::from_u32(mode)?;
let flags = AccessibilityCheckFlags::from_u32(flags)?; let flags = AccessibilityCheckFlags::from_u32(flags)?;
file_ops::do_faccessat(dirfd, &path, mode, flags).map(|_| 0) file_ops::do_faccessat(&fs_path, mode, flags).map(|_| 0)
} }
pub fn do_lseek(fd: FileDesc, offset: off_t, whence: i32) -> Result<isize> { pub fn do_lseek(fd: FileDesc, offset: off_t, whence: i32) -> Result<isize> {
@ -346,21 +310,37 @@ pub fn do_getcwd(buf_ptr: *mut u8, size: usize) -> Result<isize> {
} }
pub fn do_rename(oldpath: *const i8, newpath: *const i8) -> Result<isize> { pub fn do_rename(oldpath: *const i8, newpath: *const i8) -> Result<isize> {
self::do_renameat(AT_FDCWD, oldpath, AT_FDCWD, newpath)
}
pub fn do_renameat(
olddirfd: i32,
oldpath: *const i8,
newdirfd: i32,
newpath: *const i8,
) -> Result<isize> {
let oldpath = from_user::clone_cstring_safely(oldpath)? let oldpath = from_user::clone_cstring_safely(oldpath)?
.to_string_lossy() .to_string_lossy()
.into_owned(); .into_owned();
let newpath = from_user::clone_cstring_safely(newpath)? let newpath = from_user::clone_cstring_safely(newpath)?
.to_string_lossy() .to_string_lossy()
.into_owned(); .into_owned();
file_ops::do_rename(&oldpath, &newpath)?; let old_fs_path = FsPath::new(&oldpath, olddirfd, false)?;
let new_fs_path = FsPath::new(&newpath, newdirfd, false)?;
file_ops::do_renameat(&old_fs_path, &new_fs_path)?;
Ok(0) Ok(0)
} }
pub fn do_mkdir(path: *const i8, mode: usize) -> Result<isize> { pub fn do_mkdir(path: *const i8, mode: usize) -> Result<isize> {
self::do_mkdirat(AT_FDCWD, path, mode)
}
pub fn do_mkdirat(dirfd: i32, path: *const i8, mode: usize) -> 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();
file_ops::do_mkdir(&path, mode)?; let fs_path = FsPath::new(&path, dirfd, false)?;
file_ops::do_mkdirat(&fs_path, mode)?;
Ok(0) Ok(0)
} }
@ -373,25 +353,49 @@ pub fn do_rmdir(path: *const i8) -> Result<isize> {
} }
pub fn do_link(oldpath: *const i8, newpath: *const i8) -> Result<isize> { pub fn do_link(oldpath: *const i8, newpath: *const i8) -> Result<isize> {
self::do_linkat(AT_FDCWD, oldpath, AT_FDCWD, newpath, 0)
}
pub fn do_linkat(
olddirfd: i32,
oldpath: *const i8,
newdirfd: i32,
newpath: *const i8,
flags: i32,
) -> Result<isize> {
let oldpath = from_user::clone_cstring_safely(oldpath)? let oldpath = from_user::clone_cstring_safely(oldpath)?
.to_string_lossy() .to_string_lossy()
.into_owned(); .into_owned();
let newpath = from_user::clone_cstring_safely(newpath)? let newpath = from_user::clone_cstring_safely(newpath)?
.to_string_lossy() .to_string_lossy()
.into_owned(); .into_owned();
file_ops::do_link(&oldpath, &newpath)?; let flags = LinkFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
let old_fs_path = FsPath::new(&oldpath, olddirfd, flags.contains(LinkFlags::AT_EMPTY_PATH))?;
let new_fs_path = FsPath::new(&newpath, newdirfd, false)?;
file_ops::do_linkat(&old_fs_path, &new_fs_path, flags)?;
Ok(0) Ok(0)
} }
pub fn do_unlink(path: *const i8) -> Result<isize> { pub fn do_unlink(path: *const i8) -> Result<isize> {
self::do_unlinkat(AT_FDCWD, path, 0)
}
pub fn do_unlinkat(dirfd: i32, path: *const i8, flags: i32) -> 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();
file_ops::do_unlink(&path)?; let fs_path = FsPath::new(&path, dirfd, false)?;
let flags =
UnlinkFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flag value"))?;
file_ops::do_unlinkat(&fs_path, flags)?;
Ok(0) Ok(0)
} }
pub fn do_readlink(path: *const i8, buf: *mut u8, size: usize) -> Result<isize> { pub fn do_readlink(path: *const i8, buf: *mut u8, size: usize) -> Result<isize> {
self::do_readlinkat(AT_FDCWD, path, buf, size)
}
pub fn do_readlinkat(dirfd: i32, path: *const i8, buf: *mut u8, size: usize) -> 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();
@ -399,19 +403,13 @@ pub fn do_readlink(path: *const i8, buf: *mut u8, size: usize) -> Result<isize>
from_user::check_array(buf, size)?; from_user::check_array(buf, size)?;
unsafe { std::slice::from_raw_parts_mut(buf, size) } unsafe { std::slice::from_raw_parts_mut(buf, size) }
}; };
let len = file_ops::do_readlink(&path, buf)?; let fs_path = FsPath::new(&path, dirfd, false)?;
let len = file_ops::do_readlinkat(&fs_path, buf)?;
Ok(len as isize) Ok(len as isize)
} }
pub fn do_symlink(target: *const i8, link_path: *const i8) -> Result<isize> { pub fn do_symlink(target: *const i8, link_path: *const i8) -> Result<isize> {
let target = from_user::clone_cstring_safely(target)? self::do_symlinkat(target, AT_FDCWD, link_path)
.to_string_lossy()
.into_owned();
let link_path = from_user::clone_cstring_safely(link_path)?
.to_string_lossy()
.into_owned();
file_ops::do_symlinkat(&target, DirFd::Cwd, &link_path)?;
Ok(0)
} }
pub fn do_symlinkat(target: *const i8, new_dirfd: i32, link_path: *const i8) -> Result<isize> { pub fn do_symlinkat(target: *const i8, new_dirfd: i32, link_path: *const i8) -> Result<isize> {
@ -421,23 +419,13 @@ pub fn do_symlinkat(target: *const i8, new_dirfd: i32, link_path: *const i8) ->
let link_path = from_user::clone_cstring_safely(link_path)? let link_path = from_user::clone_cstring_safely(link_path)?
.to_string_lossy() .to_string_lossy()
.into_owned(); .into_owned();
let new_dirfd = if Path::new(&link_path).is_absolute() { let fs_path = FsPath::new(&link_path, new_dirfd, false)?;
// Link path is absolute, new_dirfd is treated as Cwd file_ops::do_symlinkat(&target, &fs_path)?;
DirFd::Cwd
} else {
DirFd::from_i32(new_dirfd)?
};
file_ops::do_symlinkat(&target, new_dirfd, &link_path)?;
Ok(0) Ok(0)
} }
pub fn do_chmod(path: *const i8, mode: u16) -> Result<isize> { pub fn do_chmod(path: *const i8, mode: u16) -> Result<isize> {
let path = from_user::clone_cstring_safely(path)? self::do_fchmodat(AT_FDCWD, path, mode, 0)
.to_string_lossy()
.into_owned();
let mode = FileMode::from_bits_truncate(mode);
file_ops::do_chmod(&path, mode)?;
Ok(0)
} }
pub fn do_fchmod(fd: FileDesc, mode: u16) -> Result<isize> { pub fn do_fchmod(fd: FileDesc, mode: u16) -> Result<isize> {
@ -446,27 +434,46 @@ pub fn do_fchmod(fd: FileDesc, mode: u16) -> Result<isize> {
Ok(0) Ok(0)
} }
pub fn do_chown(path: *const i8, uid: u32, gid: u32) -> Result<isize> { pub fn do_fchmodat(dirfd: i32, path: *const i8, mode: u16, flags: i32) -> 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();
file_ops::do_chown(&path, uid, gid)?; let mode = FileMode::from_bits_truncate(mode);
let fs_path = FsPath::new(&path, dirfd, false)?;
let flags = ChmodFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
file_ops::do_fchmodat(&fs_path, mode, flags)?;
Ok(0) Ok(0)
} }
pub fn do_chown(path: *const i8, uid: u32, gid: u32) -> Result<isize> {
self::do_fchownat(AT_FDCWD, path, uid, gid, 0)
}
pub fn do_fchown(fd: FileDesc, uid: u32, gid: u32) -> Result<isize> { pub fn do_fchown(fd: FileDesc, uid: u32, gid: u32) -> Result<isize> {
file_ops::do_fchown(fd, uid, gid)?; file_ops::do_fchown(fd, uid, gid)?;
Ok(0) Ok(0)
} }
pub fn do_lchown(path: *const i8, uid: u32, gid: u32) -> Result<isize> { pub fn do_fchownat(dirfd: i32, path: *const i8, uid: u32, gid: u32, flags: i32) -> 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();
file_ops::do_lchown(&path, uid, gid)?; let flags = ChownFlags::from_bits(flags).ok_or_else(|| errno!(EINVAL, "invalid flags"))?;
let fs_path = FsPath::new(&path, dirfd, flags.contains(ChownFlags::AT_EMPTY_PATH))?;
file_ops::do_fchownat(&fs_path, uid, gid, flags)?;
Ok(0) Ok(0)
} }
pub fn do_lchown(path: *const i8, uid: u32, gid: u32) -> Result<isize> {
self::do_fchownat(
AT_FDCWD,
path,
uid,
gid,
ChownFlags::AT_SYMLINK_NOFOLLOW.bits(),
)
}
pub fn do_sendfile( pub fn do_sendfile(
out_fd: FileDesc, out_fd: FileDesc,
in_fd: FileDesc, in_fd: FileDesc,

@ -23,11 +23,12 @@ use util::mem_util::from_user::*;
use crate::exception::do_handle_exception; use crate::exception::do_handle_exception;
use crate::fs::{ use crate::fs::{
do_access, do_chdir, do_chmod, do_chown, do_close, do_dup, do_dup2, do_dup3, do_eventfd, do_access, do_chdir, do_chmod, do_chown, do_close, do_dup, do_dup2, do_dup3, do_eventfd,
do_eventfd2, do_faccessat, do_fchmod, do_fchown, do_fcntl, do_fdatasync, do_fstat, do_fstatat, do_eventfd2, do_faccessat, do_fchmod, do_fchmodat, do_fchown, do_fchownat, do_fcntl,
do_fsync, do_ftruncate, do_getcwd, do_getdents64, do_ioctl, do_lchown, do_link, do_lseek, do_fdatasync, do_fstat, do_fstatat, do_fsync, do_ftruncate, do_getcwd, do_getdents64, do_ioctl,
do_lstat, do_mkdir, do_open, do_openat, do_pipe, do_pipe2, do_pread, do_pwrite, do_read, do_lchown, do_link, do_linkat, do_lseek, do_lstat, do_mkdir, do_mkdirat, do_open, do_openat,
do_readlink, do_readv, do_rename, do_rmdir, do_sendfile, do_stat, do_symlink, do_symlinkat, do_pipe, do_pipe2, do_pread, do_pwrite, do_read, do_readlink, do_readlinkat, do_readv,
do_sync, do_truncate, do_unlink, do_write, do_writev, iovec_t, File, FileDesc, FileRef, do_rename, do_renameat, do_rmdir, do_sendfile, do_stat, do_symlink, do_symlinkat, do_sync,
do_truncate, do_unlink, do_unlinkat, do_write, do_writev, iovec_t, File, FileDesc, FileRef,
HostStdioFds, Stat, HostStdioFds, Stat,
}; };
use crate::interrupt::{do_handle_interrupt, sgx_interrupt_info_t}; use crate::interrupt::{do_handle_interrupt, sgx_interrupt_info_t};
@ -341,17 +342,17 @@ macro_rules! process_syscall_table_with_callback {
(InotifyRmWatch = 255) => handle_unsupported(), (InotifyRmWatch = 255) => handle_unsupported(),
(MigratePages = 256) => handle_unsupported(), (MigratePages = 256) => handle_unsupported(),
(Openat = 257) => do_openat(dirfd: i32, path: *const i8, flags: u32, mode: u32), (Openat = 257) => do_openat(dirfd: i32, path: *const i8, flags: u32, mode: u32),
(Mkdirat = 258) => handle_unsupported(), (Mkdirat = 258) => do_mkdirat(dirfd: i32, path: *const i8, mode: usize),
(Mknodat = 259) => handle_unsupported(), (Mknodat = 259) => handle_unsupported(),
(Fchownat = 260) => handle_unsupported(), (Fchownat = 260) => do_fchownat(dirfd: i32, path: *const i8, uid: u32, gid: u32, flags: i32),
(Futimesat = 261) => handle_unsupported(), (Futimesat = 261) => handle_unsupported(),
(Fstatat = 262) => do_fstatat(dirfd: i32, path: *const i8, stat_buf: *mut Stat, flags: u32), (Fstatat = 262) => do_fstatat(dirfd: i32, path: *const i8, stat_buf: *mut Stat, flags: u32),
(Unlinkat = 263) => handle_unsupported(), (Unlinkat = 263) => do_unlinkat(dirfd: i32, path: *const i8, flags: i32),
(Renameat = 264) => handle_unsupported(), (Renameat = 264) => do_renameat(olddirfd: i32, oldpath: *const i8, newdirfd: i32, newpath: *const i8),
(Linkat = 265) => handle_unsupported(), (Linkat = 265) => do_linkat(olddirfd: i32, oldpath: *const i8, newdirfd: i32, newpath: *const i8, flags: i32),
(Symlinkat = 266) => do_symlinkat(target: *const i8, new_dirfd: i32, link_path: *const i8), (Symlinkat = 266) => do_symlinkat(target: *const i8, new_dirfd: i32, link_path: *const i8),
(Readlinkat = 267) => handle_unsupported(), (Readlinkat = 267) => do_readlinkat(dirfd: i32, path: *const i8, buf: *mut u8, size: usize),
(Fchmodat = 268) => handle_unsupported(), (Fchmodat = 268) => do_fchmodat(dirfd: i32, path: *const i8, mode: u16, flags: i32),
(Faccessat = 269) => do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32), (Faccessat = 269) => do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32),
(Pselect6 = 270) => handle_unsupported(), (Pselect6 = 270) => handle_unsupported(),
(Ppoll = 271) => handle_unsupported(), (Ppoll = 271) => handle_unsupported(),

@ -18,7 +18,7 @@ TEST_DEPS := client data_sink
TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe time \ TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe time \
truncate readdir mkdir open stat link symlink chmod chown tls pthread uname rlimit \ truncate readdir mkdir open stat link symlink chmod chown tls pthread uname rlimit \
server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \ server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \
ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename
# 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

@ -1,10 +1,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <libgen.h> #include "test_fs.h"
#include <unistd.h>
#include <stdio.h>
#include "test.h"
// ============================================================================ // ============================================================================
// Helper function // Helper function
@ -75,21 +72,14 @@ static int __test_faccessat_with_abs_path(const char *file_path) {
} }
static int __test_faccessat_with_dirfd(const char *file_path) { static int __test_faccessat_with_dirfd(const char *file_path) {
char dir_buf[128] = { 0 }; char dir_buf[PATH_MAX] = { 0 };
char base_buf[128] = { 0 }; char base_buf[PATH_MAX] = { 0 };
char *dir_name, *file_name; char *dir_name, *file_name;
int dirfd, ret; int dirfd;
ret = snprintf(dir_buf, sizeof(dir_buf), "%s", file_path); if (fs_split_path(file_path, dir_buf, &dir_name, base_buf, &file_name) < 0) {
if (ret >= sizeof(dir_buf) || ret < 0) { THROW_ERROR("failed to split path");
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); dirfd = open(dir_name, O_RDONLY);
if (dirfd < 0) { if (dirfd < 0) {
THROW_ERROR("failed to open dir"); THROW_ERROR("failed to open dir");

@ -1,9 +1,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include "test_fs.h"
#include <stdio.h>
#include "test.h"
// ============================================================================ // ============================================================================
// Helper function // Helper function
@ -33,7 +31,7 @@ static int remove_file(const char *file_path) {
} }
// ============================================================================ // ============================================================================
// Test cases for stat // Test cases for chmod
// ============================================================================ // ============================================================================
static int __test_chmod(const char *file_path) { static int __test_chmod(const char *file_path) {
@ -66,7 +64,6 @@ static int __test_fchmod(const char *file_path) {
} }
ret = fchmod(fd, mode); ret = fchmod(fd, mode);
if (ret < 0) { if (ret < 0) {
close(fd);
THROW_ERROR("failed to fchmod file"); THROW_ERROR("failed to fchmod file");
} }
close(fd); close(fd);
@ -80,6 +77,36 @@ static int __test_fchmod(const char *file_path) {
return 0; return 0;
} }
static int __test_fchmodat(const char *file_path) {
struct stat stat_buf;
mode_t mode = 00664;
char dir_buf[PATH_MAX] = { 0 };
char base_buf[PATH_MAX] = { 0 };
char *dir_name, *file_name;
int dirfd, ret;
if (fs_split_path(file_path, dir_buf, &dir_name, base_buf, &file_name) < 0) {
THROW_ERROR("failed to split path");
}
dirfd = open(dir_name, O_RDONLY);
if (dirfd < 0) {
THROW_ERROR("failed to open dir");
}
ret = fchmodat(dirfd, file_name, mode, 0);
if (ret < 0) {
THROW_ERROR("failed to fchmodat file with dirfd");
}
close(dirfd);
ret = stat(file_path, &stat_buf);
if (ret < 0) {
THROW_ERROR("failed to stat file");
}
if ((stat_buf.st_mode & 07777) != mode) {
THROW_ERROR("check fchmodat result failed");
}
return 0;
}
typedef int(*test_chmod_func_t)(const char *); typedef int(*test_chmod_func_t)(const char *);
static int test_chmod_framework(test_chmod_func_t fn) { static int test_chmod_framework(test_chmod_func_t fn) {
@ -105,6 +132,10 @@ static int test_fchmod() {
return test_chmod_framework(__test_fchmod); return test_chmod_framework(__test_fchmod);
} }
static int test_fchmodat() {
return test_chmod_framework(__test_fchmodat);
}
// ============================================================================ // ============================================================================
// Test suite main // Test suite main
// ============================================================================ // ============================================================================
@ -112,6 +143,7 @@ static int test_fchmod() {
static test_case_t test_cases[] = { static test_case_t test_cases[] = {
TEST_CASE(test_chmod), TEST_CASE(test_chmod),
TEST_CASE(test_fchmod), TEST_CASE(test_fchmod),
TEST_CASE(test_fchmodat),
}; };
int main(int argc, const char *argv[]) { int main(int argc, const char *argv[]) {

@ -1,9 +1,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include "test_fs.h"
#include <stdio.h>
#include "test.h"
// ============================================================================ // ============================================================================
// Helper function // Helper function
@ -33,7 +31,7 @@ static int remove_file(const char *file_path) {
} }
// ============================================================================ // ============================================================================
// Test cases for stat // Test cases for chown
// ============================================================================ // ============================================================================
static int __test_chown(const char *file_path) { static int __test_chown(const char *file_path) {
@ -88,7 +86,6 @@ static int __test_fchown(const char *file_path) {
} }
ret = fchown(fd, uid, gid); ret = fchown(fd, uid, gid);
if (ret < 0) { if (ret < 0) {
close(fd);
THROW_ERROR("failed to fchown file"); THROW_ERROR("failed to fchown file");
} }
close(fd); close(fd);
@ -102,6 +99,73 @@ static int __test_fchown(const char *file_path) {
return 0; return 0;
} }
static int __test_fchownat(const char *file_path) {
struct stat stat_buf;
uid_t uid = 100;
gid_t gid = 1000;
char dir_buf[PATH_MAX] = { 0 };
char base_buf[PATH_MAX] = { 0 };
char *dir_name, *file_name;
int dirfd, ret;
if (fs_split_path(file_path, dir_buf, &dir_name, base_buf, &file_name) < 0) {
THROW_ERROR("failed to split path");
}
dirfd = open(dir_name, O_RDONLY);
if (dirfd < 0) {
THROW_ERROR("failed to open dir");
}
ret = fchownat(dirfd, file_name, uid, gid, 0);
if (ret < 0) {
THROW_ERROR("failed to fchownat file with dirfd");
}
close(dirfd);
ret = stat(file_path, &stat_buf);
if (ret < 0) {
THROW_ERROR("failed to stat file");
}
if (stat_buf.st_uid != uid || stat_buf.st_gid != gid) {
THROW_ERROR("check fchownat result failed");
}
return 0;
}
static int __test_fchownat_with_empty_path(const char *file_path) {
struct stat stat_buf;
uid_t uid = 100;
gid_t gid = 1000;
char dir_buf[128] = { 0 };
char *dir_name;
int dirfd, ret;
if (fs_split_path(file_path, dir_buf, &dir_name, NULL, NULL) < 0) {
THROW_ERROR("failed to split path");
}
dirfd = open(dir_name, O_RDONLY);
if (dirfd < 0) {
THROW_ERROR("failed to open dir");
}
ret = fchownat(dirfd, "", uid, gid, 0);
if (!(ret < 0 && errno == ENOENT)) {
THROW_ERROR("fchownat with empty path should return ENOENT");
}
ret = fchownat(dirfd, "", uid, gid, AT_EMPTY_PATH);
if (ret < 0) {
THROW_ERROR("failed to fchownat with empty path");
}
close(dirfd);
ret = stat(dir_name, &stat_buf);
if (ret < 0) {
THROW_ERROR("failed to stat dir");
}
if (stat_buf.st_uid != uid || stat_buf.st_gid != gid) {
THROW_ERROR("check fchownat result failed");
}
return 0;
}
typedef int(*test_chown_func_t)(const char *); typedef int(*test_chown_func_t)(const char *);
static int test_chown_framework(test_chown_func_t fn) { static int test_chown_framework(test_chown_func_t fn) {
@ -131,6 +195,14 @@ static int test_fchown() {
return test_chown_framework(__test_fchown); return test_chown_framework(__test_fchown);
} }
static int test_fchownat() {
return test_chown_framework(__test_fchownat);
}
static int test_fchownat_with_empty_path() {
return test_chown_framework(__test_fchownat_with_empty_path);
}
// ============================================================================ // ============================================================================
// Test suite main // Test suite main
// ============================================================================ // ============================================================================
@ -139,6 +211,8 @@ static test_case_t test_cases[] = {
TEST_CASE(test_chown), TEST_CASE(test_chown),
TEST_CASE(test_lchown), TEST_CASE(test_lchown),
TEST_CASE(test_fchown), TEST_CASE(test_fchown),
TEST_CASE(test_fchownat),
TEST_CASE(test_fchownat_with_empty_path),
}; };
int main(int argc, const char *argv[]) { int main(int argc, const char *argv[]) {

@ -1,8 +1,6 @@
#include <unistd.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include "test_fs.h"
#include "test.h"
// ============================================================================ // ============================================================================
// Helper macro // Helper macro

@ -2,10 +2,7 @@
#include <sys/uio.h> #include <sys/uio.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include "test_fs.h"
#include <string.h>
#include <stdio.h>
#include "test.h"
// ============================================================================ // ============================================================================
// Helper function // Helper function
@ -38,7 +35,6 @@ static int remove_file(const char *file_path) {
static int __test_write_read(const char *file_path) { static int __test_write_read(const char *file_path) {
char *write_str = "Hello World\n"; char *write_str = "Hello World\n";
char read_buf[128] = { 0 };
int fd; int fd;
fd = open(file_path, O_WRONLY); fd = open(file_path, O_WRONLY);
@ -49,17 +45,11 @@ static int __test_write_read(const char *file_path) {
THROW_ERROR("failed to write"); THROW_ERROR("failed to write");
} }
close(fd); close(fd);
fd = open(file_path, O_RDONLY);
if (fd < 0) { if (fs_check_file_content(file_path, write_str) < 0) {
THROW_ERROR("failed to open a file to read"); THROW_ERROR("failed to check file content");
} }
if (read(fd, read_buf, sizeof(read_buf)) != strlen(write_str)) {
THROW_ERROR("failed to read");
}
if (strcmp(write_str, read_buf) != 0) {
THROW_ERROR("the message read from the file is not as it was written");
}
close(fd);
return 0; return 0;
} }

@ -1,10 +1,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include "test_fs.h"
#include <string.h>
#include <stdio.h>
#include "test.h"
// ============================================================================ // ============================================================================
// Helper macros // Helper macros

@ -2,12 +2,8 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <dirent.h> #include <dirent.h>
#include <libgen.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include "test_fs.h"
#include "test.h"
// ============================================================================ // ============================================================================
// Helper function // Helper function
@ -40,7 +36,6 @@ static int remove_file(const char *file_path) {
static int __test_write_read(const char *file_path) { static int __test_write_read(const char *file_path) {
char *write_str = "Write to hostfs successfully!"; char *write_str = "Write to hostfs successfully!";
char read_buf[128] = { 0 };
int fd; int fd;
fd = open(file_path, O_WRONLY); fd = open(file_path, O_WRONLY);
@ -51,17 +46,10 @@ static int __test_write_read(const char *file_path) {
THROW_ERROR("failed to write to the file"); THROW_ERROR("failed to write to the file");
} }
close(fd); close(fd);
fd = open(file_path, O_RDONLY);
if (fd < 0) { if (fs_check_file_content(file_path, write_str) < 0) {
THROW_ERROR("failed to open a file to read"); THROW_ERROR("failed to check file content");
} }
if (read(fd, read_buf, sizeof(read_buf)) != strlen(write_str)) {
THROW_ERROR("failed to read to the file");
}
if (strcmp(write_str, read_buf) != 0) {
THROW_ERROR("the message read from the file is not as it was written");
}
close(fd);
return 0; return 0;
} }
@ -91,17 +79,13 @@ static int __test_rename(const char *file_path) {
static int __test_readdir(const char *file_path) { static int __test_readdir(const char *file_path) {
struct dirent *dp; struct dirent *dp;
DIR *dirp; DIR *dirp;
char base_buf[128] = { 0 }; char base_buf[PATH_MAX] = { 0 };
char *base_name; char *base_name;
bool found = false; bool found = false;
int ret;
ret = snprintf(base_buf, sizeof(base_buf), "%s", file_path); if (fs_split_path(file_path, NULL, NULL, base_buf, &base_name) < 0) {
if (ret >= sizeof(base_buf) || ret < 0) { THROW_ERROR("failed to split path");
THROW_ERROR("failed to copy file path to the base buffer");
} }
base_name = basename(base_buf);
dirp = opendir("/host"); dirp = opendir("/host");
if (dirp == NULL) { if (dirp == NULL) {
THROW_ERROR("failed to open host directory"); THROW_ERROR("failed to open host directory");

@ -1 +0,0 @@
HostFS works!

59
test/include/test_fs.h Normal file

@ -0,0 +1,59 @@
#ifndef __TEST_FS_H
#define __TEST_FS_H
#include <linux/limits.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include <unistd.h>
#include "test.h"
int fs_split_path(const char *path, char *dir_buf, char **dir_name, char *base_buf,
char **base_name) {
size_t ret;
if (path == NULL) {
THROW_ERROR("input path is NULL");
}
if (dir_buf != NULL) {
if (dir_name == NULL) {
THROW_ERROR("dir_name is NULL");
}
ret = snprintf(dir_buf, PATH_MAX, "%s", path);
if (ret >= PATH_MAX || ret < 0) {
THROW_ERROR("failed to copy file path to the dir buffer");
}
*dir_name = dirname(dir_buf);
}
if (base_buf != NULL) {
if (base_name == NULL) {
THROW_ERROR("base_name is NULL");
}
ret = snprintf(base_buf, PATH_MAX, "%s", path);
if (ret >= PATH_MAX || ret < 0) {
THROW_ERROR("failed to copy file path to the base buffer");
}
*base_name = basename(base_buf);
}
return 0;
}
int fs_check_file_content(const char *path, const char *msg) {
char read_buf[128] = { 0 };
int fd = open(path, O_RDONLY);
if (fd < 0) {
THROW_ERROR("failed to open file");
}
size_t len = read(fd, read_buf, sizeof(read_buf));
if (len != strlen(msg)) {
THROW_ERROR("failed to read the msg from file");
}
if (strcmp(msg, read_buf) != 0) {
THROW_ERROR("the message read from the file is not expected");
}
close(fd);
return 0;
}
#endif /* __TEST_FS_H */

@ -1,91 +1,191 @@
#include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include "test_fs.h"
int main(int argc, const char *argv[]) { // ============================================================================
const char *file_name = "/root/test_filesystem_link.txt"; // Helper function
const char *link_name = "/root/link.txt"; // ============================================================================
const char *write_msg = "Hello World\n";
char read_buf[128] = {0};
int ret;
// create a file and write message #define WRITE_MSG "Hello World"
static int create_and_write_file(const char *file_path) {
int fd;
int flags = O_WRONLY | O_CREAT | O_TRUNC; int flags = O_WRONLY | O_CREAT | O_TRUNC;
int mode = 00666; int mode = 00666;
int fd = open(file_name, flags, mode);
fd = open(file_path, flags, mode);
if (fd < 0) { if (fd < 0) {
printf("ERROR: failed to open a file for write\n"); THROW_ERROR("failed to create a file");
return -1;
} }
int len = write(fd, write_msg, strlen(write_msg)); if (write(fd, WRITE_MSG, strlen(WRITE_MSG)) <= 0) {
if (len <= 0) { THROW_ERROR("failed to write to the file");
printf("ERROR: failed to write to the file\n");
return -1;
} }
close(fd); close(fd);
// link
ret = link(file_name, link_name);
if (ret < 0) {
printf("ERROR: failed to link the file\n");
return -1;
}
// read the link file
fd = open(link_name, O_RDONLY, 00666);
if (fd < 0) {
printf("ERROR: failed to open the file for read\n");
return -1;
}
len = read(fd, read_buf, sizeof(read_buf));
if (len != strlen(write_msg)) {
printf("ERROR: failed to read to the file\n");
return -1;
}
ret = strcmp(write_msg, read_buf);
if (ret != 0) {
printf("ERROR: the message read from the file is not as it was written\n");
return -1;
}
// unlink
ret = unlink(link_name);
if (ret < 0) {
printf("ERROR: failed to link the file\n");
return -1;
}
// stat
struct stat stat_buf;
ret = stat(link_name, &stat_buf);
if (!(ret < 0 && errno == ENOENT)) {
printf("ERROR: stat on \"%s\" should return ENOENT", link_name);
return -1;
}
// rename
ret = rename(file_name, link_name);
if (ret < 0) {
printf("ERROR: failed to rename the file");
return -1;
}
// stat
ret = stat(file_name, &stat_buf);
if (!(ret < 0 && errno == ENOENT)) {
printf("ERROR: stat on \"%s\" should return ENOENT", file_name);
return -1;
}
ret = stat(link_name, &stat_buf);
if (ret < 0) {
printf("ERROR: failed to stat the file");
return -1;
}
printf("link, unlink, rename test successful\n");
return 0; 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 link
// ============================================================================
static int __test_link_then_unlink(const char *old_path, const char *new_path) {
struct stat stat_buf;
int ret;
if (link(old_path, new_path) < 0) {
THROW_ERROR("failed to link file");
}
if (fs_check_file_content(new_path, WRITE_MSG) < 0) {
THROW_ERROR("failed to check file content");
}
if (unlink(new_path) < 0) {
THROW_ERROR("failed to unlink the link");
}
ret = stat(new_path, &stat_buf);
if (!(ret < 0 && errno == ENOENT)) {
THROW_ERROR("stat on \"%s\" should return ENOENT", new_path);
}
return 0;
}
static int __test_linkat_then_unlinkat(const char *old_path, const char *new_path) {
struct stat stat_buf;
char old_dir_buf[PATH_MAX] = { 0 };
char old_base_buf[PATH_MAX] = { 0 };
char new_dir_buf[PATH_MAX] = { 0 };
char new_base_buf[PATH_MAX] = { 0 };
char *old_dir_name, *old_file_name, *new_dir_name, *new_file_name;
int old_dirfd, new_dirfd, ret;
if (fs_split_path(old_path, old_dir_buf, &old_dir_name, old_base_buf,
&old_file_name) < 0) {
THROW_ERROR("failed to split old path");
}
old_dirfd = open(old_dir_name, O_RDONLY);
if (old_dirfd < 0) {
THROW_ERROR("failed to open old dir");
}
if (fs_split_path(new_path, new_dir_buf, &new_dir_name, new_base_buf,
&new_file_name) < 0) {
THROW_ERROR("failed to split new path");
}
new_dirfd = open(new_dir_name, O_RDONLY);
if (new_dirfd < 0) {
THROW_ERROR("failed to open new dir");
}
if (linkat(old_dirfd, old_file_name, new_dirfd, new_file_name, 0) < 0) {
THROW_ERROR("failed to linkat with dirfd");
}
close(old_dirfd);
if (fs_check_file_content(new_path, WRITE_MSG) < 0) {
THROW_ERROR("failed to check file content");
}
if (unlinkat(new_dirfd, new_file_name, 0) < 0) {
THROW_ERROR("failed to unlinkat the link");
}
close(new_dirfd);
ret = stat(new_path, &stat_buf);
if (!(ret < 0 && errno == ENOENT)) {
THROW_ERROR("stat on \"%s\" should return ENOENT", new_path);
}
return 0;
}
static int __test_linkat_with_empty_oldpath(const char *old_path, const char *new_path) {
char new_dir_buf[PATH_MAX] = { 0 };
char new_base_buf[PATH_MAX] = { 0 };
char *new_dir_name, *new_file_name;
int old_dirfd, new_dirfd, ret;
old_dirfd = open(old_path, O_RDONLY);
if (old_dirfd < 0) {
THROW_ERROR("failed to open old dir");
}
if (fs_split_path(new_path, new_dir_buf, &new_dir_name, new_base_buf,
&new_file_name) < 0) {
THROW_ERROR("failed to split new path");
}
new_dirfd = open(new_dir_name, O_RDONLY);
if (new_dirfd < 0) {
THROW_ERROR("failed to open new dir");
}
ret = linkat(old_dirfd, "", new_dirfd, new_file_name, 0);
if (!(ret < 0 && errno == ENOENT)) {
THROW_ERROR("linkat with empty oldpath should return ENOENT");
}
if (linkat(old_dirfd, "", new_dirfd, new_file_name, AT_EMPTY_PATH) < 0) {
THROW_ERROR("failed to linkat with empty oldpath and AT_EMPTY_PATH flags");
}
close(old_dirfd);
close(new_dirfd);
if (fs_check_file_content(new_path, WRITE_MSG) < 0) {
THROW_ERROR("failed to check file content");
}
if (unlink(new_path) < 0) {
THROW_ERROR("failed to unlink the link");
}
return 0;
}
typedef int(*test_link_func_t)(const char *, const char *);
static int test_link_framework(test_link_func_t fn) {
const char *old_path = "/root/test_filesystem_link_old.txt";
const char *new_path = "/root/test_filesystem_link_new.txt";
if (create_and_write_file(old_path) < 0) {
return -1;
}
if (fn(old_path, new_path) < 0) {
return -1;
}
if (remove_file(old_path) < 0) {
return -1;
}
return 0;
}
static int test_link_then_unlink() {
return test_link_framework(__test_link_then_unlink);
}
static int test_linkat_then_unlinkat() {
return test_link_framework(__test_linkat_then_unlinkat);
}
static int test_linkat_with_empty_oldpath() {
return test_link_framework(__test_linkat_with_empty_oldpath);
}
// ============================================================================
// Test suite main
// ============================================================================
static test_case_t test_cases[] = {
TEST_CASE(test_link_then_unlink),
TEST_CASE(test_linkat_then_unlinkat),
TEST_CASE(test_linkat_with_empty_oldpath),
};
int main(int argc, const char *argv[]) {
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
}

@ -1,69 +1,189 @@
#include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include "test_fs.h"
int main(int argc, const char *argv[]) { // ============================================================================
// Helper function
// ============================================================================
const int BUF_SIZE = 20; static int create_dir(const char *dir_path) {
char buf[10];
int ret; int ret;
char *cwd = getcwd(buf, BUF_SIZE); ret = mkdir(dir_path, 00775);
if (cwd != buf) {
printf("failed to getcwd\n");
return -1;
}
const char expect_cwd[] = "/";
if (strcmp(buf, expect_cwd)) {
printf("incorrect cwd \"%s\". expect \"%s\".\n", buf, expect_cwd);
return -1;
}
//const char DIR_NAME[] = "test_dir";
//const char DIR_PATH[] = "/test_dir";
const char DIR_NAME[] = "/root/test_dir";
const char DIR_PATH[] = "/root/test_dir";
const int DIR_MODE = 0664;
ret = mkdir(DIR_NAME, DIR_MODE);
if (ret < 0) { if (ret < 0) {
printf("failed to mkdir \"%s\"", DIR_NAME); THROW_ERROR("failed to create the dir");
return ret;
} }
ret = chdir(DIR_NAME);
if (ret < 0) {
printf("failed to chdir to \"%s\"", DIR_NAME);
return ret;
}
cwd = getcwd(buf, BUF_SIZE);
if (cwd != buf) {
printf("failed to getcwd\n");
return -1;
}
if (strcmp(buf, DIR_PATH)) {
printf("incorrect cwd \"%s\". expect \"%s\".\n", buf, DIR_PATH);
return -1;
}
ret = rmdir(DIR_PATH);
if (ret < 0) {
printf("failed to rmdir \"%s\"", DIR_PATH);
return ret;
}
struct stat stat_buf;
ret = stat(DIR_PATH, &stat_buf);
if (!(ret < 0 && errno == ENOENT)) {
printf("stat on \"%s\" should return ENOENT", DIR_PATH);
return ret;
}
printf("getcwd, mkdir, rmdir, chdir test successful\n");
return 0; return 0;
} }
static int remove_dir(const char *dir_path) {
int ret;
ret = rmdir(dir_path);
if (ret < 0) {
THROW_ERROR("failed to remove the created dir");
}
return 0;
}
// ============================================================================
// Test cases for mkdir
// ============================================================================
static int __test_mkdir(const char *dir_path) {
struct stat stat_buf;
mode_t mode = 00775;
if (mkdir(dir_path, mode) < 0) {
THROW_ERROR("failed to mkdir");
}
if (stat(dir_path, &stat_buf) < 0) {
THROW_ERROR("failed to stat dir");
}
if (!S_ISDIR(stat_buf.st_mode)) {
THROW_ERROR("failed to check if it is dir");
}
return 0;
}
static int __test_mkdirat(const char *dir_path) {
struct stat stat_buf;
mode_t mode = 00775;
char dir_buf[PATH_MAX] = { 0 };
char base_buf[PATH_MAX] = { 0 };
char *dir_name, *last_name;
int dirfd;
if (fs_split_path(dir_path, dir_buf, &dir_name, base_buf, &last_name) < 0) {
THROW_ERROR("failed to split path");
}
dirfd = open(dir_name, O_RDONLY);
if (dirfd < 0) {
THROW_ERROR("failed to open dir");
}
if (mkdirat(dirfd, last_name, mode) < 0) {
THROW_ERROR("failed to mkdirat dir with dirfd");
}
close(dirfd);
if (stat(dir_path, &stat_buf) < 0) {
THROW_ERROR("failed to stat dir");
}
if (!S_ISDIR(stat_buf.st_mode)) {
THROW_ERROR("failed to check if it is dir");
}
return 0;
}
typedef int(*test_mkdir_func_t)(const char *);
static int test_mkdir_framework(test_mkdir_func_t fn) {
const char *dir_path = "/root/test_filesystem_mkdir";
if (fn(dir_path) < 0) {
return -1;
}
if (remove_dir(dir_path) < 0) {
return -1;
}
return 0;
}
static int test_mkdir() {
return test_mkdir_framework(__test_mkdir);
}
static int test_mkdirat() {
return test_mkdir_framework(__test_mkdirat);
}
// ============================================================================
// Test cases for chdir
// ============================================================================
static int __test_chdir(const char *dir_path) {
char buf[128] = { 0 };
char *cwd;
if (chdir(dir_path) < 0) {
THROW_ERROR("failed to chdir");
}
cwd = getcwd(buf, sizeof(buf));
if (cwd != buf) {
THROW_ERROR("failed to getcwd");
}
if (strcmp(buf, dir_path)) {
THROW_ERROR("the cwd is incorrect after chdir");
}
return 0;
}
static int test_chdir_framework(test_mkdir_func_t fn) {
const char *dir_path = "/root/test_filesystem_chdir";
if (create_dir(dir_path) < 0) {
return -1;
}
if (fn(dir_path) < 0) {
return -1;
}
if (remove_dir(dir_path) < 0) {
return -1;
}
return 0;
}
static int test_chdir() {
return test_chdir_framework(__test_chdir);
}
// ============================================================================
// Test cases for rmdir
// ============================================================================
static int __test_rmdir_via_unlinkat(const char *dir_path) {
struct stat stat_buf;
int ret;
if (unlinkat(AT_FDCWD, dir_path, AT_REMOVEDIR) < 0) {
THROW_ERROR("failed to remove dir");
}
ret = stat(dir_path, &stat_buf);
if (!(ret < 0 && errno == ENOENT)) {
THROW_ERROR("stat on \"%s\" should return ENOENT", dir_path);
}
return 0;
}
static int test_rmdir_framework(test_mkdir_func_t fn) {
const char *dir_path = "/root/test_filesystem_rmdir";
if (create_dir(dir_path) < 0) {
return -1;
}
if (fn(dir_path) < 0) {
return -1;
}
return 0;
}
static int test_rmdir_via_unlinkat() {
return test_rmdir_framework(__test_rmdir_via_unlinkat);
}
// ============================================================================
// Test suite main
// ============================================================================
static test_case_t test_cases[] = {
TEST_CASE(test_mkdir),
TEST_CASE(test_mkdirat),
TEST_CASE(test_chdir),
TEST_CASE(test_rmdir_via_unlinkat),
};
int main(int argc, const char *argv[]) {
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
}

@ -1,8 +1,5 @@
#include <fcntl.h> #include <fcntl.h>
#include <libgen.h> #include "test_fs.h"
#include <unistd.h>
#include <stdio.h>
#include "test.h"
// ============================================================================ // ============================================================================
// Helper function // Helper function
@ -47,21 +44,14 @@ static int __test_openat_with_abs_path(const char *file_path, int flags, int mod
} }
static int __test_openat_with_dirfd(const char *file_path, int flags, int mode) { static int __test_openat_with_dirfd(const char *file_path, int flags, int mode) {
char dir_buf[128] = { 0 }; char dir_buf[PATH_MAX] = { 0 };
char base_buf[128] = { 0 }; char base_buf[PATH_MAX] = { 0 };
char *dir_name, *file_name; char *dir_name, *file_name;
int dirfd, fd, ret; int dirfd, fd;
ret = snprintf(dir_buf, sizeof(dir_buf), "%s", file_path); if (fs_split_path(file_path, dir_buf, &dir_name, base_buf, &file_name) < 0) {
if (ret >= sizeof(dir_buf) || ret < 0) { THROW_ERROR("failed to split path");
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); dirfd = open(dir_name, O_RDONLY);
if (dirfd < 0) { if (dirfd < 0) {
THROW_ERROR("failed to open dir"); THROW_ERROR("failed to open dir");

@ -3,10 +3,7 @@
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include "test_fs.h"
#include <stdio.h>
#include <unistd.h>
#include "test.h"
// ============================================================================ // ============================================================================
// The test case of readdir // The test case of readdir

5
test/rename/Makefile Normal file

@ -0,0 +1,5 @@
include ../test_common.mk
EXTRA_C_FLAGS :=
EXTRA_LINK_FLAGS :=
BIN_ARGS :=

195
test/rename/main.c Normal file

@ -0,0 +1,195 @@
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include "test_fs.h"
// ============================================================================
// Helper function
// ============================================================================
#define WRITE_MSG "Hello World"
static int create_file_with_content(const char *file_path, const char *msg) {
int fd;
int flags = O_WRONLY | O_CREAT | O_TRUNC;
int mode = 00666;
fd = open(file_path, flags, mode);
if (fd < 0) {
THROW_ERROR("failed to create a file");
}
if (msg != NULL) {
if (write(fd, msg, strlen(msg)) <= 0) {
THROW_ERROR("failed to write to the file");
}
}
close(fd);
return 0;
}
// ============================================================================
// Test cases for rename
// ============================================================================
static int __test_rename(const char *old_path, const char *new_path) {
struct stat stat_buf;
int ret;
if (rename(old_path, new_path) < 0) {
THROW_ERROR("failed to rename file");
}
if (fs_check_file_content(new_path, WRITE_MSG) < 0) {
THROW_ERROR("failed to check file content");
}
ret = stat(old_path, &stat_buf);
if (!(ret < 0 && errno == ENOENT)) {
THROW_ERROR("stat on old path should return ENOENT");
}
if (unlink(new_path) < 0) {
THROW_ERROR("failed to remove the new file");
}
return 0;
}
static int __test_renameat(const char *old_path, const char *new_path) {
struct stat stat_buf;
char old_dir_buf[PATH_MAX] = { 0 };
char old_base_buf[PATH_MAX] = { 0 };
char new_dir_buf[PATH_MAX] = { 0 };
char new_base_buf[PATH_MAX] = { 0 };
char *old_dir_name, *old_file_name, *new_dir_name, *new_file_name;
int old_dirfd, new_dirfd, ret;
if (fs_split_path(old_path, old_dir_buf, &old_dir_name, old_base_buf,
&old_file_name) < 0) {
THROW_ERROR("failed to split old path");
}
old_dirfd = open(old_dir_name, O_RDONLY);
if (old_dirfd < 0) {
THROW_ERROR("failed to open old dir");
}
if (fs_split_path(new_path, new_dir_buf, &new_dir_name, new_base_buf,
&new_file_name) < 0) {
THROW_ERROR("failed to split new path");
}
new_dirfd = open(new_dir_name, O_RDONLY);
if (new_dirfd < 0) {
THROW_ERROR("failed to open new dir");
}
if (renameat(old_dirfd, old_file_name, new_dirfd, new_file_name) < 0) {
THROW_ERROR("failed to rename with dirfd");
}
close(old_dirfd);
close(new_dirfd);
if (fs_check_file_content(new_path, WRITE_MSG) < 0) {
THROW_ERROR("failed to check file content");
}
ret = stat(old_path, &stat_buf);
if (!(ret < 0 && errno == ENOENT)) {
THROW_ERROR("stat on old path should return ENOENT");
}
if (unlink(new_path) < 0) {
THROW_ERROR("failed to remove the new file");
}
return 0;
}
typedef int(*test_rename_func_t)(const char *, const char *);
static int test_rename_framework(test_rename_func_t fn, bool target_exist) {
const char *old_path = "/root/test_filesystem_rename_old.txt";
const char *new_path = "/root/test_filesystem_rename_new.txt";
if (create_file_with_content(old_path, WRITE_MSG) < 0) {
THROW_ERROR("failed to create old file with content");
}
if (target_exist) {
if (create_file_with_content(new_path, NULL) < 0) {
THROW_ERROR("failed to create new file");
}
}
if (fn(old_path, new_path) < 0) {
return -1;
}
return 0;
}
static int test_rename() {
return test_rename_framework(__test_rename, false);
}
static int test_rename_with_target_exist() {
return test_rename_framework(__test_rename, true);
}
static int test_renameat() {
return test_rename_framework(__test_renameat, false);
}
static int test_rename_dir() {
const char *old_dir = "/root/test_old_dir";
const char *new_dir = "/root/test_new_dir";
const char *file_name = "test_file.txt";
char file_buf[128] = { 0 };
mode_t mode = 00775;
struct stat stat_buf;
int ret;
if (mkdir(old_dir, mode) < 0) {
THROW_ERROR("failed to mkdir old dir");
}
ret = snprintf(file_buf, sizeof(file_buf), "%s/%s", old_dir, file_name);
if (ret >= sizeof(file_buf) || ret < 0) {
THROW_ERROR("failed to copy file buf");
}
if (create_file_with_content(file_buf, WRITE_MSG) < 0) {
THROW_ERROR("failed to create file in old dir");
}
if (rename(old_dir, new_dir) < 0) {
THROW_ERROR("failed to rename dir");
}
ret = snprintf(file_buf, sizeof(file_buf), "%s/%s", new_dir, file_name);
if (ret >= sizeof(file_buf) || ret < 0) {
THROW_ERROR("failed to copy file buf");
}
if (fs_check_file_content(file_buf, WRITE_MSG) < 0) {
THROW_ERROR("failed to check file content");
}
ret = stat(old_dir, &stat_buf);
if (!(ret < 0 && errno == ENOENT)) {
THROW_ERROR("stat on old dir should return ENOENT");
}
if (unlink(file_buf) < 0) {
THROW_ERROR("failed to remove the file in new dir");
}
if (rmdir(new_dir) < 0) {
THROW_ERROR("failed to remove the new dir");
}
return 0;
}
// ============================================================================
// Test suite main
// ============================================================================
static test_case_t test_cases[] = {
// TODO: test more corner cases
TEST_CASE(test_rename),
TEST_CASE(test_rename_with_target_exist),
TEST_CASE(test_renameat),
TEST_CASE(test_rename_dir),
};
int main(int argc, const char *argv[]) {
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
}

@ -1,10 +1,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <libgen.h> #include "test_fs.h"
#include <unistd.h>
#include <stdio.h>
#include "test.h"
// ============================================================================ // ============================================================================
// Helper function // Helper function
@ -114,21 +111,14 @@ static int __test_fstatat_with_empty_path(const char *file_path) {
static int __test_fstatat_with_dirfd(const char *file_path) { static int __test_fstatat_with_dirfd(const char *file_path) {
struct stat stat_buf; struct stat stat_buf;
char dir_buf[128] = { 0 }; char dir_buf[PATH_MAX] = { 0 };
char base_buf[128] = { 0 }; char base_buf[PATH_MAX] = { 0 };
char *dir_name, *file_name; char *dir_name, *file_name;
int dirfd, ret; int dirfd, ret;
ret = snprintf(dir_buf, sizeof(dir_buf), "%s", file_path); if (fs_split_path(file_path, dir_buf, &dir_name, base_buf, &file_name) < 0) {
if (ret >= sizeof(dir_buf) || ret < 0) { THROW_ERROR("failed to split path");
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); dirfd = open(dir_name, O_RDONLY);
if (dirfd < 0) { if (dirfd < 0) {
THROW_ERROR("failed to open dir"); THROW_ERROR("failed to open dir");

@ -1,14 +1,10 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <libgen.h>
#include <limits.h> #include <limits.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#include "test.h" #include "test_fs.h"
// ============================================================================ // ============================================================================
// Helper variable and function // Helper variable and function
@ -82,16 +78,13 @@ static int __test_readlink_from_proc_self_fd(const char *file_path) {
static int __test_realpath(const char *file_path) { static int __test_realpath(const char *file_path) {
char buf[128] = { 0 }; char buf[128] = { 0 };
char dirc[128] = { 0 }; char dir_buf[PATH_MAX] = { 0 };
char basec[128] = { 0 }; char base_buf[PATH_MAX] = { 0 };
char *dir_name, *file_name, *res; char *dir_name, *file_name, *res;
if (snprintf(dirc, sizeof(dirc), "%s", file_path) < 0 || if (fs_split_path(file_path, dir_buf, &dir_name, base_buf, &file_name) < 0) {
snprintf(basec, sizeof(dirc), "%s", file_path) < 0) { THROW_ERROR("failed to split path");
THROW_ERROR("failed to copy file path");
} }
dir_name = dirname(dirc);
file_name = basename(basec);
if (chdir(dir_name) < 0) { if (chdir(dir_name) < 0) {
THROW_ERROR("failed to chdir to %s", dir_name); THROW_ERROR("failed to chdir to %s", dir_name);
} }
@ -112,6 +105,37 @@ static int __test_realpath(const char *file_path) {
return 0; return 0;
} }
static int __test_readlinkat(const char *file_path) {
int dirfd;
size_t n;
char buf[128] = { 0 };
#define LINK_DIR "/root"
#define LINK_NAME "test_symlink.link"
const char *link_path = LINK_DIR"/"LINK_NAME;
if (symlink(file_path, link_path) < 0) {
THROW_ERROR("failed to create symlink");
}
dirfd = open(LINK_DIR, O_RDONLY);
if (dirfd < 0) {
THROW_ERROR("failed to open dir");
}
n = readlinkat(dirfd, LINK_NAME, buf, sizeof(buf));
if (n < 0) {
THROW_ERROR("failed to readlinkat from %s", link_path);
} else if (n != strlen(file_path)) {
THROW_ERROR("readlink from %s length is wrong", link_path);
}
if (strncmp(buf, file_path, n) != 0) {
THROW_ERROR("check the content from %s failed", link_path);
}
close(dirfd);
if (remove_file(link_path) < 0) {
THROW_ERROR("failed to delete link file");
}
return 0;
}
typedef int(*test_readlink_func_t)(const char *); typedef int(*test_readlink_func_t)(const char *);
static int test_readlink_framework(test_readlink_func_t fn) { static int test_readlink_framework(test_readlink_func_t fn) {
@ -138,6 +162,10 @@ static int test_realpath() {
return test_readlink_framework(__test_realpath); return test_readlink_framework(__test_realpath);
} }
static int test_readlinkat() {
return test_readlink_framework(__test_readlinkat);
}
static int test_readlink_from_proc_self_exe() { static int test_readlink_from_proc_self_exe() {
char exe_buf[128] = { 0 }; char exe_buf[128] = { 0 };
char absolute_path[128] = { 0 }; char absolute_path[128] = { 0 };
@ -165,18 +193,69 @@ static int test_readlink_from_proc_self_exe() {
// Test cases for symlink // Test cases for symlink
// ============================================================================ // ============================================================================
static int __test_symlinkat(const char *target, const char *link_path) {
char dir_buf[PATH_MAX] = { 0 };
char base_buf[PATH_MAX] = { 0 };
char *dir_name, *link_name;
if (create_file(target) < 0) {
THROW_ERROR("failed to create target file");
}
int fd = open(target, O_WRONLY);
if (fd < 0) {
THROW_ERROR("failed to open target to write");
}
char *write_str = "Hello World\n";
if (write(fd, write_str, strlen(write_str)) <= 0) {
THROW_ERROR("failed to write");
}
close(fd);
if (fs_split_path(link_path, dir_buf, &dir_name, base_buf, &link_name) < 0) {
THROW_ERROR("failed to split path");
}
int dirfd = open(dir_name, O_RDONLY);
if (dirfd < 0) {
THROW_ERROR("failed to open dir");
}
if (symlinkat(target, dirfd, link_name) < 0) {
THROW_ERROR("failed to create symlink");
}
close(dirfd);
fd = open(link_path, O_RDONLY);
if (fd < 0) {
THROW_ERROR("failed to open link file to read");
}
char read_buf[128] = { 0 };
if (read(fd, read_buf, sizeof(read_buf)) != strlen(write_str)) {
THROW_ERROR("failed to read");
}
if (strcmp(write_str, read_buf) != 0) {
THROW_ERROR("the message read from the file is not as it was written");
}
close(fd);
if (remove_file(target) < 0) {
THROW_ERROR("failed to delete target file");
}
return 0;
}
static int __test_symlink(const char *target, const char *link_path) { static int __test_symlink(const char *target, const char *link_path) {
char dir_buf[128] = { 0 }; char dir_buf[PATH_MAX] = { 0 };
char *dir_name; char *dir_name;
char target_path[256] = { 0 }; char target_path[PATH_MAX * 2] = { 0 };
if (target[0] == '/') { if (target[0] == '/') {
snprintf(target_path, sizeof(target_path), "%s", target); snprintf(target_path, sizeof(target_path), "%s", target);
} else { } else {
// If `target` is not an absolute path, // If `target` is not an absolute path,
// it must be a path relative to the directory of the `link_path` // it must be a path relative to the directory of the `link_path`
snprintf(dir_buf, sizeof(dir_buf), "%s", link_path); if (fs_split_path(link_path, dir_buf, &dir_name, NULL, NULL) < 0) {
dir_name = dirname(dir_buf); THROW_ERROR("failed to split path");
}
snprintf(target_path, sizeof(target_path), "%s/%s", dir_name, target); snprintf(target_path, sizeof(target_path), "%s/%s", dir_name, target);
} }
if (create_file(target_path) < 0) { if (create_file(target_path) < 0) {
@ -231,17 +310,18 @@ static int __test_symlink(const char *target, const char *link_path) {
} }
static int __test_create_file_from_symlink(const char *target, const char *link_path) { static int __test_create_file_from_symlink(const char *target, const char *link_path) {
char dir_buf[128] = { 0 }; char dir_buf[PATH_MAX] = { 0 };
char *dir_name; char *dir_name;
char target_path[256] = { 0 }; char target_path[PATH_MAX * 2] = { 0 };
if (target[0] == '/') { if (target[0] == '/') {
snprintf(target_path, sizeof(target_path), "%s", target); snprintf(target_path, sizeof(target_path), "%s", target);
} else { } else {
// If `target` is not an absolute path, // If `target` is not an absolute path,
// it must be a path relative to the directory of the `link_path` // it must be a path relative to the directory of the `link_path`
snprintf(dir_buf, sizeof(dir_buf), "%s", link_path); if (fs_split_path(link_path, dir_buf, &dir_name, NULL, NULL) < 0) {
dir_name = dirname(dir_buf); THROW_ERROR("failed to split path");
}
snprintf(target_path, sizeof(target_path), "%s/%s", dir_name, target); snprintf(target_path, sizeof(target_path), "%s/%s", dir_name, target);
} }
@ -283,6 +363,12 @@ static int test_symlink_framework(test_symlink_func_t fn, const char *target,
return 0; return 0;
} }
static int test_symlinkat() {
char *target = "/root/test_symlink.file";
char *link = "/root/test_symlink.link";
return test_symlink_framework(__test_symlinkat, target, link);
}
static int test_symlink_to_absolute_target() { static int test_symlink_to_absolute_target() {
char *target = "/root/test_symlink.file"; char *target = "/root/test_symlink.file";
char *link = "/root/test_symlink.link"; char *link = "/root/test_symlink.link";
@ -356,6 +442,8 @@ static test_case_t test_cases[] = {
TEST_CASE(test_readlink_from_proc_self_fd), TEST_CASE(test_readlink_from_proc_self_fd),
TEST_CASE(test_realpath), TEST_CASE(test_realpath),
TEST_CASE(test_readlink_from_proc_self_exe), TEST_CASE(test_readlink_from_proc_self_exe),
TEST_CASE(test_readlinkat),
TEST_CASE(test_symlinkat),
TEST_CASE(test_symlink_to_absolute_target), TEST_CASE(test_symlink_to_absolute_target),
TEST_CASE(test_symlink_to_relative_target), TEST_CASE(test_symlink_to_relative_target),
TEST_CASE(test_symlink_from_ramfs), TEST_CASE(test_symlink_from_ramfs),