|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <thread.h> | 
|  | #include <sunrpc.h> | 
|  | #include <nfs3.h> | 
|  | #include <diskfs.h> | 
|  | #include "ffs.h" | 
|  |  | 
|  | #define BADBNO	((u64int)~0ULL) | 
|  |  | 
|  | #define checkcg 0 | 
|  | #define debug 0 | 
|  |  | 
|  | static int checkfsblk(Fsblk*); | 
|  | static int checkcgblk(Cgblk*); | 
|  | static Block *ffsblockread(Fsys*, u64int); | 
|  | static int ffssync(Fsys*); | 
|  | static void ffsclose(Fsys*); | 
|  |  | 
|  | static u64int ffsxfileblock(Fsys *fs, Nfs3Handle *h, u64int offset); | 
|  | static Nfs3Status ffsroot(Fsys*, Nfs3Handle*); | 
|  | static Nfs3Status ffsgetattr(Fsys*, SunAuthUnix *au, Nfs3Handle*, Nfs3Attr*); | 
|  | static Nfs3Status ffslookup(Fsys*, SunAuthUnix *au, Nfs3Handle*, char*, Nfs3Handle*); | 
|  | static Nfs3Status ffsreadfile(Fsys*, SunAuthUnix *au, Nfs3Handle*, u32int, u64int, uchar**, u32int*, u1int*); | 
|  | static Nfs3Status ffsreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link); | 
|  | static Nfs3Status ffsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int, u64int, uchar**, u32int*, u1int*); | 
|  | static Nfs3Status ffsaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr); | 
|  |  | 
|  | Fsys* | 
|  | fsysopenffs(Disk *disk) | 
|  | { | 
|  | Ffs *fs; | 
|  | Fsys *fsys; | 
|  |  | 
|  | fsys = emalloc(sizeof(Fsys)); | 
|  | fs = emalloc(sizeof(Ffs)); | 
|  | fs->disk = disk; | 
|  | fsys->priv = fs; | 
|  | fsys->type = "ffs"; | 
|  | fsys->_readblock = ffsblockread; | 
|  | fsys->_sync = ffssync; | 
|  | fsys->_root = ffsroot; | 
|  | fsys->_getattr = ffsgetattr; | 
|  | fsys->_access = ffsaccess; | 
|  | fsys->_lookup = ffslookup; | 
|  | fsys->_readfile = ffsreadfile; | 
|  | fsys->_readlink = ffsreadlink; | 
|  | fsys->_readdir = ffsreaddir; | 
|  | fsys->_close = ffsclose; | 
|  | fsys->fileblock = ffsxfileblock; | 
|  |  | 
|  | if(ffssync(fsys) < 0) | 
|  | goto error; | 
|  |  | 
|  | return fsys; | 
|  |  | 
|  | error: | 
|  | ffsclose(fsys); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | static Cgblk* | 
|  | ffscylgrp(Ffs *fs, u32int i, Block **pb) | 
|  | { | 
|  | Block *b; | 
|  | Cgblk *cg; | 
|  |  | 
|  | if(i >= fs->ncg) | 
|  | return nil; | 
|  |  | 
|  | b = diskread(fs->disk, fs->blocksize, (u64int)fs->cg[i].cgblkno*fs->blocksize); | 
|  | if(b == nil) | 
|  | return nil; | 
|  | cg = (Cgblk*)b->data; | 
|  | if(checkcgblk(cg) < 0){ | 
|  | fprint(2, "checkcgblk %d %lud: %r\n", i, (ulong)fs->cg[i].cgblkno); | 
|  | blockput(b); | 
|  | return nil; | 
|  | } | 
|  | *pb = b; | 
|  | return cg; | 
|  | } | 
|  |  | 
|  | static int | 
|  | ffssync(Fsys *fsys) | 
|  | { | 
|  | int i; | 
|  | int off[] = { SBOFF, SBOFF2, SBOFFPIGGY }; | 
|  | Block *b, *cgb; | 
|  | Cgblk *cgblk; | 
|  | Cylgrp *cg; | 
|  | Disk *disk; | 
|  | Ffs *fs; | 
|  | Fsblk *fsblk; | 
|  |  | 
|  | fs = fsys->priv; | 
|  | disk = fs->disk; | 
|  |  | 
|  | /* | 
|  | * Read super block. | 
|  | */ | 
|  | b = nil; | 
|  | for(i=0; i<nelem(off); i++){ | 
|  | if((b = diskread(disk, SBSIZE, off[i])) == nil) | 
|  | goto error; | 
|  | fsblk = (Fsblk*)b->data; | 
|  | //	fprint(2, "offset of magic: %ld\n", offsetof(Fsblk, magic)); | 
|  | if((fs->ufs = checkfsblk(fsblk)) > 0) | 
|  | goto okay; | 
|  | blockput(b); | 
|  | b = nil; | 
|  | } | 
|  | goto error; | 
|  |  | 
|  | okay: | 
|  | fs->blocksize = fsblk->blocksize; | 
|  | fs->nblock = (fsblk->nfrag+fsblk->fragsperblock-1) / fsblk->fragsperblock; | 
|  | fs->fragsize = fsblk->fragsize; | 
|  | fs->fragspergroup = fsblk->fragspergroup; | 
|  | fs->fragsperblock = fsblk->fragsperblock; | 
|  | fs->inosperblock = fsblk->inosperblock; | 
|  | fs->inospergroup = fsblk->inospergroup; | 
|  |  | 
|  | fs->nfrag = fsblk->nfrag; | 
|  | fs->ndfrag = fsblk->ndfrag; | 
|  | /* | 
|  | * used to use | 
|  | *	fs->blockspergroup = (u64int)fsblk->_cylspergroup * | 
|  | *		fsblk->secspercyl * BYTESPERSEC / fsblk->blocksize; | 
|  | * for UFS1, but this should work for both UFS1 and UFS2 | 
|  | */ | 
|  | fs->blockspergroup = (u64int)fsblk->fragspergroup / fsblk->fragsperblock; | 
|  | fs->ncg = fsblk->ncg; | 
|  |  | 
|  | fsys->blocksize = fs->blocksize; | 
|  | fsys->nblock = fs->nblock; | 
|  |  | 
|  | if(debug) fprint(2, "ffs %lld %d-byte blocks, %d cylinder groups\n", | 
|  | fs->nblock, fs->blocksize, fs->ncg); | 
|  | if(debug) fprint(2, "\tinospergroup %d perblock %d blockspergroup %lld\n", | 
|  | fs->inospergroup, fs->inosperblock, fs->blockspergroup); | 
|  |  | 
|  | if(fs->cg == nil) | 
|  | fs->cg = emalloc(fs->ncg*sizeof(Cylgrp)); | 
|  | for(i=0; i<fs->ncg; i++){ | 
|  | cg = &fs->cg[i]; | 
|  | if(fs->ufs == 2) | 
|  | cg->bno = (u64int)fs->blockspergroup*i; | 
|  | else | 
|  | cg->bno = fs->blockspergroup*i + fsblk->_cgoffset * (i & ~fsblk->_cgmask); | 
|  | cg->cgblkno = cg->bno + fsblk->cfragno/fs->fragsperblock; | 
|  | cg->ibno = cg->bno + fsblk->ifragno/fs->fragsperblock; | 
|  | cg->dbno = cg->bno + fsblk->dfragno/fs->fragsperblock; | 
|  |  | 
|  | if(checkcg){ | 
|  | if((cgb = diskread(disk, fs->blocksize, (u64int)cg->cgblkno*fs->blocksize)) == nil) | 
|  | goto error; | 
|  |  | 
|  | cgblk = (Cgblk*)cgb->data; | 
|  | if(checkcgblk(cgblk) < 0){ | 
|  | blockput(cgb); | 
|  | goto error; | 
|  | } | 
|  | if(cgblk->nfrag % fs->fragsperblock && i != fs->ncg-1){ | 
|  | werrstr("fractional number of blocks in non-last cylinder group %d", cgblk->nfrag); | 
|  | blockput(cgb); | 
|  | goto error; | 
|  | } | 
|  | /* cg->nfrag = cgblk->nfrag; */ | 
|  | /* cg->nblock = (cgblk->nfrag+fs->fragsperblock-1) / fs->fragsperblock; */ | 
|  | /* fprint(2, "cg #%d: cgblk %lud, %d blocks, %d inodes\n", cgblk->num, (ulong)cg->cgblkno, cg->nblock, cg->nino); */ | 
|  | } | 
|  | } | 
|  | blockput(b); | 
|  | return 0; | 
|  |  | 
|  | error: | 
|  | blockput(b); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | ffsclose(Fsys *fsys) | 
|  | { | 
|  | Ffs *fs; | 
|  |  | 
|  | fs = fsys->priv; | 
|  | if(fs->cg) | 
|  | free(fs->cg); | 
|  | free(fs); | 
|  | free(fsys); | 
|  | } | 
|  |  | 
|  | static int | 
|  | checkfsblk(Fsblk *super) | 
|  | { | 
|  | // fprint(2, "ffs magic 0x%ux\n", super->magic); | 
|  | if(super->magic == FSMAGIC){ | 
|  | super->time = super->_time; | 
|  | super->nfrag = super->_nfrag; | 
|  | super->ndfrag = super->_ndfrag; | 
|  | super->flags = super->_flags; | 
|  | return 1; | 
|  | } | 
|  | if(super->magic == FSMAGIC2){ | 
|  | return 2; | 
|  | } | 
|  |  | 
|  | werrstr("bad super block"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | checkcgblk(Cgblk *cg) | 
|  | { | 
|  | if(cg->magic != CGMAGIC){ | 
|  | werrstr("bad cylinder group block"); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Read block #bno from the disk, zeroing unused data. | 
|  | * If there is no data whatsoever, it's okay to return nil. | 
|  | */ | 
|  | int nskipx; | 
|  | static Block* | 
|  | ffsblockread(Fsys *fsys, u64int bno) | 
|  | { | 
|  | int i, o; | 
|  | u8int *fmap; | 
|  | int frag, fsize, avail; | 
|  | Block *b; | 
|  | Cgblk *cgblk; | 
|  | Ffs *fs; | 
|  |  | 
|  | fs = fsys->priv; | 
|  | i = bno / fs->blockspergroup; | 
|  | o = bno % fs->blockspergroup; | 
|  | if(i >= fs->ncg) | 
|  | return nil; | 
|  |  | 
|  | if((cgblk = ffscylgrp(fs, i, &b)) == nil) | 
|  | return nil; | 
|  |  | 
|  | fmap = (u8int*)cgblk+cgblk->fmapoff; | 
|  | frag = fs->fragsperblock; | 
|  | switch(frag){ | 
|  | default: | 
|  | sysfatal("bad frag"); | 
|  | case 8: | 
|  | avail = fmap[o]; | 
|  | break; | 
|  | case 4: | 
|  | avail = (fmap[o>>1] >> ((o&1)*4)) & 0xF; | 
|  | break; | 
|  | case 2: | 
|  | avail = (fmap[o>>2] >> ((o&3)*2)) & 0x3; | 
|  | break; | 
|  | case 1: | 
|  | avail = (fmap[o>>3] >> (o&7)) & 0x1; | 
|  | break; | 
|  | } | 
|  | blockput(b); | 
|  |  | 
|  | if(avail == ((1<<frag)-1)) | 
|  | { | 
|  | nskipx++; | 
|  | return nil; | 
|  | } | 
|  | if((b = diskread(fs->disk, fs->blocksize, bno*fs->blocksize)) == nil){ | 
|  | fprint(2, "diskread failed!!!\n"); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | fsize = fs->fragsize; | 
|  | for(i=0; i<frag; i++) | 
|  | if(avail & (1<<i)) | 
|  | memset(b->data + fsize*i, 0, fsize); | 
|  | return b; | 
|  | } | 
|  |  | 
|  | static Block* | 
|  | ffsdatablock(Ffs *fs, u64int bno, int size) | 
|  | { | 
|  | int fsize; | 
|  | u64int diskaddr; | 
|  | Block *b; | 
|  |  | 
|  | if(bno == 0) | 
|  | return nil; | 
|  |  | 
|  | fsize = size; | 
|  | if(fsize < fs->fragsize) | 
|  | fsize = fs->fragsize; | 
|  |  | 
|  | if(bno >= fs->nfrag){ | 
|  | fprint(2, "ffs: request for block %#lux; nfrag %#llux\n", (ulong)bno, fs->nfrag); | 
|  | return nil; | 
|  | } | 
|  | diskaddr = (u64int)bno*fs->fragsize; | 
|  | b = diskread(fs->disk, fsize, diskaddr); | 
|  | if(b == nil){ | 
|  | fprint(2, "ffs: disk i/o at %#llux for %#ux: %r\n", diskaddr, fsize); | 
|  | return nil; | 
|  | } | 
|  | if(b->len < fsize){ | 
|  | fprint(2, "ffs: disk i/o at %#llux for %#ux got %#ux\n", diskaddr, fsize, | 
|  | b->len); | 
|  | blockput(b); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | return b; | 
|  | } | 
|  |  | 
|  | static u64int | 
|  | ifetch(Ffs *fs, u64int bno, u32int off) | 
|  | { | 
|  | Block *b; | 
|  |  | 
|  | if(bno == BADBNO) | 
|  | return BADBNO; | 
|  | b = ffsdatablock(fs, bno, fs->blocksize); | 
|  | if(b == nil) | 
|  | return BADBNO; | 
|  | if(fs->ufs == 2) | 
|  | bno = ((u64int*)b->data)[off]; | 
|  | else | 
|  | bno = ((u32int*)b->data)[off]; | 
|  | blockput(b); | 
|  | return bno; | 
|  | } | 
|  |  | 
|  | static u64int | 
|  | ffsfileblockno(Ffs *fs, Inode *ino, u64int bno) | 
|  | { | 
|  | int ppb; | 
|  |  | 
|  | if(bno < NDADDR){ | 
|  | if(debug) fprint(2, "ffsfileblock %lud: direct %#lux\n", (ulong)bno, (ulong)ino->db[bno]); | 
|  | return ino->db[bno]; | 
|  | } | 
|  | bno -= NDADDR; | 
|  | ppb = fs->blocksize/4; | 
|  |  | 
|  | if(bno < ppb)	/* single indirect */ | 
|  | return ifetch(fs, ino->ib[0], bno); | 
|  | bno -= ppb; | 
|  |  | 
|  | if(bno < ppb*ppb) | 
|  | return ifetch(fs, ifetch(fs, ino->ib[1], bno/ppb), bno%ppb); | 
|  | bno -= ppb*ppb; | 
|  |  | 
|  | if(bno/ppb/ppb/ppb == 0)	/* bno < ppb*ppb*ppb w/o overflow */ | 
|  | return ifetch(fs, ifetch(fs, ifetch(fs, ino->ib[2], bno/ppb/ppb), (bno/ppb)%ppb), bno%ppb); | 
|  |  | 
|  | fprint(2, "ffsfileblock %llud: way too big\n", bno+NDADDR+ppb+ppb*ppb); | 
|  | return BADBNO; | 
|  | } | 
|  |  | 
|  | static Block* | 
|  | ffsfileblock(Ffs *fs, Inode *ino, u64int bno, int size) | 
|  | { | 
|  | u64int b; | 
|  |  | 
|  | b = ffsfileblockno(fs, ino, bno); | 
|  | if(b == ~0) | 
|  | return nil; | 
|  | return ffsdatablock(fs, b, size); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * NFS handles are 4-byte inode number. | 
|  | */ | 
|  | 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 u64int lastiaddr;	/* debugging */ | 
|  |  | 
|  | static void | 
|  | inode1to2(Inode1 *i1, Inode *i2) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | memset(i2, 0, sizeof *i2); | 
|  | i2->mode = i1->mode; | 
|  | i2->nlink = i1->nlink; | 
|  | i2->size = i1->size; | 
|  | i2->atime = i1->atime; | 
|  | i2->atimensec = i1->atimensec; | 
|  | i2->mtime = i1->mtime; | 
|  | i2->mtimensec = i1->mtimensec; | 
|  | i2->ctime = i1->ctime; | 
|  | i2->ctimensec = i1->ctimensec; | 
|  | for(i=0; i<NDADDR; i++) | 
|  | i2->db[i] = i1->db[i]; | 
|  | for(i=0; i<NIADDR; i++) | 
|  | i2->ib[i] = i1->ib[i]; | 
|  | i2->flags = i1->flags; | 
|  | i2->nblock = i1->nblock; | 
|  | i2->gen = i1->gen; | 
|  | i2->uid = i1->uid; | 
|  | i2->gid = i1->gid; | 
|  | } | 
|  |  | 
|  | static Nfs3Status | 
|  | handle2ino(Ffs *fs, Nfs3Handle *h, u32int *pinum, Inode *ino) | 
|  | { | 
|  | int i; | 
|  | u32int ioff; | 
|  | u32int inum; | 
|  | u64int iaddr; | 
|  | Block *b; | 
|  | Cylgrp *cg; | 
|  | Inode1 ino1; | 
|  |  | 
|  | if(h->len != 4) | 
|  | return Nfs3ErrBadHandle; | 
|  | inum = byte2u32(h->h); | 
|  | if(pinum) | 
|  | *pinum = inum; | 
|  | if(debug) print("inum %d...", (int)inum); | 
|  |  | 
|  | /* fetch inode from disk */ | 
|  | i = inum / fs->inospergroup; | 
|  | ioff = inum % fs->inospergroup; | 
|  | if(debug)print("cg %d off %d...", i, (int)ioff); | 
|  | if(i >= fs->ncg) | 
|  | return Nfs3ErrBadHandle; | 
|  | cg = &fs->cg[i]; | 
|  |  | 
|  | if(debug) print("cg->ibno %lld ufs %d...", cg->ibno, fs->ufs); | 
|  | iaddr = (cg->ibno+ioff/fs->inosperblock)*(vlong)fs->blocksize; | 
|  | ioff = ioff%fs->inosperblock; | 
|  | if((b = diskread(fs->disk, fs->blocksize, iaddr)) == nil) | 
|  | return Nfs3ErrIo; | 
|  | if(fs->ufs == 2){ | 
|  | *ino = ((Inode*)b->data)[ioff]; | 
|  | lastiaddr = iaddr+ioff*sizeof(Inode); | 
|  | }else{ | 
|  | ino1 = ((Inode1*)b->data)[ioff]; | 
|  | inode1to2(&ino1, ino); | 
|  | lastiaddr = iaddr+ioff*sizeof(Inode1); | 
|  | } | 
|  | blockput(b); | 
|  | return Nfs3Ok; | 
|  | } | 
|  |  | 
|  | static Nfs3Status | 
|  | ffsroot(Fsys *fsys, Nfs3Handle *h) | 
|  | { | 
|  | USED(fsys); | 
|  | mkhandle(h, 2); | 
|  | return Nfs3Ok; | 
|  | } | 
|  |  | 
|  | static Nfs3Status | 
|  | ino2attr(Ffs *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->db[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 = ino->atimensec; | 
|  | attr->mtime.sec = ino->mtime; | 
|  | attr->mtime.nsec = ino->mtimensec; | 
|  | attr->ctime.sec = ino->ctime; | 
|  | attr->ctime.nsec = ino->ctimensec; | 
|  | 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(au == nil) | 
|  | 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 | 
|  | ffsgetattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr) | 
|  | { | 
|  | Inode ino; | 
|  | u32int inum; | 
|  | Ffs *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 | 
|  | ffsaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr) | 
|  | { | 
|  | int have; | 
|  | Inode ino; | 
|  | u32int inum; | 
|  | Ffs *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 | 
|  | ffslookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh) | 
|  | { | 
|  | u32int nblock; | 
|  | u32int i; | 
|  | uchar *p, *ep; | 
|  | Dirent *de; | 
|  | Inode ino; | 
|  | Block *b; | 
|  | Ffs *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; | 
|  | for(i=0; i<nblock; i++){ | 
|  | if(i==nblock-1) | 
|  | want = ino.size % fs->blocksize; | 
|  | else | 
|  | want = fs->blocksize; | 
|  | b = ffsfileblock(fs, &ino, i, want); | 
|  | if(b == nil) | 
|  | 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 | 
|  | ffsreaddir(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; | 
|  | Ffs *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){ | 
|  | *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; | 
|  | done = 0; | 
|  | for(; i<nblock && !done; i++){ | 
|  | if(i==nblock-1) | 
|  | want = ino.size % fs->blocksize; | 
|  | else | 
|  | want = fs->blocksize; | 
|  | b = ffsfileblock(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.namelen = de->namlen; | 
|  | 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 u64int | 
|  | ffsxfileblock(Fsys *fsys, Nfs3Handle *h, u64int offset) | 
|  | { | 
|  | u64int bno; | 
|  | Inode ino; | 
|  | Nfs3Status ok; | 
|  | Ffs *fs; | 
|  |  | 
|  | fs = fsys->priv; | 
|  | if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok){ | 
|  | nfs3errstr(ok); | 
|  | return 0; | 
|  | } | 
|  | if(offset == 1)	/* clumsy hack for debugging */ | 
|  | return lastiaddr; | 
|  | if(offset >= ino.size){ | 
|  | werrstr("beyond end of file"); | 
|  | return 0; | 
|  | } | 
|  | bno = offset/fs->blocksize; | 
|  | bno = ffsfileblockno(fs, &ino, bno); | 
|  | if(bno == ~0) | 
|  | return 0; | 
|  | return bno*(u64int)fs->fragsize; | 
|  | } | 
|  |  | 
|  | static Nfs3Status | 
|  | ffsreadfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, | 
|  | u64int offset, uchar **pdata, u32int *pcount, u1int *peof) | 
|  | { | 
|  | uchar *data; | 
|  | Block *b; | 
|  | Ffs *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->fragsize) | 
|  | want += fs->fragsize - want%fs->fragsize; | 
|  |  | 
|  | b = ffsfileblock(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 | 
|  | ffsreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link) | 
|  | { | 
|  | Ffs *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){ | 
|  | /* assumes symlink fits in one block */ | 
|  | b = ffsfileblock(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.db + sizeof ino.ib) | 
|  | return Nfs3ErrIo; | 
|  |  | 
|  | *link = malloc(len+1); | 
|  | if(*link == 0) | 
|  | return Nfs3ErrNoMem; | 
|  | memmove(*link, ino.db, ino.size); | 
|  | (*link)[len] = 0; | 
|  | return Nfs3Ok; | 
|  | } |