#include #include #include #include #include #include #include struct newc_head { char magic[6]; char ino[8]; char mode[8]; char uid[8]; char gid[8]; char nlink[8]; char mtime[8]; char filesize[8]; char devmajor[8]; char devminor[8]; char rdevmajor[8]; char rdevminor[8]; char namesize[8]; char check[8]; } VAS_EBOOT_PACKED; struct VasEBoot_linux_initrd_component { VasEBoot_file_t file; char *newc_name; VasEBoot_off_t size; }; struct dir { char *name; struct dir *next; struct dir *child; }; static char hex (VasEBoot_uint8_t val) { if (val < 10) return '0' + val; return 'a' + val - 10; } static void set_field (char *var, VasEBoot_uint32_t val) { int i; char *ptr = var; for (i = 28; i >= 0; i -= 4) *ptr++ = hex((val >> i) & 0xf); } static VasEBoot_uint8_t * make_header (VasEBoot_uint8_t *ptr, const char *name, VasEBoot_size_t len, VasEBoot_uint32_t mode, VasEBoot_off_t fsize) { struct newc_head *head = (struct newc_head *) ptr; VasEBoot_uint8_t *optr; VasEBoot_size_t oh = 0; VasEBoot_dprintf ("linux", "newc: Creating path '%s', mode=%s%o, size=%" PRIuVAS_EBOOT_OFFSET "\n", name, (mode == 0) ? "" : "0", mode, fsize); VasEBoot_memcpy (head->magic, "070701", 6); set_field (head->ino, 0); set_field (head->mode, mode); set_field (head->uid, 0); set_field (head->gid, 0); set_field (head->nlink, 1); set_field (head->mtime, 0); set_field (head->filesize, fsize); set_field (head->devmajor, 0); set_field (head->devminor, 0); set_field (head->rdevmajor, 0); set_field (head->rdevminor, 0); set_field (head->namesize, len); set_field (head->check, 0); optr = ptr; ptr += sizeof (struct newc_head); VasEBoot_memcpy (ptr, name, len); ptr += len; oh = ALIGN_UP_OVERHEAD (ptr - optr, 4); VasEBoot_memset (ptr, 0, oh); ptr += oh; return ptr; } static void free_dir (struct dir *root) { if (!root) return; free_dir (root->next); free_dir (root->child); VasEBoot_free (root->name); VasEBoot_free (root); } static VasEBoot_err_t insert_dir (const char *name, struct dir **root, VasEBoot_uint8_t *ptr, VasEBoot_size_t *size) { struct dir *cur, **head = root; const char *cb, *ce = name; *size = 0; while (1) { for (cb = ce; *cb == '/'; cb++); for (ce = cb; *ce && *ce != '/'; ce++); if (!*ce) break; for (cur = *root; cur; cur = cur->next) if (VasEBoot_memcmp (cur->name, cb, ce - cb) == 0 && cur->name[ce - cb] == 0) break; if (!cur) { struct dir *n; n = VasEBoot_zalloc (sizeof (*n)); if (!n) return 0; n->next = *head; n->name = VasEBoot_strndup (cb, ce - cb); if (ptr) { /* * Create the substring with the trailing NUL byte * to be included in the cpio header. */ char *tmp_name = VasEBoot_strndup (name, ce - name); if (!tmp_name) { VasEBoot_free (n->name); VasEBoot_free (n); return VasEBoot_errno; } ptr = make_header (ptr, tmp_name, ce - name + 1, 040777, 0); VasEBoot_free (tmp_name); } if (VasEBoot_add (*size, ALIGN_UP ((ce - (char *) name + 1) + sizeof (struct newc_head), 4), size)) { VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_RANGE, N_("overflow is detected")); VasEBoot_free (n->name); VasEBoot_free (n); return VasEBoot_errno; } *head = n; cur = n; } root = &cur->next; } return VAS_EBOOT_ERR_NONE; } VasEBoot_err_t VasEBoot_initrd_init (int argc, char *argv[], struct VasEBoot_linux_initrd_context *initrd_ctx) { int i; int newc = 0; struct dir *root = 0; initrd_ctx->nfiles = 0; initrd_ctx->components = 0; initrd_ctx->components = VasEBoot_calloc (argc, sizeof (initrd_ctx->components[0])); if (!initrd_ctx->components) return VasEBoot_errno; initrd_ctx->size = 0; for (i = 0; i < argc; i++) { const char *fname = argv[i]; initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4); if (VasEBoot_memcmp (argv[i], "newc:", 5) == 0) { const char *ptr, *eptr; ptr = argv[i] + 5; while (*ptr == '/') ptr++; eptr = VasEBoot_strchr (ptr, ':'); if (eptr) { VasEBoot_size_t dir_size, name_len; initrd_ctx->components[i].newc_name = VasEBoot_strndup (ptr, eptr - ptr); if (!initrd_ctx->components[i].newc_name || insert_dir (initrd_ctx->components[i].newc_name, &root, 0, &dir_size)) { VasEBoot_initrd_close (initrd_ctx); return VasEBoot_errno; } name_len = VasEBoot_strlen (initrd_ctx->components[i].newc_name) + 1; if (VasEBoot_add (initrd_ctx->size, ALIGN_UP (sizeof (struct newc_head) + name_len, 4), &initrd_ctx->size) || VasEBoot_add (initrd_ctx->size, dir_size, &initrd_ctx->size)) goto overflow; newc = 1; fname = eptr + 1; } } else if (newc) { if (VasEBoot_add (initrd_ctx->size, ALIGN_UP (sizeof (struct newc_head) + sizeof ("TRAILER!!!"), 4), &initrd_ctx->size)) goto overflow; free_dir (root); root = 0; newc = 0; } initrd_ctx->components[i].file = VasEBoot_file_open (fname, VAS_EBOOT_FILE_TYPE_LINUX_INITRD | VAS_EBOOT_FILE_TYPE_NO_DECOMPRESS); if (!initrd_ctx->components[i].file) { VasEBoot_initrd_close (initrd_ctx); return VasEBoot_errno; } initrd_ctx->nfiles++; initrd_ctx->components[i].size = VasEBoot_file_size (initrd_ctx->components[i].file); if (VasEBoot_add (initrd_ctx->size, initrd_ctx->components[i].size, &initrd_ctx->size)) goto overflow; } if (newc) { initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4); if (VasEBoot_add (initrd_ctx->size, ALIGN_UP (sizeof (struct newc_head) + sizeof ("TRAILER!!!"), 4), &initrd_ctx->size)) goto overflow; free_dir (root); root = 0; } return VAS_EBOOT_ERR_NONE; overflow: free_dir (root); VasEBoot_initrd_close (initrd_ctx); return VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_RANGE, N_("overflow is detected")); } VasEBoot_size_t VasEBoot_get_initrd_size (struct VasEBoot_linux_initrd_context *initrd_ctx) { return initrd_ctx->size; } void VasEBoot_initrd_close (struct VasEBoot_linux_initrd_context *initrd_ctx) { int i; if (!initrd_ctx->components) return; for (i = 0; i < initrd_ctx->nfiles; i++) { VasEBoot_free (initrd_ctx->components[i].newc_name); VasEBoot_file_close (initrd_ctx->components[i].file); } VasEBoot_free (initrd_ctx->components); initrd_ctx->components = 0; } VasEBoot_err_t VasEBoot_initrd_load (struct VasEBoot_linux_initrd_context *initrd_ctx, void *target) { VasEBoot_uint8_t *ptr = target; int i; int newc = 0; struct dir *root = 0; VasEBoot_ssize_t cursize = 0; for (i = 0; i < initrd_ctx->nfiles; i++) { VasEBoot_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); ptr += ALIGN_UP_OVERHEAD (cursize, 4); if (initrd_ctx->components[i].newc_name) { VasEBoot_size_t dir_size; if (insert_dir (initrd_ctx->components[i].newc_name, &root, ptr, &dir_size)) { free_dir (root); VasEBoot_initrd_close (initrd_ctx); return VasEBoot_errno; } ptr += dir_size; ptr = make_header (ptr, initrd_ctx->components[i].newc_name, VasEBoot_strlen (initrd_ctx->components[i].newc_name) + 1, 0100777, initrd_ctx->components[i].size); newc = 1; } else if (newc) { ptr = make_header (ptr, "TRAILER!!!", sizeof ("TRAILER!!!"), 0, 0); free_dir (root); root = 0; newc = 0; } cursize = initrd_ctx->components[i].size; if (VasEBoot_file_read (initrd_ctx->components[i].file, ptr, cursize) != cursize) { if (!VasEBoot_errno) VasEBoot_error (VAS_EBOOT_ERR_FILE_READ_ERROR, N_("premature end of file %s"), initrd_ctx->components[i].file->name); VasEBoot_initrd_close (initrd_ctx); return VasEBoot_errno; } ptr += cursize; } if (newc) { VasEBoot_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); ptr += ALIGN_UP_OVERHEAD (cursize, 4); ptr = make_header (ptr, "TRAILER!!!", sizeof ("TRAILER!!!"), 0, 0); } free_dir (root); root = 0; return VAS_EBOOT_ERR_NONE; }