[Ocfs2-tools-devel] [patch 06/11] Modify file extending and
truncating.
tao.ma at oracle.com
tao.ma at oracle.com
Wed Aug 15 10:55:38 PDT 2007
File extending and truncating should use new mechanism of sparse files, so change it accordingly.
===================================================================
--- test.ocfs2-tools.orig/libocfs2/truncate.c 2007-08-16 00:27:42.000000000 -0400
+++ test.ocfs2-tools/libocfs2/truncate.c 2007-08-16 00:36:28.000000000 -0400
@@ -32,6 +32,11 @@
#include "ocfs2.h"
+struct truncate_ctxt {
+ uint64_t new_size_in_clusters;
+ uint32_t new_i_clusters;
+};
+
/*
* Delete and free clusters if needed. This only works with DEPTH_TRAVERSE.
*/
@@ -41,18 +46,23 @@ static int truncate_iterate(ocfs2_filesy
uint64_t ref_blkno, int ref_recno,
void *priv_data)
{
- uint32_t len, new_i_clusters = *(uint32_t *)priv_data;
+ struct truncate_ctxt *ctxt = (struct truncate_ctxt *)priv_data;
+ uint32_t len, 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;
- if ((rec->e_cpos + rec->e_clusters) <= new_i_clusters)
+ if ((rec->e_cpos + ocfs2_rec_clusters(tree_depth, rec)) <=
+ new_size_in_clusters)
return 0;
- if (rec->e_cpos >= new_i_clusters) {
+ 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 = rec->e_clusters;
+ 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
@@ -63,28 +73,120 @@ static int truncate_iterate(ocfs2_filesy
goto bail;
}
- rec->e_blkno = 0;
- rec->e_clusters = 0;
- rec->e_cpos = 0;
+ memset(rec, 0, sizeof(struct ocfs2_extent_rec));
} else {
/* we're truncating into the middle of the rec */
- len = rec->e_cpos + rec->e_clusters;
- len -= new_i_clusters;
- rec->e_clusters = new_i_clusters - rec->e_cpos;
- if (!tree_depth)
+ 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, rec->e_clusters);
+ 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;
+ memset(rec, 0, sizeof(struct ocfs2_extent_rec));
+ }
+ }
}
if (start) {
ret = ocfs2_free_clusters(fs, len, start);
if (ret)
goto bail;
+ ctxt->new_i_clusters -= len;
}
- return OCFS2_EXTENT_CHANGED;
+ func_ret = OCFS2_EXTENT_CHANGED;
bail:
- return OCFS2_EXTENT_ERROR;
+ if (buf)
+ ocfs2_free(&buf);
+ return func_ret;
+}
+
+/*
+ * Zero the area past i_size but still within an allocated
+ * cluster. This avoids exposing nonzero data on subsequent file
+ * extends.
+ */
+static errcode_t ocfs2_zero_tail_for_truncate(ocfs2_cached_inode *ci,
+ uint64_t new_size)
+{
+ errcode_t ret;
+ char *buf = NULL;
+ ocfs2_filesys *fs = ci->ci_fs;
+ uint64_t start_blk, p_blkno, contig_blocks, start_off;
+ int count, byte_counts, bpc = fs->fs_clustersize /fs->fs_blocksize;
+
+ if (new_size >= ci->ci_inode->i_size || new_size == 0)
+ return 0;
+
+ ret = ocfs2_extent_map_init(fs, ci);
+ if (ret)
+ goto out;
+
+ start_blk = new_size / fs->fs_blocksize;
+
+ ret = ocfs2_extent_map_get_blocks(ci, start_blk, 1,
+ &p_blkno, &contig_blocks);
+ if (ret)
+ goto out;
+
+ /* Tail is a hole. */
+ if (!p_blkno)
+ goto out;
+
+ /* calculate the total blocks we need to empty. */
+ count = bpc - (p_blkno & (bpc - 1));
+ ret = ocfs2_malloc_blocks(fs->fs_io, count, &buf);
+ if (ret)
+ goto out;
+
+ ret = io_read_block(fs->fs_io, p_blkno, count, buf);
+ if (ret)
+ goto out;
+
+ /* empty the content after the new_size and within the same cluster. */
+ start_off = new_size % fs->fs_blocksize;
+ byte_counts = count * fs->fs_blocksize - start_off;
+ memset(buf + start_off, 0, byte_counts);
+
+ ret = io_write_block(fs->fs_io, p_blkno, count, buf);
+
+out:
+ ocfs2_extent_map_free(ci);
+ if (buf)
+ ocfs2_free(&buf);
+ return ret;
}
/* XXX care about zeroing new clusters and final partially truncated
@@ -92,62 +194,53 @@ bail:
errcode_t ocfs2_truncate(ocfs2_filesys *fs, uint64_t ino, uint64_t new_i_size)
{
errcode_t ret;
- char *buf;
- struct ocfs2_dinode *di;
- uint32_t new_i_clusters;
- uint64_t new_i_blocks;
-
- ret = ocfs2_malloc_block(fs->fs_io, &buf);
- if (ret)
- return ret;
+ uint32_t new_size_in_clusters;
+ uint64_t new_size_in_blocks;
+ ocfs2_cached_inode *ci = NULL;
- ret = ocfs2_read_inode(fs, ino, buf);
+ ret = ocfs2_read_cached_inode(fs, ino, &ci);
if (ret)
goto out;
- di = (struct ocfs2_dinode *)buf;
- if (di->i_size == new_i_size)
+ if (ci->ci_inode->i_size == new_i_size)
goto out;
- new_i_blocks = ocfs2_blocks_in_bytes(fs, new_i_size);
- new_i_clusters = ocfs2_clusters_in_blocks(fs, new_i_blocks);
+ new_size_in_blocks = ocfs2_blocks_in_bytes(fs, new_i_size);
+ new_size_in_clusters = ocfs2_clusters_in_blocks(fs, new_size_in_blocks);
- if (di->i_clusters < new_i_clusters) {
- ret = ocfs2_extend_allocation(fs, ino,
- new_i_clusters - di->i_clusters);
- if (ret)
- goto out;
+ if (ci->ci_inode->i_size < new_i_size)
+ ret = ocfs2_extend_file(fs, ino, new_i_size);
+ else {
+ struct truncate_ctxt ctxt = {
+ .new_i_clusters = ci->ci_inode->i_clusters,
+ .new_size_in_clusters = new_size_in_clusters,
+ };
- /* the information of dinode has been changed, and we need to
- * read it again.
- */
- ret = ocfs2_read_inode(fs, ino, buf);
- if (ret)
- goto out;
- } else {
- ret = ocfs2_extent_iterate_inode(fs, di,
+ ret = ocfs2_extent_iterate_inode(fs, ci->ci_inode,
OCFS2_EXTENT_FLAG_DEPTH_TRAVERSE,
NULL, truncate_iterate,
- &new_i_clusters);
+ &ctxt);
if (ret)
goto out;
+ ci->ci_inode->i_clusters = ctxt.new_i_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_i_clusters == 0)
- di->id2.i_list.l_tree_depth = 0;
-
- }
+ if (ctxt.new_i_clusters == 0)
+ ci->ci_inode->id2.i_list.l_tree_depth = 0;
- di->i_clusters = new_i_clusters;
- di->i_size = new_i_size;
- ret = ocfs2_write_inode(fs, ino, buf);
+ ret = ocfs2_zero_tail_for_truncate(ci, new_i_size);
+ if (ret)
+ goto out;
+ ci->ci_inode->i_size = new_i_size;
+ ret = ocfs2_write_cached_inode(fs, ci);
+ }
out:
- ocfs2_free(&buf);
-
+ if (ci)
+ ocfs2_free_cached_inode(fs, ci);
return ret;
}
--
More information about the Ocfs2-tools-devel
mailing list