Whamcloud - gitweb
e2fsck: map PROMPT_* values to prompt messages
[tools/e2fsprogs.git] / misc / e2fuzz.c
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>
21 #include <unistd.h>
22 #ifdef HAVE_GETOPT_H
23 #include <getopt.h>
24 #endif
25
26 #include "ext2fs/ext2_fs.h"
27 #include "ext2fs/ext2fs.h"
28
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;
34
35 #if !defined HAVE_PWRITE64 && !defined HAVE_PWRITE
36 static ssize_t my_pwrite(int fd, const void *buf, size_t count,
37                          ext2_loff_t offset)
38 {
39         if (ext2fs_llseek(fd, offset, SEEK_SET) < 0)
40                 return 0;
41
42         return write(fd, buf, count);
43 }
44 #endif /* !defined HAVE_PWRITE64 && !defined HAVE_PWRITE */
45
46 static int getseed(void)
47 {
48         int r;
49         int fd;
50
51         fd = open("/dev/urandom", O_RDONLY);
52         if (fd < 0) {
53                 perror("open");
54                 exit(0);
55         }
56         if (read(fd, &r, sizeof(r)) != sizeof(r))
57                 printf("Unable to read random seed!\n");
58         close(fd);
59         return r;
60 }
61
62 struct find_block {
63         ext2_ino_t ino;
64         ext2fs_block_bitmap bmap;
65         struct ext2_inode *inode;
66         blk64_t corrupt_blocks;
67 };
68
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)),
73                              void *priv_data)
74 {
75         struct find_block *fb = (struct find_block *)priv_data;
76
77         if (S_ISDIR(fb->inode->i_mode) || !metadata_only || blockcnt < 0) {
78                 ext2fs_mark_block_bitmap2(fb->bmap, *blocknr);
79                 fb->corrupt_blocks++;
80         }
81
82         return 0;
83 }
84
85 static errcode_t find_metadata_blocks(ext2_filsys fs, ext2fs_block_bitmap bmap,
86                                       ext2_loff_t *corrupt_bytes)
87 {
88         dgrp_t i;
89         blk64_t b, c;
90         ext2_inode_scan scan;
91         ext2_ino_t ino;
92         struct ext2_inode inode;
93         struct find_block fb;
94         errcode_t retval;
95
96         *corrupt_bytes = 0;
97         fb.corrupt_blocks = 0;
98
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);
102
103                 /* bitmaps and inode table */
104                 b = ext2fs_block_bitmap_loc(fs, i);
105                 ext2fs_mark_block_bitmap2(bmap, b);
106                 fb.corrupt_blocks++;
107
108                 b = ext2fs_inode_bitmap_loc(fs, i);
109                 ext2fs_mark_block_bitmap2(bmap, b);
110                 fb.corrupt_blocks++;
111
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;
116         }
117
118         /* Scan inodes */
119         fb.bmap = bmap;
120         fb.inode = &inode;
121         memset(&inode, 0, sizeof(inode));
122         retval = ext2fs_open_inode_scan(fs, 0, &scan);
123         if (retval)
124                 goto out;
125
126         retval = ext2fs_get_next_inode_full(scan, &ino, &inode, sizeof(inode));
127         if (retval)
128                 goto out2;
129         while (ino) {
130                 if (inode.i_links_count == 0)
131                         goto next_loop;
132
133                 b = ext2fs_file_acl_block(fs, &inode);
134                 if (b) {
135                         ext2fs_mark_block_bitmap2(bmap, b);
136                         fb.corrupt_blocks++;
137                 }
138
139                 /*
140                  * Inline data, sockets, devices, and symlinks have
141                  * no blocks to iterate.
142                  */
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))
147                         goto next_loop;
148                 fb.ino = ino;
149                 retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY,
150                                                NULL, find_block_helper, &fb);
151                 if (retval)
152                         goto out2;
153 next_loop:
154                 retval = ext2fs_get_next_inode_full(scan, &ino, &inode,
155                                                     sizeof(inode));
156                 if (retval)
157                         goto out2;
158         }
159 out2:
160         ext2fs_close_inode_scan(scan);
161 out:
162         if (!retval)
163                 *corrupt_bytes = fb.corrupt_blocks * fs->blocksize;
164         return retval;
165 }
166
167 static uint64_t rand_num(uint64_t min, uint64_t max)
168 {
169         uint64_t x;
170         unsigned int i;
171         uint8_t *px = (uint8_t *)&x;
172
173         for (i = 0; i < sizeof(x); i++)
174                 px[i] = random();
175
176         return min + (uint64_t)((double)(max - min) *
177                                 (x / ((double) UINT64_MAX + 1.0)));
178 }
179
180 static int process_fs(const char *fsname)
181 {
182         errcode_t ret;
183         int flags, fd;
184         ext2_filsys fs = NULL;
185         ext2fs_block_bitmap corrupt_map;
186         ext2_loff_t hsize, count, off, offset, corrupt_bytes, i;
187         unsigned char c;
188
189         /* If mounted rw, force dryrun mode */
190         ret = ext2fs_check_if_mounted(fsname, &flags);
191         if (ret) {
192                 fprintf(stderr, "%s: failed to determine filesystem mount "
193                         "state.\n", fsname);
194                 return 1;
195         }
196
197         if (!dryrun && (flags & EXT2_MF_MOUNTED) &&
198             !(flags & EXT2_MF_READONLY)) {
199                 fprintf(stderr, "%s: is mounted rw, performing dry run.\n",
200                         fsname);
201                 dryrun = 1;
202         }
203
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);
207         if (ret) {
208                 fprintf(stderr, "%s: failed to open filesystem.\n",
209                         fsname);
210                 return 1;
211         }
212
213         if ((fs->super->s_state & EXT2_ERROR_FS)) {
214                 fprintf(stderr, "%s: errors detected, run fsck.\n",
215                         fsname);
216                 goto fail;
217         }
218
219         if (!dryrun && (fs->super->s_state & EXT2_VALID_FS) == 0) {
220                 fprintf(stderr, "%s: unclean shutdown, performing dry run.\n",
221                         fsname);
222                 dryrun = 1;
223         }
224
225         /* Construct a bitmap of whatever we're corrupting */
226         if (!metadata_only) {
227                 /* Load block bitmap */
228                 ret = ext2fs_read_block_bitmap(fs);
229                 if (ret) {
230                         fprintf(stderr, "%s: error while reading block bitmap\n",
231                                 fsname);
232                         goto fail;
233                 }
234                 corrupt_map = fs->block_map;
235                 corrupt_bytes = (ext2fs_blocks_count(fs->super) -
236                                  ext2fs_free_blocks_count(fs->super)) *
237                                 fs->blocksize;
238         } else {
239                 ret = ext2fs_allocate_block_bitmap(fs, "metadata block map",
240                                                    &corrupt_map);
241                 if (ret) {
242                         fprintf(stderr, "%s: unable to create block bitmap\n",
243                                 fsname);
244                         goto fail;
245                 }
246
247                 /* Iterate everything... */
248                 ret = find_metadata_blocks(fs, corrupt_map, &corrupt_bytes);
249                 if (ret) {
250                         fprintf(stderr, "%s: while finding metadata\n",
251                                 fsname);
252                         goto fail;
253                 }
254         }
255
256         /* Run around corrupting things */
257         fd = open(fsname, O_RDWR);
258         if (fd < 0) {
259                 perror(fsname);
260                 goto fail;
261         }
262         srandom(getseed());
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;
268         else
269                 count = rand_num(0, corrupt_bytes / 100);
270         offset = 4096; /* never corrupt superblock */
271         for (i = 0; i < count; i++) {
272                 do
273                         off = rand_num(offset, hsize);
274                 while (!ext2fs_test_block_bitmap2(corrupt_map,
275                                                     off / fs->blocksize));
276                 c = rand() % 256;
277                 if ((rand() % 2) && c < 128)
278                         c |= 0x80;
279                 if (verbose)
280                         printf("Corrupting byte %lld in block %lld to 0x%x\n",
281                                off % fs->blocksize,
282                                off / fs->blocksize, c);
283                 if (dryrun)
284                         continue;
285 #ifdef HAVE_PWRITE64
286                 if (pwrite64(fd, &c, sizeof(c), off) != sizeof(c)) {
287                         perror(fsname);
288                         goto fail3;
289                 }
290 #elif HAVE_PWRITE
291                 if (pwrite(fd, &c, sizeof(c), off) != sizeof(c)) {
292                         perror(fsname);
293                         goto fail3;
294                 }
295 #else
296                 if (my_pwrite(fd, &c, sizeof(c), off) != sizeof(c)) {
297                         perror(fsname);
298                         goto fail3;
299                 }
300 #endif
301         }
302         close(fd);
303
304         /* Clean up */
305         ret = ext2fs_close_free(&fs);
306         if (ret) {
307                 fprintf(stderr, "%s: error while closing filesystem\n",
308                         fsname);
309                 return 1;
310         }
311
312         return 0;
313 fail3:
314         close(fd);
315         if (corrupt_map != fs->block_map)
316                 ext2fs_free_block_bitmap(corrupt_map);
317 fail:
318         ext2fs_close_free(&fs);
319         return 1;
320 }
321
322 static void print_help(const char *progname)
323 {
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");
329         exit(0);
330 }
331
332 int main(int argc, char *argv[])
333 {
334         int c;
335
336         while ((c = getopt(argc, argv, "b:dnv")) != -1) {
337                 switch (c) {
338                 case 'b':
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",
344                                                 optarg);
345                                         return 1;
346                                 }
347                         } else
348                                 user_corrupt_bytes = strtoull(optarg, NULL, 0);
349                         if (errno) {
350                                 perror(optarg);
351                                 return 1;
352                         }
353                         break;
354                 case 'd':
355                         metadata_only = 0;
356                         break;
357                 case 'n':
358                         dryrun = 1;
359                         break;
360                 case 'v':
361                         verbose = 1;
362                         break;
363                 default:
364                         print_help(argv[0]);
365                 }
366         }
367
368         for (c = optind; c < argc; c++)
369                 if (process_fs(argv[c]))
370                         return 1;
371         return 0;
372 }