web-dev-qa-db-ja.com

tryステートメントを使用すると、競合状態をどのように回避できますか?

ファイルが存在するかどうかを判断するときに、tryステートメントを使用すると「競合状態」をどのように回避できますか?

非常に賛成の 回答 (更新:削除されました)は、os.path.exists()を使用すると、他の方法では存在しない機会が生まれることを意味しているように思われるため、質問しています。

与えられた例は次のとおりです。

_try:
   with open(filename): pass
except IOError:
   print 'Oh dear.'
_

しかし、私はそれがどのように競合状態を回避するのかを理解していません:

_if not os.path.exists(filename):
    print 'Oh dear.'
_

os.path.exists(filename)を呼び出すと、攻撃者はファイルに対して、まだ実行できなかったことをどのように実行できますか?

28
Honest Abe

もちろん、競合状態は、プログラムとファイルを操作する他のコードの間です(競合状態には、常に少なくとも2つの並列プロセスまたはスレッドが必要です。詳細については this を参照してください)。つまり、open()の代わりにexists()を使用すると、実際には次の2つの状況でのみ役立つ可能性があります。

  1. バックグラウンドプロセスによって作成または削除されたファイルの存在を確認します(ただし、Webサーバー内で実行する場合、HTTP要求を処理するために並行して実行されるプロセスのコピーが多数あることを意味することが多いため、Webアプリの競合他のプログラムがなくても状態は可能です)。
  2. 悪意のあるプログラムが実行されていて、ファイルが存在すると予想される瞬間にファイルを破壊してコードをクラッシュさせようとしている可能性があります。

exists()は1つのチェックを実行するだけです。ファイルが存在する場合、exists()Trueを返した1マイクロ秒後に削除される可能性があります。ファイルがない場合は、すぐに作成できます。

ただし、open()は、ファイルの存在をテストするだけでなく、ファイルを開きます(そして、これら2つのアクションをアトミックに実行するため、チェックとオープンの間に何も起こりません)。通常、ファイルは誰かが開いている間は削除できません。つまり、withの内部では、完全に確信している可能性があります。ファイルは開いているので、実際に存在しています。これはwith内でのみ当てはまり、ファイルはwithブロックが終了した直後に削除される可能性がありますが、ファイルがwith内に存在する必要があるコードを配置すると、コードが失敗しないことが保証されます。

30
Ellioh

使用例は次のとおりです。

try:
    with open('filename') as f:
        do_stuff_that_depends_on_the_existence_of_the_file(f)
except IOError as e:
    print 'Trouble opening file'

アクセス権を持ってファイルを開いている場合、OSはファイルが存在することを保証します。そうでない場合、エラーで失敗します。アクセスが排他的である場合、ファイルをめぐって競合している他のプロセスは、あなたによってブロックされるか、あなたをブロックします。

tryは、ファイルを開く動作のエラーまたは成功を検出するための単なる方法です。PythonのファイルI/O APIには通常、戻りコードがないためです(例外)代わりに使用されます)。したがって、実際に質問に答えるには、競合状態を回避するのはtryではなく、openです。Cでも基本的に同じです(Pythonベース)ですが、例外はありません。詳細については、 this をお読みください。

Tryブロック内のファイルへのアクセスに依存するコードを実行することをお勧めします。ファイルを閉じると、その存在は保証されなくなります。

os.path.existsを呼び出すと、ファイルが存在する場合と存在しない場合がある瞬間のスナップショットが得られるだけであり、os.path.existsが戻った後はファイルの存在を知ることはできません。悪意のあるコードまたは予期しないロジックにより、予期しないときにファイルが削除または変更される可能性があります。頭を回して、車を運転する前に道路が空いていることを確認するのと似ています。頭を後ろに向けると、もう見ていない場所で何が起こっているのかを推測するだけです。ファイルを開いたままにしておくと、運転中に(善悪を問わず)不可能な、拡張された一貫性のある状態が保証されます。 :)

try/openのスナップショットの性質のため、os.path.existsを使用するのではなく、ファイルが存在しないことを確認するという提案はまだ不十分です。残念ながら、すべての場合にファイルがディレクトリに作成されるのを防ぐ方法はわかりません。そのため、ファイルが存在しないのではなく、存在するかどうかを確認するのが最善だと思います。

9
Randall Cook

あなたが求めているのは、次のような特定の競合状態だと思います。

  1. ファイルが開かれます
  2. コンテキストが切り替えられ、ファイルが削除されます
  3. コンテキストが元に戻され、「開かれた」ファイルに対してファイル操作が試行されます

この場合の「保護」方法は、すべてのファイル処理コードをtryブロックに配置することです。いずれかの時点でファイルにアクセスできなくなったり破損したりすると、ファイル操作が「正常に」失敗する可能性があります。 catchブロック経由。

もちろん、最近のOSの場合、これはとにかく発生しません。ファイルが「削除」されると、ファイルで開いているすべてのハンドルが解決(解放)されるまで削除は行われません。

0
Mike