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*
fsysopenext2(Disk * disk)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
ext2close(Fsys * fsys)62 ext2close(Fsys *fsys)
63 {
64 	Ext2 *fs;
65 
66 	fs = fsys->priv;
67 	free(fs);
68 	free(fsys);
69 }
70 
71 static int
ext2group(Ext2 * fs,u32int i,Group * g)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*
ext2blockread(Fsys * fsys,u64int vbno)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*
ext2datablock(Ext2 * fs,u32int bno,int size)160 ext2datablock(Ext2 *fs, u32int bno, int size)
161 {
162 	USED(size);
163 	return ext2blockread(fs->fsys, bno);
164 }
165 
166 static Block*
ext2fileblock(Ext2 * fs,Inode * ino,u32int bno,int size)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
checksuper(Super * super)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
ext2sync(Fsys * fsys)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
mkhandle(Nfs3Handle * h,u64int ino)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
byte2u32(uchar * p)314 byte2u32(uchar *p)
315 {
316 	return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
317 }
318 
319 static Nfs3Status
handle2ino(Ext2 * fs,Nfs3Handle * h,u32int * pinum,Inode * ino)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
ext2root(Fsys * fsys,Nfs3Handle * h)349 ext2root(Fsys *fsys, Nfs3Handle *h)
350 {
351 	USED(fsys);
352 	mkhandle(h, ROOTINODE);
353 	return Nfs3Ok;
354 }
355 
356 static u64int
inosize(Inode * ino)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
ino2attr(Ext2 * fs,Inode * ino,u32int inum,Nfs3Attr * attr)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
ingroup(SunAuthUnix * au,uint gid)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
inoperm(Inode * ino,SunAuthUnix * au,int need)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
ext2getattr(Fsys * fsys,SunAuthUnix * au,Nfs3Handle * h,Nfs3Attr * attr)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
ext2access(Fsys * fsys,SunAuthUnix * au,Nfs3Handle * h,u32int want,u32int * got,Nfs3Attr * attr)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
ext2lookup(Fsys * fsys,SunAuthUnix * au,Nfs3Handle * h,char * name,Nfs3Handle * nh)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
ext2readdir(Fsys * fsys,SunAuthUnix * au,Nfs3Handle * h,u32int count,u64int cookie,uchar ** pdata,u32int * pcount,u1int * peof)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
ext2readfile(Fsys * fsys,SunAuthUnix * au,Nfs3Handle * h,u32int count,u64int offset,uchar ** pdata,u32int * pcount,u1int * peof)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
ext2readlink(Fsys * fsys,SunAuthUnix * au,Nfs3Handle * h,char ** link)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
l32(uchar * p)788 l32(uchar *p)
789 {
790 	return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
791 }
792 
793 static u16int
l16(uchar * p)794 l16(uchar *p)
795 {
796 	return p[0] | (p[1]<<8);
797 }
798 
799 static u8int
l8(uchar * p)800 l8(uchar *p)
801 {
802 	return p[0];
803 }
804 
805 static void
parsedirent(Dirent * de,uchar * p)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
parseinode(Inode * ino,uchar * p)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
parsegroup(Group * g,uchar * p)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
parsesuper(Super * s,uchar * p)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 }
887