From 0bee545e9136022f27297b347ce098669996b957 Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Thu, 9 Jun 2011 15:01:16 -0400 Subject: [PATCH] deleg: use delegations to satisfy opens locally added delegation.c and .h nfs41_client stores a list of nfs41_delegation_state new function nfs41_delegate_open() to look for a compatible delegation before calling nfs41_open() if nfs41_open() is granted a delegation, call nfs41_delegation_granted() to register it with the client client calls nfs41_client_delegation_free() on unmount to free list of delegations Signed-off-by: Casey Bodley --- build.vc10/daemon.vcxproj | 2 + build.vc10/daemon.vcxproj.filters | 6 + daemon/delegation.c | 216 ++++++++++++++++++++++++++++++ daemon/delegation.h | 57 ++++++++ daemon/nfs41.h | 12 ++ daemon/nfs41_client.c | 3 + daemon/open.c | 65 ++++++--- 7 files changed, 345 insertions(+), 16 deletions(-) create mode 100644 daemon/delegation.c create mode 100644 daemon/delegation.h 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