]> git.ipfire.org Git - thirdparty/mdadm.git/blob - policy.c
Provide a mdstat_ent to subarray helper
[thirdparty/mdadm.git] / policy.c
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
43 static void pol_new(struct dev_policy **pol, char *name, const char *val,
44 const char *metadata)
45 {
46 struct dev_policy *n = malloc(sizeof(*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) {
68 static const char *prev = NULL;
69 if (prev != metadata) {
70 fprintf(stderr, Name ": metadata=%s unrecognised - ignoring rule\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
83 static 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
101 static 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])
141 ^pol_lesseq(prev, p[next])
142 ^pol_lesseq(p[next], p[1-next])))
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
162 static 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 */
182 struct 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
192 static char *disk_path(struct mdinfo *disk)
193 {
194 struct stat stb;
195 int prefix_len;
196 DIR *by_path;
197 char symlink[PATH_MAX] = "/dev/disk/by-path/";
198 struct dirent *ent;
199
200 by_path = opendir(symlink);
201 if (!by_path)
202 return NULL;
203 prefix_len = strlen(symlink);
204
205 while ((ent = readdir(by_path)) != NULL) {
206 if (ent->d_type != DT_LNK)
207 continue;
208 strncpy(symlink + prefix_len,
209 ent->d_name,
210 sizeof(symlink) - prefix_len);
211 if (stat(symlink, &stb) < 0)
212 continue;
213 if ((stb.st_mode & S_IFMT) != S_IFBLK)
214 continue;
215 if (stb.st_rdev != makedev(disk->disk.major, disk->disk.minor))
216 continue;
217 closedir(by_path);
218 return strdup(ent->d_name);
219 }
220 closedir(by_path);
221 return NULL;
222 }
223
224 char type_part[] = "part";
225 char type_disk[] = "disk";
226 static char *disk_type(struct mdinfo *disk)
227 {
228 char buf[30+20+20];
229 struct stat stb;
230 sprintf(buf, "/sys/dev/block/%d:%d/partition",
231 disk->disk.major, disk->disk.minor);
232 if (stat(buf, &stb) == 0)
233 return type_part;
234 else
235 return type_disk;
236 }
237
238 static int pol_match(struct rule *rule, char *path, char *type)
239 {
240 /* check if this rule matches on path and type */
241 int pathok = 0; /* 0 == no path, 1 == match, -1 == no match yet */
242 int typeok = 0;
243
244 while (rule) {
245 if (rule->name == rule_path) {
246 if (pathok == 0)
247 pathok = -1;
248 if (fnmatch(rule->value, path, 0) == 0)
249 pathok = 1;
250 }
251 if (rule->name == rule_type) {
252 if (typeok == 0)
253 typeok = -1;
254 if (strcmp(rule->value, type) == 0)
255 typeok = 1;
256 }
257 rule = rule->next;
258 }
259 return pathok >= 0 && typeok >= 0;
260 }
261
262 static void pol_merge(struct dev_policy **pol, struct rule *rule)
263 {
264 /* copy any name assignments from rule into pol */
265 struct rule *r;
266 char *metadata = NULL;
267 for (r = rule; r ; r = r->next)
268 if (r->name == pol_metadata)
269 metadata = r->value;
270
271 for (r = rule; r ; r = r->next)
272 if (r->name == pol_act ||
273 r->name == pol_domain)
274 pol_new(pol, r->name, r->value, metadata);
275 }
276
277 static int path_has_part(char *path, char **part)
278 {
279 /* check if path ends with "-partNN" and
280 * if it does, place a pointer to "-pathNN"
281 * in 'part'.
282 */
283 int l = strlen(path);
284 while (l > 1 && isdigit(path[l-1]))
285 l--;
286 if (l < 5 || strncmp(path+l-5, "-part", 5) != 0)
287 return 0;
288 *part = path+l-4;
289 return 1;
290 }
291
292 static void pol_merge_part(struct dev_policy **pol, struct rule *rule, char *part)
293 {
294 /* copy any name assignments from rule into pol, appending
295 * -part to any domain. The string with -part appended is
296 * stored with the rule so it has a lifetime to match
297 * the rule.
298 */
299 struct rule *r;
300 char *metadata = NULL;
301 for (r = rule; r ; r = r->next)
302 if (r->name == pol_metadata)
303 metadata = r->value;
304
305 for (r = rule; r ; r = r->next) {
306 if (r->name == pol_act)
307 pol_new(pol, r->name, r->value, metadata);
308 else if (r->name == pol_domain) {
309 char *dom;
310 int len;
311 if (r->dups == NULL)
312 r->dups = dl_head();
313 len = strlen(r->value);
314 for (dom = dl_next(r->dups); dom != r->dups;
315 dom = dl_next(dom))
316 if (strcmp(dom+len+1, part)== 0)
317 break;
318 if (dom == r->dups) {
319 char *newdom = dl_strndup(
320 r->value, len + 1 + strlen(part));
321 strcat(strcat(newdom, "-"), part);
322 dl_add(r->dups, newdom);
323 dom = newdom;
324 }
325 pol_new(pol, r->name, dom, metadata);
326 }
327 }
328 }
329
330 static struct pol_rule *config_rules = NULL;
331 static struct pol_rule **config_rules_end = NULL;
332 static int config_rules_has_path = 0;
333
334 /*
335 * most policy comes from a set policy rules that are
336 * read from the config file.
337 * path_policy() gathers policy information for the
338 * disk described in the given a 'path' and a 'type'.
339 */
340 struct dev_policy *path_policy(char *path, char *type)
341 {
342 struct pol_rule *rules;
343 struct dev_policy *pol = NULL;
344 int i;
345
346 if (!type)
347 return NULL;
348
349 rules = config_rules;
350
351 while (rules) {
352 char *part;
353 if (rules->type == rule_policy)
354 if (pol_match(rules->rule, path, type))
355 pol_merge(&pol, rules->rule);
356 if (rules->type == rule_part && strcmp(type, type_part) == 0)
357 if (path_has_part(path, &part)) {
358 *part = 0;
359 if (pol_match(rules->rule, path, type_disk))
360 pol_merge_part(&pol, rules->rule, part+1);
361 *part = '-';
362 }
363 rules = rules->next;
364 }
365
366 /* Now add any metadata-specific internal knowledge
367 * about this path
368 */
369 for (i=0; superlist[i]; i++)
370 if (superlist[i]->get_disk_controller_domain) {
371 const char *d =
372 superlist[i]->get_disk_controller_domain(path);
373 if (d)
374 pol_new(&pol, pol_domain, d, superlist[i]->name);
375 }
376
377 pol_sort(&pol);
378 pol_dedup(pol);
379 return pol;
380 }
381
382 void pol_add(struct dev_policy **pol,
383 char *name, char *val,
384 char *metadata)
385 {
386 pol_new(pol, name, val, metadata);
387 pol_sort(pol);
388 pol_dedup(*pol);
389 }
390
391
392 /*
393 * disk_policy() gathers policy information for the
394 * disk described in the given mdinfo (disk.{major,minor}).
395 */
396 struct dev_policy *disk_policy(struct mdinfo *disk)
397 {
398 char *path = NULL;
399 char *type = disk_type(disk);
400 struct dev_policy *pol = NULL;
401
402 if (!type)
403 return NULL;
404 if (config_rules_has_path)
405 path = disk_path(disk);
406 if (!path)
407 return NULL;
408
409 pol = path_policy(path, type);
410
411 free(path);
412 return pol;
413 }
414
415 struct dev_policy *devnum_policy(int dev)
416 {
417 struct mdinfo disk;
418 disk.disk.major = major(dev);
419 disk.disk.minor = minor(dev);
420 return disk_policy(&disk);
421 }
422
423 /*
424 * process policy rules read from config file.
425 */
426
427 char rule_path[] = "path";
428 char rule_type[] = "type";
429
430 char rule_policy[] = "policy";
431 char rule_part[] = "part-policy";
432
433 char pol_metadata[] = "metadata";
434 char pol_act[] = "action";
435 char pol_domain[] = "domain";
436 char pol_auto[] = "auto";
437
438 static int try_rule(char *w, char *name, struct rule **rp)
439 {
440 struct rule *r;
441 int len = strlen(name);
442 if (strncmp(w, name, len) != 0 ||
443 w[len] != '=')
444 return 0;
445 r = malloc(sizeof(*r));
446 r->next = *rp;
447 r->name = name;
448 r->value = strdup(w+len+1);
449 r->dups = NULL;
450 *rp = r;
451 return 1;
452 }
453
454 void policyline(char *line, char *type)
455 {
456 struct pol_rule *pr;
457 char *w;
458
459 if (config_rules_end == NULL)
460 config_rules_end = &config_rules;
461
462 pr = malloc(sizeof(*pr));
463 pr->type = type;
464 pr->rule = NULL;
465 for (w = dl_next(line); w != line ; w = dl_next(w)) {
466 if (try_rule(w, rule_path, &pr->rule))
467 config_rules_has_path = 1;
468 else if (! try_rule(w, rule_type, &pr->rule) &&
469 ! try_rule(w, pol_metadata, &pr->rule) &&
470 ! try_rule(w, pol_act, &pr->rule) &&
471 ! try_rule(w, pol_domain, &pr->rule) &&
472 ! try_rule(w, pol_auto, &pr->rule))
473 fprintf(stderr, Name ": policy rule %s unrecognised and ignored\n",
474 w);
475 }
476 pr->next = config_rules;
477 config_rules = pr;
478 }
479
480 void policy_add(char *type, ...)
481 {
482 va_list ap;
483 struct pol_rule *pr;
484 char *name, *val;
485
486 pr = malloc(sizeof(*pr));
487 pr->type = type;
488 pr->rule = NULL;
489
490 va_start(ap, type);
491 while ((name = va_arg(ap, char*)) != NULL) {
492 struct rule *r;
493
494 val = va_arg(ap, char*);
495 r = malloc(sizeof(*r));
496 r->next = pr->rule;
497 r->name = name;
498 r->value = strdup(val);
499 r->dups = NULL;
500 pr->rule = r;
501 }
502 pr->next = config_rules;
503 config_rules = pr;
504 }
505
506 void policy_free(void)
507 {
508 while (config_rules) {
509 struct pol_rule *pr = config_rules;
510 struct rule *r;
511
512 config_rules = config_rules->next;
513
514 for (r = pr->rule; r; ) {
515 struct rule *next = r->next;
516 free(r->value);
517 if (r->dups)
518 free_line(r->dups);
519 free(r);
520 r = next;
521 }
522 free(pr);
523 }
524 config_rules_end = NULL;
525 config_rules_has_path = 0;
526 }
527
528 void dev_policy_free(struct dev_policy *p)
529 {
530 struct dev_policy *t;
531 while (p) {
532 t = p;
533 p = p->next;
534 free(t);
535 }
536 }
537
538 static enum policy_action map_act(const char *act)
539 {
540 if (strcmp(act, "include") == 0)
541 return act_include;
542 if (strcmp(act, "re-add") == 0)
543 return act_re_add;
544 if (strcmp(act, "spare") == 0)
545 return act_spare;
546 if (strcmp(act, "spare-same-slot") == 0)
547 return act_spare_same_slot;
548 if (strcmp(act, "force-spare") == 0)
549 return act_force_spare;
550 return act_err;
551 }
552
553 static enum policy_action policy_action(struct dev_policy *plist, const char *metadata)
554 {
555 enum policy_action rv = act_default;
556 struct dev_policy *p;
557
558 plist = pol_find(plist, pol_act);
559 pol_for_each(p, plist, metadata) {
560 enum policy_action a = map_act(p->value);
561 if (a > rv)
562 rv = a;
563 }
564 return rv;
565 }
566
567 int policy_action_allows(struct dev_policy *plist, const char *metadata, enum policy_action want)
568 {
569 enum policy_action act = policy_action(plist, metadata);
570
571 if (act == act_err)
572 return 0;
573 return (act >= want);
574 }
575
576 int disk_action_allows(struct mdinfo *disk, const char *metadata, enum policy_action want)
577 {
578 struct dev_policy *pol = disk_policy(disk);
579 int rv = policy_action_allows(pol, metadata, want);
580
581 dev_policy_free(pol);
582 return rv;
583 }
584
585
586 /* Domain policy:
587 * Any device can have a list of domains asserted by different policy
588 * statements.
589 * An array also has a list of domains comprising all the domains of
590 * all the devices in an array.
591 * Where an array has a spare-group, that becomes an addition domain for
592 * every device in the array and thus for the array.
593 *
594 * We keep the list of domains in a sorted linked list
595 * As dev policies are already sorted, this is fairly easy to manage.
596 */
597
598 static struct domainlist **domain_merge_one(struct domainlist **domp,
599 const char *domain)
600 {
601 /* merge a domain name into a sorted list and return the
602 * location of the insertion or match
603 */
604 struct domainlist *dom = *domp;
605
606 while (dom && strcmp(dom->dom, domain) < 0) {
607 domp = &dom->next;
608 dom = *domp;
609 }
610 if (dom == NULL || strcmp(dom->dom, domain) != 0) {
611 dom = malloc(sizeof(*dom));
612 dom->next = *domp;
613 dom->dom = domain;
614 *domp = dom;
615 }
616 return domp;
617 }
618
619 #if (DEBUG)
620 void dump_policy(struct dev_policy *policy)
621 {
622 while (policy) {
623 dprintf("policy: %p name: %s value: %s metadata: %s\n",
624 policy,
625 policy->name,
626 policy->value,
627 policy->metadata);
628 policy = policy->next;
629 }
630 }
631 #endif
632
633 void domain_merge(struct domainlist **domp, struct dev_policy *pollist,
634 const char *metadata)
635 {
636 /* Add to 'domp' all the domains in pol that apply to 'metadata'
637 * which are not already in domp
638 */
639 struct dev_policy *pol;
640 pollist = pol_find(pollist, pol_domain);
641 pol_for_each(pol, pollist, metadata)
642 domain_merge_one(domp, pol->value);
643 }
644
645 int domain_test(struct domainlist *dom, struct dev_policy *pol,
646 const char *metadata)
647 {
648 /* Check that all domains in pol (for metadata) are also in
649 * dom. Both lists are sorted.
650 * If pol has no domains, we don't really know about this device
651 * so we reject the match.
652 */
653 int found_any = 0;
654 struct dev_policy *p;
655
656 pol = pol_find(pol, pol_domain);
657 pol_for_each(p, pol, metadata) {
658 found_any = 1;
659 while (dom && strcmp(dom->dom, p->value) < 0)
660 dom = dom->next;
661 if (!dom || strcmp(dom->dom, p->value) != 0)
662 return 0;
663 }
664 return found_any;
665 }
666
667 void domainlist_add_dev(struct domainlist **dom, int devnum, const char *metadata)
668 {
669 struct dev_policy *pol = devnum_policy(devnum);
670 domain_merge(dom, pol, metadata);
671 dev_policy_free(pol);
672 }
673
674 struct domainlist *domain_from_array(struct mdinfo *mdi, const char *metadata)
675 {
676 struct domainlist *domlist = NULL;
677
678 for (mdi = mdi->devs ; mdi ; mdi = mdi->next)
679 domainlist_add_dev(&domlist, makedev(mdi->disk.major,
680 mdi->disk.minor),
681 metadata);
682
683 return domlist;
684 }
685
686 void domain_add(struct domainlist **domp, char *domain)
687 {
688 domain_merge_one(domp, domain);
689 }
690
691
692 void domain_free(struct domainlist *dl)
693 {
694 while (dl) {
695 struct domainlist *head = dl;
696 dl = dl->next;
697 free(head);
698 }
699 }
700
701 /*
702 * same-path policy.
703 * Some policy decisions are guided by knowledge of which
704 * array previously owned the device at a given physical location (path).
705 * When removing a device from an array we might record the array against
706 * the path, and when finding a new device, we might look for which
707 * array previously used that path.
708 *
709 * The 'array' is described by a map_ent, and the path by a the disk in an
710 * mdinfo, or a string.
711 */
712
713 void policy_save_path(char *id_path, struct map_ent *array)
714 {
715 char path[PATH_MAX];
716 FILE *f = NULL;
717
718 if (mkdir(FAILED_SLOTS_DIR, S_IRWXU) < 0 && errno != EEXIST) {
719 fprintf(stderr, Name ": can't create file to save path "
720 "to old disk: %s\n", strerror(errno));
721 return;
722 }
723
724 snprintf(path, PATH_MAX, FAILED_SLOTS_DIR "/%s", id_path);
725 f = fopen(path, "w");
726 if (!f) {
727 fprintf(stderr, Name ": can't create file to"
728 " save path to old disk: %s\n",
729 strerror(errno));
730 return;
731 }
732
733 if (fprintf(f, "%s %08x:%08x:%08x:%08x\n",
734 array->metadata,
735 array->uuid[0], array->uuid[1],
736 array->uuid[2], array->uuid[3]) <= 0)
737 fprintf(stderr, Name ": Failed to write to "
738 "<id_path> cookie\n");
739
740 fclose(f);
741 }
742
743 int policy_check_path(struct mdinfo *disk, struct map_ent *array)
744 {
745 char path[PATH_MAX];
746 FILE *f = NULL;
747 char *id_path = disk_path(disk);
748 int rv;
749
750 if (!id_path)
751 return 0;
752
753 snprintf(path, PATH_MAX, FAILED_SLOTS_DIR "/%s", id_path);
754 f = fopen(path, "r");
755 if (!f)
756 return 0;
757
758 rv = fscanf(f, " %s %x:%x:%x:%x\n",
759 array->metadata,
760 array->uuid,
761 array->uuid+1,
762 array->uuid+2,
763 array->uuid+3);
764 fclose(f);
765 return rv == 5;
766 }