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,因为 tar 不知道具体的 errno。有没有办法使用 bash 脚本捕获错误gpg并“重新引发”ENOSPC 错误?tar

例如,将 tar 与命名管道一起使用一旦gpg失败就会导致管道损坏,并且 tar 随后会以 SIGPIPE 141 的形式存在——但是ENOSPC仍然需要以某种方式向 tar 发出信号,而不是管道损坏错误。

我想避免指定固定磁带大小的解决方法。我还知道使用来mbuffer处理磁带跨越,这是不可取的,因为磁带无法单独提取。

编辑:我刚刚意识到这会变得更加复杂,因为遇到 ENOSPC 时已经离开 tar 并位于缓冲区中的数据很可能会丢失。尽管大多数磁带驱动程序实现允许此后进行另一次写入操作,但 gpg 和 aespipe 不包含重试逻辑来将数据保存在缓冲区中。

编辑 2:进一步的研究表明,star在 FreeBSD 上,可以选择-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_PRELOADhack进行模拟

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

答案2

你的问题有几个问题:

  • 检测磁带结束情况的正确方法是检查是否有返回 0 且未设置 errno 的 write(2)。因此,支持多卷磁带存档的正确 tar 实现会检查 write(2) 返回 0。

  • 仅当写入文件系统中的普通文件时才会创建 errno ENOSPC,因此该 errno 不适合用作多卷磁带存档的基础。

  • 不可能通过管道将写错误传播回来。

  • UNIX tar 命令不支持多卷归档

  • gtar 支持写入多卷存档,但无法正确读回,概率约为 100%。 5%,因为它并不总是能够将后续存档识别为按顺序正确的卷号。这是由 gtar 中的设计缺陷引起的,如果不引入新的不兼容的多卷格​​式,就无法修复该缺陷。

  • star 仅在被称为 root 时尝试锁定 FIFO 内存。它在您引用的消息中写入的错误代码的意思是:不是sueruser(root)。您是否在“root”权限有限的环境中运行此明星实例?

  • 如果输出不是普通文件,star 不会运行压缩程序,因为压缩程序的输出不会被阻止,但 tar 实现需要阻止输出。如果您想在这种情况下进行压缩,请调用如下命令:star -c ... | compress ...

一般来说,如果您确实想加密 tar 程序的输出,则需要通过管理多卷磁带输出的程序来传输加密程序的输出。

顺便说一句:请随时发送更多信息以获得更深入的答案。

相关内容