mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
Merge tag 'nfsd-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux
Pull nfsd updates from Chuck Lever: "Jeff Layton contributed a scalability improvement to NFSD's NFSv4 backchannel session implementation. This improvement is intended to increase the rate at which NFSD can safely recall NFSv4 delegations from clients, to avoid the need to revoke them. Revoking requires a slow state recovery process. A wide variety of bug fixes and other incremental improvements make up the bulk of commits in this series. As always I am grateful to the NFSD contributors, reviewers, testers, and bug reporters who participated during this cycle" * tag 'nfsd-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux: (72 commits) nfsd: allow for up to 32 callback session slots nfs_common: must not hold RCU while calling nfsd_file_put_local nfsd: get rid of include ../internal.h nfsd: fix nfs4_openowner leak when concurrent nfsd4_open occur NFSD: Add nfsd4_copy time-to-live NFSD: Add a laundromat reaper for async copy state NFSD: Block DESTROY_CLIENTID only when there are ongoing async COPY operations NFSD: Handle an NFS4ERR_DELAY response to CB_OFFLOAD NFSD: Free async copy information in nfsd4_cb_offload_release() NFSD: Fix nfsd4_shutdown_copy() NFSD: Add a tracepoint to record canceled async COPY operations nfsd: make nfsd4_session->se_flags a bool nfsd: remove nfsd4_session->se_bchannel nfsd: make use of warning provided by refcount_t nfsd: Don't fail OP_SETCLIENTID when there are too many clients. svcrdma: fix miss destroy percpu_counter in svc_rdma_proc_init() xdrgen: Remove program_stat_to_errno() call sites xdrgen: Update the files included in client-side source code xdrgen: Remove check for "nfs_ok" in C templates xdrgen: Remove tracepoint call site ...
This commit is contained in:
@@ -12436,6 +12436,7 @@ F: include/trace/misc/sunrpc.h
|
||||
F: include/uapi/linux/nfsd/
|
||||
F: include/uapi/linux/sunrpc/
|
||||
F: net/sunrpc/
|
||||
F: tools/net/sunrpc/
|
||||
|
||||
KERNEL PACMAN PACKAGING (in addition to generic KERNEL BUILD)
|
||||
M: Thomas Weißschuh <linux@weissschuh.net>
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
/*
|
||||
* linux/fs/lockd/clntxdr.c
|
||||
*
|
||||
* XDR functions to encode/decode NLM version 3 RPC arguments and results.
|
||||
* NLM version 3 is backwards compatible with NLM versions 1 and 2.
|
||||
* XDR functions to encode/decode NLM version 1 and 3 RPC
|
||||
* arguments and results. NLM version 2 is not specified
|
||||
* by a standard, thus it is not implemented.
|
||||
*
|
||||
* NLM client-side only.
|
||||
*
|
||||
|
||||
@@ -46,14 +46,15 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
|
||||
if (filp != NULL) {
|
||||
int mode = lock_to_openmode(&lock->fl);
|
||||
|
||||
lock->fl.c.flc_flags = FL_POSIX;
|
||||
|
||||
error = nlm_lookup_file(rqstp, &file, lock);
|
||||
if (error)
|
||||
goto no_locks;
|
||||
*filp = file;
|
||||
|
||||
/* Set up the missing parts of the file_lock structure */
|
||||
lock->fl.c.flc_flags = FL_POSIX;
|
||||
lock->fl.c.flc_file = file->f_file[mode];
|
||||
lock->fl.c.flc_file = file->f_file[mode];
|
||||
lock->fl.c.flc_pid = current->tgid;
|
||||
lock->fl.fl_start = (loff_t)lock->lock_start;
|
||||
lock->fl.fl_end = lock->lock_len ?
|
||||
@@ -108,7 +109,8 @@ __nlm4svc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
|
||||
|
||||
test_owner = argp->lock.fl.c.flc_owner;
|
||||
/* Now check for conflicting locks */
|
||||
resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie);
|
||||
resp->status = nlmsvc_testlock(rqstp, file, host, &argp->lock,
|
||||
&resp->lock);
|
||||
if (resp->status == nlm_drop_reply)
|
||||
rc = rpc_drop_reply;
|
||||
else
|
||||
@@ -142,18 +144,6 @@ __nlm4svc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp)
|
||||
if ((resp->status = nlm4svc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
#if 0
|
||||
/* If supplied state doesn't match current state, we assume it's
|
||||
* an old request that time-warped somehow. Any error return would
|
||||
* do in this case because it's irrelevant anyway.
|
||||
*
|
||||
* NB: We don't retrieve the remote host's state yet.
|
||||
*/
|
||||
if (host->h_nsmstate && host->h_nsmstate != argp->state) {
|
||||
resp->status = nlm_lck_denied_nolocks;
|
||||
} else
|
||||
#endif
|
||||
|
||||
/* Now try to lock the file */
|
||||
resp->status = nlmsvc_lock(rqstp, file, host, &argp->lock,
|
||||
argp->block, &argp->cookie,
|
||||
|
||||
@@ -608,7 +608,7 @@ out:
|
||||
__be32
|
||||
nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
|
||||
struct nlm_host *host, struct nlm_lock *lock,
|
||||
struct nlm_lock *conflock, struct nlm_cookie *cookie)
|
||||
struct nlm_lock *conflock)
|
||||
{
|
||||
int error;
|
||||
int mode;
|
||||
|
||||
@@ -130,7 +130,8 @@ __nlmsvc_proc_test(struct svc_rqst *rqstp, struct nlm_res *resp)
|
||||
test_owner = argp->lock.fl.c.flc_owner;
|
||||
|
||||
/* Now check for conflicting locks */
|
||||
resp->status = cast_status(nlmsvc_testlock(rqstp, file, host, &argp->lock, &resp->lock, &resp->cookie));
|
||||
resp->status = cast_status(nlmsvc_testlock(rqstp, file, host,
|
||||
&argp->lock, &resp->lock));
|
||||
if (resp->status == nlm_drop_reply)
|
||||
rc = rpc_drop_reply;
|
||||
else
|
||||
@@ -165,18 +166,6 @@ __nlmsvc_proc_lock(struct svc_rqst *rqstp, struct nlm_res *resp)
|
||||
if ((resp->status = nlmsvc_retrieve_args(rqstp, argp, &host, &file)))
|
||||
return resp->status == nlm_drop_reply ? rpc_drop_reply :rpc_success;
|
||||
|
||||
#if 0
|
||||
/* If supplied state doesn't match current state, we assume it's
|
||||
* an old request that time-warped somehow. Any error return would
|
||||
* do in this case because it's irrelevant anyway.
|
||||
*
|
||||
* NB: We don't retrieve the remote host's state yet.
|
||||
*/
|
||||
if (host->h_nsmstate && host->h_nsmstate != argp->state) {
|
||||
resp->status = nlm_lck_denied_nolocks;
|
||||
} else
|
||||
#endif
|
||||
|
||||
/* Now try to lock the file */
|
||||
resp->status = cast_status(nlmsvc_lock(rqstp, file, host, &argp->lock,
|
||||
argp->block, &argp->cookie,
|
||||
|
||||
@@ -89,7 +89,6 @@ svcxdr_decode_lock(struct xdr_stream *xdr, struct nlm_lock *lock)
|
||||
return false;
|
||||
|
||||
locks_init_lock(fl);
|
||||
fl->c.flc_flags = FL_POSIX;
|
||||
fl->c.flc_type = F_RDLCK;
|
||||
nlm4svc_set_file_lock_range(fl, lock->lock_start, lock->lock_len);
|
||||
return true;
|
||||
@@ -268,7 +267,6 @@ nlm4svc_decode_shareargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
|
||||
struct nlm_args *argp = rqstp->rq_argp;
|
||||
struct nlm_lock *lock = &argp->lock;
|
||||
|
||||
memset(lock, 0, sizeof(*lock));
|
||||
locks_init_lock(&lock->fl);
|
||||
lock->svid = ~(u32)0;
|
||||
|
||||
|
||||
@@ -155,11 +155,9 @@ struct nfsd_file *nfs_open_local_fh(nfs_uuid_t *uuid,
|
||||
/* We have an implied reference to net thanks to nfsd_serv_try_get */
|
||||
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt,
|
||||
cred, nfs_fh, fmode);
|
||||
if (IS_ERR(localio)) {
|
||||
rcu_read_lock();
|
||||
nfs_to->nfsd_serv_put(net);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
if (IS_ERR(localio))
|
||||
nfs_to_nfsd_net_put(net);
|
||||
|
||||
return localio;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_open_local_fh);
|
||||
|
||||
@@ -40,15 +40,24 @@
|
||||
#define EXPKEY_HASHMAX (1 << EXPKEY_HASHBITS)
|
||||
#define EXPKEY_HASHMASK (EXPKEY_HASHMAX -1)
|
||||
|
||||
static void expkey_put(struct kref *ref)
|
||||
static void expkey_put_work(struct work_struct *work)
|
||||
{
|
||||
struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref);
|
||||
struct svc_expkey *key =
|
||||
container_of(to_rcu_work(work), struct svc_expkey, ek_rcu_work);
|
||||
|
||||
if (test_bit(CACHE_VALID, &key->h.flags) &&
|
||||
!test_bit(CACHE_NEGATIVE, &key->h.flags))
|
||||
path_put(&key->ek_path);
|
||||
auth_domain_put(key->ek_client);
|
||||
kfree_rcu(key, ek_rcu);
|
||||
kfree(key);
|
||||
}
|
||||
|
||||
static void expkey_put(struct kref *ref)
|
||||
{
|
||||
struct svc_expkey *key = container_of(ref, struct svc_expkey, h.ref);
|
||||
|
||||
INIT_RCU_WORK(&key->ek_rcu_work, expkey_put_work);
|
||||
queue_rcu_work(system_wq, &key->ek_rcu_work);
|
||||
}
|
||||
|
||||
static int expkey_upcall(struct cache_detail *cd, struct cache_head *h)
|
||||
@@ -355,16 +364,26 @@ static void export_stats_destroy(struct export_stats *stats)
|
||||
EXP_STATS_COUNTERS_NUM);
|
||||
}
|
||||
|
||||
static void svc_export_put(struct kref *ref)
|
||||
static void svc_export_put_work(struct work_struct *work)
|
||||
{
|
||||
struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
|
||||
struct svc_export *exp =
|
||||
container_of(to_rcu_work(work), struct svc_export, ex_rcu_work);
|
||||
|
||||
path_put(&exp->ex_path);
|
||||
auth_domain_put(exp->ex_client);
|
||||
nfsd4_fslocs_free(&exp->ex_fslocs);
|
||||
export_stats_destroy(exp->ex_stats);
|
||||
kfree(exp->ex_stats);
|
||||
kfree(exp->ex_uuid);
|
||||
kfree_rcu(exp, ex_rcu);
|
||||
kfree(exp);
|
||||
}
|
||||
|
||||
static void svc_export_put(struct kref *ref)
|
||||
{
|
||||
struct svc_export *exp = container_of(ref, struct svc_export, h.ref);
|
||||
|
||||
INIT_RCU_WORK(&exp->ex_rcu_work, svc_export_put_work);
|
||||
queue_rcu_work(system_wq, &exp->ex_rcu_work);
|
||||
}
|
||||
|
||||
static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h)
|
||||
@@ -1078,12 +1097,14 @@ static struct svc_export *exp_find(struct cache_detail *cd,
|
||||
* check_nfsd_access - check if access to export is allowed.
|
||||
* @exp: svc_export that is being accessed.
|
||||
* @rqstp: svc_rqst attempting to access @exp (will be NULL for LOCALIO).
|
||||
* @may_bypass_gss: reduce strictness of authorization check
|
||||
*
|
||||
* Return values:
|
||||
* %nfs_ok if access is granted, or
|
||||
* %nfserr_wrongsec if access is denied
|
||||
*/
|
||||
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp)
|
||||
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
|
||||
bool may_bypass_gss)
|
||||
{
|
||||
struct exp_flavor_info *f, *end = exp->ex_flavors + exp->ex_nflavors;
|
||||
struct svc_xprt *xprt;
|
||||
@@ -1140,6 +1161,23 @@ ok:
|
||||
if (nfsd4_spo_must_allow(rqstp))
|
||||
return nfs_ok;
|
||||
|
||||
/* Some calls may be processed without authentication
|
||||
* on GSS exports. For example NFS2/3 calls on root
|
||||
* directory, see section 2.3.2 of rfc 2623.
|
||||
* For "may_bypass_gss" check that export has really
|
||||
* enabled some flavor with authentication (GSS or any
|
||||
* other) and also check that the used auth flavor is
|
||||
* without authentication (none or sys).
|
||||
*/
|
||||
if (may_bypass_gss && (
|
||||
rqstp->rq_cred.cr_flavor == RPC_AUTH_NULL ||
|
||||
rqstp->rq_cred.cr_flavor == RPC_AUTH_UNIX)) {
|
||||
for (f = exp->ex_flavors; f < end; f++) {
|
||||
if (f->pseudoflavor >= RPC_AUTH_DES)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
denied:
|
||||
return nfserr_wrongsec;
|
||||
}
|
||||
@@ -1406,9 +1444,12 @@ static int e_show(struct seq_file *m, void *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
exp_get(exp);
|
||||
if (!cache_get_rcu(&exp->h))
|
||||
return 0;
|
||||
|
||||
if (cache_check(cd, &exp->h, NULL))
|
||||
return 0;
|
||||
|
||||
exp_put(exp);
|
||||
return svc_export_show(m, cd, cp);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ struct svc_export {
|
||||
u32 ex_layout_types;
|
||||
struct nfsd4_deviceid_map *ex_devid_map;
|
||||
struct cache_detail *cd;
|
||||
struct rcu_head ex_rcu;
|
||||
struct rcu_work ex_rcu_work;
|
||||
unsigned long ex_xprtsec_modes;
|
||||
struct export_stats *ex_stats;
|
||||
};
|
||||
@@ -92,7 +92,7 @@ struct svc_expkey {
|
||||
u32 ek_fsid[6];
|
||||
|
||||
struct path ek_path;
|
||||
struct rcu_head ek_rcu;
|
||||
struct rcu_work ek_rcu_work;
|
||||
};
|
||||
|
||||
#define EX_ISSYNC(exp) (!((exp)->ex_flags & NFSEXP_ASYNC))
|
||||
@@ -101,7 +101,8 @@ struct svc_expkey {
|
||||
|
||||
struct svc_cred;
|
||||
int nfsexp_flags(struct svc_cred *cred, struct svc_export *exp);
|
||||
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp);
|
||||
__be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp,
|
||||
bool may_bypass_gss);
|
||||
|
||||
/*
|
||||
* Function declarations
|
||||
|
||||
@@ -391,19 +391,19 @@ nfsd_file_put(struct nfsd_file *nf)
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd_file_put_local - put the reference to nfsd_file and local nfsd_serv
|
||||
* @nf: nfsd_file of which to put the references
|
||||
* nfsd_file_put_local - put nfsd_file reference and arm nfsd_serv_put in caller
|
||||
* @nf: nfsd_file of which to put the reference
|
||||
*
|
||||
* First put the reference of the nfsd_file and then put the
|
||||
* reference to the associated nn->nfsd_serv.
|
||||
* First save the associated net to return to caller, then put
|
||||
* the reference of the nfsd_file.
|
||||
*/
|
||||
void
|
||||
nfsd_file_put_local(struct nfsd_file *nf) __must_hold(rcu)
|
||||
struct net *
|
||||
nfsd_file_put_local(struct nfsd_file *nf)
|
||||
{
|
||||
struct net *net = nf->nf_net;
|
||||
|
||||
nfsd_file_put(nf);
|
||||
nfsd_serv_put(net);
|
||||
return net;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1047,7 +1047,7 @@ retry:
|
||||
* the last one however, since we should hold another.
|
||||
*/
|
||||
if (nfsd_file_lru_remove(nf))
|
||||
WARN_ON_ONCE(refcount_dec_and_test(&nf->nf_ref));
|
||||
refcount_dec(&nf->nf_ref);
|
||||
goto wait_for_construction;
|
||||
}
|
||||
|
||||
@@ -1120,8 +1120,7 @@ open_file:
|
||||
status = nfs_ok;
|
||||
trace_nfsd_file_opened(nf, status);
|
||||
} else {
|
||||
ret = nfsd_open_verified(rqstp, fhp, may_flags,
|
||||
&nf->nf_file);
|
||||
ret = nfsd_open_verified(fhp, may_flags, &nf->nf_file);
|
||||
if (ret == -EOPENSTALE && stale_retry) {
|
||||
stale_retry = false;
|
||||
nfsd_file_unhash(nf);
|
||||
|
||||
@@ -55,7 +55,7 @@ void nfsd_file_cache_shutdown(void);
|
||||
int nfsd_file_cache_start_net(struct net *net);
|
||||
void nfsd_file_cache_shutdown_net(struct net *net);
|
||||
void nfsd_file_put(struct nfsd_file *nf);
|
||||
void nfsd_file_put_local(struct nfsd_file *nf);
|
||||
struct net *nfsd_file_put_local(struct nfsd_file *nf);
|
||||
struct nfsd_file *nfsd_file_get(struct nfsd_file *nf);
|
||||
struct file *nfsd_file_file(struct nfsd_file *nf);
|
||||
void nfsd_file_close_inode_sync(struct inode *inode);
|
||||
|
||||
@@ -38,11 +38,20 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp,
|
||||
memcpy(&fh.fh_handle.fh_raw, f->data, f->size);
|
||||
fh.fh_export = NULL;
|
||||
|
||||
/*
|
||||
* Allow BYPASS_GSS as some client implementations use AUTH_SYS
|
||||
* for NLM even when GSS is used for NFS.
|
||||
* Allow OWNER_OVERRIDE as permission might have been changed
|
||||
* after the file was opened.
|
||||
* Pass MAY_NLM so that authentication can be completely bypassed
|
||||
* if NFSEXP_NOAUTHNLM is set. Some older clients use AUTH_NULL
|
||||
* for NLM requests.
|
||||
*/
|
||||
access = (mode == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ;
|
||||
access |= NFSD_MAY_LOCK;
|
||||
access |= NFSD_MAY_NLM | NFSD_MAY_OWNER_OVERRIDE | NFSD_MAY_BYPASS_GSS;
|
||||
nfserr = nfsd_open(rqstp, &fh, S_IFREG, access, filp);
|
||||
fh_put(&fh);
|
||||
/* We return nlm error codes as nlm doesn't know
|
||||
/* We return nlm error codes as nlm doesn't know
|
||||
* about nfsd, but nfsd does know about nlm..
|
||||
*/
|
||||
switch (nfserr) {
|
||||
|
||||
@@ -198,8 +198,6 @@ summarize_posix_acl(struct posix_acl *acl, struct posix_acl_summary *pas)
|
||||
memset(pas, 0, sizeof(*pas));
|
||||
pas->mask = 07;
|
||||
|
||||
pe = acl->a_entries + acl->a_count;
|
||||
|
||||
FOREACH_ACL_ENTRY(pa, acl, pe) {
|
||||
switch (pa->e_tag) {
|
||||
case ACL_USER_OBJ:
|
||||
|
||||
@@ -287,17 +287,17 @@ static int decode_cb_compound4res(struct xdr_stream *xdr,
|
||||
u32 length;
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4 + 4);
|
||||
p = xdr_inline_decode(xdr, XDR_UNIT);
|
||||
if (unlikely(p == NULL))
|
||||
goto out_overflow;
|
||||
hdr->status = be32_to_cpup(p++);
|
||||
hdr->status = be32_to_cpup(p);
|
||||
/* Ignore the tag */
|
||||
length = be32_to_cpup(p++);
|
||||
p = xdr_inline_decode(xdr, length + 4);
|
||||
if (unlikely(p == NULL))
|
||||
if (xdr_stream_decode_u32(xdr, &length) < 0)
|
||||
goto out_overflow;
|
||||
if (xdr_inline_decode(xdr, length) == NULL)
|
||||
goto out_overflow;
|
||||
if (xdr_stream_decode_u32(xdr, &hdr->nops) < 0)
|
||||
goto out_overflow;
|
||||
p += XDR_QUADLEN(length);
|
||||
hdr->nops = be32_to_cpup(p);
|
||||
return 0;
|
||||
out_overflow:
|
||||
return -EIO;
|
||||
@@ -364,13 +364,29 @@ encode_cb_getattr4args(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr,
|
||||
struct nfs4_delegation *dp =
|
||||
container_of(fattr, struct nfs4_delegation, dl_cb_fattr);
|
||||
struct knfsd_fh *fh = &dp->dl_stid.sc_file->fi_fhandle;
|
||||
u32 bmap[1];
|
||||
|
||||
bmap[0] = FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE;
|
||||
|
||||
encode_nfs_cb_opnum4(xdr, OP_CB_GETATTR);
|
||||
encode_nfs_fh4(xdr, fh);
|
||||
encode_bitmap4(xdr, fattr->ncf_cb_bmap, ARRAY_SIZE(fattr->ncf_cb_bmap));
|
||||
encode_bitmap4(xdr, bmap, ARRAY_SIZE(bmap));
|
||||
hdr->nops++;
|
||||
}
|
||||
|
||||
static u32 highest_slotid(struct nfsd4_session *ses)
|
||||
{
|
||||
u32 idx;
|
||||
|
||||
spin_lock(&ses->se_lock);
|
||||
idx = fls(~ses->se_cb_slot_avail);
|
||||
if (idx > 0)
|
||||
--idx;
|
||||
idx = max(idx, ses->se_cb_highest_slot);
|
||||
spin_unlock(&ses->se_lock);
|
||||
return idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* CB_SEQUENCE4args
|
||||
*
|
||||
@@ -397,15 +413,40 @@ static void encode_cb_sequence4args(struct xdr_stream *xdr,
|
||||
encode_sessionid4(xdr, session);
|
||||
|
||||
p = xdr_reserve_space(xdr, 4 + 4 + 4 + 4 + 4);
|
||||
*p++ = cpu_to_be32(session->se_cb_seq_nr); /* csa_sequenceid */
|
||||
*p++ = xdr_zero; /* csa_slotid */
|
||||
*p++ = xdr_zero; /* csa_highest_slotid */
|
||||
*p++ = cpu_to_be32(session->se_cb_seq_nr[cb->cb_held_slot]); /* csa_sequenceid */
|
||||
*p++ = cpu_to_be32(cb->cb_held_slot); /* csa_slotid */
|
||||
*p++ = cpu_to_be32(highest_slotid(session)); /* csa_highest_slotid */
|
||||
*p++ = xdr_zero; /* csa_cachethis */
|
||||
xdr_encode_empty_array(p); /* csa_referring_call_lists */
|
||||
|
||||
hdr->nops++;
|
||||
}
|
||||
|
||||
static void update_cb_slot_table(struct nfsd4_session *ses, u32 target)
|
||||
{
|
||||
/* No need to do anything if nothing changed */
|
||||
if (likely(target == READ_ONCE(ses->se_cb_highest_slot)))
|
||||
return;
|
||||
|
||||
spin_lock(&ses->se_lock);
|
||||
if (target > ses->se_cb_highest_slot) {
|
||||
int i;
|
||||
|
||||
target = min(target, NFSD_BC_SLOT_TABLE_SIZE - 1);
|
||||
|
||||
/*
|
||||
* Growing the slot table. Reset any new sequences to 1.
|
||||
*
|
||||
* NB: There is some debate about whether the RFC requires this,
|
||||
* but the Linux client expects it.
|
||||
*/
|
||||
for (i = ses->se_cb_highest_slot + 1; i <= target; ++i)
|
||||
ses->se_cb_seq_nr[i] = 1;
|
||||
}
|
||||
ses->se_cb_highest_slot = target;
|
||||
spin_unlock(&ses->se_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* CB_SEQUENCE4resok
|
||||
*
|
||||
@@ -433,7 +474,7 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
|
||||
struct nfsd4_session *session = cb->cb_clp->cl_cb_session;
|
||||
int status = -ESERVERFAULT;
|
||||
__be32 *p;
|
||||
u32 dummy;
|
||||
u32 seqid, slotid, target;
|
||||
|
||||
/*
|
||||
* If the server returns different values for sessionID, slotID or
|
||||
@@ -449,21 +490,22 @@ static int decode_cb_sequence4resok(struct xdr_stream *xdr,
|
||||
}
|
||||
p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
|
||||
|
||||
dummy = be32_to_cpup(p++);
|
||||
if (dummy != session->se_cb_seq_nr) {
|
||||
seqid = be32_to_cpup(p++);
|
||||
if (seqid != session->se_cb_seq_nr[cb->cb_held_slot]) {
|
||||
dprintk("NFS: %s Invalid sequence number\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dummy = be32_to_cpup(p++);
|
||||
if (dummy != 0) {
|
||||
slotid = be32_to_cpup(p++);
|
||||
if (slotid != cb->cb_held_slot) {
|
||||
dprintk("NFS: %s Invalid slotid\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: process highest slotid and target highest slotid
|
||||
*/
|
||||
p++; // ignore current highest slot value
|
||||
|
||||
target = be32_to_cpup(p++);
|
||||
update_cb_slot_table(session, target);
|
||||
status = 0;
|
||||
out:
|
||||
cb->cb_seq_status = status;
|
||||
@@ -1164,6 +1206,22 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
|
||||
spin_unlock(&clp->cl_lock);
|
||||
}
|
||||
|
||||
static int grab_slot(struct nfsd4_session *ses)
|
||||
{
|
||||
int idx;
|
||||
|
||||
spin_lock(&ses->se_lock);
|
||||
idx = ffs(ses->se_cb_slot_avail) - 1;
|
||||
if (idx < 0 || idx > ses->se_cb_highest_slot) {
|
||||
spin_unlock(&ses->se_lock);
|
||||
return -1;
|
||||
}
|
||||
/* clear the bit for the slot */
|
||||
ses->se_cb_slot_avail &= ~BIT(idx);
|
||||
spin_unlock(&ses->se_lock);
|
||||
return idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* There's currently a single callback channel slot.
|
||||
* If the slot is available, then mark it busy. Otherwise, set the
|
||||
@@ -1172,28 +1230,32 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
|
||||
static bool nfsd41_cb_get_slot(struct nfsd4_callback *cb, struct rpc_task *task)
|
||||
{
|
||||
struct nfs4_client *clp = cb->cb_clp;
|
||||
struct nfsd4_session *ses = clp->cl_cb_session;
|
||||
|
||||
if (!cb->cb_holds_slot &&
|
||||
test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
|
||||
if (cb->cb_held_slot >= 0)
|
||||
return true;
|
||||
cb->cb_held_slot = grab_slot(ses);
|
||||
if (cb->cb_held_slot < 0) {
|
||||
rpc_sleep_on(&clp->cl_cb_waitq, task, NULL);
|
||||
/* Race breaker */
|
||||
if (test_and_set_bit(0, &clp->cl_cb_slot_busy) != 0) {
|
||||
dprintk("%s slot is busy\n", __func__);
|
||||
cb->cb_held_slot = grab_slot(ses);
|
||||
if (cb->cb_held_slot < 0)
|
||||
return false;
|
||||
}
|
||||
rpc_wake_up_queued_task(&clp->cl_cb_waitq, task);
|
||||
}
|
||||
cb->cb_holds_slot = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void nfsd41_cb_release_slot(struct nfsd4_callback *cb)
|
||||
{
|
||||
struct nfs4_client *clp = cb->cb_clp;
|
||||
struct nfsd4_session *ses = clp->cl_cb_session;
|
||||
|
||||
if (cb->cb_holds_slot) {
|
||||
cb->cb_holds_slot = false;
|
||||
clear_bit(0, &clp->cl_cb_slot_busy);
|
||||
if (cb->cb_held_slot >= 0) {
|
||||
spin_lock(&ses->se_lock);
|
||||
ses->se_cb_slot_avail |= BIT(cb->cb_held_slot);
|
||||
spin_unlock(&ses->se_lock);
|
||||
cb->cb_held_slot = -1;
|
||||
rpc_wake_up_next(&clp->cl_cb_waitq);
|
||||
}
|
||||
}
|
||||
@@ -1210,8 +1272,8 @@ static void nfsd41_destroy_cb(struct nfsd4_callback *cb)
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: cb_sequence should support referring call lists, cachethis, multiple
|
||||
* slots, and mark callback channel down on communication errors.
|
||||
* TODO: cb_sequence should support referring call lists, cachethis,
|
||||
* and mark callback channel down on communication errors.
|
||||
*/
|
||||
static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
|
||||
{
|
||||
@@ -1253,7 +1315,7 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!cb->cb_holds_slot)
|
||||
if (cb->cb_held_slot < 0)
|
||||
goto need_restart;
|
||||
|
||||
/* This is the operation status code for CB_SEQUENCE */
|
||||
@@ -1267,10 +1329,10 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
|
||||
* If CB_SEQUENCE returns an error, then the state of the slot
|
||||
* (sequence ID, cached reply) MUST NOT change.
|
||||
*/
|
||||
++session->se_cb_seq_nr;
|
||||
++session->se_cb_seq_nr[cb->cb_held_slot];
|
||||
break;
|
||||
case -ESERVERFAULT:
|
||||
++session->se_cb_seq_nr;
|
||||
++session->se_cb_seq_nr[cb->cb_held_slot];
|
||||
nfsd4_mark_cb_fault(cb->cb_clp);
|
||||
ret = false;
|
||||
break;
|
||||
@@ -1296,17 +1358,16 @@ static bool nfsd4_cb_sequence_done(struct rpc_task *task, struct nfsd4_callback
|
||||
case -NFS4ERR_BADSLOT:
|
||||
goto retry_nowait;
|
||||
case -NFS4ERR_SEQ_MISORDERED:
|
||||
if (session->se_cb_seq_nr != 1) {
|
||||
session->se_cb_seq_nr = 1;
|
||||
if (session->se_cb_seq_nr[cb->cb_held_slot] != 1) {
|
||||
session->se_cb_seq_nr[cb->cb_held_slot] = 1;
|
||||
goto retry_nowait;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
nfsd4_mark_cb_fault(cb->cb_clp);
|
||||
}
|
||||
nfsd41_cb_release_slot(cb);
|
||||
|
||||
trace_nfsd_cb_free_slot(task, cb);
|
||||
nfsd41_cb_release_slot(cb);
|
||||
|
||||
if (RPC_SIGNALLED(task))
|
||||
goto need_restart;
|
||||
@@ -1461,6 +1522,8 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
|
||||
ses = c->cn_session;
|
||||
}
|
||||
spin_unlock(&clp->cl_lock);
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
err = setup_callback_client(clp, &conn, ses);
|
||||
if (err) {
|
||||
@@ -1524,7 +1587,7 @@ void nfsd4_init_cb(struct nfsd4_callback *cb, struct nfs4_client *clp,
|
||||
INIT_WORK(&cb->cb_work, nfsd4_run_cb_work);
|
||||
cb->cb_status = 0;
|
||||
cb->cb_need_restart = false;
|
||||
cb->cb_holds_slot = false;
|
||||
cb->cb_held_slot = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -57,6 +57,8 @@ module_param(inter_copy_offload_enable, bool, 0644);
|
||||
MODULE_PARM_DESC(inter_copy_offload_enable,
|
||||
"Enable inter server to server copy offload. Default: false");
|
||||
|
||||
static void cleanup_async_copy(struct nfsd4_copy *copy);
|
||||
|
||||
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
|
||||
static int nfsd4_ssc_umount_timeout = 900000; /* default to 15 mins */
|
||||
module_param(nfsd4_ssc_umount_timeout, int, 0644);
|
||||
@@ -1276,6 +1278,71 @@ out:
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd4_has_active_async_copies - Check for ongoing copy operations
|
||||
* @clp: Client to be checked
|
||||
*
|
||||
* NFSD maintains state for async COPY operations after they complete,
|
||||
* and this state remains in the nfs4_client's async_copies list.
|
||||
* Ongoing copies should block the destruction of the nfs4_client, but
|
||||
* completed copies should not.
|
||||
*
|
||||
* Return values:
|
||||
* %true: At least one active async COPY is ongoing
|
||||
* %false: No active async COPY operations were found
|
||||
*/
|
||||
bool nfsd4_has_active_async_copies(struct nfs4_client *clp)
|
||||
{
|
||||
struct nfsd4_copy *copy;
|
||||
bool result = false;
|
||||
|
||||
spin_lock(&clp->async_lock);
|
||||
list_for_each_entry(copy, &clp->async_copies, copies) {
|
||||
if (!test_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags) &&
|
||||
!test_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&clp->async_lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfsd4_async_copy_reaper - Purge completed copies
|
||||
* @nn: Network namespace with possible active copy information
|
||||
*/
|
||||
void nfsd4_async_copy_reaper(struct nfsd_net *nn)
|
||||
{
|
||||
struct nfs4_client *clp;
|
||||
struct nfsd4_copy *copy;
|
||||
LIST_HEAD(reaplist);
|
||||
|
||||
spin_lock(&nn->client_lock);
|
||||
list_for_each_entry(clp, &nn->client_lru, cl_lru) {
|
||||
struct list_head *pos, *next;
|
||||
|
||||
spin_lock(&clp->async_lock);
|
||||
list_for_each_safe(pos, next, &clp->async_copies) {
|
||||
copy = list_entry(pos, struct nfsd4_copy, copies);
|
||||
if (test_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags)) {
|
||||
if (--copy->cp_ttl) {
|
||||
list_del_init(©->copies);
|
||||
list_add(©->copies, &reaplist);
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&clp->async_lock);
|
||||
}
|
||||
spin_unlock(&nn->client_lock);
|
||||
|
||||
while (!list_empty(&reaplist)) {
|
||||
copy = list_first_entry(&reaplist, struct nfsd4_copy, copies);
|
||||
list_del_init(©->copies);
|
||||
cleanup_async_copy(copy);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfs4_put_copy(struct nfsd4_copy *copy)
|
||||
{
|
||||
if (!refcount_dec_and_test(©->refcount))
|
||||
@@ -1287,12 +1354,13 @@ static void nfs4_put_copy(struct nfsd4_copy *copy)
|
||||
|
||||
static void nfsd4_stop_copy(struct nfsd4_copy *copy)
|
||||
{
|
||||
trace_nfsd_copy_async_cancel(copy);
|
||||
if (!test_and_set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags))
|
||||
kthread_stop(copy->copy_task);
|
||||
nfs4_put_copy(copy);
|
||||
}
|
||||
|
||||
static struct nfsd4_copy *nfsd4_get_copy(struct nfs4_client *clp)
|
||||
static struct nfsd4_copy *nfsd4_unhash_copy(struct nfs4_client *clp)
|
||||
{
|
||||
struct nfsd4_copy *copy = NULL;
|
||||
|
||||
@@ -1301,6 +1369,9 @@ static struct nfsd4_copy *nfsd4_get_copy(struct nfs4_client *clp)
|
||||
copy = list_first_entry(&clp->async_copies, struct nfsd4_copy,
|
||||
copies);
|
||||
refcount_inc(©->refcount);
|
||||
copy->cp_clp = NULL;
|
||||
if (!list_empty(©->copies))
|
||||
list_del_init(©->copies);
|
||||
}
|
||||
spin_unlock(&clp->async_lock);
|
||||
return copy;
|
||||
@@ -1310,7 +1381,7 @@ void nfsd4_shutdown_copy(struct nfs4_client *clp)
|
||||
{
|
||||
struct nfsd4_copy *copy;
|
||||
|
||||
while ((copy = nfsd4_get_copy(clp)) != NULL)
|
||||
while ((copy = nfsd4_unhash_copy(clp)) != NULL)
|
||||
nfsd4_stop_copy(copy);
|
||||
}
|
||||
#ifdef CONFIG_NFSD_V4_2_INTER_SSC
|
||||
@@ -1598,8 +1669,10 @@ static void nfsd4_cb_offload_release(struct nfsd4_callback *cb)
|
||||
{
|
||||
struct nfsd4_cb_offload *cbo =
|
||||
container_of(cb, struct nfsd4_cb_offload, co_cb);
|
||||
struct nfsd4_copy *copy =
|
||||
container_of(cbo, struct nfsd4_copy, cp_cb_offload);
|
||||
|
||||
kfree(cbo);
|
||||
set_bit(NFSD4_COPY_F_OFFLOAD_DONE, ©->cp_flags);
|
||||
}
|
||||
|
||||
static int nfsd4_cb_offload_done(struct nfsd4_callback *cb,
|
||||
@@ -1609,6 +1682,13 @@ static int nfsd4_cb_offload_done(struct nfsd4_callback *cb,
|
||||
container_of(cb, struct nfsd4_cb_offload, co_cb);
|
||||
|
||||
trace_nfsd_cb_offload_done(&cbo->co_res.cb_stateid, task);
|
||||
switch (task->tk_status) {
|
||||
case -NFS4ERR_DELAY:
|
||||
if (cbo->co_retries--) {
|
||||
rpc_delay(task, 1 * HZ);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1732,15 +1812,12 @@ static void cleanup_async_copy(struct nfsd4_copy *copy)
|
||||
|
||||
static void nfsd4_send_cb_offload(struct nfsd4_copy *copy)
|
||||
{
|
||||
struct nfsd4_cb_offload *cbo;
|
||||
|
||||
cbo = kzalloc(sizeof(*cbo), GFP_KERNEL);
|
||||
if (!cbo)
|
||||
return;
|
||||
struct nfsd4_cb_offload *cbo = ©->cp_cb_offload;
|
||||
|
||||
memcpy(&cbo->co_res, ©->cp_res, sizeof(copy->cp_res));
|
||||
memcpy(&cbo->co_fh, ©->fh, sizeof(copy->fh));
|
||||
cbo->co_nfserr = copy->nfserr;
|
||||
cbo->co_retries = 5;
|
||||
|
||||
nfsd4_init_cb(&cbo->co_cb, copy->cp_clp, &nfsd4_cb_offload_ops,
|
||||
NFSPROC4_CLNT_CB_OFFLOAD);
|
||||
@@ -1786,10 +1863,13 @@ static int nfsd4_do_async_copy(void *data)
|
||||
}
|
||||
|
||||
do_callback:
|
||||
/* The kthread exits forthwith. Ensure that a subsequent
|
||||
* OFFLOAD_CANCEL won't try to kill it again. */
|
||||
set_bit(NFSD4_COPY_F_STOPPED, ©->cp_flags);
|
||||
|
||||
set_bit(NFSD4_COPY_F_COMPLETED, ©->cp_flags);
|
||||
trace_nfsd_copy_async_done(copy);
|
||||
nfsd4_send_cb_offload(copy);
|
||||
cleanup_async_copy(copy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1843,6 +1923,7 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
async_copy->cp_nn = nn;
|
||||
INIT_LIST_HEAD(&async_copy->copies);
|
||||
refcount_set(&async_copy->refcount, 1);
|
||||
async_copy->cp_ttl = NFSD_COPY_INITIAL_TTL;
|
||||
/* Arbitrary cap on number of pending async copy operations */
|
||||
if (atomic_inc_return(&nn->pending_async_copies) >
|
||||
(int)rqstp->rq_pool->sp_nrthreads)
|
||||
@@ -2780,6 +2861,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
|
||||
if (op->opdesc->op_get_currentstateid)
|
||||
op->opdesc->op_get_currentstateid(cstate, &op->u);
|
||||
op->status = op->opdesc->op_func(rqstp, cstate, &op->u);
|
||||
trace_nfsd_compound_op_err(rqstp, op->opnum, op->status);
|
||||
|
||||
/* Only from SEQUENCE */
|
||||
if (cstate->status == nfserr_replay_cache) {
|
||||
@@ -2796,7 +2878,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
|
||||
|
||||
if (current_fh->fh_export &&
|
||||
need_wrongsec_check(rqstp))
|
||||
op->status = check_nfsd_access(current_fh->fh_export, rqstp);
|
||||
op->status = check_nfsd_access(current_fh->fh_export, rqstp, false);
|
||||
}
|
||||
encode_op:
|
||||
if (op->status == nfserr_replay_me) {
|
||||
@@ -3452,6 +3534,7 @@ static const struct nfsd4_operation nfsd4_ops[] = {
|
||||
/* NFSv4.1 operations */
|
||||
[OP_EXCHANGE_ID] = {
|
||||
.op_func = nfsd4_exchange_id,
|
||||
.op_release = nfsd4_exchange_id_release,
|
||||
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP
|
||||
| OP_MODIFIES_SOMETHING,
|
||||
.op_name = "OP_EXCHANGE_ID",
|
||||
|
||||
@@ -659,7 +659,8 @@ nfs4_reset_recoverydir(char *recdir)
|
||||
return status;
|
||||
status = -ENOTDIR;
|
||||
if (d_is_dir(path.dentry)) {
|
||||
strcpy(user_recovery_dirname, recdir);
|
||||
strscpy(user_recovery_dirname, recdir,
|
||||
sizeof(user_recovery_dirname));
|
||||
status = 0;
|
||||
}
|
||||
path_put(&path);
|
||||
|
||||
@@ -149,14 +149,14 @@ void nfsd4_destroy_laundry_wq(void)
|
||||
|
||||
static bool is_session_dead(struct nfsd4_session *ses)
|
||||
{
|
||||
return ses->se_flags & NFS4_SESSION_DEAD;
|
||||
return ses->se_dead;
|
||||
}
|
||||
|
||||
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;
|
||||
ses->se_dead = true;
|
||||
return nfs_ok;
|
||||
}
|
||||
|
||||
@@ -572,13 +572,6 @@ opaque_hashval(const void *ptr, int nbytes)
|
||||
return x;
|
||||
}
|
||||
|
||||
static void nfsd4_free_file_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct nfs4_file *fp = container_of(rcu, struct nfs4_file, fi_rcu);
|
||||
|
||||
kmem_cache_free(file_slab, fp);
|
||||
}
|
||||
|
||||
void
|
||||
put_nfs4_file(struct nfs4_file *fi)
|
||||
{
|
||||
@@ -586,7 +579,7 @@ put_nfs4_file(struct nfs4_file *fi)
|
||||
nfsd4_file_hash_remove(fi);
|
||||
WARN_ON_ONCE(!list_empty(&fi->fi_clnt_odstate));
|
||||
WARN_ON_ONCE(!list_empty(&fi->fi_delegations));
|
||||
call_rcu(&fi->fi_rcu, nfsd4_free_file_rcu);
|
||||
kfree_rcu(fi, fi_rcu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1184,7 +1177,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_file *fp,
|
||||
nfsd4_init_cb(&dp->dl_cb_fattr.ncf_getattr, dp->dl_stid.sc_client,
|
||||
&nfsd4_cb_getattr_ops, NFSPROC4_CLNT_CB_GETATTR);
|
||||
dp->dl_cb_fattr.ncf_file_modified = false;
|
||||
dp->dl_cb_fattr.ncf_cb_bmap[0] = FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE;
|
||||
get_nfs4_file(fp);
|
||||
dp->dl_stid.sc_file = fp;
|
||||
return dp;
|
||||
@@ -1660,6 +1652,14 @@ static void release_open_stateid(struct nfs4_ol_stateid *stp)
|
||||
free_ol_stateid_reaplist(&reaplist);
|
||||
}
|
||||
|
||||
static bool nfs4_openowner_unhashed(struct nfs4_openowner *oo)
|
||||
{
|
||||
lockdep_assert_held(&oo->oo_owner.so_client->cl_lock);
|
||||
|
||||
return list_empty(&oo->oo_owner.so_strhash) &&
|
||||
list_empty(&oo->oo_perclient);
|
||||
}
|
||||
|
||||
static void unhash_openowner_locked(struct nfs4_openowner *oo)
|
||||
{
|
||||
struct nfs4_client *clp = oo->oo_owner.so_client;
|
||||
@@ -2010,8 +2010,10 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fattrs,
|
||||
}
|
||||
|
||||
memcpy(&new->se_fchannel, fattrs, sizeof(struct nfsd4_channel_attrs));
|
||||
memcpy(&new->se_bchannel, battrs, sizeof(struct nfsd4_channel_attrs));
|
||||
|
||||
new->se_cb_slot_avail = ~0U;
|
||||
new->se_cb_highest_slot = min(battrs->maxreqs - 1,
|
||||
NFSD_BC_SLOT_TABLE_SIZE - 1);
|
||||
spin_lock_init(&new->se_lock);
|
||||
return new;
|
||||
out_free:
|
||||
while (i--)
|
||||
@@ -2142,11 +2144,14 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru
|
||||
|
||||
INIT_LIST_HEAD(&new->se_conns);
|
||||
|
||||
new->se_cb_seq_nr = 1;
|
||||
new->se_flags = cses->flags;
|
||||
atomic_set(&new->se_ref, 0);
|
||||
new->se_dead = false;
|
||||
new->se_cb_prog = cses->callback_prog;
|
||||
new->se_cb_sec = cses->cb_sec;
|
||||
atomic_set(&new->se_ref, 0);
|
||||
|
||||
for (idx = 0; idx < NFSD_BC_SLOT_TABLE_SIZE; ++idx)
|
||||
new->se_cb_seq_nr[idx] = 1;
|
||||
|
||||
idx = hash_sessionid(&new->se_sessionid);
|
||||
list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]);
|
||||
spin_lock(&clp->cl_lock);
|
||||
@@ -2239,21 +2244,16 @@ STALE_CLIENTID(clientid_t *clid, struct nfsd_net *nn)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX Should we use a slab cache ?
|
||||
* This type of memory management is somewhat inefficient, but we use it
|
||||
* anyway since SETCLIENTID is not a common operation.
|
||||
*/
|
||||
static struct nfs4_client *alloc_client(struct xdr_netobj name,
|
||||
struct nfsd_net *nn)
|
||||
{
|
||||
struct nfs4_client *clp;
|
||||
int i;
|
||||
|
||||
if (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients) {
|
||||
if (atomic_read(&nn->nfs4_client_count) >= nn->nfs4_max_clients &&
|
||||
atomic_read(&nn->nfsd_courtesy_clients) > 0)
|
||||
mod_delayed_work(laundry_wq, &nn->laundromat_work, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
clp = kmem_cache_zalloc(client_slab, GFP_KERNEL);
|
||||
if (clp == NULL)
|
||||
return NULL;
|
||||
@@ -3160,7 +3160,6 @@ static struct nfs4_client *create_client(struct xdr_netobj name,
|
||||
kref_init(&clp->cl_nfsdfs.cl_ref);
|
||||
nfsd4_init_cb(&clp->cl_cb_null, clp, NULL, NFSPROC4_CLNT_CB_NULL);
|
||||
clp->cl_time = ktime_get_boottime_seconds();
|
||||
clear_bit(0, &clp->cl_cb_slot_busy);
|
||||
copy_verf(clp, verf);
|
||||
memcpy(&clp->cl_addr, sa, sizeof(struct sockaddr_storage));
|
||||
clp->cl_cb_session = NULL;
|
||||
@@ -3487,7 +3486,7 @@ static bool client_has_state(struct nfs4_client *clp)
|
||||
#endif
|
||||
|| !list_empty(&clp->cl_delegations)
|
||||
|| !list_empty(&clp->cl_sessions)
|
||||
|| !list_empty(&clp->async_copies);
|
||||
|| nfsd4_has_active_async_copies(clp);
|
||||
}
|
||||
|
||||
static __be32 copy_impl_id(struct nfs4_client *clp,
|
||||
@@ -3525,6 +3524,12 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
__func__, rqstp, exid, exid->clname.len, exid->clname.data,
|
||||
addr_str, exid->flags, exid->spa_how);
|
||||
|
||||
exid->server_impl_name = kasprintf(GFP_KERNEL, "%s %s %s %s",
|
||||
utsname()->sysname, utsname()->release,
|
||||
utsname()->version, utsname()->machine);
|
||||
if (!exid->server_impl_name)
|
||||
return nfserr_jukebox;
|
||||
|
||||
if (exid->flags & ~EXCHGID4_FLAG_MASK_A)
|
||||
return nfserr_inval;
|
||||
|
||||
@@ -3662,6 +3667,23 @@ out_copy:
|
||||
exid->seqid = conf->cl_cs_slot.sl_seqid + 1;
|
||||
nfsd4_set_ex_flags(conf, exid);
|
||||
|
||||
exid->nii_domain.len = sizeof("kernel.org") - 1;
|
||||
exid->nii_domain.data = "kernel.org";
|
||||
|
||||
/*
|
||||
* Note that RFC 8881 places no length limit on
|
||||
* nii_name, but this implementation permits no
|
||||
* more than NFS4_OPAQUE_LIMIT bytes.
|
||||
*/
|
||||
exid->nii_name.len = strlen(exid->server_impl_name);
|
||||
if (exid->nii_name.len > NFS4_OPAQUE_LIMIT)
|
||||
exid->nii_name.len = NFS4_OPAQUE_LIMIT;
|
||||
exid->nii_name.data = exid->server_impl_name;
|
||||
|
||||
/* just send zeros - the date is in nii_name */
|
||||
exid->nii_time.tv_sec = 0;
|
||||
exid->nii_time.tv_nsec = 0;
|
||||
|
||||
dprintk("nfsd4_exchange_id seqid %d flags %x\n",
|
||||
conf->cl_cs_slot.sl_seqid, conf->cl_exchange_flags);
|
||||
status = nfs_ok;
|
||||
@@ -3678,6 +3700,14 @@ out_nolock:
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
nfsd4_exchange_id_release(union nfsd4_op_u *u)
|
||||
{
|
||||
struct nfsd4_exchange_id *exid = &u->exchange_id;
|
||||
|
||||
kfree(exid->server_impl_name);
|
||||
}
|
||||
|
||||
static __be32 check_slot_seqid(u32 seqid, u32 slot_seqid, bool slot_inuse)
|
||||
{
|
||||
/* The slot is in use, and no response has been sent. */
|
||||
@@ -3911,6 +3941,8 @@ nfsd4_create_session(struct svc_rqst *rqstp,
|
||||
cr_ses->flags &= ~SESSION4_PERSIST;
|
||||
/* Upshifting from TCP to RDMA is not supported */
|
||||
cr_ses->flags &= ~SESSION4_RDMA;
|
||||
/* Report the correct number of backchannel slots */
|
||||
cr_ses->back_channel.maxreqs = new->se_cb_highest_slot + 1;
|
||||
|
||||
init_session(rqstp, new, conf, cr_ses);
|
||||
nfsd4_get_session_locked(new);
|
||||
@@ -3931,7 +3963,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
|
||||
return status;
|
||||
|
||||
out_expired_error:
|
||||
old = NULL;
|
||||
/*
|
||||
* Revert the slot seq_nr change so the server will process
|
||||
* the client's resend instead of returning a cached response.
|
||||
@@ -3946,8 +3977,6 @@ out_cache_error:
|
||||
out_free_conn:
|
||||
spin_unlock(&nn->client_lock);
|
||||
free_conn(conn);
|
||||
if (old)
|
||||
expire_client(old);
|
||||
out_free_session:
|
||||
__free_session(new);
|
||||
out_release_drc_mem:
|
||||
@@ -4975,6 +5004,12 @@ retry:
|
||||
spin_lock(&oo->oo_owner.so_client->cl_lock);
|
||||
spin_lock(&fp->fi_lock);
|
||||
|
||||
if (nfs4_openowner_unhashed(oo)) {
|
||||
mutex_unlock(&stp->st_mutex);
|
||||
stp = NULL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
retstp = nfsd4_find_existing_open(fp, open);
|
||||
if (retstp)
|
||||
goto out_unlock;
|
||||
@@ -5957,7 +5992,7 @@ nfs4_delegation_stat(struct nfs4_delegation *dp, struct svc_fh *currentfh,
|
||||
path.dentry = file_dentry(nf->nf_file);
|
||||
|
||||
rc = vfs_getattr(&path, stat,
|
||||
(STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE),
|
||||
(STATX_MODE | STATX_SIZE | STATX_CTIME | STATX_CHANGE_COOKIE),
|
||||
AT_STATX_SYNC_AS_STAT);
|
||||
|
||||
nfsd_file_put(nf);
|
||||
@@ -6041,8 +6076,7 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
|
||||
}
|
||||
open->op_delegate_type = NFS4_OPEN_DELEGATE_WRITE;
|
||||
dp->dl_cb_fattr.ncf_cur_fsize = stat.size;
|
||||
dp->dl_cb_fattr.ncf_initial_cinfo =
|
||||
nfsd4_change_attribute(&stat, d_inode(currentfh->fh_dentry));
|
||||
dp->dl_cb_fattr.ncf_initial_cinfo = nfsd4_change_attribute(&stat);
|
||||
trace_nfsd_deleg_write(&dp->dl_stid.sc_stateid);
|
||||
} else {
|
||||
open->op_delegate_type = NFS4_OPEN_DELEGATE_READ;
|
||||
@@ -6127,6 +6161,11 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
|
||||
|
||||
if (!stp) {
|
||||
stp = init_open_stateid(fp, open);
|
||||
if (!stp) {
|
||||
status = nfserr_jukebox;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!open->op_stp)
|
||||
new_stp = true;
|
||||
}
|
||||
@@ -6562,6 +6601,7 @@ nfs4_laundromat(struct nfsd_net *nn)
|
||||
_free_cpntf_state_locked(nn, cps);
|
||||
}
|
||||
spin_unlock(&nn->s2s_cp_lock);
|
||||
nfsd4_async_copy_reaper(nn);
|
||||
nfs4_get_client_reaplist(nn, &reaplist, <);
|
||||
nfs4_process_client_reaplist(&reaplist);
|
||||
|
||||
@@ -7943,11 +7983,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
if (check_lock_length(lock->lk_offset, lock->lk_length))
|
||||
return nfserr_inval;
|
||||
|
||||
if ((status = fh_verify(rqstp, &cstate->current_fh,
|
||||
S_IFREG, NFSD_MAY_LOCK))) {
|
||||
dprintk("NFSD: nfsd4_lock: permission denied!\n");
|
||||
status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0);
|
||||
if (status != nfs_ok)
|
||||
return status;
|
||||
}
|
||||
sb = cstate->current_fh.fh_dentry->d_sb;
|
||||
|
||||
if (lock->lk_is_new) {
|
||||
@@ -8854,8 +8892,7 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate,
|
||||
* nfsd4_deleg_getattr_conflict - Recall if GETATTR causes conflict
|
||||
* @rqstp: RPC transaction context
|
||||
* @dentry: dentry of inode to be checked for a conflict
|
||||
* @modified: return true if file was modified
|
||||
* @size: new size of file if modified is true
|
||||
* @pdp: returned WRITE delegation, if one was found
|
||||
*
|
||||
* This function is called when there is a conflict between a write
|
||||
* delegation and a change/size GETATTR from another client. The server
|
||||
@@ -8865,11 +8902,12 @@ nfsd4_get_writestateid(struct nfsd4_compound_state *cstate,
|
||||
* 18.7.4.
|
||||
*
|
||||
* Returns 0 if there is no conflict; otherwise an nfs_stat
|
||||
* code is returned.
|
||||
* code is returned. If @pdp is set to a non-NULL value, then the
|
||||
* caller must put the reference.
|
||||
*/
|
||||
__be32
|
||||
nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
|
||||
bool *modified, u64 *size)
|
||||
struct nfs4_delegation **pdp)
|
||||
{
|
||||
__be32 status;
|
||||
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
|
||||
@@ -8880,10 +8918,9 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
|
||||
struct nfs4_cb_fattr *ncf;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
|
||||
*modified = false;
|
||||
ctx = locks_inode_context(inode);
|
||||
if (!ctx)
|
||||
return 0;
|
||||
return nfs_ok;
|
||||
|
||||
#define NON_NFSD_LEASE ((void *)1)
|
||||
|
||||
@@ -8949,10 +8986,10 @@ nfsd4_deleg_getattr_conflict(struct svc_rqst *rqstp, struct dentry *dentry,
|
||||
goto out_status;
|
||||
}
|
||||
ncf->ncf_cur_fsize = ncf->ncf_cb_fsize;
|
||||
*size = ncf->ncf_cur_fsize;
|
||||
*modified = true;
|
||||
*pdp = dp;
|
||||
return nfs_ok;
|
||||
}
|
||||
status = 0;
|
||||
status = nfs_ok;
|
||||
out_status:
|
||||
nfs4_put_stid(&dp->dl_stid);
|
||||
return status;
|
||||
|
||||
@@ -2652,13 +2652,10 @@ static __be32 nfsd4_encode_components_esc(struct xdr_stream *xdr, char sep,
|
||||
|
||||
strlen = end - str;
|
||||
if (strlen) {
|
||||
p = xdr_reserve_space(xdr, strlen + 4);
|
||||
if (!p)
|
||||
if (xdr_stream_encode_opaque(xdr, str, strlen) < 0)
|
||||
return nfserr_resource;
|
||||
p = xdr_encode_opaque(p, str, strlen);
|
||||
count++;
|
||||
}
|
||||
else
|
||||
} else
|
||||
end++;
|
||||
if (found_esc)
|
||||
end = next;
|
||||
@@ -2699,7 +2696,6 @@ static __be32 nfsd4_encode_pathname4(struct xdr_stream *xdr,
|
||||
const struct path *path)
|
||||
{
|
||||
struct path cur = *path;
|
||||
__be32 *p;
|
||||
struct dentry **components = NULL;
|
||||
unsigned int ncomponents = 0;
|
||||
__be32 err = nfserr_jukebox;
|
||||
@@ -2730,24 +2726,19 @@ static __be32 nfsd4_encode_pathname4(struct xdr_stream *xdr,
|
||||
components[ncomponents++] = cur.dentry;
|
||||
cur.dentry = dget_parent(cur.dentry);
|
||||
}
|
||||
err = nfserr_resource;
|
||||
p = xdr_reserve_space(xdr, 4);
|
||||
if (!p)
|
||||
goto out_free;
|
||||
*p++ = cpu_to_be32(ncomponents);
|
||||
|
||||
err = nfserr_resource;
|
||||
if (xdr_stream_encode_u32(xdr, ncomponents) != XDR_UNIT)
|
||||
goto out_free;
|
||||
while (ncomponents) {
|
||||
struct dentry *dentry = components[ncomponents - 1];
|
||||
unsigned int len;
|
||||
|
||||
spin_lock(&dentry->d_lock);
|
||||
len = dentry->d_name.len;
|
||||
p = xdr_reserve_space(xdr, len + 4);
|
||||
if (!p) {
|
||||
if (xdr_stream_encode_opaque(xdr, dentry->d_name.name,
|
||||
dentry->d_name.len) < 0) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
goto out_free;
|
||||
}
|
||||
p = xdr_encode_opaque(p, dentry->d_name.name, len);
|
||||
dprintk("/%pd", dentry);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
dput(dentry);
|
||||
@@ -2928,7 +2919,6 @@ struct nfsd4_fattr_args {
|
||||
struct kstat stat;
|
||||
struct kstatfs statfs;
|
||||
struct nfs4_acl *acl;
|
||||
u64 size;
|
||||
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
|
||||
void *context;
|
||||
int contextlen;
|
||||
@@ -3040,14 +3030,14 @@ static __be32 nfsd4_encode_fattr4_change(struct xdr_stream *xdr,
|
||||
return nfs_ok;
|
||||
}
|
||||
|
||||
c = nfsd4_change_attribute(&args->stat, d_inode(args->dentry));
|
||||
c = nfsd4_change_attribute(&args->stat);
|
||||
return nfsd4_encode_changeid4(xdr, c);
|
||||
}
|
||||
|
||||
static __be32 nfsd4_encode_fattr4_size(struct xdr_stream *xdr,
|
||||
const struct nfsd4_fattr_args *args)
|
||||
{
|
||||
return nfsd4_encode_uint64_t(xdr, args->size);
|
||||
return nfsd4_encode_uint64_t(xdr, args->stat.size);
|
||||
}
|
||||
|
||||
static __be32 nfsd4_encode_fattr4_fsid(struct xdr_stream *xdr,
|
||||
@@ -3512,6 +3502,7 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
|
||||
int ignore_crossmnt)
|
||||
{
|
||||
DECLARE_BITMAP(attr_bitmap, ARRAY_SIZE(nfsd4_enc_fattr4_encode_ops));
|
||||
struct nfs4_delegation *dp = NULL;
|
||||
struct nfsd4_fattr_args args;
|
||||
struct svc_fh *tempfh = NULL;
|
||||
int starting_len = xdr->buf->len;
|
||||
@@ -3526,8 +3517,6 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
|
||||
.dentry = dentry,
|
||||
};
|
||||
unsigned long bit;
|
||||
bool file_modified = false;
|
||||
u64 size = 0;
|
||||
|
||||
WARN_ON_ONCE(bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1);
|
||||
WARN_ON_ONCE(!nfsd_attrs_supported(minorversion, bmval));
|
||||
@@ -3555,10 +3544,8 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
|
||||
if (status)
|
||||
goto out;
|
||||
}
|
||||
args.size = 0;
|
||||
if (attrmask[0] & (FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE)) {
|
||||
status = nfsd4_deleg_getattr_conflict(rqstp, dentry,
|
||||
&file_modified, &size);
|
||||
status = nfsd4_deleg_getattr_conflict(rqstp, dentry, &dp);
|
||||
if (status)
|
||||
goto out;
|
||||
}
|
||||
@@ -3566,12 +3553,16 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
|
||||
err = vfs_getattr(&path, &args.stat,
|
||||
STATX_BASIC_STATS | STATX_BTIME | STATX_CHANGE_COOKIE,
|
||||
AT_STATX_SYNC_AS_STAT);
|
||||
if (dp) {
|
||||
struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr;
|
||||
|
||||
if (ncf->ncf_file_modified)
|
||||
args.stat.size = ncf->ncf_cur_fsize;
|
||||
|
||||
nfs4_put_stid(&dp->dl_stid);
|
||||
}
|
||||
if (err)
|
||||
goto out_nfserr;
|
||||
if (file_modified)
|
||||
args.size = size;
|
||||
else
|
||||
args.size = args.stat.size;
|
||||
|
||||
if (!(args.stat.result_mask & STATX_BTIME))
|
||||
/* underlying FS does not offer btime so we can't share it */
|
||||
@@ -3767,7 +3758,7 @@ nfsd4_encode_entry4_fattr(struct nfsd4_readdir *cd, const char *name,
|
||||
nfserr = nfserrno(err);
|
||||
goto out_put;
|
||||
}
|
||||
nfserr = check_nfsd_access(exp, cd->rd_rqstp);
|
||||
nfserr = check_nfsd_access(exp, cd->rd_rqstp, false);
|
||||
if (nfserr)
|
||||
goto out_put;
|
||||
|
||||
@@ -4825,6 +4816,25 @@ nfsd4_encode_server_owner4(struct xdr_stream *xdr, struct svc_rqst *rqstp)
|
||||
return nfsd4_encode_opaque(xdr, nn->nfsd_name, strlen(nn->nfsd_name));
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_encode_nfs_impl_id4(struct xdr_stream *xdr, struct nfsd4_exchange_id *exid)
|
||||
{
|
||||
__be32 status;
|
||||
|
||||
/* nii_domain */
|
||||
status = nfsd4_encode_opaque(xdr, exid->nii_domain.data,
|
||||
exid->nii_domain.len);
|
||||
if (status != nfs_ok)
|
||||
return status;
|
||||
/* nii_name */
|
||||
status = nfsd4_encode_opaque(xdr, exid->nii_name.data,
|
||||
exid->nii_name.len);
|
||||
if (status != nfs_ok)
|
||||
return status;
|
||||
/* nii_time */
|
||||
return nfsd4_encode_nfstime4(xdr, &exid->nii_time);
|
||||
}
|
||||
|
||||
static __be32
|
||||
nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||
union nfsd4_op_u *u)
|
||||
@@ -4859,8 +4869,11 @@ nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, __be32 nfserr,
|
||||
if (nfserr != nfs_ok)
|
||||
return nfserr;
|
||||
/* eir_server_impl_id<1> */
|
||||
if (xdr_stream_encode_u32(xdr, 0) != XDR_UNIT)
|
||||
if (xdr_stream_encode_u32(xdr, 1) != XDR_UNIT)
|
||||
return nfserr_resource;
|
||||
nfserr = nfsd4_encode_nfs_impl_id4(xdr, exid);
|
||||
if (nfserr != nfs_ok)
|
||||
return nfserr;
|
||||
|
||||
return nfs_ok;
|
||||
}
|
||||
|
||||
@@ -320,6 +320,7 @@ __fh_verify(struct svc_rqst *rqstp,
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct svc_export *exp = NULL;
|
||||
bool may_bypass_gss = false;
|
||||
struct dentry *dentry;
|
||||
__be32 error;
|
||||
|
||||
@@ -362,13 +363,12 @@ __fh_verify(struct svc_rqst *rqstp,
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* pseudoflavor restrictions are not enforced on NLM,
|
||||
* which clients virtually always use auth_sys for,
|
||||
* even while using RPCSEC_GSS for NFS.
|
||||
*/
|
||||
if (access & NFSD_MAY_LOCK || access & NFSD_MAY_BYPASS_GSS)
|
||||
goto skip_pseudoflavor_check;
|
||||
if ((access & NFSD_MAY_NLM) && (exp->ex_flags & NFSEXP_NOAUTHNLM))
|
||||
/* NLM is allowed to fully bypass authentication */
|
||||
goto out;
|
||||
|
||||
if (access & NFSD_MAY_BYPASS_GSS)
|
||||
may_bypass_gss = true;
|
||||
/*
|
||||
* Clients may expect to be able to use auth_sys during mount,
|
||||
* even if they use gss for everything else; see section 2.3.2
|
||||
@@ -376,13 +376,12 @@ __fh_verify(struct svc_rqst *rqstp,
|
||||
*/
|
||||
if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT
|
||||
&& exp->ex_path.dentry == dentry)
|
||||
goto skip_pseudoflavor_check;
|
||||
may_bypass_gss = true;
|
||||
|
||||
error = check_nfsd_access(exp, rqstp);
|
||||
error = check_nfsd_access(exp, rqstp, may_bypass_gss);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
skip_pseudoflavor_check:
|
||||
/* Finally, check access permissions. */
|
||||
error = nfsd_permission(cred, exp, dentry, access);
|
||||
out:
|
||||
@@ -667,20 +666,18 @@ out_negative:
|
||||
__be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp)
|
||||
{
|
||||
bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
|
||||
struct inode *inode;
|
||||
struct kstat stat;
|
||||
__be32 err;
|
||||
|
||||
if (fhp->fh_no_wcc || fhp->fh_pre_saved)
|
||||
return nfs_ok;
|
||||
|
||||
inode = d_inode(fhp->fh_dentry);
|
||||
err = fh_getattr(fhp, &stat);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (v4)
|
||||
fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode);
|
||||
fhp->fh_pre_change = nfsd4_change_attribute(&stat);
|
||||
|
||||
fhp->fh_pre_mtime = stat.mtime;
|
||||
fhp->fh_pre_ctime = stat.ctime;
|
||||
@@ -697,7 +694,6 @@ __be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp)
|
||||
__be32 fh_fill_post_attrs(struct svc_fh *fhp)
|
||||
{
|
||||
bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE);
|
||||
struct inode *inode = d_inode(fhp->fh_dentry);
|
||||
__be32 err;
|
||||
|
||||
if (fhp->fh_no_wcc)
|
||||
@@ -713,7 +709,7 @@ __be32 fh_fill_post_attrs(struct svc_fh *fhp)
|
||||
fhp->fh_post_saved = true;
|
||||
if (v4)
|
||||
fhp->fh_post_change =
|
||||
nfsd4_change_attribute(&fhp->fh_post_attr, inode);
|
||||
nfsd4_change_attribute(&fhp->fh_post_attr);
|
||||
return nfs_ok;
|
||||
}
|
||||
|
||||
@@ -770,7 +766,7 @@ char * SVCFH_fmt(struct svc_fh *fhp)
|
||||
struct knfsd_fh *fh = &fhp->fh_handle;
|
||||
static char buf[2+1+1+64*3+1];
|
||||
|
||||
if (fh->fh_size < 0 || fh->fh_size> 64)
|
||||
if (fh->fh_size > 64)
|
||||
return "bad-fh";
|
||||
sprintf(buf, "%d: %*ph", fh->fh_size, fh->fh_size, fh->fh_raw);
|
||||
return buf;
|
||||
@@ -804,7 +800,14 @@ enum fsid_source fsid_source(const struct svc_fh *fhp)
|
||||
return FSIDSOURCE_DEV;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* nfsd4_change_attribute - Generate an NFSv4 change_attribute value
|
||||
* @stat: inode attributes
|
||||
*
|
||||
* Caller must fill in @stat before calling, typically by invoking
|
||||
* vfs_getattr() with STATX_MODE, STATX_CTIME, and STATX_CHANGE_COOKIE.
|
||||
* Returns an unsigned 64-bit changeid4 value (RFC 8881 Section 3.2).
|
||||
*
|
||||
* We could use i_version alone as the change attribute. However, i_version
|
||||
* can go backwards on a regular file after an unclean shutdown. On its own
|
||||
* that doesn't necessarily cause a problem, but if i_version goes backwards
|
||||
@@ -821,13 +824,13 @@ enum fsid_source fsid_source(const struct svc_fh *fhp)
|
||||
* assume that the new change attr is always logged to stable storage in some
|
||||
* fashion before the results can be seen.
|
||||
*/
|
||||
u64 nfsd4_change_attribute(const struct kstat *stat, const struct inode *inode)
|
||||
u64 nfsd4_change_attribute(const struct kstat *stat)
|
||||
{
|
||||
u64 chattr;
|
||||
|
||||
if (stat->result_mask & STATX_CHANGE_COOKIE) {
|
||||
chattr = stat->change_cookie;
|
||||
if (S_ISREG(inode->i_mode) &&
|
||||
if (S_ISREG(stat->mode) &&
|
||||
!(stat->attributes & STATX_ATTR_CHANGE_MONOTONIC)) {
|
||||
chattr += (u64)stat->ctime.tv_sec << 30;
|
||||
chattr += stat->ctime.tv_nsec;
|
||||
|
||||
@@ -297,8 +297,7 @@ static inline void fh_clear_pre_post_attrs(struct svc_fh *fhp)
|
||||
fhp->fh_pre_saved = false;
|
||||
}
|
||||
|
||||
u64 nfsd4_change_attribute(const struct kstat *stat,
|
||||
const struct inode *inode);
|
||||
u64 nfsd4_change_attribute(const struct kstat *stat);
|
||||
__be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp);
|
||||
__be32 fh_fill_post_attrs(struct svc_fh *fhp);
|
||||
__be32 __must_check fh_fill_both_attrs(struct svc_fh *fhp);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user