xref: /plan9/sys/src/cmd/unix/u9fs/u9fs.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include "u.h"
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include "libc.h"
5 #include "9p.h"
6 #include "stdio.h"
7 #include "setjmp.h"
8 #include "pwd.h"
9 #include "grp.h"
10 
11 #define	DBG(f)
12 
13 struct group *getgrent(void);
14 struct passwd *getpwent(void);
15 
16 #ifdef SYSV
17 #	include <dirent.h>
18 #	define	DTYPE	struct dirent
19 #	define SOCKETS
20 #endif
21 #ifdef V10
22 #	include <ndir.h>
23 #	define	DTYPE	struct direct
24 #endif
25 #ifdef BSD
26 #	include <sys/dir.h>
27 #	define	DTYPE	struct direct
28 #	define	SOCKETS
29 #endif
30 #ifdef SOCKETS
31 #	define sendmsg	__sendmsg
32 #	include <sys/socket.h>
33 #	include <netinet/in.h>
34 #	include <netdb.h>
35 #	undef sendmsg
36 	char	bsdhost[256];
37 	void	remotehostname(void);
38 #endif
39 
40 typedef struct File	File;
41 typedef struct Rfile	Rfile;
42 typedef struct Fd	Fd;
43 typedef struct Pass	Pass;
44 
45 struct Fd{
46 	int		ref;
47 	Ulong		offset;
48 	int		fd;
49 	DIR		*dir;
50 };
51 
52 struct Rfile{
53 	int		busy;
54 	int		uid;
55 	int		gid;
56 	int		rclose;
57 	File		*file;
58 	Fd		*fd;
59 };
60 
61 struct File{
62 	int		ref;
63 	char		*path;
64 	char		*name;
65 	Qid		qid;
66 	struct stat	stbuf;
67 };
68 
69 struct Pass{
70 	int		id;
71 	int		gid;
72 	char		*name;
73 	Pass		*next;
74 };
75 
76 char	data[2][MAXMSG+MAXFDATA];
77 char	tdata[MAXMSG+MAXFDATA];
78 char	rdata[MAXFDATA];
79 Fcall	rhdr;
80 Fcall	thdr;
81 Rfile	*rfile;
82 File	*file0;
83 int	nrfilealloc;
84 jmp_buf	loopjmp;
85 Pass*	uid[256];
86 Pass*	gid[256];
87 int	devallowed;
88 int	connected;
89 
90 void	io(void);
91 void	error(char*);
92 char	*mfmt(Fcall*);
93 void	sendmsg(char*);
94 int	okfid(int);
95 Rfile*	rfilefid(void);
96 File*	newfile(void);
97 void*	erealloc(void*, unsigned);
98 char*	estrdup(char*);
99 char*	dostat(File*, char*);
100 char*	bldpath(char*, char*, char*);
101 Ulong	qid(struct stat*);
102 Ulong	vers(struct stat*);
103 void	errjmp(char*);
104 int	omode(int);
105 char*	id2name(Pass**, int);
106 Pass*	name2pass(Pass**, char*);
107 void	getpwdf(void);
108 void	getgrpf(void);
109 void	perm(Rfile*, int, struct stat*);
110 void	parentwrperm(Rfile*);
111 
112 void	rsession(void);
113 void	rattach(void);
114 void	rflush(void);
115 void	rclone(void);
116 void	rwalk(void);
117 void	ropen(void);
118 void	rcreate(void);
119 void	rread(void);
120 void	rwrite(void);
121 void	rclunk(int);
122 void	rstat(void);
123 void	rwstat(void);
124 void	rclwalk(void);
125 
126 char	Eauth[] =	"authentication failed";
127 char	Eperm[] =	"permission denied";
128 char	Ebadfid[] =	"fid unknown or out of range";
129 char	Efidactive[] =	"fid already in use";
130 char	Eopen[] =	"file is open";
131 char	Emode[] =	"invalid open mode";
132 char	Especial[] =	"no access to special file";
133 char	Especial0[] =	"already attached without access to special files";
134 char	Especial1[] =	"already attached with access to special files";
135 char	Enotopen[] =	"file is not open";
136 char	Etoolarge[] =	"i/o count too large";
137 char	Ebaddir[] =	"i/o error on directory";
138 char	Eunknown[] =	"unknown user or group";
139 char	Euid[] =	"can't set uid";
140 char	Egid[] =	"can't set gid";
141 char	Eowner[] =	"not owner";
142 
143 int
144 main(int argc, char *argv[])
145 {
146 	freopen(LOG, "a", stderr);
147 	setbuf(stderr, (void*)0);
148 	DBG(fprintf(stderr, "u9fs\nkill %d\n", getpid()));
149 	if(argc > 1)
150 		if(chroot(argv[1]) == -1)
151 			error("chroot failed");
152 
153 #	ifdef SOCKETS
154 	remotehostname();
155 #	endif
156 
157 	io();
158 	return 0;
159 }
160 
161 void
162 io(void)
163 {
164 	int m;
165 	static int toggle, ndata;
166 	char *datap;
167 
168 	/*
169 	 * TCP does not preserve record boundaries; this dance works around
170 	 * the problem.
171 	 */
172 	setjmp(loopjmp);
173 
174 	/*
175 	 * Invariant: data[toggle] has ndata bytes already
176 	 */
177     loop:
178 	datap = data[toggle];
179 	toggle ^= 1;
180 	for(;;){
181 		if(ndata){
182 			m = convM2S(datap, &rhdr, ndata);
183 			/* m is number of bytes more than a full message */
184 			if(m >= 0){
185 				memmove(data[toggle], datap+(ndata-m), m);
186 				ndata = m;
187 				break;
188 			}
189 		}
190 		m = read(0, datap+ndata, (MAXMSG+MAXFDATA)-ndata);
191 		if(m <= 0)
192 			error("read");
193 		ndata += m;
194 	}
195 
196 	thdr.type = rhdr.type+1;
197 	thdr.tag = rhdr.tag;
198 	thdr.fid = rhdr.fid;
199 	DBG(fprintf(stderr, ">> %s\n", mfmt(&rhdr)));
200 	switch(rhdr.type){
201 	case Tnop:
202 	case Tflush:	/* this is a synchronous fs; easy */
203 		break;
204 	case Tsession:
205 		rsession();
206 		break;
207 	case Tattach:
208 		rattach();
209 		break;
210 	case Tclone:
211 		rclone();
212 		break;
213 	case Twalk:
214 		rwalk();
215 		break;
216 	case Tstat:
217 		rstat();
218 		break;
219 	case Twstat:
220 		rwstat();
221 		break;
222 	case Topen:
223 		ropen();
224 		break;
225 	case Tcreate:
226 		rcreate();
227 		break;
228 	case Tread:
229 		rread();
230 		break;
231 	case Twrite:
232 		rwrite();
233 		break;
234 	case Tclunk:
235 		rclunk(0);
236 		break;
237 	case Tremove:
238 		rclunk(1);
239 		break;
240 	default:
241 		fprintf(stderr, "unknown message %s\n", mfmt(&rhdr));
242 		error("bad message");
243 	}
244 	sendmsg(0);
245 	goto loop;
246 }
247 
248 void
249 rsession(void)
250 {
251 	memset(thdr.authid, 0, sizeof(thdr.authid));
252 	memset(thdr.authdom, 0, sizeof(thdr.authdom));
253 	memset(thdr.chal, 0, sizeof(thdr.chal));
254 }
255 
256 void
257 rattach(void)
258 {
259 	Rfile *rf;
260 	char *err;
261 	Pass *p;
262 
263 	err = 0;
264 	if(file0 == 0){
265 		file0 = newfile();
266 		file0->ref++;		/* one extra to hold it up */
267 		file0->path = estrdup("/");
268 		file0->name = estrdup("/");
269 		errjmp(dostat(file0, 0));
270 	}
271 	if(!okfid(rhdr.fid))
272 		errjmp(Ebadfid);
273 	if(strncmp(rhdr.aname, "device", 6) == 0){
274 		if(connected && !devallowed)
275 			errjmp(Especial0);
276 		devallowed = 1;
277 	}else{
278 		if(connected && devallowed)
279 			errjmp(Especial1);
280 	}
281 	getpwdf();
282 	getgrpf();
283 	rf = &rfile[rhdr.fid];
284 	if(rf->busy)
285 		errjmp(Efidactive);
286 	p = name2pass(uid, rhdr.uname);
287 	if(p == 0)
288 		errjmp(Eunknown);
289 	if(p->id == 0)
290 		errjmp(Eperm);
291 #	ifdef SOCKETS
292 	if(ruserok(bsdhost, 0, rhdr.uname, rhdr.uname) < 0)
293 		errjmp(Eperm);
294 #	endif
295 	/* mark busy & inc ref cnt only after committed to succeed */
296 	rf->busy = 1;
297 	rf->file = file0;
298 	file0->ref++;
299 	rf->rclose = 0;
300 	rf->uid = p->id;
301 	rf->gid = p->gid;
302 	thdr.qid = file0->qid;
303 	connected = 1;
304 }
305 
306 void
307 rclone(void)
308 {
309 	Rfile *rf, *nrf;
310 	File *f;
311 
312 	rfilefid();
313 	if(!okfid(rhdr.newfid))
314 		errjmp(Ebadfid);
315 	rf = &rfile[rhdr.fid];
316 	nrf = &rfile[rhdr.newfid];
317 	f = rf->file;
318 	if(nrf->busy)
319 		errjmp(Efidactive);
320 	nrf->busy = 1;
321 	nrf->file = f;
322 	f->ref++;
323 	nrf->fd = rf->fd;
324 	nrf->uid = rf->uid;
325 	nrf->gid = rf->gid;
326 	nrf->rclose = rf->rclose;
327 	if(nrf->fd){
328 		if(nrf->fd->ref == 0)
329 			error("clone fd count");
330 		nrf->fd->ref++;
331 	}
332 }
333 
334 void
335 rwalk(void)
336 {
337 	char *err;
338 	Rfile *rf;
339 	File *of, *f;
340 
341 	rf = rfilefid();
342 	if(rf->fd)
343 		errjmp(Eopen);
344 	of = rf->file;
345 	perm(rf, 1, 0);
346 	f = newfile();
347 	f->path = estrdup(of->path);
348 	err = dostat(f, rhdr.name);
349 	if(err){
350 		f->ref = 0;
351 		free(f->path);
352 		errjmp(err);
353 	}
354 	if(of->ref <= 0)
355 		error("walk ref count");
356 	if(--of->ref == 0){
357 		free(of->path);
358 		free(of->name);
359 		free(of);
360 	}
361 	rf->file = f;
362 	thdr.qid = f->qid;
363 }
364 
365 void
366 ropen(void)
367 {
368 	Rfile *rf;
369 	File *f;
370 	int fd;
371 	DIR *dir;
372 	int m, trunc;
373 
374 	rf = rfilefid();
375 	f = rf->file;
376 	if(rf->fd)
377 		error("open already open");
378 	if(!devallowed && (f->stbuf.st_mode & S_IFCHR))
379 		errjmp(Especial);
380 	m = rhdr.mode & (16|3);
381 	trunc = m & 16;	/* OTRUNC */
382 	switch(m){
383 	case 0:
384 		perm(rf, 4, 0);
385 		break;
386 	case 1:
387 	case 1|16:
388 		perm(rf, 2, 0);
389 		break;
390 	case 2:
391 	case 0|16:
392 	case 2|16:
393 		perm(rf, 4, 0);
394 		perm(rf, 2, 0);
395 		break;
396 	case 3:
397 		perm(rf, 1, 0);
398 		break;
399 	default:
400 		errjmp(Emode);
401 	}
402 
403 	m = omode(m & 3);
404 	errno = 0;
405 	if(f->qid.path & CHDIR){
406 		if(rhdr.mode != 0)		/* OREAD */
407 			errjmp(Eperm);
408 		dir = opendir(f->path);
409 		if(dir == 0)
410 			errjmp(sys_errlist[errno]);
411 		fd = 0;
412 	}else{
413 		if(trunc){
414 			fd = creat(f->path, 0666);
415 			if(fd >= 0)
416 				if(m != 1){
417 					close(fd);
418 					fd = open(f->path, m);
419 				}
420 		}else
421 			fd = open(f->path, m);
422 		if(fd < 0)
423 			errjmp(sys_errlist[errno]);
424 		dir = 0;
425 	}
426 	rf->rclose = rhdr.mode & 64;	/* ORCLOSE */
427 	rf->fd = erealloc(0, sizeof(Fd));
428 	rf->fd->ref = 1;
429 	rf->fd->fd = fd;
430 	rf->fd->dir = dir;
431 	rf->fd->offset = 0;
432 	thdr.qid = f->qid;
433 }
434 
435 void
436 rcreate(void)
437 {
438 	Rfile *rf;
439 	File *f, *of;
440 	char *path, *err;
441 	int fd;
442 	int m;
443 	char name[NAMELEN];
444 
445 	rf = rfilefid();
446 	if(rf->fd)
447 		errjmp(Eopen);
448 	perm(rf, 2, 0);
449 	path = bldpath(rf->file->path, rhdr.name, name);
450 	m = omode(rhdr.mode&3);
451 	errno = 0;
452 	if(rhdr.perm & CHDIR){
453 		if(m){
454 			free(path);
455 			errjmp(Eperm);
456 		}
457 		fd = mkdir(path, 0777);
458 		if(fd < 0){
459 			free(path);
460 			errjmp(sys_errlist[errno]);
461 		}
462 		fd = open(path, 0);
463 		free(path);
464 		if(fd >= 0){
465 			fchmod(fd, rhdr.perm&0777);
466 			fchown(fd, rf->uid, rf->gid);
467 		}
468 	}else{
469 		fd = creat(path, 0666);
470 		if(fd >= 0){
471 			if(m != 1){
472 				close(fd);
473 				fd = open(path, m);
474 			}
475 			fchmod(fd, rhdr.perm&0777);
476 			fchown(fd, rf->uid, rf->gid);
477 		}
478 		free(path);
479 		if(fd < 0)
480 			errjmp(sys_errlist[errno]);
481 	}
482 	f = newfile();
483 	of = rf->file;
484 	f->path = estrdup(of->path);
485 	err = dostat(f, rhdr.name);
486 	if(err){
487 		free(f->path);
488 		free(f->name);
489 		free(f);
490 		errjmp(err);
491 	}
492 	if(!devallowed && (f->stbuf.st_mode & S_IFCHR)){
493 		free(f->path);
494 		free(f->name);
495 		free(f);
496 		errjmp(Especial);
497 	}
498 	if(--of->ref == 0){
499 		free(of->path);
500 		free(of->name);
501 		free(of);
502 	}
503 	rf->file = f;
504 	rf->rclose = rhdr.mode & 64;	/* ORCLOSE */
505 	rf->fd = erealloc(0, sizeof(Fd));
506 	rf->fd->ref = 1;
507 	rf->fd->fd = fd;
508 	rf->fd->dir = 0;
509 	rf->fd->offset = 0;
510 	thdr.qid = f->qid;
511 }
512 
513 void
514 rread(void)
515 {
516 	Rfile *rf;
517 	File *f;
518 	long n;
519 	DTYPE *de;
520 	Dir d;
521 	struct stat stbuf;
522 	char *path;
523 
524 	rf = rfilefid();
525 	if(rf->fd == 0)
526 		errjmp(Enotopen);
527 	if(rhdr.count > sizeof rdata)
528 		errjmp(Etoolarge);
529 	f = rf->file;
530 	if(rf->fd->dir){
531 		errno = 0;
532 		rhdr.count = (rhdr.count/DIRLEN)*DIRLEN;
533 		if(rf->fd->offset != rhdr.offset){
534 			rf->fd->offset = rhdr.offset;  /* sync offset */
535 			seekdir(rf->fd->dir, 0);
536 			for(n=0; n<rhdr.offset; ){
537 				de = readdir(rf->fd->dir);
538 				if(de == 0)
539 					break;
540 				if(de->d_ino==0 || de->d_name[0]==0)
541 					continue;
542 				if(strcmp(de->d_name, ".")==0 || strcmp(de->d_name, "..")==0)
543 					continue;
544 				n += DIRLEN;
545 			}
546 		}
547 		for(n=0; n<rhdr.count; ){
548 			de = readdir(rf->fd->dir);
549 			if(de == 0)
550 				break;
551 			if(de->d_ino==0 || de->d_name[0]==0)
552 				continue;
553 			if(strcmp(de->d_name, ".")==0 || strcmp(de->d_name, "..")==0)
554 				continue;
555 			strncpy(d.name, de->d_name, NAMELEN-1);
556 			d.name[NAMELEN-1] = 0;
557 			path = erealloc(0, strlen(f->path)+1+strlen(de->d_name)+1);
558 			sprintf(path, "%s/%s", f->path, de->d_name);
559 			memset(&stbuf, 0, sizeof stbuf);
560 			if(stat(path, &stbuf) < 0){
561 				fprintf(stderr, "dir: bad path %s\n", path);
562 				/* but continue... probably a bad symlink */
563 			}
564 			free(path);
565 			strncpy(d.uid, id2name(uid, stbuf.st_uid), NAMELEN);
566 			strncpy(d.gid, id2name(gid, stbuf.st_gid), NAMELEN);
567 			d.qid.path = qid(&stbuf);
568 			d.qid.vers = vers(&stbuf);
569 			d.mode = (d.qid.path&CHDIR)|(stbuf.st_mode&0777);
570 			d.atime = stbuf.st_atime;
571 			d.mtime = stbuf.st_mtime;
572 			d.len.l.hlength = 0;
573 			d.len.l.length = stbuf.st_size;
574 			convD2M(&d, rdata+n);
575 			n += DIRLEN;
576 		}
577 	}else{
578 		errno = 0;
579 		if(rf->fd->offset != rhdr.offset){
580 			rf->fd->offset = rhdr.offset;
581 			if(lseek(rf->fd->fd, rhdr.offset, 0) < 0)
582 				errjmp(sys_errlist[errno]);
583 		}
584 		n = read(rf->fd->fd, rdata, rhdr.count);
585 		if(n < 0)
586 			errjmp(sys_errlist[errno]);
587 	}
588 	rf->fd->offset += n;
589 	thdr.count = n;
590 	thdr.data = rdata;
591 }
592 
593 void
594 rwrite(void)
595 {
596 	Rfile *rf;
597 	int n;
598 
599 	rf = rfilefid();
600 	if(rf->fd == 0)
601 		errjmp(Enotopen);
602 	if(rhdr.count > sizeof rdata)
603 		errjmp(Etoolarge);
604 	errno = 0;
605 	if(rf->fd->offset != rhdr.offset){
606 		rf->fd->offset = rhdr.offset;
607 		if(lseek(rf->fd->fd, rhdr.offset, 0) < 0)
608 			errjmp(sys_errlist[errno]);
609 	}
610 	n = write(rf->fd->fd, rhdr.data, rhdr.count);
611 	if(n < 0)
612 		errjmp(sys_errlist[errno]);
613 	rf->fd->offset += n;
614 	thdr.count = n;
615 }
616 
617 void
618 rstat(void)
619 {
620 	Rfile *rf;
621 	File *f;
622 	Dir d;
623 
624 	rf = rfilefid();
625 	f = rf->file;
626 	errjmp(dostat(f, 0));
627 	strncpy(d.name, f->name, NAMELEN);
628 	strncpy(d.uid, id2name(uid, f->stbuf.st_uid), NAMELEN);
629 	strncpy(d.gid, id2name(gid, f->stbuf.st_gid), NAMELEN);
630 	d.qid = f->qid;
631 	d.mode = (f->qid.path&CHDIR)|(f->stbuf.st_mode&0777);
632 	d.atime = f->stbuf.st_atime;
633 	d.mtime = f->stbuf.st_mtime;
634 	d.len.l.hlength = 0;
635 	d.len.l.length = f->stbuf.st_size;
636 	convD2M(&d, thdr.stat);
637 }
638 
639 void
640 rwstat(void)
641 {
642 	Rfile *rf;
643 	File *f;
644 	Dir d;
645 	Pass *p;
646 	char *path, *dir, name[NAMELEN];
647 
648 	rf = rfilefid();
649 	f = rf->file;
650 	errjmp(dostat(f, 0));
651 	convM2D(rhdr.stat, &d);
652 	errno = 0;
653 	if(rf->uid != f->stbuf.st_uid)
654 		errjmp(Eowner);
655 	if(strcmp(d.name, f->name) != 0){
656 		parentwrperm(rf);
657 		dir = bldpath(f->path, "..", name);
658 		path = erealloc(0, strlen(dir)+1+strlen(d.name)+1);
659 		sprintf(path, "%s/%s", dir, d.name);
660 		if(link(f->path, path) < 0){
661 			free(path);
662 			errjmp(sys_errlist[errno]);
663 		}
664 		if(unlink(f->path) < 0){
665 			free(path);
666 			errjmp(sys_errlist[errno]);
667 		}
668 		free(f->path);
669 		free(f->name);
670 		f->path = path;
671 		f->name = estrdup(d.name);
672 	}
673 	if((d.mode&0777) != (f->stbuf.st_mode&0777)){
674 		if(chmod(f->path, d.mode&0777) < 0)
675 			errjmp(sys_errlist[errno]);
676 		f->stbuf.st_mode &= ~0777;
677 		f->stbuf.st_mode |= d.mode&0777;
678 	}
679 	p = name2pass(gid, d.gid);
680 	if(p == 0)
681 		errjmp(Eunknown);
682 	if(p->id != f->stbuf.st_gid){
683 		if(chown(f->path, f->stbuf.st_uid, p->id) < 0)
684 			errjmp(sys_errlist[errno]);
685 		f->stbuf.st_gid = p->id;
686 	}
687 }
688 
689 void
690 rclunk(int rm)
691 {
692 	int ret;
693 	char *err;
694 	Rfile *rf;
695 	File *f;
696 	Fd *fd;
697 
698 	err = 0;
699 	rf = rfilefid();
700 	f = rf->file;
701 	if(rm){
702 		parentwrperm(rf);
703 		if(f->qid.path & CHDIR)
704 			ret = rmdir(f->path);
705 		else
706 			ret = unlink(f->path);
707 		if(ret)
708 			err = sys_errlist[errno];
709 	}else if(rf->rclose){	/* ignore errors */
710 		if(f->qid.path & CHDIR)
711 			rmdir(f->path);
712 		else
713 			unlink(f->path);
714 	}
715 
716 	rf->busy = 0;
717 	if(--f->ref == 0){
718 		free(f->path);
719 		free(f->name);
720 		free(f);
721 	}
722 	fd = rf->fd;
723 	if(fd){
724 		if(fd->ref <= 0)
725 			error("clunk fd count");
726 		if(--fd->ref == 0){
727 			if(fd->fd)
728 				close(fd->fd);
729 			if(fd->dir)
730 				closedir(fd->dir);
731 			free(fd);
732 		}
733 		rf->fd = 0;
734 	}
735 	if(err)
736 		errjmp(err);
737 }
738 
739 char*
740 bldpath(char *a, char *elem, char *name)
741 {
742 	char *path, *p;
743 
744 	if(strcmp(elem, "..") == 0){
745 		if(strcmp(a, "/") == 0){
746 			path = estrdup(a);
747 			strcpy(name, a);
748 		}else{
749 			p = strrchr(a, '/');
750 			if(p == 0){
751 				fprintf(stderr, "path: '%s'\n", path);
752 				error("malformed path 1");
753 			}
754 			if(p == a)	/* reduced to "/" */
755 				p++;
756 			path = erealloc(0, (p-a)+1);
757 			memmove(path, a, (p-a));
758 			path[(p-a)] = 0;
759 			if(strcmp(path, "/") == 0)
760 				p = path;
761 			else{
762 				p = strrchr(path, '/');
763 				if(p == 0){
764 					fprintf(stderr, "path: '%s'\n", path);
765 					error("malformed path 2");
766 				}
767 				p++;
768 			}
769 			strcpy(name, p);
770 		}
771 	}else{
772 		if(strcmp(a, "/") == 0)
773 			a = "";
774 		path = erealloc(0, strlen(a)+1+strlen(elem)+1);
775 		sprintf(path, "%s/%s", a, elem);
776 		strcpy(name, elem);
777 	}
778 	if(strlen(name) >= NAMELEN)
779 		error("bldpath: name too long");
780 	return path;
781 }
782 
783 char*
784 dostat(File *f, char *elem)
785 {
786 	char *path;
787 	struct stat stbuf;
788 	char name[NAMELEN];
789 
790 	if(elem)
791 		path = bldpath(f->path, elem, name);
792 	else
793 		path = f->path;
794 	errno = 0;
795 	if(stat(path, &stbuf) < 0)
796 		return sys_errlist[errno];
797 	if(elem){
798 		free(f->path);
799 		f->path = path;
800 		f->name = estrdup(name);
801 	}
802 	f->qid.path = qid(&stbuf);
803 	f->qid.vers = vers(&stbuf);
804 	f->stbuf = stbuf;
805 	return 0;
806 }
807 
808 int
809 omode(int m)
810 {
811 	switch(m){
812 	case 0:		/* OREAD */
813 	case 3:		/* OEXEC */
814 		return 0;
815 	case 1:		/* OWRITE */
816 		return 1;
817 	case 2:		/* ORDWR */
818 		return 2;
819 	}
820 	errjmp(Emode);
821 	return 0;
822 }
823 
824 void
825 sendmsg(char *err)
826 {
827 	int n;
828 
829 	if(err){
830 		thdr.type = Rerror;
831 		strncpy(thdr.ename, err, ERRLEN);
832 	}
833 	DBG(fprintf(stderr, "<< %s\n", mfmt(&thdr)));
834 	n = convS2M(&thdr, tdata);
835 	if(n == 0)
836 		error("bad sendmsg format");
837 	if(write(1, tdata, n) != n)
838 		error("write error");
839 }
840 
841 
842 int
843 okfid(int fid)
844 {
845 	enum{ Delta=10 };
846 
847 	if(fid < 0){
848 		fprintf(stderr, "u9fs: negative fid %d\n", fid);
849 		return 0;
850 	}
851 	if(fid >= nrfilealloc){
852 		fid += Delta;
853 		rfile = erealloc(rfile, fid*sizeof(Rfile));
854 		memset(rfile+nrfilealloc, 0, (fid-nrfilealloc)*sizeof(Rfile));
855 		nrfilealloc = fid;
856 	}
857 	return 1;
858 }
859 
860 Rfile*
861 rfilefid(void)
862 {
863 	Rfile *rf;
864 
865 	if(!okfid(rhdr.fid))
866 		errjmp(Ebadfid);
867 	rf = &rfile[rhdr.fid];
868 	if(rf->busy == 0)
869 		errjmp(Ebadfid);
870 	if(rf->file->ref <= 0)
871 		error("ref count");
872 	return rf;
873 }
874 
875 void
876 perm(Rfile *rf, int mask, struct stat *st)
877 {
878 	if(st == 0)
879 		st = &rf->file->stbuf;
880 	/* plan 9 access semantics; simpler and more sensible */
881 	if(rf->uid == st->st_uid)
882 		if((st->st_mode>>6) & mask)
883 			return;
884 	if(rf->gid == st->st_gid)
885 		if((st->st_mode>>3) & mask)
886 			return;
887 	if((st->st_mode>>0) & mask)
888 		return;
889 	errjmp(Eperm);
890 }
891 
892 void
893 parentwrperm(Rfile *rf)
894 {
895 	Rfile trf;
896 	struct stat st;
897 	char *dirp, dir[512];
898 
899 	dirp = bldpath(rf->file->path, "..", dir);
900 	if(strlen(dirp) < sizeof dir){	/* ugly: avoid leaving dirp allocated */
901 		strcpy(dir, dirp);
902 		free(dirp);
903 		dirp = dir;
904 	}
905 	if(stat(dirp, &st) < 0)
906 		errjmp(Eperm);
907 	trf.uid = rf->uid;
908 	trf.gid = rf->gid;
909 	perm(&trf, 2, &st);
910 }
911 
912 File*
913 newfile(void)
914 {
915 	File *f;
916 
917 	f = erealloc(0, sizeof(File));
918 	memset(f, 0, sizeof(File));
919 	f->ref = 1;
920 	return f;
921 }
922 
923 /*
924  * qids: directory bit, seven bits of device, 24 bits of inode
925  */
926 Ulong
927 vers(struct stat *st)
928 {
929 	return st->st_mtime;
930 }
931 
932 Ulong
933 qid(struct stat *st)
934 {
935 	static int nqdev;
936 	static Uchar *qdev;
937 	Ulong q;
938 	int dev;
939 
940 	if(qdev == 0){
941 		qdev = erealloc(0, 65536U);
942 		memset(qdev, 0, 65536U);
943 	}
944 	q = 0;
945 	if((st->st_mode&S_IFMT) ==  S_IFDIR)
946 		q = CHDIR;
947 	dev = st->st_dev & 0xFFFFUL;
948 	if(qdev[dev] == 0){
949 		if(++nqdev >= 128)
950 			error("too many devices");
951 		qdev[dev] = nqdev;
952 	}
953 	q |= qdev[dev]<<24;
954 	q |= st->st_ino & 0x00FFFFFFUL;
955 	return q;
956 }
957 
958 Pass*
959 name2pass(Pass **pw, char *name)
960 {
961 	int i;
962 	Pass *p;
963 
964 	for(i=0; i<256; i++)
965 		for(p = pw[i]; p; p = p->next)
966 			if(strcmp(name, p->name) == 0)
967 				return p;
968 	return 0;
969 }
970 
971 char*
972 id2name(Pass **pw, int id)
973 {
974 	int i;
975 	Pass *p;
976 	char *s;
977 	static char buf[8];
978 
979 	s = 0;
980 	/* use last on list == first in file */
981 	i = (id&0xFF) ^ ((id>>8)&0xFF);
982 	for(p = pw[i]; p; p = p->next)
983 		if(p->id == id)
984 			s = p->name;
985 	if(s)
986 		return s;
987 	sprintf(buf, "%d", id);
988 	return buf;
989 }
990 
991 void
992 freepass(Pass **pass)
993 {
994 	int i;
995 	Pass *p, *np;
996 
997 	for(i=0; i<256; i++){
998 		for(p = pass[i]; p; p = np){
999 			np = p->next;
1000 			free(p);
1001 		}
1002 		pass[i] = 0;
1003 	}
1004 }
1005 
1006 void
1007 getpwdf(void)
1008 {
1009 	static mtime;
1010 	struct stat stbuf;
1011 	struct passwd *pw;
1012 	int i;
1013 	Pass *p;
1014 
1015 	if(stat("/etc/passwd", &stbuf) < 0)
1016 		error("can't read /etc/passwd");
1017 	if(stbuf.st_mtime <= mtime)
1018 		return;
1019 	freepass(uid);
1020 	while(pw = getpwent()){
1021 		i = pw->pw_uid;
1022 		i = (i&0xFF) ^ ((i>>8)&0xFF);
1023 		p = erealloc(0, sizeof(Pass));
1024 		p->next = uid[i];
1025 		uid[i] = p;
1026 		p->id = pw->pw_uid;
1027 		p->gid = pw->pw_gid;
1028 		p->name = estrdup(pw->pw_name);
1029 	}
1030 	setpwent();
1031 	endpwent();
1032 }
1033 
1034 void
1035 getgrpf(void)
1036 {
1037 	static mtime;
1038 	struct stat stbuf;
1039 	struct group *pw;
1040 	int i;
1041 	Pass *p;
1042 
1043 	if(stat("/etc/group", &stbuf) < 0)
1044 		error("can't read /etc/group");
1045 	if(stbuf.st_mtime <= mtime)
1046 		return;
1047 	freepass(gid);
1048 	while(pw = getgrent()){
1049 		i = pw->gr_gid;
1050 		i = (i&0xFF) ^ ((i>>8)&0xFF);
1051 		p = erealloc(0, sizeof(Pass));
1052 		p->next = gid[i];
1053 		gid[i] = p;
1054 		p->id = pw->gr_gid;
1055 		p->gid = 0;
1056 		p->name = estrdup(pw->gr_name);
1057 	}
1058 	setgrent();
1059 	endgrent();
1060 }
1061 
1062 void
1063 error(char *s)
1064 {
1065 	fprintf(stderr, "u9fs: %s\n", s);
1066 	perror("unix error");
1067 	exit(1);
1068 }
1069 
1070 void
1071 errjmp(char *s)
1072 {
1073 	if(s == 0)
1074 		return;
1075 	sendmsg(s);
1076 	longjmp(loopjmp, 1);
1077 }
1078 
1079 void*
1080 erealloc(void *p, unsigned n)
1081 {
1082 	if(p == 0)
1083 		p = malloc(n);
1084 	else
1085 		p = realloc(p, n);
1086 	if(p == 0)
1087 		error("realloc fail");
1088 	return p;
1089 }
1090 
1091 char*
1092 estrdup(char *p)
1093 {
1094 	p = strdup(p);
1095 	if(p == 0)
1096 		error("strdup fail");
1097 	return p;
1098 }
1099 
1100 #ifdef SOCKETS
1101 void
1102 remotehostname(void)
1103 {
1104 	struct sockaddr_in sock;
1105 	struct hostent *hp;
1106 	int len;
1107 	int on = 1;
1108 
1109 	len = sizeof sock;
1110 	if(getpeername(0, &sock, &len) < 0)
1111 		error("getpeername");
1112 	hp = gethostbyaddr((char *)&sock.sin_addr, sizeof (struct in_addr),
1113 		sock.sin_family);
1114 	if(hp == 0)
1115 		error("gethostbyaddr");
1116 	strcpy(bsdhost, hp->h_name);
1117 	fprintf(stderr, "bsdhost %s on %d\n", bsdhost, getpid());
1118 
1119 	setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
1120 }
1121 #endif
1122