Logo Search packages:      
Sourcecode: redhat-cluster-suite version File versions  Download package

pass1.c

/******************************************************************************
*******************************************************************************
**
**  Copyright (C) Sistina Software, Inc.  1997-2003  All rights reserved.
**  Copyright (C) 2004-2005 Red Hat, Inc.  All rights reserved.
**
**  This copyrighted material is made available to anyone wishing to use,
**  modify, copy, or redistribute it subject to the terms and conditions
**  of the GNU General Public License v.2.
**
*******************************************************************************
******************************************************************************/

/* pass1 checks inodes for format & type, duplicate blocks, & incorrect
 * block count.
 *
 * It builds up tables that contains the state of each block (free,
 * block in use, metadata type, etc), as well as bad blocks and
 * duplicate blocks.  (See block_list.[ch] for more info)
 *
 */

#include <stdio.h>

#include "fsck_incore.h"
#include "fsck.h"
#include "bio.h"
#include "fs_dir.h"
#include "fs_inode.h"
#include "util.h"
#include "block_list.h"
#include "log.h"
#include "inode_hash.h"
#include "inode.h"
#include "link.h"
#include "metawalk.h"

struct block_count {
      uint64_t indir_count;
      uint64_t data_count;
      uint64_t ea_count;
};

static int leaf(struct fsck_inode *ip, uint64_t block, osi_buf_t **bh,
            void *private)
{
      struct fsck_sb *sdp = ip->i_sbd;
      struct block_count *bc = (struct block_count *) private;
      if(check_range(sdp, block)){
            log_warn("Leaf block #%"PRIu64" is out of range for "
                   "directory #%"PRIu64".\n",
                   block, ip->i_di.di_num.no_addr);
            block_set(sdp->bl, ip->i_di.di_num.no_addr, bad_block);
            return 1;
      }
      if(get_and_read_buf(sdp, block, bh, 0)){
            log_err("Unable to read leaf block #%"PRIu64" for "
                  "directory #%"PRIu64".\n",
                  block, ip->i_di.di_num.no_addr);
            if(query(sdp, "Clear directory inode at %"PRIu64"? (y/n) ",
                   ip->i_di.di_num.no_addr)) {
                  block_set(sdp->bl, ip->i_di.di_num.no_addr, meta_inval);
            } else {
                  log_err("Unreadable block %"PRIu64" ignored\n");
            }
            return 1;
      }

      if(check_meta(*bh, GFS_METATYPE_LF)){
            log_err("Bad meta header for leaf block #%"PRIu64
                  " in directory #%"PRIu64". - is %u, should be %u\n",
                   BH_BLKNO(*bh), ip->i_di.di_num.no_addr,
                  ((struct gfs_meta_header *)BH_DATA((*bh)))->mh_type,
                  GFS_METATYPE_LF);
            if(query(sdp, "Clear directory inode at %"PRIu64"? (y/n) ",
                   ip->i_di.di_num.no_addr)) {
                  block_set(sdp->bl, ip->i_di.di_num.no_addr,
                          meta_inval);
                  log_err("Directory inode marked invalid\n");
            } else {
                  log_err("Invalid block %"PRIu64" ignored\n");
            }
            return 1;
      }

      log_debug("\tLeaf block at %15"PRIu64"\n", BH_BLKNO(*bh));
      block_set(sdp->bl, BH_BLKNO(*bh), leaf_blk);
      bc->indir_count++;

      return 0;
}

static int check_metalist(struct fsck_inode *ip, uint64_t block,
                    osi_buf_t **bh, void *private)
{
      struct fsck_sb *sdp = ip->i_sbd;
      struct block_query q = {0};
      int found_dup = 0;
      osi_buf_t *nbh;
      struct block_count *bc = (struct block_count *)private;

      *bh = NULL;

      if (check_range(ip->i_sbd, block)){ /* blk outside of FS */
            block_set(sdp->bl, ip->i_di.di_num.no_addr, bad_block);
            log_debug("Bad indirect block pointer (out of range).\n");

            return 1;
        }
      if(block_check(sdp->bl, block, &q)) {
            stack;
            return -1;
      }
      if(q.block_type != block_free) {
            log_debug("Found duplicate block in indirect block -"
                    " was marked %d\n", q.block_type);
            block_mark(sdp->bl, block, dup_block);
            found_dup = 1;
      }
        get_and_read_buf(ip->i_sbd, block, &nbh, 0);

        /** Attention -- experimental code **/
        if (check_meta(nbh, GFS_METATYPE_IN)){
            log_debug("Bad indirect block pointer "
                  "(points to something that is not an indirect block).\n");
            if(!found_dup) {
                  block_set(sdp->bl, block, meta_inval);
                  relse_buf(ip->i_sbd, nbh);
                  return 1;
            }

            relse_buf(ip->i_sbd, nbh);
        }else{  /* blk check ok */
            *bh = nbh;
        }
        /** Attention -- experimental code end **/

      block_set(sdp->bl, block, indir_blk);
      bc->indir_count++;

      return 0;
}



static int check_data(struct fsck_inode *ip, uint64_t block, void *private)
{
      struct fsck_sb *sdp = ip->i_sbd;
      struct block_query q = {0};
      osi_buf_t *data_bh;
      struct block_count *bc = (struct block_count *) private;

      if (check_range(ip->i_sbd, block)) {

            log_err( "Bad data block pointer (out of range)\n");
            /* Mark the owner of this block with the bad_block
             * designator so we know to check it for out of range
             * blocks later */
            block_set(ip->i_sbd->bl, ip->i_di.di_num.no_addr, bad_block);

            return 1;
      }

      if (ip->i_di.di_flags & GFS_DIF_JDATA){
            /* Journaled data *is* metadata */
            if(get_and_read_buf(ip->i_sbd, block, &data_bh, 0)) {
                  stack;
                  block_set(sdp->bl, ip->i_di.di_num.no_addr, meta_inval);
                  return 1;
            }
            if(check_meta(data_bh, GFS_METATYPE_JD)) {
                  log_err("Block #%"PRIu64" in inode %"PRIu64" does not have "
                        "correct meta header. is %u should be %u\n",
                        block, ip->i_di.di_num.no_addr,
                        gfs32_to_cpu(((struct gfs_meta_header *)
                                    BH_DATA((data_bh)))->mh_type),
                        GFS_METATYPE_JD);
                  relse_buf(sdp, data_bh);
                  block_set(sdp->bl, ip->i_di.di_num.no_addr, meta_inval);
                  return 1;
            }

            if(block_check(sdp->bl, block, &q)) {
                  stack;
                  relse_buf(sdp, data_bh);
                  return -1;
            }
            if(q.block_type != block_free) {
                  log_debug("Found duplicate block at %"
                          PRIu64"\n", block);
                  block_mark(sdp->bl, block, dup_block);
                  bc->data_count++;
                  relse_buf(sdp, data_bh);
                  return 1;
            }
            log_debug("Setting %"PRIu64 " to journal block\n", block);
            block_set(sdp->bl, block, journal_blk);
            bc->data_count++;
            relse_buf(sdp, data_bh);
      }
      else {
            if(block_check(sdp->bl, block, &q)) {
                  stack;
                  return -1;
            }
            if(q.block_type != block_free) {
                  log_debug("Found duplicate block at %"
                          PRIu64"\n", block);
                  block_mark(sdp->bl, block, dup_block);
                  bc->data_count++;
                  return 1;
            }
            log_debug("Setting %"PRIu64 " to data block\n", block);
            block_set(sdp->bl, block, block_used);
            bc->data_count++;
      }

      return 0;
}

static int check_eattr_indir(struct fsck_inode *ip, uint64_t indirect,
                       uint64_t parent, osi_buf_t **bh, void *private)
{
      struct fsck_sb *sdp = ip->i_sbd;
      int ret = 0;
      struct block_query q = {0};
      struct block_count *bc = (struct block_count *) private;

      /* This inode contains an eattr - it may be invalid, but the
       * eattr attributes points to a non-zero block */
      block_set(sdp->bl, ip->i_num.no_addr, eattr_block);

      if(check_range(sdp, indirect)) {
            /*log_warn("EA indirect block #%"PRIu64" is out of range.\n",
                  indirect);
                  block_set(sdp->bl, parent, bad_block);*/
            /* Doesn't help to mark this here - this gets checked
             * in pass1c */
            ret = 1;
      }
      else if(block_check(sdp->bl, indirect, &q)) {
            stack;
            ret = -1;
      }
      else if(q.block_type != block_free) {
            log_debug("Duplicate block found at #%"PRIu64".\n",
                    indirect);
            block_set(sdp->bl, indirect, dup_block);
            bc->ea_count++;
            ret = 1;
      }
      else if(get_and_read_buf(sdp, indirect, bh, 0)) {
            log_warn("Unable to read EA indirect block #%"PRIu64".\n",
                  indirect);
            block_set(sdp->bl, indirect, meta_inval);
            ret = 1;
      }
      else if(check_meta(*bh, GFS_METATYPE_IN)) {
            log_warn("EA indirect block has incorrect type.\n");
            block_set(sdp->bl, BH_BLKNO(*bh), meta_inval);
            ret = 1;
      }
      else {
            /* FIXME: do i need to differentiate this as an ea_indir? */
            block_set(sdp->bl, BH_BLKNO(*bh), indir_blk);
            bc->ea_count++;
      }
      return ret;
}

/**
 * check_extended_leaf_eattr
 * @ip
 * @el_blk: block number of the extended leaf
 *
 * An EA leaf block can contain EA's with pointers to blocks
 * where the data for that EA is kept.  Those blocks still
 * have the gfs meta header of type GFS_METATYPE_EA
 *
 * Returns: 0 if correct[able], -1 if removal is needed
 */
static int check_extended_leaf_eattr(struct fsck_inode *ip, uint64_t *data_ptr,
                             osi_buf_t *leaf_bh,
                             struct gfs_ea_header *ea_hdr,
                             struct gfs_ea_header *ea_hdr_prev,
                             void *private)
{
      osi_buf_t *el_buf;
      struct fsck_sb *sdp = ip->i_sbd;
      struct block_query q;
      uint64_t el_blk = gfs64_to_cpu(*data_ptr);
      struct block_count *bc = (struct block_count *) private;

      if(check_range(sdp, el_blk)){
            log_err("EA extended leaf block #%"PRIu64" "
                  "is out of range.\n",
                  el_blk);
            block_set(sdp->bl, ip->i_di.di_eattr, bad_block);
            return 1;
      }

      if(block_check(sdp->bl, el_blk, &q)) {
            stack;
            return -1;
      }
      if(q.block_type != block_free) {
            block_set(sdp->bl, el_blk, dup_block);
            bc->ea_count++;
            return 1;
      }

      if(get_and_read_buf(sdp, el_blk, &el_buf, 0)){
            log_err("Unable to check extended leaf block.\n");
            block_set(sdp->bl, el_blk, meta_inval);
            return 1;
      }

      if(check_meta(el_buf, GFS_METATYPE_ED)) {
            log_err("EA extended leaf block has incorrect type.\n");
            relse_buf(sdp, el_buf);
            block_set(sdp->bl, el_blk, meta_inval);
            return 1;
      }

      block_set(sdp->bl, el_blk, meta_eattr);
      bc->ea_count++;
      relse_buf(sdp, el_buf);
      return 0;
}

static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
                      uint64_t parent, osi_buf_t **bh, void *private)
{
      struct fsck_sb *sdp = ip->i_sbd;
      osi_buf_t *leaf_bh;
      int ret = 0;
      struct block_query q = {0};
      struct block_count *bc = (struct block_count *) private;

      /* This inode contains an eattr - it may be invalid, but the
       * eattr attributes points to a non-zero block */
      block_set(sdp->bl, ip->i_num.no_addr, eattr_block);

      if(check_range(sdp, block)){
            log_warn("EA leaf block #%"PRIu64" in inode %"PRIu64
                   " is out of range.\n",
                   ip->i_num.no_addr, block);
            block_set(sdp->bl, ip->i_di.di_eattr, bad_block);
            ret = 1;
      }
      else if(block_check(sdp->bl, block, &q)) {
            stack;
            return -1;
      }
      else if(q.block_type != block_free) {
            log_debug("Duplicate block found at #%"PRIu64".\n",
                    block);
            block_set(sdp->bl, block, dup_block);
            bc->ea_count++;
      }
      else if(get_and_read_buf(sdp, block, &leaf_bh, 0)){
            log_warn("Unable to read EA leaf block #%"PRIu64".\n",
                   block);
            block_set(sdp->bl, block, meta_inval);
            ret = 1;
      } else if(check_meta(leaf_bh, GFS_METATYPE_EA)) {
            log_warn("EA leaf block has incorrect type.\n");
            block_set(sdp->bl, BH_BLKNO(leaf_bh), meta_inval);
            relse_buf(sdp, leaf_bh);
            ret = 1;
      }
      else {
            block_set(sdp->bl, BH_BLKNO(leaf_bh), meta_eattr);
            bc->ea_count++;
      }

      *bh = leaf_bh;

      return ret;
}

static int check_eattr_entries(struct fsck_inode *ip,
                         osi_buf_t *leaf_bh,
                         struct gfs_ea_header *ea_hdr,
                         struct gfs_ea_header *ea_hdr_prev,
                         void *private)
{
      struct fsck_sb *sdp = ip->i_sbd;
      char ea_name[256];

      if(!ea_hdr->ea_name_len){
            /* Skip this entry for now */
            return 1;
      }

      memset(ea_name, 0, sizeof(ea_name));
      strncpy(ea_name, (char *)ea_hdr + sizeof(struct gfs_ea_header),
            ea_hdr->ea_name_len);

      if(!GFS_EATYPE_VALID(ea_hdr->ea_type) &&
         ((ea_hdr_prev) || (!ea_hdr_prev && ea_hdr->ea_type))){
            /* Skip invalid entry */
            return 1;
      }

      if(ea_hdr->ea_num_ptrs){
            uint32 avail_size;
            int max_ptrs;

            avail_size = sdp->sb.sb_bsize - sizeof(struct gfs_meta_header);
            max_ptrs = (gfs32_to_cpu(ea_hdr->ea_data_len)+avail_size-1)/avail_size;

            if(max_ptrs > ea_hdr->ea_num_ptrs) {
                  return 1;
            } else {
                  log_debug("  Pointers Required: %d\n"
                          "  Pointers Reported: %d\n",
                          max_ptrs,
                          ea_hdr->ea_num_ptrs);
            }

      }

      return 0;
}

struct metawalk_fxns pass1_fxns = {
      .private = NULL,
      .check_leaf = leaf,
      .check_metalist = check_metalist,
      .check_data = check_data,
      .check_eattr_indir = check_eattr_indir,
      .check_eattr_leaf = check_eattr_leaf,
      .check_dentry = NULL,
      .check_eattr_entry = check_eattr_entries,
      .check_eattr_extentry = check_extended_leaf_eattr,
};

int clear_metalist(struct fsck_inode *ip, uint64_t block,
               osi_buf_t **bh, void *private)
{
      struct fsck_sb *sdp = ip->i_sbd;
      struct block_query q = {0};

      *bh = NULL;

      if(block_check(sdp->bl, block, &q)) {
            stack;
            return -1;
      }
      if(!q.dup_block) {
            block_set(sdp->bl, block, block_free);
            return 0;
      }
      return 0;
}

int clear_data(struct fsck_inode *ip, uint64_t block, void *private)
{
      struct fsck_sb *sdp = ip->i_sbd;
      struct block_query q = {0};

      if(block_check(sdp->bl, block, &q)) {
            stack;
            return -1;
      }
      if(!q.dup_block) {
            block_set(sdp->bl, block, block_free);
            return 0;
      }
      return 0;

}

int clear_leaf(struct fsck_inode *ip, uint64_t block,
             osi_buf_t **bh, void *private)
{

      struct fsck_sb *sdp = ip->i_sbd;
      struct block_query q = {0};
      log_crit("Clearing leaf %"PRIu64"\n", block);

      if(block_check(sdp->bl, block, &q)) {
            stack;
            return -1;
      }
      if(!q.dup_block) {
            log_crit("Setting leaf invalid\n");
            if(block_set(sdp->bl, block, block_free)) {
                  stack;
                  return -1;
            }
            return 0;
      }
      return 0;

}

int add_to_dir_list(struct fsck_sb *sbp, uint64_t block)
{
      struct dir_info *di = NULL;
      struct dir_info *newdi;

      /* FIXME: This list should probably be a b-tree or
       * something...but since most of the time we're going to be
       * tacking the directory onto the end of the list, it doesn't
       * matter too much */
      find_di(sbp, block, &di);
      if(di) {
            log_err("Attempting to add directory block %"PRIu64
                  " which is already in list\n", block);
            return -1;
      }

      if(!(newdi = (struct dir_info *) malloc(sizeof(*newdi)))) {
            log_crit("Unable to allocate dir_info structure\n");
            return -1;
      }
      if(!memset(newdi, 0, sizeof(*newdi))) {
            log_crit("error while zeroing dir_info structure\n");
            return -1;
      }

      newdi->dinode = block;

      dinode_hash_insert(sbp->dir_hash, block, newdi);

      return 0;
}




int handle_di(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
{
      struct block_query q = {0};
      struct fsck_inode *ip;
      int error;
      struct block_count bc = {0};
      struct metawalk_fxns invalidate_metatree = {0};
      invalidate_metatree.check_metalist = clear_metalist;
      invalidate_metatree.check_data = clear_data;
      invalidate_metatree.check_leaf = clear_leaf;

      if(copyin_inode(sdp, bh, &ip)) {
            stack;
            return -1;
      }

      if (ip->i_di.di_flags & GFS_DIF_UNUSED){
            if(mfree) {
                  if(block_set(sdp->bl, block, meta_free)) {
                        stack;
                        goto fail;
                  }
                  goto success;
            } else {
                  log_err("Found unused inode marked in-use\n");
                  if(query(sdp, "Clear unused inode at block %"
                         PRIu64"? (y/n) ", block)) {
                        if(block_set(sdp->bl, block, meta_inval)) {
                              stack;
                              goto fail;
                        }
                        goto success;
                  } else {
                        log_err("Unused inode still marked in-use\n");
                  }
            }

      } else {
            if(mfree) {
                  if(block_set(sdp->bl, block, meta_free)) {
                        stack;
                        goto fail;
                  }
                  goto success;
            }
      }

      if (ip->i_di.di_num.no_addr != block) {
            log_err("Bad dinode Address.  "
                  "Found %"PRIu64", "
                  "Expected %"PRIu64"\n",
                  ip->i_di.di_num.no_addr, block);
            if(query(sdp, "Fix address in inode at block %"
                   PRIu64"? (y/n) ", block)) {
                  ip->i_di.di_num.no_addr =
                        ip->i_di.di_num.no_formal_ino =
                        block;
                  if(fs_copyout_dinode(ip)){
                        log_crit("Bad dinode address can not be reset.\n");
                        goto fail;
                  } else {
                        log_err("Bad dinode address reset.\n");
                  }
            } else {
                  log_err("Address in inode at block %"PRIu64
                         " not fixed\n", block);
            }

      }

      if(block_check(sdp->bl, block, &q)) {
            stack;
            goto fail;
      }
      if(q.block_type != block_free) {
            log_debug("Found duplicate block at %"PRIu64"\n",
                    block);
            if(block_mark(sdp->bl, block, dup_block)) {
                  stack;
                  goto fail;
            }
            goto success;
      }

      switch(ip->i_di.di_type) {

      case GFS_FILE_DIR:
            if(block_set(sdp->bl, block, inode_dir)) {
                  stack;
                  goto fail;
            }
            if(add_to_dir_list(sdp, block)) {
                  stack;
                  goto fail;
            }
            break;
      case GFS_FILE_REG:
            if(block_set(sdp->bl, block, inode_file)) {
                  stack;
                  goto fail;
            }
            break;
      case GFS_FILE_LNK:
            if(block_set(sdp->bl, block, inode_lnk)) {
                  stack;
                  goto fail;
            }
            break;
      case GFS_FILE_BLK:
            if(block_set(sdp->bl, block, inode_blk)) {
                  stack;
                  goto fail;
            }
            break;
      case GFS_FILE_CHR:
            if(block_set(sdp->bl, block, inode_chr)) {
                  stack;
                  goto fail;
            }
            break;
      case GFS_FILE_FIFO:
            if(block_set(sdp->bl, block, inode_fifo)) {
                  stack;
                  goto fail;
            }
            break;
      case GFS_FILE_SOCK:
            if(block_set(sdp->bl, block, inode_sock)) {
                  stack;
                  goto fail;
            }
            break;
      default:
            if(block_set(sdp->bl, block, meta_inval)) {
                  stack;
                  goto fail;
            }
            goto success;
      }
      if(set_link_count(ip->i_sbd, ip->i_num.no_formal_ino,
                    ip->i_di.di_nlink)) {
            stack;
            goto fail;
      }

      /* FIXME: fix height and depth here - wasn't implemented in
       * old fsck either, so no biggy... */
      if (ip->i_di.di_height < compute_height(sdp, ip->i_di.di_size)){
            log_warn("Dinode #%"PRIu64" has bad height  "
                   "Found %u, Expected >= %u\n",
                   ip->i_di.di_num.no_addr, ip->i_di.di_height,
                   compute_height(sdp, ip->i_di.di_size));
                  /* once implemented, remove continue statement */
            log_warn("Marking inode invalid\n");
            if(block_set(sdp->bl, block, meta_inval)) {
                  stack;
                  goto fail;
            }
            goto success;
      }

      if (ip->i_di.di_type == (GFS_FILE_DIR &&
                         (ip->i_di.di_flags & GFS_DIF_EXHASH)))
      {
            if (((1 << ip->i_di.di_depth) * sizeof(uint64_t)) !=
                ip->i_di.di_size)
            {
                  log_warn("Directory dinode #%"PRIu64" has bad depth.  "
                         "Found %u, Expected %u\n",
                         ip->i_di.di_num.no_addr, ip->i_di.di_depth,
                         (1 >> (ip->i_di.di_size/sizeof(uint64))));
                  /* once implemented, remove continue statement */
                  log_warn("Marking inode invalid\n");
                  if(block_set(sdp->bl, block, meta_inval)) {
                        stack;
                        goto fail;
                  }
                  goto success;
            }
      }

      pass1_fxns.private = &bc;

      error = check_metatree(ip, &pass1_fxns);
      if(error < 0) {
            return 0;
      }
      if(error > 0) {
            log_warn("Marking inode invalid\n");
            /* FIXME: Must set all leaves invalid as well */
            check_metatree(ip, &invalidate_metatree);
            block_set(ip->i_sbd->bl, ip->i_di.di_num.no_addr, meta_inval);
            return 0;
      }

      /* FIXME: is this correct? */
      if(check_inode_eattr(ip, &pass1_fxns) < 0){
            osi_buf_t   *di_bh;
            ip->i_di.di_eattr = 0;
            if(get_and_read_buf(sdp, ip->i_di.di_num.no_addr, &di_bh, 0)){
                  stack;
                  log_crit("Bad EA reference remains.\n");
            } else {
                  gfs_dinode_out(&ip->i_di, BH_DATA(di_bh));
                  if(write_buf(ip->i_sbd, di_bh, 0) < 0){
                        stack;
                        log_crit("Bad EA reference remains.\n");
                  } else {
                        log_warn("Bad EA reference cleared.\n");
                  }
                  relse_buf(sdp, di_bh);
            }
      }

      if(ip->i_di.di_blocks != (1 + bc.indir_count + bc.data_count + bc.ea_count)) {
            osi_buf_t   *di_bh;
            log_err("Ondisk block count does not match what fsck"
                  " found for inode %"PRIu64"\n", ip->i_di.di_num.no_addr);
            if(query(sdp, "Fix ondisk block count? (y/n) ")) {
                  ip->i_di.di_blocks = 1 + bc.indir_count +
                        bc.data_count +
                        bc.ea_count;
                  if(get_and_read_buf(sdp, ip->i_di.di_num.no_addr,
                                  &di_bh, 0)){
                        stack;
                        log_crit("Bad block count remains\n");
                  } else {
                        gfs_dinode_out(&ip->i_di, BH_DATA(di_bh));
                        if(write_buf(ip->i_sbd, di_bh, 0) < 0){
                              stack;
                              log_crit("Bad block count remains\n");
                        } else {
                              log_warn("Bad block count fixed\n");
                        }
                        relse_buf(sdp, di_bh);
                  }
            } else {
                  log_err("Bad block count for %"PRIu64" not fixed\n",
                        ip->i_di.di_num.no_addr);
            }
      }

 success:
      free(ip);
      return 0;

 fail:
      free(ip);
      return -1;

}


int scan_meta(struct fsck_sb *sdp, osi_buf_t *bh, uint64_t block, int mfree)
{

      if (check_meta(bh, 0)) {
            log_debug("Found invalid metadata at %"PRIu64"\n", block);
            if(block_set(sdp->bl, block, meta_inval)) {
                  stack;
                  return -1;
            }
            return 0;
      }

      log_debug("Checking metadata block %"PRIu64"\n", block);

      if (!check_type(bh, GFS_METATYPE_DI)) {
            if(handle_di(sdp, bh, block, mfree)) {
                  stack;
                  return -1;
            }
      }
      else if (!check_type(bh, GFS_METATYPE_NONE)) {
            if(block_set(sdp->bl, block, meta_free)) {
                  stack;
                  return -1;
            }
      } else {
            log_debug("Metadata block %"PRIu64
                    " not an inode or free metadata\n",
                    block);
      }
      /* Ignore everything else - they should be hit by the
       * handle_di step */

      return 0;
}

/**
 * pass1 - walk through inodes and check inode state
 *
 * this walk can be done using root inode and depth first search,
 * watching for repeat inode numbers
 *
 * format & type
 * link count
 * duplicate blocks
 * bad blocks
 * inodes size
 * dir info
 */
int pass1(struct fsck_sb *sbp)
{
      osi_buf_t *bh;
      osi_list_t *tmp;
      uint64_t block;
      struct fsck_rgrp *rgd;
      int first;
      uint64_t i;
      uint64_t j;
      uint64_t blk_count;
      uint64_t offset;
      uint64_t rg_count = 0;
      int mfree = 0;

      /* FIXME: What other metadata should we look for? */

      /* Mark the journal blocks as 'other metadata' */
      for (i = 0; i < sbp->journals; i++) {
            struct gfs_jindex *ji;
            ji = &sbp->jindex[i];
            for(j = ji->ji_addr;
                j < ji->ji_addr + (ji->ji_nsegment * sbp->sb.sb_seg_size);
                j++) {
                  if(block_set(sbp->bl, j, journal_blk)) {
                        stack;
                        return -1;
                  }
            }
      }


      /* So, do we do a depth first search starting at the root
       * inode, or use the rg bitmaps, or just read every fs block
       * to find the inodes?  If we use the depth first search, why
       * have pass3 at all - if we use the rg bitmaps, pass5 is at
       * least partially invalidated - if we read every fs block,
       * things will probably be intolerably slow.  The current fsck
       * uses the rg bitmaps, so maybe that's the best way to start
       * things - we can change the method later if necessary.
       */

      for (tmp = sbp->rglist.next; tmp != &sbp->rglist;
           tmp = tmp->next, rg_count++){
            log_info("Checking metadata in Resource Group %"PRIu64"\n",
                   rg_count);
            rgd = osi_list_entry(tmp, struct fsck_rgrp, rd_list);
            if(fs_rgrp_read(rgd)){
                  stack;
                  return -1;
            }
            log_debug("RG at %"PRIu64" is %u long\n", rgd->rd_ri.ri_addr, rgd->rd_ri.ri_length);
            for (i = 0; i < rgd->rd_ri.ri_length; i++) {
                  if(block_set(sbp->bl, rgd->rd_ri.ri_addr + i,
                             meta_other)){
                        stack;
                        return -1;
                  }
            }

            offset = sizeof(struct gfs_rgrp);
            blk_count = 1;

            first = 1;

            while (1) {

                  /* "block" is relative to the entire file system */
                  if(next_rg_meta_free(rgd, &block, first, &mfree))
                        break;


                  if(get_and_read_buf(sbp, block, &bh, 0)){
                        stack;
                        log_crit("Unable to retrieve block %"PRIu64
                               "\n", block);
                        fs_rgrp_relse(rgd);
                        return -1;
                  }

                  if(scan_meta(sbp, bh, block, mfree)) {
                        stack;
                        relse_buf(sbp, bh);
                        fs_rgrp_relse(rgd);
                        return -1;
                  }
                  relse_buf(sbp, bh);
                  first = 0;
            }
            fs_rgrp_relse(rgd);
      }

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index