[CMAKE/CLANG-CL] Silence some clang-cl warnings in consistency with our gcc build.
[reactos.git] / base / services / nfsd / callback_server.c
1 /* NFSv4.1 client for Windows
2 * Copyright © 2012 The Regents of the University of Michigan
3 *
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
6 *
7 * This library is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2.1 of the License, or (at
10 * your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * without any warranty; without even the implied warranty of merchantability
14 * or fitness for a particular purpose. See the GNU Lesser General Public
15 * License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 */
21
22 #include <windows.h>
23 #include <strsafe.h>
24
25 #include "nfs41_ops.h"
26 #include "delegation.h"
27 #include "nfs41_callback.h"
28 #include "daemon_debug.h"
29
30
31 #define CBSLVL 2 /* dprintf level for callback server logging */
32
33
34 static const char g_server_tag[] = "ms-nfs41-callback";
35
36
37 /* callback session */
38 static void replay_cache_write(
39 IN nfs41_cb_session *session,
40 IN struct cb_compound_args *args,
41 IN struct cb_compound_res *res,
42 IN bool_t cachethis);
43
44 void nfs41_callback_session_init(
45 IN nfs41_session *session)
46 {
47 /* initialize the replay cache with status NFS4ERR_SEQ_MISORDERED */
48 struct cb_compound_res res = { 0 };
49 StringCchCopyA(res.tag.str, CB_COMPOUND_MAX_TAG, g_server_tag);
50 res.tag.len = sizeof(g_server_tag);
51 res.status = NFS4ERR_SEQ_MISORDERED;
52
53 session->cb_session.cb_sessionid = session->session_id;
54
55 replay_cache_write(&session->cb_session, NULL, &res, FALSE);
56 }
57
58
59 /* OP_CB_LAYOUTRECALL */
60 static enum_t handle_cb_layoutrecall(
61 IN nfs41_rpc_clnt *rpc_clnt,
62 IN struct cb_layoutrecall_args *args,
63 OUT struct cb_layoutrecall_res *res)
64 {
65 enum pnfs_status status;
66
67 status = pnfs_file_layout_recall(rpc_clnt->client, args);
68 switch (status) {
69 case PNFS_PENDING:
70 /* not enough information to process the recall yet */
71 res->status = NFS4ERR_DELAY;
72 break;
73 default:
74 /* forgetful model for layout recalls */
75 res->status = NFS4ERR_NOMATCHING_LAYOUT;
76 break;
77 }
78
79 dprintf(CBSLVL, " OP_CB_LAYOUTRECALL { %s, %s, recall %u } %s\n",
80 pnfs_layout_type_string(args->type),
81 pnfs_iomode_string(args->iomode), args->recall.type,
82 nfs_error_string(res->status));
83 return res->status;
84 }
85
86 /* OP_CB_RECALL_SLOT */
87 static enum_t handle_cb_recall_slot(
88 IN nfs41_rpc_clnt *rpc_clnt,
89 IN struct cb_recall_slot_args *args,
90 OUT struct cb_recall_slot_res *res)
91 {
92 res->status = nfs41_session_recall_slot(rpc_clnt->client->session,
93 args->target_highest_slotid);
94
95 dprintf(CBSLVL, " OP_CB_RECALL_SLOT { %u } %s\n",
96 args->target_highest_slotid, nfs_error_string(res->status));
97 return res->status;
98 }
99
100 /* OP_CB_SEQUENCE */
101 static enum_t handle_cb_sequence(
102 IN nfs41_rpc_clnt *rpc_clnt,
103 IN struct cb_sequence_args *args,
104 OUT struct cb_sequence_res *res,
105 OUT nfs41_cb_session **session_out,
106 OUT bool_t *cachethis)
107 {
108 nfs41_cb_session *cb_session = &rpc_clnt->client->session->cb_session;
109 uint32_t status = NFS4_OK;
110 res->status = NFS4_OK;
111
112 *session_out = cb_session;
113
114 /* validate the sessionid */
115 if (memcmp(cb_session->cb_sessionid, args->sessionid,
116 NFS4_SESSIONID_SIZE)) {
117 eprintf("[cb] received sessionid doesn't match session\n");
118 res->status = NFS4ERR_BADSESSION;
119 goto out;
120 }
121
122 /* we only support 1 slot for the back channel so slotid MUST be 0 */
123 if (args->slotid != 0) {
124 eprintf("[cb] received unexpected slotid=%d\n", args->slotid);
125 res->status = NFS4ERR_BADSLOT;
126 goto out;
127 }
128 if (args->highest_slotid != 0) {
129 eprintf("[cb] received unexpected highest_slotid=%d\n",
130 args->highest_slotid);
131 res->status = NFS4ERR_BAD_HIGH_SLOT;
132 goto out;
133 }
134
135 /* check for a retry with the same seqid */
136 if (args->sequenceid == cb_session->cb_seqnum) {
137 if (!cb_session->replay.res.length) {
138 /* return success for sequence, but fail the next operation */
139 res->status = NFS4_OK;
140 status = NFS4ERR_RETRY_UNCACHED_REP;
141 } else {
142 /* return NFS4ERR_SEQ_FALSE_RETRY for all replays; if the retry
143 * turns out to be valid, this response will be replaced anyway */
144 status = res->status = NFS4ERR_SEQ_FALSE_RETRY;
145 }
146 goto out;
147 }
148
149 /* error on any unexpected seqids */
150 if (args->sequenceid != cb_session->cb_seqnum+1) {
151 eprintf("[cb] bad received seq#=%d, expected=%d\n",
152 args->sequenceid, cb_session->cb_seqnum+1);
153 res->status = NFS4ERR_SEQ_MISORDERED;
154 goto out;
155 }
156
157 cb_session->cb_seqnum = args->sequenceid;
158 *cachethis = args->cachethis;
159
160 memcpy(res->ok.sessionid, args->sessionid, NFS4_SESSIONID_SIZE);
161 res->ok.sequenceid = args->sequenceid;
162 res->ok.slotid = args->slotid;
163 res->ok.highest_slotid = args->highest_slotid;
164 res->ok.target_highest_slotid = args->highest_slotid;
165
166 out:
167 dprintf(CBSLVL, " OP_CB_SEQUENCE { seqid %u, slot %u, cachethis %d } "
168 "%s\n", args->sequenceid, args->slotid, args->cachethis,
169 nfs_error_string(res->status));
170 return status;
171 }
172
173 /* OP_CB_GETATTR */
174 static enum_t handle_cb_getattr(
175 IN nfs41_rpc_clnt *rpc_clnt,
176 IN struct cb_getattr_args *args,
177 OUT struct cb_getattr_res *res)
178 {
179 /* look up cached attributes for the given filehandle */
180 res->status = nfs41_delegation_getattr(rpc_clnt->client,
181 &args->fh, &args->attr_request, &res->info);
182 return res->status;
183 }
184
185 /* OP_CB_RECALL */
186 static enum_t handle_cb_recall(
187 IN nfs41_rpc_clnt *rpc_clnt,
188 IN struct cb_recall_args *args,
189 OUT struct cb_recall_res *res)
190 {
191 /* return the delegation asynchronously */
192 res->status = nfs41_delegation_recall(rpc_clnt->client,
193 &args->fh, &args->stateid, args->truncate);
194 return res->status;
195 }
196
197 /* OP_CB_NOTIFY_DEVICEID */
198 static enum_t handle_cb_notify_deviceid(
199 IN nfs41_rpc_clnt *rpc_clnt,
200 IN struct cb_notify_deviceid_args *args,
201 OUT struct cb_notify_deviceid_res *res)
202 {
203 uint32_t i;
204 for (i = 0; i < args->change_count; i++) {
205 pnfs_file_device_notify(rpc_clnt->client->devices,
206 &args->change_list[i]);
207 }
208 res->status = NFS4_OK;
209 return res->status;
210 }
211
212 static void replay_cache_write(
213 IN nfs41_cb_session *session,
214 IN OPTIONAL struct cb_compound_args *args,
215 IN struct cb_compound_res *res,
216 IN bool_t cachethis)
217 {
218 XDR xdr;
219 uint32_t i;
220
221 session->replay.arg.length = 0;
222 session->replay.res.length = 0;
223
224 /* encode the reply directly into the replay cache */
225 xdrmem_create(&xdr, (char*)session->replay.res.buffer,
226 NFS41_MAX_SERVER_CACHE, XDR_ENCODE);
227
228 /* always try to cache the result */
229 if (proc_cb_compound_res(&xdr, res)) {
230 session->replay.res.length = XDR_GETPOS(&xdr);
231
232 if (args) {
233 /* encode the arguments into the request cache */
234 xdrmem_create(&xdr, (char*)session->replay.arg.buffer,
235 NFS41_MAX_SERVER_CACHE, XDR_ENCODE);
236
237 if (proc_cb_compound_args(&xdr, args))
238 session->replay.arg.length = XDR_GETPOS(&xdr);
239 }
240 } else if (cachethis) {
241 /* on failure, only return errors if caching was requested */
242 res->status = NFS4ERR_REP_TOO_BIG_TO_CACHE;
243
244 /* find the first operation that failed to encode */
245 for (i = 0; i < res->resarray_count; i++) {
246 if (!res->resarray[i].xdr_ok) {
247 res->resarray[i].res.status = NFS4ERR_REP_TOO_BIG_TO_CACHE;
248 res->resarray_count = i + 1;
249 break;
250 }
251 }
252 }
253 }
254
255 static bool_t replay_validate_args(
256 IN struct cb_compound_args *args,
257 IN const struct replay_cache *cache)
258 {
259 char buffer[NFS41_MAX_SERVER_CACHE];
260 XDR xdr;
261
262 /* encode the current arguments into a temporary buffer */
263 xdrmem_create(&xdr, buffer, NFS41_MAX_SERVER_CACHE, XDR_ENCODE);
264
265 if (!proc_cb_compound_args(&xdr, args))
266 return FALSE;
267
268 /* must match the cached length */
269 if (XDR_GETPOS(&xdr) != cache->length)
270 return FALSE;
271
272 /* must match the cached buffer contents */
273 return memcmp(cache->buffer, buffer, cache->length) == 0;
274 }
275
276 static bool_t replay_validate_ops(
277 IN const struct cb_compound_args *args,
278 IN const struct cb_compound_res *res)
279 {
280 uint32_t i;
281 for (i = 0; i < res->resarray_count; i++) {
282 /* can't have more operations than the request */
283 if (i >= args->argarray_count)
284 return FALSE;
285
286 /* each opnum must match the request */
287 if (args->argarray[i].opnum != res->resarray[i].opnum)
288 return FALSE;
289
290 if (res->resarray[i].res.status)
291 break;
292 }
293 return TRUE;
294 }
295
296 static int replay_cache_read(
297 IN nfs41_cb_session *session,
298 IN struct cb_compound_args *args,
299 OUT struct cb_compound_res **res_out)
300 {
301 XDR xdr;
302 struct cb_compound_res *replay;
303 struct cb_compound_res *res = *res_out;
304 uint32_t status = NFS4_OK;
305
306 replay = calloc(1, sizeof(struct cb_compound_res));
307 if (replay == NULL) {
308 eprintf("[cb] failed to allocate replay buffer\n");
309 status = NFS4ERR_SERVERFAULT;
310 goto out;
311 }
312
313 /* decode the response from the replay cache */
314 xdrmem_create(&xdr, (char*)session->replay.res.buffer,
315 NFS41_MAX_SERVER_CACHE, XDR_DECODE);
316 if (!proc_cb_compound_res(&xdr, replay)) {
317 eprintf("[cb] failed to decode replay buffer\n");
318 status = NFS4ERR_SEQ_FALSE_RETRY;
319 goto out_free_replay;
320 }
321
322 /* if we cached the arguments, use them to validate the retry */
323 if (session->replay.arg.length) {
324 if (!replay_validate_args(args, &session->replay.arg)) {
325 eprintf("[cb] retry attempt with different arguments\n");
326 status = NFS4ERR_SEQ_FALSE_RETRY;
327 goto out_free_replay;
328 }
329 } else { /* otherwise, comparing opnums is the best we can do */
330 if (!replay_validate_ops(args, replay)) {
331 eprintf("[cb] retry attempt with different operations\n");
332 status = NFS4ERR_SEQ_FALSE_RETRY;
333 goto out_free_replay;
334 }
335 }
336
337 /* free previous response and replace it with the replay */
338 xdr.x_op = XDR_FREE;
339 proc_cb_compound_res(&xdr, res);
340
341 dprintf(2, "[cb] retry: returning cached response\n");
342
343 *res_out = replay;
344 out:
345 return status;
346
347 out_free_replay:
348 xdr.x_op = XDR_FREE;
349 proc_cb_compound_res(&xdr, replay);
350 goto out;
351 }
352
353 /* CB_COMPOUND */
354 static void handle_cb_compound(nfs41_rpc_clnt *rpc_clnt, cb_req *req, struct cb_compound_res **reply)
355 {
356 struct cb_compound_args args = { 0 };
357 struct cb_compound_res *res = NULL;
358 struct cb_argop *argop;
359 struct cb_resop *resop;
360 XDR *xdr = (XDR*)req->xdr;
361 nfs41_cb_session *session = NULL;
362 bool_t cachethis = FALSE;
363 uint32_t i, status = NFS4_OK;
364
365 dprintf(CBSLVL, "--> handle_cb_compound()\n");
366
367 /* decode the arguments */
368 if (!proc_cb_compound_args(xdr, &args)) {
369 status = NFS4ERR_BADXDR;
370 eprintf("failed to decode compound arguments\n");
371 }
372
373 /* allocate the compound results */
374 res = calloc(1, sizeof(struct cb_compound_res));
375 if (res == NULL) {
376 status = NFS4ERR_SERVERFAULT;
377 goto out;
378 }
379 res->status = status;
380 StringCchCopyA(res->tag.str, CB_COMPOUND_MAX_TAG, g_server_tag);
381 res->tag.str[CB_COMPOUND_MAX_TAG-1] = 0;
382 res->tag.len = (uint32_t)strlen(res->tag.str);
383 res->resarray = calloc(args.argarray_count, sizeof(struct cb_resop));
384 if (res->resarray == NULL) {
385 res->status = NFS4ERR_SERVERFAULT;
386 goto out;
387 }
388
389 dprintf(CBSLVL, "CB_COMPOUND('%s', %u)\n", args.tag.str, args.argarray_count);
390 if (args.minorversion != 1) {
391 res->status = NFS4ERR_MINOR_VERS_MISMATCH; //XXXXX
392 eprintf("args.minorversion %u != 1\n", args.minorversion);
393 goto out;
394 }
395
396 /* handle each operation in the compound */
397 for (i = 0; i < args.argarray_count && res->status == NFS4_OK; i++) {
398 argop = &args.argarray[i];
399 resop = &res->resarray[i];
400 resop->opnum = argop->opnum;
401 res->resarray_count++;
402
403 /* 20.9.3: The error NFS4ERR_SEQUENCE_POS MUST be returned
404 * when CB_SEQUENCE is found in any position in a CB_COMPOUND
405 * beyond the first. If any other operation is in the first
406 * position of CB_COMPOUND, NFS4ERR_OP_NOT_IN_SESSION MUST
407 * be returned.
408 */
409 if (i == 0 && argop->opnum != OP_CB_SEQUENCE) {
410 res->status = resop->res.status = NFS4ERR_OP_NOT_IN_SESSION;
411 break;
412 }
413 if (i != 0 && argop->opnum == OP_CB_SEQUENCE) {
414 res->status = resop->res.status = NFS4ERR_SEQUENCE_POS;
415 break;
416 }
417 if (status == NFS4ERR_RETRY_UNCACHED_REP) {
418 res->status = resop->res.status = status;
419 break;
420 }
421
422 switch (argop->opnum) {
423 case OP_CB_LAYOUTRECALL:
424 dprintf(1, "OP_CB_LAYOUTRECALL\n");
425 res->status = handle_cb_layoutrecall(rpc_clnt,
426 &argop->args.layoutrecall, &resop->res.layoutrecall);
427 break;
428 case OP_CB_RECALL_SLOT:
429 dprintf(1, "OP_CB_RECALL_SLOT\n");
430 res->status = handle_cb_recall_slot(rpc_clnt,
431 &argop->args.recall_slot, &resop->res.recall_slot);
432 break;
433 case OP_CB_SEQUENCE:
434 dprintf(1, "OP_CB_SEQUENCE\n");
435 status = handle_cb_sequence(rpc_clnt, &argop->args.sequence,
436 &resop->res.sequence, &session, &cachethis);
437
438 if (status == NFS4ERR_SEQ_FALSE_RETRY) {
439 /* replace the current results with the cached response */
440 status = replay_cache_read(session, &args, &res);
441 if (status) res->status = status;
442 goto out;
443 }
444
445 if (status == NFS4_OK)
446 res->status = resop->res.sequence.status;
447 break;
448 case OP_CB_GETATTR:
449 dprintf(1, "OP_CB_GETATTR\n");
450 res->status = handle_cb_getattr(rpc_clnt,
451 &argop->args.getattr, &resop->res.getattr);
452 break;
453 case OP_CB_RECALL:
454 dprintf(1, "OP_CB_RECALL\n");
455 res->status = handle_cb_recall(rpc_clnt,
456 &argop->args.recall, &resop->res.recall);
457 break;
458 case OP_CB_NOTIFY:
459 dprintf(1, "OP_CB_NOTIFY\n");
460 res->status = NFS4ERR_NOTSUPP;
461 break;
462 case OP_CB_PUSH_DELEG:
463 dprintf(1, "OP_CB_PUSH_DELEG\n");
464 res->status = NFS4ERR_NOTSUPP;
465 break;
466 case OP_CB_RECALL_ANY:
467 dprintf(1, "OP_CB_RECALL_ANY\n");
468 res->status = NFS4ERR_NOTSUPP;
469 break;
470 case OP_CB_RECALLABLE_OBJ_AVAIL:
471 dprintf(1, "OP_CB_RECALLABLE_OBJ_AVAIL\n");
472 res->status = NFS4ERR_NOTSUPP;
473 break;
474 case OP_CB_WANTS_CANCELLED:
475 dprintf(1, "OP_CB_WANTS_CANCELLED\n");
476 res->status = NFS4ERR_NOTSUPP;
477 break;
478 case OP_CB_NOTIFY_LOCK:
479 dprintf(1, "OP_CB_NOTIFY_LOCK\n");
480 res->status = NFS4ERR_NOTSUPP;
481 break;
482 case OP_CB_NOTIFY_DEVICEID:
483 dprintf(1, "OP_CB_NOTIFY_DEVICEID\n");
484 res->status = NFS4_OK;
485 break;
486 case OP_CB_ILLEGAL:
487 dprintf(1, "OP_CB_ILLEGAL\n");
488 res->status = NFS4ERR_NOTSUPP;
489 break;
490 default:
491 eprintf("operation %u not supported\n", argop->opnum);
492 res->status = NFS4ERR_NOTSUPP;
493 break;
494 }
495 }
496
497 /* always attempt to cache the reply */
498 if (session)
499 replay_cache_write(session, &args, res, cachethis);
500 out:
501 /* free the arguments */
502 xdr->x_op = XDR_FREE;
503 proc_cb_compound_args(xdr, &args);
504
505 *reply = res;
506 dprintf(CBSLVL, "<-- handle_cb_compound() returning %s (%u results)\n",
507 nfs_error_string(res ? res->status : status),
508 res ? res->resarray_count : 0);
509 }
510
511 #ifdef __REACTOS__
512 int nfs41_handle_callback(void *rpc_clnt, void *cb, void * dummy)
513 {
514 struct cb_compound_res **reply = dummy;
515 #else
516 int nfs41_handle_callback(void *rpc_clnt, void *cb, struct cb_compound_res **reply)
517 {
518 #endif
519 nfs41_rpc_clnt *rpc = (nfs41_rpc_clnt *)rpc_clnt;
520 cb_req *request = (cb_req *)cb;
521 uint32_t status = 0;
522
523 dprintf(1, "nfs41_handle_callback: received call\n");
524 if (request->rq_prog != NFS41_RPC_CBPROGRAM) {
525 eprintf("invalid rpc program %u\n", request->rq_prog);
526 status = 2;
527 goto out;
528 }
529
530 switch (request->rq_proc) {
531 case CB_NULL:
532 dprintf(1, "CB_NULL\n");
533 break;
534
535 case CB_COMPOUND:
536 dprintf(1, "CB_COMPOUND\n");
537 handle_cb_compound(rpc, request, reply);
538 break;
539
540 default:
541 dprintf(1, "invalid rpc procedure %u\n", request->rq_proc);
542 status = 3;
543 goto out;
544 }
545 out:
546 return status;
547 }