Blob


1 #include "stdinc.h"
2 #include "9.h" /* for consPrint */
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 File
9 * and then acquire the lock of its parent
10 */
12 struct File {
13 Fs *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 meta data */
23 DirEntry dir; /* meta data for this file, including component name */
25 File *up; /* parent file (directory) */
26 File *next; /* sibling */
28 /* data for file */
29 RWLock lk; /* lock for the following */
30 Source *source;
31 Source *msource; /* for directories: meta data for children */
32 File *down; /* children */
34 int mode;
35 int issnapshot;
36 };
38 static int fileMetaFlush2(File*, char*);
39 static u32int fileMetaAlloc(File*, DirEntry*, u32int);
40 static int fileRLock(File*);
41 static void fileRUnlock(File*);
42 static int fileLock(File*);
43 static void fileUnlock(File*);
44 static void fileMetaLock(File*);
45 static void fileMetaUnlock(File*);
46 static void fileRAccess(File*);
47 static void fileWAccess(File*, char*);
49 static File *
50 fileAlloc(Fs *fs)
51 {
52 File *f;
54 f = vtmallocz(sizeof(File));
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(File *f)
64 {
65 sourceClose(f->source);
66 sourceClose(f->msource);
67 deCleanup(&f->dir);
69 memset(f, ~0, sizeof(File));
70 vtfree(f);
71 }
73 /*
74 * the file is locked already
75 * f->msource is unlocked
76 */
77 static File *
78 dirLookup(File *f, char *elem)
79 {
80 int i;
81 MetaBlock mb;
82 MetaEntry me;
83 Block *b;
84 Source *meta;
85 File *ff;
86 u32int bo, nb;
88 meta = f->msource;
89 b = nil;
90 if(!sourceLock(meta, -1))
91 return nil;
92 nb = (sourceGetSize(meta)+meta->dsize-1)/meta->dsize;
93 for(bo=0; bo<nb; bo++){
94 b = sourceBlock(meta, bo, OReadOnly);
95 if(b == nil)
96 goto Err;
97 if(!mbUnpack(&mb, b->data, meta->dsize))
98 goto Err;
99 if(mbSearch(&mb, elem, &i, &me)){
100 ff = fileAlloc(f->fs);
101 if(!deUnpack(&ff->dir, &me)){
102 fileFree(ff);
103 goto Err;
105 sourceUnlock(meta);
106 blockPut(b);
107 ff->boff = bo;
108 ff->mode = f->mode;
109 ff->issnapshot = f->issnapshot;
110 return ff;
113 blockPut(b);
114 b = nil;
116 werrstr(ENoFile);
117 /* fall through */
118 Err:
119 sourceUnlock(meta);
120 blockPut(b);
121 return nil;
124 File *
125 fileRoot(Source *r)
127 Block *b;
128 Source *r0, *r1, *r2;
129 MetaBlock mb;
130 MetaEntry me;
131 File *root, *mr;
132 Fs *fs;
134 b = nil;
135 root = nil;
136 mr = nil;
137 r1 = nil;
138 r2 = nil;
140 fs = r->fs;
141 if(!sourceLock(r, -1))
142 return nil;
143 r0 = sourceOpen(r, 0, fs->mode, 0);
144 if(r0 == nil)
145 goto Err;
146 r1 = sourceOpen(r, 1, fs->mode, 0);
147 if(r1 == nil)
148 goto Err;
149 r2 = sourceOpen(r, 2, fs->mode, 0);
150 if(r2 == nil)
151 goto Err;
153 mr = fileAlloc(fs);
154 mr->msource = r2;
155 r2 = nil;
157 root = fileAlloc(fs);
158 root->boff = 0;
159 root->up = mr;
160 root->source = r0;
161 r0->file = root; /* point back to source */
162 r0 = nil;
163 root->msource = r1;
164 r1 = nil;
166 mr->down = root;
168 if(!sourceLock(mr->msource, -1))
169 goto Err;
170 b = sourceBlock(mr->msource, 0, OReadOnly);
171 sourceUnlock(mr->msource);
172 if(b == nil)
173 goto Err;
175 if(!mbUnpack(&mb, b->data, mr->msource->dsize))
176 goto Err;
178 meUnpack(&me, &mb, 0);
179 if(!deUnpack(&root->dir, &me))
180 goto Err;
181 blockPut(b);
182 sourceUnlock(r);
183 fileRAccess(root);
185 return root;
186 Err:
187 blockPut(b);
188 if(r0)
189 sourceClose(r0);
190 if(r1)
191 sourceClose(r1);
192 if(r2)
193 sourceClose(r2);
194 if(mr)
195 fileFree(mr);
196 if(root)
197 fileFree(root);
198 sourceUnlock(r);
200 return nil;
203 static Source *
204 fileOpenSource(File *f, u32int offset, u32int gen, int dir, uint mode,
205 int issnapshot)
207 char *rname, *fname;
208 Source *r;
210 if(!sourceLock(f->source, mode))
211 return nil;
212 r = sourceOpen(f->source, offset, mode, issnapshot);
213 sourceUnlock(f->source);
214 if(r == nil)
215 return nil;
216 if(r->gen != gen){
217 werrstr(ERemoved);
218 goto Err;
220 if(r->dir != dir && r->mode != -1){
221 /* this hasn't been as useful as we hoped it would be. */
222 rname = sourceName(r);
223 fname = fileName(f);
224 consPrint("%s: source %s for file %s: fileOpenSource: "
225 "dir mismatch %d %d\n",
226 f->source->fs->name, rname, fname, r->dir, dir);
227 free(rname);
228 free(fname);
230 werrstr(EBadMeta);
231 goto Err;
233 return r;
234 Err:
235 sourceClose(r);
236 return nil;
239 File *
240 _fileWalk(File *f, char *elem, int partial)
242 File *ff;
244 fileRAccess(f);
246 if(elem[0] == 0){
247 werrstr(EBadPath);
248 return nil;
251 if(!fileIsDir(f)){
252 werrstr(ENotDir);
253 return nil;
256 if(strcmp(elem, ".") == 0){
257 return fileIncRef(f);
260 if(strcmp(elem, "..") == 0){
261 if(fileIsRoot(f))
262 return fileIncRef(f);
263 return fileIncRef(f->up);
266 if(!fileLock(f))
267 return nil;
269 for(ff = f->down; ff; ff=ff->next){
270 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
271 ff->ref++;
272 goto Exit;
276 ff = dirLookup(f, elem);
277 if(ff == nil)
278 goto Err;
280 if(ff->dir.mode & ModeSnapshot){
281 ff->mode = OReadOnly;
282 ff->issnapshot = 1;
285 if(partial){
286 /*
287 * Do nothing. We're opening this file only so we can clri it.
288 * Usually the sources can't be opened, hence we won't even bother.
289 * Be VERY careful with the returned file. If you hand it to a routine
290 * expecting ff->source and/or ff->msource to be non-nil, we're
291 * likely to dereference nil. FileClri should be the only routine
292 * setting partial.
293 */
294 ff->partial = 1;
295 }else if(ff->dir.mode & ModeDir){
296 ff->source = fileOpenSource(f, ff->dir.entry, ff->dir.gen,
297 1, ff->mode, ff->issnapshot);
298 ff->msource = fileOpenSource(f, ff->dir.mentry, ff->dir.mgen,
299 0, ff->mode, ff->issnapshot);
300 if(ff->source == nil || ff->msource == nil)
301 goto Err;
302 }else{
303 ff->source = fileOpenSource(f, ff->dir.entry, ff->dir.gen,
304 0, ff->mode, ff->issnapshot);
305 if(ff->source == nil)
306 goto Err;
309 /* link in and up parent ref count */
310 if (ff->source)
311 ff->source->file = ff; /* point back */
312 ff->next = f->down;
313 f->down = ff;
314 ff->up = f;
315 fileIncRef(f);
316 Exit:
317 fileUnlock(f);
318 return ff;
319 Err:
320 fileUnlock(f);
321 if(ff != nil)
322 fileDecRef(ff);
323 return nil;
326 File *
327 fileWalk(File *f, char *elem)
329 return _fileWalk(f, elem, 0);
332 File *
333 _fileOpen(Fs *fs, char *path, int partial)
335 File *f, *ff;
336 char *p, elem[VtMaxStringSize], *opath;
337 int n;
339 f = fs->file;
340 fileIncRef(f);
341 opath = path;
342 while(*path != 0){
343 for(p = path; *p && *p != '/'; p++)
345 n = p - path;
346 if(n > 0){
347 if(n > VtMaxStringSize){
348 werrstr("%s: element too long", EBadPath);
349 goto Err;
351 memmove(elem, path, n);
352 elem[n] = 0;
353 ff = _fileWalk(f, elem, partial && *p=='\0');
354 if(ff == nil){
355 werrstr("%.*s: %r", utfnlen(opath, p-opath),
356 opath);
357 goto Err;
359 fileDecRef(f);
360 f = ff;
362 if(*p == '/')
363 p++;
364 path = p;
366 return f;
367 Err:
368 fileDecRef(f);
369 return nil;
372 File*
373 fileOpen(Fs *fs, char *path)
375 return _fileOpen(fs, path, 0);
378 static void
379 fileSetTmp(File *f, int istmp)
381 int i;
382 Entry e;
383 Source *r;
385 for(i=0; i<2; i++){
386 if(i==0)
387 r = f->source;
388 else
389 r = f->msource;
390 if(r == nil)
391 continue;
392 if(!sourceGetEntry(r, &e)){
393 fprint(2, "sourceGetEntry failed (cannot happen): %r\n");
394 continue;
396 if(istmp)
397 e.flags |= VtEntryNoArchive;
398 else
399 e.flags &= ~VtEntryNoArchive;
400 if(!sourceSetEntry(r, &e)){
401 fprint(2, "sourceSetEntry failed (cannot happen): %r\n");
402 continue;
407 File *
408 fileCreate(File *f, char *elem, ulong mode, char *uid)
410 File *ff;
411 DirEntry *dir;
412 Source *pr, *r, *mr;
413 int isdir;
415 if(!fileLock(f))
416 return nil;
418 r = nil;
419 mr = nil;
420 for(ff = f->down; ff; ff=ff->next){
421 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
422 ff = nil;
423 werrstr(EExists);
424 goto Err1;
428 ff = dirLookup(f, elem);
429 if(ff != nil){
430 werrstr(EExists);
431 goto Err1;
434 pr = f->source;
435 if(pr->mode != OReadWrite){
436 werrstr(EReadOnly);
437 goto Err1;
440 if(!sourceLock2(f->source, f->msource, -1))
441 goto Err1;
443 ff = fileAlloc(f->fs);
444 isdir = mode & ModeDir;
446 r = sourceCreate(pr, pr->dsize, isdir, 0);
447 if(r == nil)
448 goto Err;
449 if(isdir){
450 mr = sourceCreate(pr, pr->dsize, 0, r->offset);
451 if(mr == nil)
452 goto Err;
455 dir = &ff->dir;
456 dir->elem = vtstrdup(elem);
457 dir->entry = r->offset;
458 dir->gen = r->gen;
459 if(isdir){
460 dir->mentry = mr->offset;
461 dir->mgen = mr->gen;
463 dir->size = 0;
464 if(!fsNextQid(f->fs, &dir->qid))
465 goto Err;
466 dir->uid = vtstrdup(uid);
467 dir->gid = vtstrdup(f->dir.gid);
468 dir->mid = vtstrdup(uid);
469 dir->mtime = time(0L);
470 dir->mcount = 0;
471 dir->ctime = dir->mtime;
472 dir->atime = dir->mtime;
473 dir->mode = mode;
475 ff->boff = fileMetaAlloc(f, dir, 0);
476 if(ff->boff == NilBlock)
477 goto Err;
479 sourceUnlock(f->source);
480 sourceUnlock(f->msource);
482 ff->source = r;
483 r->file = ff; /* point back */
484 ff->msource = mr;
486 if(mode&ModeTemporary){
487 if(!sourceLock2(r, mr, -1))
488 goto Err1;
489 fileSetTmp(ff, 1);
490 sourceUnlock(r);
491 if(mr)
492 sourceUnlock(mr);
495 /* committed */
497 /* link in and up parent ref count */
498 ff->next = f->down;
499 f->down = ff;
500 ff->up = f;
501 fileIncRef(f);
503 fileWAccess(f, uid);
505 fileUnlock(f);
506 return ff;
508 Err:
509 sourceUnlock(f->source);
510 sourceUnlock(f->msource);
511 Err1:
512 if(r){
513 sourceLock(r, -1);
514 sourceRemove(r);
516 if(mr){
517 sourceLock(mr, -1);
518 sourceRemove(mr);
520 if(ff)
521 fileDecRef(ff);
522 fileUnlock(f);
523 return 0;
526 int
527 fileRead(File *f, void *buf, int cnt, vlong offset)
529 Source *s;
530 uvlong size;
531 u32int bn;
532 int off, dsize, n, nn;
533 Block *b;
534 uchar *p;
536 if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset);
538 if(!fileRLock(f))
539 return -1;
541 if(offset < 0){
542 werrstr(EBadOffset);
543 goto Err1;
546 fileRAccess(f);
548 if(!sourceLock(f->source, OReadOnly))
549 goto Err1;
551 s = f->source;
552 dsize = s->dsize;
553 size = sourceGetSize(s);
555 if(offset >= size)
556 offset = size;
558 if(cnt > size-offset)
559 cnt = size-offset;
560 bn = offset/dsize;
561 off = offset%dsize;
562 p = buf;
563 while(cnt > 0){
564 b = sourceBlock(s, bn, OReadOnly);
565 if(b == nil)
566 goto Err;
567 n = cnt;
568 if(n > dsize-off)
569 n = dsize-off;
570 nn = dsize-off;
571 if(nn > n)
572 nn = n;
573 memmove(p, b->data+off, nn);
574 memset(p+nn, 0, nn-n);
575 off = 0;
576 bn++;
577 cnt -= n;
578 p += n;
579 blockPut(b);
581 sourceUnlock(s);
582 fileRUnlock(f);
583 return p-(uchar*)buf;
585 Err:
586 sourceUnlock(s);
587 Err1:
588 fileRUnlock(f);
589 return -1;
592 /*
593 * Changes the file block bn to be the given block score.
594 * Very sneaky. Only used by flfmt.
595 */
596 int
597 fileMapBlock(File *f, ulong bn, uchar score[VtScoreSize], ulong tag)
599 Block *b;
600 Entry e;
601 Source *s;
603 if(!fileLock(f))
604 return 0;
606 s = nil;
607 if(f->dir.mode & ModeDir){
608 werrstr(ENotFile);
609 goto Err;
612 if(f->source->mode != OReadWrite){
613 werrstr(EReadOnly);
614 goto Err;
617 if(!sourceLock(f->source, -1))
618 goto Err;
620 s = f->source;
621 b = _sourceBlock(s, bn, OReadWrite, 1, tag);
622 if(b == nil)
623 goto Err;
625 if(!sourceGetEntry(s, &e))
626 goto Err;
627 if(b->l.type == BtDir){
628 memmove(e.score, score, VtScoreSize);
629 assert(e.tag == tag || e.tag == 0);
630 e.tag = tag;
631 e.flags |= VtEntryLocal;
632 entryPack(&e, b->data, f->source->offset % f->source->epb);
633 }else
634 memmove(b->data + (bn%(e.psize/VtScoreSize))*VtScoreSize, score, VtScoreSize);
635 blockDirty(b);
636 blockPut(b);
637 sourceUnlock(s);
638 fileUnlock(f);
639 return 1;
641 Err:
642 if(s)
643 sourceUnlock(s);
644 fileUnlock(f);
645 return 0;
648 int
649 fileSetSize(File *f, uvlong size)
651 int r;
653 if(!fileLock(f))
654 return 0;
655 r = 0;
656 if(f->dir.mode & ModeDir){
657 werrstr(ENotFile);
658 goto Err;
660 if(f->source->mode != OReadWrite){
661 werrstr(EReadOnly);
662 goto Err;
664 if(!sourceLock(f->source, -1))
665 goto Err;
666 r = sourceSetSize(f->source, size);
667 sourceUnlock(f->source);
668 Err:
669 fileUnlock(f);
670 return r;
673 int
674 fileWrite(File *f, void *buf, int cnt, vlong offset, char *uid)
676 Source *s;
677 ulong bn;
678 int off, dsize, n;
679 Block *b;
680 uchar *p;
681 vlong eof;
683 if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset);
685 if(!fileLock(f))
686 return -1;
688 s = nil;
689 if(f->dir.mode & ModeDir){
690 werrstr(ENotFile);
691 goto Err;
694 if(f->source->mode != OReadWrite){
695 werrstr(EReadOnly);
696 goto Err;
698 if(offset < 0){
699 werrstr(EBadOffset);
700 goto Err;
703 fileWAccess(f, uid);
705 if(!sourceLock(f->source, -1))
706 goto Err;
707 s = f->source;
708 dsize = s->dsize;
710 eof = sourceGetSize(s);
711 if(f->dir.mode & ModeAppend)
712 offset = eof;
713 bn = offset/dsize;
714 off = offset%dsize;
715 p = buf;
716 while(cnt > 0){
717 n = cnt;
718 if(n > dsize-off)
719 n = dsize-off;
720 b = sourceBlock(s, bn, n<dsize?OReadWrite:OOverWrite);
721 if(b == nil){
722 if(offset > eof)
723 sourceSetSize(s, offset);
724 goto Err;
726 memmove(b->data+off, p, n);
727 off = 0;
728 cnt -= n;
729 p += n;
730 offset += n;
731 bn++;
732 blockDirty(b);
733 blockPut(b);
735 if(offset > eof && !sourceSetSize(s, offset))
736 goto Err;
737 sourceUnlock(s);
738 fileUnlock(f);
739 return p-(uchar*)buf;
740 Err:
741 if(s)
742 sourceUnlock(s);
743 fileUnlock(f);
744 return -1;
747 int
748 fileGetDir(File *f, DirEntry *dir)
750 if(!fileRLock(f))
751 return 0;
753 fileMetaLock(f);
754 deCopy(dir, &f->dir);
755 fileMetaUnlock(f);
757 if(!fileIsDir(f)){
758 if(!sourceLock(f->source, OReadOnly)){
759 fileRUnlock(f);
760 return 0;
762 dir->size = sourceGetSize(f->source);
763 sourceUnlock(f->source);
765 fileRUnlock(f);
767 return 1;
770 int
771 fileTruncate(File *f, char *uid)
773 if(fileIsDir(f)){
774 werrstr(ENotFile);
775 return 0;
778 if(!fileLock(f))
779 return 0;
781 if(f->source->mode != OReadWrite){
782 werrstr(EReadOnly);
783 fileUnlock(f);
784 return 0;
786 if(!sourceLock(f->source, -1)){
787 fileUnlock(f);
788 return 0;
790 if(!sourceTruncate(f->source)){
791 sourceUnlock(f->source);
792 fileUnlock(f);
793 return 0;
795 sourceUnlock(f->source);
796 fileUnlock(f);
798 fileWAccess(f, uid);
800 return 1;
803 int
804 fileSetDir(File *f, DirEntry *dir, char *uid)
806 File *ff;
807 char *oelem;
808 u32int mask;
809 u64int size;
811 /* can not set permissions for the root */
812 if(fileIsRoot(f)){
813 werrstr(ERoot);
814 return 0;
817 if(!fileLock(f))
818 return 0;
820 if(f->source->mode != OReadWrite){
821 werrstr(EReadOnly);
822 fileUnlock(f);
823 return 0;
826 fileMetaLock(f);
828 /* check new name does not already exist */
829 if(strcmp(f->dir.elem, dir->elem) != 0){
830 for(ff = f->up->down; ff; ff=ff->next){
831 if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
832 werrstr(EExists);
833 goto Err;
837 ff = dirLookup(f->up, dir->elem);
838 if(ff != nil){
839 fileDecRef(ff);
840 werrstr(EExists);
841 goto Err;
845 if(!sourceLock2(f->source, f->msource, -1))
846 goto Err;
847 if(!fileIsDir(f)){
848 size = sourceGetSize(f->source);
849 if(size != dir->size){
850 if(!sourceSetSize(f->source, dir->size)){
851 sourceUnlock(f->source);
852 if(f->msource)
853 sourceUnlock(f->msource);
854 goto Err;
856 /* commited to changing it now */
859 /* commited to changing it now */
860 if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary))
861 fileSetTmp(f, dir->mode&ModeTemporary);
862 sourceUnlock(f->source);
863 if(f->msource)
864 sourceUnlock(f->msource);
866 oelem = nil;
867 if(strcmp(f->dir.elem, dir->elem) != 0){
868 oelem = f->dir.elem;
869 f->dir.elem = vtstrdup(dir->elem);
872 if(strcmp(f->dir.uid, dir->uid) != 0){
873 vtfree(f->dir.uid);
874 f->dir.uid = vtstrdup(dir->uid);
877 if(strcmp(f->dir.gid, dir->gid) != 0){
878 vtfree(f->dir.gid);
879 f->dir.gid = vtstrdup(dir->gid);
882 f->dir.mtime = dir->mtime;
883 f->dir.atime = dir->atime;
885 //fprint(2, "mode %x %x ", f->dir.mode, dir->mode);
886 mask = ~(ModeDir|ModeSnapshot);
887 f->dir.mode &= ~mask;
888 f->dir.mode |= mask & dir->mode;
889 f->dirty = 1;
890 //fprint(2, "->%x\n", f->dir.mode);
892 fileMetaFlush2(f, oelem);
893 vtfree(oelem);
895 fileMetaUnlock(f);
896 fileUnlock(f);
898 fileWAccess(f->up, uid);
900 return 1;
901 Err:
902 fileMetaUnlock(f);
903 fileUnlock(f);
904 return 0;
907 int
908 fileSetQidSpace(File *f, u64int offset, u64int max)
910 int ret;
912 if(!fileLock(f))
913 return 0;
914 fileMetaLock(f);
915 f->dir.qidSpace = 1;
916 f->dir.qidOffset = offset;
917 f->dir.qidMax = max;
918 ret = fileMetaFlush2(f, nil)>=0;
919 fileMetaUnlock(f);
920 fileUnlock(f);
921 return ret;
925 uvlong
926 fileGetId(File *f)
928 /* immutable */
929 return f->dir.qid;
932 ulong
933 fileGetMcount(File *f)
935 ulong mcount;
937 fileMetaLock(f);
938 mcount = f->dir.mcount;
939 fileMetaUnlock(f);
940 return mcount;
943 ulong
944 fileGetMode(File *f)
946 ulong mode;
948 fileMetaLock(f);
949 mode = f->dir.mode;
950 fileMetaUnlock(f);
951 return mode;
954 int
955 fileIsDir(File *f)
957 /* immutable */
958 return (f->dir.mode & ModeDir) != 0;
961 int
962 fileIsAppend(File *f)
964 return (f->dir.mode & ModeAppend) != 0;
967 int
968 fileIsExclusive(File *f)
970 return (f->dir.mode & ModeExclusive) != 0;
973 int
974 fileIsTemporary(File *f)
976 return (f->dir.mode & ModeTemporary) != 0;
979 int
980 fileIsRoot(File *f)
982 return f == f->fs->file;
985 int
986 fileIsRoFs(File *f)
988 return f->fs->mode == OReadOnly;
991 int
992 fileGetSize(File *f, uvlong *size)
994 if(!fileRLock(f))
995 return 0;
996 if(!sourceLock(f->source, OReadOnly)){
997 fileRUnlock(f);
998 return 0;
1000 *size = sourceGetSize(f->source);
1001 sourceUnlock(f->source);
1002 fileRUnlock(f);
1004 return 1;
1007 int
1008 fileMetaFlush(File *f, int rec)
1010 File **kids, *p;
1011 int nkids;
1012 int i, rv;
1014 fileMetaLock(f);
1015 rv = fileMetaFlush2(f, nil);
1016 fileMetaUnlock(f);
1018 if(!rec || !fileIsDir(f))
1019 return rv;
1021 if(!fileLock(f))
1022 return rv;
1023 nkids = 0;
1024 for(p=f->down; p; p=p->next)
1025 nkids++;
1026 kids = vtmalloc(nkids*sizeof(File*));
1027 i = 0;
1028 for(p=f->down; p; p=p->next){
1029 kids[i++] = p;
1030 p->ref++;
1032 fileUnlock(f);
1034 for(i=0; i<nkids; i++){
1035 rv |= fileMetaFlush(kids[i], 1);
1036 fileDecRef(kids[i]);
1038 vtfree(kids);
1039 return rv;
1042 /* assumes metaLock is held */
1043 static int
1044 fileMetaFlush2(File *f, char *oelem)
1046 File *fp;
1047 Block *b, *bb;
1048 MetaBlock mb;
1049 MetaEntry me, me2;
1050 int i, n;
1051 u32int boff;
1053 if(!f->dirty)
1054 return 0;
1056 if(oelem == nil)
1057 oelem = f->dir.elem;
1059 //print("fileMetaFlush %s->%s\n", oelem, f->dir.elem);
1061 fp = f->up;
1063 if(!sourceLock(fp->msource, -1))
1064 return -1;
1065 /* can happen if source is clri'ed out from under us */
1066 if(f->boff == NilBlock)
1067 goto Err1;
1068 b = sourceBlock(fp->msource, f->boff, OReadWrite);
1069 if(b == nil)
1070 goto Err1;
1072 if(!mbUnpack(&mb, b->data, fp->msource->dsize))
1073 goto Err;
1074 if(!mbSearch(&mb, oelem, &i, &me))
1075 goto Err;
1077 n = deSize(&f->dir);
1078 if(0)fprint(2, "old size %d new size %d\n", me.size, n);
1080 if(mbResize(&mb, &me, n)){
1081 /* fits in the block */
1082 mbDelete(&mb, i);
1083 if(strcmp(f->dir.elem, oelem) != 0)
1084 mbSearch(&mb, f->dir.elem, &i, &me2);
1085 dePack(&f->dir, &me);
1086 mbInsert(&mb, i, &me);
1087 mbPack(&mb);
1088 blockDirty(b);
1089 blockPut(b);
1090 sourceUnlock(fp->msource);
1091 f->dirty = 0;
1093 return 1;
1097 * moving entry to another block
1098 * it is feasible for the fs to crash leaving two copies
1099 * of the directory entry. This is just too much work to
1100 * fix. Given that entries are only allocated in a block that
1101 * is less than PercentageFull, most modifications of meta data
1102 * will fit within the block. i.e. this code should almost
1103 * never be executed.
1105 boff = fileMetaAlloc(fp, &f->dir, f->boff+1);
1106 if(boff == NilBlock){
1107 /* mbResize might have modified block */
1108 mbPack(&mb);
1109 blockDirty(b);
1110 goto Err;
1112 fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff);
1113 f->boff = boff;
1115 /* make sure deletion goes to disk after new entry */
1116 bb = sourceBlock(fp->msource, f->boff, OReadWrite);
1117 mbDelete(&mb, i);
1118 mbPack(&mb);
1119 blockDependency(b, bb, -1, nil, nil);
1120 blockPut(bb);
1121 blockDirty(b);
1122 blockPut(b);
1123 sourceUnlock(fp->msource);
1125 f->dirty = 0;
1127 return 1;
1129 Err:
1130 blockPut(b);
1131 Err1:
1132 sourceUnlock(fp->msource);
1133 return -1;
1136 static int
1137 fileMetaRemove(File *f, char *uid)
1139 Block *b;
1140 MetaBlock mb;
1141 MetaEntry me;
1142 int i;
1143 File *up;
1145 up = f->up;
1147 fileWAccess(up, uid);
1149 fileMetaLock(f);
1151 sourceLock(up->msource, OReadWrite);
1152 b = sourceBlock(up->msource, f->boff, OReadWrite);
1153 if(b == nil)
1154 goto Err;
1156 if(!mbUnpack(&mb, b->data, up->msource->dsize))
1158 fprint(2, "U\n");
1159 goto Err;
1161 if(!mbSearch(&mb, f->dir.elem, &i, &me))
1163 fprint(2, "S\n");
1164 goto Err;
1166 mbDelete(&mb, i);
1167 mbPack(&mb);
1168 sourceUnlock(up->msource);
1170 blockDirty(b);
1171 blockPut(b);
1173 f->removed = 1;
1174 f->boff = NilBlock;
1175 f->dirty = 0;
1177 fileMetaUnlock(f);
1178 return 1;
1180 Err:
1181 sourceUnlock(up->msource);
1182 blockPut(b);
1183 fileMetaUnlock(f);
1184 return 0;
1187 /* assume file is locked, assume f->msource is locked */
1188 static int
1189 fileCheckEmpty(File *f)
1191 u32int i, n;
1192 Block *b;
1193 MetaBlock mb;
1194 Source *r;
1196 r = f->msource;
1197 n = (sourceGetSize(r)+r->dsize-1)/r->dsize;
1198 for(i=0; i<n; i++){
1199 b = sourceBlock(r, i, OReadOnly);
1200 if(b == nil)
1201 goto Err;
1202 if(!mbUnpack(&mb, b->data, r->dsize))
1203 goto Err;
1204 if(mb.nindex > 0){
1205 werrstr(ENotEmpty);
1206 goto Err;
1208 blockPut(b);
1210 return 1;
1211 Err:
1212 blockPut(b);
1213 return 0;
1216 int
1217 fileRemove(File *f, char *uid)
1219 File *ff;
1221 /* can not remove the root */
1222 if(fileIsRoot(f)){
1223 werrstr(ERoot);
1224 return 0;
1227 if(!fileLock(f))
1228 return 0;
1230 if(f->source->mode != OReadWrite){
1231 werrstr(EReadOnly);
1232 goto Err1;
1234 if(!sourceLock2(f->source, f->msource, -1))
1235 goto Err1;
1236 if(fileIsDir(f) && !fileCheckEmpty(f))
1237 goto Err;
1239 for(ff=f->down; ff; ff=ff->next)
1240 assert(ff->removed);
1242 sourceRemove(f->source);
1243 f->source->file = nil; /* erase back pointer */
1244 f->source = nil;
1245 if(f->msource){
1246 sourceRemove(f->msource);
1247 f->msource = nil;
1250 fileUnlock(f);
1252 if(!fileMetaRemove(f, uid))
1253 return 0;
1255 return 1;
1257 Err:
1258 sourceUnlock(f->source);
1259 if(f->msource)
1260 sourceUnlock(f->msource);
1261 Err1:
1262 fileUnlock(f);
1263 return 0;
1266 static int
1267 clri(File *f, char *uid)
1269 int r;
1271 if(f == nil)
1272 return 0;
1273 if(f->up->source->mode != OReadWrite){
1274 werrstr(EReadOnly);
1275 fileDecRef(f);
1276 return 0;
1278 r = fileMetaRemove(f, uid);
1279 fileDecRef(f);
1280 return r;
1283 int
1284 fileClriPath(Fs *fs, char *path, char *uid)
1286 return clri(_fileOpen(fs, path, 1), uid);
1289 int
1290 fileClri(File *dir, char *elem, char *uid)
1292 return clri(_fileWalk(dir, elem, 1), uid);
1295 File *
1296 fileIncRef(File *vf)
1298 fileMetaLock(vf);
1299 assert(vf->ref > 0);
1300 vf->ref++;
1301 fileMetaUnlock(vf);
1302 return vf;
1305 int
1306 fileDecRef(File *f)
1308 File *p, *q, **qq;
1310 if(f->up == nil){
1311 /* never linked in */
1312 assert(f->ref == 1);
1313 fileFree(f);
1314 return 1;
1317 fileMetaLock(f);
1318 f->ref--;
1319 if(f->ref > 0){
1320 fileMetaUnlock(f);
1321 return 0;
1323 assert(f->ref == 0);
1324 assert(f->down == nil);
1326 fileMetaFlush2(f, nil);
1328 p = f->up;
1329 qq = &p->down;
1330 for(q = *qq; q; q = *qq){
1331 if(q == f)
1332 break;
1333 qq = &q->next;
1335 assert(q != nil);
1336 *qq = f->next;
1338 fileMetaUnlock(f);
1339 fileFree(f);
1341 fileDecRef(p);
1342 return 1;
1345 File *
1346 fileGetParent(File *f)
1348 if(fileIsRoot(f))
1349 return fileIncRef(f);
1350 return fileIncRef(f->up);
1353 DirEntryEnum *
1354 deeOpen(File *f)
1356 DirEntryEnum *dee;
1357 File *p;
1359 if(!fileIsDir(f)){
1360 werrstr(ENotDir);
1361 fileDecRef(f);
1362 return nil;
1365 /* flush out meta data */
1366 if(!fileLock(f))
1367 return nil;
1368 for(p=f->down; p; p=p->next)
1369 fileMetaFlush2(p, nil);
1370 fileUnlock(f);
1372 dee = vtmallocz(sizeof(DirEntryEnum));
1373 dee->file = fileIncRef(f);
1375 return dee;
1378 static int
1379 dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size)
1381 Block *b;
1382 ulong bn;
1383 Entry e;
1384 int epb;
1386 epb = s->dsize/VtEntrySize;
1387 bn = elem/epb;
1388 elem -= bn*epb;
1390 b = sourceBlock(s, bn, OReadOnly);
1391 if(b == nil)
1392 goto Err;
1393 if(!entryUnpack(&e, b->data, elem))
1394 goto Err;
1396 /* hanging entries are returned as zero size */
1397 if(!(e.flags & VtEntryActive) || e.gen != gen)
1398 *size = 0;
1399 else
1400 *size = e.size;
1401 blockPut(b);
1402 return 1;
1404 Err:
1405 blockPut(b);
1406 return 0;
1409 static int
1410 deeFill(DirEntryEnum *dee)
1412 int i, n;
1413 Source *meta, *source;
1414 MetaBlock mb;
1415 MetaEntry me;
1416 File *f;
1417 Block *b;
1418 DirEntry *de;
1420 /* clean up first */
1421 for(i=dee->i; i<dee->n; i++)
1422 deCleanup(dee->buf+i);
1423 vtfree(dee->buf);
1424 dee->buf = nil;
1425 dee->i = 0;
1426 dee->n = 0;
1428 f = dee->file;
1430 source = f->source;
1431 meta = f->msource;
1433 b = sourceBlock(meta, dee->boff, OReadOnly);
1434 if(b == nil)
1435 goto Err;
1436 if(!mbUnpack(&mb, b->data, meta->dsize))
1437 goto Err;
1439 n = mb.nindex;
1440 dee->buf = vtmalloc(n * sizeof(DirEntry));
1442 for(i=0; i<n; i++){
1443 de = dee->buf + i;
1444 meUnpack(&me, &mb, i);
1445 if(!deUnpack(de, &me))
1446 goto Err;
1447 dee->n++;
1448 if(!(de->mode & ModeDir))
1449 if(!dirEntrySize(source, de->entry, de->gen, &de->size))
1450 goto Err;
1452 dee->boff++;
1453 blockPut(b);
1454 return 1;
1455 Err:
1456 blockPut(b);
1457 return 0;
1460 int
1461 deeRead(DirEntryEnum *dee, DirEntry *de)
1463 int ret, didread;
1464 File *f;
1465 u32int nb;
1467 if(dee == nil){
1468 werrstr("cannot happen in deeRead");
1469 return -1;
1472 f = dee->file;
1473 if(!fileRLock(f))
1474 return -1;
1476 if(!sourceLock2(f->source, f->msource, OReadOnly)){
1477 fileRUnlock(f);
1478 return -1;
1481 nb = (sourceGetSize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
1483 didread = 0;
1484 while(dee->i >= dee->n){
1485 if(dee->boff >= nb){
1486 ret = 0;
1487 goto Return;
1489 didread = 1;
1490 if(!deeFill(dee)){
1491 ret = -1;
1492 goto Return;
1496 memmove(de, dee->buf + dee->i, sizeof(DirEntry));
1497 dee->i++;
1498 ret = 1;
1500 Return:
1501 sourceUnlock(f->source);
1502 sourceUnlock(f->msource);
1503 fileRUnlock(f);
1505 if(didread)
1506 fileRAccess(f);
1507 return ret;
1510 void
1511 deeClose(DirEntryEnum *dee)
1513 int i;
1514 if(dee == nil)
1515 return;
1516 for(i=dee->i; i<dee->n; i++)
1517 deCleanup(dee->buf+i);
1518 vtfree(dee->buf);
1519 fileDecRef(dee->file);
1520 vtfree(dee);
1524 * caller must lock f->source and f->msource
1525 * caller must NOT lock the source and msource
1526 * referenced by dir.
1528 static u32int
1529 fileMetaAlloc(File *f, DirEntry *dir, u32int start)
1531 u32int nb, bo;
1532 Block *b, *bb;
1533 MetaBlock mb;
1534 int nn;
1535 uchar *p;
1536 int i, n, epb;
1537 MetaEntry me;
1538 Source *s, *ms;
1540 s = f->source;
1541 ms = f->msource;
1543 n = deSize(dir);
1544 nb = (sourceGetSize(ms)+ms->dsize-1)/ms->dsize;
1545 b = nil;
1546 if(start > nb)
1547 start = nb;
1548 for(bo=start; bo<nb; bo++){
1549 b = sourceBlock(ms, bo, OReadWrite);
1550 if(b == nil)
1551 goto Err;
1552 if(!mbUnpack(&mb, b->data, ms->dsize))
1553 goto Err;
1554 nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
1555 if(n <= nn && mb.nindex < mb.maxindex)
1556 break;
1557 blockPut(b);
1558 b = nil;
1561 /* add block to meta file */
1562 if(b == nil){
1563 b = sourceBlock(ms, bo, OReadWrite);
1564 if(b == nil)
1565 goto Err;
1566 sourceSetSize(ms, (nb+1)*ms->dsize);
1567 mbInit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
1570 p = mbAlloc(&mb, n);
1571 if(p == nil){
1572 /* mbAlloc might have changed block */
1573 mbPack(&mb);
1574 blockDirty(b);
1575 werrstr(EBadMeta);
1576 goto Err;
1579 mbSearch(&mb, dir->elem, &i, &me);
1580 assert(me.p == nil);
1581 me.p = p;
1582 me.size = n;
1583 dePack(dir, &me);
1584 mbInsert(&mb, i, &me);
1585 mbPack(&mb);
1587 /* meta block depends on super block for qid ... */
1588 bb = cacheLocal(b->c, PartSuper, 0, OReadOnly);
1589 blockDependency(b, bb, -1, nil, nil);
1590 blockPut(bb);
1592 /* ... and one or two dir entries */
1593 epb = s->dsize/VtEntrySize;
1594 bb = sourceBlock(s, dir->entry/epb, OReadOnly);
1595 blockDependency(b, bb, -1, nil, nil);
1596 blockPut(bb);
1597 if(dir->mode & ModeDir){
1598 bb = sourceBlock(s, dir->mentry/epb, OReadOnly);
1599 blockDependency(b, bb, -1, nil, nil);
1600 blockPut(bb);
1603 blockDirty(b);
1604 blockPut(b);
1605 return bo;
1606 Err:
1607 blockPut(b);
1608 return NilBlock;
1611 static int
1612 chkSource(File *f)
1614 if(f->partial)
1615 return 1;
1617 if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
1618 werrstr(ERemoved);
1619 return 0;
1621 return 1;
1624 static int
1625 fileRLock(File *f)
1627 assert(!canwlock(&f->fs->elk));
1628 rlock(&f->lk);
1629 if(!chkSource(f)){
1630 fileRUnlock(f);
1631 return 0;
1633 return 1;
1636 static void
1637 fileRUnlock(File *f)
1639 runlock(&f->lk);
1642 static int
1643 fileLock(File *f)
1645 assert(!canwlock(&f->fs->elk));
1646 wlock(&f->lk);
1647 if(!chkSource(f)){
1648 fileUnlock(f);
1649 return 0;
1651 return 1;
1654 static void
1655 fileUnlock(File *f)
1657 wunlock(&f->lk);
1661 * f->source and f->msource must NOT be locked.
1662 * fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2).
1663 * We have to respect that ordering.
1665 static void
1666 fileMetaLock(File *f)
1668 if(f->up == nil)
1669 fprint(2, "f->elem = %s\n", f->dir.elem);
1670 assert(f->up != nil);
1671 assert(!canwlock(&f->fs->elk));
1672 wlock(&f->up->lk);
1675 static void
1676 fileMetaUnlock(File *f)
1678 wunlock(&f->up->lk);
1682 * f->source and f->msource must NOT be locked.
1683 * see fileMetaLock.
1685 static void
1686 fileRAccess(File* f)
1688 if(f->mode == OReadOnly || f->fs->noatimeupd)
1689 return;
1691 fileMetaLock(f);
1692 f->dir.atime = time(0L);
1693 f->dirty = 1;
1694 fileMetaUnlock(f);
1698 * f->source and f->msource must NOT be locked.
1699 * see fileMetaLock.
1701 static void
1702 fileWAccess(File* f, char *mid)
1704 if(f->mode == OReadOnly)
1705 return;
1707 fileMetaLock(f);
1708 f->dir.atime = f->dir.mtime = time(0L);
1709 if(strcmp(f->dir.mid, mid) != 0){
1710 vtfree(f->dir.mid);
1711 f->dir.mid = vtstrdup(mid);
1713 f->dir.mcount++;
1714 f->dirty = 1;
1715 fileMetaUnlock(f);
1717 /*RSC: let's try this */
1718 /*presotto - lets not
1719 if(f->up)
1720 fileWAccess(f->up, mid);
1724 static int
1725 getEntry(Source *r, Entry *e, int checkepoch)
1727 u32int epoch;
1728 Block *b;
1730 if(r == nil){
1731 memset(&e, 0, sizeof e);
1732 return 1;
1735 b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadOnly);
1736 if(b == nil)
1737 return 0;
1738 if(!entryUnpack(e, b->data, r->offset % r->epb)){
1739 blockPut(b);
1740 return 0;
1742 epoch = b->l.epoch;
1743 blockPut(b);
1745 if(checkepoch){
1746 b = cacheGlobal(r->fs->cache, e->score, entryType(e), e->tag, OReadOnly);
1747 if(b){
1748 if(b->l.epoch >= epoch)
1749 fprint(2, "warning: entry %p epoch not older %#.8ux/%d %V/%d in getEntry\n",
1750 r, b->addr, b->l.epoch, r->score, epoch);
1751 blockPut(b);
1755 return 1;
1758 static int
1759 setEntry(Source *r, Entry *e)
1761 Block *b;
1762 Entry oe;
1764 b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite);
1765 if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score);
1766 if(b == nil)
1767 return 0;
1768 if(!entryUnpack(&oe, b->data, r->offset % r->epb)){
1769 blockPut(b);
1770 return 0;
1772 e->gen = oe.gen;
1773 entryPack(e, b->data, r->offset % r->epb);
1775 /* BUG b should depend on the entry pointer */
1777 blockDirty(b);
1778 blockPut(b);
1779 return 1;
1782 /* assumes hold elk */
1783 int
1784 fileSnapshot(File *dst, File *src, u32int epoch, int doarchive)
1786 Entry e, ee;
1788 /* add link to snapshot */
1789 if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1))
1790 return 0;
1792 e.snap = epoch;
1793 e.archive = doarchive;
1794 ee.snap = epoch;
1795 ee.archive = doarchive;
1797 if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee))
1798 return 0;
1799 return 1;
1802 int
1803 fileGetSources(File *f, Entry *e, Entry *ee)
1805 if(!getEntry(f->source, e, 0)
1806 || !getEntry(f->msource, ee, 0))
1807 return 0;
1808 return 1;
1812 * Walk down to the block(s) containing the Entries
1813 * for f->source and f->msource, copying as we go.
1815 int
1816 fileWalkSources(File *f)
1818 if(f->mode == OReadOnly){
1819 fprint(2, "readonly in fileWalkSources\n");
1820 return 1;
1822 if(!sourceLock2(f->source, f->msource, OReadWrite)){
1823 fprint(2, "sourceLock2 failed in fileWalkSources\n");
1824 return 0;
1826 sourceUnlock(f->source);
1827 sourceUnlock(f->msource);
1828 return 1;
1832 * convert File* to full path name in malloced string.
1833 * this hasn't been as useful as we hoped it would be.
1835 char *
1836 fileName(File *f)
1838 char *name, *pname;
1839 File *p;
1840 static char root[] = "/";
1842 if (f == nil)
1843 return vtstrdup("/**GOK**");
1845 p = fileGetParent(f);
1846 if (p == f)
1847 name = vtstrdup(root);
1848 else {
1849 pname = fileName(p);
1850 if (strcmp(pname, root) == 0)
1851 name = smprint("/%s", f->dir.elem);
1852 else
1853 name = smprint("%s/%s", pname, f->dir.elem);
1854 free(pname);
1856 fileDecRef(p);
1857 return name;