shithub: ext4srv

ref: 5929766aa488af70c827fe65e6d0fb703bfa4548
dir: /ext4srv.c/

View raw version
#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;

typedef struct Aux Aux;

struct Aux {
	Part *p;
	char *path;
	union {
		ext4_file *file;
		ext4_dir *dir;
	};
	int type;
};

enum {
	Adir,
	Afile,
};

static Opts opts = {
	.cachewb = 0,
};

static void
rattach(Req *r)
{
	Aux *a;
	static char err[ERRMAX];

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

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 *)
{
	threadnotify(note, 1);
}

static void
rend(Srv *)
{
	closeallparts();
	threadexitsall(nil);
}

static Srv fs = {
	.attach = rattach,
	.open = ropen,
	.create = rcreate,
	.read = rread,
	.write = rwrite,
	.remove = rremove,
	.stat = rstat,
	.wstat = rwstat,
	.walk1 = rwalk1,
	.clone = rclone,
	.destroyfid = rdestroyfid,
	.start = rstart,
	.end = rend,
};

static void
usage(void)
{
	fprint(2, "usage: %s [-C] [-s srvname]\n", argv0);
	threadexitsall("usage");
}

void
threadmain(int argc, char **argv)
{
	char *srv;

	srv = "ext4";
	ARGBEGIN{
	case 'D':
		chatty9p++;
		break;
	case 'C':
		opts.cachewb = 1;
		break;
	case 's':
		srv = EARGF(usage());
	}ARGEND

	if(argc != 0)
		usage();

	threadpostmountsrv(&fs, srv, nil, 0);
	threadexits(nil);
}