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
, off_t offset
)
38 if (lseek(fd
, offset
, SEEK_SET
) < 0)
41 return write(fd
, buf
, count
);
43 #endif /* !defined HAVE_PWRITE64 && !defined HAVE_PWRITE */
45 static int getseed(void)
50 fd
= open("/dev/urandom", O_RDONLY
);
55 if (read(fd
, &r
, sizeof(r
)) != sizeof(r
))
56 printf("Unable to read random seed!\n");
63 ext2fs_block_bitmap bmap
;
64 struct ext2_inode
*inode
;
65 blk64_t corrupt_blocks
;
68 static int find_block_helper(ext2_filsys fs
EXT2FS_ATTR((unused
)),
69 blk64_t
*blocknr
, e2_blkcnt_t blockcnt
,
70 blk64_t ref_blk
EXT2FS_ATTR((unused
)),
71 int ref_offset
EXT2FS_ATTR((unused
)),
74 struct find_block
*fb
= (struct find_block
*)priv_data
;
76 if (S_ISDIR(fb
->inode
->i_mode
) || !metadata_only
|| blockcnt
< 0) {
77 ext2fs_mark_block_bitmap2(fb
->bmap
, *blocknr
);
84 static errcode_t
find_metadata_blocks(ext2_filsys fs
, ext2fs_block_bitmap bmap
,
91 struct ext2_inode inode
;
96 fb
.corrupt_blocks
= 0;
98 /* Construct bitmaps of super/descriptor blocks */
99 for (i
= 0; i
< fs
->group_desc_count
; i
++) {
100 ext2fs_reserve_super_and_bgd(fs
, i
, bmap
);
102 /* bitmaps and inode table */
103 b
= ext2fs_block_bitmap_loc(fs
, i
);
104 ext2fs_mark_block_bitmap2(bmap
, b
);
107 b
= ext2fs_inode_bitmap_loc(fs
, i
);
108 ext2fs_mark_block_bitmap2(bmap
, b
);
111 c
= ext2fs_inode_table_loc(fs
, i
);
112 ext2fs_mark_block_bitmap_range2(bmap
, c
,
113 fs
->inode_blocks_per_group
);
114 fb
.corrupt_blocks
+= fs
->inode_blocks_per_group
;
120 memset(&inode
, 0, sizeof(inode
));
121 retval
= ext2fs_open_inode_scan(fs
, 0, &scan
);
125 retval
= ext2fs_get_next_inode_full(scan
, &ino
, &inode
, sizeof(inode
));
129 if (inode
.i_links_count
== 0)
132 b
= ext2fs_file_acl_block(fs
, &inode
);
134 ext2fs_mark_block_bitmap2(bmap
, b
);
139 * Inline data, sockets, devices, and symlinks have
140 * no blocks to iterate.
142 if ((inode
.i_flags
& EXT4_INLINE_DATA_FL
) ||
143 S_ISLNK(inode
.i_mode
) || S_ISFIFO(inode
.i_mode
) ||
144 S_ISCHR(inode
.i_mode
) || S_ISBLK(inode
.i_mode
) ||
145 S_ISSOCK(inode
.i_mode
))
148 retval
= ext2fs_block_iterate3(fs
, ino
, BLOCK_FLAG_READ_ONLY
,
149 NULL
, find_block_helper
, &fb
);
153 retval
= ext2fs_get_next_inode_full(scan
, &ino
, &inode
,
159 ext2fs_close_inode_scan(scan
);
162 *corrupt_bytes
= fb
.corrupt_blocks
* fs
->blocksize
;
166 static uint64_t rand_num(uint64_t min
, uint64_t max
)
170 uint8_t *px
= (uint8_t *)&x
;
172 for (i
= 0; i
< sizeof(x
); i
++)
175 return min
+ (uint64_t)((double)(max
- min
) * (x
/ (UINT64_MAX
+ 1.0)));
178 static int process_fs(const char *fsname
)
182 ext2_filsys fs
= NULL
;
183 ext2fs_block_bitmap corrupt_map
;
184 off_t hsize
, count
, off
, offset
, corrupt_bytes
;
188 /* If mounted rw, force dryrun mode */
189 ret
= ext2fs_check_if_mounted(fsname
, &flags
);
191 fprintf(stderr
, "%s: failed to determine filesystem mount "
196 if (!dryrun
&& (flags
& EXT2_MF_MOUNTED
) &&
197 !(flags
& EXT2_MF_READONLY
)) {
198 fprintf(stderr
, "%s: is mounted rw, performing dry run.\n",
203 /* Ensure the fs is clean and does not have errors */
204 ret
= ext2fs_open(fsname
, EXT2_FLAG_64BITS
, 0, 0, unix_io_manager
,
207 fprintf(stderr
, "%s: failed to open filesystem.\n",
212 if ((fs
->super
->s_state
& EXT2_ERROR_FS
)) {
213 fprintf(stderr
, "%s: errors detected, run fsck.\n",
218 if (!dryrun
&& (fs
->super
->s_state
& EXT2_VALID_FS
) == 0) {
219 fprintf(stderr
, "%s: unclean shutdown, performing dry run.\n",
224 /* Construct a bitmap of whatever we're corrupting */
225 if (!metadata_only
) {
226 /* Load block bitmap */
227 ret
= ext2fs_read_block_bitmap(fs
);
229 fprintf(stderr
, "%s: error while reading block bitmap\n",
233 corrupt_map
= fs
->block_map
;
234 corrupt_bytes
= (ext2fs_blocks_count(fs
->super
) -
235 ext2fs_free_blocks_count(fs
->super
)) *
238 ret
= ext2fs_allocate_block_bitmap(fs
, "metadata block map",
241 fprintf(stderr
, "%s: unable to create block bitmap\n",
246 /* Iterate everything... */
247 ret
= find_metadata_blocks(fs
, corrupt_map
, &corrupt_bytes
);
249 fprintf(stderr
, "%s: while finding metadata\n",
255 /* Run around corrupting things */
256 fd
= open(fsname
, O_RDWR
);
262 hsize
= fs
->blocksize
* ext2fs_blocks_count(fs
->super
);
263 if (user_corrupt_bytes
> 0)
264 count
= user_corrupt_bytes
;
265 else if (user_corrupt_pct
> 0.0)
266 count
= user_corrupt_pct
* corrupt_bytes
/ 100;
268 count
= rand_num(0, corrupt_bytes
/ 100);
269 offset
= 4096; /* never corrupt superblock */
270 for (i
= 0; i
< count
; i
++) {
272 off
= rand_num(offset
, hsize
);
273 while (!ext2fs_test_block_bitmap2(corrupt_map
,
274 off
/ fs
->blocksize
));
276 if ((rand() % 2) && c
< 128)
279 printf("Corrupting byte %lld in block %lld to 0x%x\n",
280 (long long) off
% fs
->blocksize
,
281 (long long) off
/ fs
->blocksize
, c
);
285 if (pwrite64(fd
, &c
, sizeof(c
), off
) != sizeof(c
)) {
290 if (pwrite(fd
, &c
, sizeof(c
), off
) != sizeof(c
)) {
295 if (my_pwrite(fd
, &c
, sizeof(c
), off
) != sizeof(c
)) {
304 ret
= ext2fs_close_free(&fs
);
306 fprintf(stderr
, "%s: error while closing filesystem\n",
314 if (corrupt_map
!= fs
->block_map
)
315 ext2fs_free_block_bitmap(corrupt_map
);
317 ext2fs_close_free(&fs
);
321 static void print_help(const char *progname
)
323 printf("Usage: %s OPTIONS device\n", progname
);
324 printf("-b: Corrupt this many bytes.\n");
325 printf("-d: Fuzz data blocks too.\n");
326 printf("-n: Dry run only.\n");
327 printf("-v: Verbose output.\n");
331 int main(int argc
, char *argv
[])
335 while ((c
= getopt(argc
, argv
, "b:dnv")) != -1) {
338 if (optarg
[strlen(optarg
) - 1] == '%') {
339 user_corrupt_pct
= strtod(optarg
, NULL
);
340 if (user_corrupt_pct
> 100 ||
341 user_corrupt_pct
< 0) {
342 fprintf(stderr
, "%s: Invalid percentage.\n",
347 user_corrupt_bytes
= strtoull(optarg
, NULL
, 0);
367 for (c
= optind
; c
< argc
; c
++)
368 if (process_fs(argv
[c
]))