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 }