Add support to handle symbolic link file
This commit is contained in:
parent
3cd46fd224
commit
1ad8f22170
2
deps/sefs
vendored
2
deps/sefs
vendored
@ -1 +1 @@
|
||||
Subproject commit cc6f694c0ac27755a1b0e1546022d598eb53ca76
|
||||
Subproject commit 2420622f050efda627ccebcff6e86a71dcf405f5
|
@ -95,6 +95,7 @@ impl ToErrno for rcore_fs::vfs::FsError {
|
||||
FsError::WrProtected => EROFS,
|
||||
FsError::NoIntegrity => EIO,
|
||||
FsError::PermError => EPERM,
|
||||
FsError::NameTooLong => ENAMETOOLONG,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ pub fn do_faccessat(
|
||||
);
|
||||
if Path::new(path).is_absolute() {
|
||||
// Path is absolute, so dirfd is ignored
|
||||
return Ok(do_access(path, mode)?);
|
||||
return Ok(do_access(path, mode, flags)?);
|
||||
}
|
||||
let path = match dirfd {
|
||||
DirFd::Fd(dirfd) => {
|
||||
@ -58,14 +58,22 @@ pub fn do_faccessat(
|
||||
}
|
||||
DirFd::Cwd => path.to_owned(),
|
||||
};
|
||||
do_access(&path, mode)
|
||||
do_access(&path, mode, flags)
|
||||
}
|
||||
|
||||
fn do_access(path: &str, mode: AccessibilityCheckMode) -> Result<()> {
|
||||
fn do_access(
|
||||
path: &str,
|
||||
mode: AccessibilityCheckMode,
|
||||
flags: AccessibilityCheckFlags,
|
||||
) -> Result<()> {
|
||||
let inode = {
|
||||
let current = current!();
|
||||
let fs = current.fs().lock().unwrap();
|
||||
fs.lookup_inode(path)?
|
||||
if flags.contains(AccessibilityCheckFlags::AT_SYMLINK_NOFOLLOW) {
|
||||
fs.lookup_inode_no_follow(path)?
|
||||
} else {
|
||||
fs.lookup_inode(path)?
|
||||
}
|
||||
};
|
||||
if mode.test_for_exist() {
|
||||
return Ok(());
|
||||
|
@ -1,8 +1,17 @@
|
||||
use super::*;
|
||||
|
||||
pub fn do_chown(path: &str, uid: u32, gid: u32) -> Result<()> {
|
||||
warn!("chown is partial implemented as lchown");
|
||||
do_lchown(path, uid, gid)
|
||||
debug!("chown: path: {:?}, uid: {}, gid: {}", path, uid, gid);
|
||||
let inode = {
|
||||
let current = current!();
|
||||
let fs = current.fs().lock().unwrap();
|
||||
fs.lookup_inode(path)?
|
||||
};
|
||||
let mut info = inode.metadata()?;
|
||||
info.uid = uid as usize;
|
||||
info.gid = gid as usize;
|
||||
inode.set_metadata(&info)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn do_fchown(fd: FileDesc, uid: u32, gid: u32) -> Result<()> {
|
||||
@ -20,7 +29,7 @@ pub fn do_lchown(path: &str, uid: u32, gid: u32) -> Result<()> {
|
||||
let inode = {
|
||||
let current = current!();
|
||||
let fs = current.fs().lock().unwrap();
|
||||
fs.lookup_inode(path)?
|
||||
fs.lookup_inode_no_follow(path)?
|
||||
};
|
||||
let mut info = inode.metadata()?;
|
||||
info.uid = uid as usize;
|
||||
|
@ -71,6 +71,10 @@ impl CreationFlags {
|
||||
pub fn is_exclusive(&self) -> bool {
|
||||
self.contains(CreationFlags::O_EXCL)
|
||||
}
|
||||
|
||||
pub fn no_follow_symlink(&self) -> bool {
|
||||
self.contains(CreationFlags::O_NOFOLLOW)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
@ -99,4 +103,8 @@ impl StatusFlags {
|
||||
pub fn always_append(&self) -> bool {
|
||||
self.contains(StatusFlags::O_APPEND)
|
||||
}
|
||||
|
||||
pub fn is_fast_open(&self) -> bool {
|
||||
self.contains(StatusFlags::O_PATH)
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ pub fn do_link(oldpath: &str, newpath: &str) -> Result<()> {
|
||||
let (inode, new_dir_inode) = {
|
||||
let current = current!();
|
||||
let fs = current.fs().lock().unwrap();
|
||||
let inode = fs.lookup_inode(&oldpath)?;
|
||||
let inode = fs.lookup_inode_no_follow(&oldpath)?;
|
||||
let new_dir_inode = fs.lookup_inode(new_dir_path)?;
|
||||
(inode, new_dir_inode)
|
||||
};
|
||||
|
@ -25,7 +25,7 @@ pub use self::rename::do_rename;
|
||||
pub use self::rmdir::do_rmdir;
|
||||
pub use self::sendfile::do_sendfile;
|
||||
pub use self::stat::{do_fstat, do_fstatat, do_lstat, Stat, StatFlags};
|
||||
pub use self::symlink::do_readlink;
|
||||
pub use self::symlink::{do_readlink, do_symlinkat};
|
||||
pub use self::truncate::{do_ftruncate, do_truncate};
|
||||
pub use self::unlink::do_unlink;
|
||||
pub use self::write::{do_pwrite, do_write, do_writev};
|
||||
|
@ -139,7 +139,7 @@ fn do_stat(path: &str) -> Result<Stat> {
|
||||
let inode = {
|
||||
let current = current!();
|
||||
let fs = current.fs().lock().unwrap();
|
||||
fs.lookup_inode_follow(&path)?
|
||||
fs.lookup_inode(&path)?
|
||||
};
|
||||
let stat = Stat::from(inode.metadata()?);
|
||||
Ok(stat)
|
||||
@ -149,7 +149,6 @@ pub fn do_fstat(fd: u32) -> Result<Stat> {
|
||||
debug!("fstat: fd: {}", fd);
|
||||
let file_ref = current!().file(fd as FileDesc)?;
|
||||
let stat = Stat::from(file_ref.metadata()?);
|
||||
// TODO: handle symlink
|
||||
Ok(stat)
|
||||
}
|
||||
|
||||
@ -158,7 +157,7 @@ pub fn do_lstat(path: &str) -> Result<Stat> {
|
||||
let inode = {
|
||||
let current = current!();
|
||||
let fs = current.fs().lock().unwrap();
|
||||
fs.lookup_inode(&path)?
|
||||
fs.lookup_inode_no_follow(&path)?
|
||||
};
|
||||
let stat = Stat::from(inode.metadata()?);
|
||||
Ok(stat)
|
||||
@ -185,9 +184,14 @@ pub fn do_fstatat(dirfd: DirFd, path: &str, flags: StatFlags) -> Result<Stat> {
|
||||
} else {
|
||||
let dir_path = get_dir_path(dirfd)?;
|
||||
let path = dir_path + "/" + path;
|
||||
do_stat(&path)
|
||||
if !flags.contains(StatFlags::AT_SYMLINK_NOFOLLOW) {
|
||||
do_stat(&path)
|
||||
} else {
|
||||
do_lstat(&path)
|
||||
}
|
||||
}
|
||||
}
|
||||
DirFd::Cwd => do_stat(path),
|
||||
DirFd::Cwd if !flags.contains(StatFlags::AT_SYMLINK_NOFOLLOW) => do_stat(path),
|
||||
DirFd::Cwd => do_lstat(path),
|
||||
}
|
||||
}
|
||||
|
@ -18,11 +18,60 @@ pub fn do_readlink(path: &str, buf: &mut [u8]) -> Result<usize> {
|
||||
return_errno!(EINVAL, "not a normal file link")
|
||||
}
|
||||
} else {
|
||||
// TODO: support symbolic links
|
||||
return_errno!(EINVAL, "not a symbolic link")
|
||||
let inode = {
|
||||
let current = current!();
|
||||
let fs = current.fs().lock().unwrap();
|
||||
fs.lookup_inode_no_follow(path)?
|
||||
};
|
||||
if inode.metadata()?.type_ != FileType::SymLink {
|
||||
return_errno!(EINVAL, "not a symbolic link");
|
||||
}
|
||||
let mut content = vec![0u8; PATH_MAX];
|
||||
let len = inode.read_at(0, &mut content)?;
|
||||
let path = std::str::from_utf8(&content[..len])
|
||||
.map_err(|_| errno!(EINVAL, "invalid symlink"))?;
|
||||
String::from(path)
|
||||
}
|
||||
};
|
||||
let len = file_path.len().min(buf.len());
|
||||
buf[0..len].copy_from_slice(&file_path.as_bytes()[0..len]);
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn do_symlink(target: &str, link_path: &str) -> Result<usize> {
|
||||
let (dir_path, link_name) = split_path(&link_path);
|
||||
let dir_inode = {
|
||||
let current = current!();
|
||||
let fs = current.fs().lock().unwrap();
|
||||
fs.lookup_inode(dir_path)?
|
||||
};
|
||||
if !dir_inode.allow_write()? {
|
||||
return_errno!(EPERM, "symlink cannot be created");
|
||||
}
|
||||
let link_inode = dir_inode.create(link_name, FileType::SymLink, 0o0777)?;
|
||||
let data = target.as_bytes();
|
||||
link_inode.resize(data.len())?;
|
||||
link_inode.write_at(0, data)?;
|
||||
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)
|
||||
}
|
||||
|
@ -53,64 +53,120 @@ impl FsView {
|
||||
return Ok(Box::new(DevSgx));
|
||||
}
|
||||
let creation_flags = CreationFlags::from_bits_truncate(flags);
|
||||
let inode = if creation_flags.can_create() {
|
||||
let (dir_path, file_name) = split_path(&path);
|
||||
let dir_inode = self.lookup_inode(dir_path)?;
|
||||
match dir_inode.find(file_name) {
|
||||
Ok(file_inode) => {
|
||||
if creation_flags.is_exclusive() {
|
||||
let inode = if creation_flags.no_follow_symlink() {
|
||||
match self.lookup_inode_no_follow(path) {
|
||||
Ok(inode) => {
|
||||
let status_flags = StatusFlags::from_bits_truncate(flags);
|
||||
if inode.metadata()?.type_ == FileType::SymLink && !status_flags.is_fast_open()
|
||||
{
|
||||
return_errno!(ELOOP, "file is a symlink");
|
||||
}
|
||||
if creation_flags.can_create() && creation_flags.is_exclusive() {
|
||||
return_errno!(EEXIST, "file exists");
|
||||
}
|
||||
file_inode
|
||||
inode
|
||||
}
|
||||
Err(FsError::EntryNotFound) => {
|
||||
Err(e) if e.errno() == ENOENT && creation_flags.can_create() => {
|
||||
let (dir_path, file_name) = split_path(&path);
|
||||
let dir_inode = self.lookup_inode(dir_path)?;
|
||||
if !dir_inode.allow_write()? {
|
||||
return_errno!(EPERM, "file cannot be created");
|
||||
}
|
||||
dir_inode.create(file_name, FileType::File, mode)?
|
||||
}
|
||||
Err(e) => return Err(Error::from(e)),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
} else {
|
||||
self.lookup_inode(&path)?
|
||||
match self.lookup_inode(path) {
|
||||
Ok(inode) => {
|
||||
if creation_flags.can_create() && creation_flags.is_exclusive() {
|
||||
return_errno!(EEXIST, "file exists");
|
||||
}
|
||||
inode
|
||||
}
|
||||
Err(e) if e.errno() == ENOENT && creation_flags.can_create() => {
|
||||
let real_path = self.lookup_real_path(&path)?;
|
||||
let (dir_path, file_name) = split_path(&real_path);
|
||||
let dir_inode = self.lookup_inode(dir_path)?;
|
||||
if !dir_inode.allow_write()? {
|
||||
return_errno!(EPERM, "file cannot be created");
|
||||
}
|
||||
dir_inode.create(file_name, FileType::File, mode)?
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
};
|
||||
let abs_path = self.convert_to_abs_path(&path);
|
||||
Ok(Box::new(INodeFile::open(inode, &abs_path, flags)?))
|
||||
}
|
||||
|
||||
/// Lookup INode from the cwd of the process
|
||||
pub fn lookup_inode(&self, path: &str) -> Result<Arc<dyn INode>> {
|
||||
self.lookup_inode_follow_with_max_times(path, 0)
|
||||
/// Recursively lookup the real path of giving path, dereference symlinks
|
||||
pub fn lookup_real_path(&self, path: &str) -> Result<String> {
|
||||
let (dir_path, file_name) = split_path(&path);
|
||||
let dir_inode = self.lookup_inode(dir_path)?;
|
||||
match dir_inode.find(file_name) {
|
||||
// Handle symlink
|
||||
Ok(inode) if inode.metadata()?.type_ == FileType::SymLink => {
|
||||
let new_path = {
|
||||
let mut content = vec![0u8; PATH_MAX];
|
||||
let len = inode.read_at(0, &mut content)?;
|
||||
let path = std::str::from_utf8(&content[..len])
|
||||
.map_err(|_| errno!(ENOENT, "invalid symlink content"))?;
|
||||
let path = String::from(path);
|
||||
match path.chars().next() {
|
||||
None => unreachable!(),
|
||||
// absolute path
|
||||
Some('/') => path,
|
||||
// relative path
|
||||
_ => {
|
||||
let dir_path = if dir_path.ends_with("/") {
|
||||
String::from(dir_path)
|
||||
} else {
|
||||
String::from(dir_path) + "/"
|
||||
};
|
||||
dir_path + &path
|
||||
}
|
||||
}
|
||||
};
|
||||
self.lookup_real_path(&new_path)
|
||||
}
|
||||
Err(FsError::EntryNotFound) | Ok(_) => {
|
||||
debug!("real_path: cwd: {:?}, path: {:?}", self.cwd(), path);
|
||||
Ok(String::from(path))
|
||||
}
|
||||
Err(e) => return Err(Error::from(e)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Lookup INode from the cwd of the process, follow symlinks
|
||||
pub fn lookup_inode_follow(&self, path: &str) -> Result<Arc<dyn INode>> {
|
||||
/// Lookup INode from the cwd of the process. If path is a symlink, do not dereference it
|
||||
pub fn lookup_inode_no_follow(&self, path: &str) -> Result<Arc<dyn INode>> {
|
||||
debug!("lookup_inode: cwd: {:?}, path: {:?}", self.cwd(), path);
|
||||
let (dir_path, file_name) = split_path(&path);
|
||||
let dir_inode = self.lookup_inode(dir_path)?;
|
||||
Ok(dir_inode.lookup(file_name)?)
|
||||
}
|
||||
|
||||
/// Lookup INode from the cwd of the process, dereference symlink
|
||||
pub fn lookup_inode(&self, path: &str) -> Result<Arc<dyn INode>> {
|
||||
// Linux uses 40 as the upper limit for resolving symbolic links,
|
||||
// so Occlum use it as a reasonable value
|
||||
const MAX_SYMLINKS: usize = 40;
|
||||
self.lookup_inode_follow_with_max_times(path, MAX_SYMLINKS)
|
||||
}
|
||||
|
||||
fn lookup_inode_follow_with_max_times(
|
||||
&self,
|
||||
path: &str,
|
||||
max_times: usize,
|
||||
) -> Result<Arc<dyn INode>> {
|
||||
debug!(
|
||||
"lookup_inode_follow_with_max_times: cwd: {:?}, path: {:?}, max_times: {}",
|
||||
"lookup_inode_follow: cwd: {:?}, path: {:?}",
|
||||
self.cwd(),
|
||||
path,
|
||||
max_times
|
||||
path
|
||||
);
|
||||
if path.len() > 0 && path.as_bytes()[0] == b'/' {
|
||||
// absolute path
|
||||
let abs_path = path.trim_start_matches('/');
|
||||
let inode = ROOT_INODE.lookup_follow(abs_path, max_times)?;
|
||||
let inode = ROOT_INODE.lookup_follow(abs_path, MAX_SYMLINKS)?;
|
||||
Ok(inode)
|
||||
} else {
|
||||
// relative path
|
||||
let cwd = self.cwd().trim_start_matches('/');
|
||||
let inode = ROOT_INODE.lookup(cwd)?.lookup_follow(path, max_times)?;
|
||||
let inode = ROOT_INODE
|
||||
.lookup_follow(cwd, MAX_SYMLINKS)?
|
||||
.lookup_follow(path, MAX_SYMLINKS)?;
|
||||
Ok(inode)
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +127,10 @@ impl INode for HNode {
|
||||
FileType::File => {
|
||||
try_std!(fs::File::create(&new_path));
|
||||
}
|
||||
_ => unimplemented!("only support creating files in HostFS"),
|
||||
_ => {
|
||||
warn!("only support creating regular files in HostFS");
|
||||
return Err(FsError::PermError);
|
||||
}
|
||||
}
|
||||
Ok(Arc::new(HNode {
|
||||
path: new_path,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
|
||||
use process;
|
||||
use rcore_fs::vfs::{FileSystem, FileType, FsError, INode, Metadata, Timespec};
|
||||
use rcore_fs::vfs::{FileSystem, FileType, FsError, INode, Metadata, Timespec, PATH_MAX};
|
||||
use std;
|
||||
use std::any::Any;
|
||||
use std::fmt;
|
||||
|
@ -388,6 +388,34 @@ pub fn do_readlink(path: *const i8, buf: *mut u8, size: usize) -> Result<isize>
|
||||
Ok(len as isize)
|
||||
}
|
||||
|
||||
pub fn do_symlink(target: *const i8, link_path: *const i8) -> Result<isize> {
|
||||
let target = from_user::clone_cstring_safely(target)?
|
||||
.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> {
|
||||
let target = from_user::clone_cstring_safely(target)?
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
let link_path = from_user::clone_cstring_safely(link_path)?
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
let new_dirfd = if Path::new(&link_path).is_absolute() {
|
||||
// Link path is absolute, new_dirfd is treated as Cwd
|
||||
DirFd::Cwd
|
||||
} else {
|
||||
DirFd::from_i32(new_dirfd)?
|
||||
};
|
||||
file_ops::do_symlinkat(&target, new_dirfd, &link_path)?;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
pub fn do_chmod(path: *const i8, mode: u16) -> Result<isize> {
|
||||
let path = from_user::clone_cstring_safely(path)?
|
||||
.to_string_lossy()
|
||||
|
@ -64,7 +64,7 @@ pub fn load_file_to_vec(file_path: &str, current_ref: &ThreadRef) -> Result<Vec<
|
||||
.fs()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.lookup_inode_follow(file_path)
|
||||
.lookup_inode(file_path)
|
||||
.map_err(|e| errno!(e.errno(), "cannot find the file"))?;
|
||||
let file_mode = {
|
||||
let info = inode.metadata()?;
|
||||
|
@ -22,8 +22,9 @@ use crate::fs::{
|
||||
do_eventfd2, do_faccessat, do_fchmod, do_fchown, do_fcntl, do_fdatasync, do_fstat, do_fstatat,
|
||||
do_fsync, do_ftruncate, do_getcwd, do_getdents64, do_ioctl, do_lchown, do_link, do_lseek,
|
||||
do_lstat, do_mkdir, do_open, do_openat, do_pipe, do_pipe2, do_pread, do_pwrite, do_read,
|
||||
do_readlink, do_readv, do_rename, do_rmdir, do_sendfile, do_stat, do_sync, do_truncate,
|
||||
do_unlink, do_write, do_writev, iovec_t, File, FileDesc, FileRef, HostStdioFds, Stat,
|
||||
do_readlink, do_readv, do_rename, do_rmdir, do_sendfile, do_stat, do_symlink, do_symlinkat,
|
||||
do_sync, do_truncate, do_unlink, do_write, do_writev, iovec_t, File, FileDesc, FileRef,
|
||||
HostStdioFds, Stat,
|
||||
};
|
||||
use crate::misc::{resource_t, rlimit_t, sysinfo_t, utsname_t};
|
||||
use crate::net::{
|
||||
@ -165,7 +166,7 @@ macro_rules! process_syscall_table_with_callback {
|
||||
(Creat = 85) => handle_unsupported(),
|
||||
(Link = 86) => do_link(oldpath: *const i8, newpath: *const i8),
|
||||
(Unlink = 87) => do_unlink(path: *const i8),
|
||||
(Symlink = 88) => handle_unsupported(),
|
||||
(Symlink = 88) => do_symlink(target: *const i8, link_path: *const i8),
|
||||
(Readlink = 89) => do_readlink(path: *const i8, buf: *mut u8, size: usize),
|
||||
(Chmod = 90) => do_chmod(path: *const i8, mode: u16),
|
||||
(Fchmod = 91) => do_fchmod(fd: FileDesc, mode: u16),
|
||||
@ -343,7 +344,7 @@ macro_rules! process_syscall_table_with_callback {
|
||||
(Unlinkat = 263) => handle_unsupported(),
|
||||
(Renameat = 264) => handle_unsupported(),
|
||||
(Linkat = 265) => handle_unsupported(),
|
||||
(Symlinkat = 266) => handle_unsupported(),
|
||||
(Symlinkat = 266) => do_symlinkat(target: *const i8, new_dirfd: i32, link_path: *const i8),
|
||||
(Readlinkat = 267) => handle_unsupported(),
|
||||
(Fchmodat = 268) => handle_unsupported(),
|
||||
(Faccessat = 269) => do_faccessat(dirfd: i32, path: *const i8, mode: u32, flags: u32),
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include "test.h"
|
||||
|
||||
// ============================================================================
|
||||
@ -84,7 +85,6 @@ static int __test_realpath(const char *file_path) {
|
||||
char dirc[128] = { 0 };
|
||||
char basec[128] = { 0 };
|
||||
char *dir_name, *file_name, *res;
|
||||
int ret;
|
||||
|
||||
if (snprintf(dirc, sizeof(dirc), "%s", file_path) < 0 ||
|
||||
snprintf(basec, sizeof(dirc), "%s", file_path) < 0) {
|
||||
@ -92,8 +92,7 @@ static int __test_realpath(const char *file_path) {
|
||||
}
|
||||
dir_name = dirname(dirc);
|
||||
file_name = basename(basec);
|
||||
ret = chdir(dir_name);
|
||||
if (ret < 0) {
|
||||
if (chdir(dir_name) < 0) {
|
||||
THROW_ERROR("failed to chdir to %s", dir_name);
|
||||
}
|
||||
res = realpath(file_name, buf);
|
||||
@ -106,6 +105,9 @@ static int __test_realpath(const char *file_path) {
|
||||
if (strncmp(buf, file_path, strlen(buf)) != 0) {
|
||||
THROW_ERROR("check the realpath for '%s' failed", file_name);
|
||||
}
|
||||
if (chdir("/") < 0) {
|
||||
THROW_ERROR("failed to chdir to '/'");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -136,7 +138,6 @@ static int test_realpath() {
|
||||
return test_readlink_framework(__test_realpath);
|
||||
}
|
||||
|
||||
|
||||
static int test_readlink_from_proc_self_exe() {
|
||||
char exe_buf[128] = { 0 };
|
||||
char absolute_path[128] = { 0 };
|
||||
@ -160,6 +161,193 @@ static int test_readlink_from_proc_self_exe() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test cases for symlink
|
||||
// ============================================================================
|
||||
|
||||
static int __test_symlink(const char *target, const char *link_path) {
|
||||
char dir_buf[128] = { 0 };
|
||||
char *dir_name;
|
||||
char target_path[256] = { 0 };
|
||||
|
||||
if (target[0] == '/') {
|
||||
snprintf(target_path, sizeof(target_path), "%s", target);
|
||||
} else {
|
||||
// If `target` is not an absolute path,
|
||||
// it must be a path relative to the directory of the `link_path`
|
||||
snprintf(dir_buf, sizeof(dir_buf), "%s", link_path);
|
||||
dir_name = dirname(dir_buf);
|
||||
snprintf(target_path, sizeof(target_path), "%s/%s", dir_name, target);
|
||||
}
|
||||
if (create_file(target_path) < 0) {
|
||||
THROW_ERROR("failed to create target file");
|
||||
}
|
||||
|
||||
int fd = open(target_path, 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 (symlink(target, link_path) < 0) {
|
||||
THROW_ERROR("failed to create symlink");
|
||||
}
|
||||
|
||||
fd = open(link_path, O_RDONLY | O_NOFOLLOW);
|
||||
if (fd >= 0 || errno != ELOOP) {
|
||||
THROW_ERROR("failed to check open file with O_NOFOLLOW flags");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
char readlink_buf[256] = { 0 };
|
||||
if (readlink(link_path, readlink_buf, sizeof(readlink_buf)) < 0) {
|
||||
THROW_ERROR("readlink failed");
|
||||
}
|
||||
if (strcmp(target, readlink_buf) != 0) {
|
||||
THROW_ERROR("check readlink result failed");
|
||||
}
|
||||
|
||||
if (remove_file(target_path) < 0) {
|
||||
THROW_ERROR("failed to delete target file");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __test_create_file_from_symlink(const char *target, const char *link_path) {
|
||||
char dir_buf[128] = { 0 };
|
||||
char *dir_name;
|
||||
char target_path[256] = { 0 };
|
||||
|
||||
if (target[0] == '/') {
|
||||
snprintf(target_path, sizeof(target_path), "%s", target);
|
||||
} else {
|
||||
// If `target` is not an absolute path,
|
||||
// it must be a path relative to the directory of the `link_path`
|
||||
snprintf(dir_buf, sizeof(dir_buf), "%s", link_path);
|
||||
dir_name = dirname(dir_buf);
|
||||
snprintf(target_path, sizeof(target_path), "%s/%s", dir_name, target);
|
||||
}
|
||||
|
||||
if (symlink(target, link_path) < 0) {
|
||||
THROW_ERROR("failed to create symlink");
|
||||
}
|
||||
|
||||
int fd = open(link_path, O_RDONLY, 00666);
|
||||
if (fd >= 0 || errno != ENOENT) {
|
||||
THROW_ERROR("failed to check open a dangling symbolic link");
|
||||
}
|
||||
|
||||
if (create_file(link_path) < 0) {
|
||||
THROW_ERROR("failed to create link file");
|
||||
}
|
||||
struct stat stat_buf;
|
||||
if (stat(target_path, &stat_buf) < 0) {
|
||||
THROW_ERROR("failed to stat the target file");
|
||||
}
|
||||
|
||||
if (remove_file(target_path) < 0) {
|
||||
THROW_ERROR("failed to delete target file");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int(*test_symlink_func_t)(const char *, const char *);
|
||||
|
||||
static int test_symlink_framework(test_symlink_func_t fn, const char *target,
|
||||
const char *link) {
|
||||
if (fn(target, link) < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (remove_file(link) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_symlink_to_absolute_target() {
|
||||
char *target = "/root/test_symlink.file";
|
||||
char *link = "/root/test_symlink.link";
|
||||
return test_symlink_framework(__test_symlink, target, link);
|
||||
}
|
||||
|
||||
static int test_symlink_to_relative_target() {
|
||||
char *target = "./test_symlink.file";
|
||||
char *link = "/root/test_symlink.link";
|
||||
if (test_symlink_framework(__test_symlink, target, link) < 0) {
|
||||
return -1;
|
||||
}
|
||||
target = "../root/test_symlink.file";
|
||||
if (test_symlink_framework(__test_symlink, target, link) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_symlink_from_ramfs() {
|
||||
char *target = "/root/test_symlink.file";
|
||||
char *link = "/tmp/test_symlink.link";
|
||||
return test_symlink_framework(__test_symlink, target, link);
|
||||
}
|
||||
|
||||
static int test_symlink_to_ramfs() {
|
||||
char *target = "/tmp/test_symlink.file";
|
||||
char *link = "/root/test_symlink.link";
|
||||
return test_symlink_framework(__test_symlink, target, link);
|
||||
}
|
||||
|
||||
static int test_symlink_with_empty_target_or_link_path() {
|
||||
char *target = "/root/test_symlink.file";
|
||||
char *link_path = "/root/test_symlink.link";
|
||||
|
||||
int ret = symlink("", link_path);
|
||||
if (ret >= 0 || errno != ENOENT) {
|
||||
THROW_ERROR("failed to check symlink with empty target");
|
||||
}
|
||||
ret = symlink(target, "");
|
||||
if (ret >= 0 || errno != ENOENT) {
|
||||
THROW_ERROR("failed to check symlink with empty linkpath");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_create_file_from_symlink_to_absolute_target() {
|
||||
char *target = "/root/test_symlink.file";
|
||||
char *link = "/root/test_symlink.link";
|
||||
return test_symlink_framework(__test_create_file_from_symlink, target, link);
|
||||
}
|
||||
|
||||
static int test_create_file_from_symlink_to_relative_target() {
|
||||
char *target = "test_symlink.file";
|
||||
char *link = "/root/test_symlink.link";
|
||||
if (test_symlink_framework(__test_create_file_from_symlink, target, link) < 0) {
|
||||
return -1;
|
||||
}
|
||||
target = "../root/test_symlink.file";
|
||||
if (test_symlink_framework(__test_create_file_from_symlink, target, link) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test suite main
|
||||
// ============================================================================
|
||||
@ -168,6 +356,13 @@ static test_case_t test_cases[] = {
|
||||
TEST_CASE(test_readlink_from_proc_self_fd),
|
||||
TEST_CASE(test_realpath),
|
||||
TEST_CASE(test_readlink_from_proc_self_exe),
|
||||
TEST_CASE(test_symlink_to_absolute_target),
|
||||
TEST_CASE(test_symlink_to_relative_target),
|
||||
TEST_CASE(test_symlink_from_ramfs),
|
||||
TEST_CASE(test_symlink_to_ramfs),
|
||||
TEST_CASE(test_symlink_with_empty_target_or_link_path),
|
||||
TEST_CASE(test_create_file_from_symlink_to_absolute_target),
|
||||
TEST_CASE(test_create_file_from_symlink_to_relative_target),
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
|
Loading…
Reference in New Issue
Block a user