]> rtime.felk.cvut.cz Git - linux-imx.git/blobdiff - fs/nfs/nfs4proc.c
NFSv4: Fix up nfs4_proc_lookup_mountpoint
[linux-imx.git] / fs / nfs / nfs4proc.c
index 28241a42f363581341b3ed81de137fc0da1b428e..108a774095f7ef6a53fc04366aec3caa3bfea717 100644 (file)
@@ -77,15 +77,68 @@ static int _nfs4_recover_proc_open(struct nfs4_opendata *data);
 static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
 static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
 static void nfs_fixup_referral_attributes(struct nfs_fattr *fattr);
-static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *);
-static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
+static int nfs4_proc_getattr(struct nfs_server *, struct nfs_fh *, struct nfs_fattr *, struct nfs4_label *label);
+static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr, struct nfs4_label *label);
 static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                            struct nfs_fattr *fattr, struct iattr *sattr,
-                           struct nfs4_state *state);
+                           struct nfs4_state *state, struct nfs4_label *ilabel,
+                           struct nfs4_label *olabel);
 #ifdef CONFIG_NFS_V4_1
-static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *);
-static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *);
+static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
+               struct rpc_cred *);
+static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *,
+               struct rpc_cred *);
 #endif
+
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+static inline struct nfs4_label *
+nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
+       struct iattr *sattr, struct nfs4_label *label)
+{
+       int err;
+
+       if (label == NULL)
+               return NULL;
+
+       if (nfs_server_capable(dir, NFS_CAP_SECURITY_LABEL) == 0)
+               return NULL;
+
+       if (NFS_SERVER(dir)->nfs_client->cl_minorversion < 2)
+               return NULL;
+
+       err = security_dentry_init_security(dentry, sattr->ia_mode,
+                               &dentry->d_name, (void **)&label->label, &label->len);
+       if (err == 0)
+               return label;
+
+       return NULL;
+}
+static inline void
+nfs4_label_release_security(struct nfs4_label *label)
+{
+       if (label)
+               security_release_secctx(label->label, label->len);
+}
+static inline u32 *nfs4_bitmask(struct nfs_server *server, struct nfs4_label *label)
+{
+       if (label)
+               return server->attr_bitmask;
+
+       return server->attr_bitmask_nl;
+}
+#else
+static inline struct nfs4_label *
+nfs4_label_init_security(struct inode *dir, struct dentry *dentry,
+       struct iattr *sattr, struct nfs4_label *l)
+{ return NULL; }
+static inline void
+nfs4_label_release_security(struct nfs4_label *label)
+{ return; }
+static inline u32 *
+nfs4_bitmask(struct nfs_server *server, struct nfs4_label *label)
+{ return server->attr_bitmask; }
+#endif
+
 /* Prevent leaks of NFSv4 errors into userland */
 static int nfs4_map_errors(int err)
 {
@@ -134,7 +187,10 @@ const u32 nfs4_fattr_bitmap[3] = {
        | FATTR4_WORD1_SPACE_USED
        | FATTR4_WORD1_TIME_ACCESS
        | FATTR4_WORD1_TIME_METADATA
-       | FATTR4_WORD1_TIME_MODIFY
+       | FATTR4_WORD1_TIME_MODIFY,
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+       FATTR4_WORD2_SECURITY_LABEL
+#endif
 };
 
 static const u32 nfs4_pnfs_open_bitmap[3] = {
@@ -161,7 +217,7 @@ static const u32 nfs4_open_noattr_bitmap[3] = {
        | FATTR4_WORD0_FILEID,
 };
 
-const u32 nfs4_statfs_bitmap[2] = {
+const u32 nfs4_statfs_bitmap[3] = {
        FATTR4_WORD0_FILES_AVAIL
        | FATTR4_WORD0_FILES_FREE
        | FATTR4_WORD0_FILES_TOTAL,
@@ -170,7 +226,7 @@ const u32 nfs4_statfs_bitmap[2] = {
        | FATTR4_WORD1_SPACE_TOTAL
 };
 
-const u32 nfs4_pathconf_bitmap[2] = {
+const u32 nfs4_pathconf_bitmap[3] = {
        FATTR4_WORD0_MAXLINK
        | FATTR4_WORD0_MAXNAME,
        0
@@ -185,7 +241,7 @@ const u32 nfs4_fsinfo_bitmap[3] = { FATTR4_WORD0_MAXFILESIZE
                        FATTR4_WORD2_LAYOUT_BLKSIZE
 };
 
-const u32 nfs4_fs_locations_bitmap[2] = {
+const u32 nfs4_fs_locations_bitmap[3] = {
        FATTR4_WORD0_TYPE
        | FATTR4_WORD0_CHANGE
        | FATTR4_WORD0_SIZE
@@ -201,7 +257,7 @@ const u32 nfs4_fs_locations_bitmap[2] = {
        | FATTR4_WORD1_TIME_ACCESS
        | FATTR4_WORD1_TIME_METADATA
        | FATTR4_WORD1_TIME_MODIFY
-       | FATTR4_WORD1_MOUNTED_ON_FILEID
+       | FATTR4_WORD1_MOUNTED_ON_FILEID,
 };
 
 static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dentry,
@@ -762,6 +818,7 @@ struct nfs4_opendata {
        struct nfs4_string owner_name;
        struct nfs4_string group_name;
        struct nfs_fattr f_attr;
+       struct nfs4_label *f_label;
        struct dentry *dir;
        struct dentry *dentry;
        struct nfs4_state_owner *owner;
@@ -807,6 +864,7 @@ nfs4_map_atomic_open_claim(struct nfs_server *server,
 static void nfs4_init_opendata_res(struct nfs4_opendata *p)
 {
        p->o_res.f_attr = &p->f_attr;
+       p->o_res.f_label = p->f_label;
        p->o_res.seqid = p->o_arg.seqid;
        p->c_res.seqid = p->c_arg.seqid;
        p->o_res.server = p->o_arg.server;
@@ -818,6 +876,7 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
 static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
                struct nfs4_state_owner *sp, fmode_t fmode, int flags,
                const struct iattr *attrs,
+               struct nfs4_label *label,
                enum open_claim_type4 claim,
                gfp_t gfp_mask)
 {
@@ -829,9 +888,14 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        p = kzalloc(sizeof(*p), gfp_mask);
        if (p == NULL)
                goto err;
+
+       p->f_label = nfs4_label_alloc(server, gfp_mask);
+       if (IS_ERR(p->f_label))
+               goto err_free_p;
+
        p->o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid, gfp_mask);
        if (p->o_arg.seqid == NULL)
-               goto err_free;
+               goto err_free_label;
        nfs_sb_active(dentry->d_sb);
        p->dentry = dget(dentry);
        p->dir = parent;
@@ -852,8 +916,9 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        p->o_arg.id.uniquifier = sp->so_seqid.owner_id;
        p->o_arg.name = &dentry->d_name;
        p->o_arg.server = server;
-       p->o_arg.bitmask = server->attr_bitmask;
+       p->o_arg.bitmask = nfs4_bitmask(server, label);
        p->o_arg.open_bitmap = &nfs4_fattr_bitmap[0];
+       p->o_arg.label = label;
        p->o_arg.claim = nfs4_map_atomic_open_claim(server, claim);
        switch (p->o_arg.claim) {
        case NFS4_OPEN_CLAIM_NULL:
@@ -884,7 +949,10 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct dentry *dentry,
        nfs4_init_opendata_res(p);
        kref_init(&p->kref);
        return p;
-err_free:
+
+err_free_label:
+       nfs4_label_free(p->f_label);
+err_free_p:
        kfree(p);
 err:
        dput(parent);
@@ -901,6 +969,9 @@ static void nfs4_opendata_free(struct kref *kref)
        if (p->state != NULL)
                nfs4_put_open_state(p->state);
        nfs4_put_state_owner(p->owner);
+
+       nfs4_label_free(p->f_label);
+
        dput(p->dir);
        dput(p->dentry);
        nfs_sb_deactive(sb);
@@ -1179,6 +1250,8 @@ _nfs4_opendata_reclaim_to_nfs4_state(struct nfs4_opendata *data)
        if (ret)
                goto err;
 
+       nfs_setsecurity(inode, &data->f_attr, data->f_label);
+
        if (data->o_res.delegation_type != 0)
                nfs4_opendata_check_deleg(data, state);
        update_open_stateid(state, &data->o_res.stateid, NULL,
@@ -1205,7 +1278,7 @@ _nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data)
        ret = -EAGAIN;
        if (!(data->f_attr.valid & NFS_ATTR_FATTR))
                goto err;
-       inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr);
+       inode = nfs_fhget(data->dir->d_sb, &data->o_res.fh, &data->f_attr, data->f_label);
        ret = PTR_ERR(inode);
        if (IS_ERR(inode))
                goto err;
@@ -1258,7 +1331,7 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
        struct nfs4_opendata *opendata;
 
        opendata = nfs4_opendata_alloc(ctx->dentry, state->owner, 0, 0,
-                       NULL, claim, GFP_NOFS);
+                       NULL, NULL, claim, GFP_NOFS);
        if (opendata == NULL)
                return ERR_PTR(-ENOMEM);
        opendata->state = state;
@@ -1784,7 +1857,7 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
                        return status;
        }
        if (!(o_res->f_attr->valid & NFS_ATTR_FATTR))
-               _nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr);
+               _nfs4_proc_getattr(server, &o_res->fh, o_res->f_attr, o_res->f_label);
        return 0;
 }
 
@@ -1855,18 +1928,30 @@ static void nfs41_clear_delegation_stateid(struct nfs4_state *state)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
        nfs4_stateid *stateid = &state->stateid;
-       int status;
+       struct nfs_delegation *delegation;
+       struct rpc_cred *cred = NULL;
+       int status = -NFS4ERR_BAD_STATEID;
 
        /* If a state reset has been done, test_stateid is unneeded */
        if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
                return;
 
-       status = nfs41_test_stateid(server, stateid);
+       /* Get the delegation credential for use by test/free_stateid */
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(state->inode)->delegation);
+       if (delegation != NULL &&
+           nfs4_stateid_match(&delegation->stateid, stateid)) {
+               cred = get_rpccred(delegation->cred);
+               rcu_read_unlock();
+               status = nfs41_test_stateid(server, stateid, cred);
+       } else
+               rcu_read_unlock();
+
        if (status != NFS_OK) {
                /* Free the stateid unless the server explicitly
                 * informs us the stateid is unrecognized. */
                if (status != -NFS4ERR_BAD_STATEID)
-                       nfs41_free_stateid(server, stateid);
+                       nfs41_free_stateid(server, stateid, cred);
                nfs_remove_bad_delegation(state->inode);
 
                write_seqlock(&state->seqlock);
@@ -1874,6 +1959,9 @@ static void nfs41_clear_delegation_stateid(struct nfs4_state *state)
                write_sequnlock(&state->seqlock);
                clear_bit(NFS_DELEGATED_STATE, &state->flags);
        }
+
+       if (cred != NULL)
+               put_rpccred(cred);
 }
 
 /**
@@ -1888,6 +1976,7 @@ static int nfs41_check_open_stateid(struct nfs4_state *state)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
        nfs4_stateid *stateid = &state->open_stateid;
+       struct rpc_cred *cred = state->owner->so_cred;
        int status;
 
        /* If a state reset has been done, test_stateid is unneeded */
@@ -1896,12 +1985,12 @@ static int nfs41_check_open_stateid(struct nfs4_state *state)
            (test_bit(NFS_O_RDWR_STATE, &state->flags) == 0))
                return -NFS4ERR_BAD_STATEID;
 
-       status = nfs41_test_stateid(server, stateid);
+       status = nfs41_test_stateid(server, stateid, cred);
        if (status != NFS_OK) {
                /* Free the stateid unless the server explicitly
                 * informs us the stateid is unrecognized. */
                if (status != -NFS4ERR_BAD_STATEID)
-                       nfs41_free_stateid(server, stateid);
+                       nfs41_free_stateid(server, stateid, cred);
 
                clear_bit(NFS_O_RDONLY_STATE, &state->flags);
                clear_bit(NFS_O_WRONLY_STATE, &state->flags);
@@ -1942,10 +2031,11 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct
 static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
                fmode_t fmode,
                int flags,
-               struct nfs4_state **res)
+               struct nfs_open_context *ctx)
 {
        struct nfs4_state_owner *sp = opendata->owner;
        struct nfs_server *server = sp->so_server;
+       struct dentry *dentry;
        struct nfs4_state *state;
        unsigned int seq;
        int ret;
@@ -1963,13 +2053,31 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
        if (server->caps & NFS_CAP_POSIX_LOCK)
                set_bit(NFS_STATE_POSIX_LOCKS, &state->flags);
 
+       dentry = opendata->dentry;
+       if (dentry->d_inode == NULL) {
+               /* FIXME: Is this d_drop() ever needed? */
+               d_drop(dentry);
+               dentry = d_add_unique(dentry, igrab(state->inode));
+               if (dentry == NULL) {
+                       dentry = opendata->dentry;
+               } else if (dentry != ctx->dentry) {
+                       dput(ctx->dentry);
+                       ctx->dentry = dget(dentry);
+               }
+               nfs_set_verifier(dentry,
+                               nfs_save_change_attribute(opendata->dir->d_inode));
+       }
+
        ret = nfs4_opendata_access(sp->so_cred, opendata, state, fmode, flags);
        if (ret != 0)
                goto out;
 
-       if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
-               nfs4_schedule_stateid_recovery(server, state);
-       *res = state;
+       ctx->state = state;
+       if (dentry->d_inode == state->inode) {
+               nfs_inode_attach_open_context(ctx);
+               if (read_seqcount_retry(&sp->so_reclaim_seqcount, seq))
+                       nfs4_schedule_stateid_recovery(server, state);
+       }
 out:
        return ret;
 }
@@ -1978,19 +2086,21 @@ out:
  * Returns a referenced nfs4_state
  */
 static int _nfs4_do_open(struct inode *dir,
-                       struct dentry *dentry,
-                       fmode_t fmode,
+                       struct nfs_open_context *ctx,
                        int flags,
                        struct iattr *sattr,
-                       struct rpc_cred *cred,
-                       struct nfs4_state **res,
-                       struct nfs4_threshold **ctx_th)
+                       struct nfs4_label *label)
 {
        struct nfs4_state_owner  *sp;
        struct nfs4_state     *state = NULL;
        struct nfs_server       *server = NFS_SERVER(dir);
        struct nfs4_opendata *opendata;
+       struct dentry *dentry = ctx->dentry;
+       struct rpc_cred *cred = ctx->cred;
+       struct nfs4_threshold **ctx_th = &ctx->mdsthreshold;
+       fmode_t fmode = ctx->mode & (FMODE_READ|FMODE_WRITE|FMODE_EXEC);
        enum open_claim_type4 claim = NFS4_OPEN_CLAIM_NULL;
+       struct nfs4_label *olabel = NULL;
        int status;
 
        /* Protect against reboot recovery conflicts */
@@ -2009,22 +2119,31 @@ static int _nfs4_do_open(struct inode *dir,
        if (dentry->d_inode)
                claim = NFS4_OPEN_CLAIM_FH;
        opendata = nfs4_opendata_alloc(dentry, sp, fmode, flags, sattr,
-                       claim, GFP_KERNEL);
+                       label, claim, GFP_KERNEL);
        if (opendata == NULL)
                goto err_put_state_owner;
 
+       if (label) {
+               olabel = nfs4_label_alloc(server, GFP_KERNEL);
+               if (IS_ERR(olabel)) {
+                       status = PTR_ERR(olabel);
+                       goto err_opendata_put;
+               }
+       }
+
        if (ctx_th && server->attr_bitmask[2] & FATTR4_WORD2_MDSTHRESHOLD) {
                opendata->f_attr.mdsthreshold = pnfs_mdsthreshold_alloc();
                if (!opendata->f_attr.mdsthreshold)
-                       goto err_opendata_put;
+                       goto err_free_label;
                opendata->o_arg.open_bitmap = &nfs4_pnfs_open_bitmap[0];
        }
        if (dentry->d_inode != NULL)
                opendata->state = nfs4_get_open_state(dentry->d_inode, sp);
 
-       status = _nfs4_open_and_get_state(opendata, fmode, flags, &state);
+       status = _nfs4_open_and_get_state(opendata, fmode, flags, ctx);
        if (status != 0)
-               goto err_opendata_put;
+               goto err_free_label;
+       state = ctx->state;
 
        if ((opendata->o_arg.open_flags & O_EXCL) &&
            (opendata->o_arg.createmode != NFS4_CREATE_GUARDED)) {
@@ -2033,10 +2152,12 @@ static int _nfs4_do_open(struct inode *dir,
                nfs_fattr_init(opendata->o_res.f_attr);
                status = nfs4_do_setattr(state->inode, cred,
                                opendata->o_res.f_attr, sattr,
-                               state);
-               if (status == 0)
+                               state, label, olabel);
+               if (status == 0) {
                        nfs_setattr_update_inode(state->inode, sattr);
-               nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr);
+                       nfs_post_op_update_inode(state->inode, opendata->o_res.f_attr);
+                       nfs_setsecurity(state->inode, opendata->o_res.f_attr, olabel);
+               }
        }
 
        if (pnfs_use_threshold(ctx_th, opendata->f_attr.mdsthreshold, server))
@@ -2045,38 +2166,37 @@ static int _nfs4_do_open(struct inode *dir,
                kfree(opendata->f_attr.mdsthreshold);
        opendata->f_attr.mdsthreshold = NULL;
 
+       nfs4_label_free(olabel);
+
        nfs4_opendata_put(opendata);
        nfs4_put_state_owner(sp);
-       *res = state;
        return 0;
+err_free_label:
+       nfs4_label_free(olabel);
 err_opendata_put:
        kfree(opendata->f_attr.mdsthreshold);
        nfs4_opendata_put(opendata);
 err_put_state_owner:
        nfs4_put_state_owner(sp);
 out_err:
-       *res = NULL;
        return status;
 }
 
 
 static struct nfs4_state *nfs4_do_open(struct inode *dir,
-                                       struct dentry *dentry,
-                                       fmode_t fmode,
+                                       struct nfs_open_context *ctx,
                                        int flags,
                                        struct iattr *sattr,
-                                       struct rpc_cred *cred,
-                                       struct nfs4_threshold **ctx_th)
+                                       struct nfs4_label *label)
 {
        struct nfs_server *server = NFS_SERVER(dir);
        struct nfs4_exception exception = { };
        struct nfs4_state *res;
        int status;
 
-       fmode &= FMODE_READ|FMODE_WRITE|FMODE_EXEC;
        do {
-               status = _nfs4_do_open(dir, dentry, fmode, flags, sattr, cred,
-                                      &res, ctx_th);
+               status = _nfs4_do_open(dir, ctx, flags, sattr, label);
+               res = ctx->state;
                if (status == 0)
                        break;
                /* NOTE: BAD_SEQID means the server and client disagree about the
@@ -2122,7 +2242,8 @@ static struct nfs4_state *nfs4_do_open(struct inode *dir,
 
 static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                            struct nfs_fattr *fattr, struct iattr *sattr,
-                           struct nfs4_state *state)
+                           struct nfs4_state *state, struct nfs4_label *ilabel,
+                           struct nfs4_label *olabel)
 {
        struct nfs_server *server = NFS_SERVER(inode);
         struct nfs_setattrargs  arg = {
@@ -2130,9 +2251,11 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                 .iap            = sattr,
                .server         = server,
                .bitmask = server->attr_bitmask,
+               .label          = ilabel,
         };
         struct nfs_setattrres  res = {
                .fattr          = fattr,
+               .label          = olabel,
                .server         = server,
         };
         struct rpc_message msg = {
@@ -2146,6 +2269,10 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        bool truncate;
        int status;
 
+       arg.bitmask = nfs4_bitmask(server, ilabel);
+       if (ilabel)
+               arg.bitmask = nfs4_bitmask(server, olabel);
+
        nfs_fattr_init(fattr);
 
        /* Servers should only apply open mode checks for file size changes */
@@ -2172,7 +2299,8 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
 
 static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
                           struct nfs_fattr *fattr, struct iattr *sattr,
-                          struct nfs4_state *state)
+                          struct nfs4_state *state, struct nfs4_label *ilabel,
+                          struct nfs4_label *olabel)
 {
        struct nfs_server *server = NFS_SERVER(inode);
        struct nfs4_exception exception = {
@@ -2181,7 +2309,7 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
        };
        int err;
        do {
-               err = _nfs4_do_setattr(inode, cred, fattr, sattr, state);
+               err = _nfs4_do_setattr(inode, cred, fattr, sattr, state, ilabel, olabel);
                switch (err) {
                case -NFS4ERR_OPENMODE:
                        if (!(sattr->ia_valid & ATTR_SIZE)) {
@@ -2426,14 +2554,18 @@ static struct inode *
 nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr)
 {
        struct nfs4_state *state;
+       struct nfs4_label l = {0, 0, 0, NULL}, *label = NULL;
+
+       label = nfs4_label_init_security(dir, ctx->dentry, attr, &l);
 
        /* Protect against concurrent sillydeletes */
-       state = nfs4_do_open(dir, ctx->dentry, ctx->mode, open_flags, attr,
-                            ctx->cred, &ctx->mdsthreshold);
+       state = nfs4_do_open(dir, ctx, open_flags, attr, label);
+
+       nfs4_label_release_security(label);
+
        if (IS_ERR(state))
                return ERR_CAST(state);
-       ctx->state = state;
-       return igrab(state->inode);
+       return state->inode;
 }
 
 static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync)
@@ -2489,7 +2621,17 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
                        server->caps |= NFS_CAP_CTIME;
                if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY)
                        server->caps |= NFS_CAP_MTIME;
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+               if (res.attr_bitmask[2] & FATTR4_WORD2_SECURITY_LABEL)
+                       server->caps |= NFS_CAP_SECURITY_LABEL;
+#endif
+               memcpy(server->attr_bitmask_nl, res.attr_bitmask,
+                               sizeof(server->attr_bitmask));
 
+               if (server->caps & NFS_CAP_SECURITY_LABEL) {
+                       server->attr_bitmask_nl[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
+                       res.attr_bitmask[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
+               }
                memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
                server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
                server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
@@ -2515,8 +2657,9 @@ int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
 static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
                struct nfs_fsinfo *info)
 {
+       u32 bitmask[3];
        struct nfs4_lookup_root_arg args = {
-               .bitmask = nfs4_fattr_bitmap,
+               .bitmask = bitmask,
        };
        struct nfs4_lookup_res res = {
                .server = server,
@@ -2529,6 +2672,13 @@ static int _nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
                .rpc_resp = &res,
        };
 
+       bitmask[0] = nfs4_fattr_bitmap[0];
+       bitmask[1] = nfs4_fattr_bitmap[1];
+       /*
+        * Process the label in the upcoming getfattr
+        */
+       bitmask[2] = nfs4_fattr_bitmap[2] & ~FATTR4_WORD2_SECURITY_LABEL;
+
        nfs_fattr_init(info->fattr);
        return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
 }
@@ -2648,6 +2798,7 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
 {
        int error;
        struct nfs_fattr *fattr = info->fattr;
+       struct nfs4_label *label = NULL;
 
        error = nfs4_server_capabilities(server, mntfh);
        if (error < 0) {
@@ -2655,16 +2806,23 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *mntfh,
                return error;
        }
 
-       error = nfs4_proc_getattr(server, mntfh, fattr);
+       label = nfs4_label_alloc(server, GFP_KERNEL);
+       if (IS_ERR(label))
+               return PTR_ERR(label);
+
+       error = nfs4_proc_getattr(server, mntfh, fattr, label);
        if (error < 0) {
                dprintk("nfs4_get_root: getattr error = %d\n", -error);
-               return error;
+               goto err_free_label;
        }
 
        if (fattr->valid & NFS_ATTR_FATTR_FSID &&
            !nfs_fsid_equal(&server->fsid, &fattr->fsid))
                memcpy(&server->fsid, &fattr->fsid, sizeof(server->fsid));
 
+err_free_label:
+       nfs4_label_free(label);
+
        return error;
 }
 
@@ -2711,7 +2869,8 @@ out:
        return status;
 }
 
-static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
+                               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs4_getattr_arg args = {
                .fh = fhandle,
@@ -2719,6 +2878,7 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
        };
        struct nfs4_getattr_res res = {
                .fattr = fattr,
+               .label = label,
                .server = server,
        };
        struct rpc_message msg = {
@@ -2726,18 +2886,21 @@ static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
                .rpc_argp = &args,
                .rpc_resp = &res,
        };
-       
+
+       args.bitmask = nfs4_bitmask(server, label);
+
        nfs_fattr_init(fattr);
        return nfs4_call_sync(server->client, server, &msg, &args.seq_args, &res.seq_res, 0);
 }
 
-static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+static int nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
+                               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs4_exception exception = { };
        int err;
        do {
                err = nfs4_handle_exception(server,
-                               _nfs4_proc_getattr(server, fhandle, fattr),
+                               _nfs4_proc_getattr(server, fhandle, fattr, label),
                                &exception);
        } while (exception.retry);
        return err;
@@ -2767,6 +2930,7 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
        struct inode *inode = dentry->d_inode;
        struct rpc_cred *cred = NULL;
        struct nfs4_state *state = NULL;
+       struct nfs4_label *label = NULL;
        int status;
 
        if (pnfs_ld_layoutret_on_setattr(inode))
@@ -2793,15 +2957,22 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
                }
        }
 
-       status = nfs4_do_setattr(inode, cred, fattr, sattr, state);
-       if (status == 0)
+       label = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL);
+       if (IS_ERR(label))
+               return PTR_ERR(label);
+
+       status = nfs4_do_setattr(inode, cred, fattr, sattr, state, NULL, label);
+       if (status == 0) {
                nfs_setattr_update_inode(inode, sattr);
+               nfs_setsecurity(inode, fattr, label);
+       }
+       nfs4_label_free(label);
        return status;
 }
 
 static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
                const struct qstr *name, struct nfs_fh *fhandle,
-               struct nfs_fattr *fattr)
+               struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs_server *server = NFS_SERVER(dir);
        int                    status;
@@ -2813,6 +2984,7 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
        struct nfs4_lookup_res res = {
                .server = server,
                .fattr = fattr,
+               .label = label,
                .fh = fhandle,
        };
        struct rpc_message msg = {
@@ -2821,6 +2993,8 @@ static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
                .rpc_resp = &res,
        };
 
+       args.bitmask = nfs4_bitmask(server, label);
+
        nfs_fattr_init(fattr);
 
        dprintk("NFS call  lookup %s\n", name->name);
@@ -2839,13 +3013,13 @@ static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr)
 
 static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
                                   struct qstr *name, struct nfs_fh *fhandle,
-                                  struct nfs_fattr *fattr)
+                                  struct nfs_fattr *fattr, struct nfs4_label *label)
 {
        struct nfs4_exception exception = { };
        struct rpc_clnt *client = *clnt;
        int err;
        do {
-               err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr);
+               err = _nfs4_proc_lookup(client, dir, name, fhandle, fattr, label);
                switch (err) {
                case -NFS4ERR_BADNAME:
                        err = -ENOENT;
@@ -2879,12 +3053,13 @@ out:
 }
 
 static int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
-                           struct nfs_fh *fhandle, struct nfs_fattr *fattr)
+                           struct nfs_fh *fhandle, struct nfs_fattr *fattr,
+                           struct nfs4_label *label)
 {
        int status;
        struct rpc_clnt *client = NFS_CLIENT(dir);
 
-       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr);
+       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, label);
        if (client != NFS_CLIENT(dir)) {
                rpc_shutdown_client(client);
                nfs_fixup_secinfo_attributes(fattr);
@@ -2896,15 +3071,13 @@ struct rpc_clnt *
 nfs4_proc_lookup_mountpoint(struct inode *dir, struct qstr *name,
                            struct nfs_fh *fhandle, struct nfs_fattr *fattr)
 {
+       struct rpc_clnt *client = NFS_CLIENT(dir);
        int status;
-       struct rpc_clnt *client = rpc_clone_client(NFS_CLIENT(dir));
 
-       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr);
-       if (status < 0) {
-               rpc_shutdown_client(client);
+       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, NULL);
+       if (status < 0)
                return ERR_PTR(status);
-       }
-       return client;
+       return (client == NFS_CLIENT(dir)) ? rpc_clone_client(client) : client;
 }
 
 static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
@@ -2924,7 +3097,7 @@ static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry
                .rpc_cred = entry->cred,
        };
        int mode = entry->mask;
-       int status;
+       int status = 0;
 
        /*
         * Determine which access bits we want to ask for...
@@ -3029,6 +3202,7 @@ static int
 nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                 int flags)
 {
+       struct nfs4_label l, *ilabel = NULL;
        struct nfs_open_context *ctx;
        struct nfs4_state *state;
        int status = 0;
@@ -3037,19 +3211,16 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
        if (IS_ERR(ctx))
                return PTR_ERR(ctx);
 
+       ilabel = nfs4_label_init_security(dir, dentry, sattr, &l);
+
        sattr->ia_mode &= ~current_umask();
-       state = nfs4_do_open(dir, dentry, ctx->mode,
-                       flags, sattr, ctx->cred,
-                       &ctx->mdsthreshold);
-       d_drop(dentry);
+       state = nfs4_do_open(dir, ctx, flags, sattr, ilabel);
        if (IS_ERR(state)) {
                status = PTR_ERR(state);
                goto out;
        }
-       d_add(dentry, igrab(state->inode));
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-       ctx->state = state;
 out:
+       nfs4_label_release_security(ilabel);
        put_nfs_open_context(ctx);
        return status;
 }
@@ -3098,6 +3269,8 @@ static void nfs4_proc_unlink_setup(struct rpc_message *msg, struct inode *dir)
        res->server = server;
        msg->rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_REMOVE];
        nfs41_init_sequence(&args->seq_args, &res->seq_res, 1);
+
+       nfs_fattr_init(res->dir_attr);
 }
 
 static void nfs4_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlinkdata *data)
@@ -3173,7 +3346,7 @@ static int _nfs4_proc_rename(struct inode *old_dir, struct qstr *old_name,
                .rpc_resp = &res,
        };
        int status = -ENOMEM;
-       
+
        status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (!status) {
                update_changeattr(old_dir, &res.old_cinfo);
@@ -3207,6 +3380,7 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
        };
        struct nfs4_link_res res = {
                .server = server,
+               .label = NULL,
        };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LINK],
@@ -3219,11 +3393,24 @@ static int _nfs4_proc_link(struct inode *inode, struct inode *dir, struct qstr *
        if (res.fattr == NULL)
                goto out;
 
+       res.label = nfs4_label_alloc(server, GFP_KERNEL);
+       if (IS_ERR(res.label)) {
+               status = PTR_ERR(res.label);
+               goto out;
+       }
+       arg.bitmask = nfs4_bitmask(server, res.label);
+
        status = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
        if (!status) {
                update_changeattr(dir, &res.cinfo);
-               nfs_post_op_update_inode(inode, res.fattr);
+               status = nfs_post_op_update_inode(inode, res.fattr);
+               if (!status)
+                       nfs_setsecurity(inode, res.fattr, res.label);
        }
+
+
+       nfs4_label_free(res.label);
+
 out:
        nfs_free_fattr(res.fattr);
        return status;
@@ -3247,6 +3434,7 @@ struct nfs4_createdata {
        struct nfs4_create_res res;
        struct nfs_fh fh;
        struct nfs_fattr fattr;
+       struct nfs4_label *label;
 };
 
 static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
@@ -3258,6 +3446,10 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
        if (data != NULL) {
                struct nfs_server *server = NFS_SERVER(dir);
 
+               data->label = nfs4_label_alloc(server, GFP_KERNEL);
+               if (IS_ERR(data->label))
+                       goto out_free;
+
                data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_CREATE];
                data->msg.rpc_argp = &data->arg;
                data->msg.rpc_resp = &data->res;
@@ -3266,13 +3458,17 @@ static struct nfs4_createdata *nfs4_alloc_createdata(struct inode *dir,
                data->arg.name = name;
                data->arg.attrs = sattr;
                data->arg.ftype = ftype;
-               data->arg.bitmask = server->attr_bitmask;
+               data->arg.bitmask = nfs4_bitmask(server, data->label);
                data->res.server = server;
                data->res.fh = &data->fh;
                data->res.fattr = &data->fattr;
+               data->res.label = data->label;
                nfs_fattr_init(data->res.fattr);
        }
        return data;
+out_free:
+       kfree(data);
+       return NULL;
 }
 
 static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_createdata *data)
@@ -3281,18 +3477,20 @@ static int nfs4_do_create(struct inode *dir, struct dentry *dentry, struct nfs4_
                                    &data->arg.seq_args, &data->res.seq_res, 1);
        if (status == 0) {
                update_changeattr(dir, &data->res.dir_cinfo);
-               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
+               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, data->res.label);
        }
        return status;
 }
 
 static void nfs4_free_createdata(struct nfs4_createdata *data)
 {
+       nfs4_label_free(data->label);
        kfree(data);
 }
 
 static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
-               struct page *page, unsigned int len, struct iattr *sattr)
+               struct page *page, unsigned int len, struct iattr *sattr,
+               struct nfs4_label *label)
 {
        struct nfs4_createdata *data;
        int status = -ENAMETOOLONG;
@@ -3308,6 +3506,7 @@ static int _nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
        data->msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SYMLINK];
        data->arg.u.symlink.pages = &page;
        data->arg.u.symlink.len = len;
+       data->arg.label = label;
        
        status = nfs4_do_create(dir, dentry, data);
 
@@ -3320,18 +3519,24 @@ static int nfs4_proc_symlink(struct inode *dir, struct dentry *dentry,
                struct page *page, unsigned int len, struct iattr *sattr)
 {
        struct nfs4_exception exception = { };
+       struct nfs4_label l, *label = NULL;
        int err;
+
+       label = nfs4_label_init_security(dir, dentry, sattr, &l);
+
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
                                _nfs4_proc_symlink(dir, dentry, page,
-                                                       len, sattr),
+                                                       len, sattr, label),
                                &exception);
        } while (exception.retry);
+
+       nfs4_label_release_security(label);
        return err;
 }
 
 static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
-               struct iattr *sattr)
+               struct iattr *sattr, struct nfs4_label *label)
 {
        struct nfs4_createdata *data;
        int status = -ENOMEM;
@@ -3340,6 +3545,7 @@ static int _nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
        if (data == NULL)
                goto out;
 
+       data->arg.label = label;
        status = nfs4_do_create(dir, dentry, data);
 
        nfs4_free_createdata(data);
@@ -3351,14 +3557,19 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
                struct iattr *sattr)
 {
        struct nfs4_exception exception = { };
+       struct nfs4_label l, *label = NULL;
        int err;
 
+       label = nfs4_label_init_security(dir, dentry, sattr, &l);
+
        sattr->ia_mode &= ~current_umask();
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
-                               _nfs4_proc_mkdir(dir, dentry, sattr),
+                               _nfs4_proc_mkdir(dir, dentry, sattr, label),
                                &exception);
        } while (exception.retry);
+       nfs4_label_release_security(label);
+
        return err;
 }
 
@@ -3416,7 +3627,7 @@ static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
 }
 
 static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
-               struct iattr *sattr, dev_t rdev)
+               struct iattr *sattr, struct nfs4_label *label, dev_t rdev)
 {
        struct nfs4_createdata *data;
        int mode = sattr->ia_mode;
@@ -3441,7 +3652,8 @@ static int _nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
                status = -EINVAL;
                goto out_free;
        }
-       
+
+       data->arg.label = label;
        status = nfs4_do_create(dir, dentry, data);
 out_free:
        nfs4_free_createdata(data);
@@ -3453,14 +3665,20 @@ static int nfs4_proc_mknod(struct inode *dir, struct dentry *dentry,
                struct iattr *sattr, dev_t rdev)
 {
        struct nfs4_exception exception = { };
+       struct nfs4_label l, *label = NULL;
        int err;
 
+       label = nfs4_label_init_security(dir, dentry, sattr, &l);
+
        sattr->ia_mode &= ~current_umask();
        do {
                err = nfs4_handle_exception(NFS_SERVER(dir),
-                               _nfs4_proc_mknod(dir, dentry, sattr, rdev),
+                               _nfs4_proc_mknod(dir, dentry, sattr, label, rdev),
                                &exception);
        } while (exception.retry);
+
+       nfs4_label_release_security(label);
+
        return err;
 }
 
@@ -4187,6 +4405,155 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
        return err;
 }
 
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+static int _nfs4_get_security_label(struct inode *inode, void *buf,
+                                       size_t buflen)
+{
+       struct nfs_server *server = NFS_SERVER(inode);
+       struct nfs_fattr fattr;
+       struct nfs4_label label = {0, 0, buflen, buf};
+
+       u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL };
+       struct nfs4_getattr_arg args = {
+               .fh             = NFS_FH(inode),
+               .bitmask        = bitmask,
+       };
+       struct nfs4_getattr_res res = {
+               .fattr          = &fattr,
+               .label          = &label,
+               .server         = server,
+       };
+       struct rpc_message msg = {
+               .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_GETATTR],
+               .rpc_argp       = &args,
+               .rpc_resp       = &res,
+       };
+       int ret;
+
+       nfs_fattr_init(&fattr);
+
+       ret = rpc_call_sync(server->client, &msg, 0);
+       if (ret)
+               return ret;
+       if (!(fattr.valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL))
+               return -ENOENT;
+       if (buflen < label.len)
+               return -ERANGE;
+       return 0;
+}
+
+static int nfs4_get_security_label(struct inode *inode, void *buf,
+                                       size_t buflen)
+{
+       struct nfs4_exception exception = { };
+       int err;
+
+       if (!nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL))
+               return -EOPNOTSUPP;
+
+       do {
+               err = nfs4_handle_exception(NFS_SERVER(inode),
+                               _nfs4_get_security_label(inode, buf, buflen),
+                               &exception);
+       } while (exception.retry);
+       return err;
+}
+
+static int _nfs4_do_set_security_label(struct inode *inode,
+               struct nfs4_label *ilabel,
+               struct nfs_fattr *fattr,
+               struct nfs4_label *olabel)
+{
+
+       struct iattr sattr = {0};
+       struct nfs_server *server = NFS_SERVER(inode);
+       const u32 bitmask[3] = { 0, 0, FATTR4_WORD2_SECURITY_LABEL };
+       struct nfs_setattrargs args = {
+               .fh             = NFS_FH(inode),
+               .iap            = &sattr,
+               .server         = server,
+               .bitmask        = bitmask,
+               .label          = ilabel,
+       };
+       struct nfs_setattrres res = {
+               .fattr          = fattr,
+               .label          = olabel,
+               .server         = server,
+       };
+       struct rpc_message msg = {
+               .rpc_proc       = &nfs4_procedures[NFSPROC4_CLNT_SETATTR],
+               .rpc_argp       = &args,
+               .rpc_resp       = &res,
+       };
+       int status;
+
+       nfs4_stateid_copy(&args.stateid, &zero_stateid);
+
+       status = rpc_call_sync(server->client, &msg, 0);
+       if (status)
+               dprintk("%s failed: %d\n", __func__, status);
+
+       return status;
+}
+
+static int nfs4_do_set_security_label(struct inode *inode,
+               struct nfs4_label *ilabel,
+               struct nfs_fattr *fattr,
+               struct nfs4_label *olabel)
+{
+       struct nfs4_exception exception = { };
+       int err;
+
+       do {
+               err = nfs4_handle_exception(NFS_SERVER(inode),
+                               _nfs4_do_set_security_label(inode, ilabel,
+                               fattr, olabel),
+                               &exception);
+       } while (exception.retry);
+       return err;
+}
+
+static int
+nfs4_set_security_label(struct dentry *dentry, const void *buf, size_t buflen)
+{
+       struct nfs4_label ilabel, *olabel = NULL;
+       struct nfs_fattr fattr;
+       struct rpc_cred *cred;
+       struct inode *inode = dentry->d_inode;
+       int status;
+
+       if (!nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL))
+               return -EOPNOTSUPP;
+
+       nfs_fattr_init(&fattr);
+
+       ilabel.pi = 0;
+       ilabel.lfs = 0;
+       ilabel.label = (char *)buf;
+       ilabel.len = buflen;
+
+       cred = rpc_lookup_cred();
+       if (IS_ERR(cred))
+               return PTR_ERR(cred);
+
+       olabel = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL);
+       if (IS_ERR(olabel)) {
+               status = -PTR_ERR(olabel);
+               goto out;
+       }
+
+       status = nfs4_do_set_security_label(inode, &ilabel, &fattr, olabel);
+       if (status == 0)
+               nfs_setsecurity(inode, &fattr, olabel);
+
+       nfs4_label_free(olabel);
+out:
+       put_rpccred(cred);
+       return status;
+}
+#endif /* CONFIG_NFS_V4_SECURITY_LABEL */
+
+
 static int
 nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
 {
@@ -4345,7 +4712,7 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
        /* cb_client4 */
        rcu_read_lock();
        setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
-                               sizeof(setclientid.sc_netid),
+                               sizeof(setclientid.sc_netid), "%s",
                                rpc_peeraddr2str(clp->cl_rpcclient,
                                                        RPC_DISPLAY_NETID));
        rcu_read_unlock();
@@ -5056,13 +5423,18 @@ static int nfs41_check_expired_locks(struct nfs4_state *state)
 
        list_for_each_entry(lsp, &state->lock_states, ls_locks) {
                if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {
-                       status = nfs41_test_stateid(server, &lsp->ls_stateid);
+                       struct rpc_cred *cred = lsp->ls_state->owner->so_cred;
+
+                       status = nfs41_test_stateid(server,
+                                       &lsp->ls_stateid,
+                                       cred);
                        if (status != NFS_OK) {
                                /* Free the stateid unless the server
                                 * informs us the stateid is unrecognized. */
                                if (status != -NFS4ERR_BAD_STATEID)
                                        nfs41_free_stateid(server,
-                                                       &lsp->ls_stateid);
+                                                       &lsp->ls_stateid,
+                                                       cred);
                                clear_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags);
                                ret = status;
                        }
@@ -5295,6 +5667,53 @@ static size_t nfs4_xattr_list_nfs4_acl(struct dentry *dentry, char *list,
        return len;
 }
 
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+static inline int nfs4_server_supports_labels(struct nfs_server *server)
+{
+       return server->caps & NFS_CAP_SECURITY_LABEL;
+}
+
+static int nfs4_xattr_set_nfs4_label(struct dentry *dentry, const char *key,
+                                  const void *buf, size_t buflen,
+                                  int flags, int type)
+{
+       if (security_ismaclabel(key))
+               return nfs4_set_security_label(dentry, buf, buflen);
+
+       return -EOPNOTSUPP;
+}
+
+static int nfs4_xattr_get_nfs4_label(struct dentry *dentry, const char *key,
+                                  void *buf, size_t buflen, int type)
+{
+       if (security_ismaclabel(key))
+               return nfs4_get_security_label(dentry->d_inode, buf, buflen);
+       return -EOPNOTSUPP;
+}
+
+static size_t nfs4_xattr_list_nfs4_label(struct dentry *dentry, char *list,
+                                      size_t list_len, const char *name,
+                                      size_t name_len, int type)
+{
+       size_t len = 0;
+
+       if (nfs_server_capable(dentry->d_inode, NFS_CAP_SECURITY_LABEL)) {
+               len = security_inode_listsecurity(dentry->d_inode, NULL, 0);
+               if (list && len <= list_len)
+                       security_inode_listsecurity(dentry->d_inode, list, len);
+       }
+       return len;
+}
+
+static const struct xattr_handler nfs4_xattr_nfs4_label_handler = {
+       .prefix = XATTR_SECURITY_PREFIX,
+       .list   = nfs4_xattr_list_nfs4_label,
+       .get    = nfs4_xattr_get_nfs4_label,
+       .set    = nfs4_xattr_set_nfs4_label,
+};
+#endif
+
+
 /*
  * nfs_fhget will use either the mounted_on_fileid or the fileid
  */
@@ -5318,7 +5737,7 @@ static int _nfs4_proc_fs_locations(struct rpc_clnt *client, struct inode *dir,
                                   struct page *page)
 {
        struct nfs_server *server = NFS_SERVER(dir);
-       u32 bitmask[2] = {
+       u32 bitmask[3] = {
                [0] = FATTR4_WORD0_FSID | FATTR4_WORD0_FS_LOCATIONS,
        };
        struct nfs4_fs_locations_arg args = {
@@ -5505,7 +5924,8 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
        struct nfs41_exchange_id_args args = {
                .verifier = &verifier,
                .client = clp,
-               .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER,
+               .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
+                       EXCHGID4_FLAG_BIND_PRINC_STATEID,
        };
        struct nfs41_exchange_id_res res = {
                0
@@ -5762,17 +6182,14 @@ int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo)
  */
 static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args)
 {
-       struct nfs4_session *session = args->client->cl_session;
-       unsigned int mxrqst_sz = session->fc_target_max_rqst_sz,
-                    mxresp_sz = session->fc_target_max_resp_sz;
+       unsigned int max_rqst_sz, max_resp_sz;
+
+       max_rqst_sz = NFS_MAX_FILE_IO_SIZE + nfs41_maxwrite_overhead;
+       max_resp_sz = NFS_MAX_FILE_IO_SIZE + nfs41_maxread_overhead;
 
-       if (mxrqst_sz == 0)
-               mxrqst_sz = NFS_MAX_FILE_IO_SIZE;
-       if (mxresp_sz == 0)
-               mxresp_sz = NFS_MAX_FILE_IO_SIZE;
        /* Fore channel attributes */
-       args->fc_attrs.max_rqst_sz = mxrqst_sz;
-       args->fc_attrs.max_resp_sz = mxresp_sz;
+       args->fc_attrs.max_rqst_sz = max_rqst_sz;
+       args->fc_attrs.max_resp_sz = max_resp_sz;
        args->fc_attrs.max_ops = NFS4_MAX_OPS;
        args->fc_attrs.max_reqs = max_session_slots;
 
@@ -6159,12 +6576,14 @@ static const struct rpc_call_ops nfs4_reclaim_complete_call_ops = {
 /*
  * Issue a global reclaim complete.
  */
-static int nfs41_proc_reclaim_complete(struct nfs_client *clp)
+static int nfs41_proc_reclaim_complete(struct nfs_client *clp,
+               struct rpc_cred *cred)
 {
        struct nfs4_reclaim_complete_data *calldata;
        struct rpc_task *task;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RECLAIM_COMPLETE],
+               .rpc_cred = cred,
        };
        struct rpc_task_setup task_setup_data = {
                .rpc_client = clp->cl_rpcclient,
@@ -6348,6 +6767,7 @@ nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET],
                .rpc_argp = &lgp->args,
                .rpc_resp = &lgp->res,
+               .rpc_cred = lgp->cred,
        };
        struct rpc_task_setup task_setup_data = {
                .rpc_client = server->client,
@@ -6451,6 +6871,7 @@ int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp)
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTRETURN],
                .rpc_argp = &lrp->args,
                .rpc_resp = &lrp->res,
+               .rpc_cred = lrp->cred,
        };
        struct rpc_task_setup task_setup_data = {
                .rpc_client = lrp->clp->cl_rpcclient,
@@ -6520,7 +6941,9 @@ int nfs4_proc_getdevicelist(struct nfs_server *server,
 EXPORT_SYMBOL_GPL(nfs4_proc_getdevicelist);
 
 static int
-_nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
+_nfs4_proc_getdeviceinfo(struct nfs_server *server,
+               struct pnfs_device *pdev,
+               struct rpc_cred *cred)
 {
        struct nfs4_getdeviceinfo_args args = {
                .pdev = pdev,
@@ -6532,6 +6955,7 @@ _nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICEINFO],
                .rpc_argp = &args,
                .rpc_resp = &res,
+               .rpc_cred = cred,
        };
        int status;
 
@@ -6542,14 +6966,16 @@ _nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
        return status;
 }
 
-int nfs4_proc_getdeviceinfo(struct nfs_server *server, struct pnfs_device *pdev)
+int nfs4_proc_getdeviceinfo(struct nfs_server *server,
+               struct pnfs_device *pdev,
+               struct rpc_cred *cred)
 {
        struct nfs4_exception exception = { };
        int err;
 
        do {
                err = nfs4_handle_exception(server,
-                                       _nfs4_proc_getdeviceinfo(server, pdev),
+                                       _nfs4_proc_getdeviceinfo(server, pdev, cred),
                                        &exception);
        } while (exception.retry);
        return err;
@@ -6733,7 +7159,9 @@ out:
        return err;
 }
 
-static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
+static int _nfs41_test_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
 {
        int status;
        struct nfs41_test_stateid_args args = {
@@ -6744,6 +7172,7 @@ static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_TEST_STATEID],
                .rpc_argp = &args,
                .rpc_resp = &res,
+               .rpc_cred = cred,
        };
 
        dprintk("NFS call  test_stateid %p\n", stateid);
@@ -6764,17 +7193,20 @@ static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
  *
  * @server: server / transport on which to perform the operation
  * @stateid: state ID to test
+ * @cred: credential
  *
  * Returns NFS_OK if the server recognizes that "stateid" is valid.
  * Otherwise a negative NFS4ERR value is returned if the operation
  * failed or the state ID is not currently valid.
  */
-static int nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
+static int nfs41_test_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
 {
        struct nfs4_exception exception = { };
        int err;
        do {
-               err = _nfs41_test_stateid(server, stateid);
+               err = _nfs41_test_stateid(server, stateid, cred);
                if (err != -NFS4ERR_DELAY)
                        break;
                nfs4_handle_exception(server, err, &exception);
@@ -6823,10 +7255,12 @@ const struct rpc_call_ops nfs41_free_stateid_ops = {
 
 static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server,
                nfs4_stateid *stateid,
+               struct rpc_cred *cred,
                bool privileged)
 {
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FREE_STATEID],
+               .rpc_cred = cred,
        };
        struct rpc_task_setup task_setup = {
                .rpc_client = server->client,
@@ -6859,16 +7293,19 @@ static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server,
  *
  * @server: server / transport on which to perform the operation
  * @stateid: state ID to release
+ * @cred: credential
  *
  * Returns NFS_OK if the server freed "stateid".  Otherwise a
  * negative NFS4ERR value is returned.
  */
-static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
+static int nfs41_free_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
 {
        struct rpc_task *task;
        int ret;
 
-       task = _nfs41_free_stateid(server, stateid, true);
+       task = _nfs41_free_stateid(server, stateid, cred, true);
        if (IS_ERR(task))
                return PTR_ERR(task);
        ret = rpc_wait_for_completion_task(task);
@@ -6881,8 +7318,9 @@ static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
 static int nfs41_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
 {
        struct rpc_task *task;
+       struct rpc_cred *cred = lsp->ls_state->owner->so_cred;
 
-       task = _nfs41_free_stateid(server, &lsp->ls_stateid, false);
+       task = _nfs41_free_stateid(server, &lsp->ls_stateid, cred, false);
        nfs4_free_lock_state(server, lsp);
        if (IS_ERR(task))
                return PTR_ERR(task);
@@ -7004,11 +7442,33 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
 };
 #endif
 
+#if defined(CONFIG_NFS_V4_2)
+static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
+       .minor_version = 2,
+       .init_caps = NFS_CAP_READDIRPLUS
+               | NFS_CAP_ATOMIC_OPEN
+               | NFS_CAP_CHANGE_ATTR
+               | NFS_CAP_POSIX_LOCK
+               | NFS_CAP_STATEID_NFSV41
+               | NFS_CAP_ATOMIC_OPEN_V1,
+       .call_sync = nfs4_call_sync_sequence,
+       .match_stateid = nfs41_match_stateid,
+       .find_root_sec = nfs41_find_root_sec,
+       .free_lock_state = nfs41_free_lock_state,
+       .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
+       .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
+       .state_renewal_ops = &nfs41_state_renewal_ops,
+};
+#endif
+
 const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
        [0] = &nfs_v4_0_minor_ops,
 #if defined(CONFIG_NFS_V4_1)
        [1] = &nfs_v4_1_minor_ops,
 #endif
+#if defined(CONFIG_NFS_V4_2)
+       [2] = &nfs_v4_2_minor_ops,
+#endif
 };
 
 const struct inode_operations nfs4_dir_inode_operations = {
@@ -7108,6 +7568,9 @@ static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
 
 const struct xattr_handler *nfs4_xattr_handlers[] = {
        &nfs4_xattr_nfs4_acl_handler,
+#ifdef CONFIG_NFS_V4_SECURITY_LABEL
+       &nfs4_xattr_nfs4_label_handler,
+#endif
        NULL
 };