tffs.c - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       tffs.c (19246B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <thread.h>
            4 #include <sunrpc.h>
            5 #include <nfs3.h>
            6 #include <diskfs.h>
            7 #include "ffs.h"
            8 
            9 #define BADBNO        ((u64int)~0ULL)
           10 
           11 #define checkcg 0
           12 #define debug 0
           13 
           14 static int checkfsblk(Fsblk*);
           15 static int checkcgblk(Cgblk*);
           16 static Block *ffsblockread(Fsys*, u64int);
           17 static int ffssync(Fsys*);
           18 static void ffsclose(Fsys*);
           19 
           20 static u64int ffsxfileblock(Fsys *fs, Nfs3Handle *h, u64int offset);
           21 static Nfs3Status ffsroot(Fsys*, Nfs3Handle*);
           22 static Nfs3Status ffsgetattr(Fsys*, SunAuthUnix *au, Nfs3Handle*, Nfs3Attr*);
           23 static Nfs3Status ffslookup(Fsys*, SunAuthUnix *au, Nfs3Handle*, char*, Nfs3Handle*);
           24 static Nfs3Status ffsreadfile(Fsys*, SunAuthUnix *au, Nfs3Handle*, u32int, u64int, uchar**, u32int*, u1int*);
           25 static Nfs3Status ffsreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link);
           26 static Nfs3Status ffsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int, u64int, uchar**, u32int*, u1int*);
           27 static Nfs3Status ffsaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr);
           28 
           29 Fsys*
           30 fsysopenffs(Disk *disk)
           31 {
           32         Ffs *fs;
           33         Fsys *fsys;
           34 
           35         fsys = emalloc(sizeof(Fsys));
           36         fs = emalloc(sizeof(Ffs));
           37         fs->disk = disk;
           38         fsys->priv = fs;
           39         fsys->type = "ffs";
           40         fsys->_readblock = ffsblockread;
           41         fsys->_sync = ffssync;
           42         fsys->_root = ffsroot;
           43         fsys->_getattr = ffsgetattr;
           44         fsys->_access = ffsaccess;
           45         fsys->_lookup = ffslookup;
           46         fsys->_readfile = ffsreadfile;
           47         fsys->_readlink = ffsreadlink;
           48         fsys->_readdir = ffsreaddir;
           49         fsys->_close = ffsclose;
           50         fsys->fileblock = ffsxfileblock;
           51 
           52         if(ffssync(fsys) < 0)
           53                 goto error;
           54 
           55         return fsys;
           56 
           57 error:
           58         ffsclose(fsys);
           59         return nil;
           60 }
           61 
           62 static Cgblk*
           63 ffscylgrp(Ffs *fs, u32int i, Block **pb)
           64 {
           65         Block *b;
           66         Cgblk *cg;
           67 
           68         if(i >= fs->ncg)
           69                 return nil;
           70 
           71         b = diskread(fs->disk, fs->blocksize, (u64int)fs->cg[i].cgblkno*fs->blocksize);
           72         if(b == nil)
           73                 return nil;
           74         cg = (Cgblk*)b->data;
           75         if(checkcgblk(cg) < 0){
           76 fprint(2, "checkcgblk %d %lud: %r\n", i, (ulong)fs->cg[i].cgblkno);
           77                 blockput(b);
           78                 return nil;
           79         }
           80         *pb = b;
           81         return cg;
           82 }
           83 
           84 static int
           85 ffssync(Fsys *fsys)
           86 {
           87         int i;
           88         int off[] = { SBOFF, SBOFF2, SBOFFPIGGY };
           89         Block *b, *cgb;
           90         Cgblk *cgblk;
           91         Cylgrp *cg;
           92         Disk *disk;
           93         Ffs *fs;
           94         Fsblk *fsblk;
           95 
           96         fs = fsys->priv;
           97         disk = fs->disk;
           98 
           99         /*
          100          * Read super block.
          101          */
          102         b = nil;
          103         for(i=0; i<nelem(off); i++){
          104                 if((b = diskread(disk, SBSIZE, off[i])) == nil)
          105                         goto error;
          106                 fsblk = (Fsblk*)b->data;
          107         //        fprint(2, "offset of magic: %ld\n", offsetof(Fsblk, magic));
          108                 if((fs->ufs = checkfsblk(fsblk)) > 0)
          109                         goto okay;
          110                 blockput(b);
          111                 b = nil;
          112         }
          113         goto error;
          114 
          115 okay:
          116         fs->blocksize = fsblk->blocksize;
          117         fs->nblock = (fsblk->nfrag+fsblk->fragsperblock-1) / fsblk->fragsperblock;
          118         fs->fragsize = fsblk->fragsize;
          119         fs->fragspergroup = fsblk->fragspergroup;
          120         fs->fragsperblock = fsblk->fragsperblock;
          121         fs->inosperblock = fsblk->inosperblock;
          122         fs->inospergroup = fsblk->inospergroup;
          123 
          124         fs->nfrag = fsblk->nfrag;
          125         fs->ndfrag = fsblk->ndfrag;
          126         /*
          127          * used to use
          128          *        fs->blockspergroup = (u64int)fsblk->_cylspergroup *
          129          *                fsblk->secspercyl * BYTESPERSEC / fsblk->blocksize;
          130          * for UFS1, but this should work for both UFS1 and UFS2
          131          */
          132         fs->blockspergroup = (u64int)fsblk->fragspergroup / fsblk->fragsperblock;
          133         fs->ncg = fsblk->ncg;
          134 
          135         fsys->blocksize = fs->blocksize;
          136         fsys->nblock = fs->nblock;
          137 
          138         if(debug) fprint(2, "ffs %lld %d-byte blocks, %d cylinder groups\n",
          139                 fs->nblock, fs->blocksize, fs->ncg);
          140         if(debug) fprint(2, "\tinospergroup %d perblock %d blockspergroup %lld\n",
          141                 fs->inospergroup, fs->inosperblock, fs->blockspergroup);
          142 
          143         if(fs->cg == nil)
          144                 fs->cg = emalloc(fs->ncg*sizeof(Cylgrp));
          145         for(i=0; i<fs->ncg; i++){
          146                 cg = &fs->cg[i];
          147                 if(fs->ufs == 2)
          148                         cg->bno = (u64int)fs->blockspergroup*i;
          149                 else
          150                         cg->bno = fs->blockspergroup*i + fsblk->_cgoffset * (i & ~fsblk->_cgmask);
          151                 cg->cgblkno = cg->bno + fsblk->cfragno/fs->fragsperblock;
          152                 cg->ibno = cg->bno + fsblk->ifragno/fs->fragsperblock;
          153                 cg->dbno = cg->bno + fsblk->dfragno/fs->fragsperblock;
          154 
          155                 if(checkcg){
          156                         if((cgb = diskread(disk, fs->blocksize, (u64int)cg->cgblkno*fs->blocksize)) == nil)
          157                                 goto error;
          158 
          159                         cgblk = (Cgblk*)cgb->data;
          160                         if(checkcgblk(cgblk) < 0){
          161                                 blockput(cgb);
          162                                 goto error;
          163                         }
          164                         if(cgblk->nfrag % fs->fragsperblock && i != fs->ncg-1){
          165                                 werrstr("fractional number of blocks in non-last cylinder group %d", cgblk->nfrag);
          166                                 blockput(cgb);
          167                                 goto error;
          168                         }
          169                         /* cg->nfrag = cgblk->nfrag; */
          170                         /* cg->nblock = (cgblk->nfrag+fs->fragsperblock-1) / fs->fragsperblock; */
          171                         /* fprint(2, "cg #%d: cgblk %lud, %d blocks, %d inodes\n", cgblk->num, (ulong)cg->cgblkno, cg->nblock, cg->nino); */
          172                 }
          173         }
          174         blockput(b);
          175         return 0;
          176 
          177 error:
          178         blockput(b);
          179         return -1;
          180 }
          181 
          182 static void
          183 ffsclose(Fsys *fsys)
          184 {
          185         Ffs *fs;
          186 
          187         fs = fsys->priv;
          188         if(fs->cg)
          189                 free(fs->cg);
          190         free(fs);
          191         free(fsys);
          192 }
          193 
          194 static int
          195 checkfsblk(Fsblk *super)
          196 {
          197 // fprint(2, "ffs magic 0x%ux\n", super->magic);
          198         if(super->magic == FSMAGIC){
          199                 super->time = super->_time;
          200                 super->nfrag = super->_nfrag;
          201                 super->ndfrag = super->_ndfrag;
          202                 super->flags = super->_flags;
          203                 return 1;
          204         }
          205         if(super->magic == FSMAGIC2){
          206                 return 2;
          207         }
          208 
          209         werrstr("bad super block");
          210         return -1;
          211 }
          212 
          213 static int
          214 checkcgblk(Cgblk *cg)
          215 {
          216         if(cg->magic != CGMAGIC){
          217                 werrstr("bad cylinder group block");
          218                 return -1;
          219         }
          220         return 0;
          221 }
          222 
          223 /*
          224  * Read block #bno from the disk, zeroing unused data.
          225  * If there is no data whatsoever, it's okay to return nil.
          226  */
          227 int nskipx;
          228 static Block*
          229 ffsblockread(Fsys *fsys, u64int bno)
          230 {
          231         int i, o;
          232         u8int *fmap;
          233         int frag, fsize, avail;
          234         Block *b;
          235         Cgblk *cgblk;
          236         Ffs *fs;
          237 
          238         fs = fsys->priv;
          239         i = bno / fs->blockspergroup;
          240         o = bno % fs->blockspergroup;
          241         if(i >= fs->ncg)
          242                 return nil;
          243 
          244         if((cgblk = ffscylgrp(fs, i, &b)) == nil)
          245                 return nil;
          246 
          247         fmap = (u8int*)cgblk+cgblk->fmapoff;
          248         frag = fs->fragsperblock;
          249         switch(frag){
          250         default:
          251                 sysfatal("bad frag");
          252         case 8:
          253                 avail = fmap[o];
          254                 break;
          255         case 4:
          256                 avail = (fmap[o>>1] >> ((o&1)*4)) & 0xF;
          257                 break;
          258         case 2:
          259                 avail = (fmap[o>>2] >> ((o&3)*2)) & 0x3;
          260                 break;
          261         case 1:
          262                 avail = (fmap[o>>3] >> (o&7)) & 0x1;
          263                 break;
          264         }
          265         blockput(b);
          266 
          267         if(avail == ((1<<frag)-1))
          268 {
          269 nskipx++;
          270                 return nil;
          271 }
          272         if((b = diskread(fs->disk, fs->blocksize, bno*fs->blocksize)) == nil){
          273                 fprint(2, "diskread failed!!!\n");
          274                 return nil;
          275         }
          276 
          277         fsize = fs->fragsize;
          278         for(i=0; i<frag; i++)
          279                 if(avail & (1<<i))
          280                         memset(b->data + fsize*i, 0, fsize);
          281         return b;
          282 }
          283 
          284 static Block*
          285 ffsdatablock(Ffs *fs, u64int bno, int size)
          286 {
          287         int fsize;
          288         u64int diskaddr;
          289         Block *b;
          290 
          291         if(bno == 0)
          292                 return nil;
          293 
          294         fsize = size;
          295         if(fsize < fs->fragsize)
          296                 fsize = fs->fragsize;
          297 
          298         if(bno >= fs->nfrag){
          299                 fprint(2, "ffs: request for block %#lux; nfrag %#llux\n", (ulong)bno, fs->nfrag);
          300                 return nil;
          301         }
          302         diskaddr = (u64int)bno*fs->fragsize;
          303         b = diskread(fs->disk, fsize, diskaddr);
          304         if(b == nil){
          305                 fprint(2, "ffs: disk i/o at %#llux for %#ux: %r\n", diskaddr, fsize);
          306                 return nil;
          307         }
          308         if(b->len < fsize){
          309                 fprint(2, "ffs: disk i/o at %#llux for %#ux got %#ux\n", diskaddr, fsize,
          310                         b->len);
          311                 blockput(b);
          312                 return nil;
          313         }
          314 
          315         return b;
          316 }
          317 
          318 static u64int
          319 ifetch(Ffs *fs, u64int bno, u32int off)
          320 {
          321         Block *b;
          322 
          323         if(bno == BADBNO)
          324                 return BADBNO;
          325         b = ffsdatablock(fs, bno, fs->blocksize);
          326         if(b == nil)
          327                 return BADBNO;
          328         if(fs->ufs == 2)
          329                 bno = ((u64int*)b->data)[off];
          330         else
          331                 bno = ((u32int*)b->data)[off];
          332         blockput(b);
          333         return bno;
          334 }
          335 
          336 static u64int
          337 ffsfileblockno(Ffs *fs, Inode *ino, u64int bno)
          338 {
          339         int ppb;
          340 
          341         if(bno < NDADDR){
          342                 if(debug) fprint(2, "ffsfileblock %lud: direct %#lux\n", (ulong)bno, (ulong)ino->db[bno]);
          343                 return ino->db[bno];
          344         }
          345         bno -= NDADDR;
          346         ppb = fs->blocksize/4;
          347 
          348         if(bno < ppb)        /* single indirect */
          349                 return ifetch(fs, ino->ib[0], bno);
          350         bno -= ppb;
          351 
          352         if(bno < ppb*ppb)
          353                 return ifetch(fs, ifetch(fs, ino->ib[1], bno/ppb), bno%ppb);
          354         bno -= ppb*ppb;
          355 
          356         if(bno/ppb/ppb/ppb == 0)        /* bno < ppb*ppb*ppb w/o overflow */
          357                 return ifetch(fs, ifetch(fs, ifetch(fs, ino->ib[2], bno/ppb/ppb), (bno/ppb)%ppb), bno%ppb);
          358 
          359         fprint(2, "ffsfileblock %llud: way too big\n", bno+NDADDR+ppb+ppb*ppb);
          360         return BADBNO;
          361 }
          362 
          363 static Block*
          364 ffsfileblock(Ffs *fs, Inode *ino, u64int bno, int size)
          365 {
          366         u64int b;
          367 
          368         b = ffsfileblockno(fs, ino, bno);
          369         if(b == ~0)
          370                 return nil;
          371         return ffsdatablock(fs, b, size);
          372 }
          373 
          374 /*
          375  * NFS handles are 4-byte inode number.
          376  */
          377 static void
          378 mkhandle(Nfs3Handle *h, u64int ino)
          379 {
          380         h->h[0] = ino >> 24;
          381         h->h[1] = ino >> 16;
          382         h->h[2] = ino >> 8;
          383         h->h[3] = ino;
          384         h->len = 4;
          385 }
          386 
          387 static u32int
          388 byte2u32(uchar *p)
          389 {
          390         return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
          391 }
          392 
          393 static u64int lastiaddr;        /* debugging */
          394 
          395 static void
          396 inode1to2(Inode1 *i1, Inode *i2)
          397 {
          398         int i;
          399 
          400         memset(i2, 0, sizeof *i2);
          401         i2->mode = i1->mode;
          402         i2->nlink = i1->nlink;
          403         i2->size = i1->size;
          404         i2->atime = i1->atime;
          405         i2->atimensec = i1->atimensec;
          406         i2->mtime = i1->mtime;
          407         i2->mtimensec = i1->mtimensec;
          408         i2->ctime = i1->ctime;
          409         i2->ctimensec = i1->ctimensec;
          410         for(i=0; i<NDADDR; i++)
          411                 i2->db[i] = i1->db[i];
          412         for(i=0; i<NIADDR; i++)
          413                 i2->ib[i] = i1->ib[i];
          414         i2->flags = i1->flags;
          415         i2->nblock = i1->nblock;
          416         i2->gen = i1->gen;
          417         i2->uid = i1->uid;
          418         i2->gid = i1->gid;
          419 }
          420 
          421 static Nfs3Status
          422 handle2ino(Ffs *fs, Nfs3Handle *h, u32int *pinum, Inode *ino)
          423 {
          424         int i;
          425         u32int ioff;
          426         u32int inum;
          427         u64int iaddr;
          428         Block *b;
          429         Cylgrp *cg;
          430         Inode1 ino1;
          431 
          432         if(h->len != 4)
          433                 return Nfs3ErrBadHandle;
          434         inum = byte2u32(h->h);
          435         if(pinum)
          436                 *pinum = inum;
          437         if(debug) print("inum %d...", (int)inum);
          438 
          439         /* fetch inode from disk */
          440         i = inum / fs->inospergroup;
          441         ioff = inum % fs->inospergroup;
          442         if(debug)print("cg %d off %d...", i, (int)ioff);
          443         if(i >= fs->ncg)
          444                 return Nfs3ErrBadHandle;
          445         cg = &fs->cg[i];
          446 
          447         if(debug) print("cg->ibno %lld ufs %d...", cg->ibno, fs->ufs);
          448         iaddr = (cg->ibno+ioff/fs->inosperblock)*(vlong)fs->blocksize;
          449         ioff = ioff%fs->inosperblock;
          450         if((b = diskread(fs->disk, fs->blocksize, iaddr)) == nil)
          451                 return Nfs3ErrIo;
          452         if(fs->ufs == 2){
          453                 *ino = ((Inode*)b->data)[ioff];
          454                 lastiaddr = iaddr+ioff*sizeof(Inode);
          455         }else{
          456                 ino1 = ((Inode1*)b->data)[ioff];
          457                 inode1to2(&ino1, ino);
          458                 lastiaddr = iaddr+ioff*sizeof(Inode1);
          459         }
          460         blockput(b);
          461         return Nfs3Ok;
          462 }
          463 
          464 static Nfs3Status
          465 ffsroot(Fsys *fsys, Nfs3Handle *h)
          466 {
          467         USED(fsys);
          468         mkhandle(h, 2);
          469         return Nfs3Ok;
          470 }
          471 
          472 static Nfs3Status
          473 ino2attr(Ffs *fs, Inode *ino, u32int inum, Nfs3Attr *attr)
          474 {
          475         u32int rdev;
          476 
          477         attr->type = -1;
          478         switch(ino->mode&IFMT){
          479         case IFIFO:
          480                 attr->type = Nfs3FileFifo;
          481                 break;
          482         case IFCHR:
          483                 attr->type = Nfs3FileChar;
          484                 break;
          485         case IFDIR:
          486                 attr->type = Nfs3FileDir;
          487                 break;
          488         case IFBLK:
          489                 attr->type = Nfs3FileBlock;
          490                 break;
          491         case IFREG:
          492                 attr->type = Nfs3FileReg;
          493                 break;
          494         case IFLNK:
          495                 attr->type = Nfs3FileSymlink;
          496                 break;
          497         case IFSOCK:
          498                 attr->type = Nfs3FileSocket;
          499                 break;
          500         case IFWHT:
          501         default:
          502                 return Nfs3ErrBadHandle;
          503         }
          504 
          505         attr->mode = ino->mode&07777;
          506         attr->nlink = ino->nlink;
          507         attr->uid = ino->uid;
          508         attr->gid = ino->gid;
          509         attr->size = ino->size;
          510         attr->used = ino->nblock*fs->blocksize;
          511         if(attr->type==Nfs3FileBlock || attr->type==Nfs3FileChar){
          512                 rdev = ino->db[0];
          513                 attr->major = (rdev>>8)&0xFF;
          514                 attr->minor = rdev & 0xFFFF00FF;
          515         }else{
          516                 attr->major = 0;
          517                 attr->minor = 0;
          518         }
          519         attr->fsid = 0;
          520         attr->fileid = inum;
          521         attr->atime.sec = ino->atime;
          522         attr->atime.nsec = ino->atimensec;
          523         attr->mtime.sec = ino->mtime;
          524         attr->mtime.nsec = ino->mtimensec;
          525         attr->ctime.sec = ino->ctime;
          526         attr->ctime.nsec = ino->ctimensec;
          527         return Nfs3Ok;
          528 }
          529 
          530 static int
          531 ingroup(SunAuthUnix *au, uint gid)
          532 {
          533         int i;
          534 
          535         for(i=0; i<au->ng; i++)
          536                 if(au->g[i] == gid)
          537                         return 1;
          538         return 0;
          539 }
          540 
          541 static Nfs3Status
          542 inoperm(Inode *ino, SunAuthUnix *au, int need)
          543 {
          544         int have;
          545 
          546         if(au == nil)
          547                 return Nfs3Ok;
          548 
          549         have = ino->mode&0777;
          550         if(ino->uid == au->uid)
          551                 have >>= 6;
          552         else if(ino->gid == au->gid || ingroup(au, ino->gid))
          553                 have >>= 3;
          554 
          555         if((have&need) != need)
          556                 return Nfs3ErrNotOwner;        /* really EPERM */
          557         return Nfs3Ok;
          558 }
          559 
          560 static Nfs3Status
          561 ffsgetattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
          562 {
          563         Inode ino;
          564         u32int inum;
          565         Ffs *fs;
          566         Nfs3Status ok;
          567 
          568         fs = fsys->priv;
          569         if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
          570                 return ok;
          571 
          572         USED(au);        /* anyone can getattr */
          573 
          574         return ino2attr(fs, &ino, inum, attr);
          575 }
          576 
          577 static Nfs3Status
          578 ffsaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
          579 {
          580         int have;
          581         Inode ino;
          582         u32int inum;
          583         Ffs *fs;
          584         Nfs3Status ok;
          585 
          586         fs = fsys->priv;
          587         if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
          588                 return ok;
          589 
          590         have = ino.mode&0777;
          591         if(ino.uid == au->uid)
          592                 have >>= 6;
          593         else if(ino.gid == au->gid || ingroup(au, ino.gid))
          594                 have >>= 3;
          595 
          596         *got = 0;
          597         if((want&Nfs3AccessRead) && (have&AREAD))
          598                 *got |= Nfs3AccessRead;
          599         if((want&Nfs3AccessLookup) && (ino.mode&IFMT)==IFDIR && (have&AEXEC))
          600                 *got |= Nfs3AccessLookup;
          601         if((want&Nfs3AccessExecute) && (ino.mode&IFMT)!=IFDIR && (have&AEXEC))
          602                 *got |= Nfs3AccessExecute;
          603 
          604         return ino2attr(fs, &ino, inum, attr);
          605 }
          606 
          607 static Nfs3Status
          608 ffslookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
          609 {
          610         u32int nblock;
          611         u32int i;
          612         uchar *p, *ep;
          613         Dirent *de;
          614         Inode ino;
          615         Block *b;
          616         Ffs *fs;
          617         Nfs3Status ok;
          618         int len, want;
          619 
          620         fs = fsys->priv;
          621         if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
          622                 return ok;
          623 
          624         if((ino.mode&IFMT) != IFDIR)
          625                 return Nfs3ErrNotDir;
          626 
          627         if((ok = inoperm(&ino, au, AEXEC)) != Nfs3Ok)
          628                 return ok;
          629 
          630         len = strlen(name);
          631         nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
          632         for(i=0; i<nblock; i++){
          633                 if(i==nblock-1)
          634                         want = ino.size % fs->blocksize;
          635                 else
          636                         want = fs->blocksize;
          637                 b = ffsfileblock(fs, &ino, i, want);
          638                 if(b == nil)
          639                         continue;
          640                 p = b->data;
          641                 ep = p+b->len;
          642                 while(p < ep){
          643                         de = (Dirent*)p;
          644                         if(de->reclen == 0){
          645                                 if(debug)
          646                                         fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
          647                                 break;
          648                         }
          649                         p += de->reclen;
          650                         if(p > ep){
          651                                 if(debug)
          652                                         fprint(2, "bad len %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
          653                                 break;
          654                         }
          655                         if(de->ino == 0)
          656                                 continue;
          657                         if(4+2+2+de->namlen > de->reclen){
          658                                 if(debug)
          659                                         fprint(2, "bad namelen %d at offset %d of %d\n", de->namlen, (int)(p-b->data), b->len);
          660                                 break;
          661                         }
          662                         if(de->namlen == len && memcmp(de->name, name, len) == 0){
          663                                 mkhandle(nh, de->ino);
          664                                 blockput(b);
          665                                 return Nfs3Ok;
          666                         }
          667                 }
          668                 blockput(b);
          669         }
          670         return Nfs3ErrNoEnt;
          671 }
          672 
          673 static Nfs3Status
          674 ffsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
          675 {
          676         u32int nblock;
          677         u32int i;
          678         int off, done;
          679         uchar *data, *dp, *dep, *p, *ep, *ndp;
          680         Dirent *de;
          681         Inode ino;
          682         Block *b;
          683         Ffs *fs;
          684         Nfs3Status ok;
          685         Nfs3Entry e;
          686         int want;
          687 
          688         fs = fsys->priv;
          689         if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
          690                 return ok;
          691 
          692         if((ino.mode&IFMT) != IFDIR)
          693                 return Nfs3ErrNotDir;
          694 
          695         if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
          696                 return ok;
          697 
          698         if(cookie >= ino.size){
          699                 *peof = 1;
          700                 *pcount = 0;
          701                 *pdata = 0;
          702                 return Nfs3Ok;
          703         }
          704 
          705         dp = malloc(count);
          706         data = dp;
          707         if(dp == nil)
          708                 return Nfs3ErrNoMem;
          709         dep = dp+count;
          710         *peof = 0;
          711         nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
          712         i = cookie/fs->blocksize;
          713         off = cookie%fs->blocksize;
          714         done = 0;
          715         for(; i<nblock && !done; i++){
          716                 if(i==nblock-1)
          717                         want = ino.size % fs->blocksize;
          718                 else
          719                         want = fs->blocksize;
          720                 b = ffsfileblock(fs, &ino, i, want);
          721                 if(b == nil)
          722                         continue;
          723                 p = b->data;
          724                 ep = p+b->len;
          725                 memset(&e, 0, sizeof e);
          726                 while(p < ep){
          727                         de = (Dirent*)p;
          728                         if(de->reclen == 0){
          729                                 if(debug) fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
          730                                 break;
          731                         }
          732                         p += de->reclen;
          733                         if(p > ep){
          734                                 if(debug) fprint(2, "reclen %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
          735                                 break;
          736                         }
          737                         if(de->ino == 0){
          738                                 if(debug) fprint(2, "zero inode\n");
          739                                 continue;
          740                         }
          741                         if(4+2+2+de->namlen > de->reclen){
          742                                 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);
          743                                 break;
          744                         }
          745                         if(de->name[de->namlen] != 0){
          746                                 if(debug) fprint(2, "bad name %d %.*s\n", de->namlen, de->namlen, de->name);
          747                                 continue;
          748                         }
          749                         if(debug) print("%s/%d ", de->name, (int)de->ino);
          750                         if((uchar*)de - b->data < off)
          751                                 continue;
          752                         e.fileid = de->ino;
          753                         e.name = de->name;
          754                         e.namelen = de->namlen;
          755                         e.cookie = (u64int)i*fs->blocksize + (p - b->data);
          756                         if(nfs3entrypack(dp, dep, &ndp, &e) < 0){
          757                                 done = 1;
          758                                 break;
          759                         }
          760                         dp = ndp;
          761                 }
          762                 off = 0;
          763                 blockput(b);
          764         }
          765         if(i==nblock)
          766                 *peof = 1;
          767 
          768         *pcount = dp - data;
          769         *pdata = data;
          770         return Nfs3Ok;
          771 }
          772 
          773 static u64int
          774 ffsxfileblock(Fsys *fsys, Nfs3Handle *h, u64int offset)
          775 {
          776         u64int bno;
          777         Inode ino;
          778         Nfs3Status ok;
          779         Ffs *fs;
          780 
          781         fs = fsys->priv;
          782         if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok){
          783                 nfs3errstr(ok);
          784                 return 0;
          785         }
          786         if(offset == 1)        /* clumsy hack for debugging */
          787                 return lastiaddr;
          788         if(offset >= ino.size){
          789                 werrstr("beyond end of file");
          790                 return 0;
          791         }
          792         bno = offset/fs->blocksize;
          793         bno = ffsfileblockno(fs, &ino, bno);
          794         if(bno == ~0)
          795                 return 0;
          796         return bno*(u64int)fs->fragsize;
          797 }
          798 
          799 static Nfs3Status
          800 ffsreadfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count,
          801         u64int offset, uchar **pdata, u32int *pcount, u1int *peof)
          802 {
          803         uchar *data;
          804         Block *b;
          805         Ffs *fs;
          806         int off, want, fragcount;
          807         Inode ino;
          808         Nfs3Status ok;
          809 
          810         fs = fsys->priv;
          811         if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
          812                 return ok;
          813 
          814         if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
          815                 return ok;
          816 
          817         if(offset >= ino.size){
          818                 *pdata = 0;
          819                 *pcount = 0;
          820                 *peof = 1;
          821                 return Nfs3Ok;
          822         }
          823         if(offset+count > ino.size)
          824                 count = ino.size-offset;
          825         if(offset/fs->blocksize != (offset+count-1)/fs->blocksize)
          826                 count = fs->blocksize - offset%fs->blocksize;
          827 
          828         data = malloc(count);
          829         if(data == nil)
          830                 return Nfs3ErrNoMem;
          831 
          832         want = offset%fs->blocksize+count;
          833         if(want%fs->fragsize)
          834                 want += fs->fragsize - want%fs->fragsize;
          835 
          836         b = ffsfileblock(fs, &ino, offset/fs->blocksize, want);
          837         if(b == nil){
          838                 /* BUG: distinguish sparse file from I/O error */
          839                 memset(data, 0, count);
          840         }else{
          841                 off = offset%fs->blocksize;
          842                 fragcount = count;        /* need signed variable */
          843                 if(off+fragcount > b->len){
          844                         fragcount = b->len - off;
          845                         if(fragcount < 0)
          846                                 fragcount = 0;
          847                 }
          848                 if(fragcount > 0)
          849                         memmove(data, b->data+off, fragcount);
          850                 count = fragcount;
          851                 blockput(b);
          852         }
          853         *peof = (offset+count == ino.size);
          854         *pcount = count;
          855         *pdata = data;
          856         return Nfs3Ok;
          857 }
          858 
          859 static Nfs3Status
          860 ffsreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link)
          861 {
          862         Ffs *fs;
          863         Nfs3Status ok;
          864         int len;
          865         Inode ino;
          866         Block *b;
          867 
          868         fs = fsys->priv;
          869         if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
          870                 return ok;
          871         if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
          872                 return ok;
          873 
          874         if(ino.size > 1024)
          875                 return Nfs3ErrIo;
          876         len = ino.size;
          877 
          878         if(ino.nblock != 0){
          879                 /* assumes symlink fits in one block */
          880                 b = ffsfileblock(fs, &ino, 0, len);
          881                 if(b == nil)
          882                         return Nfs3ErrIo;
          883                 if(memchr(b->data, 0, len) != nil){
          884                         blockput(b);
          885                         return Nfs3ErrIo;
          886                 }
          887                 *link = malloc(len+1);
          888                 if(*link == 0){
          889                         blockput(b);
          890                         return Nfs3ErrNoMem;
          891                 }
          892                 memmove(*link, b->data, len);
          893                 (*link)[len] = 0;
          894                 blockput(b);
          895                 return Nfs3Ok;
          896         }
          897 
          898         if(len > sizeof ino.db + sizeof ino.ib)
          899                 return Nfs3ErrIo;
          900 
          901         *link = malloc(len+1);
          902         if(*link == 0)
          903                 return Nfs3ErrNoMem;
          904         memmove(*link, ino.db, ino.size);
          905         (*link)[len] = 0;
          906         return Nfs3Ok;
          907 }