Merge tag 'nfs-for-3.11-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 Jul 2013 19:09:43 +0000 (12:09 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 Jul 2013 19:09:43 +0000 (12:09 -0700)
Pull NFS client updates from Trond Myklebust:
 "Feature highlights include:
   - Add basic client support for NFSv4.2
   - Add basic client support for Labeled NFS (selinux for NFSv4.2)
   - Fix the use of credentials in NFSv4.1 stateful operations, and add
     support for NFSv4.1 state protection.

  Bugfix highlights:
   - Fix another NFSv4 open state recovery race
   - Fix an NFSv4.1 back channel session regression
   - Various rpc_pipefs races
   - Fix another issue with NFSv3 auth negotiation

  Please note that Labeled NFS does require some additional support from
  the security subsystem.  The relevant changesets have all been
  reviewed and acked by James Morris."

* tag 'nfs-for-3.11-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (54 commits)
  NFS: Set NFS_CS_MIGRATION for NFSv4 mounts
  NFSv4.1 Refactor nfs4_init_session and nfs4_init_channel_attrs
  nfs: have NFSv3 try server-specified auth flavors in turn
  nfs: have nfs_mount fake up a auth_flavs list when the server didn't provide it
  nfs: move server_authlist into nfs_try_mount_request
  nfs: refactor "need_mount" code out of nfs_try_mount
  SUNRPC: PipeFS MOUNT notification optimization for dying clients
  SUNRPC: split client creation routine into setup and registration
  SUNRPC: fix races on PipeFS UMOUNT notifications
  SUNRPC: fix races on PipeFS MOUNT notifications
  NFSv4.1 use pnfs_device maxcount for the objectlayout gdia_maxcount
  NFSv4.1 use pnfs_device maxcount for the blocklayout gdia_maxcount
  NFSv4.1 Fix gdia_maxcount calculation to fit in ca_maxresponsesize
  NFS: Improve legacy idmapping fallback
  NFSv4.1 end back channel session draining
  NFS: Apply v4.1 capabilities to v4.2
  NFSv4.1: Clean up layout segment comparison helper names
  NFSv4.1: layout segment comparison helpers should take 'const' parameters
  NFSv4: Move the DNS resolver into the NFSv4 module
  rpc_pipefs: only set rpc_dentry_ops if d_op isn't already set
  ...

1  2 
fs/nfs/callback.c
fs/nfs/dir.c
fs/nfs/inode.c
fs/nfs/nfs3proc.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
include/linux/security.h
net/sunrpc/sched.c
security/selinux/hooks.c
security/smack/smack_lsm.c

diff --combined fs/nfs/callback.c
@@@ -211,6 -211,7 +211,6 @@@ static int nfs_callback_start_svc(int m
        struct svc_rqst *rqstp;
        int (*callback_svc)(void *vrqstp);
        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
 -      char svc_name[12];
        int ret;
  
        nfs_callback_bc_serv(minorversion, xprt, serv);
  
        svc_sock_update_bufs(serv);
  
 -      sprintf(svc_name, "nfsv4.%u-svc", minorversion);
        cb_info->serv = serv;
        cb_info->rqst = rqstp;
 -      cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name);
 +      cb_info->task = kthread_run(callback_svc, cb_info->rqst,
 +                                  "nfsv4.%u-svc", minorversion);
        if (IS_ERR(cb_info->task)) {
                ret = PTR_ERR(cb_info->task);
                svc_exit_thread(cb_info->rqst);
@@@ -281,6 -282,7 +281,7 @@@ static int nfs_callback_up_net(int mino
                        ret = nfs4_callback_up_net(serv, net);
                        break;
                case 1:
+               case 2:
                        ret = nfs41_callback_up_net(serv, net);
                        break;
                default:
diff --combined fs/nfs/dir.c
@@@ -33,7 -33,6 +33,7 @@@
  #include <linux/pagevec.h>
  #include <linux/namei.h>
  #include <linux/mount.h>
 +#include <linux/swap.h>
  #include <linux/sched.h>
  #include <linux/kmemleak.h>
  #include <linux/xattr.h>
@@@ -47,7 -46,7 +47,7 @@@
  
  static int nfs_opendir(struct inode *, struct file *);
  static int nfs_closedir(struct inode *, struct file *);
 -static int nfs_readdir(struct file *, void *, filldir_t);
 +static int nfs_readdir(struct file *, struct dir_context *);
  static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
  static loff_t nfs_llseek_dir(struct file *, loff_t, int);
  static void nfs_readdir_clear_array(struct page*);
@@@ -55,7 -54,7 +55,7 @@@
  const struct file_operations nfs_dir_operations = {
        .llseek         = nfs_llseek_dir,
        .read           = generic_read_dir,
 -      .readdir        = nfs_readdir,
 +      .iterate        = nfs_readdir,
        .open           = nfs_opendir,
        .release        = nfs_closedir,
        .fsync          = nfs_fsync_dir,
@@@ -148,7 -147,6 +148,7 @@@ typedef int (*decode_dirent_t)(struct x
  typedef struct {
        struct file     *file;
        struct page     *page;
 +      struct dir_context *ctx;
        unsigned long   page_index;
        u64             *dir_cookie;
        u64             last_cookie;
@@@ -254,7 -252,7 +254,7 @@@ out
  static
  int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc)
  {
 -      loff_t diff = desc->file->f_pos - desc->current_index;
 +      loff_t diff = desc->ctx->pos - desc->current_index;
        unsigned int index;
  
        if (diff < 0)
@@@ -291,7 -289,7 +291,7 @@@ int nfs_readdir_search_for_cookie(struc
                            || (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))) {
                                ctx->duped = 0;
                                ctx->attr_gencount = nfsi->attr_gencount;
 -                      } else if (new_pos < desc->file->f_pos) {
 +                      } else if (new_pos < desc->ctx->pos) {
                                if (ctx->duped > 0
                                    && ctx->dup_cookie == *desc->dir_cookie) {
                                        if (printk_ratelimit()) {
                                ctx->dup_cookie = *desc->dir_cookie;
                                ctx->duped = -1;
                        }
 -                      desc->file->f_pos = new_pos;
 +                      desc->ctx->pos = new_pos;
                        desc->cache_entry_index = i;
                        return 0;
                }
@@@ -407,13 -405,13 +407,13 @@@ different
  }
  
  static
 -bool nfs_use_readdirplus(struct inode *dir, struct file *filp)
 +bool nfs_use_readdirplus(struct inode *dir, struct dir_context *ctx)
  {
        if (!nfs_server_capable(dir, NFS_CAP_READDIRPLUS))
                return false;
        if (test_and_clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags))
                return true;
 -      if (filp->f_pos == 0)
 +      if (ctx->pos == 0)
                return true;
        return false;
  }
@@@ -437,6 -435,7 +437,7 @@@ void nfs_prime_dcache(struct dentry *pa
        struct dentry *alias;
        struct inode *dir = parent->d_inode;
        struct inode *inode;
+       int status;
  
        if (filename.name[0] == '.') {
                if (filename.len == 1)
        dentry = d_lookup(parent, &filename);
        if (dentry != NULL) {
                if (nfs_same_file(dentry, entry)) {
-                       nfs_refresh_inode(dentry->d_inode, entry->fattr);
+                       status = nfs_refresh_inode(dentry->d_inode, entry->fattr);
+                       if (!status)
+                               nfs_setsecurity(dentry->d_inode, entry->fattr, entry->label);
                        goto out;
                } else {
                        if (d_invalidate(dentry) != 0)
        if (dentry == NULL)
                return;
  
-       inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr);
+       inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr, entry->label);
        if (IS_ERR(inode))
                goto out;
  
@@@ -587,10 -588,16 +590,16 @@@ int nfs_readdir_xdr_to_array(nfs_readdi
        if (entry.fh == NULL || entry.fattr == NULL)
                goto out;
  
+       entry.label = nfs4_label_alloc(NFS_SERVER(inode), GFP_NOWAIT);
+       if (IS_ERR(entry.label)) {
+               status = PTR_ERR(entry.label);
+               goto out;
+       }
        array = nfs_readdir_get_array(page);
        if (IS_ERR(array)) {
                status = PTR_ERR(array);
-               goto out;
+               goto out_label_free;
        }
        memset(array, 0, sizeof(struct nfs_cache_array));
        array->eof_index = -1;
        nfs_readdir_free_large_page(pages_ptr, pages, array_size);
  out_release_array:
        nfs_readdir_release_array(page);
+ out_label_free:
+       nfs4_label_free(entry.label);
  out:
        nfs_free_fattr(entry.fattr);
        nfs_free_fhandle(entry.fh);
@@@ -704,7 -713,8 +715,7 @@@ int readdir_search_pagecache(nfs_readdi
   * Once we've found the start of the dirent within a page: fill 'er up...
   */
  static 
 -int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
 -                 filldir_t filldir)
 +int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
  {
        struct file     *file = desc->file;
        int i = 0;
                struct nfs_cache_array_entry *ent;
  
                ent = &array->array[i];
 -              if (filldir(dirent, ent->string.name, ent->string.len,
 -                  file->f_pos, nfs_compat_user_ino64(ent->ino),
 -                  ent->d_type) < 0) {
 +              if (!dir_emit(desc->ctx, ent->string.name, ent->string.len,
 +                  nfs_compat_user_ino64(ent->ino), ent->d_type)) {
                        desc->eof = 1;
                        break;
                }
 -              file->f_pos++;
 +              desc->ctx->pos++;
                if (i < (array->size-1))
                        *desc->dir_cookie = array->array[i+1].cookie;
                else
@@@ -759,7 -770,8 +770,7 @@@ out
   *     directory in the page cache by the time we get here.
   */
  static inline
 -int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
 -                   filldir_t filldir)
 +int uncached_readdir(nfs_readdir_descriptor_t *desc)
  {
        struct page     *page = NULL;
        int             status;
        if (status < 0)
                goto out_release;
  
 -      status = nfs_do_filldir(desc, dirent, filldir);
 +      status = nfs_do_filldir(desc);
  
   out:
        dfprintk(DIRCACHE, "NFS: %s: returns %d\n",
     last cookie cache takes care of the common case of reading the
     whole directory.
   */
 -static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 +static int nfs_readdir(struct file *file, struct dir_context *ctx)
  {
 -      struct dentry   *dentry = filp->f_path.dentry;
 +      struct dentry   *dentry = file->f_path.dentry;
        struct inode    *inode = dentry->d_inode;
        nfs_readdir_descriptor_t my_desc,
                        *desc = &my_desc;
 -      struct nfs_open_dir_context *dir_ctx = filp->private_data;
 +      struct nfs_open_dir_context *dir_ctx = file->private_data;
        int res;
  
        dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n",
                        dentry->d_parent->d_name.name, dentry->d_name.name,
 -                      (long long)filp->f_pos);
 +                      (long long)ctx->pos);
        nfs_inc_stats(inode, NFSIOS_VFSGETDENTS);
  
        /*
 -       * filp->f_pos points to the dirent entry number.
 +       * ctx->pos points to the dirent entry number.
         * *desc->dir_cookie has the cookie for the next entry. We have
         * to either find the entry with the appropriate number or
         * revalidate the cookie.
         */
        memset(desc, 0, sizeof(*desc));
  
 -      desc->file = filp;
 +      desc->file = file;
 +      desc->ctx = ctx;
        desc->dir_cookie = &dir_ctx->dir_cookie;
        desc->decode = NFS_PROTO(inode)->decode_dirent;
 -      desc->plus = nfs_use_readdirplus(inode, filp) ? 1 : 0;
 +      desc->plus = nfs_use_readdirplus(inode, ctx) ? 1 : 0;
  
        nfs_block_sillyrename(dentry);
 -      res = nfs_revalidate_mapping(inode, filp->f_mapping);
 +      res = nfs_revalidate_mapping(inode, file->f_mapping);
        if (res < 0)
                goto out;
  
                        /* This means either end of directory */
                        if (*desc->dir_cookie && desc->eof == 0) {
                                /* Or that the server has 'lost' a cookie */
 -                              res = uncached_readdir(desc, dirent, filldir);
 +                              res = uncached_readdir(desc);
                                if (res == 0)
                                        continue;
                        }
                if (res < 0)
                        break;
  
 -              res = nfs_do_filldir(desc, dirent, filldir);
 +              res = nfs_do_filldir(desc);
                if (res < 0)
                        break;
        } while (!desc->eof);
@@@ -1040,6 -1051,7 +1051,7 @@@ static int nfs_lookup_revalidate(struc
        struct dentry *parent;
        struct nfs_fh *fhandle = NULL;
        struct nfs_fattr *fattr = NULL;
+       struct nfs4_label *label = NULL;
        int error;
  
        if (flags & LOOKUP_RCU)
        if (fhandle == NULL || fattr == NULL)
                goto out_error;
  
-       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
+       label = nfs4_label_alloc(NFS_SERVER(inode), GFP_NOWAIT);
+       if (IS_ERR(label))
+               goto out_error;
+       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
        if (error)
                goto out_bad;
        if (nfs_compare_fh(NFS_FH(inode), fhandle))
        if ((error = nfs_refresh_inode(inode, fattr)) != 0)
                goto out_bad;
  
+       nfs_setsecurity(inode, fattr, label);
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
+       nfs4_label_free(label);
  out_set_verifier:
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
   out_valid:
@@@ -1108,6 -1128,7 +1128,7 @@@ out_zap_parent
   out_bad:
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
+       nfs4_label_free(label);
        nfs_mark_for_revalidate(dir);
        if (inode && S_ISDIR(inode->i_mode)) {
                /* Purge readdir caches. */
  out_error:
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
+       nfs4_label_free(label);
        dput(parent);
        dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) lookup returned error %d\n",
                        __func__, dentry->d_parent->d_name.name,
@@@ -1256,6 -1278,7 +1278,7 @@@ struct dentry *nfs_lookup(struct inode 
        struct inode *inode = NULL;
        struct nfs_fh *fhandle = NULL;
        struct nfs_fattr *fattr = NULL;
+       struct nfs4_label *label = NULL;
        int error;
  
        dfprintk(VFS, "NFS: lookup(%s/%s)\n",
        if (fhandle == NULL || fattr == NULL)
                goto out;
  
+       label = nfs4_label_alloc(NFS_SERVER(dir), GFP_NOWAIT);
+       if (IS_ERR(label))
+               goto out;
        parent = dentry->d_parent;
        /* Protect against concurrent sillydeletes */
        nfs_block_sillyrename(parent);
-       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
+       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
        if (error == -ENOENT)
                goto no_entry;
        if (error < 0) {
                res = ERR_PTR(error);
                goto out_unblock_sillyrename;
        }
-       inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
+       inode = nfs_fhget(dentry->d_sb, fhandle, fattr, label);
        res = ERR_CAST(inode);
        if (IS_ERR(res))
                goto out_unblock_sillyrename;
@@@ -1310,6 -1337,7 +1337,7 @@@ no_entry
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
  out_unblock_sillyrename:
        nfs_unblock_sillyrename(parent);
+       nfs4_label_free(label);
  out:
        nfs_free_fattr(fattr);
        nfs_free_fhandle(fhandle);
@@@ -1357,18 -1385,6 +1385,6 @@@ static int nfs_finish_open(struct nfs_o
  {
        int err;
  
-       if (ctx->dentry != dentry) {
-               dput(ctx->dentry);
-               ctx->dentry = dget(dentry);
-       }
-       /* If the open_intent is for execute, we have an extra check to make */
-       if (ctx->mode & FMODE_EXEC) {
-               err = nfs_may_open(dentry->d_inode, ctx->cred, open_flags);
-               if (err < 0)
-                       goto out;
-       }
        err = finish_open(file, dentry, do_open, opened);
        if (err)
                goto out;
@@@ -1427,13 -1443,13 +1443,13 @@@ int nfs_atomic_open(struct inode *dir, 
  
        nfs_block_sillyrename(dentry->d_parent);
        inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr);
-       d_drop(dentry);
+       nfs_unblock_sillyrename(dentry->d_parent);
        if (IS_ERR(inode)) {
-               nfs_unblock_sillyrename(dentry->d_parent);
                put_nfs_open_context(ctx);
                err = PTR_ERR(inode);
                switch (err) {
                case -ENOENT:
+                       d_drop(dentry);
                        d_add(dentry, NULL);
                        break;
                case -EISDIR:
                }
                goto out;
        }
-       res = d_add_unique(dentry, inode);
-       if (res != NULL)
-               dentry = res;
-       nfs_unblock_sillyrename(dentry->d_parent);
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-       err = nfs_finish_open(ctx, dentry, file, open_flags, opened);
  
-       dput(res);
+       err = nfs_finish_open(ctx, ctx->dentry, file, open_flags, opened);
  out:
        return err;
  
@@@ -1528,7 -1536,8 +1536,8 @@@ no_open
   * Code common to create, mkdir, and mknod.
   */
  int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
-                               struct nfs_fattr *fattr)
+                               struct nfs_fattr *fattr,
+                               struct nfs4_label *label)
  {
        struct dentry *parent = dget_parent(dentry);
        struct inode *dir = parent->d_inode;
        if (dentry->d_inode)
                goto out;
        if (fhandle->size == 0) {
-               error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
+               error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, NULL);
                if (error)
                        goto out_error;
        }
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        if (!(fattr->valid & NFS_ATTR_FATTR)) {
                struct nfs_server *server = NFS_SB(dentry->d_sb);
-               error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr);
+               error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr, NULL);
                if (error < 0)
                        goto out_error;
        }
-       inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
+       inode = nfs_fhget(dentry->d_sb, fhandle, fattr, label);
        error = PTR_ERR(inode);
        if (IS_ERR(inode))
                goto out_error;
@@@ -1721,7 -1730,7 +1730,7 @@@ int nfs_unlink(struct inode *dir, struc
                dir->i_ino, dentry->d_name.name);
  
        spin_lock(&dentry->d_lock);
 -      if (dentry->d_count > 1) {
 +      if (d_count(dentry) > 1) {
                spin_unlock(&dentry->d_lock);
                /* Start asynchronous writeout of the inode */
                write_inode_now(dentry->d_inode, 0);
@@@ -1759,6 -1768,7 +1768,6 @@@ EXPORT_SYMBOL_GPL(nfs_unlink)
   */
  int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
  {
 -      struct pagevec lru_pvec;
        struct page *page;
        char *kaddr;
        struct iattr attr;
         * No big deal if we can't add this page to the page cache here.
         * READLINK will get the missing page from the server if needed.
         */
 -      pagevec_init(&lru_pvec, 0);
 -      if (!add_to_page_cache(page, dentry->d_inode->i_mapping, 0,
 +      if (!add_to_page_cache_lru(page, dentry->d_inode->i_mapping, 0,
                                                        GFP_KERNEL)) {
 -              pagevec_add(&lru_pvec, page);
 -              pagevec_lru_add_file(&lru_pvec);
                SetPageUptodate(page);
                unlock_page(page);
        } else
@@@ -1866,7 -1879,7 +1875,7 @@@ int nfs_rename(struct inode *old_dir, s
        dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
                 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
                 new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
 -               new_dentry->d_count);
 +               d_count(new_dentry));
  
        /*
         * For non-directories, check whether the target is busy and if so,
                        rehash = new_dentry;
                }
  
 -              if (new_dentry->d_count > 2) {
 +              if (d_count(new_dentry) > 2) {
                        int err;
  
                        /* copy the target dentry's name */
diff --combined fs/nfs/inode.c
@@@ -48,7 -48,6 +48,6 @@@
  #include "iostat.h"
  #include "internal.h"
  #include "fscache.h"
- #include "dns_resolve.h"
  #include "pnfs.h"
  #include "nfs.h"
  #include "netns.h"
@@@ -79,7 -78,7 +78,7 @@@ int nfs_wait_bit_killable(void *word
  {
        if (fatal_signal_pending(current))
                return -ERESTARTSYS;
 -      freezable_schedule();
 +      freezable_schedule_unsafe();
        return 0;
  }
  EXPORT_SYMBOL_GPL(nfs_wait_bit_killable);
@@@ -162,11 -161,19 +161,19 @@@ static void nfs_zap_caches_locked(struc
  
        memset(NFS_I(inode)->cookieverf, 0, sizeof(NFS_I(inode)->cookieverf));
        if (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)) {
-               nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
                nfs_fscache_invalidate(inode);
-       } else {
-               nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL|NFS_INO_REVAL_PAGECACHE;
-       }
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                                       | NFS_INO_INVALID_LABEL
+                                       | NFS_INO_INVALID_DATA
+                                       | NFS_INO_INVALID_ACCESS
+                                       | NFS_INO_INVALID_ACL
+                                       | NFS_INO_REVAL_PAGECACHE;
+       } else
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR
+                                       | NFS_INO_INVALID_LABEL
+                                       | NFS_INO_INVALID_ACCESS
+                                       | NFS_INO_INVALID_ACL
+                                       | NFS_INO_REVAL_PAGECACHE;
  }
  
  void nfs_zap_caches(struct inode *inode)
@@@ -257,12 -264,72 +264,72 @@@ nfs_init_locked(struct inode *inode, vo
        return 0;
  }
  
+ #ifdef CONFIG_NFS_V4_SECURITY_LABEL
+ void nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
+                                       struct nfs4_label *label)
+ {
+       int error;
+       if (label == NULL)
+               return;
+       if (nfs_server_capable(inode, NFS_CAP_SECURITY_LABEL) == 0)
+               return;
+       if (NFS_SERVER(inode)->nfs_client->cl_minorversion < 2)
+               return;
+       if ((fattr->valid & NFS_ATTR_FATTR_V4_SECURITY_LABEL) && inode->i_security) {
+               error = security_inode_notifysecctx(inode, label->label,
+                               label->len);
+               if (error)
+                       printk(KERN_ERR "%s() %s %d "
+                                       "security_inode_notifysecctx() %d\n",
+                                       __func__,
+                                       (char *)label->label,
+                                       label->len, error);
+       }
+ }
+ struct nfs4_label *nfs4_label_alloc(struct nfs_server *server, gfp_t flags)
+ {
+       struct nfs4_label *label = NULL;
+       int minor_version = server->nfs_client->cl_minorversion;
+       if (minor_version < 2)
+               return label;
+       if (!(server->caps & NFS_CAP_SECURITY_LABEL))
+               return label;
+       label = kzalloc(sizeof(struct nfs4_label), flags);
+       if (label == NULL)
+               return ERR_PTR(-ENOMEM);
+       label->label = kzalloc(NFS4_MAXLABELLEN, flags);
+       if (label->label == NULL) {
+               kfree(label);
+               return ERR_PTR(-ENOMEM);
+       }
+       label->len = NFS4_MAXLABELLEN;
+       return label;
+ }
+ EXPORT_SYMBOL_GPL(nfs4_label_alloc);
+ #else
+ void inline nfs_setsecurity(struct inode *inode, struct nfs_fattr *fattr,
+                                       struct nfs4_label *label)
+ {
+ }
+ #endif
+ EXPORT_SYMBOL_GPL(nfs_setsecurity);
  /*
   * This is our front-end to iget that looks up inodes by file handle
   * instead of inode number.
   */
  struct inode *
- nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
+ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr, struct nfs4_label *label)
  {
        struct nfs_find_desc desc = {
                .fh     = fh,
                         */
                        inode->i_blocks = nfs_calc_block_size(fattr->du.nfs3.used);
                }
+               nfs_setsecurity(inode, fattr, label);
                nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
                nfsi->attrtimeo_timestamp = now;
                nfsi->access_cache = RB_ROOT;
                unlock_new_inode(inode);
        } else
                nfs_refresh_inode(inode, fattr);
+               nfs_setsecurity(inode, fattr, label);
        dprintk("NFS: nfs_fhget(%s/%Ld fh_crc=0x%08x ct=%d)\n",
                inode->i_sb->s_id,
                (long long)NFS_FILEID(inode),
@@@ -449,7 -520,7 +520,7 @@@ nfs_setattr(struct dentry *dentry, stru
                NFS_PROTO(inode)->return_delegation(inode);
        error = NFS_PROTO(inode)->setattr(dentry, fattr, attr);
        if (error == 0)
-               nfs_refresh_inode(inode, fattr);
+               error = nfs_refresh_inode(inode, fattr);
        nfs_free_fattr(fattr);
  out:
        return error;
@@@ -713,16 -784,23 +784,23 @@@ EXPORT_SYMBOL_GPL(put_nfs_open_context)
   * Ensure that mmap has a recent RPC credential for use when writing out
   * shared pages
   */
- void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
+ void nfs_inode_attach_open_context(struct nfs_open_context *ctx)
  {
-       struct inode *inode = file_inode(filp);
+       struct inode *inode = ctx->dentry->d_inode;
        struct nfs_inode *nfsi = NFS_I(inode);
  
-       filp->private_data = get_nfs_open_context(ctx);
        spin_lock(&inode->i_lock);
        list_add(&ctx->list, &nfsi->open_files);
        spin_unlock(&inode->i_lock);
  }
+ EXPORT_SYMBOL_GPL(nfs_inode_attach_open_context);
+ void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
+ {
+       filp->private_data = get_nfs_open_context(ctx);
+       if (list_empty(&ctx->list))
+               nfs_inode_attach_open_context(ctx);
+ }
  EXPORT_SYMBOL_GPL(nfs_file_set_open_context);
  
  /*
@@@ -748,10 -826,11 +826,11 @@@ struct nfs_open_context *nfs_find_open_
  
  static void nfs_file_clear_open_context(struct file *filp)
  {
-       struct inode *inode = file_inode(filp);
        struct nfs_open_context *ctx = nfs_file_open_context(filp);
  
        if (ctx) {
+               struct inode *inode = ctx->dentry->d_inode;
                filp->private_data = NULL;
                spin_lock(&inode->i_lock);
                list_move_tail(&ctx->list, &NFS_I(inode)->open_files);
@@@ -790,6 -869,7 +869,7 @@@ in
  __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
  {
        int              status = -ESTALE;
+       struct nfs4_label *label = NULL;
        struct nfs_fattr *fattr = NULL;
        struct nfs_inode *nfsi = NFS_I(inode);
  
                goto out;
  
        nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE);
-       status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr);
+       label = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL);
+       if (IS_ERR(label)) {
+               status = PTR_ERR(label);
+               goto out;
+       }
+       status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr, label);
        if (status != 0) {
                dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
                         inode->i_sb->s_id,
                        if (!S_ISDIR(inode->i_mode))
                                set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
                }
-               goto out;
+               goto err_out;
        }
  
        status = nfs_refresh_inode(inode, fattr);
                dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n",
                         inode->i_sb->s_id,
                         (long long)NFS_FILEID(inode), status);
-               goto out;
+               goto err_out;
        }
  
        if (nfsi->cache_validity & NFS_INO_INVALID_ACL)
                inode->i_sb->s_id,
                (long long)NFS_FILEID(inode));
  
-  out:
+ err_out:
+       nfs4_label_free(label);
+ out:
        nfs_free_fattr(fattr);
        return status;
  }
@@@ -863,7 -952,8 +952,8 @@@ static int nfs_attribute_cache_expired(
   */
  int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
  {
-       if (!(NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATTR)
+       if (!(NFS_I(inode)->cache_validity &
+                       (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL))
                        && !nfs_attribute_cache_expired(inode))
                return NFS_STALE(inode) ? -ESTALE : 0;
        return __nfs_revalidate_inode(server, inode);
@@@ -1243,6 -1333,7 +1333,7 @@@ int nfs_post_op_update_inode(struct ino
        spin_lock(&inode->i_lock);
        status = nfs_post_op_update_inode_locked(inode, fattr);
        spin_unlock(&inode->i_lock);
        return status;
  }
  EXPORT_SYMBOL_GPL(nfs_post_op_update_inode);
@@@ -1483,7 -1574,7 +1574,7 @@@ static int nfs_update_inode(struct inod
                inode->i_blocks = fattr->du.nfs2.blocks;
  
        /* Update attrtimeo value if we're out of the unstable period */
-       if (invalid & NFS_INO_INVALID_ATTR) {
+       if (invalid & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL)) {
                nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
                nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
                nfsi->attrtimeo_timestamp = now;
                }
        }
        invalid &= ~NFS_INO_INVALID_ATTR;
+       invalid &= ~NFS_INO_INVALID_LABEL;
        /* Don't invalidate the data if we were to blame */
        if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
                                || S_ISLNK(inode->i_mode)))
@@@ -1638,12 -1730,11 +1730,11 @@@ EXPORT_SYMBOL_GPL(nfs_net_id)
  static int nfs_net_init(struct net *net)
  {
        nfs_clients_init(net);
-       return nfs_dns_resolver_cache_init(net);
+       return 0;
  }
  
  static void nfs_net_exit(struct net *net)
  {
-       nfs_dns_resolver_cache_destroy(net);
        nfs_cleanup_cb_ident_idr(net);
  }
  
@@@ -1661,10 -1752,6 +1752,6 @@@ static int __init init_nfs_fs(void
  {
        int err;
  
-       err = nfs_dns_resolver_init();
-       if (err < 0)
-               goto out10;;
        err = register_pernet_subsys(&nfs_net_ops);
        if (err < 0)
                goto out9;
@@@ -1730,8 -1817,6 +1817,6 @@@ out7
  out8:
        unregister_pernet_subsys(&nfs_net_ops);
  out9:
-       nfs_dns_resolver_destroy();
- out10:
        return err;
  }
  
@@@ -1744,7 -1829,6 +1829,6 @@@ static void __exit exit_nfs_fs(void
        nfs_destroy_nfspagecache();
        nfs_fscache_unregister();
        unregister_pernet_subsys(&nfs_net_ops);
-       nfs_dns_resolver_destroy();
  #ifdef CONFIG_PROC_FS
        rpc_proc_unregister(&init_net, "nfs");
  #endif
diff --combined fs/nfs/nfs3proc.c
@@@ -33,7 -33,7 +33,7 @@@ nfs3_rpc_wrapper(struct rpc_clnt *clnt
                res = rpc_call_sync(clnt, msg, flags);
                if (res != -EJUKEBOX)
                        break;
 -              freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME);
 +              freezable_schedule_timeout_killable_unsafe(NFS_JUKEBOX_RETRY_TIME);
                res = -ERESTARTSYS;
        } while (!fatal_signal_pending(current));
        return res;
@@@ -98,7 -98,7 +98,7 @@@ nfs3_proc_get_root(struct nfs_server *s
   */
  static int
  nfs3_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle,
-               struct nfs_fattr *fattr)
+               struct nfs_fattr *fattr, struct nfs4_label *label)
  {
        struct rpc_message msg = {
                .rpc_proc       = &nfs3_procedures[NFS3PROC_GETATTR],
@@@ -143,7 -143,8 +143,8 @@@ nfs3_proc_setattr(struct dentry *dentry
  
  static int
  nfs3_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)
  {
        struct nfs3_diropargs   arg = {
                .fh             = NFS_FH(dir),
@@@ -300,7 -301,7 +301,7 @@@ static int nfs3_do_create(struct inode 
        status = rpc_call_sync(NFS_CLIENT(dir), &data->msg, 0);
        nfs_post_op_update_inode(dir, data->res.dir_attr);
        if (status == 0)
-               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr);
+               status = nfs_instantiate(dentry, data->res.fh, data->res.fattr, NULL);
        return status;
  }
  
diff --combined fs/nfs/nfs4proc.c
@@@ -77,15 -77,68 +77,68 @@@ static int _nfs4_recover_proc_open(stru
  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 +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 +217,7 @@@ static const u32 nfs4_open_noattr_bitma
        | 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,
        | 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 +241,7 @@@ const u32 nfs4_fsinfo_bitmap[3] = { FAT
                        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
        | 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,
@@@ -268,7 -324,7 +324,7 @@@ static int nfs4_delay(struct rpc_clnt *
                *timeout = NFS4_POLL_RETRY_MIN;
        if (*timeout > NFS4_POLL_RETRY_MAX)
                *timeout = NFS4_POLL_RETRY_MAX;
 -      freezable_schedule_timeout_killable(*timeout);
 +      freezable_schedule_timeout_killable_unsafe(*timeout);
        if (fatal_signal_pending(current))
                res = -ERESTARTSYS;
        *timeout <<= 1;
@@@ -762,6 -818,7 +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 +864,7 @@@ nfs4_map_atomic_open_claim(struct nfs_s
  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;
  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)
  {
        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;
        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:
        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 +969,9 @@@ static void nfs4_opendata_free(struct k
        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 +1250,8 @@@ _nfs4_opendata_reclaim_to_nfs4_state(st
        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 +1278,7 @@@ _nfs4_opendata_to_nfs4_state(struct nfs
        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 +1331,7 @@@ static struct nfs4_opendata *nfs4_open_
        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 +1857,7 @@@ static int _nfs4_proc_open(struct nfs4_
                        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 +1928,30 @@@ static void nfs41_clear_delegation_stat
  {
        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);
                write_sequnlock(&state->seqlock);
                clear_bit(NFS_DELEGATED_STATE, &state->flags);
        }
+       if (cred != NULL)
+               put_rpccred(cred);
  }
  
  /**
@@@ -1888,6 -1976,7 +1976,7 @@@ static int nfs41_check_open_stateid(str
  {
        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 */
            (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 +2031,11 @@@ static inline void nfs4_exclusive_attrs
  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;
        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;
  }
   * 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 */
        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)) {
                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))
                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
  
  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 = {
                  .iap            = sattr,
                .server         = server,
                .bitmask = server->attr_bitmask,
+               .label          = ilabel,
          };
          struct nfs_setattrres  res = {
                .fattr          = fattr,
+               .label          = olabel,
                .server         = server,
          };
          struct rpc_message msg = {
        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 */
  
  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 = {
        };
        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 +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 +2621,17 @@@ static int _nfs4_server_capabilities(st
                        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 +2657,9 @@@ int nfs4_server_capabilities(struct nfs
  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,
                .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 +2798,7 @@@ static int nfs4_proc_get_root(struct nf
  {
        int error;
        struct nfs_fattr *fattr = info->fattr;
+       struct nfs4_label *label = NULL;
  
        error = nfs4_server_capabilities(server, mntfh);
        if (error < 0) {
                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 +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,
        };
        struct nfs4_getattr_res res = {
                .fattr = fattr,
+               .label = label,
                .server = server,
        };
        struct rpc_message msg = {
                .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 +2930,7 @@@ nfs4_proc_setattr(struct dentry *dentry
        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))
                }
        }
  
-       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;
        struct nfs4_lookup_res res = {
                .server = server,
                .fattr = fattr,
+               .label = label,
                .fh = fhandle,
        };
        struct rpc_message msg = {
                .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 +3013,13 @@@ static void nfs_fixup_secinfo_attribute
  
  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;
  }
  
  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);
@@@ -2899,7 -3074,7 +3074,7 @@@ nfs4_proc_lookup_mountpoint(struct inod
        int status;
        struct rpc_clnt *client = rpc_clone_client(NFS_CLIENT(dir));
  
-       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr);
+       status = nfs4_proc_lookup_common(&client, dir, name, fhandle, fattr, NULL);
        if (status < 0) {
                rpc_shutdown_client(client);
                return ERR_PTR(status);
@@@ -2924,7 -3099,7 +3099,7 @@@ static int _nfs4_proc_access(struct ino
                .rpc_cred = entry->cred,
        };
        int mode = entry->mask;
-       int status;
+       int status = 0;
  
        /*
         * Determine which access bits we want to ask for...
@@@ -3029,6 -3204,7 +3204,7 @@@ static in
  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;
        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 -3271,8 +3271,8 @@@ static void nfs4_proc_unlink_setup(stru
        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 -3348,7 +3348,7 @@@ static int _nfs4_proc_rename(struct ino
                .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 -3382,7 +3382,7 @@@ static int _nfs4_proc_link(struct inod
        };
        struct nfs4_link_res res = {
                .server = server,
+               .label = NULL,
        };
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LINK],
        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 -3436,7 +3436,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,
        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;
                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)
                                    &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;
        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 -3521,24 +3521,24 @@@ static int nfs4_proc_symlink(struct ino
                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;
        if (data == NULL)
                goto out;
  
+       data->arg.label = label;
        status = nfs4_do_create(dir, dentry, data);
  
        nfs4_free_createdata(data);
@@@ -3351,14 -3559,19 +3559,19 @@@ static int nfs4_proc_mkdir(struct inod
                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 -3629,7 +3629,7 @@@ static int nfs4_proc_readdir(struct den
  }
  
  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;
                status = -EINVAL;
                goto out_free;
        }
-       
+       data->arg.label = label;
        status = nfs4_do_create(dir, dentry, data);
  out_free:
        nfs4_free_createdata(data);
@@@ -3453,14 -3667,20 +3667,20 @@@ static int nfs4_proc_mknod(struct inod
                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 -4407,155 +4407,155 @@@ static int nfs4_proc_set_acl(struct ino
        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 -4714,7 +4714,7 @@@ int nfs4_proc_setclientid(struct nfs_cl
        /* 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();
@@@ -4528,7 -4897,7 +4897,7 @@@ int nfs4_proc_delegreturn(struct inode 
  static unsigned long
  nfs4_set_lock_task_retry(unsigned long timeout)
  {
 -      freezable_schedule_timeout_killable(timeout);
 +      freezable_schedule_timeout_killable_unsafe(timeout);
        timeout <<= 1;
        if (timeout > NFS4_LOCK_MAXTIMEOUT)
                return NFS4_LOCK_MAXTIMEOUT;
@@@ -5056,13 -5425,18 +5425,18 @@@ static int nfs41_check_expired_locks(st
  
        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 -5669,53 +5669,53 @@@ static size_t nfs4_xattr_list_nfs4_acl(
        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 -5739,7 +5739,7 @@@ static int _nfs4_proc_fs_locations(stru
                                   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 -5926,8 +5926,8 @@@ int nfs4_proc_exchange_id(struct nfs_cl
        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 -6184,14 +6184,14 @@@ int nfs4_proc_get_lease_time(struct nfs
   */
  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 -6578,14 +6578,14 @@@ static const struct rpc_call_ops nfs4_r
  /*
   * 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 -6769,7 +6769,7 @@@ nfs4_proc_layoutget(struct nfs4_layoutg
                .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 -6873,7 +6873,7 @@@ int nfs4_proc_layoutreturn(struct nfs4_
                .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 -6943,9 +6943,9 @@@ int nfs4_proc_getdevicelist(struct nfs_
  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,
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETDEVICEINFO],
                .rpc_argp = &args,
                .rpc_resp = &res,
+               .rpc_cred = cred,
        };
        int status;
  
        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 -7161,9 +7161,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 = {
                .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);
   *
   * @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 -7257,12 +7257,12 @@@ const struct rpc_call_ops nfs41_free_st
  
  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,
   *
   * @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);
  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 -7444,33 +7444,33 @@@ static const struct nfs4_minor_version_
  };
  #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 -7570,9 +7570,9 @@@ static const struct xattr_handler nfs4_
  
  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
  };
  
diff --combined fs/nfs/nfs4state.c
@@@ -228,19 -228,8 +228,8 @@@ static int nfs41_setup_state_renewal(st
        return status;
  }
  
- /*
-  * Back channel returns NFS4ERR_DELAY for new requests when
-  * NFS4_SESSION_DRAINING is set so there is no work to be done when draining
-  * is ended.
-  */
- static void nfs4_end_drain_session(struct nfs_client *clp)
+ static void nfs4_end_drain_slot_table(struct nfs4_slot_table *tbl)
  {
-       struct nfs4_session *ses = clp->cl_session;
-       struct nfs4_slot_table *tbl;
-       if (ses == NULL)
-               return;
-       tbl = &ses->fc_slot_table;
        if (test_and_clear_bit(NFS4_SLOT_TBL_DRAINING, &tbl->slot_tbl_state)) {
                spin_lock(&tbl->slot_tbl_lock);
                nfs41_wake_slot_table(tbl);
        }
  }
  
+ static void nfs4_end_drain_session(struct nfs_client *clp)
+ {
+       struct nfs4_session *ses = clp->cl_session;
+       if (ses != NULL) {
+               nfs4_end_drain_slot_table(&ses->bc_slot_table);
+               nfs4_end_drain_slot_table(&ses->fc_slot_table);
+       }
+ }
  /*
   * Signal state manager thread if session fore channel is drained
   */
@@@ -1194,7 -1193,7 +1193,7 @@@ void nfs4_schedule_state_manager(struc
        snprintf(buf, sizeof(buf), "%s-manager",
                        rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR));
        rcu_read_unlock();
 -      task = kthread_run(nfs4_run_state_manager, clp, buf);
 +      task = kthread_run(nfs4_run_state_manager, clp, "%s", buf);
        if (IS_ERR(task)) {
                printk(KERN_ERR "%s: kthread_run: %ld\n",
                        __func__, PTR_ERR(task));
@@@ -1373,13 -1372,13 +1372,13 @@@ static int nfs4_reclaim_locks(struct nf
        /* Guard against delegation returns and new lock/unlock calls */
        down_write(&nfsi->rwsem);
        /* Protect inode->i_flock using the BKL */
 -      lock_flocks();
 +      spin_lock(&inode->i_lock);
        for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
                if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
                        continue;
                if (nfs_file_open_context(fl->fl_file)->state != state)
                        continue;
 -              unlock_flocks();
 +              spin_unlock(&inode->i_lock);
                status = ops->recover_lock(state, fl);
                switch (status) {
                        case 0:
                                /* kill_proc(fl->fl_pid, SIGLOST, 1); */
                                status = 0;
                }
 -              lock_flocks();
 +              spin_lock(&inode->i_lock);
        }
 -      unlock_flocks();
 +      spin_unlock(&inode->i_lock);
  out:
        up_write(&nfsi->rwsem);
        return status;
@@@ -1563,11 -1562,12 +1562,12 @@@ static void nfs4_state_start_reclaim_re
  }
  
  static void nfs4_reclaim_complete(struct nfs_client *clp,
-                                const struct nfs4_state_recovery_ops *ops)
+                                const struct nfs4_state_recovery_ops *ops,
+                                struct rpc_cred *cred)
  {
        /* Notify the server we're done reclaiming our state */
        if (ops->reclaim_complete)
-               (void)ops->reclaim_complete(clp);
+               (void)ops->reclaim_complete(clp, cred);
  }
  
  static void nfs4_clear_reclaim_server(struct nfs_server *server)
@@@ -1612,9 -1612,15 +1612,15 @@@ static int nfs4_state_clear_reclaim_reb
  
  static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
  {
+       const struct nfs4_state_recovery_ops *ops;
+       struct rpc_cred *cred;
        if (!nfs4_state_clear_reclaim_reboot(clp))
                return;
-       nfs4_reclaim_complete(clp, clp->cl_mvops->reboot_recovery_ops);
+       ops = clp->cl_mvops->reboot_recovery_ops;
+       cred = ops->get_clid_cred(clp);
+       nfs4_reclaim_complete(clp, ops, cred);
+       put_rpccred(cred);
  }
  
  static void nfs_delegation_clear_all(struct nfs_client *clp)
diff --combined include/linux/security.h
@@@ -26,6 -26,7 +26,7 @@@
  #include <linux/capability.h>
  #include <linux/slab.h>
  #include <linux/err.h>
+ #include <linux/string.h>
  
  struct linux_binprm;
  struct cred;
@@@ -60,6 -61,9 +61,9 @@@ struct mm_struct
  #define SECURITY_CAP_NOAUDIT 0
  #define SECURITY_CAP_AUDIT 1
  
+ /* LSM Agnostic defines for sb_set_mnt_opts */
+ #define SECURITY_LSM_NATIVE_LABELS    1
  struct ctl_table;
  struct audit_krule;
  struct user_namespace;
@@@ -306,6 -310,15 +310,15 @@@ static inline void security_free_mnt_op
   *    Parse a string of security data filling in the opts structure
   *    @options string containing all mount options known by the LSM
   *    @opts binary data structure usable by the LSM
+  * @dentry_init_security:
+  *    Compute a context for a dentry as the inode is not yet available
+  *    since NFSv4 has no label backed by an EA anyway.
+  *    @dentry dentry to use in calculating the context.
+  *    @mode mode used to determine resource type.
+  *    @name name of the last path component used to create file
+  *    @ctx pointer to place the pointer to the resulting context in.
+  *    @ctxlen point to place the length of the resulting context.
+  *
   *
   * Security hooks for inode operations.
   *
   *    @pages contains the number of pages.
   *    Return 0 if permission is granted.
   *
+  * @ismaclabel:
+  *    Check if the extended attribute specified by @name
+  *    represents a MAC label. Returns 1 if name is a MAC
+  *    attribute otherwise returns 0.
+  *    @name full extended attribute name to check against
+  *    LSM as a MAC label.
+  *
   * @secid_to_secctx:
   *    Convert secid to security context.  If secdata is NULL the length of
   *    the result will be returned in seclen, but no secdata will be returned.
   *    @ctxlen contains the length of @ctx.
   *
   * @inode_getsecctx:
 - *    Returns a string containing all relevant security context information
 + *    On success, returns 0 and fills out @ctx and @ctxlen with the security
 + *    context for the given @inode.
   *
   *    @inode we wish to get the security context of.
   *    @ctx is a pointer in which to place the allocated security context.
@@@ -1440,10 -1459,16 +1460,16 @@@ struct security_operations 
        int (*sb_pivotroot) (struct path *old_path,
                             struct path *new_path);
        int (*sb_set_mnt_opts) (struct super_block *sb,
-                               struct security_mnt_opts *opts);
+                               struct security_mnt_opts *opts,
+                               unsigned long kern_flags,
+                               unsigned long *set_kern_flags);
        int (*sb_clone_mnt_opts) (const struct super_block *oldsb,
                                   struct super_block *newsb);
        int (*sb_parse_opts_str) (char *options, struct security_mnt_opts *opts);
+       int (*dentry_init_security) (struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen);
  
  #ifdef CONFIG_SECURITY_PATH
        int (*path_unlink) (struct path *dir, struct dentry *dentry);
  
        int (*getprocattr) (struct task_struct *p, char *name, char **value);
        int (*setprocattr) (struct task_struct *p, char *name, void *value, size_t size);
+       int (*ismaclabel) (const char *name);
        int (*secid_to_secctx) (u32 secid, char **secdata, u32 *seclen);
        int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid);
        void (*release_secctx) (char *secdata, u32 seclen);
@@@ -1726,10 -1752,16 +1753,16 @@@ int security_sb_mount(const char *dev_n
                      const char *type, unsigned long flags, void *data);
  int security_sb_umount(struct vfsmount *mnt, int flags);
  int security_sb_pivotroot(struct path *old_path, struct path *new_path);
- int security_sb_set_mnt_opts(struct super_block *sb, struct security_mnt_opts *opts);
+ int security_sb_set_mnt_opts(struct super_block *sb,
+                               struct security_mnt_opts *opts,
+                               unsigned long kern_flags,
+                               unsigned long *set_kern_flags);
  int security_sb_clone_mnt_opts(const struct super_block *oldsb,
                                struct super_block *newsb);
  int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts);
+ int security_dentry_init_security(struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen);
  
  int security_inode_alloc(struct inode *inode);
  void security_inode_free(struct inode *inode);
@@@ -1841,6 -1873,7 +1874,7 @@@ void security_d_instantiate(struct dent
  int security_getprocattr(struct task_struct *p, char *name, char **value);
  int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size);
  int security_netlink_send(struct sock *sk, struct sk_buff *skb);
+ int security_ismaclabel(const char *name);
  int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
  int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
  void security_release_secctx(char *secdata, u32 seclen);
@@@ -2012,7 -2045,9 +2046,9 @@@ static inline int security_sb_pivotroot
  }
  
  static inline int security_sb_set_mnt_opts(struct super_block *sb,
-                                          struct security_mnt_opts *opts)
+                                          struct security_mnt_opts *opts,
+                                          unsigned long kern_flags,
+                                          unsigned long *set_kern_flags)
  {
        return 0;
  }
@@@ -2036,6 -2071,16 +2072,16 @@@ static inline int security_inode_alloc(
  static inline void security_inode_free(struct inode *inode)
  { }
  
+ static inline int security_dentry_init_security(struct dentry *dentry,
+                                                int mode,
+                                                struct qstr *name,
+                                                void **ctx,
+                                                u32 *ctxlen)
+ {
+       return -EOPNOTSUPP;
+ }
  static inline int security_inode_init_security(struct inode *inode,
                                                struct inode *dir,
                                                const struct qstr *qstr,
@@@ -2521,6 -2566,11 +2567,11 @@@ static inline int security_netlink_send
        return cap_netlink_send(sk, skb);
  }
  
+ static inline int security_ismaclabel(const char *name)
+ {
+       return 0;
+ }
  static inline int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
  {
        return -EOPNOTSUPP;
diff --combined net/sunrpc/sched.c
@@@ -254,7 -254,7 +254,7 @@@ static int rpc_wait_bit_killable(void *
  {
        if (fatal_signal_pending(current))
                return -ERESTARTSYS;
 -      freezable_schedule();
 +      freezable_schedule_unsafe();
        return 0;
  }
  
@@@ -446,20 -446,6 +446,6 @@@ static void rpc_wake_up_task_queue_lock
  }
  
  /*
-  * Tests whether rpc queue is empty
-  */
- int rpc_queue_empty(struct rpc_wait_queue *queue)
- {
-       int res;
-       spin_lock_bh(&queue->lock);
-       res = queue->qlen;
-       spin_unlock_bh(&queue->lock);
-       return res == 0;
- }
- EXPORT_SYMBOL_GPL(rpc_queue_empty);
- /*
   * Wake up a task on a specific queue
   */
  void rpc_wake_up_queued_task(struct rpc_wait_queue *queue, struct rpc_task *task)
@@@ -804,7 -790,6 +790,6 @@@ static void __rpc_execute(struct rpc_ta
                        task->tk_flags |= RPC_TASK_KILLED;
                        rpc_exit(task, -ERESTARTSYS);
                }
-               rpc_set_running(task);
                dprintk("RPC: %5u sync task resuming\n", task->tk_pid);
        }
  
   */
  void rpc_execute(struct rpc_task *task)
  {
+       bool is_async = RPC_IS_ASYNC(task);
        rpc_set_active(task);
        rpc_make_runnable(task);
-       if (!RPC_IS_ASYNC(task))
+       if (!is_async)
                __rpc_execute(task);
  }
  
diff --combined security/selinux/hooks.c
@@@ -81,6 -81,7 +81,7 @@@
  #include <linux/syslog.h>
  #include <linux/user_namespace.h>
  #include <linux/export.h>
+ #include <linux/security.h>
  #include <linux/msg.h>
  #include <linux/shm.h>
  
@@@ -284,13 -285,14 +285,14 @@@ static void superblock_free_security(st
  
  /* The file system's label must be initialized prior to use. */
  
- static const char *labeling_behaviors[6] = {
+ static const char *labeling_behaviors[7] = {
        "uses xattr",
        "uses transition SIDs",
        "uses task SIDs",
        "uses genfs_contexts",
        "not configured for labeling",
        "uses mountpoint labeling",
+       "uses native labeling",
  };
  
  static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
@@@ -552,7 -554,9 +554,9 @@@ static int bad_option(struct superblock
   * labeling information.
   */
  static int selinux_set_mnt_opts(struct super_block *sb,
-                               struct security_mnt_opts *opts)
+                               struct security_mnt_opts *opts,
+                               unsigned long kern_flags,
+                               unsigned long *set_kern_flags)
  {
        const struct cred *cred = current_cred();
        int rc = 0, i;
                        "before the security server is initialized\n");
                goto out;
        }
+       if (kern_flags && !set_kern_flags) {
+               /* Specifying internal flags without providing a place to
+                * place the results is not allowed */
+               rc = -EINVAL;
+               goto out;
+       }
  
        /*
         * Binary mount data FS will come through this function twice.  Once
        if (strcmp(sb->s_type->name, "proc") == 0)
                sbsec->flags |= SE_SBPROC;
  
-       /* Determine the labeling behavior to use for this filesystem type. */
-       rc = security_fs_use((sbsec->flags & SE_SBPROC) ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
-       if (rc) {
-               printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
-                      __func__, sb->s_type->name, rc);
-               goto out;
+       if (!sbsec->behavior) {
+               /*
+                * Determine the labeling behavior to use for this
+                * filesystem type.
+                */
+               rc = security_fs_use((sbsec->flags & SE_SBPROC) ?
+                                       "proc" : sb->s_type->name,
+                                       &sbsec->behavior, &sbsec->sid);
+               if (rc) {
+                       printk(KERN_WARNING
+                               "%s: security_fs_use(%s) returned %d\n",
+                                       __func__, sb->s_type->name, rc);
+                       goto out;
+               }
        }
        /* sets the context of the superblock for the fs being mounted. */
        if (fscontext_sid) {
                rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred);
         * sets the label used on all file below the mountpoint, and will set
         * the superblock context if not already set.
         */
+       if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !context_sid) {
+               sbsec->behavior = SECURITY_FS_USE_NATIVE;
+               *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS;
+       }
        if (context_sid) {
                if (!fscontext_sid) {
                        rc = may_context_mount_sb_relabel(context_sid, sbsec,
        }
  
        if (defcontext_sid) {
-               if (sbsec->behavior != SECURITY_FS_USE_XATTR) {
+               if (sbsec->behavior != SECURITY_FS_USE_XATTR &&
+                       sbsec->behavior != SECURITY_FS_USE_NATIVE) {
                        rc = -EINVAL;
                        printk(KERN_WARNING "SELinux: defcontext option is "
                               "invalid for this filesystem type\n");
@@@ -980,7 -1003,7 +1003,7 @@@ static int superblock_doinit(struct sup
                goto out_err;
  
  out:
-       rc = selinux_set_mnt_opts(sb, &opts);
+       rc = selinux_set_mnt_opts(sb, &opts, 0, NULL);
  
  out_err:
        security_free_mnt_opts(&opts);
@@@ -1222,6 -1245,8 +1245,8 @@@ static int inode_doinit_with_dentry(str
        }
  
        switch (sbsec->behavior) {
+       case SECURITY_FS_USE_NATIVE:
+               break;
        case SECURITY_FS_USE_XATTR:
                if (!inode->i_op->getxattr) {
                        isec->sid = sbsec->def_sid;
@@@ -1547,18 -1572,6 +1572,18 @@@ static inline int path_has_perm(const s
        return inode_has_perm(cred, inode, av, &ad, 0);
  }
  
 +/* Same as path_has_perm, but uses the inode from the file struct. */
 +static inline int file_path_has_perm(const struct cred *cred,
 +                                   struct file *file,
 +                                   u32 av)
 +{
 +      struct common_audit_data ad;
 +
 +      ad.type = LSM_AUDIT_DATA_PATH;
 +      ad.u.path = file->f_path;
 +      return inode_has_perm(cred, file_inode(file), av, &ad, 0);
 +}
 +
  /* Check whether a task can use an open file descriptor to
     access an inode in a given way.  Check access to the
     descriptor itself, and then use dentry_has_perm to
@@@ -2153,14 -2166,14 +2178,14 @@@ static inline void flush_unauthorized_f
                        struct tty_file_private *file_priv;
  
                        /* Revalidate access to controlling tty.
 -                         Use path_has_perm on the tty path directly rather
 -                         than using file_has_perm, as this particular open
 -                         file may belong to another process and we are only
 -                         interested in the inode-based check here. */
 +                         Use file_path_has_perm on the tty path directly
 +                         rather than using file_has_perm, as this particular
 +                         open file may belong to another process and we are
 +                         only interested in the inode-based check here. */
                        file_priv = list_first_entry(&tty->tty_files,
                                                struct tty_file_private, list);
                        file = file_priv->file;
 -                      if (path_has_perm(cred, &file->f_path, FILE__READ | FILE__WRITE))
 +                      if (file_path_has_perm(cred, file, FILE__READ | FILE__WRITE))
                                drop_tty = 1;
                }
                spin_unlock(&tty_files_lock);
@@@ -2527,6 -2540,40 +2552,40 @@@ static void selinux_inode_free_security
        inode_free_security(inode);
  }
  
+ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
+                                       struct qstr *name, void **ctx,
+                                       u32 *ctxlen)
+ {
+       const struct cred *cred = current_cred();
+       struct task_security_struct *tsec;
+       struct inode_security_struct *dsec;
+       struct superblock_security_struct *sbsec;
+       struct inode *dir = dentry->d_parent->d_inode;
+       u32 newsid;
+       int rc;
+       tsec = cred->security;
+       dsec = dir->i_security;
+       sbsec = dir->i_sb->s_security;
+       if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
+               newsid = tsec->create_sid;
+       } else {
+               rc = security_transition_sid(tsec->sid, dsec->sid,
+                                            inode_mode_to_security_class(mode),
+                                            name,
+                                            &newsid);
+               if (rc) {
+                       printk(KERN_WARNING
+                               "%s: security_transition_sid failed, rc=%d\n",
+                              __func__, -rc);
+                       return rc;
+               }
+       }
+       return security_sid_to_context(newsid, (char **)ctx, ctxlen);
+ }
  static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
                                       const struct qstr *qstr, char **name,
                                       void **value, size_t *len)
@@@ -2861,7 -2908,10 +2920,10 @@@ static void selinux_inode_post_setxattr
                return;
        }
  
+       isec->sclass = inode_mode_to_security_class(inode->i_mode);
        isec->sid = newsid;
+       isec->initialized = 1;
        return;
  }
  
@@@ -2949,6 -2999,7 +3011,7 @@@ static int selinux_inode_setsecurity(st
        if (rc)
                return rc;
  
+       isec->sclass = inode_mode_to_security_class(inode->i_mode);
        isec->sid = newsid;
        isec->initialized = 1;
        return 0;
@@@ -3271,7 -3322,7 +3334,7 @@@ static int selinux_file_open(struct fil
         * new inode label or new policy.
         * This check is not redundant - do not remove.
         */
 -      return path_has_perm(cred, &file->f_path, open_file_to_av(file));
 +      return file_path_has_perm(cred, file, open_file_to_av(file));
  }
  
  /* task security operations */
@@@ -5432,6 -5483,11 +5495,11 @@@ abort_change
        return error;
  }
  
+ static int selinux_ismaclabel(const char *name)
+ {
+       return (strcmp(name, XATTR_SELINUX_SUFFIX) == 0);
+ }
  static int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
  {
        return security_sid_to_context(secid, secdata, seclen);
@@@ -5574,6 -5630,7 +5642,7 @@@ static struct security_operations selin
        .sb_clone_mnt_opts =            selinux_sb_clone_mnt_opts,
        .sb_parse_opts_str =            selinux_parse_opts_str,
  
+       .dentry_init_security =         selinux_dentry_init_security,
  
        .inode_alloc_security =         selinux_inode_alloc_security,
        .inode_free_security =          selinux_inode_free_security,
        .getprocattr =                  selinux_getprocattr,
        .setprocattr =                  selinux_setprocattr,
  
+       .ismaclabel =                   selinux_ismaclabel,
        .secid_to_secctx =              selinux_secid_to_secctx,
        .secctx_to_secid =              selinux_secctx_to_secid,
        .release_secctx =               selinux_release_secctx,
  #include <linux/ip.h>
  #include <linux/tcp.h>
  #include <linux/udp.h>
 +#include <linux/dccp.h>
  #include <linux/slab.h>
  #include <linux/mutex.h>
  #include <linux/pipe_fs_i.h>
  #include <net/cipso_ipv4.h>
 +#include <net/ip.h>
 +#include <net/ipv6.h>
  #include <linux/audit.h>
  #include <linux/magic.h>
  #include <linux/dcache.h>
  #define TRANS_TRUE    "TRUE"
  #define TRANS_TRUE_SIZE       4
  
 +#define SMK_CONNECTING        0
 +#define SMK_RECEIVING 1
 +#define SMK_SENDING   2
 +
 +LIST_HEAD(smk_ipv6_port_list);
 +
  /**
   * smk_fetch - Fetch the smack label from a file.
   * @ip: a pointer to the inode
   * Returns a pointer to the master list entry for the Smack label
   * or NULL if there was no label to fetch.
   */
 -static char *smk_fetch(const char *name, struct inode *ip, struct dentry *dp)
 +static struct smack_known *smk_fetch(const char *name, struct inode *ip,
 +                                      struct dentry *dp)
  {
        int rc;
        char *buffer;
 -      char *result = NULL;
 +      struct smack_known *skp = NULL;
  
        if (ip->i_op->getxattr == NULL)
                return NULL;
  
        rc = ip->i_op->getxattr(dp, name, buffer, SMK_LONGLABEL);
        if (rc > 0)
 -              result = smk_import(buffer, rc);
 +              skp = smk_import_entry(buffer, rc);
  
        kfree(buffer);
  
 -      return result;
 +      return skp;
  }
  
  /**
@@@ -112,8 -102,7 +112,8 @@@ struct inode_smack *new_inode_smack(cha
   *
   * Returns the new blob or NULL if there's no memory available
   */
 -static struct task_smack *new_task_smack(char *task, char *forked, gfp_t gfp)
 +static struct task_smack *new_task_smack(struct smack_known *task,
 +                                      struct smack_known *forked, gfp_t gfp)
  {
        struct task_smack *tsp;
  
@@@ -175,17 -164,17 +175,17 @@@ static int smack_ptrace_access_check(st
  {
        int rc;
        struct smk_audit_info ad;
 -      char *tsp;
 +      struct smack_known *skp;
  
        rc = cap_ptrace_access_check(ctp, mode);
        if (rc != 0)
                return rc;
  
 -      tsp = smk_of_task(task_security(ctp));
 +      skp = smk_of_task(task_security(ctp));
        smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
        smk_ad_setfield_u_tsk(&ad, ctp);
  
 -      rc = smk_curacc(tsp, MAY_READWRITE, &ad);
 +      rc = smk_curacc(skp->smk_known, MAY_READWRITE, &ad);
        return rc;
  }
  
@@@ -201,17 -190,17 +201,17 @@@ static int smack_ptrace_traceme(struct 
  {
        int rc;
        struct smk_audit_info ad;
 -      char *tsp;
 +      struct smack_known *skp;
  
        rc = cap_ptrace_traceme(ptp);
        if (rc != 0)
                return rc;
  
 -      tsp = smk_of_task(task_security(ptp));
 +      skp = smk_of_task(task_security(ptp));
        smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
        smk_ad_setfield_u_tsk(&ad, ptp);
  
 -      rc = smk_curacc(tsp, MAY_READWRITE, &ad);
 +      rc = smk_curacc(skp->smk_known, MAY_READWRITE, &ad);
        return rc;
  }
  
  static int smack_syslog(int typefrom_file)
  {
        int rc = 0;
 -      char *sp = smk_of_current();
 +      struct smack_known *skp = smk_of_current();
  
        if (smack_privileged(CAP_MAC_OVERRIDE))
                return 0;
  
 -       if (sp != smack_known_floor.smk_known)
 +       if (skp != &smack_known_floor)
                rc = -EACCES;
  
        return rc;
@@@ -261,9 -250,8 +261,9 @@@ static int smack_sb_alloc_security(stru
        sbsp->smk_default = smack_known_floor.smk_known;
        sbsp->smk_floor = smack_known_floor.smk_known;
        sbsp->smk_hat = smack_known_hat.smk_known;
 -      sbsp->smk_initialized = 0;
 -
 +      /*
 +       * smk_initialized will be zero from kzalloc.
 +       */
        sb->s_security = sbsp;
  
        return 0;
@@@ -307,8 -295,6 +307,8 @@@ static int smack_sb_copy_data(char *ori
                        dp = smackopts;
                else if (strstr(cp, SMK_FSROOT) == cp)
                        dp = smackopts;
 +              else if (strstr(cp, SMK_FSTRANS) == cp)
 +                      dp = smackopts;
                else
                        dp = otheropts;
  
@@@ -344,9 -330,8 +344,9 @@@ static int smack_sb_kern_mount(struct s
        char *op;
        char *commap;
        char *nsp;
 +      int transmute = 0;
  
 -      if (sp->smk_initialized != 0)
 +      if (sp->smk_initialized)
                return 0;
  
        sp->smk_initialized = 1;
                        nsp = smk_import(op, 0);
                        if (nsp != NULL)
                                sp->smk_root = nsp;
 +              } else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) {
 +                      op += strlen(SMK_FSTRANS);
 +                      nsp = smk_import(op, 0);
 +                      if (nsp != NULL) {
 +                              sp->smk_root = nsp;
 +                              transmute = 1;
 +                      }
                }
        }
  
         * Initialize the root inode.
         */
        isp = inode->i_security;
 -      if (isp == NULL)
 +      if (inode->i_security == NULL) {
                inode->i_security = new_inode_smack(sp->smk_root);
 -      else
 +              isp = inode->i_security;
 +      } else
                isp->smk_inode = sp->smk_root;
  
 +      if (transmute)
 +              isp->smk_flags |= SMK_INODE_TRANSMUTE;
 +
        return 0;
  }
  
@@@ -550,9 -524,7 +550,9 @@@ static int smack_bprm_secureexec(struc
   */
  static int smack_inode_alloc_security(struct inode *inode)
  {
 -      inode->i_security = new_inode_smack(smk_of_current());
 +      struct smack_known *skp = smk_of_current();
 +
 +      inode->i_security = new_inode_smack(skp->smk_known);
        if (inode->i_security == NULL)
                return -ENOMEM;
        return 0;
@@@ -585,8 -557,9 +585,8 @@@ static int smack_inode_init_security(st
                                     const struct qstr *qstr, char **name,
                                     void **value, size_t *len)
  {
 -      struct smack_known *skp;
        struct inode_smack *issp = inode->i_security;
 -      char *csp = smk_of_current();
 +      struct smack_known *skp = smk_of_current();
        char *isp = smk_of_inode(inode);
        char *dsp = smk_of_inode(dir);
        int may;
        }
  
        if (value) {
 -              skp = smk_find_entry(csp);
                rcu_read_lock();
 -              may = smk_access_entry(csp, dsp, &skp->smk_rules);
 +              may = smk_access_entry(skp->smk_known, dsp, &skp->smk_rules);
                rcu_read_unlock();
  
                /*
@@@ -888,31 -862,29 +888,31 @@@ static int smack_inode_setxattr(struct 
  static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
                                      const void *value, size_t size, int flags)
  {
 -      char *nsp;
 +      struct smack_known *skp;
        struct inode_smack *isp = dentry->d_inode->i_security;
  
 +      if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) {
 +              isp->smk_flags |= SMK_INODE_TRANSMUTE;
 +              return;
 +      }
 +
 +      skp = smk_import_entry(value, size);
        if (strcmp(name, XATTR_NAME_SMACK) == 0) {
 -              nsp = smk_import(value, size);
 -              if (nsp != NULL)
 -                      isp->smk_inode = nsp;
 +              if (skp != NULL)
 +                      isp->smk_inode = skp->smk_known;
                else
                        isp->smk_inode = smack_known_invalid.smk_known;
        } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
 -              nsp = smk_import(value, size);
 -              if (nsp != NULL)
 -                      isp->smk_task = nsp;
 +              if (skp != NULL)
 +                      isp->smk_task = skp;
                else
 -                      isp->smk_task = smack_known_invalid.smk_known;
 +                      isp->smk_task = &smack_known_invalid;
        } else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
 -              nsp = smk_import(value, size);
 -              if (nsp != NULL)
 -                      isp->smk_mmap = nsp;
 +              if (skp != NULL)
 +                      isp->smk_mmap = skp;
                else
 -                      isp->smk_mmap = smack_known_invalid.smk_known;
 -      } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0)
 -              isp->smk_flags |= SMK_INODE_TRANSMUTE;
 +                      isp->smk_mmap = &smack_known_invalid;
 +      }
  
        return;
  }
@@@ -1018,7 -990,7 +1018,7 @@@ static int smack_inode_getsecurity(cons
        if (strcmp(name, XATTR_SMACK_IPIN) == 0)
                isp = ssp->smk_in;
        else if (strcmp(name, XATTR_SMACK_IPOUT) == 0)
 -              isp = ssp->smk_out;
 +              isp = ssp->smk_out->smk_known;
        else
                return -EOPNOTSUPP;
  
@@@ -1098,9 -1070,7 +1098,9 @@@ static int smack_file_permission(struc
   */
  static int smack_file_alloc_security(struct file *file)
  {
 -      file->f_security = smk_of_current();
 +      struct smack_known *skp = smk_of_current();
 +
 +      file->f_security = skp->smk_known;
        return 0;
  }
  
@@@ -1211,9 -1181,10 +1211,9 @@@ static int smack_mmap_file(struct file 
                           unsigned long flags)
  {
        struct smack_known *skp;
 +      struct smack_known *mkp;
        struct smack_rule *srp;
        struct task_smack *tsp;
 -      char *sp;
 -      char *msmack;
        char *osmack;
        struct inode_smack *isp;
        int may;
        isp = file_inode(file)->i_security;
        if (isp->smk_mmap == NULL)
                return 0;
 -      msmack = isp->smk_mmap;
 +      mkp = isp->smk_mmap;
  
        tsp = current_security();
 -      sp = smk_of_current();
 -      skp = smk_find_entry(sp);
 +      skp = smk_of_current();
        rc = 0;
  
        rcu_read_lock();
                /*
                 * Matching labels always allows access.
                 */
 -              if (msmack == osmack)
 +              if (mkp->smk_known == osmack)
                        continue;
                /*
                 * If there is a matching local rule take
                 * that into account as well.
                 */
 -              may = smk_access_entry(srp->smk_subject, osmack,
 +              may = smk_access_entry(srp->smk_subject->smk_known, osmack,
                                        &tsp->smk_rules);
                if (may == -ENOENT)
                        may = srp->smk_access;
                 * If there isn't one a SMACK64MMAP subject
                 * can't have as much access as current.
                 */
 -              skp = smk_find_entry(msmack);
 -              mmay = smk_access_entry(msmack, osmack, &skp->smk_rules);
 +              mmay = smk_access_entry(mkp->smk_known, osmack,
 +                                              &mkp->smk_rules);
                if (mmay == -ENOENT) {
                        rc = -EACCES;
                        break;
                 * If there is a local entry it modifies the
                 * potential access, too.
                 */
 -              tmay = smk_access_entry(msmack, osmack, &tsp->smk_rules);
 +              tmay = smk_access_entry(mkp->smk_known, osmack,
 +                                              &tsp->smk_rules);
                if (tmay != -ENOENT)
                        mmay &= tmay;
  
   */
  static int smack_file_set_fowner(struct file *file)
  {
 -      file->f_security = smk_of_current();
 +      struct smack_known *skp = smk_of_current();
 +
 +      file->f_security = skp->smk_known;
        return 0;
  }
  
  static int smack_file_send_sigiotask(struct task_struct *tsk,
                                     struct fown_struct *fown, int signum)
  {
 +      struct smack_known *skp;
 +      struct smack_known *tkp = smk_of_task(tsk->cred->security);
        struct file *file;
        int rc;
 -      char *tsp = smk_of_task(tsk->cred->security);
        struct smk_audit_info ad;
  
        /*
        file = container_of(fown, struct file, f_owner);
  
        /* we don't log here as rc can be overriden */
 -      rc = smk_access(file->f_security, tsp, MAY_WRITE, NULL);
 +      skp = smk_find_entry(file->f_security);
 +      rc = smk_access(skp, tkp->smk_known, MAY_WRITE, NULL);
        if (rc != 0 && has_capability(tsk, CAP_MAC_OVERRIDE))
                rc = 0;
  
        smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
        smk_ad_setfield_u_tsk(&ad, tsk);
 -      smack_log(file->f_security, tsp, MAY_WRITE, rc, &ad);
 +      smack_log(file->f_security, tkp->smk_known, MAY_WRITE, rc, &ad);
        return rc;
  }
  
@@@ -1502,12 -1469,12 +1502,12 @@@ static void smack_cred_transfer(struct 
  static int smack_kernel_act_as(struct cred *new, u32 secid)
  {
        struct task_smack *new_tsp = new->security;
 -      char *smack = smack_from_secid(secid);
 +      struct smack_known *skp = smack_from_secid(secid);
  
 -      if (smack == NULL)
 +      if (skp == NULL)
                return -EINVAL;
  
 -      new_tsp->smk_task = smack;
 +      new_tsp->smk_task = skp;
        return 0;
  }
  
@@@ -1525,8 -1492,8 +1525,8 @@@ static int smack_kernel_create_files_as
        struct inode_smack *isp = inode->i_security;
        struct task_smack *tsp = new->security;
  
 -      tsp->smk_forked = isp->smk_inode;
 -      tsp->smk_task = isp->smk_inode;
 +      tsp->smk_forked = smk_find_entry(isp->smk_inode);
 +      tsp->smk_task = tsp->smk_forked;
        return 0;
  }
  
@@@ -1542,11 -1509,10 +1542,11 @@@ static int smk_curacc_on_task(struct ta
                                const char *caller)
  {
        struct smk_audit_info ad;
 +      struct smack_known *skp = smk_of_task(task_security(p));
  
        smk_ad_init(&ad, caller, LSM_AUDIT_DATA_TASK);
        smk_ad_setfield_u_tsk(&ad, p);
 -      return smk_curacc(smk_of_task(task_security(p)), access, &ad);
 +      return smk_curacc(skp->smk_known, access, &ad);
  }
  
  /**
@@@ -1592,9 -1558,7 +1592,9 @@@ static int smack_task_getsid(struct tas
   */
  static void smack_task_getsecid(struct task_struct *p, u32 *secid)
  {
 -      *secid = smack_to_secid(smk_of_task(task_security(p)));
 +      struct smack_known *skp = smk_of_task(task_security(p));
 +
 +      *secid = skp->smk_secid;
  }
  
  /**
@@@ -1698,8 -1662,6 +1698,8 @@@ static int smack_task_kill(struct task_
                           int sig, u32 secid)
  {
        struct smk_audit_info ad;
 +      struct smack_known *skp;
 +      struct smack_known *tkp = smk_of_task(task_security(p));
  
        smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
        smk_ad_setfield_u_tsk(&ad, p);
         * can write the receiver.
         */
        if (secid == 0)
 -              return smk_curacc(smk_of_task(task_security(p)), MAY_WRITE,
 -                                &ad);
 +              return smk_curacc(tkp->smk_known, MAY_WRITE, &ad);
        /*
         * If the secid isn't 0 we're dealing with some USB IO
         * specific behavior. This is not clean. For one thing
         * we can't take privilege into account.
         */
 -      return smk_access(smack_from_secid(secid),
 -                        smk_of_task(task_security(p)), MAY_WRITE, &ad);
 +      skp = smack_from_secid(secid);
 +      return smk_access(skp, tkp->smk_known, MAY_WRITE, &ad);
  }
  
  /**
@@@ -1747,9 -1710,7 +1747,9 @@@ static int smack_task_wait(struct task_
  static void smack_task_to_inode(struct task_struct *p, struct inode *inode)
  {
        struct inode_smack *isp = inode->i_security;
 -      isp->smk_inode = smk_of_task(task_security(p));
 +      struct smack_known *skp = smk_of_task(task_security(p));
 +
 +      isp->smk_inode = skp->smk_known;
  }
  
  /*
   */
  static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
  {
 -      char *csp = smk_of_current();
 +      struct smack_known *skp = smk_of_current();
        struct socket_smack *ssp;
  
        ssp = kzalloc(sizeof(struct socket_smack), gfp_flags);
        if (ssp == NULL)
                return -ENOMEM;
  
 -      ssp->smk_in = csp;
 -      ssp->smk_out = csp;
 +      ssp->smk_in = skp->smk_known;
 +      ssp->smk_out = skp;
        ssp->smk_packet = NULL;
  
        sk->sk_security = ssp;
@@@ -1863,7 -1824,7 +1863,7 @@@ static int smack_netlabel(struct sock *
            labeled == SMACK_UNLABELED_SOCKET)
                netlbl_sock_delattr(sk);
        else {
 -              skp = smk_find_entry(ssp->smk_out);
 +              skp = ssp->smk_out;
                rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel);
        }
  
   */
  static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
  {
 +      struct smack_known *skp;
        int rc;
        int sk_lbl;
        char *hostsp;
                ad.a.u.net->v4info.daddr = sap->sin_addr.s_addr;
  #endif
                sk_lbl = SMACK_UNLABELED_SOCKET;
 -              rc = smk_access(ssp->smk_out, hostsp, MAY_WRITE, &ad);
 +              skp = ssp->smk_out;
 +              rc = smk_access(skp, hostsp, MAY_WRITE, &ad);
        } else {
                sk_lbl = SMACK_CIPSO_SOCKET;
                rc = 0;
  }
  
  /**
 + * smk_ipv6_port_label - Smack port access table management
 + * @sock: socket
 + * @address: address
 + *
 + * Create or update the port list entry
 + */
 +static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
 +{
 +      struct sock *sk = sock->sk;
 +      struct sockaddr_in6 *addr6;
 +      struct socket_smack *ssp = sock->sk->sk_security;
 +      struct smk_port_label *spp;
 +      unsigned short port = 0;
 +
 +      if (address == NULL) {
 +              /*
 +               * This operation is changing the Smack information
 +               * on the bound socket. Take the changes to the port
 +               * as well.
 +               */
 +              list_for_each_entry(spp, &smk_ipv6_port_list, list) {
 +                      if (sk != spp->smk_sock)
 +                              continue;
 +                      spp->smk_in = ssp->smk_in;
 +                      spp->smk_out = ssp->smk_out;
 +                      return;
 +              }
 +              /*
 +               * A NULL address is only used for updating existing
 +               * bound entries. If there isn't one, it's OK.
 +               */
 +              return;
 +      }
 +
 +      addr6 = (struct sockaddr_in6 *)address;
 +      port = ntohs(addr6->sin6_port);
 +      /*
 +       * This is a special case that is safely ignored.
 +       */
 +      if (port == 0)
 +              return;
 +
 +      /*
 +       * Look for an existing port list entry.
 +       * This is an indication that a port is getting reused.
 +       */
 +      list_for_each_entry(spp, &smk_ipv6_port_list, list) {
 +              if (spp->smk_port != port)
 +                      continue;
 +              spp->smk_port = port;
 +              spp->smk_sock = sk;
 +              spp->smk_in = ssp->smk_in;
 +              spp->smk_out = ssp->smk_out;
 +              return;
 +      }
 +
 +      /*
 +       * A new port entry is required.
 +       */
 +      spp = kzalloc(sizeof(*spp), GFP_KERNEL);
 +      if (spp == NULL)
 +              return;
 +
 +      spp->smk_port = port;
 +      spp->smk_sock = sk;
 +      spp->smk_in = ssp->smk_in;
 +      spp->smk_out = ssp->smk_out;
 +
 +      list_add(&spp->list, &smk_ipv6_port_list);
 +      return;
 +}
 +
 +/**
 + * smk_ipv6_port_check - check Smack port access
 + * @sock: socket
 + * @address: address
 + *
 + * Create or update the port list entry
 + */
 +static int smk_ipv6_port_check(struct sock *sk, struct sockaddr *address,
 +                              int act)
 +{
 +      __be16 *bep;
 +      __be32 *be32p;
 +      struct sockaddr_in6 *addr6;
 +      struct smk_port_label *spp;
 +      struct socket_smack *ssp = sk->sk_security;
 +      struct smack_known *skp;
 +      unsigned short port = 0;
 +      char *object;
 +      struct smk_audit_info ad;
 +#ifdef CONFIG_AUDIT
 +      struct lsm_network_audit net;
 +#endif
 +
 +      if (act == SMK_RECEIVING) {
 +              skp = smack_net_ambient;
 +              object = ssp->smk_in;
 +      } else {
 +              skp = ssp->smk_out;
 +              object = smack_net_ambient->smk_known;
 +      }
 +
 +      /*
 +       * Get the IP address and port from the address.
 +       */
 +      addr6 = (struct sockaddr_in6 *)address;
 +      port = ntohs(addr6->sin6_port);
 +      bep = (__be16 *)(&addr6->sin6_addr);
 +      be32p = (__be32 *)(&addr6->sin6_addr);
 +
 +      /*
 +       * It's remote, so port lookup does no good.
 +       */
 +      if (be32p[0] || be32p[1] || be32p[2] || bep[6] || ntohs(bep[7]) != 1)
 +              goto auditout;
 +
 +      /*
 +       * It's local so the send check has to have passed.
 +       */
 +      if (act == SMK_RECEIVING) {
 +              skp = &smack_known_web;
 +              goto auditout;
 +      }
 +
 +      list_for_each_entry(spp, &smk_ipv6_port_list, list) {
 +              if (spp->smk_port != port)
 +                      continue;
 +              object = spp->smk_in;
 +              if (act == SMK_CONNECTING)
 +                      ssp->smk_packet = spp->smk_out->smk_known;
 +              break;
 +      }
 +
 +auditout:
 +
 +#ifdef CONFIG_AUDIT
 +      smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
 +      ad.a.u.net->family = sk->sk_family;
 +      ad.a.u.net->dport = port;
 +      if (act == SMK_RECEIVING)
 +              ad.a.u.net->v6info.saddr = addr6->sin6_addr;
 +      else
 +              ad.a.u.net->v6info.daddr = addr6->sin6_addr;
 +#endif
 +      return smk_access(skp, object, MAY_WRITE, &ad);
 +}
 +
 +/**
   * smack_inode_setsecurity - set smack xattrs
   * @inode: the object
   * @name: attribute name
  static int smack_inode_setsecurity(struct inode *inode, const char *name,
                                   const void *value, size_t size, int flags)
  {
 -      char *sp;
 +      struct smack_known *skp;
        struct inode_smack *nsp = inode->i_security;
        struct socket_smack *ssp;
        struct socket *sock;
        if (value == NULL || size > SMK_LONGLABEL || size == 0)
                return -EACCES;
  
 -      sp = smk_import(value, size);
 -      if (sp == NULL)
 +      skp = smk_import_entry(value, size);
 +      if (skp == NULL)
                return -EINVAL;
  
        if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
 -              nsp->smk_inode = sp;
 +              nsp->smk_inode = skp->smk_known;
                nsp->smk_flags |= SMK_INODE_INSTANT;
                return 0;
        }
        ssp = sock->sk->sk_security;
  
        if (strcmp(name, XATTR_SMACK_IPIN) == 0)
 -              ssp->smk_in = sp;
 +              ssp->smk_in = skp->smk_known;
        else if (strcmp(name, XATTR_SMACK_IPOUT) == 0) {
 -              ssp->smk_out = sp;
 -              if (sock->sk->sk_family != PF_UNIX) {
 +              ssp->smk_out = skp;
 +              if (sock->sk->sk_family == PF_INET) {
                        rc = smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
                        if (rc != 0)
                                printk(KERN_WARNING
        } else
                return -EOPNOTSUPP;
  
 +      if (sock->sk->sk_family == PF_INET6)
 +              smk_ipv6_port_label(sock, NULL);
 +
        return 0;
  }
  
@@@ -2156,25 -1963,6 +2156,25 @@@ static int smack_socket_post_create(str
  }
  
  /**
 + * smack_socket_bind - record port binding information.
 + * @sock: the socket
 + * @address: the port address
 + * @addrlen: size of the address
 + *
 + * Records the label bound to a port.
 + *
 + * Returns 0
 + */
 +static int smack_socket_bind(struct socket *sock, struct sockaddr *address,
 +                              int addrlen)
 +{
 +      if (sock->sk != NULL && sock->sk->sk_family == PF_INET6)
 +              smk_ipv6_port_label(sock, address);
 +
 +      return 0;
 +}
 +
 +/**
   * smack_socket_connect - connect access check
   * @sock: the socket
   * @sap: the other end
  static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
                                int addrlen)
  {
 -      if (sock->sk == NULL || sock->sk->sk_family != PF_INET)
 +      int rc = 0;
 +
 +      if (sock->sk == NULL)
                return 0;
 -      if (addrlen < sizeof(struct sockaddr_in))
 -              return -EINVAL;
  
 -      return smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap);
 +      switch (sock->sk->sk_family) {
 +      case PF_INET:
 +              if (addrlen < sizeof(struct sockaddr_in))
 +                      return -EINVAL;
 +              rc = smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap);
 +              break;
 +      case PF_INET6:
 +              if (addrlen < sizeof(struct sockaddr_in6))
 +                      return -EINVAL;
 +              rc = smk_ipv6_port_check(sock->sk, sap, SMK_CONNECTING);
 +              break;
 +      }
 +      return rc;
  }
  
  /**
@@@ -2235,9 -2011,7 +2235,9 @@@ static int smack_flags_to_may(int flags
   */
  static int smack_msg_msg_alloc_security(struct msg_msg *msg)
  {
 -      msg->security = smk_of_current();
 +      struct smack_known *skp = smk_of_current();
 +
 +      msg->security = skp->smk_known;
        return 0;
  }
  
@@@ -2272,9 -2046,8 +2272,9 @@@ static char *smack_of_shm(struct shmid_
  static int smack_shm_alloc_security(struct shmid_kernel *shp)
  {
        struct kern_ipc_perm *isp = &shp->shm_perm;
 +      struct smack_known *skp = smk_of_current();
  
 -      isp->security = smk_of_current();
 +      isp->security = skp->smk_known;
        return 0;
  }
  
@@@ -2396,9 -2169,8 +2396,9 @@@ static char *smack_of_sem(struct sem_ar
  static int smack_sem_alloc_security(struct sem_array *sma)
  {
        struct kern_ipc_perm *isp = &sma->sem_perm;
 +      struct smack_known *skp = smk_of_current();
  
 -      isp->security = smk_of_current();
 +      isp->security = skp->smk_known;
        return 0;
  }
  
@@@ -2515,9 -2287,8 +2515,9 @@@ static int smack_sem_semop(struct sem_a
  static int smack_msg_queue_alloc_security(struct msg_queue *msq)
  {
        struct kern_ipc_perm *kisp = &msq->q_perm;
 +      struct smack_known *skp = smk_of_current();
  
 -      kisp->security = smk_of_current();
 +      kisp->security = skp->smk_known;
        return 0;
  }
  
@@@ -2689,8 -2460,8 +2689,8 @@@ static void smack_d_instantiate(struct 
        struct super_block *sbp;
        struct superblock_smack *sbsp;
        struct inode_smack *isp;
 -      char *csp = smk_of_current();
 -      char *fetched;
 +      struct smack_known *skp;
 +      struct smack_known *ckp = smk_of_current();
        char *final;
        char trattr[TRANS_TRUE_SIZE];
        int transflag = 0;
                 * Programs that change smack have to treat the
                 * pty with respect.
                 */
 -              final = csp;
 +              final = ckp->smk_known;
                break;
        case SOCKFS_MAGIC:
                /*
                 * Get the dentry for xattr.
                 */
                dp = dget(opt_dentry);
 -              fetched = smk_fetch(XATTR_NAME_SMACK, inode, dp);
 -              if (fetched != NULL)
 -                      final = fetched;
 +              skp = smk_fetch(XATTR_NAME_SMACK, inode, dp);
 +              if (skp != NULL)
 +                      final = skp->smk_known;
  
                /*
                 * Transmuting directory
        }
  
        if (final == NULL)
 -              isp->smk_inode = csp;
 +              isp->smk_inode = ckp->smk_known;
        else
                isp->smk_inode = final;
  
@@@ -2877,14 -2648,13 +2877,14 @@@ unlockandout
   */
  static int smack_getprocattr(struct task_struct *p, char *name, char **value)
  {
 +      struct smack_known *skp = smk_of_task(task_security(p));
        char *cp;
        int slen;
  
        if (strcmp(name, "current") != 0)
                return -EINVAL;
  
 -      cp = kstrdup(smk_of_task(task_security(p)), GFP_KERNEL);
 +      cp = kstrdup(skp->smk_known, GFP_KERNEL);
        if (cp == NULL)
                return -ENOMEM;
  
@@@ -2910,7 -2680,7 +2910,7 @@@ static int smack_setprocattr(struct tas
  {
        struct task_smack *tsp;
        struct cred *new;
 -      char *newsmack;
 +      struct smack_known *skp;
  
        /*
         * Changing another process' Smack value is too dangerous
        if (strcmp(name, "current") != 0)
                return -EINVAL;
  
 -      newsmack = smk_import(value, size);
 -      if (newsmack == NULL)
 +      skp = smk_import_entry(value, size);
 +      if (skp == NULL)
                return -EINVAL;
  
        /*
         * No process is ever allowed the web ("@") label.
         */
 -      if (newsmack == smack_known_web.smk_known)
 +      if (skp == &smack_known_web)
                return -EPERM;
  
        new = prepare_creds();
                return -ENOMEM;
  
        tsp = new->security;
 -      tsp->smk_task = newsmack;
 +      tsp->smk_task = skp;
  
        commit_creds(new);
        return size;
  static int smack_unix_stream_connect(struct sock *sock,
                                     struct sock *other, struct sock *newsk)
  {
 +      struct smack_known *skp;
        struct socket_smack *ssp = sock->sk_security;
        struct socket_smack *osp = other->sk_security;
        struct socket_smack *nsp = newsk->sk_security;
        smk_ad_setfield_u_net_sk(&ad, other);
  #endif
  
 -      if (!smack_privileged(CAP_MAC_OVERRIDE))
 -              rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
 +      if (!smack_privileged(CAP_MAC_OVERRIDE)) {
 +              skp = ssp->smk_out;
 +              rc = smk_access(skp, osp->smk_in, MAY_WRITE, &ad);
 +      }
  
        /*
         * Cross reference the peer labels for SO_PEERSEC.
         */
        if (rc == 0) {
 -              nsp->smk_packet = ssp->smk_out;
 -              ssp->smk_packet = osp->smk_out;
 +              nsp->smk_packet = ssp->smk_out->smk_known;
 +              ssp->smk_packet = osp->smk_out->smk_known;
        }
  
        return rc;
@@@ -3003,8 -2770,8 +3003,8 @@@ static int smack_unix_may_send(struct s
  {
        struct socket_smack *ssp = sock->sk->sk_security;
        struct socket_smack *osp = other->sk->sk_security;
 +      struct smack_known *skp;
        struct smk_audit_info ad;
 -      int rc = 0;
  
  #ifdef CONFIG_AUDIT
        struct lsm_network_audit net;
        smk_ad_setfield_u_net_sk(&ad, other->sk);
  #endif
  
 -      if (!smack_privileged(CAP_MAC_OVERRIDE))
 -              rc = smk_access(ssp->smk_out, osp->smk_in, MAY_WRITE, &ad);
 +      if (smack_privileged(CAP_MAC_OVERRIDE))
 +              return 0;
  
 -      return rc;
 +      skp = ssp->smk_out;
 +      return smk_access(skp, osp->smk_in, MAY_WRITE, &ad);
  }
  
  /**
   * @msg: the message
   * @size: the size of the message
   *
 - * Return 0 if the current subject can write to the destination
 - * host. This is only a question if the destination is a single
 - * label host.
 + * Return 0 if the current subject can write to the destination host.
 + * For IPv4 this is only a question if the destination is a single label host.
 + * For IPv6 this is a check against the label of the port.
   */
  static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
                                int size)
  {
        struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name;
 +      struct sockaddr *sap = (struct sockaddr *) msg->msg_name;
 +      int rc = 0;
  
        /*
         * Perfectly reasonable for this to be NULL
         */
 -      if (sip == NULL || sip->sin_family != AF_INET)
 +      if (sip == NULL)
                return 0;
  
 -      return smack_netlabel_send(sock->sk, sip);
 +      switch (sip->sin_family) {
 +      case AF_INET:
 +              rc = smack_netlabel_send(sock->sk, sip);
 +              break;
 +      case AF_INET6:
 +              rc = smk_ipv6_port_check(sock->sk, sap, SMK_SENDING);
 +              break;
 +      }
 +      return rc;
  }
  
  /**
   * @sap: netlabel secattr
   * @ssp: socket security information
   *
 - * Returns a pointer to a Smack label found on the label list.
 + * Returns a pointer to a Smack label entry found on the label list.
   */
 -static char *smack_from_secattr(struct netlbl_lsm_secattr *sap,
 -                              struct socket_smack *ssp)
 +static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
 +                                              struct socket_smack *ssp)
  {
 -      struct smack_known *kp;
 -      char *sp;
 +      struct smack_known *skp;
        int found = 0;
  
        if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) {
                 * ambient value.
                 */
                rcu_read_lock();
 -              list_for_each_entry(kp, &smack_known_list, list) {
 -                      if (sap->attr.mls.lvl != kp->smk_netlabel.attr.mls.lvl)
 +              list_for_each_entry(skp, &smack_known_list, list) {
 +                      if (sap->attr.mls.lvl != skp->smk_netlabel.attr.mls.lvl)
                                continue;
                        if (memcmp(sap->attr.mls.cat,
 -                              kp->smk_netlabel.attr.mls.cat,
 +                              skp->smk_netlabel.attr.mls.cat,
                                SMK_CIPSOLEN) != 0)
                                continue;
                        found = 1;
                rcu_read_unlock();
  
                if (found)
 -                      return kp->smk_known;
 +                      return skp;
  
                if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known)
 -                      return smack_known_web.smk_known;
 -              return smack_known_star.smk_known;
 +                      return &smack_known_web;
 +              return &smack_known_star;
        }
        if ((sap->flags & NETLBL_SECATTR_SECID) != 0) {
                /*
                 * Looks like a fallback, which gives us a secid.
                 */
 -              sp = smack_from_secid(sap->attr.secid);
 +              skp = smack_from_secid(sap->attr.secid);
                /*
                 * This has got to be a bug because it is
                 * impossible to specify a fallback without
                 * it has a secid, and the only way to get a
                 * secid is from a fallback.
                 */
 -              BUG_ON(sp == NULL);
 -              return sp;
 +              BUG_ON(skp == NULL);
 +              return skp;
        }
        /*
         * Without guidance regarding the smack value
        return smack_net_ambient;
  }
  
 +static int smk_skb_to_addr_ipv6(struct sk_buff *skb, struct sockaddr *sap)
 +{
 +      struct sockaddr_in6 *sip = (struct sockaddr_in6 *)sap;
 +      u8 nexthdr;
 +      int offset;
 +      int proto = -EINVAL;
 +      struct ipv6hdr _ipv6h;
 +      struct ipv6hdr *ip6;
 +      __be16 frag_off;
 +      struct tcphdr _tcph, *th;
 +      struct udphdr _udph, *uh;
 +      struct dccp_hdr _dccph, *dh;
 +
 +      sip->sin6_port = 0;
 +
 +      offset = skb_network_offset(skb);
 +      ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
 +      if (ip6 == NULL)
 +              return -EINVAL;
 +      sip->sin6_addr = ip6->saddr;
 +
 +      nexthdr = ip6->nexthdr;
 +      offset += sizeof(_ipv6h);
 +      offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off);
 +      if (offset < 0)
 +              return -EINVAL;
 +
 +      proto = nexthdr;
 +      switch (proto) {
 +      case IPPROTO_TCP:
 +              th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph);
 +              if (th != NULL)
 +                      sip->sin6_port = th->source;
 +              break;
 +      case IPPROTO_UDP:
 +              uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph);
 +              if (uh != NULL)
 +                      sip->sin6_port = uh->source;
 +              break;
 +      case IPPROTO_DCCP:
 +              dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph);
 +              if (dh != NULL)
 +                      sip->sin6_port = dh->dccph_sport;
 +              break;
 +      }
 +      return proto;
 +}
 +
  /**
   * smack_socket_sock_rcv_skb - Smack packet delivery access check
   * @sk: socket
@@@ -3180,52 -2889,43 +3180,52 @@@ static int smack_socket_sock_rcv_skb(st
  {
        struct netlbl_lsm_secattr secattr;
        struct socket_smack *ssp = sk->sk_security;
 -      char *csp;
 -      int rc;
 +      struct smack_known *skp;
 +      struct sockaddr sadd;
 +      int rc = 0;
        struct smk_audit_info ad;
  #ifdef CONFIG_AUDIT
        struct lsm_network_audit net;
  #endif
 -      if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6)
 -              return 0;
 -
 -      /*
 -       * Translate what netlabel gave us.
 -       */
 -      netlbl_secattr_init(&secattr);
 +      switch (sk->sk_family) {
 +      case PF_INET:
 +              /*
 +               * Translate what netlabel gave us.
 +               */
 +              netlbl_secattr_init(&secattr);
  
 -      rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr);
 -      if (rc == 0)
 -              csp = smack_from_secattr(&secattr, ssp);
 -      else
 -              csp = smack_net_ambient;
 +              rc = netlbl_skbuff_getattr(skb, sk->sk_family, &secattr);
 +              if (rc == 0)
 +                      skp = smack_from_secattr(&secattr, ssp);
 +              else
 +                      skp = smack_net_ambient;
  
 -      netlbl_secattr_destroy(&secattr);
 +              netlbl_secattr_destroy(&secattr);
  
  #ifdef CONFIG_AUDIT
 -      smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
 -      ad.a.u.net->family = sk->sk_family;
 -      ad.a.u.net->netif = skb->skb_iif;
 -      ipv4_skb_to_auditdata(skb, &ad.a, NULL);
 +              smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
 +              ad.a.u.net->family = sk->sk_family;
 +              ad.a.u.net->netif = skb->skb_iif;
 +              ipv4_skb_to_auditdata(skb, &ad.a, NULL);
  #endif
 -      /*
 -       * Receiving a packet requires that the other end
 -       * be able to write here. Read access is not required.
 -       * This is the simplist possible security model
 -       * for networking.
 -       */
 -      rc = smk_access(csp, ssp->smk_in, MAY_WRITE, &ad);
 -      if (rc != 0)
 -              netlbl_skbuff_err(skb, rc, 0);
 +              /*
 +               * Receiving a packet requires that the other end
 +               * be able to write here. Read access is not required.
 +               * This is the simplist possible security model
 +               * for networking.
 +               */
 +              rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad);
 +              if (rc != 0)
 +                      netlbl_skbuff_err(skb, rc, 0);
 +              break;
 +      case PF_INET6:
 +              rc = smk_skb_to_addr_ipv6(skb, &sadd);
 +              if (rc == IPPROTO_UDP || rc == IPPROTO_TCP)
 +                      rc = smk_ipv6_port_check(sk, &sadd, SMK_RECEIVING);
 +              else
 +                      rc = 0;
 +              break;
 +      }
        return rc;
  }
  
@@@ -3279,7 -2979,7 +3279,7 @@@ static int smack_socket_getpeersec_dgra
  {
        struct netlbl_lsm_secattr secattr;
        struct socket_smack *ssp = NULL;
 -      char *sp;
 +      struct smack_known *skp;
        int family = PF_UNSPEC;
        u32 s = 0;      /* 0 is the invalid secid */
        int rc;
  
        if (family == PF_UNIX) {
                ssp = sock->sk->sk_security;
 -              s = smack_to_secid(ssp->smk_out);
 +              s = ssp->smk_out->smk_secid;
        } else if (family == PF_INET || family == PF_INET6) {
                /*
                 * Translate what netlabel gave us.
                netlbl_secattr_init(&secattr);
                rc = netlbl_skbuff_getattr(skb, family, &secattr);
                if (rc == 0) {
 -                      sp = smack_from_secattr(&secattr, ssp);
 -                      s = smack_to_secid(sp);
 +                      skp = smack_from_secattr(&secattr, ssp);
 +                      s = skp->smk_secid;
                }
                netlbl_secattr_destroy(&secattr);
        }
  static void smack_sock_graft(struct sock *sk, struct socket *parent)
  {
        struct socket_smack *ssp;
 +      struct smack_known *skp = smk_of_current();
  
        if (sk == NULL ||
            (sk->sk_family != PF_INET && sk->sk_family != PF_INET6))
                return;
  
        ssp = sk->sk_security;
 -      ssp->smk_in = ssp->smk_out = smk_of_current();
 +      ssp->smk_in = skp->smk_known;
 +      ssp->smk_out = skp;
        /* cssp->smk_packet is already set in smack_inet_csk_clone() */
  }
  
@@@ -3357,6 -3055,7 +3357,6 @@@ static int smack_inet_conn_request(stru
        struct netlbl_lsm_secattr secattr;
        struct sockaddr_in addr;
        struct iphdr *hdr;
 -      char *sp;
        char *hsp;
        int rc;
        struct smk_audit_info ad;
        struct lsm_network_audit net;
  #endif
  
 -      /* handle mapped IPv4 packets arriving via IPv6 sockets */
 -      if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
 -              family = PF_INET;
 +      if (family == PF_INET6) {
 +              /*
 +               * Handle mapped IPv4 packets arriving
 +               * via IPv6 sockets. Don't set up netlabel
 +               * processing on IPv6.
 +               */
 +              if (skb->protocol == htons(ETH_P_IP))
 +                      family = PF_INET;
 +              else
 +                      return 0;
 +      }
  
        netlbl_secattr_init(&secattr);
        rc = netlbl_skbuff_getattr(skb, family, &secattr);
        if (rc == 0)
 -              sp = smack_from_secattr(&secattr, ssp);
 +              skp = smack_from_secattr(&secattr, ssp);
        else
 -              sp = smack_known_huh.smk_known;
 +              skp = &smack_known_huh;
        netlbl_secattr_destroy(&secattr);
  
  #ifdef CONFIG_AUDIT
         * Receiving a packet requires that the other end be able to write
         * here. Read access is not required.
         */
 -      rc = smk_access(sp, ssp->smk_in, MAY_WRITE, &ad);
 +      rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad);
        if (rc != 0)
                return rc;
  
         * Save the peer's label in the request_sock so we can later setup
         * smk_packet in the child socket so that SO_PEERCRED can report it.
         */
 -      req->peer_secid = smack_to_secid(sp);
 +      req->peer_secid = skp->smk_secid;
  
        /*
         * We need to decide if we want to label the incoming connection here
        hsp = smack_host_label(&addr);
        rcu_read_unlock();
  
 -      if (hsp == NULL) {
 -              skp = smk_find_entry(sp);
 +      if (hsp == NULL)
                rc = netlbl_req_setattr(req, &skp->smk_netlabel);
 -      else
 +      else
                netlbl_req_delattr(req);
  
        return rc;
@@@ -3434,12 -3126,10 +3434,12 @@@ static void smack_inet_csk_clone(struc
                                 const struct request_sock *req)
  {
        struct socket_smack *ssp = sk->sk_security;
 +      struct smack_known *skp;
  
 -      if (req->peer_secid != 0)
 -              ssp->smk_packet = smack_from_secid(req->peer_secid);
 -      else
 +      if (req->peer_secid != 0) {
 +              skp = smack_from_secid(req->peer_secid);
 +              ssp->smk_packet = skp->smk_known;
 +      } else
                ssp->smk_packet = NULL;
  }
  
  static int smack_key_alloc(struct key *key, const struct cred *cred,
                           unsigned long flags)
  {
 -      key->security = smk_of_task(cred->security);
 +      struct smack_known *skp = smk_of_task(cred->security);
 +
 +      key->security = skp->smk_known;
        return 0;
  }
  
@@@ -3496,7 -3184,7 +3496,7 @@@ static int smack_key_permission(key_ref
  {
        struct key *keyp;
        struct smk_audit_info ad;
 -      char *tsp = smk_of_task(cred->security);
 +      struct smack_known *tkp = smk_of_task(cred->security);
  
        keyp = key_ref_to_ptr(key_ref);
        if (keyp == NULL)
        /*
         * This should not occur
         */
 -      if (tsp == NULL)
 +      if (tkp == NULL)
                return -EACCES;
  #ifdef CONFIG_AUDIT
        smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_KEY);
        ad.a.u.key_struct.key = keyp->serial;
        ad.a.u.key_struct.key_desc = keyp->description;
  #endif
 -      return smk_access(tsp, keyp->security,
 -                               MAY_READWRITE, &ad);
 +      return smk_access(tkp, keyp->security, MAY_READWRITE, &ad);
  }
  #endif /* CONFIG_KEYS */
  
@@@ -3599,7 -3288,7 +3599,7 @@@ static int smack_audit_rule_known(struc
  static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule,
                                  struct audit_context *actx)
  {
 -      char *smack;
 +      struct smack_known *skp;
        char *rule = vrule;
  
        if (!rule) {
        if (field != AUDIT_SUBJ_USER && field != AUDIT_OBJ_USER)
                return 0;
  
 -      smack = smack_from_secid(secid);
 +      skp = smack_from_secid(secid);
  
        /*
         * No need to do string comparisons. If a match occurs,
         * label.
         */
        if (op == Audit_equal)
 -              return (rule == smack);
 +              return (rule == skp->smk_known);
        if (op == Audit_not_equal)
 -              return (rule != smack);
 +              return (rule != skp->smk_known);
  
        return 0;
  }
@@@ -3640,6 -3329,16 +3640,16 @@@ static void smack_audit_rule_free(void 
  #endif /* CONFIG_AUDIT */
  
  /**
+  * smack_ismaclabel - check if xattr @name references a smack MAC label
+  * @name: Full xattr name to check.
+  */
+ static int smack_ismaclabel(const char *name)
+ {
+       return (strcmp(name, XATTR_SMACK_SUFFIX) == 0);
+ }
+ /**
   * smack_secid_to_secctx - return the smack label for a secid
   * @secid: incoming integer
   * @secdata: destination
   */
  static int smack_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
  {
 -      char *sp = smack_from_secid(secid);
 +      struct smack_known *skp = smack_from_secid(secid);
  
        if (secdata)
 -              *secdata = sp;
 -      *seclen = strlen(sp);
 +              *secdata = skp->smk_known;
 +      *seclen = strlen(skp->smk_known);
        return 0;
  }
  
@@@ -3809,7 -3508,6 +3819,7 @@@ struct security_operations smack_ops = 
        .unix_may_send =                smack_unix_may_send,
  
        .socket_post_create =           smack_socket_post_create,
 +      .socket_bind =                  smack_socket_bind,
        .socket_connect =               smack_socket_connect,
        .socket_sendmsg =               smack_socket_sendmsg,
        .socket_sock_rcv_skb =          smack_socket_sock_rcv_skb,
        .audit_rule_free =              smack_audit_rule_free,
  #endif /* CONFIG_AUDIT */
  
+       .ismaclabel =                   smack_ismaclabel,
        .secid_to_secctx =              smack_secid_to_secctx,
        .secctx_to_secid =              smack_secctx_to_secid,
        .release_secctx =               smack_release_secctx,
@@@ -3889,8 -3588,8 +3900,8 @@@ static __init int smack_init(void
        if (!security_module_enable(&smack_ops))
                return 0;
  
 -      tsp = new_task_smack(smack_known_floor.smk_known,
 -                              smack_known_floor.smk_known, GFP_KERNEL);
 +      tsp = new_task_smack(&smack_known_floor, &smack_known_floor,
 +                              GFP_KERNEL);
        if (tsp == NULL)
                return -ENOMEM;