// Copyright (c) 2023-2026 Chris (boreddevnl) // This software is released under the GNU General Public License v3.0. See LICENSE file for details. // This header needs to maintain in any file it is present in, as per the GPL license terms. #include "file_index.h" #include "vfs.h" #include "memory_manager.h" #include "spinlock.h" #include static file_index_t g_file_index = {0}; static spinlock_t g_index_lock = SPINLOCK_INIT; static bool g_index_valid = false; static int str_len(const char *s) { int n = 0; while (s[n]) n++; return n; } static void str_copy(char *d, const char *s) { while ((*d++ = *s++)); } static int str_cmp(const char *a, const char *b) { while (*a && *a == *b) { a++; b++; } return (unsigned char)*a - (unsigned char)*b; } static void str_cat(char *d, const char *s) { while (*d) d++; str_copy(d, s); } static bool str_starts_with(const char *str, const char *prefix) { while (*prefix) { if (*str++ != *prefix++) return false; } return true; } static int fuzzy_match_score(const char *query, const char *filename) { if (!query || !filename) return 0; int score = 0; int query_idx = 0; int consecutive = 0; for (int i = 0; filename[i] && query_idx < 256; i++) { char fc = filename[i]; char qc = query[query_idx]; if (fc >= 'A' && fc <= 'Z') fc += 32; if (qc >= 'A' && qc <= 'Z') qc += 32; if (fc == qc) { score += 10; consecutive++; if (consecutive > 1) score += 5; query_idx++; } else { consecutive = 0; } } if (query_idx < str_len(query)) { return 0; } if (str_starts_with(filename, query)) { score += 20; } return score; } static void index_walk_directory(const char *path, int depth) { if (depth > 16 || g_file_index.count >= FILE_INDEX_MAX_ENTRIES) { return; } if (str_starts_with(path, "/proc") || str_starts_with(path, "/sys") || str_starts_with(path, "/dev")) { return; } vfs_dirent_t *entries = (vfs_dirent_t *)kmalloc(sizeof(vfs_dirent_t) * 1024); if (!entries) { return; } int count = vfs_list_directory(path, entries, 1024); if (count <= 0 || count > 1024) { kfree(entries); return; } for (int i = 0; i < count; i++) { if (g_file_index.count >= FILE_INDEX_MAX_ENTRIES) { break; } vfs_dirent_t *entry = &entries[i]; if (!entry) continue; if (!entry->name || entry->name[0] == 0) { continue; } if (str_cmp(entry->name, ".color") == 0 || str_cmp(entry->name, ".origin") == 0 || str_cmp(entry->name, ".") == 0 || str_cmp(entry->name, "..") == 0) { continue; } char full_path[FILE_INDEX_MAX_PATH]; int path_len = 0; for (int j = 0; path[j] && path_len < FILE_INDEX_MAX_PATH - 1; j++) { full_path[path_len++] = path[j]; } if (path_len > 0 && full_path[path_len - 1] != '/' && path_len < FILE_INDEX_MAX_PATH - 1) { full_path[path_len++] = '/'; } for (int j = 0; entry->name[j] && path_len < FILE_INDEX_MAX_PATH - 1; j++) { full_path[path_len++] = entry->name[j]; } full_path[path_len] = 0; if (path_len >= FILE_INDEX_MAX_PATH - 1) { continue; } file_index_entry_t *idx_entry = &g_file_index.entries[g_file_index.count]; str_copy(idx_entry->path, full_path); idx_entry->size = entry->size; idx_entry->mod_time_low = entry->write_date; idx_entry->mod_time_high = entry->write_time; idx_entry->is_directory = entry->is_directory; g_file_index.count++; if (entry->is_directory && !str_starts_with(full_path, "/proc") && !str_starts_with(full_path, "/sys") && !str_starts_with(full_path, "/dev")) { index_walk_directory(full_path, depth + 1); } } kfree(entries); } void file_index_init(void) { g_file_index.count = 0; g_file_index.capacity = FILE_INDEX_MAX_ENTRIES; g_index_valid = false; } bool file_index_build(void) { g_file_index.count = 0; const char *safe_paths[] = {"/root", "/bin", "/Library", "/docs", NULL}; for (int p = 0; safe_paths[p] != NULL && g_file_index.count < FILE_INDEX_MAX_ENTRIES; p++) { index_walk_directory(safe_paths[p], 0); } uint64_t flags = spinlock_acquire_irqsave(&g_index_lock); g_index_valid = true; spinlock_release_irqrestore(&g_index_lock, flags); file_index_save(); return true; } bool file_index_load(void) { vfs_file_t *file = vfs_open(FILE_INDEX_CACHE_PATH, "r"); if (!file) { return false; } uint32_t version = 0; if (vfs_read(file, &version, sizeof(version)) != sizeof(version)) { vfs_close(file); return false; } if (version != FILE_INDEX_VERSION) { vfs_close(file); return false; } int count = 0; if (vfs_read(file, &count, sizeof(count)) != sizeof(count)) { vfs_close(file); return false; } if (count < 0 || count > FILE_INDEX_MAX_ENTRIES) { vfs_close(file); return false; } file_index_entry_t temp_entries[FILE_INDEX_MAX_ENTRIES]; for (int i = 0; i < count; i++) { file_index_entry_t *entry = &temp_entries[i]; int bytes_read = vfs_read(file, entry->path, FILE_INDEX_MAX_PATH); if (bytes_read != FILE_INDEX_MAX_PATH) { vfs_close(file); return false; } if (vfs_read(file, &entry->size, sizeof(entry->size)) != sizeof(entry->size)) { vfs_close(file); return false; } if (vfs_read(file, &entry->mod_time_low, sizeof(entry->mod_time_low)) != sizeof(entry->mod_time_low)) { vfs_close(file); return false; } if (vfs_read(file, &entry->mod_time_high, sizeof(entry->mod_time_high)) != sizeof(entry->mod_time_high)) { vfs_close(file); return false; } if (vfs_read(file, &entry->is_directory, sizeof(entry->is_directory)) != sizeof(entry->is_directory)) { vfs_close(file); return false; } } vfs_close(file); uint64_t flags = spinlock_acquire_irqsave(&g_index_lock); g_file_index.count = 0; for (int i = 0; i < count; i++) { g_file_index.entries[i] = temp_entries[i]; g_file_index.count++; } g_index_valid = true; spinlock_release_irqrestore(&g_index_lock, flags); return true; } bool file_index_save(void) { if (!vfs_mkdir("/Library")) { } if (!vfs_mkdir("/Library/Index")) { } vfs_file_t *file = vfs_open(FILE_INDEX_CACHE_PATH, "w"); if (!file) { return false; } uint64_t flags = spinlock_acquire_irqsave(&g_index_lock); int count = g_file_index.count; file_index_entry_t entries[FILE_INDEX_MAX_ENTRIES]; for (int i = 0; i < count; i++) { entries[i] = g_file_index.entries[i]; } spinlock_release_irqrestore(&g_index_lock, flags); uint32_t version = FILE_INDEX_VERSION; if (vfs_write(file, &version, sizeof(version)) != sizeof(version)) { vfs_close(file); return false; } if (vfs_write(file, &count, sizeof(count)) != sizeof(count)) { vfs_close(file); return false; } for (int i = 0; i < count; i++) { file_index_entry_t *entry = &entries[i]; if (vfs_write(file, entry->path, FILE_INDEX_MAX_PATH) != FILE_INDEX_MAX_PATH) { vfs_close(file); return false; } if (vfs_write(file, &entry->size, sizeof(entry->size)) != sizeof(entry->size)) { vfs_close(file); return false; } if (vfs_write(file, &entry->mod_time_low, sizeof(entry->mod_time_low)) != sizeof(entry->mod_time_low)) { vfs_close(file); return false; } if (vfs_write(file, &entry->mod_time_high, sizeof(entry->mod_time_high)) != sizeof(entry->mod_time_high)) { vfs_close(file); return false; } if (vfs_write(file, &entry->is_directory, sizeof(entry->is_directory)) != sizeof(entry->is_directory)) { vfs_close(file); return false; } } vfs_close(file); return true; } int file_index_find_fuzzy(const char *query, file_index_result_t *results, int max_results) { if (!query || !results || max_results <= 0) { return 0; } uint64_t flags = spinlock_acquire_irqsave(&g_index_lock); int result_count = 0; for (int i = 0; i < g_file_index.count && result_count < max_results; i++) { if (i < 0 || i >= FILE_INDEX_MAX_ENTRIES) { break; } const char *path = g_file_index.entries[i].path; if (!path || path[0] == 0) { continue; } const char *filename = path; for (int j = 0; path[j]; j++) { if (path[j] == '/') { filename = &path[j + 1]; } } if (!filename || filename[0] == 0) { continue; } int score = fuzzy_match_score(query, filename); if (score > 0) { results[result_count].entry = g_file_index.entries[i]; results[result_count].match_score = score; result_count++; } } spinlock_release_irqrestore(&g_index_lock, flags); for (int i = 0; i < result_count; i++) { for (int j = i + 1; j < result_count; j++) { if (results[j].match_score > results[i].match_score) { file_index_result_t tmp = results[i]; results[i] = results[j]; results[j] = tmp; } } } return result_count; } bool file_index_add_entry(const char *path, uint32_t size, uint32_t mod_time_low, uint32_t mod_time_high, bool is_dir) { if (!path || g_file_index.count >= FILE_INDEX_MAX_ENTRIES) { return false; } uint64_t flags = spinlock_acquire_irqsave(&g_index_lock); for (int i = 0; i < g_file_index.count; i++) { if (str_cmp(g_file_index.entries[i].path, path) == 0) { g_file_index.entries[i].size = size; g_file_index.entries[i].mod_time_low = mod_time_low; g_file_index.entries[i].mod_time_high = mod_time_high; spinlock_release_irqrestore(&g_index_lock, flags); return true; } } file_index_entry_t *entry = &g_file_index.entries[g_file_index.count]; str_copy(entry->path, path); entry->size = size; entry->mod_time_low = mod_time_low; entry->mod_time_high = mod_time_high; entry->is_directory = is_dir; g_file_index.count++; spinlock_release_irqrestore(&g_index_lock, flags); return true; } bool file_index_remove_entry(const char *path) { if (!path) { return false; } uint64_t flags = spinlock_acquire_irqsave(&g_index_lock); for (int i = 0; i < g_file_index.count; i++) { if (str_cmp(g_file_index.entries[i].path, path) == 0) { for (int j = i; j < g_file_index.count - 1; j++) { g_file_index.entries[j] = g_file_index.entries[j + 1]; } g_file_index.count--; spinlock_release_irqrestore(&g_index_lock, flags); return true; } } spinlock_release_irqrestore(&g_index_lock, flags); return false; } int file_index_get_entry_count(void) { uint64_t flags = spinlock_acquire_irqsave(&g_index_lock); int count = g_file_index.count; spinlock_release_irqrestore(&g_index_lock, flags); return count; } void file_index_clear(void) { uint64_t flags = spinlock_acquire_irqsave(&g_index_lock); g_file_index.count = 0; g_index_valid = false; spinlock_release_irqrestore(&g_index_lock, flags); } void file_index_invalidate_cache(void) { uint64_t flags = spinlock_acquire_irqsave(&g_index_lock); g_index_valid = false; spinlock_release_irqrestore(&g_index_lock, flags); } bool file_index_is_valid(void) { uint64_t flags = spinlock_acquire_irqsave(&g_index_lock); bool valid = g_index_valid; spinlock_release_irqrestore(&g_index_lock, flags); return valid; }