mirror of
https://github.com/ish-app/ish.git
synced 2026-01-25 14:06:40 +00:00
219 lines
6.1 KiB
C
219 lines
6.1 KiB
C
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include "kernel/calls.h"
|
|
#include "kernel/fs.h"
|
|
#include "fs/proc.h"
|
|
#include "fs/path.h"
|
|
|
|
static int proc_lookup(const char *path, struct proc_entry *entry) {
|
|
entry->meta = &proc_root;
|
|
char component[MAX_NAME + 1];
|
|
int err = 0;
|
|
while (path_next_component(&path, component, &err)) {
|
|
if (!S_ISDIR(proc_entry_mode(entry)))
|
|
return _ENOTDIR;
|
|
|
|
unsigned long index = 0;
|
|
struct proc_entry next_entry;
|
|
char entry_name[MAX_NAME];
|
|
while (proc_dir_read(entry, &index, &next_entry)) {
|
|
// tack on some dynamically generated attributes
|
|
if (next_entry.meta->parent == NULL)
|
|
next_entry.meta->parent = entry->meta;
|
|
else
|
|
// this asserts that an entry has a unique parent
|
|
assert(next_entry.meta->parent == entry->meta);
|
|
if (next_entry.meta->inode == 0)
|
|
next_entry.meta->inode = index + 1;
|
|
|
|
proc_entry_getname(&next_entry, entry_name);
|
|
if (strcmp(entry_name, component) == 0)
|
|
goto found;
|
|
}
|
|
return _ENOENT;
|
|
found:
|
|
*entry = next_entry;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
extern const struct fd_ops procfs_fdops;
|
|
|
|
static struct fd *proc_open(struct mount *UNUSED(mount), const char *path, int UNUSED(flags), int UNUSED(mode)) {
|
|
struct proc_entry entry;
|
|
int err = proc_lookup(path, &entry);
|
|
if (err < 0)
|
|
return ERR_PTR(err);
|
|
struct fd *fd = fd_create(&procfs_fdops);
|
|
fd->proc.entry = entry;
|
|
fd->proc.data.data = NULL;
|
|
return fd;
|
|
}
|
|
|
|
static int proc_getpath(struct fd *fd, char *buf) {
|
|
char *p = buf + MAX_PATH - 1;
|
|
size_t n = 0;
|
|
p[0] = '\0';
|
|
struct proc_entry entry = fd->proc.entry;
|
|
while (entry.meta != &proc_root) {
|
|
char component[MAX_NAME];
|
|
proc_entry_getname(&entry, component);
|
|
size_t component_len = strlen(component) + 1; // plus one for the slash
|
|
p -= component_len;
|
|
n += component_len;
|
|
*p = '/';
|
|
memcpy(p + 1, component, component_len);
|
|
entry.meta = entry.meta->parent;
|
|
}
|
|
memmove(buf, p, n + 1); // plus one for the null
|
|
return 0;
|
|
}
|
|
|
|
static int proc_stat(struct mount *UNUSED(mount), const char *path, struct statbuf *stat) {
|
|
struct proc_entry entry;
|
|
int err = proc_lookup(path, &entry);
|
|
if (err < 0)
|
|
return err;
|
|
return proc_entry_stat(&entry, stat);
|
|
}
|
|
|
|
static int proc_fstat(struct fd *fd, struct statbuf *stat) {
|
|
return proc_entry_stat(&fd->proc.entry, stat);
|
|
}
|
|
|
|
static int proc_refresh_data(struct fd *fd) {
|
|
mode_t_ mode = proc_entry_mode(&fd->proc.entry);
|
|
if (S_ISDIR(mode))
|
|
return _EISDIR;
|
|
assert(S_ISREG(mode));
|
|
|
|
if (fd->proc.data.data == NULL) {
|
|
fd->proc.data.capacity = 4096;
|
|
fd->proc.data.data = malloc(fd->proc.data.capacity); // default size
|
|
}
|
|
fd->proc.data.size = 0;
|
|
struct proc_entry entry = fd->proc.entry;
|
|
int err = entry.meta->show(&entry, &fd->proc.data);
|
|
if (err < 0)
|
|
return err;
|
|
return 0;
|
|
}
|
|
|
|
static off_t_ proc_seek(struct fd *fd, off_t_ off, int whence) {
|
|
int err = proc_refresh_data(fd);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
off_t_ old_off = fd->offset;
|
|
if (whence == LSEEK_SET)
|
|
fd->offset = off;
|
|
else if (whence == LSEEK_CUR)
|
|
fd->offset += off;
|
|
else if (whence == LSEEK_END)
|
|
fd->offset = fd->proc.data.size + off;
|
|
else
|
|
return _EINVAL;
|
|
|
|
if (fd->offset < 0) {
|
|
fd->offset = old_off;
|
|
return _EINVAL;
|
|
}
|
|
return fd->offset;
|
|
}
|
|
|
|
static ssize_t proc_pread(struct fd *fd, void *buf, size_t bufsize, off_t off) {
|
|
int err = proc_refresh_data(fd);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
const char *data = fd->proc.data.data;
|
|
assert(data != NULL);
|
|
|
|
size_t remaining = fd->proc.data.size - off;
|
|
if ((size_t) off > fd->proc.data.size)
|
|
remaining = 0;
|
|
size_t n = bufsize;
|
|
if (n > remaining)
|
|
n = remaining;
|
|
|
|
memcpy(buf, data + off, n);
|
|
return n;
|
|
}
|
|
|
|
static int proc_readdir(struct fd *fd, struct dir_entry *entry) {
|
|
struct proc_entry proc_entry;
|
|
bool any_left = proc_dir_read(&fd->proc.entry, &fd->offset, &proc_entry);
|
|
if (!any_left)
|
|
return 0;
|
|
proc_entry_getname(&proc_entry, entry->name);
|
|
entry->inode = 0;
|
|
return 1;
|
|
}
|
|
|
|
static int proc_close(struct fd *fd) {
|
|
if (fd->proc.data.data != NULL)
|
|
free(fd->proc.data.data);
|
|
return 0;
|
|
}
|
|
|
|
const struct fd_ops procfs_fdops = {
|
|
.pread = proc_pread,
|
|
.lseek = proc_seek,
|
|
.readdir = proc_readdir,
|
|
.close = proc_close,
|
|
};
|
|
|
|
static ssize_t proc_readlink(struct mount *UNUSED(mount), const char *path, char *buf, size_t bufsize) {
|
|
struct proc_entry entry;
|
|
int err = proc_lookup(path, &entry);
|
|
if (err < 0)
|
|
return err;
|
|
if (!S_ISLNK(proc_entry_mode(&entry)))
|
|
return _EINVAL;
|
|
|
|
char target[MAX_PATH + 1];
|
|
err = entry.meta->readlink(&entry, target);
|
|
if (err < 0)
|
|
return err;
|
|
if (bufsize > strlen(target))
|
|
bufsize = strlen(target);
|
|
memcpy(buf, target, bufsize);
|
|
return bufsize;
|
|
}
|
|
|
|
void proc_buf_write(struct proc_data *buf, const void *data, size_t size) {
|
|
if (buf->size + size > buf->capacity) {
|
|
size_t new_capacity = buf->capacity;
|
|
while (buf->size + size > new_capacity)
|
|
new_capacity *= 2;
|
|
char *new_data = realloc(buf->data, new_capacity);
|
|
if (new_data == NULL) {
|
|
// just give up, error reporting is a pain
|
|
return;
|
|
}
|
|
buf->data = new_data;
|
|
buf->capacity = new_capacity;
|
|
}
|
|
assert(buf->size + size <= buf->capacity);
|
|
memcpy(&buf->data[buf->size], data, size);
|
|
buf->size += size;
|
|
}
|
|
|
|
void proc_printf(struct proc_data *buf, const char *format, ...) {
|
|
char data[4096];
|
|
va_list args;
|
|
va_start(args, format);
|
|
size_t size = vsnprintf(data, sizeof(data), format, args);
|
|
va_end(args);
|
|
proc_buf_write(buf, data, size);
|
|
}
|
|
|
|
const struct fs_ops procfs = {
|
|
.name = "proc", .magic = 0x9fa0,
|
|
.open = proc_open,
|
|
.getpath = proc_getpath,
|
|
.stat = proc_stat,
|
|
.fstat = proc_fstat,
|
|
.readlink = proc_readlink,
|
|
};
|