/** * @file cpio.c * @brief CPIO newc archive parser implementation. * * Parses CPIO archives in the SVR4/newc format. The archive is expected * to be loaded into memory by GRUB as a Multiboot2 module. */ #include "cpio.h" #include /* Debug print helpers defined in kernel.c */ extern void offset_print(const char *str); extern void print_hex(uint32_t val); /** Pointer to the CPIO archive in memory. */ static const uint8_t *archive = NULL; /** Size of the archive (0 if unknown). */ static uint32_t archive_len = 0; /** * Parse an N-character hexadecimal ASCII string to uint32_t. * * @param s Pointer to hex string. * @param n Number of characters to parse. * @return Parsed value. */ static uint32_t parse_hex(const char *s, int n) { uint32_t val = 0; for (int i = 0; i < n; i++) { char c = s[i]; uint32_t digit; if (c >= '0' && c <= '9') { digit = (uint32_t)(c - '0'); } else if (c >= 'a' && c <= 'f') { digit = (uint32_t)(c - 'a' + 10); } else if (c >= 'A' && c <= 'F') { digit = (uint32_t)(c - 'A' + 10); } else { break; } val = (val << 4) | digit; } return val; } /** * Round up to 4-byte boundary. */ static inline uint32_t align4(uint32_t v) { return (v + 3) & ~3u; } /** * Parse a CPIO entry at the given offset. * * @param offset Byte offset into the archive. * @param entry Output entry information. * @return Offset of the next entry, or 0 on error/end. */ static uint32_t parse_entry(uint32_t offset, cpio_entry_t *entry) { if (!archive) return 0; const cpio_newc_header_t *hdr = (const cpio_newc_header_t *)(archive + offset); /* Verify magic */ if (memcmp(hdr->magic, CPIO_MAGIC, 6) != 0) { return 0; } uint32_t namesize = parse_hex(hdr->namesize, 8); uint32_t filesize = parse_hex(hdr->filesize, 8); uint32_t mode = parse_hex(hdr->mode, 8); /* Filename starts right after the header */ const char *name = (const char *)(archive + offset + CPIO_HEADER_SIZE); /* Data starts after header + name, aligned to 4 bytes */ uint32_t data_offset = align4(offset + CPIO_HEADER_SIZE + namesize); const void *data = archive + data_offset; /* Next entry starts after data, aligned to 4 bytes */ uint32_t next_offset = align4(data_offset + filesize); entry->name = name; entry->namesize = namesize; entry->data = data; entry->datasize = filesize; entry->mode = mode; return next_offset; } void cpio_init(const void *archive_start, uint32_t archive_size) { archive = (const uint8_t *)archive_start; archive_len = archive_size; offset_print(" CPIO: archive at "); print_hex((uint32_t)archive_start); offset_print(" CPIO: size = "); print_hex(archive_size); /* Count and list entries */ uint32_t count = 0; uint32_t off = 0; cpio_entry_t entry; while (1) { uint32_t next = parse_entry(off, &entry); if (next == 0) break; if (strcmp(entry.name, CPIO_TRAILER) == 0) break; offset_print(" CPIO: ["); offset_print(entry.name); offset_print("] size="); print_hex(entry.datasize); count++; off = next; } offset_print(" CPIO: "); print_hex(count); offset_print(" CPIO: files found\n"); } int cpio_find(const char *name, cpio_entry_t *entry) { if (!archive) return -1; uint32_t off = 0; while (1) { uint32_t next = parse_entry(off, entry); if (next == 0) return -1; if (strcmp(entry->name, CPIO_TRAILER) == 0) return -1; /* Match by name. CPIO entries often have "./" prefix, try both. */ if (strcmp(entry->name, name) == 0) return 0; /* Try matching without "./" prefix */ if (entry->name[0] == '.' && entry->name[1] == '/' && strcmp(entry->name + 2, name) == 0) { return 0; } /* Try matching with "./" prefix */ if (name[0] != '.' && entry->namesize > 2) { /* Already handled above */ } off = next; } } int cpio_next(uint32_t *offset, cpio_entry_t *entry) { if (!archive) return -1; uint32_t next = parse_entry(*offset, entry); if (next == 0) return -1; if (strcmp(entry->name, CPIO_TRAILER) == 0) return -1; *offset = next; return 0; } uint32_t cpio_count(void) { if (!archive) return 0; uint32_t count = 0; uint32_t off = 0; cpio_entry_t entry; while (1) { uint32_t next = parse_entry(off, &entry); if (next == 0) break; if (strcmp(entry.name, CPIO_TRAILER) == 0) break; count++; off = next; } return count; }