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)
This commit is contained in:
Sam Hardeman 2026-03-15 18:52:13 +01:00
parent 83324dd0f7
commit 10d81b7e5b
8 changed files with 288 additions and 271 deletions

View file

@ -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

98
cdz
View file

@ -1,57 +1,122 @@
#!/usr/bin/env bash
# Usage: cdz <archive>
# Usage: cdz [--check] <archive>
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"

View file

@ -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,21 +134,29 @@ 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
@ -154,6 +164,7 @@ function daisy_editor
editor=${LD_EDITOR:-$EDITOR};
ld_dbg echo Opening $editor to edit file: $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

View file

@ -1,50 +0,0 @@
#!/bin/bash
# Lackadaisical squashfs tools - Destroy
DIR="$1"
if [[ -z "$DIR" ]]; then
echo "Usage: $0 <directory>"
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\"."

View file

@ -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 <directory>"
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

View file

@ -1,46 +0,0 @@
#!/bin/bash
# Lackadaisical squashfs tools - Mount
DIR="$1"
if [[ -z "$DIR" ]]; then
echo "Usage: $0 <directory>"
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."

175
squasher Executable file
View file

@ -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} <directory>"
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

View file

@ -1,27 +0,0 @@
#!/bin/bash
# Lackadaisical squashfs tools - Unmount
DIR="$1"
if [[ -z "$DIR" ]]; then
echo "Usage: $0 <directory>"
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