Whamcloud - gitweb
debian: drop libattr1-dev from the build dependencies list
[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 static 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 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)),
72                              void *priv_data)
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
84 static errcode_t find_metadata_blocks(ext2_filsys fs, ext2fs_block_bitmap bmap,
85                                       off_t *corrupt_bytes)
86 {
87         dgrp_t i;
88         blk64_t b, c;
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;
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;
152 next_loop:
153                 retval = ext2fs_get_next_inode_full(scan, &ino, &inode,
154                                                     sizeof(inode));
155                 if (retval)
156                         goto out2;
157         }
158 out2:
159         ext2fs_close_inode_scan(scan);
160 out:
161         if (!retval)
162                 *corrupt_bytes = fb.corrupt_blocks * fs->blocksize;
163         return retval;
164 }
165
166 static uint64_t rand_num(uint64_t min, uint64_t max)
167 {
168         uint64_t x;
169         unsigned int i;
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
178 static int process_fs(const char *fsname)
179 {
180         errcode_t ret;
181         int flags, fd;
182         ext2_filsys fs = NULL;
183         ext2fs_block_bitmap corrupt_map;
184         loff_t hsize, count, off, offset, corrupt_bytes;
185         unsigned char c;
186         loff_t i;
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)
279                         printf("Corrupting byte %lld in block %lld to 0x%x\n",
280                                off % fs->blocksize,
281                                off / fs->blocksize, c);
282                 if (dryrun)
283                         continue;
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
290                 if (pwrite(fd, &c, sizeof(c), off) != sizeof(c)) {
291                         perror(fsname);
292                         goto fail3;
293                 }
294 #else
295                 if (my_pwrite(fd, &c, sizeof(c), off) != sizeof(c)) {
296                         perror(fsname);
297                         goto fail3;
298                 }
299 #endif
300         }
301         close(fd);
302
303         /* Clean up */
304         ret = ext2fs_close_free(&fs);
305         if (ret) {
306                 fprintf(stderr, "%s: error while closing filesystem\n",
307                         fsname);
308                 return 1;
309         }
310
311         return 0;
312 fail3:
313         close(fd);
314         if (corrupt_map != fs->block_map)
315                 ext2fs_free_block_bitmap(corrupt_map);
316 fail:
317         ext2fs_close_free(&fs);
318         return 1;
319 }
320
321 static void print_help(const char *progname)
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
331 int 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 }