ByteScope

TAR 檔案格式

POSIX 的 tape archive——未壓縮、把檔案循序串起來的容器。

MIME 類型
application/x-tar
副檔名
.tar
分類
壓縮與封存

Magic number(檔案簽章)

0x000001017573746172ustar'ustar' marker in the first header

關於 TAR 格式

TAR 是一串 512 位元組的區塊:每個成員先一個標頭區塊,接著是成員內容並補零對齊到下一個 512 邊界,如此重複,直到連續兩個全零區塊代表結束。沒有壓縮、沒有索引、沒有目錄——這就是為什麼 .tar 幾乎一定會再包一層 gzip、xz 或 zstd,也是為什麼想列出 .tar.gz 的內容就得整個解壓縮一遍。標頭裡每個數值欄位都是用 ASCII 八進位數字寫的,不是二進位;這個 1979 年的決定讓你不靠任何工具、光在 hex dump 裡就讀得懂 tar 標頭。

檔頭結構,一個位元組一個位元組看

位移長度欄位意義
0100name成員的路徑,以 NUL 補滿的 ASCII,相對路徑、用斜線。目錄會存成結尾帶斜線的形式。超過 100 位元組的路徑必須拆到位移 345 的 prefix 欄位,或整個改用 GNU 的 'L' 或 pax 的 'x' 延伸記錄來表達。
1008mode權限位元,寫成 6 位八進位數字後面接一個空白和一個 NUL,例如 '000644\0'(結束字元前有個空白)。只有低 12 位元有可攜性;setuid 和 sticky 活得下來,Linux capabilities 和 ACL 活不下來。
1088uid擁有者的數字使用者 id,八進位 ASCII。大於 2097151 的 id 塞不進 7 位八進位數字,GNU 與 pax 變體會存在就是為了這個。對應的名稱另外存在位移 265。
1168gid所屬群組的數字 id,編碼相同,溢位上限也相同。用非 root 身分解壓縮時,這個欄位和 uid 欄位都會被默默忽略。
12412size成員大小(位元組),11 位八進位數字加一個空白或 NUL——所以純 ustar 成員的上限是 8 GiB 減一。讀取端把這個數字無條件進位到 512 的倍數,加上目前位置,就是下一個標頭的位置。目錄項目這裡存 0。
13612mtime修改時間,以 1970 起算的八進位秒數。每個成員就只有這一個時間戳記:沒有建立時間、沒有存取時間、解析度一秒、沒有時區。要記到秒以下就得靠 pax 延伸標頭。
1488chksum把 512 個標頭位元組全部相加(無號),計算時這個欄位本身當成 8 個空白,結果寫成 6 位八進位數字、一個 NUL 和一個空白。這是 tar 唯一的完整性檢查——沒有 CRC、沒有雜湊——而且它只涵蓋標頭,從來不涵蓋檔案資料。
1561typeflag'0' 或 NUL 是一般檔案、'1' 硬連結、'2' 符號連結、'3' 和 '4' 是字元與區塊裝置、'5' 目錄、'6' FIFO、'7' 連續檔案。'x' 和 'g' 帶出 pax 延伸標頭;'L' 和 'K' 是 GNU 的長檔名與長連結記錄。
157100linknametypeflag 為 '1' 和 '2' 時的目標路徑,以 NUL 補滿,其他情況留空。符號連結指向解壓縮根目錄以外,就是經典的 tar 路徑穿越手法,所以來路不明的壓縮檔一定要看一下這一段。
2576magicPOSIX ustar 是 'ustar' 後面接一個 NUL;GNU tar 的舊變體是 'ustar' 後面接兩個空白。這是整個格式唯一位在固定位置的簽章,而它躺在檔案的第 257 個位元組——所以只讀前 16 個位元組的工具根本認不出 tar。
2632versionPOSIX ustar 是 '00'。GNU 變體則是讓 magic 欄位那兩個空白一路延續過來,所以位元組 257 到 264 整段讀起來是 'ustar \0'——這是分辨兩種方言最省事的辦法。
26532uname擁有者的登入名稱,NUL 結尾的 ASCII。解壓縮時只要名稱在目標系統上解析得到,就優先採用它而不是數字 uid,所以 uid 501 打包出來的 tar,在同一個人是 uid 1000 的機器上照樣落在正確的擁有者身上。
29732gname所屬群組名稱,規則相同。macOS 打包的壓縮檔這裡通常是 'staff' 或 'wheel',Linux 上則常是使用者自己的名字。
3298devmajor主要裝置編號,八進位,只在 typeflag 為 '3' 和 '4' 時有意義。一般檔案這裡填零。
3378devminor次要裝置編號,編碼與限制同上。tar 能夾帶 /dev 節點就是靠這兩個欄位,其他常見壓縮格式都做不到。
345155prefix長路徑的前段。讀取端把 prefix、一個斜線和 name 欄位接起來還原完整路徑,上限因此提高到 255 位元組——但前提是路徑剛好在對的位置有斜線,所以才會有 GNU 和 pax 的長檔名記錄。
50012padding補零,把標頭湊到剛好 512 位元組。成員資料從位移 512 開始,本身也會補零對齊到下一個 512 邊界,所以第二個標頭一定落在 512 的某個倍數上。
-10241024End-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 搞混的格式,可以比對它們的開頭位元組。

格式開頭位元組副檔名
GZIP1F 8B.gz
BZIP242 5A 68.bz2
XZFD 37 7A 58 5A 00.xz
ZIP50 4B 03 04.zip

常見問題

檔案的 magic number 是什麼?

檔案開頭的前幾個位元組,不管檔名寫什麼,它標明的是真正的格式。PNG 一定以 89 50 4E 47 開頭,ZIP 則是 50 4B 03 04。作業系統、瀏覽器和 file 指令讀的都是這幾個位元組,而不是相信副檔名 — 這就是為什麼把檔案改名並不會改變它實際上是什麼。

我要怎麼自己檢查 magic number?

在 macOS 或 Linux 上,xxd -l 16 檔名 會印出前 16 個位元組;在 Windows 上,PowerShell 的 Format-Hex -Path 檔名 -Count 16。或者把檔案丟進本站的檔案類型辨識工具,它會在本機讀取位元組並告訴你是什麼格式,不會上傳任何東西。

為什麼兩種格式的 magic number 會一樣?

因為其中一種是建立在另一種之上。docx、xlsx、pptx、apk、jar 與 epub 骨子裡都是 ZIP 壓縮檔,所以全都以 ZIP 的簽章開頭;要分辨它們得看裡面的項目。Java class 檔和 macOS 的通用二進位檔都以 CA FE BA BE 開頭,靠緊接其後的欄位區分。

有完全沒有 magic number 的檔案嗎?

有。XML、HTML、JSON、SVG、CSV 這類以文字為基礎的格式沒有固定的位元組簽章,所以改用結構來辨識:開頭的 <?xml 宣告、<svg 元素,或是 { 後面接著帶引號的鍵。這種偵測是合理推測而非確定,因此本參考頁會把這些格式標示為結構偵測。

magic number 相符就代表檔案安全嗎?

不代表。它告訴你的是格式,不是內容是否無害 — 一個貨真價實的 PDF 或 docx 仍然可能夾帶惡意內容。辨識真實類型是有用的第一道檢查,尤其在副檔名和位元組不一致的時候,但它不是惡意軟體掃描。

手上有檔案可能是 TAR?用檔案類型辨識工具查出來 — 它在本機讀取 magic number,不上傳任何東西。或者瀏覽所有格式。