#!/usr/bin/env ruby require 'pathname' $: << Pathname.new($0).expand_path.dirname+'lib' require 'base' require 'active_support/all' Sector = Sectors = 512 class Armbian2LVM_XFS < Base def initialize vgname = nil super chs = [?a..?z, ?A..?Z].map(&:to_a).flatten @vgname = vgname || "armbian_#{5.times.map{chs[rand(chs.length)]}.join}" end def remove_all_partitions_from image o = `parted -ms #{image.to_s.shellescape} print` d "Cannot determine partitions in dest-image #{image}", 0 == $? o.split( "\n")[2..-1].each do sh.parted image, :rm, 1 end end def run mkmppaths d "Base image does not exist", base.image.exist? base_parts = kpartx base.image d "two (root-)partition in base expected, got: #{base_parts.inspect}", 1 == base_parts.length mount -:oro, base_parts[0], base.root # creates an image dest.image.open 'w' do |f| #f.pos = 1*Sector ## copy first 4mb from base-image because of u-boot #f.write base.image.read( 4.megabytes-Sector, Sector) f.pos = 1.6.gigabytes-1 f.putc 0.chr end # partitions for: /boot (128MiBi), LVM sh.parted dest.image, *%w[-- mklabel msdos mkpart primary ext2 4MB 132MB mkpart primary ext2 132MB -1s set 2 LVM on print] sh.bash -:ec, <<-EOF.gsub( /^\t{3}/, '').sub( /\n$/m, '') BRANCH=dev source ./build/config/sources/odroidxu4.conf write_uboot_platform base/root/usr/lib/linux-u-boot-dev-odroidxu4_5.34.171103_armhf dest/base EOF dest_parts = kpartx dest.image d "two partitions in destination expected", 2 == dest_parts.length vgpath = Pathname.new( '/dev') + vgname sh.pvcreate dest_parts[1] sh.vgcreate vgname, dest_parts[1] sh.lvcreate -:nroot, '-L1.1G', vgname sh.lvcreate -:nhome, '-L100M', vgname sh.vgchange -:ae, vgname sh.mkext4fs dest_parts[0] sh.mkxfs vgpath+'root' sh.mkxfs vgpath+'home' mount vgpath+'root', dest.root addmp = {} %i[home boot].each do |n| d = addmp[n] = dest.root+n.to_s d.mkdir end mount vgpath+'home', addmp[:home] mount dest_parts[0], addmp[:boot] sh.rsync_all "#{base.root}/", dest.root (addmp[:boot]+'boot').make_symlink '.' # boot -> . (addmp[:boot]+'boot.ini').open 'r+' do |f| replace = { rootdev: "setenv rootdev \"#{vgpath+'root'}\"", rootfstype: "setenv rootfstype \"xfs\"", } lines = f.each_line.map do |l| l.chomp! case l when /^setenv rootdev / replace.delete :rootdev when /^setenv rootfstype / replace.delete :rootfstype else l.gsub ' /boot/', ' /' end end f.truncate 0 f.pos = 0 f.puts *lines d "boot.ini had no entry for: #{replace.keys}", replace.empty? end (dest.root+'etc'+'fstab').open 'r+' do |f| replace = { '/' => "#{vgpath+'root'} / xfs defaults,noatime 0 0", '/boot' => "#{dest_parts[0]} /boot ext4 defaults,noatime 0 0", '/home' => "#{vgpath+'home'} /home xfs defaults,noatime 0 0", } lines = f.each_line.flat_map do |l| mp = l.split( /\s+/)[1] replace.delete( mp) || l end lines += replace.values f.truncate 0 f.pos = 0 f.puts lines end (dest.root+'etc'+'initramfs-tools'+'initramfs.conf').open 'r+' do |f| lines = f.each_line.flat_map do |l| l.chomp! case l when /^COMPRESS=/ 'COMPRESS=xz' when /^# *COMPRESS=/ [l, 'COMPRESS=xz'] else l end end f.truncate 0 f.pos = 0 f.puts lines end sh.tar -:C, dest.root, -:xf, 'files.tar' if File.exists? 'files.tar' rescue ProgrammError err $! raise ensure STDERR.puts "="*80 umount_all ignore_exceptions: true sh.vgchange -:an, vgname rescue Object sh.sync rescue Object departx dest.image rescue Object departx base.image rescue Object sh.sync rescue Object STDERR.puts "="*80 end end Armbian2LVM_XFS.new(*ARGV).run