/* -*- mode: c; c-basic-offset: 8; -*- * vim: noexpandtab sw=8 ts=8 sts=0: * * find_dup_extents.c * * Simple tool to iterate the inodes and find duplicate extents. * * Copyright (C) 2004 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. * * 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. * * Authors: Joel Becker * * This code is a port of e2fsprogs/lib/ext2fs/dir_iterate.c * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. */ #define _XOPEN_SOURCE 600 /* Triggers magic in features.h */ #define _LARGEFILE64_SOURCE #include #include #include #include "ocfs2/ocfs2.h" static void print_usage(void) { fprintf(stderr, "Usage: find_dup_extents \n"); } struct walk_extents { char *argv0; ocfs2_filesys *fs; uint64_t blkno; int has_dups; ocfs2_bitmap *extent_map; ocfs2_bitmap *dup_map; }; static int extent_set_func(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { errcode_t ret; struct walk_extents *we = priv_data; uint32_t cluster, i; int oldval; int b_to_c_bits = OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits - OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; cluster = (uint32_t)(rec->e_blkno >> b_to_c_bits); for (i = 0; i < ocfs2_rec_clusters(tree_depth, rec); i++) { ret = ocfs2_bitmap_set(we->extent_map, cluster + i, &oldval); if (ret) { com_err(we->argv0, ret, "while setting bit for cluster %u", cluster + i); return OCFS2_EXTENT_ABORT; } if (oldval) { we->has_dups = 1; ret = ocfs2_bitmap_set(we->dup_map, cluster + i, NULL); if (ret) { com_err(we->argv0, ret, "while setting bit for cluster %u", cluster + i); return OCFS2_EXTENT_ABORT; } } } return 0; } static int extent_test_func(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data) { errcode_t ret; struct walk_extents *we = priv_data; uint32_t cluster, i; int oldval; int b_to_c_bits = OCFS2_RAW_SB(fs->fs_super)->s_clustersize_bits - OCFS2_RAW_SB(fs->fs_super)->s_blocksize_bits; cluster = (uint32_t)(rec->e_blkno >> b_to_c_bits); for (i = 0; i < ocfs2_rec_clusters(tree_depth, rec); i++) { ret = ocfs2_bitmap_test(we->dup_map, cluster + i, &oldval); if (ret) { com_err(we->argv0, ret, "while checking bit for cluster %u", cluster + i); return OCFS2_EXTENT_ABORT; } if (oldval) { fprintf(stdout, "Dup! %20"PRIu64" : %u\n", we->blkno, cluster + i); } } return 0; } static errcode_t run_scan(struct walk_extents *we, int test) { errcode_t ret; uint64_t blkno; char *buf; int done; struct ocfs2_dinode *di; ocfs2_inode_scan *scan; int (*extent_func)(ocfs2_filesys *fs, struct ocfs2_extent_rec *rec, int tree_depth, uint32_t ccount, uint64_t ref_blkno, int ref_recno, void *priv_data); if (test) extent_func = extent_test_func; else extent_func = extent_set_func; ret = ocfs2_malloc_block(we->fs->fs_io, &buf); if (ret) { com_err(we->argv0, ret, "while allocating inode buffer"); return ret; } di = (struct ocfs2_dinode *)buf; ret = ocfs2_open_inode_scan(we->fs, &scan); if (ret) { com_err(we->argv0, ret, "while opening inode scan"); goto out_free; } done = 0; while (!done) { ret = ocfs2_get_next_inode(scan, &blkno, buf); if (ret) { com_err(we->argv0, ret, "while getting next inode"); goto out_close_scan; } if (blkno) { if (memcmp(di->i_signature, OCFS2_INODE_SIGNATURE, strlen(OCFS2_INODE_SIGNATURE))) continue; ocfs2_swap_inode_to_cpu(we->fs, di); if (!(di->i_flags & OCFS2_VALID_FL)) continue; if ((di->i_flags & OCFS2_SYSTEM_FL) && (di->i_flags & (OCFS2_SUPER_BLOCK_FL | OCFS2_LOCAL_ALLOC_FL | OCFS2_CHAIN_FL))) continue; if (!di->i_clusters && S_ISLNK(di->i_mode)) continue; we->blkno = blkno; ret = ocfs2_extent_iterate(we->fs, blkno, OCFS2_EXTENT_FLAG_DATA_ONLY, NULL, extent_func, we); if (ret) { com_err(we->argv0, ret, "while walking inode %"PRIu64, blkno); goto out_close_scan; } } else done = 1; } out_close_scan: ocfs2_close_inode_scan(scan); out_free: ocfs2_free(&buf); return ret; } int main(int argc, char *argv[]) { errcode_t ret; char *filename; ocfs2_filesys *fs; struct walk_extents we; initialize_ocfs_error_table(); if (argc < 2) { fprintf(stderr, "Missing filename\n"); print_usage(); return 1; } filename = argv[1]; we.argv0 = argv[0]; we.has_dups = 0; ret = ocfs2_open(filename, OCFS2_FLAG_RO, 0, 0, &fs); if (ret) { com_err(argv[0], ret, "while opening file \"%s\"", filename); goto out; } we.fs = fs; ret = ocfs2_cluster_bitmap_new(fs, "Used extent map", &we.extent_map); if (ret) { com_err(argv[0], ret, "while creating the extent map"); goto out_close; } ret = ocfs2_cluster_bitmap_new(fs, "Dup extent map", &we.dup_map); if (ret) { com_err(argv[0], ret, "while creating the dup map"); goto out_close; } ret = run_scan(&we, 0); if (!ret && we.has_dups) ret = run_scan(&we, 1); ocfs2_bitmap_free(we.extent_map); ocfs2_bitmap_free(we.dup_map); out_close: ret = ocfs2_close(fs); if (ret) { com_err(argv[0], ret, "while closing file \"%s\"", filename); } out: return 0; }