Files
markbase/docs/fuse_poc/markbase_v13_stable.c
Warren 1300a4e223
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled
MarkBase架构升级:Multi-Volume Virtual Tree + Dual-View Management + Git Remote修正
核心功能:
-  Categories/Series双视图管理(category_view.rs + import_markdown.rs)
-  FUSE Multi-Volume支持(tree_type参数)
-  SSH/SFTP/SCP/rsync协议完整实现(4042行)
-  NFS/SMB Module Phase 1-3完成
-  Archive Module Phase 1-4完成(2916行)
-  Download Center API完整实现
-  S3兼容API实现(560行)

Git配置修正:
-  删除错误origin(gitea.momentry.ddns.net)
-  删除m5max128(指向机器名)
-  设置origin = m5max128gitea.momentry.ddns.net/admin/markbase
-  设置m4minigitea = m4minigitea.momentry.ddns.net/warren/markbase

数据清理:
-  删除38个临时SQLite(保留accusys.sqlite、demo.sqlite)
-  删除.bak、test_*.bin、调试脚本等临时文件
-  删除临时目录(build/、download files/、raid_test/等)
-  更新.gitignore排除临时文件

架构优化:
- 52个文件修改,2434行新增,4739行删除
- Workspace成员整合(16个crate)
- 数据库状态:accusys.sqlite保留(主demo测试)

远程同步:
-  准备推送到m5max128gitea(远程Gitea)
-  准备推送到m4minigitea(本地Gitea)
2026-06-12 12:59:54 +08:00

547 lines
17 KiB
C

#define FUSE_USE_VERSION 31
#include <fuse3/fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sqlite3.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
static sqlite3 *db = NULL;
static const char *db_path = "/Users/accusys/markbase/data/users/warren.sqlite";
static pthread_mutex_t db_mutex = PTHREAD_MUTEX_INITIALIZER;
// Optimized caches
typedef struct {
char node_id[64];
char file_path[512];
long file_size;
int access_count;
time_t last_access;
} FileCacheEntry;
#define CACHE_SIZE 200
static FileCacheEntry file_cache[CACHE_SIZE];
static int cache_count = 0;
typedef struct {
char node_id[64];
char node_type[20];
long file_size;
char parent_id[64];
} NodeCacheEntry;
#define NODE_CACHE_SIZE 500
static NodeCacheEntry node_cache[NODE_CACHE_SIZE];
static int node_cache_count = 0;
// Large read buffer
#define READ_CHUNK_SIZE 131072 // 128KB
static int init_db() {
pthread_mutex_lock(&db_mutex);
int result = sqlite3_open_v2(db_path, &db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX,
NULL);
pthread_mutex_unlock(&db_mutex);
if (result != SQLITE_OK) {
fprintf(stderr, "Cannot open database\n");
return -1;
}
printf("Database opened (read-write mode)\n");
return 0;
}
static FileCacheEntry* cache_lookup(const char *node_id) {
for (int i = 0; i < cache_count; i++) {
if (strcmp(file_cache[i].node_id, node_id) == 0) {
file_cache[i].access_count++;
file_cache[i].last_access = time(NULL);
return &file_cache[i];
}
}
return NULL;
}
static void cache_insert(const char *node_id, const char *file_path, long file_size) {
for (int i = 0; i < cache_count; i++) {
if (strcmp(file_cache[i].node_id, node_id) == 0) return;
}
if (cache_count >= CACHE_SIZE) {
// Simple LRU: find oldest
int lru = 0;
time_t oldest = file_cache[0].last_access;
for (int i = 1; i < cache_count; i++) {
if (file_cache[i].last_access < oldest) {
oldest = file_cache[i].last_access;
lru = i;
}
}
strcpy(file_cache[lru].node_id, node_id);
strcpy(file_cache[lru].file_path, file_path);
file_cache[lru].file_size = file_size;
file_cache[lru].access_count = 1;
file_cache[lru].last_access = time(NULL);
} else {
strcpy(file_cache[cache_count].node_id, node_id);
strcpy(file_cache[cache_count].file_path, file_path);
file_cache[cache_count].file_size = file_size;
file_cache[cache_count].access_count = 1;
file_cache[cache_count].last_access = time(NULL);
cache_count++;
}
}
static NodeCacheEntry* node_cache_lookup(const char *node_id) {
for (int i = 0; i < node_cache_count; i++) {
if (strcmp(node_cache[i].node_id, node_id) == 0) {
return &node_cache[i];
}
}
return NULL;
}
static void node_cache_insert(const char *node_id, const char *node_type,
long file_size, const char *parent_id) {
if (node_cache_count < NODE_CACHE_SIZE) {
strcpy(node_cache[node_cache_count].node_id, node_id);
strcpy(node_cache[node_cache_count].node_type, node_type);
node_cache[node_cache_count].file_size = file_size;
if (parent_id) strcpy(node_cache[node_cache_count].parent_id, parent_id);
node_cache_count++;
}
}
static void *mb_init(struct fuse_conn_info *conn, struct fuse_config *cfg) {
(void) conn;
cfg->kernel_cache = 1;
init_db();
// Pre-cache largest files
pthread_mutex_lock(&db_mutex);
const char *sql = "SELECT f.file_uuid, l.location, f.file_size "
"FROM file_nodes f JOIN file_locations l ON f.file_uuid = l.file_uuid "
"ORDER BY f.file_size DESC LIMIT 200";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
while (sqlite3_step(stmt) == SQLITE_ROW) {
cache_insert(
(const char*)sqlite3_column_text(stmt, 0),
(const char*)sqlite3_column_text(stmt, 1),
sqlite3_column_int64(stmt, 2)
);
}
sqlite3_finalize(stmt);
printf("Pre-cached %d files\n", cache_count);
}
pthread_mutex_unlock(&db_mutex);
return NULL;
}
static void mb_destroy(void *userdata) {
(void) userdata;
printf("Cache stats: %d files, %d nodes\n", cache_count, node_cache_count);
if (db) {
pthread_mutex_lock(&db_mutex);
sqlite3_close(db);
pthread_mutex_unlock(&db_mutex);
}
}
// Optimized path lookup
static char* find_node_id(const char *path) {
if (strcmp(path, "/") == 0) return NULL;
char *path_copy = strdup(path);
char *components[20];
int depth = 0;
char *token = strtok(path_copy + 1, "/");
while (token && depth < 20) {
components[depth++] = strdup(token);
token = strtok(NULL, "/");
}
free(path_copy);
if (depth == 0) return NULL;
char *current_parent_id = NULL;
pthread_mutex_lock(&db_mutex);
for (int level = 0; level < depth; level++) {
const char *sql;
sqlite3_stmt *stmt;
if (level == 0) {
sql = "SELECT node_id, node_type, file_size FROM file_nodes "
"WHERE label = ? AND (parent_id IS NULL OR parent_id = '') LIMIT 1";
} else {
sql = "SELECT node_id, node_type, file_size FROM file_nodes "
"WHERE label = ? AND parent_id = ? LIMIT 1";
}
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
pthread_mutex_unlock(&db_mutex);
for (int i = 0; i < depth; i++) free(components[i]);
if (current_parent_id) free(current_parent_id);
return NULL;
}
sqlite3_bind_text(stmt, 1, components[level], -1, SQLITE_STATIC);
if (level > 0 && current_parent_id) {
sqlite3_bind_text(stmt, 2, current_parent_id, -1, SQLITE_STATIC);
}
if (sqlite3_step(stmt) != SQLITE_ROW) {
sqlite3_finalize(stmt);
pthread_mutex_unlock(&db_mutex);
for (int i = 0; i < depth; i++) free(components[i]);
if (current_parent_id) free(current_parent_id);
return NULL;
}
const char *found_node_id = (const char*)sqlite3_column_text(stmt, 0);
node_cache_insert(found_node_id,
(const char*)sqlite3_column_text(stmt, 1),
sqlite3_column_int64(stmt, 2),
current_parent_id);
if (current_parent_id) free(current_parent_id);
current_parent_id = strdup(found_node_id);
sqlite3_finalize(stmt);
}
pthread_mutex_unlock(&db_mutex);
for (int i = 0; i < depth; i++) free(components[i]);
return current_parent_id;
}
static int mb_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi) {
(void) fi;
memset(stbuf, 0, sizeof(struct stat));
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
return 0;
}
char *node_id = find_node_id(path);
if (!node_id) return -ENOENT;
NodeCacheEntry *cached = node_cache_lookup(node_id);
if (cached) {
if (strcmp(cached->node_type, "folder") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
} else {
stbuf->st_mode = S_IFREG | 0644; // Allow write
stbuf->st_nlink = 1;
stbuf->st_size = cached->file_size;
}
free(node_id);
return 0;
}
pthread_mutex_lock(&db_mutex);
sqlite3_stmt *stmt;
const char *sql = "SELECT node_type, file_size FROM file_nodes WHERE node_id = ?";
int result = -ENOENT;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, node_id, -1, SQLITE_STATIC);
if (sqlite3_step(stmt) == SQLITE_ROW) {
const char *node_type = (const char*)sqlite3_column_text(stmt, 0);
long file_size = sqlite3_column_int64(stmt, 1);
if (strcmp(node_type, "folder") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
} else {
stbuf->st_mode = S_IFREG | 0644;
stbuf->st_nlink = 1;
stbuf->st_size = file_size;
}
result = 0;
}
sqlite3_finalize(stmt);
}
pthread_mutex_unlock(&db_mutex);
free(node_id);
return result;
}
static int mb_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags) {
(void) offset; (void) fi; (void) flags;
filler(buf, ".", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
filler(buf, "..", NULL, 0, FUSE_FILL_DIR_DEFAULTS);
pthread_mutex_lock(&db_mutex);
if (strcmp(path, "/") == 0) {
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, "SELECT label FROM file_nodes WHERE parent_id IS NULL OR parent_id = ''",
-1, &stmt, NULL) == SQLITE_OK) {
while (sqlite3_step(stmt) == SQLITE_ROW) {
filler(buf, (const char*)sqlite3_column_text(stmt, 0), NULL, 0, FUSE_FILL_DIR_DEFAULTS);
}
sqlite3_finalize(stmt);
}
pthread_mutex_unlock(&db_mutex);
return 0;
}
char *parent_node_id = find_node_id(path);
if (!parent_node_id) {
pthread_mutex_unlock(&db_mutex);
return -ENOENT;
}
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, "SELECT label FROM file_nodes WHERE parent_id = ?",
-1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, parent_node_id, -1, SQLITE_STATIC);
while (sqlite3_step(stmt) == SQLITE_ROW) {
filler(buf, (const char*)sqlite3_column_text(stmt, 0), NULL, 0, FUSE_FILL_DIR_DEFAULTS);
}
sqlite3_finalize(stmt);
}
pthread_mutex_unlock(&db_mutex);
free(parent_node_id);
return 0;
}
// Fixed: Allow write access
static int mb_open(const char *path, struct fuse_file_info *fi) {
// Allow read and write
char *node_id = find_node_id(path);
if (!node_id) return -ENOENT;
free(node_id);
return 0;
}
// Optimized read
static int mb_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi) {
(void) fi;
char *node_id = find_node_id(path);
if (!node_id) return -ENOENT;
FileCacheEntry *cached = cache_lookup(node_id);
char *file_path = NULL;
if (cached) {
file_path = strdup(cached->file_path);
} else {
pthread_mutex_lock(&db_mutex);
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, "SELECT location FROM file_locations WHERE file_uuid = ? LIMIT 1",
-1, &stmt, NULL) != SQLITE_OK) {
pthread_mutex_unlock(&db_mutex);
free(node_id);
return -EIO;
}
sqlite3_bind_text(stmt, 1, node_id, -1, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_ROW) {
sqlite3_finalize(stmt);
pthread_mutex_unlock(&db_mutex);
free(node_id);
return -ENOENT;
}
file_path = strdup((const char*)sqlite3_column_text(stmt, 0));
sqlite3_finalize(stmt);
pthread_mutex_unlock(&db_mutex);
cache_insert(node_id, file_path, 0);
}
free(node_id);
FILE *fp = fopen(file_path, "rb");
if (!fp) {
free(file_path);
return -ENOENT;
}
fseek(fp, offset, SEEK_SET);
size_t total_read = 0;
while (total_read < size) {
size_t chunk = (size - total_read > READ_CHUNK_SIZE) ? READ_CHUNK_SIZE : size - total_read;
size_t bytes = fread(buf + total_read, 1, chunk, fp);
total_read += bytes;
if (bytes < chunk) break;
}
fclose(fp);
free(file_path);
return total_read;
}
// Fixed: Write implementation
static int mb_write(const char *path, const char *buf, size_t size, off_t offset,
struct fuse_file_info *fi) {
(void) fi;
char *node_id = find_node_id(path);
if (!node_id) return -ENOENT;
FileCacheEntry *cached = cache_lookup(node_id);
char *file_path = NULL;
if (cached) {
file_path = strdup(cached->file_path);
} else {
pthread_mutex_lock(&db_mutex);
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, "SELECT location FROM file_locations WHERE file_uuid = ? LIMIT 1",
-1, &stmt, NULL) != SQLITE_OK) {
pthread_mutex_unlock(&db_mutex);
free(node_id);
return -EIO;
}
sqlite3_bind_text(stmt, 1, node_id, -1, SQLITE_STATIC);
if (sqlite3_step(stmt) != SQLITE_ROW) {
sqlite3_finalize(stmt);
pthread_mutex_unlock(&db_mutex);
free(node_id);
return -ENOENT;
}
file_path = strdup((const char*)sqlite3_column_text(stmt, 0));
sqlite3_finalize(stmt);
pthread_mutex_unlock(&db_mutex);
}
free(node_id);
FILE *fp = fopen(file_path, "r+b");
if (!fp) {
free(file_path);
return -ENOENT;
}
fseek(fp, offset, SEEK_SET);
size_t bytes_written = fwrite(buf, 1, size, fp);
fclose(fp);
free(file_path);
return bytes_written;
}
// Fixed: mkdir implementation
static int mb_mkdir(const char *path, mode_t mode) {
(void) mode;
// Extract parent path and folder name
char *path_copy = strdup(path);
char *last_slash = strrchr(path_copy, '/');
if (!last_slash || strlen(last_slash + 1) == 0) {
free(path_copy);
return -EINVAL;
}
char *folder_name = strdup(last_slash + 1);
*last_slash = '\0';
char *parent_path = (strlen(path_copy) == 0) ? strdup("/") : strdup(path_copy);
free(path_copy);
// Find parent node_id
char *parent_node_id = NULL;
if (strcmp(parent_path, "/") == 0) {
parent_node_id = NULL; // Root
} else {
parent_node_id = find_node_id(parent_path);
if (!parent_node_id) {
free(folder_name);
free(parent_path);
return -ENOENT;
}
}
// Generate new node_id
char new_node_id[64];
snprintf(new_node_id, sizeof(new_node_id), "folder_%ld_%d", time(NULL), rand() % 10000);
// Insert into database
pthread_mutex_lock(&db_mutex);
sqlite3_stmt *stmt;
const char *sql = "INSERT INTO file_nodes (node_id, label, parent_id, node_type, file_size, created_at, updated_at) "
"VALUES (?, ?, ?, 'folder', 0, datetime('now'), datetime('now'))";
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
pthread_mutex_unlock(&db_mutex);
free(folder_name);
free(parent_path);
if (parent_node_id) free(parent_node_id);
return -EIO;
}
sqlite3_bind_text(stmt, 1, new_node_id, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, folder_name, -1, SQLITE_STATIC);
if (parent_node_id) {
sqlite3_bind_text(stmt, 3, parent_node_id, -1, SQLITE_STATIC);
} else {
sqlite3_bind_null(stmt, 3);
}
int result = sqlite3_step(stmt);
sqlite3_finalize(stmt);
pthread_mutex_unlock(&db_mutex);
free(folder_name);
free(parent_path);
if (parent_node_id) free(parent_node_id);
return (result == SQLITE_DONE) ? 0 : -EIO;
}
static const struct fuse_operations mb_oper = {
.init = mb_init,
.destroy = mb_destroy,
.getattr = mb_getattr,
.readdir = mb_readdir,
.open = mb_open,
.read = mb_read,
.write = mb_write,
.mkdir = mb_mkdir,
};
int main(int argc, char *argv[]) {
printf("MarkBase FUSE v13.0 - Stable + Optimized\n");
printf("========================================\n");
printf("Features:\n");
printf(" - Fixed write support (0644 permissions)\n");
printf(" - Fixed mkdir support (proper path parsing)\n");
printf(" - Optimized read (128KB chunks)\n");
printf(" - Thread-safe SQLite\n");
printf(" - LRU cache (200 entries)\n");
printf("\n");
return fuse_main(argc, argv, &mb_oper, NULL);
}