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