fresh git tree for public release

we regretfully had to remove our git history for licensing reasons

Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
This commit is contained in:
Casey Bodley 2010-10-11 14:59:26 -04:00
commit 0ad4db4fad
271 changed files with 71255 additions and 0 deletions

381
daemon/callback_server.c Normal file
View file

@ -0,0 +1,381 @@
/* 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 <Windows.h>
#include <strsafe.h>
#include "nfs41.h"
#include "nfs41_ops.h"
#include "nfs41_callback.h"
#include "daemon_debug.h"
#define CBSLVL 2 /* dprintf level for callback server logging */
static const char g_server_tag[] = "ms-nfs41-callback";
/* OP_CB_LAYOUTRECALL */
static enum_t handle_cb_layoutrecall(
IN nfs41_rpc_clnt *rpc_clnt,
IN struct cb_layoutrecall_args *args,
OUT struct cb_layoutrecall_res *res)
{
enum pnfs_status status;
/* forgetful model for layout recalls; return NOMATCHING_LAYOUT
* and flag the layout(s) to prevent further use */
res->status = NFS4ERR_NOMATCHING_LAYOUT;
dprintf(CBSLVL, " OP_CB_LAYOUTRECALL { %s, %s, recall %u } %s\n",
pnfs_layout_type_string(args->type),
pnfs_iomode_string(args->iomode), args->recall.type,
nfs_error_string(res->status));
status = pnfs_file_layout_recall(rpc_clnt->client->layouts, args);
if (status)
eprintf("pnfs_file_layout_recall() failed with %s\n",
pnfs_error_string(status));
return res->status;
}
/* OP_CB_RECALL_SLOT */
static enum_t handle_cb_recall_slot(
IN struct cb_recall_slot_args *args,
OUT struct cb_recall_slot_res *res)
{
res->status = NFS4_OK;
dprintf(CBSLVL, " OP_CB_RECALL_SLOT { %u } %s\n",
args->target_highest_slotid, nfs_error_string(res->status));
return res->status;
}
/* OP_CB_SEQUENCE */
static enum_t handle_cb_sequence(
IN nfs41_rpc_clnt *rpc_clnt,
IN struct cb_sequence_args *args,
OUT struct cb_sequence_res *res)
{
nfs41_cb_session *cb_session = &rpc_clnt->client->session->cb_session;
uint32_t status = NFS4_OK;
res->status = NFS4_OK;
if (!cb_session->cb_is_valid_state) {
memcpy(cb_session->cb_sessionid, args->sessionid, NFS4_SESSIONID_SIZE);
if (args->sequenceid != 1) {
eprintf("[cb] 1st seq#=%d is not 1\n", args->sequenceid);
res->status = NFS4ERR_SEQ_MISORDERED;
goto out;
}
cb_session->cb_is_valid_state = TRUE;
} else {
if (memcmp(cb_session->cb_sessionid, args->sessionid,
NFS4_SESSIONID_SIZE)) {
eprintf("[cb] received sessionid doesn't match saved info\n");
print_hexbuf(1, (unsigned char *)"received sessionid",
(unsigned char *)args->sessionid, NFS4_SESSIONID_SIZE);
print_hexbuf(1, (unsigned char *)"saved sessionid",
cb_session->cb_sessionid, NFS4_SESSIONID_SIZE);
res->status = NFS4ERR_BADSESSION;
goto out;
}
}
/* 20.9.3.: If the difference between csa_sequenceid and the client's
* cachedsequence ID at the slot ID is two (2) or more, or if
* csa_sequenceid is less than the cached sequence ID (accounting for
* wraparound of the unsigned sequence ID value), then the client
* MUST return NFS4ERR_SEQ_MISORDERED.
*/
if (args->sequenceid < cb_session->cb_seqnum ||
(args->sequenceid - cb_session->cb_seqnum >= 2)) {
eprintf("[cb] bad received seq#=%d, expected=%d\n",
args->sequenceid, cb_session->cb_seqnum+1);
res->status = NFS4ERR_SEQ_MISORDERED;
goto out;
}
/* we only support 1 slot for the back channel so slotid MUST be 0 */
if (args->slotid != 0) {
eprintf("[cb] received unexpected slotid=%d\n", args->slotid);
res->status = NFS4ERR_BADSLOT;
goto out;
}
if (args->highest_slotid != 0) {
eprintf("[cb] received unexpected highest_slotid=%d\n",
args->highest_slotid);
res->status = NFS4ERR_BAD_HIGH_SLOT;
goto out;
}
if (args->sequenceid == cb_session->cb_seqnum) {
// check if the request is same as the original, if different need
// to return NFS4ERR_SEQ_FALSE_RETRY
if (!cb_session->cb_cache_this) {
res->status = NFS4_OK;
status = NFS4ERR_RETRY_UNCACHED_REP;
goto out;
} else {
res->status = NFS4ERR_REP_TOO_BIG_TO_CACHE;
goto out;
}
}
if (cb_session->cb_seqnum + 1 == 0)
cb_session->cb_seqnum = 0;
else
cb_session->cb_seqnum = args->sequenceid;
cb_session->cb_cache_this = args->cachethis;
memcpy(res->ok.sessionid, args->sessionid, NFS4_SESSIONID_SIZE);
res->ok.sequenceid = args->sequenceid;
res->ok.slotid = args->slotid;
res->ok.highest_slotid = args->highest_slotid;
res->ok.target_highest_slotid = args->highest_slotid;
out:
dprintf(CBSLVL, " OP_CB_SEQUENCE { seqid %u, slot %u, cachethis %d } "
"%s\n", args->sequenceid, args->slotid, args->cachethis,
nfs_error_string(res->status));
return status;
}
/* OP_CB_RECALL */
typedef struct _nfs41_cb_recall {
nfs41_rpc_clnt *rpc_clnt;
struct cb_recall_args *args;
} nfs41_cb_recall;
static unsigned int WINAPI _handle_cb_recall(void *args)
{
nfs41_cb_recall *cb_args = (nfs41_cb_recall *)args;
nfs41_path_fh path_fh;
dprintf(1, "_handle_cb_recall: start\n");
print_hexbuf(3, (unsigned char *)"_handle_cb_recall: fh ",
cb_args->args->fh.fh, cb_args->args->fh.len);
print_hexbuf(3, (unsigned char *)"_handle_cb_recall: stateid ",
cb_args->args->stateid.other, 12);
ZeroMemory(&path_fh, sizeof(nfs41_path_fh));
memcpy(&path_fh.fh, &cb_args->args->fh, sizeof(nfs41_fh));
path_fh.fh.superblock = NULL;
path_fh.path = NULL;
path_fh.name.len = 0;
dprintf(1, "_handle_cb_recall: sending nfs41_delegreturn\n");
nfs41_delegreturn(cb_args->rpc_clnt->client->session, &path_fh,
&cb_args->args->stateid);
free(cb_args);
dprintf(1, "_handle_cb_recall: end\n");
return 1;
}
static enum_t handle_cb_recall(
IN nfs41_rpc_clnt *rpc_clnt,
IN struct cb_recall_args *args,
OUT struct cb_recall_res *res)
{
nfs41_cb_recall *cb_args;
res->status = NFS4_OK;
dprintf(CBSLVL, "OP_CB_RECALL\n");
cb_args = calloc(1, sizeof(nfs41_cb_recall));
if (cb_args == NULL) {
res->status = NFS4ERR_RESOURCE;
goto out;
}
cb_args->rpc_clnt = rpc_clnt;
cb_args->args = args;
_beginthreadex(NULL, 0, _handle_cb_recall, cb_args, 0, NULL);
out:
return res->status;
}
/* CB_COMPOUND */
static void handle_cb_compound(nfs41_rpc_clnt *rpc_clnt, cb_req *req, struct cb_compound_res **reply)
{
struct cb_compound_args args = { 0 };
struct cb_compound_res *res = NULL;
struct cb_argop *argop;
struct cb_resop *resop;
XDR *xdr = (XDR*)req->xdr;
uint32_t i, status = NFS4_OK;
dprintf(CBSLVL, "--> handle_compound()\n");
/* decode the arguments */
proc_cb_compound_args(xdr, &args);
dprintf(CBSLVL, "CB_COMPOUND('%s', %u)\n", args.tag.str, args.argarray_count);
if (args.minorversion != 1) {
status = NFS4ERR_MINOR_VERS_MISMATCH; //XXXXX
eprintf("args.minorversion %u != 1\n", args.minorversion);
goto out;
}
/* allocate the compound results */
res = calloc(1, sizeof(struct cb_compound_res));
if (res == NULL) {
status = NFS4ERR_RESOURCE;
goto out;
}
res->status = NFS4_OK;
StringCchCopyA(res->tag.str, CB_COMPOUND_MAX_TAG, g_server_tag);
res->tag.str[CB_COMPOUND_MAX_TAG-1] = 0;
res->tag.len = (uint32_t)strlen(res->tag.str);
res->resarray = calloc(args.argarray_count, sizeof(struct cb_resop));
if (res->resarray == NULL) {
res->status = NFS4ERR_RESOURCE;
goto out;
}
/* handle each operation in the compound */
for (i = 0; i < args.argarray_count && res->status == NFS4_OK; i++) {
argop = &args.argarray[i];
resop = &res->resarray[i];
/* 20.9.3: The error NFS4ERR_SEQUENCE_POS MUST be returned
* when CB_SEQUENCE is found in any position in a CB_COMPOUND
* beyond the first. If any other operation is in the first
* position of CB_COMPOUND, NFS4ERR_OP_NOT_IN_SESSION MUST
* be returned.
*/
if (i == 0 && argop->opnum != OP_CB_SEQUENCE) {
res->status = NFS4ERR_OP_NOT_IN_SESSION;
goto out;
}
if (i != 0 && argop->opnum == OP_CB_SEQUENCE) {
res->status = NFS4ERR_SEQUENCE_POS;
goto out;
}
resop->opnum = argop->opnum;
if (status == NFS4ERR_RETRY_UNCACHED_REP) {
res->status = status;
break;
}
switch (argop->opnum) {
case OP_CB_LAYOUTRECALL:
dprintf(1, "OP_CB_LAYOUTRECALL\n");
res->status = handle_cb_layoutrecall(rpc_clnt,
&argop->args.layoutrecall, &resop->res.layoutrecall);
break;
case OP_CB_RECALL_SLOT:
dprintf(1, "OP_CB_RECALL_SLOT\n");
res->status = handle_cb_recall_slot(&argop->args.recall_slot,
&resop->res.recall_slot);
break;
case OP_CB_SEQUENCE:
dprintf(1, "OP_CB_SEQUENCE\n");
status = handle_cb_sequence(rpc_clnt, &argop->args.sequence,
&resop->res.sequence);
if (status == NFS4_OK)
res->status = resop->res.sequence.status;
break;
case OP_CB_GETATTR:
dprintf(1, "OP_CB_GETATTR\n");
res->status = NFS4ERR_NOTSUPP;
break;
case OP_CB_RECALL:
dprintf(1, "OP_CB_RECALL\n");
res->status = handle_cb_recall(rpc_clnt,
&argop->args.recall, &resop->res.recall);
break;
case OP_CB_NOTIFY:
dprintf(1, "OP_CB_NOTIFY\n");
res->status = NFS4ERR_NOTSUPP;
break;
case OP_CB_PUSH_DELEG:
dprintf(1, "OP_CB_PUSH_DELEG\n");
res->status = NFS4ERR_NOTSUPP;
break;
case OP_CB_RECALL_ANY:
dprintf(1, "OP_CB_RECALL_ANY\n");
res->status = NFS4ERR_NOTSUPP;
break;
case OP_CB_RECALLABLE_OBJ_AVAIL:
dprintf(1, "OP_CB_RECALLABLE_OBJ_AVAIL\n");
res->status = NFS4ERR_NOTSUPP;
break;
case OP_CB_WANTS_CANCELLED:
dprintf(1, "OP_CB_WANTS_CANCELLED\n");
res->status = NFS4ERR_NOTSUPP;
break;
case OP_CB_NOTIFY_LOCK:
dprintf(1, "OP_CB_NOTIFY_LOCK\n");
res->status = NFS4ERR_NOTSUPP;
break;
case OP_CB_NOTIFY_DEVICEID:
dprintf(1, "OP_CB_NOTIFY_DEVICEID\n");
res->status = NFS4ERR_NOTSUPP;
break;
case OP_CB_ILLEGAL:
dprintf(1, "OP_CB_ILLEGAL\n");
res->status = NFS4ERR_NOTSUPP;
break;
default:
eprintf("operation %u not supported\n", argop->opnum);
res->status = NFS4ERR_NOTSUPP;
goto out;
}
res->resarray_count++;
}
out:
/* free the arguments */
xdr->x_op = XDR_FREE;
proc_cb_compound_args(xdr, &args);
*reply = res;
dprintf(CBSLVL, "<-- handle_compound() returning %s (%u results)\n",
nfs_error_string(res->status), res->resarray_count);
}
int nfs41_handle_callback(void *rpc_clnt, void *cb, struct cb_compound_res **reply)
{
nfs41_rpc_clnt *rpc = (nfs41_rpc_clnt *)rpc_clnt;
cb_req *request = (cb_req *)cb;
uint32_t status = 0;
dprintf(1, "nfs41_handle_callback: received call\n");
if (request->rq_prog != NFS41_RPC_CBPROGRAM) {
eprintf("invalid rpc program %u\n", request->rq_prog);
status = 2;
goto out;
}
switch (request->rq_proc) {
case CB_NULL:
dprintf(1, "CB_NULL\n");
break;
case CB_COMPOUND:
dprintf(1, "CB_COMPOUND\n");
handle_cb_compound(rpc, request, reply);
break;
default:
dprintf(1, "invalid rpc procedure %u\n", request->rq_proc);
status = 3;
goto out;
}
out:
return status;
}

539
daemon/callback_xdr.c Normal file
View file

@ -0,0 +1,539 @@
/* 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 "nfs41_callback.h"
#include "nfs41_ops.h"
#include "daemon_debug.h"
#define CBXLVL 2 /* dprintf level for callback xdr logging */
#define CBX_ERR(msg) dprintf((CBXLVL), __FUNCTION__ ": failed at " msg "\n")
/* common types */
static bool_t common_stateid(XDR *xdr, stateid4 *stateid)
{
return xdr_u_int32_t(xdr, &stateid->seqid)
&& xdr_opaque(xdr, (char*)stateid->other, 12);
}
static bool_t common_fh(XDR *xdr, nfs41_fh *fh)
{
return xdr_u_int32_t(xdr, &fh->len)
&& fh->len <= NFS4_FHSIZE
&& xdr_opaque(xdr, (char*)fh->fh, fh->len);
}
static bool_t common_fsid(XDR *xdr, nfs41_fsid *fsid)
{
return xdr_u_int64_t(xdr, &fsid->major)
&& xdr_u_int64_t(xdr, &fsid->minor);
}
/* OP_CB_LAYOUTRECALL */
static bool_t op_cb_layoutrecall_file(XDR *xdr, struct cb_recall_file *args)
{
bool_t result;
result = common_fh(xdr, &args->fh);
if (!result) { CBX_ERR("layoutrecall_file.fh"); goto out; }
result = xdr_u_int64_t(xdr, &args->offset);
if (!result) { CBX_ERR("layoutrecall_file.offset"); goto out; }
result = xdr_u_int64_t(xdr, &args->length);
if (!result) { CBX_ERR("layoutrecall_file.length"); goto out; }
result = common_stateid(xdr, &args->stateid);
if (!result) { CBX_ERR("layoutrecall_file.stateid"); goto out; }
out:
return result;
}
static bool_t op_cb_layoutrecall_fsid(XDR *xdr, union cb_recall_file_args *args)
{
bool_t result;
result = common_fsid(xdr, &args->fsid);
if (!result) { CBX_ERR("layoutrecall_fsid.fsid"); goto out; }
out:
return result;
}
static const struct xdr_discrim cb_layoutrecall_discrim[] = {
{ PNFS_RETURN_FILE, (xdrproc_t)op_cb_layoutrecall_file },
{ PNFS_RETURN_FSID, (xdrproc_t)op_cb_layoutrecall_fsid },
{ PNFS_RETURN_ALL, (xdrproc_t)xdr_void },
{ 0, NULL_xdrproc_t }
};
static bool_t op_cb_layoutrecall_args(XDR *xdr, struct cb_layoutrecall_args *args)
{
bool_t result;
result = xdr_enum(xdr, (enum_t*)&args->type);
if (!result) { CBX_ERR("layoutrecall_args.type"); goto out; }
result = xdr_enum(xdr, (enum_t*)&args->iomode);
if (!result) { CBX_ERR("layoutrecall_args.iomode"); goto out; }
result = xdr_bool(xdr, &args->changed);
if (!result) { CBX_ERR("layoutrecall_args.changed"); goto out; }
result = xdr_union(xdr, (enum_t*)&args->recall.type,
(char*)&args->recall.args, cb_layoutrecall_discrim, NULL_xdrproc_t);
if (!result) { CBX_ERR("layoutrecall_args.recall"); goto out; }
out:
return result;
}
static bool_t op_cb_layoutrecall_res(XDR *xdr, struct cb_layoutrecall_res *res)
{
bool_t result;
result = xdr_enum(xdr, &res->status);
if (!result) { CBX_ERR("layoutrecall_res.status"); goto out; }
out:
return result;
}
/* OP_CB_RECALL_SLOT */
static bool_t op_cb_recall_slot_args(XDR *xdr, struct cb_recall_slot_args *res)
{
bool_t result;
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
if (!result) { CBX_ERR("recall_slot.target_highest_slotid"); goto out; }
out:
return result;
}
static bool_t op_cb_recall_slot_res(XDR *xdr, struct cb_recall_slot_res *res)
{
bool_t result;
result = xdr_enum(xdr, &res->status);
if (!result) { CBX_ERR("recall_slot.status"); goto out; }
out:
return result;
}
/* OP_CB_SEQUENCE */
static bool_t op_cb_sequence_ref(XDR *xdr, struct cb_sequence_ref *args)
{
bool_t result;
result = xdr_u_int32_t(xdr, &args->sequenceid);
if (!result) { CBX_ERR("sequence_ref.sequenceid"); goto out; }
result = xdr_u_int32_t(xdr, &args->slotid);
if (!result) { CBX_ERR("sequence_ref.slotid"); goto out; }
out:
return result;
}
static bool_t op_cb_sequence_ref_list(XDR *xdr, struct cb_sequence_ref_list *args)
{
bool_t result;
result = xdr_opaque(xdr, args->sessionid, NFS4_SESSIONID_SIZE);
if (!result) { CBX_ERR("sequence_ref_list.sessionid"); goto out; }
result = xdr_array(xdr, (char**)&args->calls, &args->call_count,
64, sizeof(struct cb_sequence_ref), (xdrproc_t)op_cb_sequence_ref);
if (!result) { CBX_ERR("sequence_ref_list.calls"); goto out; }
out:
return result;
}
static bool_t op_cb_sequence_args(XDR *xdr, struct cb_sequence_args *args)
{
bool_t result;
dprintf(1, "decoding sequence args\n");
result = xdr_opaque(xdr, args->sessionid, NFS4_SESSIONID_SIZE);
if (!result) { CBX_ERR("sequence_args.sessionid"); goto out; }
result = xdr_u_int32_t(xdr, &args->sequenceid);
if (!result) { CBX_ERR("sequence_args.sequenceid"); goto out; }
result = xdr_u_int32_t(xdr, &args->slotid);
if (!result) { CBX_ERR("sequence_args.slotid"); goto out; }
result = xdr_u_int32_t(xdr, &args->highest_slotid);
if (!result) { CBX_ERR("sequence_args.highest_slotid"); goto out; }
result = xdr_bool(xdr, &args->cachethis);
if (!result) { CBX_ERR("sequence_args.cachethis"); goto out; }
result = xdr_array(xdr, (char**)&args->ref_lists,
&args->ref_list_count, 64, sizeof(struct cb_sequence_ref_list),
(xdrproc_t)op_cb_sequence_ref_list);
if (!result) { CBX_ERR("sequence_args.ref_lists"); goto out; }
out:
return result;
}
static bool_t op_cb_sequence_res_ok(XDR *xdr, struct cb_sequence_res_ok *res)
{
bool_t result;
result = xdr_opaque(xdr, res->sessionid, NFS4_SESSIONID_SIZE);
if (!result) { CBX_ERR("sequence_res.sessionid"); goto out; }
result = xdr_u_int32_t(xdr, &res->sequenceid);
if (!result) { CBX_ERR("sequence_res.sequenceid"); goto out; }
result = xdr_u_int32_t(xdr, &res->slotid);
if (!result) { CBX_ERR("sequence_res.slotid"); goto out; }
result = xdr_u_int32_t(xdr, &res->highest_slotid);
if (!result) { CBX_ERR("sequence_res.highest_slotid"); goto out; }
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
if (!result) { CBX_ERR("sequence_res.target_highest_slotid"); goto out; }
out:
return result;
}
static const struct xdr_discrim cb_sequence_res_discrim[] = {
{ NFS4_OK, (xdrproc_t)op_cb_sequence_res_ok },
{ 0, NULL_xdrproc_t }
};
static bool_t op_cb_sequence_res(XDR *xdr, struct cb_sequence_res *res)
{
bool_t result;
result = xdr_union(xdr, &res->status, (char*)&res->ok,
cb_sequence_res_discrim, (xdrproc_t)xdr_void);
if (!result) { CBX_ERR("seq:argop.args"); goto out; }
out:
return result;
}
/* OP_CB_GETATTR */
static bool_t op_cb_getattr_args(XDR *xdr, struct cb_getattr_args *res)
{
bool_t result;
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
if (!result) { CBX_ERR("getattr.target_highest_slotid"); goto out; }
out:
return result;
}
static bool_t op_cb_getattr_res(XDR *xdr, struct cb_getattr_res *res)
{
bool_t result;
result = xdr_enum(xdr, &res->status);
if (!result) { CBX_ERR("getattr.status"); goto out; }
out:
return result;
}
/* OP_CB_RECALL */
static bool_t op_cb_recall_args(XDR *xdr, struct cb_recall_args *args)
{
bool_t result;
dprintf(1, "decoding recall args\n");
result = common_stateid(xdr, &args->stateid);
if (!result) { CBX_ERR("recall.stateid"); goto out; }
result = xdr_bool(xdr, &args->truncate);
if (!result) { CBX_ERR("recall.truncate"); goto out; }
result = common_fh(xdr, &args->fh);
if (!result) { CBX_ERR("recall.fh"); goto out; }
out:
return result;
}
static bool_t op_cb_recall_res(XDR *xdr, struct cb_recall_res *res)
{
bool_t result;
result = xdr_enum(xdr, &res->status);
if (!result) { CBX_ERR("recall.status"); goto out; }
out:
return result;
}
/* OP_CB_NOTIFY */
static bool_t op_cb_notify_args(XDR *xdr, struct cb_notify_args *res)
{
bool_t result;
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
if (!result) { CBX_ERR("notify.target_highest_slotid"); goto out; }
out:
return result;
}
static bool_t op_cb_notify_res(XDR *xdr, struct cb_notify_res *res)
{
bool_t result;
result = xdr_enum(xdr, &res->status);
if (!result) { CBX_ERR("notify.status"); goto out; }
out:
return result;
}
/* OP_CB_PUSH_DELEG */
static bool_t op_cb_push_deleg_args(XDR *xdr, struct cb_push_deleg_args *res)
{
bool_t result;
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
if (!result) { CBX_ERR("push_deleg.target_highest_slotid"); goto out; }
out:
return result;
}
static bool_t op_cb_push_deleg_res(XDR *xdr, struct cb_push_deleg_res *res)
{
bool_t result;
result = xdr_enum(xdr, &res->status);
if (!result) { CBX_ERR("push_deleg.status"); goto out; }
out:
return result;
}
/* OP_CB_RECALL_ANY */
static bool_t op_cb_recall_any_args(XDR *xdr, struct cb_recall_any_args *res)
{
bool_t result;
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
if (!result) { CBX_ERR("recall_any.target_highest_slotid"); goto out; }
out:
return result;
}
static bool_t op_cb_recall_any_res(XDR *xdr, struct cb_recall_any_res *res)
{
bool_t result;
result = xdr_enum(xdr, &res->status);
if (!result) { CBX_ERR("recall_any.status"); goto out; }
out:
return result;
}
/* OP_CB_RECALLABLE_OBJ_AVAIL */
static bool_t op_cb_recallable_obj_avail_args(XDR *xdr, struct cb_recallable_obj_avail_args *res)
{
bool_t result;
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
if (!result) { CBX_ERR("recallable_obj_avail.target_highest_slotid"); goto out; }
out:
return result;
}
static bool_t op_cb_recallable_obj_avail_res(XDR *xdr, struct cb_recallable_obj_avail_res *res)
{
bool_t result;
result = xdr_enum(xdr, &res->status);
if (!result) { CBX_ERR("recallable_obj_avail.status"); goto out; }
out:
return result;
}
/* OP_CB_WANTS_CANCELLED */
static bool_t op_cb_wants_cancelled_args(XDR *xdr, struct cb_wants_cancelled_args *res)
{
bool_t result;
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
if (!result) { CBX_ERR("wants_cancelled.target_highest_slotid"); goto out; }
out:
return result;
}
static bool_t op_cb_wants_cancelled_res(XDR *xdr, struct cb_wants_cancelled_res *res)
{
bool_t result;
result = xdr_enum(xdr, &res->status);
if (!result) { CBX_ERR("wants_cancelled.status"); goto out; }
out:
return result;
}
/* OP_CB_NOTIFY_LOCK */
static bool_t op_cb_notify_lock_args(XDR *xdr, struct cb_notify_lock_args *res)
{
bool_t result;
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
if (!result) { CBX_ERR("notify_lock.target_highest_slotid"); goto out; }
out:
return result;
}
static bool_t op_cb_notify_lock_res(XDR *xdr, struct cb_notify_lock_res *res)
{
bool_t result;
result = xdr_enum(xdr, &res->status);
if (!result) { CBX_ERR("notify_lock.status"); goto out; }
out:
return result;
}
/* OP_CB_NOTIFY_DEVICEID */
static bool_t op_cb_notify_deviceid_args(XDR *xdr, struct cb_notify_deviceid_args *res)
{
bool_t result;
result = xdr_u_int32_t(xdr, &res->target_highest_slotid);
if (!result) { CBX_ERR("notify_deviceid.target_highest_slotid"); goto out; }
out:
return result;
}
static bool_t op_cb_notify_deviceid_res(XDR *xdr, struct cb_notify_deviceid_res *res)
{
bool_t result;
result = xdr_enum(xdr, &res->status);
if (!result) { CBX_ERR("notify_deviceid.status"); goto out; }
out:
return result;
}
/* CB_COMPOUND */
static bool_t cb_compound_tag(XDR *xdr, struct cb_compound_tag *args)
{
return xdr_u_int32_t(xdr, &args->len)
&& args->len <= CB_COMPOUND_MAX_TAG
&& xdr_opaque(xdr, args->str, args->len);
}
static const struct xdr_discrim cb_argop_discrim[] = {
{ OP_CB_LAYOUTRECALL, (xdrproc_t)op_cb_layoutrecall_args },
{ OP_CB_RECALL_SLOT, (xdrproc_t)op_cb_recall_slot_args },
{ OP_CB_SEQUENCE, (xdrproc_t)op_cb_sequence_args },
{ OP_CB_GETATTR, (xdrproc_t)op_cb_getattr_args },
{ OP_CB_RECALL, (xdrproc_t)op_cb_recall_args },
{ OP_CB_NOTIFY, (xdrproc_t)op_cb_notify_args },
{ OP_CB_PUSH_DELEG, (xdrproc_t)op_cb_push_deleg_args },
{ OP_CB_RECALL_ANY, (xdrproc_t)op_cb_recall_any_args },
{ OP_CB_RECALLABLE_OBJ_AVAIL, (xdrproc_t)op_cb_recallable_obj_avail_args },
{ OP_CB_WANTS_CANCELLED, (xdrproc_t)op_cb_wants_cancelled_args },
{ OP_CB_NOTIFY_LOCK, (xdrproc_t)op_cb_notify_lock_args },
{ OP_CB_NOTIFY_DEVICEID, (xdrproc_t)op_cb_notify_deviceid_args },
{ OP_CB_ILLEGAL, NULL_xdrproc_t },
};
static bool_t cb_compound_argop(XDR *xdr, struct cb_argop *args)
{
bool_t result;
result = xdr_union(xdr, &args->opnum, (char*)&args->args,
cb_argop_discrim, NULL_xdrproc_t);
if (!result) { CBX_ERR("cmb:argop.args"); goto out; }
out:
return result;
}
bool_t proc_cb_compound_args(XDR *xdr, struct cb_compound_args *args)
{
bool_t result;
result = cb_compound_tag(xdr, &args->tag);
if (!result) { CBX_ERR("compound.tag"); goto out; }
result = xdr_u_int32_t(xdr, &args->minorversion);
if (!result) { CBX_ERR("compound.minorversion"); goto out; }
/* "superfluous in NFSv4.1 and MUST be ignored by the client" */
result = xdr_u_int32_t(xdr, &args->callback_ident);
if (!result) { CBX_ERR("compound.callback_ident"); goto out; }
result = xdr_array(xdr, (char**)&args->argarray,
&args->argarray_count, CB_COMPOUND_MAX_OPERATIONS,
sizeof(struct cb_argop), (xdrproc_t)cb_compound_argop);
if (!result) { CBX_ERR("compound.argarray"); goto out; }
out:
return result;
}
static const struct xdr_discrim cb_resop_discrim[] = {
{ OP_CB_LAYOUTRECALL, (xdrproc_t)op_cb_layoutrecall_res },
{ OP_CB_RECALL_SLOT, (xdrproc_t)op_cb_recall_slot_res },
{ OP_CB_SEQUENCE, (xdrproc_t)op_cb_sequence_res },
{ OP_CB_GETATTR, (xdrproc_t)op_cb_getattr_res },
{ OP_CB_RECALL, (xdrproc_t)op_cb_recall_res },
{ OP_CB_NOTIFY, (xdrproc_t)op_cb_notify_res },
{ OP_CB_PUSH_DELEG, (xdrproc_t)op_cb_push_deleg_res },
{ OP_CB_RECALL_ANY, (xdrproc_t)op_cb_recall_any_res },
{ OP_CB_RECALLABLE_OBJ_AVAIL, (xdrproc_t)op_cb_recallable_obj_avail_res },
{ OP_CB_WANTS_CANCELLED, (xdrproc_t)op_cb_wants_cancelled_res },
{ OP_CB_NOTIFY_LOCK, (xdrproc_t)op_cb_notify_lock_res },
{ OP_CB_NOTIFY_DEVICEID, (xdrproc_t)op_cb_notify_deviceid_res },
{ OP_CB_ILLEGAL, NULL_xdrproc_t },
};
static bool_t cb_compound_resop(XDR *xdr, struct cb_resop *res)
{
bool_t result;
result = xdr_union(xdr, &res->opnum, (char*)&res->res,
cb_resop_discrim, NULL_xdrproc_t);
if (!result) { CBX_ERR("resop.res"); goto out; }
out:
return result;
}
bool_t proc_cb_compound_res(XDR *xdr, struct cb_compound_res *res)
{
bool_t result;
if (res == NULL)
return TRUE;
result = xdr_enum(xdr, &res->status);
if (!result) { CBX_ERR("compound_res.status"); goto out; }
result = cb_compound_tag(xdr, &res->tag);
if (!result) { CBX_ERR("compound_res.tag"); goto out; }
result = xdr_array(xdr, (char**)&res->resarray,
&res->resarray_count, CB_COMPOUND_MAX_OPERATIONS,
sizeof(struct cb_resop), (xdrproc_t)cb_compound_resop);
if (!result) { CBX_ERR("compound_res.resarray"); goto out; }
out:
free(res->resarray);
free(res);
return result;
}

440
daemon/daemon_debug.c Normal file
View file

@ -0,0 +1,440 @@
/* 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 <windows.h>
#include <stdio.h>
#include "daemon_debug.h"
#include "from_kernel.h"
#include "nfs41_driver.h"
#include "nfs41_ops.h"
static int g_debug_level = DEFAULT_DEBUG_LEVEL;
void set_debug_level(int level) { g_debug_level = level; }
void dprintf(int level, LPCSTR format, ...)
{
if (level <= g_debug_level) {
va_list args;
va_start(args, format);
printf("%04x: ", GetCurrentThreadId());
vprintf(format, args);
va_end(args);
}
}
void eprintf(LPCSTR format, ...)
{
va_list args;
va_start(args, format);
fprintf(stderr, "%04x: ", GetCurrentThreadId());
vfprintf(stderr, format, args);
va_end(args);
}
void print_hexbuf(int level, unsigned char *title, unsigned char *buf, int len)
{
int j, k;
if (level > g_debug_level) return;
printf("%s", title);
for(j = 0, k = 0; j < len; j++, k++) {
printf("%02x '%c' ", buf[j], isascii(buf[j])? buf[j]:' ');
if (((k+1) % 10 == 0 && k > 0)) {
printf("\n");
}
}
printf("\n");
}
void print_create_attributes(int level, DWORD create_opts) {
if (level > g_debug_level) return;
printf("create attributes: ");
if (create_opts & FILE_DIRECTORY_FILE)
printf("DIRECTORY_FILE ");
if (create_opts & FILE_NON_DIRECTORY_FILE)
printf("NON_DIRECTORY_FILE ");
if (create_opts & FILE_WRITE_THROUGH)
printf("WRITE_THROUGH ");
if (create_opts & FILE_SEQUENTIAL_ONLY)
printf("SEQUENTIAL_ONLY ");
if (create_opts & FILE_RANDOM_ACCESS)
printf("RANDOM_ACCESS ");
if (create_opts & FILE_NO_INTERMEDIATE_BUFFERING)
printf("NO_INTERMEDIATE_BUFFERING ");
if (create_opts & FILE_SYNCHRONOUS_IO_ALERT)
printf("SYNCHRONOUS_IO_ALERT ");
if (create_opts & FILE_SYNCHRONOUS_IO_NONALERT)
printf("SYNCHRONOUS_IO_NONALERT ");
if (create_opts & FILE_CREATE_TREE_CONNECTION)
printf("CREATE_TREE_CONNECTION ");
if (create_opts & FILE_COMPLETE_IF_OPLOCKED)
printf("COMPLETE_IF_OPLOCKED ");
if (create_opts & FILE_NO_EA_KNOWLEDGE)
printf("NO_EA_KNOWLEDGE ");
if (create_opts & FILE_OPEN_REPARSE_POINT)
printf("OPEN_REPARSE_POINT ");
if (create_opts & FILE_DELETE_ON_CLOSE)
printf("DELETE_ON_CLOSE ");
if (create_opts & FILE_OPEN_BY_FILE_ID)
printf("OPEN_BY_FILE_ID ");
if (create_opts & FILE_OPEN_FOR_BACKUP_INTENT)
printf("OPEN_FOR_BACKUP_INTENT ");
if (create_opts & FILE_RESERVE_OPFILTER)
printf("RESERVE_OPFILTER");
printf("\n");
}
void print_disposition(int level, DWORD disposition) {
if (level > g_debug_level) return;
printf("userland disposition = ");
if (disposition == FILE_SUPERSEDE)
printf("FILE_SUPERSEDE\n");
else if (disposition == FILE_CREATE)
printf("FILE_CREATE\n");
else if (disposition == FILE_OPEN)
printf("FILE_OPEN\n");
else if (disposition == FILE_OPEN_IF)
printf("FILE_OPEN_IF\n");
else if (disposition == FILE_OVERWRITE)
printf("FILE_OVERWRITE\n");
else if (disposition == FILE_OVERWRITE_IF)
printf("FILE_OVERWRITE_IF\n");
}
void print_access_mask(int level, DWORD access_mask) {
if (level > g_debug_level) return;
printf("access mask: ");
if (access_mask & FILE_READ_DATA)
printf("READ ");
if (access_mask & STANDARD_RIGHTS_READ)
printf("READ_ACL ");
if (access_mask & FILE_READ_ATTRIBUTES)
printf("READ_ATTR ");
if (access_mask & FILE_READ_EA)
printf("READ_EA ");
if (access_mask & FILE_WRITE_DATA)
printf("WRITE ");
if (access_mask & STANDARD_RIGHTS_WRITE)
printf("WRITE_ACL ");
if (access_mask & FILE_WRITE_ATTRIBUTES)
printf("WRITE_ATTR ");
if (access_mask & FILE_WRITE_EA)
printf("WRITE_EA ");
if (access_mask & FILE_APPEND_DATA)
printf("APPEND ");
if (access_mask & FILE_EXECUTE)
printf("EXECUTE ");
if (access_mask & FILE_LIST_DIRECTORY)
printf("LIST ");
if (access_mask & FILE_TRAVERSE)
printf("TRAVERSE ");
if (access_mask & SYNCHRONIZE)
printf("SYNC ");
if (access_mask & FILE_DELETE_CHILD)
printf("DELETE_CHILD");
printf("\n");
}
void print_share_mode(int level, DWORD mode)
{
if (level > g_debug_level) return;
printf("share mode: ");
if (mode & FILE_SHARE_READ)
printf("READ ");
if (mode & FILE_SHARE_WRITE)
printf("WRITE ");
if (mode & FILE_SHARE_DELETE)
printf("DELETE");
printf("\n");
}
void print_file_id_both_dir_info(int level, FILE_ID_BOTH_DIR_INFO *pboth_dir_info)
{
if (level > g_debug_level) return;
printf("FILE_ID_BOTH_DIR_INFO %p %d\n", pboth_dir_info, sizeof(unsigned char *));
printf("\tNextEntryOffset=%ld %d %d\n", pboth_dir_info->NextEntryOffset, sizeof(pboth_dir_info->NextEntryOffset), sizeof(DWORD));
printf("\tFileIndex=%ld %d\n", pboth_dir_info->FileIndex, sizeof(pboth_dir_info->FileIndex));
printf("\tCreationTime=0x%x %d\n", pboth_dir_info->CreationTime.QuadPart, sizeof(pboth_dir_info->CreationTime));
printf("\tLastAccessTime=0x%x %d\n", pboth_dir_info->LastAccessTime.QuadPart, sizeof(pboth_dir_info->LastAccessTime));
printf("\tLastWriteTime=0x%x %d\n", pboth_dir_info->LastWriteTime.QuadPart, sizeof(pboth_dir_info->LastWriteTime));
printf("\tChangeTime=0x%x %d\n", pboth_dir_info->ChangeTime.QuadPart, sizeof(pboth_dir_info->ChangeTime));
printf("\tEndOfFile=0x%x %d\n", pboth_dir_info->EndOfFile.QuadPart, sizeof(pboth_dir_info->EndOfFile));
printf("\tAllocationSize=0x%x %d\n", pboth_dir_info->AllocationSize.QuadPart, sizeof(pboth_dir_info->AllocationSize));
printf("\tFileAttributes=%ld %d\n", pboth_dir_info->FileAttributes, sizeof(pboth_dir_info->FileAttributes));
printf("\tFileNameLength=%ld %d\n", pboth_dir_info->FileNameLength, sizeof(pboth_dir_info->FileNameLength));
printf("\tEaSize=%ld %d\n", pboth_dir_info->EaSize, sizeof(pboth_dir_info->EaSize));
printf("\tShortNameLength=%d %d\n", pboth_dir_info->ShortNameLength, sizeof(pboth_dir_info->ShortNameLength));
printf("\tShortName='%S' %d\n", pboth_dir_info->ShortName, sizeof(pboth_dir_info->ShortName));
printf("\tFileId=0x%x %d\n", pboth_dir_info->FileId.QuadPart, sizeof(pboth_dir_info->FileId));
printf("\tFileName='%S' %p\n", pboth_dir_info->FileName, pboth_dir_info->FileName);
}
void print_opcode(int level, DWORD opcode)
{
dprintf(level, (LPCSTR)opcode2string(opcode));
}
const char* opcode2string(DWORD opcode)
{
switch(opcode) {
case NFS41_SHUTDOWN: return "NFS41_SHUTDOWN";
case NFS41_MOUNT: return "NFS41_MOUNT";
case NFS41_UNMOUNT: return "NFS41_UNMOUNT";
case NFS41_OPEN: return "NFS41_OPEN";
case NFS41_CLOSE: return "NFS41_CLOSE";
case NFS41_READ: return "NFS41_READ";
case NFS41_WRITE: return "NFS41_WRITE";
case NFS41_LOCK: return "NFS41_LOCK";
case NFS41_UNLOCK: return "NFS41_UNLOCK";
case NFS41_DIR_QUERY: return "NFS41_DIR_QUERY";
case NFS41_FILE_QUERY: return "NFS41_FILE_QUERY";
case NFS41_FILE_SET: return "NFS41_FILE_SET";
case NFS41_EA_SET: return "NFS41_EA_SET";
case NFS41_VOLUME_QUERY: return "NFS41_VOLUME_QUERY";
default: return "UNKNOWN";
}
}
const char* nfs_opnum_to_string(int opnum)
{
switch (opnum)
{
case OP_ACCESS: return "ACCESS";
case OP_CLOSE: return "CLOSE";
case OP_COMMIT: return "COMMIT";
case OP_CREATE: return "CREATE";
case OP_DELEGPURGE: return "DELEGPURGE";
case OP_DELEGRETURN: return "DELEGRETURN";
case OP_GETATTR: return "GETATTR";
case OP_GETFH: return "GETFH";
case OP_LINK: return "LINK";
case OP_LOCK: return "LOCK";
case OP_LOCKT: return "LOCKT";
case OP_LOCKU: return "LOCKU";
case OP_LOOKUP: return "LOOKUP";
case OP_LOOKUPP: return "LOOKUPP";
case OP_NVERIFY: return "NVERIFY";
case OP_OPEN: return "OPEN";
case OP_OPENATTR: return "OPENATTR";
case OP_OPEN_CONFIRM: return "OPEN_CONFIRM";
case OP_OPEN_DOWNGRADE: return "OPEN_DOWNGRADE";
case OP_PUTFH: return "PUTFH";
case OP_PUTPUBFH: return "PUTPUBFH";
case OP_PUTROOTFH: return "PUTROOTFH";
case OP_READ: return "READ";
case OP_READDIR: return "READDIR";
case OP_READLINK: return "READLINK";
case OP_REMOVE: return "REMOVE";
case OP_RENAME: return "RENAME";
case OP_RENEW: return "RENEW";
case OP_RESTOREFH: return "RESTOREFH";
case OP_SAVEFH: return "SAVEFH";
case OP_SECINFO: return "SECINFO";
case OP_SETATTR: return "SETATTR";
case OP_SETCLIENTID: return "SETCLIENTID";
case OP_SETCLIENTID_CONFIRM: return "SETCLIENTID_CONFIRM";
case OP_VERIFY: return "VERIFY";
case OP_WRITE: return "WRITE";
case OP_RELEASE_LOCKOWNER: return "RELEASE_LOCKOWNER";
case OP_BACKCHANNEL_CTL: return "BACKCHANNEL_CTL";
case OP_BIND_CONN_TO_SESSION: return "BIND_CONN_TO_SESSION";
case OP_EXCHANGE_ID: return "EXCHANGE_ID";
case OP_CREATE_SESSION: return "CREATE_SESSION";
case OP_DESTROY_SESSION: return "DESTROY_SESSION";
case OP_FREE_STATEID: return "FREE_STATEID";
case OP_GET_DIR_DELEGATION: return "GET_DIR_DELEGATION";
case OP_GETDEVICEINFO: return "GETDEVICEINFO";
case OP_GETDEVICELIST: return "GETDEVICELIST";
case OP_LAYOUTCOMMIT: return "LAYOUTCOMMIT";
case OP_LAYOUTGET: return "LAYOUTGET";
case OP_LAYOUTRETURN: return "LAYOUTRETURN";
case OP_SECINFO_NO_NAME: return "SECINFO_NO_NAME";
case OP_SEQUENCE: return "SEQUENCE";
case OP_SET_SSV: return "SET_SSV";
case OP_TEST_STATEID: return "TEST_STATEID";
case OP_WANT_DELEGATION: return "WANT_DELEGATION";
case OP_DESTROY_CLIENTID: return "DESTROY_CLIENTID";
case OP_RECLAIM_COMPLETE: return "RECLAIM_COMPLETE";
case OP_ILLEGAL: return "ILLEGAL";
default: return "invalid nfs opnum";
}
}
const char* nfs_error_string(int status)
{
switch (status)
{
case NFS4_OK: return "NFS4_OK";
case NFS4ERR_PERM: return "NFS4ERR_PERM";
case NFS4ERR_NOENT: return "NFS4ERR_NOENT";
case NFS4ERR_IO: return "NFS4ERR_IO";
case NFS4ERR_NXIO: return "NFS4ERR_NXIO";
case NFS4ERR_ACCESS: return "NFS4ERR_ACCESS";
case NFS4ERR_EXIST: return "NFS4ERR_EXIST";
case NFS4ERR_XDEV: return "NFS4ERR_XDEV";
case NFS4ERR_NOTDIR: return "NFS4ERR_NOTDIR";
case NFS4ERR_ISDIR: return "NFS4ERR_ISDIR";
case NFS4ERR_INVAL: return "NFS4ERR_INVAL";
case NFS4ERR_FBIG: return "NFS4ERR_FBIG";
case NFS4ERR_NOSPC: return "NFS4ERR_NOSPC";
case NFS4ERR_ROFS: return "NFS4ERR_ROFS";
case NFS4ERR_MLINK: return "NFS4ERR_MLINK";
case NFS4ERR_NAMETOOLONG: return "NFS4ERR_NAMETOOLONG";
case NFS4ERR_NOTEMPTY: return "NFS4ERR_NOTEMPTY";
case NFS4ERR_DQUOT: return "NFS4ERR_DQUOT";
case NFS4ERR_STALE: return "NFS4ERR_STALE";
case NFS4ERR_BADHANDLE: return "NFS4ERR_BADHANDLE";
case NFS4ERR_BAD_COOKIE: return "NFS4ERR_BAD_COOKIE";
case NFS4ERR_NOTSUPP: return "NFS4ERR_NOTSUPP";
case NFS4ERR_TOOSMALL: return "NFS4ERR_TOOSMALL";
case NFS4ERR_SERVERFAULT: return "NFS4ERR_SERVERFAULT";
case NFS4ERR_BADTYPE: return "NFS4ERR_BADTYPE";
case NFS4ERR_DELAY: return "NFS4ERR_DELAY";
case NFS4ERR_SAME: return "NFS4ERR_SAME";
case NFS4ERR_DENIED: return "NFS4ERR_DENIED";
case NFS4ERR_EXPIRED: return "NFS4ERR_EXPIRED";
case NFS4ERR_LOCKED: return "NFS4ERR_LOCKED";
case NFS4ERR_GRACE: return "NFS4ERR_GRACE";
case NFS4ERR_FHEXPIRED: return "NFS4ERR_FHEXPIRED";
case NFS4ERR_SHARE_DENIED: return "NFS4ERR_SHARE_DENIED";
case NFS4ERR_WRONGSEC: return "NFS4ERR_WRONGSEC";
case NFS4ERR_CLID_INUSE: return "NFS4ERR_CLID_INUSE";
case NFS4ERR_RESOURCE: return "NFS4ERR_RESOURCE";
case NFS4ERR_MOVED: return "NFS4ERR_MOVED";
case NFS4ERR_NOFILEHANDLE: return "NFS4ERR_NOFILEHANDLE";
case NFS4ERR_MINOR_VERS_MISMATCH: return "NFS4ERR_MINOR_VERS_MISMATCH";
case NFS4ERR_STALE_CLIENTID: return "NFS4ERR_STALE_CLIENTID";
case NFS4ERR_STALE_STATEID: return "NFS4ERR_STALE_STATEID";
case NFS4ERR_OLD_STATEID: return "NFS4ERR_OLD_STATEID";
case NFS4ERR_BAD_STATEID: return "NFS4ERR_BAD_STATEID";
case NFS4ERR_BAD_SEQID: return "NFS4ERR_BAD_SEQID";
case NFS4ERR_NOT_SAME: return "NFS4ERR_NOT_SAME";
case NFS4ERR_LOCK_RANGE: return "NFS4ERR_LOCK_RANGE";
case NFS4ERR_SYMLINK: return "NFS4ERR_SYMLINK";
case NFS4ERR_RESTOREFH: return "NFS4ERR_RESTOREFH";
case NFS4ERR_LEASE_MOVED: return "NFS4ERR_LEASE_MOVED";
case NFS4ERR_ATTRNOTSUPP: return "NFS4ERR_ATTRNOTSUPP";
case NFS4ERR_NO_GRACE: return "NFS4ERR_NO_GRACE";
case NFS4ERR_RECLAIM_BAD: return "NFS4ERR_RECLAIM_BAD";
case NFS4ERR_RECLAIM_CONFLICT: return "NFS4ERR_RECLAIM_CONFLICT";
case NFS4ERR_BADXDR: return "NFS4ERR_BADXDR";
case NFS4ERR_LOCKS_HELD: return "NFS4ERR_LOCKS_HELD";
case NFS4ERR_OPENMODE: return "NFS4ERR_OPENMODE";
case NFS4ERR_BADOWNER: return "NFS4ERR_BADOWNER";
case NFS4ERR_BADCHAR: return "NFS4ERR_BADCHAR";
case NFS4ERR_BADNAME: return "NFS4ERR_BADNAME";
case NFS4ERR_BAD_RANGE: return "NFS4ERR_BAD_RANGE";
case NFS4ERR_LOCK_NOTSUPP: return "NFS4ERR_LOCK_NOTSUPP";
case NFS4ERR_OP_ILLEGAL: return "NFS4ERR_OP_ILLEGAL";
case NFS4ERR_DEADLOCK: return "NFS4ERR_DEADLOCK";
case NFS4ERR_FILE_OPEN: return "NFS4ERR_FILE_OPEN";
case NFS4ERR_ADMIN_REVOKED: return "NFS4ERR_ADMIN_REVOKED";
case NFS4ERR_CB_PATH_DOWN: return "NFS4ERR_CB_PATH_DOWN";
case NFS4ERR_BADIOMODE: return "NFS4ERR_BADIOMODE";
case NFS4ERR_BADLAYOUT: return "NFS4ERR_BADLAYOUT";
case NFS4ERR_BAD_SESSION_DIGEST: return "NFS4ERR_BAD_SESSION_DIGEST";
case NFS4ERR_BADSESSION: return "NFS4ERR_BADSESSION";
case NFS4ERR_BADSLOT: return "NFS4ERR_BADSLOT";
case NFS4ERR_COMPLETE_ALREADY: return "NFS4ERR_COMPLETE_ALREADY";
case NFS4ERR_CONN_NOT_BOUND_TO_SESSION: return "NFS4ERR_CONN_NOT_BOUND_TO_SESSION";
case NFS4ERR_DELEG_ALREADY_WANTED: return "NFS4ERR_DELEG_ALREADY_WANTED";
case NFS4ERR_BACK_CHAN_BUSY: return "NFS4ERR_BACK_CHAN_BUSY";
case NFS4ERR_LAYOUTTRYLATER: return "NFS4ERR_LAYOUTTRYLATER";
case NFS4ERR_LAYOUTUNAVAILABLE: return "NFS4ERR_LAYOUTUNAVAILABLE";
case NFS4ERR_NOMATCHING_LAYOUT: return "NFS4ERR_NOMATCHING_LAYOUT";
case NFS4ERR_RECALLCONFLICT: return "NFS4ERR_RECALLCONFLICT";
case NFS4ERR_UNKNOWN_LAYOUTTYPE: return "NFS4ERR_UNKNOWN_LAYOUTTYPE";
case NFS4ERR_SEQ_MISORDERED: return "NFS4ERR_SEQ_MISORDERED";
case NFS4ERR_SEQUENCE_POS: return "NFS4ERR_SEQUENCE_POS";
case NFS4ERR_REQ_TOO_BIG: return "NFS4ERR_REQ_TOO_BIG";
case NFS4ERR_REP_TOO_BIG: return "NFS4ERR_REP_TOO_BIG";
case NFS4ERR_REP_TOO_BIG_TO_CACHE: return "NFS4ERR_REP_TOO_BIG_TO_CACHE";
case NFS4ERR_RETRY_UNCACHED_REP: return "NFS4ERR_RETRY_UNCACHED_REP";
case NFS4ERR_UNSAFE_COMPOUND: return "NFS4ERR_UNSAFE_COMPOUND";
case NFS4ERR_TOO_MANY_OPS: return "NFS4ERR_TOO_MANY_OPS";
case NFS4ERR_OP_NOT_IN_SESSION: return "NFS4ERR_OP_NOT_IN_SESSION";
case NFS4ERR_HASH_ALG_UNSUPP: return "NFS4ERR_HASH_ALG_UNSUPP";
case NFS4ERR_CLIENTID_BUSY: return "NFS4ERR_CLIENTID_BUSY";
case NFS4ERR_PNFS_IO_HOLE: return "NFS4ERR_PNFS_IO_HOLE";
case NFS4ERR_SEQ_FALSE_RETRY: return "NFS4ERR_SEQ_FALSE_RETRY";
case NFS4ERR_BAD_HIGH_SLOT: return "NFS4ERR_BAD_HIGH_SLOT";
case NFS4ERR_DEADSESSION: return "NFS4ERR_DEADSESSION";
case NFS4ERR_ENCR_ALG_UNSUPP: return "NFS4ERR_ENCR_ALG_UNSUPP";
case NFS4ERR_PNFS_NO_LAYOUT: return "NFS4ERR_PNFS_NO_LAYOUT";
case NFS4ERR_NOT_ONLY_OP: return "NFS4ERR_NOT_ONLY_OP";
case NFS4ERR_WRONG_CRED: return "NFS4ERR_WRONG_CRED";
case NFS4ERR_WRONG_TYPE: return "NFS4ERR_WRONG_TYPE";
case NFS4ERR_DIRDELEG_UNAVAIL: return "NFS4ERR_DIRDELEG_UNAVAIL";
case NFS4ERR_REJECT_DELEG: return "NFS4ERR_REJECT_DELEG";
case NFS4ERR_RETURNCONFLICT: return "NFS4ERR_RETURNCONFLICT";
case NFS4ERR_DELEG_REVOKED: return "NFS4ERR_DELEG_REVOKED";
default: return "invalid nfs error code";
}
}
void print_condwait_status(int level, int status)
{
if (level > g_debug_level) return;
switch(status) {
case WAIT_ABANDONED: printf("WAIT_ABANDONED\n"); break;
case WAIT_OBJECT_0: printf("WAIT_OBJECT_0\n"); break;
case WAIT_TIMEOUT: printf("WAIT_TIMEOUT\n"); break;
case WAIT_FAILED: printf("WAIT_FAILED %d\n", GetLastError());
default: printf("unknown status =%d\n", status);
}
}
void print_sr_status_flags(int level, int flags)
{
if (level > g_debug_level) return;
printf("%04x: sr_status_flags: ", GetCurrentThreadId());
if (flags & SEQ4_STATUS_CB_PATH_DOWN)
printf("SEQ4_STATUS_CB_PATH_DOWN ");
if (flags & SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRING)
printf("SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRING ");
if (flags & SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRED)
printf("SEQ4_STATUS_CB_GSS_CONTEXTS_EXPIRED ");
if (flags & SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED)
printf("SEQ4_STATUS_EXPIRED_ALL_STATE_REVOKED ");
if (flags & SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED)
printf("SEQ4_STATUS_EXPIRED_SOME_STATE_REVOKED ");
if (flags & SEQ4_STATUS_ADMIN_STATE_REVOKED)
printf("SEQ4_STATUS_ADMIN_STATE_REVOKED ");
if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)
printf("SEQ4_STATUS_RECALLABLE_STATE_REVOKED ");
if (flags & SEQ4_STATUS_LEASE_MOVED)
printf("SEQ4_STATUS_LEASE_MOVED ");
if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED)
printf("SEQ4_STATUS_RESTART_RECLAIM_NEEDED ");
if (flags & SEQ4_STATUS_CB_PATH_DOWN_SESSION)
printf("SEQ4_STATUS_CB_PATH_DOWN_SESSION ");
if (flags & SEQ4_STATUS_BACKCHANNEL_FAULT)
printf("SEQ4_STATUS_BACKCHANNEL_FAULT ");
if (flags & SEQ4_STATUS_DEVID_CHANGED)
printf("SEQ4_STATUS_DEVID_CHANGED ");
if (flags & SEQ4_STATUS_DEVID_DELETED)
printf("SEQ4_STATUS_DEVID_DELETED ");
printf("\n");
}

73
daemon/daemon_debug.h Normal file
View file

@ -0,0 +1,73 @@
/* 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 _DAEMON_DEBUG_
#define _DAEMON_DEBUG_
#ifdef _DEBUG
/* use visual studio's debug heap */
# define _CRTDBG_MAP_ALLOC
# include <stdlib.h>
# include <crtdbg.h>
#else
# include <stdlib.h>
#endif
#define DEFAULT_DEBUG_LEVEL 1
/* daemon_debug.h */
void set_debug_level(int level);
void dprintf(int level, LPCSTR format, ...);
void eprintf(LPCSTR format, ...);
void print_hexbuf(int level, unsigned char *title, unsigned char *buf, int len);
void print_create_attributes(int level, DWORD create_opts);
void print_disposition(int level, DWORD disposition);
void print_access_mask(int level, DWORD access_mask);
void print_share_mode(int level, DWORD mode);
void print_file_id_both_dir_info(int level, FILE_ID_BOTH_DIR_INFO *p);
void print_opcode(int level, DWORD opcode);
const char* opcode2string(DWORD opcode);
const char* nfs_opnum_to_string(int opnum);
const char* nfs_error_string(int status);
void print_condwait_status(int level, int status);
void print_sr_status_flags(int level, int flags);
/* pnfs_debug.c */
enum pnfs_status;
enum pnfs_layout_type;
enum pnfs_iomode;
struct __pnfs_file_layout;
struct __pnfs_file_device;
const char* pnfs_error_string(enum pnfs_status status);
const char* pnfs_layout_type_string(enum pnfs_layout_type type);
const char* pnfs_iomode_string(enum pnfs_iomode iomode);
void dprint_deviceid(int level, const char *title, const unsigned char *deviceid);
void dprint_layout(int level, const struct __pnfs_file_layout *layout);
void dprint_device(int level, const struct __pnfs_file_device *device);
#endif

195
daemon/from_kernel.h Normal file
View file

@ -0,0 +1,195 @@
/* 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 _NFS41_DAEMON_
#define _NFS41_DAEMON_
#define FILE_DIRECTORY_FILE 0x00000001
#define FILE_WRITE_THROUGH 0x00000002
#define FILE_SEQUENTIAL_ONLY 0x00000004
#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
#define FILE_NON_DIRECTORY_FILE 0x00000040
#define FILE_CREATE_TREE_CONNECTION 0x00000080
#define FILE_COMPLETE_IF_OPLOCKED 0x00000100
#define FILE_NO_EA_KNOWLEDGE 0x00000200
#define FILE_OPEN_REMOTE_INSTANCE 0x00000400
#define FILE_RANDOM_ACCESS 0x00000800
#define FILE_DELETE_ON_CLOSE 0x00001000
#define FILE_OPEN_BY_FILE_ID 0x00002000
#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
#define FILE_NO_COMPRESSION 0x00008000
#define FILE_RESERVE_OPFILTER 0x00100000
#define FILE_OPEN_REPARSE_POINT 0x00200000
#define FILE_OPEN_NO_RECALL 0x00400000
#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
#define FILE_COPY_STRUCTURED_STORAGE 0x00000041
#define FILE_STRUCTURED_STORAGE 0x00000441
#define FILE_SUPERSEDE 0x00000000
#define FILE_OPEN 0x00000001
#define FILE_CREATE 0x00000002
#define FILE_OPEN_IF 0x00000003
#define FILE_OVERWRITE 0x00000004
#define FILE_OVERWRITE_IF 0x00000005
#define FILE_MAXIMUM_DISPOSITION 0x00000005
typedef enum _FILE_INFORMATION_CLASS {
FileDirectoryInformation = 1,
FileFullDirectoryInformation, // 2
FileBothDirectoryInformation, // 3
FileBasicInformation, // 4
FileStandardInformation, // 5
FileInternalInformation, // 6
FileEaInformation, // 7
FileAccessInformation, // 8
FileNameInformation, // 9
FileRenameInformation, // 10
FileLinkInformation, // 11
FileNamesInformation, // 12
FileDispositionInformation, // 13
FilePositionInformation, // 14
FileFullEaInformation, // 15
FileModeInformation, // 16
FileAlignmentInformation, // 17
FileAllInformation, // 18
FileAllocationInformation, // 19
FileEndOfFileInformation, // 20
FileAlternateNameInformation, // 21
FileStreamInformation, // 22
FilePipeInformation, // 23
FilePipeLocalInformation, // 24
FilePipeRemoteInformation, // 25
FileMailslotQueryInformation, // 26
FileMailslotSetInformation, // 27
FileCompressionInformation, // 28
FileObjectIdInformation, // 29
FileCompletionInformation, // 30
FileMoveClusterInformation, // 31
FileQuotaInformation, // 32
FileReparsePointInformation, // 33
FileNetworkOpenInformation, // 34
FileAttributeTagInformation, // 35
FileTrackingInformation, // 36
FileIdBothDirectoryInformation, // 37
FileIdFullDirectoryInformation, // 38
FileValidDataLengthInformation, // 39
FileShortNameInformation, // 40
FileIoCompletionNotificationInformation, // 41
FileIoStatusBlockRangeInformation, // 42
FileIoPriorityHintInformation, // 43
FileSfioReserveInformation, // 44
FileSfioVolumeInformation, // 45
FileHardLinkInformation, // 46
FileProcessIdsUsingFileInformation, // 47
FileNormalizedNameInformation, // 48
FileNetworkPhysicalNameInformation, // 49
FileIdGlobalTxDirectoryInformation, // 50
FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
/* kernel structures for QueryDirectory results */
typedef struct _FILE_NAMES_INFORMATION {
ULONG NextEntryOffset;
ULONG FileIndex;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION;
typedef struct _FILE_DIRECTORY_INFO {
ULONG NextEntryOffset;
ULONG FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_DIRECTORY_INFO, *PFILE_DIRECTORY_INFO;
typedef struct _FILE_BOTH_DIR_INFORMATION {
ULONG NextEntryOffset;
ULONG FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
ULONG EaSize;
CCHAR ShortNameLength;
WCHAR ShortName[12];
WCHAR FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
typedef struct _FILE_FULL_DIR_INFO {
ULONG NextEntryOffset;
ULONG FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
ULONG EaSize;
WCHAR FileName[1];
} FILE_FULL_DIR_INFO, *PFILE_FULL_DIR_INFO;
typedef struct _FILE_ID_FULL_DIR_INFO {
ULONG NextEntryOffset;
ULONG FileIndex;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
ULONG EaSize;
LARGE_INTEGER FileId;
WCHAR FileName[1];
} FILE_ID_FULL_DIR_INFO, *PFILE_ID_FULL_DIR_INFO;
typedef struct _FILE_LINK_INFORMATION {
BOOLEAN ReplaceIfExists;
HANDLE RootDirectory;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION;
#endif

152
daemon/getattr.c Normal file
View file

@ -0,0 +1,152 @@
/* 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 <Windows.h>
#include <stdio.h>
#include "from_kernel.h"
#include "daemon_debug.h"
#include "nfs41_ops.h"
#include "name_cache.h"
#include "upcall.h"
#include "util.h"
int nfs41_cached_getattr(
IN nfs41_session *session,
IN nfs41_path_fh *file,
OUT nfs41_file_info *info)
{
int status;
/* first look for cached attributes */
status = nfs41_attr_cache_lookup(session_name_cache(session),
file->fh.fileid, info);
if (status) {
/* fetch attributes from the server */
bitmap4 attr_request;
init_getattr_request(&attr_request);
status = nfs41_getattr(session, file, &attr_request, info);
if (status) {
eprintf("nfs41_getattr() failed with %s\n",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
}
}
return status;
}
int parse_getattr(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
int status;
getattr_upcall_args *args = &upcall->args.getattr;
status = safe_read(&buffer, &length, &args->query_class, sizeof(args->query_class));
if (status) goto out;
status = safe_read(&buffer, &length, &args->buf_len, sizeof(args->buf_len));
if (status) goto out;
status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE));
if (status) goto out;
status = safe_read(&buffer, &length, &args->state, sizeof(args->state));
out:
if (status)
eprintf("parsing NFS41_FILE_QUERY failed with %d\n", status);
else
dprintf(1, "parsing NFS41_FILE_QUERY: info_class=%d buf_len=%d "
"root=0x%p open_state=0x%p\n",
args->query_class, args->buf_len, args->root, args->state);
return status;
}
int handle_getattr(nfs41_upcall *upcall)
{
int status;
getattr_upcall_args *args = &upcall->args.getattr;
nfs41_open_state *state = args->state;
nfs41_file_info info;
ZeroMemory(&info, sizeof(info));
status = nfs41_cached_getattr(state->session, &state->file, &info);
if (status) {
eprintf("nfs41_cached_getattr() failed with %d\n", status);
goto out;
}
switch (args->query_class) {
case FileBasicInformation:
nfs_to_basic_info(&info, &args->basic_info);
break;
case FileStandardInformation:
nfs_to_standard_info(&info, &args->std_info);
break;
case FileAttributeTagInformation:
args->tag_info.FileAttributes = nfs_file_info_to_attributes(&info);
args->tag_info.ReparseTag = 0;
break;
default:
eprintf("unhandled file query class %d\n", args->query_class);
status = ERROR_INVALID_PARAMETER;
break;
}
out:
return status;
}
int marshall_getattr(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
{
int status;
getattr_upcall_args *args = &upcall->args.getattr;
uint32_t info_len;
switch (args->query_class) {
case FileBasicInformation:
info_len = sizeof(args->basic_info);
status = safe_write(&buffer, length, &info_len, sizeof(info_len));
if (status) goto out;
status = safe_write(&buffer, length, &args->basic_info, info_len);
if (status) goto out;
break;
case FileStandardInformation:
info_len = sizeof(args->std_info);
status = safe_write(&buffer, length, &info_len, sizeof(info_len));
if (status) goto out;
status = safe_write(&buffer, length, &args->std_info, info_len);
if (status) goto out;
break;
case FileAttributeTagInformation:
info_len = sizeof(args->tag_info);
status = safe_write(&buffer, length, &info_len, sizeof(info_len));
if (status) goto out;
status = safe_write(&buffer, length, &args->tag_info, info_len);
if (status) goto out;
break;
default:
eprintf("unknown file query class %d\n", args->query_class);
status = 103;
goto out;
}
out:
return status;
}

116
daemon/list.h Normal file
View file

@ -0,0 +1,116 @@
/* 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 NFS41_LIST_H
#define NFS41_LIST_H
/* doubly-linked list */
struct list_entry {
struct list_entry *prev;
struct list_entry *next;
};
#define list_container(entry, type, field) \
((type*)((const char*)(entry) - (const char*)(&((type*)0)->field)))
#define list_for_each(entry, head) \
for (entry = (head)->next; entry != (head); entry = entry->next)
#define list_for_each_tmp(entry, tmp, head) \
for (entry = (head)->next, tmp = entry->next; entry != (head); \
entry = tmp, tmp = entry->next)
#define list_for_each_reverse(entry, head) \
for (entry = (head)->prev; entry != (head); entry = entry->prev)
#define list_for_each_reverse_tmp(entry, tmp, head) \
for (entry = (head)->next, tmp = entry->next; entry != (head); \
entry = tmp, tmp = entry->next)
static void list_init(
struct list_entry *head)
{
head->prev = head;
head->next = head;
}
static int list_empty(
struct list_entry *head)
{
return head->next == head;
}
static void list_add(
struct list_entry *entry,
struct list_entry *prev,
struct list_entry *next)
{
/* assert(prev->next == next && next->prev == prev); */
entry->prev = prev;
entry->next = next;
prev->next = entry;
next->prev = entry;
}
static void list_add_head(
struct list_entry *head,
struct list_entry *entry)
{
list_add(entry, head, head->next);
}
static void list_add_tail(
struct list_entry *head,
struct list_entry *entry)
{
list_add(entry, head->prev, head);
}
static void list_remove(
struct list_entry *entry)
{
if (!list_empty(entry)) {
entry->next->prev = entry->prev;
entry->prev->next = entry->next;
list_init(entry);
}
}
typedef int (*list_compare_fn)(const struct list_entry*, const void*);
static struct list_entry* list_search(
const struct list_entry *head,
const void *value,
list_compare_fn compare)
{
struct list_entry *entry;
list_for_each(entry, head)
if (compare(entry, value) == 0)
return entry;
return NULL;
}
#endif /* !NFS41_LIST_H */

252
daemon/lock.c Normal file
View file

@ -0,0 +1,252 @@
/* 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 <Windows.h>
#include <stdio.h>
#include "daemon_debug.h"
#include "nfs41_ops.h"
#include "upcall.h"
#include "util.h"
#define LKLVL 2 /* dprintf level for lock logging */
stateid4* nfs41_lock_stateid_copy(
IN nfs41_lock_state *lock_state,
IN OUT stateid4 *dest)
{
stateid4 *result;
AcquireSRWLockShared(&lock_state->lock);
if (lock_state->initialized) {
memcpy(dest, &lock_state->stateid, sizeof(stateid4));
result = dest;
dprintf(LKLVL, "nfs41_lock_stateid_copy: copying existing stateid "
"with seqid=%u\n", result->seqid);
} else {
result = NULL;
dprintf(LKLVL, "nfs41_lock_stateid_copy: no existing lock state\n");
}
ReleaseSRWLockShared(&lock_state->lock);
return result;
}
static void update_last_lock_state(
OUT nfs41_lock_state *lock_state,
IN stateid4 *stateid)
{
/* update the lock state if the seqid is more recent */
AcquireSRWLockShared(&lock_state->lock);
if (stateid->seqid > lock_state->stateid.seqid) {
ReleaseSRWLockShared(&lock_state->lock);
AcquireSRWLockExclusive(&lock_state->lock);
if (stateid->seqid > lock_state->stateid.seqid) {
if (lock_state->initialized) {
/* if the lock state already existed, update the seqid only;
* assume that stateid->other remains unchanged */
dprintf(LKLVL, "update_last_lock_state: setting seqid=%u "
"(was %u)\n", stateid->seqid, lock_state->stateid.seqid);
lock_state->stateid.seqid = stateid->seqid;
} else {
/* copy the entire stateid and mark as initialized */
dprintf(LKLVL, "update_last_lock_state: stateid "
"initialized with seqid=%u\n", stateid->seqid);
memcpy(&lock_state->stateid, stateid, sizeof(stateid4));
lock_state->initialized = 1;
}
}
ReleaseSRWLockExclusive(&lock_state->lock);
} else {
dprintf(LKLVL, "update_last_lock_state: discarding seqid=%u "
"(already %u)\n", stateid->seqid, lock_state->stateid.seqid);
ReleaseSRWLockShared(&lock_state->lock);
}
}
/* NFS41_LOCK */
int parse_lock(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
int status;
lock_upcall_args *args = &upcall->args.lock;
status = safe_read(&buffer, &length, &args->state, sizeof(HANDLE));
if (status) goto out;
status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE));
if (status) goto out;
status = safe_read(&buffer, &length, &args->offset, sizeof(LONGLONG));
if (status) goto out;
status = safe_read(&buffer, &length, &args->length, sizeof(LONGLONG));
if (status) goto out;
status = safe_read(&buffer, &length, &args->exclusive, sizeof(BOOLEAN));
if (status) goto out;
status = safe_read(&buffer, &length, &args->blocking, sizeof(BOOLEAN));
if (status) goto out;
out:
if (status)
eprintf("parsing NFS41_LOCK failed with %d\n", status);
else
dprintf(1, "parsing NFS41_LOCK: state=%p root=%p offset=0x%llx "
"length=0x%llx exclusive=%u blocking=%u\n",
args->state, args->root, args->offset, args->length,
args->exclusive, args->blocking);
return status;
}
static __inline uint32_t get_lock_type(BOOLEAN exclusive, BOOLEAN blocking)
{
return blocking == 0
? ( exclusive == 0 ? READ_LT : WRITE_LT )
: ( exclusive == 0 ? READW_LT : WRITEW_LT );
}
int handle_lock(nfs41_upcall *upcall)
{
int status;
lock_upcall_args *args = &upcall->args.lock;
nfs41_open_state *state = args->state;
const uint32_t type = get_lock_type(args->exclusive, args->blocking);
stateid4 stateid, *prev_stateid;
prev_stateid = nfs41_lock_stateid_copy(&state->last_lock, &stateid);
status = nfs41_lock(state->session, state, prev_stateid,
type, args->offset, args->length, &stateid);
if (status) {
dprintf(LKLVL, "nfs41_lock failed with %s\n",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
goto out;
}
update_last_lock_state(&state->last_lock, &stateid);
out:
return status;
}
int marshall_lock(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
return NO_ERROR;
}
int cancel_lock(IN nfs41_upcall *upcall)
{
int status = NO_ERROR;
lock_upcall_args *args = &upcall->args.lock;
nfs41_open_state *state = args->state;
stateid4 stateid, *prev_stateid;
dprintf(1, "--> cancel_lock()\n");
if (upcall->status)
goto out;
prev_stateid = nfs41_lock_stateid_copy(&state->last_lock, &stateid);
status = nfs41_unlock(state->session, state,
prev_stateid, args->offset, args->length);
if (status) {
dprintf(LKLVL, "cancel_lock: nfs41_unlock() failed with %s\n",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
goto out;
}
update_last_lock_state(&state->last_lock, &stateid);
out:
dprintf(1, "<-- cancel_lock() returning %d\n", status);
return status;
}
/* NFS41_UNLOCK */
int parse_unlock(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
int status;
unlock_upcall_args *args = &upcall->args.unlock;
status = safe_read(&buffer, &length, &args->state, sizeof(HANDLE));
if (status) goto out;
status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE));
if (status) goto out;
status = safe_read(&buffer, &length, &args->count, sizeof(ULONG));
if (status) goto out;
args->buf = buffer;
args->buf_len = length;
out:
if (status)
eprintf("parsing NFS41_UNLOCK failed with %d\n", status);
else
dprintf(1, "parsing NFS41_UNLOCK: state=%p root=%p count=%u\n",
args->state, args->root, args->count);
return status;
}
int handle_unlock(nfs41_upcall *upcall)
{
int status;
unlock_upcall_args *args = &upcall->args.unlock;
nfs41_open_state *state = args->state;
stateid4 stateid;
uint32_t i, nsuccess = 0;
unsigned char *buf = args->buf;
uint32_t buf_len = args->buf_len;
uint64_t offset;
uint64_t length;
if (nfs41_lock_stateid_copy(&state->last_lock, &stateid) == NULL) {
eprintf("attempt to unlock a file with no lock state\n");
status = ERROR_NOT_LOCKED;
goto out;
}
status = NO_ERROR;
for (i = 0; i < args->count; i++) {
if (safe_read(&buf, &buf_len, &offset, sizeof(LONGLONG))) break;
if (safe_read(&buf, &buf_len, &length, sizeof(LONGLONG))) break;
status = nfs41_unlock(state->session, state, &stateid, offset, length);
if (status == NFS4_OK) {
nsuccess++;
} else {
dprintf(LKLVL, "nfs41_unlock failed with %s\n",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
}
}
if (nsuccess) {
update_last_lock_state(&state->last_lock, &stateid);
status = NO_ERROR;
}
out:
return status;
}
int marshall_unlock(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
return NO_ERROR;
}

500
daemon/lookup.c Normal file
View file

@ -0,0 +1,500 @@
/* 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 <Windows.h>
#include <strsafe.h>
#include <time.h>
#include "nfs41_compound.h"
#include "nfs41_ops.h"
#include "name_cache.h"
#include "util.h"
#include "daemon_debug.h"
#define LULVL 2 /* dprintf level for lookup logging */
#define MAX_LOOKUP_COMPONENTS 8
/* calculate how much space to allocate for rpc reply */
#define MAX_LOOKUP_RES_SIZE 4
#define MAX_GETFH_RES_SIZE (NFS4_FHSIZE + 8)
#define MAX_GETATTR_RES_SIZE 128 /* depends on what we request */
#define MAX_COMPONENT_RES_SIZE \
(MAX_LOOKUP_RES_SIZE + MAX_GETFH_RES_SIZE + MAX_GETATTR_RES_SIZE)
#define MAX_RPC_RES_SIZE(num_components) \
(MAX_COMPONENT_RES_SIZE * ((num_components) + 1))
/* map NFS4ERR_MOVED to an arbitrary windows error */
#define ERROR_FILESYSTEM_ABSENT ERROR_DEVICE_REMOVED
struct lookup_referral {
nfs41_path_fh parent;
nfs41_component name;
};
typedef struct __nfs41_lookup_component_args {
nfs41_sequence_args sequence;
nfs41_putfh_args putfh;
nfs41_lookup_args lookup[MAX_LOOKUP_COMPONENTS];
nfs41_getattr_args getrootattr;
nfs41_getattr_args getattr[MAX_LOOKUP_COMPONENTS];
bitmap4 attr_request;
} nfs41_lookup_component_args;
typedef struct __nfs41_lookup_component_res {
nfs41_sequence_res sequence;
nfs41_putfh_res putfh;
nfs41_lookup_res lookup[MAX_LOOKUP_COMPONENTS];
nfs41_getfh_res getrootfh;
nfs41_getfh_res getfh[MAX_LOOKUP_COMPONENTS];
nfs41_path_fh root;
nfs41_path_fh file[MAX_LOOKUP_COMPONENTS];
nfs41_getattr_res getrootattr;
nfs41_getattr_res getattr[MAX_LOOKUP_COMPONENTS];
nfs41_file_info rootinfo;
nfs41_file_info info[MAX_LOOKUP_COMPONENTS];
struct lookup_referral *referral;
} nfs41_lookup_component_res;
static void init_component_args(
IN nfs41_lookup_component_args *args,
IN nfs41_lookup_component_res *res,
IN nfs41_abs_path *path,
IN struct lookup_referral *referral)
{
uint32_t i;
ZeroMemory(args, sizeof(nfs41_lookup_component_args));
ZeroMemory(res, sizeof(nfs41_lookup_component_res));
args->attr_request.count = 2;
args->attr_request.arr[0] = FATTR4_WORD0_TYPE
| FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE
| FATTR4_WORD0_FSID | FATTR4_WORD0_FILEID;
args->attr_request.arr[1] = FATTR4_WORD1_MODE
| FATTR4_WORD1_NUMLINKS | FATTR4_WORD1_TIME_ACCESS
| FATTR4_WORD1_TIME_CREATE | FATTR4_WORD1_TIME_MODIFY;
args->getrootattr.attr_request = &args->attr_request;
res->root.path = path;
res->getrootfh.fh = &res->root.fh;
res->getrootattr.info = &res->rootinfo;
res->getrootattr.obj_attributes.attr_vals_len = NFS4_OPAQUE_LIMIT;
res->referral = referral;
for (i = 0; i < MAX_LOOKUP_COMPONENTS; i++) {
args->getattr[i].attr_request = &args->attr_request;
res->file[i].path = path;
args->lookup[i].name = &res->file[i].name;
res->getfh[i].fh = &res->file[i].fh;
res->getattr[i].info = &res->info[i];
res->getattr[i].obj_attributes.attr_vals_len = NFS4_OPAQUE_LIMIT;
}
}
static int lookup_rpc(
IN nfs41_session *session,
IN nfs41_path_fh *dir,
IN uint32_t component_count,
IN nfs41_lookup_component_args *args,
OUT nfs41_lookup_component_res *res)
{
int status;
uint32_t i, buffer_size;
nfs41_compound compound;
nfs_argop4 argops[4+MAX_LOOKUP_COMPONENTS*3];
nfs_resop4 resops[4+MAX_LOOKUP_COMPONENTS*3];
compound_init(&compound, argops, resops);
compound_add_op(&compound, OP_SEQUENCE, &args->sequence, &res->sequence);
status = nfs41_session_sequence(&args->sequence, session, 0);
if (status)
goto out;
if (dir == &res->root) {
compound_add_op(&compound, OP_PUTROOTFH,
NULL, &res->putfh);
compound_add_op(&compound, OP_GETFH,
NULL, &res->getrootfh);
compound_add_op(&compound, OP_GETATTR,
&args->getrootattr, &res->getrootattr);
} else {
args->putfh.file = dir;
compound_add_op(&compound, OP_PUTFH,
&args->putfh, &res->putfh);
}
for (i = 0; i < component_count; i++) {
compound_add_op(&compound, OP_LOOKUP,
&args->lookup[i], &res->lookup[i]);
compound_add_op(&compound, OP_GETFH,
NULL, &res->getfh[i]);
compound_add_op(&compound, OP_GETATTR,
&args->getattr[i], &res->getattr[i]);
}
buffer_size = MAX_RPC_RES_SIZE(component_count);
status = compound_encode_send_decode(session, &compound, 0, buffer_size);
if (status)
goto out;
compound_error(status = compound.res.status);
out:
return status;
}
static int server_lookup(
IN nfs41_session *session,
IN nfs41_path_fh *dir,
IN const char *path,
IN const char *path_end,
IN uint32_t count,
IN nfs41_lookup_component_args *args,
IN nfs41_lookup_component_res *res,
OUT OPTIONAL nfs41_path_fh **parent_out,
OUT OPTIONAL nfs41_path_fh **target_out,
OUT OPTIONAL nfs41_file_info *info_out)
{
nfs41_path_fh *file, *parent;
uint32_t i = 0;
int status;
if (parent_out) *parent_out = NULL;
if (target_out) *target_out = NULL;
lookup_rpc(session, dir, count, args, res);
status = res->sequence.sr_status; if (status) goto out;
status = res->putfh.status; if (status) goto out;
status = res->getrootfh.status; if (status) goto out;
status = res->getrootattr.status; if (status) goto out;
if (dir == &res->root) {
nfs41_component name = { 0 };
/* fill in the file handle's fileid and superblock */
dir->fh.fileid = res->getrootattr.info->fileid;
status = nfs41_superblock_for_fh(session,
&res->getrootattr.info->fsid, NULL, dir);
if (status)
goto out;
/* get the name of the parent (empty if its the root) */
last_component(path, count ? args->lookup[0].name->name : path_end, &name);
/* add the file handle and attributes to the name cache */
memcpy(&res->getrootattr.info->attrmask,
&res->getrootattr.obj_attributes.attrmask, sizeof(bitmap4));
nfs41_name_cache_insert(session_name_cache(session),
path, &name, &dir->fh, res->getrootattr.info, NULL);
}
file = dir;
if (count == 0) {
if (target_out)
*target_out = dir;
if (info_out)
memcpy(info_out, res->getrootattr.info, sizeof(nfs41_file_info));
} else if (count == 1) {
if (parent_out)
*parent_out = dir;
}
for (i = 0; i < count; i++) {
status = res->lookup[i].status; if (status) break;
if (res->getfh[i].status == NFS4ERR_MOVED) {
/* save enough information to follow the referral */
path_fh_copy(&res->referral->parent, file);
res->referral->name.name = args->lookup[i].name->name;
res->referral->name.len = args->lookup[i].name->len;
return ERROR_FILESYSTEM_ABSENT;
}
status = res->getfh[i].status; if (status) break;
status = res->getattr[i].status; if (status) break;
parent = file;
file = &res->file[i];
/* fill in the file handle's fileid and superblock */
file->fh.fileid = res->getattr[i].info->fileid;
status = nfs41_superblock_for_fh(session,
&res->getattr[i].info->fsid, &parent->fh, file);
if (status)
break;
/* add the file handle and attributes to the name cache */
memcpy(&res->getattr[i].info->attrmask,
&res->getattr[i].obj_attributes.attrmask, sizeof(bitmap4));
nfs41_name_cache_insert(session_name_cache(session),
path, args->lookup[i].name, &res->file[i].fh,
res->getattr[i].info, NULL);
if (i == count-1) {
if (target_out)
*target_out = file;
if (info_out)
memcpy(info_out, res->getattr[i].info, sizeof(nfs41_file_info));
} else if (i == count-2) {
if (parent_out)
*parent_out = file;
}
}
out:
status = nfs_to_windows_error(status, ERROR_FILE_NOT_FOUND);
/* use PATH_NOT_FOUND for all but the last name */
if (status == ERROR_FILE_NOT_FOUND && i != count-1)
status = ERROR_PATH_NOT_FOUND;
return status;
}
static uint32_t max_lookup_components(
IN const nfs41_session *session)
{
const uint32_t comps = (session->fore_chan_attrs.ca_maxoperations - 4) / 3;
return min(comps, MAX_LOOKUP_COMPONENTS);
}
static uint32_t get_component_array(
IN OUT const char **path_pos,
IN const char *path_end,
IN uint32_t max_components,
OUT nfs41_path_fh *components,
OUT uint32_t *component_count)
{
uint32_t i;
for (i = 0; i < max_components; i++) {
if (!next_component(*path_pos, path_end, &components[i].name))
break;
*path_pos = components[i].name.name + components[i].name.len;
}
*component_count = i;
return i;
}
static int server_lookup_loop(
IN nfs41_session *session,
IN OPTIONAL nfs41_path_fh *parent_in,
IN nfs41_abs_path *path,
IN const char *path_pos,
IN struct lookup_referral *referral,
OUT OPTIONAL nfs41_path_fh *parent_out,
OUT OPTIONAL nfs41_path_fh *target_out,
OUT OPTIONAL nfs41_file_info *info_out)
{
nfs41_lookup_component_args args;
nfs41_lookup_component_res res;
nfs41_path_fh *dir, *parent, *target;
const char *path_end;
const uint32_t max_components = max_lookup_components(session);
uint32_t count;
int status = NO_ERROR;
init_component_args(&args, &res, path, referral);
parent = NULL;
target = NULL;
path_end = path->path + path->len;
dir = parent_in ? parent_in : &res.root;
while (get_component_array(&path_pos, path_end,
max_components, res.file, &count)) {
status = server_lookup(session, dir, path->path, path_end, count,
&args, &res, &parent, &target, info_out);
if (status == ERROR_FILE_NOT_FOUND && is_last_component(path_pos, path_end))
goto out_parent;
if (status)
goto out;
dir = target;
}
if (dir == &res.root && (target_out || info_out)) {
/* didn't get any components, so we just need the root */
status = server_lookup(session, dir, path->path, path_end,
0, &args, &res, &parent, &target, info_out);
if (status)
goto out;
}
if (target_out && target) fh_copy(&target_out->fh, &target->fh);
out_parent:
if (parent_out && parent) fh_copy(&parent_out->fh, &parent->fh);
out:
return status;
}
static void referral_locations_free(
IN fs_locations4 *locations)
{
uint32_t i;
if (locations->locations) {
for (i = 0; i < locations->location_count; i++)
free(locations->locations[i].servers);
free(locations->locations);
}
}
static int referral_resolve(
IN nfs41_root *root,
IN nfs41_session *session_in,
IN struct lookup_referral *referral,
OUT nfs41_abs_path *path_out,
OUT nfs41_session **session_out)
{
char rest_of_path[NFS41_MAX_PATH_LEN];
fs_locations4 locations = { 0 };
const fs_location4 *location;
nfs41_client *client;
int status;
/* get fs_locations */
status = nfs41_fs_locations(session_in, &referral->parent,
&referral->name, &locations);
if (status) {
eprintf("nfs41_fs_locations() failed with %s\n",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_PATH_NOT_FOUND);
goto out;
}
/* mount the first location available */
status = nfs41_root_mount_referral(root, &locations, &location, &client);
if (status) {
eprintf("nfs41_root_mount_referral() failed with %d\n",
status);
goto out;
}
/* format a new path from that location's root */
StringCchCopyA(rest_of_path, NFS41_MAX_PATH_LEN,
referral->name.name + referral->name.len);
AcquireSRWLockExclusive(&path_out->lock);
abs_path_copy(path_out, &location->path);
StringCchCatA(path_out->path, NFS41_MAX_PATH_LEN, rest_of_path);
path_out->len += (unsigned short)strlen(rest_of_path);
ReleaseSRWLockExclusive(&path_out->lock);
if (session_out) *session_out = client->session;
out:
referral_locations_free(&locations);
return status;
}
int nfs41_lookup(
IN nfs41_root *root,
IN nfs41_session *session,
IN OUT nfs41_abs_path *path_inout,
OUT OPTIONAL nfs41_path_fh *parent_out,
OUT OPTIONAL nfs41_path_fh *target_out,
OUT OPTIONAL nfs41_file_info *info_out,
OUT nfs41_session **session_out)
{
nfs41_abs_path path;
struct nfs41_name_cache *cache = session_name_cache(session);
nfs41_path_fh parent, target, *server_start;
const char *path_pos, *path_end;
struct lookup_referral referral;
int status;
if (session_out) *session_out = session;
InitializeSRWLock(&path.lock);
/* to avoid holding this lock over multiple rpcs,
* make a copy of the path and use that instead */
AcquireSRWLockShared(&path_inout->lock);
abs_path_copy(&path, path_inout);
ReleaseSRWLockShared(&path_inout->lock);
path_pos = path.path;
path_end = path.path + path.len;
dprintf(LULVL, "--> nfs41_lookup('%s')\n", path.path);
if (parent_out == NULL) parent_out = &parent;
if (target_out == NULL) target_out = &target;
parent_out->fh.len = target_out->fh.len = 0;
status = nfs41_name_cache_lookup(cache,
path_pos, path_end, &path_pos,
&parent_out->fh, &target_out->fh, info_out);
if (status == NO_ERROR)
goto out;
if (target_out->fh.len) {
/* start where the name cache left off */
if (target_out != &target) {
/* must make a copy for server_start, because
* server_lookup_loop() will overwrite target_out */
path_fh_copy(&target, target_out);
}
server_start = &target;
} else {
/* start with PUTROOTFH */
server_start = NULL;
}
status = server_lookup_loop(session, server_start,
&path, path_pos, &referral, parent_out, target_out, info_out);
if (status == ERROR_FILESYSTEM_ABSENT) {
nfs41_session *new_session;
/* create a session to the referred server and
* reformat the path relative to that server's root */
status = referral_resolve(root, session,
&referral, path_inout, &new_session);
if (status) {
eprintf("referral_resolve() failed with %d\n", status);
goto out;
}
/* update the positions of the parent and target components */
last_component(path_inout->path, path_inout->path + path_inout->len,
&target_out->name);
last_component(path_inout->path, target_out->name.name,
&parent_out->name);
if (session_out) *session_out = new_session;
/* look up the new path */
status = nfs41_lookup(root, new_session, path_inout,
parent_out, target_out, info_out, session_out);
}
out:
dprintf(LULVL, "<-- nfs41_lookup() returning %d\n", status);
return status;
}

8
daemon/makefile Normal file
View file

@ -0,0 +1,8 @@
#
# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
# file to this component. This file merely indirects to the real make file
# that is shared by all the driver components of the Windows NT DDK
#
!INCLUDE $(NTMAKEENV)\makefile.def

131
daemon/mount.c Normal file
View file

@ -0,0 +1,131 @@
/* 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 <Windows.h>
#include <stdio.h>
#include "daemon_debug.h"
#include "nfs41_ops.h"
#include "upcall.h"
#include "util.h"
/* NFS41_MOUNT */
int parse_mount(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
int status;
mount_upcall_args *args = &upcall->args.mount;
status = get_name(&buffer, &length, args->srv_name);
if(status) goto out;
ZeroMemory(&args->path, sizeof(nfs41_abs_path));
status = get_abs_path(&buffer, &length, &args->path);
out:
if (status)
eprintf("parsing of NFS41_MOUNT failed with %d\n", status);
else
dprintf(1, "parsing NFS14_MOUNT: srv_name=%s root=%s\n",
args->srv_name, args->path.path);
return status;
}
int handle_mount(nfs41_upcall *upcall)
{
int status;
mount_upcall_args *args = &upcall->args.mount;
multi_addr4 addrs;
const unsigned short port = 2049;
nfs41_root *root;
nfs41_client *client;
// resolve hostname,port
status = nfs41_server_resolve(args->srv_name, port, &addrs);
if (status) {
eprintf("nfs41_server_resolve() failed with %d\n", status);
goto out;
}
// create root
status = nfs41_root_create(args->srv_name, port, &args->path,
NFS41_MAX_FILEIO_SIZE + WRITE_OVERHEAD,
NFS41_MAX_FILEIO_SIZE + READ_OVERHEAD, &root);
if (status) {
eprintf("nfs41_rpc_clnt_create failed %d\n", status);
goto out;
}
// add a mount
status = nfs41_root_mount_addrs(root, &addrs, 0, 0, &client);
if (status) {
eprintf("nfs41_root_mount() failed with %d\n", status);
goto out_err;
}
// look up the mount path, and fail if it doesn't exist
status = nfs41_lookup(root, client->session,
&args->path, NULL, NULL, NULL, NULL);
if (status) {
eprintf("nfs41_lookup('%s') failed with %d\n",
args->path.path, status);
goto out_err;
}
args->root = root;
out:
return status;
out_err:
nfs41_root_free(root);
goto out;
}
int marshall_mount(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
{
mount_upcall_args *args = &upcall->args.mount;
dprintf(2, "NFS41_MOUNT: writing pointer to nfs41_root %p\n", args->root);
return safe_write(&buffer, length, &args->root, sizeof(args->root));
}
/* NFS41_UNMOUNT */
int parse_unmount(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
int status;
unmount_upcall_args *args = &upcall->args.unmount;
status = safe_read(&buffer, &length, &args->root, sizeof(nfs41_session *));
if (status)
eprintf("parsing NFS41_UNMOUNT failed with %d\n", status);
else
dprintf(1, "parsing NFS41_UNMOUNT: unmount root=%p\n", args->root);
return status;
}
int handle_unmount(nfs41_upcall *upcall)
{
int status = NO_ERROR;
unmount_upcall_args *args = &upcall->args.unmount;
nfs41_root_free(args->root);
return status;
}
int marshall_unmount(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
{
return NO_ERROR;
}

1243
daemon/name_cache.c Normal file

File diff suppressed because it is too large Load diff

99
daemon/name_cache.h Normal file
View file

@ -0,0 +1,99 @@
/* 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 __NFS41_DAEMON_NAME_CACHE_H__
#define __NFS41_DAEMON_NAME_CACHE_H__
#include "nfs41.h"
static __inline struct nfs41_name_cache* client_name_cache(
IN nfs41_client *client)
{
return client_server(client)->name_cache;
}
static __inline struct nfs41_name_cache* session_name_cache(
IN nfs41_session *session)
{
return client_name_cache(session->client);
}
/* attribute cache */
int nfs41_attr_cache_lookup(
IN struct nfs41_name_cache *cache,
IN uint64_t fileid,
OUT nfs41_file_info *info_out);
int nfs41_attr_cache_update(
IN struct nfs41_name_cache *cache,
IN uint64_t fileid,
IN const nfs41_file_info *info);
/* name cache */
int nfs41_name_cache_create(
OUT struct nfs41_name_cache **cache_out);
int nfs41_name_cache_free(
IN OUT struct nfs41_name_cache **cache_out);
int nfs41_name_cache_lookup(
IN struct nfs41_name_cache *cache,
IN const char *path,
IN const char *path_end,
OUT OPTIONAL const char **remaining_path_out,
OUT OPTIONAL nfs41_fh *parent_out,
OUT OPTIONAL nfs41_fh *target_out,
OUT OPTIONAL nfs41_file_info *info_out);
int nfs41_name_cache_insert(
IN struct nfs41_name_cache *cache,
IN const char *path,
IN const nfs41_component *name,
IN const nfs41_fh *fh,
IN const nfs41_file_info *info,
IN OPTIONAL const change_info4 *cinfo);
int nfs41_name_cache_remove(
IN struct nfs41_name_cache *cache,
IN const char *path,
IN const nfs41_component *name,
IN const change_info4 *cinfo);
int nfs41_name_cache_rename(
IN struct nfs41_name_cache *cache,
IN const char *src_path,
IN const nfs41_component *src_name,
IN const change_info4 *src_cinfo,
IN const char *dst_path,
IN const nfs41_component *dst_name,
IN const change_info4 *dst_cinfo);
int nfs41_name_cache_remove_stale(
IN struct nfs41_name_cache *cache,
IN nfs41_session *session,
IN nfs41_abs_path *path);
#endif /* !__NFS41_DAEMON_NAME_CACHE_H__ */

458
daemon/namespace.c Normal file
View file

@ -0,0 +1,458 @@
/* 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 <Windows.h>
#include <strsafe.h>
#include "nfs41_ops.h"
#include "util.h"
#include "daemon_debug.h"
#define NSLVL 2 /* dprintf level for namespace logging */
#define client_entry(pos) list_container(pos, nfs41_client, root_entry)
/* nfs41_root */
int nfs41_root_create(
IN const char *hostname,
IN unsigned short port,
IN const nfs41_abs_path *path,
IN uint32_t wsize,
IN uint32_t rsize,
OUT nfs41_root **root_out)
{
int status = NO_ERROR;
nfs41_root *root;
dprintf(NSLVL, "--> nfs41_root_create(%s:%u:%s)\n",
hostname, port, path);
root = calloc(1, sizeof(nfs41_root));
if (root == NULL) {
status = GetLastError();
goto out;
}
list_init(&root->clients);
root->port = port;
root->wsize = wsize;
root->rsize = rsize;
InitializeCriticalSection(&root->lock);
/* generate a unique client_owner */
status = nfs41_client_owner(&root->client_owner);
if (status) {
eprintf("nfs41_client_owner() failed with %d\n", status);
goto out;
}
*root_out = root;
out:
dprintf(NSLVL, "<-- nfs41_root_create() returning %d\n", status);
return status;
}
void nfs41_root_free(
IN nfs41_root *root)
{
struct list_entry *entry, *tmp;
dprintf(NSLVL, "--> nfs41_root_free()\n");
/* free clients */
list_for_each_tmp(entry, tmp, &root->clients)
nfs41_client_free(client_entry(entry));
free(root);
dprintf(NSLVL, "<-- nfs41_root_free()\n");
}
/* root_client_find_addrs() */
struct cl_addr_info {
const multi_addr4 *addrs;
uint32_t roles;
};
static int cl_addr_compare(
IN const struct list_entry *entry,
IN const void *value)
{
nfs41_client *client = client_entry(entry);
const struct cl_addr_info *info = (const struct cl_addr_info*)value;
uint32_t i, roles;
/* match any of the desired roles */
AcquireSRWLockShared(&client->exid_lock);
roles = info->roles & client->roles;
ReleaseSRWLockShared(&client->exid_lock);
if (roles == 0)
return ERROR_FILE_NOT_FOUND;
/* match any address in 'addrs' with any address in client->rpc->addrs */
for (i = 0; i < info->addrs->count; i++)
if (multi_addr_find(&client->rpc->addrs, &info->addrs->arr[i], NULL))
return NO_ERROR;
return ERROR_FILE_NOT_FOUND;
}
static int root_client_find_addrs(
IN nfs41_root *root,
IN const multi_addr4 *addrs,
IN bool_t is_data,
OUT nfs41_client **client_out)
{
struct cl_addr_info info;
struct list_entry *entry;
int status;
dprintf(NSLVL, "--> root_client_find_addrs()\n");
info.addrs = addrs;
info.roles = nfs41_exchange_id_flags(is_data) & EXCHGID4_FLAG_MASK_PNFS;
entry = list_search(&root->clients, &info, cl_addr_compare);
if (entry) {
*client_out = client_entry(entry);
status = NO_ERROR;
dprintf(NSLVL, "<-- root_client_find_addrs() returning 0x%p\n",
*client_out);
} else {
status = ERROR_FILE_NOT_FOUND;
dprintf(NSLVL, "<-- root_client_find_addrs() failed with %d\n",
status);
}
return status;
}
/* root_client_find() */
struct cl_exid_info {
const nfs41_exchange_id_res *exchangeid;
uint32_t roles;
};
static int cl_exid_compare(
IN const struct list_entry *entry,
IN const void *value)
{
nfs41_client *client = client_entry(entry);
const struct cl_exid_info *info = (const struct cl_exid_info*)value;
int status = ERROR_FILE_NOT_FOUND;
AcquireSRWLockShared(&client->exid_lock);
/* match any of the desired roles */
if ((info->roles & client->roles) == 0)
goto out;
/* match clientid */
if (info->exchangeid->clientid != client->clnt_id)
goto out;
/* match server_owner.major_id */
if (strncmp(info->exchangeid->server_owner.so_major_id,
client->server->owner, NFS4_OPAQUE_LIMIT) != 0)
goto out;
/* match server_scope */
if (strncmp(info->exchangeid->server_scope,
client->server->scope, NFS4_OPAQUE_LIMIT) != 0)
goto out;
status = NO_ERROR;
out:
ReleaseSRWLockShared(&client->exid_lock);
return status;
}
static int root_client_find(
IN nfs41_root *root,
IN const nfs41_exchange_id_res *exchangeid,
IN bool_t is_data,
OUT nfs41_client **client_out)
{
struct cl_exid_info info;
struct list_entry *entry;
int status;
dprintf(NSLVL, "--> root_client_find()\n");
info.exchangeid = exchangeid;
info.roles = nfs41_exchange_id_flags(is_data) & EXCHGID4_FLAG_MASK_PNFS;
entry = list_search(&root->clients, &info, cl_exid_compare);
if (entry) {
*client_out = client_entry(entry);
status = NO_ERROR;
dprintf(NSLVL, "<-- root_client_find() returning 0x%p\n",
*client_out);
} else {
status = ERROR_FILE_NOT_FOUND;
dprintf(NSLVL, "<-- root_client_find() failed with %d\n",
status);
}
return status;
}
static int session_get_lease(
IN nfs41_session *session,
IN OPTIONAL uint32_t lease_time)
{
bool_t use_mds_lease;
int status;
/* http://tools.ietf.org/html/rfc5661#section-13.1.1
* 13.1.1. Sessions Considerations for Data Servers:
* If the reply to EXCHANGE_ID has just the EXCHGID4_FLAG_USE_PNFS_DS role
* set, then (as noted in Section 13.6) the client will not be able to
* determine the data server's lease_time attribute because GETATTR will
* not be permitted. Instead, the rule is that any time a client
* receives a layout referring it to a data server that returns just the
* EXCHGID4_FLAG_USE_PNFS_DS role, the client MAY assume that the
* lease_time attribute from the metadata server that returned the
* layout applies to the data server. */
AcquireSRWLockShared(&session->client->exid_lock);
use_mds_lease = session->client->roles == EXCHGID4_FLAG_USE_PNFS_DS;
ReleaseSRWLockShared(&session->client->exid_lock);
if (!use_mds_lease) {
/* the client is allowed to GETATTR, so query the lease_time */
nfs41_file_info info = { 0 };
bitmap4 attr_request = { 1, { FATTR4_WORD0_LEASE_TIME, 0, 0 } };
status = nfs41_getattr(session, NULL, &attr_request, &info);
if (status) {
eprintf("nfs41_getattr() failed with %s\n",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
goto out;
}
lease_time = info.lease_time;
}
status = nfs41_session_set_lease(session, lease_time);
if (status) {
eprintf("nfs41_session_set_lease() failed %d\n", status);
goto out;
}
out:
return status;
}
static int root_client_create(
IN nfs41_root *root,
IN nfs41_rpc_clnt *rpc,
IN bool_t is_data,
IN OPTIONAL uint32_t lease_time,
IN const nfs41_exchange_id_res *exchangeid,
OUT nfs41_client **client_out)
{
nfs41_client *client;
nfs41_session *session;
int status;
/* create client (transfers ownership of rpc to client) */
status = nfs41_client_create(rpc, &root->client_owner,
is_data, exchangeid, &client);
if (status) {
eprintf("nfs41_client_create() failed with %d\n", status);
goto out;
}
rpc->client = client;
/* create session (and client takes ownership) */
status = nfs41_session_create(client, &session);
if (status) {
eprintf("nfs41_session_create failed %d\n", status);
goto out_err;
}
if (!is_data) {
/* send RECLAIM_COMPLETE, but don't fail on ERR_NOTSUPP */
status = nfs41_reclaim_complete(session);
if (status && status != NFS4ERR_NOTSUPP) {
eprintf("nfs41_reclaim_complete() failed with %s\n",
nfs_error_string(status));
status = ERROR_BAD_NETPATH;
goto out_err;
}
}
/* get least time and start session renewal thread */
status = session_get_lease(session, lease_time);
if (status)
goto out_err;
*client_out = client;
out:
return status;
out_err:
nfs41_client_free(client);
goto out;
}
int nfs41_root_mount_addrs(
IN nfs41_root *root,
IN const multi_addr4 *addrs,
IN bool_t is_data,
IN OPTIONAL uint32_t lease_time,
OUT nfs41_client **client_out)
{
nfs41_exchange_id_res exchangeid;
nfs41_rpc_clnt *rpc;
nfs41_client *client, *existing;
int status;
dprintf(NSLVL, "--> nfs41_root_mount_addrs()\n");
/* look for an existing client that matches the address and role */
EnterCriticalSection(&root->lock);
status = root_client_find_addrs(root, addrs, is_data, &client);
LeaveCriticalSection(&root->lock);
if (status == NO_ERROR)
goto out;
/* create an rpc client */
status = nfs41_rpc_clnt_create(addrs, root->wsize, root->rsize, !is_data, &rpc);
if (status) {
eprintf("nfs41_rpc_clnt_create() failed %d\n", status);
goto out;
}
/* get a clientid with exchangeid */
ZeroMemory(&exchangeid, sizeof(exchangeid));
status = nfs41_exchange_id(rpc, &root->client_owner,
nfs41_exchange_id_flags(is_data), &exchangeid);
if (status) {
eprintf("nfs41_exchange_id() failed %s\n",
nfs_error_string(status));
status = ERROR_BAD_NET_RESP;
goto out_free_rpc;
}
/* attempt to match existing clients by the exchangeid response */
EnterCriticalSection(&root->lock);
status = root_client_find(root, &exchangeid, is_data, &client);
LeaveCriticalSection(&root->lock);
if (status == NO_ERROR)
goto out_free_rpc;
/* create a client for this clientid */
status = root_client_create(root, rpc, is_data,
lease_time, &exchangeid, &client);
if (status) {
eprintf("nfs41_client_create() failed %d\n", status);
goto out;
}
/* because we don't hold the root's lock over session creation,
* we could end up creating multiple clients with the same
* server and roles */
EnterCriticalSection(&root->lock);
status = root_client_find(root, &exchangeid, is_data, &existing);
if (status) {
dprintf(NSLVL, "caching new client 0x%p\n", client);
/* the client is not a duplicate, so add it to the list */
list_add_tail(&root->clients, &client->root_entry);
status = NO_ERROR;
} else {
dprintf(NSLVL, "created a duplicate client 0x%p! using "
"existing client 0x%p instead\n", client, existing);
/* a matching client has been created in parallel, so free
* the one we created and use the existing client instead */
nfs41_client_free(client);
client = existing;
}
LeaveCriticalSection(&root->lock);
out:
if (status == NO_ERROR)
*client_out = client;
dprintf(NSLVL, "<-- nfs41_root_mount_addrs() returning %d\n", status);
return status;
out_free_rpc:
nfs41_rpc_clnt_free(rpc);
goto out;
}
/* http://tools.ietf.org/html/rfc5661#section-11.9
* 11.9. The Attribute fs_locations
* An entry in the server array is a UTF-8 string and represents one of a
* traditional DNS host name, IPv4 address, IPv6 address, or a zero-length
* string. An IPv4 or IPv6 address is represented as a universal address
* (see Section 3.3.9 and [15]), minus the netid, and either with or without
* the trailing ".p1.p2" suffix that represents the port number. If the
* suffix is omitted, then the default port, 2049, SHOULD be assumed. A
* zero-length string SHOULD be used to indicate the current address being
* used for the RPC call. */
static int referral_mount_location(
IN nfs41_root *root,
IN const fs_location4 *loc,
OUT nfs41_client **client_out)
{
multi_addr4 addrs;
int status = ERROR_BAD_NET_NAME;
uint32_t i;
/* create a client and session for the first available server */
for (i = 0; i < loc->server_count; i++) {
/* XXX: only deals with 'address' as a hostname with default port */
status = nfs41_server_resolve(loc->servers[i].address, 2049, &addrs);
if (status) continue;
status = nfs41_root_mount_addrs(root, &addrs, 0, 0, client_out);
if (status == NO_ERROR)
break;
}
return status;
}
int nfs41_root_mount_referral(
IN nfs41_root *root,
IN const fs_locations4 *locations,
OUT const fs_location4 **loc_out,
OUT nfs41_client **client_out)
{
int status = ERROR_BAD_NET_NAME;
uint32_t i;
/* establish a mount to the first available location */
for (i = 0; i < locations->location_count; i++) {
status = referral_mount_location(root,
&locations->locations[i], client_out);
if (status == NO_ERROR) {
*loc_out = &locations->locations[i];
break;
}
}
return status;
}

357
daemon/nfs41.h Normal file
View file

@ -0,0 +1,357 @@
/* 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 __NFS41__
#define __NFS41__
#include "nfs41_types.h"
#include "list.h"
struct __nfs41_session;
struct __nfs41_client;
struct __rpc_client;
typedef struct __nfs41_superblock {
nfs41_fsid fsid;
bitmap4 supported_attrs;
uint64_t maxread;
uint64_t maxwrite;
uint32_t layout_types;
struct list_entry entry; /* position in nfs41_server.superblocks */
SRWLOCK lock;
} nfs41_superblock;
typedef struct __nfs41_superblock_list {
struct list_entry head;
SRWLOCK lock;
} nfs41_superblock_list;
struct server_addrs {
multi_addr4 addrs; /* list of addrs we've used with this server */
uint32_t next_index;
SRWLOCK lock;
};
typedef struct __nfs41_server {
char scope[NFS4_OPAQUE_LIMIT]; /* server_scope from exchangeid */
char owner[NFS4_OPAQUE_LIMIT]; /* server_owner.major_id from exchangeid */
struct server_addrs addrs;
nfs41_superblock_list superblocks;
struct nfs41_name_cache *name_cache;
struct list_entry entry; /* position in global server list */
LONG ref_count;
} nfs41_server;
typedef struct __nfs41_lock_state {
bool_t initialized;
stateid4 stateid;
SRWLOCK lock;
} nfs41_lock_state;
typedef struct __nfs41_open_state {
nfs41_abs_path path;
nfs41_path_fh parent;
nfs41_path_fh file;
struct __nfs41_session *session;
uint32_t type;
bool_t do_close;
stateid4 stateid;
state_owner4 owner;
nfs41_lock_state last_lock;
struct __pnfs_file_layout *layout;
SRWLOCK lock;
} nfs41_open_state;
typedef struct __nfs41_rpc_clnt {
struct __rpc_client *rpc;
SRWLOCK lock;
HANDLE cond;
struct __nfs41_client *client;
multi_addr4 addrs;
uint32_t addr_index; /* index of addr we're using */
uint32_t wsize;
uint32_t rsize;
uint32_t version;
bool_t is_valid_session;
bool_t in_recovery;
} nfs41_rpc_clnt;
typedef struct __nfs41_client {
nfs41_server *server;
client_owner4 owner;
uint64_t clnt_id;
uint32_t seq_id;
uint32_t roles;
SRWLOCK exid_lock;
struct __nfs41_session *session;
SRWLOCK session_lock;
nfs41_rpc_clnt *rpc;
bool_t is_data;
struct pnfs_file_layout_list *layouts;
struct pnfs_file_device_list *devices;
struct list_entry root_entry; /* position in nfs41_root.clients */
HANDLE cond;
bool_t in_recovery;
} nfs41_client;
#define NFS41_MAX_NUM_SLOTS NFS41_MAX_RPC_REQS
typedef struct __nfs41_slot_table {
uint32_t seq_nums[NFS41_MAX_NUM_SLOTS];
uint32_t used_slots[NFS41_MAX_NUM_SLOTS];
uint32_t max_slots;
uint32_t highest_used;
HANDLE lock;
HANDLE cond;
} nfs41_slot_table;
typedef struct __nfs41_channel_attrs {
uint32_t ca_headerpadsize;
uint32_t ca_maxrequestsize;
uint32_t ca_maxresponsesize;
uint32_t ca_maxresponsesize_cached;
uint32_t ca_maxoperations;
uint32_t ca_maxrequests;
uint32_t *ca_rdma_ird;
} nfs41_channel_attrs;
typedef struct __nfs41_cb_session {
unsigned char cb_sessionid[NFS4_SESSIONID_SIZE];
uint32_t cb_seqnum;
uint32_t cb_slotid;
bool_t cb_is_valid_state;
bool_t cb_cache_this;
} nfs41_cb_session;
typedef struct __nfs41_session {
nfs41_client *client;
unsigned char session_id[NFS4_SESSIONID_SIZE];
nfs41_channel_attrs fore_chan_attrs;
nfs41_channel_attrs back_chan_attrs;
uint32_t lease_time;
nfs41_slot_table table;
// array of slots
HANDLE renew_thread;
bool_t isValidState;
uint32_t flags;
nfs41_cb_session cb_session;
} nfs41_session;
typedef struct __nfs41_root {
client_owner4 client_owner;
CRITICAL_SECTION lock;
struct list_entry clients;
uint32_t wsize;
uint32_t rsize;
unsigned short port;
} nfs41_root;
/* nfs41_namespace.c */
int nfs41_root_create(
IN const char *hostname,
IN unsigned short port,
IN const nfs41_abs_path *path,
IN uint32_t wsize,
IN uint32_t rsize,
OUT nfs41_root **root_out);
void nfs41_root_free(
IN nfs41_root *root);
int nfs41_root_mount_addrs(
IN nfs41_root *root,
IN const multi_addr4 *addrs,
IN bool_t is_data,
IN OPTIONAL uint32_t lease_time,
OUT nfs41_client **client_out);
int nfs41_root_mount_server(
IN nfs41_root *root,
IN nfs41_server *server,
IN bool_t is_data,
IN OPTIONAL uint32_t lease_time,
OUT nfs41_client **client_out);
int nfs41_root_mount_referral(
IN nfs41_root *root,
IN const fs_locations4 *locations,
OUT const fs_location4 **loc_out,
OUT nfs41_client **client_out);
static __inline nfs41_session* nfs41_root_session(
IN nfs41_root *root)
{
nfs41_client *client;
/* return a session for the server at the root of the namespace.
* because we created it on mount, it's the first one in the list */
EnterCriticalSection(&root->lock);
client = list_container(root->clients.next, nfs41_client, root_entry);
LeaveCriticalSection(&root->lock);
return client->session;
}
/* nfs41_session.c */
int nfs41_session_create(
IN nfs41_client *client,
IN nfs41_session **session_out);
int nfs41_session_renew(
IN nfs41_session *session);
int nfs41_session_set_lease(
IN nfs41_session *session,
IN uint32_t lease_time);
void nfs41_session_free(
IN nfs41_session *session);
int nfs41_session_bump_seq(
IN nfs41_session *session,
IN uint32_t slotid);
int nfs41_session_free_slot(
IN nfs41_session *session,
IN uint32_t slotid);
int nfs41_session_get_slot(
IN nfs41_session *session,
OUT uint32_t *slot,
OUT uint32_t *seq,
OUT uint32_t *highest);
struct __nfs41_sequence_args;
int nfs41_session_sequence(
struct __nfs41_sequence_args *args,
nfs41_session *session,
bool_t cachethis);
/* nfs41_server.c */
void nfs41_server_list_init();
int nfs41_server_resolve(
IN const char *hostname,
IN unsigned short port,
OUT multi_addr4 *addrs);
int nfs41_server_find_or_create(
IN const char *server_owner_major_id,
IN const char *server_scope,
IN const netaddr4 *addr,
OUT nfs41_server **server_out);
void nfs41_server_ref(
IN nfs41_server *server);
void nfs41_server_deref(
IN nfs41_server *server);
void nfs41_server_addrs(
IN nfs41_server *server,
OUT multi_addr4 *addrs);
/* nfs41_client.c */
int nfs41_client_owner(
OUT client_owner4 *owner);
uint32_t nfs41_exchange_id_flags(
IN bool_t is_data);
struct __nfs41_exchange_id_res;
int nfs41_client_create(
IN nfs41_rpc_clnt *rpc,
IN const client_owner4 *owner,
IN bool_t is_data,
IN const struct __nfs41_exchange_id_res *exchangeid,
OUT nfs41_client **client_out);
int nfs41_client_renew(
IN nfs41_client *client);
void nfs41_client_free(
IN nfs41_client *client);
static __inline nfs41_server* client_server(
IN nfs41_client *client)
{
/* the client's server could change during nfs41_client_renew(),
* so access to client->server must be protected */
nfs41_server *server;
AcquireSRWLockShared(&client->exid_lock);
server = client->server;
ReleaseSRWLockShared(&client->exid_lock);
return server;
}
/* nfs41_superblock.c */
int nfs41_superblock_for_fh(
IN nfs41_session *session,
IN const nfs41_fsid *fsid,
IN const nfs41_fh *parent OPTIONAL,
OUT nfs41_path_fh *file);
void nfs41_superblock_list_init(
IN nfs41_superblock_list *superblocks);
void nfs41_superblock_list_free(
IN nfs41_superblock_list *superblocks);
/* nfs41_rpc.c */
int nfs41_rpc_clnt_create(
IN const multi_addr4 *addrs,
IN uint32_t wsize,
IN uint32_t rsize,
IN bool_t needcb,
OUT nfs41_rpc_clnt **rpc_out);
void nfs41_rpc_clnt_free(
IN nfs41_rpc_clnt *rpc);
int nfs41_send_compound(
IN nfs41_rpc_clnt *rpc,
IN char *inbuf,
OUT char *outbuf);
static __inline netaddr4* nfs41_rpc_netaddr(
IN nfs41_rpc_clnt *rpc)
{
uint32_t id;
AcquireSRWLockShared(&rpc->lock);
/* only addr_index needs to be protected, as rpc->addrs is write-once */
id = rpc->addr_index;
ReleaseSRWLockShared(&rpc->lock);
/* return the netaddr used to create the rpc client */
return &rpc->addrs.arr[id];
}
bool_t nfs41_renew_in_progress(nfs41_client *client, bool_t *value);
#endif /* __NFS41__ */

254
daemon/nfs41_callback.h Normal file
View file

@ -0,0 +1,254 @@
/* 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 __NFS41_CALLBACK_H__
#define __NFS41_CALLBACK_H__
//#include "nfs41.h"
#include "wintirpc.h"
#include "rpc/rpc.h"
#include "nfs41_types.h"
enum nfs41_callback_proc {
CB_NULL = 0,
CB_COMPOUND = 1,
};
enum nfs41_callback_op {
OP_CB_GETATTR = 3,
OP_CB_RECALL = 4,
OP_CB_LAYOUTRECALL = 5,
OP_CB_NOTIFY = 6,
OP_CB_PUSH_DELEG = 7,
OP_CB_RECALL_ANY = 8,
OP_CB_RECALLABLE_OBJ_AVAIL = 9,
OP_CB_RECALL_SLOT = 10,
OP_CB_SEQUENCE = 11,
OP_CB_WANTS_CANCELLED = 12,
OP_CB_NOTIFY_LOCK = 13,
OP_CB_NOTIFY_DEVICEID = 14,
OP_CB_ILLEGAL = 10044
};
int nfs41_handle_callback(void *, void *, void *);
/* OP_CB_LAYOUTRECALL */
struct cb_recall_file {
nfs41_fh fh;
uint64_t offset;
uint64_t length;
stateid4 stateid;
};
union cb_recall_file_args {
struct cb_recall_file file;
nfs41_fsid fsid;
};
struct cb_recall {
enum pnfs_return_type type;
union cb_recall_file_args args;
};
struct cb_layoutrecall_args {
enum pnfs_layout_type type;
enum pnfs_iomode iomode;
bool_t changed;
struct cb_recall recall;
};
struct cb_layoutrecall_res {
enum_t status;
};
/* OP_CB_RECALL_SLOT */
struct cb_recall_slot_args {
uint32_t target_highest_slotid;
};
struct cb_recall_slot_res {
enum_t status;
};
/* OP_CB_SEQUENCE */
struct cb_sequence_ref {
uint32_t sequenceid;
uint32_t slotid;
};
struct cb_sequence_ref_list {
char sessionid[NFS4_SESSIONID_SIZE];
struct cb_sequence_ref *calls;
uint32_t call_count;
};
struct cb_sequence_args {
char sessionid[NFS4_SESSIONID_SIZE];
uint32_t sequenceid;
uint32_t slotid;
uint32_t highest_slotid;
bool_t cachethis;
struct cb_sequence_ref_list *ref_lists;
uint32_t ref_list_count;
};
struct cb_sequence_res_ok {
char sessionid[NFS4_SESSIONID_SIZE];
uint32_t sequenceid;
uint32_t slotid;
uint32_t highest_slotid;
uint32_t target_highest_slotid;
};
struct cb_sequence_res {
enum_t status;
struct cb_sequence_res_ok ok;
};
/* OP_CB_GETATTR */
struct cb_getattr_args {
uint32_t target_highest_slotid;
};
struct cb_getattr_res {
enum_t status;
};
/* OP_CB_RECALL */
struct cb_recall_args {
stateid4 stateid;
bool_t truncate;
nfs41_fh fh;
};
struct cb_recall_res {
enum_t status;
};
/* OP_CB_NOTIFY */
struct cb_notify_args {
uint32_t target_highest_slotid;
};
struct cb_notify_res {
enum_t status;
};
/* OP_CB_PUSH_DELEG */
struct cb_push_deleg_args {
uint32_t target_highest_slotid;
};
struct cb_push_deleg_res {
enum_t status;
};
/* OP_CB_RECALL_ANY */
struct cb_recall_any_args {
uint32_t target_highest_slotid;
};
struct cb_recall_any_res {
enum_t status;
};
/* OP_CB_RECALLABLE_OBJ_AVAIL */
struct cb_recallable_obj_avail_args {
uint32_t target_highest_slotid;
};
struct cb_recallable_obj_avail_res {
enum_t status;
};
/* OP_CB_WANTS_CANCELLED */
struct cb_wants_cancelled_args {
uint32_t target_highest_slotid;
};
struct cb_wants_cancelled_res {
enum_t status;
};
/* OP_CB_NOTIFY_LOCK */
struct cb_notify_lock_args {
uint32_t target_highest_slotid;
};
struct cb_notify_lock_res {
enum_t status;
};
/* OP_CB_NOTIFY_DEVICEID */
struct cb_notify_deviceid_args {
uint32_t target_highest_slotid;
};
struct cb_notify_deviceid_res {
enum_t status;
};
/* CB_COMPOUND */
#define CB_COMPOUND_MAX_TAG 64
#define CB_COMPOUND_MAX_OPERATIONS 16
union cb_op_args {
struct cb_layoutrecall_args layoutrecall;
struct cb_recall_slot_args recall_slot;
struct cb_sequence_args sequence;
struct cb_recall_args recall;
};
struct cb_argop {
enum_t opnum;
union cb_op_args args;
};
struct cb_compound_tag {
char str[CB_COMPOUND_MAX_TAG];
uint32_t len;
};
struct cb_compound_args {
struct cb_compound_tag tag;
uint32_t minorversion;
uint32_t callback_ident; /* client MUST ignore */
struct cb_argop *argarray;
uint32_t argarray_count; /* <= CB_COMPOUND_MAX_OPERATIONS */
};
union cb_op_res {
struct cb_layoutrecall_res layoutrecall;
struct cb_recall_slot_res recall_slot;
struct cb_sequence_res sequence;
struct cb_recall_res recall;
};
struct cb_resop {
enum_t opnum;
union cb_op_res res;
};
struct cb_compound_res {
enum_t status;
struct cb_compound_tag tag;
struct cb_resop *resarray;
uint32_t resarray_count; /* <= CB_COMPOUND_MAX_OPERATIONS */
};
/* callback_xdr.c */
bool_t proc_cb_compound_args(XDR *xdr, struct cb_compound_args *args);
bool_t proc_cb_compound_res(XDR *xdr, struct cb_compound_res *res);
#endif /* !__NFS41_CALLBACK_H__ */

437
daemon/nfs41_client.c Normal file
View file

@ -0,0 +1,437 @@
/* 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 <Windows.h>
#include <stdio.h>
#include <time.h>
#include <winsock2.h>
#include <iphlpapi.h> /* for GetAdaptersAddresses() */
#include "rbtree.h"
#include "daemon_debug.h"
#include "nfs41_ops.h"
uint32_t nfs41_exchange_id_flags(
IN bool_t is_data)
{
uint32_t flags = EXCHGID4_FLAG_SUPP_MOVED_REFER;
if (is_data)
flags |= EXCHGID4_FLAG_USE_PNFS_DS;
else
flags |= EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS;
return flags;
}
static int pnfs_client_init(
IN nfs41_client *client)
{
enum pnfs_status pnfsstat;
int status = NO_ERROR;
/* initialize the pnfs layout and device lists for metadata clients */
pnfsstat = pnfs_file_layout_list_create(&client->layouts);
if (pnfsstat) {
status = ERROR_NOT_ENOUGH_MEMORY;
goto out;
}
pnfsstat = pnfs_file_device_list_create(&client->devices);
if (pnfsstat) {
status = ERROR_NOT_ENOUGH_MEMORY;
goto out_err_layouts;
}
out:
return status;
out_err_layouts:
pnfs_file_layout_list_free(client->layouts);
client->layouts = NULL;
goto out;
}
static void update_server(
IN nfs41_client *client,
IN const char *server_scope,
IN const server_owner4 *owner)
{
nfs41_server *server;
int status;
/* find a server matching the owner.major_id and scope */
status = nfs41_server_find_or_create(owner->so_major_id,
server_scope, nfs41_rpc_netaddr(client->rpc), &server);
if (status == NO_ERROR) {
/* if the server is the same, we now have an extra reference. if
* the servers are different, we still need to deref the old server.
* so both cases can be treated the same */
if (client->server)
nfs41_server_deref(client->server);
client->server = server;
}
}
static void update_exchangeid_res(
IN nfs41_client *client,
IN const nfs41_exchange_id_res *exchangeid)
{
update_server(client, exchangeid->server_scope, &exchangeid->server_owner);
client->clnt_id = exchangeid->clientid;
client->seq_id = exchangeid->sequenceid;
client->roles = exchangeid->flags & EXCHGID4_FLAG_MASK_PNFS;
}
int nfs41_client_create(
IN nfs41_rpc_clnt *rpc,
IN const client_owner4 *owner,
IN bool_t is_data,
IN const nfs41_exchange_id_res *exchangeid,
OUT nfs41_client **client_out)
{
int status;
nfs41_client *client;
client = calloc(1, sizeof(nfs41_client));
if (client == NULL) {
status = GetLastError();
goto out_err_rpc;
}
client->cond = CreateEvent(NULL, TRUE, FALSE, "client_recovery_cond");
if (client->cond == NULL) {
status = GetLastError();
eprintf("CreateEvent failed %d\n", status);
goto out_err_rpc;
}
memcpy(&client->owner, owner, sizeof(client_owner4));
client->rpc = rpc;
client->is_data = is_data;
update_exchangeid_res(client, exchangeid);
//initialize a lock used to protect access to client id and client id seq#
InitializeSRWLock(&client->exid_lock);
status = pnfs_client_init(client);
if (status) {
eprintf("pnfs_client_init() failed with %d\n", status);
goto out_err_client;
}
*client_out = client;
out:
return status;
out_err_client:
nfs41_client_free(client); /* also calls nfs41_rpc_clnt_free() */
goto out;
out_err_rpc:
nfs41_rpc_clnt_free(rpc);
goto out;
}
static void dprint_roles(
IN int level,
IN uint32_t roles)
{
dprintf(level, "roles: %s%s%s\n",
(roles & EXCHGID4_FLAG_USE_NON_PNFS) ? "USE_NON_PNFS " : "",
(roles & EXCHGID4_FLAG_USE_PNFS_MDS) ? "USE_PNFS_MDS " : "",
(roles & EXCHGID4_FLAG_USE_PNFS_DS) ? "USE_PNFS_DS" : "");
}
int nfs41_client_renew(
IN nfs41_client *client)
{
nfs41_exchange_id_res exchangeid;
int status;
ZeroMemory(&exchangeid, sizeof(exchangeid));
status = nfs41_exchange_id(client->rpc, &client->owner,
nfs41_exchange_id_flags(client->is_data), &exchangeid);
if (status) {
eprintf("nfs41_exchange_id() failed with %d\n", status);
status = ERROR_BAD_NET_RESP;
goto out;
}
if (client->is_data) { /* require USE_PNFS_DS */
if ((exchangeid.flags & EXCHGID4_FLAG_USE_PNFS_DS) == 0) {
eprintf("client expected USE_PNFS_DS\n");
status = ERROR_BAD_NET_RESP;
goto out;
}
} else { /* require USE_NON_PNFS or USE_PNFS_MDS */
if ((exchangeid.flags & EXCHGID4_FLAG_USE_NON_PNFS) == 0 &&
(exchangeid.flags & EXCHGID4_FLAG_USE_PNFS_MDS) == 0) {
eprintf("client expected USE_NON_PNFS OR USE_PNFS_MDS\n");
status = ERROR_BAD_NET_RESP;
goto out;
}
}
dprint_roles(2, exchangeid.flags);
AcquireSRWLockExclusive(&client->exid_lock);
update_exchangeid_res(client, &exchangeid);
ReleaseSRWLockExclusive(&client->exid_lock);
out:
return status;
}
bool_t nfs41_renew_in_progress(nfs41_client *client, bool_t *value)
{
bool_t status = FALSE;
if (value) {
dprintf(1, "nfs41_renew_in_progress: setting value %d\n", *value);
AcquireSRWLockExclusive(&client->exid_lock);
client->in_recovery = *value;
if (!client->in_recovery)
SetEvent(client->cond);
ReleaseSRWLockExclusive(&client->exid_lock);
} else {
AcquireSRWLockShared(&client->exid_lock);
status = client->in_recovery;
ReleaseSRWLockShared(&client->exid_lock);
dprintf(1, "nfs41_renew_in_progress: returning value %d\n", status);
}
return status;
}
void nfs41_client_free(
IN nfs41_client *client)
{
dprintf(2, "nfs41_client_free(%llu)\n", client->clnt_id);
if (client->session) nfs41_session_free(client->session);
if (client->server) nfs41_server_deref(client->server);
nfs41_rpc_clnt_free(client->rpc);
if (client->layouts) pnfs_file_layout_list_free(client->layouts);
if (client->devices) pnfs_file_device_list_free(client->devices);
CloseHandle(client->cond);
free(client);
}
/* client_owner generation
* we choose to use MAC addresses to generate a client_owner value that
* is unique to a machine and persists over restarts. because the client
* can have multiple network adapters/addresses, we take each adapter into
* account. the specification suggests that "for privacy reasons, it is
* best to perform some one-way function," so we apply an md5 hash to the
* sorted list of MAC addresses */
/* References:
* RFC 5661: 2.4. Client Identifiers and Client Owners
* http://tools.ietf.org/html/rfc5661#section-2.4
*
* MSDN: GetAdaptersAddresses Function
* http://msdn.microsoft.com/en-us/library/aa365915%28VS.85%29.aspx
*
* MSDN: Example C Program: Creating an MD5 Hash from File Content
* http://msdn.microsoft.com/en-us/library/aa382380%28VS.85%29.aspx */
/* use an rbtree to sort mac address entries */
struct mac_entry {
struct rb_node rbnode;
PBYTE address;
ULONG length;
};
static void mac_entry_insert(
IN struct rb_root *root,
IN PBYTE address,
IN ULONG length)
{
struct rb_node **node, *prev;
struct mac_entry *entry;
int diff;
node = &root->rb_node;
prev = NULL;
while (*node) {
entry = rb_entry(*node, struct mac_entry, rbnode);
prev = *node;
diff = length - entry->length;
if (diff == 0)
diff = memcmp(address, entry->address, length);
if (diff < 0)
node = &(*node)->rb_left;
else if (diff > 0)
node = &(*node)->rb_right;
else
return;
}
entry = calloc(1, sizeof(struct mac_entry));
if (entry) {
entry->address = address;
entry->length = length;
rb_link_node(&entry->rbnode, prev, node);
rb_insert_color(&entry->rbnode, root);
}
}
static int adapter_valid(
IN const IP_ADAPTER_ADDRESSES *addr)
{
/* ignore generic interfaces whose address is not unique */
switch (addr->IfType) {
case IF_TYPE_SOFTWARE_LOOPBACK:
case IF_TYPE_TUNNEL:
return 0;
}
/* must have an address */
if (addr->PhysicalAddressLength == 0)
return 0;
/* must support ip */
return addr->Ipv4Enabled || addr->Ipv6Enabled;
}
static DWORD hash_mac_addrs(
IN HCRYPTHASH hash)
{
PIP_ADAPTER_ADDRESSES addr, addrs = NULL;
struct rb_root rbtree = { NULL };
struct rb_node *node;
struct mac_entry *entry;
ULONG len;
DWORD status;
/* start with enough room for DEFAULT_MINIMUM_ENTITIES */
len = DEFAULT_MINIMUM_ENTITIES * sizeof(IP_ADAPTER_ADDRESSES);
do {
PIP_ADAPTER_ADDRESSES tmp;
/* reallocate the buffer until we can fit all of it */
tmp = realloc(addrs, len);
if (tmp == NULL) {
status = GetLastError();
goto out;
}
addrs = tmp;
status = GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_INCLUDE_ALL_INTERFACES | GAA_FLAG_SKIP_ANYCAST |
GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME |
GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_UNICAST,
NULL, addrs, &len);
} while (status == ERROR_BUFFER_OVERFLOW);
if (status) {
eprintf("GetAdaptersAddresses() failed with %d\n", status);
goto out;
}
/* get the mac address of each adapter */
for (addr = addrs; addr; addr = addr->Next)
if (adapter_valid(addr))
mac_entry_insert(&rbtree, addr->PhysicalAddress,
addr->PhysicalAddressLength);
/* require at least one valid address */
node = rb_first(&rbtree);
if (node == NULL) {
status = ERROR_FILE_NOT_FOUND;
eprintf("GetAdaptersAddresses() did not return "
"any valid mac addresses, failing with %d.\n", status);
goto out;
}
while (node) {
entry = rb_entry(node, struct mac_entry, rbnode);
node = rb_next(node);
rb_erase(&entry->rbnode, &rbtree);
if (!CryptHashData(hash, entry->address, entry->length, 0)) {
status = GetLastError();
eprintf("CryptHashData() failed with %d\n", status);
/* don't break here, we need to free the rest */
}
free(entry);
}
out:
free(addrs);
return status;
}
int nfs41_client_owner(
OUT client_owner4 *owner)
{
HCRYPTPROV context;
HCRYPTHASH hash;
PBYTE buffer;
DWORD length;
const time_t time_created = time(NULL);
int status;
/* owner.verifier = "time created" */
ZeroMemory(owner->co_verifier, sizeof(owner->co_verifier));
memcpy(owner->co_verifier, &time_created, sizeof(time_created));
/* set up the md5 hash generator */
if (!CryptAcquireContext(&context, NULL, NULL,
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
status = GetLastError();
eprintf("CryptAcquireContext() failed with %d\n", status);
goto out;
}
if (!CryptCreateHash(context, CALG_MD5, 0, 0, &hash)) {
status = GetLastError();
eprintf("CryptCreateHash() failed with %d\n", status);
goto out_context;
}
/* add the mac address from each applicable adapter to the hash */
status = hash_mac_addrs(hash);
if (status) {
eprintf("hash_mac_addrs() failed with %d\n", status);
goto out_hash;
}
/* extract the hash size (should always be 16 for md5) */
buffer = (PBYTE)&owner->co_ownerid_len;
length = (DWORD)sizeof(DWORD);
if (!CryptGetHashParam(hash, HP_HASHSIZE, buffer, &length, 0)) {
status = GetLastError();
eprintf("CryptGetHashParam(size) failed with %d\n", status);
goto out_hash;
}
/* extract the hash buffer */
buffer = owner->co_ownerid;
length = owner->co_ownerid_len;
if (!CryptGetHashParam(hash, HP_HASHVAL, buffer, &length, 0)) {
status = GetLastError();
eprintf("CryptGetHashParam(val) failed with %d\n", status);
goto out_hash;
}
out_hash:
CryptDestroyHash(hash);
out_context:
CryptReleaseContext(context, 0);
out:
return status;
}

333
daemon/nfs41_compound.c Normal file
View file

@ -0,0 +1,333 @@
/* 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 <stdio.h>
#include <stdlib.h>
#include "nfs41_compound.h"
#include "nfs41_xdr.h"
#include "nfs41_ops.h"
#include "name_cache.h"
#include "daemon_debug.h"
#define BUF_SIZE 1024
int compound_error(int status)
{
if (status != NFS4_OK)
dprintf(1, "COMPOUND failed with status %d.\n", status);
return status;
}
static void compound_args_init(
nfs41_compound_args *compound,
nfs_argop4 *argarray)
{
compound->tag_len = 8;
memcpy(compound->tag, "ms-nfs41", 8);
compound->minorversion = 1;
compound->argarray_count = 0;
compound->argarray = argarray;
}
static void compound_args_add_op(
nfs41_compound_args *compound,
uint32_t opnum,
void *arg)
{
const uint32_t i = compound->argarray_count++;
compound->argarray[i].op = opnum;
compound->argarray[i].arg = arg;
}
static void compound_res_init(
nfs41_compound_res *compound,
nfs_resop4 *resarray)
{
ZeroMemory(compound, sizeof(nfs41_compound_res));
compound->tag_len = NFS4_OPAQUE_LIMIT;
compound->resarray_count = 0;
compound->resarray = resarray;
}
static void compound_res_add_op(
nfs41_compound_res *compound,
void *res)
{
compound->resarray[compound->resarray_count++].res = res;
}
void compound_init(
nfs41_compound *compound,
nfs_argop4 *argops,
nfs_resop4 *resops)
{
compound_args_init(&compound->args, argops);
compound_res_init(&compound->res, resops);
}
void compound_add_op(
nfs41_compound *compound,
uint32_t opnum,
void *arg,
void *res)
{
compound_args_add_op(&compound->args, opnum, arg);
compound_res_add_op(&compound->res, res);
}
/* Due to the possibility of replays, we might get a response to a different
* call than the one we're expecting. If we don't have a way to check for
* this, we'll likely crash trying to decode into the wrong structures.
* This function copies the number of operations and all of the operation
* numbers from the compound arguments into the response, so we can verify
* them on decode and fail before doing any damage. */
static void set_expected_res(
nfs41_compound *compound)
{
uint32_t i;
compound->res.resarray_count = compound->args.argarray_count;
for (i = 0; i < compound->res.resarray_count; i++)
compound->res.resarray[i].op = compound->args.argarray[i].op;
}
int check_renew_in_progress(
IN nfs41_session *session)
{
int status = 0;
bool_t one = 1, zero = 0;;
while (nfs41_renew_in_progress(session->client, NULL)) {
status = WaitForSingleObject(session->client->cond, INFINITE);
if (status != WAIT_OBJECT_0) {
dprintf(1, "nfs41_renew_in_progress: WaitForSingleObject failed\n");
print_condwait_status(1, status);
status = ERROR_LOCK_VIOLATION;
goto out;
}
nfs41_renew_in_progress(session->client, &zero);
status = 1;
}
nfs41_renew_in_progress(session->client, &one);
out:
return status;
}
int compound_encode_send_decode(
nfs41_session *session,
nfs41_compound *compound,
uint32_t bufsize_in,
uint32_t bufsize_out)
{
int status, retry_count = 0, delayby = 0;
nfs41_sequence_args *args =
(nfs41_sequence_args *)compound->args.argarray[0].arg;
bool_t zero = 0;
retry:
/* send compound */
retry_count++;
set_expected_res(compound);
status = nfs41_send_compound(session->client->rpc,
(char *)&compound->args, (char *)&compound->res);
if (status) {
eprintf("nfs41_send_compound failed %d for seqid=%d, slotid=%d\n",
status, args->sa_sequenceid, args->sa_slotid);
status = NFS4ERR_IO;
goto out_free_slot;
} else {
// bump sequence number if sequence op succeeded
if (compound->res.resarray_count > 0 &&
compound->res.resarray[0].op == OP_SEQUENCE) {
nfs41_sequence_res *seq =
(nfs41_sequence_res *)compound->res.resarray[0].res;
if (seq->sr_status == NFS4_OK) {
// returned slotid must be the same we sent
status = NFS4ERR_IO;
if (seq->sr_resok4.sr_slotid != args->sa_slotid) {
eprintf("[session] sr_slotid=%d != sa_slotid=%d\n",
seq->sr_resok4.sr_slotid, args->sa_slotid);
goto out_free_slot;
}
// returned sessionid must be the same we sent
if (memcmp(seq->sr_resok4.sr_sessionid, args->sa_sessionid,
NFS4_SESSIONID_SIZE)) {
eprintf("[session] sr_sessionid != sa_sessionid\n");
print_hexbuf(1, (unsigned char *)"sr_sessionid",
seq->sr_resok4.sr_sessionid, NFS4_SESSIONID_SIZE);
print_hexbuf(1, (unsigned char *)"sa_sessionid",
args->sa_sessionid, NFS4_SESSIONID_SIZE);
goto out_free_slot;
}
if (seq->sr_resok4.sr_status_flags)
print_sr_status_flags(1, seq->sr_resok4.sr_status_flags);
status = nfs41_session_bump_seq(session, args->sa_slotid);
if (status)
goto out_free_slot;
}
}
}
if (compound->res.status != NFS4_OK)
dprintf(1, "\n################ %s ################\n\n",
nfs_error_string(compound->res.status));
if (compound->res.status != NFS4_OK &&
compound->args.argarray[0].op == OP_DESTROY_SESSION) {
dprintf(1, "OP_DESTROY_SESSION ignoring errors\n");
compound->res.status = NFS4_OK;
}
switch (compound->res.status) {
case NFS4_OK:
break;
case NFS4ERR_STALE_CLIENTID:
//try to create a new client
status = check_renew_in_progress(session);
if (status == ERROR_LOCK_VIOLATION)
goto out_free_slot;
else if (status == 1)
goto do_retry;
status = nfs41_client_renew(session->client);
if (status) {
eprintf("nfs41_exchange_id() failed with %d\n", status);
status = ERROR_BAD_NET_RESP;
goto out;
}
//fallthru and reestablish the session
case NFS4ERR_BADSESSION:
//try to create a new session
if (compound->res.status == NFS4ERR_BADSESSION) {
status = check_renew_in_progress(session);
if (status == ERROR_LOCK_VIOLATION)
goto out_free_slot;
else if (status == 1)
goto do_retry;
}
status = nfs41_session_renew(session);
if (status == NFS4ERR_STALE_CLIENTID) {
status = nfs41_client_renew(session->client);
if (status) {
eprintf("nfs41_exchange_id() failed with %d\n", status);
status = ERROR_BAD_NET_RESP;
goto out;
}
status = nfs41_session_renew(session);
if (status) {
eprintf("after reestablishing clientid: nfs41_session_renew() "
"failed with %d\n", status);
status = ERROR_BAD_NET_RESP;
goto out;
}
} else if (status && status != NFS4ERR_STALE_CLIENTID) {
eprintf("nfs41_session_renew: failed with %d\n", status);
goto out;
}
if (nfs41_renew_in_progress(session->client, NULL))
nfs41_renew_in_progress(session->client, &zero);
goto do_retry;
case NFS4ERR_GRACE:
case NFS4ERR_DELAY:
#define RETRY_INDEFINITELY
#ifndef RETRY_INDEFINITELY
#define NUMBER_2_RETRY 19
#endif
#ifndef RETRY_INDEFINITELY
if (retry_count < NUMBER_2_RETRY) {
#endif
if (compound->args.argarray[0].op == OP_SEQUENCE) {
nfs41_sequence_args *seq = (nfs41_sequence_args*)
compound->args.argarray[0].arg;
nfs41_session_free_slot(session, seq->sa_slotid);
}
if (compound->res.status == NFS4ERR_GRACE)
delayby = 5000;
else
delayby = 500*retry_count;
dprintf(1, "Compound returned %s: sleeping for %ums..\n",
(compound->res.status==NFS4ERR_GRACE)?"NFS4ERR_GRACE":"NFS4ERR_DELAY",
delayby);
Sleep(delayby);
dprintf(1, "Attempting to resend compound.\n");
goto do_retry;
#ifndef RETRY_INDEFINITELY
}
#endif
break;
case NFS4ERR_FHEXPIRED: /* TODO: recover expired volatile filehandles */
status = NFS4ERR_STALE; /* for now, treat them as ERR_STALE */
/* no break */
case NFS4ERR_STALE:
{
nfs_argop4 *argarray = compound->args.argarray;
struct nfs41_name_cache *name_cache =
session_name_cache(session);
nfs41_putfh_args *putfh;
uint32_t i, start = 0;
/* NFS4ERR_STALE generally comes from a PUTFH operation. in
* this case, remove its filehandle from the name cache. but
* because COMPOUNDs are not atomic, a file can be removed
* between PUTFH and the operation that uses it. in this
* case, we can't tell which PUTFH operation is to blame, so
* we must invalidate filehandles of all PUTFH operations in
* the COMPOUND */
if (argarray[compound->res.resarray_count-1].op == OP_PUTFH)
start = compound->res.resarray_count-1;
for (i = start; i < compound->res.resarray_count; i++) {
if (argarray[i].op == OP_PUTFH) {
putfh = (nfs41_putfh_args*)argarray[i].arg;
if (!putfh->in_recovery)
nfs41_name_cache_remove_stale(name_cache,
session, putfh->file->path);
}
}
}
break;
}
out_free_slot:
if (compound->args.argarray[0].op == OP_SEQUENCE) {
nfs41_sequence_args *seq = (nfs41_sequence_args *)compound->args.argarray[0].arg;
nfs41_session_free_slot(session, seq->sa_slotid);
}
out:
return status;
do_retry:
if (compound->res.resarray[0].op == OP_SEQUENCE) {
nfs41_sequence_args *seq = (nfs41_sequence_args*)
compound->args.argarray[0].arg;
status = nfs41_session_get_slot(session, &seq->sa_slotid,
&seq->sa_sequenceid, &seq->sa_highest_slotid);
if (status)
goto out;
}
goto retry;
}

82
daemon/nfs41_compound.h Normal file
View file

@ -0,0 +1,82 @@
/* 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 __NFS41_DAEMON_COMPOUND_H__
#define __NFS41_DAEMON_COMPOUND_H__
#include "nfs41.h"
/* COMPOUND */
typedef struct __nfs_argop4 {
uint32_t op;
void *arg;
} nfs_argop4;
typedef struct __nfs41_compound_args {
uint32_t tag_len;
unsigned char tag[NFS4_OPAQUE_LIMIT];
uint32_t minorversion;
uint32_t argarray_count;
nfs_argop4 *argarray; /* <> */
} nfs41_compound_args;
typedef struct __nfs_resop4 {
uint32_t op;
void *res;
} nfs_resop4;
typedef struct __nfs41_compound_res {
uint32_t status;
uint32_t tag_len;
unsigned char tag[NFS4_OPAQUE_LIMIT];
uint32_t resarray_count;
nfs_resop4 *resarray; /* <> */
} nfs41_compound_res;
typedef struct __nfs41_compound {
nfs41_compound_args args;
nfs41_compound_res res;
} nfs41_compound;
int compound_error(int status);
void compound_init(
nfs41_compound *compound,
nfs_argop4 *argops,
nfs_resop4 *resops);
void compound_add_op(
nfs41_compound *compound,
uint32_t opnum,
void *arg,
void *res);
int compound_encode_send_decode(
nfs41_session *session,
nfs41_compound *compound,
uint32_t bufsize_in,
uint32_t bufsize_out);
#endif /* __NFS41_DAEMON_COMPOUND_H__ */

306
daemon/nfs41_const.h Normal file
View file

@ -0,0 +1,306 @@
/* 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 __NFS41_NFS_CONST_H__
#define __NFS41_NFS_CONST_H__
/*
* Sizes
*/
#define NFS4_FHSIZE 128
#define NFS4_VERIFIER_SIZE 8
#define NFS4_OPAQUE_LIMIT 1024
#define NFS4_SESSIONID_SIZE 16
#define NFS41_MAX_FILEIO_SIZE (1024 * 1024)
#define NFS41_MAX_SERVER_CACHE 1024
#define NFS41_MAX_RPC_REQS 128
#define NFS41_MAX_COMPONENT_SIZE 64
#define UPCALL_BUF_SIZE 1024
/* see MaximumComponentNameLength in FileFsAttributeInformation
* in nfs41_driver.c:nfs41_QueryVolumeInformation() */
#define NFS41_MAX_COMPONENT_LEN 64
#define NFS41_MAX_PATH_LEN MAX_PATH
#define NFS41_HOSTNAME_LEN 64
#define NFS41_ADDRS_PER_SERVER 4
/* max length of ipv6 address 48
* sizeof(".255.255") + 8 */
#define NFS41_UNIVERSAL_ADDR_LEN 56
/* "udp" "tcp" "udp6" "tcp6" */
#define NFS41_NETWORK_ID_LEN 4
/* 424 bytes: max rpc header for reply with data */
/* 32 bytes: max COMPOUND response */
/* 40 bytes: max SEQUENCE response */
/* 4 bytes: max PUTFH response */
/* 12 bytes: max READ response */
#define READ_OVERHEAD 512
/* 840 bytes: max rpc header for call */
/* 32 bytes: max COMPOUND request */
/* 32 bytes: max SEQUENCE request */
/* 132 bytes: max PUTFH request */
/* 32 bytes: max WRITE request */
#define WRITE_OVERHEAD 1068
#define NFS41_RPC_PROGRAM 100003
#define NFS41_RPC_VERSION 4
#define NFS41_RPC_CBPROGRAM 0x2358
/*
* Error status
*/
enum nfsstat4 {
NFS4_OK = 0, /* everything is okay */
NFS4ERR_PERM = 1, /* caller not privileged */
NFS4ERR_NOENT = 2, /* no such file/directory */
NFS4ERR_IO = 5, /* hard I/O error */
NFS4ERR_NXIO = 6, /* no such device */
NFS4ERR_ACCESS = 13, /* access denied */
NFS4ERR_EXIST = 17, /* file already exists */
NFS4ERR_XDEV = 18, /* different filesystems */
NFS4ERR_NOTDIR = 20, /* should be a directory */
NFS4ERR_ISDIR = 21, /* should not be directory */
NFS4ERR_INVAL = 22, /* invalid argument */
NFS4ERR_FBIG = 27, /* file exceeds server max */
NFS4ERR_NOSPC = 28, /* no space on filesystem */
NFS4ERR_ROFS = 30, /* read-only filesystem */
NFS4ERR_MLINK = 31, /* too many hard links */
NFS4ERR_NAMETOOLONG = 63, /* name exceeds server max */
NFS4ERR_NOTEMPTY = 66, /* directory not empty */
NFS4ERR_DQUOT = 69, /* hard quota limit reached*/
NFS4ERR_STALE = 70, /* file no longer exists */
NFS4ERR_BADHANDLE = 10001, /* Illegal filehandle */
NFS4ERR_BAD_COOKIE = 10003, /* READDIR cookie is stale */
NFS4ERR_NOTSUPP = 10004, /* operation not supported */
NFS4ERR_TOOSMALL = 10005, /* response limit exceeded */
NFS4ERR_SERVERFAULT = 10006, /* undefined server error */
NFS4ERR_BADTYPE = 10007, /* type invalid for CREATE */
NFS4ERR_DELAY = 10008, /* file "busy" - retry */
NFS4ERR_SAME = 10009, /* nverify says attrs same */
NFS4ERR_DENIED = 10010, /* lock unavailable */
NFS4ERR_EXPIRED = 10011, /* lock lease expired */
NFS4ERR_LOCKED = 10012, /* I/O failed due to lock */
NFS4ERR_GRACE = 10013, /* in grace period */
NFS4ERR_FHEXPIRED = 10014, /* filehandle expired */
NFS4ERR_SHARE_DENIED = 10015, /* share reserve denied */
NFS4ERR_WRONGSEC = 10016, /* wrong security flavor */
NFS4ERR_CLID_INUSE = 10017, /* clientid in use */
/* NFS4ERR_RESOURCE is not a valid error in NFSv4.1 */
NFS4ERR_RESOURCE = 10018, /* resource exhaustion */
NFS4ERR_MOVED = 10019, /* filesystem relocated */
NFS4ERR_NOFILEHANDLE = 10020, /* current FH is not set */
NFS4ERR_MINOR_VERS_MISMATCH = 10021, /* minor vers not supp */
NFS4ERR_STALE_CLIENTID = 10022, /* server has rebooted */
NFS4ERR_STALE_STATEID = 10023, /* server has rebooted */
NFS4ERR_OLD_STATEID = 10024, /* state is out of sync */
NFS4ERR_BAD_STATEID = 10025, /* incorrect stateid */
NFS4ERR_BAD_SEQID = 10026, /* request is out of seq. */
NFS4ERR_NOT_SAME = 10027, /* verify - attrs not same */
NFS4ERR_LOCK_RANGE = 10028, /* overlapping lock range */
NFS4ERR_SYMLINK = 10029, /* should be file/directory*/
NFS4ERR_RESTOREFH = 10030, /* no saved filehandle */
NFS4ERR_LEASE_MOVED = 10031, /* some filesystem moved */
NFS4ERR_ATTRNOTSUPP = 10032, /* recommended attr not sup*/
NFS4ERR_NO_GRACE = 10033, /* reclaim outside of grace*/
NFS4ERR_RECLAIM_BAD = 10034, /* reclaim error at server */
NFS4ERR_RECLAIM_CONFLICT = 10035, /* conflict on reclaim */
NFS4ERR_BADXDR = 10036, /* XDR decode failed */
NFS4ERR_LOCKS_HELD = 10037, /* file locks held at CLOSE*/
NFS4ERR_OPENMODE = 10038, /* conflict in OPEN and I/O*/
NFS4ERR_BADOWNER = 10039, /* owner translation bad */
NFS4ERR_BADCHAR = 10040, /* utf-8 char not supported*/
NFS4ERR_BADNAME = 10041, /* name not supported */
NFS4ERR_BAD_RANGE = 10042, /* lock range not supported*/
NFS4ERR_LOCK_NOTSUPP = 10043, /* no atomic up/downgrade */
NFS4ERR_OP_ILLEGAL = 10044, /* undefined operation */
NFS4ERR_DEADLOCK = 10045, /* file locking deadlock */
NFS4ERR_FILE_OPEN = 10046, /* open file blocks op. */
NFS4ERR_ADMIN_REVOKED = 10047, /* lockowner state revoked */
NFS4ERR_CB_PATH_DOWN = 10048, /* callback path down */
/* NFSv4.1 errors start here. */
NFS4ERR_BADIOMODE = 10049,
NFS4ERR_BADLAYOUT = 10050,
NFS4ERR_BAD_SESSION_DIGEST = 10051,
NFS4ERR_BADSESSION = 10052,
NFS4ERR_BADSLOT = 10053,
NFS4ERR_COMPLETE_ALREADY = 10054,
NFS4ERR_CONN_NOT_BOUND_TO_SESSION = 10055,
NFS4ERR_DELEG_ALREADY_WANTED = 10056,
NFS4ERR_BACK_CHAN_BUSY = 10057, /*backchan reqs outstanding*/
NFS4ERR_LAYOUTTRYLATER = 10058,
NFS4ERR_LAYOUTUNAVAILABLE = 10059,
NFS4ERR_NOMATCHING_LAYOUT = 10060,
NFS4ERR_RECALLCONFLICT = 10061,
NFS4ERR_UNKNOWN_LAYOUTTYPE = 10062,
NFS4ERR_SEQ_MISORDERED = 10063, /* unexpected seq.ID in req*/
NFS4ERR_SEQUENCE_POS = 10064, /* [CB_]SEQ. op not 1st op */
NFS4ERR_REQ_TOO_BIG = 10065, /* request too big */
NFS4ERR_REP_TOO_BIG = 10066, /* reply too big */
NFS4ERR_REP_TOO_BIG_TO_CACHE = 10067, /* rep. not all cached */
NFS4ERR_RETRY_UNCACHED_REP = 10068, /* retry & rep. uncached */
NFS4ERR_UNSAFE_COMPOUND = 10069, /* retry/recovery too hard */
NFS4ERR_TOO_MANY_OPS = 10070, /*too many ops in [CB_]COMP*/
NFS4ERR_OP_NOT_IN_SESSION = 10071, /* op needs [CB_]SEQ. op */
NFS4ERR_HASH_ALG_UNSUPP = 10072, /* hash alg. not supp. */
/* Error 10073 is unused. */
NFS4ERR_CLIENTID_BUSY = 10074, /* clientid has state */
NFS4ERR_PNFS_IO_HOLE = 10075, /* IO to _SPARSE file hole */
NFS4ERR_SEQ_FALSE_RETRY = 10076, /* Retry != original req. */
NFS4ERR_BAD_HIGH_SLOT = 10077, /* req has bad highest_slot*/
NFS4ERR_DEADSESSION = 10078, /*new req sent to dead sess*/
NFS4ERR_ENCR_ALG_UNSUPP = 10079, /* encr alg. not supp. */
NFS4ERR_PNFS_NO_LAYOUT = 10080, /* I/O without a layout */
NFS4ERR_NOT_ONLY_OP = 10081, /* addl ops not allowed */
NFS4ERR_WRONG_CRED = 10082, /* op done by wrong cred */
NFS4ERR_WRONG_TYPE = 10083, /* op on wrong type object */
NFS4ERR_DIRDELEG_UNAVAIL = 10084, /* delegation not avail. */
NFS4ERR_REJECT_DELEG = 10085, /* cb rejected delegation */
NFS4ERR_RETURNCONFLICT = 10086, /* layout get before return*/
NFS4ERR_DELEG_REVOKED = 10087 /* deleg./layout revoked */
};
#define MAKE_WORD0(XXX) (1 << XXX)
#define MAKE_WORD1(XXX) (1 << (XXX-32))
#define MAKE_WORD2(XXX) (1 << (XXX-64))
enum {
/*
* Mandatory Attributes
*/
FATTR4_WORD0_SUPPORTED_ATTRS = MAKE_WORD0(0),
FATTR4_WORD0_TYPE = MAKE_WORD0(1),
FATTR4_WORD0_FH_EXPIRE_TYPE = MAKE_WORD0(2),
FATTR4_WORD0_CHANGE = MAKE_WORD0(3),
FATTR4_WORD0_SIZE = MAKE_WORD0(4),
FATTR4_WORD0_LINK_SUPPORT = MAKE_WORD0(5),
FATTR4_WORD0_SYMLINK_SUPPORT = MAKE_WORD0(6),
FATTR4_WORD0_NAMED_ATTR = MAKE_WORD0(7),
FATTR4_WORD0_FSID = MAKE_WORD0(8),
FATTR4_WORD0_UNIQUE_HANDLES = MAKE_WORD0(9),
FATTR4_WORD0_LEASE_TIME = MAKE_WORD0(10),
FATTR4_WORD0_RDATTR_ERROR = MAKE_WORD0(11),
FATTR4_WORD0_FILEHANDLE = MAKE_WORD0(19),
FATTR4_WORD2_SUPPATTR_EXCLCREAT = MAKE_WORD2(75),
/*
* Recommended Attributes
*/
FATTR4_WORD0_ACL = MAKE_WORD0(12),
FATTR4_WORD0_ACLSUPPORT = MAKE_WORD0(13),
FATTR4_WORD0_ARCHIVE = MAKE_WORD0(14),
FATTR4_WORD0_CANSETTIME = MAKE_WORD0(15),
FATTR4_WORD0_CASE_INSENSITIVE = MAKE_WORD0(16),
FATTR4_WORD0_CASE_PRESERVING = MAKE_WORD0(17),
FATTR4_WORD0_CHOWN_RESTRICTED = MAKE_WORD0(18),
FATTR4_WORD0_FILEID = MAKE_WORD0(20),
FATTR4_WORD0_FILES_AVAIL = MAKE_WORD0(21),
FATTR4_WORD0_FILES_FREE = MAKE_WORD0(22),
FATTR4_WORD0_FILES_TOTAL = MAKE_WORD0(23),
FATTR4_WORD0_FS_LOCATIONS = MAKE_WORD0(24),
FATTR4_WORD0_HIDDEN = MAKE_WORD0(25),
FATTR4_WORD0_HOMOGENEOUS = MAKE_WORD0(26),
FATTR4_WORD0_MAXFILESIZE = MAKE_WORD0(27),
FATTR4_WORD0_MAXLINK = MAKE_WORD0(28),
FATTR4_WORD0_MAXNAME = MAKE_WORD0(29),
FATTR4_WORD0_MAXREAD = MAKE_WORD0(30),
FATTR4_WORD0_MAXWRITE = MAKE_WORD0(31),
FATTR4_WORD1_MIMETYPE = MAKE_WORD1(32),
FATTR4_WORD1_MODE = MAKE_WORD1(33),
FATTR4_WORD1_NO_TRUNC = MAKE_WORD1(34),
FATTR4_WORD1_NUMLINKS = MAKE_WORD1(35),
FATTR4_WORD1_OWNER = MAKE_WORD1(36),
FATTR4_WORD1_OWNER_GROUP = MAKE_WORD1(37),
FATTR4_WORD1_QUOTA_AVAIL_HARD = MAKE_WORD1(38),
FATTR4_WORD1_QUOTA_AVAIL_SOFT = MAKE_WORD1(39),
FATTR4_WORD1_QUOTA_USED = MAKE_WORD1(40),
FATTR4_WORD1_RAWDEV = MAKE_WORD1(41),
FATTR4_WORD1_SPACE_AVAIL = MAKE_WORD1(42),
FATTR4_WORD1_SPACE_FREE = MAKE_WORD1(43),
FATTR4_WORD1_SPACE_TOTAL = MAKE_WORD1(44),
FATTR4_WORD1_SPACE_USED = MAKE_WORD1(45),
FATTR4_WORD1_SYSTEM = MAKE_WORD1(46),
FATTR4_WORD1_TIME_ACCESS = MAKE_WORD1(47),
FATTR4_WORD1_TIME_ACCESS_SET = MAKE_WORD1(48),
FATTR4_WORD1_TIME_BACKUP = MAKE_WORD1(49),
FATTR4_WORD1_TIME_CREATE = MAKE_WORD1(50),
FATTR4_WORD1_TIME_DELTA = MAKE_WORD1(51),
FATTR4_WORD1_TIME_METADATA = MAKE_WORD1(52),
FATTR4_WORD1_TIME_MODIFY = MAKE_WORD1(53),
FATTR4_WORD1_TIME_MODIFY_SET = MAKE_WORD1(54),
FATTR4_WORD1_MOUNTED_ON_FILEID = MAKE_WORD1(55),
FATTR4_WORD1_DIR_NOTIF_DELAY = MAKE_WORD1(56),
FATTR4_WORD1_DIRENT_NOTIF_DELAY = MAKE_WORD1(57),
FATTR4_WORD1_DACL = MAKE_WORD1(58),
FATTR4_WORD1_SACL = MAKE_WORD1(59),
FATTR4_WORD1_CHANGE_POLICY = MAKE_WORD1(60),
FATTR4_WORD1_FS_STATUS = MAKE_WORD1(61),
FATTR4_WORD1_FS_LAYOUT_TYPE = MAKE_WORD1(62),
FATTR4_WORD1_LAYOUT_HINT = MAKE_WORD1(63),
FATTR4_WORD2_LAYOUT_TYPE = MAKE_WORD2(64),
FATTR4_WORD2_LAYOUT_BLKSIZE = MAKE_WORD2(65),
FATTR4_WORD2_LAYOUT_ALIGNMENT = MAKE_WORD2(66),
FATTR4_WORD2_FS_LOCATIONS_INFO = MAKE_WORD2(67),
FATTR4_WORD2_MDSTHRESHOLD = MAKE_WORD2(68),
FATTR4_WORD2_RETENTION_GET = MAKE_WORD2(69),
FATTR4_WORD2_RETENTION_SET = MAKE_WORD2(70),
FATTR4_WORD2_RETENTEVT_GET = MAKE_WORD2(71),
FATTR4_WORD2_RETENTEVT_SET = MAKE_WORD2(72),
FATTR4_WORD2_RETENTION_HOLD = MAKE_WORD2(73),
FATTR4_WORD2_MODE_SET_MASKED = MAKE_WORD2(74),
FATTR4_WORD2_FS_CHARSET_CAP = MAKE_WORD2(76),
};
/*
* File types
*/
enum nfs_ftype4 {
NF4REG = 1, /* Regular File */
NF4DIR = 2, /* Directory */
NF4BLK = 3, /* Special File - block device */
NF4CHR = 4, /* Special File - character device */
NF4LNK = 5, /* Symbolic Link */
NF4SOCK = 6, /* Special File - socket */
NF4FIFO = 7, /* Special File - fifo */
NF4ATTRDIR = 8, /* Attribute Directory */
NF4NAMEDATTR = 9, /* Named Attribute */
NFS_FTYPE_MASK = 0xF
};
#define CREATE_SESSION4_FLAG_CONN_BACK_CHAN 0x00000002;
#endif /* !__NFS41_NFS_CONST_H__ */

217
daemon/nfs41_daemon.c Normal file
View file

@ -0,0 +1,217 @@
/* 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 <Windows.h>
#include <process.h>
#include <tchar.h>
#include <stdio.h>
#include <devioctl.h>
#include "nfs41_driver.h" /* for NFS41_USER_DEVICE_NAME_A */
#include "nfs41_np.h" /* for NFS41NP_SHARED_MEMORY */
#include "daemon_debug.h"
#include "upcall.h"
#include "util.h"
#define MAX_NUM_THREADS 128
BOOLEAN CREATED_SESSION = FALSE;
typedef struct _nfs41_process_thread {
HANDLE handle;
uint32_t tid;
} nfs41_process_thread;
DWORD InitSharedMemory(
OUT PHANDLE phMutex)
{
DWORD status = NO_ERROR;
*phMutex = CreateMutex(NULL, FALSE, TEXT(NFS41NP_MUTEX_NAME));
if (*phMutex == NULL) {
status = GetLastError();
eprintf("CreateMutex failed with %d\n", status);
}
return status;
}
static unsigned int WINAPI thread_main(void *args)
{
DWORD status = 0;
HANDLE pipe;
// buffer used to process upcall, assumed to be fixed size.
// if we ever need to handle non-cached IO, need to make it dynamic
unsigned char outbuf[UPCALL_BUF_SIZE];
// buffer used to send downcall content, need to dynamically allocated
// as we don't know the length of the buffer (ie. size of directory listing
unsigned char *inbuf = NULL;
DWORD inbuf_len, outbuf_len;
nfs41_upcall upcall;
pipe = CreateFile(NFS41_USER_DEVICE_NAME_A, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
0, NULL);
if (pipe == INVALID_HANDLE_VALUE)
{
eprintf("Unable to open upcall pipe %d\n", GetLastError());
return GetLastError();
}
while(1) {
status = DeviceIoControl(pipe, IOCTL_NFS41_READ, NULL, 0,
outbuf, UPCALL_BUF_SIZE, (LPDWORD)&outbuf_len, NULL);
if (!status) {
eprintf("IOCTL_NFS41_READ failed %d\n", GetLastError());
continue;
}
status = upcall_parse(outbuf, (uint32_t)outbuf_len, &upcall);
if (status)
goto write_downcall;
#if 1 //AGLO: this is just a placeholder for a real solution. I know this variable needs a lock in a
//normal case. However, this does not prevent us from receiving an upcall for an old mount
//that was not reestablished. It will only work for erroring requests until the 1st mount upcall.
if (!CREATED_SESSION && (upcall.opcode != NFS41_MOUNT && upcall.opcode != NFS41_SHUTDOWN)) {
eprintf("nfs41_daemon restarted and does not have a valid session established\n");
upcall.status = 116;
goto write_downcall;
}
#endif
if (upcall.opcode == NFS41_SHUTDOWN) {
printf("Shutting down..\n");
exit(0);
}
status = upcall_handle(&upcall);
#if 1 //AGLO: place holder for a real solution
if (upcall.opcode == NFS41_MOUNT && upcall.status == NO_ERROR)
CREATED_SESSION = 1;
#endif
write_downcall:
dprintf(1, "writing downcall: xid=%d opcode=%s status=%d "
"get_last_error=%d\n", upcall.xid, opcode2string(upcall.opcode),
upcall.status, upcall.last_error);
if (upcall.opcode == NFS41_DIR_QUERY)
inbuf_len = UPCALL_BUF_SIZE + upcall.args.readdir.query_reply_len;
else
inbuf_len = UPCALL_BUF_SIZE;
inbuf = malloc(inbuf_len);
status = upcall_marshall(&upcall, inbuf, (uint32_t)inbuf_len, (uint32_t*)&outbuf_len);
dprintf(2, "making a downcall: outbuf_len %ld\n", outbuf_len);
status = DeviceIoControl(pipe, IOCTL_NFS41_WRITE,
inbuf, inbuf_len, NULL, 0, (LPDWORD)&outbuf_len, NULL);
free(inbuf);
if (!status) {
eprintf("IOCTL_NFS41_WRITE failed with %d xid=%d opcode=%s\n",
GetLastError(), upcall.xid, opcode2string(upcall.opcode));
status = upcall_cancel(&upcall);
continue;
}
dprintf(3, "downcall returned %d\n", status);
printf("\n");
}
CloseHandle(pipe);
return GetLastError();
}
void __cdecl _tmain(int argc, TCHAR *argv[])
{
DWORD status = 0, len;
// handles related to shared memory
HANDLE hSharedMemoryMutex;
// handle to our drivers
HANDLE pipe;
nfs41_process_thread tids[MAX_NUM_THREADS];
int i;
if (argc > 2) {
const char *process = strip_path(argv[0], NULL);
printf("Usage: %s [#debug level]\n", process);
} else if (argc == 2) {
set_debug_level(_ttoi(argv[1]));
}
#ifdef _DEBUG
/* dump memory leaks to stderr on exit; this requires the debug heap,
/* available only when built in debug mode under visual studio -cbodley */
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
dprintf(1, "debug mode. dumping memory leaks to stderr on exit.\n");
#endif
nfs41_server_list_init();
pipe = CreateFile(NFS41_USER_DEVICE_NAME_A, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
0, NULL);
if (pipe == INVALID_HANDLE_VALUE)
{
eprintf("Unable to open upcall pipe %d\n", GetLastError());
return;
}
status = InitSharedMemory(&hSharedMemoryMutex);
if (status)
goto quit_pipe;
dprintf(1, "starting nfs41 mini redirector\n");
status = DeviceIoControl(pipe, IOCTL_NFS41_START,
NULL, 0, NULL, 0, (LPDWORD)&len, NULL);
if (!status) {
eprintf("IOCTL_NFS41_START failed with %d\n",
GetLastError());
goto quit;
}
for (i = 0; i < MAX_NUM_THREADS; i++) {
tids[i].handle = (HANDLE)_beginthreadex(NULL, 0, thread_main,
NULL, 0, &tids[i].tid);
if (tids[i].handle == INVALID_HANDLE_VALUE) {
status = GetLastError();
eprintf("_beginthreadex failed %d\n", status);
goto quit_pipe;
}
}
//This can be changed to waiting on an array of handles and using waitformultipleobjects
dprintf(1, "Parent waiting for children threads\n");
for (i = 0; i < MAX_NUM_THREADS; i++)
WaitForSingleObject(tids[i].handle, INFINITE );
dprintf(1, "Parent woke up!!!!\n");
quit:
CloseHandle(hSharedMemoryMutex);
quit_pipe:
CloseHandle(pipe);
return;
}

1745
daemon/nfs41_ops.c Normal file

File diff suppressed because it is too large Load diff

1074
daemon/nfs41_ops.h Normal file

File diff suppressed because it is too large Load diff

298
daemon/nfs41_rpc.c Normal file
View file

@ -0,0 +1,298 @@
/* 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 "nfs41.h"
#include "daemon_debug.h"
#include "nfs41_xdr.h"
#include "nfs41_callback.h"
#include "rpc/rpc.h"
static enum clnt_stat send_null(CLIENT *client)
{
struct timeval timeout = {10, 0};
return clnt_call(client, 0,
(xdrproc_t)xdr_void, NULL,
(xdrproc_t)xdr_void, NULL, timeout);
}
static int get_client_for_netaddr(
IN const netaddr4 *netaddr,
IN uint32_t wsize,
IN uint32_t rsize,
IN nfs41_rpc_clnt *rpc,
OUT CLIENT **client_out)
{
int status = ERROR_NETWORK_UNREACHABLE;
struct netconfig *nconf;
struct netbuf *addr;
CLIENT *client;
nconf = getnetconfigent(netaddr->netid);
if (nconf == NULL)
goto out;
addr = uaddr2taddr(nconf, netaddr->uaddr);
if (addr == NULL)
goto out_free_conf;
dprintf(1, "callback function %p args %p\n", nfs41_handle_callback, rpc);
client = clnt_tli_create(RPC_ANYFD, nconf, addr,
NFS41_RPC_PROGRAM, NFS41_RPC_VERSION, wsize, rsize,
rpc?proc_cb_compound_res:NULL, rpc?nfs41_handle_callback:NULL, rpc?rpc:NULL);
if (client) {
*client_out = client;
status = NO_ERROR;
goto out_free_addr;
}
out_free_addr:
freenetbuf(addr);
out_free_conf:
freenetconfigent(nconf);
out:
return status;
}
static int get_client_for_multi_addr(
IN const multi_addr4 *addrs,
IN uint32_t wsize,
IN uint32_t rsize,
IN nfs41_rpc_clnt *rpc,
OUT CLIENT **client_out,
OUT uint32_t *addr_index)
{
int status = ERROR_NETWORK_UNREACHABLE;
uint32_t i;
for (i = 0; i < addrs->count; i++) {
status = get_client_for_netaddr(&addrs->arr[i],
wsize, rsize, rpc, client_out);
if (status == NO_ERROR) {
*addr_index = i;
break;
}
}
return status;
}
/* Returns a client structure and an associated lock */
int nfs41_rpc_clnt_create(
IN const multi_addr4 *addrs,
IN uint32_t wsize,
IN uint32_t rsize,
bool_t needcb,
OUT nfs41_rpc_clnt **rpc_out)
{
CLIENT *client;
nfs41_rpc_clnt *rpc;
uint32_t addr_index;
int status;
rpc = calloc(1, sizeof(nfs41_rpc_clnt));
if (rpc == NULL) {
status = GetLastError();
goto out;
}
rpc->cond = CreateEvent(NULL, TRUE, FALSE, "rpc_recovery_cond");
if (rpc->cond == NULL) {
status = GetLastError();
fprintf(stderr, "CreateEvent failed %d\n", status);
goto out_free_rpc_clnt;
}
status = get_client_for_multi_addr(addrs,
wsize, rsize, needcb?rpc:NULL, &client, &addr_index);
if (status) {
clnt_pcreateerror("connecting failed");
goto out_free_rpc_clnt;
}
// XXX Pick credentials in better manner
client->cl_auth = authsys_create_default();
if (client->cl_auth == NULL) {
// XXX log failure in auth creation somewhere
// XXX Better error return
status = ERROR_NETWORK_UNREACHABLE;
goto out_err_client;
}
if (send_null(client) != RPC_SUCCESS) {
// XXX Do what here?
dprintf(1, " send_null failed\n");
status = ERROR_NETWORK_UNREACHABLE;
goto out_err_auth;
}
rpc->rpc = client;
/* keep a copy of the address and buffer sizes for reconnect */
memcpy(&rpc->addrs, addrs, sizeof(multi_addr4));
/* save the index of the address we connected to */
rpc->addr_index = addr_index;
rpc->wsize = wsize;
rpc->rsize = rsize;
rpc->is_valid_session = TRUE;
//initialize rpc client lock
InitializeSRWLock(&rpc->lock);
*rpc_out = rpc;
out:
return status;
out_err_auth:
auth_destroy(client->cl_auth);
out_err_client:
CloseHandle(rpc->cond);
clnt_destroy(client);
out_free_rpc_clnt:
free(rpc);
goto out;
}
/* Frees resources allocated in clnt_create */
void nfs41_rpc_clnt_free(
IN nfs41_rpc_clnt *rpc)
{
auth_destroy(rpc->rpc->cl_auth);
clnt_destroy(rpc->rpc);
CloseHandle(rpc->cond);
free(rpc);
}
static bool_t rpc_renew_in_progress(nfs41_rpc_clnt *rpc, int *value)
{
bool_t status = FALSE;
AcquireSRWLockExclusive(&rpc->lock);
if (value) {
dprintf(1, "nfs41_rpc_renew_in_progress: setting value %d\n", *value);
rpc->in_recovery = *value;
if (!rpc->in_recovery)
SetEvent(rpc->cond);
} else {
status = rpc->in_recovery;
dprintf(1, "nfs41_rpc_renew_in_progress: returning value %d\n", status);
}
ReleaseSRWLockExclusive(&rpc->lock);
return status;
}
static bool_t rpc_should_retry(nfs41_rpc_clnt *rpc, uint32_t version)
{
bool_t status = 0;
AcquireSRWLockExclusive(&rpc->lock);
if (rpc->version > version)
status = 1;
ReleaseSRWLockExclusive(&rpc->lock);
return status;
}
static int rpc_reconnect(
IN nfs41_rpc_clnt *rpc)
{
CLIENT *client = NULL;
uint32_t addr_index;
int status;
AcquireSRWLockExclusive(&rpc->lock);
status = get_client_for_multi_addr(&rpc->addrs,
rpc->wsize, rpc->rsize, rpc, &client, &addr_index);
if (status)
goto out_unlock;
client->cl_auth = rpc->rpc->cl_auth;
if (send_null(client) != RPC_SUCCESS)
goto out_err_client;
clnt_destroy(rpc->rpc);
rpc->rpc = client;
rpc->addr_index = addr_index;
rpc->version++;
dprintf(1, "nfs41_send_compound: reestablished RPC connection\n");
out_unlock:
ReleaseSRWLockExclusive(&rpc->lock);
return status;
out_err_client:
clnt_destroy(client);
goto out_unlock;
}
int nfs41_send_compound(
IN nfs41_rpc_clnt *rpc,
IN char *inbuf,
OUT char *outbuf)
{
struct timeval timeout = {10, 0};
enum clnt_stat rpc_status;
int status, count = 0, one = 1, zero = 0;
uint32_t version;
try_again:
AcquireSRWLockShared(&rpc->lock);
version = rpc->version;
rpc_status = clnt_call(rpc->rpc, 1,
(xdrproc_t)nfs_encode_compound, inbuf,
(xdrproc_t)nfs_decode_compound, outbuf,
timeout);
ReleaseSRWLockShared(&rpc->lock);
if (rpc_status != RPC_SUCCESS) {
eprintf("clnt_call returned rpc_status=%i\n", rpc_status);
switch(rpc_status) {
case RPC_CANTRECV:
case RPC_CANTSEND:
case RPC_TIMEDOUT:
if (!rpc->is_valid_session && ++count > 3) {
status = ERROR_NETWORK_UNREACHABLE;
break;
}
if (rpc_should_retry(rpc, version))
goto try_again;
while (rpc_renew_in_progress(rpc, NULL)) {
status = WaitForSingleObject(rpc->cond, INFINITE);
if (status != WAIT_OBJECT_0) {
dprintf(1, "nfs41_rpc_renew_in_progress: WaitForSingleObject failed\n");
print_condwait_status(1, status);
status = ERROR_LOCK_VIOLATION;
goto out;
}
rpc_renew_in_progress(rpc, &zero);
goto try_again;
}
rpc_renew_in_progress(rpc, &one);
if (rpc_reconnect(rpc))
eprintf("Failed to reconnect!\n");
rpc_renew_in_progress(rpc, &zero);
goto try_again;
default:
eprintf("UNHANDLED RPC_ERROR: %d\n", rpc_status);
status = ERROR_NETWORK_UNREACHABLE;
break;
}
goto out;
}
status = 0;
out:
return status;
}

332
daemon/nfs41_server.c Normal file
View file

@ -0,0 +1,332 @@
/* 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 <Windows.h>
#include <strsafe.h>
#include <stdio.h>
#include "wintirpc.h"
#include "rpc/rpc.h"
#include "name_cache.h"
#include "daemon_debug.h"
#include "nfs41.h"
#include "util.h"
#define SRVLVL 2 /* dprintf level for server logging */
/* nfs41_server_list */
struct server_list {
struct list_entry head;
CRITICAL_SECTION lock;
};
static struct server_list g_server_list;
#define server_entry(pos) list_container(pos, nfs41_server, entry)
void nfs41_server_list_init()
{
list_init(&g_server_list.head);
InitializeCriticalSection(&g_server_list.lock);
}
/* http://tools.ietf.org/html/rfc5661#section-1.6
* 1.6. General Definitions: Server Owner:
* "When the client has two connections each to a peer with the same major
* identifier, the client assumes that both peers are the same server (the
* server namespace is the same via each connection)" */
/* http://tools.ietf.org/html/rfc5661#section-2.10.4
* 2.10.4. Server Scope
* "When the server scope values are the same, server owner value may be
* validly compared. In cases where the server scope values are different,
* server owner values are treated as different even if they contain all
* identical bytes." */
/* given these definitions, we require that both the server_owner.major_id
* and server_scope are identical when matching instances of nfs41_server */
struct server_info {
const char *scope;
const char *owner;
};
static int server_compare(
const struct list_entry *entry,
const void *value)
{
const nfs41_server *server = server_entry(entry);
const struct server_info *info = (const struct server_info*)value;
const int diff = strncmp(server->scope, info->scope, NFS4_OPAQUE_LIMIT);
return diff ? diff : strncmp(server->owner, info->owner, NFS4_OPAQUE_LIMIT);
}
static int server_entry_find(
IN struct server_list *servers,
IN const struct server_info *info,
OUT struct list_entry **entry_out)
{
*entry_out = list_search(&servers->head, info, server_compare);
return *entry_out ? NO_ERROR : ERROR_FILE_NOT_FOUND;
}
static int server_create(
IN const struct server_info *info,
OUT nfs41_server **server_out)
{
int status = NO_ERROR;
nfs41_server *server;
server = calloc(1, sizeof(nfs41_server));
if (server == NULL) {
status = GetLastError();
eprintf("failed to allocate server %s\n", info->owner);
goto out;
}
StringCchCopyA(server->scope, NFS4_OPAQUE_LIMIT, info->scope);
StringCchCopyA(server->owner, NFS4_OPAQUE_LIMIT, info->owner);
InitializeSRWLock(&server->addrs.lock);
nfs41_superblock_list_init(&server->superblocks);
nfs41_name_cache_create(&server->name_cache);
*server_out = server;
out:
return status;
}
static void server_free(
IN nfs41_server *server)
{
dprintf(SRVLVL, "server_free(%s)\n", server->owner);
nfs41_superblock_list_free(&server->superblocks);
nfs41_name_cache_free(&server->name_cache);
free(server);
}
static __inline void server_ref_locked(
IN nfs41_server *server)
{
server->ref_count++;
dprintf(SRVLVL, "nfs41_server_ref(%s) count %d\n",
server->owner, server->ref_count);
}
void nfs41_server_ref(
IN nfs41_server *server)
{
EnterCriticalSection(&g_server_list.lock);
server_ref_locked(server);
LeaveCriticalSection(&g_server_list.lock);
}
void nfs41_server_deref(
IN nfs41_server *server)
{
EnterCriticalSection(&g_server_list.lock);
server->ref_count--;
dprintf(SRVLVL, "nfs41_server_deref(%s) count %d\n",
server->owner, server->ref_count);
if (server->ref_count == 0) {
list_remove(&server->entry);
server_free(server);
}
LeaveCriticalSection(&g_server_list.lock);
}
static void server_addrs_add(
IN OUT struct server_addrs *addrs,
IN const netaddr4 *addr)
{
/* we keep a list of addrs used to connect to each server. once it gets
* bigger than NFS41_ADDRS_PER_SERVER, overwrite the oldest addrs. use
* server_addrs.next_index to implement a circular array */
AcquireSRWLockExclusive(&addrs->lock);
if (multi_addr_find(&addrs->addrs, addr, NULL)) {
dprintf(SRVLVL, "server_addrs_add() found existing addr '%s'.\n",
addr->uaddr);
} else {
/* overwrite the address at 'next_index' */
StringCchCopyA(addrs->addrs.arr[addrs->next_index].netid,
NFS41_NETWORK_ID_LEN, addr->netid);
StringCchCopyA(addrs->addrs.arr[addrs->next_index].uaddr,
NFS41_UNIVERSAL_ADDR_LEN, addr->uaddr);
/* increment/wrap next_index */
addrs->next_index = (addrs->next_index + 1) % NFS41_ADDRS_PER_SERVER;
/* update addrs.count if necessary */
if (addrs->addrs.count < addrs->next_index)
addrs->addrs.count = addrs->next_index;
dprintf(SRVLVL, "server_addrs_add() added new addr '%s'.\n",
addr->uaddr);
}
ReleaseSRWLockExclusive(&addrs->lock);
}
void nfs41_server_addrs(
IN nfs41_server *server,
OUT multi_addr4 *addrs)
{
struct server_addrs *saddrs = &server->addrs;
uint32_t i, j;
/* make a copy of the server's addrs, with most recent first */
AcquireSRWLockShared(&saddrs->lock);
j = saddrs->next_index;
for (i = 0; i < saddrs->addrs.count; i++) {
/* decrement/wrap j */
j = (NFS41_ADDRS_PER_SERVER + j - 1) % NFS41_ADDRS_PER_SERVER;
memcpy(&addrs->arr[i], &saddrs->addrs.arr[j], sizeof(netaddr4));
}
ReleaseSRWLockShared(&saddrs->lock);
}
int nfs41_server_find_or_create(
IN const char *server_owner_major_id,
IN const char *server_scope,
IN const netaddr4 *addr,
OUT nfs41_server **server_out)
{
const struct server_info info = { server_scope, server_owner_major_id };
struct list_entry *entry;
nfs41_server *server;
int status;
dprintf(SRVLVL, "--> nfs41_server_find_or_create(%s)\n", info.owner);
EnterCriticalSection(&g_server_list.lock);
/* search for an existing server */
status = server_entry_find(&g_server_list, &info, &entry);
if (status) {
/* create a new server */
status = server_create(&info, &server);
if (status == NO_ERROR) {
/* add it to the list */
list_add_tail(&g_server_list.head, &server->entry);
*server_out = server;
dprintf(SRVLVL, "<-- nfs41_server_find_or_create() "
"returning new server %p\n", server);
} else {
dprintf(SRVLVL, "<-- nfs41_server_find_or_create() "
"returning %d\n", status);
}
} else {
server = server_entry(entry);
dprintf(SRVLVL, "<-- nfs41_server_find_or_create() "
"returning existing server %p\n", server);
}
if (server) {
/* register the address used to connect */
server_addrs_add(&server->addrs, addr);
server_ref_locked(server);
}
*server_out = server;
LeaveCriticalSection(&g_server_list.lock);
return status;
}
int nfs41_server_resolve(
IN const char *hostname,
IN unsigned short port,
OUT multi_addr4 *addrs)
{
int status = ERROR_BAD_NET_NAME;
char service[16];
struct addrinfo hints, *res, *info;
struct netconfig *nconf;
struct netbuf addr;
char *netid, *uaddr;
dprintf(SRVLVL, "--> nfs41_server_resolve(%s:%u)\n",
hostname, port);
addrs->count = 0;
StringCchPrintfA(service, 16, "%u", port);
/* request a list of tcp addrs for the given hostname,port */
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if (getaddrinfo(hostname, service, &hints, &res) != 0)
goto out;
for (info = res; info != NULL; info = info->ai_next) {
/* find the appropriate entry in /etc/netconfig */
switch (info->ai_family) {
case AF_INET: netid = "tcp"; break;
case AF_INET6: netid = "tcp6"; break;
default: continue;
}
nconf = getnetconfigent(netid);
if (nconf == NULL)
continue;
/* convert to a transport-independent universal address */
addr.buf = info->ai_addr;
addr.maxlen = addr.len = (unsigned int)info->ai_addrlen;
uaddr = taddr2uaddr(nconf, &addr);
freenetconfigent(nconf);
if (uaddr == NULL)
continue;
StringCchCopyA(addrs->arr[addrs->count].netid,
NFS41_NETWORK_ID_LEN+1, netid);
StringCchCopyA(addrs->arr[addrs->count].uaddr,
NFS41_UNIVERSAL_ADDR_LEN+1, uaddr);
freeuaddr(uaddr);
status = NO_ERROR;
if (++addrs->count >= NFS41_ADDRS_PER_SERVER)
break;
}
freeaddrinfo(res);
out:
if (status)
dprintf(SRVLVL, "<-- nfs41_server_resolve(%s:%u) returning "
"error %d\n", hostname, port, status);
else
dprintf(SRVLVL, "<-- nfs41_server_resolve(%s:%u) returning "
"%s\n", hostname, port, addrs->arr[0].uaddr);
return status;
}

385
daemon/nfs41_session.c Normal file
View file

@ -0,0 +1,385 @@
/* 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 <Windows.h>
#include <process.h>
#include <stdio.h>
#include "daemon_debug.h"
#include "nfs41_ops.h"
#include "util.h"
/* session slot mechanism */
static int init_slot_table(nfs41_slot_table *table)
{
int i, status = 0;
//initialize slot table lock
table->lock = CreateMutex(NULL, FALSE, "session_table_lock");
if (table->lock == NULL) {
status = GetLastError();
eprintf("CreateMutext failed %d\n", status);
goto out;
}
//initialize condition variable for slots
table->cond = CreateEvent(NULL, TRUE, FALSE, "session_table_cond");
if (table->cond == NULL) {
status = GetLastError();
eprintf("CreateEvent failed %d\n", status);
goto out_mutex;
}
table->max_slots = NFS41_MAX_NUM_SLOTS;
for(i = 0; i < NFS41_MAX_NUM_SLOTS; i++) {
table->seq_nums[i] = 1;
table->used_slots[i] = 0;
}
table->highest_used = 0;
out:
return status;
out_mutex:
CloseHandle(table->lock);
goto out;
}
static int reinit_slot_table(nfs41_slot_table *table)
{
int i, status = 0;
status = WaitForSingleObject(table->lock, INFINITE);
if (status != WAIT_OBJECT_0) {
dprintf(1, "nfs41_session_bump_seq: WaitForSingleObject failed\n");
print_condwait_status(1, status);
status = ERROR_LOCK_VIOLATION;
goto out;
}
table->max_slots = NFS41_MAX_NUM_SLOTS;
for(i = 0; i < NFS41_MAX_NUM_SLOTS; i++) {
table->seq_nums[i] = 1;
table->used_slots[i] = 0;
}
table->highest_used = 0;
SetEvent(table->cond);
ReleaseMutex(table->lock);
out:
return status;
}
static void free_slot_table(nfs41_slot_table *table)
{
CloseHandle(table->lock);
CloseHandle(table->cond);
}
int nfs41_session_bump_seq(
IN nfs41_session *session,
IN uint32_t slotid)
{
int status;
AcquireSRWLockShared(&session->client->session_lock);
status = WaitForSingleObject(session->table.lock, INFINITE);
if (status != WAIT_OBJECT_0) {
dprintf(1, "nfs41_session_bump_seq: WaitForSingleObject failed\n");
print_condwait_status(1, status);
status = ERROR_LOCK_VIOLATION;
goto out;
}
session->table.seq_nums[slotid]++;
ReleaseMutex(session->table.lock);
out:
ReleaseSRWLockShared(&session->client->session_lock);
return status;
}
int nfs41_session_free_slot(
IN nfs41_session *session,
IN uint32_t slotid)
{
int status, i;
AcquireSRWLockShared(&session->client->session_lock);
status = WaitForSingleObject(session->table.lock, INFINITE);
if (status != WAIT_OBJECT_0) {
dprintf(1, "nfs41_session_free_slot: WaitForSingleObject failed\n");
print_condwait_status(1, status);
status = ERROR_LOCK_VIOLATION;
goto out;
}
session->table.used_slots[slotid] = 0;
if (slotid == session->table.highest_used) {
session->table.highest_used = 0;
for (i = slotid; i > 0; i--) {
if (session->table.used_slots[i]) {
session->table.highest_used = i;
break;
}
}
}
dprintf(3, "freeing slot#=%d highest=%d\n", slotid, session->table.highest_used);
SetEvent(session->table.cond);
ReleaseMutex(session->table.lock);
out:
ReleaseSRWLockShared(&session->client->session_lock);
return status;
}
int nfs41_session_get_slot(
IN nfs41_session *session,
OUT uint32_t *slot,
OUT uint32_t *seq,
OUT uint32_t *highest)
{
uint32_t status = NO_ERROR;
uint32_t i;
AcquireSRWLockShared(&session->client->session_lock);
look_for_slot:
status = WaitForSingleObject(session->table.lock, INFINITE);
if (status != WAIT_OBJECT_0) {
dprintf(1, "nfs41_session_get_slot: WaitForSingleObject failed\n");
print_condwait_status(1, status);
status = ERROR_LOCK_VIOLATION;
goto out;
}
dprintf(3, "looking for a free slot in the slot table\n");
*highest = session->table.highest_used;
for (i = 0; i < session->table.max_slots; i++) {
if (!session->table.used_slots[i]) {
session->table.used_slots[i] = 1; // mark slot used
*slot = i; // return slot number
*seq = session->table.seq_nums[i]; // return sequence number for the slot
//update highest_slot_used if needed
if (i > session->table.highest_used)
*highest = session->table.highest_used = i;
break;
}
}
if (i == session->table.max_slots) {
dprintf(1, "all (%d) slots are used. waiting for a free slot\n", session->table.max_slots);
ReleaseMutex(session->table.lock);
status = WaitForSingleObject(session->table.cond, INFINITE);
if (status == WAIT_OBJECT_0) {
dprintf(1, "received a signal to look for a free slot\n");
ResetEvent(session->table.cond);
goto look_for_slot;
}
}
ReleaseMutex(session->table.lock);
dprintf(2, "session %p: using slot#=%d with seq#=%d highest=%d\n", session, *slot, *seq, *highest);
out:
ReleaseSRWLockShared(&session->client->session_lock);
return status;
}
int nfs41_session_sequence(
nfs41_sequence_args *args,
nfs41_session *session,
bool_t cachethis)
{
uint32_t status = NO_ERROR;
status = nfs41_session_get_slot(session, &args->sa_slotid,
&args->sa_sequenceid, &args->sa_highest_slotid);
if (status)
goto out;
args->sa_sessionid = session->session_id;
args->sa_cachethis = cachethis;
out:
return status;
}
/* session renewal */
static unsigned int WINAPI renew_session(void *args)
{
int status = NO_ERROR;
nfs41_session *session = (nfs41_session *)args;
/* sleep for 2/3 of lease_time */
const uint32_t sleep_time = (2 * session->lease_time*1000)/3;
dprintf(1, "Creating renew_session thread: %p\n", session->renew_thread);
while(1) {
dprintf(1, "Going to sleep for %dmsecs\n", sleep_time);
Sleep(sleep_time);
status = nfs41_send_sequence(session);
if (status)
dprintf(1, "renewal thread: nfs41_send_sequence failed %d\n", status);
}
return status;
}
/* session creation */
static int session_alloc(
IN nfs41_client *client,
OUT nfs41_session **session_out)
{
int status;
nfs41_session *session;
session = calloc(1, sizeof(nfs41_session));
if (session == NULL) {
status = GetLastError();
goto out;
}
session->client = client;
session->renew_thread = INVALID_HANDLE_VALUE;
session->isValidState = FALSE;
status = init_slot_table(&session->table);
if (status) {
eprintf("init_slot_table failed %d\n", status);
goto out_err_session;
}
//initialize session lock
InitializeSRWLock(&client->session_lock);
*session_out = session;
out:
return status;
out_err_session:
free(session);
goto out;
}
int nfs41_session_create(
IN nfs41_client *client,
IN nfs41_session **session_out)
{
nfs41_session *session;
int status;
status = session_alloc(client, &session);
if (status) {
eprintf("session_alloc() failed with %d\n", status);
goto out;
}
AcquireSRWLockShared(&client->exid_lock);
if (client->roles & (EXCHGID4_FLAG_USE_PNFS_MDS |
EXCHGID4_FLAG_USE_NON_PNFS))
session->flags |= CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
ReleaseSRWLockShared(&client->exid_lock);
status = nfs41_create_session(client, session);
if (status) {
eprintf("nfs41_create_session failed %d\n", status);
status = ERROR_BAD_NET_RESP;
goto out_err;
}
AcquireSRWLockExclusive(&session->client->session_lock);
client->session = session;
session->isValidState = TRUE;
ReleaseSRWLockExclusive(&session->client->session_lock);
*session_out = session;
out:
return status;
out_err:
nfs41_session_free(session);
goto out;
}
/* session renewal */
int nfs41_session_renew(
IN nfs41_session *session)
{
int status;
AcquireSRWLockExclusive(&session->client->session_lock);
status = reinit_slot_table(&session->table);
if (status) {
eprintf("init_slot_table failed %d\n", status);
goto out_err_session;
}
status = nfs41_create_session(session->client, session);
if (status && status != NFS4ERR_STALE_CLIENTID) {
eprintf("nfs41_create_session failed %d\n", status);
status = ERROR_BAD_NET_RESP;
goto out_err_slot;
}
ReleaseSRWLockExclusive(&session->client->session_lock);
out:
return status;
out_err_slot:
free_slot_table(&session->table);
out_err_session:
ReleaseSRWLockExclusive(&session->client->session_lock);
free(session);
goto out;
}
int nfs41_session_set_lease(
IN nfs41_session *session,
IN uint32_t lease_time)
{
int status = NO_ERROR;
uint32_t thread_id;
if (valid_handle(session->renew_thread)) {
eprintf("nfs41_session_set_lease(): session "
"renewal thread already started!\n");
goto out;
}
if (lease_time == 0) {
eprintf("nfs41_session_set_lease(): invalid lease_time=0\n");
status = ERROR_INVALID_PARAMETER;
goto out;
}
session->lease_time = lease_time;
session->renew_thread = (HANDLE)_beginthreadex(NULL,
0, renew_session, session, 0, &thread_id);
if (!valid_handle(session->renew_thread)) {
status = GetLastError();
eprintf("_beginthreadex failed %d\n", status);
goto out;
}
out:
return status;
}
void nfs41_session_free(
IN nfs41_session *session)
{
AcquireSRWLockExclusive(&session->client->session_lock);
if (valid_handle(session->renew_thread)) {
dprintf(1, "nfs41_session_free: terminating session renewal thread\n");
if (!TerminateThread(session->renew_thread, NO_ERROR))
eprintf("failed to terminate renewal thread %p\n",
session->renew_thread);
}
if (session->isValidState) {
session->client->rpc->is_valid_session = FALSE;
nfs41_destroy_session(session);
}
free_slot_table(&session->table);
ReleaseSRWLockExclusive(&session->client->session_lock);
free(session);
}

228
daemon/nfs41_superblock.c Normal file
View file

@ -0,0 +1,228 @@
/* 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 <Windows.h>
#include <stdio.h>
#include "daemon_debug.h"
#include "nfs41.h"
#include "nfs41_ops.h"
#include "util.h"
#define SBLVL 3 /* dprintf level for superblock logging */
static __inline int compare_fsid(
IN const nfs41_fsid *lhs,
IN const nfs41_fsid *rhs)
{
if (lhs->major > rhs->major) return 1;
if (lhs->major < rhs->major) return -1;
if (lhs->minor > rhs->minor) return 1;
if (lhs->minor < rhs->minor) return -1;
return 0;
}
/* nfs41_superblock */
static int superblock_create(
IN const nfs41_fsid *fsid,
OUT nfs41_superblock **superblock_out)
{
int status = NO_ERROR;
nfs41_superblock *superblock;
dprintf(SBLVL, "creating superblock for fsid(%llu,%llu)\n",
fsid->major, fsid->minor);
superblock = calloc(1, sizeof(nfs41_superblock));
if (superblock == NULL) {
status = GetLastError();
eprintf("failed to allocate superblock "
"for fsid(%llu,%llu)\n", fsid->major, fsid->minor);
goto out;
}
memcpy(&superblock->fsid, fsid, sizeof(nfs41_fsid));
InitializeSRWLock(&superblock->lock);
*superblock_out = superblock;
out:
return status;
}
static void superblock_free(
IN nfs41_superblock *superblock)
{
free(superblock);
}
static int get_superblock_attrs(
IN nfs41_session *session,
IN nfs41_superblock *superblock,
IN nfs41_path_fh *file)
{
int status;
bitmap4 attr_request;
nfs41_file_info info;
attr_request.arr[0] = (uint32_t)(FATTR4_WORD0_SUPPORTED_ATTRS |
FATTR4_WORD0_MAXREAD | FATTR4_WORD0_MAXWRITE);
attr_request.arr[1] = FATTR4_WORD1_FS_LAYOUT_TYPE;
attr_request.count = 2;
ZeroMemory(&info, sizeof(info));
info.supported_attrs = &superblock->supported_attrs;
status = nfs41_getattr(session, file, &attr_request, &info);
if (status) {
eprintf("nfs41_getattr() failed with %s when "
"fetching attributes for fsid(%llu,%llu)\n",
nfs_error_string(status),
superblock->fsid.major, superblock->fsid.minor);
goto out;
}
if (info.maxread)
superblock->maxread = info.maxread;
else
superblock->maxread = session->fore_chan_attrs.ca_maxresponsesize;
if (info.maxwrite)
superblock->maxwrite = info.maxwrite;
else
superblock->maxwrite = session->fore_chan_attrs.ca_maxrequestsize;
superblock->layout_types = info.fs_layout_types;
dprintf(SBLVL, "attributes for fsid(%llu,%llu): "
"maxread=%llu, maxwrite=%llu, layout_types: 0x%X\n",
superblock->fsid.major, superblock->fsid.minor,
superblock->maxread, superblock->maxwrite,
superblock->layout_types);
out:
return status;
}
/* nfs41_superblock_list */
#define superblock_entry(pos) list_container(pos, nfs41_superblock, entry)
static int superblock_compare(
const struct list_entry *entry,
const void *value)
{
const nfs41_superblock *superblock = superblock_entry(entry);
return compare_fsid(&superblock->fsid, (const nfs41_fsid*)value);
}
static nfs41_superblock* find_superblock(
IN nfs41_superblock_list *superblocks,
IN const nfs41_fsid *fsid)
{
struct list_entry *entry;
entry = list_search(&superblocks->head, fsid, superblock_compare);
return entry ? superblock_entry(entry) : NULL;
}
void nfs41_superblock_list_init(
IN nfs41_superblock_list *superblocks)
{
list_init(&superblocks->head);
InitializeSRWLock(&superblocks->lock);
}
void nfs41_superblock_list_free(
IN nfs41_superblock_list *superblocks)
{
struct list_entry *entry, *tmp;
dprintf(SBLVL, "nfs41_superblock_list_free()\n");
list_for_each_tmp(entry, tmp, &superblocks->head)
superblock_free(superblock_entry(entry));
}
int nfs41_superblock_for_fh(
IN nfs41_session *session,
IN const nfs41_fsid *fsid,
IN const nfs41_fh *parent OPTIONAL,
OUT nfs41_path_fh *file)
{
int status = NFS4_OK;
nfs41_server *server = client_server(session->client);
nfs41_superblock_list *superblocks = &server->superblocks;
nfs41_superblock *superblock;
dprintf(SBLVL, "--> nfs41_superblock_for_fh(fsid(%llu,%llu))\n",
fsid->major, fsid->minor);
/* compare with the parent's fsid, and use that if it matches */
if (parent && parent->superblock &&
compare_fsid(fsid, &parent->superblock->fsid) == 0) {
file->fh.superblock = parent->superblock;
dprintf(SBLVL, "using superblock from parent\n");
goto out;
}
/* using a shared lock, search for an existing superblock */
AcquireSRWLockShared(&superblocks->lock);
superblock = find_superblock(superblocks, fsid);
ReleaseSRWLockShared(&superblocks->lock);
if (superblock) {
dprintf(SBLVL, "found existing superblock in server list "
"[shared lock]\n");
} else {
AcquireSRWLockExclusive(&superblocks->lock);
/* must search again under an exclusive lock, in case another thread
* created it after our first search */
superblock = find_superblock(superblocks, fsid);
if (superblock) {
dprintf(SBLVL, "found newly created superblock in server list "
"[exclusive lock]\n");
} else {
/* create the superblock */
status = superblock_create(fsid, &superblock);
if (status == NO_ERROR) /* add it to the list */
list_add_tail(&superblocks->head, &superblock->entry);
}
ReleaseSRWLockExclusive(&superblocks->lock);
}
if (status == NO_ERROR && superblock->supported_attrs.count == 0) {
/* exclusive lock on the superblock while fetching attributes */
AcquireSRWLockExclusive(&superblock->lock);
if (superblock->supported_attrs.count == 0)
status = get_superblock_attrs(session, superblock, file);
ReleaseSRWLockExclusive(&superblock->lock);
}
file->fh.superblock = superblock;
out:
dprintf(SBLVL, "<-- nfs41_superblock_for_fh() returning %p, status %d\n",
file->fh.superblock, status);
return status;
}

185
daemon/nfs41_types.h Normal file
View file

@ -0,0 +1,185 @@
/* 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 __NFS41_DAEMON_TYPES_H__
#define __NFS41_DAEMON_TYPES_H__
#include "wintirpc.h"
#include "rpc/xdr.h"
#include "nfs41_const.h"
typedef char* caddr_t;
static const int64_t NFS4_INT64_MAX = 0x7fffffffffffffff;
static const uint64_t NFS4_UINT64_MAX = 0xffffffffffffffff;
static const int32_t NFS4_INT32_MAX = 0x7fffffff;
static const uint32_t NFS4_UINT32_MAX = 0xffffffff;
static const uint64_t NFS4_MAXFILELEN = 0xffffffffffffffff;
static const uint64_t NFS4_MAXFILEOFF = 0xfffffffffffffffe;
/* common nfs types */
typedef struct __nfs41_abs_path {
char path[NFS41_MAX_PATH_LEN];
unsigned short len;
SRWLOCK lock;
} nfs41_abs_path;
typedef struct __nfs41_component {
const char *name;
unsigned short len;
} nfs41_component;
typedef struct __nfs41_fh {
unsigned char fh[NFS4_FHSIZE];
uint32_t len;
uint64_t fileid;
struct __nfs41_superblock *superblock;
} nfs41_fh;
typedef struct __nfs41_path_fh {
nfs41_abs_path *path;
nfs41_component name;
nfs41_fh fh;
} nfs41_path_fh;
typedef struct __nfs41_fsid {
uint64_t major;
uint64_t minor;
} nfs41_fsid;
typedef struct __netaddr4 {
char netid[NFS41_NETWORK_ID_LEN+1];
char uaddr[NFS41_UNIVERSAL_ADDR_LEN+1];
} netaddr4;
typedef struct __multi_addr4 {
netaddr4 arr[NFS41_ADDRS_PER_SERVER];
uint32_t count;
} multi_addr4;
typedef struct __bitmap4 {
uint32_t count;
uint32_t arr[3];
} bitmap4;
typedef struct __nfstime4 {
int64_t seconds;
uint32_t nseconds;
} nfstime4;
typedef struct __client_owner4 {
unsigned char co_verifier[NFS4_VERIFIER_SIZE];
uint32_t co_ownerid_len;
unsigned char co_ownerid[NFS4_OPAQUE_LIMIT];
} client_owner4;
typedef struct __server_owner4 {
uint64_t so_minor_id;
uint32_t so_major_id_len;
char so_major_id[NFS4_OPAQUE_LIMIT];
} server_owner4;
typedef struct __state_owner4 {
uint32_t owner_len;
unsigned char owner[NFS4_OPAQUE_LIMIT];
} state_owner4;
typedef struct __nfs_impl_id4 {
uint32_t nii_domain_len;
unsigned char *nii_domain;
uint32_t nii_name_len;
unsigned char *nii_name;
nfstime4 nii_date;
} nfs_impl_id4;
typedef struct __nfsace4 {
uint32_t acetype;
uint32_t aceflag;
uint32_t acemask;
unsigned char who[NFS4_OPAQUE_LIMIT];
} nfsace4;
typedef struct __stateid4 {
uint32_t seqid;
unsigned char other[12];
} stateid4;
typedef struct __fattr4 {
bitmap4 attrmask;
uint32_t attr_vals_len;
unsigned char attr_vals[NFS4_OPAQUE_LIMIT];
} fattr4;
typedef struct __change_info4 {
bool_t atomic;
uint64_t before;
uint64_t after;
} change_info4;
typedef struct __fs_location_server {
/* 'address' represents one of a traditional DNS host name,
* IPv4 address, IPv6 address, or a zero-length string */
char address[NFS41_HOSTNAME_LEN+1];
} fs_location_server;
typedef struct __fs_location4 {
nfs41_abs_path path; /* path to fs from referred server's root */
fs_location_server *servers;
uint32_t server_count;
} fs_location4;
typedef struct __fs_locations4 {
nfs41_abs_path path; /* path to fs from referring server's root */
fs_location4 *locations;
uint32_t location_count;
} fs_locations4;
typedef struct __nfs41_file_info {
nfs41_fsid fsid;
nfstime4 time_access;
nfstime4 time_create;
nfstime4 time_modify;
bitmap4 attrmask;
bitmap4 *supported_attrs; /* XXX: per-fs */
uint64_t maxread; /* XXX: per-fs */
uint64_t maxwrite; /* XXX: per-fs */
uint64_t change;
uint64_t size;
uint64_t fileid;
uint64_t space_avail; /* XXX: per-fs */
uint64_t space_free; /* XXX: per-fs */
uint64_t space_total; /* XXX: per-fs */
uint32_t type;
uint32_t numlinks;
uint32_t rdattr_error;
uint32_t mode;
uint32_t mode_mask;
fs_locations4 *fs_locations; /* XXX: per-fs */
uint32_t lease_time; /* XXX: per-server */
uint32_t fs_layout_types; /* pnfs, XXX: per-fs */
bool_t hidden;
} nfs41_file_info;
#endif /* !__NFS41_DAEMON_TYPES_H__ */

3124
daemon/nfs41_xdr.c Normal file

File diff suppressed because it is too large Load diff

33
daemon/nfs41_xdr.h Normal file
View file

@ -0,0 +1,33 @@
/* 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 __NFS41_NFS_XDR_H__
#define __NFS41_NFS_XDR_H__
#include "nfs41_types.h"
bool_t nfs_encode_compound(XDR *xdr, caddr_t *args);
bool_t nfs_decode_compound(XDR *xdr, caddr_t *res);
#endif /* !__NFS41_NFS_XDR_H__ */

445
daemon/open.c Normal file
View file

@ -0,0 +1,445 @@
/* 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 <Windows.h>
#include <stdio.h>
#include <strsafe.h>
#include "nfs41_ops.h"
#include "from_kernel.h"
#include "daemon_debug.h"
#include "upcall.h"
#include "util.h"
static int create_open_state(
IN const nfs41_abs_path *path,
IN uint32_t open_owner_id,
OUT nfs41_open_state **state_out)
{
int status;
nfs41_open_state *state;
state = calloc(1, sizeof(nfs41_open_state));
if (state == NULL) {
status = GetLastError();
goto out;
}
InitializeSRWLock(&state->path.lock);
abs_path_copy(&state->path, path);
path_fh_init(&state->file, &state->path);
path_fh_init(&state->parent, &state->path);
last_component(state->path.path, state->file.name.name,
&state->parent.name);
StringCchPrintfA((LPSTR)state->owner.owner, NFS4_OPAQUE_LIMIT,
"%u", open_owner_id);
state->owner.owner_len = (uint32_t)strlen(
(const char*)state->owner.owner);
*state_out = state;
status = NO_ERROR;
out:
return status;
}
static void free_open_state(
IN nfs41_session *session,
IN nfs41_open_state *state)
{
free(state);
}
/* NFS41_OPEN */
int parse_open(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
int status;
open_upcall_args *args = &upcall->args.open;
ZeroMemory(&args->path, sizeof(nfs41_abs_path));
status = get_abs_path(&buffer, &length, &args->path);
if (status) goto out;
status = safe_read(&buffer, &length, &args->access_mask, sizeof(ULONG));
if (status) goto out;
status = safe_read(&buffer, &length, &args->access_mode, sizeof(ULONG));
if (status) goto out;
status = safe_read(&buffer, &length, &args->file_attrs, sizeof(ULONG));
if (status) goto out;
status = safe_read(&buffer, &length, &args->create_opts, sizeof(ULONG));
if (status) goto out;
status = safe_read(&buffer, &length, &args->disposition, sizeof(ULONG));
if (status) goto out;
status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE));
if (status) goto out;
status = safe_read(&buffer, &length, &args->open_owner_id, sizeof(ULONG));
if (status) goto out;
status = safe_read(&buffer, &length, &args->mode, sizeof(DWORD));
out:
if (status)
eprintf("parsing NFS41_OPEN failed with %d\n", status);
else {
dprintf(1, "parsing NFS41_OPEN: filename='%s' access mask=%d "
"access mode=%d\n\tfile attrs=0x%x create attrs=0x%x "
"(kernel) disposition=%d\n\tsession=%p open_owner_id=%d mode=%o\n",
args->path.path, args->access_mask, args->access_mode, args->file_attrs,
args->create_opts, args->disposition, args->root, args->open_owner_id,
args->mode);
print_disposition(2, args->disposition);
print_access_mask(2, args->access_mask);
print_share_mode(2, args->access_mode);
print_create_attributes(2, args->create_opts);
}
return status;
}
static BOOLEAN do_lookup(uint32_t type, ULONG access_mask, ULONG disposition)
{
if (type == NF4DIR) {
if (disposition == FILE_OPEN || disposition == FILE_OVERWRITE) {
dprintf(1, "Opening a directory\n");
return TRUE;
} else {
dprintf(1, "Creating a directory\n");
return FALSE;
}
}
if ((access_mask & FILE_READ_DATA) ||
(access_mask & FILE_WRITE_DATA) ||
(access_mask & FILE_APPEND_DATA) ||
(access_mask & FILE_EXECUTE))
return FALSE;
else {
dprintf(1, "Open call that wants to manage attributes\n");
return TRUE;
}
}
static int map_disposition_2_nfsopen(ULONG disposition, int in_status,
uint32_t *create, uint32_t *last_error)
{
int status = NO_ERROR;
if (disposition == FILE_SUPERSEDE) {
if (in_status == NFS4ERR_NOENT) {
*create = OPEN4_CREATE;
*last_error = ERROR_FILE_NOT_FOUND;
}
else // we need to truncate the file file then open it
*create = OPEN4_NOCREATE;
} else if (disposition == FILE_CREATE) {
// if lookup succeeded which means the file exist, return an error
if (!in_status)
status = ERROR_FILE_EXISTS;
else
*create = OPEN4_CREATE;
} else if (disposition == FILE_OPEN) {
if (in_status == NFS4ERR_NOENT)
status = ERROR_FILE_NOT_FOUND;
else
*create = OPEN4_NOCREATE;
} else if (disposition == FILE_OPEN_IF) {
if (in_status == NFS4ERR_NOENT) {
dprintf(1, "creating new file\n");
*create = OPEN4_CREATE;
*last_error = ERROR_FILE_NOT_FOUND;
} else {
dprintf(1, "opening existing file\n");
*create = OPEN4_NOCREATE;
}
} else if (disposition == FILE_OVERWRITE) {
if (in_status == NFS4ERR_NOENT)
status = ERROR_FILE_NOT_FOUND;
//truncate file
*create = OPEN4_CREATE;
} else if (disposition == FILE_OVERWRITE_IF) {
if (in_status == NFS4ERR_NOENT)
*last_error = ERROR_FILE_NOT_FOUND;
//truncate file
*create = OPEN4_CREATE;
}
return status;
}
static int check_execute_access(nfs41_open_state *state)
{
uint32_t supported, access;
int status = nfs41_access(state->session, &state->file,
ACCESS4_EXECUTE | ACCESS4_READ, &supported, &access);
if (status) {
dprintf(1, "nfs41_access() failed with %s\n",
nfs_error_string(status));
status = ERROR_ACCESS_DENIED;
} else if ((supported & ACCESS4_EXECUTE) == 0) {
/* server can't verify execute access;
* for now, assume that read access is good enough */
if ((supported & ACCESS4_READ) == 0 || (access & ACCESS4_READ) == 0) {
dprintf(2, "server can't verify execute access, and user does "
"not have read access\n");
status = ERROR_ACCESS_DENIED;
}
} else if ((access & ACCESS4_EXECUTE) == 0) {
dprintf(2, "user does not have execute access to file\n");
status = ERROR_ACCESS_DENIED;
} else
dprintf(2, "user has execute access to file\n");
return status;
}
int handle_open(nfs41_upcall *upcall)
{
int status = 0;
open_upcall_args *args = &upcall->args.open;
nfs41_open_state *state;
bitmap4 attr_request;
nfs41_file_info info;
init_getattr_request(&attr_request);
status = create_open_state(&args->path, args->open_owner_id, &state);
if (status) {
eprintf("create_open_state(%u) failed with %d\n",
args->open_owner_id, status);
goto out;
}
// first check if windows told us it's a directory
if (args->create_opts & FILE_DIRECTORY_FILE)
state->type = NF4DIR;
else
state->type = NF4REG;
// always do a lookup
status = nfs41_lookup(args->root, nfs41_root_session(args->root),
&state->path, &state->parent, &state->file, &info, &state->session);
// now if file/dir exists, use type returned by lookup
if (status == NO_ERROR) {
if (info.type == NF4DIR) {
dprintf(2, "handle_nfs41_open: DIRECTORY\n");
if (args->create_opts & FILE_NON_DIRECTORY_FILE) {
eprintf("trying to open directory %s as a file\n",
args->path.path);
status = ERROR_ACCESS_DENIED;
goto out_free_state;
}
} else if (info.type == NF4REG) {
dprintf(2, "handle nfs41_open: FILE\n");
if (args->create_opts & FILE_DIRECTORY_FILE) {
eprintf("trying to open file %s as a directory\n",
args->path.path);
#ifdef NOTEPAD_OPEN_FILE_AS_DIRFILE_FIXED
status = ERROR_ACCESS_DENIED;
goto out_free_state;
#endif
}
} else
dprintf(2, "handle nfs41_open: is it SYMLINK?\n");
state->type = info.type;
} else if (status != ERROR_FILE_NOT_FOUND)
goto out_free_state;
if (do_lookup(state->type, args->access_mask, args->disposition)) {
if (status) {
dprintf(1, "nfs41_lookup failed with %d\n", status);
goto out_free_state;
}
nfs_to_basic_info(&info, &args->basic_info);
nfs_to_standard_info(&info, &args->std_info);
args->mode = info.mode;
args->changeattr = info.change;
} else {
uint32_t allow = 0, deny = 0, create = 0;
map_access_2_allowdeny(args->access_mask, args->access_mode, &allow, &deny);
status = map_disposition_2_nfsopen(args->disposition, status, &create, &upcall->last_error);
if (status)
goto out_free_state;
if (args->access_mask & FILE_EXECUTE && state->file.fh.len) {
status = check_execute_access(state);
if (status)
goto out_free_state;
}
if (create == OPEN4_CREATE && (args->create_opts & FILE_DIRECTORY_FILE)) {
status = nfs41_create(state->session, NF4DIR, args->mode,
&state->parent, &state->file);
args->std_info.Directory = 1;
args->created = status == NFS4_OK;
} else {
status = nfs41_open(state->session, allow, deny, create,
args->mode, state, &info);
if (status == NFS4_OK) {
nfs_to_basic_info(&info, &args->basic_info);
nfs_to_standard_info(&info, &args->std_info);
state->do_close = 1;
args->mode = info.mode;
}
}
if (status) {
dprintf(1, "%s failed with %s\n", (create == OPEN4_CREATE &&
(args->create_opts & FILE_DIRECTORY_FILE))?"nfs41_create":"nfs41_open",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_FILE_NOT_FOUND);
goto out_free_state;
}
}
args->state = state;
out:
return status;
out_free_state:
free(state);
goto out;
}
int marshall_open(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
{
int status;
open_upcall_args *args = &upcall->args.open;
status = safe_write(&buffer, length, &args->basic_info, sizeof(args->basic_info));
if (status) goto out;
status = safe_write(&buffer, length, &args->std_info, sizeof(args->std_info));
if (status) goto out;
status = safe_write(&buffer, length, &args->state, sizeof(args->state));
if (status) goto out;
status = safe_write(&buffer, length, &args->mode, sizeof(args->mode));
if (status) goto out;
status = safe_write(&buffer, length, &args->changeattr, sizeof(args->changeattr));
dprintf(2, "NFS41_OPEN: passing open_state=0x%p mode %o changeattr 0x%x\n",
args->state, args->mode, args->changeattr);
out:
return status;
}
int cancel_open(IN nfs41_upcall *upcall)
{
int status = NFS4_OK;
open_upcall_args *args = &upcall->args.open;
nfs41_open_state *state = args->state;
dprintf(1, "--> cancel_open('%s')\n", args->path.path);
if (upcall->status)
goto out; /* if handle_open() failed, the state was already freed */
if (state->do_close) {
status = nfs41_close(state->session, state);
if (status)
dprintf(1, "cancel_open: nfs41_close() failed with %s\n",
nfs_error_string(status));
} else if (args->created) {
const nfs41_component *name = &state->file.name;
status = nfs41_remove(state->session, &state->parent, name);
if (status)
dprintf(1, "cancel_open: nfs41_remove() failed with %s\n",
nfs_error_string(status));
}
free_open_state(state->session, state);
out:
status = nfs_to_windows_error(status, ERROR_INTERNAL_ERROR);
dprintf(1, "<-- cancel_open() returning %d\n", status);
return status;
}
/* NFS41_CLOSE */
int parse_close(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
int status;
close_upcall_args *args = &upcall->args.close;
status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE));
if (status) goto out;
status = safe_read(&buffer, &length, &args->state, sizeof(nfs41_open_state *));
if (status) goto out;
status = safe_read(&buffer, &length, &args->remove, sizeof(BOOLEAN));
if (status) goto out;
if (args->remove) {
ZeroMemory(&args->path, sizeof(nfs41_abs_path));
status = get_abs_path(&buffer, &length, &args->path);
if (status) goto out;
status = safe_read(&buffer, &length, &args->renamed, sizeof(BOOLEAN));
}
out:
if (status)
eprintf("parsing NFS41_CLOSE failed with %d\n", status);
else
dprintf(1, "parsing NFS41_CLOSE: close root=0x%p "
"open_state=0x%p remove=%d renamed=%d filename='%s'\n",
args->root, args->state, args->remove, args->renamed,
args->remove ? args->path.path : "");
return status;
}
int handle_close(nfs41_upcall *upcall)
{
int status = NFS4_OK, rm_status = NFS4_OK;
close_upcall_args *args = &upcall->args.close;
nfs41_open_state *state = args->state;
/* return associated file layouts if necessary */
if (state->type == NF4REG)
pnfs_open_state_close(state->session, state, args->remove);
if (args->remove) {
nfs41_component *name = &state->file.name;
if (args->renamed) {
dprintf(1, "removing a renamed file %s\n", name->name);
create_silly_rename(&state->path, &state->file.fh, name);
}
dprintf(1, "calling nfs41_remove for %s\n", name->name);
rm_status = nfs41_remove(state->session, &state->parent, name);
if (rm_status) {
dprintf(1, "nfs41_remove() failed with error %s.\n",
nfs_error_string(rm_status));
rm_status = nfs_to_windows_error(rm_status, ERROR_INTERNAL_ERROR);
}
}
if (state->do_close) {
status = nfs41_close(state->session, state);
if (status) {
dprintf(1, "nfs41_close() failed with error %s.\n",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_INTERNAL_ERROR);
}
}
if (args->state->type == NF4REG || !args->remove)
free_open_state(state->session, state);
if (status || !rm_status)
return status;
else
return rm_status;
}
int marshall_close(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
{
return NO_ERROR;
}

352
daemon/pnfs.h Normal file
View file

@ -0,0 +1,352 @@
/* 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 __PNFS_H__
#define __PNFS_H__
#include "nfs41_types.h"
#include "list.h"
/* preprocessor options */
#ifndef PNFS_DISABLE
# ifndef PNFS_DISABLE_READ
# define PNFS_ENABLE_READ
# endif
# ifndef PNFS_DISABLE_WRITE
# define PNFS_ENABLE_WRITE
# endif
# define PNFS_THREADING
# define PNFS_THREAD_BY_SERVER
#endif
/* forward declarations from nfs41.h */
struct __nfs41_client;
struct __nfs41_session;
struct __nfs41_open_state;
struct __nfs41_root;
/* pnfs error values, in order of increasing severity */
enum pnfs_status {
PNFS_SUCCESS = 0,
PNFS_PENDING,
PNFS_READ_EOF,
PNFSERR_NOT_SUPPORTED,
PNFSERR_NOT_CONNECTED,
PNFSERR_IO,
PNFSERR_NO_DEVICE,
PNFSERR_NO_LAYOUT,
PNFSERR_INVALID_FH_LIST,
PNFSERR_INVALID_DS_INDEX,
PNFSERR_RESOURCES,
PNFSERR_LAYOUT_RECALLED,
PNFSERR_LAYOUT_CHANGED,
};
enum pnfs_layout_type {
PNFS_LAYOUTTYPE_FILE = 1,
PNFS_LAYOUTTYPE_OBJECT = 2,
PNFS_LAYOUTTYPE_BLOCK = 3
};
enum pnfs_iomode {
PNFS_IOMODE_READ = 0x1,
PNFS_IOMODE_RW = 0x2,
PNFS_IOMODE_ANY = PNFS_IOMODE_READ | PNFS_IOMODE_RW
};
enum pnfs_layout_status {
/* LAYOUTGET was successful, and the layout has not been returned or
* otherwise revoked by the server */
PNFS_LAYOUT_GRANTED = 0x01,
/* GETDEVICEINFO was successful, and we have a valid 'device' pointer */
PNFS_LAYOUT_HAS_DEVICE = 0x02,
/* CB_LAYOUTRECALL indicated that the server has recalled this layout,
* and it should be returned on completion of any pending io */
PNFS_LAYOUT_RECALLED = 0x04,
/* CB_LAYOUTRECALL indicated that the layout is changing, and "the client
* SHOULD NOT write and commit modified data to the storage devices!" */
PNFS_LAYOUT_CHANGED = 0x08,
/* a LAYOUTGET error indicated that this layout will never be granted */
PNFS_LAYOUT_UNAVAILABLE = 0x10,
/* LAYOUTGET returned BADIOMODE, so a RW layout will never be granted */
PNFS_LAYOUT_NOT_RW = 0x20,
};
enum pnfs_return_type {
PNFS_RETURN_FILE = 1,
PNFS_RETURN_FSID = 2,
PNFS_RETURN_ALL = 3
};
#define NFL4_UFLG_MASK 0x0000003F
#define NFL4_UFLG_DENSE 0x00000001
#define NFL4_UFLG_COMMIT_THRU_MDS 0x00000002
#define NFL4_UFLG_STRIPE_UNIT_SIZE_MASK 0xFFFFFFC0
#define PNFS_DEVICEID_SIZE 16
/* device */
typedef struct __pnfs_device {
unsigned char deviceid[PNFS_DEVICEID_SIZE];
enum pnfs_layout_type type;
} pnfs_device;
typedef struct __pnfs_stripe_indices {
uint32_t count;
uint32_t *arr;
} pnfs_stripe_indices;
typedef struct __pnfs_data_server {
struct __nfs41_client *client;
multi_addr4 addrs;
SRWLOCK lock;
} pnfs_data_server;
typedef struct __pnfs_data_server_list {
uint32_t count;
pnfs_data_server *arr;
} pnfs_data_server_list;
typedef struct __pnfs_file_device {
pnfs_device device;
pnfs_stripe_indices stripes;
pnfs_data_server_list servers;
struct list_entry entry; /* position in nfs41_client.devices */
SRWLOCK lock;
} pnfs_file_device;
/* layout */
typedef struct __pnfs_layout {
stateid4 state;
uint64_t offset;
uint64_t length;
enum pnfs_layout_type type;
enum pnfs_iomode iomode;
enum pnfs_layout_status status;
bool_t return_on_close;
uint32_t io_count; /* number of pending io operations */
SRWLOCK lock;
} pnfs_layout;
typedef struct __pnfs_file_layout_handles {
uint32_t count;
nfs41_path_fh *arr;
} pnfs_file_layout_handles;
typedef struct __pnfs_file_layout {
pnfs_layout layout;
pnfs_file_layout_handles filehandles;
unsigned char deviceid[PNFS_DEVICEID_SIZE];
struct list_entry entry; /* position in nfs41_client.layouts */
pnfs_file_device *device;
nfs41_fh meta_fh;
uint64_t pattern_offset;
uint32_t first_index;
uint32_t util;
} pnfs_file_layout;
typedef struct __pnfs_layout_recall {
enum pnfs_layout_type type;
enum pnfs_iomode iomode;
bool_t changed;
enum pnfs_return_type recall;
union {
struct {
nfs41_fh fh;
stateid4 stateid;
} file;
nfs41_fsid fsid;
} args;
} pnfs_layout_recall;
/* io */
typedef struct __pnfs_io_pattern {
struct __pnfs_io_thread *threads;
struct __nfs41_root *root;
nfs41_path_fh *meta_file;
stateid4 *stateid;
pnfs_file_layout *layout;
unsigned char *buffer;
uint64_t offset_start;
uint64_t offset_end;
uint32_t count;
uint32_t default_lease;
} pnfs_io_pattern;
typedef struct __pnfs_io_thread {
pnfs_io_pattern *pattern;
uint64_t offset;
uint64_t offset_end;
uint32_t id;
enum stable_how4 stable;
} pnfs_io_thread;
typedef struct __pnfs_io_unit {
nfs41_path_fh *file;
unsigned char *buffer;
uint64_t offset;
uint64_t length;
uint32_t stripeid;
uint32_t serverid;
} pnfs_io_unit;
typedef uint32_t (WINAPI *pnfs_io_thread_fn)(void*);
/* pnfs_layout.c */
struct pnfs_file_layout_list;
struct cb_layoutrecall_args;
enum pnfs_status pnfs_file_layout_list_create(
OUT struct pnfs_file_layout_list **layouts_out);
void pnfs_file_layout_list_free(
IN struct pnfs_file_layout_list *layouts);
enum pnfs_status pnfs_open_state_layout(
IN struct pnfs_file_layout_list *layouts,
IN struct __nfs41_session *session,
IN struct __nfs41_open_state *state,
IN enum pnfs_iomode iomode,
IN uint64_t offset,
IN uint64_t length,
OUT pnfs_file_layout **layout_out);
void pnfs_open_state_close(
IN struct __nfs41_session *session,
IN struct __nfs41_open_state *state,
IN bool_t remove);
enum pnfs_status pnfs_file_layout_recall(
IN struct pnfs_file_layout_list *layouts,
IN const struct cb_layoutrecall_args *recall);
enum pnfs_status pnfs_layout_io_start(
IN pnfs_layout *layout);
void pnfs_layout_io_finished(
IN pnfs_layout *layout);
__inline int is_dense(
IN const pnfs_file_layout *layout)
{
return (layout->util & NFL4_UFLG_DENSE) != 0;
}
__inline int should_commit_to_mds(
IN const pnfs_file_layout *layout)
{
return (layout->util & NFL4_UFLG_COMMIT_THRU_MDS) != 0;
}
__inline uint32_t layout_unit_size(
IN const pnfs_file_layout *layout)
{
return layout->util & NFL4_UFLG_STRIPE_UNIT_SIZE_MASK;
}
/* pnfs_device.c */
struct pnfs_file_device_list;
enum pnfs_status pnfs_file_device_list_create(
OUT struct pnfs_file_device_list **devices_out);
void pnfs_file_device_list_free(
IN struct pnfs_file_device_list *devices);
enum pnfs_status pnfs_file_device_get(
IN struct __nfs41_session *session,
IN struct pnfs_file_device_list *devices,
IN unsigned char *deviceid,
OUT pnfs_file_device **device_out);
enum pnfs_status pnfs_data_server_client(
IN struct __nfs41_root *root,
IN pnfs_data_server *server,
IN uint32_t default_lease,
OUT struct __nfs41_client **client_out);
enum pnfs_status pnfs_file_device_io_unit(
IN pnfs_io_pattern *pattern,
IN uint64_t offset,
OUT pnfs_io_unit *io);
__inline uint64_t stripe_unit_number(
IN pnfs_file_layout *layout,
IN uint64_t offset,
IN uint32_t unit_size)
{
const uint64_t relative_offset = offset - layout->pattern_offset;
return relative_offset / unit_size;
}
__inline uint32_t stripe_index(
IN pnfs_file_layout *layout,
IN uint64_t sui,
IN uint32_t stripe_count)
{
return (sui + layout->first_index) % stripe_count;
}
__inline uint32_t data_server_index(
IN pnfs_file_device *device,
IN uint32_t stripeid)
{
return device->stripes.arr[stripeid];
}
/* pnfs_io.c */
enum pnfs_status pnfs_read(
IN struct __nfs41_root *root,
IN struct __nfs41_session *session,
IN nfs41_path_fh *file,
IN stateid4 *stateid,
IN pnfs_file_layout *layout,
IN uint64_t offset,
IN uint64_t length,
OUT unsigned char *buffer_out,
OUT ULONG *len_out);
enum pnfs_status pnfs_write(
IN struct __nfs41_root *root,
IN struct __nfs41_session *session,
IN nfs41_path_fh *file,
IN stateid4 *stateid,
IN pnfs_file_layout *layout,
IN uint64_t offset,
IN uint64_t length,
IN unsigned char *buffer,
OUT ULONG *len_out);
#endif /* !__PNFS_H__ */

125
daemon/pnfs_debug.c Normal file
View file

@ -0,0 +1,125 @@
/* 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 <winsock2.h>
#include <strsafe.h>
#include "pnfs.h"
#include "daemon_debug.h"
const char* pnfs_error_string(enum pnfs_status status)
{
switch (status) {
case PNFS_SUCCESS: return "PNFS_SUCCESS";
case PNFS_PENDING: return "PNFS_PENDING";
case PNFS_READ_EOF: return "PNFS_READ_EOF";
case PNFSERR_NOT_SUPPORTED: return "PNFSERR_NOT_SUPPORTED";
case PNFSERR_NOT_CONNECTED: return "PNFSERR_NOT_CONNECTED";
case PNFSERR_IO: return "PNFSERR_IO";
case PNFSERR_NO_DEVICE: return "PNFSERR_NO_DEVICE";
case PNFSERR_NO_LAYOUT: return "PNFSERR_NO_LAYOUT";
case PNFSERR_INVALID_FH_LIST: return "PNFSERR_INVALID_FH_LIST";
case PNFSERR_INVALID_DS_INDEX: return "PNFSERR_INVALID_DS_INDEX";
case PNFSERR_RESOURCES: return "PNFSERR_RESOURCES";
case PNFSERR_LAYOUT_RECALLED: return "PNFSERR_LAYOUT_RECALLED";
case PNFSERR_LAYOUT_CHANGED: return "PNFSERR_LAYOUT_CHANGED";
default: return "Invalid pnfs status";
}
}
const char* pnfs_layout_type_string(enum pnfs_layout_type type)
{
switch (type) {
case PNFS_LAYOUTTYPE_FILE: return "PNFS_LAYOUTTYPE_FILE";
case PNFS_LAYOUTTYPE_OBJECT: return "PNFS_LAYOUTTYPE_OBJECT";
case PNFS_LAYOUTTYPE_BLOCK: return "PNFS_LAYOUTTYPE_BLOCK";
default: return "Invalid layout type";
}
}
const char* pnfs_iomode_string(enum pnfs_iomode iomode)
{
switch (iomode) {
case PNFS_IOMODE_READ: return "PNFS_IOMODE_READ";
case PNFS_IOMODE_RW: return "PNFS_IOMODE_RW";
case PNFS_IOMODE_ANY: return "PNFS_IOMODE_ANY";
default: return "Invalid io mode";
}
}
void dprint_deviceid(
IN int level,
IN const char *title,
IN const unsigned char *deviceid)
{
/* deviceid is 16 bytes, so print it as 4 uints */
uint32_t *p = (uint32_t*)deviceid;
dprintf(level, "%s%08X.%08X.%08X.%08X\n",
title, htonl(p[0]), htonl(p[1]), htonl(p[2]), htonl(p[3]));
}
void dprint_layout(
IN int level,
IN const pnfs_file_layout *layout)
{
dprintf(level, " type: %s\n", pnfs_layout_type_string(layout->layout.type));
dprintf(level, " iomode: %s\n", pnfs_iomode_string(layout->layout.iomode));
dprint_deviceid(level, " deviceid: ", layout->deviceid);
dprintf(level, " offset: %llu\n", layout->layout.offset);
dprintf(level, " length: %llu\n", layout->layout.length);
dprintf(level, " pattern_offset: %llu\n", layout->pattern_offset);
dprintf(level, " first_index: %u\n", layout->first_index);
dprintf(level, " dense: %u\n", is_dense(layout));
dprintf(level, " commit_to_mds: %u\n", should_commit_to_mds(layout));
dprintf(level, " stripe_unit_size: %u\n", layout_unit_size(layout));
dprintf(level, " file handles: %u\n", layout->filehandles.count);
}
#define MULTI_ADDR_BUFFER_LEN \
(NFS41_ADDRS_PER_SERVER*(NFS41_UNIVERSAL_ADDR_LEN+1)+1)
static void dprint_multi_addr(
IN int level,
IN uint32_t index,
IN const multi_addr4 *addrs)
{
char buffer[MULTI_ADDR_BUFFER_LEN] = "";
uint32_t i;
for (i = 0; i < addrs->count; i++) {
StringCchCatA(buffer, MULTI_ADDR_BUFFER_LEN, addrs->arr[i].uaddr);
StringCchCatA(buffer, MULTI_ADDR_BUFFER_LEN, " ");
}
dprintf(level, " servers[%d]: [ %s]\n", index, buffer);
}
void dprint_device(
IN int level,
IN const pnfs_file_device *device)
{
uint32_t i;
dprint_deviceid(level, " deviceid: ", device->device.deviceid);
dprintf(level, " type: %s\n", pnfs_layout_type_string(device->device.type));
dprintf(level, " stripes: %u\n", device->stripes.count);
for (i = 0; i < device->servers.count; i++)
dprint_multi_addr(level, i, &device->servers.arr[i].addrs);
}

376
daemon/pnfs_device.c Normal file
View file

@ -0,0 +1,376 @@
/* 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 <Windows.h>
#include <strsafe.h>
#include <stdio.h>
#include "nfs41_ops.h"
#include "daemon_debug.h"
#define FDLVL 2 /* dprintf level for file device logging */
/* pnfs_file_device_list */
struct pnfs_file_device_list {
struct list_entry head;
CRITICAL_SECTION lock;
};
#define device_entry(pos) list_container(pos, pnfs_file_device, entry)
static enum pnfs_status file_device_create(
IN const unsigned char *deviceid,
OUT pnfs_file_device **device_out)
{
enum pnfs_status status = PNFS_SUCCESS;
pnfs_file_device *device;
device = calloc(1, sizeof(pnfs_file_device));
if (device == NULL) {
status = PNFSERR_RESOURCES;
goto out;
}
memcpy(device->device.deviceid, deviceid, PNFS_DEVICEID_SIZE);
InitializeSRWLock(&device->lock);
*device_out = device;
out:
return status;
}
static void file_device_free(
IN pnfs_file_device *device)
{
free(device->servers.arr);
free(device->stripes.arr);
free(device);
}
static int deviceid_compare(
const struct list_entry *entry,
const void *deviceid)
{
const pnfs_file_device *device = device_entry(entry);
return memcmp(device->device.deviceid, deviceid, PNFS_DEVICEID_SIZE);
}
static enum pnfs_status file_device_entry_find(
IN struct pnfs_file_device_list *devices,
IN const unsigned char *deviceid,
OUT struct list_entry **entry_out)
{
*entry_out = list_search(&devices->head, deviceid, deviceid_compare);
return *entry_out ? PNFS_SUCCESS : PNFSERR_NO_DEVICE;
}
static enum pnfs_status file_device_find_or_create(
IN const unsigned char *deviceid,
IN struct pnfs_file_device_list *devices,
OUT pnfs_file_device **device_out)
{
struct list_entry *entry;
enum pnfs_status status;
dprintf(FDLVL, "--> pnfs_file_device_find_or_create()\n");
EnterCriticalSection(&devices->lock);
/* search for an existing device */
status = file_device_entry_find(devices, deviceid, &entry);
if (status) {
/* create a new device */
pnfs_file_device *device;
status = file_device_create(deviceid, &device);
if (status == PNFS_SUCCESS) {
/* add it to the list */
list_add_tail(&devices->head, &device->entry);
*device_out = device;
dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
"returning new device %p\n", device);
} else {
dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
"returning %s\n", pnfs_error_string(status));
}
} else {
*device_out = device_entry(entry);
dprintf(FDLVL, "<-- pnfs_file_device_find_or_create() "
"returning existing device %p\n", *device_out);
}
LeaveCriticalSection(&devices->lock);
return status;
}
enum pnfs_status pnfs_file_device_list_create(
OUT struct pnfs_file_device_list **devices_out)
{
enum pnfs_status status = PNFS_SUCCESS;
struct pnfs_file_device_list *devices;
devices = calloc(1, sizeof(struct pnfs_file_device_list));
if (devices == NULL) {
status = PNFSERR_RESOURCES;
goto out;
}
list_init(&devices->head);
InitializeCriticalSection(&devices->lock);
*devices_out = devices;
out:
return status;
}
void pnfs_file_device_list_free(
IN struct pnfs_file_device_list *devices)
{
struct list_entry *entry, *tmp;
EnterCriticalSection(&devices->lock);
list_for_each_tmp(entry, tmp, &devices->head)
file_device_free(device_entry(entry));
LeaveCriticalSection(&devices->lock);
free(devices);
}
/* pnfs_file_device */
static enum pnfs_status file_device_status(
IN pnfs_file_device *device)
{
return device->device.type == 0 ? PNFS_PENDING : PNFS_SUCCESS;
}
enum pnfs_status pnfs_file_device_get(
IN nfs41_session *session,
IN struct pnfs_file_device_list *devices,
IN unsigned char *deviceid,
OUT pnfs_file_device **device_out)
{
pnfs_file_device *device;
enum pnfs_status status;
enum nfsstat4 nfsstat;
dprintf(FDLVL, "--> pnfs_file_device_get()\n");
status = file_device_find_or_create(deviceid, devices, &device);
if (status)
goto out;
AcquireSRWLockShared(&device->lock);
status = file_device_status(device);
ReleaseSRWLockShared(&device->lock);
if (status == PNFS_PENDING) {
AcquireSRWLockExclusive(&device->lock);
status = file_device_status(device);
if (status == PNFS_PENDING) {
nfsstat = pnfs_rpc_getdeviceinfo(session, deviceid, device);
if (nfsstat == NFS4_OK) {
status = PNFS_SUCCESS;
dprintf(FDLVL, "Received device info:\n");
dprint_device(FDLVL, device);
} else {
status = PNFSERR_NO_DEVICE;
eprintf("pnfs_rpc_getdeviceinfo() failed with %s\n",
nfs_error_string(nfsstat));
}
}
ReleaseSRWLockExclusive(&device->lock);
}
*device_out = device;
out:
dprintf(FDLVL, "<-- pnfs_file_device_get() returning %s\n",
pnfs_error_string(status));
return status;
}
static enum pnfs_status data_client_status(
IN pnfs_data_server *server,
OUT nfs41_client **client_out)
{
enum pnfs_status status = PNFSERR_NOT_CONNECTED;
if (server->client) {
dprintf(FDLVL, "pnfs_data_server_client() returning "
"existing client %llu\n", server->client->clnt_id);
*client_out = server->client;
status = PNFS_SUCCESS;
}
return status;
}
enum pnfs_status pnfs_data_server_client(
IN nfs41_root *root,
IN pnfs_data_server *server,
IN uint32_t default_lease,
OUT nfs41_client **client_out)
{
int status;
enum pnfs_status pnfsstat;
dprintf(FDLVL, "--> pnfs_data_server_client('%s')\n",
server->addrs.arr[0].uaddr);
/* if we've already created the client, return it */
AcquireSRWLockShared(&server->lock);
pnfsstat = data_client_status(server, client_out);
ReleaseSRWLockShared(&server->lock);
if (pnfsstat) {
AcquireSRWLockExclusive(&server->lock);
pnfsstat = data_client_status(server, client_out);
if (pnfsstat) {
status = nfs41_root_mount_addrs(root, &server->addrs,
1, default_lease, &server->client);
if (status) {
dprintf(FDLVL, "data_client_create('%s') failed with %d\n",
server->addrs.arr[0].uaddr, status);
} else {
*client_out = server->client;
pnfsstat = PNFS_SUCCESS;
dprintf(FDLVL, "pnfs_data_server_client() returning "
"new client %llu\n", server->client->clnt_id);
}
}
ReleaseSRWLockExclusive(&server->lock);
}
return pnfsstat;
}
/* 13.4.2. Interpreting the File Layout Using Sparse Packing
* http://tools.ietf.org/html/rfc5661#section-13.4.2 */
static enum pnfs_status get_sparse_fh(
IN pnfs_io_pattern *pattern,
IN uint32_t stripeid,
OUT nfs41_path_fh **file_out)
{
pnfs_file_layout *layout = pattern->layout;
const uint32_t filehandle_count = layout->filehandles.count;
const uint32_t server_count = layout->device->servers.count;
enum pnfs_status status = PNFS_SUCCESS;
if (filehandle_count == server_count) {
const uint32_t serverid = data_server_index(layout->device, stripeid);
*file_out = &layout->filehandles.arr[serverid];
} else if (filehandle_count == 1) {
*file_out = &layout->filehandles.arr[0];
} else if (filehandle_count == 0) {
*file_out = pattern->meta_file;
} else {
eprintf("invalid sparse layout! has %u file handles "
"and %u servers\n", filehandle_count, server_count);
status = PNFSERR_INVALID_FH_LIST;
}
return status;
}
/* 13.4.3. Interpreting the File Layout Using Dense Packing
* http://tools.ietf.org/html/rfc5661#section-13.4.3 */
static enum pnfs_status get_dense_fh(
IN pnfs_io_pattern *pattern,
IN uint32_t stripeid,
OUT nfs41_path_fh **file_out)
{
pnfs_file_layout *layout = pattern->layout;
const uint32_t filehandle_count = layout->filehandles.count;
const uint32_t stripe_count = layout->device->stripes.count;
enum pnfs_status status = PNFS_SUCCESS;
if (filehandle_count == stripe_count) {
*file_out = &layout->filehandles.arr[stripeid];
} else {
eprintf("invalid dense layout! has %u file handles "
"and %u stripes\n", filehandle_count, stripe_count);
status = PNFSERR_INVALID_FH_LIST;
}
return status;
}
static __inline uint64_t positive_remainder(
IN uint64_t dividend,
IN uint32_t divisor)
{
const uint64_t remainder = dividend % divisor;
return remainder < divisor ? remainder : remainder + divisor;
}
/* 13.4.4. Sparse and Dense Stripe Unit Packing
* http://tools.ietf.org/html/rfc5661#section-13.4.4 */
enum pnfs_status pnfs_file_device_io_unit(
IN pnfs_io_pattern *pattern,
IN uint64_t offset,
OUT pnfs_io_unit *io)
{
pnfs_file_layout *layout = pattern->layout;
enum pnfs_status status = PNFS_SUCCESS;
const uint32_t unit_size = layout_unit_size(layout);
const uint32_t stripe_count = layout->device->stripes.count;
const uint64_t sui = stripe_unit_number(layout, offset, unit_size);
const uint64_t offset_end = layout->pattern_offset + unit_size * (sui + 1);
io->stripeid = stripe_index(layout, sui, stripe_count);
io->serverid = data_server_index(layout->device, io->stripeid);
if (is_dense(layout)) {
const uint64_t rel_offset = offset - layout->pattern_offset;
const uint64_t remainder = positive_remainder(rel_offset, unit_size);
const uint32_t stride = unit_size * stripe_count;
io->offset = (rel_offset / stride) * unit_size + remainder;
status = get_dense_fh(pattern, io->stripeid, &io->file);
} else {
io->offset = offset;
status = get_sparse_fh(pattern, io->stripeid, &io->file);
}
io->buffer = pattern->buffer + offset - pattern->offset_start;
io->length = offset_end - offset;
if (offset + io->length > pattern->offset_end)
io->length = pattern->offset_end - offset;
return status;
}

565
daemon/pnfs_io.c Normal file
View file

@ -0,0 +1,565 @@
/* 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 <stdio.h>
#include <process.h>
#include "nfs41_ops.h"
#include "util.h"
#include "daemon_debug.h"
#define IOLVL 2 /* dprintf level for pnfs io logging */
static uint32_t io_unit_count(
const pnfs_file_layout *layout,
uint64_t length)
{
const uint32_t unit_size = layout_unit_size(layout);
return (uint32_t)(length / unit_size) + (length % unit_size ? 1 : 0);
}
static enum pnfs_status pattern_init(
IN pnfs_io_pattern *pattern,
IN nfs41_root *root,
IN nfs41_path_fh *meta_file,
IN stateid4 *stateid,
IN pnfs_file_layout *layout,
IN unsigned char *buffer,
IN uint64_t offset,
IN uint64_t length,
IN uint32_t default_lease)
{
#ifndef PNFS_THREAD_BY_SERVER
pnfs_io_unit io;
#endif
uint64_t pos;
uint32_t i;
enum pnfs_status status;
/* take a reference on the layout so we don't return it during io */
status = pnfs_layout_io_start(&layout->layout);
if (status)
goto out;
#ifdef PNFS_THREAD_BY_SERVER
pattern->count = layout->device->servers.count;
#else
pattern->count = io_unit_count(layout, length);
#endif
pattern->threads = calloc(pattern->count, sizeof(pnfs_io_thread));
if (pattern->threads == NULL) {
status = PNFSERR_RESOURCES;
free(pattern);
goto out;
}
pattern->root = root;
pattern->meta_file = meta_file;
pattern->stateid = stateid;
pattern->layout = layout;
pattern->buffer = buffer;
pattern->offset_start = offset;
pattern->offset_end = offset + length;
pattern->default_lease = default_lease;
pos = pattern->offset_start;
for (i = 0; i < pattern->count; i++) {
pattern->threads[i].pattern = pattern;
pattern->threads[i].stable = DATA_SYNC4;
#ifdef PNFS_THREAD_BY_SERVER
pattern->threads[i].offset = pattern->offset_start;
pattern->threads[i].offset_end = pattern->offset_end;
pattern->threads[i].id = i;
#else
pnfs_file_device_io_unit(pattern, pos, &io);
pattern->threads[i].offset = pos;
pattern->threads[i].offset_end = pos += io.length;
pattern->threads[i].id = io.stripeid;
if (pattern->threads[i].offset > pattern->offset_end)
pattern->threads[i].offset = pattern->offset_end;
if (pattern->threads[i].offset_end > pattern->offset_end)
pattern->threads[i].offset_end = pattern->offset_end;
dprintf(IOLVL, "io_unit(off=%llu end=%llu id=%u)\n",
pattern->threads[i].offset,
pattern->threads[i].offset_end,
pattern->threads[i].id);
#endif
}
out:
return status;
}
static void pattern_free(
IN pnfs_io_pattern *pattern)
{
/* inform the layout that our io is finished */
pnfs_layout_io_finished(&pattern->layout->layout);
free(pattern->threads);
}
static enum pnfs_status thread_next_unit(
IN pnfs_io_thread *thread,
OUT pnfs_io_unit *io)
{
pnfs_io_pattern *pattern = thread->pattern;
pnfs_file_layout *layout = pattern->layout;
enum pnfs_status status = PNFS_SUCCESS;
AcquireSRWLockShared(&layout->layout.lock);
/* stop io if the layout is recalled */
if (layout->layout.status & PNFS_LAYOUT_CHANGED) {
status = PNFSERR_LAYOUT_CHANGED;
goto out_unlock;
}
if (layout->layout.status & PNFS_LAYOUT_RECALLED) {
status = PNFSERR_LAYOUT_RECALLED;
goto out_unlock;
}
/* loop until we find an io unit that matches this thread */
while (thread->offset < thread->offset_end) {
pnfs_file_device_io_unit(pattern, thread->offset, io);
#ifdef PNFS_THREAD_BY_SERVER
if (io->serverid == thread->id) {
#else
if (io->stripeid == thread->id) {
#endif
status = PNFS_PENDING;
break;
}
thread->offset += io->length;
}
out_unlock:
ReleaseSRWLockShared(&layout->layout.lock);
return status;
}
static enum pnfs_status thread_data_server(
IN pnfs_io_thread *thread,
OUT pnfs_data_server **server_out)
{
pnfs_file_device *device = thread->pattern->layout->device;
#ifdef PNFS_THREAD_BY_SERVER
const uint32_t serverid = thread->id;
#else
const uint32_t serverid = data_server_index(device, thread->id);
#endif
if (serverid >= device->servers.count)
return PNFSERR_INVALID_DS_INDEX;
*server_out = &device->servers.arr[serverid];
return PNFS_SUCCESS;
}
static enum pnfs_status pattern_fork(
IN pnfs_io_pattern *pattern,
IN pnfs_io_thread_fn thread_fn)
{
pnfs_io_unit io;
#ifdef PNFS_THREADING
HANDLE *threads;
uint32_t num_threads;
#endif
uint32_t i;
DWORD status;
enum pnfs_status pnfsstat = PNFS_SUCCESS;
if (pattern->count == 0)
goto out;
#ifdef PNFS_THREADING
/* create a thread for each unit that has actual io */
threads = calloc(pattern->count, sizeof(HANDLE));
if (threads == NULL) {
pnfsstat = PNFSERR_RESOURCES;
goto out;
}
num_threads = 0;
for (i = 0; i < pattern->count; i++) {
if (thread_next_unit(&pattern->threads[i], &io) == PNFS_PENDING) {
threads[num_threads++] = (HANDLE)_beginthreadex(NULL, 0,
thread_fn, &pattern->threads[i], 0, NULL);
}
}
if (num_threads) { /* wait on all threads to finish */
status = WaitForMultipleObjects(num_threads, threads, TRUE, INFINITE);
if (status == WAIT_OBJECT_0)
status = NO_ERROR;
for (i = 0; i < num_threads; i++) {
/* keep track of the most severe error returned by a thread */
if (GetExitCodeThread(threads[i], &status))
pnfsstat = max(pnfsstat, (enum pnfs_status)status);
CloseHandle(threads[i]);
}
}
free(threads);
#else
/* process each server that has actual io */
for (i = 0; i < pattern->count; i++) {
if (thread_next_unit(&pattern->threads[i], &io) == PNFS_PENDING) {
/* keep track of the most severe error returned by a thread */
status = thread_fn(&pattern->threads[i]);
pnfsstat = max(pnfsstat, (enum pnfs_status)status);
}
}
#endif
out:
return pnfsstat;
}
static uint64_t pattern_bytes_transferred(
IN pnfs_io_pattern *pattern,
OUT OPTIONAL enum stable_how4 *stable)
{
uint64_t lowest_offset = pattern->offset_end;
uint32_t i;
if (stable) *stable = DATA_SYNC4;
for (i = 0; i < pattern->count; i++) {
if (lowest_offset > pattern->threads[i].offset)
lowest_offset = pattern->threads[i].offset;
if (stable && pattern->threads[i].stable == UNSTABLE4)
*stable = UNSTABLE4;
}
return lowest_offset - pattern->offset_start;
}
static enum pnfs_status map_ds_error(
IN enum nfsstat4 nfsstat,
IN pnfs_layout *layout)
{
switch (nfsstat) {
case NO_ERROR:
return PNFS_SUCCESS;
/* 13.11 Layout Revocation and Fencing
* http://tools.ietf.org/html/rfc5661#section-13.11
* if we've been fenced, we'll either get ERR_STALE when we PUTFH
* something in layout.filehandles, or ERR_PNFS_NO_LAYOUT when
* attempting to READ or WRITE */
case NFS4ERR_STALE:
case NFS4ERR_PNFS_NO_LAYOUT:
dprintf(IOLVL, "data server fencing detected!\n");
AcquireSRWLockExclusive(&layout->lock);
/* flag the layout for return once io is finished */
layout->status |= PNFS_LAYOUT_RECALLED | PNFS_LAYOUT_CHANGED;
/* reset GRANTED so we know not to try LAYOUTRETURN */
layout->status &= ~PNFS_LAYOUT_GRANTED;
ReleaseSRWLockExclusive(&layout->lock);
/* return CHANGED to prevent any further use of the layout */
return PNFSERR_LAYOUT_CHANGED;
default:
return PNFSERR_IO;
}
}
static uint32_t WINAPI file_layout_read_thread(void *args)
{
pnfs_io_unit io;
pnfs_io_thread *thread = (pnfs_io_thread*)args;
pnfs_io_pattern *pattern = thread->pattern;
stateid4 *state = pattern->stateid;
pnfs_data_server *server;
nfs41_client *client;
uint32_t maxreadsize, bytes_read, total_read;
enum pnfs_status status;
enum nfsstat4 nfsstat;
bool_t eof;
dprintf(IOLVL, "--> file_layout_read_thread(%u)\n", thread->id);
/* get the data server for this thread */
status = thread_data_server(thread, &server);
if (status) {
eprintf("thread_data_server() failed with %s\n",
pnfs_error_string(status));
goto out;
}
/* find or establish a client for this data server */
status = pnfs_data_server_client(pattern->root,
server, pattern->default_lease, &client);
if (status) {
eprintf("pnfs_data_server_client() failed with %s\n",
pnfs_error_string(status));
goto out;
}
total_read = 0;
while ((status = thread_next_unit(thread, &io)) == PNFS_PENDING) {
maxreadsize = max_read_size(client->session, &io.file->fh);
if (io.length > maxreadsize)
io.length = maxreadsize;
nfsstat = nfs41_read(client->session, io.file, state, io.offset,
(uint32_t)io.length, io.buffer, &bytes_read, &eof);
if (nfsstat) {
eprintf("nfs41_read() failed with %s\n",
nfs_error_string(nfsstat));
status = map_ds_error(nfsstat, &pattern->layout->layout);
break;
}
total_read += bytes_read;
thread->offset += bytes_read;
if (eof) {
dprintf(IOLVL, "read thread %u reached eof: offset %llu\n",
thread->id, thread->offset);
status = total_read ? PNFS_SUCCESS : PNFS_READ_EOF;
break;
}
}
out:
dprintf(IOLVL, "<-- file_layout_read_thread(%u) returning %s\n",
thread->id, pnfs_error_string(status));
return status;
}
static uint32_t WINAPI file_layout_write_thread(void *args)
{
pnfs_io_unit io;
nfs41_write_verf verf;
pnfs_io_thread *thread = (pnfs_io_thread*)args;
pnfs_io_pattern *pattern = thread->pattern;
stateid4 *state = pattern->stateid;
pnfs_data_server *server;
pnfs_file_layout *layout = pattern->layout;
nfs41_client *client;
nfs41_path_fh *commit_file;
const uint64_t offset_start = thread->offset;
uint64_t commit_len;
uint32_t maxwritesize, bytes_written, total_written;
enum pnfs_status status;
enum nfsstat4 nfsstat;
dprintf(IOLVL, "--> file_layout_write_thread(%u)\n", thread->id);
/* get the data server for this thread */
status = thread_data_server(thread, &server);
if (status) {
eprintf("thread_data_server() failed with %s\n",
pnfs_error_string(status));
goto out;
}
/* find or establish a client for this data server */
status = pnfs_data_server_client(pattern->root,
server, pattern->default_lease, &client);
if (status) {
eprintf("pnfs_data_server_client() failed with %s\n",
pnfs_error_string(status));
goto out;
}
retry_write:
thread->offset = offset_start;
thread->stable = DATA_SYNC4;
commit_file = NULL;
total_written = 0;
while ((status = thread_next_unit(thread, &io)) == PNFS_PENDING) {
maxwritesize = max_write_size(client->session, &io.file->fh);
if (io.length > maxwritesize)
io.length = maxwritesize;
nfsstat = nfs41_write(client->session, io.file, state, io.buffer,
(uint32_t)io.length, io.offset, UNSTABLE4, &bytes_written, &verf);
if (nfsstat) {
eprintf("nfs41_write() failed with %s\n",
nfs_error_string(nfsstat));
status = map_ds_error(nfsstat, &layout->layout);
break;
}
if (!verify_write(&verf, &thread->stable))
goto retry_write;
total_written += bytes_written;
thread->offset += bytes_written;
commit_file = io.file;
}
commit_len = thread->offset - pattern->offset_start;
/* nothing to commit */
if (commit_len == 0)
goto out;
/* layout changed; redo all io against metadata server */
if (status == PNFSERR_LAYOUT_CHANGED)
goto out;
/* XXX: commit offsets (and possibly fh) are different in dense layouts! */
if (is_dense(layout))
goto out;
/* the data is already in stable storage */
if (thread->stable != UNSTABLE4)
goto out;
/* the metadata server expects us to commit there instead */
if (should_commit_to_mds(layout))
goto out;
dprintf(1, "sending COMMIT to data server for offset=%d and len=%d\n",
pattern->offset_start, commit_len);
nfsstat = nfs41_commit(client->session, commit_file,
pattern->offset_start, (uint32_t)commit_len, 0);
/* on successful commit, leave pnfs_status unchanged; if the layout
* was recalled, we still want to return the error */
if (nfsstat == NFS4_OK)
thread->stable = DATA_SYNC4;
else
status = map_ds_error(nfsstat, &pattern->layout->layout);
out:
dprintf(IOLVL, "<-- file_layout_write_thread(%u) returning %s\n",
thread->id, pnfs_error_string(status));
return status;
}
enum pnfs_status pnfs_read(
IN nfs41_root *root,
IN nfs41_session *session,
IN nfs41_path_fh *file,
IN stateid4 *stateid,
IN pnfs_file_layout *layout,
IN uint64_t offset,
IN uint64_t length,
OUT unsigned char *buffer_out,
OUT ULONG *len_out)
{
pnfs_io_pattern pattern;
enum pnfs_status status;
dprintf(IOLVL, "--> pnfs_read(%llu, %llu)\n", offset, length);
*len_out = 0;
status = pattern_init(&pattern, root, file, stateid,
layout, buffer_out, offset, length, session->lease_time);
if (status) {
eprintf("pattern_init() failed with %s\n",
pnfs_error_string(status));
goto out;
}
status = pattern_fork(&pattern, file_layout_read_thread);
if (status != PNFS_SUCCESS && status != PNFS_READ_EOF)
goto out_free_pattern;
*len_out = (ULONG)pattern_bytes_transferred(&pattern, NULL);
out_free_pattern:
pattern_free(&pattern);
out:
dprintf(IOLVL, "<-- pnfs_read() returning %s\n",
pnfs_error_string(status));
return status;
}
enum pnfs_status pnfs_write(
IN nfs41_root *root,
IN nfs41_session *session,
IN nfs41_path_fh *file,
IN stateid4 *stateid,
IN pnfs_file_layout *layout,
IN uint64_t offset,
IN uint64_t length,
IN unsigned char *buffer,
OUT ULONG *len_out)
{
pnfs_io_pattern pattern;
uint64_t new_last_offset;
enum stable_how4 stable;
enum pnfs_status status;
enum nfsstat4 nfsstat;
dprintf(IOLVL, "--> pnfs_write(%llu, %llu)\n", offset, length);
*len_out = 0;
status = pattern_init(&pattern, root, file, stateid,
layout, buffer, offset, length, session->lease_time);
if (status) {
eprintf("pattern_init() failed with %s\n",
pnfs_error_string(status));
goto out;
}
status = pattern_fork(&pattern, file_layout_write_thread);
/* on layout recall, we still attempt to commit what we wrote */
if (status != PNFS_SUCCESS && status != PNFSERR_LAYOUT_RECALLED)
goto out_free_pattern;
*len_out = (ULONG)pattern_bytes_transferred(&pattern, &stable);
if (*len_out == 0)
goto out_free_pattern;
if (stable == UNSTABLE4) {
/* not all data was committed, so commit to metadata server.
* pass do_getattr=0 to nfs41_commit() because we'll GETATTR
* after LAYOUTCOMMIT */
dprintf(1, "sending COMMIT to meta server for offset=%d and len=%d\n",
offset, *len_out);
nfsstat = nfs41_commit(session, pattern.meta_file, offset, *len_out, 0);
if (nfsstat) {
dprintf(IOLVL, "nfs41_commit() failed with %s\n",
nfs_error_string(nfsstat));
status = PNFSERR_IO;
goto out_free_pattern;
}
}
/* send LAYOUTCOMMIT */
new_last_offset = offset + *len_out - 1;
nfsstat = pnfs_rpc_layoutcommit(session, pattern.meta_file,
&pattern.layout->layout.state, offset, *len_out,
&new_last_offset, NULL);
if (nfsstat) {
dprintf(IOLVL, "pnfs_rpc_layoutcommit() failed with %s\n",
nfs_error_string(nfsstat));
/* acceptable failure? if COMMIT worked, return success */
}
out_free_pattern:
pattern_free(&pattern);
out:
dprintf(IOLVL, "<-- pnfs_write() returning %s\n",
pnfs_error_string(status));
return status;
}

835
daemon/pnfs_layout.c Normal file
View file

@ -0,0 +1,835 @@
/* 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 <stdio.h>
#include "nfs41_ops.h"
#include "nfs41_callback.h"
#include "util.h"
#include "daemon_debug.h"
#define FLLVL 2 /* dprintf level for file layout logging */
/* pnfs_file_layout_list */
struct pnfs_file_layout_list {
struct list_entry head;
CRITICAL_SECTION lock;
};
#define layout_entry(pos) list_container(pos, pnfs_file_layout, entry)
static enum pnfs_status layout_create(
IN const nfs41_fh *meta_fh,
OUT pnfs_file_layout **layout_out)
{
pnfs_file_layout *layout;
enum pnfs_status status = PNFS_SUCCESS;
layout = calloc(1, sizeof(pnfs_file_layout));
if (layout == NULL) {
status = PNFSERR_RESOURCES;
goto out;
}
layout->layout.type = PNFS_LAYOUTTYPE_FILE;
fh_copy(&layout->meta_fh, meta_fh);
InitializeSRWLock(&layout->layout.lock);
*layout_out = layout;
out:
return status;
}
static void layout_free(
IN pnfs_file_layout *layout)
{
free(layout->filehandles.arr);
free(layout);
}
static int layout_entry_compare(
IN const struct list_entry *entry,
IN const void *value)
{
const pnfs_file_layout *layout = layout_entry(entry);
const nfs41_fh *meta_fh = (const nfs41_fh*)value;
const nfs41_fh *layout_fh = (const nfs41_fh*)&layout->meta_fh;
const uint32_t diff = layout_fh->len - meta_fh->len;
return diff ? diff : memcmp(layout_fh->fh, meta_fh->fh, meta_fh->len);
}
static enum pnfs_status layout_entry_find(
IN struct pnfs_file_layout_list *layouts,
IN const nfs41_fh *meta_fh,
OUT struct list_entry **entry_out)
{
*entry_out = list_search(&layouts->head, meta_fh, layout_entry_compare);
return *entry_out ? PNFS_SUCCESS : PNFSERR_NO_LAYOUT;
}
enum pnfs_status pnfs_file_layout_list_create(
OUT struct pnfs_file_layout_list **layouts_out)
{
struct pnfs_file_layout_list *layouts;
enum pnfs_status status = PNFS_SUCCESS;
layouts = calloc(1, sizeof(struct pnfs_file_layout_list));
if (layouts == NULL) {
status = PNFSERR_RESOURCES;
goto out;
}
list_init(&layouts->head);
InitializeCriticalSection(&layouts->lock);
*layouts_out = layouts;
out:
return status;
}
void pnfs_file_layout_list_free(
IN struct pnfs_file_layout_list *layouts)
{
struct list_entry *entry, *tmp;
EnterCriticalSection(&layouts->lock);
list_for_each_tmp(entry, tmp, &layouts->head)
layout_free(layout_entry(entry));
LeaveCriticalSection(&layouts->lock);
free(layouts);
}
static enum pnfs_status file_layout_find_or_create(
IN struct pnfs_file_layout_list *layouts,
IN const nfs41_fh *meta_fh,
OUT pnfs_file_layout **layout_out)
{
struct list_entry *entry;
enum pnfs_status status;
dprintf(FLLVL, "--> file_layout_find_or_create()\n");
EnterCriticalSection(&layouts->lock);
/* search for an existing layout */
status = layout_entry_find(layouts, meta_fh, &entry);
if (status) {
/* create a new layout */
pnfs_file_layout *layout;
status = layout_create(meta_fh, &layout);
if (status == PNFS_SUCCESS) {
/* add it to the list */
list_add_head(&layouts->head, &layout->entry);
*layout_out = layout;
dprintf(FLLVL, "<-- file_layout_find_or_create() "
"returning new layout %p\n", layout);
} else {
dprintf(FLLVL, "<-- file_layout_find_or_create() "
"returning %s\n", pnfs_error_string(status));
}
} else {
*layout_out = layout_entry(entry);
dprintf(FLLVL, "<-- file_layout_find_or_create() "
"returning existing layout %p\n", *layout_out);
}
LeaveCriticalSection(&layouts->lock);
return status;
}
static enum pnfs_status file_layout_find_and_delete(
IN struct pnfs_file_layout_list *layouts,
IN const nfs41_fh *meta_fh)
{
struct list_entry *entry;
enum pnfs_status status;
dprintf(FLLVL, "--> file_layout_find_and_delete()\n");
EnterCriticalSection(&layouts->lock);
status = layout_entry_find(layouts, meta_fh, &entry);
if (status == PNFS_SUCCESS) {
list_remove(entry);
layout_free(layout_entry(entry));
}
LeaveCriticalSection(&layouts->lock);
dprintf(FLLVL, "<-- file_layout_find_and_delete() "
"returning %s\n", pnfs_error_string(status));
return status;
}
/* pnfs_file_layout */
static enum pnfs_status file_layout_fetch(
IN OUT pnfs_file_layout *layout,
IN nfs41_session *session,
IN nfs41_path_fh *meta_file,
IN stateid4 *state,
IN enum pnfs_iomode iomode,
IN uint64_t offset,
IN uint64_t length)
{
enum pnfs_status pnfsstat = PNFS_SUCCESS;
enum nfsstat4 nfsstat;
dprintf(FLLVL, "--> file_layout_fetch(%s, seqid=%u)\n",
pnfs_iomode_string(iomode), layout->layout.state.seqid);
nfsstat = pnfs_rpc_layoutget(session, meta_file,
state, iomode, offset, length, layout);
if (nfsstat) {
dprintf(FLLVL, "pnfs_rpc_layoutget() failed with %s\n",
nfs_error_string(nfsstat));
pnfsstat = PNFSERR_NOT_SUPPORTED;
}
switch (nfsstat) {
case NFS4_OK:
/* mark granted and clear other flags */
layout->layout.status = PNFS_LAYOUT_GRANTED;
dprintf(FLLVL, "Received layout:\n");
dprint_layout(FLLVL, layout);
break;
case NFS4ERR_BADIOMODE:
/* don't try RW again */
if (iomode == PNFS_IOMODE_RW)
layout->layout.status |= PNFS_LAYOUT_NOT_RW;
break;
case NFS4ERR_LAYOUTUNAVAILABLE:
case NFS4ERR_UNKNOWN_LAYOUTTYPE:
case NFS4ERR_BADLAYOUT:
/* don't try again at all */
layout->layout.status |= PNFS_LAYOUT_UNAVAILABLE;
break;
}
dprintf(FLLVL, "<-- file_layout_fetch() returning %s\n",
pnfs_error_string(pnfsstat));
return pnfsstat;
}
static bool_t layout_recalled(
IN const pnfs_layout *layout)
{
return (layout->status & PNFS_LAYOUT_RECALLED) != 0;
}
static bool_t layout_granted(
IN const pnfs_layout *layout)
{
return (layout->status & PNFS_LAYOUT_GRANTED) != 0;
}
static bool_t layout_not_rw(
IN const pnfs_layout *layout)
{
return (layout->status & PNFS_LAYOUT_NOT_RW) != 0;
}
static bool_t will_never_grant(
IN const pnfs_layout *layout,
IN enum pnfs_iomode iomode)
{
return (layout->status & PNFS_LAYOUT_UNAVAILABLE) != 0
|| (iomode == PNFS_IOMODE_RW && layout_not_rw(layout));
}
static enum pnfs_status layout_grant_status(
IN const pnfs_layout *layout,
IN enum pnfs_iomode iomode)
{
enum pnfs_status status = PNFS_PENDING;
if (layout_recalled(layout)) {
/* don't use a recalled layout */
status = PNFSERR_LAYOUT_RECALLED;
} else if (layout_granted(layout)) {
/* the layout is granted; use it if it's compatible */
status = PNFS_SUCCESS;
} else if (will_never_grant(layout, iomode)) {
/* an error from LAYOUTGET indicated that the server
* won't ever grant this layout, so stop trying */
status = PNFSERR_NOT_SUPPORTED;
}
return status;
}
static enum pnfs_status file_layout_cache(
IN OUT pnfs_file_layout *layout,
IN nfs41_session *session,
IN nfs41_path_fh *meta_file,
IN stateid4 *state,
IN enum pnfs_iomode iomode,
IN uint64_t offset,
IN uint64_t length)
{
enum pnfs_status status;
/* use a shared lock to see if it's already been granted */
AcquireSRWLockShared(&layout->layout.lock);
status = layout_grant_status(&layout->layout, iomode);
ReleaseSRWLockShared(&layout->layout.lock);
if (status == PNFS_PENDING) {
/* use an exclusive lock while attempting to get a new layout */
AcquireSRWLockExclusive(&layout->layout.lock);
status = layout_grant_status(&layout->layout, iomode);
if (status == PNFS_PENDING) {
/* if there's an existing layout stateid, use it */
if (layout->layout.state.seqid)
state = &layout->layout.state;
if (!layout_not_rw(&layout->layout)) {
/* try to get a RW layout first */
status = file_layout_fetch(layout, session,
meta_file, state, PNFS_IOMODE_RW, offset, length);
}
if (status && iomode == PNFS_IOMODE_READ) {
/* fall back on READ if necessary */
status = file_layout_fetch(layout, session,
meta_file, state, iomode, offset, length);
}
}
ReleaseSRWLockExclusive(&layout->layout.lock);
}
return status;
}
static enum pnfs_status layout_compatible(
IN OUT pnfs_layout *layout,
IN enum pnfs_iomode iomode,
IN uint64_t offset,
IN uint64_t length)
{
enum pnfs_status status = PNFS_SUCCESS;
AcquireSRWLockShared(&layout->lock);
if (iomode == PNFS_IOMODE_RW && layout->iomode == PNFS_IOMODE_READ) {
status = PNFSERR_NOT_SUPPORTED;
goto out_unlock;
}
if (offset < layout->offset ||
offset + length > layout->offset + layout->length) {
status = PNFSERR_NOT_SUPPORTED;
goto out_unlock;
}
out_unlock:
ReleaseSRWLockShared(&layout->lock);
return status;
}
static enum pnfs_status file_device_status(
IN const pnfs_layout *layout)
{
enum pnfs_status status = PNFS_PENDING;
if (layout_recalled(layout)) {
/* don't fetch deviceinfo for a recalled layout */
status = PNFSERR_LAYOUT_RECALLED;
} else if (layout->status & PNFS_LAYOUT_HAS_DEVICE) {
/* deviceinfo already cached */
status = PNFS_SUCCESS;
}
return status;
}
static enum pnfs_status file_layout_device(
IN OUT pnfs_file_layout *layout,
IN nfs41_session *session)
{
enum pnfs_status status = PNFS_PENDING;
/* use a shared lock to see if we already have a device */
AcquireSRWLockShared(&layout->layout.lock);
status = file_device_status(&layout->layout);
ReleaseSRWLockShared(&layout->layout.lock);
if (status == PNFS_PENDING) {
/* use an exclusive lock to look up device info */
AcquireSRWLockExclusive(&layout->layout.lock);
status = file_device_status(&layout->layout);
if (status == PNFS_PENDING) {
status = pnfs_file_device_get(session, session->client->devices,
layout->deviceid, &layout->device);
if (status == PNFS_SUCCESS)
layout->layout.status |= PNFS_LAYOUT_HAS_DEVICE;
}
ReleaseSRWLockExclusive(&layout->layout.lock);
}
return status;
}
static enum pnfs_status file_layout_get(
IN OUT pnfs_file_layout *layout,
IN nfs41_session *session,
IN nfs41_path_fh *meta_file,
IN stateid4 *state,
IN enum pnfs_iomode iomode,
IN uint64_t offset,
IN uint64_t length)
{
enum pnfs_status status;
/* request a range for the entire file */
status = file_layout_cache(layout, session,
meta_file, state, iomode, 0, NFS4_UINT64_MAX);
if (status) {
dprintf(FLLVL, "file_layout_cache() failed with %s\n",
pnfs_error_string(status));
goto out;
}
/* fail if we don't get everything we asked for */
status = layout_compatible(&layout->layout, iomode, offset, length);
if (status) {
dprintf(FLLVL, "file_layout_compatible() failed with %s\n",
pnfs_error_string(status));
goto out;
}
/* make sure we have a device for the layout */
status = file_layout_device(layout, session);
if (status) {
dprintf(FLLVL, "file_layout_device() failed with %s\n",
pnfs_error_string(status));
goto out;
}
out:
return status;
}
static enum pnfs_status layout_return_status(
IN const pnfs_file_layout *layout)
{
return (layout->layout.status & PNFS_LAYOUT_GRANTED) == 0
? PNFS_SUCCESS : PNFS_PENDING;
}
static enum pnfs_status file_layout_return(
IN nfs41_session *session,
IN nfs41_path_fh *file,
IN pnfs_file_layout *layout)
{
enum pnfs_status status;
enum nfsstat4 nfsstat;
dprintf(FLLVL, "--> file_layout_return()\n");
/* under shared lock, determine whether we need to return the layout */
AcquireSRWLockShared(&layout->layout.lock);
status = layout_return_status(layout);
ReleaseSRWLockShared(&layout->layout.lock);
if (status == PNFS_PENDING) {
/* under exclusive lock, return the layout and reset status flags */
AcquireSRWLockExclusive(&layout->layout.lock);
status = layout_return_status(layout);
if (status == PNFS_PENDING) {
/* reset the granted flag */
layout->layout.status &= ~PNFS_LAYOUT_GRANTED;
nfsstat = pnfs_rpc_layoutreturn(session, file, layout);
if (nfsstat) {
eprintf("pnfs_rpc_layoutreturn() failed with %s\n",
nfs_error_string(nfsstat));
status = PNFSERR_NO_LAYOUT;
} else {
status = PNFS_SUCCESS;
}
}
ReleaseSRWLockExclusive(&layout->layout.lock);
}
dprintf(FLLVL, "<-- file_layout_return() returning %s\n",
pnfs_error_string(status));
return status;
}
/* nfs41_open_state */
static enum pnfs_status client_supports_pnfs(
IN nfs41_client *client)
{
enum pnfs_status status;
AcquireSRWLockShared(&client->exid_lock);
status = client->roles & EXCHGID4_FLAG_USE_PNFS_MDS
? PNFS_SUCCESS : PNFSERR_NOT_SUPPORTED;
ReleaseSRWLockShared(&client->exid_lock);
return status;
}
static enum pnfs_status fs_supports_layout(
IN const nfs41_superblock *superblock,
IN enum pnfs_layout_type type)
{
const uint32_t flag = 1 << (type - 1);
return (superblock->layout_types & flag) == 0
? PNFSERR_NOT_SUPPORTED : PNFS_SUCCESS;
}
static enum pnfs_status open_state_layout_cached(
IN nfs41_open_state *state,
IN enum pnfs_iomode iomode,
IN uint64_t offset,
IN uint64_t length,
OUT pnfs_file_layout **layout_out)
{
enum pnfs_status status = PNFSERR_NO_LAYOUT;
if (state->layout) {
status = PNFS_SUCCESS;
*layout_out = state->layout;
dprintf(FLLVL, "pnfs_open_state_layout() found "
"cached layout %p\n", *layout_out);
}
return status;
}
enum pnfs_status pnfs_open_state_layout(
IN struct pnfs_file_layout_list *layouts,
IN nfs41_session *session,
IN nfs41_open_state *state,
IN enum pnfs_iomode iomode,
IN uint64_t offset,
IN uint64_t length,
OUT pnfs_file_layout **layout_out)
{
pnfs_file_layout *layout;
enum pnfs_status status;
dprintf(FLLVL, "--> pnfs_open_state_layout()\n");
status = client_supports_pnfs(session->client);
if (status)
goto out;
status = fs_supports_layout(state->file.fh.superblock, PNFS_LAYOUTTYPE_FILE);
if (status)
goto out;
/* under shared lock, check open state for cached layouts */
AcquireSRWLockShared(&state->lock);
status = open_state_layout_cached(state, iomode, offset, length, &layout);
ReleaseSRWLockShared(&state->lock);
if (status) {
/* under exclusive lock, find or create a layout for this file */
AcquireSRWLockExclusive(&state->lock);
status = open_state_layout_cached(state, iomode, offset, length, &layout);
if (status) {
status = file_layout_find_or_create(layouts, &state->file.fh, &layout);
if (status == PNFS_SUCCESS) {
state->layout = layout;
dprintf(FLLVL, "pnfs_open_state_layout() caching layout %p\n",
state->layout);
}
}
ReleaseSRWLockExclusive(&state->lock);
if (status)
goto out;
}
/* make sure the layout can satisfy this request */
status = file_layout_get(layout, session, &state->file,
&state->stateid, iomode, offset, length);
if (status) {
dprintf(FLLVL, "file_layout_get() failed with %s\n",
pnfs_error_string(status));
goto out;
}
*layout_out = layout;
out:
dprintf(FLLVL, "<-- pnfs_open_state_layout() returning %s\n",
pnfs_error_string(status));
return status;
}
void pnfs_open_state_close(
IN nfs41_session *session,
IN nfs41_open_state *state,
IN bool_t remove)
{
pnfs_file_layout *layout;
bool_t return_on_close;
enum pnfs_status status;
AcquireSRWLockExclusive(&state->lock);
layout = state->layout;
state->layout = NULL;
ReleaseSRWLockExclusive(&state->lock);
if (layout) {
/* check if we need to return the layout on close */
AcquireSRWLockShared(&layout->layout.lock);
return_on_close = layout->layout.return_on_close;
ReleaseSRWLockShared(&layout->layout.lock);
if (return_on_close) {
status = file_layout_return(session, &state->file, layout);
if (status)
eprintf("file_layout_return() failed with %s\n",
pnfs_error_string(status));
}
}
if (remove && session->client->layouts) {
/* free the layout when the file is removed */
file_layout_find_and_delete(session->client->layouts, &state->file.fh);
}
}
/* pnfs_layout_recall */
/* expects the caller to have an exclusive lock */
static enum pnfs_status layout_recall_return(
IN pnfs_layout *layout)
{
dprintf(FLLVL, "layout_recall_return() 'forgetting' layout\n");
/* since we're forgetful, we don't actually return the layout;
* just zero the stateid since it won't be valid anymore */
ZeroMemory(&layout->state, sizeof(layout->state));
layout->status = 0;
return PNFS_SUCCESS;
}
static enum pnfs_status file_layout_recall(
IN pnfs_file_layout *layout,
IN const struct cb_layoutrecall_args *recall)
{
enum pnfs_status status = PNFS_SUCCESS;
/* under an exclusive lock, flag the layout as recalled */
AcquireSRWLockExclusive(&layout->layout.lock);
if (layout->layout.io_count == 0) {
/* if there is no pending io, return the layout now */
status = layout_recall_return(&layout->layout);
} else {
/* flag the layout as recalled so it can be returned after io */
layout->layout.status |= PNFS_LAYOUT_RECALLED;
if (recall->changed)
layout->layout.status |= PNFS_LAYOUT_CHANGED;
}
/* if we got a stateid, update the layout's seqid */
if (recall->recall.type == PNFS_RETURN_FILE)
layout->layout.state.seqid = recall->recall.args.file.stateid.seqid;
ReleaseSRWLockExclusive(&layout->layout.lock);
return status;
}
static enum pnfs_status file_layout_recall_file(
IN struct pnfs_file_layout_list *layouts,
IN const struct cb_layoutrecall_args *recall)
{
struct list_entry *entry;
enum pnfs_status status;
dprintf(FLLVL, "--> file_layout_recall_file()\n");
EnterCriticalSection(&layouts->lock);
status = layout_entry_find(layouts, &recall->recall.args.file.fh, &entry);
if (status == PNFS_SUCCESS)
status = file_layout_recall(layout_entry(entry), recall);
LeaveCriticalSection(&layouts->lock);
dprintf(FLLVL, "<-- file_layout_recall_file() returning %s\n",
pnfs_error_string(status));
return status;
}
static bool_t fsid_matches(
IN const nfs41_fsid *lhs,
IN const nfs41_fsid *rhs)
{
return lhs->major == rhs->major
&& lhs->minor == rhs->minor;
}
static enum pnfs_status file_layout_recall_fsid(
IN struct pnfs_file_layout_list *layouts,
IN const struct cb_layoutrecall_args *recall)
{
struct list_entry *entry;
pnfs_file_layout *layout;
nfs41_fh *fh;
enum pnfs_status status = PNFSERR_NO_LAYOUT;
dprintf(FLLVL, "--> file_layout_recall_fsid(%llu, %llu)\n",
recall->recall.args.fsid.major, recall->recall.args.fsid.minor);
EnterCriticalSection(&layouts->lock);
list_for_each(entry, &layouts->head) {
layout = layout_entry(entry);
/* no locks needed to read layout.meta_fh or superblock.fsid,
* because they are only written once on creation */
fh = &layout->meta_fh;
if (fsid_matches(&recall->recall.args.fsid, &fh->superblock->fsid))
status = file_layout_recall(layout, recall);
}
LeaveCriticalSection(&layouts->lock);
dprintf(FLLVL, "<-- file_layout_recall_fsid() returning %s\n",
pnfs_error_string(status));
return status;
}
static enum pnfs_status file_layout_recall_all(
IN struct pnfs_file_layout_list *layouts,
IN const struct cb_layoutrecall_args *recall)
{
struct list_entry *entry;
enum pnfs_status status = PNFSERR_NO_LAYOUT;
dprintf(FLLVL, "--> file_layout_recall_all()\n");
EnterCriticalSection(&layouts->lock);
list_for_each(entry, &layouts->head)
status = file_layout_recall(layout_entry(entry), recall);
LeaveCriticalSection(&layouts->lock);
dprintf(FLLVL, "<-- file_layout_recall_all() returning %s\n",
pnfs_error_string(status));
return status;
}
enum pnfs_status pnfs_file_layout_recall(
IN struct pnfs_file_layout_list *layouts,
IN const struct cb_layoutrecall_args *recall)
{
enum pnfs_status status = PNFS_SUCCESS;
dprintf(FLLVL, "--> pnfs_file_layout_recall(%u, %s, %u)\n",
recall->recall.type, pnfs_iomode_string(recall->iomode),
recall->changed);
if (recall->type != PNFS_LAYOUTTYPE_FILE) {
dprintf(FLLVL, "invalid layout type %u (%s)!\n",
recall->type, pnfs_layout_type_string(recall->type));
status = PNFSERR_NOT_SUPPORTED;
goto out;
}
switch (recall->recall.type) {
case PNFS_RETURN_FILE:
status = file_layout_recall_file(layouts, recall);
break;
case PNFS_RETURN_FSID:
status = file_layout_recall_fsid(layouts, recall);
break;
case PNFS_RETURN_ALL:
status = file_layout_recall_all(layouts, recall);
break;
default:
dprintf(FLLVL, "invalid return type %u!\n", recall->recall);
status = PNFSERR_NOT_SUPPORTED;
goto out;
}
/* XXX: bulk layout recalls require invalidation of cached device info!
* see CB_LAYOUTRECALL: http://tools.ietf.org/html/rfc5661#section-20.3.3 */
out:
dprintf(FLLVL, "<-- pnfs_file_layout_recall() returning %s\n",
pnfs_error_string(status));
return status;
}
enum pnfs_status pnfs_layout_io_start(
IN pnfs_layout *layout)
{
enum pnfs_status status = PNFS_SUCCESS;
AcquireSRWLockExclusive(&layout->lock);
if (layout_recalled(layout)) {
/* don't start any more io if the layout has been recalled */
status = PNFSERR_LAYOUT_RECALLED;
dprintf(FLLVL, "pnfs_layout_io_start() failed, layout was recalled\n");
} else {
/* take a reference on the layout, so that it won't be recalled
* until all io is finished */
layout->io_count++;
dprintf(FLLVL, "pnfs_layout_io_start(): count -> %u\n",
layout->io_count);
}
ReleaseSRWLockExclusive(&layout->lock);
return status;
}
void pnfs_layout_io_finished(
IN pnfs_layout *layout)
{
AcquireSRWLockExclusive(&layout->lock);
/* return the reference to signify that an io request is finished */
layout->io_count--;
dprintf(FLLVL, "pnfs_layout_io_finished() count -> %u\n",
layout->io_count);
if (layout->io_count > 0) /* more io pending */
goto out_unlock;
/* once all io is finished, check for layout recalls */
if (layout->status & PNFS_LAYOUT_RECALLED)
layout_recall_return(layout);
out_unlock:
ReleaseSRWLockExclusive(&layout->lock);
}

385
daemon/rbtree.c Normal file
View file

@ -0,0 +1,385 @@
/*
Red Black Trees
(C) 1999 Andrea Arcangeli <andrea@suse.de>
(C) 2002 David Woodhouse <dwmw2@infradead.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
linux/lib/rbtree.c
*/
#include "rbtree.h"
#pragma warning(disable:4706)
static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
{
struct rb_node *right = node->rb_right;
struct rb_node *parent = rb_parent(node);
if ((node->rb_right = right->rb_left))
rb_set_parent(right->rb_left, node);
right->rb_left = node;
rb_set_parent(right, parent);
if (parent)
{
if (node == parent->rb_left)
parent->rb_left = right;
else
parent->rb_right = right;
}
else
root->rb_node = right;
rb_set_parent(node, right);
}
static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
{
struct rb_node *left = node->rb_left;
struct rb_node *parent = rb_parent(node);
if ((node->rb_left = left->rb_right))
rb_set_parent(left->rb_right, node);
left->rb_right = node;
rb_set_parent(left, parent);
if (parent)
{
if (node == parent->rb_right)
parent->rb_right = left;
else
parent->rb_left = left;
}
else
root->rb_node = left;
rb_set_parent(node, left);
}
void rb_insert_color(struct rb_node *node, struct rb_root *root)
{
struct rb_node *parent, *gparent;
while ((parent = rb_parent(node)) && rb_is_red(parent))
{
gparent = rb_parent(parent);
if (parent == gparent->rb_left)
{
{
register struct rb_node *uncle = gparent->rb_right;
if (uncle && rb_is_red(uncle))
{
rb_set_black(uncle);
rb_set_black(parent);
rb_set_red(gparent);
node = gparent;
continue;
}
}
if (parent->rb_right == node)
{
register struct rb_node *tmp;
__rb_rotate_left(parent, root);
tmp = parent;
parent = node;
node = tmp;
}
rb_set_black(parent);
rb_set_red(gparent);
__rb_rotate_right(gparent, root);
} else {
{
register struct rb_node *uncle = gparent->rb_left;
if (uncle && rb_is_red(uncle))
{
rb_set_black(uncle);
rb_set_black(parent);
rb_set_red(gparent);
node = gparent;
continue;
}
}
if (parent->rb_left == node)
{
register struct rb_node *tmp;
__rb_rotate_right(parent, root);
tmp = parent;
parent = node;
node = tmp;
}
rb_set_black(parent);
rb_set_red(gparent);
__rb_rotate_left(gparent, root);
}
}
rb_set_black(root->rb_node);
}
static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
struct rb_root *root)
{
struct rb_node *other;
while ((!node || rb_is_black(node)) && node != root->rb_node)
{
if (parent->rb_left == node)
{
other = parent->rb_right;
if (rb_is_red(other))
{
rb_set_black(other);
rb_set_red(parent);
__rb_rotate_left(parent, root);
other = parent->rb_right;
}
if ((!other->rb_left || rb_is_black(other->rb_left)) &&
(!other->rb_right || rb_is_black(other->rb_right)))
{
rb_set_red(other);
node = parent;
parent = rb_parent(node);
}
else
{
if (!other->rb_right || rb_is_black(other->rb_right))
{
rb_set_black(other->rb_left);
rb_set_red(other);
__rb_rotate_right(other, root);
other = parent->rb_right;
}
rb_set_color(other, rb_color(parent));
rb_set_black(parent);
rb_set_black(other->rb_right);
__rb_rotate_left(parent, root);
node = root->rb_node;
break;
}
}
else
{
other = parent->rb_left;
if (rb_is_red(other))
{
rb_set_black(other);
rb_set_red(parent);
__rb_rotate_right(parent, root);
other = parent->rb_left;
}
if ((!other->rb_left || rb_is_black(other->rb_left)) &&
(!other->rb_right || rb_is_black(other->rb_right)))
{
rb_set_red(other);
node = parent;
parent = rb_parent(node);
}
else
{
if (!other->rb_left || rb_is_black(other->rb_left))
{
rb_set_black(other->rb_right);
rb_set_red(other);
__rb_rotate_left(other, root);
other = parent->rb_left;
}
rb_set_color(other, rb_color(parent));
rb_set_black(parent);
rb_set_black(other->rb_left);
__rb_rotate_right(parent, root);
node = root->rb_node;
break;
}
}
}
if (node)
rb_set_black(node);
}
void rb_erase(struct rb_node *node, struct rb_root *root)
{
struct rb_node *child, *parent;
int color;
if (!node->rb_left)
child = node->rb_right;
else if (!node->rb_right)
child = node->rb_left;
else
{
struct rb_node *old = node, *left;
node = node->rb_right;
while ((left = node->rb_left) != NULL)
node = left;
if (rb_parent(old)) {
if (rb_parent(old)->rb_left == old)
rb_parent(old)->rb_left = node;
else
rb_parent(old)->rb_right = node;
} else
root->rb_node = node;
child = node->rb_right;
parent = rb_parent(node);
color = rb_color(node);
if (parent == old) {
parent = node;
} else {
if (child)
rb_set_parent(child, parent);
parent->rb_left = child;
node->rb_right = old->rb_right;
rb_set_parent(old->rb_right, node);
}
node->rb_parent_color = old->rb_parent_color;
node->rb_left = old->rb_left;
rb_set_parent(old->rb_left, node);
goto color;
}
parent = rb_parent(node);
color = rb_color(node);
if (child)
rb_set_parent(child, parent);
if (parent)
{
if (parent->rb_left == node)
parent->rb_left = child;
else
parent->rb_right = child;
}
else
root->rb_node = child;
color:
if (color == RB_BLACK)
__rb_erase_color(child, parent, root);
}
/*
* This function returns the first node (in sort order) of the tree.
*/
struct rb_node *rb_first(const struct rb_root *root)
{
struct rb_node *n;
n = root->rb_node;
if (!n)
return NULL;
while (n->rb_left)
n = n->rb_left;
return n;
}
struct rb_node *rb_last(const struct rb_root *root)
{
struct rb_node *n;
n = root->rb_node;
if (!n)
return NULL;
while (n->rb_right)
n = n->rb_right;
return n;
}
struct rb_node *rb_next(const struct rb_node *node)
{
struct rb_node *parent;
if (rb_parent(node) == node)
return NULL;
/* If we have a right-hand child, go down and then left as far
as we can. */
if (node->rb_right) {
node = node->rb_right;
while (node->rb_left)
node=node->rb_left;
return (struct rb_node *)node;
}
/* No right-hand children. Everything down and left is
smaller than us, so any 'next' node must be in the general
direction of our parent. Go up the tree; any time the
ancestor is a right-hand child of its parent, keep going
up. First time it's a left-hand child of its parent, said
parent is our 'next' node. */
while ((parent = rb_parent(node)) && node == parent->rb_right)
node = parent;
return parent;
}
struct rb_node *rb_prev(const struct rb_node *node)
{
struct rb_node *parent;
if (rb_parent(node) == node)
return NULL;
/* If we have a left-hand child, go down and then right as far
as we can. */
if (node->rb_left) {
node = node->rb_left;
while (node->rb_right)
node=node->rb_right;
return (struct rb_node *)node;
}
/* No left-hand children. Go up till we find an ancestor which
is a right-hand child of its parent */
while ((parent = rb_parent(node)) && node == parent->rb_left)
node = parent;
return parent;
}
void rb_replace_node(struct rb_node *victim, struct rb_node *new,
struct rb_root *root)
{
struct rb_node *parent = rb_parent(victim);
/* Set the surrounding nodes to point to the replacement */
if (parent) {
if (victim == parent->rb_left)
parent->rb_left = new;
else
parent->rb_right = new;
} else {
root->rb_node = new;
}
if (victim->rb_left)
rb_set_parent(victim->rb_left, new);
if (victim->rb_right)
rb_set_parent(victim->rb_right, new);
/* Copy the pointers/colour from the victim to the replacement */
*new = *victim;
}

159
daemon/rbtree.h Normal file
View file

@ -0,0 +1,159 @@
/*
Red Black Trees
(C) 1999 Andrea Arcangeli <andrea@suse.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
linux/include/linux/rbtree.h
To use rbtrees you'll have to implement your own insert and search cores.
This will avoid us to use callbacks and to drop drammatically performances.
I know it's not the cleaner way, but in C (not in C++) to get
performances and genericity...
Some example of insert and search follows here. The search is a plain
normal search over an ordered tree. The insert instead must be implemented
int two steps: as first thing the code must insert the element in
order as a red leaf in the tree, then the support library function
rb_insert_color() must be called. Such function will do the
not trivial work to rebalance the rbtree if necessary.
-----------------------------------------------------------------------
static inline struct page * rb_search_page_cache(struct inode * inode,
unsigned long offset)
{
struct rb_node * n = inode->i_rb_page_cache.rb_node;
struct page * page;
while (n)
{
page = rb_entry(n, struct page, rb_page_cache);
if (offset < page->offset)
n = n->rb_left;
else if (offset > page->offset)
n = n->rb_right;
else
return page;
}
return NULL;
}
static inline struct page * __rb_insert_page_cache(struct inode * inode,
unsigned long offset,
struct rb_node * node)
{
struct rb_node ** p = &inode->i_rb_page_cache.rb_node;
struct rb_node * parent = NULL;
struct page * page;
while (*p)
{
parent = *p;
page = rb_entry(parent, struct page, rb_page_cache);
if (offset < page->offset)
p = &(*p)->rb_left;
else if (offset > page->offset)
p = &(*p)->rb_right;
else
return page;
}
rb_link_node(node, parent, p);
return NULL;
}
static inline struct page * rb_insert_page_cache(struct inode * inode,
unsigned long offset,
struct rb_node * node)
{
struct page * ret;
if ((ret = __rb_insert_page_cache(inode, offset, node)))
goto out;
rb_insert_color(node, &inode->i_rb_page_cache);
out:
return ret;
}
-----------------------------------------------------------------------
*/
#ifndef _LINUX_RBTREE_H
#define _LINUX_RBTREE_H
#include <stddef.h>
struct rb_node
{
size_t rb_parent_color;
#define RB_RED 0
#define RB_BLACK 1
struct rb_node *rb_right;
struct rb_node *rb_left;
};
struct rb_root
{
struct rb_node *rb_node;
};
#define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3))
#define rb_color(r) ((r)->rb_parent_color & 1)
#define rb_is_red(r) (!rb_color(r))
#define rb_is_black(r) rb_color(r)
#define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0)
#define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0)
static __inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
{
rb->rb_parent_color = (rb->rb_parent_color & 3) | (size_t)p;
}
static __inline void rb_set_color(struct rb_node *rb, int color)
{
rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
}
#define RB_ROOT (struct rb_root) { NULL, }
#define rb_entry(ptr, type, member) CONTAINING_RECORD(ptr, type, member)
#define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL)
#define RB_EMPTY_NODE(node) (rb_parent(node) == node)
#define RB_CLEAR_NODE(node) (rb_set_parent(node, node))
extern void rb_insert_color(struct rb_node *, struct rb_root *);
extern void rb_erase(struct rb_node *, struct rb_root *);
/* Find logical next and previous nodes in a tree */
extern struct rb_node *rb_next(const struct rb_node *);
extern struct rb_node *rb_prev(const struct rb_node *);
extern struct rb_node *rb_first(const struct rb_root *);
extern struct rb_node *rb_last(const struct rb_root *);
/* Fast replacement of a single node without remove/rebalance/add/rebalance */
extern void rb_replace_node(struct rb_node *victim, struct rb_node *new,
struct rb_root *root);
static __inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
struct rb_node ** rb_link)
{
node->rb_parent_color = (size_t)parent;
node->rb_left = node->rb_right = NULL;
*rb_link = node;
}
#endif /* _LINUX_RBTREE_H */

627
daemon/readdir.c Normal file
View file

@ -0,0 +1,627 @@
/* 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 <Windows.h>
#include <strsafe.h>
#include <stdlib.h>
#include "from_kernel.h"
#include "nfs41_ops.h"
#include "daemon_debug.h"
#include "upcall.h"
#include "util.h"
typedef union _FILE_DIR_INFO_UNION {
ULONG NextEntryOffset;
FILE_NAMES_INFORMATION fni;
FILE_DIRECTORY_INFO fdi;
FILE_FULL_DIR_INFO ffdi;
FILE_ID_FULL_DIR_INFO fifdi;
FILE_BOTH_DIR_INFORMATION fbdi;
FILE_ID_BOTH_DIR_INFO fibdi;
} FILE_DIR_INFO_UNION, *PFILE_DIR_INFO_UNION;
/* NFS41_DIR_QUERY */
int parse_readdir(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
int status;
readdir_upcall_args *args = &upcall->args.readdir;
status = safe_read(&buffer, &length, &args->query_class, sizeof(args->query_class));
if (status) goto out;
status = safe_read(&buffer, &length, &args->buf_len, sizeof(args->buf_len));
if (status) goto out;
status = get_name(&buffer, &length, args->filter);
if (status) goto out;
status = safe_read(&buffer, &length, &args->initial, sizeof(args->initial));
if (status) goto out;
status = safe_read(&buffer, &length, &args->restart, sizeof(args->restart));
if (status) goto out;
status = safe_read(&buffer, &length, &args->single, sizeof(args->single));
if (status) goto out;
status = safe_read(&buffer, &length, &args->root, sizeof(args->root));
if (status) goto out;
status = safe_read(&buffer, &length, &args->state, sizeof(args->state));
if (status) goto out;
status = safe_read(&buffer, &length, &args->cookie, sizeof(args->cookie));
if (status) goto out;
if (args->cookie == INVALID_HANDLE_VALUE) {
dprintf(1, "upcall passing empty cookie\n");
args->cookie = NULL;
}
out:
if (status)
eprintf("parsing NFS41_DIR_QUERY failed with %d\n", status);
else
dprintf(1, "parsing NFS41_DIR_QUERY: info_class=%d buf_len=%d "
"filter='%s'\n\tInitial\\Restart\\Single %d\\%d\\%d "
"root=0x%p state=0x%p cookie=0x%p\n",
args->query_class, args->buf_len, args->filter,
args->initial, args->restart, args->single,
args->root, args->state, args->cookie);
return status;
}
#define FILTER_STAR '*'
#define FILTER_QM '>'
static __inline int readdir_has_wildcards(
const char *filter)
{
return strchr(filter, FILTER_STAR) || strchr(filter, FILTER_QM);
}
static __inline const char* skip_stars(
const char *filter)
{
while (*filter == FILTER_STAR)
filter++;
return filter;
}
static int readdir_filter(
const char *filter,
const char *name)
{
const char *f = filter, *n = name;
while (*f && *n) {
if (*f == FILTER_STAR) {
f = skip_stars(f);
if (*f == '\0')
return 1;
while (*n && !readdir_filter(f, n))
n++;
} else if (*f == FILTER_QM || *f == *n) {
f++;
n++;
} else
return 0;
}
return *f == *n || *skip_stars(f) == '\0';
}
static uint32_t readdir_size_for_entry(
IN int query_class,
IN uint32_t wname_size)
{
uint32_t needed = wname_size;
switch (query_class)
{
case FileDirectoryInformation:
needed += FIELD_OFFSET(FILE_DIRECTORY_INFO, FileName);
break;
case FileIdFullDirectoryInformation:
needed += FIELD_OFFSET(FILE_ID_FULL_DIR_INFO, FileName);
break;
case FileFullDirectoryInformation:
needed += FIELD_OFFSET(FILE_FULL_DIR_INFO, FileName);
break;
case FileIdBothDirectoryInformation:
needed += FIELD_OFFSET(FILE_ID_BOTH_DIR_INFO, FileName);
break;
case FileBothDirectoryInformation:
needed += FIELD_OFFSET(FILE_BOTH_DIR_INFORMATION, FileName);
break;
case FileNamesInformation:
needed += FIELD_OFFSET(FILE_NAMES_INFORMATION, FileName);
break;
default:
eprintf("unhandled dir query class %d\n", query_class);
return 0;
}
return needed;
}
static void readdir_copy_dir_info(
IN nfs41_readdir_entry *entry,
IN PFILE_DIR_INFO_UNION info)
{
info->fdi.FileIndex = 0;
nfs_time_to_file_time(&entry->attr_info.time_create,
&info->fdi.CreationTime);
nfs_time_to_file_time(&entry->attr_info.time_access,
&info->fdi.LastAccessTime);
nfs_time_to_file_time(&entry->attr_info.time_modify,
&info->fdi.LastWriteTime);
/* XXX: was using 'change' attr, but that wasn't giving a time */
nfs_time_to_file_time(&entry->attr_info.time_modify,
&info->fdi.ChangeTime);
info->fdi.EndOfFile.QuadPart =
info->fdi.AllocationSize.QuadPart =
entry->attr_info.size;
info->fdi.FileAttributes = nfs_file_info_to_attributes(
&entry->attr_info);
}
static void readdir_copy_shortname(
IN LPCWSTR name,
OUT LPWSTR name_out,
OUT CCHAR *name_size_out)
{
/* GetShortPathName returns number of characters, not including \0 */
*name_size_out = (CCHAR)GetShortPathNameW(name, name_out, 12);
if (*name_size_out) {
*name_size_out++;
*name_size_out *= sizeof(WCHAR);
}
}
static void readdir_copy_full_dir_info(
IN nfs41_readdir_entry *entry,
IN PFILE_DIR_INFO_UNION info)
{
readdir_copy_dir_info(entry, info);
info->fifdi.EaSize = 0;
}
static void readdir_copy_both_dir_info(
IN nfs41_readdir_entry *entry,
IN LPWSTR wname,
IN PFILE_DIR_INFO_UNION info)
{
readdir_copy_full_dir_info(entry, info);
readdir_copy_shortname(wname, info->fbdi.ShortName,
&info->fbdi.ShortNameLength);
}
static void readdir_copy_filename(
IN LPCWSTR name,
IN uint32_t name_size,
OUT LPWSTR name_out,
OUT ULONG *name_size_out)
{
*name_size_out = name_size;
memcpy(name_out, name, name_size);
}
static int readdir_copy_entry(
IN readdir_upcall_args *args,
IN nfs41_readdir_entry *entry,
IN OUT unsigned char **dst_pos,
IN OUT uint32_t *dst_len)
{
int status = 0;
WCHAR wname[NFS4_OPAQUE_LIMIT];
uint32_t wname_len, wname_size, needed;
PFILE_DIR_INFO_UNION info;
wname_len = MultiByteToWideChar(CP_UTF8, 0,
entry->name, entry->name_len, wname, NFS4_OPAQUE_LIMIT);
wname_size = (wname_len - 1) * sizeof(WCHAR);
needed = readdir_size_for_entry(args->query_class, wname_size);
if (!needed || needed > *dst_len) {
status = -1;
goto out;
}
info = (PFILE_DIR_INFO_UNION)*dst_pos;
info->NextEntryOffset = align8(needed);
*dst_pos += info->NextEntryOffset;
*dst_len -= info->NextEntryOffset;
switch (args->query_class)
{
case FileNamesInformation:
info->fni.FileIndex = 0;
readdir_copy_filename(wname, wname_size,
info->fni.FileName, &info->fni.FileNameLength);
break;
case FileDirectoryInformation:
readdir_copy_dir_info(entry, info);
readdir_copy_filename(wname, wname_size,
info->fdi.FileName, &info->fdi.FileNameLength);
break;
case FileFullDirectoryInformation:
readdir_copy_full_dir_info(entry, info);
readdir_copy_filename(wname, wname_size,
info->ffdi.FileName, &info->ffdi.FileNameLength);
break;
case FileIdFullDirectoryInformation:
readdir_copy_full_dir_info(entry, info);
info->fibdi.FileId.QuadPart = (LONGLONG)entry->attr_info.fileid;
readdir_copy_filename(wname, wname_size,
info->fifdi.FileName, &info->fifdi.FileNameLength);
break;
case FileBothDirectoryInformation:
readdir_copy_both_dir_info(entry, wname, info);
readdir_copy_filename(wname, wname_size,
info->fbdi.FileName, &info->fbdi.FileNameLength);
break;
case FileIdBothDirectoryInformation:
readdir_copy_both_dir_info(entry, wname, info);
info->fibdi.FileId.QuadPart = (LONGLONG)entry->attr_info.fileid;
readdir_copy_filename(wname, wname_size,
info->fibdi.FileName, &info->fibdi.FileNameLength);
break;
default:
eprintf("unhandled dir query class %d\n", args->query_class);
status = -1;
break;
}
out:
return status;
}
#define COOKIE_DOT ((uint64_t)-2)
#define COOKIE_DOTDOT ((uint64_t)-1)
int readdir_add_dots(
IN readdir_upcall_args *args,
IN OUT unsigned char *entry_buf,
IN uint32_t entry_buf_len,
OUT uint32_t *len_out,
OUT uint32_t **last_offset)
{
int status = 0;
const uint32_t entry_len = (uint32_t)FIELD_OFFSET(nfs41_readdir_entry, name);
nfs41_readdir_entry *entry;
nfs41_open_state *state = args->state;
*len_out = 0;
*last_offset = NULL;
switch (args->cookie->cookie) {
case 0:
if (entry_buf_len < entry_len + 2) {
status = ERROR_BUFFER_OVERFLOW;
dprintf(1, "not enough room for '.' entry.\n");
goto out;
}
entry = (nfs41_readdir_entry*)entry_buf;
ZeroMemory(&entry->attr_info, sizeof(nfs41_file_info));
status = nfs41_cached_getattr(state->session,
&state->file, &entry->attr_info);
if (status) {
dprintf(1, "failed to add '.' entry.\n");
goto out;
}
entry->cookie = COOKIE_DOT;
entry->name_len = 2;
StringCbCopyA(entry->name, entry->name_len, ".");
entry->next_entry_offset = entry_len + entry->name_len;
entry_buf += entry->next_entry_offset;
entry_buf_len -= entry->next_entry_offset;
*len_out += entry->next_entry_offset;
*last_offset = &entry->next_entry_offset;
if (args->single)
break;
/* else no break! */
case COOKIE_DOT:
if (entry_buf_len < entry_len + 3) {
status = ERROR_BUFFER_OVERFLOW;
dprintf(1, "not enough room for '..' entry.\n");
goto out;
}
/* XXX: this skips '..' when listing root fh */
if (state->file.name.len == 0)
break;
entry = (nfs41_readdir_entry*)entry_buf;
ZeroMemory(&entry->attr_info, sizeof(nfs41_file_info));
status = nfs41_cached_getattr(state->session,
&state->parent, &entry->attr_info);
if (status) {
status = ERROR_FILE_NOT_FOUND;
dprintf(1, "failed to add '..' entry.\n");
goto out;
}
entry->cookie = COOKIE_DOTDOT;
entry->name_len = 3;
StringCbCopyA(entry->name, entry->name_len, "..");
entry->next_entry_offset = entry_len + entry->name_len;
entry_buf += entry->next_entry_offset;
entry_buf_len -= entry->next_entry_offset;
*len_out += entry->next_entry_offset;
*last_offset = &entry->next_entry_offset;
break;
}
if (args->cookie->cookie == COOKIE_DOTDOT ||
args->cookie->cookie == COOKIE_DOT)
ZeroMemory(args->cookie, sizeof(nfs41_readdir_cookie));
out:
return status;
}
static int single_lookup(
IN nfs41_root *root,
IN nfs41_session *session,
IN nfs41_path_fh *parent,
IN const char *filter,
IN bitmap4 *attr_request,
OUT nfs41_readdir_entry *entry)
{
nfs41_abs_path path;
nfs41_path_fh file;
int status;
entry->cookie = 0;
entry->name_len = (uint32_t)strlen(filter) + 1;
StringCbCopyA(entry->name, entry->name_len, filter);
entry->next_entry_offset = 0;
/* format an absolute path 'parent\filter' */
InitializeSRWLock(&path.lock);
abs_path_copy(&path, parent->path);
if (path.len + entry->name_len >= NFS41_MAX_PATH_LEN) {
status = ERROR_BUFFER_OVERFLOW;
goto out;
}
StringCchPrintfA(path.path + path.len,
NFS41_MAX_PATH_LEN - path.len, "\\%s", entry->name);
path.len += (unsigned short)entry->name_len;
path_fh_init(&file, &path);
status = nfs41_lookup(root, session, &path,
NULL, &file, &entry->attr_info, NULL);
if (status) {
dprintf(1, "nfs41_lookup failed with %s\n", nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
goto out;
}
out:
return status;
}
int handle_readdir(nfs41_upcall *upcall)
{
int status;
readdir_upcall_args *args = &upcall->args.readdir;
nfs41_open_state *state = args->state;
unsigned char *entry_buf = NULL;
uint32_t entry_buf_len;
bitmap4 attr_request;
bool_t eof;
dprintf(1, "-> handle_nfs41_dirquery(%s,%d,%d,%d)\n",
args->filter, args->initial, args->restart, args->single);
args->buf = NULL;
args->query_reply_len = 0;
if (args->cookie) { /* cookie exists */
if (args->restart) {
dprintf(1, "restarting; clearing previous cookie (%d %p)\n",
args->cookie->cookie, args->cookie);
ZeroMemory(args->cookie, sizeof(nfs41_readdir_cookie));
} else if (args->initial) { /* shouldn't happen */
dprintf(1, "*** initial; clearing previous cookie (%d %p)!\n",
args->cookie->cookie, args->cookie);
ZeroMemory(args->cookie, sizeof(nfs41_readdir_cookie));
} else
dprintf(1, "resuming enumeration with cookie %d.\n",
args->cookie->cookie);
} else { /* cookie is null */
if (args->initial || args->restart) {
dprintf(1, "allocating memory for the 1st readdir cookie\n");
args->cookie = calloc(1, sizeof(nfs41_readdir_cookie));
if (args->cookie == NULL) {
status = GetLastError();
goto out;
}
} else {
dprintf(1, "handle_nfs41_readdir: EOF\n");
status = ERROR_NO_MORE_FILES;
goto out;
}
}
entry_buf = malloc(args->buf_len);
if (entry_buf == NULL) {
status = GetLastError();
goto out_free_cookie;
}
fetch_entries:
entry_buf_len = args->buf_len;
init_getattr_request(&attr_request);
attr_request.arr[0] |= FATTR4_WORD0_RDATTR_ERROR;
if (readdir_has_wildcards((const char*)args->filter)) {
/* use READDIR for wildcards */
uint32_t dots_len = 0;
uint32_t *dots_next_offset = NULL;
if (args->filter[0] == '*' && args->filter[1] == '\0') {
status = readdir_add_dots(args, entry_buf,
entry_buf_len, &dots_len, &dots_next_offset);
if (status)
goto out_free_cookie;
entry_buf_len -= dots_len;
}
if (dots_len && args->single) {
dprintf(2, "skipping nfs41_readdir because the single query "
"will use . or ..\n");
entry_buf_len = 0;
eof = 0;
} else {
dprintf(2, "calling nfs41_readdir with cookie %d %p \n",
args->cookie->cookie, args->cookie);
status = nfs41_readdir(state->session, &state->file,
&attr_request, args->cookie, entry_buf + dots_len,
&entry_buf_len, &eof);
if (status) {
dprintf(1, "nfs41_readdir failed with %s\n",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
goto out_free_cookie;
}
}
if (!entry_buf_len && dots_next_offset)
*dots_next_offset = 0;
entry_buf_len += dots_len;
} else {
/* use LOOKUP for single files */
nfs41_readdir_entry *entry = (nfs41_readdir_entry*)entry_buf;
entry->cookie = 0;
entry->name_len = (uint32_t)strlen(args->filter) + 1;
StringCbCopyA(entry->name, entry->name_len, args->filter);
entry->next_entry_offset = 0;
status = single_lookup(args->root, state->session,
&state->file, args->filter, &attr_request, entry);
if (status) {
dprintf(1, "single_lookup failed with %d\n", status);
goto out_free_cookie;
}
entry_buf_len = entry->name_len +
FIELD_OFFSET(nfs41_readdir_entry, name);
eof = 1;
}
status = args->initial ? ERROR_FILE_NOT_FOUND : ERROR_NO_MORE_FILES;
if (entry_buf_len) {
unsigned char *entry_pos = entry_buf;
unsigned char *dst_pos;
uint32_t dst_len = args->buf_len;
nfs41_readdir_entry *entry;
PULONG offset, last_offset = NULL;
if (args->buf == NULL) {
args->buf = malloc(args->buf_len);
if (args->buf == NULL) {
status = GetLastError();
goto out_free_cookie;
}
}
dst_pos = args->buf;
for (;;) {
entry = (nfs41_readdir_entry*)entry_pos;
offset = (PULONG)dst_pos; /* ULONG NextEntryOffset */
dprintf(2, "filter %s looking at %s with cookie %d\n",
args->filter, entry->name, entry->cookie);
if (readdir_filter((const char*)args->filter, entry->name)) {
if (readdir_copy_entry(args, entry, &dst_pos, &dst_len)) {
eof = 0;
dprintf(2, "not enough space to copy entry %s (cookie %d)\n",
entry->name, entry->cookie);
break;
}
last_offset = offset;
status = NO_ERROR;
}
args->cookie->cookie = entry->cookie;
/* last entry we got from the server */
if (!entry->next_entry_offset)
break;
/* we found our single entry, but the server has more */
if (args->single && last_offset) {
eof = 0;
break;
}
entry_pos += entry->next_entry_offset;
}
args->query_reply_len = args->buf_len - dst_len;
if (last_offset) {
*last_offset = 0;
} else if (!eof) {
dprintf(1, "no entries matched; fetch more\n");
goto fetch_entries;
}
}
if (eof) {
dprintf(1, "we don't need to save a cookie\n");
goto out_free_cookie;
} else
dprintf(1, "saving cookie %d %p\n", args->cookie->cookie, args->cookie);
out_free_entry:
free(entry_buf);
out:
dprintf(1, "<- handle_nfs41_dirquery(%s,%d,%d,%d) returning ",
args->filter, args->initial, args->restart, args->single);
if (status) {
switch (status) {
case ERROR_FILE_NOT_FOUND:
dprintf(1, "ERROR_FILE_NOT_FOUND.\n");
break;
case ERROR_NO_MORE_FILES:
dprintf(1, "ERROR_NO_MORE_FILES.\n");
break;
default:
dprintf(1, "error code %d.\n", status);
break;
}
free(args->buf);
args->buf = NULL;
} else {
dprintf(1, "success!\n");
}
return status;
out_free_cookie:
free(args->cookie);
args->cookie = NULL;
goto out_free_entry;
}
int marshall_readdir(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
{
int status;
readdir_upcall_args *args = &upcall->args.readdir;
status = safe_write(&buffer, length, &args->query_reply_len, sizeof(args->query_reply_len));
if (status) goto out;
status = safe_write(&buffer, length, args->buf, args->query_reply_len);
if (status) goto out;
status = safe_write(&buffer, length, &args->cookie, sizeof(args->cookie));
out:
free(args->buf);
return status;
}

307
daemon/readwrite.c Normal file
View file

@ -0,0 +1,307 @@
/* 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 <Windows.h>
#include <stdio.h>
#include "nfs41_ops.h"
#include "name_cache.h"
#include "upcall.h"
#include "daemon_debug.h"
#include "util.h"
int parse_rw(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
int status;
readwrite_upcall_args *args = &upcall->args.rw;
status = safe_read(&buffer, &length, &args->len, sizeof(args->len));
if (status) goto out;
status = safe_read(&buffer, &length, &args->offset, sizeof(args->offset));
if (status) goto out;
status = safe_read(&buffer, &length, &args->buffer, sizeof(args->buffer));
if (status) goto out;
status = safe_read(&buffer, &length, &args->root, sizeof(args->root));
if (status) goto out;
status = safe_read(&buffer, &length, &args->state, sizeof(args->state));
out:
if (status)
eprintf("parsing %s failed with %d\n",
opcode2string(upcall->opcode), status);
else
dprintf(1, "parsing %s len=%ld offset=%ld buf=%p root=%p "
"open_state=0x%p\n", opcode2string(upcall->opcode), args->len,
args->offset, args->buffer, args->root, args->state);
return status;
}
/* NFS41_READ */
static int read_from_mds(
IN nfs41_session *session,
IN stateid4 *stateid,
IN nfs41_path_fh *file,
IN uint64_t offset,
IN uint32_t length,
OUT unsigned char *buffer,
OUT ULONG *len_out)
{
int status = 0;
bool_t eof;
unsigned char *p = buffer;
ULONG to_rcv = length, reloffset = 0, len = 0;
const uint32_t maxreadsize = max_read_size(session, &file->fh);
if (to_rcv > maxreadsize)
dprintf(1, "handle_nfs41_read: reading %d in chunks of %d\n",
to_rcv, maxreadsize);
while(to_rcv > 0) {
uint32_t bytes_read = 0, chunk = min(to_rcv, maxreadsize);
status = nfs41_read(session, file, stateid,
offset + reloffset, chunk, p, &bytes_read, &eof);
if (status && !len) {
status = nfs_to_windows_error(status, ERROR_NET_WRITE_FAULT);
goto out;
}
p += bytes_read;
to_rcv -= bytes_read;
len += bytes_read;
offset += bytes_read;
if (status) {
status = NO_ERROR;
break;
}
if (eof) {
if (!len)
status = ERROR_HANDLE_EOF;
break;
}
}
out:
*len_out = len;
return status;
}
static int read_from_pnfs(
IN nfs41_root *root,
IN nfs41_open_state *state,
IN stateid4 *stateid,
IN uint64_t offset,
IN uint32_t length,
OUT unsigned char *buffer,
OUT ULONG *len_out)
{
pnfs_file_layout *layout;
enum pnfs_status pnfsstat;
int status = NO_ERROR;
pnfsstat = pnfs_open_state_layout(state->session->client->layouts,
state->session, state, PNFS_IOMODE_READ, offset, length, &layout);
if (pnfsstat) {
status = ERROR_NOT_SUPPORTED;
goto out;
}
pnfsstat = pnfs_read(root, state->session, &state->file, stateid,
layout, offset, length, buffer, len_out);
switch (pnfsstat) {
case PNFS_SUCCESS:
break;
case PNFS_READ_EOF:
status = ERROR_HANDLE_EOF;
break;
default:
status = ERROR_READ_FAULT;
break;
}
out:
return status;
}
int handle_read(nfs41_upcall *upcall)
{
stateid4 stateid, *pstateid;
readwrite_upcall_args *args = &upcall->args.rw;
nfs41_open_state *state = args->state;
ULONG pnfs_bytes_read = 0;
int status = NO_ERROR;
pstateid = nfs41_lock_stateid_copy(&state->last_lock, &stateid);
if (pstateid == NULL)
pstateid = &state->stateid;
#ifdef PNFS_ENABLE_READ
status = read_from_pnfs(args->root, state, pstateid,
args->offset, args->len, args->buffer, &args->out_len);
if (status == NO_ERROR || status == ERROR_HANDLE_EOF)
goto out;
if (args->out_len) {
pnfs_bytes_read = args->out_len;
args->out_len = 0;
args->offset += pnfs_bytes_read;
args->buffer += pnfs_bytes_read;
args->len -= pnfs_bytes_read;
}
#endif
status = read_from_mds(state->session, pstateid, &state->file,
args->offset, args->len, args->buffer, &args->out_len);
args->out_len += pnfs_bytes_read;
out:
return status;
}
/* NFS41_WRITE */
static int write_to_mds(
IN nfs41_session *session,
IN stateid4 *stateid,
IN nfs41_path_fh *file,
IN uint64_t offset,
IN uint32_t length,
IN unsigned char *buffer,
OUT ULONG *len_out)
{
nfs41_write_verf verf;
enum stable_how4 stable, committed;
unsigned char *p;
const uint32_t maxwritesize = max_write_size(session, &file->fh);
uint32_t to_send, reloffset, len;
int status = 0;
retry_write:
p = buffer;
to_send = length;
reloffset = 0;
len = 0;
stable = to_send <= maxwritesize ? DATA_SYNC4 : UNSTABLE4;
committed = DATA_SYNC4;
if (to_send > maxwritesize)
dprintf(1, "handle_nfs41_write: writing %d in chunks of %d\n",
to_send, maxwritesize);
while(to_send > 0) {
uint32_t bytes_written = 0, chunk = min(to_send, maxwritesize);
status = nfs41_write(session, file, stateid, p, chunk,
offset + reloffset, stable, &bytes_written, &verf);
if (status && !len)
goto out;
p += bytes_written;
to_send -= bytes_written;
len += bytes_written;
reloffset += bytes_written;
if (status) {
status = 0;
break;
}
if (!verify_write(&verf, &committed))
goto retry_write;
}
if (committed == UNSTABLE4) {
dprintf(1, "sending COMMIT for offset=%d and len=%d\n", offset, len);
status = nfs41_commit(session, file, offset, len, 1);
}
out:
*len_out = len;
return nfs_to_windows_error(status, ERROR_NET_WRITE_FAULT);
}
static int write_to_pnfs(
IN nfs41_root *root,
IN nfs41_open_state *state,
IN stateid4 *stateid,
IN uint64_t offset,
IN uint32_t length,
IN unsigned char *buffer,
OUT ULONG *len_out)
{
pnfs_file_layout *layout;
enum pnfs_status pnfsstat;
int status = NO_ERROR;
pnfsstat = pnfs_open_state_layout(state->session->client->layouts,
state->session, state, PNFS_IOMODE_RW, offset, length, &layout);
if (pnfsstat) {
status = ERROR_NOT_SUPPORTED;
goto out;
}
pnfsstat = pnfs_write(root, state->session, &state->file, stateid,
layout, offset, length, buffer, len_out);
if (pnfsstat) {
status = ERROR_WRITE_FAULT;
goto out;
}
out:
return status;
}
int handle_write(nfs41_upcall *upcall)
{
stateid4 stateid, *pstateid = NULL;
readwrite_upcall_args *args = &upcall->args.rw;
nfs41_open_state *state = args->state;
ULONG pnfs_bytes_written = 0;
int status;
pstateid = nfs41_lock_stateid_copy(&state->last_lock, &stateid);
if (pstateid == NULL)
pstateid = &state->stateid;
#ifdef PNFS_ENABLE_WRITE
status = write_to_pnfs(args->root, args->state, pstateid,
args->offset, args->len, args->buffer, &args->out_len);
if (status == NO_ERROR)
goto out;
if (args->out_len) {
pnfs_bytes_written = args->out_len;
args->out_len = 0;
args->offset += pnfs_bytes_written;
args->buffer += pnfs_bytes_written;
args->len -= pnfs_bytes_written;
}
#endif
status = write_to_mds(state->session, pstateid, &state->file,
args->offset, args->len, args->buffer, &args->out_len);
args->out_len += pnfs_bytes_written;
out:
return status;
}
int marshall_rw(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
{
readwrite_upcall_args *args = &upcall->args.rw;
return safe_write(&buffer, length, &args->out_len, sizeof(args->out_len));
}

491
daemon/setattr.c Normal file
View file

@ -0,0 +1,491 @@
/* 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 <Windows.h>
#include <stdio.h>
#include <strsafe.h>
#include "from_kernel.h"
#include "nfs41_ops.h"
#include "name_cache.h"
#include "upcall.h"
#include "util.h"
#include "daemon_debug.h"
int parse_setattr(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
int status;
setattr_upcall_args *args = &upcall->args.setattr;
ZeroMemory(&args->path, sizeof(nfs41_abs_path));
status = get_abs_path(&buffer, &length, &args->path);
if (status) goto out;
status = safe_read(&buffer, &length, &args->set_class, sizeof(args->set_class));
if (status) goto out;
status = safe_read(&buffer, &length, &args->buf_len, sizeof(args->buf_len));
if (status) goto out;
args->buf = malloc(args->buf_len);
if (args->buf == NULL) {
status = GetLastError();
goto out;
}
status = safe_read(&buffer, &length, args->buf, args->buf_len);
if (status) goto out_free;
status = safe_read(&buffer, &length, &args->root, sizeof(args->root));
if (status) goto out_free;
status = safe_read(&buffer, &length, &args->state, sizeof(args->state));
if (status) goto out_free;
status = safe_read(&buffer, &length, &args->open_owner_id, sizeof(ULONG));
if (status) goto out_free;
status = safe_read(&buffer, &length, &args->access_mask, sizeof(ULONG));
if (status) goto out_free;
status = safe_read(&buffer, &length, &args->access_mode, sizeof(ULONG));
out:
if (status)
eprintf("parsing NFS41_FILE_SET failed with %d\n", status);
else
dprintf(1, "parsing NFS41_FILE_SET: filename='%s' info_class=%d "
"buf_len=%d root=%p open_state=%p\nopen_owner_id=%d "
"access_mask=%x access_mode=%x\n", args->path.path, args->set_class,
args->buf_len, args->root, args->state, args->open_owner_id,
args->access_mask, args->access_mode);
return status;
out_free:
free(args->buf);
goto out;
}
static void remove_unsupported_attrs(
IN const bitmap4 *supported_attrs,
IN OUT bitmap4 *attrs)
{
uint32_t i, count = 0;
dprintf(2, "remove_unsupported_attrs\n");
for (i = 0; i < 3; i++) {
dprintf(2, "\tmask[%d] = %12u", i, attrs->arr[i]);
dprintf(2, " & %12u", supported_attrs->arr[i]);
attrs->arr[i] &= supported_attrs->arr[i];
if (attrs->arr[i])
count = i+1;
dprintf(2, " = %12d\n", attrs->arr[i]);
}
attrs->count = min(attrs->count, count);
dprintf(2, "\tcount = %d\n", attrs->count);
}
static int handle_nfs41_setattr(setattr_upcall_args *args)
{
int status;
PFILE_BASIC_INFO basic_info = (PFILE_BASIC_INFO)args->buf;
nfs41_open_state *state = args->state;
nfs41_superblock *superblock = state->file.fh.superblock;
stateid4 stateid, *pstateid;
nfs41_file_info info;
pstateid = nfs41_lock_stateid_copy(&state->last_lock, &stateid);
if (pstateid == NULL)
pstateid = &state->stateid;
ZeroMemory(&info, sizeof(info));
/* hidden */
info.hidden = basic_info->FileAttributes & FILE_ATTRIBUTE_HIDDEN ? 1 : 0;
info.attrmask.arr[0] |= FATTR4_WORD0_HIDDEN;
info.attrmask.count = 1;
/* time_create */
if (basic_info->CreationTime.QuadPart > 0) {
file_time_to_nfs_time(&basic_info->CreationTime, &info.time_create);
info.attrmask.arr[1] |= FATTR4_WORD1_TIME_CREATE;
info.attrmask.count = 2;
}
/* time_access_set */
if (basic_info->LastAccessTime.QuadPart > 0) {
file_time_to_nfs_time(&basic_info->LastAccessTime, &info.time_access);
info.attrmask.arr[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
info.attrmask.count = 2;
}
/* time_modify_set */
if (basic_info->LastWriteTime.QuadPart > 0) {
file_time_to_nfs_time(&basic_info->LastWriteTime, &info.time_modify);
info.attrmask.arr[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
info.attrmask.count = 2;
}
/* mode */
if (basic_info->FileAttributes & FILE_ATTRIBUTE_READONLY) {
info.mode = 0444;
info.attrmask.arr[1] |= FATTR4_WORD1_MODE;
info.attrmask.count = 2;
}
AcquireSRWLockShared(&superblock->lock);
remove_unsupported_attrs(&superblock->supported_attrs, &info.attrmask);
ReleaseSRWLockShared(&superblock->lock);
if (!info.attrmask.count)
return 0;
status = nfs41_setattr(state->session, &state->file, pstateid, &info);
if (status)
dprintf(1, "nfs41_setattr() failed with error %s.\n",
nfs_error_string(status));
return nfs_to_windows_error(status, ERROR_NOT_SUPPORTED);
}
static int handle_nfs41_remove(setattr_upcall_args *args)
{
nfs41_open_state *state = args->state;
int status;
status = nfs41_remove(state->session, &state->parent,
&state->file.name);
if (status)
dprintf(1, "nfs41_remove() failed with error %s.\n",
nfs_error_string(status));
return nfs_to_windows_error(status, ERROR_ACCESS_DENIED);
}
static void open_state_rename(
OUT nfs41_open_state *state,
IN const nfs41_abs_path *path)
{
AcquireSRWLockExclusive(&state->path.lock);
abs_path_copy(&state->path, path);
last_component(state->path.path, state->path.path + state->path.len,
&state->file.name);
last_component(state->path.path, state->file.name.name,
&state->parent.name);
ReleaseSRWLockExclusive(&state->path.lock);
}
static int handle_nfs41_rename(setattr_upcall_args *args)
{
nfs41_open_state *state = args->state;
PFILE_RENAME_INFO rename = (PFILE_RENAME_INFO)args->buf;
nfs41_abs_path dst_path;
nfs41_path_fh dst_dir;
nfs41_component dst_name, *src_name;
int status;
ZeroMemory(&dst_path, sizeof(dst_path));
src_name = &state->file.name;
if (rename->FileNameLength == 0) {
/* start from state->path instead of args->path, in case we got
* the file from a referred server */
AcquireSRWLockShared(&state->path.lock);
abs_path_copy(&args->path, &state->path);
ReleaseSRWLockShared(&state->path.lock);
path_fh_init(&dst_dir, &args->path);
fh_copy(&dst_dir.fh, &state->parent.fh);
create_silly_rename(&args->path, &state->file.fh, &dst_name);
dprintf(1, "silly rename: %s -> %s\n", src_name->name, dst_name.name);
status = nfs41_rename(state->session,
&state->parent, src_name,
&dst_dir, &dst_name);
if (status) {
dprintf(1, "nfs41_rename() failed with error %s.\n",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_ACCESS_DENIED);
} else {
/* rename state->path on success */
open_state_rename(state, &args->path);
}
goto out;
}
dst_path.len = (unsigned short)WideCharToMultiByte(CP_UTF8, 0,
rename->FileName, rename->FileNameLength/sizeof(WCHAR),
dst_path.path, NFS41_MAX_PATH_LEN, NULL, NULL);
if (dst_path.len == 0) {
eprintf("WideCharToMultiByte failed to convert destination "
"filename %S.\n", rename->FileName);
status = ERROR_INVALID_PARAMETER;
goto out;
}
path_fh_init(&dst_dir, &dst_path);
/* the destination path is absolute, so start from the root session */
status = nfs41_lookup(args->root, nfs41_root_session(args->root),
&dst_path, &dst_dir, NULL, NULL, NULL);
/* get the components after lookup in case a referral changed its path */
last_component(dst_path.path, dst_path.path + dst_path.len, &dst_name);
last_component(dst_path.path, dst_name.name, &dst_dir.name);
if (status == NO_ERROR) {
if (!rename->ReplaceIfExists) {
status = ERROR_FILE_EXISTS;
goto out;
}
} else if (status != ERROR_FILE_NOT_FOUND) {
dprintf(1, "nfs41_lookup('%s') failed to find destination "
"directory with %d\n", dst_path.path, status);
goto out;
}
/* http://tools.ietf.org/html/rfc5661#section-18.26.3
* "Source and target directories MUST reside on the same
* file system on the server." */
if (state->parent.fh.superblock != dst_dir.fh.superblock) {
status = ERROR_NOT_SAME_DEVICE;
goto out;
}
status = nfs41_rename(state->session,
&state->parent, src_name,
&dst_dir, &dst_name);
if (status) {
dprintf(1, "nfs41_rename() failed with error %s.\n",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_ACCESS_DENIED);
} else {
/* rename state->path on success */
open_state_rename(state, &dst_path);
}
out:
return status;
}
static int handle_nfs41_set_size(setattr_upcall_args *args)
{
nfs41_open_state *state = args->state;
int status;
/* note: this is called with either FILE_END_OF_FILE_INFO or
* FILE_ALLOCATION_INFO, both of which contain a single LARGE_INTEGER */
PLARGE_INTEGER size = (PLARGE_INTEGER)args->buf;
stateid4 stateid, *pstateid;
nfs41_file_info info;
pstateid = nfs41_lock_stateid_copy(&state->last_lock, &stateid);
if (pstateid == NULL)
pstateid = &state->stateid;
ZeroMemory(&info, sizeof(info));
info.size = size->QuadPart;
info.attrmask.count = 1;
info.attrmask.arr[0] = FATTR4_WORD0_SIZE;
dprintf(2, "calling setattr() with size=%lld\n", info.size);
status = nfs41_setattr(state->session, &state->file, pstateid, &info);
if (status)
dprintf(1, "nfs41_setattr() failed with error %s.\n",
nfs_error_string(status));
return status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED);
}
int handle_nfs41_link(setattr_upcall_args *args)
{
nfs41_open_state *state = args->state;
PFILE_LINK_INFORMATION link = (PFILE_LINK_INFORMATION)args->buf;
nfs41_abs_path dst_path;
nfs41_path_fh dst_dir;
nfs41_component dst_name;
int status;
ZeroMemory(&dst_path, sizeof(dst_path));
dst_path.len = (unsigned short)WideCharToMultiByte(CP_UTF8, 0,
link->FileName, link->FileNameLength/sizeof(WCHAR),
dst_path.path, NFS41_MAX_PATH_LEN, NULL, NULL);
if (dst_path.len == 0) {
eprintf("WideCharToMultiByte failed to convert destination "
"filename %S.\n", link->FileName);
status = ERROR_INVALID_PARAMETER;
goto out;
}
path_fh_init(&dst_dir, &dst_path);
/* the destination path is absolute, so start from the root session */
status = nfs41_lookup(args->root, nfs41_root_session(args->root),
&dst_path, &dst_dir, NULL, NULL, NULL);
/* get the components after lookup in case a referral changed its path */
last_component(dst_path.path, dst_path.path + dst_path.len, &dst_name);
last_component(dst_path.path, dst_name.name, &dst_dir.name);
if (status == NO_ERROR) {
if (!link->ReplaceIfExists) {
status = ERROR_FILE_EXISTS;
goto out;
}
} else if (status != ERROR_FILE_NOT_FOUND) {
dprintf(1, "nfs41_lookup('%s') failed to find destination "
"directory with %d\n", dst_path.path, status);
goto out;
}
/* http://tools.ietf.org/html/rfc5661#section-18.9.3
* "The existing file and the target directory must reside within
* the same file system on the server." */
if (state->file.fh.superblock != dst_dir.fh.superblock) {
status = ERROR_NOT_SAME_DEVICE;
goto out;
}
if (status == NO_ERROR) {
/* LINK will return NFS4ERR_EXIST if the target file exists,
* so we have to remove it ourselves */
status = nfs41_remove(state->session, &dst_dir, &dst_name);
if (status) {
dprintf(1, "nfs41_remove() failed with error %s.\n",
nfs_error_string(status));
status = ERROR_FILE_EXISTS;
goto out;
}
}
status = nfs41_link(state->session, &state->file, &dst_dir, &dst_name);
if (status) {
dprintf(1, "nfs41_link() failed with error %s.\n",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_INVALID_PARAMETER);
}
out:
return status;
}
int handle_setattr(nfs41_upcall *upcall)
{
setattr_upcall_args *args = &upcall->args.setattr;
nfs41_open_state *state = args->state;
int status;
switch (args->set_class) {
case FileAllocationInformation:
case FileEndOfFileInformation:
if (!state->do_close) {
// get a stateid
uint32_t allow = 0, deny = 0;
StringCchPrintfA((LPSTR)state->owner.owner, NFS4_OPAQUE_LIMIT,
"%u", args->open_owner_id);
state->owner.owner_len = (uint32_t)strlen(
(const char*)state->owner.owner);
map_access_2_allowdeny(args->access_mask, args->access_mode, &allow, &deny);
status = nfs41_open(state->session, allow, deny,
OPEN4_NOCREATE, 0, state, NULL);
if (status) {
dprintf(1, "nfs41_open() failed with %s\n", nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_FILE_NOT_FOUND);
goto out;
}
state->do_close = 1;
}
}
switch (args->set_class) {
case FileBasicInformation:
status = handle_nfs41_setattr(args);
break;
case FileDispositionInformation:
status = handle_nfs41_remove(args);
break;
case FileRenameInformation:
status = handle_nfs41_rename(args);
break;
case FileAllocationInformation:
case FileEndOfFileInformation:
status = handle_nfs41_set_size(args);
break;
case FileLinkInformation:
status = handle_nfs41_link(args);
break;
default:
eprintf("unknown set_file information class %d\n",
args->set_class);
status = ERROR_NOT_SUPPORTED;
break;
}
out:
free(args->buf);
return status;
}
int marshall_setattr(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
{
return NO_ERROR;
}
int parse_setexattr(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
int status;
setexattr_upcall_args *args = &upcall->args.setexattr;
status = safe_read(&buffer, &length, &args->root, sizeof(args->root));
if (status) goto out;
status = safe_read(&buffer, &length, &args->state, sizeof(args->state));
if (status) goto out;
status = safe_read(&buffer, &length, &args->mode, sizeof(args->mode));
out:
if (status)
eprintf("parsing NFS41_EA_SET failed with %d\n", status);
else
dprintf(1, "parsing NFS41_EA_SET: root=%p open_state=%p mode=%o\n",
args->root, args->state, args->mode);
return status;
}
int handle_setexattr(nfs41_upcall *upcall)
{
int status;
setexattr_upcall_args *args = &upcall->args.setexattr;
nfs41_open_state *state = args->state;
stateid4 stateid, *pstateid;
nfs41_file_info info;
pstateid = nfs41_lock_stateid_copy(&state->last_lock, &stateid);
if (pstateid == NULL)
pstateid = &state->stateid;
ZeroMemory(&info, sizeof(info));
/* mode */
info.mode = args->mode;
info.attrmask.arr[1] |= FATTR4_WORD1_MODE;
info.attrmask.count = 2;
status = nfs41_setattr(state->session, &state->file, pstateid, &info);
if (status)
dprintf(1, "nfs41_setattr() failed with error %s.\n",
nfs_error_string(status));
return nfs_to_windows_error(status, ERROR_NOT_SUPPORTED);
}
int marshall_setexattr(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
{
return NO_ERROR;
}

24
daemon/sources Normal file
View file

@ -0,0 +1,24 @@
TARGETTYPE=PROGRAM
TARGETNAME=nfsd
SOURCES=nfs41_daemon.c daemon_debug.c nfs41_ops.c nfs41_compound.c nfs41_xdr.c \
nfs41_server.c nfs41_client.c nfs41_superblock.c nfs41_session.c lookup.c \
mount.c open.c readwrite.c lock.c readdir.c getattr.c setattr.c upcall.c \
nfs41_rpc.c util.c pnfs_layout.c pnfs_device.c pnfs_debug.c pnfs_io.c \
name_cache.c namespace.c rbtree.c volume.c callback_server.c callback_xdr.c
UMTYPE=console
USE_LIBCMT=1
#USE_MSVCRT=1
INCLUDES=..\sys;..\dll;..\libtirpc\tirpc
TARGETLIBS=$(SDK_LIB_PATH)\ws2_32.lib $(SDK_LIB_PATH)\iphlpapi.lib \
..\libtirpc\src\obj$(BUILD_ALT_DIR)\*\libtirpc.lib
!IF 0
/W3 is default level
bump to /Wall, but suppress warnings generated by system includes,
as well as the following warnings:
4100 - unused function call arguments (we have lots of stubs)
4127 - constant conditional (I like to use if(0) or if(1))
4220 - varargs matching remaining parameters
4204 - nonstandard extension
!ENDIF
MSC_WARNING_LEVEL=/Wall /wd4668 /wd4619 /wd4820 /wd4255 /wd4100 /wd4127 /wd4711 /wd4220 /wd4204

222
daemon/upcall.c Normal file
View file

@ -0,0 +1,222 @@
/* 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 <Windows.h>
#include <stdio.h>
#include "upcall.h"
#include "daemon_debug.h"
#include "util.h"
int parse_mount(unsigned char*, uint32_t, nfs41_upcall*);
int handle_mount(nfs41_upcall*);
int marshall_mount(unsigned char*, uint32_t*, nfs41_upcall*);
int parse_unmount(unsigned char*, uint32_t, nfs41_upcall*);
int handle_unmount(nfs41_upcall*);
int marshall_unmount(unsigned char*, uint32_t*, nfs41_upcall*);
int parse_open(unsigned char*, uint32_t, nfs41_upcall*);
int handle_open(nfs41_upcall*);
int marshall_open(unsigned char*, uint32_t*, nfs41_upcall*);
int cancel_open(nfs41_upcall*);
int parse_close(unsigned char*, uint32_t, nfs41_upcall*);
int handle_close(nfs41_upcall*);
int marshall_close(unsigned char*, uint32_t*, nfs41_upcall*);
int parse_rw(unsigned char*, uint32_t, nfs41_upcall*);
int handle_read(nfs41_upcall*);
int handle_write(nfs41_upcall*);
int marshall_rw(unsigned char*, uint32_t*, nfs41_upcall*);
int parse_lock(unsigned char*, uint32_t, nfs41_upcall*);
int handle_lock(nfs41_upcall*);
int marshall_lock(unsigned char*, uint32_t*, nfs41_upcall*);
int cancel_lock(nfs41_upcall*);
int parse_unlock(unsigned char*, uint32_t, nfs41_upcall*);
int handle_unlock(nfs41_upcall*);
int marshall_unlock(unsigned char*, uint32_t*, nfs41_upcall*);
int parse_readdir(unsigned char*, uint32_t, nfs41_upcall*);
int handle_readdir(nfs41_upcall*);
int marshall_readdir(unsigned char*, uint32_t*, nfs41_upcall*);
int parse_getattr(unsigned char*, uint32_t, nfs41_upcall*);
int handle_getattr(nfs41_upcall*);
int marshall_getattr(unsigned char*, uint32_t*, nfs41_upcall*);
int parse_setattr(unsigned char*, uint32_t, nfs41_upcall*);
int handle_setattr(nfs41_upcall*);
int marshall_setattr(unsigned char*, uint32_t*, nfs41_upcall*);
int parse_setexattr(unsigned char*, uint32_t, nfs41_upcall*);
int handle_setexattr(nfs41_upcall*);
int marshall_setexattr(unsigned char*, uint32_t*, nfs41_upcall*);
int parse_volume(unsigned char*, uint32_t, nfs41_upcall*);
int handle_volume(nfs41_upcall*);
int marshall_volume(unsigned char*, uint32_t*, nfs41_upcall*);
static const nfs41_upcall_op g_upcall_op_table[] = {
{ parse_mount, handle_mount, marshall_mount, NULL },
{ parse_unmount, handle_unmount, marshall_unmount, NULL },
{ parse_open, handle_open, marshall_open, cancel_open },
{ parse_close, handle_close, marshall_close, NULL },
{ parse_rw, handle_read, marshall_rw, NULL },
{ parse_rw, handle_write, marshall_rw, NULL },
{ parse_lock, handle_lock, marshall_lock, cancel_lock },
{ parse_unlock, handle_unlock, marshall_unlock, NULL },
{ parse_readdir, handle_readdir, marshall_readdir, NULL },
{ parse_getattr, handle_getattr, marshall_getattr, NULL },
{ parse_setattr, handle_setattr, marshall_setattr, NULL },
{ parse_setexattr, handle_setexattr, marshall_setexattr, NULL },
{ parse_volume, handle_volume, marshall_volume, NULL },
{ NULL, NULL, NULL, NULL }, /* NFS41_SHUTDOWN */
{ NULL, NULL, NULL, NULL }, /* INVALID_OPCODE */
};
static const uint32_t g_upcall_op_table_size = ARRAYSIZE(g_upcall_op_table);
int upcall_parse(
IN unsigned char *buffer,
IN uint32_t length,
OUT nfs41_upcall *upcall)
{
int status;
const nfs41_upcall_op *op;
ZeroMemory(upcall, sizeof(nfs41_upcall));
if (!length) {
eprintf("empty upcall\n");
upcall->status = status = 102;
goto out;
}
dprintf(2, "received %d bytes upcall data: processing upcall\n", length);
print_hexbuf(3, (unsigned char *)"upcall buffer: ", buffer, length);
/* parse common elements */
status = safe_read(&buffer, &length, &upcall->xid, sizeof(uint32_t));
if (status) goto out;
status = safe_read(&buffer, &length, &upcall->opcode, sizeof(uint32_t));
if (status) goto out;
dprintf(2, "xid=%d opcode=%s\n", upcall->xid, opcode2string(upcall->opcode));
if (upcall->opcode >= g_upcall_op_table_size) {
status = ERROR_NOT_SUPPORTED;
eprintf("unrecognized upcall opcode %d!\n", upcall->opcode);
goto out;
}
/* parse the operation's arguments */
op = &g_upcall_op_table[upcall->opcode];
if (op->parse) {
status = op->parse(buffer, length, upcall);
if (status) {
eprintf("parsing of upcall '%s' failed with %d.\n",
opcode2string(upcall->opcode), status);
goto out;
}
}
out:
return status;
}
int upcall_handle(
IN nfs41_upcall *upcall)
{
int status = NO_ERROR;
const nfs41_upcall_op *op;
op = &g_upcall_op_table[upcall->opcode];
if (op->handle == NULL) {
status = ERROR_NOT_SUPPORTED;
eprintf("upcall '%s' missing handle function!\n",
opcode2string(upcall->opcode));
goto out;
}
upcall->status = op->handle(upcall);
out:
return status;
}
int upcall_marshall(
IN nfs41_upcall *upcall,
OUT unsigned char *buffer,
IN uint32_t length,
OUT uint32_t *length_out)
{
int status = NO_ERROR;
const nfs41_upcall_op *op;
unsigned char *orig_buf = buffer;
const uint32_t total = length, orig_len = length;
/* marshall common elements */
write_downcall:
length = orig_len;
buffer = orig_buf;
safe_write(&buffer, &length, &upcall->xid, sizeof(upcall->xid));
safe_write(&buffer, &length, &upcall->opcode, sizeof(upcall->opcode));
safe_write(&buffer, &length, &upcall->status, sizeof(upcall->status));
safe_write(&buffer, &length, &upcall->last_error, sizeof(upcall->last_error));
if (upcall->status)
goto out;
/* marshall the operation's results */
op = &g_upcall_op_table[upcall->opcode];
if (op->marshall == NULL) {
status = ERROR_NOT_SUPPORTED;
eprintf("upcall '%s' missing marshall function!\n",
opcode2string(upcall->opcode));
upcall->status = status;
goto write_downcall;
}
status = op->marshall(buffer, &length, upcall);
if (status) {
upcall->status = status;
goto write_downcall;
}
out:
*length_out = total - length;
return status;
}
int upcall_cancel(
IN nfs41_upcall *upcall)
{
int status = NO_ERROR;
const nfs41_upcall_op *op;
op = &g_upcall_op_table[upcall->opcode];
if (op->cancel)
status = op->cancel(upcall);
return status;
}

201
daemon/upcall.h Normal file
View file

@ -0,0 +1,201 @@
/* 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 __NFS41_DAEMON_UPCALL_H__
#define __NFS41_DAEMON_UPCALL_H__
#include "nfs41_ops.h"
/* structures for upcall arguments */
typedef struct __mount_upcall_args {
char srv_name[UPCALL_BUF_SIZE];
nfs41_abs_path path;
nfs41_root *root;
} mount_upcall_args;
typedef struct __unmount_upcall_args {
nfs41_root *root;
} unmount_upcall_args;
typedef struct __open_upcall_args {
nfs41_abs_path path;
FILE_BASIC_INFO basic_info;
FILE_STANDARD_INFO std_info;
nfs41_root *root;
nfs41_open_state *state;
ULONG access_mask;
ULONG access_mode;
ULONG file_attrs;
ULONG disposition;
ULONG create_opts;
ULONG open_owner_id;
DWORD mode;
LONGLONG changeattr;
BOOLEAN created;
} open_upcall_args;
typedef struct __close_upcall_args {
nfs41_abs_path path;
nfs41_root *root;
nfs41_open_state *state;
BOOLEAN remove;
BOOLEAN renamed;
} close_upcall_args;
typedef struct __readwrite_upcall_args {
nfs41_root *root;
nfs41_open_state *state;
unsigned char *buffer;
LONGLONG offset;
ULONG len;
ULONG out_len;
} readwrite_upcall_args;
typedef struct __lock_upcall_args {
nfs41_open_state *state;
nfs41_root *root;
LONGLONG offset;
LONGLONG length;
BOOLEAN exclusive;
BOOLEAN blocking;
} lock_upcall_args;
typedef struct __unlock_upcall_args {
nfs41_open_state *state;
nfs41_root *root;
uint32_t count;
unsigned char *buf;
uint32_t buf_len;
} unlock_upcall_args;
typedef struct __getattr_upcall_args {
FILE_BASIC_INFO basic_info;
FILE_STANDARD_INFO std_info;
FILE_ATTRIBUTE_TAG_INFO tag_info;
nfs41_root *root;
nfs41_open_state *state;
int query_class;
int buf_len;
int query_reply_len;
} getattr_upcall_args;
typedef struct __setattr_upcall_args {
nfs41_abs_path path;
nfs41_root *root;
nfs41_open_state *state;
unsigned char *buf;
uint32_t buf_len;
int set_class;
ULONG open_owner_id;
ULONG access_mask;
ULONG access_mode;
} setattr_upcall_args;
typedef struct __setexattr_upcall_args {
nfs41_root *root;
nfs41_open_state *state;
uint32_t mode;
} setexattr_upcall_args;
typedef struct __readdir_upcall_args {
char filter[UPCALL_BUF_SIZE];
FILE_BASIC_INFO basic_info;
FILE_STANDARD_INFO std_info;
FILE_ATTRIBUTE_TAG_INFO tag_info;
nfs41_readdir_cookie *cookie;
nfs41_root *root;
nfs41_open_state *state;
unsigned char *buf;
int buf_len;
int query_class;
int query_reply_len;
BOOLEAN initial;
BOOLEAN restart;
BOOLEAN single;
} readdir_upcall_args;
typedef struct __volume_upcall_args {
nfs41_root *root;
ULONGLONG total;
ULONGLONG user;
ULONGLONG avail;
} volume_upcall_args;
typedef union __upcall_args {
mount_upcall_args mount;
unmount_upcall_args unmount;
open_upcall_args open;
close_upcall_args close;
readwrite_upcall_args rw;
lock_upcall_args lock;
unlock_upcall_args unlock;
getattr_upcall_args getattr;
setattr_upcall_args setattr;
setexattr_upcall_args setexattr;
readdir_upcall_args readdir;
volume_upcall_args volume;
} upcall_args;
typedef struct __nfs41_upcall {
uint32_t xid;
uint32_t opcode;
uint32_t status;
uint32_t last_error;
upcall_args args;
} nfs41_upcall;
/* upcall operation interface */
typedef int (*upcall_parse_proc)(unsigned char*, uint32_t, nfs41_upcall*);
typedef int (*upcall_handle_proc)(nfs41_upcall*);
typedef int (*upcall_marshall_proc)(unsigned char*, uint32_t*, nfs41_upcall*);
typedef int (*upcall_cancel_proc)(nfs41_upcall*);
typedef struct __nfs41_upcall_op {
upcall_parse_proc parse;
upcall_handle_proc handle;
upcall_marshall_proc marshall;
upcall_cancel_proc cancel;
} nfs41_upcall_op;
/* upcall.c */
int upcall_parse(
IN unsigned char *buffer,
IN uint32_t length,
OUT nfs41_upcall *upcall);
int upcall_handle(
IN nfs41_upcall *upcall);
int upcall_marshall(
IN nfs41_upcall *upcall,
OUT unsigned char *buffer,
IN uint32_t length,
OUT uint32_t *length_out);
int upcall_cancel(
IN nfs41_upcall *upcall);
#endif /* !__NFS41_DAEMON_UPCALL_H__ */

450
daemon/util.c Normal file
View file

@ -0,0 +1,450 @@
/* 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 <Windows.h>
#include <strsafe.h>
#include <stdio.h>
#include <stdlib.h>
#include "daemon_debug.h"
#include "util.h"
#include "nfs41_ops.h"
int safe_read(unsigned char **pos, uint32_t *remaining, void *dest, uint32_t dest_len)
{
if (*remaining < dest_len)
return ERROR_BUFFER_OVERFLOW;
CopyMemory(dest, *pos, dest_len);
*pos += dest_len;
*remaining -= dest_len;
return 0;
}
int safe_write(unsigned char **pos, uint32_t *remaining, void *src, uint32_t src_len)
{
if (*remaining < src_len)
return ERROR_BUFFER_OVERFLOW;
CopyMemory(*pos, src, src_len);
*pos += src_len;
*remaining -= src_len;
return 0;
}
int wchar2asci(WCHAR *src, char **dest, int dest_len)
{
int len = 0;
len = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL);
if (*dest == NULL) {
*dest = malloc(len + 1);
if (*dest == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
} else if (len > dest_len)
return ERROR_BUFFER_OVERFLOW;
WideCharToMultiByte(CP_UTF8, 0, src, len, *dest, len + 1, NULL, NULL);
return 0;
}
int get_name(unsigned char **pos, uint32_t *remaining, char *out_name)
{
WCHAR name[UPCALL_BUF_SIZE];
int status, len = 0;
status = safe_read(pos, remaining, &len, sizeof(USHORT));
if (status) goto out;
status = safe_read(pos, remaining, name, len);
if (status) goto out;
name[len/sizeof(WCHAR)] = L'\0';
status = wchar2asci(name, &out_name, UPCALL_BUF_SIZE);
out:
return status;
}
int get_abs_path(unsigned char **pos, uint32_t *remaining, nfs41_abs_path *path)
{
int status = get_name(pos, remaining, path->path);
path->len = status ? 0 : (unsigned short)strlen(path->path);
return status;
}
const char* strip_path(
IN const char *path,
OUT uint32_t *len_out)
{
const char *name = strrchr(path, '\\');
name = name ? name + 1 : path;
if (len_out)
*len_out = (uint32_t)strlen(name);
return name;
}
uint32_t max_read_size(
IN const nfs41_session *session,
IN const nfs41_fh *fh)
{
const uint32_t maxresponse = session->fore_chan_attrs.ca_maxresponsesize;
return (uint32_t)min(fh->superblock->maxread, maxresponse - READ_OVERHEAD);
}
uint32_t max_write_size(
IN const nfs41_session *session,
IN const nfs41_fh *fh)
{
const uint32_t maxrequest = session->fore_chan_attrs.ca_maxrequestsize;
return (uint32_t)min(fh->superblock->maxwrite, maxrequest - WRITE_OVERHEAD);
}
bool_t verify_write(
IN nfs41_write_verf *verf,
IN OUT enum stable_how4 *stable)
{
if (verf->committed != UNSTABLE4) {
*stable = verf->committed;
dprintf(3, "verify_write: committed to stable storage\n");
return 1;
}
if (*stable != UNSTABLE4) {
memcpy(verf->expected, verf->verf, NFS4_VERIFIER_SIZE);
*stable = UNSTABLE4;
dprintf(3, "verify_write: first unstable write, saving verifier\n");
return 1;
}
if (memcmp(verf->expected, verf->verf, NFS4_VERIFIER_SIZE) == 0) {
dprintf(3, "verify_write: verifier matches expected\n");
return 1;
}
dprintf(2, "verify_write: verifier changed; writes have been lost!\n");
return 0;
}
ULONG nfs_file_info_to_attributes(
IN const nfs41_file_info *info)
{
ULONG attrs = 0;
if (info->type == NF4DIR)
attrs |= FILE_ATTRIBUTE_DIRECTORY;
else if (info->type != NF4REG)
dprintf(1, "unhandled file type %d, defaulting to NF4REG\n",
info->type);
if (info->mode == 0444) /* XXX: 0444 for READONLY */
attrs |= FILE_ATTRIBUTE_READONLY;
/* TODO: FILE_ATTRIBUTE_HIDDEN */
// FILE_ATTRIBUTE_NORMAL attribute is only set if no other attributes are present.
// all other override this value.
return attrs ? attrs : FILE_ATTRIBUTE_NORMAL;
}
void nfs_to_basic_info(
IN const nfs41_file_info *info,
OUT PFILE_BASIC_INFO basic_out)
{
nfs_time_to_file_time(&info->time_create, &basic_out->CreationTime);
nfs_time_to_file_time(&info->time_access, &basic_out->LastAccessTime);
nfs_time_to_file_time(&info->time_modify, &basic_out->LastWriteTime);
/* XXX: was using 'change' attr, but that wasn't giving a time */
nfs_time_to_file_time(&info->time_modify, &basic_out->ChangeTime);
basic_out->FileAttributes = nfs_file_info_to_attributes(info);
}
void nfs_to_standard_info(
IN const nfs41_file_info *info,
OUT PFILE_STANDARD_INFO std_out)
{
std_out->AllocationSize.QuadPart =
std_out->EndOfFile.QuadPart = (LONGLONG)info->size;
std_out->NumberOfLinks = info->numlinks;
std_out->DeletePending = FALSE;
std_out->Directory = info->type == NF4DIR ? TRUE : FALSE;
}
/* http://msdn.microsoft.com/en-us/library/ms724290%28VS.85%29.aspx:
* A file time is a 64-bit value that represents the number of
* 100-nanosecond intervals that have elapsed since 12:00 A.M.
* January 1, 1601 Coordinated Universal Time (UTC). */
static __inline void get_file_epoch(
OUT PLARGE_INTEGER time_out)
{
static const SYSTEMTIME jan_1_1970 = {1970, 1, 4, 1, 0, 0, 0, 0};
SystemTimeToFileTime(&jan_1_1970, (LPFILETIME)time_out);
}
void file_time_to_nfs_time(
IN const PLARGE_INTEGER file_time,
OUT nfstime4 *nfs_time)
{
LARGE_INTEGER diff;
get_file_epoch(&diff);
diff.QuadPart = file_time->QuadPart - diff.QuadPart;
nfs_time->seconds = diff.QuadPart / 10000000;
nfs_time->nseconds = (uint32_t)((diff.QuadPart % 10000000)*100);
}
void nfs_time_to_file_time(
IN const nfstime4 *nfs_time,
OUT PLARGE_INTEGER file_time)
{
LARGE_INTEGER diff;
get_file_epoch(&diff);
file_time->QuadPart = diff.QuadPart +
nfs_time->seconds * 10000000 +
nfs_time->nseconds / 100;
}
void get_file_time(
OUT PLARGE_INTEGER file_time)
{
GetSystemTimeAsFileTime((LPFILETIME)file_time);
}
void get_nfs_time(
OUT nfstime4 *nfs_time)
{
LARGE_INTEGER file_time;
get_file_time(&file_time);
file_time_to_nfs_time(&file_time, nfs_time);
}
void map_access_2_allowdeny(ULONG access_mask, ULONG access_mode,
uint32_t *allow, uint32_t *deny)
{
if ((access_mask & FILE_WRITE_DATA) &&
((access_mask & FILE_READ_DATA) ||
(access_mask & FILE_EXECUTE)))
*allow = OPEN4_SHARE_ACCESS_BOTH;
else if ((access_mask & FILE_READ_DATA) ||
(access_mask & FILE_EXECUTE))
*allow = OPEN4_SHARE_ACCESS_READ;
else if (access_mask & FILE_WRITE_DATA ||
(access_mask & FILE_APPEND_DATA) ||
(access_mask & FILE_WRITE_ATTRIBUTES))
*allow = OPEN4_SHARE_ACCESS_WRITE;
#define FIX_ALLOW_DENY_WIN2NFS_CONVERSION
#ifdef FIX_ALLOW_DENY_WIN2NFS_CONVERSION
if ((access_mode & FILE_SHARE_READ) &&
(access_mode & FILE_SHARE_WRITE))
*deny = OPEN4_SHARE_DENY_NONE;
else if (access_mode & FILE_SHARE_READ)
*deny = OPEN4_SHARE_DENY_WRITE;
else if (access_mode & FILE_SHARE_WRITE)
*deny = OPEN4_SHARE_DENY_READ;
else
*deny = OPEN4_SHARE_DENY_BOTH;
#else
// AGLO: 11/13/2009.
// readonly file that is being opened for reading with a
// share read mode given above logic translates into deny
// write and linux server does not allow it.
*deny = OPEN4_SHARE_DENY_NONE;
#endif
}
bool_t multi_addr_find(
IN const multi_addr4 *addrs,
IN const netaddr4 *addr,
OUT OPTIONAL uint32_t *index_out)
{
uint32_t i;
for (i = 0; i < addrs->count; i++) {
const netaddr4 *saddr = &addrs->arr[i];
if (!strncmp(saddr->netid, addr->netid, NFS41_NETWORK_ID_LEN) &&
!strncmp(saddr->uaddr, addr->uaddr, NFS41_UNIVERSAL_ADDR_LEN)) {
if (index_out) *index_out = i;
return 1;
}
}
return 0;
}
int nfs_to_windows_error(int status, int default_error)
{
/* make sure this is actually an nfs error */
if (status < 0 || (status > 70 && status < 10001) || status > 10087) {
eprintf("nfs_to_windows_error called with non-nfs "
"error code %d; returning the error as is\n", status);
return status;
}
switch (status) {
case NFS4_OK: return NO_ERROR;
case NFS4ERR_PERM: return ERROR_ACCESS_DENIED;
case NFS4ERR_NOENT: return ERROR_FILE_NOT_FOUND;
case NFS4ERR_IO: return ERROR_NET_WRITE_FAULT;
case NFS4ERR_ACCESS: return ERROR_ACCESS_DENIED;
case NFS4ERR_EXIST: return ERROR_FILE_EXISTS;
case NFS4ERR_XDEV: return ERROR_NOT_SAME_DEVICE;
case NFS4ERR_INVAL: return ERROR_INVALID_PARAMETER;
case NFS4ERR_FBIG: return ERROR_FILE_TOO_LARGE;
case NFS4ERR_NOSPC: return ERROR_DISK_FULL;
case NFS4ERR_ROFS: return ERROR_NETWORK_ACCESS_DENIED;
case NFS4ERR_MLINK: return ERROR_TOO_MANY_LINKS;
case NFS4ERR_NAMETOOLONG: return ERROR_FILENAME_EXCED_RANGE;
case NFS4ERR_STALE: return ERROR_NETNAME_DELETED;
case NFS4ERR_NOTEMPTY: return ERROR_NOT_EMPTY;
case NFS4ERR_DENIED: return ERROR_LOCK_FAILED;
case NFS4ERR_TOOSMALL: return ERROR_BUFFER_OVERFLOW;
case NFS4ERR_LOCKED: return ERROR_LOCK_VIOLATION;
case NFS4ERR_SHARE_DENIED: return ERROR_SHARING_VIOLATION;
case NFS4ERR_LOCK_RANGE: return ERROR_NOT_LOCKED;
case NFS4ERR_ATTRNOTSUPP: return ERROR_NOT_SUPPORTED;
case NFS4ERR_OPENMODE: return ERROR_ACCESS_DENIED;
case NFS4ERR_LOCK_NOTSUPP: return ERROR_ATOMIC_LOCKS_NOT_SUPPORTED;
case NFS4ERR_BADCHAR:
case NFS4ERR_BADNAME: return ERROR_INVALID_NAME;
case NFS4ERR_NOTDIR:
case NFS4ERR_ISDIR:
case NFS4ERR_SYMLINK:
case NFS4ERR_WRONG_TYPE: return ERROR_INVALID_PARAMETER;
case NFS4ERR_OLD_STATEID:
case NFS4ERR_BAD_STATEID:
case NFS4ERR_ADMIN_REVOKED: return ERROR_FILE_INVALID;
default:
dprintf(1, "nfs error %s not mapped to windows error; "
"returning default error %d\n",
nfs_error_string(status), default_error);
return default_error;
}
}
bool_t next_component(
IN const char *path,
IN const char *path_end,
OUT nfs41_component *component)
{
const char *component_end;
component->name = next_non_delimiter(path, path_end);
component_end = next_delimiter(component->name, path_end);
component->len = (unsigned short)(component_end - component->name);
return component->len > 0;
}
bool_t last_component(
IN const char *path,
IN const char *path_end,
OUT nfs41_component *component)
{
const char *component_end = prev_delimiter(path_end, path);
component->name = prev_non_delimiter(component_end, path);
component->name = prev_delimiter(component->name, path);
component->name = next_non_delimiter(component->name, component_end);
component->len = (unsigned short)(component_end - component->name);
return component->len > 0;
}
bool_t is_last_component(
IN const char *path,
IN const char *path_end)
{
path = next_delimiter(path, path_end);
return next_non_delimiter(path, path_end) == path_end;
}
void abs_path_copy(
OUT nfs41_abs_path *dst,
IN const nfs41_abs_path *src)
{
dst->len = src->len;
StringCchCopyNA(dst->path, NFS41_MAX_PATH_LEN, src->path, dst->len);
}
void path_fh_init(
OUT nfs41_path_fh *file,
IN nfs41_abs_path *path)
{
file->path = path;
last_component(path->path, path->path + path->len, &file->name);
}
void fh_copy(
OUT nfs41_fh *dst,
IN const nfs41_fh *src)
{
dst->fileid = src->fileid;
dst->superblock = src->superblock;
dst->len = src->len;
memcpy(dst->fh, src->fh, dst->len);
}
void path_fh_copy(
OUT nfs41_path_fh *dst,
IN const nfs41_path_fh *src)
{
dst->path = src->path;
if (dst->path) {
const size_t name_start = src->name.name - src->path->path;
dst->name.name = dst->path->path + name_start;
dst->name.len = src->name.len;
} else {
dst->name.name = NULL;
dst->name.len = 0;
}
fh_copy(&dst->fh, &src->fh);
}
int create_silly_rename(
IN nfs41_abs_path *path,
IN const nfs41_fh *fh,
OUT nfs41_component *silly)
{
const char *end = path->path + NFS41_MAX_PATH_LEN;
const unsigned short extra_len = 2 + 2*(unsigned short)fh->len;
char name[NFS41_MAX_COMPONENT_LEN+1];
char *tmp;
uint32_t i;
int status = NO_ERROR;
if (path->len + extra_len >= NFS41_MAX_PATH_LEN) {
status = ERROR_BUFFER_OVERFLOW;
goto out;
}
last_component(path->path, path->path + path->len, silly);
StringCchCopyNA(name, NFS41_MAX_COMPONENT_LEN+1, silly->name, silly->len);
tmp = (char*)silly->name;
StringCchPrintf(tmp, end - tmp, ".%s.", name);
tmp += silly->len + 2;
for (i = 0; i < fh->len; i++, tmp += 2)
StringCchPrintf(tmp, end - tmp, "%02x", fh->fh[i]);
path->len += extra_len;
silly->len += extra_len;
out:
return status;
}

166
daemon/util.h Normal file
View file

@ -0,0 +1,166 @@
/* 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 __NFS41_DAEMON_UTIL_H__
#define __NFS41_DAEMON_UTIL_H__
#include "nfs41_types.h"
struct __nfs41_session;
struct __nfs41_write_verf;
enum stable_how4;
int safe_read(unsigned char **pos, uint32_t *remaining, void *dest, uint32_t dest_len);
int safe_write(unsigned char **pos, uint32_t *remaining, void *dest, uint32_t dest_len);
int get_name(unsigned char **pos, uint32_t *remaining, char *out_name);
int get_abs_path(unsigned char **pos, uint32_t *remaining, nfs41_abs_path *path);
int wchar2asci(WCHAR *src, char **dest, int dest_len);
const char* strip_path(
IN const char *path,
OUT uint32_t *len_out OPTIONAL);
uint32_t max_read_size(
IN const struct __nfs41_session *session,
IN const nfs41_fh *fh);
uint32_t max_write_size(
IN const struct __nfs41_session *session,
IN const nfs41_fh *fh);
bool_t verify_write(
IN struct __nfs41_write_verf *verf,
IN OUT enum stable_how4 *stable);
ULONG nfs_file_info_to_attributes(
IN const nfs41_file_info *info);
void nfs_to_basic_info(
IN const nfs41_file_info *info,
OUT PFILE_BASIC_INFO basic_out);
void nfs_to_standard_info(
IN const nfs41_file_info *info,
OUT PFILE_STANDARD_INFO std_out);
void file_time_to_nfs_time(
IN const PLARGE_INTEGER file_time,
OUT nfstime4 *nfs_time);
void nfs_time_to_file_time(
IN const nfstime4 *nfs_time,
OUT PLARGE_INTEGER file_time);
void get_file_time(
OUT PLARGE_INTEGER file_time);
void get_nfs_time(
OUT nfstime4 *nfs_time);
int create_silly_rename(
IN nfs41_abs_path *path,
IN const nfs41_fh *fh,
OUT nfs41_component *silly);
void map_access_2_allowdeny(
IN ULONG access_mask,
IN ULONG access_mode,
OUT uint32_t *allow,
OUT uint32_t *deny);
bool_t multi_addr_find(
IN const multi_addr4 *addrs,
IN const netaddr4 *addr,
OUT OPTIONAL uint32_t *index_out);
/* nfs_to_windows_error
* Returns a windows ERROR_ code corresponding to the given NFS4ERR_ status.
* If the status is outside the range of valid NFS4ERR_ values, it is returned
* unchanged. Otherwise, if the status does not match a value in the mapping,
* a debug warning is generated and the default_error value is returned.
*/
int nfs_to_windows_error(int status, int default_error);
__inline uint32_t align8(uint32_t offset) {
return 8 + ((offset - 1) & ~7);
}
__inline uint32_t align4(uint32_t offset) {
return 4 + ((offset - 1) & ~3);
}
/* path parsing */
__inline int is_delimiter(char c) {
return c == '\\' || c == '/' || c == '\0';
}
__inline const char* next_delimiter(const char *pos, const char *end) {
while (pos < end && !is_delimiter(*pos))
pos++;
return pos;
}
__inline const char* prev_delimiter(const char *pos, const char *start) {
while (pos > start && !is_delimiter(*pos))
pos--;
return pos;
}
__inline const char* next_non_delimiter(const char *pos, const char *end) {
while (pos < end && is_delimiter(*pos))
pos++;
return pos;
}
__inline const char* prev_non_delimiter(const char *pos, const char *start) {
while (pos > start && is_delimiter(*pos))
pos--;
return pos;
}
bool_t next_component(
IN const char *path,
IN const char *path_end,
OUT nfs41_component *component);
bool_t last_component(
IN const char *path,
IN const char *path_end,
OUT nfs41_component *component);
bool_t is_last_component(
IN const char *path,
IN const char *path_end);
void abs_path_copy(
OUT nfs41_abs_path *dst,
IN const nfs41_abs_path *src);
void path_fh_init(
OUT nfs41_path_fh *file,
IN nfs41_abs_path *path);
void fh_copy(
OUT nfs41_fh *dst,
IN const nfs41_fh *src);
void path_fh_copy(
OUT nfs41_path_fh *dst,
IN const nfs41_path_fh *src);
__inline int valid_handle(HANDLE handle) {
return handle != INVALID_HANDLE_VALUE && handle != 0;
}
#endif /* !__NFS41_DAEMON_UTIL_H__ */

86
daemon/volume.c Normal file
View file

@ -0,0 +1,86 @@
/* 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 <Windows.h>
#include <stdio.h>
#include "nfs41_ops.h"
#include "from_kernel.h"
#include "upcall.h"
#include "util.h"
#include "daemon_debug.h"
int parse_volume(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
{
int status;
volume_upcall_args *args = &upcall->args.volume;
status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE));
if (status)
eprintf("parsing NFS41_VOLUME_QUERY failed with %d\n",
status);
else
dprintf(1, "parsing NFS41_VOLUME_QUERY: root=0x%p\n", args->root);
return status;
}
int handle_volume(nfs41_upcall *upcall)
{
nfs41_file_info info = { 0 };
bitmap4 attr_request = { 2, { 0, FATTR4_WORD1_SPACE_AVAIL |
FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL } };
volume_upcall_args *args = &upcall->args.volume;
int status;
/* query the space_ attributes of the root filesystem */
status = nfs41_getattr(nfs41_root_session(args->root),
NULL, &attr_request, &info);
if (status) {
eprintf("nfs41_getattr() failed with %s\n",
nfs_error_string(status));
status = nfs_to_windows_error(status, ERROR_BAD_NET_RESP);
goto out;
}
args->total = info.space_total; /* total disk space in bytes */
args->user = info.space_avail; /* bytes available to this user */
args->avail = info.space_free; /* free disk space in bytes */
dprintf(2, "Volume: %llu user, %llu free of %llu total\n",
args->user, args->avail, args->total);
out:
return status;
}
int marshall_volume(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
{
int status;
volume_upcall_args *args = &upcall->args.volume;
status = safe_write(&buffer, length, &args->total, sizeof(args->total));
if (status) goto out;
status = safe_write(&buffer, length, &args->user, sizeof(args->user));
if (status) goto out;
status = safe_write(&buffer, length, &args->avail, sizeof(args->avail));
out:
return status;
}