You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'nfs-for-2.6.32'
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
|
||||
The NFS client
|
||||
==============
|
||||
|
||||
The NFS version 2 protocol was first documented in RFC1094 (March 1989).
|
||||
Since then two more major releases of NFS have been published, with NFSv3
|
||||
being documented in RFC1813 (June 1995), and NFSv4 in RFC3530 (April
|
||||
2003).
|
||||
|
||||
The Linux NFS client currently supports all the above published versions,
|
||||
and work is in progress on adding support for minor version 1 of the NFSv4
|
||||
protocol.
|
||||
|
||||
The purpose of this document is to provide information on some of the
|
||||
upcall interfaces that are used in order to provide the NFS client with
|
||||
some of the information that it requires in order to fully comply with
|
||||
the NFS spec.
|
||||
|
||||
The DNS resolver
|
||||
================
|
||||
|
||||
NFSv4 allows for one server to refer the NFS client to data that has been
|
||||
migrated onto another server by means of the special "fs_locations"
|
||||
attribute. See
|
||||
http://tools.ietf.org/html/rfc3530#section-6
|
||||
and
|
||||
http://tools.ietf.org/html/draft-ietf-nfsv4-referrals-00
|
||||
|
||||
The fs_locations information can take the form of either an ip address and
|
||||
a path, or a DNS hostname and a path. The latter requires the NFS client to
|
||||
do a DNS lookup in order to mount the new volume, and hence the need for an
|
||||
upcall to allow userland to provide this service.
|
||||
|
||||
Assuming that the user has the 'rpc_pipefs' filesystem mounted in the usual
|
||||
/var/lib/nfs/rpc_pipefs, the upcall consists of the following steps:
|
||||
|
||||
(1) The process checks the dns_resolve cache to see if it contains a
|
||||
valid entry. If so, it returns that entry and exits.
|
||||
|
||||
(2) If no valid entry exists, the helper script '/sbin/nfs_cache_getent'
|
||||
(may be changed using the 'nfs.cache_getent' kernel boot parameter)
|
||||
is run, with two arguments:
|
||||
- the cache name, "dns_resolve"
|
||||
- the hostname to resolve
|
||||
|
||||
(3) After looking up the corresponding ip address, the helper script
|
||||
writes the result into the rpc_pipefs pseudo-file
|
||||
'/var/lib/nfs/rpc_pipefs/cache/dns_resolve/channel'
|
||||
in the following (text) format:
|
||||
|
||||
"<ip address> <hostname> <ttl>\n"
|
||||
|
||||
Where <ip address> is in the usual IPv4 (123.456.78.90) or IPv6
|
||||
(ffee:ddcc:bbaa:9988:7766:5544:3322:1100, ffee::1100, ...) format.
|
||||
<hostname> is identical to the second argument of the helper
|
||||
script, and <ttl> is the 'time to live' of this cache entry (in
|
||||
units of seconds).
|
||||
|
||||
Note: If <ip address> is invalid, say the string "0", then a negative
|
||||
entry is created, which will cause the kernel to treat the hostname
|
||||
as having no valid DNS translation.
|
||||
|
||||
|
||||
|
||||
|
||||
A basic sample /sbin/nfs_cache_getent
|
||||
=====================================
|
||||
|
||||
#!/bin/bash
|
||||
#
|
||||
ttl=600
|
||||
#
|
||||
cut=/usr/bin/cut
|
||||
getent=/usr/bin/getent
|
||||
rpc_pipefs=/var/lib/nfs/rpc_pipefs
|
||||
#
|
||||
die()
|
||||
{
|
||||
echo "Usage: $0 cache_name entry_name"
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ $# -lt 2 ] && die
|
||||
cachename="$1"
|
||||
cache_path=${rpc_pipefs}/cache/${cachename}/channel
|
||||
|
||||
case "${cachename}" in
|
||||
dns_resolve)
|
||||
name="$2"
|
||||
result="$(${getent} hosts ${name} | ${cut} -f1 -d\ )"
|
||||
[ -z "${result}" ] && result="0"
|
||||
;;
|
||||
*)
|
||||
die
|
||||
;;
|
||||
esac
|
||||
echo "${result} ${name} ${ttl}" >${cache_path}
|
||||
|
||||
@@ -1503,6 +1503,14 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
[NFS] set the TCP port on which the NFSv4 callback
|
||||
channel should listen.
|
||||
|
||||
nfs.cache_getent=
|
||||
[NFS] sets the pathname to the program which is used
|
||||
to update the NFS client cache entries.
|
||||
|
||||
nfs.cache_getent_timeout=
|
||||
[NFS] sets the timeout after which an attempt to
|
||||
update a cache entry is deemed to have failed.
|
||||
|
||||
nfs.idmap_cache_timeout=
|
||||
[NFS] set the maximum lifetime for idmapper cache
|
||||
entries.
|
||||
@@ -2395,6 +2403,18 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
stifb= [HW]
|
||||
Format: bpp:<bpp1>[:<bpp2>[:<bpp3>...]]
|
||||
|
||||
sunrpc.min_resvport=
|
||||
sunrpc.max_resvport=
|
||||
[NFS,SUNRPC]
|
||||
SunRPC servers often require that client requests
|
||||
originate from a privileged port (i.e. a port in the
|
||||
range 0 < portnr < 1024).
|
||||
An administrator who wishes to reserve some of these
|
||||
ports for other uses may adjust the range that the
|
||||
kernel's sunrpc client considers to be privileged
|
||||
using these two parameters to set the minimum and
|
||||
maximum port values.
|
||||
|
||||
sunrpc.pool_mode=
|
||||
[NFS]
|
||||
Control how the NFS server code allocates CPUs to
|
||||
@@ -2411,6 +2431,15 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
pernode one pool for each NUMA node (equivalent
|
||||
to global on non-NUMA machines)
|
||||
|
||||
sunrpc.tcp_slot_table_entries=
|
||||
sunrpc.udp_slot_table_entries=
|
||||
[NFS,SUNRPC]
|
||||
Sets the upper limit on the number of simultaneous
|
||||
RPC calls that can be sent from the client to a
|
||||
server. Increasing these values may allow you to
|
||||
improve throughput, but will also increase the
|
||||
amount of memory reserved for use by the client.
|
||||
|
||||
swiotlb= [IA-64] Number of I/O TLB slabs
|
||||
|
||||
switches= [HW,M68k]
|
||||
|
||||
+1
-13
@@ -87,18 +87,6 @@ static unsigned int nlm_hash_address(const struct sockaddr *sap)
|
||||
return hash & (NLM_HOST_NRHASH - 1);
|
||||
}
|
||||
|
||||
static void nlm_clear_port(struct sockaddr *sap)
|
||||
{
|
||||
switch (sap->sa_family) {
|
||||
case AF_INET:
|
||||
((struct sockaddr_in *)sap)->sin_port = 0;
|
||||
break;
|
||||
case AF_INET6:
|
||||
((struct sockaddr_in6 *)sap)->sin6_port = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Common host lookup routine for server & client
|
||||
*/
|
||||
@@ -177,7 +165,7 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
|
||||
host->h_addrbuf = nsm->sm_addrbuf;
|
||||
memcpy(nlm_addr(host), ni->sap, ni->salen);
|
||||
host->h_addrlen = ni->salen;
|
||||
nlm_clear_port(nlm_addr(host));
|
||||
rpc_set_port(nlm_addr(host), 0);
|
||||
memcpy(nlm_srcaddr(host), ni->src_sap, ni->src_len);
|
||||
host->h_version = ni->version;
|
||||
host->h_proto = ni->protocol;
|
||||
|
||||
+5
-39
@@ -61,43 +61,6 @@ static inline struct sockaddr *nsm_addr(const struct nsm_handle *nsm)
|
||||
return (struct sockaddr *)&nsm->sm_addr;
|
||||
}
|
||||
|
||||
static void nsm_display_ipv4_address(const struct sockaddr *sap, char *buf,
|
||||
const size_t len)
|
||||
{
|
||||
const struct sockaddr_in *sin = (struct sockaddr_in *)sap;
|
||||
snprintf(buf, len, "%pI4", &sin->sin_addr.s_addr);
|
||||
}
|
||||
|
||||
static void nsm_display_ipv6_address(const struct sockaddr *sap, char *buf,
|
||||
const size_t len)
|
||||
{
|
||||
const struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
|
||||
|
||||
if (ipv6_addr_v4mapped(&sin6->sin6_addr))
|
||||
snprintf(buf, len, "%pI4", &sin6->sin6_addr.s6_addr32[3]);
|
||||
else if (sin6->sin6_scope_id != 0)
|
||||
snprintf(buf, len, "%pI6%%%u", &sin6->sin6_addr,
|
||||
sin6->sin6_scope_id);
|
||||
else
|
||||
snprintf(buf, len, "%pI6", &sin6->sin6_addr);
|
||||
}
|
||||
|
||||
static void nsm_display_address(const struct sockaddr *sap,
|
||||
char *buf, const size_t len)
|
||||
{
|
||||
switch (sap->sa_family) {
|
||||
case AF_INET:
|
||||
nsm_display_ipv4_address(sap, buf, len);
|
||||
break;
|
||||
case AF_INET6:
|
||||
nsm_display_ipv6_address(sap, buf, len);
|
||||
break;
|
||||
default:
|
||||
snprintf(buf, len, "unsupported address family");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct rpc_clnt *nsm_create(void)
|
||||
{
|
||||
struct sockaddr_in sin = {
|
||||
@@ -307,8 +270,11 @@ static struct nsm_handle *nsm_create_handle(const struct sockaddr *sap,
|
||||
memcpy(nsm_addr(new), sap, salen);
|
||||
new->sm_addrlen = salen;
|
||||
nsm_init_private(new);
|
||||
nsm_display_address((const struct sockaddr *)&new->sm_addr,
|
||||
new->sm_addrbuf, sizeof(new->sm_addrbuf));
|
||||
|
||||
if (rpc_ntop(nsm_addr(new), new->sm_addrbuf,
|
||||
sizeof(new->sm_addrbuf)) == 0)
|
||||
(void)snprintf(new->sm_addrbuf, sizeof(new->sm_addrbuf),
|
||||
"unsupported address family");
|
||||
memcpy(new->sm_name, hostname, hostname_len);
|
||||
new->sm_name[hostname_len] = '\0';
|
||||
|
||||
|
||||
+2
-1
@@ -6,7 +6,8 @@ obj-$(CONFIG_NFS_FS) += nfs.o
|
||||
|
||||
nfs-y := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \
|
||||
direct.o pagelist.o proc.o read.o symlink.o unlink.o \
|
||||
write.o namespace.o mount_clnt.o
|
||||
write.o namespace.o mount_clnt.o \
|
||||
dns_resolve.o cache_lib.o
|
||||
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
|
||||
nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
|
||||
nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* linux/fs/nfs/cache_lib.c
|
||||
*
|
||||
* Helper routines for the NFS client caches
|
||||
*
|
||||
* Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
|
||||
*/
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/sunrpc/cache.h>
|
||||
#include <linux/sunrpc/rpc_pipe_fs.h>
|
||||
|
||||
#include "cache_lib.h"
|
||||
|
||||
#define NFS_CACHE_UPCALL_PATHLEN 256
|
||||
#define NFS_CACHE_UPCALL_TIMEOUT 15
|
||||
|
||||
static char nfs_cache_getent_prog[NFS_CACHE_UPCALL_PATHLEN] =
|
||||
"/sbin/nfs_cache_getent";
|
||||
static unsigned long nfs_cache_getent_timeout = NFS_CACHE_UPCALL_TIMEOUT;
|
||||
|
||||
module_param_string(cache_getent, nfs_cache_getent_prog,
|
||||
sizeof(nfs_cache_getent_prog), 0600);
|
||||
MODULE_PARM_DESC(cache_getent, "Path to the client cache upcall program");
|
||||
module_param_named(cache_getent_timeout, nfs_cache_getent_timeout, ulong, 0600);
|
||||
MODULE_PARM_DESC(cache_getent_timeout, "Timeout (in seconds) after which "
|
||||
"the cache upcall is assumed to have failed");
|
||||
|
||||
int nfs_cache_upcall(struct cache_detail *cd, char *entry_name)
|
||||
{
|
||||
static char *envp[] = { "HOME=/",
|
||||
"TERM=linux",
|
||||
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
|
||||
NULL
|
||||
};
|
||||
char *argv[] = {
|
||||
nfs_cache_getent_prog,
|
||||
cd->name,
|
||||
entry_name,
|
||||
NULL
|
||||
};
|
||||
int ret = -EACCES;
|
||||
|
||||
if (nfs_cache_getent_prog[0] == '\0')
|
||||
goto out;
|
||||
ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
|
||||
/*
|
||||
* Disable the upcall mechanism if we're getting an ENOENT or
|
||||
* EACCES error. The admin can re-enable it on the fly by using
|
||||
* sysfs to set the 'cache_getent' parameter once the problem
|
||||
* has been fixed.
|
||||
*/
|
||||
if (ret == -ENOENT || ret == -EACCES)
|
||||
nfs_cache_getent_prog[0] = '\0';
|
||||
out:
|
||||
return ret > 0 ? 0 : ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deferred request handling
|
||||
*/
|
||||
void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq)
|
||||
{
|
||||
if (atomic_dec_and_test(&dreq->count))
|
||||
kfree(dreq);
|
||||
}
|
||||
|
||||
static void nfs_dns_cache_revisit(struct cache_deferred_req *d, int toomany)
|
||||
{
|
||||
struct nfs_cache_defer_req *dreq;
|
||||
|
||||
dreq = container_of(d, struct nfs_cache_defer_req, deferred_req);
|
||||
|
||||
complete_all(&dreq->completion);
|
||||
nfs_cache_defer_req_put(dreq);
|
||||
}
|
||||
|
||||
static struct cache_deferred_req *nfs_dns_cache_defer(struct cache_req *req)
|
||||
{
|
||||
struct nfs_cache_defer_req *dreq;
|
||||
|
||||
dreq = container_of(req, struct nfs_cache_defer_req, req);
|
||||
dreq->deferred_req.revisit = nfs_dns_cache_revisit;
|
||||
atomic_inc(&dreq->count);
|
||||
|
||||
return &dreq->deferred_req;
|
||||
}
|
||||
|
||||
struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void)
|
||||
{
|
||||
struct nfs_cache_defer_req *dreq;
|
||||
|
||||
dreq = kzalloc(sizeof(*dreq), GFP_KERNEL);
|
||||
if (dreq) {
|
||||
init_completion(&dreq->completion);
|
||||
atomic_set(&dreq->count, 1);
|
||||
dreq->req.defer = nfs_dns_cache_defer;
|
||||
}
|
||||
return dreq;
|
||||
}
|
||||
|
||||
int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq)
|
||||
{
|
||||
if (wait_for_completion_timeout(&dreq->completion,
|
||||
nfs_cache_getent_timeout * HZ) == 0)
|
||||
return -ETIMEDOUT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nfs_cache_register(struct cache_detail *cd)
|
||||
{
|
||||
struct nameidata nd;
|
||||
struct vfsmount *mnt;
|
||||
int ret;
|
||||
|
||||
mnt = rpc_get_mount();
|
||||
if (IS_ERR(mnt))
|
||||
return PTR_ERR(mnt);
|
||||
ret = vfs_path_lookup(mnt->mnt_root, mnt, "/cache", 0, &nd);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = sunrpc_cache_register_pipefs(nd.path.dentry,
|
||||
cd->name, 0600, cd);
|
||||
path_put(&nd.path);
|
||||
if (!ret)
|
||||
return ret;
|
||||
err:
|
||||
rpc_put_mount();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void nfs_cache_unregister(struct cache_detail *cd)
|
||||
{
|
||||
sunrpc_cache_unregister_pipefs(cd);
|
||||
rpc_put_mount();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Helper routines for the NFS client caches
|
||||
*
|
||||
* Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/sunrpc/cache.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
/*
|
||||
* Deferred request handling
|
||||
*/
|
||||
struct nfs_cache_defer_req {
|
||||
struct cache_req req;
|
||||
struct cache_deferred_req deferred_req;
|
||||
struct completion completion;
|
||||
atomic_t count;
|
||||
};
|
||||
|
||||
extern int nfs_cache_upcall(struct cache_detail *cd, char *entry_name);
|
||||
extern struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void);
|
||||
extern void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq);
|
||||
extern int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq);
|
||||
|
||||
extern int nfs_cache_register(struct cache_detail *cd);
|
||||
extern void nfs_cache_unregister(struct cache_detail *cd);
|
||||
+17
-9
@@ -43,21 +43,29 @@ static struct svc_program nfs4_callback_program;
|
||||
unsigned int nfs_callback_set_tcpport;
|
||||
unsigned short nfs_callback_tcpport;
|
||||
unsigned short nfs_callback_tcpport6;
|
||||
static const int nfs_set_port_min = 0;
|
||||
static const int nfs_set_port_max = 65535;
|
||||
#define NFS_CALLBACK_MAXPORTNR (65535U)
|
||||
|
||||
static int param_set_port(const char *val, struct kernel_param *kp)
|
||||
static int param_set_portnr(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
char *endp;
|
||||
int num = simple_strtol(val, &endp, 0);
|
||||
if (endp == val || *endp || num < nfs_set_port_min || num > nfs_set_port_max)
|
||||
unsigned long num;
|
||||
int ret;
|
||||
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
*((int *)kp->arg) = num;
|
||||
ret = strict_strtoul(val, 0, &num);
|
||||
if (ret == -EINVAL || num > NFS_CALLBACK_MAXPORTNR)
|
||||
return -EINVAL;
|
||||
*((unsigned int *)kp->arg) = num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_param_call(callback_tcpport, param_set_port, param_get_int,
|
||||
&nfs_callback_set_tcpport, 0644);
|
||||
static int param_get_portnr(char *buffer, struct kernel_param *kp)
|
||||
{
|
||||
return param_get_uint(buffer, kp);
|
||||
}
|
||||
#define param_check_portnr(name, p) __param_check(name, p, unsigned int);
|
||||
|
||||
module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644);
|
||||
|
||||
/*
|
||||
* This is the NFSv4 callback kernel thread.
|
||||
|
||||
+5
-10
@@ -809,6 +809,9 @@ static int nfs_init_server(struct nfs_server *server,
|
||||
/* Initialise the client representation from the mount data */
|
||||
server->flags = data->flags;
|
||||
server->options = data->options;
|
||||
server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
|
||||
NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP|
|
||||
NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME;
|
||||
|
||||
if (data->rsize)
|
||||
server->rsize = nfs_block_size(data->rsize, NULL);
|
||||
@@ -1075,10 +1078,6 @@ struct nfs_server *nfs_create_server(const struct nfs_parsed_mount_data *data,
|
||||
(unsigned long long) server->fsid.major,
|
||||
(unsigned long long) server->fsid.minor);
|
||||
|
||||
BUG_ON(!server->nfs_client);
|
||||
BUG_ON(!server->nfs_client->rpc_ops);
|
||||
BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
|
||||
|
||||
spin_lock(&nfs_client_lock);
|
||||
list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
|
||||
list_add_tail(&server->master_link, &nfs_volume_list);
|
||||
@@ -1275,7 +1274,7 @@ static int nfs4_init_server(struct nfs_server *server,
|
||||
|
||||
/* Initialise the client representation from the mount data */
|
||||
server->flags = data->flags;
|
||||
server->caps |= NFS_CAP_ATOMIC_OPEN;
|
||||
server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR;
|
||||
server->options = data->options;
|
||||
|
||||
/* Get a client record */
|
||||
@@ -1360,10 +1359,6 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data,
|
||||
if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN)
|
||||
server->namelen = NFS4_MAXNAMLEN;
|
||||
|
||||
BUG_ON(!server->nfs_client);
|
||||
BUG_ON(!server->nfs_client->rpc_ops);
|
||||
BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops);
|
||||
|
||||
spin_lock(&nfs_client_lock);
|
||||
list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks);
|
||||
list_add_tail(&server->master_link, &nfs_volume_list);
|
||||
@@ -1401,7 +1396,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
|
||||
|
||||
/* Initialise the client representation from the parent server */
|
||||
nfs_server_copy_userdata(server, parent_server);
|
||||
server->caps |= NFS_CAP_ATOMIC_OPEN;
|
||||
server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR;
|
||||
|
||||
/* Get a client representation.
|
||||
* Note: NFSv4 always uses TCP, */
|
||||
|
||||
@@ -934,9 +934,6 @@ out:
|
||||
* back into its cache. We let the server do generic write
|
||||
* parameter checking and report problems.
|
||||
*
|
||||
* We also avoid an unnecessary invocation of generic_osync_inode(),
|
||||
* as it is fairly meaningless to sync the metadata of an NFS file.
|
||||
*
|
||||
* We eliminate local atime updates, see direct read above.
|
||||
*
|
||||
* We avoid unnecessary page cache invalidations for normal cached
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* linux/fs/nfs/dns_resolve.c
|
||||
*
|
||||
* Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
|
||||
*
|
||||
* Resolves DNS hostnames into valid ip addresses
|
||||
*/
|
||||
|
||||
#include <linux/hash.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/kmod.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/cache.h>
|
||||
#include <linux/sunrpc/svcauth.h>
|
||||
|
||||
#include "dns_resolve.h"
|
||||
#include "cache_lib.h"
|
||||
|
||||
#define NFS_DNS_HASHBITS 4
|
||||
#define NFS_DNS_HASHTBL_SIZE (1 << NFS_DNS_HASHBITS)
|
||||
|
||||
static struct cache_head *nfs_dns_table[NFS_DNS_HASHTBL_SIZE];
|
||||
|
||||
struct nfs_dns_ent {
|
||||
struct cache_head h;
|
||||
|
||||
char *hostname;
|
||||
size_t namelen;
|
||||
|
||||
struct sockaddr_storage addr;
|
||||
size_t addrlen;
|
||||
};
|
||||
|
||||
|
||||
static void nfs_dns_ent_init(struct cache_head *cnew,
|
||||
struct cache_head *ckey)
|
||||
{
|
||||
struct nfs_dns_ent *new;
|
||||
struct nfs_dns_ent *key;
|
||||
|
||||
new = container_of(cnew, struct nfs_dns_ent, h);
|
||||
key = container_of(ckey, struct nfs_dns_ent, h);
|
||||
|
||||
kfree(new->hostname);
|
||||
new->hostname = kstrndup(key->hostname, key->namelen, GFP_KERNEL);
|
||||
if (new->hostname) {
|
||||
new->namelen = key->namelen;
|
||||
memcpy(&new->addr, &key->addr, key->addrlen);
|
||||
new->addrlen = key->addrlen;
|
||||
} else {
|
||||
new->namelen = 0;
|
||||
new->addrlen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void nfs_dns_ent_put(struct kref *ref)
|
||||
{
|
||||
struct nfs_dns_ent *item;
|
||||
|
||||
item = container_of(ref, struct nfs_dns_ent, h.ref);
|
||||
kfree(item->hostname);
|
||||
kfree(item);
|
||||
}
|
||||
|
||||
static struct cache_head *nfs_dns_ent_alloc(void)
|
||||
{
|
||||
struct nfs_dns_ent *item = kmalloc(sizeof(*item), GFP_KERNEL);
|
||||
|
||||
if (item != NULL) {
|
||||
item->hostname = NULL;
|
||||
item->namelen = 0;
|
||||
item->addrlen = 0;
|
||||
return &item->h;
|
||||
}
|
||||
return NULL;
|
||||
};
|
||||
|
||||
static unsigned int nfs_dns_hash(const struct nfs_dns_ent *key)
|
||||
{
|
||||
return hash_str(key->hostname, NFS_DNS_HASHBITS);
|
||||
}
|
||||
|
||||
static void nfs_dns_request(struct cache_detail *cd,
|
||||
struct cache_head *ch,
|
||||
char **bpp, int *blen)
|
||||
{
|
||||
struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h);
|
||||
|
||||
qword_add(bpp, blen, key->hostname);
|
||||
(*bpp)[-1] = '\n';
|
||||
}
|
||||
|
||||
static int nfs_dns_upcall(struct cache_detail *cd,
|
||||
struct cache_head *ch)
|
||||
{
|
||||
struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h);
|
||||
int ret;
|
||||
|
||||
ret = nfs_cache_upcall(cd, key->hostname);
|
||||
if (ret)
|
||||
ret = sunrpc_cache_pipe_upcall(cd, ch, nfs_dns_request);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nfs_dns_match(struct cache_head *ca,
|
||||
struct cache_head *cb)
|
||||
{
|
||||
struct nfs_dns_ent *a;
|
||||
struct nfs_dns_ent *b;
|
||||
|
||||
a = container_of(ca, struct nfs_dns_ent, h);
|
||||
b = container_of(cb, struct nfs_dns_ent, h);
|
||||
|
||||
if (a->namelen == 0 || a->namelen != b->namelen)
|
||||
return 0;
|
||||
return memcmp(a->hostname, b->hostname, a->namelen) == 0;
|
||||
}
|
||||
|
||||
static int nfs_dns_show(struct seq_file *m, struct cache_detail *cd,
|
||||
struct cache_head *h)
|
||||
{
|
||||
struct nfs_dns_ent *item;
|
||||
long ttl;
|
||||
|
||||
if (h == NULL) {
|
||||
seq_puts(m, "# ip address hostname ttl\n");
|
||||
return 0;
|
||||
}
|
||||
item = container_of(h, struct nfs_dns_ent, h);
|
||||
ttl = (long)item->h.expiry_time - (long)get_seconds();
|
||||
if (ttl < 0)
|
||||
ttl = 0;
|
||||
|
||||
if (!test_bit(CACHE_NEGATIVE, &h->flags)) {
|
||||
char buf[INET6_ADDRSTRLEN+IPV6_SCOPE_ID_LEN+1];
|
||||
|
||||
rpc_ntop((struct sockaddr *)&item->addr, buf, sizeof(buf));
|
||||
seq_printf(m, "%15s ", buf);
|
||||
} else
|
||||
seq_puts(m, "<none> ");
|
||||
seq_printf(m, "%15s %ld\n", item->hostname, ttl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct nfs_dns_ent *nfs_dns_lookup(struct cache_detail *cd,
|
||||
struct nfs_dns_ent *key)
|
||||
{
|
||||
struct cache_head *ch;
|
||||
|
||||
ch = sunrpc_cache_lookup(cd,
|
||||
&key->h,
|
||||
nfs_dns_hash(key));
|
||||
if (!ch)
|
||||
return NULL;
|
||||
return container_of(ch, struct nfs_dns_ent, h);
|
||||
}
|
||||
|
||||
struct nfs_dns_ent *nfs_dns_update(struct cache_detail *cd,
|
||||
struct nfs_dns_ent *new,
|
||||
struct nfs_dns_ent *key)
|
||||
{
|
||||
struct cache_head *ch;
|
||||
|
||||
ch = sunrpc_cache_update(cd,
|
||||
&new->h, &key->h,
|
||||
nfs_dns_hash(key));
|
||||
if (!ch)
|
||||
return NULL;
|
||||
return container_of(ch, struct nfs_dns_ent, h);
|
||||
}
|
||||
|
||||
static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)
|
||||
{
|
||||
char buf1[NFS_DNS_HOSTNAME_MAXLEN+1];
|
||||
struct nfs_dns_ent key, *item;
|
||||
unsigned long ttl;
|
||||
ssize_t len;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (buf[buflen-1] != '\n')
|
||||
goto out;
|
||||
buf[buflen-1] = '\0';
|
||||
|
||||
len = qword_get(&buf, buf1, sizeof(buf1));
|
||||
if (len <= 0)
|
||||
goto out;
|
||||
key.addrlen = rpc_pton(buf1, len,
|
||||
(struct sockaddr *)&key.addr,
|
||||
sizeof(key.addr));
|
||||
|
||||
len = qword_get(&buf, buf1, sizeof(buf1));
|
||||
if (len <= 0)
|
||||
goto out;
|
||||
|
||||
key.hostname = buf1;
|
||||
key.namelen = len;
|
||||
memset(&key.h, 0, sizeof(key.h));
|
||||
|
||||
ttl = get_expiry(&buf);
|
||||
if (ttl == 0)
|
||||
goto out;
|
||||
key.h.expiry_time = ttl + get_seconds();
|
||||
|
||||
ret = -ENOMEM;
|
||||
item = nfs_dns_lookup(cd, &key);
|
||||
if (item == NULL)
|
||||
goto out;
|
||||
|
||||
if (key.addrlen == 0)
|
||||
set_bit(CACHE_NEGATIVE, &key.h.flags);
|
||||
|
||||
item = nfs_dns_update(cd, &key, item);
|
||||
if (item == NULL)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
cache_put(&item->h, cd);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct cache_detail nfs_dns_resolve = {
|
||||
.owner = THIS_MODULE,
|
||||
.hash_size = NFS_DNS_HASHTBL_SIZE,
|
||||
.hash_table = nfs_dns_table,
|
||||
.name = "dns_resolve",
|
||||
.cache_put = nfs_dns_ent_put,
|
||||
.cache_upcall = nfs_dns_upcall,
|
||||
.cache_parse = nfs_dns_parse,
|
||||
.cache_show = nfs_dns_show,
|
||||
.match = nfs_dns_match,
|
||||
.init = nfs_dns_ent_init,
|
||||
.update = nfs_dns_ent_init,
|
||||
.alloc = nfs_dns_ent_alloc,
|
||||
};
|
||||
|
||||
static int do_cache_lookup(struct cache_detail *cd,
|
||||
struct nfs_dns_ent *key,
|
||||
struct nfs_dns_ent **item,
|
||||
struct nfs_cache_defer_req *dreq)
|
||||
{
|
||||
int ret = -ENOMEM;
|
||||
|
||||
*item = nfs_dns_lookup(cd, key);
|
||||
if (*item) {
|
||||
ret = cache_check(cd, &(*item)->h, &dreq->req);
|
||||
if (ret)
|
||||
*item = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_cache_lookup_nowait(struct cache_detail *cd,
|
||||
struct nfs_dns_ent *key,
|
||||
struct nfs_dns_ent **item)
|
||||
{
|
||||
int ret = -ENOMEM;
|
||||
|
||||
*item = nfs_dns_lookup(cd, key);
|
||||
if (!*item)
|
||||
goto out_err;
|
||||
ret = -ETIMEDOUT;
|
||||
if (!test_bit(CACHE_VALID, &(*item)->h.flags)
|
||||
|| (*item)->h.expiry_time < get_seconds()
|
||||
|| cd->flush_time > (*item)->h.last_refresh)
|
||||
goto out_put;
|
||||
ret = -ENOENT;
|
||||
if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags))
|
||||
goto out_put;
|
||||
return 0;
|
||||
out_put:
|
||||
cache_put(&(*item)->h, cd);
|
||||
out_err:
|
||||
*item = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int do_cache_lookup_wait(struct cache_detail *cd,
|
||||
struct nfs_dns_ent *key,
|
||||
struct nfs_dns_ent **item)
|
||||
{
|
||||
struct nfs_cache_defer_req *dreq;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
dreq = nfs_cache_defer_req_alloc();
|
||||
if (!dreq)
|
||||
goto out;
|
||||
ret = do_cache_lookup(cd, key, item, dreq);
|
||||
if (ret == -EAGAIN) {
|
||||
ret = nfs_cache_wait_for_upcall(dreq);
|
||||
if (!ret)
|
||||
ret = do_cache_lookup_nowait(cd, key, item);
|
||||
}
|
||||
nfs_cache_defer_req_put(dreq);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
|
||||
struct sockaddr *sa, size_t salen)
|
||||
{
|
||||
struct nfs_dns_ent key = {
|
||||
.hostname = name,
|
||||
.namelen = namelen,
|
||||
};
|
||||
struct nfs_dns_ent *item = NULL;
|
||||
ssize_t ret;
|
||||
|
||||
ret = do_cache_lookup_wait(&nfs_dns_resolve, &key, &item);
|
||||
if (ret == 0) {
|
||||
if (salen >= item->addrlen) {
|
||||
memcpy(sa, &item->addr, item->addrlen);
|
||||
ret = item->addrlen;
|
||||
} else
|
||||
ret = -EOVERFLOW;
|
||||
cache_put(&item->h, &nfs_dns_resolve);
|
||||
} else if (ret == -ENOENT)
|
||||
ret = -ESRCH;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int nfs_dns_resolver_init(void)
|
||||
{
|
||||
return nfs_cache_register(&nfs_dns_resolve);
|
||||
}
|
||||
|
||||
void nfs_dns_resolver_destroy(void)
|
||||
{
|
||||
nfs_cache_unregister(&nfs_dns_resolve);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Resolve DNS hostnames into valid ip addresses
|
||||
*/
|
||||
#ifndef __LINUX_FS_NFS_DNS_RESOLVE_H
|
||||
#define __LINUX_FS_NFS_DNS_RESOLVE_H
|
||||
|
||||
#define NFS_DNS_HOSTNAME_MAXLEN (128)
|
||||
|
||||
extern int nfs_dns_resolver_init(void);
|
||||
extern void nfs_dns_resolver_destroy(void);
|
||||
extern ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
|
||||
struct sockaddr *sa, size_t salen);
|
||||
|
||||
#endif
|
||||
+47
-2
@@ -327,6 +327,42 @@ nfs_file_fsync(struct file *file, struct dentry *dentry, int datasync)
|
||||
return nfs_do_fsync(ctx, inode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decide whether a read/modify/write cycle may be more efficient
|
||||
* then a modify/write/read cycle when writing to a page in the
|
||||
* page cache.
|
||||
*
|
||||
* The modify/write/read cycle may occur if a page is read before
|
||||
* being completely filled by the writer. In this situation, the
|
||||
* page must be completely written to stable storage on the server
|
||||
* before it can be refilled by reading in the page from the server.
|
||||
* This can lead to expensive, small, FILE_SYNC mode writes being
|
||||
* done.
|
||||
*
|
||||
* It may be more efficient to read the page first if the file is
|
||||
* open for reading in addition to writing, the page is not marked
|
||||
* as Uptodate, it is not dirty or waiting to be committed,
|
||||
* indicating that it was previously allocated and then modified,
|
||||
* that there were valid bytes of data in that range of the file,
|
||||
* and that the new data won't completely replace the old data in
|
||||
* that range of the file.
|
||||
*/
|
||||
static int nfs_want_read_modify_write(struct file *file, struct page *page,
|
||||
loff_t pos, unsigned len)
|
||||
{
|
||||
unsigned int pglen = nfs_page_length(page);
|
||||
unsigned int offset = pos & (PAGE_CACHE_SIZE - 1);
|
||||
unsigned int end = offset + len;
|
||||
|
||||
if ((file->f_mode & FMODE_READ) && /* open for read? */
|
||||
!PageUptodate(page) && /* Uptodate? */
|
||||
!PagePrivate(page) && /* i/o request already? */
|
||||
pglen && /* valid bytes of file? */
|
||||
(end < pglen || offset)) /* replace all valid bytes? */
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This does the "real" work of the write. We must allocate and lock the
|
||||
* page to be sent back to the generic routine, which then copies the
|
||||
@@ -340,15 +376,16 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
struct page **pagep, void **fsdata)
|
||||
{
|
||||
int ret;
|
||||
pgoff_t index;
|
||||
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
|
||||
struct page *page;
|
||||
index = pos >> PAGE_CACHE_SHIFT;
|
||||
int once_thru = 0;
|
||||
|
||||
dfprintk(PAGECACHE, "NFS: write_begin(%s/%s(%ld), %u@%lld)\n",
|
||||
file->f_path.dentry->d_parent->d_name.name,
|
||||
file->f_path.dentry->d_name.name,
|
||||
mapping->host->i_ino, len, (long long) pos);
|
||||
|
||||
start:
|
||||
/*
|
||||
* Prevent starvation issues if someone is doing a consistency
|
||||
* sync-to-disk
|
||||
@@ -367,6 +404,13 @@ static int nfs_write_begin(struct file *file, struct address_space *mapping,
|
||||
if (ret) {
|
||||
unlock_page(page);
|
||||
page_cache_release(page);
|
||||
} else if (!once_thru &&
|
||||
nfs_want_read_modify_write(file, page, pos, len)) {
|
||||
once_thru = 1;
|
||||
ret = nfs_readpage(file, page);
|
||||
page_cache_release(page);
|
||||
if (!ret)
|
||||
goto start;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -479,6 +523,7 @@ const struct address_space_operations nfs_file_aops = {
|
||||
.invalidatepage = nfs_invalidate_page,
|
||||
.releasepage = nfs_release_page,
|
||||
.direct_IO = nfs_direct_IO,
|
||||
.migratepage = nfs_migrate_page,
|
||||
.launder_page = nfs_launder_page,
|
||||
};
|
||||
|
||||
|
||||
+3
-3
@@ -101,7 +101,7 @@ static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *);
|
||||
|
||||
static unsigned int fnvhash32(const void *, size_t);
|
||||
|
||||
static struct rpc_pipe_ops idmap_upcall_ops = {
|
||||
static const struct rpc_pipe_ops idmap_upcall_ops = {
|
||||
.upcall = idmap_pipe_upcall,
|
||||
.downcall = idmap_pipe_downcall,
|
||||
.destroy_msg = idmap_pipe_destroy_msg,
|
||||
@@ -119,8 +119,8 @@ nfs_idmap_new(struct nfs_client *clp)
|
||||
if (idmap == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
idmap->idmap_dentry = rpc_mkpipe(clp->cl_rpcclient->cl_dentry, "idmap",
|
||||
idmap, &idmap_upcall_ops, 0);
|
||||
idmap->idmap_dentry = rpc_mkpipe(clp->cl_rpcclient->cl_path.dentry,
|
||||
"idmap", idmap, &idmap_upcall_ops, 0);
|
||||
if (IS_ERR(idmap->idmap_dentry)) {
|
||||
error = PTR_ERR(idmap->idmap_dentry);
|
||||
kfree(idmap);
|
||||
|
||||
+86
-14
@@ -46,6 +46,7 @@
|
||||
#include "iostat.h"
|
||||
#include "internal.h"
|
||||
#include "fscache.h"
|
||||
#include "dns_resolve.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_VFS
|
||||
|
||||
@@ -286,6 +287,11 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
|
||||
/* We can't support update_atime(), since the server will reset it */
|
||||
inode->i_flags |= S_NOATIME|S_NOCMTIME;
|
||||
inode->i_mode = fattr->mode;
|
||||
if ((fattr->valid & NFS_ATTR_FATTR_MODE) == 0
|
||||
&& nfs_server_capable(inode, NFS_CAP_MODE))
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_ACCESS
|
||||
| NFS_INO_INVALID_ACL;
|
||||
/* Why so? Because we want revalidate for devices/FIFOs, and
|
||||
* that's precisely what we have in nfs_file_inode_operations.
|
||||
*/
|
||||
@@ -330,20 +336,46 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
|
||||
nfsi->attr_gencount = fattr->gencount;
|
||||
if (fattr->valid & NFS_ATTR_FATTR_ATIME)
|
||||
inode->i_atime = fattr->atime;
|
||||
else if (nfs_server_capable(inode, NFS_CAP_ATIME))
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
|
||||
if (fattr->valid & NFS_ATTR_FATTR_MTIME)
|
||||
inode->i_mtime = fattr->mtime;
|
||||
else if (nfs_server_capable(inode, NFS_CAP_MTIME))
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_DATA;
|
||||
if (fattr->valid & NFS_ATTR_FATTR_CTIME)
|
||||
inode->i_ctime = fattr->ctime;
|
||||
else if (nfs_server_capable(inode, NFS_CAP_CTIME))
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_ACCESS
|
||||
| NFS_INO_INVALID_ACL;
|
||||
if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
|
||||
nfsi->change_attr = fattr->change_attr;
|
||||
else if (nfs_server_capable(inode, NFS_CAP_CHANGE_ATTR))
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_DATA;
|
||||
if (fattr->valid & NFS_ATTR_FATTR_SIZE)
|
||||
inode->i_size = nfs_size_to_loff_t(fattr->size);
|
||||
else
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_DATA
|
||||
| NFS_INO_REVAL_PAGECACHE;
|
||||
if (fattr->valid & NFS_ATTR_FATTR_NLINK)
|
||||
inode->i_nlink = fattr->nlink;
|
||||
else if (nfs_server_capable(inode, NFS_CAP_NLINK))
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
|
||||
if (fattr->valid & NFS_ATTR_FATTR_OWNER)
|
||||
inode->i_uid = fattr->uid;
|
||||
else if (nfs_server_capable(inode, NFS_CAP_OWNER))
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_ACCESS
|
||||
| NFS_INO_INVALID_ACL;
|
||||
if (fattr->valid & NFS_ATTR_FATTR_GROUP)
|
||||
inode->i_gid = fattr->gid;
|
||||
else if (nfs_server_capable(inode, NFS_CAP_OWNER_GROUP))
|
||||
nfsi->cache_validity |= NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_ACCESS
|
||||
| NFS_INO_INVALID_ACL;
|
||||
if (fattr->valid & NFS_ATTR_FATTR_BLOCKS_USED)
|
||||
inode->i_blocks = fattr->du.nfs2.blocks;
|
||||
if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
|
||||
@@ -1145,6 +1177,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
||||
loff_t cur_isize, new_isize;
|
||||
unsigned long invalid = 0;
|
||||
unsigned long now = jiffies;
|
||||
unsigned long save_cache_validity;
|
||||
|
||||
dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n",
|
||||
__func__, inode->i_sb->s_id, inode->i_ino,
|
||||
@@ -1171,10 +1204,11 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
||||
*/
|
||||
nfsi->read_cache_jiffies = fattr->time_start;
|
||||
|
||||
if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) || (fattr->valid & (NFS_ATTR_FATTR_MTIME|NFS_ATTR_FATTR_CTIME)))
|
||||
nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_ATIME
|
||||
| NFS_INO_REVAL_PAGECACHE);
|
||||
save_cache_validity = nfsi->cache_validity;
|
||||
nfsi->cache_validity &= ~(NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_ATIME
|
||||
| NFS_INO_REVAL_FORCED
|
||||
| NFS_INO_REVAL_PAGECACHE);
|
||||
|
||||
/* Do atomic weak cache consistency updates */
|
||||
nfs_wcc_update_inode(inode, fattr);
|
||||
@@ -1189,7 +1223,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
||||
nfs_force_lookup_revalidate(inode);
|
||||
nfsi->change_attr = fattr->change_attr;
|
||||
}
|
||||
}
|
||||
} else if (server->caps & NFS_CAP_CHANGE_ATTR)
|
||||
invalid |= save_cache_validity;
|
||||
|
||||
if (fattr->valid & NFS_ATTR_FATTR_MTIME) {
|
||||
/* NFSv2/v3: Check if the mtime agrees */
|
||||
@@ -1201,7 +1236,12 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
||||
nfs_force_lookup_revalidate(inode);
|
||||
memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
|
||||
}
|
||||
}
|
||||
} else if (server->caps & NFS_CAP_MTIME)
|
||||
invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_DATA
|
||||
| NFS_INO_REVAL_PAGECACHE
|
||||
| NFS_INO_REVAL_FORCED);
|
||||
|
||||
if (fattr->valid & NFS_ATTR_FATTR_CTIME) {
|
||||
/* If ctime has changed we should definitely clear access+acl caches */
|
||||
if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) {
|
||||
@@ -1215,7 +1255,11 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
||||
}
|
||||
memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
|
||||
}
|
||||
}
|
||||
} else if (server->caps & NFS_CAP_CTIME)
|
||||
invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_ACCESS
|
||||
| NFS_INO_INVALID_ACL
|
||||
| NFS_INO_REVAL_FORCED);
|
||||
|
||||
/* Check if our cached file size is stale */
|
||||
if (fattr->valid & NFS_ATTR_FATTR_SIZE) {
|
||||
@@ -1231,30 +1275,50 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
||||
dprintk("NFS: isize change on server for file %s/%ld\n",
|
||||
inode->i_sb->s_id, inode->i_ino);
|
||||
}
|
||||
}
|
||||
} else
|
||||
invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_REVAL_PAGECACHE
|
||||
| NFS_INO_REVAL_FORCED);
|
||||
|
||||
|
||||
if (fattr->valid & NFS_ATTR_FATTR_ATIME)
|
||||
memcpy(&inode->i_atime, &fattr->atime, sizeof(inode->i_atime));
|
||||
else if (server->caps & NFS_CAP_ATIME)
|
||||
invalid |= save_cache_validity & (NFS_INO_INVALID_ATIME
|
||||
| NFS_INO_REVAL_FORCED);
|
||||
|
||||
if (fattr->valid & NFS_ATTR_FATTR_MODE) {
|
||||
if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)) {
|
||||
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
|
||||
inode->i_mode = fattr->mode;
|
||||
}
|
||||
}
|
||||
} else if (server->caps & NFS_CAP_MODE)
|
||||
invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_ACCESS
|
||||
| NFS_INO_INVALID_ACL
|
||||
| NFS_INO_REVAL_FORCED);
|
||||
|
||||
if (fattr->valid & NFS_ATTR_FATTR_OWNER) {
|
||||
if (inode->i_uid != fattr->uid) {
|
||||
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
|
||||
inode->i_uid = fattr->uid;
|
||||
}
|
||||
}
|
||||
} else if (server->caps & NFS_CAP_OWNER)
|
||||
invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_ACCESS
|
||||
| NFS_INO_INVALID_ACL
|
||||
| NFS_INO_REVAL_FORCED);
|
||||
|
||||
if (fattr->valid & NFS_ATTR_FATTR_GROUP) {
|
||||
if (inode->i_gid != fattr->gid) {
|
||||
invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
|
||||
inode->i_gid = fattr->gid;
|
||||
}
|
||||
}
|
||||
} else if (server->caps & NFS_CAP_OWNER_GROUP)
|
||||
invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_INVALID_ACCESS
|
||||
| NFS_INO_INVALID_ACL
|
||||
| NFS_INO_REVAL_FORCED);
|
||||
|
||||
if (fattr->valid & NFS_ATTR_FATTR_NLINK) {
|
||||
if (inode->i_nlink != fattr->nlink) {
|
||||
@@ -1263,7 +1327,9 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
||||
invalid |= NFS_INO_INVALID_DATA;
|
||||
inode->i_nlink = fattr->nlink;
|
||||
}
|
||||
}
|
||||
} else if (server->caps & NFS_CAP_NLINK)
|
||||
invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
|
||||
| NFS_INO_REVAL_FORCED);
|
||||
|
||||
if (fattr->valid & NFS_ATTR_FATTR_SPACE_USED) {
|
||||
/*
|
||||
@@ -1293,9 +1359,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
|
||||
|| S_ISLNK(inode->i_mode)))
|
||||
invalid &= ~NFS_INO_INVALID_DATA;
|
||||
if (!nfs_have_delegation(inode, FMODE_READ) ||
|
||||
(nfsi->cache_validity & NFS_INO_REVAL_FORCED))
|
||||
(save_cache_validity & NFS_INO_REVAL_FORCED))
|
||||
nfsi->cache_validity |= invalid;
|
||||
nfsi->cache_validity &= ~NFS_INO_REVAL_FORCED;
|
||||
|
||||
return 0;
|
||||
out_changed:
|
||||
@@ -1442,6 +1507,10 @@ static int __init init_nfs_fs(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = nfs_dns_resolver_init();
|
||||
if (err < 0)
|
||||
goto out8;
|
||||
|
||||
err = nfs_fscache_register();
|
||||
if (err < 0)
|
||||
goto out7;
|
||||
@@ -1500,6 +1569,8 @@ out5:
|
||||
out6:
|
||||
nfs_fscache_unregister();
|
||||
out7:
|
||||
nfs_dns_resolver_destroy();
|
||||
out8:
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1511,6 +1582,7 @@ static void __exit exit_nfs_fs(void)
|
||||
nfs_destroy_inodecache();
|
||||
nfs_destroy_nfspagecache();
|
||||
nfs_fscache_unregister();
|
||||
nfs_dns_resolver_destroy();
|
||||
#ifdef CONFIG_PROC_FS
|
||||
rpc_proc_unregister("nfs");
|
||||
#endif
|
||||
|
||||
+15
-24
@@ -48,6 +48,11 @@ struct nfs_clone_mount {
|
||||
*/
|
||||
#define NFS_MAX_SECFLAVORS (12)
|
||||
|
||||
/*
|
||||
* Value used if the user did not specify a port value.
|
||||
*/
|
||||
#define NFS_UNSPEC_PORT (-1)
|
||||
|
||||
/*
|
||||
* In-kernel mount arguments
|
||||
*/
|
||||
@@ -63,6 +68,7 @@ struct nfs_parsed_mount_data {
|
||||
unsigned int auth_flavor_len;
|
||||
rpc_authflavor_t auth_flavors[1];
|
||||
char *client_address;
|
||||
unsigned int version;
|
||||
unsigned int minorversion;
|
||||
char *fscache_uniq;
|
||||
|
||||
@@ -71,7 +77,7 @@ struct nfs_parsed_mount_data {
|
||||
size_t addrlen;
|
||||
char *hostname;
|
||||
u32 version;
|
||||
unsigned short port;
|
||||
int port;
|
||||
unsigned short protocol;
|
||||
} mount_server;
|
||||
|
||||
@@ -80,7 +86,7 @@ struct nfs_parsed_mount_data {
|
||||
size_t addrlen;
|
||||
char *hostname;
|
||||
char *export_path;
|
||||
unsigned short port;
|
||||
int port;
|
||||
unsigned short protocol;
|
||||
} nfs_server;
|
||||
|
||||
@@ -102,6 +108,7 @@ struct nfs_mount_request {
|
||||
};
|
||||
|
||||
extern int nfs_mount(struct nfs_mount_request *info);
|
||||
extern void nfs_umount(const struct nfs_mount_request *info);
|
||||
|
||||
/* client.c */
|
||||
extern struct rpc_program nfs_program;
|
||||
@@ -213,7 +220,6 @@ void nfs_zap_acl_cache(struct inode *inode);
|
||||
extern int nfs_wait_bit_killable(void *word);
|
||||
|
||||
/* super.c */
|
||||
void nfs_parse_ip_address(char *, size_t, struct sockaddr *, size_t *);
|
||||
extern struct file_system_type nfs_xdev_fs_type;
|
||||
#ifdef CONFIG_NFS_V4
|
||||
extern struct file_system_type nfs4_xdev_fs_type;
|
||||
@@ -248,6 +254,12 @@ extern void nfs_read_prepare(struct rpc_task *task, void *calldata);
|
||||
|
||||
/* write.c */
|
||||
extern void nfs_write_prepare(struct rpc_task *task, void *calldata);
|
||||
#ifdef CONFIG_MIGRATION
|
||||
extern int nfs_migrate_page(struct address_space *,
|
||||
struct page *, struct page *);
|
||||
#else
|
||||
#define nfs_migrate_page NULL
|
||||
#endif
|
||||
|
||||
/* nfs4proc.c */
|
||||
extern int _nfs4_call_sync(struct nfs_server *server,
|
||||
@@ -368,24 +380,3 @@ unsigned int nfs_page_array_len(unsigned int base, size_t len)
|
||||
return ((unsigned long)len + (unsigned long)base +
|
||||
PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
#define IPV6_SCOPE_DELIMITER '%'
|
||||
|
||||
/*
|
||||
* Set the port number in an address. Be agnostic about the address
|
||||
* family.
|
||||
*/
|
||||
static inline void nfs_set_port(struct sockaddr *sap, unsigned short port)
|
||||
{
|
||||
struct sockaddr_in *ap = (struct sockaddr_in *)sap;
|
||||
struct sockaddr_in6 *ap6 = (struct sockaddr_in6 *)sap;
|
||||
|
||||
switch (sap->sa_family) {
|
||||
case AF_INET:
|
||||
ap->sin_port = htons(port);
|
||||
break;
|
||||
case AF_INET6:
|
||||
ap6->sin6_port = htons(port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+81
-2
@@ -209,6 +209,71 @@ out_mnt_err:
|
||||
goto out;
|
||||
}
|
||||
|
||||
/**
|
||||
* nfs_umount - Notify a server that we have unmounted this export
|
||||
* @info: pointer to umount request arguments
|
||||
*
|
||||
* MOUNTPROC_UMNT is advisory, so we set a short timeout, and always
|
||||
* use UDP.
|
||||
*/
|
||||
void nfs_umount(const struct nfs_mount_request *info)
|
||||
{
|
||||
static const struct rpc_timeout nfs_umnt_timeout = {
|
||||
.to_initval = 1 * HZ,
|
||||
.to_maxval = 3 * HZ,
|
||||
.to_retries = 2,
|
||||
};
|
||||
struct rpc_create_args args = {
|
||||
.protocol = IPPROTO_UDP,
|
||||
.address = info->sap,
|
||||
.addrsize = info->salen,
|
||||
.timeout = &nfs_umnt_timeout,
|
||||
.servername = info->hostname,
|
||||
.program = &mnt_program,
|
||||
.version = info->version,
|
||||
.authflavor = RPC_AUTH_UNIX,
|
||||
.flags = RPC_CLNT_CREATE_NOPING,
|
||||
};
|
||||
struct mountres result;
|
||||
struct rpc_message msg = {
|
||||
.rpc_argp = info->dirpath,
|
||||
.rpc_resp = &result,
|
||||
};
|
||||
struct rpc_clnt *clnt;
|
||||
int status;
|
||||
|
||||
if (info->noresvport)
|
||||
args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
|
||||
|
||||
clnt = rpc_create(&args);
|
||||
if (unlikely(IS_ERR(clnt)))
|
||||
goto out_clnt_err;
|
||||
|
||||
dprintk("NFS: sending UMNT request for %s:%s\n",
|
||||
(info->hostname ? info->hostname : "server"), info->dirpath);
|
||||
|
||||
if (info->version == NFS_MNT3_VERSION)
|
||||
msg.rpc_proc = &clnt->cl_procinfo[MOUNTPROC3_UMNT];
|
||||
else
|
||||
msg.rpc_proc = &clnt->cl_procinfo[MOUNTPROC_UMNT];
|
||||
|
||||
status = rpc_call_sync(clnt, &msg, 0);
|
||||
rpc_shutdown_client(clnt);
|
||||
|
||||
if (unlikely(status < 0))
|
||||
goto out_call_err;
|
||||
|
||||
return;
|
||||
|
||||
out_clnt_err:
|
||||
dprintk("NFS: failed to create UMNT RPC client, status=%ld\n",
|
||||
PTR_ERR(clnt));
|
||||
return;
|
||||
|
||||
out_call_err:
|
||||
dprintk("NFS: UMNT request failed, status=%d\n", status);
|
||||
}
|
||||
|
||||
/*
|
||||
* XDR encode/decode functions for MOUNT
|
||||
*/
|
||||
@@ -258,7 +323,7 @@ static int decode_status(struct xdr_stream *xdr, struct mountres *res)
|
||||
return -EIO;
|
||||
status = ntohl(*p);
|
||||
|
||||
for (i = 0; i <= ARRAY_SIZE(mnt_errtbl); i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(mnt_errtbl); i++) {
|
||||
if (mnt_errtbl[i].status == status) {
|
||||
res->errno = mnt_errtbl[i].errno;
|
||||
return 0;
|
||||
@@ -309,7 +374,7 @@ static int decode_fhs_status(struct xdr_stream *xdr, struct mountres *res)
|
||||
return -EIO;
|
||||
status = ntohl(*p);
|
||||
|
||||
for (i = 0; i <= ARRAY_SIZE(mnt3_errtbl); i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(mnt3_errtbl); i++) {
|
||||
if (mnt3_errtbl[i].status == status) {
|
||||
res->errno = mnt3_errtbl[i].errno;
|
||||
return 0;
|
||||
@@ -407,6 +472,13 @@ static struct rpc_procinfo mnt_procedures[] = {
|
||||
.p_statidx = MOUNTPROC_MNT,
|
||||
.p_name = "MOUNT",
|
||||
},
|
||||
[MOUNTPROC_UMNT] = {
|
||||
.p_proc = MOUNTPROC_UMNT,
|
||||
.p_encode = (kxdrproc_t)mnt_enc_dirpath,
|
||||
.p_arglen = MNT_enc_dirpath_sz,
|
||||
.p_statidx = MOUNTPROC_UMNT,
|
||||
.p_name = "UMOUNT",
|
||||
},
|
||||
};
|
||||
|
||||
static struct rpc_procinfo mnt3_procedures[] = {
|
||||
@@ -419,6 +491,13 @@ static struct rpc_procinfo mnt3_procedures[] = {
|
||||
.p_statidx = MOUNTPROC3_MNT,
|
||||
.p_name = "MOUNT",
|
||||
},
|
||||
[MOUNTPROC3_UMNT] = {
|
||||
.p_proc = MOUNTPROC3_UMNT,
|
||||
.p_encode = (kxdrproc_t)mnt_enc_dirpath,
|
||||
.p_arglen = MNT_enc_dirpath_sz,
|
||||
.p_statidx = MOUNTPROC3_UMNT,
|
||||
.p_name = "UMOUNT",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -299,7 +299,6 @@ static void nfs3_free_createdata(struct nfs3_createdata *data)
|
||||
|
||||
/*
|
||||
* Create a regular file.
|
||||
* For now, we don't implement O_EXCL.
|
||||
*/
|
||||
static int
|
||||
nfs3_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
|
||||
|
||||
+20
-4
@@ -17,6 +17,7 @@
|
||||
#include <linux/inet.h>
|
||||
#include "internal.h"
|
||||
#include "nfs4_fs.h"
|
||||
#include "dns_resolve.h"
|
||||
|
||||
#define NFSDBG_FACILITY NFSDBG_VFS
|
||||
|
||||
@@ -95,6 +96,20 @@ static int nfs4_validate_fspath(const struct vfsmount *mnt_parent,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t nfs_parse_server_name(char *string, size_t len,
|
||||
struct sockaddr *sa, size_t salen)
|
||||
{
|
||||
ssize_t ret;
|
||||
|
||||
ret = rpc_pton(string, len, sa, salen);
|
||||
if (ret == 0) {
|
||||
ret = nfs_dns_resolve_name(string, len, sa, salen);
|
||||
if (ret < 0)
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
|
||||
char *page, char *page2,
|
||||
const struct nfs4_fs_location *location)
|
||||
@@ -121,11 +136,12 @@ static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
|
||||
|
||||
if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
|
||||
continue;
|
||||
nfs_parse_ip_address(buf->data, buf->len,
|
||||
mountdata->addr, &mountdata->addrlen);
|
||||
if (mountdata->addr->sa_family == AF_UNSPEC)
|
||||
mountdata->addrlen = nfs_parse_server_name(buf->data,
|
||||
buf->len,
|
||||
mountdata->addr, mountdata->addrlen);
|
||||
if (mountdata->addrlen == 0)
|
||||
continue;
|
||||
nfs_set_port(mountdata->addr, NFS_PORT);
|
||||
rpc_set_port(mountdata->addr, NFS_PORT);
|
||||
|
||||
memcpy(page2, buf->data, buf->len);
|
||||
page2[buf->len] = '\0';
|
||||
|
||||
+34
-6
@@ -61,6 +61,8 @@
|
||||
#define NFS4_POLL_RETRY_MIN (HZ/10)
|
||||
#define NFS4_POLL_RETRY_MAX (15*HZ)
|
||||
|
||||
#define NFS4_MAX_LOOP_ON_RECOVER (10)
|
||||
|
||||
struct nfs4_opendata;
|
||||
static int _nfs4_proc_open(struct nfs4_opendata *data);
|
||||
static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
|
||||
@@ -426,17 +428,19 @@ out:
|
||||
static int nfs4_recover_session(struct nfs4_session *session)
|
||||
{
|
||||
struct nfs_client *clp = session->clp;
|
||||
unsigned int loop;
|
||||
int ret;
|
||||
|
||||
for (;;) {
|
||||
for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
|
||||
ret = nfs4_wait_clnt_recover(clp);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
break;
|
||||
if (!test_bit(NFS4CLNT_SESSION_SETUP, &clp->cl_state))
|
||||
break;
|
||||
nfs4_schedule_state_manager(clp);
|
||||
ret = -EIO;
|
||||
}
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nfs41_setup_sequence(struct nfs4_session *session,
|
||||
@@ -1444,18 +1448,20 @@ static int _nfs4_proc_open(struct nfs4_opendata *data)
|
||||
static int nfs4_recover_expired_lease(struct nfs_server *server)
|
||||
{
|
||||
struct nfs_client *clp = server->nfs_client;
|
||||
unsigned int loop;
|
||||
int ret;
|
||||
|
||||
for (;;) {
|
||||
for (loop = NFS4_MAX_LOOP_ON_RECOVER; loop != 0; loop--) {
|
||||
ret = nfs4_wait_clnt_recover(clp);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
break;
|
||||
if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) &&
|
||||
!test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state))
|
||||
break;
|
||||
nfs4_schedule_state_recovery(clp);
|
||||
ret = -EIO;
|
||||
}
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1997,12 +2003,34 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
|
||||
status = nfs4_call_sync(server, &msg, &args, &res, 0);
|
||||
if (status == 0) {
|
||||
memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));
|
||||
server->caps &= ~(NFS_CAP_ACLS|NFS_CAP_HARDLINKS|
|
||||
NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
|
||||
NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|
|
||||
NFS_CAP_OWNER_GROUP|NFS_CAP_ATIME|
|
||||
NFS_CAP_CTIME|NFS_CAP_MTIME);
|
||||
if (res.attr_bitmask[0] & FATTR4_WORD0_ACL)
|
||||
server->caps |= NFS_CAP_ACLS;
|
||||
if (res.has_links != 0)
|
||||
server->caps |= NFS_CAP_HARDLINKS;
|
||||
if (res.has_symlinks != 0)
|
||||
server->caps |= NFS_CAP_SYMLINKS;
|
||||
if (res.attr_bitmask[0] & FATTR4_WORD0_FILEID)
|
||||
server->caps |= NFS_CAP_FILEID;
|
||||
if (res.attr_bitmask[1] & FATTR4_WORD1_MODE)
|
||||
server->caps |= NFS_CAP_MODE;
|
||||
if (res.attr_bitmask[1] & FATTR4_WORD1_NUMLINKS)
|
||||
server->caps |= NFS_CAP_NLINK;
|
||||
if (res.attr_bitmask[1] & FATTR4_WORD1_OWNER)
|
||||
server->caps |= NFS_CAP_OWNER;
|
||||
if (res.attr_bitmask[1] & FATTR4_WORD1_OWNER_GROUP)
|
||||
server->caps |= NFS_CAP_OWNER_GROUP;
|
||||
if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_ACCESS)
|
||||
server->caps |= NFS_CAP_ATIME;
|
||||
if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_METADATA)
|
||||
server->caps |= NFS_CAP_CTIME;
|
||||
if (res.attr_bitmask[1] & FATTR4_WORD1_TIME_MODIFY)
|
||||
server->caps |= NFS_CAP_MTIME;
|
||||
|
||||
memcpy(server->cache_consistency_bitmask, res.attr_bitmask, sizeof(server->cache_consistency_bitmask));
|
||||
server->cache_consistency_bitmask[0] &= FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE;
|
||||
server->cache_consistency_bitmask[1] &= FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user