Blob


1 #include "stdinc.h"
2 #include "vac.h"
3 #include "dat.h"
4 #include "fns.h"
5 #include "error.h"
7 /*
8 * locking order is upwards. A thread can hold the lock for a VacFile
9 * and then acquire the lock of its parent
10 */
11 struct VacFile
12 {
13 VacFs *fs; /* immutable */
15 /* meta data for file: protected by the lk in the parent */
16 int ref; /* holds this data structure up */
18 int partial; /* file was never really open */
19 int removed; /* file has been removed */
20 int dirty; /* dir is dirty with respect to meta data in block */
21 u32int boff; /* block offset within msource for this file's metadata */
22 VacDir dir; /* metadata for this file */
23 VacFile *up; /* parent file */
24 VacFile *next; /* sibling */
26 RWLock lk; /* lock for the following */
27 VtFile *source; /* actual data */
28 VtFile *msource; /* metadata for children in a directory */
29 VacFile *down; /* children */
30 int mode;
31 };
33 static int filelock(VacFile*);
34 static u32int filemetaalloc(VacFile*, VacDir*, u32int);
35 static int filemetaflush2(VacFile*, char*);
36 static void filemetalock(VacFile*);
37 static void filemetaunlock(VacFile*);
38 static void fileraccess(VacFile*);
39 static int filerlock(VacFile*);
40 static void filerunlock(VacFile*);
41 static void fileunlock(VacFile*);
42 static void filewaccess(VacFile*, char*);
44 void mbinit(MetaBlock*, u8int*, uint, uint);
45 int mbsearch(MetaBlock*, char*, int*, MetaEntry*);
46 int mbresize(MetaBlock*, MetaEntry*, int);
47 VacFile *vdlookup(VacFile*, char*);
49 static VacFile*
50 filealloc(VacFs *fs)
51 {
52 VacFile *f;
54 f = vtmallocz(sizeof(VacFile));
55 f->ref = 1;
56 f->fs = fs;
57 f->boff = NilBlock;
58 f->mode = fs->mode;
59 return f;
60 }
62 static void
63 filefree(VacFile *f)
64 {
65 vtfileclose(f->source);
66 vtfileclose(f->msource);
67 vdcleanup(&f->dir);
68 memset(f, ~0, sizeof *f); /* paranoia */
69 vtfree(f);
70 }
72 /*
73 * the file is locked already
74 * f->msource is unlocked
75 */
76 static VacFile*
77 dirlookup(VacFile *f, char *elem)
78 {
79 int i;
80 MetaBlock mb;
81 MetaEntry me;
82 VtBlock *b;
83 VtFile *meta;
84 VacFile *ff;
85 u32int bo, nb;
87 meta = f->msource;
88 b = nil;
89 if(vtfilelock(meta, -1) < 0)
90 return nil;
91 nb = (vtfilegetsize(meta)+meta->dsize-1)/meta->dsize;
92 for(bo=0; bo<nb; bo++){
93 b = vtfileblock(meta, bo, VtOREAD);
94 if(b == nil)
95 goto Err;
96 if(mbunpack(&mb, b->data, meta->dsize) < 0)
97 goto Err;
98 if(mbsearch(&mb, elem, &i, &me) >= 0){
99 ff = filealloc(f->fs);
100 if(vdunpack(&ff->dir, &me) < 0){
101 filefree(ff);
102 goto Err;
104 vtfileunlock(meta);
105 vtblockput(b);
106 ff->boff = bo;
107 ff->mode = f->mode;
108 return ff;
111 vtblockput(b);
112 b = nil;
114 werrstr(ENoFile);
115 /* fall through */
116 Err:
117 vtfileunlock(meta);
118 vtblockput(b);
119 return nil;
122 VacFile*
123 _vacfileroot(VacFs *fs, VtFile *r)
125 VtBlock *b;
126 VtFile *r0, *r1, *r2;
127 MetaBlock mb;
128 MetaEntry me;
129 VacFile *root, *mr;
131 b = nil;
132 root = nil;
133 mr = nil;
134 r1 = nil;
135 r2 = nil;
137 if(vtfilelock(r, -1) < 0)
138 return nil;
139 r0 = vtfileopen(r, 0, fs->mode);
140 if(r0 == nil)
141 goto Err;
142 r1 = vtfileopen(r, 1, fs->mode);
143 if(r1 == nil)
144 goto Err;
145 r2 = vtfileopen(r, 2, fs->mode);
146 if(r2 == nil)
147 goto Err;
149 mr = filealloc(fs);
150 mr->msource = r2;
151 r2 = nil;
153 root = filealloc(fs);
154 root->boff = 0;
155 root->up = mr;
156 root->source = r0;
157 r0 = nil;
158 root->msource = r1;
159 r1 = nil;
161 mr->down = root;
163 if(vtfilelock(mr->msource, -1) < 0)
164 goto Err;
165 b = vtfileblock(mr->msource, 0, VtOREAD);
166 vtfileunlock(mr->msource);
167 if(b == nil)
168 goto Err;
170 if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
171 goto Err;
173 meunpack(&me, &mb, 0);
174 if(vdunpack(&root->dir, &me) < 0)
175 goto Err;
176 vtblockput(b);
177 vtfileunlock(r);
178 fileraccess(root);
180 return root;
181 Err:
182 vtblockput(b);
183 if(r0)
184 vtfileclose(r0);
185 if(r1)
186 vtfileclose(r1);
187 if(r2)
188 vtfileclose(r2);
189 if(mr)
190 filefree(mr);
191 if(root)
192 filefree(root);
193 vtfileunlock(r);
195 return nil;
198 static VtFile *
199 fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
201 VtFile *r;
203 if(vtfilelock(f->source, mode) < 0)
204 return nil;
205 r = vtfileopen(f->source, offset, mode);
206 vtfileunlock(f->source);
207 if(r == nil)
208 return nil;
209 if(r->gen != gen){
210 werrstr(ERemoved);
211 goto Err;
213 if(r->dir != dir && r->mode != -1){
214 fprint(2, "fileopensource: dir mismatch %d %d\n", r->dir, dir);
215 werrstr(EBadMeta);
216 goto Err;
218 return r;
219 Err:
220 vtfileclose(r);
221 return nil;
224 VacFile*
225 _filewalk(VacFile *f, char *elem, int partial)
227 VacFile *ff;
229 fileraccess(f);
231 if(elem[0] == 0){
232 werrstr(EBadPath);
233 return nil;
236 if(!vacfileisdir(f)){
237 werrstr(ENotDir);
238 return nil;
241 if(strcmp(elem, ".") == 0){
242 return vacfileincref(f);
245 if(strcmp(elem, "..") == 0){
246 if(vacfileisroot(f))
247 return vacfileincref(f);
248 return vacfileincref(f->up);
251 if(filelock(f) < 0)
252 return nil;
254 for(ff = f->down; ff; ff=ff->next){
255 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
256 ff->ref++;
257 goto Exit;
261 ff = dirlookup(f, elem);
262 if(ff == nil)
263 goto Err;
265 if(ff->dir.mode & ModeSnapshot)
266 ff->mode = VtOREAD;
268 if(partial){
269 /*
270 * Do nothing. We're opening this file only so we can clri it.
271 * Usually the sources can't be opened, hence we won't even bother.
272 * Be VERY careful with the returned file. If you hand it to a routine
273 * expecting ff->source and/or ff->msource to be non-nil, we're
274 * likely to dereference nil. FileClri should be the only routine
275 * setting partial.
276 */
277 ff->partial = 1;
278 }else if(ff->dir.mode & ModeDir){
279 ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 1, ff->mode);
280 ff->msource = fileopensource(f, ff->dir.mentry, ff->dir.mgen, 0, ff->mode);
281 if(ff->source == nil || ff->msource == nil)
282 goto Err;
283 }else{
284 ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode);
285 if(ff->source == nil)
286 goto Err;
289 /* link in and up parent ref count */
290 ff->next = f->down;
291 f->down = ff;
292 ff->up = f;
293 vacfileincref(f);
294 Exit:
295 fileunlock(f);
296 return ff;
297 Err:
298 fileunlock(f);
299 if(ff != nil)
300 vacfiledecref(ff);
301 return nil;
304 VacFile*
305 vacfilewalk(VacFile *f, char *elem)
307 return _filewalk(f, elem, 0);
310 VacFile*
311 _fileopen(VacFs *fs, char *path, int partial)
313 VacFile *f, *ff;
314 char *p, elem[VtMaxStringSize], *opath;
315 int n;
317 f = fs->root;
318 vacfileincref(f);
319 opath = path;
320 while(*path != 0){
321 for(p = path; *p && *p != '/'; p++)
323 n = p - path;
324 if(n > 0){
325 if(n > VtMaxStringSize){
326 werrstr("%s: element too long", EBadPath);
327 goto Err;
329 memmove(elem, path, n);
330 elem[n] = 0;
331 ff = _filewalk(f, elem, partial && *p=='\0');
332 if(ff == nil){
333 werrstr("%.*s: %R", utfnlen(opath, p-opath), opath);
334 goto Err;
336 vacfiledecref(f);
337 f = ff;
339 if(*p == '/')
340 p++;
341 path = p;
343 return f;
344 Err:
345 vacfiledecref(f);
346 return nil;
349 VacFile*
350 vacfileopen(VacFs *fs, char *path)
352 return _fileopen(fs, path, 0);
355 #if 0
356 static void
357 filesettmp(VacFile *f, int istmp)
359 int i;
360 VtEntry e;
361 VtFile *r;
363 for(i=0; i<2; i++){
364 if(i==0)
365 r = f->source;
366 else
367 r = f->msource;
368 if(r == nil)
369 continue;
370 if(vtfilegetentry(r, &e) < 0){
371 fprint(2, "vtfilegetentry failed (cannot happen): %r\n");
372 continue;
374 if(istmp)
375 e.flags |= VtEntryNoArchive;
376 else
377 e.flags &= ~VtEntryNoArchive;
378 if(vtfilesetentry(r, &e) < 0){
379 fprint(2, "vtfilesetentry failed (cannot happen): %r\n");
380 continue;
384 #endif
386 VacFile*
387 vacfilecreate(VacFile *f, char *elem, ulong mode, char *uid)
389 VacFile *ff;
390 VacDir *dir;
391 VtFile *pr, *r, *mr;
392 int isdir;
394 if(filelock(f) < 0)
395 return nil;
397 r = nil;
398 mr = nil;
399 for(ff = f->down; ff; ff=ff->next){
400 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
401 ff = nil;
402 werrstr(EExists);
403 goto Err1;
407 ff = dirlookup(f, elem);
408 if(ff != nil){
409 werrstr(EExists);
410 goto Err1;
413 pr = f->source;
414 if(pr->mode != VtORDWR){
415 werrstr(EReadOnly);
416 goto Err1;
419 if(vtfilelock2(f->source, f->msource, -1) < 0)
420 goto Err1;
422 ff = filealloc(f->fs);
423 isdir = mode & ModeDir;
425 r = vtfilecreate(pr, pr->dsize, isdir, 0);
426 if(r == nil)
427 goto Err;
428 if(isdir){
429 mr = vtfilecreate(pr, pr->dsize, 0, r->offset);
430 if(mr == nil)
431 goto Err;
434 dir = &ff->dir;
435 dir->elem = vtstrdup(elem);
436 dir->entry = r->offset;
437 dir->gen = r->gen;
438 if(isdir){
439 dir->mentry = mr->offset;
440 dir->mgen = mr->gen;
442 dir->size = 0;
443 if(_vacfsnextqid(f->fs, &dir->qid) < 0)
444 goto Err;
445 dir->uid = vtstrdup(uid);
446 dir->gid = vtstrdup(f->dir.gid);
447 dir->mid = vtstrdup(uid);
448 dir->mtime = time(0L);
449 dir->mcount = 0;
450 dir->ctime = dir->mtime;
451 dir->atime = dir->mtime;
452 dir->mode = mode;
454 ff->boff = filemetaalloc(f, dir, 0);
455 if(ff->boff == NilBlock)
456 goto Err;
458 vtfileunlock(f->source);
459 vtfileunlock(f->msource);
461 ff->source = r;
462 ff->msource = mr;
464 #if 0
465 if(mode&ModeTemporary){
466 if(vtfilelock2(r, mr, -1) < 0)
467 goto Err1;
468 filesettmp(ff, 1);
469 vtfileunlock(r);
470 if(mr)
471 vtfileunlock(mr);
473 #endif
475 /* committed */
477 /* link in and up parent ref count */
478 ff->next = f->down;
479 f->down = ff;
480 ff->up = f;
481 vacfileincref(f);
483 filewaccess(f, uid);
485 fileunlock(f);
486 return ff;
488 Err:
489 vtfileunlock(f->source);
490 vtfileunlock(f->msource);
491 Err1:
492 if(r){
493 vtfilelock(r, -1);
494 vtfileremove(r);
496 if(mr){
497 vtfilelock(mr, -1);
498 vtfileremove(mr);
500 if(ff)
501 vacfiledecref(ff);
502 fileunlock(f);
503 return nil;
506 int
507 vacfileblockscore(VacFile *f, u32int bn, u8int *score)
509 VtFile *s;
510 uvlong size;
511 int dsize, ret;
513 ret = -1;
514 if(filerlock(f) < 0)
515 return -1;
516 fileraccess(f);
517 if(vtfilelock(f->source, VtOREAD) < 0)
518 goto out;
520 s = f->source;
521 dsize = s->dsize;
522 size = vtfilegetsize(s);
523 if((uvlong)bn*dsize >= size)
524 goto out;
525 ret = vtfileblockscore(f->source, bn, score);
527 out:
528 vtfileunlock(f->source);
529 filerunlock(f);
530 return ret;
533 int
534 vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
536 VtFile *s;
537 uvlong size;
538 u32int bn;
539 int off, dsize, n, nn;
540 VtBlock *b;
541 uchar *p;
543 if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset);
545 if(filerlock(f) < 0)
546 return -1;
548 if(offset < 0){
549 werrstr(EBadOffset);
550 goto Err1;
553 fileraccess(f);
555 if(vtfilelock(f->source, VtOREAD) < 0)
556 goto Err1;
558 s = f->source;
559 dsize = s->dsize;
560 size = vtfilegetsize(s);
562 if(offset >= size)
563 offset = size;
565 if(cnt > size-offset)
566 cnt = size-offset;
567 bn = offset/dsize;
568 off = offset%dsize;
569 p = buf;
570 while(cnt > 0){
571 b = vtfileblock(s, bn, OREAD);
572 if(b == nil)
573 goto Err;
574 n = cnt;
575 if(n > dsize-off)
576 n = dsize-off;
577 nn = dsize-off;
578 if(nn > n)
579 nn = n;
580 memmove(p, b->data+off, nn);
581 memset(p+nn, 0, nn-n);
582 off = 0;
583 bn++;
584 cnt -= n;
585 p += n;
586 vtblockput(b);
588 vtfileunlock(s);
589 filerunlock(f);
590 return p-(uchar*)buf;
592 Err:
593 vtfileunlock(s);
594 Err1:
595 filerunlock(f);
596 return -1;
599 #if 0
600 /*
601 * Changes the file block bn to be the given block score.
602 * Very sneaky. Only used by flfmt.
603 */
604 int
605 filemapblock(VacFile *f, ulong bn, uchar score[VtScoreSize], ulong tag)
607 VtBlock *b;
608 VtEntry e;
609 VtFile *s;
611 if(filelock(f) < 0)
612 return -1;
614 s = nil;
615 if(f->dir.mode & ModeDir){
616 werrstr(ENotFile);
617 goto Err;
620 if(f->source->mode != VtORDWR){
621 werrstr(EReadOnly);
622 goto Err;
625 if(vtfilelock(f->source, -1) < 0)
626 goto Err;
628 s = f->source;
629 b = _vtfileblock(s, bn, VtORDWR, 1, tag);
630 if(b == nil)
631 goto Err;
633 if(vtfilegetentry(s, &e) < 0)
634 goto Err;
635 if(b->l.type == BtDir){
636 memmove(e.score, score, VtScoreSize);
637 assert(e.tag == tag || e.tag == 0);
638 e.tag = tag;
639 e.flags |= VtEntryLocal;
640 vtentrypack(&e, b->data, f->source->offset % f->source->epb);
641 }else
642 memmove(b->data + (bn%(e.psize/VtScoreSize))*VtScoreSize, score, VtScoreSize);
643 vtblockdirty(b);
644 vtblockput(b);
645 vtfileunlock(s);
646 fileunlock(f);
647 return 0;
649 Err:
650 if(s)
651 vtfileunlock(s);
652 fileunlock(f);
653 return -1;
655 #endif
657 int
658 vacfilesetsize(VacFile *f, uvlong size)
660 int r;
662 if(filelock(f) < 0)
663 return -1;
664 r = 0;
665 if(f->dir.mode & ModeDir){
666 werrstr(ENotFile);
667 goto Err;
669 if(f->source->mode != VtORDWR){
670 werrstr(EReadOnly);
671 goto Err;
673 if(vtfilelock(f->source, -1) < 0)
674 goto Err;
675 r = vtfilesetsize(f->source, size);
676 vtfileunlock(f->source);
677 Err:
678 fileunlock(f);
679 return r;
682 int
683 filewrite(VacFile *f, void *buf, int cnt, vlong offset, char *uid)
685 VtFile *s;
686 ulong bn;
687 int off, dsize, n;
688 VtBlock *b;
689 uchar *p;
690 vlong eof;
692 if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset);
694 if(filelock(f) < 0)
695 return -1;
697 s = nil;
698 if(f->dir.mode & ModeDir){
699 werrstr(ENotFile);
700 goto Err;
703 if(f->source->mode != VtORDWR){
704 werrstr(EReadOnly);
705 goto Err;
707 if(offset < 0){
708 werrstr(EBadOffset);
709 goto Err;
712 filewaccess(f, uid);
714 if(vtfilelock(f->source, -1) < 0)
715 goto Err;
716 s = f->source;
717 dsize = s->dsize;
719 eof = vtfilegetsize(s);
720 if(f->dir.mode & ModeAppend)
721 offset = eof;
722 bn = offset/dsize;
723 off = offset%dsize;
724 p = buf;
725 while(cnt > 0){
726 n = cnt;
727 if(n > dsize-off)
728 n = dsize-off;
729 b = vtfileblock(s, bn, n<dsize?VtORDWR:VtOWRITE);
730 if(b == nil){
731 if(offset > eof)
732 vtfilesetsize(s, offset);
733 goto Err;
735 memmove(b->data+off, p, n);
736 off = 0;
737 cnt -= n;
738 p += n;
739 offset += n;
740 bn++;
741 vtblockdirty(b);
742 vtblockput(b);
744 if(offset > eof && vtfilesetsize(s, offset) < 0)
745 goto Err;
746 vtfileunlock(s);
747 fileunlock(f);
748 return p-(uchar*)buf;
749 Err:
750 if(s)
751 vtfileunlock(s);
752 fileunlock(f);
753 return -1;
756 int
757 vacfilegetdir(VacFile *f, VacDir *dir)
759 if(filerlock(f) < 0)
760 return -1;
762 filemetalock(f);
763 vdcopy(dir, &f->dir);
764 filemetaunlock(f);
766 if(vacfileisdir(f) < 0){
767 if(vtfilelock(f->source, VtOREAD) < 0){
768 filerunlock(f);
769 return -1;
771 dir->size = vtfilegetsize(f->source);
772 vtfileunlock(f->source);
774 filerunlock(f);
776 return 0;
779 int
780 vacfiletruncate(VacFile *f, char *uid)
782 if(vacfileisdir(f)){
783 werrstr(ENotFile);
784 return -1;
787 if(filelock(f) < 0)
788 return -1;
790 if(f->source->mode != VtORDWR){
791 werrstr(EReadOnly);
792 fileunlock(f);
793 return -1;
795 if(vtfilelock(f->source, -1) < 0){
796 fileunlock(f);
797 return -1;
799 if(vtfiletruncate(f->source) < 0){
800 vtfileunlock(f->source);
801 fileunlock(f);
802 return -1;
804 vtfileunlock(f->source);
805 fileunlock(f);
807 filewaccess(f, uid);
809 return 0;
812 int
813 vacfilesetdir(VacFile *f, VacDir *dir, char *uid)
815 VacFile *ff;
816 char *oelem;
817 u32int mask;
818 u64int size;
820 /* can not set permissions for the root */
821 if(vacfileisroot(f)){
822 werrstr(ERoot);
823 return -1;
826 if(filelock(f) < 0)
827 return -1;
829 if(f->source->mode != VtORDWR){
830 werrstr(EReadOnly);
831 fileunlock(f);
832 return -1;
835 filemetalock(f);
837 /* check new name does not already exist */
838 if(strcmp(f->dir.elem, dir->elem) != 0){
839 for(ff = f->up->down; ff; ff=ff->next){
840 if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
841 werrstr(EExists);
842 goto Err;
846 ff = dirlookup(f->up, dir->elem);
847 if(ff != nil){
848 vacfiledecref(ff);
849 werrstr(EExists);
850 goto Err;
854 if(vtfilelock2(f->source, f->msource, -1) < 0)
855 goto Err;
856 if(!vacfileisdir(f)){
857 size = vtfilegetsize(f->source);
858 if(size != dir->size){
859 if(vtfilesetsize(f->source, dir->size) < 0){
860 vtfileunlock(f->source);
861 if(f->msource)
862 vtfileunlock(f->msource);
863 goto Err;
865 /* commited to changing it now */
868 /* commited to changing it now */
869 #if 0
870 if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary))
871 filesettmp(f, dir->mode&ModeTemporary);
872 #endif
873 vtfileunlock(f->source);
874 if(f->msource)
875 vtfileunlock(f->msource);
877 oelem = nil;
878 if(strcmp(f->dir.elem, dir->elem) != 0){
879 oelem = f->dir.elem;
880 f->dir.elem = vtstrdup(dir->elem);
883 if(strcmp(f->dir.uid, dir->uid) != 0){
884 vtfree(f->dir.uid);
885 f->dir.uid = vtstrdup(dir->uid);
888 if(strcmp(f->dir.gid, dir->gid) != 0){
889 vtfree(f->dir.gid);
890 f->dir.gid = vtstrdup(dir->gid);
893 f->dir.mtime = dir->mtime;
894 f->dir.atime = dir->atime;
896 //fprint(2, "mode %x %x ", f->dir.mode, dir->mode);
897 mask = ~(ModeDir|ModeSnapshot);
898 f->dir.mode &= ~mask;
899 f->dir.mode |= mask & dir->mode;
900 f->dirty = 1;
901 //fprint(2, "->%x\n", f->dir.mode);
903 filemetaflush2(f, oelem);
904 vtfree(oelem);
906 filemetaunlock(f);
907 fileunlock(f);
909 filewaccess(f->up, uid);
911 return 0;
912 Err:
913 filemetaunlock(f);
914 fileunlock(f);
915 return -1;
918 int
919 vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
921 int ret;
923 if(filelock(f) < 0)
924 return -1;
925 filemetalock(f);
926 f->dir.qidspace = 1;
927 f->dir.qidoffset = offset;
928 f->dir.qidmax = max;
929 ret = filemetaflush2(f, nil);
930 filemetaunlock(f);
931 fileunlock(f);
932 return ret;
935 uvlong
936 vacfilegetid(VacFile *f)
938 /* immutable */
939 return f->dir.qid;
942 ulong
943 vacfilegetmcount(VacFile *f)
945 ulong mcount;
947 filemetalock(f);
948 mcount = f->dir.mcount;
949 filemetaunlock(f);
950 return mcount;
953 ulong
954 vacfilegetmode(VacFile *f)
956 ulong mode;
958 filemetalock(f);
959 mode = f->dir.mode;
960 filemetaunlock(f);
961 return mode;
964 int
965 vacfileisdir(VacFile *f)
967 /* immutable */
968 return (f->dir.mode & ModeDir) != 0;
971 int
972 vacfileisroot(VacFile *f)
974 return f == f->fs->root;
977 int
978 vacfilegetsize(VacFile *f, uvlong *size)
980 if(filerlock(f) < 0)
981 return 0;
982 if(vtfilelock(f->source, VtOREAD) < 0){
983 filerunlock(f);
984 return -1;
986 *size = vtfilegetsize(f->source);
987 vtfileunlock(f->source);
988 filerunlock(f);
990 return 0;
993 int
994 vacfilegetvtentry(VacFile *f, VtEntry *e)
996 if(filerlock(f) < 0)
997 return 0;
998 if(vtfilelock(f->source, VtOREAD) < 0){
999 filerunlock(f);
1000 return -1;
1002 vtfilegetentry(f->source, e);
1003 vtfileunlock(f->source);
1004 filerunlock(f);
1006 return 0;
1009 void
1010 vacfilemetaflush(VacFile *f, int rec)
1012 VacFile **kids, *p;
1013 int nkids;
1014 int i;
1016 filemetalock(f);
1017 filemetaflush2(f, nil);
1018 filemetaunlock(f);
1020 if(!rec || !vacfileisdir(f))
1021 return;
1023 if(filelock(f) < 0)
1024 return;
1025 nkids = 0;
1026 for(p=f->down; p; p=p->next)
1027 nkids++;
1028 kids = vtmalloc(nkids*sizeof(VacFile*));
1029 i = 0;
1030 for(p=f->down; p; p=p->next){
1031 kids[i++] = p;
1032 p->ref++;
1034 fileunlock(f);
1036 for(i=0; i<nkids; i++){
1037 vacfilemetaflush(kids[i], 1);
1038 vacfiledecref(kids[i]);
1040 vtfree(kids);
1043 /* assumes metaLock is held */
1044 static int
1045 filemetaflush2(VacFile *f, char *oelem)
1047 VacFile *fp;
1048 VtBlock *b, *bb;
1049 MetaBlock mb;
1050 MetaEntry me, me2;
1051 int i, n;
1052 u32int boff;
1054 if(!f->dirty)
1055 return 0;
1057 if(oelem == nil)
1058 oelem = f->dir.elem;
1060 fp = f->up;
1062 if(vtfilelock(fp->msource, -1) < 0)
1063 return 0;
1064 /* can happen if source is clri'ed out from under us */
1065 if(f->boff == NilBlock)
1066 goto Err1;
1067 b = vtfileblock(fp->msource, f->boff, VtORDWR);
1068 if(b == nil)
1069 goto Err1;
1071 if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
1072 goto Err;
1073 if(mbsearch(&mb, oelem, &i, &me) < 0)
1074 goto Err;
1076 n = vdsize(&f->dir);
1077 if(0)fprint(2, "old size %d new size %d\n", me.size, n);
1079 if(mbresize(&mb, &me, n) >= 0){
1080 /* fits in the block */
1081 mbdelete(&mb, i, &me);
1082 if(strcmp(f->dir.elem, oelem) != 0)
1083 mbsearch(&mb, f->dir.elem, &i, &me2);
1084 vdpack(&f->dir, &me);
1085 mbinsert(&mb, i, &me);
1086 mbpack(&mb);
1087 vtblockdirty(b);
1088 vtblockput(b);
1089 vtfileunlock(fp->msource);
1090 f->dirty = 0;
1091 return -1;
1095 * moving entry to another block
1096 * it is feasible for the fs to crash leaving two copies
1097 * of the directory entry. This is just too much work to
1098 * fix. Given that entries are only allocated in a block that
1099 * is less than PercentageFull, most modifications of meta data
1100 * will fit within the block. i.e. this code should almost
1101 * never be executed.
1103 boff = filemetaalloc(fp, &f->dir, f->boff+1);
1104 if(boff == NilBlock){
1105 /* mbResize might have modified block */
1106 mbpack(&mb);
1107 vtblockdirty(b);
1108 goto Err;
1110 fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff);
1111 f->boff = boff;
1113 /* make sure deletion goes to disk after new entry */
1114 bb = vtfileblock(fp->msource, f->boff, VtORDWR);
1115 mbdelete(&mb, i, &me);
1116 mbpack(&mb);
1117 // blockDependency(b, bb, -1, nil, nil);
1118 vtblockput(bb);
1119 vtblockdirty(b);
1120 vtblockput(b);
1121 vtfileunlock(fp->msource);
1123 f->dirty = 0;
1125 return 0;
1127 Err:
1128 vtblockput(b);
1129 Err1:
1130 vtfileunlock(fp->msource);
1131 return -1;
1134 static int
1135 filemetaremove(VacFile *f, char *uid)
1137 VtBlock *b;
1138 MetaBlock mb;
1139 MetaEntry me;
1140 int i;
1141 VacFile *up;
1143 b = nil;
1144 up = f->up;
1145 filewaccess(up, uid);
1146 filemetalock(f);
1148 if(vtfilelock(up->msource, VtORDWR) < 0)
1149 goto Err;
1150 b = vtfileblock(up->msource, f->boff, VtORDWR);
1151 if(b == nil)
1152 goto Err;
1154 if(mbunpack(&mb, b->data, up->msource->dsize) < 0)
1155 goto Err;
1156 if(mbsearch(&mb, f->dir.elem, &i, &me) < 0)
1157 goto Err;
1158 mbdelete(&mb, i, &me);
1159 mbpack(&mb);
1160 vtfileunlock(up->msource);
1162 vtblockdirty(b);
1163 vtblockput(b);
1165 f->removed = 1;
1166 f->boff = NilBlock;
1167 f->dirty = 0;
1169 filemetaunlock(f);
1170 return 0;
1172 Err:
1173 vtfileunlock(up->msource);
1174 vtblockput(b);
1175 filemetaunlock(f);
1176 return -1;
1179 /* assume file is locked, assume f->msource is locked */
1180 static int
1181 filecheckempty(VacFile *f)
1183 u32int i, n;
1184 VtBlock *b;
1185 MetaBlock mb;
1186 VtFile *r;
1188 r = f->msource;
1189 n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
1190 for(i=0; i<n; i++){
1191 b = vtfileblock(r, i, VtORDWR);
1192 if(b == nil)
1193 goto Err;
1194 if(mbunpack(&mb, b->data, r->dsize) < 0)
1195 goto Err;
1196 if(mb.nindex > 0){
1197 werrstr(ENotEmpty);
1198 goto Err;
1200 vtblockput(b);
1202 return 0;
1203 Err:
1204 vtblockput(b);
1205 return -1;
1208 int
1209 vacfileremove(VacFile *f, char *muid)
1211 VacFile *ff;
1213 /* can not remove the root */
1214 if(vacfileisroot(f)){
1215 werrstr(ERoot);
1216 return -1;
1219 if(filelock(f) < 0)
1220 return -1;
1222 if(f->source->mode != VtORDWR){
1223 werrstr(EReadOnly);
1224 goto Err1;
1226 if(vtfilelock2(f->source, f->msource, -1) < 0)
1227 goto Err1;
1228 if(vacfileisdir(f) && filecheckempty(f)<0)
1229 goto Err;
1231 for(ff=f->down; ff; ff=ff->next)
1232 assert(ff->removed);
1234 vtfileremove(f->source);
1235 f->source = nil;
1236 if(f->msource){
1237 vtfileremove(f->msource);
1238 f->msource = nil;
1241 fileunlock(f);
1243 if(filemetaremove(f, muid) < 0)
1244 return -1;
1246 return 0;
1248 Err:
1249 vtfileunlock(f->source);
1250 if(f->msource)
1251 vtfileunlock(f->msource);
1252 Err1:
1253 fileunlock(f);
1254 return -1;
1257 static int
1258 clri(VacFile *f, char *uid)
1260 int r;
1262 if(f == nil)
1263 return -1;
1264 if(f->up->source->mode != VtORDWR){
1265 werrstr(EReadOnly);
1266 vacfiledecref(f);
1267 return -1;
1269 r = filemetaremove(f, uid);
1270 vacfiledecref(f);
1271 return r;
1274 int
1275 vacfileclripath(VacFs *fs, char *path, char *uid)
1277 return clri(_fileopen(fs, path, 1), uid);
1280 int
1281 vacfileclri(VacFile *dir, char *elem, char *uid)
1283 return clri(_filewalk(dir, elem, 1), uid);
1286 VacFile*
1287 vacfileincref(VacFile *vf)
1289 filemetalock(vf);
1290 assert(vf->ref > 0);
1291 vf->ref++;
1292 filemetaunlock(vf);
1293 return vf;
1296 int
1297 vacfiledecref(VacFile *f)
1299 VacFile *p, *q, **qq;
1301 if(f->up == nil){
1302 /* never linked in */
1303 assert(f->ref == 1);
1304 filefree(f);
1305 return 0;
1308 filemetalock(f);
1309 f->ref--;
1310 if(f->ref > 0){
1311 filemetaunlock(f);
1312 return -1;
1314 assert(f->ref == 0);
1315 assert(f->down == nil);
1317 filemetaflush2(f, nil);
1319 p = f->up;
1320 qq = &p->down;
1321 for(q = *qq; q; q = *qq){
1322 if(q == f)
1323 break;
1324 qq = &q->next;
1326 assert(q != nil);
1327 *qq = f->next;
1329 filemetaunlock(f);
1330 filefree(f);
1331 vacfiledecref(p);
1332 return 0;
1335 VacFile*
1336 vacfilegetparent(VacFile *f)
1338 if(vacfileisroot(f))
1339 return vacfileincref(f);
1340 return vacfileincref(f->up);
1343 int
1344 vacfilewrite(VacFile *file, void *buf, int n, vlong offset, char *muid)
1346 werrstr("read only file system");
1347 return -1;
1350 VacDirEnum*
1351 vdeopen(VacFile *f)
1353 VacDirEnum *vde;
1354 VacFile *p;
1356 if(!vacfileisdir(f)){
1357 werrstr(ENotDir);
1358 return nil;
1361 /* flush out meta data */
1362 if(filelock(f) < 0)
1363 return nil;
1364 for(p=f->down; p; p=p->next)
1365 filemetaflush2(p, nil);
1366 fileunlock(f);
1368 vde = vtmallocz(sizeof(VacDirEnum));
1369 vde->file = vacfileincref(f);
1371 return vde;
1374 static int
1375 direntrysize(VtFile *s, ulong elem, ulong gen, uvlong *size)
1377 VtBlock *b;
1378 ulong bn;
1379 VtEntry e;
1380 int epb;
1382 epb = s->dsize/VtEntrySize;
1383 bn = elem/epb;
1384 elem -= bn*epb;
1386 b = vtfileblock(s, bn, VtOREAD);
1387 if(b == nil)
1388 goto Err;
1389 if(vtentryunpack(&e, b->data, elem) < 0)
1390 goto Err;
1392 /* hanging entries are returned as zero size */
1393 if(!(e.flags & VtEntryActive) || e.gen != gen)
1394 *size = 0;
1395 else
1396 *size = e.size;
1397 vtblockput(b);
1398 return 0;
1400 Err:
1401 vtblockput(b);
1402 return -1;
1405 static int
1406 vdefill(VacDirEnum *vde)
1408 int i, n;
1409 VtFile *meta, *source;
1410 MetaBlock mb;
1411 MetaEntry me;
1412 VacFile *f;
1413 VtBlock *b;
1414 VacDir *de;
1416 /* clean up first */
1417 for(i=vde->i; i<vde->n; i++)
1418 vdcleanup(vde->buf+i);
1419 vtfree(vde->buf);
1420 vde->buf = nil;
1421 vde->i = 0;
1422 vde->n = 0;
1424 f = vde->file;
1426 source = f->source;
1427 meta = f->msource;
1429 b = vtfileblock(meta, vde->boff, VtOREAD);
1430 if(b == nil)
1431 goto Err;
1432 if(mbunpack(&mb, b->data, meta->dsize) < 0)
1433 goto Err;
1435 n = mb.nindex;
1436 vde->buf = vtmalloc(n * sizeof(VacDir));
1438 for(i=0; i<n; i++){
1439 de = vde->buf + i;
1440 meunpack(&me, &mb, i);
1441 if(vdunpack(de, &me) < 0)
1442 goto Err;
1443 vde->n++;
1444 if(!(de->mode & ModeDir))
1445 if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
1446 goto Err;
1448 vde->boff++;
1449 vtblockput(b);
1450 return 0;
1451 Err:
1452 vtblockput(b);
1453 return -1;
1456 int
1457 vderead(VacDirEnum *vde, VacDir *de)
1459 int ret, didread;
1460 VacFile *f;
1461 u32int nb;
1463 f = vde->file;
1464 if(filerlock(f) < 0)
1465 return -1;
1467 if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){
1468 filerunlock(f);
1469 return -1;
1472 nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
1474 didread = 0;
1475 while(vde->i >= vde->n){
1476 if(vde->boff >= nb){
1477 ret = 0;
1478 goto Return;
1480 didread = 1;
1481 if(vdefill(vde) < 0){
1482 ret = -1;
1483 goto Return;
1487 memmove(de, vde->buf + vde->i, sizeof(VacDir));
1488 vde->i++;
1489 ret = 1;
1491 Return:
1492 vtfileunlock(f->source);
1493 vtfileunlock(f->msource);
1494 filerunlock(f);
1496 if(didread)
1497 fileraccess(f);
1498 return ret;
1501 void
1502 vdeclose(VacDirEnum *vde)
1504 int i;
1505 if(vde == nil)
1506 return;
1507 for(i=vde->i; i<vde->n; i++)
1508 vdcleanup(vde->buf+i);
1509 vtfree(vde->buf);
1510 vacfiledecref(vde->file);
1511 vtfree(vde);
1515 * caller must lock f->source and f->msource
1516 * caller must NOT lock the source and msource
1517 * referenced by dir.
1519 static u32int
1520 filemetaalloc(VacFile *f, VacDir *dir, u32int start)
1522 u32int nb, bo;
1523 VtBlock *b;
1524 MetaBlock mb;
1525 int nn;
1526 uchar *p;
1527 int i, n;
1528 MetaEntry me;
1529 VtFile *s, *ms;
1531 s = f->source;
1532 ms = f->msource;
1534 n = vdsize(dir);
1535 nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize;
1536 b = nil;
1537 if(start > nb)
1538 start = nb;
1539 for(bo=start; bo<nb; bo++){
1540 b = vtfileblock(ms, bo, VtORDWR);
1541 if(b == nil)
1542 goto Err;
1543 if(mbunpack(&mb, b->data, ms->dsize) < 0)
1544 goto Err;
1545 nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
1546 if(n <= nn && mb.nindex < mb.maxindex)
1547 break;
1548 vtblockput(b);
1549 b = nil;
1552 /* add block to meta file */
1553 if(b == nil){
1554 b = vtfileblock(ms, bo, VtORDWR);
1555 if(b == nil)
1556 goto Err;
1557 vtfilesetsize(ms, (nb+1)*ms->dsize);
1558 mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
1561 p = mballoc(&mb, n);
1562 if(p == nil){
1563 /* mbAlloc might have changed block */
1564 mbpack(&mb);
1565 vtblockdirty(b);
1566 werrstr(EBadMeta);
1567 goto Err;
1570 mbsearch(&mb, dir->elem, &i, &me);
1571 assert(me.p == nil);
1572 me.p = p;
1573 me.size = n;
1574 vdpack(dir, &me);
1575 mbinsert(&mb, i, &me);
1576 mbpack(&mb);
1578 #ifdef notdef /* XXX */
1579 /* meta block depends on super block for qid ... */
1580 bb = cacheLocal(b->c, PartSuper, 0, VtOREAD);
1581 blockDependency(b, bb, -1, nil, nil);
1582 vtblockput(bb);
1584 /* ... and one or two dir entries */
1585 epb = s->dsize/VtEntrySize;
1586 bb = vtfileblock(s, dir->entry/epb, VtOREAD);
1587 blockDependency(b, bb, -1, nil, nil);
1588 vtblockput(bb);
1589 if(dir->mode & ModeDir){
1590 bb = sourceBlock(s, dir->mentry/epb, VtOREAD);
1591 blockDependency(b, bb, -1, nil, nil);
1592 vtblockput(bb);
1594 #endif
1596 vtblockdirty(b);
1597 vtblockput(b);
1598 return bo;
1599 Err:
1600 vtblockput(b);
1601 return NilBlock;
1604 static int
1605 chksource(VacFile *f)
1607 if(f->partial)
1608 return 0;
1610 if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
1611 werrstr(ERemoved);
1612 return -1;
1614 return 0;
1617 static int
1618 filerlock(VacFile *f)
1620 // assert(!canwlock(&f->fs->elk));
1621 rlock(&f->lk);
1622 if(chksource(f) < 0){
1623 runlock(&f->lk);
1624 return -1;
1626 return 0;
1629 static void
1630 filerunlock(VacFile *f)
1632 runlock(&f->lk);
1635 static int
1636 filelock(VacFile *f)
1638 // assert(!canwlock(&f->fs->elk));
1639 wlock(&f->lk);
1640 if(chksource(f) < 0){
1641 wunlock(&f->lk);
1642 return -1;
1644 return 0;
1647 static void
1648 fileunlock(VacFile *f)
1650 wunlock(&f->lk);
1654 * f->source and f->msource must NOT be locked.
1655 * fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2).
1656 * We have to respect that ordering.
1658 static void
1659 filemetalock(VacFile *f)
1661 assert(f->up != nil);
1662 // assert(!canwlock(&f->fs->elk));
1663 wlock(&f->up->lk);
1666 static void
1667 filemetaunlock(VacFile *f)
1669 wunlock(&f->up->lk);
1673 * f->source and f->msource must NOT be locked.
1674 * see filemetalock.
1676 static void
1677 fileraccess(VacFile* f)
1679 if(f->mode == VtOREAD)
1680 return;
1682 filemetalock(f);
1683 f->dir.atime = time(0L);
1684 f->dirty = 1;
1685 filemetaunlock(f);
1689 * f->source and f->msource must NOT be locked.
1690 * see filemetalock.
1692 static void
1693 filewaccess(VacFile* f, char *mid)
1695 if(f->mode == VtOREAD)
1696 return;
1698 filemetalock(f);
1699 f->dir.atime = f->dir.mtime = time(0L);
1700 if(strcmp(f->dir.mid, mid) != 0){
1701 vtfree(f->dir.mid);
1702 f->dir.mid = vtstrdup(mid);
1704 f->dir.mcount++;
1705 f->dirty = 1;
1706 filemetaunlock(f);
1708 /*RSC: let's try this */
1709 /*presotto - lets not
1710 if(f->up)
1711 filewaccess(f->up, mid);
1715 #if 0
1716 static void
1717 markCopied(Block *b)
1719 VtBlock *lb;
1720 Label l;
1722 if(globalToLocal(b->score) == NilBlock)
1723 return;
1725 if(!(b->l.state & BsCopied)){
1727 * We need to record that there are now pointers in
1728 * b that are not unique to b. We do this by marking
1729 * b as copied. Since we don't return the label block,
1730 * the caller can't get the dependencies right. So we have
1731 * to flush the block ourselves. This is a rare occurrence.
1733 l = b->l;
1734 l.state |= BsCopied;
1735 lb = _blockSetLabel(b, &l);
1736 WriteAgain:
1737 while(!blockWrite(lb)){
1738 fprint(2, "getEntry: could not write label block\n");
1739 sleep(10*1000);
1741 while(lb->iostate != BioClean && lb->iostate != BioDirty){
1742 assert(lb->iostate == BioWriting);
1743 vtSleep(lb->ioready);
1745 if(lb->iostate == BioDirty)
1746 goto WriteAgain;
1747 vtblockput(lb);
1751 static int
1752 getEntry(VtFile *r, Entry *e, int mark)
1754 Block *b;
1756 if(r == nil){
1757 memset(&e, 0, sizeof e);
1758 return 1;
1761 b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, VtOREAD);
1762 if(b == nil)
1763 return 0;
1764 if(!entryUnpack(e, b->data, r->offset % r->epb)){
1765 vtblockput(b);
1766 return 0;
1769 if(mark)
1770 markCopied(b);
1771 vtblockput(b);
1772 return 1;
1775 static int
1776 setEntry(Source *r, Entry *e)
1778 Block *b;
1779 Entry oe;
1781 b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, VtORDWR);
1782 if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score);
1783 if(b == nil)
1784 return 0;
1785 if(!entryUnpack(&oe, b->data, r->offset % r->epb)){
1786 vtblockput(b);
1787 return 0;
1789 e->gen = oe.gen;
1790 entryPack(e, b->data, r->offset % r->epb);
1792 /* BUG b should depend on the entry pointer */
1794 markCopied(b);
1795 vtblockdirty(b);
1796 vtblockput(b);
1797 return 1;
1800 /* assumes hold elk */
1801 int
1802 fileSnapshot(VacFile *dst, VacFile *src, u32int epoch, int doarchive)
1804 Entry e, ee;
1806 /* add link to snapshot */
1807 if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1))
1808 return 0;
1810 e.snap = epoch;
1811 e.archive = doarchive;
1812 ee.snap = epoch;
1813 ee.archive = doarchive;
1815 if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee))
1816 return 0;
1817 return 1;
1820 int
1821 fileGetSources(VacFile *f, Entry *e, Entry *ee, int mark)
1823 if(!getEntry(f->source, e, mark)
1824 || !getEntry(f->msource, ee, mark))
1825 return 0;
1826 return 1;
1829 int
1830 fileWalkSources(VacFile *f)
1832 if(f->mode == VtOREAD)
1833 return 1;
1834 if(!sourceLock2(f->source, f->msource, VtORDWR))
1835 return 0;
1836 vtfileunlock(f->source);
1837 vtfileunlock(f->msource);
1838 return 1;
1841 #endif