tar 多卷模式下的磁帶結束檢測 (ENOSPC),帶有用於加密的管道
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
退出程式碼2gpg: [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。有沒有辦法從bash 腳本中擷取錯誤
gpg
並“重新引發”ENOSPC 錯誤?tar
例如,將 tar 與命名管道一起使用會導致管道一旦
gpg
失敗,並且 tar 隨後與 SIGPIPE 141 一起存在 - 但是ENOSPC
仍然必須以某種方式向 tar 發出信號,而不是管道損壞錯誤。我想避免指定固定磁帶大小的解決方法。我也知道使用
mbuffer
處理磁帶跨越,這是不可取的,因為磁帶不能單獨提取。編輯:我剛剛意識到這將變得更加複雜,因為在遇到 ENOSPC 時已經離開 tar 並且在緩衝區中的數據很可能會失去。儘管大多數磁帶驅動程序實現都允許在此之後進行另一個寫入操作,但 gpg 和 aespipe 不包含將數據保存在緩衝區中的重試邏輯。
編輯 2:進一步的研究表明,
star
在 FreeBSD 上-compress-program
可以選擇執行加密-multivol
並new-volume-script=...
引發錯誤star: Operation not permitted. Cannot lock fifo memory. star: Can only compress files
寫入設備而不是文件時。所以這也是一個死胡同。
不可能通過管道將寫入錯誤傳播回來
即使有可能通過某種黑客攻擊,管道正在緩衝,並且當管道讀取器嘗試“發送信號”管道寫入器時,後者可能已經寫入了導致錯誤進一步下降的數據, 已經獲得了成功的狀態 (>0) 並相應地更新了它的狀態。為了讓它發揮作用,寫作過程必須及時回溯。最重要的是,管道讀取器本身可能會進行自己的緩沖和狀態保持,這會不同步。
唯一的出路是
tar
直接呼叫加密常式,而不是通過某種通道傳遞數據。而不是修改它的原始碼並重新編譯它,這可以通過猴子/實時修補它來完成,LD_PRELOAD
它覆蓋write()
庫函式並在將數據傳遞給原始write()
.如何
ENOSPC
使用LD_PRELOAD
hack進行模擬這將導致對 fd 1 (stdout) 的寫入在
ENOSPC
嘗試向其寫入超過 40960 字節時失敗,之後它會重置計數器並再次成功,等等。如果您希望它使用
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