mirror of
https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git
synced 2024-11-24 02:25:03 +08:00
Merge branch 'master' into next
Conflicts: e2fsck/e2fsck.h e2fsck/unix.c
This commit is contained in:
commit
25623feab4
@ -193,6 +193,20 @@ or repairs.
|
||||
.BI fragcheck
|
||||
During pass 1, print a detailed report of any discontiguous blocks for
|
||||
files in the filesystem.
|
||||
.TP
|
||||
.BI discard
|
||||
Attempt to discard free blocks and unused inode blocks after the full
|
||||
filesystem check (discarding blocks is useful on solid state devices and sparse
|
||||
/ thin-provisioned storage). Note that discard is done in pass 5 AFTER the
|
||||
filesystem has been fully checked and only if it does not contain recognizable
|
||||
errors. However there might be cases where
|
||||
.B e2fsck
|
||||
does not fully recognise a problem and hence in this case this
|
||||
option may prevent you from further manual data recovery.
|
||||
.TP
|
||||
.BI nodiscard
|
||||
Do not attempt to discard free blocks and unused inode blocks. This option is
|
||||
exacly the opposite of discard option. This is set as default.
|
||||
.RE
|
||||
.TP
|
||||
.B \-f
|
||||
|
@ -156,6 +156,7 @@ struct resource_track {
|
||||
#define E2F_OPT_COMPRESS_DIRS 0x0400
|
||||
#define E2F_OPT_FRAGCHECK 0x0800
|
||||
#define E2F_OPT_JOURNAL_ONLY 0x1000 /* only replay the journal */
|
||||
#define E2F_OPT_DISCARD 0x2000
|
||||
|
||||
/*
|
||||
* E2fsck flags
|
||||
|
@ -10,9 +10,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "e2fsck.h"
|
||||
#include "problem.h"
|
||||
|
||||
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
|
||||
|
||||
static void check_block_bitmaps(e2fsck_t ctx);
|
||||
static void check_inode_bitmaps(e2fsck_t ctx);
|
||||
static void check_inode_end(e2fsck_t ctx);
|
||||
@ -64,6 +73,26 @@ void e2fsck_pass5(e2fsck_t ctx)
|
||||
print_resource_track(ctx, _("Pass 5"), &rtrack, ctx->fs->io);
|
||||
}
|
||||
|
||||
static void e2fsck_discard_blocks(e2fsck_t ctx, io_manager manager,
|
||||
blk64_t start, blk64_t count)
|
||||
{
|
||||
ext2_filsys fs = ctx->fs;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* If the filesystem has changed it means that there was an corruption
|
||||
* which should be repaired, but in some cases just one e2fsck run is
|
||||
* not enough to fix the problem, hence it is not safe to run discard
|
||||
* in this case.
|
||||
*/
|
||||
if (ext2fs_test_changed(ctx->fs))
|
||||
ctx->options &= ~E2F_OPT_DISCARD;
|
||||
|
||||
if ((ctx->options & E2F_OPT_DISCARD) &&
|
||||
(io_channel_discard(fs->io, start, count)))
|
||||
ctx->options &= ~E2F_OPT_DISCARD;
|
||||
}
|
||||
|
||||
#define NO_BLK ((blk64_t) -1)
|
||||
|
||||
static void print_bitmap_problem(e2fsck_t ctx, int problem,
|
||||
@ -108,6 +137,7 @@ static void check_block_bitmaps(e2fsck_t ctx)
|
||||
int group = 0;
|
||||
int blocks = 0;
|
||||
blk64_t free_blocks = 0;
|
||||
blk64_t first_free = ext2fs_blocks_count(fs->super);
|
||||
int group_free = 0;
|
||||
int actual, bitmap;
|
||||
struct problem_context pctx;
|
||||
@ -120,6 +150,7 @@ static void check_block_bitmaps(e2fsck_t ctx)
|
||||
int cmp_block = 0;
|
||||
int redo_flag = 0;
|
||||
blk64_t super_blk, old_desc_blk, new_desc_blk;
|
||||
io_manager manager = ctx->fs->io->manager;
|
||||
|
||||
clear_problem_context(&pctx);
|
||||
free_array = (int *) e2fsck_allocate_memory(ctx,
|
||||
@ -281,10 +312,25 @@ redo_counts:
|
||||
ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
|
||||
had_problem++;
|
||||
|
||||
/*
|
||||
* If there a problem we should turn off the discard so we
|
||||
* do not compromise the filesystem.
|
||||
*/
|
||||
ctx->options &= ~E2F_OPT_DISCARD;
|
||||
|
||||
do_counts:
|
||||
if (!bitmap && (!skip_group || csum_flag)) {
|
||||
group_free++;
|
||||
free_blocks++;
|
||||
if (first_free > i)
|
||||
first_free = i;
|
||||
} else {
|
||||
if ((i > first_free) &&
|
||||
(ctx->options & E2F_OPT_DISCARD)) {
|
||||
e2fsck_discard_blocks(ctx, manager, first_free,
|
||||
(i - first_free));
|
||||
}
|
||||
first_free = ext2fs_blocks_count(fs->super);
|
||||
}
|
||||
blocks ++;
|
||||
if ((blocks == fs->super->s_blocks_per_group) ||
|
||||
@ -381,6 +427,7 @@ static void check_inode_bitmaps(e2fsck_t ctx)
|
||||
int csum_flag;
|
||||
int skip_group = 0;
|
||||
int redo_flag = 0;
|
||||
io_manager manager = ctx->fs->io->manager;
|
||||
|
||||
clear_problem_context(&pctx);
|
||||
free_array = (int *) e2fsck_allocate_memory(ctx,
|
||||
@ -500,6 +547,11 @@ redo_counts:
|
||||
}
|
||||
ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
|
||||
had_problem++;
|
||||
/*
|
||||
* If there a problem we should turn off the discard so we
|
||||
* do not compromise the filesystem.
|
||||
*/
|
||||
ctx->options &= ~E2F_OPT_DISCARD;
|
||||
|
||||
do_counts:
|
||||
if (bitmap) {
|
||||
@ -509,11 +561,43 @@ do_counts:
|
||||
group_free++;
|
||||
free_inodes++;
|
||||
}
|
||||
|
||||
inodes++;
|
||||
if ((inodes == fs->super->s_inodes_per_group) ||
|
||||
(i == fs->super->s_inodes_count)) {
|
||||
|
||||
free_array[group] = group_free;
|
||||
dir_array[group] = dirs_count;
|
||||
|
||||
/* Discard inode table */
|
||||
if (ctx->options & E2F_OPT_DISCARD) {
|
||||
blk64_t used_blks, blk, num;
|
||||
|
||||
used_blks = DIV_ROUND_UP(
|
||||
(EXT2_INODES_PER_GROUP(fs->super) -
|
||||
group_free),
|
||||
EXT2_INODES_PER_BLOCK(fs->super));
|
||||
|
||||
blk = ext2fs_inode_table_loc(fs, group) +
|
||||
used_blks;
|
||||
num = fs->inode_blocks_per_group -
|
||||
used_blks;
|
||||
e2fsck_discard_blocks(ctx, manager, blk, num);
|
||||
}
|
||||
|
||||
/*
|
||||
* If discard zeroes data and the group inode table
|
||||
* was not zeroed yet, set itable as zeroed
|
||||
*/
|
||||
if ((ctx->options & E2F_OPT_DISCARD) &&
|
||||
(io_channel_discard_zeroes_data(fs->io)) &&
|
||||
!(ext2fs_bg_flags_test(fs, group,
|
||||
EXT2_BG_INODE_ZEROED))) {
|
||||
ext2fs_bg_flags_set(fs, group,
|
||||
EXT2_BG_INODE_ZEROED);
|
||||
ext2fs_group_desc_csum_set(fs, group);
|
||||
}
|
||||
|
||||
group ++;
|
||||
inodes = 0;
|
||||
skip_group = 0;
|
||||
|
@ -604,6 +604,12 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
|
||||
continue;
|
||||
}
|
||||
ctx->options |= E2F_OPT_JOURNAL_ONLY;
|
||||
} else if (strcmp(token, "discard") == 0) {
|
||||
ctx->options |= E2F_OPT_DISCARD;
|
||||
continue;
|
||||
} else if (strcmp(token, "nodiscard") == 0) {
|
||||
ctx->options &= ~E2F_OPT_DISCARD;
|
||||
continue;
|
||||
} else {
|
||||
fprintf(stderr, _("Unknown extended option: %s\n"),
|
||||
token);
|
||||
@ -620,6 +626,8 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
|
||||
fputs(("\tea_ver=<ea_version (1 or 2)>\n"), stderr);
|
||||
fputs(("\tfragcheck\n"), stderr);
|
||||
fputs(("\tjournal_only\n"), stderr);
|
||||
fputs(("\tdiscard\n"), stderr);
|
||||
fputs(("\tnodiscard\n"), stderr);
|
||||
fputc('\n', stderr);
|
||||
exit(1);
|
||||
}
|
||||
@ -678,6 +686,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
|
||||
ctx->program_name = *argv;
|
||||
else
|
||||
ctx->program_name = "e2fsck";
|
||||
|
||||
while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
|
||||
switch (c) {
|
||||
case 'C':
|
||||
@ -961,7 +970,6 @@ static errcode_t try_open_fs(e2fsck_t ctx, int flags, io_manager io_ptr,
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
static const char *my_ver_string = E2FSPROGS_VERSION;
|
||||
static const char *my_ver_date = E2FSPROGS_DATE;
|
||||
|
||||
|
@ -49,7 +49,7 @@ blk64_t ext2fs_inode_data_blocks2(ext2_filsys fs,
|
||||
struct ext2_inode *inode)
|
||||
{
|
||||
return (inode->i_blocks |
|
||||
((fs->super->s_feature_incompat &
|
||||
((fs->super->s_feature_ro_compat &
|
||||
EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ?
|
||||
(__u64) inode->osd2.linux2.l_i_blocks_hi << 32 : 0)) -
|
||||
(inode->i_file_acl ? fs->blocksize >> 9 : 0);
|
||||
@ -62,7 +62,7 @@ blk64_t ext2fs_inode_i_blocks(ext2_filsys fs,
|
||||
struct ext2_inode *inode)
|
||||
{
|
||||
return (inode->i_blocks |
|
||||
((fs->super->s_feature_incompat &
|
||||
((fs->super->s_feature_ro_compat &
|
||||
EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ?
|
||||
(__u64)inode->osd2.linux2.l_i_blocks_hi << 32 : 0));
|
||||
}
|
||||
|
@ -29,6 +29,9 @@ typedef struct struct_io_channel *io_channel;
|
||||
typedef struct struct_io_stats *io_stats;
|
||||
|
||||
#define CHANNEL_FLAGS_WRITETHROUGH 0x01
|
||||
#define CHANNEL_FLAGS_DISCARD_ZEROES 0x02
|
||||
|
||||
#define io_channel_discard_zeroes_data(i) (i->flags & CHANNEL_FLAGS_DISCARD_ZEROES)
|
||||
|
||||
struct struct_io_channel {
|
||||
errcode_t magic;
|
||||
@ -83,6 +86,8 @@ struct struct_io_manager {
|
||||
int count, void *data);
|
||||
errcode_t (*write_blk64)(io_channel channel, unsigned long long block,
|
||||
int count, const void *data);
|
||||
errcode_t (*discard)(io_channel channel, unsigned long long block,
|
||||
unsigned long long count);
|
||||
long reserved[16];
|
||||
};
|
||||
|
||||
|
@ -99,3 +99,14 @@ errcode_t io_channel_write_blk64(io_channel channel, unsigned long long block,
|
||||
return (channel->manager->write_blk)(channel, (unsigned long) block,
|
||||
count, data);
|
||||
}
|
||||
|
||||
errcode_t io_channel_discard(io_channel channel, unsigned long long block,
|
||||
unsigned long long count)
|
||||
{
|
||||
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
|
||||
|
||||
if (channel->manager->discard)
|
||||
return (channel->manager->discard)(channel, block, count);
|
||||
|
||||
return EXT2_ET_UNIMPLEMENTED;
|
||||
}
|
||||
|
@ -73,7 +73,8 @@ static errcode_t test_write_byte(io_channel channel, unsigned long offset,
|
||||
static errcode_t test_set_option(io_channel channel, const char *option,
|
||||
const char *arg);
|
||||
static errcode_t test_get_stats(io_channel channel, io_stats *stats);
|
||||
|
||||
static errcode_t test_discard(io_channel channel, unsigned long long block,
|
||||
unsigned long long count);
|
||||
|
||||
static struct struct_io_manager struct_test_manager = {
|
||||
EXT2_ET_MAGIC_IO_MANAGER,
|
||||
@ -89,6 +90,7 @@ static struct struct_io_manager struct_test_manager = {
|
||||
test_get_stats,
|
||||
test_read_blk64,
|
||||
test_write_blk64,
|
||||
test_discard,
|
||||
};
|
||||
|
||||
io_manager test_io_manager = &struct_test_manager;
|
||||
@ -120,6 +122,7 @@ void (*test_io_cb_write_byte)
|
||||
#define TEST_FLAG_FLUSH 0x08
|
||||
#define TEST_FLAG_DUMP 0x10
|
||||
#define TEST_FLAG_SET_OPTION 0x20
|
||||
#define TEST_FLAG_DISCARD 0x40
|
||||
|
||||
static void test_dump_block(io_channel channel,
|
||||
struct test_private_data *data,
|
||||
@ -495,3 +498,22 @@ static errcode_t test_get_stats(io_channel channel, io_stats *stats)
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static errcode_t test_discard(io_channel channel, unsigned long long block,
|
||||
unsigned long long count)
|
||||
{
|
||||
struct test_private_data *data;
|
||||
errcode_t retval = 0;
|
||||
|
||||
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
|
||||
data = (struct test_private_data *) channel->private_data;
|
||||
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
|
||||
|
||||
if (data->real)
|
||||
retval = io_channel_discard(data->real, block, count);
|
||||
if (data->flags & TEST_FLAG_DISCARD)
|
||||
fprintf(data->outfile,
|
||||
"Test_io: discard(%llu, %llu) returned %s\n",
|
||||
block, count, retval ? error_message(retval) : "OK");
|
||||
return retval;
|
||||
}
|
||||
|
@ -115,6 +115,8 @@ static errcode_t unix_read_blk64(io_channel channel, unsigned long long block,
|
||||
int count, void *data);
|
||||
static errcode_t unix_write_blk64(io_channel channel, unsigned long long block,
|
||||
int count, const void *data);
|
||||
static errcode_t unix_discard(io_channel channel, unsigned long long block,
|
||||
unsigned long long count);
|
||||
|
||||
static struct struct_io_manager struct_unix_manager = {
|
||||
EXT2_ET_MAGIC_IO_MANAGER,
|
||||
@ -130,6 +132,7 @@ static struct struct_io_manager struct_unix_manager = {
|
||||
unix_get_stats,
|
||||
unix_read_blk64,
|
||||
unix_write_blk64,
|
||||
unix_discard,
|
||||
};
|
||||
|
||||
io_manager unix_io_manager = &struct_unix_manager;
|
||||
@ -422,12 +425,18 @@ static errcode_t flush_cached_blocks(io_channel channel,
|
||||
}
|
||||
#endif /* NO_IO_CACHE */
|
||||
|
||||
#ifdef __linux__
|
||||
#ifndef BLKDISCARDZEROES
|
||||
#define BLKDISCARDZEROES _IO(0x12,124)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static errcode_t unix_open(const char *name, int flags, io_channel *channel)
|
||||
{
|
||||
io_channel io = NULL;
|
||||
struct unix_private_data *data = NULL;
|
||||
errcode_t retval;
|
||||
int open_flags;
|
||||
int open_flags, zeroes = 0;
|
||||
struct stat st;
|
||||
#ifdef __linux__
|
||||
struct utsname ut;
|
||||
@ -484,6 +493,12 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef BLKDISCARDZEROES
|
||||
ioctl(data->dev, BLKDISCARDZEROES, &zeroes);
|
||||
if (zeroes)
|
||||
io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
|
||||
#endif
|
||||
|
||||
#if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
/*
|
||||
* Some operating systems require that the buffers be aligned,
|
||||
@ -834,3 +849,31 @@ static errcode_t unix_set_option(io_channel channel, const char *option,
|
||||
}
|
||||
return EXT2_ET_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
#if defined(__linux__) && !defined(BLKDISCARD)
|
||||
#define BLKDISCARD _IO(0x12,119)
|
||||
#endif
|
||||
|
||||
static errcode_t unix_discard(io_channel channel, unsigned long long block,
|
||||
unsigned long long count)
|
||||
{
|
||||
#ifdef BLKDISCARD
|
||||
struct unix_private_data *data;
|
||||
__uint64_t range[2];
|
||||
int ret;
|
||||
|
||||
EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
|
||||
data = (struct unix_private_data *) channel->private_data;
|
||||
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
|
||||
|
||||
range[0] = (__uint64_t)(block) * channel->block_size;
|
||||
range[1] = (__uint64_t)(count) * channel->block_size;
|
||||
|
||||
ret = ioctl(data->dev, BLKDISCARD, &range);
|
||||
if (ret < 0)
|
||||
return errno;
|
||||
return 0;
|
||||
#else
|
||||
return EXT2_ET_UNIMPLEMENTED;
|
||||
#endif
|
||||
}
|
||||
|
@ -1960,69 +1960,6 @@ static int mke2fs_setup_tdb(const char *name, io_manager *io_ptr)
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#ifndef BLKDISCARD
|
||||
#define BLKDISCARD _IO(0x12,119)
|
||||
#endif
|
||||
|
||||
#ifndef BLKDISCARDZEROES
|
||||
#define BLKDISCARDZEROES _IO(0x12,124)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Return zero if the discard succeeds, and -1 if the discard fails.
|
||||
*/
|
||||
static int mke2fs_discard_blocks(ext2_filsys fs)
|
||||
{
|
||||
int fd;
|
||||
int ret;
|
||||
int blocksize;
|
||||
__u64 blocks;
|
||||
__uint64_t range[2];
|
||||
|
||||
blocks = ext2fs_blocks_count(fs->super);
|
||||
blocksize = EXT2_BLOCK_SIZE(fs->super);
|
||||
range[0] = 0;
|
||||
range[1] = blocks * blocksize;
|
||||
|
||||
fd = open64(fs->device_name, O_RDWR);
|
||||
|
||||
if (fd > 0) {
|
||||
ret = ioctl(fd, BLKDISCARD, &range);
|
||||
if (verbose) {
|
||||
printf(_("Calling BLKDISCARD from %llu to %llu "),
|
||||
(unsigned long long) range[0],
|
||||
(unsigned long long) range[1]);
|
||||
if (ret)
|
||||
printf(_("failed.\n"));
|
||||
else
|
||||
printf(_("succeeded.\n"));
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mke2fs_discard_zeroes_data(ext2_filsys fs)
|
||||
{
|
||||
int fd;
|
||||
int ret;
|
||||
int discard_zeroes_data = 0;
|
||||
|
||||
fd = open64(fs->device_name, O_RDWR);
|
||||
|
||||
if (fd > 0) {
|
||||
ioctl(fd, BLKDISCARDZEROES, &discard_zeroes_data);
|
||||
close(fd);
|
||||
}
|
||||
return discard_zeroes_data;
|
||||
}
|
||||
#else
|
||||
#define mke2fs_discard_blocks(fs) 1
|
||||
#define mke2fs_discard_zeroes_data(fs) 0
|
||||
#endif
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
errcode_t retval = 0;
|
||||
@ -2083,9 +2020,20 @@ int main (int argc, char *argv[])
|
||||
|
||||
/* Can't undo discard ... */
|
||||
if (discard && (io_ptr != undo_io_manager)) {
|
||||
retval = mke2fs_discard_blocks(fs);
|
||||
blk64_t blocks = ext2fs_blocks_count(fs->super);
|
||||
if (verbose)
|
||||
printf(_("Calling BLKDISCARD from 0 to %llu... "),
|
||||
(unsigned long long) blocks);
|
||||
retval = io_channel_discard(fs->io, 0, blocks, fs->blocksize);
|
||||
if (verbose) {
|
||||
if (retval)
|
||||
printf(_("failed (%s)\n"),
|
||||
error_message(retval));
|
||||
else
|
||||
printf(_("succeeded\n"));
|
||||
}
|
||||
|
||||
if (!retval && mke2fs_discard_zeroes_data(fs)) {
|
||||
if (!retval && io_channel_discard_zeroes_data(fs->io)) {
|
||||
if (verbose)
|
||||
printf(_("Discard succeeded and will return 0s "
|
||||
" - skipping inode table wipe\n"));
|
||||
|
Loading…
Reference in New Issue
Block a user