]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/hjfs/fs1.c
hjfs: disable shutdown when listening on network connections
[plan9front.git] / sys / src / cmd / hjfs / fs1.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <fcall.h>
5 #include "dat.h"
6 #include "fns.h"
7
8 int
9 chref(Fs *fs, uvlong r, int stat)
10 {
11         uvlong i;
12         int j;
13         ulong rc;
14         Buf *c;
15
16         i = fs->fstart + r / REFPERBLK;
17         j = r % REFPERBLK;
18         c = getbuf(fs->d, i, TREF, 0);
19         if(c == nil)
20                 return -1;
21         if(stat < 0 && c->refs[j] < -stat)
22                 c->refs[j] = 0;
23         else
24                 c->refs[j] += stat;
25         rc = c->refs[j];
26         if(stat != 0)
27                 c->op |= BDELWRI;
28         putbuf(c);
29         return rc;
30 }
31
32 static int
33 qidcmp(Qid *a, Qid *b)
34 {
35         if(a->type != b->type)
36                 return 1;
37         if(a->path != b->path){
38                 /* special case for dump, this is ok */
39                 if(a->path==ROOTQID && b->path==DUMPROOTQID)
40                         return 0;
41                 return 1;
42         }
43         return 0;
44 }
45
46 Dentry*
47 getdent(FLoc *l, Buf *b)
48 {
49         Dentry *d;
50
51         d = &b->de[l->deind];
52         if((d->mode & (DGONE | DALLOC)) == 0){
53                 dprint("hjfs: getdent: file gone, d=%llux, l=%llud/%d %llux, callerpc %#p\n",
54                         d->path, l->blk, l->deind, l->path, getcallerpc(&l));
55                 werrstr("phase error -- getdent");
56                 return nil;
57         }
58         if(qidcmp(d, l) != 0){
59                 dprint("hjfs: getdent: wrong qid d=%llux != l=%llud/%d %llux, callerpc %#p\n",
60                         d->path, l->blk, l->deind, l->path, getcallerpc(&l));
61                 werrstr("phase error -- getdent");
62                 return nil;
63         }
64         return d;
65 }
66
67 int
68 getfree(Fs *fs, uvlong *r)
69 {
70         Dev *d;
71         Buf *b;
72         uvlong i, l, e;
73         int j, have;
74
75         d = fs->d;
76         while(nbrecv(fs->freelist, &l) > 0){
77                 i = fs->fstart + l / REFPERBLK;
78                 j = l % REFPERBLK;
79                 b = getbuf(d, i, TREF, 0);
80                 if(b != nil){
81                         if(b->refs[j] == 0){
82                                 b->refs[j] = 1;
83                                 *r = l;
84                                 goto found;
85                         }
86                         putbuf(b);
87                 }
88         }
89
90         b = getbuf(d, SUPERBLK, TSUPERBLOCK, 0);
91         if(b == nil)
92                 return -1;
93         e = b->sb.fend;
94         putbuf(b);
95
96         have = 0;
97         for(l = 0, i = fs->fstart; i < e; i++){
98                 b = getbuf(d, i, TREF, 0);
99                 if(b == nil){
100                         l += REFPERBLK;
101                         continue;
102                 }
103                 for(j = 0; j < REFPERBLK; j++, l++)
104                         if(b->refs[j] == 0){
105                                 if(!have){
106                                         b->refs[j] = 1;
107                                         *r = l;
108                                         have = 1;
109                                 }
110                                 else if(nbsend(fs->freelist, &l) <= 0)
111                                         goto found;
112                         }
113                 if(have)
114                         goto found;
115                 putbuf(b);
116         }
117         werrstr("disk full");
118         return -1;
119 found:
120         b->op |= BDELWRI;
121         putbuf(b);
122         return 1;
123 }
124
125 int
126 putfree(Fs *fs, uvlong r)
127 {
128         int rc;
129
130         rc = chref(fs, r, -1);
131         if(rc < 0)
132                 return -1;
133         if(rc == 0)
134                 nbsend(fs->freelist, &r);
135         return 1;
136 }
137
138 static void
139 createroot(Fs *fs)
140 {
141         uvlong r;
142         Buf *c;
143         Dentry *d;
144
145         if(getfree(fs, &r) < 0)
146                 sysfatal("ream: getfree: %r");
147         c = getbuf(fs->d, r, TDENTRY, 1);
148         if(c == nil)
149         error:
150                 sysfatal("ream: getbuf: %r");
151         memset(c->de, 0, sizeof(c->de));
152         d = &c->de[0];
153         strcpy(d->name, "/");
154         d->mode = DALLOC | 0775;
155         d->path = ROOTQID;
156         d->type = QTDIR;
157         d->uid = -1;
158         d->muid = -1;
159         d->gid = -1;
160         d->mtime = time(0);
161         d->atime = d->mtime;
162         d++;
163         strcpy(d->name, "/");
164         d->mode = DALLOC | 0775;
165         d->path = ROOTQID;
166         d->type = QTDIR;
167         d->uid = -1;
168         d->muid = -1;
169         d->gid = -1;
170         d->mtime = time(0);
171         d->atime = d->mtime;
172         c->op |= BWRIM;
173         putbuf(c);
174         c = getbuf(fs->d, SUPERBLK, TSUPERBLOCK, 1);
175         if(c == nil)
176                 goto error;
177         fs->root = r;
178         c->sb.root = r;
179         putbuf(c);
180 }
181
182 void
183 writeusers(Fs *fs)
184 {
185         Chan *ch;
186         
187         ch = chanattach(fs, 0);
188         if(ch == nil)
189                 goto error;
190         ch->uid = -1;
191         chancreat(ch, "adm", DMDIR | 0775, OREAD);
192         chanclunk(ch);
193         ch = chanattach(fs, 0);
194         if(ch == nil)
195                 goto error;
196         ch->uid = -1;
197         if(chanwalk(ch, "adm") <= 0)
198                 goto error;
199         if(chanwalk(ch, "users") > 0){
200                 if(chanopen(ch, OWRITE|OTRUNC) <= 0)
201                         goto error;
202         }else if(chancreat(ch, "users", 0664, OWRITE) <= 0)
203                         goto error;
204         if(userssave(fs, ch) < 0){
205                 chanremove(ch);
206                 ch = nil;
207                 goto error;
208         }
209         chanclunk(ch);
210         return;
211 error:
212         if(ch != nil)
213                 chanclunk(ch);
214         dprint("writeusers: %r\n");
215 }
216
217 static void
218 readusers(Fs *fs)
219 {
220         Chan *ch;
221         
222         ch = chanattach(fs, 0);
223         if(ch == nil)
224                 goto err;
225         ch->uid = -1;
226         if(chanwalk(ch, "adm") <= 0)
227                 goto err;
228         if(chanwalk(ch, "users") <= 0)
229                 goto err;
230         if(chanopen(ch, OREAD) < 0)
231                 goto err;
232         if(usersload(fs, ch) < 0)
233                 goto err;
234         chanclunk(ch);
235         return;
236 err:
237         if(ch != nil)
238                 chanclunk(ch);
239         dprint("hjfs: readusers: %r\nhjfs: using default user db\n");
240 }
241
242 void
243 ream(Fs *fs)
244 {
245         Dev *d;
246         Buf *b, *c;
247         uvlong i, firsti, lasti;
248         int j, je;
249         
250         d = fs->d;
251         dprint("hjfs: reaming %s\n", d->name);
252         b = getbuf(d, SUPERBLK, TSUPERBLOCK, 1);
253         if(b == nil)
254         err:
255                 sysfatal("ream: getbuf: %r");
256         memset(&b->sb, 0, sizeof(b->sb));
257         b->sb.magic = SUPERMAGIC;
258         b->sb.size = d->size;
259         b->sb.fstart = SUPERBLK + 1;
260         fs->fstart = b->sb.fstart;
261         b->sb.fend = b->sb.fstart + HOWMANY(b->sb.size * 3, RBLOCK);
262         b->sb.qidpath = DUMPROOTQID + 1;
263         firsti = b->sb.fstart + SUPERBLK / REFPERBLK;
264         lasti = b->sb.fstart + b->sb.fend / REFPERBLK;
265         for(i = b->sb.fstart; i < b->sb.fend; i++){
266                 c = getbuf(d, i, TREF, 1);
267                 if(c == nil)
268                         goto err;
269                 memset(c->refs, 0, sizeof(b->data));
270                 if(i >= firsti && i <= lasti){
271                         j = 0;
272                         je = REFPERBLK;
273                         if(i == firsti)
274                                 j = SUPERBLK % REFPERBLK;
275                         if(i == lasti)
276                                 je = b->sb.fend % REFPERBLK;
277                         for(; j < je; j++)
278                                 c->refs[j] = 1;
279                 }
280                 if(i == b->sb.fend - 1){
281                         j = b->sb.size % REFPERBLK;
282                         if(j != 0)
283                                 for(; j < REFPERBLK; j++)
284                                         c->refs[j] = -1;
285                 }
286                 c->op |= BWRIM;
287                 putbuf(c);
288         }
289         b->op |= BDELWRI;
290         putbuf(b);
291         createroot(fs);
292         sync(1);
293         dprint("hjfs: ream successful\n");
294 }
295
296 Fs *
297 initfs(Dev *d, int doream, int flags)
298 {
299         Fs *fs;
300         Buf *b;
301         FLoc f;
302         
303         fs = emalloc(sizeof(*fs));
304         fs->d = d;
305         if(doream)
306                 ream(fs);
307         b = getbuf(d, SUPERBLK, TSUPERBLOCK, 0);
308         if(b == nil || b->sb.magic != SUPERMAGIC)
309                 goto error;
310         fs->root = b->sb.root;
311         fs->fstart = b->sb.fstart;
312         fs->freelist = chancreate(sizeof(uvlong), FREELISTLEN);
313         f.blk = fs->root;
314         f.deind = 0;
315         f.path = ROOTQID;
316         f.vers = 0;
317         f.type = QTDIR;
318         fs->rootloc = getloc(fs, f, nil);
319         f.deind++;
320         f.path = DUMPROOTQID;
321         fs->dumprootloc = getloc(fs, f, nil);
322         putbuf(b);
323         fs->flags = flags;
324         if(doream)
325                 writeusers(fs);
326         readusers(fs);
327         dprint("hjfs: fs is %s\n", d->name);
328         return fs;
329
330 error:
331         if(b != nil)
332                 putbuf(b);
333         free(fs);
334         return nil;
335 }
336
337 void
338 chbegin(Chan *ch)
339 {
340         if((ch->flags & CHFNOLOCK) == 0)
341                 rlock(ch->fs);
342 }
343
344 void
345 chend(Chan *ch)
346 {
347         if((ch->flags & CHFNOLOCK) == 0)
348                 runlock(ch->fs);
349 }
350
351 int
352 newqid(Fs *fs, uvlong *q)
353 {
354         Buf *b;
355         
356         b = getbuf(fs->d, SUPERBLK, TSUPERBLOCK, 0);
357         if(b == nil)
358                 return -1;
359         *q = b->sb.qidpath++;
360         b->op |= BDELWRI;
361         putbuf(b);
362         return 1;
363 }
364
365 Loc *
366 getloc(Fs *fs, FLoc f, Loc *next)
367 {
368         Loc *l;
369         
370         qlock(&fs->loctree);
371         if(next != nil && next->child != nil){
372                 l = next->child;
373                 do{
374                         if(l->blk == f.blk && l->deind == f.deind){
375                                 l->ref++;
376                                 qunlock(&fs->loctree);
377                                 return l;
378                         }
379                         l = l->cnext;
380                 }while(l != next->child);
381         }
382         l = emalloc(sizeof(*l));
383         l->ref = 1;
384         l->FLoc = f;
385         l->next = next;
386         if(fs->rootloc != nil){
387                 l->gnext = fs->rootloc;
388                 l->gprev = l->gnext->gprev;
389                 l->gnext->gprev = l;
390                 l->gprev->gnext = l;
391         }else
392                 l->gnext = l->gprev = l;
393         l->cprev = l->cnext = l;
394         if(next != nil){
395                 if(next->child == nil)
396                         next->child = l;
397                 else{
398                         l->cnext = next->child;
399                         l->cprev = next->child->cprev;
400                         l->cnext->cprev = l;
401                         l->cprev->cnext = l;
402                 }
403         }
404         qunlock(&fs->loctree);
405         return l;
406 }
407
408 int
409 haveloc(Fs *fs, uvlong blk, int deind, Loc *next)
410 {
411         Loc *l;
412
413         qlock(&fs->loctree);
414         l = next->child;
415         if(l != nil) do{
416                 if(l->blk == blk && l->deind == deind){
417                         qunlock(&fs->loctree);
418                         return 1;
419                 }
420                 l = l->cnext;
421         } while(l != next->child);
422         qunlock(&fs->loctree);
423         return 0;
424 }
425
426 Loc *
427 cloneloc(Fs *fs, Loc *l)
428 {
429         Loc *m;
430
431         qlock(&fs->loctree);
432         for(m = l; m != nil; m = m->next)
433                 m->ref++;
434         qunlock(&fs->loctree);
435         return l;
436 }
437
438 void
439 putloc(Fs *fs, Loc *l, int loop)
440 {
441         Loc *m;
442         Buf *b;
443
444         qlock(&fs->loctree);
445         if(!loop && --l->ref <= 0)
446                 goto freeit;
447         while(loop && l != nil && l->ref <= 1){
448 freeit:
449                 if((l->flags & LGONE) != 0){
450                         /*
451                          * safe to unlock here, the file is gone and
452                          * we'r the last reference.
453                          */
454                         qunlock(&fs->loctree);
455                         b = getbuf(fs->d, l->blk, TDENTRY, 0);
456                         if(b != nil){
457                                 delete(fs, l, b);
458                                 putbuf(b);
459                         }
460                         qlock(&fs->loctree);
461                 }
462                 l->cnext->cprev = l->cprev;
463                 l->cprev->cnext = l->cnext;
464                 l->gnext->gprev = l->gprev;
465                 l->gprev->gnext = l->gnext;
466                 if(l->next != nil && l->next->child == l){
467                         if(l->cnext == l)
468                                 l->next->child = nil;
469                         else
470                                 l->next->child = l->cnext;
471                 }
472                 m = l->next;
473                 free(l);
474                 l = m;
475         }
476         for(; loop && l != nil; l = l->next)
477                 --l->ref;
478         qunlock(&fs->loctree);
479 }
480
481 static int
482 dumpblk(Fs *fs, FLoc *, uvlong *l)
483 {
484         uvlong n;
485         int i;
486         Buf *b, *c;
487         Dentry *d;
488
489         b = getbuf(fs->d, *l, TDONTCARE, 0);
490         if(b == nil)
491                 return -1;
492         if(getfree(fs, &n) < 0){
493         berr:
494                 putbuf(b);
495                 return -1;
496         }
497         c = getbuf(fs->d, n, b->type, 1);
498         if(c == nil){
499                 putfree(fs, n);
500                 goto berr;
501         }
502         switch(b->type){
503         case TINDIR:
504                 memcpy(c->offs, b->offs, sizeof(b->offs));
505                 for(i = 0; i < OFFPERBLK; i++)
506                         if(b->offs[i] != 0)
507                                 chref(fs, b->offs[i], 1);
508                 break;
509         case TRAW:
510                 memcpy(c->data, b->data, sizeof(b->data));
511                 break;
512         case TDENTRY:
513                 memcpy(c->de, b->de, sizeof(b->de));
514                 for(d = b->de; d < &b->de[DEPERBLK]; d++){
515                         if((d->mode & DALLOC) == 0)
516                                 continue;
517                         if((d->type & QTTMP) != 0)
518                                 continue;
519                         for(i = 0; i < NDIRECT; i++)
520                                 if(d->db[i] != 0)
521                                         chref(fs, d->db[i], 1);
522                         for(i = 0; i < NINDIRECT; i++)
523                                 if(d->ib[i] != 0)
524                                         chref(fs, d->ib[i], 1);
525                 }
526                 break;
527         default:
528                 werrstr("dumpblk -- unknown type %d", b->type);
529                 putfree(fs, n);
530                 putbuf(c);
531                 putbuf(b);
532                 return -1;
533         }
534         c->op |= BDELWRI;
535         putbuf(c);
536         putbuf(b);
537         putfree(fs, *l);
538         *l = n;
539         return 0;
540 }
541
542 /*
543  * getblk returns the address of a block in a file
544  * given its relative address blk
545  * the address are returned in *r
546  * mode has to be one of:
547  * - GBREAD: this block will only be read
548  * - GBWRITE: this block will be written, but don't create it
549  *            if it doesn't exist
550  * - GBCREATE: this block will be written, create it if necessary
551  * - GBOVERWR: like GBCREATE, but return an empty block if a dump
552  *             would be necessary
553  * return value is 1 if the block existed, -1 on error
554  * a return value of 0 means the block did not exist
555  * this is only an error in case of GBREAD and GBWRITE
556  */
557
558 int
559 getblk(Fs *fs, FLoc *L, Buf *bd, uvlong blk, uvlong *r, int mode)
560 {
561         uvlong k, l;
562         uvlong *loc;
563         int i, j, rc, prc;
564         Buf *b;
565         Dentry *d;
566
567         b = bd;
568         d = getdent(L, b);
569         if(d == nil){
570                 dprint("hjfs: getblk: dirent gone\n");
571                 return -1;
572         }
573         if(blk < NDIRECT){
574                 loc = &d->db[blk];
575                 goto found;
576         }
577         blk -= NDIRECT;
578         l = 1;
579         for(i = 0; i < NINDIRECT; i++){
580                 l *= OFFPERBLK;
581                 if(blk < l)
582                         break;
583                 blk -= l;
584         }
585         if(i == NINDIRECT){
586                 werrstr("getblk: block offset too large");
587                 return -1;
588         }
589         loc = &d->ib[i];
590         for(j = 0; j <= i; j++){
591                 if(*loc == 0){
592                         if(mode == GBREAD || mode == GBWRITE){
593                                 if(b != bd)
594                                         putbuf(b);
595                                 return 0;
596                         }
597                         if(getfree(fs, loc) < 0){
598                                 if(b != bd)
599                                         putbuf(b);
600                                 return -1;
601                         }
602                         b->op |= BDELWRI;
603                         k = *loc;
604                         if(b != bd)
605                                 putbuf(b);
606                         b = getbuf(fs->d, k, TINDIR, 1);
607                         if(b == nil)
608                                 return -1;
609                         memset(b->offs, 0, sizeof(b->offs));
610                         b->op |= BDELWRI;
611                 }else{
612                         if(mode != GBREAD && chref(fs, *loc, 0) > 1){
613                                 if(dumpblk(fs, L, loc) < 0){
614                                         if(b != bd)
615                                                 putbuf(b);
616                                         return -1;
617                                 }
618                                 b->op |= BDELWRI;
619                         }
620                         k = *loc;
621                         if(b != bd)
622                                 putbuf(b);
623                         b = getbuf(fs->d, k, TINDIR, 0);
624                         if(b == nil)
625                                 return -1;
626                 }
627                 l /= OFFPERBLK;
628                 loc = &b->offs[blk / l];
629                 blk %= l;
630         }
631
632 found:
633         rc = 0;
634         prc = 0;
635         if(*loc != 0){
636                 if(mode == GBREAD)
637                         goto okay;
638                 if((rc = chref(fs, *loc, 0)) > 1){
639                         if(mode == GBOVERWR){
640                                 putfree(fs, *loc);
641                                 *loc = 0;
642                                 b->op |= BDELWRI;
643                                 prc = 1;
644                                 goto new;
645                         }
646                         if(dumpblk(fs, L, loc) < 0){
647                                 rc = -1;
648                                 goto end;
649                         }
650                         b->op |= BDELWRI;
651                 }
652                 if(rc < 0)
653                         goto end;
654                 if(rc == 0){
655                         dprint("hjfs: getblk: block %lld has refcount 0\n");
656                         werrstr("phase error -- getblk");
657                         rc = -1;
658                         goto end;
659                 }
660 okay:
661                 *r = *loc;
662                 rc = 1;
663         }else if(mode == GBCREATE || mode == GBOVERWR){
664 new:
665                 rc = getfree(fs, r);
666                 if(rc > 0){
667                         *loc = *r;
668                         b->op |= BDELWRI;
669                         rc = prc;
670                 }
671         }
672 end:
673         if(b != bd)
674                 putbuf(b);
675         return rc;
676 }
677
678 static void
679 delindir(Fs *fs, uvlong *l, int n)
680 {
681         Buf *b;
682         int k;
683
684         if(*l == 0)
685                 return;
686         if(chref(fs, *l, 0) > 1){
687                 *l = 0;
688                 return;
689         }
690         if(n > 0){
691                 b = getbuf(fs->d, *l, TINDIR, 0);
692                 if(b != nil){
693                         for(k = 0; k < OFFPERBLK; k++)
694                                 if(b->offs[k] != 0){
695                                         delindir(fs, &b->offs[k], n-1);
696                                         b->op |= BDELWRI;
697                                 }
698                         putbuf(b);
699                 }
700         }
701         putfree(fs, *l);
702         *l = 0;
703 }
704
705 static int
706 zeroes(int *a, int i)
707 {
708         while(i--)
709                 if(*a++ != 0)
710                         return 0;
711         return 1;
712 }
713
714 static void
715 delindirpart(Fs *fs, FLoc *p, uvlong *l, int *a, int n)
716 {
717         Buf *b;
718         int k;
719
720         if(*l == 0)
721                 return;
722         if(n == 0){
723                 putfree(fs, *l);
724                 *l = 0;
725                 return;
726         }
727         if(zeroes(a, n)){
728                 delindir(fs, l, n);
729                 return;
730         }
731         if(chref(fs, *l, 0) > 1)
732                 dumpblk(fs, p, l);
733         b = getbuf(fs->d, *l, TINDIR, 0);
734         if(b == nil)
735                 return;
736         delindirpart(fs, p, &b->offs[*a], a + 1, n - 1);
737         for(k = a[0] + 1; k < OFFPERBLK; k++)
738                 if(b->offs[k] != 0)
739                         delindir(fs, &b->offs[k], n - 1);
740         b->op |= BDELWRI;
741         putbuf(b);
742 }
743
744 /*
745  * call willmodify() before and modified()
746  * after calling this function
747  */
748 int
749 trunc(Fs *fs, FLoc *ll, Buf *bd, uvlong size)
750 {
751         uvlong blk;
752         Dentry *d;
753         int a[NINDIRECT];
754         uvlong l;
755         int i, j;
756
757         d = getdent(ll, bd);
758         if(d == nil)
759                 return -1;
760         if(size >= d->size)
761                 goto done;
762         blk = HOWMANY(size, RBLOCK);
763         while(blk < NDIRECT){
764                 if(d->db[blk] != 0){
765                         putfree(fs, d->db[blk]);
766                         d->db[blk] = 0;
767                         bd->op |= BDELWRI;
768                 }
769                 blk++;
770         }
771         blk -= NDIRECT;
772         l = 1;
773         for(i = 0; i < NINDIRECT; i++){
774                 l *= OFFPERBLK;
775                 if(blk < l)
776                         break;
777                 blk -= l;
778         }
779         if(blk >= l){
780                 werrstr("phase error -- truncate");
781                 return -1;
782         }
783         if(blk == 0)
784                 goto rest;
785         if(d->ib[i] == 0){
786                 i++;
787                 goto rest;
788         }
789         for(j = 0; j <= i; j++){
790                 l /= OFFPERBLK;
791                 a[j] = blk / l;
792                 blk %= l;
793         }
794         delindirpart(fs, ll, &d->ib[i], a, i + 1);
795         i++;
796 rest:
797         for(; i < NINDIRECT; i++)
798                 delindir(fs, &d->ib[i], i + 1);
799 done:
800         d->size = size;
801         bd->op |= BDELWRI;
802         return 1;
803 }
804
805 /*
806  * find a direntry
807  * name == nil allows any entry to match
808  * rl == nil allowed
809  * return value 1 on success, 0 on Enoent,
810  * -1 on other errors
811  */
812 int
813 findentry(Fs *fs, FLoc *l, Buf *b, char *name, FLoc *rl, int dump)
814 {
815         uvlong i;
816         int j;
817         Dentry *d;
818         uvlong r;
819         Buf *c;
820         
821         d = getdent(l, b);
822         if(d == nil)
823                 return -1;
824         for(i = 0; i < d->size; i++){
825                 if(getblk(fs, l, b, i, &r, GBREAD) <= 0)
826                         continue;
827                 c = getbuf(fs->d, r, TDENTRY, 0);
828                 if(c == nil)
829                         continue;
830                 for(j = 0; j < DEPERBLK; j++)
831                         if((c->de[j].mode & DALLOC) != 0 &&
832                            (name == nil || strcmp(c->de[j].name, name) == 0)){
833                                 if(dump && (c->de[j].type & QTTMP) != 0)
834                                         continue;
835                                 if(rl != nil){
836                                         rl->Qid = c->de[j].Qid;
837                                         rl->blk = r;
838                                         rl->deind = j;
839                                 }
840                                 putbuf(c);
841                                 return 1;
842                         }
843                 putbuf(c);
844         }
845         werrstr(Enoent);
846         return 0;       
847 }
848
849 void
850 modified(Chan *ch, Dentry *d)
851 {
852         d->mtime = time(0);
853         d->atime = d->mtime;
854         d->muid = ch->uid;
855         ch->loc->vers = ++d->vers;
856 }
857
858 typedef struct Del Del;
859
860 struct Del {
861         FLoc;
862         Del *next, *prev;
863 };
864
865 static int
866 deltraverse(Fs *fs, Del *p, Buf *b, Del **last)
867 {
868         Buf *c;
869         int frb;
870         Dentry *d;
871         uvlong i, s, r;
872         int j, rc;
873         Del *dd;
874         
875         frb = b == nil;
876         if(frb){
877                 b = getbuf(fs->d, p->blk, TDENTRY, 0);
878                 if(b == nil)
879                         return -1;
880         }
881         d = getdent(p, b);
882         if(d == nil){
883                 if(frb)
884                         putbuf(b);
885                 return -1;
886         }
887         s = d->size;
888         for(i = 0; i < s; i++){
889                 rc = getblk(fs, p, b, i, &r, GBREAD);
890                 if(rc <= 0)
891                         continue;
892                 c = getbuf(fs->d, r, TDENTRY, 0);
893                 if(c == nil)
894                         continue;
895                 for(j = 0; j < DEPERBLK; j++){
896                         d = &c->de[j];
897                         if((d->mode & (DALLOC|DGONE)) != 0){
898                                 if((d->type & QTDIR) == 0){
899                                         trunc(fs, p, b, 0);
900                                         memset(d, 0, sizeof(*d));
901                                         c->op |= BDELWRI;
902                                 }else{
903                                         dd = emalloc(sizeof(Del));
904                                         dd->blk = i;
905                                         dd->deind = j;
906                                         dd->Qid = d->Qid;
907                                         dd->prev = *last;
908                                         (*last)->next = dd;
909                                         *last = dd;
910                                 }
911                         }
912                 }
913                 putbuf(c);
914         }
915         if(frb)
916                 putbuf(b);
917         return 0;
918 }
919
920 int
921 delete(Fs *fs, FLoc *l, Buf *b)
922 {
923         Dentry *d;
924         Buf *c;
925         Del *first, *last, *p, *q;
926
927         d = getdent(l, b);
928         if(d == nil)
929                 return -1;
930         if((d->type & QTDIR) == 0){
931                 trunc(fs, l, b, 0);
932                 memset(d, 0, sizeof(*d));
933                 b->op |= BDELWRI;
934                 return 0;
935         }
936         first = last = emalloc(sizeof(Del));
937         first->FLoc = *l;
938         for(p = first; p != nil; p = p->next)
939                 deltraverse(fs, p, p == first ? b : nil, &last);
940         for(p = last; p != nil; q = p->prev, free(p), p = q){
941                 if(p == first)
942                         c = b;
943                 else
944                         c = getbuf(fs->d, p->blk, TDENTRY, 0);
945                 if(c == nil)
946                         continue;
947                 d = getdent(p, c);
948                 if(d != nil){
949                         trunc(fs, p, c, 0);
950                         memset(d, 0, sizeof(*d));
951                         c->op |= BDELWRI;
952                 }
953                 if(p != first)
954                         putbuf(c);
955         }
956         return 0;
957 }
958
959 /*
960  * newentry() looks for a free slot in the directory
961  * and returns FLoc pointing to the slot. if no free
962  * slot is available a new block is allocated. if
963  * dump == 0, then the resulting blk from the FLoc
964  * *is not* dumped, so to finally allocate the Dentry,
965  * one has to call willmodify() on res before modyfing it.
966  */
967 int
968 newentry(Fs *fs, Loc *l, Buf *b, char *name, FLoc *res, int dump)
969 {
970         Dentry *d, *dd;
971         uvlong i, si, r;
972         int j, sj;
973         Buf *c;
974
975         si = sj = -1;
976         d = getdent(l, b);
977         if(d != nil){
978                 for(i = 0; i < d->size; i++){
979                         if(getblk(fs, l, b, i, &r, GBREAD) <= 0)
980                                 continue;
981                         c = getbuf(fs->d, r, TDENTRY, 0);
982                         if(c == nil)
983                                 continue;
984                         for(j = 0; j < DEPERBLK; j++){
985                                 dd = &c->de[j];
986                                 if((dd->mode & DGONE) != 0)
987                                         continue;
988                                 if((dd->mode & DALLOC) != 0){
989                                         if(strcmp(dd->name, name) == 0){
990                                                 werrstr(Eexists);
991                                                 putbuf(c);
992                                                 return 0;
993                                         }
994                                         continue;
995                                 }
996                                 if(si != -1 || haveloc(fs, r, j, l))
997                                         continue;
998                                 si = i;
999                                 sj = j;
1000                         }
1001                         putbuf(c);
1002                 }
1003                 if(si == -1 && i == d->size){
1004                         if(getblk(fs, l, b, i, &r, GBCREATE) >= 0){
1005                                 c = getbuf(fs->d, r, TDENTRY, 1);
1006                                 if(c != nil){
1007                                         si = i;
1008                                         sj = 0;
1009                                         d->size = i+1;
1010                                         b->op |= BDELWRI;
1011                                         memset(c->de, 0, sizeof(c->de));
1012                                         c->op |= BDELWRI;
1013                                         putbuf(c);
1014                                 }
1015                         }
1016                 }
1017         }
1018         if(si == -1 || sj == -1){
1019                 werrstr("phase error -- create");
1020                 return -1;
1021         }
1022         if(getblk(fs, l, b, si, &res->blk, dump != 0 ? GBWRITE : GBREAD) <= 0)
1023                 return -1;
1024         res->deind = sj;
1025         res->Qid = (Qid){0, 0, 0};
1026         return 1;
1027 }