Added symlink support to vfs and plumbed it to syscall layer

This commit requires a rebuild of the toolchain.
This commit is contained in:
Mike Gerow 2015-06-03 19:07:08 -07:00
parent 478835401e
commit e8689bb0c6
5 changed files with 220 additions and 23 deletions

View File

@ -16,6 +16,9 @@
#include <logging.h>
#include <hashmap.h>
#define MAX_SYMLINK_DEPTH 8
#define MAX_SYMLINK_SIZE 4096
tree_t * fs_tree = NULL; /* File system mountpoint tree */
fs_node_t * fs_root = NULL; /* Pointer to the root mount fs_node (must be some form of filesystem, even ramdisk) */
@ -365,6 +368,54 @@ fs_node_t *clone_fs(fs_node_t *source) {
return source;
}
int symlink_fs(char * target, char * name) {
fs_node_t * parent;
char *cwd = (char *)(current_process->wd_name);
char *path = canonicalize_path(cwd, name);
char * parent_path = malloc(strlen(path) + 4);
sprintf(parent_path, "%s/..", path);
char * f_path = path + strlen(path) - 1;
while (f_path > path) {
if (*f_path == '/') {
f_path += 1;
break;
}
f_path--;
}
debug_print(WARNING, "creating symlink %s within %s", f_path, parent_path);
parent = kopen(parent_path, 0);
free(parent_path);
if (!parent) {
free(path);
return -1;
}
if (parent->symlink) {
parent->symlink(parent, target, f_path);
}
free(path);
close_fs(parent);
return 0;
}
int readlink_fs(fs_node_t *node, char * buf, uint32_t size) {
if (!node) return -1;
if (node->readlink) {
return node->readlink(node, buf, size);
} else {
return -1;
}
}
/**
* canonicalize_path: Canonicalize a path.
*
@ -721,31 +772,14 @@ fs_node_t *get_mount_point(char * path, unsigned int path_depth, char **outpath,
/**
* kopen: Open a file by name.
*
* Explore the file system tree to find the appropriate node for
* for a given path. The path can be relative to the working directory
* and will be canonicalized by the kernel.
*
* @param filename Filename to open
* @param flags Flag bits for read/write mode.
* @returns A file system node element that the caller can free.
*/
fs_node_t *kopen(char *filename, uint32_t flags) {
fs_node_t *kopen_recur(char *filename, uint32_t flags, uint32_t symlink_depth, char *relative_to) {
/* Simple sanity checks that we actually have a file system */
if (!filename) {
return NULL;
}
debug_print(INFO, "kopen(%s)", filename);
/* Reference the current working directory */
char *cwd = (char *)(current_process->wd_name);
/* Canonicalize the (potentially relative) path... */
char *path = canonicalize_path(cwd, filename);
char *path = canonicalize_path(relative_to, filename);
/* And store the length once to save recalculations */
size_t path_len = strlen(path);
@ -791,6 +825,8 @@ fs_node_t *kopen(char *filename, uint32_t flags) {
unsigned int depth = 0;
/* Find the mountpoint for this file */
fs_node_t *node_ptr = get_mount_point(path, path_depth, &path_offset, &depth);
debug_print(CRITICAL, "path_offset: %s", path_offset);
debug_print(CRITICAL, "depth: %d", depth);
if (!node_ptr) return NULL;
@ -804,13 +840,80 @@ fs_node_t *kopen(char *filename, uint32_t flags) {
/* Search the active directory for the requested directory */
debug_print(INFO, "... Searching for %s", path_offset);
node_next = finddir_fs(node_ptr, path_offset);
free(node_ptr);
close_fs(node_ptr);
node_ptr = node_next;
if (!node_ptr) {
/* We failed to find the requested directory */
free((void *)path);
return NULL;
} else if (depth == path_depth - 1) {
}
/*
* This test is a little complicated, but we basically always resolve symlinks in the
* of a path (like /home/symlink/file) even if O_NOFOLLOW and O_PATH are set. If we are
* on the leaf of the path then we will look at those flags and act accordingly
*/
if ((node_ptr->flags & FS_SYMLINK) &&
!((flags & O_NOFOLLOW) && (flags & O_PATH) && depth == path_depth - 1)) {
/* This ensures we don't return a path when NOFOLLOW is requested but PATH
* isn't passed.
*/
debug_print(CRITICAL, "resolving symlink at %s", node_ptr->name);
if ((flags & O_NOFOLLOW) && depth == path_depth - 1) {
/* TODO(gerow): should probably be setting errno from this */
debug_print(NOTICE, "Refusing to follow final entry for open with O_NOFOLLOW for %s.", node_ptr->name);
free((void *)path);
close_fs(node_ptr);
return NULL;
}
if (symlink_depth >= MAX_SYMLINK_DEPTH) {
/* TODO(gerow): should probably be setting errno from this */
debug_print(WARNING, "Reached max symlink depth on %s.", node_ptr->name);
free((void *)path);
close_fs(node_ptr);
return NULL;
}
/*
* This may actually be big enough that we wouldn't want to allocate it on
* the stack, especially considering this function is called recursively
*/
char symlink_buf[MAX_SYMLINK_SIZE];
int len = node_ptr->readlink(node_ptr, symlink_buf, sizeof(symlink_buf));
if (len < 0) {
/* TODO(gerow): should probably be setting errno from this */
debug_print(WARNING, "Got error %d from symlink for %s.", len, node_ptr->name);
free((void *)path);
close_fs(node_ptr);
return NULL;
}
if (symlink_buf[len - 1] != '\0') {
/* TODO(gerow): should probably be setting errno from this */
debug_print(WARNING, "readlink for %s doesn't end in a null pointer. That's weird...", len, node_ptr->name);
free((void *)path);
close_fs(node_ptr);
return NULL;
}
fs_node_t * old_node_ptr = node_ptr;
/* Rebuild our path up to this point. This is hella hacky. */
char * relpath = malloc(path_len + 1);
char * ptr = relpath;
memcpy(relpath, path, path_len + 1);
for (unsigned int i = 0; i < depth; i++) {
while(*ptr != '\0') {
ptr++;
}
*ptr = PATH_SEPARATOR;
}
node_ptr = kopen_recur(symlink_buf, 0, symlink_depth + 1, relpath);
free(relpath);
close_fs(old_node_ptr);
if (!node_ptr) {
/* Dangling symlink? */
debug_print(WARNING, "Failed to open symlink path %s. Perhaps it's a dangling symlink?", symlink_buf);
free((void *)path);
return NULL;
}
}
if (depth == path_depth - 1) {
/* We found the file and are done, open the node */
open_fs(node_ptr, flags);
free((void *)path);
@ -825,3 +928,20 @@ fs_node_t *kopen(char *filename, uint32_t flags) {
return NULL;
}
/**
* kopen: Open a file by name.
*
* Explore the file system tree to find the appropriate node for
* for a given path. The path can be relative to the working directory
* and will be canonicalized by the kernel.
*
* @param filename Filename to open
* @param flags Flag bits for read/write mode.
* @returns A file system node element that the caller can free.
*/
fs_node_t *kopen(char *filename, uint32_t flags) {
debug_print(NOTICE, "kopen(%s)", filename);
return kopen_recur(filename, flags, 0, (char *)(current_process->wd_name));
}

View File

@ -15,6 +15,8 @@
#define O_CREAT 0x0200
#define O_TRUNC 0x0400
#define O_EXCL 0x0800
#define O_NOFOLLOW 0x1000
#define O_PATH 0x2000
#define FS_FILE 0x01
#define FS_DIRECTORY 0x02
@ -47,6 +49,8 @@ typedef void (*mkdir_type_t) (struct fs_node *, char *name, uint16_t permission)
typedef int (*ioctl_type_t) (struct fs_node *, int request, void * argp);
typedef int (*get_size_type_t) (struct fs_node *);
typedef int (*chmod_type_t) (struct fs_node *, int mode);
typedef void (*symlink_type_t) (struct fs_node *, char * name, char * value);
typedef int (*readlink_type_t) (struct fs_node *, char * buf, size_t size);
typedef struct fs_node {
char name[256]; /* The filename. */
@ -78,6 +82,8 @@ typedef struct fs_node {
get_size_type_t get_size;
chmod_type_t chmod;
unlink_type_t unlink;
symlink_type_t symlink;
readlink_type_t readlink;
struct fs_node *ptr; /* Alias pointer, for symlinks. */
uint32_t offset; /* Offset for read operations XXX move this to new "file descriptor" entry */
@ -129,6 +135,8 @@ fs_node_t *clone_fs(fs_node_t * source);
int ioctl_fs(fs_node_t *node, int request, void * argp);
int chmod_fs(fs_node_t *node, int mode);
int unlink_fs(char * name);
int symlink_fs(char * value, char * name);
int readlink_fs(fs_node_t * node, char * buf, size_t size);
void vfs_install(void);
void * vfs_mount(char * path, fs_node_t * local_root);

View File

@ -663,6 +663,38 @@ static int sys_mount(char * arg, char * mountpoint, char * type, unsigned long f
return -EFAULT;
}
static int sys_symlink(char * target, char * name) {
PTR_VALIDATE(target);
PTR_VALIDATE(name);
return symlink_fs(target, name);
}
static int sys_readlink(const char * file, char * ptr, int len) {
PTR_VALIDATE(file);
fs_node_t * node = kopen((char *) file, O_PATH | O_NOFOLLOW);
if (!node) {
return -ENOENT;
}
int rv = readlink_fs(node, ptr, len);
close_fs(node);
return rv;
}
static int sys_lstat(char * file, uintptr_t st) {
int result;
PTR_VALIDATE(file);
PTR_VALIDATE(st);
fs_node_t * fn = kopen(file, O_PATH | O_NOFOLLOW);
result = stat_node(fn, st);
if (fn) {
close_fs(fn);
}
return result;
}
/*
* System Call Internals
*/
static int (*syscalls[])() = {
/* System Call Table */
[SYS_EXT] = sys_exit,
@ -709,6 +741,9 @@ static int (*syscalls[])() = {
[SYS_WAITPID] = sys_waitpid,
[SYS_PIPE] = sys_pipe,
[SYS_MOUNT] = sys_mount,
[SYS_SYMLINK] = sys_symlink,
[SYS_READLINK] = sys_readlink,
[SYS_LSTAT] = sys_lstat,
};
uint32_t num_syscalls = sizeof(syscalls) / sizeof(*syscalls);

View File

@ -42,3 +42,6 @@
#define SYS_WAITPID 53
#define SYS_PIPE 54
#define SYS_MOUNT 55
#define SYS_SYMLINK 56
#define SYS_READLINK 57
#define SYS_LSTAT 58

View File

@ -89,6 +89,9 @@ DEFN_SYSCALL1(unlink, 52, char *);
DEFN_SYSCALL3(waitpid, 53, int, int *, int);
DEFN_SYSCALL1(pipe, 54, int *);
DEFN_SYSCALL5(mount, SYS_MOUNT, char *, char *, char *, unsigned long, void *);
DEFN_SYSCALL2(symlink, SYS_SYMLINK, char *, char *);
DEFN_SYSCALL3(readlink, SYS_READLINK, char *, char *, int);
DEFN_SYSCALL2(lstat, SYS_LSTAT, char *, void *);
static int toaru_debug_stubs_enabled(void) {
static int checked = 0;
@ -319,8 +322,15 @@ char *getwd(char *buf) {
return getcwd(buf, 256);
}
int lstat(const char *path, struct stat *buf) {
return stat(path, buf);
int lstat(const char *path, struct stat *st) {
int ret = syscall_lstat((char *)path, (void *)st);
if (ret >= 0) {
return ret;
} else {
errno = -ret;
memset(st, 0x00, sizeof(struct stat));
return ret;
}
}
int mkdir(const char *pathname, mode_t mode) {
@ -688,3 +698,24 @@ int mount(char * source, char * target, char * type, unsigned long flags, void *
return r;
}
int symlink(char * target, char * name) {
int r = syscall_symlink(target, name);
if (r < 0) {
errno = -r;
return -1;
}
return r;
}
int readlink(char * name, char * buf, size_t len) {
int r = syscall_readlink(name, buf, len);
if (r < 0) {
errno = -r;
return -1;
}
return r;
}