#!/bin/bash ################################################################################################################ # # # WARNING: This file is automatically generated # # DO NOT modify this file directly as it will be overwritten # # # # Ubuntu uBlue ComposeFS Alternative # # Generated on: 2025-07-10 22:47:36 # # # ################################################################################################################ set -euo pipefail # Ubuntu uBlue ComposeFS Alternative - Self-contained version # This script contains all components merged into a single file # Based on composefs design principles: https://github.com/containers/composefs # Version: 25.07.10 # Ubuntu uBlue ComposeFS Alternative # Content-addressable layered filesystem for Ubuntu # Source Ubuntu uBlue configuration (if available) if [[ -f "/usr/local/etc/particle-config.sh" ]]; then source "/usr/local/etc/particle-config.sh" log_info "Loaded Ubuntu uBlue configuration" "composefs-alternative" else # Define logging functions if not available log_info() { local message="$1" local script_name="${2:-composefs-alternative}" echo "[INFO] [$script_name] $message" } log_warning() { local message="$1" local script_name="${2:-composefs-alternative}" echo "[WARNING] [$script_name] $message" >&2 } log_error() { local message="$1" local script_name="${2:-composefs-alternative}" echo "[ERROR] [$script_name] $message" >&2 } log_debug() { local message="$1" local script_name="${2:-composefs-alternative}" echo "[DEBUG] [$script_name] $message" } log_success() { local message="$1" local script_name="${2:-composefs-alternative}" echo "[SUCCESS] [$script_name] $message" } log_warning "Ubuntu uBlue configuration not found, using defaults" "composefs-alternative" fi # ============================================================================ # Header and Shared Functions # ============================================================================ # --- END OF SCRIPTLET: 00-header.sh --- # ============================================================================ # Dependency Checking and Validation # ============================================================================ # Check dependencies for Ubuntu uBlue ComposeFS Alternative check_dependencies() { local deps=( "mount" # Filesystem mounting "umount" # Filesystem unmounting "findmnt" # Mount point detection "losetup" # Loop device management "sha256sum" # Hash generation "jq" # JSON processing "tar" # Archive operations "mksquashfs" # SquashFS creation "unsquashfs" # SquashFS extraction "nproc" # CPU core detection "du" # Disk usage "stat" # File status "modprobe" # Kernel module loading "mountpoint" # Mount point checking ) local missing=() for dep in "${deps[@]}"; do if ! command -v "$dep" &> /dev/null; then missing+=("$dep") fi done if [[ ${#missing[@]} -gt 0 ]]; then log_error "Missing required dependencies: ${missing[*]}" "composefs-alternative" log_error "Please install the missing packages (squashfs-tools, jq, coreutils, util-linux)" "composefs-alternative" exit 1 fi log_info "All dependencies are available" "composefs-alternative" } # Check kernel modules check_kernel_modules() { local modules=("squashfs" "overlay") local missing_modules=() for module in "${modules[@]}"; do if ! modprobe -n "$module" >/dev/null 2>&1; then missing_modules+=("$module") fi done if [[ ${#missing_modules[@]} -gt 0 ]]; then log_warning "Missing kernel modules: ${missing_modules[*]}" "composefs-alternative" log_warning "Some features may not work correctly" "composefs-alternative" return 1 fi log_info "All required kernel modules are available" "composefs-alternative" return 0 } # --- END OF SCRIPTLET: 01-dependencies.sh --- # ============================================================================ # Content Hash Generation # ============================================================================ # PERFORMANCE: Optimized content hash generation with true parallel processing generate_content_hash() { local source_dir="$1" local temp_file temp_file=$(mktemp) # Add to cleanup list for robust file management CLEANUP_FILES+=("$temp_file") log_info "Generating content hash for: $source_dir" "composefs-alternative" # Count total files for progress indication local total_files total_files=$(find "$source_dir" -type f | wc -l) log_info "Found $total_files files to process" "composefs-alternative" # Create temporary manifest file local manifest_file manifest_file=$(mktemp) CLEANUP_FILES+=("$manifest_file") # Use true parallel processing for better performance on large directories # Process files in parallel using xargs with proper error handling local parallel_jobs parallel_jobs=$(nproc 2>/dev/null || echo 4) # Use number of CPU cores, fallback to 4 # Limit parallel jobs to prevent system overload if [[ $parallel_jobs -gt 8 ]]; then parallel_jobs=8 fi log_info "Using $parallel_jobs parallel jobs for hash generation" "composefs-alternative" # Create a temporary script for parallel processing local parallel_script parallel_script=$(mktemp) CLEANUP_FILES+=("$parallel_script") # Write the parallel processing script with error handling cat > "$parallel_script" << 'EOF' #!/bin/bash set -euo pipefail source_dir="$1" file="$2" manifest_file="$3" # Validate inputs if [[ ! -f "$file" ]]; then echo "ERROR: File not found: $file" >&2 exit 1 fi if [[ ! -r "$file" ]]; then echo "ERROR: File not readable: $file" >&2 exit 1 fi # Calculate hash for this file file_hash=$(sha256sum "$file" | cut -d' ' -f1) # Get relative path for consistent hashing relative_path=$(realpath --relative-to="$source_dir" "$file") # Add to manifest (with lock to prevent race conditions) echo "$file_hash $relative_path" >> "$manifest_file" EOF chmod +x "$parallel_script" # Process files in parallel with progress indication local processed=0 local batch_size=100 # Use find with -print0 and xargs for safe parallel processing # Fallback to sequential processing if xargs fails if find "$source_dir" -type f -print0 | xargs -0 -P"$parallel_jobs" -I {} bash "$parallel_script" "$source_dir" {} "$manifest_file" 2>/dev/null; then log_info "Parallel hash generation completed successfully" "composefs-alternative" else log_warning "Parallel processing failed, falling back to sequential processing" "composefs-alternative" # Fallback to sequential processing find "$source_dir" -type f | while IFS= read -r file; do # Calculate hash for this file file_hash=$(sha256sum "$file" | cut -d' ' -f1) # Get relative path for consistent hashing relative_path=$(realpath --relative-to="$source_dir" "$file") # Add to manifest echo "$file_hash $relative_path" >> "$manifest_file" done log_info "Sequential hash generation completed successfully" "composefs-alternative" fi # Sort manifest for consistent hashing sort "$manifest_file" > "$temp_file" # Hash the sorted manifest to create content-addressable ID local content_hash content_hash=$(sha256sum "$temp_file" | cut -d' ' -f1) log_info "Content hash generated: $content_hash" "composefs-alternative" # Clean up immediately (though trap will handle it too) rm -f "$temp_file" "$manifest_file" "$parallel_script" echo "$content_hash" } # --- END OF SCRIPTLET: 02-hash.sh --- # ============================================================================ # Layer Management # ============================================================================ # Layer management functions for Ubuntu uBlue ComposeFS Alternative # Check if layer already exists (deduplication) layer_exists() { local layer_id="$1" local layer_dir="$COMPOSEFS_DIR/layers/$layer_id" if [[ -d "$layer_dir" ]] && [[ -f "$layer_dir/metadata.json" ]]; then return 0 else return 1 fi } # Create a layer from a directory with content-addressable ID and progress indication create_layer() { local source_dir="$1" # SECURITY: Validate source directory source_dir=$(validate_path "$source_dir" "source_dir") # Generate content-addressable layer ID local layer_id layer_id=$(generate_content_hash "$source_dir") local layer_dir="$COMPOSEFS_DIR/layers/$layer_id" # Check if layer already exists (deduplication) if layer_exists "$layer_id"; then log_info "Layer already exists: $layer_id (deduplication)" "composefs-alternative" echo "$layer_id" return 0 fi log_info "Creating new layer: $layer_id from $source_dir" "composefs-alternative" # Create layer directory mkdir -p "$layer_dir" # Create squashfs image for immutable layer with progress indication local squashfs_file="$layer_dir/data.squashfs" # Calculate source directory size for better progress indication local source_size source_size=$(du -sb "$source_dir" | cut -f1) local source_size_mb=$((source_size / 1024 / 1024)) log_info "Creating squashfs image from $source_size_mb MB source directory (this may take a while)..." "composefs-alternative" # Check available disk space before creating squashfs local available_space available_space=$(get_available_space "$(dirname "$squashfs_file")") if [[ $available_space -lt $((source_size_mb / 2)) ]]; then log_error "Insufficient disk space: ${available_space}MB available, need at least $((source_size_mb / 2))MB for squashfs creation" "composefs-alternative" exit 1 fi # Use configurable compression (default to xz if not set) local compression="${UBLUE_SQUASHFS_COMPRESSION:-xz}" # Use progress indication for mksquashfs with compression info if mksquashfs "$source_dir" "$squashfs_file" -comp "$compression" -progress -quiet; then local squashfs_size squashfs_size=$(stat -c%s "$squashfs_file") local squashfs_size_mb=$((squashfs_size / 1024 / 1024)) local compression_ratio=$((source_size_mb * 100 / squashfs_size_mb)) log_info "Squashfs image created successfully: ${squashfs_size_mb}MB (${compression_ratio}% of original size)" "composefs-alternative" else log_error "Failed to create squashfs image for layer: $layer_id (check available disk space and squashfs-tools installation)" "composefs-alternative" exit 1 fi # Create layer metadata local layer_metadata="$layer_dir/metadata.json" cat > "$layer_metadata" </dev/null; then log_info "Layer already mounted: $layer_id" "composefs-alternative" return 0 fi # Mount squashfs as read-only if mount -t squashfs -o ro "$squashfs_file" "$mount_point"; then log_info "Layer mounted: $layer_id at $mount_point" "composefs-alternative" # Add to cleanup list CLEANUP_MOUNTS+=("$mount_point") else log_error "Failed to mount layer: $layer_id (check if squashfs module is loaded)" "composefs-alternative" exit 1 fi } # --- END OF SCRIPTLET: 03-layers.sh --- # ============================================================================ # Image Management # ============================================================================ # Image management functions for Ubuntu uBlue ComposeFS Alternative # Create a composefs image from multiple directories (multi-layer support) create_image() { local image_name="$1" shift local source_dirs=("$@") # SECURITY: Validate image name image_name=$(validate_image_name "$image_name") if [[ ${#source_dirs[@]} -eq 0 ]]; then log_error "No source directories provided" "composefs-alternative" exit 1 fi log_info "Creating composefs image: $image_name with ${#source_dirs[@]} layers" "composefs-alternative" # SECURITY: Validate all source directories for source_dir in "${source_dirs[@]}"; do source_dir=$(validate_path "$source_dir" "source_dir") if [[ ! -d "$source_dir" ]]; then log_error "Source directory does not exist: $source_dir (check path and permissions)" "composefs-alternative" exit 1 fi if [[ ! -r "$source_dir" ]]; then log_error "Source directory is not readable: $source_dir (check permissions)" "composefs-alternative" exit 1 fi done local image_dir="$COMPOSEFS_DIR/images/$image_name" # Check if image already exists if [[ -d "$image_dir" ]]; then log_error "Image already exists: $image_name. Use 'remove' command first if you want to recreate it." "composefs-alternative" exit 1 fi # Create image directory mkdir -p "$image_dir" # Create layers from source directories with progress indication local layer_ids=() local layer_count=0 for source_dir in "${source_dirs[@]}"; do layer_count=$((layer_count + 1)) log_info "Creating layer $layer_count/${#source_dirs[@]}: $source_dir" "composefs-alternative" local layer_id layer_id=$(create_layer "$source_dir") layer_ids+=("$layer_id") done # Create metadata local metadata_file="$image_dir/metadata.json" cat > "$metadata_file" </dev/null; then log_error "Mount point is already in use: $mount_point (unmount existing mount first)" "composefs-alternative" exit 1 fi # Check if mount point is writable (for overlay upper directory) if [[ ! -w "$(dirname "$mount_point")" ]]; then log_error "Cannot create mount point: parent directory is not writable: $(dirname "$mount_point")" "composefs-alternative" exit 1 fi # Read metadata local metadata_file="$image_dir/metadata.json" if [[ ! -f "$metadata_file" ]]; then log_error "Metadata file not found: $metadata_file" "composefs-alternative" exit 1 fi # Get layer IDs local layers layers=$(jq -r '.layers[]' "$metadata_file") # Mount all layers with progress indication local lower_dirs="" local first_layer=true local layer_count=0 local total_layers total_layers=$(jq -r '.layers | length' "$metadata_file") while IFS= read -r layer_id; do layer_count=$((layer_count + 1)) log_info "Mounting layer $layer_count/$total_layers: $layer_id" "composefs-alternative" # Mount layer using squashfs mount_layer "$layer_id" local layer_mount="$COMPOSEFS_DIR/layers/$layer_id/mount" if [[ "$first_layer" == true ]]; then lower_dirs="$layer_mount" first_layer=false else lower_dirs="${lower_dirs}:${layer_mount}" fi done <<< "$layers" # Create overlay mount local upper_dir="$COMPOSEFS_DIR/mounts/$(basename "$mount_point")_upper" local work_dir="$COMPOSEFS_DIR/mounts/$(basename "$mount_point")_work" mkdir -p "$upper_dir" "$work_dir" log_info "Creating overlay mount..." "composefs-alternative" if mount -t overlay overlay -o "lowerdir=$lower_dirs,upperdir=$upper_dir,workdir=$work_dir" "$mount_point"; then log_info "Composefs image mounted at: $mount_point" "composefs-alternative" log_info "Upper directory: $upper_dir" "composefs-alternative" log_info "Work directory: $work_dir" "composefs-alternative" # Record mount information local mount_info="$COMPOSEFS_DIR/mounts/$(basename "$mount_point").json" cat > "$mount_info" </dev/null; then umount "$layer_mount" 2>/dev/null || log_warning "Failed to unmount layer: $layer_mount" "composefs-alternative" fi done # Remove mount point if empty rmdir "$mount_point" 2>/dev/null || true else log_warning "Standard unmount failed, attempting lazy unmount" "composefs-alternative" if umount -l "$mount_point"; then log_info "Composefs image lazy unmounted from: $mount_point" "composefs-alternative" # Clean up overlay directories rm -rf "$upper_dir" "$work_dir" rm -f "$mount_info" # Unmount and clean up layer mounts IFS=':' read -ra LAYER_MOUNTS <<< "$lower_dirs" for layer_mount in "${LAYER_MOUNTS[@]}"; do if mountpoint -q "$layer_mount" 2>/dev/null; then umount "$layer_mount" 2>/dev/null || log_warning "Failed to unmount layer: $layer_mount" "composefs-alternative" fi done # Remove mount point if empty rmdir "$mount_point" 2>/dev/null || true else log_error "Failed to unmount composefs image from: $mount_point (even with lazy unmount)" "composefs-alternative" exit 1 fi fi } # --- END OF SCRIPTLET: 04-images.sh --- # ============================================================================ # Listing and Reporting Functions # ============================================================================ # Listing and reporting functions for Ubuntu uBlue ComposeFS Alternative # List composefs images list_images() { log_info "Currently available Composefs Images:" "composefs-alternative" if [[ ! -d "$COMPOSEFS_DIR/images" ]]; then log_info "No images found" "composefs-alternative" return 0 fi for image_dir in "$COMPOSEFS_DIR/images"/*; do if [[ -d "$image_dir" ]]; then local image_name image_name=$(basename "$image_dir") local metadata_file="$image_dir/metadata.json" if [[ -f "$metadata_file" ]]; then local created local layers local layer_ids=() created=$(jq -r '.created' "$metadata_file") layers=$(jq -r '.layers | length' "$metadata_file") # Collect layer IDs for size calculation while IFS= read -r layer_id; do layer_ids+=("$layer_id") done < <(jq -r '.layers[]' "$metadata_file") # Calculate actual disk usage (accounting for deduplication) local actual_size actual_size=$(calculate_layer_disk_usage "${layer_ids[@]}") local actual_size_mb=$((actual_size / 1024 / 1024)) # Calculate total size (including duplicates for reference) local total_size=0 for layer_id in "${layer_ids[@]}"; do local layer_dir="$COMPOSEFS_DIR/layers/$layer_id" local layer_metadata="$layer_dir/metadata.json" if [[ -f "$layer_metadata" ]]; then local layer_size layer_size=$(jq -r '.size' "$layer_metadata") total_size=$((total_size + layer_size)) fi done local total_size_mb=$((total_size / 1024 / 1024)) # Show both actual and total sizes if [[ $actual_size -eq $total_size ]]; then echo " $image_name (created: $created, layers: $layers, size: ${actual_size_mb}MB)" else local savings_mb=$((total_size_mb - actual_size_mb)) echo " $image_name (created: $created, layers: $layers, size: ${actual_size_mb}MB, total: ${total_size_mb}MB, saved: ${savings_mb}MB via deduplication)" fi else echo " $image_name (incomplete)" fi fi done } # List mounted composefs images list_mounts() { log_info "Currently Mounted Composefs Images:" "composefs-alternative" if [[ ! -d "$COMPOSEFS_DIR/mounts" ]]; then log_info "No mounts found" "composefs-alternative" return 0 fi local mount_count=0 for mount_info in "$COMPOSEFS_DIR/mounts"/*.json; do if [[ -f "$mount_info" ]]; then local mount_point local image_name local mounted_at mount_point=$(jq -r '.mount_point' "$mount_info") image_name=$(jq -r '.image' "$mount_info") mounted_at=$(jq -r '.mounted_at' "$mount_info") echo " $image_name -> $mount_point (mounted: $mounted_at)" mount_count=$((mount_count + 1)) fi done if [[ $mount_count -eq 0 ]]; then log_info "No active mounts" "composefs-alternative" fi } # PERFORMANCE: Optimized layer listing with caching list_layers() { log_info "Available Composefs Layers:" "composefs-alternative" if [[ ! -d "$COMPOSEFS_DIR/layers" ]]; then log_info "No layers found" "composefs-alternative" return 0 fi local layer_count=0 local total_size=0 # Pre-calculate layer references for better performance local -A layer_references for image_dir in "$COMPOSEFS_DIR/images"/*; do if [[ -d "$image_dir" ]]; then local image_metadata="$image_dir/metadata.json" if [[ -f "$image_metadata" ]]; then while IFS= read -r layer_id; do layer_references["$layer_id"]=$((layer_references["$layer_id"] + 1)) done < <(jq -r '.layers[]' "$image_metadata") fi fi done for layer_dir in "$COMPOSEFS_DIR/layers"/*; do if [[ -d "$layer_dir" ]]; then local layer_id layer_id=$(basename "$layer_dir") local metadata_file="$layer_dir/metadata.json" if [[ -f "$metadata_file" ]]; then local created local size local source local reference_count=${layer_references["$layer_id"]:-0} created=$(jq -r '.created' "$metadata_file") size=$(jq -r '.size' "$metadata_file") source=$(jq -r '.source' "$metadata_file") local size_mb=$((size / 1024 / 1024)) total_size=$((total_size + size)) if [[ $reference_count -eq 0 ]]; then echo " $layer_id (created: $created, size: ${size_mb}MB, source: $source, references: 0 - UNREFERENCED)" elif [[ $reference_count -eq 1 ]]; then echo " $layer_id (created: $created, size: ${size_mb}MB, source: $source, references: $reference_count)" else echo " $layer_id (created: $created, size: ${size_mb}MB, source: $source, references: $reference_count - SHARED)" fi layer_count=$((layer_count + 1)) else echo " $layer_id (incomplete)" fi fi done local total_size_mb=$((total_size / 1024 / 1024)) echo log_info "Layer Summary: $layer_count layers, total size: ${total_size_mb}MB" "composefs-alternative" } # Show comprehensive system status show_status() { echo "=== Composefs Alternative Status ===" echo # System information echo "System Information:" get_system_info echo # Storage information echo "Storage Information:" local composefs_size composefs_size=$(du -sb "$COMPOSEFS_DIR" 2>/dev/null | cut -f1 || echo "0") local composefs_size_mb=$((composefs_size / 1024 / 1024)) echo " Composefs directory size: ${composefs_size_mb}MB" local available_space_mb available_space_mb=$(get_available_space "$COMPOSEFS_DIR") local available_space_gb=$((available_space_mb / 1024)) echo " Available space: ${available_space_gb}GB" echo # Images and layers list_images echo list_mounts echo list_layers echo # Health check echo "Health Check:" local health_issues=0 # Check for orphaned mounts for mount_info in "$COMPOSEFS_DIR/mounts"/*.json; do if [[ -f "$mount_info" ]]; then local mount_point mount_point=$(jq -r '.mount_point' "$mount_info" 2>/dev/null) if [[ -n "$mount_point" && "$mount_point" != "null" ]]; then if ! mountpoint -q "$mount_point" 2>/dev/null; then echo " ⚠️ Orphaned mount info: $mount_info" health_issues=$((health_issues + 1)) fi fi fi done # Check for unreferenced layers local unreferenced_count=0 for layer_dir in "$COMPOSEFS_DIR/layers"/*; do if [[ -d "$layer_dir" ]]; then local layer_id layer_id=$(basename "$layer_dir") local is_referenced=false for image_dir in "$COMPOSEFS_DIR/images"/*; do if [[ -d "$image_dir" ]]; then local metadata_file="$image_dir/metadata.json" if [[ -f "$metadata_file" ]]; then if jq -e --arg layer "$layer_id" '.layers[] | select(. == $layer)' "$metadata_file" >/dev/null 2>&1; then is_referenced=true break fi fi fi done if [[ "$is_referenced" == false ]]; then unreferenced_count=$((unreferenced_count + 1)) fi fi done if [[ $unreferenced_count -gt 0 ]]; then echo " ⚠️ $unreferenced_count unreferenced layers found (run 'cleanup' to remove)" health_issues=$((health_issues + 1)) fi if [[ $health_issues -eq 0 ]]; then echo " ✓ System healthy" else echo " ⚠️ $health_issues health issues found" fi } # --- END OF SCRIPTLET: 05-listing.sh --- # ============================================================================ # Cleanup and Maintenance Functions # ============================================================================ # Cleanup and maintenance functions for Ubuntu uBlue ComposeFS Alternative # Remove unreferenced layers cleanup_unreferenced_layers() { log_info "Cleaning up unreferenced layers" "composefs-alternative" if [[ ! -d "$COMPOSEFS_DIR/layers" ]]; then log_info "No layers to clean up" "composefs-alternative" return 0 fi # Get all referenced layer IDs from images local referenced_layers=() for image_dir in "$COMPOSEFS_DIR/images"/*; do if [[ -d "$image_dir" ]]; then local metadata_file="$image_dir/metadata.json" if [[ -f "$metadata_file" ]]; then while IFS= read -r layer_id; do referenced_layers+=("$layer_id") done < <(jq -r '.layers[]' "$metadata_file") fi fi done # Check each layer local removed_count=0 for layer_dir in "$COMPOSEFS_DIR/layers"/*; do if [[ -d "$layer_dir" ]]; then local layer_id layer_id=$(basename "$layer_dir") # Check if layer is referenced local is_referenced=false for ref_layer in "${referenced_layers[@]}"; do if [[ "$ref_layer" == "$layer_id" ]]; then is_referenced=true break fi done if [[ "$is_referenced" == false ]]; then log_info "Removing unreferenced layer: $layer_id" "composefs-alternative" rm -rf "$layer_dir" removed_count=$((removed_count + 1)) fi fi done log_info "Cleaned up $removed_count unreferenced layers" "composefs-alternative" } # Remove a composefs image remove_image() { local image_name="$1" # SECURITY: Validate image name image_name=$(validate_image_name "$image_name") local image_dir="$COMPOSEFS_DIR/images/$image_name" log_info "Removing composefs image: $image_name" "composefs-alternative" if [[ ! -d "$image_dir" ]]; then log_error "Composefs image does not exist: $image_name" "composefs-alternative" exit 1 fi # Check if image is mounted for mount_info in "$COMPOSEFS_DIR/mounts"/*.json; do if [[ -f "$mount_info" ]]; then local mounted_image mounted_image=$(jq -r '.image' "$mount_info") if [[ "$mounted_image" == "$image_name" ]]; then local mount_point mount_point=$(jq -r '.mount_point' "$mount_info") log_error "Cannot remove image while mounted at: $mount_point. Unmount first." "composefs-alternative" exit 1 fi fi done # Remove image directory rm -rf "$image_dir" log_info "Composefs image removed: $image_name" "composefs-alternative" # Clean up unreferenced layers cleanup_unreferenced_layers } # Clean up orphaned mount information cleanup_orphaned_mounts() { log_info "Cleaning up orphaned mount information" "composefs-alternative" if [[ ! -d "$COMPOSEFS_DIR/mounts" ]]; then log_info "No mount information to clean up" "composefs-alternative" return 0 fi local cleaned_count=0 for mount_info in "$COMPOSEFS_DIR/mounts"/*.json; do if [[ -f "$mount_info" ]]; then local mount_point mount_point=$(jq -r '.mount_point' "$mount_info" 2>/dev/null) if [[ -n "$mount_point" && "$mount_point" != "null" ]]; then if ! mountpoint -q "$mount_point" 2>/dev/null; then log_info "Removing orphaned mount info: $mount_info" "composefs-alternative" rm -f "$mount_info" cleaned_count=$((cleaned_count + 1)) fi fi fi done log_info "Cleaned up $cleaned_count orphaned mount information files" "composefs-alternative" } # Full system cleanup full_cleanup() { log_info "Performing full system cleanup" "composefs-alternative" # Clean up unreferenced layers cleanup_unreferenced_layers # Clean up orphaned mount information cleanup_orphaned_mounts # Clean up empty directories find "$COMPOSEFS_DIR" -type d -empty -delete 2>/dev/null || true log_info "Full system cleanup completed" "composefs-alternative" } # --- END OF SCRIPTLET: 06-cleanup.sh --- # ============================================================================ # Main Dispatch and Help # ============================================================================ # Main dispatch and help system for Ubuntu uBlue ComposeFS Alternative # Show usage information show_usage() { cat << EOF composefs-alternative.sh - Ubuntu alternative to composefs Provides similar functionality to composefs using overlayfs and other Linux filesystem features Usage: $0 [options] Commands: create [source_dir2] ... Create composefs image from directories mount Mount composefs image unmount Unmount composefs image list-images List all composefs images list-mounts List mounted composefs images list-layers List all layers with usage info remove Remove composefs image cleanup Clean up unreferenced layers status Show comprehensive system status Examples: $0 create my-image /path/to/base /path/to/apps $0 mount my-image /mnt/composefs $0 list-images $0 list-layers $0 unmount /mnt/composefs $0 remove my-image $0 cleanup $0 status Features: - Content-addressable layers with deduplication - Immutable layers using squashfs - Multi-layer image support - Automatic cleanup of unreferenced layers - Proper resource management and cleanup - Accurate disk usage reporting (accounting for deduplication) - Enhanced security with input validation - Progress indicators for long operations - Optimized performance for large datasets - True parallel processing for hash generation - Comprehensive system health monitoring - Robust error handling with fallback mechanisms Security Features: - Path traversal protection - Input validation and sanitization - Secure temporary file handling - Privilege escalation prevention - Character set restrictions on inputs - Absolute path enforcement for critical operations Performance Features: - Parallel hash generation using xargs - Cached layer reference counting - Optimized SquashFS operations with compression info - Progress indication for all long operations - Fallback to sequential processing if parallel fails - Memory-efficient processing of large datasets System Requirements: - Linux kernel with squashfs and overlay modules - squashfs-tools package - jq for JSON processing - coreutils for system utilities - Root privileges for filesystem operations This script provides composefs-like functionality using overlayfs for Ubuntu systems. Based on composefs design principles: https://github.com/containers/composefs EOF } # Main function main() { # Check if running as root check_root # Check dependencies check_dependencies # Check kernel modules check_kernel_modules # Initialize directories init_directories # Parse command line arguments if [[ $# -eq 0 ]]; then show_usage exit 1 fi local command="$1" shift case "$command" in "create") if [[ $# -lt 2 ]]; then log_error "Usage: create [source_dir2] ..." "composefs-alternative" exit 1 fi local image_name="$1" shift create_image "$image_name" "$@" ;; "mount") if [[ $# -lt 2 ]]; then log_error "Usage: mount " "composefs-alternative" exit 1 fi mount_image "$1" "$2" ;; "unmount") if [[ $# -lt 1 ]]; then log_error "Usage: unmount " "composefs-alternative" exit 1 fi unmount_image "$1" ;; "list-images") list_images ;; "list-mounts") list_mounts ;; "list-layers") list_layers ;; "remove") if [[ $# -lt 1 ]]; then log_error "Usage: remove " "composefs-alternative" exit 1 fi remove_image "$1" ;; "cleanup") cleanup_unreferenced_layers ;; "status") show_status ;; "help"|"-h"|"--help") show_usage ;; *) log_error "Unknown command: $command" "composefs-alternative" show_usage exit 1 ;; esac } # Run main function with all arguments if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi # --- END OF SCRIPTLET: 99-main.sh --- # ============================================================================ # Embedded Configuration Files # ============================================================================ # ============================================================================ # External Configuration Loading (Future Enhancement) # ============================================================================ # Function to load configuration from external files # Usage: load_config_from_file "config-name" load_config_from_file() { local config_name="$1" local config_file="/etc/composefs-alternative/config/${config_name}.json" if [[ -f "$config_file" ]]; then jq -r '.' "$config_file" else log_error "Configuration file not found: $config_file" "composefs-alternative" exit 1 fi }