Blame gfs2/fsck/pass1b.c

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
}