nfsd: let nfsd_symlink assume null-terminated data
[firefly-linux-kernel-4.4.55.git] / fs / nfsd / nfs4proc.c
index a5b666ca560d7773e33080b7e78df0d14bcc0b57..7aa83bf34fa980a8bbdda261881afca2a64aaf9b 100644 (file)
@@ -177,7 +177,7 @@ fh_dup2(struct svc_fh *dst, struct svc_fh *src)
        fh_put(dst);
        dget(src->fh_dentry);
        if (src->fh_export)
-               cache_get(&src->fh_export->h);
+               exp_get(src->fh_export);
        *dst = *src;
 }
 
@@ -430,12 +430,12 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                                goto out;
                        break;
                case NFS4_OPEN_CLAIM_PREVIOUS:
-                       open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
                        status = nfs4_check_open_reclaim(&open->op_clientid,
                                                         cstate->minorversion,
                                                         nn);
                        if (status)
                                goto out;
+                       open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
                case NFS4_OPEN_CLAIM_FH:
                case NFS4_OPEN_CLAIM_DELEG_CUR_FH:
                        status = do_open_fhandle(rqstp, cstate, open);
@@ -445,7 +445,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                        break;
                case NFS4_OPEN_CLAIM_DELEG_PREV_FH:
                case NFS4_OPEN_CLAIM_DELEGATE_PREV:
-                       open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED;
                        dprintk("NFSD: unsupported OPEN claim type %d\n",
                                open->op_claim_type);
                        status = nfserr_notsupp;
@@ -582,8 +581,12 @@ static void gen_boot_verifier(nfs4_verifier *verifier, struct net *net)
        __be32 verf[2];
        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
-       verf[0] = (__be32)nn->nfssvc_boot.tv_sec;
-       verf[1] = (__be32)nn->nfssvc_boot.tv_usec;
+       /*
+        * This is opaque to client, so no need to byte-swap. Use
+        * __force to keep sparse happy
+        */
+       verf[0] = (__force __be32)nn->nfssvc_boot.tv_sec;
+       verf[1] = (__force __be32)nn->nfssvc_boot.tv_usec;
        memcpy(verifier->data, verf, sizeof(verifier->data));
 }
 
@@ -618,18 +621,9 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        switch (create->cr_type) {
        case NF4LNK:
-               /* ugh! we have to null-terminate the linktext, or
-                * vfs_symlink() will choke.  it is always safe to
-                * null-terminate by brute force, since at worst we
-                * will overwrite the first byte of the create namelen
-                * in the XDR buffer, which has already been extracted
-                * during XDR decode.
-                */
-               create->cr_linkname[create->cr_linklen] = 0;
-
                status = nfsd_symlink(rqstp, &cstate->current_fh,
                                      create->cr_name, create->cr_namelen,
-                                     create->cr_linkname, create->cr_linklen,
+                                     create->cr_linkname,
                                      &resfh, &create->cr_iattr);
                break;
 
@@ -919,8 +913,8 @@ nfsd4_secinfo_no_name(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstat
        default:
                return nfserr_inval;
        }
-       exp_get(cstate->current_fh.fh_export);
-       sin->sin_exp = cstate->current_fh.fh_export;
+
+       sin->sin_exp = exp_get(cstate->current_fh.fh_export);
        fh_put(&cstate->current_fh);
        return nfs_ok;
 }
@@ -1261,9 +1255,13 @@ static void svcxdr_init_encode(struct svc_rqst *rqstp,
        xdr->buf = buf;
        xdr->iov = head;
        xdr->p   = head->iov_base + head->iov_len;
-       xdr->end = head->iov_base + PAGE_SIZE - 2 * RPC_MAX_AUTH_SIZE;
+       xdr->end = head->iov_base + PAGE_SIZE - rqstp->rq_auth_slack;
        /* Tail and page_len should be zero at this point: */
        buf->len = buf->head[0].iov_len;
+       xdr->scratch.iov_len = 0;
+       xdr->page_ptr = buf->pages - 1;
+       buf->buflen = PAGE_SIZE * (1 + rqstp->rq_page_end - buf->pages)
+               - rqstp->rq_auth_slack;
 }
 
 /*
@@ -1279,7 +1277,6 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
        struct nfsd4_compound_state *cstate = &resp->cstate;
        struct svc_fh *current_fh = &cstate->current_fh;
        struct svc_fh *save_fh = &cstate->save_fh;
-       u32             plen = 0;
        __be32          status;
 
        svcxdr_init_encode(rqstp, resp);
@@ -1288,18 +1285,15 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
        xdr_reserve_space(&resp->xdr, 8 + args->taglen);
        resp->taglen = args->taglen;
        resp->tag = args->tag;
-       resp->opcnt = 0;
        resp->rqstp = rqstp;
        cstate->minorversion = args->minorversion;
-       cstate->replay_owner = NULL;
-       cstate->session = NULL;
        fh_init(current_fh, NFS4_FHSIZE);
        fh_init(save_fh, NFS4_FHSIZE);
        /*
         * Don't use the deferral mechanism for NFSv4; compounds make it
         * too hard to avoid non-idempotency problems.
         */
-       rqstp->rq_usedeferral = 0;
+       rqstp->rq_usedeferral = false;
 
        /*
         * According to RFC3010, this takes precedence over all other errors.
@@ -1349,9 +1343,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
 
                /* If op is non-idempotent */
                if (opdesc->op_flags & OP_MODIFIES_SOMETHING) {
-                       plen = opdesc->op_rsize_bop(rqstp, op);
                        /*
-                        * If there's still another operation, make sure
+                        * Don't execute this op if we couldn't encode a
+                        * succesful reply:
+                        */
+                       u32 plen = opdesc->op_rsize_bop(rqstp, op);
+                       /*
+                        * Plus if there's another operation, make sure
                         * we'll have space to at least encode an error:
                         */
                        if (resp->opcnt < args->opcnt)
@@ -1414,7 +1412,7 @@ encode_op:
        BUG_ON(cstate->replay_owner);
 out:
        /* Reset deferral mechanism for RPC deferrals */
-       rqstp->rq_usedeferral = 1;
+       rqstp->rq_usedeferral = true;
        dprintk("nfsv4 compound returned %d\n", ntohl(status));
        return status;
 }
@@ -1425,7 +1423,8 @@ out:
 #define op_encode_change_info_maxsz    (5)
 #define nfs4_fattr_bitmap_maxsz                (4)
 
-#define op_encode_lockowner_maxsz      (1 + XDR_QUADLEN(IDMAP_NAMESZ))
+/* We'll fall back on returning no lockowner if run out of space: */
+#define op_encode_lockowner_maxsz      (0)
 #define op_encode_lock_denied_maxsz    (8 + op_encode_lockowner_maxsz)
 
 #define nfs4_owner_maxsz               (1 + XDR_QUADLEN(IDMAP_NAMESZ))
@@ -1457,6 +1456,49 @@ static inline u32 nfsd4_create_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op
                + nfs4_fattr_bitmap_maxsz) * sizeof(__be32);
 }
 
+/*
+ * Note since this is an idempotent operation we won't insist on failing
+ * the op prematurely if the estimate is too large.  We may turn off splice
+ * reads unnecessarily.
+ */
+static inline u32 nfsd4_getattr_rsize(struct svc_rqst *rqstp,
+                                     struct nfsd4_op *op)
+{
+       u32 *bmap = op->u.getattr.ga_bmval;
+       u32 bmap0 = bmap[0], bmap1 = bmap[1], bmap2 = bmap[2];
+       u32 ret = 0;
+
+       if (bmap0 & FATTR4_WORD0_ACL)
+               return svc_max_payload(rqstp);
+       if (bmap0 & FATTR4_WORD0_FS_LOCATIONS)
+               return svc_max_payload(rqstp);
+
+       if (bmap1 & FATTR4_WORD1_OWNER) {
+               ret += IDMAP_NAMESZ + 4;
+               bmap1 &= ~FATTR4_WORD1_OWNER;
+       }
+       if (bmap1 & FATTR4_WORD1_OWNER_GROUP) {
+               ret += IDMAP_NAMESZ + 4;
+               bmap1 &= ~FATTR4_WORD1_OWNER_GROUP;
+       }
+       if (bmap0 & FATTR4_WORD0_FILEHANDLE) {
+               ret += NFS4_FHSIZE + 4;
+               bmap0 &= ~FATTR4_WORD0_FILEHANDLE;
+       }
+       if (bmap2 & FATTR4_WORD2_SECURITY_LABEL) {
+               ret += NFSD4_MAX_SEC_LABEL_LEN + 12;
+               bmap2 &= ~FATTR4_WORD2_SECURITY_LABEL;
+       }
+       /*
+        * Largest of remaining attributes are 16 bytes (e.g.,
+        * supported_attributes)
+        */
+       ret += 16 * (hweight32(bmap0) + hweight32(bmap1) + hweight32(bmap2));
+       /* bitmask, length */
+       ret += 20;
+       return ret;
+}
+
 static inline u32 nfsd4_link_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
 {
        return (op_encode_hdr_size + op_encode_change_info_maxsz)
@@ -1482,23 +1524,20 @@ static inline u32 nfsd4_read_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
        u32 maxcount = 0, rlen = 0;
 
        maxcount = svc_max_payload(rqstp);
-       rlen = op->u.read.rd_length;
-
-       if (rlen > maxcount)
-               rlen = maxcount;
+       rlen = min(op->u.read.rd_length, maxcount);
 
        return (op_encode_hdr_size + 2 + XDR_QUADLEN(rlen)) * sizeof(__be32);
 }
 
 static inline u32 nfsd4_readdir_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
 {
-       u32 rlen = op->u.readdir.rd_maxcount;
+       u32 maxcount = 0, rlen = 0;
 
-       if (rlen > PAGE_SIZE)
-               rlen = PAGE_SIZE;
+       maxcount = svc_max_payload(rqstp);
+       rlen = min(op->u.readdir.rd_maxcount, maxcount);
 
-       return (op_encode_hdr_size + op_encode_verifier_maxsz)
-                * sizeof(__be32) + rlen;
+       return (op_encode_hdr_size + op_encode_verifier_maxsz +
+               XDR_QUADLEN(rlen)) * sizeof(__be32);
 }
 
 static inline u32 nfsd4_remove_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
@@ -1513,6 +1552,12 @@ static inline u32 nfsd4_rename_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op
                + op_encode_change_info_maxsz) * sizeof(__be32);
 }
 
+static inline u32 nfsd4_sequence_rsize(struct svc_rqst *rqstp,
+                                      struct nfsd4_op *op)
+{
+       return NFS4_MAX_SESSIONID_LEN + 20;
+}
+
 static inline u32 nfsd4_setattr_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
 {
        return (op_encode_hdr_size + nfs4_fattr_bitmap_maxsz) * sizeof(__be32);
@@ -1594,6 +1639,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
        [OP_GETATTR] = {
                .op_func = (nfsd4op_func)nfsd4_getattr,
                .op_flags = ALLOWED_ON_ABSENT_FS,
+               .op_rsize_bop = nfsd4_getattr_rsize,
                .op_name = "OP_GETATTR",
        },
        [OP_GETFH] = {
@@ -1846,6 +1892,18 @@ static struct nfsd4_operation nfsd4_ops[] = {
        },
 };
 
+int nfsd4_max_reply(struct svc_rqst *rqstp, struct nfsd4_op *op)
+{
+       struct nfsd4_operation *opdesc;
+       nfsd4op_rsize estimator;
+
+       if (op->opnum == OP_ILLEGAL)
+               return op_encode_hdr_size * sizeof(__be32);
+       opdesc = OPDESC(op);
+       estimator = opdesc->op_rsize_bop;
+       return estimator ? estimator(rqstp, op) : PAGE_SIZE;
+}
+
 void warn_on_nonidempotent_op(struct nfsd4_op *op)
 {
        if (OPDESC(op)->op_flags & OP_MODIFIES_SOMETHING) {