heuristics: Create heuristics rules

Instead of using multiple ifs, create heuristics rules to be applied
to each file.

Signed-off-by: Otavio Pontes <otavio.pontes@intel.com>
This commit is contained in:
Otavio Pontes
2020-05-29 14:42:54 -07:00
parent 8d40ad38bd
commit a9301c660a
2 changed files with 158 additions and 69 deletions
+131 -69
View File
@@ -29,91 +29,124 @@
#include "swupd.h"
#include "heuristics.h"
/* trailing slash is to indicate dir itself is expected to exist, but
* contents are ignored */
static bool is_config(char *filename)
typedef int (*compare_fn_t)(const char *s1, const char *s2);
typedef void (*apply_fn_t)(struct file *f);
struct rule {
char *str;
compare_fn_t cmp;
apply_fn_t apply;
};
static void apply_config(struct file *f)
{
if (str_starts_with(filename, "/etc/") == 0) {
return true;
}
return false;
f->is_config = 1;
}
static void config_file_heuristics(struct file *file)
static void apply_state(struct file *f)
{
if (is_config(file->filename)) {
file->is_config = 1;
}
f->is_state = 1;
}
/* trailing slash is to indicate dir itself is expected to exist, but
* contents are ignored */
static bool is_state(char *filename)
static void apply_boot(struct file *f)
{
f->is_boot = 1;
}
static void apply_boot_and_bootmanager(struct file *f)
{
f->is_boot = 1;
globals.need_update_bootmanager = true;
}
static void apply_bootmanager(struct file UNUSED_PARAM *f)
{
globals.need_update_bootmanager = true;
}
static void apply_systemd(struct file UNUSED_PARAM *f)
{
globals.need_systemd_reexec = true;
}
static void apply_src_state(struct file *f)
{
// /usr/src/debug, /usr/src/kernel and everything inside /usr/src/kernel/
// directory is not a state
if (str_cmp(f->filename, "/usr/src/debug") == 0 ||
str_cmp(f->filename, "/usr/src/kernel") == 0 ||
str_starts_with(f->filename, "/usr/src/kernel/") == 0) {
return;
}
f->is_state = 1;
}
static int h_starts_with(const char *s1, const char *s2)
{
return str_starts_with(s1, s2);
}
static int h_strcmp(const char *s1, const char *s2)
{
return str_cmp(s1, s2);
}
static const struct rule heuristic_rules[] = {
// Boot Files
{"/boot/", h_starts_with, apply_boot },
{"/usr/lib/modules/", h_starts_with, apply_boot },
// State files
{"/data", h_starts_with, apply_state },
{"/dev/", h_starts_with, apply_state },
{"/home/", h_starts_with, apply_state },
{"/lost+found", h_starts_with, apply_state },
{"/proc/", h_starts_with, apply_state },
{"/root/", h_starts_with, apply_state },
{"/run/", h_starts_with, apply_state },
{"/sys/", h_starts_with, apply_state },
{"/tmp/", h_starts_with, apply_state },
{"/var/", h_starts_with, apply_state },
// Filtered state on /usr/src
{"/usr/src/", h_starts_with, apply_src_state },
// Config files
{"/etc/", h_starts_with, apply_config },
// Boot managers
{"/usr/bin/bootctl", h_strcmp, apply_bootmanager },
{"/usr/bin/clr-boot-manager", h_strcmp, apply_bootmanager },
{"/usr/bin/gummiboot", h_strcmp, apply_bootmanager },
{"/usr/lib/gummiboot", h_strcmp, apply_bootmanager },
{"/usr/share/syslinux/ldlinux.c32", h_strcmp, apply_bootmanager },
{"/usr/lib/kernel/", h_starts_with, apply_boot_and_bootmanager },
{"/usr/lib/systemd/boot", h_starts_with, apply_boot_and_bootmanager },
// Systemd
{"/usr/lib/systemd/systemd", h_strcmp, apply_systemd},
{ 0 }
};
static bool check_in_mounted_directory(char *filename)
{
if (is_directory_mounted(filename)) {
return true;
}
if ((str_len(filename) == 14) && (str_starts_with(filename, "/usr/src/debug") == 0)) {
return false;
}
/* Compare the first part of the path, first all the entries inside
* kernel directory, then only the kernel directory */
if ((str_starts_with(filename, "/usr/src/kernel/") == 0) ||
((str_len(filename) == 15) && (str_starts_with(filename, "/usr/src/kernel") == 0))) {
return false;
}
if ((str_starts_with(filename, "/data") == 0) ||
(str_starts_with(filename, "/dev/") == 0) ||
(str_starts_with(filename, "/home/") == 0) ||
(str_starts_with(filename, "/lost+found") == 0) ||
(str_starts_with(filename, "/proc/") == 0) ||
(str_starts_with(filename, "/root/") == 0) ||
(str_starts_with(filename, "/run/") == 0) ||
(str_starts_with(filename, "/sys/") == 0) ||
(str_starts_with(filename, "/tmp/") == 0) ||
(str_starts_with(filename, "/usr/src/") == 0) ||
(str_starts_with(filename, "/var/") == 0)) {
return true;
}
return false;
}
static void runtime_state_heuristics(struct file *file)
{
if (is_state(file->filename)) {
if (check_in_mounted_directory(file->filename)) {
file->is_state = 1;
}
}
static void boot_file_heuristics(struct file *file)
{
if ((str_starts_with(file->filename, "/boot/") == 0) ||
(str_starts_with(file->filename, "/usr/lib/modules/") == 0)) {
file->is_boot = 1;
}
if (str_starts_with(file->filename, "/usr/lib/kernel/") == 0 ||
str_starts_with(file->filename, "/usr/lib/systemd/boot") == 0) {
file->is_boot = 1;
globals.need_update_bootmanager = true;
}
if (str_cmp(file->filename, "/usr/lib/systemd/systemd") == 0) {
globals.need_systemd_reexec = true;
}
if (str_cmp(file->filename, "/usr/lib/gummiboot") == 0 ||
str_cmp(file->filename, "/usr/bin/gummiboot") == 0 ||
str_cmp(file->filename, "/usr/bin/bootctl") == 0 ||
str_cmp(file->filename, "/usr/bin/clr-boot-manager") == 0 ||
str_cmp(file->filename, "/usr/share/syslinux/ldlinux.c32") == 0) {
globals.need_update_bootmanager = true;
}
}
/* Determines whether or not FILE should be ignored for this swupd action. Note
* that boot files are ignored only if they are marked as deleted; this does
* not happen in current manifests produced by swupd-server, but this check is
@@ -131,21 +164,50 @@ static void check_ignore_file(struct file *file)
}
}
static void apply_heuristics_for_file(struct file *file)
static struct rule *dup_rule(const struct rule *r)
{
struct rule * rule;
rule = malloc_or_die(sizeof(struct rule));
*rule = *r;
return rule;
}
static void apply_heuristics_for_file(struct list *rules, struct file *file)
{
struct list *iter;
// Apply all rules
for (iter = rules; iter; iter = iter->next) {
const struct rule *rule = iter->data;
if (rule->cmp(file->filename, rule->str) == 0) {
rule->apply(file);
}
}
runtime_state_heuristics(file);
boot_file_heuristics(file);
config_file_heuristics(file);
check_ignore_file(file);
}
void heuristics_apply(struct list *files)
{
struct list *rules = NULL;
struct list *iter;
struct file *file;
int i;
// Create the rules list
for (i = 0; heuristic_rules[i].str; i++) {
rules = list_prepend_data(rules, dup_rule(&heuristic_rules[i]));
}
// Apply the rules
for (iter = files; iter; iter = iter->next) {
file = iter->data;
apply_heuristics_for_file(file);
apply_heuristics_for_file(rules, file);
}
list_free_list_and_data(rules, free);
}
+27
View File
@@ -103,6 +103,7 @@ void test_heuristics()
int i;
// Test heuristics on files one by one
for (i = 0; test_files[i].filename; i++) {
struct list *files;
struct file *f;
@@ -123,6 +124,32 @@ void test_heuristics()
list_free_list_and_data(files, free);
}
// Test if no heuristic is ignore when running on a list of all files
struct list *files = NULL;
for (i = 0; test_files[i].filename; i++) {
struct file *f;
f = new_file(strdup_or_die(test_files[i].filename));
files = list_prepend_data(files, f);
}
files = list_sort(files, cmp_file_filename_is_deleted);
heuristics_apply(files);
for (i = 0; test_files[i].filename; i++) {
struct list *iter;
for (iter = files; iter; iter = iter->next) {
struct file *f = iter->data;
if (str_cmp(f->filename, test_files[i].filename) == 0) {
check(f->is_boot == test_files[i].is_boot);
check(f->is_state == test_files[i].is_state);
check(f->is_config == test_files[i].is_config);
check(f->do_not_update == test_files[i].do_not_update);
break;
}
}
}
list_free_list_and_data(files, free_file_data);
}
int main() {