import the very fat e2fsck/fsck applets
This commit is contained in:
parent
bfe773f471
commit
51a43b47fe
36 changed files with 19739 additions and 23 deletions
|
@ -11,6 +11,23 @@ config CONFIG_CHATTR
|
||||||
help
|
help
|
||||||
chattr changes the file attributes on a second extended file system.
|
chattr changes the file attributes on a second extended file system.
|
||||||
|
|
||||||
|
config CONFIG_E2FSCK
|
||||||
|
bool "e2fsck"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
e2fsck is used to check Linux second extended file systems (ext2fs).
|
||||||
|
e2fsck also supports ext2 filesystems countaining a journal (ext3).
|
||||||
|
The normal compat symlinks 'fsck.ext2' and 'fsck.ext3' are also
|
||||||
|
provided.
|
||||||
|
|
||||||
|
config CONFIG_FSCK
|
||||||
|
bool "fsck"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
fsck is used to check and optionally repair one or more filesystems.
|
||||||
|
In actuality, fsck is simply a front-end for the various file system
|
||||||
|
checkers (fsck.fstype) available under Linux.
|
||||||
|
|
||||||
config CONFIG_LSATTR
|
config CONFIG_LSATTR
|
||||||
bool "lsattr"
|
bool "lsattr"
|
||||||
default n
|
default n
|
||||||
|
|
|
@ -6,17 +6,22 @@
|
||||||
|
|
||||||
E2FSPROGS_AR:=e2fsprogs.a
|
E2FSPROGS_AR:=e2fsprogs.a
|
||||||
|
|
||||||
E2FSPROGS_DIR:=$(top_builddir)/e2fsprogs/
|
E2FSPROGS_DIR:=$(top_builddir)/e2fsprogs
|
||||||
|
|
||||||
srcdir=$(top_srcdir)/e2fsprogs
|
E2FSPROGS_CFLAGS := -I$(E2FSPROGS_DIR) -include $(E2FSPROGS_DIR)/e2fsbb.h
|
||||||
|
|
||||||
E2FSPROGS_CFLAGS := -I$(srcdir) -include $(srcdir)/e2fsbb.h
|
|
||||||
|
|
||||||
BLKID_SRC := cache.c dev.c devname.c devno.c blkid_getsize.c \
|
BLKID_SRC := cache.c dev.c devname.c devno.c blkid_getsize.c \
|
||||||
probe.c read.c resolve.c save.c tag.c resolve.c
|
probe.c read.c resolve.c save.c tag.c resolve.c
|
||||||
BLKID_SRCS := $(patsubst %,blkid/%, $(BLKID_SRC))
|
BLKID_SRCS := $(patsubst %,blkid/%, $(BLKID_SRC))
|
||||||
BLKID_OBJS := $(patsubst %.c,%.o, $(BLKID_SRCS))
|
BLKID_OBJS := $(patsubst %.c,%.o, $(BLKID_SRCS))
|
||||||
|
|
||||||
|
E2FSCK_SRC := badblocks.c dict.c dirinfo.c dx_dirinfo.c e2fsck.c \
|
||||||
|
ea_refcount.c ehandler.c journal.c message.c pass1.c pass1b.c \
|
||||||
|
pass2.c pass3.c pass4.c pass5.c problem.c recovery.c region.c \
|
||||||
|
rehash.c revoke.c super.c swapfs.c util.c
|
||||||
|
E2FSCK_SRCS := $(patsubst %,e2fsck/%, $(E2FSCK_SRC))
|
||||||
|
E2FSCK_OBJS := $(patsubst %.c,%.o, $(E2FSCK_SRCS))
|
||||||
|
|
||||||
E2P_SRC := fgetsetflags.c fgetsetversion.c pf.c iod.c mntopts.c \
|
E2P_SRC := fgetsetflags.c fgetsetversion.c pf.c iod.c mntopts.c \
|
||||||
feature.c ls.c uuid.c pe.c ostype.c ps.c hashstr.c \
|
feature.c ls.c uuid.c pe.c ostype.c ps.c hashstr.c \
|
||||||
parse_num.c
|
parse_num.c
|
||||||
|
@ -29,7 +34,9 @@ EXT2FS_SRC := gen_bitmap.c bitops.c ismounted.c mkjournal.c unix_io.c \
|
||||||
openfs.c io_manager.c finddev.c read_bb.c alloc.c badblocks.c \
|
openfs.c io_manager.c finddev.c read_bb.c alloc.c badblocks.c \
|
||||||
getsize.c getsectsize.c alloc_tables.c read_bb_file.c mkdir.c \
|
getsize.c getsectsize.c alloc_tables.c read_bb_file.c mkdir.c \
|
||||||
bb_inode.c newdir.c alloc_sb.c lookup.c dirblock.c expanddir.c \
|
bb_inode.c newdir.c alloc_sb.c lookup.c dirblock.c expanddir.c \
|
||||||
dir_iterate.c link.c res_gdt.c
|
dir_iterate.c link.c res_gdt.c icount.c get_pathname.c dblist.c \
|
||||||
|
dirhash.c version.c flushb.c unlink.c check_desc.c valid_blk.c \
|
||||||
|
ext_attr.c bmap.c dblist_dir.c
|
||||||
EXT2FS_SRCS := $(patsubst %,ext2fs/%, $(EXT2FS_SRC))
|
EXT2FS_SRCS := $(patsubst %,ext2fs/%, $(EXT2FS_SRC))
|
||||||
EXT2FS_OBJS := $(patsubst %.c,%.o, $(EXT2FS_SRCS))
|
EXT2FS_OBJS := $(patsubst %.c,%.o, $(EXT2FS_SRCS))
|
||||||
|
|
||||||
|
@ -40,15 +47,17 @@ UUID_OBJS := $(patsubst %.c,%.o, $(UUID_SRCS))
|
||||||
|
|
||||||
E2FSPROGS-:=
|
E2FSPROGS-:=
|
||||||
E2FSPROGS-$(CONFIG_CHATTR) += chattr.o $(E2P_OBJS)
|
E2FSPROGS-$(CONFIG_CHATTR) += chattr.o $(E2P_OBJS)
|
||||||
|
E2FSPROGS-$(CONFIG_E2FSCK) += e2fsck.o $(BLKID_OBJS) $(E2FSCK_OBJS)
|
||||||
|
E2FSPROGS-$(CONFIG_FSCK) += fsck.o base_device.o
|
||||||
E2FSPROGS-$(CONFIG_LSATTR) += lsattr.o $(E2P_OBJS)
|
E2FSPROGS-$(CONFIG_LSATTR) += lsattr.o $(E2P_OBJS)
|
||||||
E2FSPROGS-$(CONFIG_MKE2FS) += mke2fs.o util.o $(E2P_OBJS) $(BLKID_OBJS) $(EXT2FS_OBJS) $(UUID_OBJS)
|
E2FSPROGS-$(CONFIG_MKE2FS) += mke2fs.o util.o $(E2P_OBJS) $(BLKID_OBJS) $(EXT2FS_OBJS) $(UUID_OBJS)
|
||||||
E2FSPROGS-$(CONFIG_TUNE2FS) += tune2fs.o util.o $(E2P_OBJS) $(BLKID_OBJS) $(EXT2FS_OBJS) $(UUID_OBJS)
|
E2FSPROGS-$(CONFIG_TUNE2FS) += tune2fs.o util.o $(E2P_OBJS) $(BLKID_OBJS) $(EXT2FS_OBJS) $(UUID_OBJS)
|
||||||
|
|
||||||
libraries-y+=$(E2FSPROGS_DIR)$(E2FSPROGS_AR)
|
libraries-y+=$(E2FSPROGS_DIR)/$(E2FSPROGS_AR)
|
||||||
|
|
||||||
|
|
||||||
$(E2FSPROGS_DIR)$(E2FSPROGS_AR): $(patsubst %,$(E2FSPROGS_DIR)%, $(E2FSPROGS-y))
|
$(E2FSPROGS_DIR)/$(E2FSPROGS_AR): $(patsubst %,$(E2FSPROGS_DIR)/%, $(E2FSPROGS-y))
|
||||||
$(AR) $(ARFLAGS) $@ $(patsubst %,$(E2FSPROGS_DIR)%, $(E2FSPROGS-y))
|
$(AR) $(ARFLAGS) $@ $(patsubst %,$(E2FSPROGS_DIR)/%, $(E2FSPROGS-y))
|
||||||
|
|
||||||
$(E2FSPROGS_DIR)%.o: $(srcdir)/%.c
|
$(E2FSPROGS_DIR)/%.o: $(E2FSPROGS_DIR)/%.c
|
||||||
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(E2FSPROGS_CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(E2FSPROGS_CFLAGS) -c -o $@ $<
|
||||||
|
|
|
@ -32,29 +32,33 @@ typedef long errcode_t;
|
||||||
#define P_(singular, plural, n) ((n) == 1 ? (singular) : (plural))
|
#define P_(singular, plural, n) ((n) == 1 ? (singular) : (plural))
|
||||||
|
|
||||||
/* misc crap */
|
/* misc crap */
|
||||||
#define fatal_error(msg, err) bb_error_msg_and_die(msg)
|
#define fatal_error(err, msg) bb_error_msg_and_die(msg)
|
||||||
#define usage() bb_show_usage()
|
#define usage() bb_show_usage()
|
||||||
#define perror(msg) bb_perror_msg(msg)
|
#define perror(msg) bb_perror_msg(msg)
|
||||||
|
|
||||||
/* header defines */
|
/* header defines */
|
||||||
|
#define ENABLE_HTREE 1
|
||||||
|
#define HAVE_DIRENT_H 1
|
||||||
#define HAVE_ERRNO_H 1
|
#define HAVE_ERRNO_H 1
|
||||||
#define HAVE_UNISTD_H 1
|
|
||||||
#define HAVE_EXT2_IOCTLS 1
|
#define HAVE_EXT2_IOCTLS 1
|
||||||
|
#define HAVE_GETOPT_H 1
|
||||||
|
#define HAVE_INTTYPES_H 1
|
||||||
|
#define HAVE_LINUX_FD_H 1
|
||||||
|
#define HAVE_MALLOC_H 1
|
||||||
|
#define HAVE_MNTENT_H 1
|
||||||
|
#define HAVE_NETINET_IN_H 1
|
||||||
|
#define HAVE_NET_IF_H 1
|
||||||
|
#define HAVE_SETJMP_H 1
|
||||||
|
#define HAVE_SIGNAL_H 1
|
||||||
#define HAVE_STDLIB_H 1
|
#define HAVE_STDLIB_H 1
|
||||||
|
#define HAVE_SYS_IOCTL_H 1
|
||||||
|
#define HAVE_SYS_MOUNT_H 1
|
||||||
|
#define HAVE_SYS_QUEUE_H 1
|
||||||
|
#define HAVE_SYS_RESOURCE_H 1
|
||||||
|
#define HAVE_SYS_SOCKET_H 1
|
||||||
#define HAVE_SYS_STAT_H 1
|
#define HAVE_SYS_STAT_H 1
|
||||||
|
#define HAVE_SYS_TIME_H 1
|
||||||
#define HAVE_SYS_TYPES_H 1
|
#define HAVE_SYS_TYPES_H 1
|
||||||
#define HAVE_UNISTD_H 1
|
#define HAVE_UNISTD_H 1
|
||||||
#define HAVE_SYS_IOCTL_H 1
|
|
||||||
#define HAVE_SYS_QUEUE_H 1
|
|
||||||
#define HAVE_SYS_SOCKET_H 1
|
|
||||||
#define HAVE_NETINET_IN_H 1
|
|
||||||
#define HAVE_INTTYPES_H 1
|
|
||||||
#define HAVE_NET_IF_H 1
|
|
||||||
#define HAVE_GETOPT_H 1
|
|
||||||
#define HAVE_SYS_RESOURCE_H 1
|
|
||||||
#define HAVE_SYS_MOUNT_H 1
|
|
||||||
#define HAVE_SYS_TIME_H 1
|
|
||||||
#define HAVE_LINUX_FD_H 1
|
|
||||||
#define HAVE_MNTENT_H 1
|
|
||||||
|
|
||||||
#endif /* __E2FSBB_H__ */
|
#endif /* __E2FSBB_H__ */
|
||||||
|
|
1179
e2fsprogs/e2fsck.c
Normal file
1179
e2fsprogs/e2fsck.c
Normal file
File diff suppressed because it is too large
Load diff
136
e2fsprogs/e2fsck/badblocks.c
Normal file
136
e2fsprogs/e2fsck/badblocks.c
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* 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 <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,
|
||||||
|
_("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,
|
||||||
|
_("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 %s%s%s %d", fs->blocksize,
|
||||||
|
(ctx->options & E2F_OPT_PREEN) ? "" : "-s ",
|
||||||
|
(ctx->options & E2F_OPT_WRITECHECK) ? "-n " : "",
|
||||||
|
fs->device_name, fs->super->s_blocks_count);
|
||||||
|
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,
|
||||||
|
_("while reading in list of bad blocks from file"));
|
||||||
|
goto fatal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finally, update the bad blocks from the bad_block_map
|
||||||
|
*/
|
||||||
|
retval = ext2fs_update_bb_inode(fs, bb_list);
|
||||||
|
if (retval) {
|
||||||
|
com_err("ext2fs_update_bb_inode", retval,
|
||||||
|
_("while updating bad block inode"));
|
||||||
|
goto fatal;
|
||||||
|
}
|
||||||
|
|
||||||
|
ext2fs_badblocks_list_free(bb_list);
|
||||||
|
return;
|
||||||
|
|
||||||
|
fatal:
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
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 >= fs->super->s_blocks_count ||
|
||||||
|
*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;
|
||||||
|
}
|
||||||
|
|
1519
e2fsprogs/e2fsck/dict.c
Normal file
1519
e2fsprogs/e2fsck/dict.c
Normal file
File diff suppressed because it is too large
Load diff
144
e2fsprogs/e2fsck/dict.h
Normal file
144
e2fsprogs/e2fsck/dict.h
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* Dictionary Abstract Data Type
|
||||||
|
* Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
|
||||||
|
*
|
||||||
|
* Free Software License:
|
||||||
|
*
|
||||||
|
* All rights are reserved by the author, with the following exceptions:
|
||||||
|
* Permission is granted to freely reproduce and distribute this software,
|
||||||
|
* possibly in exchange for a fee, provided that this copyright notice appears
|
||||||
|
* intact. Permission is also granted to adapt this software to produce
|
||||||
|
* derivative works, as long as the modified versions carry this copyright
|
||||||
|
* notice and additional notices stating that the work has been modified.
|
||||||
|
* This source code may be translated into executable form and incorporated
|
||||||
|
* into proprietary software; there is no requirement for such software to
|
||||||
|
* contain a copyright notice related to this source.
|
||||||
|
*
|
||||||
|
* $Id: dict.h,v 1.22.2.6 2000/11/13 01:36:44 kaz Exp $
|
||||||
|
* $Name: kazlib_1_20 $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DICT_H
|
||||||
|
#define DICT_H
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#ifdef KAZLIB_SIDEEFFECT_DEBUG
|
||||||
|
#include "sfx.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Blurb for inclusion into C++ translation units
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef unsigned long dictcount_t;
|
||||||
|
#define DICTCOUNT_T_MAX ULONG_MAX
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The dictionary is implemented as a red-black tree
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum { dnode_red, dnode_black } dnode_color_t;
|
||||||
|
|
||||||
|
typedef struct dnode_t {
|
||||||
|
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
|
||||||
|
struct dnode_t *dict_left;
|
||||||
|
struct dnode_t *dict_right;
|
||||||
|
struct dnode_t *dict_parent;
|
||||||
|
dnode_color_t dict_color;
|
||||||
|
const void *dict_key;
|
||||||
|
void *dict_data;
|
||||||
|
#else
|
||||||
|
int dict_dummy;
|
||||||
|
#endif
|
||||||
|
} dnode_t;
|
||||||
|
|
||||||
|
typedef int (*dict_comp_t)(const void *, const void *);
|
||||||
|
typedef dnode_t *(*dnode_alloc_t)(void *);
|
||||||
|
typedef void (*dnode_free_t)(dnode_t *, void *);
|
||||||
|
|
||||||
|
typedef struct dict_t {
|
||||||
|
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
|
||||||
|
dnode_t dict_nilnode;
|
||||||
|
dictcount_t dict_nodecount;
|
||||||
|
dictcount_t dict_maxcount;
|
||||||
|
dict_comp_t dict_compare;
|
||||||
|
dnode_alloc_t dict_allocnode;
|
||||||
|
dnode_free_t dict_freenode;
|
||||||
|
void *dict_context;
|
||||||
|
int dict_dupes;
|
||||||
|
#else
|
||||||
|
int dict_dummmy;
|
||||||
|
#endif
|
||||||
|
} dict_t;
|
||||||
|
|
||||||
|
typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
|
||||||
|
|
||||||
|
typedef struct dict_load_t {
|
||||||
|
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
|
||||||
|
dict_t *dict_dictptr;
|
||||||
|
dnode_t dict_nilnode;
|
||||||
|
#else
|
||||||
|
int dict_dummmy;
|
||||||
|
#endif
|
||||||
|
} dict_load_t;
|
||||||
|
|
||||||
|
extern dict_t *dict_create(dictcount_t, dict_comp_t);
|
||||||
|
extern void dict_set_allocator(dict_t *, dnode_alloc_t, dnode_free_t, void *);
|
||||||
|
extern void dict_destroy(dict_t *);
|
||||||
|
extern void dict_free_nodes(dict_t *);
|
||||||
|
extern void dict_free(dict_t *);
|
||||||
|
extern dict_t *dict_init(dict_t *, dictcount_t, dict_comp_t);
|
||||||
|
extern void dict_init_like(dict_t *, const dict_t *);
|
||||||
|
extern int dict_verify(dict_t *);
|
||||||
|
extern int dict_similar(const dict_t *, const dict_t *);
|
||||||
|
extern dnode_t *dict_lookup(dict_t *, const void *);
|
||||||
|
extern dnode_t *dict_lower_bound(dict_t *, const void *);
|
||||||
|
extern dnode_t *dict_upper_bound(dict_t *, const void *);
|
||||||
|
extern void dict_insert(dict_t *, dnode_t *, const void *);
|
||||||
|
extern dnode_t *dict_delete(dict_t *, dnode_t *);
|
||||||
|
extern int dict_alloc_insert(dict_t *, const void *, void *);
|
||||||
|
extern void dict_delete_free(dict_t *, dnode_t *);
|
||||||
|
extern dnode_t *dict_first(dict_t *);
|
||||||
|
extern dnode_t *dict_last(dict_t *);
|
||||||
|
extern dnode_t *dict_next(dict_t *, dnode_t *);
|
||||||
|
extern dnode_t *dict_prev(dict_t *, dnode_t *);
|
||||||
|
extern dictcount_t dict_count(dict_t *);
|
||||||
|
extern int dict_isempty(dict_t *);
|
||||||
|
extern int dict_isfull(dict_t *);
|
||||||
|
extern int dict_contains(dict_t *, dnode_t *);
|
||||||
|
extern void dict_allow_dupes(dict_t *);
|
||||||
|
extern int dnode_is_in_a_dict(dnode_t *);
|
||||||
|
extern dnode_t *dnode_create(void *);
|
||||||
|
extern dnode_t *dnode_init(dnode_t *, void *);
|
||||||
|
extern void dnode_destroy(dnode_t *);
|
||||||
|
extern void *dnode_get(dnode_t *);
|
||||||
|
extern const void *dnode_getkey(dnode_t *);
|
||||||
|
extern void dnode_put(dnode_t *, void *);
|
||||||
|
extern void dict_process(dict_t *, void *, dnode_process_t);
|
||||||
|
extern void dict_load_begin(dict_load_t *, dict_t *);
|
||||||
|
extern void dict_load_next(dict_load_t *, dnode_t *, const void *);
|
||||||
|
extern void dict_load_end(dict_load_t *);
|
||||||
|
extern void dict_merge(dict_t *, dict_t *);
|
||||||
|
|
||||||
|
#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG)
|
||||||
|
#ifdef KAZLIB_SIDEEFFECT_DEBUG
|
||||||
|
#define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount)
|
||||||
|
#else
|
||||||
|
#define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount)
|
||||||
|
#endif
|
||||||
|
#define dict_count(D) ((D)->dict_nodecount)
|
||||||
|
#define dict_isempty(D) ((D)->dict_nodecount == 0)
|
||||||
|
#define dnode_get(N) ((N)->dict_data)
|
||||||
|
#define dnode_getkey(N) ((N)->dict_key)
|
||||||
|
#define dnode_put(N, X) ((N)->dict_data = (X))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
137
e2fsprogs/e2fsck/dirinfo.c
Normal file
137
e2fsprogs/e2fsck/dirinfo.c
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
* dirinfo.c --- maintains the directory information table for e2fsck.
|
||||||
|
*
|
||||||
|
* Copyright (C) 1993 Theodore Ts'o. This file may be redistributed
|
||||||
|
* under the terms of the GNU Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "e2fsck.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This subroutine is called during pass1 to create a directory info
|
||||||
|
* entry. During pass1, the passed-in parent is 0; it will get filled
|
||||||
|
* in during pass2.
|
||||||
|
*/
|
||||||
|
void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
|
||||||
|
{
|
||||||
|
struct dir_info *dir;
|
||||||
|
int i, j;
|
||||||
|
ext2_ino_t num_dirs;
|
||||||
|
errcode_t retval;
|
||||||
|
unsigned long old_size;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
printf("add_dir_info for inode %lu...\n", ino);
|
||||||
|
#endif
|
||||||
|
if (!ctx->dir_info) {
|
||||||
|
ctx->dir_info_count = 0;
|
||||||
|
retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
|
||||||
|
if (retval)
|
||||||
|
num_dirs = 1024; /* Guess */
|
||||||
|
ctx->dir_info_size = num_dirs + 10;
|
||||||
|
ctx->dir_info = (struct dir_info *)
|
||||||
|
e2fsck_allocate_memory(ctx, ctx->dir_info_size
|
||||||
|
* sizeof (struct dir_info),
|
||||||
|
"directory map");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->dir_info_count >= ctx->dir_info_size) {
|
||||||
|
old_size = ctx->dir_info_size * sizeof(struct dir_info);
|
||||||
|
ctx->dir_info_size += 10;
|
||||||
|
retval = ext2fs_resize_mem(old_size, ctx->dir_info_size *
|
||||||
|
sizeof(struct dir_info),
|
||||||
|
&ctx->dir_info);
|
||||||
|
if (retval) {
|
||||||
|
ctx->dir_info_size -= 10;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Normally, add_dir_info is called with each inode in
|
||||||
|
* sequential order; but once in a while (like when pass 3
|
||||||
|
* needs to recreate the root directory or lost+found
|
||||||
|
* directory) it is called out of order. In those cases, we
|
||||||
|
* need to move the dir_info entries down to make room, since
|
||||||
|
* the dir_info array needs to be sorted by inode number for
|
||||||
|
* get_dir_info()'s sake.
|
||||||
|
*/
|
||||||
|
if (ctx->dir_info_count &&
|
||||||
|
ctx->dir_info[ctx->dir_info_count-1].ino >= ino) {
|
||||||
|
for (i = ctx->dir_info_count-1; i > 0; i--)
|
||||||
|
if (ctx->dir_info[i-1].ino < ino)
|
||||||
|
break;
|
||||||
|
dir = &ctx->dir_info[i];
|
||||||
|
if (dir->ino != ino)
|
||||||
|
for (j = ctx->dir_info_count++; j > i; j--)
|
||||||
|
ctx->dir_info[j] = ctx->dir_info[j-1];
|
||||||
|
} else
|
||||||
|
dir = &ctx->dir_info[ctx->dir_info_count++];
|
||||||
|
|
||||||
|
dir->ino = ino;
|
||||||
|
dir->dotdot = parent;
|
||||||
|
dir->parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_dir_info() --- given an inode number, try to find the directory
|
||||||
|
* information entry for it.
|
||||||
|
*/
|
||||||
|
struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
|
||||||
|
{
|
||||||
|
int low, high, mid;
|
||||||
|
|
||||||
|
low = 0;
|
||||||
|
high = ctx->dir_info_count-1;
|
||||||
|
if (!ctx->dir_info)
|
||||||
|
return 0;
|
||||||
|
if (ino == ctx->dir_info[low].ino)
|
||||||
|
return &ctx->dir_info[low];
|
||||||
|
if (ino == ctx->dir_info[high].ino)
|
||||||
|
return &ctx->dir_info[high];
|
||||||
|
|
||||||
|
while (low < high) {
|
||||||
|
mid = (low+high)/2;
|
||||||
|
if (mid == low || mid == high)
|
||||||
|
break;
|
||||||
|
if (ino == ctx->dir_info[mid].ino)
|
||||||
|
return &ctx->dir_info[mid];
|
||||||
|
if (ino < ctx->dir_info[mid].ino)
|
||||||
|
high = mid;
|
||||||
|
else
|
||||||
|
low = mid;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the dir_info structure when it isn't needed any more.
|
||||||
|
*/
|
||||||
|
void e2fsck_free_dir_info(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
if (ctx->dir_info) {
|
||||||
|
ext2fs_free_mem(&ctx->dir_info);
|
||||||
|
ctx->dir_info = 0;
|
||||||
|
}
|
||||||
|
ctx->dir_info_size = 0;
|
||||||
|
ctx->dir_info_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the count of number of directories in the dir_info structure
|
||||||
|
*/
|
||||||
|
int e2fsck_get_num_dirinfo(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
return ctx->dir_info_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A simple interator function
|
||||||
|
*/
|
||||||
|
struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control)
|
||||||
|
{
|
||||||
|
if (*control >= ctx->dir_info_count)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return(ctx->dir_info + (*control)++);
|
||||||
|
}
|
150
e2fsprogs/e2fsck/dx_dirinfo.c
Normal file
150
e2fsprogs/e2fsck/dx_dirinfo.c
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* dirinfo.c --- maintains the directory information table for e2fsck.
|
||||||
|
*
|
||||||
|
* Copyright (C) 1993 Theodore Ts'o. This file may be redistributed
|
||||||
|
* under the terms of the GNU Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "e2fsck.h"
|
||||||
|
#ifdef ENABLE_HTREE
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This subroutine is called during pass1 to create a directory info
|
||||||
|
* entry. During pass1, the passed-in parent is 0; it will get filled
|
||||||
|
* in during pass2.
|
||||||
|
*/
|
||||||
|
void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
|
||||||
|
{
|
||||||
|
struct dx_dir_info *dir;
|
||||||
|
int i, j;
|
||||||
|
errcode_t retval;
|
||||||
|
unsigned long old_size;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
printf("add_dx_dir_info for inode %lu...\n", ino);
|
||||||
|
#endif
|
||||||
|
if (!ctx->dx_dir_info) {
|
||||||
|
ctx->dx_dir_info_count = 0;
|
||||||
|
ctx->dx_dir_info_size = 100; /* Guess */
|
||||||
|
ctx->dx_dir_info = (struct dx_dir_info *)
|
||||||
|
e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size
|
||||||
|
* sizeof (struct dx_dir_info),
|
||||||
|
"directory map");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) {
|
||||||
|
old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info);
|
||||||
|
ctx->dx_dir_info_size += 10;
|
||||||
|
retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size *
|
||||||
|
sizeof(struct dx_dir_info),
|
||||||
|
&ctx->dx_dir_info);
|
||||||
|
if (retval) {
|
||||||
|
ctx->dx_dir_info_size -= 10;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Normally, add_dx_dir_info is called with each inode in
|
||||||
|
* sequential order; but once in a while (like when pass 3
|
||||||
|
* needs to recreate the root directory or lost+found
|
||||||
|
* directory) it is called out of order. In those cases, we
|
||||||
|
* need to move the dx_dir_info entries down to make room, since
|
||||||
|
* the dx_dir_info array needs to be sorted by inode number for
|
||||||
|
* get_dx_dir_info()'s sake.
|
||||||
|
*/
|
||||||
|
if (ctx->dx_dir_info_count &&
|
||||||
|
ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) {
|
||||||
|
for (i = ctx->dx_dir_info_count-1; i > 0; i--)
|
||||||
|
if (ctx->dx_dir_info[i-1].ino < ino)
|
||||||
|
break;
|
||||||
|
dir = &ctx->dx_dir_info[i];
|
||||||
|
if (dir->ino != ino)
|
||||||
|
for (j = ctx->dx_dir_info_count++; j > i; j--)
|
||||||
|
ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1];
|
||||||
|
} else
|
||||||
|
dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++];
|
||||||
|
|
||||||
|
dir->ino = ino;
|
||||||
|
dir->numblocks = num_blocks;
|
||||||
|
dir->hashversion = 0;
|
||||||
|
dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
|
||||||
|
* sizeof (struct dx_dirblock_info),
|
||||||
|
"dx_block info array");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_dx_dir_info() --- given an inode number, try to find the directory
|
||||||
|
* information entry for it.
|
||||||
|
*/
|
||||||
|
struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino)
|
||||||
|
{
|
||||||
|
int low, high, mid;
|
||||||
|
|
||||||
|
low = 0;
|
||||||
|
high = ctx->dx_dir_info_count-1;
|
||||||
|
if (!ctx->dx_dir_info)
|
||||||
|
return 0;
|
||||||
|
if (ino == ctx->dx_dir_info[low].ino)
|
||||||
|
return &ctx->dx_dir_info[low];
|
||||||
|
if (ino == ctx->dx_dir_info[high].ino)
|
||||||
|
return &ctx->dx_dir_info[high];
|
||||||
|
|
||||||
|
while (low < high) {
|
||||||
|
mid = (low+high)/2;
|
||||||
|
if (mid == low || mid == high)
|
||||||
|
break;
|
||||||
|
if (ino == ctx->dx_dir_info[mid].ino)
|
||||||
|
return &ctx->dx_dir_info[mid];
|
||||||
|
if (ino < ctx->dx_dir_info[mid].ino)
|
||||||
|
high = mid;
|
||||||
|
else
|
||||||
|
low = mid;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the dx_dir_info structure when it isn't needed any more.
|
||||||
|
*/
|
||||||
|
void e2fsck_free_dx_dir_info(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct dx_dir_info *dir;
|
||||||
|
|
||||||
|
if (ctx->dx_dir_info) {
|
||||||
|
dir = ctx->dx_dir_info;
|
||||||
|
for (i=0; i < ctx->dx_dir_info_count; i++) {
|
||||||
|
if (dir->dx_block) {
|
||||||
|
ext2fs_free_mem(&dir->dx_block);
|
||||||
|
dir->dx_block = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ext2fs_free_mem(&ctx->dx_dir_info);
|
||||||
|
ctx->dx_dir_info = 0;
|
||||||
|
}
|
||||||
|
ctx->dx_dir_info_size = 0;
|
||||||
|
ctx->dx_dir_info_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the count of number of directories in the dx_dir_info structure
|
||||||
|
*/
|
||||||
|
int e2fsck_get_num_dx_dirinfo(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
return ctx->dx_dir_info_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A simple interator function
|
||||||
|
*/
|
||||||
|
struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control)
|
||||||
|
{
|
||||||
|
if (*control >= ctx->dx_dir_info_count)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return(ctx->dx_dir_info + (*control)++);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* ENABLE_HTREE */
|
202
e2fsprogs/e2fsck/e2fsck.c
Normal file
202
e2fsprogs/e2fsck/e2fsck.c
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
/*
|
||||||
|
* e2fsck.c - a consistency checker for the new extended file system.
|
||||||
|
*
|
||||||
|
* Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "e2fsck.h"
|
||||||
|
#include "problem.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function allocates an e2fsck context
|
||||||
|
*/
|
||||||
|
errcode_t e2fsck_allocate_context(e2fsck_t *ret)
|
||||||
|
{
|
||||||
|
e2fsck_t context;
|
||||||
|
errcode_t retval;
|
||||||
|
|
||||||
|
retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
memset(context, 0, sizeof(struct e2fsck_struct));
|
||||||
|
|
||||||
|
context->process_inode_size = 256;
|
||||||
|
context->ext_attr_ver = 2;
|
||||||
|
|
||||||
|
*ret = context;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function resets an e2fsck context; it is called when e2fsck
|
||||||
|
* needs to be restarted.
|
||||||
|
*/
|
||||||
|
errcode_t e2fsck_reset_context(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ctx->flags = 0;
|
||||||
|
ctx->lost_and_found = 0;
|
||||||
|
ctx->bad_lost_and_found = 0;
|
||||||
|
if (ctx->inode_used_map) {
|
||||||
|
ext2fs_free_inode_bitmap(ctx->inode_used_map);
|
||||||
|
ctx->inode_used_map = 0;
|
||||||
|
}
|
||||||
|
if (ctx->inode_dir_map) {
|
||||||
|
ext2fs_free_inode_bitmap(ctx->inode_dir_map);
|
||||||
|
ctx->inode_dir_map = 0;
|
||||||
|
}
|
||||||
|
if (ctx->inode_reg_map) {
|
||||||
|
ext2fs_free_inode_bitmap(ctx->inode_reg_map);
|
||||||
|
ctx->inode_reg_map = 0;
|
||||||
|
}
|
||||||
|
if (ctx->block_found_map) {
|
||||||
|
ext2fs_free_block_bitmap(ctx->block_found_map);
|
||||||
|
ctx->block_found_map = 0;
|
||||||
|
}
|
||||||
|
if (ctx->inode_link_info) {
|
||||||
|
ext2fs_free_icount(ctx->inode_link_info);
|
||||||
|
ctx->inode_link_info = 0;
|
||||||
|
}
|
||||||
|
if (ctx->journal_io) {
|
||||||
|
if (ctx->fs && ctx->fs->io != ctx->journal_io)
|
||||||
|
io_channel_close(ctx->journal_io);
|
||||||
|
ctx->journal_io = 0;
|
||||||
|
}
|
||||||
|
if (ctx->fs && ctx->fs->dblist) {
|
||||||
|
ext2fs_free_dblist(ctx->fs->dblist);
|
||||||
|
ctx->fs->dblist = 0;
|
||||||
|
}
|
||||||
|
e2fsck_free_dir_info(ctx);
|
||||||
|
#ifdef ENABLE_HTREE
|
||||||
|
e2fsck_free_dx_dir_info(ctx);
|
||||||
|
#endif
|
||||||
|
if (ctx->refcount) {
|
||||||
|
ea_refcount_free(ctx->refcount);
|
||||||
|
ctx->refcount = 0;
|
||||||
|
}
|
||||||
|
if (ctx->refcount_extra) {
|
||||||
|
ea_refcount_free(ctx->refcount_extra);
|
||||||
|
ctx->refcount_extra = 0;
|
||||||
|
}
|
||||||
|
if (ctx->block_dup_map) {
|
||||||
|
ext2fs_free_block_bitmap(ctx->block_dup_map);
|
||||||
|
ctx->block_dup_map = 0;
|
||||||
|
}
|
||||||
|
if (ctx->block_ea_map) {
|
||||||
|
ext2fs_free_block_bitmap(ctx->block_ea_map);
|
||||||
|
ctx->block_ea_map = 0;
|
||||||
|
}
|
||||||
|
if (ctx->inode_bb_map) {
|
||||||
|
ext2fs_free_inode_bitmap(ctx->inode_bb_map);
|
||||||
|
ctx->inode_bb_map = 0;
|
||||||
|
}
|
||||||
|
if (ctx->inode_bad_map) {
|
||||||
|
ext2fs_free_inode_bitmap(ctx->inode_bad_map);
|
||||||
|
ctx->inode_bad_map = 0;
|
||||||
|
}
|
||||||
|
if (ctx->inode_imagic_map) {
|
||||||
|
ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
|
||||||
|
ctx->inode_imagic_map = 0;
|
||||||
|
}
|
||||||
|
if (ctx->dirs_to_hash) {
|
||||||
|
ext2fs_u32_list_free(ctx->dirs_to_hash);
|
||||||
|
ctx->dirs_to_hash = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear the array of invalid meta-data flags
|
||||||
|
*/
|
||||||
|
if (ctx->invalid_inode_bitmap_flag) {
|
||||||
|
ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag);
|
||||||
|
ctx->invalid_inode_bitmap_flag = 0;
|
||||||
|
}
|
||||||
|
if (ctx->invalid_block_bitmap_flag) {
|
||||||
|
ext2fs_free_mem(&ctx->invalid_block_bitmap_flag);
|
||||||
|
ctx->invalid_block_bitmap_flag = 0;
|
||||||
|
}
|
||||||
|
if (ctx->invalid_inode_table_flag) {
|
||||||
|
ext2fs_free_mem(&ctx->invalid_inode_table_flag);
|
||||||
|
ctx->invalid_inode_table_flag = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear statistic counters */
|
||||||
|
ctx->fs_directory_count = 0;
|
||||||
|
ctx->fs_regular_count = 0;
|
||||||
|
ctx->fs_blockdev_count = 0;
|
||||||
|
ctx->fs_chardev_count = 0;
|
||||||
|
ctx->fs_links_count = 0;
|
||||||
|
ctx->fs_symlinks_count = 0;
|
||||||
|
ctx->fs_fast_symlinks_count = 0;
|
||||||
|
ctx->fs_fifo_count = 0;
|
||||||
|
ctx->fs_total_count = 0;
|
||||||
|
ctx->fs_badblocks_count = 0;
|
||||||
|
ctx->fs_sockets_count = 0;
|
||||||
|
ctx->fs_ind_count = 0;
|
||||||
|
ctx->fs_dind_count = 0;
|
||||||
|
ctx->fs_tind_count = 0;
|
||||||
|
ctx->fs_fragmented = 0;
|
||||||
|
ctx->large_files = 0;
|
||||||
|
|
||||||
|
/* Reset the superblock to the user's requested value */
|
||||||
|
ctx->superblock = ctx->use_superblock;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2fsck_free_context(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
if (!ctx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
e2fsck_reset_context(ctx);
|
||||||
|
if (ctx->blkid)
|
||||||
|
blkid_put_cache(ctx->blkid);
|
||||||
|
|
||||||
|
ext2fs_free_mem(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function runs through the e2fsck passes and calls them all,
|
||||||
|
* returning restart, abort, or cancel as necessary...
|
||||||
|
*/
|
||||||
|
typedef void (*pass_t)(e2fsck_t ctx);
|
||||||
|
|
||||||
|
pass_t e2fsck_passes[] = {
|
||||||
|
e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
|
||||||
|
e2fsck_pass5, 0 };
|
||||||
|
|
||||||
|
#define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
|
||||||
|
|
||||||
|
int e2fsck_run(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
pass_t e2fsck_pass;
|
||||||
|
|
||||||
|
#ifdef HAVE_SETJMP_H
|
||||||
|
if (setjmp(ctx->abort_loc)) {
|
||||||
|
ctx->flags &= ~E2F_FLAG_SETJMP_OK;
|
||||||
|
return (ctx->flags & E2F_FLAG_RUN_RETURN);
|
||||||
|
}
|
||||||
|
ctx->flags |= E2F_FLAG_SETJMP_OK;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
|
||||||
|
if (ctx->flags & E2F_FLAG_RUN_RETURN)
|
||||||
|
break;
|
||||||
|
e2fsck_pass(ctx);
|
||||||
|
if (ctx->progress)
|
||||||
|
(void) (ctx->progress)(ctx, 0, 0, 0);
|
||||||
|
}
|
||||||
|
ctx->flags &= ~E2F_FLAG_SETJMP_OK;
|
||||||
|
|
||||||
|
if (ctx->flags & E2F_FLAG_RUN_RETURN)
|
||||||
|
return (ctx->flags & E2F_FLAG_RUN_RETURN);
|
||||||
|
return 0;
|
||||||
|
}
|
446
e2fsprogs/e2fsck/e2fsck.h
Normal file
446
e2fsprogs/e2fsck/e2fsck.h
Normal file
|
@ -0,0 +1,446 @@
|
||||||
|
/*
|
||||||
|
* e2fsck.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 1993, 1994 Theodore Ts'o. This file may be
|
||||||
|
* redistributed under the terms of the GNU Public License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#ifdef HAVE_UNISTD_H
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#ifdef HAVE_SYS_TYPES_H
|
||||||
|
#include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_TIME_H
|
||||||
|
#include <sys/time.h>
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SETJMP_H
|
||||||
|
#include <setjmp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if EXT2_FLAT_INCLUDES
|
||||||
|
#include "ext2_fs.h"
|
||||||
|
#include "ext2fs.h"
|
||||||
|
#include "blkid.h"
|
||||||
|
#else
|
||||||
|
#include "ext2fs/ext2_fs.h"
|
||||||
|
#include "ext2fs/ext2fs.h"
|
||||||
|
#include "blkid/blkid.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exit codes used by fsck-type programs
|
||||||
|
*/
|
||||||
|
#define FSCK_OK 0 /* No errors */
|
||||||
|
#define FSCK_NONDESTRUCT 1 /* File system errors corrected */
|
||||||
|
#define FSCK_REBOOT 2 /* System should be rebooted */
|
||||||
|
#define FSCK_UNCORRECTED 4 /* File system errors left uncorrected */
|
||||||
|
#define FSCK_ERROR 8 /* Operational error */
|
||||||
|
#define FSCK_USAGE 16 /* Usage or syntax error */
|
||||||
|
#define FSCK_CANCELED 32 /* Aborted with a signal or ^C */
|
||||||
|
#define FSCK_LIBRARY 128 /* Shared library error */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The last ext2fs revision level that this version of e2fsck is able to
|
||||||
|
* support
|
||||||
|
*/
|
||||||
|
#define E2FSCK_CURRENT_REV 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The directory information structure; stores directory information
|
||||||
|
* collected in earlier passes, to avoid disk i/o in fetching the
|
||||||
|
* directory information.
|
||||||
|
*/
|
||||||
|
struct dir_info {
|
||||||
|
ext2_ino_t ino; /* Inode number */
|
||||||
|
ext2_ino_t dotdot; /* Parent according to '..' */
|
||||||
|
ext2_ino_t parent; /* Parent according to treewalk */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The indexed directory information structure; stores information for
|
||||||
|
* directories which contain a hash tree index.
|
||||||
|
*/
|
||||||
|
struct dx_dir_info {
|
||||||
|
ext2_ino_t ino; /* Inode number */
|
||||||
|
int numblocks; /* number of blocks */
|
||||||
|
int hashversion;
|
||||||
|
short depth; /* depth of tree */
|
||||||
|
struct dx_dirblock_info *dx_block; /* Array of size numblocks */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DX_DIRBLOCK_ROOT 1
|
||||||
|
#define DX_DIRBLOCK_LEAF 2
|
||||||
|
#define DX_DIRBLOCK_NODE 3
|
||||||
|
#define DX_DIRBLOCK_CORRUPT 4
|
||||||
|
#define DX_DIRBLOCK_CLEARED 8
|
||||||
|
|
||||||
|
struct dx_dirblock_info {
|
||||||
|
int type;
|
||||||
|
blk_t phys;
|
||||||
|
int flags;
|
||||||
|
blk_t parent;
|
||||||
|
ext2_dirhash_t min_hash;
|
||||||
|
ext2_dirhash_t max_hash;
|
||||||
|
ext2_dirhash_t node_min_hash;
|
||||||
|
ext2_dirhash_t node_max_hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DX_FLAG_REFERENCED 1
|
||||||
|
#define DX_FLAG_DUP_REF 2
|
||||||
|
#define DX_FLAG_FIRST 4
|
||||||
|
#define DX_FLAG_LAST 8
|
||||||
|
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
/*
|
||||||
|
* This structure is used for keeping track of how much resources have
|
||||||
|
* been used for a particular pass of e2fsck.
|
||||||
|
*/
|
||||||
|
struct resource_track {
|
||||||
|
struct timeval time_start;
|
||||||
|
struct timeval user_start;
|
||||||
|
struct timeval system_start;
|
||||||
|
void *brk_start;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* E2fsck options
|
||||||
|
*/
|
||||||
|
#define E2F_OPT_READONLY 0x0001
|
||||||
|
#define E2F_OPT_PREEN 0x0002
|
||||||
|
#define E2F_OPT_YES 0x0004
|
||||||
|
#define E2F_OPT_NO 0x0008
|
||||||
|
#define E2F_OPT_TIME 0x0010
|
||||||
|
#define E2F_OPT_TIME2 0x0020
|
||||||
|
#define E2F_OPT_CHECKBLOCKS 0x0040
|
||||||
|
#define E2F_OPT_DEBUG 0x0080
|
||||||
|
#define E2F_OPT_FORCE 0x0100
|
||||||
|
#define E2F_OPT_WRITECHECK 0x0200
|
||||||
|
#define E2F_OPT_COMPRESS_DIRS 0x0400
|
||||||
|
|
||||||
|
/*
|
||||||
|
* E2fsck flags
|
||||||
|
*/
|
||||||
|
#define E2F_FLAG_ABORT 0x0001 /* Abort signaled */
|
||||||
|
#define E2F_FLAG_CANCEL 0x0002 /* Cancel signaled */
|
||||||
|
#define E2F_FLAG_SIGNAL_MASK 0x0003
|
||||||
|
#define E2F_FLAG_RESTART 0x0004 /* Restart signaled */
|
||||||
|
|
||||||
|
#define E2F_FLAG_SETJMP_OK 0x0010 /* Setjmp valid for abort */
|
||||||
|
|
||||||
|
#define E2F_FLAG_PROG_BAR 0x0020 /* Progress bar on screen */
|
||||||
|
#define E2F_FLAG_PROG_SUPPRESS 0x0040 /* Progress suspended */
|
||||||
|
#define E2F_FLAG_JOURNAL_INODE 0x0080 /* Create a new ext3 journal inode */
|
||||||
|
#define E2F_FLAG_SB_SPECIFIED 0x0100 /* The superblock was explicitly
|
||||||
|
* specified by the user */
|
||||||
|
#define E2F_FLAG_RESTARTED 0x0200 /* E2fsck has been restarted */
|
||||||
|
#define E2F_FLAG_RESIZE_INODE 0x0400 /* Request to recreate resize inode */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Defines for indicating the e2fsck pass number
|
||||||
|
*/
|
||||||
|
#define E2F_PASS_1 1
|
||||||
|
#define E2F_PASS_2 2
|
||||||
|
#define E2F_PASS_3 3
|
||||||
|
#define E2F_PASS_4 4
|
||||||
|
#define E2F_PASS_5 5
|
||||||
|
#define E2F_PASS_1B 6
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define the extended attribute refcount structure
|
||||||
|
*/
|
||||||
|
typedef struct ea_refcount *ext2_refcount_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the global e2fsck structure.
|
||||||
|
*/
|
||||||
|
typedef struct e2fsck_struct *e2fsck_t;
|
||||||
|
|
||||||
|
struct e2fsck_struct {
|
||||||
|
ext2_filsys fs;
|
||||||
|
const char *program_name;
|
||||||
|
char *filesystem_name;
|
||||||
|
char *device_name;
|
||||||
|
char *io_options;
|
||||||
|
int flags; /* E2fsck internal flags */
|
||||||
|
int options;
|
||||||
|
blk_t use_superblock; /* sb requested by user */
|
||||||
|
blk_t superblock; /* sb used to open fs */
|
||||||
|
int blocksize; /* blocksize */
|
||||||
|
blk_t num_blocks; /* Total number of blocks */
|
||||||
|
int mount_flags;
|
||||||
|
blkid_cache blkid; /* blkid cache */
|
||||||
|
|
||||||
|
#ifdef HAVE_SETJMP_H
|
||||||
|
jmp_buf abort_loc;
|
||||||
|
#endif
|
||||||
|
unsigned long abort_code;
|
||||||
|
|
||||||
|
int (*progress)(e2fsck_t ctx, int pass, unsigned long cur,
|
||||||
|
unsigned long max);
|
||||||
|
|
||||||
|
ext2fs_inode_bitmap inode_used_map; /* Inodes which are in use */
|
||||||
|
ext2fs_inode_bitmap inode_bad_map; /* Inodes which are bad somehow */
|
||||||
|
ext2fs_inode_bitmap inode_dir_map; /* Inodes which are directories */
|
||||||
|
ext2fs_inode_bitmap inode_bb_map; /* Inodes which are in bad blocks */
|
||||||
|
ext2fs_inode_bitmap inode_imagic_map; /* AFS inodes */
|
||||||
|
ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/
|
||||||
|
|
||||||
|
ext2fs_block_bitmap block_found_map; /* Blocks which are in use */
|
||||||
|
ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */
|
||||||
|
ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Inode count arrays
|
||||||
|
*/
|
||||||
|
ext2_icount_t inode_count;
|
||||||
|
ext2_icount_t inode_link_info;
|
||||||
|
|
||||||
|
ext2_refcount_t refcount;
|
||||||
|
ext2_refcount_t refcount_extra;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Array of flags indicating whether an inode bitmap, block
|
||||||
|
* bitmap, or inode table is invalid
|
||||||
|
*/
|
||||||
|
int *invalid_inode_bitmap_flag;
|
||||||
|
int *invalid_block_bitmap_flag;
|
||||||
|
int *invalid_inode_table_flag;
|
||||||
|
int invalid_bitmaps; /* There are invalid bitmaps/itable */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Block buffer
|
||||||
|
*/
|
||||||
|
char *block_buf;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For pass1_check_directory and pass1_get_blocks
|
||||||
|
*/
|
||||||
|
ext2_ino_t stashed_ino;
|
||||||
|
struct ext2_inode *stashed_inode;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Location of the lost and found directory
|
||||||
|
*/
|
||||||
|
ext2_ino_t lost_and_found;
|
||||||
|
int bad_lost_and_found;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Directory information
|
||||||
|
*/
|
||||||
|
int dir_info_count;
|
||||||
|
int dir_info_size;
|
||||||
|
struct dir_info *dir_info;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Indexed directory information
|
||||||
|
*/
|
||||||
|
int dx_dir_info_count;
|
||||||
|
int dx_dir_info_size;
|
||||||
|
struct dx_dir_info *dx_dir_info;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Directories to hash
|
||||||
|
*/
|
||||||
|
ext2_u32_list dirs_to_hash;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tuning parameters
|
||||||
|
*/
|
||||||
|
int process_inode_size;
|
||||||
|
int inode_buffer_blocks;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ext3 journal support
|
||||||
|
*/
|
||||||
|
io_channel journal_io;
|
||||||
|
char *journal_name;
|
||||||
|
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
/*
|
||||||
|
* For timing purposes
|
||||||
|
*/
|
||||||
|
struct resource_track global_rtrack;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* How we display the progress update (for unix)
|
||||||
|
*/
|
||||||
|
int progress_fd;
|
||||||
|
int progress_pos;
|
||||||
|
int progress_last_percent;
|
||||||
|
unsigned int progress_last_time;
|
||||||
|
int interactive; /* Are we connected directly to a tty? */
|
||||||
|
char start_meta[2], stop_meta[2];
|
||||||
|
|
||||||
|
/* File counts */
|
||||||
|
int fs_directory_count;
|
||||||
|
int fs_regular_count;
|
||||||
|
int fs_blockdev_count;
|
||||||
|
int fs_chardev_count;
|
||||||
|
int fs_links_count;
|
||||||
|
int fs_symlinks_count;
|
||||||
|
int fs_fast_symlinks_count;
|
||||||
|
int fs_fifo_count;
|
||||||
|
int fs_total_count;
|
||||||
|
int fs_badblocks_count;
|
||||||
|
int fs_sockets_count;
|
||||||
|
int fs_ind_count;
|
||||||
|
int fs_dind_count;
|
||||||
|
int fs_tind_count;
|
||||||
|
int fs_fragmented;
|
||||||
|
int large_files;
|
||||||
|
int fs_ext_attr_inodes;
|
||||||
|
int fs_ext_attr_blocks;
|
||||||
|
|
||||||
|
int ext_attr_ver;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For the use of callers of the e2fsck functions; not used by
|
||||||
|
* e2fsck functions themselves.
|
||||||
|
*/
|
||||||
|
void *priv_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Used by the region allocation code */
|
||||||
|
typedef __u32 region_addr_t;
|
||||||
|
typedef struct region_struct *region_t;
|
||||||
|
|
||||||
|
#ifndef HAVE_STRNLEN
|
||||||
|
#define strnlen(str, x) e2fsck_strnlen((str),(x))
|
||||||
|
extern int e2fsck_strnlen(const char * s, int count);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Procedure declarations
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void e2fsck_pass1(e2fsck_t ctx);
|
||||||
|
extern void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf);
|
||||||
|
extern void e2fsck_pass2(e2fsck_t ctx);
|
||||||
|
extern void e2fsck_pass3(e2fsck_t ctx);
|
||||||
|
extern void e2fsck_pass4(e2fsck_t ctx);
|
||||||
|
extern void e2fsck_pass5(e2fsck_t ctx);
|
||||||
|
|
||||||
|
/* e2fsck.c */
|
||||||
|
extern errcode_t e2fsck_allocate_context(e2fsck_t *ret);
|
||||||
|
extern errcode_t e2fsck_reset_context(e2fsck_t ctx);
|
||||||
|
extern void e2fsck_free_context(e2fsck_t ctx);
|
||||||
|
extern int e2fsck_run(e2fsck_t ctx);
|
||||||
|
|
||||||
|
|
||||||
|
/* badblock.c */
|
||||||
|
extern void read_bad_blocks_file(e2fsck_t ctx, const char *bad_blocks_file,
|
||||||
|
int replace_bad_blocks);
|
||||||
|
|
||||||
|
/* dirinfo.c */
|
||||||
|
extern void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent);
|
||||||
|
extern struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino);
|
||||||
|
extern void e2fsck_free_dir_info(e2fsck_t ctx);
|
||||||
|
extern int e2fsck_get_num_dirinfo(e2fsck_t ctx);
|
||||||
|
extern struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control);
|
||||||
|
|
||||||
|
/* dx_dirinfo.c */
|
||||||
|
extern void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks);
|
||||||
|
extern struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino);
|
||||||
|
extern void e2fsck_free_dx_dir_info(e2fsck_t ctx);
|
||||||
|
extern int e2fsck_get_num_dx_dirinfo(e2fsck_t ctx);
|
||||||
|
extern struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control);
|
||||||
|
|
||||||
|
/* ea_refcount.c */
|
||||||
|
extern errcode_t ea_refcount_create(int size, ext2_refcount_t *ret);
|
||||||
|
extern void ea_refcount_free(ext2_refcount_t refcount);
|
||||||
|
extern errcode_t ea_refcount_fetch(ext2_refcount_t refcount, blk_t blk,
|
||||||
|
int *ret);
|
||||||
|
extern errcode_t ea_refcount_increment(ext2_refcount_t refcount,
|
||||||
|
blk_t blk, int *ret);
|
||||||
|
extern errcode_t ea_refcount_decrement(ext2_refcount_t refcount,
|
||||||
|
blk_t blk, int *ret);
|
||||||
|
extern errcode_t ea_refcount_store(ext2_refcount_t refcount,
|
||||||
|
blk_t blk, int count);
|
||||||
|
extern blk_t ext2fs_get_refcount_size(ext2_refcount_t refcount);
|
||||||
|
extern void ea_refcount_intr_begin(ext2_refcount_t refcount);
|
||||||
|
extern blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret);
|
||||||
|
|
||||||
|
/* ehandler.c */
|
||||||
|
extern const char *ehandler_operation(const char *op);
|
||||||
|
extern void ehandler_init(io_channel channel);
|
||||||
|
|
||||||
|
/* journal.c */
|
||||||
|
extern int e2fsck_check_ext3_journal(e2fsck_t ctx);
|
||||||
|
extern int e2fsck_run_ext3_journal(e2fsck_t ctx);
|
||||||
|
extern void e2fsck_move_ext3_journal(e2fsck_t ctx);
|
||||||
|
|
||||||
|
/* pass1.c */
|
||||||
|
extern void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool);
|
||||||
|
extern int e2fsck_pass1_check_device_inode(ext2_filsys fs,
|
||||||
|
struct ext2_inode *inode);
|
||||||
|
extern int e2fsck_pass1_check_symlink(ext2_filsys fs,
|
||||||
|
struct ext2_inode *inode, char *buf);
|
||||||
|
|
||||||
|
/* pass2.c */
|
||||||
|
extern int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
|
||||||
|
ext2_ino_t ino, char *buf);
|
||||||
|
|
||||||
|
/* pass3.c */
|
||||||
|
extern int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode);
|
||||||
|
extern errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
|
||||||
|
int num, int gauranteed_size);
|
||||||
|
extern ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix);
|
||||||
|
extern errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino,
|
||||||
|
int adj);
|
||||||
|
|
||||||
|
|
||||||
|
/* region.c */
|
||||||
|
extern region_t region_create(region_addr_t min, region_addr_t max);
|
||||||
|
extern void region_free(region_t region);
|
||||||
|
extern int region_allocate(region_t region, region_addr_t start, int n);
|
||||||
|
|
||||||
|
/* rehash.c */
|
||||||
|
errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino);
|
||||||
|
void e2fsck_rehash_directories(e2fsck_t ctx);
|
||||||
|
|
||||||
|
/* super.c */
|
||||||
|
void check_super_block(e2fsck_t ctx);
|
||||||
|
errcode_t e2fsck_get_device_size(e2fsck_t ctx);
|
||||||
|
|
||||||
|
/* swapfs.c */
|
||||||
|
void swap_filesys(e2fsck_t ctx);
|
||||||
|
|
||||||
|
/* util.c */
|
||||||
|
extern void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
|
||||||
|
const char *description);
|
||||||
|
extern int ask(e2fsck_t ctx, const char * string, int def);
|
||||||
|
extern int ask_yn(const char * string, int def);
|
||||||
|
extern void e2fsck_read_bitmaps(e2fsck_t ctx);
|
||||||
|
extern void e2fsck_write_bitmaps(e2fsck_t ctx);
|
||||||
|
extern void preenhalt(e2fsck_t ctx);
|
||||||
|
extern char *string_copy(e2fsck_t ctx, const char *str, int len);
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
extern void print_resource_track(const char *desc,
|
||||||
|
struct resource_track *track);
|
||||||
|
extern void init_resource_track(struct resource_track *track);
|
||||||
|
#endif
|
||||||
|
extern int inode_has_valid_blocks(struct ext2_inode *inode);
|
||||||
|
extern void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
|
||||||
|
struct ext2_inode * inode, const char * proc);
|
||||||
|
extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
|
||||||
|
struct ext2_inode * inode, const char * proc);
|
||||||
|
#ifdef MTRACE
|
||||||
|
extern void mtrace_print(char *mesg);
|
||||||
|
#endif
|
||||||
|
extern blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
|
||||||
|
const char *name, io_manager manager);
|
||||||
|
extern int ext2_file_type(unsigned int mode);
|
||||||
|
|
||||||
|
/* unix.c */
|
||||||
|
extern void e2fsck_clear_progbar(e2fsck_t ctx);
|
||||||
|
extern int e2fsck_simple_progress(e2fsck_t ctx, const char *label,
|
||||||
|
float percent, unsigned int dpynum);
|
479
e2fsprogs/e2fsck/ea_refcount.c
Normal file
479
e2fsprogs/e2fsck/ea_refcount.c
Normal file
|
@ -0,0 +1,479 @@
|
||||||
|
/*
|
||||||
|
* ea_refcount.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2001 Theodore Ts'o. This file may be
|
||||||
|
* redistributed under the terms of the GNU Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if HAVE_UNISTD_H
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "e2fsck.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The strategy we use for keeping track of EA refcounts is as
|
||||||
|
* follows. We keep a sorted array of first EA blocks and its
|
||||||
|
* reference counts. Once the refcount has dropped to zero, it is
|
||||||
|
* removed from the array to save memory space. Once the EA block is
|
||||||
|
* checked, its bit is set in the block_ea_map bitmap.
|
||||||
|
*/
|
||||||
|
struct ea_refcount_el {
|
||||||
|
blk_t ea_blk;
|
||||||
|
int ea_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ea_refcount {
|
||||||
|
blk_t count;
|
||||||
|
blk_t size;
|
||||||
|
blk_t cursor;
|
||||||
|
struct ea_refcount_el *list;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ea_refcount_free(ext2_refcount_t refcount)
|
||||||
|
{
|
||||||
|
if (!refcount)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (refcount->list)
|
||||||
|
ext2fs_free_mem(&refcount->list);
|
||||||
|
ext2fs_free_mem(&refcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
errcode_t ea_refcount_create(int size, ext2_refcount_t *ret)
|
||||||
|
{
|
||||||
|
ext2_refcount_t refcount;
|
||||||
|
errcode_t retval;
|
||||||
|
size_t bytes;
|
||||||
|
|
||||||
|
retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
memset(refcount, 0, sizeof(struct ea_refcount));
|
||||||
|
|
||||||
|
if (!size)
|
||||||
|
size = 500;
|
||||||
|
refcount->size = size;
|
||||||
|
bytes = (size_t) (size * sizeof(struct ea_refcount_el));
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("Refcount allocated %d entries, %d bytes.\n",
|
||||||
|
refcount->size, bytes);
|
||||||
|
#endif
|
||||||
|
retval = ext2fs_get_mem(bytes, &refcount->list);
|
||||||
|
if (retval)
|
||||||
|
goto errout;
|
||||||
|
memset(refcount->list, 0, bytes);
|
||||||
|
|
||||||
|
refcount->count = 0;
|
||||||
|
refcount->cursor = 0;
|
||||||
|
|
||||||
|
*ret = refcount;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
errout:
|
||||||
|
ea_refcount_free(refcount);
|
||||||
|
return(retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* collapse_refcount() --- go through the refcount array, and get rid
|
||||||
|
* of any count == zero entries
|
||||||
|
*/
|
||||||
|
static void refcount_collapse(ext2_refcount_t refcount)
|
||||||
|
{
|
||||||
|
unsigned int i, j;
|
||||||
|
struct ea_refcount_el *list;
|
||||||
|
|
||||||
|
list = refcount->list;
|
||||||
|
for (i = 0, j = 0; i < refcount->count; i++) {
|
||||||
|
if (list[i].ea_count) {
|
||||||
|
if (i != j)
|
||||||
|
list[j] = list[i];
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if defined(DEBUG) || defined(TEST_PROGRAM)
|
||||||
|
printf("Refcount_collapse: size was %d, now %d\n",
|
||||||
|
refcount->count, j);
|
||||||
|
#endif
|
||||||
|
refcount->count = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* insert_refcount_el() --- Insert a new entry into the sorted list at a
|
||||||
|
* specified position.
|
||||||
|
*/
|
||||||
|
static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount,
|
||||||
|
blk_t blk, int pos)
|
||||||
|
{
|
||||||
|
struct ea_refcount_el *el;
|
||||||
|
errcode_t retval;
|
||||||
|
blk_t new_size = 0;
|
||||||
|
int num;
|
||||||
|
|
||||||
|
if (refcount->count >= refcount->size) {
|
||||||
|
new_size = refcount->size + 100;
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("Reallocating refcount %d entries...\n", new_size);
|
||||||
|
#endif
|
||||||
|
retval = ext2fs_resize_mem((size_t) refcount->size *
|
||||||
|
sizeof(struct ea_refcount_el),
|
||||||
|
(size_t) new_size *
|
||||||
|
sizeof(struct ea_refcount_el),
|
||||||
|
&refcount->list);
|
||||||
|
if (retval)
|
||||||
|
return 0;
|
||||||
|
refcount->size = new_size;
|
||||||
|
}
|
||||||
|
num = (int) refcount->count - pos;
|
||||||
|
if (num < 0)
|
||||||
|
return 0; /* should never happen */
|
||||||
|
if (num) {
|
||||||
|
memmove(&refcount->list[pos+1], &refcount->list[pos],
|
||||||
|
sizeof(struct ea_refcount_el) * num);
|
||||||
|
}
|
||||||
|
refcount->count++;
|
||||||
|
el = &refcount->list[pos];
|
||||||
|
el->ea_count = 0;
|
||||||
|
el->ea_blk = blk;
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_refcount_el() --- given an block number, try to find refcount
|
||||||
|
* information in the sorted list. If the create flag is set,
|
||||||
|
* and we can't find an entry, create one in the sorted list.
|
||||||
|
*/
|
||||||
|
static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount,
|
||||||
|
blk_t blk, int create)
|
||||||
|
{
|
||||||
|
float range;
|
||||||
|
int low, high, mid;
|
||||||
|
blk_t lowval, highval;
|
||||||
|
|
||||||
|
if (!refcount || !refcount->list)
|
||||||
|
return 0;
|
||||||
|
retry:
|
||||||
|
low = 0;
|
||||||
|
high = (int) refcount->count-1;
|
||||||
|
if (create && ((refcount->count == 0) ||
|
||||||
|
(blk > refcount->list[high].ea_blk))) {
|
||||||
|
if (refcount->count >= refcount->size)
|
||||||
|
refcount_collapse(refcount);
|
||||||
|
|
||||||
|
return insert_refcount_el(refcount, blk,
|
||||||
|
(unsigned) refcount->count);
|
||||||
|
}
|
||||||
|
if (refcount->count == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (refcount->cursor >= refcount->count)
|
||||||
|
refcount->cursor = 0;
|
||||||
|
if (blk == refcount->list[refcount->cursor].ea_blk)
|
||||||
|
return &refcount->list[refcount->cursor++];
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf("Non-cursor get_refcount_el: %u\n", blk);
|
||||||
|
#endif
|
||||||
|
while (low <= high) {
|
||||||
|
#if 0
|
||||||
|
mid = (low+high)/2;
|
||||||
|
#else
|
||||||
|
if (low == high)
|
||||||
|
mid = low;
|
||||||
|
else {
|
||||||
|
/* Interpolate for efficiency */
|
||||||
|
lowval = refcount->list[low].ea_blk;
|
||||||
|
highval = refcount->list[high].ea_blk;
|
||||||
|
|
||||||
|
if (blk < lowval)
|
||||||
|
range = 0;
|
||||||
|
else if (blk > highval)
|
||||||
|
range = 1;
|
||||||
|
else
|
||||||
|
range = ((float) (blk - lowval)) /
|
||||||
|
(highval - lowval);
|
||||||
|
mid = low + ((int) (range * (high-low)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (blk == refcount->list[mid].ea_blk) {
|
||||||
|
refcount->cursor = mid+1;
|
||||||
|
return &refcount->list[mid];
|
||||||
|
}
|
||||||
|
if (blk < refcount->list[mid].ea_blk)
|
||||||
|
high = mid-1;
|
||||||
|
else
|
||||||
|
low = mid+1;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If we need to create a new entry, it should be right at
|
||||||
|
* low (where high will be left at low-1).
|
||||||
|
*/
|
||||||
|
if (create) {
|
||||||
|
if (refcount->count >= refcount->size) {
|
||||||
|
refcount_collapse(refcount);
|
||||||
|
if (refcount->count < refcount->size)
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
return insert_refcount_el(refcount, blk, low);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
errcode_t ea_refcount_fetch(ext2_refcount_t refcount, blk_t blk,
|
||||||
|
int *ret)
|
||||||
|
{
|
||||||
|
struct ea_refcount_el *el;
|
||||||
|
|
||||||
|
el = get_refcount_el(refcount, blk, 0);
|
||||||
|
if (!el) {
|
||||||
|
*ret = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*ret = el->ea_count;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
errcode_t ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret)
|
||||||
|
{
|
||||||
|
struct ea_refcount_el *el;
|
||||||
|
|
||||||
|
el = get_refcount_el(refcount, blk, 1);
|
||||||
|
if (!el)
|
||||||
|
return EXT2_ET_NO_MEMORY;
|
||||||
|
el->ea_count++;
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
*ret = el->ea_count;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
errcode_t ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret)
|
||||||
|
{
|
||||||
|
struct ea_refcount_el *el;
|
||||||
|
|
||||||
|
el = get_refcount_el(refcount, blk, 0);
|
||||||
|
if (!el || el->ea_count == 0)
|
||||||
|
return EXT2_ET_INVALID_ARGUMENT;
|
||||||
|
|
||||||
|
el->ea_count--;
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
*ret = el->ea_count;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
errcode_t ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count)
|
||||||
|
{
|
||||||
|
struct ea_refcount_el *el;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the refcount element
|
||||||
|
*/
|
||||||
|
el = get_refcount_el(refcount, blk, count ? 1 : 0);
|
||||||
|
if (!el)
|
||||||
|
return count ? EXT2_ET_NO_MEMORY : 0;
|
||||||
|
el->ea_count = count;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blk_t ext2fs_get_refcount_size(ext2_refcount_t refcount)
|
||||||
|
{
|
||||||
|
if (!refcount)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return refcount->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ea_refcount_intr_begin(ext2_refcount_t refcount)
|
||||||
|
{
|
||||||
|
refcount->cursor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
blk_t ea_refcount_intr_next(ext2_refcount_t refcount,
|
||||||
|
int *ret)
|
||||||
|
{
|
||||||
|
struct ea_refcount_el *list;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (refcount->cursor >= refcount->count)
|
||||||
|
return 0;
|
||||||
|
list = refcount->list;
|
||||||
|
if (list[refcount->cursor].ea_count) {
|
||||||
|
if (ret)
|
||||||
|
*ret = list[refcount->cursor].ea_count;
|
||||||
|
return list[refcount->cursor++].ea_blk;
|
||||||
|
}
|
||||||
|
refcount->cursor++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef TEST_PROGRAM
|
||||||
|
|
||||||
|
errcode_t ea_refcount_validate(ext2_refcount_t refcount, FILE *out)
|
||||||
|
{
|
||||||
|
errcode_t ret = 0;
|
||||||
|
int i;
|
||||||
|
const char *bad = "bad refcount";
|
||||||
|
|
||||||
|
if (refcount->count > refcount->size) {
|
||||||
|
fprintf(out, "%s: count > size\n", bad);
|
||||||
|
return EXT2_ET_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
for (i=1; i < refcount->count; i++) {
|
||||||
|
if (refcount->list[i-1].ea_blk >= refcount->list[i].ea_blk) {
|
||||||
|
fprintf(out, "%s: list[%d].blk=%u, list[%d].blk=%u\n",
|
||||||
|
bad, i-1, refcount->list[i-1].ea_blk,
|
||||||
|
i, refcount->list[i].ea_blk);
|
||||||
|
ret = EXT2_ET_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BCODE_END 0
|
||||||
|
#define BCODE_CREATE 1
|
||||||
|
#define BCODE_FREE 2
|
||||||
|
#define BCODE_STORE 3
|
||||||
|
#define BCODE_INCR 4
|
||||||
|
#define BCODE_DECR 5
|
||||||
|
#define BCODE_FETCH 6
|
||||||
|
#define BCODE_VALIDATE 7
|
||||||
|
#define BCODE_LIST 8
|
||||||
|
#define BCODE_COLLAPSE 9
|
||||||
|
|
||||||
|
int bcode_program[] = {
|
||||||
|
BCODE_CREATE, 5,
|
||||||
|
BCODE_STORE, 3, 3,
|
||||||
|
BCODE_STORE, 4, 4,
|
||||||
|
BCODE_STORE, 1, 1,
|
||||||
|
BCODE_STORE, 8, 8,
|
||||||
|
BCODE_STORE, 2, 2,
|
||||||
|
BCODE_STORE, 4, 0,
|
||||||
|
BCODE_STORE, 2, 0,
|
||||||
|
BCODE_STORE, 6, 6,
|
||||||
|
BCODE_VALIDATE,
|
||||||
|
BCODE_STORE, 4, 4,
|
||||||
|
BCODE_STORE, 2, 2,
|
||||||
|
BCODE_FETCH, 1,
|
||||||
|
BCODE_FETCH, 2,
|
||||||
|
BCODE_INCR, 3,
|
||||||
|
BCODE_INCR, 3,
|
||||||
|
BCODE_DECR, 4,
|
||||||
|
BCODE_STORE, 4, 4,
|
||||||
|
BCODE_VALIDATE,
|
||||||
|
BCODE_STORE, 20, 20,
|
||||||
|
BCODE_STORE, 40, 40,
|
||||||
|
BCODE_STORE, 30, 30,
|
||||||
|
BCODE_STORE, 10, 10,
|
||||||
|
BCODE_DECR, 30,
|
||||||
|
BCODE_FETCH, 30,
|
||||||
|
BCODE_DECR, 2,
|
||||||
|
BCODE_DECR, 2,
|
||||||
|
BCODE_COLLAPSE,
|
||||||
|
BCODE_LIST,
|
||||||
|
BCODE_VALIDATE,
|
||||||
|
BCODE_FREE,
|
||||||
|
BCODE_END
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
ext2_refcount_t refcount;
|
||||||
|
int size, arg;
|
||||||
|
blk_t blk;
|
||||||
|
errcode_t retval;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
switch (bcode_program[i++]) {
|
||||||
|
case BCODE_END:
|
||||||
|
exit(0);
|
||||||
|
case BCODE_CREATE:
|
||||||
|
size = bcode_program[i++];
|
||||||
|
retval = ea_refcount_create(size, &refcount);
|
||||||
|
if (retval) {
|
||||||
|
com_err("ea_refcount_create",
|
||||||
|
retval, "");
|
||||||
|
exit(1);
|
||||||
|
} else
|
||||||
|
printf("Creating refcount with size %d\n",
|
||||||
|
size);
|
||||||
|
break;
|
||||||
|
case BCODE_FREE:
|
||||||
|
ea_refcount_free(refcount);
|
||||||
|
refcount = 0;
|
||||||
|
printf("Freeing refcount\n");
|
||||||
|
break;
|
||||||
|
case BCODE_STORE:
|
||||||
|
blk = (blk_t) bcode_program[i++];
|
||||||
|
arg = bcode_program[i++];
|
||||||
|
retval = ea_refcount_store(refcount, blk, arg);
|
||||||
|
printf("Storing blk %u with value %d\n", blk, arg);
|
||||||
|
if (retval)
|
||||||
|
com_err("ea_refcount_store", retval, "");
|
||||||
|
break;
|
||||||
|
case BCODE_FETCH:
|
||||||
|
blk = (blk_t) bcode_program[i++];
|
||||||
|
retval = ea_refcount_fetch(refcount, blk, &arg);
|
||||||
|
if (retval)
|
||||||
|
com_err("ea_refcount_fetch", retval, "");
|
||||||
|
else
|
||||||
|
printf("bcode_fetch(%u) returns %d\n",
|
||||||
|
blk, arg);
|
||||||
|
break;
|
||||||
|
case BCODE_INCR:
|
||||||
|
blk = (blk_t) bcode_program[i++];
|
||||||
|
retval = ea_refcount_increment(refcount, blk,
|
||||||
|
&arg);
|
||||||
|
if (retval)
|
||||||
|
com_err("ea_refcount_increment", retval,
|
||||||
|
"");
|
||||||
|
else
|
||||||
|
printf("bcode_increment(%u) returns %d\n",
|
||||||
|
blk, arg);
|
||||||
|
break;
|
||||||
|
case BCODE_DECR:
|
||||||
|
blk = (blk_t) bcode_program[i++];
|
||||||
|
retval = ea_refcount_decrement(refcount, blk,
|
||||||
|
&arg);
|
||||||
|
if (retval)
|
||||||
|
com_err("ea_refcount_decrement", retval,
|
||||||
|
"while decrementing blk %u", blk);
|
||||||
|
else
|
||||||
|
printf("bcode_decrement(%u) returns %d\n",
|
||||||
|
blk, arg);
|
||||||
|
break;
|
||||||
|
case BCODE_VALIDATE:
|
||||||
|
retval = ea_refcount_validate(refcount, stderr);
|
||||||
|
if (retval)
|
||||||
|
com_err("ea_refcount_validate",
|
||||||
|
retval, "");
|
||||||
|
else
|
||||||
|
printf("Refcount validation OK.\n");
|
||||||
|
break;
|
||||||
|
case BCODE_LIST:
|
||||||
|
ea_refcount_intr_begin(refcount);
|
||||||
|
while (1) {
|
||||||
|
blk = ea_refcount_intr_next(refcount,
|
||||||
|
&arg);
|
||||||
|
if (!blk)
|
||||||
|
break;
|
||||||
|
printf("\tblk=%u, count=%d\n", blk,
|
||||||
|
arg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BCODE_COLLAPSE:
|
||||||
|
refcount_collapse(refcount);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
124
e2fsprogs/e2fsck/ehandler.c
Normal file
124
e2fsprogs/e2fsck/ehandler.c
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
* ehandler.c --- handle bad block errors which come up during the
|
||||||
|
* course of an e2fsck session.
|
||||||
|
*
|
||||||
|
* Copyright (C) 1994 Theodore Ts'o. This file may be redistributed
|
||||||
|
* under the terms of the GNU Public License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
#include "e2fsck.h"
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
static const char *operation;
|
||||||
|
|
||||||
|
static errcode_t e2fsck_handle_read_error(io_channel channel,
|
||||||
|
unsigned long block,
|
||||||
|
int count,
|
||||||
|
void *data,
|
||||||
|
size_t size EXT2FS_ATTR((unused)),
|
||||||
|
int actual EXT2FS_ATTR((unused)),
|
||||||
|
errcode_t error)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *p;
|
||||||
|
ext2_filsys fs = (ext2_filsys) channel->app_data;
|
||||||
|
e2fsck_t ctx;
|
||||||
|
|
||||||
|
ctx = (e2fsck_t) fs->priv_data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If more than one block was read, try reading each block
|
||||||
|
* separately. We could use the actual bytes read to figure
|
||||||
|
* out where to start, but we don't bother.
|
||||||
|
*/
|
||||||
|
if (count > 1) {
|
||||||
|
p = (char *) data;
|
||||||
|
for (i=0; i < count; i++, p += channel->block_size, block++) {
|
||||||
|
error = io_channel_read_blk(channel, block,
|
||||||
|
1, p);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (operation)
|
||||||
|
printf(_("Error reading block %lu (%s) while %s. "), block,
|
||||||
|
error_message(error), operation);
|
||||||
|
else
|
||||||
|
printf(_("Error reading block %lu (%s). "), block,
|
||||||
|
error_message(error));
|
||||||
|
preenhalt(ctx);
|
||||||
|
if (ask(ctx, _("Ignore error"), 1)) {
|
||||||
|
if (ask(ctx, _("Force rewrite"), 1))
|
||||||
|
io_channel_write_blk(channel, block, 1, data);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static errcode_t e2fsck_handle_write_error(io_channel channel,
|
||||||
|
unsigned long block,
|
||||||
|
int count,
|
||||||
|
const void *data,
|
||||||
|
size_t size EXT2FS_ATTR((unused)),
|
||||||
|
int actual EXT2FS_ATTR((unused)),
|
||||||
|
errcode_t error)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const char *p;
|
||||||
|
ext2_filsys fs = (ext2_filsys) channel->app_data;
|
||||||
|
e2fsck_t ctx;
|
||||||
|
|
||||||
|
ctx = (e2fsck_t) fs->priv_data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If more than one block was written, try writing each block
|
||||||
|
* separately. We could use the actual bytes read to figure
|
||||||
|
* out where to start, but we don't bother.
|
||||||
|
*/
|
||||||
|
if (count > 1) {
|
||||||
|
p = (const char *) data;
|
||||||
|
for (i=0; i < count; i++, p += channel->block_size, block++) {
|
||||||
|
error = io_channel_write_blk(channel, block,
|
||||||
|
1, p);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation)
|
||||||
|
printf(_("Error writing block %lu (%s) while %s. "), block,
|
||||||
|
error_message(error), operation);
|
||||||
|
else
|
||||||
|
printf(_("Error writing block %lu (%s). "), block,
|
||||||
|
error_message(error));
|
||||||
|
preenhalt(ctx);
|
||||||
|
if (ask(ctx, _("Ignore error"), 1))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ehandler_operation(const char *op)
|
||||||
|
{
|
||||||
|
const char *ret = operation;
|
||||||
|
|
||||||
|
operation = op;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ehandler_init(io_channel channel)
|
||||||
|
{
|
||||||
|
channel->read_error = e2fsck_handle_read_error;
|
||||||
|
channel->write_error = e2fsck_handle_write_error;
|
||||||
|
}
|
120
e2fsprogs/e2fsck/jfs_user.h
Normal file
120
e2fsprogs/e2fsck/jfs_user.h
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
* Compatibility header file for e2fsck which should be included
|
||||||
|
* instead of linux/jfs.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2000 Stephen C. Tweedie
|
||||||
|
*
|
||||||
|
* This file may be redistributed under the terms of the
|
||||||
|
* GNU General Public License version 2 or at your discretion
|
||||||
|
* any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pull in the definition of the e2fsck context structure
|
||||||
|
*/
|
||||||
|
#include "e2fsck.h"
|
||||||
|
|
||||||
|
struct buffer_head {
|
||||||
|
char b_data[8192];
|
||||||
|
e2fsck_t b_ctx;
|
||||||
|
io_channel b_io;
|
||||||
|
int b_size;
|
||||||
|
blk_t b_blocknr;
|
||||||
|
int b_dirty;
|
||||||
|
int b_uptodate;
|
||||||
|
int b_err;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct inode {
|
||||||
|
e2fsck_t i_ctx;
|
||||||
|
ext2_ino_t i_ino;
|
||||||
|
struct ext2_inode i_ext2;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kdev_s {
|
||||||
|
e2fsck_t k_ctx;
|
||||||
|
int k_dev;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define K_DEV_FS 1
|
||||||
|
#define K_DEV_JOURNAL 2
|
||||||
|
|
||||||
|
typedef struct kdev_s *kdev_t;
|
||||||
|
|
||||||
|
#define lock_buffer(bh) do {} while(0)
|
||||||
|
#define unlock_buffer(bh) do {} while(0)
|
||||||
|
#define buffer_req(bh) 1
|
||||||
|
#define do_readahead(journal, start) do {} while(0)
|
||||||
|
|
||||||
|
extern e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int object_length;
|
||||||
|
} kmem_cache_t;
|
||||||
|
|
||||||
|
#define kmem_cache_alloc(cache,flags) malloc((cache)->object_length)
|
||||||
|
#define kmem_cache_free(cache,obj) free(obj)
|
||||||
|
#define kmem_cache_create(name,len,a,b,c,d) do_cache_create(len)
|
||||||
|
#define kmem_cache_destroy(cache) do_cache_destroy(cache)
|
||||||
|
#define kmalloc(len,flags) malloc(len)
|
||||||
|
#define kfree(p) free(p)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We use the standard libext2fs portability tricks for inline
|
||||||
|
* functions.
|
||||||
|
*/
|
||||||
|
extern kmem_cache_t * do_cache_create(int len);
|
||||||
|
extern void do_cache_destroy(kmem_cache_t *cache);
|
||||||
|
|
||||||
|
#if (defined(E2FSCK_INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
|
||||||
|
#ifdef E2FSCK_INCLUDE_INLINE_FUNCS
|
||||||
|
#define _INLINE_ extern
|
||||||
|
#else
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define _INLINE_ extern __inline__
|
||||||
|
#else /* For Watcom C */
|
||||||
|
#define _INLINE_ extern inline
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_INLINE_ kmem_cache_t * do_cache_create(int len)
|
||||||
|
{
|
||||||
|
kmem_cache_t *new_cache;
|
||||||
|
new_cache = malloc(sizeof(*new_cache));
|
||||||
|
if (new_cache)
|
||||||
|
new_cache->object_length = len;
|
||||||
|
return new_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
_INLINE_ void do_cache_destroy(kmem_cache_t *cache)
|
||||||
|
{
|
||||||
|
free(cache);
|
||||||
|
}
|
||||||
|
#undef _INLINE_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define __init
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now pull in the real linux/jfs.h definitions.
|
||||||
|
*/
|
||||||
|
#include <ext2fs/kernel-jbd.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Kernel compatibility functions are defined in journal.c
|
||||||
|
*/
|
||||||
|
int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys);
|
||||||
|
struct buffer_head *getblk(kdev_t ctx, blk_t blocknr, int blocksize);
|
||||||
|
void sync_blockdev(kdev_t kdev);
|
||||||
|
void ll_rw_block(int rw, int dummy, struct buffer_head *bh[]);
|
||||||
|
void mark_buffer_dirty(struct buffer_head *bh);
|
||||||
|
void mark_buffer_uptodate(struct buffer_head *bh, int val);
|
||||||
|
void brelse(struct buffer_head *bh);
|
||||||
|
int buffer_uptodate(struct buffer_head *bh);
|
||||||
|
void wait_on_buffer(struct buffer_head *bh);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define newer 2.5 interfaces
|
||||||
|
*/
|
||||||
|
#define __getblk(dev, blocknr, blocksize) getblk(dev, blocknr, blocksize)
|
||||||
|
#define set_buffer_uptodate(bh) mark_buffer_uptodate(bh, 1)
|
960
e2fsprogs/e2fsck/journal.c
Normal file
960
e2fsprogs/e2fsck/journal.c
Normal file
|
@ -0,0 +1,960 @@
|
||||||
|
/*
|
||||||
|
* journal.c --- code for handling the "ext3" journal
|
||||||
|
*
|
||||||
|
* Copyright (C) 2000 Andreas Dilger
|
||||||
|
* Copyright (C) 2000 Theodore Ts'o
|
||||||
|
*
|
||||||
|
* Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
|
||||||
|
* Copyright (C) 1999 Red Hat Software
|
||||||
|
*
|
||||||
|
* This file may be redistributed under the terms of the
|
||||||
|
* GNU General Public License version 2 or at your discretion
|
||||||
|
* any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_MOUNT_H
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#define MNT_FL (MS_MGC_VAL | MS_RDONLY)
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_SYS_STAT_H
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define E2FSCK_INCLUDE_INLINE_FUNCS
|
||||||
|
#include "jfs_user.h"
|
||||||
|
#include "problem.h"
|
||||||
|
#include "uuid/uuid.h"
|
||||||
|
|
||||||
|
#ifdef __CONFIG_JBD_DEBUG__E2FS /* Enabled by configure --enable-jfs-debug */
|
||||||
|
static int bh_count = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths.
|
||||||
|
* This creates a larger static binary, and a smaller binary using
|
||||||
|
* shared libraries. It's also probably slightly less CPU-efficient,
|
||||||
|
* which is why it's not on by default. But, it's a good way of
|
||||||
|
* testing the functions in inode_io.c and fileio.c.
|
||||||
|
*/
|
||||||
|
#undef USE_INODE_IO
|
||||||
|
|
||||||
|
/* Kernel compatibility functions for handling the journal. These allow us
|
||||||
|
* to use the recovery.c file virtually unchanged from the kernel, so we
|
||||||
|
* don't have to do much to keep kernel and user recovery in sync.
|
||||||
|
*/
|
||||||
|
int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
|
||||||
|
{
|
||||||
|
#ifdef USE_INODE_IO
|
||||||
|
*phys = block;
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
struct inode *inode = journal->j_inode;
|
||||||
|
errcode_t retval;
|
||||||
|
blk_t pblk;
|
||||||
|
|
||||||
|
if (!inode) {
|
||||||
|
*phys = block;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino,
|
||||||
|
&inode->i_ext2, NULL, 0, block, &pblk);
|
||||||
|
*phys = pblk;
|
||||||
|
return (retval);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize)
|
||||||
|
{
|
||||||
|
struct buffer_head *bh;
|
||||||
|
|
||||||
|
bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer");
|
||||||
|
if (!bh)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
jfs_debug(4, "getblk for block %lu (%d bytes)(total %d)\n",
|
||||||
|
(unsigned long) blocknr, blocksize, ++bh_count);
|
||||||
|
|
||||||
|
bh->b_ctx = kdev->k_ctx;
|
||||||
|
if (kdev->k_dev == K_DEV_FS)
|
||||||
|
bh->b_io = kdev->k_ctx->fs->io;
|
||||||
|
else
|
||||||
|
bh->b_io = kdev->k_ctx->journal_io;
|
||||||
|
bh->b_size = blocksize;
|
||||||
|
bh->b_blocknr = blocknr;
|
||||||
|
|
||||||
|
return bh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sync_blockdev(kdev_t kdev)
|
||||||
|
{
|
||||||
|
io_channel io;
|
||||||
|
|
||||||
|
if (kdev->k_dev == K_DEV_FS)
|
||||||
|
io = kdev->k_ctx->fs->io;
|
||||||
|
else
|
||||||
|
io = kdev->k_ctx->journal_io;
|
||||||
|
|
||||||
|
io_channel_flush(io);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
struct buffer_head *bh;
|
||||||
|
|
||||||
|
for (; nr > 0; --nr) {
|
||||||
|
bh = *bhp++;
|
||||||
|
if (rw == READ && !bh->b_uptodate) {
|
||||||
|
jfs_debug(3, "reading block %lu/%p\n",
|
||||||
|
(unsigned long) bh->b_blocknr, (void *) bh);
|
||||||
|
retval = io_channel_read_blk(bh->b_io,
|
||||||
|
bh->b_blocknr,
|
||||||
|
1, bh->b_data);
|
||||||
|
if (retval) {
|
||||||
|
com_err(bh->b_ctx->device_name, retval,
|
||||||
|
"while reading block %lu\n",
|
||||||
|
(unsigned long) bh->b_blocknr);
|
||||||
|
bh->b_err = retval;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bh->b_uptodate = 1;
|
||||||
|
} else if (rw == WRITE && bh->b_dirty) {
|
||||||
|
jfs_debug(3, "writing block %lu/%p\n",
|
||||||
|
(unsigned long) bh->b_blocknr, (void *) bh);
|
||||||
|
retval = io_channel_write_blk(bh->b_io,
|
||||||
|
bh->b_blocknr,
|
||||||
|
1, bh->b_data);
|
||||||
|
if (retval) {
|
||||||
|
com_err(bh->b_ctx->device_name, retval,
|
||||||
|
"while writing block %lu\n",
|
||||||
|
(unsigned long) bh->b_blocknr);
|
||||||
|
bh->b_err = retval;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bh->b_dirty = 0;
|
||||||
|
bh->b_uptodate = 1;
|
||||||
|
} else {
|
||||||
|
jfs_debug(3, "no-op %s for block %lu\n",
|
||||||
|
rw == READ ? "read" : "write",
|
||||||
|
(unsigned long) bh->b_blocknr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mark_buffer_dirty(struct buffer_head *bh)
|
||||||
|
{
|
||||||
|
bh->b_dirty = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mark_buffer_clean(struct buffer_head * bh)
|
||||||
|
{
|
||||||
|
bh->b_dirty = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void brelse(struct buffer_head *bh)
|
||||||
|
{
|
||||||
|
if (bh->b_dirty)
|
||||||
|
ll_rw_block(WRITE, 1, &bh);
|
||||||
|
jfs_debug(3, "freeing block %lu/%p (total %d)\n",
|
||||||
|
(unsigned long) bh->b_blocknr, (void *) bh, --bh_count);
|
||||||
|
ext2fs_free_mem(&bh);
|
||||||
|
}
|
||||||
|
|
||||||
|
int buffer_uptodate(struct buffer_head *bh)
|
||||||
|
{
|
||||||
|
return bh->b_uptodate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mark_buffer_uptodate(struct buffer_head *bh, int val)
|
||||||
|
{
|
||||||
|
bh->b_uptodate = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_on_buffer(struct buffer_head *bh)
|
||||||
|
{
|
||||||
|
if (!bh->b_uptodate)
|
||||||
|
ll_rw_block(READ, 1, &bh);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void e2fsck_clear_recover(e2fsck_t ctx, int error)
|
||||||
|
{
|
||||||
|
ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
|
||||||
|
|
||||||
|
/* if we had an error doing journal recovery, we need a full fsck */
|
||||||
|
if (error)
|
||||||
|
ctx->fs->super->s_state &= ~EXT2_VALID_FS;
|
||||||
|
ext2fs_mark_super_dirty(ctx->fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
|
||||||
|
{
|
||||||
|
struct ext2_super_block *sb = ctx->fs->super;
|
||||||
|
struct ext2_super_block jsuper;
|
||||||
|
struct problem_context pctx;
|
||||||
|
struct buffer_head *bh;
|
||||||
|
struct inode *j_inode = NULL;
|
||||||
|
struct kdev_s *dev_fs = NULL, *dev_journal;
|
||||||
|
const char *journal_name = 0;
|
||||||
|
journal_t *journal = NULL;
|
||||||
|
errcode_t retval = 0;
|
||||||
|
io_manager io_ptr = 0;
|
||||||
|
unsigned long start = 0;
|
||||||
|
blk_t blk;
|
||||||
|
int ext_journal = 0;
|
||||||
|
int tried_backup_jnl = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
|
||||||
|
if (!journal) {
|
||||||
|
return EXT2_ET_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev");
|
||||||
|
if (!dev_fs) {
|
||||||
|
retval = EXT2_ET_NO_MEMORY;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
dev_journal = dev_fs+1;
|
||||||
|
|
||||||
|
dev_fs->k_ctx = dev_journal->k_ctx = ctx;
|
||||||
|
dev_fs->k_dev = K_DEV_FS;
|
||||||
|
dev_journal->k_dev = K_DEV_JOURNAL;
|
||||||
|
|
||||||
|
journal->j_dev = dev_journal;
|
||||||
|
journal->j_fs_dev = dev_fs;
|
||||||
|
journal->j_inode = NULL;
|
||||||
|
journal->j_blocksize = ctx->fs->blocksize;
|
||||||
|
|
||||||
|
if (uuid_is_null(sb->s_journal_uuid)) {
|
||||||
|
if (!sb->s_journal_inum)
|
||||||
|
return EXT2_ET_BAD_INODE_NUM;
|
||||||
|
j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode),
|
||||||
|
"journal inode");
|
||||||
|
if (!j_inode) {
|
||||||
|
retval = EXT2_ET_NO_MEMORY;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
j_inode->i_ctx = ctx;
|
||||||
|
j_inode->i_ino = sb->s_journal_inum;
|
||||||
|
|
||||||
|
if ((retval = ext2fs_read_inode(ctx->fs,
|
||||||
|
sb->s_journal_inum,
|
||||||
|
&j_inode->i_ext2))) {
|
||||||
|
try_backup_journal:
|
||||||
|
if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS ||
|
||||||
|
tried_backup_jnl)
|
||||||
|
goto errout;
|
||||||
|
memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode));
|
||||||
|
memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks,
|
||||||
|
EXT2_N_BLOCKS*4);
|
||||||
|
j_inode->i_ext2.i_size = sb->s_jnl_blocks[16];
|
||||||
|
j_inode->i_ext2.i_links_count = 1;
|
||||||
|
j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600;
|
||||||
|
tried_backup_jnl++;
|
||||||
|
}
|
||||||
|
if (!j_inode->i_ext2.i_links_count ||
|
||||||
|
!LINUX_S_ISREG(j_inode->i_ext2.i_mode)) {
|
||||||
|
retval = EXT2_ET_NO_JOURNAL;
|
||||||
|
goto try_backup_journal;
|
||||||
|
}
|
||||||
|
if (j_inode->i_ext2.i_size / journal->j_blocksize <
|
||||||
|
JFS_MIN_JOURNAL_BLOCKS) {
|
||||||
|
retval = EXT2_ET_JOURNAL_TOO_SMALL;
|
||||||
|
goto try_backup_journal;
|
||||||
|
}
|
||||||
|
for (i=0; i < EXT2_N_BLOCKS; i++) {
|
||||||
|
blk = j_inode->i_ext2.i_block[i];
|
||||||
|
if (!blk) {
|
||||||
|
if (i < EXT2_NDIR_BLOCKS) {
|
||||||
|
retval = EXT2_ET_JOURNAL_TOO_SMALL;
|
||||||
|
goto try_backup_journal;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (blk < sb->s_first_data_block ||
|
||||||
|
blk >= sb->s_blocks_count) {
|
||||||
|
retval = EXT2_ET_BAD_BLOCK_NUM;
|
||||||
|
goto try_backup_journal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize;
|
||||||
|
|
||||||
|
#ifdef USE_INODE_IO
|
||||||
|
retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum,
|
||||||
|
&j_inode->i_ext2,
|
||||||
|
&journal_name);
|
||||||
|
if (retval)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
io_ptr = inode_io_manager;
|
||||||
|
#else
|
||||||
|
journal->j_inode = j_inode;
|
||||||
|
ctx->journal_io = ctx->fs->io;
|
||||||
|
if ((retval = journal_bmap(journal, 0, &start)) != 0)
|
||||||
|
goto errout;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
ext_journal = 1;
|
||||||
|
if (!ctx->journal_name) {
|
||||||
|
char uuid[37];
|
||||||
|
|
||||||
|
uuid_unparse(sb->s_journal_uuid, uuid);
|
||||||
|
ctx->journal_name = blkid_get_devname(ctx->blkid,
|
||||||
|
"UUID", uuid);
|
||||||
|
if (!ctx->journal_name)
|
||||||
|
ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev);
|
||||||
|
}
|
||||||
|
journal_name = ctx->journal_name;
|
||||||
|
|
||||||
|
if (!journal_name) {
|
||||||
|
fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx);
|
||||||
|
return EXT2_ET_LOAD_EXT_JOURNAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
jfs_debug(1, "Using journal file %s\n", journal_name);
|
||||||
|
io_ptr = unix_io_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
test_io_backing_manager = io_ptr;
|
||||||
|
io_ptr = test_io_manager;
|
||||||
|
#endif
|
||||||
|
#ifndef USE_INODE_IO
|
||||||
|
if (ext_journal)
|
||||||
|
#endif
|
||||||
|
retval = io_ptr->open(journal_name, IO_FLAG_RW,
|
||||||
|
&ctx->journal_io);
|
||||||
|
if (retval)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize);
|
||||||
|
|
||||||
|
if (ext_journal) {
|
||||||
|
if (ctx->fs->blocksize == 1024)
|
||||||
|
start = 1;
|
||||||
|
bh = getblk(dev_journal, start, ctx->fs->blocksize);
|
||||||
|
if (!bh) {
|
||||||
|
retval = EXT2_ET_NO_MEMORY;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
ll_rw_block(READ, 1, &bh);
|
||||||
|
if ((retval = bh->b_err) != 0)
|
||||||
|
goto errout;
|
||||||
|
memcpy(&jsuper, start ? bh->b_data : bh->b_data + 1024,
|
||||||
|
sizeof(jsuper));
|
||||||
|
brelse(bh);
|
||||||
|
#ifdef EXT2FS_ENABLE_SWAPFS
|
||||||
|
if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
|
||||||
|
ext2fs_swap_super(&jsuper);
|
||||||
|
#endif
|
||||||
|
if (jsuper.s_magic != EXT2_SUPER_MAGIC ||
|
||||||
|
!(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
|
||||||
|
fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx);
|
||||||
|
retval = EXT2_ET_LOAD_EXT_JOURNAL;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
/* Make sure the journal UUID is correct */
|
||||||
|
if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid,
|
||||||
|
sizeof(jsuper.s_uuid))) {
|
||||||
|
fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx);
|
||||||
|
retval = EXT2_ET_LOAD_EXT_JOURNAL;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
journal->j_maxlen = jsuper.s_blocks_count;
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) {
|
||||||
|
retval = EXT2_ET_NO_MEMORY;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
journal->j_sb_buffer = bh;
|
||||||
|
journal->j_superblock = (journal_superblock_t *)bh->b_data;
|
||||||
|
|
||||||
|
#ifdef USE_INODE_IO
|
||||||
|
if (j_inode)
|
||||||
|
ext2fs_free_mem(&j_inode);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*ret_journal = journal;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
errout:
|
||||||
|
if (dev_fs)
|
||||||
|
ext2fs_free_mem(&dev_fs);
|
||||||
|
if (j_inode)
|
||||||
|
ext2fs_free_mem(&j_inode);
|
||||||
|
if (journal)
|
||||||
|
ext2fs_free_mem(&journal);
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
|
||||||
|
struct problem_context *pctx)
|
||||||
|
{
|
||||||
|
struct ext2_super_block *sb = ctx->fs->super;
|
||||||
|
int recover = ctx->fs->super->s_feature_incompat &
|
||||||
|
EXT3_FEATURE_INCOMPAT_RECOVER;
|
||||||
|
int has_journal = ctx->fs->super->s_feature_compat &
|
||||||
|
EXT3_FEATURE_COMPAT_HAS_JOURNAL;
|
||||||
|
|
||||||
|
if (has_journal || sb->s_journal_inum) {
|
||||||
|
/* The journal inode is bogus, remove and force full fsck */
|
||||||
|
pctx->ino = sb->s_journal_inum;
|
||||||
|
if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
|
||||||
|
if (has_journal && sb->s_journal_inum)
|
||||||
|
printf("*** ext3 journal has been deleted - "
|
||||||
|
"filesystem is now ext2 only ***\n\n");
|
||||||
|
sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
|
||||||
|
sb->s_journal_inum = 0;
|
||||||
|
ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */
|
||||||
|
e2fsck_clear_recover(ctx, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return EXT2_ET_BAD_INODE_NUM;
|
||||||
|
} else if (recover) {
|
||||||
|
if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
|
||||||
|
e2fsck_clear_recover(ctx, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return EXT2_ET_UNSUPP_FEATURE;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define V1_SB_SIZE 0x0024
|
||||||
|
static void clear_v2_journal_fields(journal_t *journal)
|
||||||
|
{
|
||||||
|
e2fsck_t ctx = journal->j_dev->k_ctx;
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx))
|
||||||
|
return;
|
||||||
|
|
||||||
|
memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0,
|
||||||
|
ctx->fs->blocksize-V1_SB_SIZE);
|
||||||
|
mark_buffer_dirty(journal->j_sb_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static errcode_t e2fsck_journal_load(journal_t *journal)
|
||||||
|
{
|
||||||
|
e2fsck_t ctx = journal->j_dev->k_ctx;
|
||||||
|
journal_superblock_t *jsb;
|
||||||
|
struct buffer_head *jbh = journal->j_sb_buffer;
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
ll_rw_block(READ, 1, &jbh);
|
||||||
|
if (jbh->b_err) {
|
||||||
|
com_err(ctx->device_name, jbh->b_err,
|
||||||
|
_("reading journal superblock\n"));
|
||||||
|
return jbh->b_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
jsb = journal->j_superblock;
|
||||||
|
/* If we don't even have JFS_MAGIC, we probably have a wrong inode */
|
||||||
|
if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
|
||||||
|
return e2fsck_journal_fix_bad_inode(ctx, &pctx);
|
||||||
|
|
||||||
|
switch (ntohl(jsb->s_header.h_blocktype)) {
|
||||||
|
case JFS_SUPERBLOCK_V1:
|
||||||
|
journal->j_format_version = 1;
|
||||||
|
if (jsb->s_feature_compat ||
|
||||||
|
jsb->s_feature_incompat ||
|
||||||
|
jsb->s_feature_ro_compat ||
|
||||||
|
jsb->s_nr_users)
|
||||||
|
clear_v2_journal_fields(journal);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case JFS_SUPERBLOCK_V2:
|
||||||
|
journal->j_format_version = 2;
|
||||||
|
if (ntohl(jsb->s_nr_users) > 1 &&
|
||||||
|
uuid_is_null(ctx->fs->super->s_journal_uuid))
|
||||||
|
clear_v2_journal_fields(journal);
|
||||||
|
if (ntohl(jsb->s_nr_users) > 1) {
|
||||||
|
fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx);
|
||||||
|
return EXT2_ET_JOURNAL_UNSUPP_VERSION;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These should never appear in a journal super block, so if
|
||||||
|
* they do, the journal is badly corrupted.
|
||||||
|
*/
|
||||||
|
case JFS_DESCRIPTOR_BLOCK:
|
||||||
|
case JFS_COMMIT_BLOCK:
|
||||||
|
case JFS_REVOKE_BLOCK:
|
||||||
|
return EXT2_ET_CORRUPT_SUPERBLOCK;
|
||||||
|
|
||||||
|
/* If we don't understand the superblock major type, but there
|
||||||
|
* is a magic number, then it is likely to be a new format we
|
||||||
|
* just don't understand, so leave it alone. */
|
||||||
|
default:
|
||||||
|
return EXT2_ET_JOURNAL_UNSUPP_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES))
|
||||||
|
return EXT2_ET_UNSUPP_FEATURE;
|
||||||
|
|
||||||
|
if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
|
||||||
|
return EXT2_ET_RO_UNSUPP_FEATURE;
|
||||||
|
|
||||||
|
/* We have now checked whether we know enough about the journal
|
||||||
|
* format to be able to proceed safely, so any other checks that
|
||||||
|
* fail we should attempt to recover from. */
|
||||||
|
if (jsb->s_blocksize != htonl(journal->j_blocksize)) {
|
||||||
|
com_err(ctx->program_name, EXT2_ET_CORRUPT_SUPERBLOCK,
|
||||||
|
_("%s: no valid journal superblock found\n"),
|
||||||
|
ctx->device_name);
|
||||||
|
return EXT2_ET_CORRUPT_SUPERBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
|
||||||
|
journal->j_maxlen = ntohl(jsb->s_maxlen);
|
||||||
|
else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
|
||||||
|
com_err(ctx->program_name, EXT2_ET_CORRUPT_SUPERBLOCK,
|
||||||
|
_("%s: journal too short\n"),
|
||||||
|
ctx->device_name);
|
||||||
|
return EXT2_ET_CORRUPT_SUPERBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
journal->j_tail_sequence = ntohl(jsb->s_sequence);
|
||||||
|
journal->j_transaction_sequence = journal->j_tail_sequence;
|
||||||
|
journal->j_tail = ntohl(jsb->s_start);
|
||||||
|
journal->j_first = ntohl(jsb->s_first);
|
||||||
|
journal->j_last = ntohl(jsb->s_maxlen);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
|
||||||
|
journal_t *journal)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
union {
|
||||||
|
uuid_t uuid;
|
||||||
|
__u32 val[4];
|
||||||
|
} u;
|
||||||
|
__u32 new_seq = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Leave a valid existing V1 superblock signature alone.
|
||||||
|
* Anything unrecognisable we overwrite with a new V2
|
||||||
|
* signature. */
|
||||||
|
|
||||||
|
if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
|
||||||
|
jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
|
||||||
|
jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
|
||||||
|
jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zero out everything else beyond the superblock header */
|
||||||
|
|
||||||
|
p = ((char *) jsb) + sizeof(journal_header_t);
|
||||||
|
memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t));
|
||||||
|
|
||||||
|
jsb->s_blocksize = htonl(ctx->fs->blocksize);
|
||||||
|
jsb->s_maxlen = htonl(journal->j_maxlen);
|
||||||
|
jsb->s_first = htonl(1);
|
||||||
|
|
||||||
|
/* Initialize the journal sequence number so that there is "no"
|
||||||
|
* chance we will find old "valid" transactions in the journal.
|
||||||
|
* This avoids the need to zero the whole journal (slow to do,
|
||||||
|
* and risky when we are just recovering the filesystem).
|
||||||
|
*/
|
||||||
|
uuid_generate(u.uuid);
|
||||||
|
for (i = 0; i < 4; i ++)
|
||||||
|
new_seq ^= u.val[i];
|
||||||
|
jsb->s_sequence = htonl(new_seq);
|
||||||
|
|
||||||
|
mark_buffer_dirty(journal->j_sb_buffer);
|
||||||
|
ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx,
|
||||||
|
journal_t *journal,
|
||||||
|
struct problem_context *pctx)
|
||||||
|
{
|
||||||
|
struct ext2_super_block *sb = ctx->fs->super;
|
||||||
|
int recover = ctx->fs->super->s_feature_incompat &
|
||||||
|
EXT3_FEATURE_INCOMPAT_RECOVER;
|
||||||
|
|
||||||
|
if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
|
||||||
|
if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
|
||||||
|
e2fsck_journal_reset_super(ctx, journal->j_superblock,
|
||||||
|
journal);
|
||||||
|
journal->j_transaction_sequence = 1;
|
||||||
|
e2fsck_clear_recover(ctx, recover);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return EXT2_ET_CORRUPT_SUPERBLOCK;
|
||||||
|
} else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
|
||||||
|
return EXT2_ET_CORRUPT_SUPERBLOCK;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
|
||||||
|
int reset, int drop)
|
||||||
|
{
|
||||||
|
journal_superblock_t *jsb;
|
||||||
|
|
||||||
|
if (drop)
|
||||||
|
mark_buffer_clean(journal->j_sb_buffer);
|
||||||
|
else if (!(ctx->options & E2F_OPT_READONLY)) {
|
||||||
|
jsb = journal->j_superblock;
|
||||||
|
jsb->s_sequence = htonl(journal->j_transaction_sequence);
|
||||||
|
if (reset)
|
||||||
|
jsb->s_start = 0; /* this marks the journal as empty */
|
||||||
|
mark_buffer_dirty(journal->j_sb_buffer);
|
||||||
|
}
|
||||||
|
brelse(journal->j_sb_buffer);
|
||||||
|
|
||||||
|
if (ctx->journal_io) {
|
||||||
|
if (ctx->fs && ctx->fs->io != ctx->journal_io)
|
||||||
|
io_channel_close(ctx->journal_io);
|
||||||
|
ctx->journal_io = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef USE_INODE_IO
|
||||||
|
if (journal->j_inode)
|
||||||
|
ext2fs_free_mem(&journal->j_inode);
|
||||||
|
#endif
|
||||||
|
if (journal->j_fs_dev)
|
||||||
|
ext2fs_free_mem(&journal->j_fs_dev);
|
||||||
|
ext2fs_free_mem(&journal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function makes sure that the superblock fields regarding the
|
||||||
|
* journal are consistent.
|
||||||
|
*/
|
||||||
|
int e2fsck_check_ext3_journal(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
struct ext2_super_block *sb = ctx->fs->super;
|
||||||
|
journal_t *journal;
|
||||||
|
int recover = ctx->fs->super->s_feature_incompat &
|
||||||
|
EXT3_FEATURE_INCOMPAT_RECOVER;
|
||||||
|
struct problem_context pctx;
|
||||||
|
problem_t problem;
|
||||||
|
int reset = 0, force_fsck = 0;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
/* If we don't have any journal features, don't do anything more */
|
||||||
|
if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
|
||||||
|
!recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 &&
|
||||||
|
uuid_is_null(sb->s_journal_uuid))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
pctx.num = sb->s_journal_inum;
|
||||||
|
|
||||||
|
retval = e2fsck_get_journal(ctx, &journal);
|
||||||
|
if (retval) {
|
||||||
|
if ((retval == EXT2_ET_BAD_INODE_NUM) ||
|
||||||
|
(retval == EXT2_ET_BAD_BLOCK_NUM) ||
|
||||||
|
(retval == EXT2_ET_JOURNAL_TOO_SMALL) ||
|
||||||
|
(retval == EXT2_ET_NO_JOURNAL))
|
||||||
|
return e2fsck_journal_fix_bad_inode(ctx, &pctx);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = e2fsck_journal_load(journal);
|
||||||
|
if (retval) {
|
||||||
|
if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
|
||||||
|
((retval == EXT2_ET_UNSUPP_FEATURE) &&
|
||||||
|
(!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT,
|
||||||
|
&pctx))) ||
|
||||||
|
((retval == EXT2_ET_RO_UNSUPP_FEATURE) &&
|
||||||
|
(!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT,
|
||||||
|
&pctx))) ||
|
||||||
|
((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) &&
|
||||||
|
(!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx))))
|
||||||
|
retval = e2fsck_journal_fix_corrupt_super(ctx, journal,
|
||||||
|
&pctx);
|
||||||
|
e2fsck_journal_release(ctx, journal, 0, 1);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We want to make the flags consistent here. We will not leave with
|
||||||
|
* needs_recovery set but has_journal clear. We can't get in a loop
|
||||||
|
* with -y, -n, or -p, only if a user isn't making up their mind.
|
||||||
|
*/
|
||||||
|
no_has_journal:
|
||||||
|
if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
|
||||||
|
recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
|
||||||
|
pctx.str = "inode";
|
||||||
|
if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
|
||||||
|
if (recover &&
|
||||||
|
!fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
|
||||||
|
goto no_has_journal;
|
||||||
|
/*
|
||||||
|
* Need a full fsck if we are releasing a
|
||||||
|
* journal stored on a reserved inode.
|
||||||
|
*/
|
||||||
|
force_fsck = recover ||
|
||||||
|
(sb->s_journal_inum < EXT2_FIRST_INODE(sb));
|
||||||
|
/* Clear all of the journal fields */
|
||||||
|
sb->s_journal_inum = 0;
|
||||||
|
sb->s_journal_dev = 0;
|
||||||
|
memset(sb->s_journal_uuid, 0,
|
||||||
|
sizeof(sb->s_journal_uuid));
|
||||||
|
e2fsck_clear_recover(ctx, force_fsck);
|
||||||
|
} else if (!(ctx->options & E2F_OPT_READONLY)) {
|
||||||
|
sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
|
||||||
|
ext2fs_mark_super_dirty(ctx->fs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
|
||||||
|
!(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
|
||||||
|
journal->j_superblock->s_start != 0) {
|
||||||
|
/* Print status information */
|
||||||
|
fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx);
|
||||||
|
if (ctx->superblock)
|
||||||
|
problem = PR_0_JOURNAL_RUN_DEFAULT;
|
||||||
|
else
|
||||||
|
problem = PR_0_JOURNAL_RUN;
|
||||||
|
if (fix_problem(ctx, problem, &pctx)) {
|
||||||
|
ctx->options |= E2F_OPT_FORCE;
|
||||||
|
sb->s_feature_incompat |=
|
||||||
|
EXT3_FEATURE_INCOMPAT_RECOVER;
|
||||||
|
ext2fs_mark_super_dirty(ctx->fs);
|
||||||
|
} else if (fix_problem(ctx,
|
||||||
|
PR_0_JOURNAL_RESET_JOURNAL, &pctx)) {
|
||||||
|
reset = 1;
|
||||||
|
sb->s_state &= ~EXT2_VALID_FS;
|
||||||
|
ext2fs_mark_super_dirty(ctx->fs);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If the user answers no to the above question, we
|
||||||
|
* ignore the fact that journal apparently has data;
|
||||||
|
* accidentally replaying over valid data would be far
|
||||||
|
* worse than skipping a questionable recovery.
|
||||||
|
*
|
||||||
|
* XXX should we abort with a fatal error here? What
|
||||||
|
* will the ext3 kernel code do if a filesystem with
|
||||||
|
* !NEEDS_RECOVERY but with a non-zero
|
||||||
|
* journal->j_superblock->s_start is mounted?
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
e2fsck_journal_release(ctx, journal, reset, 0);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static errcode_t recover_ext3_journal(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
journal_t *journal;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
journal_init_revoke_caches();
|
||||||
|
retval = e2fsck_get_journal(ctx, &journal);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
retval = e2fsck_journal_load(journal);
|
||||||
|
if (retval)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
retval = journal_init_revoke(journal, 1024);
|
||||||
|
if (retval)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
retval = -journal_recover(journal);
|
||||||
|
if (retval)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
if (journal->j_superblock->s_errno) {
|
||||||
|
ctx->fs->super->s_state |= EXT2_ERROR_FS;
|
||||||
|
ext2fs_mark_super_dirty(ctx->fs);
|
||||||
|
journal->j_superblock->s_errno = 0;
|
||||||
|
mark_buffer_dirty(journal->j_sb_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
errout:
|
||||||
|
journal_destroy_revoke(journal);
|
||||||
|
journal_destroy_revoke_caches();
|
||||||
|
e2fsck_journal_release(ctx, journal, 1, 0);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int e2fsck_run_ext3_journal(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
io_manager io_ptr = ctx->fs->io->manager;
|
||||||
|
int blocksize = ctx->fs->blocksize;
|
||||||
|
errcode_t retval, recover_retval;
|
||||||
|
|
||||||
|
printf(_("%s: recovering journal\n"), ctx->device_name);
|
||||||
|
if (ctx->options & E2F_OPT_READONLY) {
|
||||||
|
printf(_("%s: won't do journal recovery while read-only\n"),
|
||||||
|
ctx->device_name);
|
||||||
|
return EXT2_ET_FILE_RO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->fs->flags & EXT2_FLAG_DIRTY)
|
||||||
|
ext2fs_flush(ctx->fs); /* Force out any modifications */
|
||||||
|
|
||||||
|
recover_retval = recover_ext3_journal(ctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reload the filesystem context to get up-to-date data from disk
|
||||||
|
* because journal recovery will change the filesystem under us.
|
||||||
|
*/
|
||||||
|
ext2fs_close(ctx->fs);
|
||||||
|
retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
|
||||||
|
ctx->superblock, blocksize, io_ptr,
|
||||||
|
&ctx->fs);
|
||||||
|
|
||||||
|
if (retval) {
|
||||||
|
com_err(ctx->program_name, retval,
|
||||||
|
_("while trying to re-open %s"),
|
||||||
|
ctx->device_name);
|
||||||
|
fatal_error(ctx, 0);
|
||||||
|
}
|
||||||
|
ctx->fs->priv_data = ctx;
|
||||||
|
|
||||||
|
/* Set the superblock flags */
|
||||||
|
e2fsck_clear_recover(ctx, recover_retval);
|
||||||
|
return recover_retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function will move the journal inode from a visible file in
|
||||||
|
* the filesystem directory hierarchy to the reserved inode if necessary.
|
||||||
|
*/
|
||||||
|
static const char * const journal_names[] = {
|
||||||
|
".journal", "journal", ".journal.dat", "journal.dat", 0 };
|
||||||
|
|
||||||
|
void e2fsck_move_ext3_journal(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
struct ext2_super_block *sb = ctx->fs->super;
|
||||||
|
struct problem_context pctx;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
ext2_ino_t ino;
|
||||||
|
errcode_t retval;
|
||||||
|
const char * const * cpp;
|
||||||
|
int group, mount_flags;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the filesystem is opened read-only, or there is no
|
||||||
|
* journal, then do nothing.
|
||||||
|
*/
|
||||||
|
if ((ctx->options & E2F_OPT_READONLY) ||
|
||||||
|
(sb->s_journal_inum == 0) ||
|
||||||
|
!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read in the journal inode
|
||||||
|
*/
|
||||||
|
if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If it's necessary to backup the journal inode, do so.
|
||||||
|
*/
|
||||||
|
if ((sb->s_jnl_backup_type == 0) ||
|
||||||
|
((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) &&
|
||||||
|
memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) {
|
||||||
|
if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) {
|
||||||
|
memcpy(sb->s_jnl_blocks, inode.i_block,
|
||||||
|
EXT2_N_BLOCKS*4);
|
||||||
|
sb->s_jnl_blocks[16] = inode.i_size;
|
||||||
|
sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the journal is already the hidden inode, then do nothing
|
||||||
|
*/
|
||||||
|
if (sb->s_journal_inum == EXT2_JOURNAL_INO)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The journal inode had better have only one link and not be readable.
|
||||||
|
*/
|
||||||
|
if (inode.i_links_count != 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the filesystem is mounted, or we can't tell whether
|
||||||
|
* or not it's mounted, do nothing.
|
||||||
|
*/
|
||||||
|
retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
|
||||||
|
if (retval || (mount_flags & EXT2_MF_MOUNTED))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we can't find the name of the journal inode, then do
|
||||||
|
* nothing.
|
||||||
|
*/
|
||||||
|
for (cpp = journal_names; *cpp; cpp++) {
|
||||||
|
retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp,
|
||||||
|
strlen(*cpp), 0, &ino);
|
||||||
|
if ((retval == 0) && (ino == sb->s_journal_inum))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*cpp == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* We need the inode bitmap to be loaded */
|
||||||
|
retval = ext2fs_read_bitmaps(fs);
|
||||||
|
if (retval)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pctx.str = *cpp;
|
||||||
|
if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OK, we've done all the checks, let's actually move the
|
||||||
|
* journal inode. Errors at this point mean we need to force
|
||||||
|
* an ext2 filesystem check.
|
||||||
|
*/
|
||||||
|
if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0)
|
||||||
|
goto err_out;
|
||||||
|
if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0)
|
||||||
|
goto err_out;
|
||||||
|
sb->s_journal_inum = EXT2_JOURNAL_INO;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
|
||||||
|
inode.i_links_count = 0;
|
||||||
|
inode.i_dtime = time(0);
|
||||||
|
if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0)
|
||||||
|
goto err_out;
|
||||||
|
|
||||||
|
group = ext2fs_group_of_ino(fs, ino);
|
||||||
|
ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
|
||||||
|
ext2fs_mark_ib_dirty(fs);
|
||||||
|
fs->group_desc[group].bg_free_inodes_count++;
|
||||||
|
fs->super->s_free_inodes_count++;
|
||||||
|
return;
|
||||||
|
|
||||||
|
err_out:
|
||||||
|
pctx.errcode = retval;
|
||||||
|
fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx);
|
||||||
|
fs->super->s_state &= ~EXT2_VALID_FS;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
466
e2fsprogs/e2fsck/message.c
Normal file
466
e2fsprogs/e2fsck/message.c
Normal file
|
@ -0,0 +1,466 @@
|
||||||
|
/*
|
||||||
|
* message.c --- print e2fsck messages (with compression)
|
||||||
|
*
|
||||||
|
* Copyright 1996, 1997 by Theodore Ts'o
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*
|
||||||
|
* print_e2fsck_message() prints a message to the user, using
|
||||||
|
* compression techniques and expansions of abbreviations.
|
||||||
|
*
|
||||||
|
* The following % expansions are supported:
|
||||||
|
*
|
||||||
|
* %b <blk> block number
|
||||||
|
* %B <blkcount> integer
|
||||||
|
* %c <blk2> block number
|
||||||
|
* %Di <dirent>->ino inode number
|
||||||
|
* %Dn <dirent>->name string
|
||||||
|
* %Dr <dirent>->rec_len
|
||||||
|
* %Dl <dirent>->name_len
|
||||||
|
* %Dt <dirent>->filetype
|
||||||
|
* %d <dir> inode number
|
||||||
|
* %g <group> integer
|
||||||
|
* %i <ino> inode number
|
||||||
|
* %Is <inode> -> i_size
|
||||||
|
* %IS <inode> -> i_extra_isize
|
||||||
|
* %Ib <inode> -> i_blocks
|
||||||
|
* %Il <inode> -> i_links_count
|
||||||
|
* %Im <inode> -> i_mode
|
||||||
|
* %IM <inode> -> i_mtime
|
||||||
|
* %IF <inode> -> i_faddr
|
||||||
|
* %If <inode> -> i_file_acl
|
||||||
|
* %Id <inode> -> i_dir_acl
|
||||||
|
* %Iu <inode> -> i_uid
|
||||||
|
* %Ig <inode> -> i_gid
|
||||||
|
* %j <ino2> inode number
|
||||||
|
* %m <com_err error message>
|
||||||
|
* %N <num>
|
||||||
|
* %p ext2fs_get_pathname of directory <ino>
|
||||||
|
* %P ext2fs_get_pathname of <dirent>->ino with <ino2> as
|
||||||
|
* the containing directory. (If dirent is NULL
|
||||||
|
* then return the pathname of directory <ino2>)
|
||||||
|
* %q ext2fs_get_pathname of directory <dir>
|
||||||
|
* %Q ext2fs_get_pathname of directory <ino> with <dir> as
|
||||||
|
* the containing directory.
|
||||||
|
* %s <str> miscellaneous string
|
||||||
|
* %S backup superblock
|
||||||
|
* %X <num> hexadecimal format
|
||||||
|
*
|
||||||
|
* The following '@' expansions are supported:
|
||||||
|
*
|
||||||
|
* @a extended attribute
|
||||||
|
* @A error allocating
|
||||||
|
* @b block
|
||||||
|
* @B bitmap
|
||||||
|
* @c compress
|
||||||
|
* @C conflicts with some other fs block
|
||||||
|
* @D deleted
|
||||||
|
* @d directory
|
||||||
|
* @e entry
|
||||||
|
* @E Entry '%Dn' in %p (%i)
|
||||||
|
* @f filesystem
|
||||||
|
* @F for @i %i (%Q) is
|
||||||
|
* @g group
|
||||||
|
* @h HTREE directory inode
|
||||||
|
* @i inode
|
||||||
|
* @I illegal
|
||||||
|
* @j journal
|
||||||
|
* @l lost+found
|
||||||
|
* @L is a link
|
||||||
|
* @o orphaned
|
||||||
|
* @p problem in
|
||||||
|
* @r root inode
|
||||||
|
* @s should be
|
||||||
|
* @S superblock
|
||||||
|
* @u unattached
|
||||||
|
* @v device
|
||||||
|
* @z zero-length
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
#include "e2fsck.h"
|
||||||
|
|
||||||
|
#include "problem.h"
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define _INLINE_ __inline__
|
||||||
|
#else
|
||||||
|
#define _INLINE_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure defines the abbreviations used by the text strings
|
||||||
|
* below. The first character in the string is the index letter. An
|
||||||
|
* abbreviation of the form '@<i>' is expanded by looking up the index
|
||||||
|
* letter <i> in the table below.
|
||||||
|
*/
|
||||||
|
static const char *abbrevs[] = {
|
||||||
|
N_("aextended attribute"),
|
||||||
|
N_("Aerror allocating"),
|
||||||
|
N_("bblock"),
|
||||||
|
N_("Bbitmap"),
|
||||||
|
N_("ccompress"),
|
||||||
|
N_("Cconflicts with some other fs @b"),
|
||||||
|
N_("iinode"),
|
||||||
|
N_("Iillegal"),
|
||||||
|
N_("jjournal"),
|
||||||
|
N_("Ddeleted"),
|
||||||
|
N_("ddirectory"),
|
||||||
|
N_("eentry"),
|
||||||
|
N_("E@e '%Dn' in %p (%i)"),
|
||||||
|
N_("ffilesystem"),
|
||||||
|
N_("Ffor @i %i (%Q) is"),
|
||||||
|
N_("ggroup"),
|
||||||
|
N_("hHTREE @d @i"),
|
||||||
|
N_("llost+found"),
|
||||||
|
N_("Lis a link"),
|
||||||
|
N_("oorphaned"),
|
||||||
|
N_("pproblem in"),
|
||||||
|
N_("rroot @i"),
|
||||||
|
N_("sshould be"),
|
||||||
|
N_("Ssuper@b"),
|
||||||
|
N_("uunattached"),
|
||||||
|
N_("vdevice"),
|
||||||
|
N_("zzero-length"),
|
||||||
|
"@@",
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Give more user friendly names to the "special" inodes.
|
||||||
|
*/
|
||||||
|
#define num_special_inodes 11
|
||||||
|
static const char *special_inode_name[] =
|
||||||
|
{
|
||||||
|
N_("<The NULL inode>"), /* 0 */
|
||||||
|
N_("<The bad blocks inode>"), /* 1 */
|
||||||
|
"/", /* 2 */
|
||||||
|
N_("<The ACL index inode>"), /* 3 */
|
||||||
|
N_("<The ACL data inode>"), /* 4 */
|
||||||
|
N_("<The boot loader inode>"), /* 5 */
|
||||||
|
N_("<The undelete directory inode>"), /* 6 */
|
||||||
|
N_("<The group descriptor inode>"), /* 7 */
|
||||||
|
N_("<The journal inode>"), /* 8 */
|
||||||
|
N_("<Reserved inode 9>"), /* 9 */
|
||||||
|
N_("<Reserved inode 10>"), /* 10 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function does "safe" printing. It will convert non-printable
|
||||||
|
* ASCII characters using '^' and M- notation.
|
||||||
|
*/
|
||||||
|
static void safe_print(const char *cp, int len)
|
||||||
|
{
|
||||||
|
unsigned char ch;
|
||||||
|
|
||||||
|
if (len < 0)
|
||||||
|
len = strlen(cp);
|
||||||
|
|
||||||
|
while (len--) {
|
||||||
|
ch = *cp++;
|
||||||
|
if (ch > 128) {
|
||||||
|
fputs("M-", stdout);
|
||||||
|
ch -= 128;
|
||||||
|
}
|
||||||
|
if ((ch < 32) || (ch == 0x7f)) {
|
||||||
|
fputc('^', stdout);
|
||||||
|
ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
|
||||||
|
}
|
||||||
|
fputc(ch, stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function prints a pathname, using the ext2fs_get_pathname
|
||||||
|
* function
|
||||||
|
*/
|
||||||
|
static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino)
|
||||||
|
{
|
||||||
|
errcode_t retval;
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
if (!dir && (ino < num_special_inodes)) {
|
||||||
|
fputs(_(special_inode_name[ino]), stdout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = ext2fs_get_pathname(fs, dir, ino, &path);
|
||||||
|
if (retval)
|
||||||
|
fputs("???", stdout);
|
||||||
|
else {
|
||||||
|
safe_print(path, -1);
|
||||||
|
ext2fs_free_mem(&path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function handles the '@' expansion. We allow recursive
|
||||||
|
* expansion; an @ expression can contain further '@' and '%'
|
||||||
|
* expressions.
|
||||||
|
*/
|
||||||
|
static _INLINE_ void expand_at_expression(e2fsck_t ctx, char ch,
|
||||||
|
struct problem_context *pctx,
|
||||||
|
int *first)
|
||||||
|
{
|
||||||
|
const char **cpp, *str;
|
||||||
|
|
||||||
|
/* Search for the abbreviation */
|
||||||
|
for (cpp = abbrevs; *cpp; cpp++) {
|
||||||
|
if (ch == *cpp[0])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*cpp) {
|
||||||
|
str = _(*cpp) + 1;
|
||||||
|
if (*first && islower(*str)) {
|
||||||
|
*first = 0;
|
||||||
|
fputc(toupper(*str++), stdout);
|
||||||
|
}
|
||||||
|
print_e2fsck_message(ctx, str, pctx, *first);
|
||||||
|
} else
|
||||||
|
printf("@%c", ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function expands '%IX' expressions
|
||||||
|
*/
|
||||||
|
static _INLINE_ void expand_inode_expression(char ch,
|
||||||
|
struct problem_context *ctx)
|
||||||
|
{
|
||||||
|
struct ext2_inode *inode;
|
||||||
|
struct ext2_inode_large *large_inode;
|
||||||
|
char * time_str;
|
||||||
|
time_t t;
|
||||||
|
int do_gmt = -1;
|
||||||
|
|
||||||
|
if (!ctx || !ctx->inode)
|
||||||
|
goto no_inode;
|
||||||
|
|
||||||
|
inode = ctx->inode;
|
||||||
|
large_inode = (struct ext2_inode_large *) inode;
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
|
case 's':
|
||||||
|
if (LINUX_S_ISDIR(inode->i_mode))
|
||||||
|
printf("%u", inode->i_size);
|
||||||
|
else {
|
||||||
|
#ifdef EXT2_NO_64_TYPE
|
||||||
|
if (inode->i_size_high)
|
||||||
|
printf("0x%x%08x", inode->i_size_high,
|
||||||
|
inode->i_size);
|
||||||
|
else
|
||||||
|
printf("%u", inode->i_size);
|
||||||
|
#else
|
||||||
|
printf("%llu", (inode->i_size |
|
||||||
|
((__u64) inode->i_size_high << 32)));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
printf("%u", large_inode->i_extra_isize);
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
printf("%u", inode->i_blocks);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
printf("%d", inode->i_links_count);
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
printf("0%o", inode->i_mode);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
/* The diet libc doesn't respect the TZ environemnt variable */
|
||||||
|
if (do_gmt == -1) {
|
||||||
|
time_str = getenv("TZ");
|
||||||
|
if (!time_str)
|
||||||
|
time_str = "";
|
||||||
|
do_gmt = !strcmp(time_str, "GMT");
|
||||||
|
}
|
||||||
|
t = inode->i_mtime;
|
||||||
|
time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t));
|
||||||
|
printf("%.24s", time_str);
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
printf("%u", inode->i_faddr);
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
printf("%u", inode->i_file_acl);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
|
||||||
|
inode->i_dir_acl : 0));
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
printf("%d", (inode->i_uid |
|
||||||
|
(inode->osd2.linux2.l_i_uid_high << 16)));
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
printf("%d", (inode->i_gid |
|
||||||
|
(inode->osd2.linux2.l_i_gid_high << 16)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
no_inode:
|
||||||
|
printf("%%I%c", ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function expands '%dX' expressions
|
||||||
|
*/
|
||||||
|
static _INLINE_ void expand_dirent_expression(char ch,
|
||||||
|
struct problem_context *ctx)
|
||||||
|
{
|
||||||
|
struct ext2_dir_entry *dirent;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (!ctx || !ctx->dirent)
|
||||||
|
goto no_dirent;
|
||||||
|
|
||||||
|
dirent = ctx->dirent;
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
|
case 'i':
|
||||||
|
printf("%u", dirent->inode);
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
len = dirent->name_len & 0xFF;
|
||||||
|
if (len > EXT2_NAME_LEN)
|
||||||
|
len = EXT2_NAME_LEN;
|
||||||
|
if (len > dirent->rec_len)
|
||||||
|
len = dirent->rec_len;
|
||||||
|
safe_print(dirent->name, len);
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
printf("%u", dirent->rec_len);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
printf("%u", dirent->name_len & 0xFF);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
printf("%u", dirent->name_len >> 8);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
no_dirent:
|
||||||
|
printf("%%D%c", ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch,
|
||||||
|
struct problem_context *ctx)
|
||||||
|
{
|
||||||
|
if (!ctx)
|
||||||
|
goto no_context;
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
|
case '%':
|
||||||
|
fputc('%', stdout);
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
printf("%u", ctx->blk);
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
#ifdef EXT2_NO_64_TYPE
|
||||||
|
printf("%d", ctx->blkcount);
|
||||||
|
#else
|
||||||
|
printf("%lld", ctx->blkcount);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
printf("%u", ctx->blk2);
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
printf("%u", ctx->dir);
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
printf("%d", ctx->group);
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
printf("%u", ctx->ino);
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
printf("%u", ctx->ino2);
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
printf("%s", error_message(ctx->errcode));
|
||||||
|
break;
|
||||||
|
case 'N':
|
||||||
|
#ifdef EXT2_NO_64_TYPE
|
||||||
|
printf("%u", ctx->num);
|
||||||
|
#else
|
||||||
|
printf("%llu", ctx->num);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
print_pathname(fs, ctx->ino, 0);
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
print_pathname(fs, ctx->ino2,
|
||||||
|
ctx->dirent ? ctx->dirent->inode : 0);
|
||||||
|
break;
|
||||||
|
case 'q':
|
||||||
|
print_pathname(fs, ctx->dir, 0);
|
||||||
|
break;
|
||||||
|
case 'Q':
|
||||||
|
print_pathname(fs, ctx->dir, ctx->ino);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
printf("%d", get_backup_sb(NULL, fs, NULL, NULL));
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
printf("%s", ctx->str ? ctx->str : "NULL");
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
#ifdef EXT2_NO_64_TYPE
|
||||||
|
printf("0x%x", ctx->num);
|
||||||
|
#else
|
||||||
|
printf("0x%llx", ctx->num);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
no_context:
|
||||||
|
printf("%%%c", ch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_e2fsck_message(e2fsck_t ctx, const char *msg,
|
||||||
|
struct problem_context *pctx, int first)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
const char * cp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
e2fsck_clear_progbar(ctx);
|
||||||
|
for (cp = msg; *cp; cp++) {
|
||||||
|
if (cp[0] == '@') {
|
||||||
|
cp++;
|
||||||
|
expand_at_expression(ctx, *cp, pctx, &first);
|
||||||
|
} else if (cp[0] == '%' && cp[1] == 'I') {
|
||||||
|
cp += 2;
|
||||||
|
expand_inode_expression(*cp, pctx);
|
||||||
|
} else if (cp[0] == '%' && cp[1] == 'D') {
|
||||||
|
cp += 2;
|
||||||
|
expand_dirent_expression(*cp, pctx);
|
||||||
|
} else if ((cp[0] == '%')) {
|
||||||
|
cp++;
|
||||||
|
expand_percent_expression(fs, *cp, pctx);
|
||||||
|
} else {
|
||||||
|
for (i=0; cp[i]; i++)
|
||||||
|
if ((cp[i] == '@') || cp[i] == '%')
|
||||||
|
break;
|
||||||
|
printf("%.*s", i, cp);
|
||||||
|
cp += i-1;
|
||||||
|
}
|
||||||
|
first = 0;
|
||||||
|
}
|
||||||
|
}
|
2122
e2fsprogs/e2fsck/pass1.c
Normal file
2122
e2fsprogs/e2fsck/pass1.c
Normal file
File diff suppressed because it is too large
Load diff
805
e2fsprogs/e2fsck/pass1b.c
Normal file
805
e2fsprogs/e2fsck/pass1b.c
Normal file
|
@ -0,0 +1,805 @@
|
||||||
|
/*
|
||||||
|
* pass1b.c --- Pass #1b of e2fsck
|
||||||
|
*
|
||||||
|
* This file contains pass1B, pass1C, and pass1D of e2fsck. They are
|
||||||
|
* only invoked if pass 1 discovered blocks which are in use by more
|
||||||
|
* than one inode.
|
||||||
|
*
|
||||||
|
* Pass1B scans the data blocks of all the inodes again, generating a
|
||||||
|
* complete list of duplicate blocks and which inodes have claimed
|
||||||
|
* them.
|
||||||
|
*
|
||||||
|
* Pass1C does a tree-traversal of the filesystem, to determine the
|
||||||
|
* parent directories of these inodes. This step is necessary so that
|
||||||
|
* e2fsck can print out the pathnames of affected inodes.
|
||||||
|
*
|
||||||
|
* Pass1D is a reconciliation pass. For each inode with duplicate
|
||||||
|
* blocks, the user is prompted if s/he would like to clone the file
|
||||||
|
* (so that the file gets a fresh copy of the duplicated blocks) or
|
||||||
|
* simply to delete the file.
|
||||||
|
*
|
||||||
|
* Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_INTTYPES_H
|
||||||
|
#include <inttypes.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Needed for architectures where sizeof(int) != sizeof(void *) */
|
||||||
|
#define INT_TO_VOIDPTR(val) ((void *)(intptr_t)(val))
|
||||||
|
#define VOIDPTR_TO_INT(ptr) ((int)(intptr_t)(ptr))
|
||||||
|
|
||||||
|
#include "e2fsck.h"
|
||||||
|
|
||||||
|
#include "problem.h"
|
||||||
|
#include "dict.h"
|
||||||
|
|
||||||
|
/* Define an extension to the ext2 library's block count information */
|
||||||
|
#define BLOCK_COUNT_EXTATTR (-5)
|
||||||
|
|
||||||
|
struct block_el {
|
||||||
|
blk_t block;
|
||||||
|
struct block_el *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct inode_el {
|
||||||
|
ext2_ino_t inode;
|
||||||
|
struct inode_el *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dup_block {
|
||||||
|
int num_bad;
|
||||||
|
struct inode_el *inode_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure stores information about a particular inode which
|
||||||
|
* is sharing blocks with other inodes. This information is collected
|
||||||
|
* to display to the user, so that the user knows what files he or she
|
||||||
|
* is dealing with, when trying to decide how to resolve the conflict
|
||||||
|
* of multiply-claimed blocks.
|
||||||
|
*/
|
||||||
|
struct dup_inode {
|
||||||
|
ext2_ino_t dir;
|
||||||
|
int num_dupblocks;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
struct block_el *block_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int process_pass1b_block(ext2_filsys fs, blk_t *blocknr,
|
||||||
|
e2_blkcnt_t blockcnt, blk_t ref_blk,
|
||||||
|
int ref_offset, void *priv_data);
|
||||||
|
static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
|
||||||
|
struct dup_inode *dp, char *block_buf);
|
||||||
|
static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
|
||||||
|
struct dup_inode *dp, char* block_buf);
|
||||||
|
static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk);
|
||||||
|
|
||||||
|
static void pass1b(e2fsck_t ctx, char *block_buf);
|
||||||
|
static void pass1c(e2fsck_t ctx, char *block_buf);
|
||||||
|
static void pass1d(e2fsck_t ctx, char *block_buf);
|
||||||
|
|
||||||
|
static int dup_inode_count = 0;
|
||||||
|
|
||||||
|
static dict_t blk_dict, ino_dict;
|
||||||
|
|
||||||
|
static ext2fs_inode_bitmap inode_dup_map;
|
||||||
|
|
||||||
|
static int dict_int_cmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
intptr_t ia, ib;
|
||||||
|
|
||||||
|
ia = (intptr_t)a;
|
||||||
|
ib = (intptr_t)b;
|
||||||
|
|
||||||
|
return (ia-ib);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a duplicate block record
|
||||||
|
*/
|
||||||
|
static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk,
|
||||||
|
struct ext2_inode *inode)
|
||||||
|
{
|
||||||
|
dnode_t *n;
|
||||||
|
struct dup_block *db;
|
||||||
|
struct dup_inode *di;
|
||||||
|
struct block_el *blk_el;
|
||||||
|
struct inode_el *ino_el;
|
||||||
|
|
||||||
|
n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
|
||||||
|
if (n)
|
||||||
|
db = (struct dup_block *) dnode_get(n);
|
||||||
|
else {
|
||||||
|
db = (struct dup_block *) e2fsck_allocate_memory(ctx,
|
||||||
|
sizeof(struct dup_block), "duplicate block header");
|
||||||
|
db->num_bad = 0;
|
||||||
|
db->inode_list = 0;
|
||||||
|
dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db);
|
||||||
|
}
|
||||||
|
ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx,
|
||||||
|
sizeof(struct inode_el), "inode element");
|
||||||
|
ino_el->inode = ino;
|
||||||
|
ino_el->next = db->inode_list;
|
||||||
|
db->inode_list = ino_el;
|
||||||
|
db->num_bad++;
|
||||||
|
|
||||||
|
n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino));
|
||||||
|
if (n)
|
||||||
|
di = (struct dup_inode *) dnode_get(n);
|
||||||
|
else {
|
||||||
|
di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
|
||||||
|
sizeof(struct dup_inode), "duplicate inode header");
|
||||||
|
di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0 ;
|
||||||
|
di->num_dupblocks = 0;
|
||||||
|
di->block_list = 0;
|
||||||
|
di->inode = *inode;
|
||||||
|
dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di);
|
||||||
|
}
|
||||||
|
blk_el = (struct block_el *) e2fsck_allocate_memory(ctx,
|
||||||
|
sizeof(struct block_el), "block element");
|
||||||
|
blk_el->block = blk;
|
||||||
|
blk_el->next = di->block_list;
|
||||||
|
di->block_list = blk_el;
|
||||||
|
di->num_dupblocks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free a duplicate inode record
|
||||||
|
*/
|
||||||
|
static void inode_dnode_free(dnode_t *node,
|
||||||
|
void *context EXT2FS_ATTR((unused)))
|
||||||
|
{
|
||||||
|
struct dup_inode *di;
|
||||||
|
struct block_el *p, *next;
|
||||||
|
|
||||||
|
di = (struct dup_inode *) dnode_get(node);
|
||||||
|
for (p = di->block_list; p; p = next) {
|
||||||
|
next = p->next;
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free a duplicate block record
|
||||||
|
*/
|
||||||
|
static void block_dnode_free(dnode_t *node,
|
||||||
|
void *context EXT2FS_ATTR((unused)))
|
||||||
|
{
|
||||||
|
struct dup_block *db;
|
||||||
|
struct inode_el *p, *next;
|
||||||
|
|
||||||
|
db = (struct dup_block *) dnode_get(node);
|
||||||
|
for (p = db->inode_list; p; p = next) {
|
||||||
|
next = p->next;
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Main procedure for handling duplicate blocks
|
||||||
|
*/
|
||||||
|
void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
|
||||||
|
_("multiply claimed inode map"), &inode_dup_map);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp);
|
||||||
|
dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp);
|
||||||
|
dict_set_allocator(&ino_dict, NULL, inode_dnode_free, NULL);
|
||||||
|
dict_set_allocator(&blk_dict, NULL, block_dnode_free, NULL);
|
||||||
|
|
||||||
|
pass1b(ctx, block_buf);
|
||||||
|
pass1c(ctx, block_buf);
|
||||||
|
pass1d(ctx, block_buf);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Time to free all of the accumulated data structures that we
|
||||||
|
* don't need anymore.
|
||||||
|
*/
|
||||||
|
dict_free_nodes(&ino_dict);
|
||||||
|
dict_free_nodes(&blk_dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scan the inodes looking for inodes that contain duplicate blocks.
|
||||||
|
*/
|
||||||
|
struct process_block_struct {
|
||||||
|
e2fsck_t ctx;
|
||||||
|
ext2_ino_t ino;
|
||||||
|
int dup_blocks;
|
||||||
|
struct ext2_inode *inode;
|
||||||
|
struct problem_context *pctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pass1b(e2fsck_t ctx, char *block_buf)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
ext2_ino_t ino;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
ext2_inode_scan scan;
|
||||||
|
struct process_block_struct pb;
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
if (!(ctx->options & E2F_OPT_PREEN))
|
||||||
|
fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
|
||||||
|
pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
|
||||||
|
&scan);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx->stashed_inode = &inode;
|
||||||
|
pb.ctx = ctx;
|
||||||
|
pb.pctx = &pctx;
|
||||||
|
pctx.str = "pass1b";
|
||||||
|
while (1) {
|
||||||
|
pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
|
||||||
|
if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
|
||||||
|
continue;
|
||||||
|
if (pctx.errcode) {
|
||||||
|
fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ino)
|
||||||
|
break;
|
||||||
|
pctx.ino = ctx->stashed_ino = ino;
|
||||||
|
if ((ino != EXT2_BAD_INO) &&
|
||||||
|
!ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
pb.ino = ino;
|
||||||
|
pb.dup_blocks = 0;
|
||||||
|
pb.inode = &inode;
|
||||||
|
|
||||||
|
if (ext2fs_inode_has_valid_blocks(&inode) ||
|
||||||
|
(ino == EXT2_BAD_INO))
|
||||||
|
pctx.errcode = ext2fs_block_iterate2(fs, ino,
|
||||||
|
0, block_buf, process_pass1b_block, &pb);
|
||||||
|
if (inode.i_file_acl)
|
||||||
|
process_pass1b_block(fs, &inode.i_file_acl,
|
||||||
|
BLOCK_COUNT_EXTATTR, 0, 0, &pb);
|
||||||
|
if (pb.dup_blocks) {
|
||||||
|
end_problem_latch(ctx, PR_LATCH_DBLOCK);
|
||||||
|
if (ino >= EXT2_FIRST_INODE(fs->super) ||
|
||||||
|
ino == EXT2_ROOT_INO)
|
||||||
|
dup_inode_count++;
|
||||||
|
}
|
||||||
|
if (pctx.errcode)
|
||||||
|
fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
|
||||||
|
}
|
||||||
|
ext2fs_close_inode_scan(scan);
|
||||||
|
e2fsck_use_inode_shortcuts(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int process_pass1b_block(ext2_filsys fs EXT2FS_ATTR((unused)),
|
||||||
|
blk_t *block_nr,
|
||||||
|
e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
|
||||||
|
blk_t ref_blk EXT2FS_ATTR((unused)),
|
||||||
|
int ref_offset EXT2FS_ATTR((unused)),
|
||||||
|
void *priv_data)
|
||||||
|
{
|
||||||
|
struct process_block_struct *p;
|
||||||
|
e2fsck_t ctx;
|
||||||
|
|
||||||
|
if (HOLE_BLKADDR(*block_nr))
|
||||||
|
return 0;
|
||||||
|
p = (struct process_block_struct *) priv_data;
|
||||||
|
ctx = p->ctx;
|
||||||
|
|
||||||
|
if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* OK, this is a duplicate block */
|
||||||
|
if (p->ino != EXT2_BAD_INO) {
|
||||||
|
p->pctx->blk = *block_nr;
|
||||||
|
fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
|
||||||
|
}
|
||||||
|
p->dup_blocks++;
|
||||||
|
ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
|
||||||
|
|
||||||
|
add_dupe(ctx, p->ino, *block_nr, p->inode);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass 1c: Scan directories for inodes with duplicate blocks. This
|
||||||
|
* is used so that we can print pathnames when prompting the user for
|
||||||
|
* what to do.
|
||||||
|
*/
|
||||||
|
struct search_dir_struct {
|
||||||
|
int count;
|
||||||
|
ext2_ino_t first_inode;
|
||||||
|
ext2_ino_t max_inode;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int search_dirent_proc(ext2_ino_t dir, int entry,
|
||||||
|
struct ext2_dir_entry *dirent,
|
||||||
|
int offset EXT2FS_ATTR((unused)),
|
||||||
|
int blocksize EXT2FS_ATTR((unused)),
|
||||||
|
char *buf EXT2FS_ATTR((unused)),
|
||||||
|
void *priv_data)
|
||||||
|
{
|
||||||
|
struct search_dir_struct *sd;
|
||||||
|
struct dup_inode *p;
|
||||||
|
dnode_t *n;
|
||||||
|
|
||||||
|
sd = (struct search_dir_struct *) priv_data;
|
||||||
|
|
||||||
|
if (dirent->inode > sd->max_inode)
|
||||||
|
/* Should abort this inode, but not everything */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) ||
|
||||||
|
!ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode));
|
||||||
|
if (!n)
|
||||||
|
return 0;
|
||||||
|
p = (struct dup_inode *) dnode_get(n);
|
||||||
|
p->dir = dir;
|
||||||
|
sd->count--;
|
||||||
|
|
||||||
|
return(sd->count ? 0 : DIRENT_ABORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void pass1c(e2fsck_t ctx, char *block_buf)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
struct search_dir_struct sd;
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
if (!(ctx->options & E2F_OPT_PREEN))
|
||||||
|
fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Search through all directories to translate inodes to names
|
||||||
|
* (by searching for the containing directory for that inode.)
|
||||||
|
*/
|
||||||
|
sd.count = dup_inode_count;
|
||||||
|
sd.first_inode = EXT2_FIRST_INODE(fs->super);
|
||||||
|
sd.max_inode = fs->super->s_inodes_count;
|
||||||
|
ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
|
||||||
|
search_dirent_proc, &sd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pass1d(e2fsck_t ctx, char *block_buf)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
struct dup_inode *p, *t;
|
||||||
|
struct dup_block *q;
|
||||||
|
ext2_ino_t *shared, ino;
|
||||||
|
int shared_len;
|
||||||
|
int i;
|
||||||
|
int file_ok;
|
||||||
|
int meta_data = 0;
|
||||||
|
struct problem_context pctx;
|
||||||
|
dnode_t *n, *m;
|
||||||
|
struct block_el *s;
|
||||||
|
struct inode_el *r;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
if (!(ctx->options & E2F_OPT_PREEN))
|
||||||
|
fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
|
||||||
|
e2fsck_read_bitmaps(ctx);
|
||||||
|
|
||||||
|
pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
|
||||||
|
fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
|
||||||
|
shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
|
||||||
|
sizeof(ext2_ino_t) * dict_count(&ino_dict),
|
||||||
|
"Shared inode list");
|
||||||
|
for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
|
||||||
|
p = (struct dup_inode *) dnode_get(n);
|
||||||
|
shared_len = 0;
|
||||||
|
file_ok = 1;
|
||||||
|
ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
|
||||||
|
if (ino == EXT2_BAD_INO)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find all of the inodes which share blocks with this
|
||||||
|
* one. First we find all of the duplicate blocks
|
||||||
|
* belonging to this inode, and then search each block
|
||||||
|
* get the list of inodes, and merge them together.
|
||||||
|
*/
|
||||||
|
for (s = p->block_list; s; s = s->next) {
|
||||||
|
m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block));
|
||||||
|
if (!m)
|
||||||
|
continue; /* Should never happen... */
|
||||||
|
q = (struct dup_block *) dnode_get(m);
|
||||||
|
if (q->num_bad > 1)
|
||||||
|
file_ok = 0;
|
||||||
|
if (check_if_fs_block(ctx, s->block)) {
|
||||||
|
file_ok = 0;
|
||||||
|
meta_data = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add all inodes used by this block to the
|
||||||
|
* shared[] --- which is a unique list, so
|
||||||
|
* if an inode is already in shared[], don't
|
||||||
|
* add it again.
|
||||||
|
*/
|
||||||
|
for (r = q->inode_list; r; r = r->next) {
|
||||||
|
if (r->inode == ino)
|
||||||
|
continue;
|
||||||
|
for (i = 0; i < shared_len; i++)
|
||||||
|
if (shared[i] == r->inode)
|
||||||
|
break;
|
||||||
|
if (i == shared_len) {
|
||||||
|
shared[shared_len++] = r->inode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Report the inode that we are working on
|
||||||
|
*/
|
||||||
|
pctx.inode = &p->inode;
|
||||||
|
pctx.ino = ino;
|
||||||
|
pctx.dir = p->dir;
|
||||||
|
pctx.blkcount = p->num_dupblocks;
|
||||||
|
pctx.num = meta_data ? shared_len+1 : shared_len;
|
||||||
|
fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
|
||||||
|
pctx.blkcount = 0;
|
||||||
|
pctx.num = 0;
|
||||||
|
|
||||||
|
if (meta_data)
|
||||||
|
fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
|
||||||
|
|
||||||
|
for (i = 0; i < shared_len; i++) {
|
||||||
|
m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
|
||||||
|
if (!m)
|
||||||
|
continue; /* should never happen */
|
||||||
|
t = (struct dup_inode *) dnode_get(m);
|
||||||
|
/*
|
||||||
|
* Report the inode that we are sharing with
|
||||||
|
*/
|
||||||
|
pctx.inode = &t->inode;
|
||||||
|
pctx.ino = shared[i];
|
||||||
|
pctx.dir = t->dir;
|
||||||
|
fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
|
||||||
|
}
|
||||||
|
if (file_ok) {
|
||||||
|
fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
|
||||||
|
pctx.errcode = clone_file(ctx, ino, p, block_buf);
|
||||||
|
if (pctx.errcode)
|
||||||
|
fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
|
||||||
|
delete_file(ctx, ino, p, block_buf);
|
||||||
|
else
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
}
|
||||||
|
ext2fs_free_mem(&shared);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Drop the refcount on the dup_block structure, and clear the entry
|
||||||
|
* in the block_dup_map if appropriate.
|
||||||
|
*/
|
||||||
|
static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p)
|
||||||
|
{
|
||||||
|
p->num_bad--;
|
||||||
|
if (p->num_bad <= 0 ||
|
||||||
|
(p->num_bad == 1 && !check_if_fs_block(ctx, block)))
|
||||||
|
ext2fs_unmark_block_bitmap(ctx->block_dup_map, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int delete_file_block(ext2_filsys fs,
|
||||||
|
blk_t *block_nr,
|
||||||
|
e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
|
||||||
|
blk_t ref_block EXT2FS_ATTR((unused)),
|
||||||
|
int ref_offset EXT2FS_ATTR((unused)),
|
||||||
|
void *priv_data)
|
||||||
|
{
|
||||||
|
struct process_block_struct *pb;
|
||||||
|
struct dup_block *p;
|
||||||
|
dnode_t *n;
|
||||||
|
e2fsck_t ctx;
|
||||||
|
|
||||||
|
pb = (struct process_block_struct *) priv_data;
|
||||||
|
ctx = pb->ctx;
|
||||||
|
|
||||||
|
if (HOLE_BLKADDR(*block_nr))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
|
||||||
|
n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
|
||||||
|
if (n) {
|
||||||
|
p = (struct dup_block *) dnode_get(n);
|
||||||
|
decrement_badcount(ctx, *block_nr, p);
|
||||||
|
} else
|
||||||
|
com_err("delete_file_block", 0,
|
||||||
|
_("internal error; can't find dup_blk for %d\n"),
|
||||||
|
*block_nr);
|
||||||
|
} else {
|
||||||
|
ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
|
||||||
|
ext2fs_block_alloc_stats(fs, *block_nr, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
|
||||||
|
struct dup_inode *dp, char* block_buf)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
struct process_block_struct pb;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
struct problem_context pctx;
|
||||||
|
unsigned int count;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
pctx.ino = pb.ino = ino;
|
||||||
|
pb.dup_blocks = dp->num_dupblocks;
|
||||||
|
pb.ctx = ctx;
|
||||||
|
pctx.str = "delete_file";
|
||||||
|
|
||||||
|
e2fsck_read_inode(ctx, ino, &inode, "delete_file");
|
||||||
|
if (ext2fs_inode_has_valid_blocks(&inode))
|
||||||
|
pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
|
||||||
|
delete_file_block, &pb);
|
||||||
|
if (pctx.errcode)
|
||||||
|
fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
|
||||||
|
ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
|
||||||
|
ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
|
||||||
|
if (ctx->inode_bad_map)
|
||||||
|
ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
|
||||||
|
ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
|
||||||
|
|
||||||
|
/* Inode may have changed by block_iterate, so reread it */
|
||||||
|
e2fsck_read_inode(ctx, ino, &inode, "delete_file");
|
||||||
|
inode.i_links_count = 0;
|
||||||
|
inode.i_dtime = time(0);
|
||||||
|
if (inode.i_file_acl &&
|
||||||
|
(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
|
||||||
|
count = 1;
|
||||||
|
pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
|
||||||
|
block_buf, -1, &count);
|
||||||
|
if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
|
||||||
|
pctx.errcode = 0;
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.blk = inode.i_file_acl;
|
||||||
|
fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* If the count is zero, then arrange to have the
|
||||||
|
* block deleted. If the block is in the block_dup_map,
|
||||||
|
* also call delete_file_block since it will take care
|
||||||
|
* of keeping the accounting straight.
|
||||||
|
*/
|
||||||
|
if ((count == 0) ||
|
||||||
|
ext2fs_test_block_bitmap(ctx->block_dup_map,
|
||||||
|
inode.i_file_acl))
|
||||||
|
delete_file_block(fs, &inode.i_file_acl,
|
||||||
|
BLOCK_COUNT_EXTATTR, 0, 0, &pb);
|
||||||
|
}
|
||||||
|
e2fsck_write_inode(ctx, ino, &inode, "delete_file");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct clone_struct {
|
||||||
|
errcode_t errcode;
|
||||||
|
ext2_ino_t dir;
|
||||||
|
char *buf;
|
||||||
|
e2fsck_t ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int clone_file_block(ext2_filsys fs,
|
||||||
|
blk_t *block_nr,
|
||||||
|
e2_blkcnt_t blockcnt,
|
||||||
|
blk_t ref_block EXT2FS_ATTR((unused)),
|
||||||
|
int ref_offset EXT2FS_ATTR((unused)),
|
||||||
|
void *priv_data)
|
||||||
|
{
|
||||||
|
struct dup_block *p;
|
||||||
|
blk_t new_block;
|
||||||
|
errcode_t retval;
|
||||||
|
struct clone_struct *cs = (struct clone_struct *) priv_data;
|
||||||
|
dnode_t *n;
|
||||||
|
e2fsck_t ctx;
|
||||||
|
|
||||||
|
ctx = cs->ctx;
|
||||||
|
|
||||||
|
if (HOLE_BLKADDR(*block_nr))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
|
||||||
|
n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
|
||||||
|
if (n) {
|
||||||
|
p = (struct dup_block *) dnode_get(n);
|
||||||
|
retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
|
||||||
|
&new_block);
|
||||||
|
if (retval) {
|
||||||
|
cs->errcode = retval;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
if (cs->dir && (blockcnt >= 0)) {
|
||||||
|
retval = ext2fs_set_dir_block(fs->dblist,
|
||||||
|
cs->dir, new_block, blockcnt);
|
||||||
|
if (retval) {
|
||||||
|
cs->errcode = retval;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
printf("Cloning block %u to %u\n", *block_nr,
|
||||||
|
new_block);
|
||||||
|
#endif
|
||||||
|
retval = io_channel_read_blk(fs->io, *block_nr, 1,
|
||||||
|
cs->buf);
|
||||||
|
if (retval) {
|
||||||
|
cs->errcode = retval;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
retval = io_channel_write_blk(fs->io, new_block, 1,
|
||||||
|
cs->buf);
|
||||||
|
if (retval) {
|
||||||
|
cs->errcode = retval;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
decrement_badcount(ctx, *block_nr, p);
|
||||||
|
*block_nr = new_block;
|
||||||
|
ext2fs_mark_block_bitmap(ctx->block_found_map,
|
||||||
|
new_block);
|
||||||
|
ext2fs_mark_block_bitmap(fs->block_map, new_block);
|
||||||
|
return BLOCK_CHANGED;
|
||||||
|
} else
|
||||||
|
com_err("clone_file_block", 0,
|
||||||
|
_("internal error; can't find dup_blk for %d\n"),
|
||||||
|
*block_nr);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
|
||||||
|
struct dup_inode *dp, char* block_buf)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
errcode_t retval;
|
||||||
|
struct clone_struct cs;
|
||||||
|
struct problem_context pctx;
|
||||||
|
blk_t blk;
|
||||||
|
dnode_t *n;
|
||||||
|
struct inode_el *ino_el;
|
||||||
|
struct dup_block *db;
|
||||||
|
struct dup_inode *di;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
cs.errcode = 0;
|
||||||
|
cs.dir = 0;
|
||||||
|
cs.ctx = ctx;
|
||||||
|
retval = ext2fs_get_mem(fs->blocksize, &cs.buf);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
|
||||||
|
cs.dir = ino;
|
||||||
|
|
||||||
|
pctx.ino = ino;
|
||||||
|
pctx.str = "clone_file";
|
||||||
|
if (ext2fs_inode_has_valid_blocks(&dp->inode))
|
||||||
|
pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
|
||||||
|
clone_file_block, &cs);
|
||||||
|
ext2fs_mark_bb_dirty(fs);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
|
||||||
|
retval = pctx.errcode;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
if (cs.errcode) {
|
||||||
|
com_err("clone_file", cs.errcode,
|
||||||
|
_("returned from clone_file_block"));
|
||||||
|
retval = cs.errcode;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
/* The inode may have changed on disk, so we have to re-read it */
|
||||||
|
e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA");
|
||||||
|
blk = dp->inode.i_file_acl;
|
||||||
|
if (blk && (clone_file_block(fs, &dp->inode.i_file_acl,
|
||||||
|
BLOCK_COUNT_EXTATTR, 0, 0, &cs) ==
|
||||||
|
BLOCK_CHANGED)) {
|
||||||
|
e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA");
|
||||||
|
/*
|
||||||
|
* If we cloned the EA block, find all other inodes
|
||||||
|
* which refered to that EA block, and modify
|
||||||
|
* them to point to the new EA block.
|
||||||
|
*/
|
||||||
|
n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
|
||||||
|
db = (struct dup_block *) dnode_get(n);
|
||||||
|
for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) {
|
||||||
|
if (ino_el->inode == ino)
|
||||||
|
continue;
|
||||||
|
n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode));
|
||||||
|
di = (struct dup_inode *) dnode_get(n);
|
||||||
|
if (di->inode.i_file_acl == blk) {
|
||||||
|
di->inode.i_file_acl = dp->inode.i_file_acl;
|
||||||
|
e2fsck_write_inode(ctx, ino_el->inode,
|
||||||
|
&di->inode, "clone file EA");
|
||||||
|
decrement_badcount(ctx, blk, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
errout:
|
||||||
|
ext2fs_free_mem(&cs.buf);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This routine returns 1 if a block overlaps with one of the superblocks,
|
||||||
|
* group descriptors, inode bitmaps, or block bitmaps.
|
||||||
|
*/
|
||||||
|
static int check_if_fs_block(e2fsck_t ctx, blk_t test_block)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
blk_t block;
|
||||||
|
dgrp_t i;
|
||||||
|
|
||||||
|
block = fs->super->s_first_data_block;
|
||||||
|
for (i = 0; i < fs->group_desc_count; i++) {
|
||||||
|
|
||||||
|
/* Check superblocks/block group descriptros */
|
||||||
|
if (ext2fs_bg_has_super(fs, i)) {
|
||||||
|
if (test_block >= block &&
|
||||||
|
(test_block <= block + fs->desc_blocks))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the inode table */
|
||||||
|
if ((fs->group_desc[i].bg_inode_table) &&
|
||||||
|
(test_block >= fs->group_desc[i].bg_inode_table) &&
|
||||||
|
(test_block < (fs->group_desc[i].bg_inode_table +
|
||||||
|
fs->inode_blocks_per_group)))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Check the bitmap blocks */
|
||||||
|
if ((test_block == fs->group_desc[i].bg_block_bitmap) ||
|
||||||
|
(test_block == fs->group_desc[i].bg_inode_bitmap))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
block += fs->super->s_blocks_per_group;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
1414
e2fsprogs/e2fsck/pass2.c
Normal file
1414
e2fsprogs/e2fsck/pass2.c
Normal file
File diff suppressed because it is too large
Load diff
804
e2fsprogs/e2fsck/pass3.c
Normal file
804
e2fsprogs/e2fsck/pass3.c
Normal file
|
@ -0,0 +1,804 @@
|
||||||
|
/*
|
||||||
|
* pass3.c -- pass #3 of e2fsck: Check for directory connectivity
|
||||||
|
*
|
||||||
|
* Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 Theodore Ts'o.
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*
|
||||||
|
* Pass #3 assures that all directories are connected to the
|
||||||
|
* filesystem tree, using the following algorithm:
|
||||||
|
*
|
||||||
|
* First, the root directory is checked to make sure it exists; if
|
||||||
|
* not, e2fsck will offer to create a new one. It is then marked as
|
||||||
|
* "done".
|
||||||
|
*
|
||||||
|
* Then, pass3 interates over all directory inodes; for each directory
|
||||||
|
* it attempts to trace up the filesystem tree, using dirinfo.parent
|
||||||
|
* until it reaches a directory which has been marked "done". If it
|
||||||
|
* can not do so, then the directory must be disconnected, and e2fsck
|
||||||
|
* will offer to reconnect it to /lost+found. While it is chasing
|
||||||
|
* parent pointers up the filesystem tree, if pass3 sees a directory
|
||||||
|
* twice, then it has detected a filesystem loop, and it will again
|
||||||
|
* offer to reconnect the directory to /lost+found in to break the
|
||||||
|
* filesystem loop.
|
||||||
|
*
|
||||||
|
* Pass 3 also contains the subroutine, e2fsck_reconnect_file() to
|
||||||
|
* reconnect inodes to /lost+found; this subroutine is also used by
|
||||||
|
* pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which
|
||||||
|
* is responsible for creating /lost+found if it does not exist.
|
||||||
|
*
|
||||||
|
* Pass 3 frees the following data structures:
|
||||||
|
* - The dirinfo directory information cache.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "e2fsck.h"
|
||||||
|
#include "problem.h"
|
||||||
|
|
||||||
|
static void check_root(e2fsck_t ctx);
|
||||||
|
static int check_directory(e2fsck_t ctx, struct dir_info *dir,
|
||||||
|
struct problem_context *pctx);
|
||||||
|
static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent);
|
||||||
|
|
||||||
|
static ext2fs_inode_bitmap inode_loop_detect = 0;
|
||||||
|
static ext2fs_inode_bitmap inode_done_map = 0;
|
||||||
|
|
||||||
|
void e2fsck_pass3(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
int i;
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
struct resource_track rtrack;
|
||||||
|
#endif
|
||||||
|
struct problem_context pctx;
|
||||||
|
struct dir_info *dir;
|
||||||
|
unsigned long maxdirs, count;
|
||||||
|
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
init_resource_track(&rtrack);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
#ifdef MTRACE
|
||||||
|
mtrace_print("Pass 3");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!(ctx->options & E2F_OPT_PREEN))
|
||||||
|
fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate some bitmaps to do loop detection.
|
||||||
|
*/
|
||||||
|
pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
|
||||||
|
&inode_done_map);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.num = 2;
|
||||||
|
fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
goto abort_exit;
|
||||||
|
}
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
if (ctx->options & E2F_OPT_TIME) {
|
||||||
|
e2fsck_clear_progbar(ctx);
|
||||||
|
print_resource_track(_("Peak memory"), &ctx->global_rtrack);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
check_root(ctx);
|
||||||
|
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
||||||
|
goto abort_exit;
|
||||||
|
|
||||||
|
ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
|
||||||
|
|
||||||
|
maxdirs = e2fsck_get_num_dirinfo(ctx);
|
||||||
|
count = 1;
|
||||||
|
|
||||||
|
if (ctx->progress)
|
||||||
|
if ((ctx->progress)(ctx, 3, 0, maxdirs))
|
||||||
|
goto abort_exit;
|
||||||
|
|
||||||
|
for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
|
||||||
|
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
||||||
|
goto abort_exit;
|
||||||
|
if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
|
||||||
|
goto abort_exit;
|
||||||
|
if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
|
||||||
|
if (check_directory(ctx, dir, &pctx))
|
||||||
|
goto abort_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Force the creation of /lost+found if not present
|
||||||
|
*/
|
||||||
|
if ((ctx->flags & E2F_OPT_READONLY) == 0)
|
||||||
|
e2fsck_get_lost_and_found(ctx, 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there are any directories that need to be indexed or
|
||||||
|
* optimized, do it here.
|
||||||
|
*/
|
||||||
|
e2fsck_rehash_directories(ctx);
|
||||||
|
|
||||||
|
abort_exit:
|
||||||
|
e2fsck_free_dir_info(ctx);
|
||||||
|
if (inode_loop_detect) {
|
||||||
|
ext2fs_free_inode_bitmap(inode_loop_detect);
|
||||||
|
inode_loop_detect = 0;
|
||||||
|
}
|
||||||
|
if (inode_done_map) {
|
||||||
|
ext2fs_free_inode_bitmap(inode_done_map);
|
||||||
|
inode_done_map = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
if (ctx->options & E2F_OPT_TIME2) {
|
||||||
|
e2fsck_clear_progbar(ctx);
|
||||||
|
print_resource_track(_("Pass 3"), &rtrack);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This makes sure the root inode is present; if not, we ask if the
|
||||||
|
* user wants us to create it. Not creating it is a fatal error.
|
||||||
|
*/
|
||||||
|
static void check_root(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
blk_t blk;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
char * block;
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
|
||||||
|
/*
|
||||||
|
* If the root inode is not a directory, die here. The
|
||||||
|
* user must have answered 'no' in pass1 when we
|
||||||
|
* offered to clear it.
|
||||||
|
*/
|
||||||
|
if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
|
||||||
|
EXT2_ROOT_INO))) {
|
||||||
|
fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
|
||||||
|
fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e2fsck_read_bitmaps(ctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, find a free block
|
||||||
|
*/
|
||||||
|
pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.str = "ext2fs_new_block";
|
||||||
|
fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
|
||||||
|
ext2fs_mark_block_bitmap(fs->block_map, blk);
|
||||||
|
ext2fs_mark_bb_dirty(fs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now let's create the actual data block for the inode
|
||||||
|
*/
|
||||||
|
pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
|
||||||
|
&block);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.str = "ext2fs_new_dir_block";
|
||||||
|
fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.str = "ext2fs_write_dir_block";
|
||||||
|
fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ext2fs_free_mem(&block);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the inode structure
|
||||||
|
*/
|
||||||
|
memset(&inode, 0, sizeof(inode));
|
||||||
|
inode.i_mode = 040755;
|
||||||
|
inode.i_size = fs->blocksize;
|
||||||
|
inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
|
||||||
|
inode.i_links_count = 2;
|
||||||
|
inode.i_blocks = fs->blocksize / 512;
|
||||||
|
inode.i_block[0] = blk;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write out the inode.
|
||||||
|
*/
|
||||||
|
pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.str = "ext2fs_write_inode";
|
||||||
|
fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Miscellaneous bookkeeping...
|
||||||
|
*/
|
||||||
|
e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
|
||||||
|
ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
|
||||||
|
ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
|
||||||
|
|
||||||
|
ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
|
||||||
|
ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
|
||||||
|
ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
|
||||||
|
ext2fs_mark_ib_dirty(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This subroutine is responsible for making sure that a particular
|
||||||
|
* directory is connected to the root; if it isn't we trace it up as
|
||||||
|
* far as we can go, and then offer to connect the resulting parent to
|
||||||
|
* the lost+found. We have to do loop detection; if we ever discover
|
||||||
|
* a loop, we treat that as a disconnected directory and offer to
|
||||||
|
* reparent it to lost+found.
|
||||||
|
*
|
||||||
|
* However, loop detection is expensive, because for very large
|
||||||
|
* filesystems, the inode_loop_detect bitmap is huge, and clearing it
|
||||||
|
* is non-trivial. Loops in filesystems are also a rare error case,
|
||||||
|
* and we shouldn't optimize for error cases. So we try two passes of
|
||||||
|
* the algorithm. The first time, we ignore loop detection and merely
|
||||||
|
* increment a counter; if the counter exceeds some extreme threshold,
|
||||||
|
* then we try again with the loop detection bitmap enabled.
|
||||||
|
*/
|
||||||
|
static int check_directory(e2fsck_t ctx, struct dir_info *dir,
|
||||||
|
struct problem_context *pctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
struct dir_info *p = dir;
|
||||||
|
int loop_pass = 0, parent_count = 0;
|
||||||
|
|
||||||
|
if (!p)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/*
|
||||||
|
* Mark this inode as being "done"; by the time we
|
||||||
|
* return from this function, the inode we either be
|
||||||
|
* verified as being connected to the directory tree,
|
||||||
|
* or we will have offered to reconnect this to
|
||||||
|
* lost+found.
|
||||||
|
*
|
||||||
|
* If it was marked done already, then we've reached a
|
||||||
|
* parent we've already checked.
|
||||||
|
*/
|
||||||
|
if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this directory doesn't have a parent, or we've
|
||||||
|
* seen the parent once already, then offer to
|
||||||
|
* reparent it to lost+found
|
||||||
|
*/
|
||||||
|
if (!p->parent ||
|
||||||
|
(loop_pass &&
|
||||||
|
(ext2fs_test_inode_bitmap(inode_loop_detect,
|
||||||
|
p->parent)))) {
|
||||||
|
pctx->ino = p->ino;
|
||||||
|
if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
|
||||||
|
if (e2fsck_reconnect_file(ctx, pctx->ino))
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
else {
|
||||||
|
p = e2fsck_get_dir_info(ctx, pctx->ino);
|
||||||
|
p->parent = ctx->lost_and_found;
|
||||||
|
fix_dotdot(ctx, p, ctx->lost_and_found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
p = e2fsck_get_dir_info(ctx, p->parent);
|
||||||
|
if (!p) {
|
||||||
|
fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (loop_pass) {
|
||||||
|
ext2fs_mark_inode_bitmap(inode_loop_detect,
|
||||||
|
p->ino);
|
||||||
|
} else if (parent_count++ > 2048) {
|
||||||
|
/*
|
||||||
|
* If we've run into a path depth that's
|
||||||
|
* greater than 2048, try again with the inode
|
||||||
|
* loop bitmap turned on and start from the
|
||||||
|
* top.
|
||||||
|
*/
|
||||||
|
loop_pass = 1;
|
||||||
|
if (inode_loop_detect)
|
||||||
|
ext2fs_clear_inode_bitmap(inode_loop_detect);
|
||||||
|
else {
|
||||||
|
pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect);
|
||||||
|
if (pctx->errcode) {
|
||||||
|
pctx->num = 1;
|
||||||
|
fix_problem(ctx,
|
||||||
|
PR_3_ALLOCATE_IBITMAP_ERROR, pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure that .. and the parent directory are the same;
|
||||||
|
* offer to fix it if not.
|
||||||
|
*/
|
||||||
|
if (dir->parent != dir->dotdot) {
|
||||||
|
pctx->ino = dir->ino;
|
||||||
|
pctx->ino2 = dir->dotdot;
|
||||||
|
pctx->dir = dir->parent;
|
||||||
|
if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
|
||||||
|
fix_dotdot(ctx, dir, dir->parent);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This routine gets the lost_and_found inode, making it a directory
|
||||||
|
* if necessary
|
||||||
|
*/
|
||||||
|
ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
ext2_ino_t ino;
|
||||||
|
blk_t blk;
|
||||||
|
errcode_t retval;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
char * block;
|
||||||
|
static const char name[] = "lost+found";
|
||||||
|
struct problem_context pctx;
|
||||||
|
struct dir_info *dirinfo;
|
||||||
|
|
||||||
|
if (ctx->lost_and_found)
|
||||||
|
return ctx->lost_and_found;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
|
||||||
|
sizeof(name)-1, 0, &ino);
|
||||||
|
if (retval && !fix)
|
||||||
|
return 0;
|
||||||
|
if (!retval) {
|
||||||
|
if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) {
|
||||||
|
ctx->lost_and_found = ino;
|
||||||
|
return ino;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lost+found isn't a directory! */
|
||||||
|
if (!fix)
|
||||||
|
return 0;
|
||||||
|
pctx.ino = ino;
|
||||||
|
if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* OK, unlink the old /lost+found file. */
|
||||||
|
pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.str = "ext2fs_unlink";
|
||||||
|
fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dirinfo = e2fsck_get_dir_info(ctx, ino);
|
||||||
|
if (dirinfo)
|
||||||
|
dirinfo->parent = 0;
|
||||||
|
e2fsck_adjust_inode_count(ctx, ino, -1);
|
||||||
|
} else if (retval != EXT2_ET_FILE_NOT_FOUND) {
|
||||||
|
pctx.errcode = retval;
|
||||||
|
fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
|
||||||
|
}
|
||||||
|
if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the inode and block bitmaps in; we'll be messing with
|
||||||
|
* them.
|
||||||
|
*/
|
||||||
|
e2fsck_read_bitmaps(ctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, find a free block
|
||||||
|
*/
|
||||||
|
retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
|
||||||
|
if (retval) {
|
||||||
|
pctx.errcode = retval;
|
||||||
|
fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
|
||||||
|
ext2fs_block_alloc_stats(fs, blk, +1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Next find a free inode.
|
||||||
|
*/
|
||||||
|
retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
|
||||||
|
ctx->inode_used_map, &ino);
|
||||||
|
if (retval) {
|
||||||
|
pctx.errcode = retval;
|
||||||
|
fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
|
||||||
|
ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
|
||||||
|
ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now let's create the actual data block for the inode
|
||||||
|
*/
|
||||||
|
retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
|
||||||
|
if (retval) {
|
||||||
|
pctx.errcode = retval;
|
||||||
|
fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = ext2fs_write_dir_block(fs, blk, block);
|
||||||
|
ext2fs_free_mem(&block);
|
||||||
|
if (retval) {
|
||||||
|
pctx.errcode = retval;
|
||||||
|
fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the inode structure
|
||||||
|
*/
|
||||||
|
memset(&inode, 0, sizeof(inode));
|
||||||
|
inode.i_mode = 040700;
|
||||||
|
inode.i_size = fs->blocksize;
|
||||||
|
inode.i_atime = inode.i_ctime = inode.i_mtime = time(0);
|
||||||
|
inode.i_links_count = 2;
|
||||||
|
inode.i_blocks = fs->blocksize / 512;
|
||||||
|
inode.i_block[0] = blk;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Next, write out the inode.
|
||||||
|
*/
|
||||||
|
pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.str = "ext2fs_write_inode";
|
||||||
|
fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Finally, create the directory link
|
||||||
|
*/
|
||||||
|
pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.str = "ext2fs_link";
|
||||||
|
fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Miscellaneous bookkeeping that needs to be kept straight.
|
||||||
|
*/
|
||||||
|
e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
|
||||||
|
e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
|
||||||
|
ext2fs_icount_store(ctx->inode_count, ino, 2);
|
||||||
|
ext2fs_icount_store(ctx->inode_link_info, ino, 2);
|
||||||
|
ctx->lost_and_found = ino;
|
||||||
|
#if 0
|
||||||
|
printf("/lost+found created; inode #%lu\n", ino);
|
||||||
|
#endif
|
||||||
|
return ino;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This routine will connect a file to lost+found
|
||||||
|
*/
|
||||||
|
int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
errcode_t retval;
|
||||||
|
char name[80];
|
||||||
|
struct problem_context pctx;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
int file_type = 0;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
pctx.ino = ino;
|
||||||
|
|
||||||
|
if (!ctx->bad_lost_and_found && !ctx->lost_and_found) {
|
||||||
|
if (e2fsck_get_lost_and_found(ctx, 1) == 0)
|
||||||
|
ctx->bad_lost_and_found++;
|
||||||
|
}
|
||||||
|
if (ctx->bad_lost_and_found) {
|
||||||
|
fix_problem(ctx, PR_3_NO_LPF, &pctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(name, "#%u", ino);
|
||||||
|
if (ext2fs_read_inode(fs, ino, &inode) == 0)
|
||||||
|
file_type = ext2_file_type(inode.i_mode);
|
||||||
|
retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type);
|
||||||
|
if (retval == EXT2_ET_DIR_NO_SPACE) {
|
||||||
|
if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
|
||||||
|
return 1;
|
||||||
|
retval = e2fsck_expand_directory(ctx, ctx->lost_and_found,
|
||||||
|
1, 0);
|
||||||
|
if (retval) {
|
||||||
|
pctx.errcode = retval;
|
||||||
|
fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
retval = ext2fs_link(fs, ctx->lost_and_found, name,
|
||||||
|
ino, file_type);
|
||||||
|
}
|
||||||
|
if (retval) {
|
||||||
|
pctx.errcode = retval;
|
||||||
|
fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
e2fsck_adjust_inode_count(ctx, ino, 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility routine to adjust the inode counts on an inode.
|
||||||
|
*/
|
||||||
|
errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
errcode_t retval;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
|
||||||
|
if (!ino)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
retval = ext2fs_read_inode(fs, ino, &inode);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
printf("Adjusting link count for inode %lu by %d (from %d)\n", ino, adj,
|
||||||
|
inode.i_links_count);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (adj == 1) {
|
||||||
|
ext2fs_icount_increment(ctx->inode_count, ino, 0);
|
||||||
|
if (inode.i_links_count == (__u16) ~0)
|
||||||
|
return 0;
|
||||||
|
ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
|
||||||
|
inode.i_links_count++;
|
||||||
|
} else if (adj == -1) {
|
||||||
|
ext2fs_icount_decrement(ctx->inode_count, ino, 0);
|
||||||
|
if (inode.i_links_count == 0)
|
||||||
|
return 0;
|
||||||
|
ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
|
||||||
|
inode.i_links_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = ext2fs_write_inode(fs, ino, &inode);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fix parent --- this routine fixes up the parent of a directory.
|
||||||
|
*/
|
||||||
|
struct fix_dotdot_struct {
|
||||||
|
ext2_filsys fs;
|
||||||
|
ext2_ino_t parent;
|
||||||
|
int done;
|
||||||
|
e2fsck_t ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
|
||||||
|
int offset EXT2FS_ATTR((unused)),
|
||||||
|
int blocksize EXT2FS_ATTR((unused)),
|
||||||
|
char *buf EXT2FS_ATTR((unused)),
|
||||||
|
void *priv_data)
|
||||||
|
{
|
||||||
|
struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
|
||||||
|
errcode_t retval;
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
if ((dirent->name_len & 0xFF) != 2)
|
||||||
|
return 0;
|
||||||
|
if (strncmp(dirent->name, "..", 2))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1);
|
||||||
|
if (retval) {
|
||||||
|
pctx.errcode = retval;
|
||||||
|
fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
|
||||||
|
}
|
||||||
|
retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1);
|
||||||
|
if (retval) {
|
||||||
|
pctx.errcode = retval;
|
||||||
|
fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
|
||||||
|
}
|
||||||
|
dirent->inode = fp->parent;
|
||||||
|
|
||||||
|
fp->done++;
|
||||||
|
return DIRENT_ABORT | DIRENT_CHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
errcode_t retval;
|
||||||
|
struct fix_dotdot_struct fp;
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
fp.fs = fs;
|
||||||
|
fp.parent = parent;
|
||||||
|
fp.done = 0;
|
||||||
|
fp.ctx = ctx;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
printf("Fixing '..' of inode %lu to be %lu...\n", dir->ino, parent);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
|
||||||
|
0, fix_dotdot_proc, &fp);
|
||||||
|
if (retval || !fp.done) {
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
pctx.ino = dir->ino;
|
||||||
|
pctx.errcode = retval;
|
||||||
|
fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
|
||||||
|
PR_3_FIX_PARENT_NOFIND, &pctx);
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
}
|
||||||
|
dir->dotdot = parent;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These routines are responsible for expanding a /lost+found if it is
|
||||||
|
* too small.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct expand_dir_struct {
|
||||||
|
int num;
|
||||||
|
int guaranteed_size;
|
||||||
|
int newblocks;
|
||||||
|
int last_block;
|
||||||
|
errcode_t err;
|
||||||
|
e2fsck_t ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int expand_dir_proc(ext2_filsys fs,
|
||||||
|
blk_t *blocknr,
|
||||||
|
e2_blkcnt_t blockcnt,
|
||||||
|
blk_t ref_block EXT2FS_ATTR((unused)),
|
||||||
|
int ref_offset EXT2FS_ATTR((unused)),
|
||||||
|
void *priv_data)
|
||||||
|
{
|
||||||
|
struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
|
||||||
|
blk_t new_blk;
|
||||||
|
static blk_t last_blk = 0;
|
||||||
|
char *block;
|
||||||
|
errcode_t retval;
|
||||||
|
e2fsck_t ctx;
|
||||||
|
|
||||||
|
ctx = es->ctx;
|
||||||
|
|
||||||
|
if (es->guaranteed_size && blockcnt >= es->guaranteed_size)
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
|
||||||
|
if (blockcnt > 0)
|
||||||
|
es->last_block = blockcnt;
|
||||||
|
if (*blocknr) {
|
||||||
|
last_blk = *blocknr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
|
||||||
|
&new_blk);
|
||||||
|
if (retval) {
|
||||||
|
es->err = retval;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
if (blockcnt > 0) {
|
||||||
|
retval = ext2fs_new_dir_block(fs, 0, 0, &block);
|
||||||
|
if (retval) {
|
||||||
|
es->err = retval;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
es->num--;
|
||||||
|
retval = ext2fs_write_dir_block(fs, new_blk, block);
|
||||||
|
} else {
|
||||||
|
retval = ext2fs_get_mem(fs->blocksize, &block);
|
||||||
|
if (retval) {
|
||||||
|
es->err = retval;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
memset(block, 0, fs->blocksize);
|
||||||
|
retval = io_channel_write_blk(fs->io, new_blk, 1, block);
|
||||||
|
}
|
||||||
|
if (retval) {
|
||||||
|
es->err = retval;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
ext2fs_free_mem(&block);
|
||||||
|
*blocknr = new_blk;
|
||||||
|
ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
|
||||||
|
ext2fs_block_alloc_stats(fs, new_blk, +1);
|
||||||
|
es->newblocks++;
|
||||||
|
|
||||||
|
if (es->num == 0)
|
||||||
|
return (BLOCK_CHANGED | BLOCK_ABORT);
|
||||||
|
else
|
||||||
|
return BLOCK_CHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
|
||||||
|
int num, int guaranteed_size)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
errcode_t retval;
|
||||||
|
struct expand_dir_struct es;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
|
||||||
|
if (!(fs->flags & EXT2_FLAG_RW))
|
||||||
|
return EXT2_ET_RO_FILSYS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the inode and block bitmaps in; we'll be messing with
|
||||||
|
* them.
|
||||||
|
*/
|
||||||
|
e2fsck_read_bitmaps(ctx);
|
||||||
|
|
||||||
|
retval = ext2fs_check_directory(fs, dir);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
es.num = num;
|
||||||
|
es.guaranteed_size = guaranteed_size;
|
||||||
|
es.last_block = 0;
|
||||||
|
es.err = 0;
|
||||||
|
es.newblocks = 0;
|
||||||
|
es.ctx = ctx;
|
||||||
|
|
||||||
|
retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
|
||||||
|
0, expand_dir_proc, &es);
|
||||||
|
|
||||||
|
if (es.err)
|
||||||
|
return es.err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the size and block count fields in the inode.
|
||||||
|
*/
|
||||||
|
retval = ext2fs_read_inode(fs, dir, &inode);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
inode.i_size = (es.last_block + 1) * fs->blocksize;
|
||||||
|
inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
|
||||||
|
|
||||||
|
e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
178
e2fsprogs/e2fsck/pass4.c
Normal file
178
e2fsprogs/e2fsck/pass4.c
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
* pass4.c -- pass #4 of e2fsck: Check reference counts
|
||||||
|
*
|
||||||
|
* Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*
|
||||||
|
* Pass 4 frees the following data structures:
|
||||||
|
* - A bitmap of which inodes are in bad blocks. (inode_bb_map)
|
||||||
|
* - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "e2fsck.h"
|
||||||
|
#include "problem.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This routine is called when an inode is not connected to the
|
||||||
|
* directory tree.
|
||||||
|
*
|
||||||
|
* This subroutine returns 1 then the caller shouldn't bother with the
|
||||||
|
* rest of the pass 4 tests.
|
||||||
|
*/
|
||||||
|
static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
pctx.ino = i;
|
||||||
|
pctx.inode = &inode;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Offer to delete any zero-length files that does not have
|
||||||
|
* blocks. If there is an EA block, it might have useful
|
||||||
|
* information, so we won't prompt to delete it, but let it be
|
||||||
|
* reconnected to lost+found.
|
||||||
|
*/
|
||||||
|
if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
|
||||||
|
LINUX_S_ISDIR(inode.i_mode))) {
|
||||||
|
if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
|
||||||
|
ext2fs_icount_store(ctx->inode_link_info, i, 0);
|
||||||
|
inode.i_links_count = 0;
|
||||||
|
inode.i_dtime = time(0);
|
||||||
|
e2fsck_write_inode(ctx, i, &inode,
|
||||||
|
"disconnect_inode");
|
||||||
|
/*
|
||||||
|
* Fix up the bitmaps...
|
||||||
|
*/
|
||||||
|
e2fsck_read_bitmaps(ctx);
|
||||||
|
ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
|
||||||
|
ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
|
||||||
|
ext2fs_inode_alloc_stats2(fs, i, -1,
|
||||||
|
LINUX_S_ISDIR(inode.i_mode));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prompt to reconnect.
|
||||||
|
*/
|
||||||
|
if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
|
||||||
|
if (e2fsck_reconnect_file(ctx, i))
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* If we don't attach the inode, then skip the
|
||||||
|
* i_links_test since there's no point in trying to
|
||||||
|
* force i_links_count to zero.
|
||||||
|
*/
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void e2fsck_pass4(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
ext2_ino_t i;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
struct resource_track rtrack;
|
||||||
|
#endif
|
||||||
|
struct problem_context pctx;
|
||||||
|
__u16 link_count, link_counted;
|
||||||
|
char *buf = 0;
|
||||||
|
int group, maxgroup;
|
||||||
|
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
init_resource_track(&rtrack);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MTRACE
|
||||||
|
mtrace_print("Pass 4");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
if (!(ctx->options & E2F_OPT_PREEN))
|
||||||
|
fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
|
||||||
|
|
||||||
|
group = 0;
|
||||||
|
maxgroup = fs->group_desc_count;
|
||||||
|
if (ctx->progress)
|
||||||
|
if ((ctx->progress)(ctx, 4, 0, maxgroup))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i=1; i <= fs->super->s_inodes_count; i++) {
|
||||||
|
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
||||||
|
return;
|
||||||
|
if ((i % fs->super->s_inodes_per_group) == 0) {
|
||||||
|
group++;
|
||||||
|
if (ctx->progress)
|
||||||
|
if ((ctx->progress)(ctx, 4, group, maxgroup))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (i == EXT2_BAD_INO ||
|
||||||
|
(i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
|
||||||
|
continue;
|
||||||
|
if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
|
||||||
|
(ctx->inode_imagic_map &&
|
||||||
|
ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)) ||
|
||||||
|
(ctx->inode_bb_map &&
|
||||||
|
ext2fs_test_inode_bitmap(ctx->inode_bb_map, i)))
|
||||||
|
continue;
|
||||||
|
ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
|
||||||
|
ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
|
||||||
|
if (link_counted == 0) {
|
||||||
|
if (!buf)
|
||||||
|
buf = e2fsck_allocate_memory(ctx,
|
||||||
|
fs->blocksize, "bad_inode buffer");
|
||||||
|
if (e2fsck_process_bad_inode(ctx, 0, i, buf))
|
||||||
|
continue;
|
||||||
|
if (disconnect_inode(ctx, i))
|
||||||
|
continue;
|
||||||
|
ext2fs_icount_fetch(ctx->inode_link_info, i,
|
||||||
|
&link_count);
|
||||||
|
ext2fs_icount_fetch(ctx->inode_count, i,
|
||||||
|
&link_counted);
|
||||||
|
}
|
||||||
|
if (link_counted != link_count) {
|
||||||
|
e2fsck_read_inode(ctx, i, &inode, "pass4");
|
||||||
|
pctx.ino = i;
|
||||||
|
pctx.inode = &inode;
|
||||||
|
if (link_count != inode.i_links_count) {
|
||||||
|
pctx.num = link_count;
|
||||||
|
fix_problem(ctx,
|
||||||
|
PR_4_INCONSISTENT_COUNT, &pctx);
|
||||||
|
}
|
||||||
|
pctx.num = link_counted;
|
||||||
|
if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
|
||||||
|
inode.i_links_count = link_counted;
|
||||||
|
e2fsck_write_inode(ctx, i, &inode, "pass4");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
|
||||||
|
ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
|
||||||
|
ext2fs_free_inode_bitmap(ctx->inode_bb_map);
|
||||||
|
ctx->inode_bb_map = 0;
|
||||||
|
ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
|
||||||
|
ctx->inode_imagic_map = 0;
|
||||||
|
if (buf)
|
||||||
|
ext2fs_free_mem(&buf);
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
if (ctx->options & E2F_OPT_TIME2) {
|
||||||
|
e2fsck_clear_progbar(ctx);
|
||||||
|
print_resource_track(_("Pass 4"), &rtrack);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
547
e2fsprogs/e2fsck/pass5.c
Normal file
547
e2fsprogs/e2fsck/pass5.c
Normal file
|
@ -0,0 +1,547 @@
|
||||||
|
/*
|
||||||
|
* pass5.c --- check block and inode bitmaps against on-disk bitmaps
|
||||||
|
*
|
||||||
|
* Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "e2fsck.h"
|
||||||
|
#include "problem.h"
|
||||||
|
|
||||||
|
static void check_block_bitmaps(e2fsck_t ctx);
|
||||||
|
static void check_inode_bitmaps(e2fsck_t ctx);
|
||||||
|
static void check_inode_end(e2fsck_t ctx);
|
||||||
|
static void check_block_end(e2fsck_t ctx);
|
||||||
|
|
||||||
|
void e2fsck_pass5(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
struct resource_track rtrack;
|
||||||
|
#endif
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
#ifdef MTRACE
|
||||||
|
mtrace_print("Pass 5");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
init_resource_track(&rtrack);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
if (!(ctx->options & E2F_OPT_PREEN))
|
||||||
|
fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
|
||||||
|
|
||||||
|
if (ctx->progress)
|
||||||
|
if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
|
||||||
|
return;
|
||||||
|
|
||||||
|
e2fsck_read_bitmaps(ctx);
|
||||||
|
|
||||||
|
check_block_bitmaps(ctx);
|
||||||
|
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
||||||
|
return;
|
||||||
|
check_inode_bitmaps(ctx);
|
||||||
|
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
||||||
|
return;
|
||||||
|
check_inode_end(ctx);
|
||||||
|
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
||||||
|
return;
|
||||||
|
check_block_end(ctx);
|
||||||
|
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ext2fs_free_inode_bitmap(ctx->inode_used_map);
|
||||||
|
ctx->inode_used_map = 0;
|
||||||
|
ext2fs_free_inode_bitmap(ctx->inode_dir_map);
|
||||||
|
ctx->inode_dir_map = 0;
|
||||||
|
ext2fs_free_block_bitmap(ctx->block_found_map);
|
||||||
|
ctx->block_found_map = 0;
|
||||||
|
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
if (ctx->options & E2F_OPT_TIME2) {
|
||||||
|
e2fsck_clear_progbar(ctx);
|
||||||
|
print_resource_track(_("Pass 5"), &rtrack);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NO_BLK ((blk_t) -1)
|
||||||
|
|
||||||
|
static void print_bitmap_problem(e2fsck_t ctx, int problem,
|
||||||
|
struct problem_context *pctx)
|
||||||
|
{
|
||||||
|
switch (problem) {
|
||||||
|
case PR_5_BLOCK_UNUSED:
|
||||||
|
if (pctx->blk == pctx->blk2)
|
||||||
|
pctx->blk2 = 0;
|
||||||
|
else
|
||||||
|
problem = PR_5_BLOCK_RANGE_UNUSED;
|
||||||
|
break;
|
||||||
|
case PR_5_BLOCK_USED:
|
||||||
|
if (pctx->blk == pctx->blk2)
|
||||||
|
pctx->blk2 = 0;
|
||||||
|
else
|
||||||
|
problem = PR_5_BLOCK_RANGE_USED;
|
||||||
|
break;
|
||||||
|
case PR_5_INODE_UNUSED:
|
||||||
|
if (pctx->ino == pctx->ino2)
|
||||||
|
pctx->ino2 = 0;
|
||||||
|
else
|
||||||
|
problem = PR_5_INODE_RANGE_UNUSED;
|
||||||
|
break;
|
||||||
|
case PR_5_INODE_USED:
|
||||||
|
if (pctx->ino == pctx->ino2)
|
||||||
|
pctx->ino2 = 0;
|
||||||
|
else
|
||||||
|
problem = PR_5_INODE_RANGE_USED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fix_problem(ctx, problem, pctx);
|
||||||
|
pctx->blk = pctx->blk2 = NO_BLK;
|
||||||
|
pctx->ino = pctx->ino2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_block_bitmaps(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
blk_t i;
|
||||||
|
int *free_array;
|
||||||
|
int group = 0;
|
||||||
|
unsigned int blocks = 0;
|
||||||
|
unsigned int free_blocks = 0;
|
||||||
|
int group_free = 0;
|
||||||
|
int actual, bitmap;
|
||||||
|
struct problem_context pctx;
|
||||||
|
int problem, save_problem, fixit, had_problem;
|
||||||
|
errcode_t retval;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
free_array = (int *) e2fsck_allocate_memory(ctx,
|
||||||
|
fs->group_desc_count * sizeof(int), "free block count array");
|
||||||
|
|
||||||
|
if ((fs->super->s_first_data_block <
|
||||||
|
ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
|
||||||
|
(fs->super->s_blocks_count-1 >
|
||||||
|
ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
|
||||||
|
pctx.num = 1;
|
||||||
|
pctx.blk = fs->super->s_first_data_block;
|
||||||
|
pctx.blk2 = fs->super->s_blocks_count -1;
|
||||||
|
pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
|
||||||
|
pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
|
||||||
|
fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
|
||||||
|
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT; /* fatal */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((fs->super->s_first_data_block <
|
||||||
|
ext2fs_get_block_bitmap_start(fs->block_map)) ||
|
||||||
|
(fs->super->s_blocks_count-1 >
|
||||||
|
ext2fs_get_block_bitmap_end(fs->block_map))) {
|
||||||
|
pctx.num = 2;
|
||||||
|
pctx.blk = fs->super->s_first_data_block;
|
||||||
|
pctx.blk2 = fs->super->s_blocks_count -1;
|
||||||
|
pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
|
||||||
|
pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
|
||||||
|
fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
|
||||||
|
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT; /* fatal */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
redo_counts:
|
||||||
|
had_problem = 0;
|
||||||
|
save_problem = 0;
|
||||||
|
pctx.blk = pctx.blk2 = NO_BLK;
|
||||||
|
for (i = fs->super->s_first_data_block;
|
||||||
|
i < fs->super->s_blocks_count;
|
||||||
|
i++) {
|
||||||
|
actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
|
||||||
|
bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
|
||||||
|
|
||||||
|
if (actual == bitmap)
|
||||||
|
goto do_counts;
|
||||||
|
|
||||||
|
if (!actual && bitmap) {
|
||||||
|
/*
|
||||||
|
* Block not used, but marked in use in the bitmap.
|
||||||
|
*/
|
||||||
|
problem = PR_5_BLOCK_UNUSED;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Block used, but not marked in use in the bitmap.
|
||||||
|
*/
|
||||||
|
problem = PR_5_BLOCK_USED;
|
||||||
|
}
|
||||||
|
if (pctx.blk == NO_BLK) {
|
||||||
|
pctx.blk = pctx.blk2 = i;
|
||||||
|
save_problem = problem;
|
||||||
|
} else {
|
||||||
|
if ((problem == save_problem) &&
|
||||||
|
(pctx.blk2 == i-1))
|
||||||
|
pctx.blk2++;
|
||||||
|
else {
|
||||||
|
print_bitmap_problem(ctx, save_problem, &pctx);
|
||||||
|
pctx.blk = pctx.blk2 = i;
|
||||||
|
save_problem = problem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
|
||||||
|
had_problem++;
|
||||||
|
|
||||||
|
do_counts:
|
||||||
|
if (!bitmap) {
|
||||||
|
group_free++;
|
||||||
|
free_blocks++;
|
||||||
|
}
|
||||||
|
blocks ++;
|
||||||
|
if ((blocks == fs->super->s_blocks_per_group) ||
|
||||||
|
(i == fs->super->s_blocks_count-1)) {
|
||||||
|
free_array[group] = group_free;
|
||||||
|
group ++;
|
||||||
|
blocks = 0;
|
||||||
|
group_free = 0;
|
||||||
|
if (ctx->progress)
|
||||||
|
if ((ctx->progress)(ctx, 5, group,
|
||||||
|
fs->group_desc_count*2))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pctx.blk != NO_BLK)
|
||||||
|
print_bitmap_problem(ctx, save_problem, &pctx);
|
||||||
|
if (had_problem)
|
||||||
|
fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
|
||||||
|
else
|
||||||
|
fixit = -1;
|
||||||
|
ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
|
||||||
|
|
||||||
|
if (fixit == 1) {
|
||||||
|
ext2fs_free_block_bitmap(fs->block_map);
|
||||||
|
retval = ext2fs_copy_bitmap(ctx->block_found_map,
|
||||||
|
&fs->block_map);
|
||||||
|
if (retval) {
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ext2fs_set_bitmap_padding(fs->block_map);
|
||||||
|
ext2fs_mark_bb_dirty(fs);
|
||||||
|
|
||||||
|
/* Redo the counts */
|
||||||
|
blocks = 0; free_blocks = 0; group_free = 0; group = 0;
|
||||||
|
memset(free_array, 0, fs->group_desc_count * sizeof(int));
|
||||||
|
goto redo_counts;
|
||||||
|
} else if (fixit == 0)
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
|
||||||
|
for (i = 0; i < fs->group_desc_count; i++) {
|
||||||
|
if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
|
||||||
|
pctx.group = i;
|
||||||
|
pctx.blk = fs->group_desc[i].bg_free_blocks_count;
|
||||||
|
pctx.blk2 = free_array[i];
|
||||||
|
|
||||||
|
if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
|
||||||
|
&pctx)) {
|
||||||
|
fs->group_desc[i].bg_free_blocks_count =
|
||||||
|
free_array[i];
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
} else
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (free_blocks != fs->super->s_free_blocks_count) {
|
||||||
|
pctx.group = 0;
|
||||||
|
pctx.blk = fs->super->s_free_blocks_count;
|
||||||
|
pctx.blk2 = free_blocks;
|
||||||
|
|
||||||
|
if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
|
||||||
|
fs->super->s_free_blocks_count = free_blocks;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
} else
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
}
|
||||||
|
ext2fs_free_mem(&free_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_inode_bitmaps(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
ext2_ino_t i;
|
||||||
|
unsigned int free_inodes = 0;
|
||||||
|
int group_free = 0;
|
||||||
|
int dirs_count = 0;
|
||||||
|
int group = 0;
|
||||||
|
unsigned int inodes = 0;
|
||||||
|
int *free_array;
|
||||||
|
int *dir_array;
|
||||||
|
int actual, bitmap;
|
||||||
|
errcode_t retval;
|
||||||
|
struct problem_context pctx;
|
||||||
|
int problem, save_problem, fixit, had_problem;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
free_array = (int *) e2fsck_allocate_memory(ctx,
|
||||||
|
fs->group_desc_count * sizeof(int), "free inode count array");
|
||||||
|
|
||||||
|
dir_array = (int *) e2fsck_allocate_memory(ctx,
|
||||||
|
fs->group_desc_count * sizeof(int), "directory count array");
|
||||||
|
|
||||||
|
if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
|
||||||
|
(fs->super->s_inodes_count >
|
||||||
|
ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
|
||||||
|
pctx.num = 3;
|
||||||
|
pctx.blk = 1;
|
||||||
|
pctx.blk2 = fs->super->s_inodes_count;
|
||||||
|
pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
|
||||||
|
pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
|
||||||
|
fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
|
||||||
|
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT; /* fatal */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
|
||||||
|
(fs->super->s_inodes_count >
|
||||||
|
ext2fs_get_inode_bitmap_end(fs->inode_map))) {
|
||||||
|
pctx.num = 4;
|
||||||
|
pctx.blk = 1;
|
||||||
|
pctx.blk2 = fs->super->s_inodes_count;
|
||||||
|
pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
|
||||||
|
pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
|
||||||
|
fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
|
||||||
|
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT; /* fatal */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
redo_counts:
|
||||||
|
had_problem = 0;
|
||||||
|
save_problem = 0;
|
||||||
|
pctx.ino = pctx.ino2 = 0;
|
||||||
|
for (i = 1; i <= fs->super->s_inodes_count; i++) {
|
||||||
|
actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
|
||||||
|
bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
|
||||||
|
|
||||||
|
if (actual == bitmap)
|
||||||
|
goto do_counts;
|
||||||
|
|
||||||
|
if (!actual && bitmap) {
|
||||||
|
/*
|
||||||
|
* Inode wasn't used, but marked in bitmap
|
||||||
|
*/
|
||||||
|
problem = PR_5_INODE_UNUSED;
|
||||||
|
} else /* if (actual && !bitmap) */ {
|
||||||
|
/*
|
||||||
|
* Inode used, but not in bitmap
|
||||||
|
*/
|
||||||
|
problem = PR_5_INODE_USED;
|
||||||
|
}
|
||||||
|
if (pctx.ino == 0) {
|
||||||
|
pctx.ino = pctx.ino2 = i;
|
||||||
|
save_problem = problem;
|
||||||
|
} else {
|
||||||
|
if ((problem == save_problem) &&
|
||||||
|
(pctx.ino2 == i-1))
|
||||||
|
pctx.ino2++;
|
||||||
|
else {
|
||||||
|
print_bitmap_problem(ctx, save_problem, &pctx);
|
||||||
|
pctx.ino = pctx.ino2 = i;
|
||||||
|
save_problem = problem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
|
||||||
|
had_problem++;
|
||||||
|
|
||||||
|
do_counts:
|
||||||
|
if (!bitmap) {
|
||||||
|
group_free++;
|
||||||
|
free_inodes++;
|
||||||
|
} else {
|
||||||
|
if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
|
||||||
|
dirs_count++;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
group ++;
|
||||||
|
inodes = 0;
|
||||||
|
group_free = 0;
|
||||||
|
dirs_count = 0;
|
||||||
|
if (ctx->progress)
|
||||||
|
if ((ctx->progress)(ctx, 5,
|
||||||
|
group + fs->group_desc_count,
|
||||||
|
fs->group_desc_count*2))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pctx.ino)
|
||||||
|
print_bitmap_problem(ctx, save_problem, &pctx);
|
||||||
|
|
||||||
|
if (had_problem)
|
||||||
|
fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
|
||||||
|
else
|
||||||
|
fixit = -1;
|
||||||
|
ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
|
||||||
|
|
||||||
|
if (fixit == 1) {
|
||||||
|
ext2fs_free_inode_bitmap(fs->inode_map);
|
||||||
|
retval = ext2fs_copy_bitmap(ctx->inode_used_map,
|
||||||
|
&fs->inode_map);
|
||||||
|
if (retval) {
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ext2fs_set_bitmap_padding(fs->inode_map);
|
||||||
|
ext2fs_mark_ib_dirty(fs);
|
||||||
|
|
||||||
|
/* redo counts */
|
||||||
|
inodes = 0; free_inodes = 0; group_free = 0;
|
||||||
|
dirs_count = 0; group = 0;
|
||||||
|
memset(free_array, 0, fs->group_desc_count * sizeof(int));
|
||||||
|
memset(dir_array, 0, fs->group_desc_count * sizeof(int));
|
||||||
|
goto redo_counts;
|
||||||
|
} else if (fixit == 0)
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
|
||||||
|
for (i = 0; i < fs->group_desc_count; i++) {
|
||||||
|
if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
|
||||||
|
pctx.group = i;
|
||||||
|
pctx.ino = fs->group_desc[i].bg_free_inodes_count;
|
||||||
|
pctx.ino2 = free_array[i];
|
||||||
|
if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
|
||||||
|
&pctx)) {
|
||||||
|
fs->group_desc[i].bg_free_inodes_count =
|
||||||
|
free_array[i];
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
} else
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
}
|
||||||
|
if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
|
||||||
|
pctx.group = i;
|
||||||
|
pctx.ino = fs->group_desc[i].bg_used_dirs_count;
|
||||||
|
pctx.ino2 = dir_array[i];
|
||||||
|
|
||||||
|
if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
|
||||||
|
&pctx)) {
|
||||||
|
fs->group_desc[i].bg_used_dirs_count =
|
||||||
|
dir_array[i];
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
} else
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (free_inodes != fs->super->s_free_inodes_count) {
|
||||||
|
pctx.group = -1;
|
||||||
|
pctx.ino = fs->super->s_free_inodes_count;
|
||||||
|
pctx.ino2 = free_inodes;
|
||||||
|
|
||||||
|
if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
|
||||||
|
fs->super->s_free_inodes_count = free_inodes;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
} else
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
}
|
||||||
|
ext2fs_free_mem(&free_array);
|
||||||
|
ext2fs_free_mem(&dir_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_inode_end(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
ext2_ino_t end, save_inodes_count, i;
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
|
||||||
|
pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
|
||||||
|
&save_inodes_count);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.num = 1;
|
||||||
|
fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT; /* fatal */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (save_inodes_count == end)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = save_inodes_count + 1; i <= end; i++) {
|
||||||
|
if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
|
||||||
|
if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
|
||||||
|
for (i = save_inodes_count + 1; i <= end; i++)
|
||||||
|
ext2fs_mark_inode_bitmap(fs->inode_map,
|
||||||
|
i);
|
||||||
|
ext2fs_mark_ib_dirty(fs);
|
||||||
|
} else
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
|
||||||
|
save_inodes_count, 0);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.num = 2;
|
||||||
|
fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT; /* fatal */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_block_end(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
blk_t end, save_blocks_count, i;
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
end = fs->block_map->start +
|
||||||
|
(EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
|
||||||
|
pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
|
||||||
|
&save_blocks_count);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.num = 3;
|
||||||
|
fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT; /* fatal */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (save_blocks_count == end)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = save_blocks_count + 1; i <= end; i++) {
|
||||||
|
if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
|
||||||
|
if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
|
||||||
|
for (i = save_blocks_count + 1; i <= end; i++)
|
||||||
|
ext2fs_mark_block_bitmap(fs->block_map,
|
||||||
|
i);
|
||||||
|
ext2fs_mark_bb_dirty(fs);
|
||||||
|
} else
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
|
||||||
|
save_blocks_count, 0);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
pctx.num = 4;
|
||||||
|
fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT; /* fatal */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
1655
e2fsprogs/e2fsck/problem.c
Normal file
1655
e2fsprogs/e2fsck/problem.c
Normal file
File diff suppressed because it is too large
Load diff
897
e2fsprogs/e2fsck/problem.h
Normal file
897
e2fsprogs/e2fsck/problem.h
Normal file
|
@ -0,0 +1,897 @@
|
||||||
|
/*
|
||||||
|
* problem.h --- e2fsck problem error codes
|
||||||
|
*
|
||||||
|
* Copyright 1996 by Theodore Ts'o
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef __u32 problem_t;
|
||||||
|
|
||||||
|
struct problem_context {
|
||||||
|
errcode_t errcode;
|
||||||
|
ext2_ino_t ino, ino2, dir;
|
||||||
|
struct ext2_inode *inode;
|
||||||
|
struct ext2_dir_entry *dirent;
|
||||||
|
blk_t blk, blk2;
|
||||||
|
e2_blkcnt_t blkcount;
|
||||||
|
int group;
|
||||||
|
__u64 num;
|
||||||
|
const char *str;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We define a set of "latch groups"; these are problems which are
|
||||||
|
* handled as a set. The user answers once for a particular latch
|
||||||
|
* group.
|
||||||
|
*/
|
||||||
|
#define PR_LATCH_MASK 0x0ff0 /* Latch mask */
|
||||||
|
#define PR_LATCH_BLOCK 0x0010 /* Latch for illegal blocks (pass 1) */
|
||||||
|
#define PR_LATCH_BBLOCK 0x0020 /* Latch for bad block inode blocks (pass 1) */
|
||||||
|
#define PR_LATCH_IBITMAP 0x0030 /* Latch for pass 5 inode bitmap proc. */
|
||||||
|
#define PR_LATCH_BBITMAP 0x0040 /* Latch for pass 5 inode bitmap proc. */
|
||||||
|
#define PR_LATCH_RELOC 0x0050 /* Latch for superblock relocate hint */
|
||||||
|
#define PR_LATCH_DBLOCK 0x0060 /* Latch for pass 1b dup block headers */
|
||||||
|
#define PR_LATCH_LOW_DTIME 0x0070 /* Latch for pass1 orphaned list refugees */
|
||||||
|
#define PR_LATCH_TOOBIG 0x0080 /* Latch for file to big errors */
|
||||||
|
#define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */
|
||||||
|
|
||||||
|
#define PR_LATCH(x) ((((x) & PR_LATCH_MASK) >> 4) - 1)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Latch group descriptor flags
|
||||||
|
*/
|
||||||
|
#define PRL_YES 0x0001 /* Answer yes */
|
||||||
|
#define PRL_NO 0x0002 /* Answer no */
|
||||||
|
#define PRL_LATCHED 0x0004 /* The latch group is latched */
|
||||||
|
#define PRL_SUPPRESS 0x0008 /* Suppress all latch group questions */
|
||||||
|
|
||||||
|
#define PRL_VARIABLE 0x000f /* All the flags that need to be reset */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pre-Pass 1 errors
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Block bitmap not in group */
|
||||||
|
#define PR_0_BB_NOT_GROUP 0x000001
|
||||||
|
|
||||||
|
/* Inode bitmap not in group */
|
||||||
|
#define PR_0_IB_NOT_GROUP 0x000002
|
||||||
|
|
||||||
|
/* Inode table not in group */
|
||||||
|
#define PR_0_ITABLE_NOT_GROUP 0x000003
|
||||||
|
|
||||||
|
/* Superblock corrupt */
|
||||||
|
#define PR_0_SB_CORRUPT 0x000004
|
||||||
|
|
||||||
|
/* Filesystem size is wrong */
|
||||||
|
#define PR_0_FS_SIZE_WRONG 0x000005
|
||||||
|
|
||||||
|
/* Fragments not supported */
|
||||||
|
#define PR_0_NO_FRAGMENTS 0x000006
|
||||||
|
|
||||||
|
/* Bad blocks_per_group */
|
||||||
|
#define PR_0_BLOCKS_PER_GROUP 0x000007
|
||||||
|
|
||||||
|
/* Bad first_data_block */
|
||||||
|
#define PR_0_FIRST_DATA_BLOCK 0x000008
|
||||||
|
|
||||||
|
/* Adding UUID to filesystem */
|
||||||
|
#define PR_0_ADD_UUID 0x000009
|
||||||
|
|
||||||
|
/* Relocate hint */
|
||||||
|
#define PR_0_RELOCATE_HINT 0x00000A
|
||||||
|
|
||||||
|
/* Miscellaneous superblock corruption */
|
||||||
|
#define PR_0_MISC_CORRUPT_SUPER 0x00000B
|
||||||
|
|
||||||
|
/* Error determing physical device size of filesystem */
|
||||||
|
#define PR_0_GETSIZE_ERROR 0x00000C
|
||||||
|
|
||||||
|
/* Inode count in the superblock incorrect */
|
||||||
|
#define PR_0_INODE_COUNT_WRONG 0x00000D
|
||||||
|
|
||||||
|
/* The Hurd does not support the filetype feature */
|
||||||
|
#define PR_0_HURD_CLEAR_FILETYPE 0x00000E
|
||||||
|
|
||||||
|
/* Journal inode is invalid */
|
||||||
|
#define PR_0_JOURNAL_BAD_INODE 0x00000F
|
||||||
|
|
||||||
|
/* The external journal has multiple filesystems (which we can't handle yet) */
|
||||||
|
#define PR_0_JOURNAL_UNSUPP_MULTIFS 0x000010
|
||||||
|
|
||||||
|
/* Can't find external journal */
|
||||||
|
#define PR_0_CANT_FIND_JOURNAL 0x000011
|
||||||
|
|
||||||
|
/* External journal has bad superblock */
|
||||||
|
#define PR_0_EXT_JOURNAL_BAD_SUPER 0x000012
|
||||||
|
|
||||||
|
/* Superblock has a bad journal UUID */
|
||||||
|
#define PR_0_JOURNAL_BAD_UUID 0x000013
|
||||||
|
|
||||||
|
/* Journal has an unknown superblock type */
|
||||||
|
#define PR_0_JOURNAL_UNSUPP_SUPER 0x000014
|
||||||
|
|
||||||
|
/* Journal superblock is corrupt */
|
||||||
|
#define PR_0_JOURNAL_BAD_SUPER 0x000015
|
||||||
|
|
||||||
|
/* Journal superblock is corrupt */
|
||||||
|
#define PR_0_JOURNAL_HAS_JOURNAL 0x000016
|
||||||
|
|
||||||
|
/* Superblock has recovery flag set but no journal */
|
||||||
|
#define PR_0_JOURNAL_RECOVER_SET 0x000017
|
||||||
|
|
||||||
|
/* Journal has data, but recovery flag is clear */
|
||||||
|
#define PR_0_JOURNAL_RECOVERY_CLEAR 0x000018
|
||||||
|
|
||||||
|
/* Ask if we should clear the journal */
|
||||||
|
#define PR_0_JOURNAL_RESET_JOURNAL 0x000019
|
||||||
|
|
||||||
|
/* Filesystem revision is 0, but feature flags are set */
|
||||||
|
#define PR_0_FS_REV_LEVEL 0x00001A
|
||||||
|
|
||||||
|
/* Clearing orphan inode */
|
||||||
|
#define PR_0_ORPHAN_CLEAR_INODE 0x000020
|
||||||
|
|
||||||
|
/* Illegal block found in orphaned inode */
|
||||||
|
#define PR_0_ORPHAN_ILLEGAL_BLOCK_NUM 0x000021
|
||||||
|
|
||||||
|
/* Already cleared block found in orphaned inode */
|
||||||
|
#define PR_0_ORPHAN_ALREADY_CLEARED_BLOCK 0x000022
|
||||||
|
|
||||||
|
/* Illegal orphan inode in superblock */
|
||||||
|
#define PR_0_ORPHAN_ILLEGAL_HEAD_INODE 0x000023
|
||||||
|
|
||||||
|
/* Illegal inode in orphaned inode list */
|
||||||
|
#define PR_0_ORPHAN_ILLEGAL_INODE 0x000024
|
||||||
|
|
||||||
|
/* Journal has unsupported read-only feature - abort */
|
||||||
|
#define PR_0_JOURNAL_UNSUPP_ROCOMPAT 0x000025
|
||||||
|
|
||||||
|
/* Journal has unsupported incompatible feature - abort */
|
||||||
|
#define PR_0_JOURNAL_UNSUPP_INCOMPAT 0x000026
|
||||||
|
|
||||||
|
/* Journal has unsupported version number */
|
||||||
|
#define PR_0_JOURNAL_UNSUPP_VERSION 0x000027
|
||||||
|
|
||||||
|
/* Moving journal to hidden file */
|
||||||
|
#define PR_0_MOVE_JOURNAL 0x000028
|
||||||
|
|
||||||
|
/* Error moving journal */
|
||||||
|
#define PR_0_ERR_MOVE_JOURNAL 0x000029
|
||||||
|
|
||||||
|
/* Clearing V2 journal superblock */
|
||||||
|
#define PR_0_CLEAR_V2_JOURNAL 0x00002A
|
||||||
|
|
||||||
|
/* Run journal anyway */
|
||||||
|
#define PR_0_JOURNAL_RUN 0x00002B
|
||||||
|
|
||||||
|
/* Run journal anyway by default */
|
||||||
|
#define PR_0_JOURNAL_RUN_DEFAULT 0x00002C
|
||||||
|
|
||||||
|
/* Backup journal inode blocks */
|
||||||
|
#define PR_0_BACKUP_JNL 0x00002D
|
||||||
|
|
||||||
|
/* Reserved blocks w/o resize_inode */
|
||||||
|
#define PR_0_NONZERO_RESERVED_GDT_BLOCKS 0x00002E
|
||||||
|
|
||||||
|
/* Resize_inode not enabled, but resize inode is non-zero */
|
||||||
|
#define PR_0_CLEAR_RESIZE_INODE 0x00002F
|
||||||
|
|
||||||
|
/* Resize inode invalid */
|
||||||
|
#define PR_0_RESIZE_INODE_INVALID 0x000030
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass 1 errors
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Pass 1: Checking inodes, blocks, and sizes */
|
||||||
|
#define PR_1_PASS_HEADER 0x010000
|
||||||
|
|
||||||
|
/* Root directory is not an inode */
|
||||||
|
#define PR_1_ROOT_NO_DIR 0x010001
|
||||||
|
|
||||||
|
/* Root directory has dtime set */
|
||||||
|
#define PR_1_ROOT_DTIME 0x010002
|
||||||
|
|
||||||
|
/* Reserved inode has bad mode */
|
||||||
|
#define PR_1_RESERVED_BAD_MODE 0x010003
|
||||||
|
|
||||||
|
/* Deleted inode has zero dtime */
|
||||||
|
#define PR_1_ZERO_DTIME 0x010004
|
||||||
|
|
||||||
|
/* Inode in use, but dtime set */
|
||||||
|
#define PR_1_SET_DTIME 0x010005
|
||||||
|
|
||||||
|
/* Zero-length directory */
|
||||||
|
#define PR_1_ZERO_LENGTH_DIR 0x010006
|
||||||
|
|
||||||
|
/* Block bitmap conflicts with some other fs block */
|
||||||
|
#define PR_1_BB_CONFLICT 0x010007
|
||||||
|
|
||||||
|
/* Inode bitmap conflicts with some other fs block */
|
||||||
|
#define PR_1_IB_CONFLICT 0x010008
|
||||||
|
|
||||||
|
/* Inode table conflicts with some other fs block */
|
||||||
|
#define PR_1_ITABLE_CONFLICT 0x010009
|
||||||
|
|
||||||
|
/* Block bitmap is on a bad block */
|
||||||
|
#define PR_1_BB_BAD_BLOCK 0x01000A
|
||||||
|
|
||||||
|
/* Inode bitmap is on a bad block */
|
||||||
|
#define PR_1_IB_BAD_BLOCK 0x01000B
|
||||||
|
|
||||||
|
/* Inode has incorrect i_size */
|
||||||
|
#define PR_1_BAD_I_SIZE 0x01000C
|
||||||
|
|
||||||
|
/* Inode has incorrect i_blocks */
|
||||||
|
#define PR_1_BAD_I_BLOCKS 0x01000D
|
||||||
|
|
||||||
|
/* Illegal block number in inode */
|
||||||
|
#define PR_1_ILLEGAL_BLOCK_NUM 0x01000E
|
||||||
|
|
||||||
|
/* Block number overlaps fs metadata */
|
||||||
|
#define PR_1_BLOCK_OVERLAPS_METADATA 0x01000F
|
||||||
|
|
||||||
|
/* Inode has illegal blocks (latch question) */
|
||||||
|
#define PR_1_INODE_BLOCK_LATCH 0x010010
|
||||||
|
|
||||||
|
/* Too many bad blocks in inode */
|
||||||
|
#define PR_1_TOO_MANY_BAD_BLOCKS 0x010011
|
||||||
|
|
||||||
|
/* Illegal block number in bad block inode */
|
||||||
|
#define PR_1_BB_ILLEGAL_BLOCK_NUM 0x010012
|
||||||
|
|
||||||
|
/* Bad block inode has illegal blocks (latch question) */
|
||||||
|
#define PR_1_INODE_BBLOCK_LATCH 0x010013
|
||||||
|
|
||||||
|
/* Duplicate or bad blocks in use! */
|
||||||
|
#define PR_1_DUP_BLOCKS_PREENSTOP 0x010014
|
||||||
|
|
||||||
|
/* Bad block used as bad block indirect block */
|
||||||
|
#define PR_1_BBINODE_BAD_METABLOCK 0x010015
|
||||||
|
|
||||||
|
/* Inconsistency can't be fixed prompt */
|
||||||
|
#define PR_1_BBINODE_BAD_METABLOCK_PROMPT 0x010016
|
||||||
|
|
||||||
|
/* Bad primary block */
|
||||||
|
#define PR_1_BAD_PRIMARY_BLOCK 0x010017
|
||||||
|
|
||||||
|
/* Bad primary block prompt */
|
||||||
|
#define PR_1_BAD_PRIMARY_BLOCK_PROMPT 0x010018
|
||||||
|
|
||||||
|
/* Bad primary superblock */
|
||||||
|
#define PR_1_BAD_PRIMARY_SUPERBLOCK 0x010019
|
||||||
|
|
||||||
|
/* Bad primary block group descriptors */
|
||||||
|
#define PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR 0x01001A
|
||||||
|
|
||||||
|
/* Bad superblock in group */
|
||||||
|
#define PR_1_BAD_SUPERBLOCK 0x01001B
|
||||||
|
|
||||||
|
/* Bad block group descriptors in group */
|
||||||
|
#define PR_1_BAD_GROUP_DESCRIPTORS 0x01001C
|
||||||
|
|
||||||
|
/* Block claimed for no reason */
|
||||||
|
#define PR_1_PROGERR_CLAIMED_BLOCK 0x01001D
|
||||||
|
|
||||||
|
/* Error allocating blocks for relocating metadata */
|
||||||
|
#define PR_1_RELOC_BLOCK_ALLOCATE 0x01001E
|
||||||
|
|
||||||
|
/* Error allocating block buffer during relocation process */
|
||||||
|
#define PR_1_RELOC_MEMORY_ALLOCATE 0x01001F
|
||||||
|
|
||||||
|
/* Relocating metadata group information from X to Y */
|
||||||
|
#define PR_1_RELOC_FROM_TO 0x010020
|
||||||
|
|
||||||
|
/* Relocating metatdata group information to X */
|
||||||
|
#define PR_1_RELOC_TO 0x010021
|
||||||
|
|
||||||
|
/* Block read error during relocation process */
|
||||||
|
#define PR_1_RELOC_READ_ERR 0x010022
|
||||||
|
|
||||||
|
/* Block write error during relocation process */
|
||||||
|
#define PR_1_RELOC_WRITE_ERR 0x010023
|
||||||
|
|
||||||
|
/* Error allocating inode bitmap */
|
||||||
|
#define PR_1_ALLOCATE_IBITMAP_ERROR 0x010024
|
||||||
|
|
||||||
|
/* Error allocating block bitmap */
|
||||||
|
#define PR_1_ALLOCATE_BBITMAP_ERROR 0x010025
|
||||||
|
|
||||||
|
/* Error allocating icount structure */
|
||||||
|
#define PR_1_ALLOCATE_ICOUNT 0x010026
|
||||||
|
|
||||||
|
/* Error allocating dbcount */
|
||||||
|
#define PR_1_ALLOCATE_DBCOUNT 0x010027
|
||||||
|
|
||||||
|
/* Error while scanning inodes */
|
||||||
|
#define PR_1_ISCAN_ERROR 0x010028
|
||||||
|
|
||||||
|
/* Error while iterating over blocks */
|
||||||
|
#define PR_1_BLOCK_ITERATE 0x010029
|
||||||
|
|
||||||
|
/* Error while storing inode count information */
|
||||||
|
#define PR_1_ICOUNT_STORE 0x01002A
|
||||||
|
|
||||||
|
/* Error while storing directory block information */
|
||||||
|
#define PR_1_ADD_DBLOCK 0x01002B
|
||||||
|
|
||||||
|
/* Error while reading inode (for clearing) */
|
||||||
|
#define PR_1_READ_INODE 0x01002C
|
||||||
|
|
||||||
|
/* Suppress messages prompt */
|
||||||
|
#define PR_1_SUPPRESS_MESSAGES 0x01002D
|
||||||
|
|
||||||
|
/* Imagic flag set on an inode when filesystem doesn't support it */
|
||||||
|
#define PR_1_SET_IMAGIC 0x01002F
|
||||||
|
|
||||||
|
/* Immutable flag set on a device or socket inode */
|
||||||
|
#define PR_1_SET_IMMUTABLE 0x010030
|
||||||
|
|
||||||
|
/* Compression flag set on a non-compressed filesystem */
|
||||||
|
#define PR_1_COMPR_SET 0x010031
|
||||||
|
|
||||||
|
/* Non-zero size on on device, fifo or socket inode */
|
||||||
|
#define PR_1_SET_NONZSIZE 0x010032
|
||||||
|
|
||||||
|
/* Filesystem revision is 0, but feature flags are set */
|
||||||
|
#define PR_1_FS_REV_LEVEL 0x010033
|
||||||
|
|
||||||
|
/* Journal inode not in use, needs clearing */
|
||||||
|
#define PR_1_JOURNAL_INODE_NOT_CLEAR 0x010034
|
||||||
|
|
||||||
|
/* Journal inode has wrong mode */
|
||||||
|
#define PR_1_JOURNAL_BAD_MODE 0x010035
|
||||||
|
|
||||||
|
/* Inode that was part of orphan linked list */
|
||||||
|
#define PR_1_LOW_DTIME 0x010036
|
||||||
|
|
||||||
|
/* Latch question which asks how to deal with low dtime inodes */
|
||||||
|
#define PR_1_ORPHAN_LIST_REFUGEES 0x010037
|
||||||
|
|
||||||
|
/* Error allocating refcount structure */
|
||||||
|
#define PR_1_ALLOCATE_REFCOUNT 0x010038
|
||||||
|
|
||||||
|
/* Error reading Extended Attribute block */
|
||||||
|
#define PR_1_READ_EA_BLOCK 0x010039
|
||||||
|
|
||||||
|
/* Invalid Extended Attribute block */
|
||||||
|
#define PR_1_BAD_EA_BLOCK 0x01003A
|
||||||
|
|
||||||
|
/* Error reading Extended Attribute block while fixing refcount -- abort */
|
||||||
|
#define PR_1_EXTATTR_READ_ABORT 0x01003B
|
||||||
|
|
||||||
|
/* Extended attribute reference count incorrect */
|
||||||
|
#define PR_1_EXTATTR_REFCOUNT 0x01003C
|
||||||
|
|
||||||
|
/* Error writing Extended Attribute block while fixing refcount */
|
||||||
|
#define PR_1_EXTATTR_WRITE 0x01003D
|
||||||
|
|
||||||
|
/* Multiple EA blocks not supported */
|
||||||
|
#define PR_1_EA_MULTI_BLOCK 0x01003E
|
||||||
|
|
||||||
|
/* Error allocating EA region allocation structure */
|
||||||
|
#define PR_1_EA_ALLOC_REGION 0x01003F
|
||||||
|
|
||||||
|
/* Error EA allocation collision */
|
||||||
|
#define PR_1_EA_ALLOC_COLLISION 0x010040
|
||||||
|
|
||||||
|
/* Bad extended attribute name */
|
||||||
|
#define PR_1_EA_BAD_NAME 0x010041
|
||||||
|
|
||||||
|
/* Bad extended attribute value */
|
||||||
|
#define PR_1_EA_BAD_VALUE 0x010042
|
||||||
|
|
||||||
|
/* Inode too big (latch question) */
|
||||||
|
#define PR_1_INODE_TOOBIG 0x010043
|
||||||
|
|
||||||
|
/* Directory too big */
|
||||||
|
#define PR_1_TOOBIG_DIR 0x010044
|
||||||
|
|
||||||
|
/* Regular file too big */
|
||||||
|
#define PR_1_TOOBIG_REG 0x010045
|
||||||
|
|
||||||
|
/* Symlink too big */
|
||||||
|
#define PR_1_TOOBIG_SYMLINK 0x010046
|
||||||
|
|
||||||
|
/* INDEX_FL flag set on a non-HTREE filesystem */
|
||||||
|
#define PR_1_HTREE_SET 0x010047
|
||||||
|
|
||||||
|
/* INDEX_FL flag set on a non-directory */
|
||||||
|
#define PR_1_HTREE_NODIR 0x010048
|
||||||
|
|
||||||
|
/* Invalid root node in HTREE directory */
|
||||||
|
#define PR_1_HTREE_BADROOT 0x010049
|
||||||
|
|
||||||
|
/* Unsupported hash version in HTREE directory */
|
||||||
|
#define PR_1_HTREE_HASHV 0x01004A
|
||||||
|
|
||||||
|
/* Incompatible flag in HTREE root node */
|
||||||
|
#define PR_1_HTREE_INCOMPAT 0x01004B
|
||||||
|
|
||||||
|
/* HTREE too deep */
|
||||||
|
#define PR_1_HTREE_DEPTH 0x01004C
|
||||||
|
|
||||||
|
/* Bad block has indirect block that conflicts with filesystem block */
|
||||||
|
#define PR_1_BB_FS_BLOCK 0x01004D
|
||||||
|
|
||||||
|
/* Resize inode failed */
|
||||||
|
#define PR_1_RESIZE_INODE_CREATE 0x01004E
|
||||||
|
|
||||||
|
/* inode->i_size is too long */
|
||||||
|
#define PR_1_EXTRA_ISIZE 0x01004F
|
||||||
|
|
||||||
|
/* attribute name is too long */
|
||||||
|
#define PR_1_ATTR_NAME_LEN 0x010050
|
||||||
|
|
||||||
|
/* wrong EA value offset */
|
||||||
|
#define PR_1_ATTR_VALUE_OFFSET 0x010051
|
||||||
|
|
||||||
|
/* wrong EA blocknumber */
|
||||||
|
#define PR_1_ATTR_VALUE_BLOCK 0x010052
|
||||||
|
|
||||||
|
/* wrong EA value size */
|
||||||
|
#define PR_1_ATTR_VALUE_SIZE 0x010053
|
||||||
|
|
||||||
|
/* wrong EA hash value */
|
||||||
|
#define PR_1_ATTR_HASH 0x010054
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass 1b errors
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Pass 1B: Rescan for duplicate/bad blocks */
|
||||||
|
#define PR_1B_PASS_HEADER 0x011000
|
||||||
|
|
||||||
|
/* Duplicate/bad block(s) header */
|
||||||
|
#define PR_1B_DUP_BLOCK_HEADER 0x011001
|
||||||
|
|
||||||
|
/* Duplicate/bad block(s) in inode */
|
||||||
|
#define PR_1B_DUP_BLOCK 0x011002
|
||||||
|
|
||||||
|
/* Duplicate/bad block(s) end */
|
||||||
|
#define PR_1B_DUP_BLOCK_END 0x011003
|
||||||
|
|
||||||
|
/* Error while scanning inodes */
|
||||||
|
#define PR_1B_ISCAN_ERROR 0x011004
|
||||||
|
|
||||||
|
/* Error allocating inode bitmap */
|
||||||
|
#define PR_1B_ALLOCATE_IBITMAP_ERROR 0x011005
|
||||||
|
|
||||||
|
/* Error while iterating over blocks */
|
||||||
|
#define PR_1B_BLOCK_ITERATE 0x0110006
|
||||||
|
|
||||||
|
/* Error adjusting EA refcount */
|
||||||
|
#define PR_1B_ADJ_EA_REFCOUNT 0x0110007
|
||||||
|
|
||||||
|
|
||||||
|
/* Pass 1C: Scan directories for inodes with dup blocks. */
|
||||||
|
#define PR_1C_PASS_HEADER 0x012000
|
||||||
|
|
||||||
|
|
||||||
|
/* Pass 1D: Reconciling duplicate blocks */
|
||||||
|
#define PR_1D_PASS_HEADER 0x013000
|
||||||
|
|
||||||
|
/* File has duplicate blocks */
|
||||||
|
#define PR_1D_DUP_FILE 0x013001
|
||||||
|
|
||||||
|
/* List of files sharing duplicate blocks */
|
||||||
|
#define PR_1D_DUP_FILE_LIST 0x013002
|
||||||
|
|
||||||
|
/* File sharing blocks with filesystem metadata */
|
||||||
|
#define PR_1D_SHARE_METADATA 0x013003
|
||||||
|
|
||||||
|
/* Report of how many duplicate/bad inodes */
|
||||||
|
#define PR_1D_NUM_DUP_INODES 0x013004
|
||||||
|
|
||||||
|
/* Duplicated blocks already reassigned or cloned. */
|
||||||
|
#define PR_1D_DUP_BLOCKS_DEALT 0x013005
|
||||||
|
|
||||||
|
/* Clone duplicate/bad blocks? */
|
||||||
|
#define PR_1D_CLONE_QUESTION 0x013006
|
||||||
|
|
||||||
|
/* Delete file? */
|
||||||
|
#define PR_1D_DELETE_QUESTION 0x013007
|
||||||
|
|
||||||
|
/* Couldn't clone file (error) */
|
||||||
|
#define PR_1D_CLONE_ERROR 0x013008
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass 2 errors
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Pass 2: Checking directory structure */
|
||||||
|
#define PR_2_PASS_HEADER 0x020000
|
||||||
|
|
||||||
|
/* Bad inode number for '.' */
|
||||||
|
#define PR_2_BAD_INODE_DOT 0x020001
|
||||||
|
|
||||||
|
/* Directory entry has bad inode number */
|
||||||
|
#define PR_2_BAD_INO 0x020002
|
||||||
|
|
||||||
|
/* Directory entry has deleted or unused inode */
|
||||||
|
#define PR_2_UNUSED_INODE 0x020003
|
||||||
|
|
||||||
|
/* Directry entry is link to '.' */
|
||||||
|
#define PR_2_LINK_DOT 0x020004
|
||||||
|
|
||||||
|
/* Directory entry points to inode now located in a bad block */
|
||||||
|
#define PR_2_BB_INODE 0x020005
|
||||||
|
|
||||||
|
/* Directory entry contains a link to a directory */
|
||||||
|
#define PR_2_LINK_DIR 0x020006
|
||||||
|
|
||||||
|
/* Directory entry contains a link to the root directry */
|
||||||
|
#define PR_2_LINK_ROOT 0x020007
|
||||||
|
|
||||||
|
/* Directory entry has illegal characters in its name */
|
||||||
|
#define PR_2_BAD_NAME 0x020008
|
||||||
|
|
||||||
|
/* Missing '.' in directory inode */
|
||||||
|
#define PR_2_MISSING_DOT 0x020009
|
||||||
|
|
||||||
|
/* Missing '..' in directory inode */
|
||||||
|
#define PR_2_MISSING_DOT_DOT 0x02000A
|
||||||
|
|
||||||
|
/* First entry in directory inode doesn't contain '.' */
|
||||||
|
#define PR_2_1ST_NOT_DOT 0x02000B
|
||||||
|
|
||||||
|
/* Second entry in directory inode doesn't contain '..' */
|
||||||
|
#define PR_2_2ND_NOT_DOT_DOT 0x02000C
|
||||||
|
|
||||||
|
/* i_faddr should be zero */
|
||||||
|
#define PR_2_FADDR_ZERO 0x02000D
|
||||||
|
|
||||||
|
/* i_file_acl should be zero */
|
||||||
|
#define PR_2_FILE_ACL_ZERO 0x02000E
|
||||||
|
|
||||||
|
/* i_dir_acl should be zero */
|
||||||
|
#define PR_2_DIR_ACL_ZERO 0x02000F
|
||||||
|
|
||||||
|
/* i_frag should be zero */
|
||||||
|
#define PR_2_FRAG_ZERO 0x020010
|
||||||
|
|
||||||
|
/* i_fsize should be zero */
|
||||||
|
#define PR_2_FSIZE_ZERO 0x020011
|
||||||
|
|
||||||
|
/* inode has bad mode */
|
||||||
|
#define PR_2_BAD_MODE 0x020012
|
||||||
|
|
||||||
|
/* directory corrupted */
|
||||||
|
#define PR_2_DIR_CORRUPTED 0x020013
|
||||||
|
|
||||||
|
/* filename too long */
|
||||||
|
#define PR_2_FILENAME_LONG 0x020014
|
||||||
|
|
||||||
|
/* Directory inode has a missing block (hole) */
|
||||||
|
#define PR_2_DIRECTORY_HOLE 0x020015
|
||||||
|
|
||||||
|
/* '.' is not NULL terminated */
|
||||||
|
#define PR_2_DOT_NULL_TERM 0x020016
|
||||||
|
|
||||||
|
/* '..' is not NULL terminated */
|
||||||
|
#define PR_2_DOT_DOT_NULL_TERM 0x020017
|
||||||
|
|
||||||
|
/* Illegal character device in inode */
|
||||||
|
#define PR_2_BAD_CHAR_DEV 0x020018
|
||||||
|
|
||||||
|
/* Illegal block device in inode */
|
||||||
|
#define PR_2_BAD_BLOCK_DEV 0x020019
|
||||||
|
|
||||||
|
/* Duplicate '.' entry */
|
||||||
|
#define PR_2_DUP_DOT 0x02001A
|
||||||
|
|
||||||
|
/* Duplicate '..' entry */
|
||||||
|
#define PR_2_DUP_DOT_DOT 0x02001B
|
||||||
|
|
||||||
|
/* Internal error: couldn't find dir_info */
|
||||||
|
#define PR_2_NO_DIRINFO 0x02001C
|
||||||
|
|
||||||
|
/* Final rec_len is wrong */
|
||||||
|
#define PR_2_FINAL_RECLEN 0x02001D
|
||||||
|
|
||||||
|
/* Error allocating icount structure */
|
||||||
|
#define PR_2_ALLOCATE_ICOUNT 0x02001E
|
||||||
|
|
||||||
|
/* Error iterating over directory blocks */
|
||||||
|
#define PR_2_DBLIST_ITERATE 0x02001F
|
||||||
|
|
||||||
|
/* Error reading directory block */
|
||||||
|
#define PR_2_READ_DIRBLOCK 0x020020
|
||||||
|
|
||||||
|
/* Error writing directory block */
|
||||||
|
#define PR_2_WRITE_DIRBLOCK 0x020021
|
||||||
|
|
||||||
|
/* Error allocating new directory block */
|
||||||
|
#define PR_2_ALLOC_DIRBOCK 0x020022
|
||||||
|
|
||||||
|
/* Error deallocating inode */
|
||||||
|
#define PR_2_DEALLOC_INODE 0x020023
|
||||||
|
|
||||||
|
/* Directory entry for '.' is big. Split? */
|
||||||
|
#define PR_2_SPLIT_DOT 0x020024
|
||||||
|
|
||||||
|
/* Illegal FIFO */
|
||||||
|
#define PR_2_BAD_FIFO 0x020025
|
||||||
|
|
||||||
|
/* Illegal socket */
|
||||||
|
#define PR_2_BAD_SOCKET 0x020026
|
||||||
|
|
||||||
|
/* Directory filetype not set */
|
||||||
|
#define PR_2_SET_FILETYPE 0x020027
|
||||||
|
|
||||||
|
/* Directory filetype incorrect */
|
||||||
|
#define PR_2_BAD_FILETYPE 0x020028
|
||||||
|
|
||||||
|
/* Directory filetype set when it shouldn't be */
|
||||||
|
#define PR_2_CLEAR_FILETYPE 0x020029
|
||||||
|
|
||||||
|
/* Directory filename can't be zero-length */
|
||||||
|
#define PR_2_NULL_NAME 0x020030
|
||||||
|
|
||||||
|
/* Invalid symlink */
|
||||||
|
#define PR_2_INVALID_SYMLINK 0x020031
|
||||||
|
|
||||||
|
/* i_file_acl (extended attribute) is bad */
|
||||||
|
#define PR_2_FILE_ACL_BAD 0x020032
|
||||||
|
|
||||||
|
/* Filesystem contains large files, but has no such flag in sb */
|
||||||
|
#define PR_2_FEATURE_LARGE_FILES 0x020033
|
||||||
|
|
||||||
|
/* Node in HTREE directory not referenced */
|
||||||
|
#define PR_2_HTREE_NOTREF 0x020034
|
||||||
|
|
||||||
|
/* Node in HTREE directory referenced twice */
|
||||||
|
#define PR_2_HTREE_DUPREF 0x020035
|
||||||
|
|
||||||
|
/* Node in HTREE directory has bad min hash */
|
||||||
|
#define PR_2_HTREE_MIN_HASH 0x020036
|
||||||
|
|
||||||
|
/* Node in HTREE directory has bad max hash */
|
||||||
|
#define PR_2_HTREE_MAX_HASH 0x020037
|
||||||
|
|
||||||
|
/* Clear invalid HTREE directory */
|
||||||
|
#define PR_2_HTREE_CLEAR 0x020038
|
||||||
|
|
||||||
|
/* Clear the htree flag forcibly */
|
||||||
|
/* #define PR_2_HTREE_FCLR 0x020039 */
|
||||||
|
|
||||||
|
/* Bad block in htree interior node */
|
||||||
|
#define PR_2_HTREE_BADBLK 0x02003A
|
||||||
|
|
||||||
|
/* Error adjusting EA refcount */
|
||||||
|
#define PR_2_ADJ_EA_REFCOUNT 0x02003B
|
||||||
|
|
||||||
|
/* Invalid HTREE root node */
|
||||||
|
#define PR_2_HTREE_BAD_ROOT 0x02003C
|
||||||
|
|
||||||
|
/* Invalid HTREE limit */
|
||||||
|
#define PR_2_HTREE_BAD_LIMIT 0x02003D
|
||||||
|
|
||||||
|
/* Invalid HTREE count */
|
||||||
|
#define PR_2_HTREE_BAD_COUNT 0x02003E
|
||||||
|
|
||||||
|
/* HTREE interior node has out-of-order hashes in table */
|
||||||
|
#define PR_2_HTREE_HASH_ORDER 0x02003F
|
||||||
|
|
||||||
|
/* Node in HTREE directory has bad depth */
|
||||||
|
#define PR_2_HTREE_BAD_DEPTH 0x020040
|
||||||
|
|
||||||
|
/* Duplicate directory entry found */
|
||||||
|
#define PR_2_DUPLICATE_DIRENT 0x020041
|
||||||
|
|
||||||
|
/* Non-unique filename found */
|
||||||
|
#define PR_2_NON_UNIQUE_FILE 0x020042
|
||||||
|
|
||||||
|
/* Duplicate directory entry found */
|
||||||
|
#define PR_2_REPORT_DUP_DIRENT 0x020043
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass 3 errors
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Pass 3: Checking directory connectivity */
|
||||||
|
#define PR_3_PASS_HEADER 0x030000
|
||||||
|
|
||||||
|
/* Root inode not allocated */
|
||||||
|
#define PR_3_NO_ROOT_INODE 0x030001
|
||||||
|
|
||||||
|
/* No room in lost+found */
|
||||||
|
#define PR_3_EXPAND_LF_DIR 0x030002
|
||||||
|
|
||||||
|
/* Unconnected directory inode */
|
||||||
|
#define PR_3_UNCONNECTED_DIR 0x030003
|
||||||
|
|
||||||
|
/* /lost+found not found */
|
||||||
|
#define PR_3_NO_LF_DIR 0x030004
|
||||||
|
|
||||||
|
/* .. entry is incorrect */
|
||||||
|
#define PR_3_BAD_DOT_DOT 0x030005
|
||||||
|
|
||||||
|
/* Bad or non-existent /lost+found. Cannot reconnect */
|
||||||
|
#define PR_3_NO_LPF 0x030006
|
||||||
|
|
||||||
|
/* Could not expand /lost+found */
|
||||||
|
#define PR_3_CANT_EXPAND_LPF 0x030007
|
||||||
|
|
||||||
|
/* Could not reconnect inode */
|
||||||
|
#define PR_3_CANT_RECONNECT 0x030008
|
||||||
|
|
||||||
|
/* Error while trying to find /lost+found */
|
||||||
|
#define PR_3_ERR_FIND_LPF 0x030009
|
||||||
|
|
||||||
|
/* Error in ext2fs_new_block while creating /lost+found */
|
||||||
|
#define PR_3_ERR_LPF_NEW_BLOCK 0x03000A
|
||||||
|
|
||||||
|
/* Error in ext2fs_new_inode while creating /lost+found */
|
||||||
|
#define PR_3_ERR_LPF_NEW_INODE 0x03000B
|
||||||
|
|
||||||
|
/* Error in ext2fs_new_dir_block while creating /lost+found */
|
||||||
|
#define PR_3_ERR_LPF_NEW_DIR_BLOCK 0x03000C
|
||||||
|
|
||||||
|
/* Error while writing directory block for /lost+found */
|
||||||
|
#define PR_3_ERR_LPF_WRITE_BLOCK 0x03000D
|
||||||
|
|
||||||
|
/* Error while adjusting inode count */
|
||||||
|
#define PR_3_ADJUST_INODE 0x03000E
|
||||||
|
|
||||||
|
/* Couldn't fix parent directory -- error */
|
||||||
|
#define PR_3_FIX_PARENT_ERR 0x03000F
|
||||||
|
|
||||||
|
/* Couldn't fix parent directory -- couldn't find it */
|
||||||
|
#define PR_3_FIX_PARENT_NOFIND 0x030010
|
||||||
|
|
||||||
|
/* Error allocating inode bitmap */
|
||||||
|
#define PR_3_ALLOCATE_IBITMAP_ERROR 0x030011
|
||||||
|
|
||||||
|
/* Error creating root directory */
|
||||||
|
#define PR_3_CREATE_ROOT_ERROR 0x030012
|
||||||
|
|
||||||
|
/* Error creating lost and found directory */
|
||||||
|
#define PR_3_CREATE_LPF_ERROR 0x030013
|
||||||
|
|
||||||
|
/* Root inode is not directory; aborting */
|
||||||
|
#define PR_3_ROOT_NOT_DIR_ABORT 0x030014
|
||||||
|
|
||||||
|
/* Cannot proceed without a root inode. */
|
||||||
|
#define PR_3_NO_ROOT_INODE_ABORT 0x030015
|
||||||
|
|
||||||
|
/* Internal error: couldn't find dir_info */
|
||||||
|
#define PR_3_NO_DIRINFO 0x030016
|
||||||
|
|
||||||
|
/* Lost+found is not a directory */
|
||||||
|
#define PR_3_LPF_NOTDIR 0x030017
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass 3a --- rehashing diretories
|
||||||
|
*/
|
||||||
|
/* Pass 3a: Reindexing directories */
|
||||||
|
#define PR_3A_PASS_HEADER 0x031000
|
||||||
|
|
||||||
|
/* Error iterating over directories */
|
||||||
|
#define PR_3A_OPTIMIZE_ITER 0x031001
|
||||||
|
|
||||||
|
/* Error rehash directory */
|
||||||
|
#define PR_3A_OPTIMIZE_DIR_ERR 0x031002
|
||||||
|
|
||||||
|
/* Rehashing dir header */
|
||||||
|
#define PR_3A_OPTIMIZE_DIR_HEADER 0x031003
|
||||||
|
|
||||||
|
/* Rehashing directory %d */
|
||||||
|
#define PR_3A_OPTIMIZE_DIR 0x031004
|
||||||
|
|
||||||
|
/* Rehashing dir end */
|
||||||
|
#define PR_3A_OPTIMIZE_DIR_END 0x031005
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass 4 errors
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Pass 4: Checking reference counts */
|
||||||
|
#define PR_4_PASS_HEADER 0x040000
|
||||||
|
|
||||||
|
/* Unattached zero-length inode */
|
||||||
|
#define PR_4_ZERO_LEN_INODE 0x040001
|
||||||
|
|
||||||
|
/* Unattached inode */
|
||||||
|
#define PR_4_UNATTACHED_INODE 0x040002
|
||||||
|
|
||||||
|
/* Inode ref count wrong */
|
||||||
|
#define PR_4_BAD_REF_COUNT 0x040003
|
||||||
|
|
||||||
|
/* Inconsistent inode count information cached */
|
||||||
|
#define PR_4_INCONSISTENT_COUNT 0x040004
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass 5 errors
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Pass 5: Checking group summary information */
|
||||||
|
#define PR_5_PASS_HEADER 0x050000
|
||||||
|
|
||||||
|
/* Padding at end of inode bitmap is not set. */
|
||||||
|
#define PR_5_INODE_BMAP_PADDING 0x050001
|
||||||
|
|
||||||
|
/* Padding at end of block bitmap is not set. */
|
||||||
|
#define PR_5_BLOCK_BMAP_PADDING 0x050002
|
||||||
|
|
||||||
|
/* Block bitmap differences header */
|
||||||
|
#define PR_5_BLOCK_BITMAP_HEADER 0x050003
|
||||||
|
|
||||||
|
/* Block not used, but marked in bitmap */
|
||||||
|
#define PR_5_BLOCK_UNUSED 0x050004
|
||||||
|
|
||||||
|
/* Block used, but not marked used in bitmap */
|
||||||
|
#define PR_5_BLOCK_USED 0x050005
|
||||||
|
|
||||||
|
/* Block bitmap differences end */
|
||||||
|
#define PR_5_BLOCK_BITMAP_END 0x050006
|
||||||
|
|
||||||
|
/* Inode bitmap differences header */
|
||||||
|
#define PR_5_INODE_BITMAP_HEADER 0x050007
|
||||||
|
|
||||||
|
/* Inode not used, but marked in bitmap */
|
||||||
|
#define PR_5_INODE_UNUSED 0x050008
|
||||||
|
|
||||||
|
/* Inode used, but not marked used in bitmap */
|
||||||
|
#define PR_5_INODE_USED 0x050009
|
||||||
|
|
||||||
|
/* Inode bitmap differences end */
|
||||||
|
#define PR_5_INODE_BITMAP_END 0x05000A
|
||||||
|
|
||||||
|
/* Free inodes count for group wrong */
|
||||||
|
#define PR_5_FREE_INODE_COUNT_GROUP 0x05000B
|
||||||
|
|
||||||
|
/* Directories count for group wrong */
|
||||||
|
#define PR_5_FREE_DIR_COUNT_GROUP 0x05000C
|
||||||
|
|
||||||
|
/* Free inodes count wrong */
|
||||||
|
#define PR_5_FREE_INODE_COUNT 0x05000D
|
||||||
|
|
||||||
|
/* Free blocks count for group wrong */
|
||||||
|
#define PR_5_FREE_BLOCK_COUNT_GROUP 0x05000E
|
||||||
|
|
||||||
|
/* Free blocks count wrong */
|
||||||
|
#define PR_5_FREE_BLOCK_COUNT 0x05000F
|
||||||
|
|
||||||
|
/* Programming error: bitmap endpoints don't match */
|
||||||
|
#define PR_5_BMAP_ENDPOINTS 0x050010
|
||||||
|
|
||||||
|
/* Internal error: fudging end of bitmap */
|
||||||
|
#define PR_5_FUDGE_BITMAP_ERROR 0x050011
|
||||||
|
|
||||||
|
/* Error copying in replacement inode bitmap */
|
||||||
|
#define PR_5_COPY_IBITMAP_ERROR 0x050012
|
||||||
|
|
||||||
|
/* Error copying in replacement block bitmap */
|
||||||
|
#define PR_5_COPY_BBITMAP_ERROR 0x050013
|
||||||
|
|
||||||
|
/* Block range not used, but marked in bitmap */
|
||||||
|
#define PR_5_BLOCK_RANGE_UNUSED 0x050014
|
||||||
|
|
||||||
|
/* Block range used, but not marked used in bitmap */
|
||||||
|
#define PR_5_BLOCK_RANGE_USED 0x050015
|
||||||
|
|
||||||
|
/* Inode range not used, but marked in bitmap */
|
||||||
|
#define PR_5_INODE_RANGE_UNUSED 0x050016
|
||||||
|
|
||||||
|
/* Inode rangeused, but not marked used in bitmap */
|
||||||
|
#define PR_5_INODE_RANGE_USED 0x050017
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function declarations
|
||||||
|
*/
|
||||||
|
int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
|
||||||
|
int end_problem_latch(e2fsck_t ctx, int mask);
|
||||||
|
int set_latch_flags(int mask, int setflags, int clearflags);
|
||||||
|
int get_latch_flags(int mask, int *value);
|
||||||
|
void clear_problem_context(struct problem_context *ctx);
|
||||||
|
|
||||||
|
/* message.c */
|
||||||
|
void print_e2fsck_message(e2fsck_t ctx, const char *msg,
|
||||||
|
struct problem_context *pctx, int first);
|
||||||
|
|
42
e2fsprogs/e2fsck/problemP.h
Normal file
42
e2fsprogs/e2fsck/problemP.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* problemP.h --- Private header file for fix_problem()
|
||||||
|
*
|
||||||
|
* Copyright 1997 by Theodore Ts'o
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct e2fsck_problem {
|
||||||
|
problem_t e2p_code;
|
||||||
|
const char * e2p_description;
|
||||||
|
char prompt;
|
||||||
|
int flags;
|
||||||
|
problem_t second_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct latch_descr {
|
||||||
|
int latch_code;
|
||||||
|
problem_t question;
|
||||||
|
problem_t end_message;
|
||||||
|
int flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PR_PREEN_OK 0x000001 /* Don't need to do preenhalt */
|
||||||
|
#define PR_NO_OK 0x000002 /* If user answers no, don't make fs invalid */
|
||||||
|
#define PR_NO_DEFAULT 0x000004 /* Default to no */
|
||||||
|
#define PR_MSG_ONLY 0x000008 /* Print message only */
|
||||||
|
|
||||||
|
/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */
|
||||||
|
|
||||||
|
#define PR_FATAL 0x001000 /* Fatal error */
|
||||||
|
#define PR_AFTER_CODE 0x002000 /* After asking the first question, */
|
||||||
|
/* ask another */
|
||||||
|
#define PR_PREEN_NOMSG 0x004000 /* Don't print a message if we're preening */
|
||||||
|
#define PR_NOCOLLATE 0x008000 /* Don't collate answers for this latch */
|
||||||
|
#define PR_NO_NOMSG 0x010000 /* Don't print a message if e2fsck -n */
|
||||||
|
#define PR_PREEN_NO 0x020000 /* Use No as an answer if preening */
|
||||||
|
#define PR_PREEN_NOHDR 0x040000 /* Don't print the preen header */
|
||||||
|
|
586
e2fsprogs/e2fsck/recovery.c
Normal file
586
e2fsprogs/e2fsck/recovery.c
Normal file
|
@ -0,0 +1,586 @@
|
||||||
|
/*
|
||||||
|
* linux/fs/recovery.c
|
||||||
|
*
|
||||||
|
* Written by Stephen C. Tweedie <sct@redhat.com>, 1999
|
||||||
|
*
|
||||||
|
* Copyright 1999-2000 Red Hat Software --- All Rights Reserved
|
||||||
|
*
|
||||||
|
* This file is part of the Linux kernel and is made available under
|
||||||
|
* the terms of the GNU General Public License, version 2, or at your
|
||||||
|
* option, any later version, incorporated herein by reference.
|
||||||
|
*
|
||||||
|
* Journal recovery routines for the generic filesystem journaling code;
|
||||||
|
* part of the ext2fs journaling system.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __KERNEL__
|
||||||
|
#include "jfs_user.h"
|
||||||
|
#else
|
||||||
|
#include <linux/time.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/jbd.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maintain information about the progress of the recovery job, so that
|
||||||
|
* the different passes can carry information between them.
|
||||||
|
*/
|
||||||
|
struct recovery_info
|
||||||
|
{
|
||||||
|
tid_t start_transaction;
|
||||||
|
tid_t end_transaction;
|
||||||
|
|
||||||
|
int nr_replays;
|
||||||
|
int nr_revokes;
|
||||||
|
int nr_revoke_hits;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
|
||||||
|
static int do_one_pass(journal_t *journal,
|
||||||
|
struct recovery_info *info, enum passtype pass);
|
||||||
|
static int scan_revoke_records(journal_t *, struct buffer_head *,
|
||||||
|
tid_t, struct recovery_info *);
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
|
/* Release readahead buffers after use */
|
||||||
|
void journal_brelse_array(struct buffer_head *b[], int n)
|
||||||
|
{
|
||||||
|
while (--n >= 0)
|
||||||
|
brelse (b[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When reading from the journal, we are going through the block device
|
||||||
|
* layer directly and so there is no readahead being done for us. We
|
||||||
|
* need to implement any readahead ourselves if we want it to happen at
|
||||||
|
* all. Recovery is basically one long sequential read, so make sure we
|
||||||
|
* do the IO in reasonably large chunks.
|
||||||
|
*
|
||||||
|
* This is not so critical that we need to be enormously clever about
|
||||||
|
* the readahead size, though. 128K is a purely arbitrary, good-enough
|
||||||
|
* fixed value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MAXBUF 8
|
||||||
|
static int do_readahead(journal_t *journal, unsigned int start)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
unsigned int max, nbufs, next;
|
||||||
|
unsigned long blocknr;
|
||||||
|
struct buffer_head *bh;
|
||||||
|
|
||||||
|
struct buffer_head * bufs[MAXBUF];
|
||||||
|
|
||||||
|
/* Do up to 128K of readahead */
|
||||||
|
max = start + (128 * 1024 / journal->j_blocksize);
|
||||||
|
if (max > journal->j_maxlen)
|
||||||
|
max = journal->j_maxlen;
|
||||||
|
|
||||||
|
/* Do the readahead itself. We'll submit MAXBUF buffer_heads at
|
||||||
|
* a time to the block device IO layer. */
|
||||||
|
|
||||||
|
nbufs = 0;
|
||||||
|
|
||||||
|
for (next = start; next < max; next++) {
|
||||||
|
err = journal_bmap(journal, next, &blocknr);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
printk (KERN_ERR "JBD: bad block at offset %u\n",
|
||||||
|
next);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bh = __getblk(journal->j_dev, blocknr, journal->j_blocksize);
|
||||||
|
if (!bh) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buffer_uptodate(bh) && !buffer_locked(bh)) {
|
||||||
|
bufs[nbufs++] = bh;
|
||||||
|
if (nbufs == MAXBUF) {
|
||||||
|
ll_rw_block(READ, nbufs, bufs);
|
||||||
|
journal_brelse_array(bufs, nbufs);
|
||||||
|
nbufs = 0;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
brelse(bh);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbufs)
|
||||||
|
ll_rw_block(READ, nbufs, bufs);
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
if (nbufs)
|
||||||
|
journal_brelse_array(bufs, nbufs);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __KERNEL__ */
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read a block from the journal
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int jread(struct buffer_head **bhp, journal_t *journal,
|
||||||
|
unsigned int offset)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
unsigned long blocknr;
|
||||||
|
struct buffer_head *bh;
|
||||||
|
|
||||||
|
*bhp = NULL;
|
||||||
|
|
||||||
|
J_ASSERT (offset < journal->j_maxlen);
|
||||||
|
|
||||||
|
err = journal_bmap(journal, offset, &blocknr);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
printk (KERN_ERR "JBD: bad block at offset %u\n",
|
||||||
|
offset);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bh = __getblk(journal->j_dev, blocknr, journal->j_blocksize);
|
||||||
|
if (!bh)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (!buffer_uptodate(bh)) {
|
||||||
|
/* If this is a brand new buffer, start readahead.
|
||||||
|
Otherwise, we assume we are already reading it. */
|
||||||
|
if (!buffer_req(bh))
|
||||||
|
do_readahead(journal, offset);
|
||||||
|
wait_on_buffer(bh);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buffer_uptodate(bh)) {
|
||||||
|
printk (KERN_ERR "JBD: Failed to read block at offset %u\n",
|
||||||
|
offset);
|
||||||
|
brelse(bh);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
*bhp = bh;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Count the number of in-use tags in a journal descriptor block.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int count_tags(struct buffer_head *bh, int size)
|
||||||
|
{
|
||||||
|
char * tagp;
|
||||||
|
journal_block_tag_t * tag;
|
||||||
|
int nr = 0;
|
||||||
|
|
||||||
|
tagp = &bh->b_data[sizeof(journal_header_t)];
|
||||||
|
|
||||||
|
while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
|
||||||
|
tag = (journal_block_tag_t *) tagp;
|
||||||
|
|
||||||
|
nr++;
|
||||||
|
tagp += sizeof(journal_block_tag_t);
|
||||||
|
if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
|
||||||
|
tagp += 16;
|
||||||
|
|
||||||
|
if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Make sure we wrap around the log correctly! */
|
||||||
|
#define wrap(journal, var) \
|
||||||
|
do { \
|
||||||
|
if (var >= (journal)->j_last) \
|
||||||
|
var -= ((journal)->j_last - (journal)->j_first); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* int journal_recover(journal_t *journal) - recovers a on-disk journal
|
||||||
|
* @journal: the journal to recover
|
||||||
|
*
|
||||||
|
* The primary function for recovering the log contents when mounting a
|
||||||
|
* journaled device.
|
||||||
|
*
|
||||||
|
* Recovery is done in three passes. In the first pass, we look for the
|
||||||
|
* end of the log. In the second, we assemble the list of revoke
|
||||||
|
* blocks. In the third and final pass, we replay any un-revoked blocks
|
||||||
|
* in the log.
|
||||||
|
*/
|
||||||
|
int journal_recover(journal_t *journal)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
journal_superblock_t * sb;
|
||||||
|
|
||||||
|
struct recovery_info info;
|
||||||
|
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
sb = journal->j_superblock;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The journal superblock's s_start field (the current log head)
|
||||||
|
* is always zero if, and only if, the journal was cleanly
|
||||||
|
* unmounted.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!sb->s_start) {
|
||||||
|
jbd_debug(1, "No recovery required, last transaction %d\n",
|
||||||
|
ntohl(sb->s_sequence));
|
||||||
|
journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = do_one_pass(journal, &info, PASS_SCAN);
|
||||||
|
if (!err)
|
||||||
|
err = do_one_pass(journal, &info, PASS_REVOKE);
|
||||||
|
if (!err)
|
||||||
|
err = do_one_pass(journal, &info, PASS_REPLAY);
|
||||||
|
|
||||||
|
jbd_debug(0, "JBD: recovery, exit status %d, "
|
||||||
|
"recovered transactions %u to %u\n",
|
||||||
|
err, info.start_transaction, info.end_transaction);
|
||||||
|
jbd_debug(0, "JBD: Replayed %d and revoked %d/%d blocks\n",
|
||||||
|
info.nr_replays, info.nr_revoke_hits, info.nr_revokes);
|
||||||
|
|
||||||
|
/* Restart the log at the next transaction ID, thus invalidating
|
||||||
|
* any existing commit records in the log. */
|
||||||
|
journal->j_transaction_sequence = ++info.end_transaction;
|
||||||
|
|
||||||
|
journal_clear_revoke(journal);
|
||||||
|
sync_blockdev(journal->j_fs_dev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* int journal_skip_recovery() - Start journal and wipe exiting records
|
||||||
|
* @journal: journal to startup
|
||||||
|
*
|
||||||
|
* Locate any valid recovery information from the journal and set up the
|
||||||
|
* journal structures in memory to ignore it (presumably because the
|
||||||
|
* caller has evidence that it is out of date).
|
||||||
|
* This function does'nt appear to be exorted..
|
||||||
|
*
|
||||||
|
* We perform one pass over the journal to allow us to tell the user how
|
||||||
|
* much recovery information is being erased, and to let us initialise
|
||||||
|
* the journal transaction sequence numbers to the next unused ID.
|
||||||
|
*/
|
||||||
|
int journal_skip_recovery(journal_t *journal)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
journal_superblock_t * sb;
|
||||||
|
|
||||||
|
struct recovery_info info;
|
||||||
|
|
||||||
|
memset (&info, 0, sizeof(info));
|
||||||
|
sb = journal->j_superblock;
|
||||||
|
|
||||||
|
err = do_one_pass(journal, &info, PASS_SCAN);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
printk(KERN_ERR "JBD: error %d scanning journal\n", err);
|
||||||
|
++journal->j_transaction_sequence;
|
||||||
|
} else {
|
||||||
|
#ifdef __CONFIG_JBD_DEBUG__E2FS
|
||||||
|
int dropped = info.end_transaction - ntohl(sb->s_sequence);
|
||||||
|
#endif
|
||||||
|
jbd_debug(0,
|
||||||
|
"JBD: ignoring %d transaction%s from the journal.\n",
|
||||||
|
dropped, (dropped == 1) ? "" : "s");
|
||||||
|
journal->j_transaction_sequence = ++info.end_transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
journal->j_tail = 0;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_one_pass(journal_t *journal,
|
||||||
|
struct recovery_info *info, enum passtype pass)
|
||||||
|
{
|
||||||
|
unsigned int first_commit_ID, next_commit_ID;
|
||||||
|
unsigned long next_log_block;
|
||||||
|
int err, success = 0;
|
||||||
|
journal_superblock_t * sb;
|
||||||
|
journal_header_t * tmp;
|
||||||
|
struct buffer_head * bh;
|
||||||
|
unsigned int sequence;
|
||||||
|
int blocktype;
|
||||||
|
|
||||||
|
/* Precompute the maximum metadata descriptors in a descriptor block */
|
||||||
|
int MAX_BLOCKS_PER_DESC;
|
||||||
|
MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
|
||||||
|
/ sizeof(journal_block_tag_t));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First thing is to establish what we expect to find in the log
|
||||||
|
* (in terms of transaction IDs), and where (in terms of log
|
||||||
|
* block offsets): query the superblock.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sb = journal->j_superblock;
|
||||||
|
next_commit_ID = ntohl(sb->s_sequence);
|
||||||
|
next_log_block = ntohl(sb->s_start);
|
||||||
|
|
||||||
|
first_commit_ID = next_commit_ID;
|
||||||
|
if (pass == PASS_SCAN)
|
||||||
|
info->start_transaction = first_commit_ID;
|
||||||
|
|
||||||
|
jbd_debug(1, "Starting recovery pass %d\n", pass);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we walk through the log, transaction by transaction,
|
||||||
|
* making sure that each transaction has a commit block in the
|
||||||
|
* expected place. Each complete transaction gets replayed back
|
||||||
|
* into the main filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
int flags;
|
||||||
|
char * tagp;
|
||||||
|
journal_block_tag_t * tag;
|
||||||
|
struct buffer_head * obh;
|
||||||
|
struct buffer_head * nbh;
|
||||||
|
|
||||||
|
/* If we already know where to stop the log traversal,
|
||||||
|
* check right now that we haven't gone past the end of
|
||||||
|
* the log. */
|
||||||
|
|
||||||
|
if (pass != PASS_SCAN)
|
||||||
|
if (tid_geq(next_commit_ID, info->end_transaction))
|
||||||
|
break;
|
||||||
|
|
||||||
|
jbd_debug(2, "Scanning for sequence ID %u at %lu/%lu\n",
|
||||||
|
next_commit_ID, next_log_block, journal->j_last);
|
||||||
|
|
||||||
|
/* Skip over each chunk of the transaction looking
|
||||||
|
* either the next descriptor block or the final commit
|
||||||
|
* record. */
|
||||||
|
|
||||||
|
jbd_debug(3, "JBD: checking block %ld\n", next_log_block);
|
||||||
|
err = jread(&bh, journal, next_log_block);
|
||||||
|
if (err)
|
||||||
|
goto failed;
|
||||||
|
|
||||||
|
next_log_block++;
|
||||||
|
wrap(journal, next_log_block);
|
||||||
|
|
||||||
|
/* What kind of buffer is it?
|
||||||
|
*
|
||||||
|
* If it is a descriptor block, check that it has the
|
||||||
|
* expected sequence number. Otherwise, we're all done
|
||||||
|
* here. */
|
||||||
|
|
||||||
|
tmp = (journal_header_t *)bh->b_data;
|
||||||
|
|
||||||
|
if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
|
||||||
|
brelse(bh);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
blocktype = ntohl(tmp->h_blocktype);
|
||||||
|
sequence = ntohl(tmp->h_sequence);
|
||||||
|
jbd_debug(3, "Found magic %d, sequence %d\n",
|
||||||
|
blocktype, sequence);
|
||||||
|
|
||||||
|
if (sequence != next_commit_ID) {
|
||||||
|
brelse(bh);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OK, we have a valid descriptor block which matches
|
||||||
|
* all of the sequence number checks. What are we going
|
||||||
|
* to do with it? That depends on the pass... */
|
||||||
|
|
||||||
|
switch(blocktype) {
|
||||||
|
case JFS_DESCRIPTOR_BLOCK:
|
||||||
|
/* If it is a valid descriptor block, replay it
|
||||||
|
* in pass REPLAY; otherwise, just skip over the
|
||||||
|
* blocks it describes. */
|
||||||
|
if (pass != PASS_REPLAY) {
|
||||||
|
next_log_block +=
|
||||||
|
count_tags(bh, journal->j_blocksize);
|
||||||
|
wrap(journal, next_log_block);
|
||||||
|
brelse(bh);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A descriptor block: we can now write all of
|
||||||
|
* the data blocks. Yay, useful work is finally
|
||||||
|
* getting done here! */
|
||||||
|
|
||||||
|
tagp = &bh->b_data[sizeof(journal_header_t)];
|
||||||
|
while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
|
||||||
|
<= journal->j_blocksize) {
|
||||||
|
unsigned long io_block;
|
||||||
|
|
||||||
|
tag = (journal_block_tag_t *) tagp;
|
||||||
|
flags = ntohl(tag->t_flags);
|
||||||
|
|
||||||
|
io_block = next_log_block++;
|
||||||
|
wrap(journal, next_log_block);
|
||||||
|
err = jread(&obh, journal, io_block);
|
||||||
|
if (err) {
|
||||||
|
/* Recover what we can, but
|
||||||
|
* report failure at the end. */
|
||||||
|
success = err;
|
||||||
|
printk (KERN_ERR
|
||||||
|
"JBD: IO error %d recovering "
|
||||||
|
"block %ld in log\n",
|
||||||
|
err, io_block);
|
||||||
|
} else {
|
||||||
|
unsigned long blocknr;
|
||||||
|
|
||||||
|
J_ASSERT(obh != NULL);
|
||||||
|
blocknr = ntohl(tag->t_blocknr);
|
||||||
|
|
||||||
|
/* If the block has been
|
||||||
|
* revoked, then we're all done
|
||||||
|
* here. */
|
||||||
|
if (journal_test_revoke
|
||||||
|
(journal, blocknr,
|
||||||
|
next_commit_ID)) {
|
||||||
|
brelse(obh);
|
||||||
|
++info->nr_revoke_hits;
|
||||||
|
goto skip_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find a buffer for the new
|
||||||
|
* data being restored */
|
||||||
|
nbh = __getblk(journal->j_fs_dev,
|
||||||
|
blocknr,
|
||||||
|
journal->j_blocksize);
|
||||||
|
if (nbh == NULL) {
|
||||||
|
printk(KERN_ERR
|
||||||
|
"JBD: Out of memory "
|
||||||
|
"during recovery.\n");
|
||||||
|
err = -ENOMEM;
|
||||||
|
brelse(bh);
|
||||||
|
brelse(obh);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock_buffer(nbh);
|
||||||
|
memcpy(nbh->b_data, obh->b_data,
|
||||||
|
journal->j_blocksize);
|
||||||
|
if (flags & JFS_FLAG_ESCAPE) {
|
||||||
|
*((unsigned int *)bh->b_data) =
|
||||||
|
htonl(JFS_MAGIC_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
BUFFER_TRACE(nbh, "marking dirty");
|
||||||
|
set_buffer_uptodate(nbh);
|
||||||
|
mark_buffer_dirty(nbh);
|
||||||
|
BUFFER_TRACE(nbh, "marking uptodate");
|
||||||
|
++info->nr_replays;
|
||||||
|
/* ll_rw_block(WRITE, 1, &nbh); */
|
||||||
|
unlock_buffer(nbh);
|
||||||
|
brelse(obh);
|
||||||
|
brelse(nbh);
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_write:
|
||||||
|
tagp += sizeof(journal_block_tag_t);
|
||||||
|
if (!(flags & JFS_FLAG_SAME_UUID))
|
||||||
|
tagp += 16;
|
||||||
|
|
||||||
|
if (flags & JFS_FLAG_LAST_TAG)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
brelse(bh);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case JFS_COMMIT_BLOCK:
|
||||||
|
/* Found an expected commit block: not much to
|
||||||
|
* do other than move on to the next sequence
|
||||||
|
* number. */
|
||||||
|
brelse(bh);
|
||||||
|
next_commit_ID++;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case JFS_REVOKE_BLOCK:
|
||||||
|
/* If we aren't in the REVOKE pass, then we can
|
||||||
|
* just skip over this block. */
|
||||||
|
if (pass != PASS_REVOKE) {
|
||||||
|
brelse(bh);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = scan_revoke_records(journal, bh,
|
||||||
|
next_commit_ID, info);
|
||||||
|
brelse(bh);
|
||||||
|
if (err)
|
||||||
|
goto failed;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
default:
|
||||||
|
jbd_debug(3, "Unrecognised magic %d, end of scan.\n",
|
||||||
|
blocktype);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
/*
|
||||||
|
* We broke out of the log scan loop: either we came to the
|
||||||
|
* known end of the log or we found an unexpected block in the
|
||||||
|
* log. If the latter happened, then we know that the "current"
|
||||||
|
* transaction marks the end of the valid log.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (pass == PASS_SCAN)
|
||||||
|
info->end_transaction = next_commit_ID;
|
||||||
|
else {
|
||||||
|
/* It's really bad news if different passes end up at
|
||||||
|
* different places (but possible due to IO errors). */
|
||||||
|
if (info->end_transaction != next_commit_ID) {
|
||||||
|
printk (KERN_ERR "JBD: recovery pass %d ended at "
|
||||||
|
"transaction %u, expected %u\n",
|
||||||
|
pass, next_commit_ID, info->end_transaction);
|
||||||
|
if (!success)
|
||||||
|
success = -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
|
||||||
|
failed:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Scan a revoke record, marking all blocks mentioned as revoked. */
|
||||||
|
|
||||||
|
static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
|
||||||
|
tid_t sequence, struct recovery_info *info)
|
||||||
|
{
|
||||||
|
journal_revoke_header_t *header;
|
||||||
|
int offset, max;
|
||||||
|
|
||||||
|
header = (journal_revoke_header_t *) bh->b_data;
|
||||||
|
offset = sizeof(journal_revoke_header_t);
|
||||||
|
max = ntohl(header->r_count);
|
||||||
|
|
||||||
|
while (offset < max) {
|
||||||
|
unsigned long blocknr;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
|
||||||
|
offset += 4;
|
||||||
|
err = journal_set_revoke(journal, blocknr, sequence);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
++info->nr_revokes;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
204
e2fsprogs/e2fsck/region.c
Normal file
204
e2fsprogs/e2fsck/region.c
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
/*
|
||||||
|
* region.c --- code which manages allocations within a region.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2001 Theodore Ts'o.
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if HAVE_UNISTD_H
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "e2fsck.h"
|
||||||
|
|
||||||
|
struct region_el {
|
||||||
|
region_addr_t start;
|
||||||
|
region_addr_t end;
|
||||||
|
struct region_el *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct region_struct {
|
||||||
|
region_addr_t min;
|
||||||
|
region_addr_t max;
|
||||||
|
struct region_el *allocated;
|
||||||
|
};
|
||||||
|
|
||||||
|
region_t region_create(region_addr_t min, region_addr_t max)
|
||||||
|
{
|
||||||
|
region_t region;
|
||||||
|
|
||||||
|
region = malloc(sizeof(struct region_struct));
|
||||||
|
if (!region)
|
||||||
|
return NULL;
|
||||||
|
memset(region, 0, sizeof(struct region_struct));
|
||||||
|
region->min = min;
|
||||||
|
region->max = max;
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
void region_free(region_t region)
|
||||||
|
{
|
||||||
|
struct region_el *r, *next;
|
||||||
|
|
||||||
|
for (r = region->allocated; r; r = next) {
|
||||||
|
next = r->next;
|
||||||
|
free(r);
|
||||||
|
}
|
||||||
|
memset(region, 0, sizeof(struct region_struct));
|
||||||
|
free(region);
|
||||||
|
}
|
||||||
|
|
||||||
|
int region_allocate(region_t region, region_addr_t start, int n)
|
||||||
|
{
|
||||||
|
struct region_el *r, *new_region, *prev, *next;
|
||||||
|
region_addr_t end;
|
||||||
|
|
||||||
|
end = start+n;
|
||||||
|
if ((start < region->min) || (end > region->max))
|
||||||
|
return -1;
|
||||||
|
if (n == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Search through the linked list. If we find that it
|
||||||
|
* conflicts witih something that's already allocated, return
|
||||||
|
* 1; if we can find an existing region which we can grow, do
|
||||||
|
* so. Otherwise, stop when we find the appropriate place
|
||||||
|
* insert a new region element into the linked list.
|
||||||
|
*/
|
||||||
|
for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) {
|
||||||
|
if (((start >= r->start) && (start < r->end)) ||
|
||||||
|
((end > r->start) && (end <= r->end)) ||
|
||||||
|
((start <= r->start) && (end >= r->end)))
|
||||||
|
return 1;
|
||||||
|
if (end == r->start) {
|
||||||
|
r->start = start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (start == r->end) {
|
||||||
|
if ((next = r->next)) {
|
||||||
|
if (end > next->start)
|
||||||
|
return 1;
|
||||||
|
if (end == next->start) {
|
||||||
|
r->end = next->end;
|
||||||
|
r->next = next->next;
|
||||||
|
free(next);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r->end = end;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (start < r->start)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Insert a new region element structure into the linked list
|
||||||
|
*/
|
||||||
|
new_region = malloc(sizeof(struct region_el));
|
||||||
|
if (!new_region)
|
||||||
|
return -1;
|
||||||
|
new_region->start = start;
|
||||||
|
new_region->end = start + n;
|
||||||
|
new_region->next = r;
|
||||||
|
if (prev)
|
||||||
|
prev->next = new_region;
|
||||||
|
else
|
||||||
|
region->allocated = new_region;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TEST_PROGRAM
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define BCODE_END 0
|
||||||
|
#define BCODE_CREATE 1
|
||||||
|
#define BCODE_FREE 2
|
||||||
|
#define BCODE_ALLOCATE 3
|
||||||
|
#define BCODE_PRINT 4
|
||||||
|
|
||||||
|
int bcode_program[] = {
|
||||||
|
BCODE_CREATE, 1, 1001,
|
||||||
|
BCODE_PRINT,
|
||||||
|
BCODE_ALLOCATE, 10, 10,
|
||||||
|
BCODE_ALLOCATE, 30, 10,
|
||||||
|
BCODE_PRINT,
|
||||||
|
BCODE_ALLOCATE, 1, 15,
|
||||||
|
BCODE_ALLOCATE, 15, 8,
|
||||||
|
BCODE_ALLOCATE, 1, 20,
|
||||||
|
BCODE_ALLOCATE, 1, 8,
|
||||||
|
BCODE_PRINT,
|
||||||
|
BCODE_ALLOCATE, 40, 10,
|
||||||
|
BCODE_PRINT,
|
||||||
|
BCODE_ALLOCATE, 22, 5,
|
||||||
|
BCODE_PRINT,
|
||||||
|
BCODE_ALLOCATE, 27, 3,
|
||||||
|
BCODE_PRINT,
|
||||||
|
BCODE_ALLOCATE, 20, 2,
|
||||||
|
BCODE_PRINT,
|
||||||
|
BCODE_ALLOCATE, 49, 1,
|
||||||
|
BCODE_ALLOCATE, 50, 5,
|
||||||
|
BCODE_ALLOCATE, 9, 2,
|
||||||
|
BCODE_ALLOCATE, 9, 1,
|
||||||
|
BCODE_PRINT,
|
||||||
|
BCODE_FREE,
|
||||||
|
BCODE_END
|
||||||
|
};
|
||||||
|
|
||||||
|
void region_print(region_t region, FILE *f)
|
||||||
|
{
|
||||||
|
struct region_el *r;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
fprintf(f, "Printing region (min=%d. max=%d)\n\t", region->min,
|
||||||
|
region->max);
|
||||||
|
for (r = region->allocated; r; r = r->next) {
|
||||||
|
fprintf(f, "(%d, %d) ", r->start, r->end);
|
||||||
|
if (++i >= 8)
|
||||||
|
fprintf(f, "\n\t");
|
||||||
|
}
|
||||||
|
fprintf(f, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
region_t r;
|
||||||
|
int pc = 0, ret;
|
||||||
|
region_addr_t start, end, len;
|
||||||
|
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
switch (bcode_program[pc++]) {
|
||||||
|
case BCODE_END:
|
||||||
|
exit(0);
|
||||||
|
case BCODE_CREATE:
|
||||||
|
start = bcode_program[pc++];
|
||||||
|
end = bcode_program[pc++];
|
||||||
|
printf("Creating region with args(%d, %d)\n",
|
||||||
|
start, end);
|
||||||
|
r = region_create(start, end);
|
||||||
|
if (!r) {
|
||||||
|
fprintf(stderr, "Couldn't create region.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BCODE_ALLOCATE:
|
||||||
|
start = bcode_program[pc++];
|
||||||
|
end = bcode_program[pc++];
|
||||||
|
ret = region_allocate(r, start, end);
|
||||||
|
printf("Region_allocate(%d, %d) returns %d\n",
|
||||||
|
start, end, ret);
|
||||||
|
break;
|
||||||
|
case BCODE_PRINT:
|
||||||
|
region_print(r, stdout);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* TEST_PROGRAM */
|
840
e2fsprogs/e2fsck/rehash.c
Normal file
840
e2fsprogs/e2fsck/rehash.c
Normal file
|
@ -0,0 +1,840 @@
|
||||||
|
/*
|
||||||
|
* rehash.c --- rebuild hash tree directories
|
||||||
|
*
|
||||||
|
* Copyright (C) 2002 Theodore Ts'o
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*
|
||||||
|
* This algorithm is designed for simplicity of implementation and to
|
||||||
|
* pack the directory as much as possible. It however requires twice
|
||||||
|
* as much memory as the size of the directory. The maximum size
|
||||||
|
* directory supported using a 4k blocksize is roughly a gigabyte, and
|
||||||
|
* so there may very well be problems with machines that don't have
|
||||||
|
* virtual memory, and obscenely large directories.
|
||||||
|
*
|
||||||
|
* An alternate algorithm which is much more disk intensive could be
|
||||||
|
* written, and probably will need to be written in the future. The
|
||||||
|
* design goals of such an algorithm are: (a) use (roughly) constant
|
||||||
|
* amounts of memory, no matter how large the directory, (b) the
|
||||||
|
* directory must be safe at all times, even if e2fsck is interrupted
|
||||||
|
* in the middle, (c) we must use minimal amounts of extra disk
|
||||||
|
* blocks. This pretty much requires an incremental approach, where
|
||||||
|
* we are reading from one part of the directory, and inserting into
|
||||||
|
* the front half. So the algorithm will have to keep track of a
|
||||||
|
* moving block boundary between the new tree and the old tree, and
|
||||||
|
* files will need to be moved from the old directory and inserted
|
||||||
|
* into the new tree. If the new directory requires space which isn't
|
||||||
|
* yet available, blocks from the beginning part of the old directory
|
||||||
|
* may need to be moved to the end of the directory to make room for
|
||||||
|
* the new tree:
|
||||||
|
*
|
||||||
|
* --------------------------------------------------------
|
||||||
|
* | new tree | | old tree |
|
||||||
|
* --------------------------------------------------------
|
||||||
|
* ^ ptr ^ptr
|
||||||
|
* tail new head old
|
||||||
|
*
|
||||||
|
* This is going to be a pain in the tuckus to implement, and will
|
||||||
|
* require a lot more disk accesses. So I'm going to skip it for now;
|
||||||
|
* it's only really going to be an issue for really, really big
|
||||||
|
* filesystems (when we reach the level of tens of millions of files
|
||||||
|
* in a single directory). It will probably be easier to simply
|
||||||
|
* require that e2fsck use VM first.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include "e2fsck.h"
|
||||||
|
#include "problem.h"
|
||||||
|
|
||||||
|
struct fill_dir_struct {
|
||||||
|
char *buf;
|
||||||
|
struct ext2_inode *inode;
|
||||||
|
int err;
|
||||||
|
e2fsck_t ctx;
|
||||||
|
struct hash_entry *harray;
|
||||||
|
int max_array, num_array;
|
||||||
|
int dir_size;
|
||||||
|
int compress;
|
||||||
|
ino_t parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hash_entry {
|
||||||
|
ext2_dirhash_t hash;
|
||||||
|
ext2_dirhash_t minor_hash;
|
||||||
|
struct ext2_dir_entry *dir;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct out_dir {
|
||||||
|
int num;
|
||||||
|
int max;
|
||||||
|
char *buf;
|
||||||
|
ext2_dirhash_t *hashes;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int fill_dir_block(ext2_filsys fs,
|
||||||
|
blk_t *block_nr,
|
||||||
|
e2_blkcnt_t blockcnt,
|
||||||
|
blk_t ref_block EXT2FS_ATTR((unused)),
|
||||||
|
int ref_offset EXT2FS_ATTR((unused)),
|
||||||
|
void *priv_data)
|
||||||
|
{
|
||||||
|
struct fill_dir_struct *fd = (struct fill_dir_struct *) priv_data;
|
||||||
|
struct hash_entry *new_array, *ent;
|
||||||
|
struct ext2_dir_entry *dirent;
|
||||||
|
char *dir;
|
||||||
|
unsigned int offset, dir_offset;
|
||||||
|
|
||||||
|
if (blockcnt < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
offset = blockcnt * fs->blocksize;
|
||||||
|
if (offset + fs->blocksize > fd->inode->i_size) {
|
||||||
|
fd->err = EXT2_ET_DIR_CORRUPTED;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
dir = (fd->buf+offset);
|
||||||
|
if (HOLE_BLKADDR(*block_nr)) {
|
||||||
|
memset(dir, 0, fs->blocksize);
|
||||||
|
dirent = (struct ext2_dir_entry *) dir;
|
||||||
|
dirent->rec_len = fs->blocksize;
|
||||||
|
} else {
|
||||||
|
fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
|
||||||
|
if (fd->err)
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
/* While the directory block is "hot", index it. */
|
||||||
|
dir_offset = 0;
|
||||||
|
while (dir_offset < fs->blocksize) {
|
||||||
|
dirent = (struct ext2_dir_entry *) (dir + dir_offset);
|
||||||
|
if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
|
||||||
|
(dirent->rec_len < 8) ||
|
||||||
|
((dirent->rec_len % 4) != 0) ||
|
||||||
|
(((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
|
||||||
|
fd->err = EXT2_ET_DIR_CORRUPTED;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
dir_offset += dirent->rec_len;
|
||||||
|
if (dirent->inode == 0)
|
||||||
|
continue;
|
||||||
|
if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
|
||||||
|
(dirent->name[0] == '.'))
|
||||||
|
continue;
|
||||||
|
if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
|
||||||
|
(dirent->name[0] == '.') && (dirent->name[1] == '.')) {
|
||||||
|
fd->parent = dirent->inode;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (fd->num_array >= fd->max_array) {
|
||||||
|
new_array = realloc(fd->harray,
|
||||||
|
sizeof(struct hash_entry) * (fd->max_array+500));
|
||||||
|
if (!new_array) {
|
||||||
|
fd->err = ENOMEM;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
fd->harray = new_array;
|
||||||
|
fd->max_array += 500;
|
||||||
|
}
|
||||||
|
ent = fd->harray + fd->num_array++;
|
||||||
|
ent->dir = dirent;
|
||||||
|
fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
|
||||||
|
if (fd->compress)
|
||||||
|
ent->hash = ent->minor_hash = 0;
|
||||||
|
else {
|
||||||
|
fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
|
||||||
|
dirent->name,
|
||||||
|
dirent->name_len & 0xFF,
|
||||||
|
fs->super->s_hash_seed,
|
||||||
|
&ent->hash, &ent->minor_hash);
|
||||||
|
if (fd->err)
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used for sorting the hash entry */
|
||||||
|
static EXT2_QSORT_TYPE name_cmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct hash_entry *he_a = (const struct hash_entry *) a;
|
||||||
|
const struct hash_entry *he_b = (const struct hash_entry *) b;
|
||||||
|
int ret;
|
||||||
|
int min_len;
|
||||||
|
|
||||||
|
min_len = he_a->dir->name_len;
|
||||||
|
if (min_len > he_b->dir->name_len)
|
||||||
|
min_len = he_b->dir->name_len;
|
||||||
|
|
||||||
|
ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
|
||||||
|
if (ret == 0) {
|
||||||
|
if (he_a->dir->name_len > he_b->dir->name_len)
|
||||||
|
ret = 1;
|
||||||
|
else if (he_a->dir->name_len < he_b->dir->name_len)
|
||||||
|
ret = -1;
|
||||||
|
else
|
||||||
|
ret = he_b->dir->inode - he_a->dir->inode;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used for sorting the hash entry */
|
||||||
|
static EXT2_QSORT_TYPE hash_cmp(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct hash_entry *he_a = (const struct hash_entry *) a;
|
||||||
|
const struct hash_entry *he_b = (const struct hash_entry *) b;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (he_a->hash > he_b->hash)
|
||||||
|
ret = 1;
|
||||||
|
else if (he_a->hash < he_b->hash)
|
||||||
|
ret = -1;
|
||||||
|
else {
|
||||||
|
if (he_a->minor_hash > he_b->minor_hash)
|
||||||
|
ret = 1;
|
||||||
|
else if (he_a->minor_hash < he_b->minor_hash)
|
||||||
|
ret = -1;
|
||||||
|
else
|
||||||
|
ret = name_cmp(a, b);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
|
||||||
|
int blocks)
|
||||||
|
{
|
||||||
|
void *new_mem;
|
||||||
|
|
||||||
|
if (outdir->max) {
|
||||||
|
new_mem = realloc(outdir->buf, blocks * fs->blocksize);
|
||||||
|
if (!new_mem)
|
||||||
|
return ENOMEM;
|
||||||
|
outdir->buf = new_mem;
|
||||||
|
new_mem = realloc(outdir->hashes,
|
||||||
|
blocks * sizeof(ext2_dirhash_t));
|
||||||
|
if (!new_mem)
|
||||||
|
return ENOMEM;
|
||||||
|
outdir->hashes = new_mem;
|
||||||
|
} else {
|
||||||
|
outdir->buf = malloc(blocks * fs->blocksize);
|
||||||
|
outdir->hashes = malloc(blocks * sizeof(ext2_dirhash_t));
|
||||||
|
outdir->num = 0;
|
||||||
|
}
|
||||||
|
outdir->max = blocks;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_out_dir(struct out_dir *outdir)
|
||||||
|
{
|
||||||
|
if (outdir->buf)
|
||||||
|
free(outdir->buf);
|
||||||
|
if (outdir->hashes)
|
||||||
|
free(outdir->hashes);
|
||||||
|
outdir->max = 0;
|
||||||
|
outdir->num =0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
|
||||||
|
char ** ret)
|
||||||
|
{
|
||||||
|
errcode_t retval;
|
||||||
|
|
||||||
|
if (outdir->num >= outdir->max) {
|
||||||
|
retval = alloc_size_dir(fs, outdir, outdir->max + 50);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
*ret = outdir->buf + (outdir->num++ * fs->blocksize);
|
||||||
|
memset(*ret, 0, fs->blocksize);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is used to make a unique filename. We do this by
|
||||||
|
* appending ~0, and then incrementing the number. However, we cannot
|
||||||
|
* expand the length of the filename beyond the padding available in
|
||||||
|
* the directory entry.
|
||||||
|
*/
|
||||||
|
static void mutate_name(char *str, __u16 *len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
__u16 l = *len & 0xFF, h = *len & 0xff00;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First check to see if it looks the name has been mutated
|
||||||
|
* already
|
||||||
|
*/
|
||||||
|
for (i = l-1; i > 0; i--) {
|
||||||
|
if (!isdigit(str[i]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((i == l-1) || (str[i] != '~')) {
|
||||||
|
if (((l-1) & 3) < 2)
|
||||||
|
l += 2;
|
||||||
|
else
|
||||||
|
l = (l+3) & ~3;
|
||||||
|
str[l-2] = '~';
|
||||||
|
str[l-1] = '0';
|
||||||
|
*len = l | h;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i = l-1; i >= 0; i--) {
|
||||||
|
if (isdigit(str[i])) {
|
||||||
|
if (str[i] == '9')
|
||||||
|
str[i] = '0';
|
||||||
|
else {
|
||||||
|
str[i]++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (i == 1) {
|
||||||
|
if (str[0] == 'z')
|
||||||
|
str[0] = 'A';
|
||||||
|
else if (str[0] == 'Z') {
|
||||||
|
str[0] = '~';
|
||||||
|
str[1] = '0';
|
||||||
|
} else
|
||||||
|
str[0]++;
|
||||||
|
} else if (i > 0) {
|
||||||
|
str[i] = '1';
|
||||||
|
str[i-1] = '~';
|
||||||
|
} else {
|
||||||
|
if (str[0] == '~')
|
||||||
|
str[0] = 'a';
|
||||||
|
else
|
||||||
|
str[0]++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
|
||||||
|
ext2_ino_t ino,
|
||||||
|
struct fill_dir_struct *fd)
|
||||||
|
{
|
||||||
|
struct problem_context pctx;
|
||||||
|
struct hash_entry *ent, *prev;
|
||||||
|
int i, j;
|
||||||
|
int fixed = 0;
|
||||||
|
char new_name[256];
|
||||||
|
__u16 new_len;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
pctx.ino = ino;
|
||||||
|
|
||||||
|
for (i=1; i < fd->num_array; i++) {
|
||||||
|
ent = fd->harray + i;
|
||||||
|
prev = ent - 1;
|
||||||
|
if (!ent->dir->inode ||
|
||||||
|
((ent->dir->name_len & 0xFF) !=
|
||||||
|
(prev->dir->name_len & 0xFF)) ||
|
||||||
|
(strncmp(ent->dir->name, prev->dir->name,
|
||||||
|
ent->dir->name_len & 0xFF)))
|
||||||
|
continue;
|
||||||
|
pctx.dirent = ent->dir;
|
||||||
|
if ((ent->dir->inode == prev->dir->inode) &&
|
||||||
|
fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
|
||||||
|
e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
|
||||||
|
ent->dir->inode = 0;
|
||||||
|
fixed++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
|
||||||
|
new_len = ent->dir->name_len;
|
||||||
|
mutate_name(new_name, &new_len);
|
||||||
|
for (j=0; j < fd->num_array; j++) {
|
||||||
|
if ((i==j) ||
|
||||||
|
((ent->dir->name_len & 0xFF) !=
|
||||||
|
(fd->harray[j].dir->name_len & 0xFF)) ||
|
||||||
|
(strncmp(new_name, fd->harray[j].dir->name,
|
||||||
|
new_len & 0xFF)))
|
||||||
|
continue;
|
||||||
|
mutate_name(new_name, &new_len);
|
||||||
|
|
||||||
|
j = -1;
|
||||||
|
}
|
||||||
|
new_name[new_len & 0xFF] = 0;
|
||||||
|
pctx.str = new_name;
|
||||||
|
if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
|
||||||
|
memcpy(ent->dir->name, new_name, new_len & 0xFF);
|
||||||
|
ent->dir->name_len = new_len;
|
||||||
|
ext2fs_dirhash(fs->super->s_def_hash_version,
|
||||||
|
ent->dir->name,
|
||||||
|
ent->dir->name_len & 0xFF,
|
||||||
|
fs->super->s_hash_seed,
|
||||||
|
&ent->hash, &ent->minor_hash);
|
||||||
|
fixed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static errcode_t copy_dir_entries(ext2_filsys fs,
|
||||||
|
struct fill_dir_struct *fd,
|
||||||
|
struct out_dir *outdir)
|
||||||
|
{
|
||||||
|
errcode_t retval;
|
||||||
|
char *block_start;
|
||||||
|
struct hash_entry *ent;
|
||||||
|
struct ext2_dir_entry *dirent;
|
||||||
|
int i, rec_len, left;
|
||||||
|
ext2_dirhash_t prev_hash;
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
outdir->max = 0;
|
||||||
|
retval = alloc_size_dir(fs, outdir,
|
||||||
|
(fd->dir_size / fs->blocksize) + 2);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
outdir->num = fd->compress ? 0 : 1;
|
||||||
|
offset = 0;
|
||||||
|
outdir->hashes[0] = 0;
|
||||||
|
prev_hash = 1;
|
||||||
|
if ((retval = get_next_block(fs, outdir, &block_start)))
|
||||||
|
return retval;
|
||||||
|
dirent = (struct ext2_dir_entry *) block_start;
|
||||||
|
left = fs->blocksize;
|
||||||
|
for (i=0; i < fd->num_array; i++) {
|
||||||
|
ent = fd->harray + i;
|
||||||
|
if (ent->dir->inode == 0)
|
||||||
|
continue;
|
||||||
|
rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
|
||||||
|
if (rec_len > left) {
|
||||||
|
if (left)
|
||||||
|
dirent->rec_len += left;
|
||||||
|
if ((retval = get_next_block(fs, outdir,
|
||||||
|
&block_start)))
|
||||||
|
return retval;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
left = fs->blocksize - offset;
|
||||||
|
dirent = (struct ext2_dir_entry *) (block_start + offset);
|
||||||
|
if (offset == 0) {
|
||||||
|
if (ent->hash == prev_hash)
|
||||||
|
outdir->hashes[outdir->num-1] = ent->hash | 1;
|
||||||
|
else
|
||||||
|
outdir->hashes[outdir->num-1] = ent->hash;
|
||||||
|
}
|
||||||
|
dirent->inode = ent->dir->inode;
|
||||||
|
dirent->name_len = ent->dir->name_len;
|
||||||
|
dirent->rec_len = rec_len;
|
||||||
|
memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
|
||||||
|
offset += rec_len;
|
||||||
|
left -= rec_len;
|
||||||
|
if (left < 12) {
|
||||||
|
dirent->rec_len += left;
|
||||||
|
offset += left;
|
||||||
|
left = 0;
|
||||||
|
}
|
||||||
|
prev_hash = ent->hash;
|
||||||
|
}
|
||||||
|
if (left)
|
||||||
|
dirent->rec_len += left;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
|
||||||
|
ext2_ino_t ino, ext2_ino_t parent)
|
||||||
|
{
|
||||||
|
struct ext2_dir_entry *dir;
|
||||||
|
struct ext2_dx_root_info *root;
|
||||||
|
struct ext2_dx_countlimit *limits;
|
||||||
|
int filetype = 0;
|
||||||
|
|
||||||
|
if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
|
||||||
|
filetype = EXT2_FT_DIR << 8;
|
||||||
|
|
||||||
|
memset(buf, 0, fs->blocksize);
|
||||||
|
dir = (struct ext2_dir_entry *) buf;
|
||||||
|
dir->inode = ino;
|
||||||
|
dir->name[0] = '.';
|
||||||
|
dir->name_len = 1 | filetype;
|
||||||
|
dir->rec_len = 12;
|
||||||
|
dir = (struct ext2_dir_entry *) (buf + 12);
|
||||||
|
dir->inode = parent;
|
||||||
|
dir->name[0] = '.';
|
||||||
|
dir->name[1] = '.';
|
||||||
|
dir->name_len = 2 | filetype;
|
||||||
|
dir->rec_len = fs->blocksize - 12;
|
||||||
|
|
||||||
|
root = (struct ext2_dx_root_info *) (buf+24);
|
||||||
|
root->reserved_zero = 0;
|
||||||
|
root->hash_version = fs->super->s_def_hash_version;
|
||||||
|
root->info_length = 8;
|
||||||
|
root->indirect_levels = 0;
|
||||||
|
root->unused_flags = 0;
|
||||||
|
|
||||||
|
limits = (struct ext2_dx_countlimit *) (buf+32);
|
||||||
|
limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
|
||||||
|
limits->count = 0;
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
|
||||||
|
{
|
||||||
|
struct ext2_dir_entry *dir;
|
||||||
|
struct ext2_dx_countlimit *limits;
|
||||||
|
|
||||||
|
memset(buf, 0, fs->blocksize);
|
||||||
|
dir = (struct ext2_dir_entry *) buf;
|
||||||
|
dir->inode = 0;
|
||||||
|
dir->rec_len = fs->blocksize;
|
||||||
|
|
||||||
|
limits = (struct ext2_dx_countlimit *) (buf+8);
|
||||||
|
limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
|
||||||
|
limits->count = 0;
|
||||||
|
|
||||||
|
return (struct ext2_dx_entry *) limits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function takes the leaf nodes which have been written in
|
||||||
|
* outdir, and populates the root node and any necessary interior nodes.
|
||||||
|
*/
|
||||||
|
static errcode_t calculate_tree(ext2_filsys fs,
|
||||||
|
struct out_dir *outdir,
|
||||||
|
ext2_ino_t ino,
|
||||||
|
ext2_ino_t parent)
|
||||||
|
{
|
||||||
|
struct ext2_dx_root_info *root_info;
|
||||||
|
struct ext2_dx_entry *root, *dx_ent = 0;
|
||||||
|
struct ext2_dx_countlimit *root_limit, *limit;
|
||||||
|
errcode_t retval;
|
||||||
|
char * block_start;
|
||||||
|
int i, c1, c2, nblks;
|
||||||
|
int limit_offset, root_offset;
|
||||||
|
|
||||||
|
root_info = set_root_node(fs, outdir->buf, ino, parent);
|
||||||
|
root_offset = limit_offset = ((char *) root_info - outdir->buf) +
|
||||||
|
root_info->info_length;
|
||||||
|
root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
|
||||||
|
c1 = root_limit->limit;
|
||||||
|
nblks = outdir->num;
|
||||||
|
|
||||||
|
/* Write out the pointer blocks */
|
||||||
|
if (nblks-1 <= c1) {
|
||||||
|
/* Just write out the root block, and we're done */
|
||||||
|
root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
|
||||||
|
for (i=1; i < nblks; i++) {
|
||||||
|
root->block = ext2fs_cpu_to_le32(i);
|
||||||
|
if (i != 1)
|
||||||
|
root->hash =
|
||||||
|
ext2fs_cpu_to_le32(outdir->hashes[i]);
|
||||||
|
root++;
|
||||||
|
c1--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c2 = 0;
|
||||||
|
limit = 0;
|
||||||
|
root_info->indirect_levels = 1;
|
||||||
|
for (i=1; i < nblks; i++) {
|
||||||
|
if (c1 == 0)
|
||||||
|
return ENOSPC;
|
||||||
|
if (c2 == 0) {
|
||||||
|
if (limit)
|
||||||
|
limit->limit = limit->count =
|
||||||
|
ext2fs_cpu_to_le16(limit->limit);
|
||||||
|
root = (struct ext2_dx_entry *)
|
||||||
|
(outdir->buf + root_offset);
|
||||||
|
root->block = ext2fs_cpu_to_le32(outdir->num);
|
||||||
|
if (i != 1)
|
||||||
|
root->hash =
|
||||||
|
ext2fs_cpu_to_le32(outdir->hashes[i]);
|
||||||
|
if ((retval = get_next_block(fs, outdir,
|
||||||
|
&block_start)))
|
||||||
|
return retval;
|
||||||
|
dx_ent = set_int_node(fs, block_start);
|
||||||
|
limit = (struct ext2_dx_countlimit *) dx_ent;
|
||||||
|
c2 = limit->limit;
|
||||||
|
root_offset += sizeof(struct ext2_dx_entry);
|
||||||
|
c1--;
|
||||||
|
}
|
||||||
|
dx_ent->block = ext2fs_cpu_to_le32(i);
|
||||||
|
if (c2 != limit->limit)
|
||||||
|
dx_ent->hash =
|
||||||
|
ext2fs_cpu_to_le32(outdir->hashes[i]);
|
||||||
|
dx_ent++;
|
||||||
|
c2--;
|
||||||
|
}
|
||||||
|
limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
|
||||||
|
limit->limit = ext2fs_cpu_to_le16(limit->limit);
|
||||||
|
}
|
||||||
|
root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
|
||||||
|
root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
|
||||||
|
root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct write_dir_struct {
|
||||||
|
struct out_dir *outdir;
|
||||||
|
errcode_t err;
|
||||||
|
e2fsck_t ctx;
|
||||||
|
int cleared;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper function which writes out a directory block.
|
||||||
|
*/
|
||||||
|
static int write_dir_block(ext2_filsys fs,
|
||||||
|
blk_t *block_nr,
|
||||||
|
e2_blkcnt_t blockcnt,
|
||||||
|
blk_t ref_block EXT2FS_ATTR((unused)),
|
||||||
|
int ref_offset EXT2FS_ATTR((unused)),
|
||||||
|
void *priv_data)
|
||||||
|
{
|
||||||
|
struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
|
||||||
|
blk_t blk;
|
||||||
|
char *dir;
|
||||||
|
|
||||||
|
if (*block_nr == 0)
|
||||||
|
return 0;
|
||||||
|
if (blockcnt >= wd->outdir->num) {
|
||||||
|
e2fsck_read_bitmaps(wd->ctx);
|
||||||
|
blk = *block_nr;
|
||||||
|
ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk);
|
||||||
|
ext2fs_block_alloc_stats(fs, blk, -1);
|
||||||
|
*block_nr = 0;
|
||||||
|
wd->cleared++;
|
||||||
|
return BLOCK_CHANGED;
|
||||||
|
}
|
||||||
|
if (blockcnt < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dir = wd->outdir->buf + (blockcnt * fs->blocksize);
|
||||||
|
wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
|
||||||
|
if (wd->err)
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
|
||||||
|
struct out_dir *outdir,
|
||||||
|
ext2_ino_t ino, int compress)
|
||||||
|
{
|
||||||
|
struct write_dir_struct wd;
|
||||||
|
errcode_t retval;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
|
||||||
|
retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
wd.outdir = outdir;
|
||||||
|
wd.err = 0;
|
||||||
|
wd.ctx = ctx;
|
||||||
|
wd.cleared = 0;
|
||||||
|
|
||||||
|
retval = ext2fs_block_iterate2(fs, ino, 0, 0,
|
||||||
|
write_dir_block, &wd);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
if (wd.err)
|
||||||
|
return wd.err;
|
||||||
|
|
||||||
|
e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
|
||||||
|
if (compress)
|
||||||
|
inode.i_flags &= ~EXT2_INDEX_FL;
|
||||||
|
else
|
||||||
|
inode.i_flags |= EXT2_INDEX_FL;
|
||||||
|
inode.i_size = outdir->num * fs->blocksize;
|
||||||
|
inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
|
||||||
|
e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
errcode_t retval;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
char *dir_buf = 0;
|
||||||
|
struct fill_dir_struct fd;
|
||||||
|
struct out_dir outdir;
|
||||||
|
|
||||||
|
outdir.max = outdir.num = 0;
|
||||||
|
outdir.buf = 0;
|
||||||
|
outdir.hashes = 0;
|
||||||
|
e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
|
||||||
|
|
||||||
|
retval = ENOMEM;
|
||||||
|
fd.harray = 0;
|
||||||
|
dir_buf = malloc(inode.i_size);
|
||||||
|
if (!dir_buf)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
fd.max_array = inode.i_size / 32;
|
||||||
|
fd.num_array = 0;
|
||||||
|
fd.harray = malloc(fd.max_array * sizeof(struct hash_entry));
|
||||||
|
if (!fd.harray)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
fd.ctx = ctx;
|
||||||
|
fd.buf = dir_buf;
|
||||||
|
fd.inode = &inode;
|
||||||
|
fd.err = 0;
|
||||||
|
fd.dir_size = 0;
|
||||||
|
fd.compress = 0;
|
||||||
|
if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
|
||||||
|
(inode.i_size / fs->blocksize) < 2)
|
||||||
|
fd.compress = 1;
|
||||||
|
fd.parent = 0;
|
||||||
|
|
||||||
|
/* Read in the entire directory into memory */
|
||||||
|
retval = ext2fs_block_iterate2(fs, ino, 0, 0,
|
||||||
|
fill_dir_block, &fd);
|
||||||
|
if (fd.err) {
|
||||||
|
retval = fd.err;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
printf("%d entries (%d bytes) found in inode %d\n",
|
||||||
|
fd.num_array, fd.dir_size, ino);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Sort the list */
|
||||||
|
resort:
|
||||||
|
if (fd.compress)
|
||||||
|
qsort(fd.harray+2, fd.num_array-2,
|
||||||
|
sizeof(struct hash_entry), name_cmp);
|
||||||
|
else
|
||||||
|
qsort(fd.harray, fd.num_array,
|
||||||
|
sizeof(struct hash_entry), hash_cmp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look for duplicates
|
||||||
|
*/
|
||||||
|
if (duplicate_search_and_fix(ctx, fs, ino, &fd))
|
||||||
|
goto resort;
|
||||||
|
|
||||||
|
if (ctx->options & E2F_OPT_NO) {
|
||||||
|
retval = 0;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the directory entries. In a htree directory these
|
||||||
|
* will become the leaf nodes.
|
||||||
|
*/
|
||||||
|
retval = copy_dir_entries(fs, &fd, &outdir);
|
||||||
|
if (retval)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
free(dir_buf); dir_buf = 0;
|
||||||
|
|
||||||
|
if (!fd.compress) {
|
||||||
|
/* Calculate the interior nodes */
|
||||||
|
retval = calculate_tree(fs, &outdir, ino, fd.parent);
|
||||||
|
if (retval)
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
|
||||||
|
if (retval)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
errout:
|
||||||
|
if (dir_buf)
|
||||||
|
free(dir_buf);
|
||||||
|
if (fd.harray)
|
||||||
|
free(fd.harray);
|
||||||
|
|
||||||
|
free_out_dir(&outdir);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2fsck_rehash_directories(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
struct problem_context pctx;
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
struct resource_track rtrack;
|
||||||
|
#endif
|
||||||
|
struct dir_info *dir;
|
||||||
|
ext2_u32_iterate iter;
|
||||||
|
ext2_ino_t ino;
|
||||||
|
errcode_t retval;
|
||||||
|
int i, cur, max, all_dirs, dir_index, first = 1;
|
||||||
|
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
init_resource_track(&rtrack);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
|
||||||
|
|
||||||
|
if (!ctx->dirs_to_hash && !all_dirs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
e2fsck_get_lost_and_found(ctx, 0);
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
|
||||||
|
cur = 0;
|
||||||
|
if (all_dirs) {
|
||||||
|
i = 0;
|
||||||
|
max = e2fsck_get_num_dirinfo(ctx);
|
||||||
|
} else {
|
||||||
|
retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
|
||||||
|
&iter);
|
||||||
|
if (retval) {
|
||||||
|
pctx.errcode = retval;
|
||||||
|
fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
max = ext2fs_u32_list_count(ctx->dirs_to_hash);
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
if (all_dirs) {
|
||||||
|
if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0)
|
||||||
|
break;
|
||||||
|
ino = dir->ino;
|
||||||
|
} else {
|
||||||
|
if (!ext2fs_u32_list_iterate(iter, &ino))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ino == ctx->lost_and_found)
|
||||||
|
continue;
|
||||||
|
pctx.dir = ino;
|
||||||
|
if (first) {
|
||||||
|
fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
|
||||||
|
first = 0;
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
fix_problem(ctx, PR_3A_OPTIMIZE_DIR, &pctx);
|
||||||
|
#endif
|
||||||
|
pctx.errcode = e2fsck_rehash_dir(ctx, ino);
|
||||||
|
if (pctx.errcode) {
|
||||||
|
end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
|
||||||
|
fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
|
||||||
|
}
|
||||||
|
if (ctx->progress && !ctx->progress_fd)
|
||||||
|
e2fsck_simple_progress(ctx, "Rebuilding directory",
|
||||||
|
100.0 * (float) (++cur) / (float) max, ino);
|
||||||
|
}
|
||||||
|
end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
|
||||||
|
if (!all_dirs)
|
||||||
|
ext2fs_u32_list_iterate_end(iter);
|
||||||
|
|
||||||
|
if (ctx->dirs_to_hash)
|
||||||
|
ext2fs_u32_list_free(ctx->dirs_to_hash);
|
||||||
|
ctx->dirs_to_hash = 0;
|
||||||
|
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
if (ctx->options & E2F_OPT_TIME2) {
|
||||||
|
e2fsck_clear_progbar(ctx);
|
||||||
|
print_resource_track("Pass 3A", &rtrack);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
640
e2fsprogs/e2fsck/revoke.c
Normal file
640
e2fsprogs/e2fsck/revoke.c
Normal file
|
@ -0,0 +1,640 @@
|
||||||
|
/*
|
||||||
|
* linux/fs/revoke.c
|
||||||
|
*
|
||||||
|
* Written by Stephen C. Tweedie <sct@redhat.com>, 2000
|
||||||
|
*
|
||||||
|
* Copyright 2000 Red Hat corp --- All Rights Reserved
|
||||||
|
*
|
||||||
|
* This file is part of the Linux kernel and is made available under
|
||||||
|
* the terms of the GNU General Public License, version 2, or at your
|
||||||
|
* option, any later version, incorporated herein by reference.
|
||||||
|
*
|
||||||
|
* Journal revoke routines for the generic filesystem journaling code;
|
||||||
|
* part of the ext2fs journaling system.
|
||||||
|
*
|
||||||
|
* Revoke is the mechanism used to prevent old log records for deleted
|
||||||
|
* metadata from being replayed on top of newer data using the same
|
||||||
|
* blocks. The revoke mechanism is used in two separate places:
|
||||||
|
*
|
||||||
|
* + Commit: during commit we write the entire list of the current
|
||||||
|
* transaction's revoked blocks to the journal
|
||||||
|
*
|
||||||
|
* + Recovery: during recovery we record the transaction ID of all
|
||||||
|
* revoked blocks. If there are multiple revoke records in the log
|
||||||
|
* for a single block, only the last one counts, and if there is a log
|
||||||
|
* entry for a block beyond the last revoke, then that log entry still
|
||||||
|
* gets replayed.
|
||||||
|
*
|
||||||
|
* We can get interactions between revokes and new log data within a
|
||||||
|
* single transaction:
|
||||||
|
*
|
||||||
|
* Block is revoked and then journaled:
|
||||||
|
* The desired end result is the journaling of the new block, so we
|
||||||
|
* cancel the revoke before the transaction commits.
|
||||||
|
*
|
||||||
|
* Block is journaled and then revoked:
|
||||||
|
* The revoke must take precedence over the write of the block, so we
|
||||||
|
* need either to cancel the journal entry or to write the revoke
|
||||||
|
* later in the log than the log block. In this case, we choose the
|
||||||
|
* latter: journaling a block cancels any revoke record for that block
|
||||||
|
* in the current transaction, so any revoke for that block in the
|
||||||
|
* transaction must have happened after the block was journaled and so
|
||||||
|
* the revoke must take precedence.
|
||||||
|
*
|
||||||
|
* Block is revoked and then written as data:
|
||||||
|
* The data write is allowed to succeed, but the revoke is _not_
|
||||||
|
* cancelled. We still need to prevent old log records from
|
||||||
|
* overwriting the new data. We don't even need to clear the revoke
|
||||||
|
* bit here.
|
||||||
|
*
|
||||||
|
* Revoke information on buffers is a tri-state value:
|
||||||
|
*
|
||||||
|
* RevokeValid clear: no cached revoke status, need to look it up
|
||||||
|
* RevokeValid set, Revoked clear:
|
||||||
|
* buffer has not been revoked, and cancel_revoke
|
||||||
|
* need do nothing.
|
||||||
|
* RevokeValid set, Revoked set:
|
||||||
|
* buffer has been revoked.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __KERNEL__
|
||||||
|
#include "jfs_user.h"
|
||||||
|
#else
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/jbd.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/locks.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/smp_lock.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static kmem_cache_t *revoke_record_cache;
|
||||||
|
static kmem_cache_t *revoke_table_cache;
|
||||||
|
|
||||||
|
/* Each revoke record represents one single revoked block. During
|
||||||
|
journal replay, this involves recording the transaction ID of the
|
||||||
|
last transaction to revoke this block. */
|
||||||
|
|
||||||
|
struct jbd_revoke_record_s
|
||||||
|
{
|
||||||
|
struct list_head hash;
|
||||||
|
tid_t sequence; /* Used for recovery only */
|
||||||
|
unsigned long blocknr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* The revoke table is just a simple hash table of revoke records. */
|
||||||
|
struct jbd_revoke_table_s
|
||||||
|
{
|
||||||
|
/* It is conceivable that we might want a larger hash table
|
||||||
|
* for recovery. Must be a power of two. */
|
||||||
|
int hash_size;
|
||||||
|
int hash_shift;
|
||||||
|
struct list_head *hash_table;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
static void write_one_revoke_record(journal_t *, transaction_t *,
|
||||||
|
struct journal_head **, int *,
|
||||||
|
struct jbd_revoke_record_s *);
|
||||||
|
static void flush_descriptor(journal_t *, struct journal_head *, int);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Utility functions to maintain the revoke table */
|
||||||
|
|
||||||
|
/* Borrowed from buffer.c: this is a tried and tested block hash function */
|
||||||
|
static inline int hash(journal_t *journal, unsigned long block)
|
||||||
|
{
|
||||||
|
struct jbd_revoke_table_s *table = journal->j_revoke;
|
||||||
|
int hash_shift = table->hash_shift;
|
||||||
|
|
||||||
|
return ((block << (hash_shift - 6)) ^
|
||||||
|
(block >> 13) ^
|
||||||
|
(block << (hash_shift - 12))) & (table->hash_size - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int insert_revoke_hash(journal_t *journal, unsigned long blocknr,
|
||||||
|
tid_t seq)
|
||||||
|
{
|
||||||
|
struct list_head *hash_list;
|
||||||
|
struct jbd_revoke_record_s *record;
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
repeat:
|
||||||
|
#endif
|
||||||
|
record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
|
||||||
|
if (!record)
|
||||||
|
goto oom;
|
||||||
|
|
||||||
|
record->sequence = seq;
|
||||||
|
record->blocknr = blocknr;
|
||||||
|
hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
|
||||||
|
list_add(&record->hash, hash_list);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
oom:
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
if (!journal_oom_retry)
|
||||||
|
return -ENOMEM;
|
||||||
|
jbd_debug(1, "ENOMEM in " __FUNCTION__ ", retrying.\n");
|
||||||
|
current->policy |= SCHED_YIELD;
|
||||||
|
schedule();
|
||||||
|
goto repeat;
|
||||||
|
#else
|
||||||
|
return -ENOMEM;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find a revoke record in the journal's hash table. */
|
||||||
|
|
||||||
|
static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
|
||||||
|
unsigned long blocknr)
|
||||||
|
{
|
||||||
|
struct list_head *hash_list;
|
||||||
|
struct jbd_revoke_record_s *record;
|
||||||
|
|
||||||
|
hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
|
||||||
|
|
||||||
|
record = (struct jbd_revoke_record_s *) hash_list->next;
|
||||||
|
while (&(record->hash) != hash_list) {
|
||||||
|
if (record->blocknr == blocknr)
|
||||||
|
return record;
|
||||||
|
record = (struct jbd_revoke_record_s *) record->hash.next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int __init journal_init_revoke_caches(void)
|
||||||
|
{
|
||||||
|
revoke_record_cache = kmem_cache_create("revoke_record",
|
||||||
|
sizeof(struct jbd_revoke_record_s),
|
||||||
|
0, SLAB_HWCACHE_ALIGN, NULL, NULL);
|
||||||
|
if (revoke_record_cache == 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
revoke_table_cache = kmem_cache_create("revoke_table",
|
||||||
|
sizeof(struct jbd_revoke_table_s),
|
||||||
|
0, 0, NULL, NULL);
|
||||||
|
if (revoke_table_cache == 0) {
|
||||||
|
kmem_cache_destroy(revoke_record_cache);
|
||||||
|
revoke_record_cache = NULL;
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void journal_destroy_revoke_caches(void)
|
||||||
|
{
|
||||||
|
kmem_cache_destroy(revoke_record_cache);
|
||||||
|
revoke_record_cache = 0;
|
||||||
|
kmem_cache_destroy(revoke_table_cache);
|
||||||
|
revoke_table_cache = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialise the revoke table for a given journal to a given size. */
|
||||||
|
|
||||||
|
int journal_init_revoke(journal_t *journal, int hash_size)
|
||||||
|
{
|
||||||
|
int shift, tmp;
|
||||||
|
|
||||||
|
J_ASSERT (journal->j_revoke == NULL);
|
||||||
|
|
||||||
|
journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
|
||||||
|
if (!journal->j_revoke)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* Check that the hash_size is a power of two */
|
||||||
|
J_ASSERT ((hash_size & (hash_size-1)) == 0);
|
||||||
|
|
||||||
|
journal->j_revoke->hash_size = hash_size;
|
||||||
|
|
||||||
|
shift = 0;
|
||||||
|
tmp = hash_size;
|
||||||
|
while((tmp >>= 1UL) != 0UL)
|
||||||
|
shift++;
|
||||||
|
journal->j_revoke->hash_shift = shift;
|
||||||
|
|
||||||
|
journal->j_revoke->hash_table =
|
||||||
|
kmalloc(hash_size * sizeof(struct list_head), GFP_KERNEL);
|
||||||
|
if (!journal->j_revoke->hash_table) {
|
||||||
|
kmem_cache_free(revoke_table_cache, journal->j_revoke);
|
||||||
|
journal->j_revoke = NULL;
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (tmp = 0; tmp < hash_size; tmp++)
|
||||||
|
INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Destoy a journal's revoke table. The table must already be empty! */
|
||||||
|
|
||||||
|
void journal_destroy_revoke(journal_t *journal)
|
||||||
|
{
|
||||||
|
struct jbd_revoke_table_s *table;
|
||||||
|
struct list_head *hash_list;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
table = journal->j_revoke;
|
||||||
|
if (!table)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i=0; i<table->hash_size; i++) {
|
||||||
|
hash_list = &table->hash_table[i];
|
||||||
|
J_ASSERT (list_empty(hash_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(table->hash_table);
|
||||||
|
kmem_cache_free(revoke_table_cache, table);
|
||||||
|
journal->j_revoke = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
|
/*
|
||||||
|
* journal_revoke: revoke a given buffer_head from the journal. This
|
||||||
|
* prevents the block from being replayed during recovery if we take a
|
||||||
|
* crash after this current transaction commits. Any subsequent
|
||||||
|
* metadata writes of the buffer in this transaction cancel the
|
||||||
|
* revoke.
|
||||||
|
*
|
||||||
|
* Note that this call may block --- it is up to the caller to make
|
||||||
|
* sure that there are no further calls to journal_write_metadata
|
||||||
|
* before the revoke is complete. In ext3, this implies calling the
|
||||||
|
* revoke before clearing the block bitmap when we are deleting
|
||||||
|
* metadata.
|
||||||
|
*
|
||||||
|
* Revoke performs a journal_forget on any buffer_head passed in as a
|
||||||
|
* parameter, but does _not_ forget the buffer_head if the bh was only
|
||||||
|
* found implicitly.
|
||||||
|
*
|
||||||
|
* bh_in may not be a journalled buffer - it may have come off
|
||||||
|
* the hash tables without an attached journal_head.
|
||||||
|
*
|
||||||
|
* If bh_in is non-zero, journal_revoke() will decrement its b_count
|
||||||
|
* by one.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int journal_revoke(handle_t *handle, unsigned long blocknr,
|
||||||
|
struct buffer_head *bh_in)
|
||||||
|
{
|
||||||
|
struct buffer_head *bh = NULL;
|
||||||
|
journal_t *journal;
|
||||||
|
kdev_t dev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (bh_in)
|
||||||
|
BUFFER_TRACE(bh_in, "enter");
|
||||||
|
|
||||||
|
journal = handle->h_transaction->t_journal;
|
||||||
|
if (!journal_set_features(journal, 0, 0, JFS_FEATURE_INCOMPAT_REVOKE)){
|
||||||
|
J_ASSERT (!"Cannot set revoke feature!");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev = journal->j_fs_dev;
|
||||||
|
bh = bh_in;
|
||||||
|
|
||||||
|
if (!bh) {
|
||||||
|
bh = get_hash_table(dev, blocknr, journal->j_blocksize);
|
||||||
|
if (bh)
|
||||||
|
BUFFER_TRACE(bh, "found on hash");
|
||||||
|
}
|
||||||
|
#ifdef JBD_EXPENSIVE_CHECKING
|
||||||
|
else {
|
||||||
|
struct buffer_head *bh2;
|
||||||
|
|
||||||
|
/* If there is a different buffer_head lying around in
|
||||||
|
* memory anywhere... */
|
||||||
|
bh2 = get_hash_table(dev, blocknr, journal->j_blocksize);
|
||||||
|
if (bh2) {
|
||||||
|
/* ... and it has RevokeValid status... */
|
||||||
|
if ((bh2 != bh) &&
|
||||||
|
test_bit(BH_RevokeValid, &bh2->b_state))
|
||||||
|
/* ...then it better be revoked too,
|
||||||
|
* since it's illegal to create a revoke
|
||||||
|
* record against a buffer_head which is
|
||||||
|
* not marked revoked --- that would
|
||||||
|
* risk missing a subsequent revoke
|
||||||
|
* cancel. */
|
||||||
|
J_ASSERT_BH(bh2, test_bit(BH_Revoked, &
|
||||||
|
bh2->b_state));
|
||||||
|
__brelse(bh2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* We really ought not ever to revoke twice in a row without
|
||||||
|
first having the revoke cancelled: it's illegal to free a
|
||||||
|
block twice without allocating it in between! */
|
||||||
|
if (bh) {
|
||||||
|
J_ASSERT_BH(bh, !test_bit(BH_Revoked, &bh->b_state));
|
||||||
|
set_bit(BH_Revoked, &bh->b_state);
|
||||||
|
set_bit(BH_RevokeValid, &bh->b_state);
|
||||||
|
if (bh_in) {
|
||||||
|
BUFFER_TRACE(bh_in, "call journal_forget");
|
||||||
|
journal_forget(handle, bh_in);
|
||||||
|
} else {
|
||||||
|
BUFFER_TRACE(bh, "call brelse");
|
||||||
|
__brelse(bh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lock_journal(journal);
|
||||||
|
jbd_debug(2, "insert revoke for block %lu, bh_in=%p\n", blocknr, bh_in);
|
||||||
|
err = insert_revoke_hash(journal, blocknr,
|
||||||
|
handle->h_transaction->t_tid);
|
||||||
|
unlock_journal(journal);
|
||||||
|
BUFFER_TRACE(bh_in, "exit");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cancel an outstanding revoke. For use only internally by the
|
||||||
|
* journaling code (called from journal_get_write_access).
|
||||||
|
*
|
||||||
|
* We trust the BH_Revoked bit on the buffer if the buffer is already
|
||||||
|
* being journaled: if there is no revoke pending on the buffer, then we
|
||||||
|
* don't do anything here.
|
||||||
|
*
|
||||||
|
* This would break if it were possible for a buffer to be revoked and
|
||||||
|
* discarded, and then reallocated within the same transaction. In such
|
||||||
|
* a case we would have lost the revoked bit, but when we arrived here
|
||||||
|
* the second time we would still have a pending revoke to cancel. So,
|
||||||
|
* do not trust the Revoked bit on buffers unless RevokeValid is also
|
||||||
|
* set.
|
||||||
|
*
|
||||||
|
* The caller must have the journal locked.
|
||||||
|
*/
|
||||||
|
int journal_cancel_revoke(handle_t *handle, struct journal_head *jh)
|
||||||
|
{
|
||||||
|
struct jbd_revoke_record_s *record;
|
||||||
|
journal_t *journal = handle->h_transaction->t_journal;
|
||||||
|
int need_cancel;
|
||||||
|
int did_revoke = 0; /* akpm: debug */
|
||||||
|
struct buffer_head *bh = jh2bh(jh);
|
||||||
|
|
||||||
|
jbd_debug(4, "journal_head %p, cancelling revoke\n", jh);
|
||||||
|
|
||||||
|
/* Is the existing Revoke bit valid? If so, we trust it, and
|
||||||
|
* only perform the full cancel if the revoke bit is set. If
|
||||||
|
* not, we can't trust the revoke bit, and we need to do the
|
||||||
|
* full search for a revoke record. */
|
||||||
|
if (test_and_set_bit(BH_RevokeValid, &bh->b_state))
|
||||||
|
need_cancel = (test_and_clear_bit(BH_Revoked, &bh->b_state));
|
||||||
|
else {
|
||||||
|
need_cancel = 1;
|
||||||
|
clear_bit(BH_Revoked, &bh->b_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (need_cancel) {
|
||||||
|
record = find_revoke_record(journal, bh->b_blocknr);
|
||||||
|
if (record) {
|
||||||
|
jbd_debug(4, "cancelled existing revoke on "
|
||||||
|
"blocknr %lu\n", bh->b_blocknr);
|
||||||
|
list_del(&record->hash);
|
||||||
|
kmem_cache_free(revoke_record_cache, record);
|
||||||
|
did_revoke = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef JBD_EXPENSIVE_CHECKING
|
||||||
|
/* There better not be one left behind by now! */
|
||||||
|
record = find_revoke_record(journal, bh->b_blocknr);
|
||||||
|
J_ASSERT_JH(jh, record == NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Finally, have we just cleared revoke on an unhashed
|
||||||
|
* buffer_head? If so, we'd better make sure we clear the
|
||||||
|
* revoked status on any hashed alias too, otherwise the revoke
|
||||||
|
* state machine will get very upset later on. */
|
||||||
|
if (need_cancel && !bh->b_pprev) {
|
||||||
|
struct buffer_head *bh2;
|
||||||
|
bh2 = get_hash_table(bh->b_dev, bh->b_blocknr, bh->b_size);
|
||||||
|
if (bh2) {
|
||||||
|
clear_bit(BH_Revoked, &bh2->b_state);
|
||||||
|
__brelse(bh2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return did_revoke;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write revoke records to the journal for all entries in the current
|
||||||
|
* revoke hash, deleting the entries as we go.
|
||||||
|
*
|
||||||
|
* Called with the journal lock held.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void journal_write_revoke_records(journal_t *journal,
|
||||||
|
transaction_t *transaction)
|
||||||
|
{
|
||||||
|
struct journal_head *descriptor;
|
||||||
|
struct jbd_revoke_record_s *record;
|
||||||
|
struct jbd_revoke_table_s *revoke;
|
||||||
|
struct list_head *hash_list;
|
||||||
|
int i, offset, count;
|
||||||
|
|
||||||
|
descriptor = NULL;
|
||||||
|
offset = 0;
|
||||||
|
count = 0;
|
||||||
|
revoke = journal->j_revoke;
|
||||||
|
|
||||||
|
for (i = 0; i < revoke->hash_size; i++) {
|
||||||
|
hash_list = &revoke->hash_table[i];
|
||||||
|
|
||||||
|
while (!list_empty(hash_list)) {
|
||||||
|
record = (struct jbd_revoke_record_s *)
|
||||||
|
hash_list->next;
|
||||||
|
write_one_revoke_record(journal, transaction,
|
||||||
|
&descriptor, &offset,
|
||||||
|
record);
|
||||||
|
count++;
|
||||||
|
list_del(&record->hash);
|
||||||
|
kmem_cache_free(revoke_record_cache, record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (descriptor)
|
||||||
|
flush_descriptor(journal, descriptor, offset);
|
||||||
|
jbd_debug(1, "Wrote %d revoke records\n", count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write out one revoke record. We need to create a new descriptor
|
||||||
|
* block if the old one is full or if we have not already created one.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void write_one_revoke_record(journal_t *journal,
|
||||||
|
transaction_t *transaction,
|
||||||
|
struct journal_head **descriptorp,
|
||||||
|
int *offsetp,
|
||||||
|
struct jbd_revoke_record_s *record)
|
||||||
|
{
|
||||||
|
struct journal_head *descriptor;
|
||||||
|
int offset;
|
||||||
|
journal_header_t *header;
|
||||||
|
|
||||||
|
/* If we are already aborting, this all becomes a noop. We
|
||||||
|
still need to go round the loop in
|
||||||
|
journal_write_revoke_records in order to free all of the
|
||||||
|
revoke records: only the IO to the journal is omitted. */
|
||||||
|
if (is_journal_aborted(journal))
|
||||||
|
return;
|
||||||
|
|
||||||
|
descriptor = *descriptorp;
|
||||||
|
offset = *offsetp;
|
||||||
|
|
||||||
|
/* Make sure we have a descriptor with space left for the record */
|
||||||
|
if (descriptor) {
|
||||||
|
if (offset == journal->j_blocksize) {
|
||||||
|
flush_descriptor(journal, descriptor, offset);
|
||||||
|
descriptor = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!descriptor) {
|
||||||
|
descriptor = journal_get_descriptor_buffer(journal);
|
||||||
|
if (!descriptor)
|
||||||
|
return;
|
||||||
|
header = (journal_header_t *) &jh2bh(descriptor)->b_data[0];
|
||||||
|
header->h_magic = htonl(JFS_MAGIC_NUMBER);
|
||||||
|
header->h_blocktype = htonl(JFS_REVOKE_BLOCK);
|
||||||
|
header->h_sequence = htonl(transaction->t_tid);
|
||||||
|
|
||||||
|
/* Record it so that we can wait for IO completion later */
|
||||||
|
JBUFFER_TRACE(descriptor, "file as BJ_LogCtl");
|
||||||
|
journal_file_buffer(descriptor, transaction, BJ_LogCtl);
|
||||||
|
|
||||||
|
offset = sizeof(journal_revoke_header_t);
|
||||||
|
*descriptorp = descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
* ((unsigned int *)(&jh2bh(descriptor)->b_data[offset])) =
|
||||||
|
htonl(record->blocknr);
|
||||||
|
offset += 4;
|
||||||
|
*offsetp = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flush a revoke descriptor out to the journal. If we are aborting,
|
||||||
|
* this is a noop; otherwise we are generating a buffer which needs to
|
||||||
|
* be waited for during commit, so it has to go onto the appropriate
|
||||||
|
* journal buffer list.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void flush_descriptor(journal_t *journal,
|
||||||
|
struct journal_head *descriptor,
|
||||||
|
int offset)
|
||||||
|
{
|
||||||
|
journal_revoke_header_t *header;
|
||||||
|
|
||||||
|
if (is_journal_aborted(journal)) {
|
||||||
|
JBUFFER_TRACE(descriptor, "brelse");
|
||||||
|
__brelse(jh2bh(descriptor));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
header = (journal_revoke_header_t *) jh2bh(descriptor)->b_data;
|
||||||
|
header->r_count = htonl(offset);
|
||||||
|
set_bit(BH_JWrite, &jh2bh(descriptor)->b_state);
|
||||||
|
{
|
||||||
|
struct buffer_head *bh = jh2bh(descriptor);
|
||||||
|
BUFFER_TRACE(bh, "write");
|
||||||
|
ll_rw_block (WRITE, 1, &bh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Revoke support for recovery.
|
||||||
|
*
|
||||||
|
* Recovery needs to be able to:
|
||||||
|
*
|
||||||
|
* record all revoke records, including the tid of the latest instance
|
||||||
|
* of each revoke in the journal
|
||||||
|
*
|
||||||
|
* check whether a given block in a given transaction should be replayed
|
||||||
|
* (ie. has not been revoked by a revoke record in that or a subsequent
|
||||||
|
* transaction)
|
||||||
|
*
|
||||||
|
* empty the revoke table after recovery.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, setting revoke records. We create a new revoke record for
|
||||||
|
* every block ever revoked in the log as we scan it for recovery, and
|
||||||
|
* we update the existing records if we find multiple revokes for a
|
||||||
|
* single block.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int journal_set_revoke(journal_t *journal,
|
||||||
|
unsigned long blocknr,
|
||||||
|
tid_t sequence)
|
||||||
|
{
|
||||||
|
struct jbd_revoke_record_s *record;
|
||||||
|
|
||||||
|
record = find_revoke_record(journal, blocknr);
|
||||||
|
if (record) {
|
||||||
|
/* If we have multiple occurences, only record the
|
||||||
|
* latest sequence number in the hashed record */
|
||||||
|
if (tid_gt(sequence, record->sequence))
|
||||||
|
record->sequence = sequence;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return insert_revoke_hash(journal, blocknr, sequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test revoke records. For a given block referenced in the log, has
|
||||||
|
* that block been revoked? A revoke record with a given transaction
|
||||||
|
* sequence number revokes all blocks in that transaction and earlier
|
||||||
|
* ones, but later transactions still need replayed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int journal_test_revoke(journal_t *journal,
|
||||||
|
unsigned long blocknr,
|
||||||
|
tid_t sequence)
|
||||||
|
{
|
||||||
|
struct jbd_revoke_record_s *record;
|
||||||
|
|
||||||
|
record = find_revoke_record(journal, blocknr);
|
||||||
|
if (!record)
|
||||||
|
return 0;
|
||||||
|
if (tid_gt(sequence, record->sequence))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finally, once recovery is over, we need to clear the revoke table so
|
||||||
|
* that it can be reused by the running filesystem.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void journal_clear_revoke(journal_t *journal)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct list_head *hash_list;
|
||||||
|
struct jbd_revoke_record_s *record;
|
||||||
|
struct jbd_revoke_table_s *revoke_var;
|
||||||
|
|
||||||
|
revoke_var = journal->j_revoke;
|
||||||
|
|
||||||
|
for (i = 0; i < revoke_var->hash_size; i++) {
|
||||||
|
hash_list = &revoke_var->hash_table[i];
|
||||||
|
while (!list_empty(hash_list)) {
|
||||||
|
record = (struct jbd_revoke_record_s*) hash_list->next;
|
||||||
|
list_del(&record->hash);
|
||||||
|
kmem_cache_free(revoke_record_cache, record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
713
e2fsprogs/e2fsck/super.c
Normal file
713
e2fsprogs/e2fsck/super.c
Normal file
|
@ -0,0 +1,713 @@
|
||||||
|
/*
|
||||||
|
* e2fsck.c - superblock checks
|
||||||
|
*
|
||||||
|
* Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef EXT2_SKIP_UUID
|
||||||
|
#include "uuid/uuid.h"
|
||||||
|
#endif
|
||||||
|
#include "e2fsck.h"
|
||||||
|
#include "problem.h"
|
||||||
|
|
||||||
|
#define MIN_CHECK 1
|
||||||
|
#define MAX_CHECK 2
|
||||||
|
|
||||||
|
static void check_super_value(e2fsck_t ctx, const char *descr,
|
||||||
|
unsigned long value, int flags,
|
||||||
|
unsigned long min_val, unsigned long max_val)
|
||||||
|
{
|
||||||
|
struct problem_context pctx;
|
||||||
|
|
||||||
|
if (((flags & MIN_CHECK) && (value < min_val)) ||
|
||||||
|
((flags & MAX_CHECK) && (value > max_val))) {
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
pctx.num = value;
|
||||||
|
pctx.str = descr;
|
||||||
|
fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This routine may get stubbed out in special compilations of the
|
||||||
|
* e2fsck code..
|
||||||
|
*/
|
||||||
|
#ifndef EXT2_SPECIAL_DEVICE_SIZE
|
||||||
|
errcode_t e2fsck_get_device_size(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
return (ext2fs_get_device_size(ctx->filesystem_name,
|
||||||
|
EXT2_BLOCK_SIZE(ctx->fs->super),
|
||||||
|
&ctx->num_blocks));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* helper function to release an inode
|
||||||
|
*/
|
||||||
|
struct process_block_struct {
|
||||||
|
e2fsck_t ctx;
|
||||||
|
char *buf;
|
||||||
|
struct problem_context *pctx;
|
||||||
|
int truncating;
|
||||||
|
int truncate_offset;
|
||||||
|
e2_blkcnt_t truncate_block;
|
||||||
|
int truncated_blocks;
|
||||||
|
int abort;
|
||||||
|
errcode_t errcode;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int release_inode_block(ext2_filsys fs,
|
||||||
|
blk_t *block_nr,
|
||||||
|
e2_blkcnt_t blockcnt,
|
||||||
|
blk_t ref_blk EXT2FS_ATTR((unused)),
|
||||||
|
int ref_offset EXT2FS_ATTR((unused)),
|
||||||
|
void *priv_data)
|
||||||
|
{
|
||||||
|
struct process_block_struct *pb;
|
||||||
|
e2fsck_t ctx;
|
||||||
|
struct problem_context *pctx;
|
||||||
|
blk_t blk = *block_nr;
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
pb = (struct process_block_struct *) priv_data;
|
||||||
|
ctx = pb->ctx;
|
||||||
|
pctx = pb->pctx;
|
||||||
|
|
||||||
|
pctx->blk = blk;
|
||||||
|
pctx->blkcount = blockcnt;
|
||||||
|
|
||||||
|
if (HOLE_BLKADDR(blk))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((blk < fs->super->s_first_data_block) ||
|
||||||
|
(blk >= fs->super->s_blocks_count)) {
|
||||||
|
fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_BLOCK_NUM, pctx);
|
||||||
|
return_abort:
|
||||||
|
pb->abort = 1;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
|
||||||
|
fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
|
||||||
|
goto return_abort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we are deleting an orphan, then we leave the fields alone.
|
||||||
|
* If we are truncating an orphan, then update the inode fields
|
||||||
|
* and clean up any partial block data.
|
||||||
|
*/
|
||||||
|
if (pb->truncating) {
|
||||||
|
/*
|
||||||
|
* We only remove indirect blocks if they are
|
||||||
|
* completely empty.
|
||||||
|
*/
|
||||||
|
if (blockcnt < 0) {
|
||||||
|
int i, limit;
|
||||||
|
blk_t *bp;
|
||||||
|
|
||||||
|
pb->errcode = io_channel_read_blk(fs->io, blk, 1,
|
||||||
|
pb->buf);
|
||||||
|
if (pb->errcode)
|
||||||
|
goto return_abort;
|
||||||
|
|
||||||
|
limit = fs->blocksize >> 2;
|
||||||
|
for (i = 0, bp = (blk_t *) pb->buf;
|
||||||
|
i < limit; i++, bp++)
|
||||||
|
if (*bp)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* We don't remove direct blocks until we've reached
|
||||||
|
* the truncation block.
|
||||||
|
*/
|
||||||
|
if (blockcnt >= 0 && blockcnt < pb->truncate_block)
|
||||||
|
return 0;
|
||||||
|
/*
|
||||||
|
* If part of the last block needs truncating, we do
|
||||||
|
* it here.
|
||||||
|
*/
|
||||||
|
if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
|
||||||
|
pb->errcode = io_channel_read_blk(fs->io, blk, 1,
|
||||||
|
pb->buf);
|
||||||
|
if (pb->errcode)
|
||||||
|
goto return_abort;
|
||||||
|
memset(pb->buf + pb->truncate_offset, 0,
|
||||||
|
fs->blocksize - pb->truncate_offset);
|
||||||
|
pb->errcode = io_channel_write_blk(fs->io, blk, 1,
|
||||||
|
pb->buf);
|
||||||
|
if (pb->errcode)
|
||||||
|
goto return_abort;
|
||||||
|
}
|
||||||
|
pb->truncated_blocks++;
|
||||||
|
*block_nr = 0;
|
||||||
|
retval |= BLOCK_CHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
ext2fs_block_alloc_stats(fs, blk, -1);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function releases an inode. Returns 1 if an inconsistency was
|
||||||
|
* found. If the inode has a link count, then it is being truncated and
|
||||||
|
* not deleted.
|
||||||
|
*/
|
||||||
|
static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
|
||||||
|
struct ext2_inode *inode, char *block_buf,
|
||||||
|
struct problem_context *pctx)
|
||||||
|
{
|
||||||
|
struct process_block_struct pb;
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
errcode_t retval;
|
||||||
|
__u32 count;
|
||||||
|
|
||||||
|
if (!ext2fs_inode_has_valid_blocks(inode))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pb.buf = block_buf + 3 * ctx->fs->blocksize;
|
||||||
|
pb.ctx = ctx;
|
||||||
|
pb.abort = 0;
|
||||||
|
pb.errcode = 0;
|
||||||
|
pb.pctx = pctx;
|
||||||
|
if (inode->i_links_count) {
|
||||||
|
pb.truncating = 1;
|
||||||
|
pb.truncate_block = (e2_blkcnt_t)
|
||||||
|
((((long long)inode->i_size_high << 32) +
|
||||||
|
inode->i_size + fs->blocksize - 1) /
|
||||||
|
fs->blocksize);
|
||||||
|
pb.truncate_offset = inode->i_size % fs->blocksize;
|
||||||
|
} else {
|
||||||
|
pb.truncating = 0;
|
||||||
|
pb.truncate_block = 0;
|
||||||
|
pb.truncate_offset = 0;
|
||||||
|
}
|
||||||
|
pb.truncated_blocks = 0;
|
||||||
|
retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
|
||||||
|
block_buf, release_inode_block, &pb);
|
||||||
|
if (retval) {
|
||||||
|
com_err("release_inode_blocks", retval,
|
||||||
|
_("while calling ext2fs_block_iterate for inode %d"),
|
||||||
|
ino);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (pb.abort)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Refresh the inode since ext2fs_block_iterate may have changed it */
|
||||||
|
e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
|
||||||
|
|
||||||
|
if (pb.truncated_blocks)
|
||||||
|
inode->i_blocks -= pb.truncated_blocks *
|
||||||
|
(fs->blocksize / 512);
|
||||||
|
|
||||||
|
if (inode->i_file_acl) {
|
||||||
|
retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl,
|
||||||
|
block_buf, -1, &count);
|
||||||
|
if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
|
||||||
|
retval = 0;
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
if (retval) {
|
||||||
|
com_err("release_inode_blocks", retval,
|
||||||
|
_("while calling ext2fs_adjust_ea_refocunt for inode %d"),
|
||||||
|
ino);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (count == 0)
|
||||||
|
ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
|
||||||
|
inode->i_file_acl = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function releases all of the orphan inodes. It returns 1 if
|
||||||
|
* it hit some error, and 0 on success.
|
||||||
|
*/
|
||||||
|
static int release_orphan_inodes(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
ext2_ino_t ino, next_ino;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
struct problem_context pctx;
|
||||||
|
char *block_buf;
|
||||||
|
|
||||||
|
if ((ino = fs->super->s_last_orphan) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Win or lose, we won't be using the head of the orphan inode
|
||||||
|
* list again.
|
||||||
|
*/
|
||||||
|
fs->super->s_last_orphan = 0;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the filesystem contains errors, don't run the orphan
|
||||||
|
* list, since the orphan list can't be trusted; and we're
|
||||||
|
* going to be running a full e2fsck run anyway...
|
||||||
|
*/
|
||||||
|
if (fs->super->s_state & EXT2_ERROR_FS)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((ino < EXT2_FIRST_INODE(fs->super)) ||
|
||||||
|
(ino > fs->super->s_inodes_count)) {
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
pctx.ino = ino;
|
||||||
|
fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
|
||||||
|
"block iterate buffer");
|
||||||
|
e2fsck_read_bitmaps(ctx);
|
||||||
|
|
||||||
|
while (ino) {
|
||||||
|
e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
pctx.ino = ino;
|
||||||
|
pctx.inode = &inode;
|
||||||
|
pctx.str = inode.i_links_count ? _("Truncating") :
|
||||||
|
_("Clearing");
|
||||||
|
|
||||||
|
fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
|
||||||
|
|
||||||
|
next_ino = inode.i_dtime;
|
||||||
|
if (next_ino &&
|
||||||
|
((next_ino < EXT2_FIRST_INODE(fs->super)) ||
|
||||||
|
(next_ino > fs->super->s_inodes_count))) {
|
||||||
|
pctx.ino = next_ino;
|
||||||
|
fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
|
||||||
|
goto return_abort;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
|
||||||
|
goto return_abort;
|
||||||
|
|
||||||
|
if (!inode.i_links_count) {
|
||||||
|
ext2fs_inode_alloc_stats2(fs, ino, -1,
|
||||||
|
LINUX_S_ISDIR(inode.i_mode));
|
||||||
|
inode.i_dtime = time(0);
|
||||||
|
} else {
|
||||||
|
inode.i_dtime = 0;
|
||||||
|
}
|
||||||
|
e2fsck_write_inode(ctx, ino, &inode, "delete_file");
|
||||||
|
ino = next_ino;
|
||||||
|
}
|
||||||
|
ext2fs_free_mem(&block_buf);
|
||||||
|
return 0;
|
||||||
|
return_abort:
|
||||||
|
ext2fs_free_mem(&block_buf);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check the resize inode to make sure it is sane. We check both for
|
||||||
|
* the case where on-line resizing is not enabled (in which case the
|
||||||
|
* resize inode should be cleared) as well as the case where on-line
|
||||||
|
* resizing is enabled.
|
||||||
|
*/
|
||||||
|
void check_resize_inode(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
struct ext2_inode inode;
|
||||||
|
struct problem_context pctx;
|
||||||
|
int i, j, gdt_off, ind_off;
|
||||||
|
blk_t blk, pblk, expect;
|
||||||
|
__u32 *dind_buf = 0, *ind_buf;
|
||||||
|
errcode_t retval;
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the resize inode feature isn't set, then
|
||||||
|
* s_reserved_gdt_blocks must be zero.
|
||||||
|
*/
|
||||||
|
if (!(fs->super->s_feature_compat &
|
||||||
|
EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
|
||||||
|
if (fs->super->s_reserved_gdt_blocks) {
|
||||||
|
pctx.num = fs->super->s_reserved_gdt_blocks;
|
||||||
|
if (fix_problem(ctx, PR_0_NONZERO_RESERVED_GDT_BLOCKS,
|
||||||
|
&pctx)) {
|
||||||
|
fs->super->s_reserved_gdt_blocks = 0;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the resizde inode */
|
||||||
|
pctx.ino = EXT2_RESIZE_INO;
|
||||||
|
retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
|
||||||
|
if (retval) {
|
||||||
|
if (fs->super->s_feature_compat &
|
||||||
|
EXT2_FEATURE_COMPAT_RESIZE_INODE)
|
||||||
|
ctx->flags |= E2F_FLAG_RESIZE_INODE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the resize inode feature isn't set, check to make sure
|
||||||
|
* the resize inode is cleared; then we're done.
|
||||||
|
*/
|
||||||
|
if (!(fs->super->s_feature_compat &
|
||||||
|
EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
|
||||||
|
for (i=0; i < EXT2_N_BLOCKS; i++) {
|
||||||
|
if (inode.i_block[i])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((i < EXT2_N_BLOCKS) &&
|
||||||
|
fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
|
||||||
|
memset(&inode, 0, sizeof(inode));
|
||||||
|
e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
|
||||||
|
"clear_resize");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The resize inode feature is enabled; check to make sure the
|
||||||
|
* only block in use is the double indirect block
|
||||||
|
*/
|
||||||
|
blk = inode.i_block[EXT2_DIND_BLOCK];
|
||||||
|
for (i=0; i < EXT2_N_BLOCKS; i++) {
|
||||||
|
if (i != EXT2_DIND_BLOCK && inode.i_block[i])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
|
||||||
|
!(inode.i_mode & LINUX_S_IFREG) ||
|
||||||
|
(blk < fs->super->s_first_data_block ||
|
||||||
|
blk >= fs->super->s_blocks_count)) {
|
||||||
|
resize_inode_invalid:
|
||||||
|
if (fix_problem(ctx, PR_0_RESIZE_INODE_INVALID, &pctx)) {
|
||||||
|
memset(&inode, 0, sizeof(inode));
|
||||||
|
e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
|
||||||
|
"clear_resize");
|
||||||
|
ctx->flags |= E2F_FLAG_RESIZE_INODE;
|
||||||
|
}
|
||||||
|
if (!(ctx->options & E2F_OPT_READONLY)) {
|
||||||
|
fs->super->s_state &= ~EXT2_VALID_FS;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
}
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
|
||||||
|
"resize dind buffer");
|
||||||
|
ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
|
||||||
|
|
||||||
|
retval = ext2fs_read_ind_block(fs, blk, dind_buf);
|
||||||
|
if (retval)
|
||||||
|
goto resize_inode_invalid;
|
||||||
|
|
||||||
|
gdt_off = fs->desc_blocks;
|
||||||
|
pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
|
||||||
|
for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4;
|
||||||
|
i++, gdt_off++, pblk++) {
|
||||||
|
gdt_off %= fs->blocksize/4;
|
||||||
|
if (dind_buf[gdt_off] != pblk)
|
||||||
|
goto resize_inode_invalid;
|
||||||
|
retval = ext2fs_read_ind_block(fs, pblk, ind_buf);
|
||||||
|
if (retval)
|
||||||
|
goto resize_inode_invalid;
|
||||||
|
ind_off = 0;
|
||||||
|
for (j = 1; j < fs->group_desc_count; j++) {
|
||||||
|
if (!ext2fs_bg_has_super(fs, j))
|
||||||
|
continue;
|
||||||
|
expect = pblk + (j * fs->super->s_blocks_per_group);
|
||||||
|
if (ind_buf[ind_off] != expect)
|
||||||
|
goto resize_inode_invalid;
|
||||||
|
ind_off++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (dind_buf)
|
||||||
|
ext2fs_free_mem(&dind_buf);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_super_block(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
blk_t first_block, last_block;
|
||||||
|
struct ext2_super_block *sb = fs->super;
|
||||||
|
struct ext2_group_desc *gd;
|
||||||
|
blk_t blocks_per_group = fs->super->s_blocks_per_group;
|
||||||
|
blk_t bpg_max;
|
||||||
|
int inodes_per_block;
|
||||||
|
int ipg_max;
|
||||||
|
int inode_size;
|
||||||
|
dgrp_t i;
|
||||||
|
blk_t should_be;
|
||||||
|
struct problem_context pctx;
|
||||||
|
__u32 free_blocks = 0, free_inodes = 0;
|
||||||
|
|
||||||
|
inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
|
||||||
|
ipg_max = inodes_per_block * (blocks_per_group - 4);
|
||||||
|
if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb))
|
||||||
|
ipg_max = EXT2_MAX_INODES_PER_GROUP(sb);
|
||||||
|
bpg_max = 8 * EXT2_BLOCK_SIZE(sb);
|
||||||
|
if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb))
|
||||||
|
bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb);
|
||||||
|
|
||||||
|
ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
|
||||||
|
sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap");
|
||||||
|
ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
|
||||||
|
sizeof(int) * fs->group_desc_count, "invalid_block_bitmap");
|
||||||
|
ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
|
||||||
|
sizeof(int) * fs->group_desc_count, "invalid_inode_table");
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verify the super block constants...
|
||||||
|
*/
|
||||||
|
check_super_value(ctx, "inodes_count", sb->s_inodes_count,
|
||||||
|
MIN_CHECK, 1, 0);
|
||||||
|
check_super_value(ctx, "blocks_count", sb->s_blocks_count,
|
||||||
|
MIN_CHECK, 1, 0);
|
||||||
|
check_super_value(ctx, "first_data_block", sb->s_first_data_block,
|
||||||
|
MAX_CHECK, 0, sb->s_blocks_count);
|
||||||
|
check_super_value(ctx, "log_block_size", sb->s_log_block_size,
|
||||||
|
MIN_CHECK | MAX_CHECK, 0,
|
||||||
|
EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE);
|
||||||
|
check_super_value(ctx, "log_frag_size", sb->s_log_frag_size,
|
||||||
|
MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size);
|
||||||
|
check_super_value(ctx, "frags_per_group", sb->s_frags_per_group,
|
||||||
|
MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group,
|
||||||
|
bpg_max);
|
||||||
|
check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group,
|
||||||
|
MIN_CHECK | MAX_CHECK, 8, bpg_max);
|
||||||
|
check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group,
|
||||||
|
MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
|
||||||
|
check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
|
||||||
|
MAX_CHECK, 0, sb->s_blocks_count / 2);
|
||||||
|
check_super_value(ctx, "reserved_gdt_blocks",
|
||||||
|
sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
|
||||||
|
fs->blocksize/4);
|
||||||
|
inode_size = EXT2_INODE_SIZE(sb);
|
||||||
|
check_super_value(ctx, "inode_size",
|
||||||
|
inode_size, MIN_CHECK | MAX_CHECK,
|
||||||
|
EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
|
||||||
|
if (inode_size & (inode_size - 1)) {
|
||||||
|
pctx.num = inode_size;
|
||||||
|
pctx.str = "inode_size";
|
||||||
|
fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx->num_blocks) {
|
||||||
|
pctx.errcode = e2fsck_get_device_size(ctx);
|
||||||
|
if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) {
|
||||||
|
fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) &&
|
||||||
|
(ctx->num_blocks < sb->s_blocks_count)) {
|
||||||
|
pctx.blk = sb->s_blocks_count;
|
||||||
|
pctx.blk2 = ctx->num_blocks;
|
||||||
|
if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) {
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) {
|
||||||
|
pctx.blk = EXT2_BLOCK_SIZE(sb);
|
||||||
|
pctx.blk2 = EXT2_FRAG_SIZE(sb);
|
||||||
|
fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
should_be = sb->s_frags_per_group >>
|
||||||
|
(sb->s_log_block_size - sb->s_log_frag_size);
|
||||||
|
if (sb->s_blocks_per_group != should_be) {
|
||||||
|
pctx.blk = sb->s_blocks_per_group;
|
||||||
|
pctx.blk2 = should_be;
|
||||||
|
fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
should_be = (sb->s_log_block_size == 0) ? 1 : 0;
|
||||||
|
if (sb->s_first_data_block != should_be) {
|
||||||
|
pctx.blk = sb->s_first_data_block;
|
||||||
|
pctx.blk2 = should_be;
|
||||||
|
fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
should_be = sb->s_inodes_per_group * fs->group_desc_count;
|
||||||
|
if (sb->s_inodes_count != should_be) {
|
||||||
|
pctx.ino = sb->s_inodes_count;
|
||||||
|
pctx.ino2 = should_be;
|
||||||
|
if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) {
|
||||||
|
sb->s_inodes_count = should_be;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verify the group descriptors....
|
||||||
|
*/
|
||||||
|
first_block = sb->s_first_data_block;
|
||||||
|
last_block = first_block + blocks_per_group;
|
||||||
|
|
||||||
|
for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) {
|
||||||
|
pctx.group = i;
|
||||||
|
|
||||||
|
if (i == fs->group_desc_count - 1)
|
||||||
|
last_block = sb->s_blocks_count;
|
||||||
|
if ((gd->bg_block_bitmap < first_block) ||
|
||||||
|
(gd->bg_block_bitmap >= last_block)) {
|
||||||
|
pctx.blk = gd->bg_block_bitmap;
|
||||||
|
if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx))
|
||||||
|
gd->bg_block_bitmap = 0;
|
||||||
|
}
|
||||||
|
if (gd->bg_block_bitmap == 0) {
|
||||||
|
ctx->invalid_block_bitmap_flag[i]++;
|
||||||
|
ctx->invalid_bitmaps++;
|
||||||
|
}
|
||||||
|
if ((gd->bg_inode_bitmap < first_block) ||
|
||||||
|
(gd->bg_inode_bitmap >= last_block)) {
|
||||||
|
pctx.blk = gd->bg_inode_bitmap;
|
||||||
|
if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx))
|
||||||
|
gd->bg_inode_bitmap = 0;
|
||||||
|
}
|
||||||
|
if (gd->bg_inode_bitmap == 0) {
|
||||||
|
ctx->invalid_inode_bitmap_flag[i]++;
|
||||||
|
ctx->invalid_bitmaps++;
|
||||||
|
}
|
||||||
|
if ((gd->bg_inode_table < first_block) ||
|
||||||
|
((gd->bg_inode_table +
|
||||||
|
fs->inode_blocks_per_group - 1) >= last_block)) {
|
||||||
|
pctx.blk = gd->bg_inode_table;
|
||||||
|
if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx))
|
||||||
|
gd->bg_inode_table = 0;
|
||||||
|
}
|
||||||
|
if (gd->bg_inode_table == 0) {
|
||||||
|
ctx->invalid_inode_table_flag[i]++;
|
||||||
|
ctx->invalid_bitmaps++;
|
||||||
|
}
|
||||||
|
free_blocks += gd->bg_free_blocks_count;
|
||||||
|
free_inodes += gd->bg_free_inodes_count;
|
||||||
|
first_block += sb->s_blocks_per_group;
|
||||||
|
last_block += sb->s_blocks_per_group;
|
||||||
|
|
||||||
|
if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) ||
|
||||||
|
(gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
|
||||||
|
(gd->bg_used_dirs_count > sb->s_inodes_per_group))
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the global counts from the block group counts. This
|
||||||
|
* is needed for an experimental patch which eliminates
|
||||||
|
* locking the entire filesystem when allocating blocks or
|
||||||
|
* inodes; if the filesystem is not unmounted cleanly, the
|
||||||
|
* global counts may not be accurate.
|
||||||
|
*/
|
||||||
|
if ((free_blocks != sb->s_free_blocks_count) ||
|
||||||
|
(free_inodes != sb->s_free_inodes_count)) {
|
||||||
|
if (ctx->options & E2F_OPT_READONLY)
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
else {
|
||||||
|
sb->s_free_blocks_count = free_blocks;
|
||||||
|
sb->s_free_inodes_count = free_inodes;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sb->s_free_blocks_count > sb->s_blocks_count) ||
|
||||||
|
(sb->s_free_inodes_count > sb->s_inodes_count))
|
||||||
|
ext2fs_unmark_valid(fs);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have invalid bitmaps, set the error state of the
|
||||||
|
* filesystem.
|
||||||
|
*/
|
||||||
|
if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
|
||||||
|
sb->s_state &= ~EXT2_VALID_FS;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_problem_context(&pctx);
|
||||||
|
|
||||||
|
#ifndef EXT2_SKIP_UUID
|
||||||
|
/*
|
||||||
|
* If the UUID field isn't assigned, assign it.
|
||||||
|
*/
|
||||||
|
if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
|
||||||
|
if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
|
||||||
|
uuid_generate(sb->s_uuid);
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For the Hurd, check to see if the filetype option is set,
|
||||||
|
* since it doesn't support it.
|
||||||
|
*/
|
||||||
|
if (!(ctx->options & E2F_OPT_READONLY) &&
|
||||||
|
fs->super->s_creator_os == EXT2_OS_HURD &&
|
||||||
|
(fs->super->s_feature_incompat &
|
||||||
|
EXT2_FEATURE_INCOMPAT_FILETYPE)) {
|
||||||
|
if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) {
|
||||||
|
fs->super->s_feature_incompat &=
|
||||||
|
~EXT2_FEATURE_INCOMPAT_FILETYPE;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have any of the compatibility flags set, we need to have a
|
||||||
|
* revision 1 filesystem. Most kernels will not check the flags on
|
||||||
|
* a rev 0 filesystem and we may have corruption issues because of
|
||||||
|
* the incompatible changes to the filesystem.
|
||||||
|
*/
|
||||||
|
if (!(ctx->options & E2F_OPT_READONLY) &&
|
||||||
|
fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
|
||||||
|
(fs->super->s_feature_compat ||
|
||||||
|
fs->super->s_feature_ro_compat ||
|
||||||
|
fs->super->s_feature_incompat) &&
|
||||||
|
fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) {
|
||||||
|
ext2fs_update_dynamic_rev(fs);
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
check_resize_inode(ctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clean up any orphan inodes, if present.
|
||||||
|
*/
|
||||||
|
if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) {
|
||||||
|
fs->super->s_state &= ~EXT2_VALID_FS;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Move the ext3 journal file, if necessary.
|
||||||
|
*/
|
||||||
|
e2fsck_move_ext3_journal(ctx);
|
||||||
|
return;
|
||||||
|
}
|
269
e2fsprogs/e2fsck/swapfs.c
Normal file
269
e2fsprogs/e2fsck/swapfs.c
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
* swapfs.c --- byte-swap an ext2 filesystem
|
||||||
|
*
|
||||||
|
* Copyright 1996, 1997 by Theodore Ts'o
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_ERRNO_H
|
||||||
|
#include <errno.h>
|
||||||
|
#endif
|
||||||
|
#include <et/com_err.h>
|
||||||
|
#include "e2fsck.h"
|
||||||
|
|
||||||
|
#ifdef ENABLE_SWAPFS
|
||||||
|
|
||||||
|
struct swap_block_struct {
|
||||||
|
ext2_ino_t ino;
|
||||||
|
int isdir;
|
||||||
|
errcode_t errcode;
|
||||||
|
char *dir_buf;
|
||||||
|
struct ext2_inode *inode;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a helper function for block_iterate. We mark all of the
|
||||||
|
* indirect and direct blocks as changed, so that block_iterate will
|
||||||
|
* write them out.
|
||||||
|
*/
|
||||||
|
static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
|
||||||
|
void *priv_data)
|
||||||
|
{
|
||||||
|
errcode_t retval;
|
||||||
|
|
||||||
|
struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;
|
||||||
|
|
||||||
|
if (sb->isdir && (blockcnt >= 0) && *block_nr) {
|
||||||
|
retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
|
||||||
|
if (retval) {
|
||||||
|
sb->errcode = retval;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
|
||||||
|
if (retval) {
|
||||||
|
sb->errcode = retval;
|
||||||
|
return BLOCK_ABORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (blockcnt >= 0) {
|
||||||
|
if (blockcnt < EXT2_NDIR_BLOCKS)
|
||||||
|
return 0;
|
||||||
|
return BLOCK_CHANGED;
|
||||||
|
}
|
||||||
|
if (blockcnt == BLOCK_COUNT_IND) {
|
||||||
|
if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
|
||||||
|
return 0;
|
||||||
|
return BLOCK_CHANGED;
|
||||||
|
}
|
||||||
|
if (blockcnt == BLOCK_COUNT_DIND) {
|
||||||
|
if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
|
||||||
|
return 0;
|
||||||
|
return BLOCK_CHANGED;
|
||||||
|
}
|
||||||
|
if (blockcnt == BLOCK_COUNT_TIND) {
|
||||||
|
if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
|
||||||
|
return 0;
|
||||||
|
return BLOCK_CHANGED;
|
||||||
|
}
|
||||||
|
return BLOCK_CHANGED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is responsible for byte-swapping all of the indirect,
|
||||||
|
* block pointers. It is also responsible for byte-swapping directories.
|
||||||
|
*/
|
||||||
|
static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf,
|
||||||
|
struct ext2_inode *inode)
|
||||||
|
{
|
||||||
|
errcode_t retval;
|
||||||
|
struct swap_block_struct sb;
|
||||||
|
|
||||||
|
sb.ino = ino;
|
||||||
|
sb.inode = inode;
|
||||||
|
sb.dir_buf = block_buf + ctx->fs->blocksize*3;
|
||||||
|
sb.errcode = 0;
|
||||||
|
sb.isdir = 0;
|
||||||
|
if (LINUX_S_ISDIR(inode->i_mode))
|
||||||
|
sb.isdir = 1;
|
||||||
|
|
||||||
|
retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf,
|
||||||
|
swap_block, &sb);
|
||||||
|
if (retval) {
|
||||||
|
com_err("swap_inode_blocks", retval,
|
||||||
|
_("while calling ext2fs_block_iterate"));
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sb.errcode) {
|
||||||
|
com_err("swap_inode_blocks", sb.errcode,
|
||||||
|
_("while calling iterator function"));
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void swap_inodes(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
dgrp_t group;
|
||||||
|
unsigned int i;
|
||||||
|
ext2_ino_t ino = 1;
|
||||||
|
char *buf, *block_buf;
|
||||||
|
errcode_t retval;
|
||||||
|
struct ext2_inode * inode;
|
||||||
|
|
||||||
|
e2fsck_use_inode_shortcuts(ctx, 1);
|
||||||
|
|
||||||
|
retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
|
||||||
|
&buf);
|
||||||
|
if (retval) {
|
||||||
|
com_err("swap_inodes", retval,
|
||||||
|
_("while allocating inode buffer"));
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
|
||||||
|
"block interate buffer");
|
||||||
|
for (group = 0; group < fs->group_desc_count; group++) {
|
||||||
|
retval = io_channel_read_blk(fs->io,
|
||||||
|
fs->group_desc[group].bg_inode_table,
|
||||||
|
fs->inode_blocks_per_group, buf);
|
||||||
|
if (retval) {
|
||||||
|
com_err("swap_inodes", retval,
|
||||||
|
_("while reading inode table (group %d)"),
|
||||||
|
group);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inode = (struct ext2_inode *) buf;
|
||||||
|
for (i=0; i < fs->super->s_inodes_per_group;
|
||||||
|
i++, ino++, inode++) {
|
||||||
|
ctx->stashed_ino = ino;
|
||||||
|
ctx->stashed_inode = inode;
|
||||||
|
|
||||||
|
if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
|
||||||
|
ext2fs_swap_inode(fs, inode, inode, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip deleted files.
|
||||||
|
*/
|
||||||
|
if (inode->i_links_count == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (LINUX_S_ISDIR(inode->i_mode) ||
|
||||||
|
((inode->i_block[EXT2_IND_BLOCK] ||
|
||||||
|
inode->i_block[EXT2_DIND_BLOCK] ||
|
||||||
|
inode->i_block[EXT2_TIND_BLOCK]) &&
|
||||||
|
ext2fs_inode_has_valid_blocks(inode)))
|
||||||
|
swap_inode_blocks(ctx, ino, block_buf, inode);
|
||||||
|
|
||||||
|
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
|
||||||
|
ext2fs_swap_inode(fs, inode, inode, 1);
|
||||||
|
}
|
||||||
|
retval = io_channel_write_blk(fs->io,
|
||||||
|
fs->group_desc[group].bg_inode_table,
|
||||||
|
fs->inode_blocks_per_group, buf);
|
||||||
|
if (retval) {
|
||||||
|
com_err("swap_inodes", retval,
|
||||||
|
_("while writing inode table (group %d)"),
|
||||||
|
group);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ext2fs_free_mem(&buf);
|
||||||
|
ext2fs_free_mem(&block_buf);
|
||||||
|
e2fsck_use_inode_shortcuts(ctx, 0);
|
||||||
|
ext2fs_flush_icache(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__powerpc__) && defined(EXT2FS_ENABLE_SWAPFS)
|
||||||
|
/*
|
||||||
|
* On the PowerPC, the big-endian variant of the ext2 filesystem
|
||||||
|
* has its bitmaps stored as 32-bit words with bit 0 as the LSB
|
||||||
|
* of each word. Thus a bitmap with only bit 0 set would be, as
|
||||||
|
* a string of bytes, 00 00 00 01 00 ...
|
||||||
|
* To cope with this, we byte-reverse each word of a bitmap if
|
||||||
|
* we have a big-endian filesystem, that is, if we are *not*
|
||||||
|
* byte-swapping other word-sized numbers.
|
||||||
|
*/
|
||||||
|
#define EXT2_BIG_ENDIAN_BITMAPS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef EXT2_BIG_ENDIAN_BITMAPS
|
||||||
|
static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
|
||||||
|
{
|
||||||
|
__u32 *p = (__u32 *) bmap->bitmap;
|
||||||
|
int n, nbytes = (bmap->end - bmap->start + 7) / 8;
|
||||||
|
|
||||||
|
for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
|
||||||
|
*p = ext2fs_swab32(*p);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void swap_filesys(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
struct resource_track rtrack;
|
||||||
|
|
||||||
|
init_resource_track(&rtrack);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!(ctx->options & E2F_OPT_PREEN))
|
||||||
|
printf(_("Pass 0: Doing byte-swap of filesystem\n"));
|
||||||
|
|
||||||
|
#ifdef MTRACE
|
||||||
|
mtrace_print("Byte swap");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (fs->super->s_mnt_count) {
|
||||||
|
fprintf(stderr, _("%s: the filesystem must be freshly "
|
||||||
|
"checked using fsck\n"
|
||||||
|
"and not mounted before trying to "
|
||||||
|
"byte-swap it.\n"), ctx->device_name);
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
|
||||||
|
fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
|
||||||
|
EXT2_FLAG_SWAP_BYTES_WRITE);
|
||||||
|
fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
|
||||||
|
} else {
|
||||||
|
fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
|
||||||
|
fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
|
||||||
|
}
|
||||||
|
swap_inodes(ctx);
|
||||||
|
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
|
||||||
|
return;
|
||||||
|
if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
|
||||||
|
fs->flags |= EXT2_FLAG_SWAP_BYTES;
|
||||||
|
fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
|
||||||
|
EXT2_FLAG_SWAP_BYTES_WRITE);
|
||||||
|
|
||||||
|
#ifdef EXT2_BIG_ENDIAN_BITMAPS
|
||||||
|
e2fsck_read_bitmaps(ctx);
|
||||||
|
ext2fs_swap_bitmap(fs->inode_map);
|
||||||
|
ext2fs_swap_bitmap(fs->block_map);
|
||||||
|
fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
|
||||||
|
#endif
|
||||||
|
fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
|
||||||
|
ext2fs_flush(fs);
|
||||||
|
fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
|
||||||
|
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
if (ctx->options & E2F_OPT_TIME2)
|
||||||
|
print_resource_track(_("Byte swap"), &rtrack);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
503
e2fsprogs/e2fsck/util.c
Normal file
503
e2fsprogs/e2fsck/util.c
Normal file
|
@ -0,0 +1,503 @@
|
||||||
|
/*
|
||||||
|
* util.c --- miscellaneous utilities
|
||||||
|
*
|
||||||
|
* Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
|
||||||
|
*
|
||||||
|
* %Begin-Header%
|
||||||
|
* This file may be redistributed under the terms of the GNU Public
|
||||||
|
* License.
|
||||||
|
* %End-Header%
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_CONIO_H
|
||||||
|
#undef HAVE_TERMIOS_H
|
||||||
|
#include <conio.h>
|
||||||
|
#define read_a_char() getch()
|
||||||
|
#else
|
||||||
|
#ifdef HAVE_TERMIOS_H
|
||||||
|
#include <termios.h>
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_MALLOC_H
|
||||||
|
#include <malloc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "e2fsck.h"
|
||||||
|
|
||||||
|
extern e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void fatal_error(e2fsck_t ctx, const char *msg)
|
||||||
|
{
|
||||||
|
if (msg)
|
||||||
|
fprintf (stderr, "e2fsck: %s\n", msg);
|
||||||
|
if (ctx->fs && ctx->fs->io) {
|
||||||
|
if (ctx->fs->io->magic == EXT2_ET_MAGIC_IO_CHANNEL)
|
||||||
|
io_channel_flush(ctx->fs->io);
|
||||||
|
else
|
||||||
|
fprintf(stderr, "e2fsck: io manager magic bad!\n");
|
||||||
|
}
|
||||||
|
ctx->flags |= E2F_FLAG_ABORT;
|
||||||
|
if (ctx->flags & E2F_FLAG_SETJMP_OK)
|
||||||
|
longjmp(ctx->abort_loc, 1);
|
||||||
|
exit(FSCK_ERROR);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
|
||||||
|
const char *description)
|
||||||
|
{
|
||||||
|
void *ret;
|
||||||
|
char buf[256];
|
||||||
|
|
||||||
|
#ifdef DEBUG_ALLOCATE_MEMORY
|
||||||
|
printf("Allocating %d bytes for %s...\n", size, description);
|
||||||
|
#endif
|
||||||
|
ret = malloc(size);
|
||||||
|
if (!ret) {
|
||||||
|
sprintf(buf, "Can't allocate %s\n", description);
|
||||||
|
fatal_error(ctx, buf);
|
||||||
|
}
|
||||||
|
memset(ret, 0, size);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *string_copy(e2fsck_t ctx EXT2FS_ATTR((unused)),
|
||||||
|
const char *str, int len)
|
||||||
|
{
|
||||||
|
char *ret;
|
||||||
|
|
||||||
|
if (!str)
|
||||||
|
return NULL;
|
||||||
|
if (!len)
|
||||||
|
len = strlen(str);
|
||||||
|
ret = malloc(len+1);
|
||||||
|
if (ret) {
|
||||||
|
strncpy(ret, str, len);
|
||||||
|
ret[len] = 0;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef HAVE_STRNLEN
|
||||||
|
/*
|
||||||
|
* Incredibly, libc5 doesn't appear to have strnlen. So we have to
|
||||||
|
* provide our own.
|
||||||
|
*/
|
||||||
|
int e2fsck_strnlen(const char * s, int count)
|
||||||
|
{
|
||||||
|
const char *cp = s;
|
||||||
|
|
||||||
|
while (count-- && *cp)
|
||||||
|
cp++;
|
||||||
|
return cp - s;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HAVE_CONIO_H
|
||||||
|
static int read_a_char(void)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
int r;
|
||||||
|
int fail = 0;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
if (e2fsck_global_ctx &&
|
||||||
|
(e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
r = read(0, &c, 1);
|
||||||
|
if (r == 1)
|
||||||
|
return c;
|
||||||
|
if (fail++ > 100)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return EOF;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ask_yn(const char * string, int def)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
const char *defstr;
|
||||||
|
const char *short_yes = _("yY");
|
||||||
|
const char *short_no = _("nN");
|
||||||
|
|
||||||
|
#ifdef HAVE_TERMIOS_H
|
||||||
|
struct termios termios, tmp;
|
||||||
|
|
||||||
|
tcgetattr (0, &termios);
|
||||||
|
tmp = termios;
|
||||||
|
tmp.c_lflag &= ~(ICANON | ECHO);
|
||||||
|
tmp.c_cc[VMIN] = 1;
|
||||||
|
tmp.c_cc[VTIME] = 0;
|
||||||
|
tcsetattr (0, TCSANOW, &tmp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (def == 1)
|
||||||
|
defstr = _(_("<y>"));
|
||||||
|
else if (def == 0)
|
||||||
|
defstr = _(_("<n>"));
|
||||||
|
else
|
||||||
|
defstr = _(" (y/n)");
|
||||||
|
printf("%s%s? ", string, defstr);
|
||||||
|
while (1) {
|
||||||
|
fflush (stdout);
|
||||||
|
if ((c = read_a_char()) == EOF)
|
||||||
|
break;
|
||||||
|
if (c == 3) {
|
||||||
|
#ifdef HAVE_TERMIOS_H
|
||||||
|
tcsetattr (0, TCSANOW, &termios);
|
||||||
|
#endif
|
||||||
|
if (e2fsck_global_ctx &&
|
||||||
|
e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
|
||||||
|
puts("\n");
|
||||||
|
longjmp(e2fsck_global_ctx->abort_loc, 1);
|
||||||
|
}
|
||||||
|
puts(_("cancelled!\n"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (strchr(short_yes, (char) c)) {
|
||||||
|
def = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (strchr(short_no, (char) c)) {
|
||||||
|
def = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if ((c == ' ' || c == '\n') && (def != -1))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (def)
|
||||||
|
puts(_("yes\n"));
|
||||||
|
else
|
||||||
|
puts (_("no\n"));
|
||||||
|
#ifdef HAVE_TERMIOS_H
|
||||||
|
tcsetattr (0, TCSANOW, &termios);
|
||||||
|
#endif
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ask (e2fsck_t ctx, const char * string, int def)
|
||||||
|
{
|
||||||
|
if (ctx->options & E2F_OPT_NO) {
|
||||||
|
printf (_("%s? no\n\n"), string);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ctx->options & E2F_OPT_YES) {
|
||||||
|
printf (_("%s? yes\n\n"), string);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (ctx->options & E2F_OPT_PREEN) {
|
||||||
|
printf ("%s? %s\n\n", string, def ? _("yes") : _("no"));
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
return ask_yn(string, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2fsck_read_bitmaps(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
errcode_t retval;
|
||||||
|
|
||||||
|
if (ctx->invalid_bitmaps) {
|
||||||
|
com_err(ctx->program_name, 0,
|
||||||
|
_("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
|
||||||
|
ctx->device_name);
|
||||||
|
fatal_error(ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ehandler_operation(_("reading inode and block bitmaps"));
|
||||||
|
retval = ext2fs_read_bitmaps(fs);
|
||||||
|
ehandler_operation(0);
|
||||||
|
if (retval) {
|
||||||
|
com_err(ctx->program_name, retval,
|
||||||
|
_("while retrying to read bitmaps for %s"),
|
||||||
|
ctx->device_name);
|
||||||
|
fatal_error(ctx, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void e2fsck_write_bitmaps(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
errcode_t retval;
|
||||||
|
|
||||||
|
if (ext2fs_test_bb_dirty(fs)) {
|
||||||
|
ehandler_operation(_("writing block bitmaps"));
|
||||||
|
retval = ext2fs_write_block_bitmap(fs);
|
||||||
|
ehandler_operation(0);
|
||||||
|
if (retval) {
|
||||||
|
com_err(ctx->program_name, retval,
|
||||||
|
_("while retrying to write block bitmaps for %s"),
|
||||||
|
ctx->device_name);
|
||||||
|
fatal_error(ctx, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ext2fs_test_ib_dirty(fs)) {
|
||||||
|
ehandler_operation(_("writing inode bitmaps"));
|
||||||
|
retval = ext2fs_write_inode_bitmap(fs);
|
||||||
|
ehandler_operation(0);
|
||||||
|
if (retval) {
|
||||||
|
com_err(ctx->program_name, retval,
|
||||||
|
_("while retrying to write inode bitmaps for %s"),
|
||||||
|
ctx->device_name);
|
||||||
|
fatal_error(ctx, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void preenhalt(e2fsck_t ctx)
|
||||||
|
{
|
||||||
|
ext2_filsys fs = ctx->fs;
|
||||||
|
|
||||||
|
if (!(ctx->options & E2F_OPT_PREEN))
|
||||||
|
return;
|
||||||
|
fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
|
||||||
|
"RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
|
||||||
|
ctx->device_name);
|
||||||
|
if (fs != NULL) {
|
||||||
|
fs->super->s_state |= EXT2_ERROR_FS;
|
||||||
|
ext2fs_mark_super_dirty(fs);
|
||||||
|
ext2fs_close(fs);
|
||||||
|
}
|
||||||
|
exit(FSCK_UNCORRECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef RESOURCE_TRACK
|
||||||
|
void init_resource_track(struct resource_track *track)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_GETRUSAGE
|
||||||
|
struct rusage r;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
track->brk_start = sbrk(0);
|
||||||
|
gettimeofday(&track->time_start, 0);
|
||||||
|
#ifdef HAVE_GETRUSAGE
|
||||||
|
#ifdef sun
|
||||||
|
memset(&r, 0, sizeof(struct rusage));
|
||||||
|
#endif
|
||||||
|
getrusage(RUSAGE_SELF, &r);
|
||||||
|
track->user_start = r.ru_utime;
|
||||||
|
track->system_start = r.ru_stime;
|
||||||
|
#else
|
||||||
|
track->user_start.tv_sec = track->user_start.tv_usec = 0;
|
||||||
|
track->system_start.tv_sec = track->system_start.tv_usec = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define _INLINE_ __inline__
|
||||||
|
#else
|
||||||
|
#define _INLINE_
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static _INLINE_ float timeval_subtract(struct timeval *tv1,
|
||||||
|
struct timeval *tv2)
|
||||||
|
{
|
||||||
|
return ((tv1->tv_sec - tv2->tv_sec) +
|
||||||
|
((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_resource_track(const char *desc, struct resource_track *track)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_GETRUSAGE
|
||||||
|
struct rusage r;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_MALLINFO
|
||||||
|
struct mallinfo malloc_info;
|
||||||
|
#endif
|
||||||
|
struct timeval time_end;
|
||||||
|
|
||||||
|
gettimeofday(&time_end, 0);
|
||||||
|
|
||||||
|
if (desc)
|
||||||
|
printf("%s: ", desc);
|
||||||
|
|
||||||
|
#ifdef HAVE_MALLINFO
|
||||||
|
#define kbytes(x) (((x) + 1023) / 1024)
|
||||||
|
|
||||||
|
malloc_info = mallinfo();
|
||||||
|
printf(_("Memory used: %dk/%dk (%dk/%dk), "),
|
||||||
|
kbytes(malloc_info.arena), kbytes(malloc_info.hblkhd),
|
||||||
|
kbytes(malloc_info.uordblks), kbytes(malloc_info.fordblks));
|
||||||
|
#else
|
||||||
|
printf(_("Memory used: %d, "),
|
||||||
|
(int) (((char *) sbrk(0)) - ((char *) track->brk_start)));
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_GETRUSAGE
|
||||||
|
getrusage(RUSAGE_SELF, &r);
|
||||||
|
|
||||||
|
printf(_("time: %5.2f/%5.2f/%5.2f\n"),
|
||||||
|
timeval_subtract(&time_end, &track->time_start),
|
||||||
|
timeval_subtract(&r.ru_utime, &track->user_start),
|
||||||
|
timeval_subtract(&r.ru_stime, &track->system_start));
|
||||||
|
#else
|
||||||
|
printf(_("elapsed time: %6.3f\n"),
|
||||||
|
timeval_subtract(&time_end, &track->time_start));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif /* RESOURCE_TRACK */
|
||||||
|
|
||||||
|
void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
|
||||||
|
struct ext2_inode * inode, const char *proc)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = ext2fs_read_inode(ctx->fs, ino, inode);
|
||||||
|
if (retval) {
|
||||||
|
com_err("ext2fs_read_inode", retval,
|
||||||
|
_("while reading inode %ld in %s"), ino, proc);
|
||||||
|
fatal_error(ctx, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
|
||||||
|
struct ext2_inode * inode, int bufsize,
|
||||||
|
const char *proc)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
|
||||||
|
if (retval) {
|
||||||
|
com_err("ext2fs_write_inode", retval,
|
||||||
|
_("while writing inode %ld in %s"), ino, proc);
|
||||||
|
fatal_error(ctx, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
|
||||||
|
struct ext2_inode * inode, const char *proc)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = ext2fs_write_inode(ctx->fs, ino, inode);
|
||||||
|
if (retval) {
|
||||||
|
com_err("ext2fs_write_inode", retval,
|
||||||
|
_("while writing inode %ld in %s"), ino, proc);
|
||||||
|
fatal_error(ctx, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MTRACE
|
||||||
|
void mtrace_print(char *mesg)
|
||||||
|
{
|
||||||
|
FILE *malloc_get_mallstream();
|
||||||
|
FILE *f = malloc_get_mallstream();
|
||||||
|
|
||||||
|
if (f)
|
||||||
|
fprintf(f, "============= %s\n", mesg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
|
||||||
|
io_manager manager)
|
||||||
|
{
|
||||||
|
struct ext2_super_block *sb;
|
||||||
|
io_channel io = NULL;
|
||||||
|
void *buf = NULL;
|
||||||
|
int blocksize;
|
||||||
|
blk_t superblock, ret_sb = 8193;
|
||||||
|
|
||||||
|
if (fs && fs->super) {
|
||||||
|
ret_sb = (fs->super->s_blocks_per_group +
|
||||||
|
fs->super->s_first_data_block);
|
||||||
|
if (ctx) {
|
||||||
|
ctx->superblock = ret_sb;
|
||||||
|
ctx->blocksize = fs->blocksize;
|
||||||
|
}
|
||||||
|
return ret_sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx) {
|
||||||
|
if (ctx->blocksize) {
|
||||||
|
ret_sb = ctx->blocksize * 8;
|
||||||
|
if (ctx->blocksize == 1024)
|
||||||
|
ret_sb++;
|
||||||
|
ctx->superblock = ret_sb;
|
||||||
|
return ret_sb;
|
||||||
|
}
|
||||||
|
ctx->superblock = ret_sb;
|
||||||
|
ctx->blocksize = 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name || !manager)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (manager->open(name, 0, &io) != 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
|
||||||
|
goto cleanup;
|
||||||
|
sb = (struct ext2_super_block *) buf;
|
||||||
|
|
||||||
|
for (blocksize = EXT2_MIN_BLOCK_SIZE;
|
||||||
|
blocksize <= EXT2_MAX_BLOCK_SIZE ; blocksize *= 2) {
|
||||||
|
superblock = blocksize*8;
|
||||||
|
if (blocksize == 1024)
|
||||||
|
superblock++;
|
||||||
|
io_channel_set_blksize(io, blocksize);
|
||||||
|
if (io_channel_read_blk(io, superblock,
|
||||||
|
-SUPERBLOCK_SIZE, buf))
|
||||||
|
continue;
|
||||||
|
#ifdef EXT2FS_ENABLE_SWAPFS
|
||||||
|
if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
|
||||||
|
ext2fs_swap_super(sb);
|
||||||
|
#endif
|
||||||
|
if (sb->s_magic == EXT2_SUPER_MAGIC) {
|
||||||
|
ret_sb = superblock;
|
||||||
|
if (ctx) {
|
||||||
|
ctx->superblock = superblock;
|
||||||
|
ctx->blocksize = blocksize;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (io)
|
||||||
|
io_channel_close(io);
|
||||||
|
if (buf)
|
||||||
|
ext2fs_free_mem(&buf);
|
||||||
|
return (ret_sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given a mode, return the ext2 file type
|
||||||
|
*/
|
||||||
|
int ext2_file_type(unsigned int mode)
|
||||||
|
{
|
||||||
|
if (LINUX_S_ISREG(mode))
|
||||||
|
return EXT2_FT_REG_FILE;
|
||||||
|
|
||||||
|
if (LINUX_S_ISDIR(mode))
|
||||||
|
return EXT2_FT_DIR;
|
||||||
|
|
||||||
|
if (LINUX_S_ISCHR(mode))
|
||||||
|
return EXT2_FT_CHRDEV;
|
||||||
|
|
||||||
|
if (LINUX_S_ISBLK(mode))
|
||||||
|
return EXT2_FT_BLKDEV;
|
||||||
|
|
||||||
|
if (LINUX_S_ISLNK(mode))
|
||||||
|
return EXT2_FT_SYMLINK;
|
||||||
|
|
||||||
|
if (LINUX_S_ISFIFO(mode))
|
||||||
|
return EXT2_FT_FIFO;
|
||||||
|
|
||||||
|
if (LINUX_S_ISSOCK(mode))
|
||||||
|
return EXT2_FT_SOCK;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
1302
e2fsprogs/fsck.c
Normal file
1302
e2fsprogs/fsck.c
Normal file
File diff suppressed because it is too large
Load diff
59
e2fsprogs/fsck.h
Normal file
59
e2fsprogs/fsck.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* fsck.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define FSCK_ATTR(x) __attribute__(x)
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DEFAULT_FSTYPE
|
||||||
|
#define DEFAULT_FSTYPE "ext2"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAX_DEVICES 32
|
||||||
|
#define MAX_ARGS 32
|
||||||
|
|
||||||
|
#define EXIT_OK 0
|
||||||
|
#define EXIT_NONDESTRUCT 1
|
||||||
|
#define EXIT_DESTRUCT 2
|
||||||
|
#define EXIT_UNCORRECTED 4
|
||||||
|
#define EXIT_ERROR 8
|
||||||
|
#define EXIT_USAGE 16
|
||||||
|
#define EXIT_LIBRARY 128
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal structure for mount tabel entries.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct fs_info {
|
||||||
|
char *device;
|
||||||
|
char *mountpt;
|
||||||
|
char *type;
|
||||||
|
char *opts;
|
||||||
|
int freq;
|
||||||
|
int passno;
|
||||||
|
int flags;
|
||||||
|
struct fs_info *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FLAG_DONE 1
|
||||||
|
#define FLAG_PROGRESS 2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure to allow exit codes to be stored
|
||||||
|
*/
|
||||||
|
struct fsck_instance {
|
||||||
|
int pid;
|
||||||
|
int flags;
|
||||||
|
int exit_status;
|
||||||
|
time_t start_time;
|
||||||
|
char * prog;
|
||||||
|
char * type;
|
||||||
|
char * device;
|
||||||
|
char * base_device;
|
||||||
|
struct fsck_instance *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern char *base_device(const char *device);
|
||||||
|
extern const char *identify_fs(const char *fs_name, const char *fs_types);
|
|
@ -182,6 +182,9 @@
|
||||||
#ifdef CONFIG_DUMPLEASES
|
#ifdef CONFIG_DUMPLEASES
|
||||||
APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
|
APPLET(dumpleases, dumpleases_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER)
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_E2FSCK
|
||||||
|
APPLET(e2fsck, e2fsck_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_E2LABEL
|
#ifdef CONFIG_E2LABEL
|
||||||
APPLET_NOUSAGE("e2label", tune2fs_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
|
APPLET_NOUSAGE("e2label", tune2fs_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
|
||||||
#endif
|
#endif
|
||||||
|
@ -239,6 +242,13 @@
|
||||||
#ifdef CONFIG_FREERAMDISK
|
#ifdef CONFIG_FREERAMDISK
|
||||||
APPLET(freeramdisk, freeramdisk_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
|
APPLET(freeramdisk, freeramdisk_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_FSCK
|
||||||
|
APPLET(fsck, fsck_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_E2FSCK
|
||||||
|
APPLET_NOUSAGE("fsck.ext2", e2fsck_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
|
||||||
|
APPLET_NOUSAGE("fsck.ext3", e2fsck_main, _BB_DIR_SBIN, _BB_SUID_NEVER)
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_FSCK_MINIX
|
#ifdef CONFIG_FSCK_MINIX
|
||||||
APPLET_ODDNAME("fsck.minix", fsck_minix_main, _BB_DIR_SBIN, _BB_SUID_NEVER, fsck_minix)
|
APPLET_ODDNAME("fsck.minix", fsck_minix_main, _BB_DIR_SBIN, _BB_SUID_NEVER, fsck_minix)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -546,6 +546,26 @@
|
||||||
"\t-r,\t--remaining\tInterpret lease times as time remaing\n" \
|
"\t-r,\t--remaining\tInterpret lease times as time remaing\n" \
|
||||||
"\t-a,\t--absolute\tInterpret lease times as expire time"
|
"\t-a,\t--absolute\tInterpret lease times as expire time"
|
||||||
|
|
||||||
|
#define e2fsck_trivial_usage \
|
||||||
|
"[-panyrcdfvstDFSV] [-b superblock] [-B blocksize] " \
|
||||||
|
"[-I inode_buffer_blocks] [-P process_inode_size] " \
|
||||||
|
"[-l|-L bad_blocks_file] [-C fd] [-j ext-journal] " \
|
||||||
|
"[-E extended-options] device"
|
||||||
|
#define e2fsck_full_usage \
|
||||||
|
"Check a Linux ext2/ext3 file system.\n\n" \
|
||||||
|
"Options:\n" \
|
||||||
|
"\t-p\tAutomatic repair (no questions)\n" \
|
||||||
|
"\t-n\tMake no changes to the filesystem\n" \
|
||||||
|
"\t-y\tAssume 'yes' to all questions\n" \
|
||||||
|
"\t-c\tCheck for bad blocks and add them to the badblock list\n" \
|
||||||
|
"\t-f\tForce checking even if filesystem is marked clean\n" \
|
||||||
|
"\t-v\tBe verbose\n" \
|
||||||
|
"\t-b superblock\tUse alternative superblock\n" \
|
||||||
|
"\t-B blocksize\tForce blocksize when looking for superblock\n" \
|
||||||
|
"\t-j journal\tSet location of the external journal\n" \
|
||||||
|
"\t-l file\tAdd to badblocks list\n" \
|
||||||
|
"\t-L file\tSet badblocks list"
|
||||||
|
|
||||||
#ifdef CONFIG_FEATURE_FANCY_ECHO
|
#ifdef CONFIG_FEATURE_FANCY_ECHO
|
||||||
# define USAGE_FANCY_ECHO(a) a
|
# define USAGE_FANCY_ECHO(a) a
|
||||||
#else
|
#else
|
||||||
|
@ -764,6 +784,20 @@
|
||||||
#define freeramdisk_example_usage \
|
#define freeramdisk_example_usage \
|
||||||
"$ freeramdisk /dev/ram2\n"
|
"$ freeramdisk /dev/ram2\n"
|
||||||
|
|
||||||
|
#define fsck_trivial_usage \
|
||||||
|
"[-ANPRTV] [ -C [ fd ] ] [-t fstype] [fs-options] [filesys ...]"
|
||||||
|
#define fsck_full_usage \
|
||||||
|
"Check and repair filesystems.\n\n" \
|
||||||
|
"Options:\n" \
|
||||||
|
"\t-A\tWalk /etc/fstab and check all filesystems\n" \
|
||||||
|
"\t-N\tDon't execute, just show what would be done\n" \
|
||||||
|
"\t-P\tWhen using -A, check filesystems in parallel\n" \
|
||||||
|
"\t-R\tWhen using -A, skip the root filesystem\n" \
|
||||||
|
"\t-T\tDon't show title on startup\n" \
|
||||||
|
"\t-V\tVerbose mode\n" \
|
||||||
|
"\t-C\tWrite status information to specified filedescriptor\n" \
|
||||||
|
"\t-t\tList of filesystem types to check"
|
||||||
|
|
||||||
#define fsck_minix_trivial_usage \
|
#define fsck_minix_trivial_usage \
|
||||||
"[-larvsmf] /dev/name"
|
"[-larvsmf] /dev/name"
|
||||||
#define fsck_minix_full_usage \
|
#define fsck_minix_full_usage \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue