about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorSoispha <soispha@vhack.eu>2023-08-29 13:17:27 +0200
committerSoispha <soispha@vhack.eu>2023-08-29 13:17:27 +0200
commit6a77c319f661ed5d6d6570768f78a4a2f93c04db (patch)
treed5e901003c7ad593ee2df0b91c41f382c8321d4a
parentDocs(hm/conf/lf/commads/trash): Add fixme about the implementation (diff)
downloadnixos-config-6a77c319f661ed5d6d6570768f78a4a2f93c04db.tar.gz
nixos-config-6a77c319f661ed5d6d6570768f78a4a2f93c04db.zip
Chore(hm/conf/lf/commads): Add initial draft of a reflinking cp in lf
Diffstat (limited to '')
-rwxr-xr-xhm/soispha/conf/lf/commands/scripts/cow_cp96
1 files changed, 96 insertions, 0 deletions
diff --git a/hm/soispha/conf/lf/commands/scripts/cow_cp b/hm/soispha/conf/lf/commands/scripts/cow_cp
new file mode 100755
index 00000000..9a12e5db
--- /dev/null
+++ b/hm/soispha/conf/lf/commands/scripts/cow_cp
@@ -0,0 +1,96 @@
+#!/usr/bin/env dash
+
+# shellcheck source=/dev/null
+SHELL_LIBRARY_VERSION="1.1.4" . %SHELL_LIBRARY_PATH
+
+# source: https://github.com/gokcehan/lf/wiki/Tips#use-copy-on-write-when-possible
+#
+# # FIXME: Add this. The hardest part is in checking, if a file can be reflinked, as fuse and bind mount are hard to
+#          backtrack <2023-08-29>
+
+# # This was very helpful for debugging:
+# log_file="$HOME/lf-reflink-log-$(date +'%Y-%m-%d_%H-%M-%S')"
+# [ -f "$log_file" ] || touch "$log_file"
+# exec 1>> $log_file 2>&1
+# set -x
+
+# In theory, this may fail,
+# but I tested it on selection with 10k files - everything worked (bash)
+# FIXME: This will very likely fail on dash, when the file number > 255 <2023-08-29>
+set -- "$(cat ~/.local/share/lf/files)"
+
+mode="$1"
+shift
+
+if [ "$mode" = 'copy' ]; then
+    # Reflink if all items of selection and the destination are on the
+    # same mount point and it is CoW fs.
+    # (to make sure reflink never fails in first place, so we don't have to
+    # clean up)
+
+    src_targets="$(df --output=target -- "$@" | sed '1d' | sort -u)"
+
+    if [ "$(df --output=target -- "$PWD" | tail -n 1)" = \
+        "$(echo "$src_targets" | tail -n 1)" ] && \
+        (( "$(echo "$src_targets" | wc -l)" == 1 )) && \
+        [[ "$(df --output=fstype -- "$PWD" | tail -n 1)" =~ ^(btrfs|xfs|zfs)$ ]]; then
+
+    echo 'selected copy and cp reflink paste'
+
+    start=$(date '+%s')
+
+        # Handle same names in dst
+        # TODO parallelism, idk - but exit/return/break won't stop the loop from subshell...
+        for i in "$@"; do
+            name="${i##*/}"
+            original="$name"
+
+            count=0
+            while [ -w "$PWD/$name" ]; do
+                count=$((count+1))
+                name="$original.~$count~"
+            done
+
+            set +e
+            cp_out="$(cp -rn --reflink=always -- "$i" "$PWD/$name" 2>&1)"
+            set -e
+
+            if [ ! -z "$cp_out" ]; then
+                lf -remote "send $id echoerr $cp_out"
+                exit 0
+            fi
+        done
+
+        finish=$(( $(date '+%s') - $start ))
+        t=''
+        if (( $finish > 2 )); then
+            t="${finish}s"
+        fi
+
+        # Or just skip a file when names are the same.
+        # (A LOT faster if you e.g. pasting selection of 10k files)
+        # cp -rn --reflink=always -- "$@" .
+
+        lf -remote "send clear"
+
+        green=$'\u001b[32m'
+        reset=$'\u001b[0m'
+        lf -remote "send $id echo ${green}reflinked!${reset} $t"
+    else
+        echo 'selected copy and lf native paste'
+        lf -remote "send $id paste"
+        lf -remote "send clear"
+        fi
+
+    elif [ $mode = 'move' ]; then
+        echo 'selected move and lf native paste'
+        lf -remote "send $id paste"
+        lf -remote "send clear"
+    fi
+
+# # for debug
+# set +x
+
+lf -remote "send load"
+
+# vim: ft=sh