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

pass2.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.
**
*******************************************************************************
******************************************************************************/

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

#define MAX_FILENAME 256

struct dir_status {
      uint8_t dotdir:1;
      uint8_t dotdotdir:1;
      struct block_query q;
      uint32_t entry_count;
};


static int check_leaf(struct fsck_inode *ip, uint64_t block, osi_buf_t **lbh,
                  void *private)
{
      uint64_t chain_no;
      struct fsck_sb *sbp = ip->i_sbd;
      struct gfs_leaf leaf;
      osi_buf_t *chain_head = NULL;
      osi_buf_t *bh = NULL;
      int chain=0;
      int error;

      chain_no = block;

      do {
            /* FIXME: check the range of the leaf? */
            /* check the leaf and stuff */

            error = get_and_read_buf(sbp, chain_no, &bh, 0);

            if(error){
                  stack;
                  goto fail;
            }

            gfs_leaf_in(&leaf, BH_DATA(bh));

            /* Check the leaf headers */

            if(!chain){
                  chain = 1;
                  chain_head = bh;
                  chain_no = leaf.lf_next;
            }
            else {
                  relse_buf(sbp, bh);
                  bh = NULL;
                  break;
            }
      } while(chain_no);

      *lbh = chain_head;
      return 0;

 fail:
      /* FIXME: check this error path */
      if(chain_head){
            if(chain_head == bh){ bh = NULL; }
            relse_buf(sbp, chain_head);
      }
      if(bh)
            relse_buf(sbp, bh);

      return -1;

}


/* Set children's parent inode in dir_info structure - ext2 does not set
 * dotdot inode here, but instead in pass3 - should we? */
int set_parent_dir(struct fsck_sb *sbp, uint64_t childblock,
               uint64_t parentblock)
{
      struct dir_info *di;

      if(!find_di(sbp, childblock, &di)) {
            if(di->dinode == childblock) {
                  if (di->treewalk_parent) {
                        log_err("Another directory (%"PRIu64
                              ") already contains"
                              " this child - checking %"PRIu64"\n",
                              di->treewalk_parent, parentblock);
                        return 1;
                  }
                  di->treewalk_parent = parentblock;
            }
      } else {
            log_err("Unable to find block %"PRIu64" in dir_info list\n",
                  childblock);
            return -1;
      }

      return 0;
}

/* Set's the child's '..' directory inode number in dir_info structure */
int set_dotdot_dir(struct fsck_sb *sbp, uint64_t childblock,
               uint64_t parentblock)
{
      struct dir_info *di;

      if(!find_di(sbp, childblock, &di)) {
            if(di->dinode == childblock) {
                  /* Special case for root inode because we set
                   * it earlier */
                  if(di->dotdot_parent && sbp->sb.sb_root_di.no_addr
                     != di->dinode) {
                        /* This should never happen */
                        log_crit("dotdot parent already set for"
                               " block %"PRIu64" -> %"PRIu64"\n",
                               childblock, di->dotdot_parent);
                        return -1;
                  }
                  di->dotdot_parent = parentblock;
            }
      } else {
            log_err("Unable to find block %"PRIu64" in dir_info list\n",
                  childblock);
            return -1;
      }

      return 0;

}

static int check_eattr_indir(struct fsck_inode *ip, uint64_t block,
                      uint64_t parent, osi_buf_t **bh, void *private)
{

      return 0;
}
static int check_eattr_leaf(struct fsck_inode *ip, uint64_t block,
                      uint64_t parent, osi_buf_t **bh, void *private)
{
      osi_buf_t *leaf_bh;

      if(get_and_read_buf(ip->i_sbd, block, &leaf_bh, 0)){
            log_warn("Unable to read EA leaf block #%"PRIu64".\n",
                   block);
            block_set(ip->i_sbd->bl, block, meta_inval);
            return 1;
      }


      return 0;
}

static int check_file_type(uint16_t de_type, uint8_t block_type) {
      switch(block_type) {
      case inode_dir:
            if(de_type != GFS_FILE_DIR)
                  return 1;
            break;
      case inode_file:
            if(de_type != GFS_FILE_REG)
                  return 1;
            break;
      case inode_lnk:
            if(de_type != GFS_FILE_LNK)
                  return 1;
            break;
      case inode_blk:
            if(de_type != GFS_FILE_BLK)
                  return 1;
            break;
      case inode_chr:
            if(de_type != GFS_FILE_CHR)
                  return 1;
            break;
      case inode_fifo:
            if(de_type != GFS_FILE_FIFO)
                  return 1;
            break;
      case inode_sock:
            if(de_type != GFS_FILE_SOCK)
                  return 1;
            break;
      default:
            log_err("Invalid block type\n");
            return -1;
            break;
      }
      return 0;
}

/* FIXME: should maybe refactor this a bit - but need to deal with
 * FIXMEs internally first */
int check_dentry(struct fsck_inode *ip, struct gfs_dirent *dent,
             struct gfs_dirent *prev_de,
             osi_buf_t *bh, char *filename, int *update,
             uint16_t *count, void *priv)
{
      struct fsck_sb *sbp = ip->i_sbd;
      struct block_query q = {0};
      char tmp_name[MAX_FILENAME];
      uint64_t entryblock;
      struct dir_status *ds = (struct dir_status *) priv;
      int error;
      struct fsck_inode *entry_ip = NULL;
      struct metawalk_fxns clear_eattrs = {0};
      struct gfs_dirent dentry, *de;

      memset(&dentry, 0, sizeof(struct gfs_dirent));
      gfs_dirent_in(&dentry, (char *)dent);
      de = &dentry;

      clear_eattrs.check_eattr_indir = clear_eattr_indir;
      clear_eattrs.check_eattr_leaf = clear_eattr_leaf;
      clear_eattrs.check_eattr_entry = clear_eattr_entry;
      clear_eattrs.check_eattr_extentry = clear_eattr_extentry;

      entryblock = de->de_inum.no_addr;

      /* Start of checks */
      if (de->de_rec_len < GFS_DIRENT_SIZE(de->de_name_len)){
            log_err("Dir entry with bad record or name length\n"
                  "\tRecord length = %u\n"
                  "\tName length = %u\n",
                  de->de_rec_len,
                  de->de_name_len);
            block_set(sbp->bl, ip->i_num.no_addr, meta_inval);
            return 1;
            /* FIXME: should probably delete the entry here at the
             * very least - maybe look at attempting to fix it */
      }

      if (de->de_hash != gfs_dir_hash(filename, de->de_name_len)){
              log_err("Dir entry with bad hash or name length\n"
                   "\tHash found         = %u\n"
                   "\tName found         = %s\n"
                   "\tName length found  = %u\n"
                   "\tHash expected      = %u\n",
                   de->de_hash,
                   filename,
                   de->de_name_len,
                   gfs_dir_hash(filename, de->de_name_len));
            return 1;
      }
      /* FIXME: This should probably go to the top of the fxn, and
       * references to filename should be replaced with tmp_name */
      memset(tmp_name, 0, MAX_FILENAME);
      if(de->de_name_len < MAX_FILENAME){
            strncpy(tmp_name, filename, de->de_name_len);
      } else {
            strncpy(tmp_name, filename, MAX_FILENAME - 1);
      }

      if(check_range(ip->i_sbd, entryblock)) {
            log_err("Block # referenced by directory entry %s is out of range\n",
                  tmp_name);
            if(query(ip->i_sbd, "Clear directory entry tp out of range block? (y/n) ")) {
                  log_err("Clearing %s\n", tmp_name);
                  if(dirent_del(ip, bh, prev_de, dent)) {
                        stack;
                        return -1;
                  }
                  return 1;
            } else {
                  log_err("Directory entry to out of range block remains\n");
                  *update = 1;
                  (*count)++;
                  ds->entry_count++;
                  return 0;
            }
      }
      if(block_check(sbp->bl, de->de_inum.no_addr, &q)) {
            stack;
            return -1;
      }
      /* Get the status of the directory inode */
      if(q.bad_block) {
            /* This entry's inode has bad blocks in it */

            /* FIXME: user interface */
            /* FIXME: do i want to kill the inode here? */
            /* Handle bad blocks */
            log_err("Found a bad directory entry: %s\n", filename);

            if(query(sbp, "Clear entry to inode containing bad blocks? (y/n)")) {

                  load_inode(sbp, de->de_inum.no_addr, &entry_ip);
                  check_inode_eattr(entry_ip, &clear_eattrs);
                  free_inode(&entry_ip);

                  /* FIXME: make sure all blocks referenced by
                   * this inode are cleared in the bitmap */

                  dirent_del(ip, bh, prev_de, dent);

                  block_set(sbp->bl, de->de_inum.no_addr, meta_inval);
                  return 1;
            } else {
                  log_warn("Entry to inode containing bad blocks remains\n");
                  *update = 1;
                  (*count)++;
                  ds->entry_count++;
                  return 0;
            }

      }
      if(q.block_type != inode_dir && q.block_type != inode_file &&
         q.block_type != inode_lnk && q.block_type != inode_blk &&
         q.block_type != inode_chr && q.block_type != inode_fifo &&
         q.block_type != inode_sock) {
            log_err("Found directory entry '%s' in %"PRIu64" to something"
                  " not a file or directory!\n", tmp_name,
                  ip->i_num.no_addr);
            log_debug("block #%"PRIu64" in %"PRIu64"\n",
                    de->de_inum.no_addr, ip->i_num.no_addr);

            if(query(sbp, "Clear directory entry to non-inode block? (y/n) ")) {
                  /* FIXME: make sure all blocks referenced by
                   * this inode are cleared in the bitmap */

                  if(dirent_del(ip, bh, prev_de, dent)) {
                        stack;
                        return -1;
                  }
                  log_warn("Directory entry '%s' cleared\n", tmp_name);
                  return 1;
            } else {
                  log_err("Directory entry to non-inode block remains\n");
                  *update = 1;
                  (*count)++;
                  ds->entry_count++;
                  return 0;
            }
      }


      error = check_file_type(de->de_type, q.block_type);
      if(error < 0) {
            stack;
            return -1;
      }
      if(error > 0) {
            log_warn("Type in dir entry (%s, %"PRIu64") conflicts with "
                   "type in dinode. (Dir entry is stale.)\n",
                   tmp_name, de->de_inum.no_addr);
            if(query(sbp, "Clear stale directory entry? (y/n) ")) {
                  load_inode(sbp, de->de_inum.no_addr, &entry_ip);
                  check_inode_eattr(entry_ip, &clear_eattrs);
                  free_inode(&entry_ip);

                  if(dirent_del(ip, bh, prev_de, dent)) {
                        stack;
                        return -1;
                  }
                  return 1;
            } else {
                  log_err("Stale directory entry remains\n");
                  *update  = 1;
                  (*count)++;
                  ds->entry_count++;
                  return 0;
            }
      }


      if(!strcmp(".", tmp_name)) {
            log_debug("Found . dentry\n");

            if(ds->dotdir) {
                  log_err("already found '.' entry\n");
                  if(query(sbp, "Clear duplicate '.' entry? (y/n) ")) {

                        load_inode(sbp, de->de_inum.no_addr, &entry_ip);
                        check_inode_eattr(entry_ip, &clear_eattrs);
                        free_inode(&entry_ip);

                        dirent_del(ip, bh, prev_de, dent);
                        return 1;
                  } else {
                        log_err("Duplicate '.' entry remains\n");
                        /* FIXME: Should we continue on here
                         * and check the rest of the '.'
                         * entry? */
                        increment_link(sbp, de->de_inum.no_addr);
                        *update  = 1;
                        (*count)++;
                        ds->entry_count++;
                        return 0;
                  }
            }

            /* GFS does not rely on '.' being in a certain
             * location */

            /* check that '.' refers to this inode */
            if(de->de_inum.no_addr != ip->i_num.no_addr) {
                  log_err("'.' entry's value incorrect."
                        "  Points to %"PRIu64
                        " when it should point to %"
                        PRIu64".\n",
                        de->de_inum.no_addr,
                        ip->i_num.no_addr);
                  if(query(sbp, "remove '.' reference? (y/n) ")) {
                        load_inode(sbp, de->de_inum.no_addr, &entry_ip);
                        check_inode_eattr(entry_ip, &clear_eattrs);
                        free_inode(&entry_ip);

                        dirent_del(ip, bh, prev_de, dent);
                        return 1;

                  } else {
                        log_err("Invalid '.' reference remains\n");
                        /* Not setting ds->dotdir here since
                         * this '.' entry is invalid */
                        increment_link(sbp, de->de_inum.no_addr);
                        *update = 1;
                        (*count)++;
                        ds->entry_count++;
                        return 0;
                  }
            }

            ds->dotdir = 1;
            increment_link(sbp, de->de_inum.no_addr);
            *update = 1;
            (*count)++;
            ds->entry_count++;

            return 0;
      }
      if(!strcmp("..", tmp_name)) {
            log_debug("Found .. dentry\n");
            if(ds->dotdotdir) {
                  log_err("already found '..' entry\n");
                  if(query(sbp, "Clear duplicate '..' entry? (y/n) ")) {

                        load_inode(sbp, de->de_inum.no_addr, &entry_ip);
                        check_inode_eattr(entry_ip, &clear_eattrs);
                        free_inode(&entry_ip);

                        dirent_del(ip, bh, prev_de, dent);
                        *update = 1;
                        return 1;
                  } else {
                        log_err("Duplicate '..' entry remains\n");
                        /* FIXME: Should we continue on here
                         * and check the rest of the '..'
                         * entry? */
                        increment_link(sbp, de->de_inum.no_addr);
                        *update  = 1;
                        (*count)++;
                        ds->entry_count++;
                        return 0;
                  }
            }

            if(q.block_type != inode_dir) {
                  log_err("Found '..' entry pointing to"
                        " something that's not a directory");
                  if(query(sbp, "Clear bad '..' directory entry? (y/n) ")) {
                        load_inode(sbp, de->de_inum.no_addr, &entry_ip);
                        check_inode_eattr(entry_ip, &clear_eattrs);
                        free_inode(&entry_ip);

                        dirent_del(ip, bh, prev_de, dent);
                        *update = 1;
                        return 1;
                  } else {
                        log_err("Bad '..' directory entry remains\n");
                        increment_link(sbp, de->de_inum.no_addr);
                        *update  = 1;
                        (*count)++;
                        ds->entry_count++;
                        return 0;
                  }
            }
            /* GFS does not rely on '..' being in a
             * certain location */

            /* Add the address this entry is pointing to
             * to this inode's dotdot_parent in
             * dir_info */
            if(set_dotdot_dir(sbp, ip->i_num.no_addr,
                          entryblock)) {
                  stack;
                  return -1;
            }

            ds->dotdotdir = 1;
            increment_link(sbp, de->de_inum.no_addr);
            *update = 1;
            (*count)++;
            ds->entry_count++;
            return 0;
      }

      /* After this point we're only concerned with
       * directories */
      if(q.block_type != inode_dir) {
            log_debug("Found non-dir inode dentry\n");
            increment_link(sbp, de->de_inum.no_addr);
            *update = 1;
            (*count)++;
            ds->entry_count++;
            return 0;
      }

      log_debug("Found plain directory dentry\n");
      error = set_parent_dir(sbp, entryblock, ip->i_num.no_addr);
      if(error > 0) {
            log_err("Hard link to block %"PRIu64" detected.\n", filename, entryblock);

            if(query(sbp, "Clear hard link to directory? (y/n) ")) {
                  *update = 1;

                  dirent_del(ip, bh, prev_de, dent);
                  log_warn("Directory entry %s cleared\n", filename);

                  return 1;
            } else {
                  log_err("Hard link to directory remains\n");
                  *update = 1;
                  (*count)++;
                  ds->entry_count++;
                  return 0;
            }
      }
      else if (error < 0) {
            stack;
            return -1;
      }
      increment_link(sbp, de->de_inum.no_addr);
      *update = 1;
      (*count)++;
      ds->entry_count++;
      /* End of checks */
      return 0;
}


struct metawalk_fxns pass2_fxns = {
      .private = NULL,
      .check_leaf = check_leaf,
      .check_metalist = NULL,
      .check_data = NULL,
      .check_eattr_indir = check_eattr_indir,
      .check_eattr_leaf = check_eattr_leaf,
      .check_dentry = check_dentry,
      .check_eattr_entry = NULL,
};




int build_rooti(struct fsck_sb *sbp)
{
      struct fsck_inode *ip;
      osi_buf_t *bh;
      get_and_read_buf(sbp, GFS_SB_ADDR >> sbp->fsb2bb_shift, &bh, 0);
      /* Create a new inode ondisk */
      create_inode(sbp, GFS_FILE_DIR, &ip);
      /* Attach it to the superblock's sb_root_di address */
      sbp->sb.sb_root_di.no_addr =
            sbp->sb.sb_root_di.no_formal_ino = ip->i_num.no_addr;
      /* Write out sb change */
      gfs_sb_out(&sbp->sb, BH_DATA(bh));
      write_buf(sbp, bh, 1);
      relse_buf(sbp, bh);
      sbp->rooti = ip;

      if(fs_dir_add(ip, &(osi_filename_t){".", 1},
                  &(ip->i_num), ip->i_di.di_type)){
            stack;
            log_err("Unable to add \".\" entry to new root inode\n");
            return -1;
      }

      if(fs_dir_add(ip, &(osi_filename_t){"..", 2},
                  &ip->i_num, ip->i_di.di_type)){
            stack;
            log_err("Unable to add \"..\" entry to new root inode\n");
            return -1;
      }

      block_set(sbp->bl, ip->i_num.no_addr, inode_dir);
      add_to_dir_list(sbp, ip->i_num.no_addr);

      /* Attach l+f to it */
      if(fs_mkdir(sbp->rooti, "l+f", 00700, &(sbp->lf_dip))){
            log_err("Unable to create/locate l+f directory.\n");
            return -1;
      }

      if(sbp->lf_dip){
            log_debug("Lost and Found directory inode is at "
                    "block #%"PRIu64".\n",
                    sbp->lf_dip->i_num.no_addr);
      }
      block_set(sbp->bl, sbp->lf_dip->i_num.no_addr, inode_dir);

      add_to_dir_list(sbp, sbp->lf_dip->i_num.no_addr);

      return 0;
}

/* Check root inode and verify it's in the bitmap */
int check_root_dir(struct fsck_sb *sbp)
{
      uint64_t rootblock;
      struct dir_status ds = {0};
      struct fsck_inode *ip;
      osi_buf_t b, *bh = &b;
      osi_filename_t filename;
      char tmp_name[256];
      int update=0, error = 0;
      /* Read in the root inode, look at its dentries, and start
       * reading through them */
      rootblock = sbp->sb.sb_root_di.no_addr;

      /* FIXME: check this block's validity */

      if(block_check(sbp->bl, rootblock, &ds.q)) {
            log_crit("Can't get root block %"PRIu64" from block list\n",
                   rootblock);
            /* FIXME: Need to check if the root block is out of
             * the fs range and if it is, rebuild it.  Still can
             * error out if the root block number is valid, but
             * block_check fails */
            return -1;
/*          if(build_rooti(sbp)) {
                  stack;
                  return -1;
                  }*/
      }

      /* if there are errors with the root inode here, we need to
       * create a new root inode and get it all setup - of course,
       * everything will be in l+f then, but we *need* a root inode
       * before we can do any of that.
       */
      if(ds.q.block_type != inode_dir) {
            log_err("Block %"PRIu64" marked as root inode in"
                  " superblock not a directory\n", rootblock);
            if(query(sbp, "Create new root inode? (y/n) ")) {
                  if(build_rooti(sbp)) {
                        stack;
                        return -1;
                  }
            } else {
                  log_err("Cannot continue without valid root inode\n");
                  return -1;
            }
      }

      rootblock = sbp->sb.sb_root_di.no_addr;
      pass2_fxns.private = (void *) &ds;
      if(ds.q.bad_block) {
            /* First check that the directory's metatree is valid */
            load_inode(sbp, rootblock, &ip);
            if(check_metatree(ip, &pass2_fxns)) {
                  stack;
                  free_inode(&ip);
                  return -1;
            }
            free_inode(&ip);
      }
      error = check_dir(sbp, rootblock, &pass2_fxns);
      if(error < 0) {
            stack;
            return -1;
      }
      if (error > 0) {
            block_set(sbp->bl, rootblock, meta_inval);
      }

      if(get_and_read_buf(sbp, rootblock, &bh, 0)){
            log_err("Unable to retrieve block #%"PRIu64"\n",
                  rootblock);
            block_set(sbp->bl, rootblock, meta_inval);
            return -1;
      }

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

      if(check_inode_eattr(ip, &pass2_fxns)) {
            stack;
            return -1;
      }
      /* FIXME: Should not have to do this here - fs_dir_add reads
       * the buffer too though, and commits the change to disk, so I
       * have to reread the buffer after calling it if I'm going to
       * make more changes */
      relse_buf(sbp, bh);

      if(!ds.dotdir) {
            log_err("No '.' entry found\n");
            sprintf(tmp_name, ".");
            filename.len = strlen(tmp_name);  /* no trailing NULL */
            if(!(filename.name = malloc(sizeof(char) * filename.len))) {
                  log_err("Unable to allocate name string\n");
                  stack;
                  return -1;
            }
            if(!(memset(filename.name, 0, sizeof(char) * filename.len))) {
                  log_err("Unable to zero name string\n");
                  stack;
                  return -1;
            }
            memcpy(filename.name, tmp_name, filename.len);
            log_warn("Adding '.' entry\n");
            if(fs_dir_add(ip, &filename, &(ip->i_num),
                        ip->i_di.di_type)){
                  log_err("Failed to link \".\" entry to directory.\n");
                  return -1;
            }

            increment_link(ip->i_sbd, ip->i_num.no_addr);
            ds.entry_count++;
            free(filename.name);
            update = 1;
      }
      free_inode(&ip);
      if(get_and_read_buf(sbp, rootblock, &bh, 0)){
            log_err("Unable to retrieve block #%"PRIu64"\n",
                  rootblock);
            block_set(sbp->bl, rootblock, meta_inval);
            return -1;
      }

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

      if(ip->i_di.di_entries != ds.entry_count) {
            log_err("Entries is %d - should be %d for %"PRIu64"\n",
                  ip->i_di.di_entries, ds.entry_count, ip->i_di.di_num.no_addr);
            if(query(sbp, "Fix entries for %"PRIu64"? (y/n) ",
                   ip->i_di.di_num.no_addr)) {
                  ip->i_di.di_entries = ds.entry_count;
                  log_warn("Entries updated\n");
                  update = 1;
            } else {
                  log_err("Entries for %"PRIu64" left out of sync\n",
                        ip->i_di.di_num.no_addr);
            }
      }

      if(update) {
            gfs_dinode_out(&ip->i_di, BH_DATA(bh));
            write_buf(sbp, bh, 0);
      }

      free_inode(&ip);
      relse_buf(sbp, bh);
      return 0;
}

/* What i need to do in this pass is check that the dentries aren't
 * pointing to invalid blocks...and verify the contents of each
 * directory. and start filling in the directory info structure*/

/**
 * pass2 - check pathnames
 *
 * verify root inode
 * directory name length
 * entries in range
 */
int pass2(struct fsck_sb *sbp, struct options *opts)
{
      uint64_t i;
      struct block_query q;
      struct dir_status ds = {0};
      struct fsck_inode *ip;
      osi_buf_t b, *bh = &b;
      osi_filename_t filename;
      char tmp_name[256];
      if(check_root_dir(sbp)) {
            stack;
            return -1;
      }
      int error = 0;

      /* Grab each directory inode, and run checks on it */
      for(i = 0; i < sbp->last_fs_block; i++) {

            /* Skip the root inode - it's checked above */
            if(i == sbp->sb.sb_root_di.no_addr)
                  continue;

            if(block_check(sbp->bl, i, &q)) {
                  log_err("Can't get block %"PRIu64 " from block list\n",
                        i);
                  return -1;
            }

            if(q.block_type != inode_dir)
                  continue;

            log_info("Checking directory inode at block %"PRIu64"\n", i);


            memset(&ds, 0, sizeof(ds));
            pass2_fxns.private = (void *) &ds;
            if(ds.q.bad_block) {
                  /* First check that the directory's metatree
                   * is valid */
                  load_inode(sbp, i, &ip);
                  if(check_metatree(ip, &pass2_fxns)) {
                        stack;
                        free_inode(&ip);
                        return -1;
                  }
                  free_inode(&ip);
            }
            error = check_dir(sbp, i, &pass2_fxns);
            if(error < 0) {
                  stack;
                  return -1;
            }
            if (error > 0) {
                  struct dir_info *di = NULL;
                  error = find_di(sbp, i, &di);
                  if(error < 0) {
                        stack;
                        return -1;
                  }
                  if(error == 0) {
                        /* FIXME: factor */
                        if(query(sbp, "Remove directory entry for bad"
                               " inode %"PRIu64" in %"PRIu64
                               "? (y/n)", i, di->treewalk_parent)) {
                              error = remove_dentry_from_dir(sbp,
                                                       di->treewalk_parent,
                                                       i);
                              if(error < 0) {
                                    stack;
                                    return -1;
                              }
                              if(error > 0) {
                                    log_warn("Unable to find dentry for %"
                                           PRIu64" in %"PRIu64"\n",
                                           i, di->treewalk_parent);
                              }
                              log_warn("Directory entry removed\n");
                        } else {
                              log_err("Directory entry to invalid inode remains\n");
                        }
                  }
                  block_set(sbp->bl, i, meta_inval);
            }
            if(get_and_read_buf(sbp, i, &bh, 0)){
                  /* This shouldn't happen since we were able to
                   * read it before */
                  log_err("Unable to retrieve block #%"PRIu64
                        " for directory\n",
                        i);
                  return -1;
            }

            if(copyin_inode(sbp, bh, &ip)) {
                  stack;
                  relse_buf(sbp, bh);
                  return -1;
            }
            /* FIXME: Should not have to do this here - fs_dir_add reads
             * the buffer too though, and commits the change to disk, so I
             * have to reread the buffer after calling it if I'm going to
             * make more changes */
            relse_buf(sbp, bh);

            if(!ds.dotdir) {
                  log_err("No '.' entry found\n");
                  sprintf(tmp_name, ".");
                  filename.len = strlen(tmp_name);  /* no trailing NULL */
                  if(!(filename.name = malloc(sizeof(char) * filename.len))) {
                        log_err("Unable to allocate name string\n");
                        stack;
                        return -1;
                  }
                  if(!memset(filename.name, 0, sizeof(char) * filename.len)) {
                        log_err("Unable to zero name string\n");
                        stack;
                        return -1;
                  }
                  memcpy(filename.name, tmp_name, filename.len);

                  if(fs_dir_add(ip, &filename, &(ip->i_num),
                              ip->i_di.di_type)){
                        log_err("Failed to link \".\" entry to directory.\n");
                        return -1;
                  }

                  increment_link(ip->i_sbd, ip->i_num.no_addr);
                  ds.entry_count++;
                  free(filename.name);

            }
            free_inode(&ip);

            if(get_and_read_buf(sbp, i, &bh, 0)){
                  log_err("Unable to retrieve block #%"PRIu64"\n",
                        i);
                  block_set(sbp->bl, i, meta_inval);
                  return -1;
            }

            if(copyin_inode(sbp, bh, &ip)) {
                  stack;
                  relse_buf(sbp, bh);
                  return -1;
            }
            if(ip->i_di.di_entries != ds.entry_count) {
                  log_err("Entries is %d - should be %d for %"PRIu64"\n",
                        ip->i_di.di_entries, ds.entry_count,
                        ip->i_di.di_num.no_addr);
                  ip->i_di.di_entries = ds.entry_count;
                  gfs_dinode_out(&ip->i_di, BH_DATA(bh));
                  write_buf(sbp, bh, 0);
            }
            free_inode(&ip);
            relse_buf(sbp, bh);
      }
      return 0;
}




Generated by  Doxygen 1.6.0   Back to index