diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-11 18:11:56 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-11 18:11:56 -0500 |
commit | b9d919a4ac6cf031b8e065f82ad8f1b0c9ed74b1 (patch) | |
tree | 3139b066396956fd3794df0cb1aa74dcc9f1cb28 /fs/nfsd | |
parent | 7c955fca3e1d8132982148267d9efcafae849bb6 (diff) | |
parent | 357f54d6b38252737116a6d631f6ac28ded018ed (diff) |
Merge branch 'nfs-for-2.6.38' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
* 'nfs-for-2.6.38' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6: (89 commits)
NFS fix the setting of exchange id flag
NFS: Don't use vm_map_ram() in readdir
NFSv4: Ensure continued open and lockowner name uniqueness
NFS: Move cl_delegations to the nfs_server struct
NFS: Introduce nfs_detach_delegations()
NFS: Move cl_state_owners and related fields to the nfs_server struct
NFS: Allow walking nfs_client.cl_superblocks list outside client.c
pnfs: layout roc code
pnfs: update nfs4_callback_recallany to handle layouts
pnfs: add CB_LAYOUTRECALL handling
pnfs: CB_LAYOUTRECALL xdr code
pnfs: change lo refcounting to atomic_t
pnfs: check that partial LAYOUTGET return is ignored
pnfs: add layout to client list before sending rpc
pnfs: serialize LAYOUTGET(openstateid)
pnfs: layoutget rpc code cleanup
pnfs: change how lsegs are removed from layout list
pnfs: change layout state seqlock to a spinlock
pnfs: add prefix to struct pnfs_layout_hdr fields
pnfs: add prefix to struct pnfs_layout_segment fields
...
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/nfs4callback.c | 690 |
1 files changed, 424 insertions, 266 deletions
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 143da2eecd7b..21a63da305ff 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c | |||
@@ -50,11 +50,6 @@ enum { | |||
50 | NFSPROC4_CLNT_CB_SEQUENCE, | 50 | NFSPROC4_CLNT_CB_SEQUENCE, |
51 | }; | 51 | }; |
52 | 52 | ||
53 | enum nfs_cb_opnum4 { | ||
54 | OP_CB_RECALL = 4, | ||
55 | OP_CB_SEQUENCE = 11, | ||
56 | }; | ||
57 | |||
58 | #define NFS4_MAXTAGLEN 20 | 53 | #define NFS4_MAXTAGLEN 20 |
59 | 54 | ||
60 | #define NFS4_enc_cb_null_sz 0 | 55 | #define NFS4_enc_cb_null_sz 0 |
@@ -79,61 +74,6 @@ enum nfs_cb_opnum4 { | |||
79 | cb_sequence_dec_sz + \ | 74 | cb_sequence_dec_sz + \ |
80 | op_dec_sz) | 75 | op_dec_sz) |
81 | 76 | ||
82 | /* | ||
83 | * Generic encode routines from fs/nfs/nfs4xdr.c | ||
84 | */ | ||
85 | static inline __be32 * | ||
86 | xdr_writemem(__be32 *p, const void *ptr, int nbytes) | ||
87 | { | ||
88 | int tmp = XDR_QUADLEN(nbytes); | ||
89 | if (!tmp) | ||
90 | return p; | ||
91 | p[tmp-1] = 0; | ||
92 | memcpy(p, ptr, nbytes); | ||
93 | return p + tmp; | ||
94 | } | ||
95 | |||
96 | #define WRITE32(n) *p++ = htonl(n) | ||
97 | #define WRITEMEM(ptr,nbytes) do { \ | ||
98 | p = xdr_writemem(p, ptr, nbytes); \ | ||
99 | } while (0) | ||
100 | #define RESERVE_SPACE(nbytes) do { \ | ||
101 | p = xdr_reserve_space(xdr, nbytes); \ | ||
102 | if (!p) dprintk("NFSD: RESERVE_SPACE(%d) failed in function %s\n", (int) (nbytes), __func__); \ | ||
103 | BUG_ON(!p); \ | ||
104 | } while (0) | ||
105 | |||
106 | /* | ||
107 | * Generic decode routines from fs/nfs/nfs4xdr.c | ||
108 | */ | ||
109 | #define DECODE_TAIL \ | ||
110 | status = 0; \ | ||
111 | out: \ | ||
112 | return status; \ | ||
113 | xdr_error: \ | ||
114 | dprintk("NFSD: xdr error! (%s:%d)\n", __FILE__, __LINE__); \ | ||
115 | status = -EIO; \ | ||
116 | goto out | ||
117 | |||
118 | #define READ32(x) (x) = ntohl(*p++) | ||
119 | #define READ64(x) do { \ | ||
120 | (x) = (u64)ntohl(*p++) << 32; \ | ||
121 | (x) |= ntohl(*p++); \ | ||
122 | } while (0) | ||
123 | #define READTIME(x) do { \ | ||
124 | p++; \ | ||
125 | (x.tv_sec) = ntohl(*p++); \ | ||
126 | (x.tv_nsec) = ntohl(*p++); \ | ||
127 | } while (0) | ||
128 | #define READ_BUF(nbytes) do { \ | ||
129 | p = xdr_inline_decode(xdr, nbytes); \ | ||
130 | if (!p) { \ | ||
131 | dprintk("NFSD: %s: reply buffer overflowed in line %d.\n", \ | ||
132 | __func__, __LINE__); \ | ||
133 | return -EIO; \ | ||
134 | } \ | ||
135 | } while (0) | ||
136 | |||
137 | struct nfs4_cb_compound_hdr { | 77 | struct nfs4_cb_compound_hdr { |
138 | /* args */ | 78 | /* args */ |
139 | u32 ident; /* minorversion 0 only */ | 79 | u32 ident; /* minorversion 0 only */ |
@@ -144,295 +84,513 @@ struct nfs4_cb_compound_hdr { | |||
144 | int status; | 84 | int status; |
145 | }; | 85 | }; |
146 | 86 | ||
147 | static struct { | 87 | /* |
148 | int stat; | 88 | * Handle decode buffer overflows out-of-line. |
149 | int errno; | 89 | */ |
150 | } nfs_cb_errtbl[] = { | 90 | static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) |
151 | { NFS4_OK, 0 }, | 91 | { |
152 | { NFS4ERR_PERM, EPERM }, | 92 | dprintk("NFS: %s prematurely hit the end of our receive buffer. " |
153 | { NFS4ERR_NOENT, ENOENT }, | 93 | "Remaining buffer length is %tu words.\n", |
154 | { NFS4ERR_IO, EIO }, | 94 | func, xdr->end - xdr->p); |
155 | { NFS4ERR_NXIO, ENXIO }, | 95 | } |
156 | { NFS4ERR_ACCESS, EACCES }, | ||
157 | { NFS4ERR_EXIST, EEXIST }, | ||
158 | { NFS4ERR_XDEV, EXDEV }, | ||
159 | { NFS4ERR_NOTDIR, ENOTDIR }, | ||
160 | { NFS4ERR_ISDIR, EISDIR }, | ||
161 | { NFS4ERR_INVAL, EINVAL }, | ||
162 | { NFS4ERR_FBIG, EFBIG }, | ||
163 | { NFS4ERR_NOSPC, ENOSPC }, | ||
164 | { NFS4ERR_ROFS, EROFS }, | ||
165 | { NFS4ERR_MLINK, EMLINK }, | ||
166 | { NFS4ERR_NAMETOOLONG, ENAMETOOLONG }, | ||
167 | { NFS4ERR_NOTEMPTY, ENOTEMPTY }, | ||
168 | { NFS4ERR_DQUOT, EDQUOT }, | ||
169 | { NFS4ERR_STALE, ESTALE }, | ||
170 | { NFS4ERR_BADHANDLE, EBADHANDLE }, | ||
171 | { NFS4ERR_BAD_COOKIE, EBADCOOKIE }, | ||
172 | { NFS4ERR_NOTSUPP, ENOTSUPP }, | ||
173 | { NFS4ERR_TOOSMALL, ETOOSMALL }, | ||
174 | { NFS4ERR_SERVERFAULT, ESERVERFAULT }, | ||
175 | { NFS4ERR_BADTYPE, EBADTYPE }, | ||
176 | { NFS4ERR_LOCKED, EAGAIN }, | ||
177 | { NFS4ERR_RESOURCE, EREMOTEIO }, | ||
178 | { NFS4ERR_SYMLINK, ELOOP }, | ||
179 | { NFS4ERR_OP_ILLEGAL, EOPNOTSUPP }, | ||
180 | { NFS4ERR_DEADLOCK, EDEADLK }, | ||
181 | { -1, EIO } | ||
182 | }; | ||
183 | 96 | ||
184 | static int | 97 | static __be32 *xdr_encode_empty_array(__be32 *p) |
185 | nfs_cb_stat_to_errno(int stat) | ||
186 | { | 98 | { |
187 | int i; | 99 | *p++ = xdr_zero; |
188 | for (i = 0; nfs_cb_errtbl[i].stat != -1; i++) { | 100 | return p; |
189 | if (nfs_cb_errtbl[i].stat == stat) | ||
190 | return nfs_cb_errtbl[i].errno; | ||
191 | } | ||
192 | /* If we cannot translate the error, the recovery routines should | ||
193 | * handle it. | ||
194 | * Note: remaining NFSv4 error codes have values > 10000, so should | ||
195 | * not conflict with native Linux error codes. | ||
196 | */ | ||
197 | return stat; | ||
198 | } | 101 | } |
199 | 102 | ||
200 | /* | 103 | /* |
201 | * XDR encode | 104 | * Encode/decode NFSv4 CB basic data types |
105 | * | ||
106 | * Basic NFSv4 callback data types are defined in section 15 of RFC | ||
107 | * 3530: "Network File System (NFS) version 4 Protocol" and section | ||
108 | * 20 of RFC 5661: "Network File System (NFS) Version 4 Minor Version | ||
109 | * 1 Protocol" | ||
110 | */ | ||
111 | |||
112 | /* | ||
113 | * nfs_cb_opnum4 | ||
114 | * | ||
115 | * enum nfs_cb_opnum4 { | ||
116 | * OP_CB_GETATTR = 3, | ||
117 | * ... | ||
118 | * }; | ||
202 | */ | 119 | */ |
120 | enum nfs_cb_opnum4 { | ||
121 | OP_CB_GETATTR = 3, | ||
122 | OP_CB_RECALL = 4, | ||
123 | OP_CB_LAYOUTRECALL = 5, | ||
124 | OP_CB_NOTIFY = 6, | ||
125 | OP_CB_PUSH_DELEG = 7, | ||
126 | OP_CB_RECALL_ANY = 8, | ||
127 | OP_CB_RECALLABLE_OBJ_AVAIL = 9, | ||
128 | OP_CB_RECALL_SLOT = 10, | ||
129 | OP_CB_SEQUENCE = 11, | ||
130 | OP_CB_WANTS_CANCELLED = 12, | ||
131 | OP_CB_NOTIFY_LOCK = 13, | ||
132 | OP_CB_NOTIFY_DEVICEID = 14, | ||
133 | OP_CB_ILLEGAL = 10044 | ||
134 | }; | ||
203 | 135 | ||
204 | static void | 136 | static void encode_nfs_cb_opnum4(struct xdr_stream *xdr, enum nfs_cb_opnum4 op) |
205 | encode_stateid(struct xdr_stream *xdr, stateid_t *sid) | ||
206 | { | 137 | { |
207 | __be32 *p; | 138 | __be32 *p; |
208 | 139 | ||
209 | RESERVE_SPACE(sizeof(stateid_t)); | 140 | p = xdr_reserve_space(xdr, 4); |
210 | WRITE32(sid->si_generation); | 141 | *p = cpu_to_be32(op); |
211 | WRITEMEM(&sid->si_opaque, sizeof(stateid_opaque_t)); | ||
212 | } | 142 | } |
213 | 143 | ||
214 | static void | 144 | /* |
215 | encode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr) | 145 | * nfs_fh4 |
146 | * | ||
147 | * typedef opaque nfs_fh4<NFS4_FHSIZE>; | ||
148 | */ | ||
149 | static void encode_nfs_fh4(struct xdr_stream *xdr, const struct knfsd_fh *fh) | ||
216 | { | 150 | { |
217 | __be32 * p; | 151 | u32 length = fh->fh_size; |
152 | __be32 *p; | ||
218 | 153 | ||
219 | RESERVE_SPACE(16); | 154 | BUG_ON(length > NFS4_FHSIZE); |
220 | WRITE32(0); /* tag length is always 0 */ | 155 | p = xdr_reserve_space(xdr, 4 + length); |
221 | WRITE32(hdr->minorversion); | 156 | xdr_encode_opaque(p, &fh->fh_base, length); |
222 | WRITE32(hdr->ident); | ||
223 | hdr->nops_p = p; | ||
224 | WRITE32(hdr->nops); | ||
225 | } | 157 | } |
226 | 158 | ||
227 | static void encode_cb_nops(struct nfs4_cb_compound_hdr *hdr) | 159 | /* |
160 | * stateid4 | ||
161 | * | ||
162 | * struct stateid4 { | ||
163 | * uint32_t seqid; | ||
164 | * opaque other[12]; | ||
165 | * }; | ||
166 | */ | ||
167 | static void encode_stateid4(struct xdr_stream *xdr, const stateid_t *sid) | ||
228 | { | 168 | { |
229 | *hdr->nops_p = htonl(hdr->nops); | 169 | __be32 *p; |
170 | |||
171 | p = xdr_reserve_space(xdr, NFS4_STATEID_SIZE); | ||
172 | *p++ = cpu_to_be32(sid->si_generation); | ||
173 | xdr_encode_opaque_fixed(p, &sid->si_opaque, NFS4_STATEID_OTHER_SIZE); | ||
230 | } | 174 | } |
231 | 175 | ||
232 | static void | 176 | /* |
233 | encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp, | 177 | * sessionid4 |
234 | struct nfs4_cb_compound_hdr *hdr) | 178 | * |
179 | * typedef opaque sessionid4[NFS4_SESSIONID_SIZE]; | ||
180 | */ | ||
181 | static void encode_sessionid4(struct xdr_stream *xdr, | ||
182 | const struct nfsd4_session *session) | ||
235 | { | 183 | { |
236 | __be32 *p; | 184 | __be32 *p; |
237 | int len = dp->dl_fh.fh_size; | 185 | |
238 | 186 | p = xdr_reserve_space(xdr, NFS4_MAX_SESSIONID_LEN); | |
239 | RESERVE_SPACE(4); | 187 | xdr_encode_opaque_fixed(p, session->se_sessionid.data, |
240 | WRITE32(OP_CB_RECALL); | 188 | NFS4_MAX_SESSIONID_LEN); |
241 | encode_stateid(xdr, &dp->dl_stateid); | ||
242 | RESERVE_SPACE(8 + (XDR_QUADLEN(len) << 2)); | ||
243 | WRITE32(0); /* truncate optimization not implemented */ | ||
244 | WRITE32(len); | ||
245 | WRITEMEM(&dp->dl_fh.fh_base, len); | ||
246 | hdr->nops++; | ||
247 | } | 189 | } |
248 | 190 | ||
249 | static void | 191 | /* |
250 | encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb, | 192 | * nfsstat4 |
251 | struct nfs4_cb_compound_hdr *hdr) | 193 | */ |
252 | { | 194 | static const struct { |
253 | __be32 *p; | 195 | int stat; |
254 | struct nfsd4_session *ses = cb->cb_clp->cl_cb_session; | 196 | int errno; |
197 | } nfs_cb_errtbl[] = { | ||
198 | { NFS4_OK, 0 }, | ||
199 | { NFS4ERR_PERM, -EPERM }, | ||
200 | { NFS4ERR_NOENT, -ENOENT }, | ||
201 | { NFS4ERR_IO, -EIO }, | ||
202 | { NFS4ERR_NXIO, -ENXIO }, | ||
203 | { NFS4ERR_ACCESS, -EACCES }, | ||
204 | { NFS4ERR_EXIST, -EEXIST }, | ||
205 | { NFS4ERR_XDEV, -EXDEV }, | ||
206 | { NFS4ERR_NOTDIR, -ENOTDIR }, | ||
207 | { NFS4ERR_ISDIR, -EISDIR }, | ||
208 | { NFS4ERR_INVAL, -EINVAL }, | ||
209 | { NFS4ERR_FBIG, -EFBIG }, | ||
210 | { NFS4ERR_NOSPC, -ENOSPC }, | ||
211 | { NFS4ERR_ROFS, -EROFS }, | ||
212 | { NFS4ERR_MLINK, -EMLINK }, | ||
213 | { NFS4ERR_NAMETOOLONG, -ENAMETOOLONG }, | ||
214 | { NFS4ERR_NOTEMPTY, -ENOTEMPTY }, | ||
215 | { NFS4ERR_DQUOT, -EDQUOT }, | ||
216 | { NFS4ERR_STALE, -ESTALE }, | ||
217 | { NFS4ERR_BADHANDLE, -EBADHANDLE }, | ||
218 | { NFS4ERR_BAD_COOKIE, -EBADCOOKIE }, | ||
219 | { NFS4ERR_NOTSUPP, -ENOTSUPP }, | ||
220 | { NFS4ERR_TOOSMALL, -ETOOSMALL }, | ||
221 | { NFS4ERR_SERVERFAULT, -ESERVERFAULT }, | ||
222 | { NFS4ERR_BADTYPE, -EBADTYPE }, | ||
223 | { NFS4ERR_LOCKED, -EAGAIN }, | ||
224 | { NFS4ERR_RESOURCE, -EREMOTEIO }, | ||
225 | { NFS4ERR_SYMLINK, -ELOOP }, | ||
226 | { NFS4ERR_OP_ILLEGAL, -EOPNOTSUPP }, | ||
227 | { NFS4ERR_DEADLOCK, -EDEADLK }, | ||
228 | { -1, -EIO } | ||
229 | }; | ||
255 | 230 | ||
256 | if (hdr->minorversion == 0) | 231 | /* |
257 | return; | 232 | * If we cannot translate the error, the recovery routines should |
233 | * handle it. | ||
234 | * | ||
235 | * Note: remaining NFSv4 error codes have values > 10000, so should | ||
236 | * not conflict with native Linux error codes. | ||
237 | */ | ||
238 | static int nfs_cb_stat_to_errno(int status) | ||
239 | { | ||
240 | int i; | ||
258 | 241 | ||
259 | RESERVE_SPACE(1 + NFS4_MAX_SESSIONID_LEN + 20); | 242 | for (i = 0; nfs_cb_errtbl[i].stat != -1; i++) { |
243 | if (nfs_cb_errtbl[i].stat == status) | ||
244 | return nfs_cb_errtbl[i].errno; | ||
245 | } | ||
260 | 246 | ||
261 | WRITE32(OP_CB_SEQUENCE); | 247 | dprintk("NFSD: Unrecognized NFS CB status value: %u\n", status); |
262 | WRITEMEM(ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN); | 248 | return -status; |
263 | WRITE32(ses->se_cb_seq_nr); | ||
264 | WRITE32(0); /* slotid, always 0 */ | ||
265 | WRITE32(0); /* highest slotid always 0 */ | ||
266 | WRITE32(0); /* cachethis always 0 */ | ||
267 | WRITE32(0); /* FIXME: support referring_call_lists */ | ||
268 | hdr->nops++; | ||
269 | } | 249 | } |
270 | 250 | ||
271 | static int | 251 | static int decode_cb_op_status(struct xdr_stream *xdr, enum nfs_opnum4 expected, |
272 | nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p) | 252 | enum nfsstat4 *status) |
273 | { | 253 | { |
274 | struct xdr_stream xdrs, *xdr = &xdrs; | 254 | __be32 *p; |
255 | u32 op; | ||
275 | 256 | ||
276 | xdr_init_encode(&xdrs, &req->rq_snd_buf, p); | 257 | p = xdr_inline_decode(xdr, 4 + 4); |
277 | RESERVE_SPACE(0); | 258 | if (unlikely(p == NULL)) |
259 | goto out_overflow; | ||
260 | op = be32_to_cpup(p++); | ||
261 | if (unlikely(op != expected)) | ||
262 | goto out_unexpected; | ||
263 | *status = be32_to_cpup(p); | ||
278 | return 0; | 264 | return 0; |
265 | out_overflow: | ||
266 | print_overflow_msg(__func__, xdr); | ||
267 | return -EIO; | ||
268 | out_unexpected: | ||
269 | dprintk("NFSD: Callback server returned operation %d but " | ||
270 | "we issued a request for %d\n", op, expected); | ||
271 | return -EIO; | ||
279 | } | 272 | } |
280 | 273 | ||
281 | static int | 274 | /* |
282 | nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p, | 275 | * CB_COMPOUND4args |
283 | struct nfsd4_callback *cb) | 276 | * |
277 | * struct CB_COMPOUND4args { | ||
278 | * utf8str_cs tag; | ||
279 | * uint32_t minorversion; | ||
280 | * uint32_t callback_ident; | ||
281 | * nfs_cb_argop4 argarray<>; | ||
282 | * }; | ||
283 | */ | ||
284 | static void encode_cb_compound4args(struct xdr_stream *xdr, | ||
285 | struct nfs4_cb_compound_hdr *hdr) | ||
284 | { | 286 | { |
285 | struct xdr_stream xdr; | 287 | __be32 * p; |
286 | struct nfs4_delegation *args = cb->cb_op; | ||
287 | struct nfs4_cb_compound_hdr hdr = { | ||
288 | .ident = cb->cb_clp->cl_cb_ident, | ||
289 | .minorversion = cb->cb_minorversion, | ||
290 | }; | ||
291 | 288 | ||
292 | xdr_init_encode(&xdr, &req->rq_snd_buf, p); | 289 | p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4); |
293 | encode_cb_compound_hdr(&xdr, &hdr); | 290 | p = xdr_encode_empty_array(p); /* empty tag */ |
294 | encode_cb_sequence(&xdr, cb, &hdr); | 291 | *p++ = cpu_to_be32(hdr->minorversion); |
295 | encode_cb_recall(&xdr, args, &hdr); | 292 | *p++ = cpu_to_be32(hdr->ident); |
296 | encode_cb_nops(&hdr); | 293 | |
294 | hdr->nops_p = p; | ||
295 | *p = cpu_to_be32(hdr->nops); /* argarray element count */ | ||
296 | } | ||
297 | |||
298 | /* | ||
299 | * Update argarray element count | ||
300 | */ | ||
301 | static void encode_cb_nops(struct nfs4_cb_compound_hdr *hdr) | ||
302 | { | ||
303 | BUG_ON(hdr->nops > NFS4_MAX_BACK_CHANNEL_OPS); | ||
304 | *hdr->nops_p = cpu_to_be32(hdr->nops); | ||
305 | } | ||
306 | |||
307 | /* | ||
308 | * CB_COMPOUND4res | ||
309 | * | ||
310 | * struct CB_COMPOUND4res { | ||
311 | * nfsstat4 status; | ||
312 | * utf8str_cs tag; | ||
313 | * nfs_cb_resop4 resarray<>; | ||
314 | * }; | ||
315 | */ | ||
316 | static int decode_cb_compound4res(struct xdr_stream *xdr, | ||
317 | struct nfs4_cb_compound_hdr *hdr) | ||
318 | { | ||
319 | u32 length; | ||
320 | __be32 *p; | ||
321 | |||
322 | p = xdr_inline_decode(xdr, 4 + 4); | ||
323 | if (unlikely(p == NULL)) | ||
324 | goto out_overflow; | ||
325 | hdr->status = be32_to_cpup(p++); | ||
326 | /* Ignore the tag */ | ||
327 | length = be32_to_cpup(p++); | ||
328 | p = xdr_inline_decode(xdr, length + 4); | ||
329 | if (unlikely(p == NULL)) | ||
330 | goto out_overflow; | ||
331 | hdr->nops = be32_to_cpup(p); | ||
297 | return 0; | 332 | return 0; |
333 | out_overflow: | ||
334 | print_overflow_msg(__func__, xdr); | ||
335 | return -EIO; | ||
298 | } | 336 | } |
299 | 337 | ||
338 | /* | ||
339 | * CB_RECALL4args | ||
340 | * | ||
341 | * struct CB_RECALL4args { | ||
342 | * stateid4 stateid; | ||
343 | * bool truncate; | ||
344 | * nfs_fh4 fh; | ||
345 | * }; | ||
346 | */ | ||
347 | static void encode_cb_recall4args(struct xdr_stream *xdr, | ||
348 | const struct nfs4_delegation *dp, | ||
349 | struct nfs4_cb_compound_hdr *hdr) | ||
350 | { | ||
351 | __be32 *p; | ||
352 | |||
353 | encode_nfs_cb_opnum4(xdr, OP_CB_RECALL); | ||
354 | encode_stateid4(xdr, &dp->dl_stateid); | ||
355 | |||
356 | p = xdr_reserve_space(xdr, 4); | ||
357 | *p++ = xdr_zero; /* truncate */ | ||
300 | 358 | ||
301 | static int | 359 | encode_nfs_fh4(xdr, &dp->dl_fh); |
302 | decode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr){ | ||
303 | __be32 *p; | ||
304 | u32 taglen; | ||
305 | 360 | ||
306 | READ_BUF(8); | 361 | hdr->nops++; |
307 | READ32(hdr->status); | ||
308 | /* We've got no use for the tag; ignore it: */ | ||
309 | READ32(taglen); | ||
310 | READ_BUF(taglen + 4); | ||
311 | p += XDR_QUADLEN(taglen); | ||
312 | READ32(hdr->nops); | ||
313 | return 0; | ||
314 | } | 362 | } |
315 | 363 | ||
316 | static int | 364 | /* |
317 | decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected) | 365 | * CB_SEQUENCE4args |
366 | * | ||
367 | * struct CB_SEQUENCE4args { | ||
368 | * sessionid4 csa_sessionid; | ||
369 | * sequenceid4 csa_sequenceid; | ||
370 | * slotid4 csa_slotid; | ||
371 | * slotid4 csa_highest_slotid; | ||
372 | * bool csa_cachethis; | ||
373 | * referring_call_list4 csa_referring_call_lists<>; | ||
374 | * }; | ||
375 | */ | ||
376 | static void encode_cb_sequence4args(struct xdr_stream *xdr, | ||
377 | const struct nfsd4_callback *cb, | ||
378 | struct nfs4_cb_compound_hdr *hdr) | ||
318 | { | 379 | { |
380 | struct nfsd4_session *session = cb->cb_clp->cl_cb_session; | ||
319 | __be32 *p; | 381 | __be32 *p; |
320 | u32 op; | 382 | |
321 | int32_t nfserr; | 383 | if (hdr->minorversion == 0) |
322 | 384 | return; | |
323 | READ_BUF(8); | 385 | |
324 | READ32(op); | 386 | encode_nfs_cb_opnum4(xdr, OP_CB_SEQUENCE); |
325 | if (op != expected) { | 387 | encode_sessionid4(xdr, session); |
326 | dprintk("NFSD: decode_cb_op_hdr: Callback server returned " | 388 | |
327 | " operation %d but we issued a request for %d\n", | 389 | p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4 + 4); |
328 | op, expected); | 390 | *p++ = cpu_to_be32(session->se_cb_seq_nr); /* csa_sequenceid */ |
329 | return -EIO; | 391 | *p++ = xdr_zero; /* csa_slotid */ |
330 | } | 392 | *p++ = xdr_zero; /* csa_highest_slotid */ |
331 | READ32(nfserr); | 393 | *p++ = xdr_zero; /* csa_cachethis */ |
332 | if (nfserr != NFS_OK) | 394 | xdr_encode_empty_array(p); /* csa_referring_call_lists */ |
333 | return -nfs_cb_stat_to_errno(nfserr); | 395 | |
334 | return 0; | 396 | hdr->nops++; |
335 | } | 397 | } |
336 | 398 | ||
337 | /* | 399 | /* |
400 | * CB_SEQUENCE4resok | ||
401 | * | ||
402 | * struct CB_SEQUENCE4resok { | ||
403 | * sessionid4 csr_sessionid; | ||
404 | * sequenceid4 csr_sequenceid; | ||
405 | * slotid4 csr_slotid; | ||
406 | * slotid4 csr_highest_slotid; | ||
407 | * slotid4 csr_target_highest_slotid; | ||
408 | * }; | ||
409 | * | ||
410 | * union CB_SEQUENCE4res switch (nfsstat4 csr_status) { | ||
411 | * case NFS4_OK: | ||
412 | * CB_SEQUENCE4resok csr_resok4; | ||
413 | * default: | ||
414 | * void; | ||
415 | * }; | ||
416 | * | ||
338 | * Our current back channel implmentation supports a single backchannel | 417 | * Our current back channel implmentation supports a single backchannel |
339 | * with a single slot. | 418 | * with a single slot. |
340 | */ | 419 | */ |
341 | static int | 420 | static int decode_cb_sequence4resok(struct xdr_stream *xdr, |
342 | decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb, | 421 | struct nfsd4_callback *cb) |
343 | struct rpc_rqst *rqstp) | ||
344 | { | 422 | { |
345 | struct nfsd4_session *ses = cb->cb_clp->cl_cb_session; | 423 | struct nfsd4_session *session = cb->cb_clp->cl_cb_session; |
346 | struct nfs4_sessionid id; | 424 | struct nfs4_sessionid id; |
347 | int status; | 425 | int status; |
348 | u32 dummy; | ||
349 | __be32 *p; | 426 | __be32 *p; |
427 | u32 dummy; | ||
350 | 428 | ||
351 | if (cb->cb_minorversion == 0) | 429 | status = -ESERVERFAULT; |
352 | return 0; | ||
353 | |||
354 | status = decode_cb_op_hdr(xdr, OP_CB_SEQUENCE); | ||
355 | if (status) | ||
356 | return status; | ||
357 | 430 | ||
358 | /* | 431 | /* |
359 | * If the server returns different values for sessionID, slotID or | 432 | * If the server returns different values for sessionID, slotID or |
360 | * sequence number, the server is looney tunes. | 433 | * sequence number, the server is looney tunes. |
361 | */ | 434 | */ |
362 | status = -ESERVERFAULT; | 435 | p = xdr_inline_decode(xdr, NFS4_MAX_SESSIONID_LEN + 4 + 4); |
363 | 436 | if (unlikely(p == NULL)) | |
364 | READ_BUF(NFS4_MAX_SESSIONID_LEN + 16); | 437 | goto out_overflow; |
365 | memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN); | 438 | memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN); |
366 | p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN); | 439 | if (memcmp(id.data, session->se_sessionid.data, |
367 | if (memcmp(id.data, ses->se_sessionid.data, NFS4_MAX_SESSIONID_LEN)) { | 440 | NFS4_MAX_SESSIONID_LEN) != 0) { |
368 | dprintk("%s Invalid session id\n", __func__); | 441 | dprintk("NFS: %s Invalid session id\n", __func__); |
369 | goto out; | 442 | goto out; |
370 | } | 443 | } |
371 | READ32(dummy); | 444 | p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN); |
372 | if (dummy != ses->se_cb_seq_nr) { | 445 | |
373 | dprintk("%s Invalid sequence number\n", __func__); | 446 | dummy = be32_to_cpup(p++); |
447 | if (dummy != session->se_cb_seq_nr) { | ||
448 | dprintk("NFS: %s Invalid sequence number\n", __func__); | ||
374 | goto out; | 449 | goto out; |
375 | } | 450 | } |
376 | READ32(dummy); /* slotid must be 0 */ | 451 | |
452 | dummy = be32_to_cpup(p++); | ||
377 | if (dummy != 0) { | 453 | if (dummy != 0) { |
378 | dprintk("%s Invalid slotid\n", __func__); | 454 | dprintk("NFS: %s Invalid slotid\n", __func__); |
379 | goto out; | 455 | goto out; |
380 | } | 456 | } |
381 | /* FIXME: process highest slotid and target highest slotid */ | 457 | |
458 | /* | ||
459 | * FIXME: process highest slotid and target highest slotid | ||
460 | */ | ||
382 | status = 0; | 461 | status = 0; |
383 | out: | 462 | out: |
384 | return status; | 463 | return status; |
464 | out_overflow: | ||
465 | print_overflow_msg(__func__, xdr); | ||
466 | return -EIO; | ||
385 | } | 467 | } |
386 | 468 | ||
469 | static int decode_cb_sequence4res(struct xdr_stream *xdr, | ||
470 | struct nfsd4_callback *cb) | ||
471 | { | ||
472 | enum nfsstat4 nfserr; | ||
473 | int status; | ||
474 | |||
475 | if (cb->cb_minorversion == 0) | ||
476 | return 0; | ||
477 | |||
478 | status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &nfserr); | ||
479 | if (unlikely(status)) | ||
480 | goto out; | ||
481 | if (unlikely(nfserr != NFS4_OK)) | ||
482 | goto out_default; | ||
483 | status = decode_cb_sequence4resok(xdr, cb); | ||
484 | out: | ||
485 | return status; | ||
486 | out_default: | ||
487 | return nfs_cb_stat_to_errno(status); | ||
488 | } | ||
387 | 489 | ||
388 | static int | 490 | /* |
389 | nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p) | 491 | * NFSv4.0 and NFSv4.1 XDR encode functions |
492 | * | ||
493 | * NFSv4.0 callback argument types are defined in section 15 of RFC | ||
494 | * 3530: "Network File System (NFS) version 4 Protocol" and section 20 | ||
495 | * of RFC 5661: "Network File System (NFS) Version 4 Minor Version 1 | ||
496 | * Protocol". | ||
497 | */ | ||
498 | |||
499 | /* | ||
500 | * NB: Without this zero space reservation, callbacks over krb5p fail | ||
501 | */ | ||
502 | static void nfs4_xdr_enc_cb_null(struct rpc_rqst *req, struct xdr_stream *xdr, | ||
503 | void *__unused) | ||
504 | { | ||
505 | xdr_reserve_space(xdr, 0); | ||
506 | } | ||
507 | |||
508 | /* | ||
509 | * 20.2. Operation 4: CB_RECALL - Recall a Delegation | ||
510 | */ | ||
511 | static void nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, struct xdr_stream *xdr, | ||
512 | const struct nfsd4_callback *cb) | ||
513 | { | ||
514 | const struct nfs4_delegation *args = cb->cb_op; | ||
515 | struct nfs4_cb_compound_hdr hdr = { | ||
516 | .ident = cb->cb_clp->cl_cb_ident, | ||
517 | .minorversion = cb->cb_minorversion, | ||
518 | }; | ||
519 | |||
520 | encode_cb_compound4args(xdr, &hdr); | ||
521 | encode_cb_sequence4args(xdr, cb, &hdr); | ||
522 | encode_cb_recall4args(xdr, args, &hdr); | ||
523 | encode_cb_nops(&hdr); | ||
524 | } | ||
525 | |||
526 | |||
527 | /* | ||
528 | * NFSv4.0 and NFSv4.1 XDR decode functions | ||
529 | * | ||
530 | * NFSv4.0 callback result types are defined in section 15 of RFC | ||
531 | * 3530: "Network File System (NFS) version 4 Protocol" and section 20 | ||
532 | * of RFC 5661: "Network File System (NFS) Version 4 Minor Version 1 | ||
533 | * Protocol". | ||
534 | */ | ||
535 | |||
536 | static int nfs4_xdr_dec_cb_null(struct rpc_rqst *req, struct xdr_stream *xdr, | ||
537 | void *__unused) | ||
390 | { | 538 | { |
391 | return 0; | 539 | return 0; |
392 | } | 540 | } |
393 | 541 | ||
394 | static int | 542 | /* |
395 | nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p, | 543 | * 20.2. Operation 4: CB_RECALL - Recall a Delegation |
396 | struct nfsd4_callback *cb) | 544 | */ |
545 | static int nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, | ||
546 | struct xdr_stream *xdr, | ||
547 | struct nfsd4_callback *cb) | ||
397 | { | 548 | { |
398 | struct xdr_stream xdr; | ||
399 | struct nfs4_cb_compound_hdr hdr; | 549 | struct nfs4_cb_compound_hdr hdr; |
550 | enum nfsstat4 nfserr; | ||
400 | int status; | 551 | int status; |
401 | 552 | ||
402 | xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); | 553 | status = decode_cb_compound4res(xdr, &hdr); |
403 | status = decode_cb_compound_hdr(&xdr, &hdr); | 554 | if (unlikely(status)) |
404 | if (status) | ||
405 | goto out; | 555 | goto out; |
406 | if (cb) { | 556 | |
407 | status = decode_cb_sequence(&xdr, cb, rqstp); | 557 | if (cb != NULL) { |
408 | if (status) | 558 | status = decode_cb_sequence4res(xdr, cb); |
559 | if (unlikely(status)) | ||
409 | goto out; | 560 | goto out; |
410 | } | 561 | } |
411 | status = decode_cb_op_hdr(&xdr, OP_CB_RECALL); | 562 | |
563 | status = decode_cb_op_status(xdr, OP_CB_RECALL, &nfserr); | ||
564 | if (unlikely(status)) | ||
565 | goto out; | ||
566 | if (unlikely(nfserr != NFS4_OK)) | ||
567 | goto out_default; | ||
412 | out: | 568 | out: |
413 | return status; | 569 | return status; |
570 | out_default: | ||
571 | return nfs_cb_stat_to_errno(status); | ||
414 | } | 572 | } |
415 | 573 | ||
416 | /* | 574 | /* |
417 | * RPC procedure tables | 575 | * RPC procedure tables |
418 | */ | 576 | */ |
419 | #define PROC(proc, call, argtype, restype) \ | 577 | #define PROC(proc, call, argtype, restype) \ |
420 | [NFSPROC4_CLNT_##proc] = { \ | 578 | [NFSPROC4_CLNT_##proc] = { \ |
421 | .p_proc = NFSPROC4_CB_##call, \ | 579 | .p_proc = NFSPROC4_CB_##call, \ |
422 | .p_encode = (kxdrproc_t) nfs4_xdr_##argtype, \ | 580 | .p_encode = (kxdreproc_t)nfs4_xdr_enc_##argtype, \ |
423 | .p_decode = (kxdrproc_t) nfs4_xdr_##restype, \ | 581 | .p_decode = (kxdrdproc_t)nfs4_xdr_dec_##restype, \ |
424 | .p_arglen = NFS4_##argtype##_sz, \ | 582 | .p_arglen = NFS4_enc_##argtype##_sz, \ |
425 | .p_replen = NFS4_##restype##_sz, \ | 583 | .p_replen = NFS4_dec_##restype##_sz, \ |
426 | .p_statidx = NFSPROC4_CB_##call, \ | 584 | .p_statidx = NFSPROC4_CB_##call, \ |
427 | .p_name = #proc, \ | 585 | .p_name = #proc, \ |
428 | } | 586 | } |
429 | 587 | ||
430 | static struct rpc_procinfo nfs4_cb_procedures[] = { | 588 | static struct rpc_procinfo nfs4_cb_procedures[] = { |
431 | PROC(CB_NULL, NULL, enc_cb_null, dec_cb_null), | 589 | PROC(CB_NULL, NULL, cb_null, cb_null), |
432 | PROC(CB_RECALL, COMPOUND, enc_cb_recall, dec_cb_recall), | 590 | PROC(CB_RECALL, COMPOUND, cb_recall, cb_recall), |
433 | }; | 591 | }; |
434 | 592 | ||
435 | static struct rpc_version nfs_cb_version4 = { | 593 | static struct rpc_version nfs_cb_version4 = { |
436 | /* | 594 | /* |
437 | * Note on the callback rpc program version number: despite language in rfc | 595 | * Note on the callback rpc program version number: despite language in rfc |
438 | * 5661 section 18.36.3 requiring servers to use 4 in this field, the | 596 | * 5661 section 18.36.3 requiring servers to use 4 in this field, the |
@@ -440,29 +598,29 @@ static struct rpc_version nfs_cb_version4 = { | |||
440 | * in practice that appears to be what implementations use. The section | 598 | * in practice that appears to be what implementations use. The section |
441 | * 18.36.3 language is expected to be fixed in an erratum. | 599 | * 18.36.3 language is expected to be fixed in an erratum. |
442 | */ | 600 | */ |
443 | .number = 1, | 601 | .number = 1, |
444 | .nrprocs = ARRAY_SIZE(nfs4_cb_procedures), | 602 | .nrprocs = ARRAY_SIZE(nfs4_cb_procedures), |
445 | .procs = nfs4_cb_procedures | 603 | .procs = nfs4_cb_procedures |
446 | }; | 604 | }; |
447 | 605 | ||
448 | static struct rpc_version * nfs_cb_version[] = { | 606 | static struct rpc_version *nfs_cb_version[] = { |
449 | &nfs_cb_version4, | 607 | &nfs_cb_version4, |
450 | }; | 608 | }; |
451 | 609 | ||
452 | static struct rpc_program cb_program; | 610 | static struct rpc_program cb_program; |
453 | 611 | ||
454 | static struct rpc_stat cb_stats = { | 612 | static struct rpc_stat cb_stats = { |
455 | .program = &cb_program | 613 | .program = &cb_program |
456 | }; | 614 | }; |
457 | 615 | ||
458 | #define NFS4_CALLBACK 0x40000000 | 616 | #define NFS4_CALLBACK 0x40000000 |
459 | static struct rpc_program cb_program = { | 617 | static struct rpc_program cb_program = { |
460 | .name = "nfs4_cb", | 618 | .name = "nfs4_cb", |
461 | .number = NFS4_CALLBACK, | 619 | .number = NFS4_CALLBACK, |
462 | .nrvers = ARRAY_SIZE(nfs_cb_version), | 620 | .nrvers = ARRAY_SIZE(nfs_cb_version), |
463 | .version = nfs_cb_version, | 621 | .version = nfs_cb_version, |
464 | .stats = &cb_stats, | 622 | .stats = &cb_stats, |
465 | .pipe_dir_name = "/nfsd4_cb", | 623 | .pipe_dir_name = "/nfsd4_cb", |
466 | }; | 624 | }; |
467 | 625 | ||
468 | static int max_cb_time(void) | 626 | static int max_cb_time(void) |