Add readlink from /proc/self/fd/<fd> to get file paths
* Fix readlink from `/proc/self/exe` to get absolute path of the executable file * Add readlink from`/proc/self/fd/<fd>` to get the file's real path Note that for now we only support read links _statically_, meaning that even if the file or any of its ancestors is moved after the file is opened, the absolute paths obtained from the API does not change.
This commit is contained in:
parent
daed89007a
commit
61cf75e68b
@ -5,6 +5,7 @@ use std::fmt;
|
||||
|
||||
pub struct INodeFile {
|
||||
inode: Arc<INode>,
|
||||
abs_path: String,
|
||||
offset: SgxMutex<usize>,
|
||||
access_mode: AccessMode,
|
||||
status_flags: SgxRwLock<StatusFlags>,
|
||||
@ -164,7 +165,7 @@ impl File for INodeFile {
|
||||
}
|
||||
|
||||
impl INodeFile {
|
||||
pub fn open(inode: Arc<INode>, flags: u32) -> Result<Self> {
|
||||
pub fn open(inode: Arc<INode>, abs_path: &str, flags: u32) -> Result<Self> {
|
||||
let access_mode = AccessMode::from_u32(flags)?;
|
||||
if (access_mode.readable() && !inode.allow_read()?) {
|
||||
return_errno!(EBADF, "File not readable");
|
||||
@ -175,18 +176,24 @@ impl INodeFile {
|
||||
let status_flags = StatusFlags::from_bits_truncate(flags);
|
||||
Ok(INodeFile {
|
||||
inode,
|
||||
abs_path: abs_path.to_owned(),
|
||||
offset: SgxMutex::new(0),
|
||||
access_mode,
|
||||
status_flags: SgxRwLock::new(status_flags),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_abs_path(&self) -> &str {
|
||||
&self.abs_path
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for INodeFile {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"INodeFile {{ inode: ???, pos: {}, access_mode: {:?}, status_flags: {:#o} }}",
|
||||
"INodeFile {{ inode: ???, abs_path: {}, pos: {}, access_mode: {:?}, status_flags: {:#o} }}",
|
||||
self.abs_path,
|
||||
*self.offset.lock().unwrap(),
|
||||
self.access_mode,
|
||||
*self.status_flags.read().unwrap()
|
||||
@ -225,3 +232,15 @@ impl INodeExt for INode {
|
||||
Ok(readable)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsINodeFile {
|
||||
fn as_inode_file(&self) -> Result<&INodeFile>;
|
||||
}
|
||||
|
||||
impl AsINodeFile for FileRef {
|
||||
fn as_inode_file(&self) -> Result<&INodeFile> {
|
||||
self.as_any()
|
||||
.downcast_ref::<INodeFile>()
|
||||
.ok_or_else(|| errno!(EBADF, "not an inode file"))
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ pub use self::fcntl::FcntlCmd;
|
||||
pub use self::file::{File, FileRef, SgxFile, StdinFile, StdoutFile};
|
||||
pub use self::file_flags::{AccessMode, CreationFlags, StatusFlags};
|
||||
pub use self::file_table::{FileDesc, FileTable};
|
||||
pub use self::inode_file::{INodeExt, INodeFile};
|
||||
pub use self::inode_file::{AsINodeFile, INodeExt, INodeFile};
|
||||
pub use self::io_multiplexing::*;
|
||||
pub use self::ioctl::*;
|
||||
pub use self::pipe::Pipe;
|
||||
@ -319,6 +319,7 @@ pub fn do_rename(oldpath: &str, newpath: &str) -> Result<()> {
|
||||
let (new_dir_path, new_file_name) = split_path(&newpath);
|
||||
let old_dir_inode = current_process.lookup_inode(old_dir_path)?;
|
||||
let new_dir_inode = current_process.lookup_inode(new_dir_path)?;
|
||||
// TODO: support to modify file's absolute path
|
||||
old_dir_inode.move_(old_file_name, &new_dir_inode, new_file_name)?;
|
||||
Ok(())
|
||||
}
|
||||
@ -475,7 +476,8 @@ impl Process {
|
||||
} else {
|
||||
self.lookup_inode(&path)?
|
||||
};
|
||||
Ok(Box::new(INodeFile::open(inode, flags)?))
|
||||
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
|
||||
@ -493,6 +495,27 @@ impl Process {
|
||||
Ok(inode)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the path to be absolute
|
||||
pub fn convert_to_abs_path(&self, path: &str) -> String {
|
||||
debug!(
|
||||
"convert_to_abs_path: cwd: {:?}, path: {:?}",
|
||||
self.get_cwd(),
|
||||
path
|
||||
);
|
||||
if path.len() > 0 && path.as_bytes()[0] == b'/' {
|
||||
// path is absolute path already
|
||||
return path.to_owned();
|
||||
}
|
||||
let cwd = {
|
||||
if !self.get_cwd().ends_with("/") {
|
||||
self.get_cwd().to_owned() + "/"
|
||||
} else {
|
||||
self.get_cwd().to_owned()
|
||||
}
|
||||
};
|
||||
cwd + path
|
||||
}
|
||||
}
|
||||
|
||||
/// Split a `path` str to `(base_path, file_name)`
|
||||
@ -703,19 +726,31 @@ pub fn do_fcntl(fd: FileDesc, cmd: &FcntlCmd) -> Result<isize> {
|
||||
|
||||
pub fn do_readlink(path: &str, buf: &mut [u8]) -> Result<usize> {
|
||||
info!("readlink: path: {:?}", path);
|
||||
match path {
|
||||
"/proc/self/exe" => {
|
||||
// get cwd
|
||||
let file_path = {
|
||||
if path == "/proc/self/exe" {
|
||||
let current_ref = process::get_current();
|
||||
let current = current_ref.lock().unwrap();
|
||||
let cwd = current.get_cwd();
|
||||
let len = cwd.len().min(buf.len());
|
||||
buf[0..len].copy_from_slice(&cwd.as_bytes()[0..len]);
|
||||
Ok(0)
|
||||
}
|
||||
_ => {
|
||||
current.get_elf_path().to_owned()
|
||||
} else if path.starts_with("/proc/self/fd") {
|
||||
let fd = path
|
||||
.trim_start_matches("/proc/self/fd/")
|
||||
.parse::<FileDesc>()
|
||||
.map_err(|e| errno!(EBADF, "Invalid file descriptor"))?;
|
||||
let current_ref = process::get_current();
|
||||
let current = current_ref.lock().unwrap();
|
||||
let file_ref = current.get_files().lock().unwrap().get(fd)?;
|
||||
if let Ok(inode_file) = file_ref.as_inode_file() {
|
||||
inode_file.get_abs_path().to_owned()
|
||||
} else {
|
||||
// TODO: support special device files
|
||||
return_errno!(EINVAL, "not a normal file link")
|
||||
}
|
||||
} else {
|
||||
// TODO: support symbolic links
|
||||
return_errno!(EINVAL, "not a symbolic link")
|
||||
}
|
||||
}
|
||||
};
|
||||
let len = file_path.len().min(buf.len());
|
||||
buf[0..len].copy_from_slice(&file_path.as_bytes()[0..len]);
|
||||
Ok(len)
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ pub struct Process {
|
||||
// TODO: move cwd, root_inode into a FileSystem structure
|
||||
// TODO: should cwd be a String or INode?
|
||||
cwd: String,
|
||||
elf_path: String,
|
||||
clear_child_tid: Option<*mut pid_t>,
|
||||
parent: Option<ProcessRef>,
|
||||
children: Vec<ProcessWeakRef>,
|
||||
|
@ -15,6 +15,7 @@ lazy_static! {
|
||||
host_tid: 0,
|
||||
exit_status: 0,
|
||||
cwd: "/".to_owned(),
|
||||
elf_path: "/".to_owned(),
|
||||
clear_child_tid: None,
|
||||
parent: None,
|
||||
children: Vec::new(),
|
||||
@ -29,6 +30,7 @@ lazy_static! {
|
||||
impl Process {
|
||||
pub fn new(
|
||||
cwd: &str,
|
||||
elf_path: &str,
|
||||
task: Task,
|
||||
vm_ref: ProcessVMRef,
|
||||
file_table_ref: FileTableRef,
|
||||
@ -43,6 +45,7 @@ impl Process {
|
||||
tgid: new_pid,
|
||||
host_tid: 0,
|
||||
cwd: cwd.to_owned(),
|
||||
elf_path: elf_path.to_owned(),
|
||||
clear_child_tid: None,
|
||||
exit_status: 0,
|
||||
parent: None,
|
||||
@ -87,6 +90,9 @@ impl Process {
|
||||
pub fn get_cwd(&self) -> &str {
|
||||
&self.cwd
|
||||
}
|
||||
pub fn get_elf_path(&self) -> &str {
|
||||
&self.elf_path
|
||||
}
|
||||
pub fn get_vm(&self) -> &ProcessVMRef {
|
||||
&self.vm
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ pub fn do_spawn(
|
||||
Arc::new(SgxMutex::new(files))
|
||||
};
|
||||
let rlimits_ref = Default::default();
|
||||
Process::new(&cwd, task, vm_ref, files_ref, rlimits_ref)?
|
||||
Process::new(&cwd, elf_path, task, vm_ref, files_ref, rlimits_ref)?
|
||||
};
|
||||
parent_adopts_new_child(&parent_ref, &new_process_ref);
|
||||
process_table::put(new_pid, new_process_ref.clone());
|
||||
|
@ -77,8 +77,9 @@ pub fn do_clone(
|
||||
};
|
||||
let files_ref = current.get_files().clone();
|
||||
let rlimits_ref = current.get_rlimits().clone();
|
||||
let elf_path = ¤t.elf_path;
|
||||
let cwd = ¤t.cwd;
|
||||
Process::new(cwd, task, vm_ref, files_ref, rlimits_ref)?
|
||||
Process::new(cwd, elf_path, task, vm_ref, files_ref, rlimits_ref)?
|
||||
};
|
||||
|
||||
if let Some(ctid) = ctid {
|
||||
|
@ -6,7 +6,7 @@ BUILD_DIR := $(PROJECT_DIR)/build
|
||||
TEST_DEPS := client data_sink
|
||||
# Tests: need to be compiled and run by test-% target
|
||||
TESTS := empty env hello_world malloc mmap file fs_perms getpid spawn sched pipe time \
|
||||
truncate readdir mkdir link tls pthread uname rlimit server \
|
||||
truncate readdir mkdir link symlink tls pthread uname rlimit server \
|
||||
server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group \
|
||||
ioctl fcntl
|
||||
# Benchmarks: need to be compiled and run by bench-% target
|
||||
|
@ -19,9 +19,9 @@ typedef struct {
|
||||
|
||||
#define TEST_CASE(name) { STR(name), name }
|
||||
|
||||
#define THROW_ERROR(msg) do { \
|
||||
printf("\t\tERROR: %s in func %s at line %d of file %s\n", \
|
||||
(msg), __func__, __LINE__, __FILE__); \
|
||||
#define THROW_ERROR(fmt, ...) do { \
|
||||
printf("\t\tERROR:" fmt " in func %s at line %d of file %s\n", \
|
||||
##__VA_ARGS__, __func__, __LINE__, __FILE__); \
|
||||
return -1; \
|
||||
} while (0)
|
||||
|
||||
|
5
test/symlink/Makefile
Normal file
5
test/symlink/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
include ../test_common.mk
|
||||
|
||||
EXTRA_C_FLAGS :=
|
||||
EXTRA_LINK_FLAGS :=
|
||||
BIN_ARGS :=
|
173
test/symlink/main.c
Normal file
173
test/symlink/main.c
Normal file
@ -0,0 +1,173 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "test.h"
|
||||
|
||||
// ============================================================================
|
||||
// Helper variable and function
|
||||
// ============================================================================
|
||||
const char **g_argv;
|
||||
|
||||
static ssize_t get_path_by_fd(int fd, char *buf, ssize_t buf_len) {
|
||||
char proc_fd[64] = { 0 };
|
||||
int n;
|
||||
|
||||
n = snprintf(proc_fd, sizeof(proc_fd), "/proc/self/fd/%d", fd);
|
||||
if (n < 0) {
|
||||
THROW_ERROR("failed to call snprintf for %d", fd);
|
||||
}
|
||||
|
||||
return readlink(proc_fd, buf, buf_len);
|
||||
}
|
||||
|
||||
static int create_file(const char *file_path) {
|
||||
int fd;
|
||||
int flags = O_RDONLY | O_CREAT| O_TRUNC;
|
||||
int mode = 00666;
|
||||
|
||||
fd = open(file_path, flags, mode);
|
||||
if (fd < 0) {
|
||||
THROW_ERROR("failed to create a file");
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remove_file(const char *file_path) {
|
||||
int ret;
|
||||
|
||||
ret = unlink(file_path);
|
||||
if (ret < 0) {
|
||||
THROW_ERROR("failed to unlink the created file");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test cases for readlink
|
||||
// ============================================================================
|
||||
|
||||
static int __test_readlink_from_proc_self_fd(const char *file_path) {
|
||||
char buf[128] = { 0 };
|
||||
int fd;
|
||||
ssize_t n;
|
||||
|
||||
fd = open(file_path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
THROW_ERROR("failed to open `%s` for read", file_path);
|
||||
}
|
||||
n = get_path_by_fd(fd, buf, sizeof(buf));
|
||||
close(fd);
|
||||
if (n < 0) {
|
||||
THROW_ERROR("failed to readlink for `%s`", file_path);
|
||||
}
|
||||
if (n != strlen(file_path)) {
|
||||
THROW_ERROR("readlink for `%s` length is wrong", file_path);
|
||||
}
|
||||
if (strncmp(buf, file_path, n) != 0) {
|
||||
THROW_ERROR("check the path for `%s` failed", file_path);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __test_realpath(const char *file_path) {
|
||||
char buf[128] = { 0 };
|
||||
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) {
|
||||
THROW_ERROR("failed to copy file path");
|
||||
}
|
||||
dir_name = dirname(dirc);
|
||||
file_name = basename(basec);
|
||||
ret = chdir(dir_name);
|
||||
if (ret < 0) {
|
||||
THROW_ERROR("failed to chdir to %s", dir_name);
|
||||
}
|
||||
res = realpath(file_name, buf);
|
||||
if (res == NULL) {
|
||||
THROW_ERROR("failed to get the realpath for `%s`", file_name);
|
||||
}
|
||||
if (strlen(buf) != strlen(file_path)) {
|
||||
THROW_ERROR("realpath for '%s' length is wrong", file_name);
|
||||
}
|
||||
if (strncmp(buf, file_path, strlen(buf)) != 0) {
|
||||
THROW_ERROR("check the realpath for '%s' failed", file_name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int(*test_readlink_func_t)(const char *);
|
||||
|
||||
static int test_readlink_framework(test_readlink_func_t fn) {
|
||||
const char *file_path = "/root/test_filesystem_symlink.txt";
|
||||
|
||||
if (create_file(file_path) < 0)
|
||||
return -1;
|
||||
if (fn(file_path) < 0)
|
||||
return -1;
|
||||
if (remove_file(file_path) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_readlink_from_proc_self_fd() {
|
||||
return test_readlink_framework(__test_readlink_from_proc_self_fd);
|
||||
}
|
||||
|
||||
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 };
|
||||
const char *proc_exe = "/proc/self/exe";
|
||||
ssize_t n;
|
||||
|
||||
n = snprintf(absolute_path, sizeof(absolute_path), "/bin/%s", *g_argv);
|
||||
if (n < 0) {
|
||||
THROW_ERROR("failed to call snprintf");
|
||||
}
|
||||
n = readlink(proc_exe, exe_buf, sizeof(exe_buf));
|
||||
if (n < 0) {
|
||||
THROW_ERROR("failed to readlink from %s", proc_exe);
|
||||
} else if (n != strlen(absolute_path)) {
|
||||
THROW_ERROR("readlink from %s length is wrong", proc_exe);
|
||||
}
|
||||
if (strncmp(exe_buf, absolute_path, n) != 0) {
|
||||
THROW_ERROR("check the absolute path from %s failed", proc_exe);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Test suite main
|
||||
// ============================================================================
|
||||
|
||||
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),
|
||||
};
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
g_argv = argv;
|
||||
return test_suite_run(test_cases, ARRAY_SIZE(test_cases));
|
||||
}
|
Loading…
Reference in New Issue
Block a user