nfsd41: try to check reply size before operation
[firefly-linux-kernel-4.4.55.git] / fs / nfsd / nfs4xdr.c
index c8bf405d19de53dcf521a5cac313b4fbc3da6f24..f4116cf1659548ea6e857c68d9460ba61e601524 100644 (file)
@@ -456,7 +456,6 @@ nfsd4_decode_close(struct nfsd4_compoundargs *argp, struct nfsd4_close *close)
 {
        DECODE_HEAD;
 
-       close->cl_stateowner = NULL;
        READ_BUF(4);
        READ32(close->cl_seqid);
        return nfsd4_decode_stateid(argp, &close->cl_stateid);
@@ -551,7 +550,6 @@ nfsd4_decode_lock(struct nfsd4_compoundargs *argp, struct nfsd4_lock *lock)
 {
        DECODE_HEAD;
 
-       lock->lk_replay_owner = NULL;
        /*
        * type, reclaim(boolean), offset, length, new_lock_owner(boolean)
        */
@@ -611,7 +609,6 @@ nfsd4_decode_locku(struct nfsd4_compoundargs *argp, struct nfsd4_locku *locku)
 {
        DECODE_HEAD;
 
-       locku->lu_stateowner = NULL;
        READ_BUF(8);
        READ32(locku->lu_type);
        if ((locku->lu_type < NFS4_READ_LT) || (locku->lu_type > NFS4_WRITEW_LT))
@@ -649,7 +646,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
 
        memset(open->op_bmval, 0, sizeof(open->op_bmval));
        open->op_iattr.ia_valid = 0;
-       open->op_stateowner = NULL;
+       open->op_openowner = NULL;
 
        /* seqid, share_access, share_deny, clientid, ownerlen */
        READ_BUF(16 + sizeof(clientid_t));
@@ -739,7 +736,6 @@ nfsd4_decode_open_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_open_con
 {
        DECODE_HEAD;
                    
-       open_conf->oc_stateowner = NULL;
        status = nfsd4_decode_stateid(argp, &open_conf->oc_req_stateid);
        if (status)
                return status;
@@ -754,7 +750,6 @@ nfsd4_decode_open_downgrade(struct nfsd4_compoundargs *argp, struct nfsd4_open_d
 {
        DECODE_HEAD;
                    
-       open_down->od_stateowner = NULL;
        status = nfsd4_decode_stateid(argp, &open_down->od_stateid);
        if (status)
                return status;
@@ -1630,15 +1625,19 @@ static void write_cinfo(__be32 **p, struct nfsd4_change_info *c)
  * we know whether the error to be returned is a sequence id mutating error.
  */
 
-#define ENCODE_SEQID_OP_TAIL(stateowner) do {                  \
-       if (seqid_mutating_err(nfserr) && stateowner) {         \
-               stateowner->so_seqid++;                         \
-               stateowner->so_replay.rp_status = nfserr;       \
-               stateowner->so_replay.rp_buflen =               \
-                         (((char *)(resp)->p - (char *)save)); \
-               memcpy(stateowner->so_replay.rp_buf, save,      \
-                       stateowner->so_replay.rp_buflen);       \
-       } } while (0);
+static void encode_seqid_op_tail(struct nfsd4_compoundres *resp, __be32 *save, __be32 nfserr)
+{
+       struct nfs4_stateowner *stateowner = resp->cstate.replay_owner;
+
+       if (seqid_mutating_err(ntohl(nfserr)) && stateowner) {
+               stateowner->so_seqid++;
+               stateowner->so_replay.rp_status = nfserr;
+               stateowner->so_replay.rp_buflen =
+                         (char *)resp->p - (char *)save;
+               memcpy(stateowner->so_replay.rp_buf, save,
+                       stateowner->so_replay.rp_buflen);
+       }
+}
 
 /* Encode as an array of strings the string given with components
  * separated @sep.
@@ -1697,36 +1696,89 @@ static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location,
 }
 
 /*
- * Return the path to an export point in the pseudo filesystem namespace
- * Returned string is safe to use as long as the caller holds a reference
- * to @exp.
+ * Encode a path in RFC3530 'pathname4' format
  */
-static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 *stat)
+static __be32 nfsd4_encode_path(const struct path *root,
+               const struct path *path, __be32 **pp, int *buflen)
 {
-       struct svc_fh tmp_fh;
-       char *path = NULL, *rootpath;
-       size_t rootlen;
+       struct path cur = {
+               .mnt = path->mnt,
+               .dentry = path->dentry,
+       };
+       __be32 *p = *pp;
+       struct dentry **components = NULL;
+       unsigned int ncomponents = 0;
+       __be32 err = nfserr_jukebox;
 
-       fh_init(&tmp_fh, NFS4_FHSIZE);
-       *stat = exp_pseudoroot(rqstp, &tmp_fh);
-       if (*stat)
-               return NULL;
-       rootpath = tmp_fh.fh_export->ex_pathname;
+       dprintk("nfsd4_encode_components(");
+
+       path_get(&cur);
+       /* First walk the path up to the nfsd root, and store the
+        * dentries/path components in an array.
+        */
+       for (;;) {
+               if (cur.dentry == root->dentry && cur.mnt == root->mnt)
+                       break;
+               if (cur.dentry == cur.mnt->mnt_root) {
+                       if (follow_up(&cur))
+                               continue;
+                       goto out_free;
+               }
+               if ((ncomponents & 15) == 0) {
+                       struct dentry **new;
+                       new = krealloc(components,
+                                       sizeof(*new) * (ncomponents + 16),
+                                       GFP_KERNEL);
+                       if (!new)
+                               goto out_free;
+                       components = new;
+               }
+               components[ncomponents++] = cur.dentry;
+               cur.dentry = dget_parent(cur.dentry);
+       }
 
-       path = exp->ex_pathname;
+       *buflen -= 4;
+       if (*buflen < 0)
+               goto out_free;
+       WRITE32(ncomponents);
 
-       rootlen = strlen(rootpath);
-       if (strncmp(path, rootpath, rootlen)) {
-               dprintk("nfsd: fs_locations failed;"
-                       "%s is not contained in %s\n", path, rootpath);
-               *stat = nfserr_notsupp;
-               path = NULL;
-               goto out;
+       while (ncomponents) {
+               struct dentry *dentry = components[ncomponents - 1];
+               unsigned int len = dentry->d_name.len;
+
+               *buflen -= 4 + (XDR_QUADLEN(len) << 2);
+               if (*buflen < 0)
+                       goto out_free;
+               WRITE32(len);
+               WRITEMEM(dentry->d_name.name, len);
+               dprintk("/%s", dentry->d_name.name);
+               dput(dentry);
+               ncomponents--;
        }
-       path += rootlen;
-out:
-       fh_put(&tmp_fh);
-       return path;
+
+       *pp = p;
+       err = 0;
+out_free:
+       dprintk(")\n");
+       while (ncomponents)
+               dput(components[--ncomponents]);
+       kfree(components);
+       path_put(&cur);
+       return err;
+}
+
+static __be32 nfsd4_encode_fsloc_fsroot(struct svc_rqst *rqstp,
+               const struct path *path, __be32 **pp, int *buflen)
+{
+       struct svc_export *exp_ps;
+       __be32 res;
+
+       exp_ps = rqst_find_fsidzero_export(rqstp);
+       if (IS_ERR(exp_ps))
+               return nfserrno(PTR_ERR(exp_ps));
+       res = nfsd4_encode_path(&exp_ps->ex_path, path, pp, buflen);
+       exp_put(exp_ps);
+       return res;
 }
 
 /*
@@ -1740,11 +1792,8 @@ static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp,
        int i;
        __be32 *p = *pp;
        struct nfsd4_fs_locations *fslocs = &exp->ex_fslocs;
-       char *root = nfsd4_path(rqstp, exp, &status);
 
-       if (status)
-               return status;
-       status = nfsd4_encode_components('/', root, &p, buflen);
+       status = nfsd4_encode_fsloc_fsroot(rqstp, &exp->ex_path, &p, buflen);
        if (status)
                return status;
        if ((*buflen -= 4) < 0)
@@ -1760,12 +1809,19 @@ static __be32 nfsd4_encode_fs_locations(struct svc_rqst *rqstp,
        return 0;
 }
 
-static u32 nfs4_ftypes[16] = {
-        NF4BAD,  NF4FIFO, NF4CHR, NF4BAD,
-        NF4DIR,  NF4BAD,  NF4BLK, NF4BAD,
-        NF4REG,  NF4BAD,  NF4LNK, NF4BAD,
-        NF4SOCK, NF4BAD,  NF4LNK, NF4BAD,
-};
+static u32 nfs4_file_type(umode_t mode)
+{
+       switch (mode & S_IFMT) {
+       case S_IFIFO:   return NF4FIFO;
+       case S_IFCHR:   return NF4CHR;
+       case S_IFDIR:   return NF4DIR;
+       case S_IFBLK:   return NF4BLK;
+       case S_IFLNK:   return NF4LNK;
+       case S_IFREG:   return NF4REG;
+       case S_IFSOCK:  return NF4SOCK;
+       default:        return NF4BAD;
+       };
+}
 
 static __be32
 nfsd4_encode_name(struct svc_rqst *rqstp, int whotype, uid_t id, int group,
@@ -1954,7 +2010,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        if (bmval0 & FATTR4_WORD0_TYPE) {
                if ((buflen -= 4) < 0)
                        goto out_resource;
-               dummy = nfs4_ftypes[(stat.mode & S_IFMT) >> 12];
+               dummy = nfs4_file_type(stat.mode);
                if (dummy == NF4BAD)
                        goto out_serverfault;
                WRITE32(dummy);
@@ -2488,7 +2544,7 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_c
        if (!nfserr)
                nfsd4_encode_stateid(resp, &close->cl_stateid);
 
-       ENCODE_SEQID_OP_TAIL(close->cl_stateowner);
+       encode_seqid_op_tail(resp, save, nfserr);
        return nfserr;
 }
 
@@ -2564,17 +2620,18 @@ nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh
 static void
 nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld)
 {
+       struct xdr_netobj *conf = &ld->ld_owner;
        __be32 *p;
 
-       RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0));
+       RESERVE_SPACE(32 + XDR_LEN(conf->len));
        WRITE64(ld->ld_start);
        WRITE64(ld->ld_length);
        WRITE32(ld->ld_type);
-       if (ld->ld_sop) {
+       if (conf->len) {
                WRITEMEM(&ld->ld_clientid, 8);
-               WRITE32(ld->ld_sop->so_owner.len);
-               WRITEMEM(ld->ld_sop->so_owner.data, ld->ld_sop->so_owner.len);
-               kref_put(&ld->ld_sop->so_ref, nfs4_free_stateowner);
+               WRITE32(conf->len);
+               WRITEMEM(conf->data, conf->len);
+               kfree(conf->data);
        }  else {  /* non - nfsv4 lock in conflict, no clientid nor owner */
                WRITE64((u64)0); /* clientid */
                WRITE32(0); /* length of owner name */
@@ -2592,7 +2649,7 @@ nfsd4_encode_lock(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_lo
        else if (nfserr == nfserr_denied)
                nfsd4_encode_lock_denied(resp, &lock->lk_denied);
 
-       ENCODE_SEQID_OP_TAIL(lock->lk_replay_owner);
+       encode_seqid_op_tail(resp, save, nfserr);
        return nfserr;
 }
 
@@ -2612,7 +2669,7 @@ nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_l
        if (!nfserr)
                nfsd4_encode_stateid(resp, &locku->lu_stateid);
 
-       ENCODE_SEQID_OP_TAIL(locku->lu_stateowner);
+       encode_seqid_op_tail(resp, save, nfserr);
        return nfserr;
 }
 
@@ -2693,7 +2750,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op
        }
        /* XXX save filehandle here */
 out:
-       ENCODE_SEQID_OP_TAIL(open->op_stateowner);
+       encode_seqid_op_tail(resp, save, nfserr);
        return nfserr;
 }
 
@@ -2705,7 +2762,7 @@ nfsd4_encode_open_confirm(struct nfsd4_compoundres *resp, __be32 nfserr, struct
        if (!nfserr)
                nfsd4_encode_stateid(resp, &oc->oc_resp_stateid);
 
-       ENCODE_SEQID_OP_TAIL(oc->oc_stateowner);
+       encode_seqid_op_tail(resp, save, nfserr);
        return nfserr;
 }
 
@@ -2717,7 +2774,7 @@ nfsd4_encode_open_downgrade(struct nfsd4_compoundres *resp, __be32 nfserr, struc
        if (!nfserr)
                nfsd4_encode_stateid(resp, &od->od_stateid);
 
-       ENCODE_SEQID_OP_TAIL(od->od_stateowner);
+       encode_seqid_op_tail(resp, save, nfserr);
        return nfserr;
 }
 
@@ -2759,8 +2816,6 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
                        read->rd_offset, resp->rqstp->rq_vec, read->rd_vlen,
                        &maxcount);
 
-       if (nfserr == nfserr_symlink)
-               nfserr = nfserr_inval;
        if (nfserr)
                return nfserr;
        eof = (read->rd_offset + maxcount >=
@@ -2886,8 +2941,6 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
            readdir->common.err == nfserr_toosmall &&
            readdir->buffer == page) 
                nfserr = nfserr_toosmall;
-       if (nfserr == nfserr_symlink)
-               nfserr = nfserr_notdir;
        if (nfserr)
                goto err_no_verf;
 
@@ -3218,9 +3271,9 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
        WRITEMEM(seq->sessionid.data, NFS4_MAX_SESSIONID_LEN);
        WRITE32(seq->seqid);
        WRITE32(seq->slotid);
-       WRITE32(seq->maxslots);
-       /* For now: target_maxslots = maxslots */
-       WRITE32(seq->maxslots);
+       /* Note slotid's are numbered from zero: */
+       WRITE32(seq->maxslots - 1); /* sr_highest_slotid */
+       WRITE32(seq->maxslots - 1); /* sr_target_highest_slotid */
        WRITE32(seq->status_flags);
 
        ADJUST_ARGS();
@@ -3334,34 +3387,29 @@ static nfsd4_enc nfsd4_enc_ops[] = {
 
 /*
  * Calculate the total amount of memory that the compound response has taken
- * after encoding the current operation.
+ * after encoding the current operation with pad.
  *
- * pad: add on 8 bytes for the next operation's op_code and status so that
- * there is room to cache a failure on the next operation.
+ * pad: if operation is non-idempotent, pad was calculate by op_rsize_bop()
+ *      which was specified at nfsd4_operation, else pad is zero.
  *
- * Compare this length to the session se_fmaxresp_cached.
+ * Compare this length to the session se_fmaxresp_sz and se_fmaxresp_cached.
  *
  * Our se_fmaxresp_cached will always be a multiple of PAGE_SIZE, and so
  * will be at least a page and will therefore hold the xdr_buf head.
  */
-static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp)
+int nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 pad)
 {
-       int status = 0;
        struct xdr_buf *xb = &resp->rqstp->rq_res;
-       struct nfsd4_compoundargs *args = resp->rqstp->rq_argp;
        struct nfsd4_session *session = NULL;
        struct nfsd4_slot *slot = resp->cstate.slot;
-       u32 length, tlen = 0, pad = 8;
+       u32 length, tlen = 0;
 
        if (!nfsd4_has_session(&resp->cstate))
-               return status;
+               return 0;
 
        session = resp->cstate.session;
-       if (session == NULL || slot->sl_cachethis == 0)
-               return status;
-
-       if (resp->opcnt >= args->opcnt)
-               pad = 0; /* this is the last operation */
+       if (session == NULL)
+               return 0;
 
        if (xb->page_len == 0) {
                length = (char *)resp->p - (char *)xb->head[0].iov_base + pad;
@@ -3374,10 +3422,14 @@ static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp)
        dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__,
                length, xb->page_len, tlen, pad);
 
-       if (length <= session->se_fchannel.maxresp_cached)
-               return status;
-       else
+       if (length > session->se_fchannel.maxresp_sz)
+               return nfserr_rep_too_big;
+
+       if (slot->sl_cachethis == 1 &&
+           length > session->se_fchannel.maxresp_cached)
                return nfserr_rep_too_big_to_cache;
+
+       return 0;
 }
 
 void
@@ -3397,8 +3449,8 @@ nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
               !nfsd4_enc_ops[op->opnum]);
        op->status = nfsd4_enc_ops[op->opnum](resp, op->status, &op->u);
        /* nfsd4_check_drc_limit guarantees enough room for error status */
-       if (!op->status && nfsd4_check_drc_limit(resp))
-               op->status = nfserr_rep_too_big_to_cache;
+       if (!op->status)
+               op->status = nfsd4_check_resp_size(resp, 0);
 status:
        /*
         * Note: We write the status directly, instead of using WRITE32(),