rsync
Archlinux でファイルをバックアップするために使用する bash スクリプトがあります。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
実行すると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
rsync
- ファイル記述子 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);
レポート ファイルにブロックが 0 個あるため
stat
、スパースとして分類されます。エクステントコピー(より効率的なコピー方法)でファイルを読み取ろうとします普通 スパース ファイル) であり、失敗します。
- スパースコピーによるコピー。
- 最大読み取りサイズ MAXINT から開始します。
通常、18446744073709551615
32 ビット システムではバイトです。 - 質問します。4096 バイトを読み取ります。(バッファ サイズは統計情報からメモリに割り当てられます。)
- 短い文字列、つまり 18 バイトが読み取られます。
- 穴が必要かどうか確認します。必要ありません。
- バッファをターゲットに書き込みます。
- 最大読み取りサイズから 18 を減算します。
- 質問します。4096 バイトを読み取ります。
- 最初の読み取りですべて消費されたため、0 バイトです。
- 成功を返します。
- 最大読み取りサイズ MAXINT から開始します。
- すべて正常です。ファイルのフラグを更新します。
- 大丈夫。
答え2
Rsyncはコードこれは、読み取り中にファイルが切り捨てられたかどうかを具体的にチェックし、このエラーを返しますENODATA
。わかりませんなぜ内のファイルは/sys
この動作をしますが、実際のファイルではないので、それほど驚くことではないと思います。rsync にこの特定のチェックをスキップするように指示する方法はないようです。
/sys
おそらく、rsync を使用せず、特定のスクリプトを使用して、必要な特定の情報 (ネットワーク カード アドレスなど) を厳選する方がよいと思います。
答え3
関連している可能性がありますが、拡張属性の呼び出しは sysfs では失敗します。
[root@ハイパーバイザー eth0]# lsattr アドレス
lsattr: アドレスのフラグを読み取り中にデバイスに不適切な ioctl が見つかりました
[root@ハイパーバイザー eth0]#
私の strace を見ると、rsync はデフォルトで拡張属性を取得しようとしているようです。
22964 <... getxattr が再開されました>、0x7fff42845110、132) = -1 ENODATA (データがありません)
拡張属性をスキップすることで問題が解決するかどうかを調べるためにrsyncに与えるフラグを探しましたが、何も見つかりませんでした--xattrs
(の上目的地にて。
答え4
Rsync は通常、ファイルの情報を読み取り、ファイルの内容またはデルタを宛先ディレクトリの一時ファイルに転送し、ファイルのデータを検証した後、宛先ファイル名に名前を変更します。
sysfs の問題は、すべてのファイルが 4k (1 メモリ ページ) として表示されるにもかかわらず、数バイトしか含まれていない可能性があることだと思います。破損している可能性のあるファイルをコピー先にコピーすることを避けるために、rsync は、ファイルのメタデータと実際にコピーされた内容が一致しないことが判明すると、コピーをキャンセルします。
少なくとも rsync v3.0.6 では、--inplace
スイッチを使用してこの動作を回避できます。rsync は引き続きエラーを検出しますが、検出時には宛先ファイルがすでに上書きされているため、破損している可能性のあるファイルはそのまま残ります。
ただし、その副作用として、rsync がファイルのサイズとして想定している 4k にファイルがゼロで埋められることに注意してください。null バイトは通常無視されるため、ほとんどの場合、違いはありません。