PicasaのDBを何とか解読したい (サムネイルファイル編)
はじめに
以下ではWindows用のツールを数多く利用しています。というのも個人的にLinux用のよいバイナリエディタなどを知らないからです。なのでWindows上のバイナリエディタでデータを眺めながらLinux上でHaskellのコードを書くというような面倒なことをしていました。だれかUbuntuで簡単に手に入り高機能なバイナリエディタ知ってたら教えてください。
欲しい物が多すぎて困る
前回id:xxxxxeeeee:20121214:1355454193でPMPファイルを解析することに成功したわけだが、我々が手に入れたデータは実のところ完全なるメタデータのみであった。平均色・幅・高さ・顔認識結果…しかしそんなものが欲しいのではない。我々が欲しいのは「あるファイルに対応するサムネイル」これにつきる。さて、これをどうやって探したものか?
まずファイルを色々眺めてみるとpmpファイル(と_0ファイル)を除くとPicasaには以下のファイルがあることが分かる。
- bigthumbs_0.db
- bigthumbs_index.db
- facetemplatesV2_0.db
- facetemplatesV2_index.db
- previews_0.db
- previews_index.db
- thumbindex.db
- thumbs2_0.db
- thumbs2_index.db
- thumbs_0.db
- thumbs_index.db
ここで仲間はずれを見つけてくださいと問われたら、誰もがthumbindex.dbを挙げるだろう。他のファイルは_0と_indexがペアになっているからだ。しかし、当初私はこの「いかにも怪しいファイル」を覗いてみることなくまずファイルサイズに注目した。実際のファイルサイズはさておき、***_0.dbのファイルが大きい。100Mとか1Gとかある。なのでこいつがJPEGのサムネイルてんこ盛りファイルだという気がしてくる。そこで、http://homepage3.nifty.com/kamisaka/JpegAnalyzer/というソフトを使ってみよう。このソフトはJPEGの内部を(マーカーとかセグメントとか)そのまま見せてくれるのみならず、一部をコピーアンドペーストできたりとJPEGのいかがわしい操作には必携である。さて、見てみただろうか?そうすると、なんとこの_0.dbファイルたちは最初4バイト以外はSOIというJPEGの開始コードから始まりEOI、同じく終了コードで終わる、要はJPEGのバイナリを延々と列挙したものであることが分かる。やった、これをSOIからEOIに切り出せばいい!終 了
しかしよく考えて欲しい。その切り出したJPEGは素のExifさえないJPEGである。見れば何が入っているかは分かっても、見ないと何かはわからない。10枚程度なら一枚一枚調べてもいい、しかし「私の画像ファイルは4万8千です」という状況ではなすすべもない。そしてようやく先程の怪しいファイルを調べることを思いついた。
thumbindex.dbの構造
ではthumbindex.dbを調べることにする。まず、バイナリエディタとしてBinEditの詳細情報 : Vector ソフトを探す!とMoontailの詳細情報 : Vector ソフトを探す!を推奨しておく。後者のほうが新しく機能もたくさんあるのだが、なぜか私の環境では検索ができないという致命的な問題があるので前者を検索用に利用している。さて、Moontailが優れている理由は色々あるが、まずはUTF-8をちゃんと表示できる点である。thumbindex.dbには結論からいうとUTF-8でパス名が埋め込まれている。これは「どうせGoogleだからUTF-8だろう」とか決めつけつつUTF-8表示をすれば一目瞭然に分かることだが、UTF-8表示できないエディタだと相当苦労することになるだろう。
閑話休題、UTF-8のパス名はどうやらNULL終端の文字列のようだ。そしてNULL終端の文字列の間には30バイトのデータが入っていた。最後まで通してみてみると、どうやら全体の構造としては66 66 46 40と言う謎のヘッダ、次に4バイトのリトルエンディアン(今後特記なければというか全部リトルエンディアン)でデータ長、そのあとはNULL終端文字列と30バイトのデータが延々と繰り返されているらしい、ということにたどり着いた。
そこで、このパス名と30バイトのデータをtsv(tab separated value)に吐いて表計算ソフトに食わせてじっとにらめっこする。すると、色々あって
- 最後の4バイトは連続して同じ値が出ているようだ。
- しかしディレクトリの場合は0xffffffffである
- 前の16バイトはものによっては全く同じ8バイトが繰り返されている。
- 直観で考えるとこれはファイルの作成日時と更新日時が格納されていると考えるのが妥当である。
と言う事がわかる。なのでこれらをそれぞれ普通の整数値として特別扱いしてみることにする。前の16バイトは8バイトunsigned二つとして、後の4バイトはsigned一つとして、もう一度表計算ソフトで眺める。更に少しずるをして元のファイルの作成日時と更新日時を調べる。すると、もう少しちゃんとしたことが分かる。
- 後ろの4バイトに非負の値が入っている場合、この値はそのファイルがどのディレクトリに属しているかをthumbindex.db内のインデックスで表しているらしい。
- 日時の差分を取って調べたところ作成日時と更新日時はどうやらある基準日時からの100 nano sec.オーダーでの経過時間が入っているらしい。
更に30-16-4=残りの10バイトも興味深く、初めの4バイトは不明、5バイト目はファイルタイプを表現しているらしいというところまで突き止めた。しかし、このファイル名+extraの羅列とJPEGバイナリの単純結合の間にどのような写像が存在しているかは未だ謎になっている。
いよいよthumbs_index.dbを調べよう
今回はとても本能的な調査方法に頼った。右の画像はMoontailでthumbs_index.dbを開いたところである。なにか感じないだろうか?そう、ファイルが三パートにわかれているのである。そしてまず最後のパートの先頭に飛んでみた。すると、JPEGのSOIの位置のような値が書いてある気がする。しかし、どうにもずれている。
も一個ついでにこのパートの長さを調べてみると"ほぼ"thumbindex.dbのエントリの個数*4バイトであった。ほぼ、となると「厳密に」それだけの長さを測って逆算してやる。するとどっこい、その直前にthumbindex.dbのエントリの個数がそのものズバリ含まれているではないか!後はそのエントリを延々ととるだけである。結論からいうと、頭にCD CC CC 3F 00 00 00 00というヘッダがついた後に4バイトのエントリ個数+4バイトの数値(エントリ個数と一致)と言う望ましいステップが始まる。このうち最後のブロックはSOIの位置のようなものが入っていたが、最初のものは4ずれていて、次以降はてんであたってない。しかし、なんと真ん中のブロックも同様にSOIの番地が書いてあるではないか!しかもコチラは寸分違いもせず、更に2番目、3番目の値も至当なものである。となると、最後のブロックには実はサムネイル1つずつのファイルサイズが書かれているということになる。
終局
というわけでいろんな解析をかけた結果「パス名」と「サムネイルのオフセットと長さ」が分かったわけでもういよいよ後はサムネイルの抽出……をする暇がないんだよなぁ。
PicasaのDBを何とか解読したい (PMPファイル編)
PicasaのDBとはなにか?
Google PicasaとはGoogleの画像管理ツールないしwebサービスであり、デジカメの画像を管理するのに比較的良いインターフェースを持っている、というのをあらためて解説する必要はないだろう。そのPicasaは自身の管理するデータに関するメタデータを、Windowsで言うところの%LOCALAPPDATA%\Google\Picasa2\db3
においている。
DBは大きく分けて二種類のファイルに分かれていて、片方はpmpファイルであり、もう一方はdbファイルである。
なぜ読みたいのか
HDDが壊れたのでせめてPicasaのサムネイルを復元したいからです。
PMPファイルの読み方
今回はdbファイルを読むには至らなかった。欲しいサムネイルはdbファイルに入っていて、更にindexではない方のdbファイルにはJFIF形式のJPEG画像が延々と入っているらしいことを確認している。しかし、これはただのJPEGの連続であり、もう一方のfoobar_index.dbというものを読まなければメタ情報は手に入らない。このファイル形式はThumbs.dbと同じであるという主張を確認できるが事実かはわからない。
そこで急がばまわれ、pmpファイルの方を解析してみることにした。
http://sbktech.blogspot.jp/2011/12/picasa-pmp-format.html
実際の実装
ただただ上のURLにあるバイナリのパーサを忠実に作る。そこにあるJavaのファイルよりだいぶ簡略にかけていると思う。
https://github.com/liquidamber/PicasaDB
レンタカー@12/05
- 出発
- 18:37
- 帰着
- 翌16:23
- 走行距離
- 81km
- ガソリン
- 785円, 5.49L (単価143)
- 燃費
- 14.8km/L
- 車種
- スイフト
メモ
本当にメモ。
- JPEGのIPTC規格, keyword
- 抽出するライブラリ?;でsplitするだけか?
- 書き込みが出来る必要がある。
- IPTC規格ちゃうやけんけ!
- HDDは大きいのでバックアップできないわけではない
- keywordの依存関係の定義
- DAG
- optional / must dependency
- group
レンタカー@10/17
- 出発
- 20:34
- 帰着
- 翌06:57
- 走行距離
- 104km
- ガソリン
- 1122円, 7.96L
- 燃費
- 13.1km/L
- 車種
- デミオ
/usr/以下が全てなくなった時にどうすればいいのか
その惨状の発見まで
Ubuntuの起動が遅いと思ってから、/usr/以下がほぼ消失していることに気づくには時間はかからなかった。コンソールでログインしようとしてもシェルが起動するまでの間にとてつもなく大量のエラーを吐いていたからだ。とにかくログインはできたので見てみると/homeは生きていたのでとりあえず安心したものの、/usr/以下には/usr/src/以外何もないという状態だった。
対処方法
Live CDでの起動
こうなってしまってはどうしようも無いのでとりあえずWindowsなどからUbuntuのLive CDをダウンロードする。これをCDに焼きたいところだが面倒なのでSDカードに放り込んでGRUB2から直接起動しよう。以下のコマンドをGRUB2の画面上で入力。
loopback loop (hd0,1)/ubuntu-ja-12.04-desktop-i386.iso linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=/ubuntu-ja-12.04-desktop-i386.iso noprompt noeject initrd (loop)/casper/initrd.lz boot
あっさり立ち上がった。
ファイルの復旧(失敗)
次にextundeleteというソフトウェアで復旧を試みる。うまく行くかは全くわからないが。
sudo apt-get install extundelete sudo extundelete --restore-directory '/usr/' --output-dir "/media/2fb1e6b8-2a7b-405c-9bcc-d602f7baf9c2/" --after `date -d "2012-09-04T08:18:00" +%s` /dev/sda5 #失敗 sudo extundelete --restore-all --output-dir "/media/2fb1e6b8-2a7b-405c-9bcc-d602f7baf9c2/" /dev/sda5 #失敗
全く何も引っかからなかった。
aptによる復旧
幸い飛んだファイルは/etc/でもなく/home/でもなく、/usr/でありこれはパッケージに含まれるファイルそのままである。つまり、/usr/のファイルはaptを駆使すれば理論上回復できるべきである。以下元のファイルシステムの/を/media/root/にマウントして行なっている。
まず、/usr/以下にファイルをインストールするパッケージを検索した。
~$ dpkg-query --admindir=/media/root/var/lib/dpkg -S "/usr/" | tr ',' ' ' > /tmp/pkglist ~$ vim /tmp/pkglist # 末尾の余計な文字列を削除する
なぜかdpkgはadmindirやinstdirという形で/以外へのインストールができるのに対してaptはそのような器用なことはできないようだ。なのでchrootを試みる。(rbind: http://d.hatena.ne.jp/tmatsuu/20101225/1293262061)
~$ sudo mount --rbind /dev /media/root/dev ~$ sudo mount --rbind /sys /media/root/sys ~$ sudo mount -t proc none /media/root/proc ~$ sudo mount --bind /tmp /media/root/tmp ~$ sudo chroot /media/root/ /# apt-get bash: apt-get: command not found
oops...というわけで、必要なファイルをコピーしておこうかと思ったが、思ったより多くのファイルが必要なので/usr/以下をごっそりコピーする。
後でエラーが出るとがっかりするのでhaskell-platformやemacsをインストールしてある人はLiveCD側でもインストールしておこう。
sudo cp -Rd /usr/{bin,include,lib,local,sbin,share,games} /media/root/usr/
これでとりあえず動くようになった。
tex関連: tex-commonを先に再インストールする。
/# apt-get install --reinstall `cat /tmp/pkglist` | tee hoge.log Reading package lists... Done Building dependency tree Reading state information... Done Package gnome-themes is not available, but is referred to by another package. This may mean that the package is missing, has been obsoleted, or is only available from another source ...(略) E: Package 'gnome-themes' has no installation candidate E: Package 'gnome-themes-more' has no installation candidate E: Package 'python2.6' has no installation candidate
ダミーパッケージを含んでいたようだ。怒られたパッケージを削除してもう一度やり直す。しかし、aptは特定のクエリを与えるとSegmentation Faultしてしまうことがわかったのだ。
apt-get install --reinstall libtcltk-ruby1.9.1 debconf libpam-modules-bin libpam-modules gconf-service-backend gconf-service
何も言わずに落ちるからビビるけどたしかに死んでいる。こういうこともあるので、全部まとめてreinstallしようという試みは成功しないことが多い。故に適当に分割して食わせていくしかない。
また、依存関係があるパッケージは依存されている方から再インストールしたほうがいい。なのでエラーが出たらそれを疑って依存されてる方から再インストールしてみよう。こういう泥臭いことをやり続ければうまく行きました!
--no-download --ignore-missing #0 0xb75ef744 in pkgCache::DepIterator::IsIgnorable(pkgCache::PkgIterator const&) const () from /usr/lib/i386-linux-gnu/libapt-pkg.so.4.12
最後に
結局「なぜ/usr/が飛んだのか」がわからないと正直怖くて使いたくない。恐る恐る使っています。なんで飛んだんだろうか。
*1:puppy linuxでファイルの復旧をしようとしていたのでこれは嘘です