Commit 700a800a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'nfsd-5.2' of git://linux-nfs.org/~bfields/linux

Pull nfsd updates from Bruce Fields:
 "This consists mostly of nfsd container work:

  Scott Mayhew revived an old api that communicates with a userspace
  daemon to manage some on-disk state that's used to track clients
  across server reboots. We've been using a usermode_helper upcall for
  that, but it's tough to run those with the right namespaces, so a
  daemon is much friendlier to container use cases.

  Trond fixed nfsd's handling of user credentials in user namespaces. He
  also contributed patches that allow containers to support different
  sets of NFS protocol versions.

  The only remaining container bug I'm aware of is that the NFS reply
  cache is shared between all containers. If anyone's aware of other
  gaps in our container support, let me know.

  The rest of this is miscellaneous bugfixes"

* tag 'nfsd-5.2' of git://linux-nfs.org/~bfields/linux: (23 commits)
  nfsd: update callback done processing
  locks: move checks from locks_free_lock() to locks_release_private()
  nfsd: fh_drop_write in nfsd_unlink
  nfsd: allow fh_want_write to be called twice
  nfsd: knfsd must use the container user namespace
  SUNRPC: rsi_parse() should use the current user namespace
  SUNRPC: Fix the server AUTH_UNIX userspace mappings
  lockd: Pass the user cred from knfsd when starting the lockd server
  SUNRPC: Temporary sockets should inherit the cred from their parent
  SUNRPC: Cache the process user cred in the RPC server listener
  nfsd: Allow containers to set supported nfs versions
  nfsd: Add custom rpcbind callbacks for knfsd
  SUNRPC: Allow further customisation of RPC program registration
  SUNRPC: Clean up generic dispatcher code
  SUNRPC: Add a callback to initialise server requests
  SUNRPC/nfs: Fix return value for nfs4_callback_compound()
  nfsd: handle legacy client tracking records sent by nfsdcld
  nfsd: re-order client tracking method selection
  nfsd: keep a tally of RECLAIM_COMPLETE operations when using nfsdcld
  nfsd: un-deprecate nfsdcld
  ...
parents b06ed1e7 1c73b9d2
...@@ -56,7 +56,7 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init) ...@@ -56,7 +56,7 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4; u32 nlm_version = (nlm_init->nfs_version == 2) ? 1 : 4;
int status; int status;
status = lockd_up(nlm_init->net); status = lockd_up(nlm_init->net, nlm_init->cred);
if (status < 0) if (status < 0)
return ERR_PTR(status); return ERR_PTR(status);
...@@ -241,7 +241,7 @@ reclaimer(void *ptr) ...@@ -241,7 +241,7 @@ reclaimer(void *ptr)
allow_signal(SIGKILL); allow_signal(SIGKILL);
down_write(&host->h_rwsem); down_write(&host->h_rwsem);
lockd_up(net); /* note: this cannot fail as lockd is already running */ lockd_up(net, NULL); /* note: this cannot fail as lockd is already running */
dprintk("lockd: reclaiming locks for host %s\n", host->h_name); dprintk("lockd: reclaiming locks for host %s\n", host->h_name);
......
...@@ -188,28 +188,31 @@ lockd(void *vrqstp) ...@@ -188,28 +188,31 @@ lockd(void *vrqstp)
static int create_lockd_listener(struct svc_serv *serv, const char *name, static int create_lockd_listener(struct svc_serv *serv, const char *name,
struct net *net, const int family, struct net *net, const int family,
const unsigned short port) const unsigned short port,
const struct cred *cred)
{ {
struct svc_xprt *xprt; struct svc_xprt *xprt;
xprt = svc_find_xprt(serv, name, net, family, 0); xprt = svc_find_xprt(serv, name, net, family, 0);
if (xprt == NULL) if (xprt == NULL)
return svc_create_xprt(serv, name, net, family, port, return svc_create_xprt(serv, name, net, family, port,
SVC_SOCK_DEFAULTS); SVC_SOCK_DEFAULTS, cred);
svc_xprt_put(xprt); svc_xprt_put(xprt);
return 0; return 0;
} }
static int create_lockd_family(struct svc_serv *serv, struct net *net, static int create_lockd_family(struct svc_serv *serv, struct net *net,
const int family) const int family, const struct cred *cred)
{ {
int err; int err;
err = create_lockd_listener(serv, "udp", net, family, nlm_udpport); err = create_lockd_listener(serv, "udp", net, family, nlm_udpport,
cred);
if (err < 0) if (err < 0)
return err; return err;
return create_lockd_listener(serv, "tcp", net, family, nlm_tcpport); return create_lockd_listener(serv, "tcp", net, family, nlm_tcpport,
cred);
} }
/* /*
...@@ -222,16 +225,17 @@ static int create_lockd_family(struct svc_serv *serv, struct net *net, ...@@ -222,16 +225,17 @@ static int create_lockd_family(struct svc_serv *serv, struct net *net,
* Returns zero if all listeners are available; otherwise a * Returns zero if all listeners are available; otherwise a
* negative errno value is returned. * negative errno value is returned.
*/ */
static int make_socks(struct svc_serv *serv, struct net *net) static int make_socks(struct svc_serv *serv, struct net *net,
const struct cred *cred)
{ {
static int warned; static int warned;
int err; int err;
err = create_lockd_family(serv, net, PF_INET); err = create_lockd_family(serv, net, PF_INET, cred);
if (err < 0) if (err < 0)
goto out_err; goto out_err;
err = create_lockd_family(serv, net, PF_INET6); err = create_lockd_family(serv, net, PF_INET6, cred);
if (err < 0 && err != -EAFNOSUPPORT) if (err < 0 && err != -EAFNOSUPPORT)
goto out_err; goto out_err;
...@@ -246,7 +250,8 @@ static int make_socks(struct svc_serv *serv, struct net *net) ...@@ -246,7 +250,8 @@ static int make_socks(struct svc_serv *serv, struct net *net)
return err; return err;
} }
static int lockd_up_net(struct svc_serv *serv, struct net *net) static int lockd_up_net(struct svc_serv *serv, struct net *net,
const struct cred *cred)
{ {
struct lockd_net *ln = net_generic(net, lockd_net_id); struct lockd_net *ln = net_generic(net, lockd_net_id);
int error; int error;
...@@ -258,7 +263,7 @@ static int lockd_up_net(struct svc_serv *serv, struct net *net) ...@@ -258,7 +263,7 @@ static int lockd_up_net(struct svc_serv *serv, struct net *net)
if (error) if (error)
goto err_bind; goto err_bind;
error = make_socks(serv, net); error = make_socks(serv, net, cred);
if (error < 0) if (error < 0)
goto err_bind; goto err_bind;
set_grace_period(net); set_grace_period(net);
...@@ -461,7 +466,7 @@ static struct svc_serv *lockd_create_svc(void) ...@@ -461,7 +466,7 @@ static struct svc_serv *lockd_create_svc(void)
/* /*
* Bring up the lockd process if it's not already up. * Bring up the lockd process if it's not already up.
*/ */
int lockd_up(struct net *net) int lockd_up(struct net *net, const struct cred *cred)
{ {
struct svc_serv *serv; struct svc_serv *serv;
int error; int error;
...@@ -474,7 +479,7 @@ int lockd_up(struct net *net) ...@@ -474,7 +479,7 @@ int lockd_up(struct net *net)
goto err_create; goto err_create;
} }
error = lockd_up_net(serv, net); error = lockd_up_net(serv, net, cred);
if (error < 0) { if (error < 0) {
lockd_unregister_notifiers(); lockd_unregister_notifiers();
goto err_put; goto err_put;
...@@ -807,5 +812,7 @@ static struct svc_program nlmsvc_program = { ...@@ -807,5 +812,7 @@ static struct svc_program nlmsvc_program = {
.pg_name = "lockd", /* service name */ .pg_name = "lockd", /* service name */
.pg_class = "nfsd", /* share authentication with nfsd */ .pg_class = "nfsd", /* share authentication with nfsd */
.pg_stats = &nlmsvc_stats, /* stats table */ .pg_stats = &nlmsvc_stats, /* stats table */
.pg_authenticate = &lockd_authenticate /* export authentication */ .pg_authenticate = &lockd_authenticate, /* export authentication */
.pg_init_request = svc_generic_init_request,
.pg_rpcbind_set = svc_generic_rpcbind_set,
}; };
...@@ -352,6 +352,12 @@ EXPORT_SYMBOL_GPL(locks_alloc_lock); ...@@ -352,6 +352,12 @@ EXPORT_SYMBOL_GPL(locks_alloc_lock);
void locks_release_private(struct file_lock *fl) void locks_release_private(struct file_lock *fl)
{ {
BUG_ON(waitqueue_active(&fl->fl_wait));
BUG_ON(!list_empty(&fl->fl_list));
BUG_ON(!list_empty(&fl->fl_blocked_requests));
BUG_ON(!list_empty(&fl->fl_blocked_member));
BUG_ON(!hlist_unhashed(&fl->fl_link));
if (fl->fl_ops) { if (fl->fl_ops) {
if (fl->fl_ops->fl_release_private) if (fl->fl_ops->fl_release_private)
fl->fl_ops->fl_release_private(fl); fl->fl_ops->fl_release_private(fl);
...@@ -371,12 +377,6 @@ EXPORT_SYMBOL_GPL(locks_release_private); ...@@ -371,12 +377,6 @@ EXPORT_SYMBOL_GPL(locks_release_private);
/* Free a lock which is not in use. */ /* Free a lock which is not in use. */
void locks_free_lock(struct file_lock *fl) void locks_free_lock(struct file_lock *fl)
{ {
BUG_ON(waitqueue_active(&fl->fl_wait));
BUG_ON(!list_empty(&fl->fl_list));
BUG_ON(!list_empty(&fl->fl_blocked_requests));
BUG_ON(!list_empty(&fl->fl_blocked_member));
BUG_ON(!hlist_unhashed(&fl->fl_link));
locks_release_private(fl); locks_release_private(fl);
kmem_cache_free(filelock_cache, fl); kmem_cache_free(filelock_cache, fl);
} }
......
...@@ -41,11 +41,13 @@ static struct svc_program nfs4_callback_program; ...@@ -41,11 +41,13 @@ static struct svc_program nfs4_callback_program;
static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net) static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
{ {
const struct cred *cred = current_cred();
int ret; int ret;
struct nfs_net *nn = net_generic(net, nfs_net_id); struct nfs_net *nn = net_generic(net, nfs_net_id);
ret = svc_create_xprt(serv, "tcp", net, PF_INET, ret = svc_create_xprt(serv, "tcp", net, PF_INET,
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
cred);
if (ret <= 0) if (ret <= 0)
goto out_err; goto out_err;
nn->nfs_callback_tcpport = ret; nn->nfs_callback_tcpport = ret;
...@@ -53,7 +55,8 @@ static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net) ...@@ -53,7 +55,8 @@ static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
nn->nfs_callback_tcpport, PF_INET, net->ns.inum); nn->nfs_callback_tcpport, PF_INET, net->ns.inum);
ret = svc_create_xprt(serv, "tcp", net, PF_INET6, ret = svc_create_xprt(serv, "tcp", net, PF_INET6,
nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS,
cred);
if (ret > 0) { if (ret > 0) {
nn->nfs_callback_tcpport6 = ret; nn->nfs_callback_tcpport6 = ret;
dprintk("NFS: Callback listener port = %u (af %u, net %x)\n", dprintk("NFS: Callback listener port = %u (af %u, net %x)\n",
...@@ -457,4 +460,6 @@ static struct svc_program nfs4_callback_program = { ...@@ -457,4 +460,6 @@ static struct svc_program nfs4_callback_program = {
.pg_class = "nfs", /* authentication class */ .pg_class = "nfs", /* authentication class */
.pg_stats = &nfs4_callback_stats, .pg_stats = &nfs4_callback_stats,
.pg_authenticate = nfs_callback_authenticate, .pg_authenticate = nfs_callback_authenticate,
.pg_init_request = svc_generic_init_request,
.pg_rpcbind_set = svc_generic_rpcbind_set,
}; };
...@@ -983,7 +983,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp) ...@@ -983,7 +983,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp)
out_invalidcred: out_invalidcred:
pr_warn_ratelimited("NFS: NFSv4 callback contains invalid cred\n"); pr_warn_ratelimited("NFS: NFSv4 callback contains invalid cred\n");
return rpc_autherr_badcred; return svc_return_autherr(rqstp, rpc_autherr_badcred);
} }
/* /*
......
...@@ -558,6 +558,7 @@ static int nfs_start_lockd(struct nfs_server *server) ...@@ -558,6 +558,7 @@ static int nfs_start_lockd(struct nfs_server *server)
1 : 0, 1 : 0,
.net = clp->cl_net, .net = clp->cl_net,
.nlmclnt_ops = clp->cl_nfs_mod->rpc_ops->nlmclnt_ops, .nlmclnt_ops = clp->cl_nfs_mod->rpc_ops->nlmclnt_ops,
.cred = current_cred(),
}; };
if (nlm_init.nfs_version > 3) if (nlm_init.nfs_version > 3)
......
...@@ -570,13 +570,13 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen) ...@@ -570,13 +570,13 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
err = get_int(&mesg, &an_int); err = get_int(&mesg, &an_int);
if (err) if (err)
goto out3; goto out3;
exp.ex_anon_uid= make_kuid(&init_user_ns, an_int); exp.ex_anon_uid= make_kuid(current_user_ns(), an_int);
/* anon gid */ /* anon gid */
err = get_int(&mesg, &an_int); err = get_int(&mesg, &an_int);
if (err) if (err)
goto out3; goto out3;
exp.ex_anon_gid= make_kgid(&init_user_ns, an_int); exp.ex_anon_gid= make_kgid(current_user_ns(), an_int);
/* fsid */ /* fsid */
err = get_int(&mesg, &an_int); err = get_int(&mesg, &an_int);
...@@ -1170,15 +1170,17 @@ static void show_secinfo(struct seq_file *m, struct svc_export *exp) ...@@ -1170,15 +1170,17 @@ static void show_secinfo(struct seq_file *m, struct svc_export *exp)
static void exp_flags(struct seq_file *m, int flag, int fsid, static void exp_flags(struct seq_file *m, int flag, int fsid,
kuid_t anonu, kgid_t anong, struct nfsd4_fs_locations *fsloc) kuid_t anonu, kgid_t anong, struct nfsd4_fs_locations *fsloc)
{ {
struct user_namespace *userns = m->file->f_cred->user_ns;
show_expflags(m, flag, NFSEXP_ALLFLAGS); show_expflags(m, flag, NFSEXP_ALLFLAGS);
if (flag & NFSEXP_FSID) if (flag & NFSEXP_FSID)
seq_printf(m, ",fsid=%d", fsid); seq_printf(m, ",fsid=%d", fsid);
if (!uid_eq(anonu, make_kuid(&init_user_ns, (uid_t)-2)) && if (!uid_eq(anonu, make_kuid(userns, (uid_t)-2)) &&
!uid_eq(anonu, make_kuid(&init_user_ns, 0x10000-2))) !uid_eq(anonu, make_kuid(userns, 0x10000-2)))
seq_printf(m, ",anonuid=%u", from_kuid(&init_user_ns, anonu)); seq_printf(m, ",anonuid=%u", from_kuid_munged(userns, anonu));
if (!gid_eq(anong, make_kgid(&init_user_ns, (gid_t)-2)) && if (!gid_eq(anong, make_kgid(userns, (gid_t)-2)) &&
!gid_eq(anong, make_kgid(&init_user_ns, 0x10000-2))) !gid_eq(anong, make_kgid(userns, 0x10000-2)))
seq_printf(m, ",anongid=%u", from_kgid(&init_user_ns, anong)); seq_printf(m, ",anongid=%u", from_kgid_munged(userns, anong));
if (fsloc && fsloc->locations_count > 0) { if (fsloc && fsloc->locations_count > 0) {
char *loctype = (fsloc->migrated) ? "refer" : "replicas"; char *loctype = (fsloc->migrated) ? "refer" : "replicas";
int i; int i;
......
...@@ -104,6 +104,9 @@ struct nfsd_net { ...@@ -104,6 +104,9 @@ struct nfsd_net {
time_t nfsd4_grace; time_t nfsd4_grace;
bool somebody_reclaimed; bool somebody_reclaimed;
bool track_reclaim_completes;
atomic_t nr_reclaim_complete;
bool nfsd_net_up; bool nfsd_net_up;
bool lockd_up; bool lockd_up;
...@@ -131,10 +134,18 @@ struct nfsd_net { ...@@ -131,10 +134,18 @@ struct nfsd_net {
u32 s2s_cp_cl_id; u32 s2s_cp_cl_id;
struct idr s2s_cp_stateids; struct idr s2s_cp_stateids;
spinlock_t s2s_cp_lock; spinlock_t s2s_cp_lock;
/*
* Version information
*/
bool *nfsd_versions;
bool *nfsd4_minorversions;
}; };
/* Simple check to find out if a given net was properly initialized */ /* Simple check to find out if a given net was properly initialized */
#define nfsd_netns_ready(nn) ((nn)->sessionid_hashtbl) #define nfsd_netns_ready(nn) ((nn)->sessionid_hashtbl)
extern void nfsd_netns_free_versions(struct nfsd_net *nn);
extern unsigned int nfsd_net_id; extern unsigned int nfsd_net_id;
#endif /* __NFSD_NETNS_H__ */ #endif /* __NFSD_NETNS_H__ */
...@@ -96,7 +96,7 @@ decode_filename(__be32 *p, char **namp, unsigned int *lenp) ...@@ -96,7 +96,7 @@ decode_filename(__be32 *p, char **namp, unsigned int *lenp)
} }
static __be32 * static __be32 *
decode_sattr3(__be32 *p, struct iattr *iap) decode_sattr3(__be32 *p, struct iattr *iap, struct user_namespace *userns)
{ {
u32 tmp; u32 tmp;
...@@ -107,12 +107,12 @@ decode_sattr3(__be32 *p, struct iattr *iap) ...@@ -107,12 +107,12 @@ decode_sattr3(__be32 *p, struct iattr *iap)
iap->ia_mode = ntohl(*p++); iap->ia_mode = ntohl(*p++);
} }
if (*p++) { if (*p++) {
iap->ia_uid = make_kuid(&init_user_ns, ntohl(*p++)); iap->ia_uid = make_kuid(userns, ntohl(*p++));
if (uid_valid(iap->ia_uid)) if (uid_valid(iap->ia_uid))
iap->ia_valid |= ATTR_UID; iap->ia_valid |= ATTR_UID;
} }
if (*p++) { if (*p++) {
iap->ia_gid = make_kgid(&init_user_ns, ntohl(*p++)); iap->ia_gid = make_kgid(userns, ntohl(*p++));
if (gid_valid(iap->ia_gid)) if (gid_valid(iap->ia_gid))
iap->ia_valid |= ATTR_GID; iap->ia_valid |= ATTR_GID;
} }
...@@ -165,12 +165,13 @@ static __be32 * ...@@ -165,12 +165,13 @@ static __be32 *
encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, encode_fattr3(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
struct kstat *stat) struct kstat *stat)
{ {
struct user_namespace *userns = nfsd_user_namespace(rqstp);
struct timespec ts; struct timespec ts;
*p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]); *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
*p++ = htonl((u32) (stat->mode & S_IALLUGO)); *p++ = htonl((u32) (stat->mode & S_IALLUGO));
*p++ = htonl((u32) stat->nlink); *p++ = htonl((u32) stat->nlink);
*p++ = htonl((u32) from_kuid(&init_user_ns, stat->uid)); *p++ = htonl((u32) from_kuid_munged(userns, stat->uid));
*p++ = htonl((u32) from_kgid(&init_user_ns, stat->gid)); *p++ = htonl((u32) from_kgid_munged(userns, stat->gid));
if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) { if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) {
p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN); p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
} else { } else {
...@@ -325,7 +326,7 @@ nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p) ...@@ -325,7 +326,7 @@ nfs3svc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p)
p = decode_fh(p, &args->fh); p = decode_fh(p, &args->fh);
if (!p) if (!p)
return 0; return 0;
p = decode_sattr3(p, &args->attrs); p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
if ((args->check_guard = ntohl(*p++)) != 0) { if ((args->check_guard = ntohl(*p++)) != 0) {
struct timespec time; struct timespec time;
...@@ -455,7 +456,7 @@ nfs3svc_decode_createargs(struct svc_rqst *rqstp, __be32 *p) ...@@ -455,7 +456,7 @@ nfs3svc_decode_createargs(struct svc_rqst *rqstp, __be32 *p)
switch (args->createmode = ntohl(*p++)) { switch (args->createmode = ntohl(*p++)) {
case NFS3_CREATE_UNCHECKED: case NFS3_CREATE_UNCHECKED:
case NFS3_CREATE_GUARDED: case NFS3_CREATE_GUARDED:
p = decode_sattr3(p, &args->attrs); p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
break; break;
case NFS3_CREATE_EXCLUSIVE: case NFS3_CREATE_EXCLUSIVE:
args->verf = p; args->verf = p;
...@@ -476,7 +477,7 @@ nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, __be32 *p) ...@@ -476,7 +477,7 @@ nfs3svc_decode_mkdirargs(struct svc_rqst *rqstp, __be32 *p)
if (!(p = decode_fh(p, &args->fh)) || if (!(p = decode_fh(p, &args->fh)) ||
!(p = decode_filename(p, &args->name, &args->len))) !(p = decode_filename(p, &args->name, &args->len)))
return 0; return 0;
p = decode_sattr3(p, &args->attrs); p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
return xdr_argsize_check(rqstp, p); return xdr_argsize_check(rqstp, p);
} }
...@@ -491,7 +492,7 @@ nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p) ...@@ -491,7 +492,7 @@ nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
if (!(p = decode_fh(p, &args->ffh)) || if (!(p = decode_fh(p, &args->ffh)) ||
!(p = decode_filename(p, &args->fname, &args->flen))) !(p = decode_filename(p, &args->fname, &args->flen)))
return 0; return 0;
p = decode_sattr3(p, &args->attrs); p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
args->tlen = ntohl(*p++); args->tlen = ntohl(*p++);
...@@ -519,7 +520,7 @@ nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, __be32 *p) ...@@ -519,7 +520,7 @@ nfs3svc_decode_mknodargs(struct svc_rqst *rqstp, __be32 *p)
if (args->ftype == NF3BLK || args->ftype == NF3CHR if (args->ftype == NF3BLK || args->ftype == NF3CHR
|| args->ftype == NF3SOCK || args->ftype == NF3FIFO) || args->ftype == NF3SOCK || args->ftype == NF3FIFO)
p = decode_sattr3(p, &args->attrs); p = decode_sattr3(p, &args->attrs, nfsd_user_namespace(rqstp));
if (args->ftype == NF3BLK || args->ftype == NF3CHR) { if (args->ftype == NF3BLK || args->ftype == NF3CHR) {
args->major = ntohl(*p++); args->major = ntohl(*p++);
......
...@@ -1123,10 +1123,11 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) ...@@ -1123,10 +1123,11 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
rpc_restart_call_prepare(task); rpc_restart_call_prepare(task);
return; return;
case 1: case 1:
break; switch (task->tk_status) {
case -1: case -EIO:
/* Network partition? */ case -ETIMEDOUT:
nfsd4_mark_cb_down(clp, task->tk_status); nfsd4_mark_cb_down(clp, task->tk_status);
}
break; break;
default: default:
BUG(); BUG();
......
...@@ -634,7 +634,7 @@ nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen, ...@@ -634,7 +634,7 @@ nfsd_map_name_to_uid(struct svc_rqst *rqstp, const char *name, size_t namelen,
return nfserr_inval; return nfserr_inval;
status = do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, &id); status = do_name_to_id(rqstp, IDMAP_TYPE_USER, name, namelen, &id);
*uid = make_kuid(&init_user_ns, id); *uid = make_kuid(nfsd_user_namespace(rqstp), id);
if (!uid_valid(*uid)) if (!uid_valid(*uid))
status = nfserr_badowner; status = nfserr_badowner;
return status; return status;
...@@ -651,7 +651,7 @@ nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, ...@@ -651,7 +651,7 @@ nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen,
return nfserr_inval; return nfserr_inval;
status = do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, &id); status = do_name_to_id(rqstp, IDMAP_TYPE_GROUP, name, namelen, &id);
*gid = make_kgid(&init_user_ns, id); *gid = make_kgid(nfsd_user_namespace(rqstp), id);
if (!gid_valid(*gid)) if (!gid_valid(*gid))
status = nfserr_badowner; status = nfserr_badowner;
return status; return status;
...@@ -660,13 +660,13 @@ nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen, ...@@ -660,13 +660,13 @@ nfsd_map_name_to_gid(struct svc_rqst *rqstp, const char *name, size_t namelen,
__be32 nfsd4_encode_user(struct xdr_stream *xdr, struct svc_rqst *rqstp, __be32 nfsd4_encode_user(struct xdr_stream *xdr, struct svc_rqst *rqstp,
kuid_t uid) kuid_t uid)
{ {
u32 id = from_kuid(&init_user_ns, uid); u32 id = from_kuid_munged(nfsd_user_namespace(rqstp), uid);
return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_USER, id); return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_USER, id);
} }
__be32 nfsd4_encode_group(struct xdr_stream *xdr, struct svc_rqst *rqstp, __be32 nfsd4_encode_group(struct xdr_stream *xdr, struct svc_rqst *rqstp,
kgid_t gid) kgid_t gid)
{ {
u32 id = from_kgid(&init_user_ns, gid); u32 id = from_kgid_munged(nfsd_user_namespace(rqstp), gid);
return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_GROUP, id); return encode_name_from_id(xdr, rqstp, IDMAP_TYPE_GROUP, id);
} }
...@@ -693,7 +693,7 @@ nfsd4_cb_layout_done(struct nfsd4_callback *cb, struct rpc_task *task) ...@@ -693,7 +693,7 @@ nfsd4_cb_layout_done(struct nfsd4_callback *cb, struct rpc_task *task)
ops->fence_client(ls); ops->fence_client(ls);
else else
nfsd4_cb_layout_fail(ls); nfsd4_cb_layout_fail(ls);
return -1; return 1;
case -NFS4ERR_NOMATCHING_LAYOUT: case -NFS4ERR_NOMATCHING_LAYOUT:
trace_nfsd_layout_recall_done(&ls->ls_stid.sc_stateid); trace_nfsd_layout_recall_done(&ls->ls_stid.sc_stateid);
task->tk_status = 0; task->tk_status = 0;
......
...@@ -1927,6 +1927,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp) ...@@ -1927,6 +1927,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
struct nfsd4_compound_state *cstate = &resp->cstate; struct nfsd4_compound_state *cstate = &resp->cstate;
struct svc_fh *current_fh = &cstate->current_fh; struct svc_fh *current_fh = &cstate->current_fh;
struct svc_fh *save_fh = &cstate->save_fh; struct svc_fh *save_fh = &cstate->save_fh;
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
__be32 status; __be32 status;
svcxdr_init_encode(rqstp, resp); svcxdr_init_encode(rqstp, resp);
...@@ -1949,7 +1950,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp) ...@@ -1949,7 +1950,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
* According to RFC3010, this takes precedence over all other errors. * According to RFC3010, this takes precedence over all other errors.
*/ */
status = nfserr_minor_vers_mismatch; status = nfserr_minor_vers_mismatch;
if (nfsd_minorversion(args->minorversion, NFSD_TEST) <= 0) if (nfsd_minorversion(nn, args->minorversion, NFSD_TEST) <= 0)
goto out; goto out;
status = nfserr_resource; status = nfserr_resource;
if (args->opcnt > NFSD_MAX_OPS_PER_COMPOUND) if (args->opcnt > NFSD_MAX_OPS_PER_COMPOUND)
......
This diff is collapsed.
...@@ -77,6 +77,7 @@ static u64 current_sessionid = 1; ...@@ -77,6 +77,7 @@ static u64 current_sessionid = 1;
/* forward declarations */ /* forward declarations */
static bool check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner); static bool check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner);
static void nfs4_free_ol_stateid(struct nfs4_stid *stid); static void nfs4_free_ol_stateid(struct nfs4_stid *stid);
void nfsd4_end_grace(struct nfsd_net *nn);
/* Locking: */ /* Locking: */
...@@ -1067,9 +1068,9 @@ static unsigned int clientid_hashval(u32 id) ...@@ -1067,9 +1068,9 @@ static unsigned int clientid_hashval(u32 id)
return id & CLIENT_HASH_MASK; return id & CLIENT_HASH_MASK;
} }
static unsigned int clientstr_hashval(const char *name) static unsigned int clientstr_hashval(struct xdr_netobj name)
{ {
return opaque_hashval(name, 8) & CLIENT_HASH_MASK; return opaque_hashval(name.data, 8) & CLIENT_HASH_MASK;
} }
/* /*
...@@ -1997,6 +1998,22 @@ destroy_client(struct nfs4_client *clp) ...@@ -1997,6 +1998,22 @@ destroy_client(struct nfs4_client *clp)
__destroy_client(clp); __destroy_client(clp);
} }
static void inc_reclaim_complete(struct nfs4_client *clp)
{
struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
if (!nn->track_reclaim_completes)
return;
if (!nfsd4_find_reclaim_client(clp->cl_name, nn))
return;
if (atomic_inc_return(&nn->nr_reclaim_complete) ==
nn->reclaim_str_hashtbl_size) {
printk(KERN_INFO "NFSD: all clients done reclaiming, ending NFSv4 grace period (net %x)\n",
clp->net->ns.inum);
nfsd4_end_grace(nn);
}
}
static void expire_client(struct nfs4_client *clp) static void expire_client(struct nfs4_client *clp)
{ {
unhash_client(clp); unhash_client(clp);
...@@ -2048,11 +2065,6 @@ compare_blob(const struct xdr_netobj *o1, const struct xdr_netobj *o2) ...@@ -2048,11 +2065,6 @@ compare_blob(const struct xdr_netobj *o1, const struct xdr_netobj *o2)
return memcmp(o1->data, o2->data, o1->len); return memcmp(o1->data, o2->data, o1->len);
} }
static int same_name(const char *n1, const char *n2)
{
return 0 == memcmp(n1, n2, HEXDIR_LEN);
}
static int static int
same_verf(nfs4_verifier *v1, nfs4_verifier *v2) same_verf(nfs4_verifier *v1, nfs4_verifier *v2)
{ {
...@@ -3354,6 +3366,7 @@ nfsd4_reclaim_complete(struct svc_rqst *rqstp, ...@@ -3354,6 +3366,7 @@ nfsd4_reclaim_complete(struct svc_rqst *rqstp,
status = nfs_ok; status = nfs_ok;
nfsd4_client_record_create(cstate->session->se_client); nfsd4_client_record_create(cstate->session->se_client);
inc_reclaim_complete(cstate->session->se_client);
out: out:
return status; return status;
} }
...@@ -3958,6 +3971,9 @@ static int nfsd4_cb_recall_done(struct nfsd4_callback *cb, ...@@ -3958,6 +3971,9 @@ static int nfsd4_cb_recall_done(struct nfsd4_callback *cb,
switch (task->tk_status) { switch (task->tk_status) {
case 0: case 0:
return 1; return 1;
case -NFS4ERR_DELAY:
rpc_delay(task, 2 * HZ);
return 0;
case -EBADHANDLE: case -EBADHANDLE:
case -NFS4ERR_BAD_STATEID: case -NFS4ERR_BAD_STATEID:
/* /*
...@@ -3970,7 +3986,7 @@ static int nfsd4_cb_recall_done(struct nfsd4_callback *cb, ...@@ -3970,7 +3986,7 @@ static int nfsd4_cb_recall_done(struct nfsd4_callback *cb,
} }
/*FALLTHRU*/ /*FALLTHRU*/
default: default:
return -1; return 1;
} }
} }
...@@ -4713,7 +4729,6 @@ nfsd4_end_grace(struct nfsd_net *nn) ...@@ -4713,7 +4729,6 @@ nfsd4_end_grace(struct nfsd_net *nn)
if (nn->grace_ended) if (nn->grace_ended)
return; return;
dprintk("NFSD: end of grace period\n");
nn->grace_ended = true; nn->grace_ended = true;
/* /*
* If the server goes down again right now, an NFSv4 * If the server goes down again right now, an NFSv4
...@@ -4749,6 +4764,10 @@ static bool clients_still_reclaiming(struct nfsd_net *nn) ...@@ -4749,6 +4764,10 @@ static bool clients_still_reclaiming(struct nfsd_net *nn)
unsigned long double_grace_period_end = nn->boot_time + unsigned long double_grace_period_end = nn->boot_time +
2 * nn->nfsd4_lease; 2 * nn->nfsd4_lease;
if (nn->track_reclaim_completes &&
atomic_read(&nn->nr_reclaim_complete) ==
nn->reclaim_str_hashtbl_size)
return false;
if (!nn->somebody_reclaimed) if (!nn->somebody_reclaimed)
return false; return false;
nn->somebody_reclaimed = false; nn->somebody_reclaimed = false;
...@@ -4779,6 +4798,7 @@ nfs4_laundromat(struct nfsd_net *nn) ...@@ -4779,6 +4798,7 @@ nfs4_laundromat(struct nfsd_net *nn)
new_timeo = 0; new_timeo = 0;
goto out; goto out;
} }
dprintk("NFSD: end of grace period\n");
nfsd4_end_grace(nn); nfsd4_end_grace(nn);
INIT_LIST_HEAD(&reaplist); INIT_LIST_HEAD(&reaplist);
spin_lock(&nn->client_lock); spin_lock(&nn->client_lock);
...@@ -6458,7 +6478,7 @@ alloc_reclaim(void) ...@@ -6458,7 +6478,7 @@ alloc_reclaim(void)
} }
bool bool
nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn) nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn)
{ {
struct nfs4_client_reclaim *crp; struct nfs4_client_reclaim *crp;
...@@ -6468,20 +6488,24 @@ nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn) ...@@ -6468,20 +6488,24 @@ nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn)
/* /*
* failure => all reset bets are off, nfserr_no_grace... * failure => all reset bets are off, nfserr_no_grace...
*
* The caller is responsible for freeing name.data if NULL is returned (it
* will be freed in nfs4_remove_reclaim_record in the normal case).
*/ */
struct nfs4_client_reclaim * struct nfs4_client_reclaim *
nfs4_client_to_reclaim(const char *name, struct nfsd_net *nn) nfs4_client_to_reclaim(struct xdr_netobj name, struct nfsd_net *nn)
{ {
unsigned int strhashval; unsigned int strhashval;
struct nfs4_client_reclaim *crp; struct nfs4_client_reclaim *crp;
dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", HEXDIR_LEN, name); dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", name.len, name.data);
crp = alloc_reclaim(); crp = alloc_reclaim();
if (crp) { if (crp) {
strhashval = clientstr_hashval(name); strhashval = clientstr_hashval(name);
INIT_LIST_HEAD(&crp->cr_strhash); INIT_LIST_HEAD(&crp->cr_strhash);
list_add(&crp->cr_strhash, &nn->reclaim_str_hashtbl[strhashval]); list_add(&crp->cr_strhash, &nn->reclaim_str_hashtbl[strhashval]);
memcpy(crp->cr_recdir, name, HEXDIR_LEN); crp->cr_name.data = name.data;
crp->cr_name.len = name.len;
crp->cr_clp = NULL; crp->cr_clp = NULL;
nn->reclaim_str_hashtbl_size++; nn->reclaim_str_hashtbl_size++;
} }
...@@ -6492,6 +6516,7 @@ void ...@@ -6492,6 +6516,7 @@ void
nfs4_remove_reclaim_record(struct nfs4_client_reclaim *crp, struct nfsd_net *nn) nfs4_remove_reclaim_record(struct nfs4_client_reclaim *crp, struct nfsd_net *nn)
{ {
list_del(&crp->cr_strhash); list_del(&crp->cr_strhash);
kfree(crp->cr_name.data);
kfree(crp); kfree(crp);
nn->reclaim_str_hashtbl_size--; nn->reclaim_str_hashtbl_size--;
} }
...@@ -6515,16 +6540,16 @@ nfs4_release_reclaim(struct nfsd_net *nn) ...@@ -6515,16 +6540,16 @@ nfs4_release_reclaim(struct nfsd_net *nn)
/* /*
* called from OPEN, CLAIM_PREVIOUS with a new clientid. */ * called from OPEN, CLAIM_PREVIOUS with a new clientid. */
struct nfs4_client_reclaim * struct nfs4_client_reclaim *
nfsd4_find_reclaim_client(const char *recdir, struct nfsd_net *nn) nfsd4_find_reclaim_client(struct xdr_netobj name, struct nfsd_net *nn)
{ {
unsigned int strhashval; unsigned int strhashval;
struct nfs4_client_reclaim *crp = NULL; struct nfs4_client_reclaim *crp = NULL;
dprintk("NFSD: nfs4_find_reclaim_client for recdir %s\n", recdir); dprintk("NFSD: nfs4_find_reclaim_client for name %.*s\n", name.len, name.data);
strhashval = clientstr_hashval(recdir); strhashval = clientstr_hashval(name);
list_for_each_entry(crp, &nn->reclaim_str_hashtbl[strhashval], cr_strhash) { list_for_each_entry(crp, &nn->reclaim_str_hashtbl[strhashval], cr_strhash) {
if (same_name(crp->cr_recdir, recdir)) { if (compare_blob(&crp->cr_name, &name) == 0) {
return crp; return crp;
} }
} }
...@@ -7262,10 +7287,19 @@ nfs4_state_start_net(struct net *net) ...@@ -7262,10 +7287,19 @@ nfs4_state_start_net(struct net *net)
return ret; return ret;
locks_start_grace(net, &nn->nfsd4_manager); locks_start_grace(net, &nn->nfsd4_manager);
nfsd4_client_tracking_init(net); nfsd4_client_tracking_init(net);
if (nn->track_reclaim_completes && nn->reclaim_str_hashtbl_size == 0)
goto skip_grace;
printk(KERN_INFO "NFSD: starting %ld-second grace period (net %x)\n", printk(KERN_INFO "NFSD: starting %ld-second grace period (net %x)\n",
nn->nfsd4_grace, net->ns.inum); nn->nfsd4_grace, net->ns.inum);
queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ); queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ);
return 0; return 0;
skip_grace:
printk(KERN_INFO "NFSD: no clients to reclaim, skipping NFSv4 grace period (net %x)\n",
net->ns.inum);
queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_lease * HZ);
nfsd4_end_grace(nn);
return 0;
} }
/* initialization to perform when the nfsd service is started: */ /* initialization to perform when the nfsd service is started: */
......
...@@ -521,6 +521,7 @@ nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access ...@@ -521,6 +521,7 @@ nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access
static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs) static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs)
{ {
DECODE_HEAD; DECODE_HEAD;
struct user_namespace *userns = nfsd_user_namespace(argp->rqstp);
u32 dummy, uid, gid; u32 dummy, uid, gid;
char *machine_name; char *machine_name;
int i; int i;
...@@ -563,8 +564,8 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_ ...@@ -563,8 +564,8 @@ static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_
dummy = be32_to_cpup(p++); dummy = be32_to_cpup(p++);
READ_BUF(dummy * 4); READ_BUF(dummy * 4);
if (cbs->flavor == (u32)(-1)) { if (cbs->flavor == (u32)(-1)) {
kuid_t kuid = make_kuid(&init_user_ns, uid); kuid_t kuid = make_kuid(userns, uid);
kgid_t kgid = make_kgid(&init_user_ns, gid); kgid_t kgid = make_kgid(userns, gid);
if (uid_valid(kuid) && gid_valid(kgid)) { if (uid_valid(kuid) && gid_valid(kgid)) {
cbs->uid = kuid; cbs->uid = kuid;
cbs->gid = kgid; cbs->gid = kgid;
...@@ -2420,8 +2421,10 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, ...@@ -2420,8 +2421,10 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
__be32 status; __be32 status;
int err; int err;
struct nfs4_acl *acl = NULL; struct nfs4_acl *acl = NULL;
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
void *context = NULL; void *context = NULL;
int contextlen; int contextlen;
#endif
bool contextsupport = false; bool contextsupport = false;
struct nfsd4_compoundres *resp = rqstp->rq_resp; struct nfsd4_compoundres *resp = rqstp->rq_resp;
u32 minorversion = resp->cstate.minorversion; u32 minorversion = resp->cstate.minorversion;
...@@ -2906,12 +2909,14 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp, ...@@ -2906,12 +2909,14 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
*p++ = cpu_to_be32(NFS4_CHANGE_TYPE_IS_TIME_METADATA); *p++ = cpu_to_be32(NFS4_CHANGE_TYPE_IS_TIME_METADATA);
} }
#ifdef CONFIG_NFSD_V4_SECURITY_LABEL
if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) { if (bmval2 & FATTR4_WORD2_SECURITY_LABEL) {
status = nfsd4_encode_security_label(xdr, rqstp, context, status = nfsd4_encode_security_label(xdr, rqstp, context,
contextlen); contextlen);
if (status) if (status)
goto out; goto out;
} }
#endif
attrlen = htonl(xdr->buf->len - attrlen_offset - 4); attrlen = htonl(xdr->buf->len - attrlen_offset - 4);
write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, 4); write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, 4);
......
...@@ -439,7 +439,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size) ...@@ -439,7 +439,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
return rv; return rv;
if (newthreads < 0) if (newthreads < 0)
return -EINVAL; return -EINVAL;
rv = nfsd_svc(newthreads, net); rv = nfsd_svc(newthreads, net, file->f_cred);
if (rv < 0) if (rv < 0)
return rv; return rv;
} else } else
...@@ -537,14 +537,14 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) ...@@ -537,14 +537,14 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
} }
static ssize_t static ssize_t
nfsd_print_version_support(char *buf, int remaining, const char *sep, nfsd_print_version_support(struct nfsd_net *nn, char *buf, int remaining,
unsigned vers, int minor) const char *sep, unsigned vers, int minor)
{ {
const char *format = minor < 0 ? "%s%c%u" : "%s%c%u.%u"; const char *format = minor < 0 ? "%s%c%u" : "%s%c%u.%u";
bool supported = !!nfsd_vers(vers, NFSD_TEST); bool supported = !!nfsd_vers(nn, vers, NFSD_TEST);
if (vers == 4 && minor >= 0 && if (vers == 4 && minor >= 0 &&
!nfsd_minorversion(minor, NFSD_TEST)) !nfsd_minorversion(nn, minor, NFSD_TEST))
supported = false; supported = false;
if (minor == 0 && supported) if (minor == 0 && supported)
/* /*
...@@ -599,20 +599,20 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) ...@@ -599,20 +599,20 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
switch(num) { switch(num) {
case 2: case 2:
case 3: case 3:
nfsd_vers(num, cmd); nfsd_vers(nn, num, cmd);
break; break;
case 4: case 4:
if (*minorp == '.') { if (*minorp == '.') {
if (nfsd_minorversion(minor, cmd) < 0) if (nfsd_minorversion(nn, minor, cmd) < 0)
return -EINVAL; return -EINVAL;
} else if ((cmd == NFSD_SET) != nfsd_vers(num, NFSD_TEST)) { } else if ((cmd == NFSD_SET) != nfsd_vers(nn, num, NFSD_TEST)) {
/* /*
* Either we have +4 and no minors are enabled, * Either we have +4 and no minors are enabled,
* or we have -4 and at least one minor is enabled. * or we have -4 and at least one minor is enabled.
* In either case, propagate 'cmd' to all minors. * In either case, propagate 'cmd' to all minors.
*/ */
minor = 0; minor = 0;
while (nfsd_minorversion(minor, cmd) >= 0) while (nfsd_minorversion(nn, minor, cmd) >= 0)
minor++; minor++;
} }
break; break;
...@@ -624,7 +624,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) ...@@ -624,7 +624,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
/* If all get turned off, turn them back on, as /* If all get turned off, turn them back on, as
* having no versions is BAD * having no versions is BAD
*/ */
nfsd_reset_versions(); nfsd_reset_versions(nn);
} }
/* Now write current state into reply buffer */ /* Now write current state into reply buffer */
...@@ -633,12 +633,12 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) ...@@ -633,12 +633,12 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
remaining = SIMPLE_TRANSACTION_LIMIT; remaining = SIMPLE_TRANSACTION_LIMIT;
for (num=2 ; num <= 4 ; num++) { for (num=2 ; num <= 4 ; num++) {
int minor; int minor;
if (!nfsd_vers(num, NFSD_AVAIL)) if (!nfsd_vers(nn, num, NFSD_AVAIL))
continue; continue;
minor = -1; minor = -1;
do { do {
len = nfsd_print_version_support(buf, remaining, len = nfsd_print_version_support(nn, buf, remaining,
sep, num, minor); sep, num, minor);
if (len >= remaining) if (len >= remaining)
goto out; goto out;
...@@ -717,7 +717,7 @@ static ssize_t __write_ports_names(char *buf, struct net *net) ...@@ -717,7 +717,7 @@ static ssize_t __write_ports_names(char *buf, struct net *net)
* a socket of a supported family/protocol, and we use it as an * a socket of a supported family/protocol, and we use it as an
* nfsd listener. * nfsd listener.
*/ */
static ssize_t __write_ports_addfd(char *buf, struct net *net) static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred *cred)
{ {
char *mesg = buf; char *mesg = buf;
int fd, err; int fd, err;
...@@ -736,7 +736,7 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net) ...@@ -736,7 +736,7 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net)
if (err != 0) if (err != 0)
return err; return err;
err = svc_addsock(nn->nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT); err = svc_addsock(nn->nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred);
if (err < 0) { if (err < 0) {
nfsd_destroy(net); nfsd_destroy(net);
return err; return err;
...@@ -751,7 +751,7 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net) ...@@ -751,7 +751,7 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net)
* A transport listener is added by writing it's transport name and * A transport listener is added by writing it's transport name and
* a port number. * a port number.
*/ */
static ssize_t __write_ports_addxprt(char *buf, struct net *net) static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cred *cred)
{ {
char transport[16]; char transport[16];
struct svc_xprt *xprt; struct svc_xprt *xprt;
...@@ -769,12 +769,12 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net) ...@@ -769,12 +769,12 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net)
return err; return err;
err = svc_create_xprt(nn->nfsd_serv, transport, net, err = svc_create_xprt(nn->nfsd_serv, transport, net,
PF_INET, port, SVC_SOCK_ANONYMOUS); PF_INET, port, SVC_SOCK_ANONYMOUS, cred);
if (err < 0) if (err < 0)
goto out_err; goto out_err;
err = svc_create_xprt(nn->nfsd_serv, transport, net, err = svc_create_xprt(nn->nfsd_serv, transport, net,
PF_INET6, port, SVC_SOCK_ANONYMOUS); PF_INET6, port, SVC_SOCK_ANONYMOUS, cred);
if (err < 0 && err != -EAFNOSUPPORT) if (err < 0 && err != -EAFNOSUPPORT)
goto out_close; goto out_close;
...@@ -799,10 +799,10 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size, ...@@ -799,10 +799,10 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size,
return __write_ports_names(buf, net); return __write_ports_names(buf, net);
if (isdigit(buf[0])) if (isdigit(buf[0]))
return __write_ports_addfd(buf, net); return __write_ports_addfd(buf, net, file->f_cred);
if (isalpha(buf[0])) if (isalpha(buf[0]))
return __write_ports_addxprt(buf, net); return __write_ports_addxprt(buf, net, file->f_cred);
return -EINVAL; return -EINVAL;
} }
...@@ -1239,9 +1239,12 @@ static __net_init int nfsd_init_net(struct net *net) ...@@ -1239,9 +1239,12 @@ static __net_init int nfsd_init_net(struct net *net)
retval = nfsd_idmap_init(net); retval = nfsd_idmap_init(net);
if (retval) if (retval)
goto out_idmap_error; goto out_idmap_error;
nn->nfsd_versions = NULL;
nn->nfsd4_minorversions = NULL;
nn->nfsd4_lease = 90; /* default lease time */ nn->nfsd4_lease = 90; /* default lease time */
nn->nfsd4_grace = 90; nn->nfsd4_grace = 90;
nn->somebody_reclaimed = false; nn->somebody_reclaimed = false;
nn->track_reclaim_completes = false;
nn->clverifier_counter = prandom_u32(); nn->clverifier_counter = prandom_u32();
nn->clientid_counter = prandom_u32(); nn->clientid_counter = prandom_u32();
nn->s2s_cp_cl_id = nn->clientid_counter++; nn->s2s_cp_cl_id = nn->clientid_counter++;
...@@ -1260,6 +1263,7 @@ static __net_exit void nfsd_exit_net(struct net *net) ...@@ -1260,6 +1263,7 @@ static __net_exit void nfsd_exit_net(struct net *net)
{ {
nfsd_idmap_shutdown(net); nfsd_idmap_shutdown(net);
nfsd_export_shutdown(net); nfsd_export_shutdown(net);
nfsd_netns_free_versions(net_generic(net, nfsd_net_id));
} }
static struct pernet_operations nfsd_net_ops = { static struct pernet_operations nfsd_net_ops = {
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/nfs3.h> #include <linux/nfs3.h>
#include <linux/nfs4.h> #include <linux/nfs4.h>
#include <linux/sunrpc/svc.h> #include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svc_xprt.h>
#include <linux/sunrpc/msg_prot.h> #include <linux/sunrpc/msg_prot.h>
#include <uapi/linux/nfsd/debug.h> #include <uapi/linux/nfsd/debug.h>
...@@ -73,7 +74,7 @@ extern const struct seq_operations nfs_exports_op; ...@@ -73,7 +74,7 @@ extern const struct seq_operations nfs_exports_op;
/* /*
* Function prototypes. * Function prototypes.
*/ */
int nfsd_svc(int nrservs, struct net *net); int nfsd_svc(int nrservs, struct net *net, const struct cred *cred);
int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp); int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp);
int nfsd_nrthreads(struct net *); int nfsd_nrthreads(struct net *);
...@@ -98,10 +99,12 @@ extern const struct svc_version nfsd_acl_version3; ...@@ -98,10 +99,12 @@ extern const struct svc_version nfsd_acl_version3;
#endif #endif
#endif #endif
struct nfsd_net;
enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL }; enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL };
int nfsd_vers(int vers, enum vers_op change); int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change);
int nfsd_minorversion(u32 minorversion, enum vers_op change); int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change);
void nfsd_reset_versions(void); void nfsd_reset_versions(struct nfsd_net *nn);
int nfsd_create_serv(struct net *net); int nfsd_create_serv(struct net *net);
extern int nfsd_max_blksize; extern int nfsd_max_blksize;
...@@ -110,6 +113,12 @@ static inline int nfsd_v4client(struct svc_rqst *rq) ...@@ -110,6 +113,12 @@ static inline int nfsd_v4client(struct svc_rqst *rq)
{ {
return rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4; return rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
} }
static inline struct user_namespace *
nfsd_user_namespace(const struct svc_rqst *rqstp)
{
const struct cred *cred = rqstp->rq_xprt->xpt_cred;
return cred ? cred->user_ns : &init_user_ns;
}
/* /*
* NFSv4 State * NFSv4 State
......
This diff is collapsed.
...@@ -71,7 +71,7 @@ decode_filename(__be32 *p, char **namp, unsigned int *lenp) ...@@ -71,7 +71,7 @@ decode_filename(__be32 *p, char **namp, unsigned int *lenp)
} }
static __be32 * static __be32 *
decode_sattr(__be32 *p, struct iattr *iap) decode_sattr(__be32 *p, struct iattr *iap, struct user_namespace *userns)
{ {
u32 tmp, tmp1; u32 tmp, tmp1;
...@@ -86,12 +86,12 @@ decode_sattr(__be32 *p, struct iattr *iap) ...@@ -86,12 +86,12 @@ decode_sattr(__be32 *p, struct iattr *iap)
iap->ia_mode = tmp; iap->ia_mode = tmp;
} }
if ((tmp = ntohl(*p++)) != (u32)-1) { if ((tmp = ntohl(*p++)) != (u32)-1) {
iap->ia_uid = make_kuid(&init_user_ns, tmp); iap->ia_uid = make_kuid(userns, tmp);
if (uid_valid(iap->ia_uid)) if (uid_valid(iap->ia_uid))
iap->ia_valid |= ATTR_UID; iap->ia_valid |= ATTR_UID;
} }
if ((tmp = ntohl(*p++)) != (u32)-1) { if ((tmp = ntohl(*p++)) != (u32)-1) {
iap->ia_gid = make_kgid(&init_user_ns, tmp); iap->ia_gid = make_kgid(userns, tmp);
if (gid_valid(iap->ia_gid)) if (gid_valid(iap->ia_gid))
iap->ia_valid |= ATTR_GID; iap->ia_valid |= ATTR_GID;
} }
...@@ -129,6 +129,7 @@ static __be32 * ...@@ -129,6 +129,7 @@ static __be32 *
encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
struct kstat *stat) struct kstat *stat)
{ {
struct user_namespace *userns = nfsd_user_namespace(rqstp);
struct dentry *dentry = fhp->fh_dentry; struct dentry *dentry = fhp->fh_dentry;
int type; int type;
struct timespec64 time; struct timespec64 time;
...@@ -139,8 +140,8 @@ encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp, ...@@ -139,8 +140,8 @@ encode_fattr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp,
*p++ = htonl(nfs_ftypes[type >> 12]); *p++ = htonl(nfs_ftypes[type >> 12]);
*p++ = htonl((u32) stat->mode); *p++ = htonl((u32) stat->mode);
*p++ = htonl((u32) stat->nlink); *p++ = htonl((u32) stat->nlink);
*p++ = htonl((u32) from_kuid(&init_user_ns, stat->uid)); *p++ = htonl((u32) from_kuid_munged(userns, stat->uid));
*p++ = htonl((u32) from_kgid(&init_user_ns, stat->gid)); *p++ = htonl((u32) from_kgid_munged(userns, stat->gid));
if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) { if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) {
*p++ = htonl(NFS_MAXPATHLEN); *p++ = htonl(NFS_MAXPATHLEN);
...@@ -216,7 +217,7 @@ nfssvc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p) ...@@ -216,7 +217,7 @@ nfssvc_decode_sattrargs(struct svc_rqst *rqstp, __be32 *p)
p = decode_fh(p, &args->fh); p = decode_fh(p, &args->fh);
if (!p) if (!p)
return 0; return 0;
p = decode_sattr(p, &args->attrs); p = decode_sattr(p, &args->attrs, nfsd_user_namespace(rqstp));
return xdr_argsize_check(rqstp, p); return xdr_argsize_check(rqstp, p);
} }
...@@ -319,7 +320,7 @@ nfssvc_decode_createargs(struct svc_rqst *rqstp, __be32 *p) ...@@ -319,7 +320,7 @@ nfssvc_decode_createargs(struct svc_rqst *rqstp, __be32 *p)
if ( !(p = decode_fh(p, &args->fh)) if ( !(p = decode_fh(p, &args->fh))
|| !(p = decode_filename(p, &args->name, &args->len))) || !(p = decode_filename(p, &args->name, &args->len)))
return 0; return 0;
p = decode_sattr(p, &args->attrs); p = decode_sattr(p, &args->attrs, nfsd_user_namespace(rqstp));
return xdr_argsize_check(rqstp, p); return xdr_argsize_check(rqstp, p);
} }
...@@ -398,7 +399,7 @@ nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p) ...@@ -398,7 +399,7 @@ nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p)
return 0; return 0;
p += xdrlen; p += xdrlen;
} }
decode_sattr(p, &args->attrs); decode_sattr(p, &args->attrs, nfsd_user_namespace(rqstp));
return 1; return 1;
} }
......
...@@ -368,7 +368,7 @@ struct nfs4_client { ...@@ -368,7 +368,7 @@ struct nfs4_client {
struct nfs4_client_reclaim { struct nfs4_client_reclaim {
struct list_head cr_strhash; /* hash by cr_name */ struct list_head cr_strhash; /* hash by cr_name */
struct nfs4_client *cr_clp; /* pointer to associated clp */ struct nfs4_client *cr_clp; /* pointer to associated clp */
char cr_recdir[HEXDIR_LEN]; /* recover dir */ struct xdr_netobj cr_name; /* recovery dir name */
}; };
/* A reasonable value for REPLAY_ISIZE was estimated as follows: /* A reasonable value for REPLAY_ISIZE was estimated as follows:
...@@ -620,7 +620,7 @@ void nfs4_put_stid(struct nfs4_stid *s); ...@@ -620,7 +620,7 @@ void nfs4_put_stid(struct nfs4_stid *s);
void nfs4_inc_and_copy_stateid(stateid_t *dst, struct nfs4_stid *stid); void nfs4_inc_and_copy_stateid(stateid_t *dst, struct nfs4_stid *stid);
void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *); void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *);
extern void nfs4_release_reclaim(struct nfsd_net *); extern void nfs4_release_reclaim(struct nfsd_net *);
extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir, extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(struct xdr_netobj name,
struct nfsd_net *nn); struct nfsd_net *nn);
extern __be32 nfs4_check_open_reclaim(clientid_t *clid, extern __be32 nfs4_check_open_reclaim(clientid_t *clid,
struct nfsd4_compound_state *cstate, struct nfsd_net *nn); struct nfsd4_compound_state *cstate, struct nfsd_net *nn);
...@@ -635,9 +635,9 @@ extern void nfsd4_destroy_callback_queue(void); ...@@ -635,9 +635,9 @@ extern void nfsd4_destroy_callback_queue(void);
extern void nfsd4_shutdown_callback(struct nfs4_client *); extern void nfsd4_shutdown_callback(struct nfs4_client *);
extern void nfsd4_shutdown_copy(struct nfs4_client *clp); extern void nfsd4_shutdown_copy(struct nfs4_client *clp);
extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp); extern void nfsd4_prepare_cb_recall(struct nfs4_delegation *dp);
extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name, extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(struct xdr_netobj name,
struct nfsd_net *nn); struct nfsd_net *nn);
extern bool nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn); extern bool nfs4_has_reclaimed_state(struct xdr_netobj name, struct nfsd_net *nn);
struct nfs4_file *find_file(struct knfsd_fh *fh); struct nfs4_file *find_file(struct knfsd_fh *fh);
void put_nfs4_file(struct nfs4_file *fi); void put_nfs4_file(struct nfs4_file *fi);
......
...@@ -1786,12 +1786,12 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, ...@@ -1786,12 +1786,12 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
rdentry = lookup_one_len(fname, dentry, flen); rdentry = lookup_one_len(fname, dentry, flen);
host_err = PTR_ERR(rdentry); host_err = PTR_ERR(rdentry);
if (IS_ERR(rdentry)) if (IS_ERR(rdentry))
goto out_nfserr; goto out_drop_write;
if (d_really_is_negative(rdentry)) { if (d_really_is_negative(rdentry)) {
dput(rdentry); dput(rdentry);
err = nfserr_noent; host_err = -ENOENT;
goto out; goto out_drop_write;
} }
if (!type) if (!type)
...@@ -1805,6 +1805,8 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, ...@@ -1805,6 +1805,8 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
host_err = commit_metadata(fhp); host_err = commit_metadata(fhp);
dput(rdentry); dput(rdentry);
out_drop_write:
fh_drop_write(fhp);
out_nfserr: out_nfserr:
err = nfserrno(host_err); err = nfserrno(host_err);
out: out:
......
...@@ -120,8 +120,11 @@ void nfsd_put_raparams(struct file *file, struct raparms *ra); ...@@ -120,8 +120,11 @@ void nfsd_put_raparams(struct file *file, struct raparms *ra);
static inline int fh_want_write(struct svc_fh *fh) static inline int fh_want_write(struct svc_fh *fh)
{ {
int ret = mnt_want_write(fh->fh_export->ex_path.mnt); int ret;
if (fh->fh_want_write)
return 0;
ret = mnt_want_write(fh->fh_export->ex_path.mnt);
if (!ret) if (!ret)
fh->fh_want_write = true; fh->fh_want_write = true;
return ret; return ret;
......
...@@ -76,7 +76,7 @@ struct nlmclnt_operations { ...@@ -76,7 +76,7 @@ struct nlmclnt_operations {
}; };
extern int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl, void *data); extern int nlmclnt_proc(struct nlm_host *host, int cmd, struct file_lock *fl, void *data);
extern int lockd_up(struct net *net); extern int lockd_up(struct net *net, const struct cred *cred);
extern void lockd_down(struct net *net); extern void lockd_down(struct net *net);
#endif /* LINUX_LOCKD_BIND_H */ #endif /* LINUX_LOCKD_BIND_H */
...@@ -271,6 +271,7 @@ struct svc_rqst { ...@@ -271,6 +271,7 @@ struct svc_rqst {
#define RQ_VICTIM (5) /* about to be shut down */ #define RQ_VICTIM (5) /* about to be shut down */
#define RQ_BUSY (6) /* request is busy */ #define RQ_BUSY (6) /* request is busy */
#define RQ_DATA (7) /* request has data */ #define RQ_DATA (7) /* request has data */
#define RQ_AUTHERR (8) /* Request status is auth error */
unsigned long rq_flags; /* flags field */ unsigned long rq_flags; /* flags field */
ktime_t rq_qtime; /* enqueue time */ ktime_t rq_qtime; /* enqueue time */
...@@ -382,6 +383,16 @@ struct svc_deferred_req { ...@@ -382,6 +383,16 @@ struct svc_deferred_req {
__be32 args[0]; __be32 args[0];
}; };
struct svc_process_info {
union {
int (*dispatch)(struct svc_rqst *, __be32 *);
struct {
unsigned int lovers;
unsigned int hivers;
} mismatch;
};
};
/* /*
* List of RPC programs on the same transport endpoint * List of RPC programs on the same transport endpoint
*/ */
...@@ -396,6 +407,14 @@ struct svc_program { ...@@ -396,6 +407,14 @@ struct svc_program {
char * pg_class; /* class name: services sharing authentication */ char * pg_class; /* class name: services sharing authentication */
struct svc_stat * pg_stats; /* rpc statistics */ struct svc_stat * pg_stats; /* rpc statistics */
int (*pg_authenticate)(struct svc_rqst *); int (*pg_authenticate)(struct svc_rqst *);
__be32 (*pg_init_request)(struct svc_rqst *,
const struct svc_program *,
struct svc_process_info *);
int (*pg_rpcbind_set)(struct net *net,
const struct svc_program *,
u32 version, int family,
unsigned short proto,
unsigned short port);
}; };
/* /*
...@@ -504,6 +523,20 @@ unsigned int svc_fill_write_vector(struct svc_rqst *rqstp, ...@@ -504,6 +523,20 @@ unsigned int svc_fill_write_vector(struct svc_rqst *rqstp,
char *svc_fill_symlink_pathname(struct svc_rqst *rqstp, char *svc_fill_symlink_pathname(struct svc_rqst *rqstp,
struct kvec *first, void *p, struct kvec *first, void *p,
size_t total); size_t total);
__be32 svc_return_autherr(struct svc_rqst *rqstp, __be32 auth_err);
__be32 svc_generic_init_request(struct svc_rqst *rqstp,
const struct svc_program *progp,
struct svc_process_info *procinfo);
int svc_generic_rpcbind_set(struct net *net,
const struct svc_program *progp,
u32 version, int family,
unsigned short proto,
unsigned short port);
int svc_rpcbind_set_version(struct net *net,
const struct svc_program *progp,
u32 version, int family,
unsigned short proto,
unsigned short port);
#define RPC_MAX_ADDRBUFLEN (63U) #define RPC_MAX_ADDRBUFLEN (63U)
......
...@@ -86,6 +86,7 @@ struct svc_xprt { ...@@ -86,6 +86,7 @@ struct svc_xprt {
struct list_head xpt_users; /* callbacks on free */ struct list_head xpt_users; /* callbacks on free */
struct net *xpt_net; struct net *xpt_net;
const struct cred *xpt_cred;
struct rpc_xprt *xpt_bc_xprt; /* NFSv4.1 backchannel */ struct rpc_xprt *xpt_bc_xprt; /* NFSv4.1 backchannel */
struct rpc_xprt_switch *xpt_bc_xps; /* NFSv4.1 backchannel */ struct rpc_xprt_switch *xpt_bc_xps; /* NFSv4.1 backchannel */
}; };
...@@ -119,7 +120,8 @@ void svc_unreg_xprt_class(struct svc_xprt_class *); ...@@ -119,7 +120,8 @@ void svc_unreg_xprt_class(struct svc_xprt_class *);
void svc_xprt_init(struct net *, struct svc_xprt_class *, struct svc_xprt *, void svc_xprt_init(struct net *, struct svc_xprt_class *, struct svc_xprt *,
struct svc_serv *); struct svc_serv *);
int svc_create_xprt(struct svc_serv *, const char *, struct net *, int svc_create_xprt(struct svc_serv *, const char *, struct net *,
const int, const unsigned short, int); const int, const unsigned short, int,
const struct cred *);
void svc_xprt_do_enqueue(struct svc_xprt *xprt); void svc_xprt_do_enqueue(struct svc_xprt *xprt);
void svc_xprt_enqueue(struct svc_xprt *xprt); void svc_xprt_enqueue(struct svc_xprt *xprt);
void svc_xprt_put(struct svc_xprt *xprt); void svc_xprt_put(struct svc_xprt *xprt);
......
...@@ -59,7 +59,8 @@ void svc_drop(struct svc_rqst *); ...@@ -59,7 +59,8 @@ void svc_drop(struct svc_rqst *);
void svc_sock_update_bufs(struct svc_serv *serv); void svc_sock_update_bufs(struct svc_serv *serv);
bool svc_alien_sock(struct net *net, int fd); bool svc_alien_sock(struct net *net, int fd);
int svc_addsock(struct svc_serv *serv, const int fd, int svc_addsock(struct svc_serv *serv, const int fd,
char *name_return, const size_t len); char *name_return, const size_t len,
const struct cred *cred);
void svc_init_xprt_sock(void); void svc_init_xprt_sock(void);
void svc_cleanup_xprt_sock(void); void svc_cleanup_xprt_sock(void);
struct svc_xprt *svc_sock_create(struct svc_serv *serv, int prot); struct svc_xprt *svc_sock_create(struct svc_serv *serv, int prot);
......
...@@ -36,6 +36,7 @@ enum cld_command { ...@@ -36,6 +36,7 @@ enum cld_command {
Cld_Remove, /* remove record of this cm_id */ Cld_Remove, /* remove record of this cm_id */
Cld_Check, /* is this cm_id allowed? */ Cld_Check, /* is this cm_id allowed? */
Cld_GraceDone, /* grace period is complete */ Cld_GraceDone, /* grace period is complete */
Cld_GraceStart,
}; };
/* representation of long-form NFSv4 client ID */ /* representation of long-form NFSv4 client ID */
......
...@@ -474,12 +474,12 @@ static int rsc_parse(struct cache_detail *cd, ...@@ -474,12 +474,12 @@ static int rsc_parse(struct cache_detail *cd,
* treatment so are checked for validity here.) * treatment so are checked for validity here.)
*/ */
/* uid */ /* uid */
rsci.cred.cr_uid = make_kuid(&init_user_ns, id); rsci.cred.cr_uid = make_kuid(current_user_ns(), id);
/* gid */ /* gid */
if (get_int(&mesg, &id)) if (get_int(&mesg, &id))
goto out; goto out;
rsci.cred.cr_gid = make_kgid(&init_user_ns, id); rsci.cred.cr_gid = make_kgid(current_user_ns(), id);
/* number of additional gid's */ /* number of additional gid's */
if (get_int(&mesg, &N)) if (get_int(&mesg, &N))
...@@ -497,7 +497,7 @@ static int rsc_parse(struct cache_detail *cd, ...@@ -497,7 +497,7 @@ static int rsc_parse(struct cache_detail *cd,
kgid_t kgid; kgid_t kgid;
if (get_int(&mesg, &id)) if (get_int(&mesg, &id))
goto out; goto out;
kgid = make_kgid(&init_user_ns, id); kgid = make_kgid(current_user_ns(), id);
if (!gid_valid(kgid)) if (!gid_valid(kgid))
goto out; goto out;
rsci.cred.cr_group_info->gid[i] = kgid; rsci.cred.cr_group_info->gid[i] = kgid;
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
static bool cache_defer_req(struct cache_req *req, struct cache_head *item); static bool cache_defer_req(struct cache_req *req, struct cache_head *item);
static void cache_revisit_request(struct cache_head *item); static void cache_revisit_request(struct cache_head *item);
static bool cache_listeners_exist(struct cache_detail *detail);
static void cache_init(struct cache_head *h, struct cache_detail *detail) static void cache_init(struct cache_head *h, struct cache_detail *detail)
{ {
...@@ -306,7 +307,8 @@ int cache_check(struct cache_detail *detail, ...@@ -306,7 +307,8 @@ int cache_check(struct cache_detail *detail,
cache_fresh_unlocked(h, detail); cache_fresh_unlocked(h, detail);
break; break;
} }
} } else if (!cache_listeners_exist(detail))
rv = try_to_negate_entry(detail, h);
} }
if (rv == -EAGAIN) { if (rv == -EAGAIN) {
......
This diff is collapsed.
...@@ -136,6 +136,7 @@ static void svc_xprt_free(struct kref *kref) ...@@ -136,6 +136,7 @@ static void svc_xprt_free(struct kref *kref)
struct module *owner = xprt->xpt_class->xcl_owner; struct module *owner = xprt->xpt_class->xcl_owner;
if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags))
svcauth_unix_info_release(xprt); svcauth_unix_info_release(xprt);
put_cred(xprt->xpt_cred);
put_net(xprt->xpt_net); put_net(xprt->xpt_net);
/* See comment on corresponding get in xs_setup_bc_tcp(): */ /* See comment on corresponding get in xs_setup_bc_tcp(): */
if (xprt->xpt_bc_xprt) if (xprt->xpt_bc_xprt)
...@@ -252,7 +253,8 @@ void svc_add_new_perm_xprt(struct svc_serv *serv, struct svc_xprt *new) ...@@ -252,7 +253,8 @@ void svc_add_new_perm_xprt(struct svc_serv *serv, struct svc_xprt *new)
static int _svc_create_xprt(struct svc_serv *serv, const char *xprt_name, static int _svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
struct net *net, const int family, struct net *net, const int family,
const unsigned short port, int flags) const unsigned short port, int flags,
const struct cred *cred)
{ {
struct svc_xprt_class *xcl; struct svc_xprt_class *xcl;
...@@ -273,6 +275,7 @@ static int _svc_create_xprt(struct svc_serv *serv, const char *xprt_name, ...@@ -273,6 +275,7 @@ static int _svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
module_put(xcl->xcl_owner); module_put(xcl->xcl_owner);
return PTR_ERR(newxprt); return PTR_ERR(newxprt);
} }
newxprt->xpt_cred = get_cred(cred);
svc_add_new_perm_xprt(serv, newxprt); svc_add_new_perm_xprt(serv, newxprt);
newport = svc_xprt_local_port(newxprt); newport = svc_xprt_local_port(newxprt);
return newport; return newport;
...@@ -286,15 +289,16 @@ static int _svc_create_xprt(struct svc_serv *serv, const char *xprt_name, ...@@ -286,15 +289,16 @@ static int _svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
int svc_create_xprt(struct svc_serv *serv, const char *xprt_name, int svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
struct net *net, const int family, struct net *net, const int family,
const unsigned short port, int flags) const unsigned short port, int flags,
const struct cred *cred)
{ {
int err; int err;
dprintk("svc: creating transport %s[%d]\n", xprt_name, port); dprintk("svc: creating transport %s[%d]\n", xprt_name, port);
err = _svc_create_xprt(serv, xprt_name, net, family, port, flags); err = _svc_create_xprt(serv, xprt_name, net, family, port, flags, cred);
if (err == -EPROTONOSUPPORT) { if (err == -EPROTONOSUPPORT) {
request_module("svc%s", xprt_name); request_module("svc%s", xprt_name);
err = _svc_create_xprt(serv, xprt_name, net, family, port, flags); err = _svc_create_xprt(serv, xprt_name, net, family, port, flags, cred);
} }
if (err < 0) if (err < 0)
dprintk("svc: transport %s not found, err %d\n", dprintk("svc: transport %s not found, err %d\n",
...@@ -782,9 +786,10 @@ static int svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt) ...@@ -782,9 +786,10 @@ static int svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt)
__module_get(xprt->xpt_class->xcl_owner); __module_get(xprt->xpt_class->xcl_owner);
svc_check_conn_limits(xprt->xpt_server); svc_check_conn_limits(xprt->xpt_server);
newxpt = xprt->xpt_ops->xpo_accept(xprt); newxpt = xprt->xpt_ops->xpo_accept(xprt);
if (newxpt) if (newxpt) {
newxpt->xpt_cred = get_cred(xprt->xpt_cred);
svc_add_new_temp_xprt(serv, newxpt); svc_add_new_temp_xprt(serv, newxpt);
else } else
module_put(xprt->xpt_class->xcl_owner); module_put(xprt->xpt_class->xcl_owner);
} else if (svc_xprt_reserve_slot(rqstp, xprt)) { } else if (svc_xprt_reserve_slot(rqstp, xprt)) {
/* XPT_DATA|XPT_DEFERRED case: */ /* XPT_DATA|XPT_DEFERRED case: */
......
...@@ -500,7 +500,7 @@ static int unix_gid_parse(struct cache_detail *cd, ...@@ -500,7 +500,7 @@ static int unix_gid_parse(struct cache_detail *cd,
rv = get_int(&mesg, &id); rv = get_int(&mesg, &id);
if (rv) if (rv)
return -EINVAL; return -EINVAL;
uid = make_kuid(&init_user_ns, id); uid = make_kuid(current_user_ns(), id);
ug.uid = uid; ug.uid = uid;
expiry = get_expiry(&mesg); expiry = get_expiry(&mesg);
...@@ -522,7 +522,7 @@ static int unix_gid_parse(struct cache_detail *cd, ...@@ -522,7 +522,7 @@ static int unix_gid_parse(struct cache_detail *cd,
err = -EINVAL; err = -EINVAL;
if (rv) if (rv)
goto out; goto out;
kgid = make_kgid(&init_user_ns, gid); kgid = make_kgid(current_user_ns(), gid);
if (!gid_valid(kgid)) if (!gid_valid(kgid))
goto out; goto out;
ug.gi->gid[i] = kgid; ug.gi->gid[i] = kgid;
...@@ -555,7 +555,7 @@ static int unix_gid_show(struct seq_file *m, ...@@ -555,7 +555,7 @@ static int unix_gid_show(struct seq_file *m,
struct cache_detail *cd, struct cache_detail *cd,
struct cache_head *h) struct cache_head *h)
{ {
struct user_namespace *user_ns = &init_user_ns; struct user_namespace *user_ns = m->file->f_cred->user_ns;
struct unix_gid *ug; struct unix_gid *ug;
int i; int i;
int glen; int glen;
...@@ -796,6 +796,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp) ...@@ -796,6 +796,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
struct kvec *argv = &rqstp->rq_arg.head[0]; struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec *resv = &rqstp->rq_res.head[0]; struct kvec *resv = &rqstp->rq_res.head[0];
struct svc_cred *cred = &rqstp->rq_cred; struct svc_cred *cred = &rqstp->rq_cred;
struct user_namespace *userns;
u32 slen, i; u32 slen, i;
int len = argv->iov_len; int len = argv->iov_len;
...@@ -816,8 +817,10 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp) ...@@ -816,8 +817,10 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
* (export-specific) anonymous id by nfsd_setuser. * (export-specific) anonymous id by nfsd_setuser.
* Supplementary gid's will be left alone. * Supplementary gid's will be left alone.
*/ */
cred->cr_uid = make_kuid(&init_user_ns, svc_getnl(argv)); /* uid */ userns = (rqstp->rq_xprt && rqstp->rq_xprt->xpt_cred) ?
cred->cr_gid = make_kgid(&init_user_ns, svc_getnl(argv)); /* gid */ rqstp->rq_xprt->xpt_cred->user_ns : &init_user_ns;
cred->cr_uid = make_kuid(userns, svc_getnl(argv)); /* uid */
cred->cr_gid = make_kgid(userns, svc_getnl(argv)); /* gid */
slen = svc_getnl(argv); /* gids length */ slen = svc_getnl(argv); /* gids length */
if (slen > UNX_NGROUPS || (len -= (slen + 2)*4) < 0) if (slen > UNX_NGROUPS || (len -= (slen + 2)*4) < 0)
goto badcred; goto badcred;
...@@ -825,7 +828,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp) ...@@ -825,7 +828,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
if (cred->cr_group_info == NULL) if (cred->cr_group_info == NULL)
return SVC_CLOSE; return SVC_CLOSE;
for (i = 0; i < slen; i++) { for (i = 0; i < slen; i++) {
kgid_t kgid = make_kgid(&init_user_ns, svc_getnl(argv)); kgid_t kgid = make_kgid(userns, svc_getnl(argv));
cred->cr_group_info->gid[i] = kgid; cred->cr_group_info->gid[i] = kgid;
} }
groups_sort(cred->cr_group_info); groups_sort(cred->cr_group_info);
......
...@@ -1332,13 +1332,14 @@ EXPORT_SYMBOL_GPL(svc_alien_sock); ...@@ -1332,13 +1332,14 @@ EXPORT_SYMBOL_GPL(svc_alien_sock);
* @fd: file descriptor of the new listener * @fd: file descriptor of the new listener
* @name_return: pointer to buffer to fill in with name of listener * @name_return: pointer to buffer to fill in with name of listener
* @len: size of the buffer * @len: size of the buffer
* @cred: credential
* *
* Fills in socket name and returns positive length of name if successful. * Fills in socket name and returns positive length of name if successful.
* Name is terminated with '\n'. On error, returns a negative errno * Name is terminated with '\n'. On error, returns a negative errno
* value. * value.
*/ */
int svc_addsock(struct svc_serv *serv, const int fd, char *name_return, int svc_addsock(struct svc_serv *serv, const int fd, char *name_return,
const size_t len) const size_t len, const struct cred *cred)
{ {
int err = 0; int err = 0;
struct socket *so = sockfd_lookup(fd, &err); struct socket *so = sockfd_lookup(fd, &err);
...@@ -1371,6 +1372,7 @@ int svc_addsock(struct svc_serv *serv, const int fd, char *name_return, ...@@ -1371,6 +1372,7 @@ int svc_addsock(struct svc_serv *serv, const int fd, char *name_return,
salen = kernel_getsockname(svsk->sk_sock, sin); salen = kernel_getsockname(svsk->sk_sock, sin);
if (salen >= 0) if (salen >= 0)
svc_xprt_set_local(&svsk->sk_xprt, sin, salen); svc_xprt_set_local(&svsk->sk_xprt, sin, salen);
svsk->sk_xprt.xpt_cred = get_cred(cred);
svc_add_new_perm_xprt(serv, &svsk->sk_xprt); svc_add_new_perm_xprt(serv, &svsk->sk_xprt);
return svc_one_sock_name(svsk, name_return, len); return svc_one_sock_name(svsk, name_return, len);
out: out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment