2 * e2fuzz.c -- Fuzz an ext4 image, for testing purposes.
4 * Copyright (C) 2014 Oracle.
7 * This file may be redistributed under the terms of the GNU Library
8 * General Public License, version 2.
11 #define _XOPEN_SOURCE 600
12 #define _FILE_OFFSET_BITS 64
13 #define _LARGEFILE64_SOURCE 1
17 #include <sys/types.h>
26 #include "ext2fs/ext2_fs.h"
27 #include "ext2fs/ext2fs.h"
29 static int dryrun = 0;
30 static int verbose = 0;
31 static int metadata_only = 1;
32 static unsigned long long user_corrupt_bytes = 0;
33 static double user_corrupt_pct = 0.0;
35 #if !defined HAVE_PWRITE64 && !defined HAVE_PWRITE
36 static ssize_t my_pwrite(int fd, const void *buf, size_t count,
39 if (ext2fs_llseek(fd, offset, SEEK_SET) < 0)
42 return write(fd, buf, count);
44 #endif /* !defined HAVE_PWRITE64 && !defined HAVE_PWRITE */
46 static int getseed(void)
51 fd = open("/dev/urandom", O_RDONLY);
56 if (read(fd, &r, sizeof(r)) != sizeof(r))
57 printf("Unable to read random seed!\n");
64 ext2fs_block_bitmap bmap;
65 struct ext2_inode *inode;
66 blk64_t corrupt_blocks;
69 static int find_block_helper(ext2_filsys fs EXT2FS_ATTR((unused)),
70 blk64_t *blocknr, e2_blkcnt_t blockcnt,
71 blk64_t ref_blk EXT2FS_ATTR((unused)),
72 int ref_offset EXT2FS_ATTR((unused)),
75 struct find_block *fb = (struct find_block *)priv_data;
77 if (S_ISDIR(fb->inode->i_mode) || !metadata_only || blockcnt < 0) {
78 ext2fs_mark_block_bitmap2(fb->bmap, *blocknr);
85 static errcode_t find_metadata_blocks(ext2_filsys fs, ext2fs_block_bitmap bmap,
86 ext2_loff_t *corrupt_bytes)
92 struct ext2_inode inode;
97 fb.corrupt_blocks = 0;
99 /* Construct bitmaps of super/descriptor blocks */
100 for (i = 0; i < fs->group_desc_count; i++) {
101 ext2fs_reserve_super_and_bgd(fs, i, bmap);
103 /* bitmaps and inode table */
104 b = ext2fs_block_bitmap_loc(fs, i);
105 ext2fs_mark_block_bitmap2(bmap, b);
108 b = ext2fs_inode_bitmap_loc(fs, i);
109 ext2fs_mark_block_bitmap2(bmap, b);
112 c = ext2fs_inode_table_loc(fs, i);
113 ext2fs_mark_block_bitmap_range2(bmap, c,
114 fs->inode_blocks_per_group);
115 fb.corrupt_blocks += fs->inode_blocks_per_group;
121 memset(&inode, 0, sizeof(inode));
122 retval = ext2fs_open_inode_scan(fs, 0, &scan);
126 retval = ext2fs_get_next_inode_full(scan, &ino, &inode, sizeof(inode));
130 if (inode.i_links_count == 0)
133 b = ext2fs_file_acl_block(fs, &inode);
135 ext2fs_mark_block_bitmap2(bmap, b);
140 * Inline data, sockets, devices, and symlinks have
141 * no blocks to iterate.
143 if ((inode.i_flags & EXT4_INLINE_DATA_FL) ||
144 S_ISLNK(inode.i_mode) || S_ISFIFO(inode.i_mode) ||
145 S_ISCHR(inode.i_mode) || S_ISBLK(inode.i_mode) ||
146 S_ISSOCK(inode.i_mode))
149 retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY,
150 NULL, find_block_helper, &fb);
154 retval = ext2fs_get_next_inode_full(scan, &ino, &inode,
160 ext2fs_close_inode_scan(scan);
163 *corrupt_bytes = fb.corrupt_blocks * fs->blocksize;
167 static uint64_t rand_num(uint64_t min, uint64_t max)
171 uint8_t *px = (uint8_t *)&x;
173 for (i = 0; i < sizeof(x); i++)
176 return min + (uint64_t)((double)(max - min) *
177 (x / ((double) UINT64_MAX + 1.0)));
180 static int process_fs(const char *fsname)
184 ext2_filsys fs = NULL;
185 ext2fs_block_bitmap corrupt_map;
186 ext2_loff_t hsize, count, off, offset, corrupt_bytes, i;
189 /* If mounted rw, force dryrun mode */
190 ret = ext2fs_check_if_mounted(fsname, &flags);
192 fprintf(stderr, "%s: failed to determine filesystem mount "
197 if (!dryrun && (flags & EXT2_MF_MOUNTED) &&
198 !(flags & EXT2_MF_READONLY)) {
199 fprintf(stderr, "%s: is mounted rw, performing dry run.\n",
204 /* Ensure the fs is clean and does not have errors */
205 ret = ext2fs_open(fsname, EXT2_FLAG_64BITS | EXT2_FLAG_THREADS,
206 0, 0, unix_io_manager, &fs);
208 fprintf(stderr, "%s: failed to open filesystem.\n",
213 if ((fs->super->s_state & EXT2_ERROR_FS)) {
214 fprintf(stderr, "%s: errors detected, run fsck.\n",
219 if (!dryrun && (fs->super->s_state & EXT2_VALID_FS) == 0) {
220 fprintf(stderr, "%s: unclean shutdown, performing dry run.\n",
225 /* Construct a bitmap of whatever we're corrupting */
226 if (!metadata_only) {
227 /* Load block bitmap */
228 ret = ext2fs_read_block_bitmap(fs);
230 fprintf(stderr, "%s: error while reading block bitmap\n",
234 corrupt_map = fs->block_map;
235 corrupt_bytes = (ext2fs_blocks_count(fs->super) -
236 ext2fs_free_blocks_count(fs->super)) *
239 ret = ext2fs_allocate_block_bitmap(fs, "metadata block map",
242 fprintf(stderr, "%s: unable to create block bitmap\n",
247 /* Iterate everything... */
248 ret = find_metadata_blocks(fs, corrupt_map, &corrupt_bytes);
250 fprintf(stderr, "%s: while finding metadata\n",
256 /* Run around corrupting things */
257 fd = open(fsname, O_RDWR);
263 hsize = fs->blocksize * ext2fs_blocks_count(fs->super);
264 if (user_corrupt_bytes > 0)
265 count = user_corrupt_bytes;
266 else if (user_corrupt_pct > 0.0)
267 count = user_corrupt_pct * corrupt_bytes / 100;
269 count = rand_num(0, corrupt_bytes / 100);
270 offset = 4096; /* never corrupt superblock */
271 for (i = 0; i < count; i++) {
273 off = rand_num(offset, hsize);
274 while (!ext2fs_test_block_bitmap2(corrupt_map,
275 off / fs->blocksize));
277 if ((rand() % 2) && c < 128)
280 printf("Corrupting byte %lld in block %lld to 0x%x\n",
282 off / fs->blocksize, c);
286 if (pwrite64(fd, &c, sizeof(c), off) != sizeof(c)) {
291 if (pwrite(fd, &c, sizeof(c), off) != sizeof(c)) {
296 if (my_pwrite(fd, &c, sizeof(c), off) != sizeof(c)) {
305 ret = ext2fs_close_free(&fs);
307 fprintf(stderr, "%s: error while closing filesystem\n",
315 if (corrupt_map != fs->block_map)
316 ext2fs_free_block_bitmap(corrupt_map);
318 ext2fs_close_free(&fs);
322 static void print_help(const char *progname)
324 printf("Usage: %s OPTIONS device\n", progname);
325 printf("-b: Corrupt this many bytes.\n");
326 printf("-d: Fuzz data blocks too.\n");
327 printf("-n: Dry run only.\n");
328 printf("-v: Verbose output.\n");
332 int main(int argc, char *argv[])
336 while ((c = getopt(argc, argv, "b:dnv")) != -1) {
339 if (optarg[strlen(optarg) - 1] == '%') {
340 user_corrupt_pct = strtod(optarg, NULL);
341 if (user_corrupt_pct > 100 ||
342 user_corrupt_pct < 0) {
343 fprintf(stderr, "%s: Invalid percentage.\n",
348 user_corrupt_bytes = strtoull(optarg, NULL, 0);
368 for (c = optind; c < argc; c++)
369 if (process_fs(argv[c]))