#include #include #include #include #include #include #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) { USED(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 %ud: 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<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; ingroup; 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) { USED(fsys); 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; ing; 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; iblocksize; 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(; iblocksize; 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; }