0

ユーザーがファイルをzipとしてダウンロードできる自作のWebベースのファイルシステムがあります。ただし、実稼働システムに存在しないローカル ボックスでの開発中に問題が見つかりました。

Linux では、これは問題ではありません (ローカルの開発ボックスは Windows システムです)。

次のコードがあります

algo = CipherType('AES-256', 'CBC')
decrypt = DecryptCipher(algo, cur_share.key[:32], cur_share.key[-16:])
file = open(settings.STORAGE_ROOT + 'f_' + str(cur_file.id), 'rb')
temp_file = open(temp_file_path, 'wb+')

data = file.read(settings.READ_SIZE)
while data:
    dec_data = decrypt.update(data)
    temp_file.write(dec_data)
    data = file.read(settings.READ_SIZE)

# Takes a dump right here!
# error in cipher operation (wrong final block length)
final_data = decrypt.finish()
temp_file.write(final_data)
file.close()
temp_file.close()

上記のコードはファイルを開き、(現在のファイル共有のキーを使用して) ファイルを復号化し、一時的な場所に書き込みます (後で zip ファイルに詰め込みます)。

私の問題はfile = open(settings.STORAGE_ROOT + 'f_' + str(cur_file.id), 'rb')オンラインです。ファイルを指定しないと、Windowsはバイナリファイルについてメトリックトンを気にするため'rb'、データ読み取りループでファイルが読み取られません。ただし、何らかの理由で、私もそれに書き込んでいるためtemp_file、ファイルの最後まで完全に読み取ることはありません... b の後に a + を追加しない限り'rb+'

コードをfile = open(settings.STORAGE_ROOT + 'f_' + str(cur_file.id), 'rb+')すべて変更すると、必要に応じて機能し、コードはバイナリファイル全体を正常にスクレイピングして復号化します。プラスを追加しないと失敗し、ファイル全体を読み取ることができません...

コードの別のセクション (個々のファイルをダウンロードするため) を読み取ります (OS に関係なく問題なく動作します)。

algo = CipherType('AES-256', 'CBC')
decrypt = DecryptCipher(algo, cur_share.key[:32], cur_share.key[-16:])
file = open(settings.STORAGE_ROOT + 'f_' + str(cur_file.id), 'rb')

filename = smart_str(cur_file.name, errors='replace')
response = HttpResponse(mimetype='application/octet-stream')
response['Content-Disposition'] = 'attachment; filename="' + filename + '"'

data = file.read(settings.READ_SIZE)
while data:
    dec_data = decrypt.update(data)
    response.write(dec_data)
    data = file.read(settings.READ_SIZE)

# no dumps to be taken when finishing up the decrypt process... 
final_data = decrypt.finish()
temp_file.write(final_data)
file.close()
temp_file.close()

明確化

ファイル全体が読み取られなかったために、暗号化エラーが発生した可能性があります。たとえば、一度に64*1024バイト単位で読み込む 500MB のファイルがあります。Windowsで指定しないbと、ループを2回循環し、くだらないデータを返します(pythonは、バイナリファイルではなく文字列ファイルとやり取りしていると考えているため)。

指定するbと、ファイルを完全に読み込むのに 10 ~ 15 秒かかりますが、それは正常に行われ、コードは正常に完了します。

ソースファイルから読み込むときに (最初の例のように) 同時に別のファイルに書き込んでいる場合、指定しないと、どちらがrb+指定されていない場合と同じ動作が表示さbれ、ファイルからいくつかのセグメントのみが読み取られます。ハンドルを閉じて先に進む前に、不完全なファイルになってしまい、復号化に失敗します。

4

1 に答える 1

1

ここで推測してみます:

読み込もうとしているファイルを継続的に置き換えている他のプログラムがあります。

Linux では、この別のプログラムはファイルをアトミックに置き換える (つまり、一時ファイルに書き込み、その一時ファイルをパスに移動する) ことで機能します。したがって、ファイルを開くと、8 秒前のバージョンが取得されます。数秒後、誰かがやって来てディレクトリからリンクを解除しますが、それはファイル ハンドルにはまったく影響しないので、readファイル全体を自由に使用できます。

Windows では、アトミック置換などはありません。この問題を回避するにはさまざまな方法がありますが、多くの人が行っているのは、ファイルをその場で書き直すことです。したがって、ファイルを開くと、8 秒前のバージョンが取得され、それが開始readされます。その後、突然、他の誰かがファイルを空白にして、ファイルを書き換えます。彼らは同じファイルを書き直したので、それあなたのファイルハンドルに影響を与えます。したがって、EOF にヒットします。

モードでファイルを開いても問題は解決r+しませんが、それを隠す新しい問題が追加されます: 他のプログラムがファイルを書き換えることを防ぐ共有設定でファイルを開いています。つまり、もう一方のプログラムは失敗しています。つまり、誰もこのプログラムに干渉していないということです。つまり、このプログラムは機能しているように見えます。

実際には、これよりもさらに微妙で煩わしい場合があります。それ以降のバージョンの Windows は、スマートになろうとしています。他の誰かがファイルをロックしているときにファイルを開こうとすると、すぐに失敗するのではなく、しばらく待ってから再試行することがあります。これがどのように機能するかの正確なルールは、必要な共有とアクセスに依存し、実際にはどこにも文書化されていません. そして事実上、希望どおりに動作する場合は常に、競合状態に依存していることを意味します. エクスプローラーからメモ帳にファイルをドラッグするようなインタラクティブなものには問題ありませんが (10% の確率ではなく 99% の確率で成功する方がよい)、確実に動作しようとするコード (99% の確率で成功する場合) には明らかに受け入れられません。問題のデバッグが難しいことを意味します)。したがって、 と の間で簡単に異なる動作をする可能性がrあります。r+モードを完全に理解することはできず、可能であれば頼りたくない理由があります…</p>


とにかく、これのバリエーションが問題である場合は、その他のプログラム、ファイルを書き換えるプログラム、または場合によっては両方のプログラムを協力して修正し、Windows でアトミック ファイル置換を適切にシミュレートする必要があります。このプログラムだけで解決できることはありません。*


* まあ、楽観なチェック-読み取り-チェックなどを実行して modtime が予期せず変更されるたびに最初からやり直すことも、ファイルシステム通知 API を使用することもできますが、適切な場所で修正するよりもはるかに複雑です。

于 2013-09-06T22:24:58.110 に答える