watermint.org - Takayuki Okazaki's note

ディスクイメージファイル(.dmg)をコマンドラインから

Macでたくさんのファイルをひとまとめにするのであればzipで圧縮するよりも、ディスクイメージファイルにしたほうが便利なこともあります。zipだと標準では中身を確認するのに毎回どこかに展開しないといけないですし、ファイルの内容を変更するのも面倒です。zip用のツールを使えばおそらくそれらも解決できると思いますが、個人的にはディスクイメージファイル(.dmg)を使うことの方が多いです。

ディスクイメージファイルは外付けディスクのようなイメージでマウントして使えます。macOSに標準添付されている「ディスクユーティリティー」ツールを使えばこのイメージファイルを簡単に作ることができます。読み取り専用、読み書き可能、暗号化、圧縮などオプションを選ぶこともできます。

終了したプロジェクトのファイル一式は.dmgファイルとしてまとめていますが、まとめたいプロジェクトファイルがたくさんあったり、ワークフローを自動化したいとなってくるとコマンドライン操作を覚えると便利です。

ディスクイメージファイルの作成

hdiutilコマンドを使うとディスクイメージファイルの作成や設定変更、ディスクイメージファイルのマウント、アンマウントなどさまざま操作可能です。詳しくはman hdiutilをご参照いただくとして手元での利用例をご紹介します。

フォルダ以下にあるサブフォルダをそれぞれ.dmgとして一括変換したい場合に使っているスクリプトです。それぞれ一応暗号化しておこうということで、暗号化用のパスワードを$HOME/.dmg-passwordというファイルにあらかじめ格納してあります。パスワードファイルには最後に改行が入らないようにつくっておいてください。

あとパーミッションも chmod 600 $HOME/.dmg-passwordなど他者から読み出せないようにしておきます。そうはいっても、後述しますが一連のプロセスはさほど安全性が高いわけではありませんので「添付ファイルはパスワードつきzipでパスワードは別メールですぐ送信!」とあまり変わらないセキュリティーレベルとお考えいただいていいかと思います。

#!/bin/sh

if [ $# -lt 2 ]; then
  echo $0 SRC_DIR DEST_DIR
  exit 1
fi

SRC=$1
DST=$2
PWD="$HOME/.dmg-password"

if [ ! -e $PWD ]; then
  echo Disk Image password not found
  exit 2
fi

for t in "$SRC"/*; do
  if [ -d "$t" ]; then
    echo Creating: $t
    n=$(basename $t)
    cat $PWD |                \
      hdiutil create          \
        -srcfolder "$t"       \
        -fs HFS+              \
        -encryption AES-128   \
        -format UDBZ          \
        -stdinpass            \
        "$DST/$n.dmg"
  fi
done

ディスクイメージファイルのパスワードをKeyChainにプリセットしておく

暗号化しておいたのはいいですが、作成したディスクイメージファイルを開く際にいちいちパスワードを入力するのは面倒です。Finderからダブルクリックしてマウントしようとしたときに聞かれるパスワードダイアログはコピー&ペーストを受け入れてくれないというのもさらに面倒さを増しています。

パスワードダイアログ

パスワードをキーチェインに記憶させるというオプションがありますが、あらかじめコマンドラインから記憶させておけばいいという発想です。

ディスクイメージファイルに対するパスワードは一般パスワードとして各ディスクイメージファイルのUUIDと対応して管理されています。このUUIDは

$ hdiutil isencrypted ディスクイメージファイル.dmg

というようにisencryptedオプションで知ることができます。キーチェインにパスワードを追加するには

$ security add-generic-password -a (上記のUUID) -D "disk image password" -s (ファイル名).dmg -w (パスワード)

というように実行すればよいでしょう。securityコマンドの大変残念な点はhdiutil-stdinpassのように標準入力からパスワードを受け取る手段がどうやらなさそうな点です。

コマンド引数として実行してしまうと、シェルの履歴に残ったり、psコマンドなどでパスワードが漏えいしてしまうケースがあります。このような点から上述のように「添付ファイルはパスワードつきzipでパスワードは別メールですぐ送信!」というセキュリティーレベルとたいして変わらないと思っています。

さて、上記を合わせて次のようなスクリプトにしてあります。

#!/bin/sh

if [ $# -lt 1 ]; then
  echo $0 [dmg file]...
  exit 1
fi

PWD=$HOME/.dmg-password

for FILE in "$@"; do
  UUID=$(hdiutil isencrypted "$FILE" 2>&1 | grep uuid | awk '{print $2}')
  BASE=$(basename $FILE)

  echo File: $BASE
  echo UUID: $UUID

  security add-generic-password -a $UUID -D "disk image password" -s $BASE -w $(cat $PWD)
done

このようにキーチェインにパスワードを設定したファイルを開こうとすると次のようにキーチェインからパスワードを取り出していいか確認されます。

確認ダイアログ

このダイアログをsecurityコマンドの-Aオプションでスキップする手段もありますがすべてのアプリケーションに対して許可を出してしまいます。少し面倒ですが、ここは一手間かけておいたほうがいいでしょう。