linux/fs/nfsd/nfs3xdr.c
<<
>>
Prefs
   1/*
   2 * linux/fs/nfsd/nfs3xdr.c
   3 *
   4 * XDR support for nfsd/protocol version 3.
   5 *
   6 * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
   7 *
   8 * 2003-08-09 Jamie Lokier: Use htonl() for nanoseconds, not htons()!
   9 */
  10
  11#include <linux/types.h>
  12#include <linux/time.h>
  13#include <linux/nfs3.h>
  14#include <linux/list.h>
  15#include <linux/spinlock.h>
  16#include <linux/dcache.h>
  17#include <linux/namei.h>
  18#include <linux/mm.h>
  19#include <linux/vfs.h>
  20#include <linux/sunrpc/xdr.h>
  21#include <linux/sunrpc/svc.h>
  22#include <linux/nfsd/nfsd.h>
  23#include <linux/nfsd/xdr3.h>
  24
  25#define NFSDDBG_FACILITY                NFSDDBG_XDR
  26
  27#ifdef NFSD_OPTIMIZE_SPACE
  28# define inline
  29#endif
  30
  31
  32/*
  33 * Mapping of S_IF* types to NFS file types
  34 */
  35static u32      nfs3_ftypes[] = {
  36        NF3NON,  NF3FIFO, NF3CHR, NF3BAD,
  37        NF3DIR,  NF3BAD,  NF3BLK, NF3BAD,
  38        NF3REG,  NF3BAD,  NF3LNK, NF3BAD,
  39        NF3SOCK, NF3BAD,  NF3LNK, NF3BAD,
  40};
  41
  42/*
  43 * XDR functions for basic NFS types
  44 */
  45static inline u32 *
  46encode_time3(u32 *p, struct timespec *time)
  47{
  48        *p++ = htonl((u32) time->tv_sec); *p++ = htonl(time->tv_nsec);
  49        return p;
  50}
  51
  52static inline u32 *
  53decode_time3(u32 *p, struct timespec *time)
  54{
  55        time->tv_sec = ntohl(*p++);
  56        time->tv_nsec = ntohl(*p++);
  57        return p;
  58}
  59
  60static inline u32 *
  61decode_fh(u32 *p, struct svc_fh *fhp)
  62{
  63        unsigned int size;
  64        fh_init(fhp, NFS3_FHSIZE);
  65        size = ntohl(*p++);
  66        if (size > NFS3_FHSIZE)
  67                return NULL;
  68
  69        memcpy(&fhp->fh_handle.fh_base, p, size);
  70        fhp->fh_handle.fh_size = size;
  71        return p + XDR_QUADLEN(size);
  72}
  73
  74/* Helper function for NFSv3 ACL code */
  75u32 *nfs3svc_decode_fh(u32 *p, struct svc_fh *fhp)
  76{
  77        return decode_fh(p, fhp);
  78}
  79
  80static inline u32 *
  81encode_fh(u32 *p, struct svc_fh *fhp)
  82{
  83        unsigned int size = fhp->fh_handle.fh_size;
  84        *p++ = htonl(size);
  85        if (size) p[XDR_QUADLEN(size)-1]=0;
  86        memcpy(p, &fhp->fh_handle.fh_base, size);
  87        return p + XDR_QUADLEN(size);
  88}
  89
  90/*
  91 * Decode a file name and make sure that the path contains
  92 * no slashes or null bytes.
  93 */
  94static inline u32 *
  95decode_filename(u32 *p, char **namp, int *lenp)
  96{
  97        char            *name;
  98        int             i;
  99
 100        if ((p = xdr_decode_string_inplace(p, namp, lenp, NFS3_MAXNAMLEN)) != NULL) {
 101                for (i = 0, name = *namp; i < *lenp; i++, name++) {
 102                        if (*name == '\0' || *name == '/')
 103                                return NULL;
 104                }
 105        }
 106
 107        return p;
 108}
 109
 110static inline u32 *
 111decode_sattr3(u32 *p, struct iattr *iap)
 112{
 113        u32     tmp;
 114
 115        iap->ia_valid = 0;
 116
 117        if (*p++) {
 118                iap->ia_valid |= ATTR_MODE;
 119                iap->ia_mode = ntohl(*p++);
 120        }
 121        if (*p++) {
 122                iap->ia_valid |= ATTR_UID;
 123                iap->ia_uid = ntohl(*p++);
 124        }
 125        if (*p++) {
 126                iap->ia_valid |= ATTR_GID;
 127                iap->ia_gid = ntohl(*p++);
 128        }
 129        if (*p++) {
 130                u64     newsize;
 131
 132                iap->ia_valid |= ATTR_SIZE;
 133                p = xdr_decode_hyper(p, &newsize);
 134                if (newsize <= NFS_OFFSET_MAX)
 135                        iap->ia_size = newsize;
 136                else
 137                        iap->ia_size = NFS_OFFSET_MAX;
 138        }
 139        if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
 140                iap->ia_valid |= ATTR_ATIME;
 141        } else if (tmp == 2) {          /* set to client time */
 142                iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET;
 143                iap->ia_atime.tv_sec = ntohl(*p++);
 144                iap->ia_atime.tv_nsec = ntohl(*p++);
 145        }
 146        if ((tmp = ntohl(*p++)) == 1) { /* set to server time */
 147                iap->ia_valid |= ATTR_MTIME;
 148        } else if (tmp == 2) {          /* set to client time */
 149                iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET;
 150                iap->ia_mtime.tv_sec = ntohl(*p++);
 151                iap->ia_mtime.tv_nsec = ntohl(*p++);
 152        }
 153        return p;
 154}
 155
 156static inline u32 *
 157encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp,
 158              struct kstat *stat)
 159{
 160        struct dentry   *dentry = fhp->fh_dentry;
 161        struct timespec time;
 162
 163        *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
 164        *p++ = htonl((u32) stat->mode);
 165        *p++ = htonl((u32) stat->nlink);
 166        *p++ = htonl((u32) nfsd_ruid(rqstp, stat->uid));
 167        *p++ = htonl((u32) nfsd_rgid(rqstp, stat->gid));
 168        if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) {
 169                p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
 170        } else {
 171                p = xdr_encode_hyper(p, (u64) stat->size);
 172        }
 173        p = xdr_encode_hyper(p, ((u64)stat->blocks) << 9);
 174        *p++ = htonl((u32) MAJOR(stat->rdev));
 175        *p++ = htonl((u32) MINOR(stat->rdev));
 176        if (is_fsid(fhp, rqstp->rq_reffh))
 177                p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
 178        else
 179                p = xdr_encode_hyper(p, (u64) huge_encode_dev(stat->dev));
 180        p = xdr_encode_hyper(p, (u64) stat->ino);
 181        p = encode_time3(p, &stat->atime);
 182        lease_get_mtime(dentry->d_inode, &time); 
 183        p = encode_time3(p, &time);
 184        p = encode_time3(p, &stat->ctime);
 185
 186        return p;
 187}
 188
 189static inline u32 *
 190encode_saved_post_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 191{
 192        struct inode    *inode = fhp->fh_dentry->d_inode;
 193
 194        /* Attributes to follow */
 195        *p++ = xdr_one;
 196
 197        *p++ = htonl(nfs3_ftypes[(fhp->fh_post_mode & S_IFMT) >> 12]);
 198        *p++ = htonl((u32) fhp->fh_post_mode);
 199        *p++ = htonl((u32) fhp->fh_post_nlink);
 200        *p++ = htonl((u32) nfsd_ruid(rqstp, fhp->fh_post_uid));
 201        *p++ = htonl((u32) nfsd_rgid(rqstp, fhp->fh_post_gid));
 202        if (S_ISLNK(fhp->fh_post_mode) && fhp->fh_post_size > NFS3_MAXPATHLEN) {
 203                p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
 204        } else {
 205                p = xdr_encode_hyper(p, (u64) fhp->fh_post_size);
 206        }
 207        p = xdr_encode_hyper(p, ((u64)fhp->fh_post_blocks) << 9);
 208        *p++ = fhp->fh_post_rdev[0];
 209        *p++ = fhp->fh_post_rdev[1];
 210        if (is_fsid(fhp, rqstp->rq_reffh))
 211                p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
 212        else
 213                p = xdr_encode_hyper(p, (u64)huge_encode_dev(inode->i_sb->s_dev));
 214        p = xdr_encode_hyper(p, (u64) inode->i_ino);
 215        p = encode_time3(p, &fhp->fh_post_atime);
 216        p = encode_time3(p, &fhp->fh_post_mtime);
 217        p = encode_time3(p, &fhp->fh_post_ctime);
 218
 219        return p;
 220}
 221
 222/*
 223 * Encode post-operation attributes.
 224 * The inode may be NULL if the call failed because of a stale file
 225 * handle. In this case, no attributes are returned.
 226 */
 227static u32 *
 228encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 229{
 230        struct dentry *dentry = fhp->fh_dentry;
 231        if (dentry && dentry->d_inode != NULL) {
 232                int err;
 233                struct kstat stat;
 234
 235                err = vfs_getattr(fhp->fh_export->ex_mnt, dentry, &stat);
 236                if (!err) {
 237                        *p++ = xdr_one;         /* attributes follow */
 238                        return encode_fattr3(rqstp, p, fhp, &stat);
 239                }
 240        }
 241        *p++ = xdr_zero;
 242        return p;
 243}
 244
 245/* Helper for NFSv3 ACLs */
 246u32 *
 247nfs3svc_encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 248{
 249        return encode_post_op_attr(rqstp, p, fhp);
 250}
 251
 252/*
 253 * Enocde weak cache consistency data
 254 */
 255static u32 *
 256encode_wcc_data(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 257{
 258        struct dentry   *dentry = fhp->fh_dentry;
 259
 260        if (dentry && dentry->d_inode && fhp->fh_post_saved) {
 261                if (fhp->fh_pre_saved) {
 262                        *p++ = xdr_one;
 263                        p = xdr_encode_hyper(p, (u64) fhp->fh_pre_size);
 264                        p = encode_time3(p, &fhp->fh_pre_mtime);
 265                        p = encode_time3(p, &fhp->fh_pre_ctime);
 266                } else {
 267                        *p++ = xdr_zero;
 268                }
 269                return encode_saved_post_attr(rqstp, p, fhp);
 270        }
 271        /* no pre- or post-attrs */
 272        *p++ = xdr_zero;
 273        return encode_post_op_attr(rqstp, p, fhp);
 274}
 275
 276
 277/*
 278 * XDR decode functions
 279 */
 280int
 281nfs3svc_decode_fhandle(struct svc_rqst *rqstp, u32 *p, struct nfsd_fhandle *args)
 282{
 283        if (!(p = decode_fh(p, &args->fh)))
 284                return 0;
 285        return xdr_argsize_check(rqstp, p);
 286}
 287
 288int
 289nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, u32 *p,
 290                                        struct nfsd3_sattrargs *args)
 291{
 292        if (!(p = decode_fh(p, &args->fh))
 293         || !(p = decode_sattr3(p, &args->attrs)))
 294                return 0;
 295
 296        if ((args->check_guard = ntohl(*p++)) != 0) { 
 297                struct timespec time; 
 298                p = decode_time3(p, &time);
 299                args->guardtime = time.tv_sec;
 300        }
 301
 302        return xdr_argsize_check(rqstp, p);
 303}
 304
 305int
 306nfs3svc_decode_diropargs(struct svc_rqst *rqstp, u32 *p,
 307                                        struct nfsd3_diropargs *args)
 308{
 309        if (!(p = decode_fh(p, &args->fh))
 310         || !(p = decode_filename(p, &args->name, &args->len)))
 311                return 0;
 312
 313        return xdr_argsize_check(rqstp, p);
 314}
 315
 316int
 317nfs3svc_decode_accessargs(struct svc_rqst *rqstp, u32 *p,
 318                                        struct nfsd3_accessargs *args)
 319{
 320        if (!(p = decode_fh(p, &args->fh)))
 321                return 0;
 322        args->access = ntohl(*p++);
 323
 324        return xdr_argsize_check(rqstp, p);
 325}
 326
 327int
 328nfs3svc_decode_readargs(struct svc_rqst *rqstp, u32 *p,
 329                                        struct nfsd3_readargs *args)
 330{
 331        unsigned int len;
 332        int v,pn;
 333
 334        if (!(p = decode_fh(p, &args->fh))
 335         || !(p = xdr_decode_hyper(p, &args->offset)))
 336                return 0;
 337
 338        len = args->count = ntohl(*p++);
 339
 340        if (len > NFSSVC_MAXBLKSIZE)
 341                len = NFSSVC_MAXBLKSIZE;
 342
 343        /* set up the kvec */
 344        v=0;
 345        while (len > 0) {
 346                pn = rqstp->rq_resused;
 347                svc_take_page(rqstp);
 348                args->vec[v].iov_base = page_address(rqstp->rq_respages[pn]);
 349                args->vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE;
 350                len -= args->vec[v].iov_len;
 351                v++;
 352        }
 353        args->vlen = v;
 354        return xdr_argsize_check(rqstp, p);
 355}
 356
 357int
 358nfs3svc_decode_writeargs(struct svc_rqst *rqstp, u32 *p,
 359                                        struct nfsd3_writeargs *args)
 360{
 361        unsigned int len, v, hdr;
 362
 363        if (!(p = decode_fh(p, &args->fh))
 364         || !(p = xdr_decode_hyper(p, &args->offset)))
 365                return 0;
 366
 367        args->count = ntohl(*p++);
 368        args->stable = ntohl(*p++);
 369        len = args->len = ntohl(*p++);
 370
 371        hdr = (void*)p - rqstp->rq_arg.head[0].iov_base;
 372        if (rqstp->rq_arg.len < hdr ||
 373            rqstp->rq_arg.len - hdr < len)
 374                return 0;
 375
 376        args->vec[0].iov_base = (void*)p;
 377        args->vec[0].iov_len = rqstp->rq_arg.head[0].iov_len - hdr;
 378
 379        if (len > NFSSVC_MAXBLKSIZE)
 380                len = NFSSVC_MAXBLKSIZE;
 381        v=  0;
 382        while (len > args->vec[v].iov_len) {
 383                len -= args->vec[v].iov_len;
 384                v++;
 385                args->vec[v].iov_base = page_address(rqstp->rq_argpages[v]);
 386                args->vec[v].iov_len = PAGE_SIZE;
 387        }
 388        args->vec[v].iov_len = len;
 389        args->vlen = v+1;
 390
 391        return args->count == args->len && args->vec[0].iov_len > 0;
 392}
 393
 394int
 395nfs3svc_decode_createargs(struct svc_rqst *rqstp, u32 *p,
 396                                        struct nfsd3_createargs *args)
 397{
 398        if (!(p = decode_fh(p, &args->fh))
 399         || !(p = decode_filename(p, &args->name, &args->len)))
 400                return 0;
 401
 402        switch (args->createmode = ntohl(*p++)) {
 403        case NFS3_CREATE_UNCHECKED:
 404        case NFS3_CREATE_GUARDED:
 405                if (!(p = decode_sattr3(p, &args->attrs)))
 406                        return 0;
 407                break;
 408        case NFS3_CREATE_EXCLUSIVE:
 409                args->verf = p;
 410                p += 2;
 411                break;
 412        default:
 413                return 0;
 414        }
 415
 416        return xdr_argsize_check(rqstp, p);
 417}
 418int
 419nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, u32 *p,
 420                                        struct nfsd3_createargs *args)
 421{
 422        if (!(p = decode_fh(p, &args->fh))
 423         || !(p = decode_filename(p, &args->name, &args->len))
 424         || !(p = decode_sattr3(p, &args->attrs)))
 425                return 0;
 426
 427        return xdr_argsize_check(rqstp, p);
 428}
 429
 430int
 431nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, u32 *p,
 432                                        struct nfsd3_symlinkargs *args)
 433{
 434        unsigned int len;
 435        int avail;
 436        char *old, *new;
 437        struct kvec *vec;
 438
 439        if (!(p = decode_fh(p, &args->ffh))
 440         || !(p = decode_filename(p, &args->fname, &args->flen))
 441         || !(p = decode_sattr3(p, &args->attrs))
 442                )
 443                return 0;
 444        /* now decode the pathname, which might be larger than the first page.
 445         * As we have to check for nul's anyway, we copy it into a new page
 446         * This page appears in the rq_res.pages list, but as pages_len is always
 447         * 0, it won't get in the way
 448         */
 449        svc_take_page(rqstp);
 450        len = ntohl(*p++);
 451        if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE)
 452                return 0;
 453        args->tname = new = page_address(rqstp->rq_respages[rqstp->rq_resused-1]);
 454        args->tlen = len;
 455        /* first copy and check from the first page */
 456        old = (char*)p;
 457        vec = &rqstp->rq_arg.head[0];
 458        avail = vec->iov_len - (old - (char*)vec->iov_base);
 459        while (len && avail && *old) {
 460                *new++ = *old++;
 461                len--;
 462                avail--;
 463        }
 464        /* now copy next page if there is one */
 465        if (len && !avail && rqstp->rq_arg.page_len) {
 466                avail = rqstp->rq_arg.page_len;
 467                if (avail > PAGE_SIZE) avail = PAGE_SIZE;
 468                old = page_address(rqstp->rq_arg.pages[0]);
 469        }
 470        while (len && avail && *old) {
 471                *new++ = *old++;
 472                len--;
 473                avail--;
 474        }
 475        *new = '\0';
 476        if (len)
 477                return 0;
 478
 479        return 1;
 480}
 481
 482int
 483nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, u32 *p,
 484                                        struct nfsd3_mknodargs *args)
 485{
 486        if (!(p = decode_fh(p, &args->fh))
 487         || !(p = decode_filename(p, &args->name, &args->len)))
 488                return 0;
 489
 490        args->ftype = ntohl(*p++);
 491
 492        if (args->ftype == NF3BLK  || args->ftype == NF3CHR
 493         || args->ftype == NF3SOCK || args->ftype == NF3FIFO) {
 494                if (!(p = decode_sattr3(p, &args->attrs)))
 495                        return 0;
 496        }
 497
 498        if (args->ftype == NF3BLK || args->ftype == NF3CHR) {
 499                args->major = ntohl(*p++);
 500                args->minor = ntohl(*p++);
 501        }
 502
 503        return xdr_argsize_check(rqstp, p);
 504}
 505
 506int
 507nfs3svc_decode_renameargs(struct svc_rqst *rqstp, u32 *p,
 508                                        struct nfsd3_renameargs *args)
 509{
 510        if (!(p = decode_fh(p, &args->ffh))
 511         || !(p = decode_filename(p, &args->fname, &args->flen))
 512         || !(p = decode_fh(p, &args->tfh))
 513         || !(p = decode_filename(p, &args->tname, &args->tlen)))
 514                return 0;
 515
 516        return xdr_argsize_check(rqstp, p);
 517}
 518
 519int
 520nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, u32 *p,
 521                                        struct nfsd3_readlinkargs *args)
 522{
 523        if (!(p = decode_fh(p, &args->fh)))
 524                return 0;
 525        svc_take_page(rqstp);
 526        args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]);
 527
 528        return xdr_argsize_check(rqstp, p);
 529}
 530
 531int
 532nfs3svc_decode_linkargs(struct svc_rqst *rqstp, u32 *p,
 533                                        struct nfsd3_linkargs *args)
 534{
 535        if (!(p = decode_fh(p, &args->ffh))
 536         || !(p = decode_fh(p, &args->tfh))
 537         || !(p = decode_filename(p, &args->tname, &args->tlen)))
 538                return 0;
 539
 540        return xdr_argsize_check(rqstp, p);
 541}
 542
 543int
 544nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, u32 *p,
 545                                        struct nfsd3_readdirargs *args)
 546{
 547        if (!(p = decode_fh(p, &args->fh)))
 548                return 0;
 549        p = xdr_decode_hyper(p, &args->cookie);
 550        args->verf   = p; p += 2;
 551        args->dircount = ~0;
 552        args->count  = ntohl(*p++);
 553
 554        if (args->count > PAGE_SIZE)
 555                args->count = PAGE_SIZE;
 556
 557        svc_take_page(rqstp);
 558        args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused-1]);
 559
 560        return xdr_argsize_check(rqstp, p);
 561}
 562
 563int
 564nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, u32 *p,
 565                                        struct nfsd3_readdirargs *args)
 566{
 567        int len, pn;
 568
 569        if (!(p = decode_fh(p, &args->fh)))
 570                return 0;
 571        p = xdr_decode_hyper(p, &args->cookie);
 572        args->verf     = p; p += 2;
 573        args->dircount = ntohl(*p++);
 574        args->count    = ntohl(*p++);
 575
 576        len = (args->count > NFSSVC_MAXBLKSIZE) ? NFSSVC_MAXBLKSIZE :
 577                                                  args->count;
 578        args->count = len;
 579
 580        while (len > 0) {
 581                pn = rqstp->rq_resused;
 582                svc_take_page(rqstp);
 583                if (!args->buffer)
 584                        args->buffer = page_address(rqstp->rq_respages[pn]);
 585                len -= PAGE_SIZE;
 586        }
 587
 588        return xdr_argsize_check(rqstp, p);
 589}
 590
 591int
 592nfs3svc_decode_commitargs(struct svc_rqst *rqstp, u32 *p,
 593                                        struct nfsd3_commitargs *args)
 594{
 595        if (!(p = decode_fh(p, &args->fh)))
 596                return 0;
 597        p = xdr_decode_hyper(p, &args->offset);
 598        args->count = ntohl(*p++);
 599
 600        return xdr_argsize_check(rqstp, p);
 601}
 602
 603/*
 604 * XDR encode functions
 605 */
 606/*
 607 * There must be an encoding function for void results so svc_process
 608 * will work properly.
 609 */
 610int
 611nfs3svc_encode_voidres(struct svc_rqst *rqstp, u32 *p, void *dummy)
 612{
 613        return xdr_ressize_check(rqstp, p);
 614}
 615
 616/* GETATTR */
 617int
 618nfs3svc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
 619                                        struct nfsd3_attrstat *resp)
 620{
 621        if (resp->status == 0)
 622                p = encode_fattr3(rqstp, p, &resp->fh, &resp->stat);
 623        return xdr_ressize_check(rqstp, p);
 624}
 625
 626/* SETATTR, REMOVE, RMDIR */
 627int
 628nfs3svc_encode_wccstat(struct svc_rqst *rqstp, u32 *p,
 629                                        struct nfsd3_attrstat *resp)
 630{
 631        p = encode_wcc_data(rqstp, p, &resp->fh);
 632        return xdr_ressize_check(rqstp, p);
 633}
 634
 635/* LOOKUP */
 636int
 637nfs3svc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
 638                                        struct nfsd3_diropres *resp)
 639{
 640        if (resp->status == 0) {
 641                p = encode_fh(p, &resp->fh);
 642                p = encode_post_op_attr(rqstp, p, &resp->fh);
 643        }
 644        p = encode_post_op_attr(rqstp, p, &resp->dirfh);
 645        return xdr_ressize_check(rqstp, p);
 646}
 647
 648/* ACCESS */
 649int
 650nfs3svc_encode_accessres(struct svc_rqst *rqstp, u32 *p,
 651                                        struct nfsd3_accessres *resp)
 652{
 653        p = encode_post_op_attr(rqstp, p, &resp->fh);
 654        if (resp->status == 0)
 655                *p++ = htonl(resp->access);
 656        return xdr_ressize_check(rqstp, p);
 657}
 658
 659/* READLINK */
 660int
 661nfs3svc_encode_readlinkres(struct svc_rqst *rqstp, u32 *p,
 662                                        struct nfsd3_readlinkres *resp)
 663{
 664        p = encode_post_op_attr(rqstp, p, &resp->fh);
 665        if (resp->status == 0) {
 666                *p++ = htonl(resp->len);
 667                xdr_ressize_check(rqstp, p);
 668                rqstp->rq_res.page_len = resp->len;
 669                if (resp->len & 3) {
 670                        /* need to pad the tail */
 671                        rqstp->rq_restailpage = 0;
 672                        rqstp->rq_res.tail[0].iov_base = p;
 673                        *p = 0;
 674                        rqstp->rq_res.tail[0].iov_len = 4 - (resp->len&3);
 675                }
 676                return 1;
 677        } else
 678                return xdr_ressize_check(rqstp, p);
 679}
 680
 681/* READ */
 682int
 683nfs3svc_encode_readres(struct svc_rqst *rqstp, u32 *p,
 684                                        struct nfsd3_readres *resp)
 685{
 686        p = encode_post_op_attr(rqstp, p, &resp->fh);
 687        if (resp->status == 0) {
 688                *p++ = htonl(resp->count);
 689                *p++ = htonl(resp->eof);
 690                *p++ = htonl(resp->count);      /* xdr opaque count */
 691                xdr_ressize_check(rqstp, p);
 692                /* now update rqstp->rq_res to reflect data aswell */
 693                rqstp->rq_res.page_len = resp->count;
 694                if (resp->count & 3) {
 695                        /* need to pad the tail */
 696                        rqstp->rq_restailpage = 0;
 697                        rqstp->rq_res.tail[0].iov_base = p;
 698                        *p = 0;
 699                        rqstp->rq_res.tail[0].iov_len = 4 - (resp->count & 3);
 700                }
 701                return 1;
 702        } else
 703                return xdr_ressize_check(rqstp, p);
 704}
 705
 706/* WRITE */
 707int
 708nfs3svc_encode_writeres(struct svc_rqst *rqstp, u32 *p,
 709                                        struct nfsd3_writeres *resp)
 710{
 711        p = encode_wcc_data(rqstp, p, &resp->fh);
 712        if (resp->status == 0) {
 713                *p++ = htonl(resp->count);
 714                *p++ = htonl(resp->committed);
 715                *p++ = htonl(nfssvc_boot.tv_sec);
 716                *p++ = htonl(nfssvc_boot.tv_usec);
 717        }
 718        return xdr_ressize_check(rqstp, p);
 719}
 720
 721/* CREATE, MKDIR, SYMLINK, MKNOD */
 722int
 723nfs3svc_encode_createres(struct svc_rqst *rqstp, u32 *p,
 724                                        struct nfsd3_diropres *resp)
 725{
 726        if (resp->status == 0) {
 727                *p++ = xdr_one;
 728                p = encode_fh(p, &resp->fh);
 729                p = encode_post_op_attr(rqstp, p, &resp->fh);
 730        }
 731        p = encode_wcc_data(rqstp, p, &resp->dirfh);
 732        return xdr_ressize_check(rqstp, p);
 733}
 734
 735/* RENAME */
 736int
 737nfs3svc_encode_renameres(struct svc_rqst *rqstp, u32 *p,
 738                                        struct nfsd3_renameres *resp)
 739{
 740        p = encode_wcc_data(rqstp, p, &resp->ffh);
 741        p = encode_wcc_data(rqstp, p, &resp->tfh);
 742        return xdr_ressize_check(rqstp, p);
 743}
 744
 745/* LINK */
 746int
 747nfs3svc_encode_linkres(struct svc_rqst *rqstp, u32 *p,
 748                                        struct nfsd3_linkres *resp)
 749{
 750        p = encode_post_op_attr(rqstp, p, &resp->fh);
 751        p = encode_wcc_data(rqstp, p, &resp->tfh);
 752        return xdr_ressize_check(rqstp, p);
 753}
 754
 755/* READDIR */
 756int
 757nfs3svc_encode_readdirres(struct svc_rqst *rqstp, u32 *p,
 758                                        struct nfsd3_readdirres *resp)
 759{
 760        p = encode_post_op_attr(rqstp, p, &resp->fh);
 761
 762        if (resp->status == 0) {
 763                /* stupid readdir cookie */
 764                memcpy(p, resp->verf, 8); p += 2;
 765                xdr_ressize_check(rqstp, p);
 766                if (rqstp->rq_res.head[0].iov_len + (2<<2) > PAGE_SIZE)
 767                        return 1; /*No room for trailer */
 768                rqstp->rq_res.page_len = (resp->count) << 2;
 769
 770                /* add the 'tail' to the end of the 'head' page - page 0. */
 771                rqstp->rq_restailpage = 0;
 772                rqstp->rq_res.tail[0].iov_base = p;
 773                *p++ = 0;               /* no more entries */
 774                *p++ = htonl(resp->common.err == nfserr_eof);
 775                rqstp->rq_res.tail[0].iov_len = 2<<2;
 776                return 1;
 777        } else
 778                return xdr_ressize_check(rqstp, p);
 779}
 780
 781static inline u32 *
 782encode_entry_baggage(struct nfsd3_readdirres *cd, u32 *p, const char *name,
 783             int namlen, ino_t ino)
 784{
 785        *p++ = xdr_one;                          /* mark entry present */
 786        p    = xdr_encode_hyper(p, ino);         /* file id */
 787        p    = xdr_encode_array(p, name, namlen);/* name length & name */
 788
 789        cd->offset = p;                         /* remember pointer */
 790        p = xdr_encode_hyper(p, NFS_OFFSET_MAX);/* offset of next entry */
 791
 792        return p;
 793}
 794
 795static inline u32 *
 796encode_entryplus_baggage(struct nfsd3_readdirres *cd, u32 *p,
 797                struct svc_fh *fhp)
 798{
 799                p = encode_post_op_attr(cd->rqstp, p, fhp);
 800                *p++ = xdr_one;                 /* yes, a file handle follows */
 801                p = encode_fh(p, fhp);
 802                fh_put(fhp);
 803                return p;
 804}
 805
 806static int
 807compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
 808                const char *name, int namlen)
 809{
 810        struct svc_export       *exp;
 811        struct dentry           *dparent, *dchild;
 812        int rv = 0;
 813
 814        dparent = cd->fh.fh_dentry;
 815        exp  = cd->fh.fh_export;
 816
 817        fh_init(fhp, NFS3_FHSIZE);
 818        if (isdotent(name, namlen)) {
 819                if (namlen == 2) {
 820                        dchild = dget_parent(dparent);
 821                        if (dchild == dparent) {
 822                                /* filesystem root - cannot return filehandle for ".." */
 823                                dput(dchild);
 824                                return 1;
 825                        }
 826                } else
 827                        dchild = dget(dparent);
 828        } else
 829                dchild = lookup_one_len(name, dparent, namlen);
 830        if (IS_ERR(dchild))
 831                return 1;
 832        if (d_mountpoint(dchild) ||
 833            fh_compose(fhp, exp, dchild, &cd->fh) != 0 ||
 834            !dchild->d_inode)
 835                rv = 1;
 836        dput(dchild);
 837        return rv;
 838}
 839
 840/*
 841 * Encode a directory entry. This one works for both normal readdir
 842 * and readdirplus.
 843 * The normal readdir reply requires 2 (fileid) + 1 (stringlen)
 844 * + string + 2 (cookie) + 1 (next) words, i.e. 6 + strlen.
 845 * 
 846 * The readdirplus baggage is 1+21 words for post_op_attr, plus the
 847 * file handle.
 848 */
 849
 850#define NFS3_ENTRY_BAGGAGE      (2 + 1 + 2 + 1)
 851#define NFS3_ENTRYPLUS_BAGGAGE  (1 + 21 + 1 + (NFS3_FHSIZE >> 2))
 852static int
 853encode_entry(struct readdir_cd *ccd, const char *name,
 854             int namlen, off_t offset, ino_t ino, unsigned int d_type, int plus)
 855{
 856        struct nfsd3_readdirres *cd = container_of(ccd, struct nfsd3_readdirres,
 857                                                        common);
 858        u32             *p = cd->buffer;
 859        caddr_t         curr_page_addr = NULL;
 860        int             pn;             /* current page number */
 861        int             slen;           /* string (name) length */
 862        int             elen;           /* estimated entry length in words */
 863        int             num_entry_words = 0;    /* actual number of words */
 864
 865        if (cd->offset) {
 866                u64 offset64 = offset;
 867
 868                if (unlikely(cd->offset1)) {
 869                        /* we ended up with offset on a page boundary */
 870                        *cd->offset = htonl(offset64 >> 32);
 871                        *cd->offset1 = htonl(offset64 & 0xffffffff);
 872                        cd->offset1 = NULL;
 873                } else {
 874                        xdr_encode_hyper(cd->offset, (u64) offset);
 875                }
 876        }
 877
 878        /*
 879        dprintk("encode_entry(%.*s @%ld%s)\n",
 880                namlen, name, (long) offset, plus? " plus" : "");
 881         */
 882
 883        /* truncate filename if too long */
 884        if (namlen > NFS3_MAXNAMLEN)
 885                namlen = NFS3_MAXNAMLEN;
 886
 887        slen = XDR_QUADLEN(namlen);
 888        elen = slen + NFS3_ENTRY_BAGGAGE
 889                + (plus? NFS3_ENTRYPLUS_BAGGAGE : 0);
 890
 891        if (cd->buflen < elen) {
 892                cd->common.err = nfserr_toosmall;
 893                return -EINVAL;
 894        }
 895
 896        /* determine which page in rq_respages[] we are currently filling */
 897        for (pn=1; pn < cd->rqstp->rq_resused; pn++) {
 898                curr_page_addr = page_address(cd->rqstp->rq_respages[pn]);
 899
 900                if (((caddr_t)cd->buffer >= curr_page_addr) &&
 901                    ((caddr_t)cd->buffer <  curr_page_addr + PAGE_SIZE))
 902                        break;
 903        }
 904
 905        if ((caddr_t)(cd->buffer + elen) < (curr_page_addr + PAGE_SIZE)) {
 906                /* encode entry in current page */
 907
 908                p = encode_entry_baggage(cd, p, name, namlen, ino);
 909
 910                /* throw in readdirplus baggage */
 911                if (plus) {
 912                        struct svc_fh   fh;
 913
 914                        if (compose_entry_fh(cd, &fh, name, namlen) > 0) {
 915                                *p++ = 0;
 916                                *p++ = 0;
 917                        } else
 918                                p = encode_entryplus_baggage(cd, p, &fh);
 919                }
 920                num_entry_words = p - cd->buffer;
 921        } else if (cd->rqstp->rq_respages[pn+1] != NULL) {
 922                /* temporarily encode entry into next page, then move back to
 923                 * current and next page in rq_respages[] */
 924                u32 *p1, *tmp;
 925                int len1, len2;
 926
 927                /* grab next page for temporary storage of entry */
 928                p1 = tmp = page_address(cd->rqstp->rq_respages[pn+1]);
 929
 930                p1 = encode_entry_baggage(cd, p1, name, namlen, ino);
 931
 932                /* throw in readdirplus baggage */
 933                if (plus) {
 934                        struct svc_fh   fh;
 935
 936                        if (compose_entry_fh(cd, &fh, name, namlen) > 0) {
 937                                /* zero out the filehandle */
 938                                *p1++ = 0;
 939                                *p1++ = 0;
 940                        } else
 941                                p1 = encode_entryplus_baggage(cd, p1, &fh);
 942                }
 943
 944                /* determine entry word length and lengths to go in pages */
 945                num_entry_words = p1 - tmp;
 946                len1 = curr_page_addr + PAGE_SIZE - (caddr_t)cd->buffer;
 947                if ((num_entry_words << 2) < len1) {
 948                        /* the actual number of words in the entry is less
 949                         * than elen and can still fit in the current page
 950                         */
 951                        memmove(p, tmp, num_entry_words << 2);
 952                        p += num_entry_words;
 953
 954                        /* update offset */
 955                        cd->offset = cd->buffer + (cd->offset - tmp);
 956                } else {
 957                        unsigned int offset_r = (cd->offset - tmp) << 2;
 958
 959                        /* update pointer to offset location.
 960                         * This is a 64bit quantity, so we need to
 961                         * deal with 3 cases:
 962                         *  -   entirely in first page
 963                         *  -   entirely in second page
 964                         *  -   4 bytes in each page
 965                         */
 966                        if (offset_r + 8 <= len1) {
 967                                cd->offset = p + (cd->offset - tmp);
 968                        } else if (offset_r >= len1) {
 969                                cd->offset -= len1 >> 2;
 970                        } else {
 971                                /* sitting on the fence */
 972                                BUG_ON(offset_r != len1 - 4);
 973                                cd->offset = p + (cd->offset - tmp);
 974                                cd->offset1 = tmp;
 975                        }
 976
 977                        len2 = (num_entry_words << 2) - len1;
 978
 979                        /* move from temp page to current and next pages */
 980                        memmove(p, tmp, len1);
 981                        memmove(tmp, (caddr_t)tmp+len1, len2);
 982
 983                        p = tmp + (len2 >> 2);
 984                }
 985        }
 986        else {
 987                cd->common.err = nfserr_toosmall;
 988                return -EINVAL;
 989        }
 990
 991        cd->buflen -= num_entry_words;
 992        cd->buffer = p;
 993        cd->common.err = nfs_ok;
 994        return 0;
 995
 996}
 997
 998int
 999nfs3svc_encode_entry(struct readdir_cd *cd, const char *name,
1000                     int namlen, loff_t offset, ino_t ino, unsigned int d_type)
1001{
1002        return encode_entry(cd, name, namlen, offset, ino, d_type, 0);
1003}
1004
1005int
1006nfs3svc_encode_entry_plus(struct readdir_cd *cd, const char *name,
1007                          int namlen, loff_t offset, ino_t ino, unsigned int d_type)
1008{
1009        return encode_entry(cd, name, namlen, offset, ino, d_type, 1);
1010}
1011
1012/* FSSTAT */
1013int
1014nfs3svc_encode_fsstatres(struct svc_rqst *rqstp, u32 *p,
1015                                        struct nfsd3_fsstatres *resp)
1016{
1017        struct kstatfs  *s = &resp->stats;
1018        u64             bs = s->f_bsize;
1019
1020        *p++ = xdr_zero;        /* no post_op_attr */
1021
1022        if (resp->status == 0) {
1023                p = xdr_encode_hyper(p, bs * s->f_blocks);      /* total bytes */
1024                p = xdr_encode_hyper(p, bs * s->f_bfree);       /* free bytes */
1025                p = xdr_encode_hyper(p, bs * s->f_bavail);      /* user available bytes */
1026                p = xdr_encode_hyper(p, s->f_files);    /* total inodes */
1027                p = xdr_encode_hyper(p, s->f_ffree);    /* free inodes */
1028                p = xdr_encode_hyper(p, s->f_ffree);    /* user available inodes */
1029                *p++ = htonl(resp->invarsec);   /* mean unchanged time */
1030        }
1031        return xdr_ressize_check(rqstp, p);
1032}
1033
1034/* FSINFO */
1035int
1036nfs3svc_encode_fsinfores(struct svc_rqst *rqstp, u32 *p,
1037                                        struct nfsd3_fsinfores *resp)
1038{
1039        *p++ = xdr_zero;        /* no post_op_attr */
1040
1041        if (resp->status == 0) {
1042                *p++ = htonl(resp->f_rtmax);
1043                *p++ = htonl(resp->f_rtpref);
1044                *p++ = htonl(resp->f_rtmult);
1045                *p++ = htonl(resp->f_wtmax);
1046                *p++ = htonl(resp->f_wtpref);
1047                *p++ = htonl(resp->f_wtmult);
1048                *p++ = htonl(resp->f_dtpref);
1049                p = xdr_encode_hyper(p, resp->f_maxfilesize);
1050                *p++ = xdr_one;
1051                *p++ = xdr_zero;
1052                *p++ = htonl(resp->f_properties);
1053        }
1054
1055        return xdr_ressize_check(rqstp, p);
1056}
1057
1058/* PATHCONF */
1059int
1060nfs3svc_encode_pathconfres(struct svc_rqst *rqstp, u32 *p,
1061                                        struct nfsd3_pathconfres *resp)
1062{
1063        *p++ = xdr_zero;        /* no post_op_attr */
1064
1065        if (resp->status == 0) {
1066                *p++ = htonl(resp->p_link_max);
1067                *p++ = htonl(resp->p_name_max);
1068                *p++ = htonl(resp->p_no_trunc);
1069                *p++ = htonl(resp->p_chown_restricted);
1070                *p++ = htonl(resp->p_case_insensitive);
1071                *p++ = htonl(resp->p_case_preserving);
1072        }
1073
1074        return xdr_ressize_check(rqstp, p);
1075}
1076
1077/* COMMIT */
1078int
1079nfs3svc_encode_commitres(struct svc_rqst *rqstp, u32 *p,
1080                                        struct nfsd3_commitres *resp)
1081{
1082        p = encode_wcc_data(rqstp, p, &resp->fh);
1083        /* Write verifier */
1084        if (resp->status == 0) {
1085                *p++ = htonl(nfssvc_boot.tv_sec);
1086                *p++ = htonl(nfssvc_boot.tv_usec);
1087        }
1088        return xdr_ressize_check(rqstp, p);
1089}
1090
1091/*
1092 * XDR release functions
1093 */
1094int
1095nfs3svc_release_fhandle(struct svc_rqst *rqstp, u32 *p,
1096                                        struct nfsd3_attrstat *resp)
1097{
1098        fh_put(&resp->fh);
1099        return 1;
1100}
1101
1102int
1103nfs3svc_release_fhandle2(struct svc_rqst *rqstp, u32 *p,
1104                                        struct nfsd3_fhandle_pair *resp)
1105{
1106        fh_put(&resp->fh1);
1107        fh_put(&resp->fh2);
1108        return 1;
1109}
1110
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.