linux/fs/nfs/callback_xdr.c
<<
>>
Prefs
   1/*
   2 * linux/fs/nfs/callback_xdr.c
   3 *
   4 * Copyright (C) 2004 Trond Myklebust
   5 *
   6 * NFSv4 callback encode/decode procedures
   7 */
   8#include <linux/kernel.h>
   9#include <linux/sunrpc/svc.h>
  10#include <linux/nfs4.h>
  11#include <linux/nfs_fs.h>
  12#include <linux/ratelimit.h>
  13#include <linux/printk.h>
  14#include <linux/slab.h>
  15#include <linux/sunrpc/bc_xprt.h>
  16#include "nfs4_fs.h"
  17#include "callback.h"
  18#include "internal.h"
  19
  20#define CB_OP_TAGLEN_MAXSZ      (512)
  21#define CB_OP_HDR_RES_MAXSZ     (2 + CB_OP_TAGLEN_MAXSZ)
  22#define CB_OP_GETATTR_BITMAP_MAXSZ      (4)
  23#define CB_OP_GETATTR_RES_MAXSZ (CB_OP_HDR_RES_MAXSZ + \
  24                                CB_OP_GETATTR_BITMAP_MAXSZ + \
  25                                2 + 2 + 3 + 3)
  26#define CB_OP_RECALL_RES_MAXSZ  (CB_OP_HDR_RES_MAXSZ)
  27
  28#if defined(CONFIG_NFS_V4_1)
  29#define CB_OP_LAYOUTRECALL_RES_MAXSZ    (CB_OP_HDR_RES_MAXSZ)
  30#define CB_OP_DEVICENOTIFY_RES_MAXSZ    (CB_OP_HDR_RES_MAXSZ)
  31#define CB_OP_SEQUENCE_RES_MAXSZ        (CB_OP_HDR_RES_MAXSZ + \
  32                                        4 + 1 + 3)
  33#define CB_OP_RECALLANY_RES_MAXSZ       (CB_OP_HDR_RES_MAXSZ)
  34#define CB_OP_RECALLSLOT_RES_MAXSZ      (CB_OP_HDR_RES_MAXSZ)
  35#endif /* CONFIG_NFS_V4_1 */
  36
  37#define NFSDBG_FACILITY NFSDBG_CALLBACK
  38
  39/* Internal error code */
  40#define NFS4ERR_RESOURCE_HDR    11050
  41
  42typedef __be32 (*callback_process_op_t)(void *, void *,
  43                                        struct cb_process_state *);
  44typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *);
  45typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *);
  46
  47
  48struct callback_op {
  49        callback_process_op_t process_op;
  50        callback_decode_arg_t decode_args;
  51        callback_encode_res_t encode_res;
  52        long res_maxsize;
  53};
  54
  55static struct callback_op callback_ops[];
  56
  57static __be32 nfs4_callback_null(struct svc_rqst *rqstp, void *argp, void *resp)
  58{
  59        return htonl(NFS4_OK);
  60}
  61
  62static int nfs4_decode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
  63{
  64        return xdr_argsize_check(rqstp, p);
  65}
  66
  67static int nfs4_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
  68{
  69        return xdr_ressize_check(rqstp, p);
  70}
  71
  72static __be32 *read_buf(struct xdr_stream *xdr, int nbytes)
  73{
  74        __be32 *p;
  75
  76        p = xdr_inline_decode(xdr, nbytes);
  77        if (unlikely(p == NULL))
  78                printk(KERN_WARNING "NFS: NFSv4 callback reply buffer overflowed!\n");
  79        return p;
  80}
  81
  82static __be32 decode_string(struct xdr_stream *xdr, unsigned int *len, const char **str)
  83{
  84        __be32 *p;
  85
  86        p = read_buf(xdr, 4);
  87        if (unlikely(p == NULL))
  88                return htonl(NFS4ERR_RESOURCE);
  89        *len = ntohl(*p);
  90
  91        if (*len != 0) {
  92                p = read_buf(xdr, *len);
  93                if (unlikely(p == NULL))
  94                        return htonl(NFS4ERR_RESOURCE);
  95                *str = (const char *)p;
  96        } else
  97                *str = NULL;
  98
  99        return 0;
 100}
 101
 102static __be32 decode_fh(struct xdr_stream *xdr, struct nfs_fh *fh)
 103{
 104        __be32 *p;
 105
 106        p = read_buf(xdr, 4);
 107        if (unlikely(p == NULL))
 108                return htonl(NFS4ERR_RESOURCE);
 109        fh->size = ntohl(*p);
 110        if (fh->size > NFS4_FHSIZE)
 111                return htonl(NFS4ERR_BADHANDLE);
 112        p = read_buf(xdr, fh->size);
 113        if (unlikely(p == NULL))
 114                return htonl(NFS4ERR_RESOURCE);
 115        memcpy(&fh->data[0], p, fh->size);
 116        memset(&fh->data[fh->size], 0, sizeof(fh->data) - fh->size);
 117        return 0;
 118}
 119
 120static __be32 decode_bitmap(struct xdr_stream *xdr, uint32_t *bitmap)
 121{
 122        __be32 *p;
 123        unsigned int attrlen;
 124
 125        p = read_buf(xdr, 4);
 126        if (unlikely(p == NULL))
 127                return htonl(NFS4ERR_RESOURCE);
 128        attrlen = ntohl(*p);
 129        p = read_buf(xdr, attrlen << 2);
 130        if (unlikely(p == NULL))
 131                return htonl(NFS4ERR_RESOURCE);
 132        if (likely(attrlen > 0))
 133                bitmap[0] = ntohl(*p++);
 134        if (attrlen > 1)
 135                bitmap[1] = ntohl(*p);
 136        return 0;
 137}
 138
 139static __be32 decode_stateid(struct xdr_stream *xdr, nfs4_stateid *stateid)
 140{
 141        __be32 *p;
 142
 143        p = read_buf(xdr, NFS4_STATEID_SIZE);
 144        if (unlikely(p == NULL))
 145                return htonl(NFS4ERR_RESOURCE);
 146        memcpy(stateid, p, NFS4_STATEID_SIZE);
 147        return 0;
 148}
 149
 150static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound_hdr_arg *hdr)
 151{
 152        __be32 *p;
 153        __be32 status;
 154
 155        status = decode_string(xdr, &hdr->taglen, &hdr->tag);
 156        if (unlikely(status != 0))
 157                return status;
 158        /* We do not like overly long tags! */
 159        if (hdr->taglen > CB_OP_TAGLEN_MAXSZ - 12) {
 160                printk("NFS: NFSv4 CALLBACK %s: client sent tag of length %u\n",
 161                                __func__, hdr->taglen);
 162                return htonl(NFS4ERR_RESOURCE);
 163        }
 164        p = read_buf(xdr, 12);
 165        if (unlikely(p == NULL))
 166                return htonl(NFS4ERR_RESOURCE);
 167        hdr->minorversion = ntohl(*p++);
 168        /* Check minor version is zero or one. */
 169        if (hdr->minorversion <= 1) {
 170                hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 */
 171        } else {
 172                pr_warn_ratelimited("NFS: %s: NFSv4 server callback with "
 173                        "illegal minor version %u!\n",
 174                        __func__, hdr->minorversion);
 175                return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
 176        }
 177        hdr->nops = ntohl(*p);
 178        dprintk("%s: minorversion %d nops %d\n", __func__,
 179                hdr->minorversion, hdr->nops);
 180        return 0;
 181}
 182
 183static __be32 decode_op_hdr(struct xdr_stream *xdr, unsigned int *op)
 184{
 185        __be32 *p;
 186        p = read_buf(xdr, 4);
 187        if (unlikely(p == NULL))
 188                return htonl(NFS4ERR_RESOURCE_HDR);
 189        *op = ntohl(*p);
 190        return 0;
 191}
 192
 193static __be32 decode_getattr_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_getattrargs *args)
 194{
 195        __be32 status;
 196
 197        status = decode_fh(xdr, &args->fh);
 198        if (unlikely(status != 0))
 199                goto out;
 200        args->addr = svc_addr(rqstp);
 201        status = decode_bitmap(xdr, args->bitmap);
 202out:
 203        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
 204        return status;
 205}
 206
 207static __be32 decode_recall_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_recallargs *args)
 208{
 209        __be32 *p;
 210        __be32 status;
 211
 212        args->addr = svc_addr(rqstp);
 213        status = decode_stateid(xdr, &args->stateid);
 214        if (unlikely(status != 0))
 215                goto out;
 216        p = read_buf(xdr, 4);
 217        if (unlikely(p == NULL)) {
 218                status = htonl(NFS4ERR_RESOURCE);
 219                goto out;
 220        }
 221        args->truncate = ntohl(*p);
 222        status = decode_fh(xdr, &args->fh);
 223out:
 224        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
 225        return status;
 226}
 227
 228#if defined(CONFIG_NFS_V4_1)
 229
 230static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp,
 231                                       struct xdr_stream *xdr,
 232                                       struct cb_layoutrecallargs *args)
 233{
 234        __be32 *p;
 235        __be32 status = 0;
 236        uint32_t iomode;
 237
 238        args->cbl_addr = svc_addr(rqstp);
 239        p = read_buf(xdr, 4 * sizeof(uint32_t));
 240        if (unlikely(p == NULL)) {
 241                status = htonl(NFS4ERR_BADXDR);
 242                goto out;
 243        }
 244
 245        args->cbl_layout_type = ntohl(*p++);
 246        /* Depite the spec's xdr, iomode really belongs in the FILE switch,
 247         * as it is unusable and ignored with the other types.
 248         */
 249        iomode = ntohl(*p++);
 250        args->cbl_layoutchanged = ntohl(*p++);
 251        args->cbl_recall_type = ntohl(*p++);
 252
 253        if (args->cbl_recall_type == RETURN_FILE) {
 254                args->cbl_range.iomode = iomode;
 255                status = decode_fh(xdr, &args->cbl_fh);
 256                if (unlikely(status != 0))
 257                        goto out;
 258
 259                p = read_buf(xdr, 2 * sizeof(uint64_t));
 260                if (unlikely(p == NULL)) {
 261                        status = htonl(NFS4ERR_BADXDR);
 262                        goto out;
 263                }
 264                p = xdr_decode_hyper(p, &args->cbl_range.offset);
 265                p = xdr_decode_hyper(p, &args->cbl_range.length);
 266                status = decode_stateid(xdr, &args->cbl_stateid);
 267                if (unlikely(status != 0))
 268                        goto out;
 269        } else if (args->cbl_recall_type == RETURN_FSID) {
 270                p = read_buf(xdr, 2 * sizeof(uint64_t));
 271                if (unlikely(p == NULL)) {
 272                        status = htonl(NFS4ERR_BADXDR);
 273                        goto out;
 274                }
 275                p = xdr_decode_hyper(p, &args->cbl_fsid.major);
 276                p = xdr_decode_hyper(p, &args->cbl_fsid.minor);
 277        } else if (args->cbl_recall_type != RETURN_ALL) {
 278                status = htonl(NFS4ERR_BADXDR);
 279                goto out;
 280        }
 281        dprintk("%s: ltype 0x%x iomode %d changed %d recall_type %d\n",
 282                __func__,
 283                args->cbl_layout_type, iomode,
 284                args->cbl_layoutchanged, args->cbl_recall_type);
 285out:
 286        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
 287        return status;
 288}
 289
 290static
 291__be32 decode_devicenotify_args(struct svc_rqst *rqstp,
 292                                struct xdr_stream *xdr,
 293                                struct cb_devicenotifyargs *args)
 294{
 295        __be32 *p;
 296        __be32 status = 0;
 297        u32 tmp;
 298        int n, i;
 299        args->ndevs = 0;
 300
 301        /* Num of device notifications */
 302        p = read_buf(xdr, sizeof(uint32_t));
 303        if (unlikely(p == NULL)) {
 304                status = htonl(NFS4ERR_BADXDR);
 305                goto out;
 306        }
 307        n = ntohl(*p++);
 308        if (n <= 0)
 309                goto out;
 310        if (n > ULONG_MAX / sizeof(*args->devs)) {
 311                status = htonl(NFS4ERR_BADXDR);
 312                goto out;
 313        }
 314
 315        args->devs = kmalloc(n * sizeof(*args->devs), GFP_KERNEL);
 316        if (!args->devs) {
 317                status = htonl(NFS4ERR_DELAY);
 318                goto out;
 319        }
 320
 321        /* Decode each dev notification */
 322        for (i = 0; i < n; i++) {
 323                struct cb_devicenotifyitem *dev = &args->devs[i];
 324
 325                p = read_buf(xdr, (4 * sizeof(uint32_t)) + NFS4_DEVICEID4_SIZE);
 326                if (unlikely(p == NULL)) {
 327                        status = htonl(NFS4ERR_BADXDR);
 328                        goto err;
 329                }
 330
 331                tmp = ntohl(*p++);      /* bitmap size */
 332                if (tmp != 1) {
 333                        status = htonl(NFS4ERR_INVAL);
 334                        goto err;
 335                }
 336                dev->cbd_notify_type = ntohl(*p++);
 337                if (dev->cbd_notify_type != NOTIFY_DEVICEID4_CHANGE &&
 338                    dev->cbd_notify_type != NOTIFY_DEVICEID4_DELETE) {
 339                        status = htonl(NFS4ERR_INVAL);
 340                        goto err;
 341                }
 342
 343                tmp = ntohl(*p++);      /* opaque size */
 344                if (((dev->cbd_notify_type == NOTIFY_DEVICEID4_CHANGE) &&
 345                     (tmp != NFS4_DEVICEID4_SIZE + 8)) ||
 346                    ((dev->cbd_notify_type == NOTIFY_DEVICEID4_DELETE) &&
 347                     (tmp != NFS4_DEVICEID4_SIZE + 4))) {
 348                        status = htonl(NFS4ERR_INVAL);
 349                        goto err;
 350                }
 351                dev->cbd_layout_type = ntohl(*p++);
 352                memcpy(dev->cbd_dev_id.data, p, NFS4_DEVICEID4_SIZE);
 353                p += XDR_QUADLEN(NFS4_DEVICEID4_SIZE);
 354
 355                if (dev->cbd_layout_type == NOTIFY_DEVICEID4_CHANGE) {
 356                        p = read_buf(xdr, sizeof(uint32_t));
 357                        if (unlikely(p == NULL)) {
 358                                status = htonl(NFS4ERR_BADXDR);
 359                                goto err;
 360                        }
 361                        dev->cbd_immediate = ntohl(*p++);
 362                } else {
 363                        dev->cbd_immediate = 0;
 364                }
 365
 366                args->ndevs++;
 367
 368                dprintk("%s: type %d layout 0x%x immediate %d\n",
 369                        __func__, dev->cbd_notify_type, dev->cbd_layout_type,
 370                        dev->cbd_immediate);
 371        }
 372out:
 373        dprintk("%s: status %d ndevs %d\n",
 374                __func__, ntohl(status), args->ndevs);
 375        return status;
 376err:
 377        kfree(args->devs);
 378        goto out;
 379}
 380
 381static __be32 decode_sessionid(struct xdr_stream *xdr,
 382                                 struct nfs4_sessionid *sid)
 383{
 384        __be32 *p;
 385        int len = NFS4_MAX_SESSIONID_LEN;
 386
 387        p = read_buf(xdr, len);
 388        if (unlikely(p == NULL))
 389                return htonl(NFS4ERR_RESOURCE);
 390
 391        memcpy(sid->data, p, len);
 392        return 0;
 393}
 394
 395static __be32 decode_rc_list(struct xdr_stream *xdr,
 396                               struct referring_call_list *rc_list)
 397{
 398        __be32 *p;
 399        int i;
 400        __be32 status;
 401
 402        status = decode_sessionid(xdr, &rc_list->rcl_sessionid);
 403        if (status)
 404                goto out;
 405
 406        status = htonl(NFS4ERR_RESOURCE);
 407        p = read_buf(xdr, sizeof(uint32_t));
 408        if (unlikely(p == NULL))
 409                goto out;
 410
 411        rc_list->rcl_nrefcalls = ntohl(*p++);
 412        if (rc_list->rcl_nrefcalls) {
 413                p = read_buf(xdr,
 414                             rc_list->rcl_nrefcalls * 2 * sizeof(uint32_t));
 415                if (unlikely(p == NULL))
 416                        goto out;
 417                rc_list->rcl_refcalls = kmalloc(rc_list->rcl_nrefcalls *
 418                                                sizeof(*rc_list->rcl_refcalls),
 419                                                GFP_KERNEL);
 420                if (unlikely(rc_list->rcl_refcalls == NULL))
 421                        goto out;
 422                for (i = 0; i < rc_list->rcl_nrefcalls; i++) {
 423                        rc_list->rcl_refcalls[i].rc_sequenceid = ntohl(*p++);
 424                        rc_list->rcl_refcalls[i].rc_slotid = ntohl(*p++);
 425                }
 426        }
 427        status = 0;
 428
 429out:
 430        return status;
 431}
 432
 433static __be32 decode_cb_sequence_args(struct svc_rqst *rqstp,
 434                                        struct xdr_stream *xdr,
 435                                        struct cb_sequenceargs *args)
 436{
 437        __be32 *p;
 438        int i;
 439        __be32 status;
 440
 441        status = decode_sessionid(xdr, &args->csa_sessionid);
 442        if (status)
 443                goto out;
 444
 445        status = htonl(NFS4ERR_RESOURCE);
 446        p = read_buf(xdr, 5 * sizeof(uint32_t));
 447        if (unlikely(p == NULL))
 448                goto out;
 449
 450        args->csa_addr = svc_addr(rqstp);
 451        args->csa_sequenceid = ntohl(*p++);
 452        args->csa_slotid = ntohl(*p++);
 453        args->csa_highestslotid = ntohl(*p++);
 454        args->csa_cachethis = ntohl(*p++);
 455        args->csa_nrclists = ntohl(*p++);
 456        args->csa_rclists = NULL;
 457        if (args->csa_nrclists) {
 458                args->csa_rclists = kmalloc_array(args->csa_nrclists,
 459                                                  sizeof(*args->csa_rclists),
 460                                                  GFP_KERNEL);
 461                if (unlikely(args->csa_rclists == NULL))
 462                        goto out;
 463
 464                for (i = 0; i < args->csa_nrclists; i++) {
 465                        status = decode_rc_list(xdr, &args->csa_rclists[i]);
 466                        if (status)
 467                                goto out_free;
 468                }
 469        }
 470        status = 0;
 471
 472        dprintk("%s: sessionid %x:%x:%x:%x sequenceid %u slotid %u "
 473                "highestslotid %u cachethis %d nrclists %u\n",
 474                __func__,
 475                ((u32 *)&args->csa_sessionid)[0],
 476                ((u32 *)&args->csa_sessionid)[1],
 477                ((u32 *)&args->csa_sessionid)[2],
 478                ((u32 *)&args->csa_sessionid)[3],
 479                args->csa_sequenceid, args->csa_slotid,
 480                args->csa_highestslotid, args->csa_cachethis,
 481                args->csa_nrclists);
 482out:
 483        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
 484        return status;
 485
 486out_free:
 487        for (i = 0; i < args->csa_nrclists; i++)
 488                kfree(args->csa_rclists[i].rcl_refcalls);
 489        kfree(args->csa_rclists);
 490        goto out;
 491}
 492
 493static __be32 decode_recallany_args(struct svc_rqst *rqstp,
 494                                      struct xdr_stream *xdr,
 495                                      struct cb_recallanyargs *args)
 496{
 497        uint32_t bitmap[2];
 498        __be32 *p, status;
 499
 500        args->craa_addr = svc_addr(rqstp);
 501        p = read_buf(xdr, 4);
 502        if (unlikely(p == NULL))
 503                return htonl(NFS4ERR_BADXDR);
 504        args->craa_objs_to_keep = ntohl(*p++);
 505        status = decode_bitmap(xdr, bitmap);
 506        if (unlikely(status))
 507                return status;
 508        args->craa_type_mask = bitmap[0];
 509
 510        return 0;
 511}
 512
 513static __be32 decode_recallslot_args(struct svc_rqst *rqstp,
 514                                        struct xdr_stream *xdr,
 515                                        struct cb_recallslotargs *args)
 516{
 517        __be32 *p;
 518
 519        args->crsa_addr = svc_addr(rqstp);
 520        p = read_buf(xdr, 4);
 521        if (unlikely(p == NULL))
 522                return htonl(NFS4ERR_BADXDR);
 523        args->crsa_target_max_slots = ntohl(*p++);
 524        return 0;
 525}
 526
 527#endif /* CONFIG_NFS_V4_1 */
 528
 529static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
 530{
 531        __be32 *p;
 532
 533        p = xdr_reserve_space(xdr, 4 + len);
 534        if (unlikely(p == NULL))
 535                return htonl(NFS4ERR_RESOURCE);
 536        xdr_encode_opaque(p, str, len);
 537        return 0;
 538}
 539
 540#define CB_SUPPORTED_ATTR0 (FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE)
 541#define CB_SUPPORTED_ATTR1 (FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY)
 542static __be32 encode_attr_bitmap(struct xdr_stream *xdr, const uint32_t *bitmap, __be32 **savep)
 543{
 544        __be32 bm[2];
 545        __be32 *p;
 546
 547        bm[0] = htonl(bitmap[0] & CB_SUPPORTED_ATTR0);
 548        bm[1] = htonl(bitmap[1] & CB_SUPPORTED_ATTR1);
 549        if (bm[1] != 0) {
 550                p = xdr_reserve_space(xdr, 16);
 551                if (unlikely(p == NULL))
 552                        return htonl(NFS4ERR_RESOURCE);
 553                *p++ = htonl(2);
 554                *p++ = bm[0];
 555                *p++ = bm[1];
 556        } else if (bm[0] != 0) {
 557                p = xdr_reserve_space(xdr, 12);
 558                if (unlikely(p == NULL))
 559                        return htonl(NFS4ERR_RESOURCE);
 560                *p++ = htonl(1);
 561                *p++ = bm[0];
 562        } else {
 563                p = xdr_reserve_space(xdr, 8);
 564                if (unlikely(p == NULL))
 565                        return htonl(NFS4ERR_RESOURCE);
 566                *p++ = htonl(0);
 567        }
 568        *savep = p;
 569        return 0;
 570}
 571
 572static __be32 encode_attr_change(struct xdr_stream *xdr, const uint32_t *bitmap, uint64_t change)
 573{
 574        __be32 *p;
 575
 576        if (!(bitmap[0] & FATTR4_WORD0_CHANGE))
 577                return 0;
 578        p = xdr_reserve_space(xdr, 8);
 579        if (unlikely(!p))
 580                return htonl(NFS4ERR_RESOURCE);
 581        p = xdr_encode_hyper(p, change);
 582        return 0;
 583}
 584
 585static __be32 encode_attr_size(struct xdr_stream *xdr, const uint32_t *bitmap, uint64_t size)
 586{
 587        __be32 *p;
 588
 589        if (!(bitmap[0] & FATTR4_WORD0_SIZE))
 590                return 0;
 591        p = xdr_reserve_space(xdr, 8);
 592        if (unlikely(!p))
 593                return htonl(NFS4ERR_RESOURCE);
 594        p = xdr_encode_hyper(p, size);
 595        return 0;
 596}
 597
 598static __be32 encode_attr_time(struct xdr_stream *xdr, const struct timespec *time)
 599{
 600        __be32 *p;
 601
 602        p = xdr_reserve_space(xdr, 12);
 603        if (unlikely(!p))
 604                return htonl(NFS4ERR_RESOURCE);
 605        p = xdr_encode_hyper(p, time->tv_sec);
 606        *p = htonl(time->tv_nsec);
 607        return 0;
 608}
 609
 610static __be32 encode_attr_ctime(struct xdr_stream *xdr, const uint32_t *bitmap, const struct timespec *time)
 611{
 612        if (!(bitmap[1] & FATTR4_WORD1_TIME_METADATA))
 613                return 0;
 614        return encode_attr_time(xdr,time);
 615}
 616
 617static __be32 encode_attr_mtime(struct xdr_stream *xdr, const uint32_t *bitmap, const struct timespec *time)
 618{
 619        if (!(bitmap[1] & FATTR4_WORD1_TIME_MODIFY))
 620                return 0;
 621        return encode_attr_time(xdr,time);
 622}
 623
 624static __be32 encode_compound_hdr_res(struct xdr_stream *xdr, struct cb_compound_hdr_res *hdr)
 625{
 626        __be32 status;
 627
 628        hdr->status = xdr_reserve_space(xdr, 4);
 629        if (unlikely(hdr->status == NULL))
 630                return htonl(NFS4ERR_RESOURCE);
 631        status = encode_string(xdr, hdr->taglen, hdr->tag);
 632        if (unlikely(status != 0))
 633                return status;
 634        hdr->nops = xdr_reserve_space(xdr, 4);
 635        if (unlikely(hdr->nops == NULL))
 636                return htonl(NFS4ERR_RESOURCE);
 637        return 0;
 638}
 639
 640static __be32 encode_op_hdr(struct xdr_stream *xdr, uint32_t op, __be32 res)
 641{
 642        __be32 *p;
 643        
 644        p = xdr_reserve_space(xdr, 8);
 645        if (unlikely(p == NULL))
 646                return htonl(NFS4ERR_RESOURCE_HDR);
 647        *p++ = htonl(op);
 648        *p = res;
 649        return 0;
 650}
 651
 652static __be32 encode_getattr_res(struct svc_rqst *rqstp, struct xdr_stream *xdr, const struct cb_getattrres *res)
 653{
 654        __be32 *savep = NULL;
 655        __be32 status = res->status;
 656        
 657        if (unlikely(status != 0))
 658                goto out;
 659        status = encode_attr_bitmap(xdr, res->bitmap, &savep);
 660        if (unlikely(status != 0))
 661                goto out;
 662        status = encode_attr_change(xdr, res->bitmap, res->change_attr);
 663        if (unlikely(status != 0))
 664                goto out;
 665        status = encode_attr_size(xdr, res->bitmap, res->size);
 666        if (unlikely(status != 0))
 667                goto out;
 668        status = encode_attr_ctime(xdr, res->bitmap, &res->ctime);
 669        if (unlikely(status != 0))
 670                goto out;
 671        status = encode_attr_mtime(xdr, res->bitmap, &res->mtime);
 672        *savep = htonl((unsigned int)((char *)xdr->p - (char *)(savep+1)));
 673out:
 674        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
 675        return status;
 676}
 677
 678#if defined(CONFIG_NFS_V4_1)
 679
 680static __be32 encode_sessionid(struct xdr_stream *xdr,
 681                                 const struct nfs4_sessionid *sid)
 682{
 683        __be32 *p;
 684        int len = NFS4_MAX_SESSIONID_LEN;
 685
 686        p = xdr_reserve_space(xdr, len);
 687        if (unlikely(p == NULL))
 688                return htonl(NFS4ERR_RESOURCE);
 689
 690        memcpy(p, sid, len);
 691        return 0;
 692}
 693
 694static __be32 encode_cb_sequence_res(struct svc_rqst *rqstp,
 695                                       struct xdr_stream *xdr,
 696                                       const struct cb_sequenceres *res)
 697{
 698        __be32 *p;
 699        __be32 status = res->csr_status;
 700
 701        if (unlikely(status != 0))
 702                goto out;
 703
 704        encode_sessionid(xdr, &res->csr_sessionid);
 705
 706        p = xdr_reserve_space(xdr, 4 * sizeof(uint32_t));
 707        if (unlikely(p == NULL))
 708                return htonl(NFS4ERR_RESOURCE);
 709
 710        *p++ = htonl(res->csr_sequenceid);
 711        *p++ = htonl(res->csr_slotid);
 712        *p++ = htonl(res->csr_highestslotid);
 713        *p++ = htonl(res->csr_target_highestslotid);
 714out:
 715        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
 716        return status;
 717}
 718
 719static __be32
 720preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
 721{
 722        if (op_nr == OP_CB_SEQUENCE) {
 723                if (nop != 0)
 724                        return htonl(NFS4ERR_SEQUENCE_POS);
 725        } else {
 726                if (nop == 0)
 727                        return htonl(NFS4ERR_OP_NOT_IN_SESSION);
 728        }
 729
 730        switch (op_nr) {
 731        case OP_CB_GETATTR:
 732        case OP_CB_RECALL:
 733        case OP_CB_SEQUENCE:
 734        case OP_CB_RECALL_ANY:
 735        case OP_CB_RECALL_SLOT:
 736        case OP_CB_LAYOUTRECALL:
 737        case OP_CB_NOTIFY_DEVICEID:
 738                *op = &callback_ops[op_nr];
 739                break;
 740
 741        case OP_CB_NOTIFY:
 742        case OP_CB_PUSH_DELEG:
 743        case OP_CB_RECALLABLE_OBJ_AVAIL:
 744        case OP_CB_WANTS_CANCELLED:
 745        case OP_CB_NOTIFY_LOCK:
 746                return htonl(NFS4ERR_NOTSUPP);
 747
 748        default:
 749                return htonl(NFS4ERR_OP_ILLEGAL);
 750        }
 751
 752        return htonl(NFS_OK);
 753}
 754
 755static void nfs4_callback_free_slot(struct nfs4_session *session)
 756{
 757        struct nfs4_slot_table *tbl = &session->bc_slot_table;
 758
 759        spin_lock(&tbl->slot_tbl_lock);
 760        /*
 761         * Let the state manager know callback processing done.
 762         * A single slot, so highest used slotid is either 0 or -1
 763         */
 764        tbl->highest_used_slotid = NFS4_NO_SLOT;
 765        nfs4_check_drain_bc_complete(session);
 766        spin_unlock(&tbl->slot_tbl_lock);
 767}
 768
 769static void nfs4_cb_free_slot(struct cb_process_state *cps)
 770{
 771        if (cps->slotid != NFS4_NO_SLOT)
 772                nfs4_callback_free_slot(cps->clp->cl_session);
 773}
 774
 775#else /* CONFIG_NFS_V4_1 */
 776
 777static __be32
 778preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
 779{
 780        return htonl(NFS4ERR_MINOR_VERS_MISMATCH);
 781}
 782
 783static void nfs4_cb_free_slot(struct cb_process_state *cps)
 784{
 785}
 786#endif /* CONFIG_NFS_V4_1 */
 787
 788static __be32
 789preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
 790{
 791        switch (op_nr) {
 792        case OP_CB_GETATTR:
 793        case OP_CB_RECALL:
 794                *op = &callback_ops[op_nr];
 795                break;
 796        default:
 797                return htonl(NFS4ERR_OP_ILLEGAL);
 798        }
 799
 800        return htonl(NFS_OK);
 801}
 802
 803static __be32 process_op(uint32_t minorversion, int nop,
 804                struct svc_rqst *rqstp,
 805                struct xdr_stream *xdr_in, void *argp,
 806                struct xdr_stream *xdr_out, void *resp,
 807                struct cb_process_state *cps)
 808{
 809        struct callback_op *op = &callback_ops[0];
 810        unsigned int op_nr;
 811        __be32 status;
 812        long maxlen;
 813        __be32 res;
 814
 815        dprintk("%s: start\n", __func__);
 816        status = decode_op_hdr(xdr_in, &op_nr);
 817        if (unlikely(status))
 818                return status;
 819
 820        dprintk("%s: minorversion=%d nop=%d op_nr=%u\n",
 821                __func__, minorversion, nop, op_nr);
 822
 823        status = minorversion ? preprocess_nfs41_op(nop, op_nr, &op) :
 824                                preprocess_nfs4_op(op_nr, &op);
 825        if (status == htonl(NFS4ERR_OP_ILLEGAL))
 826                op_nr = OP_CB_ILLEGAL;
 827        if (status)
 828                goto encode_hdr;
 829
 830        if (cps->drc_status) {
 831                status = cps->drc_status;
 832                goto encode_hdr;
 833        }
 834
 835        maxlen = xdr_out->end - xdr_out->p;
 836        if (maxlen > 0 && maxlen < PAGE_SIZE) {
 837                status = op->decode_args(rqstp, xdr_in, argp);
 838                if (likely(status == 0))
 839                        status = op->process_op(argp, resp, cps);
 840        } else
 841                status = htonl(NFS4ERR_RESOURCE);
 842
 843encode_hdr:
 844        res = encode_op_hdr(xdr_out, op_nr, status);
 845        if (unlikely(res))
 846                return res;
 847        if (op->encode_res != NULL && status == 0)
 848                status = op->encode_res(rqstp, xdr_out, resp);
 849        dprintk("%s: done, status = %d\n", __func__, ntohl(status));
 850        return status;
 851}
 852
 853/*
 854 * Decode, process and encode a COMPOUND
 855 */
 856static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *resp)
 857{
 858        struct cb_compound_hdr_arg hdr_arg = { 0 };
 859        struct cb_compound_hdr_res hdr_res = { NULL };
 860        struct xdr_stream xdr_in, xdr_out;
 861        __be32 *p, status;
 862        struct cb_process_state cps = {
 863                .drc_status = 0,
 864                .clp = NULL,
 865                .slotid = NFS4_NO_SLOT,
 866                .net = SVC_NET(rqstp),
 867        };
 868        unsigned int nops = 0;
 869
 870        dprintk("%s: start\n", __func__);
 871
 872        xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base);
 873
 874        p = (__be32*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len);
 875        xdr_init_encode(&xdr_out, &rqstp->rq_res, p);
 876
 877        status = decode_compound_hdr_arg(&xdr_in, &hdr_arg);
 878        if (status == __constant_htonl(NFS4ERR_RESOURCE))
 879                return rpc_garbage_args;
 880
 881        if (hdr_arg.minorversion == 0) {
 882                cps.clp = nfs4_find_client_ident(SVC_NET(rqstp), hdr_arg.cb_ident);
 883                if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp))
 884                        return rpc_drop_reply;
 885        }
 886
 887        hdr_res.taglen = hdr_arg.taglen;
 888        hdr_res.tag = hdr_arg.tag;
 889        if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0)
 890                return rpc_system_err;
 891
 892        while (status == 0 && nops != hdr_arg.nops) {
 893                status = process_op(hdr_arg.minorversion, nops, rqstp,
 894                                    &xdr_in, argp, &xdr_out, resp, &cps);
 895                nops++;
 896        }
 897
 898        /* Buffer overflow in decode_ops_hdr or encode_ops_hdr. Return
 899        * resource error in cb_compound status without returning op */
 900        if (unlikely(status == htonl(NFS4ERR_RESOURCE_HDR))) {
 901                status = htonl(NFS4ERR_RESOURCE);
 902                nops--;
 903        }
 904
 905        *hdr_res.status = status;
 906        *hdr_res.nops = htonl(nops);
 907        nfs4_cb_free_slot(&cps);
 908        nfs_put_client(cps.clp);
 909        dprintk("%s: done, status = %u\n", __func__, ntohl(status));
 910        return rpc_success;
 911}
 912
 913/*
 914 * Define NFS4 callback COMPOUND ops.
 915 */
 916static struct callback_op callback_ops[] = {
 917        [0] = {
 918                .res_maxsize = CB_OP_HDR_RES_MAXSZ,
 919        },
 920        [OP_CB_GETATTR] = {
 921                .process_op = (callback_process_op_t)nfs4_callback_getattr,
 922                .decode_args = (callback_decode_arg_t)decode_getattr_args,
 923                .encode_res = (callback_encode_res_t)encode_getattr_res,
 924                .res_maxsize = CB_OP_GETATTR_RES_MAXSZ,
 925        },
 926        [OP_CB_RECALL] = {
 927                .process_op = (callback_process_op_t)nfs4_callback_recall,
 928                .decode_args = (callback_decode_arg_t)decode_recall_args,
 929                .res_maxsize = CB_OP_RECALL_RES_MAXSZ,
 930        },
 931#if defined(CONFIG_NFS_V4_1)
 932        [OP_CB_LAYOUTRECALL] = {
 933                .process_op = (callback_process_op_t)nfs4_callback_layoutrecall,
 934                .decode_args =
 935                        (callback_decode_arg_t)decode_layoutrecall_args,
 936                .res_maxsize = CB_OP_LAYOUTRECALL_RES_MAXSZ,
 937        },
 938        [OP_CB_NOTIFY_DEVICEID] = {
 939                .process_op = (callback_process_op_t)nfs4_callback_devicenotify,
 940                .decode_args =
 941                        (callback_decode_arg_t)decode_devicenotify_args,
 942                .res_maxsize = CB_OP_DEVICENOTIFY_RES_MAXSZ,
 943        },
 944        [OP_CB_SEQUENCE] = {
 945                .process_op = (callback_process_op_t)nfs4_callback_sequence,
 946                .decode_args = (callback_decode_arg_t)decode_cb_sequence_args,
 947                .encode_res = (callback_encode_res_t)encode_cb_sequence_res,
 948                .res_maxsize = CB_OP_SEQUENCE_RES_MAXSZ,
 949        },
 950        [OP_CB_RECALL_ANY] = {
 951                .process_op = (callback_process_op_t)nfs4_callback_recallany,
 952                .decode_args = (callback_decode_arg_t)decode_recallany_args,
 953                .res_maxsize = CB_OP_RECALLANY_RES_MAXSZ,
 954        },
 955        [OP_CB_RECALL_SLOT] = {
 956                .process_op = (callback_process_op_t)nfs4_callback_recallslot,
 957                .decode_args = (callback_decode_arg_t)decode_recallslot_args,
 958                .res_maxsize = CB_OP_RECALLSLOT_RES_MAXSZ,
 959        },
 960#endif /* CONFIG_NFS_V4_1 */
 961};
 962
 963/*
 964 * Define NFS4 callback procedures
 965 */
 966static struct svc_procedure nfs4_callback_procedures1[] = {
 967        [CB_NULL] = {
 968                .pc_func = nfs4_callback_null,
 969                .pc_decode = (kxdrproc_t)nfs4_decode_void,
 970                .pc_encode = (kxdrproc_t)nfs4_encode_void,
 971                .pc_xdrressize = 1,
 972        },
 973        [CB_COMPOUND] = {
 974                .pc_func = nfs4_callback_compound,
 975                .pc_encode = (kxdrproc_t)nfs4_encode_void,
 976                .pc_argsize = 256,
 977                .pc_ressize = 256,
 978                .pc_xdrressize = NFS4_CALLBACK_BUFSIZE,
 979        }
 980};
 981
 982struct svc_version nfs4_callback_version1 = {
 983        .vs_vers = 1,
 984        .vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1),
 985        .vs_proc = nfs4_callback_procedures1,
 986        .vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
 987        .vs_dispatch = NULL,
 988        .vs_hidden = 1,
 989};
 990
 991struct svc_version nfs4_callback_version4 = {
 992        .vs_vers = 4,
 993        .vs_nproc = ARRAY_SIZE(nfs4_callback_procedures1),
 994        .vs_proc = nfs4_callback_procedures1,
 995        .vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
 996        .vs_dispatch = NULL,
 997        .vs_hidden = 1,
 998};
 999
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.