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:
parent
83324dd0f7
commit
10d81b7e5b
8 changed files with 288 additions and 271 deletions
14
README.md
14
README.md
|
|
@ -45,11 +45,13 @@ These are the included binaries and utilities:
|
||||||
NO_ARCHIVEMOUNT=1. The standard script supports zip,
|
NO_ARCHIVEMOUNT=1. The standard script supports zip,
|
||||||
tarballs, and rar archives. We recommend relying on
|
tarballs, and rar archives. We recommend relying on
|
||||||
archivemount` if you have it installed.
|
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
|
These convenient set of tools allow you to easily create
|
||||||
XZ-compressed SquashFS images from existing folders to save
|
XZ-compressed SquashFS images from existing folders to save
|
||||||
disk space. The resulting folder is still writable since it is
|
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
|
to compresses an existing folder. These folders are automatically
|
||||||
mounted when you use 'cd' to navigate to them (via an alias).
|
mounted when you use 'cd' to navigate to them (via an alias).
|
||||||
When using 'make-squash-image' on an already mounted folder, it will
|
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
|
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
|
as a cron job. Here is a list of tools, they all take the same
|
||||||
folder argument:
|
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.
|
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
|
directories for changes
|
||||||
> umount-squash-image: Self-explanatory.
|
> squasher umount: Self-explanatory.
|
||||||
> destroy-squash-image: Extracts the image and essentially reverts
|
> squasher destroy: Extracts the image and essentially reverts
|
||||||
everything. File changes are kept, however.
|
everything. File changes are kept, however.
|
||||||
- editx:
|
- editx:
|
||||||
Uses your standard CLI editor to create/modify a
|
Uses your standard CLI editor to create/modify a
|
||||||
|
|
|
||||||
98
cdz
98
cdz
|
|
@ -1,57 +1,122 @@
|
||||||
#!/usr/bin/env bash
|
#!/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
|
LD_INTERNAL=1
|
||||||
. $(dirname $(realpath $0))/daisy.source
|
. $(dirname $(realpath $0))/daisy.source
|
||||||
fi
|
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
|
target=$1
|
||||||
if [[ -z "$target" ]]; then
|
if [[ -z "$target" ]]
|
||||||
echo "No target specified."
|
then
|
||||||
|
if [[ $check_mode -eq 0 ]]
|
||||||
|
then
|
||||||
|
echo "No target specified."
|
||||||
|
fi
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ! -f "$target" ]]; then
|
if [[ ! -f "$target" ]]
|
||||||
echo "File not found: \"$target\""
|
then
|
||||||
|
if [[ $check_mode -eq 0 ]]
|
||||||
|
then
|
||||||
|
echo "File not found: \"$target\""
|
||||||
|
fi
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
target_abs=$(realpath "$target")
|
target_abs=$(realpath "$target")
|
||||||
name=$(basename "$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 $?)
|
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
|
use_mounter=1
|
||||||
else
|
else
|
||||||
use_mounter=0
|
use_mounter=0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mime_type=$(file --mime-type -b "$target")
|
mime_type=$(file --mime-type -b "$target_abs")
|
||||||
|
|
||||||
comm1=(:)
|
comm1=(:)
|
||||||
comm2=(echo "Unsupported archive type: $mime_type")
|
comm2=(echo "Unsupported archive type: $mime_type")
|
||||||
comm3=(:)
|
comm3=(:)
|
||||||
comm4=(:)
|
comm4=(:)
|
||||||
comm5=(:)
|
comm5=(:)
|
||||||
|
|
||||||
if [[ $use_mounter -eq 1 ]]; then
|
if [[ $use_mounter -eq 1 ]]
|
||||||
|
then
|
||||||
echo "Using archivemount..."
|
echo "Using archivemount..."
|
||||||
comm2=(archivemount -o allow_root,use_ino "$target_abs")
|
comm2=(archivemount -o allow_root,use_ino "$target_abs")
|
||||||
comm4=(cd ..)
|
comm4=(cd ..)
|
||||||
comm5=(umount)
|
comm5=(umount)
|
||||||
elif [[ $mime_type == *"tar"* || $target == *.tar* ]]; then
|
elif [[ $mime_type == *"tar"* || $target_abs == *.tar* ]]
|
||||||
|
then
|
||||||
comm2=(tar xvf "$target_abs" -C)
|
comm2=(tar xvf "$target_abs" -C)
|
||||||
elif [[ $mime_type == "application/zip" ]]; then
|
elif [[ $mime_type == "application/zip" ]]
|
||||||
if command -v unzip >/dev/null 2>&1; then
|
then
|
||||||
|
if command -v unzip >/dev/null 2>&1
|
||||||
|
then
|
||||||
comm2=(unzip -q "$target_abs" -d)
|
comm2=(unzip -q "$target_abs" -d)
|
||||||
else
|
else
|
||||||
comm1=(echo "unzip is missing")
|
comm1=(echo "unzip is missing")
|
||||||
comm3=(exit 1)
|
comm3=(exit 1)
|
||||||
fi
|
fi
|
||||||
elif [[ $mime_type == "application/x-rar" ]]; then
|
elif [[ $mime_type == "application/x-rar" ]]
|
||||||
if command -v unrar >/dev/null 2>&1; then
|
then
|
||||||
|
if command -v unrar >/dev/null 2>&1
|
||||||
|
then
|
||||||
# unrar needs the directory as the last argument
|
# unrar needs the directory as the last argument
|
||||||
comm2=(unrar x -idq "$target_abs")
|
comm2=(unrar x -idq "$target_abs")
|
||||||
else
|
else
|
||||||
|
|
@ -63,7 +128,8 @@ fi
|
||||||
dir=$(mktemp -d /tmp/extracted.XXXXXXXX)
|
dir=$(mktemp -d /tmp/extracted.XXXXXXXX)
|
||||||
|
|
||||||
"${comm1[@]}"
|
"${comm1[@]}"
|
||||||
if [[ ${comm2[0]} == "unrar" ]]; then
|
if [[ ${comm2[0]} == "unrar" ]]
|
||||||
|
then
|
||||||
"${comm2[@]}" "$dir"
|
"${comm2[@]}" "$dir"
|
||||||
else
|
else
|
||||||
"${comm2[@]}" "$dir"
|
"${comm2[@]}" "$dir"
|
||||||
|
|
|
||||||
31
daisy.source
31
daisy.source
|
|
@ -80,10 +80,12 @@ LD_EDITORFILE="$LD_CONFIG_FOLDER/editor.src"
|
||||||
LD_ESOURCEFILE="$LD_CONFIG_FOLDER/extra.src"
|
LD_ESOURCEFILE="$LD_CONFIG_FOLDER/extra.src"
|
||||||
touch $LD_ALIASFILE
|
touch $LD_ALIASFILE
|
||||||
touch $LD_EDITORFILE
|
touch $LD_EDITORFILE
|
||||||
|
touch $LD_ESOURCEFILE
|
||||||
|
|
||||||
ld_dbg echo "Sourced config contents:"
|
ld_dbg echo "Sourced config contents:"
|
||||||
ld_dbg cat $LD_ALIASFILE
|
ld_dbg cat $LD_ALIASFILE
|
||||||
ld_dbg cat $LD_EDITORFILE
|
ld_dbg cat $LD_EDITORFILE
|
||||||
|
ld_dbg cat $LD_ESOURCEFILE
|
||||||
|
|
||||||
# Source everything in the config folder
|
# Source everything in the config folder
|
||||||
function _daisy_source_configs
|
function _daisy_source_configs
|
||||||
|
|
@ -132,28 +134,37 @@ function daisy_wait_for_editor
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
function mountcd
|
function multicd
|
||||||
{
|
{
|
||||||
cdpath="$@"
|
cdpath="$@"
|
||||||
if [[ $cdpath == '' ]];
|
if [[ $cdpath == '' ]]
|
||||||
then
|
then
|
||||||
\cd
|
\cd
|
||||||
|
return
|
||||||
fi
|
fi
|
||||||
if [[ -f "$cdpath/.needs_mount" ]];
|
|
||||||
|
if cdz --check "$cdpath" >/dev/null 2>&1
|
||||||
then
|
then
|
||||||
mount-squash-image "$cdpath"
|
cdz "$cdpath"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$cdpath/.needs_mount" ]]
|
||||||
|
then
|
||||||
|
squasher mount "$cdpath"
|
||||||
fi
|
fi
|
||||||
\cd "$cdpath"
|
\cd "$cdpath"
|
||||||
}
|
}
|
||||||
|
|
||||||
alias cd=mountcd
|
alias cd=multicd
|
||||||
|
|
||||||
# Undocumented but internally used
|
# Undocumented but internally used
|
||||||
function daisy_editor
|
function daisy_editor
|
||||||
{
|
{
|
||||||
editor=${LD_EDITOR:-$EDITOR};
|
editor=${LD_EDITOR:-$EDITOR};
|
||||||
ld_dbg echo Opening $editor to edit file: $1
|
ld_dbg echo Opening $editor to edit file: $1
|
||||||
$editor "$1"
|
$editor "$1"
|
||||||
|
sleep 1
|
||||||
daisy_wait_for_editor $editor "$1"
|
daisy_wait_for_editor $editor "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,8 +326,12 @@ function daisy_reload
|
||||||
|
|
||||||
function ldrc
|
function ldrc
|
||||||
{
|
{
|
||||||
daisy_editor "$LD_SOURCE_FILE"
|
ARG=$1
|
||||||
LD_INTERNAL=0 source "$LD_SOURCE_FILE"
|
SOURCE="$LD_SOURCE_FILE"
|
||||||
|
[[ "$ARG" == "-e" ]] && SOURCE="$LD_ESOURCEFILE"
|
||||||
|
|
||||||
|
daisy_editor "$SOURCE"
|
||||||
|
LD_INTERNAL=0 source "$SOURCE"
|
||||||
}
|
}
|
||||||
|
|
||||||
enc_is_folder=0
|
enc_is_folder=0
|
||||||
|
|
|
||||||
|
|
@ -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\"."
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
175
squasher
Executable 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
|
||||||
|
|
@ -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
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue