ref: 9b14a2f3b443a8bd615551cb2320829d134650bd
dir: /unionfs.c/
#include <u.h> #include <libc.h> #include <fcall.h> #include <thread.h> #include <9p.h> #include "unionfs.h" Srv thefs; Branch *branch; usize nbranch; QLock mtptlock; Mtpt *mtpt; Mtpt* mtptgrab(void) { static int mtptnext = 0; Mtpt *m; qlock(&mtptlock); if(mtpt == nil){ mtpt = emalloc(sizeof(Mtpt)); mtpt->path = smprint("/mnt/mtpt%d", mtptnext++); } m = mtpt; mtpt = m->next; qunlock(&mtptlock); unmount(nil, m->path); return m; } void mtptfree(Mtpt *m) { qlock(&mtptlock); m->next = mtpt; mtpt = m; qunlock(&mtptlock); } FILE* filenew(void) { FILE *f; f = emalloc(sizeof(FILE)); f->fd = -1; return f; } void filefree(FILE *f) { if(f == nil) return; if(f->name) free(f->name); if(f->uid) free(f->uid); if(f->gid) free(f->gid); if(f->muid) free(f->muid); if(f->path) free(f->path); if(f->realpath) free(f->realpath); if(f->fd != -1) close(f->fd); if(f->dirs) free(f->dirs); if(f->mtpt) mtptfree(f->mtpt); free(f); } void dircopy(Dir *a, Dir *b) { a->type = b->type; a->dev = b->dev; a->qid = b->qid; a->mode = b->mode; a->mtime = b->mtime; a->atime = b->atime; a->name = estrdup(b->name); a->uid = estrdup(b->uid); a->gid = estrdup(b->gid); a->muid = estrdup(b->muid); } void fsattach(Req *r) { FILE *f; char *user; f = filenew(); f->name = estrdup("/"); f->mode = 0777|DMDIR; user = getuser(); f->uid = estrdup(user); f->gid = estrdup(user); f->muid = estrdup(user); f->mtime = f->atime = time(0); f->type = 0xFFFFFFFF; f->dev = 0xFFFFFFFFFFFFFFFF; f->qid = (Qid){0, 0, QTDIR}; f->qid = qencode(f); f->path = estrdup(f->name); f->realpath = estrdup(f->name); r->fid->aux = f; r->fid->qid = f->qid; r->ofcall.qid = f->qid; respond(r, nil); } char* mkpath(char *a0, ...) { va_list args; int i; char *a; char *ap[] = {a0, "", ""}; va_start(args, a0); for(i = 1; (a = va_arg(args, char*)) != nil && i < 3; i++) ap[i] = a; va_end(args); if((a = smprint("%s/%s/%s", ap[0], ap[1], ap[2])) == nil) sysfatal("smprint: %r"); return cleanname(a); } char* clone(Fid *fid, Fid *newfid, void*) { FILE *f; FILE *parent = fid->aux; f = filenew(); dircopy(f, parent); f->qid = parent->qid; f->path = estrdup(parent->path); f->realpath = estrdup(parent->realpath); newfid->aux = f; return nil; } char* walkto(Fid *fid, char *name, void *aux) { Req *r; Dir *d; FILE *f; char *path, *realpath; int i, *nwalk; r = aux; f = fid->aux; nwalk = r->aux; path = mkpath(f->path, name, nil); for(i = 0; i < nbranch; i++){ realpath = mkpath(branch[i].root, path, nil); if((d = dirstat(realpath)) == nil) continue; if(*nwalk == r->ifcall.nwname){ filefree(f); f = filenew(); dircopy(f, d); }else{ free(f->path); free(f->realpath); } f->qid = qencode(d); free(d); f->path = path; f->realpath = realpath; fid->aux = f; fid->qid = f->qid; *nwalk = *nwalk + 1; return nil; } return "not found"; } void fswalk(Req *r) { int nwalk; nwalk = 1; r->aux = &nwalk; walkandclone(r, walkto, clone, r); } void destroyfid(Fid *fid) { if(fid->aux) filefree(fid->aux); fid->aux = nil; } void fsopen(Req *r) { Fcall *T, *R; FILE *f; usize i; char *path; Dir *d; T = &r->ifcall; R = &r->ofcall; f = r->fid->aux; srvrelease(&thefs); if(f->mode & DMDIR){ f->mtpt = mtptgrab(); for(i = 0; i < nbranch; i++){ path = mkpath(branch[i].root, f->path, nil); if((d = dirstat(path)) != nil){ if(d->mode & DMDIR) if(bind(path, f->mtpt->path, MAFTER) == -1) sysfatal("bind: %r"); free(d); } free(path); } if((f->fd = open(f->mtpt->path, T->mode)) < 0){ responderror(r); goto done; } }else{ if((f->fd = open(f->realpath, T->mode)) < 0){ responderror(r); goto done; } } R->iounit = iounit(f->fd); respond(r, nil); done: srvacquire(&thefs); } void fsremove(Req *r) { FILE *f; f = r->fid->aux; srvrelease(&thefs); if(remove(f->path) < 0){ responderror(r); goto done; } respond(r, nil); done: srvacquire(&thefs); } int dirgen(int i, Dir *d, void *aux) { FILE *f = aux; if(i == 0){ if(f->dirs){ free(f->dirs); f->dirs = nil; f->ndirs = 0; } f->ndirs = dirreadall(f->fd, &f->dirs); if(f->ndirs == -1) sysfatal("dirreadall: %r"); } if(f->ndirs == i) return -1; dircopy(d, &f->dirs[i]); d->qid = qencode(d); return 0; } void fsread(Req *r) { long n; Fcall *T, *R; FILE *f; T = &r->ifcall; R = &r->ofcall; f = r->fid->aux; srvrelease(&thefs); if(f->mode&DMDIR){ dirread9p(r, dirgen, f); }else{ if((n = pread(f->fd, R->data, T->count, T->offset)) < 0){ responderror(r); goto done; } r->ofcall.count = n; } respond(r, nil); done: srvacquire(&thefs); } void fswrite(Req *r) { Fcall *T, *R; FILE *f; T = &r->ifcall; R = &r->ofcall; f = r->fid->aux; srvrelease(&thefs); if((R->count = pwrite(f->fd, T->data, T->count, T->offset)) != T->count){ responderror(r); goto done; } respond(r, nil); done: 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, *realpath; usize i; Dir *d; Fcall *T, *R; FILE *parent, *f; int fd; T = &r->ifcall; R = &r->ofcall; parent = r->fid->aux; srvrelease(&thefs); for(i = 0; i < nbranch; i++) if(branch[i].create == 1) break; path = mkpath(branch[i].root, parent->path, nil); if(mkdirp(path) < 0){ responderror(r); goto done; } realpath = mkpath(path, T->name, nil); free(path); if((fd = create(realpath, T->mode, T->perm)) < 0){ free(realpath); responderror(r); goto done; } if((d = dirfstat(fd)) == nil){ free(realpath); responderror(r); goto done; } f = filenew(); dircopy(f, d); f->fd = fd; f->qid = qencode(d); f->path = mkpath(parent->path, T->name, nil); f->realpath = realpath; free(d); filefree(parent); r->fid->aux = f; R->qid = f->qid; respond(r, nil); done: srvacquire(&thefs); } void fsstat(Req *r) { FILE *f = r->fid->aux; dircopy(&r->d, f); respond(r, nil); } void fswstat(Req *r) { FILE *f = r->fid->aux; srvrelease(&thefs); if(dirwstat(f->realpath, &r->d) < 0){ responderror(r); goto done; } respond(r, nil); done: 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 *mountat, *srvname, *path, *p; Dir *d; Branch *b; c = 0; mflag = MREPL|MCREATE; mountat = 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': mountat = EARGF(usage()); break; case 's': srvname = EARGF(usage()); break; case 'i': stdio = 1; break; default: usage(); }ARGEND; if(argc < 1) usage(); if((mountat || srvname) == 0) mountat = "/mnt/union"; nbranch = argc; branch = b = emalloc(nbranch * sizeof(Branch)); for(i = 0; i < argc; i++){ if(strcmp(argv[i], "-c") == 0){ nbranch--; c++; continue; } path = mkpath(argv[i], nil); if((d = dirstat(path)) == nil){ fprint(2, "%s: %s does not exist, skipping\n", argv0, path); free(path); continue; } free(d); if(mountat && strcmp(path, mountat) == 0){ p = pivot(path); free(path); path = p; } b->root = path; b->create = c == 1 ? c : 0; b++; } if(branch[0].root == nil) sysfatal("empty branch list"); if(c == 0) branch[0].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; if(stdio == 0){ postmountsrv(&thefs, srvname, mountat, mflag); exits(nil); } thefs.infd = 0; thefs.outfd = 1; srv(&thefs); exits(nil); }