942 lines · 14.8 KB · C
RawDownload
#include	"u.h"#include	<stdio.h> /* for remove, rename */#include	<pwd.h>#include	<grp.h>	/* going to regret this - getgrgid is a stack smasher */#include	<sys/socket.h>#include	<sys/un.h> #if defined(__FreeBSD__)#include	<sys/disk.h>#include	<sys/disklabel.h>#include	<sys/ioctl.h>#endif #if defined(__APPLE__)#include	<sys/disk.h>#endif #if defined(__linux__)#include	<linux/hdreg.h>#include	<linux/fs.h>#include	<sys/ioctl.h>#endif #include	"lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"error.h" enum{	Trace = 0,	FsChar = 'Z',}; extern char *canopen;extern Path *addelem(Path*, char*, Chan*);static char *uidtoname(int);static char *gidtoname(int);static int nametouid(char*);static int nametogid(char*); static vlong disksize(int, struct stat*); typedef struct UnixFd UnixFd;struct UnixFd{	int	fd;	int	issocket;	DIR*	dir;	vlong	diroffset;	QLock	dirlock;	struct dirent *nextde;	Path	*path;}; voidoserrstr(void){	if (errno == EINTR)		kstrcpy(up->errstr, Eintr, ERRMAX);	else		kstrcpy(up->errstr, strerror(errno), ERRMAX);} voidoserror(void){	if (errno == EINTR)		error(Eintr);	else		error(strerror(errno));} static Qidfsqid(struct stat *st){	Qid q;	int dev;	static int nqdev;	static uchar *qdev; 	if(qdev == 0)		qdev = smalloc(65536U); 	q.type = 0;	if((st->st_mode&S_IFMT) ==  S_IFDIR)		q.type = QTDIR; 	dev = st->st_dev & 0xFFFFUL;	if(qdev[dev] == 0)		qdev[dev] = ++nqdev;		q.path = (vlong)qdev[dev]<<48;	q.path ^= st->st_ino;	q.vers = st->st_mtime;		return q;} static Chan*fsattach(char *spec){	struct stat st;	Chan *c;	UnixFd *ufd;	int dev;		dev = 1;	if(spec && spec[0]){		snprint(up->genbuf, sizeof up->genbuf, "no file system #%C%s", FsChar, spec);		error(up->genbuf);	} 	if(stat("/", &st) < 0)		oserror(); 	c = devattach(FsChar, 0);	ufd = mallocz(sizeof(UnixFd), 1);	ufd->path = newpath("/");	ufd->fd = -1; 	c->aux = ufd;	c->dev = dev;	c->qid = fsqid(&st);		if(Trace)		print("fsattach /\n"); 	return c;} static Chan*fsclone(Chan *c, Chan *nc){	UnixFd *ufd;		ufd = mallocz(sizeof(UnixFd), 1);	*ufd = *(UnixFd*)c->aux;	if(ufd->path)		incref(&ufd->path->ref);	ufd->fd = -1;	nc->aux = ufd;	return nc;} static char*lastelem(char *s){	char *t; 	if(s[0] == '/' && s[1] == 0)		return s;	t = strrchr(s, '/');	if(t == nil)		return s;	return t+1;} static char*fspath(Chan *c, char *suffix){	char *s, *t;	int len;	UnixFd *ufd;		ufd = c->aux;	s = ufd->path->s;	len = strlen(s)+1;	if(suffix)		len += 1+strlen(suffix);	t = smalloc(len);	strcpy(t, s);	if(suffix){		if(s[strlen(s)-1] != '/')			strcat(t, "/");		strcat(t, suffix);	}	return t;} static intfswalk1(Chan *c, char *name){	struct stat st;	char *path;	UnixFd *ufd;		ufd = c->aux;	if(strcmp(name, "..") == 0 && strcmp(ufd->path->s, "/") == 0)		return 0;		path = fspath(c, name);	if(stat(path, &st) < 0){		if(Trace)			print("fswalk1 %s (%s)\n", path, strerror(errno));			free(path);		return -1;	}	if(Trace)		print("fswalk1 %s\n", path);		free(path);		c->qid = fsqid(&st);	return 0;} static voidreplacepath(Chan *c, Path *p){	UnixFd *ufd;		ufd = c->aux;	incref(&p->ref);	pathclose(ufd->path);	ufd->path = p;} static Walkqid*fswalk(Chan *c, Chan *nc, char **name, int nname){	int i;	Path *path;	Walkqid *wq;	UnixFd *ufd; 	if(nc != nil)		panic("fswalk: nc != nil");	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));	nc = devclone(c);	fsclone(c, nc);	ufd = c->aux;	path = ufd->path;	incref(&path->ref); 	wq->clone = nc;	for(i=0; i<nname; i++){		ufd = nc->aux;		replacepath(nc, path);		if(fswalk1(nc, name[i]) < 0){			if(i == 0){				pathclose(path);				cclose(nc);				free(wq);				error(Enonexist);			}			break;		}		path = addelem(path, name[i], nil);		wq->qid[i] = nc->qid;	}	replacepath(nc, path);	pathclose(path);	if(i != nname){		cclose(nc);		wq->clone = nil;	}	wq->nqid = i;	return wq;} static intfsdirstat(char *path, int dev, Dir *d){	int fd;	struct stat st;		if(stat(path, &st) < 0 && lstat(path, &st) < 0)		return -1; 	d->name = lastelem(path);	d->uid = uidtoname(st.st_uid);	d->gid = gidtoname(st.st_gid);	d->muid = "";	d->qid = fsqid(&st);	d->mode = (d->qid.type<<24) | (st.st_mode&0777);	d->atime = st.st_atime;	d->mtime = st.st_mtime;	d->length = st.st_size;	if(S_ISBLK(st.st_mode) && (fd = open(path, O_RDONLY)) >= 0){		d->length = disksize(fd, &st);		close(fd);	}		// devmnt leaves 1-9 unused so that we can steal them.	// it is easier for all involved if #Z shows M as the file type instead of Z.	// dev is c->dev, either 1 (#Z) or 2 (#Zplan9).	d->type = 'M';	d->dev = dev;	return 0;} static intfsstat(Chan *c, uchar *buf, int n){	Dir d;	char *path;	UnixFd *ufd; 	ufd = c->aux;	if(Trace)		print("fsstat %s\n", ufd->path->s); 	if(n < BIT16SZ)		error(Eshortstat); 	path = fspath(c, nil);	if(fsdirstat(path, c->dev, &d) < 0){		free(path);		oserror();	}	if(strcmp(ufd->path->s, "/") == 0)		d.name = "/";	n = convD2M(&d, buf, n);	free(path);	return n;} static intopensocket(UnixFd *ufd, char *path){	int fd;	struct stat st;	struct sockaddr_un su;		if(stat(path, &st) < 0)		return -1;	if(!S_ISSOCK(st.st_mode))		return -1;	memset(&su, 0, sizeof su);	su.sun_family = AF_UNIX;	if(strlen(path)+1 > sizeof su.sun_path){		errno = ENAMETOOLONG;		return -1;	}	strcpy(su.sun_path, path);	if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)		return -1;	if(connect(fd, (struct sockaddr*)&su, sizeof su) < 0){		close(fd);		return -1;	}	ufd->fd = fd;	ufd->issocket = 1;	return 0;}		 static Chan*fsopen(Chan *c, int mode){	char *path;	int m;	UnixFd *ufd;		ufd = c->aux;	if(Trace)		print("fsopen %s %#x\n", ufd->path->s, mode); 	/* protect files whose path does not begin with allowed */	if(strncmp(ufd->path->s, canopen, strlen(canopen)) != 0)		error(Eperm); 	if(mode & ~(OTRUNC|ORCLOSE|3))		error(Ebadarg); 	if((c->qid.type & QTDIR) && mode != OREAD)		error(Eperm);		if((c->qid.type&QTDIR) && mode != OREAD)		error(Eperm); 	c->mode = openmode(mode);	path = fspath(c, nil);	if(c->qid.type & QTDIR){		ufd->dir = opendir(path);		if(ufd->dir == nil){			free(path);			oserror();		}		ufd->diroffset = 0;		ufd->nextde = nil;	}else{		m = mode & 3;		if(m == OEXEC)			m = OREAD;		if(mode & OTRUNC)			m |= O_TRUNC;		if((ufd->fd = open(path, m)) < 0 && opensocket(ufd, path) < 0){			free(path);			oserror();		}	}	free(path);	c->flag |= COPEN;	return c;} static voidfscreate(Chan *c, char *name, int mode, ulong perm){	char *path, *path0;	int fd, mm;	UnixFd *ufd;	struct stat st; 	ufd = c->aux;	if(Trace)		print("fscreate %s %#x %#o\n", ufd->path->s, mode, perm); 	if(!(c->qid.type & QTDIR))		error(Enotdir); 	if(mode & ~(OTRUNC|ORCLOSE|3))		error(Ebadarg); 	if(perm & ~(DMDIR|0777))		error(Ebadarg); 	path0 = fspath(c, nil);	path = fspath(c, name);	if(waserror()){		free(path);		free(path0);		nexterror();	}		if(stat(path0, &st) < 0)		oserror(); 	if(perm & DMDIR){		if(mode != OREAD)			error(Eperm);		/* have to do the minimum 0400 so we can open it */		if(mkdir(path, (0400 | perm) & 0777) < 0)			oserror();		if((fd = open(path, 0)) < 0)			oserror();		fchown(fd, -1, st.st_gid);		if(fstat(fd, &st) < 0){			close(fd);			oserror();		}		if((ufd->dir = opendir(path)) == nil) {			/* arguably we should set the mode here too  			 * but it's hard to see that this case  			 * will ever happen  			 */			close(fd);			oserror();		}		// Be like Plan 9 file servers: inherit mode bits 		// and group from parent.		fchmod(fd, perm & st.st_mode & 0777);		close(fd);		ufd->diroffset = 0;		ufd->nextde = nil;	}else{		mm = mode & 3;		if(mode & OTRUNC)			mm |= O_TRUNC;		if((fd = open(path, mm|O_CREAT|O_EXCL, 0666)) < 0)			oserror();		// Be like Plan 9 file servers: inherit mode bits 		// and group from parent.		fchmod(fd, perm & st.st_mode & 0777);		fchown(fd, -1, st.st_gid);		if(fstat(fd, &st) < 0){			close(fd);			oserror();		}		ufd->fd = fd;	}	free(path);	free(path0);	poperror();		ufd->path = addelem(ufd->path, name, nil);	c->qid = fsqid(&st);	c->offset = 0;	c->flag |= COPEN;	c->mode = openmode(mode);} static voidfsclose(Chan *c){	UnixFd *ufd;	char *path;		ufd = c->aux;	if(Trace)		print("fsclose %s\n", ufd->path->s); 	if(c->flag & COPEN) {		if(c->flag & CRCLOSE) {			path = fspath(c, nil);			unlink(path);			free(path);		}		if(c->qid.type & QTDIR)

Lines 1–500 of 942.