/* this could certainly be more clever to issue reads in groups */ static unsigned pass2_dir_block_iterate(o2fsck_dirblock_entry *dbe, void *priv_data) { struct dirblock_data *dd = priv_data; struct ocfs2_dir_entry *dirent, *prev = NULL; unsigned int offset = 0, ret_flags = 0, end = dd->fs->fs_blocksize; struct ocfs2_dinode *di = (struct ocfs2_dinode *)dd->inoblock_buf; errcode_t ret = 0; if (!o2fsck_test_inode_allocated(dd->ost, dbe->e_ino)) { printf("Directory block %"PRIu64" belongs to directory inode " "%"PRIu64" which isn't allocated. Ignoring this " "block.", dbe->e_blkno, dbe->e_ino); goto out; } if (dbe->e_ino != dd->last_ino) { o2fsck_strings_free(&dd->strings); dd->last_ino = dbe->e_ino; ret = ocfs2_read_inode(dd->ost->ost_fs, dbe->e_ino, dd->inoblock_buf); if (ret) { com_err(whoami, ret, "while reading dir inode %"PRIu64, dbe->e_ino); ret_flags |= OCFS2_DIRENT_ABORT; goto out; } verbosef("dir inode %"PRIu64" i_size %"PRIu64"\n", dbe->e_ino, (uint64_t)di->i_size); } verbosef("dir block %"PRIu64" block offs %"PRIu64" in ino\n", dbe->e_blkno, dbe->e_blkcount); if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) { if (dbe->e_ino != dbe->e_blkno) goto out; memcpy(dd->dirblock_buf, dd->inoblock_buf, dd->fs->fs_blocksize); offset = offsetof(struct ocfs2_dinode, id2.i_data.id_data); } else { if (dbe->e_blkcount >= ocfs2_blocks_in_bytes(dd->fs,
/* detecting dups is irritating because of the storage requirements of * detecting duplicates. e2fsck avoids the storage burden for a regular fsck * pass by only detecting duplicate entries that occur in the same directory * block. its repair pass then suffers under enormous directories because it * reads the whole thing into memory to detect duplicates. * * we'll take a compromise which expands the reach of a regular fsck pass by * using a slightly larger block size but which repairs in place rather than * reading the dir into memory. * * if we ever truly care to invest in duplicate detection and repair we could * either explicitly use some external sort and merge algo or perhaps just * combine mmap and some internal sort that has strong enough locality of * reference to work well with the vm. */ static errcode_t fix_dirent_dups(o2fsck_state *ost, o2fsck_dirblock_entry *dbe, struct ocfs2_dir_entry *dirent, o2fsck_strings *strings, unsigned int *flags) { errcode_t ret = 0; char *new_name = NULL; int was_set, i; /* start over every N bytes of dirent */ if (o2fsck_strings_bytes_allocated(strings) > (4 * 1024 * 1024)) o2fsck_strings_free(strings); ret = o2fsck_strings_insert(strings, dirent->name, dirent->name_len, &was_set); if (ret) { com_err(whoami, ret, "while allocating space to find " "duplicate directory entries"); goto out; } if (!was_set) goto out; new_name = calloc(1, dirent->rec_len + 1); if (new_name == NULL) { ret = OCFS2_ET_NO_MEMORY; com_err(whoami, ret, "while trying to generate a new name " "for duplicate file name '%.*s' in dir inode " "%"PRIu64, dirent->name_len, dirent->name, dbe->e_ino); goto out; } /* just simple mangling for now */ memcpy(new_name, dirent->name, dirent->name_len); was_set = 1; /* append '_' to free space in the dirent until its unique */ for (i = dirent->name_len ; was_set && i < dirent->rec_len; i++){ new_name[i] = '_'; if (!o2fsck_strings_exists(strings, new_name, strlen(new_name))) was_set = 0; } /* rename characters at the end to '_' until its unique */ for (i = dirent->name_len - 1 ; was_set && i >= 0; i--) { new_name[i] = '_'; if (!o2fsck_strings_exists(strings, new_name, strlen(new_name))) was_set = 0; } if (was_set) { printf("Directory inode %"PRIu64" contains a duplicate " "occurance " "of the file name '%.*s' but fsck was " "unable to come up with a unique name so this duplicate " "name will not be dealt with.\n.", dbe->e_ino, dirent->name_len, dirent->name); goto out; } if (!prompt(ost, PY, PR_DIRENT_DUPLICATE, "Directory inode %"PRIu64" contains a duplicate occurance " "of the file name '%.*s'. Replace this duplicate name " "with '%s'?", dbe->e_ino, dirent->name_len, dirent->name, new_name)) { /* we don't really care that we leak new_name's recording * in strings, it'll be freed later */ goto out; } ret = o2fsck_strings_insert(strings, new_name, strlen(new_name), NULL); if (ret) { com_err(whoami, ret, "while allocating space to track " "duplicates of a newly renamed dirent"); goto out; } dirent->name_len = strlen(new_name); memcpy(dirent->name, new_name, dirent->name_len); *flags |= OCFS2_DIRENT_CHANGED; out: if (new_name != NULL) free(new_name); return ret; }
/* this could certainly be more clever to issue reads in groups */ static unsigned pass2_dir_block_iterate(o2fsck_dirblock_entry *dbe, void *priv_data) { struct dirblock_data *dd = priv_data; struct ocfs2_dir_entry *dirent, *prev = NULL; unsigned int offset = 0, ret_flags = 0, end = dd->fs->fs_blocksize; unsigned int write_off, saved_reclen; struct ocfs2_dinode *di = (struct ocfs2_dinode *)dd->inoblock_buf; errcode_t ret = 0; if (!o2fsck_test_inode_allocated(dd->ost, dbe->e_ino)) { printf("Directory block %"PRIu64" belongs to directory inode " "%"PRIu64" which isn't allocated. Ignoring this " "block.", dbe->e_blkno, dbe->e_ino); goto out; } if (dbe->e_ino != dd->last_ino) { o2fsck_strings_free(&dd->strings); dd->last_ino = dbe->e_ino; ret = ocfs2_read_inode(dd->ost->ost_fs, dbe->e_ino, dd->inoblock_buf); if (ret == OCFS2_ET_BAD_CRC32) { if (prompt(dd->ost, PY, PR_BAD_CRC32, "Directory inode %"PRIu64" " "has bad CRC32. Recalculate CRC32 " "and write inode block?", dbe->e_ino)) { ocfs2_write_inode(dd->ost->ost_fs, dbe->e_ino, dd->inoblock_buf); } } else if (ret) { com_err(whoami, ret, "while reading dir inode %"PRIu64, dbe->e_ino); ret_flags |= OCFS2_DIRENT_ABORT; goto out; } verbosef("dir inode %"PRIu64" i_size %"PRIu64"\n", dbe->e_ino, (uint64_t)di->i_size); /* Set the flag for index rebuilding */ if (ocfs2_supports_indexed_dirs(OCFS2_RAW_SB(dd->fs->fs_super)) && !(di->i_dyn_features & OCFS2_INLINE_DATA_FL) && !(di->i_dyn_features & OCFS2_INDEXED_DIR_FL) && prompt(dd->ost, PY, PR_DX_TREE_MISSING, "Directory %"PRIu64" is missing index. " "Rebuild?", dbe->e_ino)) ret_flags |= OCFS2_DIRENT_CHANGED; } verbosef("dir block %"PRIu64" block offs %"PRIu64" in ino\n", dbe->e_blkno, dbe->e_blkcount); if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) { if (dbe->e_ino != dbe->e_blkno) goto out; memcpy(dd->dirblock_buf, dd->inoblock_buf, dd->fs->fs_blocksize); offset = offsetof(struct ocfs2_dinode, id2.i_data.id_data); } else { if (dbe->e_blkcount >= ocfs2_blocks_in_bytes(dd->fs,