2010-07-07 11:12:45 +00:00
|
|
|
From: Jeff Mahoney <jeffm@suse.com>
|
|
|
|
Subject: initramfs: add initramfs_{read,write}
|
|
|
|
References: bnc#568120
|
|
|
|
Patch-mainline: Probably never
|
|
|
|
|
|
|
|
This patch adds initramfs_read and initramfs_write, which will read and
|
|
|
|
write to the initramfs without traversing huge chunks of the VFS code.
|
|
|
|
A previous incarnation of the ACPI dynamic table patches ended up
|
|
|
|
causing "scheduling while atomic" warnings during boot, resulting in a
|
|
|
|
whole lot of bug reports.
|
|
|
|
|
|
|
|
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
|
|
|
|
---
|
2011-04-19 20:09:59 +00:00
|
|
|
init/initramfs.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
|
|
|
|
1 file changed, 149 insertions(+), 3 deletions(-)
|
2010-07-07 11:12:45 +00:00
|
|
|
|
|
|
|
--- a/init/initramfs.c
|
|
|
|
+++ b/init/initramfs.c
|
|
|
|
@@ -1,5 +1,6 @@
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
+#include <linux/file.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/fcntl.h>
|
|
|
|
@@ -8,6 +9,8 @@
|
|
|
|
#include <linux/dirent.h>
|
|
|
|
#include <linux/syscalls.h>
|
|
|
|
#include <linux/utime.h>
|
|
|
|
+#include <linux/pagemap.h>
|
|
|
|
+#include <linux/uio.h>
|
|
|
|
|
|
|
|
static __initdata char *message;
|
|
|
|
static void __init error(char *x)
|
2011-04-19 20:09:59 +00:00
|
|
|
@@ -333,10 +336,153 @@ static int __init do_name(void)
|
2010-07-07 11:12:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ssize_t initramfs_file_read(struct file *file, const char *buf,
|
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
|
+{
|
|
|
|
+ struct address_space *mapping = file->f_mapping;
|
|
|
|
+ struct iovec iov = { .iov_base = (void __user *) buf,
|
|
|
|
+ .iov_len = count };
|
|
|
|
+ struct iov_iter i;
|
|
|
|
+ long status = 0;
|
|
|
|
+ loff_t pos = *ppos;
|
|
|
|
+ ssize_t read = 0;
|
|
|
|
+
|
|
|
|
+ iov_iter_init(&i, &iov, 1, count, 0);
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ struct page *page;
|
|
|
|
+ pgoff_t index;
|
|
|
|
+ unsigned long offset;
|
|
|
|
+ unsigned long bytes;
|
|
|
|
+ char *data;
|
|
|
|
+
|
|
|
|
+ offset = (pos & (PAGE_CACHE_SIZE - 1));
|
|
|
|
+ index = pos >> PAGE_CACHE_SHIFT;
|
|
|
|
+ bytes = min_t(unsigned long, PAGE_CACHE_SIZE - offset,
|
|
|
|
+ iov_iter_count(&i));
|
|
|
|
+
|
|
|
|
+ page = read_mapping_page(mapping, index, NULL);
|
|
|
|
+ if (IS_ERR(page)) {
|
|
|
|
+ status = PTR_ERR(page);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
2011-04-19 20:09:59 +00:00
|
|
|
+ data = kmap_atomic(page, KM_USER0);
|
2010-07-07 11:12:45 +00:00
|
|
|
+ memcpy(i.iov->iov_base + i.iov_offset, data + offset, bytes);
|
2011-04-19 20:09:59 +00:00
|
|
|
+ kunmap_atomic(data, KM_USER0);
|
2010-07-07 11:12:45 +00:00
|
|
|
+
|
|
|
|
+ iov_iter_advance(&i, bytes);
|
|
|
|
+ pos += bytes;
|
|
|
|
+ read += bytes;
|
|
|
|
+ } while (iov_iter_count(&i));
|
|
|
|
+
|
|
|
|
+ *ppos = pos;
|
|
|
|
+
|
|
|
|
+ return read ? read : status;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+ssize_t initramfs_file_write(struct file *file, const char * __user buf,
|
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
|
+{
|
|
|
|
+ struct address_space *mapping = file->f_mapping;
|
|
|
|
+ struct iovec iov = { .iov_base = (void __user *) buf,
|
|
|
|
+ .iov_len = count };
|
|
|
|
+ long status = 0;
|
|
|
|
+ ssize_t written = 0;
|
|
|
|
+ unsigned int flags = 0;
|
|
|
|
+ loff_t pos = *ppos;
|
|
|
|
+ struct iov_iter i;
|
|
|
|
+
|
|
|
|
+ iov_iter_init(&i, &iov, 1, count, 0);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Copies from kernel address space cannot fail (NFSD is a big user).
|
|
|
|
+ */
|
|
|
|
+ if (segment_eq(get_fs(), KERNEL_DS))
|
|
|
|
+ flags |= AOP_FLAG_UNINTERRUPTIBLE;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&mapping->host->i_mutex);
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ struct page *page;
|
|
|
|
+ pgoff_t index; /* Pagecache index for current page */
|
|
|
|
+ unsigned long offset; /* Offset into pagecache page */
|
|
|
|
+ unsigned long bytes; /* Bytes to write to page */
|
|
|
|
+ size_t copied; /* Bytes copied from user */
|
|
|
|
+ void *fsdata;
|
|
|
|
+ char *data;
|
|
|
|
+
|
|
|
|
+ offset = (pos & (PAGE_CACHE_SIZE - 1));
|
|
|
|
+ index = pos >> PAGE_CACHE_SHIFT;
|
|
|
|
+ bytes = min_t(unsigned long, PAGE_CACHE_SIZE - offset,
|
|
|
|
+ iov_iter_count(&i));
|
|
|
|
+
|
|
|
|
+ status = simple_write_begin(file, mapping, pos, bytes, flags,
|
|
|
|
+ &page, &fsdata);
|
|
|
|
+ if (unlikely(status))
|
|
|
|
+ break;
|
2011-04-19 20:09:59 +00:00
|
|
|
+ data = kmap_atomic(page, KM_USER0);
|
2010-07-07 11:12:45 +00:00
|
|
|
+ memcpy(data + offset, i.iov->iov_base + i.iov_offset, bytes);
|
2011-04-19 20:09:59 +00:00
|
|
|
+ kunmap_atomic(data, KM_USER0);
|
2010-07-07 11:12:45 +00:00
|
|
|
+ copied = bytes;
|
|
|
|
+
|
|
|
|
+ status = simple_write_end(file, mapping, pos, bytes, copied,
|
|
|
|
+ page, fsdata);
|
|
|
|
+ if (unlikely(status < 0))
|
|
|
|
+ break;
|
|
|
|
+ copied = status;
|
|
|
|
+
|
|
|
|
+ iov_iter_advance(&i, copied);
|
|
|
|
+ pos += copied;
|
|
|
|
+ written += copied;
|
|
|
|
+
|
|
|
|
+ } while (iov_iter_count(&i));
|
|
|
|
+
|
|
|
|
+ mutex_unlock(&mapping->host->i_mutex);
|
|
|
|
+
|
|
|
|
+ *ppos = pos;
|
|
|
|
+
|
|
|
|
+ return written ? written : status;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+ssize_t
|
|
|
|
+initramfs_read(unsigned int fd, const char * buf, size_t count)
|
|
|
|
+{
|
|
|
|
+ struct file *file;
|
|
|
|
+ ssize_t ret = 0;
|
|
|
|
+
|
|
|
|
+ file = fget(fd);
|
|
|
|
+ if (file) {
|
|
|
|
+ loff_t pos = file->f_pos;
|
|
|
|
+ ret = initramfs_file_read(file, buf, count, &pos);
|
|
|
|
+ file->f_pos = pos;
|
|
|
|
+ fput(file);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+ssize_t
|
|
|
|
+initramfs_write(unsigned int fd, const char * buf, size_t count)
|
|
|
|
+{
|
|
|
|
+ struct file *file;
|
|
|
|
+ ssize_t ret = 0;
|
|
|
|
+
|
|
|
|
+ file = fget(fd);
|
|
|
|
+ if (file) {
|
|
|
|
+ loff_t pos = file->f_pos;
|
|
|
|
+ ret = initramfs_file_write(file, buf, count, &pos);
|
|
|
|
+ file->f_pos = pos;
|
|
|
|
+ fput(file);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
static int __init do_copy(void)
|
|
|
|
{
|
|
|
|
if (count >= body_len) {
|
|
|
|
- sys_write(wfd, victim, body_len);
|
|
|
|
+ initramfs_write(wfd, victim, body_len);
|
|
|
|
sys_close(wfd);
|
|
|
|
do_utime(vcollected, mtime);
|
|
|
|
kfree(vcollected);
|
2011-04-19 20:09:59 +00:00
|
|
|
@@ -344,7 +490,7 @@ static int __init do_copy(void)
|
2010-07-07 11:12:45 +00:00
|
|
|
state = SkipIt;
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
- sys_write(wfd, victim, count);
|
|
|
|
+ initramfs_write(wfd, victim, count);
|
|
|
|
body_len -= count;
|
|
|
|
eat(count);
|
|
|
|
return 1;
|
2011-04-19 20:09:59 +00:00
|
|
|
@@ -592,7 +738,7 @@ static int __init populate_rootfs(void)
|
2010-07-07 11:12:45 +00:00
|
|
|
"; looks like an initrd\n", err);
|
|
|
|
fd = sys_open("/initrd.image", O_WRONLY|O_CREAT, 0700);
|
|
|
|
if (fd >= 0) {
|
|
|
|
- sys_write(fd, (char *)initrd_start,
|
|
|
|
+ initramfs_write(fd, (char *)initrd_start,
|
|
|
|
initrd_end - initrd_start);
|
|
|
|
sys_close(fd);
|
|
|
|
free_initrd();
|