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
errorany(char * errmsg)106 errorany(char *errmsg)
107 {
108 if (errmsg)
109 error(errmsg);
110 }
111
112 static void *
emalloc(ulong size)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 *
estrdup(char * q)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 *
estrconcat(char * a,...)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
gen(Chan * c,int i,Dir * dp,int lockit)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
devlogfsgen(Chan * c,char * n,Dirtab * tab,int ntab,int i,Dir * dp)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
devlogfsgennolock(Chan * c,char * n,Dirtab * tab,int ntab,int i,Dir * dp)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 *
devlogfsfind(int instance)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 *
devlogfsget(int instance)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 *
devlogfsfindbyname(char * name)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 *
devlogfssetdefname(char * name)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 *
devlogfskopen(char * name,char * suffix,int mode)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 *
xread(void * a,void * buf,long nbytes,ulong offset)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 *
xwrite(void * a,void * buf,long nbytes,ulong offset)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 *
xerase(void * a,long address)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 *
xsync(void * a)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
leakalloc(void * p,ulong callerpc)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
leakaudit(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
leakfree(void * p,ulong callerpc)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
leakrealloc(void * newp,void * oldp,ulong callerpc)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
_realloc(void * p,ulong size,ulong callerpc)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 *
logfsrealloc(void * p,ulong size)527 logfsrealloc(void *p, ulong size)
528 {
529 return _realloc(p, size, getcallerpc(&p));
530 }
531
532 void *
nandfsrealloc(void * p,ulong size)533 nandfsrealloc(void *p, ulong size)
534 {
535 return _realloc(p, size, getcallerpc(&p));
536 }
537 #else
538 void *
nandfsrealloc(void * p,ulong size)539 nandfsrealloc(void *p, ulong size)
540 {
541 return logfsrealloc(p, size);
542 }
543 #endif
544
545 void
logfsfreemem(void * p)546 logfsfreemem(void *p)
547 {
548 #ifdef LEAKHUNT
549 leakfree(p, getcallerpc(&p));
550 #endif
551 free(p);
552 }
553
554 void
nandfsfreemem(void * p)555 nandfsfreemem(void *p)
556 {
557 #ifdef LEAKHUNT
558 leakfree(p, getcallerpc(&p));
559 #endif
560 free(p);
561 }
562
563 static Devlogfs *
devlogfsconfig(char * name,char * device)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
devlogfsunconfig(Devlogfs * devlogfs)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
devlogfsllopen(Devlogfs * l)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
devlogfsllformat(Devlogfs * l,long bootsize)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 *
devlogfsattach(char * spec)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*
devlogfswalk(Chan * c,Chan * nc,char ** name,int nname)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
devlogfsstat(Chan * c,uchar * dp,int n)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*
devlogfsopen(Chan * c,int omode)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
devlogfsclose(Chan * c)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
smartio(SMARTIOFN * io,void * magic,void * buf,long n,ulong offset,long blocksize,int write)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
readok(void * a)907 readok(void *a)
908 {
909 Devlogfs *d = a;
910 return d->reading;
911 }
912
913 static int
writeok(void * a)914 writeok(void *a)
915 {
916 Devlogfs *d = a;
917 return !d->reading;
918 }
919
920 static long
lfsrvread(Devlogfs * d,void * buf,long n)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
reply(Devlogfs * d)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
rerror(Devlogfs * d,char * ename)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 *
extentdumpinit(Devlogfs * d,int argc,char ** argv)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
extentdumpread(void * magic,Devlogfs * d,int line,char * buf,int buflen)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
devlogfsdumpinit(Devlogfs * d,void * (* init)(Devlogfs * d,int argc,char ** argv),int (* read)(void * magic,Devlogfs * d,int line,char * buf,int buflen),int argc,char ** argv)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
devlogfsdumpread(char * buf,int buflen)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
devlogfsserverlogsweep(Devlogfs * d,int justone)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
devlogfsserversync(Devlogfs * d)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
lfssrvwrite(Devlogfs * d,void * buf,long n)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
devlogfsread(Chan * c,void * buf,long n,vlong off)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
devlogfswrite(Chan * c,void * buf,long n,vlong off)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
devlogfsfree(Devlogfs * devlogfs)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
logfsnow(void)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