summaryrefslogtreecommitdiff
path: root/src/kernel/vfs/procfs.c
blob: e434c45e037cc3b1c703c4ca484752d3083c4a9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <camellia/errno.h>
#include <kernel/mem/virt.h>
#include <kernel/panic.h>
#include <kernel/proc.h>
#include <kernel/vfs/procfs.h>
#include <kernel/vfs/request.h>
#include <shared/mem.h>

static uint32_t openpath(const char *path, size_t len, struct process *root);
static struct process *findgid(uint32_t gid, struct process *root);
static void procfs_accept(struct vfs_request *req);
static void procfs_cleanup(struct vfs_backend *be);

static uint32_t
openpath(const char *path, size_t len, struct process *p)
{
	if (len == 0) return 0;
	path++, len--;

	while (len) {
		uint32_t cid = 0;
		for (; 0 < len && *path != '/'; path++, len--) {
			char c = *path;
			if (!('0' <= c && c <= '9')) {
				return 0;
			}
			cid = cid * 10 + *path - '0';
		}
		if (len == 0) {
			return 0;
		}
		assert(*path == '/');
		path++, len--;

		p = p->child;
		if (!p) return 0;
		while (p->cid != cid) {
			p = p->sibling;
			if (!p) return 0;
		}
	}
	return p->globalid;
}

static struct process *
findgid(uint32_t gid, struct process *root)
{
	for (struct process *p = root; p; p = process_next(p)) {
		if (p->globalid == gid) return p;
	}
	return NULL;
}

static void
procfs_accept(struct vfs_request *req)
{
	struct process *root = req->backend->kern.data;
	struct process *p;
	char buf[512];
	assert(root);
	if (req->type == VFSOP_OPEN) {
		int gid;
		assert(req->input.kern);
		gid = openpath(req->input.buf_kern, req->input.len, root);
		vfsreq_finish_short(req, gid == 0 ? -ENOENT : gid);
		return;
	}
	p = findgid((uintptr_t)req->id, root);
	if (!p) {
		vfsreq_finish_short(req, -EGENERIC);
		return;
	}

	if (req->type == VFSOP_READ) {
		// TODO port dirbuild to kernel
		int pos = 0;
		if (req->offset != 0) {
			vfsreq_finish_short(req, -ENOSYS);
			return;
		}
		for (struct process *iter = p->child; iter; iter = iter->sibling) {
			assert(pos < 512);
			// processes could possibly be identified by unique identifiers instead
			// e.g. an encrypted gid, or just a randomly generated one
			// con: would require bringing in a crypto library
			pos += snprintf(buf + pos, 512 - pos, "%d/", iter->cid) + 1;
			if (512 <= pos) {
				vfsreq_finish_short(req, -1);
			}
		}
		assert(0 <= pos && (size_t)pos <= sizeof buf);
		virt_cpy_to(req->caller->pages, req->output.buf, buf, pos);
		vfsreq_finish_short(req, pos);
	} else {
		vfsreq_finish_short(req, -ENOSYS);
	}
}

static void
procfs_cleanup(struct vfs_backend *be)
{
	struct process *p = be->kern.data;
	assert(p);
	p->refcount--;
}

struct vfs_backend *
procfs_backend(struct process *proc)
{
	struct vfs_backend *be = kzalloc(sizeof(struct vfs_backend));
	*be = (struct vfs_backend) {
		.is_user = false,
		.potential_handlers = 1,
		.refcount = 1,
		.kern.accept = procfs_accept,
		.kern.data = proc,
		.kern.cleanup = procfs_cleanup,
	};
	proc->refcount++;
	assert(proc->refcount); /* overflow */
	return be;
}