ref: 5c21eca829380c28769f79beabb7cc51f1787f97
dir: /dentry.c/
#include "all.h" void getindspan(u64 blkno, u64 reli, u16 tag, u64 path, Spanid *s) { Iobuf *buf; u64 n; if(chatty9p > 2) dprint("getindspan blkno %llud reli %llud tag %d path %llud\n", blkno, reli, tag, path); if(tag > Tmaxind || reli > nperindunit(tag+1)){ panic("devmafs: getindblock() wrong reli %llud for %s\n", reli, tagnames[tag]); s->blkno = s->len = 0; return; } if(blkno == 0){ s->blkno = s->len = 0; return; } if((buf = getbufchk(blkno, 1,Breadonly, tag, path)) == nil){ dprint("%s",errstring[Ephase]); s->blkno = s->len = 0; return; } if(tag > Tind0){ n = nperindunit(tag); getindspan(buf->io->bufa[reli/n], reli%n, tag-1, path, s); }else{ s->blkno = buf->io->s[reli].blkno; s->len = buf->io->s[reli].len; } putbuf(buf); return; } /* 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. */ void rel2abs(Dentry *d, u64 reli, Spanid *s) { u8 tag; if(reli < Ndspanid){ s->blkno = d->dspans[reli].blkno; s->len = d->dspans[reli].len; return; } tag = rel2tind(reli); getindspan(d->iblocks[tag-Tind0], reli-Ndspanid, tag, d->qid.path, s); } u64 updateindblock(u64 indblkno, u64 reli, u16 tag, u64 path, u64 blkno, u16 len) { Iobuf *buf; u64 n, childindblkno; if(tag > Tmaxind || reli > nperindunit(tag+1)){ print("devmafs: updateindblock() wrong reli %llud for %s\n", reli, tagnames[tag]); return 0; } if(indblkno == 0){ if((buf = allocblocks(1, Bmod, tag, path)) == nil){ dprint("%s",errstring[Efull]); return 0; } indblkno = buf->blkno; }else{ if((buf = getbufchk(indblkno, 1, Bmod, tag, path)) == nil){ dprint("updateindblock: %s",errstring[Ephase]); return 0; } 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){ dprint("updateindblock invalid reli: indblkno %llud reli %llud tag %s\n" " directblkno %llud len %llud reli/n %llud nperiblock(Tind0) %llud\n", indblkno, reli, tagnames[tag], blkno, len, reli/n, Nindperblock); dprint("%s",errstring[Ephase]); return 0; } childindblkno = updateindblock(buf->io->bufa[reli/n], reli%n, tag-1, path, blkno, len); buf->io->bufa[reli/n] = childindblkno; }else{ if(reli >= nperiblock(Tind0)){ dprint("updateindblock invalid reli: indblkno %llud reli %llud tag %s\n" " directblkno %llud len %llud nperiblock(Tind0) %llud\n", indblkno, reli, tagnames[tag], blkno, len, nperiblock(Tind0)); dprint("%s",errstring[Ephase]); return 0; } buf->io->s[reli].blkno = blkno; buf->io->s[reli].len = len; } putbuf(buf); return indblkno; } /* store Spanid 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, u16 len) { u64 path, nblkno; u16 tag; if(chatty9p > 2) dprint("addrelative %llud:%s reli %llud blkno %llud len %llud\n", dblkno, d->name, reli, blkno, len); path = d->qid.path; if(reli < Ndspanid){ d->dspans[reli].blkno = blkno; d->dspans[reli].len = len; return blkno; } tag = rel2tind(reli); if(chatty9p > 2) dprint(" reli %llud tag %s d->iblocks[tag-Tind0] %llud blkno %llud len %llud\n", reli, tagnames[tag], d->iblocks[tag-Tind0], blkno, len); if((nblkno = updateindblock(d->iblocks[tag-Tind0], reli-Ndspanid, tag, path, blkno, len)) == 0){ dprint("%s",errstring[Ephase]); return 0; } d->iblocks[tag-Tind0] = nblkno; return nblkno; } /* directtag == Tdata for files and Tdentry for directories Could copy all the block numbers into memory, release 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. */ void freeindblocks(u64 iblkno, u16 tag, u64 qpath, u16 directtag) { Iobuf *ibuf; int i; u64 blkno; u16 len; if(iblkno == 0) return; ibuf = getbufchk(iblkno, 1, Bmod, tag, qpath); if(ibuf == nil) dprint("%s",errstring[Ephase]); if(tag == Tind0) for(i=Nspanidperblock-1; i >= 0; i--){ blkno = ibuf->io->s[i].blkno; if(blkno == 0) continue; len = ibuf->io->s[i].len; freeblocks(blkno, len, directtag, qpath); ibuf->io->s[i].blkno = 0; ibuf->io->s[i].len = 0; } else for(i=Nindperblock-1; i >= 0; i--){ if(ibuf->io->bufa[i] == 0) continue; freeindblocks(ibuf->io->bufa[i], tag-1, qpath, directtag); ibuf->io->bufa[i] = 0; } freeblockbuf(ibuf); } /* dbuf should be wlock'ed */ void truncatefilebuf(Iobuf *dbuf, s16 uid) { Dentry *d, d1; int i; d = &dbuf->io->d; memcpy(&d1, d, sizeof(Dentry)); for(i=0; i<Ndspanid; i++) d->dspans[i] = (Spanid){0,0}; for(i=0;i<Niblock; i++) d->iblocks[i] = 0; d->size = 0; d->mtime = nsec(); d->muid = uid; putbuf(dbuf); for(i=Niblock-1; i >= 0; i--){ if(d1.iblocks[i] == 0) continue; freeindblocks(d1.iblocks[i], Tind0+i, d1.qid.path, Tdata); } for(i=Ndspanid-1; i >= 0; i--){ if(d1.dspans[i].blkno == 0) continue; freeblocks(d1.dspans[i].blkno, d1.dspans[i].len, Tdata, d1.qid.path); } } /* 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 = getbufchk(dblkno, 1, Bmod, Tdentry, qpath); if(dbuf == nil) dprint("%s",errstring[Ephase]); truncatefilebuf(dbuf, uid); } /* 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; if(qpath < Qpusers || dblkno == 0) return; dbuf = getbufchk(dblkno, 1, Bmod, Tdentry, qpath); if(dbuf == nil) dprint("%s",errstring[Ephase]); memcpy(&d, &dbuf->io->d, sizeof(Dentry)); memset(dbuf->io->buf, 0, Blocksize); settag(dbuf, Tdentry, Qpnone); putbuf(dbuf); for(i=0; i<Ndspanid; i++){ if(d.dspans[i].blkno == 0) continue; freeblocks(d.dspans[i].blkno, d.dspans[i].len, Tdata, qpath); } for(i=0;i<Niblock; i++){ if(d.iblocks[i] == 0) continue; freeindblocks(d.iblocks[i], Tind0+i, qpath, Tdata); } } void rmdirectory(u64 qpath, u64 dblkno) { Dentry d, *child; Iobuf *dbuf, *buf; Tag ct; u64 cqpath; u64 blkno, reli; int i; u16 mode; Spanid s; if(qpath < Qpusers || dblkno == 0) return; /* clear the dentry to avoid links to removed content */ dbuf = getbufchk(dblkno, 1, Bmod, Tdentry, qpath); if(dbuf == nil) dprint("%s",errstring[Ephase]); memcpy(&d, dbuf->io->buf, sizeof(Dentry)); memset(dbuf->io->buf, 0, Blocksize); settag(dbuf, Tdentry, Qpnone); putbuf(dbuf); /* remove the children the linked dentries are still linked though zero'ed out */ for(reli = 0, blkno = 1; blkno > 0; reli++){ rel2abs(&d, reli, &s); if(s.blkno == 0) break; buf = getbuf(s.blkno, 1, Breadonly); child = &buf->io->d; cqpath = child->qid.path; mode = child->mode; memcpy(&ct, buf->io, sizeof(Tag)); putbuf(buf); /* nothing to do for already zero'ed out slots */ if(cqpath == Qpnone || ct.type == Tnone) continue; if(mode & DMDIR) rmdirectory(cqpath, s.blkno); else rmfile(cqpath, s.blkno); } /* free the chain of blocks to zero'ed dentries and the zero'ed dentries */ for(i=0; i<Ndspanid; i++){ if(d.dspans[i].blkno == 0) continue; freeblocks(d.dspans[i].blkno, d.dspans[i].len, Tdentry, Qpnone); } for(i=0;i<Niblock; i++){ if(d.iblocks[i] == 0) continue; freeindblocks(d.iblocks[i], Tind0+i, qpath, Tdentry); } } /* 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; } /* relative block numbers and the index are the same as we have 1 direntry per block */ Dentry * searchdir(u64 dblkno, u64 qpath, u16 uid, char *searchname, u64 searchidx, Iobuf **dbuf, Iobuf **buf) { u64 reli, idx; Dentry *d, *ch; Tag *cht; Spanid s; *dbuf = getbufchk(dblkno, 1, Breadonly, Tdentry, qpath); if(*dbuf == nil) return nil; d = &(*dbuf)->io->d; if(canaccess(uid, d, DMEXEC) == 0){ putbuf(*dbuf); *dbuf = nil; dprint("%s",errstring[Ephase]); return nil; } /* using idx to not include zero'ed out slots in our search */ for(reli = 0, idx = 0; ; reli++){ rel2abs(d, reli, &s); if(s.blkno == 0){ putbuf(*dbuf); *dbuf = nil; return nil; } if(chatty9p>2) dprint("searchdir reli %d s.blkno %llud s.len %d\n", reli, s.blkno, s.len); *buf = getbuf(s.blkno, s.len, Breadonly); if(*buf == nil){ putbuf(*dbuf); *dbuf = nil; dprint("%s",errstring[Ephase]); return nil; } ch = &(*buf)->io->d; cht = (Tag*)(*buf)->io; if(chatty9p > 2) dprint("searchdir: dblkno %llud qpath %llud searchname %s searchidx %d" " reli %llud s.blkno %llud s.len %d ch->qid.path %llud\n", dblkno, qpath, searchname, searchidx, reli, s.blkno, s.len, ch->qid.path); if(checktag(*buf, Tdentry, ch->qid.path) == 0){ putbuf(*buf); putbuf(*dbuf); *dbuf = *buf = nil; dprint("%s",errstring[Ephase]); return nil; } /* nothing to do for already zero'ed out slots */ if(cht->type == Tdentry && cht->path == Qpnone) goto Nextdentry; if(searchname != nil){ if(strcmp(searchname, ch->name) == 0){ if(chatty9p > 2) dprint("searchdir: found name %s\n", searchname); return ch; } }else if(idx == searchidx){ if(chatty9p > 2) dprint("searchdir: found index %d\n", searchidx); return ch; } idx++; /* so that zero'ed slots do not match */ Nextdentry: putbuf(*buf); } /* should never be here */ } void freesearchstate(Iobuf **dbuf, Iobuf **buf) { putbuf(*buf); putbuf(*dbuf); *dbuf = *buf = nil; } /* 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 * getdataspanat(Dentry *d, u64 reli, int flags) { Iobuf *buf; Spanid s; /* rel2abs() rlock's while reading. not necessary, as the dbuf is locked anyway. */ rel2abs(d, reli, &s); if(s.blkno == 0) return nil; buf = getbufchk(s.blkno, s.len, flags, Tdata, d->qid.path); if(buf == nil){ putbuf(buf); dprint("%s",errstring[Ephase]); return nil; } return buf; } /* the frees list of free blocks will include the blocks used to store the frees */ void savefrees(u64 dblkno) { s32 nbuf; s8 *buf; /* should not be necessary as we clear out the file in loadfrees() */ truncatefile(Qpfrees, dblkno, -1); /* will not be accurate as we are allocating blocks below. But, will be more than what we will actually end up using. */ nbuf = sizeofextents(&frees)+1; buf = emalloc(nbuf); /* get the extents into buf */ if(saveextents(&frees, buf, nbuf) == -1) panic("savefrees nbuf not enough"); /* writing the actual extents now */ writefile(dblkno, Qpfrees, -1, buf, nbuf, 0); free(buf); } void loadfrees(u64 dblkno) { u64 size, n; s8 *buf; Iobuf *dbuf; Dentry *d; int i; size = readfilesize(dblkno, Qpfrees); if(size == 0) panic("loadfrees size == 0"); buf = emalloc(size); if(buf == nil) panic("loadfrees: nil emalloc of %llud bytes", size); if(readfile(dblkno, Qpfrees, buf, size, 0) != size) panic("loadfrees: could not load frees"); n = loadextents(&frees, buf, size); if(chatty9p > 2) dprint("loadfrees: loaded free extents %llud\n", n); free(buf); /* clear out /adm/frees data contents */ /* 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 */ dbuf = getbufchk(dblkno, 1, Bmod, Tdentry, Qpfrees); if(dbuf == nil) dprint("%s",errstring[Ephase]); d = &dbuf->io->d; d->size = 0; for(i=0; i<Ndspanid; i++) d->dspans[i] = (Spanid){0,0}; for(i=0;i<Niblock; i++) d->iblocks[i] = 0; d->mtime = nsec(); d->muid = -1; putbuf(dbuf); }