我有一個 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
。
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 認為檔案的大小。在大多數情況下,它不會產生任何影響,因為空位元組通常會被忽略。