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 checkcg 0
10 #define debug 0
12 static int checkfsblk(Fsblk*);
13 static int checkcgblk(Cgblk*);
14 static Block *ffsblockread(Fsys*, u64int);
15 static int ffssync(Fsys*);
16 static void ffsclose(Fsys*);
18 static Nfs3Status ffsroot(Fsys*, Nfs3Handle*);
19 static Nfs3Status ffsgetattr(Fsys*, SunAuthUnix *au, Nfs3Handle*, Nfs3Attr*);
20 static Nfs3Status ffslookup(Fsys*, SunAuthUnix *au, Nfs3Handle*, char*, Nfs3Handle*);
21 static Nfs3Status ffsreadfile(Fsys*, SunAuthUnix *au, Nfs3Handle*, u32int, u64int, uchar**, u32int*, u1int*);
22 static Nfs3Status ffsreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link);
23 static Nfs3Status ffsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int, u64int, uchar**, u32int*, u1int*);
24 static Nfs3Status ffsaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr);
26 Fsys*
27 fsysopenffs(Disk *disk)
28 {
29 Ffs *fs;
30 Fsys *fsys;
32 fsys = emalloc(sizeof(Fsys));
33 fs = emalloc(sizeof(Ffs));
34 fs->disk = disk;
35 fsys->priv = fs;
36 fsys->type = "ffs";
37 fsys->_readblock = ffsblockread;
38 fsys->_sync = ffssync;
39 fsys->_root = ffsroot;
40 fsys->_getattr = ffsgetattr;
41 fsys->_access = ffsaccess;
42 fsys->_lookup = ffslookup;
43 fsys->_readfile = ffsreadfile;
44 fsys->_readlink = ffsreadlink;
45 fsys->_readdir = ffsreaddir;
47 if(ffssync(fsys) < 0)
48 goto error;
50 return fsys;
52 error:
53 ffsclose(fsys);
54 return nil;
55 }
57 static Cgblk*
58 ffscylgrp(Ffs *fs, int i, Block **pb)
59 {
60 Block *b;
61 Cgblk *cg;
63 if(i >= fs->ncg)
64 return nil;
66 b = diskread(fs->disk, fs->blocksize, (u64int)fs->cg[i].cgblkno*fs->blocksize);
67 if(b == nil)
68 return nil;
69 cg = (Cgblk*)b->data;
70 if(checkcgblk(cg) < 0){
71 fprint(2, "checkcgblk %d %lud: %r\n", i, (ulong)fs->cg[i].cgblkno);
72 blockput(b);
73 return nil;
74 }
75 *pb = b;
76 return cg;
77 }
79 static int
80 ffssync(Fsys *fsys)
81 {
82 int i;
83 Block *b, *cgb;
84 Cgblk *cgblk;
85 Cylgrp *cg;
86 Disk *disk;
87 Ffs *fs;
88 Fsblk *fsblk;
90 fs = fsys->priv;
91 disk = fs->disk;
93 /*
94 * Read super block.
95 */
96 if((b = diskread(disk, SBSIZE, SBOFF)) == nil)
97 goto error;
98 fsblk = (Fsblk*)b->data;
99 if(checkfsblk(fsblk) < 0)
100 goto error;
102 fs->blocksize = fsblk->blocksize;
103 fs->nblock = (fsblk->nfrag+fsblk->fragsperblock-1) / fsblk->fragsperblock;
104 fs->fragsize = fsblk->fragsize;
105 fs->fragspergroup = fsblk->fragspergroup;
106 fs->fragsperblock = fsblk->fragsperblock;
107 fs->inosperblock = fsblk->inosperblock;
108 fs->inospergroup = fsblk->inospergroup;
110 fs->nfrag = fsblk->nfrag;
111 fs->ndfrag = fsblk->ndfrag;
112 fs->blockspergroup = (u64int)fsblk->cylspergroup *
113 fsblk->secspercyl * BYTESPERSEC / fsblk->blocksize;
114 fs->ncg = fsblk->ncg;
116 fsys->blocksize = fs->blocksize;
117 fsys->nblock = fs->nblock;
119 if(0) fprint(2, "ffs %d %d-byte blocks, %d cylinder groups\n",
120 fs->nblock, fs->blocksize, fs->ncg);
122 if(fs->cg == nil)
123 fs->cg = emalloc(fs->ncg*sizeof(Cylgrp));
124 for(i=0; i<fs->ncg; i++){
125 cg = &fs->cg[i];
126 cg->bno = fs->blockspergroup*i + fsblk->cgoffset * (i & ~fsblk->cgmask);
127 cg->cgblkno = cg->bno + fsblk->cfragno/fs->fragsperblock;
128 cg->ibno = cg->bno + fsblk->ifragno/fs->fragsperblock;
129 cg->dbno = cg->bno + fsblk->dfragno/fs->fragsperblock;
131 if(checkcg){
132 if((cgb = diskread(disk, fs->blocksize, (u64int)cg->cgblkno*fs->blocksize)) == nil)
133 goto error;
135 cgblk = (Cgblk*)cgb->data;
136 if(checkcgblk(cgblk) < 0){
137 blockput(cgb);
138 goto error;
140 if(cgblk->nfrag % fs->fragsperblock && i != fs->ncg-1){
141 werrstr("fractional number of blocks in non-last cylinder group %d", cgblk->nfrag);
142 blockput(cgb);
143 goto error;
145 // cg->nfrag = cgblk->nfrag;
146 // cg->nblock = (cgblk->nfrag+fs->fragsperblock-1) / fs->fragsperblock;
147 // fprint(2, "cg #%d: cgblk %lud, %d blocks, %d inodes\n", cgblk->num, (ulong)cg->cgblkno, cg->nblock, cg->nino);
150 blockput(b);
151 return 0;
153 error:
154 blockput(b);
155 return -1;
158 static void
159 ffsclose(Fsys *fsys)
161 Ffs *fs;
163 fs = fsys->priv;
164 if(fs->cg)
165 free(fs->cg);
166 free(fs);
167 free(fsys);
170 static int
171 checkfsblk(Fsblk *super)
173 if(super->magic != FSMAGIC){
174 werrstr("bad super block");
175 return -1;
178 return 0;
181 static int
182 checkcgblk(Cgblk *cg)
184 if(cg->magic != CGMAGIC){
185 werrstr("bad cylinder group block");
186 return -1;
188 return 0;
191 /*
192 * Read block #bno from the disk, zeroing unused data.
193 * If there is no data whatsoever, it's okay to return nil.
194 */
195 int nskipx;
196 static Block*
197 ffsblockread(Fsys *fsys, u64int bno)
199 u32int i, o;
200 u8int *fmap;
201 int frag, fsize, avail;
202 Block *b;
203 // Cylgrp *cg;
204 Cgblk *cgblk;
205 Ffs *fs;
207 fs = fsys->priv;
208 i = bno / fs->blockspergroup;
209 o = bno % fs->blockspergroup;
210 if(i >= fs->ncg)
211 return nil;
212 // cg = &fs->cg[i];
214 // if(o >= cg->nblock)
215 // return nil;
217 if((cgblk = ffscylgrp(fs, i, &b)) == nil)
218 return nil;
220 fmap = (u8int*)cgblk+cgblk->fmapoff;
221 frag = fs->fragsperblock;
222 switch(frag){
223 default:
224 sysfatal("bad frag");
225 case 8:
226 avail = fmap[o];
227 break;
228 case 4:
229 avail = (fmap[o>>1] >> ((o&1)*4)) & 0xF;
230 break;
231 case 2:
232 avail = (fmap[o>>2] >> ((o&3)*2)) & 0x3;
233 break;
234 case 1:
235 avail = (fmap[o>>3] >> (o&7)) & 0x1;
236 break;
238 blockput(b);
240 if(avail == ((1<<frag)-1))
242 nskipx++;
243 return nil;
245 if((b = diskread(fs->disk, fs->blocksize, bno*fs->blocksize)) == nil){
246 fprint(2, "diskread failed!!!\n");
247 return nil;
250 fsize = fs->fragsize;
251 for(i=0; i<frag; i++)
252 if(avail & (1<<i))
253 memset(b->data + fsize*i, 0, fsize);
254 return b;
257 static Block*
258 ffsdatablock(Ffs *fs, u32int bno, int size)
260 int fsize;
261 u64int diskaddr;
262 Block *b;
264 if(bno == 0)
265 return nil;
267 fsize = size;
268 if(fsize < fs->fragsize)
269 fsize = fs->fragsize;
271 if(bno >= fs->nfrag){
272 fprint(2, "ffs: request for block %#lux; nfrag %#x\n", (ulong)bno, fs->nfrag);
273 return nil;
275 diskaddr = (u64int)bno*fs->fragsize;
276 b = diskread(fs->disk, fsize, diskaddr);
277 if(b == nil){
278 fprint(2, "ffs: disk i/o at %#llux for %#ux: %r\n", diskaddr, fsize);
279 return nil;
281 if(b->len < fsize){
282 fprint(2, "ffs: disk i/o at %#llux for %#ux got %#ux\n", diskaddr, fsize,
283 b->len);
284 blockput(b);
285 return nil;
288 return b;
291 static Block*
292 ffsfileblock(Ffs *fs, Inode *ino, u32int bno, int size)
294 int ppb;
295 Block *b;
296 u32int *a;
298 if(bno < NDADDR){
299 if(debug) fprint(2, "ffsfileblock %lud: direct %#lux\n", (ulong)bno, (ulong)ino->db[bno]);
300 return ffsdatablock(fs, ino->db[bno], size);
302 bno -= NDADDR;
303 ppb = fs->blocksize/4;
305 if(bno/ppb < NIADDR){
306 if(debug) fprint(2, "ffsfileblock %lud: indirect %#lux\n", (ulong)(bno+NDADDR),
307 (ulong)ino->ib[bno/ppb]);
308 b = ffsdatablock(fs, ino->ib[bno/ppb], fs->blocksize);
309 if(b == nil)
310 return nil;
311 a = (u32int*)b->data;
312 bno = a[bno%ppb];
313 if(debug) fprint(2, "ffsfileblock: indirect fetch %#lux size %d\n", (ulong)bno, size);
314 blockput(b);
315 return ffsdatablock(fs, bno, size);
318 fprint(2, "ffsfileblock %lud: too big\n", (ulong)bno+NDADDR);
319 return nil;
322 /*
323 * NFS handles are 4-byte inode number.
324 */
325 static void
326 mkhandle(Nfs3Handle *h, u64int ino)
328 h->h[0] = ino >> 24;
329 h->h[1] = ino >> 16;
330 h->h[2] = ino >> 8;
331 h->h[3] = ino;
332 h->len = 4;
335 static u32int
336 byte2u32(uchar *p)
338 return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
341 static Nfs3Status
342 handle2ino(Ffs *fs, Nfs3Handle *h, u32int *pinum, Inode *ino)
344 int i;
345 u32int ioff;
346 u32int inum;
347 Block *b;
348 Cylgrp *cg;
350 if(h->len != 4)
351 return Nfs3ErrBadHandle;
352 inum = byte2u32(h->h);
353 if(pinum)
354 *pinum = inum;
355 if(debug) print("inum %d...", (int)inum);
357 /* fetch inode from disk */
358 i = inum / fs->inospergroup;
359 ioff = inum % fs->inospergroup;
360 if(debug)print("cg %d off %d...", i, (int)ioff);
361 if(i >= fs->ncg)
362 return Nfs3ErrBadHandle;
363 cg = &fs->cg[i];
364 /*
365 if(ioff >= cg->nino)
366 return Nfs3ErrBadHandle;
367 */
369 if(debug) print("cg->ibno %d...", cg->ibno);
370 if((b = diskread(fs->disk, fs->blocksize,
371 (cg->ibno+ioff/fs->inosperblock)*(vlong)fs->blocksize)) == nil)
372 return Nfs3ErrIo;
373 *ino = ((Inode*)b->data)[ioff%fs->inosperblock];
374 blockput(b);
376 return Nfs3Ok;
379 static Nfs3Status
380 ffsroot(Fsys *fsys, Nfs3Handle *h)
382 USED(fsys);
383 mkhandle(h, 2);
384 return Nfs3Ok;
387 static Nfs3Status
388 ino2attr(Ffs *fs, Inode *ino, u32int inum, Nfs3Attr *attr)
390 u32int rdev;
392 attr->type = -1;
393 switch(ino->mode&IFMT){
394 case IFIFO:
395 attr->type = Nfs3FileFifo;
396 break;
397 case IFCHR:
398 attr->type = Nfs3FileChar;
399 break;
400 case IFDIR:
401 attr->type = Nfs3FileDir;
402 break;
403 case IFBLK:
404 attr->type = Nfs3FileBlock;
405 break;
406 case IFREG:
407 attr->type = Nfs3FileReg;
408 break;
409 case IFLNK:
410 attr->type = Nfs3FileSymlink;
411 break;
412 case IFSOCK:
413 attr->type = Nfs3FileSocket;
414 break;
415 case IFWHT:
416 default:
417 return Nfs3ErrBadHandle;
420 attr->mode = ino->mode&07777;
421 attr->nlink = ino->nlink;
422 attr->uid = ino->uid;
423 attr->gid = ino->gid;
424 attr->size = ino->size;
425 attr->used = ino->nblock*fs->blocksize;
426 if(attr->type==Nfs3FileBlock || attr->type==Nfs3FileChar){
427 rdev = ino->db[0];
428 attr->major = (rdev>>8)&0xFF;
429 attr->minor = rdev & 0xFFFF00FF;
430 }else{
431 attr->major = 0;
432 attr->minor = 0;
434 attr->fsid = 0;
435 attr->fileid = inum;
436 attr->atime.sec = ino->atime;
437 attr->atime.nsec = ino->atimensec;
438 attr->mtime.sec = ino->mtime;
439 attr->mtime.nsec = ino->mtimensec;
440 attr->ctime.sec = ino->ctime;
441 attr->ctime.nsec = ino->ctimensec;
442 return Nfs3Ok;
445 static int
446 ingroup(SunAuthUnix *au, uint gid)
448 int i;
450 for(i=0; i<au->ng; i++)
451 if(au->g[i] == gid)
452 return 1;
453 return 0;
456 static Nfs3Status
457 inoperm(Inode *ino, SunAuthUnix *au, int need)
459 int have;
461 have = ino->mode&0777;
462 if(ino->uid == au->uid)
463 have >>= 6;
464 else if(ino->gid == au->gid || ingroup(au, ino->gid))
465 have >>= 3;
467 if((have&need) != need)
468 return Nfs3ErrNotOwner; /* really EPERM */
469 return Nfs3Ok;
472 static Nfs3Status
473 ffsgetattr(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, Nfs3Attr *attr)
475 Inode ino;
476 u32int inum;
477 Ffs *fs;
478 Nfs3Status ok;
480 fs = fsys->priv;
481 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
482 return ok;
484 USED(au); /* anyone can getattr */
486 return ino2attr(fs, &ino, inum, attr);
489 static Nfs3Status
490 ffsaccess(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int want, u32int *got, Nfs3Attr *attr)
492 int have;
493 Inode ino;
494 u32int inum;
495 Ffs *fs;
496 Nfs3Status ok;
498 fs = fsys->priv;
499 if((ok = handle2ino(fs, h, &inum, &ino)) != Nfs3Ok)
500 return ok;
502 have = ino.mode&0777;
503 if(ino.uid == au->uid)
504 have >>= 6;
505 else if(ino.gid == au->gid || ingroup(au, ino.gid))
506 have >>= 3;
508 *got = 0;
509 if((want&Nfs3AccessRead) && (have&AREAD))
510 *got |= Nfs3AccessRead;
511 if((want&Nfs3AccessLookup) && (ino.mode&IFMT)==IFDIR && (have&AEXEC))
512 *got |= Nfs3AccessLookup;
513 if((want&Nfs3AccessExecute) && (ino.mode&IFMT)!=IFDIR && (have&AEXEC))
514 *got |= Nfs3AccessExecute;
516 return ino2attr(fs, &ino, inum, attr);
519 static Nfs3Status
520 ffslookup(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char *name, Nfs3Handle *nh)
522 u32int nblock;
523 u32int i;
524 uchar *p, *ep;
525 Dirent *de;
526 Inode ino;
527 Block *b;
528 Ffs *fs;
529 Nfs3Status ok;
530 int len, want;
532 fs = fsys->priv;
533 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
534 return ok;
536 if((ino.mode&IFMT) != IFDIR)
537 return Nfs3ErrNotDir;
539 if((ok = inoperm(&ino, au, AEXEC)) != Nfs3Ok)
540 return ok;
542 len = strlen(name);
543 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
544 for(i=0; i<nblock; i++){
545 if(i==nblock-1)
546 want = ino.size % fs->blocksize;
547 else
548 want = fs->blocksize;
549 b = ffsfileblock(fs, &ino, i, want);
550 if(b == nil)
551 continue;
552 p = b->data;
553 ep = p+b->len;
554 while(p < ep){
555 de = (Dirent*)p;
556 if(de->reclen == 0){
557 if(debug)
558 fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
559 break;
561 p += de->reclen;
562 if(p > ep){
563 if(debug)
564 fprint(2, "bad len %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
565 break;
567 if(de->ino == 0)
568 continue;
569 if(4+2+2+de->namlen > de->reclen){
570 if(debug)
571 fprint(2, "bad namelen %d at offset %d of %d\n", de->namlen, (int)(p-b->data), b->len);
572 break;
574 if(de->namlen == len && memcmp(de->name, name, len) == 0){
575 mkhandle(nh, de->ino);
576 blockput(b);
577 return Nfs3Ok;
580 blockput(b);
582 return Nfs3ErrNoEnt;
585 static Nfs3Status
586 ffsreaddir(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count, u64int cookie, uchar **pdata, u32int *pcount, u1int *peof)
588 u32int nblock;
589 u32int i;
590 int off, done;
591 uchar *data, *dp, *dep, *p, *ep, *ndp;
592 Dirent *de;
593 Inode ino;
594 Block *b;
595 Ffs *fs;
596 Nfs3Status ok;
597 Nfs3Entry e;
598 int want;
600 fs = fsys->priv;
601 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
602 return ok;
604 if((ino.mode&IFMT) != IFDIR)
605 return Nfs3ErrNotDir;
607 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
608 return ok;
610 if(cookie >= ino.size){
611 *pcount = 0;
612 *pdata = 0;
613 return Nfs3Ok;
616 dp = malloc(count);
617 data = dp;
618 if(dp == nil)
619 return Nfs3ErrNoMem;
620 dep = dp+count;
621 *peof = 0;
622 nblock = (ino.size+fs->blocksize-1) / fs->blocksize;
623 i = cookie/fs->blocksize;
624 off = cookie%fs->blocksize;
625 done = 0;
626 for(; i<nblock && !done; i++){
627 if(i==nblock-1)
628 want = ino.size % fs->blocksize;
629 else
630 want = fs->blocksize;
631 b = ffsfileblock(fs, &ino, i, want);
632 if(b == nil)
633 continue;
634 p = b->data;
635 ep = p+b->len;
636 memset(&e, 0, sizeof e);
637 while(p < ep){
638 de = (Dirent*)p;
639 if(de->reclen == 0){
640 if(debug) fprint(2, "reclen 0 at offset %d of %d\n", (int)(p-b->data), b->len);
641 break;
643 p += de->reclen;
644 if(p > ep){
645 if(debug) fprint(2, "reclen %d at offset %d of %d\n", de->reclen, (int)(p-b->data), b->len);
646 break;
648 if(de->ino == 0){
649 if(debug) fprint(2, "zero inode\n");
650 continue;
652 if(4+2+2+de->namlen > de->reclen){
653 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);
654 break;
656 if(de->name[de->namlen] != 0){
657 if(debug) fprint(2, "bad name %d %.*s\n", de->namlen, de->namlen, de->name);
658 continue;
660 if(debug) print("%s/%d ", de->name, (int)de->ino);
661 if((uchar*)de - b->data < off)
662 continue;
663 e.fileid = de->ino;
664 e.name = de->name;
665 e.cookie = (u64int)i*fs->blocksize + (p - b->data);
666 if(nfs3entrypack(dp, dep, &ndp, &e) < 0){
667 done = 1;
668 break;
670 dp = ndp;
672 off = 0;
673 blockput(b);
675 if(i==nblock)
676 *peof = 1;
678 *pcount = dp - data;
679 *pdata = data;
680 return Nfs3Ok;
683 static Nfs3Status
684 ffsreadfile(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, u32int count,
685 u64int offset, uchar **pdata, u32int *pcount, u1int *peof)
687 uchar *data;
688 Block *b;
689 Ffs *fs;
690 int off, want, fragcount;
691 Inode ino;
692 Nfs3Status ok;
694 fs = fsys->priv;
695 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
696 return ok;
698 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
699 return ok;
701 if(offset >= ino.size){
702 *pdata = 0;
703 *pcount = 0;
704 *peof = 1;
705 return Nfs3Ok;
707 if(offset+count > ino.size)
708 count = ino.size-offset;
709 if(offset/fs->blocksize != (offset+count-1)/fs->blocksize)
710 count = fs->blocksize - offset%fs->blocksize;
712 data = malloc(count);
713 if(data == nil)
714 return Nfs3ErrNoMem;
716 want = offset%fs->blocksize+count;
717 if(want%fs->fragsize)
718 want += fs->fragsize - want%fs->fragsize;
720 b = ffsfileblock(fs, &ino, offset/fs->blocksize, want);
721 if(b == nil){
722 /* BUG: distinguish sparse file from I/O error */
723 memset(data, 0, count);
724 }else{
725 off = offset%fs->blocksize;
726 fragcount = count; /* need signed variable */
727 if(off+fragcount > b->len){
728 fragcount = b->len - off;
729 if(fragcount < 0)
730 fragcount = 0;
732 if(fragcount > 0)
733 memmove(data, b->data+off, fragcount);
734 count = fragcount;
735 blockput(b);
737 *peof = (offset+count == ino.size);
738 *pcount = count;
739 *pdata = data;
740 return Nfs3Ok;
743 static Nfs3Status
744 ffsreadlink(Fsys *fsys, SunAuthUnix *au, Nfs3Handle *h, char **link)
746 Ffs *fs;
747 Nfs3Status ok;
748 int len;
749 Inode ino;
750 Block *b;
752 fs = fsys->priv;
753 if((ok = handle2ino(fs, h, nil, &ino)) != Nfs3Ok)
754 return ok;
755 if((ok = inoperm(&ino, au, AREAD)) != Nfs3Ok)
756 return ok;
758 if(ino.size > 1024)
759 return Nfs3ErrIo;
760 len = ino.size;
762 if(ino.nblock != 0){
763 /* BUG: assumes symlink fits in one block */
764 b = ffsfileblock(fs, &ino, 0, len);
765 if(b == nil)
766 return Nfs3ErrIo;
767 if(memchr(b->data, 0, len) != nil){
768 blockput(b);
769 return Nfs3ErrIo;
771 *link = malloc(len+1);
772 if(*link == 0){
773 blockput(b);
774 return Nfs3ErrNoMem;
776 memmove(*link, b->data, len);
777 (*link)[len] = 0;
778 blockput(b);
779 return Nfs3Ok;
782 if(len > sizeof ino.db + sizeof ino.ib)
783 return Nfs3ErrIo;
785 *link = malloc(len+1);
786 if(*link == 0)
787 return Nfs3ErrNoMem;
788 memmove(*link, ino.db, ino.size);
789 (*link)[len] = 0;
790 return Nfs3Ok;