ref: 12351d11dff058fc674198e1242b029dc19fcd26
dir: /ext4srv.c/
#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);
}