summaryrefslogtreecommitdiffstats
path: root/e2fsck/badblocks.c
blob: fec5f10dcec3d7856bd216690c9d24e389434bac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/*
 * badblocks.c --- replace/append bad blocks to the bad block inode
 *
 * Copyright (C) 1993, 1994 Theodore Ts'o.  This file may be
 * redistributed under the terms of the GNU Public License.
 */

#include "config.h"
#include <time.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif

#include <et/com_err.h>
#include "e2fsck.h"

static int check_bb_inode_blocks(ext2_filsys fs, blk_t *block_nr, int blockcnt,
				 void *priv_data);


static void invalid_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk_t blk)
{
	printf(_("Bad block %u out of range; ignored.\n"), blk);
	return;
}

void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file,
			  int replace_bad_blocks)
{
	ext2_filsys fs = ctx->fs;
	errcode_t	retval;
	badblocks_list	bb_list = 0;
	FILE		*f;
	char		buf[1024];

	e2fsck_read_bitmaps(ctx);

	/*
	 * Make sure the bad block inode is sane.  If there are any
	 * illegal blocks, clear them.
	 */
	retval = ext2fs_block_iterate(fs, EXT2_BAD_INO, 0, 0,
				      check_bb_inode_blocks, 0);
	if (retval) {
		com_err("ext2fs_block_iterate", retval, "%s",
			_("while sanity checking the bad blocks inode"));
		goto fatal;
	}

	/*
	 * If we're appending to the bad blocks inode, read in the
	 * current bad blocks.
	 */
	if (!replace_bad_blocks) {
		retval = ext2fs_read_bb_inode(fs, &bb_list);
		if (retval) {
			com_err("ext2fs_read_bb_inode", retval, "%s",
				_("while reading the bad blocks inode"));
			goto fatal;
		}
	}

	/*
	 * Now read in the bad blocks from the file; if
	 * bad_blocks_file is null, then try to run the badblocks
	 * command.
	 */
	if (bad_blocks_file) {
		f = fopen(bad_blocks_file, "r");
		if (!f) {
			com_err("read_bad_blocks_file", errno,
				_("while trying to open %s"), bad_blocks_file);
			goto fatal;
		}
	} else {
		sprintf(buf, "badblocks -b %d -X %s%s%s %llu", fs->blocksize,
			(ctx->options & E2F_OPT_PREEN) ? "" : "-s ",
			(ctx->options & E2F_OPT_WRITECHECK) ? "-n " : "",
			fs->device_name,
			(unsigned long long) ext2fs_blocks_count(fs->super)-1);
		f = popen(buf, "r");
		if (!f) {
			com_err("read_bad_blocks_file", errno,
				_("while trying popen '%s'"), buf);
			goto fatal;
		}
	}
	retval = ext2fs_read_bb_FILE(fs, f, &bb_list, invalid_block);
	if (bad_blocks_file)
		fclose(f);
	else
		pclose(f);
	if (retval) {
		com_err("ext2fs_read_bb_FILE", retval, "%s",
			_("while reading in list of bad blocks from file"));
		goto fatal;
	}

	/*
	 * Finally, update the bad blocks from the bad_block_map
	 */
	printf("%s: Updating bad block inode.\n", ctx->device_name);
	retval = ext2fs_update_bb_inode(fs, bb_list);
	if (retval) {
		com_err("ext2fs_update_bb_inode", retval, "%s",
			_("while updating bad block inode"));
		goto fatal;
	}

	ext2fs_badblocks_list_free(bb_list);
	return;

fatal:
	ctx->flags |= E2F_FLAG_ABORT;
	if (bb_list)
		ext2fs_badblocks_list_free(bb_list);
	return;

}

static int check_bb_inode_blocks(ext2_filsys fs,
				 blk_t *block_nr,
				 int blockcnt EXT2FS_ATTR((unused)),
				 void *priv_data EXT2FS_ATTR((unused)))
{
	if (!*block_nr)
		return 0;

	/*
	 * If the block number is outrageous, clear it and ignore it.
	 */
	if (*block_nr >= ext2fs_blocks_count(fs->super) ||
	    *block_nr < fs->super->s_first_data_block) {
		printf(_("Warning: illegal block %u found in bad block inode.  "
			 "Cleared.\n"), *block_nr);
		*block_nr = 0;
		return BLOCK_CHANGED;
	}

	return 0;
}