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