iTunes 2 インストーラーによるデータ削除問題
2001/11/12
● はじめに
本ページは、11月6日〜11月12日にトップページに掲載した内容を整理したものである。
一部、説明の追加や表現の変更を行っている。
● 経緯
11月4日、AppleからiTunes 2が公開された。
その後、Mac OS X版のiTunes 2をインストールすると、環境によっては一部パーティションの内容が削除されてしまう問題が発生することが判明した。
これを受けてAppleはiTunes 2の公開を中止し、翌日この問題を解決したiTunes 2.0.1が公開された。
私もiTunes 2をインストールしていたが、幸いに実害は無かった。
当初、本現象の発生条件について、「旧バージョンのiTunesを削除せずにiTunes 2をインストールした場合」あるいは「ボリューム名に空白を含んでいる場合」などの情報がいくつかのサイトに掲載されたが、これを否定する報告もあり、今一つ決め手に欠けていた。Appleの警告ページでも発生条件が明確にされていないこともあり、今後のためにも発生条件を明確にしておきたいと考え、検証を行うことにした。
● 2001/11/06
国内外の複数のサイトの情報によれば、今回の現象は、旧バージョンのiTunesがインストールされている場合、それをインストーラーが削除するのだが、その処理に問題があったということらしい。
問題のスクリプトはiTunesインストーラーパッケージ中の「preflight」というファイルである(iTunes.pkg/Contents/Resources/preflight)。
その内容は次のとおり
if [ -e $2Applications/iTunes.app ] ; then
rm -rf $2Applications/iTunes.app 2> /dev/null
fi
exit 0
ここでシェル変数$2の値を調べる必要がある。本来$2はコマンドラインから起動された場合の2番目の引数の値を保持しているが、この場合インストーラーから起動されているため引数の内容が不明である。
そこでスクリプト中に「echo $2 >>~/a.txt
」という行を追加し、値をファイルに書き出して調べてみることにした。
(書き込み禁止属性なので、パッケージごと適当な場所にコピーして書き換えた)
その結果、
起動ディスクにiTunesをインストールしたときの「$2」の値:「/」
「WORK1」というボリュームにiTunesをインストールしたときの「$2」の値:「/Volumes/WORK1」
であった。つまり$2の値は「iTunesのインストール先として指定したボリューム」を保持している。
これにより、インストールスクリプトは、iTunesを起動ディスクにインストールする場合は「/Applications/iTunes.app」の存在をチェックし、すでにインストールされていればそれを削除する。iTunesを起動ディスク以外のボリュームにインストールした場合は、「/Volumes/ボリューム名/Applications/iTunes.app」の存在をチェックし、それを削除する。
もし、起動ディスクを指定した場合は、if文のパス名にボリューム名が現われないため、ボリューム名に空白を含んでいても問題はおきない。
しかし、例えば指定したボリューム名に「WORK 1」のように空白が含まれていると、if文の内容が「if [-e /Volumes/WORK 1/Applications/iTunes.app ];
」となり、「WORK」というボリュームが存在すれば(iTunesアプリケーションの有無とは無関係に)if文の条件が成立してしまう。
if文が成立すると、rmコマンドにより/Volumes/WORK/ 以下の内容が削除されてしまう。
不幸にも、iTunesのインストールに選択したボリューム名が「 WORK1」のように先頭が空白文字の場合、if文の内容が「if [-e /Volumes/ WORK/Applications/iTunes.app ];
」となり、「/Volumes」は必ず存在するのでif文は常に成立し、rmコマンドにより /Volumes 以下、つまりマウントしている全てのボリューム(起動ディスク以外)の内容が削除されてしまう。
(対策版の2.0.1ではパスが引用符で囲まれているため、ボリューム名に空白が含まれていてもif文の判定およびiTunesアプリケーションの削除が正しく行われる。)
さらに不運だったのは、本来ファイルの削除とディレクトリの削除はrmコマンドの引数が異なるため、もしiTunesアプリケーションが単一のファイルであったならば、仮にスクリプトにバグがあったとしてもrmコマンド(-rオプション無し)ではディレクトリの削除はできず、このような現象にはならなかったと思われるが、iTunesアプリケーションはパッケージ(フォルダ=ディレクトリ)であったため、iTunesアプリケーションを削除するスクリプトが「指定したディレクトリ以下のファイルを削除する」という指定(-rf指定)であったことがこのような現象を引き起こすこととなった。
まとめると、以下の条件が重なった場合に本現象は発生する。
(1) iTunesのインストール先に起動ディスク以外のボリューム(A)を選択した。
(2) ボリューム(A)の名称が空白を含んでいる。
(3) ボリューム(A)の名称の空白以前の文字列と一致する名称の別のボリューム(B)が存在する。
またはボリューム(A)の名称の先頭が空白文字である。
現象としては、以下のようになる。
(1) ボリューム(A)の名称の空白以前の文字列と一致する名称の別のボリューム(B)が存在する場合、ボリューム(B)の内容が削除される。
(2) ボリューム(A)の名称の先頭が空白文字の場合、マウントしている起動ディスク以外のボリュームの内容が削除される。
● 2001/11/07
昨日掲載した内容について、iTunesを起動ディスクにインストールした場合でも現象が発生したとの指摘を受け、再度検証を行ったところ、検証が不十分であったことが判明した。
これまで本現象は「preflight」が原因とされていたが、問題はこのスクリプトだけではなかった。
同じiTunes 2インストールパッケージ中に「VolumeCheck」というスクリプトがある(iTunes.pkg/Contents/Resources/VolumeCheck)。
これはパッケージをインストールしたとき「/Library/Receipts」フォルダに残るインストール情報を削除するスクリプトであり、「preflight」より先に実行される。
内容は次のとおり。
if [ -e $1Library/Receipts/iTunes.pkg ] ; then
rm -rf $1Library/Receipts/iTunes.pkg 2> /dev/null
fi
exit 0
今回もシェル変数$1の値を調べるために「echo $1 >>~/a1.txt
」という行を追加して値をファイルに書き出した。
その結果は以下の通り(赤書きは補足説明)。
/Volumes/Darwin(Darwin用ボリューム)
/Volumes/WORK(検証用の作業ボリューム)
/Volumes/WORK 1(検証用の作業ボリューム)
/(Mac OS X起動ディスク)
/Volumes/MacOS(Classic用ボリューム)
これを見て分かるように、このスクリプトはマウントしている全てのボリュームに対して実行される。あとは昨日の説明と同様で、ボリューム名に空白があるとrmコマンドによるファイルの削除が行われる場合がある。上記の例では「/Volumes/WORK 1」というボリューム名があるのでif文が「if [-e /Volumes/WORK 1/Applications/iTunes.app ];
」となり、「/Volumes/WORK」も存在するためif文が成立し、rmコマンドにより「/Volumes/WORK」以下が削除されてしまう。
(実際に削除されることを確認)
したがって、発生条件は以下のようになる。
(1) マウントしているボリュームの中に、名称に空白を含むボリューム(A)がある。
(2) ボリューム(A)の名称の空白以前の文字列と一致する名称の別のボリューム(B)が存在する。
またはボリューム(A)の名称の先頭が空白文字である。
現象としては、以下のようになる。
(1) ボリューム(A)の名称の空白以前の文字列と一致する名称の別のボリューム(B)が存在する場合、ボリューム(B)の内容が削除される。
(2) ボリューム(A)の名称の先頭が空白文字の場合、マウントしている起動ディスク以外のボリュームの内容が削除される。
● 2001/11/09
AppleからInstaller Update 1.0が公開されている。
これを適用すると、インストーラーのバージョンが1.1から1.1.1に上がり、システムのビルド番号が5L21となる。
このアップデータは、ソフトウェアアップデートのインストール時の動作を改善し、今後のMac OS Xのアップデートに必要と説明されている。
iTunes 2問題の直後に公開されたため、そのような問題を解決するものではないかと期待されたが、残念ながらiTunes2インストーラーの問題のような、インストールスクリプトの不具合を解決するものではないようだ。
(インストーラー側の問題ではないので当然といえば当然なのだが。)
5L21環境でiTunes2インストーラーを起動した際に、10/7の記事で説明したような条件が重なった場合、ボリューム内容の削除が行われることを確認した。
参考:
iTunes2インストーラーによるファイル削除の映像。
(動画1.3MB)
インストール処理の初期段階で「WORK」ボリューム内のファイルが削除されることが分かる。
環境:
・iBook(Dual USB)&Mac OS X 10.1(5L21)
・インストーラー起動前にiTunesアプリケーションとインストール情報を削除。
・/Volumes以下に「WORK」と「WORK 1」という名称のボリュームがある(FireWireターゲットモードで接続したPowerBook G4のディスク)。
・iTunesを起動ディスクにインストール。
● 2001/11/11
上記iTunes 2とInstaller Updateの関連について検証を行っている際に、別の問題があることに気がついた。
発生頻度は低いと思われるが、ボリューム名に明示的に空白を使用していない場合でも問題が発生する可能性がある。
Mac OS Xでは、複数のボリュームに同じ名称を付けることができる。同一のボリューム名は、/Volumesディレクトリ下に「ボリューム名」、「ボリューム名 1」、「ボリューム名 2」...といった名称でマウントされる。
例:「TEST」という名称の2つのボリュームは、/Volumes下に「TEST」および「TEST 1」という名称でマウントされる。

この状況でiTunes 2をインストールした場合、一方の「TEST」の内容が削除されることを確認した。
起動ディスクは/Volumes下にマウントされないため、発生条件は「起動ディスク以外に同じ名称のボリュームが複数存在する場合」となる。
通常、複数のボリュームに同じ名称を付ける事は少ないと思われるが、外付けハードディスクや、別のMacをFireWireターゲットモードで接続する場合などでは、注意が必要かもしれない。
発生条件をもう一度まとめておく。
以下のいずれかの条件を満たす場合:
(1) マウントしているボリューム(起動ディスク以外)の中に、名称に空白を含むボリューム(A)が存在する。
かつ、ボリューム(A)の名称の空白以前の文字列と一致する名称の別のボリューム(B)が存在する。
(2)マウントしているボリューム(起動ディスク以外)の中に、名称の先頭が空白文字であるボリューム(C)が存在する。
(3)マウントしているボリューム(起動ディスク以外)の中に、同じ名称のボリューム(D)が複数存在する。
現象:
(1) ボリューム(A)の名称の空白以前の文字列と一致する名称の別のボリューム(B)が存在する場合、ボリューム(B)の内容が削除される。
(2) ボリューム(C)の名称の先頭が空白文字の場合、マウントしている起動ディスク以外のボリュームの内容が削除される。
(3) 複数の同一名称ボリューム(D)が存在する場合、そのうち一つのボリュームの内容が削除される。
● 最後に
今回の件では、管理者権限でのrmコマンド(ファイル削除)実行の危険性を改めて思い知らされた。
このような重大な問題が、シェルスクリプトの単純なミス(引用符の付け忘れ)で起きてしまったことを残念に思う。
同じインストールパッケージ中の「postflight」スクリプトではパスが正しく引用符で囲まれているだけに、なおさらである。
(皮肉にも、このスクリプトはiTunesHelperを起動しているだけなので、仮りに引用符を付け忘れていたとしても、ファイルの削除という問題を起こすことはなかった。)
いくつかのインストーラーは、旧バージョンのファイルを削除するのではなく、「古い○○」など、名称を変更して保存している。今さらではあるが、もしiTunes 2のインストーラーがそういった仕様または削除ではなくゴミ箱に移動するような仕様であったならば、最悪の事体は避けられたのかもしれない。
私も仕事で(主にWindowsだが)インストーラーを作成することがある。環境は異なるが、私も今後はこれまで以上に注意してプログラミングを行いたいと思う。
←TOPページに戻る
「Macintoshは米国アップルコンピュータ社の商標です」
「その他、記載の商品名などは、一般に各社の登録商標または商標です」
Copyright (C) Toshimitsu Tanaka 2001.