diff options
author | dzwdz | 2023-08-14 18:51:07 +0200 |
---|---|---|
committer | dzwdz | 2023-08-14 18:51:07 +0200 |
commit | 642b5fb0007b64c77d186fcb018d571152ee1d47 (patch) | |
tree | 1c466461f3602d306be309a053edae558ef2568e /src/cmd/ext2fs | |
parent | 8050069c57b729c18c19b1a03ab6e4bf63b4735e (diff) |
reorganization: first steps
Diffstat (limited to 'src/cmd/ext2fs')
m--------- | src/cmd/ext2fs/ext2 | 0 | ||||
-rw-r--r-- | src/cmd/ext2fs/main.c | 243 |
2 files changed, 243 insertions, 0 deletions
diff --git a/src/cmd/ext2fs/ext2 b/src/cmd/ext2fs/ext2 new file mode 160000 +Subproject 8db7b4fbb2429b504d7c711dae89d917de16bed diff --git a/src/cmd/ext2fs/main.c b/src/cmd/ext2fs/main.c new file mode 100644 index 0000000..12ef3bc --- /dev/null +++ b/src/cmd/ext2fs/main.c @@ -0,0 +1,243 @@ +#include "ext2/ex_cache.h" +#include "ext2/ext2.h" +#include <assert.h> +#include <camellia/flags.h> +#include <camellia/fs/dir.h> +#include <camellia/fs/misc.h> +#include <camellia/fsutil.h> +#include <camellia/syscalls.h> +#include <err.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +struct handle { + uint32_t n; + bool dir; +}; + +static int my_read(void *fp, void *buf, size_t len, size_t off); +static int my_write(void *fp, const void *buf, size_t len, size_t off); +static void do_open(struct ext2 *fs, hid_t reqh, struct ufs_request *req, char *buf); +static void do_read(struct ext2 *fs, hid_t reqh, struct ufs_request *req, char *buf, size_t buflen); +static void do_write(struct ext2 *fs, hid_t reqh, struct ufs_request *req, char *buf); +static void do_getsize(struct ext2 *fs, hid_t reqh, struct ufs_request *req); + +static int +my_read(void *fp, void *buf, size_t len, size_t off) +{ + if (fseek(fp, off, SEEK_SET) < 0) { + return -1; + } else if (fread(buf, len, 1, fp) == 1) { + return 0; + } else { + return -1; + } +} + +static int +my_write(void *fp, const void *buf, size_t len, size_t off) +{ + if (fseek(fp, off, SEEK_SET) < 0) { + return -1; + } else if (fwrite(buf, len, 1, fp) == 1) { + return 0; + } else { + return -1; + } +} + +static void +do_open(struct ext2 *fs, hid_t reqh, struct ufs_request *req, char *buf) +{ + bool is_dir = req->len == 0 || buf[req->len-1] == '/'; + uint32_t n = ext2c_walk(fs, buf, req->len); + if (n == 0) { + if (is_dir) { + _sys_fs_respond(reqh, NULL, -ENOSYS, 0); + return; + } + /* buf[0] == '/', strrchr != NULL */ + char *name = strrchr(buf, '/') + 1; + uint32_t dir_n = ext2c_walk(fs, buf, name - buf); + if (dir_n == 0) { + _sys_fs_respond(reqh, NULL, -ENOENT, 0); + return; + } + n = ext2_alloc_inode(fs, 0100700); + if (n == 0) { + _sys_fs_respond(reqh, NULL, -1, 0); + return; + } + if (ext2_link(fs, dir_n, name, n, 1) < 0) { + _sys_fs_respond(reqh, NULL, -1, 0); + return; + } + } else { + struct ext2d_inode *inode = ext2_req_inode(fs, n); + if (!inode) { + _sys_fs_respond(reqh, NULL, -ENOENT, 0); + return; + } + int type = (inode->perms >> 12) & 0xF; + ext2_dropreq(fs, inode, false); + + if ((type == 0x8 && is_dir) || (type == 0x4 && !is_dir)) { + _sys_fs_respond(reqh, NULL, -ENOENT, 0); + return; + } else if (type != 0x8 && type != 0x4) { + _sys_fs_respond(reqh, NULL, -ENOSYS, 0); + return; + } + } + + struct handle *h = malloc(sizeof *h); + if (!h) { + _sys_fs_respond(reqh, NULL, -1, 0); + return; + } + h->n = n; + h->dir = is_dir; + _sys_fs_respond(reqh, h, 0, 0); +} + +static void +do_read(struct ext2 *fs, hid_t reqh, struct ufs_request *req, char *buf, size_t buflen) +{ + struct handle *h = req->id; + if (!h->dir) { + struct ext2d_inode *inode = ext2_req_inode(fs, h->n); + if (!inode) goto err; + fs_normslice(&req->offset, &req->capacity, inode->size_lower, false); + ext2_dropreq(fs, inode, false); + + void *b = ext2_req_file(fs, h->n, &req->capacity, req->offset); + if (b) { + _sys_fs_respond(reqh, b, req->capacity, 0); + ext2_dropreq(fs, b, false); + } else if (req->capacity == 0) { + /* set by ext2_req_file on EOF */ + _sys_fs_respond(reqh, b, 0, 0); + } else goto err; + } else { + struct dirbuild db; + char namebuf[257]; + if (req->capacity > buflen) + req->capacity = buflen; + dir_start(&db, req->offset, buf, buflen); + for (struct ext2_diriter iter = {0}; ext2_diriter(&iter, fs, h->n); ) { + if (iter.ent->namelen_lower == 1 && iter.ent->name[0] == '.') { + continue; + } + if (iter.ent->namelen_lower == 2 + && iter.ent->name[0] == '.' + && iter.ent->name[1] == '.') + { + continue; + } + if (iter.ent->type == 2) { /* dir */ + memcpy(namebuf, iter.ent->name, iter.ent->namelen_lower); + namebuf[iter.ent->namelen_lower] = '/'; + dir_appendl(&db, namebuf, iter.ent->namelen_lower + 1); + } else { + dir_appendl(&db, iter.ent->name, iter.ent->namelen_lower); + } + } + _sys_fs_respond(reqh, buf, dir_finish(&db), 0); + } + return; +err: + _sys_fs_respond(reqh, NULL, -1, 0); +} + +static void +do_write(struct ext2 *fs, hid_t reqh, struct ufs_request *req, char *buf) +{ + struct handle *h = req->id; + if (h->dir) goto err; + + struct ext2d_inode *inode = ext2_req_inode(fs, h->n); + if (!inode) goto err; + fs_normslice(&req->offset, &req->len, inode->size_lower, true); + if ((req->flags & WRITE_TRUNCATE) || inode->size_lower < req->offset + req->len) { + inode->size_lower = req->offset + req->len; + if (ext2_dropreq(fs, inode, true) < 0) { + goto err; + } + } else { + ext2_dropreq(fs, inode, false); + } + inode = NULL; + + int ret = ext2_write(fs, h->n, buf, req->len, req->offset); + _sys_fs_respond(reqh, NULL, ret, 0); + return; +err: + _sys_fs_respond(reqh, NULL, -1, 0); +} + +static void +do_getsize(struct ext2 *fs, hid_t reqh, struct ufs_request *req) { + struct handle *h = req->id; + if (h->dir) goto err; + + struct ext2d_inode *inode = ext2_req_inode(fs, h->n); + if (!inode) goto err; + _sys_fs_respond(reqh, NULL, inode->size_lower, 0); + ext2_dropreq(fs, inode, false); + return; +err: + _sys_fs_respond(reqh, NULL, -1, 0); +} + +int +main(int argc, char **argv) +{ + intr_set(NULL); + + if (argc < 2) errx(1, "bad usage"); + // TODO pread/pwrite for normal handles + FILE *disk = fopen(argv[1], "r+"); + if (!disk) err(1, "couldn't open '%s'", argv[1]); + + struct e2device *dev = exc_init(my_read, my_write, (void*)disk); + if (!dev) errx(1, "exc_init failed"); + struct ext2 *fs = ext2_opendev(dev, exc_req, exc_drop); + if (!fs) errx(1, "ext2_opendev failed"); + + const size_t buflen = 4096; + char *buf = malloc(buflen); + struct ufs_request req; + for (;;) { + hid_t reqh = ufs_wait(buf, buflen, &req); + struct handle *h = req.id; + if (reqh < 0) break; + switch (req.op) { + case VFSOP_OPEN: + do_open(fs, reqh, &req, buf); + break; + case VFSOP_READ: + do_read(fs, reqh, &req, buf, buflen); + break; + case VFSOP_WRITE: + do_write(fs, reqh, &req, buf); + break; + case VFSOP_GETSIZE: + do_getsize(fs, reqh, &req); + break; + case VFSOP_CLOSE: + free(h); + _sys_fs_respond(reqh, NULL, -1, 0); + break; + default: + _sys_fs_respond(reqh, NULL, -1, 0); + break; + } + } + warnx("cleaning up"); + + return 1; +} |