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 for(i=0; i<nelem(off); i++){
102 if((b = diskread(disk, SBSIZE, off[i])) == nil)
103 goto error;
104 fsblk = (Fsblk*)b->data;
105 fprint(2, "offset of magic: %d\n", offsetof(Fsblk, magic));
106 if((fs->ufs = checkfsblk(fsblk)) > 0)
107 goto okay;
108 blockput(b);
110 goto error;
112 okay:
113 fs->blocksize = fsblk->blocksize;
114 fs->nblock = (fsblk->nfrag+fsblk->fragsperblock-1) / fsblk->fragsperblock;
115 fs->fragsize = fsblk->fragsize;
116 fs->fragspergroup = fsblk->fragspergroup;
117 fs->fragsperblock = fsblk->fragsperblock;
118 fs->inosperblock = fsblk->inosperblock;
119 fs->inospergroup = fsblk->inospergroup;
121 fs->nfrag = fsblk->nfrag;
122 fs->ndfrag = fsblk->ndfrag;
123 /*
124 * used to use
125 * fs->blockspergroup = (u64int)fsblk->_cylspergroup *
126 * fsblk->secspercyl * BYTESPERSEC / fsblk->blocksize;
127 * for UFS1, but this should work for both UFS1 and UFS2
128 */
129 fs->blockspergroup = (u64int)fsblk->fragspergroup / fsblk->fragsperblock;
130 fs->ncg = fsblk->ncg;
132 fsys->blocksize = fs->blocksize;
133 fsys->nblock = fs->nblock;
135 if(debug) fprint(2, "ffs %lld %d-byte blocks, %d cylinder groups\n",
136 fs->nblock, fs->blocksize, fs->ncg);
137 if(debug) fprint(2, "\tinospergroup %d perblock %d blockspergroup %lld\n",
138 fs->inospergroup, fs->inosperblock, fs->blockspergroup);
140 if(fs->cg == nil)
141 fs->cg = emalloc(fs->ncg*sizeof(Cylgrp));
142 for(i=0; i<fs->ncg; i++){
143 cg = &fs->cg[i];
144 if(fs->ufs == 2)
145 cg->bno = (u64int)fs->blockspergroup*i;
146 else
147 cg->bno = fs->blockspergroup*i + fsblk->_cgoffset * (i & ~fsblk->_cgmask);
148 cg->cgblkno = cg->bno + fsblk->cfragno/fs->fragsperblock;
149 cg->ibno = cg->bno + fsblk->ifragno/fs->fragsperblock;
150 cg->dbno = cg->bno + fsblk->dfragno/fs->fragsperblock;
152 if(checkcg){
153 if((cgb = diskread(disk, fs->blocksize, (u64int)cg->cgblkno*fs->blocksize)) == nil)
154 goto error;
156 cgblk = (Cgblk*)cgb->data;
157 if(checkcgblk(cgblk) < 0){
158 blockput(cgb);
159 goto error;
161 if(cgblk->nfrag % fs->fragsperblock && i != fs->ncg-1){
162 werrstr("fractional number of blocks in non-last cylinder group %d", cgblk->nfrag);
163 blockput(cgb);
164 goto error;
166 // cg->nfrag = cgblk->nfrag;
167 // cg->nblock = (cgblk->nfrag+fs->fragsperblock-1) / fs->fragsperblock;
168 // fprint(2, "cg #%d: cgblk %lud, %d blocks, %d inodes\n", cgblk->num, (ulong)cg->cgblkno, cg->nblock, cg->nino);
171 blockput(b);
172 return 0;
174 error:
175 blockput(b);
176 return -1;
179 static void
180 ffsclose(Fsys *fsys)
182 Ffs *fs;
184 fs = fsys->priv;
185 if(fs->cg)
186 free(fs->cg);
187 free(fs);
188 free(fsys);
191 static int
192 checkfsblk(Fsblk *super)
194 fprint(2, "ffs magic 0x%ux\n", super->magic);
195 if(super->magic == FSMAGIC){
196 super->time = super->_time;
197 super->nfrag = super->_nfrag;
198 super->ndfrag = super->_ndfrag;
199 super->flags = super->_flags;
200 return 1;
202 if(super->magic == FSMAGIC2){
203 return 2;
206 werrstr("bad super block");
207 return -1;
210 static int
211 checkcgblk(Cgblk *cg)
213 if(cg->magic != CGMAGIC){
214 werrstr("bad cylinder group block");
215 return -1;
217 return 0;
220 /*
221 * Read block #bno from the disk, zeroing unused data.
222 * If there is no data whatsoever, it's okay to return nil.
223 */
224 int nskipx;
225 static Block*
226 ffsblockread(Fsys *fsys, u64int bno)
228 int i, o;
229 u8int *fmap;
230 int frag, fsize, avail;
231 Block *b;
232 Cgblk *cgblk;
233 Ffs *fs;
235 fs = fsys->priv;
236 i = bno / fs->blockspergroup;
237 o = bno % fs->blockspergroup;
238 if(i >= fs->ncg)
239 return nil;
241 if((cgblk = ffscylgrp(fs, i, &b)) == nil)
242 return nil;
244 fmap = (u8int*)cgblk+cgblk->fmapoff;
245 frag = fs->fragsperblock;
246 switch(frag){
247 default:
248 sysfatal("bad frag");
249 case 8:
250 avail = fmap[o];
251 break;
252 case 4:
253 avail = (fmap[o>>1] >> ((o&1)*4)) & 0xF;
254 break;
255 case 2:
256 avail = (fmap[o>>2] >> ((o&3)*2)) & 0x3;
257 break;
258 case 1:
259 avail = (fmap[o>>3] >> (o&7)) & 0x1;
260 break;
262 blockput(b);
264 if(avail == ((1<<frag)-1))
266 nskipx++;
267 return nil;
269 if((b = diskread(fs->disk, fs->blocksize, bno*fs->blocksize)) == nil){
270 fprint(2, "diskread failed!!!\n");
271 return nil;
274 fsize = fs->fragsize;
275 for(i=0; i<frag; i++)
276 if(avail & (1<<i))
277 memset(b->data + fsize*i, 0, fsize);
278 return b;
281 static Block*
282 ffsdatablock(Ffs *fs, u64int bno, int size)
284 int fsize;
285 u64int diskaddr;
286 Block *b;
288 if(bno == 0)
289 return nil;
291 fsize = size;
292 if(fsize < fs->fragsize)
293 fsize = fs->fragsize;
295 if(bno >= fs->nfrag){
296 fprint(2, "ffs: request for block %#lux; nfrag %#x\n", (ulong)bno, fs->nfrag);
297 return nil;
299 diskaddr = (u64int)bno*fs->fragsize;
300 b = diskread(fs->disk, fsize, diskaddr);
301 if(b == nil){
302 fprint(2, "ffs: disk i/o at %#llux for %#ux: %r\n", diskaddr, fsize);
303 return nil;
305 if(b->len < fsize){
306 fprint(2, "ffs: disk i/o at %#llux for %#ux got %#ux\n", diskaddr, fsize,
307 b->len);
308 blockput(b);
309 return nil;
312 return b;
315 static u64int
316 ifetch(Ffs *fs, u64int bno, u32int off)
318 Block *b;
320 if(bno == BADBNO)
321 return BADBNO;
322 b = ffsdatablock(fs, bno, fs->blocksize);
323 if(b == nil)
324 return BADBNO;
325 if(fs->ufs == 2)
326 bno = ((u64int*)b->data)[off];
327 else
328 bno = ((u32int*)b->data)[off];
329 blockput(b);
330 return bno;
333 static u64int
334 ffsfileblockno(Ffs *fs, Inode *ino, u64int bno)
336 int ppb;
338 if(bno < NDADDR){
339 if(debug) fprint(2, "ffsfileblock %lud: direct %#lux\n", (ulong)bno, (ulong)ino->db[bno]);
340 return ino->db[bno];
342 bno -= NDADDR;
343 ppb = fs->blocksize/4;
345 if(bno < ppb) /* single indirect */
346 return ifetch(fs, ino->ib[0], bno);
347 bno -= ppb;
349 if(bno < ppb*ppb)
350 return ifetch(fs, ifetch(fs, ino->ib[1], bno/ppb), bno%ppb);
351 bno -= ppb*ppb;
353 if(bno/ppb/ppb/ppb == 0) /* bno < ppb*ppb*ppb w/o overflow */
354 return ifetch(fs, ifetch(fs, ifetch(fs, ino->ib[2], bno/ppb/ppb), (bno/ppb)%ppb), bno%ppb);
356 fprint(2, "ffsfileblock %llud: way too big\n", bno+NDADDR+ppb+ppb*ppb);
357 return BADBNO;
360 static Block*
361 ffsfileblock(Ffs *fs, Inode *ino, u64int bno, int size)
363 u64int b;
365 b = ffsfileblockno(fs, ino, bno);
366 if(b == ~0)
367 return nil;
368 return ffsdatablock(fs, b, size);
371 /*
372 * NFS handles are 4-byte inode number.
373 */
374 static void
375 mkhandle(Nfs3Handle *h, u64int ino)
377 h->h[0] = ino >> 24;
378 h->h[1] = ino >> 16;
379 h->h[2] = ino >> 8;
380 h->h[3] = ino;
381 h->len = 4;
384 static u32int
385 byte2u32(uchar *p)
387 return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
390 static u64int lastiaddr; /* debugging */
392 static void
393 inode1to2(Inode1 *i1, Inode *i2)
395 int i;
397 memset(i2, 0, sizeof *i2);
398 i2->mode = i1->mode;
399 i2->nlink = i1->nlink;
400 i2->size = i1->size;
401 i2->atime = i1->atime;
402 i2->atimensec = i1->atimensec;
403 i2->mtime = i1->mtime;
404 i2->mtimensec = i1->mtimensec;
405 i2->ctime = i1->ctime;
406 i2->ctimensec = i1->ctimensec;
407 for(i=0; i<NDADDR; i++)
408 i2->db[i] = i1->db[i];
409 for(i=0; i<NIADDR; i++)
410 i2->ib[i] = i1->ib[i];
411 i2->flags = i1->flags;
412 i2->nblock = i1->nblock;
413 i2->gen = i1->gen;
414 i2->uid = i1->uid;
415 i2->gid = i1->gid;
418 static Nfs3Status
419 handle2ino(Ffs *fs, Nfs3Handle *h, u32int *pinum, Inode *ino)
421 int i;
422 u32int ioff;
423 u32int inum;
424 u64int iaddr;
425 Block *b;
426 Cylgrp *cg;
427 Inode1 ino1;
429 if(h->len != 4)
430 return Nfs3ErrBadHandle;
431 inum = byte2u32(h->h);
432 if(pinum)
433 *pinum = inum;
434 if(debug) print("inum %d...", (int)inum);
436 /* fetch inode from disk */
437 i = inum / fs->inospergroup;
438 ioff = inum % fs->inospergroup;
439 if(debug)print("cg %d off %d...", i, (int)ioff);
440 if(i >= fs->ncg)
441 return Nfs3ErrBadHandle;
442 cg = &fs->cg[i];
444 if(debug) print("cg->ibno %lld ufs %d...", cg->ibno, fs->ufs);
445 iaddr = (cg->ibno+ioff/fs->inosperblock)*(vlong)fs->blocksize;
446 ioff = ioff%fs->inosperblock;
447 if((b = diskread(fs->disk, fs->blocksize, iaddr)) == nil)
448 return Nfs3ErrIo;
449 if(fs->ufs == 2){
450 *ino = ((Inode*)b->data)[ioff];
451 lastiaddr = iaddr+ioff*sizeof(Inode);
452 }else{
453 ino1 = ((Inode1*)b->data)[ioff];
454 inode1to2(&ino1, ino);
455 lastiaddr = iaddr+ioff*sizeof(Inode1);
457 blockput(b);
458 return Nfs3Ok;
461 static Nfs3Status
462 ffsroot(Fsys *fsys, Nfs3Handle *h)
464 USED(fsys);
465 mkhandle(h, 2);
466 return Nfs3Ok;
469 static Nfs3Status
470 ino2attr(Ffs *fs, Inode *ino, u32int inum, Nfs3Attr *attr)
472 u32int rdev;
474 attr->type = -1;
475 switch(ino->mode&IFMT){
476 case IFIFO:
477 attr->type = Nfs3FileFifo;
478 break;
479 case IFCHR:
480 attr->type = Nfs3FileChar;
481 break;
482 case IFDIR:
483 attr->type = Nfs3FileDir;
484 break;
485 case IFBLK:
486 attr->type = Nfs3FileBlock;
487 break;
488 case IFREG:
489 attr->type = Nfs3FileReg;
490 break;
491 case IFLNK:
492 attr->type = Nfs3FileSymlink;
493 break;
494 case IFSOCK:
495 attr->type = Nfs3FileSocket;
496 break;
497 case IFWHT:
498 default:
499 return Nfs3ErrBadHandle;
502 attr->mode = ino->mode&07777;
503 attr->nlink = ino->nlink;
504 attr->uid = ino->uid;
505 attr->gid = ino->gid;
506 attr->size = ino->size;
507 attr->used = ino->nblock*fs->blocksize;
508 if(attr->type==Nfs3FileBlock || attr->type==Nfs3FileChar){
509 rdev = ino->db[0];
510 attr->major = (rdev>>8)&0xFF;
511 attr->minor = rdev & 0xFFFF00FF;
512 }else{
513 attr->major = 0;
514 attr->minor = 0;
516 attr->fsid = 0;
517 attr->fileid = inum;
518 attr->atime.sec = ino->atime;
519 attr->atime.nsec = ino->atimensec;
520 attr->mtime.sec = ino->mtime;
521 attr->mtime.nsec = ino->mtimensec;
522 attr->ctime.sec = ino->ctime;
523 attr->ctime.nsec = ino->ctimensec;
524 return Nfs3Ok;
527 static int
528 ingroup(SunAuthUnix *au, uint gid)
530 int i;
532 for(i=0; i<au->ng; i++)
533 if(au->g[i] == gid)
534 return 1;
535 return 0;
538 static Nfs3Status
539 inoperm(Inode *ino, SunAuthUnix *au, int need)
541 int have;
543 if(au == nil)
544 return Nfs3Ok;
546 have = ino->mode&0777;
547 if(ino->uid == au->uid)
548 have >>= 6;
549 else if(ino->gid == au->gid || ingroup(au, ino->gid))
550 have >>= 3;
552 if((have&need) != need)
553 return Nfs3ErrNotOwner; /* really EPERM */
554 return Nfs3Ok;
557 static Nfs3Status
558 ffsgetattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
560 Inode ino;
561 u32int inum;
562 Ffs *fs;
563 Nfs3Status ok;
565 fs = fsys->priv;
566 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
567 return ok;
569 USED(au); /* anyone can getattr */
571 return ino2attr(fs, &ino, inum, attr);
574 static Nfs3Status
575 ffsaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
577 int have;
578 Inode ino;
579 u32int inum;
580 Ffs *fs;
581 Nfs3Status ok;
583 fs = fsys->priv;
584 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
585 return ok;
587 have = ino.mode&0777;
588 if(ino.uid == au->uid)
589 have >>= 6;
590 else if(ino.gid == au->gid || ingroup(au, ino.gid))
591 have >>= 3;
593 *got = 0;
594 if((want&Nfs3AccessRead) && (have&AREAD))
595 *got |= Nfs3AccessRead;
596 if((want&Nfs3AccessLookup) && (ino.mode&IFMT)==IFDIR && (have&AEXEC))
597 *got |= Nfs3AccessLookup;
598 if((want&Nfs3AccessExecute) && (ino.mode&IFMT)!=IFDIR && (have&AEXEC))
599 *got |= Nfs3AccessExecute;
601 return ino2attr(fs, &ino, inum, attr);
604 static Nfs3Status
605 ffslookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
607 u32int nblock;
608 u32int i;
609 uchar *p, *ep;
610 Dirent *de;
611 Inode ino;
612 Block *b;
613 Ffs *fs;
614 Nfs3Status ok;
615 int len, want;
617 fs = fsys->priv;
618 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
619 return ok;
621 if((ino.mode&IFMT) != IFDIR)
622 return Nfs3ErrNotDir;
624 if((ok = inoperm(&ino, au, AEXEC)) != Nfs3Ok)
625 return ok;
627 len = strlen(name);
628 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
629 for(i=0; i<nblock; i++){
630 if(i==nblock-1)
631 want = ino.size % fs->blocksize;
632 else
633 want = fs->blocksize;
634 b = ffsfileblock(fs, &ino, i, want);
635 if(b == nil)
636 continue;
637 p = b->data;
638 ep = p+b->len;
639 while(p < ep){
640 de = (Dirent*)p;
641 if(de->reclen == 0){
642 if(debug)
643 fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
644 break;
646 p += de->reclen;
647 if(p > ep){
648 if(debug)
649 fprint(2, "bad len %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
650 break;
652 if(de->ino == 0)
653 continue;
654 if(4+2+2+de->namlen > de->reclen){
655 if(debug)
656 fprint(2, "bad namelen %d at offset %d of %d\n", de->namlen, (int)(p-b->data), b->len);
657 break;
659 if(de->namlen == len && memcmp(de->name, name, len) == 0){
660 mkhandle(nh, de->ino);
661 blockput(b);
662 return Nfs3Ok;
665 blockput(b);
667 return Nfs3ErrNoEnt;
670 static Nfs3Status
671 ffsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
673 u32int nblock;
674 u32int i;
675 int off, done;
676 uchar *data, *dp, *dep, *p, *ep, *ndp;
677 Dirent *de;
678 Inode ino;
679 Block *b;
680 Ffs *fs;
681 Nfs3Status ok;
682 Nfs3Entry e;
683 int want;
685 fs = fsys->priv;
686 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
687 return ok;
689 if((ino.mode&IFMT) != IFDIR)
690 return Nfs3ErrNotDir;
692 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
693 return ok;
695 if(cookie >= ino.size){
696 *pcount = 0;
697 *pdata = 0;
698 return Nfs3Ok;
701 dp = malloc(count);
702 data = dp;
703 if(dp == nil)
704 return Nfs3ErrNoMem;
705 dep = dp+count;
706 *peof = 0;
707 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
708 i = cookie/fs->blocksize;
709 off = cookie%fs->blocksize;
710 done = 0;
711 for(; i<nblock && !done; i++){
712 if(i==nblock-1)
713 want = ino.size % fs->blocksize;
714 else
715 want = fs->blocksize;
716 b = ffsfileblock(fs, &ino, i, want);
717 if(b == nil)
718 continue;
719 p = b->data;
720 ep = p+b->len;
721 memset(&e, 0, sizeof e);
722 while(p < ep){
723 de = (Dirent*)p;
724 if(de->reclen == 0){
725 if(debug) fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
726 break;
728 p += de->reclen;
729 if(p > ep){
730 if(debug) fprint(2, "reclen %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
731 break;
733 if(de->ino == 0){
734 if(debug) fprint(2, "zero inode\n");
735 continue;
737 if(4+2+2+de->namlen > de->reclen){
738 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);
739 break;
741 if(de->name[de->namlen] != 0){
742 if(debug) fprint(2, "bad name %d %.*s\n", de->namlen, de->namlen, de->name);
743 continue;
745 if(debug) print("%s/%d ", de->name, (int)de->ino);
746 if((uchar*)de - b->data < off)
747 continue;
748 e.fileid = de->ino;
749 e.name = de->name;
750 e.cookie = (u64int)i*fs->blocksize + (p - b->data);
751 if(nfs3entrypack(dp, dep, &ndp, &e) < 0){
752 done = 1;
753 break;
755 dp = ndp;
757 off = 0;
758 blockput(b);
760 if(i==nblock)
761 *peof = 1;
763 *pcount = dp - data;
764 *pdata = data;
765 return Nfs3Ok;
768 static u64int
769 ffsxfileblock(Fsys *fsys, Nfs3Handle *h, u64int offset)
771 u64int bno;
772 Inode ino;
773 Nfs3Status ok;
774 Ffs *fs;
776 fs = fsys->priv;
777 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok){
778 nfs3errstr(ok);
779 return 0;
781 if(offset == 1) /* clumsy hack for debugging */
782 return lastiaddr;
783 if(offset >= ino.size){
784 werrstr("beyond end of file");
785 return 0;
787 bno = offset/fs->blocksize;
788 bno = ffsfileblockno(fs, &ino, bno);
789 if(bno == ~0)
790 return 0;
791 return bno*(u64int)fs->fragsize;
794 static Nfs3Status
795 ffsreadfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count,
796 u64int offset, uchar **pdata, u32int *pcount, u1int *peof)
798 uchar *data;
799 Block *b;
800 Ffs *fs;
801 int off, want, fragcount;
802 Inode ino;
803 Nfs3Status ok;
805 fs = fsys->priv;
806 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
807 return ok;
809 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
810 return ok;
812 if(offset >= ino.size){
813 *pdata = 0;
814 *pcount = 0;
815 *peof = 1;
816 return Nfs3Ok;
818 if(offset+count > ino.size)
819 count = ino.size-offset;
820 if(offset/fs->blocksize != (offset+count-1)/fs->blocksize)
821 count = fs->blocksize - offset%fs->blocksize;
823 data = malloc(count);
824 if(data == nil)
825 return Nfs3ErrNoMem;
827 want = offset%fs->blocksize+count;
828 if(want%fs->fragsize)
829 want += fs->fragsize - want%fs->fragsize;
831 b = ffsfileblock(fs, &ino, offset/fs->blocksize, want);
832 if(b == nil){
833 /* BUG: distinguish sparse file from I/O error */
834 memset(data, 0, count);
835 }else{
836 off = offset%fs->blocksize;
837 fragcount = count; /* need signed variable */
838 if(off+fragcount > b->len){
839 fragcount = b->len - off;
840 if(fragcount < 0)
841 fragcount = 0;
843 if(fragcount > 0)
844 memmove(data, b->data+off, fragcount);
845 count = fragcount;
846 blockput(b);
848 *peof = (offset+count == ino.size);
849 *pcount = count;
850 *pdata = data;
851 return Nfs3Ok;
854 static Nfs3Status
855 ffsreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link)
857 Ffs *fs;
858 Nfs3Status ok;
859 int len;
860 Inode ino;
861 Block *b;
863 fs = fsys->priv;
864 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
865 return ok;
866 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
867 return ok;
869 if(ino.size > 1024)
870 return Nfs3ErrIo;
871 len = ino.size;
873 if(ino.nblock != 0){
874 /* assumes symlink fits in one block */
875 b = ffsfileblock(fs, &ino, 0, len);
876 if(b == nil)
877 return Nfs3ErrIo;
878 if(memchr(b->data, 0, len) != nil){
879 blockput(b);
880 return Nfs3ErrIo;
882 *link = malloc(len+1);
883 if(*link == 0){
884 blockput(b);
885 return Nfs3ErrNoMem;
887 memmove(*link, b->data, len);
888 (*link)[len] = 0;
889 blockput(b);
890 return Nfs3Ok;
893 if(len > sizeof ino.db + sizeof ino.ib)
894 return Nfs3ErrIo;
896 *link = malloc(len+1);
897 if(*link == 0)
898 return Nfs3ErrNoMem;
899 memmove(*link, ino.db, ino.size);
900 (*link)[len] = 0;
901 return Nfs3Ok;