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 "ffs.h"
9 #define BADBNO ((u64int)~0ULL)
11 #define checkcg 0
12 #define debug 0
14 static int checkfsblk(Fsblk*);
15 static int checkcgblk(Cgblk*);
16 static Block *ffsblockread(Fsys*, u64int);
17 static int ffssync(Fsys*);
18 static void ffsclose(Fsys*);
20 static u64int ffsxfileblock(Fsys *fs, Nfs3Handle *h, u64int offset);
21 static Nfs3Status ffsroot(Fsys*, Nfs3Handle*);
22 static Nfs3Status ffsgetattr(Fsys*, SunAuthUnix *au, Nfs3Handle*, Nfs3Attr*);
23 static Nfs3Status ffslookup(Fsys*, SunAuthUnix *au, Nfs3Handle*, char*, Nfs3Handle*);
24 static Nfs3Status ffsreadfile(Fsys*, SunAuthUnix *au, Nfs3Handle*, u32int, u64int, uchar**, u32int*, u1int*);
25 static Nfs3Status ffsreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link);
26 static Nfs3Status ffsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int, u64int, uchar**, u32int*, u1int*);
27 static Nfs3Status ffsaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr);
29 Fsys*
30 fsysopenffs(Disk *disk)
31 {
32 Ffs *fs;
33 Fsys *fsys;
35 fsys = emalloc(sizeof(Fsys));
36 fs = emalloc(sizeof(Ffs));
37 fs->disk = disk;
38 fsys->priv = fs;
39 fsys->type = "ffs";
40 fsys->_readblock = ffsblockread;
41 fsys->_sync = ffssync;
42 fsys->_root = ffsroot;
43 fsys->_getattr = ffsgetattr;
44 fsys->_access = ffsaccess;
45 fsys->_lookup = ffslookup;
46 fsys->_readfile = ffsreadfile;
47 fsys->_readlink = ffsreadlink;
48 fsys->_readdir = ffsreaddir;
49 fsys->fileblock = ffsxfileblock;
51 if(ffssync(fsys) < 0)
52 goto error;
54 return fsys;
56 error:
57 ffsclose(fsys);
58 return nil;
59 }
61 static Cgblk*
62 ffscylgrp(Ffs *fs, u32int i, Block **pb)
63 {
64 Block *b;
65 Cgblk *cg;
67 if(i >= fs->ncg)
68 return nil;
70 b = diskread(fs->disk, fs->blocksize, (u64int)fs->cg[i].cgblkno*fs->blocksize);
71 if(b == nil)
72 return nil;
73 cg = (Cgblk*)b->data;
74 if(checkcgblk(cg) < 0){
75 fprint(2, "checkcgblk %d %lud: %r\n", i, (ulong)fs->cg[i].cgblkno);
76 blockput(b);
77 return nil;
78 }
79 *pb = b;
80 return cg;
81 }
83 static int
84 ffssync(Fsys *fsys)
85 {
86 int i;
87 int off[] = { SBOFF, SBOFF2, SBOFFPIGGY };
88 Block *b, *cgb;
89 Cgblk *cgblk;
90 Cylgrp *cg;
91 Disk *disk;
92 Ffs *fs;
93 Fsblk *fsblk;
95 fs = fsys->priv;
96 disk = fs->disk;
98 /*
99 * Read super block.
100 */
101 b = nil;
102 for(i=0; i<nelem(off); i++){
103 if((b = diskread(disk, SBSIZE, off[i])) == nil)
104 goto error;
105 fsblk = (Fsblk*)b->data;
106 fprint(2, "offset of magic: %ld\n", offsetof(Fsblk, magic));
107 if((fs->ufs = checkfsblk(fsblk)) > 0)
108 goto okay;
109 blockput(b);
111 goto error;
113 okay:
114 fs->blocksize = fsblk->blocksize;
115 fs->nblock = (fsblk->nfrag+fsblk->fragsperblock-1) / fsblk->fragsperblock;
116 fs->fragsize = fsblk->fragsize;
117 fs->fragspergroup = fsblk->fragspergroup;
118 fs->fragsperblock = fsblk->fragsperblock;
119 fs->inosperblock = fsblk->inosperblock;
120 fs->inospergroup = fsblk->inospergroup;
122 fs->nfrag = fsblk->nfrag;
123 fs->ndfrag = fsblk->ndfrag;
124 /*
125 * used to use
126 * fs->blockspergroup = (u64int)fsblk->_cylspergroup *
127 * fsblk->secspercyl * BYTESPERSEC / fsblk->blocksize;
128 * for UFS1, but this should work for both UFS1 and UFS2
129 */
130 fs->blockspergroup = (u64int)fsblk->fragspergroup / fsblk->fragsperblock;
131 fs->ncg = fsblk->ncg;
133 fsys->blocksize = fs->blocksize;
134 fsys->nblock = fs->nblock;
136 if(debug) fprint(2, "ffs %lld %d-byte blocks, %d cylinder groups\n",
137 fs->nblock, fs->blocksize, fs->ncg);
138 if(debug) fprint(2, "\tinospergroup %d perblock %d blockspergroup %lld\n",
139 fs->inospergroup, fs->inosperblock, fs->blockspergroup);
141 if(fs->cg == nil)
142 fs->cg = emalloc(fs->ncg*sizeof(Cylgrp));
143 for(i=0; i<fs->ncg; i++){
144 cg = &fs->cg[i];
145 if(fs->ufs == 2)
146 cg->bno = (u64int)fs->blockspergroup*i;
147 else
148 cg->bno = fs->blockspergroup*i + fsblk->_cgoffset * (i & ~fsblk->_cgmask);
149 cg->cgblkno = cg->bno + fsblk->cfragno/fs->fragsperblock;
150 cg->ibno = cg->bno + fsblk->ifragno/fs->fragsperblock;
151 cg->dbno = cg->bno + fsblk->dfragno/fs->fragsperblock;
153 if(checkcg){
154 if((cgb = diskread(disk, fs->blocksize, (u64int)cg->cgblkno*fs->blocksize)) == nil)
155 goto error;
157 cgblk = (Cgblk*)cgb->data;
158 if(checkcgblk(cgblk) < 0){
159 blockput(cgb);
160 goto error;
162 if(cgblk->nfrag % fs->fragsperblock && i != fs->ncg-1){
163 werrstr("fractional number of blocks in non-last cylinder group %d", cgblk->nfrag);
164 blockput(cgb);
165 goto error;
167 // cg->nfrag = cgblk->nfrag;
168 // cg->nblock = (cgblk->nfrag+fs->fragsperblock-1) / fs->fragsperblock;
169 // fprint(2, "cg #%d: cgblk %lud, %d blocks, %d inodes\n", cgblk->num, (ulong)cg->cgblkno, cg->nblock, cg->nino);
172 blockput(b);
173 return 0;
175 error:
176 blockput(b);
177 return -1;
180 static void
181 ffsclose(Fsys *fsys)
183 Ffs *fs;
185 fs = fsys->priv;
186 if(fs->cg)
187 free(fs->cg);
188 free(fs);
189 free(fsys);
192 static int
193 checkfsblk(Fsblk *super)
195 fprint(2, "ffs magic 0x%ux\n", super->magic);
196 if(super->magic == FSMAGIC){
197 super->time = super->_time;
198 super->nfrag = super->_nfrag;
199 super->ndfrag = super->_ndfrag;
200 super->flags = super->_flags;
201 return 1;
203 if(super->magic == FSMAGIC2){
204 return 2;
207 werrstr("bad super block");
208 return -1;
211 static int
212 checkcgblk(Cgblk *cg)
214 if(cg->magic != CGMAGIC){
215 werrstr("bad cylinder group block");
216 return -1;
218 return 0;
221 /*
222 * Read block #bno from the disk, zeroing unused data.
223 * If there is no data whatsoever, it's okay to return nil.
224 */
225 int nskipx;
226 static Block*
227 ffsblockread(Fsys *fsys, u64int bno)
229 int i, o;
230 u8int *fmap;
231 int frag, fsize, avail;
232 Block *b;
233 Cgblk *cgblk;
234 Ffs *fs;
236 fs = fsys->priv;
237 i = bno / fs->blockspergroup;
238 o = bno % fs->blockspergroup;
239 if(i >= fs->ncg)
240 return nil;
242 if((cgblk = ffscylgrp(fs, i, &b)) == nil)
243 return nil;
245 fmap = (u8int*)cgblk+cgblk->fmapoff;
246 frag = fs->fragsperblock;
247 switch(frag){
248 default:
249 sysfatal("bad frag");
250 case 8:
251 avail = fmap[o];
252 break;
253 case 4:
254 avail = (fmap[o>>1] >> ((o&1)*4)) & 0xF;
255 break;
256 case 2:
257 avail = (fmap[o>>2] >> ((o&3)*2)) & 0x3;
258 break;
259 case 1:
260 avail = (fmap[o>>3] >> (o&7)) & 0x1;
261 break;
263 blockput(b);
265 if(avail == ((1<<frag)-1))
267 nskipx++;
268 return nil;
270 if((b = diskread(fs->disk, fs->blocksize, bno*fs->blocksize)) == nil){
271 fprint(2, "diskread failed!!!\n");
272 return nil;
275 fsize = fs->fragsize;
276 for(i=0; i<frag; i++)
277 if(avail & (1<<i))
278 memset(b->data + fsize*i, 0, fsize);
279 return b;
282 static Block*
283 ffsdatablock(Ffs *fs, u64int bno, int size)
285 int fsize;
286 u64int diskaddr;
287 Block *b;
289 if(bno == 0)
290 return nil;
292 fsize = size;
293 if(fsize < fs->fragsize)
294 fsize = fs->fragsize;
296 if(bno >= fs->nfrag){
297 fprint(2, "ffs: request for block %#lux; nfrag %#llux\n", (ulong)bno, fs->nfrag);
298 return nil;
300 diskaddr = (u64int)bno*fs->fragsize;
301 b = diskread(fs->disk, fsize, diskaddr);
302 if(b == nil){
303 fprint(2, "ffs: disk i/o at %#llux for %#ux: %r\n", diskaddr, fsize);
304 return nil;
306 if(b->len < fsize){
307 fprint(2, "ffs: disk i/o at %#llux for %#ux got %#ux\n", diskaddr, fsize,
308 b->len);
309 blockput(b);
310 return nil;
313 return b;
316 static u64int
317 ifetch(Ffs *fs, u64int bno, u32int off)
319 Block *b;
321 if(bno == BADBNO)
322 return BADBNO;
323 b = ffsdatablock(fs, bno, fs->blocksize);
324 if(b == nil)
325 return BADBNO;
326 if(fs->ufs == 2)
327 bno = ((u64int*)b->data)[off];
328 else
329 bno = ((u32int*)b->data)[off];
330 blockput(b);
331 return bno;
334 static u64int
335 ffsfileblockno(Ffs *fs, Inode *ino, u64int bno)
337 int ppb;
339 if(bno < NDADDR){
340 if(debug) fprint(2, "ffsfileblock %lud: direct %#lux\n", (ulong)bno, (ulong)ino->db[bno]);
341 return ino->db[bno];
343 bno -= NDADDR;
344 ppb = fs->blocksize/4;
346 if(bno < ppb) /* single indirect */
347 return ifetch(fs, ino->ib[0], bno);
348 bno -= ppb;
350 if(bno < ppb*ppb)
351 return ifetch(fs, ifetch(fs, ino->ib[1], bno/ppb), bno%ppb);
352 bno -= ppb*ppb;
354 if(bno/ppb/ppb/ppb == 0) /* bno < ppb*ppb*ppb w/o overflow */
355 return ifetch(fs, ifetch(fs, ifetch(fs, ino->ib[2], bno/ppb/ppb), (bno/ppb)%ppb), bno%ppb);
357 fprint(2, "ffsfileblock %llud: way too big\n", bno+NDADDR+ppb+ppb*ppb);
358 return BADBNO;
361 static Block*
362 ffsfileblock(Ffs *fs, Inode *ino, u64int bno, int size)
364 u64int b;
366 b = ffsfileblockno(fs, ino, bno);
367 if(b == ~0)
368 return nil;
369 return ffsdatablock(fs, b, size);
372 /*
373 * NFS handles are 4-byte inode number.
374 */
375 static void
376 mkhandle(Nfs3Handle *h, u64int ino)
378 h->h[0] = ino >> 24;
379 h->h[1] = ino >> 16;
380 h->h[2] = ino >> 8;
381 h->h[3] = ino;
382 h->len = 4;
385 static u32int
386 byte2u32(uchar *p)
388 return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
391 static u64int lastiaddr; /* debugging */
393 static void
394 inode1to2(Inode1 *i1, Inode *i2)
396 int i;
398 memset(i2, 0, sizeof *i2);
399 i2->mode = i1->mode;
400 i2->nlink = i1->nlink;
401 i2->size = i1->size;
402 i2->atime = i1->atime;
403 i2->atimensec = i1->atimensec;
404 i2->mtime = i1->mtime;
405 i2->mtimensec = i1->mtimensec;
406 i2->ctime = i1->ctime;
407 i2->ctimensec = i1->ctimensec;
408 for(i=0; i<NDADDR; i++)
409 i2->db[i] = i1->db[i];
410 for(i=0; i<NIADDR; i++)
411 i2->ib[i] = i1->ib[i];
412 i2->flags = i1->flags;
413 i2->nblock = i1->nblock;
414 i2->gen = i1->gen;
415 i2->uid = i1->uid;
416 i2->gid = i1->gid;
419 static Nfs3Status
420 handle2ino(Ffs *fs, Nfs3Handle *h, u32int *pinum, Inode *ino)
422 int i;
423 u32int ioff;
424 u32int inum;
425 u64int iaddr;
426 Block *b;
427 Cylgrp *cg;
428 Inode1 ino1;
430 if(h->len != 4)
431 return Nfs3ErrBadHandle;
432 inum = byte2u32(h->h);
433 if(pinum)
434 *pinum = inum;
435 if(debug) print("inum %d...", (int)inum);
437 /* fetch inode from disk */
438 i = inum / fs->inospergroup;
439 ioff = inum % fs->inospergroup;
440 if(debug)print("cg %d off %d...", i, (int)ioff);
441 if(i >= fs->ncg)
442 return Nfs3ErrBadHandle;
443 cg = &fs->cg[i];
445 if(debug) print("cg->ibno %lld ufs %d...", cg->ibno, fs->ufs);
446 iaddr = (cg->ibno+ioff/fs->inosperblock)*(vlong)fs->blocksize;
447 ioff = ioff%fs->inosperblock;
448 if((b = diskread(fs->disk, fs->blocksize, iaddr)) == nil)
449 return Nfs3ErrIo;
450 if(fs->ufs == 2){
451 *ino = ((Inode*)b->data)[ioff];
452 lastiaddr = iaddr+ioff*sizeof(Inode);
453 }else{
454 ino1 = ((Inode1*)b->data)[ioff];
455 inode1to2(&ino1, ino);
456 lastiaddr = iaddr+ioff*sizeof(Inode1);
458 blockput(b);
459 return Nfs3Ok;
462 static Nfs3Status
463 ffsroot(Fsys *fsys, Nfs3Handle *h)
465 USED(fsys);
466 mkhandle(h, 2);
467 return Nfs3Ok;
470 static Nfs3Status
471 ino2attr(Ffs *fs, Inode *ino, u32int inum, Nfs3Attr *attr)
473 u32int rdev;
475 attr->type = -1;
476 switch(ino->mode&IFMT){
477 case IFIFO:
478 attr->type = Nfs3FileFifo;
479 break;
480 case IFCHR:
481 attr->type = Nfs3FileChar;
482 break;
483 case IFDIR:
484 attr->type = Nfs3FileDir;
485 break;
486 case IFBLK:
487 attr->type = Nfs3FileBlock;
488 break;
489 case IFREG:
490 attr->type = Nfs3FileReg;
491 break;
492 case IFLNK:
493 attr->type = Nfs3FileSymlink;
494 break;
495 case IFSOCK:
496 attr->type = Nfs3FileSocket;
497 break;
498 case IFWHT:
499 default:
500 return Nfs3ErrBadHandle;
503 attr->mode = ino->mode&07777;
504 attr->nlink = ino->nlink;
505 attr->uid = ino->uid;
506 attr->gid = ino->gid;
507 attr->size = ino->size;
508 attr->used = ino->nblock*fs->blocksize;
509 if(attr->type==Nfs3FileBlock || attr->type==Nfs3FileChar){
510 rdev = ino->db[0];
511 attr->major = (rdev>>8)&0xFF;
512 attr->minor = rdev & 0xFFFF00FF;
513 }else{
514 attr->major = 0;
515 attr->minor = 0;
517 attr->fsid = 0;
518 attr->fileid = inum;
519 attr->atime.sec = ino->atime;
520 attr->atime.nsec = ino->atimensec;
521 attr->mtime.sec = ino->mtime;
522 attr->mtime.nsec = ino->mtimensec;
523 attr->ctime.sec = ino->ctime;
524 attr->ctime.nsec = ino->ctimensec;
525 return Nfs3Ok;
528 static int
529 ingroup(SunAuthUnix *au, uint gid)
531 int i;
533 for(i=0; i<au->ng; i++)
534 if(au->g[i] == gid)
535 return 1;
536 return 0;
539 static Nfs3Status
540 inoperm(Inode *ino, SunAuthUnix *au, int need)
542 int have;
544 if(au == nil)
545 return Nfs3Ok;
547 have = ino->mode&0777;
548 if(ino->uid == au->uid)
549 have >>= 6;
550 else if(ino->gid == au->gid || ingroup(au, ino->gid))
551 have >>= 3;
553 if((have&need) != need)
554 return Nfs3ErrNotOwner; /* really EPERM */
555 return Nfs3Ok;
558 static Nfs3Status
559 ffsgetattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
561 Inode ino;
562 u32int inum;
563 Ffs *fs;
564 Nfs3Status ok;
566 fs = fsys->priv;
567 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
568 return ok;
570 USED(au); /* anyone can getattr */
572 return ino2attr(fs, &ino, inum, attr);
575 static Nfs3Status
576 ffsaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
578 int have;
579 Inode ino;
580 u32int inum;
581 Ffs *fs;
582 Nfs3Status ok;
584 fs = fsys->priv;
585 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
586 return ok;
588 have = ino.mode&0777;
589 if(ino.uid == au->uid)
590 have >>= 6;
591 else if(ino.gid == au->gid || ingroup(au, ino.gid))
592 have >>= 3;
594 *got = 0;
595 if((want&Nfs3AccessRead) && (have&AREAD))
596 *got |= Nfs3AccessRead;
597 if((want&Nfs3AccessLookup) && (ino.mode&IFMT)==IFDIR && (have&AEXEC))
598 *got |= Nfs3AccessLookup;
599 if((want&Nfs3AccessExecute) && (ino.mode&IFMT)!=IFDIR && (have&AEXEC))
600 *got |= Nfs3AccessExecute;
602 return ino2attr(fs, &ino, inum, attr);
605 static Nfs3Status
606 ffslookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
608 u32int nblock;
609 u32int i;
610 uchar *p, *ep;
611 Dirent *de;
612 Inode ino;
613 Block *b;
614 Ffs *fs;
615 Nfs3Status ok;
616 int len, want;
618 fs = fsys->priv;
619 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
620 return ok;
622 if((ino.mode&IFMT) != IFDIR)
623 return Nfs3ErrNotDir;
625 if((ok = inoperm(&ino, au, AEXEC)) != Nfs3Ok)
626 return ok;
628 len = strlen(name);
629 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
630 for(i=0; i<nblock; i++){
631 if(i==nblock-1)
632 want = ino.size % fs->blocksize;
633 else
634 want = fs->blocksize;
635 b = ffsfileblock(fs, &ino, i, want);
636 if(b == nil)
637 continue;
638 p = b->data;
639 ep = p+b->len;
640 while(p < ep){
641 de = (Dirent*)p;
642 if(de->reclen == 0){
643 if(debug)
644 fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
645 break;
647 p += de->reclen;
648 if(p > ep){
649 if(debug)
650 fprint(2, "bad len %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
651 break;
653 if(de->ino == 0)
654 continue;
655 if(4+2+2+de->namlen > de->reclen){
656 if(debug)
657 fprint(2, "bad namelen %d at offset %d of %d\n", de->namlen, (int)(p-b->data), b->len);
658 break;
660 if(de->namlen == len && memcmp(de->name, name, len) == 0){
661 mkhandle(nh, de->ino);
662 blockput(b);
663 return Nfs3Ok;
666 blockput(b);
668 return Nfs3ErrNoEnt;
671 static Nfs3Status
672 ffsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
674 u32int nblock;
675 u32int i;
676 int off, done;
677 uchar *data, *dp, *dep, *p, *ep, *ndp;
678 Dirent *de;
679 Inode ino;
680 Block *b;
681 Ffs *fs;
682 Nfs3Status ok;
683 Nfs3Entry e;
684 int want;
686 fs = fsys->priv;
687 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
688 return ok;
690 if((ino.mode&IFMT) != IFDIR)
691 return Nfs3ErrNotDir;
693 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
694 return ok;
696 if(cookie >= ino.size){
697 *pcount = 0;
698 *pdata = 0;
699 return Nfs3Ok;
702 dp = malloc(count);
703 data = dp;
704 if(dp == nil)
705 return Nfs3ErrNoMem;
706 dep = dp+count;
707 *peof = 0;
708 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
709 i = cookie/fs->blocksize;
710 off = cookie%fs->blocksize;
711 done = 0;
712 for(; i<nblock && !done; i++){
713 if(i==nblock-1)
714 want = ino.size % fs->blocksize;
715 else
716 want = fs->blocksize;
717 b = ffsfileblock(fs, &ino, i, want);
718 if(b == nil)
719 continue;
720 p = b->data;
721 ep = p+b->len;
722 memset(&e, 0, sizeof e);
723 while(p < ep){
724 de = (Dirent*)p;
725 if(de->reclen == 0){
726 if(debug) fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
727 break;
729 p += de->reclen;
730 if(p > ep){
731 if(debug) fprint(2, "reclen %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
732 break;
734 if(de->ino == 0){
735 if(debug) fprint(2, "zero inode\n");
736 continue;
738 if(4+2+2+de->namlen > de->reclen){
739 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);
740 break;
742 if(de->name[de->namlen] != 0){
743 if(debug) fprint(2, "bad name %d %.*s\n", de->namlen, de->namlen, de->name);
744 continue;
746 if(debug) print("%s/%d ", de->name, (int)de->ino);
747 if((uchar*)de - b->data < off)
748 continue;
749 e.fileid = de->ino;
750 e.name = de->name;
751 e.cookie = (u64int)i*fs->blocksize + (p - b->data);
752 if(nfs3entrypack(dp, dep, &ndp, &e) < 0){
753 done = 1;
754 break;
756 dp = ndp;
758 off = 0;
759 blockput(b);
761 if(i==nblock)
762 *peof = 1;
764 *pcount = dp - data;
765 *pdata = data;
766 return Nfs3Ok;
769 static u64int
770 ffsxfileblock(Fsys *fsys, Nfs3Handle *h, u64int offset)
772 u64int bno;
773 Inode ino;
774 Nfs3Status ok;
775 Ffs *fs;
777 fs = fsys->priv;
778 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok){
779 nfs3errstr(ok);
780 return 0;
782 if(offset == 1) /* clumsy hack for debugging */
783 return lastiaddr;
784 if(offset >= ino.size){
785 werrstr("beyond end of file");
786 return 0;
788 bno = offset/fs->blocksize;
789 bno = ffsfileblockno(fs, &ino, bno);
790 if(bno == ~0)
791 return 0;
792 return bno*(u64int)fs->fragsize;
795 static Nfs3Status
796 ffsreadfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count,
797 u64int offset, uchar **pdata, u32int *pcount, u1int *peof)
799 uchar *data;
800 Block *b;
801 Ffs *fs;
802 int off, want, fragcount;
803 Inode ino;
804 Nfs3Status ok;
806 fs = fsys->priv;
807 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
808 return ok;
810 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
811 return ok;
813 if(offset >= ino.size){
814 *pdata = 0;
815 *pcount = 0;
816 *peof = 1;
817 return Nfs3Ok;
819 if(offset+count > ino.size)
820 count = ino.size-offset;
821 if(offset/fs->blocksize != (offset+count-1)/fs->blocksize)
822 count = fs->blocksize - offset%fs->blocksize;
824 data = malloc(count);
825 if(data == nil)
826 return Nfs3ErrNoMem;
828 want = offset%fs->blocksize+count;
829 if(want%fs->fragsize)
830 want += fs->fragsize - want%fs->fragsize;
832 b = ffsfileblock(fs, &ino, offset/fs->blocksize, want);
833 if(b == nil){
834 /* BUG: distinguish sparse file from I/O error */
835 memset(data, 0, count);
836 }else{
837 off = offset%fs->blocksize;
838 fragcount = count; /* need signed variable */
839 if(off+fragcount > b->len){
840 fragcount = b->len - off;
841 if(fragcount < 0)
842 fragcount = 0;
844 if(fragcount > 0)
845 memmove(data, b->data+off, fragcount);
846 count = fragcount;
847 blockput(b);
849 *peof = (offset+count == ino.size);
850 *pcount = count;
851 *pdata = data;
852 return Nfs3Ok;
855 static Nfs3Status
856 ffsreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link)
858 Ffs *fs;
859 Nfs3Status ok;
860 int len;
861 Inode ino;
862 Block *b;
864 fs = fsys->priv;
865 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
866 return ok;
867 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
868 return ok;
870 if(ino.size > 1024)
871 return Nfs3ErrIo;
872 len = ino.size;
874 if(ino.nblock != 0){
875 /* assumes symlink fits in one block */
876 b = ffsfileblock(fs, &ino, 0, len);
877 if(b == nil)
878 return Nfs3ErrIo;
879 if(memchr(b->data, 0, len) != nil){
880 blockput(b);
881 return Nfs3ErrIo;
883 *link = malloc(len+1);
884 if(*link == 0){
885 blockput(b);
886 return Nfs3ErrNoMem;
888 memmove(*link, b->data, len);
889 (*link)[len] = 0;
890 blockput(b);
891 return Nfs3Ok;
894 if(len > sizeof ino.db + sizeof ino.ib)
895 return Nfs3ErrIo;
897 *link = malloc(len+1);
898 if(*link == 0)
899 return Nfs3ErrNoMem;
900 memmove(*link, ino.db, ino.size);
901 (*link)[len] = 0;
902 return Nfs3Ok;