From 2aaa3da856c9548a9505689d8d38f5f22f86d2bd Mon Sep 17 00:00:00 2001 From: Matheus Date: Thu, 20 Oct 2022 21:05:39 -0300 Subject: [PATCH] vfs: initial symlink implementation --- src/fs/initrd.c | 6 ++ src/fs/tmpfs.c | 40 +++++++++++++- src/fs/vfs.c | 115 ++++++++++++++++++++++++++++++++++++--- src/include/kernel/vfs.h | 8 ++- 4 files changed, 159 insertions(+), 10 deletions(-) diff --git a/src/fs/initrd.c b/src/fs/initrd.c index 2108950b..2366b9d4 100644 --- a/src/fs/initrd.c +++ b/src/fs/initrd.c @@ -178,6 +178,12 @@ void initrd_parse(){ if(err) printf("Failed creating %s: ERROR %lu\n", entry.name, err); break; + case TAR_SYMLINK: + err = vfs_symlink(vfs_root(), entry.name, entry.link, entry.mode); + if(err) + printf("Failed creating %s: ERROR %lu\n", entry.name, err); + break; + } diff --git a/src/fs/tmpfs.c b/src/fs/tmpfs.c index 1b7fe525..32e39ba8 100644 --- a/src/fs/tmpfs.c +++ b/src/fs/tmpfs.c @@ -182,8 +182,46 @@ static int tmpfs_mksocket(dirnode_t* parent, char* name, mode_t mode){ return 0; } + +static int tmpfs_symlink(dirnode_t* parent, char* name, char* target, mode_t mode){ + int error = tmpfs_create(parent, name, mode); + + if(error) + return error; + + vnode_t* node = hashtable_get(&parent->children, name); + + node->st.st_mode = MAKETYPE(TYPE_LINK) | mode; + + tmpfs_write(&error, node, target, strlen(target)+1, 0); + + return 0; + +} + +static int tmpfs_readlink(vnode_t *node, char** buff, size_t* linksize){ + + if(node->st.st_size >= 512) + return ENAMETOOLONG; + + *buff = alloc(node->st.st_size+1); + + if(!*buff) + return ENOMEM; + + int error; + + *linksize = tmpfs_read(&error, node, *buff, 512, 0); + + if(error) + free(*buff); + + return error; + +} + static fscalls_t funcs = { - tmpfs_mount, tmpfs_unmount, tmpfs_open, tmpfs_close, tmpfs_mkdir, tmpfs_create, tmpfs_write, tmpfs_read, tmpfs_getdirent, tmpfs_chmod, tmpfs_mksocket + tmpfs_mount, tmpfs_unmount, tmpfs_open, tmpfs_close, tmpfs_mkdir, tmpfs_create, tmpfs_write, tmpfs_read, tmpfs_getdirent, tmpfs_chmod, tmpfs_mksocket, tmpfs_symlink, tmpfs_readlink }; diff --git a/src/fs/vfs.c b/src/fs/vfs.c index 18b133b4..3cbe5a0c 100644 --- a/src/fs/vfs.c +++ b/src/fs/vfs.c @@ -9,6 +9,8 @@ #include #include +#define VFS_MAX_LOOP 5 + dirnode_t* vfsroot; hashtable fsfuncs; static int lock; @@ -219,7 +221,7 @@ int vfs_open(vnode_t** buff, dirnode_t* ref, char* path){ spinlock_acquire(&lock); - int res = vfs_resolvepath(&node, NULL, ref, path, NULL); + int res = vfs_resolvepath(&node, NULL, ref, path, NULL, true, VFS_MAX_LOOP); if(res){ spinlock_release(&lock); @@ -244,7 +246,7 @@ int vfs_create(dirnode_t* ref, char* path, mode_t mode){ spinlock_acquire(&lock); - int result = vfs_resolvepath(&buff, &parent, ref, path, name); + int result = vfs_resolvepath(&buff, &parent, ref, path, name, true, VFS_MAX_LOOP); if(buff){ spinlock_release(&lock); @@ -274,7 +276,7 @@ int vfs_mksocket(dirnode_t* ref, char* path, mode_t mode){ spinlock_acquire(&lock); - int result = vfs_resolvepath(&buff, &parent, ref, path, name); + int result = vfs_resolvepath(&buff, &parent, ref, path, name, true, VFS_MAX_LOOP); if(buff){ spinlock_release(&lock); @@ -300,6 +302,44 @@ int vfs_mksocket(dirnode_t* ref, char* path, mode_t mode){ return 0; } +int vfs_symlink(dirnode_t* ref, char* path, char* target, mode_t mode){ + + if(strlen(target) >= 512) + return ENAMETOOLONG; + + dirnode_t* parent = NULL; + dirnode_t* buff = NULL; + char name[512]; + + spinlock_acquire(&lock); + + int result = vfs_resolvepath(&buff, &parent, ref, path, name, true, VFS_MAX_LOOP); + + if(buff){ + spinlock_release(&lock); + return EEXIST; + } + + if(!parent){ + spinlock_release(&lock); + return result; + } + + if(!parent->vnode.fs->calls->symlink){ + spinlock_release(&lock); + return ENOSYS; + } + + result = parent->vnode.fs->calls->symlink(parent, name, target, mode); + + spinlock_release(&lock); + + if(result) return result; + + + +} + int vfs_mkdir(dirnode_t* ref, char* path, mode_t mode){ dirnode_t* parent = NULL; @@ -309,7 +349,7 @@ int vfs_mkdir(dirnode_t* ref, char* path, mode_t mode){ spinlock_acquire(&lock); - int result = vfs_resolvepath(&buff, &parent, ref, path, name); + int result = vfs_resolvepath(&buff, &parent, ref, path, name, true, VFS_MAX_LOOP); if(buff){ spinlock_release(&lock); @@ -346,7 +386,7 @@ int vfs_mount(dirnode_t* ref, char* device, char* mountpoint, char* fs, int moun if(device){ dev = ref; if(*device){ - int result = vfs_resolvepath(&dev, &parent, ref, device, NULL); + int result = vfs_resolvepath(&dev, &parent, ref, device, NULL, true, VFS_MAX_LOOP); if(result){ spinlock_release(&lock); @@ -356,7 +396,7 @@ int vfs_mount(dirnode_t* ref, char* device, char* mountpoint, char* fs, int moun } - int result = vfs_resolvepath(&mountdir, &parent, ref, mountpoint, NULL); + int result = vfs_resolvepath(&mountdir, &parent, ref, mountpoint, NULL, true, VFS_MAX_LOOP); if(result){ spinlock_release(&lock); @@ -429,13 +469,21 @@ dirnode_t* vfs_newdirnode(char* name, fs_t* fs, void* fsdata, dirnode_t* parent) } -int vfs_resolvepath(vnode_t** result, dirnode_t** resultparent, dirnode_t* ref, char *path, char* namebuff){ +// TODO the stack usage is scarily high + +int vfs_resolvepath(vnode_t** result, dirnode_t** resultparent, dirnode_t* ref, char *path, char* namebuff, bool followlinks, int maxloop){ + if(strlen(path) >= 512) + return ENAMETOOLONG; + + if(maxloop == 0) + return ELOOP; + dirnode_t* iterator = ref; char name[512]; off_t nameoffset = 0; - memset(name, 0, 512); + memset(name, 0, 256); iterator = mountpoint(iterator); @@ -457,6 +505,33 @@ int vfs_resolvepath(vnode_t** result, dirnode_t** resultparent, dirnode_t* ref, ++nameoffset; name[offset] = 0; + + // do we need to follow a symlink + + if(GETTYPE(iterator->vnode.st.st_mode) == TYPE_LINK && followlinks){ + + if(!iterator->vnode.fs->calls->readlink) + return ENOSYS; + + size_t size; + char* linkname; + + int error = iterator->vnode.fs->calls->readlink(iterator, &linkname, &size); + if(error) + return error; + + linkname[size] = '\0'; + + printf("reading link: %s\n", linkname); + + error = vfs_resolvepath(&iterator, NULL, iterator, linkname, namebuff, true, maxloop-1); // XXX this might break some parent stuff when following links + + free(linkname); + + if(error) + return error; + + } // make sure we're a directory @@ -501,6 +576,30 @@ int vfs_resolvepath(vnode_t** result, dirnode_t** resultparent, dirnode_t* ref, if(GETTYPE(iterator->vnode.st.st_mode) == TYPE_DIR) iterator = mountpoint(iterator); + + // check if result is a symlink + + if(GETTYPE(iterator->vnode.st.st_mode) == TYPE_LINK && followlinks){ + if(!iterator->vnode.fs->calls->readlink) + return ENOSYS; + + size_t size; + char* linkname; + + int error = iterator->vnode.fs->calls->readlink(iterator, &linkname, &size); + if(error) + return error; + + linkname[size] = '\0'; + + error = vfs_resolvepath(&iterator, NULL, iterator->vnode.parent, linkname, namebuff, true, maxloop-1); // XXX this might break some parent stuff when following links + + free(linkname); + + if(error) + return error; + } + *result = (vnode_t*)iterator; return 0; diff --git a/src/include/kernel/vfs.h b/src/include/kernel/vfs.h index ae3a42be..04474aee 100644 --- a/src/include/kernel/vfs.h +++ b/src/include/kernel/vfs.h @@ -6,6 +6,7 @@ #include #include #include +#include struct _vnode_t; struct _fscalls_t; @@ -65,6 +66,10 @@ typedef struct _fscalls_t { int (*chmod)(vnode_t* node, mode_t mode); // create socket in address int (*mksocket)(dirnode_t* ref, char* path, mode_t mode); + // creates a symlink + int (*symlink)(dirnode_t* ref, char* path, char* target, mode_t mode); + // reads a symlink + int (*readlink)(vnode_t* node, char** buff, size_t* linksize); } fscalls_t; int vfs_mksocket(dirnode_t* ref, char* path, mode_t mode); @@ -81,6 +86,7 @@ int vfs_getdirent(dirnode_t* node, dent_t* buff, size_t count, uintmax_t offset, int vfs_ioctl(vnode_t* node, unsigned long request, void* arg, int* result); int vfs_poll(vnode_t* node, pollfd* fd); int vfs_chmod(vnode_t* node, mode_t mode); +int vfs_symlink(dirnode_t* ref, char* path, char* target, mode_t mode); void vfs_init(); dirnode_t* vfs_root(); @@ -89,7 +95,7 @@ void vfs_releasenode(vnode_t* node); vnode_t* vfs_newnode(char* name, fs_t* fs, void* fsdata); dirnode_t* vfs_newdirnode(char* name, fs_t* fs, void* fsdata, dirnode_t* parent); void vfs_destroynode(vnode_t* node); -int vfs_resolvepath(vnode_t** result, dirnode_t** parent, dirnode_t* ref, char* path, char* name); +int vfs_resolvepath(vnode_t** result, dirnode_t** parent, dirnode_t* ref, char* path, char* name, bool followlinks, int maxloop); #define UNIMPLEMENTED { return ENOSYS; }