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
22 #include "delegation.h"
23 #include "nfs41_ops.h"
24 #include "name_cache.h"
26 #include "daemon_debug.h"
29 #include "nfs41_driver.h" /* for making downcall to invalidate cache */
32 #define DGLVL 2 /* dprintf level for delegation logging */
35 /* allocation and reference counting */
36 static int delegation_create(
37 IN
const nfs41_path_fh
*parent
,
38 IN
const nfs41_path_fh
*file
,
39 IN
const open_delegation4
*delegation
,
40 OUT nfs41_delegation_state
**deleg_out
)
42 nfs41_delegation_state
*state
;
43 int status
= NO_ERROR
;
45 state
= calloc(1, sizeof(nfs41_delegation_state
));
47 status
= GetLastError();
51 memcpy(&state
->state
, delegation
, sizeof(open_delegation4
));
53 abs_path_copy(&state
->path
, file
->path
);
54 path_fh_init(&state
->file
, &state
->path
);
55 fh_copy(&state
->file
.fh
, &file
->fh
);
56 path_fh_init(&state
->parent
, &state
->path
);
57 last_component(state
->path
.path
, state
->file
.name
.name
,
59 fh_copy(&state
->parent
.fh
, &parent
->fh
);
61 list_init(&state
->client_entry
);
62 state
->status
= DELEGATION_GRANTED
;
63 InitializeSRWLock(&state
->lock
);
64 InitializeConditionVariable(&state
->cond
);
71 void nfs41_delegation_ref(
72 IN nfs41_delegation_state
*state
)
74 const LONG count
= InterlockedIncrement(&state
->ref_count
);
75 dprintf(DGLVL
, "nfs41_delegation_ref(%s) count %d\n",
76 state
->path
.path
, count
);
79 void nfs41_delegation_deref(
80 IN nfs41_delegation_state
*state
)
82 const LONG count
= InterlockedDecrement(&state
->ref_count
);
83 dprintf(DGLVL
, "nfs41_delegation_deref(%s) count %d\n",
84 state
->path
.path
, count
);
89 #define open_entry(pos) list_container(pos, nfs41_open_state, client_entry)
91 static void delegation_remove(
92 IN nfs41_client
*client
,
93 IN nfs41_delegation_state
*deleg
)
95 struct list_entry
*entry
;
97 /* remove from the client's list */
98 EnterCriticalSection(&client
->state
.lock
);
99 list_remove(&deleg
->client_entry
);
101 /* remove from each associated open */
102 list_for_each(entry
, &client
->state
.opens
) {
103 nfs41_open_state
*open
= open_entry(entry
);
104 AcquireSRWLockExclusive(&open
->lock
);
105 if (open
->delegation
.state
== deleg
) {
106 /* drop the delegation reference */
107 nfs41_delegation_deref(open
->delegation
.state
);
108 open
->delegation
.state
= NULL
;
110 ReleaseSRWLockExclusive(&open
->lock
);
112 LeaveCriticalSection(&client
->state
.lock
);
114 /* signal threads waiting on delegreturn */
115 AcquireSRWLockExclusive(&deleg
->lock
);
116 deleg
->status
= DELEGATION_RETURNED
;
117 WakeAllConditionVariable(&deleg
->cond
);
118 ReleaseSRWLockExclusive(&deleg
->lock
);
120 /* release the client's reference */
121 nfs41_delegation_deref(deleg
);
125 /* delegation return */
126 #define lock_entry(pos) list_container(pos, nfs41_lock_state, open_entry)
128 static bool_t
has_delegated_locks(
129 IN nfs41_open_state
*open
)
131 struct list_entry
*entry
;
132 list_for_each(entry
, &open
->locks
.list
) {
133 if (lock_entry(entry
)->delegated
)
139 static int open_deleg_cmp(const struct list_entry
*entry
, const void *value
)
141 nfs41_open_state
*open
= open_entry(entry
);
144 /* open must match the delegation and have state to reclaim */
145 AcquireSRWLockShared(&open
->lock
);
146 if (open
->delegation
.state
!= value
) goto out
;
147 if (open
->do_close
&& !has_delegated_locks(open
)) goto out
;
150 ReleaseSRWLockShared(&open
->lock
);
154 /* find the first open that needs recovery */
155 static nfs41_open_state
* deleg_open_find(
156 IN
struct client_state
*state
,
157 IN
const nfs41_delegation_state
*deleg
)
159 struct list_entry
*entry
;
160 nfs41_open_state
*open
= NULL
;
162 EnterCriticalSection(&state
->lock
);
163 entry
= list_search(&state
->opens
, deleg
, open_deleg_cmp
);
165 open
= open_entry(entry
);
166 nfs41_open_state_ref(open
); /* return a reference */
168 LeaveCriticalSection(&state
->lock
);
172 /* find the first lock that needs recovery */
173 static bool_t
deleg_lock_find(
174 IN nfs41_open_state
*open
,
175 OUT nfs41_lock_state
*lock_out
)
177 struct list_entry
*entry
;
178 bool_t found
= FALSE
;
180 AcquireSRWLockShared(&open
->lock
);
181 list_for_each(entry
, &open
->locks
.list
) {
182 nfs41_lock_state
*lock
= lock_entry(entry
);
183 if (lock
->delegated
) {
184 /* copy offset, length, type */
185 lock_out
->offset
= lock
->offset
;
186 lock_out
->length
= lock
->length
;
187 lock_out
->exclusive
= lock
->exclusive
;
188 lock_out
->id
= lock
->id
;
193 ReleaseSRWLockShared(&open
->lock
);
197 /* find the matching lock by id, and reset lock.delegated */
198 static void deleg_lock_update(
199 IN nfs41_open_state
*open
,
200 IN
const nfs41_lock_state
*source
)
202 struct list_entry
*entry
;
204 AcquireSRWLockExclusive(&open
->lock
);
205 list_for_each(entry
, &open
->locks
.list
) {
206 nfs41_lock_state
*lock
= lock_entry(entry
);
207 if (lock
->id
== source
->id
) {
208 lock
->delegated
= FALSE
;
212 ReleaseSRWLockExclusive(&open
->lock
);
215 static int delegation_flush_locks(
216 IN nfs41_open_state
*open
,
217 IN bool_t try_recovery
)
220 nfs41_lock_state lock
;
221 int status
= NFS4_OK
;
224 stateid
.delegation
= NULL
;
226 /* get the starting open/lock stateid */
227 AcquireSRWLockShared(&open
->lock
);
228 if (open
->locks
.stateid
.seqid
) {
229 memcpy(&stateid
.stateid
, &open
->locks
.stateid
, sizeof(stateid4
));
230 stateid
.type
= STATEID_LOCK
;
232 memcpy(&stateid
.stateid
, &open
->stateid
, sizeof(stateid4
));
233 stateid
.type
= STATEID_OPEN
;
235 ReleaseSRWLockShared(&open
->lock
);
237 /* send LOCK requests for each delegated lock range */
238 while (deleg_lock_find(open
, &lock
)) {
239 status
= nfs41_lock(open
->session
, &open
->file
,
240 &open
->owner
, lock
.exclusive
? WRITE_LT
: READ_LT
,
241 lock
.offset
, lock
.length
, FALSE
, try_recovery
, &stateid
);
244 deleg_lock_update(open
, &lock
);
247 /* save the updated lock stateid */
248 if (stateid
.type
== STATEID_LOCK
) {
249 AcquireSRWLockExclusive(&open
->lock
);
250 if (open
->locks
.stateid
.seqid
== 0) {
251 /* if it's a new lock stateid, copy it in */
252 memcpy(&open
->locks
.stateid
, &stateid
.stateid
, sizeof(stateid4
));
253 } else if (stateid
.stateid
.seqid
> open
->locks
.stateid
.seqid
) {
254 /* update the seqid if it's more recent */
255 open
->locks
.stateid
.seqid
= stateid
.stateid
.seqid
;
257 ReleaseSRWLockExclusive(&open
->lock
);
262 #pragma warning (disable : 4706) /* assignment within conditional expression */
264 static int delegation_return(
265 IN nfs41_client
*client
,
266 IN nfs41_delegation_state
*deleg
,
268 IN bool_t try_recovery
)
271 nfs41_open_state
*open
;
274 if (deleg
->srv_open
) {
275 /* make an upcall to the kernel: invalide data cache */
277 unsigned char inbuf
[sizeof(HANDLE
)], *buffer
= inbuf
;
278 DWORD inbuf_len
= sizeof(HANDLE
), outbuf_len
, dstatus
;
280 dprintf(1, "delegation_return: making a downcall for srv_open=%x\n",
282 pipe
= CreateFile(NFS41_USER_DEVICE_NAME_A
, GENERIC_READ
|GENERIC_WRITE
,
283 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, NULL
);
284 if (pipe
== INVALID_HANDLE_VALUE
) {
285 eprintf("delegation_return: Unable to open downcall pipe %d\n",
290 safe_write(&buffer
, &length
, &deleg
->srv_open
, sizeof(HANDLE
));
292 dstatus
= DeviceIoControl(pipe
, IOCTL_NFS41_INVALCACHE
, inbuf
, inbuf_len
,
293 NULL
, 0, (LPDWORD
)&outbuf_len
, NULL
);
295 eprintf("IOCTL_NFS41_INVALCACHE failed %d\n", GetLastError());
300 /* recover opens and locks associated with the delegation */
301 while (open
= deleg_open_find(&client
->state
, deleg
)) {
302 status
= nfs41_delegation_to_open(open
, try_recovery
);
303 if (status
== NFS4_OK
)
304 status
= delegation_flush_locks(open
, try_recovery
);
305 nfs41_open_state_deref(open
);
311 /* return the delegation */
312 stateid
.type
= STATEID_DELEG_FILE
;
314 stateid
.delegation
= deleg
;
315 AcquireSRWLockShared(&deleg
->lock
);
316 memcpy(&stateid
.stateid
, &deleg
->state
.stateid
, sizeof(stateid4
));
317 ReleaseSRWLockShared(&deleg
->lock
);
319 status
= nfs41_delegreturn(client
->session
,
320 &deleg
->file
, &stateid
, try_recovery
);
321 if (status
== NFS4ERR_BADSESSION
)
324 delegation_remove(client
, deleg
);
329 /* open delegation */
330 int nfs41_delegation_granted(
331 IN nfs41_session
*session
,
332 IN nfs41_path_fh
*parent
,
333 IN nfs41_path_fh
*file
,
334 IN open_delegation4
*delegation
,
335 IN bool_t try_recovery
,
336 OUT nfs41_delegation_state
**deleg_out
)
339 nfs41_client
*client
= session
->client
;
340 nfs41_delegation_state
*state
;
341 int status
= NO_ERROR
;
343 if (delegation
->type
!= OPEN_DELEGATE_READ
&&
344 delegation
->type
!= OPEN_DELEGATE_WRITE
)
347 if (delegation
->recalled
) {
348 status
= NFS4ERR_DELEG_REVOKED
;
352 /* allocate the delegation state */
353 status
= delegation_create(parent
, file
, delegation
, &state
);
357 /* register the delegation with the client */
358 EnterCriticalSection(&client
->state
.lock
);
359 /* XXX: check for duplicates by fh and stateid? */
360 list_add_tail(&client
->state
.delegations
, &state
->client_entry
);
361 LeaveCriticalSection(&client
->state
.lock
);
363 nfs41_delegation_ref(state
); /* return a reference */
368 out_return
: /* return the delegation on failure */
369 memcpy(&stateid
.stateid
, &delegation
->stateid
, sizeof(stateid4
));
370 stateid
.type
= STATEID_DELEG_FILE
;
372 stateid
.delegation
= NULL
;
373 nfs41_delegreturn(session
, file
, &stateid
, try_recovery
);
377 #define deleg_entry(pos) list_container(pos, nfs41_delegation_state, client_entry)
379 static int deleg_file_cmp(const struct list_entry
*entry
, const void *value
)
381 const nfs41_fh
*lhs
= &deleg_entry(entry
)->file
.fh
;
382 const nfs41_fh
*rhs
= (const nfs41_fh
*)value
;
383 if (lhs
->superblock
!= rhs
->superblock
) return -1;
384 if (lhs
->fileid
!= rhs
->fileid
) return -1;
388 static bool_t
delegation_compatible(
389 IN
enum open_delegation_type4 type
,
395 case OPEN_DELEGATE_WRITE
:
396 /* An OPEN_DELEGATE_WRITE delegation allows the client to handle,
397 * on its own, all opens. */
400 case OPEN_DELEGATE_READ
:
401 /* An OPEN_DELEGATE_READ delegation allows a client to handle,
402 * on its own, requests to open a file for reading that do not
403 * deny OPEN4_SHARE_ACCESS_READ access to others. */
404 if (create
== OPEN4_CREATE
)
406 if (access
& OPEN4_SHARE_ACCESS_WRITE
|| deny
& OPEN4_SHARE_DENY_READ
)
415 static int delegation_find(
416 IN nfs41_client
*client
,
417 IN
const void *value
,
418 IN list_compare_fn cmp
,
419 OUT nfs41_delegation_state
**deleg_out
)
421 struct list_entry
*entry
;
422 int status
= NFS4ERR_BADHANDLE
;
424 EnterCriticalSection(&client
->state
.lock
);
425 entry
= list_search(&client
->state
.delegations
, value
, cmp
);
427 /* return a reference to the delegation */
428 *deleg_out
= deleg_entry(entry
);
429 nfs41_delegation_ref(*deleg_out
);
431 /* move to the 'most recently used' end of the list */
433 list_add_tail(&client
->state
.delegations
, entry
);
436 LeaveCriticalSection(&client
->state
.lock
);
440 static int delegation_truncate(
441 IN nfs41_delegation_state
*deleg
,
442 IN nfs41_client
*client
,
443 IN stateid_arg
*stateid
,
444 IN nfs41_file_info
*info
)
446 nfs41_superblock
*superblock
= deleg
->file
.fh
.superblock
;
448 /* use SETATTR to truncate the file */
449 info
->attrmask
.arr
[1] |= FATTR4_WORD1_TIME_CREATE
|
450 FATTR4_WORD1_TIME_MODIFY_SET
;
452 get_nfs_time(&info
->time_create
);
453 get_nfs_time(&info
->time_modify
);
454 info
->time_delta
= &superblock
->time_delta
;
456 /* mask out unsupported attributes */
457 nfs41_superblock_supported_attrs(superblock
, &info
->attrmask
);
459 return nfs41_setattr(client
->session
, &deleg
->file
, stateid
, info
);
462 int nfs41_delegate_open(
463 IN nfs41_open_state
*state
,
465 IN OPTIONAL nfs41_file_info
*createattrs
,
466 OUT nfs41_file_info
*info
)
468 nfs41_client
*client
= state
->session
->client
;
469 nfs41_path_fh
*file
= &state
->file
;
470 uint32_t access
= state
->share_access
;
471 uint32_t deny
= state
->share_deny
;
472 nfs41_delegation_state
*deleg
;
476 /* search for a delegation with this filehandle */
477 status
= delegation_find(client
, &file
->fh
, deleg_file_cmp
, &deleg
);
481 AcquireSRWLockExclusive(&deleg
->lock
);
482 if (deleg
->status
!= DELEGATION_GRANTED
) {
483 /* the delegation is being returned, wait for it to finish */
484 while (deleg
->status
!= DELEGATION_RETURNED
)
485 SleepConditionVariableSRW(&deleg
->cond
, &deleg
->lock
, INFINITE
, 0);
486 status
= NFS4ERR_BADHANDLE
;
488 else if (!delegation_compatible(deleg
->state
.type
, create
, access
, deny
)) {
489 #ifdef DELEGATION_RETURN_ON_CONFLICT
490 /* this open will conflict, start the delegation return */
491 deleg
->status
= DELEGATION_RETURNING
;
492 status
= NFS4ERR_DELEG_REVOKED
;
494 status
= NFS4ERR_BADHANDLE
;
496 } else if (create
== OPEN4_CREATE
) {
497 /* copy the stateid for SETATTR */
499 stateid
.delegation
= deleg
;
500 stateid
.type
= STATEID_DELEG_FILE
;
501 memcpy(&stateid
.stateid
, &deleg
->state
.stateid
, sizeof(stateid4
));
504 dprintf(1, "nfs41_delegate_open: updating srv_open from %x to %x\n",
505 deleg
->srv_open
, state
->srv_open
);
506 deleg
->srv_open
= state
->srv_open
;
508 ReleaseSRWLockExclusive(&deleg
->lock
);
510 if (status
== NFS4ERR_DELEG_REVOKED
)
515 if (create
== OPEN4_CREATE
) {
516 memcpy(info
, createattrs
, sizeof(nfs41_file_info
));
518 /* write delegations allow us to simulate OPEN4_CREATE with SETATTR */
519 status
= delegation_truncate(deleg
, client
, &stateid
, info
);
524 /* TODO: check access against deleg->state.permissions or send ACCESS */
526 state
->delegation
.state
= deleg
;
532 delegation_return(client
, deleg
, create
== OPEN4_CREATE
, TRUE
);
535 nfs41_delegation_deref(deleg
);
539 int nfs41_delegation_to_open(
540 IN nfs41_open_state
*open
,
541 IN bool_t try_recovery
)
543 open_delegation4 ignore
;
545 stateid4 open_stateid
= { 0 };
546 stateid_arg deleg_stateid
;
547 int status
= NFS4_OK
;
549 AcquireSRWLockExclusive(&open
->lock
);
550 if (open
->delegation
.state
== NULL
) /* no delegation to reclaim */
553 if (open
->do_close
) /* already have an open stateid */
556 /* if another thread is reclaiming the open stateid,
557 * wait for it to finish before returning success */
558 if (open
->delegation
.reclaim
) {
560 SleepConditionVariableSRW(&open
->delegation
.cond
, &open
->lock
,
562 } while (open
->delegation
.reclaim
);
566 open
->delegation
.reclaim
= 1;
568 AcquireSRWLockShared(&open
->delegation
.state
->lock
);
569 deleg_stateid
.open
= open
;
570 deleg_stateid
.delegation
= NULL
;
571 deleg_stateid
.type
= STATEID_DELEG_FILE
;
572 memcpy(&deleg_stateid
.stateid
, &open
->delegation
.state
->state
.stateid
,
574 ReleaseSRWLockShared(&open
->delegation
.state
->lock
);
576 ReleaseSRWLockExclusive(&open
->lock
);
578 /* send OPEN with CLAIM_DELEGATE_CUR */
579 claim
.claim
= CLAIM_DELEGATE_CUR
;
580 claim
.u
.deleg_cur
.delegate_stateid
= &deleg_stateid
;
581 claim
.u
.deleg_cur
.name
= &open
->file
.name
;
583 status
= nfs41_open(open
->session
, &open
->parent
, &open
->file
,
584 &open
->owner
, &claim
, open
->share_access
, open
->share_deny
,
585 OPEN4_NOCREATE
, 0, NULL
, try_recovery
, &open_stateid
, &ignore
, NULL
);
587 AcquireSRWLockExclusive(&open
->lock
);
588 if (status
== NFS4_OK
) {
589 /* save the new open stateid */
590 memcpy(&open
->stateid
, &open_stateid
, sizeof(stateid4
));
592 } else if (open
->do_close
&& (status
== NFS4ERR_BAD_STATEID
||
593 status
== NFS4ERR_STALE_STATEID
|| status
== NFS4ERR_EXPIRED
)) {
594 /* something triggered client state recovery, and the open stateid
595 * has already been reclaimed; see recover_stateid_delegation() */
598 open
->delegation
.reclaim
= 0;
600 /* signal anyone waiting on the open stateid */
601 WakeAllConditionVariable(&open
->delegation
.cond
);
603 ReleaseSRWLockExclusive(&open
->lock
);
605 eprintf("nfs41_delegation_to_open(%p) failed with %s\n",
606 open
, nfs_error_string(status
));
610 void nfs41_delegation_remove_srvopen(
611 IN nfs41_session
*session
,
612 IN nfs41_path_fh
*file
)
614 nfs41_delegation_state
*deleg
= NULL
;
616 /* find a delegation for this file */
617 if (delegation_find(session
->client
, &file
->fh
, deleg_file_cmp
, &deleg
))
619 dprintf(1, "nfs41_delegation_remove_srvopen: removing reference to "
620 "srv_open=%x\n", deleg
->srv_open
);
621 AcquireSRWLockExclusive(&deleg
->lock
);
622 deleg
->srv_open
= NULL
;
623 ReleaseSRWLockExclusive(&deleg
->lock
);
624 nfs41_delegation_deref(deleg
);
627 /* synchronous delegation return */
628 #ifdef DELEGATION_RETURN_ON_CONFLICT
629 int nfs41_delegation_return(
630 IN nfs41_session
*session
,
631 IN nfs41_path_fh
*file
,
633 IN
enum open_delegation_type4 access
,
639 nfs41_client
*client
= session
->client
;
640 nfs41_delegation_state
*deleg
;
643 /* find a delegation for this file */
644 status
= delegation_find(client
, &file
->fh
, deleg_file_cmp
, &deleg
);
648 AcquireSRWLockExclusive(&deleg
->lock
);
649 if (deleg
->status
== DELEGATION_GRANTED
) {
650 /* return unless delegation is write and access is read */
651 if (deleg
->state
.type
!= OPEN_DELEGATE_WRITE
652 || access
!= OPEN_DELEGATE_READ
) {
653 deleg
->status
= DELEGATION_RETURNING
;
654 status
= NFS4ERR_DELEG_REVOKED
;
657 /* the delegation is being returned, wait for it to finish */
658 while (deleg
->status
== DELEGATION_RETURNING
)
659 SleepConditionVariableSRW(&deleg
->cond
, &deleg
->lock
, INFINITE
, 0);
660 status
= NFS4ERR_BADHANDLE
;
662 ReleaseSRWLockExclusive(&deleg
->lock
);
664 if (status
== NFS4ERR_DELEG_REVOKED
) {
665 delegation_return(client
, deleg
, truncate
, TRUE
);
669 nfs41_delegation_deref(deleg
);
676 /* asynchronous delegation recall */
677 struct recall_thread_args
{
678 nfs41_client
*client
;
679 nfs41_delegation_state
*delegation
;
683 static unsigned int WINAPI
delegation_recall_thread(void *args
)
685 struct recall_thread_args
*recall
= (struct recall_thread_args
*)args
;
687 delegation_return(recall
->client
, recall
->delegation
, recall
->truncate
, TRUE
);
689 /* clean up thread arguments */
690 nfs41_delegation_deref(recall
->delegation
);
691 nfs41_root_deref(recall
->client
->root
);
696 static int deleg_stateid_cmp(const struct list_entry
*entry
, const void *value
)
698 const stateid4
*lhs
= &deleg_entry(entry
)->state
.stateid
;
699 const stateid4
*rhs
= (const stateid4
*)value
;
700 return memcmp(lhs
->other
, rhs
->other
, NFS4_STATEID_OTHER
);
703 int nfs41_delegation_recall(
704 IN nfs41_client
*client
,
706 IN
const stateid4
*stateid
,
709 nfs41_delegation_state
*deleg
;
710 struct recall_thread_args
*args
;
713 dprintf(2, "--> nfs41_delegation_recall()\n");
715 /* search for the delegation by stateid instead of filehandle;
716 * deleg_file_cmp() relies on a proper superblock and fileid,
717 * which we don't get with CB_RECALL */
718 status
= delegation_find(client
, stateid
, deleg_stateid_cmp
, &deleg
);
722 AcquireSRWLockExclusive(&deleg
->lock
);
723 if (deleg
->state
.recalled
) {
724 /* return BADHANDLE if we've already responded to CB_RECALL */
725 status
= NFS4ERR_BADHANDLE
;
727 deleg
->state
.recalled
= 1;
729 if (deleg
->status
== DELEGATION_GRANTED
) {
730 /* start the delegation return */
731 deleg
->status
= DELEGATION_RETURNING
;
732 status
= NFS4ERR_DELEG_REVOKED
;
733 } /* else return NFS4_OK */
735 ReleaseSRWLockExclusive(&deleg
->lock
);
737 if (status
!= NFS4ERR_DELEG_REVOKED
)
740 /* allocate thread arguments */
741 args
= calloc(1, sizeof(struct recall_thread_args
));
743 status
= NFS4ERR_SERVERFAULT
;
744 eprintf("nfs41_delegation_recall() failed to allocate arguments\n");
748 /* hold a reference on the root */
749 nfs41_root_ref(client
->root
);
750 args
->client
= client
;
751 args
->delegation
= deleg
;
752 args
->truncate
= truncate
;
754 /* the callback thread can't make rpc calls, so spawn a separate thread */
755 if (_beginthreadex(NULL
, 0, delegation_recall_thread
, args
, 0, NULL
) == 0) {
756 status
= NFS4ERR_SERVERFAULT
;
757 eprintf("nfs41_delegation_recall() failed to start thread\n");
762 dprintf(DGLVL
, "<-- nfs41_delegation_recall() returning %s\n",
763 nfs_error_string(status
));
768 nfs41_root_deref(client
->root
);
770 nfs41_delegation_deref(deleg
);
775 static int deleg_fh_cmp(const struct list_entry
*entry
, const void *value
)
777 const nfs41_fh
*lhs
= &deleg_entry(entry
)->file
.fh
;
778 const nfs41_fh
*rhs
= (const nfs41_fh
*)value
;
779 if (lhs
->len
!= rhs
->len
) return -1;
780 return memcmp(lhs
->fh
, rhs
->fh
, lhs
->len
);
783 int nfs41_delegation_getattr(
784 IN nfs41_client
*client
,
785 IN
const nfs41_fh
*fh
,
786 IN
const bitmap4
*attr_request
,
787 OUT nfs41_file_info
*info
)
789 nfs41_delegation_state
*deleg
;
793 dprintf(2, "--> nfs41_delegation_getattr()\n");
795 /* search for a delegation on this file handle */
796 status
= delegation_find(client
, fh
, deleg_fh_cmp
, &deleg
);
800 AcquireSRWLockShared(&deleg
->lock
);
801 fileid
= deleg
->file
.fh
.fileid
;
802 if (deleg
->status
!= DELEGATION_GRANTED
||
803 deleg
->state
.type
!= OPEN_DELEGATE_WRITE
) {
804 status
= NFS4ERR_BADHANDLE
;
806 ReleaseSRWLockShared(&deleg
->lock
);
810 ZeroMemory(info
, sizeof(nfs41_file_info
));
812 /* find attributes for the given fileid */
813 status
= nfs41_attr_cache_lookup(
814 client_name_cache(client
), fileid
, info
);
816 status
= NFS4ERR_BADHANDLE
;
820 nfs41_delegation_deref(deleg
);
822 dprintf(DGLVL
, "<-- nfs41_delegation_getattr() returning %s\n",
823 nfs_error_string(status
));
828 void nfs41_client_delegation_free(
829 IN nfs41_client
*client
)
831 struct list_entry
*entry
, *tmp
;
833 EnterCriticalSection(&client
->state
.lock
);
834 list_for_each_tmp (entry
, tmp
, &client
->state
.delegations
) {
836 nfs41_delegation_deref(deleg_entry(entry
));
838 LeaveCriticalSection(&client
->state
.lock
);
842 static int delegation_recovery_status(
843 IN nfs41_delegation_state
*deleg
)
845 int status
= NFS4_OK
;
847 AcquireSRWLockExclusive(&deleg
->lock
);
848 if (deleg
->status
== DELEGATION_GRANTED
) {
849 if (deleg
->revoked
) {
850 deleg
->status
= DELEGATION_RETURNED
;
851 status
= NFS4ERR_BADHANDLE
;
852 } else if (deleg
->state
.recalled
) {
853 deleg
->status
= DELEGATION_RETURNING
;
854 status
= NFS4ERR_DELEG_REVOKED
;
857 ReleaseSRWLockExclusive(&deleg
->lock
);
861 int nfs41_client_delegation_recovery(
862 IN nfs41_client
*client
)
864 struct list_entry
*entry
, *tmp
;
865 nfs41_delegation_state
*deleg
;
866 int status
= NFS4_OK
;
868 list_for_each_tmp(entry
, tmp
, &client
->state
.delegations
) {
869 deleg
= list_container(entry
, nfs41_delegation_state
, client_entry
);
871 status
= delegation_recovery_status(deleg
);
873 case NFS4ERR_DELEG_REVOKED
:
874 /* the delegation was reclaimed, but flagged as recalled;
875 * return it with try_recovery=FALSE */
876 status
= delegation_return(client
, deleg
, FALSE
, FALSE
);
879 case NFS4ERR_BADHANDLE
:
880 /* reclaim failed, so we have no delegation state on the server;
881 * 'forget' the delegation without trying to return it */
882 delegation_remove(client
, deleg
);
887 if (status
== NFS4ERR_BADSESSION
)
891 /* use DELEGPURGE to indicate that we're done reclaiming delegations */
892 status
= nfs41_delegpurge(client
->session
);
894 /* support for DELEGPURGE is optional; ignore any errors but BADSESSION */
895 if (status
!= NFS4ERR_BADSESSION
)
902 int nfs41_client_delegation_return_lru(
903 IN nfs41_client
*client
)
905 struct list_entry
*entry
;
906 nfs41_delegation_state
*state
= NULL
;
907 int status
= NFS4ERR_BADHANDLE
;
909 /* starting from the least recently opened, find and return
910 * the first delegation that's not 'in use' (currently open) */
912 /* TODO: use a more robust algorithm, taking into account:
913 * -number of total opens
914 * -time since last operation on an associated open, or
915 * -number of operations/second over last n seconds */
916 EnterCriticalSection(&client
->state
.lock
);
917 list_for_each(entry
, &client
->state
.delegations
) {
918 state
= deleg_entry(entry
);
920 /* skip if it's currently in use for an open; note that ref_count
921 * can't go from 1 to 2 without holding client->state.lock */
922 if (state
->ref_count
> 1)
925 AcquireSRWLockExclusive(&state
->lock
);
926 if (state
->status
== DELEGATION_GRANTED
) {
927 /* start returning the delegation */
928 state
->status
= DELEGATION_RETURNING
;
929 status
= NFS4ERR_DELEG_REVOKED
;
931 ReleaseSRWLockExclusive(&state
->lock
);
933 if (status
== NFS4ERR_DELEG_REVOKED
)
936 LeaveCriticalSection(&client
->state
.lock
);
938 if (status
== NFS4ERR_DELEG_REVOKED
)
939 status
= delegation_return(client
, state
, FALSE
, TRUE
);