NFS client updates for Linux 3.6

Features include:
 - More preparatory patches for modularising NFSv2/v3/v4.
   Split out the various NFSv2/v3/v4-specific code into separate
   files
 - More preparation for the NFSv4 migration code
 - Ensure that OPEN(O_CREATE) observes the pNFS mds threshold parameters
 - pNFS fast failover when the data servers are down
 - Various cleanups and debugging patches
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIcBAABAgAGBQJQFwYRAAoJEGcL54qWCgDy6YAQAIZuUPFCQbuzWev4L/XA0uhg
 0xjr9YBmg57UOs8e7FhS0azJip+D/kmF2Rx5gCMX0QO/IwaEVoms4BS8zjFOr3yL
 cPIyY5ErF+WJ25f3Mz8k0wOHTzrOvMdq4/7TQf5hztsrGYFid78sSb3O9ZO9bgb4
 QxqhgA0Z4S2vIqeObJAF9BT5UOT/cXfVKV0iTd52ZQVA+ZhqJ2SDlNFsoxnVOweV
 qzKOi0FZCPsjH+Z4a/LLdieHG5AYRPhOScBdG7gTFAdkysI8DDtSNCmJ68dMHYRw
 OHtyt/X4k9TImfwauV3Eww1sw3NkDswX+dv3GOSDTlMvVBrm0WXWj2zoHZbOB+0Q
 ETI9Zpolvd9pADtyfu9c+kCYIR+NdB4lGKd15iZfdEy/CZ0CtbLAbn5Szo2YcwNR
 iVjswR0jsbklD+mZjlMeZoG4JX2d9ZRqLs20POz7QQBmdIQ8jb9/SwVj9zGvX0w0
 NyVUHTFlGaNwkpo7oeRoWi1xjZWE6OzfoncV/46DOJWb08OKZ4fR4ugMYJWGi7Xx
 I4nQrIiHtInqL+sF5Y3wlZ48dufLuIhAcHh7klxgud4GRj7t8j+a99pTrRmpHvvC
 4K2Yu69VYcIA7N2RsSLohIllGPFmMwpdar7yERdD0So8/fz/zf7wn0dFFYY11+bL
 YNVVGhvNxccMU5EU9YR4
 =Lc59
 -----END PGP SIGNATURE-----

Merge tag 'nfs-for-3.6-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs

Pull NFS client updates from Trond Myklebust:
 "Features include:
   - More preparatory patches for modularising NFSv2/v3/v4.  Split out
     the various NFSv2/v3/v4-specific code into separate files
   - More preparation for the NFSv4 migration code
   - Ensure that OPEN(O_CREATE) observes the pNFS mds threshold
     parameters
   - pNFS fast failover when the data servers are down
   - Various cleanups and debugging patches"

* tag 'nfs-for-3.6-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (67 commits)
  nfs: fix fl_type tests in NFSv4 code
  NFS: fix pnfs regression with directio writes
  NFS: fix pnfs regression with directio reads
  sunrpc: clnt: Add missing braces
  nfs: fix stub return type warnings
  NFS: exit_nfs_v4() shouldn't be an __exit function
  SUNRPC: Add a missing spin_unlock to gss_mech_list_pseudoflavors
  NFS: Split out NFS v4 client functions
  NFS: Split out the NFS v4 filesystem types
  NFS: Create a single nfs_clone_super() function
  NFS: Split out NFS v4 server creating code
  NFS: Initialize the NFS v4 client from init_nfs_v4()
  NFS: Move the v4 getroot code to nfs4getroot.c
  NFS: Split out NFS v4 file operations
  NFS: Initialize v4 sysctls from nfs_init_v4()
  NFS: Create an init_nfs_v4() function
  NFS: Split out NFS v4 inode operations
  NFS: Split out NFS v3 inode operations
  NFS: Split out NFS v2 inode operations
  NFS: Clean up nfs4_proc_setclientid() and friends
  ...
This commit is contained in:
Linus Torvalds 2012-07-30 19:16:57 -07:00
commit 1fad1e9a74
45 changed files with 2220 additions and 1825 deletions

View File

@ -88,9 +88,8 @@ config NFS_V4
config NFS_V4_1 config NFS_V4_1
bool "NFS client support for NFSv4.1 (EXPERIMENTAL)" bool "NFS client support for NFSv4.1 (EXPERIMENTAL)"
depends on NFS_FS && NFS_V4 && EXPERIMENTAL depends on NFS_V4 && EXPERIMENTAL
select SUNRPC_BACKCHANNEL select SUNRPC_BACKCHANNEL
select PNFS_FILE_LAYOUT
help help
This option enables support for minor version 1 of the NFSv4 protocol This option enables support for minor version 1 of the NFSv4 protocol
(RFC 5661) in the kernel's NFS client. (RFC 5661) in the kernel's NFS client.
@ -99,15 +98,17 @@ config NFS_V4_1
config PNFS_FILE_LAYOUT config PNFS_FILE_LAYOUT
tristate tristate
depends on NFS_V4_1
default m
config PNFS_BLOCK config PNFS_BLOCK
tristate tristate
depends on NFS_FS && NFS_V4_1 && BLK_DEV_DM depends on NFS_V4_1 && BLK_DEV_DM
default m default m
config PNFS_OBJLAYOUT config PNFS_OBJLAYOUT
tristate tristate
depends on NFS_FS && NFS_V4_1 && SCSI_OSD_ULD depends on NFS_V4_1 && SCSI_OSD_ULD
default m default m
config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN

View File

@ -13,11 +13,16 @@ nfs-$(CONFIG_NFS_V2) += proc.o nfs2xdr.o
nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \
delegation.o idmap.o \ nfs4super.o nfs4file.o delegation.o idmap.o \
callback.o callback_xdr.o callback_proc.o \ callback.o callback_xdr.o callback_proc.o \
nfs4namespace.o nfs4namespace.o nfs4getroot.o nfs4client.o
nfs-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o nfs-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o
nfs-$(CONFIG_SYSCTL) += sysctl.o
ifeq ($(CONFIG_SYSCTL), y)
nfs-y += sysctl.o
nfs-$(CONFIG_NFS_V4) += nfs4sysctl.o
endif
nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o

View File

@ -56,35 +56,6 @@
#define NFSDBG_FACILITY NFSDBG_CLIENT #define NFSDBG_FACILITY NFSDBG_CLIENT
static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq); static DECLARE_WAIT_QUEUE_HEAD(nfs_client_active_wq);
#ifdef CONFIG_NFS_V4
/*
* Get a unique NFSv4.0 callback identifier which will be used
* by the V4.0 callback service to lookup the nfs_client struct
*/
static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
{
int ret = 0;
struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
if (clp->rpc_ops->version != 4 || minorversion != 0)
return ret;
retry:
if (!idr_pre_get(&nn->cb_ident_idr, GFP_KERNEL))
return -ENOMEM;
spin_lock(&nn->nfs_client_lock);
ret = idr_get_new(&nn->cb_ident_idr, clp, &clp->cl_cb_ident);
spin_unlock(&nn->nfs_client_lock);
if (ret == -EAGAIN)
goto retry;
return ret;
}
#endif /* CONFIG_NFS_V4 */
/*
* Turn off NFSv4 uid/gid mapping when using AUTH_SYS
*/
static bool nfs4_disable_idmapping = true;
/* /*
* RPC cruft for NFS * RPC cruft for NFS
@ -130,24 +101,13 @@ const struct rpc_program nfsacl_program = {
}; };
#endif /* CONFIG_NFS_V3_ACL */ #endif /* CONFIG_NFS_V3_ACL */
struct nfs_client_initdata {
unsigned long init_flags;
const char *hostname;
const struct sockaddr *addr;
size_t addrlen;
const struct nfs_rpc_ops *rpc_ops;
int proto;
u32 minorversion;
struct net *net;
};
/* /*
* Allocate a shared client record * Allocate a shared client record
* *
* Since these are allocated/deallocated very rarely, we don't * Since these are allocated/deallocated very rarely, we don't
* bother putting them in a slab cache... * bother putting them in a slab cache...
*/ */
static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init) struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
{ {
struct nfs_client *clp; struct nfs_client *clp;
struct rpc_cred *cred; struct rpc_cred *cred;
@ -177,18 +137,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
clp->cl_proto = cl_init->proto; clp->cl_proto = cl_init->proto;
clp->cl_net = get_net(cl_init->net); clp->cl_net = get_net(cl_init->net);
#ifdef CONFIG_NFS_V4
err = nfs_get_cb_ident_idr(clp, cl_init->minorversion);
if (err)
goto error_cleanup;
spin_lock_init(&clp->cl_lock);
INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
clp->cl_minorversion = cl_init->minorversion;
clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
#endif
cred = rpc_lookup_machine_cred("*"); cred = rpc_lookup_machine_cred("*");
if (!IS_ERR(cred)) if (!IS_ERR(cred))
clp->cl_machine_cred = cred; clp->cl_machine_cred = cred;
@ -203,45 +151,6 @@ error_0:
} }
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
#ifdef CONFIG_NFS_V4_1
static void nfs4_shutdown_session(struct nfs_client *clp)
{
if (nfs4_has_session(clp)) {
nfs4_destroy_session(clp->cl_session);
nfs4_destroy_clientid(clp);
}
}
#else /* CONFIG_NFS_V4_1 */
static void nfs4_shutdown_session(struct nfs_client *clp)
{
}
#endif /* CONFIG_NFS_V4_1 */
/*
* Destroy the NFS4 callback service
*/
static void nfs4_destroy_callback(struct nfs_client *clp)
{
if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
nfs_callback_down(clp->cl_mvops->minor_version);
}
static void nfs4_shutdown_client(struct nfs_client *clp)
{
if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
nfs4_kill_renewd(clp);
nfs4_shutdown_session(clp);
nfs4_destroy_callback(clp);
if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
nfs_idmap_delete(clp);
rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
kfree(clp->cl_serverowner);
kfree(clp->cl_serverscope);
kfree(clp->cl_implid);
}
/* idr_remove_all is not needed as all id's are removed by nfs_put_client */ /* idr_remove_all is not needed as all id's are removed by nfs_put_client */
void nfs_cleanup_cb_ident_idr(struct net *net) void nfs_cleanup_cb_ident_idr(struct net *net)
{ {
@ -264,16 +173,7 @@ static void pnfs_init_server(struct nfs_server *server)
rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC"); rpc_init_wait_queue(&server->roc_rpcwaitq, "pNFS ROC");
} }
static void nfs4_destroy_server(struct nfs_server *server)
{
nfs4_purge_state_owners(server);
}
#else #else
static void nfs4_shutdown_client(struct nfs_client *clp)
{
}
void nfs_cleanup_cb_ident_idr(struct net *net) void nfs_cleanup_cb_ident_idr(struct net *net)
{ {
} }
@ -291,12 +191,10 @@ static void pnfs_init_server(struct nfs_server *server)
/* /*
* Destroy a shared client record * Destroy a shared client record
*/ */
static void nfs_free_client(struct nfs_client *clp) void nfs_free_client(struct nfs_client *clp)
{ {
dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version); dprintk("--> nfs_free_client(%u)\n", clp->rpc_ops->version);
nfs4_shutdown_client(clp);
nfs_fscache_release_client_cookie(clp); nfs_fscache_release_client_cookie(clp);
/* -EIO all pending I/O */ /* -EIO all pending I/O */
@ -333,7 +231,7 @@ void nfs_put_client(struct nfs_client *clp)
BUG_ON(!list_empty(&clp->cl_superblocks)); BUG_ON(!list_empty(&clp->cl_superblocks));
nfs_free_client(clp); clp->rpc_ops->free_client(clp);
} }
} }
EXPORT_SYMBOL_GPL(nfs_put_client); EXPORT_SYMBOL_GPL(nfs_put_client);
@ -412,8 +310,8 @@ static int nfs_sockaddr_cmp_ip4(const struct sockaddr *sa1,
* Test if two socket addresses represent the same actual socket, * Test if two socket addresses represent the same actual socket,
* by comparing (only) relevant fields, excluding the port number. * by comparing (only) relevant fields, excluding the port number.
*/ */
static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
const struct sockaddr *sa2) const struct sockaddr *sa2)
{ {
if (sa1->sa_family != sa2->sa_family) if (sa1->sa_family != sa2->sa_family)
return 0; return 0;
@ -447,33 +345,6 @@ static int nfs_sockaddr_cmp(const struct sockaddr *sa1,
return 0; return 0;
} }
#if defined(CONFIG_NFS_V4_1)
/* Common match routine for v4.0 and v4.1 callback services */
static bool nfs4_cb_match_client(const struct sockaddr *addr,
struct nfs_client *clp, u32 minorversion)
{
struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
/* Don't match clients that failed to initialise */
if (!(clp->cl_cons_state == NFS_CS_READY ||
clp->cl_cons_state == NFS_CS_SESSION_INITING))
return false;
smp_rmb();
/* Match the version and minorversion */
if (clp->rpc_ops->version != 4 ||
clp->cl_minorversion != minorversion)
return false;
/* Match only the IP address, not the port number */
if (!nfs_sockaddr_match_ipaddr(addr, clap))
return false;
return true;
}
#endif /* CONFIG_NFS_V4_1 */
/* /*
* Find an nfs_client on the list that matches the initialisation data * Find an nfs_client on the list that matches the initialisation data
* that is supplied. * that is supplied.
@ -552,7 +423,7 @@ nfs_found_client(const struct nfs_client_initdata *cl_init,
* Look up a client by IP address and protocol version * Look up a client by IP address and protocol version
* - creates a new record if one doesn't yet exist * - creates a new record if one doesn't yet exist
*/ */
static struct nfs_client * struct nfs_client *
nfs_get_client(const struct nfs_client_initdata *cl_init, nfs_get_client(const struct nfs_client_initdata *cl_init,
const struct rpc_timeout *timeparms, const struct rpc_timeout *timeparms,
const char *ip_addr, const char *ip_addr,
@ -572,7 +443,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
if (clp) { if (clp) {
spin_unlock(&nn->nfs_client_lock); spin_unlock(&nn->nfs_client_lock);
if (new) if (new)
nfs_free_client(new); new->rpc_ops->free_client(new);
return nfs_found_client(cl_init, clp); return nfs_found_client(cl_init, clp);
} }
if (new) { if (new) {
@ -586,7 +457,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
spin_unlock(&nn->nfs_client_lock); spin_unlock(&nn->nfs_client_lock);
new = nfs_alloc_client(cl_init); new = cl_init->rpc_ops->alloc_client(cl_init);
} while (!IS_ERR(new)); } while (!IS_ERR(new));
dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n", dprintk("<-- nfs_get_client() Failed to find %s (%ld)\n",
@ -607,7 +478,7 @@ void nfs_mark_client_ready(struct nfs_client *clp, int state)
/* /*
* Initialise the timeout values for a connection * Initialise the timeout values for a connection
*/ */
static void nfs_init_timeout_values(struct rpc_timeout *to, int proto, void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
unsigned int timeo, unsigned int retrans) unsigned int timeo, unsigned int retrans)
{ {
to->to_initval = timeo * HZ / 10; to->to_initval = timeo * HZ / 10;
@ -648,9 +519,9 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
/* /*
* Create an RPC client handle * Create an RPC client handle
*/ */
static int nfs_create_rpc_client(struct nfs_client *clp, int nfs_create_rpc_client(struct nfs_client *clp,
const struct rpc_timeout *timeparms, const struct rpc_timeout *timeparms,
rpc_authflavor_t flavor) rpc_authflavor_t flavor)
{ {
struct rpc_clnt *clnt = NULL; struct rpc_clnt *clnt = NULL;
struct rpc_create_args args = { struct rpc_create_args args = {
@ -767,7 +638,7 @@ static inline void nfs_init_server_aclclient(struct nfs_server *server)
/* /*
* Create a general RPC client * Create a general RPC client
*/ */
static int nfs_init_server_rpcclient(struct nfs_server *server, int nfs_init_server_rpcclient(struct nfs_server *server,
const struct rpc_timeout *timeo, const struct rpc_timeout *timeo,
rpc_authflavor_t pseudoflavour) rpc_authflavor_t pseudoflavour)
{ {
@ -975,7 +846,6 @@ static void nfs_server_set_fsinfo(struct nfs_server *server,
server->wsize = NFS_MAX_FILE_IO_SIZE; server->wsize = NFS_MAX_FILE_IO_SIZE;
server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; server->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
server->pnfs_blksize = fsinfo->blksize; server->pnfs_blksize = fsinfo->blksize;
set_pnfs_layoutdriver(server, mntfh, fsinfo->layouttype);
server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL);
@ -1001,7 +871,7 @@ static void nfs_server_set_fsinfo(struct nfs_server *server,
/* /*
* Probe filesystem information, including the FSID on v2/v3 * Probe filesystem information, including the FSID on v2/v3
*/ */
static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr) int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs_fattr *fattr)
{ {
struct nfs_fsinfo fsinfo; struct nfs_fsinfo fsinfo;
struct nfs_client *clp = server->nfs_client; struct nfs_client *clp = server->nfs_client;
@ -1045,7 +915,7 @@ out_error:
/* /*
* Copy useful information when duplicating a server record * Copy useful information when duplicating a server record
*/ */
static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source) void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_server *source)
{ {
target->flags = source->flags; target->flags = source->flags;
target->rsize = source->rsize; target->rsize = source->rsize;
@ -1058,7 +928,7 @@ static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_serve
target->options = source->options; target->options = source->options;
} }
static void nfs_server_insert_lists(struct nfs_server *server) void nfs_server_insert_lists(struct nfs_server *server)
{ {
struct nfs_client *clp = server->nfs_client; struct nfs_client *clp = server->nfs_client;
struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
@ -1092,7 +962,7 @@ static void nfs_server_remove_lists(struct nfs_server *server)
/* /*
* Allocate and initialise a server record * Allocate and initialise a server record
*/ */
static struct nfs_server *nfs_alloc_server(void) struct nfs_server *nfs_alloc_server(void)
{ {
struct nfs_server *server; struct nfs_server *server;
@ -1138,7 +1008,6 @@ void nfs_free_server(struct nfs_server *server)
dprintk("--> nfs_free_server()\n"); dprintk("--> nfs_free_server()\n");
nfs_server_remove_lists(server); nfs_server_remove_lists(server);
unset_pnfs_layoutdriver(server);
if (server->destroy != NULL) if (server->destroy != NULL)
server->destroy(server); server->destroy(server);
@ -1226,522 +1095,6 @@ error:
return ERR_PTR(error); return ERR_PTR(error);
} }
#ifdef CONFIG_NFS_V4
/*
* NFSv4.0 callback thread helper
*
* Find a client by callback identifier
*/
struct nfs_client *
nfs4_find_client_ident(struct net *net, int cb_ident)
{
struct nfs_client *clp;
struct nfs_net *nn = net_generic(net, nfs_net_id);
spin_lock(&nn->nfs_client_lock);
clp = idr_find(&nn->cb_ident_idr, cb_ident);
if (clp)
atomic_inc(&clp->cl_count);
spin_unlock(&nn->nfs_client_lock);
return clp;
}
#if defined(CONFIG_NFS_V4_1)
/*
* NFSv4.1 callback thread helper
* For CB_COMPOUND calls, find a client by IP address, protocol version,
* minorversion, and sessionID
*
* Returns NULL if no such client
*/
struct nfs_client *
nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
struct nfs4_sessionid *sid)
{
struct nfs_client *clp;
struct nfs_net *nn = net_generic(net, nfs_net_id);
spin_lock(&nn->nfs_client_lock);
list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
if (nfs4_cb_match_client(addr, clp, 1) == false)
continue;
if (!nfs4_has_session(clp))
continue;
/* Match sessionid*/
if (memcmp(clp->cl_session->sess_id.data,
sid->data, NFS4_MAX_SESSIONID_LEN) != 0)
continue;
atomic_inc(&clp->cl_count);
spin_unlock(&nn->nfs_client_lock);
return clp;
}
spin_unlock(&nn->nfs_client_lock);
return NULL;
}
#else /* CONFIG_NFS_V4_1 */
struct nfs_client *
nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
struct nfs4_sessionid *sid)
{
return NULL;
}
#endif /* CONFIG_NFS_V4_1 */
/*
* Initialize the NFS4 callback service
*/
static int nfs4_init_callback(struct nfs_client *clp)
{
int error;
if (clp->rpc_ops->version == 4) {
struct rpc_xprt *xprt;
xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt);
if (nfs4_has_session(clp)) {
error = xprt_setup_backchannel(xprt,
NFS41_BC_MIN_CALLBACKS);
if (error < 0)
return error;
}
error = nfs_callback_up(clp->cl_mvops->minor_version, xprt);
if (error < 0) {
dprintk("%s: failed to start callback. Error = %d\n",
__func__, error);
return error;
}
__set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
}
return 0;
}
/*
* Initialize the minor version specific parts of an NFS4 client record
*/
static int nfs4_init_client_minor_version(struct nfs_client *clp)
{
#if defined(CONFIG_NFS_V4_1)
if (clp->cl_mvops->minor_version) {
struct nfs4_session *session = NULL;
/*
* Create the session and mark it expired.
* When a SEQUENCE operation encounters the expired session
* it will do session recovery to initialize it.
*/
session = nfs4_alloc_session(clp);
if (!session)
return -ENOMEM;
clp->cl_session = session;
/*
* The create session reply races with the server back
* channel probe. Mark the client NFS_CS_SESSION_INITING
* so that the client back channel can find the
* nfs_client struct
*/
nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
}
#endif /* CONFIG_NFS_V4_1 */
return nfs4_init_callback(clp);
}
/**
* nfs4_init_client - Initialise an NFS4 client record
*
* @clp: nfs_client to initialise
* @timeparms: timeout parameters for underlying RPC transport
* @ip_addr: callback IP address in presentation format
* @authflavor: authentication flavor for underlying RPC transport
*
* Returns pointer to an NFS client, or an ERR_PTR value.
*/
struct nfs_client *nfs4_init_client(struct nfs_client *clp,
const struct rpc_timeout *timeparms,
const char *ip_addr,
rpc_authflavor_t authflavour)
{
char buf[INET6_ADDRSTRLEN + 1];
int error;
if (clp->cl_cons_state == NFS_CS_READY) {
/* the client is initialised already */
dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp);
return clp;
}
/* Check NFS protocol revision and initialize RPC op vector */
clp->rpc_ops = &nfs_v4_clientops;
__set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
error = nfs_create_rpc_client(clp, timeparms, authflavour);
if (error < 0)
goto error;
/* If no clientaddr= option was specified, find a usable cb address */
if (ip_addr == NULL) {
struct sockaddr_storage cb_addr;
struct sockaddr *sap = (struct sockaddr *)&cb_addr;
error = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr));
if (error < 0)
goto error;
error = rpc_ntop(sap, buf, sizeof(buf));
if (error < 0)
goto error;
ip_addr = (const char *)buf;
}
strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
error = nfs_idmap_new(clp);
if (error < 0) {
dprintk("%s: failed to create idmapper. Error = %d\n",
__func__, error);
goto error;
}
__set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
error = nfs4_init_client_minor_version(clp);
if (error < 0)
goto error;
if (!nfs4_has_session(clp))
nfs_mark_client_ready(clp, NFS_CS_READY);
return clp;
error:
nfs_mark_client_ready(clp, error);
nfs_put_client(clp);
dprintk("<-- nfs4_init_client() = xerror %d\n", error);
return ERR_PTR(error);
}
/*
* Set up an NFS4 client
*/
static int nfs4_set_client(struct nfs_server *server,
const char *hostname,
const struct sockaddr *addr,
const size_t addrlen,
const char *ip_addr,
rpc_authflavor_t authflavour,
int proto, const struct rpc_timeout *timeparms,
u32 minorversion, struct net *net)
{
struct nfs_client_initdata cl_init = {
.hostname = hostname,
.addr = addr,
.addrlen = addrlen,
.rpc_ops = &nfs_v4_clientops,
.proto = proto,
.minorversion = minorversion,
.net = net,
};
struct nfs_client *clp;
int error;
dprintk("--> nfs4_set_client()\n");
if (server->flags & NFS_MOUNT_NORESVPORT)
set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
/* Allocate or find a client reference we can use */
clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour);
if (IS_ERR(clp)) {
error = PTR_ERR(clp);
goto error;
}
/*
* Query for the lease time on clientid setup or renewal
*
* Note that this will be set on nfs_clients that were created
* only for the DS role and did not set this bit, but now will
* serve a dual role.
*/
set_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state);
server->nfs_client = clp;
dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp);
return 0;
error:
dprintk("<-- nfs4_set_client() = xerror %d\n", error);
return error;
}
/*
* Set up a pNFS Data Server client.
*
* Return any existing nfs_client that matches server address,port,version
* and minorversion.
*
* For a new nfs_client, use a soft mount (default), a low retrans and a
* low timeout interval so that if a connection is lost, we retry through
* the MDS.
*/
struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
const struct sockaddr *ds_addr, int ds_addrlen,
int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans)
{
struct nfs_client_initdata cl_init = {
.addr = ds_addr,
.addrlen = ds_addrlen,
.rpc_ops = &nfs_v4_clientops,
.proto = ds_proto,
.minorversion = mds_clp->cl_minorversion,
.net = mds_clp->cl_net,
};
struct rpc_timeout ds_timeout;
struct nfs_client *clp;
/*
* Set an authflavor equual to the MDS value. Use the MDS nfs_client
* cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS
* (section 13.1 RFC 5661).
*/
nfs_init_timeout_values(&ds_timeout, ds_proto, ds_timeo, ds_retrans);
clp = nfs_get_client(&cl_init, &ds_timeout, mds_clp->cl_ipaddr,
mds_clp->cl_rpcclient->cl_auth->au_flavor);
dprintk("<-- %s %p\n", __func__, clp);
return clp;
}
EXPORT_SYMBOL_GPL(nfs4_set_ds_client);
/*
* Session has been established, and the client marked ready.
* Set the mount rsize and wsize with negotiated fore channel
* attributes which will be bound checked in nfs_server_set_fsinfo.
*/
static void nfs4_session_set_rwsize(struct nfs_server *server)
{
#ifdef CONFIG_NFS_V4_1
struct nfs4_session *sess;
u32 server_resp_sz;
u32 server_rqst_sz;
if (!nfs4_has_session(server->nfs_client))
return;
sess = server->nfs_client->cl_session;
server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead;
server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead;
if (server->rsize > server_resp_sz)
server->rsize = server_resp_sz;
if (server->wsize > server_rqst_sz)
server->wsize = server_rqst_sz;
#endif /* CONFIG_NFS_V4_1 */
}
static int nfs4_server_common_setup(struct nfs_server *server,
struct nfs_fh *mntfh)
{
struct nfs_fattr *fattr;
int error;
BUG_ON(!server->nfs_client);
BUG_ON(!server->nfs_client->rpc_ops);
BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
/* data servers support only a subset of NFSv4.1 */
if (is_ds_only_client(server->nfs_client))
return -EPROTONOSUPPORT;
fattr = nfs_alloc_fattr();
if (fattr == NULL)
return -ENOMEM;
/* We must ensure the session is initialised first */
error = nfs4_init_session(server);
if (error < 0)
goto out;
/* Probe the root fh to retrieve its FSID and filehandle */
error = nfs4_get_rootfh(server, mntfh);
if (error < 0)
goto out;
dprintk("Server FSID: %llx:%llx\n",
(unsigned long long) server->fsid.major,
(unsigned long long) server->fsid.minor);
dprintk("Mount FH: %d\n", mntfh->size);
nfs4_session_set_rwsize(server);
error = nfs_probe_fsinfo(server, mntfh, fattr);
if (error < 0)
goto out;
if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
server->namelen = NFS4_MAXNAMLEN;
nfs_server_insert_lists(server);
server->mount_time = jiffies;
server->destroy = nfs4_destroy_server;
out:
nfs_free_fattr(fattr);
return error;
}
/*
* Create a version 4 volume record
*/
static int nfs4_init_server(struct nfs_server *server,
const struct nfs_parsed_mount_data *data)
{
struct rpc_timeout timeparms;
int error;
dprintk("--> nfs4_init_server()\n");
nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
data->timeo, data->retrans);
/* Initialise the client representation from the mount data */
server->flags = data->flags;
server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK;
if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
server->caps |= NFS_CAP_READDIRPLUS;
server->options = data->options;
/* Get a client record */
error = nfs4_set_client(server,
data->nfs_server.hostname,
(const struct sockaddr *)&data->nfs_server.address,
data->nfs_server.addrlen,
data->client_address,
data->auth_flavors[0],
data->nfs_server.protocol,
&timeparms,
data->minorversion,
data->net);
if (error < 0)
goto error;
/*
* Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower
* authentication.
*/
if (nfs4_disable_idmapping && data->auth_flavors[0] == RPC_AUTH_UNIX)
server->caps |= NFS_CAP_UIDGID_NOMAP;
if (data->rsize)
server->rsize = nfs_block_size(data->rsize, NULL);
if (data->wsize)
server->wsize = nfs_block_size(data->wsize, NULL);
server->acregmin = data->acregmin * HZ;
server->acregmax = data->acregmax * HZ;
server->acdirmin = data->acdirmin * HZ;
server->acdirmax = data->acdirmax * HZ;
server->port = data->nfs_server.port;
error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]);
error:
/* Done */
dprintk("<-- nfs4_init_server() = %d\n", error);
return error;
}
/*
* Create a version 4 volume record
* - keyed on server and FSID
*/
struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
struct nfs_fh *mntfh)
{
struct nfs_server *server;
int error;
dprintk("--> nfs4_create_server()\n");
server = nfs_alloc_server();
if (!server)
return ERR_PTR(-ENOMEM);
/* set up the general RPC client */
error = nfs4_init_server(server, data);
if (error < 0)
goto error;
error = nfs4_server_common_setup(server, mntfh);
if (error < 0)
goto error;
dprintk("<-- nfs4_create_server() = %p\n", server);
return server;
error:
nfs_free_server(server);
dprintk("<-- nfs4_create_server() = error %d\n", error);
return ERR_PTR(error);
}
/*
* Create an NFS4 referral server record
*/
struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
struct nfs_fh *mntfh)
{
struct nfs_client *parent_client;
struct nfs_server *server, *parent_server;
int error;
dprintk("--> nfs4_create_referral_server()\n");
server = nfs_alloc_server();
if (!server)
return ERR_PTR(-ENOMEM);
parent_server = NFS_SB(data->sb);
parent_client = parent_server->nfs_client;
/* Initialise the client representation from the parent server */
nfs_server_copy_userdata(server, parent_server);
server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR;
/* Get a client representation.
* Note: NFSv4 always uses TCP, */
error = nfs4_set_client(server, data->hostname,
data->addr,
data->addrlen,
parent_client->cl_ipaddr,
data->authflavor,
rpc_protocol(parent_server->client),
parent_server->client->cl_timeout,
parent_client->cl_mvops->minor_version,
parent_client->cl_net);
if (error < 0)
goto error;
error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor);
if (error < 0)
goto error;
error = nfs4_server_common_setup(server, mntfh);
if (error < 0)
goto error;
dprintk("<-- nfs_create_referral_server() = %p\n", server);
return server;
error:
nfs_free_server(server);
dprintk("<-- nfs4_create_referral_server() = error %d\n", error);
return ERR_PTR(error);
}
#endif /* CONFIG_NFS_V4 */
/* /*
* Clone an NFS2, NFS3 or NFS4 server record * Clone an NFS2, NFS3 or NFS4 server record
*/ */
@ -2091,7 +1444,3 @@ void nfs_fs_proc_exit(void)
} }
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
module_param(nfs4_disable_idmapping, bool, 0644);
MODULE_PARM_DESC(nfs4_disable_idmapping,
"Turn off NFSv4 idmapping when using 'sec=sys'");

View File

@ -47,7 +47,7 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
* *
* Returns one if inode has the indicated delegation, otherwise zero. * Returns one if inode has the indicated delegation, otherwise zero.
*/ */
int nfs_have_delegation(struct inode *inode, fmode_t flags) int nfs4_have_delegation(struct inode *inode, fmode_t flags)
{ {
struct nfs_delegation *delegation; struct nfs_delegation *delegation;
int ret = 0; int ret = 0;
@ -388,7 +388,7 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode)
* *
* Returns zero on success, or a negative errno value. * Returns zero on success, or a negative errno value.
*/ */
int nfs_inode_return_delegation(struct inode *inode) int nfs4_inode_return_delegation(struct inode *inode)
{ {
struct nfs_server *server = NFS_SERVER(inode); struct nfs_server *server = NFS_SERVER(inode);
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
@ -417,9 +417,8 @@ static void nfs_mark_return_delegation(struct nfs_server *server,
* @sb: sb to process * @sb: sb to process
* *
*/ */
void nfs_super_return_all_delegations(struct super_block *sb) void nfs_server_return_all_delegations(struct nfs_server *server)
{ {
struct nfs_server *server = NFS_SB(sb);
struct nfs_client *clp = server->nfs_client; struct nfs_client *clp = server->nfs_client;
struct nfs_delegation *delegation; struct nfs_delegation *delegation;

View File

@ -33,12 +33,12 @@ enum {
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
int nfs_inode_return_delegation(struct inode *inode); int nfs4_inode_return_delegation(struct inode *inode);
int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid);
void nfs_inode_return_delegation_noreclaim(struct inode *inode); void nfs_inode_return_delegation_noreclaim(struct inode *inode);
struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle); struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle);
void nfs_super_return_all_delegations(struct super_block *sb); void nfs_server_return_all_delegations(struct nfs_server *);
void nfs_expire_all_delegations(struct nfs_client *clp); void nfs_expire_all_delegations(struct nfs_client *clp);
void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags); void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags);
void nfs_expire_unreferenced_delegations(struct nfs_client *clp); void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
@ -56,24 +56,13 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl);
bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, fmode_t flags); bool nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode, fmode_t flags);
void nfs_mark_delegation_referenced(struct nfs_delegation *delegation); void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
int nfs_have_delegation(struct inode *inode, fmode_t flags); int nfs4_have_delegation(struct inode *inode, fmode_t flags);
#else
static inline int nfs_have_delegation(struct inode *inode, fmode_t flags)
{
return 0;
}
static inline int nfs_inode_return_delegation(struct inode *inode)
{
nfs_wb_all(inode);
return 0;
}
#endif #endif
static inline int nfs_have_delegated_attributes(struct inode *inode) static inline int nfs_have_delegated_attributes(struct inode *inode)
{ {
return nfs_have_delegation(inode, FMODE_READ) && return NFS_PROTO(inode)->have_delegation(inode, FMODE_READ) &&
!(NFS_I(inode)->cache_validity & NFS_INO_REVAL_FORCED); !(NFS_I(inode)->cache_validity & NFS_INO_REVAL_FORCED);
} }

View File

@ -46,16 +46,6 @@
static int nfs_opendir(struct inode *, struct file *); static int nfs_opendir(struct inode *, struct file *);
static int nfs_closedir(struct inode *, struct file *); static int nfs_closedir(struct inode *, struct file *);
static int nfs_readdir(struct file *, void *, filldir_t); static int nfs_readdir(struct file *, void *, filldir_t);
static struct dentry *nfs_lookup(struct inode *, struct dentry *, unsigned int);
static int nfs_create(struct inode *, struct dentry *, umode_t, bool);
static int nfs_mkdir(struct inode *, struct dentry *, umode_t);
static int nfs_rmdir(struct inode *, struct dentry *);
static int nfs_unlink(struct inode *, struct dentry *);
static int nfs_symlink(struct inode *, struct dentry *, const char *);
static int nfs_link(struct dentry *, struct inode *, struct dentry *);
static int nfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
static int nfs_rename(struct inode *, struct dentry *,
struct inode *, struct dentry *);
static int nfs_fsync_dir(struct file *, loff_t, loff_t, int); static int nfs_fsync_dir(struct file *, loff_t, loff_t, int);
static loff_t nfs_llseek_dir(struct file *, loff_t, int); static loff_t nfs_llseek_dir(struct file *, loff_t, int);
static void nfs_readdir_clear_array(struct page*); static void nfs_readdir_clear_array(struct page*);
@ -69,73 +59,10 @@ const struct file_operations nfs_dir_operations = {
.fsync = nfs_fsync_dir, .fsync = nfs_fsync_dir,
}; };
const struct inode_operations nfs_dir_inode_operations = {
.create = nfs_create,
.lookup = nfs_lookup,
.link = nfs_link,
.unlink = nfs_unlink,
.symlink = nfs_symlink,
.mkdir = nfs_mkdir,
.rmdir = nfs_rmdir,
.mknod = nfs_mknod,
.rename = nfs_rename,
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
};
const struct address_space_operations nfs_dir_aops = { const struct address_space_operations nfs_dir_aops = {
.freepage = nfs_readdir_clear_array, .freepage = nfs_readdir_clear_array,
}; };
#ifdef CONFIG_NFS_V3
const struct inode_operations nfs3_dir_inode_operations = {
.create = nfs_create,
.lookup = nfs_lookup,
.link = nfs_link,
.unlink = nfs_unlink,
.symlink = nfs_symlink,
.mkdir = nfs_mkdir,
.rmdir = nfs_rmdir,
.mknod = nfs_mknod,
.rename = nfs_rename,
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
.listxattr = nfs3_listxattr,
.getxattr = nfs3_getxattr,
.setxattr = nfs3_setxattr,
.removexattr = nfs3_removexattr,
};
#endif /* CONFIG_NFS_V3 */
#ifdef CONFIG_NFS_V4
static int nfs_atomic_open(struct inode *, struct dentry *,
struct file *, unsigned, umode_t,
int *);
const struct inode_operations nfs4_dir_inode_operations = {
.create = nfs_create,
.lookup = nfs_lookup,
.atomic_open = nfs_atomic_open,
.link = nfs_link,
.unlink = nfs_unlink,
.symlink = nfs_symlink,
.mkdir = nfs_mkdir,
.rmdir = nfs_rmdir,
.mknod = nfs_mknod,
.rename = nfs_rename,
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
.getxattr = generic_getxattr,
.setxattr = generic_setxattr,
.listxattr = generic_listxattr,
.removexattr = generic_removexattr,
};
#endif /* CONFIG_NFS_V4 */
static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred) static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred)
{ {
struct nfs_open_dir_context *ctx; struct nfs_open_dir_context *ctx;
@ -1128,7 +1055,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
goto out_bad; goto out_bad;
} }
if (nfs_have_delegation(inode, FMODE_READ)) if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ))
goto out_set_verifier; goto out_set_verifier;
/* Force a full look up iff the parent directory has changed */ /* Force a full look up iff the parent directory has changed */
@ -1270,7 +1197,7 @@ const struct dentry_operations nfs_dentry_operations = {
.d_release = nfs_d_release, .d_release = nfs_d_release,
}; };
static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags) struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned int flags)
{ {
struct dentry *res; struct dentry *res;
struct dentry *parent; struct dentry *parent;
@ -1398,9 +1325,9 @@ out:
return err; return err;
} }
static int nfs_atomic_open(struct inode *dir, struct dentry *dentry, int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
struct file *file, unsigned open_flags, struct file *file, unsigned open_flags,
umode_t mode, int *opened) umode_t mode, int *opened)
{ {
struct nfs_open_context *ctx; struct nfs_open_context *ctx;
struct dentry *res; struct dentry *res;
@ -1588,7 +1515,7 @@ out_error:
* that the operation succeeded on the server, but an error in the * that the operation succeeded on the server, but an error in the
* reply path made it appear to have failed. * reply path made it appear to have failed.
*/ */
static int nfs_create(struct inode *dir, struct dentry *dentry, int nfs_create(struct inode *dir, struct dentry *dentry,
umode_t mode, bool excl) umode_t mode, bool excl)
{ {
struct iattr attr; struct iattr attr;
@ -1613,7 +1540,7 @@ out_err:
/* /*
* See comments for nfs_proc_create regarding failed operations. * See comments for nfs_proc_create regarding failed operations.
*/ */
static int int
nfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev) nfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev)
{ {
struct iattr attr; struct iattr attr;
@ -1640,7 +1567,7 @@ out_err:
/* /*
* See comments for nfs_proc_create regarding failed operations. * See comments for nfs_proc_create regarding failed operations.
*/ */
static int nfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) int nfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{ {
struct iattr attr; struct iattr attr;
int error; int error;
@ -1666,7 +1593,7 @@ static void nfs_dentry_handle_enoent(struct dentry *dentry)
d_delete(dentry); d_delete(dentry);
} }
static int nfs_rmdir(struct inode *dir, struct dentry *dentry) int nfs_rmdir(struct inode *dir, struct dentry *dentry)
{ {
int error; int error;
@ -1706,7 +1633,7 @@ static int nfs_safe_remove(struct dentry *dentry)
} }
if (inode != NULL) { if (inode != NULL) {
nfs_inode_return_delegation(inode); NFS_PROTO(inode)->return_delegation(inode);
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
/* The VFS may want to delete this inode */ /* The VFS may want to delete this inode */
if (error == 0) if (error == 0)
@ -1725,7 +1652,7 @@ out:
* *
* If sillyrename() returns 0, we do nothing, otherwise we unlink. * If sillyrename() returns 0, we do nothing, otherwise we unlink.
*/ */
static int nfs_unlink(struct inode *dir, struct dentry *dentry) int nfs_unlink(struct inode *dir, struct dentry *dentry)
{ {
int error; int error;
int need_rehash = 0; int need_rehash = 0;
@ -1769,7 +1696,7 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
* now have a new file handle and can instantiate an in-core NFS inode * now have a new file handle and can instantiate an in-core NFS inode
* and move the raw page into its mapping. * and move the raw page into its mapping.
*/ */
static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{ {
struct pagevec lru_pvec; struct pagevec lru_pvec;
struct page *page; struct page *page;
@ -1824,7 +1751,7 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
return 0; return 0;
} }
static int int
nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
{ {
struct inode *inode = old_dentry->d_inode; struct inode *inode = old_dentry->d_inode;
@ -1834,7 +1761,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
old_dentry->d_parent->d_name.name, old_dentry->d_name.name, old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
dentry->d_parent->d_name.name, dentry->d_name.name); dentry->d_parent->d_name.name, dentry->d_name.name);
nfs_inode_return_delegation(inode); NFS_PROTO(inode)->return_delegation(inode);
d_drop(dentry); d_drop(dentry);
error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name); error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
@ -1869,7 +1796,7 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
* If these conditions are met, we can drop the dentries before doing * If these conditions are met, we can drop the dentries before doing
* the rename. * the rename.
*/ */
static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry) struct inode *new_dir, struct dentry *new_dentry)
{ {
struct inode *old_inode = old_dentry->d_inode; struct inode *old_inode = old_dentry->d_inode;
@ -1918,9 +1845,9 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
} }
} }
nfs_inode_return_delegation(old_inode); NFS_PROTO(old_inode)->return_delegation(old_inode);
if (new_inode != NULL) if (new_inode != NULL)
nfs_inode_return_delegation(new_inode); NFS_PROTO(new_inode)->return_delegation(new_inode);
error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name, error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
new_dir, &new_dentry->d_name); new_dir, &new_dentry->d_name);

View File

@ -393,7 +393,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
size_t requested_bytes = 0; size_t requested_bytes = 0;
unsigned long seg; unsigned long seg;
nfs_pageio_init_read(&desc, dreq->inode, NFS_PROTO(dreq->inode)->read_pageio_init(&desc, dreq->inode,
&nfs_direct_read_completion_ops); &nfs_direct_read_completion_ops);
get_dreq(dreq); get_dreq(dreq);
desc.pg_dreq = dreq; desc.pg_dreq = dreq;
@ -478,7 +478,7 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
dreq->count = 0; dreq->count = 0;
get_dreq(dreq); get_dreq(dreq);
nfs_pageio_init_write(&desc, dreq->inode, FLUSH_STABLE, NFS_PROTO(dreq->inode)->write_pageio_init(&desc, dreq->inode, FLUSH_STABLE,
&nfs_direct_write_completion_ops); &nfs_direct_write_completion_ops);
desc.pg_dreq = dreq; desc.pg_dreq = dreq;
@ -782,7 +782,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
size_t requested_bytes = 0; size_t requested_bytes = 0;
unsigned long seg; unsigned long seg;
nfs_pageio_init_write(&desc, inode, FLUSH_COND_STABLE, NFS_PROTO(inode)->write_pageio_init(&desc, inode, FLUSH_COND_STABLE,
&nfs_direct_write_completion_ops); &nfs_direct_write_completion_ops);
desc.pg_dreq = dreq; desc.pg_dreq = dreq;
get_dreq(dreq); get_dreq(dreq);

View File

@ -35,36 +35,17 @@
#include "internal.h" #include "internal.h"
#include "iostat.h" #include "iostat.h"
#include "fscache.h" #include "fscache.h"
#include "pnfs.h"
#define NFSDBG_FACILITY NFSDBG_FILE #define NFSDBG_FACILITY NFSDBG_FILE
static const struct vm_operations_struct nfs_file_vm_ops; static const struct vm_operations_struct nfs_file_vm_ops;
const struct inode_operations nfs_file_inode_operations = {
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
};
#ifdef CONFIG_NFS_V3
const struct inode_operations nfs3_file_inode_operations = {
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
.listxattr = nfs3_listxattr,
.getxattr = nfs3_getxattr,
.setxattr = nfs3_setxattr,
.removexattr = nfs3_removexattr,
};
#endif /* CONFIG_NFS_v3 */
/* Hack for future NFS swap support */ /* Hack for future NFS swap support */
#ifndef IS_SWAPFILE #ifndef IS_SWAPFILE
# define IS_SWAPFILE(inode) (0) # define IS_SWAPFILE(inode) (0)
#endif #endif
static int nfs_check_flags(int flags) int nfs_check_flags(int flags)
{ {
if ((flags & (O_APPEND | O_DIRECT)) == (O_APPEND | O_DIRECT)) if ((flags & (O_APPEND | O_DIRECT)) == (O_APPEND | O_DIRECT))
return -EINVAL; return -EINVAL;
@ -93,7 +74,7 @@ nfs_file_open(struct inode *inode, struct file *filp)
return res; return res;
} }
static int int
nfs_file_release(struct inode *inode, struct file *filp) nfs_file_release(struct inode *inode, struct file *filp)
{ {
dprintk("NFS: release(%s/%s)\n", dprintk("NFS: release(%s/%s)\n",
@ -135,7 +116,7 @@ force_reval:
return __nfs_revalidate_inode(server, inode); return __nfs_revalidate_inode(server, inode);
} }
static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin) loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
{ {
dprintk("NFS: llseek file(%s/%s, %lld, %d)\n", dprintk("NFS: llseek file(%s/%s, %lld, %d)\n",
filp->f_path.dentry->d_parent->d_name.name, filp->f_path.dentry->d_parent->d_name.name,
@ -160,7 +141,7 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin)
/* /*
* Flush all dirty pages, and check for write errors. * Flush all dirty pages, and check for write errors.
*/ */
static int int
nfs_file_flush(struct file *file, fl_owner_t id) nfs_file_flush(struct file *file, fl_owner_t id)
{ {
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
@ -178,14 +159,14 @@ nfs_file_flush(struct file *file, fl_owner_t id)
* If we're holding a write delegation, then just start the i/o * If we're holding a write delegation, then just start the i/o
* but don't wait for completion (or send a commit). * but don't wait for completion (or send a commit).
*/ */
if (nfs_have_delegation(inode, FMODE_WRITE)) if (NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
return filemap_fdatawrite(file->f_mapping); return filemap_fdatawrite(file->f_mapping);
/* Flush writes to the server and return any errors */ /* Flush writes to the server and return any errors */
return vfs_fsync(file, 0); return vfs_fsync(file, 0);
} }
static ssize_t ssize_t
nfs_file_read(struct kiocb *iocb, const struct iovec *iov, nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos) unsigned long nr_segs, loff_t pos)
{ {
@ -209,7 +190,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
return result; return result;
} }
static ssize_t ssize_t
nfs_file_splice_read(struct file *filp, loff_t *ppos, nfs_file_splice_read(struct file *filp, loff_t *ppos,
struct pipe_inode_info *pipe, size_t count, struct pipe_inode_info *pipe, size_t count,
unsigned int flags) unsigned int flags)
@ -231,7 +212,7 @@ nfs_file_splice_read(struct file *filp, loff_t *ppos,
return res; return res;
} }
static int int
nfs_file_mmap(struct file * file, struct vm_area_struct * vma) nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
{ {
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
@ -264,8 +245,8 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
* nfs_file_write() that a write error occurred, and hence cause it to * nfs_file_write() that a write error occurred, and hence cause it to
* fall back to doing a synchronous write. * fall back to doing a synchronous write.
*/ */
static int int
nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync) nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync)
{ {
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
struct nfs_open_context *ctx = nfs_file_open_context(file); struct nfs_open_context *ctx = nfs_file_open_context(file);
@ -277,9 +258,6 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_parent->d_name.name, dentry->d_name.name,
datasync); datasync);
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
mutex_lock(&inode->i_mutex);
nfs_inc_stats(inode, NFSIOS_VFSFSYNC); nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
status = nfs_commit_inode(inode, FLUSH_SYNC); status = nfs_commit_inode(inode, FLUSH_SYNC);
@ -290,10 +268,20 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
ret = xchg(&ctx->error, 0); ret = xchg(&ctx->error, 0);
if (!ret && status < 0) if (!ret && status < 0)
ret = status; ret = status;
if (!ret && !datasync) return ret;
/* application has asked for meta-data sync */ }
ret = pnfs_layoutcommit_inode(inode, true);
static int
nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
int ret;
struct inode *inode = file->f_path.dentry->d_inode;
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
mutex_lock(&inode->i_mutex);
ret = nfs_file_fsync_commit(file, start, end, datasync);
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
return ret; return ret;
} }
@ -572,8 +560,8 @@ static int nfs_need_sync_write(struct file *filp, struct inode *inode)
return 0; return 0;
} }
static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos) unsigned long nr_segs, loff_t pos)
{ {
struct dentry * dentry = iocb->ki_filp->f_path.dentry; struct dentry * dentry = iocb->ki_filp->f_path.dentry;
struct inode * inode = dentry->d_inode; struct inode * inode = dentry->d_inode;
@ -624,9 +612,9 @@ out_swapfile:
goto out; goto out;
} }
static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe, ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
struct file *filp, loff_t *ppos, struct file *filp, loff_t *ppos,
size_t count, unsigned int flags) size_t count, unsigned int flags)
{ {
struct dentry *dentry = filp->f_path.dentry; struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
@ -670,7 +658,7 @@ do_getlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
} }
fl->fl_type = saved_type; fl->fl_type = saved_type;
if (nfs_have_delegation(inode, FMODE_READ)) if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
goto out_noconflict; goto out_noconflict;
if (is_local) if (is_local)
@ -765,7 +753,7 @@ do_setlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
* This makes locking act as a cache coherency point. * This makes locking act as a cache coherency point.
*/ */
nfs_sync_mapping(filp->f_mapping); nfs_sync_mapping(filp->f_mapping);
if (!nfs_have_delegation(inode, FMODE_READ)) { if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ)) {
if (is_time_granular(&NFS_SERVER(inode)->time_delta)) if (is_time_granular(&NFS_SERVER(inode)->time_delta))
__nfs_revalidate_inode(NFS_SERVER(inode), inode); __nfs_revalidate_inode(NFS_SERVER(inode), inode);
else else
@ -778,7 +766,7 @@ out:
/* /*
* Lock a (portion of) a file * Lock a (portion of) a file
*/ */
static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl) int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
{ {
struct inode *inode = filp->f_mapping->host; struct inode *inode = filp->f_mapping->host;
int ret = -ENOLCK; int ret = -ENOLCK;
@ -818,7 +806,7 @@ out_err:
/* /*
* Lock a (portion of) a file * Lock a (portion of) a file
*/ */
static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
{ {
struct inode *inode = filp->f_mapping->host; struct inode *inode = filp->f_mapping->host;
int is_local = 0; int is_local = 0;
@ -848,7 +836,7 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl)
* There is no protocol support for leases, so we have no way to implement * There is no protocol support for leases, so we have no way to implement
* them correctly in the face of opens by other clients. * them correctly in the face of opens by other clients.
*/ */
static int nfs_setlease(struct file *file, long arg, struct file_lock **fl) int nfs_setlease(struct file *file, long arg, struct file_lock **fl)
{ {
dprintk("NFS: setlease(%s/%s, arg=%ld)\n", dprintk("NFS: setlease(%s/%s, arg=%ld)\n",
file->f_path.dentry->d_parent->d_name.name, file->f_path.dentry->d_parent->d_name.name,
@ -874,104 +862,3 @@ const struct file_operations nfs_file_operations = {
.check_flags = nfs_check_flags, .check_flags = nfs_check_flags,
.setlease = nfs_setlease, .setlease = nfs_setlease,
}; };
#ifdef CONFIG_NFS_V4
static int
nfs4_file_open(struct inode *inode, struct file *filp)
{
struct nfs_open_context *ctx;
struct dentry *dentry = filp->f_path.dentry;
struct dentry *parent = NULL;
struct inode *dir;
unsigned openflags = filp->f_flags;
struct iattr attr;
int err;
BUG_ON(inode != dentry->d_inode);
/*
* If no cached dentry exists or if it's negative, NFSv4 handled the
* opens in ->lookup() or ->create().
*
* We only get this far for a cached positive dentry. We skipped
* revalidation, so handle it here by dropping the dentry and returning
* -EOPENSTALE. The VFS will retry the lookup/create/open.
*/
dprintk("NFS: open file(%s/%s)\n",
dentry->d_parent->d_name.name,
dentry->d_name.name);
if ((openflags & O_ACCMODE) == 3)
openflags--;
/* We can't create new files here */
openflags &= ~(O_CREAT|O_EXCL);
parent = dget_parent(dentry);
dir = parent->d_inode;
ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
err = PTR_ERR(ctx);
if (IS_ERR(ctx))
goto out;
attr.ia_valid = ATTR_OPEN;
if (openflags & O_TRUNC) {
attr.ia_valid |= ATTR_SIZE;
attr.ia_size = 0;
nfs_wb_all(inode);
}
inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
switch (err) {
case -EPERM:
case -EACCES:
case -EDQUOT:
case -ENOSPC:
case -EROFS:
goto out_put_ctx;
default:
goto out_drop;
}
}
iput(inode);
if (inode != dentry->d_inode)
goto out_drop;
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
nfs_file_set_open_context(filp, ctx);
err = 0;
out_put_ctx:
put_nfs_open_context(ctx);
out:
dput(parent);
return err;
out_drop:
d_drop(dentry);
err = -EOPENSTALE;
goto out_put_ctx;
}
const struct file_operations nfs4_file_operations = {
.llseek = nfs_file_llseek,
.read = do_sync_read,
.write = do_sync_write,
.aio_read = nfs_file_read,
.aio_write = nfs_file_write,
.mmap = nfs_file_mmap,
.open = nfs4_file_open,
.flush = nfs_file_flush,
.release = nfs_file_release,
.fsync = nfs_file_fsync,
.lock = nfs_lock,
.flock = nfs_flock,
.splice_read = nfs_file_splice_read,
.splice_write = nfs_file_splice_write,
.check_flags = nfs_check_flags,
.setlease = nfs_setlease,
};
#endif /* CONFIG_NFS_V4 */

View File

@ -23,21 +23,15 @@
#include <linux/sunrpc/stats.h> #include <linux/sunrpc/stats.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include <linux/nfs_mount.h> #include <linux/nfs_mount.h>
#include <linux/nfs4_mount.h>
#include <linux/lockd/bind.h> #include <linux/lockd/bind.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/nfs_idmap.h>
#include <linux/vfs.h> #include <linux/vfs.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/security.h> #include <linux/security.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "nfs4_fs.h"
#include "delegation.h"
#include "internal.h"
#define NFSDBG_FACILITY NFSDBG_CLIENT #define NFSDBG_FACILITY NFSDBG_CLIENT
/* /*
@ -135,47 +129,3 @@ out:
nfs_free_fattr(fsinfo.fattr); nfs_free_fattr(fsinfo.fattr);
return ret; return ret;
} }
#ifdef CONFIG_NFS_V4
int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh)
{
struct nfs_fsinfo fsinfo;
int ret = -ENOMEM;
dprintk("--> nfs4_get_rootfh()\n");
fsinfo.fattr = nfs_alloc_fattr();
if (fsinfo.fattr == NULL)
goto out;
/* Start by getting the root filehandle from the server */
ret = nfs4_proc_get_rootfh(server, mntfh, &fsinfo);
if (ret < 0) {
dprintk("nfs4_get_rootfh: getroot error = %d\n", -ret);
goto out;
}
if (!(fsinfo.fattr->valid & NFS_ATTR_FATTR_TYPE)
|| !S_ISDIR(fsinfo.fattr->mode)) {
printk(KERN_ERR "nfs4_get_rootfh:"
" getroot encountered non-directory\n");
ret = -ENOTDIR;
goto out;
}
if (fsinfo.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
printk(KERN_ERR "nfs4_get_rootfh:"
" getroot obtained referral\n");
ret = -EREMOTE;
goto out;
}
memcpy(&server->fsid, &fsinfo.fattr->fsid, sizeof(server->fsid));
out:
nfs_free_fattr(fsinfo.fattr);
dprintk("<-- nfs4_get_rootfh() = %d\n", ret);
return ret;
}
#endif /* CONFIG_NFS_V4 */

View File

@ -32,7 +32,6 @@
#include <linux/lockd/bind.h> #include <linux/lockd/bind.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/nfs_idmap.h>
#include <linux/vfs.h> #include <linux/vfs.h>
#include <linux/inet.h> #include <linux/inet.h>
#include <linux/nfs_xdr.h> #include <linux/nfs_xdr.h>
@ -430,7 +429,7 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
* Return any delegations if we're going to change ACLs * Return any delegations if we're going to change ACLs
*/ */
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
nfs_inode_return_delegation(inode); NFS_PROTO(inode)->return_delegation(inode);
error = NFS_PROTO(inode)->setattr(dentry, fattr, attr); error = NFS_PROTO(inode)->setattr(dentry, fattr, attr);
if (error == 0) if (error == 0)
nfs_refresh_inode(inode, fattr); nfs_refresh_inode(inode, fattr);
@ -1457,7 +1456,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
|| S_ISLNK(inode->i_mode))) || S_ISLNK(inode->i_mode)))
invalid &= ~NFS_INO_INVALID_DATA; invalid &= ~NFS_INO_INVALID_DATA;
if (!nfs_have_delegation(inode, FMODE_READ) || if (!NFS_PROTO(inode)->have_delegation(inode, FMODE_READ) ||
(save_cache_validity & NFS_INO_REVAL_FORCED)) (save_cache_validity & NFS_INO_REVAL_FORCED))
nfsi->cache_validity |= invalid; nfsi->cache_validity |= invalid;
@ -1628,87 +1627,96 @@ static int __init init_nfs_fs(void)
{ {
int err; int err;
err = nfs_idmap_init();
if (err < 0)
goto out10;
err = nfs_dns_resolver_init(); err = nfs_dns_resolver_init();
if (err < 0) if (err < 0)
goto out9; goto out11;
err = register_pernet_subsys(&nfs_net_ops); err = register_pernet_subsys(&nfs_net_ops);
if (err < 0) if (err < 0)
goto out8; goto out10;
err = nfs_fscache_register(); err = nfs_fscache_register();
if (err < 0) if (err < 0)
goto out7; goto out9;
err = nfsiod_start(); err = nfsiod_start();
if (err) if (err)
goto out6; goto out8;
err = nfs_fs_proc_init(); err = nfs_fs_proc_init();
if (err) if (err)
goto out5; goto out7;
err = nfs_init_nfspagecache(); err = nfs_init_nfspagecache();
if (err) if (err)
goto out4; goto out6;
err = nfs_init_inodecache(); err = nfs_init_inodecache();
if (err) if (err)
goto out3; goto out5;
err = nfs_init_readpagecache(); err = nfs_init_readpagecache();
if (err) if (err)
goto out2; goto out4;
err = nfs_init_writepagecache(); err = nfs_init_writepagecache();
if (err) if (err)
goto out1; goto out3;
err = nfs_init_directcache(); err = nfs_init_directcache();
if (err) if (err)
goto out0; goto out2;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
rpc_proc_register(&init_net, &nfs_rpcstat); rpc_proc_register(&init_net, &nfs_rpcstat);
#endif #endif
#ifdef CONFIG_NFS_V4
err = init_nfs_v4();
if (err)
goto out1;
#endif
if ((err = register_nfs_fs()) != 0) if ((err = register_nfs_fs()) != 0)
goto out; goto out0;
return 0; return 0;
out: out0:
#ifdef CONFIG_NFS_V4
exit_nfs_v4();
out1:
#endif
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
rpc_proc_unregister(&init_net, "nfs"); rpc_proc_unregister(&init_net, "nfs");
#endif #endif
nfs_destroy_directcache(); nfs_destroy_directcache();
out0:
nfs_destroy_writepagecache();
out1:
nfs_destroy_readpagecache();
out2: out2:
nfs_destroy_inodecache(); nfs_destroy_writepagecache();
out3: out3:
nfs_destroy_nfspagecache(); nfs_destroy_readpagecache();
out4: out4:
nfs_fs_proc_exit(); nfs_destroy_inodecache();
out5: out5:
nfsiod_stop(); nfs_destroy_nfspagecache();
out6: out6:
nfs_fscache_unregister(); nfs_fs_proc_exit();
out7: out7:
unregister_pernet_subsys(&nfs_net_ops); nfsiod_stop();
out8: out8:
nfs_dns_resolver_destroy(); nfs_fscache_unregister();
out9: out9:
nfs_idmap_quit(); unregister_pernet_subsys(&nfs_net_ops);
out10: out10:
nfs_dns_resolver_destroy();
out11:
return err; return err;
} }
static void __exit exit_nfs_fs(void) static void __exit exit_nfs_fs(void)
{ {
#ifdef CONFIG_NFS_V4
exit_nfs_v4();
#endif
nfs_destroy_directcache(); nfs_destroy_directcache();
nfs_destroy_writepagecache(); nfs_destroy_writepagecache();
nfs_destroy_readpagecache(); nfs_destroy_readpagecache();
@ -1717,7 +1725,6 @@ static void __exit exit_nfs_fs(void)
nfs_fscache_unregister(); nfs_fscache_unregister();
unregister_pernet_subsys(&nfs_net_ops); unregister_pernet_subsys(&nfs_net_ops);
nfs_dns_resolver_destroy(); nfs_dns_resolver_destroy();
nfs_idmap_quit();
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
rpc_proc_unregister(&init_net, "nfs"); rpc_proc_unregister(&init_net, "nfs");
#endif #endif

View File

@ -85,6 +85,17 @@ struct nfs_clone_mount {
*/ */
#define NFS_MAX_READDIR_PAGES 8 #define NFS_MAX_READDIR_PAGES 8
struct nfs_client_initdata {
unsigned long init_flags;
const char *hostname;
const struct sockaddr *addr;
size_t addrlen;
const struct nfs_rpc_ops *rpc_ops;
int proto;
u32 minorversion;
struct net *net;
};
/* /*
* In-kernel mount arguments * In-kernel mount arguments
*/ */
@ -142,15 +153,36 @@ struct nfs_mount_request {
struct net *net; struct net *net;
}; };
struct nfs_mount_info {
void (*fill_super)(struct super_block *, struct nfs_mount_info *);
int (*set_security)(struct super_block *, struct dentry *, struct nfs_mount_info *);
struct nfs_parsed_mount_data *parsed;
struct nfs_clone_mount *cloned;
struct nfs_fh *mntfh;
};
extern int nfs_mount(struct nfs_mount_request *info); extern int nfs_mount(struct nfs_mount_request *info);
extern void nfs_umount(const struct nfs_mount_request *info); extern void nfs_umount(const struct nfs_mount_request *info);
/* client.c */ /* client.c */
extern const struct rpc_program nfs_program; extern const struct rpc_program nfs_program;
extern void nfs_clients_init(struct net *net); extern void nfs_clients_init(struct net *net);
extern struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *);
int nfs_create_rpc_client(struct nfs_client *, const struct rpc_timeout *, rpc_authflavor_t);
struct nfs_client *nfs_get_client(const struct nfs_client_initdata *,
const struct rpc_timeout *, const char *,
rpc_authflavor_t);
int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *, struct nfs_fattr *);
void nfs_server_insert_lists(struct nfs_server *);
void nfs_init_timeout_values(struct rpc_timeout *, int, unsigned int, unsigned int);
int nfs_init_server_rpcclient(struct nfs_server *, const struct rpc_timeout *t,
rpc_authflavor_t);
struct nfs_server *nfs_alloc_server(void);
void nfs_server_copy_userdata(struct nfs_server *, struct nfs_server *);
extern void nfs_cleanup_cb_ident_idr(struct net *); extern void nfs_cleanup_cb_ident_idr(struct net *);
extern void nfs_put_client(struct nfs_client *); extern void nfs_put_client(struct nfs_client *);
extern void nfs_free_client(struct nfs_client *);
extern struct nfs_client *nfs4_find_client_ident(struct net *, int); extern struct nfs_client *nfs4_find_client_ident(struct net *, int);
extern struct nfs_client * extern struct nfs_client *
nfs4_find_client_sessionid(struct net *, const struct sockaddr *, nfs4_find_client_sessionid(struct net *, const struct sockaddr *,
@ -188,6 +220,10 @@ static inline void nfs_fs_proc_exit(void)
} }
#endif #endif
#ifdef CONFIG_NFS_V4_1
int nfs_sockaddr_match_ipaddr(const struct sockaddr *, const struct sockaddr *);
#endif
/* callback_xdr.c */ /* callback_xdr.c */
extern struct svc_version nfs4_callback_version1; extern struct svc_version nfs4_callback_version1;
extern struct svc_version nfs4_callback_version4; extern struct svc_version nfs4_callback_version4;
@ -245,6 +281,32 @@ extern struct nfs_client *nfs_init_client(struct nfs_client *clp,
/* dir.c */ /* dir.c */
extern int nfs_access_cache_shrinker(struct shrinker *shrink, extern int nfs_access_cache_shrinker(struct shrinker *shrink,
struct shrink_control *sc); struct shrink_control *sc);
struct dentry *nfs_lookup(struct inode *, struct dentry *, unsigned int);
int nfs_create(struct inode *, struct dentry *, umode_t, bool);
int nfs_mkdir(struct inode *, struct dentry *, umode_t);
int nfs_rmdir(struct inode *, struct dentry *);
int nfs_unlink(struct inode *, struct dentry *);
int nfs_symlink(struct inode *, struct dentry *, const char *);
int nfs_link(struct dentry *, struct inode *, struct dentry *);
int nfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
int nfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
/* file.c */
int nfs_file_fsync_commit(struct file *, loff_t, loff_t, int);
loff_t nfs_file_llseek(struct file *, loff_t, int);
int nfs_file_flush(struct file *, fl_owner_t);
ssize_t nfs_file_read(struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t nfs_file_splice_read(struct file *, loff_t *, struct pipe_inode_info *,
size_t, unsigned int);
int nfs_file_mmap(struct file *, struct vm_area_struct *);
ssize_t nfs_file_write(struct kiocb *, const struct iovec *, unsigned long, loff_t);
int nfs_file_release(struct inode *, struct file *);
int nfs_lock(struct file *, int, struct file_lock *);
int nfs_flock(struct file *, int, struct file_lock *);
ssize_t nfs_file_splice_write(struct pipe_inode_info *, struct file *, loff_t *,
size_t, unsigned int);
int nfs_check_flags(int);
int nfs_setlease(struct file *, long, struct file_lock **);
/* inode.c */ /* inode.c */
extern struct workqueue_struct *nfsiod_workqueue; extern struct workqueue_struct *nfsiod_workqueue;
@ -264,6 +326,16 @@ extern struct file_system_type nfs_xdev_fs_type;
extern struct file_system_type nfs4_xdev_fs_type; extern struct file_system_type nfs4_xdev_fs_type;
extern struct file_system_type nfs4_referral_fs_type; extern struct file_system_type nfs4_referral_fs_type;
#endif #endif
void nfs_initialise_sb(struct super_block *);
int nfs_set_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *);
int nfs_clone_sb_security(struct super_block *, struct dentry *, struct nfs_mount_info *);
struct dentry *nfs_fs_mount_common(struct file_system_type *, struct nfs_server *,
int, const char *, struct nfs_mount_info *);
struct dentry *nfs_fs_mount(struct file_system_type *, int, const char *, void *);
struct dentry * nfs_xdev_mount_common(struct file_system_type *, int,
const char *, struct nfs_mount_info *);
void nfs_kill_super(struct super_block *);
void nfs_fill_super(struct super_block *, struct nfs_mount_info *);
extern struct rpc_stat nfs_rpcstat; extern struct rpc_stat nfs_rpcstat;
@ -304,12 +376,23 @@ extern int nfs_initiate_read(struct rpc_clnt *clnt,
extern void nfs_read_prepare(struct rpc_task *task, void *calldata); extern void nfs_read_prepare(struct rpc_task *task, void *calldata);
extern int nfs_generic_pagein(struct nfs_pageio_descriptor *desc, extern int nfs_generic_pagein(struct nfs_pageio_descriptor *desc,
struct nfs_pgio_header *hdr); struct nfs_pgio_header *hdr);
extern void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio, extern void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
struct inode *inode, struct inode *inode,
const struct nfs_pgio_completion_ops *compl_ops); const struct nfs_pgio_completion_ops *compl_ops);
extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio); extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio);
extern void nfs_readdata_release(struct nfs_read_data *rdata); extern void nfs_readdata_release(struct nfs_read_data *rdata);
/* super.c */
void nfs_clone_super(struct super_block *, struct nfs_mount_info *);
void nfs_umount_begin(struct super_block *);
int nfs_statfs(struct dentry *, struct kstatfs *);
int nfs_show_options(struct seq_file *, struct dentry *);
int nfs_show_devname(struct seq_file *, struct dentry *);
int nfs_show_path(struct seq_file *, struct dentry *);
int nfs_show_stats(struct seq_file *, struct dentry *);
void nfs_put_super(struct super_block *);
int nfs_remount(struct super_block *sb, int *flags, char *raw_data);
/* write.c */ /* write.c */
extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
struct inode *inode, int ioflags, struct inode *inode, int ioflags,
@ -318,7 +401,7 @@ extern struct nfs_write_header *nfs_writehdr_alloc(void);
extern void nfs_writehdr_free(struct nfs_pgio_header *hdr); extern void nfs_writehdr_free(struct nfs_pgio_header *hdr);
extern int nfs_generic_flush(struct nfs_pageio_descriptor *desc, extern int nfs_generic_flush(struct nfs_pageio_descriptor *desc,
struct nfs_pgio_header *hdr); struct nfs_pgio_header *hdr);
extern void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio, extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
struct inode *inode, int ioflags, struct inode *inode, int ioflags,
const struct nfs_pgio_completion_ops *compl_ops); const struct nfs_pgio_completion_ops *compl_ops);
extern void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio); extern void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio);

View File

@ -106,19 +106,16 @@ static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result) static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result)
{ {
u32 recvd, count; u32 recvd, count;
size_t hdrlen;
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
goto out_overflow; goto out_overflow;
count = be32_to_cpup(p); count = be32_to_cpup(p);
hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; recvd = xdr_read_pages(xdr, count);
recvd = xdr->buf->len - hdrlen;
if (unlikely(count > recvd)) if (unlikely(count > recvd))
goto out_cheating; goto out_cheating;
out: out:
xdr_read_pages(xdr, count);
result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */ result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */
result->count = count; result->count = count;
return count; return count;
@ -440,7 +437,6 @@ static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
static int decode_path(struct xdr_stream *xdr) static int decode_path(struct xdr_stream *xdr)
{ {
u32 length, recvd; u32 length, recvd;
size_t hdrlen;
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
@ -449,12 +445,9 @@ static int decode_path(struct xdr_stream *xdr)
length = be32_to_cpup(p); length = be32_to_cpup(p);
if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN)) if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
goto out_size; goto out_size;
hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; recvd = xdr_read_pages(xdr, length);
recvd = xdr->buf->len - hdrlen;
if (unlikely(length > recvd)) if (unlikely(length > recvd))
goto out_cheating; goto out_cheating;
xdr_read_pages(xdr, length);
xdr_terminate_string(xdr->buf, length); xdr_terminate_string(xdr->buf, length);
return 0; return 0;
out_size: out_size:
@ -972,22 +965,7 @@ out_overflow:
*/ */
static int decode_readdirok(struct xdr_stream *xdr) static int decode_readdirok(struct xdr_stream *xdr)
{ {
u32 recvd, pglen; return xdr_read_pages(xdr, xdr->buf->page_len);
size_t hdrlen;
pglen = xdr->buf->page_len;
hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
recvd = xdr->buf->len - hdrlen;
if (unlikely(pglen > recvd))
goto out_cheating;
out:
xdr_read_pages(xdr, pglen);
return pglen;
out_cheating:
dprintk("NFS: server cheating in readdir result: "
"pglen %u > recvd %u\n", pglen, recvd);
pglen = recvd;
goto out;
} }
static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req, static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req,

View File

@ -877,6 +877,46 @@ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl)
return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl); return nlmclnt_proc(NFS_SERVER(inode)->nlm_host, cmd, fl);
} }
static int nfs3_have_delegation(struct inode *inode, fmode_t flags)
{
return 0;
}
static int nfs3_return_delegation(struct inode *inode)
{
nfs_wb_all(inode);
return 0;
}
static const struct inode_operations nfs3_dir_inode_operations = {
.create = nfs_create,
.lookup = nfs_lookup,
.link = nfs_link,
.unlink = nfs_unlink,
.symlink = nfs_symlink,
.mkdir = nfs_mkdir,
.rmdir = nfs_rmdir,
.mknod = nfs_mknod,
.rename = nfs_rename,
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
.listxattr = nfs3_listxattr,
.getxattr = nfs3_getxattr,
.setxattr = nfs3_setxattr,
.removexattr = nfs3_removexattr,
};
static const struct inode_operations nfs3_file_inode_operations = {
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
.listxattr = nfs3_listxattr,
.getxattr = nfs3_getxattr,
.setxattr = nfs3_setxattr,
.removexattr = nfs3_removexattr,
};
const struct nfs_rpc_ops nfs_v3_clientops = { const struct nfs_rpc_ops nfs_v3_clientops = {
.version = 3, /* protocol version */ .version = 3, /* protocol version */
.dentry_ops = &nfs_dentry_operations, .dentry_ops = &nfs_dentry_operations,
@ -910,9 +950,11 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
.pathconf = nfs3_proc_pathconf, .pathconf = nfs3_proc_pathconf,
.decode_dirent = nfs3_decode_dirent, .decode_dirent = nfs3_decode_dirent,
.read_setup = nfs3_proc_read_setup, .read_setup = nfs3_proc_read_setup,
.read_pageio_init = nfs_pageio_init_read,
.read_rpc_prepare = nfs3_proc_read_rpc_prepare, .read_rpc_prepare = nfs3_proc_read_rpc_prepare,
.read_done = nfs3_read_done, .read_done = nfs3_read_done,
.write_setup = nfs3_proc_write_setup, .write_setup = nfs3_proc_write_setup,
.write_pageio_init = nfs_pageio_init_write,
.write_rpc_prepare = nfs3_proc_write_rpc_prepare, .write_rpc_prepare = nfs3_proc_write_rpc_prepare,
.write_done = nfs3_write_done, .write_done = nfs3_write_done,
.commit_setup = nfs3_proc_commit_setup, .commit_setup = nfs3_proc_commit_setup,
@ -921,5 +963,9 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
.lock = nfs3_proc_lock, .lock = nfs3_proc_lock,
.clear_acl_cache = nfs3_forget_cached_acls, .clear_acl_cache = nfs3_forget_cached_acls,
.close_context = nfs_close_context, .close_context = nfs_close_context,
.have_delegation = nfs3_have_delegation,
.return_delegation = nfs3_return_delegation,
.alloc_client = nfs_alloc_client,
.init_client = nfs_init_client, .init_client = nfs_init_client,
.free_client = nfs_free_client,
}; };

View File

@ -246,7 +246,6 @@ static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
static int decode_nfspath3(struct xdr_stream *xdr) static int decode_nfspath3(struct xdr_stream *xdr)
{ {
u32 recvd, count; u32 recvd, count;
size_t hdrlen;
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
@ -255,12 +254,9 @@ static int decode_nfspath3(struct xdr_stream *xdr)
count = be32_to_cpup(p); count = be32_to_cpup(p);
if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN)) if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN))
goto out_nametoolong; goto out_nametoolong;
hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; recvd = xdr_read_pages(xdr, count);
recvd = xdr->buf->len - hdrlen;
if (unlikely(count > recvd)) if (unlikely(count > recvd))
goto out_cheating; goto out_cheating;
xdr_read_pages(xdr, count);
xdr_terminate_string(xdr->buf, count); xdr_terminate_string(xdr->buf, count);
return 0; return 0;
@ -329,14 +325,14 @@ static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier)
memcpy(p, verifier, NFS3_CREATEVERFSIZE); memcpy(p, verifier, NFS3_CREATEVERFSIZE);
} }
static int decode_writeverf3(struct xdr_stream *xdr, __be32 *verifier) static int decode_writeverf3(struct xdr_stream *xdr, struct nfs_write_verifier *verifier)
{ {
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE); p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
goto out_overflow; goto out_overflow;
memcpy(verifier, p, NFS3_WRITEVERFSIZE); memcpy(verifier->data, p, NFS3_WRITEVERFSIZE);
return 0; return 0;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr); print_overflow_msg(__func__, xdr);
@ -1587,7 +1583,6 @@ static int decode_read3resok(struct xdr_stream *xdr,
struct nfs_readres *result) struct nfs_readres *result)
{ {
u32 eof, count, ocount, recvd; u32 eof, count, ocount, recvd;
size_t hdrlen;
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, 4 + 4 + 4); p = xdr_inline_decode(xdr, 4 + 4 + 4);
@ -1598,13 +1593,10 @@ static int decode_read3resok(struct xdr_stream *xdr,
ocount = be32_to_cpup(p++); ocount = be32_to_cpup(p++);
if (unlikely(ocount != count)) if (unlikely(ocount != count))
goto out_mismatch; goto out_mismatch;
hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; recvd = xdr_read_pages(xdr, count);
recvd = xdr->buf->len - hdrlen;
if (unlikely(count > recvd)) if (unlikely(count > recvd))
goto out_cheating; goto out_cheating;
out: out:
xdr_read_pages(xdr, count);
result->eof = eof; result->eof = eof;
result->count = count; result->count = count;
return count; return count;
@ -1676,20 +1668,22 @@ static int decode_write3resok(struct xdr_stream *xdr,
{ {
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, 4 + 4 + NFS3_WRITEVERFSIZE); p = xdr_inline_decode(xdr, 4 + 4);
if (unlikely(p == NULL)) if (unlikely(p == NULL))
goto out_overflow; goto out_overflow;
result->count = be32_to_cpup(p++); result->count = be32_to_cpup(p++);
result->verf->committed = be32_to_cpup(p++); result->verf->committed = be32_to_cpup(p++);
if (unlikely(result->verf->committed > NFS_FILE_SYNC)) if (unlikely(result->verf->committed > NFS_FILE_SYNC))
goto out_badvalue; goto out_badvalue;
memcpy(result->verf->verifier, p, NFS3_WRITEVERFSIZE); if (decode_writeverf3(xdr, &result->verf->verifier))
goto out_eio;
return result->count; return result->count;
out_badvalue: out_badvalue:
dprintk("NFS: bad stable_how value: %u\n", result->verf->committed); dprintk("NFS: bad stable_how value: %u\n", result->verf->committed);
return -EIO; return -EIO;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr); print_overflow_msg(__func__, xdr);
out_eio:
return -EIO; return -EIO;
} }
@ -2039,22 +2033,7 @@ out_truncated:
*/ */
static int decode_dirlist3(struct xdr_stream *xdr) static int decode_dirlist3(struct xdr_stream *xdr)
{ {
u32 recvd, pglen; return xdr_read_pages(xdr, xdr->buf->page_len);
size_t hdrlen;
pglen = xdr->buf->page_len;
hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
recvd = xdr->buf->len - hdrlen;
if (unlikely(pglen > recvd))
goto out_cheating;
out:
xdr_read_pages(xdr, pglen);
return pglen;
out_cheating:
dprintk("NFS: server cheating in readdir result: "
"pglen %u > recvd %u\n", pglen, recvd);
pglen = recvd;
goto out;
} }
static int decode_readdir3resok(struct xdr_stream *xdr, static int decode_readdir3resok(struct xdr_stream *xdr,
@ -2337,7 +2316,7 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
goto out; goto out;
if (status != NFS3_OK) if (status != NFS3_OK)
goto out_status; goto out_status;
error = decode_writeverf3(xdr, result->verf->verifier); error = decode_writeverf3(xdr, &result->verf->verifier);
out: out:
return error; return error;
out_status: out_status:
@ -2364,7 +2343,7 @@ static inline int decode_getacl3resok(struct xdr_stream *xdr,
if (result->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) if (result->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT))
goto out; goto out;
hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; hdrlen = xdr_stream_pos(xdr);
acl = NULL; acl = NULL;
if (result->mask & NFS_ACL) if (result->mask & NFS_ACL)

View File

@ -200,7 +200,13 @@ struct nfs4_state_maintenance_ops {
}; };
extern const struct dentry_operations nfs4_dentry_operations; extern const struct dentry_operations nfs4_dentry_operations;
extern const struct inode_operations nfs4_dir_inode_operations;
/* dir.c */
int nfs_atomic_open(struct inode *, struct dentry *, struct file *,
unsigned, umode_t, int *);
/* write.c */
int nfs4_write_inode(struct inode *, struct writeback_control *);
/* nfs4namespace.c */ /* nfs4namespace.c */
rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *); rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);
@ -301,6 +307,10 @@ extern const u32 nfs4_pathconf_bitmap[2];
extern const u32 nfs4_fsinfo_bitmap[3]; extern const u32 nfs4_fsinfo_bitmap[3];
extern const u32 nfs4_fs_locations_bitmap[2]; extern const u32 nfs4_fs_locations_bitmap[2];
void nfs4_free_client(struct nfs_client *);
struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *);
/* nfs4renewd.c */ /* nfs4renewd.c */
extern void nfs4_schedule_state_renewal(struct nfs_client *); extern void nfs4_schedule_state_renewal(struct nfs_client *);
extern void nfs4_renewd_prepare_shutdown(struct nfs_server *); extern void nfs4_renewd_prepare_shutdown(struct nfs_server *);
@ -354,6 +364,27 @@ extern void nfs4_free_lock_state(struct nfs_server *server, struct nfs4_lock_sta
extern const nfs4_stateid zero_stateid; extern const nfs4_stateid zero_stateid;
/* nfs4super.c */
struct nfs_mount_info;
struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *);
int init_nfs_v4(void);
void exit_nfs_v4(void);
/* nfs4sysctl.c */
#ifdef CONFIG_SYSCTL
int nfs4_register_sysctl(void);
void nfs4_unregister_sysctl(void);
#else
static inline int nfs4_register_sysctl(void)
{
return 0;
}
static inline void nfs4_unregister_sysctl(void)
{
}
#endif
/* nfs4xdr.c */ /* nfs4xdr.c */
extern struct rpc_procinfo nfs4_procedures[]; extern struct rpc_procinfo nfs4_procedures[];

663
fs/nfs/nfs4client.c Normal file
View File

@ -0,0 +1,663 @@
/*
* Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#include <linux/module.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_idmap.h>
#include <linux/nfs_mount.h>
#include <linux/sunrpc/auth.h>
#include <linux/sunrpc/xprt.h>
#include <linux/sunrpc/bc_xprt.h>
#include "internal.h"
#include "callback.h"
#include "delegation.h"
#include "pnfs.h"
#include "netns.h"
#define NFSDBG_FACILITY NFSDBG_CLIENT
/*
* Turn off NFSv4 uid/gid mapping when using AUTH_SYS
*/
static bool nfs4_disable_idmapping = true;
/*
* Get a unique NFSv4.0 callback identifier which will be used
* by the V4.0 callback service to lookup the nfs_client struct
*/
static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion)
{
int ret = 0;
struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
if (clp->rpc_ops->version != 4 || minorversion != 0)
return ret;
retry:
if (!idr_pre_get(&nn->cb_ident_idr, GFP_KERNEL))
return -ENOMEM;
spin_lock(&nn->nfs_client_lock);
ret = idr_get_new(&nn->cb_ident_idr, clp, &clp->cl_cb_ident);
spin_unlock(&nn->nfs_client_lock);
if (ret == -EAGAIN)
goto retry;
return ret;
}
#ifdef CONFIG_NFS_V4_1
static void nfs4_shutdown_session(struct nfs_client *clp)
{
if (nfs4_has_session(clp)) {
nfs4_destroy_session(clp->cl_session);
nfs4_destroy_clientid(clp);
}
}
#else /* CONFIG_NFS_V4_1 */
static void nfs4_shutdown_session(struct nfs_client *clp)
{
}
#endif /* CONFIG_NFS_V4_1 */
struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
{
int err;
struct nfs_client *clp = nfs_alloc_client(cl_init);
if (IS_ERR(clp))
return clp;
err = nfs_get_cb_ident_idr(clp, cl_init->minorversion);
if (err)
goto error;
spin_lock_init(&clp->cl_lock);
INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
clp->cl_minorversion = cl_init->minorversion;
clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
return clp;
error:
kfree(clp);
return ERR_PTR(err);
}
/*
* Destroy the NFS4 callback service
*/
static void nfs4_destroy_callback(struct nfs_client *clp)
{
if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
nfs_callback_down(clp->cl_mvops->minor_version);
}
static void nfs4_shutdown_client(struct nfs_client *clp)
{
if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state))
nfs4_kill_renewd(clp);
nfs4_shutdown_session(clp);
nfs4_destroy_callback(clp);
if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state))
nfs_idmap_delete(clp);
rpc_destroy_wait_queue(&clp->cl_rpcwaitq);
kfree(clp->cl_serverowner);
kfree(clp->cl_serverscope);
kfree(clp->cl_implid);
}
void nfs4_free_client(struct nfs_client *clp)
{
nfs4_shutdown_client(clp);
nfs_free_client(clp);
}
/*
* Initialize the NFS4 callback service
*/
static int nfs4_init_callback(struct nfs_client *clp)
{
int error;
if (clp->rpc_ops->version == 4) {
struct rpc_xprt *xprt;
xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt);
if (nfs4_has_session(clp)) {
error = xprt_setup_backchannel(xprt,
NFS41_BC_MIN_CALLBACKS);
if (error < 0)
return error;
}
error = nfs_callback_up(clp->cl_mvops->minor_version, xprt);
if (error < 0) {
dprintk("%s: failed to start callback. Error = %d\n",
__func__, error);
return error;
}
__set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
}
return 0;
}
/*
* Initialize the minor version specific parts of an NFS4 client record
*/
static int nfs4_init_client_minor_version(struct nfs_client *clp)
{
#if defined(CONFIG_NFS_V4_1)
if (clp->cl_mvops->minor_version) {
struct nfs4_session *session = NULL;
/*
* Create the session and mark it expired.
* When a SEQUENCE operation encounters the expired session
* it will do session recovery to initialize it.
*/
session = nfs4_alloc_session(clp);
if (!session)
return -ENOMEM;
clp->cl_session = session;
/*
* The create session reply races with the server back
* channel probe. Mark the client NFS_CS_SESSION_INITING
* so that the client back channel can find the
* nfs_client struct
*/
nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING);
}
#endif /* CONFIG_NFS_V4_1 */
return nfs4_init_callback(clp);
}
/**
* nfs4_init_client - Initialise an NFS4 client record
*
* @clp: nfs_client to initialise
* @timeparms: timeout parameters for underlying RPC transport
* @ip_addr: callback IP address in presentation format
* @authflavor: authentication flavor for underlying RPC transport
*
* Returns pointer to an NFS client, or an ERR_PTR value.
*/
struct nfs_client *nfs4_init_client(struct nfs_client *clp,
const struct rpc_timeout *timeparms,
const char *ip_addr,
rpc_authflavor_t authflavour)
{
char buf[INET6_ADDRSTRLEN + 1];
int error;
if (clp->cl_cons_state == NFS_CS_READY) {
/* the client is initialised already */
dprintk("<-- nfs4_init_client() = 0 [already %p]\n", clp);
return clp;
}
/* Check NFS protocol revision and initialize RPC op vector */
clp->rpc_ops = &nfs_v4_clientops;
__set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
error = nfs_create_rpc_client(clp, timeparms, authflavour);
if (error < 0)
goto error;
/* If no clientaddr= option was specified, find a usable cb address */
if (ip_addr == NULL) {
struct sockaddr_storage cb_addr;
struct sockaddr *sap = (struct sockaddr *)&cb_addr;
error = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr));
if (error < 0)
goto error;
error = rpc_ntop(sap, buf, sizeof(buf));
if (error < 0)
goto error;
ip_addr = (const char *)buf;
}
strlcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
error = nfs_idmap_new(clp);
if (error < 0) {
dprintk("%s: failed to create idmapper. Error = %d\n",
__func__, error);
goto error;
}
__set_bit(NFS_CS_IDMAP, &clp->cl_res_state);
error = nfs4_init_client_minor_version(clp);
if (error < 0)
goto error;
if (!nfs4_has_session(clp))
nfs_mark_client_ready(clp, NFS_CS_READY);
return clp;
error:
nfs_mark_client_ready(clp, error);
nfs_put_client(clp);
dprintk("<-- nfs4_init_client() = xerror %d\n", error);
return ERR_PTR(error);
}
static void nfs4_destroy_server(struct nfs_server *server)
{
nfs_server_return_all_delegations(server);
unset_pnfs_layoutdriver(server);
nfs4_purge_state_owners(server);
}
/*
* NFSv4.0 callback thread helper
*
* Find a client by callback identifier
*/
struct nfs_client *
nfs4_find_client_ident(struct net *net, int cb_ident)
{
struct nfs_client *clp;
struct nfs_net *nn = net_generic(net, nfs_net_id);
spin_lock(&nn->nfs_client_lock);
clp = idr_find(&nn->cb_ident_idr, cb_ident);
if (clp)
atomic_inc(&clp->cl_count);
spin_unlock(&nn->nfs_client_lock);
return clp;
}
#if defined(CONFIG_NFS_V4_1)
/* Common match routine for v4.0 and v4.1 callback services */
static bool nfs4_cb_match_client(const struct sockaddr *addr,
struct nfs_client *clp, u32 minorversion)
{
struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
/* Don't match clients that failed to initialise */
if (!(clp->cl_cons_state == NFS_CS_READY ||
clp->cl_cons_state == NFS_CS_SESSION_INITING))
return false;
smp_rmb();
/* Match the version and minorversion */
if (clp->rpc_ops->version != 4 ||
clp->cl_minorversion != minorversion)
return false;
/* Match only the IP address, not the port number */
if (!nfs_sockaddr_match_ipaddr(addr, clap))
return false;
return true;
}
/*
* NFSv4.1 callback thread helper
* For CB_COMPOUND calls, find a client by IP address, protocol version,
* minorversion, and sessionID
*
* Returns NULL if no such client
*/
struct nfs_client *
nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
struct nfs4_sessionid *sid)
{
struct nfs_client *clp;
struct nfs_net *nn = net_generic(net, nfs_net_id);
spin_lock(&nn->nfs_client_lock);
list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) {
if (nfs4_cb_match_client(addr, clp, 1) == false)
continue;
if (!nfs4_has_session(clp))
continue;
/* Match sessionid*/
if (memcmp(clp->cl_session->sess_id.data,
sid->data, NFS4_MAX_SESSIONID_LEN) != 0)
continue;
atomic_inc(&clp->cl_count);
spin_unlock(&nn->nfs_client_lock);
return clp;
}
spin_unlock(&nn->nfs_client_lock);
return NULL;
}
#else /* CONFIG_NFS_V4_1 */
struct nfs_client *
nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr,
struct nfs4_sessionid *sid)
{
return NULL;
}
#endif /* CONFIG_NFS_V4_1 */
/*
* Set up an NFS4 client
*/
static int nfs4_set_client(struct nfs_server *server,
const char *hostname,
const struct sockaddr *addr,
const size_t addrlen,
const char *ip_addr,
rpc_authflavor_t authflavour,
int proto, const struct rpc_timeout *timeparms,
u32 minorversion, struct net *net)
{
struct nfs_client_initdata cl_init = {
.hostname = hostname,
.addr = addr,
.addrlen = addrlen,
.rpc_ops = &nfs_v4_clientops,
.proto = proto,
.minorversion = minorversion,
.net = net,
};
struct nfs_client *clp;
int error;
dprintk("--> nfs4_set_client()\n");
if (server->flags & NFS_MOUNT_NORESVPORT)
set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
/* Allocate or find a client reference we can use */
clp = nfs_get_client(&cl_init, timeparms, ip_addr, authflavour);
if (IS_ERR(clp)) {
error = PTR_ERR(clp);
goto error;
}
/*
* Query for the lease time on clientid setup or renewal
*
* Note that this will be set on nfs_clients that were created
* only for the DS role and did not set this bit, but now will
* serve a dual role.
*/
set_bit(NFS_CS_CHECK_LEASE_TIME, &clp->cl_res_state);
server->nfs_client = clp;
dprintk("<-- nfs4_set_client() = 0 [new %p]\n", clp);
return 0;
error:
dprintk("<-- nfs4_set_client() = xerror %d\n", error);
return error;
}
/*
* Set up a pNFS Data Server client.
*
* Return any existing nfs_client that matches server address,port,version
* and minorversion.
*
* For a new nfs_client, use a soft mount (default), a low retrans and a
* low timeout interval so that if a connection is lost, we retry through
* the MDS.
*/
struct nfs_client *nfs4_set_ds_client(struct nfs_client* mds_clp,
const struct sockaddr *ds_addr, int ds_addrlen,
int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans)
{
struct nfs_client_initdata cl_init = {
.addr = ds_addr,
.addrlen = ds_addrlen,
.rpc_ops = &nfs_v4_clientops,
.proto = ds_proto,
.minorversion = mds_clp->cl_minorversion,
.net = mds_clp->cl_net,
};
struct rpc_timeout ds_timeout;
struct nfs_client *clp;
/*
* Set an authflavor equual to the MDS value. Use the MDS nfs_client
* cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS
* (section 13.1 RFC 5661).
*/
nfs_init_timeout_values(&ds_timeout, ds_proto, ds_timeo, ds_retrans);
clp = nfs_get_client(&cl_init, &ds_timeout, mds_clp->cl_ipaddr,
mds_clp->cl_rpcclient->cl_auth->au_flavor);
dprintk("<-- %s %p\n", __func__, clp);
return clp;
}
EXPORT_SYMBOL_GPL(nfs4_set_ds_client);
/*
* Session has been established, and the client marked ready.
* Set the mount rsize and wsize with negotiated fore channel
* attributes which will be bound checked in nfs_server_set_fsinfo.
*/
static void nfs4_session_set_rwsize(struct nfs_server *server)
{
#ifdef CONFIG_NFS_V4_1
struct nfs4_session *sess;
u32 server_resp_sz;
u32 server_rqst_sz;
if (!nfs4_has_session(server->nfs_client))
return;
sess = server->nfs_client->cl_session;
server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead;
server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead;
if (server->rsize > server_resp_sz)
server->rsize = server_resp_sz;
if (server->wsize > server_rqst_sz)
server->wsize = server_rqst_sz;
#endif /* CONFIG_NFS_V4_1 */
}
static int nfs4_server_common_setup(struct nfs_server *server,
struct nfs_fh *mntfh)
{
struct nfs_fattr *fattr;
int error;
BUG_ON(!server->nfs_client);
BUG_ON(!server->nfs_client->rpc_ops);
BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
/* data servers support only a subset of NFSv4.1 */
if (is_ds_only_client(server->nfs_client))
return -EPROTONOSUPPORT;
fattr = nfs_alloc_fattr();
if (fattr == NULL)
return -ENOMEM;
/* We must ensure the session is initialised first */
error = nfs4_init_session(server);
if (error < 0)
goto out;
/* Probe the root fh to retrieve its FSID and filehandle */
error = nfs4_get_rootfh(server, mntfh);
if (error < 0)
goto out;
dprintk("Server FSID: %llx:%llx\n",
(unsigned long long) server->fsid.major,
(unsigned long long) server->fsid.minor);
dprintk("Mount FH: %d\n", mntfh->size);
nfs4_session_set_rwsize(server);
error = nfs_probe_fsinfo(server, mntfh, fattr);
if (error < 0)
goto out;
if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
server->namelen = NFS4_MAXNAMLEN;
nfs_server_insert_lists(server);
server->mount_time = jiffies;
server->destroy = nfs4_destroy_server;
out:
nfs_free_fattr(fattr);
return error;
}
/*
* Create a version 4 volume record
*/
static int nfs4_init_server(struct nfs_server *server,
const struct nfs_parsed_mount_data *data)
{
struct rpc_timeout timeparms;
int error;
dprintk("--> nfs4_init_server()\n");
nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
data->timeo, data->retrans);
/* Initialise the client representation from the mount data */
server->flags = data->flags;
server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK;
if (!(data->flags & NFS_MOUNT_NORDIRPLUS))
server->caps |= NFS_CAP_READDIRPLUS;
server->options = data->options;
/* Get a client record */
error = nfs4_set_client(server,
data->nfs_server.hostname,
(const struct sockaddr *)&data->nfs_server.address,
data->nfs_server.addrlen,
data->client_address,
data->auth_flavors[0],
data->nfs_server.protocol,
&timeparms,
data->minorversion,
data->net);
if (error < 0)
goto error;
/*
* Don't use NFS uid/gid mapping if we're using AUTH_SYS or lower
* authentication.
*/
if (nfs4_disable_idmapping && data->auth_flavors[0] == RPC_AUTH_UNIX)
server->caps |= NFS_CAP_UIDGID_NOMAP;
if (data->rsize)
server->rsize = nfs_block_size(data->rsize, NULL);
if (data->wsize)
server->wsize = nfs_block_size(data->wsize, NULL);
server->acregmin = data->acregmin * HZ;
server->acregmax = data->acregmax * HZ;
server->acdirmin = data->acdirmin * HZ;
server->acdirmax = data->acdirmax * HZ;
server->port = data->nfs_server.port;
error = nfs_init_server_rpcclient(server, &timeparms, data->auth_flavors[0]);
error:
/* Done */
dprintk("<-- nfs4_init_server() = %d\n", error);
return error;
}
/*
* Create a version 4 volume record
* - keyed on server and FSID
*/
struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
struct nfs_fh *mntfh)
{
struct nfs_server *server;
int error;
dprintk("--> nfs4_create_server()\n");
server = nfs_alloc_server();
if (!server)
return ERR_PTR(-ENOMEM);
/* set up the general RPC client */
error = nfs4_init_server(server, data);
if (error < 0)
goto error;
error = nfs4_server_common_setup(server, mntfh);
if (error < 0)
goto error;
dprintk("<-- nfs4_create_server() = %p\n", server);
return server;
error:
nfs_free_server(server);
dprintk("<-- nfs4_create_server() = error %d\n", error);
return ERR_PTR(error);
}
/*
* Create an NFS4 referral server record
*/
struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
struct nfs_fh *mntfh)
{
struct nfs_client *parent_client;
struct nfs_server *server, *parent_server;
int error;
dprintk("--> nfs4_create_referral_server()\n");
server = nfs_alloc_server();
if (!server)
return ERR_PTR(-ENOMEM);
parent_server = NFS_SB(data->sb);
parent_client = parent_server->nfs_client;
/* Initialise the client representation from the parent server */
nfs_server_copy_userdata(server, parent_server);
server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR;
/* Get a client representation.
* Note: NFSv4 always uses TCP, */
error = nfs4_set_client(server, data->hostname,
data->addr,
data->addrlen,
parent_client->cl_ipaddr,
data->authflavor,
rpc_protocol(parent_server->client),
parent_server->client->cl_timeout,
parent_client->cl_mvops->minor_version,
parent_client->cl_net);
if (error < 0)
goto error;
error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor);
if (error < 0)
goto error;
error = nfs4_server_common_setup(server, mntfh);
if (error < 0)
goto error;
dprintk("<-- nfs_create_referral_server() = %p\n", server);
return server;
error:
nfs_free_server(server);
dprintk("<-- nfs4_create_referral_server() = error %d\n", error);
return ERR_PTR(error);
}
module_param(nfs4_disable_idmapping, bool, 0644);
MODULE_PARM_DESC(nfs4_disable_idmapping,
"Turn off NFSv4 idmapping when using 'sec=sys'");

126
fs/nfs/nfs4file.c Normal file
View File

@ -0,0 +1,126 @@
/*
* linux/fs/nfs/file.c
*
* Copyright (C) 1992 Rick Sladkey
*/
#include <linux/nfs_fs.h>
#include "internal.h"
#include "pnfs.h"
#define NFSDBG_FACILITY NFSDBG_FILE
static int
nfs4_file_open(struct inode *inode, struct file *filp)
{
struct nfs_open_context *ctx;
struct dentry *dentry = filp->f_path.dentry;
struct dentry *parent = NULL;
struct inode *dir;
unsigned openflags = filp->f_flags;
struct iattr attr;
int err;
BUG_ON(inode != dentry->d_inode);
/*
* If no cached dentry exists or if it's negative, NFSv4 handled the
* opens in ->lookup() or ->create().
*
* We only get this far for a cached positive dentry. We skipped
* revalidation, so handle it here by dropping the dentry and returning
* -EOPENSTALE. The VFS will retry the lookup/create/open.
*/
dprintk("NFS: open file(%s/%s)\n",
dentry->d_parent->d_name.name,
dentry->d_name.name);
if ((openflags & O_ACCMODE) == 3)
openflags--;
/* We can't create new files here */
openflags &= ~(O_CREAT|O_EXCL);
parent = dget_parent(dentry);
dir = parent->d_inode;
ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
err = PTR_ERR(ctx);
if (IS_ERR(ctx))
goto out;
attr.ia_valid = ATTR_OPEN;
if (openflags & O_TRUNC) {
attr.ia_valid |= ATTR_SIZE;
attr.ia_size = 0;
nfs_wb_all(inode);
}
inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, &attr);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
switch (err) {
case -EPERM:
case -EACCES:
case -EDQUOT:
case -ENOSPC:
case -EROFS:
goto out_put_ctx;
default:
goto out_drop;
}
}
iput(inode);
if (inode != dentry->d_inode)
goto out_drop;
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
nfs_file_set_open_context(filp, ctx);
err = 0;
out_put_ctx:
put_nfs_open_context(ctx);
out:
dput(parent);
return err;
out_drop:
d_drop(dentry);
err = -EOPENSTALE;
goto out_put_ctx;
}
static int
nfs4_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
int ret;
struct inode *inode = file->f_path.dentry->d_inode;
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
mutex_lock(&inode->i_mutex);
ret = nfs_file_fsync_commit(file, start, end, datasync);
if (!ret && !datasync)
/* application has asked for meta-data sync */
ret = pnfs_layoutcommit_inode(inode, true);
mutex_unlock(&inode->i_mutex);
return ret;
}
const struct file_operations nfs4_file_operations = {
.llseek = nfs_file_llseek,
.read = do_sync_read,
.write = do_sync_write,
.aio_read = nfs_file_read,
.aio_write = nfs_file_write,
.mmap = nfs_file_mmap,
.open = nfs4_file_open,
.flush = nfs_file_flush,
.release = nfs_file_release,
.fsync = nfs4_file_fsync,
.lock = nfs_lock,
.flock = nfs_flock,
.splice_read = nfs_file_splice_read,
.splice_write = nfs_file_splice_write,
.check_flags = nfs_check_flags,
.setlease = nfs_setlease,
};

View File

@ -205,9 +205,9 @@ static int filelayout_async_handle_error(struct rpc_task *task,
case -EPIPE: case -EPIPE:
dprintk("%s DS connection error %d\n", __func__, dprintk("%s DS connection error %d\n", __func__,
task->tk_status); task->tk_status);
if (!filelayout_test_devid_invalid(devid))
_pnfs_return_layout(inode);
filelayout_mark_devid_invalid(devid); filelayout_mark_devid_invalid(devid);
clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags);
_pnfs_return_layout(inode);
rpc_wake_up(&tbl->slot_tbl_waitq); rpc_wake_up(&tbl->slot_tbl_waitq);
nfs4_ds_disconnect(clp); nfs4_ds_disconnect(clp);
/* fall through */ /* fall through */
@ -351,9 +351,9 @@ static void prepare_to_resend_writes(struct nfs_commit_data *data)
struct nfs_page *first = nfs_list_entry(data->pages.next); struct nfs_page *first = nfs_list_entry(data->pages.next);
data->task.tk_status = 0; data->task.tk_status = 0;
memcpy(data->verf.verifier, first->wb_verf.verifier, memcpy(&data->verf.verifier, &first->wb_verf,
sizeof(first->wb_verf.verifier)); sizeof(data->verf.verifier));
data->verf.verifier[0]++; /* ensure verifier mismatch */ data->verf.verifier.data[0]++; /* ensure verifier mismatch */
} }
static int filelayout_commit_done_cb(struct rpc_task *task, static int filelayout_commit_done_cb(struct rpc_task *task,

View File

@ -728,7 +728,7 @@ get_device_info(struct inode *inode, struct nfs4_deviceid *dev_id, gfp_t gfp_fla
pdev->layout_type = LAYOUT_NFSV4_1_FILES; pdev->layout_type = LAYOUT_NFSV4_1_FILES;
pdev->pages = pages; pdev->pages = pages;
pdev->pgbase = 0; pdev->pgbase = 0;
pdev->pglen = PAGE_SIZE * max_pages; pdev->pglen = max_resp_sz;
pdev->mincount = 0; pdev->mincount = 0;
rc = nfs4_proc_getdeviceinfo(server, pdev); rc = nfs4_proc_getdeviceinfo(server, pdev);

49
fs/nfs/nfs4getroot.c Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
*/
#include <linux/nfs_fs.h>
#include "nfs4_fs.h"
#define NFSDBG_FACILITY NFSDBG_CLIENT
int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh)
{
struct nfs_fsinfo fsinfo;
int ret = -ENOMEM;
dprintk("--> nfs4_get_rootfh()\n");
fsinfo.fattr = nfs_alloc_fattr();
if (fsinfo.fattr == NULL)
goto out;
/* Start by getting the root filehandle from the server */
ret = nfs4_proc_get_rootfh(server, mntfh, &fsinfo);
if (ret < 0) {
dprintk("nfs4_get_rootfh: getroot error = %d\n", -ret);
goto out;
}
if (!(fsinfo.fattr->valid & NFS_ATTR_FATTR_TYPE)
|| !S_ISDIR(fsinfo.fattr->mode)) {
printk(KERN_ERR "nfs4_get_rootfh:"
" getroot encountered non-directory\n");
ret = -ENOTDIR;
goto out;
}
if (fsinfo.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
printk(KERN_ERR "nfs4_get_rootfh:"
" getroot obtained referral\n");
ret = -EREMOTE;
goto out;
}
memcpy(&server->fsid, &fsinfo.fattr->fsid, sizeof(server->fsid));
out:
nfs_free_fattr(fsinfo.fattr);
dprintk("<-- nfs4_get_rootfh() = %d\n", ret);
return ret;
}

View File

@ -43,7 +43,6 @@
#include <linux/printk.h> #include <linux/printk.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/gss_api.h>
#include <linux/nfs.h> #include <linux/nfs.h>
#include <linux/nfs4.h> #include <linux/nfs4.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
@ -259,7 +258,12 @@ static int nfs4_wait_clnt_recover(struct nfs_client *clp)
res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING, res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
nfs_wait_bit_killable, TASK_KILLABLE); nfs_wait_bit_killable, TASK_KILLABLE);
return res; if (res)
return res;
if (clp->cl_cons_state < 0)
return clp->cl_cons_state;
return 0;
} }
static int nfs4_delay(struct rpc_clnt *clnt, long *timeout) static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
@ -294,8 +298,8 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
case 0: case 0:
return 0; return 0;
case -NFS4ERR_OPENMODE: case -NFS4ERR_OPENMODE:
if (inode && nfs_have_delegation(inode, FMODE_READ)) { if (inode && nfs4_have_delegation(inode, FMODE_READ)) {
nfs_inode_return_delegation(inode); nfs4_inode_return_delegation(inode);
exception->retry = 1; exception->retry = 1;
return 0; return 0;
} }
@ -1065,7 +1069,7 @@ static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmo
return; return;
} }
rcu_read_unlock(); rcu_read_unlock();
nfs_inode_return_delegation(inode); nfs4_inode_return_delegation(inode);
} }
static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata) static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
@ -1756,33 +1760,70 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
} }
#if defined(CONFIG_NFS_V4_1) #if defined(CONFIG_NFS_V4_1)
static int nfs41_check_expired_stateid(struct nfs4_state *state, nfs4_stateid *stateid, unsigned int flags) static void nfs41_clear_delegation_stateid(struct nfs4_state *state)
{ {
int status = NFS_OK;
struct nfs_server *server = NFS_SERVER(state->inode); struct nfs_server *server = NFS_SERVER(state->inode);
nfs4_stateid *stateid = &state->stateid;
int status;
if (state->flags & flags) { /* If a state reset has been done, test_stateid is unneeded */
status = nfs41_test_stateid(server, stateid); if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
if (status != NFS_OK) { return;
status = nfs41_test_stateid(server, stateid);
if (status != NFS_OK) {
/* Free the stateid unless the server explicitly
* informs us the stateid is unrecognized. */
if (status != -NFS4ERR_BAD_STATEID)
nfs41_free_stateid(server, stateid); nfs41_free_stateid(server, stateid);
state->flags &= ~flags;
} clear_bit(NFS_DELEGATED_STATE, &state->flags);
}
}
/**
* nfs41_check_open_stateid - possibly free an open stateid
*
* @state: NFSv4 state for an inode
*
* Returns NFS_OK if recovery for this stateid is now finished.
* Otherwise a negative NFS4ERR value is returned.
*/
static int nfs41_check_open_stateid(struct nfs4_state *state)
{
struct nfs_server *server = NFS_SERVER(state->inode);
nfs4_stateid *stateid = &state->stateid;
int status;
/* If a state reset has been done, test_stateid is unneeded */
if ((test_bit(NFS_O_RDONLY_STATE, &state->flags) == 0) &&
(test_bit(NFS_O_WRONLY_STATE, &state->flags) == 0) &&
(test_bit(NFS_O_RDWR_STATE, &state->flags) == 0))
return -NFS4ERR_BAD_STATEID;
status = nfs41_test_stateid(server, stateid);
if (status != NFS_OK) {
/* Free the stateid unless the server explicitly
* informs us the stateid is unrecognized. */
if (status != -NFS4ERR_BAD_STATEID)
nfs41_free_stateid(server, stateid);
clear_bit(NFS_O_RDONLY_STATE, &state->flags);
clear_bit(NFS_O_WRONLY_STATE, &state->flags);
clear_bit(NFS_O_RDWR_STATE, &state->flags);
} }
return status; return status;
} }
static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state) static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
{ {
int deleg_status, open_status; int status;
int deleg_flags = 1 << NFS_DELEGATED_STATE;
int open_flags = (1 << NFS_O_RDONLY_STATE) | (1 << NFS_O_WRONLY_STATE) | (1 << NFS_O_RDWR_STATE);
deleg_status = nfs41_check_expired_stateid(state, &state->stateid, deleg_flags); nfs41_clear_delegation_stateid(state);
open_status = nfs41_check_expired_stateid(state, &state->open_stateid, open_flags); status = nfs41_check_open_stateid(state);
if (status != NFS_OK)
if ((deleg_status == NFS_OK) && (open_status == NFS_OK)) status = nfs4_open_expired(sp, state);
return NFS_OK; return status;
return nfs4_open_expired(sp, state);
} }
#endif #endif
@ -2375,11 +2416,15 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
int i, len, status = 0; int i, len, status = 0;
rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS]; rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS];
len = gss_mech_list_pseudoflavors(&flav_array[0]); len = rpcauth_list_flavors(flav_array, ARRAY_SIZE(flav_array));
flav_array[len] = RPC_AUTH_NULL; BUG_ON(len < 0);
len += 1;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
/* AUTH_UNIX is the default flavor if none was specified,
* thus has already been tried. */
if (flav_array[i] == RPC_AUTH_UNIX)
continue;
status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]); status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]);
if (status == -NFS4ERR_WRONGSEC || status == -EACCES) if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
continue; continue;
@ -2766,9 +2811,7 @@ static int nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry)
* *
* In the case of WRITE, we also want to put the GETATTR after * In the case of WRITE, we also want to put the GETATTR after
* the operation -- in this case because we want to make sure * the operation -- in this case because we want to make sure
* we get the post-operation mtime and size. This means that * we get the post-operation mtime and size.
* we can't use xdr_encode_pages() as written: we need a variant
* of it which would leave room in the 'tail' iovec.
* *
* Both of these changes to the XDR layer would in fact be quite * Both of these changes to the XDR layer would in fact be quite
* minor, but I decided to leave them for a subsequent patch. * minor, but I decided to leave them for a subsequent patch.
@ -2821,7 +2864,9 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
return PTR_ERR(ctx); return PTR_ERR(ctx);
sattr->ia_mode &= ~current_umask(); sattr->ia_mode &= ~current_umask();
state = nfs4_do_open(dir, dentry, ctx->mode, flags, sattr, ctx->cred, NULL); state = nfs4_do_open(dir, dentry, ctx->mode,
flags, sattr, ctx->cred,
&ctx->mdsthreshold);
d_drop(dentry); d_drop(dentry);
if (IS_ERR(state)) { if (IS_ERR(state)) {
status = PTR_ERR(state); status = PTR_ERR(state);
@ -3315,8 +3360,14 @@ static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, str
static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo) static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
{ {
int error;
nfs_fattr_init(fsinfo->fattr); nfs_fattr_init(fsinfo->fattr);
return nfs4_do_fsinfo(server, fhandle, fsinfo); error = nfs4_do_fsinfo(server, fhandle, fsinfo);
if (error == 0)
set_pnfs_layoutdriver(server, fhandle, fsinfo->layouttype);
return error;
} }
static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle, static int _nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
@ -3443,7 +3494,7 @@ bool nfs4_write_need_cache_consistency_data(const struct nfs_write_data *data)
/* Otherwise, request attributes if and only if we don't hold /* Otherwise, request attributes if and only if we don't hold
* a delegation * a delegation
*/ */
return nfs_have_delegation(hdr->inode, FMODE_READ) == 0; return nfs4_have_delegation(hdr->inode, FMODE_READ) == 0;
} }
static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg) static void nfs4_proc_write_setup(struct nfs_write_data *data, struct rpc_message *msg)
@ -3732,7 +3783,8 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
.rpc_argp = &args, .rpc_argp = &args,
.rpc_resp = &res, .rpc_resp = &res,
}; };
int ret = -ENOMEM, npages, i, acl_len = 0; int ret = -ENOMEM, npages, i;
size_t acl_len = 0;
npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT; npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
/* As long as we're doing a round trip to the server anyway, /* As long as we're doing a round trip to the server anyway,
@ -3847,7 +3899,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase); i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
if (i < 0) if (i < 0)
return i; return i;
nfs_inode_return_delegation(inode); nfs4_inode_return_delegation(inode);
ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1); ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
/* /*
@ -3961,6 +4013,16 @@ static void nfs4_init_boot_verifier(const struct nfs_client *clp,
memcpy(bootverf->data, verf, sizeof(bootverf->data)); memcpy(bootverf->data, verf, sizeof(bootverf->data));
} }
/**
* nfs4_proc_setclientid - Negotiate client ID
* @clp: state data structure
* @program: RPC program for NFSv4 callback service
* @port: IP port number for NFS4 callback service
* @cred: RPC credential to use for this call
* @res: where to place the result
*
* Returns zero, a negative errno, or a negative NFS4ERR status code.
*/
int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
unsigned short port, struct rpc_cred *cred, unsigned short port, struct rpc_cred *cred,
struct nfs4_setclientid_res *res) struct nfs4_setclientid_res *res)
@ -3977,44 +4039,44 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
.rpc_resp = res, .rpc_resp = res,
.rpc_cred = cred, .rpc_cred = cred,
}; };
int loop = 0;
int status; int status;
/* nfs_client_id4 */
nfs4_init_boot_verifier(clp, &sc_verifier); nfs4_init_boot_verifier(clp, &sc_verifier);
rcu_read_lock();
for(;;) { setclientid.sc_name_len = scnprintf(setclientid.sc_name,
rcu_read_lock(); sizeof(setclientid.sc_name), "%s/%s %s",
setclientid.sc_name_len = scnprintf(setclientid.sc_name, clp->cl_ipaddr,
sizeof(setclientid.sc_name), "%s/%s %s %s %u", rpc_peeraddr2str(clp->cl_rpcclient,
clp->cl_ipaddr, RPC_DISPLAY_ADDR),
rpc_peeraddr2str(clp->cl_rpcclient, rpc_peeraddr2str(clp->cl_rpcclient,
RPC_DISPLAY_ADDR), RPC_DISPLAY_PROTO));
rpc_peeraddr2str(clp->cl_rpcclient, /* cb_client4 */
RPC_DISPLAY_PROTO), setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
clp->cl_rpcclient->cl_auth->au_ops->au_name,
clp->cl_id_uniquifier);
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
sizeof(setclientid.sc_netid), sizeof(setclientid.sc_netid),
rpc_peeraddr2str(clp->cl_rpcclient, rpc_peeraddr2str(clp->cl_rpcclient,
RPC_DISPLAY_NETID)); RPC_DISPLAY_NETID));
setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr, rcu_read_unlock();
setclientid.sc_uaddr_len = scnprintf(setclientid.sc_uaddr,
sizeof(setclientid.sc_uaddr), "%s.%u.%u", sizeof(setclientid.sc_uaddr), "%s.%u.%u",
clp->cl_ipaddr, port >> 8, port & 255); clp->cl_ipaddr, port >> 8, port & 255);
rcu_read_unlock();
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); dprintk("NFS call setclientid auth=%s, '%.*s'\n",
if (status != -NFS4ERR_CLID_INUSE) clp->cl_rpcclient->cl_auth->au_ops->au_name,
break; setclientid.sc_name_len, setclientid.sc_name);
if (loop != 0) { status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
++clp->cl_id_uniquifier; dprintk("NFS reply setclientid: %d\n", status);
break;
}
++loop;
ssleep(clp->cl_lease_time / HZ + 1);
}
return status; return status;
} }
/**
* nfs4_proc_setclientid_confirm - Confirm client ID
* @clp: state data structure
* @res: result of a previous SETCLIENTID
* @cred: RPC credential to use for this call
*
* Returns zero, a negative errno, or a negative NFS4ERR status code.
*/
int nfs4_proc_setclientid_confirm(struct nfs_client *clp, int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
struct nfs4_setclientid_res *arg, struct nfs4_setclientid_res *arg,
struct rpc_cred *cred) struct rpc_cred *cred)
@ -4029,6 +4091,9 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
unsigned long now; unsigned long now;
int status; int status;
dprintk("NFS call setclientid_confirm auth=%s, (client ID %llx)\n",
clp->cl_rpcclient->cl_auth->au_ops->au_name,
clp->cl_clientid);
now = jiffies; now = jiffies;
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
if (status == 0) { if (status == 0) {
@ -4037,6 +4102,7 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
clp->cl_last_renewal = now; clp->cl_last_renewal = now;
spin_unlock(&clp->cl_lock); spin_unlock(&clp->cl_lock);
} }
dprintk("NFS reply setclientid_confirm: %d\n", status);
return status; return status;
} }
@ -4681,9 +4747,17 @@ out:
} }
#if defined(CONFIG_NFS_V4_1) #if defined(CONFIG_NFS_V4_1)
/**
* nfs41_check_expired_locks - possibly free a lock stateid
*
* @state: NFSv4 state for an inode
*
* Returns NFS_OK if recovery for this stateid is now finished.
* Otherwise a negative NFS4ERR value is returned.
*/
static int nfs41_check_expired_locks(struct nfs4_state *state) static int nfs41_check_expired_locks(struct nfs4_state *state)
{ {
int status, ret = NFS_OK; int status, ret = -NFS4ERR_BAD_STATEID;
struct nfs4_lock_state *lsp; struct nfs4_lock_state *lsp;
struct nfs_server *server = NFS_SERVER(state->inode); struct nfs_server *server = NFS_SERVER(state->inode);
@ -4691,7 +4765,11 @@ static int nfs41_check_expired_locks(struct nfs4_state *state)
if (lsp->ls_flags & NFS_LOCK_INITIALIZED) { if (lsp->ls_flags & NFS_LOCK_INITIALIZED) {
status = nfs41_test_stateid(server, &lsp->ls_stateid); status = nfs41_test_stateid(server, &lsp->ls_stateid);
if (status != NFS_OK) { if (status != NFS_OK) {
nfs41_free_stateid(server, &lsp->ls_stateid); /* Free the stateid unless the server
* informs us the stateid is unrecognized. */
if (status != -NFS4ERR_BAD_STATEID)
nfs41_free_stateid(server,
&lsp->ls_stateid);
lsp->ls_flags &= ~NFS_LOCK_INITIALIZED; lsp->ls_flags &= ~NFS_LOCK_INITIALIZED;
ret = status; ret = status;
} }
@ -4707,9 +4785,9 @@ static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *reques
if (test_bit(LK_STATE_IN_USE, &state->flags)) if (test_bit(LK_STATE_IN_USE, &state->flags))
status = nfs41_check_expired_locks(state); status = nfs41_check_expired_locks(state);
if (status == NFS_OK) if (status != NFS_OK)
return status; status = nfs4_lock_expired(state, request);
return nfs4_lock_expired(state, request); return status;
} }
#endif #endif
@ -4807,7 +4885,7 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
* Don't rely on the VFS having checked the file open mode, * Don't rely on the VFS having checked the file open mode,
* since it won't do this for flock() locks. * since it won't do this for flock() locks.
*/ */
switch (request->fl_type & (F_RDLCK|F_WRLCK|F_UNLCK)) { switch (request->fl_type) {
case F_RDLCK: case F_RDLCK:
if (!(filp->f_mode & FMODE_READ)) if (!(filp->f_mode & FMODE_READ))
return -EBADF; return -EBADF;
@ -5168,6 +5246,8 @@ out:
/* /*
* nfs4_proc_exchange_id() * nfs4_proc_exchange_id()
* *
* Returns zero, a negative errno, or a negative NFS4ERR status code.
*
* Since the clientid has expired, all compounds using sessions * Since the clientid has expired, all compounds using sessions
* associated with the stale clientid will be returning * associated with the stale clientid will be returning
* NFS4ERR_BADSESSION in the sequence operation, and will therefore * NFS4ERR_BADSESSION in the sequence operation, and will therefore
@ -5192,16 +5272,14 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
.rpc_cred = cred, .rpc_cred = cred,
}; };
dprintk("--> %s\n", __func__);
BUG_ON(clp == NULL);
nfs4_init_boot_verifier(clp, &verifier); nfs4_init_boot_verifier(clp, &verifier);
args.id_len = scnprintf(args.id, sizeof(args.id), args.id_len = scnprintf(args.id, sizeof(args.id),
"%s/%s/%u", "%s/%s",
clp->cl_ipaddr, clp->cl_ipaddr,
clp->cl_rpcclient->cl_nodename, clp->cl_rpcclient->cl_nodename);
clp->cl_rpcclient->cl_auth->au_flavor); dprintk("NFS call exchange_id auth=%s, '%.*s'\n",
clp->cl_rpcclient->cl_auth->au_ops->au_name,
args.id_len, args.id);
res.server_owner = kzalloc(sizeof(struct nfs41_server_owner), res.server_owner = kzalloc(sizeof(struct nfs41_server_owner),
GFP_NOFS); GFP_NOFS);
@ -5264,12 +5342,12 @@ out_server_scope:
kfree(res.server_scope); kfree(res.server_scope);
out: out:
if (clp->cl_implid != NULL) if (clp->cl_implid != NULL)
dprintk("%s: Server Implementation ID: " dprintk("NFS reply exchange_id: Server Implementation ID: "
"domain: %s, name: %s, date: %llu,%u\n", "domain: %s, name: %s, date: %llu,%u\n",
__func__, clp->cl_implid->domain, clp->cl_implid->name, clp->cl_implid->domain, clp->cl_implid->name,
clp->cl_implid->date.seconds, clp->cl_implid->date.seconds,
clp->cl_implid->date.nseconds); clp->cl_implid->date.nseconds);
dprintk("<-- %s status= %d\n", __func__, status); dprintk("NFS reply exchange_id: %d\n", status);
return status; return status;
} }
@ -6570,22 +6648,36 @@ static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
.rpc_resp = &res, .rpc_resp = &res,
}; };
dprintk("NFS call test_stateid %p\n", stateid);
nfs41_init_sequence(&args.seq_args, &res.seq_res, 0); nfs41_init_sequence(&args.seq_args, &res.seq_res, 0);
status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 1); status = nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 1);
if (status != NFS_OK) {
if (status == NFS_OK) dprintk("NFS reply test_stateid: failed, %d\n", status);
return res.status; return status;
return status; }
dprintk("NFS reply test_stateid: succeeded, %d\n", -res.status);
return -res.status;
} }
/**
* nfs41_test_stateid - perform a TEST_STATEID operation
*
* @server: server / transport on which to perform the operation
* @stateid: state ID to test
*
* Returns NFS_OK if the server recognizes that "stateid" is valid.
* Otherwise a negative NFS4ERR value is returned if the operation
* failed or the state ID is not currently valid.
*/
static int nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid) static int nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
{ {
struct nfs4_exception exception = { }; struct nfs4_exception exception = { };
int err; int err;
do { do {
err = nfs4_handle_exception(server, err = _nfs41_test_stateid(server, stateid);
_nfs41_test_stateid(server, stateid), if (err != -NFS4ERR_DELAY)
&exception); break;
nfs4_handle_exception(server, err, &exception);
} while (exception.retry); } while (exception.retry);
return err; return err;
} }
@ -6601,19 +6693,34 @@ static int _nfs4_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
.rpc_argp = &args, .rpc_argp = &args,
.rpc_resp = &res, .rpc_resp = &res,
}; };
int status;
dprintk("NFS call free_stateid %p\n", stateid);
nfs41_init_sequence(&args.seq_args, &res.seq_res, 0); nfs41_init_sequence(&args.seq_args, &res.seq_res, 0);
return nfs4_call_sync_sequence(server->client, server, &msg, &args.seq_args, &res.seq_res, 1); status = nfs4_call_sync_sequence(server->client, server, &msg,
&args.seq_args, &res.seq_res, 1);
dprintk("NFS reply free_stateid: %d\n", status);
return status;
} }
/**
* nfs41_free_stateid - perform a FREE_STATEID operation
*
* @server: server / transport on which to perform the operation
* @stateid: state ID to release
*
* Returns NFS_OK if the server freed "stateid". Otherwise a
* negative NFS4ERR value is returned.
*/
static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid) static int nfs41_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
{ {
struct nfs4_exception exception = { }; struct nfs4_exception exception = { };
int err; int err;
do { do {
err = nfs4_handle_exception(server, err = _nfs4_free_stateid(server, stateid);
_nfs4_free_stateid(server, stateid), if (err != -NFS4ERR_DELAY)
&exception); break;
nfs4_handle_exception(server, err, &exception);
} while (exception.retry); } while (exception.retry);
return err; return err;
} }
@ -6725,6 +6832,26 @@ const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
#endif #endif
}; };
const struct inode_operations nfs4_dir_inode_operations = {
.create = nfs_create,
.lookup = nfs_lookup,
.atomic_open = nfs_atomic_open,
.link = nfs_link,
.unlink = nfs_unlink,
.symlink = nfs_symlink,
.mkdir = nfs_mkdir,
.rmdir = nfs_rmdir,
.mknod = nfs_mknod,
.rename = nfs_rename,
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
.getxattr = generic_getxattr,
.setxattr = generic_setxattr,
.listxattr = generic_listxattr,
.removexattr = generic_removexattr,
};
static const struct inode_operations nfs4_file_inode_operations = { static const struct inode_operations nfs4_file_inode_operations = {
.permission = nfs_permission, .permission = nfs_permission,
.getattr = nfs_getattr, .getattr = nfs_getattr,
@ -6769,9 +6896,11 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.set_capabilities = nfs4_server_capabilities, .set_capabilities = nfs4_server_capabilities,
.decode_dirent = nfs4_decode_dirent, .decode_dirent = nfs4_decode_dirent,
.read_setup = nfs4_proc_read_setup, .read_setup = nfs4_proc_read_setup,
.read_pageio_init = pnfs_pageio_init_read,
.read_rpc_prepare = nfs4_proc_read_rpc_prepare, .read_rpc_prepare = nfs4_proc_read_rpc_prepare,
.read_done = nfs4_read_done, .read_done = nfs4_read_done,
.write_setup = nfs4_proc_write_setup, .write_setup = nfs4_proc_write_setup,
.write_pageio_init = pnfs_pageio_init_write,
.write_rpc_prepare = nfs4_proc_write_rpc_prepare, .write_rpc_prepare = nfs4_proc_write_rpc_prepare,
.write_done = nfs4_write_done, .write_done = nfs4_write_done,
.commit_setup = nfs4_proc_commit_setup, .commit_setup = nfs4_proc_commit_setup,
@ -6781,7 +6910,11 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
.clear_acl_cache = nfs4_zap_acl_attr, .clear_acl_cache = nfs4_zap_acl_attr,
.close_context = nfs4_close_context, .close_context = nfs4_close_context,
.open_context = nfs4_atomic_open, .open_context = nfs4_atomic_open,
.have_delegation = nfs4_have_delegation,
.return_delegation = nfs4_inode_return_delegation,
.alloc_client = nfs4_alloc_client,
.init_client = nfs4_init_client, .init_client = nfs4_init_client,
.free_client = nfs4_free_client,
}; };
static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = { static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {

View File

@ -1606,10 +1606,15 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
return -ESERVERFAULT; return -ESERVERFAULT;
/* Lease confirmation error: retry after purging the lease */ /* Lease confirmation error: retry after purging the lease */
ssleep(1); ssleep(1);
case -NFS4ERR_CLID_INUSE:
case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_STALE_CLIENTID:
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
break; break;
case -NFS4ERR_CLID_INUSE:
pr_err("NFS: Server %s reports our clientid is in use\n",
clp->cl_hostname);
nfs_mark_client_ready(clp, -EPERM);
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
return -EPERM;
case -EACCES: case -EACCES:
if (clp->cl_machine_cred == NULL) if (clp->cl_machine_cred == NULL)
return -EACCES; return -EACCES;
@ -1642,7 +1647,7 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
return 0; return 0;
} }
static int nfs4_reclaim_lease(struct nfs_client *clp) static int nfs4_establish_lease(struct nfs_client *clp)
{ {
struct rpc_cred *cred; struct rpc_cred *cred;
const struct nfs4_state_recovery_ops *ops = const struct nfs4_state_recovery_ops *ops =
@ -1655,7 +1660,41 @@ static int nfs4_reclaim_lease(struct nfs_client *clp)
status = ops->establish_clid(clp, cred); status = ops->establish_clid(clp, cred);
put_rpccred(cred); put_rpccred(cred);
if (status != 0) if (status != 0)
return status;
pnfs_destroy_all_layouts(clp);
return 0;
}
/*
* Returns zero or a negative errno. NFS4ERR values are converted
* to local errno values.
*/
static int nfs4_reclaim_lease(struct nfs_client *clp)
{
int status;
status = nfs4_establish_lease(clp);
if (status < 0)
return nfs4_handle_reclaim_lease_error(clp, status); return nfs4_handle_reclaim_lease_error(clp, status);
if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state))
nfs4_state_start_reclaim_nograce(clp);
if (!test_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state))
set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
return 0;
}
static int nfs4_purge_lease(struct nfs_client *clp)
{
int status;
status = nfs4_establish_lease(clp);
if (status < 0)
return nfs4_handle_reclaim_lease_error(clp, status);
clear_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
nfs4_state_start_reclaim_nograce(clp);
return 0; return 0;
} }
@ -1764,6 +1803,8 @@ static int nfs4_reset_session(struct nfs_client *clp)
struct rpc_cred *cred; struct rpc_cred *cred;
int status; int status;
if (!nfs4_has_session(clp))
return 0;
nfs4_begin_drain_session(clp); nfs4_begin_drain_session(clp);
cred = nfs4_get_exchange_id_cred(clp); cred = nfs4_get_exchange_id_cred(clp);
status = nfs4_proc_destroy_session(clp->cl_session, cred); status = nfs4_proc_destroy_session(clp->cl_session, cred);
@ -1792,12 +1833,14 @@ out:
static int nfs4_recall_slot(struct nfs_client *clp) static int nfs4_recall_slot(struct nfs_client *clp)
{ {
struct nfs4_slot_table *fc_tbl = &clp->cl_session->fc_slot_table; struct nfs4_slot_table *fc_tbl;
struct nfs4_channel_attrs *fc_attrs = &clp->cl_session->fc_attrs;
struct nfs4_slot *new, *old; struct nfs4_slot *new, *old;
int i; int i;
if (!nfs4_has_session(clp))
return 0;
nfs4_begin_drain_session(clp); nfs4_begin_drain_session(clp);
fc_tbl = &clp->cl_session->fc_slot_table;
new = kmalloc(fc_tbl->target_max_slots * sizeof(struct nfs4_slot), new = kmalloc(fc_tbl->target_max_slots * sizeof(struct nfs4_slot),
GFP_NOFS); GFP_NOFS);
if (!new) if (!new)
@ -1810,11 +1853,10 @@ static int nfs4_recall_slot(struct nfs_client *clp)
fc_tbl->slots = new; fc_tbl->slots = new;
fc_tbl->max_slots = fc_tbl->target_max_slots; fc_tbl->max_slots = fc_tbl->target_max_slots;
fc_tbl->target_max_slots = 0; fc_tbl->target_max_slots = 0;
fc_attrs->max_reqs = fc_tbl->max_slots; clp->cl_session->fc_attrs.max_reqs = fc_tbl->max_slots;
spin_unlock(&fc_tbl->slot_tbl_lock); spin_unlock(&fc_tbl->slot_tbl_lock);
kfree(old); kfree(old);
nfs4_end_drain_session(clp);
return 0; return 0;
} }
@ -1823,6 +1865,8 @@ static int nfs4_bind_conn_to_session(struct nfs_client *clp)
struct rpc_cred *cred; struct rpc_cred *cred;
int ret; int ret;
if (!nfs4_has_session(clp))
return 0;
nfs4_begin_drain_session(clp); nfs4_begin_drain_session(clp);
cred = nfs4_get_exchange_id_cred(clp); cred = nfs4_get_exchange_id_cred(clp);
ret = nfs4_proc_bind_conn_to_session(clp, cred); ret = nfs4_proc_bind_conn_to_session(clp, cred);
@ -1857,37 +1901,29 @@ static int nfs4_bind_conn_to_session(struct nfs_client *clp)
static void nfs4_state_manager(struct nfs_client *clp) static void nfs4_state_manager(struct nfs_client *clp)
{ {
int status = 0; int status = 0;
const char *section = "", *section_sep = "";
/* Ensure exclusive access to NFSv4 state */ /* Ensure exclusive access to NFSv4 state */
do { do {
if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) { if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
status = nfs4_reclaim_lease(clp); section = "purge state";
status = nfs4_purge_lease(clp);
if (status < 0) if (status < 0)
goto out_error; goto out_error;
clear_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state); continue;
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
} }
if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) { if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
section = "lease expired";
/* We're going to have to re-establish a clientid */ /* We're going to have to re-establish a clientid */
status = nfs4_reclaim_lease(clp); status = nfs4_reclaim_lease(clp);
if (status < 0) if (status < 0)
goto out_error; goto out_error;
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) continue;
continue;
clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
if (test_and_clear_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH,
&clp->cl_state))
nfs4_state_start_reclaim_nograce(clp);
else
set_bit(NFS4CLNT_RECLAIM_REBOOT,
&clp->cl_state);
pnfs_destroy_all_layouts(clp);
} }
if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) { if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) {
section = "check lease";
status = nfs4_check_lease(clp); status = nfs4_check_lease(clp);
if (status < 0) if (status < 0)
goto out_error; goto out_error;
@ -1896,8 +1932,8 @@ static void nfs4_state_manager(struct nfs_client *clp)
} }
/* Initialize or reset the session */ /* Initialize or reset the session */
if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) {
&& nfs4_has_session(clp)) { section = "reset session";
status = nfs4_reset_session(clp); status = nfs4_reset_session(clp);
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
continue; continue;
@ -1907,15 +1943,26 @@ static void nfs4_state_manager(struct nfs_client *clp)
/* Send BIND_CONN_TO_SESSION */ /* Send BIND_CONN_TO_SESSION */
if (test_and_clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION, if (test_and_clear_bit(NFS4CLNT_BIND_CONN_TO_SESSION,
&clp->cl_state) && nfs4_has_session(clp)) { &clp->cl_state)) {
section = "bind conn to session";
status = nfs4_bind_conn_to_session(clp); status = nfs4_bind_conn_to_session(clp);
if (status < 0) if (status < 0)
goto out_error; goto out_error;
continue; continue;
} }
/* Recall session slots */
if (test_and_clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state)) {
section = "recall slot";
status = nfs4_recall_slot(clp);
if (status < 0)
goto out_error;
continue;
}
/* First recover reboot state... */ /* First recover reboot state... */
if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) { if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
section = "reclaim reboot";
status = nfs4_do_reclaim(clp, status = nfs4_do_reclaim(clp,
clp->cl_mvops->reboot_recovery_ops); clp->cl_mvops->reboot_recovery_ops);
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) || if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) ||
@ -1930,6 +1977,7 @@ static void nfs4_state_manager(struct nfs_client *clp)
/* Now recover expired state... */ /* Now recover expired state... */
if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) { if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
section = "reclaim nograce";
status = nfs4_do_reclaim(clp, status = nfs4_do_reclaim(clp,
clp->cl_mvops->nograce_recovery_ops); clp->cl_mvops->nograce_recovery_ops);
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) || if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) ||
@ -1945,15 +1993,6 @@ static void nfs4_state_manager(struct nfs_client *clp)
nfs_client_return_marked_delegations(clp); nfs_client_return_marked_delegations(clp);
continue; continue;
} }
/* Recall session slots */
if (test_and_clear_bit(NFS4CLNT_RECALL_SLOT, &clp->cl_state)
&& nfs4_has_session(clp)) {
status = nfs4_recall_slot(clp);
if (status < 0)
goto out_error;
continue;
}
nfs4_clear_state_manager_bit(clp); nfs4_clear_state_manager_bit(clp);
/* Did we race with an attempt to give us more work? */ /* Did we race with an attempt to give us more work? */
@ -1964,8 +2003,11 @@ static void nfs4_state_manager(struct nfs_client *clp)
} while (atomic_read(&clp->cl_count) > 1); } while (atomic_read(&clp->cl_count) > 1);
return; return;
out_error: out_error:
pr_warn_ratelimited("NFS: state manager failed on NFSv4 server %s" if (strlen(section))
" with error %d\n", clp->cl_hostname, -status); section_sep = ": ";
pr_warn_ratelimited("NFS: state manager%s%s failed on NFSv4 server %s"
" with error %d\n", section_sep, section,
clp->cl_hostname, -status);
nfs4_end_drain_session(clp); nfs4_end_drain_session(clp);
nfs4_clear_state_manager_bit(clp); nfs4_clear_state_manager_bit(clp);
} }

360
fs/nfs/nfs4super.c Normal file
View File

@ -0,0 +1,360 @@
/*
* Copyright (c) 2012 Bryan Schumaker <bjschuma@netapp.com>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/nfs_idmap.h>
#include <linux/nfs4_mount.h>
#include <linux/nfs_fs.h>
#include "internal.h"
#include "nfs4_fs.h"
#define NFSDBG_FACILITY NFSDBG_VFS
static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
static struct dentry *nfs4_xdev_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
static struct file_system_type nfs4_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs_fs_mount,
.kill_sb = nfs_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
static struct file_system_type nfs4_remote_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs4_remote_mount,
.kill_sb = nfs_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
struct file_system_type nfs4_xdev_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs4_xdev_mount,
.kill_sb = nfs_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
static struct file_system_type nfs4_remote_referral_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs4_remote_referral_mount,
.kill_sb = nfs_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
struct file_system_type nfs4_referral_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs4_referral_mount,
.kill_sb = nfs_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
static const struct super_operations nfs4_sops = {
.alloc_inode = nfs_alloc_inode,
.destroy_inode = nfs_destroy_inode,
.write_inode = nfs4_write_inode,
.put_super = nfs_put_super,
.statfs = nfs_statfs,
.evict_inode = nfs4_evict_inode,
.umount_begin = nfs_umount_begin,
.show_options = nfs_show_options,
.show_devname = nfs_show_devname,
.show_path = nfs_show_path,
.show_stats = nfs_show_stats,
.remount_fs = nfs_remount,
};
/*
* Set up an NFS4 superblock
*/
static void nfs4_fill_super(struct super_block *sb,
struct nfs_mount_info *mount_info)
{
sb->s_time_gran = 1;
sb->s_op = &nfs4_sops;
/*
* The VFS shouldn't apply the umask to mode bits. We will do
* so ourselves when necessary.
*/
sb->s_flags |= MS_POSIXACL;
sb->s_xattr = nfs4_xattr_handlers;
nfs_initialise_sb(sb);
}
/*
* Get the superblock for the NFS4 root partition
*/
static struct dentry *
nfs4_remote_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *info)
{
struct nfs_mount_info *mount_info = info;
struct nfs_server *server;
struct dentry *mntroot = ERR_PTR(-ENOMEM);
mount_info->fill_super = nfs4_fill_super;
mount_info->set_security = nfs_set_sb_security;
/* Get a volume representation */
server = nfs4_create_server(mount_info->parsed, mount_info->mntfh);
if (IS_ERR(server)) {
mntroot = ERR_CAST(server);
goto out;
}
mntroot = nfs_fs_mount_common(fs_type, server, flags, dev_name, mount_info);
out:
return mntroot;
}
static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
int flags, void *data, const char *hostname)
{
struct vfsmount *root_mnt;
char *root_devname;
size_t len;
len = strlen(hostname) + 5;
root_devname = kmalloc(len, GFP_KERNEL);
if (root_devname == NULL)
return ERR_PTR(-ENOMEM);
/* Does hostname needs to be enclosed in brackets? */
if (strchr(hostname, ':'))
snprintf(root_devname, len, "[%s]:/", hostname);
else
snprintf(root_devname, len, "%s:/", hostname);
root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data);
kfree(root_devname);
return root_mnt;
}
struct nfs_referral_count {
struct list_head list;
const struct task_struct *task;
unsigned int referral_count;
};
static LIST_HEAD(nfs_referral_count_list);
static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
static struct nfs_referral_count *nfs_find_referral_count(void)
{
struct nfs_referral_count *p;
list_for_each_entry(p, &nfs_referral_count_list, list) {
if (p->task == current)
return p;
}
return NULL;
}
#define NFS_MAX_NESTED_REFERRALS 2
static int nfs_referral_loop_protect(void)
{
struct nfs_referral_count *p, *new;
int ret = -ENOMEM;
new = kmalloc(sizeof(*new), GFP_KERNEL);
if (!new)
goto out;
new->task = current;
new->referral_count = 1;
ret = 0;
spin_lock(&nfs_referral_count_list_lock);
p = nfs_find_referral_count();
if (p != NULL) {
if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
ret = -ELOOP;
else
p->referral_count++;
} else {
list_add(&new->list, &nfs_referral_count_list);
new = NULL;
}
spin_unlock(&nfs_referral_count_list_lock);
kfree(new);
out:
return ret;
}
static void nfs_referral_loop_unprotect(void)
{
struct nfs_referral_count *p;
spin_lock(&nfs_referral_count_list_lock);
p = nfs_find_referral_count();
p->referral_count--;
if (p->referral_count == 0)
list_del(&p->list);
else
p = NULL;
spin_unlock(&nfs_referral_count_list_lock);
kfree(p);
}
static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
const char *export_path)
{
struct dentry *dentry;
int err;
if (IS_ERR(root_mnt))
return ERR_CAST(root_mnt);
err = nfs_referral_loop_protect();
if (err) {
mntput(root_mnt);
return ERR_PTR(err);
}
dentry = mount_subtree(root_mnt, export_path);
nfs_referral_loop_unprotect();
return dentry;
}
struct dentry *nfs4_try_mount(int flags, const char *dev_name,
struct nfs_mount_info *mount_info)
{
char *export_path;
struct vfsmount *root_mnt;
struct dentry *res;
struct nfs_parsed_mount_data *data = mount_info->parsed;
dfprintk(MOUNT, "--> nfs4_try_mount()\n");
mount_info->fill_super = nfs4_fill_super;
export_path = data->nfs_server.export_path;
data->nfs_server.export_path = "/";
root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
data->nfs_server.hostname);
data->nfs_server.export_path = export_path;
res = nfs_follow_remote_path(root_mnt, export_path);
dfprintk(MOUNT, "<-- nfs4_try_mount() = %ld%s\n",
IS_ERR(res) ? PTR_ERR(res) : 0,
IS_ERR(res) ? " [error]" : "");
return res;
}
/*
* Clone an NFS4 server record on xdev traversal (FSID-change)
*/
static struct dentry *
nfs4_xdev_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *raw_data)
{
struct nfs_mount_info mount_info = {
.fill_super = nfs_clone_super,
.set_security = nfs_clone_sb_security,
.cloned = raw_data,
};
return nfs_xdev_mount_common(&nfs4_fs_type, flags, dev_name, &mount_info);
}
static struct dentry *
nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *raw_data)
{
struct nfs_mount_info mount_info = {
.fill_super = nfs4_fill_super,
.set_security = nfs_clone_sb_security,
.cloned = raw_data,
};
struct nfs_server *server;
struct dentry *mntroot = ERR_PTR(-ENOMEM);
dprintk("--> nfs4_referral_get_sb()\n");
mount_info.mntfh = nfs_alloc_fhandle();
if (mount_info.cloned == NULL || mount_info.mntfh == NULL)
goto out;
/* create a new volume representation */
server = nfs4_create_referral_server(mount_info.cloned, mount_info.mntfh);
if (IS_ERR(server)) {
mntroot = ERR_CAST(server);
goto out;
}
mntroot = nfs_fs_mount_common(&nfs4_fs_type, server, flags, dev_name, &mount_info);
out:
nfs_free_fhandle(mount_info.mntfh);
return mntroot;
}
/*
* Create an NFS4 server record on referral traversal
*/
static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data)
{
struct nfs_clone_mount *data = raw_data;
char *export_path;
struct vfsmount *root_mnt;
struct dentry *res;
dprintk("--> nfs4_referral_mount()\n");
export_path = data->mnt_path;
data->mnt_path = "/";
root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
flags, data, data->hostname);
data->mnt_path = export_path;
res = nfs_follow_remote_path(root_mnt, export_path);
dprintk("<-- nfs4_referral_mount() = %ld%s\n",
IS_ERR(res) ? PTR_ERR(res) : 0,
IS_ERR(res) ? " [error]" : "");
return res;
}
int __init init_nfs_v4(void)
{
int err;
err = nfs_idmap_init();
if (err)
goto out;
err = nfs4_register_sysctl();
if (err)
goto out1;
err = register_filesystem(&nfs4_fs_type);
if (err < 0)
goto out2;
return 0;
out2:
nfs4_unregister_sysctl();
out1:
nfs_idmap_quit();
out:
return err;
}
void exit_nfs_v4(void)
{
unregister_filesystem(&nfs4_fs_type);
nfs4_unregister_sysctl();
nfs_idmap_quit();
}

68
fs/nfs/nfs4sysctl.c Normal file
View File

@ -0,0 +1,68 @@
/*
* linux/fs/nfs/nfs4sysctl.c
*
* Sysctl interface to NFS v4 parameters
*
* Copyright (c) 2006 Trond Myklebust <Trond.Myklebust@netapp.com>
*/
#include <linux/sysctl.h>
#include <linux/nfs_idmap.h>
#include <linux/nfs_fs.h>
#include "callback.h"
static const int nfs_set_port_min = 0;
static const int nfs_set_port_max = 65535;
static struct ctl_table_header *nfs4_callback_sysctl_table;
static ctl_table nfs4_cb_sysctls[] = {
{
.procname = "nfs_callback_tcpport",
.data = &nfs_callback_set_tcpport,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = (int *)&nfs_set_port_min,
.extra2 = (int *)&nfs_set_port_max,
},
{
.procname = "idmap_cache_timeout",
.data = &nfs_idmap_cache_timeout,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
{ }
};
static ctl_table nfs4_cb_sysctl_dir[] = {
{
.procname = "nfs",
.mode = 0555,
.child = nfs4_cb_sysctls,
},
{ }
};
static ctl_table nfs4_cb_sysctl_root[] = {
{
.procname = "fs",
.mode = 0555,
.child = nfs4_cb_sysctl_dir,
},
{ }
};
int nfs4_register_sysctl(void)
{
nfs4_callback_sysctl_table = register_sysctl_table(nfs4_cb_sysctl_root);
if (nfs4_callback_sysctl_table == NULL)
return -ENOMEM;
return 0;
}
void nfs4_unregister_sysctl(void)
{
unregister_sysctl_table(nfs4_callback_sysctl_table);
nfs4_callback_sysctl_table = NULL;
}

View File

@ -1236,7 +1236,7 @@ static void encode_link(struct xdr_stream *xdr, const struct qstr *name, struct
static inline int nfs4_lock_type(struct file_lock *fl, int block) static inline int nfs4_lock_type(struct file_lock *fl, int block)
{ {
if ((fl->fl_type & (F_RDLCK|F_WRLCK|F_UNLCK)) == F_RDLCK) if (fl->fl_type == F_RDLCK)
return block ? NFS4_READW_LT : NFS4_READ_LT; return block ? NFS4_READW_LT : NFS4_READ_LT;
return block ? NFS4_WRITEW_LT : NFS4_WRITE_LT; return block ? NFS4_WRITEW_LT : NFS4_WRITE_LT;
} }
@ -3078,7 +3078,7 @@ out_overflow:
return -EIO; return -EIO;
} }
static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, __be32 **savep) static int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen, unsigned int *savep)
{ {
__be32 *p; __be32 *p;
@ -3086,7 +3086,7 @@ static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen,
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; goto out_overflow;
*attrlen = be32_to_cpup(p); *attrlen = be32_to_cpup(p);
*savep = xdr->p; *savep = xdr_stream_pos(xdr);
return 0; return 0;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr); print_overflow_msg(__func__, xdr);
@ -4068,10 +4068,10 @@ static int decode_attr_time_modify(struct xdr_stream *xdr, uint32_t *bitmap, str
return status; return status;
} }
static int verify_attr_len(struct xdr_stream *xdr, __be32 *savep, uint32_t attrlen) static int verify_attr_len(struct xdr_stream *xdr, unsigned int savep, uint32_t attrlen)
{ {
unsigned int attrwords = XDR_QUADLEN(attrlen); unsigned int attrwords = XDR_QUADLEN(attrlen);
unsigned int nwords = xdr->p - savep; unsigned int nwords = (xdr_stream_pos(xdr) - savep) >> 2;
if (unlikely(attrwords != nwords)) { if (unlikely(attrwords != nwords)) {
dprintk("%s: server returned incorrect attribute length: " dprintk("%s: server returned incorrect attribute length: "
@ -4158,13 +4158,18 @@ static int decode_verifier(struct xdr_stream *xdr, void *verifier)
return decode_opaque_fixed(xdr, verifier, NFS4_VERIFIER_SIZE); return decode_opaque_fixed(xdr, verifier, NFS4_VERIFIER_SIZE);
} }
static int decode_write_verifier(struct xdr_stream *xdr, struct nfs_write_verifier *verifier)
{
return decode_opaque_fixed(xdr, verifier->data, NFS4_VERIFIER_SIZE);
}
static int decode_commit(struct xdr_stream *xdr, struct nfs_commitres *res) static int decode_commit(struct xdr_stream *xdr, struct nfs_commitres *res)
{ {
int status; int status;
status = decode_op_hdr(xdr, OP_COMMIT); status = decode_op_hdr(xdr, OP_COMMIT);
if (!status) if (!status)
status = decode_verifier(xdr, res->verf->verifier); status = decode_write_verifier(xdr, &res->verf->verifier);
return status; return status;
} }
@ -4193,7 +4198,7 @@ out_overflow:
static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res) static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res)
{ {
__be32 *savep; unsigned int savep;
uint32_t attrlen, bitmap[3] = {0}; uint32_t attrlen, bitmap[3] = {0};
int status; int status;
@ -4222,7 +4227,7 @@ xdr_error:
static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat) static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat)
{ {
__be32 *savep; unsigned int savep;
uint32_t attrlen, bitmap[3] = {0}; uint32_t attrlen, bitmap[3] = {0};
int status; int status;
@ -4254,7 +4259,7 @@ xdr_error:
static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf) static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf)
{ {
__be32 *savep; unsigned int savep;
uint32_t attrlen, bitmap[3] = {0}; uint32_t attrlen, bitmap[3] = {0};
int status; int status;
@ -4299,7 +4304,8 @@ out_overflow:
static int decode_first_threshold_item4(struct xdr_stream *xdr, static int decode_first_threshold_item4(struct xdr_stream *xdr,
struct nfs4_threshold *res) struct nfs4_threshold *res)
{ {
__be32 *p, *savep; __be32 *p;
unsigned int savep;
uint32_t bitmap[3] = {0,}, attrlen; uint32_t bitmap[3] = {0,}, attrlen;
int status; int status;
@ -4503,7 +4509,7 @@ static int decode_getfattr_generic(struct xdr_stream *xdr, struct nfs_fattr *fat
struct nfs_fh *fh, struct nfs4_fs_locations *fs_loc, struct nfs_fh *fh, struct nfs4_fs_locations *fs_loc,
const struct nfs_server *server) const struct nfs_server *server)
{ {
__be32 *savep; unsigned int savep;
uint32_t attrlen, uint32_t attrlen,
bitmap[3] = {0}; bitmap[3] = {0};
int status; int status;
@ -4615,7 +4621,7 @@ static int decode_attr_layout_blksize(struct xdr_stream *xdr, uint32_t *bitmap,
static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo) static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo)
{ {
__be32 *savep; unsigned int savep;
uint32_t attrlen, bitmap[3]; uint32_t attrlen, bitmap[3];
int status; int status;
@ -4920,9 +4926,8 @@ static int decode_putrootfh(struct xdr_stream *xdr)
static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_readres *res) static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_readres *res)
{ {
struct kvec *iov = req->rq_rcv_buf.head;
__be32 *p; __be32 *p;
uint32_t count, eof, recvd, hdrlen; uint32_t count, eof, recvd;
int status; int status;
status = decode_op_hdr(xdr, OP_READ); status = decode_op_hdr(xdr, OP_READ);
@ -4933,15 +4938,13 @@ static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_
goto out_overflow; goto out_overflow;
eof = be32_to_cpup(p++); eof = be32_to_cpup(p++);
count = be32_to_cpup(p); count = be32_to_cpup(p);
hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base; recvd = xdr_read_pages(xdr, count);
recvd = req->rq_rcv_buf.len - hdrlen;
if (count > recvd) { if (count > recvd) {
dprintk("NFS: server cheating in read reply: " dprintk("NFS: server cheating in read reply: "
"count %u > recvd %u\n", count, recvd); "count %u > recvd %u\n", count, recvd);
count = recvd; count = recvd;
eof = 0; eof = 0;
} }
xdr_read_pages(xdr, count);
res->eof = eof; res->eof = eof;
res->count = count; res->count = count;
return 0; return 0;
@ -4952,10 +4955,6 @@ out_overflow:
static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir) static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir)
{ {
struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
struct kvec *iov = rcvbuf->head;
size_t hdrlen;
u32 recvd, pglen = rcvbuf->page_len;
int status; int status;
__be32 verf[2]; __be32 verf[2];
@ -4967,22 +4966,12 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
memcpy(verf, readdir->verifier.data, sizeof(verf)); memcpy(verf, readdir->verifier.data, sizeof(verf));
dprintk("%s: verifier = %08x:%08x\n", dprintk("%s: verifier = %08x:%08x\n",
__func__, verf[0], verf[1]); __func__, verf[0], verf[1]);
return xdr_read_pages(xdr, xdr->buf->page_len);
hdrlen = (char *) xdr->p - (char *) iov->iov_base;
recvd = rcvbuf->len - hdrlen;
if (pglen > recvd)
pglen = recvd;
xdr_read_pages(xdr, pglen);
return pglen;
} }
static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
{ {
struct xdr_buf *rcvbuf = &req->rq_rcv_buf; struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
struct kvec *iov = rcvbuf->head;
size_t hdrlen;
u32 len, recvd; u32 len, recvd;
__be32 *p; __be32 *p;
int status; int status;
@ -5000,14 +4989,12 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
dprintk("nfs: server returned giant symlink!\n"); dprintk("nfs: server returned giant symlink!\n");
return -ENAMETOOLONG; return -ENAMETOOLONG;
} }
hdrlen = (char *) xdr->p - (char *) iov->iov_base; recvd = xdr_read_pages(xdr, len);
recvd = req->rq_rcv_buf.len - hdrlen;
if (recvd < len) { if (recvd < len) {
dprintk("NFS: server cheating in readlink reply: " dprintk("NFS: server cheating in readlink reply: "
"count %u > recvd %u\n", len, recvd); "count %u > recvd %u\n", len, recvd);
return -EIO; return -EIO;
} }
xdr_read_pages(xdr, len);
/* /*
* The XDR encode routine has set things up so that * The XDR encode routine has set things up so that
* the link text will be copied directly into the * the link text will be copied directly into the
@ -5063,10 +5050,10 @@ decode_restorefh(struct xdr_stream *xdr)
static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
struct nfs_getaclres *res) struct nfs_getaclres *res)
{ {
__be32 *savep, *bm_p; unsigned int savep;
__be32 *bm_p;
uint32_t attrlen, uint32_t attrlen,
bitmap[3] = {0}; bitmap[3] = {0};
struct kvec *iov = req->rq_rcv_buf.head;
int status; int status;
size_t page_len = xdr->buf->page_len; size_t page_len = xdr->buf->page_len;
@ -5089,7 +5076,6 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U))) if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U)))
return -EIO; return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_ACL)) { if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
size_t hdrlen;
/* The bitmap (xdr len + bitmaps) and the attr xdr len words /* The bitmap (xdr len + bitmaps) and the attr xdr len words
* are stored with the acl data to handle the problem of * are stored with the acl data to handle the problem of
@ -5098,7 +5084,6 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
/* We ignore &savep and don't do consistency checks on /* We ignore &savep and don't do consistency checks on
* the attr length. Let userspace figure it out.... */ * the attr length. Let userspace figure it out.... */
hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base;
attrlen += res->acl_data_offset; attrlen += res->acl_data_offset;
if (attrlen > page_len) { if (attrlen > page_len) {
if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { if (res->acl_flags & NFS4_ACL_LEN_REQUEST) {
@ -5212,13 +5197,12 @@ static int decode_write(struct xdr_stream *xdr, struct nfs_writeres *res)
if (status) if (status)
return status; return status;
p = xdr_inline_decode(xdr, 16); p = xdr_inline_decode(xdr, 8);
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; goto out_overflow;
res->count = be32_to_cpup(p++); res->count = be32_to_cpup(p++);
res->verf->committed = be32_to_cpup(p++); res->verf->committed = be32_to_cpup(p++);
memcpy(res->verf->verifier, p, NFS4_VERIFIER_SIZE); return decode_write_verifier(xdr, &res->verf->verifier);
return 0;
out_overflow: out_overflow:
print_overflow_msg(__func__, xdr); print_overflow_msg(__func__, xdr);
return -EIO; return -EIO;
@ -5599,7 +5583,7 @@ static int decode_getdevicelist(struct xdr_stream *xdr,
{ {
__be32 *p; __be32 *p;
int status, i; int status, i;
struct nfs_writeverf verftemp; nfs4_verifier verftemp;
status = decode_op_hdr(xdr, OP_GETDEVICELIST); status = decode_op_hdr(xdr, OP_GETDEVICELIST);
if (status) if (status)
@ -5613,7 +5597,7 @@ static int decode_getdevicelist(struct xdr_stream *xdr,
p += 2; p += 2;
/* Read verifier */ /* Read verifier */
p = xdr_decode_opaque_fixed(p, verftemp.verifier, NFS4_VERIFIER_SIZE); p = xdr_decode_opaque_fixed(p, verftemp.data, NFS4_VERIFIER_SIZE);
res->num_devs = be32_to_cpup(p); res->num_devs = be32_to_cpup(p);
@ -5707,9 +5691,7 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
__be32 *p; __be32 *p;
int status; int status;
u32 layout_count; u32 layout_count;
struct xdr_buf *rcvbuf = &req->rq_rcv_buf; u32 recvd;
struct kvec *iov = rcvbuf->head;
u32 hdrlen, recvd;
status = decode_op_hdr(xdr, OP_LAYOUTGET); status = decode_op_hdr(xdr, OP_LAYOUTGET);
if (status) if (status)
@ -5746,8 +5728,7 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
res->type, res->type,
res->layoutp->len); res->layoutp->len);
hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base; recvd = xdr_read_pages(xdr, res->layoutp->len);
recvd = req->rq_rcv_buf.len - hdrlen;
if (res->layoutp->len > recvd) { if (res->layoutp->len > recvd) {
dprintk("NFS: server cheating in layoutget reply: " dprintk("NFS: server cheating in layoutget reply: "
"layout len %u > recvd %u\n", "layout len %u > recvd %u\n",
@ -5755,8 +5736,6 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
return -EINVAL; return -EINVAL;
} }
xdr_read_pages(xdr, res->layoutp->len);
if (layout_count > 1) { if (layout_count > 1) {
/* We only handle a length one array at the moment. Any /* We only handle a length one array at the moment. Any
* further entries are just ignored. Note that this means * further entries are just ignored. Note that this means
@ -7103,6 +7082,7 @@ out:
int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
int plus) int plus)
{ {
unsigned int savep;
uint32_t bitmap[3] = {0}; uint32_t bitmap[3] = {0};
uint32_t len; uint32_t len;
__be32 *p = xdr_inline_decode(xdr, 4); __be32 *p = xdr_inline_decode(xdr, 4);
@ -7141,7 +7121,7 @@ int nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
if (decode_attr_bitmap(xdr, bitmap) < 0) if (decode_attr_bitmap(xdr, bitmap) < 0)
goto out_overflow; goto out_overflow;
if (decode_attr_length(xdr, &len, &p) < 0) if (decode_attr_length(xdr, &len, &savep) < 0)
goto out_overflow; goto out_overflow;
if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh, if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh,

View File

@ -651,7 +651,14 @@ out_err_free:
return NULL; return NULL;
} }
/* Initiates a LAYOUTRETURN(FILE) */ /*
* Initiates a LAYOUTRETURN(FILE), and removes the pnfs_layout_hdr
* when the layout segment list is empty.
*
* Note that a pnfs_layout_hdr can exist with an empty layout segment
* list when LAYOUTGET has failed, or when LAYOUTGET succeeded, but the
* deviceid is marked invalid.
*/
int int
_pnfs_return_layout(struct inode *ino) _pnfs_return_layout(struct inode *ino)
{ {
@ -660,22 +667,31 @@ _pnfs_return_layout(struct inode *ino)
LIST_HEAD(tmp_list); LIST_HEAD(tmp_list);
struct nfs4_layoutreturn *lrp; struct nfs4_layoutreturn *lrp;
nfs4_stateid stateid; nfs4_stateid stateid;
int status = 0; int status = 0, empty;
dprintk("--> %s\n", __func__); dprintk("NFS: %s for inode %lu\n", __func__, ino->i_ino);
spin_lock(&ino->i_lock); spin_lock(&ino->i_lock);
lo = nfsi->layout; lo = nfsi->layout;
if (!lo) { if (!lo || pnfs_test_layout_returned(lo)) {
spin_unlock(&ino->i_lock); spin_unlock(&ino->i_lock);
dprintk("%s: no layout to return\n", __func__); dprintk("NFS: %s no layout to return\n", __func__);
return status; goto out;
} }
stateid = nfsi->layout->plh_stateid; stateid = nfsi->layout->plh_stateid;
/* Reference matched in nfs4_layoutreturn_release */ /* Reference matched in nfs4_layoutreturn_release */
get_layout_hdr(lo); get_layout_hdr(lo);
empty = list_empty(&lo->plh_segs);
mark_matching_lsegs_invalid(lo, &tmp_list, NULL); mark_matching_lsegs_invalid(lo, &tmp_list, NULL);
/* Don't send a LAYOUTRETURN if list was initially empty */
if (empty) {
spin_unlock(&ino->i_lock);
put_layout_hdr(lo);
dprintk("NFS: %s no layout segments to return\n", __func__);
goto out;
}
lo->plh_block_lgets++; lo->plh_block_lgets++;
pnfs_mark_layout_returned(lo);
spin_unlock(&ino->i_lock); spin_unlock(&ino->i_lock);
pnfs_free_lseg_list(&tmp_list); pnfs_free_lseg_list(&tmp_list);
@ -686,6 +702,7 @@ _pnfs_return_layout(struct inode *ino)
status = -ENOMEM; status = -ENOMEM;
set_bit(NFS_LAYOUT_RW_FAILED, &lo->plh_flags); set_bit(NFS_LAYOUT_RW_FAILED, &lo->plh_flags);
set_bit(NFS_LAYOUT_RO_FAILED, &lo->plh_flags); set_bit(NFS_LAYOUT_RO_FAILED, &lo->plh_flags);
pnfs_clear_layout_returned(lo);
put_layout_hdr(lo); put_layout_hdr(lo);
goto out; goto out;
} }
@ -1075,6 +1092,10 @@ pnfs_update_layout(struct inode *ino,
get_layout_hdr(lo); get_layout_hdr(lo);
if (list_empty(&lo->plh_segs)) if (list_empty(&lo->plh_segs))
first = true; first = true;
/* Enable LAYOUTRETURNs */
pnfs_clear_layout_returned(lo);
spin_unlock(&ino->i_lock); spin_unlock(&ino->i_lock);
if (first) { if (first) {
/* The lo must be on the clp list if there is any /* The lo must be on the clp list if there is any
@ -1209,7 +1230,7 @@ pnfs_generic_pg_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *
} }
EXPORT_SYMBOL_GPL(pnfs_generic_pg_init_write); EXPORT_SYMBOL_GPL(pnfs_generic_pg_init_write);
bool void
pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode,
const struct nfs_pgio_completion_ops *compl_ops) const struct nfs_pgio_completion_ops *compl_ops)
{ {
@ -1217,13 +1238,12 @@ pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode,
struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld; struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld;
if (ld == NULL) if (ld == NULL)
return false; nfs_pageio_init_read(pgio, inode, compl_ops);
nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops, else
server->rsize, 0); nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops, server->rsize, 0);
return true;
} }
bool void
pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode,
int ioflags, int ioflags,
const struct nfs_pgio_completion_ops *compl_ops) const struct nfs_pgio_completion_ops *compl_ops)
@ -1232,10 +1252,9 @@ pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode,
struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld; struct pnfs_layoutdriver_type *ld = server->pnfs_curr_ld;
if (ld == NULL) if (ld == NULL)
return false; nfs_pageio_init_write(pgio, inode, ioflags, compl_ops);
nfs_pageio_init(pgio, inode, ld->pg_write_ops, compl_ops, else
server->wsize, ioflags); nfs_pageio_init(pgio, inode, ld->pg_write_ops, compl_ops, server->wsize, ioflags);
return true;
} }
bool bool
@ -1272,7 +1291,7 @@ int pnfs_write_done_resend_to_mds(struct inode *inode,
LIST_HEAD(failed); LIST_HEAD(failed);
/* Resend all requests through the MDS */ /* Resend all requests through the MDS */
nfs_pageio_init_write_mds(&pgio, inode, FLUSH_STABLE, compl_ops); nfs_pageio_init_write(&pgio, inode, FLUSH_STABLE, compl_ops);
while (!list_empty(head)) { while (!list_empty(head)) {
struct nfs_page *req = nfs_list_entry(head->next); struct nfs_page *req = nfs_list_entry(head->next);
@ -1427,7 +1446,7 @@ int pnfs_read_done_resend_to_mds(struct inode *inode,
LIST_HEAD(failed); LIST_HEAD(failed);
/* Resend all requests through the MDS */ /* Resend all requests through the MDS */
nfs_pageio_init_read_mds(&pgio, inode, compl_ops); nfs_pageio_init_read(&pgio, inode, compl_ops);
while (!list_empty(head)) { while (!list_empty(head)) {
struct nfs_page *req = nfs_list_entry(head->next); struct nfs_page *req = nfs_list_entry(head->next);

View File

@ -64,6 +64,7 @@ enum {
NFS_LAYOUT_ROC, /* some lseg had roc bit set */ NFS_LAYOUT_ROC, /* some lseg had roc bit set */
NFS_LAYOUT_DESTROYED, /* no new use of layout allowed */ NFS_LAYOUT_DESTROYED, /* no new use of layout allowed */
NFS_LAYOUT_INVALID, /* layout is being destroyed */ NFS_LAYOUT_INVALID, /* layout is being destroyed */
NFS_LAYOUT_RETURNED, /* layout has already been returned */
}; };
enum layoutdriver_policy_flags { enum layoutdriver_policy_flags {
@ -178,9 +179,9 @@ extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp);
void get_layout_hdr(struct pnfs_layout_hdr *lo); void get_layout_hdr(struct pnfs_layout_hdr *lo);
void put_lseg(struct pnfs_layout_segment *lseg); void put_lseg(struct pnfs_layout_segment *lseg);
bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *, void pnfs_pageio_init_read(struct nfs_pageio_descriptor *, struct inode *,
const struct nfs_pgio_completion_ops *); const struct nfs_pgio_completion_ops *);
bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *, void pnfs_pageio_init_write(struct nfs_pageio_descriptor *, struct inode *,
int, const struct nfs_pgio_completion_ops *); int, const struct nfs_pgio_completion_ops *);
void set_pnfs_layoutdriver(struct nfs_server *, const struct nfs_fh *, u32); void set_pnfs_layoutdriver(struct nfs_server *, const struct nfs_fh *, u32);
@ -255,6 +256,24 @@ struct nfs4_deviceid_node *nfs4_insert_deviceid_node(struct nfs4_deviceid_node *
bool nfs4_put_deviceid_node(struct nfs4_deviceid_node *); bool nfs4_put_deviceid_node(struct nfs4_deviceid_node *);
void nfs4_deviceid_purge_client(const struct nfs_client *); void nfs4_deviceid_purge_client(const struct nfs_client *);
static inline void
pnfs_mark_layout_returned(struct pnfs_layout_hdr *lo)
{
set_bit(NFS_LAYOUT_RETURNED, &lo->plh_flags);
}
static inline void
pnfs_clear_layout_returned(struct pnfs_layout_hdr *lo)
{
clear_bit(NFS_LAYOUT_RETURNED, &lo->plh_flags);
}
static inline bool
pnfs_test_layout_returned(struct pnfs_layout_hdr *lo)
{
return test_bit(NFS_LAYOUT_RETURNED, &lo->plh_flags);
}
static inline int lo_fail_bit(u32 iomode) static inline int lo_fail_bit(u32 iomode)
{ {
return iomode == IOMODE_RW ? return iomode == IOMODE_RW ?
@ -438,16 +457,16 @@ static inline void unset_pnfs_layoutdriver(struct nfs_server *s)
{ {
} }
static inline bool pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode, static inline void pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode,
const struct nfs_pgio_completion_ops *compl_ops) const struct nfs_pgio_completion_ops *compl_ops)
{ {
return false; nfs_pageio_init_read(pgio, inode, compl_ops);
} }
static inline bool pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags, static inline void pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode, int ioflags,
const struct nfs_pgio_completion_ops *compl_ops) const struct nfs_pgio_completion_ops *compl_ops)
{ {
return false; nfs_pageio_init_write(pgio, inode, ioflags, compl_ops);
} }
static inline int static inline int

View File

@ -734,6 +734,38 @@ out_einval:
return -EINVAL; return -EINVAL;
} }
static int nfs_have_delegation(struct inode *inode, fmode_t flags)
{
return 0;
}
static int nfs_return_delegation(struct inode *inode)
{
nfs_wb_all(inode);
return 0;
}
static const struct inode_operations nfs_dir_inode_operations = {
.create = nfs_create,
.lookup = nfs_lookup,
.link = nfs_link,
.unlink = nfs_unlink,
.symlink = nfs_symlink,
.mkdir = nfs_mkdir,
.rmdir = nfs_rmdir,
.mknod = nfs_mknod,
.rename = nfs_rename,
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
};
static const struct inode_operations nfs_file_inode_operations = {
.permission = nfs_permission,
.getattr = nfs_getattr,
.setattr = nfs_setattr,
};
const struct nfs_rpc_ops nfs_v2_clientops = { const struct nfs_rpc_ops nfs_v2_clientops = {
.version = 2, /* protocol version */ .version = 2, /* protocol version */
.dentry_ops = &nfs_dentry_operations, .dentry_ops = &nfs_dentry_operations,
@ -767,9 +799,11 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
.pathconf = nfs_proc_pathconf, .pathconf = nfs_proc_pathconf,
.decode_dirent = nfs2_decode_dirent, .decode_dirent = nfs2_decode_dirent,
.read_setup = nfs_proc_read_setup, .read_setup = nfs_proc_read_setup,
.read_pageio_init = nfs_pageio_init_read,
.read_rpc_prepare = nfs_proc_read_rpc_prepare, .read_rpc_prepare = nfs_proc_read_rpc_prepare,
.read_done = nfs_read_done, .read_done = nfs_read_done,
.write_setup = nfs_proc_write_setup, .write_setup = nfs_proc_write_setup,
.write_pageio_init = nfs_pageio_init_write,
.write_rpc_prepare = nfs_proc_write_rpc_prepare, .write_rpc_prepare = nfs_proc_write_rpc_prepare,
.write_done = nfs_write_done, .write_done = nfs_write_done,
.commit_setup = nfs_proc_commit_setup, .commit_setup = nfs_proc_commit_setup,
@ -777,5 +811,9 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
.lock = nfs_proc_lock, .lock = nfs_proc_lock,
.lock_check_bounds = nfs_lock_check_bounds, .lock_check_bounds = nfs_lock_check_bounds,
.close_context = nfs_close_context, .close_context = nfs_close_context,
.have_delegation = nfs_have_delegation,
.return_delegation = nfs_return_delegation,
.alloc_client = nfs_alloc_client,
.init_client = nfs_init_client, .init_client = nfs_init_client,
.free_client = nfs_free_client,
}; };

View File

@ -20,8 +20,6 @@
#include <linux/nfs_page.h> #include <linux/nfs_page.h>
#include <linux/module.h> #include <linux/module.h>
#include "pnfs.h"
#include "nfs4_fs.h" #include "nfs4_fs.h"
#include "internal.h" #include "internal.h"
#include "iostat.h" #include "iostat.h"
@ -108,7 +106,7 @@ int nfs_return_empty_page(struct page *page)
return 0; return 0;
} }
void nfs_pageio_init_read_mds(struct nfs_pageio_descriptor *pgio, void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
struct inode *inode, struct inode *inode,
const struct nfs_pgio_completion_ops *compl_ops) const struct nfs_pgio_completion_ops *compl_ops)
{ {
@ -123,14 +121,6 @@ void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio)
} }
EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds); EXPORT_SYMBOL_GPL(nfs_pageio_reset_read_mds);
void nfs_pageio_init_read(struct nfs_pageio_descriptor *pgio,
struct inode *inode,
const struct nfs_pgio_completion_ops *compl_ops)
{
if (!pnfs_pageio_init_read(pgio, inode, compl_ops))
nfs_pageio_init_read_mds(pgio, inode, compl_ops);
}
int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode, int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
struct page *page) struct page *page)
{ {
@ -149,7 +139,7 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
if (len < PAGE_CACHE_SIZE) if (len < PAGE_CACHE_SIZE)
zero_user_segment(page, len, PAGE_CACHE_SIZE); zero_user_segment(page, len, PAGE_CACHE_SIZE);
nfs_pageio_init_read(&pgio, inode, &nfs_async_read_completion_ops); NFS_PROTO(inode)->read_pageio_init(&pgio, inode, &nfs_async_read_completion_ops);
nfs_pageio_add_request(&pgio, new); nfs_pageio_add_request(&pgio, new);
nfs_pageio_complete(&pgio); nfs_pageio_complete(&pgio);
NFS_I(inode)->read_io += pgio.pg_bytes_written; NFS_I(inode)->read_io += pgio.pg_bytes_written;
@ -652,7 +642,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
if (ret == 0) if (ret == 0)
goto read_complete; /* all pages were read */ goto read_complete; /* all pages were read */
nfs_pageio_init_read(&pgio, inode, &nfs_async_read_completion_ops); NFS_PROTO(inode)->read_pageio_init(&pgio, inode, &nfs_async_read_completion_ops);
ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc); ret = read_cache_pages(mapping, pages, readpage_async_filler, &desc);

View File

@ -278,29 +278,8 @@ static match_table_t nfs_vers_tokens = {
{ Opt_vers_err, NULL } { Opt_vers_err, NULL }
}; };
struct nfs_mount_info {
void (*fill_super)(struct super_block *, struct nfs_mount_info *);
int (*set_security)(struct super_block *, struct dentry *, struct nfs_mount_info *);
struct nfs_parsed_mount_data *parsed;
struct nfs_clone_mount *cloned;
struct nfs_fh *mntfh;
};
static void nfs_umount_begin(struct super_block *);
static int nfs_statfs(struct dentry *, struct kstatfs *);
static int nfs_show_options(struct seq_file *, struct dentry *);
static int nfs_show_devname(struct seq_file *, struct dentry *);
static int nfs_show_path(struct seq_file *, struct dentry *);
static int nfs_show_stats(struct seq_file *, struct dentry *);
static struct dentry *nfs_fs_mount_common(struct file_system_type *,
struct nfs_server *, int, const char *, struct nfs_mount_info *);
static struct dentry *nfs_fs_mount(struct file_system_type *,
int, const char *, void *);
static struct dentry *nfs_xdev_mount(struct file_system_type *fs_type, static struct dentry *nfs_xdev_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data); int flags, const char *dev_name, void *raw_data);
static void nfs_put_super(struct super_block *);
static void nfs_kill_super(struct super_block *);
static int nfs_remount(struct super_block *sb, int *flags, char *raw_data);
static struct file_system_type nfs_fs_type = { static struct file_system_type nfs_fs_type = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
@ -337,72 +316,6 @@ static const struct super_operations nfs_sops = {
static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *); static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *);
static int nfs4_validate_mount_data(void *options, static int nfs4_validate_mount_data(void *options,
struct nfs_parsed_mount_data *args, const char *dev_name); struct nfs_parsed_mount_data *args, const char *dev_name);
static struct dentry *nfs4_try_mount(int flags, const char *dev_name,
struct nfs_mount_info *mount_info);
static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
static struct dentry *nfs4_xdev_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
static void nfs4_kill_super(struct super_block *sb);
static struct file_system_type nfs4_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs_fs_mount,
.kill_sb = nfs4_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
static struct file_system_type nfs4_remote_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs4_remote_mount,
.kill_sb = nfs4_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
struct file_system_type nfs4_xdev_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs4_xdev_mount,
.kill_sb = nfs4_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
static struct file_system_type nfs4_remote_referral_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs4_remote_referral_mount,
.kill_sb = nfs4_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
struct file_system_type nfs4_referral_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
.mount = nfs4_referral_mount,
.kill_sb = nfs4_kill_super,
.fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
};
static const struct super_operations nfs4_sops = {
.alloc_inode = nfs_alloc_inode,
.destroy_inode = nfs_destroy_inode,
.write_inode = nfs_write_inode,
.put_super = nfs_put_super,
.statfs = nfs_statfs,
.evict_inode = nfs4_evict_inode,
.umount_begin = nfs_umount_begin,
.show_options = nfs_show_options,
.show_devname = nfs_show_devname,
.show_path = nfs_show_path,
.show_stats = nfs_show_stats,
.remount_fs = nfs_remount,
};
#endif #endif
static struct shrinker acl_shrinker = { static struct shrinker acl_shrinker = {
@ -424,18 +337,9 @@ int __init register_nfs_fs(void)
ret = nfs_register_sysctl(); ret = nfs_register_sysctl();
if (ret < 0) if (ret < 0)
goto error_1; goto error_1;
#ifdef CONFIG_NFS_V4
ret = register_filesystem(&nfs4_fs_type);
if (ret < 0)
goto error_2;
#endif
register_shrinker(&acl_shrinker); register_shrinker(&acl_shrinker);
return 0; return 0;
#ifdef CONFIG_NFS_V4
error_2:
nfs_unregister_sysctl();
#endif
error_1: error_1:
unregister_filesystem(&nfs_fs_type); unregister_filesystem(&nfs_fs_type);
error_0: error_0:
@ -448,9 +352,6 @@ error_0:
void __exit unregister_nfs_fs(void) void __exit unregister_nfs_fs(void)
{ {
unregister_shrinker(&acl_shrinker); unregister_shrinker(&acl_shrinker);
#ifdef CONFIG_NFS_V4
unregister_filesystem(&nfs4_fs_type);
#endif
nfs_unregister_sysctl(); nfs_unregister_sysctl();
unregister_filesystem(&nfs_fs_type); unregister_filesystem(&nfs_fs_type);
} }
@ -474,7 +375,7 @@ void nfs_sb_deactive(struct super_block *sb)
/* /*
* Deliver file system statistics to userspace * Deliver file system statistics to userspace
*/ */
static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf) int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
{ {
struct nfs_server *server = NFS_SB(dentry->d_sb); struct nfs_server *server = NFS_SB(dentry->d_sb);
unsigned char blockbits; unsigned char blockbits;
@ -757,7 +658,7 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
/* /*
* Describe the mount options on this VFS mountpoint * Describe the mount options on this VFS mountpoint
*/ */
static int nfs_show_options(struct seq_file *m, struct dentry *root) int nfs_show_options(struct seq_file *m, struct dentry *root)
{ {
struct nfs_server *nfss = NFS_SB(root->d_sb); struct nfs_server *nfss = NFS_SB(root->d_sb);
@ -815,7 +716,7 @@ static void show_implementation_id(struct seq_file *m, struct nfs_server *nfss)
} }
#endif #endif
static int nfs_show_devname(struct seq_file *m, struct dentry *root) int nfs_show_devname(struct seq_file *m, struct dentry *root)
{ {
char *page = (char *) __get_free_page(GFP_KERNEL); char *page = (char *) __get_free_page(GFP_KERNEL);
char *devname, *dummy; char *devname, *dummy;
@ -831,7 +732,7 @@ static int nfs_show_devname(struct seq_file *m, struct dentry *root)
return err; return err;
} }
static int nfs_show_path(struct seq_file *m, struct dentry *dentry) int nfs_show_path(struct seq_file *m, struct dentry *dentry)
{ {
seq_puts(m, "/"); seq_puts(m, "/");
return 0; return 0;
@ -840,7 +741,7 @@ static int nfs_show_path(struct seq_file *m, struct dentry *dentry)
/* /*
* Present statistical information for this VFS mountpoint * Present statistical information for this VFS mountpoint
*/ */
static int nfs_show_stats(struct seq_file *m, struct dentry *root) int nfs_show_stats(struct seq_file *m, struct dentry *root)
{ {
int i, cpu; int i, cpu;
struct nfs_server *nfss = NFS_SB(root->d_sb); struct nfs_server *nfss = NFS_SB(root->d_sb);
@ -933,7 +834,7 @@ static int nfs_show_stats(struct seq_file *m, struct dentry *root)
* Begin unmount by attempting to remove all automounted mountpoints we added * Begin unmount by attempting to remove all automounted mountpoints we added
* in response to xdev traversals and referrals * in response to xdev traversals and referrals
*/ */
static void nfs_umount_begin(struct super_block *sb) void nfs_umount_begin(struct super_block *sb)
{ {
struct nfs_server *server; struct nfs_server *server;
struct rpc_clnt *rpc; struct rpc_clnt *rpc;
@ -2108,7 +2009,7 @@ nfs_compare_remount_data(struct nfs_server *nfss,
return 0; return 0;
} }
static int int
nfs_remount(struct super_block *sb, int *flags, char *raw_data) nfs_remount(struct super_block *sb, int *flags, char *raw_data)
{ {
int error; int error;
@ -2173,7 +2074,7 @@ out:
/* /*
* Initialise the common bits of the superblock * Initialise the common bits of the superblock
*/ */
static inline void nfs_initialise_sb(struct super_block *sb) inline void nfs_initialise_sb(struct super_block *sb)
{ {
struct nfs_server *server = NFS_SB(sb); struct nfs_server *server = NFS_SB(sb);
@ -2195,8 +2096,7 @@ static inline void nfs_initialise_sb(struct super_block *sb)
/* /*
* Finish setting up an NFS2/3 superblock * Finish setting up an NFS2/3 superblock
*/ */
static void nfs_fill_super(struct super_block *sb, void nfs_fill_super(struct super_block *sb, struct nfs_mount_info *mount_info)
struct nfs_mount_info *mount_info)
{ {
struct nfs_parsed_mount_data *data = mount_info->parsed; struct nfs_parsed_mount_data *data = mount_info->parsed;
struct nfs_server *server = NFS_SB(sb); struct nfs_server *server = NFS_SB(sb);
@ -2219,10 +2119,9 @@ static void nfs_fill_super(struct super_block *sb,
} }
/* /*
* Finish setting up a cloned NFS2/3 superblock * Finish setting up a cloned NFS2/3/4 superblock
*/ */
static void nfs_clone_super(struct super_block *sb, void nfs_clone_super(struct super_block *sb, struct nfs_mount_info *mount_info)
struct nfs_mount_info *mount_info)
{ {
const struct super_block *old_sb = mount_info->cloned->sb; const struct super_block *old_sb = mount_info->cloned->sb;
struct nfs_server *server = NFS_SB(sb); struct nfs_server *server = NFS_SB(sb);
@ -2230,16 +2129,17 @@ static void nfs_clone_super(struct super_block *sb,
sb->s_blocksize_bits = old_sb->s_blocksize_bits; sb->s_blocksize_bits = old_sb->s_blocksize_bits;
sb->s_blocksize = old_sb->s_blocksize; sb->s_blocksize = old_sb->s_blocksize;
sb->s_maxbytes = old_sb->s_maxbytes; sb->s_maxbytes = old_sb->s_maxbytes;
sb->s_xattr = old_sb->s_xattr;
sb->s_op = old_sb->s_op;
sb->s_time_gran = 1;
if (server->nfs_client->rpc_ops->version == 3) { if (server->nfs_client->rpc_ops->version != 2) {
/* The VFS shouldn't apply the umask to mode bits. We will do /* The VFS shouldn't apply the umask to mode bits. We will do
* so ourselves when necessary. * so ourselves when necessary.
*/ */
sb->s_flags |= MS_POSIXACL; sb->s_flags |= MS_POSIXACL;
sb->s_time_gran = 1;
} }
sb->s_op = old_sb->s_op;
nfs_initialise_sb(sb); nfs_initialise_sb(sb);
} }
@ -2381,14 +2281,14 @@ static int nfs_bdi_register(struct nfs_server *server)
return bdi_register_dev(&server->backing_dev_info, server->s_dev); return bdi_register_dev(&server->backing_dev_info, server->s_dev);
} }
static int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot, int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot,
struct nfs_mount_info *mount_info) struct nfs_mount_info *mount_info)
{ {
return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts); return security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts);
} }
static int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot, int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot,
struct nfs_mount_info *mount_info) struct nfs_mount_info *mount_info)
{ {
/* clone any lsm security options from the parent to the new sb */ /* clone any lsm security options from the parent to the new sb */
security_sb_clone_mnt_opts(mount_info->cloned->sb, s); security_sb_clone_mnt_opts(mount_info->cloned->sb, s);
@ -2397,10 +2297,10 @@ static int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot,
return 0; return 0;
} }
static struct dentry *nfs_fs_mount_common(struct file_system_type *fs_type, struct dentry *nfs_fs_mount_common(struct file_system_type *fs_type,
struct nfs_server *server, struct nfs_server *server,
int flags, const char *dev_name, int flags, const char *dev_name,
struct nfs_mount_info *mount_info) struct nfs_mount_info *mount_info)
{ {
struct super_block *s; struct super_block *s;
struct dentry *mntroot = ERR_PTR(-ENOMEM); struct dentry *mntroot = ERR_PTR(-ENOMEM);
@ -2470,7 +2370,7 @@ error_splat_bdi:
goto out; goto out;
} }
static struct dentry *nfs_fs_mount(struct file_system_type *fs_type, struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data) int flags, const char *dev_name, void *raw_data)
{ {
struct nfs_mount_info mount_info = { struct nfs_mount_info mount_info = {
@ -2511,7 +2411,7 @@ out:
* Ensure that we unregister the bdi before kill_anon_super * Ensure that we unregister the bdi before kill_anon_super
* releases the device name * releases the device name
*/ */
static void nfs_put_super(struct super_block *s) void nfs_put_super(struct super_block *s)
{ {
struct nfs_server *server = NFS_SB(s); struct nfs_server *server = NFS_SB(s);
@ -2521,7 +2421,7 @@ static void nfs_put_super(struct super_block *s)
/* /*
* Destroy an NFS2/3 superblock * Destroy an NFS2/3 superblock
*/ */
static void nfs_kill_super(struct super_block *s) void nfs_kill_super(struct super_block *s)
{ {
struct nfs_server *server = NFS_SB(s); struct nfs_server *server = NFS_SB(s);
@ -2533,7 +2433,7 @@ static void nfs_kill_super(struct super_block *s)
/* /*
* Clone an NFS2/3/4 server record on xdev traversal (FSID-change) * Clone an NFS2/3/4 server record on xdev traversal (FSID-change)
*/ */
static struct dentry * struct dentry *
nfs_xdev_mount_common(struct file_system_type *fs_type, int flags, nfs_xdev_mount_common(struct file_system_type *fs_type, int flags,
const char *dev_name, struct nfs_mount_info *mount_info) const char *dev_name, struct nfs_mount_info *mount_info)
{ {
@ -2580,44 +2480,6 @@ nfs_xdev_mount(struct file_system_type *fs_type, int flags,
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
/*
* Finish setting up a cloned NFS4 superblock
*/
static void nfs4_clone_super(struct super_block *sb,
struct nfs_mount_info *mount_info)
{
const struct super_block *old_sb = mount_info->cloned->sb;
sb->s_blocksize_bits = old_sb->s_blocksize_bits;
sb->s_blocksize = old_sb->s_blocksize;
sb->s_maxbytes = old_sb->s_maxbytes;
sb->s_time_gran = 1;
sb->s_op = old_sb->s_op;
/*
* The VFS shouldn't apply the umask to mode bits. We will do
* so ourselves when necessary.
*/
sb->s_flags |= MS_POSIXACL;
sb->s_xattr = old_sb->s_xattr;
nfs_initialise_sb(sb);
}
/*
* Set up an NFS4 superblock
*/
static void nfs4_fill_super(struct super_block *sb,
struct nfs_mount_info *mount_info)
{
sb->s_time_gran = 1;
sb->s_op = &nfs4_sops;
/*
* The VFS shouldn't apply the umask to mode bits. We will do
* so ourselves when necessary.
*/
sb->s_flags |= MS_POSIXACL;
sb->s_xattr = nfs4_xattr_handlers;
nfs_initialise_sb(sb);
}
static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args) static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args)
{ {
args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3| args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3|
@ -2715,250 +2577,4 @@ out_no_address:
return -EINVAL; return -EINVAL;
} }
/*
* Get the superblock for the NFS4 root partition
*/
static struct dentry *
nfs4_remote_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *info)
{
struct nfs_mount_info *mount_info = info;
struct nfs_server *server;
struct dentry *mntroot = ERR_PTR(-ENOMEM);
mount_info->fill_super = nfs4_fill_super;
mount_info->set_security = nfs_set_sb_security;
/* Get a volume representation */
server = nfs4_create_server(mount_info->parsed, mount_info->mntfh);
if (IS_ERR(server)) {
mntroot = ERR_CAST(server);
goto out;
}
mntroot = nfs_fs_mount_common(fs_type, server, flags, dev_name, mount_info);
out:
return mntroot;
}
static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
int flags, void *data, const char *hostname)
{
struct vfsmount *root_mnt;
char *root_devname;
size_t len;
len = strlen(hostname) + 5;
root_devname = kmalloc(len, GFP_KERNEL);
if (root_devname == NULL)
return ERR_PTR(-ENOMEM);
/* Does hostname needs to be enclosed in brackets? */
if (strchr(hostname, ':'))
snprintf(root_devname, len, "[%s]:/", hostname);
else
snprintf(root_devname, len, "%s:/", hostname);
root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data);
kfree(root_devname);
return root_mnt;
}
struct nfs_referral_count {
struct list_head list;
const struct task_struct *task;
unsigned int referral_count;
};
static LIST_HEAD(nfs_referral_count_list);
static DEFINE_SPINLOCK(nfs_referral_count_list_lock);
static struct nfs_referral_count *nfs_find_referral_count(void)
{
struct nfs_referral_count *p;
list_for_each_entry(p, &nfs_referral_count_list, list) {
if (p->task == current)
return p;
}
return NULL;
}
#define NFS_MAX_NESTED_REFERRALS 2
static int nfs_referral_loop_protect(void)
{
struct nfs_referral_count *p, *new;
int ret = -ENOMEM;
new = kmalloc(sizeof(*new), GFP_KERNEL);
if (!new)
goto out;
new->task = current;
new->referral_count = 1;
ret = 0;
spin_lock(&nfs_referral_count_list_lock);
p = nfs_find_referral_count();
if (p != NULL) {
if (p->referral_count >= NFS_MAX_NESTED_REFERRALS)
ret = -ELOOP;
else
p->referral_count++;
} else {
list_add(&new->list, &nfs_referral_count_list);
new = NULL;
}
spin_unlock(&nfs_referral_count_list_lock);
kfree(new);
out:
return ret;
}
static void nfs_referral_loop_unprotect(void)
{
struct nfs_referral_count *p;
spin_lock(&nfs_referral_count_list_lock);
p = nfs_find_referral_count();
p->referral_count--;
if (p->referral_count == 0)
list_del(&p->list);
else
p = NULL;
spin_unlock(&nfs_referral_count_list_lock);
kfree(p);
}
static struct dentry *nfs_follow_remote_path(struct vfsmount *root_mnt,
const char *export_path)
{
struct dentry *dentry;
int err;
if (IS_ERR(root_mnt))
return ERR_CAST(root_mnt);
err = nfs_referral_loop_protect();
if (err) {
mntput(root_mnt);
return ERR_PTR(err);
}
dentry = mount_subtree(root_mnt, export_path);
nfs_referral_loop_unprotect();
return dentry;
}
static struct dentry *nfs4_try_mount(int flags, const char *dev_name,
struct nfs_mount_info *mount_info)
{
char *export_path;
struct vfsmount *root_mnt;
struct dentry *res;
struct nfs_parsed_mount_data *data = mount_info->parsed;
dfprintk(MOUNT, "--> nfs4_try_mount()\n");
mount_info->fill_super = nfs4_fill_super;
export_path = data->nfs_server.export_path;
data->nfs_server.export_path = "/";
root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
data->nfs_server.hostname);
data->nfs_server.export_path = export_path;
res = nfs_follow_remote_path(root_mnt, export_path);
dfprintk(MOUNT, "<-- nfs4_try_mount() = %ld%s\n",
IS_ERR(res) ? PTR_ERR(res) : 0,
IS_ERR(res) ? " [error]" : "");
return res;
}
static void nfs4_kill_super(struct super_block *sb)
{
struct nfs_server *server = NFS_SB(sb);
dprintk("--> %s\n", __func__);
nfs_super_return_all_delegations(sb);
kill_anon_super(sb);
nfs_fscache_release_super_cookie(sb);
nfs_free_server(server);
dprintk("<-- %s\n", __func__);
}
/*
* Clone an NFS4 server record on xdev traversal (FSID-change)
*/
static struct dentry *
nfs4_xdev_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *raw_data)
{
struct nfs_mount_info mount_info = {
.fill_super = nfs4_clone_super,
.set_security = nfs_clone_sb_security,
.cloned = raw_data,
};
return nfs_xdev_mount_common(&nfs4_fs_type, flags, dev_name, &mount_info);
}
static struct dentry *
nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *raw_data)
{
struct nfs_mount_info mount_info = {
.fill_super = nfs4_fill_super,
.set_security = nfs_clone_sb_security,
.cloned = raw_data,
};
struct nfs_server *server;
struct dentry *mntroot = ERR_PTR(-ENOMEM);
dprintk("--> nfs4_referral_get_sb()\n");
mount_info.mntfh = nfs_alloc_fhandle();
if (mount_info.cloned == NULL || mount_info.mntfh == NULL)
goto out;
/* create a new volume representation */
server = nfs4_create_referral_server(mount_info.cloned, mount_info.mntfh);
if (IS_ERR(server)) {
mntroot = ERR_CAST(server);
goto out;
}
mntroot = nfs_fs_mount_common(&nfs4_fs_type, server, flags, dev_name, &mount_info);
out:
nfs_free_fhandle(mount_info.mntfh);
return mntroot;
}
/*
* Create an NFS4 server record on referral traversal
*/
static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data)
{
struct nfs_clone_mount *data = raw_data;
char *export_path;
struct vfsmount *root_mnt;
struct dentry *res;
dprintk("--> nfs4_referral_mount()\n");
export_path = data->mnt_path;
data->mnt_path = "/";
root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
flags, data, data->hostname);
data->mnt_path = export_path;
res = nfs_follow_remote_path(root_mnt, export_path);
dprintk("<-- nfs4_referral_mount() = %ld%s\n",
IS_ERR(res) ? PTR_ERR(res) : 0,
IS_ERR(res) ? " [error]" : "");
return res;
}
#endif /* CONFIG_NFS_V4 */ #endif /* CONFIG_NFS_V4 */

View File

@ -9,37 +9,11 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/nfs4.h>
#include <linux/nfs_idmap.h>
#include <linux/nfs_fs.h> #include <linux/nfs_fs.h>
#include "callback.h"
#ifdef CONFIG_NFS_V4
static const int nfs_set_port_min = 0;
static const int nfs_set_port_max = 65535;
#endif
static struct ctl_table_header *nfs_callback_sysctl_table; static struct ctl_table_header *nfs_callback_sysctl_table;
static ctl_table nfs_cb_sysctls[] = { static ctl_table nfs_cb_sysctls[] = {
#ifdef CONFIG_NFS_V4
{
.procname = "nfs_callback_tcpport",
.data = &nfs_callback_set_tcpport,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = (int *)&nfs_set_port_min,
.extra2 = (int *)&nfs_set_port_max,
},
{
.procname = "idmap_cache_timeout",
.data = &nfs_idmap_cache_timeout,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
#endif
{ {
.procname = "nfs_mountpoint_timeout", .procname = "nfs_mountpoint_timeout",
.data = &nfs_mountpoint_expiry_timeout, .data = &nfs_mountpoint_expiry_timeout,

View File

@ -501,7 +501,7 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry)
(unsigned long long)NFS_FILEID(dentry->d_inode)); (unsigned long long)NFS_FILEID(dentry->d_inode));
/* Return delegation in anticipation of the rename */ /* Return delegation in anticipation of the rename */
nfs_inode_return_delegation(dentry->d_inode); NFS_PROTO(dentry->d_inode)->return_delegation(dentry->d_inode);
sdentry = NULL; sdentry = NULL;
do { do {

View File

@ -336,8 +336,10 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
struct nfs_pageio_descriptor pgio; struct nfs_pageio_descriptor pgio;
int err; int err;
nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc), NFS_PROTO(page->mapping->host)->write_pageio_init(&pgio,
&nfs_async_write_completion_ops); page->mapping->host,
wb_priority(wbc),
&nfs_async_write_completion_ops);
err = nfs_do_writepage(page, wbc, &pgio); err = nfs_do_writepage(page, wbc, &pgio);
nfs_pageio_complete(&pgio); nfs_pageio_complete(&pgio);
if (err < 0) if (err < 0)
@ -380,8 +382,7 @@ int nfs_writepages(struct address_space *mapping, struct writeback_control *wbc)
nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES); nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGES);
nfs_pageio_init_write(&pgio, inode, wb_priority(wbc), NFS_PROTO(inode)->write_pageio_init(&pgio, inode, wb_priority(wbc), &nfs_async_write_completion_ops);
&nfs_async_write_completion_ops);
err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio); err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &pgio);
nfs_pageio_complete(&pgio); nfs_pageio_complete(&pgio);
@ -410,7 +411,7 @@ static void nfs_inode_add_request(struct inode *inode, struct nfs_page *req)
nfs_lock_request(req); nfs_lock_request(req);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
if (!nfsi->npages && nfs_have_delegation(inode, FMODE_WRITE)) if (!nfsi->npages && NFS_PROTO(inode)->have_delegation(inode, FMODE_WRITE))
inode->i_version++; inode->i_version++;
set_bit(PG_MAPPED, &req->wb_flags); set_bit(PG_MAPPED, &req->wb_flags);
SetPagePrivate(req->wb_page); SetPagePrivate(req->wb_page);
@ -620,7 +621,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)
goto next; goto next;
} }
if (test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) { if (test_bit(NFS_IOHDR_NEED_COMMIT, &hdr->flags)) {
memcpy(&req->wb_verf, hdr->verf, sizeof(req->wb_verf)); memcpy(&req->wb_verf, &hdr->verf->verifier, sizeof(req->wb_verf));
nfs_mark_request_commit(req, hdr->lseg, &cinfo); nfs_mark_request_commit(req, hdr->lseg, &cinfo);
goto next; goto next;
} }
@ -1202,7 +1203,7 @@ static const struct nfs_pageio_ops nfs_pageio_write_ops = {
.pg_doio = nfs_generic_pg_writepages, .pg_doio = nfs_generic_pg_writepages,
}; };
void nfs_pageio_init_write_mds(struct nfs_pageio_descriptor *pgio, void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
struct inode *inode, int ioflags, struct inode *inode, int ioflags,
const struct nfs_pgio_completion_ops *compl_ops) const struct nfs_pgio_completion_ops *compl_ops)
{ {
@ -1217,13 +1218,6 @@ void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio)
} }
EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds); EXPORT_SYMBOL_GPL(nfs_pageio_reset_write_mds);
void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
struct inode *inode, int ioflags,
const struct nfs_pgio_completion_ops *compl_ops)
{
if (!pnfs_pageio_init_write(pgio, inode, ioflags, compl_ops))
nfs_pageio_init_write_mds(pgio, inode, ioflags, compl_ops);
}
void nfs_write_prepare(struct rpc_task *task, void *calldata) void nfs_write_prepare(struct rpc_task *task, void *calldata)
{ {
@ -1547,7 +1541,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
/* Okay, COMMIT succeeded, apparently. Check the verifier /* Okay, COMMIT succeeded, apparently. Check the verifier
* returned by the server against all stored verfs. */ * returned by the server against all stored verfs. */
if (!memcmp(req->wb_verf.verifier, data->verf.verifier, sizeof(data->verf.verifier))) { if (!memcmp(&req->wb_verf, &data->verf.verifier, sizeof(req->wb_verf))) {
/* We have a match */ /* We have a match */
nfs_inode_remove_request(req); nfs_inode_remove_request(req);
dprintk(" OK\n"); dprintk(" OK\n");
@ -1677,9 +1671,14 @@ static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_contr
int nfs_write_inode(struct inode *inode, struct writeback_control *wbc) int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
{ {
int ret; return nfs_commit_unstable_pages(inode, wbc);
}
#ifdef CONFIG_NFS_V4
int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc)
{
int ret = nfs_write_inode(inode, wbc);
ret = nfs_commit_unstable_pages(inode, wbc);
if (ret >= 0 && test_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags)) { if (ret >= 0 && test_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(inode)->flags)) {
int status; int status;
bool sync = true; bool sync = true;
@ -1693,6 +1692,7 @@ int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
} }
return ret; return ret;
} }
#endif
/* /*
* flush the inode to disk. * flush the inode to disk.

View File

@ -427,10 +427,6 @@ extern __be32 root_nfs_parse_addr(char *name); /*__init*/
/* /*
* linux/fs/nfs/file.c * linux/fs/nfs/file.c
*/ */
extern const struct inode_operations nfs_file_inode_operations;
#ifdef CONFIG_NFS_V3
extern const struct inode_operations nfs3_file_inode_operations;
#endif /* CONFIG_NFS_V3 */
extern const struct file_operations nfs_file_operations; extern const struct file_operations nfs_file_operations;
#ifdef CONFIG_NFS_V4 #ifdef CONFIG_NFS_V4
extern const struct file_operations nfs4_file_operations; extern const struct file_operations nfs4_file_operations;
@ -485,10 +481,6 @@ extern ssize_t nfs_file_direct_write(struct kiocb *iocb,
/* /*
* linux/fs/nfs/dir.c * linux/fs/nfs/dir.c
*/ */
extern const struct inode_operations nfs_dir_inode_operations;
#ifdef CONFIG_NFS_V3
extern const struct inode_operations nfs3_dir_inode_operations;
#endif /* CONFIG_NFS_V3 */
extern const struct file_operations nfs_dir_operations; extern const struct file_operations nfs_dir_operations;
extern const struct dentry_operations nfs_dentry_operations; extern const struct dentry_operations nfs_dentry_operations;

View File

@ -69,10 +69,9 @@ struct nfs_client {
struct idmap * cl_idmap; struct idmap * cl_idmap;
/* Our own IP address, as a null-terminated string. /* Our own IP address, as a null-terminated string.
* This is used to generate the clientid, and the callback address. * This is used to generate the mv0 callback address.
*/ */
char cl_ipaddr[48]; char cl_ipaddr[48];
unsigned char cl_id_uniquifier;
u32 cl_cb_ident; /* v4.0 callback identifier */ u32 cl_cb_ident; /* v4.0 callback identifier */
const struct nfs4_minor_version_ops *cl_mvops; const struct nfs4_minor_version_ops *cl_mvops;

View File

@ -42,7 +42,7 @@ struct nfs_page {
wb_bytes; /* Length of request */ wb_bytes; /* Length of request */
struct kref wb_kref; /* reference count */ struct kref wb_kref; /* reference count */
unsigned long wb_flags; unsigned long wb_flags;
struct nfs_writeverf wb_verf; /* Commit cookie */ struct nfs_write_verifier wb_verf; /* Commit cookie */
}; };
struct nfs_pageio_descriptor; struct nfs_pageio_descriptor;

View File

@ -514,9 +514,13 @@ struct nfs_writeargs {
struct nfs4_sequence_args seq_args; struct nfs4_sequence_args seq_args;
}; };
struct nfs_write_verifier {
char data[8];
};
struct nfs_writeverf { struct nfs_writeverf {
struct nfs_write_verifier verifier;
enum nfs3_stable_how committed; enum nfs3_stable_how committed;
__be32 verifier[2];
}; };
struct nfs_writeres { struct nfs_writeres {
@ -1349,6 +1353,8 @@ struct nfs_renamedata {
struct nfs_access_entry; struct nfs_access_entry;
struct nfs_client; struct nfs_client;
struct rpc_timeout; struct rpc_timeout;
struct nfs_client_initdata;
struct nfs_pageio_descriptor;
/* /*
* RPC procedure vector for NFSv2/NFSv3 demuxing * RPC procedure vector for NFSv2/NFSv3 demuxing
@ -1402,9 +1408,13 @@ struct nfs_rpc_ops {
int (*set_capabilities)(struct nfs_server *, struct nfs_fh *); int (*set_capabilities)(struct nfs_server *, struct nfs_fh *);
int (*decode_dirent)(struct xdr_stream *, struct nfs_entry *, int); int (*decode_dirent)(struct xdr_stream *, struct nfs_entry *, int);
void (*read_setup) (struct nfs_read_data *, struct rpc_message *); void (*read_setup) (struct nfs_read_data *, struct rpc_message *);
void (*read_pageio_init)(struct nfs_pageio_descriptor *, struct inode *,
const struct nfs_pgio_completion_ops *);
void (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *); void (*read_rpc_prepare)(struct rpc_task *, struct nfs_read_data *);
int (*read_done) (struct rpc_task *, struct nfs_read_data *); int (*read_done) (struct rpc_task *, struct nfs_read_data *);
void (*write_setup) (struct nfs_write_data *, struct rpc_message *); void (*write_setup) (struct nfs_write_data *, struct rpc_message *);
void (*write_pageio_init)(struct nfs_pageio_descriptor *, struct inode *, int,
const struct nfs_pgio_completion_ops *);
void (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *); void (*write_rpc_prepare)(struct rpc_task *, struct nfs_write_data *);
int (*write_done) (struct rpc_task *, struct nfs_write_data *); int (*write_done) (struct rpc_task *, struct nfs_write_data *);
void (*commit_setup) (struct nfs_commit_data *, struct rpc_message *); void (*commit_setup) (struct nfs_commit_data *, struct rpc_message *);
@ -1418,9 +1428,13 @@ struct nfs_rpc_ops {
struct nfs_open_context *ctx, struct nfs_open_context *ctx,
int open_flags, int open_flags,
struct iattr *iattr); struct iattr *iattr);
int (*have_delegation)(struct inode *, fmode_t);
int (*return_delegation)(struct inode *);
struct nfs_client *(*alloc_client) (const struct nfs_client_initdata *);
struct nfs_client * struct nfs_client *
(*init_client) (struct nfs_client *, const struct rpc_timeout *, (*init_client) (struct nfs_client *, const struct rpc_timeout *,
const char *, rpc_authflavor_t); const char *, rpc_authflavor_t);
void (*free_client) (struct nfs_client *);
}; };
/* /*

View File

@ -101,6 +101,7 @@ struct rpc_authops {
struct rpc_cred * (*crcreate)(struct rpc_auth*, struct auth_cred *, int); struct rpc_cred * (*crcreate)(struct rpc_auth*, struct auth_cred *, int);
int (*pipes_create)(struct rpc_auth *); int (*pipes_create)(struct rpc_auth *);
void (*pipes_destroy)(struct rpc_auth *); void (*pipes_destroy)(struct rpc_auth *);
int (*list_pseudoflavors)(rpc_authflavor_t *, int);
}; };
struct rpc_credops { struct rpc_credops {
@ -135,6 +136,7 @@ int rpcauth_register(const struct rpc_authops *);
int rpcauth_unregister(const struct rpc_authops *); int rpcauth_unregister(const struct rpc_authops *);
struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *); struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
void rpcauth_release(struct rpc_auth *); void rpcauth_release(struct rpc_auth *);
int rpcauth_list_flavors(rpc_authflavor_t *, int);
struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int); struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int);
void rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *); void rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *);
struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int); struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int);

View File

@ -14,6 +14,7 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/sunrpc/xdr.h> #include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/msg_prot.h>
#include <linux/uio.h> #include <linux/uio.h>
/* The mechanism-independent gss-api context: */ /* The mechanism-independent gss-api context: */
@ -127,7 +128,7 @@ struct gss_api_mech *gss_mech_get_by_name(const char *);
struct gss_api_mech *gss_mech_get_by_pseudoflavor(u32); struct gss_api_mech *gss_mech_get_by_pseudoflavor(u32);
/* Fill in an array with a list of supported pseudoflavors */ /* Fill in an array with a list of supported pseudoflavors */
int gss_mech_list_pseudoflavors(u32 *); int gss_mech_list_pseudoflavors(rpc_authflavor_t *, int);
/* Just increments the mechanism's reference count and returns its input: */ /* Just increments the mechanism's reference count and returns its input: */
struct gss_api_mech * gss_mech_get(struct gss_api_mech *); struct gss_api_mech * gss_mech_get(struct gss_api_mech *);

View File

@ -104,8 +104,6 @@ __be32 *xdr_decode_string_inplace(__be32 *p, char **sp, unsigned int *lenp,
__be32 *xdr_encode_netobj(__be32 *p, const struct xdr_netobj *); __be32 *xdr_encode_netobj(__be32 *p, const struct xdr_netobj *);
__be32 *xdr_decode_netobj(__be32 *p, struct xdr_netobj *); __be32 *xdr_decode_netobj(__be32 *p, struct xdr_netobj *);
void xdr_encode_pages(struct xdr_buf *, struct page **, unsigned int,
unsigned int);
void xdr_inline_pages(struct xdr_buf *, unsigned int, void xdr_inline_pages(struct xdr_buf *, unsigned int,
struct page **, unsigned int, unsigned int); struct page **, unsigned int, unsigned int);
void xdr_terminate_string(struct xdr_buf *, const u32); void xdr_terminate_string(struct xdr_buf *, const u32);
@ -205,6 +203,7 @@ struct xdr_stream {
struct kvec *iov; /* pointer to the current kvec */ struct kvec *iov; /* pointer to the current kvec */
struct kvec scratch; /* Scratch buffer */ struct kvec scratch; /* Scratch buffer */
struct page **page_ptr; /* pointer to the current page */ struct page **page_ptr; /* pointer to the current page */
unsigned int nwords; /* Remaining decode buffer length */
}; };
/* /*
@ -217,12 +216,13 @@ extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32
extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
unsigned int base, unsigned int len); unsigned int base, unsigned int len);
extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr);
extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
extern void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, extern void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
struct page **pages, unsigned int len); struct page **pages, unsigned int len);
extern void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen); extern void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen);
extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes); extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes);
extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len); extern unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len); extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len);
extern int xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data); extern int xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data);

View File

@ -13,6 +13,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/hash.h> #include <linux/hash.h>
#include <linux/sunrpc/clnt.h> #include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/gss_api.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#ifdef RPC_DEBUG #ifdef RPC_DEBUG
@ -122,6 +123,59 @@ rpcauth_unregister(const struct rpc_authops *ops)
} }
EXPORT_SYMBOL_GPL(rpcauth_unregister); EXPORT_SYMBOL_GPL(rpcauth_unregister);
/**
* rpcauth_list_flavors - discover registered flavors and pseudoflavors
* @array: array to fill in
* @size: size of "array"
*
* Returns the number of array items filled in, or a negative errno.
*
* The returned array is not sorted by any policy. Callers should not
* rely on the order of the items in the returned array.
*/
int
rpcauth_list_flavors(rpc_authflavor_t *array, int size)
{
rpc_authflavor_t flavor;
int result = 0;
spin_lock(&rpc_authflavor_lock);
for (flavor = 0; flavor < RPC_AUTH_MAXFLAVOR; flavor++) {
const struct rpc_authops *ops = auth_flavors[flavor];
rpc_authflavor_t pseudos[4];
int i, len;
if (result >= size) {
result = -ENOMEM;
break;
}
if (ops == NULL)
continue;
if (ops->list_pseudoflavors == NULL) {
array[result++] = ops->au_flavor;
continue;
}
len = ops->list_pseudoflavors(pseudos, ARRAY_SIZE(pseudos));
if (len < 0) {
result = len;
break;
}
for (i = 0; i < len; i++) {
if (result >= size) {
result = -ENOMEM;
break;
}
array[result++] = pseudos[i];
}
}
spin_unlock(&rpc_authflavor_lock);
dprintk("RPC: %s returns %d\n", __func__, result);
return result;
}
EXPORT_SYMBOL_GPL(rpcauth_list_flavors);
struct rpc_auth * struct rpc_auth *
rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
{ {

View File

@ -1619,6 +1619,7 @@ static const struct rpc_authops authgss_ops = {
.crcreate = gss_create_cred, .crcreate = gss_create_cred,
.pipes_create = gss_pipes_dentries_create, .pipes_create = gss_pipes_dentries_create,
.pipes_destroy = gss_pipes_dentries_destroy, .pipes_destroy = gss_pipes_dentries_destroy,
.list_pseudoflavors = gss_mech_list_pseudoflavors,
}; };
static const struct rpc_credops gss_credops = { static const struct rpc_credops gss_credops = {

View File

@ -239,14 +239,28 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor); EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor);
int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr) /**
* gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors
* @array: array to fill in
* @size: size of "array"
*
* Returns the number of array items filled in, or a negative errno.
*
* The returned array is not sorted by any policy. Callers should not
* rely on the order of the items in the returned array.
*/
int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr, int size)
{ {
struct gss_api_mech *pos = NULL; struct gss_api_mech *pos = NULL;
int j, i = 0; int j, i = 0;
spin_lock(&registered_mechs_lock); spin_lock(&registered_mechs_lock);
list_for_each_entry(pos, &registered_mechs, gm_list) { list_for_each_entry(pos, &registered_mechs, gm_list) {
for (j=0; j < pos->gm_pf_num; j++) { for (j = 0; j < pos->gm_pf_num; j++) {
if (i >= size) {
spin_unlock(&registered_mechs_lock);
return -ENOMEM;
}
array_ptr[i++] = pos->gm_pfs[j].pseudoflavor; array_ptr[i++] = pos->gm_pfs[j].pseudoflavor;
} }
} }
@ -254,8 +268,6 @@ int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr)
return i; return i;
} }
EXPORT_SYMBOL_GPL(gss_mech_list_pseudoflavors);
u32 u32
gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service) gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
{ {

View File

@ -1844,12 +1844,13 @@ call_timeout(struct rpc_task *task)
return; return;
} }
if (RPC_IS_SOFT(task)) { if (RPC_IS_SOFT(task)) {
if (clnt->cl_chatty) if (clnt->cl_chatty) {
rcu_read_lock(); rcu_read_lock();
printk(KERN_NOTICE "%s: server %s not responding, timed out\n", printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
clnt->cl_protname, clnt->cl_protname,
rcu_dereference(clnt->cl_xprt)->servername); rcu_dereference(clnt->cl_xprt)->servername);
rcu_read_unlock(); rcu_read_unlock();
}
if (task->tk_flags & RPC_TASK_TIMEOUT) if (task->tk_flags & RPC_TASK_TIMEOUT)
rpc_exit(task, -ETIMEDOUT); rpc_exit(task, -ETIMEDOUT);
else else

View File

@ -128,34 +128,6 @@ xdr_terminate_string(struct xdr_buf *buf, const u32 len)
} }
EXPORT_SYMBOL_GPL(xdr_terminate_string); EXPORT_SYMBOL_GPL(xdr_terminate_string);
void
xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
unsigned int len)
{
struct kvec *tail = xdr->tail;
u32 *p;
xdr->pages = pages;
xdr->page_base = base;
xdr->page_len = len;
p = (u32 *)xdr->head[0].iov_base + XDR_QUADLEN(xdr->head[0].iov_len);
tail->iov_base = p;
tail->iov_len = 0;
if (len & 3) {
unsigned int pad = 4 - (len & 3);
*p = 0;
tail->iov_base = (char *)p + (len & 3);
tail->iov_len = pad;
len += pad;
}
xdr->buflen += len;
xdr->len += len;
}
EXPORT_SYMBOL_GPL(xdr_encode_pages);
void void
xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset, xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
struct page **pages, unsigned int base, unsigned int len) struct page **pages, unsigned int base, unsigned int len)
@ -456,6 +428,16 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len)
} }
EXPORT_SYMBOL_GPL(xdr_shift_buf); EXPORT_SYMBOL_GPL(xdr_shift_buf);
/**
* xdr_stream_pos - Return the current offset from the start of the xdr_stream
* @xdr: pointer to struct xdr_stream
*/
unsigned int xdr_stream_pos(const struct xdr_stream *xdr)
{
return (unsigned int)(XDR_QUADLEN(xdr->buf->len) - xdr->nwords) << 2;
}
EXPORT_SYMBOL_GPL(xdr_stream_pos);
/** /**
* xdr_init_encode - Initialize a struct xdr_stream for sending data. * xdr_init_encode - Initialize a struct xdr_stream for sending data.
* @xdr: pointer to xdr_stream struct * @xdr: pointer to xdr_stream struct
@ -556,13 +538,11 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int b
EXPORT_SYMBOL_GPL(xdr_write_pages); EXPORT_SYMBOL_GPL(xdr_write_pages);
static void xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov, static void xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov,
__be32 *p, unsigned int len) unsigned int len)
{ {
if (len > iov->iov_len) if (len > iov->iov_len)
len = iov->iov_len; len = iov->iov_len;
if (p == NULL) xdr->p = (__be32*)iov->iov_base;
p = (__be32*)iov->iov_base;
xdr->p = p;
xdr->end = (__be32*)(iov->iov_base + len); xdr->end = (__be32*)(iov->iov_base + len);
xdr->iov = iov; xdr->iov = iov;
xdr->page_ptr = NULL; xdr->page_ptr = NULL;
@ -609,7 +589,7 @@ static void xdr_set_next_page(struct xdr_stream *xdr)
newbase -= xdr->buf->page_base; newbase -= xdr->buf->page_base;
if (xdr_set_page_base(xdr, newbase, PAGE_SIZE) < 0) if (xdr_set_page_base(xdr, newbase, PAGE_SIZE) < 0)
xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len); xdr_set_iov(xdr, xdr->buf->tail, xdr->buf->len);
} }
static bool xdr_set_next_buffer(struct xdr_stream *xdr) static bool xdr_set_next_buffer(struct xdr_stream *xdr)
@ -618,7 +598,7 @@ static bool xdr_set_next_buffer(struct xdr_stream *xdr)
xdr_set_next_page(xdr); xdr_set_next_page(xdr);
else if (xdr->iov == xdr->buf->head) { else if (xdr->iov == xdr->buf->head) {
if (xdr_set_page_base(xdr, 0, PAGE_SIZE) < 0) if (xdr_set_page_base(xdr, 0, PAGE_SIZE) < 0)
xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len); xdr_set_iov(xdr, xdr->buf->tail, xdr->buf->len);
} }
return xdr->p != xdr->end; return xdr->p != xdr->end;
} }
@ -634,10 +614,15 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
xdr->buf = buf; xdr->buf = buf;
xdr->scratch.iov_base = NULL; xdr->scratch.iov_base = NULL;
xdr->scratch.iov_len = 0; xdr->scratch.iov_len = 0;
xdr->nwords = XDR_QUADLEN(buf->len);
if (buf->head[0].iov_len != 0) if (buf->head[0].iov_len != 0)
xdr_set_iov(xdr, buf->head, p, buf->len); xdr_set_iov(xdr, buf->head, buf->len);
else if (buf->page_len != 0) else if (buf->page_len != 0)
xdr_set_page_base(xdr, 0, buf->len); xdr_set_page_base(xdr, 0, buf->len);
if (p != NULL && p > xdr->p && xdr->end >= p) {
xdr->nwords -= p - xdr->p;
xdr->p = p;
}
} }
EXPORT_SYMBOL_GPL(xdr_init_decode); EXPORT_SYMBOL_GPL(xdr_init_decode);
@ -662,12 +647,14 @@ EXPORT_SYMBOL_GPL(xdr_init_decode_pages);
static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
{ {
unsigned int nwords = XDR_QUADLEN(nbytes);
__be32 *p = xdr->p; __be32 *p = xdr->p;
__be32 *q = p + XDR_QUADLEN(nbytes); __be32 *q = p + nwords;
if (unlikely(q > xdr->end || q < p)) if (unlikely(nwords > xdr->nwords || q > xdr->end || q < p))
return NULL; return NULL;
xdr->p = q; xdr->p = q;
xdr->nwords -= nwords;
return p; return p;
} }
@ -734,6 +721,31 @@ __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
} }
EXPORT_SYMBOL_GPL(xdr_inline_decode); EXPORT_SYMBOL_GPL(xdr_inline_decode);
static unsigned int xdr_align_pages(struct xdr_stream *xdr, unsigned int len)
{
struct xdr_buf *buf = xdr->buf;
struct kvec *iov;
unsigned int nwords = XDR_QUADLEN(len);
unsigned int cur = xdr_stream_pos(xdr);
if (xdr->nwords == 0)
return 0;
if (nwords > xdr->nwords) {
nwords = xdr->nwords;
len = nwords << 2;
}
/* Realign pages to current pointer position */
iov = buf->head;
if (iov->iov_len > cur)
xdr_shrink_bufhead(buf, iov->iov_len - cur);
/* Truncate page data and move it into the tail */
if (buf->page_len > len)
xdr_shrink_pagelen(buf, buf->page_len - len);
xdr->nwords = XDR_QUADLEN(buf->len - cur);
return len;
}
/** /**
* xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position
* @xdr: pointer to xdr_stream struct * @xdr: pointer to xdr_stream struct
@ -742,39 +754,37 @@ EXPORT_SYMBOL_GPL(xdr_inline_decode);
* Moves data beyond the current pointer position from the XDR head[] buffer * Moves data beyond the current pointer position from the XDR head[] buffer
* into the page list. Any data that lies beyond current position + "len" * into the page list. Any data that lies beyond current position + "len"
* bytes is moved into the XDR tail[]. * bytes is moved into the XDR tail[].
*
* Returns the number of XDR encoded bytes now contained in the pages
*/ */
void xdr_read_pages(struct xdr_stream *xdr, unsigned int len) unsigned int xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
{ {
struct xdr_buf *buf = xdr->buf; struct xdr_buf *buf = xdr->buf;
struct kvec *iov; struct kvec *iov;
ssize_t shift; unsigned int nwords;
unsigned int end; unsigned int end;
int padding; unsigned int padding;
/* Realign pages to current pointer position */ len = xdr_align_pages(xdr, len);
iov = buf->head; if (len == 0)
shift = iov->iov_len + (char *)iov->iov_base - (char *)xdr->p; return 0;
if (shift > 0) nwords = XDR_QUADLEN(len);
xdr_shrink_bufhead(buf, shift); padding = (nwords << 2) - len;
/* Truncate page data and move it into the tail */
if (buf->page_len > len)
xdr_shrink_pagelen(buf, buf->page_len - len);
padding = (XDR_QUADLEN(len) << 2) - len;
xdr->iov = iov = buf->tail; xdr->iov = iov = buf->tail;
/* Compute remaining message length. */ /* Compute remaining message length. */
end = iov->iov_len; end = ((xdr->nwords - nwords) << 2) + padding;
shift = buf->buflen - buf->len; if (end > iov->iov_len)
if (shift < end) end = iov->iov_len;
end -= shift;
else if (shift > 0)
end = 0;
/* /*
* Position current pointer at beginning of tail, and * Position current pointer at beginning of tail, and
* set remaining message length. * set remaining message length.
*/ */
xdr->p = (__be32 *)((char *)iov->iov_base + padding); xdr->p = (__be32 *)((char *)iov->iov_base + padding);
xdr->end = (__be32 *)((char *)iov->iov_base + end); xdr->end = (__be32 *)((char *)iov->iov_base + end);
xdr->page_ptr = NULL;
xdr->nwords = XDR_QUADLEN(end - padding);
return len;
} }
EXPORT_SYMBOL_GPL(xdr_read_pages); EXPORT_SYMBOL_GPL(xdr_read_pages);
@ -790,12 +800,13 @@ EXPORT_SYMBOL_GPL(xdr_read_pages);
*/ */
void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) void xdr_enter_page(struct xdr_stream *xdr, unsigned int len)
{ {
xdr_read_pages(xdr, len); len = xdr_align_pages(xdr, len);
/* /*
* Position current pointer at beginning of tail, and * Position current pointer at beginning of tail, and
* set remaining message length. * set remaining message length.
*/ */
xdr_set_page_base(xdr, 0, len); if (len != 0)
xdr_set_page_base(xdr, 0, len);
} }
EXPORT_SYMBOL_GPL(xdr_enter_page); EXPORT_SYMBOL_GPL(xdr_enter_page);