⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fs.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
#include <u.h>#include <libc.h>#include <bio.h>#include <String.h>#include <thread.h>#include "wiki.h"#include <auth.h>#include <fcall.h>#include <9p.h>enum {	Qindexhtml,	Qindextxt,	Qraw,	Qhistoryhtml,	Qhistorytxt,	Qdiffhtml,	Qedithtml,	Qwerrorhtml,	Qwerrortxt,	Qhttplogin,	Nfile,};static char *filelist[] = {	"index.html",	"index.txt",	"current",	"history.html",	"history.txt",	"diff.html",	"edit.html",	"werror.html",	"werror.txt",	".httplogin",};static int needhist[Nfile] = {[Qhistoryhtml] 1,[Qhistorytxt] 1,[Qdiffhtml] 1,};/* * The qids are <8-bit type><16-bit page number><16-bit page version><8-bit file index>. */enum {		/* <8-bit type> */	Droot = 1,	D1st,	D2nd,	Fnew,	Fmap,	F1st,	F2nd,};uvlongmkqid(int type, int num, int vers, int file){	return ((uvlong)type<<40) | ((uvlong)num<<24) | (vers<<8) | file;}intqidtype(uvlong path){	return (path>>40)&0xFF;}intqidnum(uvlong path){	return (path>>24)&0xFFFF;}intqidvers(uvlong path){	return (path>>8)&0xFFFF;}intqidfile(uvlong path){	return path&0xFF;}typedef struct Aux Aux;struct Aux {	String *name;	Whist *w;	int n;	ulong t;	String *s;	Map *map;};static voidfsattach(Req *r){	Aux *a;	if(r->ifcall.aname && r->ifcall.aname[0]){		respond(r, "invalid attach specifier");		return;	}	a = emalloc(sizeof(Aux));	r->fid->aux = a;	a->name = s_copy(r->ifcall.uname);	r->ofcall.qid = (Qid){mkqid(Droot, 0, 0, 0), 0, QTDIR};	r->fid->qid = r->ofcall.qid;	respond(r, nil);}static String *httplogin(void){	String *s=s_new();	Biobuf *b;	if((b = wBopen(".httplogin", OREAD)) == nil)		goto Return;	while(s_read(b, s, Bsize) > 0)		;	Bterm(b);Return:	return s;}static char*fswalk1(Fid *fid, char *name, Qid *qid){	char *q;	int i, isdotdot, n, t;	uvlong path;	Aux *a;	Whist *wh;	String *s;	isdotdot = strcmp(name, "..")==0;	n = strtoul(name, &q, 10);	path = fid->qid.path;	a = fid->aux;	switch(qidtype(path)){	case 0:		return "wikifs: bad path in server (bug)";	case Droot:		if(isdotdot){			*qid = fid->qid;			return nil;		}		if(strcmp(name, "new")==0){			*qid = (Qid){mkqid(Fnew, 0, 0, 0), 0, 0};			return nil;		}		if(strcmp(name, "map")==0){			*qid = (Qid){mkqid(Fmap, 0, 0, 0), 0, 0};			return nil;		}		if((*q!='\0' || (wh=getcurrent(n))==nil)		&& (wh=getcurrentbyname(name))==nil)			return "file does not exist";		*qid = (Qid){mkqid(D1st, wh->n, 0, 0), wh->doc->time, QTDIR};		a->w = wh;		return nil;	case D1st:		if(isdotdot){			*qid = (Qid){mkqid(Droot, 0, 0, 0), 0, QTDIR};			return nil;		}		/* handle history directories */		if(*q == '\0'){			if((wh = gethistory(qidnum(path))) == nil)				return "file does not exist";			for(i=0; i<wh->ndoc; i++)				if(wh->doc[i].time == n)					break;			if(i==wh->ndoc){				closewhist(wh);				return "file does not exist";			}			closewhist(a->w);			a->w = wh;			a->n = i;			*qid = (Qid){mkqid(D2nd, qidnum(path), i, 0), wh->doc[i].time, QTDIR};			return nil;		}		/* handle files other than index */		for(i=0; i<nelem(filelist); i++){			if(strcmp(name, filelist[i])==0){				if(needhist[i]){					if((wh = gethistory(qidnum(path))) == nil)						return "file does not exist";					closewhist(a->w);					a->w = wh;				}				*qid = (Qid){mkqid(F1st, qidnum(path), 0, i), a->w->doc->time, 0};				goto Gotfile;			}		}		return "file does not exist";	case D2nd:		if(isdotdot){			/*			 * Can't use a->w[a->ndoc-1] because that			 * might be a failed write rather than the real one.			 */			*qid = (Qid){mkqid(D1st, qidnum(path), 0, 0), 0, QTDIR};			if((wh = getcurrent(qidnum(path))) == nil)				return "file does not exist";			closewhist(a->w);			a->w = wh;			a->n = 0;			return nil;		}		for(i=0; i<=Qraw; i++){			if(strcmp(name, filelist[i])==0){				*qid = (Qid){mkqid(F2nd, qidnum(path), qidvers(path), i), a->w->doc->time, 0};				goto Gotfile;			}		}		return "file does not exist";	default:		return "bad programming";	}	/* not reached */Gotfile:	t = qidtype(qid->path);	switch(qidfile(qid->path)){	case Qindexhtml:		s = tohtml(a->w, a->w->doc+a->n,			t==F1st? Tpage : Toldpage);		break;	case Qindextxt:		s = totext(a->w, a->w->doc+a->n,			t==F1st? Tpage : Toldpage);		break;	case Qraw:		s = s_copy(a->w->title);		s = s_append(s, "\n");		s = doctext(s, &a->w->doc[a->n]);		break;	case Qhistoryhtml:		s = tohtml(a->w, a->w->doc+a->n, Thistory);		break;	case Qhistorytxt:		s = totext(a->w, a->w->doc+a->n, Thistory);		break;	case Qdiffhtml:		s = tohtml(a->w, a->w->doc+a->n, Tdiff);		break;	case Qedithtml:		s = tohtml(a->w, a->w->doc+a->n, Tedit);		break;	case Qwerrorhtml:		s = tohtml(a->w, a->w->doc+a->n, Twerror);		break;	case Qwerrortxt:		s = totext(a->w, a->w->doc+a->n, Twerror);		break;	case Qhttplogin:		s = httplogin();		break;	default:		return "internal error";	}	a->s = s;	return nil;}static voidfsopen(Req *r){	int t;	uvlong path;	Aux *a;	Fid *fid;	Whist *wh;	fid = r->fid;	path = fid->qid.path;	t = qidtype(fid->qid.path);	if((r->ifcall.mode != OREAD && t != Fnew && t != Fmap)	|| (r->ifcall.mode&ORCLOSE)){		respond(r, "permission denied");		return;	}	a = fid->aux;	switch(t){	case Droot:		currentmap(0);		rlock(&maplock);		a->map = map;		incref(map);		runlock(&maplock);		respond(r, nil);		break;			case D1st:		if((wh = gethistory(qidnum(path))) == nil){			respond(r, "file does not exist");			return;		}		closewhist(a->w);		a->w = wh;		a->n = a->w->ndoc-1;		r->ofcall.qid.vers = wh->doc[a->n].time;		r->fid->qid = r->ofcall.qid;		respond(r, nil);		break;			case D2nd:		respond(r, nil);		break;	case Fnew:		a->s = s_copy("");		respond(r, nil);		break;	case Fmap:	case F1st:	case F2nd:		respond(r, nil);		break;	default:		respond(r, "programmer error");		break;	}}static char*fsclone(Fid *old, Fid *new){	Aux *a;	a = emalloc(sizeof(*a));	*a = *(Aux*)old->aux;	if(a->s)		s_incref(a->s);	if(a->w)		incref(a->w);	if(a->map)		incref(a->map);	if(a->name)		s_incref(a->name);	new->aux = a;	new->qid = old->qid;	return nil;}static voidfsdestroyfid(Fid *fid){	Aux *a;	a = fid->aux;	if(a==nil)		return;	if(a->name)		s_free(a->name);	if(a->map)		closemap(a->map);	if(a->s)		s_free(a->s);	if(a->w)		closewhist(a->w);	free(a);	fid->aux = nil;}static voidfillstat(Dir *d, uvlong path, ulong tm, ulong length){	char tmp[32], *p;	int type;	memset(d, 0, sizeof(Dir));	d->uid = estrdup9p("wiki");	d->gid = estrdup9p("wiki");	switch(qidtype(path)){	case Droot:	case D1st:	case D2nd:		type = QTDIR;		break;	default:		type = 0;		break;	}	d->qid = (Qid){path, tm, type};	d->atime = d->mtime = tm;	d->length = length;	if(qidfile(path) == Qedithtml)		d->atime = d->mtime = time(0);	switch(qidtype(path)){	case Droot:		d->name = estrdup("/");		d->mode = DMDIR|0555;		break;	case D1st:		d->name = numtoname(qidnum(path));		if(d->name == nil)			d->name = estrdup("<dead>");		for(p=d->name; *p; p++)			if(*p==' ')				*p = '_';		d->mode = DMDIR|0555;		break;	case D2nd:		snprint(tmp, sizeof tmp, "%lud", tm);		d->name = estrdup(tmp);		d->mode = DMDIR|0555;		break;	case Fmap:		d->name = estrdup("map");		d->mode = 0666;		break;	case Fnew:		d->name = estrdup("new");		d->mode = 0666;		break;	case F1st:		d->name = estrdup(filelist[qidfile(path)]);		d->mode = 0444;		break;	case F2nd:		d->name = estrdup(filelist[qidfile(path)]);		d->mode = 0444;		break;	default:		print("bad qid path 0x%.8llux\n", path);		break;	}}static voidfsstat(Req *r){	Aux *a;	Fid *fid;	ulong t;	t = 0;	fid = r->fid;	if((a = fid->aux) && a->w)		t = a->w->doc[a->n].time;	fillstat(&r->d, fid->qid.path, t, a->s ? s_len(a->s) : 0);	respond(r, nil);}typedef struct Bogus Bogus;struct Bogus {	uvlong path;	Aux *a;};static introotgen(int i, Dir *d, void *aux){	Aux *a;	Bogus *b;	b = aux;	a = b->a;	switch(i){	case 0:	/* new */		fillstat(d, mkqid(Fnew, 0, 0, 0), a->map->t, 0);		return 0;	case 1:	/* map */		fillstat(d, mkqid(Fmap, 0, 0, 0), a->map->t, 0);		return 0;	default:	/* first-level directory */		i -= 2;		if(i >= a->map->nel)			return -1;		fillstat(d, mkqid(D1st, a->map->el[i].n, 0, 0), a->map->t, 0);		return 0;	}}static intfirstgen(int i, Dir *d, void *aux){	ulong t;	Bogus *b;	int num;	Aux *a;	b = aux;	num = qidnum(b->path);	a = b->a;	t = a->w->doc[a->n].time;	if(i < Nfile){	/* file in first-level directory */		fillstat(d, mkqid(F1st, num, 0, i), t, 0);		return 0;	}	i -= Nfile;	if(i < a->w->ndoc){	/* second-level (history) directory */		fillstat(d, mkqid(D2nd, num, i, 0), a->w->doc[i].time, 0);		return 0;	}	//i -= a->w->ndoc;	return -1;}static intsecondgen(int i, Dir *d, void *aux){	Bogus *b;	uvlong path;	Aux *a;	b = aux;	path = b->path;	a = b->a;	if(i <= Qraw){	/* index.html, index.txt, raw */		fillstat(d, mkqid(F2nd, qidnum(path), qidvers(path), i), a->w->doc[a->n].time, 0);		return 0;	}	//i -= Qraw;	return -1;}static voidfsread(Req *r){	char *t, *s;	uvlong path;	Aux *a;	Bogus b;	a = r->fid->aux;	path = r->fid->qid.path;	b.a = a;	b.path = path;	switch(qidtype(path)){	default:		respond(r, "cannot happen (bad qid)");		return;	case Droot:		if(a == nil || a->map == nil){			respond(r, "cannot happen (no map)");			return;		}		dirread9p(r, rootgen, &b);		respond(r, nil);		return;			case D1st:		if(a == nil || a->w == nil){			respond(r, "cannot happen (no wh)");			return;		}		dirread9p(r, firstgen, &b);		respond(r, nil);		return;			case D2nd:		dirread9p(r, secondgen, &b);		respond(r, nil);		return;	case Fnew:		if(a->s){			respond(r, "protocol botch");			return;		}		/* fall through */	case Fmap:		t = numtoname(a->n);		if(t == nil){			respond(r, "unknown name");			return;		}		for(s=t; *s; s++)			if(*s == ' ')				*s = '_';		readstr(r, t);		free(t);		respond(r, nil);		return;	case F1st:	case F2nd:		if(a == nil || a->s == nil){			respond(r, "cannot happen (no s)");			return;		}		readbuf(r, s_to_c(a->s), s_len(a->s));		respond(r, nil);		return;	}}typedef struct Sread Sread;struct Sread {	char *rp;};static char*Srdline(void *v, int c){	char *p, *rv;	Sread *s;	s = v;	if(s->rp == nil)		rv = nil;	else if(p = strchr(s->rp, c)){		*p = '\0';		rv = s->rp;		s->rp = p+1;	}else{		rv = s->rp;		s->rp = nil;	}	return rv;}static voidresponderrstr(Req *r){	char buf[ERRMAX];	rerrstr(buf, sizeof buf);	if(buf[0] == '\0')		strcpy(buf, "unknown error");	respond(r, buf);}static voidfswrite(Req *r){	char *author, *comment, *net, *err, *p, *title, tmp[40];	int rv, n;	ulong t;	Aux *a;	Fid *fid;	Sread s;	String *stmp;	Whist *w;	fid = r->fid;	a = fid->aux;	switch(qidtype(fid->qid.path)){	case Fmap:		stmp = s_nappend(s_reset(nil), r->ifcall.data, r->ifcall.count);		a->n = nametonum(s_to_c(stmp));		s_free(stmp);		if(a->n < 0)			respond(r, "name not found");		else			respond(r, nil);		return;	case Fnew:		break;	default:		respond(r, "cannot happen");		return;	}	if(a->s == nil){		respond(r, "protocol botch");		return;	}	if(r->ifcall.count==0){	/* do final processing */		s.rp = s_to_c(a->s);		w = nil;		err = "bad format";		if((title = Srdline(&s, '\n')) == nil){		Error:			if(w)				closewhist(w);			s_free(a->s);			a->s = nil;			respond(r, err);			return;		}		w = emalloc(sizeof(*w));		incref(w);		w->title = estrdup(title);		t = 0;		author = estrdup(s_to_c(a->name));		comment = nil;		while(s.rp && *s.rp && *s.rp != '\n'){			p = Srdline(&s, '\n');			assert(p != nil);			switch(p[0]){			case 'A':				free(author);				author = estrdup(p+1);				break;			case 'D':				t = strtoul(p+1, &p, 10);				if(*p != '\0')					goto Error;				break;			case 'C':				free(comment);				comment = estrdup(p+1);				break;			}		}		w->doc = emalloc(sizeof(w->doc[0]));		w->doc->time = time(0);		w->doc->comment = comment;		if(net = r->pool->srv->aux){			p = emalloc(strlen(author)+10+strlen(net));			strcpy(p, author);			strcat(p, " (");			strcat(p, net);			strcat(p, ")");			free(author);			author = p;		}		w->doc->author = author;		if((w->doc->wtxt = Brdpage(Srdline, &s)) == nil){			err = "empty document";			goto Error;		}		w->ndoc = 1;		if((n = allocnum(w->title, 0)) < 0)			goto Error;		sprint(tmp, "D%lud\n", w->doc->time);		a->s = s_reset(a->s);		a->s = doctext(a->s, w->doc);		rv = writepage(n, t, a->s, w->title);		s_free(a->s);		a->s = nil;		a->n = n;		closewhist(w);		if(rv < 0)			responderrstr(r);		else			respond(r, nil);		return;	}	if(s_len(a->s)+r->ifcall.count > Maxfile){		respond(r, "file too large");		s_free(a->s);		a->s = nil;		return;	}	a->s = s_nappend(a->s, r->ifcall.data, r->ifcall.count);	r->ofcall.count = r->ifcall.count;	respond(r, nil);}Srv wikisrv = {.attach=	fsattach,.destroyfid=	fsdestroyfid,.clone=	fsclone,.walk1=	fswalk1,.open=	fsopen,.read=	fsread,.write=	fswrite,.stat=	fsstat,};voidusage(void){	fprint(2, "usage: wikifs [-D] [-a addr]... [-m mtpt] [-p perm] [-s service] dir\n");	exits("usage");}voidmain(int argc, char **argv){	char **addr;	int i, naddr;	char *buf;	char *service, *mtpt;	ulong perm;	Dir d, *dp;	Srv *s;	naddr = 0;	addr = nil;	perm = 0;	service = nil;	mtpt = "/mnt/wiki";	ARGBEGIN{	case 'D':		chatty9p++;		break;	case 'a':		if(naddr%8 == 0)			addr = erealloc(addr, (naddr+8)*sizeof(addr[0]));		addr[naddr++] = EARGF(usage());		break;	case 'm':		mtpt = EARGF(usage());		break;	case 'M':		mtpt = nil;		break;	case 'p':		perm = strtoul(EARGF(usage()), nil, 8);		break;	case 's':		service = EARGF(usage());		break;	default:		usage();		break;	}ARGEND	if(argc != 1)		usage();	if((dp = dirstat(argv[0])) == nil)		sysfatal("dirstat %s: %r", argv[0]);	if((dp->mode&DMDIR) == 0)		sysfatal("%s: not a directory", argv[0]);	free(dp);	wikidir = argv[0];	currentmap(0);	for(i=0; i<naddr; i++)		listensrv(&wikisrv, addr[i]);	s = emalloc(sizeof *s);	*s = wikisrv;	postmountsrv(s, service, mtpt, MREPL|MCREATE);	if(perm){		buf = emalloc9p(5+strlen(service)+1);		strcpy(buf, "/srv/");		strcat(buf, service);		nulldir(&d);		d.mode = perm;		if(dirwstat(buf, &d) < 0)			fprint(2, "wstat: %r\n");		free(buf);	}	exits(nil);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -