ref: 80e6c118c65b013bde24f8b9c95881c0e3a73d3e
dir: /dentry.c/
#include "all.h" /* b shoud be wlock'ed */ s8 flush(Iobuf *b) { if(b->tag != Tdentry) return 0; if(b->append != nil){ if(b->appendsize > 0){ recentmetadata(b->m, &b->cur, &b->new); /* writable */ memcpy(b->new, b->cur, Blocksize); b->new->verd++; writeallappend(b, b->new, b->blkno); putbuf(b, 1); } freememunits(b->append, Maxdatablockunits); b->append = nil; return 0; } return 1; } 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; } if((buf = getmetachk(blkno, Breadonly, tag, path)) == nil){ dprint("%s",errstring[Ephase]); return 0; } if(tag > Tind0){ n = nperindunit(tag); b = getindblk(buf->cur->bufa[reli/n], reli%n, tag-1, path); }else{ b = buf->cur->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){ if((buf = allocmeta(tag, path)) == nil){ dprint("%s",errstring[Efull]); return 0; } buf->new->dblkno = dblkno; indblkno = buf->blkno; }else{ if((buf = getmetachk(indblkno, Bwritable, 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){ 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->new->bufa[reli/n], reli%n, tag-1, path, blkno); buf->new->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->new->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:%s reli %llud blkno %llud\n", dblkno, d->name, 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); if((nblkno = updateindblock(dblkno, d->iblocks[tag-Tind0], reli-tagstartreli(tag), tag, path, blkno)) == 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. */ 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 = getmetachk(iblkno, Bwritable, tag, qpath); if(ibuf == nil){ panic("%s",errstring[Ephase]); return reli; } if(tag == Tind0) for(i=0; i < Nindperblock; i++){ blkno = ibuf->new->bufa[i]; if(blkno == 0) break; if(directtag == Tdentry) freeblocks(blkno, Metadataunits, directtag, qpath); else if(reli < lastreli) freeblocks(blkno, Maxdatablockunits, directtag, qpath); else freeblocks(blkno, lastunits, directtag, qpath); ibuf->new->bufa[i] = 0; reli++; } else for(i=0; i < Nindperblock; i++){ if(ibuf->new->bufa[i] == 0) break; reli = freeindblock(ibuf->new->bufa[i], tag-1, qpath, directtag, reli, lastreli, lastunits); ibuf->new->bufa[i] = 0; } 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 = (Dentry*)dbuf->new; 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 = getmetachk(dblkno, Bwritable, 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; u64 size, reli, lastreli, lastblks; if(qpath < Qpusers || dblkno == 0) return; dbuf = getmetachk(dblkno, Bwritable, Tdentry, qpath); if(dbuf == nil) dprint("%s",errstring[Ephase]); size = dbuf->new->size; memcpy(&d, dbuf->new, sizeof(Dentry)); memset(dbuf->new, 0, Blocksize); settag(dbuf, Tdentry, Qpnone); dbuf->new->verd = d.verd; 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); } } 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 = getmetachk(dblkno, Bwritable, Tdentry, qpath); if(dbuf == nil) dprint("%s",errstring[Ephase]); memcpy(&d, dbuf->new, sizeof(Dentry)); memset(dbuf->new, 0, Blocksize); settag(dbuf, Tdentry, Qpnone); dbuf->new->verd = d.verd; 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 = getmeta(blkno, Breadonly, Bused); child = (Dentry*)buf->cur; 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], Metadataunits, 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, Metadataunits); } } /* 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; u64 blkno; *dbuf = getmetachk(dblkno, Breadonly, Tdentry, qpath); if(*dbuf == nil) return nil; d = (Dentry*)((*dbuf)->cur); if(canaccess(uid, d, DMEXEC) == 0){ putbuf(*dbuf, 0); *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++){ if((blkno = rel2abs(d, reli)) == 0){ putbuf(*dbuf, 0); *dbuf = nil; return nil; } if(chatty9p>2) dprint("searchdir reli %d s.blkno %llud s.len %d\n", reli, blkno); *buf = getmeta(blkno, Breadonly, Bused); if(*buf == nil){ putbuf(*dbuf, 0); *dbuf = nil; dprint("%s",errstring[Ephase]); return nil; } ch = (Dentry*)(*buf)->cur; if(chatty9p > 2) dprint("searchdir: dblkno %llud qpath %llud searchname %s searchidx %d" " reli %llud blkno %llud ch->path %llud\n", dblkno, qpath, searchname, searchidx, reli, blkno, ch->path); if(checktag(*buf, Metadataunits, Tdentry, ch->path) == 0){ putbuf(*buf, 0); putbuf(*dbuf, 0); *dbuf = *buf = nil; dprint("%s",errstring[Ephase]); return nil; } /* nothing to do for already zero'ed out slots */ if(ch->tag == Tdentry && ch->path == Qpnone) goto Nextdentry; if(searchname != nil){ if(strcmp(searchname, (char*)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, 0); } /* should never be here */ } void freesearchstate(Iobuf **dbuf, Iobuf **buf) { putbuf(*buf, 0); putbuf(*dbuf, 0); *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 * getdatablkat(Dentry *d, u64 reli) { Iobuf *buf; 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) buf = getbufchk(blkno, Maxdatablockunits, Breadonly, Tdata, d->path); else buf = getbufchk(blkno, nlastdatablocks(d->size), Breadonly, Tdata, d->path); if(buf == nil){ putbuf(buf, 0); 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; Iobuf *dbuf; /* 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); /* flush to the disk if append has stuff */ dbuf = getmetachk(dblkno, Bwritable, Tdentry, Qpfrees); if(dbuf == nil) return; if(dbuf->append != nil) writeallappend(dbuf, dbuf->new, dblkno); putbuf(dbuf, 1); } 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 = getmetachk(dblkno, Bwritable, Tdentry, Qpfrees); if(dbuf == nil) dprint("%s",errstring[Ephase]); d = (Dentry*)dbuf->new; d->size = 0; for(i=0; i<Ndblock; i++) d->dblocks[i] = 0; for(i=0;i<Niblock; i++) d->iblocks[i] = 0; d->mtime = nsec(); d->muid = -1; putbuf(dbuf, 1); }