static void fix_dirent_inode(o2fsck_state *ost, o2fsck_dirblock_entry *dbe, struct ocfs2_dir_entry *dirent, int offset, unsigned int *flags) { if (ocfs2_block_out_of_range(ost->ost_fs, dirent->inode) && prompt(ost, PY, PR_DIRENT_INODE_RANGE, "Directory entry '%.*s' refers to inode " "number %"PRIu64" which is out of range, clear the entry?", dirent->name_len, dirent->name, (uint64_t)dirent->inode)) { dirent->inode = 0; *flags |= OCFS2_DIRENT_CHANGED; goto out; } if (!o2fsck_test_inode_allocated(ost, dirent->inode) && prompt(ost, PY, PR_DIRENT_INODE_FREE, "Directory entry '%.*s' refers to inode number " "%"PRIu64" which isn't allocated, clear the entry?", dirent->name_len, dirent->name, (uint64_t)dirent->inode)) { dirent->inode = 0; *flags |= OCFS2_DIRENT_CHANGED; } out: return; }
static errcode_t count_tags(ocfs2_filesys *fs, journal_superblock_t *jsb, char *buf, uint64_t *nr_ret) { char *tagp, *last; journal_block_tag_t *tag; int tag_bytes = ocfs2_journal_tag_bytes(jsb); uint64_t nr = 0; if (jsb->s_blocksize < sizeof(journal_header_t) + tag_bytes) return OCFS2_ET_BAD_JOURNAL_TAG; tagp = &buf[sizeof(journal_header_t)]; last = &buf[jsb->s_blocksize - tag_bytes]; for(; tagp <= last; tagp += tag_bytes) { tag = (journal_block_tag_t *)tagp; nr++; if (ocfs2_block_out_of_range(fs, ocfs2_journal_tag_block(tag, tag_bytes))) return OCFS2_ET_BAD_JOURNAL_TAG; if (tag->t_flags & cpu_to_be32(JBD2_FLAG_LAST_TAG)) break; if (!(tag->t_flags & cpu_to_be32(JBD2_FLAG_SAME_UUID))) tagp += 16; } *nr_ret = nr; return 0; }
static PyObject * fs_block_out_of_range (Filesystem *self, PyObject *args, PyObject *kwargs) { unsigned long long block; int ret; static char *kwlist[] = { "block", NULL }; if (!PyArg_ParseTupleAndKeywords (args, kwargs, "K:block_out_of_range", kwlist, &block)) return NULL; ret = ocfs2_block_out_of_range(self->fs, block); return PyBool_FromLong (ret); }
errcode_t check_el(o2fsck_state *ost, struct extent_info *ei, struct ocfs2_dinode *di, struct ocfs2_extent_list *el, uint16_t max_recs, int *changed) { int trust_next_free = 1; struct ocfs2_extent_rec *er; uint64_t max_size; uint16_t i; uint32_t clusters; size_t cpy; verbosef("depth %u count %u next_free %u\n", el->l_tree_depth, el->l_count, el->l_next_free_rec); if (ei->ei_expect_depth && el->l_tree_depth != ei->ei_expected_depth && prompt(ost, PY, PR_EXTENT_LIST_DEPTH, "Extent list in inode %"PRIu64" is recorded as " "being at depth %u but we expect it to be at depth %u. " "update the list?", (uint64_t)di->i_blkno, el->l_tree_depth, ei->ei_expected_depth)) { el->l_tree_depth = ei->ei_expected_depth; *changed = 1; } if (el->l_count > max_recs && prompt(ost, PY, PR_EXTENT_LIST_COUNT, "Extent list in inode %"PRIu64" claims to have %u " "records, but the maximum is %u. Fix the list's count?", (uint64_t)di->i_blkno, el->l_count, max_recs)) { el->l_count = max_recs; *changed = 1; } if (max_recs > el->l_count) max_recs = el->l_count; if (el->l_next_free_rec > max_recs) { if (prompt(ost, PY, PR_EXTENT_LIST_FREE, "Extent list in inode %"PRIu64" claims %u " "as the next free chain record, but fsck believes " "the largest valid value is %u. Clamp the next " "record value?", (uint64_t)di->i_blkno, el->l_next_free_rec, max_recs)) { el->l_next_free_rec = el->l_count; *changed = 1; } else { trust_next_free = 0; } } if (trust_next_free) max_recs = el->l_next_free_rec; for (i = 0; i < max_recs; i++) { er = &el->l_recs[i]; clusters = ocfs2_rec_clusters(el->l_tree_depth, er); /* * For a sparse file, we may find an empty record * in the left most record. Just skip it. */ if ((OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_feature_incompat & OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC) && el->l_tree_depth && !i && !clusters) continue; /* returns immediately if blkno is out of range. * descends into eb. checks that data er doesn't * reference past the volume or anything crazy. */ check_er(ost, ei, di, el, er, changed); /* offer to remove records that point to nowhere */ if (ocfs2_block_out_of_range(ost->ost_fs, er->e_blkno) && prompt(ost, PY, PR_EXTENT_BLKNO_RANGE, "Extent record %u in inode %"PRIu64" " "refers to a block that is out of range. Remove " "this record from the extent list?", i, (uint64_t)di->i_blkno)) { if (!trust_next_free) { printf("Can't remove the record becuase " "next_free_rec hasn't been fixed\n"); continue; } cpy = (max_recs - i - 1) * sizeof(*er); /* shift the remaining recs into this ones place */ if (cpy != 0) { memcpy(er, er + 1, cpy); memset(&el->l_recs[max_recs - 1], 0, sizeof(*er)); i--; } el->l_next_free_rec--; max_recs--; *changed = 1; continue; } /* we've already accounted for the extent block as part of * the extent block chain groups */ if (el->l_tree_depth) continue; /* mark the data clusters as used */ o2fsck_mark_clusters_allocated(ost, ocfs2_blocks_to_clusters(ost->ost_fs, er->e_blkno), clusters); ei->ei_clusters += clusters; max_size = (er->e_cpos + clusters) << OCFS2_RAW_SB(ost->ost_fs->fs_super)->s_clustersize_bits; if (max_size > ei->ei_max_size) ei->ei_max_size = max_size; } return 0; }
/* the caller will check if er->e_blkno is out of range to determine if it * should try removing the record */ static errcode_t check_er(o2fsck_state *ost, struct extent_info *ei, struct ocfs2_dinode *di, struct ocfs2_extent_list *el, struct ocfs2_extent_rec *er, int *changed) { errcode_t ret = 0; uint64_t first_block; uint32_t last_cluster, clusters; clusters = ocfs2_rec_clusters(el->l_tree_depth, er); verbosef("cpos %u clusters %u blkno %"PRIu64"\n", er->e_cpos, clusters, (uint64_t)er->e_blkno); if (ocfs2_block_out_of_range(ost->ost_fs, er->e_blkno)) goto out; if (el->l_tree_depth) { int is_valid = 0; /* we only expect a given depth when we descend to extent blocks * from a previous depth. these start at 0 when the inode * is checked */ ei->ei_expect_depth = 1; ei->ei_expected_depth = el->l_tree_depth - 1; check_eb(ost, ei, di, er->e_blkno, &is_valid); if (!is_valid && prompt(ost, PY, PR_EXTENT_EB_INVALID, "The extent record for cluster offset " "%"PRIu32" in inode %"PRIu64" refers to an invalid " "extent block at %"PRIu64". Clear the reference " "to this invalid block?", er->e_cpos, (uint64_t)di->i_blkno, (uint64_t)er->e_blkno)) { er->e_blkno = 0; *changed = 1; } ret = 0; goto out; } if (!ocfs2_writes_unwritten_extents(OCFS2_RAW_SB(ost->ost_fs->fs_super)) && (er->e_flags & OCFS2_EXT_UNWRITTEN) && prompt(ost, PY, PR_EXTENT_MARKED_UNWRITTEN, "The extent record for cluster offset %"PRIu32" " "in inode %"PRIu64" has the UNWRITTEN flag set, but " "this filesystem does not support unwritten extents. " "Clear the UNWRITTEN flag?", er->e_cpos, (uint64_t)di->i_blkno)) { er->e_flags &= ~OCFS2_EXT_UNWRITTEN; } first_block = ocfs2_blocks_to_clusters(ost->ost_fs, er->e_blkno); first_block = ocfs2_clusters_to_blocks(ost->ost_fs, first_block); if (first_block != er->e_blkno && prompt(ost, PY, PR_EXTENT_BLKNO_UNALIGNED, "The extent record for cluster offset %"PRIu32" " "in inode %"PRIu64" refers to block %"PRIu64" which isn't " "aligned with the start of a cluster. Point the extent " "record at block %"PRIu64" which starts this cluster?", er->e_cpos, (uint64_t)di->i_blkno, (uint64_t)er->e_blkno, first_block)) { er->e_blkno = first_block; *changed = 1; } /* imagine blkno 0, 1 er_clusters. last_cluster is 1 and * fs_clusters is 1, which is ok.. */ last_cluster = ocfs2_blocks_to_clusters(ost->ost_fs, er->e_blkno) + clusters; if (last_cluster > ost->ost_fs->fs_clusters && prompt(ost, PY, PR_EXTENT_CLUSTERS_OVERRUN, "The extent record for cluster offset %"PRIu32" " "in inode %"PRIu64" refers to an extent that goes beyond " "the end of the volume. Truncate the extent by %"PRIu32" " "clusters to fit it in the volume?", er->e_cpos, (uint64_t)di->i_blkno, last_cluster - ost->ost_fs->fs_clusters)) { clusters -= last_cluster - ost->ost_fs->fs_clusters; ocfs2_set_rec_clusters(el->l_tree_depth, er, clusters); *changed = 1; } /* XXX offer to remove leaf records with er_clusters set to 0? */ /* XXX check that the blocks that are referenced aren't already * used */ out: return ret; }