0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

古いlibarchive(含cmake)ではapache commons compressで作成された7z書庫を展開できない

Last updated at Posted at 2020-08-13

はじめに

私が提出した https://github.com/libarchive/libarchive/pull/1295 について、すでに解決済みではあるが、より詳細な調査をしたくなった。

圧縮エンジン

まず、teamcityのアーカイブのファイルリストを眺めたところ、TeamCity/buildAgent/lib/commons-compress.jarなるファイルが見つかった。これはapache commons compressである。

書庫ファイル

apache commons compressを使って書庫を作成してみた( https://github.com/cielavenir/libarchive_1295_additional_check )。
Main.java/writer.sh/test.sh。writer.shで直接Main.javaを実行しているのはJava11以降の機能だが一旦classにすれば古いJavaでも十分実行可能なはずである。test.7zとして用意した。

このメタデータは以下のようになる。

01 04 06 00 02 09 8a e4 81 a9 0a 01 9e 89 a2 ea
55 5a 93 0e 00 07 0b 02 00 01 21 21 01 16 01 21
21 01 16 0c 9b 88 83 c7 0a 01 3a 27 02 9b 04 42
ad a8 00 08 00 00 05 02 11 2d 00 6c 00 69 00 63
00 65 00 6e 00 73 00 65 00 2e 00 74 00 78 00 74
00 00 00 4d 00 61 00 69 00 6e 00 2e 00 6a 00 61
00 76 00 61 00 00 00 14 12 01 00 80 6f d4 8c 67
01 d6 01 b0 3a 26 fa ed 6f d6 01 00 00

これを7zFormat.txtに従って頑張って解読すると以下のようになる。

0x01(kHeader) {
  0x04(kMainStreamsInfo) {
    0x06(kPackInfo) {
      0x00(PackPos)
      0x02(NumPackStreams)
      0x09(kSize) [
        0x8ae4 -> 2788
        0x81a9 -> 425
      ]
      0x0a(kCRC) 0x01(AllAreDefined) [
        0xeaa2899e
        0x0e935a55
      ]
      0x00(kEnd)
    }
    0x07(kUnPackInfo) {
      0x0b(kFolder)  {
        0x02(NumFolders)
        0x00(External)
        [
           0x01(NumCoders)
           0x21(CodecIdSize=1, Attributes)
           0x21(Codec=LZMA2)
           0x01(PropertiesSize)
           0x16(Properties)
        ]*2
        0x0c(kCodersUnPackSize) [
          0x9b88 -> 7048
          0x83c7 -> 967
        ]
        0x0a(kCRC) 0x01(AllAreDefined) [
          0x9b02273a
          0xa8ad4204
        ]
        0x00(kEnd)
      }
      0x08(kSubStreamsInfo) {
        0x00(kEnd)
      }
      0x00(kEnd)
  }
  0x05(kFilesInfo) {
    0x02(NumFiles)
    0x11(kNames)
    0x2d(Size)
    0x00(External)
    [
      L"license.txt"
      L"Main.java"
    ]
    0x14(kMTime)
    0x12(Size)
    0x01(AllAreDefined)
    0x00(External)
    [
      0x01d601678cd46f80 -> 2020-03-24 08:05:31
      0x01d66fedfa263ab0 -> 2020-08-11 23:44:54
    ]
    0x00(kEnd)
  }
  0x00(kEnd)
}

filetime変換は次のようになる。

def filetimeToUTC(n)
  Time.at n/10000000-11644473600
end

また、可変長整数は、1バイト目の補数のclzバイト分リトルエンディアンが続く。例えば1110xxxxyyyyyyyyzzzzzzzzwwwwwwwwというビット列はxxxxwwwwwwwwzzzzzzzzyyyyyyyyという数を表す(まあ、手動でやるなら切れ目がどこかは目視でだいたい分かる)。

なお、7z.exeで複数ファイルを圧縮すると0x01(kHeader)ではなく0x17(kEncodedHeader)が使われるが、今回は割愛する。

libarchive 3.4.0以下の問題

kPackInfo中にCRCが出てくることがある。展開後ではなく圧縮ストリーム自体のCRCを記録するものである。これはヘッダ中ではkCRC(0x0a)に続くものであるが、libarchiveではkSize(0x09)に続くものとする処理になっていた。まあ、commons compressと違って7z.exeは圧縮ストリーム自体のCRCは記録しないので、気づかなくてもしかたない。

ただ、teamcity/bsdtarだけでなく、apache commons compress全体/cmakeでも再現されるので、実際には影響は当時の想定より大きかったはずである。cmakeは 5d8b3aec0cb8652ae867ff08d2e7bfa2060138dd より古いバージョンすなわり3.18未満が影響を受ける。3.18って(2020年8月時点で)最新版じゃないですか--;;;

古いlibarchiveで実際に確認してみる

bsdtarではtar tf test.7z、cmakeではcmake -E tar tf test.7zとして確認。開けなければ、前者はtar: Damaged 7-Zip archive、後者はCMake Error: Problem with archive_read_next_header(): Damaged 7-Zip archiveと表示される。

環境 結果
Windows10 bsdtar 3.3.2 NG
Windows10 cmake 3.17.4 NG
Windows10 cmake 3.18.1 OK
macOS10.14 bsdtar 3.4.31 OK
macOS10.14 cmake 3.18.1 OK
Debian9 bsdtar 3.4.02 NG
Debian9 bsdtar 3.4.23 OK
Debian9 cmake 3.7.2/libarchive 3.4.0 NG
Debian9 cmake 3.7.2/libarchive 3.4.24 OK

想定通り再現された。

py7zlib

7zを展開するPure Python実装があるようだ。 これは圧縮ストリームのCRCをkCRCで受けることはあっていたが、中身の読み取りが誤っていた。 https://github.com/fancycode/pylzma/pull/74 にて修正を試みた。

おわりに

apache commons compressのやつを食わせると死ぬデコーダは他にもありそう。

というか7zは仕様が複雑すぎるのでまともなデコーダを書くのはほぼ不可能ではないだろうか。。

  1. Homebrew。システムのlibarchiveは2.8.3であり、そもそも7zを開けない

  2. stretch-backports

  3. dependencyさえ気をつければDebian11のlibarchiveを導入可能

  4. Debian版のCMakeはdynamic binaryなのでlibarchive.soを交換可能

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?