]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/nfs/nfs4xdr.c
NFSv4: Retry the DELEGRETURN if the embedded GETATTR is rejected with EACCES
[karo-tx-linux.git] / fs / nfs / nfs4xdr.c
index fc89e5ed07eec90f1a0ef12134c9e29a693ee209..e9255cb453e664c385c9a94969f69bc3514024b1 100644 (file)
@@ -52,6 +52,7 @@
 #include <linux/nfs.h>
 #include <linux/nfs4.h>
 #include <linux/nfs_fs.h>
+#include <linux/fs_struct.h>
 
 #include "nfs4_fs.h"
 #include "internal.h"
@@ -415,6 +416,8 @@ static int nfs4_stat_to_errno(int);
 #else /* CONFIG_NFS_V4_1 */
 #define encode_sequence_maxsz  0
 #define decode_sequence_maxsz  0
+#define encode_layoutreturn_maxsz 0
+#define decode_layoutreturn_maxsz 0
 #endif /* CONFIG_NFS_V4_1 */
 
 #define NFS4_enc_compound_sz   (1024)  /* XXX: large enough? */
@@ -499,22 +502,24 @@ static int nfs4_stat_to_errno(int);
                                (compound_encode_hdr_maxsz + \
                                 encode_sequence_maxsz + \
                                 encode_putfh_maxsz + \
-                                encode_open_downgrade_maxsz + \
-                                encode_getattr_maxsz)
+                                encode_layoutreturn_maxsz + \
+                                encode_open_downgrade_maxsz)
 #define NFS4_dec_open_downgrade_sz \
                                (compound_decode_hdr_maxsz + \
                                 decode_sequence_maxsz + \
                                 decode_putfh_maxsz + \
-                                decode_open_downgrade_maxsz + \
-                                decode_getattr_maxsz)
+                                decode_layoutreturn_maxsz + \
+                                decode_open_downgrade_maxsz)
 #define NFS4_enc_close_sz      (compound_encode_hdr_maxsz + \
                                 encode_sequence_maxsz + \
                                 encode_putfh_maxsz + \
+                                encode_layoutreturn_maxsz + \
                                 encode_close_maxsz + \
                                 encode_getattr_maxsz)
 #define NFS4_dec_close_sz      (compound_decode_hdr_maxsz + \
                                 decode_sequence_maxsz + \
                                 decode_putfh_maxsz + \
+                                decode_layoutreturn_maxsz + \
                                 decode_close_maxsz + \
                                 decode_getattr_maxsz)
 #define NFS4_enc_setattr_sz    (compound_encode_hdr_maxsz + \
@@ -708,10 +713,13 @@ static int nfs4_stat_to_errno(int);
 #define NFS4_enc_delegreturn_sz        (compound_encode_hdr_maxsz + \
                                encode_sequence_maxsz + \
                                encode_putfh_maxsz + \
+                               encode_layoutreturn_maxsz + \
                                encode_delegreturn_maxsz + \
                                encode_getattr_maxsz)
 #define NFS4_dec_delegreturn_sz (compound_decode_hdr_maxsz + \
                                decode_sequence_maxsz + \
+                               decode_putfh_maxsz + \
+                               decode_layoutreturn_maxsz + \
                                decode_delegreturn_maxsz + \
                                decode_getattr_maxsz)
 #define NFS4_enc_getacl_sz     (compound_encode_hdr_maxsz + \
@@ -1003,7 +1011,7 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve
 static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
                                const struct nfs4_label *label,
                                const struct nfs_server *server,
-                               bool excl_check)
+                               bool excl_check, const umode_t *umask)
 {
        char owner_name[IDMAP_NAMESZ];
        char owner_group[IDMAP_NAMESZ];
@@ -1017,18 +1025,21 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
 
        /*
         * We reserve enough space to write the entire attribute buffer at once.
-        * In the worst-case, this would be
-        * 16(bitmap) + 4(attrlen) + 8(size) + 4(mode) + 4(atime) + 4(mtime)
-        * = 40 bytes, plus any contribution from variable-length fields
-        *            such as owner/group.
         */
        if (iap->ia_valid & ATTR_SIZE) {
                bmval[0] |= FATTR4_WORD0_SIZE;
                len += 8;
        }
+       if (!(server->attr_bitmask[2] & FATTR4_WORD2_MODE_UMASK))
+               umask = NULL;
        if (iap->ia_valid & ATTR_MODE) {
-               bmval[1] |= FATTR4_WORD1_MODE;
-               len += 4;
+               if (umask) {
+                       bmval[2] |= FATTR4_WORD2_MODE_UMASK;
+                       len += 8;
+               } else {
+                       bmval[1] |= FATTR4_WORD1_MODE;
+                       len += 4;
+               }
        }
        if (iap->ia_valid & ATTR_UID) {
                owner_namelen = nfs_map_uid_to_name(server, iap->ia_uid, owner_name, IDMAP_NAMESZ);
@@ -1129,6 +1140,10 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap,
                *p++ = cpu_to_be32(label->len);
                p = xdr_encode_opaque_fixed(p, label->label, label->len);
        }
+       if (bmval[2] & FATTR4_WORD2_MODE_UMASK) {
+               *p++ = cpu_to_be32(iap->ia_mode & S_IALLUGO);
+               *p++ = cpu_to_be32(*umask);
+       }
 
 /* out: */
 }
@@ -1183,7 +1198,8 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *
        }
 
        encode_string(xdr, create->name->len, create->name->name);
-       encode_attrs(xdr, create->attrs, create->label, create->server, false);
+       encode_attrs(xdr, create->attrs, create->label, create->server, false,
+                    &create->umask);
 }
 
 static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct compound_hdr *hdr)
@@ -1403,11 +1419,13 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
        switch(arg->createmode) {
        case NFS4_CREATE_UNCHECKED:
                *p = cpu_to_be32(NFS4_CREATE_UNCHECKED);
-               encode_attrs(xdr, arg->u.attrs, arg->label, arg->server, false);
+               encode_attrs(xdr, arg->u.attrs, arg->label, arg->server, false,
+                            &arg->umask);
                break;
        case NFS4_CREATE_GUARDED:
                *p = cpu_to_be32(NFS4_CREATE_GUARDED);
-               encode_attrs(xdr, arg->u.attrs, arg->label, arg->server, false);
+               encode_attrs(xdr, arg->u.attrs, arg->label, arg->server, false,
+                            &arg->umask);
                break;
        case NFS4_CREATE_EXCLUSIVE:
                *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE);
@@ -1416,7 +1434,8 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
        case NFS4_CREATE_EXCLUSIVE4_1:
                *p = cpu_to_be32(NFS4_CREATE_EXCLUSIVE4_1);
                encode_nfs4_verifier(xdr, &arg->u.verifier);
-               encode_attrs(xdr, arg->u.attrs, arg->label, arg->server, true);
+               encode_attrs(xdr, arg->u.attrs, arg->label, arg->server, true,
+                            &arg->umask);
        }
 }
 
@@ -1672,7 +1691,7 @@ static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs
 {
        encode_op_hdr(xdr, OP_SETATTR, decode_setattr_maxsz, hdr);
        encode_nfs4_stateid(xdr, &arg->stateid);
-       encode_attrs(xdr, arg->iap, arg->label, server, false);
+       encode_attrs(xdr, arg->iap, arg->label, server, false, NULL);
 }
 
 static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr)
@@ -2015,6 +2034,7 @@ encode_layoutreturn(struct xdr_stream *xdr,
                    const struct nfs4_layoutreturn_args *args,
                    struct compound_hdr *hdr)
 {
+       const struct pnfs_layoutdriver_type *lr_ops = NFS_SERVER(args->inode)->pnfs_curr_ld;
        __be32 *p;
 
        encode_op_hdr(xdr, OP_LAYOUTRETURN, decode_layoutreturn_maxsz, hdr);
@@ -2029,10 +2049,11 @@ encode_layoutreturn(struct xdr_stream *xdr,
        spin_lock(&args->inode->i_lock);
        encode_nfs4_stateid(xdr, &args->stateid);
        spin_unlock(&args->inode->i_lock);
-       if (NFS_SERVER(args->inode)->pnfs_curr_ld->encode_layoutreturn) {
-               NFS_SERVER(args->inode)->pnfs_curr_ld->encode_layoutreturn(
-                       NFS_I(args->inode)->layout, xdr, args);
-       } else
+       if (args->ld_private->ops && args->ld_private->ops->encode)
+               args->ld_private->ops->encode(xdr, args, args->ld_private);
+       else if (lr_ops->encode_layoutreturn)
+               lr_ops->encode_layoutreturn(xdr, args);
+       else
                encode_uint32(xdr, 0);
 }
 
@@ -2062,6 +2083,13 @@ static void encode_free_stateid(struct xdr_stream *xdr,
        encode_op_hdr(xdr, OP_FREE_STATEID, decode_free_stateid_maxsz, hdr);
        encode_nfs4_stateid(xdr, &args->stateid);
 }
+#else
+static inline void
+encode_layoutreturn(struct xdr_stream *xdr,
+                   const struct nfs4_layoutreturn_args *args,
+                   struct compound_hdr *hdr)
+{
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 /*
@@ -2249,8 +2277,11 @@ static void nfs4_xdr_enc_close(struct rpc_rqst *req, struct xdr_stream *xdr,
        encode_compound_hdr(xdr, req, &hdr);
        encode_sequence(xdr, &args->seq_args, &hdr);
        encode_putfh(xdr, args->fh, &hdr);
+       if (args->lr_args)
+               encode_layoutreturn(xdr, args->lr_args, &hdr);
+       if (args->bitmask != NULL)
+               encode_getfattr(xdr, args->bitmask, &hdr);
        encode_close(xdr, args, &hdr);
-       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
 }
 
@@ -2327,8 +2358,9 @@ static void nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req,
        encode_compound_hdr(xdr, req, &hdr);
        encode_sequence(xdr, &args->seq_args, &hdr);
        encode_putfh(xdr, args->fh, &hdr);
+       if (args->lr_args)
+               encode_layoutreturn(xdr, args->lr_args, &hdr);
        encode_open_downgrade(xdr, args, &hdr);
-       encode_getfattr(xdr, args->bitmask, &hdr);
        encode_nops(&hdr);
 }
 
@@ -2671,7 +2703,10 @@ static void nfs4_xdr_enc_delegreturn(struct rpc_rqst *req,
        encode_compound_hdr(xdr, req, &hdr);
        encode_sequence(xdr, &args->seq_args, &hdr);
        encode_putfh(xdr, args->fhandle, &hdr);
-       encode_getfattr(xdr, args->bitmask, &hdr);
+       if (args->lr_args)
+               encode_layoutreturn(xdr, args->lr_args, &hdr);
+       if (args->bitmask)
+               encode_getfattr(xdr, args->bitmask, &hdr);
        encode_delegreturn(xdr, args->stateid, &hdr);
        encode_nops(&hdr);
 }
@@ -6089,6 +6124,13 @@ static int decode_free_stateid(struct xdr_stream *xdr,
        res->status = decode_op_hdr(xdr, OP_FREE_STATEID);
        return res->status;
 }
+#else
+static inline
+int decode_layoutreturn(struct xdr_stream *xdr,
+                              struct nfs4_layoutreturn_res *res)
+{
+       return 0;
+}
 #endif /* CONFIG_NFS_V4_1 */
 
 /*
@@ -6114,10 +6156,13 @@ static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp,
        status = decode_putfh(xdr);
        if (status)
                goto out;
+       if (res->lr_res) {
+               status = decode_layoutreturn(xdr, res->lr_res);
+               res->lr_ret = status;
+               if (status)
+                       goto out;
+       }
        status = decode_open_downgrade(xdr, res);
-       if (status != 0)
-               goto out;
-       decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -6444,16 +6489,18 @@ static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
        status = decode_putfh(xdr);
        if (status)
                goto out;
+       if (res->lr_res) {
+               status = decode_layoutreturn(xdr, res->lr_res);
+               res->lr_ret = status;
+               if (status)
+                       goto out;
+       }
+       if (res->fattr != NULL) {
+               status = decode_getfattr(xdr, res->fattr, res->server);
+               if (status != 0)
+                       goto out;
+       }
        status = decode_close(xdr, res);
-       if (status != 0)
-               goto out;
-       /*
-        * Note: Server may do delete on close for this file
-        *      in which case the getattr call will fail with
-        *      an ESTALE error. Shouldn't be a problem,
-        *      though, since fattr->valid will remain unset.
-        */
-       decode_getfattr(xdr, res->fattr, res->server);
 out:
        return status;
 }
@@ -6920,9 +6967,17 @@ static int nfs4_xdr_dec_delegreturn(struct rpc_rqst *rqstp,
        status = decode_putfh(xdr);
        if (status != 0)
                goto out;
-       status = decode_getfattr(xdr, res->fattr, res->server);
-       if (status != 0)
-               goto out;
+       if (res->lr_res) {
+               status = decode_layoutreturn(xdr, res->lr_res);
+               res->lr_ret = status;
+               if (status)
+                       goto out;
+       }
+       if (res->fattr) {
+               status = decode_getfattr(xdr, res->fattr, res->server);
+               if (status != 0)
+                       goto out;
+       }
        status = decode_delegreturn(xdr);
 out:
        return status;