Merge git://git.linux-nfs.org/projects/trondmy/nfs-2.6

* git://git.linux-nfs.org/projects/trondmy/nfs-2.6: (87 commits)
  NFSv4: Disallow 'mount -t nfs4 -overs=2' and 'mount -t nfs4 -overs=3'
  NFS: Allow the "nfs" file system type to support NFSv4
  NFS: Move details of nfs4_get_sb() to a helper
  NFS: Refactor NFSv4 text-based mount option validation
  NFS: Mount option parser should detect missing "port="
  NFS: out of date comment regarding O_EXCL above nfs3_proc_create()
  NFS: Handle a zero-length auth flavor list
  SUNRPC: Ensure that sunrpc gets initialised before nfs, lockd, etc...
  nfs: fix compile error in rpc_pipefs.h
  nfs: Remove reference to generic_osync_inode from a comment
  SUNRPC: cache must take a reference to the cache detail's module on open()
  NFS: Use the DNS resolver in the mount code.
  NFS: Add a dns resolver for use with NFSv4 referrals and migration
  SUNRPC: Fix a typo in cache_pipefs_files
  nfs: nfs4xdr: optimize low level decoding
  nfs: nfs4xdr: get rid of READ_BUF
  nfs: nfs4xdr: simplify decode_exchange_id by reusing decode_opaque_inline
  nfs: nfs4xdr: get rid of COPYMEM
  nfs: nfs4xdr: introduce decode_sessionid helper
  nfs: nfs4xdr: introduce decode_verifier helper
  ...
This commit is contained in:
Linus Torvalds
2009-09-11 16:39:11 -07:00
48 changed files with 4090 additions and 1831 deletions

View File

@@ -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;

View File

@@ -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';

View File

@@ -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

140
fs/nfs/cache_lib.c Normal file
View File

@@ -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();
}

27
fs/nfs/cache_lib.h Normal file
View File

@@ -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);

View File

@@ -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.

View File

@@ -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, */

View File

@@ -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

335
fs/nfs/dns_resolve.c Normal file
View File

@@ -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);
}

14
fs/nfs/dns_resolve.h Normal file
View File

@@ -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

View File

@@ -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,
};

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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",
},
};

View File

@@ -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,

View File

@@ -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';

View File

@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -73,7 +73,7 @@ enum {
Opt_cto, Opt_nocto,
Opt_ac, Opt_noac,
Opt_lock, Opt_nolock,
Opt_v2, Opt_v3,
Opt_v2, Opt_v3, Opt_v4,
Opt_udp, Opt_tcp, Opt_rdma,
Opt_acl, Opt_noacl,
Opt_rdirplus, Opt_nordirplus,
@@ -127,6 +127,7 @@ static const match_table_t nfs_mount_option_tokens = {
{ Opt_nolock, "nolock" },
{ Opt_v2, "v2" },
{ Opt_v3, "v3" },
{ Opt_v4, "v4" },
{ Opt_udp, "udp" },
{ Opt_tcp, "tcp" },
{ Opt_rdma, "rdma" },
@@ -158,7 +159,7 @@ static const match_table_t nfs_mount_option_tokens = {
{ Opt_mountvers, "mountvers=%s" },
{ Opt_nfsvers, "nfsvers=%s" },
{ Opt_nfsvers, "vers=%s" },
{ Opt_minorversion, "minorversion=%u" },
{ Opt_minorversion, "minorversion=%s" },
{ Opt_sec, "sec=%s" },
{ Opt_proto, "proto=%s" },
@@ -272,6 +273,10 @@ static const struct super_operations nfs_sops = {
};
#ifdef CONFIG_NFS_V4
static int nfs4_validate_text_mount_data(void *options,
struct nfs_parsed_mount_data *args, const char *dev_name);
static int nfs4_try_mount(int flags, const char *dev_name,
struct nfs_parsed_mount_data *data, struct vfsmount *mnt);
static int nfs4_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt);
static int nfs4_remote_get_sb(struct file_system_type *fs_type,
@@ -742,127 +747,23 @@ static int nfs_verify_server_address(struct sockaddr *addr)
}
}
dfprintk(MOUNT, "NFS: Invalid IP address specified\n");
return 0;
}
static void nfs_parse_ipv4_address(char *string, size_t str_len,
struct sockaddr *sap, size_t *addr_len)
{
struct sockaddr_in *sin = (struct sockaddr_in *)sap;
u8 *addr = (u8 *)&sin->sin_addr.s_addr;
if (str_len <= INET_ADDRSTRLEN) {
dfprintk(MOUNT, "NFS: parsing IPv4 address %*s\n",
(int)str_len, string);
sin->sin_family = AF_INET;
*addr_len = sizeof(*sin);
if (in4_pton(string, str_len, addr, '\0', NULL))
return;
}
sap->sa_family = AF_UNSPEC;
*addr_len = 0;
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
static int nfs_parse_ipv6_scope_id(const char *string, const size_t str_len,
const char *delim,
struct sockaddr_in6 *sin6)
{
char *p;
size_t len;
if ((string + str_len) == delim)
return 1;
if (*delim != IPV6_SCOPE_DELIMITER)
return 0;
if (!(ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL))
return 0;
len = (string + str_len) - delim - 1;
p = kstrndup(delim + 1, len, GFP_KERNEL);
if (p) {
unsigned long scope_id = 0;
struct net_device *dev;
dev = dev_get_by_name(&init_net, p);
if (dev != NULL) {
scope_id = dev->ifindex;
dev_put(dev);
} else {
if (strict_strtoul(p, 10, &scope_id) == 0) {
kfree(p);
return 0;
}
}
kfree(p);
sin6->sin6_scope_id = scope_id;
dfprintk(MOUNT, "NFS: IPv6 scope ID = %lu\n", scope_id);
return 1;
}
return 0;
}
static void nfs_parse_ipv6_address(char *string, size_t str_len,
struct sockaddr *sap, size_t *addr_len)
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
u8 *addr = (u8 *)&sin6->sin6_addr.in6_u;
const char *delim;
if (str_len <= INET6_ADDRSTRLEN) {
dfprintk(MOUNT, "NFS: parsing IPv6 address %*s\n",
(int)str_len, string);
sin6->sin6_family = AF_INET6;
*addr_len = sizeof(*sin6);
if (in6_pton(string, str_len, addr,
IPV6_SCOPE_DELIMITER, &delim) != 0) {
if (nfs_parse_ipv6_scope_id(string, str_len,
delim, sin6) != 0)
return;
}
}
sap->sa_family = AF_UNSPEC;
*addr_len = 0;
}
#else
static void nfs_parse_ipv6_address(char *string, size_t str_len,
struct sockaddr *sap, size_t *addr_len)
{
sap->sa_family = AF_UNSPEC;
*addr_len = 0;
}
#endif
/*
* Construct a sockaddr based on the contents of a string that contains
* an IP address in presentation format.
*
* If there is a problem constructing the new sockaddr, set the address
* family to AF_UNSPEC.
* Select between a default port value and a user-specified port value.
* If a zero value is set, then autobind will be used.
*/
void nfs_parse_ip_address(char *string, size_t str_len,
struct sockaddr *sap, size_t *addr_len)
static void nfs_set_default_port(struct sockaddr *sap, const int parsed_port,
const unsigned short default_port)
{
unsigned int i, colons;
unsigned short port = default_port;
colons = 0;
for (i = 0; i < str_len; i++)
if (string[i] == ':')
colons++;
if (parsed_port != NFS_UNSPEC_PORT)
port = parsed_port;
if (colons >= 2)
nfs_parse_ipv6_address(string, str_len, sap, addr_len);
else
nfs_parse_ipv4_address(string, str_len, sap, addr_len);
rpc_set_port(sap, port);
}
/*
@@ -904,8 +805,6 @@ static void nfs_set_mount_transport_protocol(struct nfs_parsed_mount_data *mnt)
/*
* Parse the value of the 'sec=' option.
*
* The flavor_len setting is for v4 mounts.
*/
static int nfs_parse_security_flavors(char *value,
struct nfs_parsed_mount_data *mnt)
@@ -916,53 +815,43 @@ static int nfs_parse_security_flavors(char *value,
switch (match_token(value, nfs_secflavor_tokens, args)) {
case Opt_sec_none:
mnt->auth_flavor_len = 0;
mnt->auth_flavors[0] = RPC_AUTH_NULL;
break;
case Opt_sec_sys:
mnt->auth_flavor_len = 0;
mnt->auth_flavors[0] = RPC_AUTH_UNIX;
break;
case Opt_sec_krb5:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5;
break;
case Opt_sec_krb5i:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5I;
break;
case Opt_sec_krb5p:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_KRB5P;
break;
case Opt_sec_lkey:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEY;
break;
case Opt_sec_lkeyi:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYI;
break;
case Opt_sec_lkeyp:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_LKEYP;
break;
case Opt_sec_spkm:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKM;
break;
case Opt_sec_spkmi:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMI;
break;
case Opt_sec_spkmp:
mnt->auth_flavor_len = 1;
mnt->auth_flavors[0] = RPC_AUTH_GSS_SPKMP;
break;
default:
return 0;
}
mnt->auth_flavor_len = 1;
return 1;
}
@@ -1001,7 +890,6 @@ static int nfs_parse_mount_options(char *raw,
while ((p = strsep(&raw, ",")) != NULL) {
substring_t args[MAX_OPT_ARGS];
unsigned long option;
int int_option;
int token;
if (!*p)
@@ -1047,10 +935,18 @@ static int nfs_parse_mount_options(char *raw,
break;
case Opt_v2:
mnt->flags &= ~NFS_MOUNT_VER3;
mnt->version = 2;
break;
case Opt_v3:
mnt->flags |= NFS_MOUNT_VER3;
mnt->version = 3;
break;
#ifdef CONFIG_NFS_V4
case Opt_v4:
mnt->flags &= ~NFS_MOUNT_VER3;
mnt->version = 4;
break;
#endif
case Opt_udp:
mnt->flags &= ~NFS_MOUNT_TCP;
mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP;
@@ -1264,20 +1160,33 @@ static int nfs_parse_mount_options(char *raw,
switch (option) {
case NFS2_VERSION:
mnt->flags &= ~NFS_MOUNT_VER3;
mnt->version = 2;
break;
case NFS3_VERSION:
mnt->flags |= NFS_MOUNT_VER3;
mnt->version = 3;
break;
#ifdef CONFIG_NFS_V4
case NFS4_VERSION:
mnt->flags &= ~NFS_MOUNT_VER3;
mnt->version = 4;
break;
#endif
default:
goto out_invalid_value;
}
break;
case Opt_minorversion:
if (match_int(args, &int_option))
return 0;
if (int_option < 0 || int_option > NFS4_MAX_MINOR_VERSION)
return 0;
mnt->minorversion = int_option;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
rc = strict_strtoul(string, 10, &option);
kfree(string);
if (rc != 0)
goto out_invalid_value;
if (option > NFS4_MAX_MINOR_VERSION)
goto out_invalid_value;
mnt->minorversion = option;
break;
/*
@@ -1352,11 +1261,14 @@ static int nfs_parse_mount_options(char *raw,
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
nfs_parse_ip_address(string, strlen(string),
(struct sockaddr *)
&mnt->nfs_server.address,
&mnt->nfs_server.addrlen);
mnt->nfs_server.addrlen =
rpc_pton(string, strlen(string),
(struct sockaddr *)
&mnt->nfs_server.address,
sizeof(mnt->nfs_server.address));
kfree(string);
if (mnt->nfs_server.addrlen == 0)
goto out_invalid_address;
break;
case Opt_clientaddr:
string = match_strdup(args);
@@ -1376,11 +1288,14 @@ static int nfs_parse_mount_options(char *raw,
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
nfs_parse_ip_address(string, strlen(string),
(struct sockaddr *)
&mnt->mount_server.address,
&mnt->mount_server.addrlen);
mnt->mount_server.addrlen =
rpc_pton(string, strlen(string),
(struct sockaddr *)
&mnt->mount_server.address,
sizeof(mnt->mount_server.address));
kfree(string);
if (mnt->mount_server.addrlen == 0)
goto out_invalid_address;
break;
case Opt_lookupcache:
string = match_strdup(args);
@@ -1432,8 +1347,11 @@ static int nfs_parse_mount_options(char *raw,
return 1;
out_invalid_address:
printk(KERN_INFO "NFS: bad IP address specified: %s\n", p);
return 0;
out_invalid_value:
printk(KERN_INFO "NFS: bad mount option value specified: %s \n", p);
printk(KERN_INFO "NFS: bad mount option value specified: %s\n", p);
return 0;
out_nomem:
printk(KERN_INFO "NFS: not enough memory to parse option\n");
@@ -1444,6 +1362,52 @@ out_security_failure:
return 0;
}
/*
* Match the requested auth flavors with the list returned by
* the server. Returns zero and sets the mount's authentication
* flavor on success; returns -EACCES if server does not support
* the requested flavor.
*/
static int nfs_walk_authlist(struct nfs_parsed_mount_data *args,
struct nfs_mount_request *request)
{
unsigned int i, j, server_authlist_len = *(request->auth_flav_len);
/*
* Certain releases of Linux's mountd return an empty
* flavor list. To prevent behavioral regression with
* these servers (ie. rejecting mounts that used to
* succeed), revert to pre-2.6.32 behavior (no checking)
* if the returned flavor list is empty.
*/
if (server_authlist_len == 0)
return 0;
/*
* We avoid sophisticated negotiating here, as there are
* plenty of cases where we can get it wrong, providing
* either too little or too much security.
*
* RFC 2623, section 2.7 suggests we SHOULD prefer the
* flavor listed first. However, some servers list
* AUTH_NULL first. Our caller plants AUTH_SYS, the
* preferred default, in args->auth_flavors[0] if user
* didn't specify sec= mount option.
*/
for (i = 0; i < args->auth_flavor_len; i++)
for (j = 0; j < server_authlist_len; j++)
if (args->auth_flavors[i] == request->auth_flavs[j]) {
dfprintk(MOUNT, "NFS: using auth flavor %d\n",
request->auth_flavs[j]);
args->auth_flavors[0] = request->auth_flavs[j];
return 0;
}
dfprintk(MOUNT, "NFS: server does not support requested auth flavor\n");
nfs_umount(request);
return -EACCES;
}
/*
* Use the remote server's MOUNT service to request the NFS file handle
* corresponding to the provided path.
@@ -1451,7 +1415,8 @@ out_security_failure:
static int nfs_try_mount(struct nfs_parsed_mount_data *args,
struct nfs_fh *root_fh)
{
unsigned int auth_flavor_len = 0;
rpc_authflavor_t server_authlist[NFS_MAX_SECFLAVORS];
unsigned int server_authlist_len = ARRAY_SIZE(server_authlist);
struct nfs_mount_request request = {
.sap = (struct sockaddr *)
&args->mount_server.address,
@@ -1459,7 +1424,8 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
.protocol = args->mount_server.protocol,
.fh = root_fh,
.noresvport = args->flags & NFS_MOUNT_NORESVPORT,
.auth_flav_len = &auth_flavor_len,
.auth_flav_len = &server_authlist_len,
.auth_flavs = server_authlist,
};
int status;
@@ -1485,23 +1451,25 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
args->mount_server.addrlen = args->nfs_server.addrlen;
}
request.salen = args->mount_server.addrlen;
/*
* autobind will be used if mount_server.port == 0
*/
nfs_set_port(request.sap, args->mount_server.port);
nfs_set_default_port(request.sap, args->mount_server.port, 0);
/*
* Now ask the mount server to map our export path
* to a file handle.
*/
status = nfs_mount(&request);
if (status == 0)
return 0;
if (status != 0) {
dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
request.hostname, status);
return status;
}
dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
request.hostname, status);
return status;
/*
* MNTv1 (NFSv2) does not support auth flavor negotiation.
*/
if (args->mount_server.version != NFS_MNT3_VERSION)
return 0;
return nfs_walk_authlist(args, &request);
}
static int nfs_parse_simple_hostname(const char *dev_name,
@@ -1661,6 +1629,7 @@ static int nfs_validate_mount_data(void *options,
const char *dev_name)
{
struct nfs_mount_data *data = (struct nfs_mount_data *)options;
struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
if (data == NULL)
goto out_no_data;
@@ -1672,10 +1641,12 @@ static int nfs_validate_mount_data(void *options,
args->acregmax = NFS_DEF_ACREGMAX;
args->acdirmin = NFS_DEF_ACDIRMIN;
args->acdirmax = NFS_DEF_ACDIRMAX;
args->mount_server.port = 0; /* autobind unless user sets port */
args->nfs_server.port = 0; /* autobind unless user sets port */
args->mount_server.port = NFS_UNSPEC_PORT;
args->nfs_server.port = NFS_UNSPEC_PORT;
args->nfs_server.protocol = XPRT_TRANSPORT_TCP;
args->auth_flavors[0] = RPC_AUTH_UNIX;
args->auth_flavor_len = 1;
args->minorversion = 0;
switch (data->version) {
case 1:
@@ -1697,8 +1668,11 @@ static int nfs_validate_mount_data(void *options,
if (data->root.size > NFS3_FHSIZE || data->root.size == 0)
goto out_invalid_fh;
mntfh->size = data->root.size;
} else
args->version = 3;
} else {
mntfh->size = NFS2_FHSIZE;
args->version = 2;
}
memcpy(mntfh->data, data->root.data, mntfh->size);
@@ -1720,11 +1694,9 @@ static int nfs_validate_mount_data(void *options,
args->acdirmin = data->acdirmin;
args->acdirmax = data->acdirmax;
memcpy(&args->nfs_server.address, &data->addr,
sizeof(data->addr));
memcpy(sap, &data->addr, sizeof(data->addr));
args->nfs_server.addrlen = sizeof(data->addr);
if (!nfs_verify_server_address((struct sockaddr *)
&args->nfs_server.address))
if (!nfs_verify_server_address(sap))
goto out_no_address;
if (!(data->flags & NFS_MOUNT_TCP))
@@ -1772,12 +1744,18 @@ static int nfs_validate_mount_data(void *options,
if (nfs_parse_mount_options((char *)options, args) == 0)
return -EINVAL;
if (!nfs_verify_server_address((struct sockaddr *)
&args->nfs_server.address))
if (!nfs_verify_server_address(sap))
goto out_no_address;
nfs_set_port((struct sockaddr *)&args->nfs_server.address,
args->nfs_server.port);
if (args->version == 4)
#ifdef CONFIG_NFS_V4
return nfs4_validate_text_mount_data(options,
args, dev_name);
#else
goto out_v4_not_compiled;
#endif
nfs_set_default_port(sap, args->nfs_server.port, 0);
nfs_set_mount_transport_protocol(args);
@@ -1825,6 +1803,12 @@ out_v3_not_compiled:
return -EPROTONOSUPPORT;
#endif /* !CONFIG_NFS_V3 */
#ifndef CONFIG_NFS_V4
out_v4_not_compiled:
dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n");
return -EPROTONOSUPPORT;
#endif /* !CONFIG_NFS_V4 */
out_nomem:
dfprintk(MOUNT, "NFS: not enough memory to handle mount options\n");
return -ENOMEM;
@@ -2120,6 +2104,14 @@ static int nfs_get_sb(struct file_system_type *fs_type,
if (error < 0)
goto out;
#ifdef CONFIG_NFS_V4
if (data->version == 4) {
error = nfs4_try_mount(flags, dev_name, data, mnt);
kfree(data->client_address);
goto out;
}
#endif /* CONFIG_NFS_V4 */
/* Get a volume representation */
server = nfs_create_server(data, mntfh);
if (IS_ERR(server)) {
@@ -2317,6 +2309,43 @@ static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args)
args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3);
}
static int nfs4_validate_text_mount_data(void *options,
struct nfs_parsed_mount_data *args,
const char *dev_name)
{
struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
nfs_set_default_port(sap, args->nfs_server.port, NFS_PORT);
nfs_validate_transport_protocol(args);
nfs4_validate_mount_flags(args);
if (args->version != 4) {
dfprintk(MOUNT,
"NFS4: Illegal mount version\n");
return -EINVAL;
}
if (args->auth_flavor_len > 1) {
dfprintk(MOUNT,
"NFS4: Too many RPC auth flavours specified\n");
return -EINVAL;
}
if (args->client_address == NULL) {
dfprintk(MOUNT,
"NFS4: mount program didn't pass callback address\n");
return -EINVAL;
}
return nfs_parse_devname(dev_name,
&args->nfs_server.hostname,
NFS4_MAXNAMLEN,
&args->nfs_server.export_path,
NFS4_MAXPATHLEN);
}
/*
* Validate NFSv4 mount options
*/
@@ -2324,7 +2353,7 @@ static int nfs4_validate_mount_data(void *options,
struct nfs_parsed_mount_data *args,
const char *dev_name)
{
struct sockaddr_in *ap;
struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
struct nfs4_mount_data *data = (struct nfs4_mount_data *)options;
char *c;
@@ -2337,23 +2366,22 @@ static int nfs4_validate_mount_data(void *options,
args->acregmax = NFS_DEF_ACREGMAX;
args->acdirmin = NFS_DEF_ACDIRMIN;
args->acdirmax = NFS_DEF_ACDIRMAX;
args->nfs_server.port = NFS_PORT; /* 2049 unless user set port= */
args->nfs_server.port = NFS_UNSPEC_PORT;
args->auth_flavors[0] = RPC_AUTH_UNIX;
args->auth_flavor_len = 0;
args->auth_flavor_len = 1;
args->version = 4;
args->minorversion = 0;
switch (data->version) {
case 1:
ap = (struct sockaddr_in *)&args->nfs_server.address;
if (data->host_addrlen > sizeof(args->nfs_server.address))
goto out_no_address;
if (data->host_addrlen == 0)
goto out_no_address;
args->nfs_server.addrlen = data->host_addrlen;
if (copy_from_user(ap, data->host_addr, data->host_addrlen))
if (copy_from_user(sap, data->host_addr, data->host_addrlen))
return -EFAULT;
if (!nfs_verify_server_address((struct sockaddr *)
&args->nfs_server.address))
if (!nfs_verify_server_address(sap))
goto out_no_address;
if (data->auth_flavourlen) {
@@ -2399,39 +2427,14 @@ static int nfs4_validate_mount_data(void *options,
nfs_validate_transport_protocol(args);
break;
default: {
int status;
default:
if (nfs_parse_mount_options((char *)options, args) == 0)
return -EINVAL;
if (!nfs_verify_server_address((struct sockaddr *)
&args->nfs_server.address))
if (!nfs_verify_server_address(sap))
return -EINVAL;
nfs_set_port((struct sockaddr *)&args->nfs_server.address,
args->nfs_server.port);
nfs_validate_transport_protocol(args);
nfs4_validate_mount_flags(args);
if (args->auth_flavor_len > 1)
goto out_inval_auth;
if (args->client_address == NULL)
goto out_no_client_address;
status = nfs_parse_devname(dev_name,
&args->nfs_server.hostname,
NFS4_MAXNAMLEN,
&args->nfs_server.export_path,
NFS4_MAXPATHLEN);
if (status < 0)
return status;
break;
}
return nfs4_validate_text_mount_data(options, args, dev_name);
}
return 0;
@@ -2448,10 +2451,6 @@ out_inval_auth:
out_no_address:
dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n");
return -EINVAL;
out_no_client_address:
dfprintk(MOUNT, "NFS4: mount program didn't pass callback address\n");
return -EINVAL;
}
/*
@@ -2618,25 +2617,15 @@ out_err:
return ret;
}
/*
* Get the superblock for an NFS4 mountpoint
*/
static int nfs4_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
static int nfs4_try_mount(int flags, const char *dev_name,
struct nfs_parsed_mount_data *data,
struct vfsmount *mnt)
{
struct nfs_parsed_mount_data *data;
char *export_path;
struct vfsmount *root_mnt;
int error = -ENOMEM;
int error;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data == NULL)
goto out_free_data;
/* Validate the mount data */
error = nfs4_validate_mount_data(raw_data, data, dev_name);
if (error < 0)
goto out;
dfprintk(MOUNT, "--> nfs4_try_mount()\n");
export_path = data->nfs_server.export_path;
data->nfs_server.export_path = "/";
@@ -2650,6 +2639,32 @@ static int nfs4_get_sb(struct file_system_type *fs_type,
error = nfs_follow_remote_path(root_mnt, export_path, mnt);
out:
dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n", error,
error != 0 ? " [error]" : "");
return error;
}
/*
* Get the superblock for an NFS4 mountpoint
*/
static int nfs4_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
{
struct nfs_parsed_mount_data *data;
int error = -ENOMEM;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (data == NULL)
goto out_free_data;
/* Validate the mount data */
error = nfs4_validate_mount_data(raw_data, data, dev_name);
if (error < 0)
goto out;
error = nfs4_try_mount(flags, dev_name, data, mnt);
out:
kfree(data->client_address);
kfree(data->nfs_server.export_path);

View File

@@ -13,6 +13,7 @@
#include <linux/file.h>
#include <linux/writeback.h>
#include <linux/swap.h>
#include <linux/migrate.h>
#include <linux/sunrpc/clnt.h>
#include <linux/nfs_fs.h>
@@ -26,6 +27,7 @@
#include "internal.h"
#include "iostat.h"
#include "nfs4_fs.h"
#include "fscache.h"
#define NFSDBG_FACILITY NFSDBG_PAGECACHE
@@ -218,24 +220,17 @@ static void nfs_end_page_writeback(struct page *page)
clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC);
}
/*
* Find an associated nfs write request, and prepare to flush it out
* May return an error if the user signalled nfs_wait_on_request().
*/
static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
struct page *page)
static struct nfs_page *nfs_find_and_lock_request(struct page *page)
{
struct inode *inode = page->mapping->host;
struct nfs_page *req;
int ret;
spin_lock(&inode->i_lock);
for(;;) {
for (;;) {
req = nfs_page_find_request_locked(page);
if (req == NULL) {
spin_unlock(&inode->i_lock);
return 0;
}
if (req == NULL)
break;
if (nfs_set_page_tag_locked(req))
break;
/* Note: If we hold the page lock, as is the case in nfs_writepage,
@@ -247,23 +242,40 @@ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
ret = nfs_wait_on_request(req);
nfs_release_request(req);
if (ret != 0)
return ret;
return ERR_PTR(ret);
spin_lock(&inode->i_lock);
}
if (test_bit(PG_CLEAN, &req->wb_flags)) {
spin_unlock(&inode->i_lock);
BUG();
}
if (nfs_set_page_writeback(page) != 0) {
spin_unlock(&inode->i_lock);
BUG();
}
spin_unlock(&inode->i_lock);
return req;
}
/*
* Find an associated nfs write request, and prepare to flush it out
* May return an error if the user signalled nfs_wait_on_request().
*/
static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio,
struct page *page)
{
struct nfs_page *req;
int ret = 0;
req = nfs_find_and_lock_request(page);
if (!req)
goto out;
ret = PTR_ERR(req);
if (IS_ERR(req))
goto out;
ret = nfs_set_page_writeback(page);
BUG_ON(ret != 0);
BUG_ON(test_bit(PG_CLEAN, &req->wb_flags));
if (!nfs_pageio_add_request(pgio, req)) {
nfs_redirty_request(req);
return pgio->pg_error;
ret = pgio->pg_error;
}
return 0;
out:
return ret;
}
static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, struct nfs_pageio_descriptor *pgio)
@@ -1580,6 +1592,41 @@ int nfs_wb_page(struct inode *inode, struct page* page)
return nfs_wb_page_priority(inode, page, FLUSH_STABLE);
}
#ifdef CONFIG_MIGRATION
int nfs_migrate_page(struct address_space *mapping, struct page *newpage,
struct page *page)
{
struct nfs_page *req;
int ret;
if (PageFsCache(page))
nfs_fscache_release_page(page, GFP_KERNEL);
req = nfs_find_and_lock_request(page);
ret = PTR_ERR(req);
if (IS_ERR(req))
goto out;
ret = migrate_page(mapping, newpage, page);
if (!req)
goto out;
if (ret)
goto out_unlock;
page_cache_get(newpage);
req->wb_page = newpage;
SetPagePrivate(newpage);
set_page_private(newpage, page_private(page));
ClearPagePrivate(page);
set_page_private(page, 0);
page_cache_release(page);
out_unlock:
nfs_clear_page_tag_locked(req);
nfs_release_request(req);
out:
return ret;
}
#endif
int __init nfs_init_writepagecache(void)
{
nfs_wdata_cachep = kmem_cache_create("nfs_write_data",

View File

@@ -85,6 +85,11 @@ static void expkey_request(struct cache_detail *cd,
(*bpp)[-1] = '\n';
}
static int expkey_upcall(struct cache_detail *cd, struct cache_head *h)
{
return sunrpc_cache_pipe_upcall(cd, h, expkey_request);
}
static struct svc_expkey *svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old);
static struct svc_expkey *svc_expkey_lookup(struct svc_expkey *);
static struct cache_detail svc_expkey_cache;
@@ -259,7 +264,7 @@ static struct cache_detail svc_expkey_cache = {
.hash_table = expkey_table,
.name = "nfsd.fh",
.cache_put = expkey_put,
.cache_request = expkey_request,
.cache_upcall = expkey_upcall,
.cache_parse = expkey_parse,
.cache_show = expkey_show,
.match = expkey_match,
@@ -355,6 +360,11 @@ static void svc_export_request(struct cache_detail *cd,
(*bpp)[-1] = '\n';
}
static int svc_export_upcall(struct cache_detail *cd, struct cache_head *h)
{
return sunrpc_cache_pipe_upcall(cd, h, svc_export_request);
}
static struct svc_export *svc_export_update(struct svc_export *new,
struct svc_export *old);
static struct svc_export *svc_export_lookup(struct svc_export *);
@@ -724,7 +734,7 @@ struct cache_detail svc_export_cache = {
.hash_table = export_table,
.name = "nfsd.export",
.cache_put = svc_export_put,
.cache_request = svc_export_request,
.cache_upcall = svc_export_upcall,
.cache_parse = svc_export_parse,
.cache_show = svc_export_show,
.match = svc_export_match,

View File

@@ -145,6 +145,12 @@ idtoname_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
(*bpp)[-1] = '\n';
}
static int
idtoname_upcall(struct cache_detail *cd, struct cache_head *ch)
{
return sunrpc_cache_pipe_upcall(cd, ch, idtoname_request);
}
static int
idtoname_match(struct cache_head *ca, struct cache_head *cb)
{
@@ -175,10 +181,10 @@ idtoname_show(struct seq_file *m, struct cache_detail *cd, struct cache_head *h)
}
static void
warn_no_idmapd(struct cache_detail *detail)
warn_no_idmapd(struct cache_detail *detail, int has_died)
{
printk("nfsd: nfsv4 idmapping failing: has idmapd %s?\n",
detail->last_close? "died" : "not been started");
has_died ? "died" : "not been started");
}
@@ -192,7 +198,7 @@ static struct cache_detail idtoname_cache = {
.hash_table = idtoname_table,
.name = "nfs4.idtoname",
.cache_put = ent_put,
.cache_request = idtoname_request,
.cache_upcall = idtoname_upcall,
.cache_parse = idtoname_parse,
.cache_show = idtoname_show,
.warn_no_listener = warn_no_idmapd,
@@ -324,6 +330,12 @@ nametoid_request(struct cache_detail *cd, struct cache_head *ch, char **bpp,
(*bpp)[-1] = '\n';
}
static int
nametoid_upcall(struct cache_detail *cd, struct cache_head *ch)
{
return sunrpc_cache_pipe_upcall(cd, ch, nametoid_request);
}
static int
nametoid_match(struct cache_head *ca, struct cache_head *cb)
{
@@ -363,7 +375,7 @@ static struct cache_detail nametoid_cache = {
.hash_table = nametoid_table,
.name = "nfs4.nametoid",
.cache_put = ent_put,
.cache_request = nametoid_request,
.cache_upcall = nametoid_upcall,
.cache_parse = nametoid_parse,
.cache_show = nametoid_show,
.warn_no_listener = warn_no_idmapd,

View File

@@ -37,6 +37,7 @@
#include <linux/nfsd/xdr.h>
#include <linux/nfsd/syscall.h>
#include <linux/lockd/lockd.h>
#include <linux/sunrpc/clnt.h>
#include <asm/uaccess.h>
#include <net/ipv6.h>
@@ -490,22 +491,18 @@ static ssize_t write_getfd(struct file *file, char *buf, size_t size)
*
* Input:
* buf: '\n'-terminated C string containing a
* presentation format IPv4 address
* presentation format IP address
* size: length of C string in @buf
* Output:
* On success: returns zero if all specified locks were released;
* returns one if one or more locks were not released
* On error: return code is negative errno value
*
* Note: Only AF_INET client addresses are passed in
*/
static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
{
struct sockaddr_in sin = {
.sin_family = AF_INET,
};
int b1, b2, b3, b4;
char c;
struct sockaddr_storage address;
struct sockaddr *sap = (struct sockaddr *)&address;
size_t salen = sizeof(address);
char *fo_path;
/* sanity check */
@@ -519,14 +516,10 @@ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
if (qword_get(&buf, fo_path, size) < 0)
return -EINVAL;
/* get ipv4 address */
if (sscanf(fo_path, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4)
if (rpc_pton(fo_path, size, sap, salen) == 0)
return -EINVAL;
if (b1 > 255 || b2 > 255 || b3 > 255 || b4 > 255)
return -EINVAL;
sin.sin_addr.s_addr = htonl((b1 << 24) | (b2 << 16) | (b3 << 8) | b4);
return nlmsvc_unlock_all_by_ip((struct sockaddr *)&sin);
return nlmsvc_unlock_all_by_ip(sap);
}
/**