#define _XOPEN_SOURCE 500
#define _GNU_SOURCE
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#include <ftw.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <time.h>
#include <sys/time.h>
#include "store.h"
static unsigned long long min_size = 0;
static unsigned short verbose = 0;
static unsigned short debug = 0;
static unsigned int zero = 0;
static char *out_file;
static struct option options[] =
{
{"zero", no_argument, NULL, 'z'},
{"min-size", required_argument, NULL, 'i'},
{"out", required_argument, NULL, 'o'},
{"verbose", no_argument, NULL, 'v'},
{"debug", no_argument, NULL, 'd'},
{NULL, 0, NULL, 0}
};
static void usage(void)
{
fprintf(stderr, "Usage: dupdump [options] <dir1> [<dir2>] ...\n"
" --zero -z Use \\0 to separate columns\n"
" --min-size -i Ignore files under this size (default 1)\n"
" --out -o Where to store results (default stdout)\n"
" --verbose -v Be more verbose (can be specified multiple times)\n"
" --debug -d Print debug information (can be specified multiple times)\n"
);
}
/*
* Callback for nftw function
*/
static int callback(const char *fpath, const struct stat *s, int tflag,
struct FTW *ftwbuf)
{
int err;
if (verbose >= 2)
fprintf(stderr, "%08x %s size=%jd base=%d level=%d dev=%lu ino=%lu\n",
tflag, fpath,
s->st_size,
ftwbuf->base, ftwbuf->level,
s->st_dev,
s->st_ino);
/* Allow only normal files and dirs */
if ((!S_ISREG(s->st_mode)) && (!S_ISDIR(s->st_mode)))
return 0;
/* Add dir */
if (tflag == FTW_D) {
err = dir_add(fpath, s, ftwbuf->level);
if (err) {
fprintf(stderr, "ERROR: Probably out of memory!\n");
return FTW_STOP;
}
return FTW_CONTINUE;
}
err = file_add(fpath, s, ftwbuf->level);
if (err) {
fprintf(stderr, "ERROR: Cannot add file!\n");
return FTW_STOP;
}
return FTW_CONTINUE;
}
int main(int argc, char *argv[])
{
int flags = 0;
int err;
int options_index = 0;
int c;
FILE *out;
struct timeval start, end;
while ((c = getopt_long(argc, argv, "zi:o:vdh", options, &options_index)) != -1) {
switch (c) {
case 'z': zero = 1; break;
case 'i': min_size = strtoull(optarg, NULL, 10); break;
case 'o': out_file = optarg; break;
case 'v': verbose++; break;
case 'd': debug++; break;
default:
usage();
return 1;
}
}
if (out_file == NULL) {
out = stdout;
} else {
out = fopen(out_file, "w");
if (out == NULL) {
fprintf(stderr, "Cannot open results file (%s)!\n",
strerror(errno));
return 1;
}
}
flags |= FTW_PHYS; /* Do not follow symlinks */
flags |= FTW_ACTIONRETVAL; /* To skip hierarchies */
if (optind >= argc) {
usage();
fprintf(stderr, "Error: no dirs to scan specified!\n");
fclose(out);
return 1;
}
set_debug(debug);
set_out(out);
if (verbose)
fprintf(stderr, "Scanning for duplicates, min-size %llu\n",
min_size);
gettimeofday(&start, NULL);
while (optind < argc) {
if (verbose)
fprintf(stderr, "Processing dir %s...\n", argv[optind]);
err = nftw(argv[optind], callback, 100, flags);
if (err == -1) {
fprintf(stderr, "Error: cannot search dir [%s] [%d] (%s)\n",
argv[optind], err, strerror(errno));
return 1;
}
optind++;
}
if (debug > 2)
dump_files();
if (verbose)
fprintf(stderr, "Finding duplicate files...\n");
err = file_find_dups();
if (err != 0) {
fprintf(stderr, "Error comparing files!\n");
return 1;
}
if (verbose)
fprintf(stderr, "Finding duplicate dirs...\n");
err = dir_find_dups();
if (err != 0) {
fprintf(stderr, "Error in finding dups procedure!\n");
return 1;
}
if (debug > 2)
dump_dirs();
dump_duplicates(min_size, zero);
gettimeofday(&end, NULL);
if (verbose) {
time_t diff;
dump_stats();
diff = end.tv_sec - start.tv_sec;
fprintf(stderr, "[*] Time: %luh%lum%lus.\n",
diff / 3600, (diff % 3600) / 60, diff % 60);
}
dev_ino_seen_clean();
return 0;
}