xref: /inferno-os/os/port/sysfile.c (revision 5d0c4cf3fc288434c41cba52dd998ab1d7375a7b)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 
8 static int
9 growfd(Fgrp *f, int fd)
10 {
11 	int n;
12 	Chan **nfd, **ofd;
13 
14 	if(fd < f->nfd)
15 		return 0;
16 	n = f->nfd+DELTAFD;
17 	if(n > MAXNFD)
18 		n = MAXNFD;
19 	if(fd >= n)
20 		return -1;
21 	nfd = malloc(n*sizeof(Chan*));
22 	if(nfd == nil)
23 		return -1;
24 	ofd = f->fd;
25 	memmove(nfd, ofd, f->nfd*sizeof(Chan *));
26 	f->fd = nfd;
27 	f->nfd = n;
28 	free(ofd);
29 	return 0;
30 }
31 
32 int
33 newfd(Chan *c)
34 {
35 	int i;
36 	Fgrp *f = up->env->fgrp;
37 
38 	lock(f);
39 	for(i=f->minfd; i<f->nfd; i++)
40 		if(f->fd[i] == 0)
41 			break;
42 	if(i >= f->nfd && growfd(f, i) < 0){
43 		unlock(f);
44 		exhausted("file descriptors");
45 		return -1;
46 	}
47 	f->minfd = i + 1;
48 	if(i > f->maxfd)
49 		f->maxfd = i;
50 	f->fd[i] = c;
51 	unlock(f);
52 	return i;
53 }
54 
55 Chan*
56 fdtochan(Fgrp *f, int fd, int mode, int chkmnt, int iref)
57 {
58 	Chan *c;
59 
60 	c = 0;
61 
62 	lock(f);
63 	if(fd<0 || f->maxfd<fd || (c = f->fd[fd])==0) {
64 		unlock(f);
65 		error(Ebadfd);
66 	}
67 	if(iref)
68 		incref(c);
69 	unlock(f);
70 
71 	if(chkmnt && (c->flag&CMSG)) {
72 		if(iref)
73 			cclose(c);
74 		error(Ebadusefd);
75 	}
76 
77 	if(mode<0 || c->mode==ORDWR)
78 		return c;
79 
80 	if((mode&OTRUNC) && c->mode==OREAD) {
81 		if(iref)
82 			cclose(c);
83 		error(Ebadusefd);
84 	}
85 
86 	if((mode&~OTRUNC) != c->mode) {
87 		if(iref)
88 			cclose(c);
89 		error(Ebadusefd);
90 	}
91 
92 	return c;
93 }
94 
95 long
96 kchanio(void *vc, void *buf, int n, int mode)
97 {
98 	int r;
99 	Chan *c;
100 
101 	c = vc;
102 	if(waserror())
103 		return -1;
104 
105 	if(mode == OREAD)
106 		r = devtab[c->type]->read(c, buf, n, c->offset);
107 	else
108 		r = devtab[c->type]->write(c, buf, n, c->offset);
109 
110 	lock(c);
111 	c->offset += r;
112 	unlock(c);
113 	poperror();
114 	return r;
115 }
116 
117 int
118 openmode(ulong o)
119 {
120 	if(o >= (OTRUNC|OCEXEC|ORCLOSE|OEXEC))
121 		error(Ebadarg);
122 	o &= ~(OTRUNC|OCEXEC|ORCLOSE);
123 	if(o > OEXEC)
124 		error(Ebadarg);
125 	if(o == OEXEC)
126 		return OREAD;
127 	return o;
128 }
129 
130 void
131 fdclose(Fgrp *f, int fd)
132 {
133 	int i;
134 	Chan *c;
135 
136 	lock(f);
137 	c = f->fd[fd];
138 	if(c == 0){
139 		/* can happen for users with shared fd tables */
140 		unlock(f);
141 		return;
142 	}
143 	f->fd[fd] = 0;
144 	if(fd == f->maxfd)
145 		for(i=fd; --i>=0 && f->fd[i]==0; )
146 			f->maxfd = i;
147 	if(fd < f->minfd)
148 		f->minfd = fd;
149 	unlock(f);
150 	cclose(c);
151 }
152 
153 int
154 kchdir(char *path)
155 {
156 	Chan *c;
157 	Pgrp *pg;
158 
159 	if(waserror())
160 		return -1;
161 
162 	c = namec(path, Atodir, 0, 0);
163 	pg = up->env->pgrp;
164 	cclose(pg->dot);
165 	pg->dot = c;
166 	poperror();
167 	return 0;
168 }
169 
170 int
171 kfgrpclose(Fgrp *f, int fd)
172 {
173 	if(waserror())
174 		return -1;
175 
176 	/*
177 	 * Take no reference on the chan because we don't really need the
178 	 * data structure, and are calling fdtochan only for error checks.
179 	 * fdclose takes care of processes racing through here.
180 	 */
181 	fdtochan(f, fd, -1, 0, 0);
182 	fdclose(f, fd);
183 	poperror();
184 	return 0;
185 }
186 
187 int
188 kclose(int fd)
189 {
190 	return kfgrpclose(up->env->fgrp, fd);
191 }
192 
193 int
194 kcreate(char *path, int mode, ulong perm)
195 {
196 	int fd;
197 	Chan *c;
198 
199 	if(waserror())
200 		return -1;
201 
202 	openmode(mode&~OEXCL);	/* error check only; OEXCL okay here */
203 	c = namec(path, Acreate, mode, perm);
204 	if(waserror()) {
205 		cclose(c);
206 		nexterror();
207 	}
208 	fd = newfd(c);
209 	if(fd < 0)
210 		error(Enofd);
211 	poperror();
212 
213 	poperror();
214 	return fd;
215 }
216 
217 int
218 kdup(int old, int new)
219 {
220 	int fd;
221 	Chan *c, *oc;
222 	Fgrp *f = up->env->fgrp;
223 
224 	if(waserror())
225 		return -1;
226 
227 	c = fdtochan(up->env->fgrp, old, -1, 0, 1);
228 	if(c->qid.type & QTAUTH)
229 		error(Eperm);
230 	fd = new;
231 	if(fd != -1){
232 		lock(f);
233 		if(fd<0 || growfd(f, fd) < 0) {
234 			unlock(f);
235 			cclose(c);
236 			error(Ebadfd);
237 		}
238 		if(fd > f->maxfd)
239 			f->maxfd = fd;
240 		oc = f->fd[fd];
241 		f->fd[fd] = c;
242 		unlock(f);
243 		if(oc)
244 			cclose(oc);
245 	}else{
246 		if(waserror()) {
247 			cclose(c);
248 			nexterror();
249 		}
250 		fd = newfd(c);
251 		if(fd < 0)
252 			error(Enofd);
253 		poperror();
254 	}
255 	poperror();
256 	return fd;
257 }
258 
259 int
260 kfstat(int fd, uchar *buf, int n)
261 {
262 	Chan *c;
263 
264 	if(waserror())
265 		return -1;
266 
267 	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
268 	if(waserror()) {
269 		cclose(c);
270 		nexterror();
271 	}
272 	devtab[c->type]->stat(c, buf, n);
273 
274 	poperror();
275 	cclose(c);
276 
277 	poperror();
278 	return n;
279 }
280 
281 char*
282 kfd2path(int fd)
283 {
284 	Chan *c;
285 	char *s;
286 
287 	if(waserror())
288 		return nil;
289 	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
290 	s = nil;
291 	if(c->name != nil){
292 		s = malloc(c->name->len+1);
293 		if(s == nil){
294 			cclose(c);
295 			error(Enomem);
296 		}
297 		memmove(s, c->name->s, c->name->len+1);
298 		cclose(c);
299 	}
300 	poperror();
301 	return s;
302 }
303 
304 int
305 kfauth(int fd, char *aname)
306 {
307 	Chan *c, *ac;
308 
309 	if(waserror())
310 		return -1;
311 
312 	validname(aname, 1);
313 	c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
314 	if(waserror()){
315 		cclose(c);
316 		nexterror();
317 	}
318 
319 	ac = mntauth(c, aname);
320 
321 	/* at this point ac is responsible for keeping c alive */
322 	poperror();	/* c */
323 	cclose(c);
324 
325 	if(waserror()){
326 		cclose(ac);
327 		nexterror();
328 	}
329 
330 	fd = newfd(ac);
331 	if(fd < 0)
332 		error(Enofd);
333 	poperror();	/* ac */
334 
335 	poperror();
336 
337 	return fd;
338 }
339 
340 int
341 kfversion(int fd, uint msize, char *vers, uint arglen)
342 {
343 	int m;
344 	Chan *c;
345 
346 	if(waserror())
347 		return -1;
348 
349 	/* check there's a NUL in the version string */
350 	if(arglen==0 || memchr(vers, 0, arglen)==0)
351 		error(Ebadarg);
352 
353 	c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
354 	if(waserror()){
355 		cclose(c);
356 		nexterror();
357 	}
358 
359 	m = mntversion(c, vers, msize, arglen);
360 
361 	poperror();
362 	cclose(c);
363 
364 	poperror();
365 	return m;
366 }
367 
368 int
369 kpipe(int fd[2])
370 {
371 	Dev *d;
372 	Fgrp *f;
373 	Chan *c[2];
374 	static char *names[] = {"data", "data1"};
375 
376 	f = up->env->fgrp;
377 
378 	d = devtab[devno('|', 0)];
379 	c[0] = namec("#|", Atodir, 0, 0);
380 	c[1] = 0;
381 	fd[0] = -1;
382 	fd[1] = -1;
383 	if(waserror()) {
384 		if(c[0] != 0)
385 			cclose(c[0]);
386 		if(c[1] != 0)
387 			cclose(c[1]);
388 		if(fd[0] >= 0)
389 			f->fd[fd[0]]=0;
390 		if(fd[1] >= 0)
391 			f->fd[fd[1]]=0;
392 		return -1;
393 	}
394 	c[1] = cclone(c[0]);
395 	if(walk(&c[0], &names[0], 1, 1, nil) < 0)
396 		error(Egreg);
397 	if(walk(&c[1], &names[1], 1, 1, nil) < 0)
398 		error(Egreg);
399 	c[0] = d->open(c[0], ORDWR);
400 	c[1] = d->open(c[1], ORDWR);
401 	fd[0] = newfd(c[0]);
402 	if(fd[0] < 0)
403 		error(Enofd);
404 	fd[1] = newfd(c[1]);
405 	if(fd[1] < 0)
406 		error(Enofd);
407 	poperror();
408 	return 0;
409 }
410 
411 int
412 kfwstat(int fd, uchar *buf, int n)
413 {
414 	Chan *c;
415 
416 	if(waserror())
417 		return -1;
418 
419 	validstat(buf, n);
420 	c = fdtochan(up->env->fgrp, fd, -1, 1, 1);
421 	if(waserror()) {
422 		cclose(c);
423 		nexterror();
424 	}
425 	n = devtab[c->type]->wstat(c, buf, n);
426 	poperror();
427 	cclose(c);
428 
429 	poperror();
430 	return n;
431 }
432 
433 long
434 bindmount(Chan *c, char *old, int flag, char *spec)
435 {
436 	int ret;
437 	Chan *c1;
438 
439 	if(flag>MMASK || (flag&MORDER) == (MBEFORE|MAFTER))
440 		error(Ebadarg);
441 
442 	c1 = namec(old, Amount, 0, 0);
443 	if(waserror()){
444 		cclose(c1);
445 		nexterror();
446 	}
447 	ret = cmount(c, c1, flag, spec);
448 
449 	poperror();
450 	cclose(c1);
451 	return ret;
452 }
453 
454 int
455 kbind(char *new, char *old, int flags)
456 {
457 	long r;
458 	Chan *c0;
459 
460 	if(waserror())
461 		return -1;
462 
463 	c0 = namec(new, Abind, 0, 0);
464 	if(waserror()) {
465 		cclose(c0);
466 		nexterror();
467 	}
468 	r = bindmount(c0, old, flags, "");
469 	poperror();
470 	cclose(c0);
471 
472 	poperror();
473 	return r;
474 }
475 
476 int
477 kmount(int fd, int afd, char *old, int flags, char *spec)
478 {
479 	long r;
480 	volatile struct { Chan *c; } c0;
481 	volatile struct { Chan *c; } bc;
482 	volatile struct { Chan *c; } ac;
483 	Mntparam mntparam;
484 
485 	ac.c = nil;
486 	bc.c = nil;
487 	c0.c = nil;
488 	if(waserror()) {
489 		cclose(ac.c);
490 		cclose(bc.c);
491 		cclose(c0.c);
492 		return -1;
493 	}
494 	bc.c = fdtochan(up->env->fgrp, fd, ORDWR, 0, 1);
495 	if(afd >= 0)
496 		ac.c = fdtochan(up->env->fgrp, afd, ORDWR, 0, 1);
497 	mntparam.chan = bc.c;
498 	mntparam.authchan = ac.c;
499 	mntparam.spec = spec;
500 	mntparam.flags = flags;
501 	c0.c = devtab[devno('M', 0)]->attach((char*)&mntparam);
502 
503 	r = bindmount(c0.c, old, flags, spec);
504 	poperror();
505 	cclose(ac.c);
506 	cclose(bc.c);
507 	cclose(c0.c);
508 
509 	return r;
510 }
511 
512 int
513 kunmount(char *old, char *new)
514 {
515 	volatile struct { Chan *c; } cmount;
516 	volatile struct { Chan *c; } cmounted;
517 
518 	cmount.c = nil;
519 	cmounted.c = nil;
520 	if(waserror()) {
521 		cclose(cmount.c);
522 		cclose(cmounted.c);
523 		return -1;
524 	}
525 
526 	cmount.c = namec(new, Amount, 0, 0);
527 	if(old != nil && old[0] != '\0') {
528 		/*
529 		 * This has to be namec(..., Aopen, ...) because
530 		 * if arg[0] is something like /srv/cs or /fd/0,
531 		 * opening it is the only way to get at the real
532 		 * Chan underneath.
533 		 */
534 		cmounted.c = namec(old, Aopen, OREAD, 0);
535 	}
536 
537 	cunmount(cmount.c, cmounted.c);
538 	poperror();
539 	cclose(cmount.c);
540 	cclose(cmounted.c);
541 	return 0;
542 }
543 
544 int
545 kopen(char *path, int mode)
546 {
547 	int fd;
548 	Chan *c;
549 
550 	if(waserror())
551 		return -1;
552 
553 	openmode(mode);                         /* error check only */
554 	c = namec(path, Aopen, mode, 0);
555 	if(waserror()){
556 		cclose(c);
557 		nexterror();
558 	}
559 	fd = newfd(c);
560 	if(fd < 0)
561 		error(Enofd);
562 	poperror();
563 
564 	poperror();
565 	return fd;
566 }
567 
568 long
569 unionread(Chan *c, void *va, long n)
570 {
571 	int i;
572 	long nr;
573 	Mhead *m;
574 	Mount *mount;
575 
576 	qlock(&c->umqlock);
577 	m = c->umh;
578 	rlock(&m->lock);
579 	mount = m->mount;
580 	/* bring mount in sync with c->uri and c->umc */
581 	for(i = 0; mount != nil && i < c->uri; i++)
582 		mount = mount->next;
583 
584 	nr = 0;
585 	while(mount != nil) {
586 		/* Error causes component of union to be skipped */
587 		if(mount->to && !waserror()) {
588 			if(c->umc == nil){
589 				c->umc = cclone(mount->to);
590 				c->umc = devtab[c->umc->type]->open(c->umc, OREAD);
591 			}
592 
593 			nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset);
594 			if(nr < 0)
595 				nr = 0;	/* dev.c can return -1 */
596 			c->umc->offset += nr;
597 			poperror();
598 		}
599 		if(nr > 0)
600 			break;
601 
602 		/* Advance to next element */
603 		c->uri++;
604 		if(c->umc) {
605 			cclose(c->umc);
606 			c->umc = nil;
607 		}
608 		mount = mount->next;
609 	}
610 	runlock(&m->lock);
611 	qunlock(&c->umqlock);
612 	return nr;
613 }
614 
615 static void
616 unionrewind(Chan *c)
617 {
618 	qlock(&c->umqlock);
619 	c->uri = 0;
620 	if(c->umc){
621 		cclose(c->umc);
622 		c->umc = nil;
623 	}
624 	qunlock(&c->umqlock);
625 }
626 
627 static long
628 rread(int fd, void *va, long n, vlong *offp)
629 {
630 	int dir;
631 	Chan *c;
632 	vlong off;
633 
634 	if(waserror())
635 		return -1;
636 
637 	c = fdtochan(up->env->fgrp, fd, OREAD, 1, 1);
638 	if(waserror()) {
639 		cclose(c);
640 		nexterror();
641 	}
642 
643 	if(n < 0)
644 		error(Etoosmall);
645 
646 	dir = c->qid.type & QTDIR;
647 	if(dir && c->umh)
648 		n = unionread(c, va, n);
649 	else{
650 		if(offp == nil){
651 			lock(c);	/* lock for vlong assignment */
652 			off = c->offset;
653 			unlock(c);
654 		}else
655 			off = *offp;
656 		if(off < 0)
657 			error(Enegoff);
658 		if(off == 0){
659 			if(offp == nil){
660 				lock(c);
661 				c->offset = 0;
662 				c->dri = 0;
663 				unlock(c);
664 			}
665 			unionrewind(c);
666 		}
667 		n = devtab[c->type]->read(c, va, n, off);
668 		lock(c);
669 		c->offset += n;
670 		unlock(c);
671 	}
672 
673 	poperror();
674 	cclose(c);
675 
676 	poperror();
677 	return n;
678 }
679 
680 long
681 kread(int fd, void *va, long n)
682 {
683 	return rread(fd, va, n, nil);
684 }
685 
686 long
687 kpread(int fd, void *va, long n, vlong off)
688 {
689 	return rread(fd, va, n, &off);
690 }
691 
692 int
693 kremove(char *path)
694 {
695 	Chan *c;
696 
697 	if(waserror())
698 		return -1;
699 
700 	c = namec(path, Aremove, 0, 0);
701 	if(waserror()) {
702 		c->type = 0;	/* see below */
703 		cclose(c);
704 		nexterror();
705 	}
706 	devtab[c->type]->remove(c);
707 	/*
708 	 * Remove clunks the fid, but we need to recover the Chan
709 	 * so fake it up.  rootclose() is known to be a nop.
710 	 */
711 	c->type = 0;
712 	poperror();
713 	cclose(c);
714 
715 	poperror();
716 	return 0;
717 }
718 
719 vlong
720 kseek(int fd, vlong off, int whence)
721 {
722 	Dir *dir;
723 	Chan *c;
724 
725 	if(waserror())
726 		return -1;
727 
728 	c = fdtochan(up->env->fgrp, fd, -1, 1, 1);
729 	if(waserror()) {
730 		cclose(c);
731 		nexterror();
732 	}
733 
734 	if(devtab[c->type]->dc == '|')
735 		error(Eisstream);
736 
737 	switch(whence) {
738 	case 0:
739 		if(c->qid.type & QTDIR){
740 			if(off != 0)
741 				error(Eisdir);
742 			unionrewind(c);
743 		}else if(off < 0)
744 			error(Enegoff);
745 		lock(c);	/* lock for vlong assignment */
746 		c->offset = off;
747 		unlock(c);
748 		break;
749 
750 	case 1:
751 		if(c->qid.type & QTDIR)
752 			error(Eisdir);
753 		lock(c);	/* lock for read/write update */
754 		off += c->offset;
755 		if(off < 0){
756 			unlock(c);
757 			error(Enegoff);
758 		}
759 		c->offset = off;
760 		unlock(c);
761 		break;
762 
763 	case 2:
764 		if(c->qid.type & QTDIR)
765 			error(Eisdir);
766 		dir = chandirstat(c);
767 		if(dir == nil)
768 			error("internal error: stat error in seek");
769 		off += dir->length;
770 		free(dir);
771 		if(off < 0)
772 			error(Enegoff);
773 		lock(c);	/* lock for read/write update */
774 		c->offset = off;
775 		unlock(c);
776 		break;
777 
778 	default:
779 		error(Ebadarg);
780 		break;
781 	}
782 	poperror();
783 	c->dri = 0;
784 	cclose(c);
785 	poperror();
786 	return off;
787 }
788 
789 void
790 validstat(uchar *s, int n)
791 {
792 	int m;
793 	char buf[64];
794 
795 	if(statcheck(s, n) < 0)
796 		error(Ebadstat);
797 	/* verify that name entry is acceptable */
798 	s += STATFIXLEN - 4*BIT16SZ;	/* location of first string */
799 	/*
800 	 * s now points at count for first string.
801 	 * if it's too long, let the server decide; this is
802 	 * only for his protection anyway. otherwise
803 	 * we'd have to allocate and waserror.
804 	 */
805 	m = GBIT16(s);
806 	s += BIT16SZ;
807 	if(m+1 > sizeof buf)
808 		return;
809 	memmove(buf, s, m);
810 	buf[m] = '\0';
811 	/* name could be '/' */
812 	if(strcmp(buf, "/") != 0)
813 		validname(buf, 0);
814 }
815 
816 int
817 kstat(char *path, uchar *buf, int n)
818 {
819 	Chan *c;
820 
821 	if(waserror())
822 		return -1;
823 
824 	c = namec(path, Aaccess, 0, 0);
825 	if(waserror()){
826 		cclose(c);
827 		nexterror();
828 	}
829 	devtab[c->type]->stat(c, buf, n);
830 	poperror();
831 	cclose(c);
832 
833 	poperror();
834 	return 0;
835 }
836 
837 static long
838 rwrite(int fd, void *va, long n, vlong *offp)
839 {
840 	Chan *c;
841 	vlong off;
842 	long m;
843 
844 	if(waserror())
845 		return -1;
846 	c = fdtochan(up->env->fgrp, fd, OWRITE, 1, 1);
847 	if(waserror()) {
848 		cclose(c);
849 		nexterror();
850 	}
851 	if(c->qid.type & QTDIR)
852 		error(Eisdir);
853 
854 	if(n < 0)
855 		error(Etoosmall);
856 
857 	if(offp == nil){
858 		lock(c);
859 		off = c->offset;
860 		c->offset += n;
861 		unlock(c);
862 	}else
863 		off = *offp;
864 
865 	if(waserror()){
866 		if(offp == nil){
867 			lock(c);
868 			c->offset -= n;
869 			unlock(c);
870 		}
871 		nexterror();
872 	}
873 	if(off < 0)
874 		error(Enegoff);
875 	m = devtab[c->type]->write(c, va, n, off);
876 	poperror();
877 
878 	if(offp == nil && m < n){
879 		lock(c);
880 		c->offset -= n - m;
881 		unlock(c);
882 	}
883 
884 	poperror();
885 	cclose(c);
886 
887 	poperror();
888 	return n;
889 }
890 
891 long
892 kwrite(int fd, void *va, long n)
893 {
894 	return rwrite(fd, va, n, nil);
895 }
896 
897 long
898 kpwrite(int fd, void *va, long n, vlong off)
899 {
900 	return rwrite(fd, va, n, &off);
901 }
902 
903 int
904 kwstat(char *path, uchar *buf, int n)
905 {
906 	Chan *c;
907 
908 	if(waserror())
909 		return -1;
910 
911 	validstat(buf, n);
912 	c = namec(path, Aaccess, 0, 0);
913 	if(waserror()){
914 		cclose(c);
915 		nexterror();
916 	}
917 	n = devtab[c->type]->wstat(c, buf, n);
918 	poperror();
919 	cclose(c);
920 
921 	poperror();
922 	return n;
923 }
924 
925 enum
926 {
927 	DIRSIZE = STATFIXLEN + 32 * 4,
928 	DIRREADLIM = 2048,	/* should handle the largest reasonable directory entry */
929 };
930 
931 Dir*
932 chandirstat(Chan *c)
933 {
934 	Dir *d;
935 	uchar *buf;
936 	int n, nd, i;
937 
938 	nd = DIRSIZE;
939 	for(i=0; i<2; i++){	/* should work by the second try */
940 		d = smalloc(sizeof(Dir) + nd);
941 		buf = (uchar*)&d[1];
942 		if(waserror()){
943 			free(d);
944 			return nil;
945 		}
946 		n = devtab[c->type]->stat(c, buf, nd);
947 		poperror();
948 		if(n < BIT16SZ){
949 			free(d);
950 			return nil;
951 		}
952 		nd = GBIT16((uchar*)buf) + BIT16SZ;	/* size needed to store whole stat buffer including count */
953 		if(nd <= n){
954 			convM2D(buf, n, d, (char*)&d[1]);
955 			return d;
956 		}
957 		/* else sizeof(Dir)+nd is plenty */
958 		free(d);
959 	}
960 	return nil;
961 
962 }
963 
964 Dir*
965 kdirstat(char *name)
966 {
967 	Chan *c;
968 	Dir *d;
969 
970 	if(waserror())
971 		return nil;
972 
973 	c = namec(name, Aaccess, 0, 0);
974 	if(waserror()){
975 		cclose(c);
976 		nexterror();
977 	}
978 	d = chandirstat(c);
979 	poperror();
980 	cclose(c);
981 
982 	poperror();
983 	return d;
984 }
985 
986 Dir*
987 kdirfstat(int fd)
988 {
989 	Chan *c;
990 	Dir *d;
991 
992 	if(waserror())
993 		return nil;
994 
995 	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
996 	if(waserror()) {
997 		cclose(c);
998 		nexterror();
999 	}
1000 	d = chandirstat(c);
1001 	poperror();
1002 	cclose(c);
1003 
1004 	poperror();
1005 	return d;
1006 }
1007 
1008 int
1009 kdirwstat(char *name, Dir *dir)
1010 {
1011 	uchar *buf;
1012 	int r;
1013 
1014 	r = sizeD2M(dir);
1015 	buf = smalloc(r);
1016 	convD2M(dir, buf, r);
1017 	r = kwstat(name, buf, r);
1018 	free(buf);
1019 	return r < 0? r: 0;
1020 }
1021 
1022 int
1023 kdirfwstat(int fd, Dir *dir)
1024 {
1025 	uchar *buf;
1026 	int r;
1027 
1028 	r = sizeD2M(dir);
1029 	buf = smalloc(r);
1030 	convD2M(dir, buf, r);
1031 	r = kfwstat(fd, buf, r);
1032 	free(buf);
1033 	return r < 0? r: 0;
1034 }
1035 
1036 static long
1037 dirpackage(uchar *buf, long ts, Dir **d)
1038 {
1039 	char *s;
1040 	long ss, i, n, nn, m;
1041 
1042 	*d = nil;
1043 	if(ts <= 0)
1044 		return ts;
1045 
1046 	/*
1047 	 * first find number of all stats, check they look like stats, & size all associated strings
1048 	 */
1049 	ss = 0;
1050 	n = 0;
1051 	for(i = 0; i < ts; i += m){
1052 		m = BIT16SZ + GBIT16(&buf[i]);
1053 		if(statcheck(&buf[i], m) < 0)
1054 			break;
1055 		ss += m;
1056 		n++;
1057 	}
1058 
1059 	if(i != ts)
1060 		error("bad directory format");
1061 
1062 	*d = malloc(n * sizeof(Dir) + ss);
1063 	if(*d == nil)
1064 		error(Enomem);
1065 
1066 	/*
1067 	 * then convert all buffers
1068 	 */
1069 	s = (char*)*d + n * sizeof(Dir);
1070 	nn = 0;
1071 	for(i = 0; i < ts; i += m){
1072 		m = BIT16SZ + GBIT16((uchar*)&buf[i]);
1073 		if(nn >= n || convM2D(&buf[i], m, *d + nn, s) != m){
1074 			free(*d);
1075 			*d = nil;
1076 			error("bad directory entry");
1077 		}
1078 		nn++;
1079 		s += m;
1080 	}
1081 
1082 	return nn;
1083 }
1084 
1085 long
1086 kdirread(int fd, Dir **d)
1087 {
1088 	uchar *buf;
1089 	long ts;
1090 
1091 	*d = nil;
1092 	if(waserror())
1093 		return -1;
1094 	buf = malloc(DIRREADLIM);
1095 	if(buf == nil)
1096 		error(Enomem);
1097 	if(waserror()){
1098 		free(buf);
1099 		nexterror();
1100 	}
1101 	ts = kread(fd, buf, DIRREADLIM);
1102 	if(ts >= 0)
1103 		ts = dirpackage(buf, ts, d);
1104 	poperror();
1105 	free(buf);
1106 	poperror();
1107 	return ts;
1108 }
1109 
1110 int
1111 kiounit(int fd)
1112 {
1113 	Chan *c;
1114 	int n;
1115 
1116 	c = fdtochan(up->env->fgrp, fd, -1, 0, 1);
1117 	if(waserror()){
1118 		cclose(c);
1119 		return 0;	/* n.b. */
1120 	}
1121 	n = c->iounit;
1122 	poperror();
1123 	cclose(c);
1124 	return n;
1125 }
1126