From eb0aab1613598af6807ea70da40d46e888978641 Mon Sep 17 00:00:00 2001 From: Sam Hardeman Date: Sun, 8 Mar 2026 20:54:07 +0100 Subject: [PATCH 1/5] Latest version --- README.md | 88 ++++++- newday => agenda | 34 +-- calm | 41 ---- cdz | 22 +- daisy.source | 566 ++++++++++++++++++++++++++++--------------- destroy-squash-image | 27 +++ editx | 2 +- filewait | 2 +- make-squash-image | 88 +++++++ mount-squash-image | 20 ++ own | 4 +- binbox => scriptbox | 90 +++++-- shrc | 40 +-- sshp | 95 ++++++++ sw | 4 +- umount-squash-image | 18 ++ what | 2 +- 17 files changed, 815 insertions(+), 328 deletions(-) rename newday => agenda (54%) delete mode 100755 calm create mode 100755 destroy-squash-image create mode 100755 make-squash-image create mode 100755 mount-squash-image rename binbox => scriptbox (58%) create mode 100755 sshp create mode 100755 umount-squash-image diff --git a/README.md b/README.md index 42d739f..f209032 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ ---*This project is still under heavy development!*--- +\Please do not delete this file, as it is used to display help information. + # L A C K A D A I S I C A L A collection of easy-to-understand, easy-to-use scripts, functions and aliases. Designed to be beginner-friendly but also useful! @@ -34,8 +36,6 @@ lackadaisical` if you so desire. To read this notice again, call the function 'daisy_help'. =============================================================================== These are the included binaries and utilities: - - calm: - Reduce a process 'niceness' to 0. - cdz: This utility extracts an archive to /tmp and changes directory to it in a new shell instance. Upon exit, @@ -45,6 +45,25 @@ These are the included binaries and utilities: NO_ARCHIVEMOUNT=1. The standard script supports zip, tarballs, and rar archives. We recommend relying on archivemount` if you have it installed. + - SquashFS tools (mount/umount/make/destroy-squash-image): + These convenient set of tools allow you to easily create + XZ-compressed SquashFS images from existing folders to save + disk space. The resulting folder is still writable since it is + mounted using an 'overlay' system. You can use 'make-squash-image' + to compresses an existing folder. These folders are automatically + mounted when you use 'cd' to navigate to them (via an alias). + When using 'make-squash-image' on an already mounted folder, it will + instead update the existing image. As of writing, we do not have a + SystemD service to auto-mount, however, you cam easily add auto-mount + as a cron job. Here is a list of tools, they all take the same + folder argument: + > make-squash-image: Converts the folder into a hidden image + on the same disk as the folder. + > mount-squash-image: Sets up a mount for the XZ image alongside + directories for changes + > umount-squash-image: Self-explanatory. + > destroy-squash-image: Extracts the image and essentially reverts + everything. File changes are kept, however. - editx: Uses your standard CLI editor to create/modify a file and make it executable. @@ -53,16 +72,32 @@ These are the included binaries and utilities: not exist yet. When the file appears on disk, the tool quits and simply returns the filename. This can be used in personal workflows to stall a longer - command that relies on the existence of said file. - - newday: - A basic but powerful journaling system. Recommended - to be set up via crontab. Can be used for everything - from diaries to general file storage to even BTRFS - snapshots. + command that relies on the existence of said file.= + - agenda: + Sets up a folder that is backed by a date-based tree + directory structure. Requires an argument for the name + of the folder to generate. Generates a symlink in this + name to a date sub-folder in a local '.daisy' folder. + Format is -> .daisy////. + Recommended to run via crontab - automatically cleans + up empty folders. + A symbolic link to the base of the folder's tree, ".tree", + is created in the root of the specified directly. + Can be used for everything you'd like to sort by date. + For example; a diary, browser downloads, backups, code. - own: A simple utility. It's effectively an alias for "sudo chown -R user:user" on the target dir/file. Root permissions required! + - sshp: + This is a wrapper for `ssh`, the meaning of the 'p' is "Plus". + Integrates SSHFS support. If both client and host have SSHFS, + this wrapper can be used to connect their file systems. + For example, if you need to move files from one machine to + another, you could do something like this: + "sshp -m /:/mnt/pc -m /home/claire:/home/claire claire@pyon.net" + If privilege escalation is necessary for FS access, you will + be asked for a password. - shrc: This tool allows you to edit the RC file for your shell in your preferred editor. After saving, the @@ -74,11 +109,11 @@ These are the included binaries and utilities: This is a tool similar to which and others, the key difference is that it returns partial matches. It can be used to search for binaries. - - binbox: + - scripbox: This tool can be used to pack bash scripts into one big megascript, much like how `busybox` works. You can also make symlinks to it to invoke a specific - script (as of writing, 11/25, symlinks do not work well). + script. - bak/unbak: These small utilities make backups of files by making a copy with a .bak suffix. Unbak reverses the process @@ -100,12 +135,27 @@ These are the included binaries and utilities: Like chsh but for your editor (EDITOR env). A list from which you can choose an installed editor (CLI or GUI) is shown. This list is by no means complete. + The editor for LACKADAISICAL and the global editor are + separate. If the EDITOR variable is already defined, + only LD_EDITOR will be changed. LD_EDITOR is the editor + used by LACKADAISICAL utilities. + To override the global EDITOR variable, pass "-g". + To restore the normal behavior of checking for an earlier + definition of EDITOR after passing "-g", run `ched` without + arguments. - cdf: Use fzf to find a file and then cd to its location. - cdp: Similar to `cdf` but uses tree+peco for the query. + - clip: + An extremely simple utility that functions as a clipboard of sorts. + To set the variable, run "clip " or provide data via stdin. + To get the variable, simply run clip without any arguments. + The variable is stored locally in the shell as "LD_CLIP". - ldrc: Edits daisy.source and re-sources it, similarly to shrc. + Append "-e" to edit "extra.src", to add custom functions in the + lackadaisical namespace. - daisy_reload: Re-sources daisy.source. Essentially `ldrc` without editing. @@ -120,7 +170,10 @@ These are the included binaries and utilities: decoded by passing the output(s) to `daisy_dec`. The output of `daisy_enc` can be concatenated with the output of another encoded file to create a multi-file - base64 archive similar to `daisy_enc_multi` + base64 archive similar to `daisy_enc_multi`. + Does not support symlinks yet, and will instead treat it + as a full input file (reads the data of the linked file). + When using stdin, please provide a filename as argument. - daisy_enc_multi: A version of `daisy_enc` that encodes multiple files and outputs `daisy_base64_data` blocks to a file @@ -155,5 +208,18 @@ These are the included binaries and utilities: `daisy_reload` - daisy_list: List all available commands without description. + - daisy_clear: + Removes all configuration, including aliases. + A backup is made and can be restored using "daisy_restore". + Triggers a reload. + - daisy_backup: + Backs up all config files. These can be restored using + "daisy_restore". + - daisy_restore: + Undoes "daisy_clear" by restoring config files. + Triggers a reload. + - ld_*: + All functions prefixed by "daisy_" are also available with + the prefix "ld_" via aliases provided in daisy.source. --- END OF DAISY HELP --- ``` diff --git a/newday b/agenda similarity index 54% rename from newday rename to agenda index 6266505..837cd0f 100755 --- a/newday +++ b/agenda @@ -1,25 +1,31 @@ -#!/bin/sh +#!/usr/bin/env bash # This script is intended to be run via cron. # It creates a folder structure in home for the current date in the following format: -# $HOME/ByDate/// +# $HOME//// -# It also sets up a symlink in home: $HOME/Today +# It also sets up a symlink for the tagged folder. # This symlink will always point to the folder for the current date. # Finally, it removes any folders without data, to prevent clutter. -# - Why did you make this? -# I remember things better when they have a date attached to them. -# You can use this for a primitive form of note-taking, but aside from notes - -# you can store any data this way. - LD_INTERNAL=1 . $(dirname $(realpath $0))/daisy.source -dir_name=ByDate -root_dir=$HOME/$dir_name -today_sym=$HOME/Today +if [[ $1 == '' ]]; then + echo "Usage: $LD_BIN " + echo "Creates a folder within '.daisy' in the current directory containing" + echo "a tree of dates associated with the folder name given by argument." + echo "A symlink is (re-)created at the same time with the same folder name." + echo "For example, if given the folder 'notes', this utility will:" + echo "- Create a folder '/.daisy/notes///'." + echo "- Create a symlink named 'notes' that points to it." + echo "It is recommended to run this via cron." + exit 1 +fi + +root_dir=$(dirname $1)/.daisy/$(basename $1) +today_sym=$1 # Present day today=$(date -I) @@ -29,7 +35,7 @@ day=$(echo $today | awk -F"-" '{print $3}') set -e -function errorFn() +function errorFn { error=$? if [[ $error -gt 0 ]]; @@ -49,6 +55,6 @@ test -L "$today_sym" && rm -rf "$today_sym" # Now we can set up today's directory mkdir -p "$root_dir/$year/$month/$day" -cd $root_dir -ln -s "./$dir_name/$year/$month/$day" "$today_sym" +ln -s "$root_dir/$year/$month/$day" "$today_sym" +ln -s "$root_dir" "$today_sym/.tree" exitcode=@? diff --git a/calm b/calm deleted file mode 100755 index 6e45689..0000000 --- a/calm +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -# Calm a process down -# NEEDS_WORK: cleanup -# calm == only one process -# calm == all processes that match query -# calm * == if used in proc or a folder with just PID files, if not you will get an error -# calm .... OK -# set's NICE value to 0 -# need sudo - -LD_INTERNAL=1 -. $(dirname $(realpath $0))/daisy.source - -pids=$@ - -errorFn() -{ - echo calm: Invalid operation or no such PID/process \(\"$(echo "$pids" | tr '\n' ' ' | cut -c -20)...\"\) - exit -} - -for pid in $pids -do - # Process to PID in elegant way - newpids=$(pidof "$pid") - if [ "$newpids" ] - then - binary=$pid - pid=$newpids - else - newbins=$pid - binary=$(ps -p "$pid" -o comm= 2>/dev/null ) - if [ $? != 0 ] - then - errorFn - fi - fi - - echo Calming down $pid \("$binary"\)... - sudo renice -n 0 -p $pid; -done diff --git a/cdz b/cdz index d39446b..a380222 100755 --- a/cdz +++ b/cdz @@ -1,8 +1,8 @@ -#!/bin/sh +#!/usr/bin/env bash if [[ $LD_INTERNAL -ne 1 ]]; then - export LD_INTERNAL=1 + LD_INTERNAL=1 . $(dirname $(realpath $0))/daisy.source fi @@ -28,6 +28,7 @@ hasmounter=$? file "$target" 1>/dev/null exitcode=$? report=$(file "$target") +name=$(echo $@ | sed 's/:.*//' | sed 's|.*/||') # Check for archive type, supported types are zip/tar/rar comm1=(:) @@ -88,7 +89,7 @@ elif (( $israr == 0 )); then comm3=(exit 1) fi elif (( $hasmounter == 0 )); then - comm2=(archivemount "$target") + comm2=(archivemount -o allow_root -o use_ino "$target") comm4=(cd ..) comm5=(umount) fi @@ -101,18 +102,17 @@ dir=$(mktemp -d /tmp/extracted.XXXXXXXX) "${comm2[@]}" $dir "${comm3[@]}" -currentpath=$(realpath .) -cd $dir +currentpath=$(pwd) +sym="$currentpath/$name.tmp" +ln -f -s "$dir" "$sym" +cd "$sym" -# With archivemount, making a symlink will alter the archive -if (( $hasmounter == 1 )); then - ln -s $currentpath ./link-back - echo "A symlink to your original path has been created under the name \`link-back\`." - echo "You can use this to copy out files, but you can also just access your filesystem regularly." -fi +echo "A symlink has been made under the name \"$sym\"." +echo "This symlink points to the data directory \"$dir\"." echo "Type 'exit' to exit the extracted archive's folder and auto-delete it." eval $SHELL "${comm4[@]}" "${comm5[@]}" $dir rm -rf $dir +rm "$sym" diff --git a/daisy.source b/daisy.source index ade487f..c39ecba 100755 --- a/daisy.source +++ b/daisy.source @@ -1,4 +1,5 @@ -#!/bin/echo "This file can only be sourced, not run stand-alone." -- #!/bin/bash +#!/usr/bin/bash +#!/usr/bin/env -S echo "This file can only be sourced, not run stand-alone." # LACKADAISICAL SOURCE-ABLE FILE # Source this in your RC file or manually to receive some of the simpler @@ -11,11 +12,21 @@ # only included if sourced from one of the included scripts, though you are # free to bypass this by setting env variable LD_INTERNAL to 1. -if [[ $LD_INTERNAL -eq 1 ]]; +# Pass _LD_DEBUG=1 during sourcing to see debug information on a variety of things. + +if [[ $LD_INTERNAL == 1 ]]; then export LD_BIN=$(basename $0) fi +function ld_dbg +{ + if [[ $_LD_DEBUG == 1 ]]; + then + $@ + fi +} + # Variables for use in other utilities # Find the right argument for our folder arg=$0 @@ -25,17 +36,26 @@ then fi # Check for dependencies -function daisy_dependency_check +function _daisy_dependency_check { - command -v $1 1>/dev/null 2>/dev/null; + BIN=$(command -v $1 2>/dev/null) res=$? + echo $(($res ^ 1)) } -LD_HAS_fzf=$(daisy_dependency_check fzf) -LD_HAS_md5sum=$(daisy_dependency_check md5sum) -LD_HAS_peco=$(daisy_dependency_check peco) -LD_HAS_tree=$(daisy_dependency_check tree) +LD_HAS_fzf=$(_daisy_dependency_check fzf) +LD_HAS_md5sum=$(_daisy_dependency_check md5sum) +LD_HAS_peco=$(_daisy_dependency_check peco) +LD_HAS_tree=$(_daisy_dependency_check tree) +LD_HAS_dialog=$(_daisy_dependency_check dialog) + +ld_dbg echo "Presence of utils:" +ld_dbg echo fzf $LD_HAS_fzf +ld_dbg echo md5sum $LD_HAS_md5sum +ld_dbg echo peco $LD_HAS_peco +ld_dbg echo tree $LD_HAS_tree +ld_dbg echo dialog $LD_HAS_dialog export LD_FOLDER=$(dirname $(realpath $arg)) export LD_SOURCE_FILE=$(realpath $arg) @@ -57,11 +77,16 @@ fi # [LEA.TODO] Turn these into arrays LD_ALIASFILE="$LD_CONFIG_FOLDER/aliases.src" LD_EDITORFILE="$LD_CONFIG_FOLDER/editor.src" +LD_ESOURCEFILE="$LD_CONFIG_FOLDER/extra.src" touch $LD_ALIASFILE touch $LD_EDITORFILE +ld_dbg echo "Sourced config contents:" +ld_dbg cat $LD_ALIASFILE +ld_dbg cat $LD_EDITORFILE + # Source everything in the config folder -function _daisy_source_configs +function _daisy_source_configs { for f in `find "$LD_CONFIG_FOLDER" -name "*.src" -type f`; do @@ -88,123 +113,8 @@ fi # FUNCTIONS and ALIASES ####################################################### ############################################################################### -# bak and unbak -function bak -{ - # Input: - target=$1 - - # Check if file exists - if ! test -f "$target"; - then - echo "Path not found: \"$target\"" - return 2 - fi - - # Handle both cases - if [[ $unbak_mode -eq 1 ]]; - then - cp -R "$target.bak" "$target" - rm -rf "$target.bak" - echo "Restored backup: $target <-- $target.bak" - else - cp -R "$target" "$target.bak" - echo "Backup made: $target --> $target.bak" - fi -} - -alias unbak="unbak_mode=1 bak" -alias lsa="ls -a -l -h" -alias lsn="ls -a -l -tu -r -h" -alias lss="ls -a -l -S -r -h" - -# Simple version of `cdf` -function cdf -{ - if [[ $LD_HAS_fzf != 1 ]]; - then - echo "This alias requires the utility 'fzf'. Please install it." - return 1 - fi - cd $(dirname $(fzf)) -} - -function cdp -{ - if [[ $LD_HAS_peco != 1 || $LD_HAS_tree != 1 ]]; - then - echo "This alias requires the utilities 'peco' and 'tree'. Please install them." - echo "Consider using 'cdf' instead." - return 1 - fi - cd $(dirname $(tree -fia --noreport . | peco)) -} - -function editpeco -{ - if [[ $LD_HAS_peco != 1 || $LD_HAS_tree != 1 ]]; - then - echo "This alias requires the utilities 'peco' and 'tree'. Please install them." - echo "Consider using 'cdf' instead." - return 1 - fi - tree --noreport -fia . | peco --prompt "Press CTRL+C to quit - query:" --exec "xargs -o -I{} $EDITOR {}" -} - -# for convenience purposes -function editbin -{ - editx $(which $1) -} - -# sets a new editor based on commony available ones, and some visual ones -function ched -{ - editors=("nano" "vim" "nvim" "vi" "emacs" "gedit" "kate" "mousepad" "micro" \ - "code" "subl" "joe" "kwrite" "gnome-text-editor") - - # Find which editors are installed - available_editors=() - for editor in "${editors[@]}"; - do - editor_real=$(command -v "$editor") - if command -v "$editor_real" >/dev/null 2>&1; - then - if [[ $(realpath "$EDITOR") == "$editor_real" ]]; - then - available_editors+=("$editor_real" "$editor (current choice)") - else - available_editors+=("$editor_real" "$editor") - fi - fi - done - - if [[ ! -z $@ ]]; - then - text="$@" - dialog --msgbox "$text" 0 0 - fi - - # Present all choices - choice=$(dialog --output-fd 1 --clear --title "Select Text Editor" \ - --menu "Choose one of the installed text editors:" 15 50 6 \ - "${available_editors[@]}") - dialog_ret=$? - - if [ $dialog_ret -ne 0 ]; - then - echo "No editor selected." - return - fi - - echo export EDITOR=$choice > "$LD_EDITORFILE" - echo export LD_EDITOR=$choice >> "$LD_EDITORFILE" - echo export LD_OLD_EDITOR=$EDITOR >>"$LD_EDITORFILE" - - _daisy_source_configs -} - -function wait_for_editor +# Undocumented but internally used +function daisy_wait_for_editor { pname="$1" fname="$2" @@ -223,32 +133,246 @@ function wait_for_editor done } +function mountcd +{ + cdpath="$@" + if [[ $cdpath == '' ]]; + then + \cd + fi + if [[ -f "$cdpath/.needs_mount" ]]; + then + mount-squash-image "$cdpath" + fi + \cd "$cdpath" +} + +alias cd=mountcd + +# Undocumented but internally used +function daisy_editor +{ + editor=${LD_EDITOR:-$EDITOR}; + ld_dbg echo Opening $editor to edit file: $1 + $editor "$1" + daisy_wait_for_editor $editor "$1" +} + +# bak and unbak +function bak +{ + # Input: + target=$1 + + # Check if file exists + if ! test -f "$target"; + then + echo "Path not found: \"$target\"" + return 2 + fi + + # Handle both cases + if [[ $unbak_mode == 1 ]]; + then + cp -R "$target.bak" "$target" + rm -rf "$target.bak" + echo "Restored backup: $target <-- $target.bak" + else + cp -R "$target" "$target.bak" + echo "Backup made: $target --> $target.bak" + fi +} + +function unbak +{ + unbak_mode=1 bak $@ +} + +function lsa +{ + ls -a -l -h $@ +} + +function lsn +{ + ls -a -l -tu -r -h $@ +} + +function lss +{ + ls -a -l -S -r -h $@ +} + +# Simple version of `cdf` +function cdf +{ + if [[ $LD_HAS_fzf != 1 ]]; + then + echo "This function requires the utility 'fzf'. Please install it." + return 1 + fi + cd "$(dirname "$(fzf)")" +} + +function cdp +{ + if [[ $LD_HAS_peco != 1 || $LD_HAS_tree != 1 ]]; + then + echo "This function requires the utilities 'peco' and 'tree'. Please install them." + echo "Consider using 'cdf' instead." + return 1 + fi + cd $(dirname $(tree -fia --noreport . | peco)) +} + +function editpeco +{ + if [[ $LD_HAS_peco != 1 || $LD_HAS_tree != 1 ]]; + then + echo "This function requires the utilities 'peco' and 'tree'. Please install them." + echo "Consider using 'cdf' instead." + return 1 + fi + tree --noreport -fia . | peco --prompt "Press CTRL+C to quit - query:" --exec "xargs -o -I{} $EDITOR {}" +} + +# for convenience purposes +function editbin +{ + editx $(which $1) +} + +# sets a new editor based on commony available ones, and some visual ones +function ched +{ + override=0 + if [[ $1 == "-g" ]]; + then + override=1 + fi + + if [[ $LD_HAS_dialog != 1 ]]; + then + echo "This function requires the utility 'dialog'. Please install it." + return 1 + fi + + editors=("nano" "vim" "nvim" "vi" "emacs" "gedit" "kate" "mousepad" "micro" \ + "code" "subl" "joe" "kwrite" "gnome-text-editor") + + # Find which editors are installed + available_editors=() + for editor in "${editors[@]}"; + do + editor_real=$(command -v "$editor") + if command -v "$editor_rmeal" >/dev/null 2>&1; + then + if [[ $(realpath "$EDITOR") == "$editor_real" ]]; + then + available_editors+=("$editor_real" "$editor (current)") + elif [[ $LD_EDITOR == "$editor_real" ]]; + then + available_editors+=("$editor_real" "$editor (LD choice)") + else + available_editors+=("$editor_real" "$editor") + fi + fi + done + + if [[ $override == 0 ]] && [[ ! -z $@ ]]; + then + text="$@" + dialog --msgbox "$text" 0 0 + elif [[ $override == 1 ]]; + then + text="You have passed '-g'. Your choice of dialog will override any other choice or setting of 'EDITOR'." + dialog --msgbox "$text" 0 0 + fi + + # Present all choices + choice=$(dialog --output-fd 1 --clear --title "Select Text Editor" \ + --menu "Choose one of the installed text editors:" 15 100 6 \ + "${available_editors[@]}") + dialog_ret=$? + + if [ $dialog_ret -ne 0 ]; + then + dialog --msgbox "No editor selected. Variables will not be updated." 0 0 + return 0 + fi + + [[ $override == 0 ]] && echo export EDITOR="${EDITOR:-$choice}" > "$LD_EDITORFILE" + [[ $override == 1 ]] && echo export EDITOR="$choice" > "$LD_EDITORFILE" + echo export LD_EDITOR="$choice" >> "$LD_EDITORFILE" + echo export LD_OLD_EDITOR="$EDITOR" >> "$LD_EDITORFILE" + + source "$LD_EDITORFILE" +} + function daisy_reload { - FROM_RC=0 source "$LD_SOURCE_FILE" + LD_INTERNAL=0 source "$LD_SOURCE_FILE" } function ldrc { - $EDITOR "$LD_SOURCE_FILE" - FROM_RC=0 source "$LD_SOURCE_FILE" + if [[ $1 == "-e" ]]; + then + daisy_editor "$LD_ESOURCEFILE" + else + daisy_editor "$LD_SOURCE_FILE" + fi + + LD_INTERNAL=0 source "$LD_SOURCE_FILE" } +enc_is_folder=0 function daisy_enc { - if [ -d $1 ]; + if [ -t 0 ] && [ -z "$1" ]; + then + echo "# $0: No arguments or stdin specified!" + return 1; + fi + + if [ ! -t 0 ] && [ -z "$1" ]; + then + echo "# $0: Please provide a filename as argument when using stdin" + return 1; + fi + + if [ -n "$1" ] && [ -d "$1" ]; then echo -e "daisy_create_folder=$1" else - file_info=$(file $1) - file_dir=$(dirname $1) - file_name=$(basename $1) + file_dir="" + file_name="" + perms=755 + + target=$1 + + # [TODO, FIX: An unknown bug is causing daisy_enc_folder to misbehave if stdin is accessed, so we disable it here.] + if [[ ! -t 0 ]] && [[ $enc_is_folder == 0 ]]; + then + file_dir="." + file_name="$1" + shift + elif [ -f "$1" ]; + then + file_dir=$(dirname "$1") + file_name=$(basename "$1") + perms=$(stat -c %a "$1") + else + echo "# $0: An error occured during encoding." + return 1 + fi base64_inner=$(cat ${1:-/dev/stdin} | base64 | tr -d '\n') # Print out our block echo -e "daisy_folder_$file_name=$file_dir" echo -e "daisy_data_base64_$file_name=\"$base64_inner\"" + echo -e "daisy_perms_$file_name=$perms" fi } @@ -272,13 +396,21 @@ function daisy_enc_folder cd "$dir" tree -fia --noreport . | sed 1d | while read -r item; do + enc_is_folder=1 daisy_enc "$item" + enc_is_folder=0 done } function daisy_dec { + if [ -t 0 ] && [ -z "$1" ]; + then + echo "$0: No arguments or stdin specified!" + return 1; + fi + data=$(cat ${1:-/dev/stdin} | grep -v "#" ) echo -e "$data" | cut -d "=" -f 2- | cut -b 2- | head -c -2 | base64 -d } @@ -287,30 +419,48 @@ function daisy_dec # Extracts to the directory function daisy_dec_multi { - [[ ! -f $1 ]] && echo "daisy_dec_multi: No input file specified" && return - [[ ! -d $2 ]] && echo "daisy_dec_multi: No output directory specified" && return + arg1=$1 + arg2=$2 -folder= -while IFS= read -r line; do - if [[ "$line" == "daisy_create_folder="* ]]; + # Handle stdin support + if [ ! -t 0 ]; then - folder=$(echo $line | cut -d "=" -f 2) - mkdir -p "$2/$folder" + arg2=$1 + arg1=/dev/stdin fi - if [[ "$line" == "daisy_folder"* ]]; - then - folder=$(echo -e $line | cut -d "=" -f 2) - continue - fi + [[ -t 0 ]] && [[ ! -f $arg1 ]] && echo "daisy_dec_multi: No input file specified" && return + [[ ! -d $arg2 ]] && echo "daisy_dec_multi: No output directory specified" && return - if [[ "$line" == "daisy_data_base64"* ]]; - then - file=$(echo -e $line | cut -d "_" -f 4- | cut -d "=" -f 1) - daisy_dec <(echo $line) > "$2/$folder/$file" - fi + folder= + while IFS= read -r line; do + if [[ "$line" == "daisy_create_folder="* ]]; + then + folder=$(echo $line | cut -d "=" -f 2) + echo $folder + mkdir -p "$arg2/$folder" + fi - done <<< $(cat "$1") + if [[ "$line" == "daisy_folder"* ]]; + then + folder=$(echo -e $line | cut -d "=" -f 2) + continue + fi + + if [[ "$line" == "daisy_data_base64"* ]]; + then + file=$(echo -e $line | cut -d "_" -f 4- | cut -d "=" -f 1) + mkdir -p "$arg2/$folder" + daisy_dec <(echo $line) > "$arg2/$folder/$file" + fi + + if [[ "$line" == "daisy_perms"* ]]; + then + file=$(echo -e $line | cut -d "_" -f 3- | cut -d "=" -f 1) + perms=$(echo -e $line | cut -d "_" -f 3- | cut -d "=" -f 2) + chmod $perms "$arg2/$folder/$file" + fi + done <<< $(cat "$arg1") } # Saves a bit on typing @@ -320,6 +470,23 @@ function grab awk '{print $'$1'}' } +function clip +{ + data="" + + if [ ! -t 0 ]; then + data="$(cat)" + elif [ "$*" != "" ]; then + data="$*" + else + echo $LD_CLIP + return 0 + fi + + # Export the variable + export LD_CLIP="$data" +} + function daisy_unalias { unalias_param=$@ @@ -338,7 +505,7 @@ function daisy_unalias # Remove from aliases list newdata=$(cat "$LD_ALIASFILE" | grep -v "alias $unalias_param") - bak "$LD_ALIASFILE" 1>/dev/null + [[ NO_BAK -lt 1 ]] && bak "$LD_ALIASFILE" 1>/dev/null echo -e $newdata > "$LD_ALIASFILE" } @@ -371,6 +538,60 @@ function daisy_alias source $LD_ALIASFILE } +function daisy_backup +{ + for f in `find "$LD_CONFIG_FOLDER" -name "*.src" -type f`; + do + bak "$f" + done +} + +function daisy_clear +{ + daisy_backup + + for f in `find "$LD_CONFIG_FOLDER" -name "*.src" -type f`; + do + echo "Removing config file: $f" + rm -rf "$f" + done + + echo "Config cleared. Use 'daisy_restore' if you would like to undo this." + daisy_reload +} + +function daisy_restore +{ + for f in `find "$LD_CONFIG_FOLDER" -name "*.src" -type f`; + do + unbak "$f" + bak "$f" 1>/dev/null + done + + echo "Config restored. Backups have been retained." + daisy_reload +} + +# Aliases for front-facing daisy_ functions +function _daisy_def_alias +{ + alias ld_$1=daisy_$1 +} + +_daisy_def_alias reload +_daisy_def_alias enc +_daisy_def_alias enc_multi +_daisy_def_alias enc_folder +_daisy_def_alias dec +_daisy_def_alias dec_multi +_daisy_def_alias alias +_daisy_def_alias unalias +_daisy_def_alias backup +_daisy_def_alias clear +_daisy_def_alias restore +_daisy_def_alias help +_daisy_def_alias list + _daisy_source_configs ############################################################################### @@ -380,32 +601,11 @@ _daisy_source_configs # End of user section! export LD_AVAILABLE=1 -# Start of internal section -function daisy_quit_if_no -{ - local dep_name="daisy_has_$1" - local has_dep="${!dep_name}" - - # Check first if we have checked for this dependency, if not, print a fixme! - # TODO: Remove upon release, or convert into self-modifying code. - if [[ -z "$has_dep" ]]; - then - echo "FIXME: Dependency `$1` should have an env variable, checking ad-hoc" - has_dep=$(daisy_dependency_check $1) - fi - - if [[ $has_dep -eq 0 ]]; - then - echo "$LD_BIN: The dependency $1 was not found! Please install it" \ - "to be able to use this utility!" - exit 1 - fi -} - [ -d "$LD_FOLDER" ] && export LD_AVAILABLE=1 # Help function, courtesy of Google Gemini -function daisy_help() { +function daisy_help() +{ local target_tool="$1" local file="$LD_FOLDER/README.md" @@ -466,7 +666,8 @@ function daisy_help() { } # Courtesy of Google Gemini -daisy_list() { +daisy_list() +{ local file="$LD_FOLDER/README.md" echo "Available LACKADAISICAL commands:" @@ -482,16 +683,3 @@ daisy_list() { } ' | sort } - -# Hide what we don't need -if [[ ! -v LD_INTERNAL ]]; -then - unset -f _daisy_source_configs - unset -f wait_for_editor - unset -f daisy_quit_if_no - unset -f daisy_dependency_check - unset LD_HAS_fzf - unset LD_HAS_peco - unset LD_HAS_md5sum - unset LD_HAS_tree -fi \ No newline at end of file diff --git a/destroy-squash-image b/destroy-squash-image new file mode 100755 index 0000000..dc4f3a3 --- /dev/null +++ b/destroy-squash-image @@ -0,0 +1,27 @@ +#!/bin/bash +DIR="$@" +DIR=$(readlink -f "$DIR") +DIR_SHORT=$(basename $DIR) + +if [[ -f "$DIR/.needs_mount" ]]; +then + echo "Not a SquashFS image, exiting." + exit 0 +fi + +echo "Destroying the existing SquashFS image." +echo "Copying data from the original directory." + +echo "Using rsync... in 2 seconds." +sleep 2 +rsync -avz --progress "$DIR" "/tmp/$DIR_SHORT.tmp" + +umount-squash-image "$DIR" + +rm -rf "$DIR" +mv "/tmp/$DIR_SHORT.tmp" "$DIR" + +rm -rf "$DIR/../.squashfs/$DIR_SHORT.img" +rm -rf "$DIR/../.squashfs/$DIR_SHORT" + +echo "Your SquashFS image was deleted." diff --git a/editx b/editx index 276209f..839dfd2 100755 --- a/editx +++ b/editx @@ -1,4 +1,4 @@ -# !/bin/sh +#!/usr/bin/env bash # This utility pre-allocs a file and adds execution permissions. It also # removes the resulting file if it is empty after the editor closes. diff --git a/filewait b/filewait index af52929..2705a43 100755 --- a/filewait +++ b/filewait @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # A simple utility that waits for a file to become available, infinitely LD_INTERNAL=1 diff --git a/make-squash-image b/make-squash-image new file mode 100755 index 0000000..4d33eeb --- /dev/null +++ b/make-squash-image @@ -0,0 +1,88 @@ +#!/bin/bash +# Lackadaisical squashfs tools +# Allows you to create a modifiable squashfs-backed image for a folder +BIN_DIR=$(dirname "$(readlink -f "$0")") +DIR=$1 + +test -d "$DIR" +if [[ $? -ne 0 ]]; then + echo Directory "$DIR" does not exist! + exit -1 +fi + +DIRSIZE=$(du -s "$DIR") + +DIR=$(readlink -f "$DIR") +DIR_SHORT=$(basename "$DIR") +echo Compressing "$DIR" + +# Make basic dirs +mkdir -p "${DIR}" +mkdir -p "${DIR_SHORT}" + +OVERLAY_ROOT=$(readlink -f "${DIR}/..")/.squashfs/${DIR_SHORT} +OVERLAY_UPPER=${OVERLAY_ROOT}/upper +OVERLAY_LOWER=${OVERLAY_ROOT}/lower +OVERLAY_WORK=${OVERLAY_ROOT}/work +OVERLAY_TARG=$DIR +RECREATE=false + +echo "Overlay information" +echo "=========================================================================" +echo "> DIR = \"$DIR\"" +echo "> DIR_SHORT = \"$DIR_SHORT\"" +echo "> OVERLAY_ROOT = \"$OVERLAY_ROOT\"" +echo "> OVERLAY_UPPER = \"$OVERLAY_LOWER\"" +echo "> OVERLAY_LOWER= \"$OVERLAY_UPPER\"" +echo "> OVERLAY_WORK = \"$OVERLAY_WORK\"" +echo "> OVERLAY_TARG = \"$OVERLAY_TARG\"" +echo "=========================================================================" + +# Make the dirs for the overlay +mkdir -p "$OVERLAY_ROOT" +mkdir -p "$OVERLAY_UPPER" +mkdir -p "$OVERLAY_LOWER" +mkdir -p "$OVERLAY_WORK" +mkdir -p "$OVERLAY_TARG" + +# Check for existing image +if test -f "${OVERLAY_ROOT}.img"; then + echo "We already have an existing image, updating i..." + RECREATE=true +fi + +sudo mksquashfs "$DIR" "${OVERLAY_ROOT}.img.1" -noappend -comp xz + +if [ $RECREATE == true ]; then + echo "Copy created, now unmount and swap everything." + sudo rm -rf "$OVERLAY_UPPER" + sudo rm -rf "$OVERLAY_LOWER" + sudo rm -rf "$OVERLAY_WORK" + rm -rf "${OVERLAY_ROOT}.img" +fi + +mv "${OVERLAY_ROOT}.img.1" "${OVERLAY_ROOT}.img" + +# Reset dir +sudo rm -rf "$DIR" +mkdir -p "$DIR" +touch "${DIR}/.needs_mount" + +echo "Your SquashFS-backed folder is ready for use." +echo "To mount it, either cd into it or use mount-squash-image." +echo "We recomnnend setting up a cronjob for that." +echo "" +echo "Should you wish to update the contents of the image with your changes made" +echo "imside of the folder, simply run make-squash-image again on the same" +echo "folder to update the imaghe." +echo +echo "To disable auto-mounting upon cd, pleas remove the" +echo "'.needs_mount' file to prevent that." +echo "To enable mount at-login, add something aking to this to cron:" +echo "\"@reboot /mount-squash-image \"" +echo +echo "=========================================================================" +echo "Storage data:" +echo "Uuncompressed: $DIRSIZE" +echo " Compressed: $(du -s ${OVERLAY_ROOT}.img)" +echo "=========================================================================" diff --git a/mount-squash-image b/mount-squash-image new file mode 100755 index 0000000..d2e065f --- /dev/null +++ b/mount-squash-image @@ -0,0 +1,20 @@ +#!/bin/bash +echo $@ + +DIR="$@" +DIR=$(readlink -f "$DIR") +DIR_SHORT=$(basename $DIR) +mkdir -p "$DIR" + +OVERLAY_ROOT=$(readlink -f "${DIR}/..")/.squashfs/${DIR_SHORT} +OVERLAY_UPPER=${OVERLAY_ROOT}/upper +OVERLAY_LOWER=${OVERLAY_ROOT}/lower +OVERLAY_WORK=${OVERLAY_ROOT}/work +OVERLAY_TARG=$DIR + +umount-squash-image $@ 1>/dev/null 2>/dev/null + +sudo mount "${OVERLAY_ROOT}.img" "$OVERLAY_LOWER" -t squashfs -o loop +sudo mount -t overlay -o lowerdir="$OVERLAY_LOWER",upperdir="$OVERLAY_UPPER",workdir="$OVERLAY_WORK" none "$OVERLAY_TARG" + +echo "SquashFS filesystem is mounted and ready," diff --git a/own b/own index 9d59e2c..84cd6fe 100755 --- a/own +++ b/own @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # Simple program that changes ownership to the current # user, recursively. @@ -14,4 +14,4 @@ then exit 2 fi -sudo chown -R $(whoami):$(whoami) $@ +sudo chown -R $(whoami):$(whoami) "$@" diff --git a/binbox b/scriptbox similarity index 58% rename from binbox rename to scriptbox index ec1e474..6aeb03d 100755 --- a/binbox +++ b/scriptbox @@ -1,5 +1,5 @@ -#!/bin/sh -# binbox: Creates a multi-binary script that self-contains the input scripts. +#!/usr/bin/env bash +# scriptbox: Creates a multi-binary script that self-contains the input scripts. # Symlinking to the resulting binary with the name of one of the original scripts will trigger # said script. The idea is similar to `busybox`. @@ -11,6 +11,7 @@ args=$@ function help() { echo "$LD_BIN is a utility that allows you to generate busybox-style combined binaries." + echo "It only supports shell scripts compatible with \`bash\`." echo "To access the original functionality of an input binary, you can either use a symlink or" echo "call the function like so: 'combi-bin input-bin ." echo "" @@ -55,32 +56,51 @@ i=0 b=0 s=0 p=0 + while [ $i -lt $# ]; do case "${args[$i]}" in -i) ((i++)) while [ $i -lt $# ] && [[ ! "${args[$i]}" =~ ^- ]]; do - inputs+=("${args[$i]}") + file="${args[$i]}" ((i++)) - ((b++)) + if [[ -e "$file" ]]; then + inputs+=("$file") + ((b++)) + else + echo "WARNING: Missing input binary: \"$file\"." + echo "Module will NOT be available!" + fi done continue ;; -s) ((i++)) while [ $i -lt $# ] && [[ ! "${args[$i]}" =~ ^- ]]; do - sources+=("${args[$i]}") + file="${args[$i]}" ((i++)) - ((s++)) + if [[ -e "$file" ]]; then + sources+=("$file") + ((s++)) + else + echo "WARNING: Missing input source: \"$file\"." + echo "File will NOT be sourced!" + fi done continue ;; -p) ((i++)) while [ $i -lt $# ] && [[ ! "${args[$i]}" =~ ^- ]]; do - includes+=("${args[$i]}") + file="${args[$i]}" ((i++)) - ((p++)) + if [[ -e "$file" ]]; then + includes+=("$file") + ((p++)) + else + echo "WARNING: Missing input include: \"$file\"." + echo "File will NOT be included!" + fi done continue ;; @@ -97,11 +117,6 @@ echo "Include files: ${includes[*]}" echo "Source files: ${sources[*]}" echo "Output binary: $output" -if [ "$b" -eq 0 ]; then - echo "Missing input binaries!" - exit 1 -fi - if [[ "$output" == "" ]]; then echo "Missing output file!" exit 1 @@ -112,11 +127,16 @@ function add() echo "$@" >> "$output" } +function add_nn() +{ + echo -n "$@" >> "$output" +} + rm -rf "$output" # Now to construct the binary # >>> Section 1, includes -add "#!/bin/bash" +add "#!/usr/bin/env bash" add "# Multi-call binary generated by LACKADAISICAL binbox" add "# $output information:" add "# Contained modules: ${inputs[*]}" @@ -135,6 +155,7 @@ for f in "${includes[@]}"; do add "$(cat "$f")" done +add "readarray -t all_funcs < <(declare -F | awk '{print \$3}')" # >>> Section 2: Modules for f in "${inputs[@]}"; do @@ -146,25 +167,52 @@ for f in "${inputs[@]}"; do done # >>> Section 3: Module selection +add "" +add "################################################################################" +add "# END OF INCLUDED MODULES ######################################################" +add "################################################################################" +add "" +add "# Array of modules as well as array of functions." +# Add a static list of modules with a no-newline add +add_nn "modules=(" + for f in "${inputs[@]}"; do + add_nn "\"$f\" " + done +add ")" +add 'mapfile -t funcs < <(printf "%s\n" "${all_funcs[@]}" "${modules[@]}" "${modules[@]}" | sort | uniq -u)' +add "" +add "# Check the export switches (-m and -f)" +add "" + add "symed=1" add "binself=\$(basename \$0)" -add "boxfile=\"$output\"" +add "boxfile=\"$(basename $output)\"" +add "" add "if [[ \$binself == \$boxfile ]]; then" add " symed=0" add " if [[ \$# -eq 0 ]]; then" -add " echo 'Available modules:'" - for f in "${inputs[@]}"; do - add " echo '$f'" - done +add " echo '$(basename $output): Multi-call binary generated by lackadaisical binbox.'" +add " echo 'Use switch \"-m\" to generate symlinks of modules in the current directory,'" +add " echo 'or use switch \"-f\" to generate symlinks of all available functions.'" +add " echo ''" +add " echo 'Exported modules:'" +add " for mod in \"\${modules[@]}\"; do" +add " echo \"- \$mod\"" +add " done" +add " echo ''" +add " echo 'Other functions:'" +add " for func in \"\${funcs[@]}\"; do" +add " echo \"- \$func\"" +add " done" add " exit 0" add " fi" add "fi" - +add "" add "if [[ \$symed -eq 0 ]]; then" add " eval \$@" add " exit \$?" add "fi" - +add "" add "if [[ \$symed -eq 1 ]]; then" add " eval \$(basename \$0) \$@" add "fi" diff --git a/shrc b/shrc index 4e0d0ee..485bfc7 100755 --- a/shrc +++ b/shrc @@ -1,4 +1,4 @@ -#!/bin/echo Please run this program with: ". shrc" or include +#!/usr/bin/env -S echo Please run this program with: ". shrc" or include # `FROM_RC=1 source shrc` in your RC file to set up an alias. # Just opens your .rc file easily, for your current shell @@ -10,37 +10,9 @@ then LD_INTERNAL=1 source $LD_SOURCE_FILE fi -function md5_opt() -{ - if [[ $daisy_has_md5sum -eq 1 ]]; - then - echo $(md5sum "$1" | awk '{print $1}') - fi -} +shellname=$(basename $SHELL) +rc_name="."$shellname"rc" +rc_path="$HOME/$rc_name" -if [[ $FROM_RC -eq 1 ]]; then - alias shrc=". shrc" -else - shellname=$(basename $SHELL) - rc_name="."$shellname"rc" - rc_path="$HOME/$rc_name" - - # Optional MD5 checks - has_changed=1 - sum1=$(md5_opt "$rc_path") - - # This sets a default if the variable does not exist. - EDITOR=${EDITOR:-$(ched)} - $EDITOR "$rc_path" - wait_for_editor $EDITOR "$rc_path" - - sum2=$(md5_opt "$rc_path") - - if [[ $daisy_has_md5sum -eq 1 && "$sum1" == "$sum2" ]]; - then - echo "The RC-file $rc_name has not changed on disk. Not sourcing." - return - fi - - source "$rc_path" -fi +daisy_editor "$rc_path" +source "$rc_path" diff --git a/sshp b/sshp new file mode 100755 index 0000000..eeedc7e --- /dev/null +++ b/sshp @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +mounts=() +ssh_args=() +remote_port=$((10000 + RANDOM % 10000)) +local_user=$(whoami) + +usage() +{ + echo "sshp (\"SSH PLUS\") from lackadaisical." + echo "Accepts all standard SSH options (see man ssh)." + echo "Additionally, supports SSHFS mounts using a Docker-style syntax (-m)." + echo "Usage: sshp -m : [user@]host [ssh_options]" + echo "Example: sshp -m /home/juli:/home/juli juli@juli.pyon" + exit 1 +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -m) + if [[ -z "$2" ]] + then + echo "Error: -m requires argument" + exit 1 + fi + mounts+=("$2") + shift 2 + ;; + *) + ssh_args+=("$1") + shift + ;; + esac +done + +if [[ ${#ssh_args[@]} -eq 0 ]] +then + usage +fi + +mount_logic="" +unmount_logic="" + +for map in "${mounts[@]}"; do + local_path="${map%%:*}" + remote_path="${map##*:}" + + if [[ "$local_path" != /* ]] + then + local_path="$PWD/$local_path" + fi + + mount_logic+=" + echo '>> Preparing mount: ${remote_path}'; + if ! mkdir -p \"${remote_path}\" 2>/dev/null + then + sudo mkdir -p \"${remote_path}\" + fi + + if ! sshfs -p ${remote_port} -o StrictHostKeyChecking=no,idmap=user ${local_user}@localhost:\"${local_path}\" \"${remote_path}\" 2>/dev/null + then + echo ' (User mount failed, attempting escalation...)'; + if ! sudo sshfs -p ${remote_port} -o StrictHostKeyChecking=no,idmap=user,allow_other ${local_user}@localhost:\"${local_path}\" \"${remote_path}\" + then + echo ' ! Mount failed completely. Check permissions or keys.'; + fi + else + echo ' > Mounted successfully.'; + fi + " + + unmount_logic+=" + fusermount -u -z \"${remote_path}\" 2>/dev/null || sudo fusermount -u -z \"${remote_path}\" 2>/dev/null; + " +done + +remote_script=" + if ! command -v sshfs >/dev/null 2>&1 + then + echo 'WARNING: \"sshfs\" not found on remote host.' + echo '>> Skipping mounts, proceeding with shell only...'; + echo '----------------------------------------------'; + else + ${mount_logic} + fi + + ${SHELL:-bash}; + + if command -v sshfs >/dev/null 2>&1 + then + ${unmount_logic} + fi +" + +ssh -t -R ${remote_port}:localhost:22 "${ssh_args[@]}" "$remote_script" diff --git a/sw b/sw index 8427de3..0971413 100755 --- a/sw +++ b/sw @@ -1,7 +1,7 @@ -#!/bin/sh +#!/usr/bin/env bash # It just swaps two files -export LD_INTERNAL1 +LD_INTERNAL1 . $(dirname $(realpath $0))/daisy.source file1=$1 diff --git a/umount-squash-image b/umount-squash-image new file mode 100755 index 0000000..9eb0522 --- /dev/null +++ b/umount-squash-image @@ -0,0 +1,18 @@ +#!/bin/bash +DIR="$@" +DIR=$(readlink -f "$DIR") +DIR_SHORT=$(basename $DIR) +mkdir -p "$DIR" + +OVERLAY_ROOT=$(readlink -f "${DIR}/..")/.squashfs/${DIR_SHORT} +OVERLAY_UPPER=${OVERLAY_ROOT}/upper +OVERLAY_LOWER=${OVERLAY_ROOT}/lower +OVERLAY_WORK=${OVERLAY_ROOT}/work +OVERLAY_TARG=$DIR + +sudo umount -R "$OVERLAY_ROTO" +sudo umount -R "$OVERLAY_LOWER" +sudo umount -R "$OVERLAY_TARG" +sudo umount -R "$DIR" + +echo "SquashFS filesystem has been unmounted." diff --git a/what b/what index 0bf8c12..b4a470d 100755 --- a/what +++ b/what @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # Where is the binary? # Usage: what [] # Returns: From 2e31283b61aa5f4c2f0cee9b1cea8442f54b6b05 Mon Sep 17 00:00:00 2001 From: Sam Hardeman Date: Fri, 13 Mar 2026 00:37:28 +0100 Subject: [PATCH 2/5] Pre-refactor --- destroy-squash-image | 65 +++++++++++++++++++------- make-squash-image | 109 ++++++++++++++++++++++++++++++++----------- mount-squash-image | 30 +++++++++--- own | 2 +- umount-squash-image | 10 ++-- 5 files changed, 157 insertions(+), 59 deletions(-) diff --git a/destroy-squash-image b/destroy-squash-image index dc4f3a3..8bde37f 100755 --- a/destroy-squash-image +++ b/destroy-squash-image @@ -1,27 +1,56 @@ #!/bin/bash -DIR="$@" -DIR=$(readlink -f "$DIR") -DIR_SHORT=$(basename $DIR) +# Lackadaisical squashfs tools - Destroy +BIN_DIR=$(dirname "$(readlink -f "$0")") +DIR="$1" -if [[ -f "$DIR/.needs_mount" ]]; -then - echo "Not a SquashFS image, exiting." - exit 0 +if [[ -z "$DIR" ]]; then + echo "Usage: $0 " + exit 1 fi -echo "Destroying the existing SquashFS image." -echo "Copying data from the original directory." +DIR=$(readlink -f "$DIR") +DIR_SHORT=$(basename "$DIR") -echo "Using rsync... in 2 seconds." -sleep 2 -rsync -avz --progress "$DIR" "/tmp/$DIR_SHORT.tmp" +if [[ ! -f "$DIR/.needs_mount" ]]; then + if ! mountpoint -q "$DIR"; then + echo "Error: $DIR does not appear to be a SquashFS-backed directory (missing .needs_mount)." + exit 1 + fi +fi -umount-squash-image "$DIR" +# Disable systemd service +ESC_PATH=$(systemd-escape -p "$DIR") +SERVICE_NAME="squash-mount@-$ESC_PATH" -rm -rf "$DIR" -mv "/tmp/$DIR_SHORT.tmp" "$DIR" +echo "Disabling systemd service ($SERVICE_NAME)..." +sudo systemctl stop "$SERVICE_NAME" 2>/dev/null +sudo systemctl disable "$SERVICE_NAME" 2>/dev/null -rm -rf "$DIR/../.squashfs/$DIR_SHORT.img" -rm -rf "$DIR/../.squashfs/$DIR_SHORT" +# Ensure it is mounted to copy merged data +echo "Ensuring image is mounted to preserve data..." +"$BIN_DIR"/mount-squash-image "$DIR" 1>/dev/null 2>/dev/null + +echo "Destroying SquashFS image and restoring data to normal directory..." + +TEMP_DIR=$(mktemp -d) +# Copy data out (preserving all attributes) +sudo rsync -a "$DIR/" "$TEMP_DIR/" + +# Unmount the overlay and squashfs +echo "Unmounting..." +"$BIN_DIR"/umount-squash-image "$DIR" + +# Restore data to original location +echo "Restoring files..." +sudo rm -rf "$DIR" +sudo mv "$TEMP_DIR" "$DIR" + +# Cleanup hidden image and overlay files +OVERLAY_ROOT=$(readlink -f "${DIR}/..")/.squashfs/${DIR_SHORT} +echo "Cleaning up ${OVERLAY_ROOT}..." +sudo rm -rf "${OVERLAY_ROOT}.img" +sudo rm -rf "${OVERLAY_ROOT}" + +echo "Success: SquashFS image destroyed and data restored to $DIR." +exit 0 -echo "Your SquashFS image was deleted." diff --git a/make-squash-image b/make-squash-image index 4d33eeb..b6111e9 100755 --- a/make-squash-image +++ b/make-squash-image @@ -4,12 +4,29 @@ BIN_DIR=$(dirname "$(readlink -f "$0")") DIR=$1 +if [[ -z "$DIR" ]]; then + echo "Usage: $0 " + exit 1 +fi + test -d "$DIR" if [[ $? -ne 0 ]]; then echo Directory "$DIR" does not exist! - exit -1 + exit -1 fi +echo "You need support for FUSE, SquashFS, and OverlayFS." +echo "Checking your system for these requirements..." +for fs in fuse squashfs overlay; do + if ! grep -q "$fs" /proc/filesystems; then + echo "Loading $fs kernel module..." + if ! sudo modprobe "$fs"; then + echo "Error: $fs is not supported and could not be loaded." + exit 1 + fi + fi +done + DIRSIZE=$(du -s "$DIR") DIR=$(readlink -f "$DIR") @@ -32,57 +49,93 @@ echo "=========================================================================" echo "> DIR = \"$DIR\"" echo "> DIR_SHORT = \"$DIR_SHORT\"" echo "> OVERLAY_ROOT = \"$OVERLAY_ROOT\"" -echo "> OVERLAY_UPPER = \"$OVERLAY_LOWER\"" -echo "> OVERLAY_LOWER= \"$OVERLAY_UPPER\"" +echo "> OVERLAY_UPPER = \"$OVERLAY_UPPER\"" +echo "> OVERLAY_LOWER= \"$OVERLAY_LOWER\"" echo "> OVERLAY_WORK = \"$OVERLAY_WORK\"" echo "> OVERLAY_TARG = \"$OVERLAY_TARG\"" echo "=========================================================================" -# Make the dirs for the overlay mkdir -p "$OVERLAY_ROOT" -mkdir -p "$OVERLAY_UPPER" -mkdir -p "$OVERLAY_LOWER" -mkdir -p "$OVERLAY_WORK" -mkdir -p "$OVERLAY_TARG" # Check for existing image if test -f "${OVERLAY_ROOT}.img"; then - echo "We already have an existing image, updating i..." + echo "We already have an existing image, updating it..." + mount-squash-image "$DIR" + DIRSIZE=$(du -s "$DIR") RECREATE=true fi sudo mksquashfs "$DIR" "${OVERLAY_ROOT}.img.1" -noappend -comp xz if [ $RECREATE == true ]; then - echo "Copy created, now unmount and swap everything." - sudo rm -rf "$OVERLAY_UPPER" - sudo rm -rf "$OVERLAY_LOWER" - sudo rm -rf "$OVERLAY_WORK" - rm -rf "${OVERLAY_ROOT}.img" + echo "Copy created, now unmount and swap everything." + umount-squash-image "$DIR" + sudo rm -rf "$OVERLAY_UPPER" + sudo rm -rf "$OVERLAY_LOWER" + sudo rm -rf "$OVERLAY_WORK" + rm -rf "${OVERLAY_ROOT}.img" fi mv "${OVERLAY_ROOT}.img.1" "${OVERLAY_ROOT}.img" +# Make the dirs for the overlay +mkdir -p "$OVERLAY_UPPER" +mkdir -p "$OVERLAY_LOWER" +mkdir -p "$OVERLAY_WORK" +mkdir -p "$OVERLAY_TARG" + # Reset dir sudo rm -rf "$DIR" mkdir -p "$DIR" touch "${DIR}/.needs_mount" -echo "Your SquashFS-backed folder is ready for use." -echo "To mount it, either cd into it or use mount-squash-image." -echo "We recomnnend setting up a cronjob for that." -echo "" -echo "Should you wish to update the contents of the image with your changes made" -echo "imside of the folder, simply run make-squash-image again on the same" -echo "folder to update the imaghe." -echo -echo "To disable auto-mounting upon cd, pleas remove the" -echo "'.needs_mount' file to prevent that." -echo "To enable mount at-login, add something aking to this to cron:" -echo "\"@reboot /mount-squash-image \"" -echo echo "=========================================================================" echo "Storage data:" -echo "Uuncompressed: $DIRSIZE" +echo "Uncompressed: $DIRSIZE" echo " Compressed: $(du -s ${OVERLAY_ROOT}.img)" echo "=========================================================================" + +# Service content embedded +SERVICE_CONTENT="[Unit] +Description=SquashFS Mount for %I +After=local-fs.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=${BIN_DIR}/mount-squash-image %I +ExecStop=${BIN_DIR}/umount-squash-image %I + +[Install] +WantedBy=multi-user.target" + +echo "$SERVICE_CONTENT" | sudo tee /etc/systemd/system/squash-mount@.service > /dev/null +sudo systemctl daemon-reload + +ESC_PATH=$(systemd-escape -p "$DIR") +SERVICE_NAME="squash-mount@-$ESC_PATH" + +read -p "Do you want to auto-enable the mount service ($SERVICE_NAME)? [y/N] " yn +case $yn in + [Yy]* ) + echo "Enabling and starting service..." + sudo systemctl enable --now "$SERVICE_NAME" + sudo systemctl stop "$SERVICE_NAME" + sudo systemctl start "$SERVICE_NAME" + ;; + * ) + echo "To enable the service manually, run: sudo systemctl enable --now $SERVICE_NAME" + ;; +esac + +echo "" +echo "Your SquashFS-backed folder is ready for use." +echo "To mount it manually, either cd into it or use mount-squash-image." +echo "" +echo "Should you wish to update the contents of the image with your changes made" +echo "inside of the folder, simply run make-squash-image again on the same" +echo "folder to update the image." +echo +echo "To disable auto-mounting upon cd, please remove the" +echo ".needs_mount file to prevent that." +echo diff --git a/mount-squash-image b/mount-squash-image index d2e065f..ee46f3d 100755 --- a/mount-squash-image +++ b/mount-squash-image @@ -1,9 +1,8 @@ #!/bin/bash -echo $@ - DIR="$@" DIR=$(readlink -f "$DIR") -DIR_SHORT=$(basename $DIR) +DIR_SHORT=$(basename "$DIR") +BIN_DIR=$(dirname "$(readlink -f "$0")") mkdir -p "$DIR" OVERLAY_ROOT=$(readlink -f "${DIR}/..")/.squashfs/${DIR_SHORT} @@ -12,9 +11,28 @@ OVERLAY_LOWER=${OVERLAY_ROOT}/lower OVERLAY_WORK=${OVERLAY_ROOT}/work OVERLAY_TARG=$DIR -umount-squash-image $@ 1>/dev/null 2>/dev/null +if [ ! -f "${OVERLAY_ROOT}.img" ]; then + echo "Error: SquashFS image ${OVERLAY_ROOT}.img not found." >&2 + exit 1 +fi + +"$BIN_DIR"/umount-squash-image "$@" 1>/dev/null 2>/dev/null sudo mount "${OVERLAY_ROOT}.img" "$OVERLAY_LOWER" -t squashfs -o loop -sudo mount -t overlay -o lowerdir="$OVERLAY_LOWER",upperdir="$OVERLAY_UPPER",workdir="$OVERLAY_WORK" none "$OVERLAY_TARG" +EXIT_CODE=$? +if [ $EXIT_CODE -ne 0 ]; then + echo "Error: Failed to mount squashfs image (exit code $EXIT_CODE)." >&2 + exit $EXIT_CODE +fi -echo "SquashFS filesystem is mounted and ready," +sudo mount -t overlay -o lowerdir="$OVERLAY_LOWER",upperdir="$OVERLAY_UPPER",workdir="$OVERLAY_WORK" none "$OVERLAY_TARG" +EXIT_CODE=$? +if [ $EXIT_CODE -ne 0 ]; then + echo "Error: Failed to mount overlay (exit code $EXIT_CODE)." >&2 + # Cleanup: unmount the lower squashfs if overlay mount fails + sudo umount "$OVERLAY_LOWER" 2>/dev/null + exit $EXIT_CODE +fi + +echo "SquashFS filesystem is mounted and ready." +exit 0 diff --git a/own b/own index 84cd6fe..a04be63 100755 --- a/own +++ b/own @@ -14,4 +14,4 @@ then exit 2 fi -sudo chown -R $(whoami):$(whoami) "$@" +sudo chown -R $(whoami):$(whoami) "$@" 1>/dev/null 2>/dev/null diff --git a/umount-squash-image b/umount-squash-image index 9eb0522..58f36b1 100755 --- a/umount-squash-image +++ b/umount-squash-image @@ -1,8 +1,7 @@ #!/bin/bash DIR="$@" DIR=$(readlink -f "$DIR") -DIR_SHORT=$(basename $DIR) -mkdir -p "$DIR" +DIR_SHORT=$(basename "$DIR") OVERLAY_ROOT=$(readlink -f "${DIR}/..")/.squashfs/${DIR_SHORT} OVERLAY_UPPER=${OVERLAY_ROOT}/upper @@ -10,9 +9,8 @@ OVERLAY_LOWER=${OVERLAY_ROOT}/lower OVERLAY_WORK=${OVERLAY_ROOT}/work OVERLAY_TARG=$DIR -sudo umount -R "$OVERLAY_ROTO" -sudo umount -R "$OVERLAY_LOWER" -sudo umount -R "$OVERLAY_TARG" -sudo umount -R "$DIR" +sudo umount -R "$OVERLAY_TARG" 2>/dev/null +sudo umount -R "$OVERLAY_LOWER" 2>/dev/null echo "SquashFS filesystem has been unmounted." +exit 0 From 83324dd0f730fee84ef2fd2fa191d1a2020a1d94 Mon Sep 17 00:00:00 2001 From: Sam Hardeman Date: Fri, 13 Mar 2026 01:38:34 +0100 Subject: [PATCH 3/5] After gemini-propoesed fixes --- agenda | 12 ++-- cdz | 133 ++++++++++++++++--------------------------- daisy.source | 21 ++++--- destroy-squash-image | 48 +++++++--------- editx | 13 ++--- make-squash-image | 101 +++++++++++++------------------- mount-squash-image | 50 +++++++++------- own | 18 ++++-- sshp | 101 +++++++++++--------------------- umount-squash-image | 27 ++++++--- what | 11 ++-- 11 files changed, 228 insertions(+), 307 deletions(-) diff --git a/agenda b/agenda index 837cd0f..f89dd61 100755 --- a/agenda +++ b/agenda @@ -28,10 +28,7 @@ root_dir=$(dirname $1)/.daisy/$(basename $1) today_sym=$1 # Present day -today=$(date -I) -year=$(echo $today | awk -F"-" '{print $1}') -month=$(echo $today | awk -F"-" '{print $2}') -day=$(echo $today | awk -F"-" '{print $3}') +read year month day < <(date "+%Y %m %d") set -e @@ -50,11 +47,10 @@ function errorFn trap errorFn ERR # First we clear out empty folders, and remove the symlink if it exists -test -e "$root_dir" && find "$root_dir" -maxdepth 3 -type d -empty -print | xargs rm -rf -test -L "$today_sym" && rm -rf "$today_sym" +test -e "$root_dir" && find "$root_dir" -maxdepth 3 -type d -empty -delete # Now we can set up today's directory mkdir -p "$root_dir/$year/$month/$day" -ln -s "$root_dir/$year/$month/$day" "$today_sym" +ln -sf "$root_dir/$year/$month/$day" "$today_sym" ln -s "$root_dir" "$today_sym/.tree" -exitcode=@? +exitcode=$? diff --git a/cdz b/cdz index a380222..94e5e4c 100755 --- a/cdz +++ b/cdz @@ -1,118 +1,85 @@ #!/usr/bin/env bash +# Usage: cdz -if [[ $LD_INTERNAL -ne 1 ]]; -then +if [[ $LD_INTERNAL -ne 1 ]]; then LD_INTERNAL=1 . $(dirname $(realpath $0))/daisy.source fi target=$1 - -# Check if file exists -if [[ -z "$target" ]]; -then +if [[ -z "$target" ]]; then echo "No target specified." exit 1 fi -if ! test -f "$target"; -then +if [[ ! -f "$target" ]]; then echo "File not found: \"$target\"" exit 2 fi -# Check if archivemount is present -which archivemount 1>/dev/null 2>/dev/null -hasmounter=$? +target_abs=$(realpath "$target") +name=$(basename "$target") -file "$target" 1>/dev/null -exitcode=$? -report=$(file "$target") -name=$(echo $@ | sed 's/:.*//' | sed 's|.*/||') +has_archivemount=$(command -v archivemount >/dev/null 2>&1; echo $?) + +if [[ $has_archivemount -eq 0 && $NO_ARCHIVEMOUNT -ne 1 ]]; then + use_mounter=1 +else + use_mounter=0 +fi + +mime_type=$(file --mime-type -b "$target") -# Check for archive type, supported types are zip/tar/rar comm1=(:) -comm2=(echo "Unsupported archive type$add: \"$target\"") +comm2=(echo "Unsupported archive type: $mime_type") comm3=(:) comm4=(:) comm5=(:) -echo $report | grep "tar archive" 1>/dev/null -istar=$? -echo $report | grep "Zip archive" 1>/dev/null -iszip=$? -echo $report | grep "Android" 1>/dev/null -iszip=$? -echo $report | grep "RAR archive" 1>/dev/null -israr=$? - -# TAR archives come in many forms, if none of our tests say it's tar -# ...but it looks like tar and barks like tar, let's take the shot. -# Seems to work fairly well for the record. -res=$(echo "$target" | grep ".tar") -if [[ $res != "" ]]; -then - istar=0 +if [[ $use_mounter -eq 1 ]]; then + echo "Using archivemount..." + comm2=(archivemount -o allow_root,use_ino "$target_abs") + comm4=(cd ..) + comm5=(umount) +elif [[ $mime_type == *"tar"* || $target == *.tar* ]]; then + comm2=(tar xvf "$target_abs" -C) +elif [[ $mime_type == "application/zip" ]]; then + if command -v unzip >/dev/null 2>&1; then + comm2=(unzip -q "$target_abs" -d) + else + comm1=(echo "unzip is missing") + comm3=(exit 1) + fi +elif [[ $mime_type == "application/x-rar" ]]; then + if command -v unrar >/dev/null 2>&1; then + # unrar needs the directory as the last argument + comm2=(unrar x -idq "$target_abs") + else + comm1=(echo "unrar is missing") + comm3=(exit 1) + fi fi -if [[ $NO_ARCHIVEMOUNT -eq 1 ]]; then - hasmounter=1 -fi - -if (( $hasmounter == 0 )); then - echo "We have \`archivemount\`, so we'll use that!" - echo "If you'd prefer we not use it, please specify NO_ARCHIVEMOUNT=1" - istar=1 - iszip=1 - israr=1 -fi - -# Now we set the right command -if (( $istar == 0 )); then - comm2=(tar xvf "$target" -C) -elif (( $iszip == 0 )); then - which unzip 1>/dev/null - exitcode=$? - if (( $exitcode == 0 )); then - comm2=(unzip -q "$target" -d) - else - comm1=(echo "The utility 'unzip' is missing, please install it") - comm3=(exit 1) - fi -elif (( $israr == 0 )); then - which unrar 1>/dev/null - exitcode=$? - if (( exitcode == 0 )); then - comm2=(unrar -i nul "$target") - else - comm1=(echo "The utility 'unrar' is missing, please install it") - comm3=(exit 1) - fi -elif (( $hasmounter == 0 )); then - comm2=(archivemount -o allow_root -o use_ino "$target") - comm4=(cd ..) - comm5=(umount) -fi - -# Create the temp dir, usually dir=$(mktemp -d /tmp/extracted.XXXXXXXX) -# And the rest of the commands "${comm1[@]}" -"${comm2[@]}" $dir +if [[ ${comm2[0]} == "unrar" ]]; then + "${comm2[@]}" "$dir" +else + "${comm2[@]}" "$dir" +fi "${comm3[@]}" -currentpath=$(pwd) -sym="$currentpath/$name.tmp" -ln -f -s "$dir" "$sym" +sym="$(pwd)/$name.tmp" +ln -sf "$dir" "$sym" cd "$sym" -echo "A symlink has been made under the name \"$sym\"." -echo "This symlink points to the data directory \"$dir\"." +echo "Extracted to $dir (linked via $sym)" +echo "Type exit to finish." -echo "Type 'exit' to exit the extracted archive's folder and auto-delete it." eval $SHELL + "${comm4[@]}" -"${comm5[@]}" $dir -rm -rf $dir +"${comm5[@]}" "$dir" +rm -rf "$dir" rm "$sym" diff --git a/daisy.source b/daisy.source index 24fa572..97a43c6 100755 --- a/daisy.source +++ b/daisy.source @@ -24,7 +24,6 @@ function ld_dbg then $@ fi - echo } # Variables for use in other utilities @@ -38,10 +37,11 @@ fi # Check for dependencies function _daisy_dependency_check { - BIN=$(command -v $1 2>/dev/null) - res=$? - - echo $(($res ^ 1)) + if command -v "$1" >/dev/null 2>&1; then + echo 1 + else + echo 0 + fi } LD_HAS_fzf=$(_daisy_dependency_check fzf) @@ -88,10 +88,9 @@ ld_dbg cat $LD_EDITORFILE # Source everything in the config folder function _daisy_source_configs { - for f in `find "$LD_CONFIG_FOLDER" -name "*.src" -type f`; - do - source "$f" - done +while IFS= read -r -d '' f; do + source "$f" + done < <(find "$LD_CONFIG_FOLDER" -name "*.src" -type f -print0) } # Installation into PATH @@ -124,7 +123,7 @@ function daisy_wait_for_editor while true; do - alive=$(ps aux | grep $fname | grep $pname) + alive=$(pgrep -f "$pname.*$fname") if [[ $alive == "" ]] then break @@ -265,7 +264,7 @@ function ched for editor in "${editors[@]}"; do editor_real=$(command -v "$editor") - if command -v "$editor_rmeal" >/dev/null 2>&1; + if command -v "$editor_real" >/dev/null 2>&1; then if [[ $(realpath "$EDITOR") == "$editor_real" ]]; then diff --git a/destroy-squash-image b/destroy-squash-image index 8bde37f..6eef52a 100755 --- a/destroy-squash-image +++ b/destroy-squash-image @@ -1,8 +1,7 @@ #!/bin/bash # Lackadaisical squashfs tools - Destroy -BIN_DIR=$(dirname "$(readlink -f "$0")") -DIR="$1" +DIR="$1" if [[ -z "$DIR" ]]; then echo "Usage: $0 " exit 1 @@ -10,47 +9,42 @@ fi DIR=$(readlink -f "$DIR") DIR_SHORT=$(basename "$DIR") +BIN_DIR=$(dirname "$(readlink -f "$0")") if [[ ! -f "$DIR/.needs_mount" ]]; then - if ! mountpoint -q "$DIR"; then - echo "Error: $DIR does not appear to be a SquashFS-backed directory (missing .needs_mount)." - exit 1 - fi + if ! mountpoint -q "$DIR"; then + echo "Error: $DIR is not a SquashFS directory." + exit 1 + fi fi -# Disable systemd service +# Stop systemd service ESC_PATH=$(systemd-escape -p "$DIR") -SERVICE_NAME="squash-mount@-$ESC_PATH" +SERVICE_NAME="squash-mount@$ESC_PATH.service" -echo "Disabling systemd service ($SERVICE_NAME)..." +echo "Disabling service ($SERVICE_NAME)..." sudo systemctl stop "$SERVICE_NAME" 2>/dev/null sudo systemctl disable "$SERVICE_NAME" 2>/dev/null # Ensure it is mounted to copy merged data echo "Ensuring image is mounted to preserve data..." -"$BIN_DIR"/mount-squash-image "$DIR" 1>/dev/null 2>/dev/null +"$BIN_DIR/mount-squash-image" "$DIR" 1>/dev/null 2>/dev/null -echo "Destroying SquashFS image and restoring data to normal directory..." +echo "Destroying image and restoring data..." -TEMP_DIR=$(mktemp -d) -# Copy data out (preserving all attributes) -sudo rsync -a "$DIR/" "$TEMP_DIR/" +# Atomic copy-out +TEMP_DIR=$(mktemp -d /tmp/squash-dest.XXXXXXXX) +sudo rsync -aX "$DIR/" "$TEMP_DIR/" || { echo "Error: Failed to copy data."; exit 1; } -# Unmount the overlay and squashfs -echo "Unmounting..." -"$BIN_DIR"/umount-squash-image "$DIR" +# Unmount +"$BIN_DIR/umount-squash-image" "$DIR" -# Restore data to original location -echo "Restoring files..." +# Restore original directory structure +OVERLAY_ROOT="$(dirname "$DIR")/.squashfs/$DIR_SHORT" sudo rm -rf "$DIR" sudo mv "$TEMP_DIR" "$DIR" -# Cleanup hidden image and overlay files -OVERLAY_ROOT=$(readlink -f "${DIR}/..")/.squashfs/${DIR_SHORT} -echo "Cleaning up ${OVERLAY_ROOT}..." -sudo rm -rf "${OVERLAY_ROOT}.img" -sudo rm -rf "${OVERLAY_ROOT}" - -echo "Success: SquashFS image destroyed and data restored to $DIR." -exit 0 +# Final cleanup +sudo rm -rf "$OVERLAY_ROOT" "${OVERLAY_ROOT}.img" +echo "Success: SquashFS image destroyed and data restored to \"$DIR\"." diff --git a/editx b/editx index 839dfd2..685a977 100755 --- a/editx +++ b/editx @@ -16,15 +16,12 @@ then ched "EDITOR env variable not set! You will have to set it yourself in the next screen." fi -exists=$(file "$1" >/dev/null && echo $?) +[[ -e "$1" ]] && existed=1 || existed=0 touch "$1" -chmod +x "$1" -$EDITOR "$1" -wait $! -size=$(du "$1" | cut -f 1) +chmod +x "$1" +daisy_editor "$1" -if [[ $size -eq 0 && $exists -ne 0 ]]; -then - rm -rf "$1" +if [[ ! -s "$1" && $existed -eq 0 ]]; then + rm -f "$1" fi diff --git a/make-squash-image b/make-squash-image index b6111e9..17e0099 100755 --- a/make-squash-image +++ b/make-squash-image @@ -1,101 +1,78 @@ #!/bin/bash # Lackadaisical squashfs tools # Allows you to create a modifiable squashfs-backed image for a folder + BIN_DIR=$(dirname "$(readlink -f "$0")") -DIR=$1 +DIR="$1" if [[ -z "$DIR" ]]; then echo "Usage: $0 " exit 1 fi -test -d "$DIR" -if [[ $? -ne 0 ]]; then - echo Directory "$DIR" does not exist! - exit -1 +if [[ ! -d "$DIR" ]]; then + echo "Error: Directory \"$DIR\" does not exist!" + exit 1 fi -echo "You need support for FUSE, SquashFS, and OverlayFS." -echo "Checking your system for these requirements..." +echo "Checking system requirements (FUSE, SquashFS, OverlayFS)..." for fs in fuse squashfs overlay; do if ! grep -q "$fs" /proc/filesystems; then - echo "Loading $fs kernel module..." - if ! sudo modprobe "$fs"; then - echo "Error: $fs is not supported and could not be loaded." - exit 1 - fi + echo "Attempting to load $fs module..." + sudo modprobe "$fs" || { echo "Error: $fs is not supported."; exit 1; } fi done -DIRSIZE=$(du -s "$DIR") - DIR=$(readlink -f "$DIR") DIR_SHORT=$(basename "$DIR") -echo Compressing "$DIR" +DIRSIZE=$(du -sh "$DIR" | cut -f1) -# Make basic dirs -mkdir -p "${DIR}" -mkdir -p "${DIR_SHORT}" - -OVERLAY_ROOT=$(readlink -f "${DIR}/..")/.squashfs/${DIR_SHORT} -OVERLAY_UPPER=${OVERLAY_ROOT}/upper -OVERLAY_LOWER=${OVERLAY_ROOT}/lower -OVERLAY_WORK=${OVERLAY_ROOT}/work -OVERLAY_TARG=$DIR +OVERLAY_ROOT="$(dirname "$DIR")/.squashfs/$DIR_SHORT" +OVERLAY_UPPER="$OVERLAY_ROOT/upper" +OVERLAY_LOWER="$OVERLAY_ROOT/lower" +OVERLAY_WORK="$OVERLAY_ROOT/work" +OVERLAY_TARG="$DIR" RECREATE=false -echo "Overlay information" -echo "=========================================================================" -echo "> DIR = \"$DIR\"" -echo "> DIR_SHORT = \"$DIR_SHORT\"" -echo "> OVERLAY_ROOT = \"$OVERLAY_ROOT\"" -echo "> OVERLAY_UPPER = \"$OVERLAY_UPPER\"" -echo "> OVERLAY_LOWER= \"$OVERLAY_LOWER\"" -echo "> OVERLAY_WORK = \"$OVERLAY_WORK\"" -echo "> OVERLAY_TARG = \"$OVERLAY_TARG\"" -echo "=========================================================================" - mkdir -p "$OVERLAY_ROOT" -# Check for existing image -if test -f "${OVERLAY_ROOT}.img"; then - echo "We already have an existing image, updating it..." - mount-squash-image "$DIR" - DIRSIZE=$(du -s "$DIR") +if [[ -f "${OVERLAY_ROOT}.img" ]]; then + echo "Existing image found, updating..." + "$BIN_DIR/mount-squash-image" "$DIR" + DIRSIZE=$(du -sh "$DIR" | cut -f1) RECREATE=true fi -sudo mksquashfs "$DIR" "${OVERLAY_ROOT}.img.1" -noappend -comp xz +echo "Compressing \"$DIR\"..." +sudo mksquashfs "$DIR" "${OVERLAY_ROOT}.img.1" -noappend -comp xz || exit 1 -if [ $RECREATE == true ]; then - echo "Copy created, now unmount and swap everything." - umount-squash-image "$DIR" - sudo rm -rf "$OVERLAY_UPPER" - sudo rm -rf "$OVERLAY_LOWER" - sudo rm -rf "$OVERLAY_WORK" - rm -rf "${OVERLAY_ROOT}.img" +if [[ "$RECREATE" == "true" ]]; then + echo "Cleaning up old layers..." + "$BIN_DIR/umount-squash-image" "$DIR" + # Safety check before destructive RM + if [[ -n "$OVERLAY_ROOT" && "$OVERLAY_ROOT" != "/" ]]; then + sudo rm -rf "$OVERLAY_UPPER" "$OVERLAY_LOWER" "$OVERLAY_WORK" + rm -f "${OVERLAY_ROOT}.img" + fi fi mv "${OVERLAY_ROOT}.img.1" "${OVERLAY_ROOT}.img" -# Make the dirs for the overlay -mkdir -p "$OVERLAY_UPPER" -mkdir -p "$OVERLAY_LOWER" -mkdir -p "$OVERLAY_WORK" -mkdir -p "$OVERLAY_TARG" +# Prepare overlay structure +mkdir -p "$OVERLAY_UPPER" "$OVERLAY_LOWER" "$OVERLAY_WORK" "$OVERLAY_TARG" -# Reset dir +# Reset original directory to mountpoint sudo rm -rf "$DIR" mkdir -p "$DIR" -touch "${DIR}/.needs_mount" +touch "$DIR/.needs_mount" -echo "=========================================================================" -echo "Storage data:" -echo "Uncompressed: $DIRSIZE" -echo " Compressed: $(du -s ${OVERLAY_ROOT}.img)" -echo "=========================================================================" +echo "-------------------------------------------------------------------------" +echo "Storage Stats:" +echo " Original size: $DIRSIZE" +echo " Compressed: $(du -sh "${OVERLAY_ROOT}.img" | cut -f1)" +echo "-------------------------------------------------------------------------" -# Service content embedded +# SystemD Service Setup SERVICE_CONTENT="[Unit] Description=SquashFS Mount for %I After=local-fs.target @@ -113,7 +90,7 @@ echo "$SERVICE_CONTENT" | sudo tee /etc/systemd/system/squash-mount@.service > / sudo systemctl daemon-reload ESC_PATH=$(systemd-escape -p "$DIR") -SERVICE_NAME="squash-mount@-$ESC_PATH" +SERVICE_NAME="squash-mount@$ESC_PATH.service" read -p "Do you want to auto-enable the mount service ($SERVICE_NAME)? [y/N] " yn case $yn in diff --git a/mount-squash-image b/mount-squash-image index ee46f3d..2c0fac9 100755 --- a/mount-squash-image +++ b/mount-squash-image @@ -1,38 +1,46 @@ #!/bin/bash -DIR="$@" +# Lackadaisical squashfs tools - Mount + +DIR="$1" +if [[ -z "$DIR" ]]; then + echo "Usage: $0 " + exit 1 +fi + DIR=$(readlink -f "$DIR") DIR_SHORT=$(basename "$DIR") BIN_DIR=$(dirname "$(readlink -f "$0")") -mkdir -p "$DIR" -OVERLAY_ROOT=$(readlink -f "${DIR}/..")/.squashfs/${DIR_SHORT} -OVERLAY_UPPER=${OVERLAY_ROOT}/upper -OVERLAY_LOWER=${OVERLAY_ROOT}/lower -OVERLAY_WORK=${OVERLAY_ROOT}/work -OVERLAY_TARG=$DIR +OVERLAY_ROOT="$(dirname "$DIR")/.squashfs/$DIR_SHORT" +OVERLAY_UPPER="$OVERLAY_ROOT/upper" +OVERLAY_LOWER="$OVERLAY_ROOT/lower" +OVERLAY_WORK="$OVERLAY_ROOT/work" +OVERLAY_TARG="$DIR" -if [ ! -f "${OVERLAY_ROOT}.img" ]; then - echo "Error: SquashFS image ${OVERLAY_ROOT}.img not found." >&2 +if [[ ! -f "${OVERLAY_ROOT}.img" ]]; then + echo "Error: SquashFS image \"${OVERLAY_ROOT}.img\" not found." >&2 exit 1 fi -"$BIN_DIR"/umount-squash-image "$@" 1>/dev/null 2>/dev/null +# Unmount existing first +"$BIN_DIR/umount-squash-image" "$DIR" 2>/dev/null +# Mount lower squashfs image via loopback +mkdir -p "$OVERLAY_LOWER" sudo mount "${OVERLAY_ROOT}.img" "$OVERLAY_LOWER" -t squashfs -o loop -EXIT_CODE=$? -if [ $EXIT_CODE -ne 0 ]; then - echo "Error: Failed to mount squashfs image (exit code $EXIT_CODE)." >&2 - exit $EXIT_CODE +if [[ $? -ne 0 ]]; then + echo "Error: Failed to mount squashfs image." >&2 + exit 1 fi -sudo mount -t overlay -o lowerdir="$OVERLAY_LOWER",upperdir="$OVERLAY_UPPER",workdir="$OVERLAY_WORK" none "$OVERLAY_TARG" -EXIT_CODE=$? -if [ $EXIT_CODE -ne 0 ]; then - echo "Error: Failed to mount overlay (exit code $EXIT_CODE)." >&2 - # Cleanup: unmount the lower squashfs if overlay mount fails +# Mount overlay +sudo mount -t overlay none "$OVERLAY_TARG" \ + -o lowerdir="$OVERLAY_LOWER",upperdir="$OVERLAY_UPPER",workdir="$OVERLAY_WORK" + +if [[ $? -ne 0 ]]; then + echo "Error: Failed to mount overlay." >&2 sudo umount "$OVERLAY_LOWER" 2>/dev/null - exit $EXIT_CODE + exit 1 fi echo "SquashFS filesystem is mounted and ready." -exit 0 diff --git a/own b/own index a04be63..6db1b49 100755 --- a/own +++ b/own @@ -5,13 +5,19 @@ LD_INTERNAL=1 . $(dirname $(realpath $0))/daisy.source -if [[ $@ == '' ]]; -then - echo "$LD_BIN: Used to quickly take ownership of files/folders." - echo "Requires sudo. If sudo is not installed, this tool will fai." +if [[ -z "$1" ]]; then + echo "$LD_BIN: Quickly take ownership of files/folders." echo "Usage: $LD_BIN " - echo "Means: chown -R : " + echo "Requires sudo. If sudo is not installed, this tool will fail." exit 2 fi -sudo chown -R $(whoami):$(whoami) "$@" 1>/dev/null 2>/dev/null +# Ensure all arguments exist before attempting chown +for target in "$@"; do + if [[ ! -e "$target" ]]; then + echo "Error: \"$target\" does not exist." + exit 1 + fi +done + +sudo chown -R "$(whoami):$(whoami)" "$@" diff --git a/sshp b/sshp index eeedc7e..8f5c5c5 100755 --- a/sshp +++ b/sshp @@ -1,95 +1,58 @@ #!/usr/bin/env bash +# sshp ("SSH PLUS") from lackadaisical mounts=() ssh_args=() remote_port=$((10000 + RANDOM % 10000)) local_user=$(whoami) -usage() -{ - echo "sshp (\"SSH PLUS\") from lackadaisical." - echo "Accepts all standard SSH options (see man ssh)." - echo "Additionally, supports SSHFS mounts using a Docker-style syntax (-m)." - echo "Usage: sshp -m : [user@]host [ssh_options]" - echo "Example: sshp -m /home/juli:/home/juli juli@juli.pyon" - exit 1 +usage() { + echo "Usage: sshp -m : [user@]host [ssh_options]" + exit 1 } while [[ $# -gt 0 ]]; do - case "$1" in - -m) - if [[ -z "$2" ]] - then - echo "Error: -m requires argument" - exit 1 - fi - mounts+=("$2") - shift 2 - ;; - *) - ssh_args+=("$1") - shift - ;; - esac + case "$1" in + -m) + if [[ -z "$2" ]]; then echo "Error: -m requires argument"; exit 1; fi + mounts+=("$2") + shift 2 + ;; + *) + ssh_args+=("$1") + shift + ;; + esac done -if [[ ${#ssh_args[@]} -eq 0 ]] -then - usage -fi +[[ ${#ssh_args[@]} -eq 0 ]] && usage mount_logic="" unmount_logic="" for map in "${mounts[@]}"; do - local_path="${map%%:*}" - remote_path="${map##*:}" + local_path="${map%%:*}" + remote_path="${map##*:}" + [[ "$local_path" != /* ]] && local_path="$PWD/$local_path" - if [[ "$local_path" != /* ]] - then - local_path="$PWD/$local_path" - fi + mount_logic+=" + echo '>> Preparing mount: ${remote_path}'; + mkdir -p \"${remote_path}\" 2>/dev/null || sudo mkdir -p \"${remote_path}\" - mount_logic+=" - echo '>> Preparing mount: ${remote_path}'; - if ! mkdir -p \"${remote_path}\" 2>/dev/null - then - sudo mkdir -p \"${remote_path}\" - fi - - if ! sshfs -p ${remote_port} -o StrictHostKeyChecking=no,idmap=user ${local_user}@localhost:\"${local_path}\" \"${remote_path}\" 2>/dev/null - then - echo ' (User mount failed, attempting escalation...)'; - if ! sudo sshfs -p ${remote_port} -o StrictHostKeyChecking=no,idmap=user,allow_other ${local_user}@localhost:\"${local_path}\" \"${remote_path}\" - then - echo ' ! Mount failed completely. Check permissions or keys.'; - fi - else - echo ' > Mounted successfully.'; - fi - " - - unmount_logic+=" - fusermount -u -z \"${remote_path}\" 2>/dev/null || sudo fusermount -u -z \"${remote_path}\" 2>/dev/null; - " + sshfs -p ${remote_port} -o StrictHostKeyChecking=no,idmap=user ${local_user}@localhost:\"${local_path}\" \"${remote_path}\" 2>/dev/null || \ + sudo sshfs -p ${remote_port} -o StrictHostKeyChecking=no,idmap=user,allow_other ${local_user}@localhost:\"${local_path}\" \"${remote_path}\" + " + unmount_logic+="fusermount -u -z \"${remote_path}\" 2>/dev/null || sudo fusermount -u -z \"${remote_path}\" 2>/dev/null;" done remote_script=" - if ! command -v sshfs >/dev/null 2>&1 - then - echo 'WARNING: \"sshfs\" not found on remote host.' - echo '>> Skipping mounts, proceeding with shell only...'; - echo '----------------------------------------------'; - else - ${mount_logic} - fi - - ${SHELL:-bash}; - - if command -v sshfs >/dev/null 2>&1 - then + if ! command -v sshfs >/dev/null 2>&1; then + echo 'WARNING: \"sshfs\" not found on remote host.' + else + ${mount_logic} + fi + \${SHELL:-bash}; ${unmount_logic} - fi " ssh -t -R ${remote_port}:localhost:22 "${ssh_args[@]}" "$remote_script" diff --git a/umount-squash-image b/umount-squash-image index 58f36b1..18fd764 100755 --- a/umount-squash-image +++ b/umount-squash-image @@ -1,16 +1,27 @@ #!/bin/bash -DIR="$@" +# Lackadaisical squashfs tools - Unmount + +DIR="$1" +if [[ -z "$DIR" ]]; then + echo "Usage: $0 " + exit 1 +fi + DIR=$(readlink -f "$DIR") DIR_SHORT=$(basename "$DIR") -OVERLAY_ROOT=$(readlink -f "${DIR}/..")/.squashfs/${DIR_SHORT} -OVERLAY_UPPER=${OVERLAY_ROOT}/upper -OVERLAY_LOWER=${OVERLAY_ROOT}/lower -OVERLAY_WORK=${OVERLAY_ROOT}/work -OVERLAY_TARG=$DIR +OVERLAY_ROOT="$(dirname "$DIR")/.squashfs/$DIR_SHORT" +OVERLAY_LOWER="$OVERLAY_ROOT/lower" +OVERLAY_TARG="$DIR" -sudo umount -R "$OVERLAY_TARG" 2>/dev/null -sudo umount -R "$OVERLAY_LOWER" 2>/dev/null +# Forcefully unmount both layers recursively +sudo umount -l -R "$OVERLAY_TARG" 2>/dev/null +sudo umount -l -R "$OVERLAY_LOWER" 2>/dev/null + +if mountpoint -q "$OVERLAY_TARG" || mountpoint -q "$OVERLAY_LOWER"; then + echo "Warning: Filesystem is still mounted. Check for open processes." + exit 1 +fi echo "SquashFS filesystem has been unmounted." exit 0 diff --git a/what b/what index b4a470d..707e0ce 100755 --- a/what +++ b/what @@ -26,7 +26,10 @@ LD_INTERNAL=1 . $(dirname $(realpath $0))/daisy.source -pwd=/ -all_bins=$(cd / && echo $PATH | sed 's/[:]/ /g' | xargs ls -A | grep -v ":" | sort | uniq) -output=$(printf '%s\n' "-n" $all_bins | grep -i "$1") -echo "$output" +all_bins=$(compgen -c | sort -u) + +if [[ -n "$1" ]]; then + echo "$all_bins" | grep -i "$1" +else + echo "$all_bins" +fi From 10d81b7e5b3304e4a16449b6f7319d7359fc0454 Mon Sep 17 00:00:00 2001 From: Sam Hardeman Date: Sun, 15 Mar 2026 18:52:13 +0100 Subject: [PATCH 4/5] Added cdz check parameter for use in multicd (normal cd, squash cd and cdz). Consolidated all squash functionality into a binary (squasher) Updated the 'cd' alias (multicd). Added -e to ldrc (to edit the user scripts) --- README.md | 14 ++-- cdz | 98 ++++++++++++++++++++---- daisy.source | 31 ++++++-- destroy-squash-image | 50 ------------- make-squash-image | 118 ----------------------------- mount-squash-image | 46 ------------ squasher | 175 +++++++++++++++++++++++++++++++++++++++++++ umount-squash-image | 27 ------- 8 files changed, 288 insertions(+), 271 deletions(-) delete mode 100755 destroy-squash-image delete mode 100755 make-squash-image delete mode 100755 mount-squash-image create mode 100755 squasher delete mode 100755 umount-squash-image diff --git a/README.md b/README.md index ecc2e66..125c122 100755 --- a/README.md +++ b/README.md @@ -45,11 +45,13 @@ These are the included binaries and utilities: NO_ARCHIVEMOUNT=1. The standard script supports zip, tarballs, and rar archives. We recommend relying on archivemount` if you have it installed. - - SquashFS tools (mount/umount/make/destroy-squash-image): + Use "--check" to only check if a file is an archive. + It returns 0 if it is, 1 otherwise. + - SquashFS tools (squasher): These convenient set of tools allow you to easily create XZ-compressed SquashFS images from existing folders to save disk space. The resulting folder is still writable since it is - mounted using an 'overlay' system. You can use 'make-squash-image' + mounted using an 'overlay' system. You can use 'squasher make' to compresses an existing folder. These folders are automatically mounted when you use 'cd' to navigate to them (via an alias). When using 'make-squash-image' on an already mounted folder, it will @@ -57,12 +59,12 @@ These are the included binaries and utilities: SystemD service to auto-mount, however, you cam easily add auto-mount as a cron job. Here is a list of tools, they all take the same folder argument: - > make-squash-image: Converts the folder into a hidden image + > squasher make: Converts the folder into a hidden image on the same disk as the folder. - > mount-squash-image: Sets up a mount for the XZ image alongside + > squasher mount: Sets up a mount for the XZ image alongside directories for changes - > umount-squash-image: Self-explanatory. - > destroy-squash-image: Extracts the image and essentially reverts + > squasher umount: Self-explanatory. + > squasher destroy: Extracts the image and essentially reverts everything. File changes are kept, however. - editx: Uses your standard CLI editor to create/modify a diff --git a/cdz b/cdz index 94e5e4c..3b8d15a 100755 --- a/cdz +++ b/cdz @@ -1,57 +1,122 @@ #!/usr/bin/env bash -# Usage: cdz +# Usage: cdz [--check] -if [[ $LD_INTERNAL -ne 1 ]]; then +if [[ $LD_INTERNAL -ne 1 ]] +then LD_INTERNAL=1 . $(dirname $(realpath $0))/daisy.source fi +is_archive() +{ + local file="$1" + local mime_type=$(file --mime-type -b "$file") + + if [[ $mime_type == *"tar"* || $file == *.tar* || $mime_type == "application/zip" || $mime_type == "application/x-rar" ]] + then + return 0 + fi + + if command -v archivemount >/dev/null 2>&1 + then + local test_dir=$(mktemp -d /tmp/cdz_check.XXXXXXXX) + local result=1 + if archivemount -o readonly "$file" "$test_dir" >/dev/null 2>&1 + then + result=0 + umount "$test_dir" + fi + rmdir "$test_dir" + return $result + fi + + return 1 +} + +check_mode=0 +if [[ "$1" == "--check" ]] +then + check_mode=1 + shift +fi + target=$1 -if [[ -z "$target" ]]; then - echo "No target specified." +if [[ -z "$target" ]] +then + if [[ $check_mode -eq 0 ]] + then + echo "No target specified." + fi exit 1 fi -if [[ ! -f "$target" ]]; then - echo "File not found: \"$target\"" +if [[ ! -f "$target" ]] +then + if [[ $check_mode -eq 0 ]] + then + echo "File not found: \"$target\"" + fi exit 2 fi target_abs=$(realpath "$target") name=$(basename "$target") +if is_archive "$target_abs" +then + if [[ $check_mode -eq 1 ]] + then + exit 0 + fi +else + if [[ $check_mode -eq 1 ]] + then + exit 1 + fi + mime_type=$(file --mime-type -b "$target_abs") + echo "Unsupported archive type: $mime_type" + exit 1 +fi + +# Proceed with extraction has_archivemount=$(command -v archivemount >/dev/null 2>&1; echo $?) -if [[ $has_archivemount -eq 0 && $NO_ARCHIVEMOUNT -ne 1 ]]; then +if [[ $has_archivemount -eq 0 && $NO_ARCHIVEMOUNT -ne 1 ]] +then use_mounter=1 else use_mounter=0 fi -mime_type=$(file --mime-type -b "$target") - +mime_type=$(file --mime-type -b "$target_abs") comm1=(:) comm2=(echo "Unsupported archive type: $mime_type") comm3=(:) comm4=(:) comm5=(:) -if [[ $use_mounter -eq 1 ]]; then +if [[ $use_mounter -eq 1 ]] +then echo "Using archivemount..." comm2=(archivemount -o allow_root,use_ino "$target_abs") comm4=(cd ..) comm5=(umount) -elif [[ $mime_type == *"tar"* || $target == *.tar* ]]; then +elif [[ $mime_type == *"tar"* || $target_abs == *.tar* ]] +then comm2=(tar xvf "$target_abs" -C) -elif [[ $mime_type == "application/zip" ]]; then - if command -v unzip >/dev/null 2>&1; then +elif [[ $mime_type == "application/zip" ]] +then + if command -v unzip >/dev/null 2>&1 + then comm2=(unzip -q "$target_abs" -d) else comm1=(echo "unzip is missing") comm3=(exit 1) fi -elif [[ $mime_type == "application/x-rar" ]]; then - if command -v unrar >/dev/null 2>&1; then +elif [[ $mime_type == "application/x-rar" ]] +then + if command -v unrar >/dev/null 2>&1 + then # unrar needs the directory as the last argument comm2=(unrar x -idq "$target_abs") else @@ -63,7 +128,8 @@ fi dir=$(mktemp -d /tmp/extracted.XXXXXXXX) "${comm1[@]}" -if [[ ${comm2[0]} == "unrar" ]]; then +if [[ ${comm2[0]} == "unrar" ]] +then "${comm2[@]}" "$dir" else "${comm2[@]}" "$dir" diff --git a/daisy.source b/daisy.source index 97a43c6..915ab1b 100755 --- a/daisy.source +++ b/daisy.source @@ -80,10 +80,12 @@ LD_EDITORFILE="$LD_CONFIG_FOLDER/editor.src" LD_ESOURCEFILE="$LD_CONFIG_FOLDER/extra.src" touch $LD_ALIASFILE touch $LD_EDITORFILE +touch $LD_ESOURCEFILE ld_dbg echo "Sourced config contents:" ld_dbg cat $LD_ALIASFILE ld_dbg cat $LD_EDITORFILE +ld_dbg cat $LD_ESOURCEFILE # Source everything in the config folder function _daisy_source_configs @@ -132,28 +134,37 @@ function daisy_wait_for_editor done } -function mountcd +function multicd { cdpath="$@" - if [[ $cdpath == '' ]]; + if [[ $cdpath == '' ]] then \cd + return fi - if [[ -f "$cdpath/.needs_mount" ]]; + + if cdz --check "$cdpath" >/dev/null 2>&1 then - mount-squash-image "$cdpath" + cdz "$cdpath" + return + fi + + if [[ -f "$cdpath/.needs_mount" ]] + then + squasher mount "$cdpath" fi \cd "$cdpath" } -alias cd=mountcd +alias cd=multicd # Undocumented but internally used function daisy_editor { editor=${LD_EDITOR:-$EDITOR}; ld_dbg echo Opening $editor to edit file: $1 - $editor "$1" + $editor "$1" + sleep 1 daisy_wait_for_editor $editor "$1" } @@ -315,8 +326,12 @@ function daisy_reload function ldrc { - daisy_editor "$LD_SOURCE_FILE" - LD_INTERNAL=0 source "$LD_SOURCE_FILE" + ARG=$1 + SOURCE="$LD_SOURCE_FILE" + [[ "$ARG" == "-e" ]] && SOURCE="$LD_ESOURCEFILE" + + daisy_editor "$SOURCE" + LD_INTERNAL=0 source "$SOURCE" } enc_is_folder=0 diff --git a/destroy-squash-image b/destroy-squash-image deleted file mode 100755 index 6eef52a..0000000 --- a/destroy-squash-image +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# Lackadaisical squashfs tools - Destroy - -DIR="$1" -if [[ -z "$DIR" ]]; then - echo "Usage: $0 " - exit 1 -fi - -DIR=$(readlink -f "$DIR") -DIR_SHORT=$(basename "$DIR") -BIN_DIR=$(dirname "$(readlink -f "$0")") - -if [[ ! -f "$DIR/.needs_mount" ]]; then - if ! mountpoint -q "$DIR"; then - echo "Error: $DIR is not a SquashFS directory." - exit 1 - fi -fi - -# Stop systemd service -ESC_PATH=$(systemd-escape -p "$DIR") -SERVICE_NAME="squash-mount@$ESC_PATH.service" - -echo "Disabling service ($SERVICE_NAME)..." -sudo systemctl stop "$SERVICE_NAME" 2>/dev/null -sudo systemctl disable "$SERVICE_NAME" 2>/dev/null - -# Ensure it is mounted to copy merged data -echo "Ensuring image is mounted to preserve data..." -"$BIN_DIR/mount-squash-image" "$DIR" 1>/dev/null 2>/dev/null - -echo "Destroying image and restoring data..." - -# Atomic copy-out -TEMP_DIR=$(mktemp -d /tmp/squash-dest.XXXXXXXX) -sudo rsync -aX "$DIR/" "$TEMP_DIR/" || { echo "Error: Failed to copy data."; exit 1; } - -# Unmount -"$BIN_DIR/umount-squash-image" "$DIR" - -# Restore original directory structure -OVERLAY_ROOT="$(dirname "$DIR")/.squashfs/$DIR_SHORT" -sudo rm -rf "$DIR" -sudo mv "$TEMP_DIR" "$DIR" - -# Final cleanup -sudo rm -rf "$OVERLAY_ROOT" "${OVERLAY_ROOT}.img" - -echo "Success: SquashFS image destroyed and data restored to \"$DIR\"." diff --git a/make-squash-image b/make-squash-image deleted file mode 100755 index 17e0099..0000000 --- a/make-squash-image +++ /dev/null @@ -1,118 +0,0 @@ -#!/bin/bash -# Lackadaisical squashfs tools -# Allows you to create a modifiable squashfs-backed image for a folder - -BIN_DIR=$(dirname "$(readlink -f "$0")") -DIR="$1" - -if [[ -z "$DIR" ]]; then - echo "Usage: $0 " - exit 1 -fi - -if [[ ! -d "$DIR" ]]; then - echo "Error: Directory \"$DIR\" does not exist!" - exit 1 -fi - -echo "Checking system requirements (FUSE, SquashFS, OverlayFS)..." -for fs in fuse squashfs overlay; do - if ! grep -q "$fs" /proc/filesystems; then - echo "Attempting to load $fs module..." - sudo modprobe "$fs" || { echo "Error: $fs is not supported."; exit 1; } - fi -done - -DIR=$(readlink -f "$DIR") -DIR_SHORT=$(basename "$DIR") -DIRSIZE=$(du -sh "$DIR" | cut -f1) - -OVERLAY_ROOT="$(dirname "$DIR")/.squashfs/$DIR_SHORT" -OVERLAY_UPPER="$OVERLAY_ROOT/upper" -OVERLAY_LOWER="$OVERLAY_ROOT/lower" -OVERLAY_WORK="$OVERLAY_ROOT/work" -OVERLAY_TARG="$DIR" -RECREATE=false - -mkdir -p "$OVERLAY_ROOT" - -if [[ -f "${OVERLAY_ROOT}.img" ]]; then - echo "Existing image found, updating..." - "$BIN_DIR/mount-squash-image" "$DIR" - DIRSIZE=$(du -sh "$DIR" | cut -f1) - RECREATE=true -fi - -echo "Compressing \"$DIR\"..." -sudo mksquashfs "$DIR" "${OVERLAY_ROOT}.img.1" -noappend -comp xz || exit 1 - -if [[ "$RECREATE" == "true" ]]; then - echo "Cleaning up old layers..." - "$BIN_DIR/umount-squash-image" "$DIR" - # Safety check before destructive RM - if [[ -n "$OVERLAY_ROOT" && "$OVERLAY_ROOT" != "/" ]]; then - sudo rm -rf "$OVERLAY_UPPER" "$OVERLAY_LOWER" "$OVERLAY_WORK" - rm -f "${OVERLAY_ROOT}.img" - fi -fi - -mv "${OVERLAY_ROOT}.img.1" "${OVERLAY_ROOT}.img" - -# Prepare overlay structure -mkdir -p "$OVERLAY_UPPER" "$OVERLAY_LOWER" "$OVERLAY_WORK" "$OVERLAY_TARG" - -# Reset original directory to mountpoint -sudo rm -rf "$DIR" -mkdir -p "$DIR" -touch "$DIR/.needs_mount" - -echo "-------------------------------------------------------------------------" -echo "Storage Stats:" -echo " Original size: $DIRSIZE" -echo " Compressed: $(du -sh "${OVERLAY_ROOT}.img" | cut -f1)" -echo "-------------------------------------------------------------------------" - -# SystemD Service Setup -SERVICE_CONTENT="[Unit] -Description=SquashFS Mount for %I -After=local-fs.target - -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=${BIN_DIR}/mount-squash-image %I -ExecStop=${BIN_DIR}/umount-squash-image %I - -[Install] -WantedBy=multi-user.target" - -echo "$SERVICE_CONTENT" | sudo tee /etc/systemd/system/squash-mount@.service > /dev/null -sudo systemctl daemon-reload - -ESC_PATH=$(systemd-escape -p "$DIR") -SERVICE_NAME="squash-mount@$ESC_PATH.service" - -read -p "Do you want to auto-enable the mount service ($SERVICE_NAME)? [y/N] " yn -case $yn in - [Yy]* ) - echo "Enabling and starting service..." - sudo systemctl enable --now "$SERVICE_NAME" - sudo systemctl stop "$SERVICE_NAME" - sudo systemctl start "$SERVICE_NAME" - ;; - * ) - echo "To enable the service manually, run: sudo systemctl enable --now $SERVICE_NAME" - ;; -esac - -echo "" -echo "Your SquashFS-backed folder is ready for use." -echo "To mount it manually, either cd into it or use mount-squash-image." -echo "" -echo "Should you wish to update the contents of the image with your changes made" -echo "inside of the folder, simply run make-squash-image again on the same" -echo "folder to update the image." -echo -echo "To disable auto-mounting upon cd, please remove the" -echo ".needs_mount file to prevent that." -echo diff --git a/mount-squash-image b/mount-squash-image deleted file mode 100755 index 2c0fac9..0000000 --- a/mount-squash-image +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash -# Lackadaisical squashfs tools - Mount - -DIR="$1" -if [[ -z "$DIR" ]]; then - echo "Usage: $0 " - exit 1 -fi - -DIR=$(readlink -f "$DIR") -DIR_SHORT=$(basename "$DIR") -BIN_DIR=$(dirname "$(readlink -f "$0")") - -OVERLAY_ROOT="$(dirname "$DIR")/.squashfs/$DIR_SHORT" -OVERLAY_UPPER="$OVERLAY_ROOT/upper" -OVERLAY_LOWER="$OVERLAY_ROOT/lower" -OVERLAY_WORK="$OVERLAY_ROOT/work" -OVERLAY_TARG="$DIR" - -if [[ ! -f "${OVERLAY_ROOT}.img" ]]; then - echo "Error: SquashFS image \"${OVERLAY_ROOT}.img\" not found." >&2 - exit 1 -fi - -# Unmount existing first -"$BIN_DIR/umount-squash-image" "$DIR" 2>/dev/null - -# Mount lower squashfs image via loopback -mkdir -p "$OVERLAY_LOWER" -sudo mount "${OVERLAY_ROOT}.img" "$OVERLAY_LOWER" -t squashfs -o loop -if [[ $? -ne 0 ]]; then - echo "Error: Failed to mount squashfs image." >&2 - exit 1 -fi - -# Mount overlay -sudo mount -t overlay none "$OVERLAY_TARG" \ - -o lowerdir="$OVERLAY_LOWER",upperdir="$OVERLAY_UPPER",workdir="$OVERLAY_WORK" - -if [[ $? -ne 0 ]]; then - echo "Error: Failed to mount overlay." >&2 - sudo umount "$OVERLAY_LOWER" 2>/dev/null - exit 1 -fi - -echo "SquashFS filesystem is mounted and ready." diff --git a/squasher b/squasher new file mode 100755 index 0000000..3049be8 --- /dev/null +++ b/squasher @@ -0,0 +1,175 @@ +#!/bin/bash +# Lackadaisical squashfs tools - Squasher +# Combined utility for make, mount, umount, and destroy operations. + +BIN_DIR=$(dirname "$(readlink -f "$0")") +COMMAND="$1" +DIR="$2" + +usage() { + echo "Usage: $0 {make|mount|umount|destroy} " + exit 1 +} + +if [[ -z "$COMMAND" || -z "$DIR" ]]; then + usage +fi + +DIR=$(readlink -f "$DIR") +DIR_SHORT=$(basename "$DIR") +OVERLAY_ROOT="$(dirname "$DIR")/.squashfs/$DIR_SHORT" +OVERLAY_UPPER="$OVERLAY_ROOT/upper" +OVERLAY_LOWER="$OVERLAY_ROOT/lower" +OVERLAY_WORK="$OVERLAY_ROOT/work" +OVERLAY_TARG="$DIR" + +case "$COMMAND" in + make) + if [[ ! -d "$DIR" ]]; then + echo "Error: Directory \"$DIR\" does not exist!" + exit 1 + fi + + echo "Checking system requirements (FUSE, SquashFS, OverlayFS)..." + for fs in fuse squashfs overlay; do + if ! grep -q "$fs" /proc/filesystems; then + echo "Attempting to load $fs module..." + sudo modprobe "$fs" || { echo "Error: $fs is not supported."; exit 1; } + fi + done + + DIRSIZE=$(du -sh "$DIR" | cut -f1) + RECREATE=false + mkdir -p "$OVERLAY_ROOT" + + if [[ -f "${OVERLAY_ROOT}.img" ]]; then + echo "Existing image found, updating..." + "$0" mount "$DIR" + DIRSIZE=$(du -sh "$DIR" | cut -f1) + RECREATE=true + fi + + echo "Compressing \"$DIR\"..." + sudo mksquashfs "$DIR" "${OVERLAY_ROOT}.img.1" -noappend -comp xz || exit 1 + + if [[ "$RECREATE" == "true" ]]; then + echo "Cleaning up old layers..." + "$0" umount "$DIR" + if [[ -n "$OVERLAY_ROOT" && "$OVERLAY_ROOT" != "/" ]]; then + sudo rm -rf "$OVERLAY_UPPER" "$OVERLAY_LOWER" "$OVERLAY_WORK" + rm -f "${OVERLAY_ROOT}.img" + fi + fi + + mv "${OVERLAY_ROOT}.img.1" "${OVERLAY_ROOT}.img" + mkdir -p "$OVERLAY_UPPER" "$OVERLAY_LOWER" "$OVERLAY_WORK" "$OVERLAY_TARG" + + sudo rm -rf "$DIR" + mkdir -p "$DIR" + touch "$DIR/.needs_mount" + + echo "-------------------------------------------------------------------------" + echo "Storage Stats:" + echo " Original size: $DIRSIZE" + echo " Compressed: $(du -sh "${OVERLAY_ROOT}.img" | cut -f1)" + echo "-------------------------------------------------------------------------" + + SERVICE_CONTENT="[Unit] +Description=SquashFS Mount for %I +After=local-fs.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=${BIN_DIR}/squasher mount %I +ExecStop=${BIN_DIR}/squasher umount %I + +[Install] +WantedBy=multi-user.target" + + echo "$SERVICE_CONTENT" | sudo tee /etc/systemd/system/squash-mount@.service > /dev/null + sudo systemctl daemon-reload + + ESC_PATH=$(systemd-escape -p "$DIR") + SERVICE_NAME="squash-mount@$ESC_PATH.service" + + read -p "Enable auto-mount service ($SERVICE_NAME)? [y/N] " yn + if [[ "$yn" =~ ^[Yy]$ ]]; then + sudo systemctl enable --now "$SERVICE_NAME" + sudo systemctl stop "$SERVICE_NAME" + sudo systemctl start "$SERVICE_NAME" + else + echo "Manual mount command: sudo systemctl start $SERVICE_NAME" + fi + ;; + + mount) + if [[ ! -f "${OVERLAY_ROOT}.img" ]]; then + echo "Error: SquashFS image \"${OVERLAY_ROOT}.img\" not found." >&2 + exit 1 + fi + + "$0" umount "$DIR" 2>/dev/null + mkdir -p "$OVERLAY_LOWER" + sudo mount "${OVERLAY_ROOT}.img" "$OVERLAY_LOWER" -t squashfs -o loop + if [[ $? -ne 0 ]]; then + echo "Error: Failed to mount squashfs image." >&2 + exit 1 + fi + + sudo mount -t overlay none "$OVERLAY_TARG" \ + -o lowerdir="$OVERLAY_LOWER",upperdir="$OVERLAY_UPPER",workdir="$OVERLAY_WORK" + + if [[ $? -ne 0 ]]; then + echo "Error: Failed to mount overlay." >&2 + sudo umount "$OVERLAY_LOWER" 2>/dev/null + exit 1 + fi + echo "SquashFS filesystem is mounted and ready." + ;; + + umount) + sudo umount -l -R "$OVERLAY_TARG" 2>/dev/null + sudo umount -l -R "$OVERLAY_LOWER" 2>/dev/null + + if mountpoint -q "$OVERLAY_TARG" || mountpoint -q "$OVERLAY_LOWER"; then + echo "Warning: Filesystem is still mounted. Check for open processes." + exit 1 + fi + echo "SquashFS filesystem has been unmounted." + ;; + + destroy) + if [[ ! -f "$DIR/.needs_mount" ]]; then + if ! mountpoint -q "$DIR"; then + echo "Error: $DIR is not a SquashFS directory." + exit 1 + fi + fi + + ESC_PATH=$(systemd-escape -p "$DIR") + SERVICE_NAME="squash-mount@$ESC_PATH.service" + + echo "Disabling service ($SERVICE_NAME)..." + sudo systemctl stop "$SERVICE_NAME" 2>/dev/null + sudo systemctl disable "$SERVICE_NAME" 2>/dev/null + + echo "Ensuring image is mounted to preserve data..." + "$0" mount "$DIR" 1>/dev/null 2>/dev/null + + echo "Destroying image and restoring data..." + TEMP_DIR=$(mktemp -d /tmp/squash-dest.XXXXXXXX) + sudo rsync -aX "$DIR/" "$TEMP_DIR/" || { echo "Error: Failed to copy data."; exit 1; } + + "$0" umount "$DIR" + sudo rm -rf "$DIR" + sudo mv "$TEMP_DIR" "$DIR" + sudo rm -rf "$OVERLAY_ROOT" "${OVERLAY_ROOT}.img" + + echo "Success: SquashFS image destroyed and data restored to \"$DIR\"." + ;; + + *) + usage + ;; +esac diff --git a/umount-squash-image b/umount-squash-image deleted file mode 100755 index 18fd764..0000000 --- a/umount-squash-image +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Lackadaisical squashfs tools - Unmount - -DIR="$1" -if [[ -z "$DIR" ]]; then - echo "Usage: $0 " - exit 1 -fi - -DIR=$(readlink -f "$DIR") -DIR_SHORT=$(basename "$DIR") - -OVERLAY_ROOT="$(dirname "$DIR")/.squashfs/$DIR_SHORT" -OVERLAY_LOWER="$OVERLAY_ROOT/lower" -OVERLAY_TARG="$DIR" - -# Forcefully unmount both layers recursively -sudo umount -l -R "$OVERLAY_TARG" 2>/dev/null -sudo umount -l -R "$OVERLAY_LOWER" 2>/dev/null - -if mountpoint -q "$OVERLAY_TARG" || mountpoint -q "$OVERLAY_LOWER"; then - echo "Warning: Filesystem is still mounted. Check for open processes." - exit 1 -fi - -echo "SquashFS filesystem has been unmounted." -exit 0 From 74ed7582f93e3dd0d813b4b6826a104cc5813cad Mon Sep 17 00:00:00 2001 From: Sam Hardeman Date: Sun, 15 Mar 2026 19:11:37 +0100 Subject: [PATCH 5/5] Small cosmetical changes --- README.md | 4 ++-- squasher | 22 +++++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 125c122..aa0dc3d 100755 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ These are the included binaries and utilities: archivemount` if you have it installed. Use "--check" to only check if a file is an archive. It returns 0 if it is, 1 otherwise. - - SquashFS tools (squasher): + - squasher: These convenient set of tools allow you to easily create XZ-compressed SquashFS images from existing folders to save disk space. The resulting folder is still writable since it is @@ -74,7 +74,7 @@ These are the included binaries and utilities: not exist yet. When the file appears on disk, the tool quits and simply returns the filename. This can be used in personal workflows to stall a longer - command that relies on the existence of said file.= + command that relies on the existence of said file. - agenda: Sets up a folder that is backed by a date-based tree directory structure. Requires an argument for the name diff --git a/squasher b/squasher index 3049be8..213181d 100755 --- a/squasher +++ b/squasher @@ -75,17 +75,17 @@ case "$COMMAND" in echo "-------------------------------------------------------------------------" SERVICE_CONTENT="[Unit] -Description=SquashFS Mount for %I -After=local-fs.target + Description=SquashFS Mount for %I + After=local-fs.target -[Service] -Type=oneshot -RemainAfterExit=yes -ExecStart=${BIN_DIR}/squasher mount %I -ExecStop=${BIN_DIR}/squasher umount %I + [Service] + Type=oneshot + RemainAfterExit=yes + ExecStart=${BIN_DIR}/squasher mount %I + ExecStop=${BIN_DIR}/squasher umount %I -[Install] -WantedBy=multi-user.target" + [Install] + WantedBy=multi-user.target" echo "$SERVICE_CONTENT" | sudo tee /etc/systemd/system/squash-mount@.service > /dev/null sudo systemctl daemon-reload @@ -94,6 +94,7 @@ WantedBy=multi-user.target" SERVICE_NAME="squash-mount@$ESC_PATH.service" read -p "Enable auto-mount service ($SERVICE_NAME)? [y/N] " yn + if [[ "$yn" =~ ^[Yy]$ ]]; then sudo systemctl enable --now "$SERVICE_NAME" sudo systemctl stop "$SERVICE_NAME" @@ -112,6 +113,7 @@ WantedBy=multi-user.target" "$0" umount "$DIR" 2>/dev/null mkdir -p "$OVERLAY_LOWER" sudo mount "${OVERLAY_ROOT}.img" "$OVERLAY_LOWER" -t squashfs -o loop + if [[ $? -ne 0 ]]; then echo "Error: Failed to mount squashfs image." >&2 exit 1 @@ -125,6 +127,7 @@ WantedBy=multi-user.target" sudo umount "$OVERLAY_LOWER" 2>/dev/null exit 1 fi + echo "SquashFS filesystem is mounted and ready." ;; @@ -136,6 +139,7 @@ WantedBy=multi-user.target" echo "Warning: Filesystem is still mounted. Check for open processes." exit 1 fi + echo "SquashFS filesystem has been unmounted." ;;