OSDN Git Service

Change to use mbr for boot disk, split dirs for each disk.
[osdn-codes/image-creator.git] / create-image
index 85c8384..8bf0092 100755 (executable)
@@ -6,75 +6,77 @@ require 'yaml'
 
 class SyncDirDef
   DEFAULT_EXCLUDE = %w[/proc/* /sys/* /dev/mqueue /dev/hugepages /run/* /var/lib/os-prober/mount /swap /dev/shm/* /var/lib/lxcfs/*]
-  attr_accessor :path, :size, :exclude, :size, :srcpath, :fs_features
+  attr_accessor :path, :size, :exclude, :size, :srcpath, :fs_features, :device
   def initialize(path: '/', size: 8, exclude: DEFAULT_EXCLUDE, srcpath: nil, fs_features: nil)
     @path = path
     @size = size.to_f
     @exclude = (DEFAULT_EXCLUDE + [*exclude]).uniq
     @srcpath = srcpath || path
     @fs_features = fs_features
+    @device = nil
   end
 end
 
-
 class ImageCreator
-  attr_accessor :name, :dirs, :src_host
+  attr_accessor :name, :dirs, :src_host, :img_path_base
+  MiB = 1024 ** 2
+  GiB = 1024 ** 3
 
   def initialize(name, dirs, src_host: nil)
     @name = name
     @dirs = dirs
     @src_host = src_host || name
+    @img_path_base = "#{name}_#{Time.now.strftime '%FT%T%z'}"
   end
 
-  def size
-    @size and return @size 
-    @size = 3 * (1024**2) # alignment + BIOS boot partition + gap
-    dirs.each do |di|
-      @size += di.size * (1024**3)
-    end
-    @size
+  def imgpath(idx)
+    "#{img_path_base}_#{idx}.img"
   end
 
-  def imgpath
-    @imgpath ||= "#{name}_#{Time.now.strftime '%FT%T%z'}.img"
+  def create_disk
+    dirs.each_with_index do |di, idx|
+      _create_disk imgpath(idx), di, idx != 0
+    end
   end
 
-  def create_disk
-    raise "Disk image #{imgpath} is already exists!" if File.exists? imgpath
-    puts "Creating disk image #{imgpath} (#{'%5.2f' % (size.to_f/(1024**3))} GiB)..."
-    last_mb = 1
-    File.open(imgpath, "w") do |f|
-      f.truncate size
+  def _create_disk path, di, use_gpt = false
+    size_gb = di.size
+    raise "Disk image #{path} is already exists!" if File.exists? path
+    puts "Creating disk image #{path} (#{'%5.2f' % size_gb.to_f} GiB)..."
+    File.open(path, "w") do |f|
+      f.truncate(size_gb * GiB)
     end
-    system("parted", "-s", imgpath, "mklabel", "gpt") or raise "Failed to create partition label"
-    system("parted", "-s", imgpath, "mkpart", "primary", "#{last_mb}MiB", "#{last_mb+1}MiB") or raise "Failed to create boot partition"
-    system("parted", "-s", imgpath, "set", "1", "bios_grub", "on") or raise "Failed to set bios boot partition"
-    last_mb += 1
-    dirs.each do |di|
-      system("parted", "-s", imgpath, "mkpart", "primary", "#{last_mb}MiB", "#{di.size * 1024 + last_mb}MiB") or raise "Failed to create partition: #{l}"
-      last_mb += di.size * 1024
+    system("parted", "-s", path, "mklabel", use_gpt ? 'gpt' : 'msdos') or raise "Failed to create partition label"
+    system("parted", "-s", path, "mkpart", "primary", "1MiB", "#{size_gb * 1024 - 1}MiB") or raise "Failed to create partition"
+    if use_gpt
+      system("parted", "-s", path, "name", "1", di.path) or raise "Failed to set part label"
+    else
+      system("parted", "-s", path, "set", "1", "boot", "on") or raise "Failed to set bios boot partition"
     end
     puts "Image partition created."
-    #system "sfdisk", "-l", imgpath
+    #system "sfdisk", "-l", path
   end
   
   def with_loopdev &block
     begin
-      system("kpartx", "-as", imgpath) or raise "Failed to map loop device"
-      maplines = `kpartx -l #{Shellwords.escape imgpath}`.split("\n")
       devices = []
-      maplines.each do |map|
-        devices << "/dev/mapper/#{map[/loop\d+p\d+/]}"
+      dirs.each_with_index do |di, idx|
+        system("kpartx", "-as", imgpath(idx)) or raise "Failed to map loop device"
+        di.device = "/dev/mapper/" + `kpartx -l #{Shellwords.escape imgpath(idx)}`.split("\n").first[/loop\d+p\d+/]
+        devices << di.device
       end
-      yield devices, maplines.first[%r{/dev/loop\d+}]
+      yield devices
     ensure
-      system "kpartx", "-d", imgpath, err: "/dev/null"
+      dirs.each_with_index do |di, idx|
+        system "kpartx", "-d", imgpath(idx), err: "/dev/null"
+        di.device = nil
+      end
     end
   end
 
   def create_fs
-    with_loopdev do |devices, root|
-      devices[1..-1].each_with_index do |(dev, _), index|
+    with_loopdev do |devices|
+      devices.each_with_index do |dev, index|
         puts "Creating filesystem on #{dev}..."
         cmd = %w(mkfs.ext4 -q)
         di = dirs[index]
@@ -88,8 +90,8 @@ class ImageCreator
   end
 
   def sync_dirs
-    with_loopdev do |devices, root|
-      devices[1..-1].each_with_index do |dev, idx|
+    with_loopdev do |devices|
+      devices.each_with_index do |dev, idx|
         di = dirs[idx]
         mount_point = "/mnt/ci-#{$$}-#{name}-#{idx}"
         system("mkdir", "-p", mount_point)
@@ -111,20 +113,28 @@ class ImageCreator
     Dir.mktmpdir("ci-#{$$}-#{name}") do |dir|
       system("cp", "-a", "/usr/lib/grub/i386-pc/boot.img", dir) or raise "Failed to copy boot.img"
       coreimg = "#{dir}/core.img"
-      system("grub-mkimage", "-o", coreimg, "-O", "i386-pc", "-p", "(hd0,gpt2)/boot/grub", "biosdisk", "part_gpt", "ext2", "gzio", "xzio", "lzopio") or raise "Failed to create grub core image."
-      with_loopdev do |devices, root|
+      system("grub-mkimage", "-o", coreimg, "-O", "i386-pc", "-p", "(hd0,msdos1)/boot/grub", "biosdisk", "part_msdos", "ext2", "gzio", "xzio", "lzopio") or raise "Failed to create grub core image."
+      with_loopdev do |devices|
+        root_dev = "/dev/#{devices.first[/loop\d+/]}"
         puts "Override grub with host version..."
-        system("grub-bios-setup", "-d", dir, root) or raise "Failed to run grub-bios-setup"
-        rootfs_uuid=`blkid -o value -s UUID #{devices[1]}`.chomp("\n")
+        system("grub-bios-setup", "-d", dir, root_dev) or raise "Failed to run grub-bios-setup"
+        fs_uuids = devices.map { |d| `blkid -o value -s UUID #{d}`.chomp("\n") }
+        rootfs_uuid = fs_uuids.first
         puts "New rootfs UUID=#{rootfs_uuid}"
         begin
-          system("mount", devices[1], dir)
+          system("mount", devices.first, dir) or raise "Failed to mount #{devices.first} to #{dir}"
+          dirs[1..-1].each_with_index do |di, idx|
+            system "mkdir", "-p", "#{dir}#{di.path}"
+            system("mount", di.device, "#{dir}#{di.path}") or raise "Failed to mount #{di.device} to #{dir}#{path}"
+          end
 
           puts "Rewrite fstab..."
-          fstab = File.read "#{dir}/etc/fstab"
-          fstab.gsub!(%r{^(UUID=|/)\S+(\s+/\s+)}, "UUID=#{rootfs_uuid}\\2")
-          fstab.gsub!(%r{^(\S+\s+\S+\s+\S+\s+sw(?=\b))}, '#\1')
-          File.write "#{dir}/etc/fstab", fstab
+          File.open "#{dir}/etc/fstab", "w" do |f|
+            devices.map.with_index { |d, idx|
+              f << %W(UUID=#{fs_uuids[idx]} #{dirs[idx].path} ext4 defaults,noatime 0 #{dirs[idx].path == '/' ? 1 : 2}).join("\t") 
+              f << "\n"
+            }
+          end
 
           unless File.exists? "#{dir}/vmlinuz"
             system("chroot", dir, "apt-get", "-qy", "update")
@@ -144,7 +154,7 @@ class ImageCreator
             system({'DEBIAN_FRONTEND' => 'noninteractive'}, "chroot", dir, "apt-get", "-y", "install", "grub-pc")
             File.write "#{dir}/boot/grub/grub.cfg", <<-EOC
               set timeout=5
-              insmod part_gpt
+              insmod part_msdos
               insmod ext2
               insmod linux
               search --no-floppy --fs-uuid --set=root #{rootfs_uuid}
@@ -155,6 +165,9 @@ class ImageCreator
             EOC
           end
         ensure
+          dirs.reverse[0..-2].each do |di, idx|
+            system("umount", "#{dir}#{di.path}")
+          end
           system("umount", dir)
         end
       end