#!/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 super chs = [?a..?z, ?A..?Z].map(&:to_a).flatten @vgname = "armbian_#{5.times.map{chs[rand(chs.length)]}.join}" d "Destination image «#{dest.image}» in use!", dest.kpartxed? end # read from input, write to output # input|output can be: # Pathname: opens file and use it as IO # String: reads from/writes to like an IO (->StringIO) # IO: use it directly # obs|ibs|bs: blocksizes (if ibs|obs not given, bs will be used, else 512) # from: set input-block-position to from # skip: skip next X blocks from current position # at: set output-block-position to to # seek: seek to next X blocks from current position def dd input, output, from: nil, skip: nil, at: nil, seek: nil, bs: nil, ibs: nil, obs: nil case input when Pathname input.open 'r' do |f| return __method__( f, output, pos: pos, seek: seek) end when String StringIO.open input do |f| return __method__( f, output, pos: pos, seek: seek) end when IO case output when Pathname output.open 'w' do |f| return __method__( input, f, pos: pos, seek: seek) end when String StringIO.open output, 'w' do |f| return __method__( input, f, pos: pos, seek: seek) end when IO ibs ||= bs || 512 obs ||= bs || 512 input.pos = from*ibs if from input.pos += skip*ibs if skip output.pos = at*obs if at output.pos += seek*obs if seek output.write input.read( count ? count*ibs : nil) end else raise ArgumentError, "input for dd must be a String, IO or Pathname" end end def run mkmppaths d "Base image does not exist", base.image.exist? base_parts = base.kpartx d "two (root-)partition in base expected, got: #{base_parts.inspect}", 1 == base_parts.length mount base_parts[0], base.root, -:oro msg 'prepare image', dest.image dest.image.open 'w' do |f| f.truncate 0 f.pos = 0 f.write 0.chr*4.megabytes f.pos = 1.5.gigabytes+128.megabytes+4.megabytes-1 f.putc 0.chr end # partitions for: /boot (128MiBi), LVM sh.parted dest.image, '--', *%w[mklabel msdos], *%w[mkpart primary ext2 4MB 132MB], *%w[mkpart primary ext2 132MB -4MB], *%w[set 1 boot on], *%w[set 2 LVM on], *%w[print] ubootdir = base.root + 'usr' + 'lib' + 'linux-u-boot-dev-odroidxu4_5.34.171103_armhf' if false sh.bash -:ec, <<-EOF.gsub( /^\t{4}/, '').sub( /\n$/m, '') echo "hallo welt" BRANCH=dev source ./build/config/sources/odroidxu4.conf dd() { echo dd "$*" >&2 command dd "$@" } write_uboot_platform #{ubootdir.to_s.shellescape} #{dest.image.to_s.shellescape} EOF else msg 'install u-boot', dest.image, ubootdir dest.image.open 'r+' do |f| msg ' apply', ubootdir+'bl1.bin.hardkernel' f.pos = 1*Sector b = (ubootdir+'bl1.bin.hardkernel').read f.write b msg ' apply', ubootdir+'bl2.bin.hardkernel.720k_uboot' f.pos = 31*Sectors f.write (ubootdir+'bl2.bin.hardkernel.720k_uboot').read msg ' apply', ubootdir+'u-boot-dtb.bin' f.pos = 63*Sectors f.write (ubootdir+'u-boot-dtb.bin').read msg ' apply', ubootdir+'tzsw.bin.hardkernel' f.pos = 1503*Sectors f.write (ubootdir+'tzsw.bin.hardkernel').read msg ' clear', 'u-boot env' f.pos = 2015*Sectors f.write 0.chr*32*Sectors end end dest_parts = dest.kpartx d "two partitions in destination expected", 2 == dest_parts.length #root = MP.new self, dest_parts[1], dest.root #home = MP.new self, nil, dest.root+'home' #boot = MP.new self, dest_parts[0], dest.root+'boot' vgpath = Pathname.new( '/dev') + vgname root = MP.new self, vgpath + 'root', dest.root #home = MP.new self, vgpath + 'home', dest.root+'home' boot = MP.new self, dest_parts[0], dest.root+'boot' #swap = MP.new self, vgpath + 'swap', 'none' sh.pvcreate dest_parts[1] sh.vgcreate vgname, dest_parts[1] sh.lvcreate -:nroot, '-L1.5G', vgname #sh.lvcreate -:nhome, '-L120M', vgname #sh.lvcreate -:nswap, '-L10M', vgname activate_vg vgname sh.mkext4fs boot.dev sh.mkxfs root.dev #sh.mkxfs home.dev #sh.mkswap swap.dev root.mount #home.mp.mkdir #home.mount boot.mp.mkdir boot.mount exclude = %w[boot/dtb-* boot/uInitrd-* boot/vmlinuz-* boot/initrd.img-* boot/config-* boot/System.map-* etc/systemd/system/basic.target.wants/resize2fs.service etc/systemd/system/sysinit.target.wants/log2ram.service etc/rc?.d/*resize2fs root/.not_logged_in_yet etc/profile.d/check_first_login.sh ].flat_map {|e| ['--exclude', e] } #"#{base.root}#{e}"] } sh.rsync_all *exclude, "#{base.root}/", root.mp msg 'create symlink', boot.mp+'boot', '->', '.' (boot.mp+'boot').make_symlink '.' manipulate_file boot.mp+'boot.ini' do |file, &push| file.each_line do |l| case l.chomp! when /^ *setenv +rootdev +/ push.call "setenv rootdev \"UUID=#{root.uuid}\"" when /^ *setenv +rootfstype +/ push.call "setenv rootfstype \"#{root.fstype}\"" else push.call l end end end manipulate_file root.mp+'etc'+'initramfs-tools'+'initramfs.conf' do |file, &push| file.each_line do |l| case l.chomp! when /^COMPRESS=/ push.call 'COMPRESS=xz' when /^# *COMPRESS=/ push.call l, 'COMPRESS=xz' else push.call l end end end manipulate_file root.mp+'etc'+'fstab' do |file, &push| file.each_line do |l| case l.chomp! when /^ *#/ push.call l when /^\s*(\S+)\s+\/\s+()/ push.call "UUID=#{root.uuid} / #{root.fstype} defaults,noatime,nodiratime,errors=remount-ro 0 1" when /^\s*\/var\/swap\s+/ # skip else push.call l end end #push.call "UUID=#{swap.uuid} none #{swap.fstype} sw 0 0" push.call "UUID=#{boot.uuid} /boot #{boot.fstype} defaults 0 0" #push.call "UUID=#{home.uuid} /home #{home.fstype} defaults,errors=remount-ro 0 0" end sh.tar -:C, root.mp, -:xf, 'files.tar' if File.exists? 'files.tar' rescue ProgrammError err $! raise ensure STDERR.puts "="*80 umount_all ignore_exceptions: true deactivate_all_vgs ignore_exceptions: true capsulated_rescue { sh.sync } capsulated_rescue { dest.departx } capsulated_rescue { base.departx } capsulated_rescue { sh.sync } end end Armbian2LVM_XFS.new.run