Header parsing has hardcoded assumption about header field size and layout. It is hard to modify the function to parse a new format. Move information about size and layout into a data structure to make parsing code more generic and simplify adding a new format. This also removes some magic numbers. Signed-off-by: Taras Kondratiuk <takondra@xxxxxxxxx> --- init/initramfs.c | 122 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 92 insertions(+), 30 deletions(-) diff --git a/init/initramfs.c b/init/initramfs.c index b3d39c8793be..7f0bbfde94e3 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -150,39 +150,100 @@ static void __init dir_utime(void) } } -static __initdata time64_t mtime; - /* cpio header parsing */ - -static __initdata unsigned long ino, major, minor, nlink; +static __initdata time64_t mtime; +static __initdata u32 ino, major, minor, nlink, rmajor, rminor; static __initdata umode_t mode; -static __initdata unsigned long body_len, name_len; +static __initdata u32 body_len, name_len; static __initdata uid_t uid; static __initdata gid_t gid; -static __initdata unsigned rdev; +static __initdata u32 mode_u32; + +struct cpio_hdr_field { + size_t offset; + size_t size; + void *out; + size_t out_size; + const char *name; +}; + +#define HDR_FIELD(type, field, variable) \ + { .offset = offsetof(type, field) + \ + BUILD_BUG_ON_ZERO(sizeof(*(variable))*2 < FIELD_SIZEOF(type, field)),\ + .size = FIELD_SIZEOF(type, field), \ + .out = variable, \ + .out_size = sizeof(*(variable)), \ + .name = #field } + +#define NEWC_FIELD(field, variable) \ + HDR_FIELD(struct cpio_newc_header, field, variable) + +#define CPIO_MAX_HEADER_SIZE sizeof(struct cpio_newc_header) +#define CPIO_MAX_FIELD_SIZE 8 +#define CPIO_MAGIC_SIZE 6 + +struct cpio_newc_header { + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; + char c_devmajor[8]; + char c_devminor[8]; + char c_rdevmajor[8]; + char c_rdevminor[8]; + char c_namesize[8]; + char c_check[8]; +}; + +static struct cpio_hdr_field cpio_newc_header_info[] __initdata = { + NEWC_FIELD(c_ino, &ino), + NEWC_FIELD(c_mode, &mode_u32), + NEWC_FIELD(c_uid, &uid), + NEWC_FIELD(c_gid, &gid), + NEWC_FIELD(c_nlink, &nlink), + NEWC_FIELD(c_mtime, &mtime), + NEWC_FIELD(c_filesize, &body_len), + NEWC_FIELD(c_devmajor, &major), + NEWC_FIELD(c_devminor, &minor), + NEWC_FIELD(c_rdevmajor, &rmajor), + NEWC_FIELD(c_rdevminor, &rminor), + NEWC_FIELD(c_namesize, &name_len), + { 0 }, +}; static void __init parse_header(char *s) { - unsigned long parsed[12]; - char buf[9]; - int i; - - buf[8] = '\0'; - for (i = 0; i < 12; i++, s += 8) { - memcpy(buf, s, 8); - parsed[i] = simple_strtoul(buf, NULL, 16); + char buf[CPIO_MAX_FIELD_SIZE + 1]; + struct cpio_hdr_field *field = cpio_newc_header_info; + + while (field->size) { + int ret = 0; + + memcpy(buf, s + field->offset, field->size); + buf[field->size] = '\0'; + switch (field->out_size) { + case sizeof(u32): + ret = kstrtou32(buf, 16, field->out); + pr_debug("cpio field %s: %u, buf: %s\n", + field->name, *(u32 *)field->out, buf); + break; + case sizeof(u64): + ret = kstrtou64(buf, 16, field->out); + pr_debug("cpio field %s: %llu, buf: %s\n", + field->name, *(u64 *)field->out, buf); + break; + default: + BUG_ON(1); + } + + if (ret) + pr_err("invalid cpio header field (%d)", ret); + field++; } - ino = parsed[0]; - mode = parsed[1]; - uid = parsed[2]; - gid = parsed[3]; - nlink = parsed[4]; - mtime = parsed[5]; /* breaks in y2106 */ - body_len = parsed[6]; - major = parsed[7]; - minor = parsed[8]; - rdev = new_encode_dev(MKDEV(parsed[9], parsed[10])); - name_len = parsed[11]; + mode = mode_u32; } /* FSM */ @@ -234,7 +295,7 @@ static __initdata char *header_buf, *symlink_buf, *name_buf; static int __init do_start(void) { - read_into(header_buf, 6, do_format); + read_into(header_buf, CPIO_MAGIC_SIZE, do_format); return 0; } @@ -254,15 +315,15 @@ static int __init do_collect(void) static int __init do_format(void) { - if (memcmp(collected, "070707", 6)==0) { + if (memcmp(collected, "070707", CPIO_MAGIC_SIZE) == 0) { error("incorrect cpio method used: use -H newc option"); return 1; } - if (memcmp(collected, "070701", 6)) { + if (memcmp(collected, "070701", CPIO_MAGIC_SIZE)) { error("no cpio magic"); return 1; } - read_into(header_buf, 104, do_header); + read_into(header_buf, sizeof(struct cpio_newc_header), do_header); return 0; } @@ -374,6 +435,7 @@ static int __init do_create(void) } else if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) { if (maybe_link(name_buf) == 0) { + u32 rdev = new_encode_dev(MKDEV(rmajor, rminor)); sys_mknod(name_buf, mode, rdev); sys_chown(name_buf, uid, gid); sys_chmod(name_buf, mode); @@ -464,7 +526,7 @@ static char * __init unpack_to_rootfs(char *buf, unsigned long len) const char *compress_name; static __initdata char msg_buf[64]; - header_buf = kmalloc(104, GFP_KERNEL); + header_buf = kmalloc(CPIO_MAX_HEADER_SIZE, GFP_KERNEL); symlink_buf = kmalloc(PATH_MAX + 1, GFP_KERNEL); name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL); -- 2.10.3.dirty -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html