diff --git a/kernel-src/arch/x86-64/syscall.asm b/kernel-src/arch/x86-64/syscall.asm index a560a006..f28969e8 100644 --- a/kernel-src/arch/x86-64/syscall.asm +++ b/kernel-src/arch/x86-64/syscall.asm @@ -39,6 +39,7 @@ extern syscall_ftruncate extern syscall_mount extern syscall_fchownat extern syscall_utimensat +extern syscall_renameat syscalltab: dq syscall_print dq syscall_mmap @@ -80,7 +81,8 @@ dq syscall_ftruncate dq syscall_mount dq syscall_fchownat dq syscall_utimensat -syscallcount equ 40 +dq syscall_renameat +syscallcount equ 41 section .text global arch_syscall_entry ; on entry: diff --git a/kernel-src/arch/x86-64/syscalllog.c b/kernel-src/arch/x86-64/syscalllog.c index 1cfc0d39..0b2e8908 100644 --- a/kernel-src/arch/x86-64/syscalllog.c +++ b/kernel-src/arch/x86-64/syscalllog.c @@ -4,7 +4,7 @@ #ifdef SYSCALL_LOGGING -#define SYSCALL_COUNT 40 +#define SYSCALL_COUNT 41 #define LOGSTR(x) arch_e9_puts(x) static char *name[] = { @@ -48,6 +48,7 @@ static char *name[] = { "mount", "fchownat", "utimensat", + "renameat" }; static char *args[] = { @@ -90,7 +91,8 @@ static char *args[] = { "fd %d size %lu", // ftruncate "backing %p mountpoint %s fs %s flags %d data %p", // mount "fd %d path %s owner %d group %d flags %x", // fchownat - "dirfd %d path %p times %p flags %x" // utimensat + "dirfd %d path %p times %p flags %x", // utimensat + "olddirfd %d oldpath %s newdirfd %d newpath %s flags %d" // renameat }; #endif diff --git a/kernel-src/fs/devfs.c b/kernel-src/fs/devfs.c index ba998ce8..6e0438f7 100644 --- a/kernel-src/fs/devfs.c +++ b/kernel-src/fs/devfs.c @@ -334,7 +334,8 @@ static vops_t vnops = { .isatty = devfs_isatty, .ioctl = devfs_ioctl, .maxseek = devfs_maxseek, - .resize = devfs_enodev + .resize = devfs_enodev, + .rename = devfs_enodev }; static void ctor(scache_t *cache, void *obj) { diff --git a/kernel-src/fs/pipefs.c b/kernel-src/fs/pipefs.c index a9d36fe8..65c52942 100644 --- a/kernel-src/fs/pipefs.c +++ b/kernel-src/fs/pipefs.c @@ -235,7 +235,8 @@ static vops_t vnops = { .mmap = pipefs_enodev, .munmap = pipefs_enodev, .getdents = pipefs_enodev, - .resize = pipefs_enodev + .resize = pipefs_enodev, + .rename = pipefs_enodev }; static void ctor(scache_t *cache, void *obj) { diff --git a/kernel-src/fs/tmpfs.c b/kernel-src/fs/tmpfs.c index 6705c9af..668c9ea5 100644 --- a/kernel-src/fs/tmpfs.c +++ b/kernel-src/fs/tmpfs.c @@ -343,6 +343,68 @@ static int tmpfs_link(vnode_t *node, vnode_t *dir, char *name, cred_t *cred) { return 0; } +static int tmpfs_rename(vnode_t *source, char *oldname, vnode_t *target, char *newname, int flags) { + if (source->vfs != target->vfs) + return EXDEV; + + if (source->type != V_TYPE_DIR || target->type != V_TYPE_DIR) + return ENOTDIR; + + tmpfsnode_t *sourcedir = (tmpfsnode_t *)source; + tmpfsnode_t *targetdir = (tmpfsnode_t *)target; + + size_t oldlen = strlen(oldname); + size_t newlen = strlen(newname); + + if (target != source) { + VOP_LOCK(source); + VOP_LOCK(target); + } else { + VOP_LOCK(target); + } + + // get original node + void *v; + int error = hashtable_get(&sourcedir->children, &v, oldname, oldlen); + if (error) + goto cleanup; + + vnode_t *node = v; + vnode_t *oldnode = NULL; + + // get old node in target + error = hashtable_get(&targetdir->children, &v, newname, newlen); + if (error != ENOENT && error != 0) + goto cleanup; + else if (error == 0) // found + oldnode = v; + + // same link, rename is a no-op + if (sourcedir == targetdir && strcmp(oldname, newname) == 0) + goto cleanup; + + // link node to new name + error = hashtable_set(&targetdir->children, node, newname, newlen, true); + if (error) + goto cleanup; + + // remove old link. should not fail + __assert(hashtable_remove(&sourcedir->children, oldname, oldlen) == 0); + + if (oldnode) + VOP_RELEASE(oldnode); + + cleanup: + if (target != source) { + VOP_UNLOCK(target); + VOP_UNLOCK(source); + } else { + VOP_UNLOCK(target); + } + + return error; +} + static int tmpfs_symlink(vnode_t *node, char *name, vattr_t *attr, char *path, cred_t *cred) { if (node->type != V_TYPE_DIR) return ENOTDIR; @@ -458,7 +520,7 @@ static int tmpfs_getdents(vnode_t *node, dent_t *buffer, size_t count, uintmax_t ent->d_off = offset; ent->d_reclen = sizeof(dent_t); ent->d_type = vfs_getposixtype(itmpnode->vnode.type); - strcpy(ent->d_name, entry->key); + memcpy(ent->d_name, entry->key, entry->keysize); *readcount += 1; } @@ -505,7 +567,8 @@ static vops_t vnops = { .mmap = tmpfs_mmap, .munmap = tmpfs_munmap, .getdents = tmpfs_getdents, - .resize = tmpfs_resize + .resize = tmpfs_resize, + .rename = tmpfs_rename }; static tmpfsnode_t *newnode(vfs_t *vfs, int type) { diff --git a/kernel-src/include/kernel/vfs.h b/kernel-src/include/kernel/vfs.h index 15a75903..2ee3a6d4 100644 --- a/kernel-src/include/kernel/vfs.h +++ b/kernel-src/include/kernel/vfs.h @@ -98,6 +98,7 @@ typedef struct vops_t { int (*ioctl)(vnode_t *node, unsigned long request, void *arg, int *result); int (*maxseek)(vnode_t *node, size_t *max); int (*resize)(vnode_t *node, size_t newsize, cred_t *cred); + int (*rename)(vnode_t *source, char *oldname, vnode_t *target, char *newname, int flags); } vops_t; #define VFS_INIT(v, o, f) \ @@ -145,6 +146,7 @@ typedef struct vops_t { #define VOP_IOCTL(v, r, a, rp) ((v)->ops->ioctl ? (v)->ops->ioctl(v, r, a, rp) : ENOTTY) #define VOP_MAXSEEK(v, rp) ((v)->ops->maxseek ? (v)->ops->maxseek(v, rp) : ENOTTY) #define VOP_RESIZE(v, s, c) (v)->ops->resize(v, s, c) +#define VOP_RENAME(s, o, t, n, f) (s)->ops->rename(s, o, t, n, f) #define VOP_HOLD(v) __atomic_add_fetch(&(v)->refcount, 1, __ATOMIC_SEQ_CST) #define VOP_RELEASE(v) {\ if (__atomic_sub_fetch(&(v)->refcount, 1, __ATOMIC_SEQ_CST) == 0) {\ diff --git a/kernel-src/sys/syscalls/renameat.c b/kernel-src/sys/syscalls/renameat.c new file mode 100644 index 00000000..0133edca --- /dev/null +++ b/kernel-src/sys/syscalls/renameat.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include + +syscallret_t syscall_renameat(context_t *context, int olddirfd, const char *uoldpath, int newdirfd, const char *unewpath, unsigned int flags) { + syscallret_t ret = { + .ret = -1 + }; + + __assert(flags == 0); + + char *oldcomponent = NULL; + char *newcomponent = NULL; + char *oldpath = alloc(strlen(uoldpath) + 1); + if (oldpath == NULL) { + ret.errno = ENOMEM; + return ret; + } + + char *newpath = alloc(strlen(unewpath) + 1); + if (newpath == NULL) { + free(oldpath); + ret.errno = ENOMEM; + return ret; + } + + // TODO safe strcopy + strcpy(oldpath, uoldpath); + strcpy(newpath, unewpath); + + vnode_t *olddirnode = NULL; + file_t *oldfile = NULL; + vnode_t *newdirnode = NULL; + file_t *newfile = NULL; + vnode_t *targetdirnode = NULL; + vnode_t *sourcedirnode = NULL; + ret.errno = dirfd_enter(oldpath, olddirfd, &oldfile, &olddirnode); + + if (ret.errno) + goto cleanup; + + ret.errno = dirfd_enter(newpath, newdirfd, &newfile, &newdirnode); + + if (ret.errno) + goto cleanup; + + oldcomponent = alloc(strlen(oldpath)); + newcomponent = alloc(strlen(newpath)); + + if (oldcomponent == NULL && newcomponent == NULL) + goto cleanup; + + ret.errno = vfs_lookup(&sourcedirnode, olddirnode, oldpath, oldcomponent, VFS_LOOKUP_NOLINK | VFS_LOOKUP_PARENT); + if (ret.errno) + goto cleanup; + + ret.errno = vfs_lookup(&targetdirnode, newdirnode, newpath, newcomponent, VFS_LOOKUP_NOLINK | VFS_LOOKUP_PARENT); + if (ret.errno) + goto cleanup; + + ret.errno = VOP_RENAME(sourcedirnode, oldcomponent, targetdirnode, newcomponent, flags); + ret.ret = ret.errno ? -1 : 0; + + cleanup: + + if (targetdirnode) + VOP_RELEASE(targetdirnode); + + if (sourcedirnode) + VOP_RELEASE(sourcedirnode); + + if (oldcomponent) + free(oldcomponent); + + if (newcomponent) + free(newcomponent); + + if (olddirnode) + dirfd_leave(olddirnode, oldfile); + + if (newdirnode) + dirfd_leave(newdirnode, newfile); + + free(oldpath); + free(newpath); + + return ret; +}