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 0
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;
44 fsys->_close = ext2close;
46 if(ext2sync(fsys) < 0)
47 goto error;
49 return fsys;
51 error:
52 ext2close(fsys);
53 return nil;
54 }
56 static void
57 ext2close(Fsys *fsys)
58 {
59 Ext2 *fs;
61 fs = fsys->priv;
62 free(fs);
63 free(fsys);
64 }
66 static Group*
67 ext2group(Ext2 *fs, u32int i, Block **pb)
68 {
69 Block *b;
70 u64int addr;
71 Group *g;
73 if(i >= fs->ngroup)
74 return nil;
76 addr = fs->groupaddr + i/fs->descperblock;
77 b = diskread(fs->disk, fs->blocksize, addr*fs->blocksize);
78 if(b == nil)
79 return nil;
80 g = (Group*)(b->data+i%fs->descperblock*GroupSize);
81 *pb = b;
82 return g;
83 }
85 static Block*
86 ext2blockread(Fsys *fsys, u64int vbno)
87 {
88 Block *bitb;
89 Group *g;
90 Block *gb;
91 uchar *bits;
92 u32int bno, boff, bitblock;
93 u64int bitpos;
94 Ext2 *fs;
96 fs = fsys->priv;
97 if(vbno >= fs->nblock)
98 return nil;
99 bno = vbno;
100 if(bno != vbno)
101 return nil;
103 /*
104 if(bno < fs->firstblock)
105 return diskread(fs->disk, fs->blocksize, (u64int)bno*fs->blocksize);
106 */
107 if(bno < fs->firstblock)
108 return nil;
110 bno -= fs->firstblock;
111 if((g = ext2group(fs, bno/fs->blockspergroup, &gb)) == nil){
112 if(debug)
113 fprint(2, "loading group: %r...");
114 return nil;
116 /*
117 if(debug)
118 fprint(2, "ext2 group %d: bitblock=%ud inodebitblock=%ud inodeaddr=%ud freeblocks=%ud freeinodes=%ud useddirs=%ud\n",
119 (int)(bno/fs->blockspergroup),
120 g->bitblock,
121 g->inodebitblock,
122 g->inodeaddr,
123 g->freeblockscount,
124 g->freeinodescount,
125 g->useddirscount);
126 if(debug)
127 fprint(2, "group %d bitblock=%d...", bno/fs->blockspergroup, g->bitblock);
128 */
129 bitblock = g->bitblock;
130 bitpos = (u64int)bitblock*fs->blocksize;
131 blockput(gb);
133 if((bitb = diskread(fs->disk, fs->blocksize, bitpos)) == nil){
134 if(debug)
135 fprint(2, "loading bitblock: %r...");
136 return nil;
138 bits = bitb->data;
139 boff = bno%fs->blockspergroup;
140 if((bits[boff>>3] & (1<<(boff&7))) == 0){
141 if(debug)
142 fprint(2, "block %d not allocated in group %d: bitblock %d/%lld bits[%d] = %#x\n",
143 boff, bno/fs->blockspergroup,
144 (int)bitblock,
145 bitpos,
146 boff>>3,
147 bits[boff>>3]);
148 blockput(bitb);
149 return nil;
151 blockput(bitb);
153 bno += fs->firstblock;
154 return diskread(fs->disk, fs->blocksize, (u64int)bno*fs->blocksize);
157 static Block*
158 ext2datablock(Ext2 *fs, u32int bno, int size)
160 USED(size);
161 return ext2blockread(fs->fsys, bno);
164 static Block*
165 ext2fileblock(Ext2 *fs, Inode *ino, u32int bno, int size)
167 int ppb;
168 Block *b;
169 u32int *a;
170 u32int obno, pbno;
172 obno = bno;
173 if(bno < NDIRBLOCKS){
174 if(debug)
175 fprint(2, "fileblock %d -> %d...",
176 bno, ino->block[bno]);
177 return ext2datablock(fs, ino->block[bno], size);
179 bno -= NDIRBLOCKS;
180 ppb = fs->blocksize/4;
182 /* one indirect */
183 if(bno < ppb){
184 b = ext2datablock(fs, ino->block[INDBLOCK], fs->blocksize);
185 if(b == nil)
186 return nil;
187 a = (u32int*)b->data;
188 bno = a[bno];
189 blockput(b);
190 return ext2datablock(fs, bno, size);
192 bno -= ppb;
194 /* one double indirect */
195 if(bno < ppb*ppb){
196 b = ext2datablock(fs, ino->block[DINDBLOCK], fs->blocksize);
197 if(b == nil)
198 return nil;
199 a = (u32int*)b->data;
200 pbno = a[bno/ppb];
201 bno = bno%ppb;
202 blockput(b);
203 b = ext2datablock(fs, pbno, fs->blocksize);
204 if(b == nil)
205 return nil;
206 a = (u32int*)b->data;
207 bno = a[bno];
208 blockput(b);
209 return ext2datablock(fs, bno, size);
211 bno -= ppb*ppb;
213 /* one triple indirect */
214 if(bno < ppb*ppb*ppb){
215 b = ext2datablock(fs, ino->block[TINDBLOCK], fs->blocksize);
216 if(b == nil)
217 return nil;
218 a = (u32int*)b->data;
219 pbno = a[bno/(ppb*ppb)];
220 bno = bno%(ppb*ppb);
221 blockput(b);
222 b = ext2datablock(fs, pbno, fs->blocksize);
223 if(b == nil)
224 return nil;
225 a = (u32int*)b->data;
226 pbno = a[bno/ppb];
227 bno = bno%ppb;
228 blockput(b);
229 b = ext2datablock(fs, pbno, fs->blocksize);
230 if(b == nil)
231 return nil;
232 a = (u32int*)b->data;
233 bno = a[bno];
234 blockput(b);
235 return ext2datablock(fs, bno, size);
238 fprint(2, "ext2fileblock %ud: too big\n", obno);
239 return nil;
242 static int
243 checksuper(Super *super)
245 if(super->magic != SUPERMAGIC){
246 werrstr("bad magic 0x%ux wanted 0x%ux", super->magic, SUPERMAGIC);
247 return -1;
249 return 0;
252 static int
253 ext2sync(Fsys *fsys)
255 int i;
256 Group *g;
257 Block *b;
258 Super *super;
259 Ext2 *fs;
260 Disk *disk;
262 fs = fsys->priv;
263 disk = fs->disk;
264 if((b = diskread(disk, SBSIZE, SBOFF)) == nil)
265 goto error;
266 super = (Super*)b->data;
267 if(checksuper(super) < 0)
268 goto error;
269 fs->blocksize = MINBLOCKSIZE<<super->logblocksize;
270 fs->nblock = super->nblock;
271 fs->ngroup = (super->nblock+super->blockspergroup-1)
272 / super->blockspergroup;
273 fs->inospergroup = super->inospergroup;
274 fs->blockspergroup = super->blockspergroup;
275 fs->inosperblock = fs->blocksize / InodeSize;
276 if(fs->blocksize == SBOFF)
277 fs->groupaddr = 2;
278 else
279 fs->groupaddr = 1;
280 fs->descperblock = fs->blocksize / GroupSize;
281 fs->firstblock = super->firstdatablock;
282 blockput(b);
284 fsys->blocksize = fs->blocksize;
285 fsys->nblock = fs->nblock;
286 fprint(2, "ext2 %d %d-byte blocks, first data block %d, %d groups of %d\n",
287 fs->nblock, fs->blocksize, fs->firstblock, fs->ngroup, fs->blockspergroup);
289 if(0){
290 for(i=0; i<fs->ngroup; i++)
291 if((g = ext2group(fs, i, &b)) != nil){
292 fprint(2, "grp %d: bitblock=%d\n", i, g->bitblock);
293 blockput(b);
296 return 0;
298 error:
299 blockput(b);
300 return -1;
303 static void
304 mkhandle(Nfs3Handle *h, u64int ino)
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;
313 static u32int
314 byte2u32(uchar *p)
316 return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
319 static Nfs3Status
320 handle2ino(Ext2 *fs, Nfs3Handle *h, u32int *pinum, Inode *ino)
322 int i;
323 uint ioff;
324 u32int inum;
325 u32int addr;
326 Block *gb, *b;
327 Group *g;
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((g = ext2group(fs, i, &gb)) == nil)
339 return Nfs3ErrIo;
340 addr = g->inodeaddr + ioff/fs->inosperblock;
341 blockput(gb);
342 if((b = diskread(fs->disk, fs->blocksize, (u64int)addr*fs->blocksize)) == nil)
343 return Nfs3ErrIo;
344 *ino = ((Inode*)b->data)[ioff%fs->inosperblock];
345 blockput(b);
346 return Nfs3Ok;
349 static Nfs3Status
350 ext2root(Fsys *fsys, Nfs3Handle *h)
352 USED(fsys);
353 mkhandle(h, ROOTINODE);
354 return Nfs3Ok;
357 static u64int
358 inosize(Inode* ino)
360 u64int size;
362 size = ino->size;
363 if((ino->mode&IFMT)==IFREG)
364 size |= (u64int)ino->diracl << 32;
365 return size;
368 static Nfs3Status
369 ino2attr(Ext2 *fs, Inode *ino, u32int inum, Nfs3Attr *attr)
371 u32int rdev;
373 attr->type = -1;
374 switch(ino->mode&IFMT){
375 case IFIFO:
376 attr->type = Nfs3FileFifo;
377 break;
378 case IFCHR:
379 attr->type = Nfs3FileChar;
380 break;
381 case IFDIR:
382 attr->type = Nfs3FileDir;
383 break;
384 case IFBLK:
385 attr->type = Nfs3FileBlock;
386 break;
387 case IFREG:
388 attr->type = Nfs3FileReg;
389 break;
390 case IFLNK:
391 attr->type = Nfs3FileSymlink;
392 break;
393 case IFSOCK:
394 attr->type = Nfs3FileSocket;
395 break;
396 case IFWHT:
397 default:
398 return Nfs3ErrBadHandle;
401 attr->mode = ino->mode&07777;
402 attr->nlink = ino->nlink;
403 attr->uid = ino->uid;
404 attr->gid = ino->gid;
405 attr->size = inosize(ino);
406 attr->used = (u64int)ino->nblock*fs->blocksize;
407 if(attr->type==Nfs3FileBlock || attr->type==Nfs3FileChar){
408 rdev = ino->block[0];
409 attr->major = (rdev>>8)&0xFF;
410 attr->minor = rdev & 0xFFFF00FF;
411 }else{
412 attr->major = 0;
413 attr->minor = 0;
415 attr->fsid = 0;
416 attr->fileid = inum;
417 attr->atime.sec = ino->atime;
418 attr->atime.nsec = 0;
419 attr->mtime.sec = ino->mtime;
420 attr->mtime.nsec = 0;
421 attr->ctime.sec = ino->ctime;
422 attr->ctime.nsec = 0;
423 return Nfs3Ok;
426 static int
427 ingroup(SunAuthUnix *au, uint gid)
429 int i;
431 for(i=0; i<au->ng; i++)
432 if(au->g[i] == gid)
433 return 1;
434 return 0;
437 static Nfs3Status
438 inoperm(Inode *ino, SunAuthUnix *au, int need)
440 int have;
442 if(allowall)
443 return Nfs3Ok;
445 have = ino->mode&0777;
446 if(ino->uid == au->uid)
447 have >>= 6;
448 else if(ino->gid == au->gid || ingroup(au, ino->gid))
449 have >>= 3;
451 if((have&need) != need)
452 return Nfs3ErrNotOwner; /* really EPERM */
453 return Nfs3Ok;
456 static Nfs3Status
457 ext2getattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
459 Inode ino;
460 u32int inum;
461 Ext2 *fs;
462 Nfs3Status ok;
464 fs = fsys->priv;
465 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
466 return ok;
468 USED(au); /* anyone can getattr */
469 return ino2attr(fs, &ino, inum, attr);
472 static Nfs3Status
473 ext2access(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
475 int have;
476 Inode ino;
477 u32int inum;
478 Ext2 *fs;
479 Nfs3Status ok;
481 fs = fsys->priv;
482 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
483 return ok;
485 have = ino.mode&0777;
486 if(ino.uid == au->uid)
487 have >>= 6;
488 else if(ino.gid == au->gid || ingroup(au, ino.gid))
489 have >>= 3;
491 *got = 0;
492 if((want&Nfs3AccessRead) && (have&AREAD))
493 *got |= Nfs3AccessRead;
494 if((want&Nfs3AccessLookup) && (ino.mode&IFMT)==IFDIR && (have&AEXEC))
495 *got |= Nfs3AccessLookup;
496 if((want&Nfs3AccessExecute) && (ino.mode&IFMT)!=IFDIR && (have&AEXEC))
497 *got |= Nfs3AccessExecute;
499 return ino2attr(fs, &ino, inum, attr);
502 static Nfs3Status
503 ext2lookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
505 u32int nblock;
506 u32int i;
507 uchar *p, *ep;
508 Dirent *de;
509 Inode ino;
510 Block *b;
511 Ext2 *fs;
512 Nfs3Status ok;
513 int len, want;
515 fs = fsys->priv;
516 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
517 return ok;
519 if((ino.mode&IFMT) != IFDIR)
520 return Nfs3ErrNotDir;
522 if((ok = inoperm(&ino, au, AEXEC)) != Nfs3Ok)
523 return ok;
525 len = strlen(name);
526 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
527 if(debug) fprint(2, "%d blocks in dir...", nblock);
528 for(i=0; i<nblock; i++){
529 if(i==nblock-1)
530 want = ino.size % fs->blocksize;
531 else
532 want = fs->blocksize;
533 b = ext2fileblock(fs, &ino, i, want);
534 if(b == nil){
535 if(debug) fprint(2, "empty block...");
536 continue;
538 p = b->data;
539 ep = p+b->len;
540 while(p < ep){
541 de = (Dirent*)p;
542 if(de->reclen == 0){
543 if(debug)
544 fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
545 break;
547 p += de->reclen;
548 if(p > ep){
549 if(debug)
550 fprint(2, "bad len %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
551 break;
553 if(de->ino == 0)
554 continue;
555 if(4+2+2+de->namlen > de->reclen){
556 if(debug)
557 fprint(2, "bad namelen %d at offset %d of %d\n", de->namlen, (int)(p-b->data), b->len);
558 break;
560 if(de->namlen == len && memcmp(de->name, name, len) == 0){
561 mkhandle(nh, de->ino);
562 blockput(b);
563 return Nfs3Ok;
566 blockput(b);
568 return Nfs3ErrNoEnt;
571 static Nfs3Status
572 ext2readdir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
574 u32int nblock;
575 u32int i;
576 int off, done;
577 uchar *data, *dp, *dep, *p, *ep, *ndp;
578 Dirent *de;
579 Inode ino;
580 Block *b;
581 Ext2 *fs;
582 Nfs3Status ok;
583 Nfs3Entry e;
584 int want;
586 fs = fsys->priv;
587 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
588 return ok;
590 if((ino.mode&IFMT) != IFDIR)
591 return Nfs3ErrNotDir;
593 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
594 return ok;
596 if(cookie >= ino.size){
597 *pcount = 0;
598 *pdata = 0;
599 return Nfs3Ok;
602 dp = malloc(count);
603 data = dp;
604 if(dp == nil)
605 return Nfs3ErrNoMem;
606 dep = dp+count;
607 *peof = 0;
608 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
609 i = cookie/fs->blocksize;
610 off = cookie%fs->blocksize;
611 done = 0;
612 for(; i<nblock && !done; i++){
613 if(i==nblock-1)
614 want = ino.size % fs->blocksize;
615 else
616 want = fs->blocksize;
617 b = ext2fileblock(fs, &ino, i, want);
618 if(b == nil)
619 continue;
620 p = b->data;
621 ep = p+b->len;
622 memset(&e, 0, sizeof e);
623 while(p < ep){
624 de = (Dirent*)p;
625 if(de->reclen == 0){
626 if(debug) fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
627 break;
629 p += de->reclen;
630 if(p > ep){
631 if(debug) fprint(2, "reclen %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
632 break;
634 if(de->ino == 0){
635 if(debug) fprint(2, "zero inode\n");
636 continue;
638 if(4+2+2+de->namlen > de->reclen){
639 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);
640 break;
642 if(debug) print("%.*s/%d ", de->namlen, de->name, (int)de->ino);
643 if((uchar*)de - b->data < off)
644 continue;
645 e.fileid = de->ino;
646 e.name = de->name;
647 e.namelen = de->namlen;
648 e.cookie = (u64int)i*fs->blocksize + (p - b->data);
649 if(nfs3entrypack(dp, dep, &ndp, &e) < 0){
650 done = 1;
651 break;
653 dp = ndp;
655 off = 0;
656 blockput(b);
658 if(i==nblock)
659 *peof = 1;
661 *pcount = dp - data;
662 *pdata = data;
663 return Nfs3Ok;
666 static Nfs3Status
667 ext2readfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count,
668 u64int offset, uchar **pdata, u32int *pcount, u1int *peof)
670 uchar *data;
671 Block *b;
672 Ext2 *fs;
673 int skip1, tot, want, fragcount;
674 Inode ino;
675 Nfs3Status ok;
676 u64int size;
678 fs = fsys->priv;
679 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
680 return ok;
682 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
683 return ok;
685 size = inosize(&ino);
686 if(offset >= size){
687 *pdata = 0;
688 *pcount = 0;
689 *peof = 1;
690 return Nfs3Ok;
692 if(offset+count > size)
693 count = size-offset;
695 data = malloc(count);
696 if(data == nil)
697 return Nfs3ErrNoMem;
698 memset(data, 0, count);
700 skip1 = offset%fs->blocksize;
701 offset -= skip1;
702 want = skip1+count;
704 /*
705 * have to read multiple blocks if we get asked for a big read.
706 * Linux NFS client assumes that if you ask for 8k and only get 4k
707 * back, the remaining 4k is zeros.
708 */
709 for(tot=0; tot<want; tot+=fragcount){
710 b = ext2fileblock(fs, &ino, (offset+tot)/fs->blocksize, fs->blocksize);
711 fragcount = fs->blocksize;
712 if(b == nil)
713 continue;
714 if(tot+fragcount > want)
715 fragcount = want - tot;
716 if(tot == 0)
717 memmove(data, b->data+skip1, fragcount-skip1);
718 else
719 memmove(data+tot-skip1, b->data, fragcount);
720 blockput(b);
722 count = tot - skip1;
724 *peof = (offset+count == size);
725 *pcount = count;
726 *pdata = data;
727 return Nfs3Ok;
730 static Nfs3Status
731 ext2readlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link)
733 Ext2 *fs;
734 Nfs3Status ok;
735 int len;
736 Inode ino;
737 Block *b;
739 fs = fsys->priv;
740 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
741 return ok;
742 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
743 return ok;
745 if(ino.size > 1024)
746 return Nfs3ErrIo;
747 len = ino.size;
749 if(ino.nblock != 0){
750 /* BUG: assumes symlink fits in one block */
751 b = ext2fileblock(fs, &ino, 0, len);
752 if(b == nil)
753 return Nfs3ErrIo;
754 if(memchr(b->data, 0, len) != nil){
755 blockput(b);
756 return Nfs3ErrIo;
758 *link = malloc(len+1);
759 if(*link == 0){
760 blockput(b);
761 return Nfs3ErrNoMem;
763 memmove(*link, b->data, len);
764 (*link)[len] = 0;
765 blockput(b);
766 return Nfs3Ok;
769 if(len > sizeof ino.block)
770 return Nfs3ErrIo;
772 *link = malloc(len+1);
773 if(*link == 0)
774 return Nfs3ErrNoMem;
775 memmove(*link, ino.block, ino.size);
776 (*link)[len] = 0;
777 return Nfs3Ok;