ReactOS  0.4.12-dev-36-g472787f
delegation.c
Go to the documentation of this file.
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 "delegation.h"
23 #include "nfs41_ops.h"
24 #include "name_cache.h"
25 #include "util.h"
26 #include "daemon_debug.h"
27 
28 #include <devioctl.h>
29 #include "nfs41_driver.h" /* for making downcall to invalidate cache */
30 #include "util.h"
31 
32 #define DGLVL 2 /* dprintf level for delegation logging */
33 
34 
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)
41 {
43  int status = NO_ERROR;
44 
45  state = calloc(1, sizeof(nfs41_delegation_state));
46  if (state == NULL) {
47  status = GetLastError();
48  goto out;
49  }
50 
51  memcpy(&state->state, delegation, sizeof(open_delegation4));
52 
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,
58  &state->parent.name);
59  fh_copy(&state->parent.fh, &parent->fh);
60 
61  list_init(&state->client_entry);
62  state->status = DELEGATION_GRANTED;
63  InitializeSRWLock(&state->lock);
65  state->ref_count = 1;
66  *deleg_out = state;
67 out:
68  return status;
69 }
70 
73 {
74  const LONG count = InterlockedIncrement(&state->ref_count);
75  dprintf(DGLVL, "nfs41_delegation_ref(%s) count %d\n",
76  state->path.path, count);
77 }
78 
81 {
82  const LONG count = InterlockedDecrement(&state->ref_count);
83  dprintf(DGLVL, "nfs41_delegation_deref(%s) count %d\n",
84  state->path.path, count);
85  if (count == 0)
86  free(state);
87 }
88 
89 #define open_entry(pos) list_container(pos, nfs41_open_state, client_entry)
90 
91 static void delegation_remove(
94 {
95  struct list_entry *entry;
96 
97  /* remove from the client's list */
98  EnterCriticalSection(&client->state.lock);
99  list_remove(&deleg->client_entry);
100 
101  /* remove from each associated open */
102  list_for_each(entry, &client->state.opens) {
103  nfs41_open_state *open = open_entry(entry);
105  if (open->delegation.state == deleg) {
106  /* drop the delegation reference */
107  nfs41_delegation_deref(open->delegation.state);
108  open->delegation.state = NULL;
109  }
111  }
112  LeaveCriticalSection(&client->state.lock);
113 
114  /* signal threads waiting on delegreturn */
115  AcquireSRWLockExclusive(&deleg->lock);
116  deleg->status = DELEGATION_RETURNED;
117  WakeAllConditionVariable(&deleg->cond);
118  ReleaseSRWLockExclusive(&deleg->lock);
119 
120  /* release the client's reference */
121  nfs41_delegation_deref(deleg);
122 }
123 
124 
125 /* delegation return */
126 #define lock_entry(pos) list_container(pos, nfs41_lock_state, open_entry)
127 
130 {
131  struct list_entry *entry;
132  list_for_each(entry, &open->locks.list) {
133  if (lock_entry(entry)->delegated)
134  return TRUE;
135  }
136  return FALSE;
137 }
138 
139 static int open_deleg_cmp(const struct list_entry *entry, const void *value)
140 {
141  nfs41_open_state *open = open_entry(entry);
142  int result = -1;
143 
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;
148  result = 0;
149 out:
150  ReleaseSRWLockShared(&open->lock);
151  return result;
152 }
153 
154 /* find the first open that needs recovery */
156  IN struct client_state *state,
157  IN const nfs41_delegation_state *deleg)
158 {
159  struct list_entry *entry;
161 
162  EnterCriticalSection(&state->lock);
163  entry = list_search(&state->opens, deleg, open_deleg_cmp);
164  if (entry) {
165  open = open_entry(entry);
166  nfs41_open_state_ref(open); /* return a reference */
167  }
168  LeaveCriticalSection(&state->lock);
169  return open;
170 }
171 
172 /* find the first lock that needs recovery */
175  OUT nfs41_lock_state *lock_out)
176 {
177  struct list_entry *entry;
178  bool_t found = FALSE;
179 
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;
189  found = TRUE;
190  break;
191  }
192  }
193  ReleaseSRWLockShared(&open->lock);
194  return found;
195 }
196 
197 /* find the matching lock by id, and reset lock.delegated */
198 static void deleg_lock_update(
200  IN const nfs41_lock_state *source)
201 {
202  struct list_entry *entry;
203 
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;
209  break;
210  }
211  }
212  ReleaseSRWLockExclusive(&open->lock);
213 }
214 
217  IN bool_t try_recovery)
218 {
219  stateid_arg stateid;
221  int status = NFS4_OK;
222 
223  stateid.open = open;
224  stateid.delegation = NULL;
225 
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;
231  } else {
232  memcpy(&stateid.stateid, &open->stateid, sizeof(stateid4));
233  stateid.type = STATEID_OPEN;
234  }
235  ReleaseSRWLockShared(&open->lock);
236 
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);
242  if (status)
243  break;
244  deleg_lock_update(open, &lock);
245  }
246 
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;
256  }
257  ReleaseSRWLockExclusive(&open->lock);
258  }
259  return status;
260 }
261 
262 #pragma warning (disable : 4706) /* assignment within conditional expression */
263 
264 static int delegation_return(
266  IN nfs41_delegation_state *deleg,
267  IN bool_t truncate,
268  IN bool_t try_recovery)
269 {
270  stateid_arg stateid;
272  int status;
273 
274  if (deleg->srv_open) {
275  /* make an upcall to the kernel: invalide data cache */
276  HANDLE pipe;
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",
281  deleg->srv_open);
284  if (pipe == INVALID_HANDLE_VALUE) {
285  eprintf("delegation_return: Unable to open downcall pipe %d\n",
286  GetLastError());
287  goto out_downcall;
288  }
289  length = inbuf_len;
290  safe_write(&buffer, &length, &deleg->srv_open, sizeof(HANDLE));
291 
292  dstatus = DeviceIoControl(pipe, IOCTL_NFS41_INVALCACHE, inbuf, inbuf_len,
293  NULL, 0, (LPDWORD)&outbuf_len, NULL);
294  if (!dstatus)
295  eprintf("IOCTL_NFS41_INVALCACHE failed %d\n", GetLastError());
296  CloseHandle(pipe);
297  }
298 out_downcall:
299 
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);
306 
307  if (status)
308  break;
309  }
310 
311  /* return the delegation */
312  stateid.type = STATEID_DELEG_FILE;
313  stateid.open = NULL;
314  stateid.delegation = deleg;
315  AcquireSRWLockShared(&deleg->lock);
316  memcpy(&stateid.stateid, &deleg->state.stateid, sizeof(stateid4));
317  ReleaseSRWLockShared(&deleg->lock);
318 
319  status = nfs41_delegreturn(client->session,
320  &deleg->file, &stateid, try_recovery);
321  if (status == NFS4ERR_BADSESSION)
322  goto out;
323 
324  delegation_remove(client, deleg);
325 out:
326  return status;
327 }
328 
329 /* open delegation */
331  IN nfs41_session *session,
334  IN open_delegation4 *delegation,
335  IN bool_t try_recovery,
336  OUT nfs41_delegation_state **deleg_out)
337 {
338  stateid_arg stateid;
339  nfs41_client *client = session->client;
341  int status = NO_ERROR;
342 
343  if (delegation->type != OPEN_DELEGATE_READ &&
344  delegation->type != OPEN_DELEGATE_WRITE)
345  goto out;
346 
347  if (delegation->recalled) {
348  status = NFS4ERR_DELEG_REVOKED;
349  goto out_return;
350  }
351 
352  /* allocate the delegation state */
353  status = delegation_create(parent, file, delegation, &state);
354  if (status)
355  goto out_return;
356 
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);
362 
363  nfs41_delegation_ref(state); /* return a reference */
364  *deleg_out = state;
365 out:
366  return status;
367 
368 out_return: /* return the delegation on failure */
369  memcpy(&stateid.stateid, &delegation->stateid, sizeof(stateid4));
370  stateid.type = STATEID_DELEG_FILE;
371  stateid.open = NULL;
372  stateid.delegation = NULL;
373  nfs41_delegreturn(session, file, &stateid, try_recovery);
374  goto out;
375 }
376 
377 #define deleg_entry(pos) list_container(pos, nfs41_delegation_state, client_entry)
378 
379 static int deleg_file_cmp(const struct list_entry *entry, const void *value)
380 {
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;
385  return 0;
386 }
387 
392  IN uint32_t deny)
393 {
394  switch (type) {
395  case OPEN_DELEGATE_WRITE:
396  /* An OPEN_DELEGATE_WRITE delegation allows the client to handle,
397  * on its own, all opens. */
398  return TRUE;
399 
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)
405  return FALSE;
406  if (access & OPEN4_SHARE_ACCESS_WRITE || deny & OPEN4_SHARE_DENY_READ)
407  return FALSE;
408  return TRUE;
409 
410  default:
411  return FALSE;
412  }
413 }
414 
415 static int delegation_find(
417  IN const void *value,
419  OUT nfs41_delegation_state **deleg_out)
420 {
421  struct list_entry *entry;
423 
424  EnterCriticalSection(&client->state.lock);
425  entry = list_search(&client->state.delegations, value, cmp);
426  if (entry) {
427  /* return a reference to the delegation */
428  *deleg_out = deleg_entry(entry);
429  nfs41_delegation_ref(*deleg_out);
430 
431  /* move to the 'most recently used' end of the list */
432  list_remove(entry);
433  list_add_tail(&client->state.delegations, entry);
434  status = NFS4_OK;
435  }
436  LeaveCriticalSection(&client->state.lock);
437  return status;
438 }
439 
441  IN nfs41_delegation_state *deleg,
443  IN stateid_arg *stateid,
445 {
446  nfs41_superblock *superblock = deleg->file.fh.superblock;
447 
448  /* use SETATTR to truncate the file */
449  info->attrmask.arr[1] |= FATTR4_WORD1_TIME_CREATE |
451 
452  get_nfs_time(&info->time_create);
453  get_nfs_time(&info->time_modify);
454  info->time_delta = &superblock->time_delta;
455 
456  /* mask out unsupported attributes */
457  nfs41_superblock_supported_attrs(superblock, &info->attrmask);
458 
459  return nfs41_setattr(client->session, &deleg->file, stateid, info);
460 }
461 
465  IN OPTIONAL nfs41_file_info *createattrs,
467 {
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;
473  stateid_arg stateid;
474  int status;
475 
476  /* search for a delegation with this filehandle */
477  status = delegation_find(client, &file->fh, deleg_file_cmp, &deleg);
478  if (status)
479  goto out;
480 
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;
487  }
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;
493 #else
494  status = NFS4ERR_BADHANDLE;
495 #endif
496  } else if (create == OPEN4_CREATE) {
497  /* copy the stateid for SETATTR */
498  stateid.open = NULL;
499  stateid.delegation = deleg;
500  stateid.type = STATEID_DELEG_FILE;
501  memcpy(&stateid.stateid, &deleg->state.stateid, sizeof(stateid4));
502  }
503  if (!status) {
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;
507  }
508  ReleaseSRWLockExclusive(&deleg->lock);
509 
510  if (status == NFS4ERR_DELEG_REVOKED)
511  goto out_return;
512  if (status)
513  goto out_deleg;
514 
515  if (create == OPEN4_CREATE) {
516  memcpy(info, createattrs, sizeof(nfs41_file_info));
517 
518  /* write delegations allow us to simulate OPEN4_CREATE with SETATTR */
519  status = delegation_truncate(deleg, client, &stateid, info);
520  if (status)
521  goto out_deleg;
522  }
523 
524  /* TODO: check access against deleg->state.permissions or send ACCESS */
525 
526  state->delegation.state = deleg;
527  status = NFS4_OK;
528 out:
529  return status;
530 
531 out_return:
532  delegation_return(client, deleg, create == OPEN4_CREATE, TRUE);
533 
534 out_deleg:
535  nfs41_delegation_deref(deleg);
536  goto out;
537 }
538 
541  IN bool_t try_recovery)
542 {
544  open_claim4 claim;
545  stateid4 open_stateid = { 0 };
546  stateid_arg deleg_stateid;
547  int status = NFS4_OK;
548 
549  AcquireSRWLockExclusive(&open->lock);
550  if (open->delegation.state == NULL) /* no delegation to reclaim */
551  goto out_unlock;
552 
553  if (open->do_close) /* already have an open stateid */
554  goto out_unlock;
555 
556  /* if another thread is reclaiming the open stateid,
557  * wait for it to finish before returning success */
558  if (open->delegation.reclaim) {
559  do {
560  SleepConditionVariableSRW(&open->delegation.cond, &open->lock,
561  INFINITE, 0);
562  } while (open->delegation.reclaim);
563  if (open->do_close)
564  goto out_unlock;
565  }
566  open->delegation.reclaim = 1;
567 
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,
573  sizeof(stateid4));
574  ReleaseSRWLockShared(&open->delegation.state->lock);
575 
576  ReleaseSRWLockExclusive(&open->lock);
577 
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;
582 
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);
586 
587  AcquireSRWLockExclusive(&open->lock);
588  if (status == NFS4_OK) {
589  /* save the new open stateid */
590  memcpy(&open->stateid, &open_stateid, sizeof(stateid4));
591  open->do_close = 1;
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() */
596  status = NFS4_OK;
597  }
598  open->delegation.reclaim = 0;
599 
600  /* signal anyone waiting on the open stateid */
601  WakeAllConditionVariable(&open->delegation.cond);
602 out_unlock:
603  ReleaseSRWLockExclusive(&open->lock);
604  if (status)
605  eprintf("nfs41_delegation_to_open(%p) failed with %s\n",
606  open, nfs_error_string(status));
607  return status;
608 }
609 
611  IN nfs41_session *session,
613 {
614  nfs41_delegation_state *deleg = NULL;
615 
616  /* find a delegation for this file */
617  if (delegation_find(session->client, &file->fh, deleg_file_cmp, &deleg))
618  return;
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);
625 }
626 
627 /* synchronous delegation return */
628 #ifdef DELEGATION_RETURN_ON_CONFLICT
630  IN nfs41_session *session,
632 #ifndef __REACTOS__
634 #else
635  IN int access,
636 #endif
637  IN bool_t truncate)
638 {
639  nfs41_client *client = session->client;
640  nfs41_delegation_state *deleg;
641  int status;
642 
643  /* find a delegation for this file */
644  status = delegation_find(client, &file->fh, deleg_file_cmp, &deleg);
645  if (status)
646  goto out;
647 
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;
655  }
656  } else {
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;
661  }
662  ReleaseSRWLockExclusive(&deleg->lock);
663 
664  if (status == NFS4ERR_DELEG_REVOKED) {
665  delegation_return(client, deleg, truncate, TRUE);
666  status = NFS4_OK;
667  }
668 
669  nfs41_delegation_deref(deleg);
670 out:
671  return status;
672 }
673 #endif
674 
675 
676 /* asynchronous delegation recall */
681 };
682 
683 static unsigned int WINAPI delegation_recall_thread(void *args)
684 {
685  struct recall_thread_args *recall = (struct recall_thread_args*)args;
686 
687  delegation_return(recall->client, recall->delegation, recall->truncate, TRUE);
688 
689  /* clean up thread arguments */
691  nfs41_root_deref(recall->client->root);
692  free(recall);
693  return 0;
694 }
695 
696 static int deleg_stateid_cmp(const struct list_entry *entry, const void *value)
697 {
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);
701 }
702 
705  IN nfs41_fh *fh,
706  IN const stateid4 *stateid,
708 {
709  nfs41_delegation_state *deleg;
710  struct recall_thread_args *args;
711  int status;
712 
713  dprintf(2, "--> nfs41_delegation_recall()\n");
714 
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);
719  if (status)
720  goto out;
721 
722  AcquireSRWLockExclusive(&deleg->lock);
723  if (deleg->state.recalled) {
724  /* return BADHANDLE if we've already responded to CB_RECALL */
725  status = NFS4ERR_BADHANDLE;
726  } else {
727  deleg->state.recalled = 1;
728 
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 */
734  }
735  ReleaseSRWLockExclusive(&deleg->lock);
736 
737  if (status != NFS4ERR_DELEG_REVOKED)
738  goto out_deleg;
739 
740  /* allocate thread arguments */
741  args = calloc(1, sizeof(struct recall_thread_args));
742  if (args == NULL) {
743  status = NFS4ERR_SERVERFAULT;
744  eprintf("nfs41_delegation_recall() failed to allocate arguments\n");
745  goto out_deleg;
746  }
747 
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;
753 
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");
758  goto out_args;
759  }
760  status = NFS4_OK;
761 out:
762  dprintf(DGLVL, "<-- nfs41_delegation_recall() returning %s\n",
763  nfs_error_string(status));
764  return status;
765 
766 out_args:
767  free(args);
768  nfs41_root_deref(client->root);
769 out_deleg:
770  nfs41_delegation_deref(deleg);
771  goto out;
772 }
773 
774 
775 static int deleg_fh_cmp(const struct list_entry *entry, const void *value)
776 {
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);
781 }
782 
785  IN const nfs41_fh *fh,
786  IN const bitmap4 *attr_request,
788 {
789  nfs41_delegation_state *deleg;
790  uint64_t fileid;
791  int status;
792 
793  dprintf(2, "--> nfs41_delegation_getattr()\n");
794 
795  /* search for a delegation on this file handle */
796  status = delegation_find(client, fh, deleg_fh_cmp, &deleg);
797  if (status)
798  goto out;
799 
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;
805  }
806  ReleaseSRWLockShared(&deleg->lock);
807  if (status)
808  goto out_deleg;
809 
810  ZeroMemory(info, sizeof(nfs41_file_info));
811 
812  /* find attributes for the given fileid */
813  status = nfs41_attr_cache_lookup(
814  client_name_cache(client), fileid, info);
815  if (status) {
816  status = NFS4ERR_BADHANDLE;
817  goto out_deleg;
818  }
819 out_deleg:
820  nfs41_delegation_deref(deleg);
821 out:
822  dprintf(DGLVL, "<-- nfs41_delegation_getattr() returning %s\n",
823  nfs_error_string(status));
824  return status;
825 }
826 
827 
830 {
831  struct list_entry *entry, *tmp;
832 
833  EnterCriticalSection(&client->state.lock);
834  list_for_each_tmp (entry, tmp, &client->state.delegations) {
835  list_remove(entry);
837  }
838  LeaveCriticalSection(&client->state.lock);
839 }
840 
841 
843  IN nfs41_delegation_state *deleg)
844 {
845  int status = NFS4_OK;
846 
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;
855  }
856  }
857  ReleaseSRWLockExclusive(&deleg->lock);
858  return status;
859 }
860 
863 {
864  struct list_entry *entry, *tmp;
865  nfs41_delegation_state *deleg;
866  int status = NFS4_OK;
867 
868  list_for_each_tmp(entry, tmp, &client->state.delegations) {
870 
871  status = delegation_recovery_status(deleg);
872  switch (status) {
874  /* the delegation was reclaimed, but flagged as recalled;
875  * return it with try_recovery=FALSE */
876  status = delegation_return(client, deleg, FALSE, FALSE);
877  break;
878 
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);
883  status = NFS4_OK;
884  break;
885  }
886 
887  if (status == NFS4ERR_BADSESSION)
888  goto out;
889  }
890 
891  /* use DELEGPURGE to indicate that we're done reclaiming delegations */
892  status = nfs41_delegpurge(client->session);
893 
894  /* support for DELEGPURGE is optional; ignore any errors but BADSESSION */
895  if (status != NFS4ERR_BADSESSION)
896  status = NFS4_OK;
897 out:
898  return status;
899 }
900 
901 
904 {
905  struct list_entry *entry;
908 
909  /* starting from the least recently opened, find and return
910  * the first delegation that's not 'in use' (currently open) */
911 
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);
919 
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)
923  continue;
924 
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;
930  }
931  ReleaseSRWLockExclusive(&state->lock);
932 
933  if (status == NFS4ERR_DELEG_REVOKED)
934  break;
935  }
936  LeaveCriticalSection(&client->state.lock);
937 
938  if (status == NFS4ERR_DELEG_REVOKED)
939  status = delegation_return(client, state, FALSE, TRUE);
940  return status;
941 }
static bool_t delegation_compatible(IN enum open_delegation_type4 type, IN uint32_t create, IN uint32_t access, IN uint32_t deny)
Definition: delegation.c:388
SRWLOCK lock
Definition: nfs41.h:139
VOID WINAPI AcquireSRWLockExclusive(PSRWLOCK Lock)
Definition: sync.c:54
void nfs41_open_state_deref(IN nfs41_open_state *state)
Definition: open.c:104
#define IN
Definition: typedefs.h:38
static int deleg_file_cmp(const struct list_entry *entry, const void *value)
Definition: delegation.c:379
void nfs41_open_state_ref(IN nfs41_open_state *state)
Definition: open.c:96
Definition: get.c:139
#define TRUE
Definition: types.h:120
uint64_t offset
Definition: nfs41.h:114
#define CloseHandle
Definition: compat.h:398
int nfs41_setattr(IN nfs41_session *session, IN nfs41_path_fh *file, IN stateid_arg *stateid, IN nfs41_file_info *info)
Definition: nfs41_ops.c:1351
int memcmp(void *Buffer1, void *Buffer2, ACPI_SIZE Count)
Definition: utclib.c:112
rwlock_t lock
Definition: tcpcore.h:1163
static int delegation_flush_locks(IN nfs41_open_state *open, IN bool_t try_recovery)
Definition: delegation.c:215
uint64_t fileid
Definition: nfs41_types.h:55
struct __nfs41_superblock * superblock
Definition: nfs41_types.h:56
static int delegation_truncate(IN nfs41_delegation_state *deleg, IN nfs41_client *client, IN stateid_arg *stateid, IN nfs41_file_info *info)
Definition: delegation.c:440
#define open
Definition: acwin.h:71
unsigned char other[NFS4_STATEID_OTHER]
Definition: nfs41_types.h:145
#define deleg_entry(pos)
Definition: delegation.c:377
int32_t bool_t
Definition: types.h:101
static int open_deleg_cmp(const struct list_entry *entry, const void *value)
Definition: delegation.c:139
GLuint GLuint GLsizei count
Definition: gl.h:1545
int ignore(int trapCode, ppc_trap_frame_t *trap)
Definition: mmuobject.c:296
#define free
Definition: debug_ros.c:5
uint8_t entry
Definition: isohybrid.c:63
struct __open_claim4::@40::__open_claim_deleg_cur deleg_cur
open_delegation_type4
Definition: nfs41_ops.h:585
#define NFS41_USER_DEVICE_NAME_A
Definition: nfs41_driver.h:28
void nfs41_client_delegation_free(IN nfs41_client *client)
Definition: delegation.c:828
GLuint GLuint GLsizei GLenum type
Definition: gl.h:1545
#define INVALID_HANDLE_VALUE
Definition: compat.h:391
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1059
#define ZeroMemory
Definition: winbase.h:1635
static void deleg_lock_update(IN nfs41_open_state *open, IN const nfs41_lock_state *source)
Definition: delegation.c:198
int nfs41_delegation_recall(IN nfs41_client *client, IN nfs41_fh *fh, IN const stateid4 *stateid, IN bool_t truncate)
Definition: delegation.c:703
void eprintf(LPCSTR format,...)
Definition: daemon_debug.c:86
GLuint buffer
Definition: glext.h:5915
#define NFS4_STATEID_OTHER
Definition: nfs41_const.h:33
void nfs41_delegation_deref(IN nfs41_delegation_state *state)
Definition: delegation.c:79
int nfs41_delegpurge(IN nfs41_session *session)
Definition: nfs41_ops.c:1666
#define FILE_SHARE_WRITE
Definition: nt_native.h:681
#define cmp(status, error)
Definition: error.c:114
void WINAPI EnterCriticalSection(LPCRITICAL_SECTION)
static void delegation_remove(IN nfs41_client *client, IN nfs41_delegation_state *deleg)
Definition: delegation.c:91
_CRTIMP uintptr_t __cdecl _beginthreadex(_In_opt_ void *_Security, _In_ unsigned _StackSize, _In_ unsigned(__stdcall *_StartAddress)(void *), _In_opt_ void *_ArgList, _In_ unsigned _InitFlag, _Out_opt_ unsigned *_ThrdAddr)
#define NO_ERROR
Definition: dderror.h:5
Definition: match.c:390
uint32_t seqid
Definition: nfs41_types.h:144
void nfs41_delegation_ref(IN nfs41_delegation_state *state)
Definition: delegation.c:71
#define FILE_SHARE_READ
Definition: compat.h:125
bool_t last_component(IN const char *path, IN const char *path_end, OUT nfs41_component *component)
Definition: util.c:317
#define IOCTL_NFS41_INVALCACHE
Definition: nfs41_driver.h:50
bool_t do_close
Definition: nfs41.h:134
__WINE_SERVER_LIST_INLINE void list_add_tail(struct list *list, struct list *elem)
Definition: list.h:102
void abs_path_copy(OUT nfs41_abs_path *dst, IN const nfs41_abs_path *src)
Definition: util.c:338
nfs41_delegation_state * delegation
Definition: delegation.c:679
VOID WINAPI WakeAllConditionVariable(PCONDITION_VARIABLE ConditionVariable)
Definition: sync.c:137
nfs41_path_fh file
Definition: nfs41.h:99
long LONG
Definition: pedump.c:60
uint32_t exclusive
Definition: nfs41.h:116
#define list_for_each(entry, head)
Definition: list.h:36
#define dprintf
Definition: regdump.c:33
#define GENERIC_WRITE
Definition: nt_native.h:90
#define DGLVL
Definition: delegation.c:32
nfstime4 time_delta
Definition: nfs41.h:45
smooth NULL
Definition: ftsmooth.c:416
nfs41_path_fh parent
Definition: nfs41.h:98
#define list_container(entry, type, field)
Definition: list.h:33
int nfs41_client_delegation_recovery(IN nfs41_client *client)
Definition: delegation.c:861
static __inline void nfs41_superblock_supported_attrs(IN const nfs41_superblock *superblock, IN OUT bitmap4 *attrs)
Definition: nfs41.h:454
uint32_t len
Definition: nfs41_types.h:54
void path_fh_init(OUT nfs41_path_fh *file, IN nfs41_abs_path *path)
Definition: util.c:346
#define client_entry(pos)
Definition: namespace.c:33
void nfs41_root_ref(IN nfs41_root *root)
Definition: namespace.c:92
static int delegation_return(IN nfs41_client *client, IN nfs41_delegation_state *deleg, IN bool_t truncate, IN bool_t try_recovery)
Definition: delegation.c:264
#define OPEN_EXISTING
Definition: compat.h:426
static nfs41_open_state * deleg_open_find(IN struct client_state *state, IN const nfs41_delegation_state *deleg)
Definition: delegation.c:155
static int delegation_find(IN nfs41_client *client, IN const void *value, IN list_compare_fn cmp, OUT nfs41_delegation_state **deleg_out)
Definition: delegation.c:415
int nfs41_delegation_to_open(IN nfs41_open_state *open, IN bool_t try_recovery)
Definition: delegation.c:539
__WINE_SERVER_LIST_INLINE void list_remove(struct list *elem)
Definition: list.h:108
open_delegation4 state
Definition: nfs41.h:96
static __inline struct nfs41_name_cache * client_name_cache(IN nfs41_client *client)
Definition: name_cache.h:28
const char * name
Definition: nfs41_types.h:48
BOOL WINAPI SleepConditionVariableSRW(PCONDITION_VARIABLE ConditionVariable, PSRWLOCK Lock, DWORD Timeout, ULONG Flags)
Definition: sync.c:121
r parent
Definition: btrfs.c:2644
static int delegation_recovery_status(IN nfs41_delegation_state *deleg)
Definition: delegation.c:842
struct list_entry client_entry
Definition: nfs41.h:100
enum stateid_type type
Definition: nfs41_ops.h:285
GLenum GLuint GLenum GLsizei length
Definition: glext.h:5579
struct __nfs41_open_state::@24 delegation
nfs41_delegation_state * delegation
Definition: nfs41_ops.h:287
int nfs41_delegation_return(IN nfs41_session *session, IN nfs41_path_fh *file, IN enum open_delegation_type4 access, IN bool_t truncate)
Definition: delegation.c:629
enum open_delegation_type4 type
Definition: nfs41_types.h:154
static FILE * client
Definition: client.c:41
static FILE * out
Definition: regtests2xml.c:44
unsigned long DWORD
Definition: ntddk_ex.h:95
PVOID HANDLE
Definition: typedefs.h:71
int safe_write(unsigned char **pos, uint32_t *remaining, void *src, uint32_t src_len)
Definition: util.c:44
const char * nfs_error_string(int status)
Definition: daemon_debug.c:370
union __open_claim4::@40 u
void fh_copy(OUT nfs41_fh *dst, IN const nfs41_fh *src)
Definition: util.c:354
int nfs41_delegreturn(IN nfs41_session *session, IN nfs41_path_fh *file, IN stateid_arg *stateid, IN bool_t try_recovery)
Definition: nfs41_ops.c:1693
uchar inbuf[M_BLOCK]
Definition: unzcrash.c:40
nfs41_open_state * open
Definition: nfs41_ops.h:286
nfs41_client * client
Definition: delegation.c:678
struct __nfs41_root * root
Definition: nfs41.h:206
uint64_t length
Definition: nfs41.h:115
#define InterlockedDecrement
Definition: armddk.h:52
static bool_t deleg_lock_find(IN nfs41_open_state *open, OUT nfs41_lock_state *lock_out)
Definition: delegation.c:173
int(* list_compare_fn)(const struct list_entry *, const void *)
Definition: list.h:100
int nfs41_open(IN nfs41_session *session, IN nfs41_path_fh *parent, IN nfs41_path_fh *file, IN state_owner4 *owner, IN open_claim4 *claim, IN uint32_t allow, IN uint32_t deny, IN uint32_t create, IN uint32_t how_mode, IN OPTIONAL nfs41_file_info *createattrs, IN bool_t try_recovery, OUT stateid4 *stateid, OUT open_delegation4 *delegation, OUT OPTIONAL nfs41_file_info *info)
Definition: nfs41_ops.c:366
struct client_state state
Definition: nfs41.h:215
static int state
Definition: maze.c:121
VOID WINAPI ReleaseSRWLockExclusive(PSRWLOCK Lock)
Definition: sync.c:82
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
#define GENERIC_READ
Definition: compat.h:124
VOID WINAPI InitializeSRWLock(PSRWLOCK Lock)
Definition: sync.c:75
#define WINAPI
Definition: msvc.h:20
static int deleg_fh_cmp(const struct list_entry *entry, const void *value)
Definition: delegation.c:775
uint32_t delegated
Definition: nfs41.h:117
static struct list_entry * list_search(const struct list_entry *head, const void *value, list_compare_fn compare)
Definition: list.h:102
static int deleg_stateid_cmp(const struct list_entry *entry, const void *value)
Definition: delegation.c:696
int nfs41_delegate_open(IN nfs41_open_state *state, IN uint32_t create, IN OPTIONAL nfs41_file_info *createattrs, OUT nfs41_file_info *info)
Definition: delegation.c:462
void get_nfs_time(OUT nfstime4 *nfs_time)
Definition: util.c:210
UINT64 uint64_t
Definition: types.h:77
unsigned char fh[NFS4_FHSIZE]
Definition: nfs41_types.h:53
BOOL WINAPI DeviceIoControl(IN HANDLE hDevice, IN DWORD dwIoControlCode, IN LPVOID lpInBuffer OPTIONAL, IN DWORD nInBufferSize OPTIONAL, OUT LPVOID lpOutBuffer OPTIONAL, IN DWORD nOutBufferSize OPTIONAL, OUT LPDWORD lpBytesReturned OPTIONAL, IN LPOVERLAPPED lpOverlapped OPTIONAL)
Definition: deviceio.c:136
#define InterlockedIncrement
Definition: armddk.h:53
IN OUT PVCB OUT PDIRENT OUT PBCB IN BOOLEAN CreateFile
Definition: fatprocs.h:904
nfs41_component name
Definition: nfs41_types.h:61
static int delegation_create(IN const nfs41_path_fh *parent, IN const nfs41_path_fh *file, IN const open_delegation4 *delegation, OUT nfs41_delegation_state **deleg_out)
Definition: delegation.c:36
GLuint GLint GLboolean GLint GLenum access
Definition: glext.h:7866
VOID WINAPI InitializeConditionVariable(PCONDITION_VARIABLE ConditionVariable)
Definition: sync.c:68
int nfs41_lock(IN nfs41_session *session, IN nfs41_path_fh *file, IN state_owner4 *owner, IN uint32_t type, IN uint64_t offset, IN uint64_t length, IN bool_t reclaim, IN bool_t try_recovery, IN OUT stateid_arg *stateid)
Definition: nfs41_ops.c:904
stateid4 stateid
Definition: nfs41_ops.h:284
static bool_t has_delegated_locks(IN nfs41_open_state *open)
Definition: delegation.c:128
Definition: list.h:27
uint32_t id
Definition: nfs41.h:118
int nfs41_delegation_getattr(IN nfs41_client *client, IN const nfs41_fh *fh, IN const bitmap4 *attr_request, OUT nfs41_file_info *info)
Definition: delegation.c:783
UINT32 uint32_t
Definition: types.h:75
enum delegation_status status
Definition: nfs41.h:103
#define list_for_each_tmp(entry, tmp, head)
Definition: list.h:39
#define calloc
Definition: rosglue.h:14
uint32_t claim
Definition: nfs41_ops.h:615
#define OUT
Definition: typedefs.h:39
__WINE_SERVER_LIST_INLINE void list_init(struct list *list)
Definition: list.h:149
uint32_t * LPDWORD
Definition: typedefs.h:57
void nfs41_delegation_remove_srvopen(IN nfs41_session *session, IN nfs41_path_fh *file)
Definition: delegation.c:610
CONDITION_VARIABLE cond
Definition: nfs41.h:105
void nfs41_root_deref(IN nfs41_root *root)
Definition: namespace.c:100
int nfs41_delegation_granted(IN nfs41_session *session, IN nfs41_path_fh *parent, IN nfs41_path_fh *file, IN open_delegation4 *delegation, IN bool_t try_recovery, OUT nfs41_delegation_state **deleg_out)
Definition: delegation.c:330
char path[NFS41_MAX_PATH_LEN]
Definition: nfs41_types.h:42
nfs41_abs_path path
Definition: nfs41.h:97
void WINAPI LeaveCriticalSection(LPCRITICAL_SECTION)
#define open_entry(pos)
Definition: delegation.c:89
#define INFINITE
Definition: serial.h:102
GLuint64EXT * result
Definition: glext.h:11304
static const struct access_res create[16]
Definition: package.c:7720
int nfs41_attr_cache_lookup(IN struct nfs41_name_cache *cache, IN uint64_t fileid, OUT nfs41_file_info *info_out)
Definition: name_cache.c:859
static SERVICE_STATUS status
Definition: service.c:31
#define args
Definition: format.c:66
VOID WINAPI AcquireSRWLockShared(PSRWLOCK Lock)
Definition: sync.c:61
int nfs41_client_delegation_return_lru(IN nfs41_client *client)
Definition: delegation.c:902
VOID WINAPI ReleaseSRWLockShared(PSRWLOCK Lock)
Definition: sync.c:89
static unsigned int WINAPI delegation_recall_thread(void *args)
Definition: delegation.c:683
#define lock_entry(pos)
Definition: delegation.c:126
Definition: fci.c:126
Definition: ps.c:97
PULONG MinorVersion OPTIONAL
Definition: CrossNt.h:68