mirror of
https://github.com/reactos/reactos.git
synced 2024-10-30 11:35:58 +00:00
3680 lines
89 KiB
C
3680 lines
89 KiB
C
/* 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;
|
|
}
|