+/* NFSv4.1 client for Windows
+ * Copyright © 2012 The Regents of the University of Michigan
+ *
+ * Olga Kornievskaia <aglo@umich.edu>
+ * Casey Bodley <cbodley@umich.edu>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library 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 Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ */
+
+#include <windows.h>
+#include <strsafe.h>
+
+#include "nfs41_compound.h"
+#include "nfs41_ops.h"
+#include "nfs41_xdr.h"
+#include "util.h"
+#include "daemon_debug.h"
+#include "rpc/rpc.h"
+
+static bool_t encode_file_attrs(
+ fattr4 *attrs,
+ nfs41_file_info *info);
+
+static __inline int unexpected_op(uint32_t op, uint32_t expected)
+{
+ if (op == expected)
+ return 0;
+
+ eprintf("Op table mismatch. Got %s (%d), expected %s (%d).\n",
+ nfs_opnum_to_string(op), op,
+ nfs_opnum_to_string(expected), expected);
+ return 1;
+}
+
+/* typedef uint32_t bitmap4<> */
+bool_t xdr_bitmap4(
+ XDR *xdr,
+ bitmap4 *bitmap)
+{
+ uint32_t i;
+
+ if (xdr->x_op == XDR_ENCODE) {
+ if (bitmap->count > 3) {
+ eprintf("encode_bitmap4: count (%d) must be <= 3\n",
+ bitmap->count);
+ return FALSE;
+ }
+ if (!xdr_u_int32_t(xdr, &bitmap->count))
+ return FALSE;
+
+ for (i = 0; i < bitmap->count; i++)
+ if (!xdr_u_int32_t(xdr, &bitmap->arr[i]))
+ return FALSE;
+
+ } else if (xdr->x_op == XDR_DECODE) {
+ if (!xdr_u_int32_t(xdr, &bitmap->count))
+ return FALSE;
+ if (bitmap->count > 3) {
+ eprintf("decode_bitmap4: count (%d) must be <= 3\n",
+ bitmap->count);
+ return FALSE;
+ }
+
+ for (i = 0; i < bitmap->count; i++)
+ if (!xdr_u_int32_t(xdr, &bitmap->arr[i]))
+ return FALSE;
+ } else
+ return FALSE;
+
+ return TRUE;
+}
+
+/* nfstime4 */
+static bool_t xdr_nfstime4(
+ XDR *xdr,
+ nfstime4 *nt)
+{
+ if (!xdr_hyper(xdr, &nt->seconds))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &nt->nseconds);
+}
+
+
+/* settime4 */
+static uint32_t settime_how(
+ nfstime4 *newtime,
+ const nfstime4 *time_delta)
+{
+ nfstime4 current;
+ get_nfs_time(¤t);
+ /* get the absolute difference between current and newtime */
+ nfstime_diff(¤t, newtime, ¤t);
+ nfstime_abs(¤t, ¤t);
+ /* compare the difference with time_delta */
+ nfstime_diff(time_delta, ¤t, ¤t);
+ /* use client time if diff > delta (i.e. time_delta - current < 0) */
+ return current.seconds < 0 ? SET_TO_CLIENT_TIME4 : SET_TO_SERVER_TIME4;
+}
+
+static bool_t xdr_settime4(
+ XDR *xdr,
+ nfstime4 *nt,
+ const nfstime4 *time_delta)
+{
+ uint32_t how = settime_how(nt, time_delta);
+
+ if (xdr->x_op != XDR_ENCODE) /* not used for decode */
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &how))
+ return FALSE;
+
+ if (how == SET_TO_CLIENT_TIME4)
+ return xdr_nfstime4(xdr, nt);
+
+ return TRUE;
+}
+
+/* stateid4 */
+static bool_t xdr_stateid4(
+ XDR *xdr,
+ stateid4 *si)
+{
+ if (!xdr_u_int32_t(xdr, &si->seqid))
+ return FALSE;
+
+ return xdr_opaque(xdr, (char *)si->other, NFS4_STATEID_OTHER);
+}
+
+/* fattr4 */
+bool_t xdr_fattr4(
+ XDR *xdr,
+ fattr4 *fattr)
+{
+ unsigned char *attr_vals = fattr->attr_vals;
+
+ if (!xdr_bitmap4(xdr, &fattr->attrmask))
+ return FALSE;
+
+ return xdr_bytes(xdr, (char **)&attr_vals, &fattr->attr_vals_len, NFS4_OPAQUE_LIMIT);
+}
+
+/* nfs41_fh */
+static bool_t xdr_fh(
+ XDR *xdr,
+ nfs41_fh *fh)
+{
+ unsigned char *pfh = fh->fh;
+ return xdr_bytes(xdr, (char **)&pfh, &fh->len, NFS4_FHSIZE);
+}
+
+/* nfs41_fsid */
+static bool_t xdr_fsid(
+ XDR *xdr,
+ nfs41_fsid *fsid)
+{
+ if (!xdr_u_hyper(xdr, &fsid->major))
+ return FALSE;
+
+ return xdr_u_hyper(xdr, &fsid->minor);
+}
+
+
+/* nfs41_component */
+static bool_t encode_component(
+ XDR *xdr,
+ const nfs41_component *component)
+{
+ uint32_t len = component->len;
+ return xdr_bytes(xdr, (char **)&component->name, &len, NFS4_OPAQUE_LIMIT);
+}
+
+static bool_t decode_component(
+ XDR *xdr,
+ nfs41_component *component)
+{
+ bool_t result;
+ uint32_t len;
+
+ result = xdr_bytes(xdr, (char **)&component->name, &len, NFS4_OPAQUE_LIMIT);
+ component->len = (result == FALSE) ? 0 : (unsigned short)len;
+ return result;
+}
+
+
+/* state_owner4 */
+static bool_t xdr_state_owner4(
+ XDR *xdr,
+ state_owner4 *so)
+{
+ u_quad_t clientid = 0;
+ unsigned char *owner = so->owner;
+
+ /* 18.16.3. "The client can set the clientid field to any value and
+ * the server MUST ignore it. Instead the server MUST derive the
+ * client ID from the session ID of the SEQUENCE operation of the
+ * COMPOUND request. */
+ if (xdr->x_op == XDR_ENCODE) {
+ if (!xdr_u_hyper(xdr, &clientid)) /* clientid = 0 */
+ return FALSE;
+ } else if (xdr->x_op == XDR_DECODE) {
+ if (!xdr_u_hyper(xdr, &clientid))
+ return FALSE;
+ } else return FALSE;
+
+ return xdr_bytes(xdr, (char **)&owner, &so->owner_len, NFS4_OPAQUE_LIMIT);
+}
+
+static bool_t xdr_layout_types(
+ XDR *xdr,
+ uint32_t *layout_type)
+{
+ u_int32_t i, count, type;
+
+ if (xdr->x_op != XDR_DECODE) {
+ eprintf("xdr_layout_types: xdr->x_op is not XDR_DECODE! "
+ "x_op %d not supported.\n", xdr->x_op);
+ return FALSE;
+ }
+
+ *layout_type = 0;
+
+ if (!xdr_u_int32_t(xdr, &count))
+ return FALSE;
+
+ for (i = 0; i < count; i++) {
+ if (!xdr_u_int32_t(xdr, &type))
+ return FALSE;
+
+ *layout_type |= 1 << (type - 1);
+ }
+ return TRUE;
+}
+
+static bool_t xdr_threshold_item(
+ XDR *xdr,
+ threshold_item4 *item)
+{
+ bitmap4 bitmap;
+
+ if (!xdr_u_int32_t(xdr, &item->type))
+ return FALSE;
+
+ if (!xdr_bitmap4(xdr, &bitmap))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &bitmap.count))
+ return FALSE;
+
+ if (bitmap.count) {
+ if (bitmap.arr[0] & 0x1 && !xdr_u_hyper(xdr, &item->hints[0]))
+ return FALSE;
+ if (bitmap.arr[0] & 0x2 && !xdr_u_hyper(xdr, &item->hints[1]))
+ return FALSE;
+ if (bitmap.arr[0] & 0x4 && !xdr_u_hyper(xdr, &item->hints[2]))
+ return FALSE;
+ if (bitmap.arr[0] & 0x8 && !xdr_u_hyper(xdr, &item->hints[3]))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static bool_t xdr_mdsthreshold(
+ XDR *xdr,
+ mdsthreshold4 *mdsthreshold)
+{
+ uint32_t i;
+
+ if (!xdr_u_int32_t(xdr, &mdsthreshold->count))
+ return FALSE;
+
+ if (mdsthreshold->count > MAX_MDSTHRESHOLD_ITEMS)
+ return FALSE;
+
+ for (i = 0; i < mdsthreshold->count; i++)
+ if (!xdr_threshold_item(xdr, &mdsthreshold->items[i]))
+ return FALSE;
+ return TRUE;
+}
+
+static bool_t xdr_nfsace4(
+ XDR *xdr,
+ nfsace4 *ace)
+{
+ char *who = ace->who;
+
+ if (!xdr_u_int32_t(xdr, &ace->acetype))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &ace->aceflag))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &ace->acemask))
+ return FALSE;
+
+ /* 'who' is a static array, so don't try to free it */
+ if (xdr->x_op == XDR_FREE)
+ return TRUE;
+
+ return xdr_string(xdr, &who, NFS4_OPAQUE_LIMIT);
+}
+
+static bool_t xdr_nfsdacl41(
+ XDR *xdr,
+ nfsacl41 *acl)
+{
+ if (!xdr_u_int32_t(xdr, &acl->flag))
+ return FALSE;
+
+ return xdr_array(xdr, (char**)&acl->aces, &acl->count,
+ 32, sizeof(nfsace4), (xdrproc_t)xdr_nfsace4);
+}
+
+static bool_t xdr_nfsacl41(
+ XDR *xdr,
+ nfsacl41 *acl)
+{
+ return xdr_array(xdr, (char**)&acl->aces, &acl->count,
+ 32, sizeof(nfsace4), (xdrproc_t)xdr_nfsace4);
+}
+
+void nfsacl41_free(nfsacl41 *acl)
+{
+ XDR xdr = { XDR_FREE };
+ xdr_nfsacl41(&xdr, acl);
+}
+
+/* pathname4
+ * decode a variable array of components into a nfs41_abs_path */
+static bool_t decode_pathname4(
+ XDR *xdr,
+ nfs41_abs_path *path)
+{
+ char *pos;
+ u_int32_t i, count, len, remaining;
+
+ /* decode the number of components */
+ if (!xdr_u_int32_t(xdr, &count))
+ return FALSE;
+
+ pos = (char *)path->path;
+ remaining = NFS41_MAX_PATH_LEN;
+
+ /* decode each component */
+ for (i = 0; i < count; i++) {
+ len = remaining;
+ if (!xdr_bytes(xdr, (char **)&pos, &len, NFS41_MAX_PATH_LEN))
+ return FALSE;
+ remaining -= len;
+ pos += len;
+
+ if (i < count-1) { /* add a \ between components */
+ if (remaining < 1)
+ return FALSE;
+ *pos++ = '\\';
+ remaining--;
+ }
+ }
+ path->len = (unsigned short)(NFS41_MAX_PATH_LEN - remaining);
+ return TRUE;
+}
+
+/* fs_location4 */
+static bool_t decode_fs_location4(
+ XDR *xdr,
+ fs_location4 *location)
+{
+ fs_location_server *arr;
+ char *address;
+ u_int32_t i, count, len;
+
+ /* decode the number of servers */
+ if (!xdr_u_int32_t(xdr, &count))
+ return FALSE;
+
+ /* allocate the fs_location_server array */
+ if (count == 0) {
+ free(location->servers);
+ arr = NULL;
+ } else if (count != location->server_count) {
+ arr = realloc(location->servers, count * sizeof(fs_location_server));
+ if (arr == NULL)
+ return FALSE;
+ ZeroMemory(arr, count * sizeof(fs_location_server));
+ } else {
+ arr = location->servers;
+ }
+
+ location->servers = arr;
+ location->server_count = count;
+
+ for (i = 0; i < count; i++) {
+ len = NFS41_HOSTNAME_LEN;
+ address = arr[i].address;
+ if (!xdr_bytes(xdr, &address, &len, NFS41_HOSTNAME_LEN)) {
+ free(arr);
+ return FALSE;
+ }
+ arr[i].address[len] = '\0';
+ }
+
+ return decode_pathname4(xdr, &location->path);
+}
+
+/* fs_locations4 */
+static bool_t decode_fs_locations4(
+ XDR *xdr,
+ fs_locations4 *locations)
+{
+ u_int32_t i, count;
+ fs_location4 *arr;
+
+ if (!decode_pathname4(xdr, &locations->path))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &count))
+ return FALSE;
+
+ /* allocate the fs_location array */
+ if (count == 0) {
+ free(locations->locations);
+ arr = NULL;
+ } else if (count != locations->location_count) {
+ arr = realloc(locations->locations, count * sizeof(fs_location4));
+ if (arr == NULL)
+ return FALSE;
+ ZeroMemory(arr, count * sizeof(fs_location4));
+ } else {
+ arr = locations->locations;
+ }
+
+ locations->locations = arr;
+ locations->location_count = count;
+
+ for (i = 0; i < count; i++) {
+ if (!decode_fs_location4(xdr, &arr[i])) {
+ free(arr);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * OP_EXCHANGE_ID
+ */
+static bool_t xdr_client_owner4(
+ XDR *xdr,
+ client_owner4 *co)
+{
+ unsigned char *co_ownerid = co->co_ownerid;
+ if (!xdr_opaque(xdr, (char *)&co->co_verifier[0], NFS4_VERIFIER_SIZE))
+ return FALSE;
+
+ return xdr_bytes(xdr, (char **)&co_ownerid, &co->co_ownerid_len, NFS4_OPAQUE_LIMIT);
+}
+
+#if 0
+static bool_t encode_state_protect_ops4(
+ XDR *xdr,
+ state_protect_ops4 *spo)
+{
+ if (!xdr_bitmap4(xdr, &spo->spo_must_enforce))
+ return FALSE;
+
+ return xdr_bitmap4(xdr, &spo->spo_must_allow);
+}
+
+static bool_t encode_ssv_sp_parms4(
+ XDR *xdr,
+ ssv_sp_parms4 *spp)
+{
+ if (!encode_state_protect_ops4(xdr, &spp->ssp_ops))
+ return FALSE;
+
+ if (!xdr_bytes(xdr, &spp->ssp_hash_algs,
+ &spp->ssp_hash_algs_len, NFS4_OPAQUE_LIMIT))
+ return FALSE;
+
+ if (!xdr_bytes(xdr, &spp->ssp_encr_algs,
+ &spp->ssp_encr_algs_len, NFS4_OPAQUE_LIMIT))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &spp->ssp_window))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &spp->ssp_num_gss_handles);
+}
+#endif
+
+static bool_t xdr_state_protect4_a(
+ XDR *xdr,
+ state_protect4_a *spa)
+{
+ bool_t result = TRUE;
+
+ if (!xdr_u_int32_t(xdr, (u_int32_t *)&spa->spa_how))
+ return FALSE;
+
+ switch (spa->spa_how)
+ {
+ case SP4_NONE:
+ break;
+#if 0
+ case SP4_MACH_CRED:
+ result = xdr_state_protect_ops4(xdr, &spa->u.spa_mach_ops);
+ break;
+ case SP4_SSV:
+ result = xdr_ssv_sp_parms4(xdr, &spa->u.spa_ssv_parms);
+ break;
+#endif
+ default:
+ eprintf("encode_state_protect4_a: state protect "
+ "type %d not supported.\n", spa->spa_how);
+ result = FALSE;
+ break;
+ }
+ return result;
+}
+
+static bool_t xdr_nfs_impl_id4(
+ XDR *xdr,
+ nfs_impl_id4 *nii)
+{
+ unsigned char *nii_domain = nii->nii_domain;
+ unsigned char *nii_name = nii->nii_name;
+
+ if (!xdr_bytes(xdr, (char **)&nii_domain, &nii->nii_domain_len, NFS4_OPAQUE_LIMIT))
+ return FALSE;
+
+ if (!xdr_bytes(xdr, (char **)&nii_name, &nii->nii_name_len, NFS4_OPAQUE_LIMIT))
+ return FALSE;
+
+ return xdr_nfstime4(xdr, &nii->nii_date);
+}
+
+
+static bool_t encode_op_exchange_id(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ uint32_t zero = 0;
+ uint32_t one = 1;
+
+ nfs41_exchange_id_args *args = (nfs41_exchange_id_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_EXCHANGE_ID))
+ return FALSE;
+
+ if (!xdr_client_owner4(xdr, args->eia_clientowner))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->eia_flags))
+ return FALSE;
+
+ if (!xdr_state_protect4_a(xdr, &args->eia_state_protect))
+ return FALSE;
+
+ if (args->eia_client_impl_id)
+ {
+ if (!xdr_u_int32_t(xdr, &one))
+ return FALSE;
+ return xdr_nfs_impl_id4(xdr, args->eia_client_impl_id);
+ }
+ else
+ return xdr_u_int32_t(xdr, &zero);
+}
+
+#if 0
+
+static bool_t decode_state_protect_ops4(
+ XDR *xdr,
+ state_protect_ops4 *spo)
+{
+ if (!xdr_bitmap4(xdr, &spo->spo_must_enforce))
+ return FALSE;
+
+ return xdr_bitmap4(xdr, &spo->spo_must_allow);
+}
+
+static bool_t decode_ssv_prot_info4(
+ XDR *xdr,
+ ssv_prot_info4 *spi)
+{
+/* uint32_t i; */
+
+ if (!decode_state_protect_ops4(xdr, &spi->spi_ops))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &spi->spi_hash_alg))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &spi->spi_encr_alg))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &spi->spi_ssv_len))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &spi->spi_window))
+ return FALSE;
+
+/* TODO: spi->spi_handles */
+ return xdr_u_int32_t(xdr, 0);
+ /*
+ if (!xdr_u_int32_t(xdr, &spi->spi_handles.count))
+ return FALSE;
+
+ for (i = 0; i < spi->spi_handles.count; i++)
+ if (!xdr_opaque(xdr, &spi->spi_handles.arr[i])
+ return FALSE;
+*/
+ return TRUE;
+}
+#endif
+
+static bool_t xdr_state_protect4_r(
+ XDR *xdr,
+ state_protect4_r *spr)
+{
+ bool_t result = TRUE;
+
+ if (!xdr_u_int32_t(xdr, (uint32_t *)&spr->spr_how))
+ return FALSE;
+
+ switch (spr->spr_how)
+ {
+ case SP4_NONE:
+ break;
+#if 0
+ case SP4_MACH_CRED:
+ result = decode_state_protect_ops4(xdr, &spr->u.spr_mach_ops);
+ break;
+ case SP4_SSV:
+ result = decode_ssv_prot_info4(xdr, &spr->u.spr_ssv_info);
+ break;
+#endif
+ default:
+ eprintf("decode_state_protect4_r: state protect "
+ "type %d not supported.\n", spr->spr_how);
+ result = FALSE;
+ break;
+ }
+ return result;
+}
+
+static bool_t xdr_server_owner4(
+ XDR *xdr,
+ server_owner4 *so)
+{
+ char *so_major_id = so->so_major_id;
+
+ if (!xdr_u_hyper(xdr, &so->so_minor_id))
+ return FALSE;
+
+ return xdr_bytes(xdr, (char **)&so_major_id,
+ &so->so_major_id_len, NFS4_OPAQUE_LIMIT);
+}
+
+static bool_t decode_op_exchange_id(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_exchange_id_res *res = (nfs41_exchange_id_res*)resop->res;
+ char *server_scope = (char *)res->server_scope;
+
+ if (unexpected_op(resop->op, OP_EXCHANGE_ID))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status != NFS4_OK)
+ return TRUE;
+
+ if (!xdr_u_hyper(xdr, &res->clientid))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->sequenceid))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->flags))
+ return FALSE;
+
+ if (!xdr_state_protect4_r(xdr, &res->state_protect))
+ return FALSE;
+
+ if (!xdr_server_owner4(xdr, &res->server_owner))
+ return FALSE;
+
+ return xdr_bytes(xdr, &server_scope,
+ &res->server_scope_len, NFS4_OPAQUE_LIMIT);
+}
+
+/*
+ * OP_CREATE_SESSION
+ */
+static bool_t xdr_channel_attrs4(
+ XDR *xdr,
+ nfs41_channel_attrs *attrs)
+{
+ uint32_t zero = 0;
+ uint32_t one = 1;
+
+ /* count4 ca_headerpadsize */
+ if (!xdr_u_int32_t(xdr, &attrs->ca_headerpadsize))
+ return FALSE;
+
+ /* count4 ca_maxrequestsize */
+ if (!xdr_u_int32_t(xdr, &attrs->ca_maxrequestsize))
+ return FALSE;
+
+ /* count4 ca_maxresponsesize */
+ if (!xdr_u_int32_t(xdr, &attrs->ca_maxresponsesize))
+ return FALSE;
+
+ /* count4 ca_maxresponsesize_cached */
+ if (!xdr_u_int32_t(xdr, &attrs->ca_maxresponsesize_cached))
+ return FALSE;
+
+ /* count4 ca_maxoperations */
+ if (!xdr_u_int32_t(xdr, &attrs->ca_maxoperations))
+ return FALSE;
+
+ /* count4 ca_maxrequests */
+ if (!xdr_u_int32_t(xdr, &attrs->ca_maxrequests))
+ return FALSE;
+
+ if (xdr->x_op == XDR_ENCODE) {
+ /* uint32_t ca_rdma_ird<1> */
+ if (attrs->ca_rdma_ird)
+ {
+ if (!xdr_u_int32_t(xdr, &one))
+ return FALSE;
+ return xdr_u_int32_t(xdr, attrs->ca_rdma_ird);
+ }
+ else {
+ return xdr_u_int32_t(xdr, &zero);
+ }
+ }
+ else if (xdr->x_op == XDR_DECODE) {
+#if 0
+ u_int32_t count;
+ /* uint32_t ca_rdma_ird<1> */
+ if (!xdr_u_int32_t(xdr, &count))
+ return FALSE;
+ if (count > 1)
+ return FALSE;
+ if (count)
+ return xdr_u_int32_t(xdr, attrs->ca_rdma_ird);
+ else
+#endif
+ return TRUE;
+ }
+ else {
+ eprintf("%s: xdr->x_op %d not supported.\n",
+ "xdr_channel_attrs4", xdr->x_op);
+ return FALSE;
+ }
+}
+
+static bool_t encode_backchannel_sec_parms(
+ XDR *xdr,
+ nfs41_callback_secparms *args)
+{
+ uint32_t zero = 0;
+
+ if (!xdr_u_int32_t(xdr, &args->type))
+ return FALSE;
+
+ switch (args->type) {
+ case AUTH_NONE: return TRUE;
+ case AUTH_SYS:
+ if (!xdr_u_int32_t(xdr, &args->u.auth_sys.stamp))
+ return FALSE;
+ if (!xdr_string(xdr, &args->u.auth_sys.machinename, NI_MAXHOST))
+ return FALSE;
+ return xdr_u_int32_t(xdr, &zero) && xdr_u_int32_t(xdr, &zero) &&
+ xdr_u_int32_t(xdr, &zero);
+ case RPCSEC_GSS:
+ default:
+ return FALSE;
+ }
+}
+
+static bool_t encode_op_create_session(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_create_session_args *args = (nfs41_create_session_args*)argop->arg;
+ nfs41_callback_secparms *cb_secparams = args->csa_cb_secparams;
+ uint32_t cb_count = 2;
+
+ if (unexpected_op(argop->op, OP_CREATE_SESSION))
+ return FALSE;
+
+ /* clientid4 csa_clientid */
+ if (!xdr_u_hyper(xdr, &args->csa_clientid))
+ return FALSE;
+
+ /* sequenceid4 csa_sequence */
+ if (!xdr_u_int32_t(xdr, &args->csa_sequence))
+ return FALSE;
+
+ /* TODO: uint32_t csa_flags = 0 */
+ if (!xdr_u_int32_t(xdr, &args->csa_flags))
+ return FALSE;
+
+ /* channel_attrs4 csa_fore_chan_attrs */
+ if (!xdr_channel_attrs4(xdr, &args->csa_fore_chan_attrs))
+ return FALSE;
+
+ /* channel_attrs4 csa_back_chan_attrs */
+ if (!xdr_channel_attrs4(xdr, &args->csa_back_chan_attrs))
+ return FALSE;
+
+ /* TODO: uint32_t csa_cb_program = 1234 */
+ if (!xdr_u_int32_t(xdr, &args->csa_cb_program))
+ return FALSE;
+
+ return xdr_array(xdr, (char **)&cb_secparams, &cb_count,
+ 3, sizeof(nfs41_callback_secparms), (xdrproc_t) encode_backchannel_sec_parms);
+}
+
+static bool_t decode_op_create_session(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ uint32_t opstatus;
+ nfs41_create_session_res *res = (nfs41_create_session_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_CREATE_SESSION))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &opstatus))
+ return FALSE;
+
+ if (opstatus != NFS4_OK)
+ return TRUE;
+
+ if (!xdr_opaque(xdr, (char *)res->csr_sessionid, NFS4_SESSIONID_SIZE))
+ return FALSE;
+
+ /* sequenceid4 csr_sequence */
+ if (!xdr_u_int32_t(xdr, &res->csr_sequence))
+ return FALSE;
+
+ /* uint32_t csr_flags */
+ if (!xdr_u_int32_t(xdr, &res->csr_flags))
+ return FALSE;
+
+ /* channel_attrs4 csr_fore_chan_attrs */
+ if (!xdr_channel_attrs4(xdr, res->csr_fore_chan_attrs))
+ return FALSE;
+
+ /* channel_attrs4 csr_back_chan_attrs */
+ return xdr_channel_attrs4(xdr, res->csr_back_chan_attrs);
+}
+
+
+/*
+ * OP_BIND_CONN_TO_SESSION
+ */
+static bool_t encode_op_bind_conn_to_session(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ uint32_t zero = 0;
+
+ nfs41_bind_conn_to_session_args *args =
+ (nfs41_bind_conn_to_session_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_BIND_CONN_TO_SESSION))
+ return FALSE;
+
+ if (!xdr_opaque(xdr, (char *)args->sessionid, NFS4_SESSIONID_SIZE))
+ return FALSE;
+
+ if (!xdr_enum(xdr, (enum_t *)&args->dir))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &zero); /* bctsa_use_conn_in_rdma_mode = false */
+}
+
+static bool_t decode_op_bind_conn_to_session(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ unsigned char sessionid_ignored[NFS4_SESSIONID_SIZE];
+ nfs41_bind_conn_to_session_res *res =
+ (nfs41_bind_conn_to_session_res*)resop->res;
+ bool_t use_rdma_ignored;
+
+ if (unexpected_op(resop->op, OP_BIND_CONN_TO_SESSION))
+ return FALSE;
+
+ if (!xdr_enum(xdr, (enum_t *)&res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK) {
+ if (!xdr_opaque(xdr, (char *)&sessionid_ignored, NFS4_SESSIONID_SIZE))
+ return FALSE;
+
+ if (!xdr_enum(xdr, (enum_t *)&res->dir))
+ return FALSE;
+
+ return xdr_bool(xdr, &use_rdma_ignored);
+ }
+ return TRUE;
+}
+
+
+/*
+ * OP_DESTROY_SESSION
+ */
+static bool_t encode_op_destroy_session(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_destroy_session_args *args = (nfs41_destroy_session_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_DESTROY_SESSION))
+ return FALSE;
+
+ return xdr_opaque(xdr, (char *)args->dsa_sessionid, NFS4_SESSIONID_SIZE);
+}
+
+static bool_t decode_op_destroy_session(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_destroy_session_res *res = (nfs41_destroy_session_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_DESTROY_SESSION))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->dsr_status);
+}
+
+/*
+ * OP_DESTROY_CLIENTID
+ */
+static bool_t encode_op_destroy_clientid(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_destroy_clientid_args *args = (nfs41_destroy_clientid_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_DESTROY_CLIENTID))
+ return FALSE;
+
+ return xdr_u_hyper(xdr, &args->dca_clientid);
+}
+
+static bool_t decode_op_destroy_clientid(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_destroy_clientid_res *res = (nfs41_destroy_clientid_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_DESTROY_CLIENTID))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->dcr_status);
+}
+
+
+/*
+ * OP_SEQUENCE
+ */
+static bool_t encode_op_sequence(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_sequence_args *args = (nfs41_sequence_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_SEQUENCE))
+ return FALSE;
+
+ if (!xdr_opaque(xdr, (char *)args->sa_sessionid, NFS4_SESSIONID_SIZE))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->sa_sequenceid))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->sa_slotid))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->sa_highest_slotid))
+ return FALSE;
+
+ return xdr_bool(xdr, &args->sa_cachethis);
+}
+
+static bool_t xdr_sequence_res_ok(
+ XDR *xdr,
+ nfs41_sequence_res_ok *res)
+{
+ if (!xdr_opaque(xdr, (char *)res->sr_sessionid, NFS4_SESSIONID_SIZE))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->sr_sequenceid))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->sr_slotid))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->sr_highest_slotid))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->sr_target_highest_slotid))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->sr_status_flags);
+}
+
+static bool_t decode_op_sequence(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_sequence_res *res = (nfs41_sequence_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_SEQUENCE))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->sr_status))
+ return FALSE;
+
+ if (res->sr_status == NFS4_OK)
+ return xdr_sequence_res_ok(xdr, &res->sr_resok4);
+
+ return TRUE;
+}
+
+
+/*
+ * OP_RECLAIM_COMPLETE
+ */
+static bool_t encode_op_reclaim_complete(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ bool_t zero = FALSE;
+
+ if (unexpected_op(argop->op, OP_RECLAIM_COMPLETE))
+ return FALSE;
+
+ /* rca_one_fs = 0 indicates that the reclaim applies to all filesystems */
+ return xdr_bool(xdr, &zero);
+}
+
+static bool_t decode_op_reclaim_complete(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_reclaim_complete_res *res = (nfs41_reclaim_complete_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_RECLAIM_COMPLETE))
+ return FALSE;
+
+ return xdr_enum(xdr, (enum_t *)&res->status);
+}
+
+
+/*
+ * OP_PUTFH
+ */
+static bool_t encode_op_putfh(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_putfh_args *args = (nfs41_putfh_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_PUTFH))
+ return FALSE;
+
+ return xdr_fh(xdr, &args->file->fh);
+}
+
+static bool_t decode_op_putfh(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_putfh_res *res = (nfs41_putfh_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_PUTFH))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->status);
+}
+
+
+/*
+ * OP_PUTROOTFH
+ */
+static bool_t encode_op_putrootfh(
+ XDR *xdr,
+ nfs_argop4* argop)
+{
+ if (unexpected_op(argop->op, OP_PUTROOTFH))
+ return FALSE;
+ /* void */
+ return TRUE;
+}
+
+static bool_t decode_op_putrootfh(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_putrootfh_res *res = (nfs41_putrootfh_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_PUTROOTFH))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->status);
+}
+
+
+/*
+ * OP_GETFH
+ */
+static bool_t encode_op_getfh(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ if (unexpected_op(argop->op, OP_GETFH))
+ return FALSE;
+
+ /* void */
+ return TRUE;
+}
+
+static bool_t decode_op_getfh(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_getfh_res *res = (nfs41_getfh_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_GETFH))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ return xdr_fh(xdr, res->fh);
+
+ return TRUE;
+}
+
+
+/*
+ * OP_LOOKUP
+ */
+static bool_t encode_op_lookup(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_lookup_args *args = (nfs41_lookup_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_LOOKUP))
+ return FALSE;
+
+ return encode_component(xdr, args->name);
+}
+
+static bool_t decode_op_lookup(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_lookup_res *res = (nfs41_lookup_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_LOOKUP))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->status);
+}
+
+
+/*
+ * OP_ACCESS
+ */
+static bool_t encode_op_access(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_access_args *args = (nfs41_access_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_ACCESS))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &args->access);
+}
+
+static bool_t decode_op_access(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_access_res *res = (nfs41_access_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_ACCESS))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ {
+ if (!xdr_u_int32_t(xdr, &res->supported))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->access);
+ }
+ return TRUE;
+}
+
+
+/*
+ * OP_CLOSE
+ */
+static bool_t encode_op_close(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_op_close_args *args = (nfs41_op_close_args*)argop->arg;
+ uint32_t zero = 0;
+
+ if (unexpected_op(argop->op, OP_CLOSE))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &zero)) // This should be ignored by server
+ return FALSE;
+
+ return xdr_stateid4(xdr, &args->stateid->stateid);
+}
+
+static bool_t decode_op_close(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ stateid4 ignored;
+ nfs41_op_close_res *res = (nfs41_op_close_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_CLOSE))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ return xdr_stateid4(xdr, &ignored);
+
+ return TRUE;
+}
+
+
+/*
+ * OP_COMMIT
+ */
+static bool_t encode_op_commit(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_commit_args *args = (nfs41_commit_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_COMMIT))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->offset))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &args->count);
+}
+
+static bool_t decode_op_commit(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_commit_res *res = (nfs41_commit_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_COMMIT))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ return xdr_opaque(xdr, (char *)res->verf->verf, NFS4_VERIFIER_SIZE);
+
+ return TRUE;
+}
+
+
+/*
+ * OP_CREATE
+ */
+static bool_t encode_createtype4(
+ XDR *xdr,
+ createtype4 *ct)
+{
+ bool_t result = TRUE;
+ const char *linkdata;
+
+ if (!xdr_u_int32_t(xdr, &ct->type))
+ return FALSE;
+
+ switch (ct->type)
+ {
+ case NF4LNK:
+ linkdata = ct->u.lnk.linkdata;
+ result = xdr_bytes(xdr, (char**)&linkdata, &ct->u.lnk.linkdata_len,
+ NFS4_OPAQUE_LIMIT);
+ break;
+ case NF4BLK:
+ case NF4CHR:
+ result = xdr_u_int32_t(xdr, &ct->u.devdata.specdata1);
+ if (result == TRUE)
+ result = xdr_u_int32_t(xdr, &ct->u.devdata.specdata2);
+ break;
+ default:
+ // Some types need no further action
+ break;
+ }
+ return result;
+}
+
+static bool_t encode_createattrs4(
+ XDR *xdr,
+ nfs41_file_info* createattrs)
+{
+ fattr4 attrs;
+
+ /* encode attribute values from createattrs->info into attrs.attr_vals */
+ attrs.attr_vals_len = NFS4_OPAQUE_LIMIT;
+ if (!encode_file_attrs(&attrs, createattrs))
+ return FALSE;
+
+ return xdr_fattr4(xdr, &attrs);
+}
+
+static bool_t encode_op_create(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_create_args *args = (nfs41_create_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_CREATE))
+ return FALSE;
+
+ if (!encode_createtype4(xdr, &args->objtype))
+ return FALSE;
+
+ if (!encode_component(xdr, args->name))
+ return FALSE;
+
+ return encode_createattrs4(xdr, args->createattrs);
+}
+
+static bool_t xdr_change_info4(
+ XDR *xdr,
+ change_info4 *cinfo)
+{
+ if (!xdr_bool(xdr, &cinfo->atomic))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &cinfo->before))
+ return FALSE;
+
+ return xdr_u_hyper(xdr, &cinfo->after);
+}
+
+static bool_t decode_op_create(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_create_res *res = (nfs41_create_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_CREATE))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ {
+ if (!xdr_change_info4(xdr, &res->cinfo))
+ return FALSE;
+ return xdr_bitmap4(xdr, &res->attrset);
+ }
+ return TRUE;
+}
+
+
+/*
+ * OP_LINK
+ */
+static bool_t encode_op_link(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_link_args *args = (nfs41_link_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_LINK))
+ return FALSE;
+
+ return encode_component(xdr, args->newname);
+}
+
+static bool_t decode_op_link(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_link_res *res = (nfs41_link_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_LINK))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ return xdr_change_info4(xdr, &res->cinfo);
+
+ return TRUE;
+}
+
+
+/*
+ * OP_LOCK
+ */
+static bool_t xdr_locker4(
+ XDR *xdr,
+ locker4 *locker)
+{
+ if (xdr->x_op != XDR_ENCODE) {
+ eprintf("%s: xdr->x_op %d is not supported!\n",
+ "xdr_locker4", xdr->x_op);
+ return FALSE;
+ }
+
+ if (!xdr_bool(xdr, &locker->new_lock_owner))
+ return FALSE;
+
+ if (locker->new_lock_owner) {
+ /* open_to_lock_owner4 open_owner */
+ if (!xdr_u_int32_t(xdr, &locker->u.open_owner.open_seqid))
+ return FALSE;
+
+ if (!xdr_stateid4(xdr, &locker->u.open_owner.open_stateid->stateid))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &locker->u.open_owner.lock_seqid))
+ return FALSE;
+
+ return xdr_state_owner4(xdr, locker->u.open_owner.lock_owner);
+ } else {
+ /* exist_lock_owner4 lock_owner */
+ if (!xdr_stateid4(xdr, &locker->u.lock_owner.lock_stateid->stateid))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &locker->u.lock_owner.lock_seqid);
+ }
+}
+
+static bool_t encode_op_lock(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_lock_args *args = (nfs41_lock_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_LOCK))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->locktype))
+ return FALSE;
+
+ if (!xdr_bool(xdr, &args->reclaim))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->offset))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->length))
+ return FALSE;
+
+ return xdr_locker4(xdr, &args->locker);
+}
+
+static bool_t decode_lock_res_denied(
+ XDR *xdr,
+ lock_res_denied *denied)
+{
+ if (!xdr_u_hyper(xdr, &denied->offset))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &denied->length))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &denied->locktype))
+ return FALSE;
+
+ return xdr_state_owner4(xdr, &denied->owner);
+}
+
+static bool_t decode_op_lock(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_lock_res *res = (nfs41_lock_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_LOCK))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ switch (res->status) {
+ case NFS4_OK:
+ return xdr_stateid4(xdr, res->u.resok4.lock_stateid);
+ break;
+ case NFS4ERR_DENIED:
+ return decode_lock_res_denied(xdr, &res->u.denied);
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * OP_LOCKT
+ */
+static bool_t encode_op_lockt(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_lockt_args *args = (nfs41_lockt_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_LOCKT))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->locktype))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->offset))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->length))
+ return FALSE;
+
+ return xdr_state_owner4(xdr, args->owner);
+}
+
+static bool_t decode_op_lockt(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_lockt_res *res = (nfs41_lockt_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_LOCKT))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4ERR_DENIED)
+ return decode_lock_res_denied(xdr, &res->denied);
+
+ return TRUE;
+}
+
+
+/*
+ * OP_LOCKU
+ */
+static bool_t encode_op_locku(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_locku_args *args = (nfs41_locku_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_LOCKU))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->locktype))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->seqid))
+ return FALSE;
+
+ if (!xdr_stateid4(xdr, &args->lock_stateid->stateid))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->offset))
+ return FALSE;
+
+ return xdr_u_hyper(xdr, &args->length);
+}
+
+static bool_t decode_op_locku(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_locku_res *res = (nfs41_locku_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_LOCKU))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ return xdr_stateid4(xdr, res->lock_stateid);
+
+ return TRUE;
+}
+
+
+/*
+ * OP_DELEGPURGE
+ */
+static bool_t encode_op_delegpurge(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ uint64_t zero = 0;
+
+ if (unexpected_op(argop->op, OP_DELEGPURGE))
+ return FALSE;
+
+ /* The client SHOULD set the client field to zero,
+ * and the server MUST ignore the clientid field. */
+ return xdr_u_int64_t(xdr, &zero);
+}
+
+static bool_t decode_op_delegpurge(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_delegpurge_res *res = (nfs41_delegpurge_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_DELEGPURGE))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->status);
+}
+
+
+/*
+ * OP_DELEGRETURN
+ */
+static bool_t encode_op_delegreturn(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_delegreturn_args *args = (nfs41_delegreturn_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_DELEGRETURN))
+ return FALSE;
+
+ return xdr_stateid4(xdr, &args->stateid->stateid);
+}
+
+static bool_t decode_op_delegreturn(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_delegreturn_res *res = (nfs41_delegreturn_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_DELEGRETURN))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->status);
+}
+
+
+/*
+ * OP_GETATTR
+ */
+static bool_t encode_op_getattr(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_getattr_args *args = (nfs41_getattr_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_GETATTR))
+ return FALSE;
+
+ return xdr_bitmap4(xdr, args->attr_request);
+}
+
+static bool_t decode_file_attrs(
+ XDR *xdr,
+ fattr4 *attrs,
+ nfs41_file_info *info)
+{
+ if (attrs->attrmask.count >= 1) {
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_SUPPORTED_ATTRS) {
+ if (!xdr_bitmap4(xdr, info->supported_attrs))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_TYPE) {
+ if (!xdr_u_int32_t(xdr, &info->type))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_CHANGE) {
+ if (!xdr_u_hyper(xdr, &info->change))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_SIZE) {
+ if (!xdr_u_hyper(xdr, &info->size))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_LINK_SUPPORT) {
+ if (!xdr_bool(xdr, &info->link_support))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_SYMLINK_SUPPORT) {
+ if (!xdr_bool(xdr, &info->symlink_support))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_FSID) {
+ if (!xdr_fsid(xdr, &info->fsid))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_LEASE_TIME) {
+ if (!xdr_u_int32_t(xdr, &info->lease_time))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_RDATTR_ERROR) {
+ if (!xdr_u_int32_t(xdr, &info->rdattr_error))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_ACL) {
+ nfsacl41 *acl = info->acl;
+ if (!xdr_array(xdr, (char**)&acl->aces, &acl->count,
+ 32, sizeof(nfsace4), (xdrproc_t)xdr_nfsace4))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_ACLSUPPORT) {
+ if (!xdr_u_int32_t(xdr, &info->aclsupport))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_ARCHIVE) {
+ if (!xdr_bool(xdr, &info->archive))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_CANSETTIME) {
+ if (!xdr_bool(xdr, &info->cansettime))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_CASE_INSENSITIVE) {
+ if (!xdr_bool(xdr, &info->case_insensitive))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_CASE_PRESERVING) {
+ if (!xdr_bool(xdr, &info->case_preserving))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_FILEID) {
+ if (!xdr_u_hyper(xdr, &info->fileid))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_FS_LOCATIONS) {
+ if (!decode_fs_locations4(xdr, info->fs_locations))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_HIDDEN) {
+ if (!xdr_bool(xdr, &info->hidden))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_MAXREAD) {
+ if (!xdr_u_hyper(xdr, &info->maxread))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[0] & FATTR4_WORD0_MAXWRITE) {
+ if (!xdr_u_hyper(xdr, &info->maxwrite))
+ return FALSE;
+ }
+ }
+ if (attrs->attrmask.count >= 2) {
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_MODE) {
+ if (!xdr_u_int32_t(xdr, &info->mode))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_NUMLINKS) {
+ if (!xdr_u_int32_t(xdr, &info->numlinks))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_OWNER) {
+ char *ptr = &info->owner[0];
+ uint32_t owner_len;
+ if (!xdr_bytes(xdr, &ptr, &owner_len,
+ NFS4_OPAQUE_LIMIT))
+ return FALSE;
+ info->owner[owner_len] = '\0';
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_OWNER_GROUP) {
+ char *ptr = &info->owner_group[0];
+ uint32_t owner_group_len;
+ if (!xdr_bytes(xdr, &ptr, &owner_group_len,
+ NFS4_OPAQUE_LIMIT))
+ return FALSE;
+ info->owner_group[owner_group_len] = '\0';
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_SPACE_AVAIL) {
+ if (!xdr_u_hyper(xdr, &info->space_avail))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_SPACE_FREE) {
+ if (!xdr_u_hyper(xdr, &info->space_free))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_SPACE_TOTAL) {
+ if (!xdr_u_hyper(xdr, &info->space_total))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_SYSTEM) {
+ if (!xdr_bool(xdr, &info->system))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_TIME_ACCESS) {
+ if (!xdr_nfstime4(xdr, &info->time_access))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_TIME_CREATE) {
+ if (!xdr_nfstime4(xdr, &info->time_create))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_TIME_DELTA) {
+ if (!xdr_nfstime4(xdr, info->time_delta))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_TIME_MODIFY) {
+ if (!xdr_nfstime4(xdr, &info->time_modify))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_DACL) {
+ if (!xdr_nfsdacl41(xdr, info->acl))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[1] & FATTR4_WORD1_FS_LAYOUT_TYPE) {
+ if (!xdr_layout_types(xdr, &info->fs_layout_types))
+ return FALSE;
+ }
+ }
+ if (attrs->attrmask.count >= 3) {
+ if (attrs->attrmask.arr[2] & FATTR4_WORD2_MDSTHRESHOLD) {
+ if (!xdr_mdsthreshold(xdr, &info->mdsthreshold))
+ return FALSE;
+ }
+ if (attrs->attrmask.arr[2] & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
+ if (!xdr_bitmap4(xdr, info->suppattr_exclcreat))
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static bool_t decode_op_getattr(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_getattr_res *res = (nfs41_getattr_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_GETATTR))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ {
+ XDR attr_xdr;
+
+ if (!xdr_fattr4(xdr, &res->obj_attributes))
+ return FALSE;
+ xdrmem_create(&attr_xdr, (char *)res->obj_attributes.attr_vals, res->obj_attributes.attr_vals_len, XDR_DECODE);
+ return decode_file_attrs(&attr_xdr, &res->obj_attributes, res->info);
+ }
+ return TRUE;
+}
+
+
+/*
+ * OP_OPEN
+ */
+static bool_t encode_createhow4(
+ XDR *xdr,
+ createhow4 *ch)
+{
+ bool_t result = TRUE;
+
+ if (!xdr_u_int32_t(xdr, &ch->mode))
+ return FALSE;
+
+ switch (ch->mode)
+ {
+ case UNCHECKED4:
+ case GUARDED4:
+ result = encode_createattrs4(xdr, ch->createattrs);
+ break;
+ case EXCLUSIVE4:
+ result = xdr_opaque(xdr, (char *)ch->createverf, NFS4_VERIFIER_SIZE);
+ break;
+ case EXCLUSIVE4_1:
+ if (!xdr_opaque(xdr, (char *)ch->createverf, NFS4_VERIFIER_SIZE))
+ return FALSE;
+ if (!encode_createattrs4(xdr, ch->createattrs))
+ return FALSE;
+ break;
+ }
+ return result;
+}
+
+static bool_t encode_openflag4(
+ XDR *xdr,
+ openflag4 *of)
+{
+ bool_t result = TRUE;
+
+ if (!xdr_u_int32_t(xdr, &of->opentype))
+ return FALSE;
+
+ switch (of->opentype)
+ {
+ case OPEN4_CREATE:
+ result = encode_createhow4(xdr, &of->how);
+ break;
+ default:
+ break;
+ }
+ return result;
+}
+
+static bool_t encode_claim_deleg_cur(
+ XDR *xdr,
+ stateid4 *stateid,
+ nfs41_component *name)
+{
+ if (!xdr_stateid4(xdr, stateid))
+ return FALSE;
+ return encode_component(xdr, name);
+}
+
+static bool_t encode_open_claim4(
+ XDR *xdr,
+ open_claim4 *oc)
+{
+ if (!xdr_u_int32_t(xdr, &oc->claim))
+ return FALSE;
+
+ switch (oc->claim)
+ {
+ case CLAIM_NULL:
+ return encode_component(xdr, oc->u.null.filename);
+ case CLAIM_PREVIOUS:
+ return xdr_u_int32_t(xdr, &oc->u.prev.delegate_type);
+ case CLAIM_FH:
+ return TRUE; /* use current file handle */
+ case CLAIM_DELEGATE_CUR:
+ return encode_claim_deleg_cur(xdr,
+ &oc->u.deleg_cur.delegate_stateid->stateid,
+ oc->u.deleg_cur.name);
+ case CLAIM_DELEG_CUR_FH:
+ return xdr_stateid4(xdr,
+ &oc->u.deleg_cur_fh.delegate_stateid->stateid);
+ case CLAIM_DELEGATE_PREV:
+ return encode_component(xdr, oc->u.deleg_prev.filename);
+ case CLAIM_DELEG_PREV_FH:
+ return TRUE; /* use current file handle */
+ default:
+ eprintf("encode_open_claim4: unsupported claim %d.\n",
+ oc->claim);
+ return FALSE;
+ }
+}
+
+static bool_t encode_op_open(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_op_open_args *args = (nfs41_op_open_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_OPEN))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->seqid))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->share_access))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->share_deny))
+ return FALSE;
+
+ if (!xdr_state_owner4(xdr, args->owner))
+ return FALSE;
+
+ if (!encode_openflag4(xdr, &args->openhow))
+ return FALSE;
+
+ return encode_open_claim4(xdr, args->claim);
+}
+
+static bool_t decode_open_none_delegation4(
+ XDR *xdr,
+ open_delegation4 *delegation)
+{
+ enum_t why_no_deleg;
+ bool_t will_signal;
+
+ if (!xdr_enum(xdr, (enum_t*)&why_no_deleg))
+ return FALSE;
+
+ switch (why_no_deleg)
+ {
+ case WND4_CONTENTION:
+ case WND4_RESOURCE:
+ return xdr_bool(xdr, &will_signal);
+ default:
+ return TRUE;
+ }
+}
+
+static bool_t decode_open_read_delegation4(
+ XDR *xdr,
+ open_delegation4 *delegation)
+{
+ if (!xdr_stateid4(xdr, &delegation->stateid))
+ return FALSE;
+
+ if (!xdr_bool(xdr, &delegation->recalled))
+ return FALSE;
+
+ return xdr_nfsace4(xdr, &delegation->permissions);
+}
+
+static bool_t decode_modified_limit4(
+ XDR *xdr,
+ uint64_t *filesize)
+{
+ uint32_t blocks, bytes_per_block;
+
+ if (!xdr_u_int32_t(xdr, &blocks))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &bytes_per_block))
+ return FALSE;
+
+ *filesize = blocks * bytes_per_block;
+ return TRUE;
+}
+
+enum limit_by4 {
+ NFS_LIMIT_SIZE = 1,
+ NFS_LIMIT_BLOCKS = 2
+};
+
+static bool_t decode_space_limit4(
+ XDR *xdr,
+ uint64_t *filesize)
+{
+ uint32_t limitby;
+
+ if (!xdr_u_int32_t(xdr, &limitby))
+ return FALSE;
+
+ switch (limitby)
+ {
+ case NFS_LIMIT_SIZE:
+ return xdr_u_hyper(xdr, filesize);
+ case NFS_LIMIT_BLOCKS:
+ return decode_modified_limit4(xdr, filesize);
+ default:
+ eprintf("decode_space_limit4: limitby %d invalid\n", limitby);
+ return FALSE;
+ }
+}
+
+static bool_t decode_open_write_delegation4(
+ XDR *xdr,
+ open_delegation4 *delegation)
+{
+ uint64_t size_limit;
+
+ if (!xdr_stateid4(xdr, &delegation->stateid))
+ return FALSE;
+
+ if (!xdr_bool(xdr, &delegation->recalled))
+ return FALSE;
+
+ if (!decode_space_limit4(xdr, &size_limit))
+ return FALSE;
+
+ return xdr_nfsace4(xdr, &delegation->permissions);
+}
+
+static bool_t decode_open_res_ok(
+ XDR *xdr,
+ nfs41_op_open_res_ok *res)
+{
+ if (!xdr_stateid4(xdr, res->stateid))
+ return FALSE;
+
+ if (!xdr_change_info4(xdr, &res->cinfo))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->rflags))
+ return FALSE;
+
+ if (!xdr_bitmap4(xdr, &res->attrset))
+ return FALSE;
+
+ if (!xdr_enum(xdr, (enum_t*)&res->delegation->type))
+ return FALSE;
+
+ switch (res->delegation->type)
+ {
+ case OPEN_DELEGATE_NONE:
+ return TRUE;
+ case OPEN_DELEGATE_NONE_EXT:
+ return decode_open_none_delegation4(xdr, res->delegation);
+ case OPEN_DELEGATE_READ:
+ return decode_open_read_delegation4(xdr, res->delegation);
+ case OPEN_DELEGATE_WRITE:
+ return decode_open_write_delegation4(xdr, res->delegation);
+ default:
+ eprintf("decode_open_res_ok: delegation type %d not "
+ "supported.\n", res->delegation->type);
+ return FALSE;
+ }
+}
+
+static bool_t decode_op_open(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_op_open_res *res = (nfs41_op_open_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_OPEN))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ return decode_open_res_ok(xdr, &res->resok4);
+
+ return TRUE;
+}
+
+
+/*
+ * OP_OPENATTR
+ */
+static bool_t encode_op_openattr(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_openattr_args *args = (nfs41_openattr_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_OPENATTR))
+ return FALSE;
+
+ return xdr_bool(xdr, &args->createdir);
+}
+
+static bool_t decode_op_openattr(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_openattr_res *res = (nfs41_openattr_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_OPENATTR))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->status);
+}
+
+
+/*
+ * OP_READ
+ */
+static bool_t encode_op_read(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_read_args *args = (nfs41_read_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_READ))
+ return FALSE;
+
+ if (!xdr_stateid4(xdr, &args->stateid->stateid))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->offset))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &args->count);
+}
+
+static bool_t decode_read_res_ok(
+ XDR *xdr,
+ nfs41_read_res_ok *res)
+{
+ unsigned char *data = res->data;
+
+ if (!xdr_bool(xdr, &res->eof))
+ return FALSE;
+
+ return xdr_bytes(xdr, (char **)&data, &res->data_len, NFS41_MAX_FILEIO_SIZE);
+}
+
+static bool_t decode_op_read(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_read_res *res = (nfs41_read_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_READ))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ return decode_read_res_ok(xdr, &res->resok4);
+
+ return TRUE;
+}
+
+
+/*
+ * OP_READDIR
+ */
+static bool_t encode_op_readdir(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_readdir_args *args = (nfs41_readdir_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_READDIR))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->cookie.cookie))
+ return FALSE;
+
+ if (!xdr_opaque(xdr, (char *)args->cookie.verf, NFS4_VERIFIER_SIZE))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->dircount))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->maxcount))
+ return FALSE;
+
+ return xdr_bitmap4(xdr, args->attr_request);
+}
+
+typedef struct __readdir_entry_iterator {
+ unsigned char *buf_pos;
+ uint32_t remaining_len;
+ uint32_t *last_entry_offset;
+ bool_t ignore_the_rest;
+ bool_t has_next_entry;
+} readdir_entry_iterator;
+
+static bool_t decode_readdir_entry(
+ XDR *xdr,
+ readdir_entry_iterator *it)
+{
+ uint64_t cookie;
+ unsigned char name[NFS4_OPAQUE_LIMIT];
+ unsigned char *nameptr = &name[0];
+ uint32_t name_len, entry_len;
+ fattr4 attrs;
+
+ /* decode into temporaries so we can determine if there's enough
+ * room in the buffer for this entry */
+ ZeroMemory(name, NFS4_OPAQUE_LIMIT);
+ name_len = NFS4_OPAQUE_LIMIT;
+ entry_len = (uint32_t)FIELD_OFFSET(nfs41_readdir_entry, name);
+ attrs.attr_vals_len = NFS4_OPAQUE_LIMIT;
+
+ if (!xdr_u_hyper(xdr, &cookie))
+ return FALSE;
+
+ if (!xdr_bytes(xdr, (char **)&nameptr, &name_len, NFS4_OPAQUE_LIMIT))
+ return FALSE;
+
+ if (!xdr_fattr4(xdr, &attrs))
+ return FALSE;
+
+ if (!xdr_bool(xdr, &it->has_next_entry))
+ return FALSE;
+
+ if (it->ignore_the_rest)
+ return TRUE;
+
+ name_len += 1; /* account for null terminator */
+ if (entry_len + name_len <= it->remaining_len)
+ {
+ XDR fattr_xdr;
+ nfs41_readdir_entry *entry = (nfs41_readdir_entry*)it->buf_pos;
+ entry->cookie = cookie;
+ entry->name_len = name_len;
+
+ if (it->has_next_entry)
+ entry->next_entry_offset = entry_len + name_len;
+ else
+ entry->next_entry_offset = 0;
+
+ xdrmem_create(&fattr_xdr, (char *)attrs.attr_vals, attrs.attr_vals_len, XDR_DECODE);
+ if (!(decode_file_attrs(&fattr_xdr, &attrs, &entry->attr_info)))
+ entry->attr_info.rdattr_error = NFS4ERR_BADXDR;
+ StringCchCopyA(entry->name, name_len, (STRSAFE_LPCSTR)name);
+
+ it->buf_pos += entry_len + name_len;
+ it->remaining_len -= entry_len + name_len;
+ it->last_entry_offset = &entry->next_entry_offset;
+ }
+ else if (it->last_entry_offset)
+ {
+ *(it->last_entry_offset) = 0;
+ it->ignore_the_rest = 1;
+ }
+
+ return TRUE;
+}
+
+static bool_t decode_readdir_list(
+ XDR *xdr,
+ nfs41_readdir_list *dirs)
+{
+ readdir_entry_iterator iter;
+ iter.buf_pos = dirs->entries;
+ iter.remaining_len = dirs->entries_len;
+ iter.last_entry_offset = NULL;
+ iter.ignore_the_rest = 0;
+ iter.has_next_entry = 0;
+
+ if (!xdr_bool(xdr, &dirs->has_entries))
+ return FALSE;
+
+ if (dirs->has_entries)
+ {
+ do {
+ if (!decode_readdir_entry(xdr, &iter))
+ return FALSE;
+
+ } while (iter.has_next_entry);
+ }
+ dirs->entries_len -= iter.remaining_len;
+
+ if (!xdr_bool(xdr, &dirs->eof))
+ return FALSE;
+
+ /* reset eof if we couldn't fit everything in the buffer */
+ if (iter.ignore_the_rest)
+ dirs->eof = 0;
+ return TRUE;
+}
+
+static bool_t decode_op_readdir(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_readdir_res *res = (nfs41_readdir_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_READDIR))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK) {
+ if (!xdr_opaque(xdr, (char *)res->cookieverf, NFS4_VERIFIER_SIZE))
+ return FALSE;
+ return decode_readdir_list(xdr, &res->reply);
+ }
+ return TRUE;
+}
+
+
+/*
+ * OP_READLINK
+ */
+static bool_t encode_op_readlink(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ if (unexpected_op(argop->op, OP_READLINK))
+ return FALSE;
+
+ /* void */
+ return TRUE;
+}
+
+static bool_t decode_op_readlink(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_readlink_res *res = (nfs41_readlink_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_READLINK))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK) {
+ char *link = res->link;
+ return xdr_bytes(xdr, &link, &res->link_len, res->link_len);
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * OP_REMOVE
+ */
+static bool_t encode_op_remove(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_remove_args *args = (nfs41_remove_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_REMOVE))
+ return FALSE;
+
+ return encode_component(xdr, args->target);
+}
+
+static bool_t decode_op_remove(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_remove_res *res = (nfs41_remove_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_REMOVE))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ return xdr_change_info4(xdr, &res->cinfo);
+
+ return TRUE;
+}
+
+
+/*
+ * OP_RENAME
+ */
+static bool_t encode_op_rename(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_rename_args *args = (nfs41_rename_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_RENAME))
+ return FALSE;
+
+ if (!encode_component(xdr, args->oldname))
+ return FALSE;
+
+ return encode_component(xdr, args->newname);
+}
+
+static bool_t decode_op_rename(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_rename_res *res = (nfs41_rename_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_RENAME))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ {
+ if (!xdr_change_info4(xdr, &res->source_cinfo))
+ return FALSE;
+ return xdr_change_info4(xdr, &res->target_cinfo);
+ }
+ return TRUE;
+}
+
+
+/*
+ * OP_RESTOREFH
+ */
+static bool_t encode_op_restorefh(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ if (unexpected_op(argop->op, OP_RESTOREFH))
+ return FALSE;
+
+ /* void */
+ return TRUE;
+}
+
+static bool_t decode_op_restorefh(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_restorefh_res *res = (nfs41_restorefh_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_RESTOREFH))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->status);
+}
+
+
+/*
+ * OP_SAVEFH
+ */
+static bool_t encode_op_savefh(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ if (unexpected_op(argop->op, OP_SAVEFH))
+ return FALSE;
+
+ /* void */
+ return TRUE;
+}
+
+static bool_t decode_op_savefh(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_savefh_res *res = (nfs41_savefh_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_SAVEFH))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->status);
+}
+
+
+/*
+ * OP_SETATTR
+ */
+static bool_t encode_file_attrs(
+ fattr4 *attrs,
+ nfs41_file_info *info)
+{
+ uint32_t i;
+ XDR localxdr;
+
+ xdrmem_create(&localxdr, (char *)attrs->attr_vals, NFS4_OPAQUE_LIMIT, XDR_ENCODE);
+
+ attrs->attr_vals_len = 0;
+ ZeroMemory(&attrs->attrmask, sizeof(bitmap4));
+ attrs->attrmask.count = info->attrmask.count;
+
+ if (info->attrmask.count > 0) {
+ if (info->attrmask.arr[0] & FATTR4_WORD0_SIZE) {
+ if (!xdr_u_hyper(&localxdr, &info->size))
+ return FALSE;
+ attrs->attrmask.arr[0] |= FATTR4_WORD0_SIZE;
+ }
+ if (info->attrmask.arr[0] & FATTR4_WORD0_ACL) {
+ if (!xdr_nfsacl41(&localxdr, info->acl))
+ return FALSE;
+ attrs->attrmask.arr[0] |= FATTR4_WORD0_ACL;
+ }
+ if (info->attrmask.arr[0] & FATTR4_WORD0_ARCHIVE) {
+ if (!xdr_bool(&localxdr, &info->archive))
+ return FALSE;
+ attrs->attrmask.arr[0] |= FATTR4_WORD0_ARCHIVE;
+ }
+ if (info->attrmask.arr[0] & FATTR4_WORD0_HIDDEN) {
+ if (!xdr_bool(&localxdr, &info->hidden))
+ return FALSE;
+ attrs->attrmask.arr[0] |= FATTR4_WORD0_HIDDEN;
+ }
+ }
+ if (info->attrmask.count > 1) {
+ if (info->attrmask.arr[1] & FATTR4_WORD1_MODE) {
+ if (!xdr_u_int32_t(&localxdr, &info->mode))
+ return FALSE;
+ attrs->attrmask.arr[1] |= FATTR4_WORD1_MODE;
+ }
+ if (info->attrmask.arr[1] & FATTR4_WORD1_SYSTEM) {
+ if (!xdr_bool(&localxdr, &info->system))
+ return FALSE;
+ attrs->attrmask.arr[1] |= FATTR4_WORD1_SYSTEM;
+ }
+ if (info->attrmask.arr[1] & FATTR4_WORD1_TIME_ACCESS_SET) {
+ if (!xdr_settime4(&localxdr, &info->time_access, info->time_delta))
+ return FALSE;
+ attrs->attrmask.arr[1] |= FATTR4_WORD1_TIME_ACCESS_SET;
+ }
+ if (info->attrmask.arr[1] & FATTR4_WORD1_TIME_CREATE) {
+ if (!xdr_nfstime4(&localxdr, &info->time_create))
+ return FALSE;
+ attrs->attrmask.arr[1] |= FATTR4_WORD1_TIME_CREATE;
+ }
+ if (info->attrmask.arr[1] & FATTR4_WORD1_TIME_MODIFY_SET) {
+ if (!xdr_settime4(&localxdr, &info->time_modify, info->time_delta))
+ return FALSE;
+ attrs->attrmask.arr[1] |= FATTR4_WORD1_TIME_MODIFY_SET;
+ }
+ if (info->attrmask.arr[1] & FATTR4_WORD1_OWNER) {
+ char *ptr = &info->owner[0];
+ uint32_t owner_len = (uint32_t)strlen(info->owner);
+ if (!xdr_bytes(&localxdr, &ptr, &owner_len,
+ NFS4_OPAQUE_LIMIT))
+ return FALSE;
+ attrs->attrmask.arr[1] |= FATTR4_WORD1_OWNER;
+ }
+ if (info->attrmask.arr[1] & FATTR4_WORD1_OWNER_GROUP) {
+ char *ptr = &info->owner_group[0];
+ uint32_t owner_group_len = (uint32_t)strlen(info->owner_group);
+ if (!xdr_bytes(&localxdr, &ptr, &owner_group_len,
+ NFS4_OPAQUE_LIMIT))
+ return FALSE;
+ attrs->attrmask.arr[1] |= FATTR4_WORD1_OWNER_GROUP;
+ }
+ }
+ if (info->attrmask.count > 2) {
+ if (info->attrmask.arr[2] & FATTR4_WORD2_MODE_SET_MASKED) {
+ if (!xdr_u_int32_t(&localxdr, &info->mode))
+ return FALSE;
+ if (!xdr_u_int32_t(&localxdr, &info->mode_mask))
+ return FALSE;
+ attrs->attrmask.arr[2] |= FATTR4_WORD2_MODE_SET_MASKED;
+ }
+ }
+
+ /* warn if we try to set attributes that aren't handled */
+ for (i = 0; i < info->attrmask.count; i++)
+ if (attrs->attrmask.arr[i] != info->attrmask.arr[i])
+ eprintf("encode_file_attrs() attempted to encode extra "
+ "attributes in arr[%d]: encoded %d, but wanted %d.\n",
+ i, attrs->attrmask.arr[i], info->attrmask.arr[i]);
+
+ attrs->attr_vals_len = xdr_getpos(&localxdr);
+ return TRUE;
+}
+
+static bool_t encode_op_setattr(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_setattr_args *args = (nfs41_setattr_args*)argop->arg;
+ fattr4 attrs;
+
+ if (unexpected_op(argop->op, OP_SETATTR))
+ return FALSE;
+
+ if (!xdr_stateid4(xdr, &args->stateid->stateid))
+ return FALSE;
+
+ /* encode attribute values from args->info into attrs.attr_vals */
+ attrs.attr_vals_len = NFS4_OPAQUE_LIMIT;
+ if (!encode_file_attrs(&attrs, args->info))
+ return FALSE;
+
+ return xdr_fattr4(xdr, &attrs);
+}
+
+static bool_t decode_op_setattr(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_setattr_res *res = (nfs41_setattr_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_SETATTR))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ return xdr_bitmap4(xdr, &res->attrsset);
+
+ return TRUE;
+}
+
+
+/*
+ * OP_WANT_DELEGATION
+ */
+static bool_t encode_op_want_delegation(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_want_delegation_args *args = (nfs41_want_delegation_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_WANT_DELEGATION))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->want))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->claim->claim))
+ return FALSE;
+
+ return args->claim->claim != CLAIM_PREVIOUS ||
+ xdr_u_int32_t(xdr, &args->claim->prev_delegate_type);
+}
+
+static bool_t decode_op_want_delegation(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_want_delegation_res *res = (nfs41_want_delegation_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_WANT_DELEGATION))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status)
+ return TRUE;
+
+ if (!xdr_enum(xdr, (enum_t*)&res->delegation->type))
+ return FALSE;
+
+ switch (res->delegation->type)
+ {
+ case OPEN_DELEGATE_NONE:
+ return TRUE;
+ case OPEN_DELEGATE_NONE_EXT:
+ return decode_open_none_delegation4(xdr, res->delegation);
+ case OPEN_DELEGATE_READ:
+ return decode_open_read_delegation4(xdr, res->delegation);
+ case OPEN_DELEGATE_WRITE:
+ return decode_open_write_delegation4(xdr, res->delegation);
+ default:
+ eprintf("decode_open_res_ok: delegation type %d not "
+ "supported.\n", res->delegation->type);
+ return FALSE;
+ }
+}
+
+
+/*
+ * OP_FREE_STATEID
+ */
+static bool_t encode_op_free_stateid(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_free_stateid_args *args = (nfs41_free_stateid_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_FREE_STATEID))
+ return FALSE;
+
+ return xdr_stateid4(xdr, args->stateid);
+}
+
+static bool_t decode_op_free_stateid(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_free_stateid_res *res = (nfs41_free_stateid_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_FREE_STATEID))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &res->status);
+}
+
+
+/*
+ * OP_TEST_STATEID
+ */
+static bool_t encode_op_test_stateid(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_test_stateid_args *args = (nfs41_test_stateid_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_TEST_STATEID))
+ return FALSE;
+
+ return xdr_array(xdr, (char**)&args->stateids, &args->count,
+ args->count, sizeof(stateid_arg), (xdrproc_t)xdr_stateid4);
+}
+
+static bool_t decode_op_test_stateid(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_test_stateid_res *res = (nfs41_test_stateid_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_TEST_STATEID))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK) {
+ return xdr_array(xdr, (char**)&res->resok.status, &res->resok.count,
+ res->resok.count, sizeof(uint32_t), (xdrproc_t)xdr_u_int32_t);
+ }
+ return TRUE;
+}
+
+
+/*
+ * OP_WRITE
+ */
+static bool_t encode_op_write(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_write_args *args = (nfs41_write_args*)argop->arg;
+ unsigned char *data = args->data;
+
+ if (unexpected_op(argop->op, OP_WRITE))
+ return FALSE;
+
+ if (!xdr_stateid4(xdr, &args->stateid->stateid))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->offset))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->stable))
+ return FALSE;
+
+ return xdr_bytes(xdr, (char **)&data, &args->data_len, NFS41_MAX_FILEIO_SIZE);
+}
+
+static bool_t xdr_write_verf(
+ XDR *xdr,
+ nfs41_write_verf *verf)
+{
+ if (!xdr_enum(xdr, (enum_t *)&verf->committed))
+ return FALSE;
+
+ return xdr_opaque(xdr, (char *)verf->verf, NFS4_VERIFIER_SIZE);
+}
+
+static bool_t xdr_write_res_ok(
+ XDR *xdr,
+ nfs41_write_res_ok *res)
+{
+ if (!xdr_u_int32_t(xdr, &res->count))
+ return FALSE;
+
+ return xdr_write_verf(xdr, res->verf);
+}
+
+static bool_t decode_op_write(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_write_res *res = (nfs41_write_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_WRITE))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ return xdr_write_res_ok(xdr, &res->resok4);
+
+ return TRUE;
+}
+
+/*
+ * OP_SECINFO_NO_NAME
+ */
+static bool_t xdr_secinfo(
+ XDR *xdr,
+ nfs41_secinfo_info *secinfo)
+{
+ if (!xdr_u_int32_t(xdr, &secinfo->sec_flavor))
+ return FALSE;
+ if (secinfo->sec_flavor == RPCSEC_GSS) {
+ char *p = secinfo->oid;
+ if (!xdr_bytes(xdr, (char **)&p, &secinfo->oid_len, MAX_OID_LEN))
+ return FALSE;
+ if (!xdr_u_int32_t(xdr, &secinfo->qop))
+ return FALSE;
+ if (!xdr_enum(xdr, (enum_t *)&secinfo->type))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static bool_t encode_op_secinfo_noname(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_secinfo_noname_args *args = (nfs41_secinfo_noname_args *)argop->arg;
+
+ if (unexpected_op(argop->op, OP_SECINFO_NO_NAME))
+ return FALSE;
+
+ if (!xdr_enum(xdr, (enum_t *)&args->type))
+ return FALSE;
+
+ return TRUE;
+}
+
+static bool_t decode_op_secinfo_noname(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_secinfo_noname_res *res = (nfs41_secinfo_noname_res *)resop->res;
+ nfs41_secinfo_info *secinfo = res->secinfo;
+ if (unexpected_op(resop->op, OP_SECINFO_NO_NAME))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ return xdr_array(xdr, (char**)&secinfo, &res->count,
+ MAX_SECINFOS, sizeof(nfs41_secinfo_info), (xdrproc_t)xdr_secinfo);
+
+ return TRUE;
+}
+
+/*
+ * OP_SECINFO
+ */
+static bool_t encode_op_secinfo(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ nfs41_secinfo_args *args = (nfs41_secinfo_args *)argop->arg;
+
+ if (unexpected_op(argop->op, OP_SECINFO))
+ return FALSE;
+
+ if (!encode_component(xdr, args->name))
+ return FALSE;
+
+ return TRUE;
+}
+
+static bool_t decode_op_secinfo(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ nfs41_secinfo_noname_res *res = (nfs41_secinfo_noname_res *)resop->res;
+ nfs41_secinfo_info *secinfo = res->secinfo;
+
+ if (unexpected_op(resop->op, OP_SECINFO))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK)
+ return xdr_array(xdr, (char**)&secinfo, &res->count,
+ MAX_SECINFOS, sizeof(nfs41_secinfo_info), (xdrproc_t)xdr_secinfo);
+
+ return TRUE;
+}
+/*
+ * OP_GETDEVICEINFO
+ */
+static bool_t encode_op_getdeviceinfo(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ pnfs_getdeviceinfo_args *args = (pnfs_getdeviceinfo_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_GETDEVICEINFO))
+ return FALSE;
+
+ if (!xdr_opaque(xdr, (char *)args->deviceid, PNFS_DEVICEID_SIZE))
+ return FALSE;
+
+ if (!xdr_enum(xdr, (enum_t *)&args->layout_type))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->maxcount))
+ return FALSE;
+
+ return xdr_bitmap4(xdr, &args->notify_types);
+}
+
+static bool_t xdr_stripe_indices(
+ XDR *xdr,
+ pnfs_stripe_indices *indices)
+{
+ uint32_t i, count;
+
+ if (!xdr_u_int32_t(xdr, &count))
+ return FALSE;
+
+ if (count && count != indices->count) {
+ uint32_t *tmp;
+ tmp = realloc(indices->arr, count * sizeof(uint32_t));
+ if (tmp == NULL)
+ return FALSE;
+ indices->arr = tmp;
+ ZeroMemory(indices->arr, count * sizeof(uint32_t));
+ indices->count = count;
+ }
+
+ for (i = 0; i < indices->count; i++) {
+ if (!xdr_u_int32_t(xdr, &indices->arr[i]))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static bool_t xdr_pnfs_addr(
+ XDR *xdr,
+ netaddr4 *addr)
+{
+ uint32_t len;
+ char *netid = addr->netid;
+ char *uaddr = addr->uaddr;
+
+ if (xdr->x_op == XDR_ENCODE)
+ len = sizeof(addr->netid);
+ if (!xdr_bytes(xdr, &netid, &len, NFS41_NETWORK_ID_LEN))
+ return FALSE;
+
+ if (xdr->x_op == XDR_DECODE) {
+ if (len < NFS41_NETWORK_ID_LEN)
+ addr->netid[len] = 0;
+ else
+ addr->netid[NFS41_NETWORK_ID_LEN] = 0;
+ }
+
+ if (xdr->x_op == XDR_ENCODE)
+ len = sizeof(addr->uaddr);
+ if (!xdr_bytes(xdr, &uaddr, &len, NFS41_UNIVERSAL_ADDR_LEN))
+ return FALSE;
+
+ if (xdr->x_op == XDR_DECODE){
+ if (len < NFS41_UNIVERSAL_ADDR_LEN)
+ addr->uaddr[len] = 0;
+ else
+ addr->uaddr[NFS41_UNIVERSAL_ADDR_LEN] = 0;
+ }
+
+ return TRUE;
+}
+
+static bool_t xdr_multi_addr(
+ XDR *xdr,
+ multi_addr4 *list)
+{
+ netaddr4 dummy, *dest;
+ uint32_t i;
+
+ if (!xdr_u_int32_t(xdr, &list->count))
+ return FALSE;
+
+ for (i = 0; i < list->count; i++) {
+ /* if there are too many addrs, decode the extras into 'dummy' */
+ dest = i < NFS41_ADDRS_PER_SERVER ? &list->arr[i] : &dummy;
+
+ if (!xdr_pnfs_addr(xdr, dest))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static bool_t xdr_data_server_list(
+ XDR *xdr,
+ pnfs_data_server_list *servers)
+{
+ uint32_t i, count;
+
+ if (!xdr_u_int32_t(xdr, &count))
+ return FALSE;
+
+ if (count && count != servers->count) {
+ pnfs_data_server *tmp;
+ /* clear data server clients; they're still cached with nfs41_root,
+ * so pnfs_data_server_client() will look them up again */
+ for (i = 0; i < servers->count; i++)
+ servers->arr[i].client = NULL;
+
+ tmp = realloc(servers->arr, count * sizeof(pnfs_data_server));
+ if (tmp == NULL)
+ return FALSE;
+ servers->arr = tmp;
+ ZeroMemory(servers->arr, count * sizeof(pnfs_data_server));
+ for (i = servers->count; i < count; i++) /* initialize new elements */
+ InitializeSRWLock(&servers->arr[i].lock);
+ servers->count = count;
+ }
+
+ for (i = 0; i < servers->count; i++) {
+ if (!xdr_multi_addr(xdr, &servers->arr[i].addrs))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static bool_t xdr_file_device(
+ XDR *xdr,
+ pnfs_file_device *device)
+{
+ if (!xdr_stripe_indices(xdr, &device->stripes))
+ return FALSE;
+
+ return xdr_data_server_list(xdr, &device->servers);
+}
+
+static bool_t decode_getdeviceinfo_ok(
+ XDR *xdr,
+ pnfs_getdeviceinfo_res_ok *res_ok)
+{
+ u_int32_t len_ignored;
+
+ if (!xdr_enum(xdr, (enum_t *)&res_ok->device->device.type))
+ return FALSE;
+
+ if (res_ok->device->device.type != PNFS_LAYOUTTYPE_FILE)
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &len_ignored))
+ return FALSE;
+
+ if (!xdr_file_device(xdr, res_ok->device))
+ return FALSE;
+
+ return xdr_bitmap4(xdr, &res_ok->notification);
+}
+
+static bool_t decode_op_getdeviceinfo(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ pnfs_getdeviceinfo_res *res = (pnfs_getdeviceinfo_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_GETDEVICEINFO))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, (uint32_t *)&res->status))
+ return FALSE;
+
+ switch (res->status) {
+ case NFS4_OK:
+ return decode_getdeviceinfo_ok(xdr, &res->u.res_ok);
+ break;
+ case NFS4ERR_TOOSMALL:
+ {
+ uint32_t ignored;
+ return xdr_u_int32_t(xdr, &ignored);
+ }
+ break;
+ }
+ return TRUE;
+}
+
+
+/*
+ * OP_LAYOUTCOMMIT
+ */
+static bool_t encode_op_layoutcommit(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ pnfs_layoutcommit_args *args = (pnfs_layoutcommit_args*)argop->arg;
+ bool_t false_bool = FALSE;
+ bool_t true_bool = TRUE;
+ enum_t pnfs_layout = PNFS_LAYOUTTYPE_FILE;
+ uint32_t zero = 0;
+
+ if (unexpected_op(argop->op, OP_LAYOUTCOMMIT))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->offset))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->length))
+ return FALSE;
+
+ if (!xdr_bool(xdr, &false_bool)) /* loca_reclaim = 0 */
+ return FALSE;
+
+ if (!xdr_stateid4(xdr, args->stateid))
+ return FALSE;
+
+ /* loca_last_write_offset */
+ if (args->new_offset) {
+ if (!xdr_bool(xdr, &true_bool))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, args->new_offset))
+ return FALSE;
+ } else {
+ if (!xdr_bool(xdr, &false_bool))
+ return FALSE;
+ }
+
+ /* loca_time_modify */
+ if (args->new_time) {
+ if (!xdr_bool(xdr, &true_bool))
+ return FALSE;
+
+ if (!xdr_nfstime4(xdr, args->new_time))
+ return FALSE;
+ } else {
+ if (!xdr_bool(xdr, &false_bool))
+ return FALSE;
+ }
+
+ /* loca_layoutupdate */
+ if (!xdr_enum(xdr, &pnfs_layout))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &zero);
+}
+
+static bool_t decode_op_layoutcommit(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ pnfs_layoutcommit_res *res = (pnfs_layoutcommit_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_LAYOUTCOMMIT))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK) {
+ if (!xdr_bool(xdr, &res->has_new_size))
+ return FALSE;
+
+ if (res->has_new_size)
+ if (!xdr_u_hyper(xdr, &res->new_size))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * OP_LAYOUTGET
+ */
+static bool_t encode_op_layoutget(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ pnfs_layoutget_args *args = (pnfs_layoutget_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_LAYOUTGET))
+ return FALSE;
+
+ if (!xdr_bool(xdr, &args->signal_layout_avail))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, (u_int32_t *)&args->layout_type))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, (u_int32_t *)&args->iomode))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->offset))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->length))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->minlength))
+ return FALSE;
+
+ if (!xdr_stateid4(xdr, &args->stateid->stateid))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &args->maxcount);
+}
+
+static bool_t decode_file_layout_handles(
+ XDR *xdr,
+ pnfs_file_layout_handles *handles)
+{
+ uint32_t i, count;
+
+ if (!xdr_u_int32_t(xdr, &count))
+ return FALSE;
+
+ if (count && count != handles->count) {
+ nfs41_path_fh *tmp;
+ tmp = realloc(handles->arr, count * sizeof(nfs41_path_fh));
+ if (tmp == NULL)
+ return FALSE;
+ handles->arr = tmp;
+ ZeroMemory(handles->arr, count * sizeof(nfs41_path_fh));
+ handles->count = count;
+ }
+
+ for (i = 0; i < handles->count; i++) {
+ if (!xdr_fh(xdr, &handles->arr[i].fh))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static bool_t decode_file_layout(
+ XDR *xdr,
+ struct list_entry *list,
+ pnfs_layout *base)
+{
+ pnfs_file_layout *layout;
+ u_int32_t len_ignored;
+
+ if (!xdr_u_int32_t(xdr, &len_ignored))
+ return FALSE;
+
+ layout = calloc(1, sizeof(pnfs_file_layout));
+ if (layout == NULL)
+ return FALSE;
+
+ layout->layout.offset = base->offset;
+ layout->layout.length = base->length;
+ layout->layout.iomode = base->iomode;
+ layout->layout.type = base->type;
+ list_init(&layout->layout.entry);
+
+ if (!xdr_opaque(xdr, (char *)layout->deviceid, PNFS_DEVICEID_SIZE))
+ goto out_error;
+
+ if (!xdr_u_int32_t(xdr, &layout->util))
+ goto out_error;
+
+ if (!xdr_u_int32_t(xdr, &layout->first_index))
+ goto out_error;
+
+ if (!xdr_u_hyper(xdr, &layout->pattern_offset))
+ goto out_error;
+
+ if (!decode_file_layout_handles(xdr, &layout->filehandles))
+ goto out_error;
+
+ list_add_tail(list, &layout->layout.entry);
+ return TRUE;
+
+out_error:
+ free(layout);
+ return FALSE;
+}
+
+static bool_t decode_layout(
+ XDR *xdr,
+ struct list_entry *list)
+{
+ pnfs_layout layout;
+
+ if (!xdr_u_hyper(xdr, &layout.offset))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &layout.length))
+ return FALSE;
+
+ if (!xdr_enum(xdr, (enum_t *)&layout.iomode))
+ return FALSE;
+
+ if (!xdr_enum(xdr, (enum_t *)&layout.type))
+ return FALSE;
+
+ switch (layout.type) {
+ case PNFS_LAYOUTTYPE_FILE:
+ return decode_file_layout(xdr, list, &layout);
+
+ default:
+ eprintf("%s: received non-FILE layout type, %d\n",
+ "decode_file_layout", layout.type);
+ }
+ return FALSE;
+}
+
+static bool_t decode_layout_res_ok(
+ XDR *xdr,
+ pnfs_layoutget_res_ok *res)
+{
+ uint32_t i;
+
+ if (!xdr_bool(xdr, &res->return_on_close))
+ return FALSE;
+
+ if (!xdr_stateid4(xdr, &res->stateid))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &res->count))
+ return FALSE;
+
+ for (i = 0; i < res->count; i++)
+ if (!decode_layout(xdr, &res->layouts))
+ return FALSE;
+ return TRUE;
+}
+
+static bool_t decode_op_layoutget(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ pnfs_layoutget_res *res = (pnfs_layoutget_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_LAYOUTGET))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, (uint32_t *)&res->status))
+ return FALSE;
+
+ switch (res->status) {
+ case NFS4_OK:
+ return decode_layout_res_ok(xdr, res->u.res_ok);
+ case NFS4ERR_LAYOUTTRYLATER:
+ return xdr_bool(xdr, &res->u.will_signal_layout_avail);
+ }
+ return TRUE;
+}
+
+
+/*
+ * OP_LAYOUTRETURN
+ */
+static bool_t encode_op_layoutreturn(
+ XDR *xdr,
+ nfs_argop4 *argop)
+{
+ pnfs_layoutreturn_args *args = (pnfs_layoutreturn_args*)argop->arg;
+
+ if (unexpected_op(argop->op, OP_LAYOUTRETURN))
+ return FALSE;
+
+ if (!xdr_bool(xdr, &args->reclaim))
+ return FALSE;
+
+ if (!xdr_enum(xdr, (enum_t *)&args->type))
+ return FALSE;
+
+ if (!xdr_enum(xdr, (enum_t *)&args->iomode))
+ return FALSE;
+
+ if (!xdr_enum(xdr, (enum_t *)&args->return_type))
+ return FALSE;
+
+ if (args->return_type == PNFS_RETURN_FILE) {
+ u_int32_t zero = 0;
+
+ if (!xdr_u_hyper(xdr, &args->offset))
+ return FALSE;
+
+ if (!xdr_u_hyper(xdr, &args->length))
+ return FALSE;
+
+ if (!xdr_stateid4(xdr, args->stateid))
+ return FALSE;
+
+ return xdr_u_int32_t(xdr, &zero); /* size of lrf_body is 0 */
+ } else {
+ eprintf("%s: layout type (%d) is not PNFS_RETURN_FILE!\n",
+ "encode_op_layoutreturn", args->return_type);
+ return FALSE;
+ }
+}
+
+static bool_t decode_op_layoutreturn(
+ XDR *xdr,
+ nfs_resop4 *resop)
+{
+ pnfs_layoutreturn_res *res = (pnfs_layoutreturn_res*)resop->res;
+
+ if (unexpected_op(resop->op, OP_LAYOUTRETURN))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, (uint32_t *)&res->status))
+ return FALSE;
+
+ if (res->status == NFS4_OK) {
+ if (!xdr_bool(xdr, &res->stateid_present))
+ return FALSE;
+
+ if (res->stateid_present)
+ return xdr_stateid4(xdr, &res->stateid);
+ }
+ return TRUE;
+}
+
+
+/* op encode/decode table */
+typedef bool_t (*nfs_op_encode_proc)(XDR*, nfs_argop4*);
+typedef bool_t (*nfs_op_decode_proc)(XDR*, nfs_resop4*);
+
+typedef struct __op_table_entry {
+ nfs_op_encode_proc encode;
+ nfs_op_decode_proc decode;
+} op_table_entry;
+
+/* table of encode/decode functions, indexed by operation number */
+static const op_table_entry g_op_table[] = {
+ { NULL, NULL }, /* 0 unused */
+ { NULL, NULL }, /* 1 unused */
+ { NULL, NULL }, /* 2 unused */
+ { encode_op_access, decode_op_access }, /* OP_ACCESS = 3 */
+ { encode_op_close, decode_op_close }, /* OP_CLOSE = 4 */
+ { encode_op_commit, decode_op_commit }, /* OP_COMMIT = 5 */
+ { encode_op_create, decode_op_create }, /* OP_CREATE = 6 */
+ { encode_op_delegpurge, decode_op_delegpurge }, /* OP_DELEGPURGE = 7 */
+ { encode_op_delegreturn, decode_op_delegreturn }, /* OP_DELEGRETURN = 8 */
+ { encode_op_getattr, decode_op_getattr }, /* OP_GETATTR = 9 */
+ { encode_op_getfh, decode_op_getfh }, /* OP_GETFH = 10 */
+ { encode_op_link, decode_op_link }, /* OP_LINK = 11 */
+ { encode_op_lock, decode_op_lock }, /* OP_LOCK = 12 */
+ { encode_op_lockt, decode_op_lockt }, /* OP_LOCKT = 13 */
+ { encode_op_locku, decode_op_locku }, /* OP_LOCKU = 14 */
+ { encode_op_lookup, decode_op_lookup }, /* OP_LOOKUP = 15 */
+ { NULL, NULL }, /* OP_LOOKUPP = 16 */
+ { NULL, NULL }, /* OP_NVERIFY = 17 */
+ { encode_op_open, decode_op_open }, /* OP_OPEN = 18 */
+ { encode_op_openattr, decode_op_openattr }, /* OP_OPENATTR = 19 */
+ { NULL, NULL }, /* OP_OPEN_CONFIRM = 20 */
+ { NULL, NULL }, /* OP_OPEN_DOWNGRADE = 21 */
+ { encode_op_putfh, decode_op_putfh }, /* OP_PUTFH = 22 */
+ { NULL, NULL }, /* OP_PUTPUBFH = 23 */
+ { encode_op_putrootfh, decode_op_putrootfh }, /* OP_PUTROOTFH = 24 */
+ { encode_op_read, decode_op_read }, /* OP_READ = 25 */
+ { encode_op_readdir, decode_op_readdir }, /* OP_READDIR = 26 */
+ { encode_op_readlink, decode_op_readlink }, /* OP_READLINK = 27 */
+ { encode_op_remove, decode_op_remove }, /* OP_REMOVE = 28 */
+ { encode_op_rename, decode_op_rename }, /* OP_RENAME = 29 */
+ { NULL, NULL }, /* OP_RENEW = 30 */
+ { encode_op_restorefh, decode_op_restorefh }, /* OP_RESTOREFH = 31 */
+ { encode_op_savefh, decode_op_savefh }, /* OP_SAVEFH = 32 */
+ { encode_op_secinfo, decode_op_secinfo }, /* OP_SECINFO = 33 */
+ { encode_op_setattr, decode_op_setattr }, /* OP_SETATTR = 34 */
+ { NULL, NULL }, /* OP_SETCLIENTID = 35 */
+ { NULL, NULL }, /* OP_SETCLIENTID_CONFIRM = 36 */
+ { NULL, NULL }, /* OP_VERIFY = 37 */
+ { encode_op_write, decode_op_write }, /* OP_WRITE = 38 */
+ { NULL, NULL }, /* OP_RELEASE_LOCKOWNER = 39 */
+ { NULL, NULL }, /* OP_BACKCHANNEL_CTL = 40 */
+ { encode_op_bind_conn_to_session, decode_op_bind_conn_to_session }, /* OP_BIND_CONN_TO_SESSION = 41 */
+ { encode_op_exchange_id, decode_op_exchange_id }, /* OP_EXCHANGE_ID = 42 */
+ { encode_op_create_session, decode_op_create_session }, /* OP_CREATE_SESSION = 43 */
+ { encode_op_destroy_session, decode_op_destroy_session }, /* OP_DESTROY_SESSION = 44 */
+ { encode_op_free_stateid, decode_op_free_stateid }, /* OP_FREE_STATEID = 45 */
+ { NULL, NULL }, /* OP_GET_DIR_DELEGATION = 46 */
+ { encode_op_getdeviceinfo, decode_op_getdeviceinfo }, /* OP_GETDEVICEINFO = 47 */
+ { NULL, NULL }, /* OP_GETDEVICELIST = 48 */
+ { encode_op_layoutcommit, decode_op_layoutcommit }, /* OP_LAYOUTCOMMIT = 49 */
+ { encode_op_layoutget, decode_op_layoutget }, /* OP_LAYOUTGET = 50 */
+ { encode_op_layoutreturn, decode_op_layoutreturn }, /* OP_LAYOUTRETURN = 51 */
+ { encode_op_secinfo_noname, decode_op_secinfo_noname }, /* OP_SECINFO_NO_NAME = 52 */
+ { encode_op_sequence, decode_op_sequence }, /* OP_SEQUENCE = 53 */
+ { NULL, NULL }, /* OP_SET_SSV = 54 */
+ { encode_op_test_stateid, decode_op_test_stateid }, /* OP_TEST_STATEID = 55 */
+ { encode_op_want_delegation, decode_op_want_delegation }, /* OP_WANT_DELEGATION = 56 */
+ { encode_op_destroy_clientid, decode_op_destroy_clientid }, /* OP_DESTROY_CLIENTID = 57 */
+ { encode_op_reclaim_complete, decode_op_reclaim_complete }, /* OP_RECLAIM_COMPLETE = 58 */
+};
+#ifdef __REACTOS__
+static const uint32_t g_op_table_size = (sizeof(g_op_table) / sizeof(g_op_table[0]));
+#else
+static const uint32_t g_op_table_size = ARRAYSIZE(g_op_table);
+#endif
+
+static const op_table_entry* op_table_find(uint32_t op)
+{
+ return op >= g_op_table_size ? NULL : &g_op_table[op];
+}
+
+
+/*
+ * COMPOUND
+ */
+bool_t nfs_encode_compound(
+ XDR *xdr,
+ caddr_t *pargs)
+{
+ unsigned char *tag;
+
+ nfs41_compound_args *args = (nfs41_compound_args*)pargs;
+ uint32_t i;
+ const op_table_entry *entry;
+
+ tag = args->tag;
+ if (!xdr_bytes(xdr, (char **)&tag, &args->tag_len, NFS4_OPAQUE_LIMIT))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->minorversion))
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->argarray_count))
+ return FALSE;
+
+ for (i = 0; i < args->argarray_count; i++)
+ {
+ entry = op_table_find(args->argarray[i].op);
+ if (entry == NULL || entry->encode == NULL)
+ return FALSE;
+
+ if (!xdr_u_int32_t(xdr, &args->argarray[i].op))
+ return FALSE;
+ if (!entry->encode(xdr, &args->argarray[i]))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool_t nfs_decode_compound(
+ XDR *xdr,
+ caddr_t *pres)
+{
+ nfs41_compound_res *res = (nfs41_compound_res*)pres;
+ uint32_t i, expected_count, expected_op;
+ const op_table_entry *entry;
+ unsigned char *tag = res->tag;
+
+ if (!xdr_u_int32_t(xdr, &res->status))
+ return FALSE;
+
+ if (!xdr_bytes(xdr, (char **)&tag, &res->tag_len, NFS4_OPAQUE_LIMIT))
+ return FALSE;
+
+ expected_count = res->resarray_count;
+ if (!xdr_u_int32_t(xdr, &res->resarray_count))
+ return FALSE;
+
+ /* validate the number of operations against what we sent */
+ if (res->resarray_count > expected_count) {
+ eprintf("reply with %u operations, only sent %u!\n",
+ res->resarray_count, expected_count);
+ return FALSE;
+ } else if (res->resarray_count < expected_count &&
+ res->status == NFS4_OK) {
+ /* illegal for a server to reply with less operations,
+ * unless one of them fails */
+ eprintf("successful reply with only %u operations, sent %u!\n",
+ res->resarray_count, expected_count);
+ return FALSE;
+ }
+
+ for (i = 0; i < res->resarray_count; i++)
+ {
+ expected_op = res->resarray[i].op;
+ if (!xdr_u_int32_t(xdr, &res->resarray[i].op))
+ return FALSE;
+
+ /* validate each operation number against what we sent */
+ if (res->resarray[i].op != expected_op) {
+ eprintf("reply with %s in operation %u, expected %s!\n",
+ nfs_opnum_to_string(res->resarray[i].op), i+1,
+ nfs_opnum_to_string(expected_op));
+ return FALSE;
+ }
+
+ entry = op_table_find(res->resarray[i].op);
+ if (entry == NULL || entry->decode == NULL)
+ return FALSE;
+ if (!entry->decode(xdr, &res->resarray[i]))
+ return FALSE;
+ }
+ return TRUE;
+}