diff options
Diffstat (limited to 'src/libdiskfs/ext2.c')
-rw-r--r-- | src/libdiskfs/ext2.c | 742 |
1 files changed, 742 insertions, 0 deletions
diff --git a/src/libdiskfs/ext2.c b/src/libdiskfs/ext2.c new file mode 100644 index 00000000..17039c0f --- /dev/null +++ b/src/libdiskfs/ext2.c @@ -0,0 +1,742 @@ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include <sunrpc.h> +#include <nfs3.h> +#include <diskfs.h> +#include "ext2.h" + +#define debug 1 + +static int ext2sync(Fsys*); +static void ext2close(Fsys*); +static Block* ext2blockread(Fsys*, u64int); + +static Nfs3Status ext2root(Fsys*, Nfs3Handle*); +static Nfs3Status ext2getattr(Fsys*, SunAuthUnix *au, Nfs3Handle*, Nfs3Attr*); +static Nfs3Status ext2lookup(Fsys*, SunAuthUnix *au, Nfs3Handle*, char*, Nfs3Handle*); +static Nfs3Status ext2readfile(Fsys*, SunAuthUnix *au, Nfs3Handle*, u32int, u64int, uchar**, u32int*, u1int*); +static Nfs3Status ext2readlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link); +static Nfs3Status ext2readdir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int, u64int, uchar**, u32int*, u1int*); +static Nfs3Status ext2access(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr); + +Fsys* +fsysopenext2(Disk *disk) +{ + Ext2 *fs; + Fsys *fsys; + + fsys = emalloc(sizeof(Fsys)); + fs = emalloc(sizeof(Ext2)); + fs->disk = disk; + fsys->priv = fs; + fs->fsys = fsys; + fsys->type = "ext2"; + fsys->_readblock = ext2blockread; + fsys->_sync = ext2sync; + fsys->_root = ext2root; + fsys->_getattr = ext2getattr; + fsys->_access = ext2access; + fsys->_lookup = ext2lookup; + fsys->_readfile = ext2readfile; + fsys->_readlink = ext2readlink; + fsys->_readdir = ext2readdir; + + if(ext2sync(fsys) < 0) + goto error; + + return fsys; + +error: + ext2close(fsys); + return nil; +} + +static void +ext2close(Fsys *fsys) +{ + Ext2 *fs; + + fs = fsys->priv; + free(fs); + free(fsys); +} + +static Group* +ext2group(Ext2 *fs, u32int i, Block **pb) +{ + Block *b; + u64int addr; + Group *g; + + if(i >= fs->ngroup) + return nil; + + addr = fs->groupaddr + i/fs->descperblock; + b = diskread(fs->disk, fs->blocksize, addr*fs->blocksize); + if(b == nil) + return nil; + g = (Group*)(b->data+i%fs->descperblock*GroupSize); + *pb = b; + return g; +} + +static Block* +ext2blockread(Fsys *fsys, u64int vbno) +{ + Block *bitb; + Group *g; + Block *gb; + uchar *bits; + u32int bno, boff; + Ext2 *fs; + + fs = fsys->priv; + if(vbno >= fs->nblock) + return nil; + bno = vbno; + if(bno != vbno) + return nil; + +/* + if(bno < fs->firstblock) + return diskread(fs->disk, fs->blocksize, (u64int)bno*fs->blocksize); +*/ + if(bno < fs->firstblock) + return nil; + + bno -= fs->firstblock; + if((g = ext2group(fs, bno/fs->blockspergroup, &gb)) == nil){ + if(debug) + fprint(2, "loading group: %r..."); + return nil; + } +// if(debug) +// fprint(2, "group %d bitblock=%d...", bno/fs->blockspergroup, g->bitblock); + + if((bitb = diskread(fs->disk, fs->blocksize, (u64int)g->bitblock*fs->blocksize)) == nil){ + if(debug) + fprint(2, "loading bitblock: %r..."); + blockput(gb); + return nil; + } + bits = bitb->data; + boff = bno%fs->blockspergroup; + if((bits[boff>>3] & (1<<(boff&7))) == 0){ + if(debug) + fprint(2, "block %d not allocated...", bno); + blockput(bitb); + blockput(gb); + return nil; + } + + bno += fs->firstblock; + return diskread(fs->disk, fs->blocksize, (u64int)bno*fs->blocksize); +} + +static Block* +ext2datablock(Ext2 *fs, u32int bno, int size) +{ + return ext2blockread(fs->fsys, bno+fs->firstblock); +} + +static Block* +ext2fileblock(Ext2 *fs, Inode *ino, u32int bno, int size) +{ + int ppb; + Block *b; + u32int *a; + u32int obno; + + obno = bno; + if(bno < NDIRBLOCKS){ + if(debug) + fprint(2, "fileblock %d -> %d...", + bno, ino->block[bno]); + return ext2datablock(fs, ino->block[bno], size); + } + bno -= NDIRBLOCKS; + ppb = fs->blocksize/4; + + /* one indirect */ + if(bno < ppb){ + b = ext2datablock(fs, ino->block[INDBLOCK], fs->blocksize); + if(b == nil) + return nil; + a = (u32int*)b->data; + bno = a[bno%ppb]; + blockput(b); + return ext2datablock(fs, bno, size); + } + bno -= ppb; + + /* one double indirect */ + if(bno < ppb*ppb){ + b = ext2datablock(fs, ino->block[DINDBLOCK], fs->blocksize); + if(b == nil) + return nil; + a = (u32int*)b->data; + bno = a[(bno/ppb)%ppb]; + blockput(b); + b = ext2datablock(fs, bno, fs->blocksize); + if(b == nil) + return nil; + a = (u32int*)b->data; + bno = a[bno%ppb]; + blockput(b); + return ext2datablock(fs, bno, size); + } + bno -= ppb*ppb; + + /* one triple indirect */ + if(bno < ppb*ppb*ppb){ + b = ext2datablock(fs, ino->block[TINDBLOCK], fs->blocksize); + if(b == nil) + return nil; + a = (u32int*)b->data; + bno = a[(bno/(ppb*ppb))%ppb]; + blockput(b); + b = ext2datablock(fs, bno, fs->blocksize); + if(b == nil) + return nil; + a = (u32int*)b->data; + bno = a[(bno/ppb)%ppb]; + blockput(b); + b = ext2datablock(fs, bno, fs->blocksize); + if(b == nil) + return nil; + a = (u32int*)b->data; + bno = a[bno%ppb]; + blockput(b); + return ext2datablock(fs, bno, size); + } + + fprint(2, "ext2fileblock %llud: too big\n", obno); + return nil; +} + +static int +checksuper(Super *super) +{ + if(super->magic != SUPERMAGIC){ + werrstr("bad magic 0x%ux wanted 0x%ux", super->magic, SUPERMAGIC); + return -1; + } + return 0; +} + +static int +ext2sync(Fsys *fsys) +{ + int i; + Group *g; + Block *b; + Super *super; + Ext2 *fs; + Disk *disk; + + fs = fsys->priv; + disk = fs->disk; + if((b = diskread(disk, SBSIZE, SBOFF)) == nil) + goto error; + super = (Super*)b->data; + if(checksuper(super) < 0) + goto error; + fs->blocksize = MINBLOCKSIZE<<super->logblocksize; + fs->nblock = super->nblock; + fs->ngroup = (super->nblock+super->blockspergroup-1) + / super->blockspergroup; + fs->inospergroup = super->inospergroup; + fs->blockspergroup = super->blockspergroup; + fs->inosperblock = fs->blocksize / InodeSize; + if(fs->blocksize == SBOFF) + fs->groupaddr = 2; + else + fs->groupaddr = 1; + fs->descperblock = fs->blocksize / GroupSize; + fs->firstblock = super->firstdatablock; + blockput(b); + + fsys->blocksize = fs->blocksize; + fsys->nblock = fs->nblock; + fprint(2, "ext2 %d %d-byte blocks, first data block %d, %d groups of %d\n", + fs->nblock, fs->blocksize, fs->firstblock, fs->ngroup, fs->blockspergroup); + + if(0){ + for(i=0; i<fs->ngroup; i++) + if((g = ext2group(fs, i, &b)) != nil){ + fprint(2, "grp %d: bitblock=%d\n", i, g->bitblock); + blockput(b); + } + } + return 0; + +error: + blockput(b); + return -1; +} + +static void +mkhandle(Nfs3Handle *h, u64int ino) +{ + h->h[0] = ino>>24; + h->h[1] = ino>>16; + h->h[2] = ino>>8; + h->h[3] = ino; + h->len = 4; +} + +static u32int +byte2u32(uchar *p) +{ + return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; +} + +static Nfs3Status +handle2ino(Ext2 *fs, Nfs3Handle *h, u32int *pinum, Inode *ino) +{ + int i; + uint ioff; + u32int inum; + u32int addr; + Block *gb, *b; + Group *g; + + if(h->len != 4) + return Nfs3ErrBadHandle; + inum = byte2u32(h->h); + if(pinum) + *pinum = inum; + i = (inum-1) / fs->inospergroup; + if(i >= fs->ngroup) + return Nfs3ErrBadHandle; + ioff = (inum-1) % fs->inospergroup; + if((g = ext2group(fs, i, &gb)) == nil) + return Nfs3ErrIo; + addr = g->inodeaddr + ioff/fs->inosperblock; + blockput(gb); + if((b = diskread(fs->disk, fs->blocksize, (u64int)addr*fs->blocksize)) == nil) + return Nfs3ErrIo; + *ino = ((Inode*)b->data)[ioff%fs->inosperblock]; + blockput(b); + return Nfs3Ok; +} + +static Nfs3Status +ext2root(Fsys *fsys, Nfs3Handle *h) +{ + mkhandle(h, ROOTINODE); + return Nfs3Ok; +} + +static Nfs3Status +ino2attr(Ext2 *fs, Inode *ino, u32int inum, Nfs3Attr *attr) +{ + u32int rdev; + + attr->type = -1; + switch(ino->mode&IFMT){ + case IFIFO: + attr->type = Nfs3FileFifo; + break; + case IFCHR: + attr->type = Nfs3FileChar; + break; + case IFDIR: + attr->type = Nfs3FileDir; + break; + case IFBLK: + attr->type = Nfs3FileBlock; + break; + case IFREG: + attr->type = Nfs3FileReg; + break; + case IFLNK: + attr->type = Nfs3FileSymlink; + break; + case IFSOCK: + attr->type = Nfs3FileSocket; + break; + case IFWHT: + default: + return Nfs3ErrBadHandle; + } + + attr->mode = ino->mode&07777; + attr->nlink = ino->nlink; + attr->uid = ino->uid; + attr->gid = ino->gid; + attr->size = ino->size; + attr->used = ino->nblock*fs->blocksize; + if(attr->type==Nfs3FileBlock || attr->type==Nfs3FileChar){ + rdev = ino->block[0]; + attr->major = (rdev>>8)&0xFF; + attr->minor = rdev & 0xFFFF00FF; + }else{ + attr->major = 0; + attr->minor = 0; + } + attr->fsid = 0; + attr->fileid = inum; + attr->atime.sec = ino->atime; + attr->atime.nsec = 0; + attr->mtime.sec = ino->mtime; + attr->mtime.nsec = 0; + attr->ctime.sec = ino->ctime; + attr->ctime.nsec = 0; + return Nfs3Ok; +} + +static int +ingroup(SunAuthUnix *au, uint gid) +{ + int i; + + for(i=0; i<au->ng; i++) + if(au->g[i] == gid) + return 1; + return 0; +} + +static Nfs3Status +inoperm(Inode *ino, SunAuthUnix *au, int need) +{ + int have; + + if(allowall) + return Nfs3Ok; + + have = ino->mode&0777; + if(ino->uid == au->uid) + have >>= 6; + else if(ino->gid == au->gid || ingroup(au, ino->gid)) + have >>= 3; + + if((have&need) != need) + return Nfs3ErrNotOwner; /* really EPERM */ + return Nfs3Ok; +} + +static Nfs3Status +ext2getattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr) +{ + Inode ino; + u32int inum; + Ext2 *fs; + Nfs3Status ok; + + fs = fsys->priv; + if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok) + return ok; + + USED(au); /* anyone can getattr */ + return ino2attr(fs, &ino, inum, attr); +} + +static Nfs3Status +ext2access(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr) +{ + int have; + Inode ino; + u32int inum; + Ext2 *fs; + Nfs3Status ok; + + fs = fsys->priv; + if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok) + return ok; + + have = ino.mode&0777; + if(ino.uid == au->uid) + have >>= 6; + else if(ino.gid == au->gid || ingroup(au, ino.gid)) + have >>= 3; + + *got = 0; + if((want&Nfs3AccessRead) && (have&AREAD)) + *got |= Nfs3AccessRead; + if((want&Nfs3AccessLookup) && (ino.mode&IFMT)==IFDIR && (have&AEXEC)) + *got |= Nfs3AccessLookup; + if((want&Nfs3AccessExecute) && (ino.mode&IFMT)!=IFDIR && (have&AEXEC)) + *got |= Nfs3AccessExecute; + + return ino2attr(fs, &ino, inum, attr); +} + +static Nfs3Status +ext2lookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh) +{ + u32int nblock; + u32int i; + uchar *p, *ep; + Dirent *de; + Inode ino; + Block *b; + Ext2 *fs; + Nfs3Status ok; + int len, want; + + fs = fsys->priv; + if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok) + return ok; + + if((ino.mode&IFMT) != IFDIR) + return Nfs3ErrNotDir; + + if((ok = inoperm(&ino, au, AEXEC)) != Nfs3Ok) + return ok; + + len = strlen(name); + nblock = (ino.size+fs->blocksize-1) / fs->blocksize; + if(debug) fprint(2, "%d blocks in dir...", nblock); + for(i=0; i<nblock; i++){ + if(i==nblock-1) + want = ino.size % fs->blocksize; + else + want = fs->blocksize; + b = ext2fileblock(fs, &ino, i, want); + if(b == nil){ + if(debug) fprint(2, "empty block..."); + continue; + } + p = b->data; + ep = p+b->len; + while(p < ep){ + de = (Dirent*)p; + if(de->reclen == 0){ + if(debug) + fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len); + break; + } + p += de->reclen; + if(p > ep){ + if(debug) + fprint(2, "bad len %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len); + break; + } + if(de->ino == 0) + continue; + if(4+2+2+de->namlen > de->reclen){ + if(debug) + fprint(2, "bad namelen %d at offset %d of %d\n", de->namlen, (int)(p-b->data), b->len); + break; + } + if(de->namlen == len && memcmp(de->name, name, len) == 0){ + mkhandle(nh, de->ino); + blockput(b); + return Nfs3Ok; + } + } + blockput(b); + } + return Nfs3ErrNoEnt; +} + +static Nfs3Status +ext2readdir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof) +{ + u32int nblock; + u32int i; + int off, done; + uchar *data, *dp, *dep, *p, *ep, *ndp; + Dirent *de; + Inode ino; + Block *b; + Ext2 *fs; + Nfs3Status ok; + Nfs3Entry e; + int want; + + fs = fsys->priv; + if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok) + return ok; + + if((ino.mode&IFMT) != IFDIR) + return Nfs3ErrNotDir; + + if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok) + return ok; + + if(cookie >= ino.size){ + *pcount = 0; + *pdata = 0; + return Nfs3Ok; + } + + dp = malloc(count); + data = dp; + if(dp == nil) + return Nfs3ErrNoMem; + dep = dp+count; + *peof = 0; + nblock = (ino.size+fs->blocksize-1) / fs->blocksize; + i = cookie/fs->blocksize; + off = cookie%fs->blocksize; + done = 0; + for(; i<nblock && !done; i++){ + if(i==nblock-1) + want = ino.size % fs->blocksize; + else + want = fs->blocksize; + b = ext2fileblock(fs, &ino, i, want); + if(b == nil) + continue; + p = b->data; + ep = p+b->len; + memset(&e, 0, sizeof e); + while(p < ep){ + de = (Dirent*)p; + if(de->reclen == 0){ + if(debug) fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len); + break; + } + p += de->reclen; + if(p > ep){ + if(debug) fprint(2, "reclen %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len); + break; + } + if(de->ino == 0){ + if(debug) fprint(2, "zero inode\n"); + continue; + } + if(4+2+2+de->namlen > de->reclen){ + if(debug) fprint(2, "bad namlen %d reclen %d at offset %d of %d\n", de->namlen, de->reclen, (int)(p-b->data), b->len); + break; + } + if(de->name[de->namlen] != 0){ + if(debug) fprint(2, "bad name %d %.*s\n", de->namlen, de->namlen, de->name); + continue; + } + if(debug) print("%s/%d ", de->name, (int)de->ino); + if((uchar*)de - b->data < off) + continue; + e.fileid = de->ino; + e.name = de->name; + e.cookie = (u64int)i*fs->blocksize + (p - b->data); + if(nfs3entrypack(dp, dep, &ndp, &e) < 0){ + done = 1; + break; + } + dp = ndp; + } + off = 0; + blockput(b); + } + if(i==nblock) + *peof = 1; + + *pcount = dp - data; + *pdata = data; + return Nfs3Ok; +} + +static Nfs3Status +ext2readfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, + u64int offset, uchar **pdata, u32int *pcount, u1int *peof) +{ + uchar *data; + Block *b; + Ext2 *fs; + int off, want, fragcount; + Inode ino; + Nfs3Status ok; + + fs = fsys->priv; + if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok) + return ok; + + if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok) + return ok; + + if(offset >= ino.size){ + *pdata = 0; + *pcount = 0; + *peof = 1; + return Nfs3Ok; + } + if(offset+count > ino.size) + count = ino.size-offset; + if(offset/fs->blocksize != (offset+count-1)/fs->blocksize) + count = fs->blocksize - offset%fs->blocksize; + + data = malloc(count); + if(data == nil) + return Nfs3ErrNoMem; + + want = offset%fs->blocksize+count; + if(want%fs->blocksize) + want += fs->blocksize - want%fs->blocksize; + + b = ext2fileblock(fs, &ino, offset/fs->blocksize, want); + if(b == nil){ + /* BUG: distinguish sparse file from I/O error */ + memset(data, 0, count); + }else{ + off = offset%fs->blocksize; + fragcount = count; /* need signed variable */ + if(off+fragcount > b->len){ + fragcount = b->len - off; + if(fragcount < 0) + fragcount = 0; + } + if(fragcount > 0) + memmove(data, b->data+off, fragcount); + count = fragcount; + blockput(b); + } + *peof = (offset+count == ino.size); + *pcount = count; + *pdata = data; + return Nfs3Ok; +} + +static Nfs3Status +ext2readlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link) +{ + Ext2 *fs; + Nfs3Status ok; + int len; + Inode ino; + Block *b; + + fs = fsys->priv; + if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok) + return ok; + if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok) + return ok; + + if(ino.size > 1024) + return Nfs3ErrIo; + len = ino.size; + + if(ino.nblock != 0){ + /* BUG: assumes symlink fits in one block */ + b = ext2fileblock(fs, &ino, 0, len); + if(b == nil) + return Nfs3ErrIo; + if(memchr(b->data, 0, len) != nil){ + blockput(b); + return Nfs3ErrIo; + } + *link = malloc(len+1); + if(*link == 0){ + blockput(b); + return Nfs3ErrNoMem; + } + memmove(*link, b->data, len); + (*link)[len] = 0; + blockput(b); + return Nfs3Ok; + } + + if(len > sizeof ino.block) + return Nfs3ErrIo; + + *link = malloc(len+1); + if(*link == 0) + return Nfs3ErrNoMem; + memmove(*link, ino.block, ino.size); + (*link)[len] = 0; + return Nfs3Ok; +} + |