summaryrefslogtreecommitdiff
path: root/mkfs.c
diff options
context:
space:
mode:
authorZach Brown <zab@redhat.com>2013-01-17 15:23:10 -0800
committerZach Brown <zab@redhat.com>2013-02-05 16:09:38 -0800
commit9e4ad990992ebb588c599832d8d7dc0fe2a37475 (patch)
treedc0e0f8b355c09da5ff2a171089f70391a7e8da0 /mkfs.c
parent968efc6f988623aff2b4c21af8317ec80836a4b9 (diff)
btrfs-progs: use ftw() unstead of system("du")
size_sourcedir() uses shockingly bad code to try and estimate the size of the files and directories in a subtree. - Its use of snprintf(), strcat(), and sscanf() with arbitrarily small on-stack buffers manages to overflow the stack a few times when given long file names. $ BIG=$(perl -e 'print "a" x 200') $ mkdir -p /tmp/$BIG/$BIG/$BIG/$BIG/$BIG $ mkfs.btrfs /tmp/img -r /tmp/$BIG/$BIG/$BIG/$BIG/$BIG *** stack smashing detected ***: mkfs.btrfs terminated - It passes raw paths to system() allowing interpreting file names as shell control characters. $ mkfs.btrfs /tmp/img -r /tmp/spacey\ dir/ du: cannot access `/tmp/spacey': No such file or directory du: cannot access `dir/': No such file or directory - It redirects du output to "temp_file" in the current directory, allowing overwriting of files through symlinks. $ echo hi > target $ ln -s target temp_file $ mkfs.btrfs /tmp/img -r /tmp/somedir/ $ cat target 3 /tmp/somedir/ This fixes the worst problems while maintaining -r functionality by tearing out the system() code and using ftw() to walk the source tree and sum up st.st_size. Signed-off-by: Zach Brown <zab@redhat.com>
Diffstat (limited to 'mkfs.c')
-rw-r--r--mkfs.c45
1 files changed, 26 insertions, 19 deletions
diff --git a/mkfs.c b/mkfs.c
index 0e042c7a..0eee8b51 100644
--- a/mkfs.c
+++ b/mkfs.c
@@ -40,6 +40,8 @@
#include <ctype.h>
#include <attr/xattr.h>
#include <blkid/blkid.h>
+#include <ftw.h>
+#include "kerncompat.h"
#include "ctree.h"
#include "disk-io.h"
#include "volumes.h"
@@ -1097,16 +1099,30 @@ fail:
return -1;
}
+/*
+ * This ignores symlinks with unreadable targets and subdirs that can't
+ * be read. It's a best-effort to give a rough estimate of the size of
+ * a subdir. It doesn't guarantee that prepopulating btrfs from this
+ * tree won't still run out of space.
+ *
+ * The rounding up to 4096 is questionable. Previous code used du -B 4096.
+ */
+static u64 global_total_size;
+static int ftw_add_entry_size(const char *fpath, const struct stat *st,
+ int type)
+{
+ if (type == FTW_F || type == FTW_D)
+ global_total_size += round_up(st->st_size, 4096);
+
+ return 0;
+}
+
static u64 size_sourcedir(char *dir_name, u64 sectorsize,
u64 *num_of_meta_chunks_ret, u64 *size_of_data_ret)
{
u64 dir_size = 0;
u64 total_size = 0;
int ret;
- char command[1024];
- char path[512];
- char *file_name = "temp_file";
- FILE *file;
u64 default_chunk_size = 8 * 1024 * 1024; /* 8MB */
u64 allocated_meta_size = 8 * 1024 * 1024; /* 8MB */
u64 allocated_total_size = 20 * 1024 * 1024; /* 20MB */
@@ -1114,23 +1130,14 @@ static u64 size_sourcedir(char *dir_name, u64 sectorsize,
u64 num_of_allocated_meta_chunks =
allocated_meta_size / default_chunk_size;
- ret = sprintf(command, "du -B 4096 -s ");
+ global_total_size = 0;
+ ret = ftw(dir_name, ftw_add_entry_size, 10);
+ dir_size = global_total_size;
if (ret < 0) {
- fprintf(stderr, "error executing sprintf for du command\n");
- return -1;
+ fprintf(stderr, "ftw subdir walk of '%s' failed: %s\n",
+ dir_name, strerror(errno));
+ exit(1);
}
- strcat(command, dir_name);
- strcat(command, " > ");
- strcat(command, file_name);
- ret = system(command);
-
- file = fopen(file_name, "r");
- ret = fscanf(file, "%lld %s\n", &dir_size, path);
- fclose(file);
- remove(file_name);
-
- dir_size *= sectorsize;
- *size_of_data_ret = dir_size;
num_of_meta_chunks = (dir_size / 2) / default_chunk_size;
if (((dir_size / 2) % default_chunk_size) != 0)