EN JA
LIBARCHIVE_INTERNALS(3)
LIBARCHIVE_INTERNALS(3) FreeBSD Library Functions Manual LIBARCHIVE_INTERNALS(3)

名称

libarchive_internalslibarchive の内部インタフェースの説明

概説

libarchive ライブラリは、tar と cpio のようなストリーミングアーカイブファイルの読み込みと書き込みのための柔軟性のあるインタフェースを提供しています。内部的に、新しいアーカイブと圧縮形式を追加することを簡単にするべきモジュールの階層構造の設計に従っています。

一般的なアーキテクチャ

外部的に、libarchive は、1 つの不透明なオブジェクトスタイルのインタフェースを通してほとんどの操作を明らかにしています。 archive_entry(3) オブジェクトは、単一のファイルシステムオブジェクトに関する情報を格納しています。ライブラリの残りは、アーカイブファイルに archive_entry(3) オブジェクトを書き込む、アーカイブファイルからそれらを読み込む、ディスクにそれらを書き込む機能を提供しています。 (同様にディスクから archive_entry(3) オブジェクトを読み込む機能を追加する計画があります。)

読み込みと書き込みの API には、各々 4 つのレイヤ (層) があります: パブリック API レイヤ、アーカイブファイル形式を理解する書式レイヤ、圧縮レイヤと I/O レイヤです。 I/O レイヤは、それを完全に自分の関数に置き換えることができるクライアントに完全に公開されています。

クライアントのためにできるだけ一貫性を提供するために、いくつかのパブリック関数は、仮想化されています。最終的に、アーカイブまたはディスクのライタ (writer) をオープンするために次に、ターゲットにかかわらず、エントリを選択して書き込むためにコードの単一のセットを使用するクライアントにとって可能であるべきです。

読み込みアーキテクチャ

外側から、クライアントは、アーカイブストリームエントリと本体を読み込むために archive オブジェクトを操作するために archive_read(3) API を使用します。内部で、 archive オブジェクトは、すべての読み込む特有のデータを保持する、 archive_read オブジェクトに割り当てられます。 API には、4 つのレイヤ (層) があります: 最下位のレイヤ (層) は、I/O レイヤです。このレイヤを、クライアントによって上書きすることができますが、ほとんどのクライアントは、例えば、 archive_read_open_memory(3)archive_read_open_fd(3) によって提供されるパッケージ化にされた I/O コールバックを使用します。圧縮レイヤは、バイトを読み込むために I/O レイヤを呼び出し、書式レイヤのためにそれらを圧縮復元します。書式レイヤは、圧縮されていないバイトのストリームをアンパックし、着信データから archive_entry オブジェクトを作成します。 API レイヤは、全面的な状態 (例えば、クライアントがヘッダを読み込む前にデータを読み込むことを防ぎます) をトレースし、登録された関数ポインタを通して書式と圧縮レイヤの操作を呼び出します。特に、API レイヤは、書式検出プロセスをドライブします: アーカイブをオープンするとき、データの最初のブロックを読み込み、それぞれ登録された圧縮ハンドラに、それを与えます。最高のビッド (bid) があるものは、最初のブロックで初期化されます。同様に、書式ハンドラは、どのハンドラが各アーカイブのために最良かを調べるためにポーリングされます。 (2.4.0 より前に、書式ビダー (bidder) は、各エントリのために呼び出されましたが、この設計は、エラーリカバリの邪魔になりました。)

I/O レイヤ (層) とクライアントのコールバック

読み込み API は、クライアントによいいくつかの長さまで進みます。結果として、クライアントのコールバックの振る舞いでの制限はほとんどありません。

クライアントの読み込みコールバックは、各呼び出しでデータのブロックを提供すると予想されます。長さ 0 の返り値は、ファイルの終りを示しますが、その他のブロックは、1 バイトしかないか、またはすべてのファイルと同じくらい大きさがあるかもしれません。特に、ブロックは、サイズが異なっているかもしれません。

クライアントのスキップのコールバックは、要求されたスキップがよりはるかに小さいかもしれない、実際にスキップされたバイトの数を返します。ただ 1 つの要求条件は、スキップがそれほど大きくないということです。特に、クライアントは、それらが扱いたくないあらゆるスキップのために 0 を返すことが許可されます。スキップのコールバックは、決して負の値で呼び出されてはなりません。

すべてのクライアントがディスクから読み込んででいるとは限らないことを心に留めてください: ネットワークから読み込むクライアントは、すべての要求で異なるサイズがあるブロックを提供し、全くスキップすることができません。高度なクライアントは、すばやくメモリ中にすべてのファイルを読み込み、すべてのファイルを単一のブロックとして libarchive に返すために mmap(2) を使用します。他のクライアントは、各要求で次のブロックのための非同期 I/O 操作を始めます。

圧縮復元レイヤ (層)

圧縮復元レイヤは、圧縮復元を扱うだけではなく、書式ハンドラがはるかによい I/O モデルを見ることができるように、データもバッファに入れます。圧縮復元 API は、2 つのステージ、peek/consume (単なる読み込み/消費) モデルです。 read_ahead 要求は、最小の読み込み量を指定します。圧縮復元レイヤは、少なくともそんなに多くのデータへのポインタを提供しなければなりません。より多くのデータが直ちに利用可能であるなら、もっと返すべきです: 書式レイヤは、最小 1 バイトを要求することによって、大量のデータの読み込みを処理し、次に、利用可能であるできるだけ多くのデータをコピーします。

consume() 関数への続く呼び出しは、読み込みポインタを進めます。 read_ahead() 呼び出しから返されたデータは、次の read_ahead() 呼び出しまで、そのまま有効であることが保証されることに注意してください。 consume() への介在する呼び出しで、データを移動させるべきではありません。

スキップ要求は、常に正確に扱われなければなりません。前にシーク出来ない圧縮復元ハンドラは、スキップハンドラを登録するべきではありません。 API レイヤは、データを読み込み廃棄する一般的なスキップハンドラの代わりをします。

圧縮復元ハンドラは、次の特定のライフサイクルがあります:

登録/設定
クライアントがパブリックにサポートされた関数を呼び出すとき、圧縮復元ハンドラは、did と初期化の関数を提供するために内部の __archive_read_register_compression() を呼び出します。この関数は、エラーのとき、 NULL を返し、そうでなければ struct decompressor_t へのポインタを返します。この構造体は、あらゆるカスタマイゼーション情報を格納のために使用することができる void * config スロットを含んでいます。
bid
bid 関数は、データのブロックのポインタとサイズを付けて呼び出されます。圧縮復元ピログラムは、 archive_read オブジェクトの decompressor 要素を通して設定データにアクセスすることができます。 bid 関数は、そうでなければ処理状態なし (stateless) です。特に、それは、あらゆる I/O 操作を行なってはなりません。

bid 関数によって返される値は、このデータストリームを扱うために適合性を示します。 0 の bid は、この圧縮復元ピログラムが決して呼び出されないを保証します。マジック番号のチェックが失敗するなら、0 を返します。そうでなければ、利用者の最初の実装は、実際にチェックされたビットの数を返すべきです。例えば、2 つの全バイトと 3 ビットの別のバイトを検証するなら、bid 19 です。最初のブロックが非常に短いかもしれないことに注意してください。与えられるデータを単に検査するように注意してください。 (現在の圧縮復元ピログラムは、正確な bidding のために 2 バイトを必要とします。)

初期化
勝った bidder は、呼び出される init 関数があります。この関数は、 archive_read オブジェクトの decompressor 要素によって指される struct decompressor_t オブジェクトの残りのスロットを初期化するべきです。特に、その構造の data スロットに必要とするあらゆる作業データを割り付けるべきです。 init 関数は、tasting に使用されたデータのブロックを付けて呼び出されます。この時点で、圧縮復元ピログラムは、クライアントのコールバックへのすべての I/O 要求に対して責任があります。圧縮復元ピログラムは、必要なときに、より多くのデータを読み込むために解放されます。
I/O 要求を満足させる
書式ハンドラは、必要に応じて read_ahead, consumeskip 関数を呼び出します。
終了
終了方法は、アーカイブがクローズされるとき、一度だけ呼び出されます。 decompressor オブジェクトの dataconfig スロットに格納されたものをすべて解放するべきです。クライアントのクローズのコールバックを呼び出すべきではありません。

書式レイヤ

読み込み形式には、圧縮復元ハンドラ対して同様のライフサイクルがあります:
登録
プライベートデータを割り付けて、ポインタを初期化します。
bid
read_ahead() 圧縮復元方法を呼び出すことによって bid を書式化しますが、 consume() 方法を呼び出しません。これによって、各 bidder は、入力ストリームで先読み (look ahead) することができます。 bidder は、多くのデータをバッファに入れるために圧縮復元レイヤで圧力をかける先読みする限り必要なものよりさらに先読みするべきではありません。ほとんどの形式は、数百バイトの先読みを単に必要とします。数キロバイトの先読みは、合理的です。 (ISO9660 リーダは、時々、上限と考えられる、48k まで先読みします。)
ヘッダを読み込む
ヘッダ読み込みは、通常あらゆる形式の最も複雑な部分です。次のように述べる価値がある少数の方法があります: tar または cpio のような形式について、ヘッダを読み込み解析することは、ヘッダがデータと交互にあるので簡単です。ファイルの初めのすべてのヘッダのデータを格納する形式については、最初のヘッダ読み込み要求は、メモリにすべてのヘッダを読み込み、ファイルのデータの位置によってソートされるデータを格納しなければなりません。続くヘッダ読み込み要求は、ファイルのデータの始めの前方にスキップし、対応するヘッダを返します。
読み込みデータ
読み込みデータのインタフェースは、スパースファイルをサポートします。これは、それぞれの呼び出しが、ファイルオフセットとサイズを指定するデータのブロックを返すことを必要とします。これは、各読み込みのために正確なファイルのオフセットを返すことができるように、注意深く位置を追跡することを要求します。圧縮復元プログラムは、それが持っているのと同じ量のデータを返すことを覚えておいてください。一般的に、利用者は、1 バイトを要求し、どれだけ多くのデータが利用可能か確かめるために返り値を調べ、恐らく、使用することができる量にそれを整えたいでしょう。利用者は、ちょうどそれを返す前に、各ブロックのための消費を呼び出すべきです。
すべてのデータをスキップする
スキップするデータの呼び出しは、すべてのファイルデータと後続するパディングをスキップするべきです。これは、各ヘッダの読み込みの直前に API レイヤによって自動的に呼び出されます。また、パブリックな data_skip() 関数を呼び出すクライアントに応えて呼び出されます。
クリーンアップ
クリーンアップにおいて、書式は、その割り付けられたメモリをすべて解放するべきです。

API レイヤ

XXX 行う XXX

書き込みアーキテクチャ

書き込み API は、4 つのレイヤの同様のセットがあります: API レイヤ、書式レイヤ、圧縮レイヤと I/O レイヤです。ここで、一度に 1 つの書式と 1 つの圧縮だけを登録することができるので、登録は、はるかに簡単です。

I/O レイヤとクライアントのコールバック

XXX 書き込むこと XXX

圧縮レイヤ

XXX 書き込むこと XXX

書式レイヤ

XXX 書き込むこと XXX

API レイヤ

XXX 書き込むこと XXX

WRITE_DISK アーキテクチャ

write_disk API は、ちょうどクライアントへの書き込み API のように見えることを目的としています。複数の書式と圧縮を扱わないので、内部的に階層化されていません。

一般的なサービス

archive_read, archive_writearchive_write_disk オブジェクトはすべて、1 組の標準サービスのための共通のサポートを行う最初の archive オブジェクトを含んでいます。 (構造体へのポインタとその構造体の最初の要素へのポインタ間で自由に cast できる、ANSI/ISO C90 保証を思い出してください。) archive オブジェクトは、このオブジェクトがどの API に関連しているか示すマジックの値、エラー情報を格納するためのスロットと仮想化された API 関数のための関数ポインタがあります。

雑多な注意

ライブラリに既存のアーカイブライブラリを結合することは、一般的に非常に困難です。特に、多くの既存のライブラリは、利用者がファイルから読み込んでいると強く仮定します。それらは、様々な情報を見つけるために必要なときに、前方と後方にシークします。対照的に、libarchive は、時々たいへん異なるアプローチを要求する、入力で決して後方にシークしません。

例えば、libarchive の ISO9660 サポートは、ほとんどの ISO9660 リーダとはたいへん異なって動作します。 libarchive サポートは、入力でそれらの位置によってソートされる既知のエントリのリストを保持するように設計された作業キューを利用します。 libarchive の ISO9660 の実装は、次のヘッダを要求されるときはいつでも、ディスク上の次の項目を見つけるために、このリストをチェックします。ディレクトリは、それらに遭遇し、新しい項目がリストに追加されるとき、解析されます。この設計は、ディレクトリが、それらが記述するファイルより以前にディスクに常に生じるように最適化されている ISO9660 イメージに極度に依存します。

特定の形式によって、そのようなアプローチは、可能ではないかもしれません。 ZIP 形式の仕様は、例えば、アーカイバがファイルの終りでのみキーの情報を格納できます。理論的には、シークせずに読み込むことができない ZIP アーカイブを作成することは可能です。幸運にも、そのようなアーカイブは、非常にまれで、 libarchive は、ほとんどの ZIP アーカイブを読み込むことができますが、専用 ZIP プログラムと同じくらいの情報を常に抽出することはできません。

歴史

libarchive ライブラリは、 FreeBSD 5.3 ではじめて登場しました。

作者

libarchive ライブラリは、 Tim Kientzle <kientzle@acm.org>によって書かれました。
January 26, 2011 FreeBSD