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 <cbodley@citi.umich.edu>
216 lines
6.4 KiB
C
216 lines
6.4 KiB
C
/* 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);
|
|
}
|