]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - misc/e2fuzz.c
Remove dropped const warning and enable pread/pwrite for Android build
[thirdparty/e2fsprogs.git] / misc / e2fuzz.c
CommitLineData
c4d5b81e
DW
1/*
2 * e2fuzz.c -- Fuzz an ext4 image, for testing purposes.
3 *
4 * Copyright (C) 2014 Oracle.
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU Library
8 * General Public License, version 2.
9 * %End-Header%
10 */
11#define _XOPEN_SOURCE 600
12#define _FILE_OFFSET_BITS 64
13#define _LARGEFILE64_SOURCE 1
14#define _GNU_SOURCE 1
15
16#include "config.h"
17#include <sys/types.h>
18#include <sys/stat.h>
19#include <fcntl.h>
20#include <stdint.h>
0165cfad 21#include <unistd.h>
c4d5b81e
DW
22#ifdef HAVE_GETOPT_H
23#include <getopt.h>
24#endif
25
26#include "ext2fs/ext2_fs.h"
27#include "ext2fs/ext2fs.h"
28
29static int dryrun = 0;
30static int verbose = 0;
31static int metadata_only = 1;
32static unsigned long long user_corrupt_bytes = 0;
33static double user_corrupt_pct = 0.0;
34
7248e265
DW
35#if !defined HAVE_PWRITE64 && !defined HAVE_PWRITE
36static ssize_t my_pwrite(int fd, const void *buf, size_t count, off_t offset)
0165cfad
DW
37{
38 if (lseek(fd, offset, SEEK_SET) < 0)
39 return 0;
40
41 return write(fd, buf, count);
42}
7248e265 43#endif /* !defined HAVE_PWRITE64 && !defined HAVE_PWRITE */
0165cfad 44
c4d5b81e
DW
45int getseed(void)
46{
47 int r;
48 int fd;
49
50 fd = open("/dev/urandom", O_RDONLY);
51 if (fd < 0) {
52 perror("open");
53 exit(0);
54 }
c4c9bc59
DW
55 if (read(fd, &r, sizeof(r)) != sizeof(r))
56 printf("Unable to read random seed!\n");
c4d5b81e
DW
57 close(fd);
58 return r;
59}
60
61struct find_block {
62 ext2_ino_t ino;
63 ext2fs_block_bitmap bmap;
64 struct ext2_inode *inode;
65 blk64_t corrupt_blocks;
66};
67
68int find_block_helper(ext2_filsys fs, blk64_t *blocknr, e2_blkcnt_t blockcnt,
69 blk64_t ref_blk, int ref_offset, void *priv_data)
70{
71 struct find_block *fb = (struct find_block *)priv_data;
72
73 if (S_ISDIR(fb->inode->i_mode) || !metadata_only || blockcnt < 0) {
74 ext2fs_mark_block_bitmap2(fb->bmap, *blocknr);
75 fb->corrupt_blocks++;
76 }
77
78 return 0;
79}
80
81errcode_t find_metadata_blocks(ext2_filsys fs, ext2fs_block_bitmap bmap,
82 off_t *corrupt_bytes)
83{
84 dgrp_t i;
0165cfad 85 blk64_t b, c;
c4d5b81e
DW
86 ext2_inode_scan scan;
87 ext2_ino_t ino;
88 struct ext2_inode inode;
89 struct find_block fb;
90 errcode_t retval;
91
92 *corrupt_bytes = 0;
93 fb.corrupt_blocks = 0;
c4d5b81e
DW
94
95 /* Construct bitmaps of super/descriptor blocks */
96 for (i = 0; i < fs->group_desc_count; i++) {
97 ext2fs_reserve_super_and_bgd(fs, i, bmap);
98
99 /* bitmaps and inode table */
100 b = ext2fs_block_bitmap_loc(fs, i);
101 ext2fs_mark_block_bitmap2(bmap, b);
102 fb.corrupt_blocks++;
103
104 b = ext2fs_inode_bitmap_loc(fs, i);
105 ext2fs_mark_block_bitmap2(bmap, b);
106 fb.corrupt_blocks++;
107
108 c = ext2fs_inode_table_loc(fs, i);
109 ext2fs_mark_block_bitmap_range2(bmap, c,
110 fs->inode_blocks_per_group);
111 fb.corrupt_blocks += fs->inode_blocks_per_group;
112 }
113
114 /* Scan inodes */
115 fb.bmap = bmap;
116 fb.inode = &inode;
117 memset(&inode, 0, sizeof(inode));
118 retval = ext2fs_open_inode_scan(fs, 0, &scan);
119 if (retval)
120 goto out;
121
122 retval = ext2fs_get_next_inode_full(scan, &ino, &inode, sizeof(inode));
123 if (retval)
124 goto out2;
125 while (ino) {
126 if (inode.i_links_count == 0)
127 goto next_loop;
128
129 b = ext2fs_file_acl_block(fs, &inode);
130 if (b) {
131 ext2fs_mark_block_bitmap2(bmap, b);
132 fb.corrupt_blocks++;
133 }
134
135 /*
136 * Inline data, sockets, devices, and symlinks have
137 * no blocks to iterate.
138 */
139 if ((inode.i_flags & EXT4_INLINE_DATA_FL) ||
140 S_ISLNK(inode.i_mode) || S_ISFIFO(inode.i_mode) ||
141 S_ISCHR(inode.i_mode) || S_ISBLK(inode.i_mode) ||
142 S_ISSOCK(inode.i_mode))
143 goto next_loop;
144 fb.ino = ino;
145 retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY,
146 NULL, find_block_helper, &fb);
147 if (retval)
148 goto out2;
149next_loop:
150 retval = ext2fs_get_next_inode_full(scan, &ino, &inode,
151 sizeof(inode));
152 if (retval)
153 goto out2;
154 }
155out2:
156 ext2fs_close_inode_scan(scan);
157out:
158 if (!retval)
159 *corrupt_bytes = fb.corrupt_blocks * fs->blocksize;
160 return retval;
161}
162
163uint64_t rand_num(uint64_t min, uint64_t max)
164{
165 uint64_t x;
166 int i;
167 uint8_t *px = (uint8_t *)&x;
168
169 for (i = 0; i < sizeof(x); i++)
170 px[i] = random();
171
172 return min + (uint64_t)((double)(max - min) * (x / (UINT64_MAX + 1.0)));
173}
174
175int process_fs(const char *fsname)
176{
177 errcode_t ret;
178 int flags, fd;
179 ext2_filsys fs = NULL;
180 ext2fs_block_bitmap corrupt_map;
181 off_t hsize, count, off, offset, corrupt_bytes;
182 unsigned char c;
183 unsigned long i;
184
185 /* If mounted rw, force dryrun mode */
186 ret = ext2fs_check_if_mounted(fsname, &flags);
187 if (ret) {
188 fprintf(stderr, "%s: failed to determine filesystem mount "
189 "state.\n", fsname);
190 return 1;
191 }
192
193 if (!dryrun && (flags & EXT2_MF_MOUNTED) &&
194 !(flags & EXT2_MF_READONLY)) {
195 fprintf(stderr, "%s: is mounted rw, performing dry run.\n",
196 fsname);
197 dryrun = 1;
198 }
199
200 /* Ensure the fs is clean and does not have errors */
201 ret = ext2fs_open(fsname, EXT2_FLAG_64BITS, 0, 0, unix_io_manager,
202 &fs);
203 if (ret) {
204 fprintf(stderr, "%s: failed to open filesystem.\n",
205 fsname);
206 return 1;
207 }
208
209 if ((fs->super->s_state & EXT2_ERROR_FS)) {
210 fprintf(stderr, "%s: errors detected, run fsck.\n",
211 fsname);
212 goto fail;
213 }
214
215 if (!dryrun && (fs->super->s_state & EXT2_VALID_FS) == 0) {
216 fprintf(stderr, "%s: unclean shutdown, performing dry run.\n",
217 fsname);
218 dryrun = 1;
219 }
220
221 /* Construct a bitmap of whatever we're corrupting */
222 if (!metadata_only) {
223 /* Load block bitmap */
224 ret = ext2fs_read_block_bitmap(fs);
225 if (ret) {
226 fprintf(stderr, "%s: error while reading block bitmap\n",
227 fsname);
228 goto fail;
229 }
230 corrupt_map = fs->block_map;
231 corrupt_bytes = (ext2fs_blocks_count(fs->super) -
232 ext2fs_free_blocks_count(fs->super)) *
233 fs->blocksize;
234 } else {
235 ret = ext2fs_allocate_block_bitmap(fs, "metadata block map",
236 &corrupt_map);
237 if (ret) {
238 fprintf(stderr, "%s: unable to create block bitmap\n",
239 fsname);
240 goto fail;
241 }
242
243 /* Iterate everything... */
244 ret = find_metadata_blocks(fs, corrupt_map, &corrupt_bytes);
245 if (ret) {
246 fprintf(stderr, "%s: while finding metadata\n",
247 fsname);
248 goto fail;
249 }
250 }
251
252 /* Run around corrupting things */
253 fd = open(fsname, O_RDWR);
254 if (fd < 0) {
255 perror(fsname);
256 goto fail;
257 }
258 srandom(getseed());
259 hsize = fs->blocksize * ext2fs_blocks_count(fs->super);
260 if (user_corrupt_bytes > 0)
261 count = user_corrupt_bytes;
262 else if (user_corrupt_pct > 0.0)
263 count = user_corrupt_pct * corrupt_bytes / 100;
264 else
265 count = rand_num(0, corrupt_bytes / 100);
266 offset = 4096; /* never corrupt superblock */
267 for (i = 0; i < count; i++) {
268 do
269 off = rand_num(offset, hsize);
270 while (!ext2fs_test_block_bitmap2(corrupt_map,
271 off / fs->blocksize));
272 c = rand() % 256;
273 if ((rand() % 2) && c < 128)
274 c |= 0x80;
275 if (verbose)
32ed2b08 276 printf("Corrupting byte %zu in block %zu to 0x%x\n",
c4d5b81e
DW
277 off % fs->blocksize, off / fs->blocksize, c);
278 if (dryrun)
279 continue;
7248e265
DW
280#ifdef HAVE_PWRITE64
281 if (pwrite64(fd, &c, sizeof(c), off) != sizeof(c)) {
282 perror(fsname);
283 goto fail3;
284 }
285#elif HAVE_PWRITE
0165cfad 286 if (pwrite(fd, &c, sizeof(c), off) != sizeof(c)) {
c4d5b81e
DW
287 perror(fsname);
288 goto fail3;
289 }
7248e265
DW
290#else
291 if (my_pwrite(fd, &c, sizeof(c), off) != sizeof(c)) {
292 perror(fsname);
293 goto fail3;
294 }
295#endif
c4d5b81e
DW
296 }
297 close(fd);
298
299 /* Clean up */
8375a881 300 ret = ext2fs_close_free(&fs);
c4d5b81e 301 if (ret) {
c4d5b81e
DW
302 fprintf(stderr, "%s: error while closing filesystem\n",
303 fsname);
8375a881 304 return 1;
c4d5b81e
DW
305 }
306
307 return 0;
308fail3:
309 close(fd);
c4d5b81e
DW
310 if (corrupt_map != fs->block_map)
311 ext2fs_free_block_bitmap(corrupt_map);
312fail:
8375a881 313 ext2fs_close_free(&fs);
c4d5b81e
DW
314 return 1;
315}
316
317void print_help(const char *progname)
318{
319 printf("Usage: %s OPTIONS device\n", progname);
320 printf("-b: Corrupt this many bytes.\n");
321 printf("-d: Fuzz data blocks too.\n");
322 printf("-n: Dry run only.\n");
323 printf("-v: Verbose output.\n");
324 exit(0);
325}
326
327int main(int argc, char *argv[])
328{
329 int c;
330
331 while ((c = getopt(argc, argv, "b:dnv")) != -1) {
332 switch (c) {
333 case 'b':
334 if (optarg[strlen(optarg) - 1] == '%') {
335 user_corrupt_pct = strtod(optarg, NULL);
336 if (user_corrupt_pct > 100 ||
337 user_corrupt_pct < 0) {
338 fprintf(stderr, "%s: Invalid percentage.\n",
339 optarg);
340 return 1;
341 }
342 } else
343 user_corrupt_bytes = strtoull(optarg, NULL, 0);
344 if (errno) {
345 perror(optarg);
346 return 1;
347 }
348 break;
349 case 'd':
350 metadata_only = 0;
351 break;
352 case 'n':
353 dryrun = 1;
354 break;
355 case 'v':
356 verbose = 1;
357 break;
358 default:
359 print_help(argv[0]);
360 }
361 }
362
363 for (c = optind; c < argc; c++)
364 if (process_fs(argv[c]))
365 return 1;
366 return 0;
367}