|
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 |
"a_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 |
}
|