<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>.:: Marcos Dione/StyXman's glob ::. (Posts about sysadmin debian)</title><link>https://www.grulic.org.ar/~mdione/glob/</link><description></description><atom:link href="https://www.grulic.org.ar/~mdione/glob/categories/sysadmin-debian.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2025 &lt;a href="mailto:mdione@grulic.org.ar"&gt;Marcos Dione&lt;/a&gt; </copyright><lastBuildDate>Sat, 15 Nov 2025 20:52:05 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Create a disk image with a booting running Debian</title><link>https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/</link><dc:creator>Marcos Dione</dc:creator><description>&lt;p&gt;Recently I had to do something that sounds very simple to any good SysAdmin:
create a disk image with a booting Debian installation, from a script, with no
human interaction. The idea is to later install our software on it. Those who
want to test out soft would just need to download the image and boot it in any
virtual machine they have: &lt;code&gt;qemu&lt;/code&gt;, &lt;code&gt;virtualbox&lt;/code&gt;&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;, you name it.&lt;/p&gt;
&lt;p&gt;So the process could be thought as this: create a disk image, partition it,
install Debian, install a bootloader, profit! Let's try to tackle them
separately, looking at different approaches&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;p&gt;A disk image is simply a file big enough: 1GiB, 10GiB, whatever you want. A
string of 1Gi of 0s should be enough:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stable.img
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now, that file is using 1GiB of space, but we're not sure if we're going to use
it all, and so is kinda a waste of space. Luckly, Linux is able to handle sparse
files: files that do not reserve
all the file system blocks would normally be needed, only those where data is
written. So for instance, a way to create a 1GiB (almost) empty sparse file is
this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;seek&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stable.img
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That is, we write a 0 at the end of a 1GiB file&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt;, but even if the file is so
big, it's actually using one file system block (4096 bytes, according to
&lt;code&gt;dumpe2fs&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;A simpler, or maybe more-intuitive-when-you-read-it&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt; alternative is to use
&lt;a href="http://pierre.palats.com/scratch/index.php?post/2008/12/15/Automatically-creating-a-disk-image-with-partition-and-bootloader"&gt;a tool that comes in &lt;code&gt;qemu-utils&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;qemu-img&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;raw&lt;span class="w"&gt; &lt;/span&gt;stable.img&lt;span class="w"&gt; &lt;/span&gt;1G
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That was easy. Now, how do we partition it? The first answer it's obvious,
namely,
&lt;code&gt;fdisk&lt;/code&gt;, but it is not scriptable. So we look for alternatives, and one that
comes to mind is &lt;code&gt;parted&lt;/code&gt;: it is designed with scriptability in mind, it should
be perfect!&lt;/p&gt;
&lt;p&gt;Almost. &lt;code&gt;parted&lt;/code&gt; needs a partition table signature in the MBR&lt;sup id="fnref:5"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fn:5"&gt;5&lt;/a&gt;&lt;/sup&gt; and it has no
way to create one. This is at least surprising, but a little more (ab)use of
&lt;code&gt;dd&lt;/code&gt; can save the day. It's just a matter of writing the bytes &lt;code&gt;0x55 0xaa&lt;/code&gt;&lt;sup id="fnref:8"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fn:8"&gt;8&lt;/a&gt;&lt;/sup&gt; in
the last two bytes of the first sector of the image. A disk sector, up to
recently, is just 512 bytes, so:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"\x55\xaa"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;seek&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;510&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stable.img&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notrunc
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;notrunc&lt;/code&gt; is so &lt;code&gt;dd&lt;/code&gt; doesn't truncate the image to be 512 bytes long (it
took me a while figuring that out). Now to &lt;code&gt;parted&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;parted&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;stable.img&lt;span class="w"&gt; &lt;/span&gt;mkpart&lt;span class="w"&gt; &lt;/span&gt;primary&lt;span class="w"&gt; &lt;/span&gt;ext2&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;1G
Warning:&lt;span class="w"&gt; &lt;/span&gt;The&lt;span class="w"&gt; &lt;/span&gt;resulting&lt;span class="w"&gt; &lt;/span&gt;partition&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;properly&lt;span class="w"&gt; &lt;/span&gt;aligned&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;best&lt;span class="w"&gt; &lt;/span&gt;performance.
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;But this will rise a problem that I'll mention later, at its proper time. So
instead, and given that we're gonna install another package anyways, we're gonna
use &lt;code&gt;sfdisk&lt;/code&gt;&lt;sup id="fnref:6"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fn:6"&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;sup id="fnref:7"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fn:7"&gt;7&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;sfdisk&lt;span class="w"&gt; &lt;/span&gt;-D&lt;span class="w"&gt; &lt;/span&gt;stable.img&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt;EOF&lt;/span&gt;
&lt;span class="s"&gt;,,L,*&lt;/span&gt;
&lt;span class="s"&gt;;&lt;/span&gt;
&lt;span class="s"&gt;;&lt;/span&gt;
&lt;span class="s"&gt;;&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The next step is to format the partition inside the image. If this were a
partition image we could simply apply &lt;code&gt;mkfs.ext2&lt;/code&gt; (or whatver filesystem type
you want) to the file, because the filesystem would start from the beginning of
the file. But as this is a disk image, the partition starts at an offset from
the beginning:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;sfdisk&lt;span class="w"&gt; &lt;/span&gt;--list&lt;span class="w"&gt; &lt;/span&gt;stable.img
Disk&lt;span class="w"&gt; &lt;/span&gt;stable.img:&lt;span class="w"&gt; &lt;/span&gt;cannot&lt;span class="w"&gt; &lt;/span&gt;get&lt;span class="w"&gt; &lt;/span&gt;geometry

Disk&lt;span class="w"&gt; &lt;/span&gt;stable.img:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;130&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cylinders,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;heads,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;63&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sectors/track
&lt;span class="nv"&gt;Units&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cylinders&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8225280&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bytes,&lt;span class="w"&gt; &lt;/span&gt;blocks&lt;span class="w"&gt; &lt;/span&gt;of&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1024&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bytes,&lt;span class="w"&gt; &lt;/span&gt;counting&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;Device&lt;span class="w"&gt; &lt;/span&gt;Boot&lt;span class="w"&gt; &lt;/span&gt;Start&lt;span class="w"&gt;     &lt;/span&gt;End&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;#cyls    #blocks   Id  System&lt;/span&gt;
stable.img1&lt;span class="w"&gt;   &lt;/span&gt;*&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;+&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;129&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="m"&gt;130&lt;/span&gt;-&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="m"&gt;1044193&lt;/span&gt;+&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;83&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;Linux
stable.img2&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;-&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;Empty
stable.img3&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;-&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;Empty
stable.img4&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;-&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;Empty
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;0+&lt;/code&gt; in the third column tells us that the partition doesn't start exactly
in the cylinder 0. That would mean it starts where the MBR is. Actually it
starts in the cylinder 0 but in the second head. According to CHS reported by
&lt;code&gt;sfdisk&lt;/code&gt;, there are 63 sectors per track, so we just need to skip so many bytes:
63x512=32256. Coincidentally, &lt;code&gt;130-&lt;/code&gt; in the fourth column means that the
partition does not reach the end of cylinder 130, which is exactly what &lt;code&gt;parted&lt;/code&gt;
was complaining above&lt;sup id="fnref:9"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fn:9"&gt;9&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;To fix the aligment we will have to do it the other way around: instead of
discovering the CHS from the image size, we'll compute the image size from some
desired CHS and a minimum image size. This can be done as such:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# bytes per sector&lt;/span&gt;
&lt;span class="nv"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;512&lt;/span&gt;
&lt;span class="c1"&gt;# sectors per track&lt;/span&gt;
&lt;span class="nv"&gt;sectors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;63&lt;/span&gt;
&lt;span class="c1"&gt;# heads per track&lt;/span&gt;
&lt;span class="nv"&gt;heads&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;
&lt;span class="c1"&gt;# bytes per cylinder is bytes*sectors*head&lt;/span&gt;
&lt;span class="nv"&gt;bpc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;sectors&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nv"&gt;heads&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# number of cylinders&lt;/span&gt;
&lt;span class="nv"&gt;cylinders&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="nv"&gt;$img_size&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$bpc&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# rebound the size&lt;/span&gt;
&lt;span class="nv"&gt;img_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$cylinders&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)*&lt;/span&gt;&lt;span class="nv"&gt;$bpc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
qemu-img&lt;span class="w"&gt; &lt;/span&gt;create&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;raw&lt;span class="w"&gt; &lt;/span&gt;stable.img&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$image_size&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So we will have to somehow tell to &lt;code&gt;mkfs.ext2&lt;/code&gt; about the partition offset inside
the disk image. We can use something that we have been using unknowingly: loopback
devices. Who hasn't mounted an ISO-9660 image in the past? We used something like
this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;mount&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;loop&lt;span class="w"&gt; &lt;/span&gt;debian-505-i386-CD-1.iso&lt;span class="w"&gt; &lt;/span&gt;/mnt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is more or less equivalent to:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;losetup&lt;span class="w"&gt; &lt;/span&gt;/dev/loop0&lt;span class="w"&gt; &lt;/span&gt;debian-505-i386-CD-1.iso
mount&lt;span class="w"&gt; &lt;/span&gt;/dev/loop0&lt;span class="w"&gt; &lt;/span&gt;/mnt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Good thing is, we can tell &lt;code&gt;losetup&lt;/code&gt; to simulate the start of the device some
bytes inside the file. And given that everything is a file in Linux, we can even
chain loop devices, such as:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;losetup&lt;span class="w"&gt; &lt;/span&gt;/dev/loop0&lt;span class="w"&gt; &lt;/span&gt;stable.img
losetup&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;32256&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/dev/loop1&lt;span class="w"&gt; &lt;/span&gt;/dev/loop0
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now &lt;code&gt;/dev/loop0&lt;/code&gt; points to the disk image and &lt;code&gt;/dev/loop1&lt;/code&gt; points to the
partition. There's really not much option here, so we skip to the formatting
part, which is even more straightforward:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;mkfs.ext2&lt;span class="w"&gt; &lt;/span&gt;/dev/loop1
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now to install Debian in this beast. Here we won't be exploring much either, but
I will explain a couple of tricks I learned to complete this task successfully.
The tool of choice is &lt;code&gt;debbootstrap&lt;/code&gt;, which is able to install packages in a
directory as if it where the root partition, so we will need to mount it first:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;mount&lt;span class="w"&gt; &lt;/span&gt;/dev/loop1&lt;span class="w"&gt; &lt;/span&gt;mnt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In my case I will need to install several packages besides the base install:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;debootstrap&lt;span class="w"&gt; &lt;/span&gt;--arch&lt;span class="w"&gt; &lt;/span&gt;i386&lt;span class="w"&gt; &lt;/span&gt;--include&lt;span class="o"&gt;=&lt;/span&gt;cdbs,debhelper,libsqlite3-dev,&lt;span class="se"&gt;\&lt;/span&gt;
libssl-dev,libgstreamer-plugins-base0.10-dev,libgmp3-dev,build-essential,&lt;span class="se"&gt;\&lt;/span&gt;
linux-image-2.6-686,grub-pc&lt;span class="w"&gt; &lt;/span&gt;stable&lt;span class="w"&gt; &lt;/span&gt;mnt
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Notice that the base set of packages does not include nor a kernel or a boot
loader, because this is normally installed by Debian Installer, so I added
them to the list of packages. But this is not the only thing that the installer
does (and that there is no way to repeat besides by hand): it also sets up the
environment, users, apt config (from the ones used to install) and more. We will
have to set those by hand.&lt;/p&gt;
&lt;p&gt;Before running anything else, which will run under &lt;code&gt;chroot&lt;/code&gt;, we will
need to also setup some of the virtual filesystems that are running on a normal
GNU/Linux setup; namely, &lt;code&gt;/dev&lt;/code&gt;, &lt;code&gt;/dev/pts&lt;/code&gt; and &lt;code&gt;/proc&lt;/code&gt;. We will reuse the
host's ones, using the hability to mount a dir in another:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;mount&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/dev/&lt;span class="w"&gt; &lt;/span&gt;mnt/dev
mkdir&lt;span class="w"&gt; &lt;/span&gt;mnt/dev/pts
mount&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/dev/pts&lt;span class="w"&gt; &lt;/span&gt;mnt/dev/pts
mount&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;bind&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/proc&lt;span class="w"&gt; &lt;/span&gt;mnt/proc
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Some minimal config needed includes:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# apt&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deb http://http.us.debian.org/debian stable         main"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt;  &lt;/span&gt;mnt/etc/apt/sources.list
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deb http://security.debian.org       stable/updates main"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;mnt/etc/apt/sources.list
&lt;span class="c1"&gt;# otherwise perl complains during installation that it can't set the locale&lt;/span&gt;
&lt;span class="c1"&gt;# actually we will have to do some little more than just this; see below&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en_US.UTF-8 UTF-8"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;mnt/etc/locale.gen
&lt;span class="c1"&gt;# when installing the kernel, if this setting is not present, it thinks the&lt;/span&gt;
&lt;span class="c1"&gt;# bootloader is not able to handle initrd images[^10]&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"do_initrd = Yes"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;mnt/etc/kernel-img.conf
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So we use this basic config to complete even more the installation:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nv"&gt;chroot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"chroot mnt"&lt;/span&gt;
&lt;span class="c1"&gt;# compile the locales as per /etc/locale.gen&lt;/span&gt;
&lt;span class="nv"&gt;$chroot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;locale-gen
&lt;span class="c1"&gt;# download package definitions&lt;/span&gt;
&lt;span class="nv"&gt;$chroot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;update
&lt;span class="c1"&gt;# resolve virtual packages and finish the setup of packages&lt;/span&gt;
&lt;span class="nv"&gt;$chroot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;-f&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;--force-yes&lt;span class="w"&gt; &lt;/span&gt;install
&lt;span class="c1"&gt;# while we're at it, install upgrades&lt;/span&gt;
&lt;span class="nv"&gt;$chroot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;apt-get&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;--force-yes&lt;span class="w"&gt; &lt;/span&gt;upgrade
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The last step is to install a bootloader. Here we have several options. &lt;code&gt;lilo&lt;/code&gt; was
the first Linux bootloader, which was started in 1992. Even if it can
bootload almost any operating system in lots of filesystems, one of its
main drawbacks is it staticness: it reads a config file, compiles the bootloader
and installs it. After that you can't change anything (except for adding more
boot parameters to the kernel), so if you wrote something wrong and your system
does not boot, it's hard to recover. Also, if you change anything in the config file,
you have to compile and install the bootloader again.&lt;/p&gt;
&lt;p&gt;The second and third options are the two
flavors of &lt;code&gt;grub&lt;/code&gt;, the GNU GRand Unified Bootloader. The first iteration of &lt;code&gt;grub&lt;/code&gt;,
&lt;code&gt;grub1&lt;/code&gt; or &lt;code&gt;grub-legacy&lt;/code&gt; how it is called now, is no longer under development or
support, but a lot of people still use it for its simplicity and power. First
developed in 1999, it has the hability to read the config file at boot time and
it lets edit it and read the filesystems before booting. Its successor, &lt;code&gt;grub2&lt;/code&gt;
or &lt;code&gt;grub-pc&lt;/code&gt;, is even more modular and flexible, but takes time to relearn it.&lt;/p&gt;
&lt;p&gt;Even with this last two options, I couldn't managed to reliably get a booting
image. To be fair, I managed to do it with &lt;code&gt;grub-pc&lt;/code&gt;, but my script had to work
in a machine that boots with &lt;code&gt;grub-legacy&lt;/code&gt;. Installing both at the same time is
impossible, and I need to use the host's bootloader because I can't reliably
fake the devices in a &lt;code&gt;chroot&lt;/code&gt;ed environment and using any virtual machine was
imposible because the image doesn't boot yet! Talk about chicken and eggs...
For the record, here's how I managed to make it work with &lt;code&gt;grub-pc&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;grub-install&lt;span class="w"&gt; &lt;/span&gt;--root-directory&lt;span class="o"&gt;=&lt;/span&gt;mnt/&lt;span class="w"&gt; &lt;/span&gt;--no-floppy&lt;span class="w"&gt; &lt;/span&gt;--modules&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'part_msdos ext2'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/dev/loop0
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So, I needed to find a bootloader that could be installed in the host machine
without changing the actual bootloader in use. Luckily I talked to a friend
sysadmin/guru, Ignacio Sánchez, which pointed me to &lt;code&gt;extlinux&lt;/code&gt;, which is part
of the &lt;code&gt;syslinux&lt;/code&gt; family of bootloaders. This family also includes &lt;code&gt;isolinux&lt;/code&gt;,
famously known for booting the iso images of most of the distributions for years.
I knew about the latter two, and I even used &lt;code&gt;syslinux&lt;/code&gt; in a company I worked for
two years ago in a floppy disk (!!!) used to boot the old firewall&lt;sup id="fnref:12"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fn:12"&gt;12&lt;/a&gt;&lt;/sup&gt; and another
set of diskettes for two diskless thin clients. &lt;code&gt;extlinux&lt;/code&gt; is
the youngest of the family, which is able to read and boot from &lt;code&gt;extX&lt;/code&gt; partitions.
The config file looks like a very simple &lt;code&gt;lilo.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;default&lt;span class="w"&gt; &lt;/span&gt;Hop
timeout&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;

label&lt;span class="w"&gt; &lt;/span&gt;Hop
&lt;span class="w"&gt;    &lt;/span&gt;kernel&lt;span class="w"&gt; &lt;/span&gt;/boot/vmlinuz-2.6.28-5
&lt;span class="w"&gt;    &lt;/span&gt;append&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;initrd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/boot/initrd.img-2.6.28-5&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;root&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;e3447f08-f8b2-4c25-93e4-76420c467384&lt;span class="w"&gt; &lt;/span&gt;ro
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The UUID can be obtained whit this command:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;blkid&lt;span class="w"&gt; &lt;/span&gt;/dev/loop1
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Installing it actually consists of two steps: first installing MBR code that
boots from the partition marked as bootable&lt;sup id="fnref:11"&gt;&lt;a class="footnote-ref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fn:11"&gt;11&lt;/a&gt;&lt;/sup&gt;. The &lt;code&gt;syslinux&lt;/code&gt; family comes with
such a MBR code, so we use it:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/lib/extlinux/mbr.bin&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stable.img&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;notrunc
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We're almost there. Installing &lt;code&gt;extlinux&lt;/code&gt; is really straightforward:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;extlinux&lt;span class="w"&gt; &lt;/span&gt;--heads&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$heads&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--sectors&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$sectors&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--install&lt;span class="w"&gt; &lt;/span&gt;mnt/boot/syslinux
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It only rests to umount and dismantle the loop devices in the reverse order:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;umount&lt;span class="w"&gt; &lt;/span&gt;mnt/
losetup&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;/dev/loop1
losetup&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;/dev/loop0
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Sometimes you need to wait a couple of seconds between these commands, because
they seem to be asynchronous. Otherwise you'll get errors that the device is
still busy, because the previous command has finished, but the async process in
the kernel has not.&lt;/p&gt;
&lt;p&gt;The image as it is is bootable with &lt;code&gt;qemu&lt;/code&gt; and &lt;code&gt;virtualbox&lt;/code&gt;, but if you want to
make it bootable in other, closed virtual machines, you must convert it to &lt;code&gt;vmdk&lt;/code&gt;.
&lt;code&gt;qemu-utils&lt;/code&gt; to the rescue again:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;qemu-img&lt;span class="w"&gt; &lt;/span&gt;convert&lt;span class="w"&gt; &lt;/span&gt;-O&lt;span class="w"&gt; &lt;/span&gt;vmdk&lt;span class="w"&gt; &lt;/span&gt;stable.img&lt;span class="w"&gt; &lt;/span&gt;stable.vmdk
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I have lots of things more to mention, but this post has got long enough as it
is. Mostly they were references to the sites I got info from, but I know that if
I try to clean it up I will procrastinate it for another month or so and probably
forget about it.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Currently this needs an image conversion. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fnref:1" title="Jump back to footnote 1 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Of course, I strongly recommend to check the manpages of the mentioned
tools. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fnref:2" title="Jump back to footnote 2 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Quick, which is the actual size of the file? You can answer with powers of 2
if it makes it easier for you :) &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fnref:3" title="Jump back to footnote 3 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;I have the tendency to write as-understandable-as-possible code; that means,
I know that I'll have to read and try to understand it 6 months after I wrote
it, soI try to
make it as readable as possible. That includes using long options when I
invoke tools in scripts and, of course, sensible class and variable names. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fnref:4" title="Jump back to footnote 4 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;Master Boot Record, the first sector in a disk. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fnref:5" title="Jump back to footnote 5 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;You will have to read &lt;code&gt;sfdisk&lt;/code&gt;'s manpage to understand what's all that. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fnref:6" title="Jump back to footnote 6 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;&lt;code&gt;sfdisk&lt;/code&gt; has a neat trick: you can dump the partition table from one disk
and pipe it to a &lt;code&gt;sfdisk&lt;/code&gt; affecting another disk, actually copying the
partition scheme. It comes very handy when adding disks to a raid setup. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fnref:7" title="Jump back to footnote 7 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:8"&gt;
&lt;p&gt;Technically we're marking it as a &lt;code&gt;MSDOS&lt;/code&gt; type partition table. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fnref:8" title="Jump back to footnote 8 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:9"&gt;
&lt;p&gt;Notice that it only complains about the end bound, not the beginning bound. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fnref:9" title="Jump back to footnote 9 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:10"&gt;
&lt;p&gt;One interesting note: even if above I told &lt;code&gt;debbootstrap&lt;/code&gt; to install a
 Linux kernel, it actually hasn't. The package &lt;code&gt;linux-image-2.6-686&lt;/code&gt; is a
 virtual one, and &lt;code&gt;debbootstrap&lt;/code&gt; seems to not resolve this ones, but it
 doesn't complain either. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fnref:10" title="Jump back to footnote 10 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:11"&gt;
&lt;p&gt;See that &lt;code&gt;Boot&lt;/code&gt; column in the output of &lt;code&gt;sfdisk&lt;/code&gt; at the beginning of the
 post? And the &lt;code&gt;*&lt;/code&gt; in the first and only partition? That shows it as bootable.
 This is an old relic from the times when operating systems relied on a dumb
 MBR code to boot. And now we're using exactly that to load a bootloader. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fnref:11" title="Jump back to footnote 11 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:12"&gt;
&lt;p&gt;&lt;strong&gt;Really&lt;/strong&gt;
   old; we're talking about a Cyrix 486DX2 at 50MHz with 16MB de RAM, 4 NICs,
   all of them ISA, two of them still donning 10Base2 connectors and configurable
   via jumpers. We really didn't need anything bigger since the ADSL line was
   merely 2.5Mib/s. &lt;a class="footnote-backref" href="https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/#fnref:12" title="Jump back to footnote 12 in the text"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><category>sysadmin debian</category><guid>https://www.grulic.org.ar/~mdione/glob/posts/create-a-disk-image-with-a-booting-running-debian/</guid><pubDate>Fri, 26 Nov 2010 11:10:04 GMT</pubDate></item></channel></rss>