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