/* * Delete and free clusters if needed. This only works with DEPTH_TRAVERSE. */ static int truncate_iterate(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { struct truncate_ctxt *ctxt = (struct truncate_ctxt *)priv_data; uint32_t len = 0, new_size_in_clusters = ctxt->new_size_in_clusters; uint64_t start = 0; errcode_t ret; int func_ret = OCFS2_EXTENT_ERROR; char *buf = NULL; struct ocfs2_extent_list *el = NULL; int cleanup_rec = 0; if ((rec->e_cpos + ocfs2_rec_clusters(tree_depth, rec)) <= new_size_in_clusters) return 0; if (rec->e_cpos >= new_size_in_clusters) { /* the rec is entirely outside the new size, free it */ if (!tree_depth) { start = rec->e_blkno; len = ocfs2_rec_clusters(tree_depth, rec); } else { /* here we meet with a full empty extent block, delete * it. The extent list it contains should already be * iterated and all the clusters have been freed. */ ret = ocfs2_delete_extent_block(fs, rec->e_blkno); if (ret) goto bail; } cleanup_rec = 1; } else { /* we're truncating into the middle of the rec */ len = rec->e_cpos + ocfs2_rec_clusters(tree_depth, rec); len -= new_size_in_clusters; if (!tree_depth) { ocfs2_set_rec_clusters(tree_depth, rec, new_size_in_clusters - rec->e_cpos); start = rec->e_blkno + ocfs2_clusters_to_blocks(fs, ocfs2_rec_clusters(tree_depth, rec)); } else { ocfs2_set_rec_clusters(tree_depth, rec, new_size_in_clusters - rec->e_cpos); /* * For a sparse file, we may meet with another * situation here: * The start of the left most extent rec is greater * than the new size we truncate the file to, but the * start of the extent block is less than that size. * In this case, actually all the extent records in * this extent block have been removed. So we have * to remove the extent block also. * In this function, we have to reread the extent list * to see whether the extent block is empty or not. */ ret = ocfs2_malloc_block(fs->fs_io, &buf); if (ret) goto bail; ret = ocfs2_read_extent_block(fs, rec->e_blkno, buf); if (ret) goto bail; el = &((struct ocfs2_extent_block *)buf)->h_list; if (el->l_next_free_rec == 0) { ret = ocfs2_delete_extent_block(fs, rec->e_blkno); if (ret) goto bail; cleanup_rec = 1; } } } if (start) { if (ctxt->free_clusters) ret = ctxt->free_clusters(fs, len, start, ctxt->free_data); else ret = ocfs2_truncate_clusters(fs, rec, ctxt->ino, len, start); if (ret) goto bail; ctxt->new_i_clusters -= len; } func_ret = OCFS2_EXTENT_CHANGED; bail: if (cleanup_rec) memset(rec, 0, sizeof(struct ocfs2_extent_rec)); if (buf) ocfs2_free(&buf); return func_ret; }
/* 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; }