1292 lines · 26.1 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <fcall.h>#include <thread.h>#include <libsec.h>#include <9p.h>#include "cifs.h" #define max(a,b)	(((a) > (b))? (a): (b))#define min(a,b)	(((a) < (b))? (a): (b)) typedef struct Aux Aux;struct Aux {	Aux	*next;	Aux	*prev;	char	*path;		/* full path fo file */	Share	*sp;		/* this share's info */	long	expire;		/* expiration time of cache */	long	off;		/* file pos of start of cache */	long	end;		/* file pos of end of cache */	char	*cache;	int	fh;		/* file handle */	int	sh;		/* search handle */	long	srch;		/* find first's internal state */}; extern int chatty9p; int Checkcase = 1;		/* enforce case significance on filenames */int Dfstout = 100;		/* timeout (in ms) for ping of dfs servers (assume they are local)  */int Billtrog = 1;		/* enable file owner/group resolution */int Attachpid;			/* pid of proc that attaches (ugh !) */char *Debug = nil;		/* messages */Qid Root;			/* root of remote system */Share Ipc;			/* Share info of IPC$ share */Session *Sess;			/* current session */int Active = IDLE_TIME;		/* secs until next keepalive is sent */static int Keeppid;		/* process ID of keepalive thread */Share Shares[MAX_SHARES]; 	/* table of connected shares */int Nshares = 0;		/* number of Shares connected */Aux *Auxroot = nil;		/* linked list of Aux structs */char *Host = nil;		/* host we are connected to */ static char *Ipcname = "IPC$"; #define ptype(x)	(((x) & 0xf))#define pindex(x)	(((x) & 0xff0) >> 4) voidsetup(void){	int fd;	char buf[32]; 	/*	 * This is revolting but I cannot see any other way to get	 * the pid of the server.  We need this as Windows doesn't	 * drop the TCP connection when it closes a connection.	 * Thus we keepalive() to detect when/if we are thrown off.	 */	Attachpid = getpid(); 	snprint(buf, sizeof buf, "#p/%d/args", getpid());	if((fd = open(buf, OWRITE)) >= 0){		fprint(fd, "%s network", Host);		close(fd);	}} intfiletableinfo(Fmt *f){	Aux *ap;	char *type; 	if((ap = Auxroot) != nil)		do{			type = "walked";			if(ap->sh != -1)				type = "opendir";			if(ap->fh != -1)				type = "openfile";			fmtprint(f, "%-9s %s\n", type, ap->path);			ap = ap->next;		}while(ap != Auxroot);	return 0;} Qidmkqid(char *s, int is_dir, long vers, int subtype, long path){	Qid q;	union {				/* align digest suitably */		uchar	digest[SHA1dlen];		uvlong	uvl;	} u; 	sha1((uchar *)s, strlen(s), u.digest, nil);	q.type = (is_dir)? QTDIR: 0;	q.vers = vers;	if(subtype){		q.path = *((uvlong *)u.digest) & ~0xfffL;		q.path |= ((path & 0xff) << 4);		q.path |= (subtype & 0xf);	}	else		q.path = *((uvlong *)u.digest) & ~0xfL;	return q;} /* * used only for root dir and shares */static voidV2D(Dir *d, Qid qid, char *name){	memset(d, 0, sizeof(Dir));	d->type = 'C';	d->dev = 1;	d->name = estrdup9p(name);	d->uid = estrdup9p("bill");	d->muid = estrdup9p("boyd");	d->gid = estrdup9p("trog");	d->mode = 0755 | DMDIR;	d->atime = time(nil);	d->mtime = d->atime;	d->length = 0;	d->qid = qid;} static voidI2D(Dir *d, Share *sp, char *path, FInfo *fi){	char *name; 	if((name = strrchr(fi->name, '\\')) != nil)		name++;	else		name = fi->name;	d->name = estrdup9p(name);	d->type = 'C';	d->dev = sp->tid;	d->uid = estrdup9p("bill");	d->gid = estrdup9p("trog");	d->muid = estrdup9p("boyd");	d->atime = fi->accessed;	d->mtime = fi->written; 	if(fi->attribs & ATTR_READONLY)		d->mode = 0444;	else		d->mode = 0666; 	d->length = fi->size;	d->qid = mkqid(path, fi->attribs & ATTR_DIRECTORY, fi->changed, 0, 0); 	if(fi->attribs & ATTR_DIRECTORY){		d->length = 0;		d->mode |= DMDIR|0111;	}} static voidresponderrstr(Req *r){	char e[ERRMAX]; 	*e = 0;	rerrstr(e, sizeof e);	respond(r, e);} static char *newpath(char *path, char *name){	char *p, *q; 	assert((p = strrchr(path, '/')) != nil); 	if(strcmp(name, "..") == 0){		if(p == path)			return estrdup9p("/");		q = emalloc9p((p-path)+1);		strecpy(q, q+(p-path)+1, path);		return q;	}	if(strcmp(path, "/") == 0)		return smprint("/%s", name);	return smprint("%s/%s", path, name);} static intdirgen(int slot, Dir *d, void *aux){	long off;	FInfo *fi;	int rc, got;	Aux *a = aux;	char *npath;	int numinf = numinfo();	int slots = min(Sess->mtu, MTU) / sizeof(FInfo); 	if(strcmp(a->path, "/") == 0){		if(slot < numinf){			dirgeninfo(slot, d);			return 0;		} else			slot -= numinf; 		if(slot >= Nshares)			return -1;		V2D(d, mkqid(Shares[slot].name, 1, 1, Pshare, slot),			Shares[slot].name);		return 0;	} 	off = slot * sizeof(FInfo);	if(off >= a->off && off < a->end && time(nil) < a->expire)		goto from_cache; 	if(off == 0){		fi = (FInfo *)a->cache;		npath = smprint("%s/*", mapfile(a->path));		a->sh = T2findfirst(Sess, a->sp, slots, npath, &got, &a->srch,			(FInfo *)a->cache);		free(npath);		if(a->sh == -1)			return -1; 		a->off = 0;		a->end = got * sizeof(FInfo); 		if(got >= 2 && strcmp(fi[0].name, ".") == 0 &&		    strcmp(fi[1].name, "..") == 0){			a->end = (got - 2) * sizeof(FInfo);			memmove(a->cache, a->cache + sizeof(FInfo)*2,				a->end - a->off);		}	} 	while(off >= a->end && a->sh != -1){		fi = (FInfo *)(a->cache + (a->end - a->off) - sizeof(FInfo));		a->off = a->end;		npath = smprint("%s/%s", mapfile(a->path), fi->name);		rc = T2findnext(Sess, a->sp, slots, npath,			&got, &a->srch, (FInfo *)a->cache, a->sh);		free(npath);		if(rc == -1 || got == 0)			break;		a->end = a->off + got * sizeof(FInfo);	}	a->expire = time(nil) + CACHETIME; 	if(got < slots){		if(a->sh != -1)			CIFSfindclose2(Sess, a->sp, a->sh);		a->sh = -1;	} 	if(off >= a->end)		return -1; from_cache:	fi = (FInfo *)(a->cache + (off - a->off));	npath = smprint("%s/%s", mapfile(a->path), fi->name);	I2D(d, a->sp, npath, fi);	if(Billtrog == 0)		upd_names(Sess, a->sp, npath, d);	free(npath);	return 0;} static voidfsattach(Req *r){	Aux *a;	static int first = 1;	char *spec = r->ifcall.aname; 	if(first)		setup(); 	if(spec && *spec){		respond(r, "invalid attach specifier");		return;	} 	r->ofcall.qid = mkqid("/", 1, 1, Proot, 0);	r->fid->qid = r->ofcall.qid; 	a = r->fid->aux = emalloc9p(sizeof(Aux));	memset(a, 0, sizeof(Aux));	a->path = estrdup9p("/");	a->sp = nil;	a->fh = -1;	a->sh = -1; 	if(Auxroot){		a->prev = Auxroot;		a->next = Auxroot->next;		Auxroot->next->prev = a;		Auxroot->next = a;	} else {		Auxroot = a;		a->next = a;		a->prev = a;	}	respond(r, nil);} static char*fsclone(Fid *ofid, Fid *fid){	Aux *oa = ofid->aux;	Aux *a = emalloc9p(sizeof(Aux)); 	fid->aux = a; 	memset(a, 0, sizeof(Aux));	a->sh = -1;	a->fh = -1;	a->sp = oa->sp;	a->path = estrdup9p(oa->path); 	if(Auxroot){		a->prev = Auxroot;		a->next = Auxroot->next;		Auxroot->next->prev = a;		Auxroot->next = a;	} else {		Auxroot = a;		a->next = a;		a->prev = a;	}	return nil;} /* * for some weird reason T2queryall() returns share names * in lower case so we have to do an extra test against * our share table to validate filename case. * * on top of this here (snell & Wilcox) most of our * redirections point to a share of the same name, * but some do not, thus the tail of the filename * returned by T2queryall() is not the same as * the name we wanted. * * We work around this by not validating the names * or files which resolve to share names as they must * be correct, having been enforced in the dfs layer. */static intvalidfile(char *found, char *want, char *winpath, Share *sp){	char *share; 	if(strcmp(want, "..") == 0)		return 1;	if(strcmp(winpath, "/") == 0){		share = trimshare(sp->name);		if(cistrcmp(want, share) == 0)			return strcmp(want, share) == 0;		/*		 * OK, a DFS redirection points us from a directory XXX		 * to a share named YYY.  There is no case checking we can		 * do so we allow either case - it's all we can do.		 */		return 1;	}	if(cistrcmp(found, want) != 0)		return 0;	if(!Checkcase)		return 1;	if(strcmp(found, want) == 0)		return 1;	return 0;}  static char*fswalk1(Fid *fid, char *name, Qid *qid){	FInfo fi;	int rc, n, i;	Aux *a = fid->aux;	static char e[ERRMAX];	char *p, *npath, *winpath; 	*e = 0;	npath = newpath(a->path, name);	if(strcmp(npath, "/") == 0){			/* root dir */		*qid = mkqid("/", 1, 1, Proot, 0);		free(a->path);		a->path = npath;		fid->qid = *qid;		return nil;	} 	if(strrchr(npath, '/') == npath){		/* top level dir */		if((n = walkinfo(name)) != -1){		/* info file */			*qid = mkqid(npath, 0, 1, Pinfo, n);		}		else {					/* volume name */			for(i = 0; i < Nshares; i++){				n = strlen(Shares[i].name);				if(cistrncmp(npath+1, Shares[i].name, n) != 0)					continue;				if(Checkcase && strncmp(npath+1, Shares[i].name, n) != 0)					continue;				if(npath[n+1] != 0 && npath[n+1] != '/')					continue;				break;			}			if(i >= Nshares){				free(npath);				return "not found";			}			a->sp = Shares+i;			*qid = mkqid(npath, 1, 1, Pshare, i);		}		free(a->path);		a->path = npath;		fid->qid = *qid;		return nil;	} 	/* must be a vanilla file or directory */again:	if(mapshare(npath, &a->sp) == -1){		rerrstr(e, sizeof(e));		free(npath);		return e;	} 	winpath = mapfile(npath);	memset(&fi, 0, sizeof fi);	if(Sess->caps & CAP_NT_SMBS)		rc = T2queryall(Sess, a->sp, winpath, &fi);	else		rc = T2querystandard(Sess, a->sp, winpath, &fi); 	if(rc == -1){		rerrstr(e, sizeof(e));		free(npath);		return e;	} 	if((a->sp->options & SMB_SHARE_IS_IN_DFS) != 0 &&	    (fi.attribs & ATTR_REPARSE) != 0){		if(redirect(Sess, a->sp, npath) != -1)			goto again;	} 	if((p = strrchr(fi.name, '/')) == nil && (p = strrchr(fi.name, '\\')) == nil)		p = fi.name;	else		p++; 	if(! validfile(p, name, winpath, a->sp)){		free(npath);		return "not found"; 	}	*qid = mkqid(npath, fi.attribs & ATTR_DIRECTORY, fi.changed, 0, 0); 	free(a->path);	a->path = npath;	fid->qid = *qid;	return nil;} static voidfsstat(Req *r){	int rc;	FInfo fi;	Aux *a = r->fid->aux; 	if(ptype(r->fid->qid.path) == Proot)		V2D(&r->d, r->fid->qid, "");	else if(ptype(r->fid->qid.path) == Pinfo)		dirgeninfo(pindex(r->fid->qid.path), &r->d);	else if(ptype(r->fid->qid.path) == Pshare)		V2D(&r->d, r->fid->qid, a->path +1);	else{		memset(&fi, 0, sizeof fi);		if(Sess->caps & CAP_NT_SMBS)			rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi);		else			rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi);

Lines 1–500 of 1292.