| #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; |
| } |