ref: 7c4ed936d9990c8fd790e655585a0ff4028b6ba8
parent: aa0d4ea543fec82928fc1ff6f57eb681e1b50716
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Fri Nov 6 08:51:43 EST 2020
proper mounting/unmounting and group file reading
--- /dev/null
+++ b/common.h
@@ -1,0 +1,33 @@
+#pragma lib "../lwext4/src/liblwext4.a"
+
+typedef struct Group Group;
+typedef struct Groups Groups;
+typedef struct Opts Opts;
+typedef struct Part Part;
+#pragma incomplete Part
+#pragma varargck type "Ð" Part*
+#pragma varargck type "M" Part*
+
+struct Group {
+ u32int id;
+ char *name;
+ char **memb;
+ int nmemb;
+};
+
+struct Groups {
+ char *raw;
+ Group *g;
+ int ng;
+};
+
+struct Opts {
+ int cachewb;
+};
+
+Part *openpart(char *dev, Opts *opts);
+void closepart(Part *p);
+
+char *errno2s(int err);
+int loadgroups(Groups *gs, char *raw);
+void freegroups(Groups *gs);
--- /dev/null
+++ b/err.c
@@ -1,0 +1,31 @@
+#include <ext4.h>
+
+char *
+errno2s(int err)
+{
+ switch(err){
+ case EROFS:
+ case EPERM:
+ case EACCES: return "permission denied";
+ case ENOMEM: return "out of memory";
+ case ENOENT: return "file not found";
+ case EISDIR: return "is a directory";
+ case EIO: return "i/o error";
+ case ENODEV:
+ case ENXIO: return "no such device";
+ case E2BIG: return "argument list too long";
+ case EFAULT: return "bad address";
+ case EEXIST: return "file exists";
+ case ENOTDIR: return "not a directory";
+ case EINVAL: return "invalid argument";
+ case EFBIG: return "file too large";
+ case ENOSPC: return "no space left on device";
+ case EMLINK: return "too many links";
+ case ERANGE: return "math result not representable";
+ case ENOTEMPTY: return "directory not empty";
+ case ENODATA: return "no data available";
+ case ENOTSUP: return "not supported";
+ }
+
+ return "???";
+}
--- a/ext4srv.c
+++ b/ext4srv.c
@@ -2,171 +2,27 @@
#include <fcall.h>
#include <thread.h>
#include <9p.h>
+#include "common.h"
+int mainstacksize = 65536;
+
typedef struct Aux Aux;
-typedef struct Root Root;
struct Aux {
- int type;
- Aux *raux;
+ Part *part;
union {
ext4_file *file;
ext4_dir *dir;
- Root *r;
};
+ int type;
};
-struct Root {
- struct ext4_blockdev *dev;
- QLock lock;
- int id;
- int f;
-};
-
enum {
- Aroot,
+ Apart,
Afile,
Adir,
-
- Frdonly = 1<<0,
};
-#define BDEV2AUX(bdev) (Aux*)(bdev-1)
-
-static int
-bdopen(struct ext4_blockdev *bdev)
-{
- Aux *a;
-
- a = BDEV2AUX(bdev);
-
- return 0;
-}
-
-static int
-bdread(struct ext4_blockdev *bdev, void *buf, u64int blkid, u32int blkcnt)
-{
- Aux *a;
-
- a = BDEV2AUX(bdev);
-
- return 0;
-}
-
-static int
-bdwrite(struct ext4_blockdev *bdev, const void *buf, u64int blkid, u32int blkcnt)
-{
- Aux *a;
-
- a = BDEV2AUX(bdev);
-
- return 0;
-}
-
-static int
-bdnowrite(struct ext4_blockdev *bdev, const void *buf, u64int blkid, u32int blkcnt)
-{
- USED(bdev, buf, blkid, blkcnt);
-
- return -1;
-}
-
-static int
-bdclose(struct ext4_blockdev *bdev)
-{
- Aux *a;
-
- a = BDEV2AUX(bdev);
-
- return 0;
-}
-
-static int
-bdlock(struct ext4_blockdev *bdev)
-{
- Aux *a;
-
- a = BDEV2AUX(bdev);
- qlock(&a->root.b.lock);
-
- return 0;
-}
-
-static int
-bdunlock(struct ext4_blockdev *bdev)
-{
- Aux *a;
-
- a = BDEV2AUX(bdev);
- qunlock(&a->root.b.lock);
-
- return 0;
-}
-
-static Aux *
-newroot(char *dev, int flags)
-{
- static int newid;
- static QLock lock;
- int id;
- Aux *a;
- Root *r;
- int f;
-
- qlock(&lock);
- id = newid++;
- if(id < 0){
- werrstr("root id overflow");
- /* no unlock on purpose, get stuck forever */
- return nil;
- }
- qunlock(&lock);
-
- if((f = open(dev, ORDWR)) < 0)
- return nil;
-
- if((a = calloc(1, sizeof(*a)+sizeof(*r)) == nil){
- close(f);
- return nil;
- }
- a->type = Aroot;
- a->raux = a;
- a->r = r = (struct ext4_blockdev*)(a+1);
-
- r->open = bdopen;
- r->bread = bdread;
- r->bwrite = (flags & Frdonly) != 0 ? bdwrite : bdnowrite;
- r->close = bdclose;
- r->lock = bdlock;
- r->unlock = bdunlock;
- r->ph_bsize =
-
- return a;
-}
-
-static struct ext4_blockdev_iface iface0 = {
-
- /**@brief Block size (bytes): physical*/
- uint32_t ph_bsize;
-
- /**@brief Block count: physical*/
- uint64_t ph_bcnt;
-
- /**@brief Block size buffer: physical*/
- uint8_t *ph_bbuf;
-
- /**@brief Reference counter to block device interface*/
- uint32_t ph_refctr;
-
- /**@brief Physical read counter*/
- uint32_t bread_ctr;
-
- /**@brief Physical write counter*/
- uint32_t bwrite_ctr;
-
- /**@brief User data pointer*/
- void* p_user;
-
static void
rattach(Req *r)
{
@@ -258,8 +114,20 @@
void
threadmain(int argc, char **argv)
{
+ Part *p;
+ Opts opts = {
+ .cachewb = 0,
+ };
+
ARGBEGIN{
+ case 'C':
+ opts.cachewb = 1;
+ break;
}ARGEND
+
+ if((p = openpart(argv[0], &opts)) == nil)
+ sysfatal("%r");
+ closepart(p);
threadexitsall(nil);
}
--- /dev/null
+++ b/group.c
@@ -1,0 +1,71 @@
+#include <u.h>
+#include <libc.h>
+#include "common.h"
+
+int
+loadgroups(Groups *gs, char *raw)
+{
+ Group *g;
+ char *m, **memb, *s, *e, *a[5], *ide;
+ int line, n;
+ vlong id;
+
+ memset(gs, 0, sizeof(*gs));
+ if((gs->raw = strdup(raw)) == nil)
+ goto error;
+
+ line = 1;
+ for(s = gs->raw; *s; s = e+1, line++){
+ if((e = strchr(s, '\n')) != nil)
+ *e = 0;
+
+ if((n = getfields(s, a, nelem(a), 1, ":")) >= 3 && strlen(a[0]) > 0 && strlen(a[2]) > 0){
+ if((id = strtoll(a[2], &ide, 0)) < 0 || id > 0xffffffff || *ide != 0){
+ werrstr("invalid uid: %s", a[2]);
+ goto error;
+ }
+
+ if((g = realloc(gs->g, (gs->ng+1)*sizeof(*g))) == nil)
+ goto error;
+ gs->g = g;
+ g += gs->ng++;
+ g->id = id;
+ g->name = a[0];
+ g->memb = nil;
+ g->nmemb = 0;
+ for(m = a[3]; n > 3 && *m; *m++ = 0){
+ if((memb = realloc(g->memb, (g->nmemb+1)*sizeof(*g->memb))) == nil)
+ goto error;
+ g->memb = memb;
+ memb += g->nmemb++;
+ *memb = m;
+ if((m = strchr(m, ',')) == nil)
+ break;
+ }
+ }else{
+ werrstr("line %d: invalid record", line);
+ goto error;
+ }
+
+ if(e == nil)
+ break;
+ }
+
+ return 0;
+error:
+ werrstr("togroups: %r");
+ freegroups(gs);
+
+ return -1;
+}
+
+void
+freegroups(Groups *gs)
+{
+ int i;
+
+ for(i = 0; i < gs->ng; i++)
+ free(gs->g[i].memb);
+ free(gs->g);
+ free(gs->raw);
+}
--- a/mkfile
+++ b/mkfile
@@ -2,12 +2,16 @@
BIN=/$objtype/bin
TARG=ext4srv
-CFLAGS=$CFLAGS -p -I../lwext4/include/plan9 -I../lwext4/include
+CFLAGS=$CFLAGS -D__${objtype}__ -p -I../lwext4/include -I../lwext4/include/plan9
HFILES=\
+ common.h\
OFILES=\
+ err.$O\
ext4srv.$O\
+ group.$O\
+ part.$O\
default:V: all
--- /dev/null
+++ b/part.c
@@ -1,0 +1,378 @@
+#include <ext4.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "common.h"
+
+#define TRACE(fmt, ...) //fprint(2, fmt, __VA_ARGS__)
+
+struct Part {
+ QLock;
+ Part *prev, *next;
+ int refcnt;
+
+ char dev[32];
+ char mnt[32];
+
+ struct ext4_blockdev bdev;
+ struct ext4_blockdev_iface bdif;
+ Qid qid;
+ Groups groups;
+ int f;
+ uchar blkbuf[];
+};
+#define BDEV2PART(bdev) ((bdev)->bdif->p_user)
+
+static struct {
+ QLock;
+ Part *ps;
+}sv;
+
+static long
+preadn(int f, void *av, long n, vlong offset)
+{
+ char *a;
+ long m, t;
+
+ assert(offset >= 0);
+
+ a = av;
+ t = 0;
+ while(t < n){
+ m = pread(f, a+t, n-t, offset);
+ if(m <= 0){
+ if(t == 0)
+ return m;
+ break;
+ }
+ t += m;
+ offset += m;
+ }
+ return t;
+}
+
+static int
+bdopen(struct ext4_blockdev *bdev)
+{
+ Part *p;
+
+ p = BDEV2PART(bdev);
+ TRACE("bdopen %p\n", p);
+ USED(p);
+
+ return 0;
+}
+
+static int
+bdread(struct ext4_blockdev *bdev, void *buf, u64int blkid, u32int blkcnt)
+{
+ Part *p;
+
+ p = BDEV2PART(bdev);
+ TRACE("bdread %p %p %llud %ud\n", p, buf, blkid, blkcnt);
+ if(preadn(p->f, buf, blkcnt*p->bdif.ph_bsize, blkid*p->bdif.ph_bsize) != blkcnt*p->bdif.ph_bsize){
+ fprint(2, "bdread: %r\n");
+ return EIO;
+ }
+
+ return 0;
+}
+
+static int
+bdwrite(struct ext4_blockdev *bdev, const void *buf, u64int blkid, u32int blkcnt)
+{
+ Part *p;
+
+ p = BDEV2PART(bdev);
+ TRACE("bdwrite %p %p %llud %ud\n", p, buf, blkid, blkcnt);
+ if(pwrite(p->f, buf, blkcnt*p->bdif.ph_bsize, blkid*p->bdif.ph_bsize) != blkcnt*p->bdif.ph_bsize)
+ return EIO;
+
+ return 0;
+}
+
+static int
+bdclose(struct ext4_blockdev *bdev)
+{
+ Part *p;
+
+ p = BDEV2PART(bdev);
+ TRACE("bdclose %p\n", p);
+ USED(p);
+
+ return 0;
+}
+
+static int
+bdlock(struct ext4_blockdev *bdev)
+{
+ Part *p;
+
+ p = BDEV2PART(bdev);
+ TRACE("bdlock %p\n", p);
+ qlock(p);
+
+ return 0;
+}
+
+static int
+bdunlock(struct ext4_blockdev *bdev)
+{
+ Part *p;
+
+ p = BDEV2PART(bdev);
+ TRACE("bdunlock %p\n", p);
+ qunlock(p);
+
+ return 0;
+}
+
+static int
+getblksz(char *dev, u32int *blksz)
+{
+ char *s, *e, *g, *a[5];
+ vlong x;
+ int f, n, r;
+
+ /* default blksz if couldn't find out the real one */
+ *blksz = 512;
+
+ f = -1;
+ g = nil;
+ if((s = smprint("%s_ctl", dev)) == nil)
+ goto error;
+ cleanname(s);
+ if((e = strrchr(s, '/')) == nil)
+ e = s;
+ strcpy(e, "/ctl");
+ f = open(s, OREAD);
+ free(s);
+ if(f >= 0){
+ if((g = malloc(4096)) == nil)
+ goto error;
+ for(n = 0; (r = read(f, g+n, 4096-n-1)) > 0; n += r);
+ g[n] = 0;
+ close(f);
+ f = -1;
+
+ for(s = g; (e = strchr(s, '\n')) != nil; s = e+1){
+ *e = 0;
+ if(tokenize(s, a, nelem(a)) >= 3 && strcmp(a[0], "geometry") == 0){
+ if((x = strtoll(a[2], &e, 0)) > 0 && *e == 0)
+ *blksz = x;
+ if(*blksz != x){
+ werrstr("invalid block size: %s", a[2]);
+ goto error;
+ }
+ break;
+ }
+ }
+ }
+
+ close(f);
+ free(g);
+ return 0;
+error:
+ close(f);
+ free(g);
+ return -1;
+}
+
+static int
+fmtpart(Fmt *f)
+{
+ Part *p;
+
+ p = va_arg(f->args, Part*);
+
+ return fmtprint(f, f->r == 'M' ? "/%#llux" : "dev%#llux", p->qid.path);
+}
+
+static void *
+readfile(Part *p, char *path, uvlong *sz)
+{
+ char *s, *d;
+ ext4_file f;
+ uvlong n;
+ ulong got;
+ int r;
+
+ d = nil;
+ while(*path == '/')
+ path++;
+ s = smprint("%M/%s", p, path);
+ r = ext4_fopen2(&f, s, O_RDONLY);
+ free(s);
+
+ if(r == 0){
+ *sz = ext4_fsize(&f);
+ if((d = malloc(*sz+1)) == nil){
+ ext4_fclose(&f);
+ goto error;
+ }
+
+ for(n = 0; n < *sz; n += got){
+ if((r = ext4_fread(&f, d+n, *sz-n, &got)) != 0){
+ werrstr("readfile: %s", errno2s(r));
+ ext4_fclose(&f);
+ goto error;
+ }
+ if(got == 0)
+ break;
+ }
+
+ *sz = n;
+ ext4_fclose(&f);
+ }else{
+error:
+ free(d);
+ d = nil;
+ *sz = 0;
+ }
+
+ return d;
+}
+
+static int
+mountpart(Part *p, Opts *opts)
+{
+ int r;
+ char *gr;
+ uvlong sz;
+
+ if(snprint(p->dev, sizeof(p->dev), "%Ð", p) >= sizeof(p->dev)){
+ werrstr("part path too long");
+ goto error;
+ }
+ if(snprint(p->mnt, sizeof(p->mnt), "%M/", p) >= sizeof(p->mnt)){
+ werrstr("part path too long");
+ goto error;
+ }
+ if((r = ext4_device_register(&p->bdev, p->dev)) != 0){
+ werrstr("register: %s", errno2s(r));
+ goto error;
+ }
+ if((r = ext4_mount(p->dev, p->mnt, false)) != 0){
+ werrstr("mount: %s", errno2s(r));
+ goto error;
+ }
+ if((r = ext4_recover(p->mnt)) != 0 && r != ENOTSUP){
+ werrstr("recover: %s", errno2s(r));
+ goto error;
+ }
+ if((r = ext4_journal_start(p->mnt)) != 0 && r != ENOTSUP){
+ werrstr("journal: %s", errno2s(r));
+ goto error;
+ }
+ if(opts->cachewb)
+ ext4_cache_write_back(p->mnt, 1);
+
+ if((gr = readfile(p, "/etc/group", &sz)) != nil){
+ gr[sz] = 0;
+ r = loadgroups(&p->groups, gr);
+ free(gr);
+ if(r != 0)
+ goto error;
+ }
+
+ return 0;
+error:
+ werrstr("mountpart: %r");
+ return -1;
+}
+
+Part *
+openpart(char *dev, Opts *opts)
+{
+ Dir *d;
+ Part *p;
+ char *s;
+ int f;
+ u32int blksz;
+
+ d = nil;
+ p = nil;
+ s = nil;
+ qlock(&sv);
+
+ fmtinstall(L'Ð', fmtpart);
+ fmtinstall('M', fmtpart);
+
+ f = open(dev, ORDWR);
+ if(f < 0 || (d = dirfstat(f)) == nil)
+ goto error;
+ /* see if it's already opened */
+ for(p = sv.ps; p != nil && p->qid.path != d->qid.path; p = p->next);
+ if(p == nil){ /* no? then make one */
+ if(getblksz(dev, &blksz) != 0 || (p = calloc(1, sizeof(*p)+blksz)) == nil)
+ goto error;
+
+ p->f = f;
+ p->qid = d->qid;
+ p->bdev.bdif = &p->bdif;
+ p->bdev.part_size = d->length;
+ p->bdif.open = bdopen;
+ p->bdif.bread = bdread;
+ p->bdif.bwrite = bdwrite;
+ p->bdif.close = bdclose;
+ p->bdif.lock = bdlock;
+ p->bdif.unlock = bdunlock;
+ p->bdif.ph_bsize = blksz;
+ p->bdif.ph_bcnt = d->length/blksz;
+ p->bdif.ph_bbuf = p->blkbuf;
+ p->bdif.p_user = p;
+
+ if(mountpart(p, opts) != 0)
+ goto error;
+
+ p->next = sv.ps;
+ if(sv.ps != nil)
+ sv.ps->prev = p;
+ sv.ps = p;
+ }else{
+ close(f);
+ p->refcnt++;
+ }
+
+ free(d);
+ free(s);
+ qunlock(&sv);
+
+ return p;
+
+error:
+ close(f);
+ free(d);
+ free(p);
+ free(s);
+ qunlock(&sv);
+
+ return nil;
+}
+
+void
+closepart(Part *p)
+{
+ int r;
+
+ qlock(&sv);
+ if(--p->refcnt < 0){
+ ext4_cache_write_back(p->mnt, 0);
+ if((r = ext4_journal_stop(p->mnt)) != 0 && r != ENOTSUP)
+ fprint(2, "closepart: journal %s: %s\n", p->mnt, errno2s(r));
+ if((r = ext4_umount(p->mnt)) != 0 && r != ENOTSUP)
+ fprint(2, "closepart: umount %s: %s\n", p->mnt, errno2s(r));
+ if((r = ext4_device_unregister(p->dev)) != 0 && r != ENOTSUP)
+ fprint(2, "closepart: unregister %s: %s\n", p->dev, errno2s(r));
+ close(p->f);
+ if(p->prev != nil)
+ p->prev = p->next;
+ if(p->next != nil)
+ p->next->prev = p->prev;
+ if(p == sv.ps)
+ sv.ps = p->next;
+ freegroups(&p->groups);
+ free(p);
+ }
+ qunlock(&sv);
+}