From b143429d2e059e53652d2e5617d875ac58bfb47f51108a43b92b9c91e292f136 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Wed, 8 May 2019 09:48:39 +0000 Subject: [PATCH 01/15] Accepting request 701534 from home:dimstar:Factory Move RPM macros to %_rpmmacrodir. OBS-URL: https://build.opensuse.org/request/show/701534 OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=18 --- fdupes.changes | 5 +++++ fdupes.spec | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/fdupes.changes b/fdupes.changes index 04694a6..e488999 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,8 @@ +------------------------------------------------------------------- +Wed May 8 09:37:54 UTC 2019 - Dominique Leuenberger + +- Move RPM macros to %_rpmmacrodir. + ------------------------------------------------------------------- Fri Dec 16 12:40:20 UTC 2016 - psimons@suse.com diff --git a/fdupes.spec b/fdupes.spec index 66b3daa..79573f4 100644 --- a/fdupes.spec +++ b/fdupes.spec @@ -47,7 +47,7 @@ make %{?_smp_mflags} COMPILER_OPTIONS="%{optflags}" %install install -D -m755 %{name} %{buildroot}%{_bindir}/%{name} install -D -m644 %{name}.1 %{buildroot}%{_mandir}/man1/%{name}.1 -install -D -m644 %{SOURCE1} %{buildroot}%{_sysconfdir}/rpm/macros.%{name} +install -D -m644 %{SOURCE1} %{buildroot}%{_rpmmacrodir}/macros.%{name} %check ./%{name} testdir @@ -60,6 +60,6 @@ install -D -m644 %{SOURCE1} %{buildroot}%{_sysconfdir}/rpm/macros.%{name} %doc CHANGES %{_bindir}/%{name} %{_mandir}/man1/%{name}.1* -%config %{_sysconfdir}/rpm/macros.%{name} +%{_rpmmacrodir}/macros.%{name} %changelog -- 2.51.1 From 0328238ce01431c7482c4da7b38178afb7a131d8c721cc58406c4c031af32588 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Sun, 26 Apr 2020 10:13:17 +0000 Subject: [PATCH 02/15] Accepting request 794679 from home:mcepl:branches:Education - Make package building even on platforms, where _rpmmacrodir is not defined. OBS-URL: https://build.opensuse.org/request/show/794679 OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=19 --- fdupes.changes | 6 ++++++ fdupes.spec | 2 ++ 2 files changed, 8 insertions(+) diff --git a/fdupes.changes b/fdupes.changes index e488999..aeec205 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Thu Apr 16 21:07:45 UTC 2020 - Matej Cepl + +- Make package building even on platforms, where _rpmmacrodir + is not defined. + ------------------------------------------------------------------- Wed May 8 09:37:54 UTC 2019 - Dominique Leuenberger diff --git a/fdupes.spec b/fdupes.spec index 79573f4..9a45030 100644 --- a/fdupes.spec +++ b/fdupes.spec @@ -20,6 +20,8 @@ # continuity: https://github.com/adrianlopezroche/fdupes/issues/74. %global upstream_version 1.6.1 +%{?!_rpmmacrodir:%define _rpmmacrodir /usr/lib/rpm/macros.d} + Name: fdupes Version: 1.61 Release: 0 -- 2.51.1 From 1c0d3136d6014b3edad9cc1bb97e38dd3711bbf6d4e9f624c91ab7814db86edc Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Fri, 5 Jun 2020 14:24:23 +0000 Subject: [PATCH 03/15] Accepting request 811804 from home:polslinux:branches:utilities - Update to v2.0.0 * Add ncurses mode for interactive file deletion (plain mode still available via --plain or ./configure). * Add --minsize option. * Add --maxsize option. * Add --time option. * Add --order=ctime option. * Add --log option. * Use configure script for installation (Autotools/Automake). - Remove fdupes-makefile.patch OBS-URL: https://build.opensuse.org/request/show/811804 OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=20 --- fdupes-1.6.1.tar.gz | 3 --- fdupes-2.0.0.tar.gz | 3 +++ fdupes-makefile.patch | 13 ------------- fdupes.changes | 13 +++++++++++++ fdupes.spec | 19 +++++++------------ 5 files changed, 23 insertions(+), 28 deletions(-) delete mode 100644 fdupes-1.6.1.tar.gz create mode 100644 fdupes-2.0.0.tar.gz delete mode 100644 fdupes-makefile.patch diff --git a/fdupes-1.6.1.tar.gz b/fdupes-1.6.1.tar.gz deleted file mode 100644 index 1b4ae27..0000000 --- a/fdupes-1.6.1.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d6b6fdb0b8419815b4df3bdfd0aebc135b8276c90bbbe78ebe6af0b88ba49ea -size 20869 diff --git a/fdupes-2.0.0.tar.gz b/fdupes-2.0.0.tar.gz new file mode 100644 index 0000000..c006581 --- /dev/null +++ b/fdupes-2.0.0.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb9e3bd3e722ebb2a272e45a1073f78c60f8989b151c3661421b86b14b203410 +size 137705 diff --git a/fdupes-makefile.patch b/fdupes-makefile.patch deleted file mode 100644 index 1884f2d..0000000 --- a/fdupes-makefile.patch +++ /dev/null @@ -1,13 +0,0 @@ -Index: Makefile -=================================================================== ---- Makefile.orig 2016-08-21 06:54:46.000000000 +0200 -+++ Makefile 2016-11-04 13:46:21.037423459 +0100 -@@ -11,7 +11,7 @@ - # determination of the actual installation directories. - # Suggested values are "/usr/local", "/usr", "/pkgs/fdupes-$(VERSION)" - # --PREFIX = /usr/local -+PREFIX = /usr - - # - # When compiling for 32-bit systems, FILEOFFSET_64BIT must be enabled diff --git a/fdupes.changes b/fdupes.changes index aeec205..71a03c4 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,16 @@ +------------------------------------------------------------------- +Fri May 22 08:27:11 UTC 2020 - Paolo Stivanin + +- Update to v2.0.0 + * Add ncurses mode for interactive file deletion (plain mode still available via --plain or ./configure). + * Add --minsize option. + * Add --maxsize option. + * Add --time option. + * Add --order=ctime option. + * Add --log option. + * Use configure script for installation (Autotools/Automake). +- Remove fdupes-makefile.patch + ------------------------------------------------------------------- Thu Apr 16 21:07:45 UTC 2020 - Matej Cepl diff --git a/fdupes.spec b/fdupes.spec index 9a45030..7f3a67f 100644 --- a/fdupes.spec +++ b/fdupes.spec @@ -15,24 +15,19 @@ # Please submit bugfixes or comments via http://bugs.opensuse.org/ # -# Upstream calls this version 1.6.1, but that version number is *lower* than -# previously released ones, like 1.51, so we mangle the number to keep -# continuity: https://github.com/adrianlopezroche/fdupes/issues/74. -%global upstream_version 1.6.1 - %{?!_rpmmacrodir:%define _rpmmacrodir /usr/lib/rpm/macros.d} Name: fdupes -Version: 1.61 +Version: 2.0.0 Release: 0 Summary: Identifying or deleting duplicate files License: MIT Group: Productivity/Archiving/Compression Url: https://github.com/adrianlopezroche/fdupes -Source0: https://github.com/adrianlopezroche/fdupes/archive/v%{upstream_version}.tar.gz#/%{name}-%{upstream_version}.tar.gz +Source0: https://github.com/adrianlopezroche/fdupes/releases/download/%{version}/fdupes-%{version}.tar.gz Source1: macros.fdupes -#PATCH-FIX-SUSE: fix patch according distro's needs -Patch0: fdupes-makefile.patch +BuildRequires: ncurses-devel +BuildRequires: pcre2-devel BuildRoot: %{_tmppath}/%{name}-%{version}-build %description @@ -40,11 +35,11 @@ FDUPES is a program for identifying or deleting duplicate files residing within specified directories. %prep -%setup -q -n %{name}-%{upstream_version} -%patch0 +%setup -q -n %{name}-%{version} %build -make %{?_smp_mflags} COMPILER_OPTIONS="%{optflags}" +%configure +%make_build %install install -D -m755 %{name} %{buildroot}%{_bindir}/%{name} -- 2.51.1 From ea56337bf1d69173950cc8bb84438540f333c7127b439ab32c87eeba77cbd469 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Tue, 9 Jun 2020 07:36:52 +0000 Subject: [PATCH 04/15] Accepting request 811952 from home:jengelh:branches:utilities - Use noun phrase in summary. Drop old specfile constructs. OBS-URL: https://build.opensuse.org/request/show/811952 OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=21 --- fdupes.changes | 5 +++++ fdupes.spec | 6 ++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/fdupes.changes b/fdupes.changes index 71a03c4..166d897 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,8 @@ +------------------------------------------------------------------- +Fri Jun 5 23:42:10 UTC 2020 - Jan Engelhardt + +- Use noun phrase in summary. Drop old specfile constructs. + ------------------------------------------------------------------- Fri May 22 08:27:11 UTC 2020 - Paolo Stivanin diff --git a/fdupes.spec b/fdupes.spec index 7f3a67f..d6a17c6 100644 --- a/fdupes.spec +++ b/fdupes.spec @@ -20,7 +20,7 @@ Name: fdupes Version: 2.0.0 Release: 0 -Summary: Identifying or deleting duplicate files +Summary: Tool to identify or delete duplicate files License: MIT Group: Productivity/Archiving/Compression Url: https://github.com/adrianlopezroche/fdupes @@ -28,14 +28,13 @@ Source0: https://github.com/adrianlopezroche/fdupes/releases/download/%{v Source1: macros.fdupes BuildRequires: ncurses-devel BuildRequires: pcre2-devel -BuildRoot: %{_tmppath}/%{name}-%{version}-build %description FDUPES is a program for identifying or deleting duplicate files residing within specified directories. %prep -%setup -q -n %{name}-%{version} +%autosetup -p1 %build %configure @@ -53,7 +52,6 @@ install -D -m644 %{SOURCE1} %{buildroot}%{_rpmmacrodir}/macros.%{name} ./%{name} --size testdir %files -%defattr(-, root, root) %doc CHANGES %{_bindir}/%{name} %{_mandir}/man1/%{name}.1* -- 2.51.1 From c440bc51227070b7da613168039f581ccac36ac13014b27e70ccbd03c32c22c2 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Tue, 18 Aug 2020 08:29:52 +0000 Subject: [PATCH 05/15] Accepting request 827069 from home:dirkmueller:branches:utilities - update to 2.1.2: * Do not enter ncurses mode when --immediate option given. * Fix logging/memory corruption bug when using --log with --immediate. * Break mtime ties using ctime when sorting by time. * Reduce number of calls to stat(), for speed. * Clear last command status when new command is entered. * Rename cs command ("clear all selections") from cs to csel. * Rename igs command ("invert selections") from igs to isel. * Add "prune" command as synonym for DELETE key. * Clear selections after deleting files via prune/DELETE. * Fix dependency issues when fdupes is configured to not use ncurses. - build without ncurses for now until buildcycles can be solved OBS-URL: https://build.opensuse.org/request/show/827069 OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=22 --- fdupes-2.0.0.tar.gz | 3 --- fdupes-2.1.2.tar.gz | 3 +++ fdupes.changes | 16 ++++++++++++++++ fdupes.spec | 11 ++++------- 4 files changed, 23 insertions(+), 10 deletions(-) delete mode 100644 fdupes-2.0.0.tar.gz create mode 100644 fdupes-2.1.2.tar.gz diff --git a/fdupes-2.0.0.tar.gz b/fdupes-2.0.0.tar.gz deleted file mode 100644 index c006581..0000000 --- a/fdupes-2.0.0.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:eb9e3bd3e722ebb2a272e45a1073f78c60f8989b151c3661421b86b14b203410 -size 137705 diff --git a/fdupes-2.1.2.tar.gz b/fdupes-2.1.2.tar.gz new file mode 100644 index 0000000..462dcb3 --- /dev/null +++ b/fdupes-2.1.2.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cd5cb53b6d898cf20f19b57b81114a5b263cc1149cd0da3104578b083b2837bd +size 142266 diff --git a/fdupes.changes b/fdupes.changes index 166d897..db1e37f 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,19 @@ +------------------------------------------------------------------- +Sun Aug 16 16:59:45 UTC 2020 - Dirk Mueller + +- update to 2.1.2: + * Do not enter ncurses mode when --immediate option given. + * Fix logging/memory corruption bug when using --log with --immediate. + * Break mtime ties using ctime when sorting by time. + * Reduce number of calls to stat(), for speed. + * Clear last command status when new command is entered. + * Rename cs command ("clear all selections") from cs to csel. + * Rename igs command ("invert selections") from igs to isel. + * Add "prune" command as synonym for DELETE key. + * Clear selections after deleting files via prune/DELETE. + * Fix dependency issues when fdupes is configured to not use ncurses. +- build without ncurses for now until buildcycles can be solved + ------------------------------------------------------------------- Fri Jun 5 23:42:10 UTC 2020 - Jan Engelhardt diff --git a/fdupes.spec b/fdupes.spec index d6a17c6..4fb6764 100644 --- a/fdupes.spec +++ b/fdupes.spec @@ -18,16 +18,14 @@ %{?!_rpmmacrodir:%define _rpmmacrodir /usr/lib/rpm/macros.d} Name: fdupes -Version: 2.0.0 +Version: 2.1.2 Release: 0 Summary: Tool to identify or delete duplicate files License: MIT Group: Productivity/Archiving/Compression Url: https://github.com/adrianlopezroche/fdupes -Source0: https://github.com/adrianlopezroche/fdupes/releases/download/%{version}/fdupes-%{version}.tar.gz +Source0: https://github.com/adrianlopezroche/fdupes/releases/download/v%{version}/fdupes-%{version}.tar.gz Source1: macros.fdupes -BuildRequires: ncurses-devel -BuildRequires: pcre2-devel %description FDUPES is a program for identifying or deleting duplicate files @@ -37,12 +35,11 @@ residing within specified directories. %autosetup -p1 %build -%configure +%configure --without-ncurses %make_build %install -install -D -m755 %{name} %{buildroot}%{_bindir}/%{name} -install -D -m644 %{name}.1 %{buildroot}%{_mandir}/man1/%{name}.1 +%make_install install -D -m644 %{SOURCE1} %{buildroot}%{_rpmmacrodir}/macros.%{name} %check -- 2.51.1 From da1a4eb97b1ef0e674d30151385faffb91f0803aa46c87896c6f2cc4163b6451 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Mon, 14 Mar 2022 09:15:34 +0000 Subject: [PATCH 06/15] Accepting request 961567 from home:coolo:branches:utilities - Simplify macros.fdupes with a call to a C++ program that does the same within a fraction of a second what the shell loop did in many seconds (bsc#1195709) OBS-URL: https://build.opensuse.org/request/show/961567 OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=23 --- fdupes.changes | 7 +++ fdupes.spec | 12 +++- fdupes_wrapper.cpp | 145 +++++++++++++++++++++++++++++++++++++++++++++ macros.fdupes | 22 +------ 4 files changed, 162 insertions(+), 24 deletions(-) create mode 100644 fdupes_wrapper.cpp diff --git a/fdupes.changes b/fdupes.changes index db1e37f..6c1f2b9 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Sat Mar 12 08:17:37 UTC 2022 - Stephan Kulow + +- Simplify macros.fdupes with a call to a C++ program that does + the same within a fraction of a second what the shell loop did + in many seconds (bsc#1195709) + ------------------------------------------------------------------- Sun Aug 16 16:59:45 UTC 2020 - Dirk Mueller diff --git a/fdupes.spec b/fdupes.spec index 4fb6764..07be3a5 100644 --- a/fdupes.spec +++ b/fdupes.spec @@ -1,7 +1,7 @@ # # spec file for package fdupes # -# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,9 +12,10 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # + %{?!_rpmmacrodir:%define _rpmmacrodir /usr/lib/rpm/macros.d} Name: fdupes @@ -23,9 +24,11 @@ Release: 0 Summary: Tool to identify or delete duplicate files License: MIT Group: Productivity/Archiving/Compression -Url: https://github.com/adrianlopezroche/fdupes +URL: https://github.com/adrianlopezroche/fdupes Source0: https://github.com/adrianlopezroche/fdupes/releases/download/v%{version}/fdupes-%{version}.tar.gz Source1: macros.fdupes +Source2: fdupes_wrapper.cpp +BuildRequires: gcc-c++ %description FDUPES is a program for identifying or deleting duplicate files @@ -37,10 +40,12 @@ residing within specified directories. %build %configure --without-ncurses %make_build +g++ $RPM_OPT_FLAGS %{S:2} -o fdupes_wrapper %install %make_install install -D -m644 %{SOURCE1} %{buildroot}%{_rpmmacrodir}/macros.%{name} +install -D -m755 fdupes_wrapper %{buildroot}/usr/lib/rpm/fdupes_wrapper %check ./%{name} testdir @@ -53,5 +58,6 @@ install -D -m644 %{SOURCE1} %{buildroot}%{_rpmmacrodir}/macros.%{name} %{_bindir}/%{name} %{_mandir}/man1/%{name}.1* %{_rpmmacrodir}/macros.%{name} +/usr/lib/rpm/fdupes_wrapper %changelog diff --git a/fdupes_wrapper.cpp b/fdupes_wrapper.cpp new file mode 100644 index 0000000..0e79721 --- /dev/null +++ b/fdupes_wrapper.cpp @@ -0,0 +1,145 @@ +/* + * A little helper to wrap around fdupes and create hard/soft links of the + * dups found. Used in openSUSE rpm. + * + * Copyright 2022 Jiri Slaby + * 2022 Stephan Kulow + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef std::map> dups_map; +typedef std::pair nlink_pair; + +bool cmp_nlink(const nlink_pair& a, const nlink_pair& b) +{ + return a.second > b.second; +} + +void sort_by_count(const dups_map& in, std::vector& out) +{ + out.clear(); + std::list nlinks; + for (auto it = in.cbegin(); it != in.cend(); ++it) { + nlinks.push_back(std::make_pair(it->first, it->second.size())); + } + nlinks.sort(cmp_nlink); + for (auto it = nlinks.cbegin(); it != nlinks.cend(); ++it) { + out.push_back(it->first); + } +} + +void link_file(const std::string& file, const std::string& target, bool symlink) +{ + std::cout << "Linking " << file << " -> " << target << std::endl; + if (unlink(file.c_str())) { + std::cerr << "Removing '" << file << "' failed." << std::endl; + exit(1); + } + int ret; + if (symlink) { + ret = ::symlink(target.c_str(), file.c_str()); + } else { + ret = link(target.c_str(), file.c_str()); + } + if (ret) { + std::cerr << "Linking '" << file << "' failed." << std::endl; + exit(1); + } +} + +void handle_dups(const dups_map& dups, bool symlink) +{ + // all are hardlinks to the same data + if (dups.size() < 2) + return; + std::vector sorted; + sort_by_count(dups, sorted); + auto inodes = sorted.begin(); + std::string target = dups.at(*inodes).front(); + + for (++inodes; inodes != sorted.end(); ++inodes) { + const std::vector files = dups.at(*inodes); + for (auto it = files.begin(); it != files.end(); ++it) { + link_file(*it, target, symlink); + } + } +} + +int main(int argc, char** argv) +{ + bool symlink = false; + std::string root; + while (1) { + int result = getopt(argc, argv, "s"); + if (result == -1) + break; /* end of list */ + switch (result) { + case 's': + symlink = true; + break; + default: /* unknown */ + break; + } + } + if (optind < argc) { + root = argv[optind++]; + } else { + std::cerr << "Missing directory argument."; + } + if (optind < argc) { + std::cerr << "Too many arguments."; + return 1; + } + /* fdupes options used: + -q: hide progress indicator + -p: don't consider files with different owner/group or permission bits as duplicates + -n: exclude zero-length files from consideration + -o name: output order of duplicates + -r: follow subdirectories + -H: also report hard links as duplicates + */ + std::string command = "fdupes -q -p -n -o name -r -H '" + root + "'"; + FILE* pipe = popen(command.c_str(), "r"); + if (!pipe) { + throw std::runtime_error("popen() failed!"); + } + std::array buffer; + dups_map dups; + while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { + std::string line = buffer.data(); + if (line.length() < 2) { + handle_dups(dups, symlink); + dups.clear(); + continue; + } + if (line.back() != '\n') { + std::cerr << "Too long lines? '" << line << "'" << std::endl; + return 1; + } + line.pop_back(); + + struct stat sb; + if (stat(line.c_str(), &sb)) { + std::cerr << "Stat on '" << buffer.data() << "' failed" << std::endl; + return 1; + } + dups[sb.st_ino].push_back(line); + } + pclose(pipe); + + return 0; +} diff --git a/macros.fdupes b/macros.fdupes index fc9b3ad..be95009 100644 --- a/macros.fdupes +++ b/macros.fdupes @@ -1,21 +1 @@ -%fdupes(s) \ - _target=""; \ - _symlinks=0; \ - %{-s:_symlinks=1;} \ - fdupes -q -p -n -H -o name -r %1 | \ - while read _file; do \ - if test -z "$_target" ; then \ - _target="$_file"; \ - else \ - if test -z "$_file" ; then \ - _target=""; \ - continue ; \ - fi ; \ - if test "$_symlinks" = 1; then \ - ln -sf "${_target#%{buildroot}}" "$_file"; \ - else \ - ln -f "$_target" "$_file"; \ - fi ;\ - fi ; \ - done \ -%{nil} +%fdupes /usr/lib/rpm/fdupes_wrapper -- 2.51.1 From 760afc07b9ced37d0cd154d63a535318558bf9a864d22449d3ba134eceb27a34 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Mon, 14 Mar 2022 14:38:27 +0000 Subject: [PATCH 07/15] Accepting request 961692 from home:coolo:branches:utilities - Handle symlinks (-s argument) correctly OBS-URL: https://build.opensuse.org/request/show/961692 OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=24 --- fdupes.changes | 5 +++++ fdupes_wrapper.cpp | 31 +++++++++++++++++++++++++++---- macros.fdupes | 2 +- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/fdupes.changes b/fdupes.changes index 6c1f2b9..89cd3df 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,8 @@ +------------------------------------------------------------------- +Mon Mar 14 13:44:54 UTC 2022 - Stephan Kulow + +- Handle symlinks (-s argument) correctly + ------------------------------------------------------------------- Sat Mar 12 08:17:37 UTC 2022 - Stephan Kulow diff --git a/fdupes_wrapper.cpp b/fdupes_wrapper.cpp index 0e79721..9972152 100644 --- a/fdupes_wrapper.cpp +++ b/fdupes_wrapper.cpp @@ -61,7 +61,7 @@ void link_file(const std::string& file, const std::string& target, bool symlink) } } -void handle_dups(const dups_map& dups, bool symlink) +void handle_dups(const dups_map& dups, const std::string& buildroot, bool symlink) { // all are hardlinks to the same data if (dups.size() < 2) @@ -70,6 +70,9 @@ void handle_dups(const dups_map& dups, bool symlink) sort_by_count(dups, sorted); auto inodes = sorted.begin(); std::string target = dups.at(*inodes).front(); + if (symlink) { + target.replace(0, buildroot.length(), ""); + } for (++inodes; inodes != sorted.end(); ++inodes) { const std::vector files = dups.at(*inodes); @@ -83,18 +86,32 @@ int main(int argc, char** argv) { bool symlink = false; std::string root; + std::string buildroot; while (1) { - int result = getopt(argc, argv, "s"); + int result = getopt(argc, argv, "sb:"); if (result == -1) break; /* end of list */ switch (result) { case 's': symlink = true; break; + case 'b': + buildroot = optarg; + break; default: /* unknown */ break; } } + if (buildroot.empty()) { + if (symlink) { + std::cerr << "Missing -b argument to remove bootroot from symlink targets"; + return 1; + } + // eliminate final slash from directory argument + if (buildroot.back() == '/') { + buildroot.pop_back(); + } + } if (optind < argc) { root = argv[optind++]; } else { @@ -112,7 +129,13 @@ int main(int argc, char** argv) -r: follow subdirectories -H: also report hard links as duplicates */ - std::string command = "fdupes -q -p -n -o name -r -H '" + root + "'"; + std::string command = "fdupes -q -p -n -o name"; + if (!symlink) { + /* if we create symlinks, avoid looking at hard links being duplicated. This way + fdupes is faster and won't break them up anyway */ + command += " -H"; + } + command += " -r '" + root + "'"; FILE* pipe = popen(command.c_str(), "r"); if (!pipe) { throw std::runtime_error("popen() failed!"); @@ -122,7 +145,7 @@ int main(int argc, char** argv) while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { std::string line = buffer.data(); if (line.length() < 2) { - handle_dups(dups, symlink); + handle_dups(dups, buildroot, symlink); dups.clear(); continue; } diff --git a/macros.fdupes b/macros.fdupes index be95009..fd514d7 100644 --- a/macros.fdupes +++ b/macros.fdupes @@ -1 +1 @@ -%fdupes /usr/lib/rpm/fdupes_wrapper +%fdupes /usr/lib/rpm/fdupes_wrapper -b %{buildroot} -- 2.51.1 From 34728dc6e5b868a7b6b64bcc4e1a3a94c1af03845a8698589212005c2d41c0cb Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Tue, 15 Mar 2022 08:17:48 +0000 Subject: [PATCH 08/15] Accepting request 961811 from home:coolo:branches:utilities This time I branched all staging failures to make sure it's the last one - A more correct approach to creating symlinks (old bug actually): Do not link the files as given by fdupes, but turn them into relative links (it works by chance if given a buildroot, but fails if running on a subdirectory) - Support multiple directories given (as glob to the macro) OBS-URL: https://build.opensuse.org/request/show/961811 OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=25 --- fdupes.changes | 9 ++++ fdupes_wrapper.cpp | 102 ++++++++++++++++++++++++++++++++++----------- macros.fdupes | 2 +- 3 files changed, 87 insertions(+), 26 deletions(-) diff --git a/fdupes.changes b/fdupes.changes index 89cd3df..4012f13 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Tue Mar 15 07:41:35 UTC 2022 - Stephan Kulow + +- A more correct approach to creating symlinks (old bug actually): + Do not link the files as given by fdupes, but turn them into + relative links (it works by chance if given a buildroot, but + fails if running on a subdirectory) +- Support multiple directories given (as glob to the macro) + ------------------------------------------------------------------- Mon Mar 14 13:44:54 UTC 2022 - Stephan Kulow diff --git a/fdupes_wrapper.cpp b/fdupes_wrapper.cpp index 9972152..1ef481a 100644 --- a/fdupes_wrapper.cpp +++ b/fdupes_wrapper.cpp @@ -20,10 +20,65 @@ #include #include #include +#include + +using namespace std; typedef std::map> dups_map; typedef std::pair nlink_pair; +vector split_paths(const string& path) +{ + string token; + vector paths; + stringstream ss(path); + while (getline(ss, token, '/')) { + if (token == "..") { + paths.pop_back(); + } else if (token != "." || ss.eof()) { + paths.push_back(token); + } + } + return paths; +} + +string merge_paths(vector paths) +{ + string path; + for (const auto& s : paths) { + if (s.empty()) + continue; + if (!path.empty()) + path += "/"; + path += s; + } + + return path; +} + +string relative(const string& p1, const string& p2) +{ + vector paths1 = split_paths(p1); + paths1.pop_back(); + vector paths2 = split_paths(p2); + vector paths; + vector::const_iterator it1 = paths1.begin(); + vector::const_iterator it2 = paths2.begin(); + // first remove the common parts + while (it1 != paths1.end() && *it1 == *it2) { + it1++; + it2++; + } + for (; it1 != paths1.end(); ++it1) { + paths.push_back(".."); + } + for (; it2 != paths2.end(); ++it2) { + paths.push_back(*it2); + } + + return merge_paths(paths); +} + bool cmp_nlink(const nlink_pair& a, const nlink_pair& b) { return a.second > b.second; @@ -61,6 +116,14 @@ void link_file(const std::string& file, const std::string& target, bool symlink) } } +std::string target_for_link(string target, const std::string &file, bool symlink) +{ + if (!symlink) // hardlinks don't care + return target; + + return relative(file, target); +} + void handle_dups(const dups_map& dups, const std::string& buildroot, bool symlink) { // all are hardlinks to the same data @@ -70,14 +133,11 @@ void handle_dups(const dups_map& dups, const std::string& buildroot, bool symlin sort_by_count(dups, sorted); auto inodes = sorted.begin(); std::string target = dups.at(*inodes).front(); - if (symlink) { - target.replace(0, buildroot.length(), ""); - } for (++inodes; inodes != sorted.end(); ++inodes) { const std::vector files = dups.at(*inodes); for (auto it = files.begin(); it != files.end(); ++it) { - link_file(*it, target, symlink); + link_file(*it, target_for_link(target, *it, symlink), symlink); } } } @@ -85,7 +145,7 @@ void handle_dups(const dups_map& dups, const std::string& buildroot, bool symlin int main(int argc, char** argv) { bool symlink = false; - std::string root; + std::vector roots; std::string buildroot; while (1) { int result = getopt(argc, argv, "sb:"); @@ -95,32 +155,22 @@ int main(int argc, char** argv) case 's': symlink = true; break; - case 'b': - buildroot = optarg; - break; default: /* unknown */ break; } } - if (buildroot.empty()) { - if (symlink) { - std::cerr << "Missing -b argument to remove bootroot from symlink targets"; - return 1; - } - // eliminate final slash from directory argument - if (buildroot.back() == '/') { - buildroot.pop_back(); + while (optind < argc) { + std::string root = argv[optind++]; + if (root.front() != '/') { + char buffer[PATH_MAX]; + root = std::string(getcwd(buffer, PATH_MAX)) + '/' + root; } + roots.push_back(root); } - if (optind < argc) { - root = argv[optind++]; - } else { + + if (roots.empty()) { std::cerr << "Missing directory argument."; } - if (optind < argc) { - std::cerr << "Too many arguments."; - return 1; - } /* fdupes options used: -q: hide progress indicator -p: don't consider files with different owner/group or permission bits as duplicates @@ -129,13 +179,15 @@ int main(int argc, char** argv) -r: follow subdirectories -H: also report hard links as duplicates */ - std::string command = "fdupes -q -p -n -o name"; + std::string command = "fdupes -q -p -r -n -o name"; if (!symlink) { /* if we create symlinks, avoid looking at hard links being duplicated. This way fdupes is faster and won't break them up anyway */ command += " -H"; } - command += " -r '" + root + "'"; + for (auto it = roots.begin(); it != roots.end(); ++it) { + command += " '" + *it + "'"; + } FILE* pipe = popen(command.c_str(), "r"); if (!pipe) { throw std::runtime_error("popen() failed!"); diff --git a/macros.fdupes b/macros.fdupes index fd514d7..be95009 100644 --- a/macros.fdupes +++ b/macros.fdupes @@ -1 +1 @@ -%fdupes /usr/lib/rpm/fdupes_wrapper -b %{buildroot} +%fdupes /usr/lib/rpm/fdupes_wrapper -- 2.51.1 From ed3b5decd24eaeaba54c266921f72b5dcb533aafd15bf59da3a3740892f8a193 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Thu, 14 Apr 2022 08:06:39 +0000 Subject: [PATCH 09/15] Accepting request 966477 from home:StefanBruens:branches:utilities - Fixes for the new wrapper: * Order duplicates by name, to get a reproducible file set (boo#1197484). * Remove redundant order parameter from fdupes invocation. * Modernize code, significantly reduce allocations. * Exit immediately when mandatory parameters are missing. * Remove obsolete buildroot parameter * Add some tests for the wrapper OBS-URL: https://build.opensuse.org/request/show/966477 OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=26 --- fdupes.changes | 12 +++++ fdupes.spec | 20 +++++++ fdupes_wrapper.cpp | 127 ++++++++++++++++++++++++++------------------- 3 files changed, 105 insertions(+), 54 deletions(-) diff --git a/fdupes.changes b/fdupes.changes index 4012f13..7966cd1 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,15 @@ +------------------------------------------------------------------- +Fri Apr 1 19:50:32 UTC 2022 - Stefan Brüns + +- Fixes for the new wrapper: + * Order duplicates by name, to get a reproducible file set + (boo#1197484). + * Remove redundant order parameter from fdupes invocation. + * Modernize code, significantly reduce allocations. + * Exit immediately when mandatory parameters are missing. + * Remove obsolete buildroot parameter + * Add some tests for the wrapper + ------------------------------------------------------------------- Tue Mar 15 07:41:35 UTC 2022 - Stephan Kulow diff --git a/fdupes.spec b/fdupes.spec index 07be3a5..a916486 100644 --- a/fdupes.spec +++ b/fdupes.spec @@ -53,6 +53,26 @@ install -D -m755 fdupes_wrapper %{buildroot}/usr/lib/rpm/fdupes_wrapper ./%{name} --recurse testdir ./%{name} --size testdir +# Check wrapper +PATH=`pwd`:$PATH +(cd testdir; md5sum ./* ./*/* > ../testdir.md5 || true) +for operation in '-n' '-s' ' '; do + cp -R testdir "testdir${operation}" + ./fdupes_wrapper ${operation} "testdir${operation}" + (cd "testdir${operation}"; md5sum --check ../testdir.md5) +done +# Check order does not depend on creation order - x should be target +mkdir testdir_order +for t in "a b x" "x a b" "a x b"; do + pushd testdir_order + for f in $t ; do cp ../testdir.md5 $f; done + ../fdupes_wrapper -s ./ + test -h ./a + test -h ./b + rm * + popd +done + %files %doc CHANGES %{_bindir}/%{name} diff --git a/fdupes_wrapper.cpp b/fdupes_wrapper.cpp index 1ef481a..7bd8216 100644 --- a/fdupes_wrapper.cpp +++ b/fdupes_wrapper.cpp @@ -4,19 +4,16 @@ * * Copyright 2022 Jiri Slaby * 2022 Stephan Kulow + * 2022 Stefan Brüns * * SPDX-License-Identifier: MIT */ #include -#include #include -#include -#include #include #include #include -#include #include #include #include @@ -24,8 +21,22 @@ using namespace std; -typedef std::map> dups_map; -typedef std::pair nlink_pair; +struct file_entry +{ + ino_t inode; + nlink_t link_count; + string path; + + file_entry(ino_t i, nlink_t n, string&& p) + : inode(i), link_count(n), path(move(p)) {} +}; +using dup_set = vector; + +enum class Operation { + Symlink, + Hardlink, + DryRun, +}; vector split_paths(const string& path) { @@ -42,7 +53,7 @@ vector split_paths(const string& path) return paths; } -string merge_paths(vector paths) +string merge_paths(const vector& paths) { string path; for (const auto& s : paths) { @@ -79,33 +90,18 @@ string relative(const string& p1, const string& p2) return merge_paths(paths); } -bool cmp_nlink(const nlink_pair& a, const nlink_pair& b) -{ - return a.second > b.second; -} - -void sort_by_count(const dups_map& in, std::vector& out) -{ - out.clear(); - std::list nlinks; - for (auto it = in.cbegin(); it != in.cend(); ++it) { - nlinks.push_back(std::make_pair(it->first, it->second.size())); - } - nlinks.sort(cmp_nlink); - for (auto it = nlinks.cbegin(); it != nlinks.cend(); ++it) { - out.push_back(it->first); - } -} - -void link_file(const std::string& file, const std::string& target, bool symlink) +void link_file(const std::string& file, const std::string& target, Operation op) { std::cout << "Linking " << file << " -> " << target << std::endl; + if (op == Operation::DryRun) + return; + if (unlink(file.c_str())) { std::cerr << "Removing '" << file << "' failed." << std::endl; exit(1); } int ret; - if (symlink) { + if (op == Operation::Symlink) { ret = ::symlink(target.c_str(), file.c_str()); } else { ret = link(target.c_str(), file.c_str()); @@ -116,44 +112,65 @@ void link_file(const std::string& file, const std::string& target, bool symlink) } } -std::string target_for_link(string target, const std::string &file, bool symlink) +std::string target_for_link(string target, const std::string &file, Operation op) { - if (!symlink) // hardlinks don't care + if (op == Operation::Hardlink) // hardlinks don't care return target; - + return relative(file, target); } -void handle_dups(const dups_map& dups, const std::string& buildroot, bool symlink) +void handle_dups(dup_set& dups, Operation op) { - // all are hardlinks to the same data - if (dups.size() < 2) - return; - std::vector sorted; - sort_by_count(dups, sorted); - auto inodes = sorted.begin(); - std::string target = dups.at(*inodes).front(); - - for (++inodes; inodes != sorted.end(); ++inodes) { - const std::vector files = dups.at(*inodes); - for (auto it = files.begin(); it != files.end(); ++it) { - link_file(*it, target_for_link(target, *it, symlink), symlink); + // calculate number of hardlinked duplicates found, for each file + // this may be different than the st_nlink value + std::sort(dups.begin(), dups.end(), [](const file_entry& a, const file_entry& b) { + return a.inode < b.inode; + }); + auto first = dups.begin(); + while (first != dups.end()) { + auto r = equal_range(first, dups.end(), *first, [](const file_entry& a, const file_entry& b) { + return a.inode < b.inode; + }); + for (auto i = r.first; i != r.second; ++i) { + i->link_count = std::distance(r.first, r.second); } + first = r.second; + } + + // use the file with most hardlinks as target + // in case of ties, sort by name to get a stable order for reproducible builds + std::sort(dups.begin(), dups.end(), [](const file_entry& a, const file_entry& b) { + if (a.link_count == b.link_count) + return a.path > b.path; + return a.link_count > b.link_count; + }); + + const string& target = dups[0].path; + + for (const file_entry& e : dups) { + // skip duplicates hardlinked to first entry + if (e.inode == dups[0].inode) + continue; + + link_file(e.path, target_for_link(target, e.path, op), op); } } int main(int argc, char** argv) { - bool symlink = false; + Operation op = Operation::Hardlink; std::vector roots; - std::string buildroot; while (1) { - int result = getopt(argc, argv, "sb:"); + int result = getopt(argc, argv, "sn"); if (result == -1) break; /* end of list */ switch (result) { case 's': - symlink = true; + op = Operation::Symlink; + break; + case 'n': + op = Operation::DryRun; break; default: /* unknown */ break; @@ -170,17 +187,17 @@ int main(int argc, char** argv) if (roots.empty()) { std::cerr << "Missing directory argument."; + return 1; } /* fdupes options used: -q: hide progress indicator -p: don't consider files with different owner/group or permission bits as duplicates -n: exclude zero-length files from consideration - -o name: output order of duplicates -r: follow subdirectories -H: also report hard links as duplicates */ - std::string command = "fdupes -q -p -r -n -o name"; - if (!symlink) { + std::string command = "fdupes -q -p -r -n"; + if (op != Operation::Symlink) { /* if we create symlinks, avoid looking at hard links being duplicated. This way fdupes is faster and won't break them up anyway */ command += " -H"; @@ -192,12 +209,14 @@ int main(int argc, char** argv) if (!pipe) { throw std::runtime_error("popen() failed!"); } - std::array buffer; - dups_map dups; + std::vector buffer; + buffer.resize(MAXPATHLEN); + + dup_set dups; while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { std::string line = buffer.data(); if (line.length() < 2) { - handle_dups(dups, buildroot, symlink); + handle_dups(dups, op); dups.clear(); continue; } @@ -212,7 +231,7 @@ int main(int argc, char** argv) std::cerr << "Stat on '" << buffer.data() << "' failed" << std::endl; return 1; } - dups[sb.st_ino].push_back(line); + dups.emplace_back(sb.st_ino, 0, std::move(line)); } pclose(pipe); -- 2.51.1 From 846974152961682c73e2872c11bf19b8e200b55156c5dc7f4004e8726fcbadd0 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Thu, 6 Oct 2022 10:34:50 +0000 Subject: [PATCH 10/15] Accepting request 1007561 from home:amanzini:branches:utilities - update to 2.2.1: * Fix bug in code meant to skip over the current log file when --log option is given. * Updates to copyright notices in source code. * Add --deferconfirmation option. * Check that files marked as duplicates haven't changed during program execution before deleting them. * Update documentation to indicate units for SIZE in command-line options. * Move some configuration settings to configure.ac file. OBS-URL: https://build.opensuse.org/request/show/1007561 OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=27 --- fdupes-2.1.2.tar.gz | 3 --- fdupes-2.2.1.tar.gz | 3 +++ fdupes.changes | 11 +++++++++++ fdupes.spec | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) delete mode 100644 fdupes-2.1.2.tar.gz create mode 100644 fdupes-2.2.1.tar.gz diff --git a/fdupes-2.1.2.tar.gz b/fdupes-2.1.2.tar.gz deleted file mode 100644 index 462dcb3..0000000 --- a/fdupes-2.1.2.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cd5cb53b6d898cf20f19b57b81114a5b263cc1149cd0da3104578b083b2837bd -size 142266 diff --git a/fdupes-2.2.1.tar.gz b/fdupes-2.2.1.tar.gz new file mode 100644 index 0000000..66730a0 --- /dev/null +++ b/fdupes-2.2.1.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:846bb79ca3f0157856aa93ed50b49217feb68e1b35226193b6bc578be0c5698d +size 144719 diff --git a/fdupes.changes b/fdupes.changes index 7966cd1..b0af349 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,14 @@ +------------------------------------------------------------------- +Sun Oct 2 16:21:27 UTC 2022 - Andrea Manzini + +- update to 2.2.1: + * Fix bug in code meant to skip over the current log file when --log option is given. + * Updates to copyright notices in source code. + * Add --deferconfirmation option. + * Check that files marked as duplicates haven't changed during program execution before deleting them. + * Update documentation to indicate units for SIZE in command-line options. + * Move some configuration settings to configure.ac file. + ------------------------------------------------------------------- Fri Apr 1 19:50:32 UTC 2022 - Stefan Brüns diff --git a/fdupes.spec b/fdupes.spec index a916486..2e1bd9f 100644 --- a/fdupes.spec +++ b/fdupes.spec @@ -19,7 +19,7 @@ %{?!_rpmmacrodir:%define _rpmmacrodir /usr/lib/rpm/macros.d} Name: fdupes -Version: 2.1.2 +Version: 2.2.1 Release: 0 Summary: Tool to identify or delete duplicate files License: MIT -- 2.51.1 From 99c011fa395dcc452d34071d621eb224182dbd6159db87e12356b66122eaa360 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Mon, 18 Mar 2024 11:40:53 +0000 Subject: [PATCH 11/15] Accepting request 1158935 from home:simonlm:branches:utilities Update to version 2.3.0 OBS-URL: https://build.opensuse.org/request/show/1158935 OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=28 --- fdupes-2.2.1.tar.gz | 3 --- fdupes-2.3.0.tar.gz | 3 +++ fdupes.changes | 9 +++++++++ fdupes.spec | 5 +++-- 4 files changed, 15 insertions(+), 5 deletions(-) delete mode 100644 fdupes-2.2.1.tar.gz create mode 100644 fdupes-2.3.0.tar.gz diff --git a/fdupes-2.2.1.tar.gz b/fdupes-2.2.1.tar.gz deleted file mode 100644 index 66730a0..0000000 --- a/fdupes-2.2.1.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:846bb79ca3f0157856aa93ed50b49217feb68e1b35226193b6bc578be0c5698d -size 144719 diff --git a/fdupes-2.3.0.tar.gz b/fdupes-2.3.0.tar.gz new file mode 100644 index 0000000..c7c8666 --- /dev/null +++ b/fdupes-2.3.0.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6170d64a7e565ee314cca4dd25a123e60aa1e3febb11e57078bebb9c1da7e019 +size 154700 diff --git a/fdupes.changes b/fdupes.changes index b0af349..e35717a 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Mon Mar 18 09:36:27 UTC 2024 - ming li + +- Update to 2.3.0: + * Add --cache option to speed up file comparisons. + * Use nanosecond precision for file times, if available. + * Fix compilation issue on OpenBSD. + * Other changes like fixing typos, wording, etc. + ------------------------------------------------------------------- Sun Oct 2 16:21:27 UTC 2022 - Andrea Manzini diff --git a/fdupes.spec b/fdupes.spec index 2e1bd9f..520bb77 100644 --- a/fdupes.spec +++ b/fdupes.spec @@ -1,7 +1,7 @@ # # spec file for package fdupes # -# Copyright (c) 2022 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %{?!_rpmmacrodir:%define _rpmmacrodir /usr/lib/rpm/macros.d} Name: fdupes -Version: 2.2.1 +Version: 2.3.0 Release: 0 Summary: Tool to identify or delete duplicate files License: MIT @@ -29,6 +29,7 @@ Source0: https://github.com/adrianlopezroche/fdupes/releases/download/v%{ Source1: macros.fdupes Source2: fdupes_wrapper.cpp BuildRequires: gcc-c++ +BuildRequires: sqlite3-devel %description FDUPES is a program for identifying or deleting duplicate files -- 2.51.1 From b229993c321d741d79e9573dba350b06566d819702238c8668dcba00a8cbb911 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Wed, 20 Mar 2024 07:53:16 +0000 Subject: [PATCH 12/15] Accepting request 1159769 from home:dimstar:Factory - Do not use sqlite, as this pulls sqlite into Ring0 at no real benefit performance wise: the cache is not reused between runs. + Drop sqlite-devel BuildRequires + Pass --without-sqlite to configure OBS-URL: https://build.opensuse.org/request/show/1159769 OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=29 --- fdupes.changes | 8 ++++++++ fdupes.spec | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fdupes.changes b/fdupes.changes index e35717a..85ccaa0 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Wed Mar 20 07:25:33 UTC 2024 - Dominique Leuenberger + +- Do not use sqlite, as this pulls sqlite into Ring0 at no real + benefit performance wise: the cache is not reused between runs. + + Drop sqlite-devel BuildRequires + + Pass --without-sqlite to configure + ------------------------------------------------------------------- Mon Mar 18 09:36:27 UTC 2024 - ming li diff --git a/fdupes.spec b/fdupes.spec index 520bb77..9b891d0 100644 --- a/fdupes.spec +++ b/fdupes.spec @@ -29,7 +29,6 @@ Source0: https://github.com/adrianlopezroche/fdupes/releases/download/v%{ Source1: macros.fdupes Source2: fdupes_wrapper.cpp BuildRequires: gcc-c++ -BuildRequires: sqlite3-devel %description FDUPES is a program for identifying or deleting duplicate files @@ -39,7 +38,7 @@ residing within specified directories. %autosetup -p1 %build -%configure --without-ncurses +%configure --without-ncurses --without-sqlite %make_build g++ $RPM_OPT_FLAGS %{S:2} -o fdupes_wrapper -- 2.51.1 From ed68c6cef602c47b160be41cc8833b0abdbdd84c070709216c7e5f713510e442 Mon Sep 17 00:00:00 2001 From: Peter Simons Date: Thu, 18 Jul 2024 05:58:10 +0000 Subject: [PATCH 13/15] - update to 2.3.1: * Fix buffer overflow bug in getrealpath() function. OBS-URL: https://build.opensuse.org/package/show/utilities/fdupes?expand=0&rev=30 --- .gitattributes | 23 ++++ .gitignore | 1 + fdupes-2.3.0.tar.gz | 3 + fdupes-2.3.1.tar.gz | 3 + fdupes.changes | 283 ++++++++++++++++++++++++++++++++++++++++++++ fdupes.spec | 83 +++++++++++++ fdupes_wrapper.cpp | 239 +++++++++++++++++++++++++++++++++++++ macros.fdupes | 1 + 8 files changed, 636 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 fdupes-2.3.0.tar.gz create mode 100644 fdupes-2.3.1.tar.gz create mode 100644 fdupes.changes create mode 100644 fdupes.spec create mode 100644 fdupes_wrapper.cpp create mode 100644 macros.fdupes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b03811 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +## Default LFS +*.7z filter=lfs diff=lfs merge=lfs -text +*.bsp filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.gem filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.lz filter=lfs diff=lfs merge=lfs -text +*.lzma filter=lfs diff=lfs merge=lfs -text +*.obscpio filter=lfs diff=lfs merge=lfs -text +*.oxt filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.rpm filter=lfs diff=lfs merge=lfs -text +*.tbz filter=lfs diff=lfs merge=lfs -text +*.tbz2 filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.txz filter=lfs diff=lfs merge=lfs -text +*.whl filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57affb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.osc diff --git a/fdupes-2.3.0.tar.gz b/fdupes-2.3.0.tar.gz new file mode 100644 index 0000000..c7c8666 --- /dev/null +++ b/fdupes-2.3.0.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6170d64a7e565ee314cca4dd25a123e60aa1e3febb11e57078bebb9c1da7e019 +size 154700 diff --git a/fdupes-2.3.1.tar.gz b/fdupes-2.3.1.tar.gz new file mode 100644 index 0000000..37e286b --- /dev/null +++ b/fdupes-2.3.1.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2482b4b8c931bd17cea21f4c27fa4747b877523029d57f794a2b48e6c378db17 +size 155094 diff --git a/fdupes.changes b/fdupes.changes new file mode 100644 index 0000000..a28d842 --- /dev/null +++ b/fdupes.changes @@ -0,0 +1,283 @@ +------------------------------------------------------------------- +Sat Jun 29 20:11:57 UTC 2024 - Dirk Müller + +- update to 2.3.1: + * Fix buffer overflow bug in getrealpath() function. + +------------------------------------------------------------------- +Wed Mar 20 07:25:33 UTC 2024 - Dominique Leuenberger + +- Do not use sqlite, as this pulls sqlite into Ring0 at no real + benefit performance wise: the cache is not reused between runs. + + Drop sqlite-devel BuildRequires + + Pass --without-sqlite to configure + +------------------------------------------------------------------- +Mon Mar 18 09:36:27 UTC 2024 - ming li + +- Update to 2.3.0: + * Add --cache option to speed up file comparisons. + * Use nanosecond precision for file times, if available. + * Fix compilation issue on OpenBSD. + * Other changes like fixing typos, wording, etc. + +------------------------------------------------------------------- +Sun Oct 2 16:21:27 UTC 2022 - Andrea Manzini + +- update to 2.2.1: + * Fix bug in code meant to skip over the current log file when --log option is given. + * Updates to copyright notices in source code. + * Add --deferconfirmation option. + * Check that files marked as duplicates haven't changed during program execution before deleting them. + * Update documentation to indicate units for SIZE in command-line options. + * Move some configuration settings to configure.ac file. + +------------------------------------------------------------------- +Fri Apr 1 19:50:32 UTC 2022 - Stefan Brüns + +- Fixes for the new wrapper: + * Order duplicates by name, to get a reproducible file set + (boo#1197484). + * Remove redundant order parameter from fdupes invocation. + * Modernize code, significantly reduce allocations. + * Exit immediately when mandatory parameters are missing. + * Remove obsolete buildroot parameter + * Add some tests for the wrapper + +------------------------------------------------------------------- +Tue Mar 15 07:41:35 UTC 2022 - Stephan Kulow + +- A more correct approach to creating symlinks (old bug actually): + Do not link the files as given by fdupes, but turn them into + relative links (it works by chance if given a buildroot, but + fails if running on a subdirectory) +- Support multiple directories given (as glob to the macro) + +------------------------------------------------------------------- +Mon Mar 14 13:44:54 UTC 2022 - Stephan Kulow + +- Handle symlinks (-s argument) correctly + +------------------------------------------------------------------- +Sat Mar 12 08:17:37 UTC 2022 - Stephan Kulow + +- Simplify macros.fdupes with a call to a C++ program that does + the same within a fraction of a second what the shell loop did + in many seconds (bsc#1195709) + +------------------------------------------------------------------- +Sun Aug 16 16:59:45 UTC 2020 - Dirk Mueller + +- update to 2.1.2: + * Do not enter ncurses mode when --immediate option given. + * Fix logging/memory corruption bug when using --log with --immediate. + * Break mtime ties using ctime when sorting by time. + * Reduce number of calls to stat(), for speed. + * Clear last command status when new command is entered. + * Rename cs command ("clear all selections") from cs to csel. + * Rename igs command ("invert selections") from igs to isel. + * Add "prune" command as synonym for DELETE key. + * Clear selections after deleting files via prune/DELETE. + * Fix dependency issues when fdupes is configured to not use ncurses. +- build without ncurses for now until buildcycles can be solved + +------------------------------------------------------------------- +Fri Jun 5 23:42:10 UTC 2020 - Jan Engelhardt + +- Use noun phrase in summary. Drop old specfile constructs. + +------------------------------------------------------------------- +Fri May 22 08:27:11 UTC 2020 - Paolo Stivanin + +- Update to v2.0.0 + * Add ncurses mode for interactive file deletion (plain mode still available via --plain or ./configure). + * Add --minsize option. + * Add --maxsize option. + * Add --time option. + * Add --order=ctime option. + * Add --log option. + * Use configure script for installation (Autotools/Automake). +- Remove fdupes-makefile.patch + +------------------------------------------------------------------- +Thu Apr 16 21:07:45 UTC 2020 - Matej Cepl + +- Make package building even on platforms, where _rpmmacrodir + is not defined. + +------------------------------------------------------------------- +Wed May 8 09:37:54 UTC 2019 - Dominique Leuenberger + +- Move RPM macros to %_rpmmacrodir. + +------------------------------------------------------------------- +Fri Dec 16 12:40:20 UTC 2016 - psimons@suse.com + +- We cannot update from fdupes 1.51 to 1.6.1. That "downgrade" + works okay'ish for Tumbleweed because we can replace the old + package with the new one, but in SLE this is not possible. We + asked upstream to please release a "2.0" version to remedy these + issues (https://github.com/adrianlopezroche/fdupes/issues/74), + but he does not respond. Therefore, we'll call this version 1.61, + ignoring upstreams change in the versioning scheme. + +------------------------------------------------------------------- +Mon Dec 5 13:54:08 UTC 2016 - psimons@suse.com + +- Upstream has changed their versioning scheme after version 1.51. + Unfortunately, the new version 1.6.x won't be recognized as + "newer" by zypper. This commit adds appropriate "provides" and + "obsoletes" attributes to the spec file to remedy that issue. + +------------------------------------------------------------------- +Fri Nov 4 14:33:59 UTC 2016 - psimons@suse.com + +- Drop 50_bts284274_hardlinkreplace.dpatch. The --linkhard option + added by this patch has an implementation bug that can cause data + loss. https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=677419 + has more details. + +------------------------------------------------------------------- +Fri Nov 4 13:47:27 UTC 2016 - psimons@suse.com + +- Update to version 1.6.1. The following patches have been applied + upstream and were dropped: + * 0001-restore-pristine-code.patch + * 0002-Added-to-escape-minus-signs-in-manpage-lintian-warni.patch + * 0003-Fix-a-typo-in-a-manpage-bts353789.patch + * 0005-add-summarize-to-manpage-bts481809.patch + * 0006-add-nohidden-support-bts511702.patch + * 0007-Disambiguate-the-options-recurse-and-recurse-bts5371.patch + * 0008-speedup-the-file-compare.patch + * 0009-glibc-endianness-check-in-md5.patch + * 0010-add-permissions-mode.patch + * 0011-add-an-option-to-sort-duplicate-files-by-name.patch +- 50_bts284274_hardlinkreplace.dpatch had to be refreshed. + +------------------------------------------------------------------- +Tue Aug 4 13:01:47 UTC 2015 - tchvatal@suse.com + +- By default relink hardlinks too, should fix bnc#940296 + +------------------------------------------------------------------- +Mon Aug 3 19:26:55 UTC 2015 - tchvatal@suse.com + +- Update to upstream git repo on github +- Refresh patches: + * fdupes-makefile.patch + * 0008-speedup-the-file-compare.patch + * 0010-add-permissions-mode.patch + * 0011-add-an-option-to-sort-duplicate-files-by-name.patch + * 50_bts284274_hardlinkreplace.dpatch +- Upstreamed patch: + * 0004-Large-file-support-for-2GB-files-bts447601.patch +- Remove whitespace from fdupes.macros file +- Cleanup with spec-cleaner + - Obey rpm-opt-flags + - run test phase + +------------------------------------------------------------------- +Sun Dec 21 19:58:41 UTC 2014 - bwiedemann@suse.com + +- add -L (--linkhard) option + add 50_bts284274_hardlinkreplace.dpatch + +------------------------------------------------------------------- +Tue Apr 29 16:08:34 UTC 2014 - stefan.bruens@rwth-aachen.de + +- sort the output of fdupes by filename to make it deterministic + for parallel builds + * 0011-add-an-option-to-sort-duplicate-files-by-name.patch + +------------------------------------------------------------------- +Tue Oct 16 11:44:08 UTC 2012 - mvyskocil@suse.com + +- update to 1.5.0-PR2 + * new "--summarize" option + * new "--recurse:" selective recursion option + * new "--noprompt" option for totally automated deletion of + duplicate files. + * sorts duplicates (old to new) for consistent order when + listing or deleteing duplicate files. + * tests for early matching of files, which should help speed up + the matching process when large files are involved. + * warns whenever a file cannot be deleted. + * bugfixes (proper file closing, zero-length files, ...) +- drop the fdupes-sort-output.diff (upstream uses mtime based) +- rename and rebase fdupes-speedup.patch to 0008-speedup-the-compare.patch +- rename and rebase fdupes-endianness.patch to + 0009-glibc-endianness-check-in-md5.patch +- add -p/--permissions switch so files with different permissions or uid/gid + are not considered as duplicates (bnc#784670) + * this mode is a default one for fdupes macro + 0010-add-permissions-mode.patch +- imported several fixes from Debian + * 0001-restore-pristine-code.patch - some common code fixes, partly obsoletes + speedup patch + * manual page fixes + 0002-Added-to-escape-minus-signs-in-manpage-lintian-warni.patch + 0003-Fix-a-typo-in-a-manpage-bts353789.patch + 0005-add-summarize-to-manpage-bts481809.patch + 0006-add-nohidden-support-bts511702.patch + 0007-Disambiguate-the-options-recurse-and-recurse-bts5371.patch + * 0004-Large-file-support-for-2GB-files-bts447601.patch - large file support + +------------------------------------------------------------------- +Sun Mar 25 22:13:12 UTC 2012 - behrisch@users.sf.net + +- added "which" requirement for red hat distros + +------------------------------------------------------------------- +Mon Feb 13 10:46:03 UTC 2012 - coolo@suse.com + +- patch license to follow spdx.org standard + +------------------------------------------------------------------- +Wed Oct 5 15:14:32 UTC 2011 - uli@suse.com + +- cross-build workaround: fake gcc script to work around build + system not honoring CC + +------------------------------------------------------------------- +Sun Sep 18 17:17:12 UTC 2011 - jengelh@medozas.de + +- Apply packaging guidelines (remove redundant/obsolete + tags/sections from specfile, etc.) + +------------------------------------------------------------------- +Mon Feb 15 15:43:34 UTC 2010 - mvyskocil@suse.cz + +- fix bnc#406825: speedup fdupes + * fdupes-speedup.patch fixes some performance gaps in code + * fdupes-endianness.patch speedups the built in md5 on little endian machines + +------------------------------------------------------------------- +Wed Aug 26 12:53:54 CEST 2009 - mls@suse.de + +- make patch0 usage consistent + +------------------------------------------------------------------- +Thu Jan 15 17:05:36 CET 2009 - coolo@suse.de + +- sort the output of fdupes to make it deterministic + +------------------------------------------------------------------- +Thu Sep 6 18:41:37 CEST 2007 - mls@suse.de + +- do not hardlink empty files in %fdupes macro + +------------------------------------------------------------------- +Wed Sep 5 15:44:52 CEST 2007 - nadvornik@suse.cz + +- support filenames with spaces in %fdupes macro [#307727] + +------------------------------------------------------------------- +Tue May 15 22:53:03 CEST 2007 - coolo@suse.de + +- add an RPM macro to make use of it in spec files + +------------------------------------------------------------------- +Thu Nov 16 13:16:07 CET 2006 - dmueller@suse.de + +- Initial package (1.40) + diff --git a/fdupes.spec b/fdupes.spec new file mode 100644 index 0000000..92af5a6 --- /dev/null +++ b/fdupes.spec @@ -0,0 +1,83 @@ +# +# spec file for package fdupes +# +# Copyright (c) 2024 SUSE LLC +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# + + +%{?!_rpmmacrodir:%define _rpmmacrodir /usr/lib/rpm/macros.d} + +Name: fdupes +Version: 2.3.1 +Release: 0 +Summary: Tool to identify or delete duplicate files +License: MIT +Group: Productivity/Archiving/Compression +URL: https://github.com/adrianlopezroche/fdupes +Source0: https://github.com/adrianlopezroche/fdupes/releases/download/v%{version}/fdupes-%{version}.tar.gz +Source1: macros.fdupes +Source2: fdupes_wrapper.cpp +BuildRequires: gcc-c++ + +%description +FDUPES is a program for identifying or deleting duplicate files +residing within specified directories. + +%prep +%autosetup -p1 + +%build +%configure --without-ncurses --without-sqlite +%make_build +g++ $RPM_OPT_FLAGS %{S:2} -o fdupes_wrapper + +%install +%make_install +install -D -m644 %{SOURCE1} %{buildroot}%{_rpmmacrodir}/macros.%{name} +install -D -m755 fdupes_wrapper %{buildroot}/usr/lib/rpm/fdupes_wrapper + +%check +./%{name} testdir +./%{name} --omitfirst testdir +./%{name} --recurse testdir +./%{name} --size testdir + +# Check wrapper +PATH=`pwd`:$PATH +(cd testdir; md5sum ./* ./*/* > ../testdir.md5 || true) +for operation in '-n' '-s' ' '; do + cp -R testdir "testdir${operation}" + ./fdupes_wrapper ${operation} "testdir${operation}" + (cd "testdir${operation}"; md5sum --check ../testdir.md5) +done +# Check order does not depend on creation order - x should be target +mkdir testdir_order +for t in "a b x" "x a b" "a x b"; do + pushd testdir_order + for f in $t ; do cp ../testdir.md5 $f; done + ../fdupes_wrapper -s ./ + test -h ./a + test -h ./b + rm * + popd +done + +%files +%doc CHANGES +%{_bindir}/%{name} +%{_mandir}/man1/%{name}.1* +%{_rpmmacrodir}/macros.%{name} +/usr/lib/rpm/fdupes_wrapper + +%changelog diff --git a/fdupes_wrapper.cpp b/fdupes_wrapper.cpp new file mode 100644 index 0000000..7bd8216 --- /dev/null +++ b/fdupes_wrapper.cpp @@ -0,0 +1,239 @@ +/* + * A little helper to wrap around fdupes and create hard/soft links of the + * dups found. Used in openSUSE rpm. + * + * Copyright 2022 Jiri Slaby + * 2022 Stephan Kulow + * 2022 Stefan Brüns + * + * SPDX-License-Identifier: MIT + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct file_entry +{ + ino_t inode; + nlink_t link_count; + string path; + + file_entry(ino_t i, nlink_t n, string&& p) + : inode(i), link_count(n), path(move(p)) {} +}; +using dup_set = vector; + +enum class Operation { + Symlink, + Hardlink, + DryRun, +}; + +vector split_paths(const string& path) +{ + string token; + vector paths; + stringstream ss(path); + while (getline(ss, token, '/')) { + if (token == "..") { + paths.pop_back(); + } else if (token != "." || ss.eof()) { + paths.push_back(token); + } + } + return paths; +} + +string merge_paths(const vector& paths) +{ + string path; + for (const auto& s : paths) { + if (s.empty()) + continue; + if (!path.empty()) + path += "/"; + path += s; + } + + return path; +} + +string relative(const string& p1, const string& p2) +{ + vector paths1 = split_paths(p1); + paths1.pop_back(); + vector paths2 = split_paths(p2); + vector paths; + vector::const_iterator it1 = paths1.begin(); + vector::const_iterator it2 = paths2.begin(); + // first remove the common parts + while (it1 != paths1.end() && *it1 == *it2) { + it1++; + it2++; + } + for (; it1 != paths1.end(); ++it1) { + paths.push_back(".."); + } + for (; it2 != paths2.end(); ++it2) { + paths.push_back(*it2); + } + + return merge_paths(paths); +} + +void link_file(const std::string& file, const std::string& target, Operation op) +{ + std::cout << "Linking " << file << " -> " << target << std::endl; + if (op == Operation::DryRun) + return; + + if (unlink(file.c_str())) { + std::cerr << "Removing '" << file << "' failed." << std::endl; + exit(1); + } + int ret; + if (op == Operation::Symlink) { + ret = ::symlink(target.c_str(), file.c_str()); + } else { + ret = link(target.c_str(), file.c_str()); + } + if (ret) { + std::cerr << "Linking '" << file << "' failed." << std::endl; + exit(1); + } +} + +std::string target_for_link(string target, const std::string &file, Operation op) +{ + if (op == Operation::Hardlink) // hardlinks don't care + return target; + + return relative(file, target); +} + +void handle_dups(dup_set& dups, Operation op) +{ + // calculate number of hardlinked duplicates found, for each file + // this may be different than the st_nlink value + std::sort(dups.begin(), dups.end(), [](const file_entry& a, const file_entry& b) { + return a.inode < b.inode; + }); + auto first = dups.begin(); + while (first != dups.end()) { + auto r = equal_range(first, dups.end(), *first, [](const file_entry& a, const file_entry& b) { + return a.inode < b.inode; + }); + for (auto i = r.first; i != r.second; ++i) { + i->link_count = std::distance(r.first, r.second); + } + first = r.second; + } + + // use the file with most hardlinks as target + // in case of ties, sort by name to get a stable order for reproducible builds + std::sort(dups.begin(), dups.end(), [](const file_entry& a, const file_entry& b) { + if (a.link_count == b.link_count) + return a.path > b.path; + return a.link_count > b.link_count; + }); + + const string& target = dups[0].path; + + for (const file_entry& e : dups) { + // skip duplicates hardlinked to first entry + if (e.inode == dups[0].inode) + continue; + + link_file(e.path, target_for_link(target, e.path, op), op); + } +} + +int main(int argc, char** argv) +{ + Operation op = Operation::Hardlink; + std::vector roots; + while (1) { + int result = getopt(argc, argv, "sn"); + if (result == -1) + break; /* end of list */ + switch (result) { + case 's': + op = Operation::Symlink; + break; + case 'n': + op = Operation::DryRun; + break; + default: /* unknown */ + break; + } + } + while (optind < argc) { + std::string root = argv[optind++]; + if (root.front() != '/') { + char buffer[PATH_MAX]; + root = std::string(getcwd(buffer, PATH_MAX)) + '/' + root; + } + roots.push_back(root); + } + + if (roots.empty()) { + std::cerr << "Missing directory argument."; + return 1; + } + /* fdupes options used: + -q: hide progress indicator + -p: don't consider files with different owner/group or permission bits as duplicates + -n: exclude zero-length files from consideration + -r: follow subdirectories + -H: also report hard links as duplicates + */ + std::string command = "fdupes -q -p -r -n"; + if (op != Operation::Symlink) { + /* if we create symlinks, avoid looking at hard links being duplicated. This way + fdupes is faster and won't break them up anyway */ + command += " -H"; + } + for (auto it = roots.begin(); it != roots.end(); ++it) { + command += " '" + *it + "'"; + } + FILE* pipe = popen(command.c_str(), "r"); + if (!pipe) { + throw std::runtime_error("popen() failed!"); + } + std::vector buffer; + buffer.resize(MAXPATHLEN); + + dup_set dups; + while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { + std::string line = buffer.data(); + if (line.length() < 2) { + handle_dups(dups, op); + dups.clear(); + continue; + } + if (line.back() != '\n') { + std::cerr << "Too long lines? '" << line << "'" << std::endl; + return 1; + } + line.pop_back(); + + struct stat sb; + if (stat(line.c_str(), &sb)) { + std::cerr << "Stat on '" << buffer.data() << "' failed" << std::endl; + return 1; + } + dups.emplace_back(sb.st_ino, 0, std::move(line)); + } + pclose(pipe); + + return 0; +} diff --git a/macros.fdupes b/macros.fdupes new file mode 100644 index 0000000..be95009 --- /dev/null +++ b/macros.fdupes @@ -0,0 +1 @@ +%fdupes /usr/lib/rpm/fdupes_wrapper -- 2.51.1 From aaa62f3707c4a30a3968f1a0086a8e5a377e4452de269816e2d27aa58acc44a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Thu, 16 Oct 2025 13:46:41 +0100 Subject: [PATCH 14/15] Only build package on RHEL based environments --- fdupes.spec | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fdupes.spec b/fdupes.spec index 92af5a6..b062090 100644 --- a/fdupes.spec +++ b/fdupes.spec @@ -29,6 +29,9 @@ Source0: https://github.com/adrianlopezroche/fdupes/releases/download/v%{ Source1: macros.fdupes Source2: fdupes_wrapper.cpp BuildRequires: gcc-c++ +%if ! 0%{?rhel} +ExclusiveArch: do_not_build +%endif %description FDUPES is a program for identifying or deleting duplicate files -- 2.51.1 From f9d4127c9f8e7b81d18b0a2ef80cf2155bcf8eafad9ac3380c51671120bf1bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= Date: Fri, 24 Oct 2025 16:06:46 +0100 Subject: [PATCH 15/15] Add missing changelog entry --- fdupes.changes | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fdupes.changes b/fdupes.changes index a28d842..5381a28 100644 --- a/fdupes.changes +++ b/fdupes.changes @@ -1,3 +1,8 @@ +------------------------------------------------------------------- +Fri Oct 24 15:06:13 UTC 2025 - Pablo Suárez Hernández + +- Only build package on RHEL environments + ------------------------------------------------------------------- Sat Jun 29 20:11:57 UTC 2024 - Dirk Müller -- 2.51.1