ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

将pebuilder变成dibuilder.sh,将di tools集入boot层(5):为离线镜像安装引导

2021-02-14 04:33:15  阅读:224  来源: 互联网

标签:tmp grub img di boot pebuilder 离线 && x00


本文关键字:通过DD安装grub代替grub-install,grub离线安装技术,restore grub2 installation using dd,bootice工具原理

在《pebuilder变成dibuilder.sh,将di tools集入boot层4》的中间,我们提到过暂代方案的思路,这种思路的第一步,就是用grub作通用booter代替难实现的虚拟booter,为此我们在上文提出了初步的在dibuilder.sh中为离线镜像实现离线安装grub2的代码,----- 在整个《pebuilder变成dibuilder.sh,将di tools集入boot层》系列中,如果说(4)是(2)的增强继续,那么本文(5)就是对(3)的增强继续。前面准备好了所有debian dists(di,pve),我们最终的目的,是要在这些dists的基础上组装minstackos,并使dibuilder.sh中的生成raw镜像/localinstall生效,实现最终能安装在mbp实机上测试,这是后话

(为什么是di,pve这二者,因为我感觉debian installer的装机方案和pve的bearmetal hypervisor+lxc as app container越来越顺眼了,毕竟,用grub+dipe+pve作为多区段镜像第一个分区完全可以替换实现虚拟boot+payload的伪不死boot效果(这也可以理解为"在一块flash上装一个类似di的pe":类似白群将synope写在nand rom中),而debian pve也可以代替vm hypervisor inside boot to replace libos这些复杂目前还没用起来的东西),况且,我们也有dibuilder.sh对接),最后,过度虚拟化也真的不好用,这也是用grub代替虚拟boot作通用boot,及用lxc代替docker这种不好instant commit的原生云部署容器的又一原因。这些暂代方案的思路,贯穿了整个《minstackos的实践部分》

剩下的问题是考虑更好的dibuilder.sh对接,比如对接上文,在dibuilder.sh中继续实现为离线镜像生成grub2引导的问题提供方案。

好了,动工

测试研究mbr脚本定制,得到core_img

为什么要得到一个core_img,这个问题从何而来?先来看下grub2这个东西,它就像deb的动态control文件,我们要把离线镜像集成grub,必须要得到一个为此镜像预安装了的grub2,这绝非拷贝/boot/grub下的文件那么简单,

关于grub2 mbr安装
grub-install只是一个脚本,内部真正执行工作的是grub-mkimage和grub-setup,grub2-install 脚本的工作包括调用grub2-probe扫描计算机并收集磁盘和分区信息,调用grub2-mkimage构建一个基于/boot/grub下脚手架文件之上的新的new.img,即core.img,grub-mkimage的过程形如grub-mkimage -d x86_64-efi -p /boot/grub -o grubx64.efi -O x86_64-efi acpi all_video bitmap bitmap_scale blocklist exfat expr ....一般地,仅 fshelp ext2 part_msdos biosdisk就够了,最后grub2-setup把grub的boot.img写入MBR中,把core.img写进设备的第一个扇区
在BIOS平台下,boot.img是grub启动的第一个img文件,它被写入到MBR中或分区的boot sector中,因为boot sector的大小是512字节,所以该img文件的大小也是512字节。boot.img唯一的作用是读取属于core.img的第一个扇区并跳转到它身上,将控制权交给该扇区的img。由于体积大小的限制,boot.img无法理解文件系统的结构,因此grub2-install将会把core.img的位置硬编码到boot.img中(这并不一定就是物理扇区号,可能是虚拟offset),这样就一定能找到core.img的位置。
关于bios下写入booter的复杂性,其实我们在装黑苹果(1)处理clover的boot的biosdisk时候也遇到了。还有上次《普化群晖将其改造成正常磁盘布局及编译源码打开kernel message》说到grub-install在/dev/loop上失效,可能是在host上dev map string没有绑定https://itectec.com/superuser/how-to-install-grub-into-an-img-file/。可能需要mkdir -p /mnt/boot/grub,cat 进 /mnt/boot/grub/device.map:(hd0) /dev/loop0,(hd0,1) /dev/loop1等正确内容

这也就是说,(以上这是一个与具体磁盘绑定的动态配置过程)磁盘上的/boot/grub/i386-pc/*.img的这些文件全是脚手架文件,本质起作用的是具体磁盘环境下已经被通过grub-install嵌写入磁盘结构,我们的工作就是要将这个动态过程,变成静态的通过可恢复文件方式就能完成安装的grub-install-static。却要求能配合我们稍后以自由方式生成的1g的boot区一起工作。(而且这又有限制和条件:在dibuilder.sh中我们基于加入了osx考虑不打算用grub-install)接下来你会看到,正确的boot.img容易生成,而core.img却难于得到。

所幸我们得到了一个脚本:

bootinfoscript,https://github.com/arvidjaar/bootinfoscript,它会显示一个主输出,同时在/tmp下保留大量有价值的临时文件(如解包的core.img),及其它输出(如Trash),它给出了一些bash算法,很为珍贵,以下是二个重要函数:

grub2_read_blocklist () 

这个函数整理hdd上存在于旧core.img->第一个sector中的diskboot.img未端部分->里的fragment信息,收集重组为新core.img上的fragments并写入,未来供grub2_info用
参数中的hdd也可能是硬盘(full core.img from blocklists,因为多个分区上都有可能有引导目录和core.img)也可能是单个普通core.img文件,后者情形我们不考虑,因为我们基本都是在读写磁盘嵌入区的这些文件区段,而不是文件系统中的这些文件。

grub2_info ()

这个函数用上一步得到的新core.img fragment信息,进一步,求得boot.img里core.img里的位置,以及更多信息(core.img中的文件夹信息,模块,配置文件),完成整个grub解析工作
stage1即是boot.img或/dev/sda mbr区,grub2的core.img是stage2,这种说法引用了grub1的stage1,2

但是bootinfoscript的实现比较理想化,依赖linux,比如它用到filefrag(apt-get install binutils for strings command),应该换成用string代替加判断才更为portable。

无论如何,我们从这个脚本和它的输出中希望得到的帮助是:它可以用于为我们的镜像输出core_img。供下一部分直接dd,而实际上,它也的确可以办到:我们可以在生成的虚拟镜像中安装一个linux,使之形成一个能启动的/dev/sda或其它需要的分区结构,在其中测试bootinfoscript,提取/tmp下的core_img。

2,实际修改dibuilder.sh并得到boot.img

得到core_img,我们接下来修改dibuilder.sh的对接部分,首先修改dibuilder.sh local remastering and install支持部分,修改得到一套较为规则的/tmp/tmpdown->/tmp/tmpremastering->/tmp/tmpmnt target or /boot target的文件夹逻辑结构:

首先,原来prepare misc中的net,grub部分中的grub归入接下来的remastering all up部分,整个脚本只有三部了,这是新的整个第三部分,


echo -e "\n \033[36m # Remastering all up... \033[0m \n"

remasteringdir='/tmp/tmpremastering';
[[ -d $remasteringdir ]] && rm -rf $remasteringdir;

sleep 2s && echo -en "\runpacking grub files ..."

export tmpMNT=/tmp/tmpmnt

[[ "$tmpMODE" == '1' && "$tmpTARGET" == 'minstackos' ]] && {
    
  prepareimg() {
    tmpDEV=$(mount | grep "$tmpMNT" | awk '{print $1}')

    [ -z "$tmpDEV" ] && {
      rm -rf /tmp/tmpimage.raw
      dd if=/dev/zero of=/tmp/tmpimage.raw bs=1024 count=1048576 >/dev/null 2>&1
      [[ "$tmpPLAT" == "0" ]] && tmpDEV=`losetup -fP --show /tmp/tmpimage.raw | awk '{print $1}'` || tmpDEV=`hdiutil attach -imagekey diskimage-class=CRawDiskImage -nomount /tmp/tmpimage.raw | awk '{print $1}'`

注意osx与linux在工具能力上的差别和分区控制方面的差别。通常我们必须在mbr和第一个分区开始前预留一定空间放core.img,一般grub-install会把core.img写入到第一个分区,但是我们放在这个预留一些空白区以更方便控制和定制,比如刻意向我们的离线grub2合成方向靠拢

      [ -n "$tmpDEV" ] && {
        [[ "$tmpPLAT" == "0" ]] && parted -s "$tmpDEV" mktable msdos >/dev/null 2>&1 && parted -s "$tmpDEV" mkpart primary ext3 2048s 100% >/dev/null 2>&1 && parted -s "$tmpDEV" set 1 boot on >/dev/null 2>&1 && mkfs.ext3 "$tmpDEV"p1 >/dev/null 2>&1
        [[ "$tmpPLAT" == "1" ]] && diskutil partitionDisk "$tmpDEV" MBR FREE %noformat% 1048576 "MS-DOS FAT16" TMPVOL 0 >/dev/null 2>&1
      }

      [ ! -d "$tmpMNT" ] && [[ "$tmpPLAT" == "0" ]] && mkdir "$tmpMNT" && mount "$tmpDEV"p1 "$tmpMNT" || tmpMNT=/Volumes/TMPVOL;[[ ! -d /tmp/tmpmnt ]] && ln -s /Volumes/TMPVOL /tmp/tmpmnt


      #echo "tmpdev is:""$tmpDEV"
      #echo "tmpmnt is:""$tmpMNT"

      #echo "- before installing/remastering grub,unzip it"
      mkdir -p /tmp/tmpremastering/grub
      tar -xf /tmp/tmpdown/debian/dists/jessie/main/binary-amd64/grub.gz -C /tmp/tmpremastering/grub

    (这里放接下来得到boot.img和离线镜像写grub的过程,这里的内容)

进生成core_img所在系统,针对/dev/sda得到其mbr(不要用脚本手架文件中的boot.img,开头字节是空的),得到原始字串并处理:sudo hexdump -v -n 446 /dev/sda | (手动处理掉换行,索引,并删掉最后一行) | sed -e "s/ //g" | sed -e "s/\([0-9a-z][0-9a-z]\)/\1 /g" |  sed -e "s/ /\\\x/g | sed -E 's/(\\x..)(\\x..)/\2\1/g'"  
(You can use xxd to dump binary files just like hexdump and od, but you can also use it to do the reverse: turn a hex dump back into binary. If y)

得到下列待处理字符串命名为grub202:


grub202='\xeb\x63\x90\x10\x8e\xd0\xbc\x00\xb0\xb8\x00\x00\x8e\xd8\x8e\xc0\xfb\xbe\x00\x7c\xbf\x00\x06\xb9\x00\x02\xf3\xa4\xea\x21\x06\x00\x00\xbe\xbe\x07\x38\x04\x75\x0b\x83\xc6\x10\x81\xfe\xfe\x07\x75\xf3\xeb\x16\xb4\x02\xb0\x01\xbb\x00\x7c\xb2\x80\x8a\x74\x01\x8b\x4c\x02\xcd\x13\xea\x00\x7c\x00\x00\xeb\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00\x00\x00\x00\x00\x00\xff\xfa\x90\x90\xf6\xc2\x80\x74\x05\xf6\xc2\x70\x74\x02\xb2\x80\xea\x79\x7c\x00\x00\x31\xc0\x8e\xd8\x8e\xd0\xbc\x00\x20\xfb\xa0\x64\x7c\x3c\xff\x74\x02\x88\xc2\x52\xbb\x17\x04\xf6\x07\x03\x74\x06\xbe\x88\x7d\xe8\x17\x01\xbe\x05\x7c\xb4\x41\xbb\xaa\x55\xcd\x13\x5a\x52\x72\x3d\x81\xfb\x55\xaa\x75\x37\x83\xe1\x01\x74\x32\x31\xc0\x89\x44\x04\x40\x88\x44\xff\x89\x44\x02\xc7\x04\x10\x00\x66\x8b\x1e\x5c\x7c\x66\x89\x5c\x08\x66\x8b\x1e\x60\x7c\x66\x89\x5c\x0c\xc7\x44\x06\x00\x70\xb4\x42\xcd\x13\x72\x05\xbb\x00\x70\xeb\x76\xb4\x08\xcd\x13\x73\x0d\x5a\x84\xd2\x0f\x83\xd0\x00\xbe\x93\x7d\xe9\x82\x00\x66\x0f\xb6\xc6\x88\x64\xff\x40\x66\x89\x44\x04\x0f\xb6\xd1\xc1\xe2\x02\x88\xe8\x88\xf4\x40\x89\x44\x08\x0f\xb6\xc2\xc0\xe8\x02\x66\x89\x04\x66\xa1\x60\x7c\x66\x09\xc0\x75\x4e\x66\xa1\x5c\x7c\x66\x31\xd2\x66\xf7\x34\x88\xd1\x31\xd2\x66\xf7\x74\x04\x3b\x44\x08\x7d\x37\xfe\xc1\x88\xc5\x30\xc0\xc1\xe8\x02\x08\xc1\x88\xd0\x5a\x88\xc6\xbb\x00\x70\x8e\xc3\x31\xdb\xb8\x01\x02\xcd\x13\x72\x1e\x8c\xc3\x60\x1e\xb9\x00\x01\x8e\xdb\x31\xf6\xbf\x00\x80\x8e\xc6\xfc\xf3\xa5\x1f\x61\xff\x26\x5a\x7c\xbe\x8e\x7d\xeb\x03\xbe\x9d\x7d\xe8\x34\x00\xbe\xa2\x7d\xe8\x2e\x00\xcd\x18\xeb\xfe\x47\x52\x55\x42\x20\x00\x47\x65\x6f\x6d\x00\x48\x61\x72\x64\x20\x44\x69\x73\x6b\x00\x52\x65\x61\x64\x00\x20\x45\x72\x72\x6f\x72\x0d\x0a\x00\xbb\x01\x00\xb4\x0e\xcd\x10\xac\x3c\x00\x75\xf4\xc3\xe1\x75\x0e\x09\x00\x00'
#(确认得到的显示正确,grub有特征码字串)
#printf $grub202 | hexdump -C

#虚拟offset号为1,无须处理
#printf $grub202 | hexdump -v -n 1 -s 92
#$grub202=`printf $grub202 | sed -e 's/\(x[0-9a-z][0-9a-z]\)/x01/93'`
#printf $grub202 | hexdump -v -n 1 -s 92

#添加可启动标识
#printf $grub202 | hexdump -v -n 1 -s 447
grub202+='\x80'
#printf $grub202 | hexdump -v -n 1 -s 447

diskutil unmountDisk "$tmpDEV"
printf $grub202 | dd of="$tmpDEV" bs=447 count=1
#确认已写入
hexdump -v -n 512 -C $tmpDEV

#core.img无特征码字串,前面10个字节判断
dd if=/tmp/tmpremastering/grub/i386-pc/core.img of="$tmpDEV" seek=512 bs=25760 count=1 conv=notrunc
hexdump -v -n 10 -C /tmp/tmpremastering/grub/i386-pc/core.img
hexdump -v -s 512 -n 10 -C $tmpDEV
diskutil mountDisk "$tmpDEV"

正式脚本中记得把上面调试输出适当注释掉



    }

    sleep 2s && echo -en "[ \033[32m /tmp/tmpremastering/grub/* \033[0m ]"

    # Automatically remove DISK on exit
    [[ "$tmpPLAT" == "0" ]] && trap 'echo; echo "- Ejecting tmpdev disk"; umount "$tmpMNT" && losetup -d "$tmpDEV"' EXIT || trap 'echo; echo "- Ejecting tmpdev disk"; diskutil eject "$tmpDEV";rm -rf /tmp/tmpmnt' EXIT
  }
  prepareimg
}


原grub remastering部分

LoaderMode='0'
setInterfaceName='0'
setIPv6='0'

......

    sed -i '$a\\n' /tmp/tmpremastering/grub/grub.new;
  fi

  sleep 2s && echo -en "[ \033[32m /tmp/tmpremastering/grub/grub.new \033[0m ]"

}

 在这里才写入脚手架文件,一切都是为了让down,remastering,copying to target这三部分清淅分开

sleep 2s && echo -en "\nprocessing grub ......"
[[ -d /Volumes/TMPVOL ]] && [[ "$tmpPLAT" == "1" ]] && mkdir -p "$tmpMNT"/boot/grub && cp -a /tmp/tmpremastering/grub/* "$tmpMNT"/boot/grub


[[ "$tmpINSTANTWITHOUTVNC" == '0' ]] && {

  GRUBPATCH='0';

  if [[ "$LoaderMode" == "0" && "$tmpPLAT" != "1" ]]; then

......

    [[ -f  $GRUBDIR/grubenv ]] && sed -i 's/saved_entry/#saved_entry/g' $GRUBDIR/grubenv;
  fi


开始remaster initrd部分的解压步骤

  sleep 2s && echo -en "\nunpacking initrd ......"

  mkdir -p /tmp/tmpremastering/initrd/usr/bin;
  cd /tmp/tmpremastering/initrd;

.....

        [[ "$tmpMODE" != "2" ]] && [[ "$tmpTARGET" == "minstackos" ]] && cp -f "$downdir/dists/jessie/main/binary-amd64/initrd.img" "/tmp/tmpremastering/$NewIMG"
          [[ "$tmpPLAT" != "1" ]] && [[ "$tmpMODE" == "2" ]] && cp -f "$downdir/dists/jessie/main/debian-installer/binary-amd64/initrd.img" "/tmp/tmpremastering/$NewIMG"

......

  [[ "$tmpPLAT" == '0' ]] && $UNCOMP < /tmp/tmpremastering/$NewIMG | cpio --extract --verbose --make-directories --no-absolute-filenames >>/dev/null 2>&1 || $UNCOMP < /tmp/tmpremastering/$NewIMG | cpio --extract --verbose --make-directories >>/dev/null 2>&1

  [[ -f '/boot/firmware.cpio.gz' ]] && {
    gzip -d < /boot/firmware.cpio.gz | cpio --extract --verbose --make-directories --no-absolute-filenames >>/dev/null 2>&1
  }


    sleep 2s &&   echo -en "\nprocessing initrd ...."

    find $downdir/dists/jessie -type f \( -name *.deb -o -name *.ldeb \) | while read line; do line2=${line##*/};echo -en "\033[s \033[K [ \033[32m ${line2:0:40} \033[0m ] \033[u";[[ $(ar -t ${line} | grep  -E -o data.tar.gz) == 'data.tar.gz' ]] && ar -p ${line} data.tar.gz |zcat|tar -xf - -C /tmp/tmpremastering/initrd || ar -p ${line} data.tar.xz |xzcat|tar -xf - -C /tmp/tmpremastering/initrd; done


......


    sleep 2s &&   echo -en "\nmake a safe wget wrapper to inc --no-check-certificate"

    mv /tmp/tmpremastering/initrd/usr/bin/wget /tmp/tmpremastering/initrd/usr/bin/wget2
    cat >/tmp/tmpremastering/initrd/usr/bin/wget<<EOF
#!/bin/sh
rdlkf() { [ -L "\$1" ] && (local lk="\$(readlink "\$1")"; local d="\$(dirname "$1")"; cd "\$d"; local l="\$(rdlkf "\$lk")"; ([[ "\$l" = /* ]] && echo "\$l" || echo "\$d/\$l")) || echo "\$1"; }
DIR="\$(dirname "\$(rdlkf "\$0")")"
exec /usr/bin/env wget2 --no-check-certificate "\$@"
EOF
    chmod +x /tmp/tmpremastering/initrd/usr/bin/wget
  fi


清楚的remasterin /copying分离

  echo -en "\ncopying vmlinuz to the target/mnt ......"
  [[ -d /boot ]] && [[ "$tmpMODE" != "2" ]] && [[ "$tmpTARGET" == "minstackos" ]] && cp -f "$downdir/dists/jessie/main/binary-amd64/vmlinuz" /boot/vmlinuz
  [[ -d /boot ]] && [[ "$tmpPLAT" != "1" ]] && [[ "$tmpMODE" == "2" ]] && cp -f "$downdir/dists/jessie/main/debian-installer/binary-amd64/vmlinuz" /boot/vmlinuz
  [[ -d /Volumes/TMPVOL ]] && [[ "$tmpPLAT" == "1" ]] && cp -f "$downdir/dists/jessie/main/binary-amd64/vmlinuz" /Volumes/TMPVOL/vmlinuz

(全程注意新脚本变动中的文件夹新命名变化)


除此之外,脚本其实在原先第一部分checkmirros部分把主体中检测1,2都注释了,直接返回提供的mirror url,相当于没检测,这是为了适应导致下到/tmp/tmpdown里的新结构debian文件夹的源镜像变化部分。

然后在osx上brew install qemu,cd /tmp/测试。
sudo qemu-system-x86_64 -machine pc-q35-2.4 -smp 4,cores=2 -cpu Penryn,kvm=off,vendor=GenuineIntel -m 4096 -device ide-hd,bus=ide.2,drive=MacHDD -drive id=MacHDD,if=none,format=raw,file=./buildpackage.raw -monitor stdio

成功进入。继续在脚本中处理硬盘镜像。


关于整合或分开使用这几个dists,除了前面“用di代替synope进行通用式dd安装镜像”“三个initramfs合为一个”等。还有一个思路,将di U盘安装代替multiboot unetbootin或ventoy,这样,比如用grub+dibuilder.sh面向的多img为基础打造这样一个类似的本地化多启U盘呢(不考虑它们的运行),我们可以以那个1G的boot区为启动区,mbr始终是现在最得到全面兼容的格式,数据区放各种img.gz。grub中写好各种安装条目。它就是dibuilder.sh的本地版了:在前面《云主机装黑果实践(1)》我们讲到过制造一个本地多启U盘的方案。它是以iso解压出的东西为内容基础的和efi方案为基础的,(后来我们发现了ventoy,它以iso为基础的,比如osx镜像全部弄为cdr/iso了,如经过hdiutil convert -format UDRW -o cdr.dmg common.iso之后,白苹果其实也可以用,不过这二者启动和对各种ISO的支持都不太稳定,继续深入一点,dibuilder.sh提供了一种更具替代性的稳定和兼容方案。

标签:tmp,grub,img,di,boot,pebuilder,离线,&&,x00
来源: https://www.cnblogs.com/minlearn/p/14401445.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有