Linux 完整备份与恢复 HOWTO

Charles Curley

           
        

修订历史
修订 2.12006-03-28修订者:c^2
添加了 NTFS 的注释。编辑了待办事项列表。开始研究 LVM 和使用 finnix
修订 2.02005-10-12修订者:c^2
Fedora Core 4 的注释。移除了旧版本 FC 和 Red Hat 的注释。此外,修改了文档和脚本以反映使用 Knoppix 而不是 tomsrtbt。请参阅脚本中的更改注释。更改了一些脚本,使长行不会超出打印页面的右侧(哎呀)。
修订 1.82005-02-19修订者:c^2
添加了 Fedora Core 3 的注释
修订 1.72004-05-11修订者:c^2
调整了版权语言。
修订 1.62004-04-29修订者:c^2
添加了 Knoppix 注释、Syslinux、PPART、QtParted、一些其他救援 CD,并进行了一些修复。
修订 1.52003-12-19修订者:c^2
Fedora 1 和 GRUB 注释。
修订 1.42003-08-17修订者:c^2
关于刻录 CD-ROM 的一些注释,以及更多关于要排除的文件的注释。
修订 1.32003-04-24修订者:c^2
替换了旧的电子邮件地址和 URL。
修订 1.22003-02-12修订者:c^2
添加了 Red Hat 8.0 注释、对 FAT32 的支持、拆分了第一阶段恢复脚本以及其他小的更改。关于 Amanda 的注释。
修订 1.12002-09-10修订者:c^2
用于处理 make.fdisk 中的 ext3 分区的新代码,以及关于 initrd 的注释。
修订 1.02002-07-24修订者:c^2
我们现在在第一阶段使用 bz2 压缩,具有运行时选项来检查坏块,并且有一个运行整个第一阶段的脚本。

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

完全恢复,有时称为裸机恢复,是在灾难性故障后重建计算机的过程。为了进行完全恢复,你必须拥有完整的备份,不仅包括你的文件系统,还包括分区信息和其他数据。本 HOWTO 是一个关于如何备份 Linux 计算机以便能够进行裸机恢复,以及如何进行裸机恢复的分步教程。它包含一些相关的脚本。


目录
1. 简介
1.1. 版权信息
1.2. 免责声明
1.3. 新版本
1.4. 致谢
1.5. 反馈
1.6. 翻译
2. 概述
2.1. 局限性
3. 准备工作
3.1. 安装 ZIP 驱动器
4. 创建第一阶段备份
4.1. 主题和变体
5. 第一阶段恢复
5.1. 启动
5.2. 恢复
6. 第二阶段恢复
7. 发行版特定说明
7.1. Fedora Core 3 和 4
7.2. Knoppix
7.3. finnix
8. 应用程序特定说明
8.1. 逻辑卷管理器
8.2. Selinux
8.3. GRUB
8.4. Tripwire
8.5. Squid
8.6. Arkeia
8.7. Amanda
8.8. NTFS
9. 灾难恢复的一些建议
10. 接下来做什么?
10.1. 待办事项
11. 脚本
11.1. 第一阶段
11.2. 第二阶段
11.3. 备份服务器脚本
12. 资源
A. GNU 自由文档许可证
0. 序言
1. 适用性和定义
2. 逐字复制
3. 批量复制
4. 修改
5. 合并文档
6. 文档集合
7. 与独立作品的聚合
8. 翻译
9. 终止
10. 本许可证的未来修订
11. 如何将本许可证用于你的文档

1. 简介

正常的裸机恢复过程是:从产品光盘安装操作系统。安装备份软件,以便你可以恢复你的数据。恢复你的数据。然后你还需要通过验证你的配置文件、权限等来恢复功能。

本 HOWTO 中解释的过程和脚本将节省重新安装操作系统的时间。这里解释的过程将仅恢复从生产计算机备份的文件。当你恢复系统时,你的配置将保持完整,这应该可以节省你数小时的验证配置和数据的时间。


1.1. 版权信息

版权所有 © 2001 年至最后修改日期 Charles Curley,并根据 GNU 自由文档许可证 (GFDL) 条款分发,如下所述。 允许复制、分发和/或修改本文档,但须遵守自由软件基金会发布的 GNU 自由文档许可证 1.1 版或任何后续版本的条款; 不包含不变部分,不包含封面文本,也不包含封底文本。许可证的副本包含在题为 “GNU 自由文档许可证” 的部分中。

如果你有任何问题,请联系.


1.2. 免责声明

作者、Linux 文档项目或任何其他人均不对本文档的内容承担任何责任。 使用文中的概念、示例和其他内容,风险自负。 可能存在可能损坏你的系统的错误和不准确之处。 请谨慎操作,虽然不太可能出现错误,但作者对这些错误不承担任何责任。

所有版权均归其各自所有者所有,除非另有明确说明。 在本文档中使用某个术语不应被视为影响任何商标或服务标记的有效性。

提及特定产品或品牌不应被视为认可。

强烈建议你在进行重大安装之前以及定期备份时对你的系统进行备份。 此外,强烈建议你在处理本文档中的材料,尤其是脚本时,使用备用实验计算机。


1.3. 新版本

你可以在其 主页Linux 文档项目 网站上找到本文档的多种格式。 请评论至

根据你的浏览器,你可能需要按住 Shift 键同时单击这些链接才能下载它们。


1.4. 致谢

本文档源自最初发表在 Linux Journal 上的两篇文章。 感谢 Linux Journal 归还了这些文章的权利,从而使本 HOWTO 成为可能。

感谢 Joy Y Goodreau 出色的 HOWTO 编辑工作,以及 David Palomares 更正了萨尔瓦多·达利名字的拼写。

此外,感谢 Pasi Oja-Nisula 提供的错误修复和关于 Knoppix 的信息。


1.5. 反馈

非常欢迎对本文档提供反馈。 没有你的更正、建议和其他意见,本文档就不会存在。 请将你的添加、评论和批评意见发送给我,地址为.


1.6. 翻译

并非所有人都会说英语。 欢迎志愿者。


2. 概述

下面显示的过程并不容易,并且可能对你的数据有害。 在你需要它之前练习它! 像我一样做,并在备用计算机上练习

本 HOWTO 的原始目标计算机是一台奔腾计算机。 最初,它在一块 IDE 硬盘驱动器上安装了 Red Hat 7.1 Linux 服务器或工作站。 从那时起,我使用了许多计算机,它们已升级到 Red Hat 8.0 和 Fedora Cores 1、3 和 4。 目标计算机没有大量数据,因为该计算机被设置为 “备用” 测试平台。 也就是说,我不想使用生产计算机和生产数据来测试此过程。 此外,我在开始测试之前进行了全新安装,以便在我需要恢复到已知配置时始终可以重新安装。

Note注意
 

示例命令将在大多数情况下显示我必须键入的内容才能恢复目标系统。 你可能必须使用类似的命令,但参数不同。 你有责任确保你复制你的设置,而不是测试计算机的设置。

基本步骤在 W. Curtis Preston 的 Unix Backup & Recovery, O'Reilly & Associates, 1999 中列出,我在 Linux Journal 中对此进行了好评。 然而,这本书在具体、实时的问题上有点单薄。 例如,你到底备份哪些文件? 你应该保留哪些元数据,以及如何保留? 本文档探讨了这些问题。

在开始本 HOWTO 中阐述的过程之前,你需要使用典型的备份工具(如 Amanda、BRU™、tar、Arkeia® 或 cpio)备份你的系统。 那么,问题是如何从损坏的硬件到达你可以运行恢复工具来恢复数据的地步。

基于 Red Hat Package Manager (RPM) 的 Linux 发行版的用户还应将 RPM 元数据作为其正常备份的一部分保存。 以下是本 HOWTO 中的一个脚本中:

bash# rpm -Va | sort +2 -t ' ' | uniq > /etc/rpmVa.txt

它为裸机恢复后的比较提供了基础。

要达到这一点,必须具备

要到达那里,你需要至少两个阶段的备份,可能需要三个阶段。 你备份什么以及在哪个阶段备份取决于你的恢复过程。 例如,如果你要恢复磁带服务器,你可能不需要在恢复过程中联网。 因此,仅在你的常规备份中备份网络。

你也将分阶段恢复。 在第一阶段,我们构建分区、文件系统等,并从 ZIP 磁盘恢复最少的文件。 第一阶段的目标是能够启动到一台正在运行的计算机,该计算机具有网络连接、磁带驱动器、恢复软件或第二阶段所需的任何东西。

如果需要,第二阶段包括恢复备份软件和任何相关数据库。 例如,假设你使用 Arkeia,并且你正在为你的备份服务器构建裸机恢复 ZIP 磁盘。 Arkeia 在服务器的硬盘驱动器上保留一个庞大的数据库。 如果你愿意,你可以从磁带中恢复数据库。 相反,为什么不 tar 和 gzip 整个 arkeia 目录(在 /usr/knox),并将其保存到另一台计算机上,通过 nfs 或 ssh? 我们在下面定义的阶段一不包括 X,因此如果你希望备份 X 以及你的备份程序,你将需要进行一些实验。 有些恢复程序需要 X。

当然,如果你使用其他一些备份程序,你可能需要做一些侦查工作。 你将必须找出它运行所需的目录和文件。 如果你使用 tar、gzip、cpio、mt 或 dd 作为你的备份和恢复工具,它们将作为下面描述的阶段一过程的一部分保存到我们的 ZIP 磁盘并从中恢复。

最后阶段是从磁带或其他介质进行完全恢复。 完成最后阶段后,你应该能够启动到完全恢复且可操作的系统。


2.1. 局限性

本 HOWTO 仅限于进行最小备份,以便在将该备份恢复到新硬件(“裸机”)后,你可以使用你的常规备份来恢复完全正常工作的系统。 本 HOWTO 完全不处理你的常规备份。

即使在狭隘的范围内,本 HOWTO 也不是详尽无遗的。 你仍然需要进行一些研究、脚本编辑和测试。

此处的脚本完全按照源硬盘驱动器上的分区数据恢复分区数据。 如果你在相同的计算机上或至少在相同的硬盘驱动器上恢复,这很好,但这通常不是这种情况。 现在,有两种补救措施(在你阅读完 HOWTO 的其余部分后,这将更有意义)

  • 编辑分区表输入文件。 我已经做过几次了。 你也可以这样做来添加新分区或删除现有分区(但也要编辑使用分区表输入文件的脚本)。

  • 手动构建一个新的分区表,然后从那里开始。 这就是 restore.metadata 不调用硬盘驱动器重建脚本的原因之一。 使用 重建脚本

此处显示的脚本仅处理 ext2fs、FAT12、FAT16 和 FAT32。 在一些热心的志愿者提供代码以在这些脚本中执行此操作之前,你将需要其他工具来备份和恢复我们尚未涵盖的文件系统。 Partition Image 看起来是一个有用的候选工具。


3. 准备工作

Note警告
 

按照常规计划进行正常备份。 如果你不这样做,本 HOWTO 将毫无用处。

为自己构建一个救援磁盘。 我现在使用 Knoppix。 请参阅下面的 Knoppix 注释。 但是,Knoppix 有一个问题:不支持 LVM。 如果你想恢复逻辑卷,请获取支持它们的发行版。 为此,我使用 finnix

过去,我曾使用过 tomsrtbt。 它有很好的文档记录,并将许多有用的工具打包到一个软盘中。 不幸的是,我不得不在脚本中进行的更改以处理更新的 Linux 系统,这给 tomsrtbt 带来了问题。 tomsrtbt 2.0.103 tar 基于 busybox,因此关于它的评论可能适用于其他使用 busybox 的 Linux 发行版。

我们将把你使用的任何 Linux 称为 “恢复 Linux”

接下来,弄清楚如何进行操作系统备份,以便你可以恢复你的正常备份。 我遵循了 Preston 的建议,并使用了 Iomega 并行端口 ZIP 驱动器。 这些驱动器为每个磁盘提供大约 90 MB 的可用存储空间。 我需要大约 85 MB 来备份我的桌面,因此 100MB ZIP 驱动器可能会超出你的运气。


3.1. 安装 ZIP 驱动器

ZIP 驱动器的安装在 ZIP 驱动器 HOWTO 中介绍,该 HOWTO 可在 Linux 文档项目 及其主页 http://www.njtcom.com/dansie/zip-drive.html 上找到。


4. 创建第一阶段备份

在制作生产备份后,你需要保存你的分区信息,以便你可以重建你的分区。

脚本 make.fdisk 扫描硬盘驱动器的分区信息,并将其保存在三个文件中。 第一个是可执行脚本,名为 make.dev.x(其中 “x” 是设备文件的名称,例如 hda)。 第二个是 mount.dev.x,它创建挂载点并将新创建的分区挂载到这些挂载点上。 最后一个 dev.xfdisk 构建分区所需的命令。 你可以通过命名关联的设备文件作为 make.fdisk 的参数来指定要为其构建脚本的硬盘驱动器(以及因此的文件名)。 例如,在典型的 IDE 系统上,

bash# make.fdisk /dev/hda

输出脚本 make.dev.hdamount.dev.hdafdisk 的输入文件 dev.hda

此外,如果 make.fdisk 遇到 FAT 分区,它会将分区的引导扇区保存在名为dev.xy的文件中,其中 x 是驱动器的设备名称(例如 sdc、hda),y 是分区号。 引导扇区是分区的第一个扇区,512 字节。 此扇区与重建分区同时在脚本 make.dev.hda 中恢复。

幸运的是,硬盘驱动器的价格几乎与公众在选举后对政客的信任一样迅速地下跌。 因此,输出文件是文本,并允许手动编辑是一件好事。 这是在更大的替换驱动器上重建的最困难但最灵活的方法。 (请参阅待办事项列表。)

其他元数据保存在脚本 save.metadata 中。 该脚本将分区信息保存在文件fdisk.hda中,该文件位于 ZIP 磁盘的根目录中。 最好打印此文件和你的/etc/fstab,这样你就可以在需要手动恢复分区数据时获得硬拷贝。 你可以通过在两个虚拟控制台之间切换来节省一棵树,在一个控制台中运行 fdisk,并在另一个控制台中 catting/etc/fstab/fdisk.hda根据需要。 但是,这样做容易出错。

你还需要保存与你的恢复方法相关的文件。 例如,如果你使用 nfs 保存你的数据,你将需要保存 hosts.allow、hosts.deny、exports 等。 此外,如果你使用任何网络支持的恢复过程,例如 Amanda 或 Quick Restore,你将需要保存网络文件,如 HOSTNAME、hosts 等,以及相关的软件树。

处理这些和类似问题的最简单方法是保存整个 etc 目录。

100 MB ZIP 驱动器不可能容纳现代 Linux 发行版的服务器安装。 我们必须比简单地保存整个 kazoo 更具选择性。 我们需要哪些文件?

要确定启动时需要的目录,我们查看启动初始化文件/etc/rc.sysinit。 它像这样设置自己的路径

PATH=/bin:/sbin:/usr/bin:/usr/sbin
export PATH

试错法表明,我们也需要一些其他目录,例如/dev。 在 Linux 中,如果没有设备文件,你就做不了多少事情。

在阅读脚本 save.metadata 时,请注意我们不一定保存使用绝对路径调用的文件。

我们可能需要多次迭代备份,测试裸机恢复,从 CD 重新安装并再次尝试,才能获得可用的备份脚本。 在我编写本 HOWTO 时,我进行了五次这样的迭代,才成功恢复。 这就是为什么尽可能使用脚本至关重要的原因之一。 彻底测试!

在基于 RPM 的系统上,你可以做的一件事是使用 rpm 程序来确定哪些文件在何处。 例如,要获取 openssh 包使用的文件的完整列表,请运行

bash# rpm -ql openssh

有些东西你不需要,例如手册页。 你可以检查每一个,并决定是否备份它。

Note警告
 

第二阶段恢复在不覆盖先前恢复的文件的情况下运行。 这意味着第一阶段恢复的文件是在完全恢复后将要使用的文件。 因此,每当你更新这些目录中的文件时,请更新你的裸机备份!

Note警告
 

tomsrtbt 中包含的 tar 版本在恢复时不会保留所有权。 这可能会给 Amanda 等应用程序带来问题。 Amanda 是一种备份和恢复工具,它有几个目录由其同名用户拥有。 解决方案是

  • 注意哪些目录和文件不是由 root 拥有的。

  • 注意它们的拥有者。

  • 安排在恢复过程中正确设置所有权。 例如

    bash# chown -R amanda:disk /var/lib/amanda

    你也可以将该行添加到你的第二阶段恢复脚本中,例如 restore.tester

Note警告
 

tomsrtbt 不支持按 UID/GID 恢复所有者。 为了使备份适合使用 tomsrtbt 恢复,请从脚本 save.metadata 中 crunch 函数中 tar 的命令行选项中删除 tar 命令行选项 “--numeric-owner”


4.1. 主题和变体

4.1.1. 没有 ZIP 驱动器

此备份过程过去要求你在每次备份时都存在 ZIP 磁盘驱动器。 它现在在目录中创建 ZIP 磁盘的内容,你可以通过网络备份该目录。 然后,你只需在需要恢复时在备份服务器上构建 ZIP 磁盘(使用 cp -rp)。

备份过程将比直接写入 ZIP 驱动器更快,但你应该检查生成的目录是否适合你的 ZIP 磁盘(使用脚本 save.metadatadu -hs $target.zip 的输出)! 请参阅变量的定义zip在该脚本中。

我的笔记本电脑在同时运行网卡和 ZIP 驱动器时遇到问题,因此这是我用来备份它的过程。 我保留备份映像以及当前映像,以便在计算机在备份期间崩溃时,我有一个回退选项。

或者,你可以在硬盘驱动器上构建多个 ZIP 磁盘的备份,并在恢复时将其馈送到系统。


4.1.2. CD-ROM

这与上面的无 ZIP 驱动器选项类似。 如上所述,将你的备份保存到硬盘驱动器上的目录。 然后使用 mkisofs 从该目录创建一个 ISO 9660 映像,并将其刻录。 这不适用于某些基于 CD-ROM 的 Linux,例如 Knoppix,因为 Linux 必须具有 CD-ROM 驱动器。 除非你有两个 CD-ROM 驱动器,例如 USB 翻盖式驱动器中的一个。 我以这种方式设置了一个 DVD 刻录机,正是出于这个目的。

或者查看 remastering Knoppix,将你的第一阶段和第二阶段备份放在 CD-ROM/DVD 上。 你也应该能够 remaster finnix

现在,许多计算机都配备了 CD-ROM 驱动器,但没有软盘驱动器。 软盘驱动器确实会发生故障。 因此,最好将你的 CD-ROM 刻录成可启动映像。 坏消息是 “El Torito” 格式支持 1.2 MB、1.44 MB 和 2.88 MB 软盘,而 tomsrtbt 使用 1.7 MB 软盘。 好消息是你可以获得 2.88 MB 版本,tomsrtbt-2.0.103.ElTorito.288.img,从你获取软盘映像的同一镜像站点获取。 在备份文件的根目录中放置一个副本 [1]。 然后使用 mkisofs 命令行选项 -b 来指定tomsrtbt-2.0.103.ElTorito.288.img作为引导映像文件。

此过程的唯一缺点是许多较旧的 BIOS 不支持 CD-ROM 上的 2.88 MB 软盘映像。 其中大多数将启动到 tomsrtbt 软盘。

另一种选择是使用 Syslinux。 它不依赖于软盘映像,你可以构建自己的 CD,其中包含许多工具,例如 tomsrtbt

你可能必须调整 BIOS 选项以允许计算机启动到 CD-ROM 驱动器。 如果你无法做到这一点,要么是因为 BIOS 不支持从 CD-ROM 启动,要么是因为你无法进入 BIOS,请参阅 Smart Boot Manager (SBM),如资源中所述。

你将在恢复时使用的驱动器上测试你的 CD。 如果你发现你需要破解脚本,你可以将它们复制到/tmptomsrtbt 下的 RAM 磁盘,并在那里编辑它们。 这些脚本将在那里运行。 由于 RAM 磁盘是易失性的,因此请务必在重新启动之前保存你的更改!


4.1.3. 多个 ZIP 磁盘

通过拆分两个第一阶段脚本 restore.metadatasave.metadata,你可以将第一阶段元数据分散到多个 ZIP 磁盘上。


4.1.4. 从第一阶段保存中排除

有时你需要从第一阶段数据中挤出几兆字节,特别是当你接近 ZIP 磁盘的限制时。 脚本 save.metadata 中的函数 crunch 接受多个参数以馈送到 tar。 它也可以接受 --exclude 参数。 因此,例如,你可以排除sambaX11目录,它们位于/etc下,像这样

crunch etc --exclude etc/samba --exclude etc/X11 etc

为什么要排除这两个? 因为它们占用了大量的硬盘空间,而且我们在启动时不需要它们。

如果你保留多个内核,你可以删除所有你不会启动的内核的模块。 检查你的lilo.confgrub.conf以查看你将使用哪个内核,然后检查/lib/modules以查找你可以排除的模块目录。

如何找到更多好的排除候选? 使用 ls -alSr 列出单个文件的目标目录,使用 du | sort -n 列出目录。

另一种(可能更简洁的)排除目录的方法是将完整的目录列表放入一个文件,然后通过 tar 选项引用它--exclude-from=FILENAME.


4.1.5. Initrd

如果您的系统使用初始 RAM 磁盘(initrd)来启动,请确保 restore.metadata 创建了目录/initrd。 最简单的方法是确保它包含在目录创建循环末尾使用的目录列表中。

如果您的系统从 SCSI 驱动器启动或根目录位于 ext3fs 分区上,则可能会使用 initrd。 检查/etc/lilo.conf以查看它是否调用了 initrd。


5. 第一阶段恢复

5.1. 启动

首先要做的是验证硬件时间是否设置正确。 使用 BIOS 设置执行此操作。 您必须将时间设置到多精确取决于您的应用程序。 对于恢复,在精确时间的几分钟内应该足够准确。 这将允许时间关键型事件在您最终启动恢复后的系统时从中断处继续。


5.1.1. tomsrtbt

在启动 tomsrtbt 之前,请确保您的 ZIP 驱动器安装在并行端口上,可以是/dev/lp0/dev/lp1。 启动软件将为您加载并行端口 ZIP 驱动器驱动程序。

下一步是设置视频模式。 我通常喜欢在屏幕上看到尽可能多的内容。 当出现选择视频模式的选项时,我使用模式 6,即 80 列 x 60 行。 您的硬件可能能够或可能不能够处理如此高的分辨率,因此请尝试一下。


5.1.2. Knoppix

这些说明可能适用于其他 CD-ROM 或 USB 笔式 Linux,但您可能需要调整它们以适应。

在启动 Knoppix 之前,请确保您的 ZIP 驱动器(或替代品)安装在并行端口上,可以是/dev/lp0/dev/lp1。 Knoppix 不会为您加载并行端口 ZIP 驱动器驱动程序。 相反,请使用命令 modprobe ppa(以 root 用户身份)安装它。

像往常一样启动 Knoppix。 我发现启动到控制台更快且更有用。 在启动菜单中,使用命令 "knoppix 2"。 然后成为 root 用户,使用 su -。 对于密码,只需按回车键。


5.1.3. Finnix

启动 finnix 的一个选项是 "toram" 选项,它允许您将整个系统移动到 RAM 中。 这反过来应该允许您将另一张 CD(包含您的第一阶段数据)加载到 CD 驱动器中。


5.2. 恢复

这些说明假设您正在运行 tomsrtbt。 如果您使用不同的 Linux 作为您的恢复系统,您可能需要稍微调整这些说明。 例如,即使其他用户为您提供所需的权限,您也应始终以 root 用户身份运行这些脚本。

一旦恢复 Linux 启动并且您有一个控制台,请挂载 ZIP 驱动器。 最好以只读方式挂载它

# mount /dev/sda1 /mnt -o ro

检查以确保它在那里

# ls -l /mnt

Knoppixfinnix 上,您可能需要在/mnt下创建一个目录并在那里挂载它,如下所示

# mkdir /mnt/zip
# mount /dev/sda1 /mnt/zip -o ro

此时,您可以自动或手动运行恢复。 如果您不需要在进行过程中进行任何更改,请使用自动恢复。

这里的一个考虑因素是您是否有多块硬盘驱动器。 如果您的 Linux 安装挂载了多块硬盘驱动器上的分区,您必须首先挂载根分区。 这是为了确保挂载点目录在它们所属的分区上创建。 脚本first.stage将按照驱动器创建的顺序运行脚本来挂载驱动器。 如果您已按照它们从根目录级联的顺序创建它们(在脚本中save.metadata),则挂载过程应该可以正常工作。

如果您有多块硬盘驱动器,并且它们交叉挂载,则您需要自行处理。 要么合并和编辑脚本以按正确的顺序挂载它们,要么手动执行。


5.2.1. 自动化

自动过程按正确的顺序调用每个手动脚本。 它不允许手动干预,例如创建此 HOWTO 不支持的文件系统。 要自动运行第一阶段恢复,请输入命令

# /mnt/root.bin/first.stage

如果您想检查坏块,请添加 -c 选项。


5.2.2. 手动

要手动运行该过程,请更改到 ZIP 驱动器上脚本所在的目录。

# cd /mnt/root.bin

现在运行将恢复分区信息并创建文件系统的脚本。 您可以按任何顺序运行它们。 例如

# ./make.dev.hda

如果您想检查坏块,请添加 -c 选项。

此脚本将

  • 清除硬盘驱动器的前 1024 个字节,杀死任何现有的分区表和主引导记录 (MBR)。

  • 从您运行 make.fdisk 时收集的信息中重新创建分区。

  • 根据需要创建 ext2 和 ext3 文件系统分区以及 Linux 交换分区。 如果您为脚本提供 -c 选项,它还将检查坏块。

  • 创建一些类型的 FAT 分区。

现在是检查驱动器几何形状的好时机。 有时,不同版本的 Linux 会拾取不同的几何形状,因此文件dev.hdX中隐含的几何形状是不正确的。 要强制它在 Knoppix 上正确,请编辑 make.dev.x。 使用 fdisk 的 -C、-H 和 -S 选项分别指定柱面、磁头和扇区。 您可以从文件fdisk.hdX中获取这些信息,该文件位于 ZIP 驱动器的根目录中。 然后重新运行它。

Note注意
 

如果您有其他操作系统或文件系统要恢复,现在是执行此操作的好时机。 完成后,重新启动到您的恢复 Linux 并继续恢复。

现在运行创建挂载点并将分区挂载到它们的脚本。

# ./mount.dev.hda

创建完所有目录并将分区挂载到它们之后,您可以运行脚本 restore.metadata

# ./restore.metadata

这将把 ZIP 驱动器的内容恢复到硬盘驱动器。

您应该看到 ZIP 磁盘根目录的目录,然后是恢复时存档文件的列表。 tomsrtbt 上的 Tar 会告诉您 tar 的块大小为 20,这很好。 您可以忽略它。 确保 lilo 打印出其结果

Added linux *

之后将是 "df -m" 命令的输出。


5.2.3. 收尾工作

如果您通常直接启动到 X,您可能会遇到一些问题。 为了安全起见,暂时更改您的启动运行级别。

如果您使用 grub 启动,在 grub 选择窗口中,选择您要启动的内核。 按 "e" 进行编辑,并在内核行末尾添加一个空格和数字 "3"。 确认新行,然后按 "b" 进行启动。

如果您不使用 grub,在重新启动之前编辑/target/etc/inittab。 找到看起来像这样的行

id:5:initdefault:

并将其更改为此

id:3:initdefault:

现在,您可以正常重新启动。 如果您尚未这样做,请从启动驱动器中取出介质,并对计算机执行三指礼,或其等效操作

# shutdown -r now

# reboot

计算机将关闭并重新启动。


6. 第二阶段恢复

当计算机重新启动时,返回 BIOS 并验证时钟是否或多或少正确。

一旦您验证时钟正确,退出 BIOS 并重新启动到硬盘驱动器。 您可以简单地让计算机按其正常顺序启动。 您将看到很多错误消息,大多类似于 "我找不到 blah! Waahhh!"。 如果到目前为止您正确完成了功课,那么这些错误消息无关紧要。 您不需要 linuxconf 或 apache 来完成您需要做的事情。

Note注意
 

作为替代方案,您可以启动到单用户模式(在 lilo 提示符下,输入 linux single),但您必须手动配置您的网络并启动 sshd 或您需要启动的任何守护程序以恢复您的系统。 您如何执行这些操作是非常系统特定的。

您应该能够登录到 root 控制台(没有 X -- 对不起,没有用户)。 您现在应该能够使用网络,例如 nfs 挂载您的系统备份。

如果您执行了我为 Arkeia 建议的两阶段备份,您现在可以恢复 Arkeia 的数据库和可执行文件。 您应该能够运行

/etc/rc.d/init.d/arkeia start

并启动服务器。 如果您在另一台安装了 X 的计算机上安装了 GUI,您现在应该能够登录到您的磁带服务器上的 Arkeia,并准备您的恢复。

Note注意
 

恢复时,请仔细阅读您的恢复程序的文档。 例如,tar 通常不会恢复文件的某些特性,例如 suid 位。 文件权限由用户的 umask 设置。 要完全按照您保存的方式恢复文件,请使用 tar 的 p 选项。 同样,请确保您的恢复软件将完全按照您保存的方式恢复所有内容。

要恢复测试计算机

bash# restore.all

如果您使用 tar 进行备份和恢复,并使用了 -k(保留旧文件,不覆盖)选项,您将看到很多这样的内容

tar: usr/sbin/rpcinfo: Could not create file: File exists
tar: usr/sbin/zdump: Could not create file: File exists
tar: usr/sbin/zic: Could not create file: File exists
tar: usr/sbin/ab: Could not create file: File exists

这是正常的,因为 tar 拒绝覆盖您在第一阶段恢复期间恢复的文件。

然后重新启动。 在关闭过程中,您会看到很多错误消息,例如 "no such pid."。 这是过程的正常部分。 关闭代码正在使用备份时运行的守护程序的 pid 文件来关闭在上次启动时未启动的守护程序。 当然没有这样的 pid。

您的系统应该正常启动,错误比以前少得多; 理想情况下没有错误。 在基于 RPM 的系统上,您的恢复工作效果如何的酸性测试是验证所有软件包

bash# rpm -Va | sort +2 -t ' ' | uniq > ~/foo.txt
diff /etc/rpmVa.txt ~/foo.txt

预链接错误消息是正常的,您可以忽略它们。 或者您可以运行命令 /etc/cron.daily/prelink 来删除它们。

某些文件(例如配置文件和日志文件)会在正常情况下发生更改,您应该能够在心理上将这些文件从报告中过滤掉。 您可以将输出重定向到一个文件,并将其与备份时创建的文件 (/etc/rpmVa.txt) 进行比较,从而大大加快此步骤。 Emacs 用户应该查看其 diff 功能。

现在您应该启动并运行了。 现在是测试您的应用程序的时候了,尤其是那些作为守护程序运行的应用程序。 应用程序越复杂,您可能需要进行的测试就越多。 如果您有远程用户,请禁止他们使用该系统,或者在您测试它时使其 "只读"。 这对于数据库尤其重要,以防止使任何损坏或数据丢失比已经可能的情况更糟。

如果您通常启动到 X,并且在上面禁用了它,请在重新启用它之前测试 X。 通过将 /etc/inittab 中的那一行改回

id:5:initdefault:

您现在应该准备好摇滚了 -- 以及一些阿司匹林和一张沙发。


7. 发行版特定说明

以下是过去经验的发型版说明。 如果您有其他发行版的其他说明想要添加,请将它们转发给我。


7.1. Fedora Core 3 和 4

这些脚本现在反映了 Fecorda Core 4,因此您不应该对这些 脚本 进行任何更改。

Note

我在 FC3 的全新安装上测试了上述内容。 当我使用从 FC2 升级到 FC3 的系统时,我在启动后遇到设备问题。


7.2. Knoppix

我最近开始使用 KnoppixPasi Oja-Nisula 报告

对我来说,使用 Knoppix 最好的事情是,我不需要为每台机器准备特定的启动介质,但我可以一直使用相同的工具。 并且 Knoppix 中的硬件支持非常棒。 我没有太多不同平台的经验,但我尝试过的所有机器都工作正常,找到了 scsi 驱动程序等等。

我正在通过网络将备份复制到另一台机器上来进行此恢复操作。 恢复涉及启动 Knoppix cd,从网络机器获取 metadata.tar.gz。 然后 make.dev、mount.dev、获取其他 tar.gz 文件、grub 和重新启动。 涉及一些打字,但感谢您的脚本,它非常直接。 除非从 ide 更改为 scsi 或其他内容,但即使那样也不难,因为 Linux 很容易恢复到不同的硬件。

让我补充一点,Knoppix 为您检测 USB 设备,这非常好。 它们是 ZIP 驱动器的绝佳(且更宽敞)替代品。

另请参阅 "使用 Knoppix 进行系统恢复"

以用户 "root" 而不是用户 "knoppix" 的身份执行恢复。 否则,您可能会得到一些由古怪用户或组拥有的目录和文件。 此外,对于 Knoppix,我们 tar 第一阶段的东西,保存数字用户和组值,而不是按名称保存。 名称可能指向 knoppix 上的不同数字,因此我们将错误地恢复文件。


7.3. finnix

Finnix 具有 Knoppix 的一些相同优势。 此外,它在命令行模式下运行,并支持鼠标,这对于手头的任务非常有用。 截至撰写本文时,它体积小,小于 100 MB,因此您可以将其与第一阶段数据一起重新制作。 它启动速度快。 并且它具有 LVM 支持。 还有 Zile,Emacs 的一个子集。 我对 finnix 的这种用途感到满意。


8. 应用程序特定说明

以下是一些关于备份特定应用程序的说明。


8.1. 逻辑卷管理器

处理逻辑卷原来有点棘手:使用 finnix 发行版的启动代码来打开和关闭 LVM。 这导致第一阶段恢复的发行版特定代码。 它在 make.fdisk 中生成。 要编辑它,请在 make.fdisk 上搜索 "Hideous"

LVM 需要添加两个新的 LVM 特定脚本,make.lvsmount.lvs。 它们仅在存在逻辑卷时生成和使用。


8.2. Selinux

Selinux 在测试机器上被禁用。/selinux在任何这些脚本中都不会备份。 我猜,您可能应该在第一阶段恢复后禁用 selinux,并且在重新启用它之前,您可能有一些 selinux 特定的任务要执行。


8.3. GRUB

Fedora 中的默认引导加载程序是 Grand Unified Bootloader (GRUB)。 它必须在第一阶段结束时运行,否则您将无法在此后启动。 要为第一阶段恢复保留它,请进行以下更改

  • 编辑 restore.metadata 的倒数第二个节

    # Now install the boot sector.
    # chroot $target /sbin/lilo -C /etc/lilo.conf
    chroot $target /sbin/grub-install /dev/hda
  • 将以下节添加到 save.metadata

    # Grub requires these at installation time.
    if [ -d  usr/share/grub ] ; then # Red Hat/Fedora
      crunch usr.share.grub usr/share/grub
    fi
    if [ -d  usr/lib/grub ] ; then # SuSE
      crunch usr.lib.grub usr/lib/grub
    fi

8.4. Tripwire

如果您运行 Tripwire 或任何其他维护文件元数据数据库的应用程序,请在恢复后立即重建该数据库。


8.5. Squid

Squid 是一个 HTTP 代理和缓存。 因此,它在硬盘驱动器上保留了大量临时数据。 备份这些数据没有意义。 将 "--exclude /var/spool/squid" 插入到第二阶段备份脚本中适当的 tar 命令中。 然后,让 squid 为您重建其目录结构。 在第二阶段恢复脚本的末尾添加一个命令,让 squid 初始化自身。 这是我在 restore.tester 中通过 ssh 完成它的方式

ssh $target "mkdir /var/spool/squid ; chown squid:squid /var/spool/squid;\
      /usr/sbin/squid -z;touch /var/spool/squid/.OPB_NOBACKUP"

最后一个命令创建一个长度为 0 的名为 .OPB_NOBACKUP 的文件。 这是为了 Arkeia 的利益,并告诉 Arkeia 不要备份此目录下的内容


8.6. Arkeia

这些说明基于使用 Arkeia 4.2 进行的测试。

Arkeia 是一个备份和恢复程序,可在各种平台上运行。 您可以将 Arkeia 用作裸机恢复方案的一部分,但有两个注意事项。

第一个可能是最成问题的,因为在没有任何更优雅的解决方案的情况下,您必须在恢复时在导航器中手动选择要恢复的目录。 原因是,显然,Arkeia 没有机制来不恢复已存在于磁盘上的文件,没有什么类似于 tar 的 -p 选项。 如果您只是允许完全恢复,则恢复将崩溃,因为 Arkeia 会覆盖在恢复时正在使用的库,例如lib/libc-2.1.1.so。 手动选择要恢复的目录充其量是冒险的,所以我建议不要这样做。

第二个注意事项是您必须备份 Arkeia 数据字典和/或程序。 要做到这一点,请修改save.metatdata脚本,通过将 Arkeia 添加到要保存的目录列表中

# arkeia specific:
tar cf - usr/knox | gzip -c > $zip/arkeia.tar.gz

必须以这种方式备份数据字典,因为 Arkeia 不会备份数据字典。 这是我对 Arkeia 的抱怨之一,我在我自己的计算机上通过使用 The TOLIS Group's BRU 将数据字典保存到磁带上来解决这个问题。

数据字典将在脚本restore.metadata中自动恢复。


8.7. Amanda

Amanda(高级马里兰自动网络磁盘归档器)与这组脚本配合得非常好。 使用正常的 Amanda 备份过程,并像往常一样构建您的第一阶段数据。 Amanda 将数据以 GNU tar 或 cpio 格式存储在磁带上,您可以从单个文件恢复到整个备份映像。 恢复整个映像的好处是,您可以然后使用此 HOWTO 中的脚本的变体从映像恢复,或直接从磁带恢复。 我能够使用 W. Curtis Preston 的 Unix 备份与恢复 中的说明来恢复我的测试机器。 有关它的更多信息,请参阅 资源。 该书的 Amanda 章节 在线

我对脚本 restore.tester 进行了两处更改。 首先,我将其更改为接受文件名作为参数。 然后,由于 Amanda 的 amrestore 在恢复数据时会解压缩数据,因此我重写了它以将文件 cat 到管道中,而不是解压缩它。

结果行看起来像这样

cat $file | ssh $target "umask 000 ; cd / ; tar -xpkf - "

其中 $file 是脚本的参数,即由 amrestore 从磁带恢复的映像。

由于 tar 的命令行参数禁止覆盖,因此请按创建映像的相反顺序从映像恢复。 首先恢复最新的。

如果您使用 save.metadata 备份 amanda 数据目录,Amanda 确实需要手动设置所有权。 类似于

bash# chown -R amanda:disk /var/lib/amanda

你也可以将该行添加到你的第二阶段恢复脚本中,例如 restore.tester


8.8. NTFS

好的,NTFS 不是一个应用程序。 它是 Microsoft 操作系统 Windows NT 及其后代(包括 Windows 2000 和 Windows XP)使用的文件系统。 您可以使用 ntfsclone 从 Linux 备份和恢复到它,ntfsclone 是 ntfsprogs 套件中的 NTFS 实用程序之一,可从 http://linux-ntfs.sourceforge.net/downloads.html 获得。

这些脚本将创建 NTFS 分区,但不会在它们上放置文件系统。 从文档中不清楚 ntfsclone 是否会在全新的分区上放置文件系统。


9. 关于灾难恢复的一些建议

您应该为每台计算机准备好 ZIP 磁盘和您制作的打印件,并将它们放在您商店中的安全位置。 您应该将这些副本存储在您的异地备份存储位置。 异地备份存储的主要目的是启用灾难恢复,将每台主机恢复到替换硬件是灾难恢复的一部分。

您还应该准备几个恢复 Linux 软盘或 CD-ROM,以及可能的一些 ZIP 驱动器在您的异地存储中。 此外,在您的几台计算机上拥有救援 linux 发行版的副本,以便它们相互备份。

您可能应该将本 HOWTO 的副本(包含您特定于站点的注释)与您的备份和异地备份存储一起保存。


10. 接下来做什么?

本 HOWTO 是在一台计算机上进行实验的结果。 毫无疑问,您会发现一些目录或文件需要在您的第一阶段备份中备份。 我没有处理在第一阶段保存和恢复 X,也没有完全触及 Intel 以外的处理器。

如果您在自己的计算机上测试和改进这些脚本,我将不胜感激您的反馈。 我还鼓励备份软件供应商记录如何对其产品进行最小备份。 我希望看到整个 Linux 社区在晚上睡得更安稳一些。


10.1. 待办事项

非常欢迎志愿者。 在您开始处理其中一项之前,请先与我联系,以防其他人已经在处理它。

  • 我们无法确定交换分区的标签。 这意味着在恢复时无法提供交换分区的标签。 我们可以假设具有单个交换分区(如 fdisk 所示)的系统具有在交换分区行中使用的标签/etc/fstab,但这仅适用于单硬盘驱动器系统,并且可能在具有多个交换分区的系统中产生细微的错误。

    解决方法是通过重新运行mkswap以及 -L 选项手动添加标签。 唉。

  • 用于在dev.hdx文件中调整分区边界的分区编辑器。 这将允许用户为不同的硬盘驱动器调整分区,或为具有不同几何形状的相同硬盘驱动器调整分区,或在同一硬盘驱动器内调整分区大小。 GUI 在这里可能是一个好主意。 另一方面,FSF 的 parted 看起来将满足部分需求。 它确实可以调整现有分区的大小,但有限制。

  • make.fdisk 当前仅识别某些 FAT 分区,而不是全部。 在 make.fdisk 中添加代码以识别其他分区,并在输出文件中生成重建它们的适当说明。

  • 对于 FAT12 或 FAT16 分区,我们不格式化,而是将零写入分区,以免 Mess-DOS 6.x 感到困惑。 请参阅关于 fdisk 的说明,以了解问题的解释。

  • 翻译成其他(人类)语言。

  • 我不时提到 Red Hat Package Manager (rpm)。 等效的 deb 命令是什么?

  • 修改第一阶段备份代码,使其仅保存当前内核。


11. 脚本

请参阅每个脚本开头的说明,以了解其功能的摘要。


11.1. 第一阶段

11.1.1. make.fdisk

此脚本在备份时运行,创建类似于下面的 make.dev.hdamount.dev.x 的脚本,供您在恢复时运行。 它还生成类似于下面的 dev.hda 的数据文件。 生成的脚本和数据文件的名称取决于作为参数提供给此脚本的设备。 该脚本在恢复时运行,在硬盘驱动器上构建分区。make.fdisk从下面的 save.metadata 调用。

#! /usr/bin/perl

# A perl script to create a script and input file for fdisk to
# re-create the partitions on the hard disk, and format the Linux and
# Linux swap partitions. The first parameter is the fully qualified
# path of the device of the hard disk, e.g. /dev/hda. The two
# resulting files are the script make.dev.x and the data file dev.x
# (where x is the hard drive described, e.g. hda, sdc). make.dev.x is
# run at restore time to rebuild hard drive x, prior to running
# restore.metadata. dev.x is the input file for fdisk.

# Time-stamp: <2006-04-08 15:23:55 ccurley make.fdisk>

# Copyright 2001 through the last date of modification Charles Curley
# except for the subroutine cut2fmt.

# cut2fmt Copyright (c) 1998 Tom Christiansen, Nathan Torkington and
# O'Reilly & Associates, Inc.  Permission is granted to use this code
# freely EXCEPT for book publication.  You may use this code for book
# publication only with the explicit permission of O'Reilly &
# Associates, Inc.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# In addition, as a special exception, Tom Christiansen, Nathan
# Torkington and O'Reilly & Associates, Inc.  give permission to use
# the code of this program with the subroutine cut2fmt (or with
# modified versions of the subroutine cut2fmt that use the same
# license as the subroutine cut2fmt), and distribute linked
# combinations including the two.  You must obey the GNU General
# Public License in all respects for all of the code used other than
# the subroutine cut2fmt.  If you modify this file, you may extend
# this exception to your version of the file, but you are not
# obligated to do so.  If you do not wish to do so, delete this
# exception statement and the subroutine cut2fmt from your version.

# You can also contact the Free Software Foundation at
# http://www.fsf.org/

# Changes:

# 2006-04-08: Primitive LVM support. It is kludgy in that it uses
# first stage restoration distribution (finnix) specific code to turn
# LVM on and off, but otherwise seems to work.

# 2006-03-28: We have a problem if swap partitions have
# labels. There's no way to retrieve the label from a swap
# partition. If we have one & only one swap partition, then we can
# pull it out of /etc/fstab. Otherwise the user is on her own. We scan
# fstab for swap mount points that have labels for their devices. If
# there is one and only one, we assume that's it, otherwise pass.

# 2005-10-29: We now provide the geometry as an argument to fdisk
# (which does not work on tomsrtbt). We also save data for sfdisk, and
# write out make.dev.xxx so that it will use sfdisk if it finds it.

# 2005-08-14: Due to experience on Knoppix, we now add the code to
# change the partition types to the end of the fdisk input file
# instead of right after creating the partition.

# 2004 04 10: fdisk v > 2.11 has wider columns. Added code to select
# the appropriate cut string based on fdisk's version.

# 2004 04 09: Added support for Mandrake's idea of devfs. On Mandrake,
# everything is mounted with devfs. So the mount devices are buried
# deep in places like /dev/ide/host0/bus0/target0/lun0/part1 instead
# of places like /dev/hda1, where $DEITY intended they should be. We
# have to reverse from the long devfs device to the shorter old style
# that tomsrtbt uses. The alternative is to keep track in an array of
# which devfs device belongs to which short device.

# 2003 12 29: Changed the regex for detecting whether a file system is
# read-write in the code that builds the mount file(s). The old test
# does not work if mount returns multiple parameters in the 5th field,
# e.g. (rw,errors=remount-ro) on some debian systems. This regex
# assumes that the rw parameter is always listed first, which may not
# always be the case. If it fails, take out the '\('. Thanks to Pasi
# Oja-Nisula <pon at iki dot fi> for pointing this out.

# 2003 01 09: Added support for FAT32. We now create two scripts for
# each hard drive, make.dev.[as]dx and mount.dev.[as]dx. These create
# and make file systems on each partition, and make mount points and
# mount them.

# 2002 12 25: added support to handle W95 extended (LBA) (f) and W95
# FAT 32 partitions. I have tested this for primary but not logical
# partitions.

# 2002 09 08: Added minimal support for ext3fs. We now detect mounted
# ext3fs partitions & rebuild but with no options. The detection
# depends on the command line "dumpe2fs <device> 2>/dev/null | grep -i
# journal" producing no output for an ext2fs, and output (we don't
# care what) for an ext3fs.

# This could stand extension to support non-default ext3 options such
# as the type of journaling. Volunteers?

# 2002 07 25: Bad block checking is now a command line option (-c) at
# the time the product script is run.

# 2002 07 03: Corrected the mechanism for specifying the default
# drive.

# 2001 11 25: Changed the way mke2fs gets its bad block
# list. badblocks does not guess at the block size, so you have to get
# it (from dumpe2fs) and feed it to badblocks. It is simpler to just
# have mke2fs call badblocks, but you do loose the ability to have a
# writing test easily. -- C^2

# 2001 11 25: Changed the regex that extracts partition labels from
# the mount command. This change does not affect the results at all,
# it just makes it possible to use Emacs' perl mode to indent
# correctly. I just escaped the left bracket in the regex. -- C^2

# Discussion:

# fdisk will spit out a file of the form below if you run it as "fdisk
# -l".

# root@tester ~/bin $ fdisk -l /dev/hda

# Disk /dev/hda: 64 heads, 63 sectors, 1023 cylinders
# Units = cylinders of 4032 * 512 bytes

#    Device Boot    Start       End    Blocks   Id  System
# /dev/hda1             1         9     18112+  83  Linux
# /dev/hda2            10      1023   2044224    5  Extended
# /dev/hda5            10       368    723712+  83  Linux
# /dev/hda6           369       727    723712+  83  Linux
# /dev/hda7           728       858    264064+  83  Linux
# /dev/hda8           859       989    264064+  83  Linux
# /dev/hda9           990      1022     66496+  82  Linux swap

# What fdisk does not do is provide output suitable for later
# importing into fdisk, a la sfdisk. This script parses the output
# from fdisk and creates an input file for fdisk. Use the input file
# like so:

# fdisk /dev/hdx < dev.hdx

# For the bare metal restore package, this script also builds a script
# that will execute the above command so you can run it from your zip
# disk. Because the bare metal restore scripts all are in /root/bin,
# the data file and script created by this script are also placed
# there. The same script also creates appropriate Linux file systems,
# either ext2fs, or Linux swap. There is limited support for FAT12,
# FAT16 and FAT32. For anything else, you're on your own.

# Note for FAT32: According to the MS KB, there are more than one
# reserved sectors for FAT32, usually 32, but it can vary. Do a search
# in M$'s KB for "boot sector" or BPB for the gory details. For more
# info than you really need on how boot sectors are used, see
# http://support.microsoft.com/support/kb/articles/Q140/4/18.asp

# You can also edit dev.x to change the sizes of partitions. Don't
# forget, if you change the size of a FAT partition across the 32MB
# boundary, you need to change the type as well! Run "fdisk /dev/hda"
# or some such, then the l command to see the available partition
# types. Then go ahead and edit dev.x appropriately. Also, when moving
# partition boundarys with hand edits, make sure you move both logical
# and extended partition boundaries appropriately.

# Bad block checking right now is a quick read of the partition. A
# writing check is also possible but more difficult. You have to run
# badblocks as a separate command, and pass the bad block list to
# mke2fs in a file (in /tmp, which is a ram disk). You also have to
# know how large the blocks are, which you learn by running
# dumpe2fs. It gets messy and I haven't done it yet. You probably
# don't need it for a new hard drive, but if you have had a hard drive
# crash on you and you are reusing it (while you are waiting for its
# replacement to come in, I presume), then I highly recommend it. Let
# me know how you do it.

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.

# cut2fmt figures out the format string for the unpack function we use
# to slice and dice the output from fdisk. From Christiansen and
# Torkington, Perl Cookbook 5.

sub cut2fmt {
    my (@positions) = @_;
    my $template    = '';
    my $lastpos     = 1;

    foreach $place (@positions) {
        $template .= "A" . ($place - $lastpos) . " ";
        $lastpos = $place;
    }

    $template .= "A*";
    return $template;
}


# Sub gpl, a subroutine to ship the GPL and other header information
# to the current output file.

sub gpl {
    my $FILE = shift;
    my $year = shift;

    print $FILE <<FINIS;

# Copyright $year through the last date of modification Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.

FINIS

}

sub getBootSector {
    my $infile = $_[0];
    my $outfile = $_[1];

    $systemcmd = "dd if=$infile of=$outfile bs=512 count=1 &> /dev/null ";
    system ($systemcmd);
}


# If we have one & only one swap partition, then this must be
# it. Otherwise the user is on her own. We scan fstab for swap mount
# points that have labels for their devices. If there is one and only
# one, we assume that's it, otherwise pass.

sub getswaplabel {
    my $dev = $_[0];

    $fstabpid = open (FSTAB, "< /etc/fstab")
        or die "Couldn't fork: $!\n";
    while (defined (my $line = <FSTAB>)) {
        chop ($line);
        @fstabs = split (" ", $line);
        if (@fstabs[1] eq "swap") {
            $swaplabel = @fstabs[0];
            if ($swaplabel =~ /LABEL/) {
                $swaps++;
                $sl = substr ($swaplabel, 6);
            }
#           print ("\"@fstabs[0]\", \"@fstabs[1]\", \"$sl\", $swaps.\n");
            break;
        }
    }
    close (FSTAB);

#   print "label is $sl.\n";

    if ($swaps == 1) {
        $ret = "mkswap \$blockcheck -L $sl";
        $ret .= " $dev\n\n";
    } else {
        $ret = "mkswap \$blockcheck $dev\n\n";
    }

#   print ("Returning :$ret\n");

    return $ret;
}

# dolvm is a subroutine to handle LVM partitions. This is
# experimental....

$lvms = 0;			# true if we've been here before

sub dolvm {

    print ("In dolvm ()...\n");

    if ($lvms == 0) {
        $lvms = 1;

        # Scan /etc/fstab for the logical volumes and write a script to
        # make file systems on them and another to mount 'em later on.

        $mklvs = open (MKLVS, "> make.lvs")
            or die "Couldn't fork: $!\n";

        print MKLVS <<FINIS;
#! /bin/sh

# A script to create file systems on logical volumes. Created at bare
# metal backup time by the Perl script make.fdisk.
FINIS

        &gpl (*MKLVS, "2006");


        print MKLVS <<FINIS;

export blockcheck=\$1;

if [ "\$blockcheck" != "-c" ] && [ -n "\$blockcheck" ]
then
    echo "\${0}: Build file systems on logical volumes."
    echo "\${0}: -c: block check during file system making."
    exit 1;
fi

export LVM_SYSTEM_DIR=\$(pwd)/lvm

FINIS

        $mtlvs = open (MTLVS, "> mount.lvs")
            or die "Couldn't fork: $!\n";

        print MTLVS <<FINIS;
#! /bin/sh

# A script to mount file systems on logical volumes. Created at bare
# metal backup time by the Perl script make.fdisk.
FINIS

        &gpl (*MTLVS, "2006");


        # Now cycle through all the known logical volumes & set them
        # up. N.B.: This has been tested on a machine with only one
        # LV. But it *should* work.

        $pvdisp = open (PVDISP, "pvdisplay -c |")
            or die ("Can't open LVM display.\n");
        while (defined (my $pv = <PVDISP>)) {
            chop ($pv);
            print ("$pv\n");
            @pv = split (":", $pv);
            $uid = @pv[11];
            $pvname = @pv[1];
            $phv = @pv[0];
            print ("pv $pvname has uid $uid.\n");

            # back up the LVM's lvm details. Get the config files.
            system ("vgcfgbackup -f LVM.backs.$pvname $pvname");

            print (MKLVS "echo \"y\\n\" | pvcreate -ff --uuid \"$uid\"\\\n");
            print (MKLVS "    --restorefile lvm/archive/${pvname}_*.vg $phv\n");
            print (MKLVS "vgcfgrestore --file LVM.backs.$pvname $pvname\n\n");
        }

        print (MKLVS "# Hideously disty dependent!\nif [ -e /etc/init.d/lvm ] ; then\n");
        print (MKLVS "    /etc/init.d/lvm start\nfi\n\n");

        $fstabpid = open (FSTAB, "< /etc/fstab")
            or die "Couldn't fork: $!\n";
        while (defined (my $line = <FSTAB>)) {
            chop ($line);
            @fstabs = split (" ", $line);
            if (@fstabs[0] =~ /VolGroup/ ) {
                #           print ("$line\n");
                if (@fstabs[2] eq "swap") {
                    print (MKLVS "echo\necho making LV @fstabs[0] a swap partition.\n");
                    print (MKLVS "mkswap \$blockcheck @fstabs[0]\n\n");
                } elsif (@fstabs[2] == "ext3") {
                    print (MKLVS "echo\necho making LV @fstabs[0], @fstabs[1],");
					print (MKLVS " an ext3 partition.\n");
                    print (MKLVS "mke2fs -j \$blockcheck @fstabs[0]\n\n");

                    print (MTLVS "mkdir -p /target$fstabs[1]\n");
                    print (MTLVS "mount @fstabs[0] /target$fstabs[1]\n\n");
                } elsif (@fstabs[2] == "ext2") {
                    print (MKLVS "echo\necho making LV @fstabs[0], @fstabs[1],");
					print (MKLVS " an ext2 partition.\n");
                    print (MKLVS "mke2fs \$blockcheck @fstabs[0]\n\n");

                    print (MTLVS "mkdir -p /target$fstabs[1]\n");
                    print (MTLVS "mount @fstabs[0] /target$fstabs[1]\n\n");
                } else {
                    print ("Opps, unknown type of logical volume, @fstabs[0]\n");
                }
            }
        }
        print (MTLVS "mount | grep -i \"/target\"\n");

        close (FSTAB);
        close (MKLVS);
        close (MTLVS);

        chmod 0700, "${outputfilepath}make.lvs";
        chmod 0700, "${outputfilepath}mount.lvs";

        # Copy the LVM configuration to where we can get at it...

        system ("cp -rp /etc/lvm .");

    }

    print ("Leaving dolvm ()...\n");

    return ($ret);
}


# Begin main line code.

# Provide a default device.

# print "\$ARGV[0] is $ARGV[0].\n";

$device = defined ($ARGV[0]) ? $ARGV[0] : "/dev/hda";

# Need to check to see if $device is a sym link. If it is, the mount
# point is the target of the link. (Mandrake) Otherwise we search for
# mount points on $device. Fedora, Red Hat.

if ( -l $device) {

    # It is a sym link. Get the target of the link, then make it into
    # an absolute path, preserving the numbering.

    $mountdev = '/dev/' . readlink ($device);
    $mountdev =~ s|ide/host(\d+)/bus(\d+)/target(\d+)/lun(\d+)/disc
        |ide/host\1/bus\2/target\3/lun\4|x;
} else {
    # not a sym link; just assign it.
    $mountdev = $device;
}

# print "Device is $device; mount device is $mountdev.\n";

# Prepare format string. Here are two format strings I have found
# useful. Here, column numbers are 1 based, i.e. the leftmost column
# is column 1, not column 0 as in Emacs.

# We select a format string according to fdisk's version.

$fdpid = open (FDVER, "fdisk -v |") or die "Couldn't fork: $!\n";
while (<FDVER>) {
    @_ = unpack ("A7 A*", $_);
    $fdver=$_[1];
    $fdver =~ s/[^\d.]//g; # strip non-numbers, non-periods, as in "2.12pre".
}

# print "fdisk version is $fdver\n";

if ($fdver < 2.12) {
# fdisk to 2.11?? Red Hat, Fedora Core 1
    $fmt = cut2fmt (11, 19, 24, 34, 45, 49);
} else {
# fdisk 2.12 & up?? Mandrake 10.0, Fedora Core 2
    $fmt = cut2fmt (12, 14, 26, 38, 50, 55);
}
# print "Format string is $fmt.\n";

# define fields in the array @_.
$dev = 0;
$bootable = 1;
$firstcyl = 2;
$lastcyl = 3;
$parttype = 5;
$partstring = 6;

$target = "\/target";

$outputfilename = $device;
$outputfilename =~ s/\//./g;
$outputfilename = substr ($outputfilename, 1, 100);

$outputfilepath = "/root/bin/";


# Make a hash of the labels.
$mpid = open (MOUNT, "mount -l |") or die "Couldn't fork: $!\n";
while (<MOUNT>) {
    if ($_ =~ /^$mountdev/i) { # is this a line with a partition in it?
#       print $_;               # print it just for grins
        split;
        if ($_[6] ne "") {      # only process if there actually is a label
            $_[6] =~ s/[\[\]]//g; # strike [ and ].
            $labels{$_[0]} = $_[6];
#           print "The label of file device $_[0] is $labels{$_[0]}.\n";
        }


        # We only mount if it's ext2fs or ext3fs and read and write.

        if ($_[4] =~ /ext[23]/ and $_[5] =~ /\(rw/ ) {
            if ($_[0] =~ /ide/i) {

                # We have a devfs system, e.g. Mandrake. This code
                # converts back from the devfs designation to the old
                # /dev/hd* designation for tomsrtb. I have NOT checked
                # this out for drives other than /dev/hda. Also, this
                # code does not handle SCSI drives.

                if ( $_[0] =~ /target0/ && $_[0] =~ /bus0/ ) {
                    $letter = 'a';
                } elsif ( $_[0] =~ /target1/ && $_[0] =~ /bus0/) {
                    $letter = 'b';
                } elsif ( $_[0] =~ /target0/ && $_[0] =~ /bus1/) {
                    $letter = 'c';
                } else {
                    $letter = 'd';
                }
                $_[0] =~ s|/ide/host\d+/bus\d+/target\d+/lun\d+/part|/hd|g;
                $_[0] =~ s/hd/hd$letter/;
            }
            $mountpoints{$_[2]} = $_[0];
#             print "$_[2] is the mountpoint for tomsrtbt";
#             print " device $mountpoints{$_[2]}.\n";
        }
    }
}
close (MOUNT);

# Get sfdisk output. If we have sfdisk at restore time (e.g. Knoppix),
# we'll use it.

system "sfdisk -d $device > $outputfilepath${outputfilename}.sfd";

# Otherwise we'll use the output from fdisk, which may or may not be
# any more accurate.

$fpid = open (FDISK, "fdisk -l $device |") or die "Couldn't fork: $!\n";

open (OUTPUT, "> $outputfilepath${outputfilename}")
    or die "Couldn't open output file $outputfilepath${outputfilename}.\n";

while (<FDISK>) {
    if ($_ =~ /^$device/i) {    # is this a line with a partition in it?
#       print $_;               # print it just for grins
        chop;                   # kill trailing \r
        @_ = unpack ($fmt, $_);

        # Now strip white spaces from cylinder numbers, white space &
        # leading plus signs from partition type.
        @_[$firstcyl] =~ s/[ \t]+//;
        @_[$lastcyl] =~ s/[ \t]+//;
        @_[$parttype] =~ s/[+ \t]+//;

        $partnumber = substr(@_[$dev], 8, 10); # get partition number for this line
        # just for grins
#         print "  $partnumber, @_[$firstcyl], @_[$lastcyl],";
#         print " @_[$parttype], @_[$partstring]\n";

        # Here we start creating the input to recreate the partition
        # this line represents.

        print OUTPUT "n\n";
        if ($partnumber < 5) {
            # primary Linux partition
            if (@_[$parttype] == 83) {
                print OUTPUT "p\n$partnumber\n@_[$firstcyl]\n";
                # in case it's all on one cylinder
                if (@_[$firstcyl] ne @_[$lastcyl]) {
                    print OUTPUT "@_[$lastcyl]\n";
                }

                # Now detect if this is an ext3 (journaling)
                # partition. We do this using dumpe2fs to dump the
                # partition and grepping on "journal". If the
                # partition is ext2, there will be no output. If it is
                # ext3, there will be output, and we use that fact to
                # set a command line switch. The command line switch
                # goes into an associative array (hash) so we don't
                # have to remember to reset it to the null string when
                # we're done.

                $dpid = open (DUMPE2FS,
                              "dumpe2fs @_[$dev] 2>/dev/null | grep -i journal |")
                    or die "Couldn't fork: $!\n";
                while (<DUMPE2FS>) {
#                   print "Dumpe2fs: $_";
                    $ext3{$_[$dev]} = "-j ";
                    last;
                }
                close (DUMPE2FS);

                if ($labels{@_[$dev]}) { # do we have a label?
                    $format .= "echo\necho formatting $checking@_[$dev]\n";
                    $format .= "mke2fs $ext3{$_[$dev]}\$blockcheck";
                    $format .= " -L $labels{@_[$dev]} @_[$dev]\n\n";
                } else {
                    $format .= "echo\necho formatting $checking@_[$dev]\n";
                    $format .= "mke2fs $ext3{$_[$dev]}\$blockcheck @_[$dev]\n\n";
                }

                # extended partition
            } elsif (@_[$parttype] == 5) {
                # print ("Creating Extended Partition.\n");
                print OUTPUT "e\n$partnumber\n@_[$firstcyl]\n";
                if (@_[$firstcyl] ne @_[$lastcyl]) {
                    print OUTPUT "@_[$lastcyl]\n";
                }

                # extended partition, Win95 Ext'd (LBA)
            } elsif (@_[$parttype] eq "f") {
                # print ("Creating Extended LBA Partition.\n");
                print OUTPUT "e\n$partnumber\n@_[$firstcyl]\n";
                if (@_[$firstcyl] ne @_[$lastcyl]) {
                    print OUTPUT "@_[$lastcyl]\n";
                }
                $typechanges .= "t\n$partnumber\nf\n";

                # primary Linux swap partition
            } elsif (@_[$parttype] == 82) {
                print OUTPUT "p\n$partnumber\n@_[$firstcyl]\n";
                if (@_[$firstcyl] ne @_[$lastcyl]) {
                    print OUTPUT "@_[$lastcyl]\n";
                }
                $typechanges .= "t\n$partnumber\n82\n";
                $format .= "echo\necho Making @_[$dev] a swap partition.\n";
                if ($labels{@_[$dev]}) { # do we have a label?
                    $format .= "mkswap \$blockcheck -L $labels{@_[$dev]}";
                    $format .= " @_[$dev]\n\n";
                } else {
                    $format .= getswaplabel (@_[$dev]);
                }

                # Primary mess-dos partition. We don't handle hidden
                # partitions.

            } elsif ( @_[$parttype] == 1 || @_[$parttype] == 4 || @_[$parttype] == 6
                      || @_[$parttype] eq "b" || @_[$parttype] eq "c"
                      || @_[$parttype] eq "e" ) {
                # print ("Making DOS primary partition.\n");

                getBootSector (@_[$dev], "$outputfilepath$outputfilename$partnumber");

                print OUTPUT "p\n$partnumber\n@_[$firstcyl]\n";
                # in case it's all on one cylinder
                if (@_[$firstcyl] ne @_[$lastcyl]) {
                    print OUTPUT "@_[$lastcyl]\n";
                }

                $typechanges .= "t\n$partnumber\n@_[$parttype]\n";
                $format .= "echo\necho formatting $checking@_[$dev]\n";
                $format .= "mkdosfs \$blockcheck";
                if ( @_[$parttype] == b || @_[$parttype] == c) {
                    # We have a W9x FAT32 partition. Add a command line switch.
                    $format .= " -F 32";
                }
                $format .= " @_[$dev]\n";
                $format .= "# restore FAT boot sector.\n";
                $format .= "dd if=$outputfilename$partnumber";
                $format .= " of=@_[$dev] bs=512 count=1\n\n";

            } elsif ( @_[$parttype] == "8e") {
                $format .= dolvm ();
            } else {
                # anything else partition
                print OUTPUT "p\n@_[$firstcyl]\n";
                if (@_[$firstcyl] ne @_[$lastcyl]) {
                    print OUTPUT "@_[$lastcyl]\n";
                }
                $typechanges .= "t\n$partnumber\n@_[$parttype]\n";
            }

        } else {
            # logical Linux partition
            if (@_[$parttype] == 83) {
                print OUTPUT "l\n@_[$firstcyl]\n";
                if (@_[$firstcyl] ne @_[$lastcyl]) {
                    print OUTPUT "@_[$lastcyl]\n";
                }

                # Now detect if this is an ext3 (journaling)
                # partition. We do this using dumpe2fs to dump the
                # partition and grepping on "journal". If the
                # partition is ext2, there will be no output. If it is
                # ext3, there will be output, and we use that fact to
                # set a command line switch. The command line switch
                # goes into an associative array (hash) so we don't
                # have to remember to reset it to the null string when
                # we're done.

                $dpid = open (DUMPE2FS,
                              "dumpe2fs @_[$dev] 2>/dev/null | grep -i journal |")
                    or die "Couldn't fork: $!\n";
                while (<DUMPE2FS>) {
#                   print "Dumpe2fs: $_";
                    $ext3{$_[$dev]} = "-j ";
                    last;
                }
                close (DUMPE2FS);

                if ($labels{@_[$dev]}) { # do we have a label?
                    $format .= "echo\necho formatting $checking@_[$dev]\n";
                    $format .= "mke2fs $ext3{@_[$dev]}\$blockcheck";
                    $format .= " -L $labels{@_[$dev]} @_[$dev]\n\n";
                } else {
                    $format .= "echo\necho formatting $checking@_[$dev]\n";
                    $format .= "mke2fs $ext3{@_[$dev]}\$blockcheck @_[$dev]\n\n";
                }

                # logical Linux swap partition
            } elsif (@_[$parttype] == 82 ) {
                print OUTPUT "l\n@_[$firstcyl]\n";
                if (@_[$firstcyl] ne @_[$lastcyl]) {
                    print OUTPUT "@_[$lastcyl]\n";
                }
                $typechanges .= "t\n$partnumber\n82\n";
                $format .= "echo\necho Making @_[$dev] a swap partition.\n";
                if ($labels{@_[$dev]}) { # do we have a label?
                    $format .= "mkswap \$blockcheck -L $labels{@_[$dev]}";
                    $format .= " @_[$dev]\n\n";
                } else {
                    $format .= getswaplabel (@_[$dev]);
                }

                # Logical mess-dos partition. We don't handle hidden
                # partitions.

            } elsif ( @_[$parttype] == 1 || @_[$parttype] == 4 || @_[$parttype] == 6
                      || @_[$parttype] eq "b" || @_[$parttype] eq "c"
                      || @_[$parttype] eq "e" ) {
#               print ("Making DOS logical partition.\n");

                getBootSector (@_[$dev], "$outputfilepath$outputfilename$partnumber");

                print OUTPUT "l\n$partnumber\n@_[$firstcyl]\n";
                # in case it's all on one cylinder
                if (@_[$firstcyl] ne @_[$lastcyl]) {
                    print OUTPUT "@_[$lastcyl]\n";
                }
                $typechanges .= "t\n$partnumber\n@_[$parttype]\n";
                $format .= "echo\necho formatting $checking@_[$dev]\n";
                $format .= "mkdosfs \$blockcheck";
                if ( @_[$parttype] == b || @_[$parttype] == c) {
                    # We have a W9x FAT32 partition. Add a command line switch.
                    $format .= " -F 32";
                }
                $format .= " @_[$dev]\n";
                $format .= "# restore FAT boot sector.\n";
                $format .= "dd if=$outputfilename$partnumber";
                $format .= " of=@_[$dev] bs=512 count=1\n\n";

            } elsif ( @_[$parttype] == "8e") {
                $format .= dolvm ();
            } else {
                # anything else partition
                print OUTPUT "l\n@_[$firstcyl]\n";
                if (@_[$firstcyl] ne @_[$lastcyl]) {
                    print OUTPUT "@_[$lastcyl]\n";
                }
                $typechanges .= "t\n$partnumber\n@_[$parttype]\n";
            }
        }

        # handle bootable partitions
        if (@_[$bootable] =~ /\*/) {
            print OUTPUT "a\n$partnumber\n";
        }
    } else {
        # If we got here, the current line does not have a partition in it.

        # Get the geometry for fdisk. Force fdisk to use the current
        # geometry at restoration time. Comment this out for
        # tomstrbt's fdisk; it doesn't like it.

        if ($_ =~ /heads.*sectors.*cylinders/i) {
#           print $_;               # again, for grins.
            chop;
            @geometry = split (/ /, $_);
            $geometry = "-H $geometry[0] -S $geometry[2] -C $geometry[4]";
#           print $geometry;
        }
    }
}

# Append all the partition type changes, validate, and print out the
# results.

print OUTPUT "${typechanges}v\nw\n";

close (OUTPUT);
close (FDISK);


open (OUTPUT, "> ${outputfilepath}make.$outputfilename")
    or die "Couldn't open output file ${outputfilepath}make.$outputfilename.\n";

print OUTPUT <<FINIS;
#! /bin/sh

# A script to restore the partition data of a hard drive and format
# the partitions. Created at bare metal backup time by the Perl script
# make.fdisk.
FINIS

&gpl (*OUTPUT, "2001");

print OUTPUT <<FINIS;

swapoff -a
# Hideously disty dependent!
if [ -e /etc/init.d/lvm ] ; then
    /etc/init.d/lvm stop
fi

export blockcheck=\$1;

if [ "\$blockcheck" != "-c" ] && [ -n "\$blockcheck" ]
then
    echo "\${0}: automated restore with no human interaction."
    echo "\${0}: -c: block check during file system making."
    exit 1;
fi

FINIS

# Clean the old partition table out. Turn off swap in case we're using
# it.

print OUTPUT "dd if=/dev/zero of=$device bs=512 count=2\n\nsync\n\n";


# command for fdisk

$fdiskcmd .= "# see if we have sfdisk & if so use it.\n";
$fdiskcmd .= "if which sfdisk ; then\n";
$fdiskcmd .= "  echo \"Using sfdisk.\"\n";
$fdiskcmd .= "  sfdisk --force $geometry $device < ${outputfilename}.sfd\n";
$fdiskcmd .= "else\n";
$fdiskcmd .= "  echo \"using fdisk.\"\n";
$fdiskcmd .= "  fdisk $geometry $device \< $outputfilename\n";
$fdiskcmd .= "fi\n\nsync\n\n";


print OUTPUT $fdiskcmd;
print OUTPUT $format;

print OUTPUT "fdisk -l \"$device\"\n";

close (OUTPUT);

# Now build the script that will build the mount points on the root
# and other partitions.

open (OUTPUT, "> ${outputfilepath}mount.$outputfilename")
    or die "Couldn't open output file ${outputfilepath}make.$outputfilename.\n";

print OUTPUT <<FINIS;
#! /bin/sh

# A script to create a minimal directory tree on the target hard drive
# and mount the partitions on it. Created at bare metal backup time by
# the Perl script make.fdisk.
FINIS

&gpl (*OUTPUT, "2001");

print OUTPUT <<FINIS;

# WARNING: If your Linux system mount partitions across hard drive
# boundaries, you will have multiple "mount.dev.* scripts. You must
# ensure that they run in the proper order. The root partition should
# be mounted first, then the rest in the order they cascade. If they
# cross mount, you'll have to handle that manually.

FINIS


# We have a hash of mount points and devices in %mountpoints. However,
# we have to process them such that directories are built on the
# appropriate target partition. E.g. where /usr/local is on its own
# partition, we have to mount /usr before we build /usr/local. We can
# ensure this by sorting them. Shorter mount point paths will be built
# first. We can't sort a hash directly, so we use an array.

# We build commands to create the appropriate mount points and then
# mount the partitions to the mount points. This is in preparation for
# untarring the contents of the ZIP disk, done in restore.metadata.

foreach $point ( sort keys %mountpoints) {
    print OUTPUT "\n# $point is the mountpoint for";
    print OUTPUT " tomsrtbt device $mountpoints{$point}.\n";
    print OUTPUT "mkdir -p $target$point\n";
    print OUTPUT "mount $mountpoints{$point} $target$point\n";
}

print OUTPUT "\nmount | grep -i \"/target\"\n";

close (OUTPUT);

# These scripts are dangerous & should only be visible to root.

chmod 0700, "${outputfilepath}make.$outputfilename";
chmod 0700, "${outputfilepath}mount.$outputfilename";
chmod 0600, "${outputfilepath}${outputfilename}*";

11.1.2. make.dev.hda

此脚本是上面 make.fdisk 生成的脚本示例。 它使用像下面的 dev.hda 这样的数据文件。 它构建分区并在其中一些分区上放置文件系统。 这是在恢复时运行的第一个脚本。

如果您足够勇敢地编辑 dev.hda(请参阅 q.v.),例如,添加新分区,您可能还需要编辑此脚本。

如果您希望 make.dev.hda 在将文件系统放在分区上时检查坏块,请使用 "-c" 命令行选项。

#! /bin/sh

# A script to restore the partition data of a hard drive and format
# the partitions. Created at bare metal backup time by the Perl script
# make.fdisk.

# Copyright 2001 through the last date of modification Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.


export blockcheck=$1;

if [ "$blockcheck" != "-c" ] && [ -n "$blockcheck" ]
then
    echo "${0}: automated restore with no human interaction."
    echo "${0}: -c: block check during file system making."
    exit 1;
fi

dd if=/dev/zero of=/dev/hda bs=512 count=2

swapoff -a
sync

# see if we have sfdisk & if so use it.
if which sfdisk ; then
  echo "Using sfdisk."
  sfdisk  -H 128 -S 63 -C 523 /dev/hda < dev.hda.sfd
else
  echo "using fdisk."
  fdisk  -H 128 -S 63 -C 523 /dev/hda < dev.hda
fi

sync

echo
echo formatting /dev/hda1
mkdosfs $blockcheck /dev/hda1
# restore FAT boot sector.
dd if=dev.hda1 of=/dev/hda1 bs=512 count=1

echo
echo formatting /dev/hda2
mke2fs -j $blockcheck -L /boot /dev/hda2

echo
echo formatting /dev/hda3
mke2fs -j $blockcheck -L / /dev/hda3

echo Making /dev/hda5 a swap partition.
mkswap $blockcheck /dev/hda5

fdisk -l "/dev/hda"

11.1.3. make.lvs

make.lvsmake.fdisk 生成,但仅当存在逻辑卷时才生成。 顾名思义,它构建逻辑卷并在其上创建文件系统。

#! /bin/sh

# A script to create file systems on logical volumes. Created at bare
# metal backup time by the Perl script make.fdisk.

# Copyright 2006 through the last date of modification Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.


export blockcheck=$1;

if [ "$blockcheck" != "-c" ] && [ -n "$blockcheck" ]
then
    echo "${0}: Build file systems on logical volumes."
    echo "${0}: -c: block check during file system making."
    exit 1;
fi

export LVM_SYSTEM_DIR=$(pwd)/lvm.cfg

echo "y\n" | pvcreate -ff --uuid "CCmw0N-0We2-HzRS-jRZa-FkC7-NxTc-oAfvpX"\
     --restorefile lvm.cfg/archive/VolGroup00_*.vg   /dev/hda3
vgcfgrestore --file LVM.backs VolGroup00

# Hideously disty dependent!
if [ -e /etc/init.d/lvm ] ; then
     /etc/init.d/lvm start
fi

echo
echo making LV /dev/VolGroup00/LogVol00 an ext3 partition.
mke2fs -j $blockcheck /dev/VolGroup00/LogVol00

echo
echo making LV /dev/VolGroup00/LogVol02 an ext3 partition.
mke2fs -j $blockcheck /dev/VolGroup00/LogVol02

echo
echo making LV /dev/VolGroup00/LogVol01 a swap partition.
mkswap $blockcheck /dev/VolGroup00/LogVol01

11.1.4. mount.dev.hda

此脚本是上面 make.fdisk 生成的脚本示例。 它构建挂载点并将分区挂载到它们,使目标文件系统准备好恢复文件。 这是在恢复时运行的第二个脚本。

如果您足够勇敢地编辑 dev.hda(请参阅 q.v.),例如,添加新分区,您可能还需要编辑此脚本。

#! /bin/sh

# A script to create a minimal directory tree on the target hard drive
# and mount the partitions on it. Created at bare metal backup time by
# the Perl script make.fdisk.

# Copyright 2001 through the last date of modification Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.


# WARNING: If your Linux system mount partitions across hard drive
# boundaries, you will have multiple "mount.dev.* scripts. You must
# ensure that they run in the proper order. The root partition should
# be mounted first, then the rest in the order they cascade. If they
# cross mount, you'll have to handle that manually.


# / is the mountpoint for tomsrtbt device /dev/hda3.
mkdir /target/
mount /dev/hda3 /target/

# /boot is the mountpoint for tomsrtbt device /dev/hda2.
mkdir /target/boot
mount /dev/hda2 /target/boot

mount | grep -i "/dev/hda"

11.1.5. mount.lvs

mount.lvsmake.fdisk 生成,但仅当存在逻辑卷时才生成。 顾名思义,它挂载逻辑卷,为恢复做好准备。

#! /bin/sh

# A script to mount file systems on logical volumes. Created at bare
# metal backup time by the Perl script make.fdisk.

# Copyright 2006 through the last date of modification Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.

mkdir -p /target/
mount /dev/VolGroup00/LogVol00 /target/

mkdir -p /target/home
mount /dev/VolGroup00/LogVol02 /target/home

mount | grep -i "/target"

11.1.6. dev.hda

此数据文件在恢复时使用。 它通过脚本 make.dev.hda 馈送到 fdisk。 它在备份时由 make.fdisk 生成。 熟悉 fdisk 的人会认识到,每一行都是一个 fdisk 命令或值,例如柱面号。 因此,可以通过编辑此文件来更改分区大小和添加新分区。 这就是倒数第二个命令是 v 的原因,以便在写入之前验证分区表。

n
p
1
1
29
a
1
n
p
2
30
44
n
e
3
45
1023
n
l
45
944
n
l
945
1023
t
1
6
t
6
82
v
w

11.1.7. save.metadata

这是作为备份过程一部分运行的第一个脚本。 它调用上面的 make.fdisk。 如果您有 SCSI 硬盘驱动器或多块硬盘驱动器要备份,请适当地编辑对 make.fdisk 的调用。

#! /bin/sh

# A script to save certain meta-data off to the boot partition. Useful for
# restoration.

# Time-stamp: <2006-04-05 20:37:09 ccurley save.metadata>

# Copyright 2000 through the last date of modification, Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at
# http://www.fsf.org/

# 2006-03-26: had a deprecated option in the sort options; fixed that.

# 2005-09-09: Added a line to create a boot disk ISO in the ZIP drive.

# 2005-08-30: Modernized sub-shell calls, a few other tweaks.

# 2005-07-29: Fedora Core 4 mods. Name of the directory to be saved
# has to be last. Also, we now specify --numeric-owner so as to avoid
# UID problems when using some live CD systems. And we now save to
# /var instead of a mounted ZIP disk.

# 2005-02-19: Fedora Core 3 mods.

# 2003 01 08: We now age the output from rpm -VA to make back
# comparisons easier.

# The loop that creates directories now has the -p option for mkdir,
# which means you can create parents on the fly if they don't already
# exist.

# initrd is now in the list of directories to create automatically.

# We now exclude more stuff when building the tarballs.

# 2002 07 01: Went to bzip2 to compress the archives, for smaller
# results. This is important in a 100MB ZIP disk. Also some general
# code cleanup.

# 2002 07 01: The function crunch will tar and BZIP2 the
# archives. This is cleaner than the old code, and has better safety
# checking.


# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.


# Crunch: A function to compress the contents of a directory and put
# the archive onto the ZIP disk.

# The first parameter is the name of the archive file to be
# created. The backup location, $zip, will be prepended and the
# extension, "tar.bz2" will be appended.

# All following parameters will be taken as additional directories or
# files to be put into the archive.

function crunch {

if [ -z "$1" ] || [ -z "$2" ]	# Checks if parameter #1 or #2 is zero length.
then
   echo "-Parameter #1 or #2 is missing.-"  # Also if no parameter is passed.
   return 1
else
   local file=$1		# The archive file to create
   shift			# Discard the file name
   local dirs=$@		# The director[y|ies] to archive
   local tarcmd="tar --numeric-owner -cjf"	# The tar command.

   local tarit="$tarcmd  $zip/$file.tar.bz2 $dirs"
   echo $tarit
   $tarit			# do it!!

   error=$?			# Preserve the exit code

   if [ $error != 0 ]		# Did we fail?
   then				# Yes
      echo "Tar failed with error $error"
      echo $tarcmd $zip/$file.tar.bz2 $dirs
      exit $error		# return tar's exit code as ours
   fi

   return 0			# For error testing if needed.
fi
}

# Begin the main line code
export zip="/var/bare.metal";	# Where we will put archives. Not the ZIP drive here.
#  export save="/mnt/save";

RPMVABACKS=/etc			# where we keep our backups
RPMVAROOT=rpmVa			# The root name of the pg backups
ANC=${RPMVABACKS}/${RPMVAROOT}.anc	# name for the oldest (ancient) backup
OLD=${RPMVABACKS}/${RPMVAROOT}.old	# name for the middling oldest backup
NEW=${RPMVABACKS}/${RPMVAROOT}.txt	# name for the newest backup

if [ -f ${ANC} ]; then
echo "Deleting ${ANC}"
rm ${ANC}
fi

if [ -f ${OLD} ]; then
echo "Aging ${OLD}"
mv ${OLD} ${ANC}
fi

if [ -f ${NEW} ]; then
echo "Aging ${NEW}"
mv ${NEW} ${OLD}
fi


# Now we save hard drive information. Run make.fdisk on each hard
# drive in the order in which it mounted from the root partition. That
# is, run it first on the hard drive with your root partition, then
# any hard drives that mount to the first hard drive, then any hard
# drives that mount to those. For example, if your root partition is
# on /dev/sdc, run "make.fdisk /dev/sdc" first.

# The reason for this is that make.fdisk produces a script to make
# mount points and then mount the appropriate partition to them during
# first stage restore. Mount points must be created on the partition
# where they will reside. The partitions must be mounted in this
# order. For example, if your /var and /var/ftp are both separate
# partitions, then you must mount /, create /var, then mount /var,
# then create /var/ftp. The order in which the script "first.stage"
# runs the mounting scripts is based on their time of creation.

# If necessary, put a line, "sleep 1" between calls to make.fdisk.

echo "Saving hard drive info"
make.fdisk /dev/hda

# back up RPM metadata

echo "Verifying RPMs."

rpm -Va | sort -t ' ' -k 3 | uniq > ${NEW}

echo "Finished verifying RPMs; now mounting the ZIP drive."

# Make sure we have the ZIP drive mounted.
# umount $zip
# modprobe ppa			# Driver for 100MB parallel port ZIP disk
# mount $zip			# It should have ext2fs on partition 1.

# clean it all out
# rm -r $zip/*
# mkdir -p $zip/lost+found

# Since we aren't saving to ZIP disk, we age the local copy.
rm -r $zip.old
mv $zip $zip.old
mkdir $zip

echo -e "$(hostname) bare metal ZIP disk, created $(date)" > $zip/README.txt
uname -a >> $zip/README.txt

# Preserve the release information. Tested with Red Hat/Fedora, should
# work with SuSE, Mandrake and other RPM based systems. Debian
# equivalent, anyone?

for releasefile in $(ls /etc/*release*) ; do
  # echo $releasefile
  if [ -e $releasefile ] && [ ! -L $releasefile ] ; then
    cat $releasefile >> $zip/README.txt
  fi
done

echo "Building the ZIP drive backups."

# These are in case we need to refer to them while rebuilding. The
# rebuilding process should be mostly automated, but you never
# know....

fdisk -l /dev/hda > $zip/fdisk.hda

ls -al /mnt > $zip/ls.mnt.txt
ls -al / > $zip/ls.root.txt

cd /

# Build our minimal archives on the ZIP disk. These appear to be
# required so we can restore later on.

crunch root --exclude root/.cpan --exclude root/.mozilla --exclude root/down root
crunch boot boot
crunch etc --exclude etc/samba --exclude etc/X11 --exclude etc/gconf etc
crunch lib lib

crunch usr.sbin usr/sbin
crunch usr.bin --exclude usr/bin/emacs-x --exclude usr/bin/emacs-21.4-x\
 --exclude usr/bin/emacsclient --exclude usr/bin/emacs-nox --exclude\
  usr/bin/gs --exclude usr/bin/pine --exclude usr/bin/gimp-1.2\
   --exclude usr/bin/doxygen --exclude usr/bin/postgres --exclude\
    usr/bin/gdb --exclude usr/bin/kmail --exclude usr/bin/splint\
	 --exclude usr/bin/odbctest --exclude usr/bin/php --exclude \
	 usr/bin/xchat --exclude usr/bin/gnucash --exclude usr/bin/pdfetex\
	  --exclude usr/bin/pdftex --exclude usr/bin/smbcacls\
	   --exclude usr/bin/evolution-calendar --exclude usr/bin/xpdf\
	    --exclude usr/bin/xmms usr/bin
crunch sbin sbin
crunch bin bin
crunch dev dev

# RH8. Fedora 1 puts them in /lib
# crunch kerberos usr/kerberos/lib/

# Now optional saves.

# arkeia specific:
# crunch arkeia usr/knox

# save these so we can use ssh for restore. *crack* for RH 7.0 login
# authentication.
# RH 8.0
# crunch usr.lib usr/lib/*crack* usr/lib/libz* usr/lib/libssl* usr/lib/libcrypto*
# Fedora 1
# crunch usr.lib usr/lib/*crack* usr/lib/libz* usr/lib/libwrap*\
#  usr/lib/libk* usr/lib/*krb5* /usr/lib/libgss*
# Fedora 3
crunch usr.lib usr/lib/*crack* usr/lib/libz* usr/lib/libwrap*\
 usr/lib/libk* usr/lib/*krb5* usr/lib/libgss*

# Grub requires these at installation time.
crunch usr.share.grub usr/share/grub

# save the scripts we used to create the ZIP disk and the ones we will
# use to restore it.
mkdir $zip/root.bin
cp -p /root/bin/* $zip/root.bin
rm $zip/root.bin/*~ $zip/root.bin/#*#

echo "Testing our results."
find $zip -iname "*.bz2" | xargs bunzip2 -t

# Not a normal part of the process: we duplicate the ZIP disk onto an
# NFS mount elsewhere.

#  echo "Backing the ZIP drive to the NFS mount."

#  umount $save
#  mount $save

#  rm -r $save/zip
#  mkdir -p $save/zip
#  cp -pr $zip $save

# Since we're doing system stuff anyway, make a boot disk ISO image
# suitable for burning. It uses the current kernel.

mkbootdisk --iso --device $zip/bootdisk.$(uname -r).iso $(uname -r)

du -hs ${zip}*
df -m

11.1.8. restore.metadata

此脚本从 ZIP 磁盘恢复元数据,作为第一阶段恢复。

#! /bin/sh

# A script to restore the meta-data from the ZIP disk. This runs under
# tomsrtbt only after partitions have been rebuilt, file systems made,
# and mounted. It also assumes the ZIP disk has already been
# mounted. Mounting the ZIP disk read only is probably a good idea.

# Time-stamp: <2006-04-05 20:36:49 ccurley restore.metadata>

# Copyright 2000 through the last date of modification Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# 2005-08-03: We now use a relative path, so you can load from
# different places depending on the first stage system you are
# using. Also added some FC4 tricks, and some changes to better
# reproduce the permissions and ownerships.

# 2003 08 23: Oops: tar on tomsrtbt does not respect -p. Try setting
# umask to 0000 instead.

# 2003 02 13: Tar was not preserving permissions on restore. Fixed
# that.

# 2002 07 01: Went to bzip2 to compress the archives, for smaller
# results. This is important in a 100MB ZIP disk. Also some general
# code cleanup.

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.

umask 0000

cd ..               # Assume we are in root.bin
zip=$(pwd);         # Where we mount the zip drive.
target="/target";       # Where the hard drive to restore is mounted.

ls -lt $zip/*.bz2       # Warm fuzzies for the user.

cd $target

# Restore the archived metadata files.
for archive in $( ls $zip/*.bz2 ); do
  echo $archive
  ls -al $archive
  bzip2 -dc $archive | tar -xf -
done

# Build the mount points for our second stage restoration and other
# things.

# If you boot via an initrd, make sure you build a directory here so
# the kernel can mount the initrd at boot. tmp/.font-unix is for the
# xfs font server.

for dir in mnt/dosc mnt/zip mnt/imports mnt/nfs proc initrd tmp/.font-unix\
 var/empty/sshd var/log back selinux sys /var/cache/yum /var/lock; do
  mkdir -p $target/$dir
done

for dir in mnt usr usr/share $(ls -d var/*) selinux usr/lib var var/cache/yum; do
  chmod go-w $target/$dir
done

chown root:lock /var/lock
chmod 775 /var/lock

# [root@jhereg /]# ll -d mnt usr usr/share  $(ls -d var/*) selinux usr/lib var var/cache/yum
# drwxr-xr-x   4 root    root     4096 Oct 10 08:55 mnt
# drwxr-xr-x   2 root    root     4096 Oct 10 08:41 selinux
# drwxr-xr-x  14 root    root     4096 Oct 10 08:46 usr
# drwxr-xr-x  40 root    root    12288 Oct 10 10:40 usr/lib
# drwxr-xr-x  63 root    root     4096 Oct 10 11:11 usr/share
# drwxr-xr-x  20 root    root     4096 Oct 10 08:52 var
# drwxr-xr-x   2 root    root     4096 Oct 10 08:51 var/account
# drwxr-xr-x   4 root    root     4096 Oct 10 08:53 var/cache
# drwxr-xr-x   4 root    root     4096 Oct 10 10:44 var/cache/yum
# drwxr-xr-x   3 netdump netdump  4096 Aug 22 13:13 var/crash
# drwxr-xr-x   3 root    root     4096 Oct 10 08:51 var/db
# drwxr-xr-x   3 root    root     4096 Oct 10 08:52 var/empty
# drwxr-xr-x  13 root    root     4096 Oct 10 11:11 var/lib
# drwxr-xr-x   2 root    root     4096 May 22 22:28 var/local
# drwxrwxr-x   4 root    lock     4096 Sep  1 08:37 var/lock
# drwxr-xr-x   7 root    root     4096 Oct 10 11:14 var/log
# lrwxrwxrwx   1 root    root       10 Oct 10 08:42 var/mail -> spool/mail
# drwxr-x---   4 root    named    4096 Aug 22 14:33 var/named
# drwxr-xr-x   2 root    root     4096 May 22 22:28 var/nis
# drwxr-xr-x   2 root    root     4096 May 22 22:28 var/opt
# drwxr-xr-x   2 root    root     4096 May 22 22:28 var/preserve
# drwxr-xr-x   2 root    root     4096 Mar 28  2005 var/racoon
# drwxr-xr-x  13 root    root     4096 Oct 10 11:14 var/run
# drwxr-xr-x  13 root    root     4096 Oct 10 08:53 var/spool
# drwxrwxrwt   2 root    root     4096 Oct 10 11:14 var/tmp
# drwxr-xr-x   3 root    root     4096 Oct 10 08:53 var/yp

# chmod a-w $target/proc        # Restore /proc's read-only permissions

# Set modes
chmod 0111 $target/var/empty/sshd

# For Fedora. First two for xfs.
# chroot $target chown xfs:xfs /tmp/.font-unix
# chmod 1777 $target/tmp/.font-unix # set the sticky bit.
chmod 1777 $target/tmp

# Restore the scripts we used to create the ZIP disk and the ones we will
# use to restore it. These should be the latest & greatest in case we had
# to do any editing during 1st stage restore.
cp -p $zip/root.bin/* $target/root/bin

# Now install the boot sector.
# chroot $target /sbin/lilo -C /etc/lilo.conf
chroot $target /sbin/grub-install /dev/hda

df -m

11.1.9. first.stage

此脚本在没有操作员干预的情况下运行整个第一阶段恢复。

如果您希望 make.dev.hda 在将文件系统放在分区上时检查坏块,请使用 "-c" 命令行选项。

#! /bin/sh

# A master script to run the other, detailed scripts. Use this script
# only if you want no human intervention in the restore process. The
# only option is -c, which forces bad block checking during formatting
# of the partitions.

# Time-stamp: <2006-04-05 20:35:39 ccurley first.stage>

# Copyright 2002 through the last date of modification Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.

# 2005-08-07 We no longer assume the working directory. This is
# because the working directory will vary greatly according to which
# Linux disty you use and how you are doing your restoration.

export blockcheck=$1;

if [ "$blockcheck" != "-c" ] && [ -n "$blockcheck" ]
then
    echo "${0}: automated restore with no human interaction."
    echo "${0}: -c: block check during file system making."
    exit 1;
fi

for drive in $( ls make.dev.* ); do
    echo $drive$'\a'
    sleep 2
    ./$drive $blockcheck;
done

# If there are any LVM volumes, now is the time to restore them.

if [ -e LVM.backs ] && [ -e make.lvs ] && [ -e mount.lvs ]
then
    echo make.lvs$'\a'
    sleep 2
    ./make.lvs

    echo mount.lvs$'\a'
    ./mount.lvs
fi


# WARNING: If your Linux system mount partitions across hard drive
# boundaries, you will have multiple "mount.dev.* scripts. You must
# ensure that they run in the proper order, which the loop below may
# not do. The root partition should be mounted first, then the rest in
# the order they cascade. If they cross mount, you'll have to handle
# that manually. If you have LVMs to deal with, that's a whole 'nother
# kettle of fish.

# The "ls -tr" will list the scripts in the order they are created, so
# it might be a good idea to create them (in the script save.metadata)
# in the order in which you should run them.

for drive in $( ls -tr mount.dev.* ); do
    echo $drive$'\a'
    sleep 2
    ./$drive;
done

./restore.metadata

# People who are really confident may comment this line in.
# reboot

11.2. 第二阶段

这些脚本在正在备份或恢复的计算机上运行。


11.2.1. back.up.all

此脚本通过 NFS 挂载保存到另一台计算机。 您可以调整它以保存到磁带驱动器或其他介质。

#! /bin/sh

# Back up the entire system to another computer's drive. To make this
# work, we need a convenient chunk of disk space on the remote computer we
# can nfs mount as /mnt/save.

# Time-stamp: <2003-04-24 09:56:05 ccurley back.up.all>

# Copyright 2000 through the last date of modification Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.

save="/mnt/save"

# Make sure it's there
umount $save
mount $save

cd /

rm $save/tester.tar.old.gz
mv $save/tester.tar.gz $save/tester.tar.old.gz

# save everything except /mnt, /proc, and nfs mounted directories.

time tar cf - / --exclude /mnt --exclude /proc --exclude $save\
    | gzip -c > $save/tester.tar.gz

11.2.2. back.up.all.ssh

这个脚本的功能与 back.up.all 完全相同,但它使用 ssh 而不是 nfs。

#! /bin/sh

# Back up the entire system to another computer's drive. To make this
# work, we need a convenient chunk of disk space on the remote
# computer. This version uses ssh to do its transfer, and compresses
# using bz2. This means this script has to know more about the other
# computer, which does not make for good modularization.

# Time-stamp: <2003-04-24 09:56:52 ccurley back.up.all.ssh>

# Copyright 2000 through the last date of modification Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.

save="/backs/tester"
backup_server="charlesc"

# rotate the old backups. Do it all in one line to minimze authentication overhead.
ssh $backup_server "rm $save/tester.tar.old.bz2; mv $save/tester.tar.bz2 \
    $save/tester.tar.old.bz2"

# save everything except /mnt, /proc, and squid directories.

time tar cf - / --exclude /mnt --exclude /proc --exclude /var/spool/squid\
    | ssh $backup_server "bzip2 -9 > $save/tester.tar.bz2"

11.2.3. restore.all

如果你使用 back.up.all 进行备份,则使用此恢复脚本。

#! /bin/sh

# A script to restore all of the data from an nfs mount. This is our final
# stage restore.

# Time-stamp: <2003-04-24 09:58:51 ccurley restore.all>

# Copyright 2000 through the last date of modification Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.

export save="/mnt/save"

mount $save

cd /
gunzip -dc $save/tester.tar.gz | tar -xpkf -

rm /var/run/*.pid

lilo

11.2.4. restore.all.ssh

如果你使用 back.up.all.ssh 进行备份,则使用此恢复脚本。

#! /bin/sh

# A script to restore all of the data using ssh and bunzip2. This is
# our final stage restore.

# Copyright 2000 through the last date of modification Charles Curley.

# Time-stamp: <2003-04-24 09:59:10 ccurley restore.all.ssh>

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.

save="/backs/tester/"
backup_server="charlesc"

cd /

ssh $backup_server "cat $save/tester.tar.bz2" | bunzip2 | tar -xpkf -

rm /var/run/*.pid

lilo

11.3. 备份服务器脚本

上面的 ssh 脚本可能存在安全问题。如果你在防火墙上运行它们,防火墙必须通过 ssh 访问备份服务器。在这种情况下,聪明的黑客也可能能够破解备份服务器。更安全的做法是在备份服务器上运行备份和恢复脚本,并让备份服务器访问防火墙。这些脚本就是为此而设计的。将它们重命名为get.xrestore.x其中x是目标计算机的名称。编辑它们(变量 $target 的初始化)以使用目标计算机的主机名,或重写它们以使用命令行参数。

这些脚本完全备份和恢复目标,而不仅仅是第一阶段的备份和恢复。另请注意get.tester也会备份 ZIP 磁盘,以防你需要更换故障的 ZIP 磁盘。

我经常使用这些脚本。


11.3.1. get.tester

#! /bin/sh

# Back up another computer's drive to this system. To make this work,
# we need a convenient chunk of disk space on this computer. This
# version uses ssh to do its transfer, and compresses using bz2. This
# version was developed so that the system to be backed up won't be
# authenticated to log onto the backup computer. This script is
# intended to be used on a firewall. You don't want the firewall to be
# authenticated to the backup system in case the firewall is cracked.

# Time-stamp: <2006-04-05 20:36:00 ccurley get.tester>

# Copyright 2000 through the last date of modification Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.

# 2004 04 03: added /sys to the list of excludes. It is a read-only
# pseudo-file system like /proc.

# 2002 07 01: We now set the path on the target to the zip drive with
# a variable. This fixes a bug in the command to eject the zip disk.

# 2002 07 01: The zip disk archives are now in bzip2 format, so this
# script has been changed to reflect that.


# The host name of the computer to be backed up.
target=tester
#zip=/mnt/zip
export zip="/var/bare.metal";	# Where we will put archives. Not the ZIP drive here.

echo Backing up $target

echo Aging the ZIP disk backups.

rm -r $target.old.zip

mv $target.zip $target.old.zip

# ssh $target "modprobe ppa ; mount -r $zip"

echo Copying the ZIP disk.

# -r for recursive copy, -p to preserve times and permissions, -q for
# quiet: no progress meter.

scp -qpr $target:$zip $target.zip

du -hs $target.*zip


echo Aging the archives

rm $target.tar.old.bz2

mv $target.tar.bz2 $target.tar.old.bz2

echo Cleaning out old yum packages
ssh $target "yum clean packages"

echo Backing up $target to the backup server.

# The "--anchored" option is there to prevent --exclude from excluding
# all files with that name. E.g. we only want to exclude /sys, not
# some other sys elsewhere in the file system.

ssh $target "cd / ; tar -cf - --anchored --exclude sys --exclude $zip\
 --exclude $zip.old --exclude mnt --exclude proc --exclude var/spool/squid\
 *" | bzip2 -9 | cat > $target.tar.bz2

# ssh $target "eject $zip"

echo Testing the results.
find . -iname "*.bz2" | xargs bunzip2 -t

11.3.2. restore.tester

#! /bin/sh

# A script to restore all of the data to tester via ssh. This is our final
# stage restore.

# Time-stamp: <2003-04-24 09:59:45 ccurley restore.tester>

# Copyright 2000 through the last date of modification Charles Curley.

# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.

# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.

# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

# You can also contact the Free Software Foundation at http://www.fsf.org/

# For more information contact the author, Charles Curley, at
# http://www.charlescurley.com/.

# The host name of the computer to be restored.

target=tester

bunzip2 -dc $target.tar.bz2 | ssh $target "cd / ; tar -xpkf - "

ssh $target lilo

12. 资源

排名不分先后。这些是你可能想要自行调查的东西。在此处列出不应被视为认可。事实上,在许多情况下,我没有使用过该产品,也无法对其进行评论。


A. GNU 自由文档许可证

版本 1.1,2000 年 3 月

版权所有 (C) 2000 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 允许所有人复制和分发本许可证文档的完整副本,但不允许更改它。


0. 前言

本许可证的目的是使手册、教科书或其他书面文档在自由的意义上“自由”:确保每个人都拥有有效自由地复制和再分发它,无论是否对其进行修改,无论是商业用途还是非商业用途。其次,本许可证为作者和出版商保留了一种因其工作获得认可的方式,同时不被视为对他人所做的修改负责。

本许可证是一种“反版权”,这意味着文档的衍生作品本身必须在相同的意义上是自由的。它补充了 GNU 通用公共许可证,后者是为自由软件设计的反版权许可证。

我们设计本许可证是为了将其用于自由软件的手册,因为自由软件需要自由文档:自由程序应该附带提供与软件相同自由的手册。但是本许可证不限于软件手册;它可以用于任何文本作品,无论主题是什么,或者是否作为印刷书籍出版。我们主要为以指导或参考为目的的作品推荐本许可证。


1. 适用性和定义

本许可证适用于任何手册或其他包含版权所有者声明可以根据本许可证条款分发的作品。“文档”(下文)是指任何此类手册或作品。任何公众成员都是被许可人,并被称为“你”。

文档的“修改版本”是指包含文档或其一部分的任何作品,无论是逐字复制,还是经过修改和/或翻译成另一种语言。

“次要章节”是文档的命名附录或前言部分,专门处理文档的出版商或作者与文档的总体主题(或相关事项)的关系,并且不包含任何可以直接属于该总体主题的内容。(例如,如果文档部分是数学教科书,则次要章节可能不解释任何数学。)该关系可以是与主题或相关事项的历史联系,或者是关于它们的法律、商业、哲学、伦理或政治立场。

“不变章节”是某些次要章节,其标题在声明文档根据本许可证发布的通知中被指定为不变章节的标题。

“封面文本”是在声明文档根据本许可证发布的通知中列出的某些简短文本段落,作为封面文本或封底文本。

文档的“透明”副本是指机器可读的副本,以规范向公众开放的格式表示,其内容可以使用通用文本编辑器或(对于像素组成的图像)通用绘画程序或(对于绘图)一些广泛可用的绘图编辑器直接且直接地查看和编辑,并且适合输入到文本格式化程序或自动翻译成各种适合输入到文本格式化程序的格式。以其他透明文件格式制作的副本,其标记旨在阻止或阻止读者随后进行修改,则不是透明的。不“透明”的副本称为“不透明”。

透明副本的合适格式示例包括没有标记的纯 ASCII、Texinfo 输入格式、LaTeX 输入格式、使用公开可用的 DTD 的 SGML 或 XML 以及为人工修改设计的符合标准的简单 HTML。不透明格式包括 PostScript、PDF、只能由专有文字处理器读取和编辑的专有格式、DTD 和/或处理工具通常不可用的 SGML 或 XML,以及某些文字处理器仅为输出目的而生成的机器生成的 HTML。

“标题页”对于印刷书籍,是指标题页本身,以及为容纳本许可证要求出现在标题页中的材料而需要的后续页。对于没有标题页格式的作品,“标题页”是指最突出的作品标题出现附近的文本,位于正文文本的开头之前。


2. 逐字复制

你可以在任何媒介中复制和分发文档,无论是商业用途还是非商业用途,前提是本许可证、版权声明和声明本许可证适用于文档的许可证声明在所有副本中都得到复制,并且你没有对本许可证的条款添加任何其他条件。你不得使用技术措施来阻碍或控制你制作或分发的副本的阅读或进一步复制。但是,你可以接受报酬以换取副本。如果你分发足够大量的副本,你还必须遵守第 3 节中的条件。

你也可以在上述相同条件下借出副本,并且你可以公开展示副本。


3. 大量复制

如果你出版印刷版文档,数量超过 100 份,并且文档的许可证声明要求封面文本,你必须将副本装在封皮中,这些封皮清楚且清晰地印有所有这些封面文本:封面上的封面文本,以及封底上的封底文本。两个封面还必须清楚且清晰地标识你为这些副本的出版商。封面必须以同等突出且可见的方式呈现完整标题的所有单词。你可以在封面上添加其他材料。仅限于封面的更改的复制,只要它们保留文档的标题并满足这些条件,就可以在其他方面被视为逐字复制。

如果任何一个封面的所需文本过于冗长而无法清晰地容纳,你应该将列出的第一个文本(尽可能多地容纳)放在实际封面上,并将其余文本继续放在相邻的页面上。

如果你出版或分发超过 100 份的不透明文档副本,你必须在每个不透明副本中包含一份机器可读的透明副本,或者在每个不透明副本中或随附声明一个公众可访问的计算机网络位置,其中包含文档的完整透明副本,没有添加材料,一般网络用户公众可以使用公共标准网络协议免费匿名下载。如果你使用后一种选项,你必须采取合理谨慎的步骤,当你开始大量分发不透明副本时,以确保该透明副本在声明的位置保持可访问状态,直到至少在你最后一次向公众(直接或通过你的代理或零售商)分发该版本的不透明副本后一年。

建议但不要求你在重新分发大量副本之前与文档的作者联系,让他们有机会为你提供文档的更新版本。


4. 修改

你可以根据上述第 2 节和第 3 节的条件复制和分发文档的修改版本,前提是你根据本许可证精确地发布修改版本,修改版本扮演文档的角色,从而将修改版本的发布和修改许可给拥有其副本的任何人。此外,你必须在修改版本中执行以下操作

  1. 在标题页上(以及封面上,如果有的话)使用与文档标题不同的标题,以及与以前版本的标题不同的标题(如果有以前的版本,则应在文档的历史记录部分中列出)。如果以前版本的原始出版商给予许可,你可以使用与以前版本相同的标题。

  2. 在标题页上,列出对修改版本中的修改负责的一个或多个个人或实体作为作者,以及文档的至少五位主要作者(如果少于五位,则列出其所有主要作者)。

  3. 在标题页上声明修改版本的出版商的名称,作为出版商。

  4. 保留文档的所有版权声明。

  5. 在其他版权声明旁边添加适用于你的修改的版权声明。

  6. 在版权声明之后立即包含一个许可证声明,允许公众根据本许可证的条款使用修改版本,形式如下面的附录所示。

  7. 在该许可证声明中保留文档的许可证声明中给出的不变章节和要求的封面文本的完整列表。

  8. 包含本许可证的未更改副本。

  9. 保留标题为“历史记录”的部分及其标题,并在其中添加一个项目,至少说明标题页上给出的修改版本的标题、年份、新作者和出版商。如果文档中没有标题为“历史记录”的部分,则创建一个部分,说明标题页上给出的文档的标题、年份、作者和出版商,然后添加一个项目,描述上一句中描述的修改版本。

  10. 保留文档中给出的用于公众访问文档透明副本的网络位置(如果有),以及文档中给出的用于其所基于的先前版本的网络位置。这些可以放在“历史记录”部分中。你可以省略在文档本身至少四年前发布的作品的网络位置,或者如果它所指的版本的原始出版商给予许可。

  11. 在任何标题为“致谢”或“献词”的部分中,保留该部分的标题,并在该部分中保留其中给出的每个贡献者致谢和/或献词的全部实质和语气。

  12. 保留文档的所有不变章节,其文本和标题均未更改。章节编号或等效编号不被视为章节标题的一部分。

  13. 删除任何标题为“认可”的部分。此类部分不得包含在修改版本中。

  14. 不要将任何现有部分重新命名为“认可”,或在标题上与任何不变章节冲突。

如果修改版本包含符合次要章节条件的新前言部分或附录,并且不包含从文档复制的材料,你可以选择将其中一些或全部部分指定为不变。为此,请将其标题添加到修改版本的许可证声明中的不变章节列表中。这些标题必须与任何其他章节标题不同。

你可以添加一个标题为“认可”的部分,前提是它只包含各方对你的修改版本的认可,例如,同行评审声明或文本已被组织批准为标准的权威定义。

你可以在修改版本的封面文本列表末尾添加最多五个单词的段落作为封面文本,以及最多 25 个单词的段落作为封底文本。任何一个实体(或通过其安排)只能添加一个封面文本段落和一个封底文本段落。如果文档已包含同一封面的封面文本,该文本先前由你添加或由你代表的同一实体安排添加,则你不得添加另一个;但你可以替换旧的,前提是获得添加旧的文本的先前出版商的明确许可。

文档的作者和出版商不通过本许可证授予使用其姓名进行宣传或声明或暗示认可任何修改版本的许可。


5. 合并文档

你可以将文档与根据本许可证发布的其他文档合并,根据上述第 4 节中为修改版本定义的条款,前提是你将所有原始文档的所有不变章节都包含在组合中,并且未修改,并在其许可证声明中将它们全部列为组合作品的不变章节。

组合作品只需要包含本许可证的一个副本,并且多个相同的不变章节可以用单个副本替换。如果有多个具有相同名称但内容不同的不变章节,请通过在其末尾添加括号中的该章节的原始作者或出版商的姓名(如果已知),或添加唯一的编号,使每个此类章节的标题唯一。在组合作品的许可证声明中的不变章节列表中对章节标题进行相同的调整。

在组合中,你必须合并各个原始文档中标题为“历史记录”的任何章节,形成一个标题为“历史记录”的章节;同样合并任何标题为“致谢”的章节,以及任何标题为“献词”的章节。你必须删除所有标题为“认可”的章节。


6. 文档集合

你可以创建一个集合,其中包含文档和根据本许可证发布的其他文档,并将各个文档中的本许可证副本替换为包含在集合中的单个副本,前提是你在所有其他方面都遵循本许可证关于每个文档的逐字复制的规则。

你可以从这样的集合中提取单个文档,并在本许可证下单独分发它,前提是你将本许可证的副本插入到提取的文档中,并在所有其他方面都遵循本许可证关于该文档的逐字复制的规定。


7. 与独立作品的聚合

将文档或其衍生作品与其他单独且独立的文档或作品汇编在存储或分发介质的卷中或卷上,作为一个整体不计为文档的修改版本,前提是未对汇编声明汇编版权。此类汇编称为“聚合”,并且本许可证不适用于因此与文档汇编在一起的其他独立作品,因为它们因此被汇编在一起,如果它们本身不是文档的衍生作品。

如果第 3 节的封面文本要求适用于文档的这些副本,那么如果文档小于整个聚合的四分之一,则文档的封面文本可以放在仅围绕聚合中文档的封面上。否则,它们必须出现在围绕整个聚合的封面上。


8. 翻译

翻译被认为是修改的一种,因此你可以根据第 4 节的条款分发文档的翻译。用翻译替换不变章节需要获得其版权所有者的特殊许可,但你可以包含一些或所有不变章节的翻译,以及这些不变章节的原始版本。你可以包含本许可证的翻译,前提是你也包含本许可证的原始英文版本。如果翻译版本与本许可证的原始英文版本之间存在分歧,则以原始英文版本为准。


9. 终止

除非本许可证明确规定,否则你不得复制、修改、再许可或分发文档。任何其他尝试复制、修改、再许可或分发文档的行为均无效,并将自动终止你在本许可证下的权利。但是,根据本许可证从你处收到副本或权利的各方,只要这些各方保持完全合规,其许可证就不会终止。


10. 本许可证的未来修订

自由软件基金会可能会不时发布 GNU 自由文档许可证的新修订版本。此类新版本在精神上与当前版本相似,但在细节上可能有所不同,以解决新问题或疑虑。请参阅 https://gnu.ac.cn/copyleft/

许可证的每个版本都给出了一个区分版本号。如果文档指定本许可证的特定编号版本“或任何更高版本”适用于它,你可以选择遵循该指定版本或自由软件基金会已发布(未作为草案)的任何更高版本的条款和条件。如果文档未指定本许可证的版本号,你可以选择自由软件基金会曾经发布(未作为草案)的任何版本。


11. 如何在你的文档中使用本许可证

要在你编写的文档中使用本许可证,请在文档中包含本许可证的副本,并将以下版权和许可证声明放在标题页之后

版权所有 (c) 年份 你的姓名。根据 GNU 自由文档许可证,版本 1.1 或自由软件基金会发布的任何更高版本的条款,授予复制、分发和/或修改本文档的许可;不变章节为 列出其标题,封面文本为 列出,封底文本为 列出。许可证的副本包含在标题为“GNU 自由文档许可证”的部分中。

如果你没有不变章节,请写“没有不变章节”,而不是说哪些是不变的。如果你没有封面文本,请写“没有封面文本”,而不是“封面文本为 列出”;封底文本也一样。

如果你的文档包含程序代码的非平凡示例,我们建议根据你选择的自由软件许可证(例如 GNU 通用公共许可证)并行发布这些示例,以允许在自由软件中使用它们。

注释

[1]

我强调复制是因为 mkisofs 会篡改从中制作 ISO 映像的目录中的文件。