mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 18:58:40 +00:00
477 lines
13 KiB
C
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;
|
|
}
|