ref: b3e875d7db45fca0cde9f4efa12872f61afc54d7
dir: /dentry.c/
#include "all.h" /* b shoud be wlock'ed */ void flush(Iobuf *b) { u8 dowrite = 0; Iobuf *oldbuf; if(b == nil || b->xiobuf == nil || b->xiobuf[0] != Tdentry) panic("flush called on buf with tag %s\n", tagnames[b->tag]); oldbuf = nil; if(b->append != nil){ if(b->appendsize > 0){ writeallappend(b, b->blkno, &oldbuf); dowrite = 1; } freememunits(b->append, Maxdatablockunits); b->append = nil; } putbuf(b, dowrite); /* to wunlock b */ if(oldbuf) freeblockbuf(oldbuf); } u64 getindblk(u64 blkno, u64 reli, u16 tag, u64 path) { Iobuf *buf; u64 n, b; if(chatty9p > 2) dprint("getindblk blkno %llud reli %llud tag %d path %llud\n", blkno, reli, tag, path); if(tag < Tind0 || tag > Tmaxind || reli > nperindunit(tag+1)){ panic("devmafs: getindblock() wrong reli %llud for %s\n", reli, tagnames[tag]); return 0; } if(blkno == 0){ return 0; } buf = egetmetachk(blkno, Breadonly, tag, path); if(tag > Tind0){ n = nperindunit(tag); b = getindblk(buf->i->bufa[reli/n], reli%n, tag-1, path); }else{ b = buf->i->bufa[reli]; } putbuf(buf, 0); return b; } /* get the Dentry at reliddr of d also locks all the indirect blocks while walking to get there using if's instead of a loop to keep it easy to debug get the block number, at the relitive location, reli, of the Dentry d. A block number of 0 is the end or beyond the end of the contents. A reli of 0 returns the value in d->dblocks[0]. A reli of Ndblock-1 returns the value in d->dblocks[Ndblock-1]. A reli of Ndblock returns the value in (blocks[d->iblocks[0]])[0]. The Iobuf holding the d should be rlock'ed else it creates inconsistencies. */ u64 rel2abs(Dentry *d, u64 reli) { u8 tag; if(reli < Ndblock) return d->dblocks[reli]; tag = rel2tind(reli); return getindblk(d->iblocks[tag-Tind0], reli-tagstartreli(tag), tag, d->path); } u64 updateindblock(u64 dblkno, u64 indblkno, u64 reli, u16 tag, u64 path, u64 blkno) { Iobuf *buf; u64 n, childindblkno; if(tag < Tind0 || tag > Tmaxind || reli > nperindunit(tag+1)){ panic("devmafs: updateindblock() wrong reli %llud for %s\n", reli, tagnames[tag]); return 0; } if(indblkno == 0){ buf = allocmeta(tag, path); buf->i->dblkno = dblkno; indblkno = buf->blkno; }else{ buf = egetmetachk(indblkno, Bwritable, tag, path); if(buf->blkno != indblkno){ dprint("updateindblock: buf->blkno != indblkno" " buf->blkno %llud indblkno %llud\n", buf->blkno, indblkno); return 0; } } if(chatty9p > 2) dprint("updateindblock indblkno %llud reli %llud tag %s" " directblkno %llud nperindunit(tag) %llud\n", indblkno, reli, tagnames[tag], blkno, nperindunit(tag)); if(tag > Tind0){ n = nperindunit(tag); if(reli/n >= Nindperblock){ panic("updateindblock invalid reli: indblkno %llud reli %llud tag %s" " directblkno %llud reli/n %llud nperindunit(tag) %llud\n", indblkno, reli, tagnames[tag], blkno, reli/n, Nindperblock); dprint("%s",errstring[Ephase]); return 0; } childindblkno = updateindblock(dblkno, buf->i->bufa[reli/n], reli%n, tag-1, path, blkno); buf->i->bufa[reli/n] = childindblkno; }else{ if(reli >= Nindperblock){ panic("updateindblock invalid reli: indblkno %llud reli %llud tag %s" " directblkno %llud Nindperblock %llud\n", indblkno, reli, tagnames[tag], blkno, Nindperblock); dprint("%s",errstring[Ephase]); return 0; } buf->i->bufa[reli] = blkno; } putbuf(buf, 1); return indblkno; } /* store the block number at the relitive location, reli, of the Dentry in dbuf. The contents buffer is already stored and we store that from the leaf to the root. If there is a crash during this process, there are no dangling unfilled slots below. dbuf should be wlock'ed. */ u64 addrelative(Dentry *d, u64 dblkno, u64 reli, u64 blkno) { u64 path, nblkno; u8 tag; if(chatty9p > 2) dprint("addrelative %llud: reli %llud blkno %llud\n", dblkno, reli, blkno); path = d->path; if(reli < Ndblock){ d->dblocks[reli] = blkno; return blkno; } tag = rel2tind(reli); if(chatty9p > 2) dprint("addrelative(): reli %llud tag %s d->iblocks[%d] %llud blkno %llud\n", reli, tagnames[tag], tag-Tind0, d->iblocks[tag-Tind0], blkno); nblkno = updateindblock(dblkno, d->iblocks[tag-Tind0], reli-tagstartreli(tag), tag, path, blkno); d->iblocks[tag-Tind0] = nblkno; return nblkno; } /* directtag == Tdata for files and Tdentry for directories Could copy all the block numbers into memory, ufree the block and work on free'ing each block. But, this does not help as this would all be dangling stuff identifiable by the fsck and removed by it on a crash. */ u64 freeindblock(u64 iblkno, u16 tag, u64 qpath, u16 directtag, u64 reli, u64 lastreli, u64 lastunits) { Iobuf *ibuf; int i; u64 blkno; if(iblkno == 0) return reli; ibuf = egetmetachk(iblkno, Bwritable, tag, qpath); if(waserror()){ putbuf(ibuf, 0); nexterror(); } if(tag == Tind0) for(i=0; i < Nindperblock; i++){ blkno = ibuf->i->bufa[i]; if(blkno == 0) break; if(directtag == Tdentry) freeblocks(blkno, 1, directtag, qpath); else if(reli < lastreli) freeblocks(blkno, Maxdatablockunits, directtag, qpath); else freeblocks(blkno, lastunits, directtag, qpath); ibuf->i->bufa[i] = 0; reli++; } else for(i=0; i < Nindperblock; i++){ if(ibuf->i->bufa[i] == 0) break; reli = freeindblock(ibuf->i->bufa[i], tag-1, qpath, directtag, reli, lastreli, lastunits); ibuf->i->bufa[i] = 0; } poperror(); freeblockbuf(ibuf); return reli; } /* dbuf should be wlock'ed */ void truncatefilebuf(Iobuf *dbuf, s16 uid) { Dentry *d, d1; int i; u64 reli, lastreli, lastblks; d = dbuf->d; memcpy(&d1, d, sizeof(Dentry)); for(i=0; i<Ndblock; i++) d->dblocks[i] = 0; for(i=0;i<Niblock; i++) d->iblocks[i] = 0; d->size = 0; d->mtime = nsec(); d->muid = uid; putbuf(dbuf, 1); if(d1.size <= Ddatasize) return; lastreli = d1.size/Maxdatablocksize; lastblks = nlastdatablocks(d1.size); for(i=0; i<Ndblock; i++){ if(d1.dblocks[i] == 0) return; if(i < lastreli) freeblocks(d1.dblocks[i], Maxdatablockunits, Tdata, d1.path); else freeblocks(d1.dblocks[i], lastblks, Tdata, d1.path); } for(i=0, reli=Ndblock;i<Niblock; i++){ if(d1.iblocks[i] == 0) return; reli = freeindblock(d1.iblocks[i], Tind0+i, d1.path, Tdata, reli, lastreli, lastblks); } } /* truncates the file's contents. Clear out the dentry data block numbers and store it to disk. If there is a crash, during the removal of the free blocks, the fsck can remove the dangling blocks without worrying about orphaned links in a dentry. */ void truncatefile(u64 qpath, u64 dblkno, s16 uid) { Iobuf *dbuf; if(qpath < Qpusers || dblkno == 0) return; dbuf = egetmetachk(dblkno, Bwritable, Tdentry, qpath); truncatefilebuf(dbuf, uid); } /* name is removed by the caller */ /* Removes the file contents and zero's the dentry The block will still be linked in the parent. When a file/directory is created in the parent, this zero'ed block will be reused for that dentry. Following this sequence to avoid being stuck with links to removed content on a crash. 1. zero out the dentry 2. free the direct and indirect blocks */ void rmfile(u64 qpath, u64 dblkno) { Iobuf *dbuf; Dentry d; int i; u64 size, reli, lastreli, lastblks; if(qpath < Qpusers || dblkno == 0) return; dbuf = egetmetachk(dblkno, Bwritable, Tdentry, qpath); size = dbuf->d->size; memcpy(&d, dbuf->d, sizeof(Dentry)); memset(dbuf->d, 0, Blocksize); settag(dbuf, Tdentry, Qpnone); if(dbuf->append){ /* data not yet written */ freememunits((u8*)dbuf->append, Maxdatablockunits); dbuf->append = nil; dbuf->appendsize = 0; } putbuf(dbuf, 1); if(size <= Ddatasize) return; lastreli = d.size/Maxdatablocksize; lastblks = nlastdatablocks(d.size); for(i=0; i<Ndblock; i++){ if(d.dblocks[i] == 0) return; if(i < lastreli) freeblocks(d.dblocks[i], Maxdatablockunits, Tdata, qpath); else freeblocks(d.dblocks[i], lastblks, Tdata, qpath); } for(i=0, reli=Ndblock;i<Niblock; i++){ if(d.iblocks[i] == 0) return; reli = freeindblock(d.iblocks[i], Tind0+i, qpath, Tdata, reli, lastreli, lastblks); } } /* name is removed by the caller */ void rmdirectory(u64 qpath, u64 dblkno) { Dentry d, *child; Iobuf *dbuf, *buf; u8 ct; u64 cqpath; u64 blkno, reli; int i; u16 mode; if(qpath < Qpusers || dblkno == 0) return; /* clear the dentry to avoid links to removed content */ dbuf = egetmetachk(dblkno, Bwritable, Tdentry, qpath); memcpy(&d, dbuf->d, sizeof(Dentry)); memset(dbuf->d, 0, Blocksize); settag(dbuf, Tdentry, Qpnone); putbuf(dbuf, 1); /* remove the children the linked dentries are still linked though zero'ed out */ for(reli = 0, blkno = 1; blkno > 0; reli++){ if((blkno = rel2abs(&d, reli)) == 0) break; buf = egetmeta(blkno, Breadonly, Bused); child = buf->d; cqpath = child->path; mode = child->mode; ct = child->tag; putbuf(buf, 0); /* nothing to do for already zero'ed out slots */ if(cqpath == Qpnone || ct == Tnone) continue; if(mode & DMDIR) rmdirectory(cqpath, blkno); else rmfile(cqpath, blkno); } /* free the chain of blocks to zero'ed dentries and the zero'ed dentries */ for(i=0; i<Ndblock; i++){ if(d.dblocks[i] == 0) return; freeblocks(d.dblocks[i], 1, Tdentry, Qpnone); } for(i=0, reli=Ndblock;i<Niblock; i++){ if(d.iblocks[i] == 0) return; reli = freeindblock(d.iblocks[i], Tind0+i, qpath, Tdentry, reli, 0, 1); } } /* the same as iaccess() of cwfs */ int canaccess(s16 uid, Dentry *d, u32 mode) { /* for not "none" users, check user or group permissions */ if(uid != None){ /* owner */ if(uid == d->uid) if((mode<<6) & d->mode) return 1; /* group membership */ if(ingroup(uid, d->gid, 0)) if((mode<<3) & d->mode) return 1; } /* check other permissions for: 1. none users 2. for users that are not the owner or in the group */ /* from fs(4) and cwfs(4) The group numbered 9999, normally called noworld, is special on the file server. Any user belonging to that group has attenuated access privileges. Specifically, when checking such a user's access to files, the file's permission bits are first ANDed with 0770 for normal files or 0771 for directories. The effect is to deny world access permissions to noworld users, except when walking directories. */ /* noworld users only have access to walk directories */ if(ingroup(uid, Noworld, 0)) if((d->mode & DMDIR) && mode == DMEXEC) return 1; else return 0; /* checking other access */ if(d->mode & mode) return 1; /* not doing the du read access or allowing god that cwfs does for now */ return 0; } /* get the contents in the reli'th block number of the Dir, d. The buffer holding the d should be locked by the caller. only for file contents. not for directory contents. */ Iobuf * getdatablkat(Dentry *d, u64 reli) { u64 blkno; /* rel2abs() rlock's while reading. not necessary, as the dbuf is locked anyway. */ if((blkno = rel2abs(d, reli)) == 0) return nil; if(reli < d->size/Maxdatablocksize) return egetbufchk(blkno, Maxdatablockunits, Breadonly, Tdata, d->path, getcallerpc(&d)); else return egetbufchk(blkno, nlastdatablocks(d->size), Breadonly, Tdata, d->path, getcallerpc(&d)); } void saveextentstofile(u64 blkno, u64 qpath, u16 uid, Extents *es) { s32 nbuf; s8 *buf; /* will not be accurate for frees as we are allocating blocks below. But, will be more than what we will actually end up using. */ nbuf = sizeofextents(es); /* +1 as snprint() always places a terminating NUL byte */ buf = emalloc9p(nbuf+1); /* get the extents into buf */ if(saveextents(es, buf, nbuf+1) == -1) error("savefrees nbuf not enough"); /* writing the actual extents now */ truncatefile(qpath, blkno, uid); writefile(blkno, qpath, uid, buf, nbuf+1, 0); free(buf); } /* the frees list of free blocks will include the blocks used to store the frees */ void savefrees(void) { Iobuf *dbuf, *oldbuf; /* should not be necessary as we clear out the file in loadfrees() */ clearfrees(); saveextentstofile(Bdfrees, Qpfrees, -1, &frees); /* flush to the disk if append has stuff */ dbuf = egetmetachk(Bdfrees, Bwritable, Tdentry, Qpfrees); if(dbuf->append == nil) putbuf(dbuf, 0); else{ oldbuf = nil; writeallappend(dbuf, Bdfrees, &oldbuf); putbuf(dbuf, 1); /* if(oldbuf) not needed as the old blocks would already be free blocks freeblockbuf(oldbuf); */ } } u64 loadextentsfile(u64 blkno, u64 qpath, Extents *es) { u64 size; s8 *buf; size = readfilesize(blkno, qpath); if(size == 0) return size; buf = emalloc9p(size); if(buf == nil) error("loadfrees: nil emalloc of %llud bytes", size); if(readfile(blkno, qpath, buf, size, 0) != size) error("loadextentsfile: could not load extents"); loadextents(es, buf, size); free(buf); return size; } void loadfrees(void) { u64 size; size = loadextentsfile(Bdfrees, Qpfrees, &frees); if(size == 0) panic("There are no free blocks.\n" "If there was an unsafe shutdown," " use \'disk/fsck %s\' to correct the disk state\n", devfile); } /* clear out /a/frees data contents after starting up I cannot use truncatefile() below as it would bfree() the blocks (which are already in the Extents frees) and that would cause an inconsistency/panic */ void clearfrees(void) { Iobuf *dbuf; Dentry *d; dbuf = egetmetachk(Bdfrees, Bwritable, Tdentry, Qpfrees); d = dbuf->d; d->size = 0; memset(d->buf, 0, Ddatasize); d->mtime = nsec(); d->muid = -1; putbuf(dbuf, 1); }