Added symlink support to vfs and plumbed it to syscall layer
This commit requires a rebuild of the toolchain.
This commit is contained in:
parent
478835401e
commit
e8689bb0c6
162
kernel/fs/vfs.c
162
kernel/fs/vfs.c
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user