381 lines
13 KiB
C
381 lines
13 KiB
C
|
|
/* Copyright (c) 2010
|
||
|
|
* The Regents of the University of Michigan
|
||
|
|
* All Rights Reserved
|
||
|
|
*
|
||
|
|
* Permission is granted to use, copy and redistribute this software
|
||
|
|
* for noncommercial education and research purposes, so long as no
|
||
|
|
* fee is charged, and so long as the name of the University of Michigan
|
||
|
|
* is not used in any advertising or publicity pertaining to the use
|
||
|
|
* or distribution of this software without specific, written prior
|
||
|
|
* authorization. Permission to modify or otherwise create derivative
|
||
|
|
* works of this software is not granted.
|
||
|
|
*
|
||
|
|
* This software is provided as is, without representation or warranty
|
||
|
|
* of any kind either express or implied, including without limitation
|
||
|
|
* the implied warranties of merchantability, fitness for a particular
|
||
|
|
* purpose, or noninfringement. The Regents of the University of
|
||
|
|
* Michigan shall not be liable for any damages, including special,
|
||
|
|
* indirect, incidental, or consequential damages, with respect to any
|
||
|
|
* claim arising out of or in connection with the use of the software,
|
||
|
|
* even if it has been or is hereafter advised of the possibility of
|
||
|
|
* such damages.
|
||
|
|
*/
|
||
|
|
#include <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;
|
||
|
|
}
|