forked from luck/tmp_suning_uos_patched
Merge branch 'for-3.11' of git://linux-nfs.org/~bfields/linux
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 ...
This commit is contained in:
commit
0ff08ba5d0
|
@ -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
|
||||
|
|
|
@ -42,6 +42,36 @@
|
|||
#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:
|
||||
|
|
|
@ -97,19 +97,20 @@ nfs4_lock_state(void)
|
|||
|
||||
static void free_session(struct nfsd4_session *);
|
||||
|
||||
void nfsd4_put_session(struct nfsd4_session *ses)
|
||||
{
|
||||
atomic_dec(&ses->se_ref);
|
||||
}
|
||||
|
||||
static bool is_session_dead(struct nfsd4_session *ses)
|
||||
{
|
||||
return ses->se_flags & NFS4_SESSION_DEAD;
|
||||
}
|
||||
|
||||
static __be32 mark_session_dead_locked(struct nfsd4_session *ses)
|
||||
void nfsd4_put_session(struct nfsd4_session *ses)
|
||||
{
|
||||
if (atomic_read(&ses->se_ref))
|
||||
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, int ref_held_by_me)
|
||||
{
|
||||
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, ¤t_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 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
|
|||
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)
|
||||
|
|
|
@ -55,6 +55,11 @@
|
|||
#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 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
|
|||
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 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue
Block a user