|
Packit Service |
360c39 |
#include "clusterautoconfig.h"
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
#include <inttypes.h>
|
|
Packit Service |
360c39 |
#include <stdio.h>
|
|
Packit Service |
360c39 |
#include <stdlib.h>
|
|
Packit Service |
360c39 |
#include <string.h>
|
|
Packit Service |
360c39 |
#include <sys/types.h>
|
|
Packit Service |
360c39 |
#include <sys/stat.h>
|
|
Packit Service |
360c39 |
#include <unistd.h>
|
|
Packit Service |
360c39 |
#include <libintl.h>
|
|
Packit Service |
360c39 |
#include <ctype.h>
|
|
Packit Service |
360c39 |
#include <fcntl.h>
|
|
Packit Service |
360c39 |
#define _(String) gettext(String)
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
#include <logging.h>
|
|
Packit Service |
360c39 |
#include "libgfs2.h"
|
|
Packit Service |
360c39 |
#include "link.h"
|
|
Packit Service |
360c39 |
#include "osi_tree.h"
|
|
Packit Service |
360c39 |
#include "fsck.h"
|
|
Packit Service |
360c39 |
#include "util.h"
|
|
Packit Service |
360c39 |
#include "metawalk.h"
|
|
Packit Service |
360c39 |
#include "inode_hash.h"
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
#define COMFORTABLE_BLKS 5242880 /* 20GB in 4K blocks */
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* There are two bitmaps: (1) The "blockmap" that fsck uses to keep track of
|
|
Packit Service |
360c39 |
what block type has been discovered, and (2) The rgrp bitmap. Function
|
|
Packit Service |
360c39 |
gfs2_blockmap_set is used to set the former and gfs2_set_bitmap
|
|
Packit Service |
360c39 |
is used to set the latter. The two must be kept in sync, otherwise
|
|
Packit Service |
360c39 |
you'll get bitmap mismatches. This function checks the status of the
|
|
Packit Service |
360c39 |
bitmap whenever the blockmap changes, and fixes it accordingly. */
|
|
Packit Service |
360c39 |
int check_n_fix_bitmap(struct gfs2_sbd *sdp, struct rgrp_tree *rgd,
|
|
Packit Service |
360c39 |
uint64_t blk, int error_on_dinode, int new_state)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int old_state;
|
|
Packit Service |
360c39 |
int treat_as_inode = 0;
|
|
Packit Service |
360c39 |
int rewrite_rgrp = 0;
|
|
Packit Service |
360c39 |
struct gfs_rgrp *gfs1rg;
|
|
Packit Service |
360c39 |
const char *allocdesc[2][5] = { /* gfs2 descriptions */
|
|
Packit Service |
360c39 |
{"free", "data", "unlinked", "inode", "reserved"},
|
|
Packit Service |
360c39 |
/* gfs1 descriptions: */
|
|
Packit Service |
360c39 |
{"free", "data", "free meta", "metadata", "reserved"}};
|
|
Packit Service |
360c39 |
static struct rgrp_tree *prevrgd = NULL;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (prevrgd && rgrp_contains_block(prevrgd, blk)) {
|
|
Packit Service |
360c39 |
rgd = prevrgd;
|
|
Packit Service |
360c39 |
} else if (rgd == NULL || !rgrp_contains_block(rgd, blk)) {
|
|
Packit Service |
360c39 |
rgd = gfs2_blk2rgrpd(sdp, blk);
|
|
Packit Service |
360c39 |
prevrgd = rgd;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
gfs1rg = (struct gfs_rgrp *)&rgd->rg;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
old_state = lgfs2_get_bitmap(sdp, blk, rgd);
|
|
Packit Service |
360c39 |
if (old_state < 0) {
|
|
Packit Service |
360c39 |
log_err( _("Block %llu (0x%llx) is not represented in the "
|
|
Packit Service |
360c39 |
"system bitmap; part of an rgrp or superblock.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)blk, (unsigned long long)blk);
|
|
Packit Service |
360c39 |
return -1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (old_state == new_state)
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (error_on_dinode && old_state == GFS2_BLKST_DINODE &&
|
|
Packit Service |
360c39 |
new_state != GFS2_BLKST_FREE) {
|
|
Packit Service |
360c39 |
log_debug(_("Reference as '%s' to block %llu (0x%llx) which "
|
|
Packit Service |
360c39 |
"was marked as dinode. Needs further "
|
|
Packit Service |
360c39 |
"investigation.\n"),
|
|
Packit Service |
360c39 |
allocdesc[sdp->gfs1][new_state],
|
|
Packit Service |
360c39 |
(unsigned long long)blk, (unsigned long long)blk);
|
|
Packit Service |
360c39 |
return 1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* Keep these messages as short as possible, or the output gets to be
|
|
Packit Service |
360c39 |
huge and unmanageable. */
|
|
Packit Service |
360c39 |
log_err( _("Block %llu (0x%llx) was '%s', should be %s.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)blk, (unsigned long long)blk,
|
|
Packit Service |
360c39 |
allocdesc[sdp->gfs1][old_state],
|
|
Packit Service |
360c39 |
allocdesc[sdp->gfs1][new_state]);
|
|
Packit Service |
360c39 |
if (!query( _("Fix the bitmap? (y/n)"))) {
|
|
Packit Service |
360c39 |
log_err( _("The bitmap inconsistency was ignored.\n"));
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* If the new bitmap state is free (and therefore the old state was
|
|
Packit Service |
360c39 |
not) we have to add to the free space in the rgrp. If the old
|
|
Packit Service |
360c39 |
bitmap state was free (and therefore it no longer is) we have to
|
|
Packit Service |
360c39 |
subtract to the free space. If the type changed from dinode to
|
|
Packit Service |
360c39 |
data or data to dinode, no change in free space. */
|
|
Packit Service |
360c39 |
gfs2_set_bitmap(rgd, blk, new_state);
|
|
Packit Service |
360c39 |
if (new_state == GFS2_BLKST_FREE) {
|
|
Packit Service |
360c39 |
rgd->rg.rg_free++;
|
|
Packit Service |
360c39 |
rewrite_rgrp = 1;
|
|
Packit Service |
360c39 |
} else if (old_state == GFS2_BLKST_FREE) {
|
|
Packit Service |
360c39 |
rgd->rg.rg_free--;
|
|
Packit Service |
360c39 |
rewrite_rgrp = 1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* If we're freeing a dinode, get rid of the data structs for it. */
|
|
Packit Service |
360c39 |
if (old_state == GFS2_BLKST_DINODE ||
|
|
Packit Service |
360c39 |
old_state == GFS2_BLKST_UNLINKED) {
|
|
Packit Service |
360c39 |
struct dir_info *dt;
|
|
Packit Service |
360c39 |
struct inode_info *ii;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
dt = dirtree_find(blk);
|
|
Packit Service |
360c39 |
if (dt) {
|
|
Packit Service |
360c39 |
dirtree_delete(dt);
|
|
Packit Service |
360c39 |
treat_as_inode = 1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
ii = inodetree_find(blk);
|
|
Packit Service |
360c39 |
if (ii) {
|
|
Packit Service |
360c39 |
inodetree_delete(ii);
|
|
Packit Service |
360c39 |
treat_as_inode = 1;
|
|
Packit Service |
360c39 |
} else if (!sdp->gfs1) {
|
|
Packit Service |
360c39 |
treat_as_inode = 1;
|
|
Packit Service |
360c39 |
} else if (link1_type(&nlink1map, blk) == 1) {
|
|
Packit Service |
360c39 |
/* This is a GFS1 fs (so all metadata is marked inode).
|
|
Packit Service |
360c39 |
We need to verify it is an inode before we can decr
|
|
Packit Service |
360c39 |
the rgrp inode count. */
|
|
Packit Service |
360c39 |
treat_as_inode = 1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (old_state == GFS2_BLKST_DINODE) {
|
|
Packit Service |
360c39 |
if (treat_as_inode && rgd->rg.rg_dinodes > 0)
|
|
Packit Service |
360c39 |
rgd->rg.rg_dinodes--;
|
|
Packit Service |
360c39 |
else if (sdp->gfs1 && gfs1rg->rg_usedmeta > 0)
|
|
Packit Service |
360c39 |
gfs1rg->rg_usedmeta--;
|
|
Packit Service |
360c39 |
rewrite_rgrp = 1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
link1_set(&nlink1map, blk, 0);
|
|
Packit Service |
360c39 |
} else if (new_state == GFS2_BLKST_DINODE) {
|
|
Packit Service |
360c39 |
if (!sdp->gfs1) {
|
|
Packit Service |
360c39 |
treat_as_inode = 1;
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
/* This is GFS1 (so all metadata is marked inode). We
|
|
Packit Service |
360c39 |
need to verify it is an inode before we can decr
|
|
Packit Service |
360c39 |
the rgrp inode count. */
|
|
Packit Service |
360c39 |
if (link1_type(&nlink1map, blk) == 1)
|
|
Packit Service |
360c39 |
treat_as_inode = 1;
|
|
Packit Service |
360c39 |
else {
|
|
Packit Service |
360c39 |
struct dir_info *dt;
|
|
Packit Service |
360c39 |
struct inode_info *ii;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
dt = dirtree_find(blk);
|
|
Packit Service |
360c39 |
if (dt)
|
|
Packit Service |
360c39 |
treat_as_inode = 1;
|
|
Packit Service |
360c39 |
else {
|
|
Packit Service |
360c39 |
ii = inodetree_find(blk);
|
|
Packit Service |
360c39 |
if (ii)
|
|
Packit Service |
360c39 |
treat_as_inode = 1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (treat_as_inode)
|
|
Packit Service |
360c39 |
rgd->rg.rg_dinodes++;
|
|
Packit Service |
360c39 |
else if (sdp->gfs1)
|
|
Packit Service |
360c39 |
gfs1rg->rg_usedmeta++;
|
|
Packit Service |
360c39 |
rewrite_rgrp = 1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (rewrite_rgrp) {
|
|
Packit Service |
360c39 |
if (sdp->gfs1)
|
|
Packit Service |
360c39 |
gfs_rgrp_out((struct gfs_rgrp *)&rgd->rg, rgd->bits[0].bi_bh);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
gfs2_rgrp_out(&rgd->rg, rgd->bits[0].bi_bh->b_data);
|
|
Packit Service |
360c39 |
bmodified(rgd->bits[0].bi_bh);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
log_err( _("The bitmap was fixed.\n"));
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/*
|
|
Packit Service |
360c39 |
* _fsck_bitmap_set - Mark a block in the bitmap, and adjust free space.
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
int _fsck_bitmap_set(struct gfs2_inode *ip, uint64_t bblock,
|
|
Packit Service |
360c39 |
const char *btype, int mark,
|
|
Packit Service |
360c39 |
int error_on_dinode, const char *caller, int fline)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int error;
|
|
Packit Service |
360c39 |
static int prev_ino_addr = 0;
|
|
Packit Service |
360c39 |
static int prev_mark = 0;
|
|
Packit Service |
360c39 |
static int prevcount = 0;
|
|
Packit Service |
360c39 |
static const char *prev_caller = NULL;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (print_level >= MSG_DEBUG) {
|
|
Packit Service |
360c39 |
if ((ip->i_di.di_num.no_addr == prev_ino_addr) &&
|
|
Packit Service |
360c39 |
(mark == prev_mark) && caller == prev_caller) {
|
|
Packit Service |
360c39 |
log_info("(0x%llx) ", (unsigned long long)bblock);
|
|
Packit Service |
360c39 |
prevcount++;
|
|
Packit Service |
360c39 |
if (prevcount > 10) {
|
|
Packit Service |
360c39 |
log_info("\n");
|
|
Packit Service |
360c39 |
prevcount = 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* I'm circumventing the log levels here on purpose to make the
|
|
Packit Service |
360c39 |
output easier to debug. */
|
|
Packit Service |
360c39 |
} else if (ip->i_di.di_num.no_addr == bblock) {
|
|
Packit Service |
360c39 |
if (prevcount) {
|
|
Packit Service |
360c39 |
log_info("\n");
|
|
Packit Service |
360c39 |
prevcount = 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
printf( _("(%s:%d) %s inode found at block "
|
|
Packit Service |
360c39 |
"(0x%llx): marking as '%s'\n"), caller, fline,
|
|
Packit Service |
360c39 |
btype,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
block_type_string(mark));
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
if (prevcount) {
|
|
Packit Service |
360c39 |
log_info("\n");
|
|
Packit Service |
360c39 |
prevcount = 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
printf( _("(%s:%d) inode (0x%llx) references %s block"
|
|
Packit Service |
360c39 |
" (0x%llx): marking as '%s'\n"),
|
|
Packit Service |
360c39 |
caller, fline,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
btype, (unsigned long long)bblock,
|
|
Packit Service |
360c39 |
block_type_string(mark));
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
prev_ino_addr = ip->i_di.di_num.no_addr;
|
|
Packit Service |
360c39 |
prev_mark = mark;
|
|
Packit Service |
360c39 |
prev_caller = caller;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
error = check_n_fix_bitmap(ip->i_sbd, ip->i_rgd, bblock,
|
|
Packit Service |
360c39 |
error_on_dinode, mark);
|
|
Packit Service |
360c39 |
if (error < 0)
|
|
Packit Service |
360c39 |
log_err(_("This block is not represented in the bitmap.\n"));
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
struct duptree *dupfind(uint64_t block)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct osi_node *node = dup_blocks.osi_node;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
while (node) {
|
|
Packit Service |
360c39 |
struct duptree *dt = (struct duptree *)node;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (block < dt->block)
|
|
Packit Service |
360c39 |
node = node->osi_left;
|
|
Packit Service |
360c39 |
else if (block > dt->block)
|
|
Packit Service |
360c39 |
node = node->osi_right;
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
return dt;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return NULL;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
struct gfs2_inode *fsck_system_inode(struct gfs2_sbd *sdp, uint64_t block)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int j;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (lf_dip && lf_dip->i_di.di_num.no_addr == block)
|
|
Packit Service |
360c39 |
return lf_dip;
|
|
Packit Service |
360c39 |
if (!sdp->gfs1)
|
|
Packit Service |
360c39 |
return is_system_inode(sdp, block);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (sdp->md.statfs && block == sdp->md.statfs->i_di.di_num.no_addr)
|
|
Packit Service |
360c39 |
return sdp->md.statfs;
|
|
Packit Service |
360c39 |
if (sdp->md.jiinode && block == sdp->md.jiinode->i_di.di_num.no_addr)
|
|
Packit Service |
360c39 |
return sdp->md.jiinode;
|
|
Packit Service |
360c39 |
if (sdp->md.riinode && block == sdp->md.riinode->i_di.di_num.no_addr)
|
|
Packit Service |
360c39 |
return sdp->md.riinode;
|
|
Packit Service |
360c39 |
if (sdp->md.qinode && block == sdp->md.qinode->i_di.di_num.no_addr)
|
|
Packit Service |
360c39 |
return sdp->md.qinode;
|
|
Packit Service |
360c39 |
if (sdp->md.rooti && block == sdp->md.rooti->i_di.di_num.no_addr)
|
|
Packit Service |
360c39 |
return sdp->md.rooti;
|
|
Packit Service |
360c39 |
for (j = 0; j < sdp->md.journals; j++)
|
|
Packit Service |
360c39 |
if (sdp->md.journal && sdp->md.journal[j] &&
|
|
Packit Service |
360c39 |
block == sdp->md.journal[j]->i_di.di_num.no_addr)
|
|
Packit Service |
360c39 |
return sdp->md.journal[j];
|
|
Packit Service |
360c39 |
return NULL;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* fsck_load_inode - same as gfs2_load_inode() in libgfs2 but system inodes
|
|
Packit Service |
360c39 |
get special treatment. */
|
|
Packit Service |
360c39 |
struct gfs2_inode *fsck_load_inode(struct gfs2_sbd *sdp, uint64_t block)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct gfs2_inode *ip = NULL;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
ip = fsck_system_inode(sdp, block);
|
|
Packit Service |
360c39 |
if (ip)
|
|
Packit Service |
360c39 |
return ip;
|
|
Packit Service |
360c39 |
if (sdp->gfs1)
|
|
Packit Service |
360c39 |
return lgfs2_gfs_inode_read(sdp, block);
|
|
Packit Service |
360c39 |
return lgfs2_inode_read(sdp, block);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* fsck_inode_get - same as inode_get() in libgfs2 but system inodes
|
|
Packit Service |
360c39 |
get special treatment. */
|
|
Packit Service |
360c39 |
struct gfs2_inode *fsck_inode_get(struct gfs2_sbd *sdp, struct rgrp_tree *rgd,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct gfs2_inode *sysip;
|
|
Packit Service |
360c39 |
struct gfs2_inode *ip;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
sysip = fsck_system_inode(sdp, bh->b_blocknr);
|
|
Packit Service |
360c39 |
if (sysip)
|
|
Packit Service |
360c39 |
return sysip;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (sdp->gfs1)
|
|
Packit Service |
360c39 |
ip = lgfs2_gfs_inode_get(sdp, bh);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
ip = lgfs2_inode_get(sdp, bh);
|
|
Packit Service |
360c39 |
if (ip)
|
|
Packit Service |
360c39 |
ip->i_rgd = rgd;
|
|
Packit Service |
360c39 |
return ip;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* fsck_inode_put - same as inode_put() in libgfs2 but system inodes
|
|
Packit Service |
360c39 |
get special treatment. */
|
|
Packit Service |
360c39 |
void fsck_inode_put(struct gfs2_inode **ip_in)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct gfs2_inode *ip = *ip_in;
|
|
Packit Service |
360c39 |
struct gfs2_inode *sysip;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
sysip = fsck_system_inode(ip->i_sbd, ip->i_di.di_num.no_addr);
|
|
Packit Service |
360c39 |
if (!sysip)
|
|
Packit Service |
360c39 |
inode_put(ip_in);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/**
|
|
Packit Service |
360c39 |
* dirent_repair - attempt to repair a corrupt directory entry.
|
|
Packit Service |
360c39 |
* @bh - The buffer header that contains the bad dirent
|
|
Packit Service |
360c39 |
* @de - The directory entry in native format
|
|
Packit Service |
360c39 |
* @dent - The directory entry in on-disk format
|
|
Packit Service |
360c39 |
* @type - Type of directory (DIR_LINEAR or DIR_EXHASH)
|
|
Packit Service |
360c39 |
* @first - TRUE if this is the first dirent in the buffer
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* This function tries to repair a corrupt directory entry. All we
|
|
Packit Service |
360c39 |
* know at this point is that the length field is wrong.
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static int dirent_repair(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
|
|
Packit Service |
360c39 |
struct gfs2_dirent *de, struct gfs2_dirent *dent,
|
|
Packit Service |
360c39 |
int type, int first)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
char *bh_end, *p;
|
|
Packit Service |
360c39 |
int calc_de_name_len = 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* If this is a sentinel, just fix the length and move on */
|
|
Packit Service |
360c39 |
if (first && !de->de_inum.no_formal_ino) { /* Is it a sentinel? */
|
|
Packit Service |
360c39 |
if (type == DIR_LINEAR)
|
|
Packit Service |
360c39 |
de->de_rec_len = ip->i_sbd->bsize -
|
|
Packit Service |
360c39 |
sizeof(struct gfs2_dinode);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
de->de_rec_len = ip->i_sbd->bsize -
|
|
Packit Service |
360c39 |
sizeof(struct gfs2_leaf);
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
bh_end = bh->b_data + ip->i_sbd->bsize;
|
|
Packit Service |
360c39 |
/* first, figure out a probable name length */
|
|
Packit Service |
360c39 |
p = (char *)dent + sizeof(struct gfs2_dirent);
|
|
Packit Service |
360c39 |
while (*p && /* while there's a non-zero char and */
|
|
Packit Service |
360c39 |
isprint(*p) && /* a printable character and */
|
|
Packit Service |
360c39 |
p < bh_end) { /* not past end of buffer */
|
|
Packit Service |
360c39 |
calc_de_name_len++;
|
|
Packit Service |
360c39 |
p++;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (!calc_de_name_len)
|
|
Packit Service |
360c39 |
return 1;
|
|
Packit Service |
360c39 |
/* There can often be noise at the end, so only */
|
|
Packit Service |
360c39 |
/* Trust the shorter of the two in case we have too much */
|
|
Packit Service |
360c39 |
/* Or rather, only trust ours if it's shorter. */
|
|
Packit Service |
360c39 |
if (!de->de_name_len || de->de_name_len > NAME_MAX ||
|
|
Packit Service |
360c39 |
calc_de_name_len < de->de_name_len) /* if dent is hosed */
|
|
Packit Service |
360c39 |
de->de_name_len = calc_de_name_len; /* use ours */
|
|
Packit Service |
360c39 |
de->de_rec_len = GFS2_DIRENT_SIZE(de->de_name_len);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
gfs2_dirent_out(de, (char *)dent);
|
|
Packit Service |
360c39 |
bmodified(bh);
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/**
|
|
Packit Service |
360c39 |
* dirblk_truncate - truncate a directory block
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static void dirblk_truncate(struct gfs2_inode *ip, struct gfs2_dirent *fixb,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
char *bh_end;
|
|
Packit Service |
360c39 |
struct gfs2_dirent de;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
bh_end = bh->b_data + ip->i_sbd->sd_sb.sb_bsize;
|
|
Packit Service |
360c39 |
/* truncate the block to save the most dentries. To do this we
|
|
Packit Service |
360c39 |
have to patch the previous dent. */
|
|
Packit Service |
360c39 |
gfs2_dirent_in(&de, (char *)fixb);
|
|
Packit Service |
360c39 |
de.de_rec_len = bh_end - (char *)fixb;
|
|
Packit Service |
360c39 |
gfs2_dirent_out(&de, (char *)fixb);
|
|
Packit Service |
360c39 |
bmodified(bh);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/*
|
|
Packit Service |
360c39 |
* check_entries - check directory entries for a given block
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* @ip - dinode associated with this leaf block
|
|
Packit Service |
360c39 |
* bh - buffer for the leaf block
|
|
Packit Service |
360c39 |
* type - type of block this is (linear or exhash)
|
|
Packit Service |
360c39 |
* @count - set to the count entries
|
|
Packit Service |
360c39 |
* @lindex - the last inde
|
|
Packit Service |
360c39 |
* @pass - structure pointing to pass-specific functions
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* returns: 0 - good block or it was repaired to be good
|
|
Packit Service |
360c39 |
* -1 - error occurred
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static int check_entries(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
|
|
Packit Service |
360c39 |
int type, uint32_t *count, int lindex,
|
|
Packit Service |
360c39 |
struct metawalk_fxns *pass)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct gfs2_dirent *dent;
|
|
Packit Service |
360c39 |
struct gfs2_dirent de, *prev;
|
|
Packit Service |
360c39 |
int error = 0;
|
|
Packit Service |
360c39 |
char *bh_end;
|
|
Packit Service |
360c39 |
char *filename;
|
|
Packit Service |
360c39 |
int first = 1;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
bh_end = bh->b_data + ip->i_sbd->bsize;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (type == DIR_LINEAR) {
|
|
Packit Service |
360c39 |
dent = (struct gfs2_dirent *)(bh->b_data + sizeof(struct gfs2_dinode));
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
dent = (struct gfs2_dirent *)(bh->b_data + sizeof(struct gfs2_leaf));
|
|
Packit Service |
360c39 |
log_debug( _("Checking leaf %llu (0x%llx)\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)bh->b_blocknr,
|
|
Packit Service |
360c39 |
(unsigned long long)bh->b_blocknr);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
prev = NULL;
|
|
Packit Service |
360c39 |
if (!pass->check_dentry)
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
while (1) {
|
|
Packit Service |
360c39 |
if (skip_this_pass || fsck_abort)
|
|
Packit Service |
360c39 |
return FSCK_OK;
|
|
Packit Service |
360c39 |
memset(&de, 0, sizeof(struct gfs2_dirent));
|
|
Packit Service |
360c39 |
gfs2_dirent_in(&de, (char *)dent);
|
|
Packit Service |
360c39 |
filename = (char *)dent + sizeof(struct gfs2_dirent);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (de.de_rec_len < sizeof(struct gfs2_dirent) +
|
|
Packit Service |
360c39 |
de.de_name_len ||
|
|
Packit Service |
360c39 |
(de.de_inum.no_formal_ino && !de.de_name_len && !first)) {
|
|
Packit Service |
360c39 |
log_err( _("Directory block %llu (0x%llx"
|
|
Packit Service |
360c39 |
"), entry %d of directory %llu "
|
|
Packit Service |
360c39 |
"(0x%llx) is corrupt.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)bh->b_blocknr,
|
|
Packit Service |
360c39 |
(unsigned long long)bh->b_blocknr,
|
|
Packit Service |
360c39 |
(*count) + 1,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr);
|
|
Packit Service |
360c39 |
if (query( _("Attempt to repair it? (y/n) "))) {
|
|
Packit Service |
360c39 |
if (dirent_repair(ip, bh, &de, dent, type,
|
|
Packit Service |
360c39 |
first)) {
|
|
Packit Service |
360c39 |
if (first) /* make a new sentinel */
|
|
Packit Service |
360c39 |
dirblk_truncate(ip, dent, bh);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
dirblk_truncate(ip, prev, bh);
|
|
Packit Service |
360c39 |
log_err( _("Unable to repair corrupt "
|
|
Packit Service |
360c39 |
"directory entry; the "
|
|
Packit Service |
360c39 |
"entry was removed "
|
|
Packit Service |
360c39 |
"instead.\n"));
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
log_err( _("Corrupt directory entry "
|
|
Packit Service |
360c39 |
"repaired.\n"));
|
|
Packit Service |
360c39 |
/* keep looping through dentries */
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
log_err( _("Corrupt directory entry ignored, "
|
|
Packit Service |
360c39 |
"stopped after checking %d entries.\n"),
|
|
Packit Service |
360c39 |
*count);
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (!de.de_inum.no_formal_ino){
|
|
Packit Service |
360c39 |
if (first){
|
|
Packit Service |
360c39 |
log_debug( _("First dirent is a sentinel (place holder).\n"));
|
|
Packit Service |
360c39 |
first = 0;
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
log_err( _("Directory entry with inode number of "
|
|
Packit Service |
360c39 |
"zero in leaf %llu (0x%llx) of "
|
|
Packit Service |
360c39 |
"directory %llu (0x%llx)!\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)bh->b_blocknr,
|
|
Packit Service |
360c39 |
(unsigned long long)bh->b_blocknr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr);
|
|
Packit Service |
360c39 |
if (query(_("Attempt to remove it? (y/n) "))) {
|
|
Packit Service |
360c39 |
dirblk_truncate(ip, prev, bh);
|
|
Packit Service |
360c39 |
log_err(_("The corrupt directory "
|
|
Packit Service |
360c39 |
"entry was removed.\n"));
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
log_err( _("Corrupt directory entry "
|
|
Packit Service |
360c39 |
"ignored, stopped after "
|
|
Packit Service |
360c39 |
"checking %d entries.\n"),
|
|
Packit Service |
360c39 |
*count);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
if (!de.de_inum.no_addr && first) { /* reverse sentinel */
|
|
Packit Service |
360c39 |
log_debug( _("First dirent is a Sentinel (place holder).\n"));
|
|
Packit Service |
360c39 |
/* Swap the two to silently make it a proper sentinel */
|
|
Packit Service |
360c39 |
de.de_inum.no_addr = de.de_inum.no_formal_ino;
|
|
Packit Service |
360c39 |
de.de_inum.no_formal_ino = 0;
|
|
Packit Service |
360c39 |
gfs2_dirent_out(&de, (char *)dent);
|
|
Packit Service |
360c39 |
bmodified(bh);
|
|
Packit Service |
360c39 |
/* Mark dirent buffer as modified */
|
|
Packit Service |
360c39 |
first = 0;
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
error = pass->check_dentry(ip, dent, prev, bh,
|
|
Packit Service |
360c39 |
filename, count,
|
|
Packit Service |
360c39 |
&lindex,
|
|
Packit Service |
360c39 |
pass->private);
|
|
Packit Service |
360c39 |
if (error < 0) {
|
|
Packit Service |
360c39 |
stack;
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if ((char *)dent + de.de_rec_len >= bh_end){
|
|
Packit Service |
360c39 |
log_debug( _("Last entry processed for %lld->%lld "
|
|
Packit Service |
360c39 |
"(0x%llx->0x%llx), di_blocks=%llu.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)bh->b_blocknr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)bh->b_blocknr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_blocks);
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* If we didn't clear the dentry, or if we did, but it
|
|
Packit Service |
360c39 |
* was the first dentry, set prev */
|
|
Packit Service |
360c39 |
if (!error || first)
|
|
Packit Service |
360c39 |
prev = dent;
|
|
Packit Service |
360c39 |
first = 0;
|
|
Packit Service |
360c39 |
dent = (struct gfs2_dirent *)((char *)dent + de.de_rec_len);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/**
|
|
Packit Service |
360c39 |
* check_leaf - check a leaf block for errors
|
|
Packit Service |
360c39 |
* Reads in the leaf block
|
|
Packit Service |
360c39 |
* Leaves the buffer around for further analysis (caller must brelse)
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
int check_leaf(struct gfs2_inode *ip, int lindex, struct metawalk_fxns *pass,
|
|
Packit Service |
360c39 |
uint64_t *leaf_no, struct gfs2_leaf *leaf, int *ref_count)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int error = 0, fix;
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *lbh = NULL;
|
|
Packit Service |
360c39 |
uint32_t count = 0;
|
|
Packit Service |
360c39 |
struct gfs2_sbd *sdp = ip->i_sbd;
|
|
Packit Service |
360c39 |
const char *msg;
|
|
Packit Service |
360c39 |
int di_depth = ip->i_di.di_depth;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Make sure the block number is in range. */
|
|
Packit Service |
360c39 |
if (!valid_block_ip(ip, *leaf_no)) {
|
|
Packit Service |
360c39 |
log_err( _("Leaf block #%llu (0x%llx) is out of range for "
|
|
Packit Service |
360c39 |
"directory #%llu (0x%llx) at index %d (0x%x).\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)*leaf_no,
|
|
Packit Service |
360c39 |
(unsigned long long)*leaf_no,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
lindex, lindex);
|
|
Packit Service |
360c39 |
msg = _("that is out of range");
|
|
Packit Service |
360c39 |
goto bad_leaf;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Try to read in the leaf block. */
|
|
Packit Service |
360c39 |
lbh = bread(sdp, *leaf_no);
|
|
Packit Service |
360c39 |
/* Make sure it's really a valid leaf block. */
|
|
Packit Service |
360c39 |
if (gfs2_check_meta(lbh, GFS2_METATYPE_LF)) {
|
|
Packit Service |
360c39 |
msg = _("that is not really a leaf");
|
|
Packit Service |
360c39 |
goto bad_leaf;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (pass->check_leaf_depth)
|
|
Packit Service |
360c39 |
error = pass->check_leaf_depth(ip, *leaf_no, *ref_count, lbh);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (error >= 0 && pass->check_leaf) {
|
|
Packit Service |
360c39 |
error = pass->check_leaf(ip, *leaf_no, pass->private);
|
|
Packit Service |
360c39 |
if (error == -EEXIST) {
|
|
Packit Service |
360c39 |
log_info(_("Previous reference to leaf %lld (0x%llx) "
|
|
Packit Service |
360c39 |
"has already checked it; skipping.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)*leaf_no,
|
|
Packit Service |
360c39 |
(unsigned long long)*leaf_no);
|
|
Packit Service |
360c39 |
brelse(lbh);
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* Early versions of GFS2 had an endianess bug in the kernel that set
|
|
Packit Service |
360c39 |
lf_dirent_format to cpu_to_be16(GFS2_FORMAT_DE). This was fixed
|
|
Packit Service |
360c39 |
to use cpu_to_be32(), but we should check for incorrect values and
|
|
Packit Service |
360c39 |
replace them with the correct value. */
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
gfs2_leaf_in(leaf, lbh->b_data);
|
|
Packit Service |
360c39 |
if (leaf->lf_dirent_format == (GFS2_FORMAT_DE << 16)) {
|
|
Packit Service |
360c39 |
log_debug( _("incorrect lf_dirent_format at leaf #%" PRIu64
|
|
Packit Service |
360c39 |
"\n"), *leaf_no);
|
|
Packit Service |
360c39 |
leaf->lf_dirent_format = GFS2_FORMAT_DE;
|
|
Packit Service |
360c39 |
gfs2_leaf_out(leaf, lbh->b_data);
|
|
Packit Service |
360c39 |
bmodified(lbh);
|
|
Packit Service |
360c39 |
log_debug( _("Fixing lf_dirent_format.\n"));
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Make sure it's really a leaf. */
|
|
Packit Service |
360c39 |
if (leaf->lf_header.mh_type != GFS2_METATYPE_LF) {
|
|
Packit Service |
360c39 |
log_err( _("Inode %llu (0x%llx) points to bad leaf %llu"
|
|
Packit Service |
360c39 |
" (0x%llx).\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)*leaf_no,
|
|
Packit Service |
360c39 |
(unsigned long long)*leaf_no);
|
|
Packit Service |
360c39 |
msg = _("that is not a leaf");
|
|
Packit Service |
360c39 |
goto bad_leaf;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (pass->check_dentry && is_dir(&ip->i_di, sdp->gfs1)) {
|
|
Packit Service |
360c39 |
error = check_entries(ip, lbh, DIR_EXHASH, &count, lindex,
|
|
Packit Service |
360c39 |
pass);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (skip_this_pass || fsck_abort)
|
|
Packit Service |
360c39 |
goto out;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (error < 0) {
|
|
Packit Service |
360c39 |
stack;
|
|
Packit Service |
360c39 |
goto out; /* This seems wrong: needs investigation */
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (count == leaf->lf_entries)
|
|
Packit Service |
360c39 |
goto out;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* release and re-read the leaf in case check_entries
|
|
Packit Service |
360c39 |
changed it. */
|
|
Packit Service |
360c39 |
brelse(lbh);
|
|
Packit Service |
360c39 |
lbh = bread(sdp, *leaf_no);
|
|
Packit Service |
360c39 |
gfs2_leaf_in(leaf, lbh->b_data);
|
|
Packit Service |
360c39 |
if (count != leaf->lf_entries) {
|
|
Packit Service |
360c39 |
log_err( _("Leaf %llu (0x%llx) entry count in "
|
|
Packit Service |
360c39 |
"directory %llu (0x%llx) does not match "
|
|
Packit Service |
360c39 |
"number of entries found - is %u, found %u\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)*leaf_no,
|
|
Packit Service |
360c39 |
(unsigned long long)*leaf_no,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
leaf->lf_entries, count);
|
|
Packit Service |
360c39 |
if (query( _("Update leaf entry count? (y/n) "))) {
|
|
Packit Service |
360c39 |
leaf->lf_entries = count;
|
|
Packit Service |
360c39 |
gfs2_leaf_out(leaf, lbh->b_data);
|
|
Packit Service |
360c39 |
bmodified(lbh);
|
|
Packit Service |
360c39 |
log_warn( _("Leaf entry count updated\n"));
|
|
Packit Service |
360c39 |
} else
|
|
Packit Service |
360c39 |
log_err( _("Leaf entry count left in "
|
|
Packit Service |
360c39 |
"inconsistent state\n"));
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
out:
|
|
Packit Service |
360c39 |
if (di_depth < ip->i_di.di_depth) {
|
|
Packit Service |
360c39 |
log_debug(_("Depth of directory %lld (0x%llx) changed from "
|
|
Packit Service |
360c39 |
"%d to %d; adjusting ref_count from %d to %d\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
di_depth, ip->i_di.di_depth,
|
|
Packit Service |
360c39 |
*ref_count,
|
|
Packit Service |
360c39 |
(*ref_count) << (ip->i_di.di_depth - di_depth));
|
|
Packit Service |
360c39 |
(*ref_count) <<= (ip->i_di.di_depth - di_depth);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
brelse(lbh);
|
|
Packit Service |
360c39 |
if (error < 0)
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
bad_leaf:
|
|
Packit Service |
360c39 |
if (lbh)
|
|
Packit Service |
360c39 |
brelse(lbh);
|
|
Packit Service |
360c39 |
if (pass->repair_leaf) {
|
|
Packit Service |
360c39 |
/* The leaf we read in is bad so we need to repair it. */
|
|
Packit Service |
360c39 |
fix = pass->repair_leaf(ip, leaf_no, lindex, *ref_count, msg);
|
|
Packit Service |
360c39 |
if (fix < 0)
|
|
Packit Service |
360c39 |
return fix;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (di_depth < ip->i_di.di_depth) {
|
|
Packit Service |
360c39 |
log_debug(_("Depth of directory %lld (0x%llx) changed from "
|
|
Packit Service |
360c39 |
"%d to %d. Adjusting ref_count from %d to %d\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
di_depth, ip->i_di.di_depth,
|
|
Packit Service |
360c39 |
*ref_count,
|
|
Packit Service |
360c39 |
(*ref_count) << (ip->i_di.di_depth - di_depth));
|
|
Packit Service |
360c39 |
(*ref_count) <<= (ip->i_di.di_depth - di_depth);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return 1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int u64cmp(const void *p1, const void *p2)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
uint64_t a = *(uint64_t *)p1;
|
|
Packit Service |
360c39 |
uint64_t b = *(uint64_t *)p2;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (a > b)
|
|
Packit Service |
360c39 |
return 1;
|
|
Packit Service |
360c39 |
if (a < b)
|
|
Packit Service |
360c39 |
return -1;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static void dir_leaf_reada(struct gfs2_inode *ip, uint64_t *tbl, unsigned hsize)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
uint64_t *t = alloca(hsize * sizeof(uint64_t));
|
|
Packit Service |
360c39 |
uint64_t leaf_no;
|
|
Packit Service |
360c39 |
struct gfs2_sbd *sdp = ip->i_sbd;
|
|
Packit Service |
360c39 |
unsigned n = 0;
|
|
Packit Service |
360c39 |
unsigned i;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
for (i = 0; i < hsize; i++) {
|
|
Packit Service |
360c39 |
leaf_no = be64_to_cpu(tbl[i]);
|
|
Packit Service |
360c39 |
if (valid_block_ip(ip, leaf_no))
|
|
Packit Service |
360c39 |
t[n++] = leaf_no * sdp->bsize;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
qsort(t, n, sizeof(uint64_t), u64cmp);
|
|
Packit Service |
360c39 |
for (i = 0; i < n; i++)
|
|
Packit Service |
360c39 |
posix_fadvise(sdp->device_fd, t[i], sdp->bsize, POSIX_FADV_WILLNEED);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Checks exhash directory entries */
|
|
Packit Service |
360c39 |
int check_leaf_blks(struct gfs2_inode *ip, struct metawalk_fxns *pass)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int error = 0;
|
|
Packit Service |
360c39 |
unsigned hsize = (1 << ip->i_di.di_depth);
|
|
Packit Service |
360c39 |
uint64_t leaf_no, leaf_next;
|
|
Packit Service |
360c39 |
uint64_t first_ok_leaf, orig_di_blocks;
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *lbh;
|
|
Packit Service |
360c39 |
int lindex;
|
|
Packit Service |
360c39 |
struct gfs2_sbd *sdp = ip->i_sbd;
|
|
Packit Service |
360c39 |
int ref_count, orig_ref_count, orig_di_depth, orig_di_height;
|
|
Packit Service |
360c39 |
uint64_t *tbl;
|
|
Packit Service |
360c39 |
int chained_leaf, tbl_valid;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
tbl = get_dir_hash(ip);
|
|
Packit Service |
360c39 |
if (tbl == NULL) {
|
|
Packit Service |
360c39 |
perror("get_dir_hash");
|
|
Packit Service |
360c39 |
return -1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
tbl_valid = 1;
|
|
Packit Service |
360c39 |
orig_di_depth = ip->i_di.di_depth;
|
|
Packit Service |
360c39 |
orig_di_height = ip->i_di.di_height;
|
|
Packit Service |
360c39 |
orig_di_blocks = ip->i_di.di_blocks;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Turn off system readahead */
|
|
Packit Service |
360c39 |
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_RANDOM);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Readahead */
|
|
Packit Service |
360c39 |
dir_leaf_reada(ip, tbl, hsize);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (pass->check_hash_tbl) {
|
|
Packit Service |
360c39 |
error = pass->check_hash_tbl(ip, tbl, hsize, pass->private);
|
|
Packit Service |
360c39 |
if (error < 0) {
|
|
Packit Service |
360c39 |
free(tbl);
|
|
Packit Service |
360c39 |
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* If hash table changes were made, read it in again. */
|
|
Packit Service |
360c39 |
if (error) {
|
|
Packit Service |
360c39 |
free(tbl);
|
|
Packit Service |
360c39 |
tbl = get_dir_hash(ip);
|
|
Packit Service |
360c39 |
if (tbl == NULL) {
|
|
Packit Service |
360c39 |
perror("get_dir_hash");
|
|
Packit Service |
360c39 |
return -1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Find the first valid leaf pointer in range and use it as our "old"
|
|
Packit Service |
360c39 |
leaf. That way, bad blocks at the beginning will be overwritten
|
|
Packit Service |
360c39 |
with the first valid leaf. */
|
|
Packit Service |
360c39 |
first_ok_leaf = leaf_no = -1;
|
|
Packit Service |
360c39 |
for (lindex = 0; lindex < hsize; lindex++) {
|
|
Packit Service |
360c39 |
leaf_no = be64_to_cpu(tbl[lindex]);
|
|
Packit Service |
360c39 |
if (valid_block_ip(ip, leaf_no)) {
|
|
Packit Service |
360c39 |
lbh = bread(sdp, leaf_no);
|
|
Packit Service |
360c39 |
/* Make sure it's really a valid leaf block. */
|
|
Packit Service |
360c39 |
if (gfs2_check_meta(lbh, GFS2_METATYPE_LF) == 0) {
|
|
Packit Service |
360c39 |
brelse(lbh);
|
|
Packit Service |
360c39 |
first_ok_leaf = leaf_no;
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
brelse(lbh);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (first_ok_leaf == -1) { /* no valid leaf found */
|
|
Packit Service |
360c39 |
log_err( _("Directory #%llu (0x%llx) has no valid leaf "
|
|
Packit Service |
360c39 |
"blocks\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr);
|
|
Packit Service |
360c39 |
free(tbl);
|
|
Packit Service |
360c39 |
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
|
|
Packit Service |
360c39 |
return 1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
lindex = 0;
|
|
Packit Service |
360c39 |
leaf_next = -1;
|
|
Packit Service |
360c39 |
while (lindex < hsize) {
|
|
Packit Service |
360c39 |
int l;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (fsck_abort)
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (!tbl_valid) {
|
|
Packit Service |
360c39 |
free(tbl);
|
|
Packit Service |
360c39 |
log_debug(_("Re-reading 0x%llx hash table.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr);
|
|
Packit Service |
360c39 |
tbl = get_dir_hash(ip);
|
|
Packit Service |
360c39 |
if (tbl == NULL) {
|
|
Packit Service |
360c39 |
perror("get_dir_hash");
|
|
Packit Service |
360c39 |
return -1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
tbl_valid = 1;
|
|
Packit Service |
360c39 |
orig_di_depth = ip->i_di.di_depth;
|
|
Packit Service |
360c39 |
orig_di_height = ip->i_di.di_height;
|
|
Packit Service |
360c39 |
orig_di_blocks = ip->i_di.di_blocks;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
leaf_no = be64_to_cpu(tbl[lindex]);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* count the number of block pointers to this leaf. We don't
|
|
Packit Service |
360c39 |
need to count the current lindex, because we already know
|
|
Packit Service |
360c39 |
it's a reference */
|
|
Packit Service |
360c39 |
ref_count = 1;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
for (l = lindex + 1; l < hsize; l++) {
|
|
Packit Service |
360c39 |
leaf_next = be64_to_cpu(tbl[l]);
|
|
Packit Service |
360c39 |
if (leaf_next != leaf_no)
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
ref_count++;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
orig_ref_count = ref_count;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
chained_leaf = 0;
|
|
Packit Service |
360c39 |
do {
|
|
Packit Service |
360c39 |
struct gfs2_leaf leaf;
|
|
Packit Service |
360c39 |
if (fsck_abort) {
|
|
Packit Service |
360c39 |
free(tbl);
|
|
Packit Service |
360c39 |
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
error = check_leaf(ip, lindex, pass, &leaf_no, &leaf,
|
|
Packit Service |
360c39 |
&ref_count);
|
|
Packit Service |
360c39 |
if (ref_count != orig_ref_count) {
|
|
Packit Service |
360c39 |
log_debug(_("Ref count of leaf 0x%llx "
|
|
Packit Service |
360c39 |
"changed from %d to %d.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)leaf_no,
|
|
Packit Service |
360c39 |
orig_ref_count, ref_count);
|
|
Packit Service |
360c39 |
tbl_valid = 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (error < 0) {
|
|
Packit Service |
360c39 |
free(tbl);
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (!leaf.lf_next || error)
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
leaf_no = leaf.lf_next;
|
|
Packit Service |
360c39 |
chained_leaf++;
|
|
Packit Service |
360c39 |
log_debug( _("Leaf chain #%d (0x%llx) detected.\n"),
|
|
Packit Service |
360c39 |
chained_leaf, (unsigned long long)leaf_no);
|
|
Packit Service |
360c39 |
} while (1); /* while we have chained leaf blocks */
|
|
Packit Service |
360c39 |
if (orig_di_depth != ip->i_di.di_depth) {
|
|
Packit Service |
360c39 |
log_debug(_("Depth of 0x%llx changed from %d to %d\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
orig_di_depth, ip->i_di.di_depth);
|
|
Packit Service |
360c39 |
tbl_valid = 0;
|
|
Packit Service |
360c39 |
lindex <<= (ip->i_di.di_depth - orig_di_depth);
|
|
Packit Service |
360c39 |
hsize = (1 << ip->i_di.di_depth);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (orig_di_height != ip->i_di.di_height) {
|
|
Packit Service |
360c39 |
log_debug(_("Height of 0x%llx changed from %d to "
|
|
Packit Service |
360c39 |
"%d\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
orig_di_height, ip->i_di.di_height);
|
|
Packit Service |
360c39 |
tbl_valid = 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (orig_di_blocks != ip->i_di.di_blocks) {
|
|
Packit Service |
360c39 |
log_debug(_("Block count of 0x%llx changed from %llu "
|
|
Packit Service |
360c39 |
"to %llu\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)orig_di_blocks,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_blocks);
|
|
Packit Service |
360c39 |
tbl_valid = 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
lindex += ref_count;
|
|
Packit Service |
360c39 |
} /* for every leaf block */
|
|
Packit Service |
360c39 |
free(tbl);
|
|
Packit Service |
360c39 |
posix_fadvise(sdp->device_fd, 0, 0, POSIX_FADV_NORMAL);
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int check_eattr_entries(struct gfs2_inode *ip,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh,
|
|
Packit Service |
360c39 |
struct metawalk_fxns *pass)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct gfs2_ea_header *ea_hdr, *ea_hdr_prev = NULL;
|
|
Packit Service |
360c39 |
uint64_t *ea_data_ptr = NULL;
|
|
Packit Service |
360c39 |
int i;
|
|
Packit Service |
360c39 |
int error = 0, err;
|
|
Packit Service |
360c39 |
uint32_t offset = (uint32_t)sizeof(struct gfs2_meta_header);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (!pass->check_eattr_entry)
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
ea_hdr = (struct gfs2_ea_header *)(bh->b_data +
|
|
Packit Service |
360c39 |
sizeof(struct gfs2_meta_header));
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
while (1){
|
|
Packit Service |
360c39 |
if (ea_hdr->ea_type == GFS2_EATYPE_UNUSED)
|
|
Packit Service |
360c39 |
error = 0;
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
error = pass->check_eattr_entry(ip, bh, ea_hdr,
|
|
Packit Service |
360c39 |
ea_hdr_prev,
|
|
Packit Service |
360c39 |
pass->private);
|
|
Packit Service |
360c39 |
if (error < 0) {
|
|
Packit Service |
360c39 |
stack;
|
|
Packit Service |
360c39 |
return -1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (error == 0 && pass->check_eattr_extentry &&
|
|
Packit Service |
360c39 |
ea_hdr->ea_num_ptrs) {
|
|
Packit Service |
360c39 |
uint32_t tot_ealen = 0;
|
|
Packit Service |
360c39 |
struct gfs2_sbd *sdp = ip->i_sbd;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
ea_data_ptr = ((uint64_t *)((char *)ea_hdr +
|
|
Packit Service |
360c39 |
sizeof(struct gfs2_ea_header) +
|
|
Packit Service |
360c39 |
((ea_hdr->ea_name_len + 7) & ~7)));
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* It is possible when a EA is shrunk
|
|
Packit Service |
360c39 |
** to have ea_num_ptrs be greater than
|
|
Packit Service |
360c39 |
** the number required for ** data.
|
|
Packit Service |
360c39 |
** In this case, the EA ** code leaves
|
|
Packit Service |
360c39 |
** the blocks ** there for **
|
|
Packit Service |
360c39 |
** reuse........... */
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
for(i = 0; i < ea_hdr->ea_num_ptrs; i++){
|
|
Packit Service |
360c39 |
err = pass->check_eattr_extentry(ip, i,
|
|
Packit Service |
360c39 |
ea_data_ptr, bh, tot_ealen,
|
|
Packit Service |
360c39 |
ea_hdr, ea_hdr_prev,
|
|
Packit Service |
360c39 |
pass->private);
|
|
Packit Service |
360c39 |
if (err)
|
|
Packit Service |
360c39 |
error = err;
|
|
Packit Service |
360c39 |
tot_ealen += sdp->sd_sb.sb_bsize -
|
|
Packit Service |
360c39 |
sizeof(struct gfs2_meta_header);
|
|
Packit Service |
360c39 |
ea_data_ptr++;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
offset += be32_to_cpu(ea_hdr->ea_rec_len);
|
|
Packit Service |
360c39 |
if (ea_hdr->ea_flags & GFS2_EAFLAG_LAST ||
|
|
Packit Service |
360c39 |
offset >= ip->i_sbd->sd_sb.sb_bsize || ea_hdr->ea_rec_len == 0){
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
ea_hdr_prev = ea_hdr;
|
|
Packit Service |
360c39 |
ea_hdr = (struct gfs2_ea_header *)
|
|
Packit Service |
360c39 |
((char *)(ea_hdr) +
|
|
Packit Service |
360c39 |
be32_to_cpu(ea_hdr->ea_rec_len));
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/**
|
|
Packit Service |
360c39 |
* check_leaf_eattr
|
|
Packit Service |
360c39 |
* @ip: the inode the eattr comes from
|
|
Packit Service |
360c39 |
* @block: block number of the leaf
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* Returns: 0 on success, 1 if removal is needed, -1 on error
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static int check_leaf_eattr(struct gfs2_inode *ip, uint64_t block,
|
|
Packit Service |
360c39 |
uint64_t parent, struct metawalk_fxns *pass)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh = NULL;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (pass->check_eattr_leaf) {
|
|
Packit Service |
360c39 |
int error = 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
log_debug( _("Checking EA leaf block #%llu (0x%llx) for "
|
|
Packit Service |
360c39 |
"inode #%llu (0x%llx).\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
error = pass->check_eattr_leaf(ip, block, parent, &bh,
|
|
Packit Service |
360c39 |
pass->private);
|
|
Packit Service |
360c39 |
if (error < 0) {
|
|
Packit Service |
360c39 |
stack;
|
|
Packit Service |
360c39 |
return -1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (error > 0) {
|
|
Packit Service |
360c39 |
if (bh)
|
|
Packit Service |
360c39 |
brelse(bh);
|
|
Packit Service |
360c39 |
return 1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (bh) {
|
|
Packit Service |
360c39 |
error = check_eattr_entries(ip, bh, pass);
|
|
Packit Service |
360c39 |
brelse(bh);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/**
|
|
Packit Service |
360c39 |
* check_indirect_eattr
|
|
Packit Service |
360c39 |
* @ip: the inode the eattr comes from
|
|
Packit Service |
360c39 |
* @indirect_block
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* Returns: 0 on success -1 on error
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static int check_indirect_eattr(struct gfs2_inode *ip, uint64_t indirect,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *indirect_buf,
|
|
Packit Service |
360c39 |
struct metawalk_fxns *pass)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int error = 0, err;
|
|
Packit Service |
360c39 |
uint64_t *ea_leaf_ptr, *end;
|
|
Packit Service |
360c39 |
uint64_t block;
|
|
Packit Service |
360c39 |
struct gfs2_sbd *sdp = ip->i_sbd;
|
|
Packit Service |
360c39 |
int first_ea_is_bad = 0;
|
|
Packit Service |
360c39 |
uint64_t di_eattr_save = ip->i_di.di_eattr;
|
|
Packit Service |
360c39 |
uint64_t offset = ip->i_sbd->gfs1 ? sizeof(struct gfs_indirect) : sizeof(struct gfs2_meta_header);
|
|
Packit Service |
360c39 |
int leaf_pointers = 0, leaf_pointer_errors = 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
ea_leaf_ptr = (uint64_t *)(indirect_buf->b_data + offset);
|
|
Packit Service |
360c39 |
end = ea_leaf_ptr + ((sdp->sd_sb.sb_bsize - offset) / 8);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
while (*ea_leaf_ptr && (ea_leaf_ptr < end)){
|
|
Packit Service |
360c39 |
block = be64_to_cpu(*ea_leaf_ptr);
|
|
Packit Service |
360c39 |
leaf_pointers++;
|
|
Packit Service |
360c39 |
err = check_leaf_eattr(ip, block, indirect, pass);
|
|
Packit Service |
360c39 |
if (err) {
|
|
Packit Service |
360c39 |
error = err;
|
|
Packit Service |
360c39 |
log_err(_("Error detected in leaf block %lld (0x%llx) "
|
|
Packit Service |
360c39 |
"referenced by indirect block %lld (0x%llx)"
|
|
Packit Service |
360c39 |
".\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)indirect,
|
|
Packit Service |
360c39 |
(unsigned long long)indirect);
|
|
Packit Service |
360c39 |
log_err(_("Subsequent leaf block pointers should be "
|
|
Packit Service |
360c39 |
"cleared.\n"));
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (error) { /* leaf blocks following an error must also be
|
|
Packit Service |
360c39 |
treated as error blocks and cleared. */
|
|
Packit Service |
360c39 |
leaf_pointer_errors++;
|
|
Packit Service |
360c39 |
log_err(_("Pointer to EA leaf block %lld (0x%llx) in "
|
|
Packit Service |
360c39 |
"indirect block %lld (0x%llx) should be "
|
|
Packit Service |
360c39 |
"cleared.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)indirect,
|
|
Packit Service |
360c39 |
(unsigned long long)indirect);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* If the first eattr lead is bad, we can't have a hole, so we
|
|
Packit Service |
360c39 |
have to treat this as an unrecoverable eattr error and
|
|
Packit Service |
360c39 |
delete all eattr info. Calling finish_eattr_indir here
|
|
Packit Service |
360c39 |
causes ip->i_di.di_eattr = 0 and that ensures that
|
|
Packit Service |
360c39 |
subsequent calls to check_leaf_eattr result in the eattr
|
|
Packit Service |
360c39 |
check_leaf_block nuking them all "due to previous errors" */
|
|
Packit Service |
360c39 |
if (leaf_pointers == 1 && leaf_pointer_errors == 1) {
|
|
Packit Service |
360c39 |
first_ea_is_bad = 1;
|
|
Packit Service |
360c39 |
if (pass->finish_eattr_indir)
|
|
Packit Service |
360c39 |
pass->finish_eattr_indir(ip, leaf_pointers,
|
|
Packit Service |
360c39 |
leaf_pointer_errors,
|
|
Packit Service |
360c39 |
pass->private);
|
|
Packit Service |
360c39 |
} else if (leaf_pointer_errors) {
|
|
Packit Service |
360c39 |
/* This is a bit tricky. We can't have eattr holes.
|
|
Packit Service |
360c39 |
So if we have 4 good eattrs, 1 bad eattr and 5 more
|
|
Packit Service |
360c39 |
good ones: GGGGBGGGGG, we need to tell
|
|
Packit Service |
360c39 |
check_leaf_eattr to delete all eattrs after the bad
|
|
Packit Service |
360c39 |
one. So we want: GGGG when we finish. To do that,
|
|
Packit Service |
360c39 |
we set di_eattr to 0 temporarily. */
|
|
Packit Service |
360c39 |
ip->i_di.di_eattr = 0;
|
|
Packit Service |
360c39 |
bmodified(ip->i_bh);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
ea_leaf_ptr++;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* If we temporarily nuked the ea block to prevent checking past
|
|
Packit Service |
360c39 |
a corrupt ea leaf, we need to restore the saved di_eattr block. */
|
|
Packit Service |
360c39 |
if (di_eattr_save != 0)
|
|
Packit Service |
360c39 |
ip->i_di.di_eattr = di_eattr_save;
|
|
Packit Service |
360c39 |
if (pass->finish_eattr_indir) {
|
|
Packit Service |
360c39 |
if (!first_ea_is_bad) {
|
|
Packit Service |
360c39 |
pass->finish_eattr_indir(ip, leaf_pointers,
|
|
Packit Service |
360c39 |
leaf_pointer_errors,
|
|
Packit Service |
360c39 |
pass->private);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (pass->delete_block && leaf_pointer_errors &&
|
|
Packit Service |
360c39 |
leaf_pointer_errors == leaf_pointers) {
|
|
Packit Service |
360c39 |
pass->delete_block(ip, indirect, NULL, "leaf", NULL);
|
|
Packit Service |
360c39 |
error = 1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/**
|
|
Packit Service |
360c39 |
* check_inode_eattr - check the EA's for a single inode
|
|
Packit Service |
360c39 |
* @ip: the inode whose EA to check
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* Returns: 0 on success, -1 on error
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
int check_inode_eattr(struct gfs2_inode *ip, struct metawalk_fxns *pass)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int error = 0;
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *indirect_buf = NULL;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (!ip->i_di.di_eattr)
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT){
|
|
Packit Service |
360c39 |
if (!pass->check_eattr_indir)
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
log_debug( _("Checking EA indirect block #%llu (0x%llx) for "
|
|
Packit Service |
360c39 |
"inode #%llu (0x%llx)..\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_eattr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_eattr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr);
|
|
Packit Service |
360c39 |
error = pass->check_eattr_indir(ip, ip->i_di.di_eattr,
|
|
Packit Service |
360c39 |
ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
&indirect_buf, pass->private);
|
|
Packit Service |
360c39 |
if (!error) {
|
|
Packit Service |
360c39 |
error = check_indirect_eattr(ip, ip->i_di.di_eattr,
|
|
Packit Service |
360c39 |
indirect_buf, pass);
|
|
Packit Service |
360c39 |
if (error)
|
|
Packit Service |
360c39 |
stack;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (indirect_buf)
|
|
Packit Service |
360c39 |
brelse(indirect_buf);
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
error = check_leaf_eattr(ip, ip->i_di.di_eattr,
|
|
Packit Service |
360c39 |
ip->i_di.di_num.no_addr, pass);
|
|
Packit Service |
360c39 |
if (error)
|
|
Packit Service |
360c39 |
stack;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/**
|
|
Packit Service |
360c39 |
* free_metalist - free all metadata on a multi-level metadata list
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static void free_metalist(struct gfs2_inode *ip, osi_list_t *mlp)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int i;
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *nbh;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
for (i = 0; i < GFS2_MAX_META_HEIGHT; i++) {
|
|
Packit Service |
360c39 |
osi_list_t *list;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
list = &mlp[i];
|
|
Packit Service |
360c39 |
while (!osi_list_empty(list)) {
|
|
Packit Service |
360c39 |
nbh = osi_list_entry(list->next,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head, b_altlist);
|
|
Packit Service |
360c39 |
if (nbh == ip->i_bh)
|
|
Packit Service |
360c39 |
osi_list_del_init(&nbh->b_altlist);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
brelse(nbh);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static void file_ra(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
|
|
Packit Service |
360c39 |
int head_size, int maxptrs, int h)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct gfs2_sbd *sdp = ip->i_sbd;
|
|
Packit Service |
360c39 |
uint64_t *p, sblock = 0, block;
|
|
Packit Service |
360c39 |
int extlen = 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (h + 2 == ip->i_di.di_height) {
|
|
Packit Service |
360c39 |
p = (uint64_t *)(bh->b_data + head_size);
|
|
Packit Service |
360c39 |
if (*p && *(p + 1)) {
|
|
Packit Service |
360c39 |
sblock = be64_to_cpu(*p);
|
|
Packit Service |
360c39 |
p++;
|
|
Packit Service |
360c39 |
block = be64_to_cpu(*p);
|
|
Packit Service |
360c39 |
extlen = block - sblock;
|
|
Packit Service |
360c39 |
if (extlen > 1 && extlen <= maxptrs) {
|
|
Packit Service |
360c39 |
posix_fadvise(sdp->device_fd,
|
|
Packit Service |
360c39 |
sblock * sdp->bsize,
|
|
Packit Service |
360c39 |
(extlen + 1) * sdp->bsize,
|
|
Packit Service |
360c39 |
POSIX_FADV_WILLNEED);
|
|
Packit Service |
360c39 |
return;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
extlen = 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
for (p = (uint64_t *)(bh->b_data + head_size);
|
|
Packit Service |
360c39 |
p < (uint64_t *)(bh->b_data + sdp->bsize); p++) {
|
|
Packit Service |
360c39 |
if (*p) {
|
|
Packit Service |
360c39 |
if (!sblock) {
|
|
Packit Service |
360c39 |
sblock = be64_to_cpu(*p);
|
|
Packit Service |
360c39 |
extlen = 1;
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
block = be64_to_cpu(*p);
|
|
Packit Service |
360c39 |
if (block == sblock + extlen) {
|
|
Packit Service |
360c39 |
extlen++;
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (extlen && sblock) {
|
|
Packit Service |
360c39 |
if (extlen > 1)
|
|
Packit Service |
360c39 |
extlen--;
|
|
Packit Service |
360c39 |
posix_fadvise(sdp->device_fd, sblock * sdp->bsize,
|
|
Packit Service |
360c39 |
extlen * sdp->bsize,
|
|
Packit Service |
360c39 |
POSIX_FADV_WILLNEED);
|
|
Packit Service |
360c39 |
extlen = 0;
|
|
Packit Service |
360c39 |
p--;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (extlen)
|
|
Packit Service |
360c39 |
posix_fadvise(sdp->device_fd, sblock * sdp->bsize,
|
|
Packit Service |
360c39 |
extlen * sdp->bsize, POSIX_FADV_WILLNEED);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/**
|
|
Packit Service |
360c39 |
* build_and_check_metalist - check a bunch of indirect blocks
|
|
Packit Service |
360c39 |
* This includes hash table blocks for directories
|
|
Packit Service |
360c39 |
* which are technically "data" in the bitmap.
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* Returns: 0 - all is well, process the blocks this metadata references
|
|
Packit Service |
360c39 |
* 1 - something went wrong, but process the sub-blocks anyway
|
|
Packit Service |
360c39 |
* -1 - something went wrong, so don't process the sub-blocks
|
|
Packit Service |
360c39 |
* @ip:
|
|
Packit Service |
360c39 |
* @mlp:
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static int build_and_check_metalist(struct gfs2_inode *ip, osi_list_t *mlp,
|
|
Packit Service |
360c39 |
struct metawalk_fxns *pass)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
uint32_t height = ip->i_di.di_height;
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh, *nbh, *metabh = ip->i_bh;
|
|
Packit Service |
360c39 |
osi_list_t *prev_list, *cur_list, *tmp;
|
|
Packit Service |
360c39 |
int h, head_size, iblk_type;
|
|
Packit Service |
360c39 |
uint64_t *ptr, block, *undoptr;
|
|
Packit Service |
360c39 |
int error, was_duplicate, is_valid;
|
|
Packit Service |
360c39 |
int maxptrs;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
osi_list_add(&metabh->b_altlist, &mlp[0]);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Directories are special. Their 'data' is the hash table, which is
|
|
Packit Service |
360c39 |
basically an indirect block list. Their height is not important
|
|
Packit Service |
360c39 |
because it checks everything through the hash table using
|
|
Packit Service |
360c39 |
"depth" field calculations. However, we still have to check the
|
|
Packit Service |
360c39 |
indirect blocks, even if the height == 1. */
|
|
Packit Service |
360c39 |
if (is_dir(&ip->i_di, ip->i_sbd->gfs1))
|
|
Packit Service |
360c39 |
height++;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* if (<there are no indirect blocks to check>) */
|
|
Packit Service |
360c39 |
if (height < 2)
|
|
Packit Service |
360c39 |
return meta_is_good;
|
|
Packit Service |
360c39 |
for (h = 1; h < height; h++) {
|
|
Packit Service |
360c39 |
if (h > 1) {
|
|
Packit Service |
360c39 |
if (is_dir(&ip->i_di, ip->i_sbd->gfs1) &&
|
|
Packit Service |
360c39 |
h == ip->i_di.di_height + 1)
|
|
Packit Service |
360c39 |
iblk_type = GFS2_METATYPE_JD;
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
iblk_type = GFS2_METATYPE_IN;
|
|
Packit Service |
360c39 |
if (ip->i_sbd->gfs1) {
|
|
Packit Service |
360c39 |
head_size = sizeof(struct gfs_indirect);
|
|
Packit Service |
360c39 |
maxptrs = (ip->i_sbd->bsize - head_size) /
|
|
Packit Service |
360c39 |
sizeof(uint64_t);
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
head_size = sizeof(struct gfs2_meta_header);
|
|
Packit Service |
360c39 |
maxptrs = ip->i_sbd->sd_inptrs;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
iblk_type = GFS2_METATYPE_DI;
|
|
Packit Service |
360c39 |
head_size = sizeof(struct gfs2_dinode);
|
|
Packit Service |
360c39 |
maxptrs = ip->i_sbd->sd_diptrs;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
prev_list = &mlp[h - 1];
|
|
Packit Service |
360c39 |
cur_list = &mlp[h];
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
for (tmp = prev_list->next; tmp != prev_list; tmp = tmp->next){
|
|
Packit Service |
360c39 |
bh = osi_list_entry(tmp, struct gfs2_buffer_head,
|
|
Packit Service |
360c39 |
b_altlist);
|
|
Packit Service |
360c39 |
if (gfs2_check_meta(bh, iblk_type)) {
|
|
Packit Service |
360c39 |
if (pass->invalid_meta_is_fatal)
|
|
Packit Service |
360c39 |
return meta_error;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (pass->readahead)
|
|
Packit Service |
360c39 |
file_ra(ip, bh, head_size, maxptrs, h);
|
|
Packit Service |
360c39 |
/* Now check the metadata itself */
|
|
Packit Service |
360c39 |
for (ptr = (uint64_t *)(bh->b_data + head_size);
|
|
Packit Service |
360c39 |
(char *)ptr < (bh->b_data + ip->i_sbd->bsize);
|
|
Packit Service |
360c39 |
ptr++) {
|
|
Packit Service |
360c39 |
if (skip_this_pass || fsck_abort) {
|
|
Packit Service |
360c39 |
free_metalist(ip, mlp);
|
|
Packit Service |
360c39 |
return meta_is_good;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
nbh = NULL;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (!*ptr)
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
block = be64_to_cpu(*ptr);
|
|
Packit Service |
360c39 |
was_duplicate = 0;
|
|
Packit Service |
360c39 |
error = pass->check_metalist(ip, block, &nbh,
|
|
Packit Service |
360c39 |
h, &is_valid,
|
|
Packit Service |
360c39 |
&was_duplicate,
|
|
Packit Service |
360c39 |
pass->private);
|
|
Packit Service |
360c39 |
/* check_metalist should hold any buffers
|
|
Packit Service |
360c39 |
it gets with "bread". */
|
|
Packit Service |
360c39 |
if (error == meta_error) {
|
|
Packit Service |
360c39 |
stack;
|
|
Packit Service |
360c39 |
log_info(_("\nSerious metadata "
|
|
Packit Service |
360c39 |
"error on block %llu "
|
|
Packit Service |
360c39 |
"(0x%llx).\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block);
|
|
Packit Service |
360c39 |
goto error_undo;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (error == meta_skip_further) {
|
|
Packit Service |
360c39 |
log_info(_("\nUnrecoverable metadata "
|
|
Packit Service |
360c39 |
"error on block %llu "
|
|
Packit Service |
360c39 |
"(0x%llx). Further metadata"
|
|
Packit Service |
360c39 |
" will be skipped.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block);
|
|
Packit Service |
360c39 |
goto error_undo;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (!is_valid) {
|
|
Packit Service |
360c39 |
log_debug( _("Skipping rejected block "
|
|
Packit Service |
360c39 |
"%llu (0x%llx)\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block);
|
|
Packit Service |
360c39 |
if (pass->invalid_meta_is_fatal) {
|
|
Packit Service |
360c39 |
error = meta_error;
|
|
Packit Service |
360c39 |
goto error_undo;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* Note that there's a special case in which
|
|
Packit Service |
360c39 |
we need to process the metadata block, even
|
|
Packit Service |
360c39 |
if it was a duplicate. That's for cases
|
|
Packit Service |
360c39 |
where we deleted the last reference as
|
|
Packit Service |
360c39 |
metadata. */
|
|
Packit Service |
360c39 |
if (was_duplicate) {
|
|
Packit Service |
360c39 |
log_debug( _("Skipping duplicate %llu "
|
|
Packit Service |
360c39 |
"(0x%llx)\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block);
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (!valid_block_ip(ip, block)) {
|
|
Packit Service |
360c39 |
log_debug( _("Skipping invalid block "
|
|
Packit Service |
360c39 |
"%lld (0x%llx)\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block);
|
|
Packit Service |
360c39 |
if (pass->invalid_meta_is_fatal) {
|
|
Packit Service |
360c39 |
error = meta_error;
|
|
Packit Service |
360c39 |
goto error_undo;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (!nbh)
|
|
Packit Service |
360c39 |
nbh = bread(ip->i_sbd, block);
|
|
Packit Service |
360c39 |
osi_list_add_prev(&nbh->b_altlist, cur_list);
|
|
Packit Service |
360c39 |
} /* for all data on the indirect block */
|
|
Packit Service |
360c39 |
} /* for blocks at that height */
|
|
Packit Service |
360c39 |
} /* for height */
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
error_undo: /* undo what we've done so far for this block */
|
|
Packit Service |
360c39 |
if (pass->undo_check_meta == NULL)
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
log_info(_("Undoing the work we did before the error on block %llu "
|
|
Packit Service |
360c39 |
"(0x%llx).\n"), (unsigned long long)bh->b_blocknr,
|
|
Packit Service |
360c39 |
(unsigned long long)bh->b_blocknr);
|
|
Packit Service |
360c39 |
for (undoptr = (uint64_t *)(bh->b_data + head_size); undoptr < ptr &&
|
|
Packit Service |
360c39 |
(char *)undoptr < (bh->b_data + ip->i_sbd->bsize);
|
|
Packit Service |
360c39 |
undoptr++) {
|
|
Packit Service |
360c39 |
if (!*undoptr)
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
block = be64_to_cpu(*undoptr);
|
|
Packit Service |
360c39 |
pass->undo_check_meta(ip, block, h, pass->private);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/**
|
|
Packit Service |
360c39 |
* check_data - check all data pointers for a given buffer
|
|
Packit Service |
360c39 |
* This does not include "data" blocks that are really
|
|
Packit Service |
360c39 |
* hash table blocks for directories.
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* @ip:
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* returns: +ENOENT if there are too many bad pointers
|
|
Packit Service |
360c39 |
* -1 if a more serious error occurred.
|
|
Packit Service |
360c39 |
* 0 if no errors occurred
|
|
Packit Service |
360c39 |
* 1 if errors were found and corrected
|
|
Packit Service |
360c39 |
* 2 (ENOENT) is there were too many bad pointers
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static int check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh, int head_size,
|
|
Packit Service |
360c39 |
uint64_t *blks_checked, struct error_block *error_blk)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int error = 0, rc = 0;
|
|
Packit Service |
360c39 |
uint64_t block, *ptr;
|
|
Packit Service |
360c39 |
uint64_t *ptr_start = (uint64_t *)(bh->b_data + head_size);
|
|
Packit Service |
360c39 |
char *ptr_end = (bh->b_data + ip->i_sbd->bsize);
|
|
Packit Service |
360c39 |
uint64_t metablock = bh->b_blocknr;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* If there isn't much pointer corruption check the pointers */
|
|
Packit Service |
360c39 |
log_debug(_("\nProcessing data blocks for inode 0x%llx, metadata "
|
|
Packit Service |
360c39 |
"block 0x%llx.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)bh->b_blocknr);
|
|
Packit Service |
360c39 |
for (ptr = ptr_start ; (char *)ptr < ptr_end && !fsck_abort; ptr++) {
|
|
Packit Service |
360c39 |
if (!*ptr)
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (skip_this_pass || fsck_abort)
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
block = be64_to_cpu(*ptr);
|
|
Packit Service |
360c39 |
/* It's important that we don't call valid_block() and
|
|
Packit Service |
360c39 |
bypass calling check_data on invalid blocks because that
|
|
Packit Service |
360c39 |
would defeat the rangecheck_block related functions in
|
|
Packit Service |
360c39 |
pass1. Therefore the individual check_data functions
|
|
Packit Service |
360c39 |
should do a range check. */
|
|
Packit Service |
360c39 |
rc = pass->check_data(ip, metablock, block, pass->private,
|
|
Packit Service |
360c39 |
bh, ptr);
|
|
Packit Service |
360c39 |
if (rc && (!error || (rc < error))) {
|
|
Packit Service |
360c39 |
log_info("\n");
|
|
Packit Service |
360c39 |
if (rc < 0) {
|
|
Packit Service |
360c39 |
/* A fatal error trumps a non-fatal one. */
|
|
Packit Service |
360c39 |
if ((error_blk->errblk == 0) ||
|
|
Packit Service |
360c39 |
(rc < error)) {
|
|
Packit Service |
360c39 |
log_debug(_("Fatal error on metadata "
|
|
Packit Service |
360c39 |
"block 0x%llx, offset "
|
|
Packit Service |
360c39 |
"0x%x, referencing block "
|
|
Packit Service |
360c39 |
"0x%llx preempts non-fatal"
|
|
Packit Service |
360c39 |
" error on block 0x%llx\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)metablock,
|
|
Packit Service |
360c39 |
(int)(ptr - ptr_start),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)error_blk->errblk);
|
|
Packit Service |
360c39 |
error_blk->metablk = metablock;
|
|
Packit Service |
360c39 |
error_blk->metaoff = ptr - ptr_start;
|
|
Packit Service |
360c39 |
error_blk->errblk = block;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
log_info(_("Unrecoverable "));
|
|
Packit Service |
360c39 |
} else { /* nonfatal error */
|
|
Packit Service |
360c39 |
if (error_blk->errblk == 0) {
|
|
Packit Service |
360c39 |
error_blk->metablk = metablock;
|
|
Packit Service |
360c39 |
error_blk->metaoff = ptr - ptr_start;
|
|
Packit Service |
360c39 |
error_blk->errblk = block;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
log_info(_("data block error %d on metadata block "
|
|
Packit Service |
360c39 |
"%lld (0x%llx), offset %d (0x%x), "
|
|
Packit Service |
360c39 |
"referencing data block %lld (0x%llx).\n"),
|
|
Packit Service |
360c39 |
rc, (unsigned long long)metablock,
|
|
Packit Service |
360c39 |
(unsigned long long)metablock,
|
|
Packit Service |
360c39 |
(int)(ptr - ptr_start),
|
|
Packit Service |
360c39 |
(int)(ptr - ptr_start),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block);
|
|
Packit Service |
360c39 |
error = rc;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (rc < 0)
|
|
Packit Service |
360c39 |
return rc;
|
|
Packit Service |
360c39 |
(*blks_checked)++;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int undo_check_data(struct gfs2_inode *ip, struct metawalk_fxns *pass,
|
|
Packit Service |
360c39 |
uint64_t metablock,
|
|
Packit Service |
360c39 |
uint64_t *ptr_start, char *ptr_end,
|
|
Packit Service |
360c39 |
struct error_block *error_blk, int error)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int rc = 0;
|
|
Packit Service |
360c39 |
uint64_t block, *ptr;
|
|
Packit Service |
360c39 |
int found_error_blk = 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* If there isn't much pointer corruption check the pointers */
|
|
Packit Service |
360c39 |
for (ptr = ptr_start ; (char *)ptr < ptr_end && !fsck_abort; ptr++) {
|
|
Packit Service |
360c39 |
if (!*ptr)
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (skip_this_pass || fsck_abort)
|
|
Packit Service |
360c39 |
return 1;
|
|
Packit Service |
360c39 |
block = be64_to_cpu(*ptr);
|
|
Packit Service |
360c39 |
if (metablock == error_blk->metablk &&
|
|
Packit Service |
360c39 |
(ptr - ptr_start == error_blk->metaoff) &&
|
|
Packit Service |
360c39 |
block == error_blk->errblk) {
|
|
Packit Service |
360c39 |
if (error < 0) { /* A fatal error that stopped it? */
|
|
Packit Service |
360c39 |
log_debug(_("Stopping the undo process: "
|
|
Packit Service |
360c39 |
"fatal error block 0x%llx was "
|
|
Packit Service |
360c39 |
"found at metadata block 0x%llx,"
|
|
Packit Service |
360c39 |
"offset 0x%x.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)error_blk->errblk,
|
|
Packit Service |
360c39 |
(unsigned long long)error_blk->metablk,
|
|
Packit Service |
360c39 |
error_blk->metaoff);
|
|
Packit Service |
360c39 |
return 1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
found_error_blk = 1;
|
|
Packit Service |
360c39 |
log_debug(_("The non-fatal error block 0x%llx was "
|
|
Packit Service |
360c39 |
"found at metadata block 0x%llx, offset "
|
|
Packit Service |
360c39 |
"0x%d, but undo processing will continue "
|
|
Packit Service |
360c39 |
"until the end of this metadata block.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)error_blk->errblk,
|
|
Packit Service |
360c39 |
(unsigned long long)error_blk->metablk,
|
|
Packit Service |
360c39 |
error_blk->metaoff);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
rc = pass->undo_check_data(ip, block, pass->private);
|
|
Packit Service |
360c39 |
if (rc < 0)
|
|
Packit Service |
360c39 |
return rc;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return found_error_blk;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int hdr_size(struct gfs2_buffer_head *bh, int height)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
if (height > 1) {
|
|
Packit Service |
360c39 |
if (gfs2_check_meta(bh, GFS2_METATYPE_IN))
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
if (bh->sdp->gfs1)
|
|
Packit Service |
360c39 |
return sizeof(struct gfs_indirect);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
return sizeof(struct gfs2_meta_header);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* if this isn't really a dinode, skip it */
|
|
Packit Service |
360c39 |
if (gfs2_check_meta(bh, GFS2_METATYPE_DI))
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
return sizeof(struct gfs2_dinode);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/**
|
|
Packit Service |
360c39 |
* check_metatree
|
|
Packit Service |
360c39 |
* @ip: inode structure in memory
|
|
Packit Service |
360c39 |
* @pass: structure passed in from caller to determine the sub-functions
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
int check_metatree(struct gfs2_inode *ip, struct metawalk_fxns *pass)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
osi_list_t metalist[GFS2_MAX_META_HEIGHT];
|
|
Packit Service |
360c39 |
osi_list_t *list, *tmp;
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh;
|
|
Packit Service |
360c39 |
uint32_t height = ip->i_di.di_height;
|
|
Packit Service |
360c39 |
int i, head_size;
|
|
Packit Service |
360c39 |
uint64_t blks_checked = 0;
|
|
Packit Service |
360c39 |
int error, rc;
|
|
Packit Service |
360c39 |
int metadata_clean = 0;
|
|
Packit Service |
360c39 |
struct error_block error_blk = {0, 0, 0};
|
|
Packit Service |
360c39 |
int hit_error_blk = 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (!height && !is_dir(&ip->i_di, ip->i_sbd->gfs1))
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
for (i = 0; i < GFS2_MAX_META_HEIGHT; i++)
|
|
Packit Service |
360c39 |
osi_list_init(&metalist[i]);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* create and check the metadata list for each height */
|
|
Packit Service |
360c39 |
error = build_and_check_metalist(ip, &metalist[0], pass);
|
|
Packit Service |
360c39 |
if (error) {
|
|
Packit Service |
360c39 |
stack;
|
|
Packit Service |
360c39 |
goto undo_metalist;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
metadata_clean = 1;
|
|
Packit Service |
360c39 |
/* For directories, we've already checked the "data" blocks which
|
|
Packit Service |
360c39 |
* comprise the directory hash table, so we perform the directory
|
|
Packit Service |
360c39 |
* checks and exit. */
|
|
Packit Service |
360c39 |
if (is_dir(&ip->i_di, ip->i_sbd->gfs1)) {
|
|
Packit Service |
360c39 |
if (!(ip->i_di.di_flags & GFS2_DIF_EXHASH))
|
|
Packit Service |
360c39 |
goto out;
|
|
Packit Service |
360c39 |
/* check validity of leaf blocks and leaf chains */
|
|
Packit Service |
360c39 |
error = check_leaf_blks(ip, pass);
|
|
Packit Service |
360c39 |
if (error)
|
|
Packit Service |
360c39 |
goto undo_metalist;
|
|
Packit Service |
360c39 |
goto out;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* check data blocks */
|
|
Packit Service |
360c39 |
list = &metalist[height - 1];
|
|
Packit Service |
360c39 |
if (ip->i_di.di_blocks > COMFORTABLE_BLKS)
|
|
Packit Service |
360c39 |
last_reported_fblock = -10000000;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
for (tmp = list->next; !error && tmp != list; tmp = tmp->next) {
|
|
Packit Service |
360c39 |
if (fsck_abort) {
|
|
Packit Service |
360c39 |
free_metalist(ip, &metalist[0]);
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
bh = osi_list_entry(tmp, struct gfs2_buffer_head, b_altlist);
|
|
Packit Service |
360c39 |
head_size = hdr_size(bh, height);
|
|
Packit Service |
360c39 |
if (!head_size)
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (pass->check_data)
|
|
Packit Service |
360c39 |
error = check_data(ip, pass, bh, head_size,
|
|
Packit Service |
360c39 |
&blks_checked, &error_blk);
|
|
Packit Service |
360c39 |
if (pass->big_file_msg && ip->i_di.di_blocks > COMFORTABLE_BLKS)
|
|
Packit Service |
360c39 |
pass->big_file_msg(ip, blks_checked);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (pass->big_file_msg && ip->i_di.di_blocks > COMFORTABLE_BLKS) {
|
|
Packit Service |
360c39 |
log_notice( _("\rLarge file at %lld (0x%llx) - 100 percent "
|
|
Packit Service |
360c39 |
"complete. "
|
|
Packit Service |
360c39 |
"\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr);
|
|
Packit Service |
360c39 |
fflush(stdout);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
undo_metalist:
|
|
Packit Service |
360c39 |
if (!error)
|
|
Packit Service |
360c39 |
goto out;
|
|
Packit Service |
360c39 |
log_err( _("Error: inode %llu (0x%llx) had unrecoverable errors at "
|
|
Packit Service |
360c39 |
"metadata block %lld (0x%llx), offset %d (0x%x), block "
|
|
Packit Service |
360c39 |
"%lld (0x%llx).\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)error_blk.metablk,
|
|
Packit Service |
360c39 |
(unsigned long long)error_blk.metablk,
|
|
Packit Service |
360c39 |
error_blk.metaoff, error_blk.metaoff,
|
|
Packit Service |
360c39 |
(unsigned long long)error_blk.errblk,
|
|
Packit Service |
360c39 |
(unsigned long long)error_blk.errblk);
|
|
Packit Service |
360c39 |
if (!query( _("Remove the invalid inode? (y/n) "))) {
|
|
Packit Service |
360c39 |
free_metalist(ip, &metalist[0]);
|
|
Packit Service |
360c39 |
log_err(_("Invalid inode not deleted.\n"));
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
for (i = 0; pass->undo_check_meta && i < height; i++) {
|
|
Packit Service |
360c39 |
while (!osi_list_empty(&metalist[i])) {
|
|
Packit Service |
360c39 |
list = &metalist[i];
|
|
Packit Service |
360c39 |
bh = osi_list_entry(list->next,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head,
|
|
Packit Service |
360c39 |
b_altlist);
|
|
Packit Service |
360c39 |
log_err(_("Undoing metadata work for block %llu "
|
|
Packit Service |
360c39 |
"(0x%llx)\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)bh->b_blocknr,
|
|
Packit Service |
360c39 |
(unsigned long long)bh->b_blocknr);
|
|
Packit Service |
360c39 |
if (i)
|
|
Packit Service |
360c39 |
rc = pass->undo_check_meta(ip, bh->b_blocknr,
|
|
Packit Service |
360c39 |
i, pass->private);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
rc = 0;
|
|
Packit Service |
360c39 |
if (metadata_clean && rc == 0 && i == height - 1 &&
|
|
Packit Service |
360c39 |
!hit_error_blk) {
|
|
Packit Service |
360c39 |
head_size = hdr_size(bh, height);
|
|
Packit Service |
360c39 |
if (head_size) {
|
|
Packit Service |
360c39 |
rc = undo_check_data(ip, pass,
|
|
Packit Service |
360c39 |
bh->b_blocknr,
|
|
Packit Service |
360c39 |
(uint64_t *)
|
|
Packit Service |
360c39 |
(bh->b_data + head_size),
|
|
Packit Service |
360c39 |
(bh->b_data + ip->i_sbd->bsize),
|
|
Packit Service |
360c39 |
&error_blk,
|
|
Packit Service |
360c39 |
error);
|
|
Packit Service |
360c39 |
if (rc > 0) {
|
|
Packit Service |
360c39 |
hit_error_blk = 1;
|
|
Packit Service |
360c39 |
log_err("Reached the error "
|
|
Packit Service |
360c39 |
"block undoing work "
|
|
Packit Service |
360c39 |
"for inode %lld "
|
|
Packit Service |
360c39 |
"(0x%llx).\n",
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
(unsigned long long)ip->i_di.di_num.no_addr);
|
|
Packit Service |
360c39 |
rc = 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (bh == ip->i_bh)
|
|
Packit Service |
360c39 |
osi_list_del(&bh->b_altlist);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
brelse(bh);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* There may be leftover duplicate records, so we need to delete them.
|
|
Packit Service |
360c39 |
For example, if a metadata block was found to be a duplicate, we
|
|
Packit Service |
360c39 |
may not have added it to the metalist, which means it's not there
|
|
Packit Service |
360c39 |
to undo. */
|
|
Packit Service |
360c39 |
delete_all_dups(ip);
|
|
Packit Service |
360c39 |
/* Set the dinode as "bad" so it gets deleted */
|
|
Packit Service |
360c39 |
fsck_bitmap_set(ip, ip->i_di.di_num.no_addr, _("corrupt"),
|
|
Packit Service |
360c39 |
GFS2_BLKST_FREE);
|
|
Packit Service |
360c39 |
log_err(_("The corrupt inode was invalidated.\n"));
|
|
Packit Service |
360c39 |
out:
|
|
Packit Service |
360c39 |
free_metalist(ip, &metalist[0]);
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Checks stuffed inode directories */
|
|
Packit Service |
360c39 |
int check_linear_dir(struct gfs2_inode *ip, struct gfs2_buffer_head *bh,
|
|
Packit Service |
360c39 |
struct metawalk_fxns *pass)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int error = 0;
|
|
Packit Service |
360c39 |
uint32_t count = 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
error = check_entries(ip, bh, DIR_LINEAR, &count, 0, pass);
|
|
Packit Service |
360c39 |
if (error < 0) {
|
|
Packit Service |
360c39 |
stack;
|
|
Packit Service |
360c39 |
return -1;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
int check_dir(struct gfs2_sbd *sdp, struct gfs2_inode *ip, struct metawalk_fxns *pass)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int error = 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (ip->i_di.di_flags & GFS2_DIF_EXHASH)
|
|
Packit Service |
360c39 |
error = check_leaf_blks(ip, pass);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
error = check_linear_dir(ip, ip->i_bh, pass);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (error < 0)
|
|
Packit Service |
360c39 |
stack;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|