mirror of
https://github.com/clearlinux/swupd-client.git
synced 2026-09-06 21:51:32 +00:00
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:
+131
-69
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user