[PATCH v3 08/15] initramfs: add newcx format
From: Taras Kondratiuk
Date: Fri Feb 16 2018 - 15:36:23 EST
Add 'newcx' format that adds extended attributes and increased size of
c_mtime and c_filesize fields.
Refer to Documentation/early-userspace/buffer-format.txt for detailed
format description.
Signed-off-by: Taras Kondratiuk <takondra@xxxxxxxxx>
---
init/initramfs.c | 121 +++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 96 insertions(+), 25 deletions(-)
diff --git a/init/initramfs.c b/init/initramfs.c
index 7f0bbfde94e3..0407e199352e 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -54,6 +54,7 @@ static void __init error(char *x)
/* link hash */
#define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
+#define X_ALIGN(len) ((len + 3) & ~3)
static __initdata struct hash {
int ino, minor, major;
@@ -109,14 +110,11 @@ static void __init free_hash(void)
}
}
-static long __init do_utime(char *filename, time64_t mtime)
+static long __init do_utime(char *filename, struct timespec64 *mtime)
{
struct timespec64 t[2];
- t[0].tv_sec = mtime;
- t[0].tv_nsec = 0;
- t[1].tv_sec = mtime;
- t[1].tv_nsec = 0;
+ t[0] = t[1] = *mtime;
return do_utimes(AT_FDCWD, filename, t, AT_SYMLINK_NOFOLLOW);
}
@@ -125,17 +123,17 @@ static __initdata LIST_HEAD(dir_list);
struct dir_entry {
struct list_head list;
char *name;
- time64_t mtime;
+ struct timespec64 mtime;
};
-static void __init dir_add(const char *name, time64_t mtime)
+static void __init dir_add(const char *name, struct timespec64 *mtime)
{
struct dir_entry *de = kmalloc(sizeof(struct dir_entry), GFP_KERNEL);
if (!de)
panic("can't allocate dir_entry buffer");
INIT_LIST_HEAD(&de->list);
de->name = kstrdup(name, GFP_KERNEL);
- de->mtime = mtime;
+ de->mtime = *mtime;
list_add(&de->list, &dir_list);
}
@@ -144,17 +142,18 @@ static void __init dir_utime(void)
struct dir_entry *de, *tmp;
list_for_each_entry_safe(de, tmp, &dir_list, list) {
list_del(&de->list);
- do_utime(de->name, de->mtime);
+ do_utime(de->name, &de->mtime);
kfree(de->name);
kfree(de);
}
}
/* cpio header parsing */
-static __initdata time64_t mtime;
+static __initdata struct timespec64 mtime;
static __initdata u32 ino, major, minor, nlink, rmajor, rminor;
static __initdata umode_t mode;
-static __initdata u32 body_len, name_len;
+static __initdata u32 name_len, xattr_len;
+static __initdata u64 body_len;
static __initdata uid_t uid;
static __initdata gid_t gid;
static __initdata u32 mode_u32;
@@ -167,6 +166,12 @@ struct cpio_hdr_field {
const char *name;
};
+static __initdata enum cpio_format {
+ CPIO_NO_MAGIC,
+ CPIO_NEWC,
+ CPIO_NEWCX,
+} cpio_format;
+
#define HDR_FIELD(type, field, variable) \
{ .offset = offsetof(type, field) + \
BUILD_BUG_ON_ZERO(sizeof(*(variable))*2 < FIELD_SIZEOF(type, field)),\
@@ -177,9 +182,11 @@ struct cpio_hdr_field {
#define NEWC_FIELD(field, variable) \
HDR_FIELD(struct cpio_newc_header, field, variable)
+#define NEWCX_FIELD(field, variable) \
+ HDR_FIELD(struct cpio_newcx_header, field, variable)
-#define CPIO_MAX_HEADER_SIZE sizeof(struct cpio_newc_header)
-#define CPIO_MAX_FIELD_SIZE 8
+#define CPIO_MAX_HEADER_SIZE sizeof(struct cpio_newcx_header)
+#define CPIO_MAX_FIELD_SIZE 16
#define CPIO_MAGIC_SIZE 6
struct cpio_newc_header {
@@ -204,7 +211,7 @@ static struct cpio_hdr_field cpio_newc_header_info[] __initdata = {
NEWC_FIELD(c_uid, &uid),
NEWC_FIELD(c_gid, &gid),
NEWC_FIELD(c_nlink, &nlink),
- NEWC_FIELD(c_mtime, &mtime),
+ NEWC_FIELD(c_mtime, &mtime.tv_sec),
NEWC_FIELD(c_filesize, &body_len),
NEWC_FIELD(c_devmajor, &major),
NEWC_FIELD(c_devminor, &minor),
@@ -214,10 +221,46 @@ static struct cpio_hdr_field cpio_newc_header_info[] __initdata = {
{ 0 },
};
+struct cpio_newcx_header {
+ char c_ino[8];
+ char c_mode[8];
+ char c_uid[8];
+ char c_gid[8];
+ char c_nlink[8];
+ char c_mtime[16];
+ char c_mtime_nsec[8];
+ char c_filesize[16];
+ char c_devmajor[8];
+ char c_devminor[8];
+ char c_rdevmajor[8];
+ char c_rdevminor[8];
+ char c_namesize[8];
+ char c_xattrsize[8];
+};
+
+static struct cpio_hdr_field cpio_newcx_header_info[] __initdata = {
+ NEWCX_FIELD(c_ino, &ino),
+ NEWCX_FIELD(c_mode, &mode_u32),
+ NEWCX_FIELD(c_uid, &uid),
+ NEWCX_FIELD(c_gid, &gid),
+ NEWCX_FIELD(c_nlink, &nlink),
+ NEWCX_FIELD(c_mtime, &mtime.tv_sec),
+ NEWCX_FIELD(c_mtime_nsec, &mtime.tv_nsec),
+ NEWCX_FIELD(c_filesize, &body_len),
+ NEWCX_FIELD(c_devmajor, &major),
+ NEWCX_FIELD(c_devminor, &minor),
+ NEWCX_FIELD(c_rdevmajor, &rmajor),
+ NEWCX_FIELD(c_rdevminor, &rminor),
+ NEWCX_FIELD(c_namesize, &name_len),
+ NEWCX_FIELD(c_xattrsize, &xattr_len),
+ { 0 },
+};
+
static void __init parse_header(char *s)
{
char buf[CPIO_MAX_FIELD_SIZE + 1];
- struct cpio_hdr_field *field = cpio_newc_header_info;
+ struct cpio_hdr_field *field = (cpio_format == CPIO_NEWC) ?
+ cpio_newc_header_info : cpio_newcx_header_info;
while (field->size) {
int ret = 0;
@@ -243,7 +286,12 @@ static void __init parse_header(char *s)
pr_err("invalid cpio header field (%d)", ret);
field++;
}
+
mode = mode_u32;
+ if (cpio_format != CPIO_NEWCX) {
+ xattr_len = 0;
+ mtime.tv_nsec = 0;
+ }
}
/* FSM */
@@ -254,6 +302,7 @@ static int __init do_format(void);
static int __init do_header(void);
static int __init do_skip(void);
static int __init do_name(void);
+static int __init do_xattrs(void);
static int __init do_create(void);
static int __init do_copy(void);
static int __init do_symlink(void);
@@ -291,7 +340,7 @@ static void __init read_into(char *buf, unsigned size, fsm_state_t next)
}
}
-static __initdata char *header_buf, *symlink_buf, *name_buf;
+static __initdata char *header_buf, *symlink_buf, *name_buf, *xattr_buf;
static int __init do_start(void)
{
@@ -315,22 +364,34 @@ static int __init do_collect(void)
static int __init do_format(void)
{
- if (memcmp(collected, "070707", CPIO_MAGIC_SIZE) == 0) {
+ int header_size = 0;
+
+ cpio_format = CPIO_NO_MAGIC;
+
+ if (!memcmp(collected, "070707", CPIO_MAGIC_SIZE)) {
error("incorrect cpio method used: use -H newc option");
return 1;
+ } else if (!memcmp(collected, "070701", CPIO_MAGIC_SIZE)) {
+ cpio_format = CPIO_NEWC;
+ header_size = sizeof(struct cpio_newc_header);
+ } else if (!memcmp(collected, "070703", CPIO_MAGIC_SIZE)) {
+ cpio_format = CPIO_NEWCX;
+ header_size = sizeof(struct cpio_newcx_header);
}
- if (memcmp(collected, "070701", CPIO_MAGIC_SIZE)) {
+
+ if (cpio_format == CPIO_NO_MAGIC) {
error("no cpio magic");
return 1;
}
- read_into(header_buf, sizeof(struct cpio_newc_header), do_header);
+ read_into(header_buf, header_size, do_header);
return 0;
}
static int __init do_header(void)
{
parse_header(collected);
- next_header = this_header + N_ALIGN(name_len) + body_len;
+ next_header = this_header + N_ALIGN(name_len) + X_ALIGN(xattr_len) +
+ body_len;
next_header = (next_header + 3) & ~3;
state = do_skip;
if (name_len <= 0 || name_len > PATH_MAX)
@@ -400,9 +461,17 @@ static int __init do_name(void)
}
memcpy_optional(name_buf, collected, N_ALIGN(name_len));
state = do_create;
+ if (xattr_len > 0)
+ read_into(xattr_buf, X_ALIGN(xattr_len), do_xattrs);
return 0;
}
+static int __init do_xattrs(void)
+{
+ /* Do nothing for now */
+ state = do_create;
+ return 0;
+}
static __initdata int wfd;
@@ -431,7 +500,7 @@ static int __init do_create(void)
sys_mkdir(name_buf, mode);
sys_chown(name_buf, uid, gid);
sys_chmod(name_buf, mode);
- dir_add(name_buf, mtime);
+ dir_add(name_buf, &mtime);
} else if (S_ISBLK(mode) || S_ISCHR(mode) ||
S_ISFIFO(mode) || S_ISSOCK(mode)) {
if (maybe_link(name_buf) == 0) {
@@ -439,7 +508,7 @@ static int __init do_create(void)
sys_mknod(name_buf, mode, rdev);
sys_chown(name_buf, uid, gid);
sys_chmod(name_buf, mode);
- do_utime(name_buf, mtime);
+ do_utime(name_buf, &mtime);
}
} else if (S_ISLNK(mode)) {
if (body_len > PATH_MAX)
@@ -455,7 +524,7 @@ static int __init do_copy(void)
if (xwrite(wfd, victim, body_len) != body_len)
error("write error");
sys_close(wfd);
- do_utime(name_buf, mtime);
+ do_utime(name_buf, &mtime);
eat(body_len);
state = do_skip;
return 0;
@@ -475,7 +544,7 @@ static int __init do_symlink(void)
clean_path(name_buf, 0);
sys_symlink(symlink_buf, name_buf);
sys_lchown(name_buf, uid, gid);
- do_utime(name_buf, mtime);
+ do_utime(name_buf, &mtime);
state = do_skip;
next_state = do_reset;
return 0;
@@ -529,8 +598,9 @@ static char * __init unpack_to_rootfs(char *buf, unsigned long len)
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);
+ xattr_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
- if (!header_buf || !symlink_buf || !name_buf)
+ if (!header_buf || !symlink_buf || !name_buf || !xattr_buf)
panic("can't allocate buffers");
state = do_start;
@@ -575,6 +645,7 @@ static char * __init unpack_to_rootfs(char *buf, unsigned long len)
len -= my_inptr;
}
dir_utime();
+ kfree(xattr_buf);
kfree(name_buf);
kfree(symlink_buf);
kfree(header_buf);
--
2.10.3.dirty