[Ocfs2-tools-devel] [PATCH 11/22] tunefs rework: Add op_list_sparse_files.c

Joel Becker joel.becker at oracle.com
Fri Jul 25 19:07:49 PDT 2008


The list_sparse_files operation traverses all the files in an ocfs2
filesystem, printing information about the files with sparse allocation.

This is the tunefs option '--list-sparse'.

Signed-off-by: Joel Becker <joel.becker at oracle.com>
---
 tunefs.ocfs2/op_list_sparse_files.c |  460 +++++++++++++++++++++++++++++++++++
 1 files changed, 460 insertions(+), 0 deletions(-)
 create mode 100644 tunefs.ocfs2/op_list_sparse_files.c

diff --git a/tunefs.ocfs2/op_list_sparse_files.c b/tunefs.ocfs2/op_list_sparse_files.c
new file mode 100644
index 0000000..817f269
--- /dev/null
+++ b/tunefs.ocfs2/op_list_sparse_files.c
@@ -0,0 +1,460 @@
+/* -*- mode: c; c-basic-offset: 8; -*-
+ * vim: noexpandtab sw=8 ts=8 sts=0:
+ *
+ * op_list_sparse_files.c
+ *
+ * ocfs2 tune utility to list sparse files.
+ *
+ * Copyright (C) 2004, 2008 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 version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include "ocfs2/ocfs2.h"
+#include "ocfs2/kernel-rbtree.h"
+
+#include "libocfs2ne.h"
+
+
+struct multi_link_file {
+	struct rb_node br_node;
+	uint64_t blkno;
+	uint32_t clusters;
+};
+
+struct list_ctxt {
+	ocfs2_filesys *fs;
+	uint32_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;
+
+	ret = ocfs2_malloc_block(fs->fs_io, &buf);
+	if (ret)
+		goto bail;
+
+	ret = ocfs2_lookup_system_inode(fs, GLOBAL_BITMAP_SYSTEM_INODE,
+					0, &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.
+ * Call "func" when we meet with a hole.
+ * Call "unwritten_func" when we meet with unwritten clusters.
+ * Call "seen_exceed" when we see some clusters exceed i_size.
+ */
+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 (*unwritten_func)(void *priv_data,
+						     uint32_t start,
+						     uint32_t len,
+						     uint64_t p_start),
+			      void (*seen_exceed)(void *priv_data),
+			      void *priv_data)
+{
+	errcode_t ret;
+	uint32_t clusters, v_cluster = 0, p_cluster, num_clusters;
+	uint32_t last_v_cluster = 0;
+	uint64_t p_blkno;
+	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);
+		}
+
+		if ((extent_flags & OCFS2_EXT_UNWRITTEN) && unwritten_func) {
+			p_blkno = ocfs2_clusters_to_blocks(fs, p_cluster);
+			unwritten_func(priv_data,
+				       v_cluster, num_clusters, p_blkno);
+		}
+
+		v_cluster += num_clusters;
+	}
+
+	/*
+	 * If the last allocated cluster's virtual offset is greater
+	 * than the clusters we calculated from i_size, this cluster
+	 * must exceed the limit of i_size.
+	 */
+	ret = ocfs2_get_last_cluster_offset(fs, di, &last_v_cluster);
+	if (ret)
+		goto bail;
+
+	if (last_v_cluster >= clusters && seen_exceed)
+		seen_exceed(priv_data);
+
+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, NULL, NULL, 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", (uint64_t)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.
+ */
+static 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++) {
+		ocfs2_sprintf_system_inode_name(file_name,
+						OCFS2_MAX_FILENAME_LEN,
+						ORPHAN_DIR_SYSTEM_INODE, i);
+		ret = ocfs2_lookup_system_inode(fs, ORPHAN_DIR_SYSTEM_INODE,
+						i, &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;
+}
+
+static int list_sparse_run(struct tunefs_operation *op, ocfs2_filesys *fs,
+			   int flags)
+{
+	int rc = 0;
+	errcode_t err;
+
+	err = list_sparse(fs);
+	if (err) {
+		tcom_err(err,
+			 "- unable to update list all sparse files on "
+			 "device \"%s\"",
+			 fs->fs_devname);
+		rc = 1;
+	}
+
+	return rc;
+}
+
+DEFINE_TUNEFS_OP(list_sparse,
+		 "Usage: op_list_sparse_files [opts] <device>\n",
+		 TUNEFS_FLAG_RW,
+		 NULL,
+		 list_sparse_run);
+
+#ifdef DEBUG_EXE
+int main(int argc, char *argv[])
+{
+	return tunefs_op_main(argc, argv, &list_sparse_op);
+}
+#endif
-- 
1.5.6.3




More information about the Ocfs2-tools-devel mailing list