シェルスクリプトは逐次実行であることを身を以て教えてくれる日本hpさんパネェっす(´・ω・`)
--
日本hpさんが京都大学さんのスパコンストレージ77TBを吹き飛ばす事例が発生したようです(´・ω・`) www.iimc.kyoto-u.ac.jp
このお知らせの中にある日本hpさんの謝罪PDFによると、
- スパコンストレージのバックアップ用シェルスクリプトに、findコマンドなどを使って2週間以上経った古いログファイルを削除する処理が書かれていた
- find の引数を変数化するなどのリファクタを行った (ここまでは問題ない)
- このバックアップスクリプトを、スクリプト実行中に 上書き保存した
- シェルスクリプトは逐次実行であるため、find の引数に未定義の変数が指定されてしまい、結果消すべきでないファイルまで軒並み削除対象としてしまった
というやらかしのようだ(´・ω・`)
再現させてみる
折角の機会なので、77TBを吹き飛ばすシェルスクリプトを再現させてみる。
擬似的なストレージ状態はこの通り。古いデータは touch コマンドの -t オプションで更新日時を15日前にしている。
$ tree /LARGE0 /LARGE0 ├── IMPORTANT_DATA │ ├── 古い大事なデータ.txt │ └── 新しい大事なデータ.txt └── LOG ├── 古い消すログ.log └── 新しいログ.log
そして、以下のスクリプトを実行中に、
# ルートフォルダに移動 cd /LARGE0 # いろんな処理 sleep 30 # 2週間以上だったログファイルを削除 (本当に消さないようにechoを挟んだ) find ./LOG -type f -mtime +14 -daystart | xargs echo rm -f
findの引数を変数化したスクリプトに上書き保存する。
# ログフォルダを変数化 LOG_DIR=./LOG # ルートフォルダに移動 cd /LARGE0 # いろんな処理 sleep 30 # 2週間以上だったログファイルを削除(フォルダは変数で指定) find $LOG_DIR -type f -mtime +14 -daystart | xargs echo rm -f
実験結果
想定動作
まず、最初のスクリプトを実行するだけだと、このような結果になる。
$ ./backup.sh rm -f ./LOG/古い消すログ.log
対象となるログだけが削除されることが分かる。
実行中に上書き保存
次は実行中に上書き保存してみる。 (バックグラウンド実行中にviで書き換えて保存)
こうすれば古い大事なデータが削除対象になるはず。
$ ./backup.sh & $ vi backup.sh rm -f ./LOG/古い消すログ.log
あれっ?再現しない(´・ω・`)
考察
この事件のおかげではてブ上位に来ていたQiitaの記事にあるように、 qiita.com 同じinodeを持つファイルが上書きされると発生する という流れとなる。
じゃあviで編集した時にinodeが変わっているのかな…と確認してみると、、
$ ls -i backup.sh 254597 backup.sh $ ./backup.sh & $ vi backup.sh $ ls -i backup.sh 254647 backup.sh
ls -i で見れるのがinode番号。そしてviで上書き保存したあとはinode番号が変わっている。 このおかげで再現しないようだ(´・ω・`)
(追記) cpで上書きすると再現した
別記事で cp上書きなら発生する、とあったので確認してみた。 (バックグラウンド実行中にcpコマンドで上書き保存)
$ ./backup.sh & [1] 3794 $ ls -i backup.sh 254948 backup.sh $ cp backupnew.sh backup.sh $ ls -i backup.sh 254948 backup.sh rm -f ./LOG/古い消すログ.log ./IMPORTANT_DATA/古い大事なデータ.txt
cpで上書きするとinode番号はそのまま内容だけ書き換わっている。 これにより、無事(?)意図しない大事なデータが削除対象になった。
まとめ
- シェルスクリプトは逐次実行なので、実行中にファイルを書き換えると意図しない動作をすることがある
- viコマンドによる編集&上書き保存の場合はinode番号が変更されるため、同じ現象は発生しない
- しかし、cpコマンドで上書き保存した場合はinode番号は変わらずファイル内容だけ書き換わる。これにより、京都大学のスパコンストレージ77TBを吹き飛ばすことができる
- find と rm は 使用上の注意をよく読み、用法・用量を守って正しく使いましょう
補足
なお、実行環境として Chrome OS 上の Debian を使用しました。
$ cat /etc/debian_version 10.11
おしまい(´・ω・`)