/* * Mark the already-existing extent at cpos as written for len clusters. * * If the existing extent is larger than the request, initiate a * split. An attempt will be made at merging with adjacent extents. * */ int ocfs2_mark_extent_written(ocfs2_filesys *fs, struct ocfs2_dinode *di, uint32_t cpos, uint32_t len, uint64_t p_blkno) { struct ocfs2_extent_tree et; if (!ocfs2_writes_unwritten_extents(OCFS2_RAW_SB(fs->fs_super))) return OCFS2_ET_UNSUPP_FEATURE; ocfs2_init_dinode_extent_tree(&et, fs, (char *)di, di->i_blkno); return ocfs2_change_extent_flag(fs, &et, cpos, len, p_blkno, 0, OCFS2_EXT_UNWRITTEN); }
/* 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; }
errcode_t ocfs2_allocate_unwritten_extents(ocfs2_filesys *fs, uint64_t ino, uint64_t offset, uint64_t len) { errcode_t ret = 0; uint32_t n_clusters = 0, cpos; uint64_t p_blkno = 0, v_blkno, v_end, contig_blocks, wanted_blocks; ocfs2_cached_inode *ci = NULL; if (!(fs->fs_flags & OCFS2_FLAG_RW)) return OCFS2_ET_RO_FILESYS; if (!ocfs2_writes_unwritten_extents(OCFS2_RAW_SB(fs->fs_super))) return OCFS2_ET_RO_UNSUPP_FEATURE; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) goto out; if (!(ci->ci_inode->i_flags & OCFS2_VALID_FL)) return OCFS2_ET_INODE_NOT_VALID; if (ci->ci_inode->i_flags & OCFS2_SYSTEM_FL) return OCFS2_ET_INVALID_ARGUMENT; if (!S_ISREG(ci->ci_inode->i_mode)) return OCFS2_ET_INVALID_ARGUMENT; v_blkno = offset / fs->fs_blocksize; v_end = (offset + len - 1) / fs->fs_blocksize; while (v_blkno <= v_end) { ret = ocfs2_extent_map_get_blocks(ci, v_blkno, 1, &p_blkno, &contig_blocks, NULL); if (ret) continue; if (p_blkno) { v_blkno += contig_blocks; continue; } /* * There is a hole, so we have to allocate the space and * insert the unwritten extents. */ wanted_blocks = ocfs2_min(contig_blocks, v_end - v_blkno + 1); n_clusters = ocfs2_clusters_in_blocks(fs, wanted_blocks); ret = ocfs2_new_clusters(fs, 1, n_clusters, &p_blkno, &n_clusters); if (ret || n_clusters == 0) break; cpos = ocfs2_blocks_to_clusters(fs, v_blkno); ret = ocfs2_cached_inode_insert_extent(ci, cpos, p_blkno, n_clusters, OCFS2_EXT_UNWRITTEN); if (ret) { /* * XXX: We don't wan't to overwrite the error * from insert_extent(). But we probably need * to BE LOUDLY UPSET. */ ocfs2_free_clusters(fs, n_clusters, p_blkno); goto out; } /* save up what we have done. */ ret = ocfs2_write_cached_inode(fs, ci); if (ret) goto out; v_blkno = ocfs2_clusters_to_blocks(fs, cpos + n_clusters); } if (ci->ci_inode->i_size <= offset + len) { ci->ci_inode->i_size = offset + len; ret = ocfs2_write_cached_inode(fs, ci); } out: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; }