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