1199767f8SToomas Soome /* $NetBSD: nfs.c,v 1.2 1998/01/24 12:43:09 drochner Exp $ */
2199767f8SToomas Soome
34fa25acaSToomas Soome /*
4199767f8SToomas Soome * Copyright (c) 1993 John Brezak
5199767f8SToomas Soome * All rights reserved.
6199767f8SToomas Soome *
7199767f8SToomas Soome * Redistribution and use in source and binary forms, with or without
8199767f8SToomas Soome * modification, are permitted provided that the following conditions
9199767f8SToomas Soome * are met:
10199767f8SToomas Soome * 1. Redistributions of source code must retain the above copyright
11199767f8SToomas Soome * notice, this list of conditions and the following disclaimer.
12199767f8SToomas Soome * 2. Redistributions in binary form must reproduce the above copyright
13199767f8SToomas Soome * notice, this list of conditions and the following disclaimer in the
14199767f8SToomas Soome * documentation and/or other materials provided with the distribution.
15199767f8SToomas Soome * 3. The name of the author may not be used to endorse or promote products
16199767f8SToomas Soome * derived from this software without specific prior written permission.
17199767f8SToomas Soome *
18199767f8SToomas Soome * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19199767f8SToomas Soome * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20199767f8SToomas Soome * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21199767f8SToomas Soome * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22199767f8SToomas Soome * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23199767f8SToomas Soome * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24199767f8SToomas Soome * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25199767f8SToomas Soome * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26199767f8SToomas Soome * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27199767f8SToomas Soome * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28199767f8SToomas Soome * POSSIBILITY OF SUCH DAMAGE.
29199767f8SToomas Soome */
30199767f8SToomas Soome
31199767f8SToomas Soome #include <sys/cdefs.h>
32199767f8SToomas Soome
33199767f8SToomas Soome #include <sys/param.h>
34199767f8SToomas Soome #include <sys/time.h>
35199767f8SToomas Soome #include <sys/socket.h>
36199767f8SToomas Soome #include <sys/stat.h>
37199767f8SToomas Soome #include <string.h>
38b68080e0SToomas Soome #include <stddef.h>
39199767f8SToomas Soome
40199767f8SToomas Soome #include <netinet/in.h>
41199767f8SToomas Soome #include <netinet/in_systm.h>
42199767f8SToomas Soome
43199767f8SToomas Soome #include "rpcv2.h"
44199767f8SToomas Soome #include "nfsv2.h"
45199767f8SToomas Soome
46199767f8SToomas Soome #include "stand.h"
47199767f8SToomas Soome #include "net.h"
48199767f8SToomas Soome #include "netif.h"
49199767f8SToomas Soome #include "rpc.h"
50199767f8SToomas Soome
51734b3a42SToomas Soome #define NFS_DEBUGxx
52199767f8SToomas Soome
53734b3a42SToomas Soome #define NFSREAD_MIN_SIZE 1024
54734b3a42SToomas Soome #define NFSREAD_MAX_SIZE 16384
55199767f8SToomas Soome
56199767f8SToomas Soome /* NFSv3 definitions */
57199767f8SToomas Soome #define NFS_V3MAXFHSIZE 64
58199767f8SToomas Soome #define NFS_VER3 3
59199767f8SToomas Soome #define RPCMNT_VER3 3
60199767f8SToomas Soome #define NFSPROCV3_LOOKUP 3
61199767f8SToomas Soome #define NFSPROCV3_READLINK 5
62199767f8SToomas Soome #define NFSPROCV3_READ 6
63199767f8SToomas Soome #define NFSPROCV3_READDIR 16
64199767f8SToomas Soome
65199767f8SToomas Soome typedef struct {
66199767f8SToomas Soome uint32_t val[2];
67199767f8SToomas Soome } n_quad;
68199767f8SToomas Soome
69199767f8SToomas Soome struct nfsv3_time {
70199767f8SToomas Soome uint32_t nfs_sec;
71199767f8SToomas Soome uint32_t nfs_nsec;
72199767f8SToomas Soome };
73199767f8SToomas Soome
74199767f8SToomas Soome struct nfsv3_fattrs {
75199767f8SToomas Soome uint32_t fa_type;
76199767f8SToomas Soome uint32_t fa_mode;
77199767f8SToomas Soome uint32_t fa_nlink;
78199767f8SToomas Soome uint32_t fa_uid;
79199767f8SToomas Soome uint32_t fa_gid;
80199767f8SToomas Soome n_quad fa_size;
81199767f8SToomas Soome n_quad fa_used;
82199767f8SToomas Soome n_quad fa_rdev;
83199767f8SToomas Soome n_quad fa_fsid;
84199767f8SToomas Soome n_quad fa_fileid;
85199767f8SToomas Soome struct nfsv3_time fa_atime;
86199767f8SToomas Soome struct nfsv3_time fa_mtime;
87199767f8SToomas Soome struct nfsv3_time fa_ctime;
88199767f8SToomas Soome };
89199767f8SToomas Soome
90199767f8SToomas Soome /*
91199767f8SToomas Soome * For NFSv3, the file handle is variable in size, so most fixed sized
92199767f8SToomas Soome * structures for arguments won't work. For most cases, a structure
93199767f8SToomas Soome * that starts with any fixed size section is followed by an array
94199767f8SToomas Soome * that covers the maximum size required.
95199767f8SToomas Soome */
96199767f8SToomas Soome struct nfsv3_readdir_repl {
97199767f8SToomas Soome uint32_t errno;
98199767f8SToomas Soome uint32_t ok;
99199767f8SToomas Soome struct nfsv3_fattrs fa;
100199767f8SToomas Soome uint32_t cookiev0;
101199767f8SToomas Soome uint32_t cookiev1;
102199767f8SToomas Soome };
103199767f8SToomas Soome
104199767f8SToomas Soome struct nfsv3_readdir_entry {
105199767f8SToomas Soome uint32_t follows;
106199767f8SToomas Soome uint32_t fid0;
107199767f8SToomas Soome uint32_t fid1;
108199767f8SToomas Soome uint32_t len;
109199767f8SToomas Soome uint32_t nameplus[0];
110199767f8SToomas Soome };
111199767f8SToomas Soome
112199767f8SToomas Soome struct nfs_iodesc {
113199767f8SToomas Soome struct iodesc *iodesc;
114199767f8SToomas Soome off_t off;
115199767f8SToomas Soome uint32_t fhsize;
116734b3a42SToomas Soome uchar_t fh[NFS_V3MAXFHSIZE];
117199767f8SToomas Soome struct nfsv3_fattrs fa; /* all in network order */
118199767f8SToomas Soome uint64_t cookie;
119199767f8SToomas Soome };
120199767f8SToomas Soome
121199767f8SToomas Soome /*
122199767f8SToomas Soome * XXX interactions with tftp? See nfswrapper.c for a confusing
123199767f8SToomas Soome * issue.
124199767f8SToomas Soome */
125734b3a42SToomas Soome int nfs_open(const char *path, struct open_file *f);
126734b3a42SToomas Soome static int nfs_close(struct open_file *f);
127734b3a42SToomas Soome static int nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
128734b3a42SToomas Soome static off_t nfs_seek(struct open_file *f, off_t offset, int where);
129734b3a42SToomas Soome static int nfs_stat(struct open_file *f, struct stat *sb);
130734b3a42SToomas Soome static int nfs_readdir(struct open_file *f, struct dirent *d);
131199767f8SToomas Soome
132199767f8SToomas Soome struct nfs_iodesc nfs_root_node;
133199767f8SToomas Soome
134199767f8SToomas Soome struct fs_ops nfs_fsops = {
135734b3a42SToomas Soome .fs_name = "nfs",
136734b3a42SToomas Soome .fo_open = nfs_open,
137734b3a42SToomas Soome .fo_close = nfs_close,
138734b3a42SToomas Soome .fo_read = nfs_read,
139734b3a42SToomas Soome .fo_write = null_write,
140734b3a42SToomas Soome .fo_seek = nfs_seek,
141734b3a42SToomas Soome .fo_stat = nfs_stat,
142734b3a42SToomas Soome .fo_readdir = nfs_readdir
143199767f8SToomas Soome };
144199767f8SToomas Soome
145b68080e0SToomas Soome static int nfs_read_size = NFSREAD_MIN_SIZE;
146b68080e0SToomas Soome
147b68080e0SToomas Soome /*
148b68080e0SToomas Soome * Improve boot performance over NFS
149b68080e0SToomas Soome */
150b68080e0SToomas Soome static void
set_nfs_read_size(void)151b68080e0SToomas Soome set_nfs_read_size(void)
152b68080e0SToomas Soome {
153b68080e0SToomas Soome char *env, *end;
154b68080e0SToomas Soome char buf[10];
155b68080e0SToomas Soome
156b68080e0SToomas Soome if ((env = getenv("nfs.read_size")) != NULL) {
157b68080e0SToomas Soome errno = 0;
158b68080e0SToomas Soome nfs_read_size = strtol(env, &end, 0);
159b68080e0SToomas Soome if (errno != 0 || *env == '\0' || *end != '\0') {
160b68080e0SToomas Soome printf("%s: bad value: \"%s\", defaulting to %d\n",
161b68080e0SToomas Soome "nfs.read_size", env, NFSREAD_MIN_SIZE);
162b68080e0SToomas Soome nfs_read_size = NFSREAD_MIN_SIZE;
163b68080e0SToomas Soome }
164b68080e0SToomas Soome }
165b68080e0SToomas Soome if (nfs_read_size < NFSREAD_MIN_SIZE) {
166b68080e0SToomas Soome printf("%s: bad value: \"%d\", defaulting to %d\n",
167b68080e0SToomas Soome "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
168b68080e0SToomas Soome nfs_read_size = NFSREAD_MIN_SIZE;
169b68080e0SToomas Soome }
170b68080e0SToomas Soome if (nfs_read_size > NFSREAD_MAX_SIZE) {
171b68080e0SToomas Soome printf("%s: bad value: \"%d\", defaulting to %d\n",
172b68080e0SToomas Soome "nfs.read_size", nfs_read_size, NFSREAD_MIN_SIZE);
173b68080e0SToomas Soome nfs_read_size = NFSREAD_MAX_SIZE;
174b68080e0SToomas Soome }
175b68080e0SToomas Soome snprintf(buf, sizeof (buf), "%d", nfs_read_size);
176b68080e0SToomas Soome setenv("nfs.read_size", buf, 1);
177b68080e0SToomas Soome }
178b68080e0SToomas Soome
179199767f8SToomas Soome /*
180199767f8SToomas Soome * Fetch the root file handle (call mount daemon)
181199767f8SToomas Soome * Return zero or error number.
182199767f8SToomas Soome */
183199767f8SToomas Soome int
nfs_getrootfh(struct iodesc * d,char * path,uint32_t * fhlenp,uchar_t * fhp)184734b3a42SToomas Soome nfs_getrootfh(struct iodesc *d, char *path, uint32_t *fhlenp, uchar_t *fhp)
185199767f8SToomas Soome {
186859472daSToomas Soome void *pkt = NULL;
187199767f8SToomas Soome int len;
188199767f8SToomas Soome struct args {
189199767f8SToomas Soome uint32_t len;
190199767f8SToomas Soome char path[FNAME_SIZE];
191199767f8SToomas Soome } *args;
192199767f8SToomas Soome struct repl {
193199767f8SToomas Soome uint32_t errno;
194199767f8SToomas Soome uint32_t fhsize;
195734b3a42SToomas Soome uchar_t fh[NFS_V3MAXFHSIZE];
196199767f8SToomas Soome uint32_t authcnt;
197199767f8SToomas Soome uint32_t auth[7];
198199767f8SToomas Soome } *repl;
199199767f8SToomas Soome struct {
200199767f8SToomas Soome uint32_t h[RPC_HEADER_WORDS];
201199767f8SToomas Soome struct args d;
202199767f8SToomas Soome } sdata;
203199767f8SToomas Soome size_t cc;
204199767f8SToomas Soome
205199767f8SToomas Soome #ifdef NFS_DEBUG
206199767f8SToomas Soome if (debug)
207199767f8SToomas Soome printf("nfs_getrootfh: %s\n", path);
208199767f8SToomas Soome #endif
209199767f8SToomas Soome
210199767f8SToomas Soome args = &sdata.d;
211199767f8SToomas Soome
212734b3a42SToomas Soome bzero(args, sizeof (*args));
213199767f8SToomas Soome len = strlen(path);
214734b3a42SToomas Soome if (len > sizeof (args->path))
215734b3a42SToomas Soome len = sizeof (args->path);
216199767f8SToomas Soome args->len = htonl(len);
217199767f8SToomas Soome bcopy(path, args->path, len);
218734b3a42SToomas Soome len = sizeof (uint32_t) + roundup(len, sizeof (uint32_t));
219199767f8SToomas Soome
220199767f8SToomas Soome cc = rpc_call(d, RPCPROG_MNT, RPCMNT_VER3, RPCMNT_MOUNT,
221859472daSToomas Soome args, len, (void **)&repl, &pkt);
222859472daSToomas Soome if (cc == -1) {
223859472daSToomas Soome free(pkt);
224199767f8SToomas Soome /* errno was set by rpc_call */
225199767f8SToomas Soome return (errno);
226859472daSToomas Soome }
227859472daSToomas Soome if (cc < 2 * sizeof (uint32_t)) {
228859472daSToomas Soome free(pkt);
229199767f8SToomas Soome return (EBADRPC);
230859472daSToomas Soome }
231859472daSToomas Soome if (repl->errno != 0) {
232859472daSToomas Soome free(pkt);
233199767f8SToomas Soome return (ntohl(repl->errno));
234859472daSToomas Soome }
235199767f8SToomas Soome *fhlenp = ntohl(repl->fhsize);
236199767f8SToomas Soome bcopy(repl->fh, fhp, *fhlenp);
237b68080e0SToomas Soome
238b68080e0SToomas Soome set_nfs_read_size();
239859472daSToomas Soome free(pkt);
240199767f8SToomas Soome return (0);
241199767f8SToomas Soome }
242199767f8SToomas Soome
243199767f8SToomas Soome /*
244199767f8SToomas Soome * Lookup a file. Store handle and attributes.
245199767f8SToomas Soome * Return zero or error number.
246199767f8SToomas Soome */
247199767f8SToomas Soome int
nfs_lookupfh(struct nfs_iodesc * d,const char * name,struct nfs_iodesc * newfd)248199767f8SToomas Soome nfs_lookupfh(struct nfs_iodesc *d, const char *name, struct nfs_iodesc *newfd)
249199767f8SToomas Soome {
250859472daSToomas Soome void *pkt = NULL;
2514fa25acaSToomas Soome int len, pos;
252199767f8SToomas Soome struct args {
253199767f8SToomas Soome uint32_t fhsize;
254199767f8SToomas Soome uint32_t fhplusname[1 +
255734b3a42SToomas Soome (NFS_V3MAXFHSIZE + FNAME_SIZE) / sizeof (uint32_t)];
256199767f8SToomas Soome } *args;
257199767f8SToomas Soome struct repl {
258199767f8SToomas Soome uint32_t errno;
259199767f8SToomas Soome uint32_t fhsize;
260199767f8SToomas Soome uint32_t fhplusattr[(NFS_V3MAXFHSIZE +
261734b3a42SToomas Soome 2 * (sizeof (uint32_t) +
262734b3a42SToomas Soome sizeof (struct nfsv3_fattrs))) / sizeof (uint32_t)];
263199767f8SToomas Soome } *repl;
264199767f8SToomas Soome struct {
265199767f8SToomas Soome uint32_t h[RPC_HEADER_WORDS];
266199767f8SToomas Soome struct args d;
267199767f8SToomas Soome } sdata;
268199767f8SToomas Soome ssize_t cc;
269199767f8SToomas Soome
270199767f8SToomas Soome #ifdef NFS_DEBUG
271199767f8SToomas Soome if (debug)
272199767f8SToomas Soome printf("lookupfh: called\n");
273199767f8SToomas Soome #endif
274199767f8SToomas Soome
275199767f8SToomas Soome args = &sdata.d;
276199767f8SToomas Soome
277734b3a42SToomas Soome bzero(args, sizeof (*args));
278199767f8SToomas Soome args->fhsize = htonl(d->fhsize);
279199767f8SToomas Soome bcopy(d->fh, args->fhplusname, d->fhsize);
280199767f8SToomas Soome len = strlen(name);
281199767f8SToomas Soome if (len > FNAME_SIZE)
282199767f8SToomas Soome len = FNAME_SIZE;
283734b3a42SToomas Soome pos = roundup(d->fhsize, sizeof (uint32_t)) / sizeof (uint32_t);
284199767f8SToomas Soome args->fhplusname[pos++] = htonl(len);
285199767f8SToomas Soome bcopy(name, &args->fhplusname[pos], len);
286734b3a42SToomas Soome len = sizeof (uint32_t) + pos * sizeof (uint32_t) +
287734b3a42SToomas Soome roundup(len, sizeof (uint32_t));
288199767f8SToomas Soome
289199767f8SToomas Soome cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_LOOKUP,
290859472daSToomas Soome args, len, (void **)&repl, &pkt);
291859472daSToomas Soome if (cc == -1) {
292859472daSToomas Soome free(pkt);
293199767f8SToomas Soome return (errno); /* XXX - from rpc_call */
294859472daSToomas Soome }
295734b3a42SToomas Soome if (cc < 2 * sizeof (uint32_t)) {
296859472daSToomas Soome free(pkt);
297199767f8SToomas Soome return (EIO);
298859472daSToomas Soome }
299859472daSToomas Soome if (repl->errno != 0) {
300859472daSToomas Soome free(pkt);
301199767f8SToomas Soome /* saerrno.h now matches NFS error numbers. */
302199767f8SToomas Soome return (ntohl(repl->errno));
303859472daSToomas Soome }
304199767f8SToomas Soome newfd->fhsize = ntohl(repl->fhsize);
305199767f8SToomas Soome bcopy(repl->fhplusattr, &newfd->fh, newfd->fhsize);
306734b3a42SToomas Soome pos = roundup(newfd->fhsize, sizeof (uint32_t)) / sizeof (uint32_t);
307859472daSToomas Soome if (repl->fhplusattr[pos++] == 0) {
308859472daSToomas Soome free(pkt);
309199767f8SToomas Soome return (EIO);
310859472daSToomas Soome }
311734b3a42SToomas Soome bcopy(&repl->fhplusattr[pos], &newfd->fa, sizeof (newfd->fa));
312859472daSToomas Soome free(pkt);
313199767f8SToomas Soome return (0);
314199767f8SToomas Soome }
315199767f8SToomas Soome
316199767f8SToomas Soome /*
317199767f8SToomas Soome * Get the destination of a symbolic link.
318199767f8SToomas Soome */
319199767f8SToomas Soome int
nfs_readlink(struct nfs_iodesc * d,char * buf)320199767f8SToomas Soome nfs_readlink(struct nfs_iodesc *d, char *buf)
321199767f8SToomas Soome {
322859472daSToomas Soome void *pkt = NULL;
323199767f8SToomas Soome struct args {
324199767f8SToomas Soome uint32_t fhsize;
325734b3a42SToomas Soome uchar_t fh[NFS_V3MAXFHSIZE];
326199767f8SToomas Soome } *args;
327199767f8SToomas Soome struct repl {
328199767f8SToomas Soome uint32_t errno;
329199767f8SToomas Soome uint32_t ok;
330199767f8SToomas Soome struct nfsv3_fattrs fa;
331199767f8SToomas Soome uint32_t len;
332734b3a42SToomas Soome uchar_t path[NFS_MAXPATHLEN];
333199767f8SToomas Soome } *repl;
334199767f8SToomas Soome struct {
335199767f8SToomas Soome uint32_t h[RPC_HEADER_WORDS];
336199767f8SToomas Soome struct args d;
337199767f8SToomas Soome } sdata;
338199767f8SToomas Soome ssize_t cc;
339859472daSToomas Soome int rc = 0;
340199767f8SToomas Soome
341199767f8SToomas Soome #ifdef NFS_DEBUG
342199767f8SToomas Soome if (debug)
343199767f8SToomas Soome printf("readlink: called\n");
344199767f8SToomas Soome #endif
345199767f8SToomas Soome
346199767f8SToomas Soome args = &sdata.d;
347199767f8SToomas Soome
348734b3a42SToomas Soome bzero(args, sizeof (*args));
349199767f8SToomas Soome args->fhsize = htonl(d->fhsize);
350199767f8SToomas Soome bcopy(d->fh, args->fh, d->fhsize);
351199767f8SToomas Soome cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READLINK,
352734b3a42SToomas Soome args, sizeof (uint32_t) + roundup(d->fhsize, sizeof (uint32_t)),
353859472daSToomas Soome (void **)&repl, &pkt);
354199767f8SToomas Soome if (cc == -1)
355199767f8SToomas Soome return (errno);
356199767f8SToomas Soome
357734b3a42SToomas Soome if (cc < 2 * sizeof (uint32_t)) {
358859472daSToomas Soome rc = EIO;
359859472daSToomas Soome goto done;
360859472daSToomas Soome }
361199767f8SToomas Soome
362859472daSToomas Soome if (repl->errno != 0) {
363859472daSToomas Soome rc = ntohl(repl->errno);
364859472daSToomas Soome goto done;
365859472daSToomas Soome }
366199767f8SToomas Soome
367859472daSToomas Soome if (repl->ok == 0) {
368859472daSToomas Soome rc = EIO;
369859472daSToomas Soome goto done;
370859472daSToomas Soome }
371199767f8SToomas Soome
372199767f8SToomas Soome repl->len = ntohl(repl->len);
373859472daSToomas Soome if (repl->len > NFS_MAXPATHLEN) {
374859472daSToomas Soome rc = ENAMETOOLONG;
375859472daSToomas Soome goto done;
376859472daSToomas Soome }
377199767f8SToomas Soome
378199767f8SToomas Soome bcopy(repl->path, buf, repl->len);
379199767f8SToomas Soome buf[repl->len] = 0;
380859472daSToomas Soome done:
381859472daSToomas Soome free(pkt);
382859472daSToomas Soome return (rc);
383199767f8SToomas Soome }
384199767f8SToomas Soome
385199767f8SToomas Soome /*
386199767f8SToomas Soome * Read data from a file.
387199767f8SToomas Soome * Return transfer count or -1 (and set errno)
388199767f8SToomas Soome */
389199767f8SToomas Soome ssize_t
nfs_readdata(struct nfs_iodesc * d,off_t off,void * addr,size_t len)390199767f8SToomas Soome nfs_readdata(struct nfs_iodesc *d, off_t off, void *addr, size_t len)
391199767f8SToomas Soome {
392859472daSToomas Soome void *pkt = NULL;
393199767f8SToomas Soome struct args {
394199767f8SToomas Soome uint32_t fhsize;
395734b3a42SToomas Soome uint32_t fhoffcnt[NFS_V3MAXFHSIZE / sizeof (uint32_t) + 3];
396199767f8SToomas Soome } *args;
397199767f8SToomas Soome struct repl {
398199767f8SToomas Soome uint32_t errno;
399199767f8SToomas Soome uint32_t ok;
400199767f8SToomas Soome struct nfsv3_fattrs fa;
401199767f8SToomas Soome uint32_t count;
402199767f8SToomas Soome uint32_t eof;
403199767f8SToomas Soome uint32_t len;
404734b3a42SToomas Soome uchar_t data[NFSREAD_MAX_SIZE];
405199767f8SToomas Soome } *repl;
406199767f8SToomas Soome struct {
407199767f8SToomas Soome uint32_t h[RPC_HEADER_WORDS];
408199767f8SToomas Soome struct args d;
409199767f8SToomas Soome } sdata;
410199767f8SToomas Soome size_t cc;
411199767f8SToomas Soome long x;
412199767f8SToomas Soome int hlen, rlen, pos;
413199767f8SToomas Soome
414199767f8SToomas Soome args = &sdata.d;
415199767f8SToomas Soome
416734b3a42SToomas Soome bzero(args, sizeof (*args));
417199767f8SToomas Soome args->fhsize = htonl(d->fhsize);
418199767f8SToomas Soome bcopy(d->fh, args->fhoffcnt, d->fhsize);
419734b3a42SToomas Soome pos = roundup(d->fhsize, sizeof (uint32_t)) / sizeof (uint32_t);
420199767f8SToomas Soome args->fhoffcnt[pos++] = 0;
421199767f8SToomas Soome args->fhoffcnt[pos++] = htonl((uint32_t)off);
422b68080e0SToomas Soome if (len > nfs_read_size)
423b68080e0SToomas Soome len = nfs_read_size;
424199767f8SToomas Soome args->fhoffcnt[pos] = htonl((uint32_t)len);
425b68080e0SToomas Soome hlen = offsetof(struct repl, data[0]);
426199767f8SToomas Soome
427199767f8SToomas Soome cc = rpc_call(d->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READ,
428734b3a42SToomas Soome args, 4 * sizeof (uint32_t) + roundup(d->fhsize, sizeof (uint32_t)),
429859472daSToomas Soome (void **)&repl, &pkt);
430859472daSToomas Soome if (cc == -1) {
431199767f8SToomas Soome /* errno was already set by rpc_call */
432199767f8SToomas Soome return (-1);
433859472daSToomas Soome }
434199767f8SToomas Soome if (cc < hlen) {
435199767f8SToomas Soome errno = EBADRPC;
436859472daSToomas Soome free(pkt);
437199767f8SToomas Soome return (-1);
438199767f8SToomas Soome }
439199767f8SToomas Soome if (repl->errno != 0) {
440199767f8SToomas Soome errno = ntohl(repl->errno);
441859472daSToomas Soome free(pkt);
442199767f8SToomas Soome return (-1);
443199767f8SToomas Soome }
444199767f8SToomas Soome rlen = cc - hlen;
445199767f8SToomas Soome x = ntohl(repl->count);
446199767f8SToomas Soome if (rlen < x) {
447199767f8SToomas Soome printf("nfsread: short packet, %d < %ld\n", rlen, x);
448199767f8SToomas Soome errno = EBADRPC;
449859472daSToomas Soome free(pkt);
450199767f8SToomas Soome return (-1);
451199767f8SToomas Soome }
452199767f8SToomas Soome bcopy(repl->data, addr, x);
453859472daSToomas Soome free(pkt);
454199767f8SToomas Soome return (x);
455199767f8SToomas Soome }
456199767f8SToomas Soome
457199767f8SToomas Soome /*
458199767f8SToomas Soome * Open a file.
459199767f8SToomas Soome * return zero or error number
460199767f8SToomas Soome */
461199767f8SToomas Soome int
nfs_open(const char * upath,struct open_file * f)462199767f8SToomas Soome nfs_open(const char *upath, struct open_file *f)
463199767f8SToomas Soome {
4646538c7b4SToomas Soome struct devdesc *dev;
465199767f8SToomas Soome struct iodesc *desc;
4664fa25acaSToomas Soome struct nfs_iodesc *currfd = NULL;
467199767f8SToomas Soome char buf[2 * NFS_V3MAXFHSIZE + 3];
468734b3a42SToomas Soome uchar_t *fh;
469199767f8SToomas Soome char *cp;
470199767f8SToomas Soome int i;
4714fa25acaSToomas Soome struct nfs_iodesc *newfd = NULL;
472199767f8SToomas Soome char *ncp;
473199767f8SToomas Soome int c;
474199767f8SToomas Soome char namebuf[NFS_MAXPATHLEN + 1];
475199767f8SToomas Soome char linkbuf[NFS_MAXPATHLEN + 1];
476199767f8SToomas Soome int nlinks = 0;
477199767f8SToomas Soome int error;
4784fa25acaSToomas Soome char *path = NULL;
479199767f8SToomas Soome
480aa61755eSToomas Soome if (netproto != NET_NFS)
481aa61755eSToomas Soome return (EINVAL);
482aa61755eSToomas Soome
4836538c7b4SToomas Soome dev = f->f_devdata;
484199767f8SToomas Soome #ifdef NFS_DEBUG
4854fa25acaSToomas Soome if (debug)
4864fa25acaSToomas Soome printf("nfs_open: %s (rootpath=%s)\n", upath, rootpath);
487199767f8SToomas Soome #endif
488199767f8SToomas Soome if (!rootpath[0]) {
489199767f8SToomas Soome printf("no rootpath, no nfs\n");
490199767f8SToomas Soome return (ENXIO);
491199767f8SToomas Soome }
492199767f8SToomas Soome
493859472daSToomas Soome if (f->f_dev->dv_type != DEVT_NET)
494199767f8SToomas Soome return (EINVAL);
495199767f8SToomas Soome
4966538c7b4SToomas Soome if (!(desc = socktodesc(*(int *)(dev->d_opendata))))
497199767f8SToomas Soome return (EINVAL);
498199767f8SToomas Soome
499199767f8SToomas Soome /* Bind to a reserved port. */
500199767f8SToomas Soome desc->myport = htons(--rpc_port);
501199767f8SToomas Soome desc->destip = rootip;
502199767f8SToomas Soome if ((error = nfs_getrootfh(desc, rootpath, &nfs_root_node.fhsize,
503199767f8SToomas Soome nfs_root_node.fh)))
504199767f8SToomas Soome return (error);
505199767f8SToomas Soome nfs_root_node.fa.fa_type = htonl(NFDIR);
506199767f8SToomas Soome nfs_root_node.fa.fa_mode = htonl(0755);
507199767f8SToomas Soome nfs_root_node.fa.fa_nlink = htonl(2);
508199767f8SToomas Soome nfs_root_node.iodesc = desc;
509199767f8SToomas Soome
510199767f8SToomas Soome fh = &nfs_root_node.fh[0];
511199767f8SToomas Soome buf[0] = 'X';
512199767f8SToomas Soome cp = &buf[1];
513199767f8SToomas Soome for (i = 0; i < nfs_root_node.fhsize; i++, cp += 2)
514199767f8SToomas Soome sprintf(cp, "%02x", fh[i]);
515199767f8SToomas Soome sprintf(cp, "X");
516199767f8SToomas Soome setenv("boot.nfsroot.server", inet_ntoa(rootip), 1);
517199767f8SToomas Soome setenv("boot.nfsroot.path", rootpath, 1);
518199767f8SToomas Soome setenv("boot.nfsroot.nfshandle", buf, 1);
519199767f8SToomas Soome sprintf(buf, "%d", nfs_root_node.fhsize);
520199767f8SToomas Soome setenv("boot.nfsroot.nfshandlelen", buf, 1);
521199767f8SToomas Soome
522199767f8SToomas Soome /* Allocate file system specific data structure */
523734b3a42SToomas Soome currfd = malloc(sizeof (*newfd));
524199767f8SToomas Soome if (currfd == NULL) {
525199767f8SToomas Soome error = ENOMEM;
526199767f8SToomas Soome goto out;
527199767f8SToomas Soome }
528734b3a42SToomas Soome bcopy(&nfs_root_node, currfd, sizeof (*currfd));
52900b63190SToomas Soome newfd = NULL;
530199767f8SToomas Soome
531199767f8SToomas Soome cp = path = strdup(upath);
532199767f8SToomas Soome if (path == NULL) {
533199767f8SToomas Soome error = ENOMEM;
534199767f8SToomas Soome goto out;
535199767f8SToomas Soome }
536199767f8SToomas Soome while (*cp) {
537199767f8SToomas Soome /*
538199767f8SToomas Soome * Remove extra separators
539199767f8SToomas Soome */
540199767f8SToomas Soome while (*cp == '/')
541199767f8SToomas Soome cp++;
542199767f8SToomas Soome
543199767f8SToomas Soome if (*cp == '\0')
544199767f8SToomas Soome break;
545199767f8SToomas Soome /*
546199767f8SToomas Soome * Check that current node is a directory.
547199767f8SToomas Soome */
548199767f8SToomas Soome if (currfd->fa.fa_type != htonl(NFDIR)) {
549199767f8SToomas Soome error = ENOTDIR;
550199767f8SToomas Soome goto out;
551199767f8SToomas Soome }
552199767f8SToomas Soome
553199767f8SToomas Soome /* allocate file system specific data structure */
554734b3a42SToomas Soome newfd = malloc(sizeof (*newfd));
555199767f8SToomas Soome if (newfd == NULL) {
556199767f8SToomas Soome error = ENOMEM;
557199767f8SToomas Soome goto out;
558199767f8SToomas Soome }
559199767f8SToomas Soome newfd->iodesc = currfd->iodesc;
560199767f8SToomas Soome
561199767f8SToomas Soome /*
562199767f8SToomas Soome * Get next component of path name.
563199767f8SToomas Soome */
564199767f8SToomas Soome {
565199767f8SToomas Soome int len = 0;
566199767f8SToomas Soome
567199767f8SToomas Soome ncp = cp;
568199767f8SToomas Soome while ((c = *cp) != '\0' && c != '/') {
569199767f8SToomas Soome if (++len > NFS_MAXNAMLEN) {
570199767f8SToomas Soome error = ENOENT;
571199767f8SToomas Soome goto out;
572199767f8SToomas Soome }
573199767f8SToomas Soome cp++;
574199767f8SToomas Soome }
575199767f8SToomas Soome *cp = '\0';
576199767f8SToomas Soome }
577199767f8SToomas Soome
578199767f8SToomas Soome /* lookup a file handle */
579199767f8SToomas Soome error = nfs_lookupfh(currfd, ncp, newfd);
580199767f8SToomas Soome *cp = c;
581199767f8SToomas Soome if (error)
582199767f8SToomas Soome goto out;
583199767f8SToomas Soome
584199767f8SToomas Soome /*
585199767f8SToomas Soome * Check for symbolic link
586199767f8SToomas Soome */
587199767f8SToomas Soome if (newfd->fa.fa_type == htonl(NFLNK)) {
588199767f8SToomas Soome int link_len, len;
589199767f8SToomas Soome
590199767f8SToomas Soome error = nfs_readlink(newfd, linkbuf);
591199767f8SToomas Soome if (error)
592199767f8SToomas Soome goto out;
593199767f8SToomas Soome
594199767f8SToomas Soome link_len = strlen(linkbuf);
595199767f8SToomas Soome len = strlen(cp);
596199767f8SToomas Soome
597734b3a42SToomas Soome if (link_len + len > MAXPATHLEN ||
598734b3a42SToomas Soome ++nlinks > MAXSYMLINKS) {
599199767f8SToomas Soome error = ENOENT;
600199767f8SToomas Soome goto out;
601199767f8SToomas Soome }
602199767f8SToomas Soome
603199767f8SToomas Soome bcopy(cp, &namebuf[link_len], len + 1);
604199767f8SToomas Soome bcopy(linkbuf, namebuf, link_len);
605199767f8SToomas Soome
606199767f8SToomas Soome /*
607199767f8SToomas Soome * If absolute pathname, restart at root.
608199767f8SToomas Soome * If relative pathname, restart at parent directory.
609199767f8SToomas Soome */
610199767f8SToomas Soome cp = namebuf;
611199767f8SToomas Soome if (*cp == '/')
612734b3a42SToomas Soome bcopy(&nfs_root_node, currfd, sizeof (*currfd));
613199767f8SToomas Soome
614199767f8SToomas Soome free(newfd);
61500b63190SToomas Soome newfd = NULL;
616199767f8SToomas Soome
617199767f8SToomas Soome continue;
618199767f8SToomas Soome }
619199767f8SToomas Soome
620199767f8SToomas Soome free(currfd);
621199767f8SToomas Soome currfd = newfd;
62200b63190SToomas Soome newfd = NULL;
623199767f8SToomas Soome }
624199767f8SToomas Soome
625199767f8SToomas Soome error = 0;
626199767f8SToomas Soome
627199767f8SToomas Soome out:
628199767f8SToomas Soome free(newfd);
629199767f8SToomas Soome free(path);
630199767f8SToomas Soome if (!error) {
631199767f8SToomas Soome currfd->off = 0;
632199767f8SToomas Soome currfd->cookie = 0;
633734b3a42SToomas Soome f->f_fsdata = currfd;
634199767f8SToomas Soome return (0);
635199767f8SToomas Soome }
636199767f8SToomas Soome
637199767f8SToomas Soome #ifdef NFS_DEBUG
638199767f8SToomas Soome if (debug)
639199767f8SToomas Soome printf("nfs_open: %s lookupfh failed: %s\n",
640199767f8SToomas Soome path, strerror(error));
641199767f8SToomas Soome #endif
642199767f8SToomas Soome free(currfd);
643199767f8SToomas Soome
644199767f8SToomas Soome return (error);
645199767f8SToomas Soome }
646199767f8SToomas Soome
647199767f8SToomas Soome int
nfs_close(struct open_file * f)648199767f8SToomas Soome nfs_close(struct open_file *f)
649199767f8SToomas Soome {
650199767f8SToomas Soome struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
651199767f8SToomas Soome
652199767f8SToomas Soome #ifdef NFS_DEBUG
653199767f8SToomas Soome if (debug)
654734b3a42SToomas Soome printf("nfs_close: fp=%#p\n", fp);
655199767f8SToomas Soome #endif
656199767f8SToomas Soome
657859472daSToomas Soome free(fp);
658859472daSToomas Soome f->f_fsdata = NULL;
659199767f8SToomas Soome
660199767f8SToomas Soome return (0);
661199767f8SToomas Soome }
662199767f8SToomas Soome
663199767f8SToomas Soome /*
664199767f8SToomas Soome * read a portion of a file
665199767f8SToomas Soome */
666199767f8SToomas Soome int
nfs_read(struct open_file * f,void * buf,size_t size,size_t * resid)667199767f8SToomas Soome nfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
668199767f8SToomas Soome {
669199767f8SToomas Soome struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
670199767f8SToomas Soome ssize_t cc;
671199767f8SToomas Soome char *addr = buf;
672199767f8SToomas Soome
673199767f8SToomas Soome #ifdef NFS_DEBUG
674199767f8SToomas Soome if (debug)
675734b3a42SToomas Soome printf("nfs_read: size=%zu off=%j\n", size,
676734b3a42SToomas Soome (intmax_t)fp->off);
677199767f8SToomas Soome #endif
678734b3a42SToomas Soome while (size > 0) {
679199767f8SToomas Soome twiddle(16);
680734b3a42SToomas Soome cc = nfs_readdata(fp, fp->off, addr, size);
681199767f8SToomas Soome /* XXX maybe should retry on certain errors */
682199767f8SToomas Soome if (cc == -1) {
683199767f8SToomas Soome #ifdef NFS_DEBUG
684199767f8SToomas Soome if (debug)
685199767f8SToomas Soome printf("nfs_read: read: %s", strerror(errno));
686199767f8SToomas Soome #endif
687199767f8SToomas Soome return (errno); /* XXX - from nfs_readdata */
688199767f8SToomas Soome }
689199767f8SToomas Soome if (cc == 0) {
690199767f8SToomas Soome #ifdef NFS_DEBUG
691199767f8SToomas Soome if (debug)
692199767f8SToomas Soome printf("nfs_read: hit EOF unexpectantly");
693199767f8SToomas Soome #endif
694199767f8SToomas Soome goto ret;
695199767f8SToomas Soome }
696199767f8SToomas Soome fp->off += cc;
697199767f8SToomas Soome addr += cc;
698199767f8SToomas Soome size -= cc;
699199767f8SToomas Soome }
700199767f8SToomas Soome ret:
701199767f8SToomas Soome if (resid)
702199767f8SToomas Soome *resid = size;
703199767f8SToomas Soome
704199767f8SToomas Soome return (0);
705199767f8SToomas Soome }
706199767f8SToomas Soome
707199767f8SToomas Soome off_t
nfs_seek(struct open_file * f,off_t offset,int where)708199767f8SToomas Soome nfs_seek(struct open_file *f, off_t offset, int where)
709199767f8SToomas Soome {
710199767f8SToomas Soome struct nfs_iodesc *d = (struct nfs_iodesc *)f->f_fsdata;
711199767f8SToomas Soome uint32_t size = ntohl(d->fa.fa_size.val[1]);
712199767f8SToomas Soome
713199767f8SToomas Soome switch (where) {
714199767f8SToomas Soome case SEEK_SET:
715199767f8SToomas Soome d->off = offset;
716199767f8SToomas Soome break;
717199767f8SToomas Soome case SEEK_CUR:
718199767f8SToomas Soome d->off += offset;
719199767f8SToomas Soome break;
720199767f8SToomas Soome case SEEK_END:
721199767f8SToomas Soome d->off = size - offset;
722199767f8SToomas Soome break;
723199767f8SToomas Soome default:
724199767f8SToomas Soome errno = EINVAL;
725199767f8SToomas Soome return (-1);
726199767f8SToomas Soome }
727199767f8SToomas Soome
728199767f8SToomas Soome return (d->off);
729199767f8SToomas Soome }
730199767f8SToomas Soome
731199767f8SToomas Soome /* NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5, NFSOCK=6, NFFIFO=7 */
732199767f8SToomas Soome int nfs_stat_types[9] = {
733199767f8SToomas Soome 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, 0 };
734199767f8SToomas Soome
735199767f8SToomas Soome int
nfs_stat(struct open_file * f,struct stat * sb)736199767f8SToomas Soome nfs_stat(struct open_file *f, struct stat *sb)
737199767f8SToomas Soome {
738199767f8SToomas Soome struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
739199767f8SToomas Soome uint32_t ftype, mode;
740199767f8SToomas Soome
741199767f8SToomas Soome ftype = ntohl(fp->fa.fa_type);
742199767f8SToomas Soome mode = ntohl(fp->fa.fa_mode);
743199767f8SToomas Soome mode |= nfs_stat_types[ftype & 7];
744199767f8SToomas Soome
745199767f8SToomas Soome sb->st_mode = mode;
746199767f8SToomas Soome sb->st_nlink = ntohl(fp->fa.fa_nlink);
747199767f8SToomas Soome sb->st_uid = ntohl(fp->fa.fa_uid);
748199767f8SToomas Soome sb->st_gid = ntohl(fp->fa.fa_gid);
749199767f8SToomas Soome sb->st_size = ntohl(fp->fa.fa_size.val[1]);
750199767f8SToomas Soome
751199767f8SToomas Soome return (0);
752199767f8SToomas Soome }
753199767f8SToomas Soome
754199767f8SToomas Soome static int
nfs_readdir(struct open_file * f,struct dirent * d)755199767f8SToomas Soome nfs_readdir(struct open_file *f, struct dirent *d)
756199767f8SToomas Soome {
757199767f8SToomas Soome struct nfs_iodesc *fp = (struct nfs_iodesc *)f->f_fsdata;
758199767f8SToomas Soome struct nfsv3_readdir_repl *repl;
759199767f8SToomas Soome struct nfsv3_readdir_entry *rent;
760859472daSToomas Soome static void *pkt = NULL;
761199767f8SToomas Soome static char *buf;
762199767f8SToomas Soome static struct nfs_iodesc *pfp = NULL;
763199767f8SToomas Soome static uint64_t cookie = 0;
764199767f8SToomas Soome size_t cc;
765859472daSToomas Soome int pos, rc;
766199767f8SToomas Soome
767199767f8SToomas Soome struct args {
768199767f8SToomas Soome uint32_t fhsize;
769199767f8SToomas Soome uint32_t fhpluscookie[5 + NFS_V3MAXFHSIZE];
770199767f8SToomas Soome } *args;
771199767f8SToomas Soome struct {
772199767f8SToomas Soome uint32_t h[RPC_HEADER_WORDS];
773199767f8SToomas Soome struct args d;
774199767f8SToomas Soome } sdata;
775199767f8SToomas Soome
776199767f8SToomas Soome if (fp != pfp || fp->off != cookie) {
777199767f8SToomas Soome pfp = NULL;
778199767f8SToomas Soome refill:
779859472daSToomas Soome free(pkt);
780859472daSToomas Soome pkt = NULL;
781199767f8SToomas Soome args = &sdata.d;
782734b3a42SToomas Soome bzero(args, sizeof (*args));
783199767f8SToomas Soome
784199767f8SToomas Soome args->fhsize = htonl(fp->fhsize);
785199767f8SToomas Soome bcopy(fp->fh, args->fhpluscookie, fp->fhsize);
786734b3a42SToomas Soome pos = roundup(fp->fhsize,
787734b3a42SToomas Soome sizeof (uint32_t)) / sizeof (uint32_t);
788199767f8SToomas Soome args->fhpluscookie[pos++] = htonl(fp->off >> 32);
789199767f8SToomas Soome args->fhpluscookie[pos++] = htonl(fp->off);
790199767f8SToomas Soome args->fhpluscookie[pos++] = htonl(fp->cookie >> 32);
791199767f8SToomas Soome args->fhpluscookie[pos++] = htonl(fp->cookie);
792199767f8SToomas Soome args->fhpluscookie[pos] = htonl(NFS_READDIRSIZE);
793199767f8SToomas Soome
794199767f8SToomas Soome cc = rpc_call(fp->iodesc, NFS_PROG, NFS_VER3, NFSPROCV3_READDIR,
795734b3a42SToomas Soome args, 6 * sizeof (uint32_t) +
796734b3a42SToomas Soome roundup(fp->fhsize, sizeof (uint32_t)),
797859472daSToomas Soome (void **)&buf, &pkt);
798859472daSToomas Soome if (cc == -1) {
799859472daSToomas Soome rc = errno;
800859472daSToomas Soome goto err;
801859472daSToomas Soome }
802199767f8SToomas Soome repl = (struct nfsv3_readdir_repl *)buf;
803859472daSToomas Soome if (repl->errno != 0) {
804859472daSToomas Soome rc = ntohl(repl->errno);
805859472daSToomas Soome goto err;
806859472daSToomas Soome }
807199767f8SToomas Soome pfp = fp;
808199767f8SToomas Soome cookie = fp->off;
809199767f8SToomas Soome fp->cookie = ((uint64_t)ntohl(repl->cookiev0) << 32) |
810199767f8SToomas Soome ntohl(repl->cookiev1);
811199767f8SToomas Soome buf += sizeof (struct nfsv3_readdir_repl);
812199767f8SToomas Soome }
813199767f8SToomas Soome rent = (struct nfsv3_readdir_entry *)buf;
814199767f8SToomas Soome
815199767f8SToomas Soome if (rent->follows == 0) {
816199767f8SToomas Soome /* fid0 is actually eof */
817199767f8SToomas Soome if (rent->fid0 != 0) {
818859472daSToomas Soome rc = ENOENT;
819859472daSToomas Soome goto err;
820199767f8SToomas Soome }
821199767f8SToomas Soome goto refill;
822199767f8SToomas Soome }
823199767f8SToomas Soome
824199767f8SToomas Soome d->d_namlen = ntohl(rent->len);
825199767f8SToomas Soome bcopy(rent->nameplus, d->d_name, d->d_namlen);
826199767f8SToomas Soome d->d_name[d->d_namlen] = '\0';
827199767f8SToomas Soome
828734b3a42SToomas Soome pos = roundup(d->d_namlen, sizeof (uint32_t)) / sizeof (uint32_t);
829199767f8SToomas Soome fp->off = cookie = ((uint64_t)ntohl(rent->nameplus[pos]) << 32) |
830199767f8SToomas Soome ntohl(rent->nameplus[pos + 1]);
831199767f8SToomas Soome pos += 2;
83238005992SToomas Soome buf = (char *)&rent->nameplus[pos];
833199767f8SToomas Soome return (0);
834859472daSToomas Soome
835859472daSToomas Soome err:
836859472daSToomas Soome free(pkt);
837859472daSToomas Soome pkt = NULL;
838859472daSToomas Soome pfp = NULL;
839859472daSToomas Soome cookie = 0;
840859472daSToomas Soome return (rc);
841199767f8SToomas Soome }
842