/* NFSv4.1 client for Windows * Copyright © 2012 The Regents of the University of Michigan * * Olga Kornievskaia * Casey Bodley * * 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 #include #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; }