Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3153415
metawalk.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
17 KB
Referenced Files
None
Subscribers
None
metawalk.c
View Options
/*****************************************************************************
*******************************************************************************
**
** Copyright (C) 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 <inttypes.h>
#include <linux_endian.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "fsck_incore.h"
#include "fsck.h"
#include "bio.h"
#include "fs_dir.h"
#include "inode.h"
#include "util.h"
#include "hash.h"
#include "metawalk.h"
int check_entries(struct fsck_inode *ip, struct buffer_head *bh, int index,
int type, int *update, uint16_t *count,
struct metawalk_fxns *pass)
{
struct gfs2_leaf *leaf = NULL;
struct gfs2_dirent *dent;
struct gfs2_dirent de, *prev;
int error = 0;
char *bh_end;
char *filename;
int first = 1;
bh_end = BH_DATA(bh) + BH_SIZE(bh);
if(type == DIR_LINEAR) {
dent = (struct gfs2_dirent *)(BH_DATA(bh)
+ sizeof(struct gfs2_dinode));
}
else if (type == DIR_EXHASH) {
dent = (struct gfs2_dirent *)(BH_DATA(bh)
+ sizeof(struct gfs2_leaf));
leaf = (struct gfs2_leaf *)BH_DATA(bh);
log_debug("Checking leaf %"PRIu64"\n", BH_BLKNO(bh));
}
else {
log_err("Invalid directory type %d specified\n", type);
return -1;
}
prev = NULL;
if(!pass->check_dentry) {
return 0;
}
while(1) {
memset(&de, 0, sizeof(struct gfs2_dirent));
gfs2_dirent_in(&de, (char *)dent);
filename = (char *)dent + sizeof(struct gfs2_dirent);
if (!de.de_inum.no_addr){
if(first){
log_debug("First dirent is a sentinel (place holder).\n");
first = 0;
} else {
/* FIXME: Do something about this */
log_err("Directory entry with inode number of zero in leaf %"PRIu64" of directory %"PRIu64"!\n", BH_BLKNO(bh), ip->i_di.di_num.no_addr);
return 1;
}
} else {
error = pass->check_dentry(ip, dent, prev, bh,
filename, update,
count,
pass->private);
if(error < 0) {
stack;
return -1;
}
/*if(error > 0) {
return 1;
}*/
}
if ((char *)dent + de.de_rec_len >= bh_end){
log_debug("Last entry processed.\n");
break;
}
/* If we didn't clear the dentry, or if we did, but it
* was the first dentry, set prev */
if(!error || first) {
prev = dent;
}
first = 0;
dent = (struct gfs2_dirent *)((char *)dent + de.de_rec_len);
}
return 0;
}
/* Checks exthash directory entries */
int check_leaf(struct fsck_inode *ip, int *update, struct metawalk_fxns *pass)
{
int error;
struct gfs2_leaf leaf;
uint64_t leaf_no, old_leaf;
struct buffer_head *lbh;
int index;
struct fsck_sb *sbp = ip->i_sbd;
uint16_t count;
int ref_count = 0, exp_count = 0;
old_leaf = 0;
for(index = 0; index < (1 << ip->i_di.di_depth); index++) {
if(get_leaf_nr(ip, index, &leaf_no)) {
log_err("Unable to get leaf block number in dir %"
PRIu64"\n"
"\tDepth = %u\n"
"\tindex = %u\n",
ip->i_num.no_addr,
ip->i_di.di_depth,
index);
return -1;
}
/* GFS has multiple indirect pointers to the same leaf
* until those extra pointers are needed, so skip the
* dups */
if(old_leaf == leaf_no) {
ref_count++;
continue;
} else {
if(ref_count != exp_count){
log_err("Dir #%"PRIu64" has an incorrect number "
"of pointers to leaf #%"PRIu64"\n"
"\tFound: %u, Expected: %u\n",
ip->i_num.no_addr,
old_leaf,
ref_count,
exp_count);
return 1;
}
ref_count = 1;
}
count = 0;
do {
/* FIXME: Do other checks (see old
* pass3:dir_exhash_scan() */
lbh = NULL;
if(pass->check_leaf) {
error = pass->check_leaf(ip, leaf_no, &lbh,
pass->private);
if(error < 0) {
stack;
relse_buf(sbp, lbh);
return -1;
}
if(error > 0) {
relse_buf(sbp, lbh);
lbh = NULL;
return 1;
}
}
if (!lbh){
if(get_and_read_buf(sbp, leaf_no,
&lbh, 0)){
log_err("Unable to read leaf block #%"
PRIu64" for "
"directory #%"PRIu64".\n",
leaf_no,
ip->i_di.di_num.no_addr);
/* FIXME: should i error out
* if this fails? */
break;
}
}
gfs2_leaf_in(&leaf, BH_DATA(lbh));
exp_count = (1 << (ip->i_di.di_depth - leaf.lf_depth));
log_debug("expected count %u - %u %u\n", exp_count,
ip->i_di.di_depth, leaf.lf_depth);
if(pass->check_dentry &&
S_ISDIR(ip->i_di.di_mode)) {
error = check_entries(ip, lbh, index,
DIR_EXHASH, update,
&count,
pass);
/* Since the buffer possibly got
updated directly, release it now,
and grab it again later if we need
it */
relse_buf(sbp, lbh);
if(error < 0) {
stack;
return -1;
}
if(error > 0) {
return 1;
}
if(update && (count != leaf.lf_entries)) {
if(get_and_read_buf(sbp, leaf_no,
&lbh, 0)){
log_err("Unable to read leaf block #%"
PRIu64" for "
"directory #%"PRIu64".\n",
leaf_no,
ip->i_di.di_num.no_addr);
return -1;
}
gfs2_leaf_in(&leaf, BH_DATA(lbh));
log_err("Leaf(%"PRIu64") entry count in directory %"PRIu64" doesn't match number of entries found - is %u, found %u\n", leaf_no, ip->i_num.no_addr, leaf.lf_entries, count);
if(query(sbp, "Update leaf entry count? (y/n) ")) {
leaf.lf_entries = count;
gfs2_leaf_out(&leaf, BH_DATA(lbh));
write_buf(sbp, lbh, 0);
log_warn("Leaf entry count updated\n");
} else {
log_err("Leaf entry count left in inconsistant state\n");
}
relse_buf(sbp, lbh);
}
/* FIXME: Need to get entry count and
* compare it against
* leaf->lf_entries */
break;
} else {
relse_buf(sbp, lbh);
if(!leaf.lf_next) {
break;
}
leaf_no = leaf.lf_next;
log_debug("Leaf chain detected.\n");
}
} while(1);
old_leaf = leaf_no;
}
return 0;
}
static int check_eattr_entries(struct fsck_inode *ip, struct buffer_head *bh,
struct metawalk_fxns *pass)
{
struct gfs2_ea_header *ea_hdr, *ea_hdr_prev = NULL;
uint64_t *ea_data_ptr = NULL;
int i;
int error = 0;
if(!pass->check_eattr_entry) {
return 0;
}
ea_hdr = (struct gfs2_ea_header *)(BH_DATA(bh) +
sizeof(struct gfs2_meta_header));
while(1){
error = pass->check_eattr_entry(ip, bh, ea_hdr, ea_hdr_prev,
pass->private);
if(error < 0) {
stack;
return -1;
}
if(error == 0) {
if(pass->check_eattr_extentry && ea_hdr->ea_num_ptrs) {
ea_data_ptr = ((uint64_t *)((char *)ea_hdr +
sizeof(struct gfs2_ea_header) +
((ea_hdr->ea_name_len + 7) & ~7)));
/* It is possible when a EA is shrunk
** to have ea_num_ptrs be greater than
** the number required for ** data.
** In this case, the EA ** code leaves
** the blocks ** there for **
** reuse........... */
for(i = 0; i < ea_hdr->ea_num_ptrs; i++){
if(pass->check_eattr_extentry(ip,
ea_data_ptr,
bh, ea_hdr,
ea_hdr_prev,
pass->private)) {
stack;
return -1;
}
ea_data_ptr++;
}
}
}
if(ea_hdr->ea_flags & GFS2_EAFLAG_LAST){
/* FIXME: better equal the end of the block */
break;
}
/* FIXME: be sure this doesn't go beyond the end */
ea_hdr_prev = ea_hdr;
ea_hdr = (struct gfs2_ea_header *)
((char *)(ea_hdr) +
gfs2_32_to_cpu(ea_hdr->ea_rec_len));
}
return 0;
}
/**
* check_leaf_eattr
* @ip: the inode the eattr comes from
* @block: block number of the leaf
*
* Returns: 0 on success, -1 if removal is needed
*/
static int check_leaf_eattr(struct fsck_inode *ip, uint64_t block,
uint64_t parent, struct metawalk_fxns *pass)
{
struct buffer_head *bh = NULL;
int error = 0;
log_debug("Checking EA leaf block #%"PRIu64".\n", block);
if(pass->check_eattr_leaf) {
error = pass->check_eattr_leaf(ip, block, parent,
&bh, pass->private);
if(error < 0) {
stack;
return -1;
}
if(error > 0) {
relse_buf(ip->i_sbd, bh);
return 1;
}
}
check_eattr_entries(ip, bh, pass);
relse_buf(ip->i_sbd, bh);
return 0;
}
/**
* check_indirect_eattr
* @ip: the inode the eattr comes from
* @indirect_block
*
* Returns: 0 on success -1 on error
*/
static int check_indirect_eattr(struct fsck_inode *ip, uint64_t indirect,
struct metawalk_fxns *pass){
int error = 0;
uint64_t *ea_leaf_ptr, *end;
uint64_t block;
struct buffer_head *indirect_buf = NULL;
struct fsck_sb *sdp = ip->i_sbd;
log_debug("Checking EA indirect block #%"PRIu64".\n", indirect);
if (!pass->check_eattr_indir ||
!pass->check_eattr_indir(ip, indirect, ip->i_di.di_num.no_addr,
&indirect_buf, pass->private)) {
ea_leaf_ptr = (uint64_t *)(BH_DATA(indirect_buf)
+ sizeof(struct gfs2_meta_header));
end = ea_leaf_ptr
+ ((sdp->sb.sb_bsize
- sizeof(struct gfs2_meta_header)) / 8);
while(*ea_leaf_ptr && (ea_leaf_ptr < end)){
block = gfs2_64_to_cpu(*ea_leaf_ptr);
/* FIXME: should I call check_leaf_eattr if we
* find a dup? */
error = check_leaf_eattr(ip, block, indirect, pass);
ea_leaf_ptr++;
}
}
relse_buf(sdp, indirect_buf);
return error;
}
/**
* check_inode_eattr - check the EA's for a single inode
* @ip: the inode whose EA to check
*
* Returns: 0 on success, -1 on error
*/
int check_inode_eattr(struct fsck_inode *ip, struct metawalk_fxns *pass)
{
int error = 0;
if(!ip->i_di.di_eattr){
return 0;
}
log_debug("Extended attributes exist for inode #%"PRIu64".\n",
ip->i_num.no_formal_ino);
if(ip->i_di.di_flags & GFS2_DIF_EA_INDIRECT){
if((error = check_indirect_eattr(ip, ip->i_di.di_eattr, pass)))
stack;
} else {
if((error = check_leaf_eattr(ip, ip->i_di.di_eattr,
ip->i_di.di_num.no_addr, pass)))
stack;
}
return error;
}
/**
* build_metalist
* @ip:
* @mlp:
*
*/
static int build_metalist(struct fsck_inode *ip, osi_list_t *mlp,
struct metawalk_fxns *pass)
{
uint32_t height = ip->i_di.di_height;
struct buffer_head *bh, *nbh;
osi_list_t *prev_list, *cur_list, *tmp;
int i, head_size;
uint64_t *ptr, block;
int err;
if(get_and_read_buf(ip->i_sbd, ip->i_di.di_num.no_addr, &bh, 0)) {
stack;
return -1;
}
osi_list_add(&bh->b_list, &mlp[0]);
/* if(<there are no indirect blocks to check>) */
if (height < 2) {
return 0;
}
for (i = 1; i < height; i++){
prev_list = &mlp[i - 1];
cur_list = &mlp[i];
for (tmp = prev_list->next; tmp != prev_list; tmp = tmp->next){
bh = osi_list_entry(tmp, struct buffer_head, b_list);
head_size = (i > 1 ?
sizeof(struct gfs2_meta_header) :
sizeof(struct gfs2_dinode));
for (ptr = (uint64_t *)(bh->b_data + head_size);
(char *)ptr < (bh->b_data + bh->b_size);
ptr++) {
nbh = NULL;
if (!*ptr)
continue;
block = gfs2_64_to_cpu(*ptr);
err = pass->check_metalist(ip, block, &nbh,
pass->private);
if(err < 0) {
stack;
goto fail;
}
if(err > 0) {
log_debug("Skipping block %"PRIu64
"\n", block);
continue;
}
if(!nbh) {
if(get_and_read_buf(ip->i_sbd, block,
&nbh, 0)) {
stack;
goto fail;
}
}
osi_list_add(&nbh->b_list, cur_list);
}
}
}
return 0;
fail:
for (i = 0; i < GFS2_MAX_META_HEIGHT; i++)
{
osi_list_t *list;
list = &mlp[i];
while (!osi_list_empty(list))
{
bh = osi_list_entry(list->next, struct buffer_head, b_list);
osi_list_del(&bh->b_list);
relse_buf(ip->i_sbd, bh);
}
}
return -1;
}
/**
* check_metatree
* @ip:
* @rgd:
*
*/
int check_metatree(struct fsck_inode *ip, struct metawalk_fxns *pass)
{
osi_list_t metalist[GFS2_MAX_META_HEIGHT];
osi_list_t *list, *tmp;
struct buffer_head *bh;
uint64_t block, *ptr;
uint32_t height = ip->i_di.di_height;
int i, head_size;
int update = 0;
int error = 0;
if (!height)
goto end;
for (i = 0; i < GFS2_MAX_META_HEIGHT; i++)
osi_list_init(&metalist[i]);
/* create metalist for each level */
if(build_metalist(ip, &metalist[0], pass)){
stack;
return -1;
}
/* We don't need to record directory blocks - they will be
* recorded later...i think... */
if (S_ISDIR(ip->i_di.di_mode)) {
log_debug("Directory with height > 0 at %"PRIu64"\n",
ip->i_di.di_num.no_addr);
}
/* check data blocks */
list = &metalist[height - 1];
for (tmp = list->next; tmp != list; tmp = tmp->next)
{
bh = osi_list_entry(tmp, struct buffer_head, b_list);
head_size = (height != 1 ? sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_dinode));
ptr = (uint64_t *)(bh->b_data + head_size);
for ( ; (char *)ptr < (bh->b_data + bh->b_size); ptr++)
{
if (!*ptr)
continue;
block = gfs2_64_to_cpu(*ptr);
if(pass->check_data &&
(pass->check_data(ip, block, pass->private) < 0)) {
stack;
return -1;
}
}
}
/* free metalists */
for (i = 0; i < GFS2_MAX_META_HEIGHT; i++)
{
list = &metalist[i];
while (!osi_list_empty(list))
{
bh = osi_list_entry(list->next, struct buffer_head, b_list);
osi_list_del(&bh->b_list);
relse_buf(ip->i_sbd, bh);
}
}
end:
if (S_ISDIR(ip->i_di.di_mode)) {
/* check validity of leaf blocks and leaf chains */
if (ip->i_di.di_flags & GFS2_DIF_EXHASH) {
error = check_leaf(ip, &update, pass);
if(error < 0)
return -1;
if(error > 0)
return 1;
}
}
return 0;
}
/* Checks stuffed inode directories */
int check_linear_dir(struct fsck_inode *ip, struct buffer_head *bh, int *update,
struct metawalk_fxns *pass)
{
int error = 0;
uint16_t count = 0;
error = check_entries(ip, bh, 0, DIR_LINEAR, update, &count, pass);
if(error < 0) {
stack;
return -1;
}
return error;
}
int check_dir(struct fsck_sb *sbp, uint64_t block, struct metawalk_fxns *pass)
{
struct buffer_head *bh;
struct fsck_inode *ip;
int update = 0;
int error = 0;
if(get_and_read_buf(sbp, block, &bh, 0)){
log_err("Unable to retrieve block #%"PRIu64"\n",
block);
block_set(sbp->bl, block, meta_inval);
return -1;
}
if(copyin_inode(sbp, bh, &ip)) {
stack;
relse_buf(sbp, bh);
return -1;
}
if(ip->i_di.di_flags & GFS2_DIF_EXHASH) {
error = check_leaf(ip, &update, pass);
if(error < 0) {
stack;
free_inode(&ip);
relse_buf(sbp, bh);
return -1;
}
}
else {
error = check_linear_dir(ip, bh, &update, pass);
if(error < 0) {
stack;
free_inode(&ip);
relse_buf(sbp, bh);
return -1;
}
}
free_inode(&ip);
relse_buf(sbp, bh);
return error;
}
static int remove_dentry(struct fsck_inode *ip, struct gfs2_dirent *dent,
struct gfs2_dirent *prev_de,
struct buffer_head *bh, char *filename, int *update,
uint16_t *count,
void *private)
{
/* the metawalk_fxn's private field must be set to the dentry
* block we want to clear */
uint64_t *dentryblock = (uint64_t *) private;
struct gfs2_dirent dentry, *de;
memset(&dentry, 0, sizeof(struct gfs2_dirent));
gfs2_dirent_in(&dentry, (char *)dent);
de = &dentry;
if(de->de_inum.no_addr == *dentryblock) {
*update = 1;
if(dirent_del(ip, bh, prev_de, dent)) {
stack;
return -1;
}
}
else {
(*count)++;
*update = 1;
}
return 0;
}
int remove_dentry_from_dir(struct fsck_sb *sbp, uint64_t dir,
uint64_t dentryblock)
{
struct metawalk_fxns remove_dentry_fxns = {0};
struct block_query q;
int error;
log_debug("Removing dentry %"PRIu64" from directory %"PRIu64"\n",
dentryblock, dir);
if(check_range(sbp, dir)) {
log_err("Parent directory out of range\n");
return 1;
}
remove_dentry_fxns.private = &dentryblock;
remove_dentry_fxns.check_dentry = remove_dentry;
if(block_check(sbp->bl, dir, &q)) {
stack;
return -1;
}
if(q.block_type != inode_dir) {
log_info("Parent block is not a directory...ignoring\n");
return 1;
}
/* Need to run check_dir with a private var of dentryblock,
* and fxns that remove that dentry if found */
error = check_dir(sbp, dir, &remove_dentry_fxns);
return error;
}
/* FIXME: These should be merged with the hash routines in inode_hash.c */
static uint32_t dinode_hash(uint64_t block_no)
{
unsigned int h;
h = fsck_hash(&block_no, sizeof (uint64_t));
h &= FSCK_HASH_MASK;
return h;
}
int find_di(struct fsck_sb *sbp, uint64_t childblock, struct dir_info **dip)
{
osi_list_t *bucket = &sbp->dir_hash[dinode_hash(childblock)];
osi_list_t *tmp;
struct dir_info *di = NULL;
osi_list_foreach(tmp, bucket) {
di = osi_list_entry(tmp, struct dir_info, list);
if(di->dinode == childblock) {
*dip = di;
return 0;
}
}
*dip = NULL;
return -1;
}
int dinode_hash_insert(osi_list_t *buckets, uint64_t key, struct dir_info *di)
{
osi_list_t *tmp;
osi_list_t *bucket = &buckets[dinode_hash(key)];
struct dir_info *dtmp = NULL;
if(osi_list_empty(bucket)) {
osi_list_add(&di->list, bucket);
return 0;
}
osi_list_foreach(tmp, bucket) {
dtmp = osi_list_entry(tmp, struct dir_info, list);
if(dtmp->dinode < key) {
continue;
}
else {
osi_list_add_prev(&di->list, tmp);
return 0;
}
}
osi_list_add_prev(&di->list, bucket);
return 0;
}
int dinode_hash_remove(osi_list_t *buckets, uint64_t key)
{
osi_list_t *tmp;
osi_list_t *bucket = &buckets[dinode_hash(key)];
struct dir_info *dtmp = NULL;
if(osi_list_empty(bucket)) {
return -1;
}
osi_list_foreach(tmp, bucket) {
dtmp = osi_list_entry(tmp, struct dir_info, list);
if(dtmp->dinode == key) {
osi_list_del(tmp);
return 0;
}
}
return -1;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Tue, Feb 25, 6:40 PM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1464931
Default Alt Text
metawalk.c (17 KB)
Attached To
Mode
rF Fence Agents
Attached
Detach File
Event Timeline
Log In to Comment