主题:【原创】如何解开Ubuntu的initrd.img和Gentoo的initramfs -- wsxx
你为什么要这么干?因为想DIY自己的Linux的启动过程。
对于我来说,我最近的目的是把Gentoo安装到移动硬盘上,让电脑直接从移动硬盘启动。做成这样的一个Linux之后,我可以拿着硬盘到不同的机器上使用,甚至在不支持USB启动的老机器上使用(当然它得有USB接口)。
如何在移动硬盘上安装我的Gentoo,参见
在内核启动之后的几秒钟之内,USB设备可能尚未被系统确认,Linux可能会提示找不到根。我们需要修改启动过程,让内核sleep几秒钟之后再继续引导,就需要修改initrd(Ubuntu是initrd.img-xxxx,Gentoo是initramfs-genkernel-xxxxx)文件中的一个叫做init的脚本(script),在其中适当的地方插入一个sleep 5让它暂停5秒钟。
要编辑init这个文件,需要把压缩的initrd解开,找到其中的init进行修改,然后再把修改好的文件连同一起解开的宋有文件再打包成initrd,更换掉原来的initrd或者使用新的文件名,在启动菜单中增加新的指向。后者是保险的方法,免得万一没搞好,原来的又没了。
现在的问题是initrd文件如何解开。
我看了不少网上的文章,介绍的基本上都是针对Linux 2.4之前的方法,那时的打包方法是把所有文件做成镜像文件,再用gzip压缩。打开那种initrd文件的过程就是先gunzip,把解压的文件挂载到一个目录中,就可以对目录中的任何文件进行阅读和修改了。
但是,我开始学Linux的时候已经是2.6版推出之后了。使用上面的方法完全无法解开initrd文件,第一步gunzip可以,后面的挂载就怎么弄都是错的,修改其中的文件也就无从谈起。
这种情况持续了可能有一个月,每天翻来覆去mount -o loop -t filetype都不行,在网上查来查去也还是这个办法。
昨天,在google的搜索结果的第三页之后随便按了一个很不起眼的链接,看到一篇文章竟然有解决办法。原来2.6版开始,不再用镜像的方法制作initrd了,据分析说是为了简化制作步骤。新的制作方法采用cpio的方式制作。
接下来,我将把Ubuntu和Gentoo的解压发放介绍一下,两者还有不同,这又花了我很长时间才搞个半懂。
(待续)
搞不懂老兄一年到头怎么花那么多时间净琢磨这些东西啊??12万分的佩服!!!
可惜那个ID被人抢注了
Ubuntu的initrd.img可以在/boot中找到,通常文件名后面还跟有很长的一串版本号。
为了保险起见,不直接操作原文件,而是把它复制到自己的家目(home)录中。如果你是用root帐号登录的,家目录就在/root中,如果是用wsxx登录的,家目录一般就在/home/wsxx中,通常登录之后自动就到了家目中。我们把initrd.img复制但家目中进行解压:
cp /boot/initrd.img-2.6.15-ubuntu-r6 ./initrd.img.gz
上面这个命令把/boot区中的文件复制到当前目录,并更名为initrd.img.gz。一方面改成短文件名好一点好操作,另一方面加上gz的后缀更清楚表明它原本就是一个gzip压缩出来的文件。
然后解压:
gunzip initrd.img.gz
也可以:
gzip -d initrd.img.gz
两者结果是相同的,都是在当前目录得到一个解压后的initrd.img,原来的initrd.img.gz被删除掉了(这也许是linux整洁的优点)。
现在这个更大的initrd.img要用cpio解开,成为一系列目录和文件。为了不与当前目录中现有的文件搞混,我们有必要新建一个目录,把initrd.img解压到新目录中去。将来把里面的文件修改好之后,还要把所有的目录文件再打包起来:
mkdir initrd #建立目录
cd initrd #进入目录
cpio -i -d < ../initrd.img #解开上层目录中的initrd.img
因为已经进入到initrd中,../initrd.img表示上层目录中的initrd.img。
现在就可以看到initrd中各目录中有很多新的目录和文件了。在这里我们可以窥视到ubuntu是如何装配起来的。可以对其中的内容进行修改了。
修改文本文件没有什么好多说的了。
修改之后,就是压缩回去,用它来启动,检验是否可以正常启动,是否达到预期的修改目的。先用cpio打包:
find . | cpio -o -H newc > ../myinitrd.img #打包当前目录中的所有目录和文件,到上层目录中的myinitrd.img
cd .. #回到上层目录
gzip -9 myinitrd.img #gzip的最高级压缩
得到的myinitrd.img.gz就是新的Ubuntu启动文件了。
做到这里,要有必要停下来看看一看,比较一下重新压缩之后的文件,是否和原来的initrd.img.gz差不多大小?都应该是4M多的文件。如果文件大小相差太多,可能就有问题。我用Ubuntu文件解压后在压缩回来,用不同的文件名,最后比较,大小完全一致,心里就踏实了。
再用Gentoo的initramfs文件进行同样方法的操作,2M多的文件解压再压缩回来,只剩下不到1K了,自己也不相信这是对的。
(待续)
解压Gentoo的initramfs方法,与遵循常规方法的Ubuntun略有不同。
我们先来看看问题在哪里:
cp /boot/initramfs-genkernel-x86-2.6.17-gentoo-r4 /initramfs.gz # 复制到当前家目录,简化文件名
gunzip initramfs.gz # gunzip解压
mkdir initrd #建立一个新目录
cd initrd #进入新目录
cpio -i -d < ../initramfs #解开上层目录中的initramfs
也可以这样:cat ../initramfs |cpio -id
可是结果是显示5 blocks,也就是说解压出来的内容一共才有5K大小!这显然是有问题的,原始的initramfs.gz有2M多,gunzip解压之后有4M多,可是怎么可能cpio解压之后只有5K呢?Ubuntu解压出来之后有12M多呢。
这个问题不解决,修改init是没有用的。曾经修改之后再打包压缩回去,剩下不到1K的内容。如果拿它去启动,不知道会是什么样子。
被这个问题困扰的人不多也不少,在网上遇到问题求问的不少,有些人似乎找到了答案,但是也不是最终正确的。例如有人觉察到cpio打开initramfs之后遇到TRAILER解包就结束了,所以来了个我称作的“二次解包法”:
cat ../initramfs |(cpio -id;cpio -id)
这样可以解出343 blocks。我第一次以为这样算是可以了,后来才明白 1个block其实只有1K,343K是比5K大了不少,但是距离成千上万K的预期值还相去甚远。
继续在网上翻,在几乎绝望的状态之下,很偶然地点击了google搜索结果第n页的一个不起眼的链接,里面竟然有正解!
对于Linux比较熟练的人看了上面的链接就知道自己做了,我是琢磨了一番才搞明白的。新手们可能还需要解释一番,我回头再写一个详细一点的解释和示例。
(待续)
在外链出处这篇文章中说到,Gentoo的initramfs文件中有TRAILER的问题,cpio碰到这些东西就停止了解压。该作者写了一个小小的脚本,用来修理TRAILER:
awk '
BEGIN{RS="07070"}
{
if(/TRAILER\!\!\!/){y=""}else{y=RT}
if(RT!=""){x=x gensub(/^.*TRAILER\!\!\!.*$/,"",1) y}else{x=x $0}
}
END{print x}' $1
这个脚本的正则表达式使用得出神入化,我们恐怕一时难以搞明白,先这么使用着吧,等有空再回来琢磨。
我们使用的时候,可以把以上内容保存为一个文本文件,按照作者的方法,命名为fixcpio,保存在家目录中。要让这个脚本可以被执行,还必须给它加上可执行的属性:
然后就可以用它来做为解包过程中的一环:
cd initrd #进入工作目录
cpio -id <../initramfs-fixed # 解压上层目录的文件到本目录中
initramfs-fixed就是修理过的cpio格式的文件,最后一步就和普通的cpio解包过程相同了。
总结一下,可以把两步合并成一步,如果已经进入工作目录,只要这样就可以了:
同理,修改init之后,再打包回去,只要这样一步就可以了:
重新打包当然就没有修理一说了。在上层家目录里将会得到myinitramfs.gz,这是可以用来启动的文件了。
简化的方法也是用于Ubuntu。
(完)
在解压的过程中,我碰到问题的时候,花了很多时间在试验各种命令的选项和参数上了,又花了很多时间去查文献,虽然这些都是值得的,但是回想起来不是很科学的方法,效率很低。
碰到这种情况,首先应该从大局出发,摸清它是什么类型的文件,知道了对手的情况才好下手解压。具体来说,要知道initramfs这个文件是什么类型的文件,不应该上去就解压,除非你已经确定知道它的类型,应该使用file这个命令先做一下火力侦察:
系统报告:
这是一个gzip压缩文件,用gzip/gunzip解压。
而后
系统报告:
原来这是cpio文档,我们很快就会找到解压的办法,就是用cpio去解压,而不是把它当作镜像文件去解。
也许这是事后诸葛亮了,但愿以后碰到问题不会这么费劲。
(完)
花