mm-only debug patch...
[mmotm.git] / fs / nfs / dns_resolve.c
blobf4d54ba97cc62653ffceedf568f4d4c987840682
1 /*
2 * linux/fs/nfs/dns_resolve.c
4 * Copyright (c) 2009 Trond Myklebust <Trond.Myklebust@netapp.com>
6 * Resolves DNS hostnames into valid ip addresses
7 */
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>
20 #include "dns_resolve.h"
21 #include "cache_lib.h"
23 #define NFS_DNS_HASHBITS 4
24 #define NFS_DNS_HASHTBL_SIZE (1 << NFS_DNS_HASHBITS)
26 static struct cache_head *nfs_dns_table[NFS_DNS_HASHTBL_SIZE];
28 struct nfs_dns_ent {
29 struct cache_head h;
31 char *hostname;
32 size_t namelen;
34 struct sockaddr_storage addr;
35 size_t addrlen;
39 static void nfs_dns_ent_init(struct cache_head *cnew,
40 struct cache_head *ckey)
42 struct nfs_dns_ent *new;
43 struct nfs_dns_ent *key;
45 new = container_of(cnew, struct nfs_dns_ent, h);
46 key = container_of(ckey, struct nfs_dns_ent, h);
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;
60 static void nfs_dns_ent_put(struct kref *ref)
62 struct nfs_dns_ent *item;
64 item = container_of(ref, struct nfs_dns_ent, h.ref);
65 kfree(item->hostname);
66 kfree(item);
69 static struct cache_head *nfs_dns_ent_alloc(void)
71 struct nfs_dns_ent *item = kmalloc(sizeof(*item), GFP_KERNEL);
73 if (item != NULL) {
74 item->hostname = NULL;
75 item->namelen = 0;
76 item->addrlen = 0;
77 return &item->h;
79 return NULL;
82 static unsigned int nfs_dns_hash(const struct nfs_dns_ent *key)
84 return hash_str(key->hostname, NFS_DNS_HASHBITS);
87 static void nfs_dns_request(struct cache_detail *cd,
88 struct cache_head *ch,
89 char **bpp, int *blen)
91 struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h);
93 qword_add(bpp, blen, key->hostname);
94 (*bpp)[-1] = '\n';
97 static int nfs_dns_upcall(struct cache_detail *cd,
98 struct cache_head *ch)
100 struct nfs_dns_ent *key = container_of(ch, struct nfs_dns_ent, h);
101 int ret;
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;
109 static int nfs_dns_match(struct cache_head *ca,
110 struct cache_head *cb)
112 struct nfs_dns_ent *a;
113 struct nfs_dns_ent *b;
115 a = container_of(ca, struct nfs_dns_ent, h);
116 b = container_of(cb, struct nfs_dns_ent, h);
118 if (a->namelen == 0 || a->namelen != b->namelen)
119 return 0;
120 return memcmp(a->hostname, b->hostname, a->namelen) == 0;
123 static int nfs_dns_show(struct seq_file *m, struct cache_detail *cd,
124 struct cache_head *h)
126 struct nfs_dns_ent *item;
127 long ttl;
129 if (h == NULL) {
130 seq_puts(m, "# ip address hostname ttl\n");
131 return 0;
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;
138 if (!test_bit(CACHE_NEGATIVE, &h->flags)) {
139 char buf[INET6_ADDRSTRLEN+IPV6_SCOPE_ID_LEN+1];
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;
149 struct nfs_dns_ent *nfs_dns_lookup(struct cache_detail *cd,
150 struct nfs_dns_ent *key)
152 struct cache_head *ch;
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);
162 struct nfs_dns_ent *nfs_dns_update(struct cache_detail *cd,
163 struct nfs_dns_ent *new,
164 struct nfs_dns_ent *key)
166 struct cache_head *ch;
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);
176 static int nfs_dns_parse(struct cache_detail *cd, char *buf, int buflen)
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;
184 if (buf[buflen-1] != '\n')
185 goto out;
186 buf[buflen-1] = '\0';
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));
195 len = qword_get(&buf, buf1, sizeof(buf1));
196 if (len <= 0)
197 goto out;
199 key.hostname = buf1;
200 key.namelen = len;
201 memset(&key.h, 0, sizeof(key.h));
203 ttl = get_expiry(&buf);
204 if (ttl == 0)
205 goto out;
206 key.h.expiry_time = ttl + get_seconds();
208 ret = -ENOMEM;
209 item = nfs_dns_lookup(cd, &key);
210 if (item == NULL)
211 goto out;
213 if (key.addrlen == 0)
214 set_bit(CACHE_NEGATIVE, &key.h.flags);
216 item = nfs_dns_update(cd, &key, item);
217 if (item == NULL)
218 goto out;
220 ret = 0;
221 cache_put(&item->h, cd);
222 out:
223 return ret;
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,
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)
246 int ret = -ENOMEM;
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;
254 return ret;
257 static int do_cache_lookup_nowait(struct cache_detail *cd,
258 struct nfs_dns_ent *key,
259 struct nfs_dns_ent **item)
261 int ret = -ENOMEM;
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;
282 static int do_cache_lookup_wait(struct cache_detail *cd,
283 struct nfs_dns_ent *key,
284 struct nfs_dns_ent **item)
286 struct nfs_cache_defer_req *dreq;
287 int ret = -ENOMEM;
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);
298 nfs_cache_defer_req_put(dreq);
299 out:
300 return ret;
303 ssize_t nfs_dns_resolve_name(char *name, size_t namelen,
304 struct sockaddr *sa, size_t salen)
306 struct nfs_dns_ent key = {
307 .hostname = name,
308 .namelen = namelen,
310 struct nfs_dns_ent *item = NULL;
311 ssize_t ret;
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;
326 int nfs_dns_resolver_init(void)
328 return nfs_cache_register(&nfs_dns_resolve);
331 void nfs_dns_resolver_destroy(void)
333 nfs_cache_unregister(&nfs_dns_resolve);