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