afs: Reorganise volume and server trees to be rooted on the cell
[linux-2.6-microblaze.git] / fs / afs / vl_alias.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* AFS cell alias detection
3  *
4  * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
5  * Written by David Howells (dhowells@redhat.com)
6  */
7
8 #include <linux/slab.h>
9 #include <linux/sched.h>
10 #include <linux/namei.h>
11 #include <keys/rxrpc-type.h>
12 #include "internal.h"
13
14 /*
15  * Sample a volume.
16  */
17 static struct afs_volume *afs_sample_volume(struct afs_cell *cell, struct key *key,
18                                             const char *name, unsigned int namelen)
19 {
20         struct afs_volume *volume;
21         struct afs_fs_context fc = {
22                 .type           = 0, /* Explicitly leave it to the VLDB */
23                 .volnamesz      = namelen,
24                 .volname        = name,
25                 .net            = cell->net,
26                 .cell           = cell,
27                 .key            = key, /* This might need to be something */
28         };
29
30         volume = afs_create_volume(&fc);
31         _leave(" = %px", volume);
32         return volume;
33 }
34
35 /*
36  * Compare two addresses.
37  */
38 static int afs_compare_addrs(const struct sockaddr_rxrpc *srx_a,
39                              const struct sockaddr_rxrpc *srx_b)
40 {
41         short port_a, port_b;
42         int addr_a, addr_b, diff;
43
44         diff = (short)srx_a->transport_type - (short)srx_b->transport_type;
45         if (diff)
46                 goto out;
47
48         switch (srx_a->transport_type) {
49         case AF_INET: {
50                 const struct sockaddr_in *a = &srx_a->transport.sin;
51                 const struct sockaddr_in *b = &srx_b->transport.sin;
52                 addr_a = ntohl(a->sin_addr.s_addr);
53                 addr_b = ntohl(b->sin_addr.s_addr);
54                 diff = addr_a - addr_b;
55                 if (diff == 0) {
56                         port_a = ntohs(a->sin_port);
57                         port_b = ntohs(b->sin_port);
58                         diff = port_a - port_b;
59                 }
60                 break;
61         }
62
63         case AF_INET6: {
64                 const struct sockaddr_in6 *a = &srx_a->transport.sin6;
65                 const struct sockaddr_in6 *b = &srx_b->transport.sin6;
66                 diff = memcmp(&a->sin6_addr, &b->sin6_addr, 16);
67                 if (diff == 0) {
68                         port_a = ntohs(a->sin6_port);
69                         port_b = ntohs(b->sin6_port);
70                         diff = port_a - port_b;
71                 }
72                 break;
73         }
74
75         default:
76                 BUG();
77         }
78
79 out:
80         return diff;
81 }
82
83 /*
84  * Compare the address lists of a pair of fileservers.
85  */
86 static int afs_compare_fs_alists(const struct afs_server *server_a,
87                                  const struct afs_server *server_b)
88 {
89         const struct afs_addr_list *la, *lb;
90         int a = 0, b = 0, addr_matches = 0;
91
92         la = rcu_dereference(server_a->addresses);
93         lb = rcu_dereference(server_b->addresses);
94
95         while (a < la->nr_addrs && b < lb->nr_addrs) {
96                 const struct sockaddr_rxrpc *srx_a = &la->addrs[a];
97                 const struct sockaddr_rxrpc *srx_b = &lb->addrs[b];
98                 int diff = afs_compare_addrs(srx_a, srx_b);
99
100                 if (diff < 0) {
101                         a++;
102                 } else if (diff > 0) {
103                         b++;
104                 } else {
105                         addr_matches++;
106                         a++;
107                         b++;
108                 }
109         }
110
111         return addr_matches;
112 }
113
114 /*
115  * Compare the fileserver lists of two volumes.  The server lists are sorted in
116  * order of ascending UUID.
117  */
118 static int afs_compare_volume_slists(const struct afs_volume *vol_a,
119                                      const struct afs_volume *vol_b)
120 {
121         const struct afs_server_list *la, *lb;
122         int i, a = 0, b = 0, uuid_matches = 0, addr_matches = 0;
123
124         la = rcu_dereference(vol_a->servers);
125         lb = rcu_dereference(vol_b->servers);
126
127         for (i = 0; i < AFS_MAXTYPES; i++)
128                 if (la->vids[i] != lb->vids[i])
129                         return 0;
130
131         while (a < la->nr_servers && b < lb->nr_servers) {
132                 const struct afs_server *server_a = la->servers[a].server;
133                 const struct afs_server *server_b = lb->servers[b].server;
134                 int diff = memcmp(&server_a->uuid, &server_b->uuid, sizeof(uuid_t));
135
136                 if (diff < 0) {
137                         a++;
138                 } else if (diff > 0) {
139                         b++;
140                 } else {
141                         uuid_matches++;
142                         addr_matches += afs_compare_fs_alists(server_a, server_b);
143                         a++;
144                         b++;
145                 }
146         }
147
148         _leave(" = %d [um %d]", addr_matches, uuid_matches);
149         return addr_matches;
150 }
151
152 /*
153  * Compare root.cell volumes.
154  */
155 static int afs_compare_cell_roots(struct afs_cell *cell)
156 {
157         struct afs_cell *p;
158
159         _enter("");
160
161         rcu_read_lock();
162
163         hlist_for_each_entry_rcu(p, &cell->net->proc_cells, proc_link) {
164                 if (p == cell || p->alias_of)
165                         continue;
166                 if (!p->root_volume)
167                         continue; /* Ignore cells that don't have a root.cell volume. */
168
169                 if (afs_compare_volume_slists(cell->root_volume, p->root_volume) != 0)
170                         goto is_alias;
171         }
172
173         rcu_read_unlock();
174         _leave(" = 0");
175         return 0;
176
177 is_alias:
178         rcu_read_unlock();
179         cell->alias_of = afs_get_cell(p);
180         return 1;
181 }
182
183 /*
184  * Query the new cell for a volume from a cell we're already using.
185  */
186 static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key,
187                                    struct afs_cell *p)
188 {
189         struct afs_volume *volume, *pvol = NULL;
190         int ret;
191
192         /* Arbitrarily pick a volume from the list. */
193         read_seqlock_excl(&p->volume_lock);
194         if (!RB_EMPTY_ROOT(&p->volumes))
195                 pvol = afs_get_volume(rb_entry(p->volumes.rb_node,
196                                                struct afs_volume, cell_node),
197                                       afs_volume_trace_get_query_alias);
198         read_sequnlock_excl(&p->volume_lock);
199         if (!pvol)
200                 return 0;
201
202         _enter("%s:%s", cell->name, pvol->name);
203
204         /* And see if it's in the new cell. */
205         volume = afs_sample_volume(cell, key, pvol->name, pvol->name_len);
206         if (IS_ERR(volume)) {
207                 afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias);
208                 if (PTR_ERR(volume) != -ENOMEDIUM)
209                         return PTR_ERR(volume);
210                 /* That volume is not in the new cell, so not an alias */
211                 return 0;
212         }
213
214         /* The new cell has a like-named volume also - compare volume ID,
215          * server and address lists.
216          */
217         ret = 0;
218         if (pvol->vid == volume->vid) {
219                 rcu_read_lock();
220                 if (afs_compare_volume_slists(volume, pvol))
221                         ret = 1;
222                 rcu_read_unlock();
223         }
224
225         afs_put_volume(cell->net, volume, afs_volume_trace_put_query_alias);
226         afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias);
227         return ret;
228 }
229
230 /*
231  * Query the new cell for volumes we know exist in cells we're already using.
232  */
233 static int afs_query_for_alias(struct afs_cell *cell, struct key *key)
234 {
235         struct afs_cell *p;
236
237         _enter("%s", cell->name);
238
239         if (mutex_lock_interruptible(&cell->net->proc_cells_lock) < 0)
240                 return -ERESTARTSYS;
241
242         hlist_for_each_entry(p, &cell->net->proc_cells, proc_link) {
243                 if (p == cell || p->alias_of)
244                         continue;
245                 if (RB_EMPTY_ROOT(&p->volumes))
246                         continue;
247                 if (p->root_volume)
248                         continue; /* Ignore cells that have a root.cell volume. */
249                 afs_get_cell(p);
250                 mutex_unlock(&cell->net->proc_cells_lock);
251
252                 if (afs_query_for_alias_one(cell, key, p) != 0)
253                         goto is_alias;
254
255                 if (mutex_lock_interruptible(&cell->net->proc_cells_lock) < 0) {
256                         afs_put_cell(cell->net, p);
257                         return -ERESTARTSYS;
258                 }
259
260                 afs_put_cell(cell->net, p);
261         }
262
263         mutex_unlock(&cell->net->proc_cells_lock);
264         _leave(" = 0");
265         return 0;
266
267 is_alias:
268         cell->alias_of = p; /* Transfer our ref */
269         return 1;
270 }
271
272 /*
273  * Look up a VLDB record for a volume.
274  */
275 static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
276 {
277         struct afs_vl_cursor vc;
278         char *cell_name = ERR_PTR(-EDESTADDRREQ);
279         bool skipped = false, not_skipped = false;
280         int ret;
281
282         if (!afs_begin_vlserver_operation(&vc, cell, key))
283                 return ERR_PTR(-ERESTARTSYS);
284
285         while (afs_select_vlserver(&vc)) {
286                 if (!test_bit(AFS_VLSERVER_FL_IS_YFS, &vc.server->flags)) {
287                         vc.ac.error = -EOPNOTSUPP;
288                         skipped = true;
289                         continue;
290                 }
291                 not_skipped = true;
292                 cell_name = afs_yfsvl_get_cell_name(&vc);
293         }
294
295         ret = afs_end_vlserver_operation(&vc);
296         if (skipped && !not_skipped)
297                 ret = -EOPNOTSUPP;
298         return ret < 0 ? ERR_PTR(ret) : cell_name;
299 }
300
301 static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
302 {
303         struct afs_cell *master;
304         char *cell_name;
305
306         cell_name = afs_vl_get_cell_name(cell, key);
307         if (IS_ERR(cell_name))
308                 return PTR_ERR(cell_name);
309
310         if (strcmp(cell_name, cell->name) == 0) {
311                 kfree(cell_name);
312                 return 0;
313         }
314
315         master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name),
316                                  NULL, false);
317         kfree(cell_name);
318         if (IS_ERR(master))
319                 return PTR_ERR(master);
320
321         cell->alias_of = master; /* Transfer our ref */
322         return 1;
323 }
324
325 static int afs_do_cell_detect_alias(struct afs_cell *cell, struct key *key)
326 {
327         struct afs_volume *root_volume;
328         int ret;
329
330         _enter("%s", cell->name);
331
332         ret = yfs_check_canonical_cell_name(cell, key);
333         if (ret != -EOPNOTSUPP)
334                 return ret;
335
336         /* Try and get the root.cell volume for comparison with other cells */
337         root_volume = afs_sample_volume(cell, key, "root.cell", 9);
338         if (!IS_ERR(root_volume)) {
339                 cell->root_volume = root_volume;
340                 return afs_compare_cell_roots(cell);
341         }
342
343         if (PTR_ERR(root_volume) != -ENOMEDIUM)
344                 return PTR_ERR(root_volume);
345
346         /* Okay, this cell doesn't have an root.cell volume.  We need to
347          * locate some other random volume and use that to check.
348          */
349         return afs_query_for_alias(cell, key);
350 }
351
352 /*
353  * Check to see if a new cell is an alias of a cell we already have.  At this
354  * point we have the cell's volume server list.
355  *
356  * Returns 0 if we didn't detect an alias, 1 if we found an alias and an error
357  * if we had problems gathering the data required.  In the case the we did
358  * detect an alias, cell->alias_of is set to point to the assumed master.
359  */
360 int afs_cell_detect_alias(struct afs_cell *cell, struct key *key)
361 {
362         struct afs_net *net = cell->net;
363         int ret;
364
365         if (mutex_lock_interruptible(&net->cells_alias_lock) < 0)
366                 return -ERESTARTSYS;
367
368         if (test_bit(AFS_CELL_FL_CHECK_ALIAS, &cell->flags)) {
369                 ret = afs_do_cell_detect_alias(cell, key);
370                 if (ret >= 0)
371                         clear_bit_unlock(AFS_CELL_FL_CHECK_ALIAS, &cell->flags);
372         } else {
373                 ret = cell->alias_of ? 1 : 0;
374         }
375
376         mutex_unlock(&net->cells_alias_lock);
377
378         if (ret == 1)
379                 pr_notice("kAFS: Cell %s is an alias of %s\n",
380                           cell->name, cell->alias_of->name);
381         return ret;
382 }