187 lines
6.8 KiB
Diff
187 lines
6.8 KiB
Diff
From f5a3deef588759c49dc862427a265aced612002d Mon Sep 17 00:00:00 2001
|
|
From: "Richard W.M. Jones" <rjones@redhat.com>
|
|
Date: Wed, 6 Jul 2016 12:12:10 +0100
|
|
Subject: [PATCH 5/7] ext2: Don't load whole files into memory when copying to
|
|
the appliance (RHBZ#1113065).
|
|
|
|
Obviously for very large files this is going to be a problem, as well
|
|
as not being very cache efficient.
|
|
|
|
libext2fs can handle writes to parts of files just fine so copy files
|
|
in blocks.
|
|
|
|
Also demote the "Permission denied" error to a warning, and add some
|
|
explanatory text telling people not to use sudo.
|
|
---
|
|
src/ext2fs-c.c | 127 +++++++++++++++++++++++++++++++++++----------------------
|
|
1 file changed, 78 insertions(+), 49 deletions(-)
|
|
|
|
diff --git a/src/ext2fs-c.c b/src/ext2fs-c.c
|
|
index cb9282b..96a3de0 100644
|
|
--- a/src/ext2fs-c.c
|
|
+++ b/src/ext2fs-c.c
|
|
@@ -185,6 +185,7 @@ supermin_ext2fs_read_bitmaps (value fsv)
|
|
static void ext2_mkdir (ext2_filsys fs, ext2_ino_t dir_ino, const char *dirname, const char *basename, mode_t mode, uid_t uid, gid_t gid, time_t ctime, time_t atime, time_t mtime);
|
|
static void ext2_empty_inode (ext2_filsys fs, ext2_ino_t dir_ino, const char *dirname, const char *basename, mode_t mode, uid_t uid, gid_t gid, time_t ctime, time_t atime, time_t mtime, int major, int minor, int dir_ft, ext2_ino_t *ino_ret);
|
|
static void ext2_write_file (ext2_filsys fs, ext2_ino_t ino, const char *buf, size_t size, const char *filename);
|
|
+static void ext2_write_host_file (ext2_filsys fs, ext2_ino_t ino, const char *src, const char *filename);
|
|
static void ext2_link (ext2_filsys fs, ext2_ino_t dir_ino, const char *basename, ext2_ino_t ino, int dir_ft);
|
|
static void ext2_clean_path (ext2_filsys fs, ext2_ino_t dir_ino, const char *dirname, const char *basename, int isdir);
|
|
static void ext2_copy_file (struct ext2_data *data, const char *src, const char *dest);
|
|
@@ -500,6 +501,81 @@ ext2_write_file (ext2_filsys fs,
|
|
ext2_error_to_exception ("ext2fs_write_inode", err, filename);
|
|
}
|
|
|
|
+/* Same as ext2_write_file, but it copies the file contents from the
|
|
+ * host. You must create the file first with ext2_empty_inode, and
|
|
+ * the host file must be a regular file.
|
|
+ */
|
|
+static void
|
|
+ext2_write_host_file (ext2_filsys fs,
|
|
+ ext2_ino_t ino,
|
|
+ const char *src, /* source (host) file */
|
|
+ const char *filename)
|
|
+{
|
|
+ int fd;
|
|
+ char buf[BUFSIZ];
|
|
+ ssize_t r;
|
|
+ size_t size = 0;
|
|
+ errcode_t err;
|
|
+ ext2_file_t file;
|
|
+ unsigned int written;
|
|
+
|
|
+ fd = open (src, O_RDONLY);
|
|
+ if (fd == -1) {
|
|
+ static int warned = 0;
|
|
+
|
|
+ /* We skip unreadable files. However if the error is -EACCES then
|
|
+ * modify the message so as not to frighten the horses.
|
|
+ */
|
|
+ fprintf (stderr, "supermin: warning: %s: %m (ignored)\n", filename);
|
|
+ if (errno == EACCES && !warned) {
|
|
+ fprintf (stderr,
|
|
+ "Some distro files are not public readable, so supermin cannot copy them\n"
|
|
+ "into the appliance. This is a problem with your Linux distro. Please ask\n"
|
|
+ "your distro to stop doing pointless security by obscurity.\n"
|
|
+ "You can ignore these warnings. You *do not* need to use sudo.\n");
|
|
+ warned = 1;
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ err = ext2fs_file_open2 (fs, ino, NULL, EXT2_FILE_WRITE, &file);
|
|
+ if (err != 0)
|
|
+ ext2_error_to_exception ("ext2fs_file_open2", err, filename);
|
|
+
|
|
+ while ((r = read (fd, buf, sizeof buf)) > 0) {
|
|
+ err = ext2fs_file_write (file, buf, r, &written);
|
|
+ if (err != 0)
|
|
+ ext2_error_to_exception ("ext2fs_file_open2", err, filename);
|
|
+ if ((ssize_t) written != r)
|
|
+ caml_failwith ("ext2fs_file_write: requested write size != bytes written");
|
|
+ size += written;
|
|
+ }
|
|
+
|
|
+ if (r == -1)
|
|
+ unix_error (errno, (char *) "read", caml_copy_string (filename));
|
|
+
|
|
+ if (close (fd) == -1)
|
|
+ unix_error (errno, (char *) "close", caml_copy_string (filename));
|
|
+
|
|
+ /* Flush out the ext2 file. */
|
|
+ err = ext2fs_file_flush (file);
|
|
+ if (err != 0)
|
|
+ ext2_error_to_exception ("ext2fs_file_flush", err, filename);
|
|
+ err = ext2fs_file_close (file);
|
|
+ if (err != 0)
|
|
+ ext2_error_to_exception ("ext2fs_file_close", err, filename);
|
|
+
|
|
+ /* Update the true size in the inode. */
|
|
+ struct ext2_inode inode;
|
|
+ err = ext2fs_read_inode (fs, ino, &inode);
|
|
+ if (err != 0)
|
|
+ ext2_error_to_exception ("ext2fs_read_inode", err, filename);
|
|
+ inode.i_size = size;
|
|
+ err = ext2fs_write_inode (fs, ino, &inode);
|
|
+ if (err != 0)
|
|
+ ext2_error_to_exception ("ext2fs_write_inode", err, filename);
|
|
+}
|
|
+
|
|
/* This is just a wrapper around ext2fs_link which calls
|
|
* ext2fs_expand_dir as necessary if the directory fills up. See
|
|
* definition of expand_dir in the sources of debugfs.
|
|
@@ -589,43 +665,6 @@ ext2_clean_path (ext2_filsys fs, ext2_ino_t dir_ino,
|
|
/* else it's a directory, what to do? XXX */
|
|
}
|
|
|
|
-/* Read in the whole file into memory. Check the size is still 'size'. */
|
|
-static char *
|
|
-read_whole_file (const char *filename, size_t size)
|
|
-{
|
|
- char *buf = malloc (size);
|
|
- if (buf == NULL)
|
|
- caml_raise_out_of_memory ();
|
|
-
|
|
- int fd = open (filename, O_RDONLY);
|
|
- if (fd == -1) {
|
|
- /* Skip unreadable files. */
|
|
- fprintf (stderr, "supermin: open: %s: %m\n", filename);
|
|
- free (buf);
|
|
- return NULL;
|
|
- }
|
|
-
|
|
- size_t n = 0;
|
|
- char *p = buf;
|
|
-
|
|
- while (n < size) {
|
|
- ssize_t r = read (fd, p, size - n);
|
|
- if (r == -1)
|
|
- unix_error (errno, (char *) "read", caml_copy_string (filename));
|
|
- if (r == 0) {
|
|
- fprintf (stderr, "supermin: end of file reading '%s'\n", filename);
|
|
- caml_invalid_argument ("ext2fs: file has changed size unexpectedly");
|
|
- }
|
|
- n += r;
|
|
- p += r;
|
|
- }
|
|
-
|
|
- if (close (fd) == -1)
|
|
- unix_error (errno, (char *) "close", caml_copy_string (filename));
|
|
-
|
|
- return buf;
|
|
-}
|
|
-
|
|
/* Copy a file (or directory etc) from the host. */
|
|
static void
|
|
ext2_copy_file (struct ext2_data *data, const char *src, const char *dest)
|
|
@@ -766,24 +805,14 @@ ext2_copy_file (struct ext2_data *data, const char *src, const char *dest)
|
|
if (S_ISREG (statbuf.st_mode)) {
|
|
/* XXX Hard links get duplicated here. */
|
|
ext2_ino_t ino;
|
|
- char *buf = NULL;
|
|
-
|
|
- if (statbuf.st_size > 0) {
|
|
- buf = read_whole_file (src, statbuf.st_size);
|
|
- if (buf == NULL)
|
|
- goto skip_unreadable_file;
|
|
- }
|
|
|
|
ext2_empty_inode (data->fs, dir_ino, dirname, basename,
|
|
statbuf.st_mode, statbuf.st_uid, statbuf.st_gid,
|
|
statbuf.st_ctime, statbuf.st_atime, statbuf.st_mtime,
|
|
0, 0, EXT2_FT_REG_FILE, &ino);
|
|
|
|
- if (statbuf.st_size > 0) {
|
|
- ext2_write_file (data->fs, ino, buf, statbuf.st_size, dest);
|
|
- free (buf);
|
|
- }
|
|
- skip_unreadable_file: ;
|
|
+ if (statbuf.st_size > 0)
|
|
+ ext2_write_host_file (data->fs, ino, src, dest);
|
|
}
|
|
/* Create a symlink. */
|
|
else if (S_ISLNK (statbuf.st_mode)) {
|
|
--
|
|
2.7.4
|
|
|