2 #include "9.h" /* for consPrint */
8 * locking order is upwards. A thread can hold the lock for a File
9 * and then acquire the lock of its parent
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 */
29 RWLock lk; /* lock for the following */
31 Source *msource; /* for directories: meta data for children */
32 File *down; /* children */
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*);
54 f = vtmallocz(sizeof(File));
65 sourceClose(f->source);
66 sourceClose(f->msource);
69 memset(f, ~0, sizeof(File));
74 * the file is locked already
75 * f->msource is unlocked
78 dirLookup(File *f, char *elem)
90 if(!sourceLock(meta, -1))
92 nb = (sourceGetSize(meta)+meta->dsize-1)/meta->dsize;
93 for(bo=0; bo<nb; bo++){
94 b = sourceBlock(meta, bo, OReadOnly);
97 if(!mbUnpack(&mb, b->data, meta->dsize))
99 if(mbSearch(&mb, elem, &i, &me)){
100 ff = fileAlloc(f->fs);
101 if(!deUnpack(&ff->dir, &me)){
109 ff->issnapshot = f->issnapshot;
128 Source *r0, *r1, *r2;
141 if(!sourceLock(r, -1))
143 r0 = sourceOpen(r, 0, fs->mode, 0);
146 r1 = sourceOpen(r, 1, fs->mode, 0);
149 r2 = sourceOpen(r, 2, fs->mode, 0);
157 root = fileAlloc(fs);
161 r0->file = root; /* point back to source */
168 if(!sourceLock(mr->msource, -1))
170 b = sourceBlock(mr->msource, 0, OReadOnly);
171 sourceUnlock(mr->msource);
175 if(!mbUnpack(&mb, b->data, mr->msource->dsize))
178 meUnpack(&me, &mb, 0);
179 if(!deUnpack(&root->dir, &me))
204 fileOpenSource(File *f, u32int offset, u32int gen, int dir, uint mode,
210 if(!sourceLock(f->source, mode))
212 r = sourceOpen(f->source, offset, mode, issnapshot);
213 sourceUnlock(f->source);
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);
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);
240 _fileWalk(File *f, char *elem, int partial)
256 if(strcmp(elem, ".") == 0){
257 return fileIncRef(f);
260 if(strcmp(elem, "..") == 0){
262 return fileIncRef(f);
263 return fileIncRef(f->up);
269 for(ff = f->down; ff; ff=ff->next){
270 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
276 ff = dirLookup(f, elem);
280 if(ff->dir.mode & ModeSnapshot){
281 ff->mode = OReadOnly;
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
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)
303 ff->source = fileOpenSource(f, ff->dir.entry, ff->dir.gen,
304 0, ff->mode, ff->issnapshot);
305 if(ff->source == nil)
309 /* link in and up parent ref count */
311 ff->source->file = ff; /* point back */
327 fileWalk(File *f, char *elem)
329 return _fileWalk(f, elem, 0);
333 _fileOpen(Fs *fs, char *path, int partial)
336 char *p, elem[VtMaxStringSize], *opath;
343 for(p = path; *p && *p != '/'; p++)
347 if(n > VtMaxStringSize){
348 werrstr("%s: element too long", EBadPath);
351 memmove(elem, path, n);
353 ff = _fileWalk(f, elem, partial && *p=='\0');
355 werrstr("%.*s: %r", utfnlen(opath, p-opath),
373 fileOpen(Fs *fs, char *path)
375 return _fileOpen(fs, path, 0);
379 fileSetTmp(File *f, int istmp)
392 if(!sourceGetEntry(r, &e)){
393 fprint(2, "sourceGetEntry failed (cannot happen): %r\n");
397 e.flags |= VtEntryNoArchive;
399 e.flags &= ~VtEntryNoArchive;
400 if(!sourceSetEntry(r, &e)){
401 fprint(2, "sourceSetEntry failed (cannot happen): %r\n");
408 fileCreate(File *f, char *elem, ulong mode, char *uid)
420 for(ff = f->down; ff; ff=ff->next){
421 if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
428 ff = dirLookup(f, elem);
435 if(pr->mode != OReadWrite){
440 if(!sourceLock2(f->source, f->msource, -1))
443 ff = fileAlloc(f->fs);
444 isdir = mode & ModeDir;
446 r = sourceCreate(pr, pr->dsize, isdir, 0);
450 mr = sourceCreate(pr, pr->dsize, 0, r->offset);
456 dir->elem = vtstrdup(elem);
457 dir->entry = r->offset;
460 dir->mentry = mr->offset;
464 if(!fsNextQid(f->fs, &dir->qid))
466 dir->uid = vtstrdup(uid);
467 dir->gid = vtstrdup(f->dir.gid);
468 dir->mid = vtstrdup(uid);
469 dir->mtime = time(0L);
471 dir->ctime = dir->mtime;
472 dir->atime = dir->mtime;
475 ff->boff = fileMetaAlloc(f, dir, 0);
476 if(ff->boff == NilBlock)
479 sourceUnlock(f->source);
480 sourceUnlock(f->msource);
483 r->file = ff; /* point back */
486 if(mode&ModeTemporary){
487 if(!sourceLock2(r, mr, -1))
497 /* link in and up parent ref count */
509 sourceUnlock(f->source);
510 sourceUnlock(f->msource);
527 fileRead(File *f, void *buf, int cnt, vlong offset)
532 int off, dsize, n, nn;
536 if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset);
548 if(!sourceLock(f->source, OReadOnly))
553 size = sourceGetSize(s);
558 if(cnt > size-offset)
564 b = sourceBlock(s, bn, OReadOnly);
573 memmove(p, b->data+off, nn);
574 memset(p+nn, 0, nn-n);
583 return p-(uchar*)buf;
593 * Changes the file block bn to be the given block score.
594 * Very sneaky. Only used by flfmt.
597 fileMapBlock(File *f, ulong bn, uchar score[VtScoreSize], ulong tag)
607 if(f->dir.mode & ModeDir){
612 if(f->source->mode != OReadWrite){
617 if(!sourceLock(f->source, -1))
621 b = _sourceBlock(s, bn, OReadWrite, 1, tag);
625 if(!sourceGetEntry(s, &e))
627 if(b->l.type == BtDir){
628 memmove(e.score, score, VtScoreSize);
629 assert(e.tag == tag || e.tag == 0);
631 e.flags |= VtEntryLocal;
632 entryPack(&e, b->data, f->source->offset % f->source->epb);
634 memmove(b->data + (bn%(e.psize/VtScoreSize))*VtScoreSize, score, VtScoreSize);
649 fileSetSize(File *f, uvlong size)
656 if(f->dir.mode & ModeDir){
660 if(f->source->mode != OReadWrite){
664 if(!sourceLock(f->source, -1))
666 r = sourceSetSize(f->source, size);
667 sourceUnlock(f->source);
674 fileWrite(File *f, void *buf, int cnt, vlong offset, char *uid)
683 if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset);
689 if(f->dir.mode & ModeDir){
694 if(f->source->mode != OReadWrite){
705 if(!sourceLock(f->source, -1))
710 eof = sourceGetSize(s);
711 if(f->dir.mode & ModeAppend)
720 b = sourceBlock(s, bn, n<dsize?OReadWrite:OOverWrite);
723 sourceSetSize(s, offset);
726 memmove(b->data+off, p, n);
735 if(offset > eof && !sourceSetSize(s, offset))
739 return p-(uchar*)buf;
748 fileGetDir(File *f, DirEntry *dir)
754 deCopy(dir, &f->dir);
758 if(!sourceLock(f->source, OReadOnly)){
762 dir->size = sourceGetSize(f->source);
763 sourceUnlock(f->source);
771 fileTruncate(File *f, char *uid)
781 if(f->source->mode != OReadWrite){
786 if(!sourceLock(f->source, -1)){
790 if(!sourceTruncate(f->source)){
791 sourceUnlock(f->source);
795 sourceUnlock(f->source);
804 fileSetDir(File *f, DirEntry *dir, char *uid)
811 /* can not set permissions for the root */
820 if(f->source->mode != OReadWrite){
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){
837 ff = dirLookup(f->up, dir->elem);
845 if(!sourceLock2(f->source, f->msource, -1))
848 size = sourceGetSize(f->source);
849 if(size != dir->size){
850 if(!sourceSetSize(f->source, dir->size)){
851 sourceUnlock(f->source);
853 sourceUnlock(f->msource);
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);
864 sourceUnlock(f->msource);
867 if(strcmp(f->dir.elem, dir->elem) != 0){
869 f->dir.elem = vtstrdup(dir->elem);
872 if(strcmp(f->dir.uid, dir->uid) != 0){
874 f->dir.uid = vtstrdup(dir->uid);
877 if(strcmp(f->dir.gid, dir->gid) != 0){
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;
890 //fprint(2, "->%x\n", f->dir.mode);
892 fileMetaFlush2(f, oelem);
898 fileWAccess(f->up, uid);
908 fileSetQidSpace(File *f, u64int offset, u64int max)
916 f->dir.qidOffset = offset;
918 ret = fileMetaFlush2(f, nil)>=0;
933 fileGetMcount(File *f)
938 mcount = f->dir.mcount;
958 return (f->dir.mode & ModeDir) != 0;
962 fileIsAppend(File *f)
964 return (f->dir.mode & ModeAppend) != 0;
968 fileIsExclusive(File *f)
970 return (f->dir.mode & ModeExclusive) != 0;
974 fileIsTemporary(File *f)
976 return (f->dir.mode & ModeTemporary) != 0;
982 return f == f->fs->file;
988 return f->fs->mode == OReadOnly;
992 fileGetSize(File *f, uvlong *size)
996 if(!sourceLock(f->source, OReadOnly)){
1000 *size = sourceGetSize(f->source);
1001 sourceUnlock(f->source);
1008 fileMetaFlush(File *f, int rec)
1015 rv = fileMetaFlush2(f, nil);
1018 if(!rec || !fileIsDir(f))
1024 for(p=f->down; p; p=p->next)
1026 kids = vtmalloc(nkids*sizeof(File*));
1028 for(p=f->down; p; p=p->next){
1034 for(i=0; i<nkids; i++){
1035 rv |= fileMetaFlush(kids[i], 1);
1036 fileDecRef(kids[i]);
1042 /* assumes metaLock is held */
1044 fileMetaFlush2(File *f, char *oelem)
1057 oelem = f->dir.elem;
1059 //print("fileMetaFlush %s->%s\n", oelem, f->dir.elem);
1063 if(!sourceLock(fp->msource, -1))
1065 /* can happen if source is clri'ed out from under us */
1066 if(f->boff == NilBlock)
1068 b = sourceBlock(fp->msource, f->boff, OReadWrite);
1072 if(!mbUnpack(&mb, b->data, fp->msource->dsize))
1074 if(!mbSearch(&mb, oelem, &i, &me))
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 */
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);
1090 sourceUnlock(fp->msource);
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 */
1112 fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff);
1115 /* make sure deletion goes to disk after new entry */
1116 bb = sourceBlock(fp->msource, f->boff, OReadWrite);
1119 blockDependency(b, bb, -1, nil, nil);
1123 sourceUnlock(fp->msource);
1132 sourceUnlock(fp->msource);
1137 fileMetaRemove(File *f, char *uid)
1147 fileWAccess(up, uid);
1151 sourceLock(up->msource, OReadWrite);
1152 b = sourceBlock(up->msource, f->boff, OReadWrite);
1156 if(!mbUnpack(&mb, b->data, up->msource->dsize))
1161 if(!mbSearch(&mb, f->dir.elem, &i, &me))
1168 sourceUnlock(up->msource);
1181 sourceUnlock(up->msource);
1187 /* assume file is locked, assume f->msource is locked */
1189 fileCheckEmpty(File *f)
1197 n = (sourceGetSize(r)+r->dsize-1)/r->dsize;
1199 b = sourceBlock(r, i, OReadOnly);
1202 if(!mbUnpack(&mb, b->data, r->dsize))
1217 fileRemove(File *f, char *uid)
1221 /* can not remove the root */
1230 if(f->source->mode != OReadWrite){
1234 if(!sourceLock2(f->source, f->msource, -1))
1236 if(fileIsDir(f) && !fileCheckEmpty(f))
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 */
1246 sourceRemove(f->msource);
1252 if(!fileMetaRemove(f, uid))
1258 sourceUnlock(f->source);
1260 sourceUnlock(f->msource);
1267 clri(File *f, char *uid)
1273 if(f->up->source->mode != OReadWrite){
1278 r = fileMetaRemove(f, uid);
1284 fileClriPath(Fs *fs, char *path, char *uid)
1286 return clri(_fileOpen(fs, path, 1), uid);
1290 fileClri(File *dir, char *elem, char *uid)
1292 return clri(_fileWalk(dir, elem, 1), uid);
1296 fileIncRef(File *vf)
1299 assert(vf->ref > 0);
1311 /* never linked in */
1312 assert(f->ref == 1);
1323 assert(f->ref == 0);
1324 assert(f->down == nil);
1326 fileMetaFlush2(f, nil);
1330 for(q = *qq; q; q = *qq){
1346 fileGetParent(File *f)
1349 return fileIncRef(f);
1350 return fileIncRef(f->up);
1365 /* flush out meta data */
1368 for(p=f->down; p; p=p->next)
1369 fileMetaFlush2(p, nil);
1372 dee = vtmallocz(sizeof(DirEntryEnum));
1373 dee->file = fileIncRef(f);
1379 dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size)
1386 epb = s->dsize/VtEntrySize;
1390 b = sourceBlock(s, bn, OReadOnly);
1393 if(!entryUnpack(&e, b->data, elem))
1396 /* hanging entries are returned as zero size */
1397 if(!(e.flags & VtEntryActive) || e.gen != gen)
1410 deeFill(DirEntryEnum *dee)
1413 Source *meta, *source;
1420 /* clean up first */
1421 for(i=dee->i; i<dee->n; i++)
1422 deCleanup(dee->buf+i);
1433 b = sourceBlock(meta, dee->boff, OReadOnly);
1436 if(!mbUnpack(&mb, b->data, meta->dsize))
1440 dee->buf = vtmalloc(n * sizeof(DirEntry));
1444 meUnpack(&me, &mb, i);
1445 if(!deUnpack(de, &me))
1448 if(!(de->mode & ModeDir))
1449 if(!dirEntrySize(source, de->entry, de->gen, &de->size))
1461 deeRead(DirEntryEnum *dee, DirEntry *de)
1468 werrstr("cannot happen in deeRead");
1476 if(!sourceLock2(f->source, f->msource, OReadOnly)){
1481 nb = (sourceGetSize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
1484 while(dee->i >= dee->n){
1485 if(dee->boff >= nb){
1496 memmove(de, dee->buf + dee->i, sizeof(DirEntry));
1501 sourceUnlock(f->source);
1502 sourceUnlock(f->msource);
1511 deeClose(DirEntryEnum *dee)
1516 for(i=dee->i; i<dee->n; i++)
1517 deCleanup(dee->buf+i);
1519 fileDecRef(dee->file);
1524 * caller must lock f->source and f->msource
1525 * caller must NOT lock the source and msource
1526 * referenced by dir.
1529 fileMetaAlloc(File *f, DirEntry *dir, u32int start)
1544 nb = (sourceGetSize(ms)+ms->dsize-1)/ms->dsize;
1548 for(bo=start; bo<nb; bo++){
1549 b = sourceBlock(ms, bo, OReadWrite);
1552 if(!mbUnpack(&mb, b->data, ms->dsize))
1554 nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
1555 if(n <= nn && mb.nindex < mb.maxindex)
1561 /* add block to meta file */
1563 b = sourceBlock(ms, bo, OReadWrite);
1566 sourceSetSize(ms, (nb+1)*ms->dsize);
1567 mbInit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
1570 p = mbAlloc(&mb, n);
1572 /* mbAlloc might have changed block */
1579 mbSearch(&mb, dir->elem, &i, &me);
1580 assert(me.p == nil);
1584 mbInsert(&mb, i, &me);
1587 /* meta block depends on super block for qid ... */
1588 bb = cacheLocal(b->c, PartSuper, 0, OReadOnly);
1589 blockDependency(b, bb, -1, nil, nil);
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);
1597 if(dir->mode & ModeDir){
1598 bb = sourceBlock(s, dir->mentry/epb, OReadOnly);
1599 blockDependency(b, bb, -1, nil, nil);
1617 if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
1627 assert(!canwlock(&f->fs->elk));
1637 fileRUnlock(File *f)
1645 assert(!canwlock(&f->fs->elk));
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.
1666 fileMetaLock(File *f)
1669 fprint(2, "f->elem = %s\n", f->dir.elem);
1670 assert(f->up != nil);
1671 assert(!canwlock(&f->fs->elk));
1676 fileMetaUnlock(File *f)
1678 wunlock(&f->up->lk);
1682 * f->source and f->msource must NOT be locked.
1686 fileRAccess(File* f)
1688 if(f->mode == OReadOnly || f->fs->noatimeupd)
1692 f->dir.atime = time(0L);
1698 * f->source and f->msource must NOT be locked.
1702 fileWAccess(File* f, char *mid)
1704 if(f->mode == OReadOnly)
1708 f->dir.atime = f->dir.mtime = time(0L);
1709 if(strcmp(f->dir.mid, mid) != 0){
1711 f->dir.mid = vtstrdup(mid);
1717 /*RSC: let's try this */
1718 /*presotto - lets not
1720 fileWAccess(f->up, mid);
1725 getEntry(Source *r, Entry *e, int checkepoch)
1731 memset(&e, 0, sizeof e);
1735 b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadOnly);
1738 if(!entryUnpack(e, b->data, r->offset % r->epb)){
1746 b = cacheGlobal(r->fs->cache, e->score, entryType(e), e->tag, OReadOnly);
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);
1759 setEntry(Source *r, Entry *e)
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);
1768 if(!entryUnpack(&oe, b->data, r->offset % r->epb)){
1773 entryPack(e, b->data, r->offset % r->epb);
1775 /* BUG b should depend on the entry pointer */
1782 /* assumes hold elk */
1784 fileSnapshot(File *dst, File *src, u32int epoch, int doarchive)
1788 /* add link to snapshot */
1789 if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1))
1793 e.archive = doarchive;
1795 ee.archive = doarchive;
1797 if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee))
1803 fileGetSources(File *f, Entry *e, Entry *ee)
1805 if(!getEntry(f->source, e, 0)
1806 || !getEntry(f->msource, ee, 0))
1812 * Walk down to the block(s) containing the Entries
1813 * for f->source and f->msource, copying as we go.
1816 fileWalkSources(File *f)
1818 if(f->mode == OReadOnly){
1819 fprint(2, "readonly in fileWalkSources\n");
1822 if(!sourceLock2(f->source, f->msource, OReadWrite)){
1823 fprint(2, "sourceLock2 failed in fileWalkSources\n");
1826 sourceUnlock(f->source);
1827 sourceUnlock(f->msource);
1832 * convert File* to full path name in malloced string.
1833 * this hasn't been as useful as we hoped it would be.
1840 static char root[] = "/";
1843 return vtstrdup("/**GOK**");
1845 p = fileGetParent(f);
1847 name = vtstrdup(root);
1849 pname = fileName(p);
1850 if (strcmp(pname, root) == 0)
1851 name = smprint("/%s", f->dir.elem);
1853 name = smprint("%s/%s", pname, f->dir.elem);