Blame gfs2/fsck/pass2.c

Packit Service 360c39
#include "clusterautoconfig.h"
Packit Service 360c39
Packit Service 360c39
#include <dirent.h>
Packit Service 360c39
#include <stdio.h>
Packit Service 360c39
#include <stdlib.h>
Packit Service 360c39
#include <string.h>
Packit Service 360c39
#include <inttypes.h>
Packit Service 360c39
#include <sys/stat.h>
Packit Service 360c39
#include <libintl.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 "fsck.h"
Packit Service 360c39
#include "util.h"
Packit Service 360c39
#include "metawalk.h"
Packit Service 360c39
#include "link.h"
Packit Service 360c39
#include "lost_n_found.h"
Packit Service 360c39
#include "inode_hash.h"
Packit Service 360c39
#include "afterpass1_common.h"
Packit Service 360c39
Packit Service 360c39
#define MAX_FILENAME 256
Packit Service 360c39
Packit Service 360c39
struct metawalk_fxns pass2_fxns;
Packit Service 360c39
Packit Service 360c39
struct metawalk_fxns delete_eattrs = {
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
Packit Service 360c39
/* Set children's parent inode in dir_info structure - ext2 does not set
Packit Service 360c39
 * dotdot inode here, but instead in pass3 - should we? */
Packit Service 360c39
static int set_parent_dir(struct gfs2_sbd *sdp, struct gfs2_inum child,
Packit Service 360c39
			  struct gfs2_inum parent)
Packit Service 360c39
{
Packit Service 360c39
	struct dir_info *di;
Packit Service 360c39
Packit Service 360c39
	di = dirtree_find(child.no_addr);
Packit Service 360c39
	if (!di) {
Packit Service 360c39
		log_err( _("Unable to find block %llu (0x%llx"
Packit Service 360c39
			   ") in dir_info list\n"),
Packit Service 360c39
			(unsigned long long)child.no_addr,
Packit Service 360c39
			(unsigned long long)child.no_addr);
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	if (di->dinode.no_addr == child.no_addr &&
Packit Service 360c39
	    di->dinode.no_formal_ino == child.no_formal_ino) {
Packit Service 360c39
		if (di->treewalk_parent) {
Packit Service 360c39
			log_err( _("Another directory at block %lld (0x%llx) "
Packit Service 360c39
				   "already contains this child %lld (0x%llx)"
Packit Service 360c39
				   " - checking parent %lld (0x%llx)\n"),
Packit Service 360c39
				 (unsigned long long)di->treewalk_parent,
Packit Service 360c39
				 (unsigned long long)di->treewalk_parent,
Packit Service 360c39
				 (unsigned long long)child.no_addr,
Packit Service 360c39
				 (unsigned long long)child.no_addr,
Packit Service 360c39
				 (unsigned long long)parent.no_addr,
Packit Service 360c39
				 (unsigned long long)parent.no_addr);
Packit Service 360c39
			return 1;
Packit Service 360c39
		}
Packit Service 360c39
		log_debug( _("Child %lld (0x%llx) has parent %lld (0x%llx)\n"),
Packit Service 360c39
			   (unsigned long long)child.no_addr,
Packit Service 360c39
			   (unsigned long long)child.no_addr,
Packit Service 360c39
			   (unsigned long long)parent.no_addr,
Packit Service 360c39
			   (unsigned long long)parent.no_addr);
Packit Service 360c39
		di->treewalk_parent = parent.no_addr;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	return 0;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/* Set's the child's '..' directory inode number in dir_info structure */
Packit Service 360c39
static int set_dotdot_dir(struct gfs2_sbd *sdp, uint64_t childblock,
Packit Service 360c39
			  struct gfs2_inum parent)
Packit Service 360c39
{
Packit Service 360c39
	struct dir_info *di;
Packit Service 360c39
Packit Service 360c39
	di = dirtree_find(childblock);
Packit Service 360c39
	if (!di) {
Packit Service 360c39
		log_err( _("Unable to find block %"PRIu64" (0x%" PRIx64
Packit Service 360c39
			   ") in dir_info tree\n"), childblock, childblock);
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	if (di->dinode.no_addr != childblock) {
Packit Service 360c39
		log_debug("'..' doesn't point to what we found: childblock "
Packit Service 360c39
			  "(0x%llx) != dinode (0x%llx)\n",
Packit Service 360c39
			  (unsigned long long)childblock,
Packit Service 360c39
			  (unsigned long long)di->dinode.no_addr);
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	/* Special case for root inode because we set it earlier */
Packit Service 360c39
	if (di->dotdot_parent.no_addr &&
Packit Service 360c39
	    sdp->md.rooti->i_di.di_num.no_addr != di->dinode.no_addr) {
Packit Service 360c39
		/* This should never happen */
Packit Service 360c39
		log_crit( _("Dotdot parent already set for block %llu (0x%llx)"
Packit Service 360c39
			    "-> %llu (0x%llx)\n"),
Packit Service 360c39
			  (unsigned long long)childblock,
Packit Service 360c39
			  (unsigned long long)childblock,
Packit Service 360c39
			  (unsigned long long)di->dotdot_parent.no_addr,
Packit Service 360c39
			  (unsigned long long)di->dotdot_parent.no_addr);
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	log_debug("Setting '..' for directory block (0x%llx) to parent "
Packit Service 360c39
		  "(0x%llx)\n", (unsigned long long)childblock,
Packit Service 360c39
		  (unsigned long long)parent.no_addr);
Packit Service 360c39
	di->dotdot_parent.no_addr = parent.no_addr;
Packit Service 360c39
	di->dotdot_parent.no_formal_ino = parent.no_formal_ino;
Packit Service 360c39
	return 0;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
static int check_eattr_indir(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
	*bh = bread(ip->i_sbd, block);
Packit Service 360c39
	return 0;
Packit Service 360c39
}
Packit Service 360c39
static int check_eattr_leaf(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
	*bh = bread(ip->i_sbd, block);
Packit Service 360c39
	return 0;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
static const char *de_type_string(uint8_t de_type)
Packit Service 360c39
{
Packit Service 360c39
	const char *de_types[15] = {"unknown", "fifo", "chrdev", "invalid",
Packit Service 360c39
				    "directory", "invalid", "blkdev", "invalid",
Packit Service 360c39
				    "file", "invalid", "symlink", "invalid",
Packit Service 360c39
				    "socket", "invalid", "wht"};
Packit Service 360c39
	if (de_type < 15)
Packit Service 360c39
		return de_types[de_type];
Packit Service 360c39
	return de_types[3]; /* invalid */
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
static int check_file_type(uint64_t block, uint8_t de_type, int q,
Packit Service 360c39
			   int gfs1, int *isdir)
Packit Service 360c39
{
Packit Service 360c39
	struct dir_info *dt;
Packit Service 360c39
Packit Service 360c39
	*isdir = 0;
Packit Service 360c39
	if (q != GFS2_BLKST_DINODE) {
Packit Service 360c39
		log_err( _("Invalid block type\n"));
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	if (de_type == (gfs1 ? GFS_FILE_DIR : DT_DIR))
Packit Service 360c39
		*isdir = 1;
Packit Service 360c39
	/* Check if the dinode is in the dir tree */
Packit Service 360c39
	dt = dirtree_find(block);
Packit Service 360c39
	/* This is a bit confusing, so let me explain:
Packit Service 360c39
	   If the dirent says the inode supposed to be for a directory,
Packit Service 360c39
	   it should be in the dir tree. If it is, no problem, return 0.
Packit Service 360c39
	   If it's not, return 1 (wrong type). If it's not supposed to be
Packit Service 360c39
	   a directory, it shouldn't be in the dir tree. */
Packit Service 360c39
	if (dt)
Packit Service 360c39
		return !(*isdir);
Packit Service 360c39
	return *isdir;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
struct metawalk_fxns pass2_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
Packit Service 360c39
/* bad_formal_ino - handle mismatches in formal inode number
Packit Service 360c39
 * Returns: 0 if the dirent was repaired
Packit Service 360c39
 *          1 if the caller should delete the dirent
Packit Service 360c39
 */
Packit Service 360c39
static int bad_formal_ino(struct gfs2_inode *ip, struct gfs2_dirent *dent,
Packit Service 360c39
			  struct gfs2_inum entry, const char *tmp_name,
Packit Service 360c39
			  int q, struct gfs2_dirent *de,
Packit Service 360c39
			  struct gfs2_buffer_head *bh)
Packit Service 360c39
{
Packit Service 360c39
	struct inode_info *ii;
Packit Service 360c39
	struct dir_info *di = NULL;
Packit Service 360c39
	struct gfs2_inode *child_ip;
Packit Service 360c39
	struct gfs2_inum childs_dotdot;
Packit Service 360c39
	struct gfs2_sbd *sdp = ip->i_sbd;
Packit Service 360c39
	int error;
Packit Service 360c39
	struct gfs2_inum inum = { 0 };
Packit Service 360c39
Packit Service 360c39
	ii = inodetree_find(entry.no_addr);
Packit Service 360c39
	if (ii)
Packit Service 360c39
		inum = ii->di_num;
Packit Service 360c39
	else {
Packit Service 360c39
		di = dirtree_find(entry.no_addr);
Packit Service 360c39
		if (di)
Packit Service 360c39
			inum = di->dinode;
Packit Service 360c39
		else if (link1_type(&clink1map, entry.no_addr) == 1) {
Packit Service 360c39
			struct gfs2_inode *dent_ip;
Packit Service 360c39
Packit Service 360c39
			dent_ip = fsck_load_inode(ip->i_sbd, entry.no_addr);
Packit Service 360c39
			inum = dent_ip->i_di.di_num;
Packit Service 360c39
			fsck_inode_put(&dent_ip);
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
	log_err( _("Directory entry '%s' pointing to block %llu (0x%llx) in "
Packit Service 360c39
		   "directory %llu (0x%llx) has the wrong 'formal' inode "
Packit Service 360c39
		   "number.\n"), tmp_name, (unsigned long long)entry.no_addr,
Packit Service 360c39
		 (unsigned long long)entry.no_addr,
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
	log_err( _("The directory entry has %llu (0x%llx) but the inode has "
Packit Service 360c39
		   "%llu (0x%llx)\n"), (unsigned long long)entry.no_formal_ino,
Packit Service 360c39
		 (unsigned long long)entry.no_formal_ino,
Packit Service 360c39
		 (unsigned long long)inum.no_formal_ino,
Packit Service 360c39
		 (unsigned long long)inum.no_formal_ino);
Packit Service 360c39
	if (q != GFS2_BLKST_DINODE || !strcmp("..", tmp_name)) {
Packit Service 360c39
		if (query( _("Remove the corrupt directory entry? (y/n) ")))
Packit Service 360c39
			return 1;
Packit Service 360c39
		log_err( _("Corrupt directory entry not removed.\n"));
Packit Service 360c39
		return 0;
Packit Service 360c39
	}
Packit Service 360c39
	/* We have a directory pointing to another directory, but the
Packit Service 360c39
	   formal inode number still doesn't match. If that directory
Packit Service 360c39
	   has a '..' pointing back, just fix up the no_formal_ino. */
Packit Service 360c39
	child_ip = lgfs2_inode_read(sdp, entry.no_addr);
Packit Service 360c39
	error = dir_search(child_ip, "..", 2, NULL, &childs_dotdot);
Packit Service 360c39
	if (!error && childs_dotdot.no_addr == ip->i_di.di_num.no_addr) {
Packit Service 360c39
		log_err( _("The entry points to another directory with intact "
Packit Service 360c39
			   "linkage.\n"));
Packit Service 360c39
		if (query( _("Fix the bad directory entry? (y/n) "))) {
Packit Service 360c39
			log_err( _("Fixing the corrupt directory entry.\n"));
Packit Service 360c39
			entry.no_formal_ino = inum.no_formal_ino;
Packit Service 360c39
			de->de_inum.no_formal_ino = entry.no_formal_ino;
Packit Service 360c39
			gfs2_dirent_out(de, (char *)dent);
Packit Service 360c39
			bmodified(bh);
Packit Service 360c39
			incr_link_count(entry, ip, _("fixed reference"));
Packit Service 360c39
			set_parent_dir(sdp, entry, ip->i_di.di_num);
Packit Service 360c39
		} else {
Packit Service 360c39
			log_err( _("Directory entry not fixed.\n"));
Packit Service 360c39
		}
Packit Service 360c39
	} else {
Packit Service 360c39
		if (query( _("Remove the corrupt directory entry? (y/n) "))) {
Packit Service 360c39
			inode_put(&child_ip);
Packit Service 360c39
			return 1;
Packit Service 360c39
		}
Packit Service 360c39
		log_err( _("Corrupt directory entry not removed.\n"));
Packit Service 360c39
	}
Packit Service 360c39
	inode_put(&child_ip);
Packit Service 360c39
	return 0;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
static int hash_table_index(uint32_t hash, struct gfs2_inode *ip)
Packit Service 360c39
{
Packit Service 360c39
	return hash >> (32 - ip->i_di.di_depth);
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
static int hash_table_max(int lindex, struct gfs2_inode *ip,
Packit Service 360c39
		   struct gfs2_buffer_head *bh)
Packit Service 360c39
{
Packit Service 360c39
	struct gfs2_leaf *leaf = (struct gfs2_leaf *)bh->b_data;
Packit Service 360c39
	return (1 << (ip->i_di.di_depth - be16_to_cpu(leaf->lf_depth))) +
Packit Service 360c39
		lindex - 1;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
static int check_leaf_depth(struct gfs2_inode *ip, uint64_t leaf_no,
Packit Service 360c39
			    int ref_count, struct gfs2_buffer_head *lbh)
Packit Service 360c39
{
Packit Service 360c39
	struct gfs2_leaf *leaf = (struct gfs2_leaf *)lbh->b_data;
Packit Service 360c39
	int cur_depth = be16_to_cpu(leaf->lf_depth);
Packit Service 360c39
	int exp_count = 1 << (ip->i_di.di_depth - cur_depth);
Packit Service 360c39
	int divisor;
Packit Service 360c39
	int factor, correct_depth;
Packit Service 360c39
Packit Service 360c39
	if (exp_count == ref_count)
Packit Service 360c39
		return 0;
Packit Service 360c39
Packit Service 360c39
	factor = 0;
Packit Service 360c39
	divisor = ref_count;
Packit Service 360c39
	while (divisor > 1) {
Packit Service 360c39
		factor++;
Packit Service 360c39
		divisor >>= 1;
Packit Service 360c39
	}
Packit Service 360c39
	if (ip->i_di.di_depth < factor) /* can't be fixed--leaf must be on the
Packit Service 360c39
					   wrong dinode. */
Packit Service 360c39
		return -1;
Packit Service 360c39
	correct_depth = ip->i_di.di_depth - factor;
Packit Service 360c39
	if (cur_depth == correct_depth)
Packit Service 360c39
		return 0;
Packit Service 360c39
Packit Service 360c39
	log_err(_("Leaf block %llu (0x%llx) in dinode %llu (0x%llx) has the "
Packit Service 360c39
		  "wrong depth: is %d (length %d), should be %d (length "
Packit Service 360c39
		  "%d).\n"),
Packit Service 360c39
		(unsigned long long)leaf_no, (unsigned long long)leaf_no,
Packit Service 360c39
		(unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
		(unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
		cur_depth, ref_count, correct_depth, exp_count);
Packit Service 360c39
	if (!query( _("Fix the leaf block? (y/n)"))) {
Packit Service 360c39
		log_err( _("The leaf block was not fixed.\n"));
Packit Service 360c39
		return 0;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	leaf->lf_depth = cpu_to_be16(correct_depth);
Packit Service 360c39
	bmodified(lbh);
Packit Service 360c39
	log_err( _("The leaf block depth was fixed.\n"));
Packit Service 360c39
	return 1;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/* wrong_leaf: Deal with a dirent discovered to be on the wrong leaf block
Packit Service 360c39
 *
Packit Service 360c39
 * Returns: 1 if the dirent is to be removed, 0 if it needs to be kept,
Packit Service 360c39
 *          or -1 on error
Packit Service 360c39
 */
Packit Service 360c39
static int wrong_leaf(struct gfs2_inode *ip, struct gfs2_inum *entry,
Packit Service 360c39
		      const char *tmp_name, int *lindex, int lindex_max,
Packit Service 360c39
		      int hash_index, struct gfs2_buffer_head *bh,
Packit Service 360c39
		      struct dir_status *ds, struct gfs2_dirent *dent,
Packit Service 360c39
		      struct gfs2_dirent *de, struct gfs2_dirent *prev_de,
Packit Service 360c39
		      uint32_t *count, int q)
Packit Service 360c39
{
Packit Service 360c39
	struct gfs2_sbd *sdp = ip->i_sbd;
Packit Service 360c39
	struct gfs2_buffer_head *dest_lbh;
Packit Service 360c39
	uint64_t planned_leaf, real_leaf;
Packit Service 360c39
	int li, dest_ref, error;
Packit Service 360c39
	uint64_t *tbl;
Packit Service 360c39
	int di_depth;
Packit Service 360c39
Packit Service 360c39
	log_err(_("Directory entry '%s' at block %lld (0x%llx) is on the "
Packit Service 360c39
		  "wrong leaf block.\n"), tmp_name,
Packit Service 360c39
		(unsigned long long)entry->no_addr,
Packit Service 360c39
		(unsigned long long)entry->no_addr);
Packit Service 360c39
	log_err(_("Leaf index is: 0x%x. The range for this leaf block is "
Packit Service 360c39
		  "0x%x - 0x%x\n"), hash_index, *lindex, lindex_max);
Packit Service 360c39
	if (!query( _("Move the misplaced directory entry to "
Packit Service 360c39
		      "a valid leaf block? (y/n) "))) {
Packit Service 360c39
		log_err( _("Misplaced directory entry not moved.\n"));
Packit Service 360c39
		return 0;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	/* check the destination leaf block's depth */
Packit Service 360c39
	tbl = get_dir_hash(ip);
Packit Service 360c39
	if (tbl == NULL) {
Packit Service 360c39
		perror("get_dir_hash");
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	planned_leaf = be64_to_cpu(tbl[hash_index]);
Packit Service 360c39
	log_err(_("Moving it from leaf %llu (0x%llx) to %llu (0x%llx)\n"),
Packit Service 360c39
		(unsigned long long)be64_to_cpu(tbl[*lindex]),
Packit Service 360c39
		(unsigned long long)be64_to_cpu(tbl[*lindex]),
Packit Service 360c39
		(unsigned long long)planned_leaf,
Packit Service 360c39
		(unsigned long long)planned_leaf);
Packit Service 360c39
	/* Can't trust lf_depth; we have to count */
Packit Service 360c39
	dest_ref = 0;
Packit Service 360c39
	for (li = 0; li < (1 << ip->i_di.di_depth); li++) {
Packit Service 360c39
		if (be64_to_cpu(tbl[li]) == planned_leaf)
Packit Service 360c39
			dest_ref++;
Packit Service 360c39
		else if (dest_ref)
Packit Service 360c39
			break;
Packit Service 360c39
	}
Packit Service 360c39
	dest_lbh = bread(sdp, planned_leaf);
Packit Service 360c39
	check_leaf_depth(ip, planned_leaf, dest_ref, dest_lbh);
Packit Service 360c39
	brelse(dest_lbh);
Packit Service 360c39
	free(tbl);
Packit Service 360c39
Packit Service 360c39
	/* check if it's already on the correct leaf block */
Packit Service 360c39
	error = dir_search(ip, tmp_name, de->de_name_len, NULL, &de->de_inum);
Packit Service 360c39
	if (!error) {
Packit Service 360c39
		log_err(_("The misplaced directory entry already appears on "
Packit Service 360c39
			  "the correct leaf block.\n"));
Packit Service 360c39
		log_err( _("The bad duplicate directory entry "
Packit Service 360c39
			   "'%s' was cleared.\n"), tmp_name);
Packit Service 360c39
		return 1; /* nuke the dent upon return */
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	di_depth = ip->i_di.di_depth;
Packit Service 360c39
	if (dir_add(ip, tmp_name, de->de_name_len, &de->de_inum,
Packit Service 360c39
		    de->de_type) == 0) {
Packit Service 360c39
		log_err(_("The misplaced directory entry was moved to a "
Packit Service 360c39
			  "valid leaf block.\n"));
Packit Service 360c39
		if (ip->i_di.di_depth > di_depth) {
Packit Service 360c39
			log_err(_("Directory hash table was doubled.\n"));
Packit Service 360c39
			hash_index <<= (ip->i_di.di_depth - di_depth);
Packit Service 360c39
			(*lindex) <<= (ip->i_di.di_depth - di_depth);
Packit Service 360c39
		}
Packit Service 360c39
		if (lgfs2_get_leaf_ptr(ip, hash_index, &real_leaf)) {
Packit Service 360c39
			log_err(_("Could not read leaf %d in dinode %"PRIu64": %s\n"), hash_index,
Packit Service 360c39
			        (uint64_t)ip->i_di.di_num.no_addr, strerror(errno));
Packit Service 360c39
		}
Packit Service 360c39
		if (real_leaf != planned_leaf) {
Packit Service 360c39
			log_err(_("The planned leaf was split. The new leaf "
Packit Service 360c39
				  "is: %llu (0x%llx). di_blocks=%llu\n"),
Packit Service 360c39
				(unsigned long long)real_leaf,
Packit Service 360c39
				(unsigned long long)real_leaf,
Packit Service 360c39
				(unsigned long long)ip->i_di.di_blocks);
Packit Service 360c39
			fsck_bitmap_set(ip, real_leaf, _("split leaf"),
Packit Service 360c39
					sdp->gfs1 ? GFS2_BLKST_DINODE :
Packit Service 360c39
					GFS2_BLKST_USED);
Packit Service 360c39
		}
Packit Service 360c39
		/* If the misplaced dirent was supposed to be earlier in the
Packit Service 360c39
		   hash table, we need to adjust our counts for the blocks
Packit Service 360c39
		   that have already been processed. If it's supposed to
Packit Service 360c39
		   appear later, we'll count it has part of our normal
Packit Service 360c39
		   processing when we get to that leaf block later on in the
Packit Service 360c39
		   hash table. */
Packit Service 360c39
		if (hash_index > *lindex) {
Packit Service 360c39
			log_err(_("Accounting deferred.\n"));
Packit Service 360c39
			return 1; /* nuke the dent upon return */
Packit Service 360c39
		}
Packit Service 360c39
		/* If we get here, it's because we moved a dent to another
Packit Service 360c39
		   leaf, but that leaf has already been processed. So we have
Packit Service 360c39
		   to nuke the dent from this leaf when we return, but we
Packit Service 360c39
		   still need to do the "good dent" accounting. */
Packit Service 360c39
		if (de->de_type == (sdp->gfs1 ? GFS_FILE_DIR : DT_DIR)) {
Packit Service 360c39
			error = set_parent_dir(sdp, de->de_inum,
Packit Service 360c39
					       ip->i_di.di_num);
Packit Service 360c39
			if (error > 0)
Packit Service 360c39
				/* This is a bit of a kludge, but returning 0
Packit Service 360c39
				   in this case causes the caller to go through
Packit Service 360c39
				   function set_parent_dir a second time and
Packit Service 360c39
				   deal properly with the hard link. */
Packit Service 360c39
				return 0;
Packit Service 360c39
		}
Packit Service 360c39
		error = incr_link_count(*entry, ip,
Packit Service 360c39
					_("moved valid reference"));
Packit Service 360c39
		if (error > 0 &&
Packit Service 360c39
		    bad_formal_ino(ip, dent, *entry, tmp_name, q, de, bh) == 1)
Packit Service 360c39
			return 1; /* nuke it */
Packit Service 360c39
Packit Service 360c39
		/* You cannot do this:
Packit Service 360c39
		   (*count)++;
Packit Service 360c39
		   The reason is: *count is the count of dentries on the leaf,
Packit Service 360c39
		   and we moved the dentry to a previous leaf within the same
Packit Service 360c39
		   directory dinode. So the directory counts still get
Packit Service 360c39
		   incremented, but not leaf entries. When we called dir_add
Packit Service 360c39
		   above, it should have fixed that prev leaf's lf_entries. */
Packit Service 360c39
		ds->entry_count++;
Packit Service 360c39
		return 1;
Packit Service 360c39
	} else {
Packit Service 360c39
		log_err(_("Error moving directory entry.\n"));
Packit Service 360c39
		return 1; /* nuke it */
Packit Service 360c39
	}
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/* basic_dentry_checks - fundamental checks for directory entries
Packit Service 360c39
 *
Packit Service 360c39
 * @ip: pointer to the incode inode structure
Packit Service 360c39
 * @entry: pointer to the inum info
Packit Service 360c39
 * @tmp_name: user-friendly file name
Packit Service 360c39
 * @count: pointer to the entry count
Packit Service 360c39
 * @de: pointer to the directory entry
Packit Service 360c39
 *
Packit Service 360c39
 * Returns: 1 means corruption, nuke the dentry, 0 means checks pass
Packit Service 360c39
 */
Packit Service 360c39
static int basic_dentry_checks(struct gfs2_inode *ip, struct gfs2_dirent *dent,
Packit Service 360c39
			       struct gfs2_inum *entry, const char *tmp_name,
Packit Service 360c39
			       uint32_t *count, struct gfs2_dirent *de,
Packit Service 360c39
			       struct dir_status *ds, int *q,
Packit Service 360c39
			       struct gfs2_buffer_head *bh, int *isdir)
Packit Service 360c39
{
Packit Service 360c39
	struct gfs2_sbd *sdp = ip->i_sbd;
Packit Service 360c39
	uint32_t calculated_hash;
Packit Service 360c39
	struct gfs2_inode *entry_ip = NULL;
Packit Service 360c39
	int error;
Packit Service 360c39
	struct inode_info *ii;
Packit Service 360c39
	struct dir_info *di = NULL;
Packit Service 360c39
	struct gfs2_inum inum = { 0 };
Packit Service 360c39
Packit Service 360c39
	*isdir = 0;
Packit Service 360c39
	if (!valid_block_ip(ip, entry->no_addr)) {
Packit Service 360c39
		log_err( _("Block # referenced by directory entry %s in inode "
Packit Service 360c39
			   "%lld (0x%llx) is invalid\n"),
Packit Service 360c39
			 tmp_name, (unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
			 (unsigned long long)ip->i_di.di_num.no_addr);
Packit Service 360c39
		if (query( _("Clear directory entry to out of range block? "
Packit Service 360c39
			    "(y/n) "))) {
Packit Service 360c39
			return 1;
Packit Service 360c39
		} else {
Packit Service 360c39
			log_err( _("Directory entry to out of range block remains\n"));
Packit Service 360c39
			(*count)++;
Packit Service 360c39
			ds->entry_count++;
Packit Service 360c39
			/* can't do this because the block is out of range:
Packit Service 360c39
			   incr_link_count(entry); */
Packit Service 360c39
			return 0;
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	if (de->de_rec_len < GFS2_DIRENT_SIZE(de->de_name_len) ||
Packit Service 360c39
	    de->de_name_len > GFS2_FNAMESIZE) {
Packit Service 360c39
		log_err( _("Dir entry with bad record or name length\n"
Packit Service 360c39
			"\tRecord length = %u\n\tName length = %u\n"),
Packit Service 360c39
			de->de_rec_len, de->de_name_len);
Packit Service 360c39
		if (!query( _("Clear the directory entry? (y/n) "))) {
Packit Service 360c39
			log_err( _("Directory entry not fixed.\n"));
Packit Service 360c39
			return 0;
Packit Service 360c39
		}
Packit Service 360c39
		/* Don't be tempted to do this:
Packit Service 360c39
		fsck_bitmap_set(ip, ip->i_di.di_num.no_addr,
Packit Service 360c39
				_("corrupt directory entry"),
Packit Service 360c39
				GFS2_BLKST_FREE);
Packit Service 360c39
		We can't free it because another dir may have a valid reference
Packit Service 360c39
		to it. Just return 1 so we can delete the bad dirent. */
Packit Service 360c39
		log_err( _("Bad directory entry deleted.\n"));
Packit Service 360c39
		return 1;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	calculated_hash = gfs2_disk_hash(tmp_name, de->de_name_len);
Packit Service 360c39
	if (de->de_hash != calculated_hash){
Packit Service 360c39
	        log_err( _("Dir entry with bad hash or name length\n"
Packit Service 360c39
			   "\tHash found         = %u (0x%x)\n"
Packit Service 360c39
			   "\tFilename           = %s\n"),
Packit Service 360c39
			 de->de_hash, de->de_hash, tmp_name);
Packit Service 360c39
		log_err( _("\tName length found  = %u\n"
Packit Service 360c39
			   "\tHash expected      = %u (0x%x)\n"),
Packit Service 360c39
			 de->de_name_len, calculated_hash, calculated_hash);
Packit Service 360c39
		if (!query( _("Fix directory hash for %s? (y/n) "),
Packit Service 360c39
			   tmp_name)) {
Packit Service 360c39
			log_err( _("Directory entry hash for %s not "
Packit Service 360c39
				   "fixed.\n"), tmp_name);
Packit Service 360c39
			return 0;
Packit Service 360c39
		}
Packit Service 360c39
		de->de_hash = calculated_hash;
Packit Service 360c39
		gfs2_dirent_out(de, (char *)dent);
Packit Service 360c39
		bmodified(bh);
Packit Service 360c39
		log_err( _("Directory entry hash for %s fixed.\n"),
Packit Service 360c39
			 tmp_name);
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	*q = bitmap_type(sdp, entry->no_addr);
Packit Service 360c39
	/* Get the status of the directory inode */
Packit Service 360c39
	/**
Packit Service 360c39
	 * 1. Blocks marked "invalid" were invalidated due to duplicate
Packit Service 360c39
	 * block references.  Pass1b should have already taken care of deleting
Packit Service 360c39
	 * their metadata, so here we only need to delete the directory entries
Packit Service 360c39
	 * pointing to them.  We delete the metadata in pass1b because we need
Packit Service 360c39
	 * to eliminate the inode referencing the duplicate-referenced block
Packit Service 360c39
	 * from the list of candidates to keep.  So we have a delete-as-we-go
Packit Service 360c39
	 * policy.
Packit Service 360c39
	 *
Packit Service 360c39
	 * 2. Blocks marked "bad" need to have their entire
Packit Service 360c39
	 * metadata tree deleted.
Packit Service 360c39
	*/
Packit Service 360c39
	if (*q != GFS2_BLKST_DINODE) {
Packit Service 360c39
		log_err( _("Directory entry '%s' referencing inode %llu "
Packit Service 360c39
			   "(0x%llx) in dir inode %llu (0x%llx) block type "
Packit Service 360c39
			   "%d: %s.\n"), tmp_name,
Packit Service 360c39
			 (unsigned long long)entry->no_addr,
Packit Service 360c39
			 (unsigned long long)entry->no_addr,
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
			 *q, *q == GFS2_BLKST_FREE ?
Packit Service 360c39
			 _("was previously marked invalid") :
Packit Service 360c39
			 _("was deleted or is not an inode"));
Packit Service 360c39
Packit Service 360c39
		if (!query( _("Clear directory entry to non-inode block? "
Packit Service 360c39
			     "(y/n) "))) {
Packit Service 360c39
			log_err( _("Directory entry to non-inode block remains\n"));
Packit Service 360c39
			return 0;
Packit Service 360c39
		}
Packit Service 360c39
Packit Service 360c39
		/* Don't decrement the link here: Here in pass2, we increment
Packit Service 360c39
		   only when we know it's okay.
Packit Service 360c39
		   decr_link_count(ip->i_di.di_num.no_addr, blah); */
Packit Service 360c39
		/* If it was previously marked invalid (i.e. known
Packit Service 360c39
		   to be bad, not just a free block, etc.) then the temptation
Packit Service 360c39
		   would be to delete any metadata it holds.  The trouble is:
Packit Service 360c39
		   if it's invalid, we may or _may_not_ have traversed its
Packit Service 360c39
		   metadata tree, and therefore may or may not have marked the
Packit Service 360c39
		   blocks it points to as a metadata type, or as a duplicate.
Packit Service 360c39
		   If there is really a duplicate reference, but we didn't
Packit Service 360c39
		   process the metadata tree because it's invalid, some other
Packit Service 360c39
		   inode has a reference to the metadata block, in which case
Packit Service 360c39
		   freeing it would do more harm than good.  IOW we cannot
Packit Service 360c39
		   count on "delete_block_if_notdup" knowing whether it's
Packit Service 360c39
		   really a duplicate block if we never traversed the metadata
Packit Service 360c39
		   tree for the invalid inode. */
Packit Service 360c39
		return 1;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	error = check_file_type(entry->no_addr, de->de_type, *q, sdp->gfs1,
Packit Service 360c39
				isdir);
Packit Service 360c39
	if (error < 0) {
Packit Service 360c39
		log_err( _("Error: directory entry type is "
Packit Service 360c39
			   "incompatible with block type at block %lld "
Packit Service 360c39
			   "(0x%llx) in directory inode %llu (0x%llx).\n"),
Packit Service 360c39
			 (unsigned long long)entry->no_addr,
Packit Service 360c39
			 (unsigned long long)entry->no_addr,
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
		log_err( _("Directory entry type is %d, block type is %d.\n"),
Packit Service 360c39
			 de->de_type, *q);
Packit Service 360c39
		stack;
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	if (error > 0) {
Packit Service 360c39
		log_err( _("Type '%s' in dir entry (%s, %llu/0x%llx) conflicts"
Packit Service 360c39
			 " with type '%s' in dinode. (Dir entry is stale.)\n"),
Packit Service 360c39
			 de_type_string(de->de_type), tmp_name,
Packit Service 360c39
			 (unsigned long long)entry->no_addr,
Packit Service 360c39
			 (unsigned long long)entry->no_addr,
Packit Service 360c39
			 block_type_string(*q));
Packit Service 360c39
		if (!query( _("Clear stale directory entry? (y/n) "))) {
Packit Service 360c39
			log_err( _("Stale directory entry remains\n"));
Packit Service 360c39
			return 0;
Packit Service 360c39
		}
Packit Service 360c39
		if (ip->i_di.di_num.no_addr == entry->no_addr)
Packit Service 360c39
			entry_ip = ip;
Packit Service 360c39
		else
Packit Service 360c39
			entry_ip = fsck_load_inode(sdp, entry->no_addr);
Packit Service 360c39
		check_inode_eattr(entry_ip, &delete_eattrs);
Packit Service 360c39
		if (entry_ip != ip)
Packit Service 360c39
			fsck_inode_put(&entry_ip);
Packit Service 360c39
		return 1;
Packit Service 360c39
	}
Packit Service 360c39
	/* We need to verify the formal inode number matches. If it doesn't,
Packit Service 360c39
	   it needs to be deleted. */
Packit Service 360c39
	ii = inodetree_find(entry->no_addr);
Packit Service 360c39
	if (ii)
Packit Service 360c39
		inum = ii->di_num;
Packit Service 360c39
	else {
Packit Service 360c39
		di = dirtree_find(entry->no_addr);
Packit Service 360c39
		if (di)
Packit Service 360c39
			inum = di->dinode;
Packit Service 360c39
		else if (link1_type(&nlink1map, entry->no_addr) == 1) {
Packit Service 360c39
			/* Since we don't have ii or di, the only way to
Packit Service 360c39
			   validate formal_ino is to read in the inode, which
Packit Service 360c39
			   would kill performance. So skip it for now. */
Packit Service 360c39
			return 0;
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
	if (inum.no_formal_ino != entry->no_formal_ino) {
Packit Service 360c39
		log_err( _("Directory entry '%s' pointing to block %llu "
Packit Service 360c39
			   "(0x%llx) in directory %llu (0x%llx) has the "
Packit Service 360c39
			   "wrong 'formal' inode number.\n"), tmp_name,
Packit Service 360c39
			 (unsigned long long)entry->no_addr,
Packit Service 360c39
			 (unsigned long long)entry->no_addr,
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
		log_err( _("The directory entry has %llu (0x%llx) but the "
Packit Service 360c39
			   "inode has %llu (0x%llx)\n"),
Packit Service 360c39
			 (unsigned long long)entry->no_formal_ino,
Packit Service 360c39
			 (unsigned long long)entry->no_formal_ino,
Packit Service 360c39
			 (unsigned long long)inum.no_formal_ino,
Packit Service 360c39
			 (unsigned long long)inum.no_formal_ino);
Packit Service 360c39
		return 1;
Packit Service 360c39
	}
Packit Service 360c39
	/* Check for a special case where a (bad) GFS1 dirent points to what
Packit Service 360c39
	 * is not a known inode. It could be other GFS1 metadata, such as an
Packit Service 360c39
	 * eattr or indirect block, but marked "dinode" in the bitmap because
Packit Service 360c39
	 * gfs1 marked all gfs1 metadata that way. */
Packit Service 360c39
	if (ii == NULL && di == NULL && sdp->gfs1) {
Packit Service 360c39
		struct gfs2_buffer_head *tbh;
Packit Service 360c39
Packit Service 360c39
		tbh = bread(sdp, entry->no_addr);
Packit Service 360c39
		if (gfs2_check_meta(tbh, GFS2_METATYPE_DI)) { /* not dinode */
Packit Service 360c39
			log_err( _("Directory entry '%s' pointing to block "
Packit Service 360c39
				   "%llu (0x%llx) in directory %llu (0x%llx) "
Packit Service 360c39
				   "is not really a GFS1 dinode.\n"), tmp_name,
Packit Service 360c39
			 (unsigned long long)entry->no_addr,
Packit Service 360c39
			 (unsigned long long)entry->no_addr,
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
			brelse(tbh);
Packit Service 360c39
			return 1;
Packit Service 360c39
		}
Packit Service 360c39
		brelse(tbh);
Packit Service 360c39
	}
Packit Service 360c39
	return 0;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
static int dirref_find(struct gfs2_inode *ip, struct gfs2_dirent *dent,
Packit Service 360c39
		       struct gfs2_dirent *prev, struct gfs2_buffer_head *bh,
Packit Service 360c39
		       char *filename, uint32_t *count, int *lindex,
Packit Service 360c39
		       void *private)
Packit Service 360c39
{
Packit Service 360c39
	/* the metawalk_fxn's private field must be set to the dentry
Packit Service 360c39
	 * block we want to clear */
Packit Service 360c39
	struct gfs2_inum *entry = (struct gfs2_inum *)private;
Packit Service 360c39
	struct gfs2_dirent dentry, *de;
Packit Service 360c39
	char fn[MAX_FILENAME];
Packit Service 360c39
Packit Service 360c39
	memset(&dentry, 0, sizeof(struct gfs2_dirent));
Packit Service 360c39
	gfs2_dirent_in(&dentry, (char *)dent);
Packit Service 360c39
	de = &dentry;
Packit Service 360c39
Packit Service 360c39
	if (de->de_inum.no_addr != entry->no_addr) {
Packit Service 360c39
		(*count)++;
Packit Service 360c39
		return 0;
Packit Service 360c39
	}
Packit Service 360c39
	if (de->de_inum.no_formal_ino == dent->de_inum.no_formal_ino) {
Packit Service 360c39
		log_debug("Formal inode number matches; must be a hard "
Packit Service 360c39
			  "link.\n");
Packit Service 360c39
		goto out;
Packit Service 360c39
	}
Packit Service 360c39
	log_err(_("The original reference to inode %lld (0x%llx) from "
Packit Service 360c39
		  "directory %lld (0x%llx) has the wrong 'formal' inode "
Packit Service 360c39
		  "number.\n"), (unsigned long long)entry->no_addr,
Packit Service 360c39
		(unsigned long long)entry->no_addr,
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
	memset(fn, 0, sizeof(fn));
Packit Service 360c39
	if (de->de_name_len < MAX_FILENAME)
Packit Service 360c39
		strncpy(fn, filename, de->de_name_len);
Packit Service 360c39
	else
Packit Service 360c39
		strncpy(fn, filename, MAX_FILENAME - 1);
Packit Service 360c39
	log_err(_("The bad reference '%s' had formal inode number: %lld "
Packit Service 360c39
		  "(0x%llx) but the correct value is: %lld (0x%llx)\n"),
Packit Service 360c39
		fn, (unsigned long long)de->de_inum.no_formal_ino,
Packit Service 360c39
		(unsigned long long)de->de_inum.no_formal_ino,
Packit Service 360c39
		(unsigned long long)entry->no_formal_ino,
Packit Service 360c39
		(unsigned long long)entry->no_formal_ino);
Packit Service 360c39
	if (!query(_("Delete the bad directory entry? (y/n) "))) {
Packit Service 360c39
		log_err(_("The corrupt directory entry was not fixed.\n"));
Packit Service 360c39
		goto out;
Packit Service 360c39
	}
Packit Service 360c39
	decr_link_count(entry->no_addr, ip->i_di.di_num.no_addr,
Packit Service 360c39
			ip->i_sbd->gfs1, _("bad original reference"));
Packit Service 360c39
	dirent2_del(ip, bh, prev, dent);
Packit Service 360c39
	log_err(_("The corrupt directory entry '%s' was deleted.\n"), fn);
Packit Service 360c39
out:
Packit Service 360c39
	return -1; /* force check_dir to stop; don't waste time. */
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/**
Packit Service 360c39
 * check_suspicious_dirref - double-check a questionable first dentry ref
Packit Service 360c39
 *
Packit Service 360c39
 * This function is called when a dentry has caused us to increment the
Packit Service 360c39
 * link count to a file from 1 to 2, and we know the object pointed to is
Packit Service 360c39
 * not a directory. (Most likely, it'a a file). The second directory to
Packit Service 360c39
 * reference the dinode has the correct formal inode number, but when we
Packit Service 360c39
 * created the original reference in the counted links bitmap (clink1map),
Packit Service 360c39
 * we had no way to check the formal inode number. (Well, we could have read
Packit Service 360c39
 * in the dinode, but that would kill fsck.gfs2 performance.)
Packit Service 360c39
 * So now we have to walk through the directory tree and find that original
Packit Service 360c39
 * reference so make sure it's a valid reference. If the formal inode number
Packit Service 360c39
 * is the same, it's a hard link (which is unlikely for gfs2). If it's not
Packit Service 360c39
 * the same, that's an error, and we need to delete the damaged original
Packit Service 360c39
 * dentry, since we failed to detect the problem earlier.
Packit Service 360c39
 */
Packit Service 360c39
static int check_suspicious_dirref(struct gfs2_sbd *sdp,
Packit Service 360c39
				   struct gfs2_inum *entry)
Packit Service 360c39
{
Packit Service 360c39
	struct osi_node *tmp, *next = NULL;
Packit Service 360c39
	struct dir_info *dt;
Packit Service 360c39
	struct gfs2_inode *ip;
Packit Service 360c39
	uint64_t dirblk;
Packit Service 360c39
	int error = FSCK_OK;
Packit Service 360c39
	struct metawalk_fxns dirref_hunt = {
Packit Service 360c39
		.private = (void *)entry,
Packit Service 360c39
		.check_dentry = dirref_find,
Packit Service 360c39
	};
Packit Service 360c39
Packit Service 360c39
	log_debug("This dentry is good, but since this is a second "
Packit Service 360c39
		  "reference to block 0x%llx, we need to check the "
Packit Service 360c39
		  "original.\n", (unsigned long long)entry->no_addr);
Packit Service 360c39
	for (tmp = osi_first(&dirtree); tmp; tmp = next) {
Packit Service 360c39
		next = osi_next(tmp);
Packit Service 360c39
		dt = (struct dir_info *)tmp;
Packit Service 360c39
		dirblk = dt->dinode.no_addr;
Packit Service 360c39
		if (skip_this_pass || fsck_abort) /* asked to skip the rest */
Packit Service 360c39
			break;
Packit Service 360c39
		ip = fsck_load_inode(sdp, dirblk);
Packit Service 360c39
		if (ip == NULL) {
Packit Service 360c39
			stack;
Packit Service 360c39
			return FSCK_ERROR;
Packit Service 360c39
		}
Packit Service 360c39
		error = check_dir(sdp, ip, &dirref_hunt);
Packit Service 360c39
		fsck_inode_put(&ip);
Packit Service 360c39
		/* Error just means we found the dentry and dealt with it. */
Packit Service 360c39
		if (error)
Packit Service 360c39
			break;
Packit Service 360c39
	}
Packit Service 360c39
	log_debug("Original reference check complete. Found = %d.\n",
Packit Service 360c39
		  error ? 1 : 0);
Packit Service 360c39
	return 0;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/* FIXME: should maybe refactor this a bit - but need to deal with
Packit Service 360c39
 * FIXMEs internally first */
Packit Service 360c39
static int check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
Packit Service 360c39
			struct gfs2_dirent *prev_de,
Packit Service 360c39
			struct gfs2_buffer_head *bh, char *filename,
Packit Service 360c39
			uint32_t *count, int *lindex, void *priv)
Packit Service 360c39
{
Packit Service 360c39
	struct gfs2_sbd *sdp = ip->i_sbd;
Packit Service 360c39
	int q = 0;
Packit Service 360c39
	char tmp_name[MAX_FILENAME];
Packit Service 360c39
	struct gfs2_inum entry;
Packit Service 360c39
	struct dir_status *ds = (struct dir_status *) priv;
Packit Service 360c39
	int error;
Packit Service 360c39
	struct gfs2_inode *entry_ip = NULL;
Packit Service 360c39
	struct gfs2_dirent dentry, *de;
Packit Service 360c39
	int hash_index; /* index into the hash table based on the hash */
Packit Service 360c39
	int lindex_max; /* largest acceptable hash table index for hash */
Packit Service 360c39
	int isdir;
Packit Service 360c39
Packit Service 360c39
	memset(&dentry, 0, sizeof(struct gfs2_dirent));
Packit Service 360c39
	gfs2_dirent_in(&dentry, (char *)dent);
Packit Service 360c39
	de = &dentry;
Packit Service 360c39
Packit Service 360c39
	entry.no_addr = de->de_inum.no_addr;
Packit Service 360c39
	entry.no_formal_ino = de->de_inum.no_formal_ino;
Packit Service 360c39
Packit Service 360c39
	/* Start of checks */
Packit Service 360c39
	memset(tmp_name, 0, MAX_FILENAME);
Packit Service 360c39
	if (de->de_name_len < MAX_FILENAME)
Packit Service 360c39
		strncpy(tmp_name, filename, de->de_name_len);
Packit Service 360c39
	else
Packit Service 360c39
		strncpy(tmp_name, filename, MAX_FILENAME - 1);
Packit Service 360c39
Packit Service 360c39
	error = basic_dentry_checks(ip, dent, &entry, tmp_name, count, de,
Packit Service 360c39
				    ds, &q, bh, &isdir);
Packit Service 360c39
	if (error)
Packit Service 360c39
		goto nuke_dentry;
Packit Service 360c39
Packit Service 360c39
	if (!strcmp(".", tmp_name)) {
Packit Service 360c39
		log_debug( _("Found . dentry in directory %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
Packit Service 360c39
		if (ds->dotdir) {
Packit Service 360c39
			log_err( _("Already found '.' entry in directory %llu"
Packit Service 360c39
				" (0x%llx)\n"),
Packit Service 360c39
				(unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
				(unsigned long long)ip->i_di.di_num.no_addr);
Packit Service 360c39
			if (!query( _("Clear duplicate '.' entry? (y/n) "))) {
Packit Service 360c39
				log_err( _("Duplicate '.' entry remains\n"));
Packit Service 360c39
				/* FIXME: Should we continue on here
Packit Service 360c39
				 * and check the rest of the '.' entry? */
Packit Service 360c39
				goto dentry_is_valid;
Packit Service 360c39
			}
Packit Service 360c39
			if (ip->i_di.di_num.no_addr == entry.no_addr)
Packit Service 360c39
				entry_ip = ip;
Packit Service 360c39
			else
Packit Service 360c39
				entry_ip = fsck_load_inode(sdp, entry.no_addr);
Packit Service 360c39
			check_inode_eattr(entry_ip, &delete_eattrs);
Packit Service 360c39
			if (entry_ip != ip)
Packit Service 360c39
				fsck_inode_put(&entry_ip);
Packit Service 360c39
			goto nuke_dentry;
Packit Service 360c39
		}
Packit Service 360c39
Packit Service 360c39
		/* GFS2 does not rely on '.' being in a certain
Packit Service 360c39
		 * location */
Packit Service 360c39
Packit Service 360c39
		/* check that '.' refers to this inode */
Packit Service 360c39
		if (entry.no_addr != ip->i_di.di_num.no_addr) {
Packit Service 360c39
			log_err( _("'.' entry's value incorrect in directory %llu"
Packit Service 360c39
				" (0x%llx).  Points to %llu"
Packit Service 360c39
				" (0x%llx) when it should point to %llu"
Packit Service 360c39
				" (0x%llx).\n"),
Packit Service 360c39
				(unsigned long long)entry.no_addr,
Packit Service 360c39
				(unsigned long long)entry.no_addr,
Packit Service 360c39
				(unsigned long long)entry.no_addr,
Packit Service 360c39
				(unsigned long long)entry.no_addr,
Packit Service 360c39
				(unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
				(unsigned long long)ip->i_di.di_num.no_addr);
Packit Service 360c39
			if (!query( _("Remove '.' reference? (y/n) "))) {
Packit Service 360c39
				log_err( _("Invalid '.' reference remains\n"));
Packit Service 360c39
				/* Not setting ds->dotdir here since
Packit Service 360c39
				 * this '.' entry is invalid */
Packit Service 360c39
				goto dentry_is_valid;
Packit Service 360c39
			}
Packit Service 360c39
			if (ip->i_di.di_num.no_addr == entry.no_addr)
Packit Service 360c39
				entry_ip = ip;
Packit Service 360c39
			else
Packit Service 360c39
				entry_ip = fsck_load_inode(sdp, entry.no_addr);
Packit Service 360c39
			check_inode_eattr(entry_ip, &delete_eattrs);
Packit Service 360c39
			if (entry_ip != ip)
Packit Service 360c39
				fsck_inode_put(&entry_ip);
Packit Service 360c39
			goto nuke_dentry;
Packit Service 360c39
		}
Packit Service 360c39
Packit Service 360c39
		ds->dotdir = 1;
Packit Service 360c39
		goto dentry_is_valid;
Packit Service 360c39
	}
Packit Service 360c39
	if (!strcmp("..", tmp_name)) {
Packit Service 360c39
		log_debug( _("Found '..' dentry in directory %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
		if (ds->dotdotdir) {
Packit Service 360c39
			log_err( _("Already had a '..' entry in directory %llu"
Packit Service 360c39
				"(0x%llx)\n"),
Packit Service 360c39
				(unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
				(unsigned long long)ip->i_di.di_num.no_addr);
Packit Service 360c39
			if (!query( _("Clear duplicate '..' entry? (y/n) "))) {
Packit Service 360c39
				log_err( _("Duplicate '..' entry remains\n"));
Packit Service 360c39
				/* FIXME: Should we continue on here
Packit Service 360c39
				 * and check the rest of the '..'
Packit Service 360c39
				 * entry? */
Packit Service 360c39
				goto dentry_is_valid;
Packit Service 360c39
			}
Packit Service 360c39
Packit Service 360c39
			if (ip->i_di.di_num.no_addr == entry.no_addr)
Packit Service 360c39
				entry_ip = ip;
Packit Service 360c39
			else
Packit Service 360c39
				entry_ip = fsck_load_inode(sdp, entry.no_addr);
Packit Service 360c39
			check_inode_eattr(entry_ip, &delete_eattrs);
Packit Service 360c39
			if (entry_ip != ip)
Packit Service 360c39
				fsck_inode_put(&entry_ip);
Packit Service 360c39
Packit Service 360c39
			goto nuke_dentry;
Packit Service 360c39
		}
Packit Service 360c39
		if (!isdir) {
Packit Service 360c39
			log_err( _("Found '..' entry in directory %llu (0x%llx) "
Packit Service 360c39
				"pointing to something that's not a directory"),
Packit Service 360c39
				(unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
				(unsigned long long)ip->i_di.di_num.no_addr);
Packit Service 360c39
			if (!query( _("Clear bad '..' directory entry? (y/n) "))) {
Packit Service 360c39
				log_err( _("Bad '..' directory entry remains\n"));
Packit Service 360c39
				goto dentry_is_valid;
Packit Service 360c39
			}
Packit Service 360c39
			if (ip->i_di.di_num.no_addr == entry.no_addr)
Packit Service 360c39
				entry_ip = ip;
Packit Service 360c39
			else
Packit Service 360c39
				entry_ip = fsck_load_inode(sdp, entry.no_addr);
Packit Service 360c39
			check_inode_eattr(entry_ip, &delete_eattrs);
Packit Service 360c39
			if (entry_ip != ip)
Packit Service 360c39
				fsck_inode_put(&entry_ip);
Packit Service 360c39
Packit Service 360c39
			goto nuke_dentry;
Packit Service 360c39
		}
Packit Service 360c39
		/* GFS2 does not rely on '..' being in a certain location */
Packit Service 360c39
Packit Service 360c39
		/* Add the address this entry is pointing to
Packit Service 360c39
		 * to this inode's dotdot_parent in
Packit Service 360c39
		 * dir_info */
Packit Service 360c39
		if (set_dotdot_dir(sdp, ip->i_di.di_num.no_addr, entry)) {
Packit Service 360c39
			stack;
Packit Service 360c39
			return -1;
Packit Service 360c39
		}
Packit Service 360c39
Packit Service 360c39
		ds->dotdotdir = 1;
Packit Service 360c39
		goto dentry_is_valid;
Packit Service 360c39
	}
Packit Service 360c39
	/* If this is an exhash directory, make sure the dentries in the leaf
Packit Service 360c39
	   block have a hash table index that fits */
Packit Service 360c39
	if (ip->i_di.di_flags & GFS2_DIF_EXHASH) {
Packit Service 360c39
		hash_index = hash_table_index(de->de_hash, ip);
Packit Service 360c39
		lindex_max = hash_table_max(*lindex, ip, bh);
Packit Service 360c39
		if (hash_index < *lindex || hash_index > lindex_max) {
Packit Service 360c39
			int nuke_dent;
Packit Service 360c39
Packit Service 360c39
			nuke_dent = wrong_leaf(ip, &entry, tmp_name, lindex,
Packit Service 360c39
					       lindex_max, hash_index, bh, ds,
Packit Service 360c39
					       dent, de, prev_de, count, q);
Packit Service 360c39
			if (nuke_dent)
Packit Service 360c39
				goto nuke_dentry;
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	/* After this point we're only concerned with directories */
Packit Service 360c39
	if (!isdir) {
Packit Service 360c39
		log_debug( _("Found non-dir inode dentry pointing to %lld "
Packit Service 360c39
			     "(0x%llx)\n"),
Packit Service 360c39
			   (unsigned long long)entry.no_addr,
Packit Service 360c39
			   (unsigned long long)entry.no_addr);
Packit Service 360c39
		goto dentry_is_valid;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	/*log_debug( _("Found plain directory dentry\n"));*/
Packit Service 360c39
	error = set_parent_dir(sdp, entry, ip->i_di.di_num);
Packit Service 360c39
	if (error > 0) {
Packit Service 360c39
		log_err( _("%s: Hard link to block %llu (0x%llx"
Packit Service 360c39
			   ") detected.\n"), tmp_name,
Packit Service 360c39
			(unsigned long long)entry.no_addr,
Packit Service 360c39
			(unsigned long long)entry.no_addr);
Packit Service 360c39
Packit Service 360c39
		if (query( _("Clear hard link to directory? (y/n) ")))
Packit Service 360c39
			goto nuke_dentry;
Packit Service 360c39
		else {
Packit Service 360c39
			log_err( _("Hard link to directory remains\n"));
Packit Service 360c39
			goto dentry_is_valid;
Packit Service 360c39
		}
Packit Service 360c39
	} else if (error < 0) {
Packit Service 360c39
		stack;
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
dentry_is_valid:
Packit Service 360c39
	/* This directory inode links to this inode via this dentry */
Packit Service 360c39
	error = incr_link_count(entry, ip, _("valid reference"));
Packit Service 360c39
	if (error == incr_link_check_orig) {
Packit Service 360c39
		error = check_suspicious_dirref(sdp, &entry);
Packit Service 360c39
	} else if (error == incr_link_ino_mismatch) {
Packit Service 360c39
		log_err("incr_link_count err=%d.\n", error);
Packit Service 360c39
		if (bad_formal_ino(ip, dent, entry, tmp_name, q, de, bh) == 1)
Packit Service 360c39
			goto nuke_dentry;
Packit Service 360c39
	}
Packit Service 360c39
	(*count)++;
Packit Service 360c39
	ds->entry_count++;
Packit Service 360c39
	/* End of checks */
Packit Service 360c39
	return 0;
Packit Service 360c39
Packit Service 360c39
nuke_dentry:
Packit Service 360c39
	dirent2_del(ip, bh, prev_de, dent);
Packit Service 360c39
	log_err( _("Bad directory entry '%s' cleared.\n"), tmp_name);
Packit Service 360c39
	return 1;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/*
Packit Service 360c39
 * write_new_leaf - allocate and write a new leaf to cover a gap in hash table
Packit Service 360c39
 * @dip: the directory inode
Packit Service 360c39
 * @start_lindex: where in the hash table to start writing
Packit Service 360c39
 * @num_copies: number of copies of the pointer to write into hash table
Packit Service 360c39
 * @before_or_after: desc. of whether this is being added before/after/etc.
Packit Service 360c39
 * @bn: pointer to return the newly allocated leaf's block number
Packit Service 360c39
 */
Packit Service 360c39
static int write_new_leaf(struct gfs2_inode *dip, int start_lindex,
Packit Service 360c39
			  int num_copies, const char *before_or_after,
Packit Service 360c39
			  uint64_t *bn)
Packit Service 360c39
{
Packit Service 360c39
	struct gfs2_buffer_head *nbh;
Packit Service 360c39
	struct gfs2_leaf *leaf;
Packit Service 360c39
	struct gfs2_dirent *dent;
Packit Service 360c39
	int count, i;
Packit Service 360c39
	int factor = 0, pad_size;
Packit Service 360c39
	uint64_t *cpyptr;
Packit Service 360c39
	char *padbuf;
Packit Service 360c39
	int divisor = num_copies;
Packit Service 360c39
	int end_lindex = start_lindex + num_copies;
Packit Service 360c39
Packit Service 360c39
	padbuf = malloc(num_copies * sizeof(uint64_t));
Packit Service 360c39
	/* calculate the depth needed for the new leaf */
Packit Service 360c39
	while (divisor > 1) {
Packit Service 360c39
		factor++;
Packit Service 360c39
		divisor /= 2;
Packit Service 360c39
	}
Packit Service 360c39
	/* Make sure the number of copies is properly a factor of 2 */
Packit Service 360c39
	if ((1 << factor) != num_copies) {
Packit Service 360c39
		log_err(_("Program error: num_copies not a factor of 2.\n"));
Packit Service 360c39
		log_err(_("num_copies=%d, dinode = %lld (0x%llx)\n"),
Packit Service 360c39
			num_copies,
Packit Service 360c39
			(unsigned long long)dip->i_di.di_num.no_addr,
Packit Service 360c39
			(unsigned long long)dip->i_di.di_num.no_addr);
Packit Service 360c39
		log_err(_("lindex = %d (0x%x)\n"), start_lindex, start_lindex);
Packit Service 360c39
		stack;
Packit Service 360c39
		free(padbuf);
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	/* allocate and write out a new leaf block */
Packit Service 360c39
	if (lgfs2_meta_alloc(dip, bn)) {
Packit Service 360c39
		log_err( _("Error: allocation failed while fixing directory leaf "
Packit Service 360c39
			   "pointers.\n"));
Packit Service 360c39
		free(padbuf);
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	fsck_bitmap_set(dip, *bn, _("directory leaf"), dip->i_sbd->gfs1 ?
Packit Service 360c39
			GFS2_BLKST_DINODE : GFS2_BLKST_USED);
Packit Service 360c39
	log_err(_("A new directory leaf was allocated at block %lld "
Packit Service 360c39
		  "(0x%llx) to fill the %d (0x%x) pointer gap %s the existing "
Packit Service 360c39
		  "pointer at index %d (0x%x).\n"), (unsigned long long)*bn,
Packit Service 360c39
		(unsigned long long)*bn, num_copies, num_copies,
Packit Service 360c39
		before_or_after, start_lindex, start_lindex);
Packit Service 360c39
	dip->i_di.di_blocks++;
Packit Service 360c39
	bmodified(dip->i_bh);
Packit Service 360c39
	nbh = bget(dip->i_sbd, *bn);
Packit Service 360c39
	memset(nbh->b_data, 0, dip->i_sbd->bsize);
Packit Service 360c39
	leaf = (struct gfs2_leaf *)nbh->b_data;
Packit Service 360c39
	leaf->lf_header.mh_magic = cpu_to_be32(GFS2_MAGIC);
Packit Service 360c39
	leaf->lf_header.mh_type = cpu_to_be32(GFS2_METATYPE_LF);
Packit Service 360c39
	leaf->lf_header.mh_format = cpu_to_be32(GFS2_FORMAT_LF);
Packit Service 360c39
	leaf->lf_depth = cpu_to_be16(dip->i_di.di_depth - factor);
Packit Service 360c39
Packit Service 360c39
	/* initialize the first dirent on the new leaf block */
Packit Service 360c39
	dent = (struct gfs2_dirent *)(nbh->b_data + sizeof(struct gfs2_leaf));
Packit Service 360c39
	dent->de_rec_len = cpu_to_be16(dip->i_sbd->bsize -
Packit Service 360c39
				       sizeof(struct gfs2_leaf));
Packit Service 360c39
	bmodified(nbh);
Packit Service 360c39
	brelse(nbh);
Packit Service 360c39
Packit Service 360c39
	/* pad the hash table with the new leaf block */
Packit Service 360c39
	cpyptr = (uint64_t *)padbuf;
Packit Service 360c39
	for (i = start_lindex; i < end_lindex; i++) {
Packit Service 360c39
		*cpyptr = cpu_to_be64(*bn);
Packit Service 360c39
		cpyptr++;
Packit Service 360c39
	}
Packit Service 360c39
	pad_size = num_copies * sizeof(uint64_t);
Packit Service 360c39
	log_err(_("Writing to the hash table of directory %lld "
Packit Service 360c39
		  "(0x%llx) at index: 0x%x for 0x%lx pointers.\n"),
Packit Service 360c39
		(unsigned long long)dip->i_di.di_num.no_addr,
Packit Service 360c39
		(unsigned long long)dip->i_di.di_num.no_addr,
Packit Service 360c39
		start_lindex, (unsigned long)pad_size / sizeof(uint64_t));
Packit Service 360c39
	if (dip->i_sbd->gfs1)
Packit Service 360c39
		count = gfs1_writei(dip, padbuf, start_lindex *
Packit Service 360c39
				    sizeof(uint64_t), pad_size);
Packit Service 360c39
	else
Packit Service 360c39
		count = gfs2_writei(dip, padbuf, start_lindex *
Packit Service 360c39
				    sizeof(uint64_t), pad_size);
Packit Service 360c39
	free(padbuf);
Packit Service 360c39
	if (count != pad_size) {
Packit Service 360c39
		log_err( _("Error: bad write while fixing directory leaf "
Packit Service 360c39
			   "pointers.\n"));
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	return 0;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/* pad_with_leafblks - pad a hash table with pointers to new leaf blocks
Packit Service 360c39
 *
Packit Service 360c39
 * @ip: pointer to the dinode structure
Packit Service 360c39
 * @tbl: pointer to the hash table in memory
Packit Service 360c39
 * @lindex: index location within the hash table to pad
Packit Service 360c39
 * @len: number of pointers to be padded
Packit Service 360c39
 */
Packit Service 360c39
static void pad_with_leafblks(struct gfs2_inode *ip, uint64_t *tbl,
Packit Service 360c39
			      int lindex, int len)
Packit Service 360c39
{
Packit Service 360c39
	int new_len, i;
Packit Service 360c39
	uint32_t proper_start = lindex;
Packit Service 360c39
	uint64_t new_leaf_blk;
Packit Service 360c39
Packit Service 360c39
	log_err(_("Padding inode %llu (0x%llx) hash table at offset %d (0x%x) "
Packit Service 360c39
		  "for %d pointers.\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, lindex, lindex,
Packit Service 360c39
		len);
Packit Service 360c39
	while (len) {
Packit Service 360c39
		new_len = 1;
Packit Service 360c39
		/* Determine the next factor of 2 down from extras. We can't
Packit Service 360c39
		   just write out a leaf block on a power-of-two boundary.
Packit Service 360c39
		   We also need to make sure it has a length that will
Packit Service 360c39
		   ensure a "proper start" block as well. */
Packit Service 360c39
		while ((new_len << 1) <= len) {
Packit Service 360c39
			/* Translation: If doubling the size of the new leaf
Packit Service 360c39
			   will make its start boundary wrong, we have to
Packit Service 360c39
			   settle for a smaller length (and iterate more). */
Packit Service 360c39
			proper_start = (lindex & ~((new_len << 1) - 1));
Packit Service 360c39
			if (lindex != proper_start)
Packit Service 360c39
				break;
Packit Service 360c39
			new_len <<= 1;
Packit Service 360c39
		}
Packit Service 360c39
		write_new_leaf(ip, lindex, new_len, "after", &new_leaf_blk);
Packit Service 360c39
		log_err(_("New leaf block was allocated at %llu (0x%llx) for "
Packit Service 360c39
			  "index %d (0x%x), length %d\n"),
Packit Service 360c39
			(unsigned long long)new_leaf_blk,
Packit Service 360c39
			(unsigned long long)new_leaf_blk,
Packit Service 360c39
			lindex, lindex, new_len);
Packit Service 360c39
		fsck_bitmap_set(ip, new_leaf_blk, _("pad leaf"),
Packit Service 360c39
				ip->i_sbd->gfs1 ?
Packit Service 360c39
				GFS2_BLKST_DINODE : GFS2_BLKST_USED);
Packit Service 360c39
		/* Fix the hash table in memory to have the new leaf */
Packit Service 360c39
		for (i = 0; i < new_len; i++)
Packit Service 360c39
			tbl[lindex + i] = cpu_to_be64(new_leaf_blk);
Packit Service 360c39
		len -= new_len;
Packit Service 360c39
		lindex += new_len;
Packit Service 360c39
	}
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/* lost_leaf - repair a leaf block that's on the wrong directory inode
Packit Service 360c39
 *
Packit Service 360c39
 * If the correct index is less than the starting index, we have a problem.
Packit Service 360c39
 * Since we process the index sequentially, the previous index has already
Packit Service 360c39
 * been processed, fixed, and is now correct. But this leaf wants to overwrite
Packit Service 360c39
 * a previously written good leaf. The only thing we can do is move all the
Packit Service 360c39
 * directory entries to lost+found so we don't overwrite the good leaf. Then
Packit Service 360c39
 * we need to pad the gap we leave.
Packit Service 360c39
 */
Packit Service 360c39
static int lost_leaf(struct gfs2_inode *ip, uint64_t *tbl, uint64_t leafno,
Packit Service 360c39
		     int ref_count, int lindex, struct gfs2_buffer_head *bh)
Packit Service 360c39
{
Packit Service 360c39
	char *filename;
Packit Service 360c39
	char *bh_end = bh->b_data + ip->i_sbd->bsize;
Packit Service 360c39
	struct gfs2_dirent de, *dent;
Packit Service 360c39
	int error;
Packit Service 360c39
	int isdir = 0;
Packit Service 360c39
Packit Service 360c39
	log_err(_("Leaf block %llu (0x%llx) seems to be out of place and its "
Packit Service 360c39
		  "contents need to be moved to lost+found.\n"),
Packit Service 360c39
		(unsigned long long)leafno, (unsigned long long)leafno);
Packit Service 360c39
	if (!query( _("Attempt to fix it? (y/n) "))) {
Packit Service 360c39
		log_err( _("Directory leaf was not fixed.\n"));
Packit Service 360c39
		return 0;
Packit Service 360c39
	}
Packit Service 360c39
	make_sure_lf_exists(ip);
Packit Service 360c39
Packit Service 360c39
	dent = (struct gfs2_dirent *)(bh->b_data + sizeof(struct gfs2_leaf));
Packit Service 360c39
	while (1) {
Packit Service 360c39
		char tmp_name[PATH_MAX];
Packit Service 360c39
Packit Service 360c39
		memset(&de, 0, sizeof(struct gfs2_dirent));
Packit Service 360c39
		gfs2_dirent_in(&de, (char *)dent);
Packit Service 360c39
		filename = (char *)dent + sizeof(struct gfs2_dirent);
Packit Service 360c39
		memset(tmp_name, 0, sizeof(tmp_name));
Packit Service 360c39
		if (de.de_name_len > sizeof(filename)) {
Packit Service 360c39
			log_debug(_("Encountered bad filename length; "
Packit Service 360c39
				    "stopped processing.\n"));
Packit Service 360c39
			break;
Packit Service 360c39
		}
Packit Service 360c39
		memcpy(tmp_name, filename, de.de_name_len);
Packit Service 360c39
		if ((de.de_name_len == 1 && filename[0] == '.')) {
Packit Service 360c39
			log_debug(_("Skipping entry '.'\n"));
Packit Service 360c39
		} else if (de.de_name_len == 2 && filename[0] == '.' &&
Packit Service 360c39
			   filename[1] == '.') {
Packit Service 360c39
			log_debug(_("Skipping entry '..'\n"));
Packit Service 360c39
		} else if (!de.de_inum.no_formal_ino) { /* sentinel */
Packit Service 360c39
			log_debug(_("Skipping sentinel '%s'\n"), tmp_name);
Packit Service 360c39
		} else {
Packit Service 360c39
			uint32_t count;
Packit Service 360c39
			struct dir_status ds = {0};
Packit Service 360c39
			int q = 0;
Packit Service 360c39
Packit Service 360c39
			error = basic_dentry_checks(ip, dent, &de.de_inum,
Packit Service 360c39
						    tmp_name, &count, &de,
Packit Service 360c39
						    &ds, &q, bh, &isdir);
Packit Service 360c39
			if (error) {
Packit Service 360c39
				log_err(_("Not relocating corrupt entry "
Packit Service 360c39
					  "\"%s\".\n"), tmp_name);
Packit Service 360c39
			} else {
Packit Service 360c39
				error = dir_add(lf_dip, filename,
Packit Service 360c39
						de.de_name_len, &de.de_inum,
Packit Service 360c39
						de.de_type);
Packit Service 360c39
				if (error && error != -EEXIST) {
Packit Service 360c39
					log_err(_("Error %d encountered while "
Packit Service 360c39
						  "trying to relocate \"%s\" "
Packit Service 360c39
						  "to lost+found.\n"), error,
Packit Service 360c39
						tmp_name);
Packit Service 360c39
					return error;
Packit Service 360c39
				}
Packit Service 360c39
				/* This inode is linked from lost+found */
Packit Service 360c39
				incr_link_count(de.de_inum, lf_dip,
Packit Service 360c39
						_("from lost+found"));
Packit Service 360c39
				/* If it's a directory, lost+found is
Packit Service 360c39
				   back-linked to it via .. */
Packit Service 360c39
				if (isdir)
Packit Service 360c39
					incr_link_count(lf_dip->i_di.di_num,
Packit Service 360c39
							NULL,
Packit Service 360c39
							_("to lost+found"));
Packit Service 360c39
				log_err(_("Relocated \"%s\", block %llu "
Packit Service 360c39
					  "(0x%llx) to lost+found.\n"),
Packit Service 360c39
					tmp_name,
Packit Service 360c39
					(unsigned long long)de.de_inum.no_addr,
Packit Service 360c39
					(unsigned long long)de.de_inum.no_addr);
Packit Service 360c39
			}
Packit Service 360c39
		}
Packit Service 360c39
		if ((char *)dent + de.de_rec_len >= bh_end)
Packit Service 360c39
			break;
Packit Service 360c39
		dent = (struct gfs2_dirent *)((char *)dent + de.de_rec_len);
Packit Service 360c39
	}
Packit Service 360c39
	log_err(_("Directory entries from misplaced leaf block were relocated "
Packit Service 360c39
		  "to lost+found.\n"));
Packit Service 360c39
	/* Free the lost leaf. */
Packit Service 360c39
	fsck_bitmap_set(ip, leafno, _("lost leaf"), GFS2_BLKST_FREE);
Packit Service 360c39
	ip->i_di.di_blocks--;
Packit Service 360c39
	bmodified(ip->i_bh);
Packit Service 360c39
	/* Now we have to deal with the bad hash table entries pointing to the
Packit Service 360c39
	   misplaced leaf block. But we can't just fill the gap with a single
Packit Service 360c39
	   leaf. We have to write on nice power-of-two boundaries, and we have
Packit Service 360c39
	   to pad out any extra pointers. */
Packit Service 360c39
	pad_with_leafblks(ip, tbl, lindex, ref_count);
Packit Service 360c39
	return 1;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
static int basic_check_dentry(struct gfs2_inode *ip, struct gfs2_dirent *dent,
Packit Service 360c39
			      struct gfs2_dirent *prev_de,
Packit Service 360c39
			      struct gfs2_buffer_head *bh, char *filename,
Packit Service 360c39
			      uint32_t *count, int *lindex, void *priv)
Packit Service 360c39
{
Packit Service 360c39
	int q = 0;
Packit Service 360c39
	char tmp_name[MAX_FILENAME];
Packit Service 360c39
	struct gfs2_inum entry;
Packit Service 360c39
	struct dir_status *ds = (struct dir_status *) priv;
Packit Service 360c39
	struct gfs2_dirent dentry, *de;
Packit Service 360c39
	int error;
Packit Service 360c39
	int isdir;
Packit Service 360c39
Packit Service 360c39
	memset(&dentry, 0, sizeof(struct gfs2_dirent));
Packit Service 360c39
	gfs2_dirent_in(&dentry, (char *)dent);
Packit Service 360c39
	de = &dentry;
Packit Service 360c39
Packit Service 360c39
	entry.no_addr = de->de_inum.no_addr;
Packit Service 360c39
	entry.no_formal_ino = de->de_inum.no_formal_ino;
Packit Service 360c39
Packit Service 360c39
	/* Start of checks */
Packit Service 360c39
	memset(tmp_name, 0, MAX_FILENAME);
Packit Service 360c39
	if (de->de_name_len < MAX_FILENAME)
Packit Service 360c39
		strncpy(tmp_name, filename, de->de_name_len);
Packit Service 360c39
	else
Packit Service 360c39
		strncpy(tmp_name, filename, MAX_FILENAME - 1);
Packit Service 360c39
Packit Service 360c39
	error = basic_dentry_checks(ip, dent, &entry, tmp_name, count, de,
Packit Service 360c39
				    ds, &q, bh, &isdir);
Packit Service 360c39
	if (error) {
Packit Service 360c39
		dirent2_del(ip, bh, prev_de, dent);
Packit Service 360c39
		log_err( _("Bad directory entry '%s' cleared.\n"), tmp_name);
Packit Service 360c39
		return 1;
Packit Service 360c39
	} else {
Packit Service 360c39
		(*count)++;
Packit Service 360c39
		return 0;
Packit Service 360c39
	}
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/* pass2_repair_leaf - Warn the user of an error and ask permission to fix it
Packit Service 360c39
 * Process a bad leaf pointer and ask to repair the first time.
Packit Service 360c39
 * The repair process involves extending the previous leaf's entries
Packit Service 360c39
 * so that they replace the bad ones.  We have to hack up the old
Packit Service 360c39
 * leaf a bit, but it's better than deleting the whole directory,
Packit Service 360c39
 * which is what used to happen before. */
Packit Service 360c39
static int pass2_repair_leaf(struct gfs2_inode *ip, uint64_t *leaf_no,
Packit Service 360c39
			     int lindex, int ref_count, const char *msg)
Packit Service 360c39
{
Packit Service 360c39
	int new_leaf_blks = 0, error, refs;
Packit Service 360c39
	uint64_t bn = 0;
Packit Service 360c39
Packit Service 360c39
	log_err( _("Directory Inode %llu (0x%llx) points to leaf %llu"
Packit Service 360c39
		   " (0x%llx) %s.\n"),
Packit Service 360c39
		 (unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
		 (unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
		 (unsigned long long)*leaf_no,
Packit Service 360c39
		 (unsigned long long)*leaf_no, msg);
Packit Service 360c39
	if (!query( _("Attempt to patch around it? (y/n) "))) {
Packit Service 360c39
		log_err( _("Bad leaf left in place.\n"));
Packit Service 360c39
		goto out;
Packit Service 360c39
	}
Packit Service 360c39
	/* We can only write leafs in quantities that are factors of
Packit Service 360c39
	   two, since leaves are doubled, not added sequentially.
Packit Service 360c39
	   So if we have a hole that's not a factor of 2, we have to
Packit Service 360c39
	   break it down into separate leaf blocks that are. */
Packit Service 360c39
	while (ref_count) {
Packit Service 360c39
		refs = 1;
Packit Service 360c39
		while (refs <= ref_count) {
Packit Service 360c39
			if (refs * 2 > ref_count)
Packit Service 360c39
				break;
Packit Service 360c39
			refs *= 2;
Packit Service 360c39
		}
Packit Service 360c39
		error = write_new_leaf(ip, lindex, refs, _("replacing"), &bn);
Packit Service 360c39
		if (error)
Packit Service 360c39
			return error;
Packit Service 360c39
Packit Service 360c39
		new_leaf_blks++;
Packit Service 360c39
		lindex += refs;
Packit Service 360c39
		ref_count -= refs;
Packit Service 360c39
	}
Packit Service 360c39
	log_err( _("Directory Inode %llu (0x%llx) repaired.\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
out:
Packit Service 360c39
	*leaf_no = bn;
Packit Service 360c39
	return new_leaf_blks;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/* The purpose of leafck_fxns is to provide a means for function fix_hashtable
Packit Service 360c39
 * to do basic sanity checks on leaf blocks before manipulating them, for
Packit Service 360c39
 * example, splitting them. If they're corrupt, splitting them or trying to
Packit Service 360c39
 * move their contents can cause a segfault. We can't really use the standard
Packit Service 360c39
 * pass2_fxns because that will do things we don't want. For example, it will
Packit Service 360c39
 * find '.' and '..' and increment the directory link count, which would be
Packit Service 360c39
 * done a second time when the dirent is really checked in pass2_fxns.
Packit Service 360c39
 * We don't want it to do the "wrong leaf" thing, or set_parent_dir either.
Packit Service 360c39
 * We just want a basic sanity check on pointers and lengths.
Packit Service 360c39
 */
Packit Service 360c39
struct metawalk_fxns leafck_fxns = {
Packit Service 360c39
	.check_leaf_depth = check_leaf_depth,
Packit Service 360c39
	.check_dentry = basic_check_dentry,
Packit Service 360c39
	.repair_leaf = pass2_repair_leaf,
Packit Service 360c39
};
Packit Service 360c39
Packit Service 360c39
/* fix_hashtable - fix a corrupt hash table
Packit Service 360c39
 *
Packit Service 360c39
 * The main intent of this function is to sort out hash table problems.
Packit Service 360c39
 * That is, it needs to determine if leaf blocks are in the wrong place,
Packit Service 360c39
 * if the count of pointers is wrong, and if there are extra pointers.
Packit Service 360c39
 * Everything should be placed on correct power-of-two boundaries appropriate
Packit Service 360c39
 * to their leaf depth, and extra pointers should be correctly padded with new
Packit Service 360c39
 * leaf blocks.
Packit Service 360c39
 *
Packit Service 360c39
 * @ip: the directory dinode structure pointer
Packit Service 360c39
 * @tbl: hash table that's already read into memory
Packit Service 360c39
 * @hsize: hash table size, as dictated by the dinode's di_depth
Packit Service 360c39
 * @leafblk: the leaf block number that appears at this lindex in the tbl
Packit Service 360c39
 * @lindex: leaf index that has a problem
Packit Service 360c39
 * @proper_start: where this leaf's pointers should start, as far as the
Packit Service 360c39
 *                hash table is concerned (sight unseen; trusting the leaf
Packit Service 360c39
 *                really belongs here).
Packit Service 360c39
 * @len: count of pointers in the hash table to this leafblk
Packit Service 360c39
 * @proper_len: pointer to return the proper number of pointers, as the kernel
Packit Service 360c39
 *              calculates it, based on the leaf depth.
Packit Service 360c39
 * @factor: the proper depth, given this number of pointers (rounded down).
Packit Service 360c39
 *
Packit Service 360c39
 * Returns: 0 - no changes made, or X if changes were made
Packit Service 360c39
 */
Packit Service 360c39
static int fix_hashtable(struct gfs2_inode *ip, uint64_t *tbl, unsigned hsize,
Packit Service 360c39
			 uint64_t leafblk, int lindex, uint32_t proper_start,
Packit Service 360c39
			 int len, int *proper_len, int factor)
Packit Service 360c39
{
Packit Service 360c39
	struct gfs2_buffer_head *lbh;
Packit Service 360c39
	struct gfs2_leaf leaf;
Packit Service 360c39
	struct gfs2_dirent dentry, *de;
Packit Service 360c39
	int changes = 0, error, i, extras, hash_index;
Packit Service 360c39
	uint64_t new_leaf_blk;
Packit Service 360c39
	uint64_t leaf_no;
Packit Service 360c39
	uint32_t leaf_proper_start;
Packit Service 360c39
Packit Service 360c39
	*proper_len = len;
Packit Service 360c39
	log_err(_("Dinode %llu (0x%llx) has a hash table error at index "
Packit Service 360c39
		  "0x%x, length 0x%x: leaf block %llu (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, lindex, len,
Packit Service 360c39
		(unsigned long long)leafblk, (unsigned long long)leafblk);
Packit Service 360c39
	if (!query( _("Fix the hash table? (y/n) "))) {
Packit Service 360c39
		log_err(_("Hash table not fixed.\n"));
Packit Service 360c39
		return 0;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	memset(&leaf, 0, sizeof(leaf));
Packit Service 360c39
	leaf_no = leafblk;
Packit Service 360c39
	error = check_leaf(ip, lindex, &leafck_fxns, &leaf_no, &leaf, &len;;
Packit Service 360c39
	if (error) {
Packit Service 360c39
		log_debug("Leaf repaired while fixing the hash table.\n");
Packit Service 360c39
		error = 0;
Packit Service 360c39
	}
Packit Service 360c39
	lbh = bread(ip->i_sbd, leafblk);
Packit Service 360c39
	/* If the leaf's depth is out of range for this dinode, it's obviously
Packit Service 360c39
	   attached to the wrong dinode. Move the dirents to lost+found. */
Packit Service 360c39
	if (leaf.lf_depth > ip->i_di.di_depth) {
Packit Service 360c39
		log_err(_("This leaf block's depth (%d) is too big for this "
Packit Service 360c39
			  "dinode's depth (%d)\n"),
Packit Service 360c39
			leaf.lf_depth, ip->i_di.di_depth);
Packit Service 360c39
		error = lost_leaf(ip, tbl, leafblk, len, lindex, lbh);
Packit Service 360c39
		brelse(lbh);
Packit Service 360c39
		return error;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	memset(&dentry, 0, sizeof(struct gfs2_dirent));
Packit Service 360c39
	de = (struct gfs2_dirent *)(lbh->b_data + sizeof(struct gfs2_leaf));
Packit Service 360c39
	gfs2_dirent_in(&dentry, (char *)de);
Packit Service 360c39
Packit Service 360c39
	/* If this is an empty leaf, we can just delete it and pad. */
Packit Service 360c39
	if ((dentry.de_rec_len == cpu_to_be16(ip->i_sbd->bsize -
Packit Service 360c39
					      sizeof(struct gfs2_leaf))) &&
Packit Service 360c39
	    (dentry.de_inum.no_formal_ino == 0)) {
Packit Service 360c39
		brelse(lbh);
Packit Service 360c39
		gfs2_free_block(ip->i_sbd, leafblk);
Packit Service 360c39
		log_err(_("Out of place leaf block %llu (0x%llx) had no "
Packit Service 360c39
			"entries, so it was deleted.\n"),
Packit Service 360c39
			(unsigned long long)leafblk,
Packit Service 360c39
			(unsigned long long)leafblk);
Packit Service 360c39
		pad_with_leafblks(ip, tbl, lindex, len);
Packit Service 360c39
		log_err(_("Reprocessing index 0x%x (case 1).\n"), lindex);
Packit Service 360c39
		return 1;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	/* Calculate the proper number of pointers based on the leaf depth. */
Packit Service 360c39
	*proper_len = 1 << (ip->i_di.di_depth - leaf.lf_depth);
Packit Service 360c39
Packit Service 360c39
	/* Look at the first dirent and check its hash value to see if it's
Packit Service 360c39
	   at the proper starting offset. */
Packit Service 360c39
	hash_index = hash_table_index(dentry.de_hash, ip);
Packit Service 360c39
	/* Need to use len here, not *proper_len because the leaf block may
Packit Service 360c39
	   be valid within the range, but starts too soon in the hash table. */
Packit Service 360c39
	if (hash_index < lindex ||  hash_index > lindex + len) {
Packit Service 360c39
		log_err(_("This leaf block has hash index %d, which is out of "
Packit Service 360c39
			  "bounds for where it appears in the hash table "
Packit Service 360c39
			  "(%d - %d)\n"),
Packit Service 360c39
			hash_index, lindex, lindex + *proper_len);
Packit Service 360c39
		error = lost_leaf(ip, tbl, leafblk, len, lindex, lbh);
Packit Service 360c39
		brelse(lbh);
Packit Service 360c39
		return error;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	/* Now figure out where this leaf should start, and pad any pointers
Packit Service 360c39
	   up to that point with new leaf blocks. */
Packit Service 360c39
	leaf_proper_start = (hash_index & ~(*proper_len - 1));
Packit Service 360c39
	if (lindex < leaf_proper_start) {
Packit Service 360c39
		log_err(_("Leaf pointers start at %d (0x%x), should be %d "
Packit Service 360c39
			  "(%x).\n"), lindex, lindex,
Packit Service 360c39
			leaf_proper_start, leaf_proper_start);
Packit Service 360c39
		pad_with_leafblks(ip, tbl, lindex, leaf_proper_start - lindex);
Packit Service 360c39
		brelse(lbh);
Packit Service 360c39
		return 1; /* reprocess the starting lindex */
Packit Service 360c39
	}
Packit Service 360c39
	/* If the proper start according to the leaf's hash index is later
Packit Service 360c39
	   than the proper start according to the hash table, it's once
Packit Service 360c39
	   again lost and we have to relocate it. The same applies if the
Packit Service 360c39
	   leaf's hash index is prior to the proper state, but the leaf is
Packit Service 360c39
	   already at its maximum depth. */
Packit Service 360c39
	if ((leaf_proper_start < proper_start) ||
Packit Service 360c39
	    ((*proper_len > len || lindex > leaf_proper_start) &&
Packit Service 360c39
	     leaf.lf_depth == ip->i_di.di_depth)) {
Packit Service 360c39
		log_err(_("Leaf block should start at 0x%x, but it appears at "
Packit Service 360c39
			  "0x%x in the hash table.\n"), leaf_proper_start,
Packit Service 360c39
			proper_start);
Packit Service 360c39
		error = lost_leaf(ip, tbl, leafblk, len, lindex, lbh);
Packit Service 360c39
		brelse(lbh);
Packit Service 360c39
		return error;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	/* If we SHOULD have more pointers than we do, we can solve the
Packit Service 360c39
	   problem by splitting the block to a lower depth. Then we may have
Packit Service 360c39
	   the right number of pointers. If the leaf block pointers start
Packit Service 360c39
	   later than they should, we can split the leaf to give it a smaller
Packit Service 360c39
	   footprint in the hash table. */
Packit Service 360c39
	if ((*proper_len > len || lindex > leaf_proper_start) &&
Packit Service 360c39
	    ip->i_di.di_depth > leaf.lf_depth) {
Packit Service 360c39
		log_err(_("For depth %d, length %d, the proper start is: "
Packit Service 360c39
			  "0x%x.\n"), factor, len, proper_start);
Packit Service 360c39
		changes++;
Packit Service 360c39
		new_leaf_blk = find_free_blk(ip->i_sbd);
Packit Service 360c39
		dir_split_leaf(ip, lindex, leafblk, lbh);
Packit Service 360c39
		/* re-read the leaf to pick up dir_split_leaf's changes */
Packit Service 360c39
		gfs2_leaf_in(&leaf, lbh->b_data);
Packit Service 360c39
		*proper_len = 1 << (ip->i_di.di_depth - leaf.lf_depth);
Packit Service 360c39
		log_err(_("Leaf block %llu (0x%llx) was split from length "
Packit Service 360c39
			  "%d to %d\n"), (unsigned long long)leafblk,
Packit Service 360c39
			(unsigned long long)leafblk, len, *proper_len);
Packit Service 360c39
		if (*proper_len < 0) {
Packit Service 360c39
			log_err(_("Programming error: proper_len=%d, "
Packit Service 360c39
				  "di_depth = %d, lf_depth = %d.\n"),
Packit Service 360c39
				*proper_len, ip->i_di.di_depth, leaf.lf_depth);
Packit Service 360c39
			exit(FSCK_ERROR);
Packit Service 360c39
		}
Packit Service 360c39
		log_err(_("New split-off leaf block was allocated at %lld "
Packit Service 360c39
			  "(0x%llx) for index %d (0x%x)\n"),
Packit Service 360c39
			(unsigned long long)new_leaf_blk,
Packit Service 360c39
			(unsigned long long)new_leaf_blk, lindex, lindex);
Packit Service 360c39
		fsck_bitmap_set(ip, new_leaf_blk, _("split leaf"),
Packit Service 360c39
				ip->i_sbd->gfs1 ?
Packit Service 360c39
				GFS2_BLKST_DINODE : GFS2_BLKST_USED);
Packit Service 360c39
		log_err(_("Hash table repaired.\n"));
Packit Service 360c39
		/* Fix up the hash table in memory to include the new leaf */
Packit Service 360c39
		for (i = 0; i < *proper_len; i++)
Packit Service 360c39
			tbl[lindex + i] = cpu_to_be64(new_leaf_blk);
Packit Service 360c39
		if (*proper_len < (len >> 1)) {
Packit Service 360c39
			log_err(_("One leaf split is not enough. The hash "
Packit Service 360c39
				  "table will need to be reprocessed.\n"));
Packit Service 360c39
			brelse(lbh);
Packit Service 360c39
			return changes;
Packit Service 360c39
		}
Packit Service 360c39
		lindex += (*proper_len); /* skip the new leaf from the split */
Packit Service 360c39
		len -= (*proper_len);
Packit Service 360c39
	}
Packit Service 360c39
	if (*proper_len < len) {
Packit Service 360c39
		log_err(_("There are %d pointers, but leaf 0x%llx's "
Packit Service 360c39
			  "depth, %d, only allows %d\n"),
Packit Service 360c39
			len, (unsigned long long)leafblk, leaf.lf_depth,
Packit Service 360c39
			*proper_len);
Packit Service 360c39
	}
Packit Service 360c39
	brelse(lbh);
Packit Service 360c39
	/* At this point, lindex should be at the proper end of the pointers.
Packit Service 360c39
	   Now we need to replace any extra duplicate pointers to the old
Packit Service 360c39
	   (original) leafblk (that ran off the end) with new leaf blocks. */
Packit Service 360c39
	lindex += (*proper_len); /* Skip past the normal good pointers */
Packit Service 360c39
	len -= (*proper_len);
Packit Service 360c39
	extras = 0;
Packit Service 360c39
	for (i = 0; i < len; i++) {
Packit Service 360c39
		if (be64_to_cpu(tbl[lindex + i]) == leafblk)
Packit Service 360c39
			extras++;
Packit Service 360c39
		else
Packit Service 360c39
			break;
Packit Service 360c39
	}
Packit Service 360c39
	if (extras) {
Packit Service 360c39
		log_err(_("Found %d extra pointers to leaf %llu (0x%llx)\n"),
Packit Service 360c39
			extras, (unsigned long long)leafblk,
Packit Service 360c39
			(unsigned long long)leafblk);
Packit Service 360c39
		pad_with_leafblks(ip, tbl, lindex, extras);
Packit Service 360c39
		log_err(_("Reprocessing index 0x%x (case 2).\n"), lindex);
Packit Service 360c39
		return 1;
Packit Service 360c39
	}
Packit Service 360c39
	return changes;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/* check_hash_tbl_dups - check for the same leaf in multiple places */
Packit Service 360c39
static int check_hash_tbl_dups(struct gfs2_inode *ip, uint64_t *tbl,
Packit Service 360c39
			       unsigned hsize, int lindex, int len)
Packit Service 360c39
{
Packit Service 360c39
	int l, len2;
Packit Service 360c39
	uint64_t leafblk, leaf_no;
Packit Service 360c39
	struct gfs2_buffer_head *lbh;
Packit Service 360c39
	struct gfs2_leaf leaf;
Packit Service 360c39
	struct gfs2_dirent dentry, *de;
Packit Service 360c39
	int hash_index; /* index into the hash table based on the hash */
Packit Service 360c39
Packit Service 360c39
	leafblk = be64_to_cpu(tbl[lindex]);
Packit Service 360c39
	for (l = 0; l < hsize; l++) {
Packit Service 360c39
		if (l == lindex) { /* skip the valid reference */
Packit Service 360c39
			l += len - 1;
Packit Service 360c39
			continue;
Packit Service 360c39
		}
Packit Service 360c39
		if (be64_to_cpu(tbl[l]) != leafblk)
Packit Service 360c39
			continue;
Packit Service 360c39
Packit Service 360c39
		for (len2 = 0; l + len2 < hsize; len2++) {
Packit Service 360c39
			if (l + len2 == lindex)
Packit Service 360c39
				break;
Packit Service 360c39
			if (be64_to_cpu(tbl[l + len2]) != leafblk)
Packit Service 360c39
				break;
Packit Service 360c39
		}
Packit Service 360c39
		log_err(_("Dinode %llu (0x%llx) has duplicate leaf pointers "
Packit Service 360c39
			  "to block %llu (0x%llx) at offsets %u (0x%x) "
Packit Service 360c39
			  "(for 0x%x) and %u (0x%x) (for 0x%x)\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)leafblk,
Packit Service 360c39
			(unsigned long long)leafblk, lindex, lindex, len,
Packit Service 360c39
			l, l, len2);
Packit Service 360c39
Packit Service 360c39
		/* See which set of references is valid: the one passed in
Packit Service 360c39
		   or the duplicate we found. */
Packit Service 360c39
		memset(&leaf, 0, sizeof(leaf));
Packit Service 360c39
		leaf_no = leafblk;
Packit Service 360c39
		if (!valid_block_ip(ip, leaf_no)) /* Checked later */
Packit Service 360c39
			continue;
Packit Service 360c39
Packit Service 360c39
		lbh = bread(ip->i_sbd, leafblk);
Packit Service 360c39
		if (gfs2_check_meta(lbh, GFS2_METATYPE_LF)) { /* Chked later */
Packit Service 360c39
			brelse(lbh);
Packit Service 360c39
			continue;
Packit Service 360c39
		}
Packit Service 360c39
Packit Service 360c39
		memset(&dentry, 0, sizeof(struct gfs2_dirent));
Packit Service 360c39
		de = (struct gfs2_dirent *)(lbh->b_data +
Packit Service 360c39
					    sizeof(struct gfs2_leaf));
Packit Service 360c39
		gfs2_dirent_in(&dentry, (char *)de);
Packit Service 360c39
		hash_index = hash_table_index(dentry.de_hash, ip);
Packit Service 360c39
		brelse(lbh);
Packit Service 360c39
		/* check the duplicate ref first */
Packit Service 360c39
		if (hash_index < l ||  hash_index > l + len2) {
Packit Service 360c39
			log_err(_("This leaf block has hash index %d, which "
Packit Service 360c39
				  "is out of bounds for lindex (%d - %d)\n"),
Packit Service 360c39
				hash_index, l, l + len2);
Packit Service 360c39
			if (!query( _("Fix the hash table? (y/n) "))) {
Packit Service 360c39
				log_err(_("Hash table not fixed.\n"));
Packit Service 360c39
				return 0;
Packit Service 360c39
			}
Packit Service 360c39
			/* Adjust the ondisk block count. The original value
Packit Service 360c39
			   may have been correct without the duplicates but
Packit Service 360c39
			   pass1 would have counted them and adjusted the
Packit Service 360c39
			   count to include them. So we must subtract them. */
Packit Service 360c39
			ip->i_di.di_blocks--;
Packit Service 360c39
			bmodified(ip->i_bh);
Packit Service 360c39
			pad_with_leafblks(ip, tbl, l, len2);
Packit Service 360c39
		} else {
Packit Service 360c39
			log_debug(_("Hash index 0x%x is the proper "
Packit Service 360c39
				    "reference to leaf 0x%llx.\n"),
Packit Service 360c39
				  l, (unsigned long long)leafblk);
Packit Service 360c39
		}
Packit Service 360c39
		/* Check the original ref: both references might be bad.
Packit Service 360c39
		   If both were bad, just return and if we encounter it
Packit Service 360c39
		   again, we'll treat it as new. If the original ref is not
Packit Service 360c39
		   bad, keep looking for (and fixing) other instances. */
Packit Service 360c39
		if (hash_index < lindex ||  hash_index > lindex + len) {
Packit Service 360c39
			log_err(_("This leaf block has hash index %d, which "
Packit Service 360c39
				  "is out of bounds for lindex (%d - %d).\n"),
Packit Service 360c39
				hash_index, lindex, lindex + len);
Packit Service 360c39
			if (!query( _("Fix the hash table? (y/n) "))) {
Packit Service 360c39
				log_err(_("Hash table not fixed.\n"));
Packit Service 360c39
				return 0;
Packit Service 360c39
			}
Packit Service 360c39
			ip->i_di.di_blocks--;
Packit Service 360c39
			bmodified(ip->i_bh);
Packit Service 360c39
			pad_with_leafblks(ip, tbl, lindex, len);
Packit Service 360c39
			/* At this point we know both copies are bad, so we
Packit Service 360c39
			   return to start fresh */
Packit Service 360c39
			return -EFAULT;
Packit Service 360c39
		} else {
Packit Service 360c39
			log_debug(_("Hash index 0x%x is the proper "
Packit Service 360c39
				    "reference to leaf 0x%llx.\n"),
Packit Service 360c39
				  lindex, (unsigned long long)leafblk);
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
	return 0;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/* check_hash_tbl - check that the hash table is sane
Packit Service 360c39
 *
Packit Service 360c39
 * We've got to make sure the hash table is sane. Each leaf needs to
Packit Service 360c39
 * be counted a proper power of 2. We can't just have 3 pointers to a leaf.
Packit Service 360c39
 * The number of pointers must correspond to the proper leaf depth, and they
Packit Service 360c39
 * must all fall on power-of-two boundaries. The leaf block pointers all need
Packit Service 360c39
 * to fall properly on these boundaries, otherwise the kernel code's
Packit Service 360c39
 * calculations will land it on the wrong leaf block while it's searching,
Packit Service 360c39
 * and the result will be files you can see with ls, but can't open, delete
Packit Service 360c39
 * or use them.
Packit Service 360c39
 *
Packit Service 360c39
 * The goal of this function is to check the hash table to make sure the
Packit Service 360c39
 * boundaries and lengths all line up properly, and if not, to fix it.
Packit Service 360c39
 *
Packit Service 360c39
 * Note: There's a delicate balance here, because this function gets called
Packit Service 360c39
 *       BEFORE leaf blocks are checked by function check_leaf from function
Packit Service 360c39
 *       check_leaf_blks: the hash table has to be sane before we can start
Packit Service 360c39
 *       checking all the leaf blocks. And yet if there's hash table corruption
Packit Service 360c39
 *       we may need to reference leaf blocks to fix it, which means we need
Packit Service 360c39
 *       to check and/or fix a leaf block along the way.
Packit Service 360c39
 */
Packit Service 360c39
static int check_hash_tbl(struct gfs2_inode *ip, uint64_t *tbl,
Packit Service 360c39
			  unsigned hsize, void *private)
Packit Service 360c39
{
Packit Service 360c39
	int error = 0;
Packit Service 360c39
	int lindex, len, proper_len, i, changes = 0;
Packit Service 360c39
	uint64_t leafblk;
Packit Service 360c39
	struct gfs2_leaf leaf;
Packit Service 360c39
	struct gfs2_buffer_head *lbh;
Packit Service 360c39
	int factor;
Packit Service 360c39
	uint32_t proper_start;
Packit Service 360c39
	int anomaly;
Packit Service 360c39
Packit Service 360c39
	lindex = 0;
Packit Service 360c39
	while (lindex < hsize) {
Packit Service 360c39
		if (fsck_abort)
Packit Service 360c39
			return changes;
Packit Service 360c39
		len = 1;
Packit Service 360c39
		factor = 0;
Packit Service 360c39
		leafblk = be64_to_cpu(tbl[lindex]);
Packit Service 360c39
		anomaly = 0;
Packit Service 360c39
		while (lindex + (len << 1) - 1 < hsize) {
Packit Service 360c39
			uint32_t next_proper_start;
Packit Service 360c39
			if (be64_to_cpu(tbl[lindex + (len << 1) - 1]) !=
Packit Service 360c39
			    leafblk)
Packit Service 360c39
				break;
Packit Service 360c39
			next_proper_start = (lindex & ~((len << 1) - 1));
Packit Service 360c39
			if (lindex != next_proper_start)
Packit Service 360c39
				anomaly = 1;
Packit Service 360c39
			/* Check if there are other values written between
Packit Service 360c39
			   here and the next factor. */
Packit Service 360c39
			for (i = len; !anomaly && i + lindex < hsize &&
Packit Service 360c39
				     i < (len << 1); i++)
Packit Service 360c39
				if (be64_to_cpu(tbl[lindex + i]) != leafblk)
Packit Service 360c39
					anomaly = 1;
Packit Service 360c39
			if (anomaly)
Packit Service 360c39
				break;
Packit Service 360c39
			len <<= 1;
Packit Service 360c39
			factor++;
Packit Service 360c39
		}
Packit Service 360c39
Packit Service 360c39
		/* Check for leftover pointers after the factor of two: */
Packit Service 360c39
		proper_len = len; /* A factor of 2 that fits nicely */
Packit Service 360c39
		while (lindex + len < hsize &&
Packit Service 360c39
		       be64_to_cpu(tbl[lindex + len]) == leafblk)
Packit Service 360c39
			len++;
Packit Service 360c39
Packit Service 360c39
		/* See if that leaf block is valid. If not, write a new one
Packit Service 360c39
		   that falls on a proper boundary. If it doesn't naturally,
Packit Service 360c39
		   we may need more. */
Packit Service 360c39
		if (!valid_block_ip(ip, leafblk)) {
Packit Service 360c39
			uint64_t new_leafblk;
Packit Service 360c39
Packit Service 360c39
			log_err(_("Dinode %llu (0x%llx) has bad leaf pointers "
Packit Service 360c39
				  "at offset %d for %d\n"),
Packit Service 360c39
				(unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
				(unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
				lindex, len);
Packit Service 360c39
			if (!query( _("Fix the hash table? (y/n) "))) {
Packit Service 360c39
				log_err(_("Hash table not fixed.\n"));
Packit Service 360c39
				lindex += len;
Packit Service 360c39
				continue;
Packit Service 360c39
			}
Packit Service 360c39
			error = write_new_leaf(ip, lindex, proper_len,
Packit Service 360c39
					       _("replacing"), &new_leafblk);
Packit Service 360c39
			if (error)
Packit Service 360c39
				return error;
Packit Service 360c39
Packit Service 360c39
			for (i = lindex; i < lindex + proper_len; i++)
Packit Service 360c39
				tbl[i] = cpu_to_be64(new_leafblk);
Packit Service 360c39
			lindex += proper_len;
Packit Service 360c39
			continue;
Packit Service 360c39
		}
Packit Service 360c39
Packit Service 360c39
		if (check_hash_tbl_dups(ip, tbl, hsize, lindex, len))
Packit Service 360c39
			continue;
Packit Service 360c39
Packit Service 360c39
		/* Make sure they call on proper leaf-split boundaries. This
Packit Service 360c39
		   is the calculation used by the kernel, and dir_split_leaf */
Packit Service 360c39
		proper_start = (lindex & ~(proper_len - 1));
Packit Service 360c39
		if (lindex != proper_start) {
Packit Service 360c39
			log_debug(_("lindex 0x%llx is not a proper starting "
Packit Service 360c39
				    "point for leaf %llu (0x%llx): 0x%llx\n"),
Packit Service 360c39
				  (unsigned long long)lindex,
Packit Service 360c39
				  (unsigned long long)leafblk,
Packit Service 360c39
				  (unsigned long long)leafblk,
Packit Service 360c39
				  (unsigned long long)proper_start);
Packit Service 360c39
			changes = fix_hashtable(ip, tbl, hsize, leafblk,
Packit Service 360c39
						lindex, proper_start, len,
Packit Service 360c39
						&proper_len, factor);
Packit Service 360c39
			/* Check if we need to split more leaf blocks */
Packit Service 360c39
			if (changes) {
Packit Service 360c39
				if (proper_len < (len >> 1))
Packit Service 360c39
					log_err(_("More leaf splits are "
Packit Service 360c39
						  "needed; "));
Packit Service 360c39
				log_err(_("Reprocessing index 0x%x (case 3).\n"),
Packit Service 360c39
					lindex);
Packit Service 360c39
				continue; /* Make it reprocess the lindex */
Packit Service 360c39
			}
Packit Service 360c39
		}
Packit Service 360c39
		/* Check for extra pointers to this leaf. At this point, len
Packit Service 360c39
		   is the number of pointers we have. proper_len is the proper
Packit Service 360c39
		   number of pointers if the hash table is assumed correct.
Packit Service 360c39
		   Function fix_hashtable will read in the leaf block and
Packit Service 360c39
		   determine the "actual" proper length based on the leaf
Packit Service 360c39
		   depth, and adjust the hash table accordingly. */
Packit Service 360c39
		if (len != proper_len) {
Packit Service 360c39
			log_err(_("Length %d (0x%x) is not a proper length "
Packit Service 360c39
				  "for leaf %llu (0x%llx). Valid boundary "
Packit Service 360c39
				  "assumed to be %d (0x%x).\n"), len, len,
Packit Service 360c39
				(unsigned long long)leafblk,
Packit Service 360c39
				(unsigned long long)leafblk,
Packit Service 360c39
				proper_len, proper_len);
Packit Service 360c39
			lbh = bread(ip->i_sbd, leafblk);
Packit Service 360c39
			gfs2_leaf_in(&leaf, lbh->b_data);
Packit Service 360c39
			if (gfs2_check_meta(lbh, GFS2_METATYPE_LF) ||
Packit Service 360c39
			    leaf.lf_depth > ip->i_di.di_depth)
Packit Service 360c39
				leaf.lf_depth = factor;
Packit Service 360c39
			brelse(lbh);
Packit Service 360c39
			changes = fix_hashtable(ip, tbl, hsize, leafblk,
Packit Service 360c39
						lindex, lindex, len,
Packit Service 360c39
						&proper_len, leaf.lf_depth);
Packit Service 360c39
			/* If fixing the hash table made changes, we can no
Packit Service 360c39
			   longer count on the leaf block pointers all pointing
Packit Service 360c39
			   to the same leaf (which is checked below). To avoid
Packit Service 360c39
			   flagging another error, reprocess the offset. */
Packit Service 360c39
			if (changes) {
Packit Service 360c39
				log_err(_("Reprocessing index 0x%x (case 4).\n"),
Packit Service 360c39
					lindex);
Packit Service 360c39
				continue; /* Make it reprocess the lindex */
Packit Service 360c39
			}
Packit Service 360c39
		}
Packit Service 360c39
Packit Service 360c39
		/* Now make sure they're all the same pointer */
Packit Service 360c39
		for (i = lindex; i < lindex + proper_len; i++) {
Packit Service 360c39
			if (fsck_abort)
Packit Service 360c39
				return changes;
Packit Service 360c39
Packit Service 360c39
			if (be64_to_cpu(tbl[i]) == leafblk) /* No problem */
Packit Service 360c39
				continue;
Packit Service 360c39
Packit Service 360c39
			log_err(_("Dinode %llu (0x%llx) has a hash table "
Packit Service 360c39
				  "inconsistency at index %d (0x%x) for %d\n"),
Packit Service 360c39
				(unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
				(unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
				i, i, len);
Packit Service 360c39
			if (!query( _("Fix the hash table? (y/n) "))) {
Packit Service 360c39
				log_err(_("Hash table not fixed.\n"));
Packit Service 360c39
				continue;
Packit Service 360c39
			}
Packit Service 360c39
			changes++;
Packit Service 360c39
			/* Now we have to determine if the hash table is
Packit Service 360c39
			   corrupt, or if the leaf has the wrong depth. */
Packit Service 360c39
			lbh = bread(ip->i_sbd, leafblk);
Packit Service 360c39
			gfs2_leaf_in(&leaf, lbh->b_data);
Packit Service 360c39
			brelse(lbh);
Packit Service 360c39
			/* Calculate the expected pointer count based on the
Packit Service 360c39
			   leaf depth. */
Packit Service 360c39
			proper_len = 1 << (ip->i_di.di_depth - leaf.lf_depth);
Packit Service 360c39
			if (proper_len != len) {
Packit Service 360c39
				log_debug(_("Length 0x%x is not proper for "
Packit Service 360c39
					    "leaf %llu (0x%llx): 0x%x\n"),
Packit Service 360c39
					  len, (unsigned long long)leafblk,
Packit Service 360c39
					  (unsigned long long)leafblk,
Packit Service 360c39
					  proper_len);
Packit Service 360c39
				changes = fix_hashtable(ip, tbl, hsize,
Packit Service 360c39
							leafblk, lindex,
Packit Service 360c39
							lindex, len,
Packit Service 360c39
							&proper_len,
Packit Service 360c39
							leaf.lf_depth);
Packit Service 360c39
				break;
Packit Service 360c39
			}
Packit Service 360c39
		}
Packit Service 360c39
		lindex += proper_len;
Packit Service 360c39
	}
Packit Service 360c39
	if (!error && changes)
Packit Service 360c39
		error = 1;
Packit Service 360c39
	return error;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
struct metawalk_fxns pass2_fxns = {
Packit Service 360c39
	.private = NULL,
Packit Service 360c39
	.check_leaf_depth = check_leaf_depth,
Packit Service 360c39
	.check_leaf = NULL,
Packit Service 360c39
	.check_metalist = NULL,
Packit Service 360c39
	.check_data = NULL,
Packit Service 360c39
	.check_eattr_indir = check_eattr_indir,
Packit Service 360c39
	.check_eattr_leaf = check_eattr_leaf,
Packit Service 360c39
	.check_dentry = check_dentry,
Packit Service 360c39
	.check_eattr_entry = NULL,
Packit Service 360c39
	.check_hash_tbl = check_hash_tbl,
Packit Service 360c39
	.repair_leaf = pass2_repair_leaf,
Packit Service 360c39
};
Packit Service 360c39
Packit Service 360c39
static int check_metalist_qc(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 meta_is_good;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
static int check_data_qc(struct gfs2_inode *ip, uint64_t metablock,
Packit Service 360c39
			 uint64_t block, void *private,
Packit Service 360c39
			 struct gfs2_buffer_head *bbh, uint64_t *ptr)
Packit Service 360c39
{
Packit Service 360c39
	struct gfs2_buffer_head *bh;
Packit Service 360c39
Packit Service 360c39
	/* At this point, basic data block checks have already been done,
Packit Service 360c39
	   so we only need to make sure they're QC blocks. */
Packit Service 360c39
	if (!valid_block_ip(ip, block))
Packit Service 360c39
		return -1;
Packit Service 360c39
Packit Service 360c39
	bh = bread(ip->i_sbd, block);
Packit Service 360c39
	if (gfs2_check_meta(bh, GFS2_METATYPE_QC) != 0) {
Packit Service 360c39
		log_crit(_("Error: quota_change block at %lld (0x%llx) is "
Packit Service 360c39
			   "the wrong metadata type.\n"),
Packit Service 360c39
			 (unsigned long long)block, (unsigned long long)block);
Packit Service 360c39
		brelse(bh);
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	brelse(bh);
Packit Service 360c39
	return 0;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
struct metawalk_fxns quota_change_fxns = {
Packit Service 360c39
	.check_metalist = check_metalist_qc,
Packit Service 360c39
	.check_data = check_data_qc,
Packit Service 360c39
};
Packit Service 360c39
Packit Service 360c39
/* check_pernode_for - verify a file within the system per_node directory
Packit Service 360c39
 * @x - index number X
Packit Service 360c39
 * @per_node - pointer to the per_node inode
Packit Service 360c39
 * @fn - system file name
Packit Service 360c39
 * @filelen - the file length the system file needs to be
Packit Service 360c39
 * @multiple - the file length must be a multiple (versus the exact value)
Packit Service 360c39
 * @pass - a metawalk function for checking the data blocks (if any)
Packit Service 360c39
 * @builder - a rebuild function for the file
Packit Service 360c39
 *
Packit Service 360c39
 * Returns: 0 if all went well, else error. */
Packit Service 360c39
static int check_pernode_for(int x, struct gfs2_inode *pernode, const char *fn,
Packit Service 360c39
			     unsigned long long filelen, int multiple,
Packit Service 360c39
			     struct metawalk_fxns *pass,
Packit Service 360c39
			     int builder(struct gfs2_inode *per_node,
Packit Service 360c39
					 unsigned int j))
Packit Service 360c39
{
Packit Service 360c39
	struct gfs2_inode *ip;
Packit Service 360c39
	int error, valid_size = 1;
Packit Service 360c39
Packit Service 360c39
	log_debug(_("Checking system file %s\n"), fn);
Packit Service 360c39
	error = gfs2_lookupi(pernode, fn, strlen(fn), &ip);
Packit Service 360c39
	if (error) {
Packit Service 360c39
		log_err(_("System file %s is missing.\n"), fn);
Packit Service 360c39
		if (!query( _("Rebuild the system file? (y/n) ")))
Packit Service 360c39
			return 0;
Packit Service 360c39
		goto build_it;
Packit Service 360c39
	}
Packit Service 360c39
	if (!ip->i_di.di_size)
Packit Service 360c39
		valid_size = 0;
Packit Service 360c39
	else if (!multiple && ip->i_di.di_size != filelen)
Packit Service 360c39
		valid_size = 0;
Packit Service 360c39
	else if (multiple && (ip->i_di.di_size % filelen))
Packit Service 360c39
		valid_size = 0;
Packit Service 360c39
	if (!valid_size) {
Packit Service 360c39
		log_err(_("System file %s has an invalid size. Is %llu, "
Packit Service 360c39
			  "should be %llu.\n"), fn, ip->i_di.di_size, filelen);
Packit Service 360c39
		if (!query( _("Rebuild the system file? (y/n) ")))
Packit Service 360c39
			goto out_good;
Packit Service 360c39
		fsck_inode_put(&ip);
Packit Service 360c39
		goto build_it;
Packit Service 360c39
	}
Packit Service 360c39
	if (pass) {
Packit Service 360c39
		error = check_metatree(ip, pass);
Packit Service 360c39
		if (!error)
Packit Service 360c39
			goto out_good;
Packit Service 360c39
		log_err(_("System file %s has bad contents.\n"), fn);
Packit Service 360c39
		if (!query( _("Delete and rebuild the system file? (y/n) ")))
Packit Service 360c39
			goto out_good;
Packit Service 360c39
		check_metatree(ip, &pass2_fxns_delete);
Packit Service 360c39
		fsck_inode_put(&ip);
Packit Service 360c39
		gfs2_dirent_del(pernode, fn, strlen(fn));
Packit Service 360c39
		goto build_it;
Packit Service 360c39
	}
Packit Service 360c39
out_good:
Packit Service 360c39
	fsck_inode_put(&ip);
Packit Service 360c39
	return 0;
Packit Service 360c39
Packit Service 360c39
build_it:
Packit Service 360c39
	if (builder(pernode, x)) {
Packit Service 360c39
		log_err(_("Error building %s\n"), fn);
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	error = gfs2_lookupi(pernode, fn, strlen(fn), &ip);
Packit Service 360c39
	if (error) {
Packit Service 360c39
		log_err(_("Error rebuilding %s.\n"), fn);
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	fsck_bitmap_set(ip, ip->i_di.di_num.no_addr, fn, GFS2_BLKST_DINODE);
Packit Service 360c39
	log_err(_("System file %s rebuilt.\n"), fn);
Packit Service 360c39
	goto out_good;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/* Check system directory inode                                           */
Packit Service 360c39
/* Should work for all system directories: root, master, jindex, per_node */
Packit Service 360c39
static int check_system_dir(struct gfs2_inode *sysinode, const char *dirname,
Packit Service 360c39
		     int builder(struct gfs2_sbd *sdp))
Packit Service 360c39
{
Packit Service 360c39
	uint64_t iblock = 0;
Packit Service 360c39
	struct dir_status ds = {0};
Packit Service 360c39
	int error = 0;
Packit Service 360c39
Packit Service 360c39
	log_info( _("Checking system directory inode '%s'\n"), dirname);
Packit Service 360c39
Packit Service 360c39
	if (!sysinode) {
Packit Service 360c39
		log_err( _("Failed to check '%s': sysinode is null\n"), dirname);
Packit Service 360c39
		stack;
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	iblock = sysinode->i_di.di_num.no_addr;
Packit Service 360c39
	ds.q = bitmap_type(sysinode->i_sbd, iblock);
Packit Service 360c39
Packit Service 360c39
	pass2_fxns.private = (void *) &ds;
Packit Service 360c39
	if (ds.q == GFS2_BLKST_FREE) {
Packit Service 360c39
		/* First check that the directory's metatree is valid */
Packit Service 360c39
		error = check_metatree(sysinode, &pass2_fxns);
Packit Service 360c39
		if (error < 0) {
Packit Service 360c39
			stack;
Packit Service 360c39
			return error;
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
	error = check_dir(sysinode->i_sbd, sysinode, &pass2_fxns);
Packit Service 360c39
	if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
Packit Service 360c39
		return FSCK_OK;
Packit Service 360c39
	if (error < 0) {
Packit Service 360c39
		stack;
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	if (error > 0)
Packit Service 360c39
		fsck_bitmap_set(sysinode, iblock, dirname, GFS2_BLKST_FREE);
Packit Service 360c39
Packit Service 360c39
	if (check_inode_eattr(sysinode, &pass2_fxns)) {
Packit Service 360c39
		stack;
Packit Service 360c39
		return -1;
Packit Service 360c39
	}
Packit Service 360c39
	if (!ds.dotdir) {
Packit Service 360c39
		log_err( _("No '.' entry found for %s directory.\n"), dirname);
Packit Service 360c39
		if (query( _("Is it okay to add '.' entry? (y/n) "))) {
Packit Service 360c39
			log_warn( _("Adding '.' entry\n"));
Packit Service 360c39
			error = dir_add(sysinode, ".", 1, &(sysinode->i_di.di_num),
Packit Service 360c39
			                (sysinode->i_sbd->gfs1 ? GFS_FILE_DIR : DT_DIR));
Packit Service 360c39
			if (error) {
Packit Service 360c39
				log_err(_("Error adding directory %s: %s\n"), "'.'",
Packit Service 360c39
				         strerror(errno));
Packit Service 360c39
				return -errno;
Packit Service 360c39
			}
Packit Service 360c39
			/* This system inode is linked to itself via '.' */
Packit Service 360c39
			incr_link_count(sysinode->i_di.di_num, sysinode,
Packit Service 360c39
					"sysinode \".\"");
Packit Service 360c39
			ds.entry_count++;
Packit Service 360c39
		} else
Packit Service 360c39
			log_err( _("The directory was not fixed.\n"));
Packit Service 360c39
	}
Packit Service 360c39
	if (sysinode->i_di.di_entries != ds.entry_count) {
Packit Service 360c39
		log_err( _("%s inode %llu (0x%llx"
Packit Service 360c39
			"): Entries is %d - should be %d\n"), dirname,
Packit Service 360c39
			(unsigned long long)sysinode->i_di.di_num.no_addr,
Packit Service 360c39
			(unsigned long long)sysinode->i_di.di_num.no_addr,
Packit Service 360c39
			sysinode->i_di.di_entries, ds.entry_count);
Packit Service 360c39
		if (query( _("Fix entries for %s inode %llu (0x%llx)? (y/n) "),
Packit Service 360c39
			  dirname,
Packit Service 360c39
			  (unsigned long long)sysinode->i_di.di_num.no_addr,
Packit Service 360c39
			  (unsigned long long)sysinode->i_di.di_num.no_addr)) {
Packit Service 360c39
			sysinode->i_di.di_entries = ds.entry_count;
Packit Service 360c39
			bmodified(sysinode->i_bh);
Packit Service 360c39
			log_warn( _("Entries updated\n"));
Packit Service 360c39
		} else {
Packit Service 360c39
			log_err( _("Entries for inode %llu (0x%llx"
Packit Service 360c39
				") left out of sync\n"),
Packit Service 360c39
				(unsigned long long)
Packit Service 360c39
				sysinode->i_di.di_num.no_addr,
Packit Service 360c39
				(unsigned long long)
Packit Service 360c39
				sysinode->i_di.di_num.no_addr);
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
	error = 0;
Packit Service 360c39
	if (sysinode == sysinode->i_sbd->md.pinode) {
Packit Service 360c39
		int j;
Packit Service 360c39
		char fn[64];
Packit Service 360c39
Packit Service 360c39
		/* Make sure all the per_node files are there, and valid */
Packit Service 360c39
		for (j = 0; j < sysinode->i_sbd->md.journals; j++) {
Packit Service 360c39
			sprintf(fn, "inum_range%d", j);
Packit Service 360c39
			error += check_pernode_for(j, sysinode, fn, 16, 0,
Packit Service 360c39
						   NULL, build_inum_range);
Packit Service 360c39
			sprintf(fn, "statfs_change%d", j);
Packit Service 360c39
			error += check_pernode_for(j, sysinode, fn, 24, 0,
Packit Service 360c39
						   NULL, build_statfs_change);
Packit Service 360c39
			sprintf(fn, "quota_change%d", j);
Packit Service 360c39
			error += check_pernode_for(j, sysinode, fn, 1048576, 1,
Packit Service 360c39
						   &quota_change_fxns,
Packit Service 360c39
						   build_quota_change);
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
	return error;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/**
Packit Service 360c39
 * is_system_dir - determine if a given block is for a system directory.
Packit Service 360c39
 */
Packit Service 360c39
static inline int is_system_dir(struct gfs2_sbd *sdp, uint64_t block)
Packit Service 360c39
{
Packit Service 360c39
	if (block == sdp->md.rooti->i_di.di_num.no_addr)
Packit Service 360c39
		return TRUE;
Packit Service 360c39
	if (sdp->gfs1)
Packit Service 360c39
		return FALSE;
Packit Service 360c39
	if (block == sdp->md.jiinode->i_di.di_num.no_addr ||
Packit Service 360c39
	    block == sdp->md.pinode->i_di.di_num.no_addr ||
Packit Service 360c39
	    block == sdp->master_dir->i_di.di_num.no_addr)
Packit Service 360c39
		return TRUE;
Packit Service 360c39
	return FALSE;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
static int pass2_check_dir(struct gfs2_sbd *sdp, struct gfs2_inode *ip)
Packit Service 360c39
{
Packit Service 360c39
	uint64_t dirblk = ip->i_di.di_num.no_addr;
Packit Service 360c39
	struct dir_status ds = {0};
Packit Service 360c39
	int error;
Packit Service 360c39
Packit Service 360c39
	pass2_fxns.private = &ds;
Packit Service 360c39
	error = check_dir(sdp, ip, &pass2_fxns);
Packit Service 360c39
	if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
Packit Service 360c39
		return FSCK_OK;
Packit Service 360c39
	if (error < 0) {
Packit Service 360c39
		stack;
Packit Service 360c39
		return FSCK_ERROR;
Packit Service 360c39
	}
Packit Service 360c39
	if (error > 0) {
Packit Service 360c39
		struct dir_info *di;
Packit Service 360c39
Packit Service 360c39
		di = dirtree_find(dirblk);
Packit Service 360c39
		if (!di) {
Packit Service 360c39
			stack;
Packit Service 360c39
			return FSCK_ERROR;
Packit Service 360c39
		}
Packit Service 360c39
		if (query(_("Remove directory entry for bad inode "
Packit Service 360c39
		            "%llu (0x%llx) in %llu (0x%llx)? (y/n)"),
Packit Service 360c39
			  (unsigned long long)dirblk,
Packit Service 360c39
			  (unsigned long long)dirblk,
Packit Service 360c39
			  (unsigned long long)di->treewalk_parent,
Packit Service 360c39
			  (unsigned long long)di->treewalk_parent)) {
Packit Service 360c39
			error = remove_dentry_from_dir(sdp, di->treewalk_parent, dirblk);
Packit Service 360c39
			if (error < 0) {
Packit Service 360c39
				stack;
Packit Service 360c39
				return FSCK_ERROR;
Packit Service 360c39
			}
Packit Service 360c39
			if (error > 0) {
Packit Service 360c39
				log_warn(_("Unable to find dentry for %llu (0x%llx) "
Packit Service 360c39
				           "in %llu (0x%llx)\n"),
Packit Service 360c39
					  (unsigned long long)dirblk,
Packit Service 360c39
					  (unsigned long long)dirblk,
Packit Service 360c39
					  (unsigned long long)di->treewalk_parent,
Packit Service 360c39
					  (unsigned long long)di->treewalk_parent);
Packit Service 360c39
			}
Packit Service 360c39
			log_warn(_("Directory entry removed\n"));
Packit Service 360c39
		} else
Packit Service 360c39
			log_err(_("Directory entry to invalid inode remains.\n"));
Packit Service 360c39
Packit Service 360c39
		log_debug(_("Directory block %lld (0x%llx) is now marked as 'invalid'\n"),
Packit Service 360c39
			   (unsigned long long)dirblk, (unsigned long long)dirblk);
Packit Service 360c39
		check_n_fix_bitmap(sdp, ip->i_rgd, dirblk, 0, GFS2_BLKST_FREE);
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	if (!ds.dotdir) {
Packit Service 360c39
		log_err(_("No '.' entry found for directory inode at block %llu (0x%llx)\n"),
Packit Service 360c39
			(unsigned long long)dirblk, (unsigned long long)dirblk);
Packit Service 360c39
Packit Service 360c39
		if (query( _("Is it okay to add '.' entry? (y/n) "))) {
Packit Service 360c39
			error = dir_add(ip, ".", 1, &(ip->i_di.di_num),
Packit Service 360c39
					(sdp->gfs1 ? GFS_FILE_DIR : DT_DIR));
Packit Service 360c39
			if (error) {
Packit Service 360c39
				log_err(_("Error adding directory %s: %s\n"), "'.'",
Packit Service 360c39
					strerror(errno));
Packit Service 360c39
				return -errno;
Packit Service 360c39
			}
Packit Service 360c39
			/* directory links to itself via '.' */
Packit Service 360c39
			incr_link_count(ip->i_di.di_num, ip, _("\". (itself)\""));
Packit Service 360c39
			ds.entry_count++;
Packit Service 360c39
			log_err(_("The directory was fixed.\n"));
Packit Service 360c39
		} else {
Packit Service 360c39
			log_err(_("The directory was not fixed.\n"));
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	if (!fsck_abort && ip->i_di.di_entries != ds.entry_count) {
Packit Service 360c39
		log_err(_("Entries is %d - should be %d for inode block %llu (0x%llx)\n"),
Packit Service 360c39
			ip->i_di.di_entries, ds.entry_count,
Packit Service 360c39
			(unsigned long long)ip->i_di.di_num.no_addr,
Packit Service 360c39
			(unsigned long long)ip->i_di.di_num.no_addr);
Packit Service 360c39
		if (query(_("Fix the entry count? (y/n) "))) {
Packit Service 360c39
			ip->i_di.di_entries = ds.entry_count;
Packit Service 360c39
			bmodified(ip->i_bh);
Packit Service 360c39
		} else {
Packit Service 360c39
			log_err(_("The entry count was not fixed.\n"));
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
	return FSCK_OK;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/* What i need to do in this pass is check that the dentries aren't
Packit Service 360c39
 * pointing to invalid blocks...and verify the contents of each
Packit Service 360c39
 * directory. and start filling in the directory info structure*/
Packit Service 360c39
Packit Service 360c39
/**
Packit Service 360c39
 * pass2 - check pathnames
Packit Service 360c39
 *
Packit Service 360c39
 * verify root inode
Packit Service 360c39
 * directory name length
Packit Service 360c39
 * entries in range
Packit Service 360c39
 */
Packit Service 360c39
int pass2(struct gfs2_sbd *sdp)
Packit Service 360c39
{
Packit Service 360c39
	struct osi_node *tmp, *next = NULL;
Packit Service 360c39
	struct gfs2_inode *ip;
Packit Service 360c39
	struct dir_info *dt;
Packit Service 360c39
	uint64_t dirblk;
Packit Service 360c39
	int error;
Packit Service 360c39
Packit Service 360c39
	/* Check all the system directory inodes. */
Packit Service 360c39
	if (!sdp->gfs1 &&
Packit Service 360c39
	    check_system_dir(sdp->md.jiinode, "jindex", build_jindex)) {
Packit Service 360c39
		stack;
Packit Service 360c39
		return FSCK_ERROR;
Packit Service 360c39
	}
Packit Service 360c39
	if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
Packit Service 360c39
		return FSCK_OK;
Packit Service 360c39
	if (!sdp->gfs1 &&
Packit Service 360c39
	    check_system_dir(sdp->md.pinode, "per_node", build_per_node)) {
Packit Service 360c39
		stack;
Packit Service 360c39
		return FSCK_ERROR;
Packit Service 360c39
	}
Packit Service 360c39
	if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
Packit Service 360c39
		return FSCK_OK;
Packit Service 360c39
	if (!sdp->gfs1 &&
Packit Service 360c39
	    check_system_dir(sdp->master_dir, "master", build_master)) {
Packit Service 360c39
		stack;
Packit Service 360c39
		return FSCK_ERROR;
Packit Service 360c39
	}
Packit Service 360c39
	if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
Packit Service 360c39
		return FSCK_OK;
Packit Service 360c39
	if (check_system_dir(sdp->md.rooti, "root", build_root)) {
Packit Service 360c39
		stack;
Packit Service 360c39
		return FSCK_ERROR;
Packit Service 360c39
	}
Packit Service 360c39
	if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
Packit Service 360c39
		return FSCK_OK;
Packit Service 360c39
	log_info( _("Checking directory inodes.\n"));
Packit Service 360c39
	/* Grab each directory inode, and run checks on it */
Packit Service 360c39
	for (tmp = osi_first(&dirtree); tmp; tmp = next) {
Packit Service 360c39
		next = osi_next(tmp);
Packit Service 360c39
Packit Service 360c39
		dt = (struct dir_info *)tmp;
Packit Service 360c39
		dirblk = dt->dinode.no_addr;
Packit Service 360c39
		warm_fuzzy_stuff(dirblk);
Packit Service 360c39
		if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
Packit Service 360c39
			return FSCK_OK;
Packit Service 360c39
Packit Service 360c39
		/* Skip the system inodes - they're checked above */
Packit Service 360c39
		if (is_system_dir(sdp, dirblk))
Packit Service 360c39
			continue;
Packit Service 360c39
Packit Service 360c39
		/* If we created lost+found, its links should have been
Packit Service 360c39
		   properly adjusted, so don't check it. */
Packit Service 360c39
		if (lf_was_created &&
Packit Service 360c39
		    (dirblk == lf_dip->i_di.di_num.no_addr)) {
Packit Service 360c39
			log_debug(_("Pass2 skipping the new lost+found.\n"));
Packit Service 360c39
			continue;
Packit Service 360c39
		}
Packit Service 360c39
Packit Service 360c39
		log_debug(_("Checking directory inode at block %llu (0x%llx)\n"),
Packit Service 360c39
			  (unsigned long long)dirblk, (unsigned long long)dirblk);
Packit Service 360c39
Packit Service 360c39
		ip = fsck_load_inode(sdp, dirblk);
Packit Service 360c39
		if (ip == NULL) {
Packit Service 360c39
			stack;
Packit Service 360c39
			return FSCK_ERROR;
Packit Service 360c39
		}
Packit Service 360c39
		error = pass2_check_dir(sdp, ip);
Packit Service 360c39
		fsck_inode_put(&ip);
Packit Service 360c39
Packit Service 360c39
		if (skip_this_pass || fsck_abort)
Packit Service 360c39
			return FSCK_OK;
Packit Service 360c39
Packit Service 360c39
		if (error != FSCK_OK) {
Packit Service 360c39
			stack;
Packit Service 360c39
			return error;
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
	return FSCK_OK;
Packit Service 360c39
}