mirror of
https://github.com/kdave/btrfs-progs
synced 2025-02-04 11:51:42 +00:00
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>
This commit is contained in:
parent
968efc6f98
commit
9e4ad99099
10
kerncompat.h
10
kerncompat.h
@ -202,6 +202,16 @@ static inline long IS_ERR(const void *ptr)
|
||||
#define max_t(type,x,y) \
|
||||
({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
|
||||
|
||||
/*
|
||||
* This looks more complex than it should be. But we need to
|
||||
* get the type for the ~ right in round_down (it needs to be
|
||||
* as wide as the result!), and we want to evaluate the macro
|
||||
* arguments just once each.
|
||||
*/
|
||||
#define __round_mask(x, y) ((__typeof__(x))((y)-1))
|
||||
#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
|
||||
#define round_down(x, y) ((x) & ~__round_mask(x, y))
|
||||
|
||||
/*
|
||||
* printk
|
||||
*/
|
||||
|
45
mkfs.c
45
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)
|
||||
|
Loading…
Reference in New Issue
Block a user