[Ocfs2-tools-devel] [PATCH 3/5] Add "Set sparse flag" to ocfs2-tools, take 1

Mark Fasheh mark.fasheh at oracle.com
Wed Oct 17 17:57:23 PDT 2007


On Wed, Oct 17, 2007 at 05:26:14PM +0800, tao.ma wrote:
> "Set sparse" is used when we want to add "sparse" features for an
> old-formatted OCFS2 volume. Now the user can use
> "--fs-features=sparse" to add this feature to those volumes and
> enable the new "sparse" feature.
> 
> Signed-off-by: Tao Ma <tao.ma at oracle.com>
> ---
>  libocfs2/include/feature_string.h |    8 --
>  libocfs2/include/ocfs2.h          |    8 ++
>  tunefs.ocfs2/Makefile             |    2 +-
>  tunefs.ocfs2/features.c           |   59 ++++++++++++++++
>  tunefs.ocfs2/sparse_file.c        |  137 +++++++++++++++++++++++++++++++++++++
>  tunefs.ocfs2/tunefs.c             |   61 +++++++++++++++--
>  tunefs.ocfs2/tunefs.h             |    8 ++
>  7 files changed, 269 insertions(+), 14 deletions(-)
>  create mode 100644 tunefs.ocfs2/features.c
> 

Again, I really like how you're keeping the feature check / parsing code
seperate. This should make it easier for us to add support for other
features in the future.


> +/*
> + * Check whether we can add or remove a feature.
> + *
> + * Currently, we only handle "sparse files".
> + * More feature check may be added if we want to
> + * support more options in tunefs.ocfs2.
> + */
> +errcode_t feature_check(ocfs2_filesys *fs)
> +{
> +	errcode_t ret = 0;
> +
> +	if ((opts.set_feature.incompat &
> +	     OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC) &&
> +	    ocfs2_sparse_alloc(OCFS2_RAW_SB(fs->fs_super)))
> +		ret = 1;
> +
> +	return ret;
> +}

Shouldn't feature_check() fail if the user asks for a feature which tunefs
can't handle yet?


> +errcode_t update_feature(ocfs2_filesys *fs)
> +{
> +	errcode_t ret = 0;
> +
> +	if (opts.set_feature.incompat & OCFS2_FEATURE_INCOMPAT_SPARSE_ALLOC)
> +		ret = set_sparse_file_flag(fs, opts.progname);
> +
> +	return ret;
> +}
> diff --git a/tunefs.ocfs2/sparse_file.c b/tunefs.ocfs2/sparse_file.c
> index 4877408..3499ae4 100644
> --- a/tunefs.ocfs2/sparse_file.c
> +++ b/tunefs.ocfs2/sparse_file.c
> @@ -463,3 +463,140 @@ bail:
>  		ocfs2_free(&buf);
>  	return ret;
>  }
> +
> +static errcode_t fill_file_end(ocfs2_filesys *fs, struct ocfs2_dinode *di)
> +{
> +	errcode_t ret = 0;
> +	char *buf = NULL, *data_buf = NULL;;
> +	struct ocfs2_extent_rec *rec = NULL;
> +	struct ocfs2_extent_list *el = NULL;
> +	struct ocfs2_extent_block *eb = NULL;
> +	uint64_t blkno, len_in_blk, last_cluster_start;
> +	uint16_t offset, blk_off, bpc = fs->fs_clustersize / fs->fs_blocksize;
> +
> +	ret = ocfs2_malloc_block(fs->fs_io, &data_buf);
> +	if (ret)
> +		goto bail;
> +
> +	el = &di->id2.i_list;
> +	if (el->l_tree_depth > 0) {
> +		ret = ocfs2_malloc_block(fs->fs_io, &buf);
> +		if (ret)
> +			goto bail;
> +
> +		ret = ocfs2_read_extent_block(fs, di->i_last_eb_blk, buf);
> +		if (ret)
> +			goto bail;
> +
> +		eb = (struct ocfs2_extent_block *)buf;
> +		el = &eb->h_list;
> +	}

Use something like ocfs2_get_clusters for this instead...

Actually, doesn't ocfs2_zero_tail_for_truncate() do most of this for you?


> +	if (!el->l_next_free_rec)
> +		goto bail;
> +
> +	rec = &el->l_recs[el->l_next_free_rec - 1];
> +
> +	offset = di->i_size % fs->fs_blocksize;
> +	len_in_blk = (di->i_size + fs->fs_blocksize - 1) / fs->fs_blocksize;
> +	blk_off = len_in_blk % bpc;
> +
> +	/*
> +	 * Although in a non-sparse file system a extent record doesn't have
> +	 * the extent flag, 2-bytes are still enough to store the length of
> +	 * the extent record. And the disk storage of OCFS2 is little endian,
> +	 * so we can use e_leaf_cluster here safely.
> +	 */
> +	last_cluster_start = rec->e_blkno + (rec->e_leaf_clusters - 1) * bpc;
> +	blkno = last_cluster_start + blk_off - 1;
> +
> +	/* first emtpy the extra byte in the last used block. */
> +	ret = io_read_block(fs->fs_io, blkno, 1,data_buf);
> +	if (ret)
> +		goto bail;
> +
> +	memset(data_buf + offset, 0, fs->fs_blocksize - offset);
> +	ret = io_write_block(fs->fs_io, blkno, 1, data_buf);
> +	if (ret)
> +		goto bail;
> +
> +	/* Then empty the extra blocks in the last cluster. */
> +	memset(data_buf, 0, fs->fs_blocksize);
> +	while (++blkno % bpc != 0) {
> +		ret = io_write_block(fs->fs_io, blkno, 1, data_buf);
> +		if (ret)
> +			goto bail;
> +	}
> +
> +bail:
> +	if (data_buf)
> +		ocfs2_free(&data_buf);
> +	if (buf)
> +		ocfs2_free(&buf);
> +
> +	return ret;
> +}
> +
> +errcode_t set_sparse_file_flag(ocfs2_filesys *fs, char *progname)
> +{
> +	errcode_t ret;
> +	uint64_t blkno;
> +	char *buf;
> +	struct ocfs2_dinode *di;
> +	ocfs2_inode_scan *scan;
> +	struct ocfs2_super_block *super = OCFS2_RAW_SB(fs->fs_super);
> +
> +	ret = ocfs2_malloc_block(fs->fs_io, &buf);
> +	if (ret)
> +		goto out;
> +
> +	di = (struct ocfs2_dinode *)buf;
> +
> +	ret = ocfs2_open_inode_scan(fs, &scan);
> +	if (ret) {
> +		com_err(progname, ret, "while opening inode scan");
> +		goto out_free;
> +	}
> +
> +	for(;;) {
> +		ret = ocfs2_get_next_inode(scan, &blkno, buf);
> +		if (ret) {
> +			com_err(progname, ret,
> +				"while getting next inode");
> +			goto out_close_scan;
> +		}
> +		if (blkno == 0)
> +			break;
> +
> +		if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE,
> +			    strlen(OCFS2_INODE_SIGNATURE)))
> +			continue;
> +
> +		ocfs2_swap_inode_to_cpu(di);
> +
> +		if (di->i_fs_generation != fs->fs_super->i_fs_generation)
> +			continue;
> +
> +		if (di->i_flags & OCFS2_SYSTEM_FL)
> +			continue;
> +
> +		if (S_ISREG(di->i_mode)) {
> +			if (!(di->i_size % fs->fs_clustersize))
> +				continue;
> +
> +			ret = fill_file_end(fs, di);
> +			if (ret)
> +				goto out_close_scan;

We should also truncate all clusters past i_size. The file system might have
left them allocated without the proper i_size change if there was a failed
extend.

The only complication is this check in ocfs2_truncate():

	if (ci->ci_inode->i_size == new_i_size)
		goto out;

We don't actually want to change i_size from what it is, but we want to do
the iteration over 

Maybe we should abstract out the truncate part of ocfs2_truncate() into a
seperate function and just have this code call that? It looks like it'd
call ocfs2_zero_tail_for_truncate() for you.
	--Mark

--
Mark Fasheh
Senior Software Developer, Oracle
mark.fasheh at oracle.com



More information about the Ocfs2-tools-devel mailing list