shithub: ext4srv

Download patch

ref: 12351d11dff058fc674198e1242b029dc19fcd26
parent: 7c4ed936d9990c8fd790e655585a0ff4028b6ba8
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Sat Nov 7 06:56:09 EST 2020

read-only mode works now; still no group mapping or permission checking

--- a/README.md
+++ b/README.md
@@ -1,3 +1,10 @@
 # ext4srv
 
 Ext[2-4] file system for Plan 9. *WIP*
+
+## Usage
+
+	; mk install
+	; ext4srv
+	; mount /dev/ext4 /n/linux /n/sdN0/ext4partition
+	; ls -l /n/linux
--- a/common.h
+++ b/common.h
@@ -1,33 +1,34 @@
 #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 Opts {
+	int cachewb;
 };
 
-struct Groups {
-	char *raw;
-	Group *g;
-	int ng;
-};
+struct Part {
+	Ref;
+	QLock;
+	Part *prev, *next;
 
-struct Opts {
-	int cachewb;
+	char dev[32];
+	char mnt[32];
+
+	struct ext4_blockdev bdev;
+	struct ext4_blockdev_iface bdif;
+	Qid qid;
+	Qid qidmask;
+	Groups groups;
+	int f;
+	uchar blkbuf[];
 };
 
 Part *openpart(char *dev, Opts *opts);
 void closepart(Part *p);
+void closeallparts(void);
 
 char *errno2s(int err);
-int loadgroups(Groups *gs, char *raw);
-void freegroups(Groups *gs);
--- a/ext4srv.c
+++ b/ext4srv.c
@@ -1,7 +1,9 @@
 #include <ext4.h>
+#include <ext4_inode.h>
 #include <fcall.h>
 #include <thread.h>
 #include <9p.h>
+#include "group.h"
 #include "common.h"
 
 int mainstacksize = 65536;
@@ -9,7 +11,8 @@
 typedef struct Aux Aux;
 
 struct Aux {
-	Part *part;
+	Part *p;
+	char *path;
 	union {
 		ext4_file *file;
 		ext4_dir *dir;
@@ -18,65 +21,346 @@
 };
 
 enum {
-	Apart,
-	Afile,
 	Adir,
+	Afile,
 };
 
+static Opts opts = {
+	.cachewb = 0,
+};
+
 static void
 rattach(Req *r)
 {
-}
+	Aux *a;
+	static char err[ERRMAX];
 
-static void
-rauth(Req *r)
-{
+	if((a = calloc(1, sizeof(*a))) == nil)
+		respond(r, "memory");
+	else if((a->p = openpart(r->ifcall.aname, &opts)) == nil){
+		free(a);
+		rerrstr(err, sizeof(err));
+		respond(r, err);
+	}else{
+		incref(a->p);
+		a->type = Adir;
+		a->path = strdup("");
+		r->ofcall.qid = a->p->qidmask;
+		r->fid->qid = a->p->qidmask;
+		r->fid->aux = a;
+		respond(r, nil);
+	}
 }
 
 static void
 ropen(Req *r)
 {
+	Aux *a;
+	char *path;
+	int res;
+
+	a = r->fid->aux;
+	switch(a->type){
+	case Adir:
+		if(r->ifcall.mode != OREAD){
+			respond(r, "permission denied");
+			return;
+		}
+		if(a->dir != nil){
+			respond(r, "double open");
+			return;
+		}
+		if((a->dir = malloc(sizeof(*a->dir))) == nil)
+			goto Nomem;
+		if((path = smprint("%M/%s", a->p, a->path)) == nil){
+			free(a->dir);
+			a->dir = nil;
+			goto Nomem;
+		}
+		res = ext4_dir_open(a->dir, path);
+		free(path);
+		if(res != 0){
+			free(a->dir);
+			a->dir = nil;
+			respond(r, errno2s(res));
+			return;
+		}
+		break;
+
+	case Afile:
+		if(r->ifcall.mode != OREAD){
+			respond(r, "permission denied");
+			return;
+		}
+		if(a->file != nil){
+			respond(r, "double open");
+			return;
+		}
+		if((a->file = malloc(sizeof(*a->file))) == nil)
+			goto Nomem;
+		if((path = smprint("%M/%s", a->p, a->path)) == nil){
+			free(a->file);
+			a->file = nil;
+			goto Nomem;
+		}
+		res = ext4_fopen2(a->file, path, O_RDONLY);
+		free(path);
+		if(res != 0){
+			free(a->file);
+			a->file = nil;
+			respond(r, errno2s(res));
+			return;
+		}
+		break;
+
+Nomem:
+		respond(r, "memory");
+		return;
+	}
+
+	respond(r, nil);
 }
 
 static void
 rcreate(Req *r)
 {
+	respond(r, "nope");
 }
 
+static char *
+linkresolve(Aux *a, char *s)
+{
+	char *q;
+	char buf[4096+1];
+	ulong sz;
+
+	if(ext4_readlink(s, buf, sizeof(buf)-1, &sz) == 0){
+		buf[sz] = 0;
+		cleanname(buf);
+		if(buf[0] == '/'){
+			free(s);
+			s = smprint("%M%s", a->p, buf);
+		}else{
+			q = strrchr(s, '/');
+			*q = 0;
+			q = s;
+			s = smprint("%s/%s", q, buf);
+			cleanname(s);
+			free(q);
+		}
+	}
+
+	return s;
+}
+
+static int
+dirfill(Dir *dir, Aux *a, char *path)
+{
+	struct ext4_inode inode;
+	Group *g;
+	char *s, *q;
+	int r, i;
+	u32int uid, gid, t, ino;
+	struct ext4_sblock *sb;
+
+	memset(dir, 0, sizeof(*dir));
+	s = smprint("%M/", a->p);
+	r = ext4_get_sblock(s, &sb);
+	free(s);
+	if(r != 0){
+		fprint(2, "sblock: %s\n", errno2s(r));
+		return -1;
+	}
+
+	if(path == nil){
+		path = a->path;
+		s = smprint("%M/%s", a->p, a->path);
+	}else{
+		if(*a->path == 0 && *path == 0)
+			path = "/";
+		s = smprint("%M%s%s/%s", a->p, *a->path ? "/" : "", a->path, path);
+	}
+	s = linkresolve(a, s);
+	if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0)
+		fprint(2, "inode: %s: %s\n", s, errno2s(r));
+
+	dir->mode = ext4_inode_get_mode(sb, &inode);
+	dir->qid.path = a->p->qid.path | ino;
+	dir->qid.vers = ext4_inode_get_generation(&inode);
+	t = ext4_inode_type(sb, &inode);
+	if(t & EXT4_INODE_MODE_DIRECTORY){
+		dir->qid.type |= QTDIR;
+		dir->mode |= DMDIR;
+	}else
+		dir->length = ext4_inode_get_size(sb, &inode);
+	if(ext4_inode_get_flags(&inode) & EXT4_INODE_FLAG_APPEND){
+		dir->qid.type |= QTAPPEND;
+		dir->mode |= DMAPPEND;
+	}
+
+	if((q = strrchr(path, '/')) != nil)
+		path = q+1;
+	dir->name = estrdup9p(path);
+	dir->atime = ext4_inode_get_access_time(&inode);
+	dir->mtime = ext4_inode_get_modif_time(&inode);
+
+	uid = ext4_inode_get_uid(&inode);
+	gid = ext4_inode_get_gid(&inode);
+	for(i = 0, g = a->p->groups.g; i < a->p->groups.ng && (dir->uid == nil || dir->gid == nil); i++, g++){
+		if(g->id == uid)
+			dir->uid = estrdup9p(g->name);
+		if(g->id == gid)
+			dir->gid = estrdup9p(g->name);
+	}
+
+	free(s);
+
+	return 0;
+}
+
+static int
+dirgen(int n, Dir *dir, void *aux)
+{
+	const ext4_direntry *e;
+	Aux *a;
+
+	a = aux;
+	if(n == 0)
+		ext4_dir_entry_rewind(a->dir);
+	do{
+		if((e = ext4_dir_entry_next(a->dir)) == nil)
+			return -1;
+	}while(strcmp((char*)e->name, ".") == 0 || strcmp((char*)e->name, "..") == 0);
+
+	return dirfill(dir, a, (char*)e->name);
+}
+
 static void
 rread(Req *r)
 {
+	Aux *a;
+	ulong n;
+
+	a = r->fid->aux;
+	if(a->type == Adir && a->dir != nil){
+		dirread9p(r, dirgen, a);
+		respond(r, nil);
+		return;
+	}else if(a->type == Afile && a->file != nil){
+		ext4_fseek(a->file, r->ifcall.offset, 0);
+		if(ext4_fread(a->file, r->ofcall.data, r->ifcall.count, &n) != 0){
+			respond(r, "i/o error");
+		}else{
+			r->ofcall.count = n;
+			respond(r, nil);
+		}
+		return;
+	}
+
+	respond(r, "eh?");
 }
 
 static void
 rwrite(Req *r)
 {
+	respond(r, "nope");
 }
 
 static void
 rremove(Req *r)
 {
+	respond(r, "nope");
 }
 
 static void
 rstat(Req *r)
 {
+	Aux *a;
+
+	a = r->fid->aux;
+	dirfill(&r->d, a, nil);
+
+	respond(r, nil);
 }
 
 static void
 rwstat(Req *r)
 {
+	respond(r, "nope");
 }
 
 static char *
 rwalk1(Fid *fid, char *name, Qid *qid)
 {
+	Aux *a;
+	char *s, *q;
+	u32int ino, t;
+	struct ext4_inode inode;
+	int r;
+	struct ext4_sblock *sb;
+
+	a = fid->aux;
+
+	s = smprint("%M/", a->p);
+	if((r = ext4_get_sblock(s, &sb)) != 0)
+		goto error;
+	free(s);
+
+	s = smprint("%M/%s", a->p, a->path);
+	s = linkresolve(a, s);
+
+	q = s;
+	s = smprint("%s/%s", q, name);
+	cleanname(s);
+	free(q);
+	s = linkresolve(a, s);
+	if((r = ext4_raw_inode_fill(s, &ino, &inode)) != 0)
+		goto error;
+	qid->type = 0;
+	qid->path = a->p->qid.path | ino;
+	qid->vers = ext4_inode_get_generation(&inode);
+	t = ext4_inode_type(sb, &inode);
+	if(t & EXT4_INODE_MODE_DIRECTORY){
+		qid->type |= QTDIR;
+		a->type = Adir;
+	}else
+		a->type = Afile;
+	if(ext4_inode_get_flags(&inode) & EXT4_INODE_FLAG_APPEND)
+		qid->type |= QTAPPEND;
+	free(a->path);
+	a->path = strdup(strchr(s+1, '/')+1);
+	free(s);
+	fid->qid = *qid;
+
 	return nil;
+error:
+	free(s);
+	return errno2s(r);
 }
 
 static char *
 rclone(Fid *oldfid, Fid *newfid)
 {
+	Aux *a, *c;
+
+	a = oldfid->aux;
+	switch(a->type){
+	case Afile:
+	case Adir:
+		if((c = calloc(1, sizeof(*c))) == nil)
+			return "memory";
+		memmove(c, a, sizeof(*c));
+		c->path = strdup(a->path);
+		c->file = nil;
+		c->dir = nil;
+		break;
+
+	default:
+		return "unknown aux type";
+	}
+	incref(c->p);
+	newfid->aux = c;
+
 	return nil;
 }
 
@@ -83,16 +367,56 @@
 static void
 rdestroyfid(Fid *fid)
 {
+	Aux *a;
+
+	a = fid->aux;
+	if(a == nil)
+		return;
+	fid->aux = nil;
+
+	if(a->type == Adir){
+		if(a->dir != nil){
+			ext4_dir_close(a->dir);
+			free(a->dir);
+		}
+	}else if(a->type == Afile){
+		if(a->file != nil){
+			ext4_fclose(a->file);
+			free(a->file);
+		}
+	}else{
+		/* that would be a BUG */
+		return;
+	}
+
+	if(decref(a->p) == 0)
+		closepart(a->p);
+	free(a->path);
+	free(a);
 }
 
+static int
+note(void *, char *s)
+{
+	if(strncmp(s, "sys:", 4) != 0){
+		closeallparts();
+		return 1;
+	}
+
+	return 0;
+}
+
 static void
-rstart(Srv *s)
+rstart(Srv *)
 {
+	threadnotify(note, 1);
 }
 
 static void
-rend(Srv *s)
+rend(Srv *)
 {
+	closeallparts();
+	threadexitsall(nil);
 }
 
 static Srv fs = {
@@ -111,23 +435,33 @@
 	.end = rend,
 };
 
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-C] [-s srvname]\n", argv0);
+	threadexitsall("usage");
+}
+
 void
 threadmain(int argc, char **argv)
 {
-	Part *p;
-	Opts opts = {
-		.cachewb = 0,
-	};
+	char *srv;
 
+	srv = "ext4";
 	ARGBEGIN{
+	case 'D':
+		chatty9p++;
+		break;
 	case 'C':
 		opts.cachewb = 1;
 		break;
+	case 's':
+		srv = EARGF(usage());
 	}ARGEND
 
-	if((p = openpart(argv[0], &opts)) == nil)
-		sysfatal("%r");
-	closepart(p);
+	if(argc != 0)
+		usage();
 
-	threadexitsall(nil);
+	threadpostmountsrv(&fs, srv, nil, 0);
+	threadexits(nil);
 }
--- a/group.c
+++ b/group.c
@@ -1,6 +1,6 @@
 #include <u.h>
 #include <libc.h>
-#include "common.h"
+#include "group.h"
 
 int
 loadgroups(Groups *gs, char *raw)
--- /dev/null
+++ b/group.h
@@ -1,0 +1,18 @@
+typedef struct Group Group;
+typedef struct Groups Groups;
+
+struct Group {
+	u32int id;
+	char *name;
+	char **memb;
+	int nmemb;
+};
+
+struct Groups {
+	char *raw;
+	Group *g;
+	int ng;
+};
+
+int loadgroups(Groups *gs, char *raw);
+void freegroups(Groups *gs);
--- a/part.c
+++ b/part.c
@@ -1,31 +1,16 @@
 #include <ext4.h>
-#include <fcall.h>
 #include <thread.h>
-#include <9p.h>
+#include "group.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;
+	u32int id;
 }sv;
 
 static long
@@ -329,9 +314,10 @@
 		if(sv.ps != nil)
 			sv.ps->prev = p;
 		sv.ps = p;
+		p->qidmask.path = ((uvlong)sv.id++) << 32;
+		p->qidmask.type = QTDIR;
 	}else{
 		close(f);
-		p->refcnt++;
 	}
 
 	free(d);
@@ -341,7 +327,9 @@
 	return p;
 
 error:
-	close(f);
+	werrstr("openpart: %r");
+	if(f >= 0)
+		close(f);
 	free(d);
 	free(p);
 	free(s);
@@ -356,23 +344,28 @@
 	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);
-	}
+	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);
+}
+
+void
+closeallparts(void)
+{
+	while(sv.ps != nil)
+		closepart(sv.ps);
 }