diff --git a/aide.spec b/aide.spec index ee5b4041dfda86008fd0fa04d80195b464a7cc04..44b8e84a3cb48b11e6ea22b8851522e16d51f301 100644 --- a/aide.spec +++ b/aide.spec @@ -2,7 +2,7 @@ Name: aide Version: 0.19.1 -Release: 1 +Release: 2 Summary: Advanced Intrusion Detection Environment License: GPLv2+ URL: https://sourceforge.net/projects/aide @@ -22,6 +22,10 @@ BuildRequires: check-devel Requires: libgcrypt-sm3 Patch0: Add-sm3-algorithm-for-aide.patch +Patch1: backport-Define-MAGIC-constants-added-since-Linux-4.9.patch +Patch2: backport-Extend-expiration-dates-of-GPG-key-in-SECURITY.patch +Patch3: backport-Add-info-about-worker-states-to-progress-bar.patch +Patch4: backport-Fix-race-condition-when-adding-new-nodes.patch %description AIDE (Advanced Intrusion Detection Environment, [eyd]) is a file and directory integrity checker. @@ -75,6 +79,15 @@ make check %{_mandir}/*/* %changelog +* Fri Aug 1 2025 liuzhilin - 0.19.1-2 +- Type: enhancement +- ID: NA +- SUG: NA +- DESC: Define MAGIC constants added since Linux 4.9 + Extend expiration dates of GPG key in SECURITY.md + Add info about worker states to progress bar + Fix race condition when adding new nodes + * Fri Jul 11 2025 yixiangzhike - 0.19.1-1 - Type: enhancement - ID: NA diff --git a/backport-Add-info-about-worker-states-to-progress-bar.patch b/backport-Add-info-about-worker-states-to-progress-bar.patch new file mode 100644 index 0000000000000000000000000000000000000000..5f2be0498ba41fe8babbde7c95fa4cce2d55dab8 --- /dev/null +++ b/backport-Add-info-about-worker-states-to-progress-bar.patch @@ -0,0 +1,823 @@ +From a2876aefd4713994388f20c845e7e396add64f3c Mon Sep 17 00:00:00 2001 +From: Hannes von Haugwitz +Date: Mon, 9 Jun 2025 16:56:14 +0200 +Subject: [PATCH] Add info about worker states to progress bar + +--- + ChangeLog | 3 + + NEWS | 5 ++ + include/do_md.h | 2 +- + include/gen_list.h | 2 +- + include/progress.h | 14 ++++- + include/util.h | 4 +- + src/aide.c | 25 ++++---- + src/conf_lex.l | 4 +- + src/db_disk.c | 19 ++++-- + src/db_file.c | 2 +- + src/do_md.c | 13 +++- + src/gen_list.c | 24 ++++---- + src/progress.c | 144 ++++++++++++++++++++++++++++++++++++++++++--- + src/util.c | 82 +++++++++++++++++--------- + 14 files changed, 269 insertions(+), 74 deletions(-) + +diff --git a/ChangeLog b/ChangeLog +index 405fb758..283013db 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -1,3 +1,6 @@ ++2025-06-09 Hannes von Haugwitz ++ * Add info about worker states to progress bar ++ + 2025-05-01 Hannes von Haugwitz + * Extend expiration dates of GPG key in SECURITY.md + +diff --git a/NEWS b/NEWS +index 01846f36..3143f23c 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,3 +1,8 @@ ++Version 0.20 (UNRELEASED) ++ * Add info about worker states to progress bar ++ * Bug fixes ++ * Update documentation ++ + Version 0.19 (2025-04-05) + * BACKWARDS INCOMPATIBLE CHANGES + - switch from libmhash to libnettle +diff --git a/include/do_md.h b/include/do_md.h +index 194c264d..eef9dff1 100644 +--- a/include/do_md.h ++++ b/include/do_md.h +@@ -32,7 +32,7 @@ + + list* do_md(list* file_lst,db_config* conf); + int stat_cmp(struct stat*, struct stat*, bool); +-md_hashsums calc_hashsums(disk_entry *, DB_ATTR_TYPE, ssize_t, bool, const char *); ++md_hashsums calc_hashsums(disk_entry *, DB_ATTR_TYPE, ssize_t, bool, int, const char *); + + #ifdef WITH_ACL + void acl2line(db_line* line, int, const char *); +diff --git a/include/gen_list.h b/include/gen_list.h +index 70dc8cbd..f472f09d 100644 +--- a/include/gen_list.h ++++ b/include/gen_list.h +@@ -45,7 +45,7 @@ void write_tree(seltree*); + match_t check_rxtree(file_t, seltree*, char *, bool, const char *); + match_result check_limit(char*, bool, const char *); + +-struct db_line* get_file_attrs(disk_entry *, DB_ATTR_TYPE, DB_ATTR_TYPE, const char *); ++struct db_line* get_file_attrs(disk_entry *, DB_ATTR_TYPE, DB_ATTR_TYPE, int, const char *); + void add_file_to_tree(seltree*, db_line*, int, const database *, disk_entry *, const char*); + + void print_match(file_t, match_t); +diff --git a/include/progress.h b/include/progress.h +index 72fe6c00..fffc4875 100644 +--- a/include/progress.h ++++ b/include/progress.h +@@ -1,7 +1,7 @@ + /* + * AIDE (Advanced Intrusion Detection Environment) + * +- * Copyright (C) 2023 Hannes von Haugwitz ++ * Copyright (C) 2023,2025 Hannes von Haugwitz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as +@@ -34,9 +34,19 @@ typedef enum progress_state { + PROGRESS_SKIPPED, + } progress_state; + ++typedef enum progress_worker_state { ++ progress_worker_state_idle, ++ progress_worker_state_processing, ++} progress_worker_state; ++ + bool progress_start(void); + void progress_stop(void); + +-void progress_status(progress_state, const char*); ++void progress_worker_state_init(void); ++ ++void update_progress_status(progress_state, const char*); ++ ++void update_progress_worker_status(int, progress_worker_state, void*); ++void update_progress_worker_progress(int, int); + + #endif +diff --git a/include/util.h b/include/util.h +index d4c53fcc..80b2c68b 100644 +--- a/include/util.h ++++ b/include/util.h +@@ -1,7 +1,7 @@ + /* + * AIDE (Advanced Intrusion Detection Environment) + * +- * Copyright (C) 1999-2002, 2006, 2013, 2020-2024 Rami Lehti, Pablo Virolainen, ++ * Copyright (C) 1999-2002, 2006, 2013, 2020-2025 Rami Lehti, Pablo Virolainen, + * Richard van den Berg, Hannes von Haugwitz + * + * This program is free software; you can redistribute it and/or +@@ -76,6 +76,7 @@ void vstderr_prefix_line(const char*, const char*, va_list) + #endif + ; + void stderr_set_line_erasure(bool); ++void stderr_multi_lines(char* *, int); + + const char* btoa(bool); + +@@ -93,6 +94,7 @@ void decode_string(char*); + + char* encode_string(const char*); + ++int print_path(char *, const char *, const char*, int); + char *get_progress_bar_string(const char *, const char *, long unsigned, long unsigned, int, int); + + char* perm_to_char(mode_t perm); +diff --git a/src/aide.c b/src/aide.c +index 2a5ec9c9..4e59e6d1 100644 +--- a/src/aide.c ++++ b/src/aide.c +@@ -110,7 +110,6 @@ static void init_db_sighandler(void) + + static void sig_handler(int signum) + { +- struct winsize winsize; + char *str; + switch(signum){ + case SIGINT : +@@ -139,13 +138,6 @@ static void sig_handler(int signum) + (void) !write(STDERR_FILENO ,str, strlen(str)); + toogle_log_level(LOG_LEVEL_DEBUG); + break; +- case SIGWINCH : +- if(ioctl(STDERR_FILENO, TIOCGWINSZ, &winsize) == -1) { +- conf->progress = 80; +- } else { +- conf->progress = winsize.ws_col; +- } +- break; + } + } + +@@ -701,8 +693,15 @@ int main(int argc,char**argv) + if (stderr_isatty) { + log_msg(LOG_LEVEL_DEBUG, "enable progress bar (stderr refers to a terminal)"); + if (progress_start()) { +- log_msg(LOG_LEVEL_DEBUG, "initialize signal handler for SIGWINCH"); +- signal(SIGWINCH,sig_handler); ++ sigset_t set; ++ ++ sigemptyset(&set); ++ sigaddset(&set, SIGWINCH); ++ ++ if(pthread_sigmask(SIG_BLOCK, &set, NULL)) { ++ log_msg(LOG_LEVEL_ERROR, "%10s: pthread_sigmask failed to set mask for blocked signals", "(main)"); ++ exit(THREAD_ERROR); ++ } + } + } else { + log_msg(LOG_LEVEL_INFO, "disable progress bar (stderr does not refer to a terminal)"); +@@ -721,7 +720,7 @@ int main(int argc,char**argv) + } + + log_msg(LOG_LEVEL_INFO, "parse configuration"); +- progress_status(PROGRESS_CONFIG, NULL); ++ update_progress_status(PROGRESS_CONFIG, NULL); + errorno=parse_config(before, conf->config_file, after); + if (errorno==RETFAIL){ + exit(INVALID_CONFIGURELINE_ERROR); +@@ -734,6 +733,8 @@ int main(int argc,char**argv) + log_msg(LOG_LEVEL_DEBUG, "initialize signal handler for SIGUSR1"); + signal(SIGUSR1,sig_handler); + ++ progress_worker_state_init(); ++ + log_msg(LOG_LEVEL_CONFIG, "report_urls:"); + log_report_urls(LOG_LEVEL_CONFIG); + +@@ -877,7 +878,7 @@ int main(int argc,char**argv) + populate_tree(conf->tree); + + if(conf->action&DO_INIT) { +- progress_status(PROGRESS_WRITEDB, NULL); ++ update_progress_status(PROGRESS_WRITEDB, NULL); + log_msg(LOG_LEVEL_INFO, "write new entries to database: %s", (conf->database_out.url)->raw); + write_tree(conf->tree); + } +diff --git a/src/conf_lex.l b/src/conf_lex.l +index ef59650e..f2925b96 100644 +--- a/src/conf_lex.l ++++ b/src/conf_lex.l +@@ -596,7 +596,7 @@ void conf_lex_string(const char * name, const char *string) { + log_msg(LOG_LEVEL_DEBUG, "parse: '%s'", name); + conf_linenumber = 0; + conf_filename = checked_strdup(name); /* not to be freed, needed for logging */ +- progress_status(PROGRESS_CONFIG, conf_filename); ++ update_progress_status(PROGRESS_CONFIG, conf_filename); + conf_scan_string(string); + } + +@@ -609,7 +609,7 @@ void conf_lex_file(const char * config) { + confin = stdin; + } else { + conf_filename = checked_strdup(config); /* not to be freed, needed for logging */ +- progress_status(PROGRESS_CONFIG, conf_filename); ++ update_progress_status(PROGRESS_CONFIG, conf_filename); + char *expanded_config = expand_tilde(checked_strdup(config)); + confin = fopen( expanded_config, "r" ); + if (!confin) { +diff --git a/src/db_disk.c b/src/db_disk.c +index c1e55898..e4580214 100644 +--- a/src/db_disk.c ++++ b/src/db_disk.c +@@ -45,6 +45,7 @@ + #include "file.h" + #include "gen_list.h" + #include "log.h" ++#include "progress.h" + #include "queue.h" + #include "rx_rule.h" + #include "seltree_struct.h" +@@ -160,7 +161,7 @@ static bool open_for_reading(disk_entry *entry, bool dir_rec, const char *whoami + return false; + } + +-static void process_path(char *path, bool dry_run, const char *whoami) { ++static void process_path(char *path, bool dry_run, int worker_index, const char *whoami) { + db_line *line = NULL; + + LOG_WHOAMI(LOG_LEVEL_DEBUG, "process '%s' (fullpath: '%s')", &path[conf->root_prefix_length], path); +@@ -322,7 +323,7 @@ static void process_path(char *path, bool dry_run, const char *whoami) { + LOG_WHOAMI(LOG_LEVEL_DEBUG, "%s> requested attributes: %s", entry.filename, attrs_str); + free(attrs_str); + +- line = get_file_attrs(&entry, entry.attrs, transition_hashsums, whoami); ++ line = get_file_attrs(&entry, entry.attrs, transition_hashsums, worker_index, whoami); + + /* attr_filename is always needed/returned but never requested */ + DB_ATTR_TYPE returned_attr = (~ATTR(attr_filename) & line->attr); +@@ -346,14 +347,20 @@ static void process_path(char *path, bool dry_run, const char *whoami) { + } + } + +-static void process_disk_entries(bool dry_run, const char *whoami) { ++static void process_disk_entries(bool dry_run, int worker_index, const char *whoami) { + const char * whoami_log_thread = whoami ? whoami : "(main)"; + while (1) { + log_msg(LOG_LEVEL_THREAD, "%10s: process_disk_entries: wait for entries", whoami_log_thread); + char *data = queue_ts_dequeue_wait(queue_worker_entries, whoami_log_thread); + if (data) { + log_msg(LOG_LEVEL_THREAD, "%10s: process_disk_entries: got entry %p from queue of worker entries (path: '%s')", whoami_log_thread, (void*) data, data); +- process_path(data, dry_run, whoami); ++ if (worker_index >=0) { ++ update_progress_worker_status(worker_index, progress_worker_state_processing, data); ++ } ++ process_path(data, dry_run, worker_index, whoami); ++ if (worker_index >=0) { ++ update_progress_worker_status(worker_index, progress_worker_state_idle, NULL); ++ } + free(data); + } else { + log_msg(LOG_LEVEL_THREAD, "%10s: process_disk_entries: queue empty", whoami_log_thread); +@@ -373,7 +380,7 @@ static void * worker(void *arg) { + + log_msg(LOG_LEVEL_THREAD, "%10s: worker: initialized worker thread #%ld", whoami, args.worker_index); + +- process_disk_entries(args.dry_run, whoami); ++ process_disk_entries(args.dry_run, args.worker_index, whoami); + + log_msg(LOG_LEVEL_THREAD, "%10s: worker: exit thread", whoami); + return (void *) pthread_self(); +@@ -391,7 +398,7 @@ void db_scan_disk(bool dry_run) { + queue_ts_enqueue(queue_worker_entries, full_path, whoami_main); + queue_ts_release(queue_worker_entries, whoami_main); + +- process_disk_entries(dry_run, NULL); ++ process_disk_entries(dry_run, 0, NULL); + + queue_ts_free(queue_worker_entries); + } else { +diff --git a/src/db_file.c b/src/db_file.c +index e016e117..20743b45 100644 +--- a/src/db_file.c ++++ b/src/db_file.c +@@ -244,7 +244,7 @@ db_entry_t db_readline_file(database* db, bool include_limited_entries) { + entry.limit = true; + db_parse_log_level = LOG_LEVEL_LIMIT; + } else { +- progress_status(PROGRESS_SKIPPED, NULL); ++ update_progress_status(PROGRESS_SKIPPED, NULL); + break; + } + } +diff --git a/src/do_md.c b/src/do_md.c +index c07fe3f3..453bc114 100644 +--- a/src/do_md.c ++++ b/src/do_md.c +@@ -22,6 +22,7 @@ + + #include "config.h" + #include "db_disk.h" ++#include "progress.h" + #include + + #include +@@ -194,7 +195,7 @@ static int hashsum_close(hashsums_file file) { + return -1; + } + +-md_hashsums calc_hashsums(disk_entry *entry, DB_ATTR_TYPE attr, ssize_t limit_size, bool uncompress, const char *whoami) { ++md_hashsums calc_hashsums(disk_entry *entry, DB_ATTR_TYPE attr, ssize_t limit_size, bool uncompress, int worker_index, const char *whoami) { + md_hashsums md_hash; + md_hash.attrs = 0LU; + +@@ -224,6 +225,9 @@ md_hashsums calc_hashsums(disk_entry *entry, DB_ATTR_TYPE attr, ssize_t limit_si + #if READ_BLOCK_SIZE>SSIZE_MAX + #error "READ_BLOCK_SIZE" is too large. Max value is SSIZE_MAX, and current is READ_BLOCK_SIZE + #endif ++ if (worker_index) { ++ update_progress_worker_progress(worker_index, 0); ++ } + while ((size = hashsum_read(file,buf,READ_BLOCK_SIZE)) > 0) { + + off_t update_md_size; +@@ -250,8 +254,15 @@ md_hashsums calc_hashsums(disk_entry *entry, DB_ATTR_TYPE attr, ssize_t limit_si + } else if (attr&ATTR(attr_growing) && r_size == entry->fs.st_size) { + LOG_WHOAMI(LOG_LEVEL_DEBUG, "hash calculation: stat size (%zi) reached for growing file '%s'", entry->fs.st_size, entry->filename); + break; ++ } else { ++ if (worker_index) { ++ update_progress_worker_progress(worker_index, r_size*100/(limit_size > 0 ? limit_size : entry->fs.st_size)); ++ } + } + } ++ if (worker_index) { ++ update_progress_worker_progress(worker_index, 0); ++ } + free(buf); + hashsum_close(file); + if (size == -1) { +diff --git a/src/gen_list.c b/src/gen_list.c +index 7564fa8f..031a6c90 100644 +--- a/src/gen_list.c ++++ b/src/gen_list.c +@@ -245,7 +245,7 @@ static DB_ATTR_TYPE get_changed_attributes(db_line* l1,db_line* l2, DB_ATTR_TYPE + LOG_WHOAMI(compare_log_level, "┝ old:'%s' has growing attribute set, check for growing hashsums", l1->filename); + LOG_WHOAMI(compare_log_level, "│ compare hashsums of old:'%s' and new:'%s' (limited to old size %lld)", l1->filename, l2->filename, l1->size); + DB_ATTR_TYPE transition_hashsums = get_transition_hashsums(l1->filename, l1->attr, l2->filename, l2->attr); +- md_hashsums hs = calc_hashsums(entry, l2->attr|transition_hashsums, l1->size, false, whoami); ++ md_hashsums hs = calc_hashsums(entry, l2->attr|transition_hashsums, l1->size, false, 0, whoami); + + byte* new_hashsums[num_hashes]; + copy_hashsums(l2->fullpath, &hs, &new_hashsums, whoami); +@@ -431,25 +431,25 @@ void add_file_to_tree(seltree* tree,db_line* file,int db_flags, const database * + + switch (db_flags) { + case DB_OLD: { +- progress_status(PROGRESS_OLDDB, file->filename); ++ update_progress_status(PROGRESS_OLDDB, file->filename); + LOG_WHOAMI(add_entry_log_level, "add old database entry '%s' (%c) to node '%s' (%p) as old data", file->filename, get_f_type_char_from_perm(file->perm), node->path, (void*) node); + node->old_data=file; + break; + } + case DB_NEW|DB_DISK: { +- progress_status(PROGRESS_DISK, file->filename); ++ update_progress_status(PROGRESS_DISK, file->filename); + LOG_WHOAMI(add_entry_log_level, "add disk entry '%s' (%c) to node '%s' (%p) as new data", file->filename, get_f_type_char_from_perm(file->perm), node->path, (void*) node); + node->new_data=file; + break; + } + case DB_NEW: { +- progress_status(PROGRESS_NEWDB, file->filename); ++ update_progress_status(PROGRESS_NEWDB, file->filename); + LOG_WHOAMI(add_entry_log_level, "add new database entry '%s' (%c) to node '%s' (%p) as new data", file->filename, get_f_type_char_from_perm(file->perm), node->path, (void*) node); + node->new_data=file; + break; + } + case DB_OLD|DB_NEW: { +- progress_status(PROGRESS_SKIPPED, NULL); ++ update_progress_status(PROGRESS_SKIPPED, NULL); + node->new_data=file; + node->checked|=NODE_FREE; + LOG_WHOAMI(LOG_LEVEL_LIMIT, "add old database entry '%s' (%c) to node (%p) as new data (entry does not match limit but keep it for database_out)", file->filename, get_f_type_char_from_perm(file->perm), (void*) node); +@@ -512,7 +512,7 @@ void add_file_to_tree(seltree* tree,db_line* file,int db_flags, const database * + + seltree *moved_node = NULL; + +- md_hashsums hs = calc_hashsums(entry, new_file->attr, -1, true, whoami); ++ md_hashsums hs = calc_hashsums(entry, new_file->attr, -1, true, 0, whoami); + if (hs.attrs) { + byte* new_hashsums[num_hashes]; + copy_hashsums(new_file->fullpath, &hs, &new_hashsums, whoami); +@@ -743,7 +743,7 @@ match_t check_rxtree(file_t file, seltree* tree, char* source, bool check_parent + return match; + } + +-db_line* get_file_attrs(disk_entry *file, DB_ATTR_TYPE attrs, DB_ATTR_TYPE extra_hashsums, const char *whoami) { ++db_line* get_file_attrs(disk_entry *file, DB_ATTR_TYPE attrs, DB_ATTR_TYPE extra_hashsums, int worker_index, const char *whoami) { + LOG_WHOAMI(LOG_LEVEL_DEBUG, "get file attributes '%s' (fullpath: '%s')", &file->filename[conf->root_prefix_length], file->filename); + db_line* line=NULL; + time_t cur_time; +@@ -834,7 +834,7 @@ db_line* get_file_attrs(disk_entry *file, DB_ATTR_TYPE attrs, DB_ATTR_TYPE extra + + DB_ATTR_TYPE all_hashsums = get_hashes(true); + if (line->attr&all_hashsums) { +- md_hashsums hs = calc_hashsums(file, line->attr|extra_hashsums, -1, false, whoami); ++ md_hashsums hs = calc_hashsums(file, line->attr|extra_hashsums, -1, false, worker_index, whoami); + if (hs.attrs) { + hashsums2line(&hs,line, whoami); + } else { +@@ -848,7 +848,7 @@ db_line* get_file_attrs(disk_entry *file, DB_ATTR_TYPE attrs, DB_ATTR_TYPE extra + void write_tree(seltree* node) { + pthread_rwlock_rdlock(&node->rwlock); + if (node->checked&DB_NEW) { +- progress_status(PROGRESS_WRITEDB, (node->new_data)->filename); ++ update_progress_status(PROGRESS_WRITEDB, (node->new_data)->filename); + db_writeline(node->new_data,conf); + if (node->checked&NODE_FREE) { + free_db_line(node->new_data); +@@ -865,7 +865,7 @@ void write_tree(seltree* node) { + void populate_tree(seltree* tree) { + db_entry_t entry; + if((conf->action&DO_COMPARE)||(conf->action&DO_DIFF)){ +- progress_status(PROGRESS_OLDDB, NULL); ++ update_progress_status(PROGRESS_OLDDB, NULL); + log_msg(LOG_LEVEL_INFO, "read old entries from database: %s", (conf->database_in.url)->raw); + while((entry = db_readline(&(conf->database_in), conf->action&DO_INIT)).line != NULL) { + if (entry.limit) { +@@ -876,7 +876,7 @@ void populate_tree(seltree* tree) { + } + } + if(conf->action&DO_DIFF){ +- progress_status(PROGRESS_NEWDB, NULL); ++ update_progress_status(PROGRESS_NEWDB, NULL); + log_msg(LOG_LEVEL_INFO, "read new entries from database: %s", (conf->database_new.url)->raw); + while((entry = db_readline(&(conf->database_new), false)).line != NULL){ + add_file_to_tree(tree,entry.line,DB_NEW, &(conf->database_new), NULL, NULL); +@@ -884,7 +884,7 @@ void populate_tree(seltree* tree) { + } + + if((conf->action&DO_INIT)||(conf->action&DO_COMPARE)){ +- progress_status(PROGRESS_DISK, NULL); ++ update_progress_status(PROGRESS_DISK, NULL); + log_msg(LOG_LEVEL_INFO, "read new entries from disk (limit: '%s', root prefix: '%s')", conf->limit?conf->limit:"(none)", conf->root_prefix); + + db_scan_disk(false); +diff --git a/src/progress.c b/src/progress.c +index ea85a68d..373cca27 100644 +--- a/src/progress.c ++++ b/src/progress.c +@@ -20,6 +20,7 @@ + + #include "config.h" + #include ++#include + #include + #include + #include +@@ -45,6 +46,27 @@ static long unsigned num_entries = 0; + static long unsigned num_skipped = 0LU; + static char* path = NULL; + ++typedef struct progress_worker_status { ++ progress_worker_state state; ++ char * data; ++ int percentage; ++} progress_worker_status; ++ ++static progress_worker_status *worker_status = NULL; ++static bool progress_worker_status_enabled = false; ++ ++static char* *lines = NULL; ++ ++static char *get_worker_state_string(progress_worker_state s) { ++ switch (s) { ++ case progress_worker_state_idle: ++ return "idle"; ++ case progress_worker_state_processing: ++ return "processing"; ++ } ++ return NULL; ++} ++ + static char *get_state_string(progress_state s) { + switch (s) { + case PROGRESS_OLDDB: +@@ -65,29 +87,82 @@ static char *get_state_string(progress_state s) { + return NULL; + } + ++static void progress_sig_handler(int signum) { ++ struct winsize winsize; ++ switch(signum){ ++ case SIGWINCH : ++ if(ioctl(STDERR_FILENO, TIOCGWINSZ, &winsize) == -1) { ++ conf->progress = 80; ++ progress_worker_status_enabled = false; ++ } else { ++ conf->progress = winsize.ws_col; ++ progress_worker_status_enabled = (winsize.ws_row > (conf->num_workers + 10)); ++ } ++ break; ++ } ++} ++ + static void * progress_updater( __attribute__((unused)) void *arg) { + const char *whoami = "(progress)"; + log_msg(LOG_LEVEL_THREAD, "%10s: initialized progress_updater thread", whoami); + +- mask_sig(whoami); ++ sigset_t set; ++ ++ sigemptyset(&set); ++ sigaddset(&set, SIGINT); ++ sigaddset(&set, SIGTERM); ++ sigaddset(&set, SIGHUP); ++ sigaddset(&set, SIGUSR1); ++ ++ if(pthread_sigmask(SIG_BLOCK, &set, NULL)) { ++ log_msg(LOG_LEVEL_ERROR, "%10s: pthread_sigmask failed to set mask of blocked signals", whoami); ++ exit(THREAD_ERROR); ++ } ++ ++ log_msg(LOG_LEVEL_DEBUG, "%10s: initialize signal handler for SIGWINCH", whoami); ++ signal(SIGWINCH, progress_sig_handler); + + bool _continue = true; ++ + while (_continue) { + pthread_mutex_lock(&progress_update_mutex); ++ int width = conf->progress; + time_t now = time(NULL); + int elapsed = (unsigned long) now - (unsigned long) conf->start_time; +- char *progress_bar = NULL; ++ int num_of_lines = 1; + switch (state) { ++ case PROGRESS_DISK: ++ if (progress_worker_status_enabled && worker_status) { ++ for (long i = 0 ; i < conf->num_workers; ++i) { ++ int n = 0; ++ int left = width; ++ lines[i+1] = checked_malloc(left + 1); ++ n += snprintf(lines[i+1], left, "worker #%0*ld> %10s", 2, i+1, ++ get_worker_state_string(worker_status[i].state) ++ ); ++ left = width - n; ++ if (worker_status[i].data && left > 12) { ++ n += print_path(&(lines[i+1])[n], worker_status[i].data, " ", left); ++ left = width - n;; ++ if (worker_status[i].percentage > 0 && left >= 7) { ++ snprintf(&(lines[i+1])[n], left, " (%2d%%)", worker_status[i].percentage); ++ } ++ } ++ num_of_lines++; ++ } ++ } ++ /* fall through */ + case PROGRESS_CONFIG: + case PROGRESS_NEWDB: + case PROGRESS_SKIPPED: +- case PROGRESS_DISK: + case PROGRESS_WRITEDB: + case PROGRESS_OLDDB: +- progress_bar = get_progress_bar_string(get_state_string(state), path, num_entries, num_skipped, elapsed, conf->progress); +- stderr_msg("%s\r", progress_bar); +- free(progress_bar); +- progress_bar = NULL; ++ lines[0] = get_progress_bar_string(get_state_string(state), path, num_entries, num_skipped, elapsed, width); ++ stderr_multi_lines(lines, num_of_lines); ++ for (int i = 0 ; i < num_of_lines; ++i) { ++ free(lines[i]); ++ lines[i] = NULL; ++ } + break; + case PROGRESS_CLEAR: + _continue = false; +@@ -154,6 +229,25 @@ static void update_state(progress_state new_state) { + state = new_state; + } + ++void progress_worker_state_init(void) { ++ struct winsize winsize; ++ pthread_mutex_lock(&progress_update_mutex); ++ progress_worker_status_enabled = false; ++ if (conf->progress >= 0 && conf->num_workers > 0) { ++ lines = checked_realloc(lines, (conf->num_workers + 1) * sizeof(char*)); ++ worker_status = checked_malloc(conf->num_workers * sizeof(progress_worker_status)); ++ for (int i = 0 ; i < conf->num_workers ; ++i) { ++ worker_status[i].state = progress_worker_state_idle; ++ worker_status[i].data = NULL; ++ worker_status[i].percentage = 0; ++ } ++ if(ioctl(STDERR_FILENO, TIOCGWINSZ, &winsize) != -1) { ++ progress_worker_status_enabled = (winsize.ws_row > (conf->num_workers + 10)); ++ } ++ } ++ pthread_mutex_unlock(&progress_update_mutex); ++} ++ + bool progress_start(void) { + struct winsize winsize; + +@@ -162,10 +256,12 @@ bool progress_start(void) { + } else { + conf->progress = winsize.ws_col; + } ++ lines = checked_malloc(1 * sizeof(char*)); + if (pthread_create(&progress_updater_thread, NULL, &progress_updater, NULL) != 0) { + log_msg(LOG_LEVEL_WARNING, "failed to start progress_updater thread (disable progress bar)"); + return false; + } ++ + stderr_set_line_erasure(true); + return true; + } +@@ -182,9 +278,13 @@ void progress_stop(void) { + } + log_msg(LOG_LEVEL_THREAD, "%10s: progress_updater thread finished", "(main)"); + } ++ free(lines); ++ if (conf->num_workers) { ++ free(worker_status); ++ } + } + +-void progress_status(progress_state new_state, const char* data) { ++void update_progress_status(progress_state new_state, const char* data) { + pthread_mutex_lock(&progress_update_mutex); + switch (new_state) { + case PROGRESS_CONFIG: +@@ -215,3 +315,31 @@ void progress_status(progress_state new_state, const char* data) { + } + pthread_mutex_unlock(&progress_update_mutex); + } ++ ++void update_progress_worker_progress(int index, int percentage) { ++ pthread_mutex_lock(&progress_update_mutex); ++ if (worker_status) { ++ worker_status[index-1].percentage = percentage; ++ } ++ pthread_mutex_unlock(&progress_update_mutex); ++} ++ ++void update_progress_worker_status(int index, progress_worker_state new_state, void* data) { ++ pthread_mutex_lock(&progress_update_mutex); ++ if (worker_status) { ++ switch (new_state) { ++ case progress_worker_state_processing: ++ if (data) { ++ free(worker_status[index-1].data); ++ worker_status[index-1].data = checked_strdup(data); ++ } ++ break; ++ case progress_worker_state_idle: ++ free(worker_status[index-1].data); ++ worker_status[index-1].data = NULL; ++ break; ++ } ++ worker_status[index-1].state = new_state; ++ } ++ pthread_mutex_unlock(&progress_update_mutex); ++} +diff --git a/src/util.c b/src/util.c +index 900b3f43..707e1cf4 100644 +--- a/src/util.c ++++ b/src/util.c +@@ -1,7 +1,7 @@ + /* + * AIDE (Advanced Intrusion Detection Environment) + * +- * Copyright (C) 1999-2002, 2004-2006, 2010-2011, 2013, 2016, 2019-2023 ++ * Copyright (C) 1999-2002, 2004-2006, 2010-2011, 2013, 2016, 2019-2025 + * Rami Lehti, Pablo Virolainen, Mike Markley, + * Richard van den Berg, Hannes von Haugwitz + * +@@ -53,12 +53,40 @@ + + pthread_mutex_t stderr_mutex = PTHREAD_MUTEX_INITIALIZER; + bool stderr_erase_line = false; ++int stderr_num_lines = 0; + +-void stderr_msg(const char* format, ...) { +- pthread_mutex_lock(&stderr_mutex); ++static void stderr_erase_lines(void) { + if (stderr_erase_line) { + fprintf(stderr, "\33[2K"); ++ if (stderr_num_lines) { ++ for (int i = 0 ; i < stderr_num_lines ; ++i) { ++ fprintf(stderr, "\n\33[2K"); ++ } ++ fprintf(stderr, "\33[%dA", stderr_num_lines); ++ stderr_num_lines = 0; ++ } + } ++} ++void stderr_multi_lines(char* *lines, int num_of_lines) { ++ pthread_mutex_lock(&stderr_mutex); ++ stderr_erase_lines(); ++ for (int i = 0 ; i < num_of_lines; ++i) { ++ fprintf(stderr, "%s", lines[i]); ++ if (i < num_of_lines-1) { ++ fprintf(stderr, "\n"); ++ } ++ } ++ fprintf(stderr, "\r"); ++ stderr_num_lines = num_of_lines-1; ++ if (stderr_num_lines) { ++ fprintf(stderr, "\33[%dA", stderr_num_lines); ++ } ++ pthread_mutex_unlock(&stderr_mutex); ++} ++ ++void stderr_msg(const char* format, ...) { ++ pthread_mutex_lock(&stderr_mutex); ++ stderr_erase_lines(); + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); +@@ -68,9 +96,7 @@ void stderr_msg(const char* format, ...) { + + void vstderr_prefix_line(const char* prefix, const char* format, va_list ap) { + pthread_mutex_lock(&stderr_mutex); +- if (stderr_erase_line) { +- fprintf(stderr, "\33[2K"); +- } ++ stderr_erase_lines(); + fprintf(stderr, "%s: ", prefix); + vfprintf(stderr, format, ap); + fprintf(stderr, "\n"); +@@ -79,9 +105,7 @@ void vstderr_prefix_line(const char* prefix, const char* format, va_list ap) { + + void stderr_set_line_erasure(bool erase_line) { + pthread_mutex_lock(&stderr_mutex); +- if (stderr_erase_line) { +- fprintf(stderr, "\33[2K"); +- } ++ stderr_erase_lines(); + stderr_erase_line = erase_line; + pthread_mutex_unlock(&stderr_mutex); + } +@@ -224,33 +248,23 @@ char* byte_to_base16(const byte* src, size_t ssize) { + return str; + } + +-char *get_progress_bar_string(const char* state_str, const char* path, long unsigned num_entries, long unsigned num_skipped, int elapsed, int length) { +- char *progress_bar = checked_malloc(length+1); +- int n = 0; +- int left = length; +- n += snprintf(&progress_bar[n], left+1, "[%02d:%02d] %s> %lu %s", elapsed/60, elapsed%60, state_str, num_entries, num_entries == 1?"file":"files"); +- left = length-n; +- if (num_skipped && left > 0) { +- n += snprintf(&progress_bar[n], left+1, " (%lu skipped)", num_skipped); +- left = length-n; +- } +- if (path && left > 0) { ++int print_path(char *str, const char *path, const char *prefix, int length) { ++ if (path && length > 0) { + const char *ellipsis = "/..."; +- const char *last_str = ", last "; +- int last_str_len = strlen(last_str); ++ int prefix_len = strlen(prefix); + int ellipsis_len = 0; +- int prefix_len = 0; ++ int prefix_path_len = 0; + + const char *suffix_path = path; +- if ((long) strlen(path) > (left-last_str_len) ) { ++ if ((long) strlen(path) > (length-prefix_len) ) { + const char *first_slash = strchr(path+1, '/'); + if (first_slash) { + ellipsis_len = strlen(ellipsis); +- prefix_len = first_slash - path; ++ prefix_path_len = first_slash - path; + + suffix_path = first_slash+1; + +- int path_left = left - last_str_len - prefix_len - ellipsis_len; ++ int path_left = length - prefix_len - prefix_path_len - ellipsis_len; + while ((long) strlen(suffix_path) > path_left) { + char *slash = strchr(suffix_path+1, '/'); + if (slash) { +@@ -261,8 +275,22 @@ char *get_progress_bar_string(const char* state_str, const char* path, long unsi + } + } + } +- snprintf(&progress_bar[n], left+1, "%s%.*s%.*s%s", last_str, prefix_len, path, ellipsis_len, ellipsis, suffix_path); ++ return snprintf(str, length+1, "%s%.*s%.*s%s", prefix, prefix_path_len, path, ellipsis_len, ellipsis, suffix_path); ++ } ++ return 0; ++} ++ ++char *get_progress_bar_string(const char* state_str, const char* path, long unsigned num_entries, long unsigned num_skipped, int elapsed, int length) { ++ char *progress_bar = checked_malloc(length+1); ++ int n = 0; ++ int left = length; ++ n += snprintf(&progress_bar[n], left+1, "[%02d:%02d] %s> %lu %s", elapsed/60, elapsed%60, state_str, num_entries, num_entries == 1?"file":"files"); ++ left = length-n; ++ if (num_skipped && left > 0) { ++ n += snprintf(&progress_bar[n], left+1, " (%lu skipped)", num_skipped); ++ left = length-n; + } ++ print_path(&progress_bar[n], path, ", last ", left); + return progress_bar; + } diff --git a/backport-Define-MAGIC-constants-added-since-Linux-4.9.patch b/backport-Define-MAGIC-constants-added-since-Linux-4.9.patch new file mode 100644 index 0000000000000000000000000000000000000000..b7a1c0a1fe89bbc9908d1a78c8925ee93c77c14a --- /dev/null +++ b/backport-Define-MAGIC-constants-added-since-Linux-4.9.patch @@ -0,0 +1,42 @@ +From 3a218b2a3affe034bb51506fd0b770eab723ca95 Mon Sep 17 00:00:00 2001 +From: Hannes von Haugwitz +Date: Sun, 20 Apr 2025 23:36:34 +0200 +Subject: [PATCH] Define MAGIC constants added since Linux 4.9 + +* closes: #192 +--- + ChangeLog | 3 +++ + src/file.c | 9 +++++++++ + 2 files changed, 12 insertions(+) + +diff --git a/ChangeLog b/ChangeLog +index 4ec8f9bd..5bd570b1 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -1,3 +1,6 @@ ++2025-04-20 Hannes von Haugwitz ++ * Define MAGIC constants added since Linux 4.9 (closes: #192) ++ + 2025-04-05 Hannes von Haugwitz + * Fix build with additional libraries on non-Linux systems + * Update NEWS file and aide.conf.5 man page +diff --git a/src/file.c b/src/file.c +index 79753f97..c1ad944d 100644 +--- a/src/file.c ++++ b/src/file.c +@@ -24,6 +24,15 @@ + #include + #include + #include ++#ifndef EXFAT_SUPER_MAGIC ++#define EXFAT_SUPER_MAGIC 0x2011BAB0 ++#endif ++#ifndef FUSE_SUPER_MAGIC ++#define FUSE_SUPER_MAGIC 0x65735546 ++#endif ++#ifndef XFS_SUPER_MAGIC ++#define XFS_SUPER_MAGIC 0x58465342 ++#endif + #include + #include + #include "util.h" diff --git a/backport-Extend-expiration-dates-of-GPG-key-in-SECURITY.patch b/backport-Extend-expiration-dates-of-GPG-key-in-SECURITY.patch new file mode 100644 index 0000000000000000000000000000000000000000..a76edb02fa765d0a42c1511f7ab290f1d1fc80a0 --- /dev/null +++ b/backport-Extend-expiration-dates-of-GPG-key-in-SECURITY.patch @@ -0,0 +1,189 @@ +From 2677eff4c7126df76e98148023cdbdc2b18f278f Mon Sep 17 00:00:00 2001 +From: Hannes von Haugwitz +Date: Thu, 1 May 2025 17:17:08 +0200 +Subject: [PATCH] Extend expiration dates of GPG key in SECURITY.md + +--- + ChangeLog | 3 ++ + SECURITY.md | 130 ++++++++++++++++++++++++++-------------------------- + 2 files changed, 68 insertions(+), 65 deletions(-) + +diff --git a/ChangeLog b/ChangeLog +index 5bd570b1..405fb758 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -1,3 +1,6 @@ ++2025-05-01 Hannes von Haugwitz ++ * Extend expiration dates of GPG key in SECURITY.md ++ + 2025-04-20 Hannes von Haugwitz + * Define MAGIC constants added since Linux 4.9 (closes: #192) + +diff --git a/SECURITY.md b/SECURITY.md +index 94662526..282b847c 100644 +--- a/SECURITY.md ++++ b/SECURITY.md +@@ -54,33 +54,33 @@ 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 +@@ -90,7 +90,7 @@ GedM9wFtn+rXNJ0PzVt0Ez2yJ+0FIKn0o/dT40h6oSDdXOce0WIW+jcAkKtpzTkf + NhWLXNuuxykMFyoQ591qSetDFH6egnjIFaIR7TNZITew49cZi1ZcYaIEb00EdjlR + 6gMzX/WOA/tptfAcaK4r8A5NnDh0cxcaGQPN9WMtcyeWIJogFFMTC07YXB13l4yU + d/WfXI2l6QARAQABiQPbBBgBCgAmAhsCFiEEK7vTD6qymzJTvPum9pR9q2jnuTEF +-AmR8KHMFCRpUNwEBqcDdIAQZAQgABgUCTgn8cgAKCRAY7oY4YCLvV/x3C/44CpgL ++AmgP8jYFCRw2Q0QBqcDdIAQZAQgABgUCTgn8cgAKCRAY7oY4YCLvV/x3C/44CpgL + VRUZT8bxDp7ZjIpyxTB43f+tpGlykSFMYS3/Cw/i7ar1fjoAeVonXAp0PpqeuJ9w + +p9r3UWPZeVlmibYybLujnNDnV6RmeNtzc4HUtgPP/s7rynU6RFX46T5YRUBo/aC + hjFcWVi+YUaNfBdgaKyf4INWtuNTndLXlOJkuqGCikKOuuwReJ4pvs49whVj9Nug +@@ -99,17 +99,17 @@ jsotEf4/+tzsrCIWLtSF2BI/Fz0xV2vlmCzsB5fN4nC/ksaaXAL7jHwaUbTMLJ3W + znaGh3XK6e05Avss8mIaju+Zf/Vk+oLNzGqI+YAgczWyK82yDbuxXfWauBI32nmF + XDwqN8pvPGGEm8BgMQxfKnV0mt3BezPTYBSuPw22+wVbao3xMJSIlIbFitw2ZOSL + mit64IYYVGaWr3awn65MSK/Db9SRaGv52gOR6ylDul7wkjNE8ohlaos0y9sJEPaU +-fato57kxHA8QANbsBb9seSTd0jNBsqsSh2QWWaFU55DRSixz4AiTQZOwxQ6Bv0gi +-gnWbKmPNuxzUXfTTK1PG8z7R3tfTObXFslrwjLmnZR0o7EYz9wJKLGBFhSxa+KJZ +-chTRrWI7AALbBULgAgxP9nOLni1cRD+NhKfbipsph4jB97fgTqOi4Fglp5E7aC+8 +-tfLiVgi3g8c+IWRgO4T1PT0JWzy/V78j1C+sv343zjtvwZAk35Z7mfX6KSjFsTWG +-CFUSLT9FzgvJfTvlaneNdaE9mT1Ldb/2O2J1BF2FzL1EkQBVBkS83r3EtdN/tuZl +-nJk+E1IrJ63ilgkzmM38SeGvwj7K8lK/EuRT3/+qTD1/f6uGOE8DNqi6CF8gi1bP +-zDGfN1EbyR3ZQmfNey6nMBYDkS0bmAtNrvifeiVpwvNcRBHhCnpsOSZGPVEsJ2Wo +-AslY0FOkgF+CUrxUb9TpCKY+9eXgQhCk3EqR7f0rKgsCp8w4Vbp4R0jII7jzrDkQ +-x0mN2gbKz2PsJPxnQkEBFxfrVq/aMGyDsVL0Vz6K/MZWvUKheaLeatVudntE/Vso +-lC9G3UsAAMDChecOrAnge/kyySqosJcfvfCvgLLxZlHHv+QWpFDaaQE9JZdDuaru +-GkA33Xp0WFc0GhiEfnBR4iqoAqtdb8rp+vPQiA6sHu13NkThfsSiQn4CuQGNBE4J ++fato57kxE/4P/ifW5lLtZVWKvS6pmGgDGQCEbyOIlhmPLhRdZIwUgphCkp1FEwil ++CECHy3j+aBSRTHP8J+83LO+3V5DI2PO37JfEyBxKIzTAkoX3wu9NJtGQDmDv/Bc+ ++J3EpmfW6CtoWTn6bXAzIqCN7YxTWqNY4UKafovzm+wV5X6h04VK4qjzqwsRMUECr ++H/KNfiuj2L9ytB0DkSSKR9YK0VaDgOP91ybUVoOtVYhECSja6M9Ebno+UG5FuNnS ++ktbpWWwrmW9VR2GOQtmkPak+QKpGjJ3GZA+2HJ81kEwtg9qpIkWrlWtD3HkWjv6p ++DR0K+JXs4zd6DaMBUrUkR2JqO5jnYCsDGI2LB01WdODuTs6YqIiM9pvn8loSvjCU ++6tyH9kZRpcU2qddoCFeeQdRUbAhCyqULtgGARZJmFiW6owrIMNbpr/f1CuHyUhfx ++kV8dm+ebvarzf/WYbUXRi9ZY+j7Qm8AvhgCkudbISOOe9RXok5b6NG4W+owuB+4+ ++vcmVfV7/ATaVTXuCUZ5x5Xuq4TlQB6R11eKrz0RUWBYU3PJV+B96DpIdxcPzfYOR ++t9E+nlglpHGjCGxQmhYmZhskZPQl7pnE/l+/tK8iwD6/djvMvtkkoVLqVWHwusoi ++6sgDop7YHtaChNEP5dJL1DsVlHnaXsSak1d0hTsbiiqKKlm/OZRl0e9yuQGNBE4J + /TQBDACoLGAOK8w/Mv1B3SZN/mfUYXgjJnOS1lqCNdKRG8MVQQCBVEe9QPU8yavh + /MpraEvPZhz6WSg7k1pHNMbKsDfv80ZX5WM95uMN69nmF/l+qo+eBJU8YIHWabkv + MSWTBeD1roo8CwHOl102ajgo0XzhCqeb4MkUCZCZxdTaoHcD+IW+4IbajozgzTYV +@@ -118,19 +118,19 @@ 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 +@@ -139,18 +139,18 @@ Y7KGzId4y4Bh+Ni8uQO1eTGcc2XITAj5oFdYdC61wJ3B2i1w24gAYNqAJ8bodnYA + JatFRncuaYT6X5bNKHGT+u4KqedR55njEP7XxkXyfL06gI4ri1ef22d8X0kJIY3d + d2LD81qGfAEU8Q/qboPdeaVEtG0FfMCTqQ1yyct1jkbKZMUK/EPompgUZb6JTQov + bRGUPZFbhpq8nVAsu+jRRPVFzmkAEQEAAYkCPAQYAQoAJgIbIBYhBCu70w+qspsy +-U7z7pvaUfato57kxBQJkfCiTBQkaVDXmAAoJEPaUfato57kx2aoQAMmvNTQYIRZE +-GbUnHhfDlPKFHuQWUTZNgn8QzksUUF6JORdCoU3MqN/6Z2nHQ3LLiM07a9byk7zh +-+W3K3J//UzZ24p5FfC3YOKzIOBP08Ij0EvuCajCeR5o/P7zHu5WBhoEgr5tEnFQC +-oaeWdeL5GEBRrSmntUivhSUxWs0ntqk4cC0ncRgUb+28ZBGNa0ljqYoiatIHkTpR +-E9RgJiaUsnmE5THkZ3xcvIemNFRjzQ5CjP65zlRbfrCJXsYiXF1gcOIPFoKaxtBi +-w5HkgVd7cmHzClCkq/RTM+dDVo3V9b3zHB//1D8XwiQWvF7gMYymCCrnH+onKPi4 +-XCDrCfm6toP6fEQa/nPJ92JtQiPLDpu1AxfpwNfZV/FIApNGuZ/Egs3sK7aVI76B +-jvQs4chZUYfIY6axIJYceceXg1SFqiv15vjXLXi3RrtN7HaCDOCpSMQUG6gJ4dRh +-VsKHK+wkqCmiIAPQt8rYkabev5dg+B+LGjj6oCmUyrCaoXubnkuX7pFqOCMDUw2b +-ihb2H4k48HVxZdke3d4wPgq0oPCx3/04vFExq3PqOW9s0MTIC0XOY9GXJwJcFumQ +-suOSVzMPRvWnBQnJYTfBtWehaxHG5dqNdOsNTdFfPfeD/qtHQ56RyYoqjZ87IY7q +-E5GuuIiJTEypzeYfM0OkPPmPL4ku3B5J +-=MtRc ++U7z7pvaUfato57kxBQJoD/JqBQkcNkI9AAoJEPaUfato57kxsHIP/idmkMDpHyBp ++1G+9Z/JMxbbAXqKtM1VecPi9ptUtKUL1rGg1iMFUX/E+Sdihw7QkjSWa+l1ANRRD ++bMF9xvLgBjHkcp3YJ2gcaUQ1S3SihftqlA1rXYhD4ZkRxRQQmGdV2ueS1QZJuc0P ++QRq/63D9lw72nSiuuXLprtU86THncfwJVLPc+Q62MrvmLA6yInczsXjhhBzfdiie ++rqIwfWQAZw0C3sg9C5vjIFYscbMa1Y9AggCtWyRenJJiPvBE0nkwMVZMA7TxFi0h ++SiBjRacdBy2BN5HyXLOHP+Lgb8fSxYEKAozJ5YzzfqJjPsNpt7GukxVeQrsRCvRi ++FDmYHHgm/TZZ8+ilzrJnFGd0Ikazh8K5nOQo5DUBeBDreNcSPRHHFLqShm/Unhb5 ++uESyClCSm745w/zJ4JFA+bxaP64HbnkT9ceERmeKvh+pHXFmhBUPyWLlquTyhCnu ++mDG7A71OUsqayVswuN2fz09cfbVXdVJg1wGtDHdXbywSrlLxv4Y5Gn4mczkYqMhU ++lt8iCQHoQKRKtuZEUEBbpLiOl21CEKX0PXsg6qB/Uij3jaZ3PIfDpWa14HnVV4Ro ++eP0X7uYO+T8LmU97AZgVB/nKIWtuXu+E03RpZqwVbtR5XZUYOzadSha9uYYpUj4x ++UDPDXVVv4sGio8wU3lSJRhp2U4W96PEU ++=CF3r + -----END PGP PUBLIC KEY BLOCK----- + ``` diff --git a/backport-Fix-race-condition-when-adding-new-nodes.patch b/backport-Fix-race-condition-when-adding-new-nodes.patch new file mode 100644 index 0000000000000000000000000000000000000000..521be74d2ac9cb65e83bec08375734b27dddb40f --- /dev/null +++ b/backport-Fix-race-condition-when-adding-new-nodes.patch @@ -0,0 +1,79 @@ +From 88297ac33c3e4783e2b70d8047db77e8bfd2889b Mon Sep 17 00:00:00 2001 +From: Hannes von Haugwitz +Date: Thu, 3 Jul 2025 20:16:25 +0200 +Subject: [PATCH] Fix race condition when adding new nodes + +--- + ChangeLog | 3 +++ + src/seltree.c | 31 +++++++++++-------------------- + 2 files changed, 14 insertions(+), 20 deletions(-) + +diff --git a/ChangeLog b/ChangeLog +index 283013d..1162afb 100644 +--- a/ChangeLog ++++ b/ChangeLog +@@ -1,3 +1,6 @@ ++2025-07-03 Hannes von Haugwitz ++ * Fix race condition when adding new nodes during file system scan ++ + 2025-06-09 Hannes von Haugwitz + * Add info about worker states to progress bar + +diff --git a/src/seltree.c b/src/seltree.c +index 3abe535..912a2bc 100644 +--- a/src/seltree.c ++++ b/src/seltree.c +@@ -144,14 +144,6 @@ static seltree *create_seltree_node(char *path, seltree *parent) { + return node; + } + +-static seltree *_insert_new_node(char *path, seltree *parent) { +- seltree *node = create_seltree_node(path, parent); +- pthread_rwlock_wrlock(&parent->rwlock); +- parent ->children = tree_insert(parent->children, strrchr(node->path,'/'), (void*)node, (tree_cmp_f) strcmp); +- pthread_rwlock_unlock(&parent->rwlock); +- return node; +-} +- + static seltree* _get_seltree_node(seltree* node, char *path, bool create) { + LOG_LEVEL log_level = LOG_LEVEL_TRACE; + pthread_rwlock_rdlock(&node->rwlock); +@@ -159,6 +151,7 @@ static seltree* _get_seltree_node(seltree* node, char *path, bool create) { + pthread_rwlock_unlock(&node->rwlock); + seltree *parent = NULL; + char *tmp = checked_strdup(path); ++ + if (node && strcmp(node->path, path) != 0) { + char *next_dir = path;; + do { +@@ -169,20 +162,18 @@ static seltree* _get_seltree_node(seltree* node, char *path, bool create) { + log_msg(log_level, "_get_seltree_node(): %s> search for child node '%s' (parent: '%s' (%p))", path, strrchr(tmp,'/'), parent->path, (void*) parent); + node = tree_search(parent->children, strrchr(tmp,'/'), (tree_cmp_f) strcmp); + pthread_rwlock_unlock(&parent->rwlock); ++ if (create && node == NULL) { ++ pthread_rwlock_wrlock(&parent->rwlock); ++ node = tree_search(parent->children, strrchr(tmp,'/'), (tree_cmp_f) strcmp); ++ if (node == NULL) { ++ node = create_seltree_node(tmp, parent); ++ parent ->children = tree_insert(parent->children, strrchr(node->path,'/'), (void*)node, (tree_cmp_f) strcmp); ++ log_msg(log_level, "_get_seltree_node(): %s> created new %s node '%s' (%p) (parent: %p)", path, next_dir?"inner":"leaf", tmp, (void*) node, (void*) parent); ++ } ++ pthread_rwlock_unlock(&parent->rwlock); ++ } + if (next_dir) { tmp[next_dir-path] = '/'; } + } while (node != NULL && next_dir); +- if (create && node == NULL) { +- while (next_dir) { +- tmp[next_dir-path] = '\0'; +- node = _insert_new_node(tmp, parent); +- log_msg(log_level, "_get_seltree_node(): %s> created new inner node '%s' (%p) (parent: %p)", path, tmp, (void*) node, (void*) parent); +- parent = node; +- tmp[next_dir-path] = '/'; +- next_dir = strchr(&next_dir[1], '/'); +- } +- node = _insert_new_node(path, parent); +- log_msg(LOG_LEVEL_TRACE, "created new leaf node '%s' (%p) (parent: %p)", path, (void*) node, (void*) parent); +- } + } + free(tmp); + if (node == NULL) {