Blob


1 #include "stdinc.h"
2 #include "vac.h"
3 #include "dat.h"
4 #include "fns.h"
5 #include "error.h"
7 #define debug 0
9 /*
10 * locking order is upwards. A thread can hold the lock for a VacFile
11 * and then acquire the lock of its parent
12 */
13 struct VacFile
14 {
15 VacFs *fs; /* immutable */
17 /* meta data for file: protected by the lk in the parent */
18 int ref; /* holds this data structure up */
20 int partial; /* file was never really open */
21 int removed; /* file has been removed */
22 int dirty; /* dir is dirty with respect to meta data in block */
23 u32int boff; /* block offset within msource for this file's metadata */
24 VacDir dir; /* metadata for this file */
25 VacFile *up; /* parent file */
26 VacFile *next; /* sibling */
28 RWLock lk; /* lock for the following */
29 VtFile *source; /* actual data */
30 VtFile *msource; /* metadata for children in a directory */
31 VacFile *down; /* children */
32 int mode;
33 };
35 static int filelock(VacFile*);
36 static u32int filemetaalloc(VacFile*, VacDir*, u32int);
37 static int filemetaflush2(VacFile*, char*);
38 static void filemetalock(VacFile*);
39 static void filemetaunlock(VacFile*);
40 static void fileraccess(VacFile*);
41 static int filerlock(VacFile*);
42 static void filerunlock(VacFile*);
43 static void fileunlock(VacFile*);
44 static void filewaccess(VacFile*, char*);
46 void mbinit(MetaBlock*, u8int*, uint, uint);
47 int mbsearch(MetaBlock*, char*, int*, MetaEntry*);
48 int mbresize(MetaBlock*, MetaEntry*, int);
49 VacFile *vdlookup(VacFile*, char*);
51 static VacFile*
52 filealloc(VacFs *fs)
53 {
54 VacFile *f;
56 f = vtmallocz(sizeof(VacFile));
57 f->ref = 1;
58 f->fs = fs;
59 f->boff = NilBlock;
60 f->mode = fs->mode;
61 return f;
62 }
64 static void
65 filefree(VacFile *f)
66 {
67 vtfileclose(f->source);
68 vtfileclose(f->msource);
69 vdcleanup(&f->dir);
70 memset(f, ~0, sizeof *f); /* paranoia */
71 vtfree(f);
72 }
74 /*
75 * the file is locked already
76 * f->msource is unlocked
77 */
78 static VacFile*
79 dirlookup(VacFile *f, char *elem)
80 {
81 int i;
82 MetaBlock mb;
83 MetaEntry me;
84 VtBlock *b;
85 VtFile *meta;
86 VacFile *ff;
87 u32int bo, nb;
89 meta = f->msource;
90 b = nil;
91 if(vtfilelock(meta, -1) < 0)
92 return nil;
93 nb = (vtfilegetsize(meta)+meta->dsize-1)/meta->dsize;
94 for(bo=0; bo<nb; bo++){
95 b = vtfileblock(meta, bo, VtOREAD);
96 if(b == nil)
97 goto Err;
98 if(mbunpack(&mb, b->data, meta->dsize) < 0)
99 goto Err;
100 if(mbsearch(&mb, elem, &i, &me) >= 0){
101 ff = filealloc(f->fs);
102 if(vdunpack(&ff->dir, &me) < 0){
103 filefree(ff);
104 goto Err;
106 vtfileunlock(meta);
107 vtblockput(b);
108 ff->boff = bo;
109 ff->mode = f->mode;
110 return ff;
113 vtblockput(b);
114 b = nil;
116 werrstr(ENoFile);
117 /* fall through */
118 Err:
119 vtfileunlock(meta);
120 vtblockput(b);
121 return nil;
124 VacFile*
125 _vacfileroot(VacFs *fs, VtFile *r)
127 int redirected;
128 char err[ERRMAX];
129 VtBlock *b;
130 VtFile *r0, *r1, *r2;
131 MetaBlock mb;
132 MetaEntry me;
133 VacFile *root, *mr;
135 redirected = 0;
136 Top:
137 b = nil;
138 root = nil;
139 mr = nil;
140 r1 = nil;
141 r2 = nil;
143 if(vtfilelock(r, -1) < 0)
144 return nil;
145 r0 = vtfileopen(r, 0, fs->mode);
146 if(debug)
147 fprint(2, "r0 %p\n", r0);
148 if(r0 == nil)
149 goto Err;
150 r2 = vtfileopen(r, 2, fs->mode);
151 if(debug)
152 fprint(2, "r2 %p\n", r2);
153 if(r2 == nil){
154 /*
155 * some vac files (e.g., from fossil)
156 * have an extra layer of indirection.
157 */
158 rerrstr(err, sizeof err);
159 if(!redirected && strstr(err, "not active")){
160 redirected = 1;
161 vtfileunlock(r);
162 r = r0;
163 goto Top;
165 goto Err;
167 r1 = vtfileopen(r, 1, fs->mode);
168 if(debug)
169 fprint(2, "r1 %p\n", r1);
170 if(r1 == nil)
171 goto Err;
173 mr = filealloc(fs);
174 mr->msource = r2;
175 r2 = nil;
177 root = filealloc(fs);
178 root->boff = 0;
179 root->up = mr;
180 root->source = r0;
181 r0 = nil;
182 root->msource = r1;
183 r1 = nil;
185 mr->down = root;
187 if(vtfilelock(mr->msource, -1) < 0)
188 goto Err;
189 b = vtfileblock(mr->msource, 0, VtOREAD);
190 vtfileunlock(mr->msource);
191 if(b == nil)
192 goto Err;
194 if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
195 goto Err;
197 meunpack(&me, &mb, 0);
198 if(vdunpack(&root->dir, &me) < 0)
199 goto Err;
200 vtblockput(b);
201 vtfileunlock(r);
202 fileraccess(root);
204 return root;
205 Err:
206 vtblockput(b);
207 if(r0)
208 vtfileclose(r0);
209 if(r1)
210 vtfileclose(r1);
211 if(r2)
212 vtfileclose(r2);
213 if(mr)
214 filefree(mr);
215 if(root)
216 filefree(root);
217 vtfileunlock(r);
219 return nil;
222 static VtFile *
223 fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
225 VtFile *r;
227 if(vtfilelock(f->source, mode) < 0)
228 return nil;
229 r = vtfileopen(f->source, offset, mode);
230 vtfileunlock(f->source);
231 if(r == nil)
232 return nil;
233 if(r->gen != gen){
234 werrstr(ERemoved);
235 goto Err;
237 if(r->dir != dir && r->mode != -1){
238 fprint(2, "fileopensource: dir mismatch %d %d\n", r->dir, dir);
239 werrstr(EBadMeta);
240 goto Err;
242 return r;
243 Err:
244 vtfileclose(r);
245 return nil;
248 VacFile*
249 _filewalk(VacFile *f, char *elem, int partial)
251 VacFile *ff;
253 fileraccess(f);
255 if(elem[0] == 0){
256 werrstr(EBadPath);
257 return nil;
260 if(!vacfileisdir(f)){
261 werrstr(ENotDir);
262 return nil;
265 if(strcmp(elem, ".") == 0){
266 return vacfileincref(f);
269 if(strcmp(elem, "..") == 0){
270 if(vacfileisroot(f))
271 return vacfileincref(f);
272 return vacfileincref(f->up);
275 if(filelock(f) < 0)
276 return nil;
278 for(ff = f->down; ff; ff=ff->next){
279 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
280 ff->ref++;
281 goto Exit;
285 ff = dirlookup(f, elem);
286 if(ff == nil)
287 goto Err;
289 if(ff->dir.mode & ModeSnapshot)
290 ff->mode = VtOREAD;
292 if(partial){
293 /*
294 * Do nothing. We're opening this file only so we can clri it.
295 * Usually the sources can't be opened, hence we won't even bother.
296 * Be VERY careful with the returned file. If you hand it to a routine
297 * expecting ff->source and/or ff->msource to be non-nil, we're
298 * likely to dereference nil. FileClri should be the only routine
299 * setting partial.
300 */
301 ff->partial = 1;
302 }else if(ff->dir.mode & ModeDir){
303 ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 1, ff->mode);
304 ff->msource = fileopensource(f, ff->dir.mentry, ff->dir.mgen, 0, ff->mode);
305 if(ff->source == nil || ff->msource == nil)
306 goto Err;
307 }else{
308 ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode);
309 if(ff->source == nil)
310 goto Err;
313 /* link in and up parent ref count */
314 ff->next = f->down;
315 f->down = ff;
316 ff->up = f;
317 vacfileincref(f);
318 Exit:
319 fileunlock(f);
320 return ff;
321 Err:
322 fileunlock(f);
323 if(ff != nil)
324 vacfiledecref(ff);
325 return nil;
328 VacFile*
329 vacfilewalk(VacFile *f, char *elem)
331 return _filewalk(f, elem, 0);
334 VacFile*
335 _fileopen(VacFs *fs, char *path, int partial)
337 VacFile *f, *ff;
338 char *p, elem[VtMaxStringSize], *opath;
339 int n;
341 f = fs->root;
342 vacfileincref(f);
343 opath = path;
344 while(*path != 0){
345 for(p = path; *p && *p != '/'; p++)
347 n = p - path;
348 if(n > 0){
349 if(n > VtMaxStringSize){
350 werrstr("%s: element too long", EBadPath);
351 goto Err;
353 memmove(elem, path, n);
354 elem[n] = 0;
355 ff = _filewalk(f, elem, partial && *p=='\0');
356 if(ff == nil){
357 werrstr("%.*s: %R", utfnlen(opath, p-opath), opath);
358 goto Err;
360 vacfiledecref(f);
361 f = ff;
363 if(*p == '/')
364 p++;
365 path = p;
367 return f;
368 Err:
369 vacfiledecref(f);
370 return nil;
373 VacFile*
374 vacfileopen(VacFs *fs, char *path)
376 return _fileopen(fs, path, 0);
379 #if 0
380 static void
381 filesettmp(VacFile *f, int istmp)
383 int i;
384 VtEntry e;
385 VtFile *r;
387 for(i=0; i<2; i++){
388 if(i==0)
389 r = f->source;
390 else
391 r = f->msource;
392 if(r == nil)
393 continue;
394 if(vtfilegetentry(r, &e) < 0){
395 fprint(2, "vtfilegetentry failed (cannot happen): %r\n");
396 continue;
398 if(istmp)
399 e.flags |= VtEntryNoArchive;
400 else
401 e.flags &= ~VtEntryNoArchive;
402 if(vtfilesetentry(r, &e) < 0){
403 fprint(2, "vtfilesetentry failed (cannot happen): %r\n");
404 continue;
408 #endif
410 VacFile*
411 vacfilecreate(VacFile *f, char *elem, ulong mode, char *uid)
413 VacFile *ff;
414 VacDir *dir;
415 VtFile *pr, *r, *mr;
416 int isdir;
418 if(filelock(f) < 0)
419 return nil;
421 r = nil;
422 mr = nil;
423 for(ff = f->down; ff; ff=ff->next){
424 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
425 ff = nil;
426 werrstr(EExists);
427 goto Err1;
431 ff = dirlookup(f, elem);
432 if(ff != nil){
433 werrstr(EExists);
434 goto Err1;
437 pr = f->source;
438 if(pr->mode != VtORDWR){
439 werrstr(EReadOnly);
440 goto Err1;
443 if(vtfilelock2(f->source, f->msource, -1) < 0)
444 goto Err1;
446 ff = filealloc(f->fs);
447 isdir = mode & ModeDir;
449 r = vtfilecreate(pr, pr->psize, pr->dsize, isdir ? VtDirType : VtDataType);
450 if(r == nil)
451 goto Err;
452 if(isdir){
453 mr = vtfilecreate(pr, pr->psize, pr->dsize, VtDataType);
454 if(mr == nil)
455 goto Err;
458 dir = &ff->dir;
459 dir->elem = vtstrdup(elem);
460 dir->entry = r->offset;
461 dir->gen = r->gen;
462 if(isdir){
463 dir->mentry = mr->offset;
464 dir->mgen = mr->gen;
466 dir->size = 0;
467 if(_vacfsnextqid(f->fs, &dir->qid) < 0)
468 goto Err;
469 dir->uid = vtstrdup(uid);
470 dir->gid = vtstrdup(f->dir.gid);
471 dir->mid = vtstrdup(uid);
472 dir->mtime = time(0L);
473 dir->mcount = 0;
474 dir->ctime = dir->mtime;
475 dir->atime = dir->mtime;
476 dir->mode = mode;
478 ff->boff = filemetaalloc(f, dir, 0);
479 if(ff->boff == NilBlock)
480 goto Err;
482 vtfileunlock(f->source);
483 vtfileunlock(f->msource);
485 ff->source = r;
486 ff->msource = mr;
488 #if 0
489 if(mode&ModeTemporary){
490 if(vtfilelock2(r, mr, -1) < 0)
491 goto Err1;
492 filesettmp(ff, 1);
493 vtfileunlock(r);
494 if(mr)
495 vtfileunlock(mr);
497 #endif
499 /* committed */
501 /* link in and up parent ref count */
502 ff->next = f->down;
503 f->down = ff;
504 ff->up = f;
505 vacfileincref(f);
507 filewaccess(f, uid);
509 fileunlock(f);
510 return ff;
512 Err:
513 vtfileunlock(f->source);
514 vtfileunlock(f->msource);
515 Err1:
516 if(r){
517 vtfilelock(r, -1);
518 vtfileremove(r);
520 if(mr){
521 vtfilelock(mr, -1);
522 vtfileremove(mr);
524 if(ff)
525 vacfiledecref(ff);
526 fileunlock(f);
527 return nil;
530 int
531 vacfileblockscore(VacFile *f, u32int bn, u8int *score)
533 VtFile *s;
534 uvlong size;
535 int dsize, ret;
537 ret = -1;
538 if(filerlock(f) < 0)
539 return -1;
540 fileraccess(f);
541 if(vtfilelock(f->source, VtOREAD) < 0)
542 goto out;
544 s = f->source;
545 dsize = s->dsize;
546 size = vtfilegetsize(s);
547 if((uvlong)bn*dsize >= size)
548 goto out;
549 ret = vtfileblockscore(f->source, bn, score);
551 out:
552 vtfileunlock(f->source);
553 filerunlock(f);
554 return ret;
557 int
558 vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
560 VtFile *s;
561 uvlong size;
562 u32int bn;
563 int off, dsize, n, nn;
564 VtBlock *b;
565 uchar *p;
567 if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset);
569 if(filerlock(f) < 0)
570 return -1;
572 if(offset < 0){
573 werrstr(EBadOffset);
574 goto Err1;
577 fileraccess(f);
579 if(vtfilelock(f->source, VtOREAD) < 0)
580 goto Err1;
582 s = f->source;
583 dsize = s->dsize;
584 size = vtfilegetsize(s);
586 if(offset >= size)
587 offset = size;
589 if(cnt > size-offset)
590 cnt = size-offset;
591 bn = offset/dsize;
592 off = offset%dsize;
593 p = buf;
594 while(cnt > 0){
595 b = vtfileblock(s, bn, OREAD);
596 if(b == nil)
597 goto Err;
598 n = cnt;
599 if(n > dsize-off)
600 n = dsize-off;
601 nn = dsize-off;
602 if(nn > n)
603 nn = n;
604 memmove(p, b->data+off, nn);
605 memset(p+nn, 0, nn-n);
606 off = 0;
607 bn++;
608 cnt -= n;
609 p += n;
610 vtblockput(b);
612 vtfileunlock(s);
613 filerunlock(f);
614 return p-(uchar*)buf;
616 Err:
617 vtfileunlock(s);
618 Err1:
619 filerunlock(f);
620 return -1;
623 #if 0
624 /*
625 * Changes the file block bn to be the given block score.
626 * Very sneaky. Only used by flfmt.
627 */
628 int
629 filemapblock(VacFile *f, ulong bn, uchar score[VtScoreSize], ulong tag)
631 VtBlock *b;
632 VtEntry e;
633 VtFile *s;
635 if(filelock(f) < 0)
636 return -1;
638 s = nil;
639 if(f->dir.mode & ModeDir){
640 werrstr(ENotFile);
641 goto Err;
644 if(f->source->mode != VtORDWR){
645 werrstr(EReadOnly);
646 goto Err;
649 if(vtfilelock(f->source, -1) < 0)
650 goto Err;
652 s = f->source;
653 b = _vtfileblock(s, bn, VtORDWR, 1, tag);
654 if(b == nil)
655 goto Err;
657 if(vtfilegetentry(s, &e) < 0)
658 goto Err;
659 if(b->l.type == BtDir){
660 memmove(e.score, score, VtScoreSize);
661 assert(e.tag == tag || e.tag == 0);
662 e.tag = tag;
663 e.flags |= VtEntryLocal;
664 vtentrypack(&e, b->data, f->source->offset % f->source->epb);
665 }else
666 memmove(b->data + (bn%(e.psize/VtScoreSize))*VtScoreSize, score, VtScoreSize);
667 /* vtblockdirty(b); */
668 vtblockput(b);
669 vtfileunlock(s);
670 fileunlock(f);
671 return 0;
673 Err:
674 if(s)
675 vtfileunlock(s);
676 fileunlock(f);
677 return -1;
679 #endif
681 int
682 vacfilesetsize(VacFile *f, uvlong size)
684 int r;
686 if(filelock(f) < 0)
687 return -1;
688 r = 0;
689 if(f->dir.mode & ModeDir){
690 werrstr(ENotFile);
691 goto Err;
693 if(f->source->mode != VtORDWR){
694 werrstr(EReadOnly);
695 goto Err;
697 if(vtfilelock(f->source, -1) < 0)
698 goto Err;
699 r = vtfilesetsize(f->source, size);
700 vtfileunlock(f->source);
701 Err:
702 fileunlock(f);
703 return r;
706 int
707 filewrite(VacFile *f, void *buf, int cnt, vlong offset, char *uid)
709 VtFile *s;
710 ulong bn;
711 int off, dsize, n;
712 VtBlock *b;
713 uchar *p;
714 vlong eof;
716 if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset);
718 if(filelock(f) < 0)
719 return -1;
721 s = nil;
722 if(f->dir.mode & ModeDir){
723 werrstr(ENotFile);
724 goto Err;
727 if(f->source->mode != VtORDWR){
728 werrstr(EReadOnly);
729 goto Err;
731 if(offset < 0){
732 werrstr(EBadOffset);
733 goto Err;
736 filewaccess(f, uid);
738 if(vtfilelock(f->source, -1) < 0)
739 goto Err;
740 s = f->source;
741 dsize = s->dsize;
743 eof = vtfilegetsize(s);
744 if(f->dir.mode & ModeAppend)
745 offset = eof;
746 bn = offset/dsize;
747 off = offset%dsize;
748 p = buf;
749 while(cnt > 0){
750 n = cnt;
751 if(n > dsize-off)
752 n = dsize-off;
753 b = vtfileblock(s, bn, n<dsize?VtORDWR:VtOWRITE);
754 if(b == nil){
755 if(offset > eof)
756 vtfilesetsize(s, offset);
757 goto Err;
759 memmove(b->data+off, p, n);
760 off = 0;
761 cnt -= n;
762 p += n;
763 offset += n;
764 bn++;
765 /* vtblockdirty(b); */
766 vtblockput(b);
768 if(offset > eof && vtfilesetsize(s, offset) < 0)
769 goto Err;
770 vtfileunlock(s);
771 fileunlock(f);
772 return p-(uchar*)buf;
773 Err:
774 if(s)
775 vtfileunlock(s);
776 fileunlock(f);
777 return -1;
780 int
781 vacfilegetdir(VacFile *f, VacDir *dir)
783 if(filerlock(f) < 0)
784 return -1;
786 filemetalock(f);
787 vdcopy(dir, &f->dir);
788 filemetaunlock(f);
790 if(!vacfileisdir(f)){
791 if(vtfilelock(f->source, VtOREAD) < 0){
792 filerunlock(f);
793 return -1;
795 dir->size = vtfilegetsize(f->source);
796 vtfileunlock(f->source);
798 filerunlock(f);
800 return 0;
803 int
804 vacfiletruncate(VacFile *f, char *uid)
806 if(vacfileisdir(f)){
807 werrstr(ENotFile);
808 return -1;
811 if(filelock(f) < 0)
812 return -1;
814 if(f->source->mode != VtORDWR){
815 werrstr(EReadOnly);
816 fileunlock(f);
817 return -1;
819 if(vtfilelock(f->source, -1) < 0){
820 fileunlock(f);
821 return -1;
823 if(vtfiletruncate(f->source) < 0){
824 vtfileunlock(f->source);
825 fileunlock(f);
826 return -1;
828 vtfileunlock(f->source);
829 fileunlock(f);
831 filewaccess(f, uid);
833 return 0;
836 int
837 vacfilesetdir(VacFile *f, VacDir *dir, char *uid)
839 VacFile *ff;
840 char *oelem;
841 u32int mask;
842 u64int size;
844 /* can not set permissions for the root */
845 if(vacfileisroot(f)){
846 werrstr(ERoot);
847 return -1;
850 if(filelock(f) < 0)
851 return -1;
853 if(f->source->mode != VtORDWR){
854 werrstr(EReadOnly);
855 fileunlock(f);
856 return -1;
859 filemetalock(f);
861 /* check new name does not already exist */
862 if(strcmp(f->dir.elem, dir->elem) != 0){
863 for(ff = f->up->down; ff; ff=ff->next){
864 if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
865 werrstr(EExists);
866 goto Err;
870 ff = dirlookup(f->up, dir->elem);
871 if(ff != nil){
872 vacfiledecref(ff);
873 werrstr(EExists);
874 goto Err;
878 if(vtfilelock2(f->source, f->msource, -1) < 0)
879 goto Err;
880 if(!vacfileisdir(f)){
881 size = vtfilegetsize(f->source);
882 if(size != dir->size){
883 if(vtfilesetsize(f->source, dir->size) < 0){
884 vtfileunlock(f->source);
885 if(f->msource)
886 vtfileunlock(f->msource);
887 goto Err;
889 /* commited to changing it now */
892 /* commited to changing it now */
893 #if 0
894 if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary))
895 filesettmp(f, dir->mode&ModeTemporary);
896 #endif
897 vtfileunlock(f->source);
898 if(f->msource)
899 vtfileunlock(f->msource);
901 oelem = nil;
902 if(strcmp(f->dir.elem, dir->elem) != 0){
903 oelem = f->dir.elem;
904 f->dir.elem = vtstrdup(dir->elem);
907 if(strcmp(f->dir.uid, dir->uid) != 0){
908 vtfree(f->dir.uid);
909 f->dir.uid = vtstrdup(dir->uid);
912 if(strcmp(f->dir.gid, dir->gid) != 0){
913 vtfree(f->dir.gid);
914 f->dir.gid = vtstrdup(dir->gid);
917 f->dir.mtime = dir->mtime;
918 f->dir.atime = dir->atime;
920 /*fprint(2, "mode %x %x ", f->dir.mode, dir->mode); */
921 mask = ~(ModeDir|ModeSnapshot);
922 f->dir.mode &= ~mask;
923 f->dir.mode |= mask & dir->mode;
924 f->dirty = 1;
925 /*fprint(2, "->%x\n", f->dir.mode); */
927 filemetaflush2(f, oelem);
928 vtfree(oelem);
930 filemetaunlock(f);
931 fileunlock(f);
933 filewaccess(f->up, uid);
935 return 0;
936 Err:
937 filemetaunlock(f);
938 fileunlock(f);
939 return -1;
942 int
943 vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
945 int ret;
947 if(filelock(f) < 0)
948 return -1;
949 filemetalock(f);
950 f->dir.qidspace = 1;
951 f->dir.qidoffset = offset;
952 f->dir.qidmax = max;
953 ret = filemetaflush2(f, nil);
954 filemetaunlock(f);
955 fileunlock(f);
956 return ret;
959 uvlong
960 vacfilegetid(VacFile *f)
962 /* immutable */
963 return f->dir.qid;
966 ulong
967 vacfilegetmcount(VacFile *f)
969 ulong mcount;
971 filemetalock(f);
972 mcount = f->dir.mcount;
973 filemetaunlock(f);
974 return mcount;
977 ulong
978 vacfilegetmode(VacFile *f)
980 ulong mode;
982 filemetalock(f);
983 mode = f->dir.mode;
984 filemetaunlock(f);
985 return mode;
988 int
989 vacfileisdir(VacFile *f)
991 /* immutable */
992 return (f->dir.mode & ModeDir) != 0;
995 int
996 vacfileisroot(VacFile *f)
998 return f == f->fs->root;
1001 int
1002 vacfilegetsize(VacFile *f, uvlong *size)
1004 if(filerlock(f) < 0)
1005 return 0;
1006 if(vtfilelock(f->source, VtOREAD) < 0){
1007 filerunlock(f);
1008 return -1;
1010 *size = vtfilegetsize(f->source);
1011 vtfileunlock(f->source);
1012 filerunlock(f);
1014 return 0;
1017 int
1018 vacfilegetvtentry(VacFile *f, VtEntry *e)
1020 if(filerlock(f) < 0)
1021 return 0;
1022 if(vtfilelock(f->source, VtOREAD) < 0){
1023 filerunlock(f);
1024 return -1;
1026 vtfilegetentry(f->source, e);
1027 vtfileunlock(f->source);
1028 filerunlock(f);
1030 return 0;
1033 void
1034 vacfilemetaflush(VacFile *f, int rec)
1036 VacFile **kids, *p;
1037 int nkids;
1038 int i;
1040 filemetalock(f);
1041 filemetaflush2(f, nil);
1042 filemetaunlock(f);
1044 if(!rec || !vacfileisdir(f))
1045 return;
1047 if(filelock(f) < 0)
1048 return;
1049 nkids = 0;
1050 for(p=f->down; p; p=p->next)
1051 nkids++;
1052 kids = vtmalloc(nkids*sizeof(VacFile*));
1053 i = 0;
1054 for(p=f->down; p; p=p->next){
1055 kids[i++] = p;
1056 p->ref++;
1058 fileunlock(f);
1060 for(i=0; i<nkids; i++){
1061 vacfilemetaflush(kids[i], 1);
1062 vacfiledecref(kids[i]);
1064 vtfree(kids);
1067 /* assumes metaLock is held */
1068 static int
1069 filemetaflush2(VacFile *f, char *oelem)
1071 VacFile *fp;
1072 VtBlock *b, *bb;
1073 MetaBlock mb;
1074 MetaEntry me, me2;
1075 int i, n;
1076 u32int boff;
1078 if(!f->dirty)
1079 return 0;
1081 if(oelem == nil)
1082 oelem = f->dir.elem;
1084 fp = f->up;
1086 if(vtfilelock(fp->msource, -1) < 0)
1087 return 0;
1088 /* can happen if source is clri'ed out from under us */
1089 if(f->boff == NilBlock)
1090 goto Err1;
1091 b = vtfileblock(fp->msource, f->boff, VtORDWR);
1092 if(b == nil)
1093 goto Err1;
1095 if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
1096 goto Err;
1097 if(mbsearch(&mb, oelem, &i, &me) < 0)
1098 goto Err;
1100 n = vdsize(&f->dir);
1101 if(0)fprint(2, "old size %d new size %d\n", me.size, n);
1103 if(mbresize(&mb, &me, n) >= 0){
1104 /* fits in the block */
1105 mbdelete(&mb, i, &me);
1106 if(strcmp(f->dir.elem, oelem) != 0)
1107 mbsearch(&mb, f->dir.elem, &i, &me2);
1108 vdpack(&f->dir, &me);
1109 mbinsert(&mb, i, &me);
1110 mbpack(&mb);
1111 /* vtblockdirty(b); */
1112 vtblockput(b);
1113 vtfileunlock(fp->msource);
1114 f->dirty = 0;
1115 return -1;
1119 * moving entry to another block
1120 * it is feasible for the fs to crash leaving two copies
1121 * of the directory entry. This is just too much work to
1122 * fix. Given that entries are only allocated in a block that
1123 * is less than PercentageFull, most modifications of meta data
1124 * will fit within the block. i.e. this code should almost
1125 * never be executed.
1127 boff = filemetaalloc(fp, &f->dir, f->boff+1);
1128 if(boff == NilBlock){
1129 /* mbResize might have modified block */
1130 mbpack(&mb);
1131 /* vtblockdirty(b); */
1132 goto Err;
1134 fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff);
1135 f->boff = boff;
1137 /* make sure deletion goes to disk after new entry */
1138 bb = vtfileblock(fp->msource, f->boff, VtORDWR);
1139 mbdelete(&mb, i, &me);
1140 mbpack(&mb);
1141 /* blockDependency(b, bb, -1, nil, nil); */
1142 vtblockput(bb);
1143 /* vtblockdirty(b); */
1144 vtblockput(b);
1145 vtfileunlock(fp->msource);
1147 f->dirty = 0;
1149 return 0;
1151 Err:
1152 vtblockput(b);
1153 Err1:
1154 vtfileunlock(fp->msource);
1155 return -1;
1158 static int
1159 filemetaremove(VacFile *f, char *uid)
1161 VtBlock *b;
1162 MetaBlock mb;
1163 MetaEntry me;
1164 int i;
1165 VacFile *up;
1167 b = nil;
1168 up = f->up;
1169 filewaccess(up, uid);
1170 filemetalock(f);
1172 if(vtfilelock(up->msource, VtORDWR) < 0)
1173 goto Err;
1174 b = vtfileblock(up->msource, f->boff, VtORDWR);
1175 if(b == nil)
1176 goto Err;
1178 if(mbunpack(&mb, b->data, up->msource->dsize) < 0)
1179 goto Err;
1180 if(mbsearch(&mb, f->dir.elem, &i, &me) < 0)
1181 goto Err;
1182 mbdelete(&mb, i, &me);
1183 mbpack(&mb);
1184 vtfileunlock(up->msource);
1186 /* vtblockdirty(b); */
1187 vtblockput(b);
1189 f->removed = 1;
1190 f->boff = NilBlock;
1191 f->dirty = 0;
1193 filemetaunlock(f);
1194 return 0;
1196 Err:
1197 vtfileunlock(up->msource);
1198 vtblockput(b);
1199 filemetaunlock(f);
1200 return -1;
1203 /* assume file is locked, assume f->msource is locked */
1204 static int
1205 filecheckempty(VacFile *f)
1207 u32int i, n;
1208 VtBlock *b;
1209 MetaBlock mb;
1210 VtFile *r;
1212 r = f->msource;
1213 n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
1214 for(i=0; i<n; i++){
1215 b = vtfileblock(r, i, VtORDWR);
1216 if(b == nil)
1217 goto Err;
1218 if(mbunpack(&mb, b->data, r->dsize) < 0)
1219 goto Err;
1220 if(mb.nindex > 0){
1221 werrstr(ENotEmpty);
1222 goto Err;
1224 vtblockput(b);
1226 return 0;
1227 Err:
1228 vtblockput(b);
1229 return -1;
1232 int
1233 vacfileremove(VacFile *f, char *muid)
1235 VacFile *ff;
1237 /* can not remove the root */
1238 if(vacfileisroot(f)){
1239 werrstr(ERoot);
1240 return -1;
1243 if(filelock(f) < 0)
1244 return -1;
1246 if(f->source->mode != VtORDWR){
1247 werrstr(EReadOnly);
1248 goto Err1;
1250 if(vtfilelock2(f->source, f->msource, -1) < 0)
1251 goto Err1;
1252 if(vacfileisdir(f) && filecheckempty(f)<0)
1253 goto Err;
1255 for(ff=f->down; ff; ff=ff->next)
1256 assert(ff->removed);
1258 vtfileremove(f->source);
1259 f->source = nil;
1260 if(f->msource){
1261 vtfileremove(f->msource);
1262 f->msource = nil;
1265 fileunlock(f);
1267 if(filemetaremove(f, muid) < 0)
1268 return -1;
1270 return 0;
1272 Err:
1273 vtfileunlock(f->source);
1274 if(f->msource)
1275 vtfileunlock(f->msource);
1276 Err1:
1277 fileunlock(f);
1278 return -1;
1281 static int
1282 clri(VacFile *f, char *uid)
1284 int r;
1286 if(f == nil)
1287 return -1;
1288 if(f->up->source->mode != VtORDWR){
1289 werrstr(EReadOnly);
1290 vacfiledecref(f);
1291 return -1;
1293 r = filemetaremove(f, uid);
1294 vacfiledecref(f);
1295 return r;
1298 int
1299 vacfileclripath(VacFs *fs, char *path, char *uid)
1301 return clri(_fileopen(fs, path, 1), uid);
1304 int
1305 vacfileclri(VacFile *dir, char *elem, char *uid)
1307 return clri(_filewalk(dir, elem, 1), uid);
1310 VacFile*
1311 vacfileincref(VacFile *vf)
1313 filemetalock(vf);
1314 assert(vf->ref > 0);
1315 vf->ref++;
1316 filemetaunlock(vf);
1317 return vf;
1320 int
1321 vacfiledecref(VacFile *f)
1323 VacFile *p, *q, **qq;
1325 if(f->up == nil){
1326 /* never linked in */
1327 assert(f->ref == 1);
1328 filefree(f);
1329 return 0;
1332 filemetalock(f);
1333 f->ref--;
1334 if(f->ref > 0){
1335 filemetaunlock(f);
1336 return -1;
1338 assert(f->ref == 0);
1339 assert(f->down == nil);
1341 filemetaflush2(f, nil);
1343 p = f->up;
1344 qq = &p->down;
1345 for(q = *qq; q; q = *qq){
1346 if(q == f)
1347 break;
1348 qq = &q->next;
1350 assert(q != nil);
1351 *qq = f->next;
1353 filemetaunlock(f);
1354 filefree(f);
1355 vacfiledecref(p);
1356 return 0;
1359 VacFile*
1360 vacfilegetparent(VacFile *f)
1362 if(vacfileisroot(f))
1363 return vacfileincref(f);
1364 return vacfileincref(f->up);
1367 int
1368 vacfilewrite(VacFile *file, void *buf, int n, vlong offset, char *muid)
1370 werrstr("read only file system");
1371 return -1;
1374 VacDirEnum*
1375 vdeopen(VacFile *f)
1377 VacDirEnum *vde;
1378 VacFile *p;
1380 if(!vacfileisdir(f)){
1381 werrstr(ENotDir);
1382 return nil;
1385 /* flush out meta data */
1386 if(filelock(f) < 0)
1387 return nil;
1388 for(p=f->down; p; p=p->next)
1389 filemetaflush2(p, nil);
1390 fileunlock(f);
1392 vde = vtmallocz(sizeof(VacDirEnum));
1393 vde->file = vacfileincref(f);
1395 return vde;
1398 static int
1399 direntrysize(VtFile *s, ulong elem, ulong gen, uvlong *size)
1401 VtBlock *b;
1402 ulong bn;
1403 VtEntry e;
1404 int epb;
1406 epb = s->dsize/VtEntrySize;
1407 bn = elem/epb;
1408 elem -= bn*epb;
1410 b = vtfileblock(s, bn, VtOREAD);
1411 if(b == nil)
1412 goto Err;
1413 if(vtentryunpack(&e, b->data, elem) < 0)
1414 goto Err;
1416 /* hanging entries are returned as zero size */
1417 if(!(e.flags & VtEntryActive) || e.gen != gen)
1418 *size = 0;
1419 else
1420 *size = e.size;
1421 vtblockput(b);
1422 return 0;
1424 Err:
1425 vtblockput(b);
1426 return -1;
1429 static int
1430 vdefill(VacDirEnum *vde)
1432 int i, n;
1433 VtFile *meta, *source;
1434 MetaBlock mb;
1435 MetaEntry me;
1436 VacFile *f;
1437 VtBlock *b;
1438 VacDir *de;
1440 /* clean up first */
1441 for(i=vde->i; i<vde->n; i++)
1442 vdcleanup(vde->buf+i);
1443 vtfree(vde->buf);
1444 vde->buf = nil;
1445 vde->i = 0;
1446 vde->n = 0;
1448 f = vde->file;
1450 source = f->source;
1451 meta = f->msource;
1453 b = vtfileblock(meta, vde->boff, VtOREAD);
1454 if(b == nil)
1455 goto Err;
1456 if(mbunpack(&mb, b->data, meta->dsize) < 0)
1457 goto Err;
1459 n = mb.nindex;
1460 vde->buf = vtmalloc(n * sizeof(VacDir));
1462 for(i=0; i<n; i++){
1463 de = vde->buf + i;
1464 meunpack(&me, &mb, i);
1465 if(vdunpack(de, &me) < 0)
1466 goto Err;
1467 vde->n++;
1468 if(!(de->mode & ModeDir))
1469 if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
1470 goto Err;
1472 vde->boff++;
1473 vtblockput(b);
1474 return 0;
1475 Err:
1476 vtblockput(b);
1477 return -1;
1480 int
1481 vderead(VacDirEnum *vde, VacDir *de)
1483 int ret, didread;
1484 VacFile *f;
1485 u32int nb;
1487 f = vde->file;
1488 if(filerlock(f) < 0)
1489 return -1;
1491 if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){
1492 filerunlock(f);
1493 return -1;
1496 nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
1498 didread = 0;
1499 while(vde->i >= vde->n){
1500 if(vde->boff >= nb){
1501 ret = 0;
1502 goto Return;
1504 didread = 1;
1505 if(vdefill(vde) < 0){
1506 ret = -1;
1507 goto Return;
1511 memmove(de, vde->buf + vde->i, sizeof(VacDir));
1512 vde->i++;
1513 ret = 1;
1515 Return:
1516 vtfileunlock(f->source);
1517 vtfileunlock(f->msource);
1518 filerunlock(f);
1520 if(didread)
1521 fileraccess(f);
1522 return ret;
1525 void
1526 vdeclose(VacDirEnum *vde)
1528 int i;
1529 if(vde == nil)
1530 return;
1531 for(i=vde->i; i<vde->n; i++)
1532 vdcleanup(vde->buf+i);
1533 vtfree(vde->buf);
1534 vacfiledecref(vde->file);
1535 vtfree(vde);
1539 * caller must lock f->source and f->msource
1540 * caller must NOT lock the source and msource
1541 * referenced by dir.
1543 static u32int
1544 filemetaalloc(VacFile *f, VacDir *dir, u32int start)
1546 u32int nb, bo;
1547 VtBlock *b;
1548 MetaBlock mb;
1549 int nn;
1550 uchar *p;
1551 int i, n;
1552 MetaEntry me;
1553 VtFile *s, *ms;
1555 s = f->source;
1556 ms = f->msource;
1558 n = vdsize(dir);
1559 nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize;
1560 b = nil;
1561 if(start > nb)
1562 start = nb;
1563 for(bo=start; bo<nb; bo++){
1564 b = vtfileblock(ms, bo, VtORDWR);
1565 if(b == nil)
1566 goto Err;
1567 if(mbunpack(&mb, b->data, ms->dsize) < 0)
1568 goto Err;
1569 nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
1570 if(n <= nn && mb.nindex < mb.maxindex)
1571 break;
1572 vtblockput(b);
1573 b = nil;
1576 /* add block to meta file */
1577 if(b == nil){
1578 b = vtfileblock(ms, bo, VtORDWR);
1579 if(b == nil)
1580 goto Err;
1581 vtfilesetsize(ms, (nb+1)*ms->dsize);
1582 mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
1585 p = mballoc(&mb, n);
1586 if(p == nil){
1587 /* mbAlloc might have changed block */
1588 mbpack(&mb);
1589 /* vtblockdirty(b); */
1590 werrstr(EBadMeta);
1591 goto Err;
1594 mbsearch(&mb, dir->elem, &i, &me);
1595 assert(me.p == nil);
1596 me.p = p;
1597 me.size = n;
1598 vdpack(dir, &me);
1599 mbinsert(&mb, i, &me);
1600 mbpack(&mb);
1602 #ifdef notdef /* XXX */
1603 /* meta block depends on super block for qid ... */
1604 bb = cacheLocal(b->c, PartSuper, 0, VtOREAD);
1605 blockDependency(b, bb, -1, nil, nil);
1606 vtblockput(bb);
1608 /* ... and one or two dir entries */
1609 epb = s->dsize/VtEntrySize;
1610 bb = vtfileblock(s, dir->entry/epb, VtOREAD);
1611 blockDependency(b, bb, -1, nil, nil);
1612 vtblockput(bb);
1613 if(dir->mode & ModeDir){
1614 bb = sourceBlock(s, dir->mentry/epb, VtOREAD);
1615 blockDependency(b, bb, -1, nil, nil);
1616 vtblockput(bb);
1618 #endif
1620 /* vtblockdirty(b); */
1621 vtblockput(b);
1622 return bo;
1623 Err:
1624 vtblockput(b);
1625 return NilBlock;
1628 static int
1629 chksource(VacFile *f)
1631 if(f->partial)
1632 return 0;
1634 if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
1635 werrstr(ERemoved);
1636 return -1;
1638 return 0;
1641 static int
1642 filerlock(VacFile *f)
1644 /* assert(!canwlock(&f->fs->elk)); */
1645 rlock(&f->lk);
1646 if(chksource(f) < 0){
1647 runlock(&f->lk);
1648 return -1;
1650 return 0;
1653 static void
1654 filerunlock(VacFile *f)
1656 runlock(&f->lk);
1659 static int
1660 filelock(VacFile *f)
1662 /* assert(!canwlock(&f->fs->elk)); */
1663 wlock(&f->lk);
1664 if(chksource(f) < 0){
1665 wunlock(&f->lk);
1666 return -1;
1668 return 0;
1671 static void
1672 fileunlock(VacFile *f)
1674 wunlock(&f->lk);
1678 * f->source and f->msource must NOT be locked.
1679 * fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2).
1680 * We have to respect that ordering.
1682 static void
1683 filemetalock(VacFile *f)
1685 assert(f->up != nil);
1686 /* assert(!canwlock(&f->fs->elk)); */
1687 wlock(&f->up->lk);
1690 static void
1691 filemetaunlock(VacFile *f)
1693 wunlock(&f->up->lk);
1697 * f->source and f->msource must NOT be locked.
1698 * see filemetalock.
1700 static void
1701 fileraccess(VacFile* f)
1703 if(f->mode == VtOREAD)
1704 return;
1706 filemetalock(f);
1707 f->dir.atime = time(0L);
1708 f->dirty = 1;
1709 filemetaunlock(f);
1713 * f->source and f->msource must NOT be locked.
1714 * see filemetalock.
1716 static void
1717 filewaccess(VacFile* f, char *mid)
1719 if(f->mode == VtOREAD)
1720 return;
1722 filemetalock(f);
1723 f->dir.atime = f->dir.mtime = time(0L);
1724 if(strcmp(f->dir.mid, mid) != 0){
1725 vtfree(f->dir.mid);
1726 f->dir.mid = vtstrdup(mid);
1728 f->dir.mcount++;
1729 f->dirty = 1;
1730 filemetaunlock(f);
1732 /*RSC: let's try this */
1733 /*presotto - lets not
1734 if(f->up)
1735 filewaccess(f->up, mid);
1739 #if 0
1740 static void
1741 markCopied(Block *b)
1743 VtBlock *lb;
1744 Label l;
1746 if(globalToLocal(b->score) == NilBlock)
1747 return;
1749 if(!(b->l.state & BsCopied)){
1751 * We need to record that there are now pointers in
1752 * b that are not unique to b. We do this by marking
1753 * b as copied. Since we don't return the label block,
1754 * the caller can't get the dependencies right. So we have
1755 * to flush the block ourselves. This is a rare occurrence.
1757 l = b->l;
1758 l.state |= BsCopied;
1759 lb = _blockSetLabel(b, &l);
1760 WriteAgain:
1761 while(!blockWrite(lb)){
1762 fprint(2, "getEntry: could not write label block\n");
1763 sleep(10*1000);
1765 while(lb->iostate != BioClean && lb->iostate != BioDirty){
1766 assert(lb->iostate == BioWriting);
1767 vtSleep(lb->ioready);
1769 if(lb->iostate == BioDirty)
1770 goto WriteAgain;
1771 vtblockput(lb);
1775 static int
1776 getEntry(VtFile *r, Entry *e, int mark)
1778 Block *b;
1780 if(r == nil){
1781 memset(&e, 0, sizeof e);
1782 return 1;
1785 b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, VtOREAD);
1786 if(b == nil)
1787 return 0;
1788 if(!entryUnpack(e, b->data, r->offset % r->epb)){
1789 vtblockput(b);
1790 return 0;
1793 if(mark)
1794 markCopied(b);
1795 vtblockput(b);
1796 return 1;
1799 static int
1800 setEntry(Source *r, Entry *e)
1802 Block *b;
1803 Entry oe;
1805 b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, VtORDWR);
1806 if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score);
1807 if(b == nil)
1808 return 0;
1809 if(!entryUnpack(&oe, b->data, r->offset % r->epb)){
1810 vtblockput(b);
1811 return 0;
1813 e->gen = oe.gen;
1814 entryPack(e, b->data, r->offset % r->epb);
1816 /* BUG b should depend on the entry pointer */
1818 markCopied(b);
1819 /* vtblockdirty(b); */
1820 vtblockput(b);
1821 return 1;
1824 /* assumes hold elk */
1825 int
1826 fileSnapshot(VacFile *dst, VacFile *src, u32int epoch, int doarchive)
1828 Entry e, ee;
1830 /* add link to snapshot */
1831 if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1))
1832 return 0;
1834 e.snap = epoch;
1835 e.archive = doarchive;
1836 ee.snap = epoch;
1837 ee.archive = doarchive;
1839 if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee))
1840 return 0;
1841 return 1;
1844 int
1845 fileGetSources(VacFile *f, Entry *e, Entry *ee, int mark)
1847 if(!getEntry(f->source, e, mark)
1848 || !getEntry(f->msource, ee, mark))
1849 return 0;
1850 return 1;
1853 int
1854 fileWalkSources(VacFile *f)
1856 if(f->mode == VtOREAD)
1857 return 1;
1858 if(!sourceLock2(f->source, f->msource, VtORDWR))
1859 return 0;
1860 vtfileunlock(f->source);
1861 vtfileunlock(f->msource);
1862 return 1;
1865 #endif