xref: /inferno-os/emu/port/devlogfs.c (revision d0e1d143ef6f03c75c008c7ec648859dd260cbab)
1 #ifndef EMU
2 #include "u.h"
3 #include "../port/lib.h"
4 #include "../port/error.h"
5 #else
6 #include	"error.h"
7 #endif
8 #include	<dat.h>
9 #include	<fns.h>
10 #include	<kernel.h>
11 #include	<logfs.h>
12 #include	<nandfs.h>
13 
14 #ifndef EMU
15 #define Sleep sleep
16 #define Wakeup wakeup
17 #endif
18 
19 #ifndef offsetof
20 #define offsetof(T,X) ((ulong)&(((T*)0)->X))
21 #endif
22 
23 typedef struct Devlogfs Devlogfs;
24 typedef struct DevlogfsSession DevlogfsSession;
25 
26 //#define CALLTRACE
27 
28 enum {
29 	DEVLOGFSDEBUG = 0,
30 	DEVLOGFSIODEBUG = 0,
31 	DEVLOGFSBAD = 1,
32 };
33 
34 enum {
35 	Qdir,
36 	Qctl,
37 	Qusers,
38 	Qdump,
39 	Qfs,
40 	Qfsboot,
41 	Qend,
42 };
43 
44 typedef enum DevlogfsServerState { Closed, BootOpen, NeedVersion, NeedAttach, Attached, Hungup } DevlogfsServerState;
45 
46 struct Devlogfs {
47 	QLock qlock;
48 	Ref ref;
49 	int instance;
50 	int trace;	/* (debugging) trace of read/write actions */
51 	int nand;
52 	char *name;
53 	char *device;
54 	char *filename[Qend - Qfs];
55 	LogfsLowLevel *ll;
56 	Chan *flash, *flashctl;
57 	QLock bootqlock;
58 	int logfstrace;
59 	LogfsBoot *lb;
60 	/* stuff for server */
61 	ulong openflags;
62 	Fcall in;
63 	Fcall out;
64 	int reading;
65 	DevlogfsServerState state;
66 	Rendez readrendez;
67 	Rendez writerendez;
68 	uint readcount;
69 	ulong readbufsize;
70 	uchar *readbuf;
71 	uchar *readp;
72 	LogfsServer *server;
73 	Devlogfs *next;
74 };
75 
76 #define MAXMSIZE 8192
77 
78 static struct {
79 	RWlock rwlock;		/* rlock when walking, wlock when changing */
80 	QLock configqlock;		/* serialises addition of new configurations */
81 	Devlogfs *head;
82 	char *defname;
83 } devlogfslist;
84 
85 static LogfsIdentityStore *is;
86 
87 #ifndef EMU
88 char Eunknown[] = "unknown user or group id";
89 #endif
90 
91 static	void	devlogfsfree(Devlogfs*);
92 
93 #define SPLITPATH(path, qtype, instance, qid, qt) { instance = path >> 4; qid = path & 0xf; qt = qtype & QTDIR; }
94 #define DATAQID(q, qt) (!(qt) && (q) >= Qfs && (q) < Qend)
95 #define MKPATH(instance, qid) ((instance << 4) | qid)
96 
97 #define PREFIX "logfs"
98 
99 static char *devlogfsprefix = PREFIX;
100 static char *devlogfsctlname = PREFIX "ctl";
101 static char *devlogfsusersname = PREFIX "users";
102 static char *devlogfsdumpname = PREFIX "dump";
103 static char *devlogfsbootsuffix = "boot";
104 static char *devlogfs9pversion = "9P2000";
105 
106 enum {
107 	Toshiba = 0x98,
108 	Samsung = 0xec,
109 };
110 
111 static struct {
112 	uchar manufacturer;
113 	uchar device;
114 } nandtab[] = {
115 	{ 0,	0xe6 },
116 	{ 0,	0xea },
117 	{ 0,	0xe3 },
118 	{ 0,	0xe5 },
119 	{ 0,	0x73 },
120 	{ 0,	0x75 },
121 	{ 0,	0x76 },
122 };
123 
124 static void
125 errorany(char *errmsg)
126 {
127 	if (errmsg)
128 		error(errmsg);
129 }
130 
131 static void *
132 emalloc(ulong size)
133 {
134 	void *p;
135 	p = logfsrealloc(nil, size);
136 	if (p == nil)
137 		error(Enomem);
138 	return p;
139 }
140 
141 static char *
142 estrdup(char *q)
143 {
144 	void *p;
145 	if (q == nil)
146 		return nil;
147 	p = logfsrealloc(nil, strlen(q) + 1);
148 	if (p == nil)
149 		error(Enomem);
150 	return strcpy(p, q);
151 }
152 
153 static char *
154 estrconcat(char *a, ...)
155 {
156 	va_list l;
157 	char *p, *r;
158 	int t;
159 
160 	t = strlen(a);
161 	va_start(l, a);
162 	while ((p = va_arg(l, char *)) != nil)
163 		t += strlen(p);
164 
165 	r = logfsrealloc(nil, t + 1);
166 	if (r == nil)
167 		error(Enomem);
168 
169 	strcpy(r, a);
170 	va_start(l, a);
171 	while ((p = va_arg(l, char *)) != nil)
172 		strcat(r, p);
173 
174 	va_end(l);
175 
176 	return r;
177 }
178 
179 static int
180 gen(Chan *c, int i, Dir *dp, int lockit)
181 {
182 	Devlogfs *l;
183 	long size;
184 	Qid qid;
185 	qid.vers = 0;
186 	qid.type = 0;
187 
188 	if (i + Qctl < Qfs) {
189 		switch (i + Qctl) {
190 		case Qctl:
191 			qid.path = Qctl;
192 			devdir(c, qid, devlogfsctlname, 0, eve, 0666, dp);
193 			return 1;
194 		case Qusers:
195 			qid.path = Qusers;
196 			devdir(c, qid, devlogfsusersname, 0, eve, 0444, dp);
197 			return 1;
198 		case Qdump:
199 			qid.path = Qdump;
200 			devdir(c, qid, devlogfsdumpname, 0, eve, 0444, dp);
201 			return 1;
202 		}
203 	}
204 
205 	i -= Qfs - Qctl;
206 
207 	if (lockit)
208 		rlock(&devlogfslist.rwlock);
209 
210 	if (waserror()) {
211 		if (lockit)
212 			runlock(&devlogfslist.rwlock);
213 		nexterror();
214 	}
215 
216 	for (l = devlogfslist.head; l; l = l->next) {
217 		if (i < Qend - Qfs)
218 			break;
219 		i -= Qend - Qfs;
220 	}
221 
222 	if (l == nil) {
223 		poperror();
224 		if (lockit)
225 			runlock(&devlogfslist.rwlock);
226 		return -1;
227 	}
228 
229 	switch (Qfs + i) {
230 	case Qfsboot:
231 		size = l->lb ? logfsbootgetsize(l->lb) : 0;
232 		break;
233 	default:
234 		size = 0;
235 		break;
236 	}
237 	/* perhaps the user id should come from the underlying file */
238 	qid.path = MKPATH(l->instance, Qfs + i);
239 	devdir(c, qid, l->filename[i], size, eve, 0666, dp);
240 
241 	poperror();
242 	if (lockit)
243 		runlock(&devlogfslist.rwlock);
244 
245 	return 1;
246 }
247 
248 static int
249 devlogfsgen(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp)
250 {
251 	USED(n);
252 	USED(tab);
253 	USED(ntab);
254 	return gen(c, i, dp, 1);
255 }
256 
257 static int
258 devlogfsgennolock(Chan *c, char *n, Dirtab *tab, int ntab, int i, Dir *dp)
259 {
260 	USED(n);
261 	USED(tab);
262 	USED(ntab);
263 	return gen(c, i, dp, 0);
264 }
265 
266 /* called under lock */
267 static Devlogfs *
268 devlogfsfind(int instance)
269 {
270 	Devlogfs *l;
271 
272 	for (l = devlogfslist.head; l; l = l->next)
273 		if (l->instance == instance)
274 			break;
275 	return l;
276 }
277 
278 static Devlogfs *
279 devlogfsget(int instance)
280 {
281 	Devlogfs *l;
282 	rlock(&devlogfslist.rwlock);
283 	for (l = devlogfslist.head; l; l = l->next)
284 		if (l->instance == instance)
285 			break;
286 	if (l)
287 		incref(&l->ref);
288 	runlock(&devlogfslist.rwlock);
289 	return l;
290 }
291 
292 static Devlogfs *
293 devlogfsfindbyname(char *name)
294 {
295 	Devlogfs *l;
296 
297 	rlock(&devlogfslist.rwlock);
298 	for (l = devlogfslist.head; l; l = l->next)
299 		if (strcmp(l->name, name) == 0)
300 			break;
301 	runlock(&devlogfslist.rwlock);
302 	return l;
303 }
304 
305 static Devlogfs *
306 devlogfssetdefname(char *name)
307 {
308 	Devlogfs *l;
309 	char *searchname;
310 	wlock(&devlogfslist.rwlock);
311 	if (waserror()) {
312 		wunlock(&devlogfslist.rwlock);
313 		nexterror();
314 	}
315 	if (name == nil)
316 		searchname = devlogfslist.defname;
317 	else
318 		searchname = name;
319 	for (l = devlogfslist.head; l; l = l->next)
320 		if (strcmp(l->name, searchname) == 0)
321 			break;
322 	if (l == nil) {
323 		logfsfreemem(devlogfslist.defname);
324 		devlogfslist.defname = nil;
325 	}
326 	else if (name) {
327 		if (devlogfslist.defname) {
328 			logfsfreemem(devlogfslist.defname);
329 			devlogfslist.defname = nil;
330 		}
331 		devlogfslist.defname = estrdup(name);
332 	}
333 	poperror();
334 	wunlock(&devlogfslist.rwlock);
335 	return l;
336 }
337 
338 static Chan *
339 devlogfskopen(char *name, char *suffix, int mode)
340 {
341 	Chan *c;
342 	char *fn;
343 	int fd;
344 
345 	fn = estrconcat(name, suffix, 0);
346 	fd = kopen(fn, mode);
347 	logfsfreemem(fn);
348 	if (fd < 0)
349 		error(up->env->errstr);
350 	c = fdtochan(up->env->fgrp, fd, mode, 0, 1);
351 	kclose(fd);
352 	return c;
353 }
354 
355 static char *
356 xread(void *a, void *buf, long nbytes, ulong offset)
357 {
358 	Devlogfs *l = a;
359 	long rv;
360 
361 	if (DEVLOGFSIODEBUG || l->trace)
362 		print("devlogfs: %s: read(0x%lux, %ld)\n", l->device, offset, nbytes);
363 	l->flash->offset = offset;
364 	rv = kchanio(l->flash, buf, nbytes, OREAD);
365 	if (rv < 0) {
366 		print("devlogfs: %s: flash read error: %s\n", l->device, up->env->errstr);
367 		return up->env->errstr;
368 	}
369 	if (rv != nbytes) {
370 		print("devlogfs: %s: short flash read: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes);
371 		return "short read";
372 	}
373 	return nil;
374 }
375 
376 static char *
377 xwrite(void *a, void *buf, long nbytes, ulong offset)
378 {
379 	Devlogfs *l = a;
380 	long rv;
381 
382 	if (DEVLOGFSIODEBUG || l->trace)
383 		print("devlogfs: %s: write(0x%lux, %ld)\n", l->device, offset, nbytes);
384 	l->flash->offset = offset;
385 	rv = kchanio(l->flash, buf, nbytes, OWRITE);
386 	if (rv < 0) {
387 		print("devlogfs: %s: flash write error: %s\n", l->device, up->env->errstr);
388 		return up->env->errstr;
389 	}
390 	if (rv != nbytes) {
391 		print("devlogfs: %s: short flash write: offset %lud, %ld not %ld\n", l->device, offset, rv, nbytes);
392 		return "short write";
393 	}
394 	return nil;
395 }
396 
397 static char *
398 xerase(void *a, long address)
399 {
400 	Devlogfs *l = a;
401 	char cmd[40];
402 
403 	if (DEVLOGFSIODEBUG || l->trace)
404 		print("devlogfs: %s: erase(0x%lux)\n", l->device, address);
405 	snprint(cmd, sizeof(cmd), "erase 0x%8.8lux", address);
406 	if (kchanio(l->flashctl, cmd, strlen(cmd), OWRITE) <= 0) {
407 		print("devlogfs: %s: flash erase error: %s\n", l->device, up->env->errstr);
408 		return up->env->errstr;
409 	}
410 	return nil;
411 }
412 
413 static char *
414 xsync(void *a)
415 {
416 	Devlogfs *l = a;
417 	uchar statbuf[STATFIXLEN];
418 
419 	if (DEVLOGFSIODEBUG || l->trace)
420 		print("devlogfs: %s: sync()\n", l->device);
421 	memset(statbuf, 0xff, sizeof(statbuf));
422 	memset(statbuf + STATFIXLEN - 8, 0x00, 8);
423 	PBIT16(statbuf, sizeof(statbuf) - BIT16SZ);
424 	if (kwstat(l->device, statbuf, sizeof(statbuf)) < 0)
425 		return up->env->errstr;
426 	return nil;
427 }
428 
429 //#define LEAKHUNT
430 #ifdef LEAKHUNT
431 #define MAXLIVE 2000
432 typedef struct Live {
433 	void *p;
434 	int freed;
435 	ulong callerpc;
436 } Live;
437 
438 static Live livemem[MAXLIVE];
439 
440 static void
441 leakalloc(void *p, ulong callerpc)
442 {
443 	int x;
444 	int use = -1;
445 	for (x = 0; x < MAXLIVE; x++) {
446 		if (livemem[x].p == p) {
447 			if (!livemem[x].freed)
448 				print("leakalloc: unexpected realloc of 0x%.8lux from 0x%.8lux\n", p, callerpc);
449 //			else
450 //				print("leakalloc: reusing address 0x%.8lux from 0x%.8lux\n", p, callerpc);
451 			livemem[x].freed = 0;
452 			livemem[x].callerpc = callerpc;
453 			return;
454 		}
455 		else if (use < 0 && livemem[x].p == 0)
456 			use = x;
457 	}
458 	if (use < 0)
459 		panic("leakalloc: too many live entries");
460 	livemem[use].p = p;
461 	livemem[use].freed = 0;
462 	livemem[use].callerpc = callerpc;
463 }
464 
465 static void
466 leakaudit(void)
467 {
468 	int x;
469 	for (x = 0; x < MAXLIVE; x++) {
470 		if (livemem[x].p && !livemem[x].freed)
471 			print("leakaudit: 0x%.8lux from 0x%.8lux\n", livemem[x].p, livemem[x].callerpc);
472 	}
473 }
474 
475 static void
476 leakfree(void *p, ulong callerpc)
477 {
478 	int x;
479 	if (p == nil)
480 		return;
481 	for (x = 0; x < MAXLIVE; x++) {
482 		if (livemem[x].p == p) {
483 			if (livemem[x].freed)
484 				print("leakfree: double free of 0x%.8lux from 0x%.8lux, originally by 0x%.8lux\n",
485 					p, callerpc, livemem[x].callerpc);
486 			livemem[x].freed = 1;
487 			livemem[x].callerpc = callerpc;
488 			return;
489 		}
490 	}
491 	print("leakfree: free of unalloced address 0x%.8lux from 0x%.8lux\n", p, callerpc);
492 	leakaudit();
493 }
494 
495 static void
496 leakrealloc(void *newp, void *oldp, ulong callerpc)
497 {
498 	leakfree(oldp, callerpc);
499 	leakalloc(newp, callerpc);
500 }
501 #endif
502 
503 
504 #ifdef LEAKHUNT
505 static void *_realloc(void *p, ulong size, ulong callerpc)
506 #else
507 void *
508 logfsrealloc(void *p, ulong size)
509 #endif
510 {
511 	void *q;
512 	ulong osize;
513 	if (waserror()) {
514 		print("wobbly thrown in memory allocator: %s\n", up->env->errstr);
515 		nexterror();
516 	}
517 	if (p == nil) {
518 		q = smalloc(size);
519 		poperror();
520 #ifdef LEAKHUNT
521 		leakrealloc(q, nil, callerpc);
522 #endif
523 		return q;
524 	}
525 	q = realloc(p, size);
526 	if (q) {
527 		poperror();
528 #ifdef LEAKHUNT
529 		leakrealloc(q, p, callerpc);
530 #endif
531 		return q;
532 	}
533 	q = smalloc(size);
534 	osize = msize(p);
535 	if (osize > size)
536 		osize = size;
537 	memmove(q, p, osize);
538 	free(p);
539 	poperror();
540 #ifdef LEAKHUNT
541 	leakrealloc(q, p, callerpc);
542 #endif
543 	return q;
544 }
545 
546 #ifdef LEAKHUNT
547 void *
548 logfsrealloc(void *p, ulong size)
549 {
550 	return _realloc(p, size, getcallerpc(&p));
551 }
552 
553 void *
554 nandfsrealloc(void *p, ulong size)
555 {
556 	return _realloc(p, size, getcallerpc(&p));
557 }
558 #else
559 void *
560 nandfsrealloc(void *p, ulong size)
561 {
562 	return logfsrealloc(p, size);
563 }
564 #endif
565 
566 void
567 logfsfreemem(void *p)
568 {
569 #ifdef LEAKHUNT
570 	leakfree(p, getcallerpc(&p));
571 #endif
572 	free(p);
573 }
574 
575 void
576 nandfsfreemem(void *p)
577 {
578 #ifdef LEAKHUNT
579 	leakfree(p, getcallerpc(&p));
580 #endif
581 	free(p);
582 }
583 
584 static Devlogfs *
585 devlogfsconfig(char *name, char *device)
586 {
587 	Devlogfs *newl, *l;
588 	int i;
589 	int n;
590 	char buf[100], *fields[8];
591 	long rawblocksize, rawsize;
592 
593 	newl = nil;
594 
595 	qlock(&devlogfslist.configqlock);
596 
597 	if (waserror()) {
598 		qunlock(&devlogfslist.configqlock);
599 		devlogfsfree(newl);
600 		nexterror();
601 	}
602 
603 	rlock(&devlogfslist.rwlock);
604 	for (l = devlogfslist.head; l; l = l->next)
605 		if (strcmp(l->name, name) == 0) {
606 			runlock(&devlogfslist.rwlock);
607 			error(Einuse);
608 		}
609 
610 	/* horrid n^2 solution to finding a unique instance number */
611 
612 	for (i = 0;; i++) {
613 		for (l = devlogfslist.head; l; l = l->next)
614 			if (l->instance == i)
615 				break;
616 		if (l == nil)
617 			break;
618 	}
619 	runlock(&devlogfslist.rwlock);
620 
621 	newl = emalloc(sizeof(Devlogfs));
622 	newl->instance = i;
623 	newl->name = estrdup(name);
624 	newl->device = estrdup(device);
625 	newl->filename[Qfs - Qfs] = estrconcat(devlogfsprefix, name, nil);
626 	newl->filename[Qfsboot - Qfs] = estrconcat(devlogfsprefix, name, devlogfsbootsuffix, nil);
627 	newl->flash = devlogfskopen(device, nil, ORDWR);
628 	newl->flashctl = devlogfskopen(device, "ctl", ORDWR);
629 	newl->flashctl->offset = 0;
630 	if ((n = kchanio(newl->flashctl, buf, sizeof(buf), OREAD)) <= 0) {
631 		print("devlogfsconfig: read ctl failed: %s\n", up->env->errstr);
632 		error(up->env->errstr);
633 	}
634 
635 	if (n >= sizeof(buf))
636 		n = sizeof(buf) - 1;
637 	buf[n] = 0;
638 	n = getfields(buf, fields, nelem(fields), 1, " \t\n");
639 	newl->nand = 0;
640 	if (n >= 2) {
641 		/* detect NAND devices, and learn parameters from there */
642 		ulong manufacturer = strtoul(fields[0], nil, 16);
643 		ulong device = strtoul(fields[1], nil, 16);
644 		int d;
645 
646 		for (d = 0; d < sizeof(nandtab) / sizeof(nandtab[0]); d++) {
647 			if ((nandtab[d].manufacturer == manufacturer
648 				&& nandtab[d].device == device)
649 				|| (nandtab[d].manufacturer == 0
650 					&& (manufacturer == Toshiba || manufacturer == Samsung)
651 					&& nandtab[d].device == device))
652 			{
653 				if (DEVLOGFSDEBUG)
654 					print("devlogfsconfig: nand device detected\n");
655 				newl->nand = 1;
656 				break;
657 			}
658 		}
659 	}
660 	if (n < 4)
661 		error("unknown erase size");
662 	rawblocksize = strtol(fields[5], nil, 0);
663 	rawsize = strtol(fields[4], nil, 0)-strtol(fields[3], nil, 0);
664 	if (newl->nand == 0)
665 		error("only NAND supported at the moment");
666 	errorany(nandfsinit(newl, rawsize, rawblocksize, xread, xwrite, xerase, xsync, &newl->ll));
667 	wlock(&devlogfslist.rwlock);
668 	newl->next = devlogfslist.head;
669 	devlogfslist.head = newl;
670 	logfsfreemem(devlogfslist.defname);
671 	devlogfslist.defname = nil;
672 	if (waserror()) {
673 	}
674 	else {
675 		devlogfslist.defname = estrdup(name);
676 		poperror();
677 	}
678 	wunlock(&devlogfslist.rwlock);
679 	poperror();
680 	qunlock(&devlogfslist.configqlock);
681 	return newl;
682 }
683 
684 void
685 devlogfsunconfig(Devlogfs *devlogfs)
686 {
687 	Devlogfs **lp;
688 
689 	qlock(&devlogfslist.configqlock);
690 
691 	if (waserror()) {
692 		qunlock(&devlogfslist.configqlock);
693 		nexterror();
694 	}
695 
696 	wlock(&devlogfslist.rwlock);
697 
698 	if (waserror()) {
699 		wunlock(&devlogfslist.rwlock);
700 		nexterror();
701 	}
702 
703 	for (lp = &devlogfslist.head; *lp && (*lp) != devlogfs; lp = &(*lp)->next)
704 		;
705 	if (*lp == nil) {
706 		if (DEVLOGFSBAD)
707 			print("devlogfsunconfig: not in list\n");
708 	}
709 	else
710 		*lp = devlogfs->next;
711 
712 	poperror();
713 	wunlock(&devlogfslist.rwlock);
714 
715 	/* now invisible to the naked eye */
716 	devlogfsfree(devlogfs);
717 	poperror();
718 	qunlock(&devlogfslist.configqlock);
719 }
720 
721 static void
722 devlogfsllopen(Devlogfs *l)
723 {
724 	qlock(&l->qlock);
725 	if (waserror()) {
726 		qunlock(&l->qlock);
727 		nexterror();
728 	}
729 	if (l->lb == nil)
730 		errorany(logfsbootopen(l->ll, 0, 0, l->logfstrace, 1, &l->lb));
731 	l->state = BootOpen;
732 	poperror();
733 	qunlock(&l->qlock);
734 }
735 
736 static void
737 devlogfsllformat(Devlogfs *l, long bootsize)
738 {
739 	qlock(&l->qlock);
740 	if (waserror()) {
741 		qunlock(&l->qlock);
742 		nexterror();
743 	}
744 	if (l->lb == nil)
745 		errorany(logfsformat(l->ll, 0, 0, bootsize, l->logfstrace));
746 	poperror();
747 	qunlock(&l->qlock);
748 }
749 
750 static Chan *
751 devlogfsattach(char *spec)
752 {
753 	Chan *c;
754 #ifdef CALLTRACE
755 	print("devlogfsattach(spec = %s) - start\n", spec);
756 #endif
757 	/* create the identity store on first attach */
758 	if (is == nil)
759 		errorany(logfsisnew(&is));
760 	c =  devattach(0x29f, spec);
761 //	c =  devattach(L'ʟ', spec);
762 #ifdef CALLTRACE
763 	print("devlogfsattach(spec = %s) - return %.8lux\n", spec, (ulong)c);
764 #endif
765 	return c;
766 }
767 
768 static Walkqid*
769 devlogfswalk(Chan *c, Chan *nc, char **name, int nname)
770 {
771 	int instance, qid, qt, clone;
772 	Walkqid *wq;
773 
774 #ifdef CALLTRACE
775 	print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - start\n",
776 		(ulong)c, (ulong)nc, (ulong)name, nname);
777 #endif
778 	clone = 0;
779 	if(nc == nil){
780 		nc = devclone(c);
781 		nc->type = 0;
782 		SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
783 		if(DATAQID(qid, qt))
784 			nc->aux = devlogfsget(instance);
785 		clone = 1;
786 	}
787 	wq = devwalk(c, nc, name, nname, 0, 0, devlogfsgen);
788 	if (wq == nil || wq->nqid < nname) {
789 		if(clone)
790 			cclose(nc);
791 	}
792 	else if (clone) {
793 		wq->clone = nc;
794 		nc->type = c->type;
795 	}
796 #ifdef CALLTRACE
797 	print("devlogfswalk(c = 0x%.8lux, nc = 0x%.8lux, name = 0x%.8lux, nname = %d) - return\n",
798 		(ulong)c, (ulong)nc, (ulong)name, nname);
799 #endif
800 	return wq;
801 }
802 
803 static int
804 devlogfsstat(Chan *c, uchar *dp, int n)
805 {
806 #ifdef CALLTRACE
807 	print("devlogfsstat(c = 0x%.8lux, dp = 0x%.8lux n= %d)\n",
808 		(ulong)c, (ulong)dp, n);
809 #endif
810 	return devstat(c, dp, n, 0, 0, devlogfsgen);
811 }
812 
813 static Chan*
814 devlogfsopen(Chan *c, int omode)
815 {
816 	int instance, qid, qt;
817 
818 	omode = openmode(omode);
819 	SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
820 #ifdef CALLTRACE
821 	print("devlogfsopen(c = 0x%.8lux, omode = %o, instance = %d, qid = %d, qt = %d)\n",
822 		(ulong)c, omode, instance, qid, qt);
823 #endif
824 
825 
826 	rlock(&devlogfslist.rwlock);
827 	if (waserror()) {
828 		runlock(&devlogfslist.rwlock);
829 #ifdef CALLTRACE
830 		print("devlogfsopen(c = 0x%.8lux, omode = %o) - error %s\n", (ulong)c, omode, up->env->errstr);
831 #endif
832 		nexterror();
833 	}
834 
835 	if (DATAQID(qid, qt)) {
836 		Devlogfs *d;
837 		d = devlogfsfind(instance);
838 		if (d == nil)
839 			error(Enodev);
840 		if (strcmp(up->env->user, eve) != 0)
841 			error(Eperm);
842 		if (qid == Qfs && d->state != BootOpen)
843 			error(Eperm);
844 		if (d->server == nil) {
845 			errorany(logfsservernew(d->lb, d->ll, is, d->openflags, d->logfstrace, &d->server));
846 			d->state = NeedVersion;
847 		}
848 		c = devopen(c, omode, 0, 0, devlogfsgennolock);
849 		incref(&d->ref);
850 		c->aux = d;
851 	}
852 	else if (qid == Qctl || qid == Qusers) {
853 		if (strcmp(up->env->user, eve) != 0)
854 			error(Eperm);
855 		c = devopen(c, omode, 0, 0, devlogfsgennolock);
856 	}
857 	else
858 		c = devopen(c, omode, 0, 0, devlogfsgennolock);
859 	poperror();
860 	runlock(&devlogfslist.rwlock);
861 #ifdef CALLTRACE
862 	print("devlogfsopen(c = 0x%.8lux, omode = %o) - return\n", (ulong)c, omode);
863 #endif
864 	return c;
865 }
866 
867 static void
868 devlogfsclose(Chan *c)
869 {
870 	int instance, qid, qt;
871 #ifdef CALLTRACE
872 	print("devlogfsclose(c = 0x%.8lux)\n", (ulong)c);
873 #endif
874 	SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
875 	USED(instance);
876 	if(DATAQID(qid, qt) && (c->flag & COPEN) != 0) {
877 		Devlogfs *d;
878 		d = c->aux;
879 		qlock(&d->qlock);
880 		if (qid == Qfs && d->state == Attached) {
881 			logfsserverflush(d->server);
882 			logfsserverfree(&d->server);
883 			d->state = BootOpen;
884 		}
885 		qunlock(&d->qlock);
886 		decref(&d->ref);
887 	}
888 #ifdef CALLTRACE
889 	print("devlogfsclose(c = 0x%.8lux) - return\n", (ulong)c);
890 #endif
891 }
892 
893 typedef char *(SMARTIOFN)(void *magic, void *buf, long n, ulong offset, int write);
894 
895 void
896 smartio(SMARTIOFN *io, void *magic, void *buf, long n, ulong offset, long blocksize, int write)
897 {
898 	void *tmp = nil;
899 	ulong blocks, toread;
900 
901 	if (waserror()) {
902 		logfsfreemem(tmp);
903 		nexterror();
904 	}
905 	if (offset % blocksize) {
906 		ulong aoffset;
907 		int tmpoffset;
908 		int tocopy;
909 
910 		if (tmp == nil)
911 			tmp = emalloc(blocksize);
912 		aoffset = offset / blocksize;
913 		aoffset *= blocksize;
914 		errorany((*io)(magic, tmp, blocksize, aoffset, 0));
915 		tmpoffset = offset - aoffset;
916 		tocopy = blocksize - tmpoffset;
917 		if (tocopy > n)
918 			tocopy = n;
919 		if (write) {
920 			memmove((uchar *)tmp + tmpoffset, buf, tocopy);
921 			errorany((*io)(magic, tmp, blocksize, aoffset, 1));
922 		}
923 		else
924 			memmove(buf, (uchar *)tmp + tmpoffset, tocopy);
925 		buf = (uchar *)buf + tocopy;
926 		n -= tocopy;
927 		offset = aoffset + blocksize;
928 	}
929 	blocks = n / blocksize;
930 	toread = blocks * blocksize;
931 	errorany((*io)(magic, buf, toread, offset, write));
932 	buf = (uchar *)buf + toread;
933 	n -= toread;
934 	offset += toread;
935 	if (n) {
936 		if (tmp == nil)
937 			tmp = emalloc(blocksize);
938 		errorany((*io)(magic, tmp, blocksize, offset, 0));
939 		if (write) {
940 			memmove(tmp, buf, n);
941 			errorany((*io)(magic, tmp, blocksize, offset, 1));
942 		}
943 		memmove(buf, tmp, n);
944 	}
945 	poperror();
946 	logfsfreemem(tmp);
947 }
948 
949 static int
950 readok(void *a)
951 {
952 	Devlogfs *d = a;
953 	return d->reading;
954 }
955 
956 static int
957 writeok(void *a)
958 {
959 	Devlogfs *d = a;
960 	return !d->reading;
961 }
962 
963 long
964 devlogfsserverread(Devlogfs *d, void *buf, long n)
965 {
966 	if (d->state == Hungup)
967 		error(Ehungup);
968 	Sleep(&d->readrendez, readok, d);
969 	if (n > d->readcount)
970 		n = d->readcount;
971 	memmove(buf, d->readp, n);
972 	d->readp += n;
973 	d->readcount -= n;
974 	if (d->readcount == 0) {
975 		d->reading = 0;
976 		Wakeup(&d->writerendez);
977 	}
978 	return n;
979 }
980 
981 static void
982 reply(Devlogfs *d)
983 {
984 	d->readp = d->readbuf;
985 	d->readcount = convS2M(&d->out, d->readp, d->readbufsize);
986 //print("reply is %d bytes\n", d->readcount);
987 	if (d->readcount == 0)
988 		panic("logfs: reply: did not fit\n");
989 	d->reading = 1;
990 	Wakeup(&d->readrendez);
991 }
992 
993 static void
994 rerror(Devlogfs *d, char *ename)
995 {
996 	d->out.type = Rerror;
997 	d->out.ename = ename;
998 	reply(d);
999 }
1000 
1001 static struct {
1002 	QLock qlock;
1003 	int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen);
1004 	void *magic;
1005 	Devlogfs *d;
1006 	int line;
1007 } dump;
1008 
1009 static void *
1010 extentdumpinit(Devlogfs *d, int argc, char **argv)
1011 {
1012 	int *p;
1013 	ulong path;
1014 	ulong flashaddr, length;
1015 	long block;
1016 	int page, offset;
1017 	if (argc != 1)
1018 		error(Ebadarg);
1019 	path = strtoul(argv[0], 0, 0);
1020 	errorany(logfsserverreadpathextent(d->server, path, 0, &flashaddr, &length, &block, &page, &offset));
1021 	p = emalloc(sizeof(ulong));
1022 	*p = path;
1023 	return p;
1024 }
1025 
1026 static int
1027 extentdumpread(void *magic, Devlogfs *d, int line, char *buf, int buflen)
1028 {
1029 	ulong *p = magic;
1030 	ulong flashaddr, length;
1031 	long block;
1032 	int page, offset;
1033 	USED(d);
1034 	errorany(logfsserverreadpathextent(d->server, *p, line, &flashaddr, &length, &block, &page, &offset));
1035 	if (length == 0)
1036 		return 0;
1037 	return snprint(buf, buflen, "%.8ux %ud %ld %d %d\n", flashaddr, length, block, page, offset);
1038 }
1039 
1040 void
1041 devlogfsdumpinit(Devlogfs *d,
1042 	void *(*init)(Devlogfs *d, int argc, char **argv),
1043 	int (*read)(void *magic, Devlogfs *d, int line, char *buf, int buflen), int argc, char **argv)
1044 {
1045 	qlock(&dump.qlock);
1046 	if (waserror()) {
1047 		qunlock(&dump.qlock);
1048 		nexterror();
1049 	}
1050 	if (d) {
1051 		if (d->state < NeedVersion)
1052 			error("not mounted");
1053 		qlock(&d->qlock);
1054 		if (waserror()) {
1055 			qunlock(&d->qlock);
1056 			nexterror();
1057 		}
1058 	}
1059 	if (dump.magic) {
1060 		logfsfreemem(dump.magic);
1061 		dump.magic = nil;
1062 	}
1063 	dump.d = d;
1064 	dump.magic = (*init)(d, argc, argv);
1065 	dump.read = read;
1066 	dump.line = 0;
1067 	if (d) {
1068 		poperror();
1069 		qunlock(&d->qlock);
1070 	}
1071 	poperror();
1072 	qunlock(&dump.qlock);
1073 }
1074 
1075 long
1076 devlogfsdumpread(char *buf, int buflen)
1077 {
1078 	char *tmp = nil;
1079 	long n;
1080 	qlock(&dump.qlock);
1081 	if (waserror()) {
1082 		logfsfreemem(tmp);
1083 		qunlock(&dump.qlock);
1084 		nexterror();
1085 	}
1086 	if (dump.magic == nil)
1087 		error(Eio);
1088 	tmp = emalloc(READSTR);
1089 	if (dump.d) {
1090 		if (dump.d->state < NeedVersion)
1091 			error("not mounted");
1092 		qlock(&dump.d->qlock);
1093 		if (waserror()) {
1094 			qunlock(&dump.d->qlock);
1095 			nexterror();
1096 		}
1097 	}
1098 	n = (*dump.read)(dump.magic, dump.d, dump.line, tmp, READSTR);
1099 	if (n) {
1100 		dump.line++;
1101 		n = readstr(0, buf, buflen, tmp);
1102 	}
1103 	if (dump.d) {
1104 		poperror();
1105 		qunlock(&dump.d->qlock);
1106 	}
1107 	logfsfreemem(tmp);
1108 	poperror();
1109 	qunlock(&dump.qlock);
1110 	return n;
1111 }
1112 
1113 void
1114 devlogfsserverlogsweep(Devlogfs *d, int justone)
1115 {
1116 	int didsomething;
1117 	if (d->state < NeedVersion)
1118 		error("not mounted");
1119 	qlock(&d->qlock);
1120 	if (waserror()) {
1121 		qunlock(&d->qlock);
1122 		nexterror();
1123 	}
1124 	errorany(logfsserverlogsweep(d->server, justone, &didsomething));
1125 	poperror();
1126 	qunlock(&d->qlock);
1127 }
1128 
1129 void
1130 devlogfsserverwrite(Devlogfs *d, void *buf, long n)
1131 {
1132 	int locked = 0;
1133 	if (d->state == Hungup)
1134 		error(Ehungup);
1135 	Sleep(&d->writerendez, writeok, d);
1136 	if (convM2S(buf, n, &d->in) != n) {
1137 		/*
1138 		 * someone is writing drivel; have nothing to do with them anymore
1139 		 * most common cause; trying to mount authenticated
1140 		 */
1141 		d->state = Hungup;
1142 		error(Ehungup);
1143 	}
1144 	d->out.tag = d->in.tag;
1145 	d->out.fid = d->in.fid;
1146 	d->out.type = d->in.type + 1;
1147 	if (waserror()) {
1148 		if (locked)
1149 			qunlock(&d->qlock);
1150 		rerror(d, up->env->errstr);
1151 		return;
1152 	}
1153 	if (d->in.type != Tversion && d->in.type != Tattach) {
1154 		if (d->state != Attached)
1155 			error("must be attached");
1156 		qlock(&d->qlock);
1157 		locked = 1;
1158 	}
1159 	switch (d->in.type) {
1160 	case Tauth:
1161 		error("no authentication needed");
1162 	case Tversion: {
1163 		char *rversion;
1164 		if (d->state != NeedVersion)
1165 			error("unexpected Tversion");
1166 		 if (d->in.tag != NOTAG)
1167 			error("protocol botch");
1168 		/*
1169 		 * check the version string
1170 		 */
1171 		if (strcmp(d->in.version, devlogfs9pversion) != 0)
1172 			rversion = "unknown";
1173 		else
1174 			rversion = devlogfs9pversion;
1175 		/*
1176 		 * allocate the reply buffer
1177 		 */
1178 		d->readbufsize = d->in.msize;
1179 		if (d->readbufsize > MAXMSIZE)
1180 			d->readbufsize = MAXMSIZE;
1181 		d->readbuf = emalloc(d->readbufsize);
1182 		/*
1183 		 * compose the Rversion
1184 		 */
1185 		d->out.msize = d->readbufsize;
1186 		d->out.version = rversion;
1187 		d->state = NeedAttach;
1188 		break;
1189 	}
1190 	case Tattach:
1191 		if (d->state != NeedAttach)
1192 			error("unexpected attach");
1193 		if (d->in.afid != NOFID)
1194 			error("unexpected afid");
1195 		errorany(logfsserverattach(d->server, d->in.fid, d->in.uname, &d->out.qid));
1196 		d->state = Attached;
1197 		break;
1198 	case Tclunk:
1199 		errorany(logfsserverclunk(d->server, d->in.fid));
1200 		break;
1201 	case Tcreate:
1202 		errorany(logfsservercreate(d->server, d->in.fid, d->in.name, d->in.perm, d->in.mode, &d->out.qid));
1203 		d->out.iounit = d->readbufsize - 11;
1204 		break;
1205 	case Tflush:
1206 		break;
1207 	case Topen:
1208 		errorany(logfsserveropen(d->server, d->in.fid, d->in.mode, &d->out.qid));
1209 		d->out.iounit = d->readbufsize - 11;
1210 		break;
1211 	case Tread:
1212 		d->out.data = (char *)d->readbuf + 11;
1213 		/* TODO - avoid memmove */
1214 		errorany(logfsserverread(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->out.data,
1215 			d->readbufsize - 11, &d->out.count));
1216 		break;
1217 	case Tremove:
1218 		errorany(logfsserverremove(d->server, d->in.fid));
1219 		break;
1220 	case Tstat:
1221 		d->out.stat = d->readbuf + 9;
1222 		/* TODO - avoid memmove */
1223 		errorany(logfsserverstat(d->server, d->in.fid, d->out.stat, d->readbufsize - 9, &d->out.nstat));
1224 //		print("nstat %d\n", d->out.nstat);
1225 		break;
1226 	case Twalk:
1227 		errorany(logfsserverwalk(d->server, d->in.fid, d->in.newfid,
1228 			d->in.nwname, d->in.wname, &d->out.nwqid, d->out.wqid));
1229 		break;
1230 	case Twrite:
1231 		errorany(logfsserverwrite(d->server, d->in.fid, d->in.offset, d->in.count, (uchar *)d->in.data,
1232 			&d->out.count));
1233 		break;
1234 	case Twstat:
1235 		errorany(logfsserverwstat(d->server, d->in.fid, d->in.stat, d->in.nstat));
1236 		break;
1237 	default:
1238 		print("devlogfsserverwrite: msg %d unimplemented\n", d->in.type);
1239 		error("unimplemented");
1240 	}
1241 	poperror();
1242 	if (locked)
1243 		qunlock(&d->qlock);
1244 	reply(d);
1245 }
1246 
1247 static long
1248 devlogfsread(Chan *c, void *buf, long n, vlong off)
1249 {
1250 	int instance, qid, qt;
1251 
1252 	SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
1253 	USED(instance);
1254 #ifdef CALLTRACE
1255 	print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n",
1256 		(ulong)c, (ulong)buf, n, instance, qid, qt);
1257 #endif
1258 	if(qt & QTDIR) {
1259 #ifdef CALLTRACE
1260 		print("devlogfsread(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - calling devdirread\n",
1261 			(ulong)c, (ulong)buf, n, instance, qid, qt);
1262 #endif
1263 		return devdirread(c, buf, n, 0, 0, devlogfsgen);
1264 	}
1265 
1266 	if(DATAQID(qid, qt)) {
1267 		if (qid == Qfsboot) {
1268 			Devlogfs *l = c->aux;
1269 			qlock(&l->bootqlock);
1270 			if (waserror()) {
1271 				qunlock(&l->bootqlock);
1272 				nexterror();
1273 			}
1274 			smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 0);
1275 			poperror();
1276 			qunlock(&l->bootqlock);
1277 			return n;
1278 		}
1279 		else if (qid == Qfs) {
1280 			Devlogfs *d = c->aux;
1281 			return devlogfsserverread(d, buf, n);
1282 		}
1283 		error(Eio);
1284 	}
1285 
1286 	if (qid == Qusers) {
1287 		long nr;
1288 		errorany(logfsisusersread(is, buf, n, (ulong)off, &nr));
1289 		return nr;
1290 	}
1291 	else if (qid == Qdump)
1292 		return devlogfsdumpread(buf, n);
1293 
1294 	if (qid != Qctl)
1295 		error(Egreg);
1296 
1297 	return 0;
1298 }
1299 
1300 static long
1301 devlogfswrite(Chan *c, void *buf, long n, vlong off)
1302 {
1303 	char cmd[64], *realfields[6];
1304 	int i;
1305 	int instance, qid, qt;
1306 
1307 	if(n <= 0)
1308 		return 0;
1309 	SPLITPATH(c->qid.path, c->qid.type, instance, qid, qt);
1310 #ifdef CALLTRACE
1311 	print("devlogfswrite(c = 0x%.8lux, buf = 0x%.8lux, n = %ld, instance = %d, qid = %d, qt = %d) - start\n",
1312 		(ulong)c, (ulong)buf, n, instance, qid, qt);
1313 #endif
1314 	USED(instance);
1315 	if(DATAQID(qid, qt)){
1316 		if (qid == Qfsboot) {
1317 			Devlogfs *l = c->aux;
1318 			qlock(&l->bootqlock);
1319 			if (waserror()) {
1320 				qunlock(&l->bootqlock);
1321 				nexterror();
1322 			}
1323 			smartio((SMARTIOFN *)logfsbootio, l->lb, buf, n, off, logfsbootgetiosize(l->lb), 1);
1324 			poperror();
1325 			qunlock(&l->bootqlock);
1326 			return n;
1327 		}
1328 		else if (qid == Qfs) {
1329 			Devlogfs *d = c->aux;
1330 			devlogfsserverwrite(d, buf, n);
1331 			return n;
1332 		}
1333 		error(Eio);
1334 	}
1335 	else if (qid == Qctl) {
1336 		Devlogfs *l = nil;
1337 		char **fields;
1338 
1339 		if(n > sizeof(cmd)-1)
1340 			n = sizeof(cmd)-1;
1341 		memmove(cmd, buf, n);
1342 		cmd[n] = 0;
1343 		i = getfields(cmd, realfields, 6, 1, " \t\n");
1344 //print("i = %d\n", i);
1345 		if (i <= 0)
1346 			error(Ebadarg);
1347 		fields = realfields;
1348 		if (i == 3 && strcmp(fields[0], "uname") == 0) {
1349 			switch (fields[2][0]) {
1350 			default:
1351 				errorany(logfsisgroupcreate(is, fields[1], fields[2]));
1352 				break;
1353 			case ':':
1354 				errorany(logfsisgroupcreate(is, fields[1], fields[2] + 1));
1355 				break;
1356 			case '%':
1357 				errorany(logfsisgrouprename(is, fields[1], fields[2] + 1));
1358 				break;
1359 			case '=':
1360 				errorany(logfsisgroupsetleader(is, fields[1], fields[2] + 1));
1361 				break;
1362 			case '+':
1363 				errorany(logfsisgroupaddmember(is, fields[1], fields[2] + 1));
1364 				break;
1365 			case '-':
1366 				errorany(logfsisgroupremovemember(is, fields[1], fields[2] + 1));
1367 				break;
1368 			}
1369 			i = 0;
1370 		}
1371 		if (i == 4 && strcmp(fields[0], "fsys") == 0 && strcmp(fields[2], "config") == 0) {
1372 			l = devlogfsconfig(fields[1], fields[3]);
1373 			i = 0;
1374 		}
1375 		else if (i >= 2 && strcmp(fields[0], "fsys") == 0) {
1376 			l = devlogfssetdefname(fields[1]);
1377 			if (l == nil)
1378 				error(Ebadarg);
1379 			i -= 2;
1380 			fields += 2;
1381 		}
1382 		if (i != 0) {
1383 			if (l == nil)
1384 				l = devlogfssetdefname(nil);
1385 			if (i >= 1 && strcmp(fields[0], "open") == 0) {
1386 				int a;
1387 				if (l == nil)
1388 					error(Ebadarg);
1389 				for (a = 1; a < i; a++)
1390 					if (fields[a][0] == '-')
1391 						switch (fields[a][1]) {
1392 						case 'P':
1393 							l->openflags |= LogfsOpenFlagNoPerm;
1394 							break;
1395 						case 'W':
1396 							l->openflags |= LogfsOpenFlagWstatAllow;
1397 							break;
1398 						default:
1399 							error(Ebadarg);
1400 						}
1401 				devlogfsllopen(l);
1402 				i = 0;
1403 			}
1404 			else if (i == 2 && strcmp(fields[0], "format") == 0) {
1405 				if (l == nil)
1406 					error(Ebadarg);
1407 				devlogfsllformat(l, strtol(fields[1], nil, 0));
1408 				i = 0;
1409 			}
1410 			else if (i >= 1 && strcmp(fields[0], "sweep") == 0) {
1411 				if (l == nil)
1412 					error(Ebadarg);
1413 				devlogfsserverlogsweep(l, 0);
1414 				i = 0;
1415 			}
1416 			else if (i >= 1 && strcmp(fields[0], "sweepone") == 0) {
1417 				if (l == nil)
1418 					error(Ebadarg);
1419 				devlogfsserverlogsweep(l, 1);
1420 				i = 0;
1421 			}
1422 			else if (i <= 2&& strcmp(fields[0], "trace") == 0) {
1423 				if (l == nil)
1424 					error(Ebadarg);
1425 				l->logfstrace = i > 1 ? strtol(fields[1], nil, 0) : 0;
1426 				if (l->server)
1427 					logfsservertrace(l->server, l->logfstrace);
1428 				if (l->lb)
1429 					logfsboottrace(l->lb, l->logfstrace);
1430 				i = 0;
1431 			}
1432 			else if (i == 1 && strcmp(fields[0], "unconfig") == 0) {
1433 				if (l == nil)
1434 					error(Ebadarg);
1435 				if (l->ref.ref > 0)
1436 					error(Einuse);
1437 				devlogfsunconfig(l);
1438 				i = 0;
1439 			}
1440 			else if (i == 2 && strcmp(fields[0], "extent") == 0) {
1441 				if (l == nil)
1442 					error(Ebadarg);
1443 				devlogfsdumpinit(l, extentdumpinit, extentdumpread, i - 1, fields + 1);
1444 				i = 0;
1445 			}
1446 			else if (i >= 2 && strcmp(fields[0], "test") == 0) {
1447 				if (l == nil)
1448 					error(Ebadarg);
1449 				errorany(logfsservertestcmd(l->server, i - 1, fields + 1));
1450 				i = 0;
1451 			}
1452 #ifdef LEAKHUNT
1453 			else if (i == 1 && strcmp(fields[0], "leakaudit") == 0) {
1454 				leakaudit();
1455 				i = 0;
1456 			}
1457 #endif
1458 		}
1459 		if (i != 0)
1460 			error(Ebadarg);
1461 		return n;
1462 	}
1463 	error(Egreg);
1464 	return 0;		/* not reached */
1465 }
1466 
1467 static void
1468 devlogfsfree(Devlogfs *devlogfs)
1469 {
1470 	if (devlogfs != nil) {
1471 		int i;
1472 		logfsfreemem(devlogfs->device);
1473 		logfsfreemem(devlogfs->name);
1474 		for (i = 0; i < Qend - Qfs; i++)
1475 			logfsfreemem(devlogfs->filename[i]);
1476 		cclose(devlogfs->flash);
1477 		cclose(devlogfs->flashctl);
1478 		qlock(&devlogfs->qlock);
1479 		logfsserverfree(&devlogfs->server);
1480 		logfsbootfree(devlogfs->lb);
1481 		if (devlogfs->ll)
1482 			(*devlogfs->ll->free)(devlogfs->ll);
1483 		logfsfreemem(devlogfs->readbuf);
1484 		qunlock(&devlogfs->qlock);
1485 		logfsfreemem(devlogfs);
1486 	}
1487 }
1488 
1489 #ifdef EMU
1490 ulong
1491 logfsnow(void)
1492 {
1493 	extern vlong timeoffset;
1494 	return (timeoffset + osusectime()) / 1000000;
1495 }
1496 #endif
1497 
1498 Dev logfsdevtab = {
1499 	0x29f,
1500 //	L'ʟ',
1501 	"logfs",
1502 
1503 #ifndef EMU
1504 	devreset,
1505 #endif
1506 	devinit,
1507 #ifndef EMU
1508 	devshutdown,
1509 #endif
1510 	devlogfsattach,
1511 	devlogfswalk,
1512 	devlogfsstat,
1513 	devlogfsopen,
1514 	devcreate,
1515 	devlogfsclose,
1516 	devlogfsread,
1517 	devbread,
1518 	devlogfswrite,
1519 	devbwrite,
1520 	devremove,
1521 	devwstat,
1522 };
1523