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 return ext2blockread(fs->fsys, bno+fs->firstblock);
143 static Block*
144 ext2fileblock(Ext2 *fs, Inode *ino, u32int bno, int size)
146 int ppb;
147 Block *b;
148 u32int *a;
149 u32int obno;
151 obno = bno;
152 if(bno < NDIRBLOCKS){
153 if(debug)
154 fprint(2, "fileblock %d -> %d...",
155 bno, ino->block[bno]);
156 return ext2datablock(fs, ino->block[bno], size);
158 bno -= NDIRBLOCKS;
159 ppb = fs->blocksize/4;
161 /* one indirect */
162 if(bno < ppb){
163 b = ext2datablock(fs, ino->block[INDBLOCK], fs->blocksize);
164 if(b == nil)
165 return nil;
166 a = (u32int*)b->data;
167 bno = a[bno%ppb];
168 blockput(b);
169 return ext2datablock(fs, bno, size);
171 bno -= ppb;
173 /* one double indirect */
174 if(bno < ppb*ppb){
175 b = ext2datablock(fs, ino->block[DINDBLOCK], fs->blocksize);
176 if(b == nil)
177 return nil;
178 a = (u32int*)b->data;
179 bno = a[(bno/ppb)%ppb];
180 blockput(b);
181 b = ext2datablock(fs, bno, fs->blocksize);
182 if(b == nil)
183 return nil;
184 a = (u32int*)b->data;
185 bno = a[bno%ppb];
186 blockput(b);
187 return ext2datablock(fs, bno, size);
189 bno -= ppb*ppb;
191 /* one triple indirect */
192 if(bno < ppb*ppb*ppb){
193 b = ext2datablock(fs, ino->block[TINDBLOCK], fs->blocksize);
194 if(b == nil)
195 return nil;
196 a = (u32int*)b->data;
197 bno = a[(bno/(ppb*ppb))%ppb];
198 blockput(b);
199 b = ext2datablock(fs, bno, fs->blocksize);
200 if(b == nil)
201 return nil;
202 a = (u32int*)b->data;
203 bno = a[(bno/ppb)%ppb];
204 blockput(b);
205 b = ext2datablock(fs, bno, fs->blocksize);
206 if(b == nil)
207 return nil;
208 a = (u32int*)b->data;
209 bno = a[bno%ppb];
210 blockput(b);
211 return ext2datablock(fs, bno, size);
214 fprint(2, "ext2fileblock %llud: too big\n", obno);
215 return nil;
218 static int
219 checksuper(Super *super)
221 if(super->magic != SUPERMAGIC){
222 werrstr("bad magic 0x%ux wanted 0x%ux", super->magic, SUPERMAGIC);
223 return -1;
225 return 0;
228 static int
229 ext2sync(Fsys *fsys)
231 int i;
232 Group *g;
233 Block *b;
234 Super *super;
235 Ext2 *fs;
236 Disk *disk;
238 fs = fsys->priv;
239 disk = fs->disk;
240 if((b = diskread(disk, SBSIZE, SBOFF)) == nil)
241 goto error;
242 super = (Super*)b->data;
243 if(checksuper(super) < 0)
244 goto error;
245 fs->blocksize = MINBLOCKSIZE<<super->logblocksize;
246 fs->nblock = super->nblock;
247 fs->ngroup = (super->nblock+super->blockspergroup-1)
248 / super->blockspergroup;
249 fs->inospergroup = super->inospergroup;
250 fs->blockspergroup = super->blockspergroup;
251 fs->inosperblock = fs->blocksize / InodeSize;
252 if(fs->blocksize == SBOFF)
253 fs->groupaddr = 2;
254 else
255 fs->groupaddr = 1;
256 fs->descperblock = fs->blocksize / GroupSize;
257 fs->firstblock = super->firstdatablock;
258 blockput(b);
260 fsys->blocksize = fs->blocksize;
261 fsys->nblock = fs->nblock;
262 fprint(2, "ext2 %d %d-byte blocks, first data block %d, %d groups of %d\n",
263 fs->nblock, fs->blocksize, fs->firstblock, fs->ngroup, fs->blockspergroup);
265 if(0){
266 for(i=0; i<fs->ngroup; i++)
267 if((g = ext2group(fs, i, &b)) != nil){
268 fprint(2, "grp %d: bitblock=%d\n", i, g->bitblock);
269 blockput(b);
272 return 0;
274 error:
275 blockput(b);
276 return -1;
279 static void
280 mkhandle(Nfs3Handle *h, u64int ino)
282 h->h[0] = ino>>24;
283 h->h[1] = ino>>16;
284 h->h[2] = ino>>8;
285 h->h[3] = ino;
286 h->len = 4;
289 static u32int
290 byte2u32(uchar *p)
292 return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
295 static Nfs3Status
296 handle2ino(Ext2 *fs, Nfs3Handle *h, u32int *pinum, Inode *ino)
298 int i;
299 uint ioff;
300 u32int inum;
301 u32int addr;
302 Block *gb, *b;
303 Group *g;
305 if(h->len != 4)
306 return Nfs3ErrBadHandle;
307 inum = byte2u32(h->h);
308 if(pinum)
309 *pinum = inum;
310 i = (inum-1) / fs->inospergroup;
311 if(i >= fs->ngroup)
312 return Nfs3ErrBadHandle;
313 ioff = (inum-1) % fs->inospergroup;
314 if((g = ext2group(fs, i, &gb)) == nil)
315 return Nfs3ErrIo;
316 addr = g->inodeaddr + ioff/fs->inosperblock;
317 blockput(gb);
318 if((b = diskread(fs->disk, fs->blocksize, (u64int)addr*fs->blocksize)) == nil)
319 return Nfs3ErrIo;
320 *ino = ((Inode*)b->data)[ioff%fs->inosperblock];
321 blockput(b);
322 return Nfs3Ok;
325 static Nfs3Status
326 ext2root(Fsys *fsys, Nfs3Handle *h)
328 mkhandle(h, ROOTINODE);
329 return Nfs3Ok;
332 static Nfs3Status
333 ino2attr(Ext2 *fs, Inode *ino, u32int inum, Nfs3Attr *attr)
335 u32int rdev;
337 attr->type = -1;
338 switch(ino->mode&IFMT){
339 case IFIFO:
340 attr->type = Nfs3FileFifo;
341 break;
342 case IFCHR:
343 attr->type = Nfs3FileChar;
344 break;
345 case IFDIR:
346 attr->type = Nfs3FileDir;
347 break;
348 case IFBLK:
349 attr->type = Nfs3FileBlock;
350 break;
351 case IFREG:
352 attr->type = Nfs3FileReg;
353 break;
354 case IFLNK:
355 attr->type = Nfs3FileSymlink;
356 break;
357 case IFSOCK:
358 attr->type = Nfs3FileSocket;
359 break;
360 case IFWHT:
361 default:
362 return Nfs3ErrBadHandle;
365 attr->mode = ino->mode&07777;
366 attr->nlink = ino->nlink;
367 attr->uid = ino->uid;
368 attr->gid = ino->gid;
369 attr->size = ino->size;
370 attr->used = ino->nblock*fs->blocksize;
371 if(attr->type==Nfs3FileBlock || attr->type==Nfs3FileChar){
372 rdev = ino->block[0];
373 attr->major = (rdev>>8)&0xFF;
374 attr->minor = rdev & 0xFFFF00FF;
375 }else{
376 attr->major = 0;
377 attr->minor = 0;
379 attr->fsid = 0;
380 attr->fileid = inum;
381 attr->atime.sec = ino->atime;
382 attr->atime.nsec = 0;
383 attr->mtime.sec = ino->mtime;
384 attr->mtime.nsec = 0;
385 attr->ctime.sec = ino->ctime;
386 attr->ctime.nsec = 0;
387 return Nfs3Ok;
390 static int
391 ingroup(SunAuthUnix *au, uint gid)
393 int i;
395 for(i=0; i<au->ng; i++)
396 if(au->g[i] == gid)
397 return 1;
398 return 0;
401 static Nfs3Status
402 inoperm(Inode *ino, SunAuthUnix *au, int need)
404 int have;
406 if(allowall)
407 return Nfs3Ok;
409 have = ino->mode&0777;
410 if(ino->uid == au->uid)
411 have >>= 6;
412 else if(ino->gid == au->gid || ingroup(au, ino->gid))
413 have >>= 3;
415 if((have&need) != need)
416 return Nfs3ErrNotOwner; /* really EPERM */
417 return Nfs3Ok;
420 static Nfs3Status
421 ext2getattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
423 Inode ino;
424 u32int inum;
425 Ext2 *fs;
426 Nfs3Status ok;
428 fs = fsys->priv;
429 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
430 return ok;
432 USED(au); /* anyone can getattr */
433 return ino2attr(fs, &ino, inum, attr);
436 static Nfs3Status
437 ext2access(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
439 int have;
440 Inode ino;
441 u32int inum;
442 Ext2 *fs;
443 Nfs3Status ok;
445 fs = fsys->priv;
446 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
447 return ok;
449 have = ino.mode&0777;
450 if(ino.uid == au->uid)
451 have >>= 6;
452 else if(ino.gid == au->gid || ingroup(au, ino.gid))
453 have >>= 3;
455 *got = 0;
456 if((want&Nfs3AccessRead) && (have&AREAD))
457 *got |= Nfs3AccessRead;
458 if((want&Nfs3AccessLookup) && (ino.mode&IFMT)==IFDIR && (have&AEXEC))
459 *got |= Nfs3AccessLookup;
460 if((want&Nfs3AccessExecute) && (ino.mode&IFMT)!=IFDIR && (have&AEXEC))
461 *got |= Nfs3AccessExecute;
463 return ino2attr(fs, &ino, inum, attr);
466 static Nfs3Status
467 ext2lookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
469 u32int nblock;
470 u32int i;
471 uchar *p, *ep;
472 Dirent *de;
473 Inode ino;
474 Block *b;
475 Ext2 *fs;
476 Nfs3Status ok;
477 int len, want;
479 fs = fsys->priv;
480 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
481 return ok;
483 if((ino.mode&IFMT) != IFDIR)
484 return Nfs3ErrNotDir;
486 if((ok = inoperm(&ino, au, AEXEC)) != Nfs3Ok)
487 return ok;
489 len = strlen(name);
490 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
491 if(debug) fprint(2, "%d blocks in dir...", nblock);
492 for(i=0; i<nblock; i++){
493 if(i==nblock-1)
494 want = ino.size % fs->blocksize;
495 else
496 want = fs->blocksize;
497 b = ext2fileblock(fs, &ino, i, want);
498 if(b == nil){
499 if(debug) fprint(2, "empty block...");
500 continue;
502 p = b->data;
503 ep = p+b->len;
504 while(p < ep){
505 de = (Dirent*)p;
506 if(de->reclen == 0){
507 if(debug)
508 fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
509 break;
511 p += de->reclen;
512 if(p > ep){
513 if(debug)
514 fprint(2, "bad len %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
515 break;
517 if(de->ino == 0)
518 continue;
519 if(4+2+2+de->namlen > de->reclen){
520 if(debug)
521 fprint(2, "bad namelen %d at offset %d of %d\n", de->namlen, (int)(p-b->data), b->len);
522 break;
524 if(de->namlen == len && memcmp(de->name, name, len) == 0){
525 mkhandle(nh, de->ino);
526 blockput(b);
527 return Nfs3Ok;
530 blockput(b);
532 return Nfs3ErrNoEnt;
535 static Nfs3Status
536 ext2readdir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
538 u32int nblock;
539 u32int i;
540 int off, done;
541 uchar *data, *dp, *dep, *p, *ep, *ndp;
542 Dirent *de;
543 Inode ino;
544 Block *b;
545 Ext2 *fs;
546 Nfs3Status ok;
547 Nfs3Entry e;
548 int want;
550 fs = fsys->priv;
551 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
552 return ok;
554 if((ino.mode&IFMT) != IFDIR)
555 return Nfs3ErrNotDir;
557 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
558 return ok;
560 if(cookie >= ino.size){
561 *pcount = 0;
562 *pdata = 0;
563 return Nfs3Ok;
566 dp = malloc(count);
567 data = dp;
568 if(dp == nil)
569 return Nfs3ErrNoMem;
570 dep = dp+count;
571 *peof = 0;
572 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
573 i = cookie/fs->blocksize;
574 off = cookie%fs->blocksize;
575 done = 0;
576 for(; i<nblock && !done; i++){
577 if(i==nblock-1)
578 want = ino.size % fs->blocksize;
579 else
580 want = fs->blocksize;
581 b = ext2fileblock(fs, &ino, i, want);
582 if(b == nil)
583 continue;
584 p = b->data;
585 ep = p+b->len;
586 memset(&e, 0, sizeof e);
587 while(p < ep){
588 de = (Dirent*)p;
589 if(de->reclen == 0){
590 if(debug) fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
591 break;
593 p += de->reclen;
594 if(p > ep){
595 if(debug) fprint(2, "reclen %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
596 break;
598 if(de->ino == 0){
599 if(debug) fprint(2, "zero inode\n");
600 continue;
602 if(4+2+2+de->namlen > de->reclen){
603 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);
604 break;
606 if(de->name[de->namlen] != 0){
607 if(debug) fprint(2, "bad name %d %.*s\n", de->namlen, de->namlen, de->name);
608 continue;
610 if(debug) print("%s/%d ", de->name, (int)de->ino);
611 if((uchar*)de - b->data < off)
612 continue;
613 e.fileid = de->ino;
614 e.name = de->name;
615 e.cookie = (u64int)i*fs->blocksize + (p - b->data);
616 if(nfs3entrypack(dp, dep, &ndp, &e) < 0){
617 done = 1;
618 break;
620 dp = ndp;
622 off = 0;
623 blockput(b);
625 if(i==nblock)
626 *peof = 1;
628 *pcount = dp - data;
629 *pdata = data;
630 return Nfs3Ok;
633 static Nfs3Status
634 ext2readfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count,
635 u64int offset, uchar **pdata, u32int *pcount, u1int *peof)
637 uchar *data;
638 Block *b;
639 Ext2 *fs;
640 int off, want, fragcount;
641 Inode ino;
642 Nfs3Status ok;
644 fs = fsys->priv;
645 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
646 return ok;
648 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
649 return ok;
651 if(offset >= ino.size){
652 *pdata = 0;
653 *pcount = 0;
654 *peof = 1;
655 return Nfs3Ok;
657 if(offset+count > ino.size)
658 count = ino.size-offset;
659 if(offset/fs->blocksize != (offset+count-1)/fs->blocksize)
660 count = fs->blocksize - offset%fs->blocksize;
662 data = malloc(count);
663 if(data == nil)
664 return Nfs3ErrNoMem;
666 want = offset%fs->blocksize+count;
667 if(want%fs->blocksize)
668 want += fs->blocksize - want%fs->blocksize;
670 b = ext2fileblock(fs, &ino, offset/fs->blocksize, want);
671 if(b == nil){
672 /* BUG: distinguish sparse file from I/O error */
673 memset(data, 0, count);
674 }else{
675 off = offset%fs->blocksize;
676 fragcount = count; /* need signed variable */
677 if(off+fragcount > b->len){
678 fragcount = b->len - off;
679 if(fragcount < 0)
680 fragcount = 0;
682 if(fragcount > 0)
683 memmove(data, b->data+off, fragcount);
684 count = fragcount;
685 blockput(b);
687 *peof = (offset+count == ino.size);
688 *pcount = count;
689 *pdata = data;
690 return Nfs3Ok;
693 static Nfs3Status
694 ext2readlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link)
696 Ext2 *fs;
697 Nfs3Status ok;
698 int len;
699 Inode ino;
700 Block *b;
702 fs = fsys->priv;
703 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
704 return ok;
705 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
706 return ok;
708 if(ino.size > 1024)
709 return Nfs3ErrIo;
710 len = ino.size;
712 if(ino.nblock != 0){
713 /* BUG: assumes symlink fits in one block */
714 b = ext2fileblock(fs, &ino, 0, len);
715 if(b == nil)
716 return Nfs3ErrIo;
717 if(memchr(b->data, 0, len) != nil){
718 blockput(b);
719 return Nfs3ErrIo;
721 *link = malloc(len+1);
722 if(*link == 0){
723 blockput(b);
724 return Nfs3ErrNoMem;
726 memmove(*link, b->data, len);
727 (*link)[len] = 0;
728 blockput(b);
729 return Nfs3Ok;
732 if(len > sizeof ino.block)
733 return Nfs3ErrIo;
735 *link = malloc(len+1);
736 if(*link == 0)
737 return Nfs3ErrNoMem;
738 memmove(*link, ino.block, ino.size);
739 (*link)[len] = 0;
740 return Nfs3Ok;