]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - misc/e2fuzz.c
Merge branch 'maint' into next
[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
4e222d9b 45static int getseed(void)
c4d5b81e
DW
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
4e222d9b
TT
68static 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)),
72 void *priv_data)
c4d5b81e
DW
73{
74 struct find_block *fb = (struct find_block *)priv_data;
75
76 if (S_ISDIR(fb->inode->i_mode) || !metadata_only || blockcnt < 0) {
77 ext2fs_mark_block_bitmap2(fb->bmap, *blocknr);
78 fb->corrupt_blocks++;
79 }
80
81 return 0;
82}
83
4e222d9b
TT
84static errcode_t find_metadata_blocks(ext2_filsys fs, ext2fs_block_bitmap bmap,
85 off_t *corrupt_bytes)
c4d5b81e
DW
86{
87 dgrp_t i;
0165cfad 88 blk64_t b, c;
c4d5b81e
DW
89 ext2_inode_scan scan;
90 ext2_ino_t ino;
91 struct ext2_inode inode;
92 struct find_block fb;
93 errcode_t retval;
94
95 *corrupt_bytes = 0;
96 fb.corrupt_blocks = 0;
c4d5b81e
DW
97
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);
101
102 /* bitmaps and inode table */
103 b = ext2fs_block_bitmap_loc(fs, i);
104 ext2fs_mark_block_bitmap2(bmap, b);
105 fb.corrupt_blocks++;
106
107 b = ext2fs_inode_bitmap_loc(fs, i);
108 ext2fs_mark_block_bitmap2(bmap, b);
109 fb.corrupt_blocks++;
110
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;
115 }
116
117 /* Scan inodes */
118 fb.bmap = bmap;
119 fb.inode = &inode;
120 memset(&inode, 0, sizeof(inode));
121 retval = ext2fs_open_inode_scan(fs, 0, &scan);
122 if (retval)
123 goto out;
124
125 retval = ext2fs_get_next_inode_full(scan, &ino, &inode, sizeof(inode));
126 if (retval)
127 goto out2;
128 while (ino) {
129 if (inode.i_links_count == 0)
130 goto next_loop;
131
132 b = ext2fs_file_acl_block(fs, &inode);
133 if (b) {
134 ext2fs_mark_block_bitmap2(bmap, b);
135 fb.corrupt_blocks++;
136 }
137
138 /*
139 * Inline data, sockets, devices, and symlinks have
140 * no blocks to iterate.
141 */
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))
146 goto next_loop;
147 fb.ino = ino;
148 retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY,
149 NULL, find_block_helper, &fb);
150 if (retval)
151 goto out2;
152next_loop:
153 retval = ext2fs_get_next_inode_full(scan, &ino, &inode,
154 sizeof(inode));
155 if (retval)
156 goto out2;
157 }
158out2:
159 ext2fs_close_inode_scan(scan);
160out:
161 if (!retval)
162 *corrupt_bytes = fb.corrupt_blocks * fs->blocksize;
163 return retval;
164}
165
4e222d9b 166static uint64_t rand_num(uint64_t min, uint64_t max)
c4d5b81e
DW
167{
168 uint64_t x;
4e222d9b 169 unsigned int i;
c4d5b81e
DW
170 uint8_t *px = (uint8_t *)&x;
171
172 for (i = 0; i < sizeof(x); i++)
173 px[i] = random();
174
175 return min + (uint64_t)((double)(max - min) * (x / (UINT64_MAX + 1.0)));
176}
177
4e222d9b 178static int process_fs(const char *fsname)
c4d5b81e
DW
179{
180 errcode_t ret;
181 int flags, fd;
182 ext2_filsys fs = NULL;
183 ext2fs_block_bitmap corrupt_map;
184 off_t hsize, count, off, offset, corrupt_bytes;
185 unsigned char c;
4e222d9b 186 off_t i;
c4d5b81e
DW
187
188 /* If mounted rw, force dryrun mode */
189 ret = ext2fs_check_if_mounted(fsname, &flags);
190 if (ret) {
191 fprintf(stderr, "%s: failed to determine filesystem mount "
192 "state.\n", fsname);
193 return 1;
194 }
195
196 if (!dryrun && (flags & EXT2_MF_MOUNTED) &&
197 !(flags & EXT2_MF_READONLY)) {
198 fprintf(stderr, "%s: is mounted rw, performing dry run.\n",
199 fsname);
200 dryrun = 1;
201 }
202
203 /* Ensure the fs is clean and does not have errors */
204 ret = ext2fs_open(fsname, EXT2_FLAG_64BITS, 0, 0, unix_io_manager,
205 &fs);
206 if (ret) {
207 fprintf(stderr, "%s: failed to open filesystem.\n",
208 fsname);
209 return 1;
210 }
211
212 if ((fs->super->s_state & EXT2_ERROR_FS)) {
213 fprintf(stderr, "%s: errors detected, run fsck.\n",
214 fsname);
215 goto fail;
216 }
217
218 if (!dryrun && (fs->super->s_state & EXT2_VALID_FS) == 0) {
219 fprintf(stderr, "%s: unclean shutdown, performing dry run.\n",
220 fsname);
221 dryrun = 1;
222 }
223
224 /* Construct a bitmap of whatever we're corrupting */
225 if (!metadata_only) {
226 /* Load block bitmap */
227 ret = ext2fs_read_block_bitmap(fs);
228 if (ret) {
229 fprintf(stderr, "%s: error while reading block bitmap\n",
230 fsname);
231 goto fail;
232 }
233 corrupt_map = fs->block_map;
234 corrupt_bytes = (ext2fs_blocks_count(fs->super) -
235 ext2fs_free_blocks_count(fs->super)) *
236 fs->blocksize;
237 } else {
238 ret = ext2fs_allocate_block_bitmap(fs, "metadata block map",
239 &corrupt_map);
240 if (ret) {
241 fprintf(stderr, "%s: unable to create block bitmap\n",
242 fsname);
243 goto fail;
244 }
245
246 /* Iterate everything... */
247 ret = find_metadata_blocks(fs, corrupt_map, &corrupt_bytes);
248 if (ret) {
249 fprintf(stderr, "%s: while finding metadata\n",
250 fsname);
251 goto fail;
252 }
253 }
254
255 /* Run around corrupting things */
256 fd = open(fsname, O_RDWR);
257 if (fd < 0) {
258 perror(fsname);
259 goto fail;
260 }
261 srandom(getseed());
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;
267 else
268 count = rand_num(0, corrupt_bytes / 100);
269 offset = 4096; /* never corrupt superblock */
270 for (i = 0; i < count; i++) {
271 do
272 off = rand_num(offset, hsize);
273 while (!ext2fs_test_block_bitmap2(corrupt_map,
274 off / fs->blocksize));
275 c = rand() % 256;
276 if ((rand() % 2) && c < 128)
277 c |= 0x80;
278 if (verbose)
377e3a96
TT
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);
c4d5b81e
DW
282 if (dryrun)
283 continue;
7248e265
DW
284#ifdef HAVE_PWRITE64
285 if (pwrite64(fd, &c, sizeof(c), off) != sizeof(c)) {
286 perror(fsname);
287 goto fail3;
288 }
289#elif HAVE_PWRITE
0165cfad 290 if (pwrite(fd, &c, sizeof(c), off) != sizeof(c)) {
c4d5b81e
DW
291 perror(fsname);
292 goto fail3;
293 }
7248e265
DW
294#else
295 if (my_pwrite(fd, &c, sizeof(c), off) != sizeof(c)) {
296 perror(fsname);
297 goto fail3;
298 }
299#endif
c4d5b81e
DW
300 }
301 close(fd);
302
303 /* Clean up */
8375a881 304 ret = ext2fs_close_free(&fs);
c4d5b81e 305 if (ret) {
c4d5b81e
DW
306 fprintf(stderr, "%s: error while closing filesystem\n",
307 fsname);
8375a881 308 return 1;
c4d5b81e
DW
309 }
310
311 return 0;
312fail3:
313 close(fd);
c4d5b81e
DW
314 if (corrupt_map != fs->block_map)
315 ext2fs_free_block_bitmap(corrupt_map);
316fail:
8375a881 317 ext2fs_close_free(&fs);
c4d5b81e
DW
318 return 1;
319}
320
4e222d9b 321static void print_help(const char *progname)
c4d5b81e
DW
322{
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");
328 exit(0);
329}
330
331int main(int argc, char *argv[])
332{
333 int c;
334
335 while ((c = getopt(argc, argv, "b:dnv")) != -1) {
336 switch (c) {
337 case 'b':
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",
343 optarg);
344 return 1;
345 }
346 } else
347 user_corrupt_bytes = strtoull(optarg, NULL, 0);
348 if (errno) {
349 perror(optarg);
350 return 1;
351 }
352 break;
353 case 'd':
354 metadata_only = 0;
355 break;
356 case 'n':
357 dryrun = 1;
358 break;
359 case 'v':
360 verbose = 1;
361 break;
362 default:
363 print_help(argv[0]);
364 }
365 }
366
367 for (c = optind; c < argc; c++)
368 if (process_fs(argv[c]))
369 return 1;
370 return 0;
371}