18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* AFS cell alias detection
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/sched.h>
108c2ecf20Sopenharmony_ci#include <linux/namei.h>
118c2ecf20Sopenharmony_ci#include <keys/rxrpc-type.h>
128c2ecf20Sopenharmony_ci#include "internal.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*
158c2ecf20Sopenharmony_ci * Sample a volume.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_cistatic struct afs_volume *afs_sample_volume(struct afs_cell *cell, struct key *key,
188c2ecf20Sopenharmony_ci					    const char *name, unsigned int namelen)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	struct afs_volume *volume;
218c2ecf20Sopenharmony_ci	struct afs_fs_context fc = {
228c2ecf20Sopenharmony_ci		.type		= 0, /* Explicitly leave it to the VLDB */
238c2ecf20Sopenharmony_ci		.volnamesz	= namelen,
248c2ecf20Sopenharmony_ci		.volname	= name,
258c2ecf20Sopenharmony_ci		.net		= cell->net,
268c2ecf20Sopenharmony_ci		.cell		= cell,
278c2ecf20Sopenharmony_ci		.key		= key, /* This might need to be something */
288c2ecf20Sopenharmony_ci	};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	volume = afs_create_volume(&fc);
318c2ecf20Sopenharmony_ci	_leave(" = %p", volume);
328c2ecf20Sopenharmony_ci	return volume;
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * Compare two addresses.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_cistatic int afs_compare_addrs(const struct sockaddr_rxrpc *srx_a,
398c2ecf20Sopenharmony_ci			     const struct sockaddr_rxrpc *srx_b)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	short port_a, port_b;
428c2ecf20Sopenharmony_ci	int addr_a, addr_b, diff;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	diff = (short)srx_a->transport_type - (short)srx_b->transport_type;
458c2ecf20Sopenharmony_ci	if (diff)
468c2ecf20Sopenharmony_ci		goto out;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	switch (srx_a->transport_type) {
498c2ecf20Sopenharmony_ci	case AF_INET: {
508c2ecf20Sopenharmony_ci		const struct sockaddr_in *a = &srx_a->transport.sin;
518c2ecf20Sopenharmony_ci		const struct sockaddr_in *b = &srx_b->transport.sin;
528c2ecf20Sopenharmony_ci		addr_a = ntohl(a->sin_addr.s_addr);
538c2ecf20Sopenharmony_ci		addr_b = ntohl(b->sin_addr.s_addr);
548c2ecf20Sopenharmony_ci		diff = addr_a - addr_b;
558c2ecf20Sopenharmony_ci		if (diff == 0) {
568c2ecf20Sopenharmony_ci			port_a = ntohs(a->sin_port);
578c2ecf20Sopenharmony_ci			port_b = ntohs(b->sin_port);
588c2ecf20Sopenharmony_ci			diff = port_a - port_b;
598c2ecf20Sopenharmony_ci		}
608c2ecf20Sopenharmony_ci		break;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	case AF_INET6: {
648c2ecf20Sopenharmony_ci		const struct sockaddr_in6 *a = &srx_a->transport.sin6;
658c2ecf20Sopenharmony_ci		const struct sockaddr_in6 *b = &srx_b->transport.sin6;
668c2ecf20Sopenharmony_ci		diff = memcmp(&a->sin6_addr, &b->sin6_addr, 16);
678c2ecf20Sopenharmony_ci		if (diff == 0) {
688c2ecf20Sopenharmony_ci			port_a = ntohs(a->sin6_port);
698c2ecf20Sopenharmony_ci			port_b = ntohs(b->sin6_port);
708c2ecf20Sopenharmony_ci			diff = port_a - port_b;
718c2ecf20Sopenharmony_ci		}
728c2ecf20Sopenharmony_ci		break;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	default:
768c2ecf20Sopenharmony_ci		WARN_ON(1);
778c2ecf20Sopenharmony_ci		diff = 1;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ciout:
818c2ecf20Sopenharmony_ci	return diff;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/*
858c2ecf20Sopenharmony_ci * Compare the address lists of a pair of fileservers.
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_cistatic int afs_compare_fs_alists(const struct afs_server *server_a,
888c2ecf20Sopenharmony_ci				 const struct afs_server *server_b)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	const struct afs_addr_list *la, *lb;
918c2ecf20Sopenharmony_ci	int a = 0, b = 0, addr_matches = 0;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	la = rcu_dereference(server_a->addresses);
948c2ecf20Sopenharmony_ci	lb = rcu_dereference(server_b->addresses);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	while (a < la->nr_addrs && b < lb->nr_addrs) {
978c2ecf20Sopenharmony_ci		const struct sockaddr_rxrpc *srx_a = &la->addrs[a];
988c2ecf20Sopenharmony_ci		const struct sockaddr_rxrpc *srx_b = &lb->addrs[b];
998c2ecf20Sopenharmony_ci		int diff = afs_compare_addrs(srx_a, srx_b);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		if (diff < 0) {
1028c2ecf20Sopenharmony_ci			a++;
1038c2ecf20Sopenharmony_ci		} else if (diff > 0) {
1048c2ecf20Sopenharmony_ci			b++;
1058c2ecf20Sopenharmony_ci		} else {
1068c2ecf20Sopenharmony_ci			addr_matches++;
1078c2ecf20Sopenharmony_ci			a++;
1088c2ecf20Sopenharmony_ci			b++;
1098c2ecf20Sopenharmony_ci		}
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return addr_matches;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/*
1168c2ecf20Sopenharmony_ci * Compare the fileserver lists of two volumes.  The server lists are sorted in
1178c2ecf20Sopenharmony_ci * order of ascending UUID.
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_cistatic int afs_compare_volume_slists(const struct afs_volume *vol_a,
1208c2ecf20Sopenharmony_ci				     const struct afs_volume *vol_b)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	const struct afs_server_list *la, *lb;
1238c2ecf20Sopenharmony_ci	int i, a = 0, b = 0, uuid_matches = 0, addr_matches = 0;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	la = rcu_dereference(vol_a->servers);
1268c2ecf20Sopenharmony_ci	lb = rcu_dereference(vol_b->servers);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	for (i = 0; i < AFS_MAXTYPES; i++)
1298c2ecf20Sopenharmony_ci		if (la->vids[i] != lb->vids[i])
1308c2ecf20Sopenharmony_ci			return 0;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	while (a < la->nr_servers && b < lb->nr_servers) {
1338c2ecf20Sopenharmony_ci		const struct afs_server *server_a = la->servers[a].server;
1348c2ecf20Sopenharmony_ci		const struct afs_server *server_b = lb->servers[b].server;
1358c2ecf20Sopenharmony_ci		int diff = memcmp(&server_a->uuid, &server_b->uuid, sizeof(uuid_t));
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		if (diff < 0) {
1388c2ecf20Sopenharmony_ci			a++;
1398c2ecf20Sopenharmony_ci		} else if (diff > 0) {
1408c2ecf20Sopenharmony_ci			b++;
1418c2ecf20Sopenharmony_ci		} else {
1428c2ecf20Sopenharmony_ci			uuid_matches++;
1438c2ecf20Sopenharmony_ci			addr_matches += afs_compare_fs_alists(server_a, server_b);
1448c2ecf20Sopenharmony_ci			a++;
1458c2ecf20Sopenharmony_ci			b++;
1468c2ecf20Sopenharmony_ci		}
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	_leave(" = %d [um %d]", addr_matches, uuid_matches);
1508c2ecf20Sopenharmony_ci	return addr_matches;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/*
1548c2ecf20Sopenharmony_ci * Compare root.cell volumes.
1558c2ecf20Sopenharmony_ci */
1568c2ecf20Sopenharmony_cistatic int afs_compare_cell_roots(struct afs_cell *cell)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct afs_cell *p;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	_enter("");
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	rcu_read_lock();
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(p, &cell->net->proc_cells, proc_link) {
1658c2ecf20Sopenharmony_ci		if (p == cell || p->alias_of)
1668c2ecf20Sopenharmony_ci			continue;
1678c2ecf20Sopenharmony_ci		if (!p->root_volume)
1688c2ecf20Sopenharmony_ci			continue; /* Ignore cells that don't have a root.cell volume. */
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		if (afs_compare_volume_slists(cell->root_volume, p->root_volume) != 0)
1718c2ecf20Sopenharmony_ci			goto is_alias;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	rcu_read_unlock();
1758c2ecf20Sopenharmony_ci	_leave(" = 0");
1768c2ecf20Sopenharmony_ci	return 0;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ciis_alias:
1798c2ecf20Sopenharmony_ci	rcu_read_unlock();
1808c2ecf20Sopenharmony_ci	cell->alias_of = afs_use_cell(p, afs_cell_trace_use_alias);
1818c2ecf20Sopenharmony_ci	return 1;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/*
1858c2ecf20Sopenharmony_ci * Query the new cell for a volume from a cell we're already using.
1868c2ecf20Sopenharmony_ci */
1878c2ecf20Sopenharmony_cistatic int afs_query_for_alias_one(struct afs_cell *cell, struct key *key,
1888c2ecf20Sopenharmony_ci				   struct afs_cell *p)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct afs_volume *volume, *pvol = NULL;
1918c2ecf20Sopenharmony_ci	int ret;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* Arbitrarily pick a volume from the list. */
1948c2ecf20Sopenharmony_ci	read_seqlock_excl(&p->volume_lock);
1958c2ecf20Sopenharmony_ci	if (!RB_EMPTY_ROOT(&p->volumes))
1968c2ecf20Sopenharmony_ci		pvol = afs_get_volume(rb_entry(p->volumes.rb_node,
1978c2ecf20Sopenharmony_ci					       struct afs_volume, cell_node),
1988c2ecf20Sopenharmony_ci				      afs_volume_trace_get_query_alias);
1998c2ecf20Sopenharmony_ci	read_sequnlock_excl(&p->volume_lock);
2008c2ecf20Sopenharmony_ci	if (!pvol)
2018c2ecf20Sopenharmony_ci		return 0;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	_enter("%s:%s", cell->name, pvol->name);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	/* And see if it's in the new cell. */
2068c2ecf20Sopenharmony_ci	volume = afs_sample_volume(cell, key, pvol->name, pvol->name_len);
2078c2ecf20Sopenharmony_ci	if (IS_ERR(volume)) {
2088c2ecf20Sopenharmony_ci		afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias);
2098c2ecf20Sopenharmony_ci		if (PTR_ERR(volume) != -ENOMEDIUM)
2108c2ecf20Sopenharmony_ci			return PTR_ERR(volume);
2118c2ecf20Sopenharmony_ci		/* That volume is not in the new cell, so not an alias */
2128c2ecf20Sopenharmony_ci		return 0;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* The new cell has a like-named volume also - compare volume ID,
2168c2ecf20Sopenharmony_ci	 * server and address lists.
2178c2ecf20Sopenharmony_ci	 */
2188c2ecf20Sopenharmony_ci	ret = 0;
2198c2ecf20Sopenharmony_ci	if (pvol->vid == volume->vid) {
2208c2ecf20Sopenharmony_ci		rcu_read_lock();
2218c2ecf20Sopenharmony_ci		if (afs_compare_volume_slists(volume, pvol))
2228c2ecf20Sopenharmony_ci			ret = 1;
2238c2ecf20Sopenharmony_ci		rcu_read_unlock();
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	afs_put_volume(cell->net, volume, afs_volume_trace_put_query_alias);
2278c2ecf20Sopenharmony_ci	afs_put_volume(cell->net, pvol, afs_volume_trace_put_query_alias);
2288c2ecf20Sopenharmony_ci	return ret;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci/*
2328c2ecf20Sopenharmony_ci * Query the new cell for volumes we know exist in cells we're already using.
2338c2ecf20Sopenharmony_ci */
2348c2ecf20Sopenharmony_cistatic int afs_query_for_alias(struct afs_cell *cell, struct key *key)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct afs_cell *p;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	_enter("%s", cell->name);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&cell->net->proc_cells_lock) < 0)
2418c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	hlist_for_each_entry(p, &cell->net->proc_cells, proc_link) {
2448c2ecf20Sopenharmony_ci		if (p == cell || p->alias_of)
2458c2ecf20Sopenharmony_ci			continue;
2468c2ecf20Sopenharmony_ci		if (RB_EMPTY_ROOT(&p->volumes))
2478c2ecf20Sopenharmony_ci			continue;
2488c2ecf20Sopenharmony_ci		if (p->root_volume)
2498c2ecf20Sopenharmony_ci			continue; /* Ignore cells that have a root.cell volume. */
2508c2ecf20Sopenharmony_ci		afs_use_cell(p, afs_cell_trace_use_check_alias);
2518c2ecf20Sopenharmony_ci		mutex_unlock(&cell->net->proc_cells_lock);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		if (afs_query_for_alias_one(cell, key, p) != 0)
2548c2ecf20Sopenharmony_ci			goto is_alias;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci		if (mutex_lock_interruptible(&cell->net->proc_cells_lock) < 0) {
2578c2ecf20Sopenharmony_ci			afs_unuse_cell(cell->net, p, afs_cell_trace_unuse_check_alias);
2588c2ecf20Sopenharmony_ci			return -ERESTARTSYS;
2598c2ecf20Sopenharmony_ci		}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci		afs_unuse_cell(cell->net, p, afs_cell_trace_unuse_check_alias);
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	mutex_unlock(&cell->net->proc_cells_lock);
2658c2ecf20Sopenharmony_ci	_leave(" = 0");
2668c2ecf20Sopenharmony_ci	return 0;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ciis_alias:
2698c2ecf20Sopenharmony_ci	cell->alias_of = p; /* Transfer our ref */
2708c2ecf20Sopenharmony_ci	return 1;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci/*
2748c2ecf20Sopenharmony_ci * Look up a VLDB record for a volume.
2758c2ecf20Sopenharmony_ci */
2768c2ecf20Sopenharmony_cistatic char *afs_vl_get_cell_name(struct afs_cell *cell, struct key *key)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct afs_vl_cursor vc;
2798c2ecf20Sopenharmony_ci	char *cell_name = ERR_PTR(-EDESTADDRREQ);
2808c2ecf20Sopenharmony_ci	bool skipped = false, not_skipped = false;
2818c2ecf20Sopenharmony_ci	int ret;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (!afs_begin_vlserver_operation(&vc, cell, key))
2848c2ecf20Sopenharmony_ci		return ERR_PTR(-ERESTARTSYS);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	while (afs_select_vlserver(&vc)) {
2878c2ecf20Sopenharmony_ci		if (!test_bit(AFS_VLSERVER_FL_IS_YFS, &vc.server->flags)) {
2888c2ecf20Sopenharmony_ci			vc.ac.error = -EOPNOTSUPP;
2898c2ecf20Sopenharmony_ci			skipped = true;
2908c2ecf20Sopenharmony_ci			continue;
2918c2ecf20Sopenharmony_ci		}
2928c2ecf20Sopenharmony_ci		not_skipped = true;
2938c2ecf20Sopenharmony_ci		cell_name = afs_yfsvl_get_cell_name(&vc);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	ret = afs_end_vlserver_operation(&vc);
2978c2ecf20Sopenharmony_ci	if (skipped && !not_skipped)
2988c2ecf20Sopenharmony_ci		ret = -EOPNOTSUPP;
2998c2ecf20Sopenharmony_ci	return ret < 0 ? ERR_PTR(ret) : cell_name;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic int yfs_check_canonical_cell_name(struct afs_cell *cell, struct key *key)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct afs_cell *master;
3058c2ecf20Sopenharmony_ci	char *cell_name;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	cell_name = afs_vl_get_cell_name(cell, key);
3088c2ecf20Sopenharmony_ci	if (IS_ERR(cell_name))
3098c2ecf20Sopenharmony_ci		return PTR_ERR(cell_name);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (strcmp(cell_name, cell->name) == 0) {
3128c2ecf20Sopenharmony_ci		kfree(cell_name);
3138c2ecf20Sopenharmony_ci		return 0;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	master = afs_lookup_cell(cell->net, cell_name, strlen(cell_name),
3178c2ecf20Sopenharmony_ci				 NULL, false);
3188c2ecf20Sopenharmony_ci	kfree(cell_name);
3198c2ecf20Sopenharmony_ci	if (IS_ERR(master))
3208c2ecf20Sopenharmony_ci		return PTR_ERR(master);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	cell->alias_of = master; /* Transfer our ref */
3238c2ecf20Sopenharmony_ci	return 1;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic int afs_do_cell_detect_alias(struct afs_cell *cell, struct key *key)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct afs_volume *root_volume;
3298c2ecf20Sopenharmony_ci	int ret;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	_enter("%s", cell->name);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	ret = yfs_check_canonical_cell_name(cell, key);
3348c2ecf20Sopenharmony_ci	if (ret != -EOPNOTSUPP)
3358c2ecf20Sopenharmony_ci		return ret;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* Try and get the root.cell volume for comparison with other cells */
3388c2ecf20Sopenharmony_ci	root_volume = afs_sample_volume(cell, key, "root.cell", 9);
3398c2ecf20Sopenharmony_ci	if (!IS_ERR(root_volume)) {
3408c2ecf20Sopenharmony_ci		cell->root_volume = root_volume;
3418c2ecf20Sopenharmony_ci		return afs_compare_cell_roots(cell);
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (PTR_ERR(root_volume) != -ENOMEDIUM)
3458c2ecf20Sopenharmony_ci		return PTR_ERR(root_volume);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	/* Okay, this cell doesn't have an root.cell volume.  We need to
3488c2ecf20Sopenharmony_ci	 * locate some other random volume and use that to check.
3498c2ecf20Sopenharmony_ci	 */
3508c2ecf20Sopenharmony_ci	return afs_query_for_alias(cell, key);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci/*
3548c2ecf20Sopenharmony_ci * Check to see if a new cell is an alias of a cell we already have.  At this
3558c2ecf20Sopenharmony_ci * point we have the cell's volume server list.
3568c2ecf20Sopenharmony_ci *
3578c2ecf20Sopenharmony_ci * Returns 0 if we didn't detect an alias, 1 if we found an alias and an error
3588c2ecf20Sopenharmony_ci * if we had problems gathering the data required.  In the case the we did
3598c2ecf20Sopenharmony_ci * detect an alias, cell->alias_of is set to point to the assumed master.
3608c2ecf20Sopenharmony_ci */
3618c2ecf20Sopenharmony_ciint afs_cell_detect_alias(struct afs_cell *cell, struct key *key)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct afs_net *net = cell->net;
3648c2ecf20Sopenharmony_ci	int ret;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&net->cells_alias_lock) < 0)
3678c2ecf20Sopenharmony_ci		return -ERESTARTSYS;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (test_bit(AFS_CELL_FL_CHECK_ALIAS, &cell->flags)) {
3708c2ecf20Sopenharmony_ci		ret = afs_do_cell_detect_alias(cell, key);
3718c2ecf20Sopenharmony_ci		if (ret >= 0)
3728c2ecf20Sopenharmony_ci			clear_bit_unlock(AFS_CELL_FL_CHECK_ALIAS, &cell->flags);
3738c2ecf20Sopenharmony_ci	} else {
3748c2ecf20Sopenharmony_ci		ret = cell->alias_of ? 1 : 0;
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	mutex_unlock(&net->cells_alias_lock);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (ret == 1)
3808c2ecf20Sopenharmony_ci		pr_notice("kAFS: Cell %s is an alias of %s\n",
3818c2ecf20Sopenharmony_ci			  cell->name, cell->alias_of->name);
3828c2ecf20Sopenharmony_ci	return ret;
3838c2ecf20Sopenharmony_ci}
384