summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/getxattr.291
-rw-r--r--src/bootstrap/tar.c70
-rw-r--r--src/cmd/getxattr.c56
-rw-r--r--src/kernel/malloc.c1
-rw-r--r--src/kernel/malloc.h1
-rw-r--r--src/kernel/syscalls.c47
-rw-r--r--src/kernel/vfs/request.c8
-rw-r--r--src/libc/syscall.c4
-rw-r--r--src/libk/include/camellia/syscalls.h3
-rw-r--r--src/libk/include/camellia/types.h4
-rw-r--r--src/libk/include/limits.h3
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