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 } 548