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*
fsysopenffs(Disk * disk)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*
ffscylgrp(Ffs * fs,u32int i,Block ** pb)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
ffssync(Fsys * fsys)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
ffsclose(Fsys * fsys)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
checkfsblk(Fsblk * super)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
checkcgblk(Cgblk * cg)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*
ffsblockread(Fsys * fsys,u64int bno)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*
ffsdatablock(Ffs * fs,u64int bno,int size)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
ifetch(Ffs * fs,u64int bno,u32int off)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
ffsfileblockno(Ffs * fs,Inode * ino,u64int bno)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*
ffsfileblock(Ffs * fs,Inode * ino,u64int bno,int size)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
mkhandle(Nfs3Handle * h,u64int ino)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
byte2u32(uchar * p)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
inode1to2(Inode1 * i1,Inode * i2)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
handle2ino(Ffs * fs,Nfs3Handle * h,u32int * pinum,Inode * ino)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
ffsroot(Fsys * fsys,Nfs3Handle * h)465 ffsroot(Fsys *fsys, Nfs3Handle *h)
466 {
467 	USED(fsys);
468 	mkhandle(h, 2);
469 	return Nfs3Ok;
470 }
471 
472 static Nfs3Status
ino2attr(Ffs * fs,Inode * ino,u32int inum,Nfs3Attr * attr)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
ingroup(SunAuthUnix * au,uint gid)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
inoperm(Inode * ino,SunAuthUnix * au,int need)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
ffsgetattr(Fsys * fsys,SunAuthUnix * au,Nfs3Handle * h,Nfs3Attr * attr)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
ffsaccess(Fsys * fsys,SunAuthUnix * au,Nfs3Handle * h,u32int want,u32int * got,Nfs3Attr * attr)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
ffslookup(Fsys * fsys,SunAuthUnix * au,Nfs3Handle * h,char * name,Nfs3Handle * nh)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
ffsreaddir(Fsys * fsys,SunAuthUnix * au,Nfs3Handle * h,u32int count,u64int cookie,uchar ** pdata,u32int * pcount,u1int * peof)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
ffsxfileblock(Fsys * fsys,Nfs3Handle * h,u64int offset)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
ffsreadfile(Fsys * fsys,SunAuthUnix * au,Nfs3Handle * h,u32int count,u64int offset,uchar ** pdata,u32int * pcount,u1int * peof)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
ffsreadlink(Fsys * fsys,SunAuthUnix * au,Nfs3Handle * h,char ** link)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 }
908