forked from Minki/linux
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:
commit
1fad1e9a74
@ -88,9 +88,8 @@ config NFS_V4
|
||||
|
||||
config NFS_V4_1
|
||||
bool "NFS client support for NFSv4.1 (EXPERIMENTAL)"
|
||||
depends on NFS_FS && NFS_V4 && EXPERIMENTAL
|
||||
depends on NFS_V4 && EXPERIMENTAL
|
||||
select SUNRPC_BACKCHANNEL
|
||||
select PNFS_FILE_LAYOUT
|
||||
help
|
||||
This option enables support for minor version 1 of the NFSv4 protocol
|
||||
(RFC 5661) in the kernel's NFS client.
|
||||
@ -99,15 +98,17 @@ config NFS_V4_1
|
||||
|
||||
config PNFS_FILE_LAYOUT
|
||||
tristate
|
||||
depends on NFS_V4_1
|
||||
default m
|
||||
|
||||
config PNFS_BLOCK
|
||||
tristate
|
||||
depends on NFS_FS && NFS_V4_1 && BLK_DEV_DM
|
||||
depends on NFS_V4_1 && BLK_DEV_DM
|
||||
default m
|
||||
|
||||
config PNFS_OBJLAYOUT
|
||||
tristate
|
||||
depends on NFS_FS && NFS_V4_1 && SCSI_OSD_ULD
|
||||
depends on NFS_V4_1 && SCSI_OSD_ULD
|
||||
default m
|
||||
|
||||
config NFS_V4_1_IMPLEMENTATION_ID_DOMAIN
|
||||
|
@ -13,11 +13,16 @@ nfs-$(CONFIG_NFS_V2) += proc.o nfs2xdr.o
|
||||
nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
|
||||
nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.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 \
|
||||
nfs4namespace.o
|
||||
nfs4namespace.o nfs4getroot.o nfs4client.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
|
||||
|
||||
obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o
|
||||
|
685
fs/nfs/client.c
685
fs/nfs/client.c
@ -56,35 +56,6 @@
|
||||
#define NFSDBG_FACILITY NFSDBG_CLIENT
|
||||
|
||||
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
|
||||
@ -130,24 +101,13 @@ const struct rpc_program nfsacl_program = {
|
||||
};
|
||||
#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
|
||||
*
|
||||
* Since these are allocated/deallocated very rarely, we don't
|
||||
* 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 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_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("*");
|
||||
if (!IS_ERR(cred))
|
||||
clp->cl_machine_cred = cred;
|
||||
@ -203,45 +151,6 @@ error_0:
|
||||
}
|
||||
|
||||
#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 */
|
||||
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");
|
||||
}
|
||||
|
||||
static void nfs4_destroy_server(struct nfs_server *server)
|
||||
{
|
||||
nfs4_purge_state_owners(server);
|
||||
}
|
||||
|
||||
#else
|
||||
static void nfs4_shutdown_client(struct nfs_client *clp)
|
||||
{
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
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);
|
||||
|
||||
nfs4_shutdown_client(clp);
|
||||
|
||||
nfs_fscache_release_client_cookie(clp);
|
||||
|
||||
/* -EIO all pending I/O */
|
||||
@ -333,7 +231,7 @@ void nfs_put_client(struct nfs_client *clp)
|
||||
|
||||
BUG_ON(!list_empty(&clp->cl_superblocks));
|
||||
|
||||
nfs_free_client(clp);
|
||||
clp->rpc_ops->free_client(clp);
|
||||
}
|
||||
}
|
||||
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,
|
||||
* by comparing (only) relevant fields, excluding the port number.
|
||||
*/
|
||||
static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
|
||||
const struct sockaddr *sa2)
|
||||
int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
|
||||
const struct sockaddr *sa2)
|
||||
{
|
||||
if (sa1->sa_family != sa2->sa_family)
|
||||
return 0;
|
||||
@ -447,33 +345,6 @@ static int nfs_sockaddr_cmp(const struct sockaddr *sa1,
|
||||
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
|
||||
* 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
|
||||
* - 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,
|
||||
const struct rpc_timeout *timeparms,
|
||||
const char *ip_addr,
|
||||
@ -572,7 +443,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
|
||||
if (clp) {
|
||||
spin_unlock(&nn->nfs_client_lock);
|
||||
if (new)
|
||||
nfs_free_client(new);
|
||||
new->rpc_ops->free_client(new);
|
||||
return nfs_found_client(cl_init, clp);
|
||||
}
|
||||
if (new) {
|
||||
@ -586,7 +457,7 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
|
||||
|
||||
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));
|
||||
|
||||
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
|
||||
*/
|
||||
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)
|
||||
{
|
||||
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
|
||||
*/
|
||||
static int nfs_create_rpc_client(struct nfs_client *clp,
|
||||
const struct rpc_timeout *timeparms,
|
||||
rpc_authflavor_t flavor)
|
||||
int nfs_create_rpc_client(struct nfs_client *clp,
|
||||
const struct rpc_timeout *timeparms,
|
||||
rpc_authflavor_t flavor)
|
||||
{
|
||||
struct rpc_clnt *clnt = NULL;
|
||||
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
|
||||
*/
|
||||
static int nfs_init_server_rpcclient(struct nfs_server *server,
|
||||
int nfs_init_server_rpcclient(struct nfs_server *server,
|
||||
const struct rpc_timeout *timeo,
|
||||
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->wpages = (server->wsize + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
||||
server->pnfs_blksize = fsinfo->blksize;
|
||||
set_pnfs_layoutdriver(server, mntfh, fsinfo->layouttype);
|
||||
|
||||
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
|
||||
*/
|
||||
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_client *clp = server->nfs_client;
|
||||
@ -1045,7 +915,7 @@ out_error:
|
||||
/*
|
||||
* 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->rsize = source->rsize;
|
||||
@ -1058,7 +928,7 @@ static void nfs_server_copy_userdata(struct nfs_server *target, struct nfs_serve
|
||||
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_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
|
||||
*/
|
||||
static struct nfs_server *nfs_alloc_server(void)
|
||||
struct nfs_server *nfs_alloc_server(void)
|
||||
{
|
||||
struct nfs_server *server;
|
||||
|
||||
@ -1138,7 +1008,6 @@ void nfs_free_server(struct nfs_server *server)
|
||||
dprintk("--> nfs_free_server()\n");
|
||||
|
||||
nfs_server_remove_lists(server);
|
||||
unset_pnfs_layoutdriver(server);
|
||||
|
||||
if (server->destroy != NULL)
|
||||
server->destroy(server);
|
||||
@ -1226,522 +1095,6 @@ 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
|
||||
*/
|
||||
@ -2091,7 +1444,3 @@ void nfs_fs_proc_exit(void)
|
||||
}
|
||||
|
||||
#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'");
|
||||
|
@ -47,7 +47,7 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
|
||||
*
|
||||
* 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;
|
||||
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.
|
||||
*/
|
||||
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_inode *nfsi = NFS_I(inode);
|
||||
@ -417,9 +417,8 @@ static void nfs_mark_return_delegation(struct nfs_server *server,
|
||||
* @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_delegation *delegation;
|
||||
|
||||
|
@ -33,12 +33,12 @@ enum {
|
||||
|
||||
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);
|
||||
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);
|
||||
void nfs_inode_return_delegation_noreclaim(struct inode *inode);
|
||||
|
||||
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_delegation_types(struct nfs_client *clp, fmode_t flags);
|
||||
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);
|
||||
|
||||
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
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
107
fs/nfs/dir.c
107
fs/nfs/dir.c
@ -46,16 +46,6 @@
|
||||
static int nfs_opendir(struct inode *, struct file *);
|
||||
static int nfs_closedir(struct inode *, struct file *);
|
||||
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 loff_t nfs_llseek_dir(struct file *, loff_t, int);
|
||||
static void nfs_readdir_clear_array(struct page*);
|
||||
@ -69,73 +59,10 @@ const struct file_operations nfs_dir_operations = {
|
||||
.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 = {
|
||||
.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)
|
||||
{
|
||||
struct nfs_open_dir_context *ctx;
|
||||
@ -1128,7 +1055,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
goto out_bad;
|
||||
}
|
||||
|
||||
if (nfs_have_delegation(inode, FMODE_READ))
|
||||
if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ))
|
||||
goto out_set_verifier;
|
||||
|
||||
/* 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,
|
||||
};
|
||||
|
||||
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 *parent;
|
||||
@ -1398,9 +1325,9 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
|
||||
struct file *file, unsigned open_flags,
|
||||
umode_t mode, int *opened)
|
||||
int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
|
||||
struct file *file, unsigned open_flags,
|
||||
umode_t mode, int *opened)
|
||||
{
|
||||
struct nfs_open_context *ctx;
|
||||
struct dentry *res;
|
||||
@ -1588,7 +1515,7 @@ out_error:
|
||||
* that the operation succeeded on the server, but an error in the
|
||||
* 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)
|
||||
{
|
||||
struct iattr attr;
|
||||
@ -1613,7 +1540,7 @@ out_err:
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct iattr attr;
|
||||
@ -1640,7 +1567,7 @@ out_err:
|
||||
/*
|
||||
* 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;
|
||||
int error;
|
||||
@ -1666,7 +1593,7 @@ static void nfs_dentry_handle_enoent(struct dentry *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;
|
||||
|
||||
@ -1706,7 +1633,7 @@ static int nfs_safe_remove(struct dentry *dentry)
|
||||
}
|
||||
|
||||
if (inode != NULL) {
|
||||
nfs_inode_return_delegation(inode);
|
||||
NFS_PROTO(inode)->return_delegation(inode);
|
||||
error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
|
||||
/* The VFS may want to delete this inode */
|
||||
if (error == 0)
|
||||
@ -1725,7 +1652,7 @@ out:
|
||||
*
|
||||
* 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 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
|
||||
* 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 page *page;
|
||||
@ -1824,7 +1751,7 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
|
||||
{
|
||||
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,
|
||||
dentry->d_parent->d_name.name, dentry->d_name.name);
|
||||
|
||||
nfs_inode_return_delegation(inode);
|
||||
NFS_PROTO(inode)->return_delegation(inode);
|
||||
|
||||
d_drop(dentry);
|
||||
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
|
||||
* 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 *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)
|
||||
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,
|
||||
new_dir, &new_dentry->d_name);
|
||||
|
@ -393,7 +393,7 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq,
|
||||
size_t requested_bytes = 0;
|
||||
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);
|
||||
get_dreq(dreq);
|
||||
desc.pg_dreq = dreq;
|
||||
@ -478,7 +478,7 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
|
||||
dreq->count = 0;
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
desc.pg_dreq = dreq;
|
||||
get_dreq(dreq);
|
||||
|
179
fs/nfs/file.c
179
fs/nfs/file.c
@ -35,36 +35,17 @@
|
||||
#include "internal.h"
|
||||
#include "iostat.h"
|
||||
#include "fscache.h"
|
||||
#include "pnfs.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_FILE
|
||||
|
||||
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 */
|
||||
#ifndef IS_SWAPFILE
|
||||
# define IS_SWAPFILE(inode) (0)
|
||||
#endif
|
||||
|
||||
static int nfs_check_flags(int flags)
|
||||
int nfs_check_flags(int flags)
|
||||
{
|
||||
if ((flags & (O_APPEND | O_DIRECT)) == (O_APPEND | O_DIRECT))
|
||||
return -EINVAL;
|
||||
@ -93,7 +74,7 @@ nfs_file_open(struct inode *inode, struct file *filp)
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
nfs_file_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
dprintk("NFS: release(%s/%s)\n",
|
||||
@ -135,7 +116,7 @@ force_reval:
|
||||
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",
|
||||
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.
|
||||
*/
|
||||
static int
|
||||
int
|
||||
nfs_file_flush(struct file *file, fl_owner_t id)
|
||||
{
|
||||
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
|
||||
* 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);
|
||||
|
||||
/* Flush writes to the server and return any errors */
|
||||
return vfs_fsync(file, 0);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ssize_t
|
||||
nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
|
||||
unsigned long nr_segs, loff_t pos)
|
||||
{
|
||||
@ -209,7 +190,7 @@ nfs_file_read(struct kiocb *iocb, const struct iovec *iov,
|
||||
return result;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ssize_t
|
||||
nfs_file_splice_read(struct file *filp, loff_t *ppos,
|
||||
struct pipe_inode_info *pipe, size_t count,
|
||||
unsigned int flags)
|
||||
@ -231,7 +212,7 @@ nfs_file_splice_read(struct file *filp, loff_t *ppos,
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
|
||||
{
|
||||
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
|
||||
* fall back to doing a synchronous write.
|
||||
*/
|
||||
static int
|
||||
nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
int
|
||||
nfs_file_fsync_commit(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
{
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
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,
|
||||
datasync);
|
||||
|
||||
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
|
||||
have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
|
||||
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);
|
||||
if (!ret && status < 0)
|
||||
ret = status;
|
||||
if (!ret && !datasync)
|
||||
/* application has asked for meta-data sync */
|
||||
ret = pnfs_layoutcommit_inode(inode, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -572,8 +560,8 @@ static int nfs_need_sync_write(struct file *filp, struct inode *inode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
|
||||
unsigned long nr_segs, loff_t pos)
|
||||
ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov,
|
||||
unsigned long nr_segs, loff_t pos)
|
||||
{
|
||||
struct dentry * dentry = iocb->ki_filp->f_path.dentry;
|
||||
struct inode * inode = dentry->d_inode;
|
||||
@ -624,9 +612,9 @@ out_swapfile:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
|
||||
struct file *filp, loff_t *ppos,
|
||||
size_t count, unsigned int flags)
|
||||
ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe,
|
||||
struct file *filp, loff_t *ppos,
|
||||
size_t count, unsigned int flags)
|
||||
{
|
||||
struct dentry *dentry = filp->f_path.dentry;
|
||||
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;
|
||||
|
||||
if (nfs_have_delegation(inode, FMODE_READ))
|
||||
if (NFS_PROTO(inode)->have_delegation(inode, FMODE_READ))
|
||||
goto out_noconflict;
|
||||
|
||||
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.
|
||||
*/
|
||||
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))
|
||||
__nfs_revalidate_inode(NFS_SERVER(inode), inode);
|
||||
else
|
||||
@ -778,7 +766,7 @@ out:
|
||||
/*
|
||||
* 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;
|
||||
int ret = -ENOLCK;
|
||||
@ -818,7 +806,7 @@ out_err:
|
||||
/*
|
||||
* 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;
|
||||
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
|
||||
* 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",
|
||||
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,
|
||||
.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 */
|
||||
|
@ -23,21 +23,15 @@
|
||||
#include <linux/sunrpc/stats.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
#include <linux/nfs_mount.h>
|
||||
#include <linux/nfs4_mount.h>
|
||||
#include <linux/lockd/bind.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/nfs_idmap.h>
|
||||
#include <linux/vfs.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "nfs4_fs.h"
|
||||
#include "delegation.h"
|
||||
#include "internal.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_CLIENT
|
||||
|
||||
/*
|
||||
@ -135,47 +129,3 @@ out:
|
||||
nfs_free_fattr(fsinfo.fattr);
|
||||
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 */
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include <linux/lockd/bind.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/nfs_idmap.h>
|
||||
#include <linux/vfs.h>
|
||||
#include <linux/inet.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
|
||||
*/
|
||||
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);
|
||||
if (error == 0)
|
||||
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)
|
||||
|| S_ISLNK(inode->i_mode)))
|
||||
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))
|
||||
nfsi->cache_validity |= invalid;
|
||||
|
||||
@ -1628,87 +1627,96 @@ static int __init init_nfs_fs(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = nfs_idmap_init();
|
||||
if (err < 0)
|
||||
goto out10;
|
||||
|
||||
err = nfs_dns_resolver_init();
|
||||
if (err < 0)
|
||||
goto out9;
|
||||
goto out11;
|
||||
|
||||
err = register_pernet_subsys(&nfs_net_ops);
|
||||
if (err < 0)
|
||||
goto out8;
|
||||
goto out10;
|
||||
|
||||
err = nfs_fscache_register();
|
||||
if (err < 0)
|
||||
goto out7;
|
||||
goto out9;
|
||||
|
||||
err = nfsiod_start();
|
||||
if (err)
|
||||
goto out6;
|
||||
goto out8;
|
||||
|
||||
err = nfs_fs_proc_init();
|
||||
if (err)
|
||||
goto out5;
|
||||
goto out7;
|
||||
|
||||
err = nfs_init_nfspagecache();
|
||||
if (err)
|
||||
goto out4;
|
||||
goto out6;
|
||||
|
||||
err = nfs_init_inodecache();
|
||||
if (err)
|
||||
goto out3;
|
||||
goto out5;
|
||||
|
||||
err = nfs_init_readpagecache();
|
||||
if (err)
|
||||
goto out2;
|
||||
goto out4;
|
||||
|
||||
err = nfs_init_writepagecache();
|
||||
if (err)
|
||||
goto out1;
|
||||
goto out3;
|
||||
|
||||
err = nfs_init_directcache();
|
||||
if (err)
|
||||
goto out0;
|
||||
goto out2;
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
rpc_proc_register(&init_net, &nfs_rpcstat);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NFS_V4
|
||||
err = init_nfs_v4();
|
||||
if (err)
|
||||
goto out1;
|
||||
#endif
|
||||
|
||||
if ((err = register_nfs_fs()) != 0)
|
||||
goto out;
|
||||
goto out0;
|
||||
|
||||
return 0;
|
||||
out:
|
||||
out0:
|
||||
#ifdef CONFIG_NFS_V4
|
||||
exit_nfs_v4();
|
||||
out1:
|
||||
#endif
|
||||
#ifdef CONFIG_PROC_FS
|
||||
rpc_proc_unregister(&init_net, "nfs");
|
||||
#endif
|
||||
nfs_destroy_directcache();
|
||||
out0:
|
||||
nfs_destroy_writepagecache();
|
||||
out1:
|
||||
nfs_destroy_readpagecache();
|
||||
out2:
|
||||
nfs_destroy_inodecache();
|
||||
nfs_destroy_writepagecache();
|
||||
out3:
|
||||
nfs_destroy_nfspagecache();
|
||||
nfs_destroy_readpagecache();
|
||||
out4:
|
||||
nfs_fs_proc_exit();
|
||||
nfs_destroy_inodecache();
|
||||
out5:
|
||||
nfsiod_stop();
|
||||
nfs_destroy_nfspagecache();
|
||||
out6:
|
||||
nfs_fscache_unregister();
|
||||
nfs_fs_proc_exit();
|
||||
out7:
|
||||
unregister_pernet_subsys(&nfs_net_ops);
|
||||
nfsiod_stop();
|
||||
out8:
|
||||
nfs_dns_resolver_destroy();
|
||||
nfs_fscache_unregister();
|
||||
out9:
|
||||
nfs_idmap_quit();
|
||||
unregister_pernet_subsys(&nfs_net_ops);
|
||||
out10:
|
||||
nfs_dns_resolver_destroy();
|
||||
out11:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit exit_nfs_fs(void)
|
||||
{
|
||||
#ifdef CONFIG_NFS_V4
|
||||
exit_nfs_v4();
|
||||
#endif
|
||||
nfs_destroy_directcache();
|
||||
nfs_destroy_writepagecache();
|
||||
nfs_destroy_readpagecache();
|
||||
@ -1717,7 +1725,6 @@ static void __exit exit_nfs_fs(void)
|
||||
nfs_fscache_unregister();
|
||||
unregister_pernet_subsys(&nfs_net_ops);
|
||||
nfs_dns_resolver_destroy();
|
||||
nfs_idmap_quit();
|
||||
#ifdef CONFIG_PROC_FS
|
||||
rpc_proc_unregister(&init_net, "nfs");
|
||||
#endif
|
||||
|
@ -85,6 +85,17 @@ struct nfs_clone_mount {
|
||||
*/
|
||||
#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
|
||||
*/
|
||||
@ -142,15 +153,36 @@ struct nfs_mount_request {
|
||||
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 void nfs_umount(const struct nfs_mount_request *info);
|
||||
|
||||
/* client.c */
|
||||
extern const struct rpc_program nfs_program;
|
||||
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_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_sessionid(struct net *, const struct sockaddr *,
|
||||
@ -188,6 +220,10 @@ static inline void nfs_fs_proc_exit(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NFS_V4_1
|
||||
int nfs_sockaddr_match_ipaddr(const struct sockaddr *, const struct sockaddr *);
|
||||
#endif
|
||||
|
||||
/* callback_xdr.c */
|
||||
extern struct svc_version nfs4_callback_version1;
|
||||
extern struct svc_version nfs4_callback_version4;
|
||||
@ -245,6 +281,32 @@ extern struct nfs_client *nfs_init_client(struct nfs_client *clp,
|
||||
/* dir.c */
|
||||
extern int nfs_access_cache_shrinker(struct shrinker *shrink,
|
||||
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 */
|
||||
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_referral_fs_type;
|
||||
#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;
|
||||
|
||||
@ -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 int nfs_generic_pagein(struct nfs_pageio_descriptor *desc,
|
||||
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,
|
||||
const struct nfs_pgio_completion_ops *compl_ops);
|
||||
extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio);
|
||||
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 */
|
||||
extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
|
||||
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 int nfs_generic_flush(struct nfs_pageio_descriptor *desc,
|
||||
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,
|
||||
const struct nfs_pgio_completion_ops *compl_ops);
|
||||
extern void nfs_pageio_reset_write_mds(struct nfs_pageio_descriptor *pgio);
|
||||
|
@ -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)
|
||||
{
|
||||
u32 recvd, count;
|
||||
size_t hdrlen;
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(p == NULL))
|
||||
goto out_overflow;
|
||||
count = be32_to_cpup(p);
|
||||
hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
|
||||
recvd = xdr->buf->len - hdrlen;
|
||||
recvd = xdr_read_pages(xdr, count);
|
||||
if (unlikely(count > recvd))
|
||||
goto out_cheating;
|
||||
out:
|
||||
xdr_read_pages(xdr, count);
|
||||
result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */
|
||||
result->count = 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)
|
||||
{
|
||||
u32 length, recvd;
|
||||
size_t hdrlen;
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
@ -449,12 +445,9 @@ static int decode_path(struct xdr_stream *xdr)
|
||||
length = be32_to_cpup(p);
|
||||
if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN))
|
||||
goto out_size;
|
||||
hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
|
||||
recvd = xdr->buf->len - hdrlen;
|
||||
recvd = xdr_read_pages(xdr, length);
|
||||
if (unlikely(length > recvd))
|
||||
goto out_cheating;
|
||||
|
||||
xdr_read_pages(xdr, length);
|
||||
xdr_terminate_string(xdr->buf, length);
|
||||
return 0;
|
||||
out_size:
|
||||
@ -972,22 +965,7 @@ out_overflow:
|
||||
*/
|
||||
static int decode_readdirok(struct xdr_stream *xdr)
|
||||
{
|
||||
u32 recvd, pglen;
|
||||
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;
|
||||
return xdr_read_pages(xdr, xdr->buf->page_len);
|
||||
}
|
||||
|
||||
static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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 = {
|
||||
.version = 3, /* protocol version */
|
||||
.dentry_ops = &nfs_dentry_operations,
|
||||
@ -910,9 +950,11 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
|
||||
.pathconf = nfs3_proc_pathconf,
|
||||
.decode_dirent = nfs3_decode_dirent,
|
||||
.read_setup = nfs3_proc_read_setup,
|
||||
.read_pageio_init = nfs_pageio_init_read,
|
||||
.read_rpc_prepare = nfs3_proc_read_rpc_prepare,
|
||||
.read_done = nfs3_read_done,
|
||||
.write_setup = nfs3_proc_write_setup,
|
||||
.write_pageio_init = nfs_pageio_init_write,
|
||||
.write_rpc_prepare = nfs3_proc_write_rpc_prepare,
|
||||
.write_done = nfs3_write_done,
|
||||
.commit_setup = nfs3_proc_commit_setup,
|
||||
@ -921,5 +963,9 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
|
||||
.lock = nfs3_proc_lock,
|
||||
.clear_acl_cache = nfs3_forget_cached_acls,
|
||||
.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,
|
||||
.free_client = nfs_free_client,
|
||||
};
|
||||
|
@ -246,7 +246,6 @@ static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
|
||||
static int decode_nfspath3(struct xdr_stream *xdr)
|
||||
{
|
||||
u32 recvd, count;
|
||||
size_t hdrlen;
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
@ -255,12 +254,9 @@ static int decode_nfspath3(struct xdr_stream *xdr)
|
||||
count = be32_to_cpup(p);
|
||||
if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN))
|
||||
goto out_nametoolong;
|
||||
hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
|
||||
recvd = xdr->buf->len - hdrlen;
|
||||
recvd = xdr_read_pages(xdr, count);
|
||||
if (unlikely(count > recvd))
|
||||
goto out_cheating;
|
||||
|
||||
xdr_read_pages(xdr, count);
|
||||
xdr_terminate_string(xdr->buf, count);
|
||||
return 0;
|
||||
|
||||
@ -329,14 +325,14 @@ static void encode_createverf3(struct xdr_stream *xdr, const __be32 *verifier)
|
||||
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;
|
||||
|
||||
p = xdr_inline_decode(xdr, NFS3_WRITEVERFSIZE);
|
||||
if (unlikely(p == NULL))
|
||||
goto out_overflow;
|
||||
memcpy(verifier, p, NFS3_WRITEVERFSIZE);
|
||||
memcpy(verifier->data, p, NFS3_WRITEVERFSIZE);
|
||||
return 0;
|
||||
out_overflow:
|
||||
print_overflow_msg(__func__, xdr);
|
||||
@ -1587,7 +1583,6 @@ static int decode_read3resok(struct xdr_stream *xdr,
|
||||
struct nfs_readres *result)
|
||||
{
|
||||
u32 eof, count, ocount, recvd;
|
||||
size_t hdrlen;
|
||||
__be32 *p;
|
||||
|
||||
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++);
|
||||
if (unlikely(ocount != count))
|
||||
goto out_mismatch;
|
||||
hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
|
||||
recvd = xdr->buf->len - hdrlen;
|
||||
recvd = xdr_read_pages(xdr, count);
|
||||
if (unlikely(count > recvd))
|
||||
goto out_cheating;
|
||||
|
||||
out:
|
||||
xdr_read_pages(xdr, count);
|
||||
result->eof = eof;
|
||||
result->count = count;
|
||||
return count;
|
||||
@ -1676,20 +1668,22 @@ static int decode_write3resok(struct xdr_stream *xdr,
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4 + 4 + NFS3_WRITEVERFSIZE);
|
||||
p = xdr_inline_decode(xdr, 4 + 4);
|
||||
if (unlikely(p == NULL))
|
||||
goto out_overflow;
|
||||
result->count = be32_to_cpup(p++);
|
||||
result->verf->committed = be32_to_cpup(p++);
|
||||
if (unlikely(result->verf->committed > NFS_FILE_SYNC))
|
||||
goto out_badvalue;
|
||||
memcpy(result->verf->verifier, p, NFS3_WRITEVERFSIZE);
|
||||
if (decode_writeverf3(xdr, &result->verf->verifier))
|
||||
goto out_eio;
|
||||
return result->count;
|
||||
out_badvalue:
|
||||
dprintk("NFS: bad stable_how value: %u\n", result->verf->committed);
|
||||
return -EIO;
|
||||
out_overflow:
|
||||
print_overflow_msg(__func__, xdr);
|
||||
out_eio:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@ -2039,22 +2033,7 @@ out_truncated:
|
||||
*/
|
||||
static int decode_dirlist3(struct xdr_stream *xdr)
|
||||
{
|
||||
u32 recvd, pglen;
|
||||
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;
|
||||
return xdr_read_pages(xdr, xdr->buf->page_len);
|
||||
}
|
||||
|
||||
static int decode_readdir3resok(struct xdr_stream *xdr,
|
||||
@ -2337,7 +2316,7 @@ static int nfs3_xdr_dec_commit3res(struct rpc_rqst *req,
|
||||
goto out;
|
||||
if (status != NFS3_OK)
|
||||
goto out_status;
|
||||
error = decode_writeverf3(xdr, result->verf->verifier);
|
||||
error = decode_writeverf3(xdr, &result->verf->verifier);
|
||||
out:
|
||||
return error;
|
||||
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))
|
||||
goto out;
|
||||
|
||||
hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base;
|
||||
hdrlen = xdr_stream_pos(xdr);
|
||||
|
||||
acl = NULL;
|
||||
if (result->mask & NFS_ACL)
|
||||
|
@ -200,7 +200,13 @@ struct nfs4_state_maintenance_ops {
|
||||
};
|
||||
|
||||
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 */
|
||||
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_fs_locations_bitmap[2];
|
||||
|
||||
void nfs4_free_client(struct nfs_client *);
|
||||
|
||||
struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *);
|
||||
|
||||
/* nfs4renewd.c */
|
||||
extern void nfs4_schedule_state_renewal(struct nfs_client *);
|
||||
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;
|
||||
|
||||
/* 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 */
|
||||
extern struct rpc_procinfo nfs4_procedures[];
|
||||
|
||||
|
663
fs/nfs/nfs4client.c
Normal file
663
fs/nfs/nfs4client.c
Normal 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
126
fs/nfs/nfs4file.c
Normal 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,
|
||||
};
|
@ -205,9 +205,9 @@ static int filelayout_async_handle_error(struct rpc_task *task,
|
||||
case -EPIPE:
|
||||
dprintk("%s DS connection error %d\n", __func__,
|
||||
task->tk_status);
|
||||
if (!filelayout_test_devid_invalid(devid))
|
||||
_pnfs_return_layout(inode);
|
||||
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);
|
||||
nfs4_ds_disconnect(clp);
|
||||
/* 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);
|
||||
|
||||
data->task.tk_status = 0;
|
||||
memcpy(data->verf.verifier, first->wb_verf.verifier,
|
||||
sizeof(first->wb_verf.verifier));
|
||||
data->verf.verifier[0]++; /* ensure verifier mismatch */
|
||||
memcpy(&data->verf.verifier, &first->wb_verf,
|
||||
sizeof(data->verf.verifier));
|
||||
data->verf.verifier.data[0]++; /* ensure verifier mismatch */
|
||||
}
|
||||
|
||||
static int filelayout_commit_done_cb(struct rpc_task *task,
|
||||
|
@ -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->pages = pages;
|
||||
pdev->pgbase = 0;
|
||||
pdev->pglen = PAGE_SIZE * max_pages;
|
||||
pdev->pglen = max_resp_sz;
|
||||
pdev->mincount = 0;
|
||||
|
||||
rc = nfs4_proc_getdeviceinfo(server, pdev);
|
||||
|
49
fs/nfs/nfs4getroot.c
Normal file
49
fs/nfs/nfs4getroot.c
Normal 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;
|
||||
}
|
@ -43,7 +43,6 @@
|
||||
#include <linux/printk.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/gss_api.h>
|
||||
#include <linux/nfs.h>
|
||||
#include <linux/nfs4.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,
|
||||
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)
|
||||
@ -294,8 +298,8 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
|
||||
case 0:
|
||||
return 0;
|
||||
case -NFS4ERR_OPENMODE:
|
||||
if (inode && nfs_have_delegation(inode, FMODE_READ)) {
|
||||
nfs_inode_return_delegation(inode);
|
||||
if (inode && nfs4_have_delegation(inode, FMODE_READ)) {
|
||||
nfs4_inode_return_delegation(inode);
|
||||
exception->retry = 1;
|
||||
return 0;
|
||||
}
|
||||
@ -1065,7 +1069,7 @@ static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmo
|
||||
return;
|
||||
}
|
||||
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)
|
||||
@ -1756,33 +1760,70 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
|
||||
}
|
||||
|
||||
#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);
|
||||
nfs4_stateid *stateid = &state->stateid;
|
||||
int status;
|
||||
|
||||
if (state->flags & flags) {
|
||||
status = nfs41_test_stateid(server, stateid);
|
||||
if (status != NFS_OK) {
|
||||
/* If a state reset has been done, test_stateid is unneeded */
|
||||
if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
|
||||
{
|
||||
int deleg_status, open_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);
|
||||
int status;
|
||||
|
||||
deleg_status = nfs41_check_expired_stateid(state, &state->stateid, deleg_flags);
|
||||
open_status = nfs41_check_expired_stateid(state, &state->open_stateid, open_flags);
|
||||
|
||||
if ((deleg_status == NFS_OK) && (open_status == NFS_OK))
|
||||
return NFS_OK;
|
||||
return nfs4_open_expired(sp, state);
|
||||
nfs41_clear_delegation_stateid(state);
|
||||
status = nfs41_check_open_stateid(state);
|
||||
if (status != NFS_OK)
|
||||
status = nfs4_open_expired(sp, state);
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -2375,11 +2416,15 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
|
||||
int i, len, status = 0;
|
||||
rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS];
|
||||
|
||||
len = gss_mech_list_pseudoflavors(&flav_array[0]);
|
||||
flav_array[len] = RPC_AUTH_NULL;
|
||||
len += 1;
|
||||
len = rpcauth_list_flavors(flav_array, ARRAY_SIZE(flav_array));
|
||||
BUG_ON(len < 0);
|
||||
|
||||
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]);
|
||||
if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
|
||||
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
|
||||
* the operation -- in this case because we want to make sure
|
||||
* we get the post-operation mtime and size. This means that
|
||||
* we can't use xdr_encode_pages() as written: we need a variant
|
||||
* of it which would leave room in the 'tail' iovec.
|
||||
* we get the post-operation mtime and size.
|
||||
*
|
||||
* Both of these changes to the XDR layer would in fact be quite
|
||||
* 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);
|
||||
|
||||
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);
|
||||
if (IS_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)
|
||||
{
|
||||
int error;
|
||||
|
||||
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,
|
||||
@ -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
|
||||
* 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)
|
||||
@ -3732,7 +3783,8 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
|
||||
.rpc_argp = &args,
|
||||
.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;
|
||||
/* 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);
|
||||
if (i < 0)
|
||||
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);
|
||||
|
||||
/*
|
||||
@ -3961,6 +4013,16 @@ static void nfs4_init_boot_verifier(const struct nfs_client *clp,
|
||||
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,
|
||||
unsigned short port, struct rpc_cred *cred,
|
||||
struct nfs4_setclientid_res *res)
|
||||
@ -3977,44 +4039,44 @@ int nfs4_proc_setclientid(struct nfs_client *clp, u32 program,
|
||||
.rpc_resp = res,
|
||||
.rpc_cred = cred,
|
||||
};
|
||||
int loop = 0;
|
||||
int status;
|
||||
|
||||
/* nfs_client_id4 */
|
||||
nfs4_init_boot_verifier(clp, &sc_verifier);
|
||||
|
||||
for(;;) {
|
||||
rcu_read_lock();
|
||||
setclientid.sc_name_len = scnprintf(setclientid.sc_name,
|
||||
sizeof(setclientid.sc_name), "%s/%s %s %s %u",
|
||||
clp->cl_ipaddr,
|
||||
rpc_peeraddr2str(clp->cl_rpcclient,
|
||||
RPC_DISPLAY_ADDR),
|
||||
rpc_peeraddr2str(clp->cl_rpcclient,
|
||||
RPC_DISPLAY_PROTO),
|
||||
clp->cl_rpcclient->cl_auth->au_ops->au_name,
|
||||
clp->cl_id_uniquifier);
|
||||
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
|
||||
rcu_read_lock();
|
||||
setclientid.sc_name_len = scnprintf(setclientid.sc_name,
|
||||
sizeof(setclientid.sc_name), "%s/%s %s",
|
||||
clp->cl_ipaddr,
|
||||
rpc_peeraddr2str(clp->cl_rpcclient,
|
||||
RPC_DISPLAY_ADDR),
|
||||
rpc_peeraddr2str(clp->cl_rpcclient,
|
||||
RPC_DISPLAY_PROTO));
|
||||
/* cb_client4 */
|
||||
setclientid.sc_netid_len = scnprintf(setclientid.sc_netid,
|
||||
sizeof(setclientid.sc_netid),
|
||||
rpc_peeraddr2str(clp->cl_rpcclient,
|
||||
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",
|
||||
clp->cl_ipaddr, port >> 8, port & 255);
|
||||
rcu_read_unlock();
|
||||
|
||||
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
||||
if (status != -NFS4ERR_CLID_INUSE)
|
||||
break;
|
||||
if (loop != 0) {
|
||||
++clp->cl_id_uniquifier;
|
||||
break;
|
||||
}
|
||||
++loop;
|
||||
ssleep(clp->cl_lease_time / HZ + 1);
|
||||
}
|
||||
dprintk("NFS call setclientid auth=%s, '%.*s'\n",
|
||||
clp->cl_rpcclient->cl_auth->au_ops->au_name,
|
||||
setclientid.sc_name_len, setclientid.sc_name);
|
||||
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
||||
dprintk("NFS reply setclientid: %d\n", 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,
|
||||
struct nfs4_setclientid_res *arg,
|
||||
struct rpc_cred *cred)
|
||||
@ -4029,6 +4091,9 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
|
||||
unsigned long now;
|
||||
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;
|
||||
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
|
||||
if (status == 0) {
|
||||
@ -4037,6 +4102,7 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
|
||||
clp->cl_last_renewal = now;
|
||||
spin_unlock(&clp->cl_lock);
|
||||
}
|
||||
dprintk("NFS reply setclientid_confirm: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -4681,9 +4747,17 @@ out:
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
int status, ret = NFS_OK;
|
||||
int status, ret = -NFS4ERR_BAD_STATEID;
|
||||
struct nfs4_lock_state *lsp;
|
||||
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) {
|
||||
status = nfs41_test_stateid(server, &lsp->ls_stateid);
|
||||
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;
|
||||
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))
|
||||
status = nfs41_check_expired_locks(state);
|
||||
if (status == NFS_OK)
|
||||
return status;
|
||||
return nfs4_lock_expired(state, request);
|
||||
if (status != NFS_OK)
|
||||
status = nfs4_lock_expired(state, request);
|
||||
return status;
|
||||
}
|
||||
#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,
|
||||
* 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:
|
||||
if (!(filp->f_mode & FMODE_READ))
|
||||
return -EBADF;
|
||||
@ -5168,6 +5246,8 @@ out:
|
||||
/*
|
||||
* nfs4_proc_exchange_id()
|
||||
*
|
||||
* Returns zero, a negative errno, or a negative NFS4ERR status code.
|
||||
*
|
||||
* Since the clientid has expired, all compounds using sessions
|
||||
* associated with the stale clientid will be returning
|
||||
* 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,
|
||||
};
|
||||
|
||||
dprintk("--> %s\n", __func__);
|
||||
BUG_ON(clp == NULL);
|
||||
|
||||
nfs4_init_boot_verifier(clp, &verifier);
|
||||
|
||||
args.id_len = scnprintf(args.id, sizeof(args.id),
|
||||
"%s/%s/%u",
|
||||
"%s/%s",
|
||||
clp->cl_ipaddr,
|
||||
clp->cl_rpcclient->cl_nodename,
|
||||
clp->cl_rpcclient->cl_auth->au_flavor);
|
||||
clp->cl_rpcclient->cl_nodename);
|
||||
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),
|
||||
GFP_NOFS);
|
||||
@ -5264,12 +5342,12 @@ out_server_scope:
|
||||
kfree(res.server_scope);
|
||||
out:
|
||||
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",
|
||||
__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.nseconds);
|
||||
dprintk("<-- %s status= %d\n", __func__, status);
|
||||
dprintk("NFS reply exchange_id: %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -6570,22 +6648,36 @@ static int _nfs41_test_stateid(struct nfs_server *server, nfs4_stateid *stateid)
|
||||
.rpc_resp = &res,
|
||||
};
|
||||
|
||||
dprintk("NFS call test_stateid %p\n", stateid);
|
||||
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);
|
||||
|
||||
if (status == NFS_OK)
|
||||
return res.status;
|
||||
return status;
|
||||
if (status != NFS_OK) {
|
||||
dprintk("NFS reply test_stateid: failed, %d\n", 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)
|
||||
{
|
||||
struct nfs4_exception exception = { };
|
||||
int err;
|
||||
do {
|
||||
err = nfs4_handle_exception(server,
|
||||
_nfs41_test_stateid(server, stateid),
|
||||
&exception);
|
||||
err = _nfs41_test_stateid(server, stateid);
|
||||
if (err != -NFS4ERR_DELAY)
|
||||
break;
|
||||
nfs4_handle_exception(server, err, &exception);
|
||||
} while (exception.retry);
|
||||
return err;
|
||||
}
|
||||
@ -6601,19 +6693,34 @@ static int _nfs4_free_stateid(struct nfs_server *server, nfs4_stateid *stateid)
|
||||
.rpc_argp = &args,
|
||||
.rpc_resp = &res,
|
||||
};
|
||||
int status;
|
||||
|
||||
dprintk("NFS call free_stateid %p\n", stateid);
|
||||
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)
|
||||
{
|
||||
struct nfs4_exception exception = { };
|
||||
int err;
|
||||
do {
|
||||
err = nfs4_handle_exception(server,
|
||||
_nfs4_free_stateid(server, stateid),
|
||||
&exception);
|
||||
err = _nfs4_free_stateid(server, stateid);
|
||||
if (err != -NFS4ERR_DELAY)
|
||||
break;
|
||||
nfs4_handle_exception(server, err, &exception);
|
||||
} while (exception.retry);
|
||||
return err;
|
||||
}
|
||||
@ -6725,6 +6832,26 @@ const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
|
||||
#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 = {
|
||||
.permission = nfs_permission,
|
||||
.getattr = nfs_getattr,
|
||||
@ -6769,9 +6896,11 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
|
||||
.set_capabilities = nfs4_server_capabilities,
|
||||
.decode_dirent = nfs4_decode_dirent,
|
||||
.read_setup = nfs4_proc_read_setup,
|
||||
.read_pageio_init = pnfs_pageio_init_read,
|
||||
.read_rpc_prepare = nfs4_proc_read_rpc_prepare,
|
||||
.read_done = nfs4_read_done,
|
||||
.write_setup = nfs4_proc_write_setup,
|
||||
.write_pageio_init = pnfs_pageio_init_write,
|
||||
.write_rpc_prepare = nfs4_proc_write_rpc_prepare,
|
||||
.write_done = nfs4_write_done,
|
||||
.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,
|
||||
.close_context = nfs4_close_context,
|
||||
.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,
|
||||
.free_client = nfs4_free_client,
|
||||
};
|
||||
|
||||
static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
|
||||
|
@ -1606,10 +1606,15 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
|
||||
return -ESERVERFAULT;
|
||||
/* Lease confirmation error: retry after purging the lease */
|
||||
ssleep(1);
|
||||
case -NFS4ERR_CLID_INUSE:
|
||||
case -NFS4ERR_STALE_CLIENTID:
|
||||
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
|
||||
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:
|
||||
if (clp->cl_machine_cred == NULL)
|
||||
return -EACCES;
|
||||
@ -1642,7 +1647,7 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfs4_reclaim_lease(struct nfs_client *clp)
|
||||
static int nfs4_establish_lease(struct nfs_client *clp)
|
||||
{
|
||||
struct rpc_cred *cred;
|
||||
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);
|
||||
put_rpccred(cred);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1764,6 +1803,8 @@ static int nfs4_reset_session(struct nfs_client *clp)
|
||||
struct rpc_cred *cred;
|
||||
int status;
|
||||
|
||||
if (!nfs4_has_session(clp))
|
||||
return 0;
|
||||
nfs4_begin_drain_session(clp);
|
||||
cred = nfs4_get_exchange_id_cred(clp);
|
||||
status = nfs4_proc_destroy_session(clp->cl_session, cred);
|
||||
@ -1792,12 +1833,14 @@ out:
|
||||
|
||||
static int nfs4_recall_slot(struct nfs_client *clp)
|
||||
{
|
||||
struct nfs4_slot_table *fc_tbl = &clp->cl_session->fc_slot_table;
|
||||
struct nfs4_channel_attrs *fc_attrs = &clp->cl_session->fc_attrs;
|
||||
struct nfs4_slot_table *fc_tbl;
|
||||
struct nfs4_slot *new, *old;
|
||||
int i;
|
||||
|
||||
if (!nfs4_has_session(clp))
|
||||
return 0;
|
||||
nfs4_begin_drain_session(clp);
|
||||
fc_tbl = &clp->cl_session->fc_slot_table;
|
||||
new = kmalloc(fc_tbl->target_max_slots * sizeof(struct nfs4_slot),
|
||||
GFP_NOFS);
|
||||
if (!new)
|
||||
@ -1810,11 +1853,10 @@ static int nfs4_recall_slot(struct nfs_client *clp)
|
||||
fc_tbl->slots = new;
|
||||
fc_tbl->max_slots = fc_tbl->target_max_slots;
|
||||
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);
|
||||
|
||||
kfree(old);
|
||||
nfs4_end_drain_session(clp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1823,6 +1865,8 @@ static int nfs4_bind_conn_to_session(struct nfs_client *clp)
|
||||
struct rpc_cred *cred;
|
||||
int ret;
|
||||
|
||||
if (!nfs4_has_session(clp))
|
||||
return 0;
|
||||
nfs4_begin_drain_session(clp);
|
||||
cred = nfs4_get_exchange_id_cred(clp);
|
||||
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)
|
||||
{
|
||||
int status = 0;
|
||||
const char *section = "", *section_sep = "";
|
||||
|
||||
/* Ensure exclusive access to NFSv4 state */
|
||||
do {
|
||||
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)
|
||||
goto out_error;
|
||||
clear_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state);
|
||||
set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
|
||||
continue;
|
||||
}
|
||||
|
||||
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 */
|
||||
status = nfs4_reclaim_lease(clp);
|
||||
if (status < 0)
|
||||
goto out_error;
|
||||
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
|
||||
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);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) {
|
||||
section = "check lease";
|
||||
status = nfs4_check_lease(clp);
|
||||
if (status < 0)
|
||||
goto out_error;
|
||||
@ -1896,8 +1932,8 @@ static void nfs4_state_manager(struct nfs_client *clp)
|
||||
}
|
||||
|
||||
/* Initialize or reset the session */
|
||||
if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)
|
||||
&& nfs4_has_session(clp)) {
|
||||
if (test_and_clear_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) {
|
||||
section = "reset session";
|
||||
status = nfs4_reset_session(clp);
|
||||
if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
|
||||
continue;
|
||||
@ -1907,15 +1943,26 @@ static void nfs4_state_manager(struct nfs_client *clp)
|
||||
|
||||
/* Send 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);
|
||||
if (status < 0)
|
||||
goto out_error;
|
||||
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... */
|
||||
if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
|
||||
section = "reclaim reboot";
|
||||
status = nfs4_do_reclaim(clp,
|
||||
clp->cl_mvops->reboot_recovery_ops);
|
||||
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... */
|
||||
if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
|
||||
section = "reclaim nograce";
|
||||
status = nfs4_do_reclaim(clp,
|
||||
clp->cl_mvops->nograce_recovery_ops);
|
||||
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);
|
||||
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);
|
||||
/* 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);
|
||||
return;
|
||||
out_error:
|
||||
pr_warn_ratelimited("NFS: state manager failed on NFSv4 server %s"
|
||||
" with error %d\n", clp->cl_hostname, -status);
|
||||
if (strlen(section))
|
||||
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_clear_state_manager_bit(clp);
|
||||
}
|
||||
|
360
fs/nfs/nfs4super.c
Normal file
360
fs/nfs/nfs4super.c
Normal 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
68
fs/nfs/nfs4sysctl.c
Normal 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;
|
||||
}
|
@ -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)
|
||||
{
|
||||
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_WRITEW_LT : NFS4_WRITE_LT;
|
||||
}
|
||||
@ -3078,7 +3078,7 @@ out_overflow:
|
||||
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;
|
||||
|
||||
@ -3086,7 +3086,7 @@ static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen,
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
*attrlen = be32_to_cpup(p);
|
||||
*savep = xdr->p;
|
||||
*savep = xdr_stream_pos(xdr);
|
||||
return 0;
|
||||
out_overflow:
|
||||
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;
|
||||
}
|
||||
|
||||
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 nwords = xdr->p - savep;
|
||||
unsigned int nwords = (xdr_stream_pos(xdr) - savep) >> 2;
|
||||
|
||||
if (unlikely(attrwords != nwords)) {
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = decode_op_hdr(xdr, OP_COMMIT);
|
||||
if (!status)
|
||||
status = decode_verifier(xdr, res->verf->verifier);
|
||||
status = decode_write_verifier(xdr, &res->verf->verifier);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -4193,7 +4198,7 @@ out_overflow:
|
||||
|
||||
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};
|
||||
int status;
|
||||
|
||||
@ -4222,7 +4227,7 @@ xdr_error:
|
||||
|
||||
static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat)
|
||||
{
|
||||
__be32 *savep;
|
||||
unsigned int savep;
|
||||
uint32_t attrlen, bitmap[3] = {0};
|
||||
int status;
|
||||
|
||||
@ -4254,7 +4259,7 @@ xdr_error:
|
||||
|
||||
static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf)
|
||||
{
|
||||
__be32 *savep;
|
||||
unsigned int savep;
|
||||
uint32_t attrlen, bitmap[3] = {0};
|
||||
int status;
|
||||
|
||||
@ -4299,7 +4304,8 @@ out_overflow:
|
||||
static int decode_first_threshold_item4(struct xdr_stream *xdr,
|
||||
struct nfs4_threshold *res)
|
||||
{
|
||||
__be32 *p, *savep;
|
||||
__be32 *p;
|
||||
unsigned int savep;
|
||||
uint32_t bitmap[3] = {0,}, attrlen;
|
||||
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,
|
||||
const struct nfs_server *server)
|
||||
{
|
||||
__be32 *savep;
|
||||
unsigned int savep;
|
||||
uint32_t attrlen,
|
||||
bitmap[3] = {0};
|
||||
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)
|
||||
{
|
||||
__be32 *savep;
|
||||
unsigned int savep;
|
||||
uint32_t attrlen, bitmap[3];
|
||||
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)
|
||||
{
|
||||
struct kvec *iov = req->rq_rcv_buf.head;
|
||||
__be32 *p;
|
||||
uint32_t count, eof, recvd, hdrlen;
|
||||
uint32_t count, eof, recvd;
|
||||
int status;
|
||||
|
||||
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;
|
||||
eof = be32_to_cpup(p++);
|
||||
count = be32_to_cpup(p);
|
||||
hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base;
|
||||
recvd = req->rq_rcv_buf.len - hdrlen;
|
||||
recvd = xdr_read_pages(xdr, count);
|
||||
if (count > recvd) {
|
||||
dprintk("NFS: server cheating in read reply: "
|
||||
"count %u > recvd %u\n", count, recvd);
|
||||
count = recvd;
|
||||
eof = 0;
|
||||
}
|
||||
xdr_read_pages(xdr, count);
|
||||
res->eof = eof;
|
||||
res->count = count;
|
||||
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)
|
||||
{
|
||||
struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
|
||||
struct kvec *iov = rcvbuf->head;
|
||||
size_t hdrlen;
|
||||
u32 recvd, pglen = rcvbuf->page_len;
|
||||
int status;
|
||||
__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));
|
||||
dprintk("%s: verifier = %08x:%08x\n",
|
||||
__func__, verf[0], verf[1]);
|
||||
|
||||
hdrlen = (char *) xdr->p - (char *) iov->iov_base;
|
||||
recvd = rcvbuf->len - hdrlen;
|
||||
if (pglen > recvd)
|
||||
pglen = recvd;
|
||||
xdr_read_pages(xdr, pglen);
|
||||
|
||||
|
||||
return pglen;
|
||||
return xdr_read_pages(xdr, xdr->buf->page_len);
|
||||
}
|
||||
|
||||
static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req)
|
||||
{
|
||||
struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
|
||||
struct kvec *iov = rcvbuf->head;
|
||||
size_t hdrlen;
|
||||
u32 len, recvd;
|
||||
__be32 *p;
|
||||
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");
|
||||
return -ENAMETOOLONG;
|
||||
}
|
||||
hdrlen = (char *) xdr->p - (char *) iov->iov_base;
|
||||
recvd = req->rq_rcv_buf.len - hdrlen;
|
||||
recvd = xdr_read_pages(xdr, len);
|
||||
if (recvd < len) {
|
||||
dprintk("NFS: server cheating in readlink reply: "
|
||||
"count %u > recvd %u\n", len, recvd);
|
||||
return -EIO;
|
||||
}
|
||||
xdr_read_pages(xdr, len);
|
||||
/*
|
||||
* The XDR encode routine has set things up so that
|
||||
* 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,
|
||||
struct nfs_getaclres *res)
|
||||
{
|
||||
__be32 *savep, *bm_p;
|
||||
unsigned int savep;
|
||||
__be32 *bm_p;
|
||||
uint32_t attrlen,
|
||||
bitmap[3] = {0};
|
||||
struct kvec *iov = req->rq_rcv_buf.head;
|
||||
int status;
|
||||
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)))
|
||||
return -EIO;
|
||||
if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
|
||||
size_t hdrlen;
|
||||
|
||||
/* The bitmap (xdr len + bitmaps) and the attr xdr len words
|
||||
* 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
|
||||
* the attr length. Let userspace figure it out.... */
|
||||
hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base;
|
||||
attrlen += res->acl_data_offset;
|
||||
if (attrlen > page_len) {
|
||||
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)
|
||||
return status;
|
||||
|
||||
p = xdr_inline_decode(xdr, 16);
|
||||
p = xdr_inline_decode(xdr, 8);
|
||||
if (unlikely(!p))
|
||||
goto out_overflow;
|
||||
res->count = be32_to_cpup(p++);
|
||||
res->verf->committed = be32_to_cpup(p++);
|
||||
memcpy(res->verf->verifier, p, NFS4_VERIFIER_SIZE);
|
||||
return 0;
|
||||
return decode_write_verifier(xdr, &res->verf->verifier);
|
||||
out_overflow:
|
||||
print_overflow_msg(__func__, xdr);
|
||||
return -EIO;
|
||||
@ -5599,7 +5583,7 @@ static int decode_getdevicelist(struct xdr_stream *xdr,
|
||||
{
|
||||
__be32 *p;
|
||||
int status, i;
|
||||
struct nfs_writeverf verftemp;
|
||||
nfs4_verifier verftemp;
|
||||
|
||||
status = decode_op_hdr(xdr, OP_GETDEVICELIST);
|
||||
if (status)
|
||||
@ -5613,7 +5597,7 @@ static int decode_getdevicelist(struct xdr_stream *xdr,
|
||||
p += 2;
|
||||
|
||||
/* 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);
|
||||
|
||||
@ -5707,9 +5691,7 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
|
||||
__be32 *p;
|
||||
int status;
|
||||
u32 layout_count;
|
||||
struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
|
||||
struct kvec *iov = rcvbuf->head;
|
||||
u32 hdrlen, recvd;
|
||||
u32 recvd;
|
||||
|
||||
status = decode_op_hdr(xdr, OP_LAYOUTGET);
|
||||
if (status)
|
||||
@ -5746,8 +5728,7 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
|
||||
res->type,
|
||||
res->layoutp->len);
|
||||
|
||||
hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base;
|
||||
recvd = req->rq_rcv_buf.len - hdrlen;
|
||||
recvd = xdr_read_pages(xdr, res->layoutp->len);
|
||||
if (res->layoutp->len > recvd) {
|
||||
dprintk("NFS: server cheating in layoutget reply: "
|
||||
"layout len %u > recvd %u\n",
|
||||
@ -5755,8 +5736,6 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
xdr_read_pages(xdr, res->layoutp->len);
|
||||
|
||||
if (layout_count > 1) {
|
||||
/* We only handle a length one array at the moment. Any
|
||||
* 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 plus)
|
||||
{
|
||||
unsigned int savep;
|
||||
uint32_t bitmap[3] = {0};
|
||||
uint32_t len;
|
||||
__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)
|
||||
goto out_overflow;
|
||||
|
||||
if (decode_attr_length(xdr, &len, &p) < 0)
|
||||
if (decode_attr_length(xdr, &len, &savep) < 0)
|
||||
goto out_overflow;
|
||||
|
||||
if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh,
|
||||
|
@ -651,7 +651,14 @@ out_err_free:
|
||||
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
|
||||
_pnfs_return_layout(struct inode *ino)
|
||||
{
|
||||
@ -660,22 +667,31 @@ _pnfs_return_layout(struct inode *ino)
|
||||
LIST_HEAD(tmp_list);
|
||||
struct nfs4_layoutreturn *lrp;
|
||||
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);
|
||||
lo = nfsi->layout;
|
||||
if (!lo) {
|
||||
if (!lo || pnfs_test_layout_returned(lo)) {
|
||||
spin_unlock(&ino->i_lock);
|
||||
dprintk("%s: no layout to return\n", __func__);
|
||||
return status;
|
||||
dprintk("NFS: %s no layout to return\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
stateid = nfsi->layout->plh_stateid;
|
||||
/* Reference matched in nfs4_layoutreturn_release */
|
||||
get_layout_hdr(lo);
|
||||
empty = list_empty(&lo->plh_segs);
|
||||
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++;
|
||||
pnfs_mark_layout_returned(lo);
|
||||
spin_unlock(&ino->i_lock);
|
||||
pnfs_free_lseg_list(&tmp_list);
|
||||
|
||||
@ -686,6 +702,7 @@ _pnfs_return_layout(struct inode *ino)
|
||||
status = -ENOMEM;
|
||||
set_bit(NFS_LAYOUT_RW_FAILED, &lo->plh_flags);
|
||||
set_bit(NFS_LAYOUT_RO_FAILED, &lo->plh_flags);
|
||||
pnfs_clear_layout_returned(lo);
|
||||
put_layout_hdr(lo);
|
||||
goto out;
|
||||
}
|
||||
@ -1075,6 +1092,10 @@ pnfs_update_layout(struct inode *ino,
|
||||
get_layout_hdr(lo);
|
||||
if (list_empty(&lo->plh_segs))
|
||||
first = true;
|
||||
|
||||
/* Enable LAYOUTRETURNs */
|
||||
pnfs_clear_layout_returned(lo);
|
||||
|
||||
spin_unlock(&ino->i_lock);
|
||||
if (first) {
|
||||
/* 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);
|
||||
|
||||
bool
|
||||
void
|
||||
pnfs_pageio_init_read(struct nfs_pageio_descriptor *pgio, struct inode *inode,
|
||||
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;
|
||||
|
||||
if (ld == NULL)
|
||||
return false;
|
||||
nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops,
|
||||
server->rsize, 0);
|
||||
return true;
|
||||
nfs_pageio_init_read(pgio, inode, compl_ops);
|
||||
else
|
||||
nfs_pageio_init(pgio, inode, ld->pg_read_ops, compl_ops, server->rsize, 0);
|
||||
}
|
||||
|
||||
bool
|
||||
void
|
||||
pnfs_pageio_init_write(struct nfs_pageio_descriptor *pgio, struct inode *inode,
|
||||
int ioflags,
|
||||
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;
|
||||
|
||||
if (ld == NULL)
|
||||
return false;
|
||||
nfs_pageio_init(pgio, inode, ld->pg_write_ops, compl_ops,
|
||||
server->wsize, ioflags);
|
||||
return true;
|
||||
nfs_pageio_init_write(pgio, inode, ioflags, compl_ops);
|
||||
else
|
||||
nfs_pageio_init(pgio, inode, ld->pg_write_ops, compl_ops, server->wsize, ioflags);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -1272,7 +1291,7 @@ int pnfs_write_done_resend_to_mds(struct inode *inode,
|
||||
LIST_HEAD(failed);
|
||||
|
||||
/* 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)) {
|
||||
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);
|
||||
|
||||
/* 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)) {
|
||||
struct nfs_page *req = nfs_list_entry(head->next);
|
||||
|
||||
|
@ -64,6 +64,7 @@ enum {
|
||||
NFS_LAYOUT_ROC, /* some lseg had roc bit set */
|
||||
NFS_LAYOUT_DESTROYED, /* no new use of layout allowed */
|
||||
NFS_LAYOUT_INVALID, /* layout is being destroyed */
|
||||
NFS_LAYOUT_RETURNED, /* layout has already been returned */
|
||||
};
|
||||
|
||||
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 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 *);
|
||||
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 *);
|
||||
|
||||
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 *);
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return false;
|
||||
nfs_pageio_init_write(pgio, inode, ioflags, compl_ops);
|
||||
}
|
||||
|
||||
static inline int
|
||||
|
@ -734,6 +734,38 @@ out_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 = {
|
||||
.version = 2, /* protocol version */
|
||||
.dentry_ops = &nfs_dentry_operations,
|
||||
@ -767,9 +799,11 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
|
||||
.pathconf = nfs_proc_pathconf,
|
||||
.decode_dirent = nfs2_decode_dirent,
|
||||
.read_setup = nfs_proc_read_setup,
|
||||
.read_pageio_init = nfs_pageio_init_read,
|
||||
.read_rpc_prepare = nfs_proc_read_rpc_prepare,
|
||||
.read_done = nfs_read_done,
|
||||
.write_setup = nfs_proc_write_setup,
|
||||
.write_pageio_init = nfs_pageio_init_write,
|
||||
.write_rpc_prepare = nfs_proc_write_rpc_prepare,
|
||||
.write_done = nfs_write_done,
|
||||
.commit_setup = nfs_proc_commit_setup,
|
||||
@ -777,5 +811,9 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
|
||||
.lock = nfs_proc_lock,
|
||||
.lock_check_bounds = nfs_lock_check_bounds,
|
||||
.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,
|
||||
.free_client = nfs_free_client,
|
||||
};
|
||||
|
@ -20,8 +20,6 @@
|
||||
#include <linux/nfs_page.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "pnfs.h"
|
||||
|
||||
#include "nfs4_fs.h"
|
||||
#include "internal.h"
|
||||
#include "iostat.h"
|
||||
@ -108,7 +106,7 @@ int nfs_return_empty_page(struct page *page)
|
||||
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,
|
||||
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);
|
||||
|
||||
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,
|
||||
struct page *page)
|
||||
{
|
||||
@ -149,7 +139,7 @@ int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
|
||||
if (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_complete(&pgio);
|
||||
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)
|
||||
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);
|
||||
|
||||
|
438
fs/nfs/super.c
438
fs/nfs/super.c
@ -278,29 +278,8 @@ static match_table_t nfs_vers_tokens = {
|
||||
{ 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,
|
||||
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 = {
|
||||
.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 int nfs4_validate_mount_data(void *options,
|
||||
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
|
||||
|
||||
static struct shrinker acl_shrinker = {
|
||||
@ -424,18 +337,9 @@ int __init register_nfs_fs(void)
|
||||
ret = nfs_register_sysctl();
|
||||
if (ret < 0)
|
||||
goto error_1;
|
||||
#ifdef CONFIG_NFS_V4
|
||||
ret = register_filesystem(&nfs4_fs_type);
|
||||
if (ret < 0)
|
||||
goto error_2;
|
||||
#endif
|
||||
register_shrinker(&acl_shrinker);
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_NFS_V4
|
||||
error_2:
|
||||
nfs_unregister_sysctl();
|
||||
#endif
|
||||
error_1:
|
||||
unregister_filesystem(&nfs_fs_type);
|
||||
error_0:
|
||||
@ -448,9 +352,6 @@ error_0:
|
||||
void __exit unregister_nfs_fs(void)
|
||||
{
|
||||
unregister_shrinker(&acl_shrinker);
|
||||
#ifdef CONFIG_NFS_V4
|
||||
unregister_filesystem(&nfs4_fs_type);
|
||||
#endif
|
||||
nfs_unregister_sysctl();
|
||||
unregister_filesystem(&nfs_fs_type);
|
||||
}
|
||||
@ -474,7 +375,7 @@ void nfs_sb_deactive(struct super_block *sb)
|
||||
/*
|
||||
* 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);
|
||||
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
|
||||
*/
|
||||
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);
|
||||
|
||||
@ -815,7 +716,7 @@ static void show_implementation_id(struct seq_file *m, struct nfs_server *nfss)
|
||||
}
|
||||
#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 *devname, *dummy;
|
||||
@ -831,7 +732,7 @@ static int nfs_show_devname(struct seq_file *m, struct dentry *root)
|
||||
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, "/");
|
||||
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
|
||||
*/
|
||||
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;
|
||||
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
|
||||
* 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 rpc_clnt *rpc;
|
||||
@ -2108,7 +2009,7 @@ nfs_compare_remount_data(struct nfs_server *nfss,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
nfs_remount(struct super_block *sb, int *flags, char *raw_data)
|
||||
{
|
||||
int error;
|
||||
@ -2173,7 +2074,7 @@ out:
|
||||
/*
|
||||
* 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);
|
||||
|
||||
@ -2195,8 +2096,7 @@ static inline void nfs_initialise_sb(struct super_block *sb)
|
||||
/*
|
||||
* Finish setting up an NFS2/3 superblock
|
||||
*/
|
||||
static void nfs_fill_super(struct super_block *sb,
|
||||
struct nfs_mount_info *mount_info)
|
||||
void nfs_fill_super(struct super_block *sb, struct nfs_mount_info *mount_info)
|
||||
{
|
||||
struct nfs_parsed_mount_data *data = mount_info->parsed;
|
||||
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,
|
||||
struct nfs_mount_info *mount_info)
|
||||
void nfs_clone_super(struct super_block *sb, struct nfs_mount_info *mount_info)
|
||||
{
|
||||
const struct super_block *old_sb = mount_info->cloned->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 = old_sb->s_blocksize;
|
||||
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
|
||||
* so ourselves when necessary.
|
||||
*/
|
||||
sb->s_flags |= MS_POSIXACL;
|
||||
sb->s_time_gran = 1;
|
||||
}
|
||||
|
||||
sb->s_op = old_sb->s_op;
|
||||
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);
|
||||
}
|
||||
|
||||
static int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot,
|
||||
struct nfs_mount_info *mount_info)
|
||||
int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot,
|
||||
struct nfs_mount_info *mount_info)
|
||||
{
|
||||
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,
|
||||
struct nfs_mount_info *mount_info)
|
||||
int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot,
|
||||
struct nfs_mount_info *mount_info)
|
||||
{
|
||||
/* clone any lsm security options from the parent to the new sb */
|
||||
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;
|
||||
}
|
||||
|
||||
static struct dentry *nfs_fs_mount_common(struct file_system_type *fs_type,
|
||||
struct nfs_server *server,
|
||||
int flags, const char *dev_name,
|
||||
struct nfs_mount_info *mount_info)
|
||||
struct dentry *nfs_fs_mount_common(struct file_system_type *fs_type,
|
||||
struct nfs_server *server,
|
||||
int flags, const char *dev_name,
|
||||
struct nfs_mount_info *mount_info)
|
||||
{
|
||||
struct super_block *s;
|
||||
struct dentry *mntroot = ERR_PTR(-ENOMEM);
|
||||
@ -2470,7 +2370,7 @@ error_splat_bdi:
|
||||
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)
|
||||
{
|
||||
struct nfs_mount_info mount_info = {
|
||||
@ -2511,7 +2411,7 @@ out:
|
||||
* Ensure that we unregister the bdi before kill_anon_super
|
||||
* 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);
|
||||
|
||||
@ -2521,7 +2421,7 @@ static void nfs_put_super(struct super_block *s)
|
||||
/*
|
||||
* 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);
|
||||
|
||||
@ -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)
|
||||
*/
|
||||
static struct dentry *
|
||||
struct dentry *
|
||||
nfs_xdev_mount_common(struct file_system_type *fs_type, int flags,
|
||||
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
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3|
|
||||
@ -2715,250 +2577,4 @@ out_no_address:
|
||||
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 */
|
||||
|
@ -9,37 +9,11 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nfs4.h>
|
||||
#include <linux/nfs_idmap.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 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",
|
||||
.data = &nfs_mountpoint_expiry_timeout,
|
||||
|
@ -501,7 +501,7 @@ nfs_sillyrename(struct inode *dir, struct dentry *dentry)
|
||||
(unsigned long long)NFS_FILEID(dentry->d_inode));
|
||||
|
||||
/* 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;
|
||||
do {
|
||||
|
@ -336,8 +336,10 @@ static int nfs_writepage_locked(struct page *page, struct writeback_control *wbc
|
||||
struct nfs_pageio_descriptor pgio;
|
||||
int err;
|
||||
|
||||
nfs_pageio_init_write(&pgio, page->mapping->host, wb_priority(wbc),
|
||||
&nfs_async_write_completion_ops);
|
||||
NFS_PROTO(page->mapping->host)->write_pageio_init(&pgio,
|
||||
page->mapping->host,
|
||||
wb_priority(wbc),
|
||||
&nfs_async_write_completion_ops);
|
||||
err = nfs_do_writepage(page, wbc, &pgio);
|
||||
nfs_pageio_complete(&pgio);
|
||||
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_pageio_init_write(&pgio, inode, wb_priority(wbc),
|
||||
&nfs_async_write_completion_ops);
|
||||
NFS_PROTO(inode)->write_pageio_init(&pgio, inode, wb_priority(wbc), &nfs_async_write_completion_ops);
|
||||
err = write_cache_pages(mapping, wbc, nfs_writepages_callback, &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);
|
||||
|
||||
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++;
|
||||
set_bit(PG_MAPPED, &req->wb_flags);
|
||||
SetPagePrivate(req->wb_page);
|
||||
@ -620,7 +621,7 @@ static void nfs_write_completion(struct nfs_pgio_header *hdr)
|
||||
goto next;
|
||||
}
|
||||
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);
|
||||
goto next;
|
||||
}
|
||||
@ -1202,7 +1203,7 @@ static const struct nfs_pageio_ops nfs_pageio_write_ops = {
|
||||
.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,
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
@ -1547,7 +1541,7 @@ static void nfs_commit_release_pages(struct nfs_commit_data *data)
|
||||
|
||||
/* Okay, COMMIT succeeded, apparently. Check the verifier
|
||||
* 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 */
|
||||
nfs_inode_remove_request(req);
|
||||
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 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)) {
|
||||
int status;
|
||||
bool sync = true;
|
||||
@ -1693,6 +1692,7 @@ int nfs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* flush the inode to disk.
|
||||
|
@ -427,10 +427,6 @@ extern __be32 root_nfs_parse_addr(char *name); /*__init*/
|
||||
/*
|
||||
* 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;
|
||||
#ifdef CONFIG_NFS_V4
|
||||
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
|
||||
*/
|
||||
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 dentry_operations nfs_dentry_operations;
|
||||
|
||||
|
@ -69,10 +69,9 @@ struct nfs_client {
|
||||
struct idmap * cl_idmap;
|
||||
|
||||
/* 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];
|
||||
unsigned char cl_id_uniquifier;
|
||||
u32 cl_cb_ident; /* v4.0 callback identifier */
|
||||
const struct nfs4_minor_version_ops *cl_mvops;
|
||||
|
||||
|
@ -42,7 +42,7 @@ struct nfs_page {
|
||||
wb_bytes; /* Length of request */
|
||||
struct kref wb_kref; /* reference count */
|
||||
unsigned long wb_flags;
|
||||
struct nfs_writeverf wb_verf; /* Commit cookie */
|
||||
struct nfs_write_verifier wb_verf; /* Commit cookie */
|
||||
};
|
||||
|
||||
struct nfs_pageio_descriptor;
|
||||
|
@ -514,9 +514,13 @@ struct nfs_writeargs {
|
||||
struct nfs4_sequence_args seq_args;
|
||||
};
|
||||
|
||||
struct nfs_write_verifier {
|
||||
char data[8];
|
||||
};
|
||||
|
||||
struct nfs_writeverf {
|
||||
struct nfs_write_verifier verifier;
|
||||
enum nfs3_stable_how committed;
|
||||
__be32 verifier[2];
|
||||
};
|
||||
|
||||
struct nfs_writeres {
|
||||
@ -1349,6 +1353,8 @@ struct nfs_renamedata {
|
||||
struct nfs_access_entry;
|
||||
struct nfs_client;
|
||||
struct rpc_timeout;
|
||||
struct nfs_client_initdata;
|
||||
struct nfs_pageio_descriptor;
|
||||
|
||||
/*
|
||||
* 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 (*decode_dirent)(struct xdr_stream *, struct nfs_entry *, int);
|
||||
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 *);
|
||||
int (*read_done) (struct rpc_task *, struct nfs_read_data *);
|
||||
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 *);
|
||||
int (*write_done) (struct rpc_task *, struct nfs_write_data *);
|
||||
void (*commit_setup) (struct nfs_commit_data *, struct rpc_message *);
|
||||
@ -1418,9 +1428,13 @@ struct nfs_rpc_ops {
|
||||
struct nfs_open_context *ctx,
|
||||
int open_flags,
|
||||
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 *
|
||||
(*init_client) (struct nfs_client *, const struct rpc_timeout *,
|
||||
const char *, rpc_authflavor_t);
|
||||
void (*free_client) (struct nfs_client *);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -101,6 +101,7 @@ struct rpc_authops {
|
||||
struct rpc_cred * (*crcreate)(struct rpc_auth*, struct auth_cred *, int);
|
||||
int (*pipes_create)(struct rpc_auth *);
|
||||
void (*pipes_destroy)(struct rpc_auth *);
|
||||
int (*list_pseudoflavors)(rpc_authflavor_t *, int);
|
||||
};
|
||||
|
||||
struct rpc_credops {
|
||||
@ -135,6 +136,7 @@ int rpcauth_register(const struct rpc_authops *);
|
||||
int rpcauth_unregister(const struct rpc_authops *);
|
||||
struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
|
||||
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);
|
||||
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);
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <linux/sunrpc/msg_prot.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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: */
|
||||
struct gss_api_mech * gss_mech_get(struct gss_api_mech *);
|
||||
|
@ -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_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,
|
||||
struct page **, unsigned int, unsigned int);
|
||||
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 scratch; /* Scratch buffer */
|
||||
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 void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
|
||||
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_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
|
||||
struct page **pages, unsigned int len);
|
||||
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 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 int xdr_process_buf(struct xdr_buf *buf, unsigned int offset, unsigned int len, int (*actor)(struct scatterlist *, void *), void *data);
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/gss_api.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
@ -122,6 +123,59 @@ rpcauth_unregister(const struct rpc_authops *ops)
|
||||
}
|
||||
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 *
|
||||
rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
|
||||
{
|
||||
|
@ -1619,6 +1619,7 @@ static const struct rpc_authops authgss_ops = {
|
||||
.crcreate = gss_create_cred,
|
||||
.pipes_create = gss_pipes_dentries_create,
|
||||
.pipes_destroy = gss_pipes_dentries_destroy,
|
||||
.list_pseudoflavors = gss_mech_list_pseudoflavors,
|
||||
};
|
||||
|
||||
static const struct rpc_credops gss_credops = {
|
||||
|
@ -239,14 +239,28 @@ gss_mech_get_by_pseudoflavor(u32 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;
|
||||
int j, i = 0;
|
||||
|
||||
spin_lock(®istered_mechs_lock);
|
||||
list_for_each_entry(pos, ®istered_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(®istered_mechs_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
array_ptr[i++] = pos->gm_pfs[j].pseudoflavor;
|
||||
}
|
||||
}
|
||||
@ -254,8 +268,6 @@ int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr)
|
||||
return i;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(gss_mech_list_pseudoflavors);
|
||||
|
||||
u32
|
||||
gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
|
||||
{
|
||||
|
@ -1844,12 +1844,13 @@ call_timeout(struct rpc_task *task)
|
||||
return;
|
||||
}
|
||||
if (RPC_IS_SOFT(task)) {
|
||||
if (clnt->cl_chatty)
|
||||
if (clnt->cl_chatty) {
|
||||
rcu_read_lock();
|
||||
printk(KERN_NOTICE "%s: server %s not responding, timed out\n",
|
||||
clnt->cl_protname,
|
||||
rcu_dereference(clnt->cl_xprt)->servername);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
if (task->tk_flags & RPC_TASK_TIMEOUT)
|
||||
rpc_exit(task, -ETIMEDOUT);
|
||||
else
|
||||
|
127
net/sunrpc/xdr.c
127
net/sunrpc/xdr.c
@ -128,34 +128,6 @@ xdr_terminate_string(struct xdr_buf *buf, const u32 len)
|
||||
}
|
||||
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
|
||||
xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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: 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);
|
||||
|
||||
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)
|
||||
len = iov->iov_len;
|
||||
if (p == NULL)
|
||||
p = (__be32*)iov->iov_base;
|
||||
xdr->p = p;
|
||||
xdr->p = (__be32*)iov->iov_base;
|
||||
xdr->end = (__be32*)(iov->iov_base + len);
|
||||
xdr->iov = iov;
|
||||
xdr->page_ptr = NULL;
|
||||
@ -609,7 +589,7 @@ static void xdr_set_next_page(struct xdr_stream *xdr)
|
||||
newbase -= xdr->buf->page_base;
|
||||
|
||||
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)
|
||||
@ -618,7 +598,7 @@ static bool xdr_set_next_buffer(struct xdr_stream *xdr)
|
||||
xdr_set_next_page(xdr);
|
||||
else if (xdr->iov == xdr->buf->head) {
|
||||
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;
|
||||
}
|
||||
@ -634,10 +614,15 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
|
||||
xdr->buf = buf;
|
||||
xdr->scratch.iov_base = NULL;
|
||||
xdr->scratch.iov_len = 0;
|
||||
xdr->nwords = XDR_QUADLEN(buf->len);
|
||||
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)
|
||||
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);
|
||||
|
||||
@ -662,12 +647,14 @@ EXPORT_SYMBOL_GPL(xdr_init_decode_pages);
|
||||
|
||||
static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
|
||||
{
|
||||
unsigned int nwords = XDR_QUADLEN(nbytes);
|
||||
__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;
|
||||
xdr->p = q;
|
||||
xdr->nwords -= nwords;
|
||||
return p;
|
||||
}
|
||||
|
||||
@ -734,6 +721,31 @@ __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
|
||||
}
|
||||
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: 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
|
||||
* into the page list. Any data that lies beyond current position + "len"
|
||||
* 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 kvec *iov;
|
||||
ssize_t shift;
|
||||
unsigned int nwords;
|
||||
unsigned int end;
|
||||
int padding;
|
||||
unsigned int padding;
|
||||
|
||||
/* Realign pages to current pointer position */
|
||||
iov = buf->head;
|
||||
shift = iov->iov_len + (char *)iov->iov_base - (char *)xdr->p;
|
||||
if (shift > 0)
|
||||
xdr_shrink_bufhead(buf, shift);
|
||||
|
||||
/* 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;
|
||||
len = xdr_align_pages(xdr, len);
|
||||
if (len == 0)
|
||||
return 0;
|
||||
nwords = XDR_QUADLEN(len);
|
||||
padding = (nwords << 2) - len;
|
||||
xdr->iov = iov = buf->tail;
|
||||
/* Compute remaining message length. */
|
||||
end = iov->iov_len;
|
||||
shift = buf->buflen - buf->len;
|
||||
if (shift < end)
|
||||
end -= shift;
|
||||
else if (shift > 0)
|
||||
end = 0;
|
||||
end = ((xdr->nwords - nwords) << 2) + padding;
|
||||
if (end > iov->iov_len)
|
||||
end = iov->iov_len;
|
||||
|
||||
/*
|
||||
* Position current pointer at beginning of tail, and
|
||||
* set remaining message length.
|
||||
*/
|
||||
xdr->p = (__be32 *)((char *)iov->iov_base + padding);
|
||||
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);
|
||||
|
||||
@ -790,12 +800,13 @@ EXPORT_SYMBOL_GPL(xdr_read_pages);
|
||||
*/
|
||||
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
|
||||
* 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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user