ref: 44ce503a9ecea0e1793ee1c7917e65c0ae498f2c
dir: /unionfs.c/
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "unionfs.h"
Union u0 = {.next = &u0, .prev = &u0};
Union *unionlist = &u0;
F *root;
Srv thefs;
void
unionlink(Union *p, Union *n)
{
p = p->prev;
n->next = p->next;
n->prev = p;
p->next->prev = n;
p->next = n;
}
F*
filenew(Dir *d)
{
F *f;
f = emalloc(sizeof(*f));
f->ref = 1;
f->Dir = *d;
f->qid = qencode(d);
f->name = estrdup(d->name);
f->uid = estrdup(d->uid);
f->gid = estrdup(d->gid);
f->muid = estrdup(d->muid);
return f;
}
void
filefree(F *f)
{
if(f == root)
return;
if(decref(f))
return;
free(f->name);
free(f->uid);
free(f->gid);
free(f->muid);
free(f->path);
free(f->fspath);
free(f);
}
uint
fthash(char *s)
{
uint h;
for(h = 0; *s; s++)
h = *s + 31*h;
return h % Nftab;
}
Ftab*
ftnew(void)
{
int i;
Ftab *ft, *p;
ft = emalloc(Nftab*sizeof(Ftab));
for(i = 0; i < Nftab; i++){
p = &ft[i];
p->sz = Nftlist;
p->l = emalloc(p->sz*sizeof(*p->l));
}
return ft;
}
void
ftfree(Ftab *ft)
{
int i, j;
Ftab *p;
for(i = 0; i < Nftab; i++){
p = &ft[i];
for(j = 0; j < p->n; j++)
filefree(p->l[j]);
free(p->l);
}
free(ft);
}
void
ftadd(Ftab *ft, F *f)
{
Ftab *p;
p = &ft[fthash(f->name)];
if(p->n == p->sz){
p->sz *= 2;
p->l = erealloc(p->l, p->sz*sizeof(*p->l));
}
p->l[p->n++] = f;
}
int
fthas(Ftab *ft, char *name)
{
int i;
Ftab *p;
p = &ft[fthash(name)];
for(i = 0; i < p->n; i++)
if(strcmp(p->l[i]->name, name) == 0)
return 1;
return 0;
}
F*
ftidx(Ftab *ft, long i)
{
long y;
Ftab *p;
for(y = 0; y < Nftab; y++){
p = &ft[y];
if(p->n == 0)
continue;
if(i >= p->n){
i -= p->n;
continue;
}
return p->l[i];
}
return nil;
}
Fstate*
fstatenew(F *f)
{
Fstate *st;
st = emalloc(sizeof(*st));
st->fd = -1;
st->file = (F*)copyref(f);
return st;
}
void
fstatefree(Fstate *st)
{
if(st->file)
filefree(st->file);
if(st->ftab)
ftfree(st->ftab);
close(st->fd);
free(st);
}
void
initroot(void)
{
char *user;
Dir d;
nulldir(&d);
d.qid = (Qid){0, 0, QTDIR};
d.name = ".";
d.mode = 0777|DMDIR;
user = getuser();
d.uid = user;
d.gid = user;
d.muid = user;
d.mtime = time(0);
d.atime = time(0);
d.length = 0;
root = filenew(&d);
root->fspath = estrdup(d.name);
root->path = estrdup(d.name);
}
void
fsattach(Req *r)
{
Fstate *st;
st = fstatenew(root);
r->fid->aux = st;
r->fid->qid = root->qid;
r->ofcall.qid = root->qid;
respond(r, nil);
}
F*
filewalk(F *p, char *name)
{
char *path, *np;
Dir *d;
F *f;
Union *u;
np = mkpath(p->fspath, name, nil);
for(u = unionlist->next; u != unionlist; u = u->next){
path = mkpath(u->root, np, nil);
if((d = dirstat(path)) == nil){
free(path);
continue;
}
f = filenew(d);
free(d);
f->fspath = np;
f->path = path;
filefree(p);
return f;
}
free(np);
return nil;
}
char*
walk1(Fid *fid, char *name, void *)
{
F *p, *f;
Fstate *st;
st = fid->aux;
p = st->file;
if((f = filewalk(p, name)) == nil)
return "not found";
st->file = f;
fid->qid = f->qid;
return nil;
}
char*
clone(Fid *old, Fid *new, void*)
{
Fstate *fs;
fs = old->aux;
new->aux = fstatenew(fs->file);
return nil;
}
void
destroyfid(Fid *fid)
{
if(fid->aux)
fstatefree(fid->aux);
fid->aux = nil;
}
void
fswalk(Req *r)
{
srvrelease(&thefs);
walkandclone(r, walk1, clone, nil);
srvacquire(&thefs);
}
Ftab*
filereaddir(F *p)
{
int fd;
long i, n;
Dir *dir, *d;
char *path;
Union *u;
F *f;
Ftab *ft;
ft = ftnew();
for(u = unionlist->next; u != unionlist; u = u->next){
path = mkpath(u->root, p->fspath, nil);
if((d = dirstat(path)) == nil){
err:
free(path);
continue;
}
free(d);
if((fd = open(path, OREAD)) < 0)
goto err;
free(path);
while((n = dirread(fd, &dir)) > 0){
for(i = 0; i < n; i++){
if(u->prev != unionlist && fthas(ft, dir[i].name))
continue;
f = filenew(&dir[i]);
ftadd(ft, f);
}
free(dir);
}
if(n < 0)
fprint(2, "dirread: %r\n");
close(fd);
}
return ft;
}
void
fsopen(Req *r)
{
Fcall *i, *o;
Fstate *st;
F *f;
i = &r->ifcall;
o = &r->ofcall;
st = r->fid->aux;
f = st->file;
srvrelease(&thefs);
if(f->mode&DMDIR)
st->ftab = filereaddir(f);
else{
if((st->fd = open(f->path, i->mode)) < 0){
responderror(r);
srvacquire(&thefs);
return;
}
o->iounit = iounit(st->fd);
}
respond(r, nil);
srvacquire(&thefs);
}
int
mkdirp(char *path)
{
int fd;
char *p;
Dir *d;
assert(path != nil);
if((d = dirstat(path)) != nil){
free(d);
return 1;
}
path = p = estrdup(path);
for(; p != nil;){
if(p[0] == '/')
p++;
if(p = strchr(p, '/'))
*p = 0;
if((d = dirstat(path)) == nil){
if((fd = create(path, 0, 0777|DMDIR)) < 0){
free(path);
return -1;
}
close(fd);
}
free(d);
if(p != nil)
*p++ = '/';
}
free(path);
return 1;
}
void
fscreate(Req *r)
{
char *path, *npath;
Dir *d;
Fcall *i, *o;
Union *u;
Fstate *st;
F *f, *nf;
i = &r->ifcall;
o = &r->ofcall;
st = r->fid->aux;
f = st->file;
srvrelease(&thefs);
for(u = unionlist->next; u != unionlist; u = u->next)
if(u->create == 1)
break;
path = mkpath(u->root, f->fspath, nil);
if(mkdirp(path) < 0){
responderror(r);
srvacquire(&thefs);
return;
}
npath = mkpath(path, i->name, nil);
free(path);
st = emalloc(sizeof(*st));
if((st->fd = create(npath, i->mode, i->perm)) < 0){
responderror(r);
srvacquire(&thefs);
return;
}
if((d = dirfstat(st->fd)) == nil){
fstatefree(st);
responderror(r);
srvacquire(&thefs);
return;
}
nf = filenew(d);
free(d);
nf->path = npath;
nf->fspath = estrdup(f->fspath);
st->file = nf;
r->fid->aux = st;
r->fid->qid = nf->qid;
o->qid = nf->qid;
respond(r, nil);
srvacquire(&thefs);
}
void
fsremove(Req *r)
{
Fstate *st;
F *f;
st = r->fid->aux;
f = st->file;
srvrelease(&thefs);
if(remove(f->path) < 0){
responderror(r);
srvacquire(&thefs);
return;
}
respond(r, nil);
srvacquire(&thefs);
}
void
dirfill(Dir *dir, F *f)
{
*dir = f->Dir;
dir->qid = f->qid;
dir->name = estrdup(f->name);
dir->uid = estrdup(f->uid);
dir->gid = estrdup(f->gid);
dir->muid = estrdup(f->muid);
}
int
dirgen(int i, Dir *dir, void *aux)
{
Fstate *fs;
F *f;
fs = aux;
f = ftidx(fs->ftab, i);
if(f == nil)
return -1;
dirfill(dir, f);
return 0;
}
void
fsread(Req *r)
{
long n;
Fcall *i, *o;
F *f;
Fstate *st;
i = &r->ifcall;
o = &r->ofcall;
st = r->fid->aux;
f = st->file;
srvrelease(&thefs);
if(f->mode&DMDIR){
dirread9p(r, dirgen, st);
respond(r, nil);
srvacquire(&thefs);
return;
}
if((n = pread(st->fd, o->data, i->count, i->offset)) < 0){
responderror(r);
srvacquire(&thefs);
return;
}
r->ofcall.count = n;
respond(r, nil);
srvacquire(&thefs);
}
void
fswrite(Req *r)
{
Fcall *i, *o;
Fstate *fs;
i = &r->ifcall;
o = &r->ofcall;
fs = r->fid->aux;
srvrelease(&thefs);
if((o->count = pwrite(fs->fd, i->data, i->count, i->offset)) != i->count){
responderror(r);
srvacquire(&thefs);
return;
}
respond(r, nil);
srvacquire(&thefs);
}
void
fsstat(Req *r)
{
Fstate *st;
st = r->fid->aux;
dirfill(&r->d, st->file);
respond(r, nil);
}
void
fswstat(Req *r)
{
Fstate *st;
F *f;
st = r->fid->aux;
f = st->file;
srvrelease(&thefs);
if(dirwstat(f->path, &r->d) < 0){
responderror(r);
srvacquire(&thefs);
return;
}
respond(r, nil);
srvacquire(&thefs);
}
char*
pivot(char *p)
{
static n = 0;
char *q;
if((q = smprint("/mnt/union.%d.%d", getpid(), n++)) == nil)
sysfatal("smprint: %r");
if(bind(p, q, MREPL) < 0)
sysfatal("bind: %r");
return q;
}
void
main(int argc, char *argv[])
{
int c, i, mflag, stdio;
char *mtpt, *srvname, *branch, *p;
Dir *d;
Union *u;
c = 0;
mflag = MREPL|MCREATE;
mtpt = nil;
srvname = nil;
stdio = 0;
ARGBEGIN{
case 'a':
mflag |= MAFTER;
break;
case 'b':
mflag |= MBEFORE;
break;
case 'c':
c = 1;
break;
case 'C':
mflag |= MCACHE;
break;
case 'D':
chatty9p++;
break;
case 'm':
mtpt = EARGF(usage());
break;
case 's':
srvname = EARGF(usage());
break;
case 'i':
stdio = 1;
break;
default:
usage();
}ARGEND;
if(argc < 1)
usage();
if((mtpt || srvname) == 0)
mtpt = "/mnt/union";
for(i = 0; i < argc; i++){
if(strncmp(argv[i], "-c", 2) == 0){
c++;
continue;
}
branch = mkpath(argv[i], nil);
if((d = dirstat(branch)) == nil){
fprint(2, "%s: %s does not exist, skipping\n", argv0, branch);
free(branch);
continue;
}
free(d);
if(mtpt && strcmp(branch, mtpt) == 0){
p = pivot(branch);
free(branch);
branch = p;
}
u = emalloc(sizeof(*u));
u->create = c == 1 ? c : 0;
u->root = branch;
unionlink(unionlist, u);
}
if(unionlist->next == &u0)
sysfatal("empty branch list");
if(c == 0)
unionlist->next->create = 1;
thefs.attach = fsattach;
thefs.walk = fswalk;
thefs.open = fsopen;
thefs.create = fscreate;
thefs.remove = fsremove;
thefs.read = fsread;
thefs.write = fswrite;
thefs.stat = fsstat;
thefs.wstat = fswstat;
thefs.destroyfid = destroyfid;
initroot();
if(stdio == 0){
postmountsrv(&thefs, srvname, mtpt, mflag);
exits(nil);
}
thefs.infd = 0;
thefs.outfd = 1;
srv(&thefs);
exits(nil);
}