裸机恢复,重温

作者:Charles Curley

想象一下,你的硬盘驱动器刚刚变成了一个非常昂贵的冰球。 想象一下,你家发生了火灾,你的电脑机箱现在看起来像是萨尔瓦多·达利会喜欢画的东西。 现在怎么办?

这就是我在 2000 年 11 月刊的 *Linux Journal* 上一篇关于这个主题的文章的开头方式。 这篇文章描述了一个备份计算机并随后将其恢复到裸机的过程。 这篇文章描述了一套脚本,这些脚本是备份过程和恢复过程的一部分。 读者可以在 www.linuxjournal.com/article/4175 找到这篇文章。

从那时起,我向这套脚本添加了一些脚本。 大部分新脚本是为网络备份设计的,并利用了安全外壳 (SSH)。 (有关 SSH 的更多信息,请参阅 Mick Bauer 在 2001 年 1 月和 2 月刊的 *LJ* 中的 “OpenSSH 的 101 个用途”)。 我还对原始文章中介绍的脚本做了一些更改。 修订后的脚本套件可在我的主页上找到(请参阅“资源”)。

缺陷

我的 2000 年 11 月文章及其描述的过程的最大问题是,该过程在恢复过程的开始时需要大量输入。 您必须手动将分区边界和其他数据输入 fdisk,然后对照您的打印输出检查结果。(打印输出!?看在墨菲定律的份上!) 然后您手动为每个分区创建适当的文件系统。 然后您手动挂载它们。

这需要大量输入。 我不知道在写这篇文章时,我在我的测试计算机上进行了多少次测试备份和恢复。 肯定比我想再做的次数要多。 这也容易出错。 过一会儿,所有这些数字开始变得模糊。

显而易见的解决方案是一两个脚本。 我们需要的是一个脚本,它可以将分区信息恢复到硬盘驱动器,然后构建文件系统并挂载它们,以便您可以运行第一阶段恢复。

我对这个脚本的第一次尝试是脚本 make.partitions,它可以在我的主页上的脚本 tarball 中找到。 它有两个问题:首先,它不重建分区,因此您仍然必须手动运行 fdisk; 其次,它必须为每台计算机手动创建。 添加、删除、重新格式化或以其他方式修改分区,您都必须编辑脚本。 这还不够好。 该脚本,它是 GPL 许可的,应该看起来有点像列表 1。

列表 1. make.dev.hda

一个编写脚本的脚本

第二个解决方案要聪明得多。 为什么不自动化这个过程呢? 我们使用 gcc 编译 gcc。 哎,您可以使用 gcc 编译 Perl。 为什么不创建一个脚本来创建 make.partitions 应该成为的脚本呢? 为什么不创建一个编写脚本的脚本呢?

make.fdisk 解析来自 fdisk -l 和 mount -l 的输出,并创建一个新脚本来恢复给定的硬盘驱动器。

使用重定向

我们面临的第一个问题是我在原始文章中提到的一个问题:fdisk 不以允许以后重新导入的方式导出分区信息。 虽然其他版本的 fdisk 允许导出,但 tomsrtbt(我为裸机恢复推荐的基于软盘的发行版)附带了 fdisk,我不想重建 tomsrtbt 磁盘。 我们可以使用所有行为良好的 Linux 程序都具有的功能来处理这个问题:I/O 重定向。 给定一个程序 foo 和一个名为 bar 的 foo 命令文件,我们可以通过将 foo 的输入从键盘重定向到 bar 来将命令馈送到 foo,如下所示

foo < bar

所以我们想要能够做的是

fdisk /dev/x < dev.x
其中 x 是要重建的硬盘驱动器的名称。

make.fdisk 创建两个文件。 一个是可执行脚本,名为 make.dev.x,如列表 1 所示。 另一个 dev.x 包含 fdisk 构建分区所需的命令。 您可以通过将关联的设备文件命名为 make.fdisk 的参数来指定要为其构建脚本的硬盘驱动器(以及文件名)。 例如,在典型的 IDE 系统上,

make.fdisk /dev/hda

输出 make.dev.hda 脚本和 fdisk 的输入文件 dev.hda。

工作原理

当您查看列表 2 中显示的脚本 make.fdisk [可在 ftp.linuxjournal.com/pub/lj/listings/issue100/5484.tgz 找到] 时,请记住在什么时间发生什么。 就像 C 源代码一样,有些事情发生在稍后,在运行时。 其他事情发生在程序编译时,例如定义的评估和头文件的包含。

在检查 make.fdisk 时,我们首先看到的是它是一个 Perl 脚本。 接下来是脚本功能的简要描述。 之后是时间戳和两个版权声明。 然后我们看到通常的声明,即代码是自由软件,并根据通用公共许可证分发。 接下来是对我们已经看到的 fdisk 问题的详细描述以及解决方案。 以这种方式记录程序是良好的编码实践; 它使程序几乎可以自我记录。

现在我们开始实际的 Perl 代码。 子程序 cut2fmt 接受一系列列号,并计算一个格式字符串,供以后与 unpack 一起使用。 在子程序之后,我们使用它来创建一个格式字符串,以解包来自 fdisk 的输出。

之后是一系列 fdisk 输出中列的定义。 有了这些,我们可以按名称而不是按列号索引到使用 unpack 创建的数组中。 这应该使脚本更易于阅读和更易于维护。

将要挂载重建的硬盘驱动器的目录被命名为 $target,以便第一阶段恢复可以找到它。 确保这与您的 restore.metadata 脚本副本中 $target 的定义一致。

接下来,代码处理设备名称以生成我们将发送输出的文件名。 然后我们定义将放置输出文件的目录的路径。

磁盘标签

标签是 Linux 用于抽象分区的工具。 在 fstab 中使用设备文件名的​​问题在于,如果您添加或删除硬盘驱动器,可能会影响另一个分区显示的设备文件。 标签随分区一起移动,因此通过按标签挂载,您始终可以获得正确的分区。 它们对我们来说是一个问题,因为 tomsrtbt 不处理标签。

代码的下一部分使用命令行开关执行 mount,使其显示标签。 如果任何给定行中存在标签,我们会将标签和设备文件名保存在哈希中。 这样,稍后当我们在分区中创建文件系统时,我们可以分配标签。 此外,我们需要通过设备文件名挂载分区,以便我们可以恢复到它。 我们创建一个从设备文件名到挂载点的哈希映射,以便稍后我们可以构建挂载点目录并挂载分区。

接下来是一个典型的 Perl 命令,用于生成一个进程并将结果放入文件句柄中,在本例中是 FDISK。 它已完成错误检查。 然后我们打开我们的输出文件,该文件最终将被重定向为 fdisk 的输入。

现在我们开始一个循环来解析来自 fdisk 系统调用的输出的每一行。 我们对其中包含设备的任何行感兴趣。 如果我们找到一个,我们会稍微处理一下它,将其解包到数组 @_ 中,并进一步处理数组元素。

磁盘分区

如果分区号小于五,则它要么是主分区,这意味着它可以包含文件系统,要么是扩展分区,这意味着它可以包含多个逻辑分区。 在任何一种情况下,我们都将构建分区的命令写入输出文件。 如果它是 Linux 交换分区,我们必须告诉 fdisk 更改其分区类型。

如果我们看到一个主分区,它是 FAT(但目前不包括 FAT32)、Linux 或 Linux 交换分区,我们会将适当的命令附加到 $format,使该分区成为 FAT、ext2 文件系统或交换分区。 稍后,我们将使用 $format 创建输出脚本。

分区号为五或更大的分区只能是逻辑分区,即包含在扩展分区中的分区。 就我们而言,这些分区可以是 Linux ext3fs、Linux 交换分区、FAT 或任何其他分区。 如上所述,适当的 fdisk 命令被发送到输出文件,并且创建文件系统的适当命令被附加到 $format。

我们查看每个 ext2 分区是否有标签。 如果有,我们使用一个命令,该命令将在新分区上重新创建该标签,否则我们使用相同的命令,但不带标签。

坏块检查

您会注意到有两个命令来创建每个文件系统,其中一个被注释掉了。 被注释掉的那个创建的文件系统没有坏块检查。 如果我要安装到全新的硬盘驱动器,我会考虑使用这个。 另一个执行坏块检查。 我更喜欢在重用硬盘驱动器时检查坏块。 坏块检查是一个简单的只读测试,在大多数情况下是合理的。 您可以通过将 -w 添加到坏块的命令行选项来添加写入测试,该测试更彻底但需要更长时间。 写入测试是破坏性的,但由于您将在分区中构建新的文件系统,因此您不在乎。

在我们的行解析循环结束时,如果任何分区被标记为“可启动”(通常是 MS-DOS、Windows 或 Windows NT 分区,因为 LILO 忽略可启动标志),我们会将使其可启动的命令发送到命令文件。

我们为命令文件做的最后一件事是发送一个“v”,这将使 fdisk 验证新构建的分区表。 然后我们发送一个“w”,这将导致 fdisk 将分区表写入硬盘驱动器,然后退出。 然后我们关闭我们的两个文件。

接下来,我们打开将成为我们脚本的文件,并向脚本发送一个适当的标头,类似于此脚本的标头。 脚本实际要做的第一件事是使用 dd 将零写入硬盘驱动器的前 1,024 个字节。 这将覆盖任何现有的主引导记录 (MBR),这样我们就不必担心在创建新分区之前删除分区。

下一步是创建将对硬盘驱动器进行分区的命令,使用我们已经创建的命令文件。 然后代码遍历挂载点哈希,创建注释行、创建目录的命令,然后是将设备文件名挂载到目录的命令。

我们必须从根分区开始挂载,以便在正确的分区中创建挂载点。 例如,假设 /usr/local 在其自己的分区上; 我们必须在构建 /usr/local 之前挂载 /usr。 为了确保完成此操作,我们对哈希的键进行排序并按该顺序处理哈希。

我们做的最后一件事是更改我们刚刚创建的文件的模式。 由于偏执狂活得更久,我们禁止除 root 之外的任何人甚至读取脚本,并使其可执行。

使用脚本

脚本 make.fdisk 应该作为为裸机恢复备份做准备的正常部分运行。 在运行 save.metadata 之前运行它,以便将输出文件保存到 ZIP 驱动器。 更好的是,让 save.metadata 调用它,每个硬盘驱动器调用一次。

当您恢复时,为您拥有的每个硬盘驱动器运行 make.dev.x。 同样,这可以通过将其包含在 restore.metadata 中来实现自动化。

您可以使用此脚本做其他事情。 假设您要添加一个新分区。 使用裸机备份过程保存硬盘驱动器,然后编辑 dev.x 命令文件以更改分区定义,并使用编辑后的文件进行恢复。 我使用这种技术成功地向我的测试计算机添加了一个 30MB Mess-DOS 分区。

改进

如果您愿意,您可以处理的一些改进包括让 make.fdisk 处理多个硬盘驱动器,所有硬盘驱动器都在命令行中指示; 为 make.fdisk 的参数添加错误检查,使其生成一个构建所有硬盘驱动器的脚本,扩展 FAT 文件系统支持(例如,目前代码忽略 FAT32); 并扩展代码以支持其他文件系统。

资源

Charles Curley (w3.trib.com/~ccurley) 是一位自由软件工程师、作家,偶尔也是怀俄明州荒野中的牛仔。 有时,当他在后院写文章时,一些鹿会漫步而过,他就会失去思路。

加载 Disqus 评论