/* * Copyright 1996-2004 by Hans Reiser, licensing governed by * reiserfsprogs/README */ #include "fsck.h" int screen_width; int screen_curr_pos; char *screen_savebuffer; int screen_savebuffer_len; #define MIN(a, b) (((a)>(b))?(b):(a)) /* semantic pass progress */ void print_name(char *name, int len) { int i; if (fsck_quiet(fs)) return; if (len + screen_curr_pos + 1 > screen_savebuffer_len) { char *t; t = expandmem(screen_savebuffer, screen_savebuffer_len + 1, len + screen_curr_pos - screen_savebuffer_len + 1); if (!t) { fsck_progress("\nOut of memory\n"); return; // ???? } screen_savebuffer = t; screen_savebuffer_len = len + screen_curr_pos + 1; } strcat(screen_savebuffer, "/"); strncat(screen_savebuffer, name, len); i = screen_curr_pos; screen_curr_pos += len + 1; for (; i < screen_curr_pos; i++) if (screen_savebuffer[i] < 32) screen_savebuffer[i] = '?'; screen_savebuffer[screen_curr_pos] = 0; if (screen_width < screen_curr_pos) { printf("\r... %.*s", screen_width - 4, screen_savebuffer + (screen_curr_pos - (screen_width - 4))); } else printf("/%.*s", len, screen_savebuffer + screen_curr_pos - len); fflush(stdout); } void erase_name(int len) { int i; if (fsck_quiet(fs)) return; if (screen_curr_pos < screen_width) { screen_curr_pos -= len + 1; screen_savebuffer[screen_curr_pos] = 0; for (i = 0; i <= len; i++) printf("\b"); for (i = 0; i <= len + 1; i++) printf(" "); for (i = 0; i <= len + 1; i++) printf("\b"); } else { screen_curr_pos -= len + 1; if (screen_curr_pos < 0) die("%s: Get out of buffer's data!\n", __FUNCTION__); screen_savebuffer[screen_curr_pos] = 0; printf("\r"); if (screen_curr_pos >= screen_width) { int t_preface = MIN(screen_curr_pos - screen_width, 4); printf("%.*s%.*s", t_preface, "... ", screen_width - t_preface - 1, screen_savebuffer + (screen_curr_pos - (screen_width - t_preface)) + 1); } else { int space_used = printf("%s", screen_savebuffer); for (i = space_used; i < screen_width; i++) printf(" "); for (i = space_used; i < screen_width; i++) printf("\b"); } } fflush(stdout); } /* *size is "real" file size, sd_size - size from stat data */ int wrong_st_size(struct reiserfs_key *key, unsigned long long max_file_size, int blocksize, __u64 * size, __u64 sd_size, int type) { if (sd_size <= max_file_size) { if (sd_size == *size) return 0; if (type == TYPE_DIRENTRY) { /* directory size must match to the sum of length of its entries */ fsck_log ("vpf-10650: The directory %K has the wrong size in the StatData " "(%Lu)%s(%Lu)\n", key, sd_size, fsck_mode(fs) == FSCK_CHECK ? ", should be " : " - corrected to ", *size); return 1; } if (sd_size > *size) { /* size in stat data can be bigger than size calculated by items */ if (fsck_adjust_file_size(fs) || type == TYPE_SYMLINK) { /* but it -o is given - fix that */ fsck_log ("vpf-10660: The file %K has too big size in the StatData " "(%Lu)%s(%Lu)\n", key, sd_size, fsck_mode(fs) == FSCK_CHECK ? ", should be " : " - corrected to ", *size); sem_pass_stat(fs)->fixed_sizes++; return 1; } *size = sd_size; return 0; } if (!(*size % blocksize)) { /* last item is indirect */ if (((sd_size & ~(blocksize - 1)) == (*size - blocksize)) && sd_size % blocksize) { /* size in stat data is correct */ *size = sd_size; return 0; } } else { /* last item is a direct one */ if (!(*size % 8)) { if (((sd_size & ~7) == (*size - 8)) && sd_size % 8) { /* size in stat data is correct */ *size = sd_size; return 0; } } } } fsck_log ("vpf-10670: The file %K has the wrong size in the StatData (%Lu)%s(%Lu)\n", key, sd_size, fsck_mode(fs) == FSCK_CHECK ? ", should be " : " - corrected to ", *size); sem_pass_stat(fs)->fixed_sizes++; return 1; } /* sd_blocks is 32 bit only */ /* old stat data shares sd_block and sd_dev - do not wipe sd_rdev out */ /* we should fix it as following: |------------------------------------------------------------------| | | 3.6 | 3.5 | |---------------------------------------------------| | | | blocks | r_dev|generation | blocks/r_dev | |------------------------------------------------------------------| | fifo, sockets | 0 | generation | 0 | | chr/blk_dev | 0 | maj:min | maj:min | | file, dir, link | blocks | generation | blocks | |------------------------------------------------------------------| */ int wrong_st_blocks(struct reiserfs_key *key, __u32 * blocks, __u32 sd_blocks, __u16 mode, int new_format) { int ret = 0; if (S_ISREG(mode) || S_ISLNK(mode) || S_ISDIR(mode)) { if ((!S_ISLNK(mode) && *blocks != sd_blocks) || (S_ISLNK(mode) && *blocks != sd_blocks && (ROUND_UP(*blocks) != sd_blocks))) { fsck_log ("vpf-10680: The %s %K has the wrong block count in the StatData " "(%u)%s(%u)\n", S_ISDIR(mode) ? "directory" : S_ISREG(mode) ? "file" : "link", key, sd_blocks, fsck_mode(fs) == FSCK_CHECK ? ", should be " : " - corrected to ", *blocks); ret = 1; } } else if (new_format || (S_ISFIFO(mode) || S_ISSOCK(mode))) { if (sd_blocks != 0) { fsck_log ("vpf-10690: The object %K has the wrong block count in the StatData " "(%u)%s(%u)\n", key, sd_blocks, fsck_mode(fs) == FSCK_CHECK ? ", should be " : " - corrected to ", 0); *blocks = 0; ret = 1; } } return ret; } /* int wrong_st_rdev (struct reiserfs_key *key, __u32 * sd_rdev, __u16 mode, int new_format) { int ret = 0; if (!new_format) return 0; if (!S_ISCHR (mode) && !S_ISBLK (mode)) { if (*sd_rdev != 0) { fsck_log ("%s %K has wrong sd_rdev %u, has to be 0\n", S_ISDIR (mode) ? "dir" : "file", key, *sd_rdev); *sd_rdev = 0; ret = 1; } } return ret; } */ /* only regular files and symlinks may have items but stat data. Symlink should have body */ int wrong_mode(struct reiserfs_key *key, __u16 * mode, __u64 real_size, int symlink) { int retval = 0; if (S_ISLNK(*mode) && !symlink) { fsck_log ("The file %K (%M) is too big to be the symlink%s regfile\n", key, *mode, fsck_mode(fs) == FSCK_CHECK ? ", should be the" : " - corrected " "to the"); *mode &= ~S_IFMT; *mode |= S_IFREG; retval = 1; } if (ftypelet(*mode) != '?') { /* mode looks reasonable */ if (S_ISREG(*mode) || S_ISLNK(*mode)) return retval; /* device, pipe, socket have no items */ if (!real_size) return retval; } /* there are items, so change file mode to regular file. Otherwise - file bodies do not get deleted */ if (fsck_mode(fs) == FSCK_CHECK) { fsck_log("The object %K has wrong mode (%M)\n", key, *mode); } else { fsck_log ("The object %K has wrong mode (%M) - corrected to %M\n", key, *mode, (S_IFREG | 0600)); } *mode = (S_IFREG | 0600); return 1; } /* key is a key of last file item */ int wrong_first_direct_byte(struct reiserfs_key *key, int blocksize, __u32 * first_direct_byte, __u32 sd_first_direct_byte, __u32 size) { if (!size || is_indirect_key(key)) { /* there is no direct item */ *first_direct_byte = NO_BYTES_IN_DIRECT_ITEM; if (sd_first_direct_byte != NO_BYTES_IN_DIRECT_ITEM) { if (fsck_mode(fs) == FSCK_CHECK) { fsck_log ("The file %K: The wrong info about the tail in the StatData, " "first_direct_byte (%d) - should be (%d)\n", key, sd_first_direct_byte, *first_direct_byte); } else { fsck_log ("The file %K: The wrong info about the tail in the StatData, " "first_direct_byte (%d) - corrected to (%d)\n", key, sd_first_direct_byte, *first_direct_byte); } return 1; } return 0; } /* there is direct item */ *first_direct_byte = (get_offset(key) & ~(blocksize - 1)) + 1; if (*first_direct_byte != sd_first_direct_byte) { if (fsck_mode(fs) == FSCK_CHECK) { fsck_log ("The file %K: The wrong info about the tail in the StatData, " "first_direct_byte (%d) - should be (%d)\n", key, sd_first_direct_byte, *first_direct_byte); } else { fsck_log ("The file %K: The wrong info about the tail in the StatData, " "first_direct_byte (%d) - corrected to (%d)\n", key, sd_first_direct_byte, *first_direct_byte); } return 1; } return 0; } /* delete all items (but directory ones) with the same key 'ih' has (including stat data of not a directory) and put them back at the other place */ void relocate_dir(struct item_head *ih, int change_ih) { struct reiserfs_key key; struct reiserfs_key *rkey; struct reiserfs_path path; struct item_head *path_ih; struct si *si; __u32 new_objectid; /* starting with the leftmost one - look for all items of file, store them and delete */ key = ih->ih_key; set_type_and_offset(KEY_FORMAT_1, &key, SD_OFFSET, TYPE_STAT_DATA); si = NULL; while (1) { reiserfs_search_by_key_4(fs, &key, &path); if (get_item_pos(&path) == B_NR_ITEMS(get_bh(&path))) { rkey = uget_rkey(&path); if (rkey && !not_of_one_file(&key, rkey)) { /* file continues in the right neighbor */ key = *rkey; pathrelse(&path); continue; } /* there is no more items of a directory */ pathrelse(&path); break; } if (is_stat_data_ih(tp_item_head(&path))) fix_obviously_wrong_sd_mode(&path); path_ih = tp_item_head(&path); if (not_of_one_file(&key, &(path_ih->ih_key))) { /* there are no more item with this key */ pathrelse(&path); break; } /* ok, item found, but make sure that it is not a directory one */ if ((is_stat_data_ih(path_ih) && not_a_directory(tp_item_body(&path))) || is_direct_ih(path_ih) || is_indirect_ih(path_ih)) { /* item of not a directory found. Leave it in the tree. FIXME: should not happen */ key = path_ih->ih_key; set_offset(KEY_FORMAT_1, &key, get_offset(&key) + 1); pathrelse(&path); continue; } /* directory stat data ro directory item */ si = save_and_delete_file_item(si, &path); } if (!si) { fsck_log ("relocate_dir: WARNING: No one item of the directory %K found\n", &key); return; } /* get new objectid for relocation or get objectid with which file was relocated already */ new_objectid = objectid_for_relocation(&ih->ih_key); set_key_objectid(&ih->ih_key, new_objectid); /* put all items removed back into tree */ while (si) { fsck_log("relocate_dir: Moving %k to ", &si->si_ih.ih_key); set_key_objectid(&si->si_ih.ih_key, new_objectid); fsck_log("%k\n", &si->si_ih.ih_key); if (get_offset(&(si->si_ih.ih_key)) == DOT_OFFSET) { /* fix "." entry to point to a directtory properly */ struct reiserfs_de_head *deh; deh = (struct reiserfs_de_head *)si->si_dnm_data; set_deh_objectid(deh, new_objectid); } insert_item_separately(&(si->si_ih), si->si_dnm_data, 1 /*was in tree */ ); si = remove_saved_item(si); } } /* path is path to stat data. If file will be relocated - new_ih will contain a key file was relocated with */ int rebuild_check_regular_file(struct reiserfs_path *path, void *sd, struct item_head *new_ih) { int is_new_file; // struct reiserfs_key sd_key; __u16 mode; __u32 nlink; __u64 real_size, saved_size; __u32 blocks, saved_blocks; /* proper values and value in stat data */ __u32 first_direct_byte, saved_first_direct_byte; struct buffer_head *bh; struct item_head *ih, sd_ih; int fix_sd; int symlnk = 0; int retval; retval = OK; /* stat data of a file */ ih = tp_item_head(path); bh = get_bh(path); if (new_ih) { /* this objectid is used already */ *new_ih = *ih; pathrelse(path); rewrite_file(new_ih, 1, 1); linked_already(&new_ih->ih_key); sem_pass_stat(fs)->oid_sharing_files_relocated++; retval = RELOCATED; if (reiserfs_search_by_key_4(fs, &(new_ih->ih_key), path) == ITEM_NOT_FOUND) reiserfs_panic ("%s: Could not find the StatData of the relocated file %k", __FUNCTION__, &(new_ih->ih_key)); /* stat data is marked unreachable again due to relocation, fix that */ ih = tp_item_head(path); bh = get_bh(path); mark_item_reachable(ih, bh); sd = tp_item_body(path); } id_map_mark(semantic_id_map(fs), get_key_objectid(&ih->ih_key)); /* check and set nlink first */ get_sd_nlink(ih, sd, &nlink); nlink++; set_sd_nlink(ih, sd, &nlink); mark_buffer_dirty(bh); if (nlink > 1) return retval; /* firts name of a file found */ if (get_ih_item_len(ih) == SD_SIZE) is_new_file = 1; else is_new_file = 0; get_sd_mode(ih, sd, &mode); get_sd_size(ih, sd, &saved_size); get_sd_blocks(ih, sd, &saved_blocks); if (!is_new_file) get_sd_first_direct_byte(ih, sd, &saved_first_direct_byte); /* we met this file first time */ if (S_ISREG(mode)) { sem_pass_stat(fs)->regular_files++; } else if (S_ISLNK(mode)) { symlnk = 1; sem_pass_stat(fs)->symlinks++; } else { sem_pass_stat(fs)->others++; } sd_ih = *ih; // sd_key = sd_ih.ih_key; pathrelse(path); if (are_file_items_correct (&sd_ih, sd, &real_size, &blocks, 1 /*mark items reachable */ , &symlnk) != 1) { /* unpassed items will be deleted in pass 4 as they left unaccessed */ sem_pass_stat(fs)->broken_files++; } fix_sd = 0; fix_sd += wrong_mode( /*&sd_key, */ &sd_ih.ih_key, &mode, real_size, symlnk); if (!is_new_file) fix_sd += wrong_first_direct_byte(&sd_ih.ih_key, fs->fs_blocksize, &first_direct_byte, saved_first_direct_byte, real_size); fix_sd += wrong_st_size( /*&sd_key, */ &sd_ih.ih_key, is_new_file ? MAX_FILE_SIZE_V2 : MAX_FILE_SIZE_V1, fs->fs_blocksize, &real_size, saved_size, symlnk ? TYPE_SYMLINK : 0); fix_sd += wrong_st_blocks(&sd_ih.ih_key, &blocks, saved_blocks, mode, is_new_file); if (fix_sd) { /* find stat data and correct it */ set_type_and_offset(KEY_FORMAT_1, &sd_ih.ih_key, SD_OFFSET, TYPE_STAT_DATA); if (reiserfs_search_by_key_4(fs, &sd_ih.ih_key, path) != ITEM_FOUND) reiserfs_panic ("%s: The StatData of the file %k could not be found", __FUNCTION__, &sd_ih.ih_key); bh = get_bh(path); ih = tp_item_head(path); sd = tp_item_body(path); set_sd_size(ih, sd, &real_size); set_sd_blocks(ih, sd, &blocks); set_sd_mode(ih, sd, &mode); if (!is_new_file) set_sd_first_direct_byte(ih, sd, &first_direct_byte); mark_buffer_dirty(bh); } return retval; } static int is_rootdir_key(struct reiserfs_key *key) { if (comp_keys(key, &root_dir_key)) return 0; return 1; } /* returns buffer, containing found directory item.*/ static char *get_next_directory_item(struct reiserfs_key *key, /* on return this will contain key of next item in the tree */ struct reiserfs_key *parent, struct item_head *ih, /*not in tree */ __u32 * pos_in_item) { INITIALIZE_REISERFS_PATH(path); char *dir_item; struct reiserfs_key *rdkey; struct buffer_head *bh; struct reiserfs_de_head *deh; int i; int retval; if ((retval = reiserfs_search_by_entry_key(fs, key, &path)) != POSITION_FOUND) reiserfs_panic ("get_next_directory_item: The current directory %k cannot be " "found", key); /* leaf containing directory item */ bh = PATH_PLAST_BUFFER(&path); *pos_in_item = path.pos_in_item; *ih = *tp_item_head(&path); deh = B_I_DEH(bh, ih); /* make sure, that ".." exists as well */ if (get_offset(key) == DOT_OFFSET) { if (get_ih_entry_count(ih) < 2 || name_in_entry_length(ih, deh + 1, 1) != 2 || strncmp(name_in_entry(deh + 1, 1), "..", 2)) { fsck_log ("get_next_directory_item: The entry \"..\" cannot be found in %k\n", &ih->ih_key); pathrelse(&path); return NULL; } } /* mark hidden entries as visible, set "." and ".." correctly */ deh += *pos_in_item; for (i = *pos_in_item; i < get_ih_entry_count(ih); i++, deh++) { int namelen; char *name; name = name_in_entry(deh, i); namelen = name_in_entry_length(ih, deh, i); if (de_hidden(deh)) reiserfs_panic ("get_next_directory_item: item %k: hidden entry %d \'%.*s\'\n", key, i, namelen, name); if (get_deh_offset(deh) == DOT_OFFSET) { if (not_of_one_file(&(deh->deh2_dir_id), key)) { /* "." must point to the directory it is in */ //deh->deh_objectid != REISERFS_ROOT_PARENT_OBJECTID)/*????*/ { fsck_log ("get_next_directory_item: The entry \".\" of the directory %K pointes to %K, instead of %K", key, (struct reiserfs_key *)(&(deh->deh2_dir_id)), key); set_deh_dirid(deh, get_key_dirid(key)); set_deh_objectid(deh, get_key_objectid(key)); mark_buffer_dirty(bh); fsck_log(" - corrected\n"); } } if (get_deh_offset(deh) == DOT_DOT_OFFSET) { /* set ".." so that it points to the correct parent directory */ if (comp_short_keys(&(deh->deh2_dir_id), parent)) { fsck_log ("get_next_directory_item: The entry \"..\" of the directory %K pointes to %K, instead of %K", key, (struct reiserfs_key *)(&(deh->deh2_dir_id)), parent); set_deh_dirid(deh, get_key_dirid(parent)); set_deh_objectid(deh, get_key_objectid(parent)); mark_buffer_dirty(bh); fsck_log(" - corrected\n"); } } } /* copy directory item to the temporary buffer */ dir_item = getmem(get_ih_item_len(ih)); memcpy(dir_item, ih_item_body(bh, ih), get_ih_item_len(ih)); /* next item key */ if (PATH_LAST_POSITION(&path) == (B_NR_ITEMS(bh) - 1) && (rdkey = uget_rkey(&path))) copy_key(key, rdkey); else { set_key_dirid(key, 0); set_key_objectid(key, 0); } if (fsck_mode(fs) == FSCK_REBUILD) mark_item_reachable(tp_item_head(&path), bh); pathrelse(&path); return dir_item; } // get key of an object pointed by direntry and the key of the entry itself void get_object_key(struct reiserfs_de_head *deh, struct reiserfs_key *key, struct reiserfs_key *entry_key, struct item_head *ih) { /* entry points to this key */ set_key_dirid(key, get_deh_dirid(deh)); set_key_objectid(key, get_deh_objectid(deh)); set_key_offset_v1(key, SD_OFFSET); set_key_uniqueness(key, 0); /* key of entry */ set_key_dirid(entry_key, get_key_dirid(&ih->ih_key)); set_key_objectid(entry_key, get_key_objectid(&ih->ih_key)); set_key_offset_v1(entry_key, get_deh_offset(deh)); set_key_uniqueness(entry_key, DIRENTRY_UNIQUENESS); } int fix_obviously_wrong_sd_mode(struct reiserfs_path *path) { struct reiserfs_key *next_key = NULL; __u16 mode; int retval = 0; next_key = reiserfs_next_key(path); if (!next_key || not_of_one_file(next_key, &tp_item_head(path)->ih_key)) return 0; /* next item exists and of the same file. Fix the SD mode */ if (not_a_directory(tp_item_body(path)) && is_direntry_key(next_key)) { /* make SD mode SD of dir */ get_sd_mode(tp_item_head(path), tp_item_body(path), &mode); fsck_log("The directory %K had wrong mode %M", &tp_item_head(path)->ih_key, mode); if (fsck_mode(fs) != FSCK_CHECK) { mode &= ~S_IFMT; mode |= S_IFDIR; fsck_log(" - corrected to %M\n", mode); set_sd_mode(tp_item_head(path), tp_item_body(path), &mode); mark_buffer_dirty(get_bh(path)); } else { fsck_log("\n"); retval = 1; } } else if (!not_a_directory(tp_item_body(path)) && !is_direntry_key(next_key)) { /* make SD mode SD of regular file */ get_sd_mode(tp_item_head(path), tp_item_body(path), &mode); fsck_log("The file %K had wrong mode %M", &tp_item_head(path)->ih_key, mode); if (fsck_mode(fs) != FSCK_CHECK) { mode &= ~S_IFMT; mode |= S_IFREG; fsck_log(" - corrected to %M\n", mode); set_sd_mode(tp_item_head(path), tp_item_body(path), &mode); mark_buffer_dirty(get_bh(path)); } else { fsck_log("\n"); retval = 1; } } return retval; } /* check recursively the semantic tree. Returns OK if entry points to good object, STAT_DATA_NOT_FOUND if stat data was not found or RELOCATED when file was relocated because its objectid was already marked as used by another file */ int rebuild_semantic_pass(struct reiserfs_key *key, struct reiserfs_key *parent, int dot_dot, struct item_head *new_ih) { struct reiserfs_path path; void *sd; int is_new_dir; __u32 nlink; struct buffer_head *bh; struct item_head *ih; int retval, retval1; char *dir_item; __u32 pos_in_item; struct item_head tmp_ih; struct reiserfs_key item_key, entry_key, object_key; __u64 dir_size; __u32 blocks; __u64 saved_size; __u32 saved_blocks; int fix_sd; int relocate; int dir_format = 0; __u16 mode; retval = OK; start_again: /* when directory was relocated */ if (!KEY_IS_STAT_DATA_KEY(key)) reiserfs_panic ("rebuild_semantic_pass: The key %k must be key of a StatData", key); /* look for stat data of an object */ if (reiserfs_search_by_key_4(fs, key, &path) == ITEM_NOT_FOUND) { pathrelse(&path); if (is_rootdir_key(key)) /* root directory has to exist at this point */ reiserfs_panic ("rebuild_semantic_pass: The root directory StatData was not found"); return STAT_DATA_NOT_FOUND; } /* stat data has been found */ bh = get_bh(&path); ih = tp_item_head(&path); sd = tp_item_body(&path); /* */ get_sd_nlink(ih, sd, &nlink); relocate = 0; if (!nlink) { /* we reached the stat data for the first time */ if (id_map_mark (semantic_id_map(fs), get_key_objectid(&ih->ih_key))) { /* calculate number of found files/dirs who are using objectid which is used by another file */ sem_pass_stat(fs)->oid_sharing++; if (1 /*fsck_adjust_file_size (fs) */ ) /* this works for files only */ relocate = 1; } mark_item_reachable(ih, bh); } fix_obviously_wrong_sd_mode(&path); if (not_a_directory(sd)) { retval = rebuild_check_regular_file(&path, sd, relocate ? new_ih : NULL); pathrelse(&path); return retval; } if (relocate) { if (!new_ih) reiserfs_panic ("rebuild_semantic_pass: Memory is not prepared for relocation of %K", &ih->ih_key); *new_ih = *ih; pathrelse(&path); sem_pass_stat(fs)->oid_sharing_dirs_relocated++; relocate_dir(new_ih, 1); linked_already(&new_ih->ih_key); *key = new_ih->ih_key; retval = RELOCATED; goto start_again; } /* it looks like stat data of a directory found */ if (nlink) { /* we saw this directory already */ if (!dot_dot) { /* this name is not ".." - and hard links are not allowed on directories */ pathrelse(&path); return STAT_DATA_NOT_FOUND; } else { /* ".." found */ nlink++; set_sd_nlink(ih, sd, &nlink); mark_buffer_dirty(bh); pathrelse(&path); return OK; } } /* we see the directory first time */ sem_pass_stat(fs)->directories++; nlink = 2; if (get_key_objectid(key) == REISERFS_ROOT_OBJECTID) nlink++; set_sd_nlink(ih, sd, &nlink); mark_buffer_dirty(bh); if (get_ih_item_len(ih) == SD_SIZE) is_new_dir = 1; else is_new_dir = 0; /* { struct reiserfs_key *tmp; // check next item: if it is not a directory item of the same file // therefore sd_mode is corrupted so we just reset it to regular file // mode tmp = reiserfs_next_key (&path); if (tmp && !not_of_one_file (tmp, key) && !is_direntry_key (tmp)) { __u16 mode; get_sd_mode (ih, sd, &mode); fsck_log ("file %K had broken mode %M, ", key, mode); mode &= ~S_IFMT; mode |= S_IFREG; fsck_log ("fixed to %M\n", mode); set_sd_mode (ih, sd, &mode); nlink = 0; set_sd_nlink (ih, sd, &nlink); retval = rebuild_check_regular_file (&path, sd, 0); //no relocate pathrelse (&path); return retval; } } */ dir_format = (get_ih_item_len(tp_item_head(&path)) == SD_SIZE) ? KEY_FORMAT_2 : KEY_FORMAT_1; /* save stat data's size and st_blocks */ get_sd_size(ih, sd, &saved_size); get_sd_blocks(ih, sd, &saved_blocks); get_sd_mode(ih, sd, &mode); /* release path pointing to stat data */ pathrelse(&path); /* make sure that "." and ".." exist */ reiserfs_add_entry(fs, key, ".", name_length(".", dir_format), key, 1 << IH_Unreachable); reiserfs_add_entry(fs, key, "..", name_length("..", dir_format), parent, 1 << IH_Unreachable); set_key_dirid(&item_key, get_key_dirid(key)); set_key_objectid(&item_key, get_key_objectid(key)); set_key_offset_v1(&item_key, DOT_OFFSET); set_key_uniqueness(&item_key, DIRENTRY_UNIQUENESS); dir_size = 0; while ((dir_item = get_next_directory_item(&item_key, parent, &tmp_ih, &pos_in_item)) != NULL) { /* dir_item is copy of the item in separately allocated memory, item_key is a key of next item in the tree */ int i; char *name = NULL; int namelen, entry_len; struct reiserfs_de_head *deh = (struct reiserfs_de_head *)dir_item + pos_in_item; for (i = pos_in_item; i < get_ih_entry_count(&tmp_ih); i++, deh++) { struct item_head relocated_ih; if (name) { free(name); name = NULL; } namelen = name_in_entry_length(&tmp_ih, deh, i); asprintf(&name, "%.*s", namelen, name_in_entry(deh, i)); entry_len = entry_length(&tmp_ih, deh, i); get_object_key(deh, &object_key, &entry_key, &tmp_ih); if ((dir_format == KEY_FORMAT_2) && (entry_len % 8 != 0)) { /* not alighed directory of new format - delete it */ fsck_log ("Entry %K (\"%.*s\") in the directory %K is not formated properly - fixed.\n", (struct reiserfs_key *)&(deh->deh2_dir_id), namelen, name, &tmp_ih.ih_key); reiserfs_remove_entry(fs, &entry_key); entry_len = name_length(name, dir_format); reiserfs_add_entry(fs, key, name, entry_len, (struct reiserfs_key *) &(deh->deh2_dir_id), 0); } /* if ((dir_format == KEY_FORMAT_1) && (namelen != entry_len)) { // aligned entry in directory of old format - remove and insert it back fsck_log ("Entry %K (\"%.*s\") in the directory %K is not formated properly - deleted\n", (struct reiserfs_key *)&(deh->deh2_dir_id), namelen, name, &tmp_ih.ih_key); reiserfs_remove_entry (fs, &entry_key); entry_len = name_length (name, dir_format); reiserfs_add_entry (fs, key, name, entry_len, (struct reiserfs_key *)&(deh->deh2_dir_id), 0); } */ if (is_dot(name, namelen)) { dir_size += DEH_SIZE + entry_len; continue; } print_name(name, namelen); if (!is_properly_hashed (fs, name, namelen, get_deh_offset(deh))) reiserfs_panic ("rebuild_semantic_pass: Hash mismatch detected for (\"%.*s\") in directory %K\n", namelen, name, &tmp_ih.ih_key); retval1 = rebuild_semantic_pass(&object_key, key, is_dot_dot(name, namelen), &relocated_ih); erase_name(namelen); switch (retval1) { case OK: dir_size += DEH_SIZE + entry_len; break; case STAT_DATA_NOT_FOUND: case DIRECTORY_HAS_NO_ITEMS: if (get_offset(&entry_key) == DOT_DOT_OFFSET && get_key_objectid(&object_key) == REISERFS_ROOT_PARENT_OBJECTID) { /* ".." of root directory can not be found */ dir_size += DEH_SIZE + entry_len; continue; } fsck_log ("%s: The entry %K (\"%.*s\") in directory %K points to nowhere - is removed\n", __FUNCTION__, &object_key, namelen, name, &tmp_ih.ih_key); reiserfs_remove_entry(fs, &entry_key); sem_pass_stat(fs)->deleted_entries++; break; case RELOCATED: /* file was relocated, update key in corresponding directory entry */ if (reiserfs_search_by_entry_key (fs, &entry_key, &path) != POSITION_FOUND) { fsck_log ("WARNING: Cannot find the name of the relocated file %K in the directory %K\n", &object_key, &tmp_ih.ih_key); } else { /* update key dir entry points to */ struct reiserfs_de_head *tmp_deh; tmp_deh = B_I_DEH(get_bh(&path), tp_item_head(&path)) + path.pos_in_item; fsck_log ("The entry %K (\"%.*s\") in directory %K updated to point to ", &object_key, namelen, name, &tmp_ih.ih_key); set_deh_dirid(tmp_deh, get_key_dirid (&relocated_ih.ih_key)); set_deh_objectid(tmp_deh, get_key_objectid (&relocated_ih. ih_key)); fsck_log("%K\n", &tmp_deh->deh2_dir_id); mark_buffer_dirty(get_bh(&path)); } dir_size += DEH_SIZE + entry_len; pathrelse(&path); break; } } /* for */ freemem(dir_item); free(name); name = NULL; if (not_of_one_file(&item_key, key)) /* next key is not of this directory */ break; } /* while (dir_item) */ if (dir_size == 0) /* FIXME: is it possible? */ return DIRECTORY_HAS_NO_ITEMS; /* calc correct value of sd_blocks field of stat data */ blocks = dir_size2st_blocks(dir_size); fix_sd = 0; fix_sd += wrong_st_blocks(key, &blocks, saved_blocks, mode, is_new_dir); fix_sd += wrong_st_size(key, is_new_dir ? MAX_FILE_SIZE_V2 : MAX_FILE_SIZE_V1, fs->fs_blocksize, &dir_size, saved_size, TYPE_DIRENTRY); if (fix_sd) { /* we have to fix either sd_size or sd_blocks, so look for stat data again */ if (reiserfs_search_by_key_4(fs, key, &path) != ITEM_FOUND) reiserfs_panic ("rebuild_semantic_pass: The StatData of the file %K was not found", key); bh = get_bh(&path); ih = tp_item_head(&path); sd = tp_item_body(&path); set_sd_size(ih, sd, &dir_size); set_sd_blocks(ih, sd, &blocks); mark_buffer_dirty(bh); pathrelse(&path); } return retval; } int is_dot(char *name, int namelen) { return (namelen == 1 && name[0] == '.') ? 1 : 0; } int is_dot_dot(char *name, int namelen) { return (namelen == 2 && name[0] == '.' && name[1] == '.') ? 1 : 0; } int not_a_directory(void *sd) { /* mode is at the same place and of the same size in both stat datas (v1 and v2) */ struct stat_data_v1 *sd_v1 = sd; return !(S_ISDIR(le16_to_cpu(sd_v1->sd_mode))); } int not_a_regfile(void *sd) { /* mode is at the same place and of the same size in both stat datas (v1 and v2) */ struct stat_data_v1 *sd_v1 = sd; return !(S_ISREG(le16_to_cpu(sd_v1->sd_mode))); } void zero_nlink(struct item_head *ih, void *sd) { int zero = 0; if (get_ih_item_len(ih) == SD_V1_SIZE && get_ih_key_format(ih) != KEY_FORMAT_1) { fsck_log ("zero_nlink: The StatData %k of the wrong format version (%d) - corrected to (%d)\n", ih, get_ih_key_format(ih), KEY_FORMAT_1); set_ih_key_format(ih, KEY_FORMAT_1); } if (get_ih_item_len(ih) == SD_SIZE && get_ih_key_format(ih) != KEY_FORMAT_2) { fsck_log ("zero_nlink: The StatData %k of the wrong format version (%d) - corrected to (%d)\n", ih, get_ih_key_format(ih), KEY_FORMAT_2); set_ih_key_format(ih, KEY_FORMAT_2); } set_sd_nlink(ih, sd, &zero); } void modify_item(struct item_head *ih, void *item) { zero_nlink(ih, item); mark_item_unreachable(ih); } /* mkreiserfs should have created this */ static void make_sure_lost_found_exists(reiserfs_filsys_t *fs) { int retval; INITIALIZE_REISERFS_PATH(path); unsigned int gen_counter; __u32 objectid; __u64 sd_size; __u32 sd_blocks; struct buffer_head *bh; struct item_head *ih; void *sd; int item_len; /* look for "lost+found" in the root directory */ retval = reiserfs_find_entry(fs, &root_dir_key, "lost+found", &gen_counter, &lost_found_dir_key); if (!retval) { objectid = id_map_alloc(proper_id_map(fs)); if (!objectid) { fsck_progress ("Could not allocate an objectid for \"/lost+found\", \ lost files will not be linked\n"); return; } set_key_dirid(&lost_found_dir_key, REISERFS_ROOT_OBJECTID); set_key_objectid(&lost_found_dir_key, objectid); } /* look for stat data of "lost+found" */ retval = reiserfs_search_by_key_4(fs, &lost_found_dir_key, &path); if (retval == ITEM_NOT_FOUND) lost_found_dir_format = create_dir_sd(fs, &path, &lost_found_dir_key, modify_item); else { struct item_head *ih = tp_item_head(&path); if (!is_stat_data_ih(ih)) reiserfs_panic("It must be lost+found's stat data %k\n", &ih->ih_key); fix_obviously_wrong_sd_mode(&path); if (not_a_directory(tp_item_body(&path))) { fsck_progress ("\"/lost+found\" exists, but it is not a directory, \ lost files will not be linked\n"); set_key_objectid(&lost_found_dir_key, 0); pathrelse(&path); return; } lost_found_dir_format = (get_ih_item_len(tp_item_head(&path)) == SD_SIZE) ? KEY_FORMAT_2 : KEY_FORMAT_1; pathrelse(&path); } /* add "." and ".." if any of them do not exist */ reiserfs_add_entry(fs, &lost_found_dir_key, ".", name_length(".", lost_found_dir_format), &lost_found_dir_key, 1 << IH_Unreachable); reiserfs_add_entry(fs, &lost_found_dir_key, "..", name_length("..", lost_found_dir_format), &root_dir_key, 1 << IH_Unreachable); item_len = reiserfs_add_entry(fs, &root_dir_key, "lost+found", name_length("lost+found", root_dir_format), &lost_found_dir_key, 1 << IH_Unreachable); if (item_len) { if (reiserfs_search_by_key_4(fs, &root_dir_key, &path) == ITEM_NOT_FOUND) reiserfs_panic ("%s: StatData of the root directory must exists", __FUNCTION__); bh = get_bh(&path); ih = tp_item_head(&path); sd = tp_item_body(&path); get_sd_size(ih, sd, &sd_size); sd_size += item_len; set_sd_size(ih, sd, &sd_size); sd_blocks = dir_size2st_blocks(sd_size); set_sd_blocks(ih, sd, &sd_blocks); mark_buffer_dirty(bh); pathrelse(&path); } return; } /* Result of the rebuild pass will be saved in the state file which is needed to start * fsck again from the next pass. */ static void save_rebuild_semantic_result(reiserfs_filsys_t *fs) { FILE *file; int retval; file = open_file("temp_fsck_file.deleteme", "w+"); if (!file) return; reiserfs_begin_stage_info_save(file, SEMANTIC_DONE); /* Method not implemented yet. reiserfs_objectid_map_save (file, semantic_id_map (fs)); */ reiserfs_end_stage_info_save(file); close_file(file); retval = rename("temp_fsck_file.deleteme", state_dump_file(fs)); if (retval != 0) fsck_progress ("%s: Could not rename the temporary file temp_fsck_file.deleteme to %s", __FUNCTION__, state_dump_file(fs)); } /* we have nothing to load from a state file, but we have to fetch on-disk bitmap, copy it to allocable bitmap, and fetch objectid map */ void load_semantic_result(FILE * file, reiserfs_filsys_t *fs) { fsck_new_bitmap(fs) = reiserfs_create_bitmap(get_sb_block_count(fs->fs_ondisk_sb)); reiserfs_bitmap_copy(fsck_new_bitmap(fs), fs->fs_bitmap2); fsck_allocable_bitmap(fs) = reiserfs_create_bitmap(get_sb_block_count(fs->fs_ondisk_sb)); reiserfs_bitmap_copy(fsck_allocable_bitmap(fs), fs->fs_bitmap2); fs->block_allocator = reiserfsck_reiserfs_new_blocknrs; fs->block_deallocator = reiserfsck_reiserfs_free_block; /* we need objectid map on semantic pass to be able to relocate files */ proper_id_map(fs) = id_map_init(); /* Not implemented yet. fetch_objectid_map (proper_id_map (fs), fs); semantic_id_map (fs) = reiserfs_objectid_map_load (file); */ } static void before_pass_3(reiserfs_filsys_t *fs) { semantic_id_map(fs) = id_map_init(); } static void after_pass_3(reiserfs_filsys_t *fs) { /* update super block: objectid map, fsck state */ set_sb_fs_state(fs->fs_ondisk_sb, SEMANTIC_DONE); mark_buffer_dirty(fs->fs_super_bh); /* write all dirty blocks */ fsck_progress("Flushing.."); id_map_flush(proper_id_map(fs), fs); fs->fs_dirt = 1; reiserfs_flush_to_ondisk_bitmap(fsck_new_bitmap(fs), fs); reiserfs_flush(fs); fsck_progress("finished\n"); stage_report(3, fs); if (!fsck_run_one_step(fs)) { if (fsck_user_confirmed(fs, "Continue? (Yes):", "Yes\n", 1)) /* reiserfsck continues */ return; } save_rebuild_semantic_result(fs); id_map_free(proper_id_map(fs)); proper_id_map(fs) = NULL; fs->fs_dirt = 1; reiserfs_close(fs); exit(0); } /* this is part of rebuild tree */ void pass_3_semantic(reiserfs_filsys_t *fs) { before_pass_3(fs); fsck_progress("Pass 3 (semantic):\n"); /* when warnings go not to stderr - separate them in the log */ if (fsck_log_file(fs) != stderr) fsck_log("####### Pass 3 #########\n"); if (!fs->fs_hash_function) reiserfs_panic("Hash function should be selected already"); make_sure_root_dir_exists(fs, modify_item, 1 << IH_Unreachable); make_sure_lost_found_exists(fs); id_map_mark(proper_id_map(fs), get_key_objectid(&root_dir_key)); id_map_mark(proper_id_map(fs), get_key_objectid(&lost_found_dir_key)); /* link all relocated files into /lost+found directory */ link_relocated_files(); rebuild_semantic_pass(&root_dir_key, &parent_root_dir_key, 0 /*!dot_dot */ , NULL /*reloc_ih */ ); add_badblock_list(fs, 1); after_pass_3(fs); }