1 /* NFSv4.1 client for Windows
2 * Copyright © 2012 The Regents of the University of Michigan
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
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.
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.
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
25 #include "delegation.h"
26 #include "nfs41_callback.h"
27 #include "nfs41_compound.h"
28 #include "nfs41_ops.h"
29 #include "daemon_debug.h"
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
)
39 EnterCriticalSection(&client
->recovery
.lock
);
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
;
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
);
53 LeaveCriticalSection(&client
->recovery
.lock
);
57 void nfs41_recovery_finish(
58 IN nfs41_client
*client
)
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
);
68 /* session/client/state recovery */
69 int nfs41_recover_session(
70 IN nfs41_session
*session
,
71 IN bool_t client_state_lost
)
73 enum nfsstat4 status
= NFS4_OK
;
76 /* recover the session */
77 status
= nfs41_session_renew(session
);
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 */
86 eprintf("nfs41_client_renew() failed with %d\n", 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
;
98 void nfs41_recover_sequence_flags(
99 IN nfs41_session
*session
,
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
;
110 /* no state recovery needed */
111 if (revoked
== 0 && restarted
== 0)
114 if (!nfs41_recovery_start_or_wait(session
->client
))
118 /* free stateids and attempt to recover them */
119 nfs41_client_state_revoked(session
, session
->client
, revoked
);
121 /* if RESTART_RECLAIM_NEEDED is also set, just do RECLAIM_COMPLETE */
122 if (restarted
) nfs41_reclaim_complete(session
);
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
);
133 nfs41_recovery_finish(session
->client
);
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
,
145 OUT stateid4
*stateid
,
146 OUT open_delegation4
*delegation
)
148 /* reclaim the open stateid with CLAIM_PREVIOUS */
150 claim
.claim
= CLAIM_PREVIOUS
;
151 claim
.u
.prev
.delegate_type
= delegation
->type
;
153 return nfs41_open(session
, parent
, file
, owner
, &claim
, access
, deny
,
154 OPEN4_NOCREATE
, 0, NULL
, FALSE
, stateid
, delegation
, NULL
);
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
,
164 OUT stateid4
*stateid
,
165 OUT open_delegation4
*delegation
)
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
;
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
)
181 /* server support for CLAIM_DELEGATE_PREV is optional;
182 * fall back to CLAIM_NULL on errors */
185 /* attempt out-of-grace recovery with CLAIM_NULL */
186 claim
.claim
= CLAIM_NULL
;
187 claim
.u
.null
.filename
= &file
->name
;
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
;
195 status
= nfs41_open(session
, parent
, file
, owner
,
196 &claim
, access
, deny
, OPEN4_NOCREATE
, 0, NULL
, FALSE
,
197 stateid
, delegation
, NULL
);
202 static int recover_open(
203 IN nfs41_session
*session
,
204 IN nfs41_open_state
*open
,
205 IN OUT bool_t
*grace
)
207 open_delegation4 delegation
= { 0 };
208 stateid4 stateid
= { 0 };
209 int status
= NFS4ERR_BADHANDLE
;
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
;
229 ReleaseSRWLockExclusive(&open
->lock
);
231 if (status
== NFS4_OK
) /* use existing delegation */
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
) {
240 /* send RECLAIM_COMPLETE before any out-of-grace recovery */
241 nfs41_reclaim_complete(session
);
245 status
= recover_open_no_grace(session
, &open
->parent
, &open
->file
,
246 &open
->owner
, open
->share_access
, open
->share_deny
,
247 &stateid
, &delegation
);
253 AcquireSRWLockExclusive(&open
->lock
);
254 /* update the open stateid */
255 memcpy(&open
->stateid
, &stateid
, sizeof(stateid4
));
256 open
->do_close
= TRUE
;
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
);
268 memcpy(&deleg
->state
, &delegation
, sizeof(open_delegation4
));
269 deleg
->revoked
= FALSE
;
271 ReleaseSRWLockExclusive(&deleg
->lock
);
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
);
281 static int recover_locks(
282 IN nfs41_session
*session
,
283 IN nfs41_open_state
*open
,
284 IN OUT bool_t
*grace
)
287 struct list_entry
*entry
;
288 nfs41_lock_state
*lock
;
289 int status
= NFS4_OK
;
291 AcquireSRWLockExclusive(&open
->lock
);
293 /* initialize the open stateid for the first lock request */
294 memcpy(&stateid
.stateid
, &open
->stateid
, sizeof(stateid4
));
295 stateid
.type
= STATEID_OPEN
;
297 stateid
.delegation
= NULL
;
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
);
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
) {
311 /* send RECLAIM_COMPLETE before any out-of-grace recovery */
312 nfs41_reclaim_complete(session
);
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
);
321 if (status
== NFS4ERR_BADSESSION
)
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
));
330 open
->locks
.stateid
.seqid
= 0;
333 ReleaseSRWLockExclusive(&open
->lock
);
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
)
344 open_delegation4 delegation
= { 0 };
345 uint32_t want_flags
= 0;
346 int status
= NFS4_OK
;
348 AcquireSRWLockShared(&deleg
->lock
);
349 delegation
.type
= deleg
->state
.type
;
350 ReleaseSRWLockShared(&deleg
->lock
);
352 if (delegation
.type
== OPEN_DELEGATE_READ
)
353 want_flags
|= OPEN4_SHARE_ACCESS_WANT_READ_DELEG
;
355 want_flags
|= OPEN4_SHARE_ACCESS_WANT_WRITE_DELEG
;
358 /* recover the delegation with WANT_DELEGATION/CLAIM_PREVIOUS */
359 claim
.claim
= CLAIM_PREVIOUS
;
360 claim
.prev_delegate_type
= delegation
.type
;
362 status
= nfs41_want_delegation(session
, &deleg
->file
, &claim
,
363 want_flags
, FALSE
, &delegation
);
364 if (status
== NFS4ERR_NO_GRACE
) {
366 /* send RECLAIM_COMPLETE before any out-of-grace recovery */
367 nfs41_reclaim_complete(session
);
371 /* attempt out-of-grace recovery with with CLAIM_DELEG_PREV_FH */
372 claim
.claim
= CLAIM_DELEG_PREV_FH
;
374 status
= nfs41_want_delegation(session
, &deleg
->file
, &claim
,
375 want_flags
, FALSE
, &delegation
);
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
);
387 memcpy(&deleg
->state
, &delegation
, sizeof(open_delegation4
));
388 deleg
->revoked
= FALSE
;
390 ReleaseSRWLockExclusive(&deleg
->lock
);
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
)
402 open_delegation4 delegation
= { 0 };
404 uint32_t access
= OPEN4_SHARE_ACCESS_READ
;
405 uint32_t deny
= OPEN4_SHARE_DENY_NONE
;
406 int status
= NFS4_OK
;
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
;
414 access
|= OPEN4_SHARE_ACCESS_WANT_READ_DELEG
;
415 ReleaseSRWLockShared(&deleg
->lock
);
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
);
424 status
= recover_open_grace(session
, &deleg
->parent
, &deleg
->file
,
425 &owner
, access
, deny
, &stateid
.stateid
, &delegation
);
426 if (status
== NFS4ERR_NO_GRACE
) {
428 /* send RECLAIM_COMPLETE before any out-of-grace recovery */
429 nfs41_reclaim_complete(session
);
433 status
= recover_open_no_grace(session
, &deleg
->parent
, &deleg
->file
,
434 &owner
, access
, deny
, &stateid
.stateid
, &delegation
);
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
);
446 memcpy(&deleg
->state
, &delegation
, sizeof(open_delegation4
));
447 deleg
->revoked
= FALSE
;
449 ReleaseSRWLockExclusive(&deleg
->lock
);
451 /* send CLOSE to free the open stateid */
453 stateid
.delegation
= NULL
;
454 stateid
.type
= STATEID_OPEN
;
455 nfs41_close(session
, &deleg
->file
, &stateid
);
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
)
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. */
476 status
= recover_delegation_want(session
, deleg
, grace
);
478 status
= NFS4ERR_NOTSUPP
;
480 if (status
== NFS4ERR_NOTSUPP
) {
481 *want_supported
= FALSE
;
482 status
= recover_delegation_open(session
, deleg
, grace
);
487 int nfs41_recover_client_state(
488 IN nfs41_session
*session
,
489 IN nfs41_client
*client
)
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
;
498 bool_t want_supported
= TRUE
;
499 int status
= NFS4_OK
;
501 EnterCriticalSection(&state
->lock
);
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
;
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
)
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
)
532 /* return any delegations that were reclaimed as 'recalled' */
533 status
= nfs41_client_delegation_recovery(client
);
535 LeaveCriticalSection(&state
->lock
);
537 /* revoke all of the client's layouts */
538 pnfs_file_layout_recall(client
, &recall
);
540 if (grace
&& status
!= NFS4ERR_BADSESSION
) {
541 /* send reclaim_complete, but don't fail on errors */
542 nfs41_reclaim_complete(session
);
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
)
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;
560 /* count how many stateids the client needs to test */
561 list_for_each(entry
, delegations
)
563 list_for_each(entry
, opens
)
564 count
+= 3; /* open and potentially lock and layout */
569 /* allocate the stateid and status arrays */
570 stateids
= calloc(count
, sizeof(stateid_arg
));
571 if (stateids
== NULL
)
573 statuses
= calloc(count
, sizeof(uint32_t));
574 if (statuses
== NULL
)
576 memset(statuses
, NFS4ERR_BAD_STATEID
, count
* sizeof(uint32_t));
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
;
587 ReleaseSRWLockShared(&deleg
->lock
);
590 list_for_each(entry
, opens
) {
591 open
= list_container(entry
, nfs41_open_state
, client_entry
);
593 AcquireSRWLockShared(&open
->lock
);
595 memcpy(&stateids
[i
].stateid
, &open
->stateid
, sizeof(stateid4
));
596 stateids
[i
].type
= STATEID_OPEN
;
597 stateids
[i
].open
= open
;
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
;
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
;
615 ReleaseSRWLockShared(&open
->layout
->lock
);
617 ReleaseSRWLockShared(&open
->lock
);
621 *stateids_out
= stateids
;
622 *statuses_out
= statuses
;
633 void nfs41_client_state_revoked(
634 IN nfs41_session
*session
,
635 IN nfs41_client
*client
,
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
;
646 bool_t want_supported
= TRUE
;
648 EnterCriticalSection(&clientstate
->lock
);
650 if (revoked
== SEQ4_STATUS_RECALLABLE_STATE_REVOKED
) {
651 /* only delegations were revoked. use an empty list for opens */
655 opens
= &clientstate
->opens
;
658 /* get an array of the client's stateids */
659 count
= stateid_array(&clientstate
->delegations
,
660 opens
, &stateids
, &statuses
);
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
);
668 /* free all revoked stateids with FREE_STATEID */
669 for (i
= 0; i
< count
; i
++)
671 nfs41_free_stateid(session
, &stateids
[i
].stateid
);
673 /* revoke all of the client's layouts */
674 pnfs_file_layout_recall(client
, &recall
);
676 /* recover the revoked stateids */
677 for (i
= 0; i
< count
; 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
);
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
);
695 nfs41_client_delegation_recovery(client
);
697 LeaveCriticalSection(&clientstate
->lock
);
703 static bool_t
recover_stateid_open(
704 IN nfs_argop4
*argop
,
705 IN stateid_arg
*stateid
)
707 bool_t retry
= FALSE
;
710 stateid4
*source
= &stateid
->open
->stateid
;
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
));
718 ReleaseSRWLockShared(&stateid
->open
->lock
);
723 static bool_t
recover_stateid_lock(
724 IN nfs_argop4
*argop
,
725 IN stateid_arg
*stateid
)
727 bool_t retry
= FALSE
;
730 stateid4
*source
= &stateid
->open
->locks
.stateid
;
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
;
744 memcpy(&stateid
->stateid
, source
, sizeof(stateid4
));
747 ReleaseSRWLockShared(&stateid
->open
->lock
);
752 static bool_t
recover_stateid_delegation(
753 IN nfs_argop4
*argop
,
754 IN stateid_arg
*stateid
)
756 bool_t retry
= FALSE
;
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
));
772 ReleaseSRWLockShared(&deleg
->lock
);
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
));
783 ReleaseSRWLockShared(&deleg
->lock
);
788 bool_t
nfs41_recover_stateid(
789 IN nfs41_session
*session
,
790 IN nfs_argop4
*argop
)
792 stateid_arg
*stateid
= NULL
;
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
;
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
;
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
);
839 switch (stateid
->type
) {
841 return recover_stateid_open(argop
, stateid
);
844 return recover_stateid_lock(argop
, stateid
);
846 case STATEID_DELEG_FILE
:
847 return recover_stateid_delegation(argop
, stateid
);
850 eprintf("%s can't recover stateid type %u\n",
851 nfs_opnum_to_string(argop
->op
), stateid
->type
);