Blob


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"
9 #define debug 1
11 static int ext2sync(Fsys*);
12 static void ext2close(Fsys*);
13 static Block* ext2blockread(Fsys*, u64int);
15 static Nfs3Status ext2root(Fsys*, Nfs3Handle*);
16 static Nfs3Status ext2getattr(Fsys*, SunAuthUnix *au, Nfs3Handle*, Nfs3Attr*);
17 static Nfs3Status ext2lookup(Fsys*, SunAuthUnix *au, Nfs3Handle*, char*, Nfs3Handle*);
18 static Nfs3Status ext2readfile(Fsys*, SunAuthUnix *au, Nfs3Handle*, u32int, u64int, uchar**, u32int*, u1int*);
19 static Nfs3Status ext2readlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link);
20 static Nfs3Status ext2readdir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int, u64int, uchar**, u32int*, u1int*);
21 static Nfs3Status ext2access(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr);
23 Fsys*
24 fsysopenext2(Disk *disk)
25 {
26 Ext2 *fs;
27 Fsys *fsys;
29 fsys = emalloc(sizeof(Fsys));
30 fs = emalloc(sizeof(Ext2));
31 fs->disk = disk;
32 fsys->priv = fs;
33 fs->fsys = fsys;
34 fsys->type = "ext2";
35 fsys->_readblock = ext2blockread;
36 fsys->_sync = ext2sync;
37 fsys->_root = ext2root;
38 fsys->_getattr = ext2getattr;
39 fsys->_access = ext2access;
40 fsys->_lookup = ext2lookup;
41 fsys->_readfile = ext2readfile;
42 fsys->_readlink = ext2readlink;
43 fsys->_readdir = ext2readdir;
45 if(ext2sync(fsys) < 0)
46 goto error;
48 return fsys;
50 error:
51 ext2close(fsys);
52 return nil;
53 }
55 static void
56 ext2close(Fsys *fsys)
57 {
58 Ext2 *fs;
60 fs = fsys->priv;
61 free(fs);
62 free(fsys);
63 }
65 static Group*
66 ext2group(Ext2 *fs, u32int i, Block **pb)
67 {
68 Block *b;
69 u64int addr;
70 Group *g;
72 if(i >= fs->ngroup)
73 return nil;
75 addr = fs->groupaddr + i/fs->descperblock;
76 b = diskread(fs->disk, fs->blocksize, addr*fs->blocksize);
77 if(b == nil)
78 return nil;
79 g = (Group*)(b->data+i%fs->descperblock*GroupSize);
80 *pb = b;
81 return g;
82 }
84 static Block*
85 ext2blockread(Fsys *fsys, u64int vbno)
86 {
87 Block *bitb;
88 Group *g;
89 Block *gb;
90 uchar *bits;
91 u32int bno, boff;
92 Ext2 *fs;
94 fs = fsys->priv;
95 if(vbno >= fs->nblock)
96 return nil;
97 bno = vbno;
98 if(bno != vbno)
99 return nil;
101 /*
102 if(bno < fs->firstblock)
103 return diskread(fs->disk, fs->blocksize, (u64int)bno*fs->blocksize);
104 */
105 if(bno < fs->firstblock)
106 return nil;
108 bno -= fs->firstblock;
109 if((g = ext2group(fs, bno/fs->blockspergroup, &gb)) == nil){
110 if(debug)
111 fprint(2, "loading group: %r...");
112 return nil;
114 // if(debug)
115 // fprint(2, "group %d bitblock=%d...", bno/fs->blockspergroup, g->bitblock);
117 if((bitb = diskread(fs->disk, fs->blocksize, (u64int)g->bitblock*fs->blocksize)) == nil){
118 if(debug)
119 fprint(2, "loading bitblock: %r...");
120 blockput(gb);
121 return nil;
123 bits = bitb->data;
124 boff = bno%fs->blockspergroup;
125 if((bits[boff>>3] & (1<<(boff&7))) == 0){
126 if(debug)
127 fprint(2, "block %d not allocated...", bno);
128 blockput(bitb);
129 blockput(gb);
130 return nil;
133 bno += fs->firstblock;
134 return diskread(fs->disk, fs->blocksize, (u64int)bno*fs->blocksize);
137 static Block*
138 ext2datablock(Ext2 *fs, u32int bno, int size)
140 USED(size);
141 return ext2blockread(fs->fsys, bno+fs->firstblock);
144 static Block*
145 ext2fileblock(Ext2 *fs, Inode *ino, u32int bno, int size)
147 int ppb;
148 Block *b;
149 u32int *a;
150 u32int obno;
152 obno = bno;
153 if(bno < NDIRBLOCKS){
154 if(debug)
155 fprint(2, "fileblock %d -> %d...",
156 bno, ino->block[bno]);
157 return ext2datablock(fs, ino->block[bno], size);
159 bno -= NDIRBLOCKS;
160 ppb = fs->blocksize/4;
162 /* one indirect */
163 if(bno < ppb){
164 b = ext2datablock(fs, ino->block[INDBLOCK], fs->blocksize);
165 if(b == nil)
166 return nil;
167 a = (u32int*)b->data;
168 bno = a[bno%ppb];
169 blockput(b);
170 return ext2datablock(fs, bno, size);
172 bno -= ppb;
174 /* one double indirect */
175 if(bno < ppb*ppb){
176 b = ext2datablock(fs, ino->block[DINDBLOCK], fs->blocksize);
177 if(b == nil)
178 return nil;
179 a = (u32int*)b->data;
180 bno = a[(bno/ppb)%ppb];
181 blockput(b);
182 b = ext2datablock(fs, bno, fs->blocksize);
183 if(b == nil)
184 return nil;
185 a = (u32int*)b->data;
186 bno = a[bno%ppb];
187 blockput(b);
188 return ext2datablock(fs, bno, size);
190 bno -= ppb*ppb;
192 /* one triple indirect */
193 if(bno < ppb*ppb*ppb){
194 b = ext2datablock(fs, ino->block[TINDBLOCK], fs->blocksize);
195 if(b == nil)
196 return nil;
197 a = (u32int*)b->data;
198 bno = a[(bno/(ppb*ppb))%ppb];
199 blockput(b);
200 b = ext2datablock(fs, bno, fs->blocksize);
201 if(b == nil)
202 return nil;
203 a = (u32int*)b->data;
204 bno = a[(bno/ppb)%ppb];
205 blockput(b);
206 b = ext2datablock(fs, bno, fs->blocksize);
207 if(b == nil)
208 return nil;
209 a = (u32int*)b->data;
210 bno = a[bno%ppb];
211 blockput(b);
212 return ext2datablock(fs, bno, size);
215 fprint(2, "ext2fileblock %ud: too big\n", obno);
216 return nil;
219 static int
220 checksuper(Super *super)
222 if(super->magic != SUPERMAGIC){
223 werrstr("bad magic 0x%ux wanted 0x%ux", super->magic, SUPERMAGIC);
224 return -1;
226 return 0;
229 static int
230 ext2sync(Fsys *fsys)
232 int i;
233 Group *g;
234 Block *b;
235 Super *super;
236 Ext2 *fs;
237 Disk *disk;
239 fs = fsys->priv;
240 disk = fs->disk;
241 if((b = diskread(disk, SBSIZE, SBOFF)) == nil)
242 goto error;
243 super = (Super*)b->data;
244 if(checksuper(super) < 0)
245 goto error;
246 fs->blocksize = MINBLOCKSIZE<<super->logblocksize;
247 fs->nblock = super->nblock;
248 fs->ngroup = (super->nblock+super->blockspergroup-1)
249 / super->blockspergroup;
250 fs->inospergroup = super->inospergroup;
251 fs->blockspergroup = super->blockspergroup;
252 fs->inosperblock = fs->blocksize / InodeSize;
253 if(fs->blocksize == SBOFF)
254 fs->groupaddr = 2;
255 else
256 fs->groupaddr = 1;
257 fs->descperblock = fs->blocksize / GroupSize;
258 fs->firstblock = super->firstdatablock;
259 blockput(b);
261 fsys->blocksize = fs->blocksize;
262 fsys->nblock = fs->nblock;
263 fprint(2, "ext2 %d %d-byte blocks, first data block %d, %d groups of %d\n",
264 fs->nblock, fs->blocksize, fs->firstblock, fs->ngroup, fs->blockspergroup);
266 if(0){
267 for(i=0; i<fs->ngroup; i++)
268 if((g = ext2group(fs, i, &b)) != nil){
269 fprint(2, "grp %d: bitblock=%d\n", i, g->bitblock);
270 blockput(b);
273 return 0;
275 error:
276 blockput(b);
277 return -1;
280 static void
281 mkhandle(Nfs3Handle *h, u64int ino)
283 h->h[0] = ino>>24;
284 h->h[1] = ino>>16;
285 h->h[2] = ino>>8;
286 h->h[3] = ino;
287 h->len = 4;
290 static u32int
291 byte2u32(uchar *p)
293 return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
296 static Nfs3Status
297 handle2ino(Ext2 *fs, Nfs3Handle *h, u32int *pinum, Inode *ino)
299 int i;
300 uint ioff;
301 u32int inum;
302 u32int addr;
303 Block *gb, *b;
304 Group *g;
306 if(h->len != 4)
307 return Nfs3ErrBadHandle;
308 inum = byte2u32(h->h);
309 if(pinum)
310 *pinum = inum;
311 i = (inum-1) / fs->inospergroup;
312 if(i >= fs->ngroup)
313 return Nfs3ErrBadHandle;
314 ioff = (inum-1) % fs->inospergroup;
315 if((g = ext2group(fs, i, &gb)) == nil)
316 return Nfs3ErrIo;
317 addr = g->inodeaddr + ioff/fs->inosperblock;
318 blockput(gb);
319 if((b = diskread(fs->disk, fs->blocksize, (u64int)addr*fs->blocksize)) == nil)
320 return Nfs3ErrIo;
321 *ino = ((Inode*)b->data)[ioff%fs->inosperblock];
322 blockput(b);
323 return Nfs3Ok;
326 static Nfs3Status
327 ext2root(Fsys *fsys, Nfs3Handle *h)
329 USED(fsys);
330 mkhandle(h, ROOTINODE);
331 return Nfs3Ok;
334 static Nfs3Status
335 ino2attr(Ext2 *fs, Inode *ino, u32int inum, Nfs3Attr *attr)
337 u32int rdev;
339 attr->type = -1;
340 switch(ino->mode&IFMT){
341 case IFIFO:
342 attr->type = Nfs3FileFifo;
343 break;
344 case IFCHR:
345 attr->type = Nfs3FileChar;
346 break;
347 case IFDIR:
348 attr->type = Nfs3FileDir;
349 break;
350 case IFBLK:
351 attr->type = Nfs3FileBlock;
352 break;
353 case IFREG:
354 attr->type = Nfs3FileReg;
355 break;
356 case IFLNK:
357 attr->type = Nfs3FileSymlink;
358 break;
359 case IFSOCK:
360 attr->type = Nfs3FileSocket;
361 break;
362 case IFWHT:
363 default:
364 return Nfs3ErrBadHandle;
367 attr->mode = ino->mode&07777;
368 attr->nlink = ino->nlink;
369 attr->uid = ino->uid;
370 attr->gid = ino->gid;
371 attr->size = ino->size;
372 attr->used = ino->nblock*fs->blocksize;
373 if(attr->type==Nfs3FileBlock || attr->type==Nfs3FileChar){
374 rdev = ino->block[0];
375 attr->major = (rdev>>8)&0xFF;
376 attr->minor = rdev & 0xFFFF00FF;
377 }else{
378 attr->major = 0;
379 attr->minor = 0;
381 attr->fsid = 0;
382 attr->fileid = inum;
383 attr->atime.sec = ino->atime;
384 attr->atime.nsec = 0;
385 attr->mtime.sec = ino->mtime;
386 attr->mtime.nsec = 0;
387 attr->ctime.sec = ino->ctime;
388 attr->ctime.nsec = 0;
389 return Nfs3Ok;
392 static int
393 ingroup(SunAuthUnix *au, uint gid)
395 int i;
397 for(i=0; i<au->ng; i++)
398 if(au->g[i] == gid)
399 return 1;
400 return 0;
403 static Nfs3Status
404 inoperm(Inode *ino, SunAuthUnix *au, int need)
406 int have;
408 if(allowall)
409 return Nfs3Ok;
411 have = ino->mode&0777;
412 if(ino->uid == au->uid)
413 have >>= 6;
414 else if(ino->gid == au->gid || ingroup(au, ino->gid))
415 have >>= 3;
417 if((have&need) != need)
418 return Nfs3ErrNotOwner; /* really EPERM */
419 return Nfs3Ok;
422 static Nfs3Status
423 ext2getattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
425 Inode ino;
426 u32int inum;
427 Ext2 *fs;
428 Nfs3Status ok;
430 fs = fsys->priv;
431 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
432 return ok;
434 USED(au); /* anyone can getattr */
435 return ino2attr(fs, &ino, inum, attr);
438 static Nfs3Status
439 ext2access(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
441 int have;
442 Inode ino;
443 u32int inum;
444 Ext2 *fs;
445 Nfs3Status ok;
447 fs = fsys->priv;
448 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
449 return ok;
451 have = ino.mode&0777;
452 if(ino.uid == au->uid)
453 have >>= 6;
454 else if(ino.gid == au->gid || ingroup(au, ino.gid))
455 have >>= 3;
457 *got = 0;
458 if((want&Nfs3AccessRead) && (have&AREAD))
459 *got |= Nfs3AccessRead;
460 if((want&Nfs3AccessLookup) && (ino.mode&IFMT)==IFDIR && (have&AEXEC))
461 *got |= Nfs3AccessLookup;
462 if((want&Nfs3AccessExecute) && (ino.mode&IFMT)!=IFDIR && (have&AEXEC))
463 *got |= Nfs3AccessExecute;
465 return ino2attr(fs, &ino, inum, attr);
468 static Nfs3Status
469 ext2lookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
471 u32int nblock;
472 u32int i;
473 uchar *p, *ep;
474 Dirent *de;
475 Inode ino;
476 Block *b;
477 Ext2 *fs;
478 Nfs3Status ok;
479 int len, want;
481 fs = fsys->priv;
482 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
483 return ok;
485 if((ino.mode&IFMT) != IFDIR)
486 return Nfs3ErrNotDir;
488 if((ok = inoperm(&ino, au, AEXEC)) != Nfs3Ok)
489 return ok;
491 len = strlen(name);
492 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
493 if(debug) fprint(2, "%d blocks in dir...", nblock);
494 for(i=0; i<nblock; i++){
495 if(i==nblock-1)
496 want = ino.size % fs->blocksize;
497 else
498 want = fs->blocksize;
499 b = ext2fileblock(fs, &ino, i, want);
500 if(b == nil){
501 if(debug) fprint(2, "empty block...");
502 continue;
504 p = b->data;
505 ep = p+b->len;
506 while(p < ep){
507 de = (Dirent*)p;
508 if(de->reclen == 0){
509 if(debug)
510 fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
511 break;
513 p += de->reclen;
514 if(p > ep){
515 if(debug)
516 fprint(2, "bad len %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
517 break;
519 if(de->ino == 0)
520 continue;
521 if(4+2+2+de->namlen > de->reclen){
522 if(debug)
523 fprint(2, "bad namelen %d at offset %d of %d\n", de->namlen, (int)(p-b->data), b->len);
524 break;
526 if(de->namlen == len && memcmp(de->name, name, len) == 0){
527 mkhandle(nh, de->ino);
528 blockput(b);
529 return Nfs3Ok;
532 blockput(b);
534 return Nfs3ErrNoEnt;
537 static Nfs3Status
538 ext2readdir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
540 u32int nblock;
541 u32int i;
542 int off, done;
543 uchar *data, *dp, *dep, *p, *ep, *ndp;
544 Dirent *de;
545 Inode ino;
546 Block *b;
547 Ext2 *fs;
548 Nfs3Status ok;
549 Nfs3Entry e;
550 int want;
552 fs = fsys->priv;
553 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
554 return ok;
556 if((ino.mode&IFMT) != IFDIR)
557 return Nfs3ErrNotDir;
559 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
560 return ok;
562 if(cookie >= ino.size){
563 *pcount = 0;
564 *pdata = 0;
565 return Nfs3Ok;
568 dp = malloc(count);
569 data = dp;
570 if(dp == nil)
571 return Nfs3ErrNoMem;
572 dep = dp+count;
573 *peof = 0;
574 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
575 i = cookie/fs->blocksize;
576 off = cookie%fs->blocksize;
577 done = 0;
578 for(; i<nblock && !done; i++){
579 if(i==nblock-1)
580 want = ino.size % fs->blocksize;
581 else
582 want = fs->blocksize;
583 b = ext2fileblock(fs, &ino, i, want);
584 if(b == nil)
585 continue;
586 p = b->data;
587 ep = p+b->len;
588 memset(&e, 0, sizeof e);
589 while(p < ep){
590 de = (Dirent*)p;
591 if(de->reclen == 0){
592 if(debug) fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
593 break;
595 p += de->reclen;
596 if(p > ep){
597 if(debug) fprint(2, "reclen %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
598 break;
600 if(de->ino == 0){
601 if(debug) fprint(2, "zero inode\n");
602 continue;
604 if(4+2+2+de->namlen > de->reclen){
605 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);
606 break;
608 if(de->name[de->namlen] != 0){
609 if(debug) fprint(2, "bad name %d %.*s\n", de->namlen, de->namlen, de->name);
610 continue;
612 if(debug) print("%s/%d ", de->name, (int)de->ino);
613 if((uchar*)de - b->data < off)
614 continue;
615 e.fileid = de->ino;
616 e.name = de->name;
617 e.cookie = (u64int)i*fs->blocksize + (p - b->data);
618 if(nfs3entrypack(dp, dep, &ndp, &e) < 0){
619 done = 1;
620 break;
622 dp = ndp;
624 off = 0;
625 blockput(b);
627 if(i==nblock)
628 *peof = 1;
630 *pcount = dp - data;
631 *pdata = data;
632 return Nfs3Ok;
635 static Nfs3Status
636 ext2readfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count,
637 u64int offset, uchar **pdata, u32int *pcount, u1int *peof)
639 uchar *data;
640 Block *b;
641 Ext2 *fs;
642 int off, want, fragcount;
643 Inode ino;
644 Nfs3Status ok;
646 fs = fsys->priv;
647 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
648 return ok;
650 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
651 return ok;
653 if(offset >= ino.size){
654 *pdata = 0;
655 *pcount = 0;
656 *peof = 1;
657 return Nfs3Ok;
659 if(offset+count > ino.size)
660 count = ino.size-offset;
661 if(offset/fs->blocksize != (offset+count-1)/fs->blocksize)
662 count = fs->blocksize - offset%fs->blocksize;
664 data = malloc(count);
665 if(data == nil)
666 return Nfs3ErrNoMem;
668 want = offset%fs->blocksize+count;
669 if(want%fs->blocksize)
670 want += fs->blocksize - want%fs->blocksize;
672 b = ext2fileblock(fs, &ino, offset/fs->blocksize, want);
673 if(b == nil){
674 /* BUG: distinguish sparse file from I/O error */
675 memset(data, 0, count);
676 }else{
677 off = offset%fs->blocksize;
678 fragcount = count; /* need signed variable */
679 if(off+fragcount > b->len){
680 fragcount = b->len - off;
681 if(fragcount < 0)
682 fragcount = 0;
684 if(fragcount > 0)
685 memmove(data, b->data+off, fragcount);
686 count = fragcount;
687 blockput(b);
689 *peof = (offset+count == ino.size);
690 *pcount = count;
691 *pdata = data;
692 return Nfs3Ok;
695 static Nfs3Status
696 ext2readlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link)
698 Ext2 *fs;
699 Nfs3Status ok;
700 int len;
701 Inode ino;
702 Block *b;
704 fs = fsys->priv;
705 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
706 return ok;
707 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
708 return ok;
710 if(ino.size > 1024)
711 return Nfs3ErrIo;
712 len = ino.size;
714 if(ino.nblock != 0){
715 /* BUG: assumes symlink fits in one block */
716 b = ext2fileblock(fs, &ino, 0, len);
717 if(b == nil)
718 return Nfs3ErrIo;
719 if(memchr(b->data, 0, len) != nil){
720 blockput(b);
721 return Nfs3ErrIo;
723 *link = malloc(len+1);
724 if(*link == 0){
725 blockput(b);
726 return Nfs3ErrNoMem;
728 memmove(*link, b->data, len);
729 (*link)[len] = 0;
730 blockput(b);
731 return Nfs3Ok;
734 if(len > sizeof ino.block)
735 return Nfs3ErrIo;
737 *link = malloc(len+1);
738 if(*link == 0)
739 return Nfs3ErrNoMem;
740 memmove(*link, ino.block, ino.size);
741 (*link)[len] = 0;
742 return Nfs3Ok;