TAR 檔案格式
POSIX 的 tape archive——未壓縮、把檔案循序串起來的容器。
- MIME 類型
- application/x-tar
- 副檔名
- .tar
- 分類
- 壓縮與封存
Magic number(檔案簽章)
| 0x00000101 | 7573746172 | ustar | 'ustar' marker in the first header |
關於 TAR 格式
TAR 是一串 512 位元組的區塊:每個成員先一個標頭區塊,接著是成員內容並補零對齊到下一個 512 邊界,如此重複,直到連續兩個全零區塊代表結束。沒有壓縮、沒有索引、沒有目錄——這就是為什麼 .tar 幾乎一定會再包一層 gzip、xz 或 zstd,也是為什麼想列出 .tar.gz 的內容就得整個解壓縮一遍。標頭裡每個數值欄位都是用 ASCII 八進位數字寫的,不是二進位;這個 1979 年的決定讓你不靠任何工具、光在 hex dump 裡就讀得懂 tar 標頭。
檔頭結構,一個位元組一個位元組看
| 位移 | 長度 | 欄位 | 意義 |
|---|---|---|---|
| 0 | 100 | name | 成員的路徑,以 NUL 補滿的 ASCII,相對路徑、用斜線。目錄會存成結尾帶斜線的形式。超過 100 位元組的路徑必須拆到位移 345 的 prefix 欄位,或整個改用 GNU 的 'L' 或 pax 的 'x' 延伸記錄來表達。 |
| 100 | 8 | mode | 權限位元,寫成 6 位八進位數字後面接一個空白和一個 NUL,例如 '000644\0'(結束字元前有個空白)。只有低 12 位元有可攜性;setuid 和 sticky 活得下來,Linux capabilities 和 ACL 活不下來。 |
| 108 | 8 | uid | 擁有者的數字使用者 id,八進位 ASCII。大於 2097151 的 id 塞不進 7 位八進位數字,GNU 與 pax 變體會存在就是為了這個。對應的名稱另外存在位移 265。 |
| 116 | 8 | gid | 所屬群組的數字 id,編碼相同,溢位上限也相同。用非 root 身分解壓縮時,這個欄位和 uid 欄位都會被默默忽略。 |
| 124 | 12 | size | 成員大小(位元組),11 位八進位數字加一個空白或 NUL——所以純 ustar 成員的上限是 8 GiB 減一。讀取端把這個數字無條件進位到 512 的倍數,加上目前位置,就是下一個標頭的位置。目錄項目這裡存 0。 |
| 136 | 12 | mtime | 修改時間,以 1970 起算的八進位秒數。每個成員就只有這一個時間戳記:沒有建立時間、沒有存取時間、解析度一秒、沒有時區。要記到秒以下就得靠 pax 延伸標頭。 |
| 148 | 8 | chksum | 把 512 個標頭位元組全部相加(無號),計算時這個欄位本身當成 8 個空白,結果寫成 6 位八進位數字、一個 NUL 和一個空白。這是 tar 唯一的完整性檢查——沒有 CRC、沒有雜湊——而且它只涵蓋標頭,從來不涵蓋檔案資料。 |
| 156 | 1 | typeflag | '0' 或 NUL 是一般檔案、'1' 硬連結、'2' 符號連結、'3' 和 '4' 是字元與區塊裝置、'5' 目錄、'6' FIFO、'7' 連續檔案。'x' 和 'g' 帶出 pax 延伸標頭;'L' 和 'K' 是 GNU 的長檔名與長連結記錄。 |
| 157 | 100 | linkname | typeflag 為 '1' 和 '2' 時的目標路徑,以 NUL 補滿,其他情況留空。符號連結指向解壓縮根目錄以外,就是經典的 tar 路徑穿越手法,所以來路不明的壓縮檔一定要看一下這一段。 |
| 257 | 6 | magic | POSIX ustar 是 'ustar' 後面接一個 NUL;GNU tar 的舊變體是 'ustar' 後面接兩個空白。這是整個格式唯一位在固定位置的簽章,而它躺在檔案的第 257 個位元組——所以只讀前 16 個位元組的工具根本認不出 tar。 |
| 263 | 2 | version | POSIX ustar 是 '00'。GNU 變體則是讓 magic 欄位那兩個空白一路延續過來,所以位元組 257 到 264 整段讀起來是 'ustar \0'——這是分辨兩種方言最省事的辦法。 |
| 265 | 32 | uname | 擁有者的登入名稱,NUL 結尾的 ASCII。解壓縮時只要名稱在目標系統上解析得到,就優先採用它而不是數字 uid,所以 uid 501 打包出來的 tar,在同一個人是 uid 1000 的機器上照樣落在正確的擁有者身上。 |
| 297 | 32 | gname | 所屬群組名稱,規則相同。macOS 打包的壓縮檔這裡通常是 'staff' 或 'wheel',Linux 上則常是使用者自己的名字。 |
| 329 | 8 | devmajor | 主要裝置編號,八進位,只在 typeflag 為 '3' 和 '4' 時有意義。一般檔案這裡填零。 |
| 337 | 8 | devminor | 次要裝置編號,編碼與限制同上。tar 能夾帶 /dev 節點就是靠這兩個欄位,其他常見壓縮格式都做不到。 |
| 345 | 155 | prefix | 長路徑的前段。讀取端把 prefix、一個斜線和 name 欄位接起來還原完整路徑,上限因此提高到 255 位元組——但前提是路徑剛好在對的位置有斜線,所以才會有 GNU 和 pax 的長檔名記錄。 |
| 500 | 12 | padding | 補零,把標頭湊到剛好 512 位元組。成員資料從位移 512 開始,本身也會補零對齊到下一個 512 邊界,所以第二個標頭一定落在 512 的某個倍數上。 |
| -1024 | 1024 | End-of-archive marker | 連續兩個全零的 512 位元組區塊代表壓縮檔結束。很多寫入端還會多補,補到 10 KiB 或 20 KiB 的區塊因數,所以結束標記後面常常還有一堆零——但如果最後 1024 個位元組不是全零,這個壓縮檔就是被截斷了。 |
怎麼跟長得像的格式分開
| 容易搞混的 | 怎麼分 |
|---|---|
| GZIP | .tar.gz 是包在 gzip 資料流裡的 tar,所以位移 0 讀到 1F 8B 08,不解壓縮的話哪裡都找不到 'ustar'。兩者混淆到 .tgz 這個副檔名乾脆把兩層合成一個。解掉一層 gzip 之後,結果的位移 257 應該要讀到 'ustar';讀不到的話,你手上的是被 gzip 壓過的別的東西。 |
| ZIP | 兩者都是「裝在一個檔案裡的資料夾」,光看副檔名習慣分不出來,但位元組毫不含糊:位移 0 的 50 4B 03 04 對上位移 257 的 'ustar'。實務上的差別在於 ZIP 有中央目錄、支援隨機存取,tar 只能從頭讀——所以列一個大的 .tar.gz 要付出完整解壓縮的代價,列一個 .zip 只要 seek 一下。 |
| cpio | 另一個 Unix 串流打包工具,每個 RPM 和 initramfs 裡面裝的就是它。可攜的 ASCII cpio 格式在每個成員的邊界處以 ASCII 的 '070701' 或 '070707' 開頭,而不是固定的位移 257,而且完全沒有 512 位元組對齊這回事。 |
| GNU tar/pax | 三者都在位移 257 寫 'ustar',差別全在後面。POSIX ustar 是 'ustar\0' 再接 '00';GNU tar 是 'ustar' 接兩個空白、一路吃進 version 欄位;pax 則是一個 ustar 壓縮檔,額外夾帶 typeflag 為 'x' 或 'g' 的成員標頭,裡面放 key=value 記錄,用來表達長檔名、過大的 uid 和秒以下的時間。忽略這些記錄的讀取端不會報錯,只會默默把那些中繼資料丟掉。 |
常見的壞法
- 傳輸被截斷。因為 tar 是往前串接的資料流,截斷點以前的東西都解得出來,只損失最後一個成員——GNU tar 甚至會報 'Unexpected EOF' 然後保留已經寫出來的部分。檢查方式是最後 1024 個位元組:兩個全零區塊代表是正常結束,不是的話就是被切斷了。
- 壞的是 gzip 那一層,不是 tar。deflate 資料流裡翻掉一個位元組,後面的東西全毀,所以 .tar.gz 會在損壞點失敗,而底下那個完好的 tar 你根本碰不到。這就是改用 .tar.zst、或大型壓縮檔乾脆不壓的實務理由:tar 本身壞得很有分寸,壓縮外殼沒有。
- 位移 148 的標頭檢查碼不符。tar 唯一的完整性檢查只涵蓋那 512 位元組的標頭,所以檔名或大小被翻掉一個位元組會立刻現形,檔案資料被翻掉一個位元組則完全看不出來。壓縮檔可以每個檢查碼都過,然後交給你一個壞掉的檔案。
- 八進位溢位。超過 8 GiB 的檔案,或大於 2097151 的 uid,塞不進位移 124 和 108 的八進位欄位。舊的寫入端會繞回去或寫出垃圾;GNU tar 則是把第一個位元組的最高位元設起來,改用 base-256 二進位,嚴格的 ustar 讀取端會直接拒收。size 欄位第一個位元組是 0x80 的時候,那是這種情況,不是損壞。
- 文字模式傳輸。成員資料裡的換行字元被轉換過,所有標頭和位移都完好如初,所以壓縮檔列得出來、解壓縮過程一句警告都沒有——tar 沒有資料檢查碼可以發現。問題會晚一點才浮現:從壓縮檔裡解出來的執行檔跑不動。
這個格式現在還在用嗎
TAR 是 Unix 上的預設壓縮檔格式,而且看不出有要退位的跡象,理由只有一個:Unix 檔案系統在乎的東西它都保得住。權限、擁有者、符號連結、硬連結、裝置節點、稀疏檔案,走一趟 tar 全都活著,換成 ZIP 沒有一項能乾淨保留。所以原始碼發行檔、容器映像層、備份,以及所有建構在它們之上的套件格式,統統是 tarball。它的弱點是結構性的、補不起來:沒有索引,要列清單就得整份讀完;沒有資料檢查碼,靜默損壞看不出來;壓縮是外掛在整體上而不是逐成員做的,所以 gzip 那層壞掉一個位元組,整個壓縮檔陪葬。ZIP 贏在隨機存取——從 4 GB 的 .tar.gz 裡抽一個檔案,得先解壓縮到那個位置——這正是文件與應用程式容器選 ZIP 的原因。現在的走向不是離開 tar,而是換一層更好的外包裝:.tar.zst 的壓縮與解壓縮速度是 .tar.gz 的好幾倍,壓縮率還更好;體積比時間重要時,.tar.xz 仍然是首選。
在這個站上打開 TAR 檔
這些全部在你的瀏覽器裡跑,檔案不會離開你的電腦。
相關格式
容易和 TAR 搞混的格式,可以比對它們的開頭位元組。