plan9port

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

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