3.2. Devices and Partitions

3.2.1. Introduction

After using parted disk1.raw has a partition table, but does that mean we can create partitions on it now? Let's run fdisk on disk1.raw.

$ fdisk disk1.raw
You must set cylinders.
You can do this from the extra functions menu.

Command (m for help):

TODO: Maybe this next paragraph should be rejiggered. It's not so much that it's too difficult to calculate, but rather, it's because the virtual disk has no actual physical properties.

Or, just explain that it's impractical and unnecessary. Provide as a reference in the appendix.

A much simpler way to create partitions (still using fdisk) is by accessing the file as if it were an actual device. Doing this requires creating loop devices.

Instead of using fdisk on disk1.raw directly, we'll create a loop device and associate disk1.raw with it. From here on we'll be accessing our virtual drives through loop devices.

Why are we doing this? And what is a loop device?

Unfortunately for disk1.raw, it will never be anything more than just a file. The operating system just doesn't have interfaces for block operations against files. As the kernel creates the block special device /dev/sda to represent my hard drive, we need to create a block special device to represent our virtual disk. This is called a loop device. You can think of a loop device, e.g., /dev/loop1, like a translator.

With a loop device inserted between programs and our disk image we can view and operate on the disk image as if it were a regular drive. When accessed through a loop device fdisk can properly determine the number of cylinders, heads, and everything else required to create partitions.

3.2.2. Creating a Loop Device

[Note]Note

Since we'll be working with the kernel to create a device you'll need to have super user permissions to continue.

To create a loop device run the losetup command with the -f option. The first available loop device will be selected automatically and associated with disk1.raw [28] .

Example 3.6. Creating a loop device with losetup

$ sudo losetup -f disk1.raw

$ sudo losetup -a
/dev/loop1: [0805]:151552 (/home/tim/images/disk1.raw)

You can run file, stat, and fdisk on disk1.raw to verify that nothing has changed since we put a partition table on it with parted.

3.2.3. Examine the loop device

Example 3.7. Examining the Loop Device

$ file /dev/loop0
/dev/loop0: block special

$ stat /dev/loop0
  File: `/dev/loop0'
  Size: 0               Blocks: 0          IO Block: 4096   block special file
Device: 5h/5d   Inode: 5102        Links: 1     Device type: 7,0
Access: (0660/brw-rw----)  Uid: (    0/    root)   Gid: (    6/    disk)
Access: 2010-09-15 01:22:09.909721760 -0400
Modify: 2010-09-12 11:03:19.351004598 -0400
Change: 2010-09-12 11:03:24.694640781 -0400

$ sudo fdisk -l /dev/loop0
 Disk /dev/loop0: 1073 MB, 1073741824 bytes
255 heads, 63 sectors/track, 130 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x000e44e8

      Device Boot      Start         End      Blocks   Id  System

Look back at Example 3.1, “Regular Disk Drive” where I ran these commands against my actual disk drive (/dev/sda) and you'll see the results are quite similar.

Table 3.3. Examining the Loop Device

Commandsdadisk1.rawdisk1.raw (via parted)/dev/loop0
fileblock specialdatax86 boot sectorblock special
statblock specialregular fileregular fileblock special
fdiskhas partition tableno partition tablevalid partition table. unknown cylinder countvalid partition table. known cylinder count

  • file detects loop0 as a block special device.

  • stat does too.

  • fdisk no longer says we need to set the cylinders.

Our virtual disk is starting to look like a real hard drive now! To conclude this section we'll:

  • create a partition

  • format it with an ext3 filesystem

  • mount it for reading and writing

3.2.4. Creating partitions

Open /dev/loop0 (or whatever loop device your disk was associated with) in fdisk to create a partition.

Example 3.8. Creating a partition with fdisk

$ sudo fdisk /dev/loop0
Command (m for help): n
Command action
   e   extended
   p   primary partition (1-4)
p
Partition number (1-4): 1
First cylinder (1-130, default 1):
Using default value 1
Last cylinder, +cylinders or +size{K,M,G} (1-130, default 130):
Using default value 130

Command (m for help): t
Selected partition 1
Hex code (type L to list codes): 83

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 22: Invalid argument.
The kernel still uses the old table. The new table will be used at
the next reboot or after you run partprobe(8) or kpartx(8)
Syncing disks.

$ sudo fdisk -l /dev/loop0
Disk /dev/loop0: 1073 MB, 1073741824 bytes
255 heads, 63 sectors/track, 130 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x000e44e8

      Device Boot      Start         End      Blocks   Id  System
/dev/loop0p1               1         130     1044193+  83  Linux

TODO: That example would benefit a LOT from some well placed callouts

TODO: Speak about the kpartx and partprobe commands

3.2.5. Formatting Partitions

Unlike /dev/sda we can't just create a partition on the loop0 device by addressing it as /dev/loop0. This is because the kernel has no device created to represent it. Instead we'll have to create another device associated with a specific offset in our device/file.

TODO: Rephrase the above paragraph ^ wrt: device representation

Q:

What is an offset, and why do we have to specify one?

A:

An offset indicates how far from the beginning of a device something is.

The first partition is not located at the beginning of the device. That is where the Master Boot Record (MBR) is stored (offset=0). If we tried to create a partition at offset=0 we would overwrite the MBR. Knowing the offset will allow us to create a device mapped to where the first partition begins without overwriting the MBR. Linux does this automatically for regular disks during the boot process.

Q:

How do we calculate the offset to specify?

A:

To calculate the offset we need to know what sector the partition (loop0p1) starts on. fdisk can give us this information. (Spoiler: 9 times out of 10 the offset for the first partition will be 512 * 63 = 32256).

Q:

Why doesn't the first partition begin after the MBR? Specifically, why is there empty space between the first sector (where the MBR is stored) and the first partition?

A:

It's complicated but worth learning about. See Appendix B, Appendix: Disk Drive History for a complete explanation.

Here's the short answer: In current PC MBRs there may be up to 446B of executable code and a partition table containing up to 64B of data. When you add in another 2B to record a Boot Signature you have 512B, which up until recently happened to be the typical size of one sector.

Partitioning tools historically left the space between the MBR and the second cylinder empty. Modern boot loaders (NTLDR[29], GRUB[30], etc) use this space to store additional code and data necessary to boot the system [31] [32].

Some software, such as licensing managers and virus scanners, also use this space to store files [33] .

Print the partition table using fdisk with the -u option to switch the printing format to sectors instead of cylinders for units.

$ sudo fdisk -u -l /dev/loop0
Disk /dev/loop0: 1073 MB, 1073741824 bytes
255 heads, 63 sectors/track, 130 cylinders, total 2097152 sectors
Units = sectors of 1 * 512 = 512 bytes
Disk identifier: 0x000e44e8

Device Boot      Start         End      Blocks   Id  System
/dev/loop0p1              63     2088449     1044193+  83  Linux

/dev/loop0p1 is our first partition and from the table above we know that it starts on sector 63. Since we have to specify offsets in bytes we multiply 63 by 512 (the default block size) to obtain an offset of 32256 bytes.

$ sudo losetup -o 32256 -f /dev/loop0

$ sudo losetup -a
/dev/loop0: [0805]:151552 (/home/tim/images/disk1.raw)
/dev/loop1: [0005]:5102 (/dev/loop0), offset 32256

Now that we have /dev/loop1 representing the first partition of our virtual disk we can create a filesystem on it and finally mount it.

Example 3.9. Formatting and mounting the partition

$ sudo mkfs -t ext3 /dev/loop1
mke2fs 1.41.9 (22-Aug-2009)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
65536 inodes, 262136 blocks
13106 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=268435456
8 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376

Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 25 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.

$ sudo losetup -d /dev/loop1

$ sudo losetup -d /dev/loop0

$ mkdir partition1

$ sudo mount -t ext3 -o loop,offset=32256 disk1.raw partition1/

$ mount | grep partition1
/dev/loop0 on /home/tim/images/partition1 type ext3 (rw,offset=32256)

$ df -h partition1/
Filesystem            Size  Used Avail Use% Mounted on
/dev/loop0           1008M   18M  940M   2% /home/tim/images/partition1

[Note]Note

The same procedure applies to any arbitrary partition: obtain the starting sector, multiply by block size.

3.2.6. Cleaning Up

You can detach the loop device (while leaving your file intact) by giving the -d option to losetup.

Example 3.10. Detaching a loop device

$ sudo losetup -d /dev/loop1



[28] FUSE (Filesystem in Userspace) has a module called Mountlo that allows non-root users to make make loop devices.

[30] The Grand Unified Bootloader (GRUB): http://www.gnu.org/software/grub/

[33] Ubuntu Forums - Sector 32 FlexNet Problem -- Grub: http://ubuntuforums.org/showthread.php?t=1661254