Fixing “error checking mount status” when using mkfs.btrfs to a new file

(Update: Fix is also available at http://permalink.gmane.org/gmane.comp.file-systems.btrfs/15906)

Symptom

When you're following instructions to debug btrfs with gdb and UML (User Mode Linux), you might come across this error when making btrfs on a file:

error checking <file name> mount status

And here're the steps before:
1. Download latest btrfs-prog(git clone git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-progs.git)
2. Build the tools
3. Run mkfs.btrfs <image file>

Root cause

If you're sure that you just created the file with dd and not touching it, then it is quite likely you hit an issue in the mkfs.btrfs code which works well on later linux kernel.

mkfs.btrfs will check if the target device/file is mounted anywhere or it is formatted in btrfs before it do any write. One last thing is to check that the file is not backing a loop device. In utils.c, it resolves the loop device by parsing /sys/block/<loop name>/loop/backing_file, which doens't exist until 2.6.37 (I'm running on 2.6.29). Failure in opening the file results in failure of the whole operation.

Fix

Unmount any loop device

I'm not going to file any bug request or provide any fix myself, due to the fact that this issue won't happen in the real environment (should be running a later linux kernel). So just put it down with the workaround for your reference.

宝山图书馆半日游

计划很久的一次探访,上次似乎是在两年前,那会儿宝图还在改建,事先没做好功课(或者是网上信息提示不够充分),家门口坐了116B(那会儿还叫116)直接杀过去,下车了却发现是一个工地(虽然封顶了)。后话是直接在宝杨路坐车去了顾村,然后再转528回家。

直接上图:

宝图建筑外立面,字体很特殊

 

进门处的自助办证机,非常方便,1分钟完成办证。另外罚款通过充值完成。我忍不住也办了一张,借了本书。后来想到还书还得回来,囧。

 

一楼的英文杂志区,数量不多,不过居然有《Mac World》

 

历年国家地理杂志合订本,这个很不错,而且可以外借

 

所有的电脑的主机就是显示器后面一个小盒子,目测似乎是瘦终端,跑虚拟化。

 

一楼的露天平台,远处是二楼和三楼的平台,可以拿着图书馆的书坐着看。

 

从二楼看一楼的入口

 

3楼的指示牌

 

上海中心图书馆的书架,占了3楼的一小块

 

回来在海江路上看到的温德姆酒店,有点像威尼斯人酒店

 

回来路上碰到3号线北段的悲剧间隔

MBR,GPT与Boot Camp

MBR

作为BIOS时代的分区管理格式,MBR (Master Boot Record)已经走过了31个年头。1982年,MBR被引入IBM PC DOS 2.0,用来支持10MB大小的硬盘。

MBR的结构非常简单。前面446字节是引导代码,接着4条16字节的主分区信息,最后是两个字节的签名(魔数):

Start Size Description
0 446 Boot code
446 16 Primary partition entry 1
462 16 Primary partition entry 2
478 16 Primary partition entry 3
494 16 Primary partition entry 3
510 2 Signature (0xaa55)

这么看每个磁盘的分区数被限死在了4个,不过后来又引入了扩展分区技术(EBR)解决(或者说是绕过)了这个问题。简单来说,EBR就是把最后一个主分区特别对待,这个分区(又称扩展分区)的第一个扇区看成是一个MBR,又叫EBR,不过不同的是EBR只用了前两个分区项(同样从446字节处开始),第一项指明了新的普通分区,而第二项除了可以指新的普通分区外,还可能用来指向新的扩展分区。这样通过链表的形式把非主分区(又称逻辑分区)给串起来。下面这两张张图(来自MSDN)基本说明了是怎么回事:

 

上图前三个主分区项都指向了普通分区,最后一项指向了扩展分区。下图则演示了扩展分区的细节:

 

EBR

扩展分区里只用了两项分区项,第一项指向普通分区,第二项指向下一个扩展分区。前一级扩展分区总是涵盖了后面的所有分区,形成了嵌套的链接关系。

这样的做法缺点很明显,链表当中要是断了,后面的可就都丢掉了。另外要让一些主流操作系统从分主分区启动也要花一些功夫。

下面简单说说分区项的具体内容:

Start Size Descripton
0 1 Status (0x80 = boot flag)
1 3 CHS start (deprecated, use 0xfeffff)
4 1 Partition type
5 3 CHS end (deprecated, 0xfeffff)
8 4 LBA start (in sectors)
12 4 LBA size (in sectors)

可以看到一个明显的限制是磁盘的大小。LBA的宽度只有4个字节,这样只能用来描述最大2TB的磁盘,再大就不够了(有别的hack,不过至少肯定不能用来当启动盘)。另外分区类型最多256种,不够描述当今所有的文件系统类型(包括操作系统)。MBR格式(以及其背后的BIOS)过于古老,严重阻碍了新技术的推广。但是PC市场这么大,想要改也没那么容易,涉及到硬件软件以及大量第三方公司和整个产业链。

EFI

Intel和HP在90年代一起研发Itanium的时候,就打算趁引入新架构的机会也把老掉牙的BIOS踢到一边去。98年搞了一个项目叫Intel Boot Initiative,后来更名为EFI,2005年又更名为现在的名字UEFI。Itanium算是没做起来,UEFI这些年的势头开始起来了,除了Intel架构的Mac以外,Windows 8的加入无疑将大大加速UEFI的推广(虽然Secure Boot备受争议)。

EFI的一个组成部分是规定了新的磁盘分区格式GPT(GUID Partition Table)。首先GPT支持最高达8ZB(4PB*2M)的磁盘容量(在可见的未来足够了),以及最多128项磁盘分区。另外出于保护数据的目的,分区表在磁盘上存放了两份,分别放在磁盘的开头和结尾,以防以外丢失。

保护MBR(Protective MBR)

由于市场上大量非EFI机器的存在,GPT通过保护MBR来达成与MBR的兼容,或者说是保护GPT分区不被非EFI机器破坏。方法很简单,结合上面MBR的描述就可以明白:

  1. 占领MBR的第一项分区项;
  2. 分区类别设定为特殊类型;
  3. 起始地址为扇区1;
  4. 分区大小为磁盘大小-1。

这样非EFI机器读MBR的时候,就会发现整个磁盘已经被别的分区全部占满了,而用户也不会轻易的重新分区。这就是所谓的“保护性”。

GPT头和分区项

GPT头描述了磁盘和分区表的基本信息,如磁盘的GUID和分区表的大小。分区项最多有128项,一般4项为一个单位。分区项每项占128字节:

Start Size Descrption
0 16 Partition type GUID
16 16 Unique Partion GUID
32 8 Start LBA
40 8 Last LBA
48 8 Flags
56 72 Partition name

(令人意外的是,分区类型虽然是GUID,不过已经有冲突了)

GPT和MBR这两种磁盘分区格式可以说是天差地别,融合起来可不是很容易。不过好在GPT给MBR让出了扇区0作为保护MBR,在一个磁盘上支持两种分区格式也成为可能。典型的做法是Boot Camp的实现。

Boot Camp

苹果在2005年宣布使用Intel作为Mac平台以后,由于控制了硬件软件平台,使得它应用EFI没有什么问题。不过大部分Windows并不支持GPT而只能从MBR磁盘启动,苹果也提供了Boot Camp方便使用者在Mac上安装双系统。

Boot Camp的原理是混合MBR (Hybrid MBR)。利用MBR里的空余主分区项,使得一个分区在两个分区表里都有份。

前面说过了保护性MBR的做法是把整个磁盘写成一个大分区,这时候这个大分区被Mac OSX占据了。如果我们把Mac OSX分区缩小(比如一半),那后面一半分区就可以腾出来给Windows用了。要做到这一步很简单,只需要把保护性MBR里那条特殊的分区项的分区大小改小就可以了,另外需要往MBR里再加一条分区项(并设置成启动),描述后面半个磁盘上的Windows分区。

操作系统按道理可以安装启动了,不过下一步我们还需要在两个操作系统里分别都能看到对方操作系统的分区,至少方便拷贝数据(假定都有相应文件系统的驱动)。对于Windows来说,由于MBR里已经有两条分区项充分描述了这两个分区,所以Windows里能够看到两个分区;但是对于Mac OSX来说,它只认识GPT的分区,所以还看不到Windows的分区。当然这也很简单,只要往GPT分区表里加一条描述Windows分区的分区项就可以了。

具体来说Boot Camp做了三件事:

  • 把Mac OSX分区缩小(Shrink HFS+)
  • 改动GPT和MBR的分区表;
  • 在启动菜单里加上其他操作系统的选项,另外给其他操作系统提供驱动。

下面是我启用了Boot Camp的MBR导出结果:

00fe ffff eefe ffff 0100 0000 2740 0600   <<<< EFI partition (200M ESP)
00fe ffff affe ffff 2840 0600 0047 0740   <<<< Mac main partition, size 0x40074700 (512G)
00fe ffff abfe ffff 2887 0d40 205f 1300   <<<< Mac recovery partition
80fe ffff 07fe ffff 00e8 2040 0078 3317   <<<< Windows partition, size 0x1733008 (185G), boot bit             
                                               (first bit) is set

或者是用fdisk导出结果

wum@dev$ sudo fdisk -d /dev/rdisk0

1,409639,0xEE,-,1023,254,63,1023,254,63
409640,1074218752,0xAF,-,1023,254,63,1023,254,63
1074628392,1269536,0xAB,-,1023,254,63,1023,254,63
1075898368,389249024,0×07,*,1023,254,63,1023,254,63

参考文献

Hybrid MBRs

What's a GPT?

Apple support of GPT

更换Mac OSX启动内核(XNU)

前面的文章讲述了编译Mac OSX内核的基本步骤,下面的步骤就是更换内核重启了。对于Macbook的同学来说,除了如何把内核启动起来,最关心的恐怕就是如果内核crash的话,怎么样换回来原来的内核文件,至少别把自己的Mac给玩挂了。
网上搜了很多资料,最靠谱的就是这封邮件列表里的邮件了。邮件的标题是“choosing kernel on Intel”,我想很大一部分原因是之前众多的资料是关于PowerPC平台的Open Firmware(连苹果官网里的文章还停留在那个年代)。而现在鸟枪换炮用上了EFI这个新鲜玩意儿,方法自然是大不同了。(而苹果却又没把EFI shell这些东西给提供出来,也给开发者造成了一些麻烦。不过从苹果角度出发的确也没必要给普通用户提供SHELL)。
下面是这些方法:

EFI Shell

装个EFI Shell,比如rEFIt。rEFIt提供了在Macbook上一些方便的安装方法,比如光盘启动、U盘启动或者直接放硬盘上。其中硬盘上的安装一步到位,兼容BootCamp,开机以后看到的就是rEFIt的界面,几个菜单项,如下图,另外每个菜单项还可以有简单的定制,以带入不同的启动选项。左下角就是EFI Shell。

相比于古老的BOIS,EFIShell提供了强大的功能和运行环境,具体的用法可以看Intel的这个手册,或者中文版。常用的命令有:
(EFI Shell进入后分辨率偏低,可以通过mode命令调整。打一个命令往往会超出屏幕的显示范围,这个时候需要在每个命令后加一个“-b“的选项,达到more命令那样的效果。)

  • help/?: 用法很简单,help <command>,类似man
  • fs1:     切换文件系统,类似于DOS下的盘符切换。具体有什么fs可以切换参见map命令
  • map:显示当前映射表,也可以把当前的块设备加上盘符;
  • 简单的文件系统命令,比如cd/rm/mkdir等等,还可以通过edit, hexedit来编辑文件(这个很有用,可以用来改下面步骤里的东西)

而如果需要启动内核的话(就像grub里敲initrd,boot),只需要找到你Mac OSX的挂载盘(我的是fs1)
fs1:/> \System\Library\CoreServices\boot.efi Kernel=mach_kernel.test
我相信应该还可以带一些别的参数,比如调试位,不过目前还没有尝试过。

编辑启动文件

这应该是最简单的办法,也是邮件里最推荐的办法。
打开/Library/Preferences/SystemConfiguration/com.apple.Boot.plist,往里面增加一项

<key>Kernel</key>
<string>mach_kernel.test</string>

如果你自己编译的内核挂了,建议的办法是用rEFIt引导,然后修改配置文件。建议无论如何都要在文件系统里保留一份mach_kernel的备份。

NVRAM变量

修改NVRAM里的boot-args变量,使用以下命令:

$sudo nvram boot-args=”debug=0x144 -v Kernel=mach_kernel.test”

如果玩坏了,办法是在启动时按住“Cmd-Opt-P-R”把NVRAM清空(并载入默认选项)。当然如果你把mach_kernel搞坏了这招估计行不通。

EFI启动选项

$sudo bless –mount / -setBoot –options “Kernel=mach_kernel.test”

这是传给EFI启动的引导选项(显然可以用rEFIt引导来跳过),如果你想暂时用原来的kernel启动,则在启动时按住Opt,进入启动菜单,并选择Mac OSX进行启动。这样会覆盖bless的选项,从而直接启动mach_kernel。同样用上面的办法清空NVRAM也可以达到同样的目的(只不过下次启动就是默认了)。

在Mac OSX 10.7(Lion)上编译XNU内核

Apple虽然备受封闭系统的指责,但事实上只要访问http://www.opensource.apple.com,你就能看到苹果对开源软件的贡献。其中最值得一提的就是OSX内核——XNU。
根据我的理解,XNU是Mac OSX的内核,是一种融合了Mach、BSD和IOKit的混合型内核。其中Mach提供核心的基本操作(IPC、同步、VM、进程管理),BSD负责POSIX API,网络,文件系统(以及很多其他),另外IOKit则包含了驱动模型。
虽然网上有不少关于如何编译XNU的文章(包括项目),但在我的试验中,最后能够成功编译的只有这篇文章(因为正好符合各项条件,另外10.8的文章链接在此,都需翻墙),苹果官网上的文档甚至还停留在PowerPC的时代,告诉你如何通过Open Firmware进入调试模式,而Macbook早已在5年前转换到Intel平台,使用EFI作为bootloader。(另外一个明显问题是gcc4.3可以编译最新XNU,无需倒退到3.3)
具体的步骤我不逐句搬过来,概括来说就是:

  1. 下载dtrace(调试追踪工具)和bootstrap_cmds(用来生成Mach IPC服务端代码,又称Mach Interface Generator,这个是服务器版叫migcom);
  2. 分别编译并安装;
  3. 进入xnu代码目录编译;
  4. 在BUILD/obj/RELEASE_<arch> 目录里的mach_kernel就是最终文件。

有几点需要注意:

  • CC和C++一定要换成GCC前端而不是clang,CC是一个符号链接,C++我改了符号链接似乎还不行,最后指定了CXX的环境变量。
  • dtrace和bootstrap_cmds可以在http://www.opensource.apple.com/tarballs/下载打包好的tarball;
  • Xcode的Command Line Tools需要升到最新,这个不会跟着Xcode一起升级,否则在安装bootstrap_cmds时install_name_tool会报告“malformed object (unknown load command XX)”,具体看这个问答

编译完成之后当然就是更换内核重新启动了。具体步骤请看这里
另外编个内核好歹留点纪念吧,如果需要在uname信息留下你的印记的话,编辑config/version.c (我的OS是中文的,居然uname还给出中文了)

wum@xnu-1699.32.7$ uname -a
Darwin Marshalls-MacBook-Pro.local 11.4.2 DarwinMarshall’s Kernel Version 11.4.2: 2013年 2月24日 星期日 20时20分00秒 CST; wum:xnu-1699.32.7/BUILD/obj//RELEASE_X86_64 x86_64

最后附上原文里的一些步骤,防止原来的链接丢失。

  1. Build dtrace

    $ cd dtrace-90
    $ mkdir -p obj sym dst
    $ xcodebuild install -target ctfconvert -target ctfdump -target ctfmerge ARCHS="i386 x86_64" SRCROOT=$PWD OBJROOT=$PWD/obj SYMROOT=$PWD/sym DSTROOT=$PWD/dst
    ...
    $ sudo ditto $PWD/dst/usr/local /usr/local
    Password:
    $ cd ..
  2. Build bootstrap_cmds

    $ cd bootstrap_cmds-79
    $ mkdir -p obj sym dst
    $ make install RC_ARCHS="i386" SRCROOT=$PWD OBJROOT=$PWD/obj SYMROOT=$PWD/sym DSTROOT=$PWD/dst
    ...
    $ sudo ditto $PWD/dst/usr/local /usr/local
    Password:
    $ cd ..
  3. Build xnu

    $ cd xnu-1699.22.73
    $ make ARCH_CONFIGS="I386 X86_64" KERNEL_CONFIGS="RELEASE"
    ...
    $ file BUILD/obj/RELEASE_*/mach_kernel
    BUILD/obj/RELEASE_I386/mach_kernel: Mach-O executable i386
    BUILD/obj/RELEASE_X86_64/mach_kernel: Mach-O 64-bit executable x86_64