Commit 98219727 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-3.8' of git://linux-nfs.org/~bfields/linux

Pull nfsd update from Bruce Fields:
 "Included this time:

   - more nfsd containerization work from Stanislav Kinsbursky: we're
     not quite there yet, but should be by 3.9.

   - NFSv4.1 progress: implementation of basic backchannel security
     negotiation and the mandatory BACKCHANNEL_CTL operation.  See

       http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues

     for remaining TODO's

   - Fixes for some bugs that could be triggered by unusual compounds.
     Our xdr code wasn't designed with v4 compounds in mind, and it
     shows.  A more thorough rewrite is still a todo.

   - If you've ever seen "RPC: multiple fragments per record not
     supported" logged while using some sort of odd userland NFS client,
     that should now be fixed.

   - Further work from Jeff Layton on our mechanism for storing
     information about NFSv4 clients across reboots.

   - Further work from Bryan Schumaker on his fault-injection mechanism
     (which allows us to discard selective NFSv4 state, to excercise
     rarely-taken recovery code paths in the client.)

   - The usual mix of miscellaneous bugs and cleanup.

  Thanks to everyone who tested or contributed this cycle."

* 'for-3.8' of git://linux-nfs.org/~bfields/linux: (111 commits)
  nfsd4: don't leave freed stateid hashed
  nfsd4: free_stateid can use the current stateid
  nfsd4: cleanup: replace rq_resused count by rq_next_page pointer
  nfsd: warn on odd reply state in nfsd_vfs_read
  nfsd4: fix oops on unusual readlike compound
  nfsd4: disable zero-copy on non-final read ops
  svcrpc: fix some printks
  NFSD: Correct the size calculation in fault_inject_write
  NFSD: Pass correct buffer size to rpc_ntop
  nfsd: pass proper net to nfsd_destroy() from NFSd kthreads
  nfsd: simplify service shutdown
  nfsd: replace boolean nfsd_up flag by users counter
  nfsd: simplify NFSv4 state init and shutdown
  nfsd: introduce helpers for generic resources init and shutdown
  nfsd: make NFSd service structure allocated per net
  nfsd: make NFSd service boot time per-net
  nfsd: per-net NFSd up flag introduced
  nfsd: move per-net startup code to separated function
  nfsd: pass net to __write_ports() and down
  nfsd: pass net to nfsd_set_nrthreads()
  ...
parents 40889e8d 24ffb938
......@@ -39,21 +39,10 @@ interoperability problems with future clients. Known issues:
from a linux client are possible, but we aren't really
conformant with the spec (for example, we don't use kerberos
on the backchannel correctly).
- Incomplete backchannel support: incomplete backchannel gss
support and no support for BACKCHANNEL_CTL mean that
callbacks (hence delegations and layouts) may not be
available and clients confused by the incomplete
implementation may fail.
- We do not support SSV, which provides security for shared
client-server state (thus preventing unauthorized tampering
with locks and opens, for example). It is mandatory for
servers to support this, though no clients use it yet.
- Mandatory operations which we do not support, such as
DESTROY_CLIENTID, are not currently used by clients, but will be
(and the spec recommends their uses in common cases), and
clients should not be expected to know how to recover from the
case where they are not supported. This will eventually cause
interoperability failures.
In addition, some limitations are inherited from the current NFSv4
implementation:
......@@ -89,7 +78,7 @@ Operations
| | MNI | or OPT) | |
+----------------------+------------+--------------+----------------+
| ACCESS | REQ | | Section 18.1 |
NS | BACKCHANNEL_CTL | REQ | | Section 18.33 |
I | BACKCHANNEL_CTL | REQ | | Section 18.33 |
I | BIND_CONN_TO_SESSION | REQ | | Section 18.34 |
| CLOSE | REQ | | Section 18.2 |
| COMMIT | REQ | | Section 18.3 |
......@@ -99,7 +88,7 @@ NS*| DELEGPURGE | OPT | FDELG (REQ) | Section 18.5 |
| DELEGRETURN | OPT | FDELG, | Section 18.6 |
| | | DDELG, pNFS | |
| | | (REQ) | |
NS | DESTROY_CLIENTID | REQ | | Section 18.50 |
I | DESTROY_CLIENTID | REQ | | Section 18.50 |
I | DESTROY_SESSION | REQ | | Section 18.37 |
I | EXCHANGE_ID | REQ | | Section 18.35 |
I | FREE_STATEID | REQ | | Section 18.38 |
......@@ -192,7 +181,6 @@ EXCHANGE_ID:
CREATE_SESSION:
* backchannel attributes are ignored
* backchannel security parameters are ignored
SEQUENCE:
* no support for dynamic slot table renegotiation (optional)
......@@ -202,7 +190,7 @@ Nonstandard compound limitations:
ca_maxrequestsize request and a ca_maxresponsesize reply, so we may
fail to live up to the promise we made in CREATE_SESSION fore channel
negotiation.
* No more than one IO operation (read, write, readdir) allowed per
compound.
* No more than one read-like operation allowed per compound; encoding
replies that cross page boundaries (except for read data) not handled.
See also http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues.
......@@ -322,10 +322,10 @@ static int export_encode_fh(struct inode *inode, struct fid *fid,
if (parent && (len < 4)) {
*max_len = 4;
return 255;
return FILEID_INVALID;
} else if (len < 2) {
*max_len = 2;
return 255;
return FILEID_INVALID;
}
len = 2;
......
......@@ -52,7 +52,7 @@ static long do_sys_name_to_handle(struct path *path,
handle_bytes = handle_dwords * sizeof(u32);
handle->handle_bytes = handle_bytes;
if ((handle->handle_bytes > f_handle.handle_bytes) ||
(retval == 255) || (retval == -ENOSPC)) {
(retval == FILEID_INVALID) || (retval == -ENOSPC)) {
/* As per old exportfs_encode_fh documentation
* we could return ENOSPC to indicate overflow
* But file system returned 255 always. So handle
......
......@@ -8,61 +8,144 @@
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/nsproxy.h>
#include <linux/sunrpc/clnt.h>
#include <asm/uaccess.h>
#include "state.h"
#include "fault_inject.h"
#include "netns.h"
struct nfsd_fault_inject_op {
char *file;
void (*func)(u64);
u64 (*forget)(struct nfs4_client *, u64);
u64 (*print)(struct nfs4_client *, u64);
};
static struct nfsd_fault_inject_op inject_ops[] = {
{
.file = "forget_clients",
.func = nfsd_forget_clients,
.forget = nfsd_forget_client,
.print = nfsd_print_client,
},
{
.file = "forget_locks",
.func = nfsd_forget_locks,
.forget = nfsd_forget_client_locks,
.print = nfsd_print_client_locks,
},
{
.file = "forget_openowners",
.func = nfsd_forget_openowners,
.forget = nfsd_forget_client_openowners,
.print = nfsd_print_client_openowners,
},
{
.file = "forget_delegations",
.func = nfsd_forget_delegations,
.forget = nfsd_forget_client_delegations,
.print = nfsd_print_client_delegations,
},
{
.file = "recall_delegations",
.func = nfsd_recall_delegations,
.forget = nfsd_recall_client_delegations,
.print = nfsd_print_client_delegations,
},
};
static long int NUM_INJECT_OPS = sizeof(inject_ops) / sizeof(struct nfsd_fault_inject_op);
static struct dentry *debug_dir;
static int nfsd_inject_set(void *op_ptr, u64 val)
static void nfsd_inject_set(struct nfsd_fault_inject_op *op, u64 val)
{
struct nfsd_fault_inject_op *op = op_ptr;
u64 count = 0;
if (val == 0)
printk(KERN_INFO "NFSD Fault Injection: %s (all)", op->file);
else
printk(KERN_INFO "NFSD Fault Injection: %s (n = %llu)", op->file, val);
op->func(val);
return 0;
nfs4_lock_state();
count = nfsd_for_n_state(val, op->forget);
nfs4_unlock_state();
printk(KERN_INFO "NFSD: %s: found %llu", op->file, count);
}
static int nfsd_inject_get(void *data, u64 *val)
static void nfsd_inject_set_client(struct nfsd_fault_inject_op *op,
struct sockaddr_storage *addr,
size_t addr_size)
{
*val = 0;
return 0;
char buf[INET6_ADDRSTRLEN];
struct nfs4_client *clp;
u64 count;
nfs4_lock_state();
clp = nfsd_find_client(addr, addr_size);
if (clp) {
count = op->forget(clp, 0);
rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf));
printk(KERN_INFO "NFSD [%s]: Client %s had %llu state object(s)\n", op->file, buf, count);
}
nfs4_unlock_state();
}
static void nfsd_inject_get(struct nfsd_fault_inject_op *op, u64 *val)
{
nfs4_lock_state();
*val = nfsd_for_n_state(0, op->print);
nfs4_unlock_state();
}
DEFINE_SIMPLE_ATTRIBUTE(fops_nfsd, nfsd_inject_get, nfsd_inject_set, "%llu\n");
static ssize_t fault_inject_read(struct file *file, char __user *buf,
size_t len, loff_t *ppos)
{
static u64 val;
char read_buf[25];
size_t size, ret;
loff_t pos = *ppos;
if (!pos)
nfsd_inject_get(file->f_dentry->d_inode->i_private, &val);
size = scnprintf(read_buf, sizeof(read_buf), "%llu\n", val);
if (pos < 0)
return -EINVAL;
if (pos >= size || !len)
return 0;
if (len > size - pos)
len = size - pos;
ret = copy_to_user(buf, read_buf + pos, len);
if (ret == len)
return -EFAULT;
len -= ret;
*ppos = pos + len;
return len;
}
static ssize_t fault_inject_write(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
{
char write_buf[INET6_ADDRSTRLEN];
size_t size = min(sizeof(write_buf) - 1, len);
struct net *net = current->nsproxy->net_ns;
struct sockaddr_storage sa;
u64 val;
if (copy_from_user(write_buf, buf, size))
return -EFAULT;
write_buf[size] = '\0';
size = rpc_pton(net, write_buf, size, (struct sockaddr *)&sa, sizeof(sa));
if (size > 0)
nfsd_inject_set_client(file->f_dentry->d_inode->i_private, &sa, size);
else {
val = simple_strtoll(write_buf, NULL, 0);
nfsd_inject_set(file->f_dentry->d_inode->i_private, val);
}
return len; /* on success, claim we got the whole input */
}
static const struct file_operations fops_nfsd = {
.owner = THIS_MODULE,
.read = fault_inject_read,
.write = fault_inject_write,
};
void nfsd_fault_inject_cleanup(void)
{
......
/*
* Copyright (c) 2011 Bryan Schumaker <bjschuma@netapp.com>
*
* Function definitions for fault injection
*/
#ifndef LINUX_NFSD_FAULT_INJECT_H
#define LINUX_NFSD_FAULT_INJECT_H
#ifdef CONFIG_NFSD_FAULT_INJECTION
int nfsd_fault_inject_init(void);
void nfsd_fault_inject_cleanup(void);
void nfsd_forget_clients(u64);
void nfsd_forget_locks(u64);
void nfsd_forget_openowners(u64);
void nfsd_forget_delegations(u64);
void nfsd_recall_delegations(u64);
#else /* CONFIG_NFSD_FAULT_INJECTION */
static inline int nfsd_fault_inject_init(void) { return 0; }
static inline void nfsd_fault_inject_cleanup(void) {}
static inline void nfsd_forget_clients(u64 num) {}
static inline void nfsd_forget_locks(u64 num) {}
static inline void nfsd_forget_openowners(u64 num) {}
static inline void nfsd_forget_delegations(u64 num) {}
static inline void nfsd_recall_delegations(u64 num) {}
#endif /* CONFIG_NFSD_FAULT_INJECTION */
#endif /* LINUX_NFSD_FAULT_INJECT_H */
......@@ -24,7 +24,18 @@
#include <net/net_namespace.h>
#include <net/netns/generic.h>
/* Hash tables for nfs4_clientid state */
#define CLIENT_HASH_BITS 4
#define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS)
#define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1)
#define LOCKOWNER_INO_HASH_BITS 8
#define LOCKOWNER_INO_HASH_SIZE (1 << LOCKOWNER_INO_HASH_BITS)
#define SESSION_HASH_SIZE 512
struct cld_net;
struct nfsd4_client_tracking_ops;
struct nfsd_net {
struct cld_net *cld_net;
......@@ -38,7 +49,62 @@ struct nfsd_net {
struct lock_manager nfsd4_manager;
bool grace_ended;
time_t boot_time;
/*
* reclaim_str_hashtbl[] holds known client info from previous reset/reboot
* used in reboot/reset lease grace period processing
*
* conf_id_hashtbl[], and conf_name_tree hold confirmed
* setclientid_confirmed info.
*
* unconf_str_hastbl[] and unconf_name_tree hold unconfirmed
* setclientid info.
*/
struct list_head *reclaim_str_hashtbl;
int reclaim_str_hashtbl_size;
struct list_head *conf_id_hashtbl;
struct rb_root conf_name_tree;
struct list_head *unconf_id_hashtbl;
struct rb_root unconf_name_tree;
struct list_head *ownerstr_hashtbl;
struct list_head *lockowner_ino_hashtbl;
struct list_head *sessionid_hashtbl;
/*
* client_lru holds client queue ordered by nfs4_client.cl_time
* for lease renewal.
*
* close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time
* for last close replay.
*
* All of the above fields are protected by the client_mutex.
*/
struct list_head client_lru;
struct list_head close_lru;
struct delayed_work laundromat_work;
/* client_lock protects the client lru list and session hash table */
spinlock_t client_lock;
struct file *rec_file;
bool in_grace;
struct nfsd4_client_tracking_ops *client_tracking_ops;
time_t nfsd4_lease;
time_t nfsd4_grace;
bool nfsd_net_up;
/*
* Time of server startup
*/
struct timeval nfssvc_boot;
struct svc_serv *nfsd_serv;
};
/* Simple check to find out if a given net was properly initialized */
#define nfsd_netns_ready(nn) ((nn)->sessionid_hashtbl)
extern int nfsd_net_id;
#endif /* __NFSD_NETNS_H__ */
......@@ -253,7 +253,7 @@ static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p,
(resp->mask & NFS_ACL) ? resp->acl_access : NULL,
(resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
while (w > 0) {
if (!rqstp->rq_respages[rqstp->rq_resused++])
if (!*(rqstp->rq_next_page++))
return 0;
w -= PAGE_SIZE;
}
......
......@@ -184,7 +184,7 @@ static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p,
(resp->mask & NFS_ACL) ? resp->acl_access : NULL,
(resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
while (w > 0) {
if (!rqstp->rq_respages[rqstp->rq_resused++])
if (!*(rqstp->rq_next_page++))
return 0;
w -= PAGE_SIZE;
}
......
......@@ -460,7 +460,7 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
__be32 nfserr;
int count = 0;
loff_t offset;
int i;
struct page **p;
caddr_t page_addr = NULL;
dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n",
......@@ -484,8 +484,8 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp,
&resp->common,
nfs3svc_encode_entry_plus);
memcpy(resp->verf, argp->verf, 8);
for (i=1; i<rqstp->rq_resused ; i++) {
page_addr = page_address(rqstp->rq_respages[i]);
for (p = rqstp->rq_respages + 1; p < rqstp->rq_next_page; p++) {
page_addr = page_address(*p);
if (((caddr_t)resp->buffer >= page_addr) &&
((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) {
......
......@@ -7,8 +7,10 @@
*/
#include <linux/namei.h>
#include <linux/sunrpc/svc_xprt.h>
#include "xdr3.h"
#include "auth.h"
#include "netns.h"
#define NFSDDBG_FACILITY NFSDDBG_XDR
......@@ -323,7 +325,7 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_readargs *args)
{
unsigned int len;
int v,pn;
int v;
u32 max_blocksize = svc_max_payload(rqstp);
if (!(p = decode_fh(p, &args->fh)))
......@@ -338,8 +340,9 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p,
/* set up the kvec */
v=0;
while (len > 0) {
pn = rqstp->rq_resused++;
rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_respages[pn]);
struct page *p = *(rqstp->rq_next_page++);
rqstp->rq_vec[v].iov_base = page_address(p);
rqstp->rq_vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE;
len -= rqstp->rq_vec[v].iov_len;
v++;
......@@ -461,8 +464,7 @@ nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p,
len = ntohl(*p++);
if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE)
return 0;
args->tname = new =
page_address(rqstp->rq_respages[rqstp->rq_resused++]);
args->tname = new = page_address(*(rqstp->rq_next_page++));
args->tlen = len;
/* first copy and check from the first page */
old = (char*)p;
......@@ -533,8 +535,7 @@ nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p,
{
if (!(p = decode_fh(p, &args->fh)))
return 0;
args->buffer =
page_address(rqstp->rq_respages[rqstp->rq_resused++]);
args->buffer = page_address(*(rqstp->rq_next_page++));
return xdr_argsize_check(rqstp, p);
}
......@@ -565,8 +566,7 @@ nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p,
if (args->count > PAGE_SIZE)
args->count = PAGE_SIZE;
args->buffer =
page_address(rqstp->rq_respages[rqstp->rq_resused++]);
args->buffer = page_address(*(rqstp->rq_next_page++));
return xdr_argsize_check(rqstp, p);
}
......@@ -575,7 +575,7 @@ int
nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_readdirargs *args)
{
int len, pn;
int len;
u32 max_blocksize = svc_max_payload(rqstp);
if (!(p = decode_fh(p, &args->fh)))
......@@ -590,9 +590,9 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p,
args->count = len;
while (len > 0) {
pn = rqstp->rq_resused++;
struct page *p = *(rqstp->rq_next_page++);
if (!args->buffer)
args->buffer = page_address(rqstp->rq_respages[pn]);
args->buffer = page_address(p);
len -= PAGE_SIZE;
}
......@@ -720,12 +720,14 @@ int
nfs3svc_encode_writeres(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_writeres *resp)
{
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
p = encode_wcc_data(rqstp, p, &resp->fh);
if (resp->status == 0) {
*p++ = htonl(resp->count);
*p++ = htonl(resp->committed);
*p++ = htonl(nfssvc_boot.tv_sec);
*p++ = htonl(nfssvc_boot.tv_usec);
*p++ = htonl(nn->nfssvc_boot.tv_sec);
*p++ = htonl(nn->nfssvc_boot.tv_usec);
}
return xdr_ressize_check(rqstp, p);
}
......@@ -876,7 +878,7 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen,
common);
__be32 *p = cd->buffer;
caddr_t curr_page_addr = NULL;
int pn; /* current page number */
struct page ** page;
int slen; /* string (name) length */
int elen; /* estimated entry length in words */
int num_entry_words = 0; /* actual number of words */
......@@ -913,8 +915,9 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen,
}
/* determine which page in rq_respages[] we are currently filling */
for (pn=1; pn < cd->rqstp->rq_resused; pn++) {
curr_page_addr = page_address(cd->rqstp->rq_respages[pn]);
for (page = cd->rqstp->rq_respages + 1;
page < cd->rqstp->rq_next_page; page++) {
curr_page_addr = page_address(*page);
if (((caddr_t)cd->buffer >= curr_page_addr) &&
((caddr_t)cd->buffer < curr_page_addr + PAGE_SIZE))
......@@ -929,14 +932,14 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen,
if (plus)
p = encode_entryplus_baggage(cd, p, name, namlen);
num_entry_words = p - cd->buffer;
} else if (cd->rqstp->rq_respages[pn+1] != NULL) {
} else if (*(page+1) != NULL) {
/* temporarily encode entry into next page, then move back to
* current and next page in rq_respages[] */
__be32 *p1, *tmp;
int len1, len2;
/* grab next page for temporary storage of entry */
p1 = tmp = page_address(cd->rqstp->rq_respages[pn+1]);
p1 = tmp = page_address(*(page+1));
p1 = encode_entry_baggage(cd, p1, name, namlen, ino);
......@@ -1082,11 +1085,13 @@ int
nfs3svc_encode_commitres(struct svc_rqst *rqstp, __be32 *p,
struct nfsd3_commitres *resp)
{
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
p = encode_wcc_data(rqstp, p, &resp->fh);
/* Write verifier */
if (resp->status == 0) {
*p++ = htonl(nfssvc_boot.tv_sec);
*p++ = htonl(nfssvc_boot.tv_usec);
*p++ = htonl(nn->nfssvc_boot.tv_sec);
*p++ = htonl(nn->nfssvc_boot.tv_usec);
}
return xdr_ressize_check(rqstp, p);
}
......
......@@ -36,6 +36,7 @@
#include <linux/slab.h>
#include "nfsd.h"
#include "state.h"
#include "netns.h"
#define NFSDDBG_FACILITY NFSDDBG_PROC
......@@ -625,20 +626,46 @@ static const struct rpc_program cb_program = {
.pipe_dir_name = "nfsd4_cb",
};
static int max_cb_time(void)
static int max_cb_time(struct net *net)
{
return max(nfsd4_lease/10, (time_t)1) * HZ;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
return max(nn->nfsd4_lease/10, (time_t)1) * HZ;
}
static struct rpc_cred *callback_cred;
int set_callback_cred(void)
{
if (callback_cred)
return 0;
callback_cred = rpc_lookup_machine_cred("nfs");
if (!callback_cred)
return -ENOMEM;
return 0;
}
static struct rpc_cred *get_backchannel_cred(struct nfs4_client *clp, struct rpc_clnt *client, struct nfsd4_session *ses)
{
if (clp->cl_minorversion == 0) {
return get_rpccred(callback_cred);
} else {
struct rpc_auth *auth = client->cl_auth;
struct auth_cred acred = {};
acred.uid = ses->se_cb_sec.uid;
acred.gid = ses->se_cb_sec.gid;
return auth->au_ops->lookup_cred(client->cl_auth, &acred, 0);
}
}
static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses)
{
struct rpc_timeout timeparms = {
.to_initval = max_cb_time(),
.to_initval = max_cb_time(clp->net),
.to_retries = 0,
};
struct rpc_create_args args = {
.net = &init_net,
.net = clp->net,
.address = (struct sockaddr *) &conn->cb_addr,
.addrsize = conn->cb_addrlen,
.saddress = (struct sockaddr *) &conn->cb_saddr,
......@@ -648,6 +675,7 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
.flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
};
struct rpc_clnt *client;
struct rpc_cred *cred;
if (clp->cl_minorversion == 0) {
if (!clp->cl_cred.cr_principal &&
......@@ -666,7 +694,7 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
args.bc_xprt = conn->cb_xprt;
args.prognumber = clp->cl_cb_session->se_cb_prog;
args.protocol = XPRT_TRANSPORT_BC_TCP;
args.authflavor = RPC_AUTH_UNIX;
args.authflavor = ses->se_cb_sec.flavor;
}
/* Create RPC client */
client = rpc_create(&args);
......@@ -675,9 +703,14 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
PTR_ERR(client));
return PTR_ERR(client);
}
cred = get_backchannel_cred(clp, client, ses);
if (IS_ERR(cred)) {
rpc_shutdown_client(client);
return PTR_ERR(cred);
}
clp->cl_cb_client = client;
clp->cl_cb_cred = cred;
return 0;
}
static void warn_no_callback_path(struct nfs4_client *clp, int reason)
......@@ -714,18 +747,6 @@ static const struct rpc_call_ops nfsd4_cb_probe_ops = {
.rpc_call_done = nfsd4_cb_probe_done,
};
static struct rpc_cred *callback_cred;
int set_callback_cred(void)
{
if (callback_cred)
return 0;
callback_cred = rpc_lookup_machine_cred("nfs");
if (!callback_cred)
return -ENOMEM;
return 0;
}
static struct workqueue_struct *callback_wq;
static void run_nfsd4_cb(struct nfsd4_callback *cb)
......@@ -743,7 +764,6 @@ static void do_probe_callback(struct nfs4_client *clp)
cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL];
cb->cb_msg.rpc_argp = NULL;
cb->cb_msg.rpc_resp = NULL;
cb->cb_msg.rpc_cred = callback_cred;
cb->cb_ops = &nfsd4_cb_probe_ops;
......@@ -962,6 +982,8 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
if (clp->cl_cb_client) {
rpc_shutdown_client(clp->cl_cb_client);
clp->cl_cb_client = NULL;
put_rpccred(clp->cl_cb_cred);
clp->cl_cb_cred = NULL;
}
if (clp->cl_cb_conn.cb_xprt) {
svc_xprt_put(clp->cl_cb_conn.cb_xprt);
......@@ -995,7 +1017,7 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)
run_nfsd4_cb(cb);
}
void nfsd4_do_callback_rpc(struct work_struct *w)
static void nfsd4_do_callback_rpc(struct work_struct *w)
{
struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
struct nfs4_client *clp = cb->cb_clp;
......@@ -1010,10 +1032,16 @@ void nfsd4_do_callback_rpc(struct work_struct *w)
nfsd4_release_cb(cb);
return;
}
cb->cb_msg.rpc_cred = clp->cl_cb_cred;
rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
cb->cb_ops, cb);
}
void nfsd4_init_callback(struct nfsd4_callback *cb)
{
INIT_WORK(&cb->cb_work, nfsd4_do_callback_rpc);
}
void nfsd4_cb_recall(struct nfs4_delegation *dp)
{
struct nfsd4_callback *cb = &dp->dl_recall;
......@@ -1025,7 +1053,6 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp)
cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL];
cb->cb_msg.rpc_argp = cb;
cb->cb_msg.rpc_resp = cb;
cb->cb_msg.rpc_cred = callback_cred;
cb->cb_ops = &nfsd4_cb_recall_ops;
......
......@@ -40,6 +40,7 @@
#include "xdr4.h"
#include "vfs.h"
#include "current_stateid.h"
#include "netns.h"
#define NFSDDBG_FACILITY NFSDDBG_PROC
......@@ -194,6 +195,7 @@ static __be32
do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
struct svc_fh *resfh;
int accmode;
__be32 status;
resfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL);
......@@ -253,9 +255,10 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
/* set reply cache */
fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh,
&resfh->fh_handle);
if (!open->op_created)
status = do_open_permission(rqstp, resfh, open,
NFSD_MAY_NOP);
accmode = NFSD_MAY_NOP;
if (open->op_created)
accmode |= NFSD_MAY_OWNER_OVERRIDE;
status = do_open_permission(rqstp, resfh, open, accmode);
set_change_info(&open->op_cinfo, current_fh);
fh_dup2(current_fh, resfh);
out:
......@@ -304,6 +307,8 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
{
__be32 status;