text2.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
       ---
       text2.c (18673B)
       ---
            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 "ext2.h"
            8 
            9 static void parsedirent(Dirent*, uchar*);
           10 static void parseinode(Inode*, uchar*);
           11 static void parsegroup(Group*, uchar*);
           12 static void parsesuper(Super*, uchar*);
           13 
           14 #define debug 0
           15 
           16 static int ext2sync(Fsys*);
           17 static void ext2close(Fsys*);
           18 static Block* ext2blockread(Fsys*, u64int);
           19 
           20 static Nfs3Status ext2root(Fsys*, Nfs3Handle*);
           21 static Nfs3Status ext2getattr(Fsys*, SunAuthUnix *au, Nfs3Handle*, Nfs3Attr*);
           22 static Nfs3Status ext2lookup(Fsys*, SunAuthUnix *au, Nfs3Handle*, char*, Nfs3Handle*);
           23 static Nfs3Status ext2readfile(Fsys*, SunAuthUnix *au, Nfs3Handle*, u32int, u64int, uchar**, u32int*, u1int*);
           24 static Nfs3Status ext2readlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link);
           25 static Nfs3Status ext2readdir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int, u64int, uchar**, u32int*, u1int*);
           26 static Nfs3Status ext2access(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr);
           27 
           28 Fsys*
           29 fsysopenext2(Disk *disk)
           30 {
           31         Ext2 *fs;
           32         Fsys *fsys;
           33 
           34         fsys = emalloc(sizeof(Fsys));
           35         fs = emalloc(sizeof(Ext2));
           36         fs->disk = disk;
           37         fsys->priv = fs;
           38         fs->fsys = fsys;
           39         fsys->type = "ext2";
           40         fsys->_readblock = ext2blockread;
           41         fsys->_sync = ext2sync;
           42         fsys->_root = ext2root;
           43         fsys->_getattr = ext2getattr;
           44         fsys->_access = ext2access;
           45         fsys->_lookup = ext2lookup;
           46         fsys->_readfile = ext2readfile;
           47         fsys->_readlink = ext2readlink;
           48         fsys->_readdir = ext2readdir;
           49         fsys->_close = ext2close;
           50 
           51         if(ext2sync(fsys) < 0)
           52                 goto error;
           53 
           54         return fsys;
           55 
           56 error:
           57         ext2close(fsys);
           58         return nil;
           59 }
           60 
           61 static void
           62 ext2close(Fsys *fsys)
           63 {
           64         Ext2 *fs;
           65 
           66         fs = fsys->priv;
           67         free(fs);
           68         free(fsys);
           69 }
           70 
           71 static int
           72 ext2group(Ext2 *fs, u32int i, Group *g)
           73 {
           74         Block *b;
           75         u64int addr;
           76 
           77         if(i >= fs->ngroup)
           78                 return -1;
           79 
           80         addr = fs->groupaddr + i/fs->descperblock;
           81         b = diskread(fs->disk, fs->blocksize, addr*fs->blocksize);
           82         if(b == nil)
           83                 return -1;
           84         parsegroup(g, b->data+i%fs->descperblock*GroupSize);
           85         blockput(b);
           86         return 0;
           87 }
           88 
           89 static Block*
           90 ext2blockread(Fsys *fsys, u64int vbno)
           91 {
           92         Block *bitb;
           93         Group g;
           94         uchar *bits;
           95         u32int bno, boff, bitblock;
           96         u64int bitpos;
           97         Ext2 *fs;
           98 
           99         fs = fsys->priv;
          100         if(vbno >= fs->nblock)
          101                 return nil;
          102         bno = vbno;
          103         if(bno != vbno)
          104                 return nil;
          105 
          106 /*
          107         if(bno < fs->firstblock)
          108                 return diskread(fs->disk, fs->blocksize, (u64int)bno*fs->blocksize);
          109 */
          110         if(bno < fs->firstblock)
          111                 return nil;
          112 
          113         bno -= fs->firstblock;
          114         if(ext2group(fs, bno/fs->blockspergroup, &g) < 0){
          115                 if(debug)
          116                         fprint(2, "loading group: %r...");
          117                 return nil;
          118         }
          119 /*
          120         if(debug)
          121                 fprint(2, "ext2 group %d: bitblock=%ud inodebitblock=%ud inodeaddr=%ud freeblocks=%ud freeinodes=%ud useddirs=%ud\n",
          122                         (int)(bno/fs->blockspergroup),
          123                         g.bitblock,
          124                         g.inodebitblock,
          125                         g.inodeaddr,
          126                         g.freeblockscount,
          127                         g.freeinodescount,
          128                         g.useddirscount);
          129         if(debug)
          130                 fprint(2, "group %d bitblock=%d...", bno/fs->blockspergroup, g.bitblock);
          131 */
          132         bitblock = g.bitblock;
          133         bitpos = (u64int)bitblock*fs->blocksize;
          134 
          135         if((bitb = diskread(fs->disk, fs->blocksize, bitpos)) == nil){
          136                 if(debug)
          137                         fprint(2, "loading bitblock: %r...");
          138                 return nil;
          139         }
          140         bits = bitb->data;
          141         boff = bno%fs->blockspergroup;
          142         if((bits[boff>>3] & (1<<(boff&7))) == 0){
          143                 if(debug)
          144                         fprint(2, "block %d not allocated in group %d: bitblock %d/%lld bits[%d] = %#x\n",
          145                                 boff, bno/fs->blockspergroup,
          146                                 (int)bitblock,
          147                                 bitpos,
          148                                 boff>>3,
          149                                 bits[boff>>3]);
          150                 blockput(bitb);
          151                 return nil;
          152         }
          153         blockput(bitb);
          154 
          155         bno += fs->firstblock;
          156         return diskread(fs->disk, fs->blocksize, (u64int)bno*fs->blocksize);
          157 }
          158 
          159 static Block*
          160 ext2datablock(Ext2 *fs, u32int bno, int size)
          161 {
          162         USED(size);
          163         return ext2blockread(fs->fsys, bno);
          164 }
          165 
          166 static Block*
          167 ext2fileblock(Ext2 *fs, Inode *ino, u32int bno, int size)
          168 {
          169         int ppb;
          170         Block *b;
          171         u32int *a;
          172         u32int obno, pbno;
          173 
          174         obno = bno;
          175         if(bno < NDIRBLOCKS){
          176                 if(debug)
          177                         fprint(2, "fileblock %d -> %d...",
          178                                 bno, ino->block[bno]);
          179                 return ext2datablock(fs, ino->block[bno], size);
          180         }
          181         bno -= NDIRBLOCKS;
          182         ppb = fs->blocksize/4;
          183 
          184         /* one indirect */
          185         if(bno < ppb){
          186                 b = ext2datablock(fs, ino->block[INDBLOCK], fs->blocksize);
          187                 if(b == nil)
          188                         return nil;
          189                 a = (u32int*)b->data;
          190                 bno = a[bno];
          191                 blockput(b);
          192                 return ext2datablock(fs, bno, size);
          193         }
          194         bno -= ppb;
          195 
          196         /* one double indirect */
          197         if(bno < ppb*ppb){
          198                 b = ext2datablock(fs, ino->block[DINDBLOCK], fs->blocksize);
          199                 if(b == nil)
          200                         return nil;
          201                 a = (u32int*)b->data;
          202                 pbno = a[bno/ppb];
          203                 bno = bno%ppb;
          204                 blockput(b);
          205                 b = ext2datablock(fs, pbno, fs->blocksize);
          206                 if(b == nil)
          207                         return nil;
          208                 a = (u32int*)b->data;
          209                 bno = a[bno];
          210                 blockput(b);
          211                 return ext2datablock(fs, bno, size);
          212         }
          213         bno -= ppb*ppb;
          214 
          215         /* one triple indirect */
          216         if(bno < ppb*ppb*ppb){
          217                 b = ext2datablock(fs, ino->block[TINDBLOCK], fs->blocksize);
          218                 if(b == nil)
          219                         return nil;
          220                 a = (u32int*)b->data;
          221                 pbno = a[bno/(ppb*ppb)];
          222                 bno = bno%(ppb*ppb);
          223                 blockput(b);
          224                 b = ext2datablock(fs, pbno, fs->blocksize);
          225                 if(b == nil)
          226                         return nil;
          227                 a = (u32int*)b->data;
          228                 pbno = a[bno/ppb];
          229                 bno = bno%ppb;
          230                 blockput(b);
          231                 b = ext2datablock(fs, pbno, fs->blocksize);
          232                 if(b == nil)
          233                         return nil;
          234                 a = (u32int*)b->data;
          235                 bno = a[bno];
          236                 blockput(b);
          237                 return ext2datablock(fs, bno, size);
          238         }
          239 
          240         fprint(2, "ext2fileblock %ud: too big\n", obno);
          241         return nil;
          242 }
          243 
          244 static int
          245 checksuper(Super *super)
          246 {
          247         if(super->magic != SUPERMAGIC){
          248                 werrstr("bad magic 0x%ux wanted 0x%ux", super->magic, SUPERMAGIC);
          249                 return -1;
          250         }
          251         return 0;
          252 }
          253 
          254 static int
          255 ext2sync(Fsys *fsys)
          256 {
          257         int i;
          258         Group g;
          259         Block *b;
          260         Super super;
          261         Ext2 *fs;
          262         Disk *disk;
          263 
          264         fs = fsys->priv;
          265         disk = fs->disk;
          266         if((b = diskread(disk, SBSIZE, SBOFF)) == nil)
          267                 return -1;
          268         parsesuper(&super, b->data);
          269         blockput(b);
          270         if(checksuper(&super) < 0)
          271                 return -1;
          272         fs->blocksize = MINBLOCKSIZE<<super.logblocksize;
          273         fs->nblock = super.nblock;
          274         fs->ngroup = (super.nblock+super.blockspergroup-1)
          275                 / super.blockspergroup;
          276         fs->inospergroup = super.inospergroup;
          277         fs->blockspergroup = super.blockspergroup;
          278         if(super.revlevel >= 1)
          279                 fs->inosize = super.inosize;
          280         else
          281                 fs->inosize = 128;
          282         fs->inosperblock = fs->blocksize / fs->inosize;
          283         if(fs->blocksize == SBOFF)
          284                 fs->groupaddr = 2;
          285         else
          286                 fs->groupaddr = 1;
          287         fs->descperblock = fs->blocksize / GroupSize;
          288         fs->firstblock = super.firstdatablock;
          289 
          290         fsys->blocksize = fs->blocksize;
          291         fsys->nblock = fs->nblock;
          292         if(debug) fprint(2, "ext2 %d %d-byte blocks, first data block %d, %d groups of %d\n",
          293                 fs->nblock, fs->blocksize, fs->firstblock, fs->ngroup, fs->blockspergroup);
          294 
          295         if(0){
          296                 for(i=0; i<fs->ngroup; i++)
          297                         if(ext2group(fs, i, &g) >= 0)
          298                                 fprint(2, "grp %d: bitblock=%d\n", i, g.bitblock);
          299         }
          300         return 0;
          301 }
          302 
          303 static void
          304 mkhandle(Nfs3Handle *h, u64int ino)
          305 {
          306         h->h[0] = ino>>24;
          307         h->h[1] = ino>>16;
          308         h->h[2] = ino>>8;
          309         h->h[3] = ino;
          310         h->len = 4;
          311 }
          312 
          313 static u32int
          314 byte2u32(uchar *p)
          315 {
          316         return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
          317 }
          318 
          319 static Nfs3Status
          320 handle2ino(Ext2 *fs, Nfs3Handle *h, u32int *pinum, Inode *ino)
          321 {
          322         int i;
          323         uint ioff;
          324         u32int inum;
          325         u32int addr;
          326         Block *b;
          327         Group g;
          328 
          329         if(h->len != 4)
          330                 return Nfs3ErrBadHandle;
          331         inum = byte2u32(h->h);
          332         if(pinum)
          333                 *pinum = inum;
          334         i = (inum-1) / fs->inospergroup;
          335         if(i >= fs->ngroup)
          336                 return Nfs3ErrBadHandle;
          337         ioff = (inum-1) % fs->inospergroup;
          338         if(ext2group(fs, i, &g) < 0)
          339                 return Nfs3ErrIo;
          340         addr = g.inodeaddr + ioff/fs->inosperblock;
          341         if((b = diskread(fs->disk, fs->blocksize, (u64int)addr*fs->blocksize)) == nil)
          342                 return Nfs3ErrIo;
          343         parseinode(ino, b->data+fs->inosize*(ioff%fs->inosperblock));
          344         blockput(b);
          345         return Nfs3Ok;
          346 }
          347 
          348 static Nfs3Status
          349 ext2root(Fsys *fsys, Nfs3Handle *h)
          350 {
          351         USED(fsys);
          352         mkhandle(h, ROOTINODE);
          353         return Nfs3Ok;
          354 }
          355 
          356 static u64int
          357 inosize(Inode* ino)
          358 {
          359         u64int size;
          360 
          361         size = ino->size;
          362         if((ino->mode&IFMT)==IFREG)
          363                 size |= (u64int)ino->diracl << 32;
          364         return size;
          365 }
          366 
          367 static Nfs3Status
          368 ino2attr(Ext2 *fs, Inode *ino, u32int inum, Nfs3Attr *attr)
          369 {
          370         u32int rdev;
          371 
          372         attr->type = -1;
          373         switch(ino->mode&IFMT){
          374         case IFIFO:
          375                 attr->type = Nfs3FileFifo;
          376                 break;
          377         case IFCHR:
          378                 attr->type = Nfs3FileChar;
          379                 break;
          380         case IFDIR:
          381                 attr->type = Nfs3FileDir;
          382                 break;
          383         case IFBLK:
          384                 attr->type = Nfs3FileBlock;
          385                 break;
          386         case IFREG:
          387                 attr->type = Nfs3FileReg;
          388                 break;
          389         case IFLNK:
          390                 attr->type = Nfs3FileSymlink;
          391                 break;
          392         case IFSOCK:
          393                 attr->type = Nfs3FileSocket;
          394                 break;
          395         case IFWHT:
          396         default:
          397                 return Nfs3ErrBadHandle;
          398         }
          399 
          400         attr->mode = ino->mode&07777;
          401         attr->nlink = ino->nlink;
          402         attr->uid = ino->uid;
          403         attr->gid = ino->gid;
          404         attr->size = inosize(ino);
          405         attr->used = (u64int)ino->nblock*fs->blocksize;
          406         if(attr->type==Nfs3FileBlock || attr->type==Nfs3FileChar){
          407                 rdev = ino->block[0];
          408                 attr->major = (rdev>>8)&0xFF;
          409                 attr->minor = rdev & 0xFFFF00FF;
          410         }else{
          411                 attr->major = 0;
          412                 attr->minor = 0;
          413         }
          414         attr->fsid = 0;
          415         attr->fileid = inum;
          416         attr->atime.sec = ino->atime;
          417         attr->atime.nsec = 0;
          418         attr->mtime.sec = ino->mtime;
          419         attr->mtime.nsec = 0;
          420         attr->ctime.sec = ino->ctime;
          421         attr->ctime.nsec = 0;
          422         return Nfs3Ok;
          423 }
          424 
          425 static int
          426 ingroup(SunAuthUnix *au, uint gid)
          427 {
          428         int i;
          429 
          430         for(i=0; i<au->ng; i++)
          431                 if(au->g[i] == gid)
          432                         return 1;
          433         return 0;
          434 }
          435 
          436 static Nfs3Status
          437 inoperm(Inode *ino, SunAuthUnix *au, int need)
          438 {
          439         int have;
          440 
          441         if(allowall)
          442                 return Nfs3Ok;
          443 
          444         have = ino->mode&0777;
          445         if(ino->uid == au->uid)
          446                 have >>= 6;
          447         else if(ino->gid == au->gid || ingroup(au, ino->gid))
          448                 have >>= 3;
          449 
          450         if((have&need) != need)
          451                 return Nfs3ErrNotOwner;        /* really EPERM */
          452         return Nfs3Ok;
          453 }
          454 
          455 static Nfs3Status
          456 ext2getattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
          457 {
          458         Inode ino;
          459         u32int inum;
          460         Ext2 *fs;
          461         Nfs3Status ok;
          462 
          463         fs = fsys->priv;
          464         if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
          465                 return ok;
          466 
          467         USED(au);        /* anyone can getattr */
          468         return ino2attr(fs, &ino, inum, attr);
          469 }
          470 
          471 static Nfs3Status
          472 ext2access(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
          473 {
          474         int have;
          475         Inode ino;
          476         u32int inum;
          477         Ext2 *fs;
          478         Nfs3Status ok;
          479 
          480         fs = fsys->priv;
          481         if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
          482                 return ok;
          483 
          484         have = ino.mode&0777;
          485         if(ino.uid == au->uid)
          486                 have >>= 6;
          487         else if(ino.gid == au->gid || ingroup(au, ino.gid))
          488                 have >>= 3;
          489 
          490         *got = 0;
          491         if((want&Nfs3AccessRead) && (have&AREAD))
          492                 *got |= Nfs3AccessRead;
          493         if((want&Nfs3AccessLookup) && (ino.mode&IFMT)==IFDIR && (have&AEXEC))
          494                 *got |= Nfs3AccessLookup;
          495         if((want&Nfs3AccessExecute) && (ino.mode&IFMT)!=IFDIR && (have&AEXEC))
          496                 *got |= Nfs3AccessExecute;
          497 
          498         return ino2attr(fs, &ino, inum, attr);
          499 }
          500 
          501 static Nfs3Status
          502 ext2lookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
          503 {
          504         u32int nblock;
          505         u32int i;
          506         uchar *p, *ep;
          507         Dirent de;
          508         Inode ino;
          509         Block *b;
          510         Ext2 *fs;
          511         Nfs3Status ok;
          512         int len, want;
          513 
          514         fs = fsys->priv;
          515         if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
          516                 return ok;
          517 
          518         if((ino.mode&IFMT) != IFDIR)
          519                 return Nfs3ErrNotDir;
          520 
          521         if((ok = inoperm(&ino, au, AEXEC)) != Nfs3Ok)
          522                 return ok;
          523 
          524         len = strlen(name);
          525         nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
          526         if(debug) fprint(2, "%d blocks in dir...", nblock);
          527         for(i=0; i<nblock; i++){
          528                 if(i==nblock-1)
          529                         want = ino.size % fs->blocksize;
          530                 else
          531                         want = fs->blocksize;
          532                 b = ext2fileblock(fs, &ino, i, want);
          533                 if(b == nil){
          534                         if(debug) fprint(2, "empty block...");
          535                         continue;
          536                 }
          537                 p = b->data;
          538                 ep = p+b->len;
          539                 while(p < ep){
          540                         parsedirent(&de, p);
          541                         if(de.reclen == 0){
          542                                 if(debug)
          543                                         fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
          544                                 break;
          545                         }
          546                         p += de.reclen;
          547                         if(p > ep){
          548                                 if(debug)
          549                                         fprint(2, "bad len %d at offset %d of %d\n", de.reclen, (int)(p-b->data), b->len);
          550                                 break;
          551                         }
          552                         if(de.ino == 0)
          553                                 continue;
          554                         if(4+2+2+de.namlen > de.reclen){
          555                                 if(debug)
          556                                         fprint(2, "bad namelen %d at offset %d of %d\n", de.namlen, (int)(p-b->data), b->len);
          557                                 break;
          558                         }
          559                         if(de.namlen == len && memcmp(de.name, name, len) == 0){
          560                                 mkhandle(nh, de.ino);
          561                                 blockput(b);
          562                                 return Nfs3Ok;
          563                         }
          564                 }
          565                 blockput(b);
          566         }
          567         return Nfs3ErrNoEnt;
          568 }
          569 
          570 static Nfs3Status
          571 ext2readdir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
          572 {
          573         u32int nblock;
          574         u32int i;
          575         int off, outofspace;
          576         uchar *data, *dp, *dep, *p, *ep, *ndp;
          577         Dirent de;
          578         Inode ino;
          579         Block *b;
          580         Ext2 *fs;
          581         Nfs3Status ok;
          582         Nfs3Entry e;
          583         int want;
          584 
          585         fs = fsys->priv;
          586         if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
          587                 return ok;
          588 
          589         if((ino.mode&IFMT) != IFDIR)
          590                 return Nfs3ErrNotDir;
          591 
          592         if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
          593                 return ok;
          594 
          595         if(debug) print("readdir cookie %#llux ino.size %#llux\n",
          596                 (u64int)cookie, (u64int)ino.size);
          597 
          598         if(cookie >= ino.size){
          599                 *peof = 1;
          600                 *pcount = 0;
          601                 *pdata = 0;
          602                 return Nfs3Ok;
          603         }
          604 
          605         dp = malloc(count);
          606         data = dp;
          607         if(dp == nil)
          608                 return Nfs3ErrNoMem;
          609         dep = dp+count;
          610         *peof = 0;
          611         nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
          612         i = cookie/fs->blocksize;
          613         off = cookie%fs->blocksize;
          614         outofspace = 0;
          615         for(; i<nblock && !outofspace; i++, off=0){
          616                 if(i==nblock-1)
          617                         want = ino.size % fs->blocksize;
          618                 else
          619                         want = fs->blocksize;
          620                 b = ext2fileblock(fs, &ino, i, want);
          621                 if(b == nil)
          622                         continue;
          623                 p = b->data;
          624                 ep = p+b->len;
          625                 memset(&e, 0, sizeof e);
          626                 while(p < ep){
          627                         parsedirent(&de, p);
          628                         if(de.reclen == 0){
          629                                 if(debug) fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
          630                                 break;
          631                         }
          632                         p += de.reclen;
          633                         if(p > ep){
          634                                 if(debug) fprint(2, "reclen %d at offset %d of %d\n", de.reclen, (int)(p-b->data), b->len);
          635                                 break;
          636                         }
          637                         if(de.ino == 0){
          638                                 if(debug) fprint(2, "zero inode\n");
          639                                 continue;
          640                         }
          641                         if(4+2+2+de.namlen > de.reclen){
          642                                 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);
          643                                 break;
          644                         }
          645                         if(debug) print("%.*s/%d ", de.namlen, de.name, (int)de.ino);
          646                         if(p-de.reclen - b->data < off)
          647                                 continue;
          648                         e.fileid = de.ino;
          649                         e.name = de.name;
          650                         e.namelen = de.namlen;
          651                         e.cookie = (u64int)i*fs->blocksize + (p - b->data);
          652                         if(debug) print("%.*s %#llux\n", utfnlen(e.name, e.namelen), e.name, (u64int)e.cookie);
          653                         if(nfs3entrypack(dp, dep, &ndp, &e) < 0){
          654                                 outofspace = 1;
          655                                 break;
          656                         }
          657                         dp = ndp;
          658                 }
          659                 blockput(b);
          660         }
          661         if(i==nblock && !outofspace)
          662                 *peof = 1;
          663 
          664         *pcount = dp - data;
          665         *pdata = data;
          666         return Nfs3Ok;
          667 }
          668 
          669 static Nfs3Status
          670 ext2readfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count,
          671         u64int offset, uchar **pdata, u32int *pcount, u1int *peof)
          672 {
          673         uchar *data;
          674         Block *b;
          675         Ext2 *fs;
          676         int skip1, tot, want, fragcount;
          677         Inode ino;
          678         Nfs3Status ok;
          679         u64int size;
          680 
          681         fs = fsys->priv;
          682         if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
          683                 return ok;
          684 
          685         if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
          686                 return ok;
          687 
          688         size = inosize(&ino);
          689         if(offset >= size){
          690                 *pdata = 0;
          691                 *pcount = 0;
          692                 *peof = 1;
          693                 return Nfs3Ok;
          694         }
          695         if(offset+count > size)
          696                 count = size-offset;
          697 
          698         data = malloc(count);
          699         if(data == nil)
          700                 return Nfs3ErrNoMem;
          701         memset(data, 0, count);
          702 
          703         skip1 = offset%fs->blocksize;
          704         offset -= skip1;
          705         want = skip1+count;
          706 
          707         /*
          708          * have to read multiple blocks if we get asked for a big read.
          709          * Linux NFS client assumes that if you ask for 8k and only get 4k
          710          * back, the remaining 4k is zeros.
          711          */
          712         for(tot=0; tot<want; tot+=fragcount){
          713                 b = ext2fileblock(fs, &ino, (offset+tot)/fs->blocksize, fs->blocksize);
          714                 fragcount = fs->blocksize;
          715                 if(b == nil)
          716                         continue;
          717                 if(tot+fragcount > want)
          718                         fragcount = want - tot;
          719                 if(tot == 0)
          720                         memmove(data, b->data+skip1, fragcount-skip1);
          721                 else
          722                         memmove(data+tot-skip1, b->data, fragcount);
          723                 blockput(b);
          724         }
          725         count = tot - skip1;
          726 
          727         *peof = (offset+count == size);
          728         *pcount = count;
          729         *pdata = data;
          730         return Nfs3Ok;
          731 }
          732 
          733 static Nfs3Status
          734 ext2readlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link)
          735 {
          736         Ext2 *fs;
          737         Nfs3Status ok;
          738         int len;
          739         Inode ino;
          740         Block *b;
          741 
          742         fs = fsys->priv;
          743         if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
          744                 return ok;
          745         if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
          746                 return ok;
          747 
          748         if(ino.size > 1024)
          749                 return Nfs3ErrIo;
          750         len = ino.size;
          751 
          752         if(ino.nblock != 0){
          753                 /* BUG: assumes symlink fits in one block */
          754                 b = ext2fileblock(fs, &ino, 0, len);
          755                 if(b == nil)
          756                         return Nfs3ErrIo;
          757                 if(memchr(b->data, 0, len) != nil){
          758                         blockput(b);
          759                         return Nfs3ErrIo;
          760                 }
          761                 *link = malloc(len+1);
          762                 if(*link == 0){
          763                         blockput(b);
          764                         return Nfs3ErrNoMem;
          765                 }
          766                 memmove(*link, b->data, len);
          767                 (*link)[len] = 0;
          768                 blockput(b);
          769                 return Nfs3Ok;
          770         }
          771 
          772         if(len > sizeof ino.block)
          773                 return Nfs3ErrIo;
          774 
          775         *link = malloc(len+1);
          776         if(*link == 0)
          777                 return Nfs3ErrNoMem;
          778         memmove(*link, ino.block, ino.size);
          779         (*link)[len] = 0;
          780         return Nfs3Ok;
          781 }
          782 
          783 /*
          784  * Ext2 is always little-endian, even on big-endian machines.
          785  */
          786 
          787 static u32int
          788 l32(uchar *p)
          789 {
          790         return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
          791 }
          792 
          793 static u16int
          794 l16(uchar *p)
          795 {
          796         return p[0] | (p[1]<<8);
          797 }
          798 
          799 static u8int
          800 l8(uchar *p)
          801 {
          802         return p[0];
          803 }
          804 
          805 static void
          806 parsedirent(Dirent *de, uchar *p)
          807 {
          808         de->ino = l32(p);
          809         de->reclen = l16(p+4);
          810         de->namlen = l8(p+6);
          811         /* 1 byte pad */
          812         de->name = (char*)p+8;
          813 }
          814 
          815 static void
          816 parseinode(Inode *ino, uchar *p)
          817 {
          818         int i;
          819 
          820         ino->mode = l16(p);
          821         ino->uid = l16(p+2);
          822         ino->size = l32(p+4);
          823         ino->atime = l32(p+8);
          824         ino->ctime = l32(p+12);
          825         ino->mtime = l32(p+16);
          826         ino->dtime = l32(p+20);
          827         ino->gid = l16(p+24);
          828         ino->nlink = l16(p+26);
          829         ino->nblock = l32(p+28);
          830         ino->flags = l32(p+32);
          831         /* 4 byte osd1 */
          832         for(i=0; i<NBLOCKS; i++)
          833                 ino->block[i] = l32(p+40+i*4);
          834         ino->version = l32(p+100);
          835         ino->fileacl = l32(p+104);
          836         ino->diracl = l32(p+108);
          837         ino->faddr = l32(p+112);
          838         /* 12 byte osd2 */
          839 }
          840 
          841 static void
          842 parsegroup(Group *g, uchar *p)
          843 {
          844         g->bitblock = l32(p);
          845         g->inodebitblock = l32(p+4);
          846         g->inodeaddr = l32(p+8);
          847         g->freeblockscount = l16(p+12);
          848         g->freeinodescount = l16(p+14);
          849         g->useddirscount = l16(p+16);
          850         /* 2 byte pad */
          851         /* 12 byte reserved */
          852 }
          853 
          854 static void
          855 parsesuper(Super *s, uchar *p)
          856 {
          857         s->ninode = l32(p);
          858         s->nblock = l32(p+4);
          859         s->rblockcount = l32(p+8);
          860         s->freeblockcount = l32(p+12);
          861         s->freeinodecount = l32(p+16);
          862         s->firstdatablock = l32(p+20);
          863         s->logblocksize = l32(p+24);
          864         s->logfragsize = l32(p+28);
          865         s->blockspergroup = l32(p+32);
          866         s->fragpergroup = l32(p+36);
          867         s->inospergroup = l32(p+40);
          868         s->mtime = l32(p+44);
          869         s->wtime = l32(p+48);
          870         s->mntcount = l16(p+52);
          871         s->maxmntcount = l16(p+54);
          872         s->magic = l16(p+56);
          873         s->state = l16(p+58);
          874         s->errors = l16(p+60);
          875         /* 2 byte pad */
          876         s->lastcheck = l32(p+64);
          877         s->checkinterval = l32(p+68);
          878         s->creatoros = l32(p+72);
          879         s->revlevel = l32(p+76);
          880         s->defresuid = l16(p+80);
          881         s->defresgid = l16(p+82);
          882         s->firstino = l32(p+84);
          883         s->inosize = l32(p+88);
          884         s->blockgroupnr = l16(p+60);
          885         /* 932 byte reserved */
          886 }