1 /* $NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $ */
2
3 /*
4 * Copyright (c) 1993 John Brezak
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32
33 #include <sys/param.h>
34 #include <sys/time.h>
35 #include <sys/socket.h>
36 #include <sys/stat.h>
37 #include <string.h>
38 #include <stddef.h>
39
40 #include <netinet/in.h>
41 #include <netinet/in_systm.h>
42
43 #include "rpcv2.h"
44 #include "nfsv2.h"
45
46 #include "stand.h"
47 #include "net.h"
48 #include "netif.h"
49 #include "rpc.h"
50
51 #define NFS_DEBUGxx
52
53 #define NFSREAD_MIN_SIZE 1024
54 #define NFSREAD_MAX_SIZE 16384
55
56 /* NFSv3 definitions */
57 #define NFS_V3MAXFHSIZE 64
58 #define NFS_VER3 3
59 #define RPCMNT_VER3 3
60 #define NFSPROCV3_LOOKUP 3
61 #define NFSPROCV3_READLINK 5
62 #define NFSPROCV3_READ 6
63 #define NFSPROCV3_READDIR 16
64
65 typedef struct {
66 uint32_t val[2];
67 } n_quad;
68
69 struct nfsv3_time {
70 uint32_t nfs_sec;
71 uint32_t nfs_nsec;
72 };
73
74 struct nfsv3_fattrs {
75 uint32_t fa_type;
76 uint32_t fa_mode;
77 uint32_t fa_nlink;
78 uint32_t fa_uid;
79 uint32_t fa_gid;
80 n_quad fa_size;
81 n_quad fa_used;
82 n_quad fa_rdev;
83 n_quad fa_fsid;
84 n_quad fa_fileid;
85 struct nfsv3_time fa_atime;
86 struct nfsv3_time fa_mtime;
87 struct nfsv3_time fa_ctime;
88 };
89
90 /*
91 * For NFSv3, the file handle is variable in size, so most fixed sized
92 * structures for arguments won't work. For most cases, a structure
93 * that starts with any fixed size section is followed by an array
94 * that covers the maximum size required.
95 */
96 struct nfsv3_readdir_repl {
97 uint32_t errno;
98 uint32_t ok;
99 struct nfsv3_fattrs fa;
100 uint32_t cookiev0;
101 uint32_t cookiev1;
102 };
103
104 struct nfsv3_readdir_entry {
105 uint32_t follows;
106 uint32_t fid0;
107 uint32_t fid1;
108 uint32_t len;
109 uint32_t nameplus[0];
110 };
111
112 struct nfs_iodesc {
113 struct iodesc *iodesc;
114 off_t off;
115 uint32_t fhsize;
116 uchar_t fh[NFS_V3MAXFHSIZE];
117 struct nfsv3_fattrs fa; /* all in network order */
118 uint64_t cookie;
119 };
120
121 /*
122 * XXX interactions with tftp? See nfswrapper.c for a confusing
123 * issue.
124 */
125 int nfs_open(const char *path, struct open_file *f);
126 static int nfs_close(struct open_file *f);
127 static int nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
128 static off_t nfs_seek(struct open_file *f, off_t offset, int where);
129 static int nfs_stat(struct open_file *f, struct stat *sb);
130 static int nfs_readdir(struct open_file *f, struct dirent *d);
131
132 struct nfs_iodesc nfs_root_node;
133
134 struct fs_ops nfs_fsops = {
135 .fs_name = "nfs",
136 .fo_open = nfs_open,
137 .fo_close = nfs_close,
138 .fo_read = nfs_read,
139 .fo_write = null_write,
140 .fo_seek = nfs_seek,
141 .fo_stat = nfs_stat,
142 .fo_readdir = nfs_readdir
143 };
144
145 static int nfs_read_size = NFSREAD_MIN_SIZE;
146
147 /*
148 * Improve boot performance over NFS
149 */
150 static void
set_nfs_read_size(void)151 set_nfs_read_size(void)
152 {
153 char *env, *end;
154 char buf[10];
155
156 if ((env = getenv("nfs.read_size")) != NULL) {
157 errno = 0;
158 nfs_read_size = strtol(env, &end, 0);
159 if (errno != 0 || *env == '\0' || *end != '\0') {
160 printf("%s: bad value: \"%s\", defaulting to %d\n",
161 "nfs.read_size", env, NFSREAD_MIN_SIZE);
162 nfs_read_size = NFSREAD_MIN_SIZE;
163 }
164 }
165 if (nfs_read_size < NFSREAD_MIN_SIZE) {
166 printf("%s: bad value: \"%d\", defaulting to %d\n",
167 "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
168 nfs_read_size = NFSREAD_MIN_SIZE;
169 }
170 if (nfs_read_size > NFSREAD_MAX_SIZE) {
171 printf("%s: bad value: \"%d\", defaulting to %d\n",
172 "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
173 nfs_read_size = NFSREAD_MAX_SIZE;
174 }
175 snprintf(buf, sizeof (buf), "%d", nfs_read_size);
176 setenv("nfs.read_size", buf, 1);
177 }
178
179 /*
180 * Fetch the root file handle (call mount daemon)
181 * Return zero or error number.
182 */
183 int
nfs_getrootfh(struct iodesc * d,char * path,uint32_t * fhlenp,uchar_t * fhp)184 nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, uchar_t *fhp)
185 {
186 void *pkt = NULL;
187 int len;
188 struct args {
189 uint32_t len;
190 char path[FNAME_SIZE];
191 } *args;
192 struct repl {
193 uint32_t errno;
194 uint32_t fhsize;
195 uchar_t fh[NFS_V3MAXFHSIZE];
196 uint32_t authcnt;
197 uint32_t auth[7];
198 } *repl;
199 struct {
200 uint32_t h[RPC_HEADER_WORDS];
201 struct args d;
202 } sdata;
203 size_t cc;
204
205 #ifdef NFS_DEBUG
206 if (debug)
207 printf("nfs_getrootfh: %s\n", path);
208 #endif
209
210 args = &sdata.d;
211
212 bzero(args, sizeof (*args));
213 len = strlen(path);
214 if (len > sizeof (args->path))
215 len = sizeof (args->path);
216 args->len = htonl(len);
217 bcopy(path, args->path, len);
218 len = sizeof (uint32_t) + roundup(len, sizeof (uint32_t));
219
220 cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
221 args, len, (void **)&repl, &pkt);
222 if (cc == -1) {
223 free(pkt);
224 /* errno was set by rpc_call */
225 return (errno);
226 }
227 if (cc < 2 * sizeof (uint32_t)) {
228 free(pkt);
229 return (EBADRPC);
230 }
231 if (repl->errno != 0) {
232 free(pkt);
233 return (ntohl(repl->errno));
234 }
235 *fhlenp = ntohl(repl->fhsize);
236 bcopy(repl->fh, fhp, *fhlenp);
237
238 set_nfs_read_size();
239 free(pkt);
240 return (0);
241 }
242
243 /*
244 * Lookup a file. Store handle and attributes.
245 * Return zero or error number.
246 */
247 int
nfs_lookupfh(struct nfs_iodesc * d,const char * name,struct nfs_iodesc * newfd)248 nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
249 {
250 void *pkt = NULL;
251 int len, pos;
252 struct args {
253 uint32_t fhsize;
254 uint32_t fhplusname[1 +
255 (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof (uint32_t)];
256 } *args;
257 struct repl {
258 uint32_t errno;
259 uint32_t fhsize;
260 uint32_t fhplusattr[(NFS_V3MAXFHSIZE +
261 2 * (sizeof (uint32_t) +
262 sizeof (struct nfsv3_fattrs))) / sizeof (uint32_t)];
263 } *repl;
264 struct {
265 uint32_t h[RPC_HEADER_WORDS];
266 struct args d;
267 } sdata;
268 ssize_t cc;
269
270 #ifdef NFS_DEBUG
271 if (debug)
272 printf("lookupfh: called\n");
273 #endif
274
275 args = &sdata.d;
276
277 bzero(args, sizeof (*args));
278 args->fhsize = htonl(d->fhsize);
279 bcopy(d->fh, args->fhplusname, d->fhsize);
280 len = strlen(name);
281 if (len > FNAME_SIZE)
282 len = FNAME_SIZE;
283 pos = roundup(d->fhsize, sizeof (uint32_t)) / sizeof (uint32_t);
284 args->fhplusname[pos++] = htonl(len);
285 bcopy(name, &args->fhplusname[pos], len);
286 len = sizeof (uint32_t) + pos * sizeof (uint32_t) +
287 roundup(len, sizeof (uint32_t));
288
289 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP,
290 args, len, (void **)&repl, &pkt);
291 if (cc == -1) {
292 free(pkt);
293 return (errno); /* XXX - from rpc_call */
294 }
295 if (cc < 2 * sizeof (uint32_t)) {
296 free(pkt);
297 return (EIO);
298 }
299 if (repl->errno != 0) {
300 free(pkt);
301 /* saerrno.h now matches NFS error numbers. */
302 return (ntohl(repl->errno));
303 }
304 newfd->fhsize = ntohl(repl->fhsize);
305 bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize);
306 pos = roundup(newfd->fhsize, sizeof (uint32_t)) / sizeof (uint32_t);
307 if (repl->fhplusattr[pos++] == 0) {
308 free(pkt);
309 return (EIO);
310 }
311 bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof (newfd->fa));
312 free(pkt);
313 return (0);
314 }
315
316 /*
317 * Get the destination of a symbolic link.
318 */
319 int
nfs_readlink(struct nfs_iodesc * d,char * buf)320 nfs_readlink(struct nfs_iodesc *d, char *buf)
321 {
322 void *pkt = NULL;
323 struct args {
324 uint32_t fhsize;
325 uchar_t fh[NFS_V3MAXFHSIZE];
326 } *args;
327 struct repl {
328 uint32_t errno;
329 uint32_t ok;
330 struct nfsv3_fattrs fa;
331 uint32_t len;
332 uchar_t path[NFS_MAXPATHLEN];
333 } *repl;
334 struct {
335 uint32_t h[RPC_HEADER_WORDS];
336 struct args d;
337 } sdata;
338 ssize_t cc;
339 int rc = 0;
340
341 #ifdef NFS_DEBUG
342 if (debug)
343 printf("readlink: called\n");
344 #endif
345
346 args = &sdata.d;
347
348 bzero(args, sizeof (*args));
349 args->fhsize = htonl(d->fhsize);
350 bcopy(d->fh, args->fh, d->fhsize);
351 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK,
352 args, sizeof (uint32_t) + roundup(d->fhsize, sizeof (uint32_t)),
353 (void **)&repl, &pkt);
354 if (cc == -1)
355 return (errno);
356
357 if (cc < 2 * sizeof (uint32_t)) {
358 rc = EIO;
359 goto done;
360 }
361
362 if (repl->errno != 0) {
363 rc = ntohl(repl->errno);
364 goto done;
365 }
366
367 if (repl->ok == 0) {
368 rc = EIO;
369 goto done;
370 }
371
372 repl->len = ntohl(repl->len);
373 if (repl->len > NFS_MAXPATHLEN) {
374 rc = ENAMETOOLONG;
375 goto done;
376 }
377
378 bcopy(repl->path, buf, repl->len);
379 buf[repl->len] = 0;
380 done:
381 free(pkt);
382 return (rc);
383 }
384
385 /*
386 * Read data from a file.
387 * Return transfer count or -1 (and set errno)
388 */
389 ssize_t
nfs_readdata(struct nfs_iodesc * d,off_t off,void * addr,size_t len)390 nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
391 {
392 void *pkt = NULL;
393 struct args {
394 uint32_t fhsize;
395 uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof (uint32_t) + 3];
396 } *args;
397 struct repl {
398 uint32_t errno;
399 uint32_t ok;
400 struct nfsv3_fattrs fa;
401 uint32_t count;
402 uint32_t eof;
403 uint32_t len;
404 uchar_t data[NFSREAD_MAX_SIZE];
405 } *repl;
406 struct {
407 uint32_t h[RPC_HEADER_WORDS];
408 struct args d;
409 } sdata;
410 size_t cc;
411 long x;
412 int hlen, rlen, pos;
413
414 args = &sdata.d;
415
416 bzero(args, sizeof (*args));
417 args->fhsize = htonl(d->fhsize);
418 bcopy(d->fh, args->fhoffcnt, d->fhsize);
419 pos = roundup(d->fhsize, sizeof (uint32_t)) / sizeof (uint32_t);
420 args->fhoffcnt[pos++] = 0;
421 args->fhoffcnt[pos++] = htonl((uint32_t)off);
422 if (len > nfs_read_size)
423 len = nfs_read_size;
424 args->fhoffcnt[pos] = htonl((uint32_t)len);
425 hlen = offsetof(struct repl, data[0]);
426
427 cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ,
428 args, 4 * sizeof (uint32_t) + roundup(d->fhsize, sizeof (uint32_t)),
429 (void **)&repl, &pkt);
430 if (cc == -1) {
431 /* errno was already set by rpc_call */
432 return (-1);
433 }
434 if (cc < hlen) {
435 errno = EBADRPC;
436 free(pkt);
437 return (-1);
438 }
439 if (repl->errno != 0) {
440 errno = ntohl(repl->errno);
441 free(pkt);
442 return (-1);
443 }
444 rlen = cc - hlen;
445 x = ntohl(repl->count);
446 if (rlen < x) {
447 printf("nfsread: short packet, %d < %ld\n", rlen, x);
448 errno = EBADRPC;
449 free(pkt);
450 return (-1);
451 }
452 bcopy(repl->data, addr, x);
453 free(pkt);
454 return (x);
455 }
456
457 /*
458 * Open a file.
459 * return zero or error number
460 */
461 int
nfs_open(const char * upath,struct open_file * f)462 nfs_open(const char *upath, struct open_file *f)
463 {
464 struct devdesc *dev;
465 struct iodesc *desc;
466 struct nfs_iodesc *currfd = NULL;
467 char buf[2 * NFS_V3MAXFHSIZE + 3];
468 uchar_t *fh;
469 char *cp;
470 int i;
471 struct nfs_iodesc *newfd = NULL;
472 char *ncp;
473 int c;
474 char namebuf[NFS_MAXPATHLEN + 1];
475 char linkbuf[NFS_MAXPATHLEN + 1];
476 int nlinks = 0;
477 int error;
478 char *path = NULL;
479
480 if (netproto != NET_NFS)
481 return (EINVAL);
482
483 dev = f->f_devdata;
484 #ifdef NFS_DEBUG
485 if (debug)
486 printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
487 #endif
488 if (!rootpath[0]) {
489 printf("no rootpath, no nfs\n");
490 return (ENXIO);
491 }
492
493 if (f->f_dev->dv_type != DEVT_NET)
494 return (EINVAL);
495
496 if (!(desc = socktodesc(*(int *)(dev->d_opendata))))
497 return (EINVAL);
498
499 /* Bind to a reserved port. */
500 desc->myport = htons(--rpc_port);
501 desc->destip = rootip;
502 if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize,
503 nfs_root_node.fh)))
504 return (error);
505 nfs_root_node.fa.fa_type = htonl(NFDIR);
506 nfs_root_node.fa.fa_mode = htonl(0755);
507 nfs_root_node.fa.fa_nlink = htonl(2);
508 nfs_root_node.iodesc = desc;
509
510 fh = &nfs_root_node.fh[0];
511 buf[0] = 'X';
512 cp = &buf[1];
513 for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2)
514 sprintf(cp, "%02x", fh[i]);
515 sprintf(cp, "X");
516 setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
517 setenv("boot.nfsroot.path", rootpath, 1);
518 setenv("boot.nfsroot.nfshandle", buf, 1);
519 sprintf(buf, "%d", nfs_root_node.fhsize);
520 setenv("boot.nfsroot.nfshandlelen", buf, 1);
521
522 /* Allocate file system specific data structure */
523 currfd = malloc(sizeof (*newfd));
524 if (currfd == NULL) {
525 error = ENOMEM;
526 goto out;
527 }
528 bcopy(&nfs_root_node, currfd, sizeof (*currfd));
529 newfd = NULL;
530
531 cp = path = strdup(upath);
532 if (path == NULL) {
533 error = ENOMEM;
534 goto out;
535 }
536 while (*cp) {
537 /*
538 * Remove extra separators
539 */
540 while (*cp == '/')
541 cp++;
542
543 if (*cp == '\0')
544 break;
545 /*
546 * Check that current node is a directory.
547 */
548 if (currfd->fa.fa_type != htonl(NFDIR)) {
549 error = ENOTDIR;
550 goto out;
551 }
552
553 /* allocate file system specific data structure */
554 newfd = malloc(sizeof (*newfd));
555 if (newfd == NULL) {
556 error = ENOMEM;
557 goto out;
558 }
559 newfd->iodesc = currfd->iodesc;
560
561 /*
562 * Get next component of path name.
563 */
564 {
565 int len = 0;
566
567 ncp = cp;
568 while ((c = *cp) != '\0' && c != '/') {
569 if (++len > NFS_MAXNAMLEN) {
570 error = ENOENT;
571 goto out;
572 }
573 cp++;
574 }
575 *cp = '\0';
576 }
577
578 /* lookup a file handle */
579 error = nfs_lookupfh(currfd, ncp, newfd);
580 *cp = c;
581 if (error)
582 goto out;
583
584 /*
585 * Check for symbolic link
586 */
587 if (newfd->fa.fa_type == htonl(NFLNK)) {
588 int link_len, len;
589
590 error = nfs_readlink(newfd, linkbuf);
591 if (error)
592 goto out;
593
594 link_len = strlen(linkbuf);
595 len = strlen(cp);
596
597 if (link_len + len > MAXPATHLEN ||
598 ++nlinks > MAXSYMLINKS) {
599 error = ENOENT;
600 goto out;
601 }
602
603 bcopy(cp, &namebuf[link_len], len + 1);
604 bcopy(linkbuf, namebuf, link_len);
605
606 /*
607 * If absolute pathname, restart at root.
608 * If relative pathname, restart at parent directory.
609 */
610 cp = namebuf;
611 if (*cp == '/')
612 bcopy(&nfs_root_node, currfd, sizeof (*currfd));
613
614 free(newfd);
615 newfd = NULL;
616
617 continue;
618 }
619
620 free(currfd);
621 currfd = newfd;
622 newfd = NULL;
623 }
624
625 error = 0;
626
627 out:
628 free(newfd);
629 free(path);
630 if (!error) {
631 currfd->off = 0;
632 currfd->cookie = 0;
633 f->f_fsdata = currfd;
634 return (0);
635 }
636
637 #ifdef NFS_DEBUG
638 if (debug)
639 printf("nfs_open: %s lookupfh failed: %s\n",
640 path, strerror(error));
641 #endif
642 free(currfd);
643
644 return (error);
645 }
646
647 int
nfs_close(struct open_file * f)648 nfs_close(struct open_file *f)
649 {
650 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
651
652 #ifdef NFS_DEBUG
653 if (debug)
654 printf("nfs_close: fp=%#p\n", fp);
655 #endif
656
657 free(fp);
658 f->f_fsdata = NULL;
659
660 return (0);
661 }
662
663 /*
664 * read a portion of a file
665 */
666 int
nfs_read(struct open_file * f,void * buf,size_t size,size_t * resid)667 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
668 {
669 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
670 ssize_t cc;
671 char *addr = buf;
672
673 #ifdef NFS_DEBUG
674 if (debug)
675 printf("nfs_read: size=%zu off=%j\n", size,
676 (intmax_t)fp->off);
677 #endif
678 while (size > 0) {
679 twiddle(16);
680 cc = nfs_readdata(fp, fp->off, addr, size);
681 /* XXX maybe should retry on certain errors */
682 if (cc == -1) {
683 #ifdef NFS_DEBUG
684 if (debug)
685 printf("nfs_read: read: %s", strerror(errno));
686 #endif
687 return (errno); /* XXX - from nfs_readdata */
688 }
689 if (cc == 0) {
690 #ifdef NFS_DEBUG
691 if (debug)
692 printf("nfs_read: hit EOF unexpectantly");
693 #endif
694 goto ret;
695 }
696 fp->off += cc;
697 addr += cc;
698 size -= cc;
699 }
700 ret:
701 if (resid)
702 *resid = size;
703
704 return (0);
705 }
706
707 off_t
nfs_seek(struct open_file * f,off_t offset,int where)708 nfs_seek(struct open_file *f, off_t offset, int where)
709 {
710 struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
711 uint32_t size = ntohl(d->fa.fa_size.val[1]);
712
713 switch (where) {
714 case SEEK_SET:
715 d->off = offset;
716 break;
717 case SEEK_CUR:
718 d->off += offset;
719 break;
720 case SEEK_END:
721 d->off = size - offset;
722 break;
723 default:
724 errno = EINVAL;
725 return (-1);
726 }
727
728 return (d->off);
729 }
730
731 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */
732 int nfs_stat_types[9] = {
733 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 };
734
735 int
nfs_stat(struct open_file * f,struct stat * sb)736 nfs_stat(struct open_file *f, struct stat *sb)
737 {
738 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
739 uint32_t ftype, mode;
740
741 ftype = ntohl(fp->fa.fa_type);
742 mode = ntohl(fp->fa.fa_mode);
743 mode |= nfs_stat_types[ftype & 7];
744
745 sb->st_mode = mode;
746 sb->st_nlink = ntohl(fp->fa.fa_nlink);
747 sb->st_uid = ntohl(fp->fa.fa_uid);
748 sb->st_gid = ntohl(fp->fa.fa_gid);
749 sb->st_size = ntohl(fp->fa.fa_size.val[1]);
750
751 return (0);
752 }
753
754 static int
nfs_readdir(struct open_file * f,struct dirent * d)755 nfs_readdir(struct open_file *f, struct dirent *d)
756 {
757 struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
758 struct nfsv3_readdir_repl *repl;
759 struct nfsv3_readdir_entry *rent;
760 static void *pkt = NULL;
761 static char *buf;
762 static struct nfs_iodesc *pfp = NULL;
763 static uint64_t cookie = 0;
764 size_t cc;
765 int pos, rc;
766
767 struct args {
768 uint32_t fhsize;
769 uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE];
770 } *args;
771 struct {
772 uint32_t h[RPC_HEADER_WORDS];
773 struct args d;
774 } sdata;
775
776 if (fp != pfp || fp->off != cookie) {
777 pfp = NULL;
778 refill:
779 free(pkt);
780 pkt = NULL;
781 args = &sdata.d;
782 bzero(args, sizeof (*args));
783
784 args->fhsize = htonl(fp->fhsize);
785 bcopy(fp->fh, args->fhpluscookie, fp->fhsize);
786 pos = roundup(fp->fhsize,
787 sizeof (uint32_t)) / sizeof (uint32_t);
788 args->fhpluscookie[pos++] = htonl(fp->off >> 32);
789 args->fhpluscookie[pos++] = htonl(fp->off);
790 args->fhpluscookie[pos++] = htonl(fp->cookie >> 32);
791 args->fhpluscookie[pos++] = htonl(fp->cookie);
792 args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE);
793
794 cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR,
795 args, 6 * sizeof (uint32_t) +
796 roundup(fp->fhsize, sizeof (uint32_t)),
797 (void **)&buf, &pkt);
798 if (cc == -1) {
799 rc = errno;
800 goto err;
801 }
802 repl = (struct nfsv3_readdir_repl *)buf;
803 if (repl->errno != 0) {
804 rc = ntohl(repl->errno);
805 goto err;
806 }
807 pfp = fp;
808 cookie = fp->off;
809 fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) |
810 ntohl(repl->cookiev1);
811 buf += sizeof (struct nfsv3_readdir_repl);
812 }
813 rent = (struct nfsv3_readdir_entry *)buf;
814
815 if (rent->follows == 0) {
816 /* fid0 is actually eof */
817 if (rent->fid0 != 0) {
818 rc = ENOENT;
819 goto err;
820 }
821 goto refill;
822 }
823
824 d->d_namlen = ntohl(rent->len);
825 bcopy(rent->nameplus, d->d_name, d->d_namlen);
826 d->d_name[d->d_namlen] = '\0';
827
828 pos = roundup(d->d_namlen, sizeof (uint32_t)) / sizeof (uint32_t);
829 fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) |
830 ntohl(rent->nameplus[pos + 1]);
831 pos += 2;
832 buf = (char *)&rent->nameplus[pos];
833 return (0);
834
835 err:
836 free(pkt);
837 pkt = NULL;
838 pfp = NULL;
839 cookie = 0;
840 return (rc);
841 }
842