第 10 章。数据管理

目录

10.1. 共享、复制和归档
10.1.1. 归档和压缩工具
10.1.2. 复制和同步工具
10.1.3. 归档的惯用法
10.1.4. 复制的惯用法
10.1.5. 文件选择的惯用法
10.1.6. 备份和恢复
10.1.7. 备份实用程序套件
10.1.8. 系统备份的示例脚本
10.1.9. 数据备份的复制脚本
10.1.10. 可移动存储设备
10.1.11. 用于共享数据的文件系统选择
10.1.12. 通过网络共享数据
10.1.13. 归档介质
10.2. 磁盘镜像
10.2.1. 制作磁盘镜像文件
10.2.2. 直接写入磁盘
10.2.3. 挂载磁盘镜像文件
10.2.4. 清理磁盘镜像文件
10.2.5. 制作空磁盘镜像文件
10.2.6. 制作 ISO9660 镜像文件
10.2.7. 直接写入 CD/DVD-R/RW
10.2.8. 挂载 ISO9660 镜像文件
10.3. 二进制数据
10.3.1. 查看和编辑二进制数据
10.3.2. 在不挂载磁盘的情况下操作文件
10.3.3. 数据冗余
10.3.4. 数据文件恢复和取证分析
10.3.5. 将大文件拆分成小文件
10.3.6. 清空文件内容
10.3.7. 虚拟文件
10.3.8. 擦除整个硬盘
10.3.9. 擦除硬盘的未使用区域
10.3.10. 恢复删除但仍打开的文件
10.3.11. 搜索所有硬链接
10.3.12. 隐形磁盘空间消耗
10.4. 数据安全基础设施
10.4.1. GnuPG 的密钥管理
10.4.2. 在文件上使用 GnuPG
10.4.3. 将 GnuPG 与 Mutt 一起使用
10.4.4. 将 GnuPG 与 Vim 一起使用
10.4.5. MD5 校验和
10.5. 源代码合并工具
10.5.1. 提取源文件的差异
10.5.2. 合并源文件的更新
10.5.3. 通过三向合并更新
10.6. 版本控制系统
10.6.1. VCS 命令的比较
10.7. CVS
10.7.1. CVS 仓库的配置
10.7.2. 本地访问 CVS
10.7.3. 使用 pserver 远程访问 CVS
10.7.4. 使用 ssh 远程访问 CVS
10.7.5. 将新源代码导入 CVS
10.7.6. CVS 仓库中的文件权限
10.7.7. CVS 的工作流程
10.7.8. 来自 CVS 的最新文件
10.7.9. CVS 的管理
10.7.10. CVS 检出的执行位
10.8. Subversion
10.8.1. Subversion 仓库的配置
10.8.2. 通过 Apache2 服务器访问 Subversion
10.8.3. 通过组本地访问 Subversion
10.8.4. 通过 SSH 远程访问 Subversion
10.8.5. Subversion 目录结构
10.8.6. 将新源代码导入 Subversion
10.8.7. Subversion 的工作流程
10.9. Git
10.9.1. Git 客户端的配置
10.9.2. Git 引用
10.9.3. Git 命令
10.9.4. 用于 Subversion 仓库的 Git
10.9.5. 用于记录配置历史的 Git

描述了在 Debian 系统上管理二进制和文本数据的工具和技巧。

[Warning] 警告

不得对正在被多个进程访问的设备和文件进行非协调的写入访问,以避免竞争条件文件锁定机制可以使用 flock(1) 来避免这种情况。

10.1. 共享、复制和归档

数据安全及其受控共享具有多个方面。

  • 数据归档的创建

  • 远程存储访问

  • 复制

  • 修改历史的跟踪

  • 促进数据共享

  • 防止未经授权的文件访问

  • 检测未经授权的文件修改

这些可以通过使用一些工具组合来实现。

  • 归档和压缩工具

  • 复制和同步工具

  • 网络文件系统

  • 可移动存储介质

  • 安全 shell

  • 身份验证系统

  • 版本控制系统工具

  • 哈希和加密工具

10.1.1. 归档和压缩工具

以下是在 Debian 系统上可用的归档和压缩工具的摘要。

表 10.1. 归档和压缩工具列表

软件包 popcon 大小 命令 扩展名 注释
tar http://qa.debian.org/popcon.php?package=tar 2464 tar(1) .tar 标准归档程序(事实标准)
cpio http://qa.debian.org/popcon.php?package=cpio 920 cpio(1) .cpio Unix System V 风格归档程序,与 find(1) 一起使用
binutils http://qa.debian.org/popcon.php?package=binutils 14823 ar(1) .ar 用于创建静态库的归档程序
fastjar http://qa.debian.org/popcon.php?package=fastjar 216 fastjar(1) .jar Java 归档程序(类似 zip)
pax http://qa.debian.org/popcon.php?package=pax 178 pax(1) .pax 新的 POSIX 标准归档程序,tarcpio 之间的折衷
gzip http://qa.debian.org/popcon.php?package=gzip 179 gzip(1), zcat(1), … .gz GNU LZ77 压缩实用程序(事实标准)
bzip2 http://qa.debian.org/popcon.php?package=bzip2 86 bzip2(1), bzcat(1), … .bz2 Burrows-Wheeler 块排序压缩实用程序,比 gzip(1) 具有更高的压缩率(比 gzip 慢,语法相似)
lzma http://qa.debian.org/popcon.php?package=lzma 144 lzma(1) .lzma LZMA 压缩实用程序,比 gzip(1) 具有更高的压缩率(已弃用)
xz-utils http://qa.debian.org/popcon.php?package=xz-utils 383 xz(1), xzdec(1), … .xz XZ 压缩实用程序,比 bzip2(1) 具有更高的压缩率(比 gzip 慢,但比 bzip2 快;替代 LZMA 压缩实用程序)
p7zip http://qa.debian.org/popcon.php?package=p7zip 986 7zr(1), p7zip(1) .7z 7-Zip 文件归档程序,具有高压缩率(LZMA 压缩)
p7zip-full http://qa.debian.org/popcon.php?package=p7zip-full 3895 7z(1), 7za(1) .7z 7-Zip 文件归档程序,具有高压缩率(LZMA 压缩等)
lzop http://qa.debian.org/popcon.php?package=lzop 112 lzop(1) .lzo LZO 压缩实用程序,比 gzip(1) 具有更高的压缩和解压缩速度(压缩率低于 gzip,语法相似)
zip http://qa.debian.org/popcon.php?package=zip 636 zip(1) .zip InfoZIP:DOS 归档和压缩工具
unzip http://qa.debian.org/popcon.php?package=unzip 377 unzip(1) .zip InfoZIP:DOS 解归档和解压缩工具

[Warning] 警告

除非您知道会发生什么,否则不要设置“$TAPE”变量。它会更改 tar(1) 的行为。

[Note] 注意

gzip 压缩的 tar(1) 归档文件使用文件扩展名“.tgz”或“.tar.gz”。

[Note] 注意

xz 压缩的 tar(1) 归档文件使用文件扩展名“.txz”或“.tar.xz”。

[Note] 注意

FOSS 工具(例如 tar(1))中流行的压缩方法一直在如下发展:gzipbzip2xz

[Note] 注意

cp(1)scp(1)tar(1) 对于特殊文件可能有一些限制。cpio(1) 是最通用的。

[Note] 注意

cpio(1) 设计为与 find(1) 和其他命令一起使用,并且适用于创建备份脚本,因为脚本的文件选择部分可以独立测试。

[Note] 注意

OpenOffice 数据文件的内部结构是“.jar”文件。

10.1.2. 复制和同步工具

以下是在 Debian 系统上可用的简单复制和备份工具的摘要。

表 10.2. 复制和同步工具列表

软件包 popcon 大小 工具 功能
coreutils http://qa.debian.org/popcon.php?package=coreutils 14088 GNU cp 本地复制文件和目录(“-a”表示递归)
openssh-client http://qa.debian.org/popcon.php?package=openssh-client 2246 scp 远程复制文件和目录(客户端,“-r”表示递归)
openssh-server http://qa.debian.org/popcon.php?package=openssh-server 701 sshd 远程复制文件和目录(远程服务器)
rsync http://qa.debian.org/popcon.php?package=rsync 724 - 单向远程同步和备份
unison http://qa.debian.org/popcon.php?package=unison 1987 - 双向远程同步和备份

使用 rsync(8) 复制文件比其他工具提供更丰富的功能。

  • 增量传输算法,仅发送源文件和目标位置现有文件之间的差异

  • 快速检查算法(默认),查找大小或上次修改时间已更改的文件

  • 类似于 tar(1) 的“--exclude”和“--exclude-from”选项

  • “源目录上的尾部斜杠”语法,避免在目标位置创建额外的目录级别。

[Tip] 提示

cron(8) 下使用“-gl”选项执行 第 10.1.9 节“数据备份的复制脚本”中提到的 bkup 脚本,应提供与 Plan9 的 dumpfs 非常相似的静态数据归档功能。

[Tip] 提示

表 10.15 “版本控制系统工具列表”中的版本控制系统 (VCS) 工具可以用作多向复制和同步工具。

10.1.3. 归档的惯用法

以下是使用不同工具归档和解归档目录“./source”的整个内容的几种方法。

GNU tar(1)

$ tar cvzf archive.tar.gz ./source
$ tar xvzf archive.tar.gz

cpio(1):

$ find ./source -xdev -print0 | cpio -ov --null > archive.cpio; gzip archive.cpio
$ zcat archive.cpio.gz | cpio -i

10.1.4. 复制的惯用法

以下是使用不同工具复制目录“./source”的整个内容的几种方法。

  • 本地复制:“./source”目录 → “/dest”目录

  • 远程复制:本地主机上的“./source”目录 → “user@host.dom”主机上的 “/dest”目录

rsync(8):

# cd ./source; rsync -av . /dest
# cd ./source; rsync -av . user@host.dom:/dest

您可以选择使用“源目录上的尾部斜杠”语法。

# rsync -av ./source/ /dest
# rsync -av ./source/ user@host.dom:/dest

GNU cp(1) 和 openSSH scp(1)

# cd ./source; cp -a . /dest
# cd ./source; scp -pr . user@host.dom:/dest

GNU tar(1)

# (cd ./source && tar cf - . ) | (cd /dest && tar xvfp - )
# (cd ./source && tar cf - . ) | ssh user@host.dom '(cd /dest && tar xvfp - )'

cpio(1):

# cd ./source; find . -print0 | cpio -pvdm --null --sparse /dest

您可以将所有包含“.”的示例中的“.”替换为“foo”,以将文件从 “./source/foo”目录复制到 “/dest/foo”目录。

您可以将所有包含“.”的示例中的“.”替换为绝对路径 “/path/to/source/foo”,以删除 “cd ./source;”。这些会将文件复制到不同的位置,具体取决于使用的工具,如下所示。

  • /dest/foo”:rsync(8)、GNU cp(1)scp(1)

  • /path/to/source/foo”:GNU tar(1)cpio(1)

[Tip] 提示

rsync(8) 和 GNU cp(1) 具有 “-u” 选项,用于跳过接收器上较新的文件。

10.1.5. 文件选择的惯用法

find(1) 用于为归档和复制命令选择文件(请参阅第 10.1.3 节“归档的惯用法”第 10.1.4 节“复制的惯用法”)或用于 xargs(1)(请参阅第 9.5.9 节“重复命令循环处理文件”)。这可以通过使用其命令参数来增强。

find(1) 的基本语法可以概括如下。

  • 其条件参数从左到右求值。

  • 一旦确定其结果,此求值就会停止。

  • “逻辑”(由条件之间的“-o”指定)的优先级低于“逻辑”(由条件之间的“-a”或无任何内容指定)。

  • “逻辑”(由条件之前的“!”指定)的优先级高于“逻辑”。

  • -prune”始终返回逻辑,并且如果它是一个目录,则文件搜索将在此点之后停止。

  • -name”将文件名的基本名称与 shell glob 匹配(请参阅第 1.5.6 节“Shell glob”),但它也将其初始 “.” 与元字符(例如 “*” 和 “?”)匹配。(新的 POSIX 功能)

  • -regex”将完整路径与 emacs 风格的 BRE 匹配(默认情况下,请参阅第 1.6.2 节“正则表达式”)。

  • -size”根据文件大小匹配文件(对于较大的文件,值以 “+” 开头;对于较小的文件,值以 “-” 开头)

  • -newer”匹配比其参数中指定的文件更新的文件。

  • -print0”始终返回逻辑,并在标准输出上打印完整文件名(空字符结尾)。

find(1) 通常以以下惯用风格使用。

# find /path/to \
    -xdev -regextype posix-extended \
    -type f -regex ".*\.cpio|.*~" -prune -o \
    -type d -regex ".*/\.git" -prune -o \
    -type f -size +99M -prune -o \
    -type f -newer /path/to/timestamp -print0

这意味着执行以下操作。

  1. 从 “/path/to” 开始搜索所有文件

  2. 全局限制其搜索在其起始文件系统内,并使用 ERE (请参阅第 1.6.2 节“正则表达式”)代替

  3. 通过停止处理,从搜索中排除与 “.*\.cpio” 或 “.*~” 正则表达式匹配的文件

  4. 通过停止处理,从搜索中排除与 “.*/\.git” 正则表达式匹配的目录

  5. 通过停止处理,从搜索中排除大于 99 兆字节(单位为 1048576 字节)的文件

  6. 打印满足上述搜索条件且比 “/path/to/timestamp” 新的文件名

请注意在上面的示例中排除文件时惯用的使用 “-prune -o”。

[Note] 注意

对于非 Debian 类 Unix 系统,某些选项可能不受 find(1) 支持。在这种情况下,请考虑调整匹配方法,并将 “-print0” 替换为 “-print”。您可能还需要调整相关命令。

10.1.6. 备份和恢复

我们都知道计算机有时会发生故障,或者人为错误会导致系统和数据损坏。备份和恢复操作是成功系统管理的重要组成部分。所有可能的故障模式总有一天会发生在你身上。

[Tip] 提示

保持备份系统简单,并经常备份系统。拥有备份数据比你的备份方法在技术上有多好更重要。

有 3 个关键因素决定了实际的备份和恢复策略。

  1. 了解要备份和恢复的内容。

    • 您直接创建的数据文件: “~/” 中的数据

    • 您使用的应用程序创建的数据文件: “/var/” 中的数据(“/var/cache/”、“/var/run/” 和 “/var/tmp/” 除外)

    • 系统配置文件: “/etc/” 中的数据

    • 本地软件: “/usr/local/” 或 “/opt/” 中的数据

    • 系统安装信息:关于关键步骤(分区,…)的纯文本备忘录

    • 经过验证的数据集:通过预先的实验性恢复操作确认

  2. 了解如何备份和恢复。

    • 数据的安全存储:防止覆盖和系统故障

    • 频繁备份:计划备份

    • 冗余备份:数据镜像

    • 傻瓜式流程:简单的单命令备份

  3. 评估涉及的风险和成本。

    • 数据丢失时的价值

    • 备份所需的资源:人力、硬件、软件、…

    • 故障模式及其可能性

关于数据的安全存储,数据应至少位于不同的磁盘分区上,最好位于不同的磁盘和机器上,以承受文件系统损坏。重要数据最好存储在一次写入介质(如 CD/DVD-R)上,以防止意外覆盖。(有关如何从 shell 命令行写入存储介质,请参阅第 10.3 节“二进制数据”。GNOME 桌面 GUI 环境使您可以通过菜单轻松访问:“位置→CD/DVD 创建器”。)

[Note] 注意

您可能希望在备份数据时停止某些应用程序守护进程,例如 MTA(请参阅第 6.3 节“邮件传输代理 (MTA)”)。

[Note] 注意

您应该特别注意身份相关数据文件(例如 “/etc/ssh/ssh_host_dsa_key”、“/etc/ssh/ssh_host_rsa_key”、“~/.gnupg/*”、“~/.ssh/*”、“/etc/passwd”、“/etc/shadow”、“/etc/fetchmailrc”、“popularity-contest.conf”、“/etc/ppp/pap-secrets” 和 “/etc/exim4/passwd.client”)的备份和恢复。某些数据无法通过向系统输入相同的输入字符串来重新生成。

[Note] 注意

如果您以用户进程身份运行 cron 作业,则必须恢复 “/var/spool/cron/crontabs” 目录中的文件并重新启动 cron(8)。有关 cron(8)crontab(1),请参阅第 9.5.14 节“定期调度任务”

10.1.7. 备份实用程序套件

以下是 Debian 系统上可用的著名备份实用程序套件的选择列表。

表 10.3. 备份套件实用程序列表

软件包 popcon 大小 描述
rdiff-backup http://qa.debian.org/popcon.php?package=rdiff-backup 704 (远程)增量备份
dump http://qa.debian.org/popcon.php?package=dump 716 用于 ext2/ext3 文件系统的 4.4 BSD dump(8)restore(8)
xfsdump http://qa.debian.org/popcon.php?package=xfsdump 595 使用 xfsdump(8)xfsrestore(8) 在 GNU/Linux 和 IRIX 上为 XFS 文件系统进行转储和恢复
backupninja http://qa.debian.org/popcon.php?package=backupninja 276 轻量级、可扩展的元备份系统
sbackup http://qa.debian.org/popcon.php?package=sbackup 488 用于 GNOME 桌面的简单备份套件
bacula-common http://qa.debian.org/popcon.php?package=bacula-common 1083 Bacula:网络备份、恢复和验证 - 通用支持文件
bacula-client http://qa.debian.org/popcon.php?package=bacula-client 53 Bacula:网络备份、恢复和验证 - 客户端元软件包
bacula-console http://qa.debian.org/popcon.php?package=bacula-console 154 Bacula:网络备份、恢复和验证 - 文本控制台
bacula-server http://qa.debian.org/popcon.php?package=bacula-server 53 Bacula:网络备份、恢复和验证 - 服务器元软件包
amanda-common http://qa.debian.org/popcon.php?package=amanda-common 7013 Amanda:高级马里兰自动网络磁盘归档器 (Libs)
amanda-client http://qa.debian.org/popcon.php?package=amanda-client 789 Amanda:高级马里兰自动网络磁盘归档器 (Client)
amanda-server http://qa.debian.org/popcon.php?package=amanda-server 920 Amanda:高级马里兰自动网络磁盘归档器 (Server)
backuppc http://qa.debian.org/popcon.php?package=backuppc 1955 BackupPC 是一个高性能、企业级的 PC 备份系统(基于磁盘)
backup-manager http://qa.debian.org/popcon.php?package=backup-manager 615 命令行备份工具
backup2l http://qa.debian.org/popcon.php?package=backup2l 86 用于可挂载介质的低维护备份/恢复工具(基于磁盘)

备份工具各有其专注点。

  • Mondo Rescue 是一个备份系统,旨在促进从备份 CD/DVD 等快速恢复完整系统,而无需经过正常的系统安装过程。

  • sbackupkeep 软件包为桌面用户提供了简单的 GUI 前端,用于定期备份用户数据。可以通过一个简单的脚本(第 10.1.8 节“系统备份的示例脚本”)和 cron(8) 来实现等效功能。

  • BaculaAmandaBackupPC 是功能齐全的备份套件实用程序,专注于通过网络进行定期备份。

第 10.1.1 节“归档和压缩工具”第 10.1.2 节“复制和同步工具”中描述的基本工具可以用于通过自定义脚本来促进系统备份。可以通过以下方式增强此类脚本。

  • rdiff-backup 软件包启用(远程)增量备份。

  • dump 软件包有助于增量且高效地归档和恢复整个文件系统。

[Tip] 提示

请参阅 “/usr/share/doc/dump/” 中的文件和 “dump 真的被弃用了吗?” 以了解有关 dump 软件包的信息。

10.1.8. 系统备份的示例脚本

对于运行 unstable 套件的个人 Debian 桌面系统,我只需要保护个人和关键数据。我无论如何每年都会重新安装系统一次。因此,我认为没有理由备份整个系统或安装功能齐全的备份实用程序。

我使用一个简单的脚本来制作备份归档,并使用 GUI 将其刻录到 CD/DVD 中。这是一个示例脚本。

#!/bin/sh -e
# Copyright (C) 2007-2008 Osamu Aoki <osamu@debian.org>, Public Domain
BUUID=1000; USER=osamu # UID and name of a user who accesses backup files
BUDIR="/var/backups"
XDIR0=".+/Mail|.+/Desktop"
XDIR1=".+/\.thumbnails|.+/\.?Trash|.+/\.?[cC]ache|.+/\.gvfs|.+/sessions"
XDIR2=".+/CVS|.+/\.git|.+/\.svn|.+/Downloads|.+/Archive|.+/Checkout|.+/tmp"
XSFX=".+\.iso|.+\.tgz|.+\.tar\.gz|.+\.tar\.bz2|.+\.cpio|.+\.tmp|.+\.swp|.+~"
SIZE="+99M"
DATE=$(date --utc +"%Y%m%d-%H%M")
[ -d "$BUDIR" ] || mkdir -p "BUDIR"
umask 077
dpkg --get-selections \* > /var/lib/dpkg/dpkg-selections.list
debconf-get-selections > /var/cache/debconf/debconf-selections

{
find /etc /usr/local /opt /var/lib/dpkg/dpkg-selections.list \
     /var/cache/debconf/debconf-selections -xdev -print0
find /home/$USER /root -xdev -regextype posix-extended \
  -type d -regex "$XDIR0|$XDIR1" -prune -o -type f -regex "$XSFX" -prune -o \
  -type f -size  "$SIZE" -prune -o -print0
find /home/$USER/Mail/Inbox /home/$USER/Mail/Outbox -print0
find /home/$USER/Desktop  -xdev -regextype posix-extended \
  -type d -regex "$XDIR2" -prune -o -type f -regex "$XSFX" -prune -o \
  -type f -size  "$SIZE" -prune -o -print0
} | cpio -ov --null -O $BUDIR/BU$DATE.cpio
chown $BUUID $BUDIR/BU$DATE.cpio
touch $BUDIR/backup.stamp

这旨在成为从 root 执行的脚本示例。

我希望您按如下方式更改和执行此脚本。

保持简单!

[Tip] 提示

您可以使用“debconf-set-selections debconf-selections”恢复 debconf 配置数据,并使用“dpkg --set-selection <dpkg-selections.list”恢复 dpkg 选择数据。

10.1.9. 用于数据备份的复制脚本

对于目录树下的一组数据,使用“cp -a”进行复制即可提供常规备份。

对于目录树下的大型非覆盖静态数据集合,例如“/var/cache/apt/packages/”目录下的数据,使用“cp -al”进行硬链接复制可以替代常规备份,并有效利用磁盘空间。

这是一个用于数据备份的复制脚本,我将其命名为 bkup。此脚本将当前目录下的所有(非 VCS)文件复制到父目录或远程主机上的日期目录中。

#!/bin/sh -e
# Copyright (C) 2007-2008 Osamu Aoki <osamu@debian.org>, Public Domain
fdot(){ find . -type d \( -iname ".?*" -o -iname "CVS" \) -prune -o -print0;}
fall(){ find . -print0;}
mkdircd(){ mkdir -p "$1";chmod 700 "$1";cd "$1">/dev/null;}
FIND="fdot";OPT="-a";MODE="CPIOP";HOST="localhost";EXTP="$(hostname -f)"
BKUP="$(basename $(pwd)).bkup";TIME="$(date  +%Y%m%d-%H%M%S)";BU="$BKUP/$TIME"
while getopts gcCsStrlLaAxe:h:T f; do case $f in
g)  MODE="GNUCP";; # cp (GNU)
c)  MODE="CPIOP";; # cpio -p
C)  MODE="CPIOI";; # cpio -i
s)  MODE="CPIOSSH";; # cpio/ssh
t)  MODE="TARSSH";; # tar/ssh
r)  MODE="RSYNCSSH";; # rsync/ssh
l)  OPT="-alv";; # hardlink (GNU cp)
L)  OPT="-av";;  # copy (GNU cp)
a)  FIND="fall";; # find all
A)  FIND="fdot";; # find non CVS/ .???/
x)  set -x;; # trace
e)  EXTP="${OPTARG}";; # hostname -f
h)  HOST="${OPTARG}";; # user@remotehost.example.com
T)  MODE="TEST";; # test find mode
\?) echo "use -x for trace."
esac; done
shift $(expr $OPTIND - 1)
if [ $# -gt 0 ]; then
  for x in $@; do cp $OPT $x $x.$TIME; done
elif [ $MODE = GNUCP ]; then
  mkdir -p "../$BU";chmod 700 "../$BU";cp $OPT . "../$BU/"
elif [ $MODE = CPIOP ]; then
  mkdir -p "../$BU";chmod 700 "../$BU"
  $FIND|cpio --null --sparse -pvd ../$BU
elif [ $MODE = CPIOI ]; then
  $FIND|cpio -ov --null | ( mkdircd "../$BU"&&cpio -i )
elif [ $MODE = CPIOSSH ]; then
  $FIND|cpio -ov --null|ssh -C $HOST "( mkdircd \"$EXTP/$BU\"&&cpio -i )"
elif [ $MODE = TARSSH ]; then
  (tar cvf - . )|ssh -C $HOST "( mkdircd \"$EXTP/$BU\"&& tar xvfp - )"
elif [ $MODE = RSYNCSSH ]; then
  rsync -rlpt ./ "${HOST}:${EXTP}-${BKUP}-${TIME}"
else
  echo "Any other idea to backup?"
  $FIND |xargs -0 -n 1 echo
fi

这仅为命令示例。请在使用前阅读并自行编辑脚本。

[Tip] 提示

我将此 bkup 脚本保存在我的“/usr/local/bin/”目录中。每当我需要临时快照备份时,我都会在工作目录中不带任何选项地发出此 bkup 命令。

[Tip] 提示

为了制作源文件树或配置文件树的快照历史记录,使用 git(7) (参见 第 10.9.5 节,“使用 Git 记录配置历史”) 更容易且节省空间。

10.1.10. 可移动存储设备

可移动存储设备可以是以下任何一种。

它们可以通过以下任何一种方式连接。

现代桌面环境(如 GNOME 和 KDE)可以自动挂载这些可移动设备,而无需匹配的“/etc/fstab”条目。

  • udisks 软件包提供了一个守护进程和相关实用程序来挂载和卸载这些设备。

  • D-bus 创建事件以启动自动进程。

  • PolicyKit 提供所需的权限。

[Tip] 提示

自动挂载的设备可能具有 “uhelper=” 挂载选项,该选项由 umount(8) 使用。

[Tip] 提示

仅当这些可移动媒体设备未在“/etc/fstab”中列出时,现代桌面环境下的自动挂载才会发生。

现代桌面环境下的挂载点被选择为 “/media/<disk_label>”,可以通过以下方式自定义。

  • mlabel(1) 用于 FAT 文件系统

  • genisoimage(1) 带有 “-V” 选项用于 ISO9660 文件系统

  • tune2fs(1) 带有 “-L” 选项用于 ext2/ext3/ext4 文件系统

[Tip] 提示

编码的选择可能需要作为挂载选项提供(参见 第 8.3.6 节,“文件名编码”)。

10.1.11. 用于共享数据的文件系统选择

当通过可移动存储设备与其他系统共享数据时,您应该使用通用 文件系统,该文件系统应被两个系统都支持。以下是文件系统选择列表。

表 10.4. 可移动存储设备的文件系统选择列表及典型使用场景

文件系统 典型使用场景描述
FAT12 软盘上的数据跨平台共享(<32MiB)
FAT16 小型硬盘类设备上的数据跨平台共享(<2GiB)
FAT32 大型硬盘类设备上的数据跨平台共享(<8TiB,由 MS Windows95 OSR2 及更高版本支持)
NTFS 大型硬盘类设备上的数据跨平台共享(在 MS Windows NT 及更高版本上原生支持,并通过 NTFS-3G 在 Linux 上通过 FUSE 支持)
ISO9660 CD-R 和 DVD+/-R 上的静态数据跨平台共享
UDF CD-R 和 DVD+/-R 上的增量数据写入(新)
MINIX 文件系统 软盘上节省空间的 Unix 文件数据存储
ext2 文件系统 与较旧的 Linux 系统共享硬盘类设备上的数据
ext3 文件系统 与较旧的 Linux 系统共享硬盘类设备上的数据
ext4 文件系统 与当前的 Linux 系统共享硬盘类设备上的数据

[Tip] 提示

有关使用设备级加密进行跨平台数据共享的信息,请参见 第 9.4.1 节,“使用 dm-crypt/LUKS 进行可移动磁盘加密”

FAT 文件系统几乎被所有现代操作系统支持,并且对于通过可移动硬盘类介质进行数据交换非常有用。

当为跨平台数据共享使用 FAT 文件系统格式化可移动硬盘类设备时,以下应该是安全的选择。

  • 使用 fdisk(8)cfdisk(8)parted(8) (参见 第 9.3.2 节,“磁盘分区配置”) 将它们分区为单个主分区,并将其标记为以下类型。

    • 对于小于 2GB 的介质,类型为 “6” 表示 FAT16。

    • 对于更大的介质,类型为 “c” 表示 FAT32 (LBA)。

  • 使用 mkfs.vfat(8) 格式化主分区,使用以下命令。

    • 仅使用其设备名称,例如 “/dev/sda1” 用于 FAT16

    • 显式选项及其设备名称,例如 “-F 32 /dev/sda1” 用于 FAT32

当使用 FAT 或 ISO9660 文件系统共享数据时,以下应该是安全考虑事项。

  • 首先使用 tar(1)cpio(1) 将文件存档到存档文件中,以保留长文件名、符号链接、原始 Unix 文件权限和所有者信息。

  • 使用 split(1) 命令将存档文件拆分为小于 2 GiB 的块,以防止文件大小限制。

  • 加密存档文件以保护其内容免受未经授权的访问。

[Note] 注意

根据 FAT 文件系统的设计,最大文件大小为 (2^32 - 1) bytes = (4GiB - 1 byte)。对于某些较旧的 32 位操作系统上的应用程序,最大文件大小甚至更小 (2^31 - 1) bytes = (2GiB - 1 byte)。Debian 没有后一个问题。

[Note] 注意

Microsoft 本身不建议对超过 200 MB 的驱动器或分区使用 FAT。Microsoft 在其 “FAT、HPFS 和 NTFS 文件系统概述” 中强调了其缺点,例如磁盘空间利用率低。当然,对于 Linux,我们通常应该使用 ext4 文件系统。

[Tip] 提示

有关文件系统和访问文件系统的更多信息,请阅读 “文件系统 HOWTO”。

10.1.12. 通过网络共享数据

当通过网络与其他系统共享数据时,您应该使用通用服务。以下是一些提示。

表 10.5. 网络服务选择列表及典型使用场景

网络服务 典型使用场景描述
SMB/CIFS 网络挂载文件系统,使用 Samba 通过 “Microsoft Windows 网络” 共享文件,参见 smb.conf(5)The Official Samba 3.2.x HOWTO and Reference Guidesamba-doc 软件包
NFS 网络挂载文件系统,使用 Linux 内核 通过 “Unix/Linux 网络” 共享文件,参见 exports(5)Linux NFS-HOWTO
HTTP 服务 在 Web 服务器/客户端之间共享文件
HTTPS 服务 在 Web 服务器/客户端之间共享文件,使用加密的安全套接字层 (SSL) 或 传输层安全 (TLS)
FTP 服务 在 FTP 服务器/客户端之间共享文件

虽然这些通过网络挂载的文件系统和通过网络的文件传输方法对于共享数据非常方便,但它们可能不安全。它们的网络连接必须通过以下方式保护。

  • 使用 SSL/TLS 加密

  • 通过 SSH 隧道化

  • 通过 VPN 隧道化

  • 限制在安全防火墙之后

另请参见 第 6.10 节,“其他网络应用程序服务器”第 6.11 节,“其他网络应用程序客户端”

10.1.13. 存档介质

当为重要数据存档选择 计算机数据存储介质 时,您应该注意它们的局限性。对于小型个人数据备份,我使用品牌公司的 CD-R 和 DVD-R,并将它们存储在阴凉、干燥、清洁的环境中。(磁带存档介质似乎在专业用途中很受欢迎。)

[Note] 注意

防火保险箱 是为纸质文档设计的。大多数计算机数据存储介质的耐温性都低于纸张。我通常依赖于存储在多个安全位置的多个安全加密副本。

在网络上看到的存档介质的乐观存储寿命(主要来自供应商信息)。

  • 100+ 年:无酸纸和墨水

  • 100 年:光存储(CD/DVD、CD/DVD-R)

  • 30 年:磁存储(磁带、软盘)

  • 20 年:相变光存储(CD-RW)

这些不包括因处理等引起的机械故障。

在网络上看到的存档介质的乐观写入周期(主要来自供应商信息)。

  • 250,000+ 次循环:硬盘驱动器

  • 10,000+ 次循环:闪存

  • 1,000 次循环:CD/DVD-RW

  • 1 次循环:CD/DVD-R、纸张

[Caution] 注意

此处的存储寿命和写入周期数据不应用于任何关键数据存储的决策。请查阅制造商提供的具体产品信息。

[Tip] 提示

由于 CD/DVD-R 和纸张只有 1 次写入周期,因此它们本质上可以防止因覆盖而意外丢失数据。这是一个优点!

[Tip] 提示

如果您需要快速且频繁地备份大量数据,则连接快速网络的远程主机上的硬盘可能是唯一现实的选择。

10.2. 磁盘镜像

在这里,我们讨论磁盘镜像的操作。也请参见 第 9.3 节,“数据存储技巧”

10.2.1. 制作磁盘镜像文件

可以使用 cp(1)dd(1) 通过以下方式制作未挂载设备的磁盘镜像文件 “disk.img”,例如,第二个 SCSI 驱动器 “/dev/sdb”。

# cp /dev/sdb disk.img
# dd if=/dev/sdb of=disk.img

传统 PC 的 主引导记录 (MBR) 的磁盘镜像 (参见 第 9.3.2 节,“磁盘分区配置”) 位于主 IDE 磁盘的第一个扇区,可以使用 dd(1) 通过以下方式制作。

# dd if=/dev/hda of=mbr.img bs=512 count=1
# dd if=/dev/hda of=mbr-nopart.img bs=446 count=1
# dd if=/dev/hda of=mbr-part.img skip=446 bs=1 count=66
  • mbr.img”:带有分区表 MBR

  • mbr-nopart.img”:不带分区表的 MBR

  • part.img”:仅 MBR 的分区表

如果您的引导磁盘是 SCSI 设备(包括新的串行 ATA 驱动器),请将 “/dev/hda” 替换为 “/dev/sda”。

如果您正在制作原始磁盘的磁盘分区的镜像,请将 “/dev/hda” 替换为 “/dev/hda1” 等。

10.2.2. 直接写入磁盘

磁盘镜像文件 “disk.img” 可以写入到大小匹配的未挂载设备,例如,第二个 SCSI 驱动器 “/dev/sdb”,通过以下方式。

# dd if=disk.img of=/dev/sdb

类似地,磁盘分区镜像文件 “partition.img” 可以写入到大小匹配的未挂载分区,例如,第二个 SCSI 驱动器的第一个分区 “/dev/sdb1”,通过以下方式。

# dd if=partition.img of=/dev/sdb1

10.2.3. 挂载磁盘镜像文件

可以使用 loop 设备 挂载和卸载包含单个分区镜像的磁盘镜像 “partition.img”,如下所示。

# losetup -v -f partition.img
Loop device is /dev/loop0
# mkdir -p /mnt/loop0
# mount -t auto /dev/loop0 /mnt/loop0
...hack...hack...hack
# umount /dev/loop0
# losetup -d /dev/loop0

可以简化为如下所示。

# mkdir -p /mnt/loop0
# mount -t auto -o loop partition.img /mnt/loop0
...hack...hack...hack
# umount partition.img

可以使用 loop 设备 挂载包含多个分区的磁盘镜像 “disk.img” 的每个分区。由于 loop 设备默认情况下不管理分区,我们需要如下所示重置它。

# modinfo -p loop # verify kernel capability
max_part:Maximum number of partitions per loop device
max_loop:Maximum number of loop devices
# losetup -a # verify nothing using the loop device
# rmmod loop
# modprobe loop max_part=16

现在,loop 设备可以管理最多 16 个分区。

# losetup -v -f disk.img
Loop device is /dev/loop0
# fdisk -l /dev/loop0

Disk /dev/loop0: 5368 MB, 5368709120 bytes
255 heads, 63 sectors/track, 652 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x452b6464

      Device Boot      Start         End      Blocks   Id  System
/dev/loop0p1               1         600     4819468+  83  Linux
/dev/loop0p2             601         652      417690   83  Linux
# mkdir -p /mnt/loop0p1
# mount -t ext3 /dev/loop0p1 /mnt/loop0p1
# mkdir -p /mnt/loop0p2
# mount -t ext3 /dev/loop0p2 /mnt/loop0p2
...hack...hack...hack
# umount /dev/loop0p1
# umount /dev/loop0p2
# losetup -d /dev/loop0

或者,可以使用由 kpartx 软件包中的 kpartx(8) 创建的 设备映射器 设备来实现类似的效果,如下所示。

# kpartx -a -v disk.img
...
# mkdir -p /mnt/loop0p2
# mount -t ext3 /dev/mapper/loop0p2 /mnt/loop0p2
...
...hack...hack...hack
# umount /dev/mapper/loop0p2
...
# kpartx -d /mnt/loop0
[Note] 注意

您也可以使用偏移量跳过 MBR 等,使用 loop 设备 挂载此类磁盘镜像的单个分区。但这更容易出错。

10.2.4. 清理磁盘镜像文件

可以通过以下方式将磁盘镜像文件 “disk.img” 中所有已删除的文件清理为干净的稀疏镜像 “new.img”。

# mkdir old; mkdir new
# mount -t auto -o loop disk.img old
# dd bs=1 count=0 if=/dev/zero of=new.img seek=5G
# mount -t auto -o loop new.img new
# cd old
# cp -a --sparse=always ./ ../new/
# cd ..
# umount new.img
# umount disk.img

如果 “disk.img” 是 ext2 或 ext3 格式,您也可以使用 zerofree 软件包中的 zerofree(8),如下所示。

# losetup -f -v disk.img
Loop device is /dev/loop3
# zerofree /dev/loop3
# cp --sparse=always disk.img new.img

10.2.5. 制作空磁盘镜像文件

可以使用 dd(1) 制作可以增长到 5GiB 的空磁盘镜像 “disk.img”,如下所示。

$ dd bs=1 count=0 if=/dev/zero of=disk.img seek=5G

您可以使用 loop 设备 在此磁盘镜像 “disk.img” 上创建 ext3 文件系统,如下所示。

# losetup -f -v disk.img
Loop device is /dev/loop1
# mkfs.ext3 /dev/loop1
...hack...hack...hack
# losetup -d /dev/loop1
$ du  --apparent-size -h disk.img
5.0G  disk.img
$ du -h disk.img
83M disk.img

对于 “disk.img”,其文件大小为 5.0 GiB,而实际磁盘使用量仅为 83MiB。这种差异是可能的,因为 ext2fs 可以容纳 稀疏文件

[Tip] 提示

稀疏文件 的实际磁盘使用量会随着写入其中的数据而增长。

使用与 第 10.2.3 节,“挂载磁盘镜像文件” 中相同的操作,在由 loop 设备设备映射器 设备创建的设备上,您可以使用 parted(8)fdisk(8) 对此磁盘镜像 “disk.img” 进行分区,并可以使用 mkfs.ext3(8)mkswap(8) 等在上面创建文件系统。

10.2.6. 制作 ISO9660 镜像文件

可以使用 cdrkit 提供的 genisoimage(1) 从 “source_directory” 的源目录树制作 ISO9660 镜像文件 “cd.iso”,如下所示。

#  genisoimage -r -J -T -V volume_id -o cd.iso source_directory

类似地,可以使用以下命令从 “source_directory” 中类似 debian-installer 的目录树制作可引导的 ISO9660 镜像文件 “cdboot.iso”。

#  genisoimage -r -o cdboot.iso -V volume_id \
   -b isolinux/isolinux.bin -c isolinux/boot.cat \
   -no-emul-boot -boot-load-size 4 -boot-info-table source_directory

这里使用 Isolinux 引导加载程序(参见 第 3.3 节,“阶段 2:引导加载程序”)进行引导。

您可以计算 md5sum 值并直接从 CD-ROM 设备制作 ISO9660 镜像,如下所示。

$ isoinfo -d -i /dev/cdrom
CD-ROM is in ISO 9660 format
...
Logical block size is: 2048
Volume size is: 23150592
...
# dd if=/dev/cdrom bs=2048 count=23150592 conv=notrunc,noerror | md5sum
# dd if=/dev/cdrom bs=2048 count=23150592 conv=notrunc,noerror > cd.iso
[Warning] 警告

您必须小心避免 Linux 的 ISO9660 文件系统预读错误,如上所述,以获得正确的结果。

10.2.7. 直接写入 CD/DVD-R/RW

[Tip] 提示

对于 cdrkit 提供的 wodim(1) 来说,DVD 只是一个更大的 CD。

您可以通过以下方式找到可用的设备。

# wodim --devices

然后将空白 CD-R 插入 CD 驱动器,并使用 wodim(1) 将 ISO9660 镜像文件 “cd.iso” 写入此设备,例如 “/dev/hda”,如下所示。

# wodim -v -eject dev=/dev/hda cd.iso

如果使用 CD-RW 而不是 CD-R,请改为执行以下操作。

# wodim -v -eject blank=fast dev=/dev/hda cd.iso
[Tip] 提示

如果您的桌面系统自动挂载 CD,请在使用 wodim(1) 之前使用 “sudo unmount /dev/hda” 卸载它。

10.2.8. 挂载 ISO9660 镜像文件

如果 “cd.iso” 包含 ISO9660 镜像,则以下命令手动将其挂载到 “/cdrom”。

# mount -t iso9660 -o ro,loop cd.iso /cdrom
[Tip] 提示

现代桌面系统会自动挂载可移动媒体(参见 第 10.1.10 节,“可移动存储设备”)。

10.3. 二进制数据

在这里,我们讨论直接操作存储介质上的二进制数据。也请参见 第 9.3 节,“数据存储技巧”

10.3.1. 查看和编辑二进制数据

最基本的二进制数据查看方法是使用 “od -t x1” 命令。

表 10.6. 查看和编辑二进制数据的软件包列表

软件包 popcon 大小 描述
coreutils http://qa.debian.org/popcon.php?package=coreutils 14088 基本软件包,具有 od(1) 来转储文件(HEX、ASCII、OCTAL、…)
bsdmainutils http://qa.debian.org/popcon.php?package=bsdmainutils 558 实用程序软件包,具有 hd(1) 来转储文件(HEX、ASCII、OCTAL、…)
hexedit http://qa.debian.org/popcon.php?package=hexedit 108 二进制编辑器和查看器(HEX、ASCII)
bless http://qa.debian.org/popcon.php?package=bless 991 功能齐全的十六进制编辑器 (GNOME)
okteta http://qa.debian.org/popcon.php?package=okteta 295 功能齐全的十六进制编辑器 (KDE4)
ncurses-hexedit http://qa.debian.org/popcon.php?package=ncurses-hexedit 192 二进制编辑器和查看器(HEX、ASCII、EBCDIC)
beav http://qa.debian.org/popcon.php?package=beav 164 二进制编辑器和查看器(HEX、ASCII、EBCDIC、OCTAL、…)

[Tip] 提示

HEX 用作 十六进制 格式的缩写,基数 为 16。OCTAL 用于 八进制 格式,基数 为 8。ASCII 用于 美国信息交换标准代码,即,普通英语文本代码。EBCDIC 用于 扩展二进制编码的十进制交换码,用于 IBM 大型机 操作系统。

10.3.2. 在不挂载磁盘的情况下操作文件

有一些工具可以在不挂载磁盘的情况下读取和写入文件。

表 10.7. 在不挂载磁盘的情况下操作文件的软件包列表

软件包 popcon 大小 描述
mtools http://qa.debian.org/popcon.php?package=mtools 408 用于 MSDOS 文件的实用程序,无需挂载它们
hfsutils http://qa.debian.org/popcon.php?package=hfsutils 236 用于 HFS 和 HFS+ 文件的实用程序,无需挂载它们

10.3.3. 数据冗余

Linux 内核提供的软件 RAID 系统在内核文件系统级别提供数据冗余,以实现高水平的存储可靠性。

也有一些工具可以在应用程序级别向文件添加数据冗余,以实现高水平的存储可靠性。

表 10.8. 向文件添加数据冗余的工具列表

软件包 popcon 大小 描述
par2 http://qa.debian.org/popcon.php?package=par2 272 奇偶校验存档卷集,用于检查和修复文件
dvdisaster http://qa.debian.org/popcon.php?package=dvdisaster 1481 CD/DVD 介质的数据丢失/划痕/老化保护
dvbackup http://qa.debian.org/popcon.php?package=dvbackup 392 使用 MiniDV 摄像机的备份工具(提供 rsbep(1)
vdmfec http://qa.debian.org/popcon.php?package=vdmfec 88 使用前向纠错恢复丢失的块

10.3.4. 数据文件恢复和取证分析

有一些用于数据文件恢复和取证分析的工具。

表 10.9. 数据文件恢复和取证分析的软件包列表

软件包 popcon 大小 描述
testdisk http://qa.debian.org/popcon.php?package=testdisk 1153 用于分区扫描和磁盘恢复的实用程序
magicrescue http://qa.debian.org/popcon.php?package=magicrescue 344 通过查找魔术字节来恢复文件的实用程序
scalpel http://qa.debian.org/popcon.php?package=scalpel 124 节俭、高性能的文件雕刻器
myrescue http://qa.debian.org/popcon.php?package=myrescue 84 从损坏的硬盘中恢复数据
recover http://qa.debian.org/popcon.php?package=recover 104 用于在 ext2 文件系统上取消删除文件的实用程序
e2undel http://qa.debian.org/popcon.php?package=e2undel 244 用于在 ext2 文件系统上取消删除文件的实用程序
ext3grep http://qa.debian.org/popcon.php?package=ext3grep 296 用于帮助恢复 ext3 文件系统上已删除文件的工具
scrounge-ntfs http://qa.debian.org/popcon.php?package=scrounge-ntfs 80 用于 NTFS 文件系统的数据恢复程序
gzrt http://qa.debian.org/popcon.php?package=gzrt 68 gzip 恢复工具包
sleuthkit http://qa.debian.org/popcon.php?package=sleuthkit 750 用于取证分析的工具。(Sleuthkit)
autopsy http://qa.debian.org/popcon.php?package=autopsy 1372 SleuthKit 的图形界面
foremost http://qa.debian.org/popcon.php?package=foremost 140 用于恢复数据的取证应用程序
guymager http://qa.debian.org/popcon.php?package=guymager 949 基于 Qt 的取证镜像工具
dcfldd http://qa.debian.org/popcon.php?package=dcfldd 109 dd 的增强版本,用于取证和安全
rdd http://qa.debian.org/popcon.php?package=rdd 200 取证复制程序

10.3.5. 将大文件拆分为小文件

当数据太大而无法作为单个文件备份时,您可以将其内容拆分为例如 2000MiB 的块进行备份,并在以后将这些块合并回原始文件。

$ split -b 2000m large_file
$ cat x* >large_file
[Caution] 注意

请确保您没有任何以 “x” 开头的文件,以避免名称冲突。

10.3.6. 清除文件内容

为了清除文件(例如日志文件)的内容,请不要使用 rm(1) 删除文件,然后再创建一个新的空文件,因为文件可能在命令之间的间隔内仍然被访问。以下是清除文件内容的安全方法。

$ :>file_to_be_cleared

10.3.7. 虚拟文件

以下命令创建虚拟文件或空文件。

$ dd if=/dev/zero    of=5kb.file bs=1k count=5
$ dd if=/dev/urandom of=7mb.file bs=1M count=7
$ touch zero.file
$ : > alwayszero.file

您应该找到以下文件。

  • 5kb.file” 是 5KB 的零数据。

  • 7mb.file” 是 7MB 的随机数据。

  • zero.file” 可能是 0 字节文件。如果它存在,则其 mtime 会被更新,而其内容和长度保持不变。

  • alwayszero.file” 始终是 0 字节文件。如果它存在,则其 mtime 会被更新,并且其内容会被重置。

10.3.8. 擦除整个硬盘

有几种方法可以从整个硬盘类设备(例如,位于 “/dev/sda” 的 USB 记忆棒)上完全擦除数据。

[Caution] 注意

在执行此处的命令之前,请先使用 mount(8) 检查您的 USB 记忆棒位置。“/dev/sda” 指向的设备可能是 SCSI 硬盘或串行-ATA 硬盘,您的整个系统都位于其中。

通过将数据重置为 0 来擦除所有磁盘内容,使用以下命令。

# dd if=/dev/zero of=/dev/sda

通过覆盖随机数据来擦除所有内容,使用以下命令。

# dd if=/dev/urandom of=/dev/sda

通过非常有效地覆盖随机数据来擦除所有内容,使用以下命令。

# shred -v -n 1 /dev/sda

由于 dd(1) 可以从许多可引导的 Linux CD(例如 Debian 安装程序 CD)的 shell 中获得,因此您可以通过在此类介质上对系统硬盘(例如 “/dev/hda”、“/dev/sda” 等)运行擦除命令来完全擦除已安装的系统。

10.3.9. 擦除硬盘的未使用区域

硬盘(或 USB 记忆棒)上的未使用区域,例如 “/dev/sdb1”,可能仍然包含已擦除的数据本身,因为它们仅从文件系统中取消链接。可以通过覆盖它们来清理这些数据。

# mount -t auto /dev/sdb1 /mnt/foo
# cd /mnt/foo
# dd if=/dev/zero of=junk
dd: writing to `junk': No space left on device
...
# sync
# umount /dev/sdb1
[Warning] 警告

对于您的 USB 记忆棒来说,这通常已经足够好了。但这并不完美。已擦除的文件名及其属性的大部分可能被隐藏并保留在文件系统中。

10.3.10. 取消删除已删除但仍打开的文件

即使您意外删除了文件,只要该文件仍被某些应用程序(读或写模式)使用,就可以恢复此类文件。

例如,尝试以下操作

$ echo foo > bar
$ less bar
$ ps aux | grep ' less[ ]'
bozo    4775  0.0  0.0  92200   884 pts/8    S+   00:18   0:00 less bar
$ rm bar
$ ls -l /proc/4775/fd | grep bar
lr-x------ 1 bozo bozo 64 2008-05-09 00:19 4 -> /home/bozo/bar (deleted)
$ cat /proc/4775/fd/4 >bar
$ ls -l
-rw-r--r-- 1 bozo bozo 4 2008-05-09 00:25 bar
$ cat bar
foo

在另一个终端上执行(当您安装了 lsof 软件包时),如下所示。

$ ls -li bar
2228329 -rw-r--r-- 1 bozo bozo 4 2008-05-11 11:02 bar
$ lsof |grep bar|grep less
less 4775 bozo 4r REG 8,3 4 2228329 /home/bozo/bar
$ rm bar
$ lsof |grep bar|grep less
less 4775 bozo 4r REG 8,3 4 2228329 /home/bozo/bar (deleted)
$ cat /proc/4775/fd/4 >bar
$ ls -li bar
2228302 -rw-r--r-- 1 bozo bozo 4 2008-05-11 11:05 bar
$ cat bar
foo

10.3.11. 搜索所有硬链接

可以使用 “ls -li” 识别带有硬链接的文件。

$ ls -li
total 0
2738405 -rw-r--r-- 1 root root 0 2008-09-15 20:21 bar
2738404 -rw-r--r-- 2 root root 0 2008-09-15 20:21 baz
2738404 -rw-r--r-- 2 root root 0 2008-09-15 20:21 foo

baz” 和 “foo” 的链接计数均为 “2” (>1),表明它们具有硬链接。它们的 inode 号码是相同的 “2738404”。这意味着它们是同一个硬链接文件。如果您碰巧没有找到所有硬链接文件,则可以按 inode(例如 “2738404”)进行搜索,如下所示。

# find /path/to/mount/point -xdev -inum 2738404

10.3.12. 不可见的磁盘空间消耗

所有已删除但仍打开的文件会消耗磁盘空间,即使它们在正常的 du(1) 中不可见。它们可以通过以下命令及其大小列出。

# lsof -s -X / |grep deleted

10.4. 数据安全基础设施

数据安全基础设施由数据加密工具、消息摘要工具和签名工具的组合提供。

表 10.10. 数据安全基础设施工具列表

命令 软件包 popcon 大小 描述
gpg(1) gnupg http://qa.debian.org/popcon.php?package=gnupg 4621 GNU Privacy Guard - OpenPGP 加密和签名工具
N/A gnupg-doc http://qa.debian.org/popcon.php?package=gnupg-doc 4124 GNU Privacy Guard 文档
gpgv(1) gpgv http://qa.debian.org/popcon.php?package=gpgv 397 GNU Privacy Guard - 签名验证工具
paperkey(1) paperkey http://qa.debian.org/popcon.php?package=paperkey 88 仅从 OpenPGP 密钥中提取秘密信息
cryptsetup(8), … cryptsetup http://qa.debian.org/popcon.php?package=cryptsetup 648 用于 dm-crypto 块设备加密的实用程序,支持 LUKS
ecryptfs(7), … ecryptfs-utils http://qa.debian.org/popcon.php?package=ecryptfs-utils 368 用于 ecryptfs 堆叠文件系统加密的实用程序
md5sum(1) coreutils http://qa.debian.org/popcon.php?package=coreutils 14088 计算和检查 MD5 消息摘要
sha1sum(1) coreutils http://qa.debian.org/popcon.php?package=coreutils 14088 计算和检查 SHA1 消息摘要
openssl(1ssl) openssl http://qa.debian.org/popcon.php?package=openssl 1079 使用 "openssl dgst" (OpenSSL) 计算消息摘要

请参阅关于 dm-cryptoecryptfs第 9.4 节,“数据加密技巧”,它们通过 Linux 内核模块实现自动数据加密基础设施。

10.4.1. GnuPG 的密钥管理

以下是 GNU Privacy Guard 用于基本密钥管理的命令。

表 10.11. 用于密钥管理的 GNU Privacy Guard 命令列表

命令 描述
gpg --gen-key 生成新密钥
gpg --gen-revoke my_user_ID 为 my_user_ID 生成吊销密钥
gpg --edit-key user_ID 交互式编辑密钥,"help" 获取帮助
gpg -o file --exports 将所有密钥导出到文件
gpg --imports file 从文件导入所有密钥
gpg --send-keys user_ID 将 user_ID 的密钥发送到密钥服务器
gpg --recv-keys user_ID 从密钥服务器接收 user_ID 的密钥
gpg --list-keys user_ID 列出 user_ID 的密钥
gpg --list-sigs user_ID 列出 user_ID 的签名
gpg --check-sigs user_ID 检查 user_ID 的签名
gpg --fingerprint user_ID 检查 user_ID 的指纹
gpg --refresh-keys 更新本地密钥环

以下是信任代码的含义。

表 10.12. 信任代码含义列表

代码 信任描述
- 未分配所有者信任 / 尚未计算
e 信任计算失败
q 信息不足,无法计算
n 从不信任此密钥
m 勉强信任
f 完全信任
u 最终信任

以下命令将我的密钥 "1DD8D791" 上传到流行的密钥服务器 "hkp://keys.gnupg.net"。

$ gpg --keyserver hkp://keys.gnupg.net --send-keys 1DD8D791

在 "~/.gnupg/gpg.conf" (或旧位置 "~/.gnupg/options") 中设置的良好默认密钥服务器包含以下内容。

keyserver hkp://keys.gnupg.net

以下命令从密钥服务器获取未知密钥。

$ gpg --list-sigs --with-colons | grep '^sig.*\[User ID not found\]' |\
  cut -d ':' -f 5| sort | uniq | xargs gpg --recv-keys

OpenPGP Public Key Server (0.9.6 版本之前) 中存在一个错误,该错误会损坏包含超过 2 个子密钥的密钥。较新的 gnupg (>1.2.1-2) 软件包可以处理这些损坏的子密钥。请参阅 gpg(1) 中的 "--repair-pks-subkey-bug" 选项。

10.4.2. 在文件上使用 GnuPG

以下是在文件上使用 GNU Privacy Guard 命令的示例。

表 10.13. 在文件上使用 GNU Privacy Guard 命令列表

命令 描述
gpg -a -s file 将文件签名到 ASCII 文本文件 file.asc
gpg --armor --sign file , ,
gpg --clearsign file 明文签名消息
gpg --clearsign file|mail foo@example.org 将明文签名消息邮件发送到 foo@example.org
gpg --clearsign --not-dash-escaped patchfile 明文签名补丁文件
gpg --verify file 验证明文签名文件
gpg -o file.sig -b file 创建分离签名
gpg -o file.sig --detach-sig file , ,
gpg --verify file.sig file 使用 file.sig 验证文件
gpg -o crypt_file.gpg -r name -e file 公钥加密,目标接收者为 name,从文件加密到二进制 crypt_file.gpg
gpg -o crypt_file.gpg --recipient name --encrypt file , ,
gpg -o crypt_file.asc -a -r name -e file 公钥加密,目标接收者为 name,从文件加密到 ASCII 文本 crypt_file.asc
gpg -o crypt_file.gpg -c file 对称加密,从文件加密到 crypt_file.gpg
gpg -o crypt_file.gpg --symmetric file , ,
gpg -o crypt_file.asc -a -c file 对称加密,目标接收者为 name,从文件加密到 ASCII 文本 crypt_file.asc
gpg -o file -d crypt_file.gpg -r name 解密
gpg -o file --decrypt crypt_file.gpg , ,

10.4.3. 在 Mutt 中使用 GnuPG

将以下内容添加到 "~/.muttrc" 以防止缓慢的 GnuPG 自动启动,同时允许通过在索引菜单中键入 "S" 来使用它。

macro index S ":toggle pgp_verify_sig\n"
set pgp_verify_sig=no

10.4.4. 在 Vim 中使用 GnuPG

gnupg 插件允许您透明地为扩展名为 ".gpg"、".asc" 和 ".ppg" 的文件运行 GnuPG。

# aptitude install vim-scripts vim-addon-manager
$ vim-addons install gnupg

10.4.5. MD5 校验和

md5sum(1) 提供了实用程序,可以使用 rfc1321 中的方法制作摘要文件,并使用它验证每个文件。

$ md5sum foo bar >baz.md5
$ cat baz.md5
d3b07384d113edec49eaa6238ad5ff00  foo
c157a79031e1c40f85931829bc5fc552  bar
$ md5sum -c baz.md5
foo: OK
bar: OK
[Note] 注意

GNU Privacy Guard (GnuPG) 的加密签名相比,MD5 校验和的计算 CPU 密集程度较低。通常,只有顶层摘要文件会进行加密签名以确保数据完整性。

10.5. 源代码合并工具

有许多用于源代码的合并工具。以下命令引起了我的注意。

表 10.14. 源代码合并工具列表

命令 软件包 popcon 大小 描述
diff(1) diffutils http://qa.debian.org/popcon.php?package=diffutils 1118 逐行比较文件
diff3(1) diffutils http://qa.debian.org/popcon.php?package=diffutils 1118 逐行比较和合并三个文件
vimdiff(1) vim http://qa.debian.org/popcon.php?package=vim 1873 在 vim 中并排比较 2 个文件
patch(1) patch http://qa.debian.org/popcon.php?package=patch 218 将 diff 文件应用到原始文件
dpatch(1) dpatch http://qa.debian.org/popcon.php?package=dpatch 237 管理 Debian 软件包的一系列补丁
diffstat(1) diffstat http://qa.debian.org/popcon.php?package=diffstat 45 生成 diff 更改的直方图
combinediff(1) patchutils http://qa.debian.org/popcon.php?package=patchutils 221 从两个增量补丁创建累积补丁
dehtmldiff(1) patchutils http://qa.debian.org/popcon.php?package=patchutils 221 从 HTML 页面提取 diff
filterdiff(1) patchutils http://qa.debian.org/popcon.php?package=patchutils 221 从 diff 文件中提取或排除 diff
fixcvsdiff(1) patchutils http://qa.debian.org/popcon.php?package=patchutils 221 修复 CVS 创建的 diff 文件,patch(1) 错误地解释了这些文件
flipdiff(1) patchutils http://qa.debian.org/popcon.php?package=patchutils 221 交换两个补丁的顺序
grepdiff(1) patchutils http://qa.debian.org/popcon.php?package=patchutils 221 显示补丁修改了哪些与正则表达式匹配的文件
interdiff(1) patchutils http://qa.debian.org/popcon.php?package=patchutils 221 显示两个统一 diff 文件之间的差异
lsdiff(1) patchutils http://qa.debian.org/popcon.php?package=patchutils 221 显示补丁修改了哪些文件
recountdiff(1) patchutils http://qa.debian.org/popcon.php?package=patchutils 221 重新计算统一上下文 diff 中的计数和偏移量
rediff(1) patchutils http://qa.debian.org/popcon.php?package=patchutils 221 修复手动编辑的 diff 的偏移量和计数
splitdiff(1) patchutils http://qa.debian.org/popcon.php?package=patchutils 221 分离出增量补丁
unwrapdiff(1) patchutils http://qa.debian.org/popcon.php?package=patchutils 221 解开已进行单词换行的补丁
wiggle(1) wiggle http://qa.debian.org/popcon.php?package=wiggle 203 应用被拒绝的补丁
quilt(1) quilt http://qa.debian.org/popcon.php?package=quilt 814 管理一系列补丁
meld(1) meld http://qa.debian.org/popcon.php?package=meld 2023 比较和合并文件 (GTK)
xxdiff(1) xxdiff http://qa.debian.org/popcon.php?package=xxdiff 1090 比较和合并文件 (plain X)
dirdiff(1) dirdiff http://qa.debian.org/popcon.php?package=dirdiff 224 显示目录树之间的差异并合并更改
docdiff(1) docdiff http://qa.debian.org/popcon.php?package=docdiff 692 逐字/逐字符比较两个文件
imediff2(1) imediff2 http://qa.debian.org/popcon.php?package=imediff2 76 交互式全屏双向合并工具
makepatch(1) makepatch http://qa.debian.org/popcon.php?package=makepatch 148 生成扩展补丁文件
applypatch(1) makepatch http://qa.debian.org/popcon.php?package=makepatch 148 应用扩展补丁文件
wdiff(1) wdiff http://qa.debian.org/popcon.php?package=wdiff 900 显示文本文件之间的单词差异

10.5.1. 提取源文件的差异

以下过程之一提取两个源文件之间的差异,并根据文件位置创建统一的 diff 文件 "file.patch0" 或 "file.patch1"。

$ diff -u file.old file.new > file.patch0
$ diff -u old/file new/file > file.patch1

10.5.2. 合并源文件的更新

diff 文件(也称为补丁文件)用于发送程序更新。接收方通过以下方式将此更新应用于另一个文件。

$ patch -p0 file < file.patch0
$ patch -p1 file < file.patch1

10.5.3. 通过三向合并更新

如果您有三个版本的源代码,则可以使用 diff3(1) 通过以下方式有效地执行三向合并。

$ diff3 -m file.mine file.old file.yours > file

10.6. 版本控制系统

以下是 Debian 系统上 版本控制系统 (VCS) 的摘要。

[Note] 注意

如果您是 VCS 系统的新手,则应从学习 Git 开始,它正迅速普及。

表 10.15. 版本控制系统工具列表

软件包 popcon 大小 工具 VCS 类型 注释
cssc http://qa.debian.org/popcon.php?package=cssc 2240 CSSC 本地 Unix SCCS 的克隆(已弃用)
rcs http://qa.debian.org/popcon.php?package=rcs 1101 RCS 本地 Unix SCCS 的正确做法”
cvs http://qa.debian.org/popcon.php?package=cvs 4059 CVS 远程 以前的标准远程 VCS
subversion http://qa.debian.org/popcon.php?package=subversion 4107 Subversion 远程 “CVS 的正确做法”,新的事实标准远程 VCS
git http://qa.debian.org/popcon.php?package=git 13073 Git 分布式 C 语言编写的快速 DVCS(由 Linux 内核和其他项目使用)
mercurial http://qa.debian.org/popcon.php?package=mercurial 369 Mercurial 分布式 Python 和一些 C 语言编写的 DVCS
bzr http://qa.debian.org/popcon.php?package=bzr 65 Bazaar 分布式 tla 影响,用 Python 编写的 DVCS(由 Ubuntu 使用)
darcs http://qa.debian.org/popcon.php?package=darcs 13347 Darcs 分布式 具有智能补丁代数的 DVCS(速度较慢)
tla http://qa.debian.org/popcon.php?package=tla 881 GNU arch 分布式 主要由 Tom Lord 开发的 DVCS(历史性)
monotone http://qa.debian.org/popcon.php?package=monotone 5150 Monotone 分布式 C++ 编写的 DVCS
tkcvs http://qa.debian.org/popcon.php?package=tkcvs 2476 CVS, … 远程 VCS(CVS、Subversion、RCS)存储库树的 GUI 显示
gitk http://qa.debian.org/popcon.php?package=gitk 1045 Git 分布式 VCS(Git)存储库树的 GUI 显示

VCS 有时被称为版本控制系统 (RCS) 或软件配置管理 (SCM)。

像 Git 这样的分布式 VCS 是当今的首选工具。CVS 和 Subversion 对于加入一些现有的开源程序活动可能仍然有用。

Debian 通过 Debian Alioth 服务 提供免费的 VCS 服务。它实际上支持所有 VCS。其文档可以在 http://wiki.debian.org/Alioth 找到。

创建共享访问 VCS 归档文件有一些基本知识。

10.6.1. VCS 命令的比较

这是一个简化的本机 VCS 命令比较,以提供总体概况。典型的命令序列可能需要选项和参数。

表 10.16. 本机 VCS 命令的比较

CVS Subversion Git 功能
cvs init svn create git init 创建(本地)存储库
cvs login - - 登录到远程存储库
cvs co svn co git clone 将远程存储库检出为工作树
cvs up svn up git pull 通过合并远程存储库来更新工作树
cvs add svn add git add . 将工作树中的文件添加到 VCS
cvs rm svn rm git rm 从 VCS 中删除工作树中的文件
cvs ci svn ci - 将更改提交到远程存储库
- - git commit -a 将更改提交到本地存储库
- - git push 通过本地存储库更新远程存储库
cvs status svn status git status 从 VCS 显示工作树状态
cvs diff svn diff git diff diff <reference_repository> <working_tree>
- - git repack -a -d; git prune 将本地存储库重新打包到单个包中
tkcvs tkcvs gitk VCS 存储库树的 GUI 显示

[Caution] 注意

从命令行直接调用 git 子命令作为 "git-xyz" 自 2006 年初以来已被弃用。

[Tip] 提示

诸如 tkcvs(1)gitk(1) 之类的 GUI 工具确实可以帮助您跟踪文件的修订历史记录。许多公共归档文件为其存储库提供的 Web 界面也非常有用。

[Tip] 提示

Git 可以直接与不同的 VCS 存储库(例如 CVS 和 Subversion 提供的存储库)一起工作,并通过 git-cvsgit-svn 软件包为本地更改提供本地存储库。请参阅 git for CVS users 和关于 第 10.9.4 节,“Git for the Subversion 存储库”

[Tip] 提示

Git 有一些 CVS 和 Subversion 中没有等效命令的命令:“fetch”、“rebase”、“cherry-pick”,…

10.7. CVS

请参阅以下内容。

  • cvs(1)

  • /usr/share/doc/cvs/html-cvsclient

  • /usr/share/doc/cvs/html-info

  • /usr/share/doc/cvsbook

  • info cvs

10.7.1. CVS 存储库的配置

以下配置允许仅由 "src" 组的成员提交到 CVS 存储库,并且仅由 "staff" 组的成员管理 CVS,从而减少了自杀的可能性。

# cd /var/lib; umask 002; mkdir cvs
# export CVSROOT=/srv/cvs/project
# cd $CVSROOT
# chown root:src .
# chmod 2775 .
# cvs -d $CVSROOT init
# cd CVSROOT
# chown -R root:staff .
# chmod 2775 .
# touch val-tags
# chmod 664 history val-tags
# chown root:src history val-tags
[Tip] 提示

您可以通过将 "$CVSROOT" 目录的所有者更改为 "root:staff" 并将其权限更改为 "3775" 来限制新项目的创建。

10.7.2. 本地访问 CVS

默认的 CVS 存储库由 "$CVSROOT" 指向。以下为本地访问设置 "$CVSROOT"。

$ export CVSROOT=/srv/cvs/project

10.7.3. 使用 pserver 远程访问 CVS

许多公共 CVS 服务器通过 pserver 服务使用帐户名 "anonymous" 提供对它们的只读远程访问。例如,Debian 网站内容由 webwml 项目 通过 Debian alioth 服务上的 CVS 维护。以下为远程访问此 CVS 存储库设置 "$CVSROOT"。

$ export CVSROOT=:pserver:anonymous@cvs.alioth.debian.org:/cvsroot/webwml
$ cvs login
[Note] 注意

由于 pserver 容易受到窃听攻击且不安全,因此服务器管理员通常禁用写入访问。

10.7.4. 使用 ssh 远程访问 CVS

以下为通过 SSH 远程访问 webwml 项目 的 CVS 存储库设置 "$CVS_RSH" 和 "$CVSROOT"。

$ export CVS_RSH=ssh
$ export CVSROOT=:ext:account@cvs.alioth.debian.org:/cvs/webwml

您还可以对 SSH 使用公钥身份验证,这消除了远程密码提示。

10.7.5. 将新源导入 CVS

通过以下方式在 "~/path/to/module1" 创建新的本地源树位置。

$ mkdir -p ~/path/to/module1; cd ~/path/to/module1

使用文件填充 "~/path/to/module1" 下的新本地源树。

使用以下参数将其导入到 CVS。

  • 模块名称: "module1"

  • 供应商标签: "Main-branch" (整个分支的标签)

  • 发布标签: "Release-initial" (特定版本的标签)

$ cd ~/path/to/module1
$ cvs import -m "Start module1" module1 Main-branch Release-initial
$ rm -Rf . # optional

10.7.6. CVS 存储库中的文件权限

CVS 不会覆盖当前的存储库文件,而是用另一个文件替换它。因此,对存储库目录的写入权限至关重要。对于 "/srv/cvs/project" 存储库中 "module1" 的每个新模块,如果需要,请运行以下命令以确保此条件。

# cd /srv/cvs/project
# chown -R root:src module1
# chmod -R ug+rwX   module1
# chmod    2775     module1

10.7.7. CVS 的工作流程

以下是使用 CVS 的典型工作流程示例。

通过以下方式检查由 "$CVSROOT" 指向的 CVS 项目中的所有可用模块。

$ cvs rls
CVSROOT
module1
module2
...

通过以下方式将 "module1" 检出到其默认目录 "./module1"。

$ cd ~/path/to
$ cvs co module1
$ cd module1

根据需要更改内容。

通过以下方式进行等效于 "diff -u [repository] [local]" 的更改检查。

$ cvs diff -u

您发现您严重破坏了一些文件 "file_to_undo",但其他文件都很好。

通过以下方式使用 CVS 中的干净副本覆盖 "file_to_undo" 文件。

$ cvs up -C file_to_undo

通过以下方式将更新后的本地源树保存到 CVS。

$ cvs ci -m "Describe change"

通过以下方式创建 "file_to_add" 文件并将其添加到 CVS。

$ vi file_to_add
$ cvs add file_to_add
$ cvs ci -m "Added file_to_add"

通过以下方式合并来自 CVS 的最新版本。

$ cvs up -d

注意以 "C filename" 开头的行,这表示冲突的更改。

在 ".#filename.version" 中查找未修改的代码。

在文件中搜索 "<<<<<<<" 和 ">>>>>>>" 以查找冲突的更改。

根据需要编辑文件以修复冲突。

通过以下方式添加发布标签 "Release-1"。

$ cvs ci -m "last commit for Release-1"
$ cvs tag Release-1

进一步编辑。

通过以下方式删除发布标签 "Release-1"。

$ cvs tag -d Release-1

通过以下方式将更改检入到 CVS。

$ cvs ci -m "real last commit for Release-1"

通过以下方式将发布标签 "Release-1" 重新添加到已更新的 CVS main HEAD。

$ cvs tag Release-1

通过以下方式从标签 "Release-initial" 指向的原始版本创建带有粘性分支标签 "Release-initial-bugfixes" 的分支,并将其检出到 "~/path/to/old" 目录。

$ cvs rtag -b -r Release-initial Release-initial-bugfixes module1
$ cd ~/path/to
$ cvs co -r Release-initial-bugfixes -d old module1
$ cd old
[Tip] 提示

使用 "-D 2005-12-20" (ISO 8601 日期格式) 而不是 "-r Release-initial" 来指定特定日期作为分支点。

处理具有粘性标签 "Release-initial-bugfixes" 的本地源树,该标签基于原始版本。

自己在此分支上工作…直到其他人加入到此 "Release-initial-bugfixes" 分支。

通过以下方式同步此分支上其他人修改的文件,同时根据需要创建新目录。

$ cvs up -d

根据需要编辑文件以修复冲突。

通过以下方式将更改检入到 CVS。

$ cvs ci -m "checked into this branch"

通过以下方式更新本地树(内容 = main HEAD),同时删除粘性标签 ("-A") 且不进行关键字扩展 ("-kk")。

$ cvs up -d -kk -A

通过以下方式,通过从 "Release-initial-bugfixes" 分支合并来更新本地树(内容 = main HEAD)且不进行关键字扩展。

$ cvs up -d -kk -j Release-initial-bugfixes

使用编辑器修复冲突。

通过以下方式将更改检入到 CVS。

$ cvs ci -m "merged Release-initial-bugfixes"

通过以下方式创建归档文件。

$ cd ..
$ mv old old-module1-bugfixes
$ tar -cvzf old-module1-bugfixes.tar.gz old-module1-bugfixes
$ rm -rf old-module1-bugfixes
[Tip] 提示

cvs up” 命令可以使用 "-d" 选项来创建新目录,并使用 "-P" 选项来修剪空目录。

[Tip] 提示

您可以通过提供子目录的名称作为 "cvs co module1/subdir" 来仅检出 "module1" 的子目录。

表 10.17. CVS 命令的显着选项(用作 cvs(1) 的第一个参数)

选项 含义
-n 试运行,无效果
-t 显示消息,显示 cvs 活动的步骤

10.7.8. 来自 CVS 的最新文件

要从 CVS 获取最新文件,请通过以下方式使用 "tomorrow"。

$ cvs ex -D tomorrow module_name

10.7.9. CVS 的管理

通过以下方式将模块别名 "mx" 添加到 CVS 项目(本地服务器)。

$ export CVSROOT=/srv/cvs/project
$ cvs co CVSROOT/modules
$ cd CVSROOT
$ echo "mx -a module1" >>modules
$ cvs ci -m "Now mx is an alias for module1"
$ cvs release -d .

现在,您可以通过以下方式将 "module1"(别名: "mx")从 CVS 检出到 "new" 目录。

$ cvs co -d new mx
$ cd new
[Note] 注意

为了执行上述过程,您应该具有适当的文件权限。

10.7.10. CVS 检出的执行位

当您从 CVS 检出文件时,它们的执行权限位将被保留。

每当您在检出的文件(例如 "filename")中看到执行权限问题时,请通过以下方式更改相应 CVS 存储库中的权限以修复它。

# chmod ugo-x filename

10.8. Subversion

Subversion 是取代旧 CVS 的新一代版本控制系统。它具有 CVS 的大多数功能,除了标签和分支。

您需要安装 subversionlibapache2-svnsubversion-tools 软件包以设置 Subversion 服务器。

10.8.1. Subversion 存储库的配置

目前,subversion 软件包未设置存储库,因此必须手动设置。存储库的一个可能位置是 "/srv/svn/project"。

通过以下方式创建目录。

# mkdir -p        /srv/svn/project

通过以下方式创建存储库数据库。

# svnadmin create /srv/svn/project

10.8.2. 通过 Apache2 服务器访问 Subversion

如果您仅通过 Apache2 服务器访问 Subversion 存储库,则只需使存储库仅可由 WWW 服务器写入,方法如下。

# chown -R www-data:www-data /srv/svn/project

在 "/etc/apache2/mods-available/dav_svn.conf" 中添加(或取消注释)以下内容,以允许通过用户身份验证访问存储库。

<Location /project>
  DAV svn
  SVNPath /srv/svn/project
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /etc/subversion/passwd
<LimitExcept GET PROPFIND OPTIONS REPORT>
    Require valid-user
</LimitExcept>
</Location>

使用以下命令创建用户身份验证文件。

# htpasswd2 -c /etc/subversion/passwd some-username

重启 Apache2。

您的新 Subversion 存储库可通过 URL "http://localhost/project" 和 "http://example.com/project" 从 svn(1) 访问(假设您的 Web 服务器 URL 为 "http://example.com/")。

10.8.3. 通过组本地访问 Subversion

以下为通过组(例如 project)本地访问设置 Subversion 存储库。

# chmod  2775     /srv/svn/project
# chown -R root:src /srv/svn/project
# chmod -R ug+rwX   /srv/svn/project

对于属于 project 组的本地用户,您的新 Subversion 存储库可通过 URL "file:///localhost/srv/svn/project" 或 "file:///srv/svn/project" 从 svn(1) 进行组访问。您必须在 "umask 002" 下运行诸如 svnsvnservesvnlooksvnadmin 之类的命令,以确保组访问。

10.8.4. 通过 SSH 远程访问 Subversion

对于 SSH,组可访问的 Subversion 存储库位于 URL "example.com:/srv/svn/project",您可以从 svn(1) 以 URL "svn+ssh://example.com:/srv/svn/project" 访问它。

10.8.5. Subversion 目录结构

许多项目使用类似于以下的目录树用于 Subversion,以弥补其缺少分支和标签的不足。

  ----- module1
    |   |-- branches
    |   |-- tags
    |   |   |-- release-1.0
    |   |   `-- release-2.0
    |   |
    |   `-- trunk
    |       |-- file1
    |       |-- file2
    |       `-- file3
    |
    `-- module2
[Tip] 提示

您必须使用 "svn copy …" 命令来标记分支和标签。这确保 Subversion 正确记录文件的修改历史记录并节省存储空间。

10.8.6. 将新源导入 Subversion

通过以下方式在 "~/path/to/module1" 创建新的本地源树位置。

$ mkdir -p ~/path/to/module1; cd ~/path/to/module1

使用文件填充 "~/path/to/module1" 下的新本地源树。

使用以下参数将其导入到 Subversion。

  • 模块名称: "module1"

  • Subversion 站点 URL: "file:///srv/svn/project"

  • Subversion 目录: "module1/trunk"

  • Subversion 标签: "module1/tags/Release-initial"

$ cd ~/path/to/module1
$ svn import file:///srv/svn/project/module1/trunk -m "Start module1"
$ svn cp file:///srv/svn/project/module1/trunk file:///srv/svn/project/module1/tags/Release-initial

或者,通过以下方式。

$ svn import ~/path/to/module1 file:///srv/svn/project/module1/trunk -m "Start module1"
$ svn cp file:///srv/svn/project/module1/trunk file:///srv/svn/project/module1/tags/Release-initial
[Tip] 提示

您可以将诸如 "file:///…" 之类的 URL 替换为任何其他 URL 格式,例如 "http://…" 和 "svn+ssh://…"。

10.8.7. Subversion 的工作流程

以下是使用 Subversion 及其本机客户端的典型工作流程示例。

[Tip] 提示

git-svn 软件包提供的客户端命令可以使用 git 命令提供 Subversion 的替代工作流程。请参阅关于 第 10.9.4 节,“Git for the Subversion 存储库”

通过以下方式检查由 URL "file:///srv/svn/project" 指向的 Subversion 项目中的所有可用模块。

$ svn list file:///srv/svn/project
module1
module2
...

通过以下方式将 "module1/trunk" 检出到目录 "module1"。

$ cd ~/path/to
$ svn co file:///srv/svn/project/module1/trunk module1
$ cd module1

根据需要更改内容。

通过以下方式进行等效于 "diff -u [repository] [local]" 的更改检查。

$ svn diff

您发现您严重破坏了一些文件 "file_to_undo",但其他文件都很好。

通过以下方式使用 Subversion 中的干净副本覆盖 "file_to_undo" 文件。

$ svn revert file_to_undo

通过以下方式将更新后的本地源树保存到 Subversion。

$ svn ci -m "Describe change"

通过以下方式创建 "file_to_add" 文件并将其添加到 Subversion。

$ vi file_to_add
$ svn add file_to_add
$ svn ci -m "Added file_to_add"

通过以下方式合并来自 Subversion 的最新版本。

$ svn up

注意以 "C filename" 开头的行,这表示冲突的更改。

在例如 "filename.r6"、"filename.r9" 和 "filename.mine" 中查找未修改的代码。

在文件中搜索 "<<<<<<<" 和 ">>>>>>>" 以查找冲突的更改。

根据需要编辑文件以修复冲突。

通过以下方式添加发布标签 "Release-1"。

$ svn ci -m "last commit for Release-1"
$ svn cp file:///srv/svn/project/module1/trunk file:///srv/svn/project/module1/tags/Release-1

进一步编辑。

通过以下方式删除发布标签 "Release-1"。

$ svn rm file:///srv/svn/project/module1/tags/Release-1

通过以下方式将更改检入到 Subversion。

$ svn ci -m "real last commit for Release-1"

通过以下方式从已更新的 trunk 的 Subversion HEAD 重新添加发布标签 "Release-1"。

$ svn cp file:///srv/svn/project/module1/trunk file:///srv/svn/project/module1/tags/Release-1

通过以下方式从路径 "module1/tags/Release-initial" 指向的原始版本创建路径为 "module1/branches/Release-initial-bugfixes" 的分支,并将其检出到 "~/path/to/old" 目录。

$ svn cp file:///srv/svn/project/module1/tags/Release-initial file:///srv/svn/project/module1/branches/Release-initial-bugfixes
$ cd ~/path/to
$ svn co file:///srv/svn/project/module1/branches/Release-initial-bugfixes old
$ cd old
[Tip] 提示

使用 "module1/trunk@{2005-12-20}" (ISO 8601 日期格式) 而不是 "module1/tags/Release-initial" 来指定特定日期作为分支点。

处理指向分支 "Release-initial-bugfixes" 的本地源树,该分支基于原始版本。

自己在此分支上工作…直到其他人加入到此 "Release-initial-bugfixes" 分支。

通过以下方式同步此分支上其他人修改的文件。

$ svn up

根据需要编辑文件以修复冲突。

通过以下方式将更改检入到 Subversion。

$ svn ci -m "checked into this branch"

通过以下方式使用 trunk 的 HEAD 更新本地树。

$ svn switch file:///srv/svn/project/module1/trunk

通过以下方式,通过从 "Release-initial-bugfixes" 分支合并来更新本地树(内容 = trunk 的 HEAD)。

$ svn merge file:///srv/svn/project/module1/branches/Release-initial-bugfixes

使用编辑器修复冲突。

通过以下方式将更改检入到 Subversion。

$ svn ci -m "merged Release-initial-bugfixes"

通过以下方式创建归档文件。

$ cd ..
$ mv old old-module1-bugfixes
$ tar -cvzf old-module1-bugfixes.tar.gz old-module1-bugfixes
$ rm -rf old-module1-bugfixes
[Tip] 提示

您可以将诸如 "file:///…" 之类的 URL 替换为任何其他 URL 格式,例如 "http://…" 和 "svn+ssh://…"。

[Tip] 提示

您可以通过提供子目录的名称作为 "svn co file:///srv/svn/project/module1/trunk/subdir module1/subdir" 等来仅检出 "module1" 的子目录。

表 10.18. Subversion 命令的显着选项(用作 svn(1) 的第一个参数)

选项 含义
--dry-run 试运行,无效果
-v 显示 svn 活动的详细消息

10.9. Git

Git 可以为本地和远程源代码管理做所有事情。这意味着您无需连接到远程仓库的网络即可记录源代码更改。

10.9.1. Git 客户端的配置

您可能希望在“~/.gitconfig”中设置几个全局配置,例如您的姓名和 Git 使用的电子邮件地址,如下所示。

$ git config --global user.name "Name Surname"
$ git config --global user.email yourname@example.com

如果您太习惯 CVS 或 Subversion 命令,您可能希望通过以下方式设置几个命令别名。

$ git config --global alias.ci "commit -a"
$ git config --global alias.co checkout

您可以通过以下方式检查您的全局配置。

$ git config --global --list

10.9.2. Git 参考

请参阅以下内容。

git-gui(1)gitk(1) 命令使 Git 的使用非常容易。

[Warning] 警告

即使某些工具(如 gitk(1))允许您使用,也不要在标签字符串中使用空格。它可能会使其他一些 git 命令崩溃。

10.9.3. Git 命令

即使您的上游使用不同的 VCS,为本地活动使用 git(1) 也可能是一个好主意,因为您可以在没有连接到上游的网络的情况下管理本地源代码树副本。以下是一些与 git(1) 一起使用的软件包和命令。

表 10.19. git 相关软件包和命令列表

命令 软件包 popcon 大小 描述
N/A git-doc http://qa.debian.org/popcon.php?package=git-doc 8398 Git 的官方文档
N/A gitmagic http://qa.debian.org/popcon.php?package=gitmagic 924 “Git Magic”,更容易理解的 Git 指南
git(7) git http://qa.debian.org/popcon.php?package=git 13073 Git,快速、可扩展的分布式版本控制系统
gitk(1) gitk http://qa.debian.org/popcon.php?package=gitk 1045 带有历史记录的 GUI Git 仓库浏览器
git-gui(1) git-gui http://qa.debian.org/popcon.php?package=git-gui 1666 Git 的 GUI(无历史记录)
git-svnimport(1) git-svn http://qa.debian.org/popcon.php?package=git-svn 686 将数据从 Subversion 导入到 Git
git-svn(1) git-svn http://qa.debian.org/popcon.php?package=git-svn 686 在 Subversion 和 Git 之间提供双向操作
git-cvsimport(1) git-cvs http://qa.debian.org/popcon.php?package=git-cvs 779 将数据从 CVS 导入到 Git
git-cvsexportcommit(1) git-cvs http://qa.debian.org/popcon.php?package=git-cvs 779 从 Git 将提交导出到 CVS 检出
git-cvsserver(1) git-cvs http://qa.debian.org/popcon.php?package=git-cvs 779 Git 的 CVS 服务器模拟器
git-send-email(1) git-email http://qa.debian.org/popcon.php?package=git-email 529 从 Git 发送补丁集合作为电子邮件
stg(1) stgit http://qa.debian.org/popcon.php?package=stgit 1628 基于 git 的 quilt (Python)
git-buildpackage(1) git-buildpackage http://qa.debian.org/popcon.php?package=git-buildpackage 2219 使用 Git 自动化 Debian 打包
guilt(7) guilt http://qa.debian.org/popcon.php?package=guilt 360 基于 git 的 quilt (SH/AWK/SED/…)

[Tip] 提示

使用 git(1),您可以在本地分支上进行许多提交的工作,并使用类似“git rebase -i master”之类的命令稍后重新组织更改历史记录。这使您能够创建干净的更改历史记录。请参阅 git-rebase(1)git-cherry-pick(1)

[Tip] 提示

当您想要返回到干净的工作目录而又不丢失工作目录的当前状态时,可以使用“git stash”。请参阅 git-stash(1)

10.9.4. 用于 Subversion 仓库的 Git

您可以将 Subversion 仓库“svn+ssh://svn.example.org/project/module/trunk”检出到本地 Git 仓库“./dest”,然后提交回 Subversion 仓库。例如:

$ git svn clone -s -rHEAD svn+ssh://svn.example.org/project dest
$ cd dest
... make changes
$ git commit -a
... keep working locally with git
$ git svn dcommit
[Tip] 提示

使用“-rHEAD”使我们能够避免从 Subversion 仓库克隆整个历史内容。

10.9.5. 用于记录配置历史记录的 Git

您可以使用 Git 工具手动记录配置的时间顺序历史记录。这是一个简单的示例,供您练习记录“/etc/apt/”内容。

$ cd /etc/apt/
$ sudo git init
$ sudo chmod 700 .git
$ sudo git add .
$ sudo git commit -a

提交配置并附带描述。

修改配置文件。

$ cd /etc/apt/
$ sudo git commit -a

提交配置并附带描述,然后继续您的生活。

$ cd /etc/apt/
$ sudo gitk --all

您拥有完整的配置历史记录。

[Note] 注意

需要 sudo(8) 才能处理配置数据的任何文件权限。对于用户配置数据,您可以跳过 sudo

[Note] 注意

上述示例中的“chmod 700 .git”命令是保护存档数据免受未经授权的读取访问所必需的。

[Tip] 提示

有关记录配置历史记录的更完整设置,请查找 etckeeper 软件包:第 9.2.10 节,“记录配置文件中的更改”