From d3c7028710e832130f9c23d3df656f9460c5a925 Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Mon, 27 Nov 2017 18:18:37 +0100 Subject: [PATCH] Support CoW clones on both macOS (with APFS) and Linux (with BTRFS) and enable those actions in the GUI --- source/net/filebot/StandardRenameAction.java | 59 +++++++++++------- source/net/filebot/cli/ArgumentBean.java | 2 +- source/net/filebot/resources/action.clone.png | Bin 0 -> 1909 bytes .../net/filebot/resources/action.clone@2x.png | Bin 0 -> 3107 bytes .../filebot/resources/rename.action.clone.png | Bin 0 -> 1055 bytes .../resources/rename.action.clone@2x.png | Bin 0 -> 1909 bytes source/net/filebot/ui/rename/Preset.java | 10 ++- source/net/filebot/ui/rename/RenamePanel.java | 3 +- 8 files changed, 46 insertions(+), 28 deletions(-) create mode 100644 source/net/filebot/resources/action.clone.png create mode 100644 source/net/filebot/resources/action.clone@2x.png create mode 100644 source/net/filebot/resources/rename.action.clone.png create mode 100644 source/net/filebot/resources/rename.action.clone@2x.png diff --git a/source/net/filebot/StandardRenameAction.java b/source/net/filebot/StandardRenameAction.java index 478f5154..ed0132ca 100644 --- a/source/net/filebot/StandardRenameAction.java +++ b/source/net/filebot/StandardRenameAction.java @@ -11,6 +11,8 @@ import java.nio.file.LinkOption; import java.nio.file.attribute.BasicFileAttributes; import java.util.List; +import com.sun.jna.Platform; + import net.filebot.util.FileUtilities; public enum StandardRenameAction implements RenameAction { @@ -79,6 +81,35 @@ public enum StandardRenameAction implements RenameAction { } }, + CLONE { + + @Override + public File rename(File from, File to) throws Exception { + File dest = FileUtilities.resolveDestination(from, to); + + // clonefile or reflink requires filesystem that supports copy-on-write (e.g. apfs or btrfs) + ProcessBuilder process = new ProcessBuilder(); + + if (Platform.isMac()) { + // -c copy files using clonefile + process.command("cp", "-c", "-f", from.getPath(), dest.getPath()); + } else { + // --reflink copy files using reflink + process.command("cp", "--reflink", "--force", from.isDirectory() ? "--recursive" : "--no-target-directory", from.getPath(), dest.getPath()); + } + + process.directory(from.getParentFile()); + process.inheritIO(); + + int exitCode = process.start().waitFor(); + if (exitCode != 0) { + throw new IOException(String.format("%s failed with exit code %d", process.command(), exitCode)); + } + + return dest; + } + }, + DUPLICATE { @Override @@ -91,26 +122,6 @@ public enum StandardRenameAction implements RenameAction { } }, - REFLINK { - - @Override - public File rename(File from, File to) throws Exception { - File dest = FileUtilities.resolveDestination(from, to); - - // reflink requires Linux and a filesystem that supports copy-on-write (e.g. btrfs) - ProcessBuilder process = new ProcessBuilder("cp", "--reflink", "--force", from.isDirectory() ? "--recursive" : "--no-target-directory", from.getPath(), dest.getPath()); - process.directory(from.getParentFile()); - process.inheritIO(); - - int exitCode = process.start().waitFor(); - if (exitCode != 0) { - throw new IOException(String.format("reflink: %s failed with exit code %d", process.command(), exitCode)); - } - - return dest; - } - }, - TEST { @Override @@ -136,10 +147,10 @@ public enum StandardRenameAction implements RenameAction { return "Symlink"; case HARDLINK: return "Hardlink"; + case CLONE: + return "Clone"; case DUPLICATE: return "Hardlink or Copy"; - case REFLINK: - return "Lightweight Copy"; default: return "Test"; } @@ -157,10 +168,10 @@ public enum StandardRenameAction implements RenameAction { return "Symlinking"; case HARDLINK: return "Hardlinking"; + case CLONE: + return "Cloning"; case DUPLICATE: return "Duplicating"; - case REFLINK: - return "Reflinking"; default: return "Testing"; } diff --git a/source/net/filebot/cli/ArgumentBean.java b/source/net/filebot/cli/ArgumentBean.java index 1e16d921..ac8c6b76 100644 --- a/source/net/filebot/cli/ArgumentBean.java +++ b/source/net/filebot/cli/ArgumentBean.java @@ -60,7 +60,7 @@ public class ArgumentBean { @Option(name = "--order", usage = "Episode order", metaVar = "[Airdate, Absolute, DVD]") public String order = "Airdate"; - @Option(name = "--action", usage = "Rename action", metaVar = "[move, copy, keeplink, symlink, hardlink, reflink, test]") + @Option(name = "--action", usage = "Rename action", metaVar = "[move, copy, keeplink, symlink, hardlink, clone, test]") public String action = "move"; @Option(name = "--conflict", usage = "Conflict resolution", metaVar = "[skip, override, auto, index, fail]") diff --git a/source/net/filebot/resources/action.clone.png b/source/net/filebot/resources/action.clone.png new file mode 100644 index 0000000000000000000000000000000000000000..3bd472dd87f76be9e0059a7403b555dd122b0731 GIT binary patch literal 1909 zcmai!dsLEX9><@&09q!QTTYrGW$m^YFK8;PjHno*q=q`C)p&syqzFjROiat1&6=`x zwu@9=@-*qZR<=20LX zKb{faiFfOnML#m$8JBGQyG?%OgW7CYOp(|pw}fSgZP!rsV~oOG7Uqof`8o4bC7mG> z0%rED=ZS>KTJ@xBP1iDl>GQXpMRowI?|fU12s`H-KKtgHPNHi#V>xwSVhRC>EhuZB z;Fpb?^Clu=DmPzNym7su!~i#|tu<}6vOX&?`bWgw`6Mn7SEL_gU+RO+dj!eriM*JV zE8NOWCKKYGXl6gpxz!^rgySmADM2+Q*WFs0@UNqXJJI7S(#*c=KQ`{*qPb;xLV--r zJfF|<6be17NdIMEw0Cn#B8(xhl4Lc|Z{+;?&W? z;cycjx!yI<6k2t?mOK!k;4 zVtt+Wx?`6QVmhY~OUc+fg-gWPX9aNNhS zceO!bCil*k*2ngKa$8^PuW@Gef!rr`6nc@bdQ`o7}p1!f1l_)_FZX@w&TU(phzWFO%cNhRS*F&9|cv;x)0c*hzOQs9sI<55)b*q14c=`SXGawyna<*ccXV2m5$#`AbK`T4{p!rtY6_&rWH zkl{7KJHB^NmQ~=!*;d!Fj8hf7?}re?Au#1+xl<^9Jw3j@M@lKx7k+zA!Xhy0%75XT zu^*tyi_loFs_(+fq*Vg!o|r#ey6w{3RM+snFKuAkcif)CN8)Gt6gbpwKirEjpznZ-Hj4cY-5bOBReEFc^dD^m#3 zDKLS}*&aC}aF?P+_XQ*WqrpRfx&Re|XdV=b2gN6v3eh2N`c_{L$cGL=z6ZiopMV7? zeUU-|3giJ%7XS){zLiGbN?8P4B9~(S2iPwYh=qs#1E2|%Xo?pd4Walf0=IXVBq-pu zuq3XS4@(s=NKq=~bTav4mN*^eqg#?m6P{3&+FiMt|%7+0*s!SD&(qICIFOw3YVCBtCSNu`Ww*Pt(VtCw7>r#mK%O$W_ zDwU-tDaA7B7AdSGi=_fMV~adR?k`Ug!3-*OqvGS_4C+R)fI&SMFx7(22spvvteb&} Gx&H=DvO?Sd literal 0 HcmV?d00001 diff --git a/source/net/filebot/resources/action.clone@2x.png b/source/net/filebot/resources/action.clone@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..44c3989cf1406697640bbb7da3fce71253662b8a GIT binary patch literal 3107 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 R=P{s(44$rjF6*2UngDM4j?Vx9 literal 0 HcmV?d00001 diff --git a/source/net/filebot/resources/rename.action.clone@2x.png b/source/net/filebot/resources/rename.action.clone@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3bd472dd87f76be9e0059a7403b555dd122b0731 GIT binary patch literal 1909 zcmai!dsLEX9><@&09q!QTTYrGW$m^YFK8;PjHno*q=q`C)p&syqzFjROiat1&6=`x zwu@9=@-*qZR<=20LX zKb{faiFfOnML#m$8JBGQyG?%OgW7CYOp(|pw}fSgZP!rsV~oOG7Uqof`8o4bC7mG> z0%rED=ZS>KTJ@xBP1iDl>GQXpMRowI?|fU12s`H-KKtgHPNHi#V>xwSVhRC>EhuZB z;Fpb?^Clu=DmPzNym7su!~i#|tu<}6vOX&?`bWgw`6Mn7SEL_gU+RO+dj!eriM*JV zE8NOWCKKYGXl6gpxz!^rgySmADM2+Q*WFs0@UNqXJJI7S(#*c=KQ`{*qPb;xLV--r zJfF|<6be17NdIMEw0Cn#B8(xhl4Lc|Z{+;?&W? z;cycjx!yI<6k2t?mOK!k;4 zVtt+Wx?`6QVmhY~OUc+fg-gWPX9aNNhS zceO!bCil*k*2ngKa$8^PuW@Gef!rr`6nc@bdQ`o7}p1!f1l_)_FZX@w&TU(phzWFO%cNhRS*F&9|cv;x)0c*hzOQs9sI<55)b*q14c=`SXGawyna<*ccXV2m5$#`AbK`T4{p!rtY6_&rWH zkl{7KJHB^NmQ~=!*;d!Fj8hf7?}re?Au#1+xl<^9Jw3j@M@lKx7k+zA!Xhy0%75XT zu^*tyi_loFs_(+fq*Vg!o|r#ey6w{3RM+snFKuAkcif)CN8)Gt6gbpwKirEjpznZ-Hj4cY-5bOBReEFc^dD^m#3 zDKLS}*&aC}aF?P+_XQ*WqrpRfx&Re|XdV=b2gN6v3eh2N`c_{L$cGL=z6ZiopMV7? zeUU-|3giJ%7XS){zLiGbN?8P4B9~(S2iPwYh=qs#1E2|%Xo?pd4Walf0=IXVBq-pu zuq3XS4@(s=NKq=~bTav4mN*^eqg#?m6P{3&+FiMt|%7+0*s!SD&(qICIFOw3YVCBtCSNu`Ww*Pt(VtCw7>r#mK%O$W_ zDwU-tDaA7B7AdSGi=_fMV~adR?k`Ug!3-*OqvGS_4C+R)fI&SMFx7(22spvvteb&} Gx&H=DvO?Sd literal 0 HcmV?d00001 diff --git a/source/net/filebot/ui/rename/Preset.java b/source/net/filebot/ui/rename/Preset.java index da9c416c..ec4a5b11 100644 --- a/source/net/filebot/ui/rename/Preset.java +++ b/source/net/filebot/ui/rename/Preset.java @@ -2,6 +2,7 @@ package net.filebot.ui.rename; import static java.util.Collections.*; import static net.filebot.Logging.*; +import static net.filebot.Settings.*; import static net.filebot.WebServices.*; import static net.filebot.util.FileUtilities.*; @@ -147,7 +148,14 @@ public class Preset { } public static StandardRenameAction[] getSupportedActions() { - return new StandardRenameAction[] { StandardRenameAction.MOVE, StandardRenameAction.COPY, StandardRenameAction.KEEPLINK, StandardRenameAction.SYMLINK, StandardRenameAction.HARDLINK }; + if (isWindowsApp()) { + // CoW clones not supported on Windows + return new StandardRenameAction[] { StandardRenameAction.MOVE, StandardRenameAction.COPY, StandardRenameAction.KEEPLINK, StandardRenameAction.SYMLINK, StandardRenameAction.HARDLINK }; + } else { + // CoW clones / reflinks supported on macOS and Linux + return new StandardRenameAction[] { StandardRenameAction.MOVE, StandardRenameAction.COPY, StandardRenameAction.KEEPLINK, StandardRenameAction.SYMLINK, StandardRenameAction.HARDLINK, StandardRenameAction.CLONE }; + } + } public static Language[] getSupportedLanguages() { diff --git a/source/net/filebot/ui/rename/RenamePanel.java b/source/net/filebot/ui/rename/RenamePanel.java index 01e417e8..316aff50 100644 --- a/source/net/filebot/ui/rename/RenamePanel.java +++ b/source/net/filebot/ui/rename/RenamePanel.java @@ -22,7 +22,6 @@ import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.io.File; import java.util.ArrayList; -import java.util.EnumSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -547,7 +546,7 @@ public class RenamePanel extends JComponent { actionPopup.addSeparator(); actionPopup.addDescription(new JLabel("Action:")); - for (StandardRenameAction action : EnumSet.of(StandardRenameAction.MOVE, StandardRenameAction.COPY, StandardRenameAction.KEEPLINK, StandardRenameAction.SYMLINK, StandardRenameAction.HARDLINK)) { + for (StandardRenameAction action : Preset.getSupportedActions()) { actionPopup.add(new SetRenameAction(action)); }