| #include <u.h> |
| #include <libc.h> |
| #include <thread.h> |
| #include <sunrpc.h> |
| #include <nfs3.h> |
| #include <diskfs.h> |
| #include "ext2.h" |
| |
| static void parsedirent(Dirent*, uchar*); |
| static void parseinode(Inode*, uchar*); |
| static void parsegroup(Group*, uchar*); |
| static void parsesuper(Super*, uchar*); |
| |
| #define debug 0 |
| |
| 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; |
| fsys->_close = ext2close; |
| |
| 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 int |
| ext2group(Ext2 *fs, u32int i, Group *g) |
| { |
| Block *b; |
| u64int addr; |
| |
| if(i >= fs->ngroup) |
| return -1; |
| |
| addr = fs->groupaddr + i/fs->descperblock; |
| b = diskread(fs->disk, fs->blocksize, addr*fs->blocksize); |
| if(b == nil) |
| return -1; |
| parsegroup(g, b->data+i%fs->descperblock*GroupSize); |
| blockput(b); |
| return 0; |
| } |
| |
| static Block* |
| ext2blockread(Fsys *fsys, u64int vbno) |
| { |
| Block *bitb; |
| Group g; |
| uchar *bits; |
| u32int bno, boff, bitblock; |
| u64int bitpos; |
| 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(ext2group(fs, bno/fs->blockspergroup, &g) < 0){ |
| if(debug) |
| fprint(2, "loading group: %r..."); |
| return nil; |
| } |
| /* |
| if(debug) |
| fprint(2, "ext2 group %d: bitblock=%ud inodebitblock=%ud inodeaddr=%ud freeblocks=%ud freeinodes=%ud useddirs=%ud\n", |
| (int)(bno/fs->blockspergroup), |
| g.bitblock, |
| g.inodebitblock, |
| g.inodeaddr, |
| g.freeblockscount, |
| g.freeinodescount, |
| g.useddirscount); |
| if(debug) |
| fprint(2, "group %d bitblock=%d...", bno/fs->blockspergroup, g.bitblock); |
| */ |
| bitblock = g.bitblock; |
| bitpos = (u64int)bitblock*fs->blocksize; |
| |
| if((bitb = diskread(fs->disk, fs->blocksize, bitpos)) == nil){ |
| if(debug) |
| fprint(2, "loading bitblock: %r..."); |
| 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 in group %d: bitblock %d/%lld bits[%d] = %#x\n", |
| boff, bno/fs->blockspergroup, |
| (int)bitblock, |
| bitpos, |
| boff>>3, |
| bits[boff>>3]); |
| blockput(bitb); |
| return nil; |
| } |
| blockput(bitb); |
| |
| 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); |
| } |
| |
| static Block* |
| ext2fileblock(Ext2 *fs, Inode *ino, u32int bno, int size) |
| { |
| int ppb; |
| Block *b; |
| u32int *a; |
| u32int obno, pbno; |
| |
| 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]; |
| 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; |
| pbno = a[bno/ppb]; |
| bno = bno%ppb; |
| blockput(b); |
| b = ext2datablock(fs, pbno, fs->blocksize); |
| if(b == nil) |
| return nil; |
| a = (u32int*)b->data; |
| bno = a[bno]; |
| 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; |
| pbno = a[bno/(ppb*ppb)]; |
| bno = bno%(ppb*ppb); |
| blockput(b); |
| b = ext2datablock(fs, pbno, fs->blocksize); |
| if(b == nil) |
| return nil; |
| a = (u32int*)b->data; |
| pbno = a[bno/ppb]; |
| bno = bno%ppb; |
| blockput(b); |
| b = ext2datablock(fs, pbno, fs->blocksize); |
| if(b == nil) |
| return nil; |
| a = (u32int*)b->data; |
| bno = a[bno]; |
| 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) |
| return -1; |
| parsesuper(&super, b->data); |
| blockput(b); |
| if(checksuper(&super) < 0) |
| return -1; |
| 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; |
| if(super.revlevel >= 1) |
| fs->inosize = super.inosize; |
| else |
| fs->inosize = 128; |
| fs->inosperblock = fs->blocksize / fs->inosize; |
| if(fs->blocksize == SBOFF) |
| fs->groupaddr = 2; |
| else |
| fs->groupaddr = 1; |
| fs->descperblock = fs->blocksize / GroupSize; |
| fs->firstblock = super.firstdatablock; |
| |
| fsys->blocksize = fs->blocksize; |
| fsys->nblock = fs->nblock; |
| if(debug) 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(ext2group(fs, i, &g) >= 0) |
| fprint(2, "grp %d: bitblock=%d\n", i, g.bitblock); |
| } |
| return 0; |
| } |
| |
| 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 *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(ext2group(fs, i, &g) < 0) |
| return Nfs3ErrIo; |
| addr = g.inodeaddr + ioff/fs->inosperblock; |
| if((b = diskread(fs->disk, fs->blocksize, (u64int)addr*fs->blocksize)) == nil) |
| return Nfs3ErrIo; |
| parseinode(ino, b->data+fs->inosize*(ioff%fs->inosperblock)); |
| blockput(b); |
| return Nfs3Ok; |
| } |
| |
| static Nfs3Status |
| ext2root(Fsys *fsys, Nfs3Handle *h) |
| { |
| USED(fsys); |
| mkhandle(h, ROOTINODE); |
| return Nfs3Ok; |
| } |
| |
| static u64int |
| inosize(Inode* ino) |
| { |
| u64int size; |
| |
| size = ino->size; |
| if((ino->mode&IFMT)==IFREG) |
| size |= (u64int)ino->diracl << 32; |
| return size; |
| } |
| |
| 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 = inosize(ino); |
| attr->used = (u64int)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){ |
| parsedirent(&de, 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, outofspace; |
| 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(debug) print("readdir cookie %#llux ino.size %#llux\n", |
| (u64int)cookie, (u64int)ino.size); |
| |
| if(cookie >= ino.size){ |
| *peof = 1; |
| *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; |
| outofspace = 0; |
| for(; i<nblock && !outofspace; i++, off=0){ |
| 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){ |
| parsedirent(&de, 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(debug) print("%.*s/%d ", de.namlen, de.name, (int)de.ino); |
| if(p-de.reclen - b->data < off) |
| continue; |
| e.fileid = de.ino; |
| e.name = de.name; |
| e.namelen = de.namlen; |
| e.cookie = (u64int)i*fs->blocksize + (p - b->data); |
| if(debug) print("%.*s %#llux\n", utfnlen(e.name, e.namelen), e.name, (u64int)e.cookie); |
| if(nfs3entrypack(dp, dep, &ndp, &e) < 0){ |
| outofspace = 1; |
| break; |
| } |
| dp = ndp; |
| } |
| blockput(b); |
| } |
| if(i==nblock && !outofspace) |
| *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 skip1, tot, want, fragcount; |
| Inode ino; |
| Nfs3Status ok; |
| u64int size; |
| |
| fs = fsys->priv; |
| if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok) |
| return ok; |
| |
| if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok) |
| return ok; |
| |
| size = inosize(&ino); |
| if(offset >= size){ |
| *pdata = 0; |
| *pcount = 0; |
| *peof = 1; |
| return Nfs3Ok; |
| } |
| if(offset+count > size) |
| count = size-offset; |
| |
| data = malloc(count); |
| if(data == nil) |
| return Nfs3ErrNoMem; |
| memset(data, 0, count); |
| |
| skip1 = offset%fs->blocksize; |
| offset -= skip1; |
| want = skip1+count; |
| |
| /* |
| * have to read multiple blocks if we get asked for a big read. |
| * Linux NFS client assumes that if you ask for 8k and only get 4k |
| * back, the remaining 4k is zeros. |
| */ |
| for(tot=0; tot<want; tot+=fragcount){ |
| b = ext2fileblock(fs, &ino, (offset+tot)/fs->blocksize, fs->blocksize); |
| fragcount = fs->blocksize; |
| if(b == nil) |
| continue; |
| if(tot+fragcount > want) |
| fragcount = want - tot; |
| if(tot == 0) |
| memmove(data, b->data+skip1, fragcount-skip1); |
| else |
| memmove(data+tot-skip1, b->data, fragcount); |
| blockput(b); |
| } |
| count = tot - skip1; |
| |
| *peof = (offset+count == 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; |
| } |
| |
| /* |
| * Ext2 is always little-endian, even on big-endian machines. |
| */ |
| |
| static u32int |
| l32(uchar *p) |
| { |
| return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); |
| } |
| |
| static u16int |
| l16(uchar *p) |
| { |
| return p[0] | (p[1]<<8); |
| } |
| |
| static u8int |
| l8(uchar *p) |
| { |
| return p[0]; |
| } |
| |
| static void |
| parsedirent(Dirent *de, uchar *p) |
| { |
| de->ino = l32(p); |
| de->reclen = l16(p+4); |
| de->namlen = l8(p+6); |
| /* 1 byte pad */ |
| de->name = (char*)p+8; |
| } |
| |
| static void |
| parseinode(Inode *ino, uchar *p) |
| { |
| int i; |
| |
| ino->mode = l16(p); |
| ino->uid = l16(p+2); |
| ino->size = l32(p+4); |
| ino->atime = l32(p+8); |
| ino->ctime = l32(p+12); |
| ino->mtime = l32(p+16); |
| ino->dtime = l32(p+20); |
| ino->gid = l16(p+24); |
| ino->nlink = l16(p+26); |
| ino->nblock = l32(p+28); |
| ino->flags = l32(p+32); |
| /* 4 byte osd1 */ |
| for(i=0; i<NBLOCKS; i++) |
| ino->block[i] = l32(p+40+i*4); |
| ino->version = l32(p+100); |
| ino->fileacl = l32(p+104); |
| ino->diracl = l32(p+108); |
| ino->faddr = l32(p+112); |
| /* 12 byte osd2 */ |
| } |
| |
| static void |
| parsegroup(Group *g, uchar *p) |
| { |
| g->bitblock = l32(p); |
| g->inodebitblock = l32(p+4); |
| g->inodeaddr = l32(p+8); |
| g->freeblockscount = l16(p+12); |
| g->freeinodescount = l16(p+14); |
| g->useddirscount = l16(p+16); |
| /* 2 byte pad */ |
| /* 12 byte reserved */ |
| } |
| |
| static void |
| parsesuper(Super *s, uchar *p) |
| { |
| s->ninode = l32(p); |
| s->nblock = l32(p+4); |
| s->rblockcount = l32(p+8); |
| s->freeblockcount = l32(p+12); |
| s->freeinodecount = l32(p+16); |
| s->firstdatablock = l32(p+20); |
| s->logblocksize = l32(p+24); |
| s->logfragsize = l32(p+28); |
| s->blockspergroup = l32(p+32); |
| s->fragpergroup = l32(p+36); |
| s->inospergroup = l32(p+40); |
| s->mtime = l32(p+44); |
| s->wtime = l32(p+48); |
| s->mntcount = l16(p+52); |
| s->maxmntcount = l16(p+54); |
| s->magic = l16(p+56); |
| s->state = l16(p+58); |
| s->errors = l16(p+60); |
| /* 2 byte pad */ |
| s->lastcheck = l32(p+64); |
| s->checkinterval = l32(p+68); |
| s->creatoros = l32(p+72); |
| s->revlevel = l32(p+76); |
| s->defresuid = l16(p+80); |
| s->defresgid = l16(p+82); |
| s->firstino = l32(p+84); |
| s->inosize = l32(p+88); |
| s->blockgroupnr = l16(p+60); |
| /* 932 byte reserved */ |
| } |