Convert Raspian Images to lvm + xfs and installs some usefull defaults.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

229 lines
6.4 KiB

#!/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