diff --git a/daemon/callback_server.c b/daemon/callback_server.c index 3c80475..edd33dc 100644 --- a/daemon/callback_server.c +++ b/daemon/callback_server.c @@ -172,6 +172,18 @@ out: return status; } +/* OP_CB_GETATTR */ +static enum_t handle_cb_getattr( + IN nfs41_rpc_clnt *rpc_clnt, + IN struct cb_getattr_args *args, + OUT struct cb_getattr_res *res) +{ + /* look up cached attributes for the given filehandle */ + res->status = nfs41_delegation_getattr(rpc_clnt->client, + &args->fh, &args->attr_request, &res->info); + return res->status; +} + /* OP_CB_RECALL */ static enum_t handle_cb_recall( IN nfs41_rpc_clnt *rpc_clnt, @@ -437,7 +449,8 @@ static void handle_cb_compound(nfs41_rpc_clnt *rpc_clnt, cb_req *req, struct cb_ break; case OP_CB_GETATTR: dprintf(1, "OP_CB_GETATTR\n"); - res->status = NFS4ERR_NOTSUPP; + res->status = handle_cb_getattr(rpc_clnt, + &argop->args.getattr, &resop->res.getattr); break; case OP_CB_RECALL: dprintf(1, "OP_CB_RECALL\n"); diff --git a/daemon/callback_xdr.c b/daemon/callback_xdr.c index 0bf0332..8c1fda2 100644 --- a/daemon/callback_xdr.c +++ b/daemon/callback_xdr.c @@ -25,16 +25,18 @@ */ #include "nfs41_callback.h" - #include "nfs41_ops.h" +#include "util.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 */ bool_t xdr_bitmap4(XDR *xdr, bitmap4 *bitmap); +bool_t xdr_fattr4(XDR *xdr, fattr4 *fattr); static bool_t common_stateid(XDR *xdr, stateid4 *stateid) { @@ -245,12 +247,41 @@ out: } /* OP_CB_GETATTR */ -static bool_t op_cb_getattr_args(XDR *xdr, struct cb_getattr_args *res) +static bool_t op_cb_getattr_args(XDR *xdr, struct cb_getattr_args *args) { bool_t result; - result = xdr_u_int32_t(xdr, &res->target_highest_slotid); - if (!result) { CBX_ERR("getattr.target_highest_slotid"); goto out; } + result = common_fh(xdr, &args->fh); + if (!result) { CBX_ERR("getattr.fh"); goto out; } + + result = xdr_bitmap4(xdr, &args->attr_request); + if (!result) { CBX_ERR("getattr.attr_request"); goto out; } +out: + return result; +} + +static bool_t info_to_fattr4(nfs41_file_info *info, fattr4 *fattr) +{ + XDR fattr_xdr; + bool_t result = TRUE; + + /* encode nfs41_file_info into fattr4 */ + xdrmem_create(&fattr_xdr, (char*)fattr->attr_vals, + NFS4_OPAQUE_LIMIT, XDR_ENCODE); + + /* The only attributes that the server can reliably + * query via CB_GETATTR are size and change. */ + if (bitmap_isset(&info->attrmask, 0, FATTR4_WORD0_CHANGE)) { + result = xdr_u_hyper(&fattr_xdr, &info->change); + if (!result) { CBX_ERR("getattr.info.change"); goto out; } + bitmap_set(&fattr->attrmask, 0, FATTR4_WORD0_CHANGE); + } + if (bitmap_isset(&info->attrmask, 0, FATTR4_WORD0_SIZE)) { + result = xdr_u_hyper(&fattr_xdr, &info->size); + if (!result) { CBX_ERR("getattr.info.size"); goto out; } + bitmap_set(&fattr->attrmask, 0, FATTR4_WORD0_SIZE); + } + fattr->attr_vals_len = xdr_getpos(&fattr_xdr); out: return result; } @@ -261,6 +292,16 @@ static bool_t op_cb_getattr_res(XDR *xdr, struct cb_getattr_res *res) result = xdr_enum(xdr, &res->status); if (!result) { CBX_ERR("getattr.status"); goto out; } + + if (res->status == NFS4_OK) { + fattr4 fattr = { 0 }; + + result = info_to_fattr4(&res->info, &fattr); + if (!result) { goto out; } + + result = xdr_fattr4(xdr, &fattr); + if (!result) { CBX_ERR("getattr.obj_attributes"); goto out; } + } out: return result; } diff --git a/daemon/delegation.c b/daemon/delegation.c index d48e8ef..d84cf12 100644 --- a/daemon/delegation.c +++ b/daemon/delegation.c @@ -26,6 +26,7 @@ #include "delegation.h" #include "nfs41_ops.h" +#include "name_cache.h" #include "util.h" #include "daemon_debug.h" @@ -725,6 +726,49 @@ out_deleg: } +int nfs41_delegation_getattr( + IN nfs41_client *client, + IN const nfs41_fh *fh, + IN const bitmap4 *attr_request, + OUT nfs41_file_info *info) +{ + nfs41_delegation_state *deleg; + uint64_t fileid; + int status; + + dprintf(2, "--> nfs41_delegation_getattr()\n"); + + /* search for a delegation on this file handle */ + status = delegation_find(client, fh, deleg_fh_cmp, &deleg); + if (status) + goto out; + + AcquireSRWLockShared(&deleg->lock); + fileid = deleg->file.fh.fileid; + if (deleg->status != DELEGATION_GRANTED || + deleg->state.type != OPEN_DELEGATE_WRITE) { + status = NFS4ERR_BADHANDLE; + } + ReleaseSRWLockShared(&deleg->lock); + if (status) + goto out; + + ZeroMemory(&info, sizeof(nfs41_file_info)); + + /* find attributes for the given fileid */ + status = nfs41_attr_cache_lookup( + client_name_cache(client), fileid, info); + if (status) { + status = NFS4ERR_BADHANDLE; + goto out; + } +out: + dprintf(DGLVL, "<-- nfs41_delegation_getattr() returning %s\n", + nfs_error_string(status)); + return status; +} + + void nfs41_client_delegation_free( IN nfs41_client *client) { diff --git a/daemon/delegation.h b/daemon/delegation.h index d280add..ce20f27 100644 --- a/daemon/delegation.h +++ b/daemon/delegation.h @@ -91,6 +91,12 @@ int nfs41_delegation_recall( IN const stateid4 *stateid, IN bool_t truncate); +int nfs41_delegation_getattr( + IN nfs41_client *client, + IN const nfs41_fh *fh, + IN const bitmap4 *attr_request, + OUT nfs41_file_info *info); + /* after client state recovery, return any 'recalled' delegations; * must be called under the client's state lock */ diff --git a/daemon/nfs41_callback.h b/daemon/nfs41_callback.h index 5edf09f..c654a73 100644 --- a/daemon/nfs41_callback.h +++ b/daemon/nfs41_callback.h @@ -124,11 +124,13 @@ struct cb_sequence_res { /* OP_CB_GETATTR */ struct cb_getattr_args { - uint32_t target_highest_slotid; + nfs41_fh fh; + bitmap4 attr_request; }; struct cb_getattr_res { enum_t status; + nfs41_file_info info; }; /* OP_CB_RECALL */ @@ -231,7 +233,8 @@ 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_getattr_args getattr; + struct cb_recall_args recall; struct cb_notify_deviceid_args notify_deviceid; }; struct cb_argop { @@ -255,6 +258,7 @@ union cb_op_res { struct cb_layoutrecall_res layoutrecall; struct cb_recall_slot_res recall_slot; struct cb_sequence_res sequence; + struct cb_getattr_res getattr; struct cb_recall_res recall; struct cb_notify_deviceid_res notify_deviceid; }; diff --git a/daemon/nfs41_xdr.c b/daemon/nfs41_xdr.c index 89e1166..2f6fff8 100644 --- a/daemon/nfs41_xdr.c +++ b/daemon/nfs41_xdr.c @@ -146,7 +146,7 @@ static bool_t xdr_stateid4( } /* fattr4 */ -static bool_t xdr_fattr4( +bool_t xdr_fattr4( XDR *xdr, fattr4 *fattr) {