[Ocfs2-tools-devel] [PATCH 2/7] Add "list-sparse" option in tunefs.ocfs2, take 3

Tao Ma tao.ma at oracle.com
Sun Nov 4 23:42:13 PST 2007


Modification from V2 to V3:
1. Move the hole judgment into "!p_cluster" to avoid concatenate the last
   extent length in a very large file.
2. Change OCFS2_MAX_FILENAME_LEN to PATH_MAX in dir iteration.
3. Move io_cache out of list sparse.

ocfs2 now support sparse files, so add a new option "list-sparse"
to show all the sparse files and their holes in the volume.

Signed-off-by: Tao Ma <tao.ma at oracle.com>
---
 libocfs2/extent_map.c      |   10 +-
 libocfs2/include/ocfs2.h   |    5 +
 tunefs.ocfs2/Makefile      |    2 +-
 tunefs.ocfs2/sparse_file.c |  402 ++++++++++++++++++++++++++++++++++++++++++++
 tunefs.ocfs2/tunefs.c      |   48 +++++-
 tunefs.ocfs2/tunefs.h      |    8 +
 6 files changed, 466 insertions(+), 9 deletions(-)
 create mode 100644 tunefs.ocfs2/sparse_file.c

diff --git a/libocfs2/extent_map.c b/libocfs2/extent_map.c
index 4db0c13..c0b1b2c 100644
--- a/libocfs2/extent_map.c
+++ b/libocfs2/extent_map.c
@@ -126,11 +126,11 @@ out:
 	return ret;
 }
 
-static errcode_t ocfs2_get_clusters(ocfs2_cached_inode *cinode,
-				    uint32_t v_cluster,
-				    uint32_t *p_cluster,
-				    uint32_t *num_clusters,
-				    uint16_t *extent_flags)
+errcode_t ocfs2_get_clusters(ocfs2_cached_inode *cinode,
+			     uint32_t v_cluster,
+			     uint32_t *p_cluster,
+			     uint32_t *num_clusters,
+			     uint16_t *extent_flags)
 {
 	int i;
 	uint16_t flags = 0;
diff --git a/libocfs2/include/ocfs2.h b/libocfs2/include/ocfs2.h
index 6ee68a4..ab84119 100644
--- a/libocfs2/include/ocfs2.h
+++ b/libocfs2/include/ocfs2.h
@@ -255,6 +255,11 @@ errcode_t ocfs2_extent_map_get_blocks(ocfs2_cached_inode *cinode,
 				      uint64_t *p_blkno,
 				      uint64_t *ret_count,
 				      uint16_t *extent_flags);
+errcode_t ocfs2_get_clusters(ocfs2_cached_inode *cinode,
+			     uint32_t v_cluster,
+			     uint32_t *p_cluster,
+			     uint32_t *num_clusters,
+			     uint16_t *extent_flags);
 int ocfs2_find_leaf(ocfs2_filesys *fs, struct ocfs2_dinode *di,
 		    uint32_t cpos, char **leaf_buf);
 int ocfs2_search_extent_list(struct ocfs2_extent_list *el, uint32_t v_cluster);
diff --git a/tunefs.ocfs2/Makefile b/tunefs.ocfs2/Makefile
index 6bbdbd4..d41f961 100644
--- a/tunefs.ocfs2/Makefile
+++ b/tunefs.ocfs2/Makefile
@@ -31,7 +31,7 @@ DEFINES = -DOCFS2_FLAT_INCLUDES -DVERSION=\"$(VERSION)\" -DO2DLM_FLAT_INCLUDES -
 
 MANS = tunefs.ocfs2.8
 
-CFILES = tunefs.c query.c remove_slot.c
+CFILES = tunefs.c query.c remove_slot.c sparse_file.c
 HFILES = tunefs.h
 
 OBJS = $(subst .c,.o,$(CFILES))
diff --git a/tunefs.ocfs2/sparse_file.c b/tunefs.ocfs2/sparse_file.c
new file mode 100644
index 0000000..040014c
--- /dev/null
+++ b/tunefs.ocfs2/sparse_file.c
@@ -0,0 +1,402 @@
+/*
+ * sparse_files.c
+ *
+ * source file of sparse files functions for tunefs.
+ *
+ * Copyright (C) 2007 Oracle.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ *
+ */
+
+#include <kernel-rbtree.h>
+#include <tunefs.h>
+#include <assert.h>
+
+struct multi_link_file {
+	struct rb_node br_node;
+	uint64_t blkno;
+	uint32_t clusters;
+};
+
+struct list_ctxt {
+	ocfs2_filesys *fs;
+	uint64_t total_clusters;
+	char file_name[OCFS2_MAX_FILENAME_LEN];
+	int file_name_len;
+	uint64_t ino;
+	uint32_t file_hole_len;
+	int duplicated;
+	void (*func)(void *priv_data,
+		     uint32_t hole_start,
+		     uint32_t hole_len);
+	struct rb_root multi_link_files;
+};
+
+static void inline empty_multi_link_files(struct list_ctxt *ctxt)
+{
+	struct multi_link_file *lf;
+	struct rb_node *node;
+
+	while ((node = rb_first(&ctxt->multi_link_files)) != NULL) {
+		lf = rb_entry(node, struct multi_link_file, br_node);
+
+		rb_erase(&lf->br_node, &ctxt->multi_link_files);
+		ocfs2_free(&lf);
+	}
+}
+
+static struct multi_link_file *multi_link_file_lookup(struct list_ctxt *ctxt,
+						      uint64_t blkno)
+{
+	struct rb_node *p = ctxt->multi_link_files.rb_node;
+	struct multi_link_file *file;
+
+	while (p) {
+		file = rb_entry(p, struct multi_link_file, br_node);
+		if (blkno < file->blkno) {
+			p = p->rb_left;
+		} else if (blkno > file->blkno) {
+			p = p->rb_right;
+		} else
+			return file;
+	}
+
+	return NULL;
+}
+
+static errcode_t multi_link_file_insert(struct list_ctxt *ctxt,
+					uint64_t blkno, uint32_t clusters)
+{
+	errcode_t ret;
+	struct multi_link_file *file = NULL;
+	struct rb_node **p = &ctxt->multi_link_files.rb_node;
+	struct rb_node *parent = NULL;
+
+	while (*p) {
+		parent = *p;
+		file = rb_entry(parent, struct multi_link_file, br_node);
+		if (blkno < file->blkno) {
+			p = &(*p)->rb_left;
+			file = NULL;
+		} else if (blkno > file->blkno) {
+			p = &(*p)->rb_right;
+			file = NULL;
+		} else
+			assert(0);  /* We shouldn't find it. */
+	}
+
+	ret = ocfs2_malloc0(sizeof(struct multi_link_file), &file);
+	if (ret)
+		goto out;
+
+	file->blkno = blkno;
+	file->clusters = clusters;
+
+	rb_link_node(&file->br_node, parent, p);
+	rb_insert_color(&file->br_node, &ctxt->multi_link_files);
+	ret = 0;
+
+out:
+	return ret;
+}
+
+static errcode_t get_total_free_clusters(ocfs2_filesys *fs, uint32_t *clusters)
+{
+	errcode_t ret;
+	uint64_t blkno;
+	char *buf = NULL;
+	struct ocfs2_dinode *di = NULL;
+	char file_name[OCFS2_MAX_FILENAME_LEN];
+	struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super);
+
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret)
+		goto bail;
+
+	snprintf(file_name, sizeof(file_name),
+		 ocfs2_system_inodes[GLOBAL_BITMAP_SYSTEM_INODE].si_name);
+
+	ret = ocfs2_lookup(fs, sb->s_system_dir_blkno, file_name,
+			   strlen(file_name), NULL, &blkno);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_read_inode(fs, blkno, buf);
+	if (ret)
+		goto bail;
+
+	di = (struct ocfs2_dinode *)buf;
+
+	if (clusters)
+		*clusters = di->id1.bitmap1.i_total - di->id1.bitmap1.i_used;
+bail:
+	if (buf)
+		ocfs2_free(&buf);
+	return ret;
+}
+
+static void list_sparse_iterate(void *priv_data,
+				uint32_t hole_start,
+				uint32_t hole_len)
+{
+	struct list_ctxt *ctxt =
+			(struct list_ctxt *)priv_data;
+
+	ctxt->file_hole_len += hole_len;
+}
+
+/*
+ * Iterate a file and call "func" when we meet with a hole.
+ */
+static errcode_t iterate_file(ocfs2_filesys *fs,
+			      struct ocfs2_dinode *di,
+			      void (*func)(void *priv_data,
+					   uint32_t hole_start,
+					   uint32_t hole_len),
+			      void *priv_data)
+{
+	errcode_t ret;
+	uint32_t clusters, v_cluster = 0, p_cluster, num_clusters;
+	uint16_t extent_flags;
+	ocfs2_cached_inode *ci = NULL;
+
+	clusters = (di->i_size + fs->fs_clustersize -1 ) /
+			fs->fs_clustersize;
+
+	ret = ocfs2_read_cached_inode(fs, di->i_blkno, &ci);
+	if (ret)
+		goto bail;
+
+	while (v_cluster < clusters) {
+		ret = ocfs2_get_clusters(ci,
+					 v_cluster, &p_cluster,
+					 &num_clusters, &extent_flags);
+		if (ret)
+			goto bail;
+
+		if (!p_cluster) {
+			/*
+			 * If the tail of the file is a hole, let the
+			 * hole length only cover the last i_size.
+			 */
+			if (v_cluster + num_clusters == UINT32_MAX)
+				num_clusters = clusters - v_cluster;
+
+			if (func)
+				func(priv_data, v_cluster, num_clusters);
+		}
+
+		v_cluster += num_clusters;
+	}
+
+bail:
+	if (ci)
+		ocfs2_free_cached_inode(fs, ci);
+	return ret;
+}
+
+/*
+ * For a regular file, we will iterate it and calculate all the
+ * hole in it and store the information in ctxt->file_hole_len.
+ *
+ * for the file which has i_links_count > 1, we only iterate it
+ * when we meet it the first time and record it into multi_link_file
+ * tree, so the next time we will just search the tree and set
+ * file_hole_len accordingly.
+ */
+static errcode_t list_sparse_file(ocfs2_filesys *fs,
+				  struct ocfs2_dinode *di,
+				  struct list_ctxt *ctxt)
+{
+	errcode_t ret = 0;
+	struct multi_link_file *file = NULL;
+
+	assert(S_ISREG(di->i_mode));
+
+	ctxt->file_hole_len = 0;
+
+	if (di->i_links_count > 1) {
+		file = multi_link_file_lookup(ctxt, di->i_blkno);
+
+		if (file) {
+			ctxt->file_hole_len = file->clusters;
+			ctxt->duplicated = 1;
+			goto print;
+		}
+	}
+
+	ret = iterate_file(fs, di, ctxt->func, ctxt);
+	if (ret)
+		goto bail;
+
+	if ( di->i_links_count > 1) {
+		ret = multi_link_file_insert(ctxt,
+					     di->i_blkno, ctxt->file_hole_len);
+		if (ret)
+			goto bail;
+	}
+
+print:
+	if (ctxt->file_hole_len > 0)
+		printf("%"PRIu64"\t%u\t\t%s\n", di->i_blkno,
+			ctxt->file_hole_len, ctxt->file_name);
+
+bail:
+	return ret;
+}
+
+static int list_sparse_func(struct ocfs2_dir_entry *dirent,
+			    int offset, int blocksize,
+			    char *buf, void *priv_data)
+{
+	errcode_t ret;
+	char *di_buf = NULL;
+	struct ocfs2_dinode *di = NULL;
+	char file_name[OCFS2_MAX_FILENAME_LEN];
+	int file_name_len = 0;
+	struct list_ctxt *ctxt = (struct list_ctxt *)priv_data;
+	ocfs2_filesys *fs = ctxt->fs;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &di_buf);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_read_inode(fs, (uint64_t)dirent->inode, di_buf);
+	if (ret)
+		goto bail;
+
+	di = (struct ocfs2_dinode *)di_buf;
+
+	/* currently, we only handle directories and regular files. */
+	if (!S_ISDIR(di->i_mode) && !S_ISREG(di->i_mode))
+		return 0;
+
+	strcpy(file_name, ctxt->file_name);
+	file_name_len = ctxt->file_name_len;
+
+	if (dirent->name_len + ctxt->file_name_len + 1 >= PATH_MAX)
+		goto bail;
+
+	strncat(ctxt->file_name,
+		dirent->name,dirent->name_len);
+	ctxt->file_name_len += dirent->name_len;
+
+	if (S_ISDIR(di->i_mode)) {
+		strcat(ctxt->file_name,"/");
+		ctxt->file_name_len++;
+		ret = ocfs2_dir_iterate(fs, di->i_blkno,
+					OCFS2_DIRENT_FLAG_EXCLUDE_DOTS,
+					NULL, list_sparse_func, ctxt);
+		if (ret)
+			goto bail;
+	} else {
+		ctxt->duplicated = 0;
+		ret = list_sparse_file(fs, di, ctxt);
+		if (ret)
+			goto bail;
+		if (!ctxt->duplicated)
+			ctxt->total_clusters +=
+					 ctxt->file_hole_len;
+	}
+
+bail:
+	strcpy(ctxt->file_name, file_name);
+	ctxt->file_name_len = file_name_len;
+
+	if (di_buf)
+		ocfs2_free(&di_buf);
+
+	return ret;
+}
+
+/*
+ * list_sparse will iterate from "/" and all the orphan_dirs recursively
+ * and print out all the hole information on the screen.
+ *
+ * We will use ocfs2_dir_iterate to iterate from the very beginning and
+ * tunefs_iterate_func will handle every dir entry.
+ */
+errcode_t list_sparse(ocfs2_filesys *fs)
+{
+	int i;
+	errcode_t ret;
+	uint64_t blkno;
+	char file_name[OCFS2_MAX_FILENAME_LEN];
+	struct list_ctxt ctxt;
+	struct ocfs2_super_block *sb = OCFS2_RAW_SB(fs->fs_super);
+	uint32_t total_holes = 0, free_clusters = 0;
+
+	printf("Iterating from the root directory:\n");
+	printf("#inode\tcluster nums\tfilepath\n");
+
+	memset(&ctxt, 0, sizeof(ctxt));
+	ctxt.fs = fs;
+	ctxt.multi_link_files = RB_ROOT;
+	ctxt.func = list_sparse_iterate;
+	sprintf(ctxt.file_name, "/");
+	ctxt.file_name_len = strlen(ctxt.file_name);
+	ret = ocfs2_dir_iterate(fs, sb->s_root_blkno,
+				OCFS2_DIRENT_FLAG_EXCLUDE_DOTS,
+				NULL, list_sparse_func, &ctxt);
+	if (ret)
+		goto bail;
+
+	printf("Total hole clusters in /: %u\n", ctxt.total_clusters);
+	total_holes += ctxt.total_clusters;
+
+	printf("Iterating orphan_dirs:\n");
+
+	for (i = 0; i < sb->s_max_slots; i++) {
+		snprintf(file_name, sizeof(file_name),
+		ocfs2_system_inodes[ORPHAN_DIR_SYSTEM_INODE].si_name, i);
+
+		ret = ocfs2_lookup(fs, sb->s_system_dir_blkno, file_name,
+				   strlen(file_name), NULL, &blkno);
+		if (ret)
+			goto bail;
+
+		empty_multi_link_files(&ctxt);
+		memset(&ctxt, 0, sizeof(ctxt));
+		ctxt.fs = fs;
+		ctxt.multi_link_files = RB_ROOT;
+		ctxt.func = list_sparse_iterate;
+		sprintf(ctxt.file_name, "%s/", file_name);
+		ctxt.file_name_len = strlen(ctxt.file_name);
+		ret = ocfs2_dir_iterate(fs, blkno,
+					OCFS2_DIRENT_FLAG_EXCLUDE_DOTS,
+					NULL, list_sparse_func, &ctxt);
+		if (ret)
+			goto bail;
+
+		printf("Total hole clusters in %s: %u\n",
+			file_name, ctxt.total_clusters);
+		total_holes += ctxt.total_clusters;
+	}
+
+	printf("Total hole clusters in the volume: %u\n\n", total_holes);
+
+	/* Get the total free bits in the global_bitmap. */
+	ret = get_total_free_clusters(fs, &free_clusters);
+	if (ret)
+		goto bail;
+
+	printf("Total free %u clusters in the volume.\n", free_clusters);
+
+bail:
+	empty_multi_link_files(&ctxt);
+	return ret;
+}
diff --git a/tunefs.ocfs2/tunefs.c b/tunefs.ocfs2/tunefs.c
index 85070f7..ed0a18f 100644
--- a/tunefs.ocfs2/tunefs.c
+++ b/tunefs.ocfs2/tunefs.c
@@ -34,7 +34,8 @@ static void usage(const char *progname)
 {
 	fprintf(stderr, "usage: %s [-J journal-options] [-L volume-label]\n"
 			"\t\t[-M mount-type] [-N number-of-node-slots] [-Q query-fmt]\n"
-			"\t\t[-qSUvV] [--backup-super] device [blocks-count]\n",
+			"\t\t[-qSUvV] [--backup-super] [--list-sparse]\n"
+			"\t\tdevice [blocks-count]\n",
 			progname);
 	exit(0);
 }
@@ -195,6 +196,7 @@ static void get_options(int argc, char **argv)
 		{ "uuid-reset", 0, 0, 'U'},
 		{ "mount", 1, 0, 'M' },
 		{ "backup-super", 0, 0, BACKUP_SUPER_OPTION },
+		{ "list-sparse", 0, 0, LIST_SPARSE_FILES },
 		{ 0, 0, 0, 0}
 	};
 
@@ -293,6 +295,10 @@ static void get_options(int argc, char **argv)
 			opts.backup_super = 1;
 			break;
 
+		case LIST_SPARSE_FILES:
+			opts.list_sparse = 1;
+			break;
+
 		default:
 			usage(opts.progname);
 			break;
@@ -304,7 +310,8 @@ static void get_options(int argc, char **argv)
 	 */
 	if (opts.backup_super &&
 	    (opts.vol_label || opts.num_slots ||
-	     opts.mount || opts.jrnl_size || resize)) {
+	     opts.mount || opts.jrnl_size || resize ||
+	     opts.list_sparse)) {
 		com_err(opts.progname, 0, "Cannot backup superblock"
 			" along with other tasks");
 		exit(1);
@@ -319,6 +326,18 @@ static void get_options(int argc, char **argv)
 		exit(0);
 	}
 
+	/*
+	 * We don't allow list-sparse to be coexist with other tunefs
+	 * options to keep things simple.
+	 */
+	if (opts.list_sparse &&
+	    (opts.vol_label || opts.num_slots ||
+	     opts.mount || opts.jrnl_size || resize || opts.backup_super)) {
+		com_err(opts.progname, 0, "Cannot list sparse files"
+			" along with other tasks");
+		exit(1);
+	}
+
 	if (show_version)
 		exit(0);
 
@@ -1355,6 +1374,15 @@ int main(int argc, char **argv)
 	if (ret || dirty)
 		goto unlock;
 
+	if (opts.list_sparse) {
+		if (!ocfs2_sparse_alloc(OCFS2_RAW_SB(fs->fs_super))) {
+			com_err(opts.progname, 0,
+				"sparse_file flag check failed. ");
+			goto unlock;
+		}
+		printf("List all the sparse files in the volume\n");
+	}
+
 	/* If operation requires touching the global bitmap, ensure it is good */
 	/* This is to handle failed resize */
 	if (opts.num_blocks || opts.num_slots || opts.jrnl_size ||
@@ -1450,7 +1478,7 @@ int main(int argc, char **argv)
 
 	if (!opts.vol_label && !opts.vol_uuid && !opts.num_slots &&
 	    !opts.jrnl_size && !opts.num_blocks && !opts.mount &&
-	    !opts.backup_super) {
+	    !opts.backup_super && !opts.list_sparse) {
 		com_err(opts.progname, 0, "Nothing to do. Exiting.");
 		goto unlock;
 	}
@@ -1553,6 +1581,20 @@ int main(int argc, char **argv)
 			printf("Resized volume\n");
 	}
 
+	/*
+	 * list all the files in the volume, and since list_sparse
+	 * option can coexist with others, we jump to the end after
+	 * the work.
+	 */
+	if (opts.list_sparse) {
+		ret = list_sparse(fs);
+		if (ret) {
+			com_err(opts.progname, ret,
+				"while listing sparse files");
+			goto unlock;
+		}
+	}
+
 	/* update the backup superblock. */
 	if (opts.backup_super ||
 	    (opts.num_blocks &&
diff --git a/tunefs.ocfs2/tunefs.h b/tunefs.ocfs2/tunefs.h
index c4c172a..4415703 100644
--- a/tunefs.ocfs2/tunefs.h
+++ b/tunefs.ocfs2/tunefs.h
@@ -22,6 +22,9 @@
  *
  */
 
+#ifndef _TUNEFS_H
+#define _TUNEFS_H
+
 #define _LARGEFILE64_SOURCE
 #define _GNU_SOURCE /* Because libc really doesn't want us using O_DIRECT? */
 
@@ -70,6 +73,7 @@
 
 enum {
 	BACKUP_SUPER_OPTION = CHAR_MAX + 1,
+	LIST_SPARSE_FILES,
 };
 
 typedef struct _ocfs2_tune_opts {
@@ -86,6 +90,7 @@ typedef struct _ocfs2_tune_opts {
 	int quiet;
 	int prompt;
 	int backup_super;
+	int list_sparse;
 	time_t tune_time;
 	int fd;
 } ocfs2_tune_opts;
@@ -94,3 +99,6 @@ void print_query(char *queryfmt);
 
 errcode_t remove_slots(ocfs2_filesys *fs);
 errcode_t remove_slot_check(ocfs2_filesys *fs);
+
+errcode_t list_sparse(ocfs2_filesys *fs);
+#endif /* _TUNEFS_H */
-- 
1.5.3.2.g4f337



More information about the Ocfs2-tools-devel mailing list