9 static void parsedirent(Dirent*, uchar*);
10 static void parseinode(Inode*, uchar*);
11 static void parsegroup(Group*, uchar*);
12 static void parsesuper(Super*, uchar*);
16 static int ext2sync(Fsys*);
17 static void ext2close(Fsys*);
18 static Block* ext2blockread(Fsys*, u64int);
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);
29 fsysopenext2(Disk *disk)
34 fsys = emalloc(sizeof(Fsys));
35 fs = emalloc(sizeof(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;
51 if(ext2sync(fsys) < 0)
72 ext2group(Ext2 *fs, u32int i, Group *g)
80 addr = fs->groupaddr + i/fs->descperblock;
81 b = diskread(fs->disk, fs->blocksize, addr*fs->blocksize);
84 parsegroup(g, b->data+i%fs->descperblock*GroupSize);
90 ext2blockread(Fsys *fsys, u64int vbno)
95 u32int bno, boff, bitblock;
100 if(vbno >= fs->nblock)
107 if(bno < fs->firstblock)
108 return diskread(fs->disk, fs->blocksize, (u64int)bno*fs->blocksize);
110 if(bno < fs->firstblock)
113 bno -= fs->firstblock;
114 if(ext2group(fs, bno/fs->blockspergroup, &g) < 0){
116 fprint(2, "loading group: %r...");
121 fprint(2, "ext2 group %d: bitblock=%ud inodebitblock=%ud inodeaddr=%ud freeblocks=%ud freeinodes=%ud useddirs=%ud\n",
122 (int)(bno/fs->blockspergroup),
130 fprint(2, "group %d bitblock=%d...", bno/fs->blockspergroup, g.bitblock);
132 bitblock = g.bitblock;
133 bitpos = (u64int)bitblock*fs->blocksize;
135 if((bitb = diskread(fs->disk, fs->blocksize, bitpos)) == nil){
137 fprint(2, "loading bitblock: %r...");
141 boff = bno%fs->blockspergroup;
142 if((bits[boff>>3] & (1<<(boff&7))) == 0){
144 fprint(2, "block %d not allocated in group %d: bitblock %d/%lld bits[%d] = %#x\n",
145 boff, bno/fs->blockspergroup,
155 bno += fs->firstblock;
156 return diskread(fs->disk, fs->blocksize, (u64int)bno*fs->blocksize);
160 ext2datablock(Ext2 *fs, u32int bno, int size)
163 return ext2blockread(fs->fsys, bno);
167 ext2fileblock(Ext2 *fs, Inode *ino, u32int bno, int size)
175 if(bno < NDIRBLOCKS){
177 fprint(2, "fileblock %d -> %d...",
178 bno, ino->block[bno]);
179 return ext2datablock(fs, ino->block[bno], size);
182 ppb = fs->blocksize/4;
186 b = ext2datablock(fs, ino->block[INDBLOCK], fs->blocksize);
189 a = (u32int*)b->data;
192 return ext2datablock(fs, bno, size);
196 /* one double indirect */
198 b = ext2datablock(fs, ino->block[DINDBLOCK], fs->blocksize);
201 a = (u32int*)b->data;
205 b = ext2datablock(fs, pbno, fs->blocksize);
208 a = (u32int*)b->data;
211 return ext2datablock(fs, bno, size);
215 /* one triple indirect */
216 if(bno < ppb*ppb*ppb){
217 b = ext2datablock(fs, ino->block[TINDBLOCK], fs->blocksize);
220 a = (u32int*)b->data;
221 pbno = a[bno/(ppb*ppb)];
224 b = ext2datablock(fs, pbno, fs->blocksize);
227 a = (u32int*)b->data;
231 b = ext2datablock(fs, pbno, fs->blocksize);
234 a = (u32int*)b->data;
237 return ext2datablock(fs, bno, size);
240 fprint(2, "ext2fileblock %ud: too big\n", obno);
245 checksuper(Super *super)
247 if(super->magic != SUPERMAGIC){
248 werrstr("bad magic 0x%ux wanted 0x%ux", super->magic, SUPERMAGIC);
266 if((b = diskread(disk, SBSIZE, SBOFF)) == nil)
268 parsesuper(&super, b->data);
270 if(checksuper(&super) < 0)
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 fs->inosperblock = fs->blocksize / InodeSize;
279 if(fs->blocksize == SBOFF)
283 fs->descperblock = fs->blocksize / GroupSize;
284 fs->firstblock = super.firstdatablock;
286 fsys->blocksize = fs->blocksize;
287 fsys->nblock = fs->nblock;
288 if(debug) fprint(2, "ext2 %d %d-byte blocks, first data block %d, %d groups of %d\n",
289 fs->nblock, fs->blocksize, fs->firstblock, fs->ngroup, fs->blockspergroup);
292 for(i=0; i<fs->ngroup; i++)
293 if(ext2group(fs, i, &g) >= 0)
294 fprint(2, "grp %d: bitblock=%d\n", i, g.bitblock);
300 mkhandle(Nfs3Handle *h, u64int ino)
312 return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
316 handle2ino(Ext2 *fs, Nfs3Handle *h, u32int *pinum, Inode *ino)
326 return Nfs3ErrBadHandle;
327 inum = byte2u32(h->h);
330 i = (inum-1) / fs->inospergroup;
332 return Nfs3ErrBadHandle;
333 ioff = (inum-1) % fs->inospergroup;
334 if(ext2group(fs, i, &g) < 0)
336 addr = g.inodeaddr + ioff/fs->inosperblock;
337 if((b = diskread(fs->disk, fs->blocksize, (u64int)addr*fs->blocksize)) == nil)
339 parseinode(ino, b->data+InodeSize*(ioff%fs->inosperblock));
345 ext2root(Fsys *fsys, Nfs3Handle *h)
348 mkhandle(h, ROOTINODE);
358 if((ino->mode&IFMT)==IFREG)
359 size |= (u64int)ino->diracl << 32;
364 ino2attr(Ext2 *fs, Inode *ino, u32int inum, Nfs3Attr *attr)
369 switch(ino->mode&IFMT){
371 attr->type = Nfs3FileFifo;
374 attr->type = Nfs3FileChar;
377 attr->type = Nfs3FileDir;
380 attr->type = Nfs3FileBlock;
383 attr->type = Nfs3FileReg;
386 attr->type = Nfs3FileSymlink;
389 attr->type = Nfs3FileSocket;
393 return Nfs3ErrBadHandle;
396 attr->mode = ino->mode&07777;
397 attr->nlink = ino->nlink;
398 attr->uid = ino->uid;
399 attr->gid = ino->gid;
400 attr->size = inosize(ino);
401 attr->used = (u64int)ino->nblock*fs->blocksize;
402 if(attr->type==Nfs3FileBlock || attr->type==Nfs3FileChar){
403 rdev = ino->block[0];
404 attr->major = (rdev>>8)&0xFF;
405 attr->minor = rdev & 0xFFFF00FF;
412 attr->atime.sec = ino->atime;
413 attr->atime.nsec = 0;
414 attr->mtime.sec = ino->mtime;
415 attr->mtime.nsec = 0;
416 attr->ctime.sec = ino->ctime;
417 attr->ctime.nsec = 0;
422 ingroup(SunAuthUnix *au, uint gid)
426 for(i=0; i<au->ng; i++)
433 inoperm(Inode *ino, SunAuthUnix *au, int need)
440 have = ino->mode&0777;
441 if(ino->uid == au->uid)
443 else if(ino->gid == au->gid || ingroup(au, ino->gid))
446 if((have&need) != need)
447 return Nfs3ErrNotOwner; /* really EPERM */
452 ext2getattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
460 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
463 USED(au); /* anyone can getattr */
464 return ino2attr(fs, &ino, inum, attr);
468 ext2access(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
477 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
480 have = ino.mode&0777;
481 if(ino.uid == au->uid)
483 else if(ino.gid == au->gid || ingroup(au, ino.gid))
487 if((want&Nfs3AccessRead) && (have&AREAD))
488 *got |= Nfs3AccessRead;
489 if((want&Nfs3AccessLookup) && (ino.mode&IFMT)==IFDIR && (have&AEXEC))
490 *got |= Nfs3AccessLookup;
491 if((want&Nfs3AccessExecute) && (ino.mode&IFMT)!=IFDIR && (have&AEXEC))
492 *got |= Nfs3AccessExecute;
494 return ino2attr(fs, &ino, inum, attr);
498 ext2lookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
511 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
514 if((ino.mode&IFMT) != IFDIR)
515 return Nfs3ErrNotDir;
517 if((ok = inoperm(&ino, au, AEXEC)) != Nfs3Ok)
521 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
522 if(debug) fprint(2, "%d blocks in dir...", nblock);
523 for(i=0; i<nblock; i++){
525 want = ino.size % fs->blocksize;
527 want = fs->blocksize;
528 b = ext2fileblock(fs, &ino, i, want);
530 if(debug) fprint(2, "empty block...");
539 fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
545 fprint(2, "bad len %d at offset %d of %d\n", de.reclen, (int)(p-b->data), b->len);
550 if(4+2+2+de.namlen > de.reclen){
552 fprint(2, "bad namelen %d at offset %d of %d\n", de.namlen, (int)(p-b->data), b->len);
555 if(de.namlen == len && memcmp(de.name, name, len) == 0){
556 mkhandle(nh, de.ino);
567 ext2readdir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
572 uchar *data, *dp, *dep, *p, *ep, *ndp;
582 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
585 if((ino.mode&IFMT) != IFDIR)
586 return Nfs3ErrNotDir;
588 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
591 if(debug) print("readdir cookie %#llux ino.size %#llux\n",
592 (u64int)cookie, (u64int)ino.size);
594 if(cookie >= ino.size){
607 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
608 i = cookie/fs->blocksize;
609 off = cookie%fs->blocksize;
611 for(; i<nblock && !outofspace; i++, off=0){
613 want = ino.size % fs->blocksize;
615 want = fs->blocksize;
616 b = ext2fileblock(fs, &ino, i, want);
621 memset(&e, 0, sizeof e);
625 if(debug) fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
630 if(debug) fprint(2, "reclen %d at offset %d of %d\n", de.reclen, (int)(p-b->data), b->len);
634 if(debug) fprint(2, "zero inode\n");
637 if(4+2+2+de.namlen > de.reclen){
638 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);
641 if(debug) print("%.*s/%d ", de.namlen, de.name, (int)de.ino);
642 if(p-de.reclen - b->data < off)
646 e.namelen = de.namlen;
647 e.cookie = (u64int)i*fs->blocksize + (p - b->data);
648 if(debug) print("%.*s %#llux\n", utfnlen(e.name, e.namelen), e.name, (u64int)e.cookie);
649 if(nfs3entrypack(dp, dep, &ndp, &e) < 0){
657 if(i==nblock && !outofspace)
666 ext2readfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count,
667 u64int offset, uchar **pdata, u32int *pcount, u1int *peof)
672 int skip1, tot, want, fragcount;
678 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
681 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
684 size = inosize(&ino);
691 if(offset+count > size)
694 data = malloc(count);
697 memset(data, 0, count);
699 skip1 = offset%fs->blocksize;
704 * have to read multiple blocks if we get asked for a big read.
705 * Linux NFS client assumes that if you ask for 8k and only get 4k
706 * back, the remaining 4k is zeros.
708 for(tot=0; tot<want; tot+=fragcount){
709 b = ext2fileblock(fs, &ino, (offset+tot)/fs->blocksize, fs->blocksize);
710 fragcount = fs->blocksize;
713 if(tot+fragcount > want)
714 fragcount = want - tot;
716 memmove(data, b->data+skip1, fragcount-skip1);
718 memmove(data+tot-skip1, b->data, fragcount);
723 *peof = (offset+count == size);
730 ext2readlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link)
739 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
741 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
749 /* BUG: assumes symlink fits in one block */
750 b = ext2fileblock(fs, &ino, 0, len);
753 if(memchr(b->data, 0, len) != nil){
757 *link = malloc(len+1);
762 memmove(*link, b->data, len);
768 if(len > sizeof ino.block)
771 *link = malloc(len+1);
774 memmove(*link, ino.block, ino.size);
780 * Ext2 is always little-endian, even on big-endian machines.
786 return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
792 return p[0] | (p[1]<<8);
802 parsedirent(Dirent *de, uchar *p)
805 de->reclen = l16(p+4);
806 de->namlen = l8(p+6);
808 de->name = (char*)p+8;
812 parseinode(Inode *ino, uchar *p)
818 ino->size = l32(p+4);
819 ino->atime = l32(p+8);
820 ino->ctime = l32(p+12);
821 ino->mtime = l32(p+16);
822 ino->dtime = l32(p+20);
823 ino->gid = l16(p+24);
824 ino->nlink = l16(p+26);
825 ino->nblock = l32(p+28);
826 ino->flags = l32(p+32);
828 for(i=0; i<NBLOCKS; i++)
829 ino->block[i] = l32(p+40+i*4);
830 ino->version = l32(p+100);
831 ino->fileacl = l32(p+104);
832 ino->diracl = l32(p+108);
833 ino->faddr = l32(p+112);
838 parsegroup(Group *g, uchar *p)
840 g->bitblock = l32(p);
841 g->inodebitblock = l32(p+4);
842 g->inodeaddr = l32(p+8);
843 g->freeblockscount = l16(p+12);
844 g->freeinodescount = l16(p+14);
845 g->useddirscount = l16(p+16);
847 /* 12 byte reserved */
851 parsesuper(Super *s, uchar *p)
854 s->nblock = l32(p+4);
855 s->rblockcount = l32(p+8);
856 s->freeblockcount = l32(p+12);
857 s->freeinodecount = l32(p+16);
858 s->firstdatablock = l32(p+20);
859 s->logblocksize = l32(p+24);
860 s->logfragsize = l32(p+28);
861 s->blockspergroup = l32(p+32);
862 s->fragpergroup = l32(p+36);
863 s->inospergroup = l32(p+40);
864 s->mtime = l32(p+44);
865 s->wtime = l32(p+48);
866 s->mntcount = l16(p+52);
867 s->maxmntcount = l16(p+54);
868 s->magic = l16(p+56);
869 s->state = l16(p+58);
870 s->errors = l16(p+60);
872 s->lastcheck = l32(p+64);
873 s->checkinterval = l32(p+68);
874 s->creatoros = l32(p+72);
875 s->revlevel = l32(p+76);
876 s->defresuid = l16(p+80);
877 s->defresgid = l16(p+82);
878 /* 940 byte reserved */