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

Tao Ma tao.ma at oracle.com
Wed Oct 17 02:25:56 PDT 2007


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>
---
 tunefs.ocfs2/Makefile      |    2 +-
 tunefs.ocfs2/sparse_file.c |  465 ++++++++++++++++++++++++++++++++++++++++++++
 tunefs.ocfs2/tunefs.c      |   48 +++++-
 tunefs.ocfs2/tunefs.h      |    8 +
 4 files changed, 519 insertions(+), 4 deletions(-)
 create mode 100644 tunefs.ocfs2/sparse_file.c

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..4877408
--- /dev/null
+++ b/tunefs.ocfs2/sparse_file.c
@@ -0,0 +1,465 @@
+/*
+ * 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 {
+	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 void list_sparse_iterate(void *priv_data,
+				uint32_t hole_start,
+				uint32_t hole_end)
+{
+	struct list_ctxt *ctxt =
+			(struct list_ctxt *)priv_data;
+
+	ctxt->file_hole_len += hole_end - hole_start;
+}
+
+static errcode_t tunefs_dir_iterate(ocfs2_filesys *fs,
+				    struct ocfs2_dinode *di,
+				    struct list_ctxt *ctxt);
+
+/*
+ * iterate a extent list and output the total clusters occupied by the
+ * extent list. The holes will exist only in the leaf extent list.
+ *
+ * start and end indicate:
+ * the range of a file if the extent list belongs to the dinode;
+ * the range of the extent rec if the extent list belongs to a extent block.
+ */
+static errcode_t iterate_extent_list(ocfs2_filesys *fs,
+				     struct ocfs2_extent_list *el,
+				     uint32_t start,
+				     uint32_t end,
+				     void (*func)(void *priv_data,
+				     		  uint32_t hole_start,
+						  uint32_t hole_end),
+				     void *priv_data)
+{
+	int i;
+	errcode_t ret = 0;
+	uint64_t blkno;
+	uint32_t first_rec_begin = 0, prev_end = 0;
+	struct ocfs2_extent_rec *rec = NULL;
+	char *eb_buf = NULL;
+	struct ocfs2_extent_block *eb = NULL;
+	struct ocfs2_extent_list *child_el = NULL;
+
+	assert(func);
+
+	if (el->l_tree_depth > 0) {
+
+		ret = ocfs2_malloc_block(fs->fs_io, &eb_buf);
+		if (ret)
+			goto bail;
+
+		for (i = 0; i <= el->l_next_free_rec; i++) {
+			rec = &el->l_recs[i];
+
+			if (!ocfs2_rec_clusters(el->l_tree_depth, rec))
+				continue;
+
+			blkno = rec->e_blkno;
+			ret = ocfs2_read_extent_block(fs, blkno, eb_buf);
+			if (ret)
+				goto bail;
+
+			eb = (struct ocfs2_extent_block *)eb_buf;
+			child_el = &eb->h_list;
+
+			ret = iterate_extent_list(fs, child_el, rec->e_cpos,
+					rec->e_cpos + rec->e_int_clusters,
+					func, priv_data);
+			if (ret)
+				goto bail;
+		}
+	} else {
+		for (i = 0; i < el->l_next_free_rec; i++) {
+			rec = &el->l_recs[i];
+
+			if (!ocfs2_rec_clusters(el->l_tree_depth, rec))
+				continue;
+
+			/*
+			 * We have to consider the hole between the start of
+			 * the first extent rec and the beginning of the extent
+			 * list.
+			 *
+			 * We have to check prev_end here because if the
+			 * first extent rec begins with 0, we don't want to
+			 * calculate it every time.
+			 */
+			if (!first_rec_begin && !prev_end) {
+				first_rec_begin = rec->e_cpos;
+				if (first_rec_begin > start)
+					func(priv_data, start, first_rec_begin);
+			}
+
+			if (prev_end && prev_end < rec->e_cpos)
+				func(priv_data, prev_end, rec->e_cpos);
+
+			prev_end = rec->e_cpos + rec->e_leaf_clusters;
+		}
+
+		if (prev_end && prev_end < end)
+			func(priv_data, prev_end, end);
+	}
+
+bail:
+	if (eb_buf)
+		ocfs2_free(&eb_buf);
+
+	return ret;
+}
+
+static errcode_t tunefs_iterate_regular(ocfs2_filesys *fs,
+					struct ocfs2_dinode *di,
+					struct list_ctxt *ctxt)
+{
+	errcode_t ret = 0;
+	uint32_t end_clusters;
+	struct multi_link_file *file = NULL;
+	struct ocfs2_extent_list *el = &di->id2.i_list;
+
+	assert(S_ISREG(di->i_mode));
+
+	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;
+		}
+	}
+
+	end_clusters = (di->i_size + fs->fs_clustersize -1 ) /
+			fs->fs_clustersize;
+	ret = iterate_extent_list(fs, el, 0, end_clusters, 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 iterate_dir_entry(ocfs2_filesys *fs,
+			     uint64_t blkno,
+			     uint64_t bcount,
+			     uint16_t flags,
+			     void *priv_data)
+{
+	errcode_t ret;
+	uint32_t offset = 0;
+	struct list_ctxt *ctxt;
+	char *dirblock_buf = NULL, *di_buf = NULL;
+	struct ocfs2_dir_entry *dirent = NULL;
+	struct ocfs2_dinode *di = NULL;
+	char file_name[OCFS2_MAX_FILENAME_LEN];
+	int file_name_len;
+
+	ctxt = (struct list_ctxt *)priv_data;
+	strcpy(file_name, ctxt->file_name);
+	file_name_len = ctxt->file_name_len;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &dirblock_buf);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &di_buf);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_read_dir_block(fs, blkno, dirblock_buf);
+	if (ret)
+		goto bail;
+
+	while (offset < fs->fs_blocksize) {
+		dirent = (struct ocfs2_dir_entry *)(dirblock_buf + offset);
+
+		/* skip "." and "..". */
+		if (dirent->name[0] == '.')
+			 switch (dirent->name_len) {
+				default:
+                                	break;
+				case 2:
+				if (dirent->name[1] != '.')
+					break;
+				case 1:
+					goto next_ent;
+                }
+
+		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)) {
+			ctxt->file_hole_len = 0;
+			ctxt->duplicated = 0;
+			strcpy(ctxt->file_name, file_name);
+			ctxt->file_name_len = file_name_len;
+
+			if (dirent->name_len + ctxt->file_name_len + 1 >=
+				OCFS2_MAX_FILENAME_LEN) {
+				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 = tunefs_dir_iterate(fs, di, ctxt);
+				if (ret)
+					goto bail;
+			} else {
+				ret = tunefs_iterate_regular(fs, di, ctxt);
+				if (ret)
+					goto bail;
+				if (!ctxt->duplicated)
+					ctxt->total_clusters +=
+							 ctxt->file_hole_len;
+			}
+
+		}
+
+next_ent:
+		offset += dirent->rec_len;
+	}
+
+bail:
+	if (di_buf)
+		ocfs2_free(&di_buf);
+	if (dirblock_buf)
+		ocfs2_free(&dirblock_buf);
+	return ret;
+}
+
+static errcode_t tunefs_dir_iterate(ocfs2_filesys *fs,
+				    struct ocfs2_dinode *di,
+				    struct list_ctxt *ctxt)
+{
+	assert(S_ISDIR(di->i_mode));
+
+	return ocfs2_block_iterate_inode(fs, di,
+					OCFS2_EXTENT_FLAG_DATA_ONLY,
+					iterate_dir_entry, ctxt);
+}
+
+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);
+	char *buf = NULL;
+	struct ocfs2_dinode *di = NULL;
+	uint32_t total_holes = 0;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret)
+		goto bail;
+
+	printf("Iterating from the root directory:\n");
+	printf("#inode\tcluster nums\tfilepath\n");
+
+	ret = ocfs2_read_inode(fs, sb->s_root_blkno, buf);
+	if (ret)
+		goto bail;
+
+	di = (struct ocfs2_dinode *)buf;
+
+	memset(&ctxt, 0, sizeof(ctxt));
+	ctxt.multi_link_files = RB_ROOT;
+	ctxt.func = list_sparse_iterate;
+	sprintf(ctxt.file_name, "/");
+	ctxt.file_name_len = strlen(ctxt.file_name);
+	ret = tunefs_dir_iterate(fs, di, &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;
+
+		ret = ocfs2_read_inode(fs, blkno, buf);
+		if (ret)
+			goto bail;
+
+		di = (struct ocfs2_dinode *)buf;
+
+		empty_multi_link_files(&ctxt);
+		memset(&ctxt, 0, sizeof(ctxt));
+		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 = tunefs_dir_iterate(fs, di, &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. */
+	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;
+	printf("Total free %u clusters in the volume.\n",
+	       di->id1.bitmap1.i_total - di->id1.bitmap1.i_used);
+
+bail:
+	empty_multi_link_files(&ctxt);
+	if (buf)
+		ocfs2_free(&buf);
+	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