]> git.ipfire.org Git - thirdparty/mdadm.git/blame - policy.c
mdadm: test_and_add device policies implementation
[thirdparty/mdadm.git] / policy.c
CommitLineData
5527fc74
N
1/*
2 * mdadm - manage Linux "md" devices aka RAID arrays.
3 *
4 * Copyright (C) 2001-2009 Neil Brown <neilb@suse.de>
5 *
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 * Author: Neil Brown
22 * Email: <neilb@suse.de>
23 */
24
25#include "mdadm.h"
26#include <dirent.h>
27#include <fnmatch.h>
28#include <ctype.h>
29#include "dlink.h"
30/*
31 * Policy module for mdadm.
32 * A policy statement about a device lists a set of values for each
33 * of a set of names. Each value can have a metadata type as context.
34 *
35 * names include:
36 * action - the actions that can be taken on hot-plug
37 * domain - the domain(s) that the device is part of
38 *
39 * Policy information is extracted from various sources, but
40 * particularly from a set of policy rules in mdadm.conf
41 */
42
2cda7640
ML
43static void pol_new(struct dev_policy **pol, char *name, const char *val,
44 const char *metadata)
5527fc74 45{
503975b9 46 struct dev_policy *n = xmalloc(sizeof(*n));
5527fc74
N
47 const char *real_metadata = NULL;
48 int i;
49
50 n->name = name;
51 n->value = val;
52
53 /* We need to normalise the metadata name */
54 if (metadata) {
55 for (i = 0; superlist[i] ; i++)
56 if (strcmp(metadata, superlist[i]->name) == 0) {
57 real_metadata = superlist[i]->name;
58 break;
59 }
60 if (!real_metadata) {
61 if (strcmp(metadata, "1") == 0 ||
62 strcmp(metadata, "1.0") == 0 ||
63 strcmp(metadata, "1.1") == 0 ||
64 strcmp(metadata, "1.2") == 0)
65 real_metadata = super1.name;
66 }
67 if (!real_metadata) {
2cda7640 68 static const char *prev = NULL;
5527fc74 69 if (prev != metadata) {
e7b84f9d 70 pr_err("metadata=%s unrecognised - ignoring rule\n",
5527fc74
N
71 metadata);
72 prev = metadata;
73 }
74 real_metadata = "unknown";
75 }
76 }
77
78 n->metadata = real_metadata;
79 n->next = *pol;
80 *pol = n;
81}
82
83static int pol_lesseq(struct dev_policy *a, struct dev_policy *b)
84{
85 int cmp;
86
87 if (a->name < b->name)
88 return 1;
89 if (a->name > b->name)
90 return 0;
91
92 cmp = strcmp(a->value, b->value);
93 if (cmp < 0)
94 return 1;
95 if (cmp > 0)
96 return 0;
97
98 return (a->metadata <= b->metadata);
99}
100
101static void pol_sort(struct dev_policy **pol)
102{
103 /* sort policy list in *pol by name/metadata/value
104 * using merge sort
105 */
106
107 struct dev_policy *pl[2];
108 pl[0] = *pol;
109 pl[1] = NULL;
110
111 do {
112 struct dev_policy **plp[2], *p[2];
113 int curr = 0;
114 struct dev_policy nul = { NULL, NULL, NULL, NULL };
115 struct dev_policy *prev = &nul;
116 int next = 0;
117
118 /* p[] are the two lists that we are merging.
119 * plp[] are the ends of the two lists we create
120 * from the merge.
121 * 'curr' is which of plp[] that we are currently
122 * adding items to.
123 * 'next' is which if p[] we will take the next
124 * item from.
125 * 'prev' is that last value, which was placed in
126 * plp[curr].
127 */
128 plp[0] = &pl[0];
129 plp[1] = &pl[1];
130 p[0] = pl[0];
131 p[1] = pl[1];
132
133 /* take least of p[0] and p[1]
134 * if it is larger than prev, add to
135 * plp[curr], else swap curr then add
136 */
137 while (p[0] || p[1]) {
138 if (p[next] == NULL ||
139 (p[1-next] != NULL &&
140 !(pol_lesseq(prev, p[1-next])
37194990
N
141 ^pol_lesseq(prev, p[next])
142 ^pol_lesseq(p[next], p[1-next])))
5527fc74
N
143 )
144 next = 1 - next;
145
146 if (!pol_lesseq(prev, p[next]))
147 curr = 1 - curr;
148
149 *plp[curr] = prev = p[next];
150 plp[curr] = &p[next]->next;
151 p[next] = p[next]->next;
152 }
153 *plp[0] = NULL;
154 *plp[1] = NULL;
155 } while (pl[0] && pl[1]);
156 if (pl[0])
157 *pol = pl[0];
158 else
159 *pol = pl[1];
160}
161
162static void pol_dedup(struct dev_policy *pol)
163{
164 /* This is a sorted list - remove duplicates. */
165 while (pol && pol->next) {
166 if (pol_lesseq(pol->next, pol)) {
167 struct dev_policy *tmp = pol->next;
168 pol->next = tmp->next;
169 free(tmp);
170 } else
171 pol = pol->next;
172 }
173}
174
175/*
176 * pol_find finds the first entry in the policy
177 * list to match name.
178 * If it returns non-NULL there is at least one
179 * value, but how many can only be found by
180 * iterating through the list.
181 */
182struct dev_policy *pol_find(struct dev_policy *pol, char *name)
183{
184 while (pol && pol->name < name)
185 pol = pol->next;
186
187 if (!pol || pol->name != name)
188 return NULL;
189 return pol;
190}
191
cd72f9d1 192static char **disk_paths(struct mdinfo *disk)
5527fc74
N
193{
194 struct stat stb;
195 int prefix_len;
196 DIR *by_path;
197 char symlink[PATH_MAX] = "/dev/disk/by-path/";
cd72f9d1
N
198 char **paths;
199 int cnt = 0;
5527fc74 200 struct dirent *ent;
cd72f9d1
N
201
202 paths = xmalloc(sizeof(*paths) * (cnt+1));
5527fc74
N
203
204 by_path = opendir(symlink);
75a721fd
LD
205 if (by_path) {
206 prefix_len = strlen(symlink);
207 while ((ent = readdir(by_path)) != NULL) {
208 if (ent->d_type != DT_LNK)
209 continue;
210 strncpy(symlink + prefix_len,
211 ent->d_name,
212 sizeof(symlink) - prefix_len);
213 if (stat(symlink, &stb) < 0)
214 continue;
215 if ((stb.st_mode & S_IFMT) != S_IFBLK)
216 continue;
217 if (stb.st_rdev != makedev(disk->disk.major, disk->disk.minor))
218 continue;
cd72f9d1
N
219 paths[cnt++] = xstrdup(ent->d_name);
220 paths = xrealloc(paths, sizeof(*paths) * (cnt+1));
75a721fd 221 }
5527fc74 222 closedir(by_path);
5527fc74 223 }
cd72f9d1
N
224 paths[cnt] = NULL;
225 return paths;
5527fc74
N
226}
227
228char type_part[] = "part";
229char type_disk[] = "disk";
230static char *disk_type(struct mdinfo *disk)
231{
232 char buf[30+20+20];
233 struct stat stb;
234 sprintf(buf, "/sys/dev/block/%d:%d/partition",
235 disk->disk.major, disk->disk.minor);
236 if (stat(buf, &stb) == 0)
237 return type_part;
238 else
239 return type_disk;
240}
241
cd72f9d1 242static int path_has_part(char *path, char **part)
5527fc74 243{
cd72f9d1
N
244 /* check if path ends with "-partNN" and
245 * if it does, place a pointer to "-pathNN"
246 * in 'part'.
247 */
248 int l;
249 if (!path)
250 return 0;
251 l = strlen(path);
252 while (l > 1 && isdigit(path[l-1]))
253 l--;
254 if (l < 5 || strncmp(path+l-5, "-part", 5) != 0)
255 return 0;
256 *part = path+l-5;
257 return 1;
258}
259
260static int pol_match(struct rule *rule, char **paths, char *type, char **part)
261{
262 /* Check if this rule matches on any path and type.
263 * If 'part' is not NULL, then 'path' must end in -partN, which
264 * we ignore for matching, and return in *part on success.
265 */
5527fc74
N
266 int pathok = 0; /* 0 == no path, 1 == match, -1 == no match yet */
267 int typeok = 0;
268
cd72f9d1 269 for (; rule; rule = rule->next) {
5527fc74 270 if (rule->name == rule_path) {
757e5543 271 char *p = NULL;
cd72f9d1 272 int i;
5527fc74
N
273 if (pathok == 0)
274 pathok = -1;
cd72f9d1
N
275 if (!paths)
276 continue;
277 for (i = 0; paths[i]; i++) {
278 if (part) {
279 if (!path_has_part(paths[i], &p))
280 continue;
281 *p = '\0';
282 *part = p+1;
283 }
284 if (fnmatch(rule->value, paths[i], 0) == 0)
285 pathok = 1;
286 if (part)
287 *p = '-';
288 }
5527fc74
N
289 }
290 if (rule->name == rule_type) {
291 if (typeok == 0)
292 typeok = -1;
b451aa48 293 if (type && strcmp(rule->value, type) == 0)
5527fc74
N
294 typeok = 1;
295 }
5527fc74
N
296 }
297 return pathok >= 0 && typeok >= 0;
298}
299
300static void pol_merge(struct dev_policy **pol, struct rule *rule)
301{
302 /* copy any name assignments from rule into pol */
303 struct rule *r;
304 char *metadata = NULL;
305 for (r = rule; r ; r = r->next)
306 if (r->name == pol_metadata)
307 metadata = r->value;
308
309 for (r = rule; r ; r = r->next)
310 if (r->name == pol_act ||
b451aa48
N
311 r->name == pol_domain ||
312 r->name == pol_auto)
5527fc74
N
313 pol_new(pol, r->name, r->value, metadata);
314}
315
5527fc74
N
316static void pol_merge_part(struct dev_policy **pol, struct rule *rule, char *part)
317{
318 /* copy any name assignments from rule into pol, appending
319 * -part to any domain. The string with -part appended is
320 * stored with the rule so it has a lifetime to match
321 * the rule.
322 */
323 struct rule *r;
324 char *metadata = NULL;
325 for (r = rule; r ; r = r->next)
326 if (r->name == pol_metadata)
327 metadata = r->value;
328
329 for (r = rule; r ; r = r->next) {
330 if (r->name == pol_act)
331 pol_new(pol, r->name, r->value, metadata);
332 else if (r->name == pol_domain) {
333 char *dom;
334 int len;
335 if (r->dups == NULL)
336 r->dups = dl_head();
337 len = strlen(r->value);
338 for (dom = dl_next(r->dups); dom != r->dups;
339 dom = dl_next(dom))
340 if (strcmp(dom+len+1, part)== 0)
341 break;
342 if (dom == r->dups) {
343 char *newdom = dl_strndup(
344 r->value, len + 1 + strlen(part));
345 strcat(strcat(newdom, "-"), part);
346 dl_add(r->dups, newdom);
347 dom = newdom;
348 }
349 pol_new(pol, r->name, dom, metadata);
350 }
351 }
352}
353
354static struct pol_rule *config_rules = NULL;
355static struct pol_rule **config_rules_end = NULL;
356static int config_rules_has_path = 0;
357
358/*
359 * most policy comes from a set policy rules that are
360 * read from the config file.
73c9c47c
N
361 * path_policy() gathers policy information for the
362 * disk described in the given a 'path' and a 'type'.
5527fc74 363 */
cd72f9d1 364struct dev_policy *path_policy(char **paths, char *type)
5527fc74 365{
5527fc74
N
366 struct pol_rule *rules;
367 struct dev_policy *pol = NULL;
2cda7640 368 int i;
5527fc74 369
5527fc74
N
370 rules = config_rules;
371
372 while (rules) {
cd72f9d1 373 char *part = NULL;
5527fc74 374 if (rules->type == rule_policy)
cd72f9d1 375 if (pol_match(rules->rule, paths, type, NULL))
5527fc74
N
376 pol_merge(&pol, rules->rule);
377 if (rules->type == rule_part && strcmp(type, type_part) == 0)
cd72f9d1
N
378 if (pol_match(rules->rule, paths, type_disk, &part))
379 pol_merge_part(&pol, rules->rule, part);
5527fc74
N
380 rules = rules->next;
381 }
2cda7640
ML
382
383 /* Now add any metadata-specific internal knowledge
384 * about this path
385 */
467e6a1b 386 for (i=0; paths && paths[0] && superlist[i]; i++)
2cda7640
ML
387 if (superlist[i]->get_disk_controller_domain) {
388 const char *d =
cd72f9d1
N
389 superlist[i]->get_disk_controller_domain(
390 paths[0]);
2cda7640
ML
391 if (d)
392 pol_new(&pol, pol_domain, d, superlist[i]->name);
393 }
394
5527fc74
N
395 pol_sort(&pol);
396 pol_dedup(pol);
73c9c47c
N
397 return pol;
398}
399
5a2e194c
MT
400/**
401 * drive_test_and_add_policies() - get policies for drive and add them to pols.
402 * @st: supertype.
403 * @pols: pointer to pointer of first list entry, cannot be NULL, may point to NULL.
404 * @fd: device descriptor.
405 * @verbose: verbose flag.
406 *
407 * If supertype doesn't support this functionality return success. Use metadata handler to get
408 * policies.
409 */
410mdadm_status_t drive_test_and_add_policies(struct supertype *st, dev_policy_t **pols, int fd,
411 const int verbose)
412{
413 if (!st->ss->test_and_add_drive_policies)
414 return MDADM_STATUS_SUCCESS;
415
416 if (st->ss->test_and_add_drive_policies(pols, fd, verbose) == MDADM_STATUS_SUCCESS) {
417 /* After successful call list cannot be empty */
418 assert(*pols);
419 return MDADM_STATUS_SUCCESS;
420 }
421
422 return MDADM_STATUS_ERROR;
423}
424
425/**
426 * sysfs_test_and_add_policies() - get policies for mddev and add them to pols.
427 * @st: supertype.
428 * @pols: pointer to pointer of first list entry, cannot be NULL, may point to NULL.
429 * @mdi: mdinfo describes the MD array, must have GET_DISKS option.
430 * @verbose: verbose flag.
431 *
432 * If supertype doesn't support this functionality return success. To get policies, all disks
433 * connected to mddev are analyzed.
434 */
435mdadm_status_t sysfs_test_and_add_drive_policies(struct supertype *st, dev_policy_t **pols,
436 struct mdinfo *mdi, const int verbose)
437{
438 struct mdinfo *sd;
439
440 if (!st->ss->test_and_add_drive_policies)
441 return MDADM_STATUS_SUCCESS;
442
443 for (sd = mdi->devs; sd; sd = sd->next) {
444 char *devpath = map_dev(sd->disk.major, sd->disk.minor, 0);
445 int fd = dev_open(devpath, O_RDONLY);
446 int rv;
447
448 if (!is_fd_valid(fd)) {
449 pr_err("Cannot open fd for %s\n", devpath);
450 return MDADM_STATUS_ERROR;
451 }
452
453 rv = drive_test_and_add_policies(st, pols, fd, verbose);
454 close(fd);
455
456 if (rv)
457 return MDADM_STATUS_ERROR;
458 }
459
460 return MDADM_STATUS_SUCCESS;
461}
462
463/**
464 * mddev_test_and_add_policies() - get policies for mddev and add them to pols.
465 * @st: supertype.
466 * @pols: pointer to pointer of first list entry, cannot be NULL, may point to NULL.
467 * @array_fd: MD device descriptor.
468 * @verbose: verbose flag.
469 *
470 * If supertype doesn't support this functionality return success. Use fd to extract disks.
471 */
472mdadm_status_t mddev_test_and_add_drive_policies(struct supertype *st, dev_policy_t **pols,
473 int array_fd, const int verbose)
474{
475 struct mdinfo *sra;
476 int ret;
477
478 if (!st->ss->test_and_add_drive_policies)
479 return MDADM_STATUS_SUCCESS;
480
481 sra = sysfs_read(array_fd, NULL, GET_DEVS);
482 if (!sra) {
483 pr_err("Cannot load sysfs for %s\n", fd2devnm(array_fd));
484 return MDADM_STATUS_ERROR;
485 }
486
487 ret = sysfs_test_and_add_drive_policies(st, pols, sra, verbose);
488
489 sysfs_free(sra);
490 return ret;
491}
492
e78dda3b
N
493void pol_add(struct dev_policy **pol,
494 char *name, char *val,
495 char *metadata)
496{
497 pol_new(pol, name, val, metadata);
498 pol_sort(pol);
499 pol_dedup(*pol);
500}
501
cd72f9d1
N
502static void free_paths(char **paths)
503{
504 int i;
505
506 if (!paths)
507 return;
508
509 for (i = 0; paths[i]; i++)
510 free(paths[i]);
511 free(paths);
512}
513
73c9c47c
N
514/*
515 * disk_policy() gathers policy information for the
516 * disk described in the given mdinfo (disk.{major,minor}).
517 */
518struct dev_policy *disk_policy(struct mdinfo *disk)
519{
cd72f9d1 520 char **paths = NULL;
73c9c47c
N
521 char *type = disk_type(disk);
522 struct dev_policy *pol = NULL;
523
b9b004eb 524 if (config_rules_has_path)
cd72f9d1 525 paths = disk_paths(disk);
73c9c47c 526
cd72f9d1 527 pol = path_policy(paths, type);
73c9c47c 528
cd72f9d1 529 free_paths(paths);
5527fc74
N
530 return pol;
531}
532
4dd2df09 533struct dev_policy *devid_policy(int dev)
4e8d9f0a
N
534{
535 struct mdinfo disk;
536 disk.disk.major = major(dev);
537 disk.disk.minor = minor(dev);
538 return disk_policy(&disk);
539}
540
5527fc74
N
541/*
542 * process policy rules read from config file.
543 */
544
545char rule_path[] = "path";
546char rule_type[] = "type";
547
548char rule_policy[] = "policy";
549char rule_part[] = "part-policy";
550
551char pol_metadata[] = "metadata";
552char pol_act[] = "action";
553char pol_domain[] = "domain";
4e8d9f0a 554char pol_auto[] = "auto";
5527fc74
N
555
556static int try_rule(char *w, char *name, struct rule **rp)
557{
558 struct rule *r;
559 int len = strlen(name);
560 if (strncmp(w, name, len) != 0 ||
561 w[len] != '=')
562 return 0;
503975b9 563 r = xmalloc(sizeof(*r));
5527fc74
N
564 r->next = *rp;
565 r->name = name;
503975b9 566 r->value = xstrdup(w+len+1);
5527fc74
N
567 r->dups = NULL;
568 *rp = r;
569 return 1;
570}
571
572void policyline(char *line, char *type)
573{
574 struct pol_rule *pr;
575 char *w;
576
577 if (config_rules_end == NULL)
578 config_rules_end = &config_rules;
579
503975b9 580 pr = xmalloc(sizeof(*pr));
5527fc74
N
581 pr->type = type;
582 pr->rule = NULL;
583 for (w = dl_next(line); w != line ; w = dl_next(w)) {
584 if (try_rule(w, rule_path, &pr->rule))
585 config_rules_has_path = 1;
586 else if (! try_rule(w, rule_type, &pr->rule) &&
587 ! try_rule(w, pol_metadata, &pr->rule) &&
588 ! try_rule(w, pol_act, &pr->rule) &&
4e8d9f0a
N
589 ! try_rule(w, pol_domain, &pr->rule) &&
590 ! try_rule(w, pol_auto, &pr->rule))
e7b84f9d 591 pr_err("policy rule %s unrecognised and ignored\n",
5527fc74
N
592 w);
593 }
594 pr->next = config_rules;
595 config_rules = pr;
596}
597
4e8d9f0a
N
598void policy_add(char *type, ...)
599{
600 va_list ap;
601 struct pol_rule *pr;
602 char *name, *val;
603
503975b9 604 pr = xmalloc(sizeof(*pr));
4e8d9f0a
N
605 pr->type = type;
606 pr->rule = NULL;
607
608 va_start(ap, type);
609 while ((name = va_arg(ap, char*)) != NULL) {
610 struct rule *r;
611
612 val = va_arg(ap, char*);
503975b9 613 r = xmalloc(sizeof(*r));
4e8d9f0a
N
614 r->next = pr->rule;
615 r->name = name;
503975b9 616 r->value = xstrdup(val);
4e8d9f0a
N
617 r->dups = NULL;
618 pr->rule = r;
619 }
620 pr->next = config_rules;
621 config_rules = pr;
a5cd79fe 622 va_end(ap);
4e8d9f0a
N
623}
624
5527fc74
N
625void policy_free(void)
626{
627 while (config_rules) {
628 struct pol_rule *pr = config_rules;
629 struct rule *r;
630
631 config_rules = config_rules->next;
632
633 for (r = pr->rule; r; ) {
634 struct rule *next = r->next;
635 free(r->value);
636 if (r->dups)
637 free_line(r->dups);
638 free(r);
639 r = next;
640 }
641 free(pr);
642 }
643 config_rules_end = NULL;
644 config_rules_has_path = 0;
645}
646
647void dev_policy_free(struct dev_policy *p)
648{
649 struct dev_policy *t;
650 while (p) {
651 t = p;
652 p = p->next;
653 free(t);
654 }
655}
e3bb5f14 656
2cda7640 657static enum policy_action map_act(const char *act)
e3bb5f14
N
658{
659 if (strcmp(act, "include") == 0)
660 return act_include;
661 if (strcmp(act, "re-add") == 0)
662 return act_re_add;
663 if (strcmp(act, "spare") == 0)
664 return act_spare;
d2db3045
N
665 if (strcmp(act, "spare-same-slot") == 0)
666 return act_spare_same_slot;
e3bb5f14
N
667 if (strcmp(act, "force-spare") == 0)
668 return act_force_spare;
669 return act_err;
670}
671
672static enum policy_action policy_action(struct dev_policy *plist, const char *metadata)
673{
674 enum policy_action rv = act_default;
675 struct dev_policy *p;
676
677 plist = pol_find(plist, pol_act);
678 pol_for_each(p, plist, metadata) {
679 enum policy_action a = map_act(p->value);
680 if (a > rv)
681 rv = a;
682 }
683 return rv;
684}
685
686int policy_action_allows(struct dev_policy *plist, const char *metadata, enum policy_action want)
687{
688 enum policy_action act = policy_action(plist, metadata);
689
690 if (act == act_err)
691 return 0;
692 return (act >= want);
693}
694
695int disk_action_allows(struct mdinfo *disk, const char *metadata, enum policy_action want)
696{
697 struct dev_policy *pol = disk_policy(disk);
698 int rv = policy_action_allows(pol, metadata, want);
699
700 dev_policy_free(pol);
701 return rv;
702}
f5f12c84 703
f5f12c84
N
704/* Domain policy:
705 * Any device can have a list of domains asserted by different policy
706 * statements.
707 * An array also has a list of domains comprising all the domains of
708 * all the devices in an array.
709 * Where an array has a spare-group, that becomes an addition domain for
710 * every device in the array and thus for the array.
711 *
712 * We keep the list of domains in a sorted linked list
713 * As dev policies are already sorted, this is fairly easy to manage.
714 */
715
2cda7640
ML
716static struct domainlist **domain_merge_one(struct domainlist **domp,
717 const char *domain)
f5f12c84
N
718{
719 /* merge a domain name into a sorted list and return the
720 * location of the insertion or match
721 */
722 struct domainlist *dom = *domp;
723
724 while (dom && strcmp(dom->dom, domain) < 0) {
725 domp = &dom->next;
726 dom = *domp;
727 }
728 if (dom == NULL || strcmp(dom->dom, domain) != 0) {
503975b9 729 dom = xmalloc(sizeof(*dom));
f5f12c84
N
730 dom->next = *domp;
731 dom->dom = domain;
732 *domp = dom;
733 }
734 return domp;
735}
736
2cda7640
ML
737#if (DEBUG)
738void dump_policy(struct dev_policy *policy)
739{
740 while (policy) {
741 dprintf("policy: %p name: %s value: %s metadata: %s\n",
742 policy,
743 policy->name,
744 policy->value,
745 policy->metadata);
746 policy = policy->next;
747 }
748}
749#endif
750
f5f12c84
N
751void domain_merge(struct domainlist **domp, struct dev_policy *pollist,
752 const char *metadata)
753{
754 /* Add to 'domp' all the domains in pol that apply to 'metadata'
755 * which are not already in domp
756 */
757 struct dev_policy *pol;
758 pollist = pol_find(pollist, pol_domain);
759 pol_for_each(pol, pollist, metadata)
e78dda3b 760 domain_merge_one(domp, pol->value);
f5f12c84
N
761}
762
763int domain_test(struct domainlist *dom, struct dev_policy *pol,
764 const char *metadata)
765{
766 /* Check that all domains in pol (for metadata) are also in
767 * dom. Both lists are sorted.
768 * If pol has no domains, we don't really know about this device
e5508b36
N
769 * so we allow caller to choose:
770 * -1: has no domains
771 * 0: has domains, not all match
772 * 1: has domains, all match
f5f12c84 773 */
e5508b36 774 int found_any = -1;
3bf94952 775 int has_one_domain = 1;
f5f12c84
N
776 struct dev_policy *p;
777
778 pol = pol_find(pol, pol_domain);
779 pol_for_each(p, pol, metadata) {
780 found_any = 1;
4e8d9f0a 781 while (dom && strcmp(dom->dom, p->value) < 0)
f5f12c84 782 dom = dom->next;
4e8d9f0a 783 if (!dom || strcmp(dom->dom, p->value) != 0)
f5f12c84 784 return 0;
3bf94952
MT
785 if (has_one_domain && metadata && strcmp(metadata, "imsm") == 0)
786 found_any = -1;
787 has_one_domain = 0;
f5f12c84
N
788 }
789 return found_any;
790}
791
4dd2df09 792void domainlist_add_dev(struct domainlist **dom, int devid, const char *metadata)
e78dda3b 793{
4dd2df09 794 struct dev_policy *pol = devid_policy(devid);
e78dda3b
N
795 domain_merge(dom, pol, metadata);
796 dev_policy_free(pol);
797}
798
f5f12c84
N
799struct domainlist *domain_from_array(struct mdinfo *mdi, const char *metadata)
800{
801 struct domainlist *domlist = NULL;
802
75c2df65
N
803 if (!mdi)
804 return NULL;
e78dda3b
N
805 for (mdi = mdi->devs ; mdi ; mdi = mdi->next)
806 domainlist_add_dev(&domlist, makedev(mdi->disk.major,
807 mdi->disk.minor),
808 metadata);
f5f12c84 809
f5f12c84
N
810 return domlist;
811}
812
e78dda3b
N
813void domain_add(struct domainlist **domp, char *domain)
814{
815 domain_merge_one(domp, domain);
816}
817
f5f12c84
N
818void domain_free(struct domainlist *dl)
819{
820 while (dl) {
821 struct domainlist *head = dl;
822 dl = dl->next;
823 free(head);
824 }
825}
403410eb
PC
826
827/*
828 * same-path policy.
829 * Some policy decisions are guided by knowledge of which
830 * array previously owned the device at a given physical location (path).
831 * When removing a device from an array we might record the array against
832 * the path, and when finding a new device, we might look for which
833 * array previously used that path.
834 *
835 * The 'array' is described by a map_ent, and the path by a the disk in an
836 * mdinfo, or a string.
837 */
838
839void policy_save_path(char *id_path, struct map_ent *array)
840{
841 char path[PATH_MAX];
842 FILE *f = NULL;
843
844 if (mkdir(FAILED_SLOTS_DIR, S_IRWXU) < 0 && errno != EEXIST) {
7a862a02 845 pr_err("can't create file to save path to old disk: %s\n", strerror(errno));
403410eb
PC
846 return;
847 }
848
849 snprintf(path, PATH_MAX, FAILED_SLOTS_DIR "/%s", id_path);
850 f = fopen(path, "w");
851 if (!f) {
7a862a02 852 pr_err("can't create file to save path to old disk: %s\n",
403410eb
PC
853 strerror(errno));
854 return;
855 }
856
1c66260d 857 if (fprintf(f, "%20s %08x:%08x:%08x:%08x\n",
403410eb
PC
858 array->metadata,
859 array->uuid[0], array->uuid[1],
860 array->uuid[2], array->uuid[3]) <= 0)
7a862a02 861 pr_err("Failed to write to <id_path> cookie\n");
403410eb
PC
862
863 fclose(f);
864}
865
866int policy_check_path(struct mdinfo *disk, struct map_ent *array)
867{
868 char path[PATH_MAX];
869 FILE *f = NULL;
cd72f9d1
N
870 char **id_paths = disk_paths(disk);
871 int i;
872 int rv = 0;
873
874 for (i = 0; id_paths[i]; i++) {
875 snprintf(path, PATH_MAX, FAILED_SLOTS_DIR "/%s", id_paths[i]);
876 f = fopen(path, "r");
877 if (!f)
878 continue;
879
1c66260d 880 rv = fscanf(f, " %20s %x:%x:%x:%x\n",
cd72f9d1
N
881 array->metadata,
882 array->uuid,
883 array->uuid+1,
884 array->uuid+2,
885 array->uuid+3);
886 fclose(f);
887 break;
e3da7890 888 }
cd72f9d1 889 free_paths(id_paths);
403410eb
PC
890 return rv == 5;
891}
20b60dcd
LM
892
893/* invocation of udev rule file */
894char udev_template_start[] =
895"# do not edit this file, it is automatically generated by mdadm\n"
896"\n";
897
898/* find rule named rule_type and return its value */
899char *find_rule(struct rule *rule, char *rule_type)
900{
5d500228
N
901 while (rule) {
902 if (rule->name == rule_type)
903 return rule->value;
20b60dcd 904
5d500228
N
905 rule = rule->next;
906 }
907 return NULL;
20b60dcd
LM
908}
909
910#define UDEV_RULE_FORMAT \
c8826c3e 911"ACTION==\"add\", SUBSYSTEM==\"block\", " \
20b60dcd 912"ENV{DEVTYPE}==\"%s\", ENV{ID_PATH}==\"%s\", " \
85945e19 913"RUN+=\"" BINDIR "/mdadm --incremental $env{DEVNAME}\"\n"
20b60dcd
LM
914
915#define UDEV_RULE_FORMAT_NOTYPE \
c8826c3e 916"ACTION==\"add\", SUBSYSTEM==\"block\", " \
20b60dcd 917"ENV{ID_PATH}==\"%s\", " \
85945e19 918"RUN+=\"" BINDIR "/mdadm --incremental $env{DEVNAME}\"\n"
20b60dcd
LM
919
920/* Write rule in the rule file. Use format from UDEV_RULE_FORMAT */
921int write_rule(struct rule *rule, int fd, int force_part)
922{
923 char line[1024];
924 char *pth = find_rule(rule, rule_path);
925 char *typ = find_rule(rule, rule_type);
926 if (!pth)
927 return -1;
928
929 if (force_part)
930 typ = type_part;
931 if (typ)
932 snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT, typ, pth);
933 else
934 snprintf(line, sizeof(line) - 1, UDEV_RULE_FORMAT_NOTYPE, pth);
935 return write(fd, line, strlen(line)) == (int)strlen(line);
936}
937
938/* Generate single entry in udev rule basing on POLICY line found in config
939 * file. Take only those with paths, only first occurrence if paths are equal
940 * and if actions supports handling of spares (>=act_spare_same_slot)
941 */
942int generate_entries(int fd)
943{
5d500228
N
944 struct pol_rule *loop, *dup;
945 char *loop_value, *dup_value;
946 int duplicate;
947
948 for (loop = config_rules; loop; loop = loop->next) {
949 if (loop->type != rule_policy && loop->type != rule_part)
950 continue;
951 duplicate = 0;
952
953 /* only policies with paths and with actions supporting
954 * bare disks are considered */
955 loop_value = find_rule(loop->rule, pol_act);
956 if (!loop_value || map_act(loop_value) < act_spare_same_slot)
957 continue;
958 loop_value = find_rule(loop->rule, rule_path);
959 if (!loop_value)
960 continue;
961 for (dup = config_rules; dup != loop; dup = dup->next) {
962 if (dup->type != rule_policy && loop->type != rule_part)
963 continue;
964 dup_value = find_rule(dup->rule, pol_act);
965 if (!dup_value || map_act(dup_value) < act_spare_same_slot)
966 continue;
967 dup_value = find_rule(dup->rule, rule_path);
968 if (!dup_value)
969 continue;
970 if (strcmp(loop_value, dup_value) == 0) {
971 duplicate = 1;
972 break;
973 }
974 }
975
976 /* not a dup or first occurrence */
977 if (!duplicate)
978 if (!write_rule(loop->rule, fd, loop->type == rule_part) )
979 return 0;
980 }
981 return 1;
20b60dcd
LM
982}
983
984/* Write_rules routine creates dynamic udev rules used to handle
985 * hot-plug events for bare devices (and making them spares)
986 */
987int Write_rules(char *rule_name)
988{
5d500228
N
989 int fd;
990 char udev_rule_file[PATH_MAX];
991
992 if (rule_name) {
993 strncpy(udev_rule_file, rule_name, sizeof(udev_rule_file) - 6);
994 udev_rule_file[sizeof(udev_rule_file) - 6] = '\0';
995 strcat(udev_rule_file, ".temp");
996 fd = creat(udev_rule_file,
997 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
998 if (fd == -1)
999 return 1;
1000 } else
1001 fd = 1;
1002
1003 /* write static invocation */
d16a7494
JS
1004 if (write(fd, udev_template_start, sizeof(udev_template_start) - 1) !=
1005 (int)sizeof(udev_template_start) - 1)
5d500228
N
1006 goto abort;
1007
1008 /* iterate, if none created or error occurred, remove file */
1009 if (generate_entries(fd) < 0)
1010 goto abort;
1011
1012 fsync(fd);
1013 if (rule_name) {
1014 close(fd);
1015 rename(udev_rule_file, rule_name);
1016 }
1017 return 0;
20b60dcd 1018abort:
5d500228
N
1019 if (rule_name) {
1020 close(fd);
1021 unlink(udev_rule_file);
1022 }
1023 return 1;
20b60dcd 1024}