|
Packit Service |
360c39 |
#include "clusterautoconfig.h"
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
#include <inttypes.h>
|
|
Packit Service |
360c39 |
#include <stdlib.h>
|
|
Packit Service |
360c39 |
#include <string.h>
|
|
Packit Service |
360c39 |
#include <unistd.h>
|
|
Packit Service |
360c39 |
#include <libintl.h>
|
|
Packit Service |
360c39 |
#include <sys/stat.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 "fsck.h"
|
|
Packit Service |
360c39 |
#include "osi_list.h"
|
|
Packit Service |
360c39 |
#include "util.h"
|
|
Packit Service |
360c39 |
#include "metawalk.h"
|
|
Packit Service |
360c39 |
#include "inode_hash.h"
|
|
Packit Service |
360c39 |
#include "afterpass1_common.h"
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
struct fxn_info {
|
|
Packit Service |
360c39 |
uint64_t block;
|
|
Packit Service |
360c39 |
int found;
|
|
Packit Service |
360c39 |
int ea_only; /* The only dups were found in EAs */
|
|
Packit Service |
360c39 |
};
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
struct dup_handler {
|
|
Packit Service |
360c39 |
struct duptree *dt;
|
|
Packit Service |
360c39 |
int ref_inode_count;
|
|
Packit Service |
360c39 |
int ref_count;
|
|
Packit Service |
360c39 |
};
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
struct clone_target {
|
|
Packit Service |
360c39 |
uint64_t dup_block;
|
|
Packit Service |
360c39 |
int first;
|
|
Packit Service |
360c39 |
};
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
struct meta_blk_ref {
|
|
Packit Service |
360c39 |
uint64_t block; /* block to locate */
|
|
Packit Service |
360c39 |
uint64_t metablock; /* returned metadata block addr containing ref */
|
|
Packit Service |
360c39 |
int off; /* offset to the reference within the buffer */
|
|
Packit Service |
360c39 |
};
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int clone_data(struct gfs2_inode *ip, uint64_t metablock,
|
|
Packit Service |
360c39 |
uint64_t block, void *private,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh, uint64_t *ptr);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static void log_inode_reference(struct duptree *dt, osi_list_t *tmp, int inval)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
char reftypestring[32];
|
|
Packit Service |
360c39 |
struct inode_with_dups *id;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
id = osi_list_entry(tmp, struct inode_with_dups, list);
|
|
Packit Service |
360c39 |
if (id->dup_count == 1)
|
|
Packit Service |
360c39 |
sprintf(reftypestring, "as %s", reftypes[get_ref_type(id)]);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
sprintf(reftypestring, "%d/%d/%d/%d",
|
|
Packit Service |
360c39 |
id->reftypecount[ref_is_inode],
|
|
Packit Service |
360c39 |
id->reftypecount[ref_as_data],
|
|
Packit Service |
360c39 |
id->reftypecount[ref_as_meta],
|
|
Packit Service |
360c39 |
id->reftypecount[ref_as_ea]);
|
|
Packit Service |
360c39 |
if (inval)
|
|
Packit Service |
360c39 |
log_warn( _("Invalid "));
|
|
Packit Service |
360c39 |
log_warn( _("Inode %s (%lld/0x%llx) has %d reference(s) to "
|
|
Packit Service |
360c39 |
"block %llu (0x%llx) (%s)\n"), id->name,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no, id->dup_count,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block, reftypestring);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int findref_meta(struct gfs2_inode *ip, uint64_t block,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head **bh, int h,
|
|
Packit Service |
360c39 |
int *is_valid, int *was_duplicate, void *private)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
*is_valid = 1;
|
|
Packit Service |
360c39 |
*was_duplicate = 0;
|
|
Packit Service |
360c39 |
return meta_is_good;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int findref_data(struct gfs2_inode *ip, uint64_t metablock,
|
|
Packit Service |
360c39 |
uint64_t block, void *private,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh, uint64_t *ptr)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct meta_blk_ref *mbr = (struct meta_blk_ref *)private;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (block == mbr->block) {
|
|
Packit Service |
360c39 |
mbr->metablock = bh->b_blocknr;
|
|
Packit Service |
360c39 |
mbr->off = (ptr - (uint64_t *)bh->b_data);
|
|
Packit Service |
360c39 |
log_debug("Duplicate data reference located on metadata "
|
|
Packit Service |
360c39 |
"block 0x%llx, offset 0x%x\n",
|
|
Packit Service |
360c39 |
(unsigned long long)mbr->metablock, mbr->off);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return meta_is_good;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static void clone_data_block(struct gfs2_sbd *sdp, struct duptree *dt,
|
|
Packit Service |
360c39 |
struct inode_with_dups *id)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct meta_blk_ref metaref = { .block = dt->block, };
|
|
Packit Service |
360c39 |
struct metawalk_fxns find1ref_fxns = {
|
|
Packit Service |
360c39 |
.private = &metaref,
|
|
Packit Service |
360c39 |
.check_metalist = findref_meta,
|
|
Packit Service |
360c39 |
.check_data = findref_data,
|
|
Packit Service |
360c39 |
};
|
|
Packit Service |
360c39 |
struct clone_target clone = {.dup_block = dt->block,};
|
|
Packit Service |
360c39 |
struct gfs2_inode *ip;
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh;
|
|
Packit Service |
360c39 |
uint64_t *ptr;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (!(query(_("Okay to clone data block %lld (0x%llx) for inode "
|
|
Packit Service |
360c39 |
"%lld (0x%llx)? (y/n) "),
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no))) {
|
|
Packit Service |
360c39 |
log_warn(_("The duplicate reference was not cloned.\n"));
|
|
Packit Service |
360c39 |
return;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
ip = fsck_load_inode(sdp, id->block_no);
|
|
Packit Service |
360c39 |
check_metatree(ip, &find1ref_fxns);
|
|
Packit Service |
360c39 |
if (metaref.metablock == 0) {
|
|
Packit Service |
360c39 |
log_err(_("Unable to clone data block.\n"));
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
if (metaref.metablock != id->block_no)
|
|
Packit Service |
360c39 |
bh = bread(sdp, metaref.metablock);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
bh = ip->i_bh;
|
|
Packit Service |
360c39 |
ptr = (uint64_t *)bh->b_data + metaref.off;
|
|
Packit Service |
360c39 |
clone_data(ip, 0, dt->block, &clone, bh, ptr);
|
|
Packit Service |
360c39 |
if (metaref.metablock != id->block_no)
|
|
Packit Service |
360c39 |
brelse(bh);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
bmodified(ip->i_bh);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
fsck_inode_put(&ip); /* out, brelse, free */
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* revise_dup_handler - get current information about a duplicate reference
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* Function resolve_dup_references can delete dinodes that reference blocks
|
|
Packit Service |
360c39 |
* which may have duplicate references. Therefore, the duplicate tree is
|
|
Packit Service |
360c39 |
* constantly being changed. This function revises the duplicate handler so
|
|
Packit Service |
360c39 |
* that it accurately matches what's in the duplicate tree regarding this block
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static void revise_dup_handler(uint64_t dup_blk, struct dup_handler *dh)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
osi_list_t *tmp;
|
|
Packit Service |
360c39 |
struct duptree *dt;
|
|
Packit Service |
360c39 |
struct inode_with_dups *id;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
dh->ref_inode_count = 0;
|
|
Packit Service |
360c39 |
dh->ref_count = 0;
|
|
Packit Service |
360c39 |
dh->dt = NULL;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
dt = dupfind(dup_blk);
|
|
Packit Service |
360c39 |
if (!dt)
|
|
Packit Service |
360c39 |
return;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
dh->dt = dt;
|
|
Packit Service |
360c39 |
/* Count the duplicate references, both valid and invalid */
|
|
Packit Service |
360c39 |
osi_list_foreach(tmp, &dt->ref_invinode_list) {
|
|
Packit Service |
360c39 |
id = osi_list_entry(tmp, struct inode_with_dups, list);
|
|
Packit Service |
360c39 |
dh->ref_inode_count++;
|
|
Packit Service |
360c39 |
dh->ref_count += id->dup_count;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
osi_list_foreach(tmp, &dt->ref_inode_list) {
|
|
Packit Service |
360c39 |
id = osi_list_entry(tmp, struct inode_with_dups, list);
|
|
Packit Service |
360c39 |
dh->ref_inode_count++;
|
|
Packit Service |
360c39 |
dh->ref_count += id->dup_count;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/*
|
|
Packit Service |
360c39 |
* resolve_dup_references - resolve all but the last dinode that has a
|
|
Packit Service |
360c39 |
* duplicate reference to a given block.
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* @sdp - pointer to the superblock structure
|
|
Packit Service |
360c39 |
* @dt - pointer to the duplicate reference rbtree to use
|
|
Packit Service |
360c39 |
* @ref_list - list of duplicate references to be resolved (invalid or valid)
|
|
Packit Service |
360c39 |
* @dh - duplicate handler
|
|
Packit Service |
360c39 |
* inval - The references on this ref_list are invalid. We prefer to delete
|
|
Packit Service |
360c39 |
* these first before resorting to deleting valid dinodes.
|
|
Packit Service |
360c39 |
* acceptable_ref - Delete dinodes that reference the given block as anything
|
|
Packit Service |
360c39 |
* _but_ this type. Try to save references as this type.
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static void resolve_dup_references(struct gfs2_sbd *sdp, struct duptree *dt,
|
|
Packit Service |
360c39 |
osi_list_t *ref_list,
|
|
Packit Service |
360c39 |
struct dup_handler *dh,
|
|
Packit Service |
360c39 |
int inval, int acceptable_ref)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct gfs2_inode *ip;
|
|
Packit Service |
360c39 |
struct inode_with_dups *id;
|
|
Packit Service |
360c39 |
osi_list_t *tmp, *x;
|
|
Packit Service |
360c39 |
struct metawalk_fxns pass1b_fxns_delete = {
|
|
Packit Service |
360c39 |
.private = NULL,
|
|
Packit Service |
360c39 |
.check_metalist = delete_metadata,
|
|
Packit Service |
360c39 |
.check_data = delete_data,
|
|
Packit Service |
360c39 |
.check_leaf = delete_leaf,
|
|
Packit Service |
360c39 |
.check_eattr_indir = delete_eattr_indir,
|
|
Packit Service |
360c39 |
.check_eattr_leaf = delete_eattr_leaf,
|
|
Packit Service |
360c39 |
.check_eattr_entry = delete_eattr_entry,
|
|
Packit Service |
360c39 |
.check_eattr_extentry = delete_eattr_extentry,
|
|
Packit Service |
360c39 |
};
|
|
Packit Service |
360c39 |
enum dup_ref_type this_ref;
|
|
Packit Service |
360c39 |
struct inode_info *ii;
|
|
Packit Service |
360c39 |
struct dir_info *di;
|
|
Packit Service |
360c39 |
int found_good_ref = 0;
|
|
Packit Service |
360c39 |
int q;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
osi_list_foreach_safe(tmp, ref_list, x) {
|
|
Packit Service |
360c39 |
if (skip_this_pass || fsck_abort)
|
|
Packit Service |
360c39 |
return;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
id = osi_list_entry(tmp, struct inode_with_dups, list);
|
|
Packit Service |
360c39 |
dh->dt = dt;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (dh->ref_inode_count == 1) /* down to the last reference */
|
|
Packit Service |
360c39 |
return;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
this_ref = get_ref_type(id);
|
|
Packit Service |
360c39 |
q = bitmap_type(sdp, id->block_no);
|
|
Packit Service |
360c39 |
if (inval)
|
|
Packit Service |
360c39 |
log_warn( _("Invalid "));
|
|
Packit Service |
360c39 |
/* FIXME: If we already found an acceptable reference to this
|
|
Packit Service |
360c39 |
* block, we should really duplicate the block and fix all
|
|
Packit Service |
360c39 |
* references to it in this inode. Unfortunately, we would
|
|
Packit Service |
360c39 |
* have to traverse the entire metadata tree to do that. */
|
|
Packit Service |
360c39 |
if (acceptable_ref != ref_types && /* If we're nuking all but
|
|
Packit Service |
360c39 |
an acceptable reference
|
|
Packit Service |
360c39 |
type and */
|
|
Packit Service |
360c39 |
this_ref == acceptable_ref) { /* this ref is acceptable */
|
|
Packit Service |
360c39 |
/* If this is an invalid inode, but not on the invalid
|
|
Packit Service |
360c39 |
list, it's better to delete it. */
|
|
Packit Service |
360c39 |
if (q == GFS2_BLKST_DINODE) {
|
|
Packit Service |
360c39 |
found_good_ref = 1;
|
|
Packit Service |
360c39 |
log_warn( _("Inode %s (%lld/0x%llx)'s "
|
|
Packit Service |
360c39 |
"reference to block %llu (0x%llx) "
|
|
Packit Service |
360c39 |
"as '%s' is acceptable.\n"),
|
|
Packit Service |
360c39 |
id->name,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
reftypes[this_ref]);
|
|
Packit Service |
360c39 |
continue; /* don't delete the dinode */
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* If this reference is from a system inode, for example, if
|
|
Packit Service |
360c39 |
it's data or metadata inside a journal, the reference
|
|
Packit Service |
360c39 |
should take priority over user dinodes that reference the
|
|
Packit Service |
360c39 |
block. */
|
|
Packit Service |
360c39 |
if (!found_good_ref && fsck_system_inode(sdp, id->block_no)) {
|
|
Packit Service |
360c39 |
found_good_ref = 1;
|
|
Packit Service |
360c39 |
continue; /* don't delete the dinode */
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
log_warn( _("Inode %s (%lld/0x%llx) references block "
|
|
Packit Service |
360c39 |
"%llu (0x%llx) as '%s', but the block is "
|
|
Packit Service |
360c39 |
"really %s.\n"),
|
|
Packit Service |
360c39 |
id->name, (unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
reftypes[this_ref], reftypes[acceptable_ref]);
|
|
Packit Service |
360c39 |
if (this_ref == ref_as_ea) {
|
|
Packit Service |
360c39 |
if (!(query( _("Okay to remove extended attributes "
|
|
Packit Service |
360c39 |
"from %s inode %lld (0x%llx)? (y/n) "),
|
|
Packit Service |
360c39 |
(inval ? _("invalidated") : ""),
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no))) {
|
|
Packit Service |
360c39 |
log_warn( _("The bad EA reference was not "
|
|
Packit Service |
360c39 |
"cleared."));
|
|
Packit Service |
360c39 |
/* delete the list entry so we don't leak
|
|
Packit Service |
360c39 |
memory but leave the reference count. If we
|
|
Packit Service |
360c39 |
decrement the ref count, we could get down
|
|
Packit Service |
360c39 |
to 1 and the dinode would be changed
|
|
Packit Service |
360c39 |
without a 'Yes' answer. */
|
|
Packit Service |
360c39 |
/* (dh->ref_inode_count)--;*/
|
|
Packit Service |
360c39 |
dup_listent_delete(dt, id);
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
} else if (acceptable_ref == ref_types &&
|
|
Packit Service |
360c39 |
this_ref == ref_as_data) {
|
|
Packit Service |
360c39 |
clone_data_block(sdp, dt, id);
|
|
Packit Service |
360c39 |
dup_listent_delete(dt, id);
|
|
Packit Service |
360c39 |
revise_dup_handler(dt->block, dh);
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
} else if (!(query( _("Okay to delete %s inode %lld (0x%llx)? "
|
|
Packit Service |
360c39 |
"(y/n) "),
|
|
Packit Service |
360c39 |
(inval ? _("invalidated") : ""),
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no))) {
|
|
Packit Service |
360c39 |
log_warn( _("The bad inode was not cleared."));
|
|
Packit Service |
360c39 |
/* delete the list entry so we don't leak memory but
|
|
Packit Service |
360c39 |
leave the reference count. If we decrement the
|
|
Packit Service |
360c39 |
ref count, we could get down to 1 and the dinode
|
|
Packit Service |
360c39 |
would be changed without a 'Yes' answer. */
|
|
Packit Service |
360c39 |
/* (dh->ref_inode_count)--;*/
|
|
Packit Service |
360c39 |
dup_listent_delete(dt, id);
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
if (q == GFS2_BLKST_FREE)
|
|
Packit Service |
360c39 |
log_warn( _("Inode %lld (0x%llx) was previously "
|
|
Packit Service |
360c39 |
"deleted.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no);
|
|
Packit Service |
360c39 |
else if (this_ref == ref_as_ea)
|
|
Packit Service |
360c39 |
log_warn(_("Pass1b is removing extended attributes "
|
|
Packit Service |
360c39 |
"from inode %lld (0x%llx).\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
log_warn(_("Pass1b is deleting inode %lld (0x%llx).\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
ip = fsck_load_inode(sdp, id->block_no);
|
|
Packit Service |
360c39 |
/* If we've already deleted this dinode, don't try to delete
|
|
Packit Service |
360c39 |
it again. That could free blocks that used to be duplicate
|
|
Packit Service |
360c39 |
references that are now resolved (and gone). */
|
|
Packit Service |
360c39 |
if (q != GFS2_BLKST_FREE) {
|
|
Packit Service |
360c39 |
/* If the inode's eattr pointer is to the duplicate
|
|
Packit Service |
360c39 |
ref block, we don't want to call check_inode_eattr
|
|
Packit Service |
360c39 |
because that would traverse the structure, and it's
|
|
Packit Service |
360c39 |
not ours to do anymore; it rightly belongs to a
|
|
Packit Service |
360c39 |
different dinode. On the other hand, if the dup
|
|
Packit Service |
360c39 |
block is buried deep within the eattr structure
|
|
Packit Service |
360c39 |
of this dinode, we need to traverse the structure
|
|
Packit Service |
360c39 |
because it IS ours, and we need to remove all the
|
|
Packit Service |
360c39 |
eattr leaf blocks: they do belong to us (except for
|
|
Packit Service |
360c39 |
the duplicate referenced one, which is handled). */
|
|
Packit Service |
360c39 |
if (ip->i_di.di_eattr == dt->block) {
|
|
Packit Service |
360c39 |
ip->i_di.di_eattr = 0;
|
|
Packit Service |
360c39 |
if (ip->i_di.di_blocks > 0)
|
|
Packit Service |
360c39 |
ip->i_di.di_blocks--;
|
|
Packit Service |
360c39 |
ip->i_di.di_flags &= ~GFS2_DIF_EA_INDIRECT;
|
|
Packit Service |
360c39 |
bmodified(ip->i_bh);
|
|
Packit Service |
360c39 |
dup_listent_delete(dt, id);
|
|
Packit Service |
360c39 |
(dh->ref_inode_count)--;
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
/* Clear the EAs for the inode first */
|
|
Packit Service |
360c39 |
check_inode_eattr(ip, &pass1b_fxns_delete);
|
|
Packit Service |
360c39 |
(dh->ref_inode_count)--;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* If the reference was as metadata or data, we've got
|
|
Packit Service |
360c39 |
a corrupt dinode that will be deleted. */
|
|
Packit Service |
360c39 |
if ((this_ref != ref_as_ea) &&
|
|
Packit Service |
360c39 |
(inval || id->reftypecount[ref_as_data] ||
|
|
Packit Service |
360c39 |
id->reftypecount[ref_as_meta])) {
|
|
Packit Service |
360c39 |
/* Fix the bitmap first, while the inodetree
|
|
Packit Service |
360c39 |
and dirtree entries exist. That way, the
|
|
Packit Service |
360c39 |
bitmap_set will do proper accounting for
|
|
Packit Service |
360c39 |
the rgrp dinode count. */
|
|
Packit Service |
360c39 |
fsck_bitmap_set(ip, ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
_("duplicate referencing bad"),
|
|
Packit Service |
360c39 |
GFS2_BLKST_FREE);
|
|
Packit Service |
360c39 |
/* Remove the inode from the inode tree */
|
|
Packit Service |
360c39 |
ii = inodetree_find(ip->i_di.di_num.no_addr);
|
|
Packit Service |
360c39 |
if (ii)
|
|
Packit Service |
360c39 |
inodetree_delete(ii);
|
|
Packit Service |
360c39 |
di = dirtree_find(ip->i_di.di_num.no_addr);
|
|
Packit Service |
360c39 |
if (di)
|
|
Packit Service |
360c39 |
dirtree_delete(di);
|
|
Packit Service |
360c39 |
link1_set(&nlink1map, ip->i_di.di_num.no_addr,
|
|
Packit Service |
360c39 |
0);
|
|
Packit Service |
360c39 |
/* We delete the dup_handler inode count and
|
|
Packit Service |
360c39 |
duplicate id BEFORE clearing the metadata,
|
|
Packit Service |
360c39 |
because if this is the last reference to
|
|
Packit Service |
360c39 |
this metadata block, we need to traverse the
|
|
Packit Service |
360c39 |
tree and free the data blocks it references.
|
|
Packit Service |
360c39 |
However, we don't want to delete other
|
|
Packit Service |
360c39 |
duplicates that may be used by other
|
|
Packit Service |
360c39 |
dinodes. */
|
|
Packit Service |
360c39 |
(dh->ref_inode_count)--;
|
|
Packit Service |
360c39 |
/* FIXME: other option should be to duplicate
|
|
Packit Service |
360c39 |
the block for each duplicate and point the
|
|
Packit Service |
360c39 |
metadata at the cloned blocks */
|
|
Packit Service |
360c39 |
check_metatree(ip, &pass1b_fxns_delete);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* Now we've got to go through and delete any other duplicate
|
|
Packit Service |
360c39 |
references from this dinode we're deleting. If we don't,
|
|
Packit Service |
360c39 |
pass1b will discover the other duplicate record, try to
|
|
Packit Service |
360c39 |
delete this dinode a second time, and this time its earlier
|
|
Packit Service |
360c39 |
duplicate references won't be seen as duplicates anymore
|
|
Packit Service |
360c39 |
(because they were eliminated earlier in pass1b). And so
|
|
Packit Service |
360c39 |
the blocks will be mistakenly freed, when, in fact, they're
|
|
Packit Service |
360c39 |
still being referenced by a valid dinode. */
|
|
Packit Service |
360c39 |
if (this_ref != ref_as_ea)
|
|
Packit Service |
360c39 |
delete_all_dups(ip);
|
|
Packit Service |
360c39 |
fsck_inode_put(&ip); /* out, brelse, free */
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int clone_check_meta(struct gfs2_inode *ip, uint64_t block,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head **bh, int h,
|
|
Packit Service |
360c39 |
int *is_valid, int *was_duplicate, void *private)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
*was_duplicate = 0;
|
|
Packit Service |
360c39 |
*is_valid = 1;
|
|
Packit Service |
360c39 |
*bh = bread(ip->i_sbd, block);
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* clone_data - clone a duplicate reference
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* This function remembers the first reference to the specified block, and
|
|
Packit Service |
360c39 |
* clones all subsequent references to it (with permission).
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static int clone_data(struct gfs2_inode *ip, uint64_t metablock,
|
|
Packit Service |
360c39 |
uint64_t block, void *private,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh, uint64_t *ptr)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct clone_target *clonet = (struct clone_target *)private;
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *clone_bh;
|
|
Packit Service |
360c39 |
uint64_t cloneblock;
|
|
Packit Service |
360c39 |
int error;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (block != clonet->dup_block)
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (clonet->first) {
|
|
Packit Service |
360c39 |
log_debug(_("Inode %lld (0x%llx)'s first reference to "
|
|
Packit Service |
360c39 |
"block %lld (0x%llx) is targeted for cloning.\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)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block);
|
|
Packit Service |
360c39 |
clonet->first = 0;
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
log_err(_("Error: Inode %lld (0x%llx)'s reference to block %lld "
|
|
Packit Service |
360c39 |
"(0x%llx) should be replaced with a clone.\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)block, (unsigned long long)block);
|
|
Packit Service |
360c39 |
if (query( _("Okay to clone the duplicated reference? (y/n) "))) {
|
|
Packit Service |
360c39 |
error = lgfs2_meta_alloc(ip, &cloneblock);
|
|
Packit Service |
360c39 |
if (!error) {
|
|
Packit Service |
360c39 |
clone_bh = bread(ip->i_sbd, clonet->dup_block);
|
|
Packit Service |
360c39 |
if (clone_bh) {
|
|
Packit Service |
360c39 |
fsck_bitmap_set(ip, cloneblock, _("data"),
|
|
Packit Service |
360c39 |
GFS2_BLKST_USED);
|
|
Packit Service |
360c39 |
clone_bh->b_blocknr = cloneblock;
|
|
Packit Service |
360c39 |
bmodified(clone_bh);
|
|
Packit Service |
360c39 |
brelse(clone_bh);
|
|
Packit Service |
360c39 |
/* Now fix the reference: */
|
|
Packit Service |
360c39 |
*ptr = cpu_to_be64(cloneblock);
|
|
Packit Service |
360c39 |
bmodified(bh);
|
|
Packit Service |
360c39 |
log_err(_("Duplicate reference to block %lld "
|
|
Packit Service |
360c39 |
"(0x%llx) was cloned to block %lld "
|
|
Packit Service |
360c39 |
"(0x%llx).\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)cloneblock,
|
|
Packit Service |
360c39 |
(unsigned long long)cloneblock);
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
log_err(_("Error: Unable to allocate a new data block.\n"));
|
|
Packit Service |
360c39 |
if (!query("Should I zero the reference instead? (y/n)")) {
|
|
Packit Service |
360c39 |
log_err(_("Duplicate reference to block %lld "
|
|
Packit Service |
360c39 |
"(0x%llx) was not fixed.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block);
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
*ptr = 0;
|
|
Packit Service |
360c39 |
bmodified(bh);
|
|
Packit Service |
360c39 |
log_err(_("Duplicate reference to block %lld (0x%llx) was "
|
|
Packit Service |
360c39 |
"zeroed.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block);
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
log_err(_("Duplicate reference to block %lld (0x%llx) "
|
|
Packit Service |
360c39 |
"was not fixed.\n"), (unsigned long long)block,
|
|
Packit Service |
360c39 |
(unsigned long long)block);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* clone_dup_ref_in_inode - clone a duplicate reference within a single inode
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* This function traverses the metadata tree of an inode, cloning all
|
|
Packit Service |
360c39 |
* but the first reference to a duplicate block reference.
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static void clone_dup_ref_in_inode(struct gfs2_inode *ip, struct duptree *dt)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
int error;
|
|
Packit Service |
360c39 |
struct clone_target clonet = {.dup_block = dt->block, .first = 1};
|
|
Packit Service |
360c39 |
struct metawalk_fxns pass1b_fxns_clone = {
|
|
Packit Service |
360c39 |
.private = &clonet,
|
|
Packit Service |
360c39 |
.check_metalist = clone_check_meta,
|
|
Packit Service |
360c39 |
.check_data = clone_data,
|
|
Packit Service |
360c39 |
};
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
log_err(_("There are multiple references to block %lld (0x%llx) in "
|
|
Packit Service |
360c39 |
"inode %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)dt->block, (unsigned long long)dt->block);
|
|
Packit Service |
360c39 |
error = check_metatree(ip, &pass1b_fxns_clone);
|
|
Packit Service |
360c39 |
if (error) {
|
|
Packit Service |
360c39 |
log_err(_("Error cloning duplicate reference(s) to block %lld "
|
|
Packit Service |
360c39 |
"(0x%llx).\n"), (unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int set_ip_bitmap(struct gfs2_inode *ip)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
uint64_t block = ip->i_bh->b_blocknr;
|
|
Packit Service |
360c39 |
uint32_t mode;
|
|
Packit Service |
360c39 |
const char *ty;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (ip->i_sbd->gfs1)
|
|
Packit Service |
360c39 |
mode = gfs_to_gfs2_mode(ip);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
mode = ip->i_di.di_mode & S_IFMT;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
switch (mode) {
|
|
Packit Service |
360c39 |
case S_IFDIR:
|
|
Packit Service |
360c39 |
ty = _("directory");
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
case S_IFREG:
|
|
Packit Service |
360c39 |
ty = _("file");
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
case S_IFLNK:
|
|
Packit Service |
360c39 |
ty = _("symlink");
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
case S_IFBLK:
|
|
Packit Service |
360c39 |
ty = _("block device");
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
case S_IFCHR:
|
|
Packit Service |
360c39 |
ty = _("character device");
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
case S_IFIFO:
|
|
Packit Service |
360c39 |
ty = _("fifo");
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
case S_IFSOCK:
|
|
Packit Service |
360c39 |
ty = _("socket");
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
default:
|
|
Packit Service |
360c39 |
return -EINVAL;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
fsck_bitmap_set(ip, block, ty, GFS2_BLKST_DINODE);
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static void resolve_last_reference(struct gfs2_sbd *sdp, struct duptree *dt,
|
|
Packit Service |
360c39 |
enum dup_ref_type acceptable_ref)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct gfs2_inode *ip;
|
|
Packit Service |
360c39 |
struct inode_with_dups *id;
|
|
Packit Service |
360c39 |
osi_list_t *tmp;
|
|
Packit Service |
360c39 |
int q;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
log_notice( _("Block %llu (0x%llx) has only one remaining "
|
|
Packit Service |
360c39 |
"valid inode referencing it.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block);
|
|
Packit Service |
360c39 |
/* If we're down to a single reference (and not all references
|
|
Packit Service |
360c39 |
deleted, which may be the case of an inode that has only
|
|
Packit Service |
360c39 |
itself and a reference), we need to reset the block type
|
|
Packit Service |
360c39 |
from invalid to data or metadata. Start at the first one
|
|
Packit Service |
360c39 |
in the list, not the structure's place holder. */
|
|
Packit Service |
360c39 |
tmp = dt->ref_inode_list.next;
|
|
Packit Service |
360c39 |
id = osi_list_entry(tmp, struct inode_with_dups, list);
|
|
Packit Service |
360c39 |
log_debug( _("----------------------------------------------\n"
|
|
Packit Service |
360c39 |
"Step 4. Set block type based on the remaining "
|
|
Packit Service |
360c39 |
"reference in inode %lld (0x%llx).\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no);
|
|
Packit Service |
360c39 |
ip = fsck_load_inode(sdp, id->block_no);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (dt->dup_flags & DUPFLAG_REF1_IS_DUPL)
|
|
Packit Service |
360c39 |
clone_dup_ref_in_inode(ip, dt);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
q = bitmap_type(sdp, id->block_no);
|
|
Packit Service |
360c39 |
if (q == GFS2_BLKST_FREE) {
|
|
Packit Service |
360c39 |
log_debug( _("The remaining reference inode %lld (0x%llx) was "
|
|
Packit Service |
360c39 |
"already marked free.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no);
|
|
Packit Service |
360c39 |
} else if (id->reftypecount[ref_is_inode]) {
|
|
Packit Service |
360c39 |
set_ip_bitmap(ip);
|
|
Packit Service |
360c39 |
} else if (id->reftypecount[ref_as_data]) {
|
|
Packit Service |
360c39 |
fsck_bitmap_set(ip, dt->block, _("reference-repaired data"),
|
|
Packit Service |
360c39 |
GFS2_BLKST_USED);
|
|
Packit Service |
360c39 |
} else if (id->reftypecount[ref_as_meta]) {
|
|
Packit Service |
360c39 |
if (is_dir(&ip->i_di, sdp->gfs1))
|
|
Packit Service |
360c39 |
fsck_bitmap_set(ip, dt->block,
|
|
Packit Service |
360c39 |
_("reference-repaired leaf"),
|
|
Packit Service |
360c39 |
sdp->gfs1 ? GFS2_BLKST_DINODE :
|
|
Packit Service |
360c39 |
GFS2_BLKST_USED);
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
fsck_bitmap_set(ip, dt->block,
|
|
Packit Service |
360c39 |
_("reference-repaired indirect"),
|
|
Packit Service |
360c39 |
sdp->gfs1 ? GFS2_BLKST_DINODE :
|
|
Packit Service |
360c39 |
GFS2_BLKST_USED);
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
if (acceptable_ref == ref_as_ea)
|
|
Packit Service |
360c39 |
fsck_bitmap_set(ip, dt->block,
|
|
Packit Service |
360c39 |
_("reference-repaired extended "
|
|
Packit Service |
360c39 |
"attribute"),
|
|
Packit Service |
360c39 |
sdp->gfs1 ? GFS2_BLKST_DINODE :
|
|
Packit Service |
360c39 |
GFS2_BLKST_USED);
|
|
Packit Service |
360c39 |
else {
|
|
Packit Service |
360c39 |
log_err(_("Error: The remaining reference to block "
|
|
Packit Service |
360c39 |
" %lld (0x%llx) is as extended attribute, "
|
|
Packit Service |
360c39 |
"in inode %lld (0x%llx) but the block is "
|
|
Packit Service |
360c39 |
"not an EA.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no);
|
|
Packit Service |
360c39 |
if (query(_("Okay to remove the bad extended "
|
|
Packit Service |
360c39 |
"attribute from inode %lld (0x%llx)? "
|
|
Packit Service |
360c39 |
"(y/n) "),
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no,
|
|
Packit Service |
360c39 |
(unsigned long long)id->block_no)) {
|
|
Packit Service |
360c39 |
ip->i_di.di_eattr = 0;
|
|
Packit Service |
360c39 |
ip->i_di.di_flags &= ~GFS2_DIF_EA_INDIRECT;
|
|
Packit Service |
360c39 |
ip->i_di.di_blocks--;
|
|
Packit Service |
360c39 |
bmodified(ip->i_bh);
|
|
Packit Service |
360c39 |
fsck_bitmap_set(ip, dt->block,
|
|
Packit Service |
360c39 |
_("reference-repaired EA"),
|
|
Packit Service |
360c39 |
GFS2_BLKST_FREE);
|
|
Packit Service |
360c39 |
log_err(_("The bad extended attribute was "
|
|
Packit Service |
360c39 |
"removed.\n"));
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
log_err(_("The bad extended attribute was not "
|
|
Packit Service |
360c39 |
"removed.\n"));
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
fsck_inode_put(&ip); /* out, brelse, free */
|
|
Packit Service |
360c39 |
log_debug(_("Done with duplicate reference to block 0x%llx\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block);
|
|
Packit Service |
360c39 |
dup_delete(dt);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* handle_dup_blk - handle a duplicate block reference.
|
|
Packit Service |
360c39 |
*
|
|
Packit Service |
360c39 |
* This function should resolve and delete the duplicate block reference given,
|
|
Packit Service |
360c39 |
* iow dt.
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
static int handle_dup_blk(struct gfs2_sbd *sdp, struct duptree *dt)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
osi_list_t *tmp;
|
|
Packit Service |
360c39 |
struct dup_handler dh = {0};
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh;
|
|
Packit Service |
360c39 |
uint32_t cmagic, ctype;
|
|
Packit Service |
360c39 |
enum dup_ref_type acceptable_ref;
|
|
Packit Service |
360c39 |
uint64_t dup_blk;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
dup_blk = dt->block;
|
|
Packit Service |
360c39 |
revise_dup_handler(dup_blk, &dh);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Log the duplicate references */
|
|
Packit Service |
360c39 |
log_notice( _("Block %llu (0x%llx) has %d inodes referencing it"
|
|
Packit Service |
360c39 |
" for a total of %d duplicate references:\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
dh.ref_inode_count, dh.ref_count);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
osi_list_foreach(tmp, &dt->ref_invinode_list)
|
|
Packit Service |
360c39 |
log_inode_reference(dt, tmp, 1);
|
|
Packit Service |
360c39 |
osi_list_foreach(tmp, &dt->ref_inode_list)
|
|
Packit Service |
360c39 |
log_inode_reference(dt, tmp, 0);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Figure out the block type to see if we can eliminate references
|
|
Packit Service |
360c39 |
to a different type. In other words, if the duplicate block looks
|
|
Packit Service |
360c39 |
like metadata, we can delete dinodes that reference it as data.
|
|
Packit Service |
360c39 |
If the block doesn't look like metadata, we can eliminate any
|
|
Packit Service |
360c39 |
references to it as metadata. Dinodes with such references are
|
|
Packit Service |
360c39 |
clearly corrupt and need to be deleted.
|
|
Packit Service |
360c39 |
And if we're left with a single reference, problem solved. */
|
|
Packit Service |
360c39 |
bh = bread(sdp, dt->block);
|
|
Packit Service |
360c39 |
cmagic = ((struct gfs2_meta_header *)(bh->b_data))->mh_magic;
|
|
Packit Service |
360c39 |
ctype = ((struct gfs2_meta_header *)(bh->b_data))->mh_type;
|
|
Packit Service |
360c39 |
brelse(bh);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* If this is a dinode, any references to it (except in directory
|
|
Packit Service |
360c39 |
entries) are invalid and should be deleted. */
|
|
Packit Service |
360c39 |
if (be32_to_cpu(cmagic) == GFS2_MAGIC &&
|
|
Packit Service |
360c39 |
be32_to_cpu(ctype) == GFS2_METATYPE_DI)
|
|
Packit Service |
360c39 |
acceptable_ref = ref_is_inode;
|
|
Packit Service |
360c39 |
else if (be32_to_cpu(cmagic) == GFS2_MAGIC &&
|
|
Packit Service |
360c39 |
(be32_to_cpu(ctype) == GFS2_METATYPE_EA ||
|
|
Packit Service |
360c39 |
be32_to_cpu(ctype) == GFS2_METATYPE_ED))
|
|
Packit Service |
360c39 |
acceptable_ref = ref_as_ea;
|
|
Packit Service |
360c39 |
else if (be32_to_cpu(cmagic) == GFS2_MAGIC &&
|
|
Packit Service |
360c39 |
be32_to_cpu(ctype) <= GFS2_METATYPE_QC)
|
|
Packit Service |
360c39 |
acceptable_ref = ref_as_meta;
|
|
Packit Service |
360c39 |
else
|
|
Packit Service |
360c39 |
acceptable_ref = ref_as_data;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* A single reference to the block implies a possible situation where
|
|
Packit Service |
360c39 |
a data pointer points to a metadata block. In other words, the
|
|
Packit Service |
360c39 |
duplicate reference in the file system is (1) Metadata block X and
|
|
Packit Service |
360c39 |
(2) A dinode reference such as a data pointer pointing to block X.
|
|
Packit Service |
360c39 |
We can't really check for that in pass1 because user data might
|
|
Packit Service |
360c39 |
just _look_ like metadata by coincidence, and at the time we're
|
|
Packit Service |
360c39 |
checking, we might not have processed the referenced block.
|
|
Packit Service |
360c39 |
Here in pass1b we're sure. */
|
|
Packit Service |
360c39 |
/* Another possibility here is that there is a single reference
|
|
Packit Service |
360c39 |
because all the other metadata references were in inodes that got
|
|
Packit Service |
360c39 |
invalidated for other reasons, such as bad pointers. So we need to
|
|
Packit Service |
360c39 |
make sure at this point that any inode deletes reverse out any
|
|
Packit Service |
360c39 |
duplicate reference before we get to this point. */
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Step 1 - eliminate references from inodes that are not valid.
|
|
Packit Service |
360c39 |
* This may be because they were deleted due to corruption.
|
|
Packit Service |
360c39 |
* All block types are unacceptable, so we use ref_types.
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
if (dh.ref_count > 1) {
|
|
Packit Service |
360c39 |
log_debug( _("----------------------------------------------\n"
|
|
Packit Service |
360c39 |
"Step 1: Eliminate references to block %llu "
|
|
Packit Service |
360c39 |
"(0x%llx) that were previously marked "
|
|
Packit Service |
360c39 |
"invalid.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block);
|
|
Packit Service |
360c39 |
resolve_dup_references(sdp, dt, &dt->ref_invinode_list,
|
|
Packit Service |
360c39 |
&dh, 1, ref_types);
|
|
Packit Service |
360c39 |
revise_dup_handler(dup_blk, &dh);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* Step 2 - eliminate reference from inodes that reference it as the
|
|
Packit Service |
360c39 |
* wrong type. For example, a data file referencing it as
|
|
Packit Service |
360c39 |
* a data block, but it's really a metadata block. Or a
|
|
Packit Service |
360c39 |
* directory inode referencing a data block as a leaf block.
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
if (dh.ref_count > 1) {
|
|
Packit Service |
360c39 |
log_debug( _("----------------------------------------------\n"
|
|
Packit Service |
360c39 |
"Step 2: Eliminate references to block %llu "
|
|
Packit Service |
360c39 |
"(0x%llx) that need the wrong block type.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block);
|
|
Packit Service |
360c39 |
resolve_dup_references(sdp, dt, &dt->ref_inode_list, &dh, 0,
|
|
Packit Service |
360c39 |
acceptable_ref);
|
|
Packit Service |
360c39 |
revise_dup_handler(dup_blk, &dh);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* Step 3 - We have multiple dinodes referencing it as the correct
|
|
Packit Service |
360c39 |
* type. Just blast one of them.
|
|
Packit Service |
360c39 |
* All block types are fair game, so we use ref_types.
|
|
Packit Service |
360c39 |
*/
|
|
Packit Service |
360c39 |
if (dh.ref_count > 1) {
|
|
Packit Service |
360c39 |
log_debug( _("----------------------------------------------\n"
|
|
Packit Service |
360c39 |
"Step 3: Choose one reference to block %llu "
|
|
Packit Service |
360c39 |
"(0x%llx) to keep.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block,
|
|
Packit Service |
360c39 |
(unsigned long long)dt->block);
|
|
Packit Service |
360c39 |
resolve_dup_references(sdp, dt, &dt->ref_inode_list, &dh, 0,
|
|
Packit Service |
360c39 |
ref_types);
|
|
Packit Service |
360c39 |
revise_dup_handler(dup_blk, &dh);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* If there's still a last remaining reference, and it's a valid
|
|
Packit Service |
360c39 |
reference, use it to determine the correct block type for our
|
|
Packit Service |
360c39 |
blockmap and bitmap. */
|
|
Packit Service |
360c39 |
if (dh.ref_inode_count == 1 && !osi_list_empty(&dt->ref_inode_list)) {
|
|
Packit Service |
360c39 |
resolve_last_reference(sdp, dt, acceptable_ref);
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
/* They may have answered no and not fixed all references. */
|
|
Packit Service |
360c39 |
log_debug( _("All duplicate references to block 0x%llx were "
|
|
Packit Service |
360c39 |
"processed.\n"), (unsigned long long)dup_blk);
|
|
Packit Service |
360c39 |
if (dh.ref_count) {
|
|
Packit Service |
360c39 |
log_debug(_("Done with duplicate reference to block "
|
|
Packit Service |
360c39 |
"0x%llx, but %d references remain.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)dup_blk, dh.ref_count);
|
|
Packit Service |
360c39 |
} else {
|
|
Packit Service |
360c39 |
log_notice( _("Block %llu (0x%llx) has no more "
|
|
Packit Service |
360c39 |
"references; Marking as 'free'.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)dup_blk,
|
|
Packit Service |
360c39 |
(unsigned long long)dup_blk);
|
|
Packit Service |
360c39 |
if (dh.dt)
|
|
Packit Service |
360c39 |
dup_delete(dh.dt);
|
|
Packit Service |
360c39 |
check_n_fix_bitmap(sdp, NULL, dup_blk, 0,
|
|
Packit Service |
360c39 |
GFS2_BLKST_FREE);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int check_leaf_refs(struct gfs2_inode *ip, uint64_t block,
|
|
Packit Service |
360c39 |
void *private)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int check_metalist_refs(struct gfs2_inode *ip, uint64_t block,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head **bh, int h,
|
|
Packit Service |
360c39 |
int *is_valid, int *was_duplicate,
|
|
Packit Service |
360c39 |
void *private)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
*was_duplicate = 0;
|
|
Packit Service |
360c39 |
*is_valid = 1;
|
|
Packit Service |
360c39 |
return add_duplicate_ref(ip, block, ref_as_meta, 1, INODE_VALID);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int check_data_refs(struct gfs2_inode *ip, uint64_t metablock,
|
|
Packit Service |
360c39 |
uint64_t block, void *private,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *bh, uint64_t *ptr)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
return add_duplicate_ref(ip, block, ref_as_data, 1, INODE_VALID);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int check_eattr_indir_refs(struct gfs2_inode *ip, uint64_t block,
|
|
Packit Service |
360c39 |
uint64_t parent,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head **bh, void *private)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct gfs2_sbd *sdp = ip->i_sbd;
|
|
Packit Service |
360c39 |
int error;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
error = add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
|
|
Packit Service |
360c39 |
if (!error)
|
|
Packit Service |
360c39 |
*bh = bread(sdp, block);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int check_eattr_leaf_refs(struct gfs2_inode *ip, uint64_t block,
|
|
Packit Service |
360c39 |
uint64_t parent, struct gfs2_buffer_head **bh,
|
|
Packit Service |
360c39 |
void *private)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct gfs2_sbd *sdp = ip->i_sbd;
|
|
Packit Service |
360c39 |
int error;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
error = add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
|
|
Packit Service |
360c39 |
if (!error)
|
|
Packit Service |
360c39 |
*bh = bread(sdp, block);
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int check_eattr_entry_refs(struct gfs2_inode *ip,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *leaf_bh,
|
|
Packit Service |
360c39 |
struct gfs2_ea_header *ea_hdr,
|
|
Packit Service |
360c39 |
struct gfs2_ea_header *ea_hdr_prev,
|
|
Packit Service |
360c39 |
void *private)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
return 0;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
static int check_eattr_extentry_refs(struct gfs2_inode *ip, int i,
|
|
Packit Service |
360c39 |
uint64_t *ea_data_ptr,
|
|
Packit Service |
360c39 |
struct gfs2_buffer_head *leaf_bh,
|
|
Packit Service |
360c39 |
uint32_t tot_ealen,
|
|
Packit Service |
360c39 |
struct gfs2_ea_header *ea_hdr,
|
|
Packit Service |
360c39 |
struct gfs2_ea_header *ea_hdr_prev,
|
|
Packit Service |
360c39 |
void *private)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
uint64_t block = be64_to_cpu(*ea_data_ptr);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* This is a case where a bad return code may be sent back, and
|
|
Packit Service |
360c39 |
behavior has changed. Before, if add_duplicate_ref returned a
|
|
Packit Service |
360c39 |
non-zero return code, the caller would delete the eattr from
|
|
Packit Service |
360c39 |
the blockmap. In this case, we should be okay because the only
|
|
Packit Service |
360c39 |
error possible is a malloc that fails, in which case we don't
|
|
Packit Service |
360c39 |
want to delete the eattr anyway. */
|
|
Packit Service |
360c39 |
return add_duplicate_ref(ip, block, ref_as_ea, 1, INODE_VALID);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Finds all references to duplicate blocks in the metadata */
|
|
Packit Service |
360c39 |
/* Finds all references to duplicate blocks in the metadata */
|
|
Packit Service |
360c39 |
static int find_block_ref(struct gfs2_sbd *sdp, uint64_t inode)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct gfs2_inode *ip;
|
|
Packit Service |
360c39 |
int error = 0;
|
|
Packit Service |
360c39 |
struct metawalk_fxns find_refs = {
|
|
Packit Service |
360c39 |
.private = NULL,
|
|
Packit Service |
360c39 |
.check_leaf = check_leaf_refs,
|
|
Packit Service |
360c39 |
.check_metalist = check_metalist_refs,
|
|
Packit Service |
360c39 |
.check_data = check_data_refs,
|
|
Packit Service |
360c39 |
.check_eattr_indir = check_eattr_indir_refs,
|
|
Packit Service |
360c39 |
.check_eattr_leaf = check_eattr_leaf_refs,
|
|
Packit Service |
360c39 |
.check_eattr_entry = check_eattr_entry_refs,
|
|
Packit Service |
360c39 |
.check_eattr_extentry = check_eattr_extentry_refs,
|
|
Packit Service |
360c39 |
};
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
ip = fsck_load_inode(sdp, inode); /* bread, inode_get */
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* double-check the meta header just to be sure it's metadata */
|
|
Packit Service |
360c39 |
if (ip->i_di.di_header.mh_magic != GFS2_MAGIC ||
|
|
Packit Service |
360c39 |
ip->i_di.di_header.mh_type != GFS2_METATYPE_DI) {
|
|
Packit Service |
360c39 |
if (!sdp->gfs1)
|
|
Packit Service |
360c39 |
log_debug( _("Block %lld (0x%llx) is not a dinode.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)inode,
|
|
Packit Service |
360c39 |
(unsigned long long)inode);
|
|
Packit Service |
360c39 |
error = 1;
|
|
Packit Service |
360c39 |
goto out;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
/* Check to see if this inode was referenced by another by mistake */
|
|
Packit Service |
360c39 |
add_duplicate_ref(ip, inode, ref_is_inode, 1, INODE_VALID);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Check this dinode's metadata for references to known duplicates */
|
|
Packit Service |
360c39 |
error = check_metatree(ip, &find_refs);
|
|
Packit Service |
360c39 |
if (error < 0)
|
|
Packit Service |
360c39 |
stack;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Check for ea references in the inode */
|
|
Packit Service |
360c39 |
if (!error)
|
|
Packit Service |
360c39 |
error = check_inode_eattr(ip, &find_refs);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
out:
|
|
Packit Service |
360c39 |
fsck_inode_put(&ip); /* out, brelse, free */
|
|
Packit Service |
360c39 |
return error;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Pass 1b handles finding the previous inode for a duplicate block
|
|
Packit Service |
360c39 |
* When found, store the inodes pointing to the duplicate block for
|
|
Packit Service |
360c39 |
* use in pass2 */
|
|
Packit Service |
360c39 |
int pass1b(struct gfs2_sbd *sdp)
|
|
Packit Service |
360c39 |
{
|
|
Packit Service |
360c39 |
struct duptree *dt;
|
|
Packit Service |
360c39 |
uint64_t i;
|
|
Packit Service |
360c39 |
int q;
|
|
Packit Service |
360c39 |
struct osi_node *n;
|
|
Packit Service |
360c39 |
int rc = FSCK_OK;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
log_info( _("Looking for duplicate blocks...\n"));
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* If there were no dups in the bitmap, we don't need to do anymore */
|
|
Packit Service |
360c39 |
if (dup_blocks.osi_node == NULL) {
|
|
Packit Service |
360c39 |
log_info( _("No duplicate blocks found\n"));
|
|
Packit Service |
360c39 |
return FSCK_OK;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Rescan the fs looking for pointers to blocks that are in
|
|
Packit Service |
360c39 |
* the duplicate block map */
|
|
Packit Service |
360c39 |
log_info( _("Scanning filesystem for inodes containing duplicate blocks...\n"));
|
|
Packit Service |
360c39 |
log_debug( _("Filesystem has %llu (0x%llx) blocks total\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)last_fs_block,
|
|
Packit Service |
360c39 |
(unsigned long long)last_fs_block);
|
|
Packit Service |
360c39 |
for (i = 0; i < last_fs_block; i++) {
|
|
Packit Service |
360c39 |
if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
|
|
Packit Service |
360c39 |
goto out;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (dups_found_first == dups_found) {
|
|
Packit Service |
360c39 |
log_debug(_("Found all %d original references to "
|
|
Packit Service |
360c39 |
"duplicates.\n"), dups_found);
|
|
Packit Service |
360c39 |
break;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
q = bitmap_type(sdp, i);
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (q == GFS2_BLKST_FREE || q == GFS2_BLKST_USED || q < 0)
|
|
Packit Service |
360c39 |
continue;
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
if (q == GFS2_BLKST_UNLINKED) {
|
|
Packit Service |
360c39 |
log_debug( _("Error: block %lld (0x%llx) is still "
|
|
Packit Service |
360c39 |
"marked UNLINKED.\n"),
|
|
Packit Service |
360c39 |
(unsigned long long)i,
|
|
Packit Service |
360c39 |
(unsigned long long)i);
|
|
Packit Service |
360c39 |
return FSCK_ERROR;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
warm_fuzzy_stuff(i);
|
|
Packit Service |
360c39 |
if (find_block_ref(sdp, i) < 0) {
|
|
Packit Service |
360c39 |
stack;
|
|
Packit Service |
360c39 |
rc = FSCK_ERROR;
|
|
Packit Service |
360c39 |
goto out;
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
|
|
Packit Service |
360c39 |
/* Fix dups here - it's going to slow things down a lot to fix
|
|
Packit Service |
360c39 |
* it later */
|
|
Packit Service |
360c39 |
log_info( _("Handling duplicate blocks\n"));
|
|
Packit Service |
360c39 |
out:
|
|
Packit Service |
360c39 |
/* Resolve all duplicates by clearing out the dup tree */
|
|
Packit Service |
360c39 |
while ((n = osi_first(&dup_blocks))) {
|
|
Packit Service |
360c39 |
dt = (struct duptree *)n;
|
|
Packit Service |
360c39 |
if (!skip_this_pass && !rc) /* no error & not asked to skip the rest */
|
|
Packit Service |
360c39 |
handle_dup_blk(sdp, dt);
|
|
Packit Service |
360c39 |
}
|
|
Packit Service |
360c39 |
return rc;
|
|
Packit Service |
360c39 |
}
|