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

📄 ftpfs.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
字号:
#include <u.h>#include <libc.h>#include <auth.h>#include <fcall.h>#include <String.h>#include "ftpfs.h"/* an active fid */typedef struct Fid	Fid;struct Fid{	int	fid;	Node	*node;		/* path to remote file */	int	busy;	Fid	*next;	int	open;};Fid	*fids;			/* linked list of fids */char	errstring[128];		/* error to return */int	mfd;			/* fd for 9fs */int	messagesize = 4*1024*IOHDRSZ;uchar	mdata[8*1024*IOHDRSZ];uchar	mbuf[8*1024*IOHDRSZ];Fcall	rhdr;Fcall	thdr;int	debug;int	usenlst;int	usetls;char	*ext;int	quiet;int	kapid = -1;int	dying;		/* set when any process decides to die */int	dokeepalive;char	*rflush(Fid*), *rnop(Fid*), *rversion(Fid*),	*rattach(Fid*), *rclone(Fid*), *rwalk(Fid*),	*rclwalk(Fid*), *ropen(Fid*), *rcreate(Fid*),	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),	*rauth(Fid*);;void	mountinit(char*);void	io(void);int	readpdir(Node*);char 	*(*fcalls[])(Fid*) = {	[Tflush]	rflush,	[Tversion]	rversion,	[Tattach]	rattach,	[Tauth]		rauth,	[Twalk]		rwalk,	[Topen]		ropen,	[Tcreate]	rcreate,	[Tread]		rread,	[Twrite]	rwrite,	[Tclunk]	rclunk,	[Tremove]	rremove,	[Tstat]		rstat,	[Twstat]	rwstat,};OS oslist[] = {	{ Plan9,	"Plan 9", },	{ Plan9,	"Plan9", },	{ Plan9,	"UNIX Type: L8 Version: Plan 9", },	{ Unix,		"SUN", },	{ Unix,		"UNIX", },	{ VMS,		"VMS", },	{ VM,		"VM", },	{ Tops,		"TOPS", },	{ MVS,		"MVS", },	{ NetWare,	"NetWare", },	{ NetWare,	"NETWARE", },	{ OS½,		"OS/2", },	{ TSO,		"TSO", },	{ NT,		"Windows_NT", },	/* DOS like interface */	{ NT,		"WINDOWS_NT", },	/* Unix like interface */	{ Unknown,	0 },};char *nouid = "?uid?";#define S2P(x) (((ulong)(x)) & 0xffffff)char *nosuchfile = "file does not exist";char *keyspec = "";voidusage(void){	fprint(2, "ftpfs [-/dqnt] [-a passwd] [-m mountpoint] [-e ext] [-o os] [-r root] [net!]address\n");	exits("usage");}voidmain(int argc, char *argv[]){	char *mountroot = 0;	char *mountpoint = "/n/ftp";	char *cpassword = 0;	char *cp;	int p[2];	OS *o;	defos = Unix;	user = strdup(getuser());	usetls = 0;	ARGBEGIN {	case '/':		mountroot = "/";		break;	case 'a':		cpassword = ARGF();		break;	case 'd':		debug = 1;		break;	case 'k':		keyspec = EARGF(usage());		break;	case 'K':		dokeepalive = 1;		break;	case 'm':		mountpoint = ARGF();		break;	case 'n':		usenlst = 1;		break;	case 'e':		ext = ARGF();		break;	case 't':		usetls = 1;		break;	case 'o':		cp = ARGF();		for(o = oslist; o->os != Unknown; o++)			if(strncmp(cp, o->name, strlen(o->name)) == 0){				defos = o->os;				break;			}		break;	case 'r':		mountroot = ARGF();		break;	case 'q':		quiet = 1;		break;	} ARGEND	if(argc != 1)		usage();	/* get a pipe to mount and run 9fs on */	if(pipe(p) < 0)		fatal("pipe failed: %r");	mfd = p[0];	/* initial handshakes with remote side */	hello(*argv);	if(cpassword == 0)		rlogin(*argv, keyspec);	else		clogin("anonymous", cpassword);	preamble(mountroot);	/* start the 9fs protocol */	switch(rfork(RFPROC|RFNAMEG|RFENVG|RFFDG|RFNOTEG|RFREND)){	case -1:		fatal("fork: %r");	case 0:		/* seal off standard input/output */		close(0);		open("/dev/null", OREAD);		close(1);		open("/dev/null", OWRITE);		close(p[1]);		fmtinstall('F', fcallfmt); /* debugging */		fmtinstall('D', dirfmt); /* expected by %F */		fmtinstall('M', dirmodefmt); /* expected by %F */		io();		quit();		break;	default:		close(p[0]);		if(mount(p[1], -1, mountpoint, MREPL|MCREATE, "") < 0)			fatal("mount failed: %r");	}	exits(0);}/* *  lookup an fid. if not found, create a new one. */Fid *newfid(int fid){	Fid *f, *ff;	ff = 0;	for(f = fids; f; f = f->next){		if(f->fid == fid){			if(f->busy)				return f;			else{				ff = f;				break;			}		} else if(!ff && !f->busy)			ff = f;	}	if(ff == 0){		ff = mallocz(sizeof(*f), 1);		ff->next = fids;		fids = ff;	}	ff->node = nil;	ff->fid = fid;	return ff;}/* *  a process that sends keep alive messages to *  keep the server from shutting down the connection */intkaproc(void){	int pid;	if(!dokeepalive)		return -1;	switch(pid = rfork(RFPROC|RFMEM)){	case -1:		return -1;	case 0:		break;	default:		return pid;	}	while(!dying){		sleep(5000);		nop();	}	_exits(0);	return -1;}voidio(void){	char *err, buf[ERRMAX];	int n;	kapid = kaproc();	while(!dying){		n = read9pmsg(mfd, mdata, messagesize);		if(n <= 0){			errstr(buf, sizeof buf);			if(buf[0]=='\0' || strstr(buf, "hungup"))				exits("");			fatal("mount read: %s\n", buf);		}		if(convM2S(mdata, n, &thdr) == 0)			continue;		if(debug)			fprint(2, "<-%F\n", &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, "->%F\n", &rhdr);/**/		n = convS2M(&rhdr, mdata, messagesize);		if(write(mfd, mdata, n) != n)			fatal("mount write");	}}char*rnop(Fid *f){	USED(f);	return 0;}char*rversion(Fid*){	if(thdr.msize > sizeof(mdata))		rhdr.msize = messagesize;	else		rhdr.msize = thdr.msize;	messagesize = thdr.msize;	if(strncmp(thdr.version, "9P2000", 6) != 0)		return "unknown 9P version";	rhdr.version = "9P2000";	return nil;}char*rflush(Fid*){	return 0;}char*rauth(Fid*){	return "auth unimplemented";}char*rattach(Fid *f){	f->busy = 1;	f->node = remroot;	rhdr.qid = f->node->d->qid;	return 0;}char*rwalk(Fid *f){	Node *np;	Fid *nf;	char **elems;	int i, nelems;	char *err;	Node *node;	/* clone fid */	nf = nil;	if(thdr.newfid != thdr.fid){		nf = newfid(thdr.newfid);		if(nf->busy)			return "newfid in use";		nf->busy = 1;		nf->node = f->node;		f = nf;	}	err = nil;	elems = thdr.wname;	nelems = thdr.nwname;	node = f->node;	rhdr.nwqid = 0;	if(nelems > 0){		/* walk fid */		for(i=0; i<nelems && i<MAXWELEM; i++){			if((node->d->qid.type & QTDIR) == 0){				err = "not a directory";				break;			}			if(strcmp(elems[i], ".") == 0){   Found:				rhdr.wqid[i] = node->d->qid;				rhdr.nwqid++;				continue;			}			if(strcmp(elems[i], "..") == 0){				node = node->parent;				goto Found;			}			if(strcmp(elems[i], ".flush.ftpfs") == 0){				/* hack to flush the cache */				invalidate(node);				readdir(node);				goto Found;			}			/* some top level names are special */			if((os == Tops || os == VM || os == VMS) && node == remroot){				np = newtopsdir(elems[i]);				if(np){					node = np;					goto Found;				} else {					err = nosuchfile;					break;				}			}			/* everything else */			node = extendpath(node, s_copy(elems[i]));			if(ISCACHED(node->parent)){				/* the cache of the parent is good, believe it */				if(!ISVALID(node)){					err = nosuchfile;					break;				}				if(node->parent->chdirunknown || (node->d->mode & DMSYML))					fixsymbolic(node);			} else if(!ISVALID(node)){				/* this isn't a valid node, try cd'ing */				if(changedir(node) == 0){					node->d->qid.type = QTDIR;					node->d->mode |= DMDIR;				}else{					node->d->qid.type = QTFILE;					node->d->mode &= ~DMDIR;				}			}			goto Found;		}		if(i == 0 && err == 0)			err = "file does not exist";	}	/* clunk a newly cloned fid if the walk didn't succeed */	if(nf != nil && (err != nil || rhdr.nwqid<nelems)){		nf->busy = 0;		nf->fid = 0;	}	/* if it all worked, point the fid to the enw node */	if(err == nil)		f->node = node;	return err;}char *ropen(Fid *f){	int mode;	mode = thdr.mode;	if(f->node->d->qid.type & QTDIR)		if(mode != OREAD)			return "permission denied";	if(mode & OTRUNC){		uncache(f->node);		uncache(f->node->parent);		filedirty(f->node);	} else {		/* read the remote file or directory */		if(!ISCACHED(f->node)){			filefree(f->node);			if(f->node->d->qid.type & QTDIR){				invalidate(f->node);				if(readdir(f->node) < 0)					return nosuchfile;			} else {				if(readfile(f->node) < 0)					return errstring;			}			CACHED(f->node);		}	}	rhdr.qid = f->node->d->qid;	f->open = 1;	f->node->opens++;	return 0;}char*rcreate(Fid *f){	char *name;	if((f->node->d->qid.type&QTDIR) == 0)		return "not a directory";	name = thdr.name;	f->node = extendpath(f->node, s_copy(name));	uncache(f->node);	if(thdr.perm & DMDIR){		if(createdir(f->node) < 0)			return "permission denied";	} else		filedirty(f->node);	invalidate(f->node->parent);	uncache(f->node->parent);	rhdr.qid = f->node->d->qid;	f->open = 1;	f->node->opens++;	return 0;}char*rread(Fid *f){	long off;	int n, cnt, rv;	Node *np;	rhdr.count = 0;	off = thdr.offset;	cnt = thdr.count;	if(cnt > messagesize-IOHDRSZ)		cnt = messagesize-IOHDRSZ;	if(f->node->d->qid.type & QTDIR){		rv = 0;		for(np = f->node->children; np != nil; np = np->sibs){			if(!ISVALID(np))				continue;			if(off <= BIT16SZ)				break;			n = convD2M(np->d, mbuf, messagesize-IOHDRSZ);			off -= n;		}		for(; rv < cnt && np != nil; np = np->sibs){			if(!ISVALID(np))				continue;			if(np->d->mode & DMSYML)				fixsymbolic(np);			n = convD2M(np->d, mbuf + rv, cnt-rv);			if(n <= BIT16SZ)				break;			rv += n;		}	} else {		/* reread file if it's fallen out of the cache */		if(!ISCACHED(f->node))			if(readfile(f->node) < 0)				return errstring;		CACHED(f->node);		rv = fileread(f->node, (char*)mbuf, off, cnt);		if(rv < 0)			return errstring;	}	rhdr.data = (char*)mbuf;	rhdr.count = rv;	return 0;}char*rwrite(Fid *f){	long off;	int cnt;	if(f->node->d->qid.type & QTDIR)		return "directories are not writable";	rhdr.count = 0;	off = thdr.offset;	cnt = thdr.count;	cnt = filewrite(f->node, thdr.data, off, cnt);	if(cnt < 0)		return errstring;	filedirty(f->node);	rhdr.count = cnt;	return 0;}char *rclunk(Fid *f){	if(fileisdirty(f->node)){		if(createfile(f->node) < 0)			fprint(2, "ftpfs: couldn't create %s\n", f->node->d->name);		fileclean(f->node);		uncache(f->node);	}	if(f->open){		f->open = 0;		f->node->opens--;	}	f->busy = 0;	return 0;}/* *  remove is an implicit clunk */char *rremove(Fid *f){	char *e;	e = nil;	if(QTDIR & f->node->d->qid.type){		if(removedir(f->node) < 0)			e = errstring;	} else {		if(removefile(f->node) < 0)			e = errstring;	}	uncache(f->node->parent);	uncache(f->node);	if(e == nil)		INVALID(f->node);	f->busy = 0;	return e;}char *rstat(Fid *f){	Node *p;	p = f->node->parent;	if(!ISCACHED(p)){		invalidate(p);		readdir(p);		CACHED(p);	}	if(!ISVALID(f->node))		return nosuchfile;	if(p->chdirunknown)		fixsymbolic(f->node);	rhdr.nstat = convD2M(f->node->d, mbuf, messagesize-IOHDRSZ);	rhdr.stat = mbuf;	return 0;}char *rwstat(Fid *f){	USED(f);	return "wstat not implemented";}/* *  print message and die */voidfatal(char *fmt, ...){	va_list arg;	char buf[8*1024];	dying = 1;	va_start(arg, fmt);	vseprint(buf, buf + (sizeof(buf)-1) / sizeof(*buf), fmt, arg);	va_end(arg);	fprint(2, "ftpfs: %s\n", buf);	if(kapid > 0)		postnote(PNGROUP, kapid, "die");	exits(buf);}/* *  like strncpy but make sure there's a terminating null */void*safecpy(void *to, void *from, int n){	char *a = ((char*)to) + n - 1;	strncpy(to, from, n);	*a = 0;	return to;}/* *  set the error string */intseterr(char *fmt, ...){	va_list arg;	va_start(arg, fmt);	vsnprint(errstring, sizeof errstring, fmt, arg);	va_end(arg);	return -1;}/* *  create a new node */Node*newnode(Node *parent, String *name){	Node *np;	static ulong path;	Dir d;	np = mallocz(sizeof(Node), 1);	if(np == 0)		fatal("out of memory");	np->children = 0;	if(parent){		np->parent = parent;		np->sibs = parent->children;		parent->children = np;		np->depth = parent->depth + 1;		d.dev = 0;		/* not stat'd */	} else {		/* the root node */		np->parent = np;		np->sibs = 0;		np->depth = 0;		d.dev = 1;	}	np->remname = name;	d.name = s_to_c(name);	d.atime = time(0);	d.mtime = d.atime;	d.uid = nouid;	d.gid = nouid;	d.muid = nouid;	np->fp = 0;	d.qid.path = ++path;	d.qid.vers = 0;	d.qid.type = QTFILE;	d.type = 0;	np->d = reallocdir(&d, 0);	return np;}/* *  walk one down the local mirror of the remote directory tree */Node*extendpath(Node *parent, String *elem){	Node *np;	for(np = parent->children; np; np = np->sibs)		if(strcmp(s_to_c(np->remname), s_to_c(elem)) == 0){			s_free(elem);			return np;		}	return newnode(parent, elem);}/* *  flush the cached file, write it back if it's dirty */voiduncache(Node *np){	if(fileisdirty(np))		createfile(np);	filefree(np);	UNCACHED(np);}/* *  invalidate all children of a node */voidinvalidate(Node *node){	Node *np;	if(node->opens)		return;		/* don't invalidate something that's open */	uncachedir(node, 0);	/* invalidate children */	for(np = node->children; np; np = np->sibs){		if(np->opens)			continue;	/* don't invalidate something that's open */		UNCACHED(np);		invalidate(np);		np->d->dev = 0;	}}/* *  make a top level tops-20 directory.  They are automaticly valid. */Node*newtopsdir(char *name){	Node *np;	np = extendpath(remroot, s_copy(name));	if(!ISVALID(np)){		np->d->qid.type = QTDIR;		np->d->atime = time(0);		np->d->mtime = np->d->atime;		np->d->uid = "?uid?";		np->d->gid = "?uid?";		np->d->muid = "?uid?";		np->d->mode = DMDIR|0777;		np->d->length = 0;		np->d = reallocdir(np->d, 1);				if(changedir(np) >= 0)			VALID(np);	}	return np;}/* *  figure out if a symbolic link is to a directory or a file */voidfixsymbolic(Node *node){	if(changedir(node) == 0){		node->d->mode |= DMDIR;		node->d->qid.type = QTDIR;	} else		node->d->qid.type = QTFILE;	node->d->mode &= ~DMSYML; }

⌨️ 快捷键说明

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