diff --git a/README b/README index 47a5adc..13c8dea 100644 --- a/README +++ b/README @@ -62,6 +62,7 @@ Usage: fdupes [options] DIRECTORY... change time (BY='ctime'), or filename (BY='name') -i --reverse reverse order while sorting -l --log=LOGFILE log file deletion choices to LOGFILE + -e --noinodes don't use inodes to test for changed file contents -v --version display fdupes version -h --help display this help message @@ -84,6 +85,13 @@ once. All files within that directory will be listed as their own duplicates, leading to data loss should a user preserve a file without its "duplicate" (the file itself!). +Some remote-mounted filesystems (e.g. gvfs-smb) generate inodes in +the client instead of sending server inode values. With a large +number of files or client cache activity, inodes can change even +though the file has not changed or moved. Using -e or --noinodes +allows fdupes to delete files where the device, creation and +modification times, and size has not changed during processing, +ignoring any inode change. Contact Information for Adrian Lopez -------------------------------------------------------------------- diff --git a/fdupes.1 b/fdupes.1 index 8116830..82a1051 100644 --- a/fdupes.1 +++ b/fdupes.1 @@ -127,6 +127,9 @@ Reverse order while sorting. .B -l --log\fR=\fILOGFILE\fR Log file deletion choices to LOGFILE. .TP +.B -e --noinodes +Don't use inodes to test for changed file contents. +.TP .B -v --version Display fdupes version. .TP @@ -148,6 +151,13 @@ or is specified, spaces and backslash characters (\fB\e\fP) appearing in a filename are preceded by a backslash character. +When +.B -e +or +.B --noinodes +is specified, file inode will not be used to test for file content +changes before deletion. + .SH EXAMPLES .TP .B fdupes a --recurse: b diff --git a/fdupes.c b/fdupes.c index 76a7136..64875bb 100644 --- a/fdupes.c +++ b/fdupes.c @@ -924,6 +924,7 @@ void deletefiles(file_t *files, int prompt, FILE *tty, char *logfile) int ismatch; char *deletepath; char *errorstring; + int checkinode = !ISFLAG(flags, F_NOINODES); curfile = files; @@ -1125,7 +1126,7 @@ void deletefiles(file_t *files, int prompt, FILE *tty, char *logfile) } if (ismatch) { - if (removeifnotchanged(dupelist[x], &errorstring) == 0) { + if (removeifnotchanged(dupelist[x], &errorstring, checkinode) == 0) { printf(" [-] %s\n", dupelist[x]->d_name); #ifndef NO_SQLITE @@ -1286,6 +1287,7 @@ void deletesuccessor(file_t **existing, file_t *duplicate, int matchconfirmed, file_t *to_delete; char *deletepath; char *errorstring; + int checkinode = !ISFLAG(flags, F_NOINODES); if (comparef(duplicate, *existing) >= 0) { @@ -1312,7 +1314,7 @@ void deletesuccessor(file_t **existing, file_t *duplicate, int matchconfirmed, if (matchconfirmed) { - if (removeifnotchanged(to_delete, &errorstring) == 0) { + if (removeifnotchanged(to_delete, &errorstring, checkinode) == 0) { printf(" [-] %s\n", to_delete->d_name); #ifndef NO_SQLITE @@ -1419,6 +1421,7 @@ void help_text() printf(" change time (BY='ctime'), or filename (BY='name')\n"); printf(" -i --reverse reverse order while sorting\n"); printf(" -l --log=LOGFILE log file deletion choices to LOGFILE\n"); + printf(" -e --noinodes don't use inodes to test for changed file contents\n"); printf(" -v --version display fdupes version\n"); printf(" -h --help display this help message\n\n"); #ifndef HAVE_GETOPT_H @@ -1503,6 +1506,7 @@ int main(int argc, char **argv) { { "log", 1, 0, 'l' }, { "deferconfirmation", 0, 0, 'D' }, { "cache", 0, 0, 'c' }, + { "noinodes", 0, 0, 'e'}, { 0, 0, 0, 0 } }; #define GETOPT getopt_long @@ -1516,7 +1520,7 @@ int main(int argc, char **argv) { oldargv = cloneargs(argc, argv); - while ((opt = GETOPT(argc, argv, "frRq1StsHG:L:nAdPvhNImpo:il:Dcx:" + while ((opt = GETOPT(argc, argv, "frRq1StsHG:L:nAdPvhNImpo:il:Dcex:" #ifdef HAVE_GETOPT_H , long_options, NULL #endif @@ -1619,6 +1623,9 @@ int main(int argc, char **argv) { case 'c': SETFLAG(flags, F_CACHESIGNATURES); break; + case 'e': + SETFLAG(flags, F_NOINODES); + break; case 'x': if (strcmp("cache.readonly", optarg) == 0) SETFLAG(flags, F_READONLYCACHE); diff --git a/flags.h b/flags.h index 0033f8e..3d63497 100644 --- a/flags.h +++ b/flags.h @@ -28,6 +28,7 @@ #define F_PRUNECACHE 0x200000 #define F_READONLYCACHE 0x400000 #define F_VACUUMCACHE 0x800000 +#define F_NOINODES 0x1000000 extern unsigned long flags; diff --git a/ncurses-commands.c b/ncurses-commands.c index 40e1ea2..52f74bb 100644 --- a/ncurses-commands.c +++ b/ncurses-commands.c @@ -715,6 +715,7 @@ int cmd_prune(struct filegroup *groups, int groupcount, wchar_t *commandargument wchar_t *statuscopy; struct groupfile *firstnotdeleted; char *deletepath; + int checkinode = !ISFLAG(flags, F_NOINODES); if (logfile != 0) loginfo = log_open(logfile, 0); @@ -808,7 +809,7 @@ int cmd_prune(struct filegroup *groups, int groupcount, wchar_t *commandargument } #endif - if (ismatch && removeifnotchanged(groups[g].files[f].file, 0) == 0) + if (ismatch && removeifnotchanged(groups[g].files[f].file, 0, checkinode) == 0) { set_file_action(&groups[g].files[f], FILEACTION_DELIST, deletiontally); diff --git a/removeifnotchanged.c b/removeifnotchanged.c index b268171..a64f101 100644 --- a/removeifnotchanged.c +++ b/removeifnotchanged.c @@ -25,7 +25,7 @@ #include #include -int removeifnotchanged(const file_t *file, char **errorstring) +int removeifnotchanged(const file_t *file, char **errorstring, int checkinode) { int result; struct stat st; @@ -36,7 +36,7 @@ int removeifnotchanged(const file_t *file, char **errorstring) stat(file->d_name, &st); if (file->device != st.st_dev || - file->inode != st.st_ino || + (checkinode && (file->inode != st.st_ino)) || file->ctime != st.st_ctime || file->mtime != st.st_mtime || #ifdef HAVE_NSEC_TIMES diff --git a/removeifnotchanged.h b/removeifnotchanged.h index c360d72..0900060 100644 --- a/removeifnotchanged.h +++ b/removeifnotchanged.h @@ -24,6 +24,6 @@ #include "fdupes.h" -int removeifnotchanged(const file_t *file, char **errorstring); +int removeifnotchanged(const file_t *file, char **errorstring, int checkinode); #endif \ No newline at end of file