if (p[next] == NULL ||
(p[1-next] != NULL &&
!(pol_lesseq(prev, p[1-next])
- ^pol_lesseq(p[1-next], p[next])
- ^pol_lesseq(p[next], prev)))
+ ^pol_lesseq(prev, p[next])
+ ^pol_lesseq(p[next], p[1-next])))
)
next = 1 - next;
/*
* most policy comes from a set policy rules that are
* read from the config file.
- * disk_policy() gathers policy information for the
- * disk described in the given mdinfo (disk.{major,minor}).
+ * path_policy() gathers policy information for the
+ * disk described in the given a 'path' and a 'type'.
*/
-struct dev_policy *disk_policy(struct mdinfo *disk)
+struct dev_policy *path_policy(char *path, char *type)
{
- char *path = NULL;
- char *type = disk_type(disk);
struct pol_rule *rules;
struct dev_policy *pol = NULL;
if (!type)
return NULL;
- if (config_rules_has_path) {
- path = disk_path(disk);
- if (!path)
- return NULL;
- }
rules = config_rules;
}
pol_sort(&pol);
pol_dedup(pol);
+ return pol;
+}
+
+/*
+ * 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 *type = disk_type(disk);
+ struct dev_policy *pol = NULL;
+
+ if (!type)
+ return NULL;
+ if (config_rules_has_path)
+ path = disk_path(disk);
+ if (!path)
+ return NULL;
+
+ pol = path_policy(path, type);
+
free(path);
return pol;
}
+struct dev_policy *devnum_policy(int dev)
+{
+ struct mdinfo disk;
+ disk.disk.major = major(dev);
+ disk.disk.minor = minor(dev);
+ return disk_policy(&disk);
+}
+
/*
* process policy rules read from config file.
*/
char pol_metadata[] = "metadata";
char pol_act[] = "action";
char pol_domain[] = "domain";
+char pol_auto[] = "auto";
static int try_rule(char *w, char *name, struct rule **rp)
{
else if (! try_rule(w, rule_type, &pr->rule) &&
! try_rule(w, pol_metadata, &pr->rule) &&
! try_rule(w, pol_act, &pr->rule) &&
- ! try_rule(w, pol_domain, &pr->rule))
+ ! try_rule(w, pol_domain, &pr->rule) &&
+ ! try_rule(w, pol_auto, &pr->rule))
fprintf(stderr, Name ": policy rule %s unrecognised and ignored\n",
w);
}
config_rules = pr;
}
+void policy_add(char *type, ...)
+{
+ va_list ap;
+ struct pol_rule *pr;
+ char *name, *val;
+
+ pr = malloc(sizeof(*pr));
+ pr->type = type;
+ pr->rule = NULL;
+
+ va_start(ap, type);
+ while ((name = va_arg(ap, char*)) != NULL) {
+ struct rule *r;
+
+ val = va_arg(ap, char*);
+ r = malloc(sizeof(*r));
+ r->next = pr->rule;
+ r->name = name;
+ r->value = strdup(val);
+ r->dups = NULL;
+ pr->rule = r;
+ }
+ pr->next = config_rules;
+ config_rules = pr;
+}
+
void policy_free(void)
{
while (config_rules) {
free(t);
}
}
+
+static enum policy_action map_act(char *act)
+{
+ if (strcmp(act, "include") == 0)
+ return act_include;
+ if (strcmp(act, "re-add") == 0)
+ return act_re_add;
+ if (strcmp(act, "spare") == 0)
+ return act_spare;
+ if (strcmp(act, "force-spare") == 0)
+ return act_force_spare;
+ return act_err;
+}
+
+static enum policy_action policy_action(struct dev_policy *plist, const char *metadata)
+{
+ enum policy_action rv = act_default;
+ struct dev_policy *p;
+
+ plist = pol_find(plist, pol_act);
+ pol_for_each(p, plist, metadata) {
+ enum policy_action a = map_act(p->value);
+ if (a > rv)
+ rv = a;
+ }
+ return rv;
+}
+
+int policy_action_allows(struct dev_policy *plist, const char *metadata, enum policy_action want)
+{
+ enum policy_action act = policy_action(plist, metadata);
+
+ if (act == act_err)
+ return 0;
+ return (act >= want);
+}
+
+int disk_action_allows(struct mdinfo *disk, const char *metadata, enum policy_action want)
+{
+ struct dev_policy *pol = disk_policy(disk);
+ int rv = policy_action_allows(pol, metadata, want);
+
+ dev_policy_free(pol);
+ return rv;
+}
+
+
+/* Domain policy:
+ * Any device can have a list of domains asserted by different policy
+ * statements.
+ * An array also has a list of domains comprising all the domains of
+ * all the devices in an array.
+ * Where an array has a spare-group, that becomes an addition domain for
+ * every device in the array and thus for the array.
+ *
+ * We keep the list of domains in a sorted linked list
+ * As dev policies are already sorted, this is fairly easy to manage.
+ */
+
+static struct domainlist **domain_merge_one(struct domainlist **domp, char *domain)
+{
+ /* merge a domain name into a sorted list and return the
+ * location of the insertion or match
+ */
+ struct domainlist *dom = *domp;
+
+ while (dom && strcmp(dom->dom, domain) < 0) {
+ domp = &dom->next;
+ dom = *domp;
+ }
+ if (dom == NULL || strcmp(dom->dom, domain) != 0) {
+ dom = malloc(sizeof(*dom));
+ dom->next = *domp;
+ dom->dom = domain;
+ *domp = dom;
+ }
+ return domp;
+}
+
+void domain_merge(struct domainlist **domp, struct dev_policy *pollist,
+ const char *metadata)
+{
+ /* Add to 'domp' all the domains in pol that apply to 'metadata'
+ * which are not already in domp
+ */
+ struct dev_policy *pol;
+ pollist = pol_find(pollist, pol_domain);
+ pol_for_each(pol, pollist, metadata)
+ domp = domain_merge_one(domp, pol->value);
+}
+
+int domain_test(struct domainlist *dom, struct dev_policy *pol,
+ const char *metadata)
+{
+ /* Check that all domains in pol (for metadata) are also in
+ * dom. Both lists are sorted.
+ * If pol has no domains, we don't really know about this device
+ * so we reject the match.
+ */
+ int found_any = 0;
+ struct dev_policy *p;
+
+ pol = pol_find(pol, pol_domain);
+ pol_for_each(p, pol, metadata) {
+ found_any = 1;
+ while (dom && strcmp(dom->dom, p->value) < 0)
+ dom = dom->next;
+ if (!dom || strcmp(dom->dom, p->value) != 0)
+ return 0;
+ }
+ return found_any;
+}
+
+struct domainlist *domain_from_array(struct mdinfo *mdi, const char *metadata)
+{
+ struct domainlist *domlist = NULL;
+
+ for (mdi = mdi->devs ; mdi ; mdi = mdi->next) {
+ struct dev_policy *pol = disk_policy(mdi);
+
+ domain_merge(&domlist, pol, metadata);
+ dev_policy_free(pol);
+ }
+ return domlist;
+}
+
+void domain_free(struct domainlist *dl)
+{
+ while (dl) {
+ struct domainlist *head = dl;
+ dl = dl->next;
+ free(head);
+ }
+}