Unify scan and watcher tree traversal with a shared scan walker

This commit is contained in:
Roman Tarasov committed 2026-03-22 17:54:58 +03:00
1 parent cec94656c6
commit ef814ad56b
4 files changed
+239 -227

No files matched your search

+31
View File
@@ -0,0 +1,31 @@
#ifndef SM_SCAN_TREE_H
#define SM_SCAN_TREE_H
#include <stdbool.h>
typedef enum {
SM_SCAN_TREE_DIR_DESCEND = 0,
SM_SCAN_TREE_DIR_SKIP_DESCEND,
SM_SCAN_TREE_DIR_ABORT,
} sm_scan_tree_dir_visit_t;
typedef sm_scan_tree_dir_visit_t (*sm_scan_tree_dir_fn)(const char *dir_path,
unsigned int depth_from_root,
void *ctx);
typedef bool (*sm_scan_tree_image_fn)(const char *image_path,
const char *image_name,
unsigned int depth_from_root,
void *ctx);
typedef struct {
sm_scan_tree_dir_fn on_directory;
sm_scan_tree_image_fn on_image_file;
} sm_scan_tree_callbacks_t;
// Walk a managed scan subtree using the shared scan-depth and path filtering rules.
bool sm_scan_tree_walk(const char *scan_root, const char *dir_path,
unsigned int depth_from_root,
unsigned int remaining_depth,
const sm_scan_tree_callbacks_t *callbacks, void *ctx);
#endif
+46 -127
View File
@@ -1,6 +1,7 @@
#include "sm_platform.h"
#include "sm_runtime.h"
#include "sm_scan.h"
#include "sm_scan_tree.h"
#include "sm_types.h"
#include "sm_game_cache.h"
#include "sm_gameinfo.h"
@@ -31,43 +32,6 @@ static bool is_under_discovered_param_root(
return false;
}
static void classify_scan_entry(const char *full_path, unsigned char d_type,
bool *is_dir_out, bool *is_regular_out) {
bool is_dir = false;
bool is_regular = false;
if (d_type == DT_DIR) {
is_dir = true;
} else if (d_type == DT_REG) {
is_regular = true;
} else if (d_type == DT_UNKNOWN) {
struct stat st;
if (lstat(full_path, &st) == 0) {
is_dir = S_ISDIR(st.st_mode);
is_regular = S_ISREG(st.st_mode);
}
}
if (is_dir_out)
*is_dir_out = is_dir;
if (is_regular_out)
*is_regular_out = is_regular;
}
static bool is_distinct_configured_scan_root(const char *current_scan_root,
const char *path) {
for (int i = 0; i < get_scan_path_count(); i++) {
const char *scan_path = get_scan_path(i);
if (strcmp(scan_path, path) != 0)
continue;
if (current_scan_root && strcmp(current_scan_root, path) == 0)
return false;
return true;
}
return false;
}
// --- Candidate Discovery ---
static bool try_collect_candidate_for_directory(
const char *full_path, scan_candidate_t *candidates, int max_candidates,
@@ -200,65 +164,43 @@ static bool try_collect_candidate_for_directory(
return true;
}
static void collect_candidates_with_depth(
const char *current_scan_root, const char *dir_path,
unsigned int remaining_depth, scan_candidate_t *candidates,
int max_candidates, int *candidate_count,
const struct AppDbTitleList *app_db_titles, bool app_db_titles_ready,
char discovered_param_roots[][MAX_PATH],
int *discovered_param_root_count, bool *unstable_found_out) {
if (should_stop_requested())
return;
typedef struct {
scan_candidate_t *candidates;
int max_candidates;
int *candidate_count;
const struct AppDbTitleList *app_db_titles;
bool app_db_titles_ready;
char (*discovered_param_roots)[MAX_PATH];
int *discovered_param_root_count;
bool *unstable_found_out;
} collect_candidates_walk_ctx_t;
if (is_distinct_configured_scan_root(current_scan_root, dir_path))
return;
static sm_scan_tree_dir_visit_t collect_candidate_directory_visit(
const char *dir_path, unsigned int depth_from_root, void *ctx_ptr) {
if (depth_from_root == 0u)
return SM_SCAN_TREE_DIR_DESCEND;
// Once a directory has sce_sys/param.json (valid or not),
// it is treated as a terminal game root and descendants are skipped.
collect_candidates_walk_ctx_t *ctx = (collect_candidates_walk_ctx_t *)ctx_ptr;
if (try_collect_candidate_for_directory(
dir_path, candidates, max_candidates, candidate_count, app_db_titles,
app_db_titles_ready, discovered_param_roots,
discovered_param_root_count, unstable_found_out)) {
return;
dir_path, ctx->candidates, ctx->max_candidates, ctx->candidate_count,
ctx->app_db_titles, ctx->app_db_titles_ready,
ctx->discovered_param_roots, ctx->discovered_param_root_count,
ctx->unstable_found_out)) {
return SM_SCAN_TREE_DIR_SKIP_DESCEND;
}
if (remaining_depth == 0)
return;
return SM_SCAN_TREE_DIR_DESCEND;
}
DIR *d = opendir(dir_path);
if (!d)
return;
static bool collect_candidate_image_visit(const char *image_path,
const char *image_name,
unsigned int depth_from_root,
void *ctx_ptr) {
(void)depth_from_root;
struct dirent *entry;
while ((entry = readdir(d)) != NULL) {
if (should_stop_requested())
break;
if (entry->d_name[0] == '.')
continue;
char full_path[MAX_PATH];
snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);
bool is_dir = false;
bool is_regular = false;
classify_scan_entry(full_path, entry->d_type, &is_dir, &is_regular);
if (!path_matches_root_or_child(current_scan_root, IMAGE_MOUNT_BASE) &&
is_regular && is_supported_image_file_name(entry->d_name)) {
maybe_mount_image_file(full_path, entry->d_name, unstable_found_out);
}
if (!is_dir)
continue;
collect_candidates_with_depth(
current_scan_root, full_path, remaining_depth - 1u, candidates,
max_candidates, candidate_count, app_db_titles, app_db_titles_ready,
discovered_param_roots, discovered_param_root_count,
unstable_found_out);
}
closedir(d);
collect_candidates_walk_ctx_t *ctx = (collect_candidates_walk_ctx_t *)ctx_ptr;
maybe_mount_image_file(image_path, image_name, ctx->unstable_found_out);
return true;
}
static bool build_backport_mount_context(const char *title_id,
@@ -366,47 +308,24 @@ static void collect_scan_candidates_from_root(
return;
unsigned int scan_depth = runtime_config()->scan_depth;
bool has_backports_root = !is_under_image_mount_base(scan_path);
if (scan_depth < MIN_SCAN_DEPTH)
scan_depth = MIN_SCAN_DEPTH;
DIR *d = opendir(scan_path);
if (!d)
return;
struct dirent *entry;
while ((entry = readdir(d)) != NULL) {
if (should_stop_requested())
break;
if (entry->d_name[0] == '.')
continue;
if (has_backports_root &&
strcmp(entry->d_name, DEFAULT_BACKPORTS_DIR_NAME) == 0) {
continue;
}
char full_path[MAX_PATH];
snprintf(full_path, sizeof(full_path), "%s/%s", scan_path, entry->d_name);
bool is_dir = false;
bool is_regular = false;
classify_scan_entry(full_path, entry->d_type, &is_dir, &is_regular);
if (!path_matches_root_or_child(scan_path, IMAGE_MOUNT_BASE) && is_regular &&
is_supported_image_file_name(entry->d_name))
maybe_mount_image_file(full_path, entry->d_name, unstable_found_out);
if (!is_dir)
continue;
collect_candidates_with_depth(
scan_path, full_path, scan_depth - 1u, candidates, max_candidates,
candidate_count, app_db_titles, app_db_titles_ready,
discovered_param_roots, discovered_param_root_count,
unstable_found_out);
}
closedir(d);
collect_candidates_walk_ctx_t ctx = {
.candidates = candidates,
.max_candidates = max_candidates,
.candidate_count = candidate_count,
.app_db_titles = app_db_titles,
.app_db_titles_ready = app_db_titles_ready,
.discovered_param_roots = discovered_param_roots,
.discovered_param_root_count = discovered_param_root_count,
.unstable_found_out = unstable_found_out,
};
sm_scan_tree_callbacks_t callbacks = {
.on_directory = collect_candidate_directory_visit,
.on_image_file = collect_candidate_image_visit,
};
(void)sm_scan_tree_walk(scan_path, scan_path, 0u, scan_depth, &callbacks, &ctx);
}
int collect_scan_candidates_for_scan_root(const char *scan_root,
+121
View File
@@ -0,0 +1,121 @@
#include "sm_platform.h"
#include "sm_scan_tree.h"
#include "sm_config_mount.h"
#include "sm_filesystem.h"
#include "sm_image.h"
#include "sm_path_utils.h"
#include "sm_paths.h"
#include "sm_runtime.h"
static void classify_scan_tree_entry(const char *full_path, unsigned char d_type,
bool *is_dir_out, bool *is_regular_out) {
bool is_dir = false;
bool is_regular = false;
if (d_type == DT_DIR) {
is_dir = true;
} else if (d_type == DT_REG) {
is_regular = true;
} else if (d_type == DT_UNKNOWN) {
struct stat st;
if (lstat(full_path, &st) == 0) {
is_dir = S_ISDIR(st.st_mode);
is_regular = S_ISREG(st.st_mode);
}
}
*is_dir_out = is_dir;
*is_regular_out = is_regular;
}
static bool is_distinct_configured_scan_root(const char *current_scan_root,
const char *path) {
for (int i = 0; i < get_scan_path_count(); i++) {
const char *scan_path = get_scan_path(i);
if (strcmp(scan_path, path) != 0)
continue;
if (strcmp(current_scan_root, path) == 0)
return false;
return true;
}
return false;
}
bool sm_scan_tree_walk(const char *scan_root, const char *dir_path,
unsigned int depth_from_root,
unsigned int remaining_depth,
const sm_scan_tree_callbacks_t *callbacks, void *ctx) {
if (should_stop_requested())
return true;
if (depth_from_root > 0u &&
is_distinct_configured_scan_root(scan_root, dir_path)) {
return true;
}
sm_scan_tree_dir_visit_t dir_visit = SM_SCAN_TREE_DIR_DESCEND;
if (callbacks->on_directory) {
dir_visit = callbacks->on_directory(dir_path, depth_from_root, ctx);
if (dir_visit == SM_SCAN_TREE_DIR_ABORT)
return false;
if (dir_visit == SM_SCAN_TREE_DIR_SKIP_DESCEND)
return true;
}
if (remaining_depth == 0u)
return true;
DIR *d = opendir(dir_path);
if (!d)
return true;
bool skip_backports_root =
(depth_from_root == 0u && !is_under_image_mount_base(scan_root));
bool allow_image_file_visits =
callbacks->on_image_file &&
!path_matches_root_or_child(scan_root, IMAGE_MOUNT_BASE);
struct dirent *entry;
while ((entry = readdir(d)) != NULL) {
if (should_stop_requested()) {
closedir(d);
return true;
}
if (entry->d_name[0] == '.')
continue;
if (skip_backports_root &&
strcmp(entry->d_name, DEFAULT_BACKPORTS_DIR_NAME) == 0) {
continue;
}
char full_path[MAX_PATH];
snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);
bool is_dir = false;
bool is_regular = false;
classify_scan_tree_entry(full_path, entry->d_type, &is_dir, &is_regular);
if (allow_image_file_visits && is_regular &&
is_supported_image_file_name(entry->d_name)) {
if (!callbacks->on_image_file(full_path, entry->d_name, depth_from_root + 1u,
ctx)) {
closedir(d);
return false;
}
}
if (!is_dir)
continue;
if (!sm_scan_tree_walk(scan_root, full_path, depth_from_root + 1u,
remaining_depth - 1u, callbacks, ctx)) {
closedir(d);
return false;
}
}
closedir(d);
return true;
}
+41 -100
View File
@@ -14,6 +14,7 @@
#include "sm_paths.h"
#include "sm_runtime.h"
#include "sm_scan.h"
#include "sm_scan_tree.h"
#include "sm_scanner.h"
#include "sm_time.h"
#include "sm_types.h"
@@ -225,41 +226,6 @@ static bool rebuild_scanner_watch_fd_index(void) {
return rebuild_scanner_watch_fd_index_with_count(g_scanner_watch_count);
}
static bool is_distinct_configured_scan_root(const char *current_scan_root,
const char *path) {
for (int i = 0; i < get_scan_path_count(); i++) {
const char *scan_path = get_scan_path(i);
if (strcmp(scan_path, path) != 0)
continue;
if (current_scan_root && strcmp(current_scan_root, path) == 0)
return false;
return true;
}
return false;
}
static void classify_watch_entry(const char *full_path, unsigned char d_type,
bool *is_dir_out, bool *is_regular_out) {
bool is_dir = false;
bool is_regular = false;
if (d_type == DT_DIR) {
is_dir = true;
} else if (d_type == DT_REG) {
is_regular = true;
} else if (d_type == DT_UNKNOWN) {
struct stat st;
if (lstat(full_path, &st) == 0) {
is_dir = S_ISDIR(st.st_mode);
is_regular = S_ISREG(st.st_mode);
}
}
*is_dir_out = is_dir;
*is_regular_out = is_regular;
}
static void link_scanner_watch_entry_to_root(size_t index) {
scanner_watch_entry_t *entry = &g_scanner_watch_entries[index];
size_t head = g_scanner_root_watch_heads[entry->scan_root_index];
@@ -464,76 +430,35 @@ static bool resolve_watch_tree_rebuild_target(const scanner_watch_entry_t *entry
}
}
static bool register_scan_dir_tree(int kq, int scan_root_index,
const char *current_scan_root,
const char *dir_path,
unsigned int depth_from_root,
unsigned int remaining_depth) {
if (depth_from_root > 0 &&
is_distinct_configured_scan_root(current_scan_root, dir_path)) {
return true;
}
typedef struct {
int kq;
int scan_root_index;
} register_watch_tree_ctx_t;
static sm_scan_tree_dir_visit_t register_watch_directory_visit(
const char *dir_path, unsigned int depth_from_root, void *ctx_ptr) {
register_watch_tree_ctx_t *ctx = (register_watch_tree_ctx_t *)ctx_ptr;
scanner_watch_kind_t kind =
(depth_from_root == 0u) ? SCANNER_WATCH_SCAN_ROOT
: SCANNER_WATCH_SCAN_SUBDIR;
if (!register_scanner_watch_entry(kq, scan_root_index, dir_path, kind,
if (!register_scanner_watch_entry(ctx->kq, ctx->scan_root_index, dir_path, kind,
(uint8_t)depth_from_root)) {
return false;
return SM_SCAN_TREE_DIR_ABORT;
}
if (remaining_depth == 0u)
return true;
return SM_SCAN_TREE_DIR_DESCEND;
}
DIR *d = opendir(dir_path);
if (!d)
return true;
static bool register_watch_image_visit(const char *image_path,
const char *image_name,
unsigned int depth_from_root,
void *ctx_ptr) {
(void)image_name;
bool skip_backports_root =
(depth_from_root == 0u && !is_under_image_mount_base(current_scan_root));
struct dirent *entry;
while ((entry = readdir(d)) != NULL) {
if (should_stop_requested()) {
closedir(d);
return true;
}
if (entry->d_name[0] == '.')
continue;
if (skip_backports_root &&
strcmp(entry->d_name, DEFAULT_BACKPORTS_DIR_NAME) == 0) {
continue;
}
char full_path[MAX_PATH];
snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);
bool is_dir = false;
bool is_regular = false;
classify_watch_entry(full_path, entry->d_type, &is_dir, &is_regular);
if (!path_matches_root_or_child(current_scan_root, IMAGE_MOUNT_BASE) &&
is_regular && is_supported_image_file_name(entry->d_name)) {
if (!register_scanner_watch_entry(kq, scan_root_index, full_path,
SCANNER_WATCH_SCAN_IMAGE_FILE,
(uint8_t)(depth_from_root + 1u))) {
closedir(d);
return false;
}
}
if (!is_dir)
continue;
if (!register_scan_dir_tree(kq, scan_root_index, current_scan_root,
full_path, depth_from_root + 1u,
remaining_depth - 1u)) {
closedir(d);
return false;
}
}
closedir(d);
return true;
register_watch_tree_ctx_t *ctx = (register_watch_tree_ctx_t *)ctx_ptr;
return register_scanner_watch_entry(ctx->kq, ctx->scan_root_index, image_path,
SCANNER_WATCH_SCAN_IMAGE_FILE,
(uint8_t)depth_from_root);
}
static bool rebuild_scan_root_watch_tree(int kq, int scan_root_index) {
@@ -545,8 +470,16 @@ static bool rebuild_scan_root_watch_tree(int kq, int scan_root_index) {
if (scan_depth < MIN_SCAN_DEPTH)
scan_depth = MIN_SCAN_DEPTH;
if (!register_scan_dir_tree(kq, scan_root_index, scan_root, scan_root, 0u,
scan_depth)) {
register_watch_tree_ctx_t walk_ctx = {
.kq = kq,
.scan_root_index = scan_root_index,
};
sm_scan_tree_callbacks_t callbacks = {
.on_directory = register_watch_directory_visit,
.on_image_file = register_watch_image_visit,
};
if (!sm_scan_tree_walk(scan_root, scan_root, 0u, scan_depth, &callbacks,
&walk_ctx)) {
return false;
}
@@ -596,8 +529,16 @@ static bool rebuild_scan_root_watch_subtree(int kq, int scan_root_index,
return true;
}
if (!register_scan_dir_tree(kq, scan_root_index, scan_root, rebuild_path,
rebuild_depth, scan_depth - rebuild_depth)) {
register_watch_tree_ctx_t walk_ctx = {
.kq = kq,
.scan_root_index = scan_root_index,
};
sm_scan_tree_callbacks_t callbacks = {
.on_directory = register_watch_directory_visit,
.on_image_file = register_watch_image_visit,
};
if (!sm_scan_tree_walk(scan_root, rebuild_path, rebuild_depth,
scan_depth - rebuild_depth, &callbacks, &walk_ctx)) {
return false;
}