#!/bin/bash # This script packs a file or directory into a 7z archive with the given password. # It can (optionally) split the archives into volumes of a fixed size in megabytes. source functions.sh set -euo pipefail cleanup() { local archive_name="$1" rm -f "${archive_name}"* log "INFO" "Cleaning up "${archive_name##*/}" remains before exit" } check_input() { local input_file="$1" local password="$2" local size="$3" [[ -e "$input_file" ]] || { log "ERROR" "Input file '${input_file}' does not exist" && exit 2 ;} [[ -n "${password}" ]] || { log "ERROR" "No password given" && exit 2 ;} if [[ ! "$size" =~ ^([1-9][0-9]*)?$ ]]; then log "ERROR" "Invalid size '$size', must be empty or a positive number" exit 2 fi } calc_smart_volumes() { # Calculates a "smart" volume size so that each volume (including the final part) # will be approximately the same size local input_file="$1" local size="$2" local total_size num_volumes smart_size total_size=$(du -s -BM "${input_file}" | grep -oP '^\d+') || return 1 # If total file size is smaller than volume size, don't pack it into volumes (( total_size < size )) && return 1 num_volumes=$(( (total_size + size - 1) / size )) smart_size=$(( (total_size + num_volumes - 1) / num_volumes )) log "INFO" "Calculated smart volume size '$smart_size'" echo "$smart_size" } get_volumes() { local input_file="$1" local size="${2:-}" local volumes="" local smart_size if [[ -n "$size" ]]; then log "INFO" "Volumes size is set to ${size}MB" smart_size="$(calc_smart_volumes "$input_file" "$size" || echo -n)" volumes="-v${smart_size}m" fi [[ -n "$volumes" ]] && log "INFO" "Volumes set to '$volumes'" || log "INFO" "Volumes argument unset" echo "$volumes" } generate_name() { # Generates a random archive name of 20 alphanumeric characters local archive_name archive_name="$archive_dir/$(tr -dc '[:alnum:]' < /dev/urandom | head -c 20).7z" log "INFO" "Generated archive name '$archive_name'" echo "$archive_name" } create_archive() { local input_file="$1" local password="$2" local volumes="$3" local size="$4" local archive_name="$5" 7z a -mhe=on "-p${password}" "$volumes" "$archive_name" "${input_file}" } main() { local input_file="${1:-}" local password="${2:-}" local size="${3:-}" local volumes archive_name archive_dir archive_dir="$(dirname "$0")/../archives" check_dependencies "7z" check_input "$input_file" "$password" "$size" if ! mkdir -p "$archive_dir"; then log "ERROR" "Unable to create archive directory" exit 1 fi volumes="$(get_volumes "$input_file" "$size")" archive_name="$(generate_name)" trap 'cleanup "$archive_name"' ERR INT log "INFO" "Packing '${input_file}' into archive '$archive_name' with password '${password}'" create_archive \ "$input_file" \ "$password" \ "$volumes" \ "$size" \ "$archive_name" log "NOTICE" "Archive created" } main "$@"