blob: fb1e772a45a79e440058371924a6a5e9490265f2 [file] [log] [blame]
/*
* e2fsck
*
* Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
* This file may be
* redistributed under the terms of the GNU Public License.
*
*
* 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.
*
* linux/fs/recovery and linux/fs/revoke
* 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 _GNU_SOURCE
#define _GNU_SOURCE 1 /* get strnlen() */
#endif
#include <sys/types.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <ctype.h>
#include <setjmp.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <stddef.h>
#include <assert.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/ioctl.h>
#include <malloc.h>
#include <termios.h>
#include <mntent.h>
#include <dirent.h>
#include "fsck.h"
#include "ext2fs/ext2_fs.h"
#include "ext2fs/ext2fs.h"
#include "blkid/blkid.h"
#include "ext2fs/ext2_ext_attr.h"
#include "uuid/uuid.h"
#ifdef __GNUC__
#define _INLINE_ __inline__
#define EXT2FS_ATTR(x) __attribute__(x)
#else
#define _INLINE_
#define EXT2FS_ATTR(x)
#endif
/*
* 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
/*
* This is the global e2fsck structure.
*/
typedef struct e2fsck_struct *e2fsck_t;
/*
* Define the extended attribute refcount structure
*/
typedef struct ea_refcount *ext2_refcount_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 */
jmp_buf abort_loc;
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;
/*
* Procedure declarations
*/
static void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf);
/* pass1.c */
static void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool);
/* pass2.c */
static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
ext2_ino_t ino, char *buf);
/* pass3.c */
static int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode);
static errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
int num, int gauranteed_size);
static ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix);
static errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino,
int adj);
/* rehash.c */
static void e2fsck_rehash_directories(e2fsck_t ctx);
/* util.c */
static void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
const char *description);
static int ask(e2fsck_t ctx, const char * string, int def);
static void e2fsck_read_bitmaps(e2fsck_t ctx);
static void preenhalt(e2fsck_t ctx);
#ifdef RESOURCE_TRACK
static void print_resource_track(const char *desc,
struct resource_track *track);
static void init_resource_track(struct resource_track *track);
#endif
static void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
struct ext2_inode * inode, const char * proc);
static void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
struct ext2_inode * inode, const char * proc);
#ifdef MTRACE
static void mtrace_print(char *mesg);
#endif
static blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
const char *name, io_manager manager);
/* unix.c */
static void e2fsck_clear_progbar(e2fsck_t ctx);
static int e2fsck_simple_progress(e2fsck_t ctx, const char *label,
float percent, unsigned int dpynum);
/*
* problem.h --- e2fsck problem error codes
*/
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
*/
static int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
static int end_problem_latch(e2fsck_t ctx, int mask);
static int set_latch_flags(int mask, int setflags, int clearflags);
static void clear_problem_context(struct problem_context *ctx);
/*
* Dictionary Abstract Data Type
* Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
*
* dict.h v 1.22.2.6 2000/11/13 01:36:44 kaz
* kazlib_1_20
*/
#ifndef DICT_H
#define DICT_H
/*
* Blurb for inclusion into C++ translation units
*/
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 {
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;
} dnode_t;
typedef int (*dict_comp_t)(const void *, const void *);
typedef void (*dnode_free_t)(dnode_t *);
typedef struct dict_t {
dnode_t dict_nilnode;
dictcount_t dict_nodecount;
dictcount_t dict_maxcount;
dict_comp_t dict_compare;
dnode_free_t dict_freenode;
int dict_dupes;
} dict_t;
typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
typedef struct dict_load_t {
dict_t *dict_dictptr;
dnode_t dict_nilnode;
} dict_load_t;
#define dict_count(D) ((D)->dict_nodecount)
#define dnode_get(N) ((N)->dict_data)
#define dnode_getkey(N) ((N)->dict_key)
#endif
/*
* Compatibility header file for e2fsck which should be included
* instead of linux/jfs.h
*
* Copyright (C) 2000 Stephen C. Tweedie
*/
/*
* Pull in the definition of the e2fsck context structure
*/
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)
static 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)
/*
* We use the standard libext2fs portability tricks for inline
* functions.
*/
static _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;
}
static _INLINE_ void do_cache_destroy(kmem_cache_t *cache)
{
free(cache);
}
/*
* Now pull in the real linux/jfs.h definitions.
*/
#include "ext2fs/kernel-jbd.h"
/*
* badblocks.c --- replace/append bad blocks to the bad block inode
*/
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 FSCK_ATTR((unused)), blk_t blk)
{
printf(_("Bad block %u out of range; ignored.\n"), blk);
return;
}
static 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 FSCK_ATTR((unused)),
void *priv_data FSCK_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;
}
/*
* Dictionary Abstract Data Type
*/
/*
* These macros provide short convenient names for structure members,
* which are embellished with dict_ prefixes so that they are
* properly confined to the documented namespace. It's legal for a
* program which uses dict to define, for instance, a macro called ``parent''.
* Such a macro would interfere with the dnode_t struct definition.
* In general, highly portable and reusable C modules which expose their
* structures need to confine structure member names to well-defined spaces.
* The resulting identifiers aren't necessarily convenient to use, nor
* readable, in the implementation, however!
*/
#define left dict_left
#define right dict_right
#define parent dict_parent
#define color dict_color
#define key dict_key
#define data dict_data
#define nilnode dict_nilnode
#define maxcount dict_maxcount
#define compare dict_compare
#define dupes dict_dupes
#define dict_root(D) ((D)->nilnode.left)
#define dict_nil(D) (&(D)->nilnode)
#define DICT_DEPTH_MAX 64
static void dnode_free(dnode_t *node);
/*
* Perform a ``left rotation'' adjustment on the tree. The given node P and
* its right child C are rearranged so that the P instead becomes the left
* child of C. The left subtree of C is inherited as the new right subtree
* for P. The ordering of the keys within the tree is thus preserved.
*/
static void rotate_left(dnode_t *upper)
{
dnode_t *lower, *lowleft, *upparent;
lower = upper->right;
upper->right = lowleft = lower->left;
lowleft->parent = upper;
lower->parent = upparent = upper->parent;
/* don't need to check for root node here because root->parent is
the sentinel nil node, and root->parent->left points back to root */
if (upper == upparent->left) {
upparent->left = lower;
} else {
assert (upper == upparent->right);
upparent->right = lower;
}
lower->left = upper;
upper->parent = lower;
}
/*
* This operation is the ``mirror'' image of rotate_left. It is
* the same procedure, but with left and right interchanged.
*/
static void rotate_right(dnode_t *upper)
{
dnode_t *lower, *lowright, *upparent;
lower = upper->left;
upper->left = lowright = lower->right;
lowright->parent = upper;
lower->parent = upparent = upper->parent;
if (upper == upparent->right) {
upparent->right = lower;
} else {
assert (upper == upparent->left);
upparent->left = lower;
}
lower->right = upper;
upper->parent = lower;
}
/*
* Do a postorder traversal of the tree rooted at the specified
* node and free everything under it. Used by dict_free().
*/
static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
{
if (node == nil)
return;
free_nodes(dict, node->left, nil);
free_nodes(dict, node->right, nil);
dict->dict_freenode(node);
}
/*
* Verify that the tree contains the given node. This is done by
* traversing all of the nodes and comparing their pointers to the
* given pointer. Returns 1 if the node is found, otherwise
* returns zero. It is intended for debugging purposes.
*/
static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
{
if (root != nil) {
return root == node
|| verify_dict_has_node(nil, root->left, node)
|| verify_dict_has_node(nil, root->right, node);
}
return 0;
}
/*
* Select a different set of node allocator routines.
*/
static void dict_set_allocator(dict_t *dict, dnode_free_t fr)
{
assert (dict_count(dict) == 0);
dict->dict_freenode = fr;
}
/*
* Free all the nodes in the dictionary by using the dictionary's
* installed free routine. The dictionary is emptied.
*/
static void dict_free_nodes(dict_t *dict)
{
dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
free_nodes(dict, root, nil);
dict->dict_nodecount = 0;
dict->nilnode.left = &dict->nilnode;
dict->nilnode.right = &dict->nilnode;
}
/*
* Initialize a user-supplied dictionary object.
*/
static dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
{
dict->compare = comp;
dict->dict_freenode = dnode_free;
dict->dict_nodecount = 0;
dict->maxcount = maxcount;
dict->nilnode.left = &dict->nilnode;
dict->nilnode.right = &dict->nilnode;
dict->nilnode.parent = &dict->nilnode;
dict->nilnode.color = dnode_black;
dict->dupes = 0;
return dict;
}
/*
* Locate a node in the dictionary having the given key.
* If the node is not found, a null a pointer is returned (rather than
* a pointer that dictionary's nil sentinel node), otherwise a pointer to the
* located node is returned.
*/
static dnode_t *dict_lookup(dict_t *dict, const void *key)
{
dnode_t *root = dict_root(dict);
dnode_t *nil = dict_nil(dict);
dnode_t *saved;
int result;
/* simple binary search adapted for trees that contain duplicate keys */
while (root != nil) {
result = dict->compare(key, root->key);
if (result < 0)
root = root->left;
else if (result > 0)
root = root->right;
else {
if (!dict->dupes) { /* no duplicates, return match */
return root;
} else { /* could be dupes, find leftmost one */
do {
saved = root;
root = root->left;
while (root != nil && dict->compare(key, root->key))
root = root->right;
} while (root != nil);
return saved;
}
}
}
return NULL;
}
/*
* Insert a node into the dictionary. The node should have been
* initialized with a data field. All other fields are ignored.
* The behavior is undefined if the user attempts to insert into
* a dictionary that is already full (for which the dict_isfull()
* function returns true).
*/
static void dict_insert(dict_t *dict, dnode_t *node, const void *key)
{
dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
dnode_t *parent = nil, *uncle, *grandpa;
int result = -1;
node->key = key;
/* basic binary tree insert */
while (where != nil) {
parent = where;
result = dict->compare(key, where->key);
/* trap attempts at duplicate key insertion unless it's explicitly allowed */
assert (dict->dupes || result != 0);
if (result < 0)
where = where->left;
else
where = where->right;
}
assert (where == nil);
if (result < 0)
parent->left = node;
else
parent->right = node;
node->parent = parent;
node->left = nil;
node->right = nil;
dict->dict_nodecount++;
/* red black adjustments */
node->color = dnode_red;
while (parent->color == dnode_red) {
grandpa = parent->parent;
if (parent == grandpa->left) {
uncle = grandpa->right;
if (uncle->color == dnode_red) { /* red parent, red uncle */
parent->color = dnode_black;
uncle->color = dnode_black;
grandpa->color = dnode_red;
node = grandpa;
parent = grandpa->parent;
} else { /* red parent, black uncle */
if (node == parent->right) {
rotate_left(parent);
parent = node;
assert (grandpa == parent->parent);
/* rotation between parent and child preserves grandpa */
}
parent->color = dnode_black;
grandpa->color = dnode_red;
rotate_right(grandpa);
break;
}
} else { /* symmetric cases: parent == parent->parent->right */
uncle = grandpa->left;
if (uncle->color == dnode_red) {
parent->color = dnode_black;
uncle->color = dnode_black;
grandpa->color = dnode_red;
node = grandpa;
parent = grandpa->parent;
} else {
if (node == parent->left) {
rotate_right(parent);
parent = node;
assert (grandpa == parent->parent);
}
parent->color = dnode_black;
grandpa->color = dnode_red;
rotate_left(grandpa);
break;
}
}
}
dict_root(dict)->color = dnode_black;
}
/*
* Allocate a node using the dictionary's allocator routine, give it
* the data item.
*/
static dnode_t *dnode_init(dnode_t *dnode, void *data)
{
dnode->data = data;
dnode->parent = NULL;
dnode->left = NULL;
dnode->right = NULL;
return dnode;
}
static int dict_alloc_insert(dict_t *dict, const void *key, void *data)
{
dnode_t *node = malloc(sizeof(dnode_t));
if (node) {
dnode_init(node, data);
dict_insert(dict, node, key);
return 1;
}
return 0;
}
/*
* Return the node with the lowest (leftmost) key. If the dictionary is empty
* (that is, dict_isempty(dict) returns 1) a null pointer is returned.
*/
static dnode_t *dict_first(dict_t *dict)
{
dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
if (root != nil)
while ((left = root->left) != nil)
root = left;
return (root == nil) ? NULL : root;
}
/*
* Return the given node's successor node---the node which has the
* next key in the the left to right ordering. If the node has
* no successor, a null pointer is returned rather than a pointer to
* the nil node.
*/
static dnode_t *dict_next(dict_t *dict, dnode_t *curr)
{
dnode_t *nil = dict_nil(dict), *parent, *left;
if (curr->right != nil) {
curr = curr->right;
while ((left = curr->left) != nil)
curr = left;
return curr;
}
parent = curr->parent;
while (parent != nil && curr == parent->right) {
curr = parent;
parent = curr->parent;
}
return (parent == nil) ? NULL : parent;
}
static void dnode_free(dnode_t *node)
{
free(node);
}
#undef left
#undef right
#undef parent
#undef color
#undef key
#undef data
#undef nilnode
#undef maxcount
#undef compare
#undef dupes
/*
* dirinfo.c --- maintains the directory information table for e2fsck.
*/
/*
* 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.
*/
static 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.
*/
static 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.
*/
static void e2fsck_free_dir_info(e2fsck_t ctx)
{
ext2fs_free_mem(&ctx->dir_info);
ctx->dir_info_size = 0;
ctx->dir_info_count = 0;
}
/*
* Return the count of number of directories in the dir_info structure
*/
static inline int e2fsck_get_num_dirinfo(e2fsck_t ctx)
{
return ctx->dir_info_count;
}
/*
* A simple interator function
*/
static 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)++);
}
/*
* dirinfo.c --- maintains the directory information table for e2fsck.
*
*/
#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.
*/
static 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.
*/
static 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.
*/
static 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++) {
ext2fs_free_mem(&dir->dx_block);
}
ext2fs_free_mem(&ctx->dx_dir_info);
}
ctx->dx_dir_info_size = 0;
ctx->dx_dir_info_count = 0;
}
/*
* A simple interator function
*/
static 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 */
/*
* e2fsck.c - a consistency checker for the new extended file system.
*
*/
/*
* This function allocates an e2fsck context
*/
static 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;
}
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;
};
static void ea_refcount_free(ext2_refcount_t refcount)
{
if (!refcount)
return;
ext2fs_free_mem(&refcount->list);
ext2fs_free_mem(&refcount);
}
/*
* This function resets an e2fsck context; it is called when e2fsck
* needs to be restarted.
*/
static errcode_t e2fsck_reset_context(e2fsck_t ctx)
{
ctx->flags = 0;
ctx->lost_and_found = 0;
ctx->bad_lost_and_found = 0;
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_inode_bitmap(ctx->inode_reg_map);
ctx->inode_reg_map = 0;
ext2fs_free_block_bitmap(ctx->block_found_map);
ctx->block_found_map = 0;
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) {
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
ea_refcount_free(ctx->refcount);
ctx->refcount = 0;
ea_refcount_free(ctx->refcount_extra);
ctx->refcount_extra = 0;
ext2fs_free_block_bitmap(ctx->block_dup_map);
ctx->block_dup_map = 0;
ext2fs_free_block_bitmap(ctx->block_ea_map);
ctx->block_ea_map = 0;
ext2fs_free_inode_bitmap(ctx->inode_bb_map);
ctx->inode_bb_map = 0;
ext2fs_free_inode_bitmap(ctx->inode_bad_map);
ctx->inode_bad_map = 0;
ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
ctx->inode_imagic_map = 0;
ext2fs_u32_list_free(ctx->dirs_to_hash);
ctx->dirs_to_hash = 0;
/*
* Clear the array of invalid meta-data flags
*/
ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag);
ext2fs_free_mem(&ctx->invalid_block_bitmap_flag);
ext2fs_free_mem(&ctx->invalid_inode_table_flag);
/* 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;
}
static 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);
}
/*
* ea_refcount.c
*/
/*
* 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.
*/
static 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;
}
static 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;
}
static 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;
}
static 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;
}
static inline void ea_refcount_intr_begin(ext2_refcount_t refcount)
{
refcount->cursor = 0;
}
static 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++;
}
}
/*
* ehandler.c --- handle bad block errors which come up during the
* course of an e2fsck session.
*/
static const char *operation;
static errcode_t
e2fsck_handle_read_error(io_channel channel, unsigned long block, int count,
void *data, size_t size FSCK_ATTR((unused)),
int actual FSCK_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 FSCK_ATTR((unused)),
int actual FSCK_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;
}
static inline const char *ehandler_operation(const char *op)
{
const char *ret = operation;
operation = op;
return ret;
}
static void ehandler_init(io_channel channel)
{
channel->read_error = e2fsck_handle_read_error;
channel->write_error = e2fsck_handle_write_error;
}
/*
* 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.
*/
#define MNT_FL (MS_MGC_VAL | MS_RDONLY)
#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.
*/
static 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
}
static 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;
}
static 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);
}
static 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);
}
}
}
static inline void mark_buffer_dirty(struct buffer_head *bh)
{
bh->b_dirty = 1;
}
static inline void mark_buffer_clean(struct buffer_head * bh)
{
bh->b_dirty = 0;
}
static 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);
}
static inline int buffer_uptodate(struct buffer_head *bh)
{
return bh->b_uptodate;
}
static inline void mark_buffer_uptodate(struct buffer_head *bh, int val)
{
bh->b_uptodate = val;
}
static 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
ext2fs_free_mem(&j_inode);
#endif
*ret_journal = journal;
return 0;
errout:
ext2fs_free_mem(&dev_fs);
ext2fs_free_mem(&j_inode);
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
ext2fs_free_mem(&journal->j_inode);
#endif
ext2fs_free_mem(&journal->j_fs_dev);
ext2fs_free_mem(&journal);
}
/*
* This function makes sure that the superblock fields regarding the
* journal are consistent.
*/
static 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;
}
/*