暗号化用パイプを使用したtarマルチボリュームモードのENOSPC(テープエンド検出)

暗号化用パイプを使用したtarマルチボリュームモードのENOSPC(テープエンド検出)

tar使用マルチボリュームモードエラーを使用してENOSPC最初のテープの終わりを検出し、ユーザーに次のテープの入力を求めます。この動作をシミュレートするには、次の例を検討してください。/dev/full

tar -cvf - --multi-volume . > /dev/full

期待される結果

[...]
Prepare volume #2 for ‘-’ and hit return:

次の暗号化プログラムを介してtar出力を送信すると問題が発生します。aespipegpg

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に伝播されません。gpgbashスクリプトを使用してエラーをキャッチしてENOSPCエラーを「再生成」する方法はありますか?tar

たとえば、名前付きパイプでtarを使用すると、失敗するとgpgパイプが破損し、tarはSIGPIPE 141として存在します。ただし、ENOSPC破損したパイプエラー以外の方法でtarにシグナルを送信する必要があります。

固定テープサイズを指定する回避策を避けたいです。また、テープスパニングを処理するために使用することも知っています。mbufferこれはテープを個別に抽出できないためお勧めできません。

編集:ENOSPCが発生したときにtarを残し、バッファ内のデータが失われる可能性が高いため、これがより複雑になることに気づきました。ほとんどのテープドライバの実装では、後で別の書き込み操作を実行できますが、gpgとaespipeにはデータをバッファに格納するための再試行ロジックは含まれていません。

編集2:さらなる研究によると、FreeBSDにはエラーが発生したときに暗号化を実行するstarオプションがあることがわかりました。-compress-program-multivolnew-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

おすすめ記事