]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /* |
3 | * linux/fs/nfs/nfs2xdr.c | |
4 | * | |
5 | * XDR functions to encode/decode NFS RPC arguments and results. | |
6 | * | |
7 | * Copyright (C) 1992, 1993, 1994 Rick Sladkey | |
8 | * Copyright (C) 1996 Olaf Kirch | |
9 | * 04 Aug 1998 Ion Badulescu <[email protected]> | |
10 | * FIFO's need special handling in NFSv2 | |
11 | */ | |
12 | ||
13 | #include <linux/param.h> | |
14 | #include <linux/time.h> | |
15 | #include <linux/mm.h> | |
1da177e4 LT |
16 | #include <linux/errno.h> |
17 | #include <linux/string.h> | |
18 | #include <linux/in.h> | |
19 | #include <linux/pagemap.h> | |
20 | #include <linux/proc_fs.h> | |
21 | #include <linux/sunrpc/clnt.h> | |
22 | #include <linux/nfs.h> | |
23 | #include <linux/nfs2.h> | |
24 | #include <linux/nfs_fs.h> | |
4806ded4 | 25 | #include <linux/nfs_common.h> |
f23f6584 | 26 | #include "nfstrace.h" |
816724e6 | 27 | #include "internal.h" |
1da177e4 LT |
28 | |
29 | #define NFSDBG_FACILITY NFSDBG_XDR | |
1da177e4 | 30 | |
1da177e4 LT |
31 | /* |
32 | * Declare the space requirements for NFS arguments and replies as | |
33 | * number of 32bit-words | |
34 | */ | |
9ed5af26 | 35 | #define NFS_pagepad_sz (1) /* Page padding */ |
1da177e4 LT |
36 | #define NFS_fhandle_sz (8) |
37 | #define NFS_sattr_sz (8) | |
38 | #define NFS_filename_sz (1+(NFS2_MAXNAMLEN>>2)) | |
39 | #define NFS_path_sz (1+(NFS2_MAXPATHLEN>>2)) | |
40 | #define NFS_fattr_sz (17) | |
41 | #define NFS_info_sz (5) | |
42 | #define NFS_entry_sz (NFS_filename_sz+3) | |
43 | ||
44 | #define NFS_diropargs_sz (NFS_fhandle_sz+NFS_filename_sz) | |
4fdc17b2 | 45 | #define NFS_removeargs_sz (NFS_fhandle_sz+NFS_filename_sz) |
1da177e4 LT |
46 | #define NFS_sattrargs_sz (NFS_fhandle_sz+NFS_sattr_sz) |
47 | #define NFS_readlinkargs_sz (NFS_fhandle_sz) | |
48 | #define NFS_readargs_sz (NFS_fhandle_sz+3) | |
49 | #define NFS_writeargs_sz (NFS_fhandle_sz+4) | |
50 | #define NFS_createargs_sz (NFS_diropargs_sz+NFS_sattr_sz) | |
51 | #define NFS_renameargs_sz (NFS_diropargs_sz+NFS_diropargs_sz) | |
52 | #define NFS_linkargs_sz (NFS_fhandle_sz+NFS_diropargs_sz) | |
94a6d753 | 53 | #define NFS_symlinkargs_sz (NFS_diropargs_sz+1+NFS_sattr_sz) |
1da177e4 LT |
54 | #define NFS_readdirargs_sz (NFS_fhandle_sz+2) |
55 | ||
56 | #define NFS_attrstat_sz (1+NFS_fattr_sz) | |
57 | #define NFS_diropres_sz (1+NFS_fhandle_sz+NFS_fattr_sz) | |
9ed5af26 TM |
58 | #define NFS_readlinkres_sz (2+NFS_pagepad_sz) |
59 | #define NFS_readres_sz (1+NFS_fattr_sz+1+NFS_pagepad_sz) | |
1da177e4 LT |
60 | #define NFS_writeres_sz (NFS_attrstat_sz) |
61 | #define NFS_stat_sz (1) | |
9ed5af26 | 62 | #define NFS_readdirres_sz (1+NFS_pagepad_sz) |
1da177e4 LT |
63 | #define NFS_statfsres_sz (1+NFS_info_sz) |
64 | ||
25a0866c CL |
65 | /* |
66 | * Encode/decode NFSv2 basic data types | |
67 | * | |
68 | * Basic NFSv2 data types are defined in section 2.3 of RFC 1094: | |
69 | * "NFS: Network File System Protocol Specification". | |
70 | * | |
71 | * Not all basic data types have their own encoding and decoding | |
72 | * functions. For run-time efficiency, some data types are encoded | |
73 | * or decoded inline. | |
74 | */ | |
75 | ||
c207db2f TM |
76 | static struct user_namespace *rpc_userns(const struct rpc_clnt *clnt) |
77 | { | |
78 | if (clnt && clnt->cl_cred) | |
79 | return clnt->cl_cred->user_ns; | |
80 | return &init_user_ns; | |
81 | } | |
82 | ||
83 | static struct user_namespace *rpc_rqst_userns(const struct rpc_rqst *rqstp) | |
84 | { | |
85 | if (rqstp->rq_task) | |
86 | return rpc_userns(rqstp->rq_task->tk_client); | |
87 | return &init_user_ns; | |
88 | } | |
89 | ||
f796f8b3 CL |
90 | /* |
91 | * typedef opaque nfsdata<>; | |
92 | */ | |
9137bdf3 | 93 | static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_pgio_res *result) |
f796f8b3 CL |
94 | { |
95 | u32 recvd, count; | |
f796f8b3 CL |
96 | __be32 *p; |
97 | ||
98 | p = xdr_inline_decode(xdr, 4); | |
eb72f484 CL |
99 | if (unlikely(!p)) |
100 | return -EIO; | |
f796f8b3 | 101 | count = be32_to_cpup(p); |
64bd577e | 102 | recvd = xdr_read_pages(xdr, count); |
f796f8b3 CL |
103 | if (unlikely(count > recvd)) |
104 | goto out_cheating; | |
105 | out: | |
f796f8b3 CL |
106 | result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */ |
107 | result->count = count; | |
108 | return count; | |
109 | out_cheating: | |
110 | dprintk("NFS: server cheating in read result: " | |
111 | "count %u > recvd %u\n", count, recvd); | |
112 | count = recvd; | |
113 | goto out; | |
f796f8b3 CL |
114 | } |
115 | ||
116 | /* | |
117 | * enum stat { | |
118 | * NFS_OK = 0, | |
119 | * NFSERR_PERM = 1, | |
120 | * NFSERR_NOENT = 2, | |
121 | * NFSERR_IO = 5, | |
122 | * NFSERR_NXIO = 6, | |
123 | * NFSERR_ACCES = 13, | |
124 | * NFSERR_EXIST = 17, | |
125 | * NFSERR_NODEV = 19, | |
126 | * NFSERR_NOTDIR = 20, | |
127 | * NFSERR_ISDIR = 21, | |
128 | * NFSERR_FBIG = 27, | |
129 | * NFSERR_NOSPC = 28, | |
130 | * NFSERR_ROFS = 30, | |
131 | * NFSERR_NAMETOOLONG = 63, | |
132 | * NFSERR_NOTEMPTY = 66, | |
133 | * NFSERR_DQUOT = 69, | |
134 | * NFSERR_STALE = 70, | |
135 | * NFSERR_WFLUSH = 99 | |
136 | * }; | |
137 | */ | |
138 | static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status) | |
139 | { | |
140 | __be32 *p; | |
141 | ||
142 | p = xdr_inline_decode(xdr, 4); | |
eb72f484 CL |
143 | if (unlikely(!p)) |
144 | return -EIO; | |
f23f6584 CL |
145 | if (unlikely(*p != cpu_to_be32(NFS_OK))) |
146 | goto out_status; | |
147 | *status = 0; | |
148 | return 0; | |
149 | out_status: | |
f796f8b3 | 150 | *status = be32_to_cpup(p); |
62a92ba9 | 151 | trace_nfs_xdr_status(xdr, (int)*status); |
f796f8b3 | 152 | return 0; |
f796f8b3 CL |
153 | } |
154 | ||
5f96e5e3 CL |
155 | /* |
156 | * 2.3.2. ftype | |
157 | * | |
158 | * enum ftype { | |
159 | * NFNON = 0, | |
160 | * NFREG = 1, | |
161 | * NFDIR = 2, | |
162 | * NFBLK = 3, | |
163 | * NFCHR = 4, | |
164 | * NFLNK = 5 | |
165 | * }; | |
166 | * | |
167 | */ | |
168 | static __be32 *xdr_decode_ftype(__be32 *p, u32 *type) | |
169 | { | |
170 | *type = be32_to_cpup(p++); | |
171 | if (unlikely(*type > NF2FIFO)) | |
172 | *type = NFBAD; | |
173 | return p; | |
174 | } | |
175 | ||
25a0866c CL |
176 | /* |
177 | * 2.3.3. fhandle | |
178 | * | |
179 | * typedef opaque fhandle[FHSIZE]; | |
180 | */ | |
181 | static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh) | |
182 | { | |
183 | __be32 *p; | |
184 | ||
25a0866c CL |
185 | p = xdr_reserve_space(xdr, NFS2_FHSIZE); |
186 | memcpy(p, fh->data, NFS2_FHSIZE); | |
187 | } | |
188 | ||
f796f8b3 CL |
189 | static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh) |
190 | { | |
191 | __be32 *p; | |
192 | ||
193 | p = xdr_inline_decode(xdr, NFS2_FHSIZE); | |
eb72f484 CL |
194 | if (unlikely(!p)) |
195 | return -EIO; | |
f796f8b3 CL |
196 | fh->size = NFS2_FHSIZE; |
197 | memcpy(fh->data, p, NFS2_FHSIZE); | |
198 | return 0; | |
f796f8b3 CL |
199 | } |
200 | ||
282ac2a5 CL |
201 | /* |
202 | * 2.3.4. timeval | |
203 | * | |
204 | * struct timeval { | |
205 | * unsigned int seconds; | |
206 | * unsigned int useconds; | |
207 | * }; | |
208 | */ | |
c9dbfd96 | 209 | static __be32 *xdr_encode_time(__be32 *p, const struct timespec64 *timep) |
282ac2a5 | 210 | { |
c9dbfd96 | 211 | *p++ = cpu_to_be32((u32)timep->tv_sec); |
282ac2a5 CL |
212 | if (timep->tv_nsec != 0) |
213 | *p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC); | |
214 | else | |
215 | *p++ = cpu_to_be32(0); | |
216 | return p; | |
217 | } | |
218 | ||
219 | /* | |
220 | * Passing the invalid value useconds=1000000 is a Sun convention for | |
221 | * "set to current server time". It's needed to make permissions checks | |
222 | * for the "touch" program across v2 mounts to Solaris and Irix servers | |
223 | * work correctly. See description of sattr in section 6.1 of "NFS | |
224 | * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5. | |
225 | */ | |
226 | static __be32 *xdr_encode_current_server_time(__be32 *p, | |
c9dbfd96 | 227 | const struct timespec64 *timep) |
282ac2a5 CL |
228 | { |
229 | *p++ = cpu_to_be32(timep->tv_sec); | |
230 | *p++ = cpu_to_be32(1000000); | |
231 | return p; | |
232 | } | |
233 | ||
e86d5a02 | 234 | static __be32 *xdr_decode_time(__be32 *p, struct timespec64 *timep) |
5f96e5e3 CL |
235 | { |
236 | timep->tv_sec = be32_to_cpup(p++); | |
237 | timep->tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC; | |
238 | return p; | |
239 | } | |
240 | ||
f796f8b3 CL |
241 | /* |
242 | * 2.3.5. fattr | |
243 | * | |
244 | * struct fattr { | |
245 | * ftype type; | |
246 | * unsigned int mode; | |
247 | * unsigned int nlink; | |
248 | * unsigned int uid; | |
249 | * unsigned int gid; | |
250 | * unsigned int size; | |
251 | * unsigned int blocksize; | |
252 | * unsigned int rdev; | |
253 | * unsigned int blocks; | |
254 | * unsigned int fsid; | |
255 | * unsigned int fileid; | |
256 | * timeval atime; | |
257 | * timeval mtime; | |
258 | * timeval ctime; | |
259 | * }; | |
260 | * | |
261 | */ | |
c207db2f TM |
262 | static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr, |
263 | struct user_namespace *userns) | |
f796f8b3 | 264 | { |
5f96e5e3 | 265 | u32 rdev, type; |
f796f8b3 CL |
266 | __be32 *p; |
267 | ||
268 | p = xdr_inline_decode(xdr, NFS_fattr_sz << 2); | |
eb72f484 CL |
269 | if (unlikely(!p)) |
270 | return -EIO; | |
5f96e5e3 CL |
271 | |
272 | fattr->valid |= NFS_ATTR_FATTR_V2; | |
273 | ||
274 | p = xdr_decode_ftype(p, &type); | |
275 | ||
276 | fattr->mode = be32_to_cpup(p++); | |
277 | fattr->nlink = be32_to_cpup(p++); | |
c207db2f | 278 | fattr->uid = make_kuid(userns, be32_to_cpup(p++)); |
cfa0898d EB |
279 | if (!uid_valid(fattr->uid)) |
280 | goto out_uid; | |
c207db2f | 281 | fattr->gid = make_kgid(userns, be32_to_cpup(p++)); |
cfa0898d EB |
282 | if (!gid_valid(fattr->gid)) |
283 | goto out_gid; | |
284 | ||
5f96e5e3 CL |
285 | fattr->size = be32_to_cpup(p++); |
286 | fattr->du.nfs2.blocksize = be32_to_cpup(p++); | |
287 | ||
288 | rdev = be32_to_cpup(p++); | |
289 | fattr->rdev = new_decode_dev(rdev); | |
290 | if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) { | |
291 | fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO; | |
292 | fattr->rdev = 0; | |
293 | } | |
294 | ||
295 | fattr->du.nfs2.blocks = be32_to_cpup(p++); | |
296 | fattr->fsid.major = be32_to_cpup(p++); | |
297 | fattr->fsid.minor = 0; | |
298 | fattr->fileid = be32_to_cpup(p++); | |
299 | ||
300 | p = xdr_decode_time(p, &fattr->atime); | |
301 | p = xdr_decode_time(p, &fattr->mtime); | |
302 | xdr_decode_time(p, &fattr->ctime); | |
3a1556e8 TM |
303 | fattr->change_attr = nfs_timespec_to_change_attr(&fattr->ctime); |
304 | ||
f796f8b3 | 305 | return 0; |
cfa0898d EB |
306 | out_uid: |
307 | dprintk("NFS: returned invalid uid\n"); | |
308 | return -EINVAL; | |
309 | out_gid: | |
310 | dprintk("NFS: returned invalid gid\n"); | |
311 | return -EINVAL; | |
f796f8b3 CL |
312 | } |
313 | ||
25a0866c CL |
314 | /* |
315 | * 2.3.6. sattr | |
316 | * | |
317 | * struct sattr { | |
318 | * unsigned int mode; | |
319 | * unsigned int uid; | |
320 | * unsigned int gid; | |
321 | * unsigned int size; | |
322 | * timeval atime; | |
323 | * timeval mtime; | |
324 | * }; | |
325 | */ | |
326 | ||
327 | #define NFS2_SATTR_NOT_SET (0xffffffff) | |
328 | ||
329 | static __be32 *xdr_time_not_set(__be32 *p) | |
330 | { | |
331 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | |
332 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | |
333 | return p; | |
334 | } | |
335 | ||
c207db2f TM |
336 | static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr, |
337 | struct user_namespace *userns) | |
25a0866c CL |
338 | { |
339 | __be32 *p; | |
340 | ||
341 | p = xdr_reserve_space(xdr, NFS_sattr_sz << 2); | |
342 | ||
343 | if (attr->ia_valid & ATTR_MODE) | |
344 | *p++ = cpu_to_be32(attr->ia_mode); | |
345 | else | |
346 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | |
347 | if (attr->ia_valid & ATTR_UID) | |
c207db2f | 348 | *p++ = cpu_to_be32(from_kuid_munged(userns, attr->ia_uid)); |
25a0866c CL |
349 | else |
350 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | |
351 | if (attr->ia_valid & ATTR_GID) | |
c207db2f | 352 | *p++ = cpu_to_be32(from_kgid_munged(userns, attr->ia_gid)); |
25a0866c CL |
353 | else |
354 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | |
355 | if (attr->ia_valid & ATTR_SIZE) | |
356 | *p++ = cpu_to_be32((u32)attr->ia_size); | |
357 | else | |
358 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | |
359 | ||
e5189e9a | 360 | if (attr->ia_valid & ATTR_ATIME_SET) |
c9dbfd96 | 361 | p = xdr_encode_time(p, &attr->ia_atime); |
e5189e9a | 362 | else if (attr->ia_valid & ATTR_ATIME) |
c9dbfd96 | 363 | p = xdr_encode_current_server_time(p, &attr->ia_atime); |
e5189e9a | 364 | else |
25a0866c | 365 | p = xdr_time_not_set(p); |
e5189e9a | 366 | if (attr->ia_valid & ATTR_MTIME_SET) |
c9dbfd96 | 367 | xdr_encode_time(p, &attr->ia_mtime); |
e5189e9a | 368 | else if (attr->ia_valid & ATTR_MTIME) |
c9dbfd96 | 369 | xdr_encode_current_server_time(p, &attr->ia_mtime); |
e5189e9a | 370 | else |
25a0866c CL |
371 | xdr_time_not_set(p); |
372 | } | |
373 | ||
374 | /* | |
375 | * 2.3.7. filename | |
376 | * | |
377 | * typedef string filename<MAXNAMLEN>; | |
378 | */ | |
379 | static void encode_filename(struct xdr_stream *xdr, | |
380 | const char *name, u32 length) | |
381 | { | |
382 | __be32 *p; | |
383 | ||
7fc38846 | 384 | WARN_ON_ONCE(length > NFS2_MAXNAMLEN); |
25a0866c CL |
385 | p = xdr_reserve_space(xdr, 4 + length); |
386 | xdr_encode_opaque(p, name, length); | |
387 | } | |
388 | ||
f796f8b3 CL |
389 | static int decode_filename_inline(struct xdr_stream *xdr, |
390 | const char **name, u32 *length) | |
391 | { | |
392 | __be32 *p; | |
393 | u32 count; | |
394 | ||
395 | p = xdr_inline_decode(xdr, 4); | |
eb72f484 CL |
396 | if (unlikely(!p)) |
397 | return -EIO; | |
f796f8b3 CL |
398 | count = be32_to_cpup(p); |
399 | if (count > NFS3_MAXNAMLEN) | |
400 | goto out_nametoolong; | |
401 | p = xdr_inline_decode(xdr, count); | |
eb72f484 CL |
402 | if (unlikely(!p)) |
403 | return -EIO; | |
f796f8b3 CL |
404 | *name = (const char *)p; |
405 | *length = count; | |
406 | return 0; | |
407 | out_nametoolong: | |
408 | dprintk("NFS: returned filename too long: %u\n", count); | |
409 | return -ENAMETOOLONG; | |
f796f8b3 CL |
410 | } |
411 | ||
25a0866c CL |
412 | /* |
413 | * 2.3.8. path | |
414 | * | |
415 | * typedef string path<MAXPATHLEN>; | |
416 | */ | |
417 | static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length) | |
418 | { | |
419 | __be32 *p; | |
420 | ||
25a0866c CL |
421 | p = xdr_reserve_space(xdr, 4); |
422 | *p = cpu_to_be32(length); | |
423 | xdr_write_pages(xdr, pages, 0, length); | |
424 | } | |
425 | ||
f796f8b3 CL |
426 | static int decode_path(struct xdr_stream *xdr) |
427 | { | |
428 | u32 length, recvd; | |
f796f8b3 CL |
429 | __be32 *p; |
430 | ||
431 | p = xdr_inline_decode(xdr, 4); | |
eb72f484 CL |
432 | if (unlikely(!p)) |
433 | return -EIO; | |
f796f8b3 CL |
434 | length = be32_to_cpup(p); |
435 | if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN)) | |
436 | goto out_size; | |
64bd577e | 437 | recvd = xdr_read_pages(xdr, length); |
f796f8b3 CL |
438 | if (unlikely(length > recvd)) |
439 | goto out_cheating; | |
f796f8b3 CL |
440 | xdr_terminate_string(xdr->buf, length); |
441 | return 0; | |
442 | out_size: | |
443 | dprintk("NFS: returned pathname too long: %u\n", length); | |
444 | return -ENAMETOOLONG; | |
445 | out_cheating: | |
446 | dprintk("NFS: server cheating in pathname result: " | |
447 | "length %u > received %u\n", length, recvd); | |
448 | return -EIO; | |
f796f8b3 CL |
449 | } |
450 | ||
451 | /* | |
452 | * 2.3.9. attrstat | |
453 | * | |
454 | * union attrstat switch (stat status) { | |
455 | * case NFS_OK: | |
456 | * fattr attributes; | |
457 | * default: | |
458 | * void; | |
459 | * }; | |
460 | */ | |
aabff4dd | 461 | static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result, |
c207db2f TM |
462 | __u32 *op_status, |
463 | struct user_namespace *userns) | |
f796f8b3 CL |
464 | { |
465 | enum nfs_stat status; | |
466 | int error; | |
467 | ||
468 | error = decode_stat(xdr, &status); | |
469 | if (unlikely(error)) | |
470 | goto out; | |
aabff4dd PT |
471 | if (op_status) |
472 | *op_status = status; | |
f796f8b3 CL |
473 | if (status != NFS_OK) |
474 | goto out_default; | |
c207db2f | 475 | error = decode_fattr(xdr, result, userns); |
f796f8b3 CL |
476 | out: |
477 | return error; | |
478 | out_default: | |
479 | return nfs_stat_to_errno(status); | |
480 | } | |
481 | ||
25a0866c CL |
482 | /* |
483 | * 2.3.10. diropargs | |
484 | * | |
485 | * struct diropargs { | |
486 | * fhandle dir; | |
487 | * filename name; | |
488 | * }; | |
489 | */ | |
490 | static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh, | |
491 | const char *name, u32 length) | |
492 | { | |
493 | encode_fhandle(xdr, fh); | |
494 | encode_filename(xdr, name, length); | |
495 | } | |
496 | ||
f796f8b3 CL |
497 | /* |
498 | * 2.3.11. diropres | |
499 | * | |
500 | * union diropres switch (stat status) { | |
501 | * case NFS_OK: | |
502 | * struct { | |
503 | * fhandle file; | |
504 | * fattr attributes; | |
505 | * } diropok; | |
506 | * default: | |
507 | * void; | |
508 | * }; | |
509 | */ | |
c207db2f TM |
510 | static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result, |
511 | struct user_namespace *userns) | |
f796f8b3 CL |
512 | { |
513 | int error; | |
514 | ||
515 | error = decode_fhandle(xdr, result->fh); | |
516 | if (unlikely(error)) | |
517 | goto out; | |
c207db2f | 518 | error = decode_fattr(xdr, result->fattr, userns); |
f796f8b3 CL |
519 | out: |
520 | return error; | |
521 | } | |
522 | ||
c207db2f TM |
523 | static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result, |
524 | struct user_namespace *userns) | |
f796f8b3 CL |
525 | { |
526 | enum nfs_stat status; | |
527 | int error; | |
528 | ||
529 | error = decode_stat(xdr, &status); | |
530 | if (unlikely(error)) | |
531 | goto out; | |
532 | if (status != NFS_OK) | |
533 | goto out_default; | |
c207db2f | 534 | error = decode_diropok(xdr, result, userns); |
f796f8b3 CL |
535 | out: |
536 | return error; | |
537 | out_default: | |
538 | return nfs_stat_to_errno(status); | |
539 | } | |
540 | ||
25a0866c | 541 | |
1da177e4 | 542 | /* |
2d70f533 CL |
543 | * NFSv2 XDR encode functions |
544 | * | |
545 | * NFSv2 argument types are defined in section 2.2 of RFC 1094: | |
546 | * "NFS: Network File System Protocol Specification". | |
1da177e4 | 547 | */ |
1da177e4 | 548 | |
9f06c719 CL |
549 | static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req, |
550 | struct xdr_stream *xdr, | |
fcc85819 | 551 | const void *data) |
25a0866c | 552 | { |
fcc85819 CH |
553 | const struct nfs_fh *fh = data; |
554 | ||
9f06c719 | 555 | encode_fhandle(xdr, fh); |
25a0866c CL |
556 | } |
557 | ||
25a0866c CL |
558 | /* |
559 | * 2.2.3. sattrargs | |
560 | * | |
561 | * struct sattrargs { | |
562 | * fhandle file; | |
563 | * sattr attributes; | |
564 | * }; | |
565 | */ | |
9f06c719 CL |
566 | static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req, |
567 | struct xdr_stream *xdr, | |
fcc85819 | 568 | const void *data) |
25a0866c | 569 | { |
fcc85819 CH |
570 | const struct nfs_sattrargs *args = data; |
571 | ||
9f06c719 | 572 | encode_fhandle(xdr, args->fh); |
c207db2f | 573 | encode_sattr(xdr, args->sattr, rpc_rqst_userns(req)); |
25a0866c CL |
574 | } |
575 | ||
9f06c719 CL |
576 | static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req, |
577 | struct xdr_stream *xdr, | |
fcc85819 | 578 | const void *data) |
25a0866c | 579 | { |
fcc85819 CH |
580 | const struct nfs_diropargs *args = data; |
581 | ||
9f06c719 | 582 | encode_diropargs(xdr, args->fh, args->name, args->len); |
25a0866c CL |
583 | } |
584 | ||
9f06c719 CL |
585 | static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req, |
586 | struct xdr_stream *xdr, | |
fcc85819 | 587 | const void *data) |
25a0866c | 588 | { |
fcc85819 CH |
589 | const struct nfs_readlinkargs *args = data; |
590 | ||
9f06c719 | 591 | encode_fhandle(xdr, args->fh); |
9ed5af26 TM |
592 | rpc_prepare_reply_pages(req, args->pages, args->pgbase, args->pglen, |
593 | NFS_readlinkres_sz - NFS_pagepad_sz); | |
25a0866c CL |
594 | } |
595 | ||
25a0866c CL |
596 | /* |
597 | * 2.2.7. readargs | |
598 | * | |
599 | * struct readargs { | |
600 | * fhandle file; | |
601 | * unsigned offset; | |
602 | * unsigned count; | |
603 | * unsigned totalcount; | |
604 | * }; | |
605 | */ | |
606 | static void encode_readargs(struct xdr_stream *xdr, | |
3c6b899c | 607 | const struct nfs_pgio_args *args) |
25a0866c CL |
608 | { |
609 | u32 offset = args->offset; | |
610 | u32 count = args->count; | |
611 | __be32 *p; | |
612 | ||
613 | encode_fhandle(xdr, args->fh); | |
614 | ||
615 | p = xdr_reserve_space(xdr, 4 + 4 + 4); | |
616 | *p++ = cpu_to_be32(offset); | |
617 | *p++ = cpu_to_be32(count); | |
618 | *p = cpu_to_be32(count); | |
619 | } | |
620 | ||
9f06c719 CL |
621 | static void nfs2_xdr_enc_readargs(struct rpc_rqst *req, |
622 | struct xdr_stream *xdr, | |
fcc85819 | 623 | const void *data) |
25a0866c | 624 | { |
fcc85819 CH |
625 | const struct nfs_pgio_args *args = data; |
626 | ||
9f06c719 | 627 | encode_readargs(xdr, args); |
9ed5af26 TM |
628 | rpc_prepare_reply_pages(req, args->pages, args->pgbase, args->count, |
629 | NFS_readres_sz - NFS_pagepad_sz); | |
25a0866c | 630 | req->rq_rcv_buf.flags |= XDRBUF_READ; |
25a0866c CL |
631 | } |
632 | ||
25a0866c CL |
633 | /* |
634 | * 2.2.9. writeargs | |
635 | * | |
636 | * struct writeargs { | |
637 | * fhandle file; | |
638 | * unsigned beginoffset; | |
639 | * unsigned offset; | |
640 | * unsigned totalcount; | |
641 | * nfsdata data; | |
642 | * }; | |
643 | */ | |
644 | static void encode_writeargs(struct xdr_stream *xdr, | |
3c6b899c | 645 | const struct nfs_pgio_args *args) |
25a0866c CL |
646 | { |
647 | u32 offset = args->offset; | |
648 | u32 count = args->count; | |
649 | __be32 *p; | |
650 | ||
651 | encode_fhandle(xdr, args->fh); | |
652 | ||
653 | p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4); | |
654 | *p++ = cpu_to_be32(offset); | |
655 | *p++ = cpu_to_be32(offset); | |
656 | *p++ = cpu_to_be32(count); | |
657 | ||
658 | /* nfsdata */ | |
659 | *p = cpu_to_be32(count); | |
660 | xdr_write_pages(xdr, args->pages, args->pgbase, count); | |
661 | } | |
662 | ||
9f06c719 CL |
663 | static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req, |
664 | struct xdr_stream *xdr, | |
fcc85819 | 665 | const void *data) |
25a0866c | 666 | { |
fcc85819 CH |
667 | const struct nfs_pgio_args *args = data; |
668 | ||
9f06c719 CL |
669 | encode_writeargs(xdr, args); |
670 | xdr->buf->flags |= XDRBUF_WRITE; | |
25a0866c CL |
671 | } |
672 | ||
25a0866c CL |
673 | /* |
674 | * 2.2.10. createargs | |
675 | * | |
676 | * struct createargs { | |
677 | * diropargs where; | |
678 | * sattr attributes; | |
679 | * }; | |
680 | */ | |
9f06c719 CL |
681 | static void nfs2_xdr_enc_createargs(struct rpc_rqst *req, |
682 | struct xdr_stream *xdr, | |
fcc85819 | 683 | const void *data) |
25a0866c | 684 | { |
fcc85819 CH |
685 | const struct nfs_createargs *args = data; |
686 | ||
9f06c719 | 687 | encode_diropargs(xdr, args->fh, args->name, args->len); |
c207db2f | 688 | encode_sattr(xdr, args->sattr, rpc_rqst_userns(req)); |
25a0866c CL |
689 | } |
690 | ||
9f06c719 CL |
691 | static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req, |
692 | struct xdr_stream *xdr, | |
fcc85819 | 693 | const void *data) |
25a0866c | 694 | { |
fcc85819 CH |
695 | const struct nfs_removeargs *args = data; |
696 | ||
9f06c719 | 697 | encode_diropargs(xdr, args->fh, args->name.name, args->name.len); |
25a0866c CL |
698 | } |
699 | ||
25a0866c CL |
700 | /* |
701 | * 2.2.12. renameargs | |
702 | * | |
703 | * struct renameargs { | |
704 | * diropargs from; | |
705 | * diropargs to; | |
706 | * }; | |
707 | */ | |
9f06c719 CL |
708 | static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req, |
709 | struct xdr_stream *xdr, | |
fcc85819 | 710 | const void *data) |
25a0866c | 711 | { |
fcc85819 | 712 | const struct nfs_renameargs *args = data; |
25a0866c CL |
713 | const struct qstr *old = args->old_name; |
714 | const struct qstr *new = args->new_name; | |
25a0866c | 715 | |
9f06c719 CL |
716 | encode_diropargs(xdr, args->old_dir, old->name, old->len); |
717 | encode_diropargs(xdr, args->new_dir, new->name, new->len); | |
25a0866c CL |
718 | } |
719 | ||
25a0866c CL |
720 | /* |
721 | * 2.2.13. linkargs | |
722 | * | |
723 | * struct linkargs { | |
724 | * fhandle from; | |
725 | * diropargs to; | |
726 | * }; | |
727 | */ | |
9f06c719 CL |
728 | static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req, |
729 | struct xdr_stream *xdr, | |
fcc85819 | 730 | const void *data) |
25a0866c | 731 | { |
fcc85819 CH |
732 | const struct nfs_linkargs *args = data; |
733 | ||
9f06c719 CL |
734 | encode_fhandle(xdr, args->fromfh); |
735 | encode_diropargs(xdr, args->tofh, args->toname, args->tolen); | |
25a0866c CL |
736 | } |
737 | ||
25a0866c CL |
738 | /* |
739 | * 2.2.14. symlinkargs | |
740 | * | |
741 | * struct symlinkargs { | |
742 | * diropargs from; | |
743 | * path to; | |
744 | * sattr attributes; | |
745 | * }; | |
746 | */ | |
9f06c719 CL |
747 | static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req, |
748 | struct xdr_stream *xdr, | |
fcc85819 | 749 | const void *data) |
25a0866c | 750 | { |
fcc85819 CH |
751 | const struct nfs_symlinkargs *args = data; |
752 | ||
9f06c719 CL |
753 | encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen); |
754 | encode_path(xdr, args->pages, args->pathlen); | |
c207db2f | 755 | encode_sattr(xdr, args->sattr, rpc_rqst_userns(req)); |
25a0866c CL |
756 | } |
757 | ||
25a0866c CL |
758 | /* |
759 | * 2.2.17. readdirargs | |
760 | * | |
761 | * struct readdirargs { | |
762 | * fhandle dir; | |
763 | * nfscookie cookie; | |
764 | * unsigned count; | |
765 | * }; | |
766 | */ | |
767 | static void encode_readdirargs(struct xdr_stream *xdr, | |
768 | const struct nfs_readdirargs *args) | |
769 | { | |
770 | __be32 *p; | |
771 | ||
772 | encode_fhandle(xdr, args->fh); | |
773 | ||
774 | p = xdr_reserve_space(xdr, 4 + 4); | |
775 | *p++ = cpu_to_be32(args->cookie); | |
776 | *p = cpu_to_be32(args->count); | |
777 | } | |
778 | ||
9f06c719 CL |
779 | static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req, |
780 | struct xdr_stream *xdr, | |
fcc85819 | 781 | const void *data) |
25a0866c | 782 | { |
fcc85819 CH |
783 | const struct nfs_readdirargs *args = data; |
784 | ||
9f06c719 | 785 | encode_readdirargs(xdr, args); |
9ed5af26 TM |
786 | rpc_prepare_reply_pages(req, args->pages, 0, args->count, |
787 | NFS_readdirres_sz - NFS_pagepad_sz); | |
25a0866c CL |
788 | } |
789 | ||
1da177e4 | 790 | /* |
661ad423 CL |
791 | * NFSv2 XDR decode functions |
792 | * | |
793 | * NFSv2 result types are defined in section 2.2 of RFC 1094: | |
794 | * "NFS: Network File System Protocol Specification". | |
1da177e4 | 795 | */ |
1da177e4 | 796 | |
bf269551 | 797 | static int nfs2_xdr_dec_stat(struct rpc_rqst *req, struct xdr_stream *xdr, |
f796f8b3 CL |
798 | void *__unused) |
799 | { | |
f796f8b3 CL |
800 | enum nfs_stat status; |
801 | int error; | |
802 | ||
bf269551 | 803 | error = decode_stat(xdr, &status); |
f796f8b3 CL |
804 | if (unlikely(error)) |
805 | goto out; | |
806 | if (status != NFS_OK) | |
807 | goto out_default; | |
808 | out: | |
809 | return error; | |
810 | out_default: | |
811 | return nfs_stat_to_errno(status); | |
812 | } | |
813 | ||
bf269551 | 814 | static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr, |
fc016483 | 815 | void *result) |
f796f8b3 | 816 | { |
c207db2f | 817 | return decode_attrstat(xdr, result, NULL, rpc_rqst_userns(req)); |
f796f8b3 CL |
818 | } |
819 | ||
bf269551 | 820 | static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr, |
fc016483 | 821 | void *result) |
f796f8b3 | 822 | { |
c207db2f | 823 | return decode_diropres(xdr, result, rpc_rqst_userns(req)); |
f796f8b3 CL |
824 | } |
825 | ||
f796f8b3 CL |
826 | /* |
827 | * 2.2.6. readlinkres | |
828 | * | |
829 | * union readlinkres switch (stat status) { | |
830 | * case NFS_OK: | |
831 | * path data; | |
832 | * default: | |
833 | * void; | |
834 | * }; | |
835 | */ | |
bf269551 CL |
836 | static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req, |
837 | struct xdr_stream *xdr, void *__unused) | |
f796f8b3 | 838 | { |
f796f8b3 CL |
839 | enum nfs_stat status; |
840 | int error; | |
841 | ||
bf269551 | 842 | error = decode_stat(xdr, &status); |
f796f8b3 CL |
843 | if (unlikely(error)) |
844 | goto out; | |
845 | if (status != NFS_OK) | |
846 | goto out_default; | |
bf269551 | 847 | error = decode_path(xdr); |
f796f8b3 CL |
848 | out: |
849 | return error; | |
850 | out_default: | |
851 | return nfs_stat_to_errno(status); | |
852 | } | |
853 | ||
854 | /* | |
855 | * 2.2.7. readres | |
856 | * | |
857 | * union readres switch (stat status) { | |
858 | * case NFS_OK: | |
859 | * fattr attributes; | |
860 | * nfsdata data; | |
861 | * default: | |
862 | * void; | |
863 | * }; | |
864 | */ | |
bf269551 | 865 | static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr, |
fc016483 | 866 | void *data) |
f796f8b3 | 867 | { |
fc016483 | 868 | struct nfs_pgio_res *result = data; |
f796f8b3 CL |
869 | enum nfs_stat status; |
870 | int error; | |
871 | ||
bf269551 | 872 | error = decode_stat(xdr, &status); |
f796f8b3 CL |
873 | if (unlikely(error)) |
874 | goto out; | |
aabff4dd | 875 | result->op_status = status; |
f796f8b3 CL |
876 | if (status != NFS_OK) |
877 | goto out_default; | |
c207db2f | 878 | error = decode_fattr(xdr, result->fattr, rpc_rqst_userns(req)); |
f796f8b3 CL |
879 | if (unlikely(error)) |
880 | goto out; | |
bf269551 | 881 | error = decode_nfsdata(xdr, result); |
f796f8b3 CL |
882 | out: |
883 | return error; | |
884 | out_default: | |
885 | return nfs_stat_to_errno(status); | |
886 | } | |
887 | ||
bf269551 | 888 | static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr, |
fc016483 | 889 | void *data) |
f796f8b3 | 890 | { |
fc016483 CH |
891 | struct nfs_pgio_res *result = data; |
892 | ||
f796f8b3 CL |
893 | /* All NFSv2 writes are "file sync" writes */ |
894 | result->verf->committed = NFS_FILE_SYNC; | |
c207db2f TM |
895 | return decode_attrstat(xdr, result->fattr, &result->op_status, |
896 | rpc_rqst_userns(req)); | |
f796f8b3 CL |
897 | } |
898 | ||
899 | /** | |
900 | * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in | |
901 | * the local page cache. | |
902 | * @xdr: XDR stream where entry resides | |
903 | * @entry: buffer to fill in with entry data | |
f796f8b3 CL |
904 | * @plus: boolean indicating whether this should be a readdirplus entry |
905 | * | |
573c4e1e CL |
906 | * Returns zero if successful, otherwise a negative errno value is |
907 | * returned. | |
f796f8b3 CL |
908 | * |
909 | * This function is not invoked during READDIR reply decoding, but | |
910 | * rather whenever an application invokes the getdents(2) system call | |
911 | * on a directory already in our cache. | |
912 | * | |
913 | * 2.2.17. entry | |
914 | * | |
915 | * struct entry { | |
916 | * unsigned fileid; | |
917 | * filename name; | |
918 | * nfscookie cookie; | |
919 | * entry *nextentry; | |
920 | * }; | |
921 | */ | |
573c4e1e | 922 | int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, |
a7a3b1e9 | 923 | bool plus) |
f796f8b3 CL |
924 | { |
925 | __be32 *p; | |
926 | int error; | |
927 | ||
928 | p = xdr_inline_decode(xdr, 4); | |
eb72f484 CL |
929 | if (unlikely(!p)) |
930 | return -EAGAIN; | |
f796f8b3 CL |
931 | if (*p++ == xdr_zero) { |
932 | p = xdr_inline_decode(xdr, 4); | |
eb72f484 CL |
933 | if (unlikely(!p)) |
934 | return -EAGAIN; | |
f796f8b3 | 935 | if (*p++ == xdr_zero) |
573c4e1e | 936 | return -EAGAIN; |
f796f8b3 | 937 | entry->eof = 1; |
573c4e1e | 938 | return -EBADCOOKIE; |
f796f8b3 CL |
939 | } |
940 | ||
941 | p = xdr_inline_decode(xdr, 4); | |
eb72f484 CL |
942 | if (unlikely(!p)) |
943 | return -EAGAIN; | |
f796f8b3 CL |
944 | entry->ino = be32_to_cpup(p); |
945 | ||
946 | error = decode_filename_inline(xdr, &entry->name, &entry->len); | |
947 | if (unlikely(error)) | |
f67b55b6 | 948 | return error == -ENAMETOOLONG ? -ENAMETOOLONG : -EAGAIN; |
f796f8b3 CL |
949 | |
950 | /* | |
951 | * The type (size and byte order) of nfscookie isn't defined in | |
952 | * RFC 1094. This implementation assumes that it's an XDR uint32. | |
953 | */ | |
f796f8b3 | 954 | p = xdr_inline_decode(xdr, 4); |
eb72f484 CL |
955 | if (unlikely(!p)) |
956 | return -EAGAIN; | |
f796f8b3 CL |
957 | entry->cookie = be32_to_cpup(p); |
958 | ||
959 | entry->d_type = DT_UNKNOWN; | |
960 | ||
573c4e1e | 961 | return 0; |
f796f8b3 CL |
962 | } |
963 | ||
964 | /* | |
965 | * 2.2.17. readdirres | |
966 | * | |
967 | * union readdirres switch (stat status) { | |
968 | * case NFS_OK: | |
969 | * struct { | |
970 | * entry *entries; | |
971 | * bool eof; | |
972 | * } readdirok; | |
973 | * default: | |
974 | * void; | |
975 | * }; | |
976 | * | |
977 | * Read the directory contents into the page cache, but don't | |
978 | * touch them. The actual decoding is done by nfs2_decode_dirent() | |
979 | * during subsequent nfs_readdir() calls. | |
980 | */ | |
981 | static int decode_readdirok(struct xdr_stream *xdr) | |
982 | { | |
64bd577e | 983 | return xdr_read_pages(xdr, xdr->buf->page_len); |
f796f8b3 CL |
984 | } |
985 | ||
bf269551 CL |
986 | static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req, |
987 | struct xdr_stream *xdr, void *__unused) | |
f796f8b3 | 988 | { |
f796f8b3 CL |
989 | enum nfs_stat status; |
990 | int error; | |
991 | ||
bf269551 | 992 | error = decode_stat(xdr, &status); |
f796f8b3 CL |
993 | if (unlikely(error)) |
994 | goto out; | |
995 | if (status != NFS_OK) | |
996 | goto out_default; | |
bf269551 | 997 | error = decode_readdirok(xdr); |
f796f8b3 CL |
998 | out: |
999 | return error; | |
1000 | out_default: | |
1001 | return nfs_stat_to_errno(status); | |
1002 | } | |
1003 | ||
f796f8b3 CL |
1004 | /* |
1005 | * 2.2.18. statfsres | |
1006 | * | |
1007 | * union statfsres (stat status) { | |
1008 | * case NFS_OK: | |
1009 | * struct { | |
1010 | * unsigned tsize; | |
1011 | * unsigned bsize; | |
1012 | * unsigned blocks; | |
1013 | * unsigned bfree; | |
1014 | * unsigned bavail; | |
1015 | * } info; | |
1016 | * default: | |
1017 | * void; | |
1018 | * }; | |
1019 | */ | |
1020 | static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result) | |
1021 | { | |
1022 | __be32 *p; | |
1023 | ||
1024 | p = xdr_inline_decode(xdr, NFS_info_sz << 2); | |
eb72f484 CL |
1025 | if (unlikely(!p)) |
1026 | return -EIO; | |
f796f8b3 CL |
1027 | result->tsize = be32_to_cpup(p++); |
1028 | result->bsize = be32_to_cpup(p++); | |
1029 | result->blocks = be32_to_cpup(p++); | |
1030 | result->bfree = be32_to_cpup(p++); | |
1031 | result->bavail = be32_to_cpup(p); | |
1032 | return 0; | |
f796f8b3 CL |
1033 | } |
1034 | ||
bf269551 | 1035 | static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr, |
fc016483 | 1036 | void *result) |
f796f8b3 | 1037 | { |
f796f8b3 CL |
1038 | enum nfs_stat status; |
1039 | int error; | |
1040 | ||
bf269551 | 1041 | error = decode_stat(xdr, &status); |
f796f8b3 CL |
1042 | if (unlikely(error)) |
1043 | goto out; | |
1044 | if (status != NFS_OK) | |
1045 | goto out_default; | |
bf269551 | 1046 | error = decode_info(xdr, result); |
f796f8b3 CL |
1047 | out: |
1048 | return error; | |
1049 | out_default: | |
1050 | return nfs_stat_to_errno(status); | |
1051 | } | |
1052 | ||
1da177e4 LT |
1053 | #define PROC(proc, argtype, restype, timer) \ |
1054 | [NFSPROC_##proc] = { \ | |
1055 | .p_proc = NFSPROC_##proc, \ | |
fcc85819 | 1056 | .p_encode = nfs2_xdr_enc_##argtype, \ |
fc016483 | 1057 | .p_decode = nfs2_xdr_dec_##restype, \ |
2bea90d4 CL |
1058 | .p_arglen = NFS_##argtype##_sz, \ |
1059 | .p_replen = NFS_##restype##_sz, \ | |
cc0175c1 CL |
1060 | .p_timer = timer, \ |
1061 | .p_statidx = NFSPROC_##proc, \ | |
1062 | .p_name = #proc, \ | |
1da177e4 | 1063 | } |
511e936b | 1064 | const struct rpc_procinfo nfs_procedures[] = { |
7d93bd71 CL |
1065 | PROC(GETATTR, fhandle, attrstat, 1), |
1066 | PROC(SETATTR, sattrargs, attrstat, 0), | |
1067 | PROC(LOOKUP, diropargs, diropres, 2), | |
1068 | PROC(READLINK, readlinkargs, readlinkres, 3), | |
1069 | PROC(READ, readargs, readres, 3), | |
1070 | PROC(WRITE, writeargs, writeres, 4), | |
1071 | PROC(CREATE, createargs, diropres, 0), | |
1072 | PROC(REMOVE, removeargs, stat, 0), | |
1073 | PROC(RENAME, renameargs, stat, 0), | |
1074 | PROC(LINK, linkargs, stat, 0), | |
1075 | PROC(SYMLINK, symlinkargs, stat, 0), | |
1076 | PROC(MKDIR, createargs, diropres, 0), | |
1077 | PROC(RMDIR, diropargs, stat, 0), | |
1078 | PROC(READDIR, readdirargs, readdirres, 3), | |
1079 | PROC(STATFS, fhandle, statfsres, 0), | |
1da177e4 LT |
1080 | }; |
1081 | ||
c551858a | 1082 | static unsigned int nfs_version2_counts[ARRAY_SIZE(nfs_procedures)]; |
a613fa16 | 1083 | const struct rpc_version nfs_version2 = { |
1da177e4 | 1084 | .number = 2, |
e8c96f8c | 1085 | .nrprocs = ARRAY_SIZE(nfs_procedures), |
c551858a CH |
1086 | .procs = nfs_procedures, |
1087 | .counts = nfs_version2_counts, | |
1da177e4 | 1088 | }; |