policy: support devices with multiple paths.

As new releases of Linux some time change the name of
a path, some distros keep "legacy" names as well.  This
is useful, but confuses mdadm which assumes each device has
precisely one path.

So change this assumption:  allow a disk to have several
paths, and allow any to match when looking for a policy
which matches a disk.

Reported-and-tested-by: Mariusz Tkaczyk <mariusz.tkaczyk@intel.com>
Signed-off-by: NeilBrown <neilb@suse.com>
Signed-off-by: Jes Sorensen <jsorensen@fb.com>
This commit is contained in:
NeilBrown 2018-11-09 17:12:33 +11:00 committed by Jes Sorensen
parent 6b61128420
commit cd72f9d114
3 changed files with 111 additions and 91 deletions

View File

@ -1080,6 +1080,7 @@ static int partition_try_spare(char *devname, int *dfdp, struct dev_policy *pol,
struct supertype *st2 = NULL;
char *devname = NULL;
unsigned long long devsectors;
char *pathlist[2];
if (de->d_ino == 0 || de->d_name[0] == '.' ||
(de->d_type != DT_LNK && de->d_type != DT_UNKNOWN))
@ -1094,7 +1095,9 @@ static int partition_try_spare(char *devname, int *dfdp, struct dev_policy *pol,
/* This is a partition - skip it */
goto next;
pol2 = path_policy(de->d_name, type_disk);
pathlist[0] = de->d_name;
pathlist[1] = NULL;
pol2 = path_policy(pathlist, type_disk);
domain_merge(&domlist, pol2, st ? st->ss->name : NULL);
if (domain_test(domlist, pol, st ? st->ss->name : NULL) != 1)

View File

@ -1247,7 +1247,7 @@ extern void policyline(char *line, char *type);
extern void policy_add(char *type, ...);
extern void policy_free(void);
extern struct dev_policy *path_policy(char *path, char *type);
extern struct dev_policy *path_policy(char **paths, char *type);
extern struct dev_policy *disk_policy(struct mdinfo *disk);
extern struct dev_policy *devid_policy(int devid);
extern void dev_policy_free(struct dev_policy *p);

195
policy.c
View File

@ -189,15 +189,17 @@ struct dev_policy *pol_find(struct dev_policy *pol, char *name)
return pol;
}
static char *disk_path(struct mdinfo *disk)
static char **disk_paths(struct mdinfo *disk)
{
struct stat stb;
int prefix_len;
DIR *by_path;
char symlink[PATH_MAX] = "/dev/disk/by-path/";
char nm[PATH_MAX];
char **paths;
int cnt = 0;
struct dirent *ent;
int rv;
paths = xmalloc(sizeof(*paths) * (cnt+1));
by_path = opendir(symlink);
if (by_path) {
@ -214,22 +216,13 @@ static char *disk_path(struct mdinfo *disk)
continue;
if (stb.st_rdev != makedev(disk->disk.major, disk->disk.minor))
continue;
closedir(by_path);
return xstrdup(ent->d_name);
paths[cnt++] = xstrdup(ent->d_name);
paths = xrealloc(paths, sizeof(*paths) * (cnt+1));
}
closedir(by_path);
}
/* A NULL path isn't really acceptable - use the devname.. */
sprintf(symlink, "/sys/dev/block/%d:%d", disk->disk.major, disk->disk.minor);
rv = readlink(symlink, nm, sizeof(nm)-1);
if (rv > 0) {
char *dname;
nm[rv] = 0;
dname = strrchr(nm, '/');
if (dname)
return xstrdup(dname + 1);
}
return xstrdup("unknown");
paths[cnt] = NULL;
return paths;
}
char type_part[] = "part";
@ -246,46 +239,6 @@ static char *disk_type(struct mdinfo *disk)
return type_disk;
}
static int pol_match(struct rule *rule, char *path, char *type)
{
/* check if this rule matches on path and type */
int pathok = 0; /* 0 == no path, 1 == match, -1 == no match yet */
int typeok = 0;
while (rule) {
if (rule->name == rule_path) {
if (pathok == 0)
pathok = -1;
if (path && fnmatch(rule->value, path, 0) == 0)
pathok = 1;
}
if (rule->name == rule_type) {
if (typeok == 0)
typeok = -1;
if (type && strcmp(rule->value, type) == 0)
typeok = 1;
}
rule = rule->next;
}
return pathok >= 0 && typeok >= 0;
}
static void pol_merge(struct dev_policy **pol, struct rule *rule)
{
/* copy any name assignments from rule into pol */
struct rule *r;
char *metadata = NULL;
for (r = rule; r ; r = r->next)
if (r->name == pol_metadata)
metadata = r->value;
for (r = rule; r ; r = r->next)
if (r->name == pol_act ||
r->name == pol_domain ||
r->name == pol_auto)
pol_new(pol, r->name, r->value, metadata);
}
static int path_has_part(char *path, char **part)
{
/* check if path ends with "-partNN" and
@ -304,6 +257,62 @@ static int path_has_part(char *path, char **part)
return 1;
}
static int pol_match(struct rule *rule, char **paths, char *type, char **part)
{
/* Check if this rule matches on any path and type.
* If 'part' is not NULL, then 'path' must end in -partN, which
* we ignore for matching, and return in *part on success.
*/
int pathok = 0; /* 0 == no path, 1 == match, -1 == no match yet */
int typeok = 0;
for (; rule; rule = rule->next) {
if (rule->name == rule_path) {
char *p;
int i;
if (pathok == 0)
pathok = -1;
if (!paths)
continue;
for (i = 0; paths[i]; i++) {
if (part) {
if (!path_has_part(paths[i], &p))
continue;
*p = '\0';
*part = p+1;
}
if (fnmatch(rule->value, paths[i], 0) == 0)
pathok = 1;
if (part)
*p = '-';
}
}
if (rule->name == rule_type) {
if (typeok == 0)
typeok = -1;
if (type && strcmp(rule->value, type) == 0)
typeok = 1;
}
}
return pathok >= 0 && typeok >= 0;
}
static void pol_merge(struct dev_policy **pol, struct rule *rule)
{
/* copy any name assignments from rule into pol */
struct rule *r;
char *metadata = NULL;
for (r = rule; r ; r = r->next)
if (r->name == pol_metadata)
metadata = r->value;
for (r = rule; r ; r = r->next)
if (r->name == pol_act ||
r->name == pol_domain ||
r->name == pol_auto)
pol_new(pol, r->name, r->value, metadata);
}
static void pol_merge_part(struct dev_policy **pol, struct rule *rule, char *part)
{
/* copy any name assignments from rule into pol, appending
@ -352,7 +361,7 @@ static int config_rules_has_path = 0;
* path_policy() gathers policy information for the
* disk described in the given a 'path' and a 'type'.
*/
struct dev_policy *path_policy(char *path, char *type)
struct dev_policy *path_policy(char **paths, char *type)
{
struct pol_rule *rules;
struct dev_policy *pol = NULL;
@ -361,27 +370,24 @@ struct dev_policy *path_policy(char *path, char *type)
rules = config_rules;
while (rules) {
char *part;
char *part = NULL;
if (rules->type == rule_policy)
if (pol_match(rules->rule, path, type))
if (pol_match(rules->rule, paths, type, NULL))
pol_merge(&pol, rules->rule);
if (rules->type == rule_part && strcmp(type, type_part) == 0)
if (path_has_part(path, &part)) {
*part = 0;
if (pol_match(rules->rule, path, type_disk))
pol_merge_part(&pol, rules->rule, part+1);
*part = '-';
}
if (pol_match(rules->rule, paths, type_disk, &part))
pol_merge_part(&pol, rules->rule, part);
rules = rules->next;
}
/* Now add any metadata-specific internal knowledge
* about this path
*/
for (i=0; path && superlist[i]; i++)
for (i=0; paths[0] && superlist[i]; i++)
if (superlist[i]->get_disk_controller_domain) {
const char *d =
superlist[i]->get_disk_controller_domain(path);
superlist[i]->get_disk_controller_domain(
paths[0]);
if (d)
pol_new(&pol, pol_domain, d, superlist[i]->name);
}
@ -400,22 +406,34 @@ void pol_add(struct dev_policy **pol,
pol_dedup(*pol);
}
static void free_paths(char **paths)
{
int i;
if (!paths)
return;
for (i = 0; paths[i]; i++)
free(paths[i]);
free(paths);
}
/*
* disk_policy() gathers policy information for the
* disk described in the given mdinfo (disk.{major,minor}).
*/
struct dev_policy *disk_policy(struct mdinfo *disk)
{
char *path = NULL;
char **paths = NULL;
char *type = disk_type(disk);
struct dev_policy *pol = NULL;
if (config_rules_has_path)
path = disk_path(disk);
paths = disk_paths(disk);
pol = path_policy(path, type);
pol = path_policy(paths, type);
free(path);
free_paths(paths);
return pol;
}
@ -756,27 +774,26 @@ int policy_check_path(struct mdinfo *disk, struct map_ent *array)
{
char path[PATH_MAX];
FILE *f = NULL;
char *id_path = disk_path(disk);
int rv;
char **id_paths = disk_paths(disk);
int i;
int rv = 0;
if (!id_path)
return 0;
for (i = 0; id_paths[i]; i++) {
snprintf(path, PATH_MAX, FAILED_SLOTS_DIR "/%s", id_paths[i]);
f = fopen(path, "r");
if (!f)
continue;
snprintf(path, PATH_MAX, FAILED_SLOTS_DIR "/%s", id_path);
f = fopen(path, "r");
if (!f) {
free(id_path);
return 0;
rv = fscanf(f, " %s %x:%x:%x:%x\n",
array->metadata,
array->uuid,
array->uuid+1,
array->uuid+2,
array->uuid+3);
fclose(f);
break;
}
rv = fscanf(f, " %s %x:%x:%x:%x\n",
array->metadata,
array->uuid,
array->uuid+1,
array->uuid+2,
array->uuid+3);
fclose(f);
free(id_path);
free_paths(id_paths);
return rv == 5;
}