tar
使用マルチボリュームモードエラーを使用してENOSPC
最初のテープの終わりを検出し、ユーザーに次のテープの入力を求めます。この動作をシミュレートするには、次の例を検討してください。/dev/full
tar -cvf - --multi-volume . > /dev/full
期待される結果
[...]
Prepare volume #2 for ‘-’ and hit return:
次の暗号化プログラムを介してtar
出力を送信すると問題が発生します。aespipe
gpg
tar -cvf - --multi-volume . | gpg -c --batch -q --passphrase 123 > /dev/full
gpg
これでコード2で終了します。
gpg: [stdout]: write error: No space left on device
gpg: [stdout]: write error: No space left on device
gpg: filter_flush failed on close: No space left on device
ENOSPCは、tarが特定のerrnoを知らないため、明らかにtarに伝播されません。gpg
bashスクリプトを使用してエラーをキャッチしてENOSPCエラーを「再生成」する方法はありますか?tar
たとえば、名前付きパイプでtarを使用すると、失敗するとgpg
パイプが破損し、tarはSIGPIPE 141として存在します。ただし、ENOSPC
破損したパイプエラー以外の方法でtarにシグナルを送信する必要があります。
固定テープサイズを指定する回避策を避けたいです。また、テープスパニングを処理するために使用することも知っています。mbuffer
これはテープを個別に抽出できないためお勧めできません。
編集:ENOSPCが発生したときにtarを残し、バッファ内のデータが失われる可能性が高いため、これがより複雑になることに気づきました。ほとんどのテープドライバの実装では、後で別の書き込み操作を実行できますが、gpgとaespipeにはデータをバッファに格納するための再試行ロジックは含まれていません。
編集2:さらなる研究によると、FreeBSDにはエラーが発生したときに暗号化を実行するstar
オプションがあることがわかりました。-compress-program
-multivol
new-volume-script=...
star: Operation not permitted. Cannot lock fifo memory.
star: Can only compress files
ファイルではなくデバイスに書き込む場合。だから、これはまた路地の路地です。
ベストアンサー1
パイプを介して書き込みエラーを伝播できません。
どのようなハッキングで達成できるとしても、パイプラインはバッファーパイプリーダーがパイプライターに「シグナル」を送信しようとすると、後者はすでにエラーを引き起こしたデータを記録し、すでに成功ステータス(> 0)を取得し、それに応じてステータスを更新した可能性があります。それがうまくいくには、書き込みプロセスが時間をさかのぼる必要があります。さらに、パイプリーダー自体が独自のバッファリングとステートホールドを実行でき、これにより非同期が発生する可能性があります。
唯一の方法は、tar
一部のチャネルを介してデータを渡すのではなく、暗号化ルーチンを直接呼び出すことです。ソースコードを変更して再コンパイルするのではなく、サル/ライブパッチを使用してライブラリ関数をLD_PRELOAD
置き換えwrite()
、データをソースしますwrite()
。
ENOSPC
ハッキングを使ってLD_PRELOAD
シミュレーションする方法
ENOSPC
これにより、40960バイトを超えて書き込もうとすると、fd 1(stdout)への書き込みが失敗し、その後カウンタをリセットして再び成功します。
tar -cf filename
ではなくで動作するようにtar -cf -
テストしfd == 1
ますfd != 2
。
$ cat <<'EOT' >enospc.c
#define _GNU_SOURCE
#include <unistd.h>
#include <dlfcn.h>
#include <err.h>
#include <errno.h>
#define MAX 40960
ssize_t write(int fd, const void *b, size_t z){
ssize_t w;
static typeof (write) *o_write;
static size_t count;
if(!o_write) o_write = dlsym(RTLD_NEXT, "write");
if(fd == 1 && count + z > MAX){
count = 0;
errno = ENOSPC;
return -1;
}
w = o_write(fd, b, z);
if(w > 0) count += w;
return w;
}
EOT
$ cc -Wall -shared enospc.c -o enospc.so -ldl
$ seq -f 'n foo%04g.tar' 1 10000 |
LD_PRELOAD=./enospc.so tar -M -cf- /etc/X11 > foo0000.tar
tar: Removing leading `/' from member names
Prepare volume #2 for ‘-’ and hit return: Prepare volume #3 for ‘/tmp/foo0001.tar’ and hit return: Prepare volume #4 for ‘/tmp/foo0002.tar’ and hit return: Prepare volume #5 for ‘/tmp/foo0003.tar’ and hit return: Prepare volume #6 for ‘/tmp/foo0004.tar’ and hit return: Prepare volume #7 for ‘/tmp/foo0005.tar’ and hit return: Prepare volume #8 for ‘/tmp/foo0006.tar’ and hit return: Prepare volume #9 for ‘/tmp/foo0007.tar’ and hit return: $
$ ls foo000*
foo0000.tar foo0002.tar foo0004.tar foo0006.tar foo0008.tar
foo0001.tar foo0003.tar foo0005.tar foo0007.tar