luajitos

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

ramdisk.c (36324B)


      1 #include "ramdisk.h"
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <lauxlib.h>
      5 
      6 /* Global C ramdisk root */
      7 ramdisk_node_t* c_ramdisk_root = NULL;
      8 
      9 /* Custom strdup for freestanding environment */
     10 static char* my_strdup(const char* s) {
     11     if (!s) return NULL;
     12     size_t len = strlen(s) + 1;
     13     char* new_str = (char*)malloc(len);
     14     if (new_str) {
     15         memcpy(new_str, s, len);
     16     }
     17     return new_str;
     18 }
     19 
     20 /* Custom strtok for freestanding environment */
     21 static char* my_strtok(char* str, const char* delim) {
     22     static char* saved = NULL;
     23 
     24     if (str) {
     25         saved = str;
     26     }
     27 
     28     if (!saved || !*saved) {
     29         return NULL;
     30     }
     31 
     32     /* Skip leading delimiters */
     33     while (*saved && strchr(delim, *saved)) {
     34         saved++;
     35     }
     36 
     37     if (!*saved) {
     38         return NULL;
     39     }
     40 
     41     char* token = saved;
     42 
     43     /* Find end of token */
     44     while (*saved && !strchr(delim, *saved)) {
     45         saved++;
     46     }
     47 
     48     if (*saved) {
     49         *saved = '\0';
     50         saved++;
     51     }
     52 
     53     return token;
     54 }
     55 
     56 #define strdup my_strdup
     57 #define strtok my_strtok
     58 
     59 /* Create root directory */
     60 ramdisk_node_t* ramdisk_create_root(void) {
     61     ramdisk_node_t* root = (ramdisk_node_t*)malloc(sizeof(ramdisk_node_t));
     62     if (!root) return NULL;
     63 
     64     root->name = strdup("");
     65     root->type = RAMDISK_DIR;
     66     root->parent = NULL;
     67     root->dir.entries = NULL;
     68     root->dir.entry_count = 0;
     69 
     70     return root;
     71 }
     72 
     73 /* Destroy node and all children recursively */
     74 void ramdisk_destroy_node(ramdisk_node_t* node) {
     75     if (!node) return;
     76 
     77     if (node->type == RAMDISK_DIR) {
     78         /* Free all directory entries */
     79         ramdisk_dir_entry_t* entry = node->dir.entries;
     80         while (entry) {
     81             ramdisk_dir_entry_t* next = entry->next;
     82             ramdisk_destroy_node(entry->node);
     83             free(entry->name);
     84             free(entry);
     85             entry = next;
     86         }
     87     } else {
     88         /* Free file data */
     89         if (node->file.data) {
     90             free(node->file.data);
     91         }
     92     }
     93 
     94     free(node->name);
     95     free(node);
     96 }
     97 
     98 /* Create directory */
     99 ramdisk_node_t* ramdisk_create_dir(ramdisk_node_t* parent, const char* name) {
    100     if (!parent || parent->type != RAMDISK_DIR || !name) {
    101         return NULL;
    102     }
    103 
    104     /* Check if name already exists */
    105     ramdisk_dir_entry_t* entry = parent->dir.entries;
    106     while (entry) {
    107         if (strcmp(entry->name, name) == 0) {
    108             return NULL;  /* Already exists */
    109         }
    110         entry = entry->next;
    111     }
    112 
    113     /* Create new directory node */
    114     ramdisk_node_t* dir = (ramdisk_node_t*)malloc(sizeof(ramdisk_node_t));
    115     if (!dir) return NULL;
    116 
    117     dir->name = strdup(name);
    118     dir->type = RAMDISK_DIR;
    119     dir->parent = parent;
    120     dir->dir.entries = NULL;
    121     dir->dir.entry_count = 0;
    122 
    123     /* Add to parent */
    124     ramdisk_dir_entry_t* new_entry = (ramdisk_dir_entry_t*)malloc(sizeof(ramdisk_dir_entry_t));
    125     if (!new_entry) {
    126         free(dir->name);
    127         free(dir);
    128         return NULL;
    129     }
    130 
    131     new_entry->name = strdup(name);
    132     new_entry->node = dir;
    133     new_entry->next = parent->dir.entries;
    134     parent->dir.entries = new_entry;
    135     parent->dir.entry_count++;
    136 
    137     return dir;
    138 }
    139 
    140 /* Create file */
    141 ramdisk_node_t* ramdisk_create_file(ramdisk_node_t* parent, const char* name) {
    142     if (!parent || parent->type != RAMDISK_DIR || !name) {
    143         return NULL;
    144     }
    145 
    146     /* Check if name already exists */
    147     ramdisk_dir_entry_t* entry = parent->dir.entries;
    148     while (entry) {
    149         if (strcmp(entry->name, name) == 0) {
    150             return NULL;  /* Already exists */
    151         }
    152         entry = entry->next;
    153     }
    154 
    155     /* Create new file node */
    156     ramdisk_node_t* file = (ramdisk_node_t*)malloc(sizeof(ramdisk_node_t));
    157     if (!file) return NULL;
    158 
    159     file->name = strdup(name);
    160     file->type = RAMDISK_FILE;
    161     file->parent = parent;
    162     file->file.data = NULL;
    163     file->file.size = 0;
    164     file->file.capacity = 0;
    165 
    166     /* Add to parent */
    167     ramdisk_dir_entry_t* new_entry = (ramdisk_dir_entry_t*)malloc(sizeof(ramdisk_dir_entry_t));
    168     if (!new_entry) {
    169         free(file->name);
    170         free(file);
    171         return NULL;
    172     }
    173 
    174     new_entry->name = strdup(name);
    175     new_entry->node = file;
    176     new_entry->next = parent->dir.entries;
    177     parent->dir.entries = new_entry;
    178     parent->dir.entry_count++;
    179 
    180     return file;
    181 }
    182 
    183 /* Write to file (replaces content) */
    184 int ramdisk_write_file(ramdisk_node_t* file, const uint8_t* data, uint32_t size) {
    185     if (!file || file->type != RAMDISK_FILE) return -1;
    186 
    187     /* Allocate or reallocate buffer */
    188     if (size > file->file.capacity) {
    189         uint8_t* new_data = (uint8_t*)realloc(file->file.data, size);
    190         if (!new_data) return -1;
    191         file->file.data = new_data;
    192         file->file.capacity = size;
    193     }
    194 
    195     if (data && size > 0) {
    196         memcpy(file->file.data, data, size);
    197     }
    198     file->file.size = size;
    199 
    200     return 0;
    201 }
    202 
    203 /* Append to file */
    204 int ramdisk_append_file(ramdisk_node_t* file, const uint8_t* data, uint32_t size) {
    205     if (!file || file->type != RAMDISK_FILE) return -1;
    206     if (!data || size == 0) return 0;
    207 
    208     uint32_t new_size = file->file.size + size;
    209 
    210     /* Reallocate if needed */
    211     if (new_size > file->file.capacity) {
    212         uint32_t new_capacity = new_size * 2;  /* Double for growth */
    213         uint8_t* new_data = (uint8_t*)realloc(file->file.data, new_capacity);
    214         if (!new_data) return -1;
    215         file->file.data = new_data;
    216         file->file.capacity = new_capacity;
    217     }
    218 
    219     memcpy(file->file.data + file->file.size, data, size);
    220     file->file.size = new_size;
    221 
    222     return 0;
    223 }
    224 
    225 /* Read from file */
    226 int ramdisk_read_file(ramdisk_node_t* file, uint8_t* buffer, uint32_t size, uint32_t* bytes_read) {
    227     if (!file || file->type != RAMDISK_FILE || !buffer) return -1;
    228 
    229     uint32_t to_read = (size < file->file.size) ? size : file->file.size;
    230 
    231     if (to_read > 0 && file->file.data) {
    232         memcpy(buffer, file->file.data, to_read);
    233     }
    234 
    235     if (bytes_read) {
    236         *bytes_read = to_read;
    237     }
    238 
    239     return 0;
    240 }
    241 
    242 /* Truncate file */
    243 void ramdisk_truncate_file(ramdisk_node_t* file) {
    244     if (!file || file->type != RAMDISK_FILE) return;
    245     file->file.size = 0;
    246 }
    247 
    248 /* Split path into components */
    249 static char** split_path(const char* path, uint32_t* count) {
    250     if (!path || !count) return NULL;
    251 
    252     *count = 0;
    253 
    254     /* Skip leading slashes */
    255     while (*path == '/') path++;
    256 
    257     if (*path == '\0') {
    258         return NULL;  /* Root path */
    259     }
    260 
    261     /* Count components */
    262     const char* p = path;
    263     uint32_t num_components = 1;
    264     while (*p) {
    265         if (*p == '/') {
    266             num_components++;
    267             while (*p == '/') p++;  /* Skip multiple slashes */
    268         } else {
    269             p++;
    270         }
    271     }
    272 
    273     /* Allocate array */
    274     char** components = (char**)malloc(sizeof(char*) * num_components);
    275     if (!components) return NULL;
    276 
    277     /* Extract components */
    278     uint32_t idx = 0;
    279     const char* start = path;
    280     p = path;
    281 
    282     while (*p) {
    283         if (*p == '/') {
    284             uint32_t len = p - start;
    285             if (len > 0) {
    286                 components[idx] = (char*)malloc(len + 1);
    287                 memcpy(components[idx], start, len);
    288                 components[idx][len] = '\0';
    289                 idx++;
    290             }
    291             while (*p == '/') p++;
    292             start = p;
    293         } else {
    294             p++;
    295         }
    296     }
    297 
    298     /* Last component */
    299     if (p > start) {
    300         uint32_t len = p - start;
    301         components[idx] = (char*)malloc(len + 1);
    302         memcpy(components[idx], start, len);
    303         components[idx][len] = '\0';
    304         idx++;
    305     }
    306 
    307     *count = idx;
    308     return components;
    309 }
    310 
    311 /* Traverse path */
    312 ramdisk_node_t* ramdisk_traverse(ramdisk_node_t* root, const char* path) {
    313     if (!root || !path) return NULL;
    314 
    315     /* Empty path or just "/" returns root */
    316     if (*path == '\0' || (path[0] == '/' && path[1] == '\0')) {
    317         return root;
    318     }
    319 
    320     uint32_t count;
    321     char** components = split_path(path, &count);
    322 
    323     if (!components) {
    324         return root;
    325     }
    326 
    327     ramdisk_node_t* current = root;
    328 
    329     for (uint32_t i = 0; i < count; i++) {
    330         if (current->type != RAMDISK_DIR) {
    331             /* Not a directory, can't traverse further */
    332             for (uint32_t j = 0; j < count; j++) {
    333                 free(components[j]);
    334             }
    335             free(components);
    336             return NULL;
    337         }
    338 
    339         /* Search for component in directory */
    340         ramdisk_dir_entry_t* entry = current->dir.entries;
    341         int found = 0;
    342 
    343         while (entry) {
    344             if (strcmp(entry->name, components[i]) == 0) {
    345                 current = entry->node;
    346                 found = 1;
    347                 break;
    348             }
    349             entry = entry->next;
    350         }
    351 
    352         if (!found) {
    353             for (uint32_t j = 0; j < count; j++) {
    354                 free(components[j]);
    355             }
    356             free(components);
    357             return NULL;
    358         }
    359     }
    360 
    361     for (uint32_t i = 0; i < count; i++) {
    362         free(components[i]);
    363     }
    364     free(components);
    365 
    366     return current;
    367 }
    368 
    369 /* Get full path of node */
    370 char* ramdisk_get_full_path(ramdisk_node_t* node) {
    371     if (!node) return NULL;
    372 
    373     /* Count path length */
    374     uint32_t length = 0;
    375     ramdisk_node_t* current = node;
    376     while (current->parent) {
    377         length += strlen(current->name) + 1;  /* +1 for '/' */
    378         current = current->parent;
    379     }
    380 
    381     if (length == 0) {
    382         return strdup("/");
    383     }
    384 
    385     /* Build path backwards */
    386     char* path = (char*)malloc(length + 2);  /* +2 for leading '/' and null */
    387     if (!path) return NULL;
    388 
    389     path[length + 1] = '\0';
    390     path[0] = '/';
    391     uint32_t pos = length;
    392 
    393     current = node;
    394     while (current->parent) {
    395         uint32_t name_len = strlen(current->name);
    396         pos -= name_len;
    397         memcpy(path + pos + 1, current->name, name_len);
    398         pos--;
    399         path[pos + 1] = '/';
    400         current = current->parent;
    401     }
    402 
    403     return path;
    404 }
    405 
    406 /* Open file handle */
    407 ramdisk_handle_t* ramdisk_open(ramdisk_node_t* root, const char* path, const char* mode) {
    408     if (!root || !path || !mode) return NULL;
    409 
    410     char open_mode = mode[0];
    411     if (open_mode != 'r' && open_mode != 'w' && open_mode != 'a') {
    412         return NULL;
    413     }
    414 
    415     ramdisk_node_t* node = ramdisk_traverse(root, path);
    416 
    417     if (open_mode == 'r') {
    418         /* Read mode: file must exist */
    419         if (!node || node->type != RAMDISK_FILE) {
    420             return NULL;
    421         }
    422     } else if (open_mode == 'w' || open_mode == 'a') {
    423         /* Write/Append mode: create if doesn't exist */
    424         if (!node) {
    425             /* Create file */
    426             uint32_t count;
    427             char** components = split_path(path, &count);
    428             if (!components || count == 0) return NULL;
    429 
    430             /* Find parent directory */
    431             ramdisk_node_t* parent = root;
    432             for (uint32_t i = 0; i < count - 1; i++) {
    433                 parent = ramdisk_traverse(parent, components[i]);
    434                 if (!parent || parent->type != RAMDISK_DIR) {
    435                     for (uint32_t j = 0; j < count; j++) free(components[j]);
    436                     free(components);
    437                     return NULL;
    438                 }
    439             }
    440 
    441             /* Create file */
    442             node = ramdisk_create_file(parent, components[count - 1]);
    443 
    444             for (uint32_t i = 0; i < count; i++) free(components[i]);
    445             free(components);
    446 
    447             if (!node) return NULL;
    448         }
    449 
    450         if (node->type != RAMDISK_FILE) {
    451             return NULL;
    452         }
    453 
    454         /* Truncate in write mode */
    455         if (open_mode == 'w') {
    456             ramdisk_truncate_file(node);
    457         }
    458     }
    459 
    460     /* Create handle */
    461     ramdisk_handle_t* handle = (ramdisk_handle_t*)malloc(sizeof(ramdisk_handle_t));
    462     if (!handle) return NULL;
    463 
    464     handle->node = node;
    465     handle->position = (open_mode == 'a') ? node->file.size : 0;
    466     handle->mode = open_mode;
    467 
    468     return handle;
    469 }
    470 
    471 /* Read from handle */
    472 int ramdisk_handle_read(ramdisk_handle_t* handle, uint8_t* buffer, uint32_t size, uint32_t* bytes_read) {
    473     if (!handle || handle->mode != 'r' || !buffer) return -1;
    474 
    475     ramdisk_node_t* file = handle->node;
    476     if (!file || file->type != RAMDISK_FILE) return -1;
    477 
    478     uint32_t available = file->file.size - handle->position;
    479     uint32_t to_read = (size < available) ? size : available;
    480 
    481     if (to_read > 0 && file->file.data) {
    482         memcpy(buffer, file->file.data + handle->position, to_read);
    483         handle->position += to_read;
    484     }
    485 
    486     if (bytes_read) {
    487         *bytes_read = to_read;
    488     }
    489 
    490     return 0;
    491 }
    492 
    493 /* Write to handle */
    494 int ramdisk_handle_write(ramdisk_handle_t* handle, const uint8_t* data, uint32_t size) {
    495     if (!handle || (handle->mode != 'w' && handle->mode != 'a') || !data) return -1;
    496 
    497     ramdisk_node_t* file = handle->node;
    498     if (!file || file->type != RAMDISK_FILE) return -1;
    499 
    500     if (handle->mode == 'a') {
    501         /* Append mode */
    502         int result = ramdisk_append_file(file, data, size);
    503         if (result == 0) {
    504             handle->position = file->file.size;
    505         }
    506         return result;
    507     } else {
    508         /* Write mode - insert at position */
    509         uint32_t new_size = handle->position + size;
    510 
    511         if (new_size > file->file.capacity) {
    512             uint32_t new_capacity = new_size * 2;
    513             uint8_t* new_data = (uint8_t*)realloc(file->file.data, new_capacity);
    514             if (!new_data) return -1;
    515             file->file.data = new_data;
    516             file->file.capacity = new_capacity;
    517         }
    518 
    519         memcpy(file->file.data + handle->position, data, size);
    520         handle->position += size;
    521 
    522         if (handle->position > file->file.size) {
    523             file->file.size = handle->position;
    524         }
    525 
    526         return 0;
    527     }
    528 }
    529 
    530 /* Seek */
    531 int ramdisk_handle_seek(ramdisk_handle_t* handle, int32_t offset, int whence) {
    532     if (!handle) return -1;
    533 
    534     ramdisk_node_t* file = handle->node;
    535     if (!file || file->type != RAMDISK_FILE) return -1;
    536 
    537     int32_t new_pos;
    538 
    539     if (whence == 0) {  /* SEEK_SET */
    540         new_pos = offset;
    541     } else if (whence == 1) {  /* SEEK_CUR */
    542         new_pos = handle->position + offset;
    543     } else if (whence == 2) {  /* SEEK_END */
    544         new_pos = file->file.size + offset;
    545     } else {
    546         return -1;
    547     }
    548 
    549     if (new_pos < 0) new_pos = 0;
    550     if ((uint32_t)new_pos > file->file.size) new_pos = file->file.size;
    551 
    552     handle->position = new_pos;
    553     return 0;
    554 }
    555 
    556 /* Tell */
    557 uint32_t ramdisk_handle_tell(ramdisk_handle_t* handle) {
    558     if (!handle) return 0;
    559     return handle->position;
    560 }
    561 
    562 /* Close handle */
    563 void ramdisk_close(ramdisk_handle_t* handle) {
    564     if (handle) {
    565         free(handle);
    566     }
    567 }
    568 
    569 /* Check if path exists */
    570 int ramdisk_exists(ramdisk_node_t* root, const char* path) {
    571     return ramdisk_traverse(root, path) != NULL;
    572 }
    573 
    574 /* Check if node is file */
    575 int ramdisk_is_file(ramdisk_node_t* node) {
    576     return node && node->type == RAMDISK_FILE;
    577 }
    578 
    579 /* Check if node is directory */
    580 int ramdisk_is_dir(ramdisk_node_t* node) {
    581     return node && node->type == RAMDISK_DIR;
    582 }
    583 
    584 /* Get file size */
    585 uint32_t ramdisk_get_file_size(ramdisk_node_t* file) {
    586     if (!file || file->type != RAMDISK_FILE) return 0;
    587     return file->file.size;
    588 }
    589 
    590 /* List directory */
    591 ramdisk_dir_list_t* ramdisk_list_dir(ramdisk_node_t* dir) {
    592     if (!dir || dir->type != RAMDISK_DIR) return NULL;
    593 
    594     ramdisk_dir_list_t* list = (ramdisk_dir_list_t*)malloc(sizeof(ramdisk_dir_list_t));
    595     if (!list) return NULL;
    596 
    597     list->count = dir->dir.entry_count;
    598 
    599     if (list->count == 0) {
    600         list->names = NULL;
    601         list->types = NULL;
    602         return list;
    603     }
    604 
    605     list->names = (char**)malloc(sizeof(char*) * list->count);
    606     list->types = (ramdisk_node_type_t*)malloc(sizeof(ramdisk_node_type_t) * list->count);
    607 
    608     if (!list->names || !list->types) {
    609         free(list->names);
    610         free(list->types);
    611         free(list);
    612         return NULL;
    613     }
    614 
    615     ramdisk_dir_entry_t* entry = dir->dir.entries;
    616     uint32_t idx = 0;
    617 
    618     while (entry && idx < list->count) {
    619         list->names[idx] = strdup(entry->name);
    620         list->types[idx] = entry->node->type;
    621         entry = entry->next;
    622         idx++;
    623     }
    624 
    625     return list;
    626 }
    627 
    628 /* Free directory list */
    629 void ramdisk_free_dir_list(ramdisk_dir_list_t* list) {
    630     if (!list) return;
    631 
    632     if (list->names) {
    633         for (uint32_t i = 0; i < list->count; i++) {
    634             free(list->names[i]);
    635         }
    636         free(list->names);
    637     }
    638 
    639     free(list->types);
    640     free(list);
    641 }
    642 
    643 /* Lua bindings */
    644 
    645 /* Create C ramdisk */
    646 int lua_ramdisk_create(lua_State* L) {
    647     if (c_ramdisk_root) {
    648         ramdisk_destroy_node(c_ramdisk_root);
    649     }
    650 
    651     c_ramdisk_root = ramdisk_create_root();
    652 
    653     if (c_ramdisk_root) {
    654         lua_pushboolean(L, 1);
    655         return 1;
    656     } else {
    657         lua_pushnil(L);
    658         lua_pushstring(L, "Failed to create ramdisk");
    659         return 2;
    660     }
    661 }
    662 
    663 /* Open file */
    664 int lua_ramdisk_open(lua_State* L) {
    665     const char* path = luaL_checkstring(L, 1);
    666     const char* mode = luaL_optstring(L, 2, "r");
    667 
    668     if (!c_ramdisk_root) {
    669         lua_pushnil(L);
    670         lua_pushstring(L, "C ramdisk not initialized");
    671         return 2;
    672     }
    673 
    674     ramdisk_handle_t* handle = ramdisk_open(c_ramdisk_root, path, mode);
    675 
    676     if (handle) {
    677         lua_pushlightuserdata(L, handle);
    678         return 1;
    679     } else {
    680         lua_pushnil(L);
    681         lua_pushstring(L, "Failed to open file");
    682         return 2;
    683     }
    684 }
    685 
    686 /* Read from file handle */
    687 int lua_ramdisk_read(lua_State* L) {
    688     ramdisk_handle_t* handle = (ramdisk_handle_t*)lua_touserdata(L, 1);
    689     int size = luaL_optinteger(L, 2, -1);  /* -1 means read all */
    690 
    691     if (!handle) {
    692         lua_pushnil(L);
    693         lua_pushstring(L, "Invalid handle");
    694         return 2;
    695     }
    696 
    697     ramdisk_node_t* file = handle->node;
    698     if (!file || file->type != RAMDISK_FILE) {
    699         lua_pushnil(L);
    700         lua_pushstring(L, "Invalid file");
    701         return 2;
    702     }
    703 
    704     /* Determine how much to read */
    705     uint32_t available = file->file.size - handle->position;
    706     uint32_t to_read = (size < 0 || (uint32_t)size > available) ? available : (uint32_t)size;
    707 
    708     if (to_read == 0) {
    709         lua_pushnil(L);  /* EOF */
    710         return 1;
    711     }
    712 
    713     /* Read data */
    714     uint8_t* buffer = (uint8_t*)malloc(to_read);
    715     if (!buffer) {
    716         lua_pushnil(L);
    717         lua_pushstring(L, "Out of memory");
    718         return 2;
    719     }
    720 
    721     uint32_t bytes_read;
    722     int result = ramdisk_handle_read(handle, buffer, to_read, &bytes_read);
    723 
    724     if (result == 0) {
    725         lua_pushlstring(L, (const char*)buffer, bytes_read);
    726         free(buffer);
    727         return 1;
    728     } else {
    729         free(buffer);
    730         lua_pushnil(L);
    731         lua_pushstring(L, "Read failed");
    732         return 2;
    733     }
    734 }
    735 
    736 /* Write to file handle */
    737 int lua_ramdisk_write(lua_State* L) {
    738     ramdisk_handle_t* handle = (ramdisk_handle_t*)lua_touserdata(L, 1);
    739     size_t size;
    740     const uint8_t* data = (const uint8_t*)luaL_checklstring(L, 2, &size);
    741 
    742     if (!handle) {
    743         lua_pushnil(L);
    744         lua_pushstring(L, "Invalid handle");
    745         return 2;
    746     }
    747 
    748     int result = ramdisk_handle_write(handle, data, size);
    749 
    750     if (result == 0) {
    751         lua_pushboolean(L, 1);
    752         return 1;
    753     } else {
    754         lua_pushnil(L);
    755         lua_pushstring(L, "Write failed");
    756         return 2;
    757     }
    758 }
    759 
    760 /* Close file handle */
    761 int lua_ramdisk_close(lua_State* L) {
    762     ramdisk_handle_t* handle = (ramdisk_handle_t*)lua_touserdata(L, 1);
    763 
    764     if (handle) {
    765         ramdisk_close(handle);
    766     }
    767 
    768     return 0;
    769 }
    770 
    771 /* Seek */
    772 int lua_ramdisk_seek(lua_State* L) {
    773     ramdisk_handle_t* handle = (ramdisk_handle_t*)lua_touserdata(L, 1);
    774     const char* whence_str = luaL_optstring(L, 2, "cur");
    775     int offset = luaL_optinteger(L, 3, 0);
    776 
    777     if (!handle) {
    778         lua_pushnil(L);
    779         lua_pushstring(L, "Invalid handle");
    780         return 2;
    781     }
    782 
    783     int whence = 1;  /* SEEK_CUR */
    784     if (strcmp(whence_str, "set") == 0) {
    785         whence = 0;  /* SEEK_SET */
    786     } else if (strcmp(whence_str, "end") == 0) {
    787         whence = 2;  /* SEEK_END */
    788     }
    789 
    790     int result = ramdisk_handle_seek(handle, offset, whence);
    791 
    792     if (result == 0) {
    793         lua_pushinteger(L, ramdisk_handle_tell(handle));
    794         return 1;
    795     } else {
    796         lua_pushnil(L);
    797         lua_pushstring(L, "Seek failed");
    798         return 2;
    799     }
    800 }
    801 
    802 /* Create directory */
    803 int lua_ramdisk_mkdir(lua_State* L) {
    804     const char* path = luaL_checkstring(L, 1);
    805 
    806     if (!c_ramdisk_root) {
    807         lua_pushnil(L);
    808         lua_pushstring(L, "C ramdisk not initialized");
    809         return 2;
    810     }
    811 
    812     /* Parse path to get parent and dir name */
    813     uint32_t count;
    814     char** components = split_path(path, &count);
    815 
    816     if (!components || count == 0) {
    817         lua_pushnil(L);
    818         lua_pushstring(L, "Invalid path");
    819         return 2;
    820     }
    821 
    822     /* Find parent */
    823     ramdisk_node_t* parent = c_ramdisk_root;
    824     for (uint32_t i = 0; i < count - 1; i++) {
    825         ramdisk_dir_entry_t* entry = parent->dir.entries;
    826         int found = 0;
    827 
    828         while (entry) {
    829             if (strcmp(entry->name, components[i]) == 0) {
    830                 parent = entry->node;
    831                 found = 1;
    832                 break;
    833             }
    834             entry = entry->next;
    835         }
    836 
    837         if (!found) {
    838             for (uint32_t j = 0; j < count; j++) free(components[j]);
    839             free(components);
    840             lua_pushnil(L);
    841             lua_pushstring(L, "Parent directory not found");
    842             return 2;
    843         }
    844     }
    845 
    846     /* Create directory */
    847     ramdisk_node_t* dir = ramdisk_create_dir(parent, components[count - 1]);
    848 
    849     for (uint32_t i = 0; i < count; i++) free(components[i]);
    850     free(components);
    851 
    852     if (dir) {
    853         lua_pushboolean(L, 1);
    854         return 1;
    855     } else {
    856         lua_pushnil(L);
    857         lua_pushstring(L, "Failed to create directory");
    858         return 2;
    859     }
    860 }
    861 
    862 /* Check if exists */
    863 int lua_ramdisk_exists(lua_State* L) {
    864     const char* path = luaL_checkstring(L, 1);
    865 
    866     if (!c_ramdisk_root) {
    867         lua_pushboolean(L, 0);
    868         return 1;
    869     }
    870 
    871     lua_pushboolean(L, ramdisk_exists(c_ramdisk_root, path));
    872     return 1;
    873 }
    874 
    875 /* List directory */
    876 int lua_ramdisk_list(lua_State* L) {
    877     const char* path = luaL_checkstring(L, 1);
    878 
    879     if (!c_ramdisk_root) {
    880         lua_pushnil(L);
    881         lua_pushstring(L, "C ramdisk not initialized");
    882         return 2;
    883     }
    884 
    885     ramdisk_node_t* dir = ramdisk_traverse(c_ramdisk_root, path);
    886 
    887     if (!dir || dir->type != RAMDISK_DIR) {
    888         lua_pushnil(L);
    889         lua_pushstring(L, "Not a directory");
    890         return 2;
    891     }
    892 
    893     ramdisk_dir_list_t* list = ramdisk_list_dir(dir);
    894 
    895     if (!list) {
    896         lua_pushnil(L);
    897         lua_pushstring(L, "Failed to list directory");
    898         return 2;
    899     }
    900 
    901     lua_newtable(L);
    902 
    903     for (uint32_t i = 0; i < list->count; i++) {
    904         lua_newtable(L);
    905 
    906         lua_pushstring(L, list->names[i]);
    907         lua_setfield(L, -2, "name");
    908 
    909         lua_pushstring(L, (list->types[i] == RAMDISK_FILE) ? "file" : "dir");
    910         lua_setfield(L, -2, "type");
    911 
    912         lua_rawseti(L, -2, i + 1);
    913     }
    914 
    915     ramdisk_free_dir_list(list);
    916 
    917     return 1;
    918 }
    919 
    920 /* Remove file/directory */
    921 int lua_ramdisk_remove(lua_State* L) {
    922     const char* path = luaL_checkstring(L, 1);
    923 
    924     if (!c_ramdisk_root) {
    925         lua_pushnil(L);
    926         lua_pushstring(L, "C ramdisk not initialized");
    927         return 2;
    928     }
    929 
    930     ramdisk_node_t* node = ramdisk_traverse(c_ramdisk_root, path);
    931 
    932     if (!node) {
    933         lua_pushnil(L);
    934         lua_pushstring(L, "Path not found");
    935         return 2;
    936     }
    937 
    938     if (!node->parent) {
    939         lua_pushnil(L);
    940         lua_pushstring(L, "Cannot remove root");
    941         return 2;
    942     }
    943 
    944     /* Remove from parent's entry list */
    945     ramdisk_dir_entry_t* entry = node->parent->dir.entries;
    946     ramdisk_dir_entry_t* prev = NULL;
    947 
    948     while (entry) {
    949         if (entry->node == node) {
    950             if (prev) {
    951                 prev->next = entry->next;
    952             } else {
    953                 node->parent->dir.entries = entry->next;
    954             }
    955 
    956             free(entry->name);
    957             free(entry);
    958             node->parent->dir.entry_count--;
    959 
    960             break;
    961         }
    962 
    963         prev = entry;
    964         entry = entry->next;
    965     }
    966 
    967     /* Destroy node */
    968     ramdisk_destroy_node(node);
    969 
    970     lua_pushboolean(L, 1);
    971     return 1;
    972 }
    973 
    974 /* ========== Packed Ramdisk Parsing ========== */
    975 
    976 /* Base64 decoding table */
    977 static const uint8_t base64_decode_table[256] = {
    978     ['A'] = 0,  ['B'] = 1,  ['C'] = 2,  ['D'] = 3,  ['E'] = 4,  ['F'] = 5,
    979     ['G'] = 6,  ['H'] = 7,  ['I'] = 8,  ['J'] = 9,  ['K'] = 10, ['L'] = 11,
    980     ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, ['Q'] = 16, ['R'] = 17,
    981     ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23,
    982     ['Y'] = 24, ['Z'] = 25, ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29,
    983     ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35,
    984     ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41,
    985     ['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47,
    986     ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51, ['0'] = 52, ['1'] = 53,
    987     ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
    988     ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63
    989 };
    990 
    991 /* Decode base64 string */
    992 static uint8_t* base64_decode(const char* input, uint32_t input_len, uint32_t* output_len) {
    993     if (!input || input_len == 0) {
    994         *output_len = 0;
    995         return NULL;
    996     }
    997 
    998     /* Calculate output size */
    999     uint32_t padding = 0;
   1000     if (input_len >= 2 && input[input_len - 1] == '=') padding++;
   1001     if (input_len >= 3 && input[input_len - 2] == '=') padding++;
   1002 
   1003     uint32_t max_output = (input_len / 4) * 3 - padding;
   1004     uint8_t* output = (uint8_t*)malloc(max_output);
   1005     if (!output) {
   1006         *output_len = 0;
   1007         return NULL;
   1008     }
   1009 
   1010     uint32_t out_pos = 0;
   1011     for (uint32_t i = 0; i < input_len; i += 4) {
   1012         uint8_t b1 = base64_decode_table[(uint8_t)input[i]];
   1013         uint8_t b2 = (i + 1 < input_len) ? base64_decode_table[(uint8_t)input[i + 1]] : 0;
   1014         uint8_t b3 = (i + 2 < input_len && input[i + 2] != '=') ? base64_decode_table[(uint8_t)input[i + 2]] : 0;
   1015         uint8_t b4 = (i + 3 < input_len && input[i + 3] != '=') ? base64_decode_table[(uint8_t)input[i + 3]] : 0;
   1016 
   1017         output[out_pos++] = (b1 << 2) | (b2 >> 4);
   1018         if (i + 2 < input_len && input[i + 2] != '=') {
   1019             output[out_pos++] = (b2 << 4) | (b3 >> 2);
   1020         }
   1021         if (i + 3 < input_len && input[i + 3] != '=') {
   1022             output[out_pos++] = (b3 << 6) | b4;
   1023         }
   1024     }
   1025 
   1026     *output_len = out_pos;
   1027     return output;
   1028 }
   1029 
   1030 /* Unescape text content */
   1031 static char* unescape_text(const char* input, uint32_t input_len) {
   1032     char* output = (char*)malloc(input_len + 1);
   1033     if (!output) return NULL;
   1034 
   1035     uint32_t out_pos = 0;
   1036     for (uint32_t i = 0; i < input_len; i++) {
   1037         if (input[i] == '\\' && i + 1 < input_len) {
   1038             switch (input[i + 1]) {
   1039                 case 'n': output[out_pos++] = '\n'; i++; break;
   1040                 case 't': output[out_pos++] = '\t'; i++; break;
   1041                 case 'r': output[out_pos++] = '\r'; i++; break;
   1042                 case '\\': output[out_pos++] = '\\'; i++; break;
   1043                 default: output[out_pos++] = input[i]; break;
   1044             }
   1045         } else {
   1046             output[out_pos++] = input[i];
   1047         }
   1048     }
   1049     output[out_pos] = '\0';
   1050     return output;
   1051 }
   1052 
   1053 /* Create directory path recursively */
   1054 static ramdisk_node_t* ensure_directory_path(ramdisk_node_t* root, const char* path) {
   1055     if (!path || path[0] == '\0') return root;
   1056 
   1057     /* Split path into components */
   1058     char* path_copy = strdup(path);
   1059     ramdisk_node_t* current = root;
   1060 
   1061     char* token = strtok(path_copy, "/");
   1062     while (token) {
   1063         /* Check if directory exists */
   1064         ramdisk_dir_entry_t* entry = current->dir.entries;
   1065         ramdisk_node_t* next = NULL;
   1066 
   1067         while (entry) {
   1068             if (strcmp(entry->name, token) == 0 && entry->node->type == RAMDISK_DIR) {
   1069                 next = entry->node;
   1070                 break;
   1071             }
   1072             entry = entry->next;
   1073         }
   1074 
   1075         /* Create if doesn't exist */
   1076         if (!next) {
   1077             next = ramdisk_create_dir(current, token);
   1078             if (!next) {
   1079                 free(path_copy);
   1080                 return NULL;
   1081             }
   1082         }
   1083 
   1084         current = next;
   1085         token = strtok(NULL, "/");
   1086     }
   1087 
   1088     free(path_copy);
   1089     return current;
   1090 }
   1091 
   1092 /* Parse packed ramdisk format and populate ramdisk tree */
   1093 int ramdisk_load_packed(ramdisk_node_t* root, const uint8_t* packed_data, uint32_t packed_size) {
   1094     if (!root || !packed_data || packed_size == 0) {
   1095         return -1;
   1096     }
   1097 
   1098     /* Convert to string for parsing */
   1099     char* data = (char*)malloc(packed_size + 1);
   1100     if (!data) return -1;
   1101     memcpy(data, packed_data, packed_size);
   1102     data[packed_size] = '\0';
   1103 
   1104     /* Split by null byte delimiter (\0\0\0\0) */
   1105     char* entry_start = data;
   1106     uint32_t entry_count = 0;
   1107     char* data_end = data + packed_size;
   1108 
   1109     while (entry_start < data_end && *entry_start) {
   1110         /* Find next \0\0\0\0 delimiter */
   1111         char* entry_end = NULL;
   1112         for (char* p = entry_start; p < data_end - 3; p++) {
   1113             if (p[0] == '\0' && p[1] == '\0' && p[2] == '\0' && p[3] == '\0') {
   1114                 entry_end = p;
   1115                 break;
   1116             }
   1117         }
   1118 
   1119         uint32_t entry_len;
   1120 
   1121         if (entry_end) {
   1122             entry_len = entry_end - entry_start;
   1123         } else {
   1124             entry_len = data_end - entry_start;
   1125         }
   1126 
   1127         if (entry_len > 0) {
   1128             /* Process entry */
   1129             char* entry = (char*)malloc(entry_len + 1);
   1130             memcpy(entry, entry_start, entry_len);
   1131             entry[entry_len] = '\0';
   1132 
   1133             /* Check if it's a directory (ends with /) */
   1134             if (entry[entry_len - 1] == '/') {
   1135                 /* Directory */
   1136                 entry[entry_len - 1] = '\0';  /* Remove trailing slash */
   1137 
   1138                 /* Get parent directory path */
   1139                 char* last_slash = strrchr(entry, '/');
   1140                 char* dir_name;
   1141                 ramdisk_node_t* parent;
   1142 
   1143                 if (last_slash) {
   1144                     *last_slash = '\0';
   1145                     dir_name = last_slash + 1;
   1146                     parent = ensure_directory_path(root, entry);
   1147                 } else {
   1148                     dir_name = entry;
   1149                     parent = root;
   1150                 }
   1151 
   1152                 if (parent) {
   1153                     ramdisk_create_dir(parent, dir_name);
   1154                     entry_count++;
   1155                 }
   1156             } else {
   1157                 /* File - find ~~~~ separator */
   1158                 char* separator = strstr(entry, "~~~~");
   1159                 if (separator) {
   1160                     *separator = '\0';
   1161                     char* filepath = entry;
   1162                     char* content = separator + 4;
   1163 
   1164                     /* Get parent directory */
   1165                     char* last_slash = strrchr(filepath, '/');
   1166                     char* filename;
   1167                     ramdisk_node_t* parent;
   1168 
   1169                     if (last_slash) {
   1170                         *last_slash = '\0';
   1171                         filename = last_slash + 1;
   1172                         parent = ensure_directory_path(root, filepath);
   1173                     } else {
   1174                         filename = filepath;
   1175                         parent = root;
   1176                     }
   1177 
   1178                     if (parent) {
   1179                         /* Create file */
   1180                         ramdisk_node_t* file = ramdisk_create_file(parent, filename);
   1181                         if (file) {
   1182                             /* Check if binary (BIN: prefix) */
   1183                             if (strncmp(content, "BIN:", 4) == 0) {
   1184                                 /* Base64 encoded binary */
   1185                                 content += 4;
   1186                                 uint32_t decoded_len;
   1187                                 uint8_t* decoded = base64_decode(content, strlen(content), &decoded_len);
   1188 
   1189                                 if (decoded) {
   1190                                     ramdisk_write_file(file, decoded, decoded_len);
   1191                                     free(decoded);
   1192                                     entry_count++;
   1193                                 }
   1194                             } else {
   1195                                 /* Text content - unescape */
   1196                                 char* unescaped = unescape_text(content, strlen(content));
   1197                                 if (unescaped) {
   1198                                     ramdisk_write_file(file, (uint8_t*)unescaped, strlen(unescaped));
   1199                                     free(unescaped);
   1200                                     entry_count++;
   1201                                 }
   1202                             }
   1203                         }
   1204                     }
   1205                 }
   1206             }
   1207 
   1208             free(entry);
   1209         }
   1210 
   1211         /* Move to next entry */
   1212         if (entry_end) {
   1213             entry_start = entry_end + 4;  /* Skip \0\0\0\0 */
   1214         } else {
   1215             break;
   1216         }
   1217     }
   1218 
   1219     free(data);
   1220     return entry_count;
   1221 }
   1222 
   1223 /* Find a node relative to a starting node (supports both absolute and relative paths) */
   1224 ramdisk_node_t* ramdisk_find(ramdisk_node_t* start_node, const char* path) {
   1225     if (!start_node || !path) {
   1226         return NULL;
   1227     }
   1228 
   1229     /* Check if it's an absolute path */
   1230     if (path[0] == '/') {
   1231         /* Find root */
   1232         ramdisk_node_t* root = start_node;
   1233         while (root->parent) {
   1234             root = root->parent;
   1235         }
   1236         /* Use traverse from root */
   1237         return ramdisk_traverse(root, path);
   1238     }
   1239 
   1240     /* Relative path - traverse from start_node */
   1241     if (path[0] == '\0') {
   1242         return start_node;
   1243     }
   1244 
   1245     /* Split path and traverse */
   1246     char* path_copy = my_strdup(path);
   1247     ramdisk_node_t* current = start_node;
   1248 
   1249     char* token = my_strtok(path_copy, "/");
   1250     while (token && current) {
   1251         if (current->type != RAMDISK_DIR) {
   1252             free(path_copy);
   1253             return NULL;
   1254         }
   1255 
   1256         /* Search for token in current directory */
   1257         ramdisk_dir_entry_t* entry = current->dir.entries;
   1258         ramdisk_node_t* found = NULL;
   1259 
   1260         while (entry) {
   1261             if (strcmp(entry->name, token) == 0) {
   1262                 found = entry->node;
   1263                 break;
   1264             }
   1265             entry = entry->next;
   1266         }
   1267 
   1268         if (!found) {
   1269             free(path_copy);
   1270             return NULL;
   1271         }
   1272 
   1273         current = found;
   1274         token = my_strtok(NULL, "/");
   1275     }
   1276 
   1277     free(path_copy);
   1278     return current;
   1279 }
   1280 
   1281 /* Lua binding for find */
   1282 int lua_ramdisk_find(lua_State* L) {
   1283     /* Get start node from upvalue (closure) */
   1284     void* start_node_ptr = lua_touserdata(L, lua_upvalueindex(1));
   1285     if (!start_node_ptr) {
   1286         lua_pushnil(L);
   1287         lua_pushstring(L, "invalid start node in upvalue");
   1288         return 2;
   1289     }
   1290 
   1291     ramdisk_node_t* start_node = (ramdisk_node_t*)start_node_ptr;
   1292 
   1293     /* When called as fsRoot:traverse(path), arg 1 is self (table), arg 2 is path
   1294      * When called as fsRoot.traverse(path), arg 1 is path
   1295      * We'll handle both by checking if arg 1 is a table */
   1296     int path_index = 1;
   1297     if (lua_istable(L, 1)) {
   1298         path_index = 2;  /* Skip self argument */
   1299     }
   1300 
   1301     const char* path = luaL_checkstring(L, path_index);
   1302 
   1303     ramdisk_node_t* found = ramdisk_find(start_node, path);
   1304 
   1305     if (found) {
   1306         /* Create a table with file/dir info */
   1307         lua_newtable(L);
   1308 
   1309         lua_pushstring(L, "type");
   1310         lua_pushstring(L, found->type == RAMDISK_FILE ? "file" : "directory");
   1311         lua_settable(L, -3);
   1312 
   1313         lua_pushstring(L, "name");
   1314         lua_pushstring(L, found->name);
   1315         lua_settable(L, -3);
   1316 
   1317         if (found->type == RAMDISK_FILE) {
   1318             lua_pushstring(L, "content");
   1319             lua_pushlstring(L, (const char*)found->file.data, found->file.size);
   1320             lua_settable(L, -3);
   1321 
   1322             lua_pushstring(L, "size");
   1323             lua_pushinteger(L, found->file.size);
   1324             lua_settable(L, -3);
   1325         }
   1326 
   1327         return 1;
   1328     } else {
   1329         lua_pushnil(L);
   1330         return 1;
   1331     }
   1332 }