Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/fs/afs/vl_alias.c
26278 views
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 ([email protected])
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(" = %p", volume);
32
return volume;
33
}
34
35
/*
36
* Compare the address lists of a pair of fileservers.
37
*/
38
static int afs_compare_fs_alists(const struct afs_server *server_a,
39
const struct afs_server *server_b)
40
{
41
const struct afs_addr_list *la, *lb;
42
int a = 0, b = 0, addr_matches = 0;
43
44
la = rcu_dereference(server_a->endpoint_state)->addresses;
45
lb = rcu_dereference(server_b->endpoint_state)->addresses;
46
47
while (a < la->nr_addrs && b < lb->nr_addrs) {
48
unsigned long pa = (unsigned long)la->addrs[a].peer;
49
unsigned long pb = (unsigned long)lb->addrs[b].peer;
50
long diff = pa - pb;
51
52
if (diff < 0) {
53
a++;
54
} else if (diff > 0) {
55
b++;
56
} else {
57
addr_matches++;
58
a++;
59
b++;
60
}
61
}
62
63
return addr_matches;
64
}
65
66
/*
67
* Compare the fileserver lists of two volumes. The server lists are sorted in
68
* order of ascending UUID.
69
*/
70
static int afs_compare_volume_slists(const struct afs_volume *vol_a,
71
const struct afs_volume *vol_b)
72
{
73
const struct afs_server_list *la, *lb;
74
int i, a = 0, b = 0, uuid_matches = 0, addr_matches = 0;
75
76
la = rcu_dereference(vol_a->servers);
77
lb = rcu_dereference(vol_b->servers);
78
79
for (i = 0; i < AFS_MAXTYPES; i++)
80
if (vol_a->vids[i] != vol_b->vids[i])
81
return 0;
82
83
while (a < la->nr_servers && b < lb->nr_servers) {
84
const struct afs_server *server_a = la->servers[a].server;
85
const struct afs_server *server_b = lb->servers[b].server;
86
int diff = memcmp(&server_a->uuid, &server_b->uuid, sizeof(uuid_t));
87
88
if (diff < 0) {
89
a++;
90
} else if (diff > 0) {
91
b++;
92
} else {
93
uuid_matches++;
94
addr_matches += afs_compare_fs_alists(server_a, server_b);
95
a++;
96
b++;
97
}
98
}
99
100
_leave(" = %d [um %d]", addr_matches, uuid_matches);
101
return addr_matches;
102
}
103
104
/*
105
* Compare root.cell volumes.
106
*/
107
static int afs_compare_cell_roots(struct afs_cell *cell)
108
{
109
struct afs_cell *p;
110
111
_enter("");
112
113
rcu_read_lock();
114
115
hlist_for_each_entry_rcu(p, &cell->net->proc_cells, proc_link) {
116
if (p == cell || p->alias_of)
117
continue;
118
if (!p->root_volume)
119
continue; /* Ignore cells that don't have a root.cell volume. */
120
121
if (afs_compare_volume_slists(cell->root_volume, p->root_volume) != 0)
122
goto is_alias;
123
}
124
125
rcu_read_unlock();
126
_leave(" = 0");
127
return 0;
128
129
is_alias:
130
rcu_read_unlock();
131
cell->alias_of = afs_use_cell(p, afs_cell_trace_use_alias);
132
return 1;
133
}
134
135
/*
136
* Query the new cell for a volume from a cell we're already using.
137
*/
138
static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key,
139
struct afs_cell *p)
140
{
141
struct afs_volume *volume, *pvol = NULL;
142
int ret;
143
144
/* Arbitrarily pick a volume from the list. */
145
read_seqlock_excl(&p->volume_lock);
146
if (!RB_EMPTY_ROOT(&p->volumes))
147
pvol = afs_get_volume(rb_entry(p->volumes.rb_node,
148
struct afs_volume, cell_node),
149
afs_volume_trace_get_query_alias);
150
read_sequnlock_excl(&p->volume_lock);
151
if (!pvol)
152
return 0;
153
154
_enter("%s:%s", cell->name, pvol->name);
155
156
/* And see if it's in the new cell. */
157
volume = afs_sample_volume(cell, key, pvol->name, pvol->name_len);
158
if (IS_ERR(volume)) {
159
afs_put_volume(pvol, afs_volume_trace_put_query_alias);
160
if (PTR_ERR(volume) != -ENOMEDIUM)
161
return PTR_ERR(volume);
162
/* That volume is not in the new cell, so not an alias */
163
return 0;
164
}
165
166
/* The new cell has a like-named volume also - compare volume ID,
167
* server and address lists.
168
*/
169
ret = 0;
170
if (pvol->vid == volume->vid) {
171
rcu_read_lock();
172
if (afs_compare_volume_slists(volume, pvol))
173
ret = 1;
174
rcu_read_unlock();
175
}
176
177
afs_put_volume(volume, afs_volume_trace_put_query_alias);
178
afs_put_volume(pvol, afs_volume_trace_put_query_alias);
179
return ret;
180
}
181
182
/*
183
* Query the new cell for volumes we know exist in cells we're already using.
184
*/
185
static int afs_query_for_alias(struct afs_cell *cell, struct key *key)
186
{
187
struct afs_cell *p;
188
189
_enter("%s", cell->name);
190
191
if (mutex_lock_interruptible(&cell->net->proc_cells_lock) < 0)
192
return -ERESTARTSYS;
193
194
hlist_for_each_entry(p, &cell->net->proc_cells, proc_link) {
195
if (p == cell || p->alias_of)
196
continue;
197
if (RB_EMPTY_ROOT(&p->volumes))
198
continue;
199
if (p->root_volume)
200
continue; /* Ignore cells that have a root.cell volume. */
201
afs_use_cell(p, afs_cell_trace_use_check_alias);
202
mutex_unlock(&cell->net->proc_cells_lock);
203
204
if (afs_query_for_alias_one(cell, key, p) != 0)
205
goto is_alias;
206
207
if (mutex_lock_interruptible(&cell->net->proc_cells_lock) < 0) {
208
afs_unuse_cell(p, afs_cell_trace_unuse_check_alias);
209
return -ERESTARTSYS;
210
}
211
212
afs_unuse_cell(p, afs_cell_trace_unuse_check_alias);
213
}
214
215
mutex_unlock(&cell->net->proc_cells_lock);
216
_leave(" = 0");
217
return 0;
218
219
is_alias:
220
cell->alias_of = p; /* Transfer our ref */
221
return 1;
222
}
223
224
/*
225
* Look up a VLDB record for a volume.
226
*/
227
static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
228
{
229
struct afs_vl_cursor vc;
230
char *cell_name = ERR_PTR(-EDESTADDRREQ);
231
bool skipped = false, not_skipped = false;
232
int ret;
233
234
if (!afs_begin_vlserver_operation(&vc, cell, key))
235
return ERR_PTR(-ERESTARTSYS);
236
237
while (afs_select_vlserver(&vc)) {
238
if (!test_bit(AFS_VLSERVER_FL_IS_YFS, &vc.server->flags)) {
239
vc.call_error = -EOPNOTSUPP;
240
skipped = true;
241
continue;
242
}
243
not_skipped = true;
244
cell_name = afs_yfsvl_get_cell_name(&vc);
245
}
246
247
ret = afs_end_vlserver_operation(&vc);
248
if (skipped && !not_skipped)
249
ret = -EOPNOTSUPP;
250
return ret < 0 ? ERR_PTR(ret) : cell_name;
251
}
252
253
static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
254
{
255
struct afs_cell *master;
256
size_t name_len;
257
char *cell_name;
258
259
cell_name = afs_vl_get_cell_name(cell, key);
260
if (IS_ERR(cell_name))
261
return PTR_ERR(cell_name);
262
263
if (strcmp(cell_name, cell->name) == 0) {
264
kfree(cell_name);
265
return 0;
266
}
267
268
name_len = strlen(cell_name);
269
if (!name_len || name_len > AFS_MAXCELLNAME)
270
master = ERR_PTR(-EOPNOTSUPP);
271
else
272
master = afs_lookup_cell(cell->net, cell_name, name_len, NULL, false,
273
afs_cell_trace_use_lookup_canonical);
274
kfree(cell_name);
275
if (IS_ERR(master))
276
return PTR_ERR(master);
277
278
cell->alias_of = master; /* Transfer our ref */
279
return 1;
280
}
281
282
static int afs_do_cell_detect_alias(struct afs_cell *cell, struct key *key)
283
{
284
struct afs_volume *root_volume;
285
int ret;
286
287
_enter("%s", cell->name);
288
289
ret = yfs_check_canonical_cell_name(cell, key);
290
if (ret != -EOPNOTSUPP)
291
return ret;
292
293
/* Try and get the root.cell volume for comparison with other cells */
294
root_volume = afs_sample_volume(cell, key, "root.cell", 9);
295
if (!IS_ERR(root_volume)) {
296
cell->root_volume = root_volume;
297
return afs_compare_cell_roots(cell);
298
}
299
300
if (PTR_ERR(root_volume) != -ENOMEDIUM)
301
return PTR_ERR(root_volume);
302
303
/* Okay, this cell doesn't have an root.cell volume. We need to
304
* locate some other random volume and use that to check.
305
*/
306
return afs_query_for_alias(cell, key);
307
}
308
309
/*
310
* Check to see if a new cell is an alias of a cell we already have. At this
311
* point we have the cell's volume server list.
312
*
313
* Returns 0 if we didn't detect an alias, 1 if we found an alias and an error
314
* if we had problems gathering the data required. In the case the we did
315
* detect an alias, cell->alias_of is set to point to the assumed master.
316
*/
317
int afs_cell_detect_alias(struct afs_cell *cell, struct key *key)
318
{
319
struct afs_net *net = cell->net;
320
int ret;
321
322
if (mutex_lock_interruptible(&net->cells_alias_lock) < 0)
323
return -ERESTARTSYS;
324
325
if (test_bit(AFS_CELL_FL_CHECK_ALIAS, &cell->flags)) {
326
ret = afs_do_cell_detect_alias(cell, key);
327
if (ret >= 0)
328
clear_bit_unlock(AFS_CELL_FL_CHECK_ALIAS, &cell->flags);
329
} else {
330
ret = cell->alias_of ? 1 : 0;
331
}
332
333
mutex_unlock(&net->cells_alias_lock);
334
335
if (ret == 1)
336
pr_notice("kAFS: Cell %s is an alias of %s\n",
337
cell->name, cell->alias_of->name);
338
return ret;
339
}
340
341