summaryrefslogtreecommitdiff
path: root/init/initramfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'init/initramfs.c')
-rw-r--r--init/initramfs.c108
1 files changed, 63 insertions, 45 deletions
diff --git a/init/initramfs.c b/init/initramfs.c
index 76deb48c38cb..72bad44a1d41 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -16,8 +16,11 @@
#include <linux/mm.h>
#include <linux/namei.h>
#include <linux/init_syscalls.h>
-#include <linux/task_work.h>
#include <linux/umh.h>
+#include <linux/security.h>
+
+#include "do_mounts.h"
+#include "initramfs_internal.h"
static __initdata bool csum_present;
static __initdata u32 io_csum;
@@ -73,6 +76,7 @@ static __initdata struct hash {
struct hash *next;
char name[N_ALIGN(PATH_MAX)];
} *head[32];
+static __initdata bool hardlink_seen;
static inline int hash(int major, int minor, int ino)
{
@@ -106,19 +110,21 @@ static char __init *find_link(int major, int minor, int ino,
strcpy(q->name, name);
q->next = NULL;
*p = q;
+ hardlink_seen = true;
return NULL;
}
static void __init free_hash(void)
{
struct hash **p, *q;
- for (p = head; p < head + 32; p++) {
+ for (p = head; hardlink_seen && p < head + 32; p++) {
while (*p) {
q = *p;
*p = q->next;
kfree(q);
}
}
+ hardlink_seen = false;
}
#ifdef CONFIG_INITRAMFS_PRESERVE_MTIME
@@ -141,9 +147,8 @@ struct dir_entry {
char name[];
};
-static void __init dir_add(const char *name, time64_t mtime)
+static void __init dir_add(const char *name, size_t nlen, time64_t mtime)
{
- size_t nlen = strlen(name) + 1;
struct dir_entry *de;
de = kmalloc(sizeof(struct dir_entry) + nlen, GFP_KERNEL);
@@ -167,7 +172,7 @@ static void __init dir_utime(void)
#else
static void __init do_utime(char *filename, time64_t mtime) {}
static void __init do_utime_path(const struct path *path, time64_t mtime) {}
-static void __init dir_add(const char *name, time64_t mtime) {}
+static void __init dir_add(const char *name, size_t nlen, time64_t mtime) {}
static void __init dir_utime(void) {}
#endif
@@ -186,14 +191,11 @@ static __initdata u32 hdr_csum;
static void __init parse_header(char *s)
{
unsigned long parsed[13];
- char buf[9];
int i;
- buf[8] = '\0';
- for (i = 0, s += 6; i < 13; i++, s += 8) {
- memcpy(buf, s, 8);
- parsed[i] = simple_strtoul(buf, NULL, 16);
- }
+ for (i = 0, s += 6; i < 13; i++, s += 8)
+ parsed[i] = simple_strntoul(s, NULL, 16, 8);
+
ino = parsed[0];
mode = parsed[1];
uid = parsed[2];
@@ -254,7 +256,7 @@ static __initdata char *header_buf, *symlink_buf, *name_buf;
static int __init do_start(void)
{
- read_into(header_buf, 110, GotHeader);
+ read_into(header_buf, CPIO_HDRLEN, GotHeader);
return 0;
}
@@ -358,6 +360,15 @@ static int __init do_name(void)
{
state = SkipIt;
next_state = Reset;
+
+ /* name_len > 0 && name_len <= PATH_MAX checked in do_header */
+ if (collected[name_len - 1] != '\0') {
+ pr_err("initramfs name without nulterm: %.*s\n",
+ (int)name_len, collected);
+ error("malformed archive");
+ return 1;
+ }
+
if (strcmp(collected, "TRAILER!!!") == 0) {
free_hash();
return 0;
@@ -366,7 +377,7 @@ static int __init do_name(void)
if (S_ISREG(mode)) {
int ml = maybe_link();
if (ml >= 0) {
- int openflags = O_WRONLY|O_CREAT;
+ int openflags = O_WRONLY|O_CREAT|O_LARGEFILE;
if (ml != 1)
openflags |= O_TRUNC;
wfile = filp_open(collected, openflags, mode);
@@ -385,7 +396,7 @@ static int __init do_name(void)
init_mkdir(collected, mode);
init_chown(collected, uid, gid, 0);
init_chmod(collected, mode);
- dir_add(collected, mtime);
+ dir_add(collected, name_len, mtime);
} else if (S_ISBLK(mode) || S_ISCHR(mode) ||
S_ISFIFO(mode) || S_ISSOCK(mode)) {
if (maybe_link() == 0) {
@@ -422,6 +433,12 @@ static int __init do_copy(void)
static int __init do_symlink(void)
{
+ if (collected[name_len - 1] != '\0') {
+ pr_err("initramfs symlink without nulterm: %.*s\n",
+ (int)name_len, collected);
+ error("malformed archive");
+ return 1;
+ }
collected[N_ALIGN(name_len) + body_len] = '\0';
clean_path(collected, 0);
init_symlink(collected + N_ALIGN(name_len), collected);
@@ -480,20 +497,33 @@ static unsigned long my_inptr __initdata; /* index of next byte to be processed
#include <linux/decompress/generic.h>
-static char * __init unpack_to_rootfs(char *buf, unsigned long len)
+/**
+ * unpack_to_rootfs - decompress and extract an initramfs archive
+ * @buf: input initramfs archive to extract
+ * @len: length of initramfs data to process
+ *
+ * Returns: NULL for success or an error message string
+ *
+ * This symbol shouldn't be used externally. It's available for unit tests.
+ */
+char * __init unpack_to_rootfs(char *buf, unsigned long len)
{
long written;
decompress_fn decompress;
const char *compress_name;
- static __initdata char msg_buf[64];
-
- header_buf = kmalloc(110, GFP_KERNEL);
- symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL);
- name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL);
+ struct {
+ char header[CPIO_HDRLEN];
+ char symlink[PATH_MAX + N_ALIGN(PATH_MAX) + 1];
+ char name[N_ALIGN(PATH_MAX)];
+ } *bufs = kmalloc(sizeof(*bufs), GFP_KERNEL);
- if (!header_buf || !symlink_buf || !name_buf)
+ if (!bufs)
panic_show_mem("can't allocate buffers");
+ header_buf = bufs->header;
+ symlink_buf = bufs->symlink;
+ name_buf = bufs->name;
+
state = Start;
this_header = 0;
message = NULL;
@@ -521,12 +551,9 @@ static char * __init unpack_to_rootfs(char *buf, unsigned long len)
if (res)
error("decompressor failed");
} else if (compress_name) {
- if (!message) {
- snprintf(msg_buf, sizeof msg_buf,
- "compression method %s not configured",
- compress_name);
- message = msg_buf;
- }
+ pr_err("compression method %s not configured\n",
+ compress_name);
+ error("decompressor failed");
} else
error("invalid magic at start of compressed archive");
if (state != Reset)
@@ -536,9 +563,9 @@ static char * __init unpack_to_rootfs(char *buf, unsigned long len)
len -= my_inptr;
}
dir_utime();
- kfree(name_buf);
- kfree(symlink_buf);
- kfree(header_buf);
+ /* free any hardlink state collected without optional TRAILER!!! */
+ free_hash();
+ kfree(bufs);
return message;
}
@@ -574,15 +601,7 @@ extern unsigned long __initramfs_size;
#include <linux/initrd.h>
#include <linux/kexec.h>
-static ssize_t raw_read(struct file *file, struct kobject *kobj,
- struct bin_attribute *attr, char *buf,
- loff_t pos, size_t count)
-{
- memcpy(buf, attr->private + pos, count);
- return count;
-}
-
-static BIN_ATTR(initrd, 0440, raw_read, NULL, 0);
+static BIN_ATTR(initrd, 0440, sysfs_bin_attr_simple_read, NULL, 0);
void __init reserve_initrd_mem(void)
{
@@ -642,7 +661,7 @@ void __weak __init free_initrd_mem(unsigned long start, unsigned long end)
"initrd");
}
-#ifdef CONFIG_KEXEC_CORE
+#ifdef CONFIG_CRASH_RESERVE
static bool __init kexec_free_initrd(void)
{
unsigned long crashk_start = (unsigned long)__va(crashk_res.start);
@@ -679,11 +698,9 @@ static void __init populate_initrd_image(char *err)
struct file *file;
loff_t pos = 0;
- unpack_to_rootfs(__initramfs_start, __initramfs_size);
-
printk(KERN_INFO "rootfs image is not initramfs (%s); looks like an initrd\n",
err);
- file = filp_open("/initrd.image", O_WRONLY | O_CREAT, 0700);
+ file = filp_open("/initrd.image", O_WRONLY|O_CREAT|O_LARGEFILE, 0700);
if (IS_ERR(file))
return;
@@ -721,6 +738,8 @@ static void __init do_populate_rootfs(void *unused, async_cookie_t cookie)
}
done:
+ security_initramfs_populated();
+
/*
* If the initrd region is overlapped with crashkernel reserved region,
* free only memory that is not part of crashkernel region.
@@ -736,8 +755,7 @@ done:
initrd_start = 0;
initrd_end = 0;
- flush_delayed_fput();
- task_work_run();
+ init_flush_fput();
}
static ASYNC_DOMAIN_EXCLUSIVE(initramfs_domain);