[Ocfs2-tools-devel] [patch 06/11] Modify file extending and truncating.take 2

tao.ma at oracle.com tao.ma at oracle.com
Wed Aug 22 17:07:46 PDT 2007


File extending and truncating should use new mechanism of sparse files, so change it accordingly.
===================================================================
--- quilt.ocfs2-tools.orig/libocfs2/truncate.c	2007-08-21 06:38:06.000000000 -0400
+++ quilt.ocfs2-tools/libocfs2/truncate.c	2007-08-22 05:51:42.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,115 @@ 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;
+
+	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:
+	if (buf)
+		ocfs2_free(&buf);
+	return ret;
 }
 
 /* XXX care about zeroing new clusters and final partially truncated 
@@ -92,62 +189,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