Whamcloud - gitweb
misc: remove broken whole device check
[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, off_t offset)
37 {
38         if (lseek(fd, offset, SEEK_SET) < 0)
39                 return 0;
40
41         return write(fd, buf, count);
42 }
43 #endif /* !defined HAVE_PWRITE64 && !defined HAVE_PWRITE */
44
45 int 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         }
55         if (read(fd, &r, sizeof(r)) != sizeof(r))
56                 printf("Unable to read random seed!\n");
57         close(fd);
58         return r;
59 }
60
61 struct find_block {
62         ext2_ino_t ino;
63         ext2fs_block_bitmap bmap;
64         struct ext2_inode *inode;
65         blk64_t corrupt_blocks;
66 };
67
68 int 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
81 errcode_t find_metadata_blocks(ext2_filsys fs, ext2fs_block_bitmap bmap,
82                                off_t *corrupt_bytes)
83 {
84         dgrp_t i;
85         blk64_t b, c;
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;
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;
149 next_loop:
150                 retval = ext2fs_get_next_inode_full(scan, &ino, &inode,
151                                                     sizeof(inode));
152                 if (retval)
153                         goto out2;
154         }
155 out2:
156         ext2fs_close_inode_scan(scan);
157 out:
158         if (!retval)
159                 *corrupt_bytes = fb.corrupt_blocks * fs->blocksize;
160         return retval;
161 }
162
163 uint64_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
175 int 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)
276                         printf("Corrupting byte %zu in block %zu to 0x%x\n",
277                                off % fs->blocksize, off / fs->blocksize, c);
278                 if (dryrun)
279                         continue;
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
286                 if (pwrite(fd, &c, sizeof(c), off) != sizeof(c)) {
287                         perror(fsname);
288                         goto fail3;
289                 }
290 #else
291                 if (my_pwrite(fd, &c, sizeof(c), off) != sizeof(c)) {
292                         perror(fsname);
293                         goto fail3;
294                 }
295 #endif
296         }
297         close(fd);
298
299         /* Clean up */
300         ret = ext2fs_close_free(&fs);
301         if (ret) {
302                 fprintf(stderr, "%s: error while closing filesystem\n",
303                         fsname);
304                 return 1;
305         }
306
307         return 0;
308 fail3:
309         close(fd);
310         if (corrupt_map != fs->block_map)
311                 ext2fs_free_block_bitmap(corrupt_map);
312 fail:
313         ext2fs_close_free(&fs);
314         return 1;
315 }
316
317 void 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
327 int 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 }