// Copyright (c) 2023-2026 Chris (boreddevnl) // This software is released under the GNU General Public License v3.0. See LICENSE file for details. // This header needs to maintain in any file it is present in, as per the GPL license terms. #include "tar.h" #include "fat32.h" // The standard TAR header block is 512 bytes. struct tar_header { char filename[100]; char mode[8]; char uid[8]; char gid[8]; char size[12]; char mtime[12]; char chksum[8]; char typeflag; char linkname[100]; char magic[6]; char version[2]; char uname[32]; char gname[32]; char devmajor[8]; char devminor[8]; char prefix[155]; char pad[12]; } __attribute__((packed)); // Helper: parse tar octal field representation static uint64_t tar_parse_octal(const char *str, int size) { uint64_t result = 0; while (size-- > 0) { if (*str >= '0' && *str <= '7') { result = (result << 3) + (*str - '0'); } str++; } return result; } // Helper: Make directories sequentially for nested paths static void tar_mkdir_recursive(const char *path) { char temp[256]; int i = 0; if (path[0] == '/') { temp[0] = '/'; i = 1; } while (path[i] && i < 255) { temp[i] = path[i]; if (path[i] == '/') { temp[i] = '\0'; fat32_mkdir(temp); temp[i] = '/'; } i++; } if (i > 0 && temp[i - 1] != '/') { temp[i] = '\0'; fat32_mkdir(temp); } } void tar_parse(void *archive, uint64_t archive_size) { uint8_t *ptr = (uint8_t *)archive; uint8_t *end = ptr + archive_size; while (ptr + 512 <= end) { struct tar_header *header = (struct tar_header *)ptr; // End of archive is marked by empty blocks if (header->filename[0] == '\0') { break; } uint64_t file_size = tar_parse_octal(header->size, 11); char full_path[256]; // Ensure path starts with a '/' for VFS consistency if (header->filename[0] != '/') { full_path[0] = '/'; int j = 0; while (header->filename[j] && j < 254) { full_path[j + 1] = header->filename[j]; j++; } full_path[j + 1] = '\0'; } else { int j = 0; while (header->filename[j] && j < 255) { full_path[j] = header->filename[j]; j++; } full_path[j] = '\0'; } if (header->typeflag == '5') { // It's a directory tar_mkdir_recursive(full_path); } else if (header->typeflag == '0' || header->typeflag == '\0') { // It's a normal file // First ensure the parent directory exists char parent_path[256]; int last_slash = -1; for (int j = 0; full_path[j]; j++) { parent_path[j] = full_path[j]; if (full_path[j] == '/') { last_slash = j; } } if (last_slash > 0) { parent_path[last_slash] = '\0'; tar_mkdir_recursive(parent_path); } // Extract the file data block directly into the VFS FAT32_FileHandle *fh = fat32_open(full_path, "w"); if (fh && fh->valid) { fat32_write(fh, ptr + 512, file_size); fat32_close(fh); } } // Advance pointer to the next file header // Header block (512) + File data (padded to 512-byte multiples) uint64_t data_blocks = (file_size + 511) / 512; ptr += 512 + (data_blocks * 512); } }