diff options
-rw-r--r-- | Documentation/filesystems/nfs.txt | 98 | ||||
-rw-r--r-- | Documentation/kernel-parameters.txt | 8 | ||||
-rw-r--r-- | fs/nfs/Makefile | 3 | ||||
-rw-r--r-- | fs/nfs/cache_lib.c | 140 | ||||
-rw-r--r-- | fs/nfs/cache_lib.h | 27 | ||||
-rw-r--r-- | fs/nfs/dns_resolve.c | 335 | ||||
-rw-r--r-- | fs/nfs/dns_resolve.h | 14 | ||||
-rw-r--r-- | fs/nfs/inode.c | 8 | ||||
-rw-r--r-- | net/sunrpc/rpc_pipe.c | 7 |
9 files changed, 639 insertions, 1 deletions
diff --git a/Documentation/filesystems/nfs.txt b/Documentation/filesystems/nfs.txt new file mode 100644 index 000000000000..f50f26ce6cd0 --- /dev/null +++ b/Documentation/filesystems/nfs.txt | |||
@@ -0,0 +1,98 @@ | |||
1 | |||
2 | The NFS client | ||
3 | ============== | ||
4 | |||
5 | The NFS version 2 protocol was first documented in RFC1094 (March 1989). | ||
6 | Since then two more major releases of NFS have been published, with NFSv3 | ||
7 | being documented in RFC1813 (June 1995), and NFSv4 in RFC3530 (April | ||
8 | 2003). | ||
9 | |||
10 | The Linux NFS client currently supports all the above published versions, | ||
11 | and work is in progress on adding support for minor version 1 of the NFSv4 | ||
12 | protocol. | ||
13 | |||
14 | The purpose of this document is to provide information on some of the | ||
15 | upcall interfaces that are used in order to provide the NFS client with | ||
16 | some of the information that it requires in order to fully comply with | ||
17 | the NFS spec. | ||
18 | |||
19 | The DNS resolver | ||
20 | ================ | ||
21 | |||
22 | NFSv4 allows for one server to refer the NFS client to data that has been | ||
23 | migrated onto another server by means of the special "fs_locations" | ||
24 | attribute. See | ||
25 | http://tools.ietf.org/html/rfc3530#section-6 | ||
26 | and | ||
27 | http://tools.ietf.org/html/draft-ietf-nfsv4-referrals-00 | ||
28 | |||
29 | The fs_locations information can take the form of either an ip address and | ||
30 | a path, or a DNS hostname and a path. The latter requires the NFS client to | ||
31 | do a DNS lookup in order to mount the new volume, and hence the need for an | ||
32 | upcall to allow userland to provide this service. | ||
33 | |||
34 | Assuming that the user has the 'rpc_pipefs' filesystem mounted in the usual | ||
35 | /var/lib/nfs/rpc_pipefs, the upcall consists of the following steps: | ||
36 | |||
37 | (1) The process checks the dns_resolve cache to see if it contains a | ||
38 | valid entry. If so, it returns that entry and exits. | ||
39 | |||
40 | (2) If no valid entry exists, the helper script '/sbin/nfs_cache_getent' | ||
41 | (may be changed using the 'nfs.cache_getent' kernel boot parameter) | ||
42 | is run, with two arguments: | ||
43 | - the cache name, "dns_resolve" | ||
44 | - the hostname to resolve | ||
45 | |||
46 | (3) After looking up the corresponding ip address, the helper script | ||
47 | writes the result into the rpc_pipefs pseudo-file | ||
48 | '/var/lib/nfs/rpc_pipefs/cache/dns_resolve/channel' | ||
49 | in the following (text) format: | ||
50 | |||
51 | "<ip address> <hostname> <ttl>\n" | ||
52 | |||
53 | Where <ip address> is in the usual IPv4 (123.456.78.90) or IPv6 | ||
54 | (ffee:ddcc:bbaa:9988:7766:5544:3322:1100, ffee::1100, ...) format. | ||
55 | <hostname> is identical to the second argument of the helper | ||
56 | script, and <ttl> is the 'time to live' of this cache entry (in | ||
57 | units of seconds). | ||
58 | |||
59 | Note: If <ip address> is invalid, say the string "0", then a negative | ||
60 | entry is created, which will cause the kernel to treat the hostname | ||
61 | as having no valid DNS translation. | ||
62 | |||
63 | |||
64 | |||
65 | |||
66 | A basic sample /sbin/nfs_cache_getent | ||
67 | ===================================== | ||
68 | |||
69 | #!/bin/bash | ||
70 | # | ||
71 | ttl=600 | ||
72 | # | ||
73 | cut=/usr/bin/cut | ||
74 | getent=/usr/bin/getent | ||
75 | rpc_pipefs=/var/lib/nfs/rpc_pipefs | ||
76 | # | ||
77 | die() | ||
78 | { | ||
79 | echo "Usage: $0 cache_name entry_name" | ||
80 | exit 1 | ||
81 | } | ||
82 | |||
83 | [ $# -lt 2 ] && die | ||
84 | cachename="$1" | ||
85 | cache_path=${rpc_pipefs}/cache/${cachename}/channel | ||
86 | |||
87 | case "${cachename}" in | ||
88 | dns_resolve) | ||
89 | name="$2" | ||
90 | result="$(${getent} hosts ${name} | ${cut} -f1 -d\ )" | ||
91 | [ -z "${result}" ] && result="0" | ||
92 | ;; | ||
93 | *) | ||
94 | die | ||
95 | ;; | ||
96 | esac | ||
97 | echo "${result} ${name} ${ttl}" >${cache_path} | ||
98 | |||
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c08813dbfce2..ce8853755814 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
@@ -1503,6 +1503,14 @@ and is between 256 and 4096 characters. It is defined in the file | |||
1503 | [NFS] set the TCP port on which the NFSv4 callback | 1503 | [NFS] set the TCP port on which the NFSv4 callback |
1504 | channel should listen. | 1504 | channel should listen. |
1505 | 1505 | ||
1506 | nfs.cache_getent= | ||
1507 | [NFS] sets the pathname to the program which is used | ||
1508 | to update the NFS client cache entries. | ||
1509 | |||
1510 | nfs.cache_getent_timeout= | ||
1511 | [NFS] sets the timeout after which an attempt to | ||
1512 | update a cache entry is deemed to have failed. | ||
1513 | |||
1506 | nfs.idmap_cache_timeout= | 1514 | nfs.idmap_cache_timeout= |
1507 | [NFS] set the maximum lifetime for idmapper cache | 1515 | [NFS] set the maximum lifetime for idmapper cache |
1508 | entries. | 1516 | entries. |
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile index 845159814de2..da7fda639eac 100644 --- a/fs/nfs/Makefile +++ b/fs/nfs/Makefile | |||
@@ -6,7 +6,8 @@ obj-$(CONFIG_NFS_FS) += nfs.o | |||
6 | 6 | ||
7 | nfs-y := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \ | 7 | nfs-y := client.o dir.o file.o getroot.o inode.o super.o nfs2xdr.o \ |
8 | direct.o pagelist.o proc.o read.o symlink.o unlink.o \ | 8 | direct.o pagelist.o proc.o read.o symlink.o unlink.o \ |
9 | write.o namespace.o mount_clnt.o | 9 | write.o namespace.o mount_clnt.o \ |
10 | dns_resolve.o cache_lib.o | ||
10 | nfs-$(CONFIG_ROOT_NFS) += nfsroot.o | 11 | nfs-$(CONFIG_ROOT_NFS) += nfsroot.o |
11 | nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o | 12 | nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o |
12 | nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o | 13 | nfs-$(CONFIG_NFS_V3_ACL) += nfs3acl.o |
diff --git a/fs/nfs/cache_lib.c b/fs/nfs/cache_lib.c new file mode 100644 index 000000000000..b4ffd0146ea6 --- /dev/null +++ b/fs/nfs/cache_lib.c | |||
@@ -0,0 +1,140 @@ | |||
1 | /* | ||
2 | * linux/fs/nfs/cache_lib.c | ||
3 | * | ||
4 | * Helper routines for the NFS client caches | ||
5 | * | ||
6 | * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com> | ||
7 | */ | ||
8 | #include <linux/kmod.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/moduleparam.h> | ||
11 | #include <linux/mount.h> | ||
12 | #include <linux/namei.h> | ||
13 | #include <linux/sunrpc/cache.h> | ||
14 | #include <linux/sunrpc/rpc_pipe_fs.h> | ||
15 | |||
16 | #include "cache_lib.h" | ||
17 | |||
18 | #define NFS_CACHE_UPCALL_PATHLEN 256 | ||
19 | #define NFS_CACHE_UPCALL_TIMEOUT 15 | ||
20 | |||
21 | static char nfs_cache_getent_prog[NFS_CACHE_UPCALL_PATHLEN] = | ||
22 | "/sbin/nfs_cache_getent"; | ||
23 | static unsigned long nfs_cache_getent_timeout = NFS_CACHE_UPCALL_TIMEOUT; | ||
24 | |||
25 | module_param_string(cache_getent, nfs_cache_getent_prog, | ||
26 | sizeof(nfs_cache_getent_prog), 0600); | ||
27 | MODULE_PARM_DESC(cache_getent, "Path to the client cache upcall program"); | ||
28 | module_param_named(cache_getent_timeout, nfs_cache_getent_timeout, ulong, 0600); | ||
29 | MODULE_PARM_DESC(cache_getent_timeout, "Timeout (in seconds) after which " | ||
30 | "the cache upcall is assumed to have failed"); | ||
31 | |||
32 | int nfs_cache_upcall(struct cache_detail *cd, char *entry_name) | ||
33 | { | ||
34 | static char *envp[] = { "HOME=/", | ||
35 | "TERM=linux", | ||
36 | "PATH=/sbin:/usr/sbin:/bin:/usr/bin", | ||
37 | NULL | ||
38 | }; | ||
39 | char *argv[] = { | ||
40 | nfs_cache_getent_prog, | ||
41 | cd->name, | ||
42 | entry_name, | ||
43 | NULL | ||
44 | }; | ||
45 | int ret = -EACCES; | ||
46 | |||
47 | if (nfs_cache_getent_prog[0] == '\0') | ||
48 | goto out; | ||
49 | ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); | ||
50 | /* | ||
51 | * Disable the upcall mechanism if we're getting an ENOENT or | ||
52 | * EACCES error. The admin can re-enable it on the fly by using | ||
53 | * sysfs to set the 'cache_getent' parameter once the problem | ||
54 | * has been fixed. | ||
55 | */ | ||
56 | if (ret == -ENOENT || ret == -EACCES) | ||
57 | nfs_cache_getent_prog[0] = '\0'; | ||
58 | out: | ||
59 | return ret > 0 ? 0 : ret; | ||
60 | } | ||
61 | |||
62 | /* | ||
63 | * Deferred request handling | ||
64 | */ | ||
65 | void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq) | ||
66 | { | ||
67 | if (atomic_dec_and_test(&dreq->count)) | ||
68 | kfree(dreq); | ||
69 | } | ||
70 | |||
71 | static void nfs_dns_cache_revisit(struct cache_deferred_req *d, int toomany) | ||
72 | { | ||
73 | struct nfs_cache_defer_req *dreq; | ||
74 | |||
75 | dreq = container_of(d, struct nfs_cache_defer_req, deferred_req); | ||
76 | |||
77 | complete_all(&dreq->completion); | ||
78 | nfs_cache_defer_req_put(dreq); | ||
79 | } | ||
80 | |||
81 | static struct cache_deferred_req *nfs_dns_cache_defer(struct cache_req *req) | ||
82 | { | ||
83 | struct nfs_cache_defer_req *dreq; | ||
84 | |||
85 | dreq = container_of(req, struct nfs_cache_defer_req, req); | ||
86 | dreq->deferred_req.revisit = nfs_dns_cache_revisit; | ||
87 | atomic_inc(&dreq->count); | ||
88 | |||
89 | return &dreq->deferred_req; | ||
90 | } | ||
91 | |||
92 | struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void) | ||
93 | { | ||
94 | struct nfs_cache_defer_req *dreq; | ||
95 | |||
96 | dreq = kzalloc(sizeof(*dreq), GFP_KERNEL); | ||
97 | if (dreq) { | ||
98 | init_completion(&dreq->completion); | ||
99 | atomic_set(&dreq->count, 1); | ||
100 | dreq->req.defer = nfs_dns_cache_defer; | ||
101 | } | ||
102 | return dreq; | ||
103 | } | ||
104 | |||
105 | int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq) | ||
106 | { | ||
107 | if (wait_for_completion_timeout(&dreq->completion, | ||
108 | nfs_cache_getent_timeout * HZ) == 0) | ||
109 | return -ETIMEDOUT; | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | int nfs_cache_register(struct cache_detail *cd) | ||
114 | { | ||
115 | struct nameidata nd; | ||
116 | struct vfsmount *mnt; | ||
117 | int ret; | ||
118 | |||
119 | mnt = rpc_get_mount(); | ||
120 | if (IS_ERR(mnt)) | ||
121 | return PTR_ERR(mnt); | ||
122 | ret = vfs_path_lookup(mnt->mnt_root, mnt, "/cache", 0, &nd); | ||
123 | if (ret) | ||
124 | goto err; | ||
125 | ret = sunrpc_cache_register_pipefs(nd.path.dentry, | ||
126 | cd->name, 0600, cd); | ||
127 | path_put(&nd.path); | ||
128 | if (!ret) | ||
129 | return ret; | ||
130 | err: | ||
131 | rpc_put_mount(); | ||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | void nfs_cache_unregister(struct cache_detail *cd) | ||
136 | { | ||
137 | sunrpc_cache_unregister_pipefs(cd); | ||
138 | rpc_put_mount(); | ||
139 | } | ||
140 | |||
diff --git a/fs/nfs/cache_lib.h b/fs/nfs/cache_lib.h new file mode 100644 index 000000000000..76f856e284e4 --- /dev/null +++ b/fs/nfs/cache_lib.h | |||
@@ -0,0 +1,27 @@ | |||
1 | /* | ||
2 | * Helper routines for the NFS client caches | ||
3 | * | ||
4 | * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com> | ||
5 | */ | ||
6 | |||
7 | #include <linux/completion.h> | ||
8 | #include <linux/sunrpc/cache.h> | ||
9 | #include <asm/atomic.h> | ||
10 | |||
11 | /* | ||
12 | * Deferred request handling | ||
13 | */ | ||
14 | struct nfs_cache_defer_req { | ||
15 | struct cache_req req; | ||
16 | struct cache_deferred_req deferred_req; | ||
17 | struct completion completion; | ||
18 | atomic_t count; | ||
19 | }; | ||
20 | |||
21 | extern int nfs_cache_upcall(struct cache_detail *cd, char *entry_name); | ||
22 | extern struct nfs_cache_defer_req *nfs_cache_defer_req_alloc(void); | ||
23 | extern void nfs_cache_defer_req_put(struct nfs_cache_defer_req *dreq); | ||
24 | extern int nfs_cache_wait_for_upcall(struct nfs_cache_defer_req *dreq); | ||
25 | |||
26 | extern int nfs_cache_register(struct cache_detail *cd); | ||
27 | extern void nfs_cache_unregister(struct cache_detail *cd); | ||
diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c new file mode 100644 index 000000000000..f4d54ba97cc6 --- /dev/null +++ b/fs/nfs/dns_resolve.c | |||
@@ -0,0 +1,335 @@ | |||
1 | /* | ||
2 | * linux/fs/nfs/dns_resolve.c | ||
3 | * | ||
4 | * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com> | ||
5 | * | ||
6 | * Resolves DNS hostnames into valid ip addresses | ||
7 | */ | ||
8 | |||
9 | #include <linux/hash.h> | ||
10 | #include <linux/string.h> | ||
11 | #include <linux/kmod.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/socket.h> | ||
14 | #include <linux/seq_file.h> | ||
15 | #include <linux/inet.h> | ||
16 | #include <linux/sunrpc/clnt.h> | ||
17 | #include <linux/sunrpc/cache.h> | ||
18 | #include <linux/sunrpc/svcauth.h> | ||
19 | |||
20 | #include "dns_resolve.h" | ||
21 | #include "cache_lib.h" | ||
22 | |||
23 | #define NFS_DNS_HASHBITS 4 | ||
24 | #define NFS_DNS_HASHTBL_SIZE (1 << NFS_DNS_HASHBITS) | ||
25 | |||
26 | static struct cache_head *nfs_dns_table[NFS_DNS_HASHTBL_SIZE]; | ||
27 | |||
28 | struct nfs_dns_ent { | ||
29 | struct cache_head h; | ||
30 | |||
31 | char *hostname; | ||
32 | size_t namelen; | ||
33 | |||
34 | struct sockaddr_storage addr; | ||
35 | size_t addrlen; | ||
36 | }; | ||
37 | |||
38 | |||
39 | static void nfs_dns_ent_init(struct cache_head *cnew, | ||
40 | struct cache_head *ckey) | ||
41 | { | ||
42 | struct nfs_dns_ent *new; | ||
43 | struct nfs_dns_ent *key; | ||
44 | |||
45 | new = container_of(cnew, struct nfs_dns_ent, h); | ||
46 | key = container_of(ckey, struct nfs_dns_ent, h); | ||
47 | |||
48 | kfree(new->hostname); | ||
49 | new->hostname = kstrndup(key->hostname, key->namelen, GFP_KERNEL); | ||
50 | if (new->hostname) { | ||
51 | new->namelen = key->namelen; | ||
52 | memcpy(&new->addr, &key->addr, key->addrlen); | ||
53 | new->addrlen = key->addrlen; | ||
54 | } else { | ||
55 | new->namelen = 0; | ||
56 | new->addrlen = 0; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | static void nfs_dns_ent_put(struct kref *ref) | ||
61 | { | ||
62 | struct nfs_dns_ent *item; | ||
63 | |||
64 | item = container_of(ref, struct nfs_dns_ent, h.ref); | ||
65 | kfree(item->hostname); | ||
66 | kfree(item); | ||
67 | } | ||
68 | |||
69 | static struct cache_head *nfs_dns_ent_alloc(void) | ||
70 | { | ||
71 | struct nfs_dns_ent *item = kmalloc(sizeof(*item), GFP_KERNEL); | ||
72 | |||
73 | if (item != NULL) { | ||
74 | item->hostname = NULL; | ||
75 | item->namelen = 0; | ||
76 | item->addrlen = 0; | ||
77 | return &item->h; | ||
78 | } | ||
79 | return NULL; | ||
80 | }; | ||
81 | |||
82 | static unsigned int nfs_dns_hash(const struct nfs_dns_ent *key) | ||
83 | { | ||
84 | return hash_str(key->hostname, NFS_DNS_HASHBITS); | ||
85 | } | ||
86 | |||
87 | static void nfs_dns_request(struct cache_detail *cd, | ||
88 | struct cache_head *ch, | ||
89 | char **bpp, int *blen) | ||
90 | { | ||
91 | struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h); | ||
92 | |||
93 | qword_add(bpp, blen, key->hostname); | ||
94 | (*bpp)[-1] = '\n'; | ||
95 | } | ||
96 | |||
97 | static int nfs_dns_upcall(struct cache_detail *cd, | ||
98 | struct cache_head *ch) | ||
99 | { | ||
100 | struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h); | ||
101 | int ret; | ||
102 | |||
103 | ret = nfs_cache_upcall(cd, key->hostname); | ||
104 | if (ret) | ||
105 | ret = sunrpc_cache_pipe_upcall(cd, ch, nfs_dns_request); | ||
106 | return ret; | ||
107 | } | ||
108 | |||
109 | static int nfs_dns_match(struct cache_head *ca, | ||
110 | struct cache_head *cb) | ||
111 | { | ||
112 | struct nfs_dns_ent *a; | ||
113 | struct nfs_dns_ent *b; | ||
114 | |||
115 | a = container_of(ca, struct nfs_dns_ent, h); | ||
116 | b = container_of(cb, struct nfs_dns_ent, h); | ||
117 | |||
118 | if (a->namelen == 0 || a->namelen != b->namelen) | ||
119 | return 0; | ||
120 | return memcmp(a->hostname, b->hostname, a->namelen) == 0; | ||
121 | } | ||
122 | |||
123 | static int nfs_dns_show(struct seq_file *m, struct cache_detail *cd, | ||
124 | struct cache_head *h) | ||
125 | { | ||
126 | struct nfs_dns_ent *item; | ||
127 | long ttl; | ||
128 | |||
129 | if (h == NULL) { | ||
130 | seq_puts(m, "# ip address hostname ttl\n"); | ||
131 | return 0; | ||
132 | } | ||
133 | item = container_of(h, struct nfs_dns_ent, h); | ||
134 | ttl = (long)item->h.expiry_time - (long)get_seconds(); | ||
135 | if (ttl < 0) | ||
136 | ttl = 0; | ||
137 | |||
138 | if (!test_bit(CACHE_NEGATIVE, &h->flags)) { | ||
139 | char buf[INET6_ADDRSTRLEN+IPV6_SCOPE_ID_LEN+1]; | ||
140 | |||
141 | rpc_ntop((struct sockaddr *)&item->addr, buf, sizeof(buf)); | ||
142 | seq_printf(m, "%15s ", buf); | ||
143 | } else | ||
144 | seq_puts(m, "<none> "); | ||
145 | seq_printf(m, "%15s %ld\n", item->hostname, ttl); | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | struct nfs_dns_ent *nfs_dns_lookup(struct cache_detail *cd, | ||
150 | struct nfs_dns_ent *key) | ||
151 | { | ||
152 | struct cache_head *ch; | ||
153 | |||
154 | ch = sunrpc_cache_lookup(cd, | ||
155 | &key->h, | ||
156 | nfs_dns_hash(key)); | ||
157 | if (!ch) | ||
158 | return NULL; | ||
159 | return container_of(ch, struct nfs_dns_ent, h); | ||
160 | } | ||
161 | |||
162 | struct nfs_dns_ent *nfs_dns_update(struct cache_detail *cd, | ||
163 | struct nfs_dns_ent *new, | ||
164 | struct nfs_dns_ent *key) | ||
165 | { | ||
166 | struct cache_head *ch; | ||
167 | |||
168 | ch = sunrpc_cache_update(cd, | ||
169 | &new->h, &key->h, | ||
170 | nfs_dns_hash(key)); | ||
171 | if (!ch) | ||
172 | return NULL; | ||
173 | return container_of(ch, struct nfs_dns_ent, h); | ||
174 | } | ||
175 | |||
176 | static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen) | ||
177 | { | ||
178 | char buf1[NFS_DNS_HOSTNAME_MAXLEN+1]; | ||
179 | struct nfs_dns_ent key, *item; | ||
180 | unsigned long ttl; | ||
181 | ssize_t len; | ||
182 | int ret = -EINVAL; | ||
183 | |||
184 | if (buf[buflen-1] != '\n') | ||
185 | goto out; | ||
186 | buf[buflen-1] = '\0'; | ||
187 | |||
188 | len = qword_get(&buf, buf1, sizeof(buf1)); | ||
189 | if (len <= 0) | ||
190 | goto out; | ||
191 | key.addrlen = rpc_pton(buf1, len, | ||
192 | (struct sockaddr *)&key.addr, | ||
193 | sizeof(key.addr)); | ||
194 | |||
195 | len = qword_get(&buf, buf1, sizeof(buf1)); | ||
196 | if (len <= 0) | ||
197 | goto out; | ||
198 | |||
199 | key.hostname = buf1; | ||
200 | key.namelen = len; | ||
201 | memset(&key.h, 0, sizeof(key.h)); | ||
202 | |||
203 | ttl = get_expiry(&buf); | ||
204 | if (ttl == 0) | ||
205 | goto out; | ||
206 | key.h.expiry_time = ttl + get_seconds(); | ||
207 | |||
208 | ret = -ENOMEM; | ||
209 | item = nfs_dns_lookup(cd, &key); | ||
210 | if (item == NULL) | ||
211 | goto out; | ||
212 | |||
213 | if (key.addrlen == 0) | ||
214 | set_bit(CACHE_NEGATIVE, &key.h.flags); | ||
215 | |||
216 | item = nfs_dns_update(cd, &key, item); | ||
217 | if (item == NULL) | ||
218 | goto out; | ||
219 | |||
220 | ret = 0; | ||
221 | cache_put(&item->h, cd); | ||
222 | out: | ||
223 | return ret; | ||
224 | } | ||
225 | |||
226 | static struct cache_detail nfs_dns_resolve = { | ||
227 | .owner = THIS_MODULE, | ||
228 | .hash_size = NFS_DNS_HASHTBL_SIZE, | ||
229 | .hash_table = nfs_dns_table, | ||
230 | .name = "dns_resolve", | ||
231 | .cache_put = nfs_dns_ent_put, | ||
232 | .cache_upcall = nfs_dns_upcall, | ||
233 | .cache_parse = nfs_dns_parse, | ||
234 | .cache_show = nfs_dns_show, | ||
235 | .match = nfs_dns_match, | ||
236 | .init = nfs_dns_ent_init, | ||
237 | .update = nfs_dns_ent_init, | ||
238 | .alloc = nfs_dns_ent_alloc, | ||
239 | }; | ||
240 | |||
241 | static int do_cache_lookup(struct cache_detail *cd, | ||
242 | struct nfs_dns_ent *key, | ||
243 | struct nfs_dns_ent **item, | ||
244 | struct nfs_cache_defer_req *dreq) | ||
245 | { | ||
246 | int ret = -ENOMEM; | ||
247 | |||
248 | *item = nfs_dns_lookup(cd, key); | ||
249 | if (*item) { | ||
250 | ret = cache_check(cd, &(*item)->h, &dreq->req); | ||
251 | if (ret) | ||
252 | *item = NULL; | ||
253 | } | ||
254 | return ret; | ||
255 | } | ||
256 | |||
257 | static int do_cache_lookup_nowait(struct cache_detail *cd, | ||
258 | struct nfs_dns_ent *key, | ||
259 | struct nfs_dns_ent **item) | ||
260 | { | ||
261 | int ret = -ENOMEM; | ||
262 | |||
263 | *item = nfs_dns_lookup(cd, key); | ||
264 | if (!*item) | ||
265 | goto out_err; | ||
266 | ret = -ETIMEDOUT; | ||
267 | if (!test_bit(CACHE_VALID, &(*item)->h.flags) | ||
268 | || (*item)->h.expiry_time < get_seconds() | ||
269 | || cd->flush_time > (*item)->h.last_refresh) | ||
270 | goto out_put; | ||
271 | ret = -ENOENT; | ||
272 | if (test_bit(CACHE_NEGATIVE, &(*item)->h.flags)) | ||
273 | goto out_put; | ||
274 | return 0; | ||
275 | out_put: | ||
276 | cache_put(&(*item)->h, cd); | ||
277 | out_err: | ||
278 | *item = NULL; | ||
279 | return ret; | ||
280 | } | ||
281 | |||
282 | static int do_cache_lookup_wait(struct cache_detail *cd, | ||
283 | struct nfs_dns_ent *key, | ||
284 | struct nfs_dns_ent **item) | ||
285 | { | ||
286 | struct nfs_cache_defer_req *dreq; | ||
287 | int ret = -ENOMEM; | ||
288 | |||
289 | dreq = nfs_cache_defer_req_alloc(); | ||
290 | if (!dreq) | ||
291 | goto out; | ||
292 | ret = do_cache_lookup(cd, key, item, dreq); | ||
293 | if (ret == -EAGAIN) { | ||
294 | ret = nfs_cache_wait_for_upcall(dreq); | ||
295 | if (!ret) | ||
296 | ret = do_cache_lookup_nowait(cd, key, item); | ||
297 | } | ||
298 | nfs_cache_defer_req_put(dreq); | ||
299 | out: | ||
300 | return ret; | ||
301 | } | ||
302 | |||
303 | ssize_t nfs_dns_resolve_name(char *name, size_t namelen, | ||
304 | struct sockaddr *sa, size_t salen) | ||
305 | { | ||
306 | struct nfs_dns_ent key = { | ||
307 | .hostname = name, | ||
308 | .namelen = namelen, | ||
309 | }; | ||
310 | struct nfs_dns_ent *item = NULL; | ||
311 | ssize_t ret; | ||
312 | |||
313 | ret = do_cache_lookup_wait(&nfs_dns_resolve, &key, &item); | ||
314 | if (ret == 0) { | ||
315 | if (salen >= item->addrlen) { | ||
316 | memcpy(sa, &item->addr, item->addrlen); | ||
317 | ret = item->addrlen; | ||
318 | } else | ||
319 | ret = -EOVERFLOW; | ||
320 | cache_put(&item->h, &nfs_dns_resolve); | ||
321 | } else if (ret == -ENOENT) | ||
322 | ret = -ESRCH; | ||
323 | return ret; | ||
324 | } | ||
325 | |||
326 | int nfs_dns_resolver_init(void) | ||
327 | { | ||
328 | return nfs_cache_register(&nfs_dns_resolve); | ||
329 | } | ||
330 | |||
331 | void nfs_dns_resolver_destroy(void) | ||
332 | { | ||
333 | nfs_cache_unregister(&nfs_dns_resolve); | ||
334 | } | ||
335 | |||
diff --git a/fs/nfs/dns_resolve.h b/fs/nfs/dns_resolve.h new file mode 100644 index 000000000000..a3f0938babf7 --- /dev/null +++ b/fs/nfs/dns_resolve.h | |||
@@ -0,0 +1,14 @@ | |||
1 | /* | ||
2 | * Resolve DNS hostnames into valid ip addresses | ||
3 | */ | ||
4 | #ifndef __LINUX_FS_NFS_DNS_RESOLVE_H | ||
5 | #define __LINUX_FS_NFS_DNS_RESOLVE_H | ||
6 | |||
7 | #define NFS_DNS_HOSTNAME_MAXLEN (128) | ||
8 | |||
9 | extern int nfs_dns_resolver_init(void); | ||
10 | extern void nfs_dns_resolver_destroy(void); | ||
11 | extern ssize_t nfs_dns_resolve_name(char *name, size_t namelen, | ||
12 | struct sockaddr *sa, size_t salen); | ||
13 | |||
14 | #endif | ||
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index fe5a8b45d867..060022b4651c 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include "iostat.h" | 46 | #include "iostat.h" |
47 | #include "internal.h" | 47 | #include "internal.h" |
48 | #include "fscache.h" | 48 | #include "fscache.h" |
49 | #include "dns_resolve.h" | ||
49 | 50 | ||
50 | #define NFSDBG_FACILITY NFSDBG_VFS | 51 | #define NFSDBG_FACILITY NFSDBG_VFS |
51 | 52 | ||
@@ -1506,6 +1507,10 @@ static int __init init_nfs_fs(void) | |||
1506 | { | 1507 | { |
1507 | int err; | 1508 | int err; |
1508 | 1509 | ||
1510 | err = nfs_dns_resolver_init(); | ||
1511 | if (err < 0) | ||
1512 | goto out8; | ||
1513 | |||
1509 | err = nfs_fscache_register(); | 1514 | err = nfs_fscache_register(); |
1510 | if (err < 0) | 1515 | if (err < 0) |
1511 | goto out7; | 1516 | goto out7; |
@@ -1564,6 +1569,8 @@ out5: | |||
1564 | out6: | 1569 | out6: |
1565 | nfs_fscache_unregister(); | 1570 | nfs_fscache_unregister(); |
1566 | out7: | 1571 | out7: |
1572 | nfs_dns_resolver_destroy(); | ||
1573 | out8: | ||
1567 | return err; | 1574 | return err; |
1568 | } | 1575 | } |
1569 | 1576 | ||
@@ -1575,6 +1582,7 @@ static void __exit exit_nfs_fs(void) | |||
1575 | nfs_destroy_inodecache(); | 1582 | nfs_destroy_inodecache(); |
1576 | nfs_destroy_nfspagecache(); | 1583 | nfs_destroy_nfspagecache(); |
1577 | nfs_fscache_unregister(); | 1584 | nfs_fscache_unregister(); |
1585 | nfs_dns_resolver_destroy(); | ||
1578 | #ifdef CONFIG_PROC_FS | 1586 | #ifdef CONFIG_PROC_FS |
1579 | rpc_proc_unregister("nfs"); | 1587 | rpc_proc_unregister("nfs"); |
1580 | #endif | 1588 | #endif |
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 3fdacaf5c708..7f676bdf70d3 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c | |||
@@ -416,11 +416,13 @@ struct vfsmount *rpc_get_mount(void) | |||
416 | return ERR_PTR(err); | 416 | return ERR_PTR(err); |
417 | return rpc_mount; | 417 | return rpc_mount; |
418 | } | 418 | } |
419 | EXPORT_SYMBOL_GPL(rpc_get_mount); | ||
419 | 420 | ||
420 | void rpc_put_mount(void) | 421 | void rpc_put_mount(void) |
421 | { | 422 | { |
422 | simple_release_fs(&rpc_mount, &rpc_mount_count); | 423 | simple_release_fs(&rpc_mount, &rpc_mount_count); |
423 | } | 424 | } |
425 | EXPORT_SYMBOL_GPL(rpc_put_mount); | ||
424 | 426 | ||
425 | static int rpc_delete_dentry(struct dentry *dentry) | 427 | static int rpc_delete_dentry(struct dentry *dentry) |
426 | { | 428 | { |
@@ -946,6 +948,7 @@ enum { | |||
946 | RPCAUTH_portmap, | 948 | RPCAUTH_portmap, |
947 | RPCAUTH_statd, | 949 | RPCAUTH_statd, |
948 | RPCAUTH_nfsd4_cb, | 950 | RPCAUTH_nfsd4_cb, |
951 | RPCAUTH_cache, | ||
949 | RPCAUTH_RootEOF | 952 | RPCAUTH_RootEOF |
950 | }; | 953 | }; |
951 | 954 | ||
@@ -974,6 +977,10 @@ static const struct rpc_filelist files[] = { | |||
974 | .name = "nfsd4_cb", | 977 | .name = "nfsd4_cb", |
975 | .mode = S_IFDIR | S_IRUGO | S_IXUGO, | 978 | .mode = S_IFDIR | S_IRUGO | S_IXUGO, |
976 | }, | 979 | }, |
980 | [RPCAUTH_cache] = { | ||
981 | .name = "cache", | ||
982 | .mode = S_IFDIR | S_IRUGO | S_IXUGO, | ||
983 | }, | ||
977 | }; | 984 | }; |
978 | 985 | ||
979 | static int | 986 | static int |