Dropboxの「オンラインのみ」ファイルは、シェルから読めない


Dropboxの「オンラインのみ」は、ローカルには名前とサイズだけを置いて、中身はクラウドに残しておく状態です。Finderで開けば裏で実体が落ちてくるので普段は困りません。困るのはDropboxアプリを通さないとき。シェルやスクリプトから直接開くと Resource deadlock avoided(EDEADLK)で失敗します。

注意点は失敗そのものではなく、失敗の隠れ方です。shasum はこのエラーを握りつぶし、0バイトを読んだことにして空データのハッシュ(e3b0c442…)を返します。空と空は一致するので、空ファイルをコピーしてもハッシュ照合は通る。バックアップや別ドライブへの移行でオンラインのみファイルを扱うなら、先にフォルダを右クリックして「ダウンロード」で実体を落とし、それからハッシュを取ってください。ファイルサイズだけはローカルに残っているので、そちらは信用できます。

同期の設定を変えたいだけなら、この先は関係ありません。以下は、私がその穴に片足を突っ込んで戻ってきたときの記録です。


所長のDropboxを解約すると決めた話は、少し前に書きました。ただし「解約する」と「消す」の間には、確かめる工程があります。クラウドにしか無いファイルを、消す前にちゃんと別の場所へ退避できているか。ここを飛ばすと、家族の記録や仕事の書類が静かに消えます。

なので私は、古い業務書類のフォルダを一つ、NASと突き合わせていました。数千件あります。Dropbox側とNAS側で、同じファイルが本当に同じ中身か。目視は無理なので、ハッシュで照合しました。ファイルの中身から計算する指紋のようなもので、1バイトでも違えば別の値になる。同じ値なら中身は同一。これ以上に確実な照合はありません。

……そのはずでした。

1,221件が「違う」と出た

照合の結果、1,221件がDropboxとNASで一致しないと出ました。

内訳を見ると、NAS側にそもそも入っていないファイルが大半でした。古い年代の書類がごっそり欠けている。「NASへの退避が中途半端だったんだな、じゃあこの1,221件を追加でコピーすればいい」。そう判断しかけました。

判断しかけて、手を止めたのは、3件だけ様子が違ったからです。

その3件は、NASにもDropboxにもあってサイズもほぼ同じなのに、ハッシュだけが違う。中身が書き換わっているように見える。念のためDropbox側のハッシュ値を眺めて、私は嫌な予感がしました。

e3b0c44298fc1c149afbf4c8996fb924...

この値、見覚えがあったんです。空データのSHA-256。長さゼロのファイル、つまり「何も無い」を指紋にすると、必ずこの値になります。

Dropbox側が、空っぽのハッシュを返していました。

「違う」んじゃない、「読めてない」

中身が違うんじゃありませんでした。1バイトも読めていなかったんです。

原因はすぐ分かりました。シェルからそのファイルを開こうとすると、こう出ます。

Resource deadlock avoided

Dropboxのオンラインのみファイル(実体はクラウドにあって、ローカルには見出しだけ置いてあるやつ)を、Dropboxアプリを経由せずシェルから直接読もうとすると、この EDEADLK というエラーで失敗する。ファイルの実体を今すぐ引っ張ってこられないからです。

問題はここからです。shasum は、この失敗を握りつぶします。 「読めませんでした」とは言わない。読めなかった=0バイトを読んだ、として、しれっと空データのハッシュを返してくる

だから私の照合は、根本から無効でした。「中身が違う3件」は「1バイトも読めていない3件」だったし、そもそも1,221件全部が同じ穴の可能性があった。ハッシュが違うんじゃなく、Dropbox側が軒並み空を返していただけかもしれない。

一番怖かったのは、この先です

もしここで気づかず、「1,221件をNASにコピー」を実行していたら、何が起きたか。

Dropboxから読めるのは0バイトです。つまりNASに、空っぽのファイルが1,221個書き込まれる。しかもこれは上書き事故じゃない。NASには元々無かったファイルなので、「空ファイルが、正しいデータの顔をして新しく置かれる」。パッと見、ファイルはちゃんと在る。名前も揃っている。開くまで空だと分からない。

そして――ここが前回の記事の最後に予告した罠なんですが――もし後でハッシュで検証していたら、その検証は通ってしまう。コピー元の空ファイルと、コピー先の空ファイル。どちらも「何も無い」の同じ指紋を返す。空と空は、完璧に一致する。私は1,221件の消えたデータに「照合OK」の判子を押して、安心して原本を消していたでしょう。

ハッシュが一致したら中身は同じ、という一番信じていた前提が、両方とも空のときだけ静かに裏切る

助かった理由は、別のメタデータ

救いは、ファイルのサイズだけは正しかったことです。

サイズは中身を読まなくても分かる情報(メタデータ)で、これはDropboxがローカルに保持しています。だから「どのファイルがNASに欠けているか」を名前とサイズで割り出した1,221件のリスト自体は、正しかった。壊れていたのは「中身を読んで比べる」操作だけ。

やり直しの手順はこうしました。コピーの前に、Finderで対象フォルダを右クリックして「ダウンロード」し、全ファイルの実体を確実にローカルへ落とす。落ちきってから、初めてハッシュを取る。実体化した3件を計算し直したら、セルの中身まで完全一致でした。バイト差は保存時の書式メタデータだけ。所長の「NAS優先で残す」判断は正しかった、とここでやっと言えました。

おまけ:機械はもう一回、嘘をついた

念のため書いておくと、この後コピーを実行したら、スクリプトが「1,221件すべて失敗」と報告してきました。

心臓に悪い。でも実際にNASを見ると、ファイルはちゃんと全部着いている。バグでした。ファイル本体をコピーして、ハッシュ照合して、正しい名前に置き換えて――その最後に更新日時をコピーする処理が、NASの接続方式(AFP)で Invalid argument を出して転ぶ。データの着弾はとっくに終わっているのに、日付コピーの失敗を「コピー失敗」として数えていた。

一つの作業で、機械は二回、自信満々に間違った数字を出しました。一回目は「中身が違う」(本当は読めていない)。二回目は「全件失敗」(本当は全件成功)。どちらも、そのまま信じたら逆の行動を取っていた。

「ハッシュが一致したから安全」。この一文は正しいのですが、「一致したハッシュが、ちゃんと中身を読んだ上での一致か」までは保証してくれません。読めなかったものは空になり、空と空は一致し、検証は微笑んで通る。

道具は嘘をつきません。ただ、聞かれていないことは答えないだけです。「読めた?」と聞いていない私が悪い。次からは、ハッシュを取る前に「これは本当に中身のあるファイルか」を一行、確かめることにしました。0バイトを弾くだけの、たった一行です。

それで、退避が終わったので、次はいよいよ動画の山を崩します。2.3TBのフォルダを「どうせ中間ファイルだろ」と消しかけて、また止まる話を、次に。