|
Packit |
6ef888 |
#include "clusterautoconfig.h"
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
#include <inttypes.h>
|
|
Packit |
6ef888 |
#include <stdlib.h>
|
|
Packit |
6ef888 |
#include <stdio.h>
|
|
Packit |
6ef888 |
#include <libintl.h>
|
|
Packit |
6ef888 |
#define _(String) gettext(String)
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
#include <logging.h>
|
|
Packit |
6ef888 |
#include "libgfs2.h"
|
|
Packit |
6ef888 |
#include "fsck.h"
|
|
Packit |
6ef888 |
#include "link.h"
|
|
Packit |
6ef888 |
#include "lost_n_found.h"
|
|
Packit |
6ef888 |
#include "inode_hash.h"
|
|
Packit |
6ef888 |
#include "metawalk.h"
|
|
Packit |
6ef888 |
#include "util.h"
|
|
Packit |
6ef888 |
#include "afterpass1_common.h"
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
struct metawalk_fxns pass4_fxns_delete = {
|
|
Packit |
6ef888 |
.private = NULL,
|
|
Packit |
6ef888 |
.check_metalist = delete_metadata,
|
|
Packit |
6ef888 |
.check_data = delete_data,
|
|
Packit |
6ef888 |
.check_eattr_indir = delete_eattr_indir,
|
|
Packit |
6ef888 |
.check_eattr_leaf = delete_eattr_leaf,
|
|
Packit |
6ef888 |
};
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* Updates the link count of an inode to what the fsck has seen for
|
|
Packit |
6ef888 |
* link count */
|
|
Packit |
6ef888 |
static int fix_link_count(uint32_t counted_links, struct gfs2_inode *ip)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
log_info( _("Fixing inode link count (%d->%d) for %llu (0x%llx) \n"),
|
|
Packit |
6ef888 |
ip->i_di.di_nlink, counted_links,
|
|
Packit |
6ef888 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit |
6ef888 |
(unsigned long long)ip->i_di.di_num.no_addr);
|
|
Packit |
6ef888 |
if (ip->i_di.di_nlink == counted_links)
|
|
Packit |
6ef888 |
return 0;
|
|
Packit |
6ef888 |
ip->i_di.di_nlink = counted_links;
|
|
Packit |
6ef888 |
bmodified(ip->i_bh);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
log_debug( _("Changing inode %llu (0x%llx) to have %u links\n"),
|
|
Packit |
6ef888 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit |
6ef888 |
(unsigned long long)ip->i_di.di_num.no_addr, counted_links);
|
|
Packit |
6ef888 |
return 0;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/**
|
|
Packit |
6ef888 |
* handle_unlinked - handle an unlinked dinode
|
|
Packit |
6ef888 |
*
|
|
Packit |
6ef888 |
* Note: We need to pass in *counted_links here, not counted_links because
|
|
Packit |
6ef888 |
* add_inode_to_lf may be called here, and that might change the original
|
|
Packit |
6ef888 |
* value, whether that's in the dirtree or the inodetree.
|
|
Packit |
6ef888 |
*
|
|
Packit |
6ef888 |
* Returns: 1 if caller should do "continue", 0 if not.
|
|
Packit |
6ef888 |
*/
|
|
Packit |
6ef888 |
static int handle_unlinked(struct gfs2_sbd *sdp, uint64_t no_addr,
|
|
Packit |
6ef888 |
uint32_t *counted_links, int *lf_addition)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
struct gfs2_inode *ip;
|
|
Packit |
6ef888 |
int q;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
log_err( _("Found unlinked inode at %llu (0x%llx)\n"),
|
|
Packit |
6ef888 |
(unsigned long long)no_addr, (unsigned long long)no_addr);
|
|
Packit |
6ef888 |
q = bitmap_type(sdp, no_addr);
|
|
Packit |
6ef888 |
if (q == GFS2_BLKST_FREE) {
|
|
Packit |
6ef888 |
log_err( _("Unlinked inode %llu (0x%llx) contains bad "
|
|
Packit |
6ef888 |
"blocks\n"), (unsigned long long)no_addr,
|
|
Packit |
6ef888 |
(unsigned long long)no_addr);
|
|
Packit |
6ef888 |
if (query(_("Delete unlinked inode with bad blocks? "
|
|
Packit |
6ef888 |
"(y/n) "))) {
|
|
Packit |
6ef888 |
ip = fsck_load_inode(sdp, no_addr);
|
|
Packit |
6ef888 |
check_inode_eattr(ip, &pass4_fxns_delete);
|
|
Packit |
6ef888 |
check_metatree(ip, &pass4_fxns_delete);
|
|
Packit |
6ef888 |
fsck_bitmap_set(ip, no_addr, _("bad unlinked"),
|
|
Packit |
6ef888 |
GFS2_BLKST_FREE);
|
|
Packit |
6ef888 |
fsck_inode_put(&ip);
|
|
Packit |
6ef888 |
return 1;
|
|
Packit |
6ef888 |
} else {
|
|
Packit |
6ef888 |
log_err( _("Unlinked inode with bad blocks not "
|
|
Packit |
6ef888 |
"cleared\n"));
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
if (q != GFS2_BLKST_DINODE) {
|
|
Packit |
6ef888 |
log_err( _("Unlinked block %lld (0x%llx) marked as inode is "
|
|
Packit |
6ef888 |
"not an inode (%d)\n"),
|
|
Packit |
6ef888 |
(unsigned long long)no_addr,
|
|
Packit |
6ef888 |
(unsigned long long)no_addr, q);
|
|
Packit |
6ef888 |
ip = fsck_load_inode(sdp, no_addr);
|
|
Packit |
6ef888 |
if (query(_("Delete unlinked inode? (y/n) "))) {
|
|
Packit |
6ef888 |
check_inode_eattr(ip, &pass4_fxns_delete);
|
|
Packit |
6ef888 |
check_metatree(ip, &pass4_fxns_delete);
|
|
Packit |
6ef888 |
fsck_bitmap_set(ip, no_addr, _("invalid unlinked"),
|
|
Packit |
6ef888 |
GFS2_BLKST_FREE);
|
|
Packit |
6ef888 |
fsck_inode_put(&ip);
|
|
Packit |
6ef888 |
log_err( _("The inode was deleted\n"));
|
|
Packit |
6ef888 |
} else {
|
|
Packit |
6ef888 |
log_err( _("The inode was not deleted\n"));
|
|
Packit |
6ef888 |
fsck_inode_put(&ip);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
return 1;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
ip = fsck_load_inode(sdp, no_addr);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* We don't want to clear zero-size files with eattrs - there might be
|
|
Packit |
6ef888 |
relevent info in them. */
|
|
Packit |
6ef888 |
if (!ip->i_di.di_size && !ip->i_di.di_eattr){
|
|
Packit |
6ef888 |
log_err( _("Unlinked inode has zero size\n"));
|
|
Packit |
6ef888 |
if (query(_("Clear zero-size unlinked inode? (y/n) "))) {
|
|
Packit |
6ef888 |
fsck_bitmap_set(ip, no_addr, _("unlinked zero-length"),
|
|
Packit |
6ef888 |
GFS2_BLKST_FREE);
|
|
Packit |
6ef888 |
fsck_inode_put(&ip);
|
|
Packit |
6ef888 |
return 1;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
if (query( _("Add unlinked inode to lost+found? (y/n)"))) {
|
|
Packit |
6ef888 |
if (add_inode_to_lf(ip)) {
|
|
Packit |
6ef888 |
stack;
|
|
Packit |
6ef888 |
fsck_inode_put(&ip);
|
|
Packit |
6ef888 |
return -1;
|
|
Packit |
6ef888 |
} else {
|
|
Packit |
6ef888 |
fix_link_count(*counted_links, ip);
|
|
Packit |
6ef888 |
*lf_addition = 1;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
} else
|
|
Packit |
6ef888 |
log_err( _("Unlinked inode left unlinked\n"));
|
|
Packit |
6ef888 |
fsck_inode_put(&ip);
|
|
Packit |
6ef888 |
return 0;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
static void handle_inconsist(struct gfs2_sbd *sdp, uint64_t no_addr,
|
|
Packit |
6ef888 |
uint32_t *di_nlink, uint32_t counted_links)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
log_err( _("Link count inconsistent for inode %llu"
|
|
Packit |
6ef888 |
" (0x%llx) has %u but fsck found %u.\n"),
|
|
Packit |
6ef888 |
(unsigned long long)no_addr, (unsigned long long)no_addr,
|
|
Packit |
6ef888 |
*di_nlink, counted_links);
|
|
Packit |
6ef888 |
/* Read in the inode, adjust the link count, and write it back out */
|
|
Packit |
6ef888 |
if (query( _("Update link count for inode %llu (0x%llx) ? (y/n) "),
|
|
Packit |
6ef888 |
(unsigned long long)no_addr, (unsigned long long)no_addr)) {
|
|
Packit |
6ef888 |
struct gfs2_inode *ip;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
ip = fsck_load_inode(sdp, no_addr); /* bread, inode_get */
|
|
Packit |
6ef888 |
fix_link_count(counted_links, ip);
|
|
Packit |
6ef888 |
*di_nlink = counted_links;
|
|
Packit |
6ef888 |
fsck_inode_put(&ip); /* out, brelse, free */
|
|
Packit |
6ef888 |
log_warn(_("Link count updated to %d for inode %llu "
|
|
Packit |
6ef888 |
"(0x%llx)\n"), *di_nlink,
|
|
Packit |
6ef888 |
(unsigned long long)no_addr,
|
|
Packit |
6ef888 |
(unsigned long long)no_addr);
|
|
Packit |
6ef888 |
} else {
|
|
Packit |
6ef888 |
log_err( _("Link count for inode %llu (0x%llx) still "
|
|
Packit |
6ef888 |
"incorrect\n"),
|
|
Packit |
6ef888 |
(unsigned long long)no_addr,
|
|
Packit |
6ef888 |
(unsigned long long)no_addr);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
static int adjust_lf_links(int lf_addition)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
struct dir_info *lf_di;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (lf_dip == NULL)
|
|
Packit |
6ef888 |
return 0;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (!lf_addition)
|
|
Packit |
6ef888 |
return 0;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (!(lf_di = dirtree_find(lf_dip->i_di.di_num.no_addr))) {
|
|
Packit |
6ef888 |
log_crit(_("Unable to find lost+found inode in "
|
|
Packit |
6ef888 |
"inode_hash!!\n"));
|
|
Packit |
6ef888 |
return -1;
|
|
Packit |
6ef888 |
} else {
|
|
Packit |
6ef888 |
fix_link_count(lf_di->counted_links, lf_dip);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
return 0;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
static int scan_inode_list(struct gfs2_sbd *sdp)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
struct osi_node *tmp, *next = NULL;
|
|
Packit |
6ef888 |
struct inode_info *ii;
|
|
Packit |
6ef888 |
int lf_addition = 0;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* FIXME: should probably factor this out into a generic
|
|
Packit |
6ef888 |
* scanning fxn */
|
|
Packit |
6ef888 |
for (tmp = osi_first(&inodetree); tmp; tmp = next) {
|
|
Packit |
6ef888 |
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
|
|
Packit |
6ef888 |
return 0;
|
|
Packit |
6ef888 |
next = osi_next(tmp);
|
|
Packit |
6ef888 |
ii = (struct inode_info *)tmp;
|
|
Packit |
6ef888 |
/* Don't check reference counts on the special gfs files */
|
|
Packit |
6ef888 |
if (sdp->gfs1 &&
|
|
Packit |
6ef888 |
((ii->di_num.no_addr == sdp->md.riinode->i_di.di_num.no_addr) ||
|
|
Packit |
6ef888 |
(ii->di_num.no_addr == sdp->md.qinode->i_di.di_num.no_addr) ||
|
|
Packit |
6ef888 |
(ii->di_num.no_addr == sdp->md.statfs->i_di.di_num.no_addr)))
|
|
Packit |
6ef888 |
continue;
|
|
Packit |
6ef888 |
if (ii->counted_links == 0) {
|
|
Packit |
6ef888 |
if (handle_unlinked(sdp, ii->di_num.no_addr,
|
|
Packit |
6ef888 |
&ii->counted_links, &lf_addition))
|
|
Packit |
6ef888 |
continue;
|
|
Packit |
6ef888 |
} /* if (ii->counted_links == 0) */
|
|
Packit |
6ef888 |
else if (ii->di_nlink != ii->counted_links) {
|
|
Packit |
6ef888 |
handle_inconsist(sdp, ii->di_num.no_addr,
|
|
Packit |
6ef888 |
&ii->di_nlink, ii->counted_links);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
log_debug( _("block %llu (0x%llx) has link count %d\n"),
|
|
Packit |
6ef888 |
(unsigned long long)ii->di_num.no_addr,
|
|
Packit |
6ef888 |
(unsigned long long)ii->di_num.no_addr, ii->di_nlink);
|
|
Packit |
6ef888 |
} /* osi_list_foreach(tmp, list) */
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
return adjust_lf_links(lf_addition);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
static int scan_dir_list(struct gfs2_sbd *sdp)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
struct osi_node *tmp, *next = NULL;
|
|
Packit |
6ef888 |
struct dir_info *di;
|
|
Packit |
6ef888 |
int lf_addition = 0;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* FIXME: should probably factor this out into a generic
|
|
Packit |
6ef888 |
* scanning fxn */
|
|
Packit |
6ef888 |
for (tmp = osi_first(&dirtree); tmp; tmp = next) {
|
|
Packit |
6ef888 |
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
|
|
Packit |
6ef888 |
return 0;
|
|
Packit |
6ef888 |
next = osi_next(tmp);
|
|
Packit |
6ef888 |
di = (struct dir_info *)tmp;
|
|
Packit |
6ef888 |
/* Don't check reference counts on the special gfs files */
|
|
Packit |
6ef888 |
if (sdp->gfs1 &&
|
|
Packit |
6ef888 |
di->dinode.no_addr == sdp->md.jiinode->i_di.di_num.no_addr)
|
|
Packit |
6ef888 |
continue;
|
|
Packit |
6ef888 |
if (di->counted_links == 0) {
|
|
Packit |
6ef888 |
if (handle_unlinked(sdp, di->dinode.no_addr,
|
|
Packit |
6ef888 |
&di->counted_links, &lf_addition))
|
|
Packit |
6ef888 |
continue;
|
|
Packit |
6ef888 |
} else if (di->di_nlink != di->counted_links) {
|
|
Packit |
6ef888 |
handle_inconsist(sdp, di->dinode.no_addr,
|
|
Packit |
6ef888 |
&di->di_nlink, di->counted_links);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
log_debug( _("block %llu (0x%llx) has link count %d\n"),
|
|
Packit |
6ef888 |
(unsigned long long)di->dinode.no_addr,
|
|
Packit |
6ef888 |
(unsigned long long)di->dinode.no_addr, di->di_nlink);
|
|
Packit |
6ef888 |
} /* osi_list_foreach(tmp, list) */
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
return adjust_lf_links(lf_addition);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
static int scan_nlink1_list(struct gfs2_sbd *sdp)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
uint64_t blk;
|
|
Packit |
6ef888 |
uint32_t counted_links;
|
|
Packit |
6ef888 |
int lf_addition = 0;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
for (blk = 0; blk < last_fs_block; blk++) {
|
|
Packit |
6ef888 |
if (skip_this_pass || fsck_abort)
|
|
Packit |
6ef888 |
return 0;
|
|
Packit |
6ef888 |
if (link1_type(&nlink1map, blk) == 0)
|
|
Packit |
6ef888 |
continue;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (link1_type(&clink1map, blk) == 0) {
|
|
Packit |
6ef888 |
/* In other cases, counted_links is a pointer to a
|
|
Packit |
6ef888 |
real count that gets incremented when it's added
|
|
Packit |
6ef888 |
to lost+found. In this case, however, there's not a
|
|
Packit |
6ef888 |
real count, so we fake it out to be 1. */
|
|
Packit |
6ef888 |
counted_links = 1;
|
|
Packit |
6ef888 |
if (handle_unlinked(sdp, blk, &counted_links,
|
|
Packit |
6ef888 |
&lf_addition))
|
|
Packit |
6ef888 |
continue;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
return adjust_lf_links(lf_addition);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/**
|
|
Packit |
6ef888 |
* pass4 - Check reference counts (pass 2 & 6 in current fsck)
|
|
Packit |
6ef888 |
*
|
|
Packit |
6ef888 |
* handle unreferenced files
|
|
Packit |
6ef888 |
* lost+found errors (missing, not a directory, no space)
|
|
Packit |
6ef888 |
* adjust link count
|
|
Packit |
6ef888 |
* handle unreferenced inodes of other types
|
|
Packit |
6ef888 |
* handle bad blocks
|
|
Packit |
6ef888 |
*/
|
|
Packit |
6ef888 |
int pass4(struct gfs2_sbd *sdp)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
if (lf_dip)
|
|
Packit |
6ef888 |
log_debug( _("At beginning of pass4, lost+found entries is %u\n"),
|
|
Packit |
6ef888 |
lf_dip->i_di.di_entries);
|
|
Packit |
6ef888 |
log_info( _("Checking inode reference counts: multi-links.\n"));
|
|
Packit |
6ef888 |
if (scan_inode_list(sdp)) {
|
|
Packit |
6ef888 |
stack;
|
|
Packit |
6ef888 |
return FSCK_ERROR;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
log_info( _("Checking inode reference counts: directories.\n"));
|
|
Packit |
6ef888 |
if (scan_dir_list(sdp)) {
|
|
Packit |
6ef888 |
stack;
|
|
Packit |
6ef888 |
return FSCK_ERROR;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
log_info( _("Checking inode reference counts: normal links.\n"));
|
|
Packit |
6ef888 |
if (scan_nlink1_list(sdp)) {
|
|
Packit |
6ef888 |
stack;
|
|
Packit |
6ef888 |
return FSCK_ERROR;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (lf_dip)
|
|
Packit |
6ef888 |
log_debug( _("At end of pass4, lost+found entries is %u\n"),
|
|
Packit |
6ef888 |
lf_dip->i_di.di_entries);
|
|
Packit |
6ef888 |
return FSCK_OK;
|
|
Packit |
6ef888 |
}
|