boredos_mirror/src/sys/file_index.c
2026-04-12 21:46:28 +02:00

477 lines
13 KiB
C

// 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 <stddef.h>
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;
}