ref: c1eed337afadb15156a5364767c3e3755586e537
dir: /unionfs.c/
#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/exportfs/%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) s_free(f->path); if(f->realpath) s_free(f->realpath); if(f->fd != -1) close(f->fd); if(f->dl) dirlistfree(f->dl); 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->length = b->length; if(a->name) free(a->name); a->name = estrdup(b->name); if(a->uid) free(a->uid); a->uid = estrdup(b->uid); if(a->gid) free(a->gid); a->gid = estrdup(b->gid); if(a->muid) free(a->muid); a->muid = estrdup(b->muid); } int catchflush(void*, char *note) { if(strcmp(note, "flush") == 0){ atnotify(catchflush, 0); return 1; } return 0; } 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 = s_copy(f->name); f->realpath = s_new(); r->fid->aux = f; r->fid->qid = f->qid; r->ofcall.qid = f->qid; respond(r, nil); } String* walk(String *s, char *n0, char *n1) { s_append(s, "/"); s_append(s, n0); s_append(s, "/"); s_append(s, n1); cleanname(s->base); s->ptr = s->base + strlen(s->base); return s; } char* clone(Fid *fid, Fid *newfid, void*) { FILE *f; FILE *parent = fid->aux; f = filenew(); dircopy(f, parent); f->path = s_clone(parent->path); f->realpath = s_clone(parent->realpath); newfid->aux = f; return nil; } char* walkto(Fid *fid, char *name, void *) { Dir *d; FILE *f; int i; f = fid->aux; walk(f->path, name, nil); for(i = 0; i < nbranch; i++){ s_reset(f->realpath); walk(f->realpath, branch[i].root, s_to_c(f->path)); if((d = dirstat(s_to_c(f->realpath))) == nil) continue; dircopy(f, d); f->qid = qencode(d); free(d); fid->qid = f->qid; return nil; } return "not found"; } void fswalk(Req *r) { 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; String *path; Dir *d; T = &r->ifcall; R = &r->ofcall; f = r->fid->aux; f->pid = getpid(); atnotify(catchflush, 1); srvrelease(&thefs); if(f->mode & DMDIR){ f->mtpt = mtptgrab(); path = s_new(); for(i = 0; i < nbranch; i++){ s_reset(path); walk(path, branch[i].root, s_to_c(f->path)); if((d = dirstat(s_to_c(path))) != nil){ if(d->mode & DMDIR) bind(s_to_c(path), f->mtpt->path, MAFTER); free(d); } } s_free(path); f->fd = open(f->mtpt->path, T->mode); mtptfree(f->mtpt); if(f->fd == -1) goto error; }else if((f->fd = open(s_to_c(f->realpath), T->mode)) == -1) goto error; R->iounit = iounit(f->fd); if(f->flushed == 0) respond(r, nil); srvacquire(&thefs); atnotify(catchflush, 0); return; error: if(f->flushed == 0) responderror(r); srvacquire(&thefs); atnotify(catchflush, 0); } void fsremove(Req *r) { FILE *f; f = r->fid->aux; srvrelease(&thefs); if(remove(s_to_c(f->realpath)) == -1){ responderror(r); goto done; } respond(r, nil); done: srvacquire(&thefs); } int dirgen(int i, Dir *d, void *aux) { Dirlist *dl = aux; Dir *dd; if(dl->ndirs == i) return -1; dd = dl->dirs[i]; dircopy(d, dd); d->qid = qencode(dd); return 0; } void fsread(Req *r) { long n; Fcall *T, *R; FILE *f; T = &r->ifcall; R = &r->ofcall; f = r->fid->aux; f->pid = getpid(); atnotify(catchflush, 1); srvrelease(&thefs); if(f->mode&DMDIR){ if(T->offset == 0){ if(seek(f->fd, 0, 0) == -1) goto error; if(f->dl != nil) dirlistfree(f->dl); if((f->dl = dirlist(f->fd)) == nil) goto error; } dirread9p(r, dirgen, f->dl); }else{ if((n = pread(f->fd, R->data, T->count, T->offset)) == -1) goto error; r->ofcall.count = n; } if(f->flushed == 0) respond(r, nil); srvacquire(&thefs); atnotify(catchflush, 0); return; error: if(f->flushed == 0) responderror(r); srvacquire(&thefs); atnotify(catchflush, 0); } void fswrite(Req *r) { Fcall *T, *R; FILE *f; T = &r->ifcall; R = &r->ofcall; f = r->fid->aux; srvrelease(&thefs); atnotify(catchflush, 1); if((R->count = pwrite(f->fd, T->data, T->count, T->offset)) != T->count){ if(f->flushed == 0) responderror(r); goto done; } if(f->flushed == 0) respond(r, nil); done: srvacquire(&thefs); atnotify(catchflush, 0); } void fsflush(Req *r) { FILE *f = r->oldreq->fid->aux; if(f->pid == 0){ respond(r, nil); return; } switch(r->oldreq->type){ case Topen: case Tread: case Twrite: f->flushed = 1; while(postnote(PNPROC, f->pid, "flush") != 0) sleep(100); respond(r->oldreq, "interrupted"); } respond(r, nil); } 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)) == -1){ free(path); return -1; } close(fd); } free(d); if(p != nil) *p++ = '/'; } free(path); return 1; } void fscreate(Req *r) { String *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; fd = -1; realpath = s_new(); walk(realpath, branch[i].root, s_to_c(parent->path)); if(mkdirp(s_to_c(realpath)) == -1){ error: s_free(realpath); if(fd != -1) close(fd); responderror(r); srvacquire(&thefs); return; } walk(realpath, T->name, nil); if((fd = create(s_to_c(realpath), T->mode, T->perm)) == -1) goto error; if((d = dirfstat(fd)) == nil) goto error; f = filenew(); dircopy(f, d); f->fd = fd; f->qid = qencode(d); free(d); f->path = walk(s_clone(parent->path), T->name, nil); f->realpath = realpath; filefree(parent); r->fid->aux = f; R->qid = f->qid; respond(r, nil); 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(s_to_c(f->realpath), &r->d) == -1){ 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) == -1) sysfatal("bind: %r"); return q; } void main(int argc, char *argv[]) { int c, i, mflag, stdio; char pwd[512]; char *mountat, *srvname, *path; 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"; if(getwd(pwd, sizeof pwd) == nil) sysfatal("getwd: %r"); nbranch = argc; branch = b = emalloc(nbranch * sizeof(Branch)); for(i = 0; i < argc; i++){ if(strcmp(argv[i], "-c") == 0){ nbranch--; c++; continue; } path = cleanname(argv[i]); if(!(path[0] == '/' || path[0] == '#')) path = cleanname(smprint("%s/%s", pwd, path)); if((d = dirstat(path)) == nil){ fprint(2, "%s: %s does not exist, skipping\n", argv0, path); continue; } free(d); if(mountat && strcmp(path, mountat) == 0) path = pivot(path); 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.flush = fsflush; thefs.destroyfid = destroyfid; if(stdio == 0){ postmountsrv(&thefs, srvname, mountat, mflag); exits(nil); } thefs.infd = 0; thefs.outfd = 1; srv(&thefs); exits(nil); }