hfs: update timestamp on truncate()
[linux-2.6-block.git] / fs / afs / addr_list.c
CommitLineData
8b2a464c
DH
1/* Server address list management
2 *
3 * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
10 */
11
12#include <linux/slab.h>
13#include <linux/ctype.h>
14#include <linux/dns_resolver.h>
15#include <linux/inet.h>
16#include <keys/rxrpc-type.h>
17#include "internal.h"
18#include "afs_fs.h"
19
8b2a464c
DH
20/*
21 * Release an address list.
22 */
23void afs_put_addrlist(struct afs_addr_list *alist)
24{
25 if (alist && refcount_dec_and_test(&alist->usage))
26 call_rcu(&alist->rcu, (rcu_callback_t)kfree);
27}
28
29/*
30 * Allocate an address list.
31 */
32struct afs_addr_list *afs_alloc_addrlist(unsigned int nr,
33 unsigned short service,
34 unsigned short port)
35{
36 struct afs_addr_list *alist;
37 unsigned int i;
38
39 _enter("%u,%u,%u", nr, service, port);
40
68eb64c3
DH
41 if (nr > AFS_MAX_ADDRESSES)
42 nr = AFS_MAX_ADDRESSES;
43
acafe7e3 44 alist = kzalloc(struct_size(alist, addrs, nr), GFP_KERNEL);
8b2a464c
DH
45 if (!alist)
46 return NULL;
47
48 refcount_set(&alist->usage, 1);
68eb64c3 49 alist->max_addrs = nr;
8b2a464c
DH
50
51 for (i = 0; i < nr; i++) {
52 struct sockaddr_rxrpc *srx = &alist->addrs[i];
53 srx->srx_family = AF_RXRPC;
54 srx->srx_service = service;
55 srx->transport_type = SOCK_DGRAM;
56 srx->transport_len = sizeof(srx->transport.sin6);
57 srx->transport.sin6.sin6_family = AF_INET6;
58 srx->transport.sin6.sin6_port = htons(port);
59 }
60
61 return alist;
62}
63
64/*
65 * Parse a text string consisting of delimited addresses.
66 */
67struct afs_addr_list *afs_parse_text_addrs(const char *text, size_t len,
68 char delim,
69 unsigned short service,
70 unsigned short port)
71{
72 struct afs_addr_list *alist;
73 const char *p, *end = text + len;
74 unsigned int nr = 0;
75
76 _enter("%*.*s,%c", (int)len, (int)len, text, delim);
77
78 if (!len)
79 return ERR_PTR(-EDESTADDRREQ);
80
81 if (delim == ':' && (memchr(text, ',', len) || !memchr(text, '.', len)))
82 delim = ',';
83
84 /* Count the addresses */
85 p = text;
86 do {
87 if (!*p)
88 return ERR_PTR(-EINVAL);
89 if (*p == delim)
90 continue;
91 nr++;
92 if (*p == '[') {
93 p++;
94 if (p == end)
95 return ERR_PTR(-EINVAL);
96 p = memchr(p, ']', end - p);
97 if (!p)
98 return ERR_PTR(-EINVAL);
99 p++;
100 if (p >= end)
101 break;
102 }
103
104 p = memchr(p, delim, end - p);
105 if (!p)
106 break;
107 p++;
108 } while (p < end);
109
110 _debug("%u/%u addresses", nr, AFS_MAX_ADDRESSES);
8b2a464c
DH
111
112 alist = afs_alloc_addrlist(nr, service, port);
113 if (!alist)
114 return ERR_PTR(-ENOMEM);
115
116 /* Extract the addresses */
117 p = text;
118 do {
01fd79e6 119 const char *q, *stop;
4c19bbdc
DH
120 unsigned int xport = port;
121 __be32 x[4];
122 int family;
8b2a464c
DH
123
124 if (*p == delim) {
125 p++;
126 continue;
127 }
128
129 if (*p == '[') {
130 p++;
01fd79e6
DH
131 q = memchr(p, ']', end - p);
132 } else {
133 for (q = p; q < end; q++)
134 if (*q == '+' || *q == delim)
135 break;
8b2a464c
DH
136 }
137
4c19bbdc
DH
138 if (in4_pton(p, q - p, (u8 *)&x[0], -1, &stop))
139 family = AF_INET;
140 else if (in6_pton(p, q - p, (u8 *)x, -1, &stop))
141 family = AF_INET6;
142 else
8b2a464c 143 goto bad_address;
8b2a464c 144
01fd79e6
DH
145 if (stop != q)
146 goto bad_address;
147
148 p = q;
149 if (q < end && *q == ']')
8b2a464c 150 p++;
8b2a464c
DH
151
152 if (p < end) {
153 if (*p == '+') {
154 /* Port number specification "+1234" */
4c19bbdc 155 xport = 0;
8b2a464c
DH
156 p++;
157 if (p >= end || !isdigit(*p))
158 goto bad_address;
159 do {
160 xport *= 10;
161 xport += *p - '0';
162 if (xport > 65535)
163 goto bad_address;
164 p++;
165 } while (p < end && isdigit(*p));
8b2a464c
DH
166 } else if (*p == delim) {
167 p++;
168 } else {
169 goto bad_address;
170 }
171 }
172
4c19bbdc
DH
173 if (family == AF_INET)
174 afs_merge_fs_addr4(alist, x[0], xport);
175 else
176 afs_merge_fs_addr6(alist, x, xport);
177
178 } while (p < end);
8b2a464c
DH
179
180 _leave(" = [nr %u]", alist->nr_addrs);
181 return alist;
182
183bad_address:
184 kfree(alist);
185 return ERR_PTR(-EINVAL);
186}
187
188/*
189 * Compare old and new address lists to see if there's been any change.
190 * - How to do this in better than O(Nlog(N)) time?
191 * - We don't really want to sort the address list, but would rather take the
192 * list as we got it so as not to undo record rotation by the DNS server.
193 */
194#if 0
195static int afs_cmp_addr_list(const struct afs_addr_list *a1,
196 const struct afs_addr_list *a2)
197{
198}
199#endif
200
201/*
202 * Perform a DNS query for VL servers and build a up an address list.
203 */
204struct afs_addr_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry)
205{
206 struct afs_addr_list *alist;
207 char *vllist = NULL;
208 int ret;
209
210 _enter("%s", cell->name);
211
212 ret = dns_query("afsdb", cell->name, cell->name_len,
c88d5a7f 213 "", &vllist, _expiry);
8b2a464c
DH
214 if (ret < 0)
215 return ERR_PTR(ret);
216
217 alist = afs_parse_text_addrs(vllist, strlen(vllist), ',',
218 VL_SERVICE, AFS_VL_PORT);
219 if (IS_ERR(alist)) {
220 kfree(vllist);
221 if (alist != ERR_PTR(-ENOMEM))
222 pr_err("Failed to parse DNS data\n");
223 return alist;
224 }
225
226 kfree(vllist);
227 return alist;
228}
229
d2ddc776
DH
230/*
231 * Merge an IPv4 entry into a fileserver address list.
232 */
bf99a53c 233void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
d2ddc776 234{
46894a13 235 struct sockaddr_rxrpc *srx;
66be646b 236 u32 addr = ntohl(xdr);
d2ddc776
DH
237 int i;
238
68eb64c3
DH
239 if (alist->nr_addrs >= alist->max_addrs)
240 return;
241
d2ddc776 242 for (i = 0; i < alist->nr_ipv4; i++) {
46894a13
DH
243 struct sockaddr_in *a = &alist->addrs[i].transport.sin;
244 u32 a_addr = ntohl(a->sin_addr.s_addr);
245 u16 a_port = ntohs(a->sin_port);
66be646b
DH
246
247 if (addr == a_addr && port == a_port)
d2ddc776 248 return;
66be646b 249 if (addr == a_addr && port < a_port)
bf99a53c 250 break;
66be646b 251 if (addr < a_addr)
d2ddc776
DH
252 break;
253 }
254
255 if (i < alist->nr_addrs)
256 memmove(alist->addrs + i + 1,
257 alist->addrs + i,
258 sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
259
46894a13
DH
260 srx = &alist->addrs[i];
261 srx->transport_len = sizeof(srx->transport.sin);
262 srx->transport.sin.sin_family = AF_INET;
263 srx->transport.sin.sin_port = htons(port);
264 srx->transport.sin.sin_addr.s_addr = xdr;
d2ddc776
DH
265 alist->nr_ipv4++;
266 alist->nr_addrs++;
267}
268
bf99a53c
DH
269/*
270 * Merge an IPv6 entry into a fileserver address list.
271 */
272void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port)
273{
46894a13 274 struct sockaddr_rxrpc *srx;
bf99a53c
DH
275 int i, diff;
276
68eb64c3
DH
277 if (alist->nr_addrs >= alist->max_addrs)
278 return;
279
bf99a53c 280 for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
66be646b
DH
281 struct sockaddr_in6 *a = &alist->addrs[i].transport.sin6;
282 u16 a_port = ntohs(a->sin6_port);
283
bf99a53c 284 diff = memcmp(xdr, &a->sin6_addr, 16);
66be646b 285 if (diff == 0 && port == a_port)
bf99a53c 286 return;
66be646b 287 if (diff == 0 && port < a_port)
bf99a53c
DH
288 break;
289 if (diff < 0)
290 break;
291 }
292
293 if (i < alist->nr_addrs)
294 memmove(alist->addrs + i + 1,
295 alist->addrs + i,
296 sizeof(alist->addrs[0]) * (alist->nr_addrs - i));
297
46894a13
DH
298 srx = &alist->addrs[i];
299 srx->transport_len = sizeof(srx->transport.sin6);
300 srx->transport.sin6.sin6_family = AF_INET6;
301 srx->transport.sin6.sin6_port = htons(port);
302 memcpy(&srx->transport.sin6.sin6_addr, xdr, 16);
bf99a53c
DH
303 alist->nr_addrs++;
304}
305
8b2a464c
DH
306/*
307 * Get an address to try.
308 */
309bool afs_iterate_addresses(struct afs_addr_cursor *ac)
310{
311 _enter("%hu+%hd", ac->start, (short)ac->index);
312
313 if (!ac->alist)
314 return false;
315
316 if (ac->begun) {
317 ac->index++;
318 if (ac->index == ac->alist->nr_addrs)
319 ac->index = 0;
320
321 if (ac->index == ac->start) {
322 ac->error = -EDESTADDRREQ;
323 return false;
324 }
325 }
326
327 ac->begun = true;
328 ac->responded = false;
329 ac->addr = &ac->alist->addrs[ac->index];
330 return true;
331}
332
333/*
334 * Release an address list cursor.
335 */
336int afs_end_cursor(struct afs_addr_cursor *ac)
337{
fe4d774c
DH
338 struct afs_addr_list *alist;
339
340 alist = ac->alist;
341 if (alist) {
342 if (ac->responded && ac->index != ac->start)
343 WRITE_ONCE(alist->index, ac->index);
344 afs_put_addrlist(alist);
345 }
8b2a464c 346
fe4d774c 347 ac->addr = NULL;
8b2a464c 348 ac->alist = NULL;
fe4d774c 349 ac->begun = false;
8b2a464c
DH
350 return ac->error;
351}
352
353/*
354 * Set the address cursor for iterating over VL servers.
355 */
356int afs_set_vl_cursor(struct afs_addr_cursor *ac, struct afs_cell *cell)
357{
358 struct afs_addr_list *alist;
359 int ret;
360
361 if (!rcu_access_pointer(cell->vl_addrs)) {
362 ret = wait_on_bit(&cell->flags, AFS_CELL_FL_NO_LOOKUP_YET,
363 TASK_INTERRUPTIBLE);
364 if (ret < 0)
365 return ret;
366
367 if (!rcu_access_pointer(cell->vl_addrs) &&
368 ktime_get_real_seconds() < cell->dns_expiry)
369 return cell->error;
370 }
371
372 read_lock(&cell->vl_addrs_lock);
373 alist = rcu_dereference_protected(cell->vl_addrs,
374 lockdep_is_held(&cell->vl_addrs_lock));
375 if (alist->nr_addrs > 0)
376 afs_get_addrlist(alist);
377 else
378 alist = NULL;
379 read_unlock(&cell->vl_addrs_lock);
380
381 if (!alist)
382 return -EDESTADDRREQ;
383
384 ac->alist = alist;
385 ac->addr = NULL;
386 ac->start = READ_ONCE(alist->index);
387 ac->index = ac->start;
388 ac->error = 0;
389 ac->begun = false;
390 return 0;
391}