#!/usr/bin/env ruby require 'open-uri' require 'zip' # gem install rubyzip require 'stringio' require 'fileutils' require 'uri' require 'xz' # gem install ruby-xz url = 'https://artur.gurgul.pro/static/vms/debian.ext2.img.zip' # 'https://artur.gurgul.pro/static/vms/linux-v1.zip' # debian.ext2.img.zip debian.ext4.img.zip linux-v1.zip # sudo fdisk -l debian.img # [sudo] password for artur: # Disk debian.img: 4 GiB, 4294967296 bytes, 8388608 sectors # Units: sectors of 1 * 512 = 512 bytes # Sector size (logical/physical): 512 bytes / 512 bytes # I/O size (minimum/optimal): 512 bytes / 512 bytes # Disklabel type: dos # Disk identifier: 0x878d05dc # # Device Boot Start End Sectors Size Id Type # debian.img1 * 2048 8386559 8384512 4G 83 Linux # artur@gurgul.xyz ~/vm ➜ sudo mount -o loop,offset=1048576 debian.img /mnt # 1048576 = 512 * 2048 # sudo tar --xattrs --acls --numeric-owner -cpf - -C /mnt/diskimg . | xz -T0 > disk-backup.tar.xz # To preserve all metadata, ownership, permissions, symlinks, devices, etc., use tar with --xattrs, --numeric-owner # -cpf - → write tar to stdout # -C /mnt/diskimg . → change directory so root of the archive matches root of the image # xz -T0 → compress using all CPU cores # https://chatgpt.com/c/688b32da-5e14-8327-9abc-b83a44252c05 # compare for differences # sudo diff -r /mnt/diskimg /tmp/old # Determine file extension ext = File.extname(URI.parse(url).path) # Open remote stream URI.open(url) do |remote_file| case ext when '.zip' puts "Processing ZIP file..." buffer = remote_file.read Zip::File.open_buffer(StringIO.new(buffer)) do |zip_file| zip_file.each do |entry| puts "Extracting #{entry.name}..." target_path = File.join(Dir.pwd, entry.name) FileUtils.mkdir_p(File.dirname(target_path)) entry.extract(target_path) { true } # Overwrite if exists end end when '.xz' puts "Processing XZ file..." # Use XZ::StreamReader to decompress on-the-fly xz_reader = XZ::StreamReader.new(remote_file) # Assume .xz contains a single file; decide on output name output_filename = File.basename(url, '.xz') output_path = File.join(Dir.pwd, output_filename) File.open(output_path, 'wb') do |out| IO.copy_stream(xz_reader, out) end puts "Decompressed to #{output_path}" else puts "Unsupported file extension: #{ext}" end end