[Ocfs2-tools-devel] [PATCH 2/5] Add "list-sparse" option in
tunefs.ocfs2, take 2
Tao Ma
tao.ma at oracle.com
Thu Oct 25 00:36:25 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>
---
libocfs2/extent_map.c | 10 +-
libocfs2/include/ocfs2.h | 5 +
tunefs.ocfs2/Makefile | 2 +-
tunefs.ocfs2/sparse_file.c | 410 ++++++++++++++++++++++++++++++++++++++++++++
tunefs.ocfs2/tunefs.c | 48 +++++-
tunefs.ocfs2/tunefs.h | 8 +
6 files changed, 474 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..9279304
--- /dev/null
+++ b/tunefs.ocfs2/sparse_file.c
@@ -0,0 +1,410 @@
+/*
+ * 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 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 (!p_cluster && 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 >=
+ 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 = 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;
+
+ /*
+ * We will use block cache in io, so that the extent block
+ * information can be cached to speed up our extent iteration.
+ * If io_init_cache failed, we will go on the list work without
+ * the io_cache, so there is no check here.
+ */
+ io_init_cache(fs->fs_io, ocfs2_extent_recs_per_eb(fs->fs_blocksize));
+ 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);
+ io_destroy_cache(fs->fs_io);
+ 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