diff options
author | dzwdz | 2024-07-20 23:17:36 +0200 |
---|---|---|
committer | dzwdz | 2024-07-20 23:17:36 +0200 |
commit | 1fc2d9c88af77c3af81b6bd461e6b019b97d2dbb (patch) | |
tree | 56d19935958983882872495f556bdb74c8ae26b8 | |
parent | e88aee660ea668cc96d8a5a11060b6e02b2f0fd7 (diff) |
*: moving files
-rw-r--r-- | man/duplex.2 | 56 | ||||
-rw-r--r-- | src/cmd/mv.c | 17 | ||||
-rw-r--r-- | src/cmd/tmpfs.c | 71 | ||||
-rw-r--r-- | src/kernel/handle.h | 1 | ||||
-rw-r--r-- | src/kernel/syscalls.c | 32 | ||||
-rw-r--r-- | src/kernel/vfs/request.c | 1 | ||||
-rw-r--r-- | src/kernel/vfs/request.h | 4 | ||||
-rw-r--r-- | src/libc/include/stdio.h | 2 | ||||
-rw-r--r-- | src/libc/stdio/misc.c | 30 | ||||
-rw-r--r-- | src/libc/syscall.c | 4 | ||||
-rw-r--r-- | src/libk/include/camellia/flags.h | 2 | ||||
-rw-r--r-- | src/libk/include/camellia/syscalls.h | 3 | ||||
-rw-r--r-- | src/libk/include/camellia/types.h | 3 |
13 files changed, 215 insertions, 11 deletions
diff --git a/man/duplex.2 b/man/duplex.2 new file mode 100644 index 0000000..192e72c --- /dev/null +++ b/man/duplex.2 @@ -0,0 +1,56 @@ +.Dd Jul 17, 2024 +.Dt DUPLEX 2 +.Os Camellia +.Sh NAME +.Nm duplex +.Nd rename a file +.Sh SYNOPSIS +.In camellia/syscalls.h +.Ft long +.Fn _sys_duplex "hid_t from" "hid_t to" "int flags" +.Sh DESCRIPTION +.Nm +is used for operations on two files or locations within the same filesystem +\(em renames, links, etc. +.Fa flags +is a bitmask with the following options: +.Bl -tag -width DUPLEX_REMOVE +.It Dv DUPLEX_REMOVE +Remove the source file after the operation \(em in other words, perform a move, +instead of creating a new link. +.El +.Sh SEE ALSO +.Xr open 2 , +.Xr rename 3 +.Sh BUGS +.Xr rename 3 isn't atomic, +as the destination file needs to be created before the duplex call. +This could be fixed by having +.Xr open 2 +accept a (hypothetical) +.Dv OPEN_MOVETARGET +flag. +.Pp +There are some cases where a +.Nm +call across different filesystems fails despite "making sense". +.Sh RATIONALE +Accepting two handles is the only sensible way to handle moving files, +considering +.Xr open 2 Ns 's +role as a +.Dq narrow waist . +Having a generic +.Nm +call for all operations on two handles within a single filesystem seems to +make sense. +.Pp +One other similar approach would be to have a way to send arbitrary handles +to open files +.Pq which I already plan to implement , +and to also have a syscall that can +.Dq unwrap +a handle controlled by the current process. +Then that syscall would effectively be able to do everything +.Nm +can \(em but that seems more complex and awkward to use. diff --git a/src/cmd/mv.c b/src/cmd/mv.c new file mode 100644 index 0000000..e7bcc46 --- /dev/null +++ b/src/cmd/mv.c @@ -0,0 +1,17 @@ +#include <errno.h> +#include <string.h> +#include <stdio.h> + +int +main(int argc, const char *argv[]) +{ + if (argc != 3) { + fprintf(stderr, "usage: mv from to\n"); + return 1; + } + + if (rename(argv[1], argv[2]) < 0) { + fprintf(stderr, "mv: %s\n", strerror(errno)); + return 1; + } +} diff --git a/src/cmd/tmpfs.c b/src/cmd/tmpfs.c index 6d58790..a031a44 100644 --- a/src/cmd/tmpfs.c +++ b/src/cmd/tmpfs.c @@ -1,3 +1,4 @@ +#include <assert.h> #include <camellia/flags.h> #include <camellia/fs/dir.h> #include <camellia/fs/misc.h> @@ -38,8 +39,10 @@ static struct node *tmpfs_open(const char *path, struct ufs_request *req); static struct node *node_getchild(struct node *parent, const char *name, size_t len); /** Corresponds to close(); drops a reference. */ static void node_close(struct node *node); +static long node_move(struct node *from, struct node *to); /** Removes a file. It's kept in memory until all the open handles are closed. */ static long node_remove(struct node *node); +static void node_sanity(struct node *node, struct node **from); static struct node *node_getchild(struct node *parent, const char *name, size_t len) { @@ -100,27 +103,84 @@ static struct node *tmpfs_open(const char *path, struct ufs_request *req) { static void node_close(struct node *node) { node->open--; - if (!node->ref && node != &node_root && node->open == 0) { + if (node->ref == NULL && node != &node_root && node->open == 0) { + if (node->name) { + free(node->name); + } free(node->buf); free(node); } } +static long node_move(struct node *from, struct node *to) { + if (from == &node_root || to == &node_root) { + return -1; + } + + assert(from && to); + /* 1. unlink from */ + if (from->ref) { + assert(*from->ref == from); + if (from->sibling) { + from->sibling->ref = from->ref; + *from->sibling->ref = from->sibling; + } else { + *from->ref = NULL; + } + from->sibling = NULL; + from->ref = NULL; + } else { + assert(from->sibling == NULL); + } + + /* 2. link *from in place of *to + * note that there's still a reference to *to in the handle */ + if (to->ref) { + assert(*to->ref == to); + from->sibling = to->sibling; + if (from->sibling) { + from->sibling->ref = &from->sibling; + } + from->ref = to->ref; + *from->ref = from; + + to->ref = NULL; + to->sibling = NULL; + } + from->name = to->name; + from->namelen = to->namelen; + to->name = NULL; + to->namelen = 0; + return 0; +} + static long node_remove(struct node *node) { if (node == &node_root) return -1; if (!node->ref) return -1; if (node->child) return -ENOTEMPTY; *node->ref = node->sibling; node->ref = NULL; + node->sibling = NULL; node_close(node); return 0; } +static void node_sanity(struct node *node, struct node **from) { + if (node == NULL) return; + if (node != &node_root && node->ref != from) { + assert(false); + } + node_sanity(node->sibling, &node->sibling); + node_sanity(node->child, &node->child); +} + int main(void) { const size_t buflen = 4096; char *buf = malloc(buflen); if (!buf) return -1; + node_sanity(&node_root, NULL); + for (;;) { struct ufs_request req; hid_t reqh = ufs_wait(buf, buflen, &req); @@ -189,10 +249,19 @@ int main(void) { _sys_fs_respond(reqh, NULL, -1, 0); break; + case VFSOP_DUPLEX: + if (req.flags == DUPLEX_REMOVE) { + _sys_fs_respond(reqh, NULL, node_move(req.id, req.id2), 0); + } else { + _sys_fs_respond(reqh, NULL, -ENOSYS, 0); + } + break; + default: _sys_fs_respond(reqh, NULL, -1, 0); break; } + node_sanity(&node_root, NULL); } return 1; } diff --git a/src/kernel/handle.h b/src/kernel/handle.h index af9d45a..abbfa68 100644 --- a/src/kernel/handle.h +++ b/src/kernel/handle.h @@ -19,6 +19,7 @@ struct Handle { enum handle_type type; VfsBackend *backend; /* HANDLE_FILE | HANDLE_FS_FRONT */ void __user *file_id; /* only applicable to HANDLE_FILE */ + // TODO store flags instead bool readable, writeable; /* HANDLE_FILE | HANDLE_PIPE */ VfsReq *req; /* HANDLE_FS_REQ */ struct { diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c index 3fc09fa..23a8883 100644 --- a/src/kernel/syscalls.c +++ b/src/kernel/syscalls.c @@ -456,6 +456,37 @@ hid_t _sys_getnull(int flags) { SYSCALL_RETURN((0 <= hid) ? hid : -EMFILE); } +long _sys_duplex(hid_t from, hid_t to, int flags) { + if ((flags & ~DUPLEX_REMOVE) != 0) { + SYSCALL_RETURN(-ENOSYS); + } + + Handle *fromh = hs_get(proc_cur->hs, from); + Handle *toh = hs_get(proc_cur->hs, to); + if (fromh == NULL || toh == NULL) { + SYSCALL_RETURN(-EBADF); + } + if (fromh->type != HANDLE_FILE || toh->type != HANDLE_FILE) { + SYSCALL_RETURN(-ENOSYS); + } + if (fromh->backend != toh->backend) { + SYSCALL_RETURN(-ENOSYS); + } + if (!fromh->readable || !fromh->writeable || !toh->writeable) { + SYSCALL_RETURN(-EACCES); + } + + vfsreq_dispatchcopy((VfsReq) { + .type = VFSOP_DUPLEX, + .id = fromh->file_id, + .id2 = toh->file_id, + .flags = flags, + .caller = proc_cur, + .backend = fromh->backend, + }); + return -1; // dummy +} + long _sys_execbuf(void __user *ubuf, size_t len) { if (len == 0) SYSCALL_RETURN(0); static_assert(EXECBUF_MAX_LEN <= KMALLOC_MAX); @@ -512,6 +543,7 @@ long _syscall(long num, long a, long b, long c, long d, long e) { break; case _SYS_GETPROCFS: _sys_getprocfs(a); break; case _SYS_TIME: _sys_time(a); break; case _SYS_GETNULL: _sys_getnull(a); + break; case _SYS_DUPLEX: _sys_duplex(a, b, c); break; case _SYS_EXECBUF: _sys_execbuf((userptr_t)a, b); break; case _SYS_DEBUG_KLOG: _sys_debug_klog((userptr_t)a, b); break; diff --git a/src/kernel/vfs/request.c b/src/kernel/vfs/request.c index 92b904f..3f6fda7 100644 --- a/src/kernel/vfs/request.c +++ b/src/kernel/vfs/request.c @@ -166,6 +166,7 @@ vfsback_useraccept(VfsReq *req) res.len = len; res.capacity = req->output.len; res.id = req->id; + res.id2 = req->id2; res.offset = req->offset; res.flags = req->flags; res.op = req->type; diff --git a/src/kernel/vfs/request.h b/src/kernel/vfs/request.h index 45ec687..d6facfd 100644 --- a/src/kernel/vfs/request.h +++ b/src/kernel/vfs/request.h @@ -50,9 +50,7 @@ struct VfsReq { size_t len; } output; - // TODO why doesn't this just have a reference to the handle? - - void __user *id; // handle.file.id + void __user *id, *id2; // handle.file.id long offset; int flags; diff --git a/src/libc/include/stdio.h b/src/libc/include/stdio.h index 159bdca..d533025 100644 --- a/src/libc/include/stdio.h +++ b/src/libc/include/stdio.h @@ -83,7 +83,7 @@ int putchar(int c); off_t lseek(int fd, off_t off, int whence); int remove(const char *path); -int rename(const char *old, const char *new); +int rename(const char *oldpath, const char *newpath); #define L_tmpnam (5 + 16 + 1) char *tmpnam(char *s); diff --git a/src/libc/stdio/misc.c b/src/libc/stdio/misc.c index 7e8e746..6cd5b35 100644 --- a/src/libc/stdio/misc.c +++ b/src/libc/stdio/misc.c @@ -1,3 +1,6 @@ +#include <camellia.h> +#include <camellia/flags.h> +#include <camellia/syscalls.h> #include <errno.h> #include <stdio.h> #include <string.h> @@ -30,11 +33,28 @@ int remove(const char *path) { return unlink(path); } -// TODO! VFSOP_MOVE -int rename(const char *old, const char *new) { - (void)old; (void)new; - errno = ENOSYS; - return -1; +int rename(const char *oldpath, const char *newpath) { + // TODO require a duplex flag in open() + hid_t from, to; + from = camellia_open(oldpath, OPEN_RW); + if (from < 0) { + return -1; + } + to = camellia_open(newpath, OPEN_WRITE | OPEN_CREATE); + if (to < 0) { + close(from); + return -1; + } + + long ret = _sys_duplex(from, to, DUPLEX_REMOVE); + close(from); + close(to); + if (ret < 0) { + errno = -ret; + return -1; + } else { + return 0; + } } // TODO tmpnam diff --git a/src/libc/syscall.c b/src/libc/syscall.c index f44c775..c6e3762 100644 --- a/src/libc/syscall.c +++ b/src/libc/syscall.c @@ -98,6 +98,10 @@ hid_t _sys_getnull(int flags) { return (hid_t)_syscall(_SYS_GETNULL, (long)flags, 0, 0, 0, 0); } +long _sys_duplex(hid_t from, hid_t to, int flags) { + return _syscall(_SYS_DUPLEX, (long)from, (long)to, (long)flags, 0, 0); +} + long _sys_execbuf(void __user *buf, size_t len) { return _syscall(_SYS_EXECBUF, (long)buf, (long)len, 0, 0, 0); } diff --git a/src/libk/include/camellia/flags.h b/src/libk/include/camellia/flags.h index 472215b..1585ace 100644 --- a/src/libk/include/camellia/flags.h +++ b/src/libk/include/camellia/flags.h @@ -30,3 +30,5 @@ * The idea is that if all flags which allow modifying the filesystem state require * OPEN_WRITE to be set, filesystem handlers could just check for the OPEN_WRITE flag. */ #define OPEN_CREATE 4 + +#define DUPLEX_REMOVE 1 diff --git a/src/libk/include/camellia/syscalls.h b/src/libk/include/camellia/syscalls.h index 3e7576e..16576e0 100644 --- a/src/libk/include/camellia/syscalls.h +++ b/src/libk/include/camellia/syscalls.h @@ -23,6 +23,7 @@ #define _SYS_GETPROCFS 22 #define _SYS_TIME 23 #define _SYS_GETNULL 24 +#define _SYS_DUPLEX 25 #define _SYS_EXECBUF 100 #define _SYS_DEBUG_KLOG 101 @@ -89,6 +90,8 @@ uint64_t _sys_time(int flags); hid_t _sys_getnull(int flags); +long _sys_duplex(hid_t from, hid_t to, int flags); + /* see shared/execbuf.h */ long _sys_execbuf(void __user *buf, size_t len); diff --git a/src/libk/include/camellia/types.h b/src/libk/include/camellia/types.h index 65200d7..3117877 100644 --- a/src/libk/include/camellia/types.h +++ b/src/libk/include/camellia/types.h @@ -20,13 +20,14 @@ enum vfs_op { VFSOP_GETSIZE, VFSOP_REMOVE, VFSOP_CLOSE, + VFSOP_DUPLEX, }; struct ufs_request { enum vfs_op op; size_t len; // how much was put in *buf size_t capacity; // how much output can be accepted by the caller - void __user *id; // file id (returned by the open handler, passed to other calls) + void __user *id, *id2; // file id (returned by the open handler, passed to other calls) long offset; int flags; }; |