#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]; } GRUB_PACKED; struct grub_linux_initrd_component { grub_file_t file; char *newc_name; grub_off_t size; }; struct dir { char *name; struct dir *next; struct dir *child; }; static char hex (grub_uint8_t val) { if (val < 10) return '0' + val; return 'a' + val - 10; } static void set_field (char *var, grub_uint32_t val) { int i; char *ptr = var; for (i = 28; i >= 0; i -= 4) *ptr++ = hex((val >> i) & 0xf); } static grub_uint8_t * make_header (grub_uint8_t *ptr, const char *name, grub_size_t len, grub_uint32_t mode, grub_off_t fsize) { struct newc_head *head = (struct newc_head *) ptr; grub_uint8_t *optr; grub_size_t oh = 0; grub_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); grub_memcpy (ptr, name, len); ptr += len; oh = ALIGN_UP_OVERHEAD (ptr - optr, 4); grub_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); grub_free (root->name); grub_free (root); } static grub_err_t insert_dir (const char *name, struct dir **root, grub_uint8_t *ptr, grub_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 (grub_memcmp (cur->name, cb, ce - cb) && cur->name[ce - cb] == 0) break; if (!cur) { struct dir *n; n = grub_zalloc (sizeof (*n)); if (!n) return 0; n->next = *head; n->name = grub_strndup (cb, ce - cb); if (ptr) { grub_dprintf ("linux", "Creating directory %s, %s\n", name, ce); ptr = make_header (ptr, name, ce - name, 040777, 0); } if (grub_add (*size, ALIGN_UP ((ce - (char *) name) + sizeof (struct newc_head), 4), size)) { grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); grub_free (n->name); grub_free (n); return grub_errno; } *head = n; cur = n; } root = &cur->next; } return GRUB_ERR_NONE; } grub_err_t grub_initrd_init (int argc, char *argv[], struct grub_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 = grub_calloc (argc, sizeof (initrd_ctx->components[0])); if (!initrd_ctx->components) return grub_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 (grub_memcmp (argv[i], "newc:", 5) == 0) { const char *ptr, *eptr; ptr = argv[i] + 5; while (*ptr == '/') ptr++; eptr = grub_strchr (ptr, ':'); if (eptr) { grub_size_t dir_size, name_len; initrd_ctx->components[i].newc_name = grub_strndup (ptr, eptr - ptr); if (!initrd_ctx->components[i].newc_name || insert_dir (initrd_ctx->components[i].newc_name, &root, 0, &dir_size)) { grub_initrd_close (initrd_ctx); return grub_errno; } name_len = grub_strlen (initrd_ctx->components[i].newc_name); if (grub_add (initrd_ctx->size, ALIGN_UP (sizeof (struct newc_head) + name_len, 4), &initrd_ctx->size) || grub_add (initrd_ctx->size, dir_size, &initrd_ctx->size)) goto overflow; newc = 1; fname = eptr + 1; } } else if (newc) { if (grub_add (initrd_ctx->size, ALIGN_UP (sizeof (struct newc_head) + sizeof ("TRAILER!!!") - 1, 4), &initrd_ctx->size)) goto overflow; free_dir (root); root = 0; newc = 0; } initrd_ctx->components[i].file = grub_file_open (fname, GRUB_FILE_TYPE_LINUX_INITRD | GRUB_FILE_TYPE_NO_DECOMPRESS); if (!initrd_ctx->components[i].file) { grub_initrd_close (initrd_ctx); return grub_errno; } initrd_ctx->nfiles++; initrd_ctx->components[i].size = grub_file_size (initrd_ctx->components[i].file); if (grub_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 (grub_add (initrd_ctx->size, ALIGN_UP (sizeof (struct newc_head) + sizeof ("TRAILER!!!") - 1, 4), &initrd_ctx->size)) goto overflow; free_dir (root); root = 0; } return GRUB_ERR_NONE; overflow: free_dir (root); grub_initrd_close (initrd_ctx); return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); } grub_size_t grub_get_initrd_size (struct grub_linux_initrd_context *initrd_ctx) { return initrd_ctx->size; } void grub_initrd_close (struct grub_linux_initrd_context *initrd_ctx) { int i; if (!initrd_ctx->components) return; for (i = 0; i < initrd_ctx->nfiles; i++) { grub_free (initrd_ctx->components[i].newc_name); grub_file_close (initrd_ctx->components[i].file); } grub_free (initrd_ctx->components); initrd_ctx->components = 0; } grub_err_t grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx, char *argv[], void *target) { grub_uint8_t *ptr = target; int i; int newc = 0; struct dir *root = 0; grub_ssize_t cursize = 0; for (i = 0; i < initrd_ctx->nfiles; i++) { grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); ptr += ALIGN_UP_OVERHEAD (cursize, 4); if (initrd_ctx->components[i].newc_name) { grub_size_t dir_size; if (insert_dir (initrd_ctx->components[i].newc_name, &root, ptr, &dir_size)) { free_dir (root); grub_initrd_close (initrd_ctx); return grub_errno; } ptr += dir_size; ptr = make_header (ptr, initrd_ctx->components[i].newc_name, grub_strlen (initrd_ctx->components[i].newc_name), 0100777, initrd_ctx->components[i].size); newc = 1; } else if (newc) { ptr = make_header (ptr, "TRAILER!!!", sizeof ("TRAILER!!!") - 1, 0, 0); free_dir (root); root = 0; newc = 0; } cursize = initrd_ctx->components[i].size; if (grub_file_read (initrd_ctx->components[i].file, ptr, cursize) != cursize) { if (!grub_errno) grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), argv[i]); grub_initrd_close (initrd_ctx); return grub_errno; } ptr += cursize; } if (newc) { grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); ptr += ALIGN_UP_OVERHEAD (cursize, 4); ptr = make_header (ptr, "TRAILER!!!", sizeof ("TRAILER!!!") - 1, 0, 0); } free_dir (root); root = 0; return GRUB_ERR_NONE; }