diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/nfs/nfs3xdr.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/nfs/nfs3xdr.c')
-rw-r--r-- | fs/nfs/nfs3xdr.c | 1023 |
1 files changed, 1023 insertions, 0 deletions
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c new file mode 100644 index 000000000000..a3593d47e5ab --- /dev/null +++ b/fs/nfs/nfs3xdr.c | |||
@@ -0,0 +1,1023 @@ | |||
1 | /* | ||
2 | * linux/fs/nfs/nfs3xdr.c | ||
3 | * | ||
4 | * XDR functions to encode/decode NFSv3 RPC arguments and results. | ||
5 | * | ||
6 | * Copyright (C) 1996, 1997 Olaf Kirch | ||
7 | */ | ||
8 | |||
9 | #include <linux/param.h> | ||
10 | #include <linux/time.h> | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/utsname.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/string.h> | ||
16 | #include <linux/in.h> | ||
17 | #include <linux/pagemap.h> | ||
18 | #include <linux/proc_fs.h> | ||
19 | #include <linux/kdev_t.h> | ||
20 | #include <linux/sunrpc/clnt.h> | ||
21 | #include <linux/nfs.h> | ||
22 | #include <linux/nfs3.h> | ||
23 | #include <linux/nfs_fs.h> | ||
24 | |||
25 | #define NFSDBG_FACILITY NFSDBG_XDR | ||
26 | |||
27 | /* Mapping from NFS error code to "errno" error code. */ | ||
28 | #define errno_NFSERR_IO EIO | ||
29 | |||
30 | extern int nfs_stat_to_errno(int); | ||
31 | |||
32 | /* | ||
33 | * Declare the space requirements for NFS arguments and replies as | ||
34 | * number of 32bit-words | ||
35 | */ | ||
36 | #define NFS3_fhandle_sz (1+16) | ||
37 | #define NFS3_fh_sz (NFS3_fhandle_sz) /* shorthand */ | ||
38 | #define NFS3_sattr_sz (15) | ||
39 | #define NFS3_filename_sz (1+(NFS3_MAXNAMLEN>>2)) | ||
40 | #define NFS3_path_sz (1+(NFS3_MAXPATHLEN>>2)) | ||
41 | #define NFS3_fattr_sz (21) | ||
42 | #define NFS3_wcc_attr_sz (6) | ||
43 | #define NFS3_pre_op_attr_sz (1+NFS3_wcc_attr_sz) | ||
44 | #define NFS3_post_op_attr_sz (1+NFS3_fattr_sz) | ||
45 | #define NFS3_wcc_data_sz (NFS3_pre_op_attr_sz+NFS3_post_op_attr_sz) | ||
46 | #define NFS3_fsstat_sz | ||
47 | #define NFS3_fsinfo_sz | ||
48 | #define NFS3_pathconf_sz | ||
49 | #define NFS3_entry_sz (NFS3_filename_sz+3) | ||
50 | |||
51 | #define NFS3_sattrargs_sz (NFS3_fh_sz+NFS3_sattr_sz+3) | ||
52 | #define NFS3_diropargs_sz (NFS3_fh_sz+NFS3_filename_sz) | ||
53 | #define NFS3_accessargs_sz (NFS3_fh_sz+1) | ||
54 | #define NFS3_readlinkargs_sz (NFS3_fh_sz) | ||
55 | #define NFS3_readargs_sz (NFS3_fh_sz+3) | ||
56 | #define NFS3_writeargs_sz (NFS3_fh_sz+5) | ||
57 | #define NFS3_createargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz) | ||
58 | #define NFS3_mkdirargs_sz (NFS3_diropargs_sz+NFS3_sattr_sz) | ||
59 | #define NFS3_symlinkargs_sz (NFS3_diropargs_sz+NFS3_path_sz+NFS3_sattr_sz) | ||
60 | #define NFS3_mknodargs_sz (NFS3_diropargs_sz+2+NFS3_sattr_sz) | ||
61 | #define NFS3_renameargs_sz (NFS3_diropargs_sz+NFS3_diropargs_sz) | ||
62 | #define NFS3_linkargs_sz (NFS3_fh_sz+NFS3_diropargs_sz) | ||
63 | #define NFS3_readdirargs_sz (NFS3_fh_sz+2) | ||
64 | #define NFS3_commitargs_sz (NFS3_fh_sz+3) | ||
65 | |||
66 | #define NFS3_attrstat_sz (1+NFS3_fattr_sz) | ||
67 | #define NFS3_wccstat_sz (1+NFS3_wcc_data_sz) | ||
68 | #define NFS3_lookupres_sz (1+NFS3_fh_sz+(2 * NFS3_post_op_attr_sz)) | ||
69 | #define NFS3_accessres_sz (1+NFS3_post_op_attr_sz+1) | ||
70 | #define NFS3_readlinkres_sz (1+NFS3_post_op_attr_sz+1) | ||
71 | #define NFS3_readres_sz (1+NFS3_post_op_attr_sz+3) | ||
72 | #define NFS3_writeres_sz (1+NFS3_wcc_data_sz+4) | ||
73 | #define NFS3_createres_sz (1+NFS3_fh_sz+NFS3_post_op_attr_sz+NFS3_wcc_data_sz) | ||
74 | #define NFS3_renameres_sz (1+(2 * NFS3_wcc_data_sz)) | ||
75 | #define NFS3_linkres_sz (1+NFS3_post_op_attr_sz+NFS3_wcc_data_sz) | ||
76 | #define NFS3_readdirres_sz (1+NFS3_post_op_attr_sz+2) | ||
77 | #define NFS3_fsstatres_sz (1+NFS3_post_op_attr_sz+13) | ||
78 | #define NFS3_fsinfores_sz (1+NFS3_post_op_attr_sz+12) | ||
79 | #define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6) | ||
80 | #define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2) | ||
81 | |||
82 | /* | ||
83 | * Map file type to S_IFMT bits | ||
84 | */ | ||
85 | static struct { | ||
86 | unsigned int mode; | ||
87 | unsigned int nfs2type; | ||
88 | } nfs_type2fmt[] = { | ||
89 | { 0, NFNON }, | ||
90 | { S_IFREG, NFREG }, | ||
91 | { S_IFDIR, NFDIR }, | ||
92 | { S_IFBLK, NFBLK }, | ||
93 | { S_IFCHR, NFCHR }, | ||
94 | { S_IFLNK, NFLNK }, | ||
95 | { S_IFSOCK, NFSOCK }, | ||
96 | { S_IFIFO, NFFIFO }, | ||
97 | { 0, NFBAD } | ||
98 | }; | ||
99 | |||
100 | /* | ||
101 | * Common NFS XDR functions as inlines | ||
102 | */ | ||
103 | static inline u32 * | ||
104 | xdr_encode_fhandle(u32 *p, struct nfs_fh *fh) | ||
105 | { | ||
106 | return xdr_encode_array(p, fh->data, fh->size); | ||
107 | } | ||
108 | |||
109 | static inline u32 * | ||
110 | xdr_decode_fhandle(u32 *p, struct nfs_fh *fh) | ||
111 | { | ||
112 | if ((fh->size = ntohl(*p++)) <= NFS3_FHSIZE) { | ||
113 | memcpy(fh->data, p, fh->size); | ||
114 | return p + XDR_QUADLEN(fh->size); | ||
115 | } | ||
116 | return NULL; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * Encode/decode time. | ||
121 | */ | ||
122 | static inline u32 * | ||
123 | xdr_encode_time3(u32 *p, struct timespec *timep) | ||
124 | { | ||
125 | *p++ = htonl(timep->tv_sec); | ||
126 | *p++ = htonl(timep->tv_nsec); | ||
127 | return p; | ||
128 | } | ||
129 | |||
130 | static inline u32 * | ||
131 | xdr_decode_time3(u32 *p, struct timespec *timep) | ||
132 | { | ||
133 | timep->tv_sec = ntohl(*p++); | ||
134 | timep->tv_nsec = ntohl(*p++); | ||
135 | return p; | ||
136 | } | ||
137 | |||
138 | static u32 * | ||
139 | xdr_decode_fattr(u32 *p, struct nfs_fattr *fattr) | ||
140 | { | ||
141 | unsigned int type, major, minor; | ||
142 | int fmode; | ||
143 | |||
144 | type = ntohl(*p++); | ||
145 | if (type >= NF3BAD) | ||
146 | type = NF3BAD; | ||
147 | fmode = nfs_type2fmt[type].mode; | ||
148 | fattr->type = nfs_type2fmt[type].nfs2type; | ||
149 | fattr->mode = (ntohl(*p++) & ~S_IFMT) | fmode; | ||
150 | fattr->nlink = ntohl(*p++); | ||
151 | fattr->uid = ntohl(*p++); | ||
152 | fattr->gid = ntohl(*p++); | ||
153 | p = xdr_decode_hyper(p, &fattr->size); | ||
154 | p = xdr_decode_hyper(p, &fattr->du.nfs3.used); | ||
155 | |||
156 | /* Turn remote device info into Linux-specific dev_t */ | ||
157 | major = ntohl(*p++); | ||
158 | minor = ntohl(*p++); | ||
159 | fattr->rdev = MKDEV(major, minor); | ||
160 | if (MAJOR(fattr->rdev) != major || MINOR(fattr->rdev) != minor) | ||
161 | fattr->rdev = 0; | ||
162 | |||
163 | p = xdr_decode_hyper(p, &fattr->fsid_u.nfs3); | ||
164 | p = xdr_decode_hyper(p, &fattr->fileid); | ||
165 | p = xdr_decode_time3(p, &fattr->atime); | ||
166 | p = xdr_decode_time3(p, &fattr->mtime); | ||
167 | p = xdr_decode_time3(p, &fattr->ctime); | ||
168 | |||
169 | /* Update the mode bits */ | ||
170 | fattr->valid |= (NFS_ATTR_FATTR | NFS_ATTR_FATTR_V3); | ||
171 | fattr->timestamp = jiffies; | ||
172 | return p; | ||
173 | } | ||
174 | |||
175 | static inline u32 * | ||
176 | xdr_encode_sattr(u32 *p, struct iattr *attr) | ||
177 | { | ||
178 | if (attr->ia_valid & ATTR_MODE) { | ||
179 | *p++ = xdr_one; | ||
180 | *p++ = htonl(attr->ia_mode); | ||
181 | } else { | ||
182 | *p++ = xdr_zero; | ||
183 | } | ||
184 | if (attr->ia_valid & ATTR_UID) { | ||
185 | *p++ = xdr_one; | ||
186 | *p++ = htonl(attr->ia_uid); | ||
187 | } else { | ||
188 | *p++ = xdr_zero; | ||
189 | } | ||
190 | if (attr->ia_valid & ATTR_GID) { | ||
191 | *p++ = xdr_one; | ||
192 | *p++ = htonl(attr->ia_gid); | ||
193 | } else { | ||
194 | *p++ = xdr_zero; | ||
195 | } | ||
196 | if (attr->ia_valid & ATTR_SIZE) { | ||
197 | *p++ = xdr_one; | ||
198 | p = xdr_encode_hyper(p, (__u64) attr->ia_size); | ||
199 | } else { | ||
200 | *p++ = xdr_zero; | ||
201 | } | ||
202 | if (attr->ia_valid & ATTR_ATIME_SET) { | ||
203 | *p++ = xdr_two; | ||
204 | p = xdr_encode_time3(p, &attr->ia_atime); | ||
205 | } else if (attr->ia_valid & ATTR_ATIME) { | ||
206 | *p++ = xdr_one; | ||
207 | } else { | ||
208 | *p++ = xdr_zero; | ||
209 | } | ||
210 | if (attr->ia_valid & ATTR_MTIME_SET) { | ||
211 | *p++ = xdr_two; | ||
212 | p = xdr_encode_time3(p, &attr->ia_mtime); | ||
213 | } else if (attr->ia_valid & ATTR_MTIME) { | ||
214 | *p++ = xdr_one; | ||
215 | } else { | ||
216 | *p++ = xdr_zero; | ||
217 | } | ||
218 | return p; | ||
219 | } | ||
220 | |||
221 | static inline u32 * | ||
222 | xdr_decode_wcc_attr(u32 *p, struct nfs_fattr *fattr) | ||
223 | { | ||
224 | p = xdr_decode_hyper(p, &fattr->pre_size); | ||
225 | p = xdr_decode_time3(p, &fattr->pre_mtime); | ||
226 | p = xdr_decode_time3(p, &fattr->pre_ctime); | ||
227 | fattr->valid |= NFS_ATTR_WCC; | ||
228 | return p; | ||
229 | } | ||
230 | |||
231 | static inline u32 * | ||
232 | xdr_decode_post_op_attr(u32 *p, struct nfs_fattr *fattr) | ||
233 | { | ||
234 | if (*p++) | ||
235 | p = xdr_decode_fattr(p, fattr); | ||
236 | return p; | ||
237 | } | ||
238 | |||
239 | static inline u32 * | ||
240 | xdr_decode_pre_op_attr(u32 *p, struct nfs_fattr *fattr) | ||
241 | { | ||
242 | if (*p++) | ||
243 | return xdr_decode_wcc_attr(p, fattr); | ||
244 | return p; | ||
245 | } | ||
246 | |||
247 | |||
248 | static inline u32 * | ||
249 | xdr_decode_wcc_data(u32 *p, struct nfs_fattr *fattr) | ||
250 | { | ||
251 | p = xdr_decode_pre_op_attr(p, fattr); | ||
252 | return xdr_decode_post_op_attr(p, fattr); | ||
253 | } | ||
254 | |||
255 | /* | ||
256 | * NFS encode functions | ||
257 | */ | ||
258 | |||
259 | /* | ||
260 | * Encode file handle argument | ||
261 | */ | ||
262 | static int | ||
263 | nfs3_xdr_fhandle(struct rpc_rqst *req, u32 *p, struct nfs_fh *fh) | ||
264 | { | ||
265 | p = xdr_encode_fhandle(p, fh); | ||
266 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | /* | ||
271 | * Encode SETATTR arguments | ||
272 | */ | ||
273 | static int | ||
274 | nfs3_xdr_sattrargs(struct rpc_rqst *req, u32 *p, struct nfs3_sattrargs *args) | ||
275 | { | ||
276 | p = xdr_encode_fhandle(p, args->fh); | ||
277 | p = xdr_encode_sattr(p, args->sattr); | ||
278 | *p++ = htonl(args->guard); | ||
279 | if (args->guard) | ||
280 | p = xdr_encode_time3(p, &args->guardtime); | ||
281 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | /* | ||
286 | * Encode directory ops argument | ||
287 | */ | ||
288 | static int | ||
289 | nfs3_xdr_diropargs(struct rpc_rqst *req, u32 *p, struct nfs3_diropargs *args) | ||
290 | { | ||
291 | p = xdr_encode_fhandle(p, args->fh); | ||
292 | p = xdr_encode_array(p, args->name, args->len); | ||
293 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * Encode access() argument | ||
299 | */ | ||
300 | static int | ||
301 | nfs3_xdr_accessargs(struct rpc_rqst *req, u32 *p, struct nfs3_accessargs *args) | ||
302 | { | ||
303 | p = xdr_encode_fhandle(p, args->fh); | ||
304 | *p++ = htonl(args->access); | ||
305 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | /* | ||
310 | * Arguments to a READ call. Since we read data directly into the page | ||
311 | * cache, we also set up the reply iovec here so that iov[1] points | ||
312 | * exactly to the page we want to fetch. | ||
313 | */ | ||
314 | static int | ||
315 | nfs3_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args) | ||
316 | { | ||
317 | struct rpc_auth *auth = req->rq_task->tk_auth; | ||
318 | unsigned int replen; | ||
319 | u32 count = args->count; | ||
320 | |||
321 | p = xdr_encode_fhandle(p, args->fh); | ||
322 | p = xdr_encode_hyper(p, args->offset); | ||
323 | *p++ = htonl(count); | ||
324 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
325 | |||
326 | /* Inline the page array */ | ||
327 | replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readres_sz) << 2; | ||
328 | xdr_inline_pages(&req->rq_rcv_buf, replen, | ||
329 | args->pages, args->pgbase, count); | ||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * Write arguments. Splice the buffer to be written into the iovec. | ||
335 | */ | ||
336 | static int | ||
337 | nfs3_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args) | ||
338 | { | ||
339 | struct xdr_buf *sndbuf = &req->rq_snd_buf; | ||
340 | u32 count = args->count; | ||
341 | |||
342 | p = xdr_encode_fhandle(p, args->fh); | ||
343 | p = xdr_encode_hyper(p, args->offset); | ||
344 | *p++ = htonl(count); | ||
345 | *p++ = htonl(args->stable); | ||
346 | *p++ = htonl(count); | ||
347 | sndbuf->len = xdr_adjust_iovec(sndbuf->head, p); | ||
348 | |||
349 | /* Copy the page array */ | ||
350 | xdr_encode_pages(sndbuf, args->pages, args->pgbase, count); | ||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | /* | ||
355 | * Encode CREATE arguments | ||
356 | */ | ||
357 | static int | ||
358 | nfs3_xdr_createargs(struct rpc_rqst *req, u32 *p, struct nfs3_createargs *args) | ||
359 | { | ||
360 | p = xdr_encode_fhandle(p, args->fh); | ||
361 | p = xdr_encode_array(p, args->name, args->len); | ||
362 | |||
363 | *p++ = htonl(args->createmode); | ||
364 | if (args->createmode == NFS3_CREATE_EXCLUSIVE) { | ||
365 | *p++ = args->verifier[0]; | ||
366 | *p++ = args->verifier[1]; | ||
367 | } else | ||
368 | p = xdr_encode_sattr(p, args->sattr); | ||
369 | |||
370 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | /* | ||
375 | * Encode MKDIR arguments | ||
376 | */ | ||
377 | static int | ||
378 | nfs3_xdr_mkdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_mkdirargs *args) | ||
379 | { | ||
380 | p = xdr_encode_fhandle(p, args->fh); | ||
381 | p = xdr_encode_array(p, args->name, args->len); | ||
382 | p = xdr_encode_sattr(p, args->sattr); | ||
383 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | /* | ||
388 | * Encode SYMLINK arguments | ||
389 | */ | ||
390 | static int | ||
391 | nfs3_xdr_symlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_symlinkargs *args) | ||
392 | { | ||
393 | p = xdr_encode_fhandle(p, args->fromfh); | ||
394 | p = xdr_encode_array(p, args->fromname, args->fromlen); | ||
395 | p = xdr_encode_sattr(p, args->sattr); | ||
396 | p = xdr_encode_array(p, args->topath, args->tolen); | ||
397 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | /* | ||
402 | * Encode MKNOD arguments | ||
403 | */ | ||
404 | static int | ||
405 | nfs3_xdr_mknodargs(struct rpc_rqst *req, u32 *p, struct nfs3_mknodargs *args) | ||
406 | { | ||
407 | p = xdr_encode_fhandle(p, args->fh); | ||
408 | p = xdr_encode_array(p, args->name, args->len); | ||
409 | *p++ = htonl(args->type); | ||
410 | p = xdr_encode_sattr(p, args->sattr); | ||
411 | if (args->type == NF3CHR || args->type == NF3BLK) { | ||
412 | *p++ = htonl(MAJOR(args->rdev)); | ||
413 | *p++ = htonl(MINOR(args->rdev)); | ||
414 | } | ||
415 | |||
416 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | /* | ||
421 | * Encode RENAME arguments | ||
422 | */ | ||
423 | static int | ||
424 | nfs3_xdr_renameargs(struct rpc_rqst *req, u32 *p, struct nfs3_renameargs *args) | ||
425 | { | ||
426 | p = xdr_encode_fhandle(p, args->fromfh); | ||
427 | p = xdr_encode_array(p, args->fromname, args->fromlen); | ||
428 | p = xdr_encode_fhandle(p, args->tofh); | ||
429 | p = xdr_encode_array(p, args->toname, args->tolen); | ||
430 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | /* | ||
435 | * Encode LINK arguments | ||
436 | */ | ||
437 | static int | ||
438 | nfs3_xdr_linkargs(struct rpc_rqst *req, u32 *p, struct nfs3_linkargs *args) | ||
439 | { | ||
440 | p = xdr_encode_fhandle(p, args->fromfh); | ||
441 | p = xdr_encode_fhandle(p, args->tofh); | ||
442 | p = xdr_encode_array(p, args->toname, args->tolen); | ||
443 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | /* | ||
448 | * Encode arguments to readdir call | ||
449 | */ | ||
450 | static int | ||
451 | nfs3_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs3_readdirargs *args) | ||
452 | { | ||
453 | struct rpc_auth *auth = req->rq_task->tk_auth; | ||
454 | unsigned int replen; | ||
455 | u32 count = args->count; | ||
456 | |||
457 | p = xdr_encode_fhandle(p, args->fh); | ||
458 | p = xdr_encode_hyper(p, args->cookie); | ||
459 | *p++ = args->verf[0]; | ||
460 | *p++ = args->verf[1]; | ||
461 | if (args->plus) { | ||
462 | /* readdirplus: need dircount + buffer size. | ||
463 | * We just make sure we make dircount big enough */ | ||
464 | *p++ = htonl(count >> 3); | ||
465 | } | ||
466 | *p++ = htonl(count); | ||
467 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
468 | |||
469 | /* Inline the page array */ | ||
470 | replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readdirres_sz) << 2; | ||
471 | xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, count); | ||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | /* | ||
476 | * Decode the result of a readdir call. | ||
477 | * We just check for syntactical correctness. | ||
478 | */ | ||
479 | static int | ||
480 | nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res) | ||
481 | { | ||
482 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | ||
483 | struct kvec *iov = rcvbuf->head; | ||
484 | struct page **page; | ||
485 | int hdrlen, recvd; | ||
486 | int status, nr; | ||
487 | unsigned int len, pglen; | ||
488 | u32 *entry, *end, *kaddr; | ||
489 | |||
490 | status = ntohl(*p++); | ||
491 | /* Decode post_op_attrs */ | ||
492 | p = xdr_decode_post_op_attr(p, res->dir_attr); | ||
493 | if (status) | ||
494 | return -nfs_stat_to_errno(status); | ||
495 | /* Decode verifier cookie */ | ||
496 | if (res->verf) { | ||
497 | res->verf[0] = *p++; | ||
498 | res->verf[1] = *p++; | ||
499 | } else { | ||
500 | p += 2; | ||
501 | } | ||
502 | |||
503 | hdrlen = (u8 *) p - (u8 *) iov->iov_base; | ||
504 | if (iov->iov_len < hdrlen) { | ||
505 | printk(KERN_WARNING "NFS: READDIR reply header overflowed:" | ||
506 | "length %d > %Zu\n", hdrlen, iov->iov_len); | ||
507 | return -errno_NFSERR_IO; | ||
508 | } else if (iov->iov_len != hdrlen) { | ||
509 | dprintk("NFS: READDIR header is short. iovec will be shifted.\n"); | ||
510 | xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen); | ||
511 | } | ||
512 | |||
513 | pglen = rcvbuf->page_len; | ||
514 | recvd = rcvbuf->len - hdrlen; | ||
515 | if (pglen > recvd) | ||
516 | pglen = recvd; | ||
517 | page = rcvbuf->pages; | ||
518 | kaddr = p = (u32 *)kmap_atomic(*page, KM_USER0); | ||
519 | end = (u32 *)((char *)p + pglen); | ||
520 | entry = p; | ||
521 | for (nr = 0; *p++; nr++) { | ||
522 | if (p + 3 > end) | ||
523 | goto short_pkt; | ||
524 | p += 2; /* inode # */ | ||
525 | len = ntohl(*p++); /* string length */ | ||
526 | p += XDR_QUADLEN(len) + 2; /* name + cookie */ | ||
527 | if (len > NFS3_MAXNAMLEN) { | ||
528 | printk(KERN_WARNING "NFS: giant filename in readdir (len %x)!\n", | ||
529 | len); | ||
530 | goto err_unmap; | ||
531 | } | ||
532 | |||
533 | if (res->plus) { | ||
534 | /* post_op_attr */ | ||
535 | if (p + 2 > end) | ||
536 | goto short_pkt; | ||
537 | if (*p++) { | ||
538 | p += 21; | ||
539 | if (p + 1 > end) | ||
540 | goto short_pkt; | ||
541 | } | ||
542 | /* post_op_fh3 */ | ||
543 | if (*p++) { | ||
544 | if (p + 1 > end) | ||
545 | goto short_pkt; | ||
546 | len = ntohl(*p++); | ||
547 | if (len > NFS3_FHSIZE) { | ||
548 | printk(KERN_WARNING "NFS: giant filehandle in " | ||
549 | "readdir (len %x)!\n", len); | ||
550 | goto err_unmap; | ||
551 | } | ||
552 | p += XDR_QUADLEN(len); | ||
553 | } | ||
554 | } | ||
555 | |||
556 | if (p + 2 > end) | ||
557 | goto short_pkt; | ||
558 | entry = p; | ||
559 | } | ||
560 | if (!nr && (entry[0] != 0 || entry[1] == 0)) | ||
561 | goto short_pkt; | ||
562 | out: | ||
563 | kunmap_atomic(kaddr, KM_USER0); | ||
564 | return nr; | ||
565 | short_pkt: | ||
566 | entry[0] = entry[1] = 0; | ||
567 | /* truncate listing ? */ | ||
568 | if (!nr) { | ||
569 | printk(KERN_NOTICE "NFS: readdir reply truncated!\n"); | ||
570 | entry[1] = 1; | ||
571 | } | ||
572 | goto out; | ||
573 | err_unmap: | ||
574 | nr = -errno_NFSERR_IO; | ||
575 | goto out; | ||
576 | } | ||
577 | |||
578 | u32 * | ||
579 | nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus) | ||
580 | { | ||
581 | struct nfs_entry old = *entry; | ||
582 | |||
583 | if (!*p++) { | ||
584 | if (!*p) | ||
585 | return ERR_PTR(-EAGAIN); | ||
586 | entry->eof = 1; | ||
587 | return ERR_PTR(-EBADCOOKIE); | ||
588 | } | ||
589 | |||
590 | p = xdr_decode_hyper(p, &entry->ino); | ||
591 | entry->len = ntohl(*p++); | ||
592 | entry->name = (const char *) p; | ||
593 | p += XDR_QUADLEN(entry->len); | ||
594 | entry->prev_cookie = entry->cookie; | ||
595 | p = xdr_decode_hyper(p, &entry->cookie); | ||
596 | |||
597 | if (plus) { | ||
598 | entry->fattr->valid = 0; | ||
599 | p = xdr_decode_post_op_attr(p, entry->fattr); | ||
600 | /* In fact, a post_op_fh3: */ | ||
601 | if (*p++) { | ||
602 | p = xdr_decode_fhandle(p, entry->fh); | ||
603 | /* Ugh -- server reply was truncated */ | ||
604 | if (p == NULL) { | ||
605 | dprintk("NFS: FH truncated\n"); | ||
606 | *entry = old; | ||
607 | return ERR_PTR(-EAGAIN); | ||
608 | } | ||
609 | } else | ||
610 | memset((u8*)(entry->fh), 0, sizeof(*entry->fh)); | ||
611 | } | ||
612 | |||
613 | entry->eof = !p[0] && p[1]; | ||
614 | return p; | ||
615 | } | ||
616 | |||
617 | /* | ||
618 | * Encode COMMIT arguments | ||
619 | */ | ||
620 | static int | ||
621 | nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args) | ||
622 | { | ||
623 | p = xdr_encode_fhandle(p, args->fh); | ||
624 | p = xdr_encode_hyper(p, args->offset); | ||
625 | *p++ = htonl(args->count); | ||
626 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | /* | ||
631 | * NFS XDR decode functions | ||
632 | */ | ||
633 | |||
634 | /* | ||
635 | * Decode attrstat reply. | ||
636 | */ | ||
637 | static int | ||
638 | nfs3_xdr_attrstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr) | ||
639 | { | ||
640 | int status; | ||
641 | |||
642 | if ((status = ntohl(*p++))) | ||
643 | return -nfs_stat_to_errno(status); | ||
644 | xdr_decode_fattr(p, fattr); | ||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | /* | ||
649 | * Decode status+wcc_data reply | ||
650 | * SATTR, REMOVE, RMDIR | ||
651 | */ | ||
652 | static int | ||
653 | nfs3_xdr_wccstat(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr) | ||
654 | { | ||
655 | int status; | ||
656 | |||
657 | if ((status = ntohl(*p++))) | ||
658 | status = -nfs_stat_to_errno(status); | ||
659 | xdr_decode_wcc_data(p, fattr); | ||
660 | return status; | ||
661 | } | ||
662 | |||
663 | /* | ||
664 | * Decode LOOKUP reply | ||
665 | */ | ||
666 | static int | ||
667 | nfs3_xdr_lookupres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res) | ||
668 | { | ||
669 | int status; | ||
670 | |||
671 | if ((status = ntohl(*p++))) { | ||
672 | status = -nfs_stat_to_errno(status); | ||
673 | } else { | ||
674 | if (!(p = xdr_decode_fhandle(p, res->fh))) | ||
675 | return -errno_NFSERR_IO; | ||
676 | p = xdr_decode_post_op_attr(p, res->fattr); | ||
677 | } | ||
678 | xdr_decode_post_op_attr(p, res->dir_attr); | ||
679 | return status; | ||
680 | } | ||
681 | |||
682 | /* | ||
683 | * Decode ACCESS reply | ||
684 | */ | ||
685 | static int | ||
686 | nfs3_xdr_accessres(struct rpc_rqst *req, u32 *p, struct nfs3_accessres *res) | ||
687 | { | ||
688 | int status = ntohl(*p++); | ||
689 | |||
690 | p = xdr_decode_post_op_attr(p, res->fattr); | ||
691 | if (status) | ||
692 | return -nfs_stat_to_errno(status); | ||
693 | res->access = ntohl(*p++); | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | static int | ||
698 | nfs3_xdr_readlinkargs(struct rpc_rqst *req, u32 *p, struct nfs3_readlinkargs *args) | ||
699 | { | ||
700 | struct rpc_auth *auth = req->rq_task->tk_auth; | ||
701 | unsigned int replen; | ||
702 | |||
703 | p = xdr_encode_fhandle(p, args->fh); | ||
704 | req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | ||
705 | |||
706 | /* Inline the page array */ | ||
707 | replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS3_readlinkres_sz) << 2; | ||
708 | xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, args->pgbase, args->pglen); | ||
709 | return 0; | ||
710 | } | ||
711 | |||
712 | /* | ||
713 | * Decode READLINK reply | ||
714 | */ | ||
715 | static int | ||
716 | nfs3_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr) | ||
717 | { | ||
718 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | ||
719 | struct kvec *iov = rcvbuf->head; | ||
720 | int hdrlen, len, recvd; | ||
721 | char *kaddr; | ||
722 | int status; | ||
723 | |||
724 | status = ntohl(*p++); | ||
725 | p = xdr_decode_post_op_attr(p, fattr); | ||
726 | |||
727 | if (status != 0) | ||
728 | return -nfs_stat_to_errno(status); | ||
729 | |||
730 | /* Convert length of symlink */ | ||
731 | len = ntohl(*p++); | ||
732 | if (len >= rcvbuf->page_len || len <= 0) { | ||
733 | dprintk(KERN_WARNING "nfs: server returned giant symlink!\n"); | ||
734 | return -ENAMETOOLONG; | ||
735 | } | ||
736 | |||
737 | hdrlen = (u8 *) p - (u8 *) iov->iov_base; | ||
738 | if (iov->iov_len < hdrlen) { | ||
739 | printk(KERN_WARNING "NFS: READLINK reply header overflowed:" | ||
740 | "length %d > %Zu\n", hdrlen, iov->iov_len); | ||
741 | return -errno_NFSERR_IO; | ||
742 | } else if (iov->iov_len != hdrlen) { | ||
743 | dprintk("NFS: READLINK header is short. iovec will be shifted.\n"); | ||
744 | xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen); | ||
745 | } | ||
746 | recvd = req->rq_rcv_buf.len - hdrlen; | ||
747 | if (recvd < len) { | ||
748 | printk(KERN_WARNING "NFS: server cheating in readlink reply: " | ||
749 | "count %u > recvd %u\n", len, recvd); | ||
750 | return -EIO; | ||
751 | } | ||
752 | |||
753 | /* NULL terminate the string we got */ | ||
754 | kaddr = (char*)kmap_atomic(rcvbuf->pages[0], KM_USER0); | ||
755 | kaddr[len+rcvbuf->page_base] = '\0'; | ||
756 | kunmap_atomic(kaddr, KM_USER0); | ||
757 | return 0; | ||
758 | } | ||
759 | |||
760 | /* | ||
761 | * Decode READ reply | ||
762 | */ | ||
763 | static int | ||
764 | nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res) | ||
765 | { | ||
766 | struct kvec *iov = req->rq_rcv_buf.head; | ||
767 | int status, count, ocount, recvd, hdrlen; | ||
768 | |||
769 | status = ntohl(*p++); | ||
770 | p = xdr_decode_post_op_attr(p, res->fattr); | ||
771 | |||
772 | if (status != 0) | ||
773 | return -nfs_stat_to_errno(status); | ||
774 | |||
775 | /* Decode reply could and EOF flag. NFSv3 is somewhat redundant | ||
776 | * in that it puts the count both in the res struct and in the | ||
777 | * opaque data count. */ | ||
778 | count = ntohl(*p++); | ||
779 | res->eof = ntohl(*p++); | ||
780 | ocount = ntohl(*p++); | ||
781 | |||
782 | if (ocount != count) { | ||
783 | printk(KERN_WARNING "NFS: READ count doesn't match RPC opaque count.\n"); | ||
784 | return -errno_NFSERR_IO; | ||
785 | } | ||
786 | |||
787 | hdrlen = (u8 *) p - (u8 *) iov->iov_base; | ||
788 | if (iov->iov_len < hdrlen) { | ||
789 | printk(KERN_WARNING "NFS: READ reply header overflowed:" | ||
790 | "length %d > %Zu\n", hdrlen, iov->iov_len); | ||
791 | return -errno_NFSERR_IO; | ||
792 | } else if (iov->iov_len != hdrlen) { | ||
793 | dprintk("NFS: READ header is short. iovec will be shifted.\n"); | ||
794 | xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen); | ||
795 | } | ||
796 | |||
797 | recvd = req->rq_rcv_buf.len - hdrlen; | ||
798 | if (count > recvd) { | ||
799 | printk(KERN_WARNING "NFS: server cheating in read reply: " | ||
800 | "count %d > recvd %d\n", count, recvd); | ||
801 | count = recvd; | ||
802 | res->eof = 0; | ||
803 | } | ||
804 | |||
805 | if (count < res->count) | ||
806 | res->count = count; | ||
807 | |||
808 | return count; | ||
809 | } | ||
810 | |||
811 | /* | ||
812 | * Decode WRITE response | ||
813 | */ | ||
814 | static int | ||
815 | nfs3_xdr_writeres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res) | ||
816 | { | ||
817 | int status; | ||
818 | |||
819 | status = ntohl(*p++); | ||
820 | p = xdr_decode_wcc_data(p, res->fattr); | ||
821 | |||
822 | if (status != 0) | ||
823 | return -nfs_stat_to_errno(status); | ||
824 | |||
825 | res->count = ntohl(*p++); | ||
826 | res->verf->committed = (enum nfs3_stable_how)ntohl(*p++); | ||
827 | res->verf->verifier[0] = *p++; | ||
828 | res->verf->verifier[1] = *p++; | ||
829 | |||
830 | return res->count; | ||
831 | } | ||
832 | |||
833 | /* | ||
834 | * Decode a CREATE response | ||
835 | */ | ||
836 | static int | ||
837 | nfs3_xdr_createres(struct rpc_rqst *req, u32 *p, struct nfs3_diropres *res) | ||
838 | { | ||
839 | int status; | ||
840 | |||
841 | status = ntohl(*p++); | ||
842 | if (status == 0) { | ||
843 | if (*p++) { | ||
844 | if (!(p = xdr_decode_fhandle(p, res->fh))) | ||
845 | return -errno_NFSERR_IO; | ||
846 | p = xdr_decode_post_op_attr(p, res->fattr); | ||
847 | } else { | ||
848 | memset(res->fh, 0, sizeof(*res->fh)); | ||
849 | /* Do decode post_op_attr but set it to NULL */ | ||
850 | p = xdr_decode_post_op_attr(p, res->fattr); | ||
851 | res->fattr->valid = 0; | ||
852 | } | ||
853 | } else { | ||
854 | status = -nfs_stat_to_errno(status); | ||
855 | } | ||
856 | p = xdr_decode_wcc_data(p, res->dir_attr); | ||
857 | return status; | ||
858 | } | ||
859 | |||
860 | /* | ||
861 | * Decode RENAME reply | ||
862 | */ | ||
863 | static int | ||
864 | nfs3_xdr_renameres(struct rpc_rqst *req, u32 *p, struct nfs3_renameres *res) | ||
865 | { | ||
866 | int status; | ||
867 | |||
868 | if ((status = ntohl(*p++)) != 0) | ||
869 | status = -nfs_stat_to_errno(status); | ||
870 | p = xdr_decode_wcc_data(p, res->fromattr); | ||
871 | p = xdr_decode_wcc_data(p, res->toattr); | ||
872 | return status; | ||
873 | } | ||
874 | |||
875 | /* | ||
876 | * Decode LINK reply | ||
877 | */ | ||
878 | static int | ||
879 | nfs3_xdr_linkres(struct rpc_rqst *req, u32 *p, struct nfs3_linkres *res) | ||
880 | { | ||
881 | int status; | ||
882 | |||
883 | if ((status = ntohl(*p++)) != 0) | ||
884 | status = -nfs_stat_to_errno(status); | ||
885 | p = xdr_decode_post_op_attr(p, res->fattr); | ||
886 | p = xdr_decode_wcc_data(p, res->dir_attr); | ||
887 | return status; | ||
888 | } | ||
889 | |||
890 | /* | ||
891 | * Decode FSSTAT reply | ||
892 | */ | ||
893 | static int | ||
894 | nfs3_xdr_fsstatres(struct rpc_rqst *req, u32 *p, struct nfs_fsstat *res) | ||
895 | { | ||
896 | int status; | ||
897 | |||
898 | status = ntohl(*p++); | ||
899 | |||
900 | p = xdr_decode_post_op_attr(p, res->fattr); | ||
901 | if (status != 0) | ||
902 | return -nfs_stat_to_errno(status); | ||
903 | |||
904 | p = xdr_decode_hyper(p, &res->tbytes); | ||
905 | p = xdr_decode_hyper(p, &res->fbytes); | ||
906 | p = xdr_decode_hyper(p, &res->abytes); | ||
907 | p = xdr_decode_hyper(p, &res->tfiles); | ||
908 | p = xdr_decode_hyper(p, &res->ffiles); | ||
909 | p = xdr_decode_hyper(p, &res->afiles); | ||
910 | |||
911 | /* ignore invarsec */ | ||
912 | return 0; | ||
913 | } | ||
914 | |||
915 | /* | ||
916 | * Decode FSINFO reply | ||
917 | */ | ||
918 | static int | ||
919 | nfs3_xdr_fsinfores(struct rpc_rqst *req, u32 *p, struct nfs_fsinfo *res) | ||
920 | { | ||
921 | int status; | ||
922 | |||
923 | status = ntohl(*p++); | ||
924 | |||
925 | p = xdr_decode_post_op_attr(p, res->fattr); | ||
926 | if (status != 0) | ||
927 | return -nfs_stat_to_errno(status); | ||
928 | |||
929 | res->rtmax = ntohl(*p++); | ||
930 | res->rtpref = ntohl(*p++); | ||
931 | res->rtmult = ntohl(*p++); | ||
932 | res->wtmax = ntohl(*p++); | ||
933 | res->wtpref = ntohl(*p++); | ||
934 | res->wtmult = ntohl(*p++); | ||
935 | res->dtpref = ntohl(*p++); | ||
936 | p = xdr_decode_hyper(p, &res->maxfilesize); | ||
937 | |||
938 | /* ignore time_delta and properties */ | ||
939 | res->lease_time = 0; | ||
940 | return 0; | ||
941 | } | ||
942 | |||
943 | /* | ||
944 | * Decode PATHCONF reply | ||
945 | */ | ||
946 | static int | ||
947 | nfs3_xdr_pathconfres(struct rpc_rqst *req, u32 *p, struct nfs_pathconf *res) | ||
948 | { | ||
949 | int status; | ||
950 | |||
951 | status = ntohl(*p++); | ||
952 | |||
953 | p = xdr_decode_post_op_attr(p, res->fattr); | ||
954 | if (status != 0) | ||
955 | return -nfs_stat_to_errno(status); | ||
956 | res->max_link = ntohl(*p++); | ||
957 | res->max_namelen = ntohl(*p++); | ||
958 | |||
959 | /* ignore remaining fields */ | ||
960 | return 0; | ||
961 | } | ||
962 | |||
963 | /* | ||
964 | * Decode COMMIT reply | ||
965 | */ | ||
966 | static int | ||
967 | nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res) | ||
968 | { | ||
969 | int status; | ||
970 | |||
971 | status = ntohl(*p++); | ||
972 | p = xdr_decode_wcc_data(p, res->fattr); | ||
973 | if (status != 0) | ||
974 | return -nfs_stat_to_errno(status); | ||
975 | |||
976 | res->verf->verifier[0] = *p++; | ||
977 | res->verf->verifier[1] = *p++; | ||
978 | return 0; | ||
979 | } | ||
980 | |||
981 | #ifndef MAX | ||
982 | # define MAX(a, b) (((a) > (b))? (a) : (b)) | ||
983 | #endif | ||
984 | |||
985 | #define PROC(proc, argtype, restype, timer) \ | ||
986 | [NFS3PROC_##proc] = { \ | ||
987 | .p_proc = NFS3PROC_##proc, \ | ||
988 | .p_encode = (kxdrproc_t) nfs3_xdr_##argtype, \ | ||
989 | .p_decode = (kxdrproc_t) nfs3_xdr_##restype, \ | ||
990 | .p_bufsiz = MAX(NFS3_##argtype##_sz,NFS3_##restype##_sz) << 2, \ | ||
991 | .p_timer = timer \ | ||
992 | } | ||
993 | |||
994 | struct rpc_procinfo nfs3_procedures[] = { | ||
995 | PROC(GETATTR, fhandle, attrstat, 1), | ||
996 | PROC(SETATTR, sattrargs, wccstat, 0), | ||
997 | PROC(LOOKUP, diropargs, lookupres, 2), | ||
998 | PROC(ACCESS, accessargs, accessres, 1), | ||
999 | PROC(READLINK, readlinkargs, readlinkres, 3), | ||
1000 | PROC(READ, readargs, readres, 3), | ||
1001 | PROC(WRITE, writeargs, writeres, 4), | ||
1002 | PROC(CREATE, createargs, createres, 0), | ||
1003 | PROC(MKDIR, mkdirargs, createres, 0), | ||
1004 | PROC(SYMLINK, symlinkargs, createres, 0), | ||
1005 | PROC(MKNOD, mknodargs, createres, 0), | ||
1006 | PROC(REMOVE, diropargs, wccstat, 0), | ||
1007 | PROC(RMDIR, diropargs, wccstat, 0), | ||
1008 | PROC(RENAME, renameargs, renameres, 0), | ||
1009 | PROC(LINK, linkargs, linkres, 0), | ||
1010 | PROC(READDIR, readdirargs, readdirres, 3), | ||
1011 | PROC(READDIRPLUS, readdirargs, readdirres, 3), | ||
1012 | PROC(FSSTAT, fhandle, fsstatres, 0), | ||
1013 | PROC(FSINFO, fhandle, fsinfores, 0), | ||
1014 | PROC(PATHCONF, fhandle, pathconfres, 0), | ||
1015 | PROC(COMMIT, commitargs, commitres, 5), | ||
1016 | }; | ||
1017 | |||
1018 | struct rpc_version nfs_version3 = { | ||
1019 | .number = 3, | ||
1020 | .nrprocs = sizeof(nfs3_procedures)/sizeof(nfs3_procedures[0]), | ||
1021 | .procs = nfs3_procedures | ||
1022 | }; | ||
1023 | |||