#include #include #include #include #include #include #include "ffs.h" #define BADBNO ((u64int)~0ULL) #define checkcg 0 #define debug 0 static int checkfsblk(Fsblk*); static int checkcgblk(Cgblk*); static Block *ffsblockread(Fsys*, u64int); static int ffssync(Fsys*); static void ffsclose(Fsys*); static u64int ffsxfileblock(Fsys *fs, Nfs3Handle *h, u64int offset); static Nfs3Status ffsroot(Fsys*, Nfs3Handle*); static Nfs3Status ffsgetattr(Fsys*, SunAuthUnix *au, Nfs3Handle*, Nfs3Attr*); static Nfs3Status ffslookup(Fsys*, SunAuthUnix *au, Nfs3Handle*, char*, Nfs3Handle*); static Nfs3Status ffsreadfile(Fsys*, SunAuthUnix *au, Nfs3Handle*, u32int, u64int, uchar**, u32int*, u1int*); static Nfs3Status ffsreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link); static Nfs3Status ffsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int, u64int, uchar**, u32int*, u1int*); static Nfs3Status ffsaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr); Fsys* fsysopenffs(Disk *disk) { Ffs *fs; Fsys *fsys; fsys = emalloc(sizeof(Fsys)); fs = emalloc(sizeof(Ffs)); fs->disk = disk; fsys->priv = fs; fsys->type = "ffs"; fsys->_readblock = ffsblockread; fsys->_sync = ffssync; fsys->_root = ffsroot; fsys->_getattr = ffsgetattr; fsys->_access = ffsaccess; fsys->_lookup = ffslookup; fsys->_readfile = ffsreadfile; fsys->_readlink = ffsreadlink; fsys->_readdir = ffsreaddir; fsys->_close = ffsclose; fsys->fileblock = ffsxfileblock; if(ffssync(fsys) < 0) goto error; return fsys; error: ffsclose(fsys); return nil; } static Cgblk* ffscylgrp(Ffs *fs, u32int i, Block **pb) { Block *b; Cgblk *cg; if(i >= fs->ncg) return nil; b = diskread(fs->disk, fs->blocksize, (u64int)fs->cg[i].cgblkno*fs->blocksize); if(b == nil) return nil; cg = (Cgblk*)b->data; if(checkcgblk(cg) < 0){ fprint(2, "checkcgblk %d %lud: %r\n", i, (ulong)fs->cg[i].cgblkno); blockput(b); return nil; } *pb = b; return cg; } static int ffssync(Fsys *fsys) { int i; int off[] = { SBOFF, SBOFF2, SBOFFPIGGY }; Block *b, *cgb; Cgblk *cgblk; Cylgrp *cg; Disk *disk; Ffs *fs; Fsblk *fsblk; fs = fsys->priv; disk = fs->disk; /* * Read super block. */ b = nil; for(i=0; idata; // fprint(2, "offset of magic: %ld\n", offsetof(Fsblk, magic)); if((fs->ufs = checkfsblk(fsblk)) > 0) goto okay; blockput(b); b = nil; } goto error; okay: fs->blocksize = fsblk->blocksize; fs->nblock = (fsblk->nfrag+fsblk->fragsperblock-1) / fsblk->fragsperblock; fs->fragsize = fsblk->fragsize; fs->fragspergroup = fsblk->fragspergroup; fs->fragsperblock = fsblk->fragsperblock; fs->inosperblock = fsblk->inosperblock; fs->inospergroup = fsblk->inospergroup; fs->nfrag = fsblk->nfrag; fs->ndfrag = fsblk->ndfrag; /* * used to use * fs->blockspergroup = (u64int)fsblk->_cylspergroup * * fsblk->secspercyl * BYTESPERSEC / fsblk->blocksize; * for UFS1, but this should work for both UFS1 and UFS2 */ fs->blockspergroup = (u64int)fsblk->fragspergroup / fsblk->fragsperblock; fs->ncg = fsblk->ncg; fsys->blocksize = fs->blocksize; fsys->nblock = fs->nblock; if(debug) fprint(2, "ffs %lld %d-byte blocks, %d cylinder groups\n", fs->nblock, fs->blocksize, fs->ncg); if(debug) fprint(2, "\tinospergroup %d perblock %d blockspergroup %lld\n", fs->inospergroup, fs->inosperblock, fs->blockspergroup); if(fs->cg == nil) fs->cg = emalloc(fs->ncg*sizeof(Cylgrp)); for(i=0; incg; i++){ cg = &fs->cg[i]; if(fs->ufs == 2) cg->bno = (u64int)fs->blockspergroup*i; else cg->bno = fs->blockspergroup*i + fsblk->_cgoffset * (i & ~fsblk->_cgmask); cg->cgblkno = cg->bno + fsblk->cfragno/fs->fragsperblock; cg->ibno = cg->bno + fsblk->ifragno/fs->fragsperblock; cg->dbno = cg->bno + fsblk->dfragno/fs->fragsperblock; if(checkcg){ if((cgb = diskread(disk, fs->blocksize, (u64int)cg->cgblkno*fs->blocksize)) == nil) goto error; cgblk = (Cgblk*)cgb->data; if(checkcgblk(cgblk) < 0){ blockput(cgb); goto error; } if(cgblk->nfrag % fs->fragsperblock && i != fs->ncg-1){ werrstr("fractional number of blocks in non-last cylinder group %d", cgblk->nfrag); blockput(cgb); goto error; } /* cg->nfrag = cgblk->nfrag; */ /* cg->nblock = (cgblk->nfrag+fs->fragsperblock-1) / fs->fragsperblock; */ /* fprint(2, "cg #%d: cgblk %lud, %d blocks, %d inodes\n", cgblk->num, (ulong)cg->cgblkno, cg->nblock, cg->nino); */ } } blockput(b); return 0; error: blockput(b); return -1; } static void ffsclose(Fsys *fsys) { Ffs *fs; fs = fsys->priv; if(fs->cg) free(fs->cg); free(fs); free(fsys); } static int checkfsblk(Fsblk *super) { // fprint(2, "ffs magic 0x%ux\n", super->magic); if(super->magic == FSMAGIC){ super->time = super->_time; super->nfrag = super->_nfrag; super->ndfrag = super->_ndfrag; super->flags = super->_flags; return 1; } if(super->magic == FSMAGIC2){ return 2; } werrstr("bad super block"); return -1; } static int checkcgblk(Cgblk *cg) { if(cg->magic != CGMAGIC){ werrstr("bad cylinder group block"); return -1; } return 0; } /* * Read block #bno from the disk, zeroing unused data. * If there is no data whatsoever, it's okay to return nil. */ int nskipx; static Block* ffsblockread(Fsys *fsys, u64int bno) { int i, o; u8int *fmap; int frag, fsize, avail; Block *b; Cgblk *cgblk; Ffs *fs; fs = fsys->priv; i = bno / fs->blockspergroup; o = bno % fs->blockspergroup; if(i >= fs->ncg) return nil; if((cgblk = ffscylgrp(fs, i, &b)) == nil) return nil; fmap = (u8int*)cgblk+cgblk->fmapoff; frag = fs->fragsperblock; switch(frag){ default: sysfatal("bad frag"); case 8: avail = fmap[o]; break; case 4: avail = (fmap[o>>1] >> ((o&1)*4)) & 0xF; break; case 2: avail = (fmap[o>>2] >> ((o&3)*2)) & 0x3; break; case 1: avail = (fmap[o>>3] >> (o&7)) & 0x1; break; } blockput(b); if(avail == ((1<disk, fs->blocksize, bno*fs->blocksize)) == nil){ fprint(2, "diskread failed!!!\n"); return nil; } fsize = fs->fragsize; for(i=0; idata + fsize*i, 0, fsize); return b; } static Block* ffsdatablock(Ffs *fs, u64int bno, int size) { int fsize; u64int diskaddr; Block *b; if(bno == 0) return nil; fsize = size; if(fsize < fs->fragsize) fsize = fs->fragsize; if(bno >= fs->nfrag){ fprint(2, "ffs: request for block %#lux; nfrag %#llux\n", (ulong)bno, fs->nfrag); return nil; } diskaddr = (u64int)bno*fs->fragsize; b = diskread(fs->disk, fsize, diskaddr); if(b == nil){ fprint(2, "ffs: disk i/o at %#llux for %#ux: %r\n", diskaddr, fsize); return nil; } if(b->len < fsize){ fprint(2, "ffs: disk i/o at %#llux for %#ux got %#ux\n", diskaddr, fsize, b->len); blockput(b); return nil; } return b; } static u64int ifetch(Ffs *fs, u64int bno, u32int off) { Block *b; if(bno == BADBNO) return BADBNO; b = ffsdatablock(fs, bno, fs->blocksize); if(b == nil) return BADBNO; if(fs->ufs == 2) bno = ((u64int*)b->data)[off]; else bno = ((u32int*)b->data)[off]; blockput(b); return bno; } static u64int ffsfileblockno(Ffs *fs, Inode *ino, u64int bno) { int ppb; if(bno < NDADDR){ if(debug) fprint(2, "ffsfileblock %lud: direct %#lux\n", (ulong)bno, (ulong)ino->db[bno]); return ino->db[bno]; } bno -= NDADDR; ppb = fs->blocksize/4; if(bno < ppb) /* single indirect */ return ifetch(fs, ino->ib[0], bno); bno -= ppb; if(bno < ppb*ppb) return ifetch(fs, ifetch(fs, ino->ib[1], bno/ppb), bno%ppb); bno -= ppb*ppb; if(bno/ppb/ppb/ppb == 0) /* bno < ppb*ppb*ppb w/o overflow */ return ifetch(fs, ifetch(fs, ifetch(fs, ino->ib[2], bno/ppb/ppb), (bno/ppb)%ppb), bno%ppb); fprint(2, "ffsfileblock %llud: way too big\n", bno+NDADDR+ppb+ppb*ppb); return BADBNO; } static Block* ffsfileblock(Ffs *fs, Inode *ino, u64int bno, int size) { u64int b; b = ffsfileblockno(fs, ino, bno); if(b == ~0) return nil; return ffsdatablock(fs, b, size); } /* * NFS handles are 4-byte inode number. */ static void mkhandle(Nfs3Handle *h, u64int ino) { h->h[0] = ino >> 24; h->h[1] = ino >> 16; h->h[2] = ino >> 8; h->h[3] = ino; h->len = 4; } static u32int byte2u32(uchar *p) { return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; } static u64int lastiaddr; /* debugging */ static void inode1to2(Inode1 *i1, Inode *i2) { int i; memset(i2, 0, sizeof *i2); i2->mode = i1->mode; i2->nlink = i1->nlink; i2->size = i1->size; i2->atime = i1->atime; i2->atimensec = i1->atimensec; i2->mtime = i1->mtime; i2->mtimensec = i1->mtimensec; i2->ctime = i1->ctime; i2->ctimensec = i1->ctimensec; for(i=0; idb[i] = i1->db[i]; for(i=0; iib[i] = i1->ib[i]; i2->flags = i1->flags; i2->nblock = i1->nblock; i2->gen = i1->gen; i2->uid = i1->uid; i2->gid = i1->gid; } static Nfs3Status handle2ino(Ffs *fs, Nfs3Handle *h, u32int *pinum, Inode *ino) { int i; u32int ioff; u32int inum; u64int iaddr; Block *b; Cylgrp *cg; Inode1 ino1; if(h->len != 4) return Nfs3ErrBadHandle; inum = byte2u32(h->h); if(pinum) *pinum = inum; if(debug) print("inum %d...", (int)inum); /* fetch inode from disk */ i = inum / fs->inospergroup; ioff = inum % fs->inospergroup; if(debug)print("cg %d off %d...", i, (int)ioff); if(i >= fs->ncg) return Nfs3ErrBadHandle; cg = &fs->cg[i]; if(debug) print("cg->ibno %lld ufs %d...", cg->ibno, fs->ufs); iaddr = (cg->ibno+ioff/fs->inosperblock)*(vlong)fs->blocksize; ioff = ioff%fs->inosperblock; if((b = diskread(fs->disk, fs->blocksize, iaddr)) == nil) return Nfs3ErrIo; if(fs->ufs == 2){ *ino = ((Inode*)b->data)[ioff]; lastiaddr = iaddr+ioff*sizeof(Inode); }else{ ino1 = ((Inode1*)b->data)[ioff]; inode1to2(&ino1, ino); lastiaddr = iaddr+ioff*sizeof(Inode1); } blockput(b); return Nfs3Ok; } static Nfs3Status ffsroot(Fsys *fsys, Nfs3Handle *h) { USED(fsys); mkhandle(h, 2); return Nfs3Ok; } static Nfs3Status ino2attr(Ffs *fs, Inode *ino, u32int inum, Nfs3Attr *attr) { u32int rdev; attr->type = -1; switch(ino->mode&IFMT){ case IFIFO: attr->type = Nfs3FileFifo; break; case IFCHR: attr->type = Nfs3FileChar; break; case IFDIR: attr->type = Nfs3FileDir; break; case IFBLK: attr->type = Nfs3FileBlock; break; case IFREG: attr->type = Nfs3FileReg; break; case IFLNK: attr->type = Nfs3FileSymlink; break; case IFSOCK: attr->type = Nfs3FileSocket; break; case IFWHT: default: return Nfs3ErrBadHandle; } attr->mode = ino->mode&07777; attr->nlink = ino->nlink; attr->uid = ino->uid; attr->gid = ino->gid; attr->size = ino->size; attr->used = ino->nblock*fs->blocksize; if(attr->type==Nfs3FileBlock || attr->type==Nfs3FileChar){ rdev = ino->db[0]; attr->major = (rdev>>8)&0xFF; attr->minor = rdev & 0xFFFF00FF; }else{ attr->major = 0; attr->minor = 0; } attr->fsid = 0; attr->fileid = inum; attr->atime.sec = ino->atime; attr->atime.nsec = ino->atimensec; attr->mtime.sec = ino->mtime; attr->mtime.nsec = ino->mtimensec; attr->ctime.sec = ino->ctime; attr->ctime.nsec = ino->ctimensec; return Nfs3Ok; } static int ingroup(SunAuthUnix *au, uint gid) { int i; for(i=0; ing; i++) if(au->g[i] == gid) return 1; return 0; } static Nfs3Status inoperm(Inode *ino, SunAuthUnix *au, int need) { int have; if(au == nil) return Nfs3Ok; have = ino->mode&0777; if(ino->uid == au->uid) have >>= 6; else if(ino->gid == au->gid || ingroup(au, ino->gid)) have >>= 3; if((have&need) != need) return Nfs3ErrNotOwner; /* really EPERM */ return Nfs3Ok; } static Nfs3Status ffsgetattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr) { Inode ino; u32int inum; Ffs *fs; Nfs3Status ok; fs = fsys->priv; if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok) return ok; USED(au); /* anyone can getattr */ return ino2attr(fs, &ino, inum, attr); } static Nfs3Status ffsaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr) { int have; Inode ino; u32int inum; Ffs *fs; Nfs3Status ok; fs = fsys->priv; if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok) return ok; have = ino.mode&0777; if(ino.uid == au->uid) have >>= 6; else if(ino.gid == au->gid || ingroup(au, ino.gid)) have >>= 3; *got = 0; if((want&Nfs3AccessRead) && (have&AREAD)) *got |= Nfs3AccessRead; if((want&Nfs3AccessLookup) && (ino.mode&IFMT)==IFDIR && (have&AEXEC)) *got |= Nfs3AccessLookup; if((want&Nfs3AccessExecute) && (ino.mode&IFMT)!=IFDIR && (have&AEXEC)) *got |= Nfs3AccessExecute; return ino2attr(fs, &ino, inum, attr); } static Nfs3Status ffslookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh) { u32int nblock; u32int i; uchar *p, *ep; Dirent *de; Inode ino; Block *b; Ffs *fs; Nfs3Status ok; int len, want; fs = fsys->priv; if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok) return ok; if((ino.mode&IFMT) != IFDIR) return Nfs3ErrNotDir; if((ok = inoperm(&ino, au, AEXEC)) != Nfs3Ok) return ok; len = strlen(name); nblock = (ino.size+fs->blocksize-1) / fs->blocksize; for(i=0; iblocksize; else want = fs->blocksize; b = ffsfileblock(fs, &ino, i, want); if(b == nil) continue; p = b->data; ep = p+b->len; while(p < ep){ de = (Dirent*)p; if(de->reclen == 0){ if(debug) fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len); break; } p += de->reclen; if(p > ep){ if(debug) fprint(2, "bad len %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len); break; } if(de->ino == 0) continue; if(4+2+2+de->namlen > de->reclen){ if(debug) fprint(2, "bad namelen %d at offset %d of %d\n", de->namlen, (int)(p-b->data), b->len); break; } if(de->namlen == len && memcmp(de->name, name, len) == 0){ mkhandle(nh, de->ino); blockput(b); return Nfs3Ok; } } blockput(b); } return Nfs3ErrNoEnt; } static Nfs3Status ffsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof) { u32int nblock; u32int i; int off, done; uchar *data, *dp, *dep, *p, *ep, *ndp; Dirent *de; Inode ino; Block *b; Ffs *fs; Nfs3Status ok; Nfs3Entry e; int want; fs = fsys->priv; if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok) return ok; if((ino.mode&IFMT) != IFDIR) return Nfs3ErrNotDir; if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok) return ok; if(cookie >= ino.size){ *peof = 1; *pcount = 0; *pdata = 0; return Nfs3Ok; } dp = malloc(count); data = dp; if(dp == nil) return Nfs3ErrNoMem; dep = dp+count; *peof = 0; nblock = (ino.size+fs->blocksize-1) / fs->blocksize; i = cookie/fs->blocksize; off = cookie%fs->blocksize; done = 0; for(; iblocksize; else want = fs->blocksize; b = ffsfileblock(fs, &ino, i, want); if(b == nil) continue; p = b->data; ep = p+b->len; memset(&e, 0, sizeof e); while(p < ep){ de = (Dirent*)p; if(de->reclen == 0){ if(debug) fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len); break; } p += de->reclen; if(p > ep){ if(debug) fprint(2, "reclen %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len); break; } if(de->ino == 0){ if(debug) fprint(2, "zero inode\n"); continue; } if(4+2+2+de->namlen > de->reclen){ 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); break; } if(de->name[de->namlen] != 0){ if(debug) fprint(2, "bad name %d %.*s\n", de->namlen, de->namlen, de->name); continue; } if(debug) print("%s/%d ", de->name, (int)de->ino); if((uchar*)de - b->data < off) continue; e.fileid = de->ino; e.name = de->name; e.namelen = de->namlen; e.cookie = (u64int)i*fs->blocksize + (p - b->data); if(nfs3entrypack(dp, dep, &ndp, &e) < 0){ done = 1; break; } dp = ndp; } off = 0; blockput(b); } if(i==nblock) *peof = 1; *pcount = dp - data; *pdata = data; return Nfs3Ok; } static u64int ffsxfileblock(Fsys *fsys, Nfs3Handle *h, u64int offset) { u64int bno; Inode ino; Nfs3Status ok; Ffs *fs; fs = fsys->priv; if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok){ nfs3errstr(ok); return 0; } if(offset == 1) /* clumsy hack for debugging */ return lastiaddr; if(offset >= ino.size){ werrstr("beyond end of file"); return 0; } bno = offset/fs->blocksize; bno = ffsfileblockno(fs, &ino, bno); if(bno == ~0) return 0; return bno*(u64int)fs->fragsize; } static Nfs3Status ffsreadfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int offset, uchar **pdata, u32int *pcount, u1int *peof) { uchar *data; Block *b; Ffs *fs; int off, want, fragcount; Inode ino; Nfs3Status ok; fs = fsys->priv; if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok) return ok; if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok) return ok; if(offset >= ino.size){ *pdata = 0; *pcount = 0; *peof = 1; return Nfs3Ok; } if(offset+count > ino.size) count = ino.size-offset; if(offset/fs->blocksize != (offset+count-1)/fs->blocksize) count = fs->blocksize - offset%fs->blocksize; data = malloc(count); if(data == nil) return Nfs3ErrNoMem; want = offset%fs->blocksize+count; if(want%fs->fragsize) want += fs->fragsize - want%fs->fragsize; b = ffsfileblock(fs, &ino, offset/fs->blocksize, want); if(b == nil){ /* BUG: distinguish sparse file from I/O error */ memset(data, 0, count); }else{ off = offset%fs->blocksize; fragcount = count; /* need signed variable */ if(off+fragcount > b->len){ fragcount = b->len - off; if(fragcount < 0) fragcount = 0; } if(fragcount > 0) memmove(data, b->data+off, fragcount); count = fragcount; blockput(b); } *peof = (offset+count == ino.size); *pcount = count; *pdata = data; return Nfs3Ok; } static Nfs3Status ffsreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link) { Ffs *fs; Nfs3Status ok; int len; Inode ino; Block *b; fs = fsys->priv; if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok) return ok; if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok) return ok; if(ino.size > 1024) return Nfs3ErrIo; len = ino.size; if(ino.nblock != 0){ /* assumes symlink fits in one block */ b = ffsfileblock(fs, &ino, 0, len); if(b == nil) return Nfs3ErrIo; if(memchr(b->data, 0, len) != nil){ blockput(b); return Nfs3ErrIo; } *link = malloc(len+1); if(*link == 0){ blockput(b); return Nfs3ErrNoMem; } memmove(*link, b->data, len); (*link)[len] = 0; blockput(b); return Nfs3Ok; } if(len > sizeof ino.db + sizeof ino.ib) return Nfs3ErrIo; *link = malloc(len+1); if(*link == 0) return Nfs3ErrNoMem; memmove(*link, ino.db, ino.size); (*link)[len] = 0; return Nfs3Ok; }