Merge branch 'for-3.11' of git://linux-nfs.org/~bfields/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 Jul 2013 17:17:13 +0000 (10:17 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 Jul 2013 17:17:13 +0000 (10:17 -0700)
Pull nfsd changes from Bruce Fields:
 "Changes this time include:

   - 4.1 enabled on the server by default: the last 4.1-specific issues
     I know of are fixed, so we're not going to find the rest of the
     bugs without more exposure.
   - Experimental support for NFSv4.2 MAC Labeling (to allow running
     selinux over NFS), from Dave Quigley.
   - Fixes for some delicate cache/upcall races that could cause rare
     server hangs; thanks to Neil Brown and Bodo Stroesser for extreme
     debugging persistence.
   - Fixes for some bugs found at the recent NFS bakeathon, mostly v4
     and v4.1-specific, but also a generic bug handling fragmented rpc
     calls"

* 'for-3.11' of git://linux-nfs.org/~bfields/linux: (31 commits)
  nfsd4: support minorversion 1 by default
  nfsd4: allow destroy_session over destroyed session
  svcrpc: fix failures to handle -1 uid's
  sunrpc: Don't schedule an upcall on a replaced cache entry.
  net/sunrpc: xpt_auth_cache should be ignored when expired.
  sunrpc/cache: ensure items removed from cache do not have pending upcalls.
  sunrpc/cache: use cache_fresh_unlocked consistently and correctly.
  sunrpc/cache: remove races with queuing an upcall.
  nfsd4: return delegation immediately if lease fails
  nfsd4: do not throw away 4.1 lock state on last unlock
  nfsd4: delegation-based open reclaims should bypass permissions
  svcrpc: don't error out on small tcp fragment
  svcrpc: fix handling of too-short rpc's
  nfsd4: minor read_buf cleanup
  nfsd4: fix decoding of compounds across page boundaries
  nfsd4: clean up nfs4_open_delegation
  NFSD: Don't give out read delegations on creates
  nfsd4: allow client to send no cb_sec flavors
  nfsd4: fail attempts to request gss on the backchannel
  nfsd4: implement minimal SP4_MACH_CRED
  ...

20 files changed:
fs/nfsd/Kconfig
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfsd.h
fs/nfsd/nfssvc.c
fs/nfsd/state.h
fs/nfsd/vfs.c
fs/nfsd/vfs.h
fs/nfsd/xdr4.h
include/linux/sunrpc/cache.h
include/linux/sunrpc/gss_api.h
include/linux/sunrpc/svcauth.h
net/sunrpc/auth_gss/gss_mech_switch.c
net/sunrpc/auth_gss/svcauth_gss.c
net/sunrpc/cache.c
net/sunrpc/svcauth_unix.c
net/sunrpc/svcsock.c
net/sunrpc/xprtsock.c
security/capability.c

index 430b687..dc8f1ef 100644 (file)
@@ -81,6 +81,22 @@ config NFSD_V4
 
          If unsure, say N.
 
+config NFSD_V4_SECURITY_LABEL
+       bool "Provide Security Label support for NFSv4 server"
+       depends on NFSD_V4 && SECURITY
+       help
+
+       Say Y here if you want enable fine-grained security label attribute
+       support for NFS version 4.  Security labels allow security modules like
+       SELinux and Smack to label files to facilitate enforcement of their policies.
+       Without this an NFSv4 mount will have the same label on each file.
+
+       If you do not wish to enable fine-grained security labels SELinux or
+       Smack policies on NFSv4 files, say N.
+
+       WARNING: there is still a chance of backwards-incompatible protocol changes.
+       For now we recommend "Y" only for developers and testers."
+
 config NFSD_FAULT_INJECTION
        bool "NFS server manual fault injection"
        depends on NFSD_V4 && DEBUG_KERNEL
index 27d74a2..a7cee86 100644 (file)
 #include "current_stateid.h"
 #include "netns.h"
 
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+#include <linux/security.h>
+
+static inline void
+nfsd4_security_inode_setsecctx(struct svc_fh *resfh, struct xdr_netobj *label, u32 *bmval)
+{
+       struct inode *inode = resfh->fh_dentry->d_inode;
+       int status;
+
+       mutex_lock(&inode->i_mutex);
+       status = security_inode_setsecctx(resfh->fh_dentry,
+               label->data, label->len);
+       mutex_unlock(&inode->i_mutex);
+
+       if (status)
+               /*
+                * XXX: We should really fail the whole open, but we may
+                * already have created a new file, so it may be too
+                * late.  For now this seems the least of evils:
+                */
+               bmval[2] &= ~FATTR4_WORD2_SECURITY_LABEL;
+
+       return;
+}
+#else
+static inline void
+nfsd4_security_inode_setsecctx(struct svc_fh *resfh, struct xdr_netobj *label, u32 *bmval)
+{ }
+#endif
+
 #define NFSDDBG_FACILITY               NFSDDBG_PROC
 
 static u32 nfsd_attrmask[] = {
@@ -239,6 +269,9 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
                                        (u32 *)open->op_verf.data,
                                        &open->op_truncate, &open->op_created);
 
+               if (!status && open->op_label.len)
+                       nfsd4_security_inode_setsecctx(resfh, &open->op_label, open->op_bmval);
+
                /*
                 * Following rfc 3530 14.2.16, use the returned bitmask
                 * to indicate which attributes we used to store the
@@ -263,7 +296,8 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
 
        nfsd4_set_open_owner_reply_cache(cstate, open, resfh);
        accmode = NFSD_MAY_NOP;
-       if (open->op_created)
+       if (open->op_created ||
+                       open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR)
                accmode |= NFSD_MAY_OWNER_OVERRIDE;
        status = do_open_permission(rqstp, resfh, open, accmode);
        set_change_info(&open->op_cinfo, current_fh);
@@ -637,6 +671,9 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (status)
                goto out;
 
+       if (create->cr_label.len)
+               nfsd4_security_inode_setsecctx(&resfh, &create->cr_label, create->cr_bmval);
+
        if (create->cr_acl != NULL)
                do_set_nfs4_acl(rqstp, &resfh, create->cr_acl,
                                create->cr_bmval);
@@ -916,6 +953,11 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                                            setattr->sa_acl);
        if (status)
                goto out;
+       if (setattr->sa_label.len)
+               status = nfsd4_set_nfs4_label(rqstp, &cstate->current_fh,
+                               &setattr->sa_label);
+       if (status)
+               goto out;
        status = nfsd_setattr(rqstp, &cstate->current_fh, &setattr->sa_iattr,
                                0, (time_t)0);
 out:
index f170518..280acef 100644 (file)
@@ -97,19 +97,20 @@ nfs4_lock_state(void)
 
 static void free_session(struct nfsd4_session *);
 
-void nfsd4_put_session(struct nfsd4_session *ses)
+static bool is_session_dead(struct nfsd4_session *ses)
 {
-       atomic_dec(&ses->se_ref);
+       return ses->se_flags & NFS4_SESSION_DEAD;
 }
 
-static bool is_session_dead(struct nfsd4_session *ses)
+void nfsd4_put_session(struct nfsd4_session *ses)
 {
-       return ses->se_flags & NFS4_SESSION_DEAD;
+       if (atomic_dec_and_test(&ses->se_ref) && is_session_dead(ses))
+               free_session(ses);
 }
 
-static __be32 mark_session_dead_locked(struct nfsd4_session *ses)
+static __be32 mark_session_dead_locked(struct nfsd4_session *ses, int ref_held_by_me)
 {
-       if (atomic_read(&ses->se_ref))
+       if (atomic_read(&ses->se_ref) > ref_held_by_me)
                return nfserr_jukebox;
        ses->se_flags |= NFS4_SESSION_DEAD;
        return nfs_ok;
@@ -364,19 +365,12 @@ static struct nfs4_ol_stateid * nfs4_alloc_stateid(struct nfs4_client *clp)
 }
 
 static struct nfs4_delegation *
-alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh, u32 type)
+alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh)
 {
        struct nfs4_delegation *dp;
        struct nfs4_file *fp = stp->st_file;
 
        dprintk("NFSD alloc_init_deleg\n");
-       /*
-        * Major work on the lease subsystem (for example, to support
-        * calbacks on stat) will be required before we can support
-        * write delegations properly.
-        */
-       if (type != NFS4_OPEN_DELEGATE_READ)
-               return NULL;
        if (fp->fi_had_conflict)
                return NULL;
        if (num_delegations > max_delegations)
@@ -397,7 +391,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
        INIT_LIST_HEAD(&dp->dl_recall_lru);
        get_nfs4_file(fp);
        dp->dl_file = fp;
-       dp->dl_type = type;
+       dp->dl_type = NFS4_OPEN_DELEGATE_READ;
        fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle);
        dp->dl_time = 0;
        atomic_set(&dp->dl_count, 1);
@@ -1188,6 +1182,9 @@ static int copy_cred(struct svc_cred *target, struct svc_cred *source)
        target->cr_gid = source->cr_gid;
        target->cr_group_info = source->cr_group_info;
        get_group_info(target->cr_group_info);
+       target->cr_gss_mech = source->cr_gss_mech;
+       if (source->cr_gss_mech)
+               gss_mech_get(source->cr_gss_mech);
        return 0;
 }
 
@@ -1262,6 +1259,31 @@ same_creds(struct svc_cred *cr1, struct svc_cred *cr2)
        return 0 == strcmp(cr1->cr_principal, cr2->cr_principal);
 }
 
+static bool svc_rqst_integrity_protected(struct svc_rqst *rqstp)
+{
+       struct svc_cred *cr = &rqstp->rq_cred;
+       u32 service;
+
+       service = gss_pseudoflavor_to_service(cr->cr_gss_mech, cr->cr_flavor);
+       return service == RPC_GSS_SVC_INTEGRITY ||
+              service == RPC_GSS_SVC_PRIVACY;
+}
+
+static bool mach_creds_match(struct nfs4_client *cl, struct svc_rqst *rqstp)
+{
+       struct svc_cred *cr = &rqstp->rq_cred;
+
+       if (!cl->cl_mach_cred)
+               return true;
+       if (cl->cl_cred.cr_gss_mech != cr->cr_gss_mech)
+               return false;
+       if (!svc_rqst_integrity_protected(rqstp))
+               return false;
+       if (!cr->cr_principal)
+               return false;
+       return 0 == strcmp(cl->cl_cred.cr_principal, cr->cr_principal);
+}
+
 static void gen_clid(struct nfs4_client *clp, struct nfsd_net *nn)
 {
        static u32 current_clientid = 1;
@@ -1639,16 +1661,16 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
        if (exid->flags & ~EXCHGID4_FLAG_MASK_A)
                return nfserr_inval;
 
-       /* Currently only support SP4_NONE */
        switch (exid->spa_how) {
+       case SP4_MACH_CRED:
+               if (!svc_rqst_integrity_protected(rqstp))
+                       return nfserr_inval;
        case SP4_NONE:
                break;
        default:                                /* checked by xdr code */
                WARN_ON_ONCE(1);
        case SP4_SSV:
                return nfserr_encr_alg_unsupp;
-       case SP4_MACH_CRED:
-               return nfserr_serverfault;      /* no excuse :-/ */
        }
 
        /* Cases below refer to rfc 5661 section 18.35.4: */
@@ -1663,6 +1685,10 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
                                status = nfserr_inval;
                                goto out;
                        }
+                       if (!mach_creds_match(conf, rqstp)) {
+                               status = nfserr_wrong_cred;
+                               goto out;
+                       }
                        if (!creds_match) { /* case 9 */
                                status = nfserr_perm;
                                goto out;
@@ -1709,7 +1735,8 @@ out_new:
                status = nfserr_jukebox;
                goto out;
        }
-       new->cl_minorversion = 1;
+       new->cl_minorversion = cstate->minorversion;
+       new->cl_mach_cred = (exid->spa_how == SP4_MACH_CRED);
 
        gen_clid(new, nn);
        add_to_unconfirmed(new);
@@ -1839,6 +1866,24 @@ static __be32 check_backchannel_attrs(struct nfsd4_channel_attrs *ca)
        return nfs_ok;
 }
 
+static __be32 nfsd4_check_cb_sec(struct nfsd4_cb_sec *cbs)
+{
+       switch (cbs->flavor) {
+       case RPC_AUTH_NULL:
+       case RPC_AUTH_UNIX:
+               return nfs_ok;
+       default:
+               /*
+                * GSS case: the spec doesn't allow us to return this
+                * error.  But it also doesn't allow us not to support
+                * GSS.
+                * I'd rather this fail hard than return some error the
+                * client might think it can already handle:
+                */
+               return nfserr_encr_alg_unsupp;
+       }
+}
+
 __be32
 nfsd4_create_session(struct svc_rqst *rqstp,
                     struct nfsd4_compound_state *cstate,
@@ -1854,6 +1899,9 @@ nfsd4_create_session(struct svc_rqst *rqstp,
 
        if (cr_ses->flags & ~SESSION4_FLAG_MASK_A)
                return nfserr_inval;
+       status = nfsd4_check_cb_sec(&cr_ses->cb_sec);
+       if (status)
+               return status;
        status = check_forechannel_attrs(&cr_ses->fore_channel, nn);
        if (status)
                return status;
@@ -1874,6 +1922,9 @@ nfsd4_create_session(struct svc_rqst *rqstp,
        WARN_ON_ONCE(conf && unconf);
 
        if (conf) {
+               status = nfserr_wrong_cred;
+               if (!mach_creds_match(conf, rqstp))
+                       goto out_free_conn;
                cs_slot = &conf->cl_cs_slot;
                status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
                if (status == nfserr_replay_cache) {
@@ -1890,6 +1941,9 @@ nfsd4_create_session(struct svc_rqst *rqstp,
                        status = nfserr_clid_inuse;
                        goto out_free_conn;
                }
+               status = nfserr_wrong_cred;
+               if (!mach_creds_match(unconf, rqstp))
+                       goto out_free_conn;
                cs_slot = &unconf->cl_cs_slot;
                status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
                if (status) {
@@ -1957,7 +2011,11 @@ __be32 nfsd4_backchannel_ctl(struct svc_rqst *rqstp, struct nfsd4_compound_state
 {
        struct nfsd4_session *session = cstate->session;
        struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+       __be32 status;
 
+       status = nfsd4_check_cb_sec(&bc->bc_cb_sec);
+       if (status)
+               return status;
        spin_lock(&nn->client_lock);
        session->se_cb_prog = bc->bc_cb_program;
        session->se_cb_sec = bc->bc_cb_sec;
@@ -1986,6 +2044,9 @@ __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp,
        status = nfserr_badsession;
        if (!session)
                goto out;
+       status = nfserr_wrong_cred;
+       if (!mach_creds_match(session->se_client, rqstp))
+               goto out;
        status = nfsd4_map_bcts_dir(&bcts->dir);
        if (status)
                goto out;
@@ -2014,6 +2075,7 @@ nfsd4_destroy_session(struct svc_rqst *r,
 {
        struct nfsd4_session *ses;
        __be32 status;
+       int ref_held_by_me = 0;
        struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id);
 
        nfs4_lock_state();
@@ -2021,6 +2083,7 @@ nfsd4_destroy_session(struct svc_rqst *r,
        if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) {
                if (!nfsd4_last_compound_op(r))
                        goto out;
+               ref_held_by_me++;
        }
        dump_sessionid(__func__, &sessionid->sessionid);
        spin_lock(&nn->client_lock);
@@ -2028,17 +2091,22 @@ nfsd4_destroy_session(struct svc_rqst *r,
        status = nfserr_badsession;
        if (!ses)
                goto out_client_lock;
-       status = mark_session_dead_locked(ses);
-       if (status)
+       status = nfserr_wrong_cred;
+       if (!mach_creds_match(ses->se_client, r))
                goto out_client_lock;
+       nfsd4_get_session_locked(ses);
+       status = mark_session_dead_locked(ses, 1 + ref_held_by_me);
+       if (status)
+               goto out_put_session;
        unhash_session(ses);
        spin_unlock(&nn->client_lock);
 
        nfsd4_probe_callback_sync(ses->se_client);
 
        spin_lock(&nn->client_lock);
-       free_session(ses);
        status = nfs_ok;
+out_put_session:
+       nfsd4_put_session(ses);
 out_client_lock:
        spin_unlock(&nn->client_lock);
 out:
@@ -2058,26 +2126,31 @@ static struct nfsd4_conn *__nfsd4_find_conn(struct svc_xprt *xpt, struct nfsd4_s
        return NULL;
 }
 
-static void nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_session *ses)
+static __be32 nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_session *ses)
 {
        struct nfs4_client *clp = ses->se_client;
        struct nfsd4_conn *c;
+       __be32 status = nfs_ok;
        int ret;
 
        spin_lock(&clp->cl_lock);
        c = __nfsd4_find_conn(new->cn_xprt, ses);
-       if (c) {
-               spin_unlock(&clp->cl_lock);
-               free_conn(new);
-               return;
-       }
+       if (c)
+               goto out_free;
+       status = nfserr_conn_not_bound_to_session;
+       if (clp->cl_mach_cred)
+               goto out_free;
        __nfsd4_hash_conn(new, ses);
        spin_unlock(&clp->cl_lock);
        ret = nfsd4_register_conn(new);
        if (ret)
                /* oops; xprt is already down: */
                nfsd4_conn_lost(&new->cn_xpt_user);
-       return;
+       return nfs_ok;
+out_free:
+       spin_unlock(&clp->cl_lock);
+       free_conn(new);
+       return status;
 }
 
 static bool nfsd4_session_too_many_ops(struct svc_rqst *rqstp, struct nfsd4_session *session)
@@ -2169,8 +2242,10 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        if (status)
                goto out_put_session;
 
-       nfsd4_sequence_check_conn(conn, session);
+       status = nfsd4_sequence_check_conn(conn, session);
        conn = NULL;
+       if (status)
+               goto out_put_session;
 
        /* Success! bump slot seqid */
        slot->sl_seqid = seq->seqid;
@@ -2232,7 +2307,10 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta
                status = nfserr_stale_clientid;
                goto out;
        }
-
+       if (!mach_creds_match(clp, rqstp)) {
+               status = nfserr_wrong_cred;
+               goto out;
+       }
        expire_client(clp);
 out:
        nfs4_unlock_state();
@@ -2940,13 +3018,13 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp, int f
        return fl;
 }
 
-static int nfs4_setlease(struct nfs4_delegation *dp, int flag)
+static int nfs4_setlease(struct nfs4_delegation *dp)
 {
        struct nfs4_file *fp = dp->dl_file;
        struct file_lock *fl;
        int status;
 
-       fl = nfs4_alloc_init_lease(dp, flag);
+       fl = nfs4_alloc_init_lease(dp, NFS4_OPEN_DELEGATE_READ);
        if (!fl)
                return -ENOMEM;
        fl->fl_file = find_readable_file(fp);
@@ -2964,12 +3042,12 @@ static int nfs4_setlease(struct nfs4_delegation *dp, int flag)
        return 0;
 }
 
-static int nfs4_set_delegation(struct nfs4_delegation *dp, int flag)
+static int nfs4_set_delegation(struct nfs4_delegation *dp)
 {
        struct nfs4_file *fp = dp->dl_file;
 
        if (!fp->fi_lease)
-               return nfs4_setlease(dp, flag);
+               return nfs4_setlease(dp);
        spin_lock(&recall_lock);
        if (fp->fi_had_conflict) {
                spin_unlock(&recall_lock);
@@ -3005,6 +3083,9 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status)
 
 /*
  * Attempt to hand out a delegation.
+ *
+ * Note we don't support write delegations, and won't until the vfs has
+ * proper support for them.
  */
 static void
 nfs4_open_delegation(struct net *net, struct svc_fh *fh,
@@ -3013,39 +3094,45 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh,
        struct nfs4_delegation *dp;
        struct nfs4_openowner *oo = container_of(stp->st_stateowner, struct nfs4_openowner, oo_owner);
        int cb_up;
-       int status = 0, flag = 0;
+       int status = 0;
 
        cb_up = nfsd4_cb_channel_good(oo->oo_owner.so_client);
-       flag = NFS4_OPEN_DELEGATE_NONE;
        open->op_recall = 0;
        switch (open->op_claim_type) {
                case NFS4_OPEN_CLAIM_PREVIOUS:
                        if (!cb_up)
                                open->op_recall = 1;
-                       flag = open->op_delegate_type;
-                       if (flag == NFS4_OPEN_DELEGATE_NONE)
-                               goto out;
+                       if (open->op_delegate_type != NFS4_OPEN_DELEGATE_READ)
+                               goto out_no_deleg;
                        break;
                case NFS4_OPEN_CLAIM_NULL:
-                       /* Let's not give out any delegations till everyone's
-                        * had the chance to reclaim theirs.... */
+                       /*
+                        * Let's not give out any delegations till everyone's
+                        * had the chance to reclaim theirs....
+                        */
                        if (locks_in_grace(net))
-                               goto out;
+                               goto out_no_deleg;
                        if (!cb_up || !(oo->oo_flags & NFS4_OO_CONFIRMED))
-                               goto out;
+                               goto out_no_deleg;
+                       /*
+                        * Also, if the file was opened for write or
+                        * create, there's a good chance the client's
+                        * about to write to it, resulting in an
+                        * immediate recall (since we don't support
+                        * write delegations):
+                        */
                        if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE)
-                               flag = NFS4_OPEN_DELEGATE_WRITE;
-                       else
-                               flag = NFS4_OPEN_DELEGATE_READ;
+                               goto out_no_deleg;
+                       if (open->op_create == NFS4_OPEN_CREATE)
+                               goto out_no_deleg;
                        break;
                default:
-                       goto out;
+                       goto out_no_deleg;
        }
-
-       dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh, flag);
+       dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh);
        if (dp == NULL)
                goto out_no_deleg;
-       status = nfs4_set_delegation(dp, flag);
+       status = nfs4_set_delegation(dp);
        if (status)
                goto out_free;
 
@@ -3053,24 +3140,23 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh,
 
        dprintk("NFSD: delegation stateid=" STATEID_FMT "\n",
                STATEID_VAL(&dp->dl_stid.sc_stateid));
-out:
-       open->op_delegate_type = flag;
-       if (flag == NFS4_OPEN_DELEGATE_NONE) {
-               if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS &&
-                   open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE)
-                       dprintk("NFSD: WARNING: refusing delegation reclaim\n");
-
-               /* 4.1 client asking for a delegation? */
-               if (open->op_deleg_want)
-                       nfsd4_open_deleg_none_ext(open, status);
-       }
+       open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
        return;
 out_free:
        unhash_stid(&dp->dl_stid);
        nfs4_put_delegation(dp);
 out_no_deleg:
-       flag = NFS4_OPEN_DELEGATE_NONE;
-       goto out;
+       open->op_delegate_type = NFS4_OPEN_DELEGATE_NONE;
+       if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS &&
+           open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE) {
+               dprintk("NFSD: WARNING: refusing delegation reclaim\n");
+               open->op_recall = 1;
+       }
+
+       /* 4.1 client asking for a delegation? */
+       if (open->op_deleg_want)
+               nfsd4_open_deleg_none_ext(open, status);
+       return;
 }
 
 static void nfsd4_deleg_xgrade_none_ext(struct nfsd4_open *open,
@@ -3427,7 +3513,7 @@ grace_disallows_io(struct net *net, struct inode *inode)
 /* Returns true iff a is later than b: */
 static bool stateid_generation_after(stateid_t *a, stateid_t *b)
 {
-       return (s32)a->si_generation - (s32)b->si_generation > 0;
+       return (s32)(a->si_generation - b->si_generation) > 0;
 }
 
 static __be32 check_stateid_generation(stateid_t *in, stateid_t *ref, bool has_session)
@@ -4435,7 +4521,6 @@ __be32
 nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
            struct nfsd4_locku *locku)
 {
-       struct nfs4_lockowner *lo;
        struct nfs4_ol_stateid *stp;
        struct file *filp = NULL;
        struct file_lock *file_lock = NULL;
@@ -4468,10 +4553,9 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                status = nfserr_jukebox;
                goto out;
        }
-       lo = lockowner(stp->st_stateowner);
        locks_init_lock(file_lock);
        file_lock->fl_type = F_UNLCK;
-       file_lock->fl_owner = (fl_owner_t)lo;
+       file_lock->fl_owner = (fl_owner_t)lockowner(stp->st_stateowner);
        file_lock->fl_pid = current->tgid;
        file_lock->fl_file = filp;
        file_lock->fl_flags = FL_POSIX;
@@ -4490,11 +4574,6 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        update_stateid(&stp->st_stid.sc_stateid);
        memcpy(&locku->lu_stateid, &stp->st_stid.sc_stateid, sizeof(stateid_t));
 
-       if (nfsd4_has_session(cstate) && !check_for_locks(stp->st_file, lo)) {
-               WARN_ON_ONCE(cstate->replay_owner);
-               release_lockowner(lo);
-       }
-
 out:
        nfsd4_bump_seqid(cstate, status);
        if (!cstate->replay_owner)
index 6cd86e0..0c0f3ea 100644 (file)
 #include "cache.h"
 #include "netns.h"
 
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+#include <linux/security.h>
+#endif
+
+
 #define NFSDDBG_FACILITY               NFSDDBG_XDR
 
 /*
@@ -134,6 +139,19 @@ xdr_error:                                 \
        }                                       \
 } while (0)
 
+static void next_decode_page(struct nfsd4_compoundargs *argp)
+{
+       argp->pagelist++;
+       argp->p = page_address(argp->pagelist[0]);
+       if (argp->pagelen < PAGE_SIZE) {
+               argp->end = argp->p + (argp->pagelen>>2);
+               argp->pagelen = 0;
+       } else {
+               argp->end = argp->p + (PAGE_SIZE>>2);
+               argp->pagelen -= PAGE_SIZE;
+       }
+}
+
 static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
 {
        /* We want more bytes than seem to be available.
@@ -161,16 +179,7 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
         * guarantee p points to at least nbytes bytes.
         */
        memcpy(p, argp->p, avail);
-       /* step to next page */
-       argp->p = page_address(argp->pagelist[0]);
-       argp->pagelist++;
-       if (argp->pagelen < PAGE_SIZE) {
-               argp->end = argp->p + (argp->pagelen>>2);
-               argp->pagelen = 0;
-       } else {
-               argp->end = argp->p + (PAGE_SIZE>>2);
-               argp->pagelen -= PAGE_SIZE;
-       }
+       next_decode_page(argp);
        memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
        argp->p += XDR_QUADLEN(nbytes - avail);
        return p;
@@ -242,7 +251,8 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
 
 static __be32
 nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
-                  struct iattr *iattr, struct nfs4_acl **acl)
+                  struct iattr *iattr, struct nfs4_acl **acl,
+                  struct xdr_netobj *label)
 {
        int expected_len, len = 0;
        u32 dummy32;
@@ -380,6 +390,32 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
                        goto xdr_error;
                }
        }
+
+       label->len = 0;
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+       if (bmval[2] & FATTR4_WORD2_SECURITY_LABEL) {
+               READ_BUF(4);
+               len += 4;
+               READ32(dummy32); /* lfs: we don't use it */
+               READ_BUF(4);
+               len += 4;
+               READ32(dummy32); /* pi: we don't use it either */
+               READ_BUF(4);
+               len += 4;
+               READ32(dummy32);
+               READ_BUF(dummy32);
+               if (dummy32 > NFSD4_MAX_SEC_LABEL_LEN)
+                       return nfserr_badlabel;
+               len += (XDR_QUADLEN(dummy32) << 2);
+               READMEM(buf, dummy32);
+               label->data = kzalloc(dummy32 + 1, GFP_KERNEL);
+               if (!label->data)
+                       return nfserr_jukebox;
+               defer_free(argp, kfree, label->data);
+               memcpy(label->data, buf, dummy32);
+       }
+#endif
+
        if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
            || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
            || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2)
@@ -428,7 +464,11 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_
        /* callback_sec_params4 */
        READ_BUF(4);
        READ32(nr_secflavs);
-       cbs->flavor = (u32)(-1);
+       if (nr_secflavs)
+               cbs->flavor = (u32)(-1);
+       else
+               /* Is this legal? Be generous, take it to mean AUTH_NONE: */
+               cbs->flavor = 0;
        for (i = 0; i < nr_secflavs; ++i) {
                READ_BUF(4);
                READ32(dummy);
@@ -576,7 +616,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
                return status;
 
        status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr,
-                                   &create->cr_acl);
+                                   &create->cr_acl, &create->cr_label);
        if (status)
                goto out;
 
@@ -827,7 +867,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
                case NFS4_CREATE_UNCHECKED:
                case NFS4_CREATE_GUARDED:
                        status = nfsd4_decode_fattr(argp, open->op_bmval,
-                               &open->op_iattr, &open->op_acl);
+                               &open->op_iattr, &open->op_acl, &open->op_label);
                        if (status)
                                goto out;
                        break;
@@ -841,7 +881,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
                        READ_BUF(NFS4_VERIFIER_SIZE);
                        COPYMEM(open->op_verf.data, NFS4_VERIFIER_SIZE);
                        status = nfsd4_decode_fattr(argp, open->op_bmval,
-                               &open->op_iattr, &open->op_acl);
+                               &open->op_iattr, &open->op_acl, &open->op_label);
                        if (status)
                                goto out;
                        break;
@@ -1063,7 +1103,7 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *seta
        if (status)
                return status;
        return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
-                                 &setattr->sa_acl);
+                                 &setattr->sa_acl, &setattr->sa_label);
 }
 
 static __be32
@@ -1567,6 +1607,7 @@ struct nfsd4_minorversion_ops {
 static struct nfsd4_minorversion_ops nfsd4_minorversion[] = {
        [0] = { nfsd4_dec_ops, ARRAY_SIZE(nfsd4_dec_ops) },
        [1] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
+       [2] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) },
 };
 
 static __be32
@@ -1953,6 +1994,36 @@ nfsd4_encode_aclname(struct svc_rqst *rqstp, struct nfs4_ace *ace,
                              FATTR4_WORD0_RDATTR_ERROR)
 #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
 
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+static inline __be32
+nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen)
+{
+       __be32 *p = *pp;
+
+       if (*buflen < ((XDR_QUADLEN(len) << 2) + 4 + 4 + 4))
+               return nfserr_resource;
+
+       /*
+        * For now we use a 0 here to indicate the null translation; in
+        * the future we may place a call to translation code here.
+        */
+       if ((*buflen -= 8) < 0)
+               return nfserr_resource;
+
+       WRITE32(0); /* lfs */
+       WRITE32(0); /* pi */
+       p = xdr_encode_opaque(p, context, len);
+       *buflen -= (XDR_QUADLEN(len) << 2) + 4;
+
+       *pp = p;
+       return 0;
+}
+#else
+static inline __be32
+nfsd4_encode_security_label(struct svc_rqst *rqstp, void *context, int len, __be32 **pp, int *buflen)
+{ return 0; }
+#endif
+
 static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err)
 {
        /* As per referral draft:  */
@@ -2012,6 +2083,9 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
        int err;
        int aclsupport = 0;
        struct nfs4_acl *acl = NULL;
+       void *context = NULL;
+       int contextlen;
+       bool contextsupport = false;
        struct nfsd4_compoundres *resp = rqstp->rq_resp;
        u32 minorversion = resp->cstate.minorversion;
        struct path path = {
@@ -2065,6 +2139,21 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                }
        }
 
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+       if ((bmval[2] & FATTR4_WORD2_SECURITY_LABEL) ||
+                       bmval[0] & FATTR4_WORD0_SUPPORTED_ATTRS) {
+               err = security_inode_getsecctx(dentry->d_inode,
+                                               &context, &contextlen);
+               contextsupport = (err == 0);
+               if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
+                       if (err == -EOPNOTSUPP)
+                               bmval2 &= ~FATTR4_WORD2_SECURITY_LABEL;
+                       else if (err)
+                               goto out_nfserr;
+               }
+       }
+#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
+
        if (bmval2) {
                if ((buflen -= 16) < 0)
                        goto out_resource;
@@ -2093,6 +2182,8 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
 
                if (!aclsupport)
                        word0 &= ~FATTR4_WORD0_ACL;
+               if (!contextsupport)
+                       word2 &= ~FATTR4_WORD2_SECURITY_LABEL;
                if (!word2) {
                        if ((buflen -= 12) < 0)
                                goto out_resource;
@@ -2400,6 +2491,12 @@ out_acl:
                        get_parent_attributes(exp, &stat);
                WRITE64(stat.ino);
        }
+       if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
+               status = nfsd4_encode_security_label(rqstp, context,
+                               contextlen, &p, &buflen);
+               if (status)
+                       goto out;
+       }
        if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) {
                WRITE32(3);
                WRITE32(NFSD_SUPPATTR_EXCLCREAT_WORD0);
@@ -2412,6 +2509,10 @@ out_acl:
        status = nfs_ok;
 
 out:
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+       if (context)
+               security_release_secctx(context, contextlen);
+#endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
        kfree(acl);
        if (fhp == &tempfh)
                fh_put(&tempfh);
@@ -3176,16 +3277,18 @@ nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
 {
        __be32 *p;
 
-       RESERVE_SPACE(12);
+       RESERVE_SPACE(16);
        if (nfserr) {
-               WRITE32(2);
+               WRITE32(3);
+               WRITE32(0);
                WRITE32(0);
                WRITE32(0);
        }
        else {
-               WRITE32(2);
+               WRITE32(3);
                WRITE32(setattr->sa_bmval[0]);
                WRITE32(setattr->sa_bmval[1]);
+               WRITE32(setattr->sa_bmval[2]);
        }
        ADJUST_ARGS();
        return nfserr;
@@ -3226,6 +3329,14 @@ nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_w
        return nfserr;
 }
 
+static const u32 nfs4_minimal_spo_must_enforce[2] = {
+       [1] = 1 << (OP_BIND_CONN_TO_SESSION - 32) |
+             1 << (OP_EXCHANGE_ID - 32) |
+             1 << (OP_CREATE_SESSION - 32) |
+             1 << (OP_DESTROY_SESSION - 32) |
+             1 << (OP_DESTROY_CLIENTID - 32)
+};
+
 static __be32
 nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
                         struct nfsd4_exchange_id *exid)
@@ -3264,6 +3375,20 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
        /* state_protect4_r. Currently only support SP4_NONE */
        BUG_ON(exid->spa_how != SP4_NONE);
        WRITE32(exid->spa_how);
+       switch (exid->spa_how) {
+       case SP4_NONE:
+               break;
+       case SP4_MACH_CRED:
+               /* spo_must_enforce bitmap: */
+               WRITE32(2);
+               WRITE32(nfs4_minimal_spo_must_enforce[0]);
+               WRITE32(nfs4_minimal_spo_must_enforce[1]);
+               /* empty spo_must_allow bitmap: */
+               WRITE32(0);
+               break;
+       default:
+               WARN_ON_ONCE(1);
+       }
 
        /* The server_owner struct */
        WRITE64(minor_id);      /* Minor id */
@@ -3635,13 +3760,17 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
        iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
        BUG_ON(iov->iov_len > PAGE_SIZE);
        if (nfsd4_has_session(cs)) {
+               struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+               struct nfs4_client *clp = cs->session->se_client;
                if (cs->status != nfserr_replay_cache) {
                        nfsd4_store_cache_entry(resp);
                        cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE;
                }
                /* Renew the clientid on success and on replay */
-               put_client_renew(cs->session->se_client);
+               spin_lock(&nn->client_lock);
                nfsd4_put_session(cs->session);
+               spin_unlock(&nn->client_lock);
+               put_client_renew(clp);
        }
        return 1;
 }
index c0d9317..2bbd94e 100644 (file)
@@ -24,7 +24,7 @@
 /*
  * nfsd version
  */
-#define NFSD_SUPPORTED_MINOR_VERSION   1
+#define NFSD_SUPPORTED_MINOR_VERSION   2
 /*
  * Maximum blocksizes supported by daemon under various circumstances.
  */
@@ -328,6 +328,13 @@ void               nfsd_lockd_shutdown(void);
 #define NFSD4_1_SUPPORTED_ATTRS_WORD2 \
        (NFSD4_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SUPPATTR_EXCLCREAT)
 
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+#define NFSD4_2_SUPPORTED_ATTRS_WORD2 \
+       (NFSD4_1_SUPPORTED_ATTRS_WORD2 | FATTR4_WORD2_SECURITY_LABEL)
+#else
+#define NFSD4_2_SUPPORTED_ATTRS_WORD2 0
+#endif
+
 static inline u32 nfsd_suppattrs0(u32 minorversion)
 {
        return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD0
@@ -342,8 +349,11 @@ static inline u32 nfsd_suppattrs1(u32 minorversion)
 
 static inline u32 nfsd_suppattrs2(u32 minorversion)
 {
-       return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD2
-                           : NFSD4_SUPPORTED_ATTRS_WORD2;
+       switch (minorversion) {
+       default: return NFSD4_2_SUPPORTED_ATTRS_WORD2;
+       case 1:  return NFSD4_1_SUPPORTED_ATTRS_WORD2;
+       case 0:  return NFSD4_SUPPORTED_ATTRS_WORD2;
+       }
 }
 
 /* These will return ERR_INVAL if specified in GETATTR or READDIR. */
@@ -356,7 +366,11 @@ static inline u32 nfsd_suppattrs2(u32 minorversion)
 #define NFSD_WRITEABLE_ATTRS_WORD1 \
        (FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \
        | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET)
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+#define NFSD_WRITEABLE_ATTRS_WORD2 FATTR4_WORD2_SECURITY_LABEL
+#else
 #define NFSD_WRITEABLE_ATTRS_WORD2 0
+#endif
 
 #define NFSD_SUPPATTR_EXCLCREAT_WORD0 \
        NFSD_WRITEABLE_ATTRS_WORD0
index 262df5c..6b9f48c 100644 (file)
@@ -116,7 +116,7 @@ struct svc_program          nfsd_program = {
 
 };
 
-u32 nfsd_supported_minorversion;
+u32 nfsd_supported_minorversion = 1;
 
 int nfsd_vers(int vers, enum vers_op change)
 {
index 274e2a1..424d8f5 100644 (file)
@@ -246,6 +246,7 @@ struct nfs4_client {
        nfs4_verifier           cl_verifier;    /* generated by client */
        time_t                  cl_time;        /* time of last lease renewal */
        struct sockaddr_storage cl_addr;        /* client ipaddress */
+       bool                    cl_mach_cred;   /* SP4_MACH_CRED in force */
        struct svc_cred         cl_cred;        /* setclientid principal */
        clientid_t              cl_clientid;    /* generated by server */
        nfs4_verifier           cl_confirm;     /* generated by server */
index a6bc8a7..8ff6a00 100644 (file)
@@ -28,6 +28,7 @@
 #include <asm/uaccess.h>
 #include <linux/exportfs.h>
 #include <linux/writeback.h>
+#include <linux/security.h>
 
 #ifdef CONFIG_NFSD_V3
 #include "xdr3.h"
@@ -621,6 +622,33 @@ int nfsd4_is_junction(struct dentry *dentry)
                return 0;
        return 1;
 }
+#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
+__be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
+               struct xdr_netobj *label)
+{
+       __be32 error;
+       int host_error;
+       struct dentry *dentry;
+
+       error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, NFSD_MAY_SATTR);
+       if (error)
+               return error;
+
+       dentry = fhp->fh_dentry;
+
+       mutex_lock(&dentry->d_inode->i_mutex);
+       host_error = security_inode_setsecctx(dentry, label->data, label->len);
+       mutex_unlock(&dentry->d_inode->i_mutex);
+       return nfserrno(host_error);
+}
+#else
+__be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
+               struct xdr_netobj *label)
+{
+       return nfserr_notsupp;
+}
+#endif
+
 #endif /* defined(CONFIG_NFSD_V4) */
 
 #ifdef CONFIG_NFSD_V3
index 5b58941..a4be2e3 100644 (file)
@@ -39,7 +39,6 @@
 typedef int (*nfsd_dirop_t)(struct inode *, struct dentry *, int, int);
 
 /* nfsd/vfs.c */
-int            fh_lock_parent(struct svc_fh *, struct dentry *);
 int            nfsd_racache_init(int);
 void           nfsd_racache_shutdown(void);
 int            nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
@@ -56,6 +55,8 @@ int nfsd_mountpoint(struct dentry *, struct svc_export *);
 __be32          nfsd4_set_nfs4_acl(struct svc_rqst *, struct svc_fh *,
                     struct nfs4_acl *);
 int             nfsd4_get_nfs4_acl(struct svc_rqst *, struct dentry *, struct nfs4_acl **);
+__be32          nfsd4_set_nfs4_label(struct svc_rqst *, struct svc_fh *,
+                   struct xdr_netobj *);
 #endif /* CONFIG_NFSD_V4 */
 __be32         nfsd_create(struct svc_rqst *, struct svc_fh *,
                                char *name, int len, struct iattr *attrs,
@@ -92,17 +93,13 @@ __be32              nfsd_remove(struct svc_rqst *,
                                struct svc_fh *, char *, int);
 __be32         nfsd_unlink(struct svc_rqst *, struct svc_fh *, int type,
                                char *name, int len);
-int            nfsd_truncate(struct svc_rqst *, struct svc_fh *,
-                               unsigned long size);
 __be32         nfsd_readdir(struct svc_rqst *, struct svc_fh *,
                             loff_t *, struct readdir_cd *, filldir_t);
 __be32         nfsd_statfs(struct svc_rqst *, struct svc_fh *,
                                struct kstatfs *, int access);
 
-int            nfsd_notify_change(struct inode *, struct iattr *);
 __be32         nfsd_permission(struct svc_rqst *, struct svc_export *,
                                struct dentry *, int);
-int            nfsd_sync_dir(struct dentry *dp);
 
 #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
 struct posix_acl *nfsd_get_posix_acl(struct svc_fh *, int);
index 3b271d2..b3ed644 100644 (file)
@@ -40,6 +40,7 @@
 #include "state.h"
 #include "nfsd.h"
 
+#define NFSD4_MAX_SEC_LABEL_LEN        2048
 #define NFSD4_MAX_TAGLEN       128
 #define XDR_LEN(n)                     (((n) + 3) & ~3)
 
@@ -118,6 +119,7 @@ struct nfsd4_create {
        struct iattr    cr_iattr;           /* request */
        struct nfsd4_change_info  cr_cinfo; /* response */
        struct nfs4_acl *cr_acl;
+       struct xdr_netobj cr_label;
 };
 #define cr_linklen     u.link.namelen
 #define cr_linkname    u.link.name
@@ -246,6 +248,7 @@ struct nfsd4_open {
        struct nfs4_file *op_file;          /* used during processing */
        struct nfs4_ol_stateid *op_stp;     /* used during processing */
        struct nfs4_acl *op_acl;
+       struct xdr_netobj op_label;
 };
 #define op_iattr       iattr
 
@@ -330,6 +333,7 @@ struct nfsd4_setattr {
        u32             sa_bmval[3];        /* request */
        struct iattr    sa_iattr;           /* request */
        struct nfs4_acl *sa_acl;
+       struct xdr_netobj sa_label;
 };
 
 struct nfsd4_setclientid {
index 303399b..6ce690d 100644 (file)
@@ -57,6 +57,7 @@ struct cache_head {
 #define        CACHE_VALID     0       /* Entry contains valid data */
 #define        CACHE_NEGATIVE  1       /* Negative entry - there is no match for the key */
 #define        CACHE_PENDING   2       /* An upcall has been sent but no reply received yet*/
+#define        CACHE_CLEANED   3       /* Entry has been cleaned from cache */
 
 #define        CACHE_NEW_EXPIRY 120    /* keep new things pending confirmation for 120 seconds */
 
@@ -148,6 +149,24 @@ struct cache_deferred_req {
                                           int too_many);
 };
 
+/*
+ * timestamps kept in the cache are expressed in seconds
+ * since boot.  This is the best for measuring differences in
+ * real time.
+ */
+static inline time_t seconds_since_boot(void)
+{
+       struct timespec boot;
+       getboottime(&boot);
+       return get_seconds() - boot.tv_sec;
+}
+
+static inline time_t convert_to_wallclock(time_t sinceboot)
+{
+       struct timespec boot;
+       getboottime(&boot);
+       return boot.tv_sec + sinceboot;
+}
 
 extern const struct file_operations cache_file_operations_pipefs;
 extern const struct file_operations content_file_operations_pipefs;
@@ -181,15 +200,10 @@ static inline void cache_put(struct cache_head *h, struct cache_detail *cd)
        kref_put(&h->ref, cd->cache_put);
 }
 
-static inline int cache_valid(struct cache_head *h)
+static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h)
 {
-       /* If an item has been unhashed pending removal when
-        * the refcount drops to 0, the expiry_time will be
-        * set to 0.  We don't want to consider such items
-        * valid in this context even though CACHE_VALID is
-        * set.
-        */
-       return (h->expiry_time != 0 && test_bit(CACHE_VALID, &h->flags));
+       return  (h->expiry_time < seconds_since_boot()) ||
+               (detail->flush_time > h->last_refresh);
 }
 
 extern int cache_check(struct cache_detail *detail,
@@ -250,25 +264,6 @@ static inline int get_uint(char **bpp, unsigned int *anint)
        return 0;
 }
 
-/*
- * timestamps kept in the cache are expressed in seconds
- * since boot.  This is the best for measuring differences in
- * real time.
- */
-static inline time_t seconds_since_boot(void)
-{
-       struct timespec boot;
-       getboottime(&boot);
-       return get_seconds() - boot.tv_sec;
-}
-
-static inline time_t convert_to_wallclock(time_t sinceboot)
-{
-       struct timespec boot;
-       getboottime(&boot);
-       return boot.tv_sec + sinceboot;
-}
-
 static inline time_t get_expiry(char **bpp)
 {
        int rv;
index 161463e..1f911cc 100644 (file)
@@ -151,6 +151,8 @@ struct gss_api_mech *gss_mech_get_by_pseudoflavor(u32);
 /* Fill in an array with a list of supported pseudoflavors */
 int gss_mech_list_pseudoflavors(rpc_authflavor_t *, int);
 
+struct gss_api_mech * gss_mech_get(struct gss_api_mech *);
+
 /* For every successful gss_mech_get or gss_mech_get_by_* call there must be a
  * corresponding call to gss_mech_put. */
 void gss_mech_put(struct gss_api_mech *);
index ff374ab..8d71d65 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/string.h>
 #include <linux/sunrpc/msg_prot.h>
 #include <linux/sunrpc/cache.h>
+#include <linux/sunrpc/gss_api.h>
 #include <linux/hash.h>
 #include <linux/cred.h>
 
@@ -23,13 +24,23 @@ struct svc_cred {
        struct group_info       *cr_group_info;
        u32                     cr_flavor; /* pseudoflavor */
        char                    *cr_principal; /* for gss */
+       struct gss_api_mech     *cr_gss_mech;
 };
 
+static inline void init_svc_cred(struct svc_cred *cred)
+{
+       cred->cr_group_info = NULL;
+       cred->cr_principal = NULL;
+       cred->cr_gss_mech = NULL;
+}
+
 static inline void free_svc_cred(struct svc_cred *cred)
 {
        if (cred->cr_group_info)
                put_group_info(cred->cr_group_info);
        kfree(cred->cr_principal);
+       gss_mech_put(cred->cr_gss_mech);
+       init_svc_cred(cred);
 }
 
 struct svc_rqst;               /* forward decl */
index defa9d3..27ce262 100644 (file)
@@ -139,11 +139,12 @@ void gss_mech_unregister(struct gss_api_mech *gm)
 }
 EXPORT_SYMBOL_GPL(gss_mech_unregister);
 
-static struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
+struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
 {
        __module_get(gm->gm_owner);
        return gm;
 }
+EXPORT_SYMBOL(gss_mech_get);
 
 static struct gss_api_mech *
 _gss_mech_get_by_name(const char *name)
@@ -360,6 +361,7 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
        }
        return 0;
 }
+EXPORT_SYMBOL(gss_pseudoflavor_to_service);
 
 char *
 gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
@@ -379,6 +381,7 @@ gss_mech_put(struct gss_api_mech * gm)
        if (gm)
                module_put(gm->gm_owner);
 }
+EXPORT_SYMBOL(gss_mech_put);
 
 /* The mech could probably be determined from the token instead, but it's just
  * as easy for now to pass it in. */
index b05ace4..d0347d1 100644 (file)
@@ -377,8 +377,7 @@ rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
        new->handle.data = tmp->handle.data;
        tmp->handle.data = NULL;
        new->mechctx = NULL;
-       new->cred.cr_group_info = NULL;
-       new->cred.cr_principal = NULL;
+       init_svc_cred(&new->cred);
 }
 
 static void
@@ -392,9 +391,7 @@ update_rsc(struct cache_head *cnew, struct cache_head *ctmp)
        memset(&new->seqdata, 0, sizeof(new->seqdata));
        spin_lock_init(&new->seqdata.sd_lock);
        new->cred = tmp->cred;
-       tmp->cred.cr_group_info = NULL;
-       new->cred.cr_principal = tmp->cred.cr_principal;
-       tmp->cred.cr_principal = NULL;
+       init_svc_cred(&tmp->cred);
 }
 
 static struct cache_head *
@@ -487,7 +484,7 @@ static int rsc_parse(struct cache_detail *cd,
                len = qword_get(&mesg, buf, mlen);
                if (len < 0)
                        goto out;
-               gm = gss_mech_get_by_name(buf);
+               gm = rsci.cred.cr_gss_mech = gss_mech_get_by_name(buf);
                status = -EOPNOTSUPP;
                if (!gm)
                        goto out;
@@ -517,7 +514,6 @@ static int rsc_parse(struct cache_detail *cd,
        rscp = rsc_update(cd, &rsci, rscp);
        status = 0;
 out:
-       gss_mech_put(gm);
        rsc_free(&rsci);
        if (rscp)
                cache_put(&rscp->h, cd);
index 80fe5c8..49eb370 100644 (file)
@@ -50,12 +50,6 @@ static void cache_init(struct cache_head *h)
        h->last_refresh = now;
 }
 
-static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h)
-{
-       return  (h->expiry_time < seconds_since_boot()) ||
-               (detail->flush_time > h->last_refresh);
-}
-
 struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
                                       struct cache_head *key, int hash)
 {
@@ -201,7 +195,7 @@ static int cache_make_upcall(struct cache_detail *cd, struct cache_head *h)
        return sunrpc_cache_pipe_upcall(cd, h);
 }
 
-static inline int cache_is_valid(struct cache_detail *detail, struct cache_head *h)
+static inline int cache_is_valid(struct cache_head *h)
 {
        if (!test_bit(CACHE_VALID, &h->flags))
                return -EAGAIN;
@@ -227,16 +221,15 @@ static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h
        int rv;
 
        write_lock(&detail->hash_lock);
-       rv = cache_is_valid(detail, h);
-       if (rv != -EAGAIN) {
-               write_unlock(&detail->hash_lock);
-               return rv;
+       rv = cache_is_valid(h);
+       if (rv == -EAGAIN) {
+               set_bit(CACHE_NEGATIVE, &h->flags);
+               cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
+               rv = -ENOENT;
        }
-       set_bit(CACHE_NEGATIVE, &h->flags);
-       cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
        write_unlock(&detail->hash_lock);
        cache_fresh_unlocked(h, detail);
-       return -ENOENT;
+       return rv;
 }
 
 /*
@@ -260,7 +253,7 @@ int cache_check(struct cache_detail *detail,
        long refresh_age, age;
 
        /* First decide return status as best we can */
-       rv = cache_is_valid(detail, h);
+       rv = cache_is_valid(h);
 
        /* now see if we want to start an upcall */
        refresh_age = (h->expiry_time - h->last_refresh);
@@ -269,19 +262,17 @@ int cache_check(struct cache_detail *detail,
        if (rqstp == NULL) {
                if (rv == -EAGAIN)
                        rv = -ENOENT;
-       } else if (rv == -EAGAIN || age > refresh_age/2) {
+       } else if (rv == -EAGAIN ||
+                  (h->expiry_time != 0 && age > refresh_age/2)) {
                dprintk("RPC:       Want update, refage=%ld, age=%ld\n",
                                refresh_age, age);
                if (!test_and_set_bit(CACHE_PENDING, &h->flags)) {
                        switch (cache_make_upcall(detail, h)) {
                        case -EINVAL:
-                               clear_bit(CACHE_PENDING, &h->flags);
-                               cache_revisit_request(h);
                                rv = try_to_negate_entry(detail, h);
                                break;
                        case -EAGAIN:
-                               clear_bit(CACHE_PENDING, &h->flags);
-                               cache_revisit_request(h);
+                               cache_fresh_unlocked(h, detail);
                                break;
                        }
                }
@@ -293,7 +284,7 @@ int cache_check(struct cache_detail *detail,
                         * Request was not deferred; handle it as best
                         * we can ourselves:
                         */
-                       rv = cache_is_valid(detail, h);
+                       rv = cache_is_valid(h);
                        if (rv == -EAGAIN)
                                rv = -ETIMEDOUT;
                }
@@ -310,7 +301,7 @@ EXPORT_SYMBOL_GPL(cache_check);
  * a current pointer into that list and into the table
  * for that entry.
  *
- * Each time clean_cache is called it finds the next non-empty entry
+ * Each time cache_clean is called it finds the next non-empty entry
  * in the current table and walks the list in that entry
  * looking for entries that can be removed.
  *
@@ -457,9 +448,8 @@ static int cache_clean(void)
                        current_index ++;
                spin_unlock(&cache_list_lock);
                if (ch) {
-                       if (test_and_clear_bit(CACHE_PENDING, &ch->flags))
-                               cache_dequeue(current_detail, ch);
-                       cache_revisit_request(ch);
+                       set_bit(CACHE_CLEANED, &ch->flags);
+                       cache_fresh_unlocked(ch, d);
                        cache_put(ch, d);
                }
        } else
@@ -1036,23 +1026,32 @@ static int cache_release(struct inode *inode, struct file *filp,
 
 static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch)
 {
-       struct cache_queue *cq;
+       struct cache_queue *cq, *tmp;
+       struct cache_request *cr;
+       struct list_head dequeued;
+
+       INIT_LIST_HEAD(&dequeued);
        spin_lock(&queue_lock);
-       list_for_each_entry(cq, &detail->queue, list)
+       list_for_each_entry_safe(cq, tmp, &detail->queue, list)
                if (!cq->reader) {
-                       struct cache_request *cr = container_of(cq, struct cache_request, q);
+                       cr = container_of(cq, struct cache_request, q);
                        if (cr->item != ch)
                                continue;
+                       if (test_bit(CACHE_PENDING, &ch->flags))
+                               /* Lost a race and it is pending again */
+                               break;
                        if (cr->readers != 0)
                                continue;
-                       list_del(&cr->q.list);
-                       spin_unlock(&queue_lock);
-                       cache_put(cr->item, detail);
-                       kfree(cr->buf);
-                       kfree(cr);
-                       return;
+                       list_move(&cr->q.list, &dequeued);
                }
        spin_unlock(&queue_lock);
+       while (!list_empty(&dequeued)) {
+               cr = list_entry(dequeued.next, struct cache_request, q.list);
+               list_del(&cr->q.list);
+               cache_put(cr->item, detail);
+               kfree(cr->buf);
+               kfree(cr);
+       }
 }
 
 /*
@@ -1166,6 +1165,7 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
 
        char *buf;
        struct cache_request *crq;
+       int ret = 0;
 
        if (!detail->cache_request)
                return -EINVAL;
@@ -1174,6 +1174,9 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
                warn_no_listener(detail);
                return -EINVAL;
        }
+       if (test_bit(CACHE_CLEANED, &h->flags))
+               /* Too late to make an upcall */
+               return -EAGAIN;
 
        buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
        if (!buf)
@@ -1191,10 +1194,18 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h)
        crq->len = 0;
        crq->readers = 0;
        spin_lock(&queue_lock);
-       list_add_tail(&crq->q.list, &detail->queue);
+       if (test_bit(CACHE_PENDING, &h->flags))
+               list_add_tail(&crq->q.list, &detail->queue);
+       else
+               /* Lost a race, no longer PENDING, so don't enqueue */
+               ret = -EAGAIN;
        spin_unlock(&queue_lock);
        wake_up(&queue_wait);
-       return 0;
+       if (ret == -EAGAIN) {
+               kfree(buf);
+               kfree(crq);
+       }
+       return ret;
 }
 EXPORT_SYMBOL_GPL(sunrpc_cache_pipe_upcall);
 
index 06bdf5a..621ca7b 100644 (file)
@@ -347,13 +347,13 @@ ip_map_cached_get(struct svc_xprt *xprt)
                spin_lock(&xprt->xpt_lock);
                ipm = xprt->xpt_auth_cache;
                if (ipm != NULL) {
-                       if (!cache_valid(&ipm->h)) {
+                       sn = net_generic(xprt->xpt_net, sunrpc_net_id);
+                       if (cache_is_expired(sn->ip_map_cache, &ipm->h)) {
                                /*
                                 * The entry has been invalidated since it was
                                 * remembered, e.g. by a second mount from the
                                 * same IP address.
                                 */
-                               sn = net_generic(xprt->xpt_net, sunrpc_net_id);
                                xprt->xpt_auth_cache = NULL;
                                spin_unlock(&xprt->xpt_lock);
                                cache_put(&ipm->h, sn->ip_map_cache);
@@ -493,8 +493,6 @@ static int unix_gid_parse(struct cache_detail *cd,
        if (rv)
                return -EINVAL;
        uid = make_kuid(&init_user_ns, id);
-       if (!uid_valid(uid))
-               return -EINVAL;
        ug.uid = uid;
 
        expiry = get_expiry(&mesg);
index 0f679df..305374d 100644 (file)
@@ -917,7 +917,10 @@ static void svc_tcp_clear_pages(struct svc_sock *svsk)
        len = svsk->sk_datalen;
        npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
        for (i = 0; i < npages; i++) {
-               BUG_ON(svsk->sk_pages[i] == NULL);
+               if (svsk->sk_pages[i] == NULL) {
+                       WARN_ON_ONCE(1);
+                       continue;
+               }
                put_page(svsk->sk_pages[i]);
                svsk->sk_pages[i] = NULL;
        }
@@ -1092,8 +1095,10 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
                goto err_noclose;
        }
 
-       if (svc_sock_reclen(svsk) < 8)
+       if (svsk->sk_datalen < 8) {
+               svsk->sk_datalen = 0;
                goto err_delete; /* client is nuts. */
+       }
 
        rqstp->rq_arg.len = svsk->sk_datalen;
        rqstp->rq_arg.page_base = 0;
index 412de7c..ddf0602 100644 (file)
@@ -2534,7 +2534,6 @@ static struct rpc_xprt_ops bc_tcp_ops = {
        .reserve_xprt           = xprt_reserve_xprt,
        .release_xprt           = xprt_release_xprt,
        .alloc_slot             = xprt_alloc_slot,
-       .rpcbind                = xs_local_rpcbind,
        .buf_alloc              = bc_malloc,
        .buf_free               = bc_free,
        .send_request           = bc_send_request,
index d32e16e..32b5157 100644 (file)
@@ -858,7 +858,7 @@ static int cap_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
 
 static int cap_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
 {
-       return 0;
+       return -EOPNOTSUPP;
 }
 #ifdef CONFIG_KEYS
 static int cap_key_alloc(struct key *key, const struct cred *cred,