plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

ext2.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 }