Blame gfs2/fsck/pass3.c

Packit Service 360c39
#include "clusterautoconfig.h"
Packit Service 360c39
Packit Service 360c39
#include <stdio.h>
Packit Service 360c39
#include <stdlib.h>
Packit Service 360c39
#include <inttypes.h>
Packit Service 360c39
#include <string.h>
Packit Service 360c39
#include <dirent.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 "osi_list.h"
Packit Service 360c39
#include "fsck.h"
Packit Service 360c39
#include "lost_n_found.h"
Packit Service 360c39
#include "link.h"
Packit Service 360c39
#include "metawalk.h"
Packit Service 360c39
#include "util.h"
Packit Service 360c39
#include "afterpass1_common.h"
Packit Service 360c39
Packit Service 360c39
static int attach_dotdot_to(struct gfs2_sbd *sdp, uint64_t newdotdot,
Packit Service 360c39
			    uint64_t olddotdot, uint64_t block)
Packit Service 360c39
{
Packit Service 360c39
	const char *filename = "..";
Packit Service 360c39
	int filename_len = 2;
Packit Service 360c39
	int err;
Packit Service 360c39
	struct gfs2_inode *ip, *pip;
Packit Service 360c39
Packit Service 360c39
	ip = fsck_load_inode(sdp, block);
Packit Service 360c39
	pip = fsck_load_inode(sdp, newdotdot);
Packit Service 360c39
	/* FIXME: Need to add some interactive
Packit Service 360c39
	 * options here and come up with a
Packit Service 360c39
	 * good default for non-interactive */
Packit Service 360c39
	/* FIXME: do i need to correct the
Packit Service 360c39
	 * '..' entry for this directory in
Packit Service 360c39
	 * this case? */
Packit Service 360c39
Packit Service 360c39
	if (gfs2_dirent_del(ip, filename, filename_len))
Packit Service 360c39
		log_warn( _("Unable to remove \"..\" directory entry.\n"));
Packit Service 360c39
	else
Packit Service 360c39
		decr_link_count(olddotdot, block, sdp->gfs1, _("old \"..\""));
Packit Service 360c39
	err = dir_add(ip, filename, filename_len, &pip->i_di.di_num,
Packit Service 360c39
		      (sdp->gfs1 ? GFS_FILE_DIR : DT_DIR));
Packit Service 360c39
	if (err) {
Packit Service 360c39
		log_err(_("Error adding directory %s: %s\n"),
Packit Service 360c39
		        filename, strerror(errno));
Packit Service 360c39
		exit(FSCK_ERROR);
Packit Service 360c39
	}
Packit Service 360c39
	incr_link_count(pip->i_di.di_num, ip, _("new \"..\""));
Packit Service 360c39
	fsck_inode_put(&ip);
Packit Service 360c39
	fsck_inode_put(&pip;;
Packit Service 360c39
	return 0;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
static struct dir_info *mark_and_return_parent(struct gfs2_sbd *sdp,
Packit Service 360c39
					       struct dir_info *di)
Packit Service 360c39
{
Packit Service 360c39
	struct dir_info *pdi;
Packit Service 360c39
	int q_dotdot, q_treewalk;
Packit Service 360c39
	int error = 0;
Packit Service 360c39
	struct dir_info *dt_dotdot, *dt_treewalk;
Packit Service 360c39
Packit Service 360c39
	di->checked = 1;
Packit Service 360c39
Packit Service 360c39
	if (!di->treewalk_parent)
Packit Service 360c39
		return NULL;
Packit Service 360c39
Packit Service 360c39
	if (di->dotdot_parent.no_addr == di->treewalk_parent) {
Packit Service 360c39
		q_dotdot = bitmap_type(sdp, di->dotdot_parent.no_addr);
Packit Service 360c39
		if (q_dotdot != GFS2_BLKST_DINODE) {
Packit Service 360c39
			log_err( _("Orphaned directory at block %llu (0x%llx) "
Packit Service 360c39
				   "moved to lost+found\n"),
Packit Service 360c39
				 (unsigned long long)di->dinode.no_addr,
Packit Service 360c39
				 (unsigned long long)di->dinode.no_addr);
Packit Service 360c39
			return NULL;
Packit Service 360c39
		}
Packit Service 360c39
		goto out;
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	log_warn( _("Directory '..' and treewalk connections disagree for "
Packit Service 360c39
		    "inode %llu (0x%llx)\n"),
Packit Service 360c39
		  (unsigned long long)di->dinode.no_addr,
Packit Service 360c39
		  (unsigned long long)di->dinode.no_addr);
Packit Service 360c39
	log_notice( _("'..' has %llu (0x%llx), treewalk has %llu (0x%llx)\n"),
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
		    (unsigned long long)di->treewalk_parent,
Packit Service 360c39
		    (unsigned long long)di->treewalk_parent);
Packit Service 360c39
	q_dotdot = bitmap_type(sdp, di->dotdot_parent.no_addr);
Packit Service 360c39
	dt_dotdot = dirtree_find(di->dotdot_parent.no_addr);
Packit Service 360c39
	q_treewalk = bitmap_type(sdp, di->treewalk_parent);
Packit Service 360c39
	dt_treewalk = dirtree_find(di->treewalk_parent);
Packit Service 360c39
	/* if the dotdot entry isn't a directory, but the
Packit Service 360c39
	 * treewalk is, treewalk is correct - if the treewalk
Packit Service 360c39
	 * entry isn't a directory, but the dotdot is, dotdot
Packit Service 360c39
	 * is correct - if both are directories, which do we
Packit Service 360c39
	 * choose? if neither are directories, we have a
Packit Service 360c39
	 * problem - need to move this directory into lost+found
Packit Service 360c39
	 */
Packit Service 360c39
	if (q_dotdot != GFS2_BLKST_DINODE || dt_dotdot == NULL) {
Packit Service 360c39
		if (q_treewalk != GFS2_BLKST_DINODE) {
Packit Service 360c39
			log_err( _("Orphaned directory, move to "
Packit Service 360c39
				   "lost+found\n"));
Packit Service 360c39
			return NULL;
Packit Service 360c39
		} else {
Packit Service 360c39
			log_warn( _("Treewalk parent is correct, fixing "
Packit Service 360c39
				    "dotdot -> %llu (0x%llx)\n"),
Packit Service 360c39
				  (unsigned long long)di->treewalk_parent,
Packit Service 360c39
				  (unsigned long long)di->treewalk_parent);
Packit Service 360c39
			attach_dotdot_to(sdp, di->treewalk_parent,
Packit Service 360c39
					 di->dotdot_parent.no_addr,
Packit Service 360c39
					 di->dinode.no_addr);
Packit Service 360c39
			di->dotdot_parent.no_addr = di->treewalk_parent;
Packit Service 360c39
		}
Packit Service 360c39
		goto out;
Packit Service 360c39
	}
Packit Service 360c39
	if (dt_treewalk) {
Packit Service 360c39
		log_err( _("Both .. and treewalk parents are directories, "
Packit Service 360c39
			   "going with treewalk...\n"));
Packit Service 360c39
		attach_dotdot_to(sdp, di->treewalk_parent,
Packit Service 360c39
				 di->dotdot_parent.no_addr,
Packit Service 360c39
				 di->dinode.no_addr);
Packit Service 360c39
		di->dotdot_parent.no_addr = di->treewalk_parent;
Packit Service 360c39
		goto out;
Packit Service 360c39
	}
Packit Service 360c39
	log_warn( _(".. parent is valid, but treewalk is bad - reattaching to "
Packit Service 360c39
		    "lost+found"));
Packit Service 360c39
Packit Service 360c39
	/* FIXME: add a dinode for this entry instead? */
Packit Service 360c39
Packit Service 360c39
	if (!query( _("Remove directory entry for bad inode %llu (0x%llx) in "
Packit Service 360c39
		      "%llu (0x%llx)? (y/n)"),
Packit Service 360c39
		    (unsigned long long)di->dinode.no_addr,
Packit Service 360c39
		    (unsigned long long)di->dinode.no_addr,
Packit Service 360c39
		    (unsigned long long)di->treewalk_parent,
Packit Service 360c39
		    (unsigned long long)di->treewalk_parent)) {
Packit Service 360c39
		log_err( _("Directory entry to invalid inode remains\n"));
Packit Service 360c39
		return NULL;
Packit Service 360c39
	}
Packit Service 360c39
	error = remove_dentry_from_dir(sdp, di->treewalk_parent,
Packit Service 360c39
				       di->dinode.no_addr);
Packit Service 360c39
	if (error < 0) {
Packit Service 360c39
		stack;
Packit Service 360c39
		return NULL;
Packit Service 360c39
	}
Packit Service 360c39
	if (error > 0)
Packit Service 360c39
		log_warn( _("Unable to find dentry for block %llu"
Packit Service 360c39
			    " (0x%llx) in %llu (0x%llx)\n"),
Packit Service 360c39
			  (unsigned long long)di->dinode.no_addr,
Packit Service 360c39
			  (unsigned long long)di->dinode.no_addr,
Packit Service 360c39
			  (unsigned long long)di->treewalk_parent,
Packit Service 360c39
			  (unsigned long long)di->treewalk_parent);
Packit Service 360c39
	log_warn( _("Directory entry removed\n"));
Packit Service 360c39
	log_info( _("Marking directory unlinked\n"));
Packit Service 360c39
Packit Service 360c39
	return NULL;
Packit Service 360c39
Packit Service 360c39
out:
Packit Service 360c39
	pdi = dirtree_find(di->dotdot_parent.no_addr);
Packit Service 360c39
Packit Service 360c39
	return pdi;
Packit Service 360c39
}
Packit Service 360c39
Packit Service 360c39
/**
Packit Service 360c39
 * pass3 - check connectivity of directories
Packit Service 360c39
 *
Packit Service 360c39
 * handle disconnected directories
Packit Service 360c39
 * handle lost+found directory errors (missing, not a directory, no space)
Packit Service 360c39
 */
Packit Service 360c39
int pass3(struct gfs2_sbd *sdp)
Packit Service 360c39
{
Packit Service 360c39
	struct osi_node *tmp, *next = NULL;
Packit Service 360c39
	struct dir_info *di, *tdi;
Packit Service 360c39
	struct gfs2_inode *ip;
Packit Service 360c39
	int q;
Packit Service 360c39
Packit Service 360c39
	di = dirtree_find(sdp->md.rooti->i_di.di_num.no_addr);
Packit Service 360c39
	if (di) {
Packit Service 360c39
		log_info( _("Marking root inode connected\n"));
Packit Service 360c39
		di->checked = 1;
Packit Service 360c39
	}
Packit Service 360c39
	if (sdp->gfs1) {
Packit Service 360c39
		di = dirtree_find(sdp->md.statfs->i_di.di_num.no_addr);
Packit Service 360c39
		if (di) {
Packit Service 360c39
			log_info( _("Marking GFS1 statfs file inode "
Packit Service 360c39
				    "connected\n"));
Packit Service 360c39
			di->checked = 1;
Packit Service 360c39
		}
Packit Service 360c39
		di = dirtree_find(sdp->md.jiinode->i_di.di_num.no_addr);
Packit Service 360c39
		if (di) {
Packit Service 360c39
			log_info( _("Marking GFS1 jindex file inode "
Packit Service 360c39
				    "connected\n"));
Packit Service 360c39
			di->checked = 1;
Packit Service 360c39
		}
Packit Service 360c39
		di = dirtree_find(sdp->md.riinode->i_di.di_num.no_addr);
Packit Service 360c39
		if (di) {
Packit Service 360c39
			log_info( _("Marking GFS1 rindex file inode "
Packit Service 360c39
				    "connected\n"));
Packit Service 360c39
			di->checked = 1;
Packit Service 360c39
		}
Packit Service 360c39
		di = dirtree_find(sdp->md.qinode->i_di.di_num.no_addr);
Packit Service 360c39
		if (di) {
Packit Service 360c39
			log_info( _("Marking GFS1 quota file inode "
Packit Service 360c39
				    "connected\n"));
Packit Service 360c39
			di->checked = 1;
Packit Service 360c39
		}
Packit Service 360c39
	} else {
Packit Service 360c39
		di = dirtree_find(sdp->master_dir->i_di.di_num.no_addr);
Packit Service 360c39
		if (di) {
Packit Service 360c39
			log_info( _("Marking master directory inode "
Packit Service 360c39
				    "connected\n"));
Packit Service 360c39
			di->checked = 1;
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
Packit Service 360c39
	/* Go through the directory list, working up through the parents
Packit Service 360c39
	 * until we find one that's been checked already.  If we don't
Packit Service 360c39
	 * find a parent, put in lost+found.
Packit Service 360c39
	 */
Packit Service 360c39
	log_info( _("Checking directory linkage.\n"));
Packit Service 360c39
	for (tmp = osi_first(&dirtree); tmp; tmp = next) {
Packit Service 360c39
		next = osi_next(tmp);
Packit Service 360c39
		di = (struct dir_info *)tmp;
Packit Service 360c39
		while (!di->checked) {
Packit Service 360c39
			/* FIXME: Change this so it returns success or
Packit Service 360c39
			 * failure and put the parent inode in a
Packit Service 360c39
			 * param */
Packit Service 360c39
			if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
Packit Service 360c39
				return FSCK_OK;
Packit Service 360c39
			tdi = mark_and_return_parent(sdp, di);
Packit Service 360c39
Packit Service 360c39
			if (tdi) {
Packit Service 360c39
				log_debug( _("Directory at block %llu "
Packit Service 360c39
					     "(0x%llx) connected\n"),
Packit Service 360c39
					   (unsigned long long)di->dinode.no_addr,
Packit Service 360c39
					   (unsigned long long)di->dinode.no_addr);
Packit Service 360c39
				di = tdi;
Packit Service 360c39
				continue;
Packit Service 360c39
			}
Packit Service 360c39
			q = bitmap_type(sdp, di->dinode.no_addr);
Packit Service 360c39
			ip = fsck_load_inode(sdp, di->dinode.no_addr);
Packit Service 360c39
			if (q == GFS2_BLKST_FREE) {
Packit Service 360c39
				log_err( _("Found unlinked directory "
Packit Service 360c39
					   "containing bad block at block %llu"
Packit Service 360c39
					   " (0x%llx)\n"),
Packit Service 360c39
					(unsigned long long)di->dinode.no_addr,
Packit Service 360c39
					(unsigned long long)di->dinode.no_addr);
Packit Service 360c39
				if (query(_("Clear unlinked directory "
Packit Service 360c39
					   "with bad blocks? (y/n) "))) {
Packit Service 360c39
					log_warn( _("inode %lld (0x%llx) is "
Packit Service 360c39
						    "now marked as free\n"),
Packit Service 360c39
						  (unsigned long long)
Packit Service 360c39
						  di->dinode.no_addr,
Packit Service 360c39
						  (unsigned long long)
Packit Service 360c39
						  di->dinode.no_addr);
Packit Service 360c39
					check_n_fix_bitmap(sdp, ip->i_rgd,
Packit Service 360c39
							   di->dinode.no_addr,
Packit Service 360c39
							   0, GFS2_BLKST_FREE);
Packit Service 360c39
					fsck_inode_put(&ip);
Packit Service 360c39
					break;
Packit Service 360c39
				} else
Packit Service 360c39
					log_err( _("Unlinked directory with bad block remains\n"));
Packit Service 360c39
			}
Packit Service 360c39
			if (q != GFS2_BLKST_DINODE) {
Packit Service 360c39
				log_err( _("Unlinked block marked as an inode "
Packit Service 360c39
					   "is not an inode\n"));
Packit Service 360c39
				if (!query(_("Clear the unlinked block?"
Packit Service 360c39
					    " (y/n) "))) {
Packit Service 360c39
					log_err( _("The block was not "
Packit Service 360c39
						   "cleared\n"));
Packit Service 360c39
					fsck_inode_put(&ip);
Packit Service 360c39
					break;
Packit Service 360c39
				}
Packit Service 360c39
				log_warn( _("inode %lld (0x%llx) is now "
Packit Service 360c39
					    "marked as free\n"),
Packit Service 360c39
					  (unsigned long long)di->dinode.no_addr,
Packit Service 360c39
					  (unsigned long long)di->dinode.no_addr);
Packit Service 360c39
				check_n_fix_bitmap(sdp, ip->i_rgd,
Packit Service 360c39
						   di->dinode.no_addr, 0,
Packit Service 360c39
						   GFS2_BLKST_FREE);
Packit Service 360c39
				log_err( _("The block was cleared\n"));
Packit Service 360c39
				fsck_inode_put(&ip);
Packit Service 360c39
				break;
Packit Service 360c39
			}
Packit Service 360c39
Packit Service 360c39
			log_err( _("Found unlinked directory at block %llu"
Packit Service 360c39
				   " (0x%llx)\n"),
Packit Service 360c39
				 (unsigned long long)di->dinode.no_addr,
Packit Service 360c39
				 (unsigned long long)di->dinode.no_addr);
Packit Service 360c39
			/* Don't skip zero size directories with eattrs */
Packit Service 360c39
			if (!ip->i_di.di_size && !ip->i_di.di_eattr){
Packit Service 360c39
				log_err( _("Unlinked directory has zero "
Packit Service 360c39
					   "size.\n"));
Packit Service 360c39
				if (query( _("Remove zero-size unlinked "
Packit Service 360c39
					    "directory? (y/n) "))) {
Packit Service 360c39
					fsck_bitmap_set(ip, di->dinode.no_addr,
Packit Service 360c39
						_("zero-sized unlinked inode"),
Packit Service 360c39
							GFS2_BLKST_FREE);
Packit Service 360c39
					fsck_inode_put(&ip);
Packit Service 360c39
					break;
Packit Service 360c39
				} else {
Packit Service 360c39
					log_err( _("Zero-size unlinked "
Packit Service 360c39
						   "directory remains\n"));
Packit Service 360c39
				}
Packit Service 360c39
			}
Packit Service 360c39
			if (query( _("Add unlinked directory to "
Packit Service 360c39
				    "lost+found? (y/n) "))) {
Packit Service 360c39
				if (add_inode_to_lf(ip)) {
Packit Service 360c39
					fsck_inode_put(&ip);
Packit Service 360c39
					stack;
Packit Service 360c39
					return FSCK_ERROR;
Packit Service 360c39
				}
Packit Service 360c39
				log_warn( _("Directory relinked to lost+found\n"));
Packit Service 360c39
			} else {
Packit Service 360c39
				log_err( _("Unlinked directory remains unlinked\n"));
Packit Service 360c39
			}
Packit Service 360c39
			fsck_inode_put(&ip);
Packit Service 360c39
			break;
Packit Service 360c39
		}
Packit Service 360c39
	}
Packit Service 360c39
	if (lf_dip) {
Packit Service 360c39
		log_debug( _("At end of pass3, lost+found entries is %u\n"),
Packit Service 360c39
				  lf_dip->i_di.di_entries);
Packit Service 360c39
	}
Packit Service 360c39
	return FSCK_OK;
Packit Service 360c39
}