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