{"article":{"slug":"netbsd-playing-with-disklabels","title":"NetBSD: Playing with disklabels","subtitle":null,"summary":"A hands-on walkthrough of NetBSD disklabels for someone coming from Linux/DOS: how labels are structured, how to inspect and edit them safely, and the gotchas when partitioning disks the BSD way.","content_type":"tutorial","language":"en","canonical_url":"https://movq.de/blog/postings/2026-09-25/0/POSTING-en.html","author":{"name":"movq","url":"https://movq.de/","person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"movq.de","url":"https://movq.de/","listing_slug":null,"listing":null},"topics":[{"name":"Linux","slug":"linux","url":"https://listedarticles.com/topics/linux"},{"name":"Systems Programming","slug":"systems-programming","url":"https://listedarticles.com/topics/systems-programming"},{"name":"Open Source","slug":"open-source","url":"https://listedarticles.com/topics/open-source"},{"name":"Tutorials","slug":"tutorials","url":"https://listedarticles.com/topics/tutorials"}],"about_listings":[],"cover_image_url":null,"license":"all-rights-reserved","word_count":1633,"reading_minutes":7,"published_at":"2026-09-25T12:00:00.000Z","added_at":"2026-09-26T21:13:28.272Z","updated_at":"2026-09-26T21:13:28.272Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/netbsd-playing-with-disklabels","markdown_url":"https://listedarticles.com/articles/netbsd-playing-with-disklabels.md","example":false,"citation":"movq, movq.de. \"NetBSD: Playing with disklabels.\" 25 Sept 2026. https://movq.de/blog/postings/2026-09-25/0/POSTING-en.html (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://movq.de/blog/postings/2026-09-25/0/POSTING-en.html"},"body_markdown":"[blog](</blog/>) \\- [git](</git/>) \\- [desktop](</desktop/>) \\- [contact](</contact.html>)\n\n* * *\n\n# NetBSD: Playing with disklabels\n\n2026-09-25\n\nComing from DOS and Linux (and having largely ignored this on my OpenBSD systems -- yeah, shame on me), I'm not very familiar with the BSD disklabels. So let's have a look.\n\nI'm not entirely certain if all BSDs use the exact same format and as far as I know there are differences among architectures, so let's be specific here: I'm looking at NetBSD 11 on x86_64.\n\nI'll be running everything in a VM, so I can easily swap disks and inspect them.\n\n[To avoid conflicts with MBR systems](<https://en.wikipedia.org/wiki/BSD_disklabel#Where_disklabels_are_stored>), disklabels are stored at a different location. In my test VM, it can be found at the second sector:\n\n[![bine-initial.png: Hex editor screenshot: At offset 0x200, we can see the disklabel magic number.](t/bine-initial.png.jpg)](<bine-initial.png>)\n\nI was primarily interested in the format of this disklabel: What do the individual bytes mean? And where is that specificed/documented?\n\n* * *\n\nTable of contents:\n\n  * Where to find documentation?\n  * Part 0: Where is the disklabel?\n  * Part 1: \"Disk metadata\"\n  * Part 2: The partition table\n  * Verifying the checksum\n  * Exercise: Create some random partition in the hex editor\n  * Conclusion\n\n* * *\n\n## Where to find documentation?\n\nThis question is easy to answer. Look at `man diskabel` (as you would \"instinctively\" do, because that's the tool to manipulate those labels), there's `disklabel(5)` in the `SEE ALSO` section: Section 5 describes file formats, so that's what we want to look at (`man 5 disklabel`).\n\nFor most of this post, though, I looked directly at `/usr/include/sys/disklabel.h` instead of the manual page.\n\n## Part 0: Where is the disklabel?\n\nFirst things first. The manual page tells you where to find the disklabel: `getlabelsector()` and `getlabeloffset()` answer that. So let's write a little C program to see what they return.\n    \n    \n    netbsd# cat test.c\n    #include <stdio.h>\n    #include <util.h>\n    \n    int\n    main()\n    {\n        printf(\"getlabelsector() = %d\\n\", getlabelsector());\n        printf(\"getlabeloffset() = %ld\\n\", getlabeloffset());\n    \n        return 0;\n    }\n    \n    netbsd# cc -Wall -Wextra -o test test.c -lutil\n    \n\nOutput:\n    \n    \n    netbsd# ./test\n    getlabelsector() = 1\n    getlabeloffset() = 0\n    \n\nSo sector 1 (byte offset 512 in my case) is correct. It's not just some random data in the screenshot above but that _is_ the disklabel.\n\n## Part 1: \"Disk metadata\"\n\nThe disklabel begins with a bunch of \"metadata\" about the disk, then the actual partition table follows at the end. Let's look at this metadata first.\n\nA bit hard to illustrate this. Here's an overview first (open it in a new tab next to this text), and then we'll go through this one by one:\n\n[![disklabel-metadata.png: Hex dump of my disklabel. Colors indicate the individual fields.](t/disklabel-metadata.png.jpg)](<disklabel-metadata.png>)\n\nAll of this is _little endian_ on my Intel machine.\n\nGeneric disk/drive information:\n\n  * `5745 5682`: `d_magic`, Magic Number\n  * `0f00`: `d_type`, drive type = `0x0f` = \"logical disk\"\n  * `0000`: `d_subtype`, specific to `d_type`\n  * `6c64...0000` / `\"ld1\"`: `d_typename`\n  * `4d79...0000` / `\"My Cool Disk\"`: `d_packname`, user enters this name\n\n`disklabel.h` contains a table of the possible values for `d_type`.\n\nGeometry/size:\n\n  * `0002 0000`: `d_secsize`, 512 bytes per sector\n  * `3f00 0000`: `d_nsectors`, 63 sectors per track\n  * `1000 0000`: `d_ntracks`, 16 tracks per cylinder\n  * `0401 0000`: `d_ncylinders`, 260 cylinders per unit\n  * `f003 0000`: `d_secpercyl`, 1008 sectors per cylinder\n  * `0000 0400`: `d_secperunit`, 262144 sectors per unit (128 MiB)\n  * `0000`: `d_sparespertrack`, no spare sectors per track\n  * `0000`: `d_sparespercyl`, no spare sectors per cylinder\n  * `0000 0000`: `d_acylinders`, \"alternative cylinders\" (?)\n\nHardware parameters and timings, probably very irrelevant on x86_64:\n\n  * `100e`: `d_rpm`, 3600\n  * `0100`: `d_interleave`\n  * `0000`: `d_trackskew`\n  * `0000`: `d_cylskew`\n  * `0000 0000`: `d_headswitch`, head switch time in microseconds\n  * `0000 0000`: `d_trkseek`, track-to-track seek time in microseconds\n  * `0000 0000`: `d_flags`, \"generic flags\" :-)\n  * 5x `0000 0000`: `d_drivedata`, drive-type specific\n  * 5x `0000 0000`: `d_space`, reserved\n\nEnd of this first part:\n\n  * `5745 5682`: `d_magic2`, Magic Number\n  * `c61d`: `d_checksum`\n  * `0400`: `d_npartitions`, how many partitions follow in the array below\n  * `0020 0000`: `d_bbsize`, size of boot area at sn0 in bytes\n  * `0020 0000`: `d_sbsize`, max size of fs superblock in bytes\n\nThese are the values that I determined by manually reading the hex dump and comparing it with `struct disklabel` in the header file. As far as I can tell, it matches the output of the `diskabel` tool:\n    \n    \n    netbsd# disklabel ld1\n    # /dev/rld1:\n    type: ld\n    disk: ld1\n    label: My Cool Disk\n    flags:\n    bytes/sector: 512\n    sectors/track: 63\n    tracks/cylinder: 16\n    sectors/cylinder: 1008\n    cylinders: 260\n    total sectors: 262144\n    rpm: 3600\n    interleave: 1\n    trackskew: 0\n    cylinderskew: 0\n    headswitch: 0           # microseconds\n    track-to-track seek: 0  # microseconds\n    drivedata: 0\n    \n\nA lot of this information feels quite outdated, at least on \"modern\" x86_64. (But MBR isn't _much_ better in this regard, with all the CHS stuff still going on.) And much of it is indeed unused and just set to 0.\n\n## Part 2: The partition table\n\n`d_npartitions` said there are four partitions. Here's an overview of this table, it immediately follows the \"metadata\" section:\n\n[![disklabel-partitions-overview.png: Same hex dump as above, but the area where the partition table is located is highlighted.](t/disklabel-partitions-overview.png.jpg)](<disklabel-partitions-overview.png>)\n\nThis corresponds to partitions `a`, `b`, `c`, and `d`.\n\nLet's take a closer look at partition `a`:\n\n[![disklabel-partition-a.png: Same hex dump as above, individual fields of partition 'a' highlighted.](t/disklabel-partition-a.png.jpg)](<disklabel-partition-a.png>)\n\nThe individual fields:\n\n  * `0000 0400`: `p_size`, 262144 sectors\n  * `0000 0000`: `p_offset`, 0 sectors\n  * `0000 0000`: `p_fsize`, \"filesystem basic fragment size\" (?)\n  * `07`: `p_fstype`, filesystem type = `0x07` = `4.2BSD / ffs`\n  * `00`: `p_frag`, \"filesystem fragments per block\" (?)\n  * `0000`: `p_cpg` or `p_sgs`: UFS or LFS specific\n\n`disklabel.h` contains a table of the possible values for `p_fstype`.\n\nThere's really only three important bits of information, I think:\n\n  * Start,\n  * end,\n  * and type of the partition.\n\nAgain, my interpretation matches the output of `disklabel`:\n    \n    \n    netbsd# disklabel ld1\n    ...\n    4 partitions:\n    #        size    offset     fstype [fsize bsize cpg/sgs]\n     a:    262144         0     4.2BSD      0     0     0  # (Cyl.      0 -    260*)\n     d:    262144         0     unused      0     0        # (Cyl.      0 -    260*)\n    \n\nAccording to [this mail by Martin Husemann](<https://mail-index.netbsd.org/netbsd-users/2018/09/22/msg021452.html>), partition `d` is always there on x86_64 and describes the entire disk. Partition `c` would be the area usable for NetBSD. Why `c` isn't included in the output here, I'm not sure. (Actually, no, I think I know why: Size, offset, and type are all zero, so it gets hidden, I guess. But why didn't `disklabel -I` include `c` when I first created the disklabel?)\n\n## Verifying the checksum\n\nThe header file says:\n    \n    \n    uint16_t d_checksum;  /* xor of data incl. partitions */\n    \n\nSo ... I guess we chunk up all this data into 16-byte pieces and then xor them all together? Let's try:\n    \n    \n    $ dd if=zwei.raw bs=512 count=1 skip=1 status=none |\n        od -An -vt x2 -w2 |\n        gawk '{ v = strtonum(\"0x\" $1); cksum = xor(cksum, v) } END { printf(\"%04x\\n\", cksum) }'\n    0000\n    \n\nAll zeroes. When you think about it: That's confirmation that everything is correct. :-) The actual checksum is one of those 16-byte chunks, so zero is the correct answer.\n\nExclude the checksum from the data:\n    \n    \n    $ dd if=zwei.raw bs=512 count=1 skip=1 status=none |\n        od -An -vt x2 -w2 |\n        sed 69d |\n        gawk '{ v = strtonum(\"0x\" $1); cksum = xor(cksum, v) } END { printf(\"%04x\\n\", cksum) }'\n    1dc6\n    \n\nThere you go, that matches the little-endian `c61d` that we saw in the dump. (Obviously, because ... that's the line that I excluded ... yeah ... but you get the idea.)\n\nI'm not familiar with NetBSD's code base yet, but I think this might be their code to compute the checksum (at least it does the same thing I did):\n\n<https://cvsweb.netbsd.org/bsdweb.cgi/src/sys/lib/libkern/dkcksum.c?rev=1.1.6.2;content-type=text%2Fplain>\n\n## Exercise: Create some random partition in the hex editor\n\nLet's do it the other way around: Given what we know now, open the hex editor and define partition `i`, size 1234 sectors, start at sector 5678, filesystem type ZFS.\n\nPartition `i` is the ninth partition, so its entry should start at byte 788 on the disk:\n    \n    \n    512 + 0x94 + (9 - 1) * 16 = 788\n    \\_/   \\__/   \\_____/   |\n     |      |       |      \\- Each label is 16 bytes in size, see\n     |      |       |         disklabel.h.\n     |      |       |\n     |      |       \\- We want to know the *start* of the ninth label.\n     |      |\n     |      \\- Start of first partition entry, see hex dump above.\n     |\n     \\- Start of disklabel on disk.\n    \n\nWe're going to write these fields in the partition table (this is already _little endian_):\n\n  * `p_size` = `d204 0000` for 1234 sectors\n  * `p_offset` = `2e16 0000` for offset 5678\n  * `p_fstype` = `21`, according to the table in `disklabel.h`\n\nWe also need to update `d_npartitions` to `9`. All the partitions in between remain unused.\n\nI'll be overwriting `d_checksum` with four zeros, so that we can just run the `gawk` snippet above to calculate the new checksum. Lo and behold, it is: `160f` (little endian).\n\nThese are the bytes that I touched:\n\n[![bine-edit.png: Screenshot of my hex editor, pink highlights show which bytes I changed.](t/bine-edit.png.jpg)](<bine-edit.png>)\n\nLet's boot the VM again and see what we got:\n    \n    \n    netbsd# disklabel ld1\n    # /dev/rld1:\n    type: ld\n    disk: ld1\n    label: My Cool Disk\n    flags:\n    bytes/sector: 512\n    sectors/track: 63\n    tracks/cylinder: 16\n    sectors/cylinder: 1008\n    cylinders: 260\n    total sectors: 262144\n    rpm: 3600\n    interleave: 1\n    trackskew: 0\n    cylinderskew: 0\n    headswitch: 0           # microseconds\n    track-to-track seek: 0  # microseconds\n    drivedata: 0\n    \n    9 partitions:\n    #        size    offset     fstype [fsize bsize cpg/sgs]\n     a:    262144         0     4.2BSD      0     0     0  # (Cyl.      0 -    260*)\n     d:    262144         0     unused      0     0        # (Cyl.      0 -    260*)\n     i:      1234      5678        ZFS                     # (Cyl.      5*-      6*)\n    disklabel: partitions a and i overlap\n    \n\nLooks good!\n\n(The partitions obviously overlap, because `a` was already the entire disk.)\n\n## Conclusion\n\nThis was a fun afternoon and I indeed feel more comfortable with disklabels now. As usual, I really have to have a _hands-on session_ like this in order to grow familiar with something.\n\nAs you can see, I marked some fields with `(?)`, because their meaning isn't entirely clear to me yet. Maybe more on that some other day (or maybe not).","body_html":"<p><a href=\"/blog/\">blog</a> - <a href=\"/git/\">git</a> - <a href=\"/desktop/\">desktop</a> - <a href=\"/contact.html\">contact</a></p>\n<ul><li>* *</li></ul>\n<h1 id=\"netbsd-playing-with-disklabels\">NetBSD: Playing with disklabels</h1>\n<p>2026-09-25</p>\n<p>Coming from DOS and Linux (and having largely ignored this on my OpenBSD systems -- yeah, shame on me), I&#39;m not very familiar with the BSD disklabels. So let&#39;s have a look.</p>\n<p>I&#39;m not entirely certain if all BSDs use the exact same format and as far as I know there are differences among architectures, so let&#39;s be specific here: I&#39;m looking at NetBSD 11 on x86_64.</p>\n<p>I&#39;ll be running everything in a VM, so I can easily swap disks and inspect them.</p>\n<p><a href=\"https://en.wikipedia.org/wiki/BSD_disklabel#Where_disklabels_are_stored\" rel=\"nofollow ugc noopener\">To avoid conflicts with MBR systems</a>, disklabels are stored at a different location. In my test VM, it can be found at the second sector:</p>\n<p>bine-initial.png: Hex editor screenshot: At offset 0x200, we can see the disklabel magic number.</p>\n<p>I was primarily interested in the format of this disklabel: What do the individual bytes mean? And where is that specificed/documented?</p>\n<ul><li>* *</li></ul>\n<p>Table of contents:</p>\n<ul><li>Where to find documentation?</li><li>Part 0: Where is the disklabel?</li><li>Part 1: &quot;Disk metadata&quot;</li><li>Part 2: The partition table</li><li>Verifying the checksum</li><li>Exercise: Create some random partition in the hex editor</li><li>Conclusion</li><li>* *</li></ul>\n<h2 id=\"where-to-find-documentation\">Where to find documentation?</h2>\n<p>This question is easy to answer. Look at <code>man diskabel</code> (as you would &quot;instinctively&quot; do, because that&#39;s the tool to manipulate those labels), there&#39;s <code>disklabel(5)</code> in the <code>SEE ALSO</code> section: Section 5 describes file formats, so that&#39;s what we want to look at (<code>man 5 disklabel</code>).</p>\n<p>For most of this post, though, I looked directly at <code>/usr/include/sys/disklabel.h</code> instead of the manual page.</p>\n<h2 id=\"part-0-where-is-the-disklabel\">Part 0: Where is the disklabel?</h2>\n<p>First things first. The manual page tells you where to find the disklabel: <code>getlabelsector()</code> and <code>getlabeloffset()</code> answer that. So let&#39;s write a little C program to see what they return.</p>\n<pre><code>netbsd# cat test.c\n#include &lt;stdio.h&gt;\n#include &lt;util.h&gt;\n\nint\nmain()\n{\n    printf(&quot;getlabelsector() = %d\\n&quot;, getlabelsector());\n    printf(&quot;getlabeloffset() = %ld\\n&quot;, getlabeloffset());\n\n    return 0;\n}\n\nnetbsd# cc -Wall -Wextra -o test test.c -lutil</code></pre>\n<p>Output:</p>\n<pre><code>netbsd# ./test\ngetlabelsector() = 1\ngetlabeloffset() = 0</code></pre>\n<p>So sector 1 (byte offset 512 in my case) is correct. It&#39;s not just some random data in the screenshot above but that <em>is</em> the disklabel.</p>\n<h2 id=\"part-1-disk-metadata\">Part 1: &quot;Disk metadata&quot;</h2>\n<p>The disklabel begins with a bunch of &quot;metadata&quot; about the disk, then the actual partition table follows at the end. Let&#39;s look at this metadata first.</p>\n<p>A bit hard to illustrate this. Here&#39;s an overview first (open it in a new tab next to this text), and then we&#39;ll go through this one by one:</p>\n<p>disklabel-metadata.png: Hex dump of my disklabel. Colors indicate the individual fields.</p>\n<p>All of this is <em>little endian</em> on my Intel machine.</p>\n<p>Generic disk/drive information:</p>\n<ul><li><code>5745 5682</code>: <code>d_magic</code>, Magic Number</li><li><code>0f00</code>: <code>d_type</code>, drive type = <code>0x0f</code> = &quot;logical disk&quot;</li><li><code>0000</code>: <code>d_subtype</code>, specific to <code>d_type</code></li><li><code>6c64...0000</code> / <code>&quot;ld1&quot;</code>: <code>d_typename</code></li><li><code>4d79...0000</code> / <code>&quot;My Cool Disk&quot;</code>: <code>d_packname</code>, user enters this name</li></ul>\n<p><code>disklabel.h</code> contains a table of the possible values for <code>d_type</code>.</p>\n<p>Geometry/size:</p>\n<ul><li><code>0002 0000</code>: <code>d_secsize</code>, 512 bytes per sector</li><li><code>3f00 0000</code>: <code>d_nsectors</code>, 63 sectors per track</li><li><code>1000 0000</code>: <code>d_ntracks</code>, 16 tracks per cylinder</li><li><code>0401 0000</code>: <code>d_ncylinders</code>, 260 cylinders per unit</li><li><code>f003 0000</code>: <code>d_secpercyl</code>, 1008 sectors per cylinder</li><li><code>0000 0400</code>: <code>d_secperunit</code>, 262144 sectors per unit (128 MiB)</li><li><code>0000</code>: <code>d_sparespertrack</code>, no spare sectors per track</li><li><code>0000</code>: <code>d_sparespercyl</code>, no spare sectors per cylinder</li><li><code>0000 0000</code>: <code>d_acylinders</code>, &quot;alternative cylinders&quot; (?)</li></ul>\n<p>Hardware parameters and timings, probably very irrelevant on x86_64:</p>\n<ul><li><code>100e</code>: <code>d_rpm</code>, 3600</li><li><code>0100</code>: <code>d_interleave</code></li><li><code>0000</code>: <code>d_trackskew</code></li><li><code>0000</code>: <code>d_cylskew</code></li><li><code>0000 0000</code>: <code>d_headswitch</code>, head switch time in microseconds</li><li><code>0000 0000</code>: <code>d_trkseek</code>, track-to-track seek time in microseconds</li><li><code>0000 0000</code>: <code>d_flags</code>, &quot;generic flags&quot; :-)</li><li>5x <code>0000 0000</code>: <code>d_drivedata</code>, drive-type specific</li><li>5x <code>0000 0000</code>: <code>d_space</code>, reserved</li></ul>\n<p>End of this first part:</p>\n<ul><li><code>5745 5682</code>: <code>d_magic2</code>, Magic Number</li><li><code>c61d</code>: <code>d_checksum</code></li><li><code>0400</code>: <code>d_npartitions</code>, how many partitions follow in the array below</li><li><code>0020 0000</code>: <code>d_bbsize</code>, size of boot area at sn0 in bytes</li><li><code>0020 0000</code>: <code>d_sbsize</code>, max size of fs superblock in bytes</li></ul>\n<p>These are the values that I determined by manually reading the hex dump and comparing it with <code>struct disklabel</code> in the header file. As far as I can tell, it matches the output of the <code>diskabel</code> tool:</p>\n<pre><code>netbsd# disklabel ld1\n# /dev/rld1:\ntype: ld\ndisk: ld1\nlabel: My Cool Disk\nflags:\nbytes/sector: 512\nsectors/track: 63\ntracks/cylinder: 16\nsectors/cylinder: 1008\ncylinders: 260\ntotal sectors: 262144\nrpm: 3600\ninterleave: 1\ntrackskew: 0\ncylinderskew: 0\nheadswitch: 0           # microseconds\ntrack-to-track seek: 0  # microseconds\ndrivedata: 0</code></pre>\n<p>A lot of this information feels quite outdated, at least on &quot;modern&quot; x86_64. (But MBR isn&#39;t <em>much</em> better in this regard, with all the CHS stuff still going on.) And much of it is indeed unused and just set to 0.</p>\n<h2 id=\"part-2-the-partition-table\">Part 2: The partition table</h2>\n<p><code>d_npartitions</code> said there are four partitions. Here&#39;s an overview of this table, it immediately follows the &quot;metadata&quot; section:</p>\n<p>disklabel-partitions-overview.png: Same hex dump as above, but the area where the partition table is located is highlighted.</p>\n<p>This corresponds to partitions <code>a</code>, <code>b</code>, <code>c</code>, and <code>d</code>.</p>\n<p>Let&#39;s take a closer look at partition <code>a</code>:</p>\n<p>disklabel-partition-a.png: Same hex dump as above, individual fields of partition &#39;a&#39; highlighted.</p>\n<p>The individual fields:</p>\n<ul><li><code>0000 0400</code>: <code>p_size</code>, 262144 sectors</li><li><code>0000 0000</code>: <code>p_offset</code>, 0 sectors</li><li><code>0000 0000</code>: <code>p_fsize</code>, &quot;filesystem basic fragment size&quot; (?)</li><li><code>07</code>: <code>p_fstype</code>, filesystem type = <code>0x07</code> = <code>4.2BSD / ffs</code></li><li><code>00</code>: <code>p_frag</code>, &quot;filesystem fragments per block&quot; (?)</li><li><code>0000</code>: <code>p_cpg</code> or <code>p_sgs</code>: UFS or LFS specific</li></ul>\n<p><code>disklabel.h</code> contains a table of the possible values for <code>p_fstype</code>.</p>\n<p>There&#39;s really only three important bits of information, I think:</p>\n<ul><li>Start,</li><li>end,</li><li>and type of the partition.</li></ul>\n<p>Again, my interpretation matches the output of <code>disklabel</code>:</p>\n<pre><code>netbsd# disklabel ld1\n...\n4 partitions:\n#        size    offset     fstype [fsize bsize cpg/sgs]\n a:    262144         0     4.2BSD      0     0     0  # (Cyl.      0 -    260*)\n d:    262144         0     unused      0     0        # (Cyl.      0 -    260*)</code></pre>\n<p>According to <a href=\"https://mail-index.netbsd.org/netbsd-users/2018/09/22/msg021452.html\" rel=\"nofollow ugc noopener\">this mail by Martin Husemann</a>, partition <code>d</code> is always there on x86_64 and describes the entire disk. Partition <code>c</code> would be the area usable for NetBSD. Why <code>c</code> isn&#39;t included in the output here, I&#39;m not sure. (Actually, no, I think I know why: Size, offset, and type are all zero, so it gets hidden, I guess. But why didn&#39;t <code>disklabel -I</code> include <code>c</code> when I first created the disklabel?)</p>\n<h2 id=\"verifying-the-checksum\">Verifying the checksum</h2>\n<p>The header file says:</p>\n<pre><code>uint16_t d_checksum;  /* xor of data incl. partitions */</code></pre>\n<p>So ... I guess we chunk up all this data into 16-byte pieces and then xor them all together? Let&#39;s try:</p>\n<pre><code>$ dd if=zwei.raw bs=512 count=1 skip=1 status=none |\n    od -An -vt x2 -w2 |\n    gawk &#39;{ v = strtonum(&quot;0x&quot; $1); cksum = xor(cksum, v) } END { printf(&quot;%04x\\n&quot;, cksum) }&#39;\n0000</code></pre>\n<p>All zeroes. When you think about it: That&#39;s confirmation that everything is correct. :-) The actual checksum is one of those 16-byte chunks, so zero is the correct answer.</p>\n<p>Exclude the checksum from the data:</p>\n<pre><code>$ dd if=zwei.raw bs=512 count=1 skip=1 status=none |\n    od -An -vt x2 -w2 |\n    sed 69d |\n    gawk &#39;{ v = strtonum(&quot;0x&quot; $1); cksum = xor(cksum, v) } END { printf(&quot;%04x\\n&quot;, cksum) }&#39;\n1dc6</code></pre>\n<p>There you go, that matches the little-endian <code>c61d</code> that we saw in the dump. (Obviously, because ... that&#39;s the line that I excluded ... yeah ... but you get the idea.)</p>\n<p>I&#39;m not familiar with NetBSD&#39;s code base yet, but I think this might be their code to compute the checksum (at least it does the same thing I did):</p>\n<p><a href=\"https://cvsweb.netbsd.org/bsdweb.cgi/src/sys/lib/libkern/dkcksum.c?rev=1.1.6.2;content-type=text%2Fplain\" rel=\"nofollow ugc noopener\">https://cvsweb.netbsd.org/bsdweb.cgi/src/sys/lib/libkern/dkcksum.c?rev=1.1.6.2;content-type=text%2Fplain</a></p>\n<h2 id=\"exercise-create-some-random-partition-in-the-hex-editor\">Exercise: Create some random partition in the hex editor</h2>\n<p>Let&#39;s do it the other way around: Given what we know now, open the hex editor and define partition <code>i</code>, size 1234 sectors, start at sector 5678, filesystem type ZFS.</p>\n<p>Partition <code>i</code> is the ninth partition, so its entry should start at byte 788 on the disk:</p>\n<pre><code>512 + 0x94 + (9 - 1) * 16 = 788\n\\_/   \\__/   \\_____/   |\n |      |       |      \\- Each label is 16 bytes in size, see\n |      |       |         disklabel.h.\n |      |       |\n |      |       \\- We want to know the *start* of the ninth label.\n |      |\n |      \\- Start of first partition entry, see hex dump above.\n |\n \\- Start of disklabel on disk.</code></pre>\n<p>We&#39;re going to write these fields in the partition table (this is already <em>little endian</em>):</p>\n<ul><li><code>p_size</code> = <code>d204 0000</code> for 1234 sectors</li><li><code>p_offset</code> = <code>2e16 0000</code> for offset 5678</li><li><code>p_fstype</code> = <code>21</code>, according to the table in <code>disklabel.h</code></li></ul>\n<p>We also need to update <code>d_npartitions</code> to <code>9</code>. All the partitions in between remain unused.</p>\n<p>I&#39;ll be overwriting <code>d_checksum</code> with four zeros, so that we can just run the <code>gawk</code> snippet above to calculate the new checksum. Lo and behold, it is: <code>160f</code> (little endian).</p>\n<p>These are the bytes that I touched:</p>\n<p>bine-edit.png: Screenshot of my hex editor, pink highlights show which bytes I changed.</p>\n<p>Let&#39;s boot the VM again and see what we got:</p>\n<pre><code>netbsd# disklabel ld1\n# /dev/rld1:\ntype: ld\ndisk: ld1\nlabel: My Cool Disk\nflags:\nbytes/sector: 512\nsectors/track: 63\ntracks/cylinder: 16\nsectors/cylinder: 1008\ncylinders: 260\ntotal sectors: 262144\nrpm: 3600\ninterleave: 1\ntrackskew: 0\ncylinderskew: 0\nheadswitch: 0           # microseconds\ntrack-to-track seek: 0  # microseconds\ndrivedata: 0\n\n9 partitions:\n#        size    offset     fstype [fsize bsize cpg/sgs]\n a:    262144         0     4.2BSD      0     0     0  # (Cyl.      0 -    260*)\n d:    262144         0     unused      0     0        # (Cyl.      0 -    260*)\n i:      1234      5678        ZFS                     # (Cyl.      5*-      6*)\ndisklabel: partitions a and i overlap</code></pre>\n<p>Looks good!</p>\n<p>(The partitions obviously overlap, because <code>a</code> was already the entire disk.)</p>\n<h2 id=\"conclusion\">Conclusion</h2>\n<p>This was a fun afternoon and I indeed feel more comfortable with disklabels now. As usual, I really have to have a <em>hands-on session</em> like this in order to grow familiar with something.</p>\n<p>As you can see, I marked some fields with <code>(?)</code>, because their meaning isn&#39;t entirely clear to me yet. Maybe more on that some other day (or maybe not).</p>","headings":[{"level":1,"text":"NetBSD: Playing with disklabels","id":"netbsd-playing-with-disklabels"},{"level":2,"text":"Where to find documentation?","id":"where-to-find-documentation"},{"level":2,"text":"Part 0: Where is the disklabel?","id":"part-0-where-is-the-disklabel"},{"level":2,"text":"Part 1: \"Disk metadata\"","id":"part-1-disk-metadata"},{"level":2,"text":"Part 2: The partition table","id":"part-2-the-partition-table"},{"level":2,"text":"Verifying the checksum","id":"verifying-the-checksum"},{"level":2,"text":"Exercise: Create some random partition in the hex editor","id":"exercise-create-some-random-partition-in-the-hex-editor"},{"level":2,"text":"Conclusion","id":"conclusion"}]}}