[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