shithub: ext4srv

Download patch

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);
+}