MBR の場合でもディスク識別子が存在した

GPT は名前の通り、128ビットのGUIDによりディスクを識別できるのだが、実は MBR の場合でもディスク固有の値として32ビットの値がMBR(セクタ0)のオフセット 440 - 443 に書き込まれている。
fdisk コマンドでも「Disk identifier」として出力され、以下の例では「e64d8f76」がそれだ。

# fdisk -l /dev/sdd

Disk /dev/sdd: 1073 MB, 1073741824 bytes
34 heads, 61 sectors/track, 1011 cylinders
Units = cylinders of 2074 * 512 = 1061888 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xe64d8f76

   Device Boot      Start         End      Blocks   Id  System
/dev/sdd1               1         100      103669+  83  Linux
/dev/sdd2             101         200      103700   83  Linux

なお、確認に使用しているのは CentOS 6.4 x86_64 である。

どこにこの Disk identifer は格納されているのか?

この Disk identifier だが、恥ずかしながら私はずっと「なにか適当な数字」ぐらいにしか思っていなかった。その理由は MBR の形式をずっと以下の図の通りと認識していたからだ。

実際に日本語のWikipediaにおける「マスターブートレコード」ではこの構造が記載されており、「0(先頭)〜445(446バイト)にブートストラップローダ、446〜509(64バイト)にパーティションテーブル、510〜511(2バイト)にブートシグニチャがある。」とも明言されている。これによれば Disk identifer などといういかにも永続的なものを書いておく余地などない。書いておくことができない永続的な情報があるわけがない、そう思っているから見えていなかったといってもよい(無いと思っているものは目の前にあっても見えない、とか言いますよね)。

ところが、英語のWikipediaにおける「Master boot record」では上記の図の形式を「Structure of a classical generic MBR」だとして説明している。そして、「Structure of modern standard MBR」として以下の図の形式を説明している。つまり、現在の標準的な(といってもそのMBR自体がレガシーになっていくが) MBR の形式ではオフセット 440 - 445 の扱いが決定的に変わっているのである。そしてこの場所こそが Disk identifer が書き込まれている場所だ。

Disk identifer が fdisk 実行時に生成されることの確認

パーティションを削除したあとにディスクをクリアする(先頭64セクタのみ)。

# dd if=/dev/zero of=/dev/sdd bs=512 count=64
64+0 records in
64+0 records out
32768 bytes (33 kB) copied, 0.0694129 s, 472 kB/s

パーティションテーブルもなく、Disk identifer は 00000000 になっている。

# fdisk -l /dev/sdd

Disk /dev/sdd: 1073 MB, 1073741824 bytes
34 heads, 61 sectors/track, 1011 cylinders
Units = cylinders of 2074 * 512 = 1061888 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

fdisk を実行し、パーティションは作らずに保存する。
実行直後の p (print) で既に Disk identifier が表示されているのは、fdisk のメモリ上ではもう MBR(セクタ0) のイメージが生成済みだからだろう。

# fdisk /dev/sdd
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0x3907301d.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
         switch off the mode (command 'c') and change display units to
         sectors (command 'u').

Command (m for help): p

Disk /dev/sdd: 1073 MB, 1073741824 bytes
34 heads, 61 sectors/track, 1011 cylinders
Units = cylinders of 2074 * 512 = 1061888 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x3907301d

   Device Boot      Start         End      Blocks   Id  System

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

Calling ioctl() to re-read partition table.
Syncing disks.

fdisk -l で表示してみると、Disk identifer がしっかり書き込まれていることが確認できた。
dd でもちょうどオフセット 440 - 443 に書き込まれていることがわかる。何もパーティションを作成しない初期の MBR(セクタ0) のイメージはこの Disk identifer と末尾2バイトのブートシグニチャのみということのようだ。

# fdisk -l /dev/sdd

Disk /dev/sdd: 1073 MB, 1073741824 bytes
34 heads, 61 sectors/track, 1011 cylinders
Units = cylinders of 2074 * 512 = 1061888 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x3907301d

   Device Boot      Start         End      Blocks   Id  System

# dd if=/dev/sdd bs=512 count=1 2>/dev/null | hexdump -C
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001b0  00 00 00 00 00 00 00 00  1d 30 07 39 00 00 00 00  |.........0.9....|
000001c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
00000200

fdisk を使用した Disk identifier の書き換え

fdisk にはエキスパートモードというものがあり、そのモードに切り替えるの Disk identifer を書き換えることができる。
ただし、Disk identifer の書き換えのみでは fdisk が変更として扱わず、MBR に実際に書き込みを行わないようである。そのため、適当なパーティションのシステム識別子を一旦書き換えて戻す(変更フラグを立てさせるだけ)という一見無駄なオペレーションも行う。もちろんパーティションの一時作成・削除でもよい。

元々の Disk identifer は「1c1f80db」である。

# fdisk -l /dev/sdd

Disk /dev/sdd: 1073 MB, 1073741824 bytes
34 heads, 61 sectors/track, 1011 cylinders
Units = cylinders of 2074 * 512 = 1061888 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x1c1f80db

   Device Boot      Start         End      Blocks   Id  System
/dev/sdd1               1         100      103669+  83  Linux

fdisk を起動し、エキスパートモードに入る。

# fdisk /dev/sdd

WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
         switch off the mode (command 'c') and change display units to
         sectors (command 'u').

Command (m for help): m
Command action
   a   toggle a bootable flag
   b   edit bsd disklabel
   c   toggle the dos compatibility flag
   d   delete a partition
   l   list known partition types
   m   print this menu
   n   add a new partition
   o   create a new empty DOS partition table
   p   print the partition table
   q   quit without saving changes
   s   create a new empty Sun disklabel
   t   change a partition's system id
   u   change display/entry units
   v   verify the partition table
   w   write table to disk and exit
   x   extra functionality (experts only)

Command (m for help): x

Expert command (m for help): m
Command action
   b   move beginning of data in a partition
   c   change number of cylinders
   d   print the raw data in the partition table
   e   list extended partitions
   f   fix partition order
   g   create an IRIX (SGI) partition table
   h   change number of heads
   i   change the disk identifier
   m   print this menu
   p   print the partition table
   q   quit without saving changes
   r   return to main menu
   s   change number of sectors/track
   v   verify the partition table
   w   write table to disk and exit

Disk identifer の書き換えのため i を入力し、新しい Disk identifier として「0xe64d8f76」を入力する(ここでは16進数を表す0xを付ける必要がある)。

Expert command (m for help): i
New disk identifier (current 0x1c1f80db): 0xe64d8f76
Disk identifier: 0xe64d8f76

r を入力し、エキスパートを終了する。p にて確認すると Disk identifier は変更されている(まだ fdisk のメモリ上のみ)。
ここで w で書き込みを要求しても書き込みをしてくれないので、パーティション1のシステム識別子を 83 → 8e → 83 に書き換えてから w を実行する。

Expert command (m for help): r

Command (m for help): p

Disk /dev/sdd: 1073 MB, 1073741824 bytes
34 heads, 61 sectors/track, 1011 cylinders
Units = cylinders of 2074 * 512 = 1061888 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xe64d8f76

   Device Boot      Start         End      Blocks   Id  System
/dev/sdd1               1         100      103669+  83  Linux

Command (m for help): t
Selected partition 1
Hex code (type L to list codes): 8e
Changed system type of partition 1 to 8e (Linux LVM)

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

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

Calling ioctl() to re-read partition table.
Syncing disks.

無事に Disk identifier が書き換えられた。

# fdisk -l /dev/sdd

Disk /dev/sdd: 1073 MB, 1073741824 bytes
34 heads, 61 sectors/track, 1011 cylinders
Units = cylinders of 2074 * 512 = 1061888 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xe64d8f76

   Device Boot      Start         End      Blocks   Id  System
/dev/sdd1               1         100      103669+  83  Linux