/* compose directory item containing "." and ".." entries */ void make_empty_dir_item (char * body, __u32 dirid, __u32 objid, __u32 par_dirid, __u32 par_objid) { struct reiserfs_de_head * deh; memset (body, 0, EMPTY_DIR_SIZE); deh = (struct reiserfs_de_head *)body; /* direntry header of "." */ put_deh_offset( &(deh[0]), DOT_OFFSET ); /* these two are from make_le_item_head, and are are LE */ deh[0].deh_dir_id = dirid; deh[0].deh_objectid = objid; deh[0].deh_state = 0; /* Endian safe if 0 */ put_deh_location( &(deh[0]), EMPTY_DIR_SIZE - ROUND_UP( strlen( "." ) ) ); mark_de_visible(&(deh[0])); /* direntry header of ".." */ put_deh_offset( &(deh[1]), DOT_DOT_OFFSET ); /* key of ".." for the root directory */ /* these two are from the inode, and are are LE */ deh[1].deh_dir_id = par_dirid; deh[1].deh_objectid = par_objid; deh[1].deh_state = 0; /* Endian safe if 0 */ put_deh_location( &(deh[1]), deh_location( &(deh[0])) - ROUND_UP( strlen( ".." ) ) ); mark_de_visible(&(deh[1])); /* copy ".." and "." */ memcpy (body + deh_location( &(deh[0]) ), ".", 1); memcpy (body + deh_location( &(deh[1]) ), "..", 2); }
void make_empty_dir_item(char *body, __le32 dirid, __le32 objid, __le32 par_dirid, __le32 par_objid) { struct reiserfs_de_head *deh; memset(body, 0, EMPTY_DIR_SIZE); deh = (struct reiserfs_de_head *)body; put_deh_offset(&(deh[0]), DOT_OFFSET); deh[0].deh_dir_id = dirid; deh[0].deh_objectid = objid; deh[0].deh_state = 0; put_deh_location(&(deh[0]), EMPTY_DIR_SIZE - ROUND_UP(strlen("."))); mark_de_visible(&(deh[0])); put_deh_offset(&(deh[1]), DOT_DOT_OFFSET); deh[1].deh_dir_id = par_dirid; deh[1].deh_objectid = par_objid; deh[1].deh_state = 0; put_deh_location(&(deh[1]), deh_location(&(deh[0])) - ROUND_UP(strlen(".."))); mark_de_visible(&(deh[1])); memcpy(body + deh_location(&(deh[0])), ".", 1); memcpy(body + deh_location(&(deh[1])), "..", 2); }
/* * compose directory item containing "." and ".." entries (entries are * not aligned to 4 byte boundary) */ void make_empty_dir_item_v1(char *body, __le32 dirid, __le32 objid, __le32 par_dirid, __le32 par_objid) { struct reiserfs_de_head *dot, *dotdot; memset(body, 0, EMPTY_DIR_SIZE_V1); dot = (struct reiserfs_de_head *)body; dotdot = dot + 1; /* direntry header of "." */ put_deh_offset(dot, DOT_OFFSET); /* these two are from make_le_item_head, and are are LE */ dot->deh_dir_id = dirid; dot->deh_objectid = objid; dot->deh_state = 0; /* Endian safe if 0 */ put_deh_location(dot, EMPTY_DIR_SIZE_V1 - strlen(".")); mark_de_visible(dot); /* direntry header of ".." */ put_deh_offset(dotdot, DOT_DOT_OFFSET); /* key of ".." for the root directory */ /* these two are from the inode, and are are LE */ dotdot->deh_dir_id = par_dirid; dotdot->deh_objectid = par_objid; dotdot->deh_state = 0; /* Endian safe if 0 */ put_deh_location(dotdot, deh_location(dot) - strlen("..")); mark_de_visible(dotdot); /* copy ".." and "." */ memcpy(body + deh_location(dot), ".", 1); memcpy(body + deh_location(dotdot), "..", 2); }
static void sprintf_de_head( char *buf, struct reiserfs_de_head *deh ) { if( deh ) sprintf( buf, "[offset=%d dir_id=%d objectid=%d location=%d state=%04x]", deh_offset(deh), deh_dir_id(deh), deh_objectid(deh), deh_location(deh), deh_state(deh) ); else sprintf( buf, "[NULL]" ); }
// de_bh, de_ih, de_deh (points to first element of array), de_item_num is set inline void set_de_name_and_namelen(struct reiserfs_dir_entry *de) { struct reiserfs_de_head *deh = de->de_deh + de->de_entry_num; BUG_ON(de->de_entry_num >= ih_entry_count(de->de_ih)); de->de_entrylen = entry_length(de->de_bh, de->de_ih, de->de_entry_num); de->de_namelen = de->de_entrylen - (de_with_sd(deh) ? SD_SIZE : 0); de->de_name = B_I_PITEM(de->de_bh, de->de_ih) + deh_location(deh); if (de->de_name[de->de_namelen - 1] == 0) de->de_namelen = strlen(de->de_name); }
void print_directory_item (FILE * fp, reiserfs_filsys_t fs, struct buffer_head * bh, struct item_head * ih) { int i; int namelen; struct reiserfs_de_head * deh; char * name; /* static char namebuf [80];*/ if (!I_IS_DIRECTORY_ITEM (ih)) return; //printk ("\n%2%-25s%-30s%-15s%-15s%-15s\n", " Name", "length", "Object key", "Hash", "Gen number", "Status"); reiserfs_warning (fp, "%3s: %-25s%s%-22s%-12s%s\n", "###", "Name", "length", " Object key", " Hash", "Gen number"); deh = B_I_DEH (bh, ih); for (i = 0; i < ih_entry_count (ih); i ++, deh ++) { if (dir_entry_bad_location (deh, ih, i == 0 ? 1 : 0)) { reiserfs_warning (fp, "%3d: wrong entry location %u, deh_offset %u\n", i, deh_location (deh), deh_offset (deh)); continue; } if (i && dir_entry_bad_location (deh - 1, ih, ((i - 1) == 0) ? 1 : 0)) /* previous entry has bad location so we can not calculate entry length */ namelen = 25; else namelen = name_length (ih, deh, i); name = name_in_entry (deh, i); reiserfs_warning (fp, "%3d: \"%-25.*s\"(%3d)%20K%12d%5d, loc %u, state %x %s\n", i, namelen, name, namelen, /* this gets converted in print_short_key() */ (struct key *)&(deh->deh_dir_id), GET_HASH_VALUE (deh_offset(deh)), GET_GENERATION_NUMBER (deh_offset(deh)), deh_location (deh), deh_state(deh), fs ? (is_properly_hashed (fs, name, namelen, deh_offset (deh)) ? "" : "(BROKEN)") : "??"); } }
/* * de_bh, de_ih, de_deh (points to first element of array), de_item_num * is set */ void set_de_name_and_namelen(struct reiserfs_dir_entry *de) { struct reiserfs_de_head *deh = de->de_deh + de->de_entry_num; if (de->de_entry_num >= ih_entry_count(de->de_ih)) { reiserfs_log(LOG_DEBUG, "BUG\n"); return; } de->de_entrylen = entry_length(de->de_bp, de->de_ih, de->de_entry_num); de->de_namelen = de->de_entrylen - (de_with_sd(deh) ? SD_SIZE : 0); de->de_name = B_I_PITEM(de->de_bp, de->de_ih) + deh_location(deh); if (de->de_name[de->de_namelen - 1] == 0) de->de_namelen = strlen(de->de_name); }
/* FIXME: we can improve fixing of broken keys: we can ssfe direct items which go after stat data and have broken keys */ static void pass0_correct_leaf (reiserfs_filsys_t fs, struct buffer_head * bh) { int i, j; struct item_head * ih; __u32 * ind_item; unsigned long unfm_ptr; int dirty = 0; start_again: ih = B_N_PITEM_HEAD (bh, 0); for (i = 0; i < node_item_number (bh); i ++, ih ++) { if (ih->ih_key.k_dir_id == 0 || ih->ih_key.k_objectid == 0) { /* sometimes stat datas get k_objectid==0 or k_dir_id==0 */ if (i == (node_item_number (bh) - 1)) { /* */ if (i == 0) { fsck_log ("block %lu: item %d: (%H) is alone in the block\n", bh->b_blocknr, i, ih); return; } /* delete last item */ delete_item (fs, bh, i - 1); return; } /* there is next item: if it is not stat data - take its k_dir_id and k_objectid. if key order will be still wrong - the changed item will be deleted */ if (!is_stat_data_ih (ih + 1)) { fsck_log ("block %lu: item %d: (%H) fixed to ", bh->b_blocknr, i, ih); ih->ih_key.k_dir_id = (ih + 1)->ih_key.k_dir_id; ih->ih_key.k_objectid = (ih + 1)->ih_key.k_objectid; set_offset (KEY_FORMAT_1, &ih->ih_key, 0); set_type (KEY_FORMAT_1, &ih->ih_key, TYPE_STAT_DATA); fsck_log ("(%H)\n", ih); dirty = 1; } else if (i == 0) { delete_item (fs, bh, i); goto start_again; } } /* this recovers corruptions like the below: 1774 1732 0 0 116262638 1732 1 3 1774 1736 0 0 */ if (i && is_stat_data_ih (ih - 1) && !is_stat_data_ih (ih)) { if (ih->ih_key.k_objectid != (ih - 1)->ih_key.k_objectid || ih->ih_key.k_dir_id != (ih - 1)->ih_key.k_dir_id || get_offset (&ih->ih_key) != 1) { if (is_direntry_ih (ih)) { fsck_log ("block %lu: item %d: no \".\" entry found in " "the first item of a directory\n", bh->b_blocknr, i); } else { fsck_log ("block %lu: item %d: (%H) fixed to ", bh->b_blocknr, i, ih); ih->ih_key.k_dir_id = (ih - 1)->ih_key.k_dir_id; ih->ih_key.k_objectid = (ih - 1)->ih_key.k_objectid; if (ih_item_len (ih - 1) == SD_SIZE) { /* stat data is new, therefore this item is new too */ set_offset (KEY_FORMAT_2, &(ih->ih_key), 1); if (ih_entry_count (ih) != 0xffff) set_type (KEY_FORMAT_2, &(ih->ih_key), TYPE_INDIRECT); else set_type (KEY_FORMAT_2, &(ih->ih_key), TYPE_DIRECT); set_ih_key_format (ih, KEY_FORMAT_2); } else { /* stat data is old, therefore this item is old too */ set_offset (KEY_FORMAT_1, &(ih->ih_key), 1); if (ih_entry_count (ih) != 0xffff) set_type (KEY_FORMAT_1, &(ih->ih_key), TYPE_INDIRECT); else set_type (KEY_FORMAT_1, &(ih->ih_key), TYPE_DIRECT); set_ih_key_format (ih, KEY_FORMAT_1); } fsck_log ("%H\n", ih); dirty = 1; } } } /* FIXME: corruptions like: 56702 66802 1 2 56702 65536 0 0 56702 66803 1 2 do not get recovered (both last items will be deleted) */ /* delete item if it is not in correct order of object items */ if (i && not_of_one_file (&ih->ih_key, &(ih - 1)->ih_key) && !is_stat_data_ih (ih)) { fsck_log ("block %lu: item %d: %H follows non stat item %H - deleted\n", bh->b_blocknr, i, ih, ih - 1); delete_item (fs, bh, i); goto start_again; } if (i && comp_keys (&(ih - 1)->ih_key, &ih->ih_key) != -1) { /* previous item has key not smaller than the key of currect item */ if (is_stat_data_ih (ih - 1) && !is_stat_data_ih (ih)) { /* fix key of stat data such as if it was stat data of that item */ fsck_log ("pass0: block %lu: %d-th item %k is out of order, made a stat data of %d-th (%k)\n", bh->b_blocknr, i - 1, &(ih - 1)->ih_key, i, &ih->ih_key); (ih - 1)->ih_key.k_dir_id = ih->ih_key.k_dir_id; (ih - 1)->ih_key.k_objectid = ih->ih_key.k_objectid; set_offset (KEY_FORMAT_1, &(ih - 1)->ih_key, 0); set_type (KEY_FORMAT_1, &(ih - 1)->ih_key, TYPE_STAT_DATA); dirty = 1; } else { /* ok, we have to delete one of these two - decide which one */ int retval; /* something will be deleted */ dirty = 1; retval = upper_correct (bh, ih - 1, i - 1); switch (retval) { case 0: /* delete upper item */ fsck_log ("pass0: block %lu: %d-th (upper) item (%k) is out of order, deleted\n", bh->b_blocknr, i - 1, &(ih - 1)->ih_key); delete_item (fs, bh, i - 1); goto start_again; case 1: /* delete lower item */ fsck_log ("pass0: block %lu: %d-th (lower) item (%k) is out of order, deleted\n", bh->b_blocknr, i, &ih->ih_key); delete_item (fs, bh, i); goto start_again; default: /* upper item was the first item of a node */ } retval = lower_correct (bh, ih, i); switch (retval) { case 0: /* delete lower item */ fsck_log ("pass0: block %lu: %d-th (lower) item (%k) is out of order, deleted\n", bh->b_blocknr, i, &ih->ih_key); delete_item (fs, bh, i); goto start_again; case 1: /* delete upper item */ fsck_log ("pass0: block %lu: %d-th (upper) item (%k) is out of order, deleted\n", bh->b_blocknr, i - 1, &(ih - 1)->ih_key); delete_item (fs, bh, i - 1); goto start_again; default: /* there wer only two items in a node, so we could not decide what to delete, go and ask user */ } fsck_log ("pass0: which of these items looks better (other will be deleted)?\n" "%H\n%H\n", ih - 1, ih); if (fsck_user_confirmed (fs, "1 or 2?", "1\n", 1)) delete_item (fs, bh, i - 1); else delete_item (fs, bh, i); goto start_again; } } if (is_stat_data_ih (ih) && (ih_item_len (ih) != SD_SIZE && ih_item_len (ih) != SD_V1_SIZE)) { fsck_log ("pass0: block %lu, stat data of wrong length %H - deleted\n", bh, ih); delete_item (fs, bh, i); goto start_again; } dirty += correct_key_format (ih); if (is_stat_data_ih (ih)) { ;/*correct_stat_data (fs, bh, i);*/ } if (is_direntry_ih (ih)) { verify_directory_item (fs, bh, i); continue; } if (!is_indirect_ih (ih)) continue; ind_item = (__u32 *)B_I_PITEM (bh, ih); for (j = 0; j < I_UNFM_NUM (ih); j ++) { unfm_ptr = le32_to_cpu (ind_item [j]); if (!unfm_ptr) continue; if (fsck_mode (fs) == FSCK_ZERO_FILES) { /* FIXME: this is temporary mode of fsck */ ind_item [j] = 0; reiserfs_bitmap_clear_bit (fsck_new_bitmap(fs), unfm_ptr); tmp_zeroed ++; dirty = 1; continue; } if (not_data_block (fs, unfm_ptr) || /* journal area or bitmap or super block */ unfm_ptr >= SB_BLOCK_COUNT (fs)) {/* garbage in pointer */ stats (fs)->wrong_pointers ++; /* fsck_log ("pass0: %d-th pointer (%lu) in item %k (leaf block %lu) is wrong\n", j, unfm_ptr, &ih->ih_key, bh->b_blocknr); */ ind_item [j] = 0; dirty = 1; continue; } #if 0 if (!was_block_used (unfm_ptr)) { /* this will get to a pool of allocable blocks */ ind_item [j] = 0; dirty = 1; stat_wrong_pointer_found (fs); continue; } #endif /* mark block in bitmaps of unformatted nodes */ register_unfm (unfm_ptr); } } /* mark all objectids in use */ ih = B_N_PITEM_HEAD (bh, 0); for (i = 0; i < node_item_number (bh); i ++, ih ++) { mark_objectid_really_used (proper_id_map (fs), le32_to_cpu (ih->ih_key.k_dir_id)); mark_objectid_really_used (proper_id_map (fs), le32_to_cpu (ih->ih_key.k_objectid)); } if (node_item_number (bh) < 1) { /* pass 1 will skip this */ stats(fs)->all_contents_removed ++; fsck_log ("pass0: block %lu got all items deleted\n", bh->b_blocknr); } else { /* pass1 will use this bitmap */ pass0_mark_leaf (bh->b_blocknr); } if (dirty) { stats(fs)->leaves_corrected ++; mark_buffer_dirty (bh); } } static int is_bad_sd (struct item_head * ih, char * item) { struct stat_data * sd = (struct stat_data *)item; if (le32_to_cpu(ih->ih_key.u.k_offset_v1.k_offset) || le32_to_cpu(ih->ih_key.u.k_offset_v1.k_uniqueness)) { reiserfs_warning (stderr, "Bad SD? %H\n", ih); return 1; } if (ih_item_len (ih) == SD_V1_SIZE) { /* looks like old stat data */ if (ih_key_format (ih) != KEY_FORMAT_1) fsck_log ("item %H has wrong format\n", ih); return 0; } if (!S_ISDIR (sd_v2_mode(sd)) && !S_ISREG(sd_v2_mode(sd)) && !S_ISCHR (sd_v2_mode(sd)) && !S_ISBLK(sd_v2_mode(sd)) && !S_ISLNK (sd_v2_mode(sd)) && !S_ISFIFO(sd_v2_mode(sd)) && !S_ISSOCK(sd_v2_mode(sd))) { /*fsck_log ("file %k unexpected mode encountered 0%o\n", &ih->ih_key, sd_v2_mode(sd))*/; } return 0; } int is_bad_directory (struct item_head * ih, char * item, int dev, int blocksize) { int i; char * name; int namelen, entrylen; struct reiserfs_de_head * deh = (struct reiserfs_de_head *)item; __u32 prev_offset = 0; __u16 prev_location = ih_item_len (ih); int min_entry_size = 1;/* we have no way to understand whether the filesystem were created in 3.6 format or converted to it. So, we assume that minimal name length is 1 */ if (ih_item_len (ih) / (DEH_SIZE + min_entry_size) < ih_entry_count (ih)) /* entry count is too big */ return 1; for (i = 0; i < ih_entry_count (ih); i ++, deh ++) { entrylen = entry_length(ih, deh, i); if (entrylen > REISERFS_MAX_NAME_LEN (blocksize)) { return 1; } if (deh_offset (deh) <= prev_offset) { return 1; } prev_offset = deh_offset (deh); if (deh_location(deh) + entrylen != prev_location) { return 1; } prev_location = deh_location (deh); namelen = name_length (ih, deh, i); name = name_in_entry (deh, i); if (!is_properly_hashed (fs, name, namelen, deh_offset (deh))) { return 1; } } return 0; } /* change incorrect block adresses by 0. Do not consider such item as incorrect */ static int is_bad_indirect (struct item_head * ih, char * item, int dev, int blocksize) { int i; int bad = 0; int blocks; if (ih_item_len(ih) % UNFM_P_SIZE) { fsck_log ("is_bad_indirect: indirect item of %H of invalid length\n", ih); return 1; } blocks = SB_BLOCK_COUNT (fs); for (i = 0; i < I_UNFM_NUM (ih); i ++) { __u32 * ind = (__u32 *)item; if (le32_to_cpu (ind[i]) >= blocks) { bad ++; fsck_log ("is_bad_indirect: %d-th pointer of item %H looks bad (%lu)\n", i, ih, le32_to_cpu (ind [i])); continue; } } return bad; }
/* check directory item and try to recover something */ static int verify_directory_item (reiserfs_filsys_t fs, struct buffer_head * bh, int item_num) { struct item_head * ih; struct item_head tmp; char * item; struct reiserfs_de_head * deh; char * name; int name_len; int bad; int i, j; #if 0 int bad_entries; /* how many bad neighboring entries */ int total_entry_len; char * entries, * end; #endif int dirty; int entry_count; int hash_code; int bad_locations; #ifdef DEBUG_VERIFY_DENTRY char * direntries; #endif ih = B_N_PITEM_HEAD (bh, item_num); item = B_I_PITEM (bh,ih); deh = (struct reiserfs_de_head *)item; dirty = 0; bad_locations = 0; entry_count = ih_entry_count (ih); /* check deh_location */ for (i = 0; i < ih_entry_count (ih); i ++) { /* silently fix deh_state */ if (deh [i].deh_state != (1 << DEH_Visible)) { deh [i].deh_state = cpu_to_le16 (1 << DEH_Visible); mark_buffer_dirty (bh); } if (dir_entry_bad_location (deh + i, ih, !i)) mark_de_bad_location (deh + i); } #ifdef DEBUG_VERIFY_DENTRY direntries = getmem (ih_entry_count (ih) * sizeof (int)); printf ("entries with bad locations: "); for (i = 0; i < ih_entry_count (ih); i ++) { if (de_bad_location (deh + i)) printf ("%d ", i); } printf ("\n"); #endif /* DEBUG_VERIFY_DENTRY */ /* find entries names in which have mismatching deh_offset */ for (i = ih_entry_count (ih) - 1; i >= 0; i --) { if (de_bad (deh + i)) /* bad location */ continue; if (i) { if (deh_location (deh + i - 1) < deh_location (deh + i)) mark_de_bad_location (deh + i - 1); } name = name_in_entry (deh + i, i); /* we found a name, but we not always we can get its length as it depends on deh_location of previous entry */ name_len = try_to_get_name_length (ih, deh + i, i); #ifdef DEBUG_VERIFY_DENTRY if (name_len == 0) printf ("trying to find name length for %d-th entry\n", i); #endif /* DEBUG_VERIFY_DENTRY */ if (is_dot (name, name_len)) { if (i != 0) fsck_log ("block %lu: item %d: \".\" is %d-th entry\n", bh->b_blocknr, item_num, i); /* check and fix "." */ if (deh_offset (deh + i) != DOT_OFFSET) { set_deh_offset(&(deh[i]), DOT_OFFSET); mark_buffer_dirty (bh); } /* "." must point to the directory it is in */ if (not_of_one_file (&(deh[i].deh_dir_id), &(ih->ih_key))) { /* endian safe */ fsck_log ("verify_direntry: block %lu, item %H has entry \".\" " "pointing to (%K) instead of (%K)\n", bh->b_blocknr, ih, &(deh[i].deh_dir_id), &(ih->ih_key)); deh[i].deh_dir_id = ih->ih_key.k_dir_id; /* both LE */ deh[i].deh_objectid = ih->ih_key.k_objectid; /* both LE */ mark_buffer_dirty (bh); } } else if (is_dot_dot (name, name_len)) { if (i != 1) fsck_log ("block %lu: item %d: \"..\" is %d-th entry\n", bh->b_blocknr, item_num, i); /* check and fix ".." */ if (deh_offset (deh + i) != DOT_DOT_OFFSET) set_deh_offset(&(deh[i]), DOT_DOT_OFFSET); } else { int min_length, max_length; /* check other name */ if (name_len == 0) { /* we do not know the length of name - we will try to find it */ min_length = 1; max_length = item + ih_item_len (ih) - name; } else /* we kow name length, so we will try only one name length */ min_length = max_length = name_len; for (j = min_length; j <= max_length; j ++) { hash_code = find_hash_in_use (name, j, GET_HASH_VALUE (deh_offset (deh + i)), rs_hash (fs->s_rs)); add_hash_hit (fs, hash_code); if (code2func (hash_code) != 0) { /* deh_offset matches to some hash of the name */ if (!name_len) { fsck_log ("pass0: block %lu, item %H: entry %d. found a name \"%.*s\" " "matching to deh_offset %u. FIXME: should set deh_location " "of previous entry (not ready)\n", bh->b_blocknr, ih, i, j, name, deh_offset (deh + i)); /* FIXME: if next byte is 0 we think that the name is aligned to 8 byte boundary */ if (i) { set_deh_location( &(deh[i - 1]), deh_location (deh + i) + ((name[j] || SB_VERSION (fs) == REISERFS_VERSION_1) ? j : ROUND_UP (j))); mark_de_good_location (deh + i - 1); mark_buffer_dirty (bh); } } break; } } if (j == max_length + 1) { /* deh_offset does not match to anything. it will be deleted for now, but maybe we could just fix a deh_offset if it is in ordeer */ mark_de_bad_offset (deh + i); } } } /* for */ #if 0 /* find entries names in which have mismatching deh_offset */ for (i = 0; i < ih_entry_count (ih); i ++) { if (de_bad (deh + i)) /* bad location */ continue; name = name_in_entry (deh + i, i); /* we found a name, but we not always we can get its length as it depends on deh_location of previous entry */ name_len = try_to_get_name_length (ih, deh + i, i); if (i == 0 && is_dot (name, name_len)) { /* check and fix "." */ if (deh_offset (deh + i) != DOT_OFFSET) { deh[i].deh_offset = cpu_to_le32 (DOT_OFFSET); } /* "." must point to the directory it is in */ if (not_of_one_file (&(deh[i].deh_dir_id), &(ih->ih_key))) { fsck_log ("verify_direntry: block %lu, item %H has entry \".\" " "pointing to (%K) instead of (%K)\n", bh->b_blocknr, ih, &(deh[i].deh_dir_id), &(ih->ih_key)); deh[i].deh_dir_id = ih->ih_key.k_dir_id; /* both 32 bit LE */ deh[i].deh_objectid = ih->ih_key.k_objectid; /* both 32 bit LE */ mark_buffer_dirty (bh); } } else if (i == 1 && is_dot_dot (name, name_len)) { /* check and fix ".." */ if (deh_offset (deh + i) != DOT_DOT_OFFSET) deh[i].deh_offset = cpu_to_le32 (DOT_DOT_OFFSET); } else { int min_length, max_length; /* check other name */ if (name_len == 0) { /* we do not know the length of name - we will try to find it */ min_length = 1; max_length = item + ih_item_len (ih) - name; } else /* we kow name length, so we will try only one name length */ min_length = max_length = name_len; for (j = min_length; j <= max_length; j ++) { hash_code = find_hash_in_use (name, j, GET_HASH_VALUE (deh_offset (deh + i)), rs_hash (fs->s_rs)); add_hash_hit (fs, hash_code); if (code2func (hash_code) != 0) { /* deh_offset matches to some hash of the name */ if (!name_len) { fsck_log ("pass0: block %lu, item %H: entry %d. found a name \"%.*s\" " "matching to deh_offset %u. FIXME: should set deh_location " "of previous entry (not ready)\n", bh->b_blocknr, ih, i, j, name, deh_offset (deh + i)); /* FIXME: if next byte is 0 we think that the name is aligned to 8 byte boundary */ deh[i - 1].deh_location = cpu_to_le16 (deh_location (deh + i) + ((name[j] || SB_VERSION (fs) == REISERFS_VERSION_1) ? j : ROUND_UP (j))); } break; } } if (j == max_length + 1) { /* deh_offset does not match to anything. it will be deleted for now, but maybe we could just fix a deh_offset if it is in ordeer */ mark_de_bad_offset (deh + i); } } } #endif #ifdef DEBUG_VERIFY_DENTRY printf ("entries with mismatching deh_offsets: "); for (i = 0; i < ih_entry_count (ih); i ++) { if (de_bad_offset (deh + i)) printf ("%d ", i); } printf ("\n"); #endif /* DEBUG_VERIFY_DENTRY */ /* correct deh_locations such that code cutting entries will not get screwed up */ { int prev_loc; int loc_fixed; prev_loc = ih_item_len (ih); for (i = 0; i < ih_entry_count (ih); i ++) { loc_fixed = 0; if (de_bad_location (deh + i)) { set_deh_location(&(deh[i]), prev_loc/* - 1*/); mark_buffer_dirty (bh); loc_fixed = 1; } else { if (deh_location (deh + i) >= prev_loc) { set_deh_location(&(deh[i]), prev_loc/* - 1*/); mark_buffer_dirty (bh); loc_fixed = 1; } } prev_loc = deh_location (deh + i); if (i == ih_entry_count (ih) - 1) { /* last entry starts right after an array of dir entry headers */ if (!de_bad (deh + i) && deh_location (deh + i) != (DEH_SIZE * ih_entry_count (ih))) { /* free space in the directory item */ fsck_log ("verify_direntry: block %lu, item %H has free pace\n", bh->b_blocknr, ih); cut_entry (fs, bh, item_num, ih_entry_count (ih), 0); } if (deh_location (deh + i) != (DEH_SIZE * ih_entry_count (ih))) { set_deh_location(&(deh[i]), DEH_SIZE * ih_entry_count (ih)); loc_fixed = 1; mark_buffer_dirty (bh); } } #ifdef DEBUG_VERIFY_DENTRY if (loc_fixed) direntries [i] = 1; #endif } /* for */ #ifdef DEBUG_VERIFY_DENTRY printf ("entries with fixed deh_locations: "); for (i = 0; i < ih_entry_count (ih); i ++) { if (direntries [i]) printf ("%d ", i); } printf ("\n"); #endif /* DEBUG_VERIFY_DENTRY */ } #ifdef DEBUG_VERIFY_DENTRY printf (" N location name\n"); for (i = 0; i < ih_entry_count (ih); i ++) { if (de_bad (deh + i) || (i && de_bad (deh + i - 1)) || /* previous entry marked bad */ (i < ih_entry_count (ih) - 1 && de_bad (deh + i + 1))) { /* next ntry is marked bad */ /* print only entries to be deleted and their nearest neighbors */ printf ("%3d: %8d ", i, deh_location (deh + i)); if (de_bad (deh + i)) printf ("will be deleted\n"); else printf ("\"%.*s\"\n", name_length (ih, deh + i, i), name_in_entry (deh + i, i)); } } #endif bad = 0; tmp = *ih; /* delete entries which are marked bad */ for (i = 0; i < ih_entry_count (ih); i ++) { deh = B_I_DEH (bh, ih) + i; if (de_bad (deh)) { bad ++; if (ih_entry_count (ih) == 1) { delete_item (fs, bh, item_num); break; } else { cut_entry (fs, bh, item_num, i, 1); } i --; } } if (bad == ih_entry_count (&tmp)) { fsck_log ("pass0: block %lu, item %H - all entries were deleted\n", bh->b_blocknr, &tmp); return 0; } deh = B_I_DEH (bh, ih); if (get_offset (&ih->ih_key) != deh_offset (deh)) { fsck_log ("verify_direntry: block %lu, item %H: k_offset and deh_offset %u mismatched\n", bh->b_blocknr, ih, deh_offset (deh)); set_offset (KEY_FORMAT_1, &ih->ih_key, deh_offset (deh)); mark_buffer_dirty (bh); } if (bad) fsck_log ("pass0: block %lu, item %H: %d entries were deleted of \n", bh->b_blocknr, &tmp, bad); return 0; #if 0 /* FIXME: temporary */ if (bad_locations > ih_entry_count (ih) / 2) { fsck_log ("pass0: block %u: item %d (%H) had too bad directory - deleted\n", bh->b_blocknr, item_num, ih); delete_item (fs, bh, item_num); return 0; } if (!dirty) return 0; /* something is broken */ fsck_log ("pass0: block %lu: %d-th item (%H) has %d bad entries..", bh->b_blocknr, item_num, ih, dirty); if (get_offset (&ih->ih_key) == DOT_OFFSET) { /* first item of directory - make sure that "." and ".." are in place */ if (deh_offset (deh) != DOT_OFFSET || name_in_entry (deh, 0)[0] != '.') { set_deh_offset(deh, DOT_OFFSET); name_in_entry (deh, 0)[0] = '.'; } if (deh_offset (deh + 1) != DOT_DOT_OFFSET || name_in_entry (deh + 1, 1)[0] != '.' || name_in_entry (deh + 1, 1)[1] != '.') { set_deh_offset((deh + 1), DOT_DOT_OFFSET); name_in_entry (deh + 1, 1)[0] = '.'; name_in_entry (deh + 1, 1)[1] = '.'; } } end = item + ih_item_len (ih); deh += ih_entry_count (ih); entries = (char *)deh; total_entry_len = ih_item_len (ih) - DEH_SIZE * ih_entry_count (ih); i = ih_entry_count (ih); bad_entries = 0; do { i --; deh --; name_len = prob_name (fs, &entries, total_entry_len, deh_offset (deh)); if (!name_len) { if (!bad_entries) { set_deh_location(deh, entries - item); } else { set_deh_location(deh, deh_location((deh + 1)) + 1 ); } bad_entries ++; /*fsck_log ("verify_directory_item: entry %d: in string \'%s\' there is no substring matching hash %ld\n", i, bad_name (entries, total_entry_len), masked_offset);*/ mark_de_bad (deh); continue; } bad_entries = 0; /*fsck_log ("verify_directory_item: entry %d: found \"%s\" name matching hash %ld\n", i, bad_name (entries, name_len), masked_offset);*/ /* 'entries' points now to the name which match given offset - so, set deh_location */ set_deh_location(deh, entries - item); deh->deh_state = 0; mark_de_visible (deh); entries += name_len; total_entry_len = end - entries; /* skip padding zeros */ while (!*entries) { entries ++; total_entry_len --; } /* 'entries' points now at the place where next (previous) entry should start */ } while ((char *)deh != item); /* fixme: this will not work if all entries are to be deleted */ for (i = 0; i < ih_entry_count (ih); i ++, deh ++) { deh = (struct reiserfs_de_head *)B_I_PITEM (bh, ih) + i; if (de_bad (deh)) { if (ih_entry_count (ih) == 1) { delete_item (fs, bh, i); break; } else { cut_entry (fs, bh, item_num, i); } i --; } /* fsck_log ("verify_directory_item: %d-th entry is to be deleted: " "\"%s\" does not match to hash %lu\n", i, bad_name (name_in_entry (deh, i), name_length (ih, deh, i)), deh_offset (deh)); */ } fsck_log ("%d entries were deleted\n", entry_count - ih_entry_count (ih)); mark_buffer_dirty (bh); return 0; #endif }
/* preconditions: reiserfs_read_super already executed, therefore * INFO block is valid * returns: 0 if error, nonzero iff we were able to find the file successfully * postconditions: on a nonzero return, INFO->fileinfo contains the info * of the file we were trying to look up, filepos is 0 and filemax is * the size of the file. */ static int reiserfs_open_file( char *dirname ) { struct reiserfs_de_head *de_head; char *rest, ch; __u32 dir_id, objectid, parent_dir_id = 0, parent_objectid = 0; char linkbuf[PATH_MAX]; /* buffer for following symbolic links */ int link_count = 0; int mode; errnum = 0; dir_id = cpu_to_le32(REISERFS_ROOT_PARENT_OBJECTID); objectid = cpu_to_le32(REISERFS_ROOT_OBJECTID); while ( 1 ) { DEBUG_F( "dirname=%s\n", dirname ); /* Search for the stat info first. */ if ( !search_stat( dir_id, objectid ) ) return 0; DEBUG_F( "sd_mode=0%o sd_size=%Lu\n", sd_mode((struct stat_data *) INFO->current_item ), sd_size(INFO->current_ih, INFO->current_item )); mode = sd_mode((struct stat_data *)INFO->current_item); /* If we've got a symbolic link, then chase it. */ if ( S_ISLNK( mode ) ) { int len = 0; DEBUG_F("link count = %d\n", link_count); DEBUG_SLEEP; if ( ++link_count > MAX_LINK_COUNT ) { DEBUG_F("Symlink loop\n"); errnum = FILE_ERR_SYMLINK_LOOP; return 0; } /* Get the symlink size. */ INFO->file->len = sd_size(INFO->current_ih, INFO->current_item); /* Find out how long our remaining name is. */ while ( dirname[len] && !isspace( dirname[len] ) ) len++; if ( INFO->file->len + len > sizeof ( linkbuf ) - 1 ) { errnum = FILE_ERR_LENGTH; return 0; } /* Copy the remaining name to the end of the symlink data. Note * * that DIRNAME and LINKBUF may overlap! */ memmove( linkbuf + INFO->file->len, dirname, len + 1 ); INFO->fileinfo.k_dir_id = dir_id; INFO->fileinfo.k_objectid = objectid; INFO->file->pos = 0; if ( !next_key() || reiserfs_read_data( linkbuf, INFO->file->len ) != INFO->file->len ) { DEBUG_F("reiserfs_open_file - if !next_key || reiserfs_read_data\n"); DEBUG_SLEEP; errnum = FILE_IOERR; return 0; } DEBUG_F( "symlink=%s\n", linkbuf ); DEBUG_SLEEP; dirname = linkbuf; if ( *dirname == '/' ) { /* It's an absolute link, so look it up in root. */ dir_id = cpu_to_le32(REISERFS_ROOT_PARENT_OBJECTID); objectid = cpu_to_le32(REISERFS_ROOT_OBJECTID); } else { /* Relative, so look it up in our parent directory. */ dir_id = parent_dir_id; objectid = parent_objectid; } /* Now lookup the new name. */ continue; } /* if we have a real file (and we're not just printing * * possibilities), then this is where we want to exit */ if ( !*dirname || isspace( *dirname ) ) { if ( !S_ISREG( mode ) ) { errnum = FILE_ERR_BAD_TYPE; return 0; } INFO->file->pos = 0; INFO->file->len = sd_size(INFO->current_ih, INFO->current_item); INFO->fileinfo.k_dir_id = dir_id; INFO->fileinfo.k_objectid = objectid; return next_key(); } /* continue with the file/directory name interpretation */ while ( *dirname == '/' ) dirname++; if ( !S_ISDIR( mode ) ) { errnum = FILE_ERR_NOTDIR; return 0; } for ( rest = dirname; ( ch = *rest ) && !isspace( ch ) && ch != '/'; rest++ ) ; *rest = 0; while ( 1 ) { char *name_end; int num_entries; if ( !next_key() ) return 0; if ( INFO->current_ih->ih_key.k_objectid != objectid ) break; name_end = INFO->current_item + ih_item_len(INFO->current_ih); de_head = ( struct reiserfs_de_head * ) INFO->current_item; num_entries = ih_entry_count(INFO->current_ih); while ( num_entries > 0 ) { char *filename = INFO->current_item + deh_location(de_head); char tmp = *name_end; if( deh_state(de_head) & (1 << DEH_Visible)) { int cmp; /* Directory names in ReiserFS are not null * terminated. * We write a temporary 0 behind it. * NOTE: that this * may overwrite the first block in * the tree cache. * That doesn't hurt as long as we * don't call next_key * () in between. */ *name_end = 0; cmp = strcmp( dirname, filename ); *name_end = tmp; if ( cmp == 0 ) goto found; } /* The beginning of this name marks the end of the next name. */ name_end = filename; de_head++; num_entries--; } } errnum = FILE_ERR_NOTFOUND; *rest = ch; return 0; found: *rest = ch; dirname = rest; parent_dir_id = dir_id; parent_objectid = objectid; dir_id = de_head->deh_dir_id; /* LE */ objectid = de_head->deh_objectid; /* LE */ } }