diff --git a/build.vc10/daemon.vcxproj b/build.vc10/daemon.vcxproj
index 68ddf97..22a0fdc 100644
--- a/build.vc10/daemon.vcxproj
+++ b/build.vc10/daemon.vcxproj
@@ -195,6 +195,7 @@
+
@@ -227,6 +228,7 @@
+
diff --git a/build.vc10/daemon.vcxproj.filters b/build.vc10/daemon.vcxproj.filters
index 860e343..bda3139 100644
--- a/build.vc10/daemon.vcxproj.filters
+++ b/build.vc10/daemon.vcxproj.filters
@@ -113,6 +113,9 @@
Source Files
+
+ Source Files
+
@@ -166,6 +169,9 @@
Header Files
+
+ Header Files
+
diff --git a/daemon/delegation.c b/daemon/delegation.c
new file mode 100644
index 0000000..1a94882
--- /dev/null
+++ b/daemon/delegation.c
@@ -0,0 +1,216 @@
+/* Copyright (c) 2010
+ * The Regents of the University of Michigan
+ * All Rights Reserved
+ *
+ * Permission is granted to use, copy and redistribute this software
+ * for noncommercial education and research purposes, so long as no
+ * fee is charged, and so long as the name of the University of Michigan
+ * is not used in any advertising or publicity pertaining to the use
+ * or distribution of this software without specific, written prior
+ * authorization. Permission to modify or otherwise create derivative
+ * works of this software is not granted.
+ *
+ * This software is provided as is, without representation or warranty
+ * of any kind either express or implied, including without limitation
+ * the implied warranties of merchantability, fitness for a particular
+ * purpose, or noninfringement. The Regents of the University of
+ * Michigan shall not be liable for any damages, including special,
+ * indirect, incidental, or consequential damages, with respect to any
+ * claim arising out of or in connection with the use of the software,
+ * even if it has been or is hereafter advised of the possibility of
+ * such damages.
+ */
+
+#include "delegation.h"
+#include "nfs41_ops.h"
+#include "util.h"
+#include "daemon_debug.h"
+
+
+#define DGLVL 2 /* dprintf level for delegation logging */
+
+
+/* allocation and reference counting */
+static int delegation_create(
+ IN const nfs41_path_fh *file,
+ IN const open_delegation4 *delegation,
+ OUT nfs41_delegation_state **deleg_out)
+{
+ nfs41_delegation_state *state;
+ int status = NO_ERROR;
+
+ state = calloc(1, sizeof(nfs41_delegation_state));
+ if (state == NULL) {
+ status = GetLastError();
+ goto out;
+ }
+
+ memcpy(&state->state, delegation, sizeof(open_delegation4));
+ fh_copy(&state->fh, &file->fh);
+ list_init(&state->client_entry);
+ state->ref_count = 1;
+ *deleg_out = state;
+out:
+ return status;
+}
+
+void nfs41_delegation_ref(
+ IN nfs41_delegation_state *state)
+{
+ const LONG count = InterlockedIncrement(&state->ref_count);
+ dprintf(DGLVL, "nfs41_delegation_ref() count %d\n", count);
+}
+
+void nfs41_delegation_deref(
+ IN nfs41_delegation_state *state)
+{
+ const LONG count = InterlockedDecrement(&state->ref_count);
+ dprintf(DGLVL, "nfs41_delegation_deref() count %d\n", count);
+ if (count == 0)
+ free(state);
+}
+
+
+/* open delegation */
+int nfs41_delegation_granted(
+ IN nfs41_session *session,
+ IN nfs41_path_fh *file,
+ IN open_delegation4 *delegation,
+ OUT nfs41_delegation_state **deleg_out)
+{
+ nfs41_client *client = session->client;
+ nfs41_delegation_state *state;
+ int status = NO_ERROR;
+
+ if (delegation->recalled ||
+ delegation->type == OPEN_DELEGATE_NONE ||
+ delegation->type == OPEN_DELEGATE_NONE_EXT)
+ goto out;
+
+ /* allocate the delegation state */
+ status = delegation_create(file, delegation, &state);
+ if (status)
+ goto out_return;
+
+ /* register the delegation with the client */
+ EnterCriticalSection(&client->state.lock);
+ /* XXX: check for duplicates by fh and stateid? */
+ list_add_tail(&client->state.delegations, &state->client_entry);
+ LeaveCriticalSection(&client->state.lock);
+
+ nfs41_delegation_ref(state); /* return a reference */
+ *deleg_out = state;
+out:
+ return status;
+
+out_return: /* return the delegation on failure */
+ nfs41_delegreturn(session, file, &delegation->stateid);
+ goto out;
+}
+
+#define deleg_entry(pos) list_container(pos, nfs41_delegation_state, client_entry)
+
+static int deleg_fh_cmp(const struct list_entry *entry, const void *value)
+{
+ const nfs41_fh *lhs = &deleg_entry(entry)->fh;
+ const nfs41_fh *rhs = (const nfs41_fh*)value;
+ if (lhs->superblock != rhs->superblock) return -1;
+ if (lhs->fileid != rhs->fileid) return -1;
+ return 0;
+}
+
+static bool_t delegation_compatible(
+ IN enum open_delegation_type4 type,
+ IN uint32_t create,
+ IN uint32_t access,
+ IN uint32_t deny)
+{
+ /* TODO: allow write delegation to handle OPEN4_CREATE */
+ if (create == OPEN4_CREATE)
+ return FALSE;
+
+ switch (type) {
+ case OPEN_DELEGATE_WRITE:
+ /* An OPEN_DELEGATE_WRITE delegation allows the client to handle,
+ * on its own, all opens. */
+ return TRUE;
+
+ case OPEN_DELEGATE_READ:
+ /* An OPEN_DELEGATE_READ delegation allows a client to handle,
+ * on its own, requests to open a file for reading that do not
+ * deny OPEN4_SHARE_ACCESS_READ access to others. */
+ if (access & OPEN4_SHARE_ACCESS_WRITE || deny & OPEN4_SHARE_DENY_READ)
+ return FALSE;
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static int delegation_find(
+ IN nfs41_client *client,
+ IN const void *value,
+ IN list_compare_fn cmp,
+ OUT nfs41_delegation_state **deleg_out)
+{
+ struct list_entry *entry;
+ int status = NFS4ERR_BADHANDLE;
+
+ EnterCriticalSection(&client->state.lock);
+ entry = list_search(&client->state.delegations, value, cmp);
+ if (entry) {
+ *deleg_out = deleg_entry(entry);
+ nfs41_delegation_ref(*deleg_out);
+ status = NFS4_OK;
+ }
+ LeaveCriticalSection(&client->state.lock);
+ return status;
+}
+
+int nfs41_delegate_open(
+ IN nfs41_client *client,
+ IN nfs41_path_fh *file,
+ IN uint32_t create,
+ IN uint32_t access,
+ IN uint32_t deny,
+ OUT nfs41_delegation_state **deleg_out,
+ OUT nfs41_file_info *info)
+{
+ nfs41_delegation_state *deleg;
+ int status;
+
+ /* search for a delegation with this filehandle */
+ status = delegation_find(client, &file->fh, deleg_fh_cmp, &deleg);
+ if (status)
+ goto out;
+
+ if (!delegation_compatible(deleg->state.type, create, access, deny)) {
+ status = NFS4ERR_BADHANDLE;
+ goto out_deleg;
+ }
+
+ /* TODO: check access against deleg->state.permissions or send ACCESS */
+
+ *deleg_out = deleg;
+ status = NFS4_OK;
+out:
+ return status;
+
+out_deleg:
+ nfs41_delegation_deref(deleg);
+ goto out;
+}
+
+void nfs41_client_delegation_free(
+ IN nfs41_client *client)
+{
+ struct list_entry *entry, *tmp;
+
+ EnterCriticalSection(&client->state.lock);
+ list_for_each_tmp (entry, tmp, &client->state.delegations) {
+ list_remove(entry);
+ nfs41_delegation_deref(deleg_entry(entry));
+ }
+ LeaveCriticalSection(&client->state.lock);
+}
diff --git a/daemon/delegation.h b/daemon/delegation.h
new file mode 100644
index 0000000..d29a9a3
--- /dev/null
+++ b/daemon/delegation.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2010
+ * The Regents of the University of Michigan
+ * All Rights Reserved
+ *
+ * Permission is granted to use, copy and redistribute this software
+ * for noncommercial education and research purposes, so long as no
+ * fee is charged, and so long as the name of the University of Michigan
+ * is not used in any advertising or publicity pertaining to the use
+ * or distribution of this software without specific, written prior
+ * authorization. Permission to modify or otherwise create derivative
+ * works of this software is not granted.
+ *
+ * This software is provided as is, without representation or warranty
+ * of any kind either express or implied, including without limitation
+ * the implied warranties of merchantability, fitness for a particular
+ * purpose, or noninfringement. The Regents of the University of
+ * Michigan shall not be liable for any damages, including special,
+ * indirect, incidental, or consequential damages, with respect to any
+ * claim arising out of or in connection with the use of the software,
+ * even if it has been or is hereafter advised of the possibility of
+ * such damages.
+ */
+
+#ifndef DELEGATION_H
+#define DELEGATION_H
+
+#include "nfs41.h"
+
+
+/* reference counting and cleanup */
+void nfs41_delegation_ref(
+ IN nfs41_delegation_state *state);
+
+void nfs41_delegation_deref(
+ IN nfs41_delegation_state *state);
+
+void nfs41_client_delegation_free(
+ IN nfs41_client *client);
+
+
+/* open delegation */
+int nfs41_delegation_granted(
+ IN nfs41_session *session,
+ IN nfs41_path_fh *file,
+ IN open_delegation4 *delegation,
+ OUT nfs41_delegation_state **deleg_out);
+
+int nfs41_delegate_open(
+ IN nfs41_client *client,
+ IN nfs41_path_fh *file,
+ IN uint32_t create,
+ IN uint32_t access,
+ IN uint32_t deny,
+ OUT nfs41_delegation_state **deleg_out,
+ OUT nfs41_file_info *info);
+
+#endif /* DELEGATION_H */
diff --git a/daemon/nfs41.h b/daemon/nfs41.h
index 9dce6cf..3124b2e 100644
--- a/daemon/nfs41.h
+++ b/daemon/nfs41.h
@@ -80,6 +80,13 @@ typedef struct __nfs41_server {
LONG ref_count;
} nfs41_server;
+typedef struct __nfs41_delegation_state {
+ open_delegation4 state;
+ nfs41_fh fh;
+ struct list_entry client_entry; /* entry in nfs41_client.delegations */
+ LONG ref_count;
+} nfs41_delegation_state;
+
typedef struct __nfs41_lock_state {
struct list_entry open_entry; /* entry in nfs41_open_state.locks */
uint64_t offset;
@@ -110,6 +117,10 @@ typedef struct __nfs41_open_state {
uint32_t share_access;
uint32_t share_deny;
+ struct {
+ nfs41_delegation_state *state;
+ } delegation;
+
struct { /* list of open lock state for recovery */
stateid4 stateid;
struct list_entry list;
@@ -137,6 +148,7 @@ typedef struct __nfs41_rpc_clnt {
struct client_state {
struct list_entry opens; /* list of associated nfs41_open_state */
+ struct list_entry delegations; /* list of associated delegations */
CRITICAL_SECTION lock;
};
diff --git a/daemon/nfs41_client.c b/daemon/nfs41_client.c
index ecd51df..84ee58d 100644
--- a/daemon/nfs41_client.c
+++ b/daemon/nfs41_client.c
@@ -30,6 +30,7 @@
#include /* for hostent struct */
#include "tree.h"
+#include "delegation.h"
#include "daemon_debug.h"
#include "nfs41_ops.h"
@@ -248,6 +249,7 @@ int nfs41_client_create(
goto out_err_client;
list_init(&client->state.opens);
+ list_init(&client->state.delegations);
InitializeCriticalSection(&client->state.lock);
//initialize a lock used to protect access to client id and client id seq#
@@ -326,6 +328,7 @@ void nfs41_client_free(
IN nfs41_client *client)
{
dprintf(2, "nfs41_client_free(%llu)\n", client->clnt_id);
+ nfs41_client_delegation_free(client);
if (client->session) nfs41_session_free(client->session);
nfs41_destroy_clientid(client->rpc, client->clnt_id);
if (client->server) nfs41_server_deref(client->server);
diff --git a/daemon/open.c b/daemon/open.c
index f207726..27adfdd 100644
--- a/daemon/open.c
+++ b/daemon/open.c
@@ -26,6 +26,7 @@
#include
#include "nfs41_ops.h"
+#include "delegation.h"
#include "from_kernel.h"
#include "daemon_debug.h"
#include "upcall.h"
@@ -63,6 +64,7 @@ static int create_open_state(
(const char*)state->owner.owner);
state->ref_count = 1;
list_init(&state->locks.list);
+ list_init(&state->client_entry);
*state_out = state;
status = NO_ERROR;
@@ -82,6 +84,8 @@ static void open_state_free(
/* free associated lock state */
list_for_each_tmp(entry, tmp, &state->locks.list)
free(list_container(entry, nfs41_lock_state, open_entry));
+ if (state->delegation.state)
+ nfs41_delegation_deref(state->delegation.state);
free(state);
}
@@ -119,7 +123,7 @@ void nfs41_open_stateid_arg(
}
/* client list of associated open state */
-void client_state_add(
+static void client_state_add(
IN nfs41_open_state *state)
{
nfs41_client *client = state->session->client;
@@ -129,7 +133,7 @@ void client_state_add(
LeaveCriticalSection(&client->state.lock);
}
-void client_state_remove(
+static void client_state_remove(
IN nfs41_open_state *state)
{
nfs41_client *client = state->session->client;
@@ -148,7 +152,9 @@ int nfs41_open(
OUT OPTIONAL nfs41_file_info *info)
{
open_claim4 claim;
+ stateid4 open_stateid;
open_delegation4 delegation = { 0 };
+ nfs41_delegation_state *deleg_state = NULL;
int status;
claim.claim = CLAIM_NULL;
@@ -156,18 +162,45 @@ int nfs41_open(
status = nfs41_rpc_open(state->session, &state->parent, &state->file,
&state->owner, &claim, state->share_access, state->share_deny,
- create, createhow, mode, TRUE, &state->stateid, &delegation, info);
+ create, createhow, mode, TRUE, &open_stateid, &delegation, info);
if (status)
goto out;
- if (delegation.type == OPEN_DELEGATE_READ ||
- delegation.type == OPEN_DELEGATE_WRITE)
- nfs41_delegreturn(state->session, &state->file, &delegation.stateid);
+ /* allocate delegation state and register it with the client */
+ nfs41_delegation_granted(state->session,
+ &state->file, &delegation, &deleg_state);
+
+ AcquireSRWLockExclusive(&state->lock);
+ /* update the stateid */
+ memcpy(&state->stateid, &open_stateid, sizeof(open_stateid));
+ state->do_close = 1;
+ state->delegation.state = deleg_state;
+ ReleaseSRWLockExclusive(&state->lock);
+out:
+ return status;
+}
+
+static int open_or_delegate(
+ IN OUT nfs41_open_state *state,
+ IN uint32_t create,
+ IN uint32_t createhow,
+ IN uint32_t mode,
+ IN bool_t try_recovery,
+ OUT nfs41_file_info *info)
+{
+ int status;
+
+ /* check for existing delegation */
+ status = nfs41_delegate_open(state->session->client, &state->file, create,
+ state->share_access, state->share_deny, &state->delegation.state, info);
+
+ /* get an open stateid if we have no delegation stateid */
+ if (status)
+ status = nfs41_open(state, create, createhow, mode, try_recovery, info);
/* register the client's open state on success */
- client_state_add(state);
- state->do_close = 1;
-out:
+ if (status == NFS4_OK)
+ client_state_add(state);
return status;
}
@@ -509,8 +542,8 @@ static int handle_open(nfs41_upcall *upcall)
args->std_info.Directory = 1;
args->created = status == NFS4_OK ? TRUE : FALSE;
} else {
- status = nfs41_open(state, create, createhowmode,
- args->mode, TRUE, &info);
+ status = open_or_delegate(state, create,
+ createhowmode, args->mode, TRUE, &info);
if (status == NFS4_OK) {
nfs_to_basic_info(&info, &args->basic_info);
nfs_to_standard_info(&info, &args->std_info);
@@ -589,8 +622,6 @@ static void cancel_open(IN nfs41_upcall *upcall)
dprintf(1, "cancel_open: nfs41_close() failed with %s\n",
nfs_error_string(status));
- /* remove from the client's list of state for recovery */
- client_state_remove(state);
} else if (args->created) {
const nfs41_component *name = &state->file.name;
status = nfs41_remove(state->session, &state->parent, name);
@@ -599,6 +630,8 @@ static void cancel_open(IN nfs41_upcall *upcall)
nfs_error_string(status));
}
+ /* remove from the client's list of state for recovery */
+ client_state_remove(state);
nfs41_open_state_deref(state);
out:
status = nfs_to_windows_error(status, ERROR_INTERNAL_ERROR);
@@ -664,11 +697,11 @@ static int handle_close(nfs41_upcall *upcall)
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_INTERNAL_ERROR);
}
-
- /* remove from the client's list of state for recovery */
- client_state_remove(state);
}
+ /* remove from the client's list of state for recovery */
+ client_state_remove(state);
+
if (status || !rm_status)
return status;
else