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

📄 lnfs.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
#include <u.h>#include <libc.h>#include <bio.h>#include <auth.h>#include <fcall.h>#include <String.h>#include <libsec.h>enum{	OPERM	= 0x3,		/* mask of all permission types in open mode */	Maxsize	= 512*1024*1024,	Maxfdata	= 8192,	NAMELEN = 28,};typedef struct Fid Fid;struct Fid{	short	busy;	int	fid;	Fid	*next;	char	*user;	String	*path;		/* complete path */	int	fd;		/* set on open or create */	Qid	qid;		/* set on open or create */	int	attach;		/* this is an attach fd */	ulong	diroff;		/* directory offset */	Dir	*dir;		/* directory entries */	int	ndir;		/* number of directory entries */};Fid	*fids;int	mfd[2];char	*user;uchar	mdata[IOHDRSZ+Maxfdata];uchar	rdata[Maxfdata];	/* buffer for data in reply */uchar	statbuf[STATMAX];Fcall	thdr;Fcall	rhdr;int	messagesize = sizeof mdata;int	readonly;char	*srvname;int	debug;Fid *	newfid(int);void	io(void);void	*erealloc(void*, ulong);void	*emalloc(ulong);char	*estrdup(char*);void	usage(void);void	fidqid(Fid*, Qid*);char*	short2long(char*);char*	long2short(char*, int);void	readnames(void);void	post(char*, int);char	*rflush(Fid*), *rversion(Fid*), *rauth(Fid*),	*rattach(Fid*), *rwalk(Fid*),	*ropen(Fid*), *rcreate(Fid*),	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*);char 	*(*fcalls[])(Fid*) = {	[Tversion]	rversion,	[Tflush]	rflush,	[Tauth]	rauth,	[Tattach]	rattach,	[Twalk]		rwalk,	[Topen]		ropen,	[Tcreate]	rcreate,	[Tread]		rread,	[Twrite]	rwrite,	[Tclunk]	rclunk,	[Tremove]	rremove,	[Tstat]		rstat,	[Twstat]	rwstat,};char	Eperm[] =	"permission denied";char	Enotdir[] =	"not a directory";char	Enoauth[] =	"lnfs: authentication not required";char	Enotexist[] =	"file does not exist";char	Einuse[] =	"file in use";char	Eexist[] =	"file exists";char	Eisdir[] =	"file is a directory";char	Enotowner[] =	"not owner";char	Eisopen[] = 	"file already open for I/O";char	Excl[] = 	"exclusive use file already open";char	Ename[] = 	"illegal name";char	Eversion[] =	"unknown 9P version";voidusage(void){	fprint(2, "usage: %s [-r] [-s srvname] mountpoint\n", argv0);	exits("usage");}voidnotifyf(void *a, char *s){	USED(a);	if(strncmp(s, "interrupt", 9) == 0)		noted(NCONT);	noted(NDFLT);}voidmain(int argc, char *argv[]){	char *defmnt;	int p[2];	Dir *d;	ARGBEGIN{	case 'r':		readonly = 1;		break;	case 'd':		debug = 1;		break;	case 's':		srvname = ARGF();		if(srvname == nil)			usage();		break;	default:		usage();	}ARGEND	if(argc < 1)		usage();	defmnt = argv[0];	d = dirstat(defmnt);	if(d == nil || !(d->qid.type & QTDIR))		sysfatal("mountpoint must be an accessible directory");	free(d);	if(pipe(p) < 0)		sysfatal("pipe failed");	mfd[0] = p[0];	mfd[1] = p[0];	user = getuser();	notify(notifyf);	if(srvname != nil)		post(srvname, p[1]);	if(debug)		fmtinstall('F', fcallfmt);	switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){	case -1:		sysfatal("fork: %r");	case 0:		close(p[1]);		chdir(defmnt);		io();		break;	default:		close(p[0]);	/* don't deadlock if child fails */		if(mount(p[1], -1, defmnt, MREPL|MCREATE, "") < 0)			sysfatal("mount failed: %r");	}	exits(0);}voidpost(char *srvname, int pfd){	char name[128];	int fd;	snprint(name, sizeof name, "#s/%s", srvname);	fd = create(name, OWRITE, 0666);	if(fd < 0)		sysfatal("create of %s failed: %r", srvname);	sprint(name, "%d", pfd);	if(write(fd, name, strlen(name)) < 0)		sysfatal("writing %s: %r", srvname);}char*rversion(Fid*){	Fid *f;	for(f = fids; f; f = f->next)		if(f->busy)			rclunk(f);	if(thdr.msize > sizeof mdata)		rhdr.msize = sizeof mdata;	else		rhdr.msize = thdr.msize;	messagesize = rhdr.msize;	if(strncmp(thdr.version, "9P2000", 6) != 0)		return Eversion;	rhdr.version = "9P2000";	return nil;}char*rauth(Fid*){	return Enoauth;}char*rflush(Fid *f){	USED(f);	return nil;}char*rattach(Fid *f){	/* no authentication! */	f->busy = 1;	if(thdr.uname[0])		f->user = estrdup(thdr.uname);	else		f->user = "none";	if(strcmp(user, "none") == 0)		user = f->user;	if(f->path)		s_free(f->path);	f->path = s_copy(".");	fidqid(f, &rhdr.qid);	f->attach = 1;	return nil;}char*clone(Fid *f, Fid **nf){	if(f->fd >= 0)		return Eisopen;	*nf = newfid(thdr.newfid);	(*nf)->busy = 1;	if((*nf)->path)		s_free((*nf)->path);	(*nf)->path = s_clone(f->path);	(*nf)->fd = -1;	(*nf)->user = f->user;	(*nf)->attach = 0;	return nil;}char*rwalk(Fid *f){	char *name;	Fid *nf;	char *err;	int i;	String *npath;	Dir *d;	char *cp;	Qid qid;	err = nil;	nf = nil;	rhdr.nwqid = 0;	if(rhdr.newfid != rhdr.fid){		err = clone(f, &nf);		if(err)			return err;		f = nf;	/* walk the new fid */	}	readnames();	npath = s_clone(f->path);	if(thdr.nwname > 0){		for(i=0; i<thdr.nwname && i<MAXWELEM; i++){			name = long2short(thdr.wname[i], 0);			if(strcmp(name, ".") == 0){				;			} else if(strcmp(name, "..") == 0){				cp = strrchr(s_to_c(npath), '/');				if(cp != nil){					*cp = 0;					npath->ptr = cp;				}			} else {				s_append(npath, "/");				s_append(npath, name);			}			d = dirstat(s_to_c(npath));			if(d == nil)				break;			rhdr.nwqid++;			qid = d->qid;			rhdr.wqid[i] = qid;			free(d);		}		if(i==0 && err == nil)			err = Enotexist;	}	/* if there was an error and we cloned, get rid of the new fid */	if(nf != nil && (err!=nil || rhdr.nwqid<thdr.nwname)){		f->busy = 0;		s_free(npath);	}	/* update the fid after a successful walk */	if(rhdr.nwqid == thdr.nwname){		s_free(f->path);		f->path = npath;	}	return err;}static char*passerror(void){	static char err[256];	rerrstr(err, sizeof err);	return err;}char*ropen(Fid *f){	if(readonly && (thdr.mode & 3))		return Eperm;	if(f->fd >= 0)		return Eisopen;	f->fd = open(s_to_c(f->path), thdr.mode);	if(f->fd < 0)		return passerror();	fidqid(f, &rhdr.qid);	f->qid = rhdr.qid;	rhdr.iounit = messagesize-IOHDRSZ;	return nil;}char*rcreate(Fid *f){	char *name;	if(readonly)		return Eperm;	readnames();	name = long2short(thdr.name, 1);	if(f->fd >= 0)		return Eisopen;	s_append(f->path, "/");	s_append(f->path, name);	f->fd = create(s_to_c(f->path), thdr.mode, thdr.perm);	if(f->fd < 0)		return passerror();	fidqid(f, &rhdr.qid);	f->qid = rhdr.qid;	rhdr.iounit = messagesize-IOHDRSZ;	return nil;}char*rreaddir(Fid *f){	int i;	int n;	/* reread the directory */	if(thdr.offset == 0){		if(f->dir)			free(f->dir);		f->dir = nil;		if(f->diroff != 0)			seek(f->fd, 0, 0);		f->ndir = dirreadall(f->fd, &f->dir);		f->diroff = 0;		if(f->ndir < 0)			return passerror();		readnames();		for(i = 0; i < f->ndir; i++)			f->dir[i].name = short2long(f->dir[i].name);	}	/* copy in as many directory entries as possible */	for(n = 0; f->diroff < f->ndir; n += i){		i = convD2M(&f->dir[f->diroff], rdata+n, thdr.count - n);		if(i <= BIT16SZ)			break;		f->diroff++;	}	rhdr.data = (char*)rdata;	rhdr.count = n;	return nil;}char*rread(Fid *f){	long n;	if(f->fd < 0)		return Enotexist;	if(thdr.count > messagesize-IOHDRSZ)		thdr.count = messagesize-IOHDRSZ;	if(f->qid.type & QTDIR)		return rreaddir(f);	n = pread(f->fd, rdata, thdr.count, thdr.offset);	if(n < 0)		return passerror();	rhdr.data = (char*)rdata;	rhdr.count = n;	return nil;}char*rwrite(Fid *f){	long n;	if(readonly || (f->qid.type & QTDIR))		return Eperm;	if(f->fd < 0)		return Enotexist;	if(thdr.count > messagesize-IOHDRSZ)	/* shouldn't happen, anyway */		thdr.count = messagesize-IOHDRSZ;	n = pwrite(f->fd, thdr.data, thdr.count, thdr.offset);	if(n < 0)		return passerror();	rhdr.count = n;	return nil;}char*rclunk(Fid *f){	f->busy = 0;	close(f->fd);	f->fd = -1;	f->path = s_reset(f->path);	if(f->attach){		free(f->user);		f->user = nil;	}	f->attach = 0;	if(f->dir != nil){		free(f->dir);		f->dir = nil;	}	f->diroff = f->ndir = 0;	return nil;}char*rremove(Fid *f){	if(remove(s_to_c(f->path)) < 0)		return passerror();	return nil;}char*rstat(Fid *f){	int n;	Dir *d;	d = dirstat(s_to_c(f->path));	if(d == nil)		return passerror();	d->name = short2long(d->name);	n = convD2M(d, statbuf, sizeof statbuf);	free(d);	if(n <= BIT16SZ)		return passerror();	rhdr.nstat = n;	rhdr.stat = statbuf;	return nil;}char*rwstat(Fid *f){	int n;	Dir d;	if(readonly)		return Eperm;	convM2D(thdr.stat, thdr.nstat, &d, (char*)rdata);	d.name = long2short(d.name, 1);	n = dirwstat(s_to_c(f->path), &d);	if(n < 0)		return passerror();	return nil;}Fid *newfid(int fid){	Fid *f, *ff;	ff = 0;	for(f = fids; f; f = f->next)		if(f->fid == fid)			return f;		else if(!ff && !f->busy)			ff = f;	if(ff){		ff->fid = fid;		return ff;	}	f = emalloc(sizeof *f);	f->path = s_reset(f->path);	f->fd = -1;	f->fid = fid;	f->next = fids;	fids = f;	return f;}voidio(void){	char *err;	int n, pid;	pid = getpid();	for(;;){		/*		 * reading from a pipe or a network device		 * will give an error after a few eof reads.		 * however, we cannot tell the difference		 * between a zero-length read and an interrupt		 * on the processes writing to us,		 * so we wait for the error.		 */		n = read9pmsg(mfd[0], mdata, messagesize);		if(n < 0)			sysfatal("mount read");		if(n == 0)			continue;		if(convM2S(mdata, n, &thdr) == 0)			continue;		if(debug)			fprint(2, "%s %d:<-%F\n", argv0, pid, &thdr);		if(!fcalls[thdr.type])			err = "bad fcall type";		else			err = (*fcalls[thdr.type])(newfid(thdr.fid));		if(err){			rhdr.type = Rerror;			rhdr.ename = err;		}else{			rhdr.type = thdr.type + 1;			rhdr.fid = thdr.fid;		}		rhdr.tag = thdr.tag;		if(debug)			fprint(2, "%s %d:->%F\n", argv0, pid, &rhdr);/**/		n = convS2M(&rhdr, mdata, messagesize);		if(n == 0)			sysfatal("convS2M error on write");		if(write(mfd[1], mdata, n) != n)			sysfatal("mount write");	}}void *emalloc(ulong n){	void *p;	p = malloc(n);	if(!p)		sysfatal("out of memory");	memset(p, 0, n);	return p;}void *erealloc(void *p, ulong n){	p = realloc(p, n);	if(!p)		sysfatal("out of memory");	return p;}char *estrdup(char *q){	char *p;	int n;	n = strlen(q)+1;	p = malloc(n);	if(!p)		sysfatal("out of memory");	memmove(p, q, n);	return p;}voidfidqid(Fid *f, Qid *q){	Dir *d;	d = dirstat(s_to_c(f->path));	if(d == nil)		*q = (Qid){0, 0, QTFILE};	else {		*q = d->qid;		free(d);	}}/* *  table of name translations * *  the file ./.longnames contains all the known long names. *  the short name is the first NAMELEN-1 bytes of the base64 *  encoding of the MD5 hash of the longname. */typedef struct Name Name;struct Name{	Name	*next;	char	shortname[NAMELEN];	char	*longname;};Dir *dbstat;	/* last stat of the name file */char *namefile = "./.longnames";char *namebuf;Name *names;Name*newname(char *longname, int writeflag){	Name *np;	int n;	uchar digest[MD5dlen];	int fd;	/* chain in new name */	n = strlen(longname);	np = emalloc(sizeof(*np)+n+1);	np->longname = (char*)&np[1];	strcpy(np->longname, longname);	md5((uchar*)longname, n, digest, nil);	enc32(np->shortname, sizeof(np->shortname), digest, MD5dlen);	np->next = names;	names = np;	/* don't change namefile if we're read only */	if(!writeflag)		return np;	/* add to namefile */	fd = open(namefile, OWRITE);	if(fd >= 0){		seek(fd, 0, 2);		fprint(fd, "%s\n", longname);		close(fd);	}	return np;}voidfreenames(void){	Name *np, *next;	for(np = names; np != nil; np = next){		next = np->next;		free(np);	}	names = nil;}/* *  reread the file if the qid.path has changed. * *  read any new entries if length has changed. */voidreadnames(void){	Dir *d;	int fd;	vlong offset;	Biobuf *b;	char *p;	d = dirstat(namefile);	if(d == nil){		if(readonly)			return;		/* create file if it doesn't exist */		fd = create(namefile, OREAD, DMAPPEND|0666);		if(fd < 0)			return;		if(dbstat != nil)			free(dbstat);		dbstat = nil;		close(fd);		return;	}	/* up to date? */	offset = 0;	if(dbstat != nil){		if(d->qid.path == dbstat->qid.path){			if(d->length <= dbstat->length){				free(d);				return;			}			offset = dbstat->length;		} else {			freenames();		}		free(dbstat);		dbstat = nil;	}	/* read file */	b = Bopen(namefile, OREAD);	if(b == nil){		free(d);		return;	}	Bseek(b, offset, 0);	while((p = Brdline(b, '\n')) != nil){		p[Blinelen(b)-1] = 0;		newname(p, 0);	}	Bterm(b);	dbstat = d;}/* *  look up a long name,  if it doesn't exist in the *  file, add an entry to the file if the writeflag is *  non-zero.  Return a pointer to the short name. */char*long2short(char *longname, int writeflag){	Name *np;	if(strlen(longname) < NAMELEN-1 && strpbrk(longname, " ")==nil)		return longname;	for(np = names; np != nil; np = np->next)		if(strcmp(longname, np->longname) == 0)			return np->shortname;	if(!writeflag)		return longname;	np = newname(longname, !readonly);	return np->shortname;}/* *  look up a short name, if it doesn't exist, return the *  longname. */char*short2long(char *shortname){	Name *np;	for(np = names; np != nil; np = np->next)		if(strcmp(shortname, np->shortname) == 0)			return np->longname;	return shortname;}

⌨️ 快捷键说明

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