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:
commit
0ad4db4fad
271 changed files with 71255 additions and 0 deletions
381
daemon/callback_server.c
Normal file
381
daemon/callback_server.c
Normal 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
539
daemon/callback_xdr.c
Normal 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
440
daemon/daemon_debug.c
Normal 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
73
daemon/daemon_debug.h
Normal 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
195
daemon/from_kernel.h
Normal 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
152
daemon/getattr.c
Normal 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
116
daemon/list.h
Normal 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
252
daemon/lock.c
Normal 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
500
daemon/lookup.c
Normal 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 = ⌖
|
||||
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 = ⌖
|
||||
} 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
8
daemon/makefile
Normal 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
131
daemon/mount.c
Normal 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
1243
daemon/name_cache.c
Normal file
File diff suppressed because it is too large
Load diff
99
daemon/name_cache.h
Normal file
99
daemon/name_cache.h
Normal 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
458
daemon/namespace.c
Normal 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
357
daemon/nfs41.h
Normal 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
254
daemon/nfs41_callback.h
Normal 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
437
daemon/nfs41_client.c
Normal 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
333
daemon/nfs41_compound.c
Normal 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
82
daemon/nfs41_compound.h
Normal 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
306
daemon/nfs41_const.h
Normal 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
217
daemon/nfs41_daemon.c
Normal 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
1745
daemon/nfs41_ops.c
Normal file
File diff suppressed because it is too large
Load diff
1074
daemon/nfs41_ops.h
Normal file
1074
daemon/nfs41_ops.h
Normal file
File diff suppressed because it is too large
Load diff
298
daemon/nfs41_rpc.c
Normal file
298
daemon/nfs41_rpc.c
Normal 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
332
daemon/nfs41_server.c
Normal 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
385
daemon/nfs41_session.c
Normal 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
228
daemon/nfs41_superblock.c
Normal 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
185
daemon/nfs41_types.h
Normal 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
3124
daemon/nfs41_xdr.c
Normal file
File diff suppressed because it is too large
Load diff
33
daemon/nfs41_xdr.h
Normal file
33
daemon/nfs41_xdr.h
Normal 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
445
daemon/open.c
Normal 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
352
daemon/pnfs.h
Normal 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
125
daemon/pnfs_debug.c
Normal 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
376
daemon/pnfs_device.c
Normal 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
565
daemon/pnfs_io.c
Normal 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
835
daemon/pnfs_layout.c
Normal 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
385
daemon/rbtree.c
Normal 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
159
daemon/rbtree.h
Normal 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
627
daemon/readdir.c
Normal 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
307
daemon/readwrite.c
Normal 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
491
daemon/setattr.c
Normal 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
24
daemon/sources
Normal 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
222
daemon/upcall.c
Normal 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
201
daemon/upcall.h
Normal 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
450
daemon/util.c
Normal 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
166
daemon/util.h
Normal 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
86
daemon/volume.c
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue