探索NetBSD磁盘标签

Lobsters Hottest 新闻

摘要

这篇文章通过十六进制编辑和C编程示例,详细介绍了NetBSD 11上BSD磁盘标签的格式、文档来源和结构。

<p><a href="https://lobste.rs/s/qknavp/netbsd_playing_with_disklabels">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/09/26 03:20

# NetBSD: 深入磁盘标签 来源:https://movq.de/blog/postings/2026-09-25/0/POSTING-en.html 博客 (https://movq.de/blog/)\-git (https://movq.de/git/)\-桌面 (https://movq.de/desktop/)\-联系 (https://movq.de/contact.html) --- 2026\-09\-25 由于我来自 DOS 和 Linux 环境(并且在我的 OpenBSD 系统上基本忽略了这点——是的,该为此感到羞愧),我对 BSD 磁盘标签不太熟悉。那么,让我们来探究一下。我不能完全确定所有 BSD 系统都使用完全相同的格式,而且据我所知,不同架构之间存在差异,所以让我们具体来说:我正在研究 x86\_64 架构上的 NetBSD 11。我将在虚拟机中运行所有内容,这样我可以轻松地更换磁盘并进行检查。为了避免与 MBR 系统冲突(https://en.wikipedia.org/wiki/BSD_disklabel#Where_disklabels_are_stored),磁盘标签存储在不同的位置。在我的测试虚拟机中,可以在第二个扇区找到它: bine-initial.png:十六进制编辑器截图: 在偏移量 0x200 处,我们可以看到磁盘标签的魔数。(https://movq.de/blog/postings/2026-09-25/0/bine-initial.png) 我主要对这个磁盘标签的格式感兴趣:各个字节意味着什么?在哪里有规范/文档? --- ## 目录 - 在哪里可以找到文档?(https://movq.de/blog/postings/2026-09-25/0/POSTING-en.html#where-to-find-documentation) - 第 0 部分:磁盘标签在哪里?(https://movq.de/blog/postings/2026-09-25/0/POSTING-en.html#part-0-where-is-the-disklabel) - 第 1 部分:“磁盘元数据”(https://movq.de/blog/postings/2026-09-25/0/POSTING-en.html#part-1-disk-metadata) - 第 2 部分:分区表(https://movq.de/blog/postings/2026-09-25/0/POSTING-en.html#part-2-the-partition-table) - 验证校验和(https://movq.de/blog/postings/2026-09-25/0/POSTING-en.html#verifying-the-checksum) - 练习:在十六进制编辑器中创建一个随机分区(https://movq.de/blog/postings/2026-09-25/0/POSTING-en.html#exercise-create-some-random-partition-in-the-hex-editor) - 总结(https://movq.de/blog/postings/2026-09-25/0/POSTING-en.html#conclusion) --- ## 在哪里可以找到文档? 这个问题很容易回答。查看`man disklabel`(正如你会“本能”地那样做,因为它是操作这些标签的工具),在`SEE ALSO`部分有`disklabel(5)`:第 5 部分描述文件格式,所以这是我们想要查看的(`man 5 disklabel`)。不过,对于本文的大部分内容,我直接查看了`/usr/include/sys/disklabel.h`而不是手册页。 ## 第 0 部分:磁盘标签在哪里? 首先是最重要的问题。手册页告诉你在哪里找到磁盘标签:`getlabelsector()`和`getlabeloffset()`就是答案。所以让我们写一个小程序来看看它们返回什么。 ```netbsd # cat test.c #include <stdio.h> #include <util.h> int main() { printf("getlabelsector() = %d\n", getlabelsector()); printf("getlabeloffset() = %ld\n", getlabeloffset()); return 0; } # cc -Wall -Wextra -o test test.c -lutil ``` 输出: ``` netbsd# ./test getlabelsector() = 1 getlabeloffset() = 0 ``` 所以扇区 1(在我的例子中是字节偏移量 512)是正确的。上面截图中的不是随机数据,那*就是*磁盘标签。磁盘标签以一堆关于磁盘的“元数据”开始,然后是实际的分区表。让我们先来看看这些元数据。这有点难以图解。先给出一个概览(在文本旁边的新标签页中打开它),然后我们将逐一查看: disklabel-metadata.png:我的磁盘标签的十六进制转储。颜色表示各个字段。(https://movq.de/blog/postings/2026-09-25/0/disklabel-metadata.png) 所有这些在我的 Intel 机器上都是*小端序*。 通用磁盘/驱动器信息: - `5745 5682`:`d_magic`,魔数 - `0f00`:`d_type`,驱动器类型 =`0x0f`= “逻辑磁盘” - `0000`:`d_subtype`,特定于`d_type` - `6c64...0000` / `"ld1"`:`d_typename` - `4d79...0000` / `"My Cool Disk"`:`d_packname`,用户输入的名称 `disklabel.h`包含`d_type`可能值的表格。 几何/大小: - `0002 0000`:`d_secsize`,每扇区 512 字节 - `3f00 0000`:`d_nsectors`,每磁道 63 个扇区 - `1000 0000`:`d_ntracks`,每柱面 16 个磁道 - `0401 0000`:`d_ncylinders`,每个单元 260 个柱面 - `f003 0000`:`d_secpercyl`,每柱面 1008 个扇区 - `0000 0400`:`d_secperunit`,每个单元 262144 个扇区(128 MiB) - `0000`:`d_sparespertrack`,每磁道无备用扇区 - `0000`:`d_sparespercyl`,每柱面无备用扇区 - `0000 0000`:`d_acylinders`,“备用柱面”(?) 硬件参数和时序,在 x86\_64 上可能非常无关紧要: - `100e`:`d_rpm`,3600 - `0100`:`d_interleave` - `0000`:`d_trackskew` - `0000`:`d_cylskew` - `0000 0000`:`d_headswitch`,磁头切换时间(微秒) - `0000 0000`:`d_trkseek`,磁道到磁道寻道时间(微秒) - `0000 0000`:`d_flags`,“通用标志” :-) - 5x`0000 0000`:`d_drivedata`,驱动器类型特定 - 5x`0000 0000`:`d_space`,保留 第一部分的结束: - `5745 5682`:`d_magic2`,魔数 - `c61d`:`d_checksum` - `0400`:`d_npartitions`,下面数组中有多少个分区 - `0020 0000`:`d_bbsize`,启动区域在 sn0 的大小(字节) - `0020 0000`:`d_sbsize`,文件系统超级块的最大大小(字节) 这些是我通过手动读取十六进制转储并与头文件中的`struct disklabel`比较后确定的值。就我所知,它与`disklabel`工具的输出匹配: ``` netbsd# disklabel ld1 # /dev/rld1: type: ld disk: ld1 label: My Cool Disk flags: bytes/sector: 512 sectors/track: 63 tracks/cylinder: 16 sectors/cylinder: 1008 cylinders: 260 total sectors: 262144 rpm: 3600 interleave: 1 trackskew: 0 cylinderskew: 0 headswitch: 0 # microseconds track-to-track seek: 0 # microseconds drivedata: 0 ``` 很多信息感觉相当过时,至少在“现代” x86\_64 上是这样。(但 MBR 在这方面也没好*多少*,仍然有所有的 CHS 东西。)而且其中很多确实未被使用,只是设置为 0。 ## 第 2 部分:分区表 `d_npartitions`说有四个分区。这里是该表格的概览,它紧跟在“元数据”部分之后: disklabel-partitions-overview.png:与上面相同的十六进制转储,但分区表所在区域被高亮显示。(https://movq.de/blog/postings/2026-09-25/0/disklabel-partitions-overview.png) 这对应于分区`a`、`b`、`c`和`d`。让我们仔细看看分区`a`: disklabel-partition-a.png:与上面相同的十六进制转储,分区'a'的各个字段被高亮显示。(https://movq.de/blog/postings/2026-09-25/0/disklabel-partition-a.png) 各个字段: - `0000 0400`:`p_size`,262144 个扇区 - `0000 0000`:`p_offset`,0 个扇区 - `0000 0000`:`p_fsize`,“文件系统基本片段大小”(?) - `07`:`p_fstype`,文件系统类型 =`0x07`=`4.2BSD / ffs` - `00`:`p_frag`,“文件系统每个块的片段数”(?) - `0000`:`p_cpg`或`p_sgs`:UFS 或 LFS 特定 `disklabel.h`包含`p_fstype`可能值的表格。我认为只有三个重要的信息: - 起始, - 结束, - 分区类型。 再次,我的解释与`disklabel`的输出匹配: ``` netbsd# disklabel ld1 ... 4 partitions: # size offset fstype [fsize bsize cpg/sgs] a: 262144 0 4.2BSD 0 0 0 # (Cyl. 0 - 260*) d: 262144 0 unused 0 0 # (Cyl. 0 - 260*) ``` 根据 Martin Husemann 的这封邮件(https://mail-index.netbsd.org/netbsd-users/2018/09/22/msg021452.html),分区`d`在 x86\_64 上始终存在,并描述整个磁盘。分区`c`将是 NetBSD 可用的区域。为什么这里没有输出`c`,我不确定。(实际上,不,我想我知道原因:大小、偏移量和类型都是零,所以它被隐藏了,我猜。但为什么当我第一次创建磁盘标签时`disklabel -I`没有包含`c`?) ## 验证校验和 头文件说: ``` uint16_t d_checksum; /* xor of data incl. partitions */ ``` 所以...我想我们将所有这些数据分成 16 字节的块,然后将它们全部异或?让我们试试: ``` $ dd if=zwei.raw bs=512 count=1 skip=1 status=none | od -An -vt x2 -w2 | gawk '{ v = strtonum("0x" $1); cksum = and(xor(cksum, v), 0xFFFF) } END { printf("%04x\n", cksum) }' 0000 ``` 全零。当你想想:这证实了一切都是正确的。:-) 实际的校验和是那些 16 字节块之一,所以零是正确的答案。从数据中排除校验和: ``` $ dd if=zwei.raw bs=512 count=1 skip=1 status=none | od -An -vt x2 -w2 | sed 69d | gawk '{ v = strtonum("0x" $1); cksum = and(xor(cksum, v), 0xFFFF) } END { printf("%04x\n", cksum) }' 1dc6 ``` 这就对上了,它与我们在转储中看到的小端序`c61d`匹配。(显然,因为...那是我排除的那一行...是的...但你明白了。) 我还不熟悉 NetBSD 的代码库,但我认为这可能是他们计算校验和的代码(至少它做了和我一样的事情): https://cvsweb.netbsd.org/bsdweb.cgi/src/sys/lib/libkern/dkcksum.c?rev=1.1.6.2;content-type=text%2Fplain ## 练习:在十六进制编辑器中创建一个随机分区 让我们反其道而行:根据我们现在所知的,打开十六进制编辑器并定义分区`i`,大小 1234 个扇区,从扇区 5678 开始,文件系统类型 ZFS。 分区`i`是第九个分区,所以它的条目应该从磁盘的字节 788 开始: ``` 512 + 0x94 + (9 - 1) * 16 = 788 \_/ \____/ \_______/ | | | \- 每个标签大小为 16 字节,参见 | | | disklabel.h。 | | | /- 我们想知道第九个标签的*起始位置*。 | | |-----/ | |-----/(第一个分区条目的起始位置,参见上面的十六进制转储) |-------/(磁盘上磁盘标签的起始位置) ``` 我们将在分区表中写入这些字段(这已经是*小端序*了): - `p_size`=`d204 0000` 表示 1234 个扇区 - `p_offset`=`2e16 0000` 表示偏移量 5678 - `p_fstype`=`21`,根据`disklabel.h`中的表格 我们还需要将`d_npartitions`更新为`9`。所有中间的分区保持未使用状态。我将把`d_checksum`覆盖为四个零,这样我们就可以直接运行上面的`gawk`片段来计算新的校验和。看哪,它是:`160f`(小端序)。这些是我修改过的字节: bine-edit.png:我的十六进制编辑器截图,粉色高亮显示我更改的字节。(https://movq.de/blog/postings/2026-09-25/0/bine-edit.png) 让我们再次启动虚拟机,看看我们得到了什么: ``` netbsd# disklabel ld1 # /dev/rld1: type: ld disk: ld1 label: My Cool Disk flags: bytes/sector: 512 sectors/track: 63 tracks/cylinder: 16 sectors/cylinder: 1008 cylinders: 260 total sectors: 262144 rpm: 3600 interleave: 1 trackskew: 0 cylinderskew: 0 headswitch: 0 # microseconds track-to-track seek: 0 # microseconds drivedata: 0 9 partitions: # size offset fstype [fsize bsize cpg/sgs] a: 262144 0 4.2BSD 0 0 0 # (Cyl. 0 - 260*) d: 262144 0 unused 0 0 # (Cyl. 0 - 260*) i: 1234 5678 ZFS # (Cyl. 5*- 6*) disklabel: partitions a and i overlap ``` 看起来不错!(分区显然重叠了,因为`a`已经占用了整个磁盘。) ## 总结 这是一个有趣的下午,我确实感觉对磁盘标签更熟悉了。和往常一样,我真的需要这样一个*动手实践*的环节才能熟悉某样东西。如你所见,我用`(?)`标记了一些字段,因为它们的含义对我来说还不完全清楚。也许改天再说(或者也许不)。

相似文章

NetBSD 11.0 发布

Lobsters Hottest

NetBSD 11.0 历经长期延迟后终于发布,现已提供安装镜像和发布说明。该版本承认存在未解决的安全问题,并计划在两个月内发布后续的 11.1 版本。

基于 NetBSD 的研究

Hacker News Top

本 NetBSD 画廊页面重点展示了各类基于该操作系统的学术及工业研究项目,包括 NASA 的卫星 TCP 实验、KAME IPv6/IPsec 项目以及高速网络演示。

NetBSD GSoC 2026 改进 RAIDframe

Hacker News Top

这篇文章报道了Google Summer of Code 2026年在NetBSD中改进RAIDframe的工作,包括实现用于多磁盘镜像的N-way RAID 1和添加RAID scrubbing支持。

FreeBSD 吃掉我的内存

Hacker News Top

一篇解释为什么 FreeBSD 看起来占用大量内存的文章,将其归因于磁盘缓存和虚拟内存管理,类似于 Linux 的“吃掉我的内存”现象。