diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /fs/nfs/nfs2xdr.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'fs/nfs/nfs2xdr.c')
-rw-r--r-- | fs/nfs/nfs2xdr.c | 1339 |
1 files changed, 870 insertions, 469 deletions
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index db8846a0e82e..792cb13a4304 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c | |||
@@ -61,609 +61,1008 @@ | |||
61 | #define NFS_readdirres_sz (1) | 61 | #define NFS_readdirres_sz (1) |
62 | #define NFS_statfsres_sz (1+NFS_info_sz) | 62 | #define NFS_statfsres_sz (1+NFS_info_sz) |
63 | 63 | ||
64 | |||
65 | /* | ||
66 | * While encoding arguments, set up the reply buffer in advance to | ||
67 | * receive reply data directly into the page cache. | ||
68 | */ | ||
69 | static void prepare_reply_buffer(struct rpc_rqst *req, struct page **pages, | ||
70 | unsigned int base, unsigned int len, | ||
71 | unsigned int bufsize) | ||
72 | { | ||
73 | struct rpc_auth *auth = req->rq_cred->cr_auth; | ||
74 | unsigned int replen; | ||
75 | |||
76 | replen = RPC_REPHDRSIZE + auth->au_rslack + bufsize; | ||
77 | xdr_inline_pages(&req->rq_rcv_buf, replen << 2, pages, base, len); | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * Handle decode buffer overflows out-of-line. | ||
82 | */ | ||
83 | static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) | ||
84 | { | ||
85 | dprintk("NFS: %s prematurely hit the end of our receive buffer. " | ||
86 | "Remaining buffer length is %tu words.\n", | ||
87 | func, xdr->end - xdr->p); | ||
88 | } | ||
89 | |||
90 | |||
91 | /* | ||
92 | * Encode/decode NFSv2 basic data types | ||
93 | * | ||
94 | * Basic NFSv2 data types are defined in section 2.3 of RFC 1094: | ||
95 | * "NFS: Network File System Protocol Specification". | ||
96 | * | ||
97 | * Not all basic data types have their own encoding and decoding | ||
98 | * functions. For run-time efficiency, some data types are encoded | ||
99 | * or decoded inline. | ||
100 | */ | ||
101 | |||
102 | /* | ||
103 | * typedef opaque nfsdata<>; | ||
104 | */ | ||
105 | static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result) | ||
106 | { | ||
107 | u32 recvd, count; | ||
108 | size_t hdrlen; | ||
109 | __be32 *p; | ||
110 | |||
111 | p = xdr_inline_decode(xdr, 4); | ||
112 | if (unlikely(p == NULL)) | ||
113 | goto out_overflow; | ||
114 | count = be32_to_cpup(p); | ||
115 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | ||
116 | recvd = xdr->buf->len - hdrlen; | ||
117 | if (unlikely(count > recvd)) | ||
118 | goto out_cheating; | ||
119 | out: | ||
120 | xdr_read_pages(xdr, count); | ||
121 | result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */ | ||
122 | result->count = count; | ||
123 | return count; | ||
124 | out_cheating: | ||
125 | dprintk("NFS: server cheating in read result: " | ||
126 | "count %u > recvd %u\n", count, recvd); | ||
127 | count = recvd; | ||
128 | goto out; | ||
129 | out_overflow: | ||
130 | print_overflow_msg(__func__, xdr); | ||
131 | return -EIO; | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * enum stat { | ||
136 | * NFS_OK = 0, | ||
137 | * NFSERR_PERM = 1, | ||
138 | * NFSERR_NOENT = 2, | ||
139 | * NFSERR_IO = 5, | ||
140 | * NFSERR_NXIO = 6, | ||
141 | * NFSERR_ACCES = 13, | ||
142 | * NFSERR_EXIST = 17, | ||
143 | * NFSERR_NODEV = 19, | ||
144 | * NFSERR_NOTDIR = 20, | ||
145 | * NFSERR_ISDIR = 21, | ||
146 | * NFSERR_FBIG = 27, | ||
147 | * NFSERR_NOSPC = 28, | ||
148 | * NFSERR_ROFS = 30, | ||
149 | * NFSERR_NAMETOOLONG = 63, | ||
150 | * NFSERR_NOTEMPTY = 66, | ||
151 | * NFSERR_DQUOT = 69, | ||
152 | * NFSERR_STALE = 70, | ||
153 | * NFSERR_WFLUSH = 99 | ||
154 | * }; | ||
155 | */ | ||
156 | static int decode_stat(struct xdr_stream *xdr, enum nfs_stat *status) | ||
157 | { | ||
158 | __be32 *p; | ||
159 | |||
160 | p = xdr_inline_decode(xdr, 4); | ||
161 | if (unlikely(p == NULL)) | ||
162 | goto out_overflow; | ||
163 | *status = be32_to_cpup(p); | ||
164 | return 0; | ||
165 | out_overflow: | ||
166 | print_overflow_msg(__func__, xdr); | ||
167 | return -EIO; | ||
168 | } | ||
169 | |||
64 | /* | 170 | /* |
65 | * Common NFS XDR functions as inlines | 171 | * 2.3.2. ftype |
172 | * | ||
173 | * enum ftype { | ||
174 | * NFNON = 0, | ||
175 | * NFREG = 1, | ||
176 | * NFDIR = 2, | ||
177 | * NFBLK = 3, | ||
178 | * NFCHR = 4, | ||
179 | * NFLNK = 5 | ||
180 | * }; | ||
181 | * | ||
66 | */ | 182 | */ |
67 | static inline __be32 * | 183 | static __be32 *xdr_decode_ftype(__be32 *p, u32 *type) |
68 | xdr_encode_fhandle(__be32 *p, const struct nfs_fh *fhandle) | ||
69 | { | 184 | { |
70 | memcpy(p, fhandle->data, NFS2_FHSIZE); | 185 | *type = be32_to_cpup(p++); |
71 | return p + XDR_QUADLEN(NFS2_FHSIZE); | 186 | if (unlikely(*type > NF2FIFO)) |
187 | *type = NFBAD; | ||
188 | return p; | ||
72 | } | 189 | } |
73 | 190 | ||
74 | static inline __be32 * | 191 | /* |
75 | xdr_decode_fhandle(__be32 *p, struct nfs_fh *fhandle) | 192 | * 2.3.3. fhandle |
193 | * | ||
194 | * typedef opaque fhandle[FHSIZE]; | ||
195 | */ | ||
196 | static void encode_fhandle(struct xdr_stream *xdr, const struct nfs_fh *fh) | ||
76 | { | 197 | { |
77 | /* NFSv2 handles have a fixed length */ | 198 | __be32 *p; |
78 | fhandle->size = NFS2_FHSIZE; | 199 | |
79 | memcpy(fhandle->data, p, NFS2_FHSIZE); | 200 | BUG_ON(fh->size != NFS2_FHSIZE); |
80 | return p + XDR_QUADLEN(NFS2_FHSIZE); | 201 | p = xdr_reserve_space(xdr, NFS2_FHSIZE); |
202 | memcpy(p, fh->data, NFS2_FHSIZE); | ||
81 | } | 203 | } |
82 | 204 | ||
83 | static inline __be32* | 205 | static int decode_fhandle(struct xdr_stream *xdr, struct nfs_fh *fh) |
84 | xdr_encode_time(__be32 *p, struct timespec *timep) | ||
85 | { | 206 | { |
86 | *p++ = htonl(timep->tv_sec); | 207 | __be32 *p; |
87 | /* Convert nanoseconds into microseconds */ | 208 | |
88 | *p++ = htonl(timep->tv_nsec ? timep->tv_nsec / 1000 : 0); | 209 | p = xdr_inline_decode(xdr, NFS2_FHSIZE); |
210 | if (unlikely(p == NULL)) | ||
211 | goto out_overflow; | ||
212 | fh->size = NFS2_FHSIZE; | ||
213 | memcpy(fh->data, p, NFS2_FHSIZE); | ||
214 | return 0; | ||
215 | out_overflow: | ||
216 | print_overflow_msg(__func__, xdr); | ||
217 | return -EIO; | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * 2.3.4. timeval | ||
222 | * | ||
223 | * struct timeval { | ||
224 | * unsigned int seconds; | ||
225 | * unsigned int useconds; | ||
226 | * }; | ||
227 | */ | ||
228 | static __be32 *xdr_encode_time(__be32 *p, const struct timespec *timep) | ||
229 | { | ||
230 | *p++ = cpu_to_be32(timep->tv_sec); | ||
231 | if (timep->tv_nsec != 0) | ||
232 | *p++ = cpu_to_be32(timep->tv_nsec / NSEC_PER_USEC); | ||
233 | else | ||
234 | *p++ = cpu_to_be32(0); | ||
89 | return p; | 235 | return p; |
90 | } | 236 | } |
91 | 237 | ||
92 | static inline __be32* | 238 | /* |
93 | xdr_encode_current_server_time(__be32 *p, struct timespec *timep) | 239 | * Passing the invalid value useconds=1000000 is a Sun convention for |
240 | * "set to current server time". It's needed to make permissions checks | ||
241 | * for the "touch" program across v2 mounts to Solaris and Irix servers | ||
242 | * work correctly. See description of sattr in section 6.1 of "NFS | ||
243 | * Illustrated" by Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5. | ||
244 | */ | ||
245 | static __be32 *xdr_encode_current_server_time(__be32 *p, | ||
246 | const struct timespec *timep) | ||
94 | { | 247 | { |
95 | /* | 248 | *p++ = cpu_to_be32(timep->tv_sec); |
96 | * Passing the invalid value useconds=1000000 is a | 249 | *p++ = cpu_to_be32(1000000); |
97 | * Sun convention for "set to current server time". | ||
98 | * It's needed to make permissions checks for the | ||
99 | * "touch" program across v2 mounts to Solaris and | ||
100 | * Irix boxes work correctly. See description of | ||
101 | * sattr in section 6.1 of "NFS Illustrated" by | ||
102 | * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5 | ||
103 | */ | ||
104 | *p++ = htonl(timep->tv_sec); | ||
105 | *p++ = htonl(1000000); | ||
106 | return p; | 250 | return p; |
107 | } | 251 | } |
108 | 252 | ||
109 | static inline __be32* | 253 | static __be32 *xdr_decode_time(__be32 *p, struct timespec *timep) |
110 | xdr_decode_time(__be32 *p, struct timespec *timep) | ||
111 | { | 254 | { |
112 | timep->tv_sec = ntohl(*p++); | 255 | timep->tv_sec = be32_to_cpup(p++); |
113 | /* Convert microseconds into nanoseconds */ | 256 | timep->tv_nsec = be32_to_cpup(p++) * NSEC_PER_USEC; |
114 | timep->tv_nsec = ntohl(*p++) * 1000; | ||
115 | return p; | 257 | return p; |
116 | } | 258 | } |
117 | 259 | ||
118 | static __be32 * | 260 | /* |
119 | xdr_decode_fattr(__be32 *p, struct nfs_fattr *fattr) | 261 | * 2.3.5. fattr |
262 | * | ||
263 | * struct fattr { | ||
264 | * ftype type; | ||
265 | * unsigned int mode; | ||
266 | * unsigned int nlink; | ||
267 | * unsigned int uid; | ||
268 | * unsigned int gid; | ||
269 | * unsigned int size; | ||
270 | * unsigned int blocksize; | ||
271 | * unsigned int rdev; | ||
272 | * unsigned int blocks; | ||
273 | * unsigned int fsid; | ||
274 | * unsigned int fileid; | ||
275 | * timeval atime; | ||
276 | * timeval mtime; | ||
277 | * timeval ctime; | ||
278 | * }; | ||
279 | * | ||
280 | */ | ||
281 | static int decode_fattr(struct xdr_stream *xdr, struct nfs_fattr *fattr) | ||
120 | { | 282 | { |
121 | u32 rdev, type; | 283 | u32 rdev, type; |
122 | type = ntohl(*p++); | 284 | __be32 *p; |
123 | fattr->mode = ntohl(*p++); | 285 | |
124 | fattr->nlink = ntohl(*p++); | 286 | p = xdr_inline_decode(xdr, NFS_fattr_sz << 2); |
125 | fattr->uid = ntohl(*p++); | 287 | if (unlikely(p == NULL)) |
126 | fattr->gid = ntohl(*p++); | 288 | goto out_overflow; |
127 | fattr->size = ntohl(*p++); | 289 | |
128 | fattr->du.nfs2.blocksize = ntohl(*p++); | ||
129 | rdev = ntohl(*p++); | ||
130 | fattr->du.nfs2.blocks = ntohl(*p++); | ||
131 | fattr->fsid.major = ntohl(*p++); | ||
132 | fattr->fsid.minor = 0; | ||
133 | fattr->fileid = ntohl(*p++); | ||
134 | p = xdr_decode_time(p, &fattr->atime); | ||
135 | p = xdr_decode_time(p, &fattr->mtime); | ||
136 | p = xdr_decode_time(p, &fattr->ctime); | ||
137 | fattr->valid |= NFS_ATTR_FATTR_V2; | 290 | fattr->valid |= NFS_ATTR_FATTR_V2; |
291 | |||
292 | p = xdr_decode_ftype(p, &type); | ||
293 | |||
294 | fattr->mode = be32_to_cpup(p++); | ||
295 | fattr->nlink = be32_to_cpup(p++); | ||
296 | fattr->uid = be32_to_cpup(p++); | ||
297 | fattr->gid = be32_to_cpup(p++); | ||
298 | fattr->size = be32_to_cpup(p++); | ||
299 | fattr->du.nfs2.blocksize = be32_to_cpup(p++); | ||
300 | |||
301 | rdev = be32_to_cpup(p++); | ||
138 | fattr->rdev = new_decode_dev(rdev); | 302 | fattr->rdev = new_decode_dev(rdev); |
139 | if (type == NFCHR && rdev == NFS2_FIFO_DEV) { | 303 | if (type == (u32)NFCHR && rdev == (u32)NFS2_FIFO_DEV) { |
140 | fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO; | 304 | fattr->mode = (fattr->mode & ~S_IFMT) | S_IFIFO; |
141 | fattr->rdev = 0; | 305 | fattr->rdev = 0; |
142 | } | 306 | } |
143 | return p; | 307 | |
308 | fattr->du.nfs2.blocks = be32_to_cpup(p++); | ||
309 | fattr->fsid.major = be32_to_cpup(p++); | ||
310 | fattr->fsid.minor = 0; | ||
311 | fattr->fileid = be32_to_cpup(p++); | ||
312 | |||
313 | p = xdr_decode_time(p, &fattr->atime); | ||
314 | p = xdr_decode_time(p, &fattr->mtime); | ||
315 | xdr_decode_time(p, &fattr->ctime); | ||
316 | return 0; | ||
317 | out_overflow: | ||
318 | print_overflow_msg(__func__, xdr); | ||
319 | return -EIO; | ||
144 | } | 320 | } |
145 | 321 | ||
146 | static inline __be32 * | 322 | /* |
147 | xdr_encode_sattr(__be32 *p, struct iattr *attr) | 323 | * 2.3.6. sattr |
148 | { | 324 | * |
149 | const __be32 not_set = __constant_htonl(0xFFFFFFFF); | 325 | * struct sattr { |
326 | * unsigned int mode; | ||
327 | * unsigned int uid; | ||
328 | * unsigned int gid; | ||
329 | * unsigned int size; | ||
330 | * timeval atime; | ||
331 | * timeval mtime; | ||
332 | * }; | ||
333 | */ | ||
150 | 334 | ||
151 | *p++ = (attr->ia_valid & ATTR_MODE) ? htonl(attr->ia_mode) : not_set; | 335 | #define NFS2_SATTR_NOT_SET (0xffffffff) |
152 | *p++ = (attr->ia_valid & ATTR_UID) ? htonl(attr->ia_uid) : not_set; | 336 | |
153 | *p++ = (attr->ia_valid & ATTR_GID) ? htonl(attr->ia_gid) : not_set; | 337 | static __be32 *xdr_time_not_set(__be32 *p) |
154 | *p++ = (attr->ia_valid & ATTR_SIZE) ? htonl(attr->ia_size) : not_set; | 338 | { |
339 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | ||
340 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | ||
341 | return p; | ||
342 | } | ||
155 | 343 | ||
156 | if (attr->ia_valid & ATTR_ATIME_SET) { | 344 | static void encode_sattr(struct xdr_stream *xdr, const struct iattr *attr) |
345 | { | ||
346 | __be32 *p; | ||
347 | |||
348 | p = xdr_reserve_space(xdr, NFS_sattr_sz << 2); | ||
349 | |||
350 | if (attr->ia_valid & ATTR_MODE) | ||
351 | *p++ = cpu_to_be32(attr->ia_mode); | ||
352 | else | ||
353 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | ||
354 | if (attr->ia_valid & ATTR_UID) | ||
355 | *p++ = cpu_to_be32(attr->ia_uid); | ||
356 | else | ||
357 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | ||
358 | if (attr->ia_valid & ATTR_GID) | ||
359 | *p++ = cpu_to_be32(attr->ia_gid); | ||
360 | else | ||
361 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | ||
362 | if (attr->ia_valid & ATTR_SIZE) | ||
363 | *p++ = cpu_to_be32((u32)attr->ia_size); | ||
364 | else | ||
365 | *p++ = cpu_to_be32(NFS2_SATTR_NOT_SET); | ||
366 | |||
367 | if (attr->ia_valid & ATTR_ATIME_SET) | ||
157 | p = xdr_encode_time(p, &attr->ia_atime); | 368 | p = xdr_encode_time(p, &attr->ia_atime); |
158 | } else if (attr->ia_valid & ATTR_ATIME) { | 369 | else if (attr->ia_valid & ATTR_ATIME) |
159 | p = xdr_encode_current_server_time(p, &attr->ia_atime); | 370 | p = xdr_encode_current_server_time(p, &attr->ia_atime); |
160 | } else { | 371 | else |
161 | *p++ = not_set; | 372 | p = xdr_time_not_set(p); |
162 | *p++ = not_set; | 373 | if (attr->ia_valid & ATTR_MTIME_SET) |
163 | } | 374 | xdr_encode_time(p, &attr->ia_mtime); |
164 | 375 | else if (attr->ia_valid & ATTR_MTIME) | |
165 | if (attr->ia_valid & ATTR_MTIME_SET) { | 376 | xdr_encode_current_server_time(p, &attr->ia_mtime); |
166 | p = xdr_encode_time(p, &attr->ia_mtime); | 377 | else |
167 | } else if (attr->ia_valid & ATTR_MTIME) { | 378 | xdr_time_not_set(p); |
168 | p = xdr_encode_current_server_time(p, &attr->ia_mtime); | ||
169 | } else { | ||
170 | *p++ = not_set; | ||
171 | *p++ = not_set; | ||
172 | } | ||
173 | return p; | ||
174 | } | 379 | } |
175 | 380 | ||
176 | /* | 381 | /* |
177 | * NFS encode functions | 382 | * 2.3.7. filename |
178 | */ | 383 | * |
179 | /* | 384 | * typedef string filename<MAXNAMLEN>; |
180 | * Encode file handle argument | ||
181 | * GETATTR, READLINK, STATFS | ||
182 | */ | 385 | */ |
183 | static int | 386 | static void encode_filename(struct xdr_stream *xdr, |
184 | nfs_xdr_fhandle(struct rpc_rqst *req, __be32 *p, struct nfs_fh *fh) | 387 | const char *name, u32 length) |
185 | { | 388 | { |
186 | p = xdr_encode_fhandle(p, fh); | 389 | __be32 *p; |
187 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 390 | |
391 | BUG_ON(length > NFS2_MAXNAMLEN); | ||
392 | p = xdr_reserve_space(xdr, 4 + length); | ||
393 | xdr_encode_opaque(p, name, length); | ||
394 | } | ||
395 | |||
396 | static int decode_filename_inline(struct xdr_stream *xdr, | ||
397 | const char **name, u32 *length) | ||
398 | { | ||
399 | __be32 *p; | ||
400 | u32 count; | ||
401 | |||
402 | p = xdr_inline_decode(xdr, 4); | ||
403 | if (unlikely(p == NULL)) | ||
404 | goto out_overflow; | ||
405 | count = be32_to_cpup(p); | ||
406 | if (count > NFS3_MAXNAMLEN) | ||
407 | goto out_nametoolong; | ||
408 | p = xdr_inline_decode(xdr, count); | ||
409 | if (unlikely(p == NULL)) | ||
410 | goto out_overflow; | ||
411 | *name = (const char *)p; | ||
412 | *length = count; | ||
188 | return 0; | 413 | return 0; |
414 | out_nametoolong: | ||
415 | dprintk("NFS: returned filename too long: %u\n", count); | ||
416 | return -ENAMETOOLONG; | ||
417 | out_overflow: | ||
418 | print_overflow_msg(__func__, xdr); | ||
419 | return -EIO; | ||
189 | } | 420 | } |
190 | 421 | ||
191 | /* | 422 | /* |
192 | * Encode SETATTR arguments | 423 | * 2.3.8. path |
424 | * | ||
425 | * typedef string path<MAXPATHLEN>; | ||
193 | */ | 426 | */ |
194 | static int | 427 | static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length) |
195 | nfs_xdr_sattrargs(struct rpc_rqst *req, __be32 *p, struct nfs_sattrargs *args) | 428 | { |
429 | __be32 *p; | ||
430 | |||
431 | BUG_ON(length > NFS2_MAXPATHLEN); | ||
432 | p = xdr_reserve_space(xdr, 4); | ||
433 | *p = cpu_to_be32(length); | ||
434 | xdr_write_pages(xdr, pages, 0, length); | ||
435 | } | ||
436 | |||
437 | static int decode_path(struct xdr_stream *xdr) | ||
196 | { | 438 | { |
197 | p = xdr_encode_fhandle(p, args->fh); | 439 | u32 length, recvd; |
198 | p = xdr_encode_sattr(p, args->sattr); | 440 | size_t hdrlen; |
199 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 441 | __be32 *p; |
442 | |||
443 | p = xdr_inline_decode(xdr, 4); | ||
444 | if (unlikely(p == NULL)) | ||
445 | goto out_overflow; | ||
446 | length = be32_to_cpup(p); | ||
447 | if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN)) | ||
448 | goto out_size; | ||
449 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | ||
450 | recvd = xdr->buf->len - hdrlen; | ||
451 | if (unlikely(length > recvd)) | ||
452 | goto out_cheating; | ||
453 | |||
454 | xdr_read_pages(xdr, length); | ||
455 | xdr_terminate_string(xdr->buf, length); | ||
200 | return 0; | 456 | return 0; |
457 | out_size: | ||
458 | dprintk("NFS: returned pathname too long: %u\n", length); | ||
459 | return -ENAMETOOLONG; | ||
460 | out_cheating: | ||
461 | dprintk("NFS: server cheating in pathname result: " | ||
462 | "length %u > received %u\n", length, recvd); | ||
463 | return -EIO; | ||
464 | out_overflow: | ||
465 | print_overflow_msg(__func__, xdr); | ||
466 | return -EIO; | ||
201 | } | 467 | } |
202 | 468 | ||
203 | /* | 469 | /* |
204 | * Encode directory ops argument | 470 | * 2.3.9. attrstat |
205 | * LOOKUP, RMDIR | 471 | * |
472 | * union attrstat switch (stat status) { | ||
473 | * case NFS_OK: | ||
474 | * fattr attributes; | ||
475 | * default: | ||
476 | * void; | ||
477 | * }; | ||
206 | */ | 478 | */ |
207 | static int | 479 | static int decode_attrstat(struct xdr_stream *xdr, struct nfs_fattr *result) |
208 | nfs_xdr_diropargs(struct rpc_rqst *req, __be32 *p, struct nfs_diropargs *args) | ||
209 | { | 480 | { |
210 | p = xdr_encode_fhandle(p, args->fh); | 481 | enum nfs_stat status; |
211 | p = xdr_encode_array(p, args->name, args->len); | 482 | int error; |
212 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 483 | |
213 | return 0; | 484 | error = decode_stat(xdr, &status); |
485 | if (unlikely(error)) | ||
486 | goto out; | ||
487 | if (status != NFS_OK) | ||
488 | goto out_default; | ||
489 | error = decode_fattr(xdr, result); | ||
490 | out: | ||
491 | return error; | ||
492 | out_default: | ||
493 | return nfs_stat_to_errno(status); | ||
214 | } | 494 | } |
215 | 495 | ||
216 | /* | 496 | /* |
217 | * Encode REMOVE argument | 497 | * 2.3.10. diropargs |
498 | * | ||
499 | * struct diropargs { | ||
500 | * fhandle dir; | ||
501 | * filename name; | ||
502 | * }; | ||
218 | */ | 503 | */ |
219 | static int | 504 | static void encode_diropargs(struct xdr_stream *xdr, const struct nfs_fh *fh, |
220 | nfs_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs *args) | 505 | const char *name, u32 length) |
221 | { | 506 | { |
222 | p = xdr_encode_fhandle(p, args->fh); | 507 | encode_fhandle(xdr, fh); |
223 | p = xdr_encode_array(p, args->name.name, args->name.len); | 508 | encode_filename(xdr, name, length); |
224 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
225 | return 0; | ||
226 | } | 509 | } |
227 | 510 | ||
228 | /* | 511 | /* |
229 | * Arguments to a READ call. Since we read data directly into the page | 512 | * 2.3.11. diropres |
230 | * cache, we also set up the reply iovec here so that iov[1] points | 513 | * |
231 | * exactly to the page we want to fetch. | 514 | * union diropres switch (stat status) { |
515 | * case NFS_OK: | ||
516 | * struct { | ||
517 | * fhandle file; | ||
518 | * fattr attributes; | ||
519 | * } diropok; | ||
520 | * default: | ||
521 | * void; | ||
522 | * }; | ||
232 | */ | 523 | */ |
233 | static int | 524 | static int decode_diropok(struct xdr_stream *xdr, struct nfs_diropok *result) |
234 | nfs_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args) | ||
235 | { | 525 | { |
236 | struct rpc_auth *auth = req->rq_cred->cr_auth; | 526 | int error; |
237 | unsigned int replen; | 527 | |
238 | u32 offset = (u32)args->offset; | 528 | error = decode_fhandle(xdr, result->fh); |
239 | u32 count = args->count; | 529 | if (unlikely(error)) |
240 | 530 | goto out; | |
241 | p = xdr_encode_fhandle(p, args->fh); | 531 | error = decode_fattr(xdr, result->fattr); |
242 | *p++ = htonl(offset); | 532 | out: |
243 | *p++ = htonl(count); | 533 | return error; |
244 | *p++ = htonl(count); | 534 | } |
245 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
246 | 535 | ||
247 | /* Inline the page array */ | 536 | static int decode_diropres(struct xdr_stream *xdr, struct nfs_diropok *result) |
248 | replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readres_sz) << 2; | 537 | { |
249 | xdr_inline_pages(&req->rq_rcv_buf, replen, | 538 | enum nfs_stat status; |
250 | args->pages, args->pgbase, count); | 539 | int error; |
251 | req->rq_rcv_buf.flags |= XDRBUF_READ; | 540 | |
252 | return 0; | 541 | error = decode_stat(xdr, &status); |
542 | if (unlikely(error)) | ||
543 | goto out; | ||
544 | if (status != NFS_OK) | ||
545 | goto out_default; | ||
546 | error = decode_diropok(xdr, result); | ||
547 | out: | ||
548 | return error; | ||
549 | out_default: | ||
550 | return nfs_stat_to_errno(status); | ||
253 | } | 551 | } |
254 | 552 | ||
553 | |||
255 | /* | 554 | /* |
256 | * Decode READ reply | 555 | * NFSv2 XDR encode functions |
556 | * | ||
557 | * NFSv2 argument types are defined in section 2.2 of RFC 1094: | ||
558 | * "NFS: Network File System Protocol Specification". | ||
257 | */ | 559 | */ |
258 | static int | ||
259 | nfs_xdr_readres(struct rpc_rqst *req, __be32 *p, struct nfs_readres *res) | ||
260 | { | ||
261 | struct kvec *iov = req->rq_rcv_buf.head; | ||
262 | size_t hdrlen; | ||
263 | u32 count, recvd; | ||
264 | int status; | ||
265 | |||
266 | if ((status = ntohl(*p++))) | ||
267 | return nfs_stat_to_errno(status); | ||
268 | p = xdr_decode_fattr(p, res->fattr); | ||
269 | |||
270 | count = ntohl(*p++); | ||
271 | res->eof = 0; | ||
272 | hdrlen = (u8 *) p - (u8 *) iov->iov_base; | ||
273 | if (iov->iov_len < hdrlen) { | ||
274 | dprintk("NFS: READ reply header overflowed:" | ||
275 | "length %Zu > %Zu\n", hdrlen, iov->iov_len); | ||
276 | return -errno_NFSERR_IO; | ||
277 | } else if (iov->iov_len != hdrlen) { | ||
278 | dprintk("NFS: READ header is short. iovec will be shifted.\n"); | ||
279 | xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen); | ||
280 | } | ||
281 | 560 | ||
282 | recvd = req->rq_rcv_buf.len - hdrlen; | 561 | static void nfs2_xdr_enc_fhandle(struct rpc_rqst *req, |
283 | if (count > recvd) { | 562 | struct xdr_stream *xdr, |
284 | dprintk("NFS: server cheating in read reply: " | 563 | const struct nfs_fh *fh) |
285 | "count %u > recvd %u\n", count, recvd); | 564 | { |
286 | count = recvd; | 565 | encode_fhandle(xdr, fh); |
287 | } | 566 | } |
288 | 567 | ||
289 | dprintk("RPC: readres OK count %u\n", count); | 568 | /* |
290 | if (count < res->count) | 569 | * 2.2.3. sattrargs |
291 | res->count = count; | 570 | * |
571 | * struct sattrargs { | ||
572 | * fhandle file; | ||
573 | * sattr attributes; | ||
574 | * }; | ||
575 | */ | ||
576 | static void nfs2_xdr_enc_sattrargs(struct rpc_rqst *req, | ||
577 | struct xdr_stream *xdr, | ||
578 | const struct nfs_sattrargs *args) | ||
579 | { | ||
580 | encode_fhandle(xdr, args->fh); | ||
581 | encode_sattr(xdr, args->sattr); | ||
582 | } | ||
292 | 583 | ||
293 | return count; | 584 | static void nfs2_xdr_enc_diropargs(struct rpc_rqst *req, |
585 | struct xdr_stream *xdr, | ||
586 | const struct nfs_diropargs *args) | ||
587 | { | ||
588 | encode_diropargs(xdr, args->fh, args->name, args->len); | ||
294 | } | 589 | } |
295 | 590 | ||
591 | static void nfs2_xdr_enc_readlinkargs(struct rpc_rqst *req, | ||
592 | struct xdr_stream *xdr, | ||
593 | const struct nfs_readlinkargs *args) | ||
594 | { | ||
595 | encode_fhandle(xdr, args->fh); | ||
596 | prepare_reply_buffer(req, args->pages, args->pgbase, | ||
597 | args->pglen, NFS_readlinkres_sz); | ||
598 | } | ||
296 | 599 | ||
297 | /* | 600 | /* |
298 | * Write arguments. Splice the buffer to be written into the iovec. | 601 | * 2.2.7. readargs |
602 | * | ||
603 | * struct readargs { | ||
604 | * fhandle file; | ||
605 | * unsigned offset; | ||
606 | * unsigned count; | ||
607 | * unsigned totalcount; | ||
608 | * }; | ||
299 | */ | 609 | */ |
300 | static int | 610 | static void encode_readargs(struct xdr_stream *xdr, |
301 | nfs_xdr_writeargs(struct rpc_rqst *req, __be32 *p, struct nfs_writeargs *args) | 611 | const struct nfs_readargs *args) |
302 | { | 612 | { |
303 | struct xdr_buf *sndbuf = &req->rq_snd_buf; | 613 | u32 offset = args->offset; |
304 | u32 offset = (u32)args->offset; | ||
305 | u32 count = args->count; | 614 | u32 count = args->count; |
615 | __be32 *p; | ||
306 | 616 | ||
307 | p = xdr_encode_fhandle(p, args->fh); | 617 | encode_fhandle(xdr, args->fh); |
308 | *p++ = htonl(offset); | ||
309 | *p++ = htonl(offset); | ||
310 | *p++ = htonl(count); | ||
311 | *p++ = htonl(count); | ||
312 | sndbuf->len = xdr_adjust_iovec(sndbuf->head, p); | ||
313 | 618 | ||
314 | /* Copy the page array */ | 619 | p = xdr_reserve_space(xdr, 4 + 4 + 4); |
315 | xdr_encode_pages(sndbuf, args->pages, args->pgbase, count); | 620 | *p++ = cpu_to_be32(offset); |
316 | sndbuf->flags |= XDRBUF_WRITE; | 621 | *p++ = cpu_to_be32(count); |
317 | return 0; | 622 | *p = cpu_to_be32(count); |
318 | } | 623 | } |
319 | 624 | ||
320 | /* | 625 | static void nfs2_xdr_enc_readargs(struct rpc_rqst *req, |
321 | * Encode create arguments | 626 | struct xdr_stream *xdr, |
322 | * CREATE, MKDIR | 627 | const struct nfs_readargs *args) |
323 | */ | ||
324 | static int | ||
325 | nfs_xdr_createargs(struct rpc_rqst *req, __be32 *p, struct nfs_createargs *args) | ||
326 | { | 628 | { |
327 | p = xdr_encode_fhandle(p, args->fh); | 629 | encode_readargs(xdr, args); |
328 | p = xdr_encode_array(p, args->name, args->len); | 630 | prepare_reply_buffer(req, args->pages, args->pgbase, |
329 | p = xdr_encode_sattr(p, args->sattr); | 631 | args->count, NFS_readres_sz); |
330 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 632 | req->rq_rcv_buf.flags |= XDRBUF_READ; |
331 | return 0; | ||
332 | } | 633 | } |
333 | 634 | ||
334 | /* | 635 | /* |
335 | * Encode RENAME arguments | 636 | * 2.2.9. writeargs |
637 | * | ||
638 | * struct writeargs { | ||
639 | * fhandle file; | ||
640 | * unsigned beginoffset; | ||
641 | * unsigned offset; | ||
642 | * unsigned totalcount; | ||
643 | * nfsdata data; | ||
644 | * }; | ||
336 | */ | 645 | */ |
337 | static int | 646 | static void encode_writeargs(struct xdr_stream *xdr, |
338 | nfs_xdr_renameargs(struct rpc_rqst *req, __be32 *p, struct nfs_renameargs *args) | 647 | const struct nfs_writeargs *args) |
339 | { | 648 | { |
340 | p = xdr_encode_fhandle(p, args->fromfh); | 649 | u32 offset = args->offset; |
341 | p = xdr_encode_array(p, args->fromname, args->fromlen); | 650 | u32 count = args->count; |
342 | p = xdr_encode_fhandle(p, args->tofh); | 651 | __be32 *p; |
343 | p = xdr_encode_array(p, args->toname, args->tolen); | 652 | |
344 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | 653 | encode_fhandle(xdr, args->fh); |
345 | return 0; | 654 | |
655 | p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4); | ||
656 | *p++ = cpu_to_be32(offset); | ||
657 | *p++ = cpu_to_be32(offset); | ||
658 | *p++ = cpu_to_be32(count); | ||
659 | |||
660 | /* nfsdata */ | ||
661 | *p = cpu_to_be32(count); | ||
662 | xdr_write_pages(xdr, args->pages, args->pgbase, count); | ||
346 | } | 663 | } |
347 | 664 | ||
348 | /* | 665 | static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req, |
349 | * Encode LINK arguments | 666 | struct xdr_stream *xdr, |
350 | */ | 667 | const struct nfs_writeargs *args) |
351 | static int | ||
352 | nfs_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs_linkargs *args) | ||
353 | { | 668 | { |
354 | p = xdr_encode_fhandle(p, args->fromfh); | 669 | encode_writeargs(xdr, args); |
355 | p = xdr_encode_fhandle(p, args->tofh); | 670 | xdr->buf->flags |= XDRBUF_WRITE; |
356 | p = xdr_encode_array(p, args->toname, args->tolen); | ||
357 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
358 | return 0; | ||
359 | } | 671 | } |
360 | 672 | ||
361 | /* | 673 | /* |
362 | * Encode SYMLINK arguments | 674 | * 2.2.10. createargs |
675 | * | ||
676 | * struct createargs { | ||
677 | * diropargs where; | ||
678 | * sattr attributes; | ||
679 | * }; | ||
363 | */ | 680 | */ |
364 | static int | 681 | static void nfs2_xdr_enc_createargs(struct rpc_rqst *req, |
365 | nfs_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_symlinkargs *args) | 682 | struct xdr_stream *xdr, |
683 | const struct nfs_createargs *args) | ||
366 | { | 684 | { |
367 | struct xdr_buf *sndbuf = &req->rq_snd_buf; | 685 | encode_diropargs(xdr, args->fh, args->name, args->len); |
368 | size_t pad; | 686 | encode_sattr(xdr, args->sattr); |
687 | } | ||
369 | 688 | ||
370 | p = xdr_encode_fhandle(p, args->fromfh); | 689 | static void nfs2_xdr_enc_removeargs(struct rpc_rqst *req, |
371 | p = xdr_encode_array(p, args->fromname, args->fromlen); | 690 | struct xdr_stream *xdr, |
372 | *p++ = htonl(args->pathlen); | 691 | const struct nfs_removeargs *args) |
373 | sndbuf->len = xdr_adjust_iovec(sndbuf->head, p); | 692 | { |
693 | encode_diropargs(xdr, args->fh, args->name.name, args->name.len); | ||
694 | } | ||
374 | 695 | ||
375 | xdr_encode_pages(sndbuf, args->pages, 0, args->pathlen); | 696 | /* |
697 | * 2.2.12. renameargs | ||
698 | * | ||
699 | * struct renameargs { | ||
700 | * diropargs from; | ||
701 | * diropargs to; | ||
702 | * }; | ||
703 | */ | ||
704 | static void nfs2_xdr_enc_renameargs(struct rpc_rqst *req, | ||
705 | struct xdr_stream *xdr, | ||
706 | const struct nfs_renameargs *args) | ||
707 | { | ||
708 | const struct qstr *old = args->old_name; | ||
709 | const struct qstr *new = args->new_name; | ||
376 | 710 | ||
377 | /* | 711 | encode_diropargs(xdr, args->old_dir, old->name, old->len); |
378 | * xdr_encode_pages may have added a few bytes to ensure the | 712 | encode_diropargs(xdr, args->new_dir, new->name, new->len); |
379 | * pathname ends on a 4-byte boundary. Start encoding the | ||
380 | * attributes after the pad bytes. | ||
381 | */ | ||
382 | pad = sndbuf->tail->iov_len; | ||
383 | if (pad > 0) | ||
384 | p++; | ||
385 | p = xdr_encode_sattr(p, args->sattr); | ||
386 | sndbuf->len += xdr_adjust_iovec(sndbuf->tail, p) - pad; | ||
387 | return 0; | ||
388 | } | 713 | } |
389 | 714 | ||
390 | /* | 715 | /* |
391 | * Encode arguments to readdir call | 716 | * 2.2.13. linkargs |
717 | * | ||
718 | * struct linkargs { | ||
719 | * fhandle from; | ||
720 | * diropargs to; | ||
721 | * }; | ||
392 | */ | 722 | */ |
393 | static int | 723 | static void nfs2_xdr_enc_linkargs(struct rpc_rqst *req, |
394 | nfs_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs_readdirargs *args) | 724 | struct xdr_stream *xdr, |
725 | const struct nfs_linkargs *args) | ||
395 | { | 726 | { |
396 | struct rpc_auth *auth = req->rq_cred->cr_auth; | 727 | encode_fhandle(xdr, args->fromfh); |
397 | unsigned int replen; | 728 | encode_diropargs(xdr, args->tofh, args->toname, args->tolen); |
398 | u32 count = args->count; | 729 | } |
399 | |||
400 | p = xdr_encode_fhandle(p, args->fh); | ||
401 | *p++ = htonl(args->cookie); | ||
402 | *p++ = htonl(count); /* see above */ | ||
403 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
404 | 730 | ||
405 | /* Inline the page array */ | 731 | /* |
406 | replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readdirres_sz) << 2; | 732 | * 2.2.14. symlinkargs |
407 | xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count); | 733 | * |
408 | return 0; | 734 | * struct symlinkargs { |
735 | * diropargs from; | ||
736 | * path to; | ||
737 | * sattr attributes; | ||
738 | * }; | ||
739 | */ | ||
740 | static void nfs2_xdr_enc_symlinkargs(struct rpc_rqst *req, | ||
741 | struct xdr_stream *xdr, | ||
742 | const struct nfs_symlinkargs *args) | ||
743 | { | ||
744 | encode_diropargs(xdr, args->fromfh, args->fromname, args->fromlen); | ||
745 | encode_path(xdr, args->pages, args->pathlen); | ||
746 | encode_sattr(xdr, args->sattr); | ||
409 | } | 747 | } |
410 | 748 | ||
411 | /* | 749 | /* |
412 | * Decode the result of a readdir call. | 750 | * 2.2.17. readdirargs |
413 | * We're not really decoding anymore, we just leave the buffer untouched | 751 | * |
414 | * and only check that it is syntactically correct. | 752 | * struct readdirargs { |
415 | * The real decoding happens in nfs_decode_entry below, called directly | 753 | * fhandle dir; |
416 | * from nfs_readdir for each entry. | 754 | * nfscookie cookie; |
755 | * unsigned count; | ||
756 | * }; | ||
417 | */ | 757 | */ |
418 | static int | 758 | static void encode_readdirargs(struct xdr_stream *xdr, |
419 | nfs_xdr_readdirres(struct rpc_rqst *req, __be32 *p, void *dummy) | 759 | const struct nfs_readdirargs *args) |
420 | { | 760 | { |
421 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | 761 | __be32 *p; |
422 | struct kvec *iov = rcvbuf->head; | ||
423 | struct page **page; | ||
424 | size_t hdrlen; | ||
425 | unsigned int pglen, recvd; | ||
426 | u32 len; | ||
427 | int status, nr = 0; | ||
428 | __be32 *end, *entry, *kaddr; | ||
429 | |||
430 | if ((status = ntohl(*p++))) | ||
431 | return nfs_stat_to_errno(status); | ||
432 | |||
433 | hdrlen = (u8 *) p - (u8 *) iov->iov_base; | ||
434 | if (iov->iov_len < hdrlen) { | ||
435 | dprintk("NFS: READDIR reply header overflowed:" | ||
436 | "length %Zu > %Zu\n", hdrlen, iov->iov_len); | ||
437 | return -errno_NFSERR_IO; | ||
438 | } else if (iov->iov_len != hdrlen) { | ||
439 | dprintk("NFS: READDIR header is short. iovec will be shifted.\n"); | ||
440 | xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen); | ||
441 | } | ||
442 | 762 | ||
443 | pglen = rcvbuf->page_len; | 763 | encode_fhandle(xdr, args->fh); |
444 | recvd = rcvbuf->len - hdrlen; | ||
445 | if (pglen > recvd) | ||
446 | pglen = recvd; | ||
447 | page = rcvbuf->pages; | ||
448 | kaddr = p = kmap_atomic(*page, KM_USER0); | ||
449 | end = (__be32 *)((char *)p + pglen); | ||
450 | entry = p; | ||
451 | |||
452 | /* Make sure the packet actually has a value_follows and EOF entry */ | ||
453 | if ((entry + 1) > end) | ||
454 | goto short_pkt; | ||
455 | |||
456 | for (; *p++; nr++) { | ||
457 | if (p + 2 > end) | ||
458 | goto short_pkt; | ||
459 | p++; /* fileid */ | ||
460 | len = ntohl(*p++); | ||
461 | p += XDR_QUADLEN(len) + 1; /* name plus cookie */ | ||
462 | if (len > NFS2_MAXNAMLEN) { | ||
463 | dprintk("NFS: giant filename in readdir (len 0x%x)!\n", | ||
464 | len); | ||
465 | goto err_unmap; | ||
466 | } | ||
467 | if (p + 2 > end) | ||
468 | goto short_pkt; | ||
469 | entry = p; | ||
470 | } | ||
471 | 764 | ||
472 | /* | 765 | p = xdr_reserve_space(xdr, 4 + 4); |
473 | * Apparently some server sends responses that are a valid size, but | 766 | *p++ = cpu_to_be32(args->cookie); |
474 | * contain no entries, and have value_follows==0 and EOF==0. For | 767 | *p = cpu_to_be32(args->count); |
475 | * those, just set the EOF marker. | ||
476 | */ | ||
477 | if (!nr && entry[1] == 0) { | ||
478 | dprintk("NFS: readdir reply truncated!\n"); | ||
479 | entry[1] = 1; | ||
480 | } | ||
481 | out: | ||
482 | kunmap_atomic(kaddr, KM_USER0); | ||
483 | return nr; | ||
484 | short_pkt: | ||
485 | /* | ||
486 | * When we get a short packet there are 2 possibilities. We can | ||
487 | * return an error, or fix up the response to look like a valid | ||
488 | * response and return what we have so far. If there are no | ||
489 | * entries and the packet was short, then return -EIO. If there | ||
490 | * are valid entries in the response, return them and pretend that | ||
491 | * the call was successful, but incomplete. The caller can retry the | ||
492 | * readdir starting at the last cookie. | ||
493 | */ | ||
494 | entry[0] = entry[1] = 0; | ||
495 | if (!nr) | ||
496 | nr = -errno_NFSERR_IO; | ||
497 | goto out; | ||
498 | err_unmap: | ||
499 | nr = -errno_NFSERR_IO; | ||
500 | goto out; | ||
501 | } | 768 | } |
502 | 769 | ||
503 | __be32 * | 770 | static void nfs2_xdr_enc_readdirargs(struct rpc_rqst *req, |
504 | nfs_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | 771 | struct xdr_stream *xdr, |
772 | const struct nfs_readdirargs *args) | ||
505 | { | 773 | { |
506 | if (!*p++) { | 774 | encode_readdirargs(xdr, args); |
507 | if (!*p) | 775 | prepare_reply_buffer(req, args->pages, 0, |
508 | return ERR_PTR(-EAGAIN); | 776 | args->count, NFS_readdirres_sz); |
509 | entry->eof = 1; | ||
510 | return ERR_PTR(-EBADCOOKIE); | ||
511 | } | ||
512 | |||
513 | entry->ino = ntohl(*p++); | ||
514 | entry->len = ntohl(*p++); | ||
515 | entry->name = (const char *) p; | ||
516 | p += XDR_QUADLEN(entry->len); | ||
517 | entry->prev_cookie = entry->cookie; | ||
518 | entry->cookie = ntohl(*p++); | ||
519 | entry->eof = !p[0] && p[1]; | ||
520 | |||
521 | return p; | ||
522 | } | 777 | } |
523 | 778 | ||
524 | /* | 779 | /* |
525 | * NFS XDR decode functions | 780 | * NFSv2 XDR decode functions |
526 | */ | 781 | * |
527 | /* | 782 | * NFSv2 result types are defined in section 2.2 of RFC 1094: |
528 | * Decode simple status reply | 783 | * "NFS: Network File System Protocol Specification". |
529 | */ | 784 | */ |
530 | static int | 785 | |
531 | nfs_xdr_stat(struct rpc_rqst *req, __be32 *p, void *dummy) | 786 | static int nfs2_xdr_dec_stat(struct rpc_rqst *req, struct xdr_stream *xdr, |
787 | void *__unused) | ||
532 | { | 788 | { |
533 | int status; | 789 | enum nfs_stat status; |
790 | int error; | ||
791 | |||
792 | error = decode_stat(xdr, &status); | ||
793 | if (unlikely(error)) | ||
794 | goto out; | ||
795 | if (status != NFS_OK) | ||
796 | goto out_default; | ||
797 | out: | ||
798 | return error; | ||
799 | out_default: | ||
800 | return nfs_stat_to_errno(status); | ||
801 | } | ||
534 | 802 | ||
535 | if ((status = ntohl(*p++)) != 0) | 803 | static int nfs2_xdr_dec_attrstat(struct rpc_rqst *req, struct xdr_stream *xdr, |
536 | status = nfs_stat_to_errno(status); | 804 | struct nfs_fattr *result) |
537 | return status; | 805 | { |
806 | return decode_attrstat(xdr, result); | ||
807 | } | ||
808 | |||
809 | static int nfs2_xdr_dec_diropres(struct rpc_rqst *req, struct xdr_stream *xdr, | ||
810 | struct nfs_diropok *result) | ||
811 | { | ||
812 | return decode_diropres(xdr, result); | ||
538 | } | 813 | } |
539 | 814 | ||
540 | /* | 815 | /* |
541 | * Decode attrstat reply | 816 | * 2.2.6. readlinkres |
542 | * GETATTR, SETATTR, WRITE | 817 | * |
818 | * union readlinkres switch (stat status) { | ||
819 | * case NFS_OK: | ||
820 | * path data; | ||
821 | * default: | ||
822 | * void; | ||
823 | * }; | ||
543 | */ | 824 | */ |
544 | static int | 825 | static int nfs2_xdr_dec_readlinkres(struct rpc_rqst *req, |
545 | nfs_xdr_attrstat(struct rpc_rqst *req, __be32 *p, struct nfs_fattr *fattr) | 826 | struct xdr_stream *xdr, void *__unused) |
546 | { | 827 | { |
547 | int status; | 828 | enum nfs_stat status; |
548 | 829 | int error; | |
549 | if ((status = ntohl(*p++))) | 830 | |
550 | return nfs_stat_to_errno(status); | 831 | error = decode_stat(xdr, &status); |
551 | xdr_decode_fattr(p, fattr); | 832 | if (unlikely(error)) |
552 | return 0; | 833 | goto out; |
834 | if (status != NFS_OK) | ||
835 | goto out_default; | ||
836 | error = decode_path(xdr); | ||
837 | out: | ||
838 | return error; | ||
839 | out_default: | ||
840 | return nfs_stat_to_errno(status); | ||
553 | } | 841 | } |
554 | 842 | ||
555 | /* | 843 | /* |
556 | * Decode diropres reply | 844 | * 2.2.7. readres |
557 | * LOOKUP, CREATE, MKDIR | 845 | * |
846 | * union readres switch (stat status) { | ||
847 | * case NFS_OK: | ||
848 | * fattr attributes; | ||
849 | * nfsdata data; | ||
850 | * default: | ||
851 | * void; | ||
852 | * }; | ||
558 | */ | 853 | */ |
559 | static int | 854 | static int nfs2_xdr_dec_readres(struct rpc_rqst *req, struct xdr_stream *xdr, |
560 | nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res) | 855 | struct nfs_readres *result) |
561 | { | 856 | { |
562 | int status; | 857 | enum nfs_stat status; |
858 | int error; | ||
859 | |||
860 | error = decode_stat(xdr, &status); | ||
861 | if (unlikely(error)) | ||
862 | goto out; | ||
863 | if (status != NFS_OK) | ||
864 | goto out_default; | ||
865 | error = decode_fattr(xdr, result->fattr); | ||
866 | if (unlikely(error)) | ||
867 | goto out; | ||
868 | error = decode_nfsdata(xdr, result); | ||
869 | out: | ||
870 | return error; | ||
871 | out_default: | ||
872 | return nfs_stat_to_errno(status); | ||
873 | } | ||
563 | 874 | ||
564 | if ((status = ntohl(*p++))) | 875 | static int nfs2_xdr_dec_writeres(struct rpc_rqst *req, struct xdr_stream *xdr, |
565 | return nfs_stat_to_errno(status); | 876 | struct nfs_writeres *result) |
566 | p = xdr_decode_fhandle(p, res->fh); | 877 | { |
567 | xdr_decode_fattr(p, res->fattr); | 878 | /* All NFSv2 writes are "file sync" writes */ |
568 | return 0; | 879 | result->verf->committed = NFS_FILE_SYNC; |
880 | return decode_attrstat(xdr, result->fattr); | ||
569 | } | 881 | } |
570 | 882 | ||
571 | /* | 883 | /** |
572 | * Encode READLINK args | 884 | * nfs2_decode_dirent - Decode a single NFSv2 directory entry stored in |
885 | * the local page cache. | ||
886 | * @xdr: XDR stream where entry resides | ||
887 | * @entry: buffer to fill in with entry data | ||
888 | * @plus: boolean indicating whether this should be a readdirplus entry | ||
889 | * | ||
890 | * Returns zero if successful, otherwise a negative errno value is | ||
891 | * returned. | ||
892 | * | ||
893 | * This function is not invoked during READDIR reply decoding, but | ||
894 | * rather whenever an application invokes the getdents(2) system call | ||
895 | * on a directory already in our cache. | ||
896 | * | ||
897 | * 2.2.17. entry | ||
898 | * | ||
899 | * struct entry { | ||
900 | * unsigned fileid; | ||
901 | * filename name; | ||
902 | * nfscookie cookie; | ||
903 | * entry *nextentry; | ||
904 | * }; | ||
573 | */ | 905 | */ |
574 | static int | 906 | int nfs2_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, |
575 | nfs_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_readlinkargs *args) | 907 | int plus) |
576 | { | 908 | { |
577 | struct rpc_auth *auth = req->rq_cred->cr_auth; | 909 | __be32 *p; |
578 | unsigned int replen; | 910 | int error; |
911 | |||
912 | p = xdr_inline_decode(xdr, 4); | ||
913 | if (unlikely(p == NULL)) | ||
914 | goto out_overflow; | ||
915 | if (*p++ == xdr_zero) { | ||
916 | p = xdr_inline_decode(xdr, 4); | ||
917 | if (unlikely(p == NULL)) | ||
918 | goto out_overflow; | ||
919 | if (*p++ == xdr_zero) | ||
920 | return -EAGAIN; | ||
921 | entry->eof = 1; | ||
922 | return -EBADCOOKIE; | ||
923 | } | ||
924 | |||
925 | p = xdr_inline_decode(xdr, 4); | ||
926 | if (unlikely(p == NULL)) | ||
927 | goto out_overflow; | ||
928 | entry->ino = be32_to_cpup(p); | ||
929 | |||
930 | error = decode_filename_inline(xdr, &entry->name, &entry->len); | ||
931 | if (unlikely(error)) | ||
932 | return error; | ||
933 | |||
934 | /* | ||
935 | * The type (size and byte order) of nfscookie isn't defined in | ||
936 | * RFC 1094. This implementation assumes that it's an XDR uint32. | ||
937 | */ | ||
938 | entry->prev_cookie = entry->cookie; | ||
939 | p = xdr_inline_decode(xdr, 4); | ||
940 | if (unlikely(p == NULL)) | ||
941 | goto out_overflow; | ||
942 | entry->cookie = be32_to_cpup(p); | ||
579 | 943 | ||
580 | p = xdr_encode_fhandle(p, args->fh); | 944 | entry->d_type = DT_UNKNOWN; |
581 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
582 | 945 | ||
583 | /* Inline the page array */ | ||
584 | replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS_readlinkres_sz) << 2; | ||
585 | xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen); | ||
586 | return 0; | 946 | return 0; |
947 | |||
948 | out_overflow: | ||
949 | print_overflow_msg(__func__, xdr); | ||
950 | return -EAGAIN; | ||
587 | } | 951 | } |
588 | 952 | ||
589 | /* | 953 | /* |
590 | * Decode READLINK reply | 954 | * 2.2.17. readdirres |
955 | * | ||
956 | * union readdirres switch (stat status) { | ||
957 | * case NFS_OK: | ||
958 | * struct { | ||
959 | * entry *entries; | ||
960 | * bool eof; | ||
961 | * } readdirok; | ||
962 | * default: | ||
963 | * void; | ||
964 | * }; | ||
965 | * | ||
966 | * Read the directory contents into the page cache, but don't | ||
967 | * touch them. The actual decoding is done by nfs2_decode_dirent() | ||
968 | * during subsequent nfs_readdir() calls. | ||
591 | */ | 969 | */ |
592 | static int | 970 | static int decode_readdirok(struct xdr_stream *xdr) |
593 | nfs_xdr_readlinkres(struct rpc_rqst *req, __be32 *p, void *dummy) | ||
594 | { | 971 | { |
595 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | 972 | u32 recvd, pglen; |
596 | struct kvec *iov = rcvbuf->head; | ||
597 | size_t hdrlen; | 973 | size_t hdrlen; |
598 | u32 len, recvd; | ||
599 | char *kaddr; | ||
600 | int status; | ||
601 | |||
602 | if ((status = ntohl(*p++))) | ||
603 | return nfs_stat_to_errno(status); | ||
604 | /* Convert length of symlink */ | ||
605 | len = ntohl(*p++); | ||
606 | if (len >= rcvbuf->page_len) { | ||
607 | dprintk("nfs: server returned giant symlink!\n"); | ||
608 | return -ENAMETOOLONG; | ||
609 | } | ||
610 | hdrlen = (u8 *) p - (u8 *) iov->iov_base; | ||
611 | if (iov->iov_len < hdrlen) { | ||
612 | dprintk("NFS: READLINK reply header overflowed:" | ||
613 | "length %Zu > %Zu\n", hdrlen, iov->iov_len); | ||
614 | return -errno_NFSERR_IO; | ||
615 | } else if (iov->iov_len != hdrlen) { | ||
616 | dprintk("NFS: READLINK header is short. iovec will be shifted.\n"); | ||
617 | xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen); | ||
618 | } | ||
619 | recvd = req->rq_rcv_buf.len - hdrlen; | ||
620 | if (recvd < len) { | ||
621 | dprintk("NFS: server cheating in readlink reply: " | ||
622 | "count %u > recvd %u\n", len, recvd); | ||
623 | return -EIO; | ||
624 | } | ||
625 | 974 | ||
626 | /* NULL terminate the string we got */ | 975 | pglen = xdr->buf->page_len; |
627 | kaddr = (char *)kmap_atomic(rcvbuf->pages[0], KM_USER0); | 976 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; |
628 | kaddr[len+rcvbuf->page_base] = '\0'; | 977 | recvd = xdr->buf->len - hdrlen; |
629 | kunmap_atomic(kaddr, KM_USER0); | 978 | if (unlikely(pglen > recvd)) |
630 | return 0; | 979 | goto out_cheating; |
980 | out: | ||
981 | xdr_read_pages(xdr, pglen); | ||
982 | return pglen; | ||
983 | out_cheating: | ||
984 | dprintk("NFS: server cheating in readdir result: " | ||
985 | "pglen %u > recvd %u\n", pglen, recvd); | ||
986 | pglen = recvd; | ||
987 | goto out; | ||
631 | } | 988 | } |
632 | 989 | ||
633 | /* | 990 | static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req, |
634 | * Decode WRITE reply | 991 | struct xdr_stream *xdr, void *__unused) |
635 | */ | ||
636 | static int | ||
637 | nfs_xdr_writeres(struct rpc_rqst *req, __be32 *p, struct nfs_writeres *res) | ||
638 | { | 992 | { |
639 | res->verf->committed = NFS_FILE_SYNC; | 993 | enum nfs_stat status; |
640 | return nfs_xdr_attrstat(req, p, res->fattr); | 994 | int error; |
995 | |||
996 | error = decode_stat(xdr, &status); | ||
997 | if (unlikely(error)) | ||
998 | goto out; | ||
999 | if (status != NFS_OK) | ||
1000 | goto out_default; | ||
1001 | error = decode_readdirok(xdr); | ||
1002 | out: | ||
1003 | return error; | ||
1004 | out_default: | ||
1005 | return nfs_stat_to_errno(status); | ||
641 | } | 1006 | } |
642 | 1007 | ||
643 | /* | 1008 | /* |
644 | * Decode STATFS reply | 1009 | * 2.2.18. statfsres |
1010 | * | ||
1011 | * union statfsres (stat status) { | ||
1012 | * case NFS_OK: | ||
1013 | * struct { | ||
1014 | * unsigned tsize; | ||
1015 | * unsigned bsize; | ||
1016 | * unsigned blocks; | ||
1017 | * unsigned bfree; | ||
1018 | * unsigned bavail; | ||
1019 | * } info; | ||
1020 | * default: | ||
1021 | * void; | ||
1022 | * }; | ||
645 | */ | 1023 | */ |
646 | static int | 1024 | static int decode_info(struct xdr_stream *xdr, struct nfs2_fsstat *result) |
647 | nfs_xdr_statfsres(struct rpc_rqst *req, __be32 *p, struct nfs2_fsstat *res) | ||
648 | { | 1025 | { |
649 | int status; | 1026 | __be32 *p; |
650 | 1027 | ||
651 | if ((status = ntohl(*p++))) | 1028 | p = xdr_inline_decode(xdr, NFS_info_sz << 2); |
652 | return nfs_stat_to_errno(status); | 1029 | if (unlikely(p == NULL)) |
653 | 1030 | goto out_overflow; | |
654 | res->tsize = ntohl(*p++); | 1031 | result->tsize = be32_to_cpup(p++); |
655 | res->bsize = ntohl(*p++); | 1032 | result->bsize = be32_to_cpup(p++); |
656 | res->blocks = ntohl(*p++); | 1033 | result->blocks = be32_to_cpup(p++); |
657 | res->bfree = ntohl(*p++); | 1034 | result->bfree = be32_to_cpup(p++); |
658 | res->bavail = ntohl(*p++); | 1035 | result->bavail = be32_to_cpup(p); |
659 | return 0; | 1036 | return 0; |
1037 | out_overflow: | ||
1038 | print_overflow_msg(__func__, xdr); | ||
1039 | return -EIO; | ||
1040 | } | ||
1041 | |||
1042 | static int nfs2_xdr_dec_statfsres(struct rpc_rqst *req, struct xdr_stream *xdr, | ||
1043 | struct nfs2_fsstat *result) | ||
1044 | { | ||
1045 | enum nfs_stat status; | ||
1046 | int error; | ||
1047 | |||
1048 | error = decode_stat(xdr, &status); | ||
1049 | if (unlikely(error)) | ||
1050 | goto out; | ||
1051 | if (status != NFS_OK) | ||
1052 | goto out_default; | ||
1053 | error = decode_info(xdr, result); | ||
1054 | out: | ||
1055 | return error; | ||
1056 | out_default: | ||
1057 | return nfs_stat_to_errno(status); | ||
660 | } | 1058 | } |
661 | 1059 | ||
1060 | |||
662 | /* | 1061 | /* |
663 | * We need to translate between nfs status return values and | 1062 | * We need to translate between nfs status return values and |
664 | * the local errno values which may not be the same. | 1063 | * the local errno values which may not be the same. |
665 | */ | 1064 | */ |
666 | static struct { | 1065 | static const struct { |
667 | int stat; | 1066 | int stat; |
668 | int errno; | 1067 | int errno; |
669 | } nfs_errtbl[] = { | 1068 | } nfs_errtbl[] = { |
@@ -703,28 +1102,30 @@ static struct { | |||
703 | { -1, -EIO } | 1102 | { -1, -EIO } |
704 | }; | 1103 | }; |
705 | 1104 | ||
706 | /* | 1105 | /** |
707 | * Convert an NFS error code to a local one. | 1106 | * nfs_stat_to_errno - convert an NFS status code to a local errno |
708 | * This one is used jointly by NFSv2 and NFSv3. | 1107 | * @status: NFS status code to convert |
1108 | * | ||
1109 | * Returns a local errno value, or -EIO if the NFS status code is | ||
1110 | * not recognized. This function is used jointly by NFSv2 and NFSv3. | ||
709 | */ | 1111 | */ |
710 | int | 1112 | int nfs_stat_to_errno(enum nfs_stat status) |
711 | nfs_stat_to_errno(int stat) | ||
712 | { | 1113 | { |
713 | int i; | 1114 | int i; |
714 | 1115 | ||
715 | for (i = 0; nfs_errtbl[i].stat != -1; i++) { | 1116 | for (i = 0; nfs_errtbl[i].stat != -1; i++) { |
716 | if (nfs_errtbl[i].stat == stat) | 1117 | if (nfs_errtbl[i].stat == (int)status) |
717 | return nfs_errtbl[i].errno; | 1118 | return nfs_errtbl[i].errno; |
718 | } | 1119 | } |
719 | dprintk("nfs_stat_to_errno: bad nfs status return value: %d\n", stat); | 1120 | dprintk("NFS: Unrecognized nfs status value: %u\n", status); |
720 | return nfs_errtbl[i].errno; | 1121 | return nfs_errtbl[i].errno; |
721 | } | 1122 | } |
722 | 1123 | ||
723 | #define PROC(proc, argtype, restype, timer) \ | 1124 | #define PROC(proc, argtype, restype, timer) \ |
724 | [NFSPROC_##proc] = { \ | 1125 | [NFSPROC_##proc] = { \ |
725 | .p_proc = NFSPROC_##proc, \ | 1126 | .p_proc = NFSPROC_##proc, \ |
726 | .p_encode = (kxdrproc_t) nfs_xdr_##argtype, \ | 1127 | .p_encode = (kxdreproc_t)nfs2_xdr_enc_##argtype, \ |
727 | .p_decode = (kxdrproc_t) nfs_xdr_##restype, \ | 1128 | .p_decode = (kxdrdproc_t)nfs2_xdr_dec_##restype, \ |
728 | .p_arglen = NFS_##argtype##_sz, \ | 1129 | .p_arglen = NFS_##argtype##_sz, \ |
729 | .p_replen = NFS_##restype##_sz, \ | 1130 | .p_replen = NFS_##restype##_sz, \ |
730 | .p_timer = timer, \ | 1131 | .p_timer = timer, \ |
@@ -732,21 +1133,21 @@ nfs_stat_to_errno(int stat) | |||
732 | .p_name = #proc, \ | 1133 | .p_name = #proc, \ |
733 | } | 1134 | } |
734 | struct rpc_procinfo nfs_procedures[] = { | 1135 | struct rpc_procinfo nfs_procedures[] = { |
735 | PROC(GETATTR, fhandle, attrstat, 1), | 1136 | PROC(GETATTR, fhandle, attrstat, 1), |
736 | PROC(SETATTR, sattrargs, attrstat, 0), | 1137 | PROC(SETATTR, sattrargs, attrstat, 0), |
737 | PROC(LOOKUP, diropargs, diropres, 2), | 1138 | PROC(LOOKUP, diropargs, diropres, 2), |
738 | PROC(READLINK, readlinkargs, readlinkres, 3), | 1139 | PROC(READLINK, readlinkargs, readlinkres, 3), |
739 | PROC(READ, readargs, readres, 3), | 1140 | PROC(READ, readargs, readres, 3), |
740 | PROC(WRITE, writeargs, writeres, 4), | 1141 | PROC(WRITE, writeargs, writeres, 4), |
741 | PROC(CREATE, createargs, diropres, 0), | 1142 | PROC(CREATE, createargs, diropres, 0), |
742 | PROC(REMOVE, removeargs, stat, 0), | 1143 | PROC(REMOVE, removeargs, stat, 0), |
743 | PROC(RENAME, renameargs, stat, 0), | 1144 | PROC(RENAME, renameargs, stat, 0), |
744 | PROC(LINK, linkargs, stat, 0), | 1145 | PROC(LINK, linkargs, stat, 0), |
745 | PROC(SYMLINK, symlinkargs, stat, 0), | 1146 | PROC(SYMLINK, symlinkargs, stat, 0), |
746 | PROC(MKDIR, createargs, diropres, 0), | 1147 | PROC(MKDIR, createargs, diropres, 0), |
747 | PROC(RMDIR, diropargs, stat, 0), | 1148 | PROC(RMDIR, diropargs, stat, 0), |
748 | PROC(READDIR, readdirargs, readdirres, 3), | 1149 | PROC(READDIR, readdirargs, readdirres, 3), |
749 | PROC(STATFS, fhandle, statfsres, 0), | 1150 | PROC(STATFS, fhandle, statfsres, 0), |
750 | }; | 1151 | }; |
751 | 1152 | ||
752 | struct rpc_version nfs_version2 = { | 1153 | struct rpc_version nfs_version2 = { |