0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/slab.h>
0009 #include <linux/sched.h>
0010 #include <linux/namei.h>
0011 #include <keys/rxrpc-type.h>
0012 #include "internal.h"
0013
0014
0015
0016
0017 static struct afs_volume *afs_sample_volume(struct afs_cell *cell, struct key *key,
0018 const char *name, unsigned int namelen)
0019 {
0020 struct afs_volume *volume;
0021 struct afs_fs_context fc = {
0022 .type = 0,
0023 .volnamesz = namelen,
0024 .volname = name,
0025 .net = cell->net,
0026 .cell = cell,
0027 .key = key,
0028 };
0029
0030 volume = afs_create_volume(&fc);
0031 _leave(" = %p", volume);
0032 return volume;
0033 }
0034
0035
0036
0037
0038 static int afs_compare_addrs(const struct sockaddr_rxrpc *srx_a,
0039 const struct sockaddr_rxrpc *srx_b)
0040 {
0041 short port_a, port_b;
0042 int addr_a, addr_b, diff;
0043
0044 diff = (short)srx_a->transport_type - (short)srx_b->transport_type;
0045 if (diff)
0046 goto out;
0047
0048 switch (srx_a->transport_type) {
0049 case AF_INET: {
0050 const struct sockaddr_in *a = &srx_a->transport.sin;
0051 const struct sockaddr_in *b = &srx_b->transport.sin;
0052 addr_a = ntohl(a->sin_addr.s_addr);
0053 addr_b = ntohl(b->sin_addr.s_addr);
0054 diff = addr_a - addr_b;
0055 if (diff == 0) {
0056 port_a = ntohs(a->sin_port);
0057 port_b = ntohs(b->sin_port);
0058 diff = port_a - port_b;
0059 }
0060 break;
0061 }
0062
0063 case AF_INET6: {
0064 const struct sockaddr_in6 *a = &srx_a->transport.sin6;
0065 const struct sockaddr_in6 *b = &srx_b->transport.sin6;
0066 diff = memcmp(&a->sin6_addr, &b->sin6_addr, 16);
0067 if (diff == 0) {
0068 port_a = ntohs(a->sin6_port);
0069 port_b = ntohs(b->sin6_port);
0070 diff = port_a - port_b;
0071 }
0072 break;
0073 }
0074
0075 default:
0076 WARN_ON(1);
0077 diff = 1;
0078 }
0079
0080 out:
0081 return diff;
0082 }
0083
0084
0085
0086
0087 static int afs_compare_fs_alists(const struct afs_server *server_a,
0088 const struct afs_server *server_b)
0089 {
0090 const struct afs_addr_list *la, *lb;
0091 int a = 0, b = 0, addr_matches = 0;
0092
0093 la = rcu_dereference(server_a->addresses);
0094 lb = rcu_dereference(server_b->addresses);
0095
0096 while (a < la->nr_addrs && b < lb->nr_addrs) {
0097 const struct sockaddr_rxrpc *srx_a = &la->addrs[a];
0098 const struct sockaddr_rxrpc *srx_b = &lb->addrs[b];
0099 int diff = afs_compare_addrs(srx_a, srx_b);
0100
0101 if (diff < 0) {
0102 a++;
0103 } else if (diff > 0) {
0104 b++;
0105 } else {
0106 addr_matches++;
0107 a++;
0108 b++;
0109 }
0110 }
0111
0112 return addr_matches;
0113 }
0114
0115
0116
0117
0118
0119 static int afs_compare_volume_slists(const struct afs_volume *vol_a,
0120 const struct afs_volume *vol_b)
0121 {
0122 const struct afs_server_list *la, *lb;
0123 int i, a = 0, b = 0, uuid_matches = 0, addr_matches = 0;
0124
0125 la = rcu_dereference(vol_a->servers);
0126 lb = rcu_dereference(vol_b->servers);
0127
0128 for (i = 0; i < AFS_MAXTYPES; i++)
0129 if (la->vids[i] != lb->vids[i])
0130 return 0;
0131
0132 while (a < la->nr_servers && b < lb->nr_servers) {
0133 const struct afs_server *server_a = la->servers[a].server;
0134 const struct afs_server *server_b = lb->servers[b].server;
0135 int diff = memcmp(&server_a->uuid, &server_b->uuid, sizeof(uuid_t));
0136
0137 if (diff < 0) {
0138 a++;
0139 } else if (diff > 0) {
0140 b++;
0141 } else {
0142 uuid_matches++;
0143 addr_matches += afs_compare_fs_alists(server_a, server_b);
0144 a++;
0145 b++;
0146 }
0147 }
0148
0149 _leave(" = %d [um %d]", addr_matches, uuid_matches);
0150 return addr_matches;
0151 }
0152
0153
0154
0155
0156 static int afs_compare_cell_roots(struct afs_cell *cell)
0157 {
0158 struct afs_cell *p;
0159
0160 _enter("");
0161
0162 rcu_read_lock();
0163
0164 hlist_for_each_entry_rcu(p, &cell->net->proc_cells, proc_link) {
0165 if (p == cell || p->alias_of)
0166 continue;
0167 if (!p->root_volume)
0168 continue;
0169
0170 if (afs_compare_volume_slists(cell->root_volume, p->root_volume) != 0)
0171 goto is_alias;
0172 }
0173
0174 rcu_read_unlock();
0175 _leave(" = 0");
0176 return 0;
0177
0178 is_alias:
0179 rcu_read_unlock();
0180 cell->alias_of = afs_use_cell(p, afs_cell_trace_use_alias);
0181 return 1;
0182 }
0183
0184
0185
0186
0187 static int afs_query_for_alias_one(struct afs_cell *cell, struct key *key,
0188 struct afs_cell *p)
0189 {
0190 struct afs_volume *volume, *pvol = NULL;
0191 int ret;
0192
0193
0194 read_seqlock_excl(&p->volume_lock);
0195 if (!RB_EMPTY_ROOT(&p->volumes))
0196 pvol = afs_get_volume(rb_entry(p->volumes.rb_node,
0197 struct afs_volume, cell_node),
0198 afs_volume_trace_get_query_alias);
0199 read_sequnlock_excl(&p->volume_lock);
0200 if (!pvol)
0201 return 0;
0202
0203 _enter("%s:%s", cell->name, pvol->name);
0204
0205
0206 volume = afs_sample_volume(cell, key, pvol->name, pvol->name_len);
0207 if (IS_ERR(volume)) {
0208 afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias);
0209 if (PTR_ERR(volume) != -ENOMEDIUM)
0210 return PTR_ERR(volume);
0211
0212 return 0;
0213 }
0214
0215
0216
0217
0218 ret = 0;
0219 if (pvol->vid == volume->vid) {
0220 rcu_read_lock();
0221 if (afs_compare_volume_slists(volume, pvol))
0222 ret = 1;
0223 rcu_read_unlock();
0224 }
0225
0226 afs_put_volume(cell->net, volume, afs_volume_trace_put_query_alias);
0227 afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias);
0228 return ret;
0229 }
0230
0231
0232
0233
0234 static int afs_query_for_alias(struct afs_cell *cell, struct key *key)
0235 {
0236 struct afs_cell *p;
0237
0238 _enter("%s", cell->name);
0239
0240 if (mutex_lock_interruptible(&cell->net->proc_cells_lock) < 0)
0241 return -ERESTARTSYS;
0242
0243 hlist_for_each_entry(p, &cell->net->proc_cells, proc_link) {
0244 if (p == cell || p->alias_of)
0245 continue;
0246 if (RB_EMPTY_ROOT(&p->volumes))
0247 continue;
0248 if (p->root_volume)
0249 continue;
0250 afs_use_cell(p, afs_cell_trace_use_check_alias);
0251 mutex_unlock(&cell->net->proc_cells_lock);
0252
0253 if (afs_query_for_alias_one(cell, key, p) != 0)
0254 goto is_alias;
0255
0256 if (mutex_lock_interruptible(&cell->net->proc_cells_lock) < 0) {
0257 afs_unuse_cell(cell->net, p, afs_cell_trace_unuse_check_alias);
0258 return -ERESTARTSYS;
0259 }
0260
0261 afs_unuse_cell(cell->net, p, afs_cell_trace_unuse_check_alias);
0262 }
0263
0264 mutex_unlock(&cell->net->proc_cells_lock);
0265 _leave(" = 0");
0266 return 0;
0267
0268 is_alias:
0269 cell->alias_of = p;
0270 return 1;
0271 }
0272
0273
0274
0275
0276 static char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
0277 {
0278 struct afs_vl_cursor vc;
0279 char *cell_name = ERR_PTR(-EDESTADDRREQ);
0280 bool skipped = false, not_skipped = false;
0281 int ret;
0282
0283 if (!afs_begin_vlserver_operation(&vc, cell, key))
0284 return ERR_PTR(-ERESTARTSYS);
0285
0286 while (afs_select_vlserver(&vc)) {
0287 if (!test_bit(AFS_VLSERVER_FL_IS_YFS, &vc.server->flags)) {
0288 vc.ac.error = -EOPNOTSUPP;
0289 skipped = true;
0290 continue;
0291 }
0292 not_skipped = true;
0293 cell_name = afs_yfsvl_get_cell_name(&vc);
0294 }
0295
0296 ret = afs_end_vlserver_operation(&vc);
0297 if (skipped && !not_skipped)
0298 ret = -EOPNOTSUPP;
0299 return ret < 0 ? ERR_PTR(ret) : cell_name;
0300 }
0301
0302 static int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
0303 {
0304 struct afs_cell *master;
0305 char *cell_name;
0306
0307 cell_name = afs_vl_get_cell_name(cell, key);
0308 if (IS_ERR(cell_name))
0309 return PTR_ERR(cell_name);
0310
0311 if (strcmp(cell_name, cell->name) == 0) {
0312 kfree(cell_name);
0313 return 0;
0314 }
0315
0316 master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name),
0317 NULL, false);
0318 kfree(cell_name);
0319 if (IS_ERR(master))
0320 return PTR_ERR(master);
0321
0322 cell->alias_of = master;
0323 return 1;
0324 }
0325
0326 static int afs_do_cell_detect_alias(struct afs_cell *cell, struct key *key)
0327 {
0328 struct afs_volume *root_volume;
0329 int ret;
0330
0331 _enter("%s", cell->name);
0332
0333 ret = yfs_check_canonical_cell_name(cell, key);
0334 if (ret != -EOPNOTSUPP)
0335 return ret;
0336
0337
0338 root_volume = afs_sample_volume(cell, key, "root.cell", 9);
0339 if (!IS_ERR(root_volume)) {
0340 cell->root_volume = root_volume;
0341 return afs_compare_cell_roots(cell);
0342 }
0343
0344 if (PTR_ERR(root_volume) != -ENOMEDIUM)
0345 return PTR_ERR(root_volume);
0346
0347
0348
0349
0350 return afs_query_for_alias(cell, key);
0351 }
0352
0353
0354
0355
0356
0357
0358
0359
0360
0361 int afs_cell_detect_alias(struct afs_cell *cell, struct key *key)
0362 {
0363 struct afs_net *net = cell->net;
0364 int ret;
0365
0366 if (mutex_lock_interruptible(&net->cells_alias_lock) < 0)
0367 return -ERESTARTSYS;
0368
0369 if (test_bit(AFS_CELL_FL_CHECK_ALIAS, &cell->flags)) {
0370 ret = afs_do_cell_detect_alias(cell, key);
0371 if (ret >= 0)
0372 clear_bit_unlock(AFS_CELL_FL_CHECK_ALIAS, &cell->flags);
0373 } else {
0374 ret = cell->alias_of ? 1 : 0;
0375 }
0376
0377 mutex_unlock(&net->cells_alias_lock);
0378
0379 if (ret == 1)
0380 pr_notice("kAFS: Cell %s is an alias of %s\n",
0381 cell->name, cell->alias_of->name);
0382 return ret;
0383 }