errcode_t ocfs2_extend_allocation(ocfs2_filesys *fs, uint64_t ino, uint32_t new_clusters) { errcode_t ret; ocfs2_cached_inode *ci = NULL; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) goto bail; ret = ocfs2_cached_inode_extend_allocation(ci, new_clusters); if (ret) goto bail; ret = ocfs2_write_cached_inode(fs, ci); bail: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; }
static errcode_t ocfs2_try_to_write_inline_data(ocfs2_cached_inode *ci, void *buf, uint32_t count, uint64_t offset) { int ret; uint64_t end = offset + count; ocfs2_filesys *fs = ci->ci_fs; struct ocfs2_dinode *di = ci->ci_inode; /* Handle inodes which already have inline data 1st. */ if (di->i_dyn_features & OCFS2_INLINE_DATA_FL) { if (ocfs2_size_fits_inline_data(ci->ci_inode, end)) goto do_inline_write; /* * The write won't fit - we have to give this inode an * inline extent list now. */ ret = ocfs2_convert_inline_data_to_extents(ci); if (!ret) ret = OCFS2_ET_CANNOT_INLINE_DATA; goto out; } if (di->i_clusters > 0 || end > ocfs2_max_inline_data_with_xattr(fs->fs_blocksize, di)) return OCFS2_ET_CANNOT_INLINE_DATA; ocfs2_set_inode_data_inline(fs, ci->ci_inode); ci->ci_inode->i_dyn_features |= OCFS2_INLINE_DATA_FL; do_inline_write: ret = ocfs2_inline_data_write(di, buf, count, offset); if (ret) goto out; ret = ocfs2_write_cached_inode(fs, ci); out: return ret; }
/* * Insert an extent into an inode btree. */ errcode_t ocfs2_inode_insert_extent(ocfs2_filesys *fs, uint64_t ino, uint32_t cpos, uint64_t c_blkno, uint32_t clusters, uint16_t flag) { errcode_t ret; ocfs2_cached_inode *ci = NULL; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) goto bail; ret = ocfs2_cached_inode_insert_extent(ci, cpos, c_blkno, clusters, flag); if (ret) goto bail; ret = ocfs2_write_cached_inode(fs, ci); bail: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; }
/* XXX care about zeroing new clusters and final partially truncated * clusters */ errcode_t ocfs2_truncate_full(ocfs2_filesys *fs, uint64_t ino, uint64_t new_i_size, errcode_t (*free_clusters)(ocfs2_filesys *fs, uint32_t len, uint64_t start, void *free_data), void *free_data) { errcode_t ret; uint32_t new_clusters; ocfs2_cached_inode *ci = NULL; ret = ocfs2_read_cached_inode(fs, ino, &ci); if (ret) goto out; /* in case of dio crashed, force do trucate since blocks may already * be allocated */ if (ci->ci_inode->i_flags & cpu_to_le32(OCFS2_DIO_ORPHANED_FL)) { ci->ci_inode->i_flags &= ~cpu_to_le32(OCFS2_DIO_ORPHANED_FL); ci->ci_inode->i_dio_orphaned_slot = 0; new_i_size = ci->ci_inode->i_size; goto truncate; } if (ci->ci_inode->i_size == new_i_size) goto out; if (ci->ci_inode->i_size < new_i_size) { ret = ocfs2_extend_file(fs, ino, new_i_size); goto out; } truncate: if ((S_ISLNK(ci->ci_inode->i_mode) && !ci->ci_inode->i_clusters) || (ci->ci_inode->i_dyn_features & OCFS2_INLINE_DATA_FL)) ret = ocfs2_truncate_inline(fs, ino, new_i_size); else { ret = ocfs2_zero_tail_and_truncate_full(fs, ci, new_i_size, &new_clusters, free_clusters, free_data); if (ret) goto out; ci->ci_inode->i_clusters = new_clusters; /* now all the clusters and extent blocks are freed. * only when the file's content is empty, should the tree depth * change. */ if (new_clusters == 0) ci->ci_inode->id2.i_list.l_tree_depth = 0; ci->ci_inode->i_size = new_i_size; ret = ocfs2_write_cached_inode(fs, ci); } if (!ret && !new_i_size && ci->ci_inode->i_refcount_loc && (ci->ci_inode->i_dyn_features & OCFS2_HAS_REFCOUNT_FL)) ret = ocfs2_detach_refcount_tree(fs, ino, ci->ci_inode->i_refcount_loc); out: if (ci) ocfs2_free_cached_inode(fs, ci); return ret; }
errcode_t ocfs2_convert_inline_data_to_extents(ocfs2_cached_inode *ci) { errcode_t ret; uint32_t bytes, n_clusters; uint64_t p_start; char *inline_data = NULL; struct ocfs2_dinode *di = ci->ci_inode; ocfs2_filesys *fs = ci->ci_fs; uint64_t bpc = fs->fs_clustersize/fs->fs_blocksize; unsigned int new_size; if (di->i_size) { ret = ocfs2_malloc_block(fs->fs_io, &inline_data); if (ret) goto out; ret = ocfs2_inline_data_read(di, inline_data, fs->fs_blocksize, 0, &bytes); if (ret) goto out; } ocfs2_dinode_new_extent_list(fs, di); di->i_dyn_features &= ~OCFS2_INLINE_DATA_FL; ret = ocfs2_new_clusters(fs, 1, 1, &p_start, &n_clusters); if (ret || n_clusters == 0) goto out; ret = empty_blocks(fs, p_start, bpc); if (ret) goto out; if (di->i_size) { if (S_ISDIR(di->i_mode)) { if (ocfs2_supports_dir_trailer(fs)) new_size = ocfs2_dir_trailer_blk_off(fs); else new_size = fs->fs_blocksize; ocfs2_expand_last_dirent(inline_data, di->i_size, new_size); if (ocfs2_supports_dir_trailer(fs)) ocfs2_init_dir_trailer(fs, di, p_start, inline_data); di->i_size = fs->fs_blocksize; ret = ocfs2_write_dir_block(fs, di, p_start, inline_data); } else ret = io_write_block(fs->fs_io, p_start, 1, inline_data); if (ret) goto out; } ret = ocfs2_cached_inode_insert_extent(ci, 0, p_start, n_clusters, 0); if (ret) goto out; ret = ocfs2_write_cached_inode(fs, ci); out: if (inline_data) ocfs2_free(&inline_data); return ret; }
static errcode_t ocfs2_file_block_write(ocfs2_cached_inode *ci, void *buf, uint32_t count, uint64_t offset, uint32_t *wrote) { ocfs2_filesys *fs = ci->ci_fs; errcode_t ret = 0; char *ptr = (char *) buf; uint32_t wanted_blocks; uint64_t contig_blocks; uint64_t v_blkno; uint64_t p_blkno, p_start, p_end; uint64_t begin_blocks = 0, end_blocks = 0; uint32_t tmp; uint64_t num_blocks; int bs_bits = OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; uint64_t ino = ci->ci_blkno; uint32_t n_clusters, cluster_begin, cluster_end; uint64_t bpc = fs->fs_clustersize/fs->fs_blocksize; int insert = 0; uint16_t extent_flags = 0; /* o_direct requires aligned io */ tmp = fs->fs_blocksize - 1; if ((count & tmp) || (offset & (uint64_t)tmp) || ((unsigned long)ptr & tmp)) return OCFS2_ET_INVALID_ARGUMENT; wanted_blocks = count >> bs_bits; v_blkno = offset >> bs_bits; *wrote = 0; num_blocks = (ci->ci_inode->i_size + fs->fs_blocksize - 1) >> bs_bits; if (v_blkno >= num_blocks) return 0; if (v_blkno + wanted_blocks > num_blocks) wanted_blocks = (uint32_t) (num_blocks - v_blkno); while(wanted_blocks) { ret = ocfs2_extent_map_get_blocks(ci, v_blkno, 1, &p_blkno, &contig_blocks, &extent_flags); if (ret) return ret; if (contig_blocks > wanted_blocks) contig_blocks = wanted_blocks; begin_blocks = 0; end_blocks = 0; p_end = 0; if (!p_blkno) { /* * We meet with a hole here, so we allocate clusters * and empty the both ends in case. * * We will postpone the extent insertion after we * successfully write the extent block, so that and * problems happens in block writing would not affect * the file. */ cluster_begin = ocfs2_blocks_to_clusters(fs, v_blkno); cluster_end = ocfs2_blocks_to_clusters(fs, v_blkno + contig_blocks -1); n_clusters = cluster_end - cluster_begin + 1; ret = ocfs2_new_clusters(fs, 1, n_clusters, &p_start, &n_clusters); if (ret || n_clusters == 0) return ret; begin_blocks = v_blkno & (bpc - 1); p_blkno = p_start + begin_blocks; contig_blocks = n_clusters * bpc - begin_blocks; if (contig_blocks > wanted_blocks) { end_blocks = contig_blocks - wanted_blocks; contig_blocks = wanted_blocks; p_end = p_blkno + wanted_blocks; } insert = 1; } else if (extent_flags & OCFS2_EXT_UNWRITTEN) { begin_blocks = v_blkno & (bpc - 1); p_start = p_blkno - begin_blocks; p_end = p_blkno + wanted_blocks; end_blocks = (p_end & (bpc - 1)) ? bpc - (p_end & (bpc - 1 )) : 0; } if (begin_blocks) { /* * The user don't write the first blocks, * so we have to empty them. */ ret = empty_blocks(fs, p_start, begin_blocks); if (ret) return ret; } if (end_blocks) { /* * we don't need to write that many blocks, * so empty the blocks at the bottom. */ ret = empty_blocks(fs, p_end, end_blocks); if (ret) return ret; } ret = io_write_block(fs->fs_io, p_blkno, contig_blocks, ptr); if (ret) return ret; if (insert) { ret = ocfs2_cached_inode_insert_extent(ci, ocfs2_blocks_to_clusters(fs,v_blkno), p_start, n_clusters, 0); 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_start); return ret; } /* save up what we have done. */ ret = ocfs2_write_cached_inode(fs, ci); if (ret) return ret; ret = ocfs2_extent_map_get_blocks(ci, v_blkno, 1, &p_blkno, NULL, NULL); /* now we shouldn't find a hole. */ if (!p_blkno || p_blkno != p_start + begin_blocks) ret = OCFS2_ET_INTERNAL_FAILURE; if (ret) return ret; insert = 0; } else if (extent_flags & OCFS2_EXT_UNWRITTEN) { cluster_begin = ocfs2_blocks_to_clusters(fs, v_blkno); cluster_end = ocfs2_blocks_to_clusters(fs, v_blkno + contig_blocks -1); n_clusters = cluster_end - cluster_begin + 1; ret = ocfs2_mark_extent_written(fs, ci->ci_inode, cluster_begin, n_clusters, p_blkno & ~(bpc - 1)); if (ret) return ret; ocfs2_free_cached_inode(fs, ci); ocfs2_read_cached_inode(fs,ino, &ci); } *wrote += (contig_blocks << bs_bits); wanted_blocks -= contig_blocks; if (wanted_blocks) { ptr += (contig_blocks << bs_bits); v_blkno += (uint64_t)contig_blocks; } else { if (*wrote + offset > ci->ci_inode->i_size) *wrote = (uint32_t) (ci->ci_inode->i_size - offset); /* break */ } } 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; }