1 /*        $NetBSD: nfs.c,v 1.53 2024/06/29 07:49:36 rin 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 /*
32  * XXX Does not currently implement:
33  * XXX
34  * XXX LIBSA_NO_FS_CLOSE
35  * XXX LIBSA_NO_FS_SEEK
36  * XXX LIBSA_NO_FS_WRITE
37  * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?)
38  * XXX LIBSA_FS_SINGLECOMPONENT (does this even make sense?)
39  */
40 
41 #include <sys/param.h>
42 #include <sys/time.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #ifdef _STANDALONE
46 #include <lib/libkern/libkern.h>
47 #else
48 #include <string.h>
49 #endif
50 
51 #include <netinet/in.h>
52 #include <netinet/in_systm.h>
53 
54 #include "rpcv2.h"
55 #include "nfsv2.h"
56 #include "nfsv3.h"
57 
58 #include "stand.h"
59 #include "net.h"
60 #include "nfs.h"
61 #include "rpc.h"
62 
63 /* Storage for any filehandle (including length for V3) */
64 #define NFS_FHSTORE (NFS_FHSIZE < NFS_V3FHSIZE ? NFS_V3FHSIZE + 4: NFS_FHSIZE)
65 
66 /* Data part of nfs rpc reply (also the largest thing we receive) */
67 #define NFSREAD_SIZE 1024
68 
69 #ifndef NFS_NOSYMLINK
70 struct nfs_readlnk_repl {
71           n_long    errno;
72           n_long    len;
73           char      path[NFS_MAXPATHLEN];
74 };
75 #endif
76 
77 static inline uint64_t
getnquad(n_long x[2])78 getnquad(n_long x[2]) {
79           return (uint64_t)ntohl(x[0]) << 32 | ntohl(x[1]);
80 }
81 
82 static inline void
setnquad(n_long x[2],uint64_t v)83 setnquad(n_long x[2], uint64_t v)
84 {
85           x[0] = htonl((n_long)(v >> 32));
86           x[1] = htonl((n_long)(v & 0xffffffff));
87 }
88 
89 struct nfs_iodesc {
90           struct    iodesc    *iodesc;
91           off_t     off;
92           int       version;
93           u_char    fh[NFS_FHSTORE];
94           union {
95                     /* all in network order */
96                     struct nfsv2_fattr v2;
97                     struct nfsv3_fattr v3;
98           } u_fa;
99 };
100 
101 static inline size_t
fhstore(int version,u_char * fh)102 fhstore(int version, u_char *fh)
103 {
104           size_t len;
105 
106           switch (version) {
107           case NFS_VER2:
108                     len = NFS_FHSIZE;
109                     break;
110           case NFS_VER3:
111                     len = fh[0] << 24 | fh[1] << 16 | fh[2] << 8 | fh[3];
112                     if (len > NFS_V3FHSIZE)
113                               len = NFS_V3FHSIZE;
114                     len = 4 + roundup(len, 4);
115                     break;
116           default:
117                     len = 0;
118                     break;
119           }
120 
121           return len;
122 }
123 
124 static inline size_t
fhcopy(int version,u_char * src,u_char * dst)125 fhcopy(int version, u_char *src, u_char *dst)
126 {
127           size_t len = fhstore(version, src);
128           memcpy(dst, src, len);
129           return len;
130 }
131 
132 #define setfh(d, s) fhcopy((d)->version, (s), (d)->fh)
133 #define getfh(d, s) fhcopy((d)->version, (d)->fh, (s))
134 
135 
136 struct nfs_iodesc nfs_root_node;
137 
138 int       nfs_getrootfh(struct iodesc *, char *, u_char *, int *);
139 int       nfs_lookupfh(struct nfs_iodesc *, const char *, int,
140               struct nfs_iodesc *);
141 int       nfs_readlink(struct nfs_iodesc *, char *);
142 ssize_t   nfs_readdata(struct nfs_iodesc *, off_t, void *, size_t);
143 
144 /*
145  * Fetch the root file handle (call mount daemon)
146  * On error, return non-zero and set errno.
147  */
148 int
nfs_getrootfh(struct iodesc * d,char * path,u_char * fhp,int * versionp)149 nfs_getrootfh(struct iodesc *d, char *path, u_char *fhp, int *versionp)
150 {
151           int len;
152           struct args {
153                     n_long    len;
154                     char      path[FNAME_SIZE];
155           } *args;
156           struct repl {
157                     n_long    errno;
158                     u_char    fh[NFS_FHSTORE];
159           } *repl;
160           struct {
161                     n_long    h[RPC_HEADER_WORDS];
162                     struct args d;
163           } sdata;
164           struct {
165                     n_long    h[RPC_HEADER_WORDS];
166                     struct repl d;
167           } rdata;
168           ssize_t cc;
169 
170 #ifdef NFS_DEBUG
171           if (debug)
172                     printf("%s: %s\n", __func__, path);
173 #endif
174 
175           args = &sdata.d;
176           repl = &rdata.d;
177 
178           (void)memset(args, 0, sizeof(*args));
179           len = strlen(path);
180           if ((size_t)len > sizeof(args->path))
181                     len = sizeof(args->path);
182           args->len = htonl(len);
183           (void)memcpy(args->path, path, len);
184           len = 4 + roundup(len, 4);
185 
186           *versionp = NFS_VER3;
187           cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
188               args, len, repl, sizeof(*repl));
189           if (cc == -1 || cc < 4 || repl->errno) {
190                     *versionp = NFS_VER2;
191                     cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT,
192                         args, len, repl, sizeof(*repl));
193           }
194           if (cc == -1) {
195                     /* errno was set by rpc_call */
196                     return -1;
197           }
198           if (cc < 4) {
199                     errno = EBADRPC;
200                     return -1;
201           }
202           if (repl->errno) {
203                     errno = ntohl(repl->errno);
204                     return -1;
205           }
206           fhcopy(*versionp, repl->fh, fhp);
207           return 0;
208 }
209 
210 /*
211  * Lookup a file.  Store handle and attributes.
212  * Return zero or error number.
213  */
214 int
nfs_lookupfh(struct nfs_iodesc * d,const char * name,int len,struct nfs_iodesc * newfd)215 nfs_lookupfh(struct nfs_iodesc *d, const char *name, int len,
216           struct nfs_iodesc *newfd)
217 {
218           struct argsv2 {
219                     u_char    fh[NFS_FHSIZE];
220                     n_long    len;
221                     char      name[FNAME_SIZE];
222           } *argsv2;
223           struct argsv3 {
224                     u_char    fh[NFS_FHSTORE];
225                     n_long    len;
226                     char      name[FNAME_SIZE];
227           } *argsv3;
228           struct replv2 {
229                     n_long    errno;
230                     u_char    fh[NFS_FHSIZE];
231                     struct    nfsv2_fattr fa;
232           } *replv2;
233           struct replv3 {
234                     n_long    errno;
235                     u_char    fh[NFS_FHSTORE];
236                     n_long    fattrflag;
237                     struct    nfsv3_fattr fa;
238                     n_long    dattrflag;
239                     struct    nfsv3_fattr da;
240           } *replv3;
241           struct {
242                     n_long    h[RPC_HEADER_WORDS];
243                     union {
244                               struct argsv2 v2;
245                               struct argsv3 v3;
246                     } u_d;
247           } sdata;
248           struct {
249                     n_long    h[RPC_HEADER_WORDS];
250                     union {
251                               struct replv2 v2;
252                               struct replv3 v3;
253                     } u_d;
254           } rdata;
255           ssize_t cc;
256           size_t alen;
257 
258 #ifdef NFS_DEBUG
259           if (debug)
260                     printf("%s: called\n", __func__);
261 #endif
262 
263           argsv2 = &sdata.u_d.v2;
264           argsv3 = &sdata.u_d.v3;
265           replv2 = &rdata.u_d.v2;
266           replv3 = &rdata.u_d.v3;
267 
268           switch (d->version) {
269           case NFS_VER2:
270                     (void)memset(argsv2, 0, sizeof(*argsv2));
271                     getfh(d, argsv2->fh);
272                     if ((size_t)len > sizeof(argsv2->name))
273                               len = sizeof(argsv2->name);
274                     (void)memcpy(argsv2->name, name, len);
275                     argsv2->len = htonl(len);
276 
277                     /* padded name, name length */
278                     len = roundup(len, 4) + 4;
279                     /* filehandle size */
280                     alen = fhstore(d->version, argsv2->fh);
281 
282                     cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER2, NFSPROC_LOOKUP,
283                         argsv2, alen+len, replv2, sizeof(*replv2));
284                     break;
285           case NFS_VER3:
286                     (void)memset(argsv3, 0, sizeof(*argsv3));
287                     getfh(d, argsv3->fh);
288                     if ((size_t)len > sizeof(argsv3->name))
289                               len = sizeof(argsv3->name);
290                     (void)memcpy(argsv3->name, name, len);
291                     argsv3->len = htonl(len);
292 
293                     /* padded name, name length */
294                     len = roundup(len, 4) + 4;
295                     /* filehandle size */
296                     alen = fhstore(d->version, argsv3->fh);
297 
298                     /* adjust for variable sized file handle */
299                     memmove(argsv3->fh + alen, &argsv3->len, len);
300 
301                     cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSV3PROC_LOOKUP,
302                         argsv3, alen+len, replv3, sizeof(*replv3));
303                     break;
304           default:
305                     return ENOSYS;
306           }
307 
308           if (cc == -1)
309                     return errno;                 /* XXX - from rpc_call */
310           if (cc < 4)
311                     return EIO;
312 
313           switch (d->version) {
314           case NFS_VER2:
315                     if (replv2->errno) {
316                               /* saerrno.h now matches NFS error numbers. */
317                               return ntohl(replv2->errno);
318                     }
319 
320                     setfh(newfd, replv2->fh);
321                     (void)memcpy(&newfd->u_fa.v2, &replv2->fa,
322                         sizeof(newfd->u_fa.v2));
323                     break;
324           case NFS_VER3:
325                     if (replv3->errno) {
326                               /* saerrno.h now matches NFS error numbers. */
327                               return ntohl(replv3->errno);
328                     }
329 
330                     setfh(newfd, replv3->fh);
331 
332                     if (replv3->fattrflag) {
333                               (void)memcpy(&newfd->u_fa.v3, &replv3->fa,
334                                   sizeof(newfd->u_fa.v3));
335                     }
336                     break;
337           }
338           return 0;
339 }
340 
341 #ifndef NFS_NOSYMLINK
342 /*
343  * Get the destination of a symbolic link.
344  */
345 int
nfs_readlink(struct nfs_iodesc * d,char * buf)346 nfs_readlink(struct nfs_iodesc *d, char *buf)
347 {
348           struct {
349                     n_long    h[RPC_HEADER_WORDS];
350                     u_char    fh[NFS_FHSTORE];
351           } sdata;
352           struct {
353                     n_long    h[RPC_HEADER_WORDS];
354                     struct nfs_readlnk_repl d;
355           } rdata;
356           ssize_t cc;
357 
358 #ifdef NFS_DEBUG
359           if (debug)
360                     printf("%s: called\n", __func__);
361 #endif
362 
363           getfh(d, sdata.fh);
364           cc = rpc_call(d->iodesc, NFS_PROG, d->version, NFSPROC_READLINK,
365                           sdata.fh, fhstore(d->version, sdata.fh),
366                           &rdata.d, sizeof(rdata.d));
367           if (cc == -1)
368                     return errno;
369 
370           if (cc < 4)
371                     return EIO;
372 
373           if (rdata.d.errno)
374                     return ntohl(rdata.d.errno);
375 
376           rdata.d.len = ntohl(rdata.d.len);
377           if (rdata.d.len > NFS_MAXPATHLEN)
378                     return ENAMETOOLONG;
379 
380           (void)memcpy(buf, rdata.d.path, rdata.d.len);
381           buf[rdata.d.len] = 0;
382           return 0;
383 }
384 #endif
385 
386 /*
387  * Read data from a file.
388  * Return transfer count or -1 (and set errno)
389  */
390 ssize_t
nfs_readdata(struct nfs_iodesc * d,off_t off,void * addr,size_t len)391 nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
392 {
393           struct argsv2 {
394                     u_char    fh[NFS_FHSIZE];
395                     n_long    off;
396                     n_long    len;
397                     n_long    xxx;                          /* XXX what's this for? */
398           } *argsv2;
399           struct argsv3 {
400                     u_char    fh[NFS_FHSTORE];
401                     n_long    off[2];
402                     n_long    len;
403           } *argsv3;
404           struct replv2 {
405                     n_long    errno;
406                     struct    nfsv2_fattr fa;
407                     n_long    count;
408                     u_char    data[NFSREAD_SIZE];
409           } *replv2;
410           struct replv3 {
411                     n_long    errno;
412                     n_long    attrflag;
413                     struct    nfsv3_fattr fa;
414                     n_long    count;
415                     n_long    eof;
416                     n_long    length;
417                     u_char    data[NFSREAD_SIZE];
418           } *replv3;
419           struct replv3_noattr {
420                     n_long    errno;
421                     n_long    attrflag;
422                     n_long    count;
423                     n_long    eof;
424                     n_long    length;
425                     u_char    data[NFSREAD_SIZE];
426           } *replv3no;
427           struct {
428                     n_long    h[RPC_HEADER_WORDS];
429                     union {
430                               struct argsv2 v2;
431                               struct argsv3 v3;
432                     } u_d;
433           } sdata;
434           struct {
435                     n_long    h[RPC_HEADER_WORDS];
436                     union {
437                               struct replv2 v2;
438                               struct replv3 v3;
439                     } u_d;
440           } rdata;
441           ssize_t cc;
442           long x;
443           size_t hlen, rlen, alen;
444           u_char *data;
445 
446           argsv2 = &sdata.u_d.v2;
447           argsv3 = &sdata.u_d.v3;
448           replv2 = &rdata.u_d.v2;
449           replv3 = &rdata.u_d.v3;
450 
451           if (len > NFSREAD_SIZE)
452                     len = NFSREAD_SIZE;
453 
454           switch (d->version) {
455           case NFS_VER2:
456                     getfh(d, argsv2->fh);
457                     argsv2->off = htonl((n_long)off);
458                     argsv2->len = htonl((n_long)len);
459                     argsv2->xxx = htonl((n_long)0);
460                     hlen = sizeof(*replv2) - NFSREAD_SIZE;
461                     cc = rpc_call(d->iodesc, NFS_PROG, d->version, NFSPROC_READ,
462                         argsv2, sizeof(*argsv2),
463                         replv2, sizeof(*replv2));
464                     break;
465           case NFS_VER3:
466                     getfh(d, argsv3->fh);
467                     setnquad(argsv3->off, (uint64_t)off);
468                     argsv3->len = htonl((n_long)len);
469                     hlen = sizeof(*replv3) - NFSREAD_SIZE;
470 
471                     /* adjust for variable sized file handle */
472                     alen = sizeof(*argsv3) - offsetof(struct argsv3, off);
473                     memmove(argsv3->fh + fhstore(d->version, argsv3->fh),
474                         &argsv3->off, alen);
475                     alen += fhstore(d->version, argsv3->fh);
476 
477                     cc = rpc_call(d->iodesc, NFS_PROG, d->version, NFSPROC_READ,
478                         argsv3, alen,
479                         replv3, sizeof(*replv3));
480                     break;
481           default:
482                     errno = ENOSYS;
483                     return -1;
484           }
485 
486           if (cc == -1) {
487                     /* errno was already set by rpc_call */
488                     return -1;
489           }
490           if (cc < (ssize_t)hlen) {
491                     errno = EBADRPC;
492                     return -1;
493           }
494 
495           switch (d->version) {
496           case NFS_VER2:
497                     if (replv2->errno) {
498                               errno = ntohl(replv2->errno);
499                               return -1;
500                     }
501                     x = ntohl(replv2->count);
502                     data = replv2->data;
503                     break;
504           case NFS_VER3:
505                     if (replv3->errno) {
506                               errno = ntohl(replv3->errno);
507                               return -1;
508                     }
509 
510                     /* adjust for optional attributes */
511                     if (replv3->attrflag) {
512                               x = ntohl(replv3->length);
513                               data = replv3->data;
514                     } else {
515                               replv3no = (struct replv3_noattr *)replv3;
516                               x = ntohl(replv3no->length);
517                               data = replv3no->data;
518                     }
519                     break;
520           default:
521                     errno = ENOSYS;
522                     return -1;
523           }
524 
525           rlen = cc - hlen;
526           if (rlen < (size_t)x) {
527                     printf("%s: short packet, %zu < %ld\n", __func__, rlen, x);
528                     errno = EBADRPC;
529                     return -1;
530           }
531           (void)memcpy(addr, data, x);
532           return x;
533 }
534 
535 /*
536  * nfs_mount - mount this nfs filesystem to a host
537  * On error, return non-zero and set errno.
538  */
539 int
nfs_mount(int sock,struct in_addr ip,char * path)540 nfs_mount(int sock, struct in_addr ip, char *path)
541 {
542           struct iodesc *desc;
543           struct nfsv2_fattr *fa2;
544           struct nfsv3_fattr *fa3;
545 
546           if (!(desc = socktodesc(sock))) {
547                     errno = EINVAL;
548                     return -1;
549           }
550 
551           /* Bind to a reserved port. */
552           desc->myport = htons(--rpc_port);
553           desc->destip = ip;
554           if (nfs_getrootfh(desc, path, nfs_root_node.fh, &nfs_root_node.version))
555                     return -1;
556           nfs_root_node.iodesc = desc;
557           /* Fake up attributes for the root dir. */
558           switch (nfs_root_node.version) {
559           case NFS_VER2:
560                     fa2 = &nfs_root_node.u_fa.v2;
561                     fa2->fa_type  = htonl(NFDIR);
562                     fa2->fa_mode  = htonl(0755);
563                     fa2->fa_nlink = htonl(2);
564                     break;
565           case NFS_VER3:
566                     fa3 = &nfs_root_node.u_fa.v3;
567                     fa3->fa_type  = htonl(NFDIR);
568                     fa3->fa_mode  = htonl(0755);
569                     fa3->fa_nlink = htonl(2);
570                     break;
571           default:
572                     errno = ENOSYS;
573                     return -1;
574           }
575 
576 #ifdef NFS_DEBUG
577           if (debug)
578                     printf("%s: got fh for %s\n", __func__, path);
579 #endif
580 
581           return 0;
582 }
583 
584 /*
585  * Open a file.
586  * return zero or error number
587  */
588 __compactcall int
nfs_open(const char * path,struct open_file * f)589 nfs_open(const char *path, struct open_file *f)
590 {
591           struct nfs_iodesc *newfd, *currfd;
592           const char *cp;
593 #ifndef NFS_NOSYMLINK
594           const char *ncp;
595           int c;
596           char namebuf[NFS_MAXPATHLEN + 1];
597           char linkbuf[NFS_MAXPATHLEN + 1];
598           int nlinks = 0;
599           n_long fa_type;
600 #endif
601           int error = 0;
602 
603 #ifdef NFS_DEBUG
604           if (debug)
605                     printf("%s: %s\n", __func__, path);
606 #endif
607 
608 #ifdef LIBSA_NFS_IMPLICIT_MOUNT
609           if (nfs_mount(*((int *)(f->f_devdata)), rootip, rootpath))
610                     return errno;
611 #endif
612 
613           if (nfs_root_node.iodesc == NULL) {
614                     printf("%s: must mount first.\n", __func__);
615                     return ENXIO;
616           }
617 
618           currfd = &nfs_root_node;
619           newfd = 0;
620 
621 #ifndef NFS_NOSYMLINK
622           cp = path;
623           while (*cp) {
624                     /*
625                      * Remove extra separators
626                      */
627                     while (*cp == '/')
628                               cp++;
629 
630                     if (*cp == '\0')
631                               break;
632                     /*
633                      * Check that current node is a directory.
634                      */
635                     switch (currfd->version) {
636                     case NFS_VER2:
637                               fa_type = currfd->u_fa.v2.fa_type;
638                               break;
639                     case NFS_VER3:
640                               fa_type = currfd->u_fa.v3.fa_type;
641                               break;
642                     default:
643                               fa_type = htonl(NFNON);
644                               break;
645                     }
646                     if (fa_type != htonl(NFDIR)) {
647                               error = ENOTDIR;
648                               goto out;
649                     }
650 
651                     /* allocate file system specific data structure */
652                     newfd = alloc(sizeof(*newfd));
653                     newfd->iodesc = currfd->iodesc;
654                     newfd->off = 0;
655                     newfd->version = currfd->version;
656 
657                     /*
658                      * Get next component of path name.
659                      */
660                     {
661                               int len = 0;
662 
663                               ncp = cp;
664                               while ((c = *cp) != '\0' && c != '/') {
665                                         if (++len > NFS_MAXNAMLEN) {
666                                                   error = ENOENT;
667                                                   goto out;
668                                         }
669                                         cp++;
670                               }
671                     }
672 
673                     /* lookup a file handle */
674                     error = nfs_lookupfh(currfd, ncp, cp - ncp, newfd);
675                     if (error)
676                               goto out;
677 
678                     /*
679                      * Check for symbolic link
680                      */
681                     switch (newfd->version) {
682                     case NFS_VER2:
683                               fa_type = newfd->u_fa.v2.fa_type;
684                               break;
685                     case NFS_VER3:
686                               fa_type = newfd->u_fa.v3.fa_type;
687                               break;
688                     default:
689                               fa_type = htonl(NFNON);
690                               break;
691                     }
692                     if (fa_type == htonl(NFLNK)) {
693                               int link_len, len;
694 
695                               error = nfs_readlink(newfd, linkbuf);
696                               if (error)
697                                         goto out;
698 
699                               link_len = strlen(linkbuf);
700                               len = strlen(cp);
701 
702                               if (link_len + len > MAXPATHLEN
703                                   || ++nlinks > MAXSYMLINKS) {
704                                         error = ENOENT;
705                                         goto out;
706                               }
707 
708                               (void)memcpy(&namebuf[link_len], cp, len + 1);
709                               (void)memcpy(namebuf, linkbuf, link_len);
710 
711                               /*
712                                * If absolute pathname, restart at root.
713                                * If relative pathname, restart at parent directory.
714                                */
715                               cp = namebuf;
716                               if (*cp == '/') {
717                                         if (currfd != &nfs_root_node)
718                                                   dealloc(currfd, sizeof(*currfd));
719                                         currfd = &nfs_root_node;
720                               }
721 
722                               dealloc(newfd, sizeof(*newfd));
723                               newfd = 0;
724 
725                               continue;
726                     }
727 
728                     if (currfd != &nfs_root_node)
729                               dealloc(currfd, sizeof(*currfd));
730                     currfd = newfd;
731                     newfd = 0;
732           }
733 
734           error = 0;
735 
736 out:
737 #else
738           /* allocate file system specific data structure */
739           currfd = alloc(sizeof(*currfd));
740           currfd->iodesc = nfs_root_node.iodesc;
741           currfd->off = 0;
742           currfd->version = nfs_root_node.version;
743 
744           cp = path;
745           /*
746            * Remove extra separators
747            */
748           while (*cp == '/')
749                     cp++;
750 
751           /* XXX: Check for empty path here? */
752 
753           error = nfs_lookupfh(&nfs_root_node, cp, strlen(cp), currfd);
754 #endif
755           if (!error) {
756                     f->f_fsdata = (void *)currfd;
757                     fsmod = "nfs";
758                     return 0;
759           }
760 
761 #ifdef NFS_DEBUG
762           if (debug)
763                     printf("%s: %s lookupfh failed: %s\n", __func__,
764                         path, strerror(error));
765 #endif
766           if (currfd != &nfs_root_node)
767                     dealloc(currfd, sizeof(*currfd));
768           if (newfd)
769                     dealloc(newfd, sizeof(*newfd));
770 
771           return error;
772 }
773 
774 __compactcall int
nfs_close(struct open_file * f)775 nfs_close(struct open_file *f)
776 {
777           struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
778 
779 #ifdef NFS_DEBUG
780           if (debug)
781                     printf("%s: fp=%p\n", __func__, fp);
782 #endif
783 
784           if (fp)
785                     dealloc(fp, sizeof(struct nfs_iodesc));
786           f->f_fsdata = (void *)0;
787 
788           return 0;
789 }
790 
791 /*
792  * read a portion of a file
793  */
794 __compactcall int
nfs_read(struct open_file * f,void * buf,size_t size,size_t * resid)795 nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
796 {
797           struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
798           ssize_t cc;
799           char *addr = buf;
800 
801 #ifdef NFS_DEBUG
802           if (debug)
803                     printf("%s: size=%zu off=%" PRIx64 "\n", __func__, size, fp->off);
804 #endif
805           while ((int)size > 0) {
806 #if !defined(LIBSA_NO_TWIDDLE)
807                     twiddle();
808 #endif
809                     cc = nfs_readdata(fp, fp->off, (void *)addr, size);
810                     /* XXX maybe should retry on certain errors */
811                     if (cc == -1) {
812 #ifdef NFS_DEBUG
813                               if (debug)
814                                         printf("%s: read: %s\n", __func__,
815                                             strerror(errno));
816 #endif
817                               return errno;       /* XXX - from nfs_readdata */
818                     }
819                     if (cc == 0) {
820 #ifdef NFS_DEBUG
821                               if (debug)
822                                         printf("%s: hit EOF unexpectedly\n", __func__);
823 #endif
824                               goto ret;
825                     }
826                     fp->off += cc;
827                     addr += cc;
828                     size -= cc;
829           }
830 ret:
831           if (resid)
832                     *resid = size;
833 
834           return 0;
835 }
836 
837 /*
838  * Not implemented.
839  */
840 __compactcall int
nfs_write(struct open_file * f,void * buf,size_t size,size_t * resid)841 nfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
842 {
843           return EROFS;
844 }
845 
846 __compactcall off_t
nfs_seek(struct open_file * f,off_t offset,int where)847 nfs_seek(struct open_file *f, off_t offset, int where)
848 {
849           struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
850           off_t size;
851 
852           switch (d->version) {
853           case NFS_VER2:
854                     size = ntohl(d->u_fa.v2.fa_size);
855                     break;
856           case NFS_VER3:
857                     size = getnquad(d->u_fa.v3.fa_size);
858                     break;
859           default:
860                     return -1;
861           }
862 
863           switch (where) {
864           case SEEK_SET:
865                     d->off = offset;
866                     break;
867           case SEEK_CUR:
868                     d->off += offset;
869                     break;
870           case SEEK_END:
871                     d->off = size - offset;
872                     break;
873           default:
874                     return -1;
875           }
876 
877           return d->off;
878 }
879 
880 /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5 */
881 const int nfs_stat_types[8] = {
882           0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, 0 };
883 
884 __compactcall int
nfs_stat(struct open_file * f,struct stat * sb)885 nfs_stat(struct open_file *f, struct stat *sb)
886 {
887           struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
888           n_long ftype, mode;
889 
890           switch (fp->version) {
891           case NFS_VER2:
892                     ftype = ntohl(fp->u_fa.v2.fa_type);
893                     mode  = ntohl(fp->u_fa.v2.fa_mode);
894                     sb->st_nlink = ntohl(fp->u_fa.v2.fa_nlink);
895                     sb->st_uid   = ntohl(fp->u_fa.v2.fa_uid);
896                     sb->st_gid   = ntohl(fp->u_fa.v2.fa_gid);
897                     sb->st_size  = ntohl(fp->u_fa.v2.fa_size);
898                     break;
899           case NFS_VER3:
900                     ftype = ntohl(fp->u_fa.v3.fa_type);
901                     mode  = ntohl(fp->u_fa.v3.fa_mode);
902                     sb->st_nlink = ntohl(fp->u_fa.v3.fa_nlink);
903                     sb->st_uid   = ntohl(fp->u_fa.v3.fa_uid);
904                     sb->st_gid   = ntohl(fp->u_fa.v3.fa_gid);
905                     sb->st_size  = getnquad(fp->u_fa.v3.fa_size);
906                     break;
907           default:
908                     return -1;
909           }
910 
911           mode |= nfs_stat_types[ftype & 7];
912           sb->st_mode  = mode;
913 
914           return 0;
915 }
916 
917 #if defined(LIBSA_ENABLE_LS_OP)
918 #include "ls.h"
919 __compactcall void
nfs_ls(struct open_file * f,const char * pattern)920 nfs_ls(struct open_file *f, const char *pattern)
921 {
922           lsunsup("nfs");
923 }
924 #endif
925