Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / base / services / nfsd / recovery.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 <time.h>
23
24 #include "recovery.h"
25 #include "delegation.h"
26 #include "nfs41_callback.h"
27 #include "nfs41_compound.h"
28 #include "nfs41_ops.h"
29 #include "daemon_debug.h"
30
31
32 /* session/client recovery uses a lock and condition variable in nfs41_client
33 * to prevent multiple threads from attempting to recover at the same time */
34 bool_t nfs41_recovery_start_or_wait(
35 IN nfs41_client *client)
36 {
37 bool_t status = TRUE;
38
39 EnterCriticalSection(&client->recovery.lock);
40
41 if (!client->recovery.in_recovery) {
42 dprintf(1, "Entering recovery mode for client %llu\n", client->clnt_id);
43 client->recovery.in_recovery = TRUE;
44 } else {
45 status = FALSE;
46 dprintf(1, "Waiting for recovery of client %llu\n", client->clnt_id);
47 while (client->recovery.in_recovery)
48 SleepConditionVariableCS(&client->recovery.cond,
49 &client->recovery.lock, INFINITE);
50 dprintf(1, "Woke up after recovery of client %llu\n", client->clnt_id);
51 }
52
53 LeaveCriticalSection(&client->recovery.lock);
54 return status;
55 }
56
57 void nfs41_recovery_finish(
58 IN nfs41_client *client)
59 {
60 EnterCriticalSection(&client->recovery.lock);
61 dprintf(1, "Finished recovery for client %llu\n", client->clnt_id);
62 client->recovery.in_recovery = FALSE;
63 WakeAllConditionVariable(&client->recovery.cond);
64 LeaveCriticalSection(&client->recovery.lock);
65 }
66
67
68 /* session/client/state recovery */
69 int nfs41_recover_session(
70 IN nfs41_session *session,
71 IN bool_t client_state_lost)
72 {
73 enum nfsstat4 status = NFS4_OK;
74
75 restart_recovery:
76 /* recover the session */
77 status = nfs41_session_renew(session);
78
79 if (status == NFS4ERR_STALE_CLIENTID) {
80 /* recover the client */
81 client_state_lost = TRUE;
82 status = nfs41_client_renew(session->client);
83 if (status == NFS4_OK)
84 goto restart_recovery; /* resume session recovery */
85
86 eprintf("nfs41_client_renew() failed with %d\n", status);
87 } else if (status) {
88 eprintf("nfs41_session_renew() failed with %d\n", status);
89 } else if (client_state_lost) {
90 /* recover the client's state */
91 status = nfs41_recover_client_state(session, session->client);
92 if (status == NFS4ERR_BADSESSION)
93 goto restart_recovery;
94 }
95 return status;
96 }
97
98 void nfs41_recover_sequence_flags(
99 IN nfs41_session *session,
100 IN uint32_t flags)
101 {
102 const uint32_t revoked = flags &
103 (SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED
104 | SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED
105 | SEQ4_STATUS_ADMIN_STATE_REVOKED
106 | SEQ4_STATUS_RECALLABLE_STATE_REVOKED);
107 const uint32_t restarted = flags &
108 SEQ4_STATUS_RESTART_RECLAIM_NEEDED;
109
110 /* no state recovery needed */
111 if (revoked == 0 && restarted == 0)
112 return;
113
114 if (!nfs41_recovery_start_or_wait(session->client))
115 return;
116
117 if (revoked) {
118 /* free stateids and attempt to recover them */
119 nfs41_client_state_revoked(session, session->client, revoked);
120
121 /* if RESTART_RECLAIM_NEEDED is also set, just do RECLAIM_COMPLETE */
122 if (restarted) nfs41_reclaim_complete(session);
123
124 } else if (restarted) {
125 /* do server reboot state recovery */
126 uint32_t status = nfs41_recover_client_state(session, session->client);
127 if (status == NFS4ERR_BADSESSION) {
128 /* recover the session and finish state recovery */
129 nfs41_recover_session(session, TRUE);
130 }
131 }
132
133 nfs41_recovery_finish(session->client);
134 }
135
136
137 /* client state recovery for server reboot or lease expiration */
138 static int recover_open_grace(
139 IN nfs41_session *session,
140 IN nfs41_path_fh *parent,
141 IN nfs41_path_fh *file,
142 IN state_owner4 *owner,
143 IN uint32_t access,
144 IN uint32_t deny,
145 OUT stateid4 *stateid,
146 OUT open_delegation4 *delegation)
147 {
148 /* reclaim the open stateid with CLAIM_PREVIOUS */
149 open_claim4 claim;
150 claim.claim = CLAIM_PREVIOUS;
151 claim.u.prev.delegate_type = delegation->type;
152
153 return nfs41_open(session, parent, file, owner, &claim, access, deny,
154 OPEN4_NOCREATE, 0, NULL, FALSE, stateid, delegation, NULL);
155 }
156
157 static int recover_open_no_grace(
158 IN nfs41_session *session,
159 IN nfs41_path_fh *parent,
160 IN nfs41_path_fh *file,
161 IN state_owner4 *owner,
162 IN uint32_t access,
163 IN uint32_t deny,
164 OUT stateid4 *stateid,
165 OUT open_delegation4 *delegation)
166 {
167 open_claim4 claim;
168 int status;
169
170 if (delegation->type != OPEN_DELEGATE_NONE) {
171 /* attempt out-of-grace recovery with CLAIM_DELEGATE_PREV */
172 claim.claim = CLAIM_DELEGATE_PREV;
173 claim.u.deleg_prev.filename = &file->name;
174
175 status = nfs41_open(session, parent, file, owner,
176 &claim, access, deny, OPEN4_NOCREATE, 0, NULL, FALSE,
177 stateid, delegation, NULL);
178 if (status == NFS4_OK || status == NFS4ERR_BADSESSION)
179 goto out;
180
181 /* server support for CLAIM_DELEGATE_PREV is optional;
182 * fall back to CLAIM_NULL on errors */
183 }
184
185 /* attempt out-of-grace recovery with CLAIM_NULL */
186 claim.claim = CLAIM_NULL;
187 claim.u.null.filename = &file->name;
188
189 /* ask nicely for the delegation we had */
190 if (delegation->type == OPEN_DELEGATE_READ)
191 access |= OPEN4_SHARE_ACCESS_WANT_READ_DELEG;
192 else if (delegation->type == OPEN_DELEGATE_WRITE)
193 access |= OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG;
194
195 status = nfs41_open(session, parent, file, owner,
196 &claim, access, deny, OPEN4_NOCREATE, 0, NULL, FALSE,
197 stateid, delegation, NULL);
198 out:
199 return status;
200 }
201
202 static int recover_open(
203 IN nfs41_session *session,
204 IN nfs41_open_state *open,
205 IN OUT bool_t *grace)
206 {
207 open_delegation4 delegation = { 0 };
208 stateid4 stateid = { 0 };
209 int status = NFS4ERR_BADHANDLE;
210
211 /* check for an associated delegation */
212 AcquireSRWLockExclusive(&open->lock);
213 if (open->delegation.state) {
214 nfs41_delegation_state *deleg = open->delegation.state;
215 if (deleg->revoked) {
216 /* reclaim the delegation along with the open */
217 AcquireSRWLockShared(&deleg->lock);
218 delegation.type = deleg->state.type;
219 ReleaseSRWLockShared(&deleg->lock);
220 } else if (deleg->state.recalled) {
221 /* we'll need an open stateid regardless */
222 } else if (list_empty(&open->locks.list)) {
223 /* if there are locks, we need an open stateid to
224 * reclaim them; otherwise, the open can be delegated */
225 open->do_close = FALSE;
226 status = NFS4_OK;
227 }
228 }
229 ReleaseSRWLockExclusive(&open->lock);
230
231 if (status == NFS4_OK) /* use existing delegation */
232 goto out;
233
234 if (*grace) {
235 status = recover_open_grace(session, &open->parent, &open->file,
236 &open->owner, open->share_access, open->share_deny,
237 &stateid, &delegation);
238 if (status == NFS4ERR_NO_GRACE) {
239 *grace = FALSE;
240 /* send RECLAIM_COMPLETE before any out-of-grace recovery */
241 nfs41_reclaim_complete(session);
242 }
243 }
244 if (!*grace) {
245 status = recover_open_no_grace(session, &open->parent, &open->file,
246 &open->owner, open->share_access, open->share_deny,
247 &stateid, &delegation);
248 }
249
250 if (status)
251 goto out;
252
253 AcquireSRWLockExclusive(&open->lock);
254 /* update the open stateid */
255 memcpy(&open->stateid, &stateid, sizeof(stateid4));
256 open->do_close = TRUE;
257
258 if (open->delegation.state) {
259 nfs41_delegation_state *deleg = open->delegation.state;
260 if (deleg->revoked) {
261 /* update delegation state */
262 AcquireSRWLockExclusive(&deleg->lock);
263 if (delegation.type != OPEN_DELEGATE_READ &&
264 delegation.type != OPEN_DELEGATE_WRITE) {
265 eprintf("recover_open() got delegation type %u, "
266 "expected %u\n", delegation.type, deleg->state.type);
267 } else {
268 memcpy(&deleg->state, &delegation, sizeof(open_delegation4));
269 deleg->revoked = FALSE;
270 }
271 ReleaseSRWLockExclusive(&deleg->lock);
272 }
273 } else /* granted a new delegation? */
274 nfs41_delegation_granted(session, &open->parent, &open->file,
275 &delegation, FALSE, &open->delegation.state);
276 ReleaseSRWLockExclusive(&open->lock);
277 out:
278 return status;
279 }
280
281 static int recover_locks(
282 IN nfs41_session *session,
283 IN nfs41_open_state *open,
284 IN OUT bool_t *grace)
285 {
286 stateid_arg stateid;
287 struct list_entry *entry;
288 nfs41_lock_state *lock;
289 int status = NFS4_OK;
290
291 AcquireSRWLockExclusive(&open->lock);
292
293 /* initialize the open stateid for the first lock request */
294 memcpy(&stateid.stateid, &open->stateid, sizeof(stateid4));
295 stateid.type = STATEID_OPEN;
296 stateid.open = open;
297 stateid.delegation = NULL;
298
299 /* recover any locks for this open */
300 list_for_each(entry, &open->locks.list) {
301 lock = list_container(entry, nfs41_lock_state, open_entry);
302 if (lock->delegated)
303 continue;
304
305 if (*grace) {
306 status = nfs41_lock(session, &open->file, &open->owner,
307 lock->exclusive ? WRITE_LT : READ_LT, lock->offset,
308 lock->length, TRUE, FALSE, &stateid);
309 if (status == NFS4ERR_NO_GRACE) {
310 *grace = FALSE;
311 /* send RECLAIM_COMPLETE before any out-of-grace recovery */
312 nfs41_reclaim_complete(session);
313 }
314 }
315 if (!*grace) {
316 /* attempt out-of-grace recovery with a normal LOCK */
317 status = nfs41_lock(session, &open->file, &open->owner,
318 lock->exclusive ? WRITE_LT : READ_LT, lock->offset,
319 lock->length, FALSE, FALSE, &stateid);
320 }
321 if (status == NFS4ERR_BADSESSION)
322 break;
323 }
324
325 if (status != NFS4ERR_BADSESSION) {
326 /* if we got a lock stateid back, save the lock with the open */
327 if (stateid.type == STATEID_LOCK)
328 memcpy(&open->locks.stateid, &stateid.stateid, sizeof(stateid4));
329 else
330 open->locks.stateid.seqid = 0;
331 }
332
333 ReleaseSRWLockExclusive(&open->lock);
334 return status;
335 }
336
337 /* delegation recovery via WANT_DELEGATION */
338 static int recover_delegation_want(
339 IN nfs41_session *session,
340 IN nfs41_delegation_state *deleg,
341 IN OUT bool_t *grace)
342 {
343 deleg_claim4 claim;
344 open_delegation4 delegation = { 0 };
345 uint32_t want_flags = 0;
346 int status = NFS4_OK;
347
348 AcquireSRWLockShared(&deleg->lock);
349 delegation.type = deleg->state.type;
350 ReleaseSRWLockShared(&deleg->lock);
351
352 if (delegation.type == OPEN_DELEGATE_READ)
353 want_flags |= OPEN4_SHARE_ACCESS_WANT_READ_DELEG;
354 else
355 want_flags |= OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG;
356
357 if (*grace) {
358 /* recover the delegation with WANT_DELEGATION/CLAIM_PREVIOUS */
359 claim.claim = CLAIM_PREVIOUS;
360 claim.prev_delegate_type = delegation.type;
361
362 status = nfs41_want_delegation(session, &deleg->file, &claim,
363 want_flags, FALSE, &delegation);
364 if (status == NFS4ERR_NO_GRACE) {
365 *grace = FALSE;
366 /* send RECLAIM_COMPLETE before any out-of-grace recovery */
367 nfs41_reclaim_complete(session);
368 }
369 }
370 if (!*grace) {
371 /* attempt out-of-grace recovery with with CLAIM_DELEG_PREV_FH */
372 claim.claim = CLAIM_DELEG_PREV_FH;
373
374 status = nfs41_want_delegation(session, &deleg->file, &claim,
375 want_flags, FALSE, &delegation);
376 }
377 if (status)
378 goto out;
379
380 /* update delegation state */
381 AcquireSRWLockExclusive(&deleg->lock);
382 if (delegation.type != OPEN_DELEGATE_READ &&
383 delegation.type != OPEN_DELEGATE_WRITE) {
384 eprintf("recover_delegation_want() got delegation type %u, "
385 "expected %u\n", delegation.type, deleg->state.type);
386 } else {
387 memcpy(&deleg->state, &delegation, sizeof(open_delegation4));
388 deleg->revoked = FALSE;
389 }
390 ReleaseSRWLockExclusive(&deleg->lock);
391 out:
392 return status;
393 }
394
395 /* delegation recovery via OPEN (requires corresponding CLOSE) */
396 static int recover_delegation_open(
397 IN nfs41_session *session,
398 IN nfs41_delegation_state *deleg,
399 IN OUT bool_t *grace)
400 {
401 state_owner4 owner;
402 open_delegation4 delegation = { 0 };
403 stateid_arg stateid;
404 uint32_t access = OPEN4_SHARE_ACCESS_READ;
405 uint32_t deny = OPEN4_SHARE_DENY_NONE;
406 int status = NFS4_OK;
407
408 /* choose the desired access mode based on delegation type */
409 AcquireSRWLockShared(&deleg->lock);
410 delegation.type = deleg->state.type;
411 if (delegation.type == OPEN_DELEGATE_WRITE)
412 access |= OPEN4_SHARE_ACCESS_WRITE | OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG;
413 else
414 access |= OPEN4_SHARE_ACCESS_WANT_READ_DELEG;
415 ReleaseSRWLockShared(&deleg->lock);
416
417 /* construct a temporary open owner by concatenating the time
418 * in seconds with the delegation pointer */
419 time((time_t*)owner.owner);
420 memcpy(owner.owner + sizeof(time_t), deleg, sizeof(deleg));
421 owner.owner_len = sizeof(time_t) + sizeof(deleg);
422
423 if (*grace) {
424 status = recover_open_grace(session, &deleg->parent, &deleg->file,
425 &owner, access, deny, &stateid.stateid, &delegation);
426 if (status == NFS4ERR_NO_GRACE) {
427 *grace = FALSE;
428 /* send RECLAIM_COMPLETE before any out-of-grace recovery */
429 nfs41_reclaim_complete(session);
430 }
431 }
432 if (!*grace) {
433 status = recover_open_no_grace(session, &deleg->parent, &deleg->file,
434 &owner, access, deny, &stateid.stateid, &delegation);
435 }
436 if (status)
437 goto out;
438
439 /* update delegation state */
440 AcquireSRWLockExclusive(&deleg->lock);
441 if (delegation.type != OPEN_DELEGATE_READ &&
442 delegation.type != OPEN_DELEGATE_WRITE) {
443 eprintf("recover_delegation_open() got delegation type %u, "
444 "expected %u\n", delegation.type, deleg->state.type);
445 } else {
446 memcpy(&deleg->state, &delegation, sizeof(open_delegation4));
447 deleg->revoked = FALSE;
448 }
449 ReleaseSRWLockExclusive(&deleg->lock);
450
451 /* send CLOSE to free the open stateid */
452 stateid.open = NULL;
453 stateid.delegation = NULL;
454 stateid.type = STATEID_OPEN;
455 nfs41_close(session, &deleg->file, &stateid);
456 out:
457 return status;
458 }
459
460 static int recover_delegation(
461 IN nfs41_session *session,
462 IN nfs41_delegation_state *deleg,
463 IN OUT bool_t *grace,
464 IN OUT bool_t *want_supported)
465 {
466 int status;
467
468 /* 10.2.1. Delegation Recovery
469 * When a client needs to reclaim a delegation and there is no
470 * associated open, the client may use the CLAIM_PREVIOUS variant
471 * of the WANT_DELEGATION operation. However, since the server is
472 * not required to support this operation, an alternative is to
473 * reclaim via a dummy OPEN together with the delegation using an
474 * OPEN of type CLAIM_PREVIOUS. */
475 if (*want_supported)
476 status = recover_delegation_want(session, deleg, grace);
477 else
478 status = NFS4ERR_NOTSUPP;
479
480 if (status == NFS4ERR_NOTSUPP) {
481 *want_supported = FALSE;
482 status = recover_delegation_open(session, deleg, grace);
483 }
484 return status;
485 }
486
487 int nfs41_recover_client_state(
488 IN nfs41_session *session,
489 IN nfs41_client *client)
490 {
491 const struct cb_layoutrecall_args recall = { PNFS_LAYOUTTYPE_FILE,
492 PNFS_IOMODE_ANY, TRUE, { PNFS_RETURN_ALL } };
493 struct client_state *state = &session->client->state;
494 struct list_entry *entry;
495 nfs41_open_state *open;
496 nfs41_delegation_state *deleg;
497 bool_t grace = TRUE;
498 bool_t want_supported = TRUE;
499 int status = NFS4_OK;
500
501 EnterCriticalSection(&state->lock);
502
503 /* flag all delegations as revoked until successful recovery;
504 * recover_open() and recover_delegation_open() will only ask
505 * for delegations when revoked = TRUE */
506 list_for_each(entry, &state->delegations) {
507 deleg = list_container(entry, nfs41_delegation_state, client_entry);
508 deleg->revoked = TRUE;
509 }
510
511 /* recover each of the client's opens and associated delegations */
512 list_for_each(entry, &state->opens) {
513 open = list_container(entry, nfs41_open_state, client_entry);
514 status = recover_open(session, open, &grace);
515 if (status == NFS4_OK)
516 status = recover_locks(session, open, &grace);
517 if (status == NFS4ERR_BADSESSION)
518 goto unlock;
519 }
520
521 /* recover delegations that weren't associated with any opens */
522 list_for_each(entry, &state->delegations) {
523 deleg = list_container(entry, nfs41_delegation_state, client_entry);
524 if (deleg->revoked) {
525 status = recover_delegation(session,
526 deleg, &grace, &want_supported);
527 if (status == NFS4ERR_BADSESSION)
528 goto unlock;
529 }
530 }
531
532 /* return any delegations that were reclaimed as 'recalled' */
533 status = nfs41_client_delegation_recovery(client);
534 unlock:
535 LeaveCriticalSection(&state->lock);
536
537 /* revoke all of the client's layouts */
538 pnfs_file_layout_recall(client, &recall);
539
540 if (grace && status != NFS4ERR_BADSESSION) {
541 /* send reclaim_complete, but don't fail on errors */
542 nfs41_reclaim_complete(session);
543 }
544 return status;
545 }
546
547 static uint32_t stateid_array(
548 IN struct list_entry *delegations,
549 IN struct list_entry *opens,
550 OUT stateid_arg **stateids_out,
551 OUT uint32_t **statuses_out)
552 {
553 struct list_entry *entry;
554 nfs41_open_state *open;
555 nfs41_delegation_state *deleg;
556 stateid_arg *stateids = NULL;
557 uint32_t *statuses = NULL;
558 uint32_t i = 0, count = 0;
559
560 /* count how many stateids the client needs to test */
561 list_for_each(entry, delegations)
562 count++;
563 list_for_each(entry, opens)
564 count += 3; /* open and potentially lock and layout */
565
566 if (count == 0)
567 goto out;
568
569 /* allocate the stateid and status arrays */
570 stateids = calloc(count, sizeof(stateid_arg));
571 if (stateids == NULL)
572 goto out_err;
573 statuses = calloc(count, sizeof(uint32_t));
574 if (statuses == NULL)
575 goto out_err;
576 memset(statuses, NFS4ERR_BAD_STATEID, count * sizeof(uint32_t));
577
578 /* copy stateids into the array */
579 list_for_each(entry, delegations) {
580 deleg = list_container(entry, nfs41_delegation_state, client_entry);
581 AcquireSRWLockShared(&deleg->lock);
582 /* delegation stateid */
583 memcpy(&stateids[i].stateid, &deleg->state.stateid, sizeof(stateid4));
584 stateids[i].type = STATEID_DELEG_FILE;
585 stateids[i].delegation = deleg;
586 i++;
587 ReleaseSRWLockShared(&deleg->lock);
588 }
589
590 list_for_each(entry, opens) {
591 open = list_container(entry, nfs41_open_state, client_entry);
592
593 AcquireSRWLockShared(&open->lock);
594 /* open stateid */
595 memcpy(&stateids[i].stateid, &open->stateid, sizeof(stateid4));
596 stateids[i].type = STATEID_OPEN;
597 stateids[i].open = open;
598 i++;
599
600 if (open->locks.stateid.seqid) { /* lock stateid? */
601 memcpy(&stateids[i].stateid, &open->locks.stateid, sizeof(stateid4));
602 stateids[i].type = STATEID_LOCK;
603 stateids[i].open = open;
604 i++;
605 }
606
607 if (open->layout) { /* layout stateid? */
608 AcquireSRWLockShared(&open->layout->lock);
609 if (open->layout->stateid.seqid) {
610 memcpy(&stateids[i].stateid, &open->layout->stateid, sizeof(stateid4));
611 stateids[i].type = STATEID_LAYOUT;
612 stateids[i].open = open;
613 i++;
614 }
615 ReleaseSRWLockShared(&open->layout->lock);
616 }
617 ReleaseSRWLockShared(&open->lock);
618 }
619
620 count = i;
621 *stateids_out = stateids;
622 *statuses_out = statuses;
623 out:
624 return count;
625
626 out_err:
627 free(stateids);
628 free(statuses);
629 count = 0;
630 goto out;
631 }
632
633 void nfs41_client_state_revoked(
634 IN nfs41_session *session,
635 IN nfs41_client *client,
636 IN uint32_t revoked)
637 {
638 const struct cb_layoutrecall_args recall = { PNFS_LAYOUTTYPE_FILE,
639 PNFS_IOMODE_ANY, TRUE, { PNFS_RETURN_ALL } };
640 struct list_entry empty, *opens;
641 struct client_state *clientstate = &session->client->state;
642 stateid_arg *stateids = NULL;
643 uint32_t *statuses = NULL;
644 uint32_t i, count;
645 bool_t grace = TRUE;
646 bool_t want_supported = TRUE;
647
648 EnterCriticalSection(&clientstate->lock);
649
650 if (revoked == SEQ4_STATUS_RECALLABLE_STATE_REVOKED) {
651 /* only delegations were revoked. use an empty list for opens */
652 list_init(&empty);
653 opens = &empty;
654 } else {
655 opens = &clientstate->opens;
656 }
657
658 /* get an array of the client's stateids */
659 count = stateid_array(&clientstate->delegations,
660 opens, &stateids, &statuses);
661 if (count == 0)
662 goto out;
663
664 /* determine which stateids were revoked with TEST_STATEID */
665 if ((revoked & SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED) == 0)
666 nfs41_test_stateid(session, stateids, count, statuses);
667
668 /* free all revoked stateids with FREE_STATEID */
669 for (i = 0; i < count; i++)
670 if (statuses[i])
671 nfs41_free_stateid(session, &stateids[i].stateid);
672
673 /* revoke all of the client's layouts */
674 pnfs_file_layout_recall(client, &recall);
675
676 /* recover the revoked stateids */
677 for (i = 0; i < count; i++) {
678 if (statuses[i]) {
679 if (stateids[i].type == STATEID_DELEG_FILE)
680 stateids[i].delegation->revoked = TRUE;
681 else if (stateids[i].type == STATEID_OPEN)
682 recover_open(session, stateids[i].open, &grace);
683 else if (stateids[i].type == STATEID_LOCK)
684 recover_locks(session, stateids[i].open, &grace);
685 }
686 }
687 for (i = 0; i < count; i++) {
688 /* delegations that weren't recovered by recover_open() */
689 if (statuses[i] && stateids[i].type == STATEID_DELEG_FILE
690 && stateids[i].delegation->revoked)
691 recover_delegation(session, stateids[i].delegation,
692 &grace, &want_supported);
693 }
694
695 nfs41_client_delegation_recovery(client);
696 out:
697 LeaveCriticalSection(&clientstate->lock);
698 free(stateids);
699 free(statuses);
700 }
701
702
703 static bool_t recover_stateid_open(
704 IN nfs_argop4 *argop,
705 IN stateid_arg *stateid)
706 {
707 bool_t retry = FALSE;
708
709 if (stateid->open) {
710 stateid4 *source = &stateid->open->stateid;
711
712 /* if the source stateid is different, update and retry */
713 AcquireSRWLockShared(&stateid->open->lock);
714 if (memcmp(&stateid->stateid, source, sizeof(stateid4))) {
715 memcpy(&stateid->stateid, source, sizeof(stateid4));
716 retry = TRUE;
717 }
718 ReleaseSRWLockShared(&stateid->open->lock);
719 }
720 return retry;
721 }
722
723 static bool_t recover_stateid_lock(
724 IN nfs_argop4 *argop,
725 IN stateid_arg *stateid)
726 {
727 bool_t retry = FALSE;
728
729 if (stateid->open) {
730 stateid4 *source = &stateid->open->locks.stateid;
731
732 /* if the source stateid is different, update and retry */
733 AcquireSRWLockShared(&stateid->open->lock);
734 if (memcmp(&stateid->stateid, source, sizeof(stateid4))) {
735 if (argop->op == OP_LOCK && source->seqid == 0) {
736 /* resend LOCK with an open stateid */
737 nfs41_lock_args *lock = (nfs41_lock_args*)argop->arg;
738 lock->locker.new_lock_owner = 1;
739 lock->locker.u.open_owner.open_stateid = stateid;
740 lock->locker.u.open_owner.lock_owner = &stateid->open->owner;
741 source = &stateid->open->stateid;
742 }
743
744 memcpy(&stateid->stateid, source, sizeof(stateid4));
745 retry = TRUE;
746 }
747 ReleaseSRWLockShared(&stateid->open->lock);
748 }
749 return retry;
750 }
751
752 static bool_t recover_stateid_delegation(
753 IN nfs_argop4 *argop,
754 IN stateid_arg *stateid)
755 {
756 bool_t retry = FALSE;
757
758 if (stateid->open) {
759 /* if the source stateid is different, update and retry */
760 AcquireSRWLockShared(&stateid->open->lock);
761 if (argop->op == OP_OPEN && stateid->open->do_close) {
762 /* for nfs41_delegation_to_open(); if we've already reclaimed
763 * an open stateid, just fail this OPEN with BAD_STATEID */
764 } else if (stateid->open->delegation.state) {
765 nfs41_delegation_state *deleg = stateid->open->delegation.state;
766 stateid4 *source = &deleg->state.stateid;
767 AcquireSRWLockShared(&deleg->lock);
768 if (memcmp(&stateid->stateid, source, sizeof(stateid4))) {
769 memcpy(&stateid->stateid, source, sizeof(stateid4));
770 retry = TRUE;
771 }
772 ReleaseSRWLockShared(&deleg->lock);
773 }
774 ReleaseSRWLockShared(&stateid->open->lock);
775 } else if (stateid->delegation) {
776 nfs41_delegation_state *deleg = stateid->delegation;
777 stateid4 *source = &deleg->state.stateid;
778 AcquireSRWLockShared(&deleg->lock);
779 if (memcmp(&stateid->stateid, source, sizeof(stateid4))) {
780 memcpy(&stateid->stateid, source, sizeof(stateid4));
781 retry = TRUE;
782 }
783 ReleaseSRWLockShared(&deleg->lock);
784 }
785 return retry;
786 }
787
788 bool_t nfs41_recover_stateid(
789 IN nfs41_session *session,
790 IN nfs_argop4 *argop)
791 {
792 stateid_arg *stateid = NULL;
793
794 /* get the stateid_arg from the operation's arguments */
795 if (argop->op == OP_OPEN) {
796 nfs41_op_open_args *open = (nfs41_op_open_args*)argop->arg;
797 if (open->claim->claim == CLAIM_DELEGATE_CUR)
798 stateid = open->claim->u.deleg_cur.delegate_stateid;
799 else if (open->claim->claim == CLAIM_DELEG_CUR_FH)
800 stateid = open->claim->u.deleg_cur_fh.delegate_stateid;
801 } else if (argop->op == OP_CLOSE) {
802 nfs41_op_close_args *close = (nfs41_op_close_args*)argop->arg;
803 stateid = close->stateid;
804 } else if (argop->op == OP_READ) {
805 nfs41_read_args *read = (nfs41_read_args*)argop->arg;
806 stateid = read->stateid;
807 } else if (argop->op == OP_WRITE) {
808 nfs41_write_args *write = (nfs41_write_args*)argop->arg;
809 stateid = write->stateid;
810 } else if (argop->op == OP_LOCK) {
811 nfs41_lock_args *lock = (nfs41_lock_args*)argop->arg;
812 if (lock->locker.new_lock_owner)
813 stateid = lock->locker.u.open_owner.open_stateid;
814 else
815 stateid = lock->locker.u.lock_owner.lock_stateid;
816 } else if (argop->op == OP_LOCKU) {
817 nfs41_locku_args *locku = (nfs41_locku_args*)argop->arg;
818 stateid = locku->lock_stateid;
819 } else if (argop->op == OP_SETATTR) {
820 nfs41_setattr_args *setattr = (nfs41_setattr_args*)argop->arg;
821 stateid = setattr->stateid;
822 } else if (argop->op == OP_LAYOUTGET) {
823 pnfs_layoutget_args *lget = (pnfs_layoutget_args*)argop->arg;
824 stateid = lget->stateid;
825 } else if (argop->op == OP_DELEGRETURN) {
826 nfs41_delegreturn_args *dr = (nfs41_delegreturn_args*)argop->arg;
827 stateid = dr->stateid;
828 }
829 if (stateid == NULL)
830 return FALSE;
831
832 /* if there's recovery in progress, wait for it to finish */
833 EnterCriticalSection(&session->client->recovery.lock);
834 while (session->client->recovery.in_recovery)
835 SleepConditionVariableCS(&session->client->recovery.cond,
836 &session->client->recovery.lock, INFINITE);
837 LeaveCriticalSection(&session->client->recovery.lock);
838
839 switch (stateid->type) {
840 case STATEID_OPEN:
841 return recover_stateid_open(argop, stateid);
842
843 case STATEID_LOCK:
844 return recover_stateid_lock(argop, stateid);
845
846 case STATEID_DELEG_FILE:
847 return recover_stateid_delegation(argop, stateid);
848
849 default:
850 eprintf("%s can't recover stateid type %u\n",
851 nfs_opnum_to_string(argop->op), stateid->type);
852 break;
853 }
854 return FALSE;
855 }