Merge branch 'for-3.10' of git://linux-nfs.org/~bfields/linux
Linus Torvalds [Fri, 3 May 2013 17:59:39 +0000 (10:59 -0700)]
Pull nfsd changes from J Bruce Fields:
 "Highlights include:

   - Some more DRC cleanup and performance work from Jeff Layton

   - A gss-proxy upcall from Simo Sorce: currently krb5 mounts to the
     server using credentials from Active Directory often fail due to
     limitations of the svcgssd upcall interface.  This replacement
     lifts those limitations.  The existing upcall is still supported
     for backwards compatibility.

   - More NFSv4.1 support: at this point, if a user with a current
     client who upgrades from 4.0 to 4.1 should see no regressions.  In
     theory we do everything a 4.1 server is required to do.  Patches
     for a couple minor exceptions are ready for 3.11, and with those
     and some more testing I'd like to turn 4.1 on by default in 3.11."

Fix up semantic conflict as per Stephen Rothwell and linux-next:

Commit 030d794bf498 ("SUNRPC: Use gssproxy upcall for server RPCGSS
authentication") adds two new users of "PDE(inode)->data", but we're
supposed to use "PDE_DATA(inode)" instead since commit d9dda78bad87
("procfs: new helper - PDE_DATA(inode)").

The old PDE() macro is no longer available since commit c30480b92cf4
("proc: Make the PROC_I() and PDE() macros internal to procfs")

* 'for-3.10' of git://linux-nfs.org/~bfields/linux: (60 commits)
  NFSD: SECINFO doesn't handle unsupported pseudoflavors correctly
  NFSD: Simplify GSS flavor encoding in nfsd4_do_encode_secinfo()
  nfsd: make symbol nfsd_reply_cache_shrinker static
  svcauth_gss: fix error return code in rsc_parse()
  nfsd4: don't remap EISDIR errors in rename
  svcrpc: fix gss-proxy to respect user namespaces
  SUNRPC: gssp_procedures[] can be static
  SUNRPC: define {create,destroy}_use_gss_proxy_proc_entry in !PROC case
  nfsd4: better error return to indicate SSV non-support
  nfsd: fix EXDEV checking in rename
  SUNRPC: Use gssproxy upcall for server RPCGSS authentication.
  SUNRPC: Add RPC based upcall mechanism for RPCGSS auth
  SUNRPC: conditionally return endtime from import_sec_context
  SUNRPC: allow disabling idle timeout
  SUNRPC: attempt AF_LOCAL connect on setup
  nfsd: Decode and send 64bit time values
  nfsd4: put_client_renew_locked can be static
  nfsd4: remove unused macro
  nfsd4: remove some useless code
  nfsd4: implement SEQ4_STATUS_RECALLABLE_STATE_REVOKED
  ...

1  2 
fs/nfsd/nfs4state.c
fs/nfsd/nfsctl.c
net/sunrpc/auth_gss/svcauth_gss.c
net/sunrpc/cache.c

@@@ -260,9 -353,13 +352,9 @@@ kmem_cache *slab
         * amount of time until an id is reused, by ensuring they always
         * "increase" (mod INT_MAX):
         */
 -
 -      min_stateid = new_id+1;
 -      if (min_stateid == INT_MAX)
 -              min_stateid = 0;
        return stid;
  out_free:
-       kfree(stid);
+       kmem_cache_free(slab, stid);
        return NULL;
  }
  
Simple merge
        return ret;
  }
  
+ static int gss_proxy_save_rsc(struct cache_detail *cd,
+                               struct gssp_upcall_data *ud,
+                               uint64_t *handle)
+ {
+       struct rsc rsci, *rscp = NULL;
+       static atomic64_t ctxhctr;
+       long long ctxh;
+       struct gss_api_mech *gm = NULL;
+       time_t expiry;
+       int status = -EINVAL;
+       memset(&rsci, 0, sizeof(rsci));
+       /* context handle */
+       status = -ENOMEM;
+       /* the handle needs to be just a unique id,
+        * use a static counter */
+       ctxh = atomic64_inc_return(&ctxhctr);
+       /* make a copy for the caller */
+       *handle = ctxh;
+       /* make a copy for the rsc cache */
+       if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t)))
+               goto out;
+       rscp = rsc_lookup(cd, &rsci);
+       if (!rscp)
+               goto out;
+       /* creds */
+       if (!ud->found_creds) {
+               /* userspace seem buggy, we should always get at least a
+                * mapping to nobody */
+               dprintk("RPC:       No creds found, marking Negative!\n");
+               set_bit(CACHE_NEGATIVE, &rsci.h.flags);
+       } else {
+               /* steal creds */
+               rsci.cred = ud->creds;
+               memset(&ud->creds, 0, sizeof(struct svc_cred));
+               status = -EOPNOTSUPP;
+               /* get mech handle from OID */
+               gm = gss_mech_get_by_OID(&ud->mech_oid);
+               if (!gm)
+                       goto out;
+               status = -EINVAL;
+               /* mech-specific data: */
+               status = gss_import_sec_context(ud->out_handle.data,
+                                               ud->out_handle.len,
+                                               gm, &rsci.mechctx,
+                                               &expiry, GFP_KERNEL);
+               if (status)
+                       goto out;
+       }
+       rsci.h.expiry_time = expiry;
+       rscp = rsc_update(cd, &rsci, rscp);
+       status = 0;
+ out:
+       gss_mech_put(gm);
+       rsc_free(&rsci);
+       if (rscp)
+               cache_put(&rscp->h, cd);
+       else
+               status = -ENOMEM;
+       return status;
+ }
+ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
+                       struct rpc_gss_wire_cred *gc, __be32 *authp)
+ {
+       struct kvec *resv = &rqstp->rq_res.head[0];
+       struct xdr_netobj cli_handle;
+       struct gssp_upcall_data ud;
+       uint64_t handle;
+       int status;
+       int ret;
+       struct net *net = rqstp->rq_xprt->xpt_net;
+       struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+       memset(&ud, 0, sizeof(ud));
+       ret = gss_read_proxy_verf(rqstp, gc, authp,
+                                 &ud.in_handle, &ud.in_token);
+       if (ret)
+               return ret;
+       ret = SVC_CLOSE;
+       /* Perform synchronous upcall to gss-proxy */
+       status = gssp_accept_sec_context_upcall(net, &ud);
+       if (status)
+               goto out;
+       dprintk("RPC:       svcauth_gss: gss major status = %d\n",
+                       ud.major_status);
+       switch (ud.major_status) {
+       case GSS_S_CONTINUE_NEEDED:
+               cli_handle = ud.out_handle;
+               break;
+       case GSS_S_COMPLETE:
+               status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle);
+               if (status)
+                       goto out;
+               cli_handle.data = (u8 *)&handle;
+               cli_handle.len = sizeof(handle);
+               break;
+       default:
+               ret = SVC_CLOSE;
+               goto out;
+       }
+       /* Got an answer to the upcall; use it: */
+       if (gss_write_init_verf(sn->rsc_cache, rqstp,
+                               &cli_handle, &ud.major_status))
+               goto out;
+       if (gss_write_resv(resv, PAGE_SIZE,
+                          &cli_handle, &ud.out_token,
+                          ud.major_status, ud.minor_status))
+               goto out;
+       ret = SVC_COMPLETE;
+ out:
+       gssp_free_upcall_data(&ud);
+       return ret;
+ }
+ DEFINE_SPINLOCK(use_gssp_lock);
+ static bool use_gss_proxy(struct net *net)
+ {
+       struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+       if (sn->use_gss_proxy != -1)
+               return sn->use_gss_proxy;
+       spin_lock(&use_gssp_lock);
+       /*
+        * If you wanted gss-proxy, you should have said so before
+        * starting to accept requests:
+        */
+       sn->use_gss_proxy = 0;
+       spin_unlock(&use_gssp_lock);
+       return 0;
+ }
+ #ifdef CONFIG_PROC_FS
+ static bool set_gss_proxy(struct net *net, int type)
+ {
+       struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+       int ret = 0;
+       WARN_ON_ONCE(type != 0 && type != 1);
+       spin_lock(&use_gssp_lock);
+       if (sn->use_gss_proxy == -1 || sn->use_gss_proxy == type)
+               sn->use_gss_proxy = type;
+       else
+               ret = -EBUSY;
+       spin_unlock(&use_gssp_lock);
+       wake_up(&sn->gssp_wq);
+       return ret;
+ }
+ static inline bool gssp_ready(struct sunrpc_net *sn)
+ {
+       switch (sn->use_gss_proxy) {
+               case -1:
+                       return false;
+               case 0:
+                       return true;
+               case 1:
+                       return sn->gssp_clnt;
+       }
+       WARN_ON_ONCE(1);
+       return false;
+ }
+ static int wait_for_gss_proxy(struct net *net)
+ {
+       struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+       return wait_event_interruptible(sn->gssp_wq, gssp_ready(sn));
+ }
+ static ssize_t write_gssp(struct file *file, const char __user *buf,
+                        size_t count, loff_t *ppos)
+ {
 -      struct net *net = PDE(file->f_path.dentry->d_inode)->data;
++      struct net *net = PDE_DATA(file->f_path.dentry->d_inode);
+       char tbuf[20];
+       unsigned long i;
+       int res;
+       if (*ppos || count > sizeof(tbuf)-1)
+               return -EINVAL;
+       if (copy_from_user(tbuf, buf, count))
+               return -EFAULT;
+       tbuf[count] = 0;
+       res = kstrtoul(tbuf, 0, &i);
+       if (res)
+               return res;
+       if (i != 1)
+               return -EINVAL;
+       res = set_gss_proxy(net, 1);
+       if (res)
+               return res;
+       res = set_gssp_clnt(net);
+       if (res)
+               return res;
+       return count;
+ }
+ static ssize_t read_gssp(struct file *file, char __user *buf,
+                        size_t count, loff_t *ppos)
+ {
 -      struct net *net = PDE(file->f_path.dentry->d_inode)->data;
++      struct net *net = PDE_DATA(file->f_path.dentry->d_inode);
+       unsigned long p = *ppos;
+       char tbuf[10];
+       size_t len;
+       int ret;
+       ret = wait_for_gss_proxy(net);
+       if (ret)
+               return ret;
+       snprintf(tbuf, sizeof(tbuf), "%d\n", use_gss_proxy(net));
+       len = strlen(tbuf);
+       if (p >= len)
+               return 0;
+       len -= p;
+       if (len > count)
+               len = count;
+       if (copy_to_user(buf, (void *)(tbuf+p), len))
+               return -EFAULT;
+       *ppos += len;
+       return len;
+ }
+ static const struct file_operations use_gss_proxy_ops = {
+       .open = nonseekable_open,
+       .write = write_gssp,
+       .read = read_gssp,
+ };
+ static int create_use_gss_proxy_proc_entry(struct net *net)
+ {
+       struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+       struct proc_dir_entry **p = &sn->use_gssp_proc;
+       sn->use_gss_proxy = -1;
+       *p = proc_create_data("use-gss-proxy", S_IFREG|S_IRUSR|S_IWUSR,
+                             sn->proc_net_rpc,
+                             &use_gss_proxy_ops, net);
+       if (!*p)
+               return -ENOMEM;
+       init_gssp_clnt(sn);
+       return 0;
+ }
+ static void destroy_use_gss_proxy_proc_entry(struct net *net)
+ {
+       struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+       if (sn->use_gssp_proc) {
+               remove_proc_entry("use-gss-proxy", sn->proc_net_rpc); 
+               clear_gssp_clnt(sn);
+       }
+ }
+ #else /* CONFIG_PROC_FS */
+ static int create_use_gss_proxy_proc_entry(struct net *net)
+ {
+       return 0;
+ }
+ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
+ #endif /* CONFIG_PROC_FS */
  /*
   * Accept an rpcsec packet.
   * If context establishment, punt to user space
Simple merge