我有一个 bash 脚本,用于rsync
在 Archlinux 中备份文件。我注意到无法rsync
从 复制文件/sys
,但cp
工作得很好:
# rsync /sys/class/net/enp3s1/address /tmp
rsync: read errors mapping "/sys/class/net/enp3s1/address": No data available (61)
rsync: read errors mapping "/sys/class/net/enp3s1/address": No data available (61)
ERROR: address failed verification -- update discarded.
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1052) [sender=3.0.9]
# cp /sys/class/net/enp3s1/address /tmp ## this works
我想知道为什么会rsync
失败,是否可以用它复制文件?
答案1
首先/sys
是一个伪文件系统。如果您查看,/proc/filesystems
您会发现一个已注册文件系统的列表,其中有不少nodev
位于前面。这表明他们是伪文件系统。这意味着它们作为基于 RAM 的文件系统存在于正在运行的内核上。此外,它们不需要块设备。
$ cat /proc/filesystems
nodev sysfs
nodev rootfs
nodev bdev
...
启动时,内核会挂载该系统并在合适时更新条目。例如,当在引导期间或通过udev
.
通常,/etc/mtab
您可以通过以下方式找到安装座:
sysfs /sys sysfs rw,noexec,nosuid,nodev 0 0
有关该主题的一篇好论文请阅读 Patric Mochel 的 – sysfs 文件系统。
/sys 文件的统计信息
如果您进入下面的目录/sys
并执行 a,ls -l
您会发现所有文件都有一个大小。通常为 4096 字节。这是由 报道的sysfs
。
:/sys/devices/pci0000:00/0000:00:19.0/net/eth2$ ls -l
-r--r--r-- 1 root root 4096 Apr 24 20:09 addr_assign_type
-r--r--r-- 1 root root 4096 Apr 24 20:09 address
-r--r--r-- 1 root root 4096 Apr 24 20:09 addr_len
...
stat
此外,您可以对文件进行操作并注意到另一个明显的特征;它占用0块。另外,根节点 (stat /sys) 的 inode 为 1。/stat/fs
通常有 inode 2。等等。
rsync 与 cp
对于 rsync 同步伪文件失败的最简单解释可能是通过示例。
假设我们有一个名为address
18 字节的文件。文件的ls
or报告4096 字节。stat
同步
- 打开文件描述符 fd。
- 使用 fstat(fd) 获取大小等信息。
- 开始读取大小字节,即 4096。那将是第253行链接的代码的@马特德姆。
read_size == 4096
- 问;读取:4096 字节。
- 读取一个短字符串,即 18 个字节。
nread == 18
read_size = read_size - nread (4096 - 18 = 4078)
- 问;读取:4078 字节
- 读取 0 个字节(因为第一次读取消耗了文件中的所有字节)。
nread == 0
,255号线- 无法读取
4096
字节。将缓冲区清零。 - 设置错误
ENODATA
。 - 返回。
- 报告错误。
- 重试。 (以上循环)。
- 失败。
- 报告错误。
- 美好的。
在此过程中,它实际上读取整个文件。但由于没有可用的尺寸,它无法验证结果 - 因此失败是唯一的选择。
CP
- 打开文件描述符 fd。
- 使用 fstat(fd) 获取 st_size 等信息(也使用 lstat 和 stat)。
检查文件是否可能稀疏。那就是文件有漏洞等。
copy.c:1010 /* Use a heuristic to determine whether SRC_NAME contains any sparse * blocks. If the file has fewer blocks than would normally be * needed for a file of its size, then at least one of the blocks in * the file is a hole. */ sparse_src = is_probably_sparse (&src_open_sb);
由于
stat
报告文件有零个块,因此它被归类为稀疏。尝试通过范围复制来读取文件(一种更有效的复制方式普通的 稀疏文件),并且失败。
- 通过稀疏复制进行复制。
- 从最大读取大小 MAXINT 开始。在 32 位系统上
通常 为字节。18446744073709551615
- 问;读取 4096 字节。 (根据统计信息在内存中分配的缓冲区大小。)
- 读取一个短字符串,即 18 个字节。
- 检查是否需要打孔,不需要。
- 将缓冲区写入目标。
- 从最大读取大小中减去 18。
- 问;读取 4096 字节。
- 0 字节,因为第一次读取时全部被消耗。
- 返回成功。
- 从最大读取大小 MAXINT 开始。在 32 位系统上
- 一切都好。更新文件的标志。
- 美好的。
答案2
Rsync有代码它专门检查文件在读取过程中是否被截断并给出此错误 - ENODATA
。我不知道为什么中的文件/sys
有这种行为,但由于它们不是真正的文件,我想这并不太令人惊讶。似乎没有办法告诉 rsync 跳过这个特定的检查。
我认为您最好不要进行 rsync/sys
并使用特定脚本来挑选出您想要的特定信息(例如网卡地址)。
答案3
可能相关,但扩展属性调用将在 sysfs 上失败:
[root@hypervisor eth0]# lsattr 地址
lsattr:读取地址上的标志时,设备的 ioctl 不合适
[root@hypervisor eth0]#
看看我的 strace,看起来 rsync 默认情况下会尝试引入扩展属性:
22964 <... getxattr 恢复> , 0x7fff42845110, 132) = -1 ENODATA (无可用数据)
我尝试找到一个标志来提供 rsync 以查看跳过扩展属性是否可以解决问题,但无法找到任何内容(--xattrs
将它们变成在到达目的地)。
答案4
Rsync 通常读取文件的信息,将文件内容或增量传输到目标目录中的临时文件,然后在验证文件数据后将其重命名为目标文件名。
我认为 sysfs 的问题是所有文件都显示为 4k(一个内存页),但它们可能只包含几个字节。为了避免将可能损坏的文件复制到目标,当 rsync 发现文件的元数据与实际复制的内容不匹配时,它会取消复制。
至少在 rsync v3.0.6 上可以使用--inplace
开关避免这种行为。 Rsync 仍会检测错误,但由于目标文件在检测时已被覆盖,因此会将可能损坏的文件留在那里。
请注意,它的一个副作用是文件最终会被零填充到 4k,因为这是 rsync 认为文件的大小。在大多数情况下,它不会产生任何影响,因为空字节通常会被忽略。