diff options
author | dzwdz | 2024-08-17 17:10:04 +0200 |
---|---|---|
committer | dzwdz | 2024-08-17 17:10:04 +0200 |
commit | 2ea826b428246eb62be81630f441a4367a675968 (patch) | |
tree | f2bb2c528f3e496efed741c5ecdfd4f922547067 | |
parent | 3609248bab04e0d273bb58d2544034b3ed7c35e0 (diff) |
*: getxattr
-rw-r--r-- | man/getxattr.2 | 91 | ||||
-rw-r--r-- | src/bootstrap/tar.c | 70 | ||||
-rw-r--r-- | src/cmd/getxattr.c | 56 | ||||
-rw-r--r-- | src/kernel/malloc.c | 1 | ||||
-rw-r--r-- | src/kernel/malloc.h | 1 | ||||
-rw-r--r-- | src/kernel/syscalls.c | 47 | ||||
-rw-r--r-- | src/kernel/vfs/request.c | 8 | ||||
-rw-r--r-- | src/libc/syscall.c | 4 | ||||
-rw-r--r-- | src/libk/include/camellia/syscalls.h | 3 | ||||
-rw-r--r-- | src/libk/include/camellia/types.h | 4 | ||||
-rw-r--r-- | src/libk/include/limits.h | 3 |
11 files changed, 255 insertions, 33 deletions
diff --git a/man/getxattr.2 b/man/getxattr.2 new file mode 100644 index 0000000..d7a287e --- /dev/null +++ b/man/getxattr.2 @@ -0,0 +1,91 @@ +.Dd August 17, 2024 +.Dt GETXATTR 2 +.Os Camellia +.Sh NAME +.Nm getxattr +.Nd get file attribute +.Sh SYNOPSIS +.In camellia/syscalls.h +.Ft ssize_t +.Fo _sys_getxattr +.Fa "hid_t h" +.Fa "const char *name" +.Fa "void *buf" +.Fa "size_t len" +.Fa "int flags" +.Fc +.Sh DESCRIPTION +.Nm +reads the attribute of +.Fa h +named by +.Fa name , +into +.Fa buf . +.Fa flags +is reserved for future use, for now it has to be 0. +.Pp +The attributes can have arbitrary names of up to +.Dv XATTRNAME_MAX +characters, but some are explicitly defined: +.Bl -tag -width virt.index +.It Sy virt.index +The list of all xattrs in arbitrary order as concatenated NUL-terminated names, +including exactly one NUL at the end. +.It Sy virt.mode +The Unix file mode in octal, with or without the trailing 0. +.It Sy virt.owner +The Unix file owner, as either a decimal UID or an username that doesn't begin +with a digit. +.It Sy virt.group +See +.Sy virt.owner . +.El +.Sh RETURN VALUES +.Nm +returns the length of the entire attribute (which might be larger than len), +or a negative value on failure. +The most common errors are: +.Bl -tag -width EGENERIC +.It Er ENOSYS +The filesystem doesn't support xattrs. +.It Er ENOENT +This xattr doesn't exist. +.It Er EGENERIC +The filesystem probably doesn't support xattrs, +and it wasn't written very well. +Sorry. +.El +.\" .Sh SEE ALSO +.Sh NOTES +The maximum size of the xattr value might become limited in the future, +as in Linux. +.Sh RATIONALE +The original idea for storing metadata was something like +Alternate Data Streams, where you'd be able to open another +.Dq file +that stores the metadata for any handle. +It would either be unstructured +.Pq gross , +or you'd need to open a separate file for each attribute +.Pq slow . +It also seemed like it would be complex to implement. +.Pp +A generic key-value store thing for handles also happens to seem more useful. +I was thinking about making the API more similar to +.Xr read 2 +and +.Xr write 2 +with an offset argument, but that seems unnecessary. +I don't expect the xattr values to be particularly large, +and only allowing writes/reads of an entire xattr at once will simplify +future attempts to add wrapper filesystems that enforce Unix-style permissions. +This made me end up with a Linux-style interface +.Pq thus the name . +.Pp +Instead of a separate syscall for listing xattrs I just use +.Sy virt.index , +as that seems simpler and without any downside. +It also lets me define paging using +.Sy virt.index.1 +and so on, avoiding the issue Linux has with listxattrs. diff --git a/src/bootstrap/tar.c b/src/bootstrap/tar.c index 678f54f..61b5b03 100644 --- a/src/bootstrap/tar.c +++ b/src/bootstrap/tar.c @@ -1,12 +1,13 @@ #include "tar.h" +#include <camellia/compat.h> #include <camellia/flags.h> +#include <camellia/fs/dir.h> #include <camellia/fsutil.h> #include <camellia/syscalls.h> +#include <errno.h> #include <stdint.h> #include <stdlib.h> #include <string.h> -#include <camellia/compat.h> -#include <camellia/fs/dir.h> #define BUF_SIZE 64 @@ -27,29 +28,46 @@ void tar_driver(void *base) { void *ptr; while (!c0_fs_wait(buf, BUF_SIZE, &res)) { switch (res.op) { - case VFSOP_OPEN: - ptr = tar_open(buf, res.len, base, ~0); - c0_fs_respond(ptr, ptr ? 0 : -1, 0); - break; - - case VFSOP_READ: - tar_read(&res, base, ~0); - break; - - case VFSOP_GETSIZE: - if (tar_type(res.id) != '5') { - c0_fs_respond(NULL, tar_size(res.id), 0); - } else { - struct dirbuild db; - dir_start(&db, res.offset, NULL, 0); - tar_dirbuild(&db, res.id, base, ~0); - c0_fs_respond(NULL, dir_finish(&db), 0); - } - break; - - default: - c0_fs_respond(NULL, -1, 0); // unsupported - break; + case VFSOP_OPEN: + ptr = tar_open(buf, res.len, base, ~0); + c0_fs_respond(ptr, ptr ? 0 : -1, 0); + break; + + case VFSOP_READ: + tar_read(&res, base, ~0); + break; + + case VFSOP_GETSIZE: + if (tar_type(res.id) != '5') { + c0_fs_respond(NULL, tar_size(res.id), 0); + } else { + struct dirbuild db; + dir_start(&db, res.offset, NULL, 0); + tar_dirbuild(&db, res.id, base, ~0); + c0_fs_respond(NULL, dir_finish(&db), 0); + } + break; + + case VFSOP_GETXATTR: + if (strcmp("virt.index", buf) == 0) { + char res[] = "virt.mode\0virt.owner\0virt.group"; + c0_fs_respond(res, sizeof(res), 0); + } else if (strcmp("virt.mode", buf) == 0) { + c0_fs_respond(res.id + 100, 7, 0); + } else if (strcmp("virt.owner", buf) == 0) { + ssize_t ret = snprintf(buf, BUF_SIZE, "%d", oct_parse(res.id + 108, 7)); + c0_fs_respond(buf, ret, 0); + } else if (strcmp("virt.group", buf) == 0) { + ssize_t ret = snprintf(buf, BUF_SIZE, "%d", oct_parse(res.id + 116, 7)); + c0_fs_respond(buf, ret, 0); + } else { + c0_fs_respond(NULL, -ENOENT, 0); + } + break; + + default: + c0_fs_respond(NULL, -ENOSYS, 0); // unsupported + break; } } exit(0); @@ -60,7 +78,6 @@ static char tar_type(void *meta) { return *(char*)(meta + 156); } -#include <stdio.h> static void *tar_open(const char *path, int len, void *base, size_t base_len) { void *res; if (len <= 0) return NULL; @@ -78,7 +95,6 @@ static void *tar_open(const char *path, int len, void *base, size_t base_len) { res = tar_find(path, len, base, base_len); if (res && tar_type(res) == '1') { /* hard link */ - _klogf("hard link to %s", res+157); res = tar_find(res + 157, strnlen(res + 157, 100), base, base_len); } return res; diff --git a/src/cmd/getxattr.c b/src/cmd/getxattr.c new file mode 100644 index 0000000..3a3a4f9 --- /dev/null +++ b/src/cmd/getxattr.c @@ -0,0 +1,56 @@ +#include <camellia.h> +#include <camellia/syscalls.h> +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <string.h> + +ssize_t +get(hid_t h, const char *name, char *buf, size_t len) +{ + ssize_t ret = _sys_getxattr(h, name, buf, len, 0); + if (ret < 0) { + fprintf(stderr, "error when getting %s: %s\n", name, strerror(-ret)); + } else { + printf("%s: ", name); + + for (ssize_t i = 0; i < ret; i++) { + if (isprint(buf[i])) { + putchar(buf[i]); + } else { + printf("\\x%02x", (unsigned char)buf[i]); + } + } + printf("\n"); + } + return ret; +} + +int +main(int argc, char **argv) +{ + if (argc < 2) { + fprintf(stderr, "usage: getxattr file [xattrs...]\n"); + return 1; + } + hid_t h = camellia_open(argv[1], OPEN_READ); + char buf[512]; + if (h < 0) err(1, "open"); + + if (argc == 2) { + ssize_t ret = get(h, "virt.index", buf, sizeof(buf)); + if (ret < 0) return 1; + if (0 < ret && buf[ret-1] != '\0') { + fprintf(stderr, "index truncated\n"); + } else { + for (ssize_t i = 0; i < ret; i += strlen(buf+i) + 1) { + char buf2[512]; + get(h, buf+i, buf2, sizeof(buf2)); + } + } + } else { + for (int i = 2; i < argc; i++) { + get(h, argv[i], buf, sizeof(buf)); + } + } +} diff --git a/src/kernel/malloc.c b/src/kernel/malloc.c index 2fe4792..eb0d01f 100644 --- a/src/kernel/malloc.c +++ b/src/kernel/malloc.c @@ -28,6 +28,7 @@ static const char *tagnames[] = { "RootCache", /* TagRootCache */ "PageRefcount", /* TagPageRefcount */ "Intr", /* TagIntr */ + "XattrName", /* TagXattrName */ }; static_assert(sizeof(tagnames) == sizeof(const char *) * TagLast); diff --git a/src/kernel/malloc.h b/src/kernel/malloc.h index af033f5..2108dad 100644 --- a/src/kernel/malloc.h +++ b/src/kernel/malloc.h @@ -30,6 +30,7 @@ enum MallocTag { TagRootCache, TagPageRefcount, TagIntr, + TagXattrName, TagLast, }; diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c index 0730620..436000c 100644 --- a/src/kernel/syscalls.c +++ b/src/kernel/syscalls.c @@ -302,11 +302,13 @@ long _sys_fs_respond(hid_t hid, const void __user *buf, long ret, int flags) { if (!h || h->type != HANDLE_FS_REQ) SYSCALL_RETURN(-EBADF); VfsReq *req = h->req; if (req) { + // TODO this shouldn't be limited to 32 bits + long len = min(ret, capped_cast32(req->outlen)); if (ret > 0 && req->type == VFSOP_READ) { - /* vfsreq_finish can't copy this data, as it doesn't know where the - * buf argument came from */ - ret = min(ret, capped_cast32(req->outlen)); - ret = pcpy_bi(req->caller, req->out, proc_cur, buf, ret); + ret = pcpy_bi(req->caller, req->out, proc_cur, buf, len); + } else if (ret > 0 && req->type == VFSOP_GETXATTR) { + /* intentionally ignoring the return value */ + pcpy_bi(req->caller, req->out, proc_cur, buf, len); } vfsreq_finish(req, (void __user *)buf, ret, flags, proc_cur); } @@ -510,6 +512,42 @@ void _sys_intr_return(struct intr_data __user *intr, int flags) { regs_safecopy(&proc_cur->regs, &d.reg); } +ssize_t _sys_getxattr(hid_t hid, const char __user *name, void __user *buf, size_t len, int flags) { + Handle *h = hs_get(proc_cur->hs, hid); + if (!h) { + SYSCALL_RETURN(-EBADF); + } + if (h->type != HANDLE_FILE || flags != 0) { + SYSCALL_RETURN(-ENOSYS); + } + if (!h->readable) { + SYSCALL_RETURN(-EACCES); + } + char tmpbuf[XATTRNAME_MAX]; + // TODO test pcpy_from_str edge cases + size_t namelen = pcpy_from_str(proc_cur, tmpbuf, name, XATTRNAME_MAX); + if (namelen == 0) { + SYSCALL_RETURN(-EFAULT); + } + assert(namelen <= XATTRNAME_MAX); + assert(tmpbuf[namelen-1] == '\0'); + + char *namebuf = kmalloc(namelen, TagXattrName); + memcpy(namebuf, tmpbuf, namelen); + + vfsreq_dispatchcopy((VfsReq) { + .type = VFSOP_GETXATTR, + .backend = h->backend, + .id = h->file_id, + .caller = proc_cur, + .kin = namebuf, + .kinlen = namelen-1, + .out = buf, + .outlen = len, + }); + return 0; +} + long _sys_execbuf(void __user *ubuf, size_t len) { if (len == 0) SYSCALL_RETURN(0); static_assert(EXECBUF_MAX_LEN <= KMALLOC_MAX); @@ -568,6 +606,7 @@ long _syscall(long num, long a, long b, long c, long d, long e) { break; case _SYS_GETNULL: _sys_getnull(a); break; case _SYS_DUPLEX: _sys_duplex(a, b, c); break; case _SYS_INTR_RETURN: _sys_intr_return((userptr_t)a, b); + break; case _SYS_GETXATTR: _sys_getxattr(a, (userptr_t)b, (userptr_t)c, d, e); 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 9b60080..50fcc22 100644 --- a/src/kernel/vfs/request.c +++ b/src/kernel/vfs/request.c @@ -151,8 +151,12 @@ vfsback_useraccept(VfsReq *req) assert(!(req->kin && req->uin)); if (req->kin) { void __user *buf = handler->awaited_req.buf; - len = min(req->kinlen, handler->awaited_req.max_len); - pcpy_to(handler, buf, req->kin, len); + if (req->kinlen+1 <= handler->awaited_req.max_len) { + len = req->kinlen; + pcpy_to(handler, buf, req->kin, len+1); /* +1 for NUL */ + } else { + panic_unimplemented(); // TODO! fail + } } else if (req->uin) { void __user *buf = handler->awaited_req.buf; len = min(req->uinlen, handler->awaited_req.max_len); diff --git a/src/libc/syscall.c b/src/libc/syscall.c index 023a7bb..6577141 100644 --- a/src/libc/syscall.c +++ b/src/libc/syscall.c @@ -106,6 +106,10 @@ void _sys_intr_return(struct intr_data __user *intr, int flags) { return (void)_syscall(_SYS_INTR_RETURN, (long)intr, (long)flags, 0, 0, 0); } +ssize_t _sys_getxattr(hid_t h, const char __user *name, void __user *buf, size_t len, int flags) { + return (ssize_t)_syscall(_SYS_GETXATTR, (long)h, (long)name, (long)buf, (long)len, (long)flags); +} + 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/syscalls.h b/src/libk/include/camellia/syscalls.h index 628a511..b8dd52e 100644 --- a/src/libk/include/camellia/syscalls.h +++ b/src/libk/include/camellia/syscalls.h @@ -25,6 +25,7 @@ #define _SYS_GETNULL 24 #define _SYS_DUPLEX 25 #define _SYS_INTR_RETURN 26 +#define _SYS_GETXATTR 27 #define _SYS_EXECBUF 100 #define _SYS_DEBUG_KLOG 101 @@ -95,6 +96,8 @@ long _sys_duplex(hid_t from, hid_t to, int flags); void _sys_intr_return(struct intr_data __user *intr, int flags); +ssize_t _sys_getxattr(hid_t h, const char __user *name, void __user *buf, size_t len, 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 0790db3..6864d4c 100644 --- a/src/libk/include/camellia/types.h +++ b/src/libk/include/camellia/types.h @@ -13,6 +13,9 @@ typedef void __user * userptr_t; typedef int hid_t; +// TODO custom stdint +typedef long long ssize_t; + enum vfs_op { VFSOP_OPEN, VFSOP_READ, @@ -21,6 +24,7 @@ enum vfs_op { VFSOP_REMOVE, VFSOP_CLOSE, VFSOP_DUPLEX, + VFSOP_GETXATTR, }; struct ufs_request { diff --git a/src/libk/include/limits.h b/src/libk/include/limits.h index f0f028c..5ea0a71 100644 --- a/src/libk/include/limits.h +++ b/src/libk/include/limits.h @@ -4,3 +4,6 @@ #define PATH_MAX 512 #define _POSIX2_RE_DUP_MAX 255 #define INTR_MAX 64 + +/* Maximum length of xattr name, including the terminating NUL. */ +#define XATTRNAME_MAX 256 |