アーカイブしたいストレージに適した接続方法の検討が重要である。
SCSI 接続の HD は、入手困難ではあるが、対応する SCSI カードに接続することでアーカイブ化可能である。特に ATTO 製の SCSI カードは Apple の OEM 製品だったために標準ドライバが存在し、インストール不要で利用できるという。 ATTO ExpressPCI UL5D は PCI Express なので最新の macOS でも Thunderbolt 経由などで利用することができるだろう。ATTO ExpressPCI UL4D, UL3S などの SCSI カードは Power Mac G4, G5 などがある場合に利用できる。 SCSI は無数の端子形状が存在していた。SCSI カードによってはピンの変換が必要であるが、amazon.co.jp などで売られているアダプタの中には配線が間違っているアダプタもあるため、接続ピンの確認が必要である。
UltraATA など IDE 接続の HD は、Seagate 製など一部 HD の相性問題はあるが、アキバや日本橋でんでんタウンなどのパーツショップで手に入る IDE/SATA 変換基盤を用いる事で簡単に接続できる。
MO の場合は、ドライバ不要の SCSI 機器であれば SCSI カードを用いる方法があるが、ATAPI 接続の内蔵ドライブを使用する方法がより簡便である。 ATAPI 接続の内蔵ドライブは IDE/SATA 変換基盤によって SATA 接続が可能になる場合があり、実際にATAPI 接続の富士通製 MO ドライブの MCR3230AP と IDE/SATA 変換基盤によって最新の macOS において MO の読み取りが可能である事を確認している。
FD の場合は、現在入手可能な適当な USB 接続のドライブによって最新の macOS でも認識可能であるが、FD の形式によっては認識できないので注意が必要である。 現在入手可能なドライブでは Classic 環境用の FD は 2HD 形式の 1440K の FD 以外は読み取ることができない。この問題はハードウェア由来のためソフトウェアで解決することができず、古い Macintosh が必要である。 Macintosh では可変速スピンドルによる ZCAV (Zone Constant Angular Velocity) 方式で記録され、GCR (Group Code Recoding) 方式での符号化を採用していた。この方法は FD を効率よく使用することで容量を増やせる利点があり、1DD 形式や 2DD 形式の FD において採用された。 PC 互換機では固定速スピンドルの CAV (Constant Angular Velocity) 方式で記録され、MFM (Modified Frequency Modulation) 方式での符号化を採用し、Macintosh においても 2HD 形式の FD において採用され、1440K の FD にはハードウェア上の互換性が生まれた。 実際この様な違いから、2DD 形式の場合、現在 USB 接続のドライブで読み取り可能な FD は、CAV 方式で記録する 720K で初期化された FD であり、Macintosh 内蔵ドライブでは ZCAV 方式方式で記録する 800K で初期化された FD が使用され、互換性の問題が生じることとなった。 Apple は複数の方式に対応することから Macintosh 内蔵の FD ドライブを SuperDrive と呼称していたが、SuperDrive 互換の USB 接続のドライブは市販されていない。読み取り可能なコンピュータを調べるには、Apple が公開している古い Macintosh のデータシートを参照せねばならない。 内蔵ドライブが SuperDrive 400K/800K など書かれていれば、2DD などの FD が読み取れる可能性がある。PowerBook G3 "Wallstreet" までは 2DD 800K といった FD の読み取りができたという。 また System 7.6.1 以前のシステム用の 1DD 形式 400K の FD では MFS (Macintosh File System) というより古いファイルシステムが使われている可能性もある。 互換性の問題が明確な場合、古い Macintosh は AppleTalk の設定も面倒で接続速度も遅いことから、HD に複製した上で HD から取り出した方が簡単である。 古い FD は磁気記録が読み取れなくなっている可能性も高いため、アーカイブ対象の FD が古い場合は、実機を用意し、問題が互換性にあるかどうか切り分けられる様にしておいた方が良いだろう。
何らかの方法で macOS にストレージを接続できた場合、いよいよ HFS イメージの作成が可能となる。但し場合によっては HFS フォーマットであったとしても macOS にとって認識できないフォーマットであると表示され、初期化するかどうか訊かれることがある。この場合はこの警告を無視しておく必要がある。
/dev/disk1
, /dev/disk2
などの BSD 名を確認すれば、任意の場所に dd
コマンドでストレージの内容を吸い出すとよい。
BSD 名は Disk Utility.app や df
コマンド、hdiutil
コマンドなどで確認できる。例えば disk1
を吸い出すには次のようにする。
dd if=/dev/disk1 of=~/backup.img bs=16m conv=sync,noerror
dd if=/dev/disk1 ibs=16m conv=sync,noerror | ssh user@domain dd obs=16m of=backup.img
実行には管理者権限が必要であり、/dev/disk1
の排他制御が必要な為にマウントされている場合はマウント解除が必要である。Disk Utility.app や umount
コマンドで実施可能である。
二つのコマンド列は似た内容であるが、後者はパイプを使うことで ssh
で別のサーバにデータを転送する方式である。if
と of
を間違えるとデータが上書きされてしまうので注意が必要である。
bs
オプションは block size の略であり、dd
コマンドで取り扱うセクタサイズを指定するもので、デフォルトは 512
、単位は Bytes である。16m
と書けば 16 MB を意味し、4k
と書けば 4 KB を意味する。
セクタサイズは入力の ibs
と出力の obs
に分けて指定でき、ストレージの物理セクタサイズやフォーマットの論理セクタサイズと異なっていても良い。
因みに物理セクタサイズは一般に HD の場合は 512 Bytes か 4 KB、MO の場合は 512 Bytes か 2 KB、FD の場合はほぼ 512 Bytes である。
conv
オプションは、エラーを抑制する c と、読み取りエラー時に読み取れなかった箇所からセクタサイズに到達するまで 0 で埋める sync
を指定している。conv
は古いストレージはセクタエラーが発生しやすいので必要である。
sync
の意味は、喩えば dd
コマンドのセクタサイズが 4 KB の時 1025-1536 Bytes 目で読み取りエラーが発生すると読み取れた 1-1024 Bytes 目はそのままで 1025-4096 Bytes 目までが 0 で埋め尽くされる。
セクタエラーが発生している場合は bs
オプションを取りやめて 512
を指定した方が消失するデータが少なくて良い。
また場合によりパーティションの違いを表す disk1s1
などが存在し、各パーティションに対しても dd
コマンドは実行可能であるが、今の場合はストレージ全体を指す disk1
に対して実施すれば良い。
backup.img の作成はストレージの容量次第でそれなりの時間を要する。作成後は次の hdiutil
コマンドを実施することによって適宜イメージのサイズを圧縮することができる。
hdiutil convert backup.img -format UDZO -o compressed-backup.dmg
hdiutil convert backup.img -format Rdxx -imagekey encrypted-encoding-version=1 -o compressed-backup.img
前者は macOS 向けの zlib により圧縮する UDZO 形式と呼ばれる形式で compressed-backup.dmg を作成している。後者は Classic 環境の Disk Copy 6.3.3 向けに圧縮する Rdxx 形式と呼ばれる形式で compressed-backup.img を作成している。
いずれも読み取り専用のイメージファイルとなる。SheepShaver などの仮想 Classic 環境の運用を考えている場合は Rdxx 形式のイメージファイルを別途保管しておくことをオススメする。
この様にして作成した Rdxx 形式のイメージファイルが Classic 環境で使えないという話もあり、実際その場合は Disk Copy によって「イメージのマウントは完了できませんでした。」といったエラーが表示される。
その理由は、man hdiutil
に互換性情報として記載されているが、イメージファイルの符号化方式はバージョン 2 に更新され Mac OS X v10.5 Leopard 以降は全て自動的にバージョン 2 が選択されているためである。
従って Classic 環境や Mac OS X v10.2 Jaguar 以前との互換性を考える場合は -imagekey
オプションでバージョン 1 の符号化方式を明示しておく必要性がある。またイメージファイルにする場合は、パーティションマップが存在しているとマウントに失敗する点も注意が必要である。
HFS にせよ HFS+ にせよ Mac OS X で作られたイメージファイルを Classic 環境で認識させる場合、hdiutil create -layout NONE ...
とパーティションマップのないイメージを作った上で Rdxx 形式に変換せねばならない。
2 GB を超える場合は Rdxx 形式に変換できないため、2 GB を超える HFS をイメージ化する場合は HFS が作成できた Mac OS X v10.5 Leopard までの Mac OS X で HFS の分割を行う必要がある。
macOS の Disk Utility.app で作成される圧縮形式のイメージファイルは互換性のためか UDZO 形式が取られているようであるが、hdiutil
ではより新しい圧縮形式も選択できる。
なお man hdiutil
によれば、このコマンドでは macOS Catalina v10.15 において次の -format
オプションが選べる。
さて、次説からはこの様にして作成した UDZO 形式のイメージファイル compressed-backup.dmg (以下では単に backup.dmg と呼ぶことにする) をマウントし、そこから HFS+ のイメージを作成する方法を考えていくことにする。
HFS イメージを使用する場合に頻発する問題として文字コードの問題がある。HFS では使用していた環境によって埋め込まれている文字コードが異なっている。 HFS+ や最新の APFS では Unicode を使用しているため、NFD 問題など正規化の問題はあっても、使用環境によって文字化けすることはないが、HFS では Unicode とは異なる Apple による文字コードが使われている。 日本語環境では MacJapanese エンコーディングという Shift_JIS の亜種が用いられており、喩えば三点リーダー…や囲み文字①など Shift_JIS にはないが需要のあった文字が追加されているため、絵文字同様、Shift_JIS として単純に処理することができない。
文字化け問題に簡単に対処する方法は macOS に用意されている処理系を利用する方法である。Mac OS X では HFS を MacJapanese で処理するための KEXT として HFS_MacJapanese.kext
が用意されていた。
OS X Yosemite v10.10 までは /System/Library/Filesystems/hfs.fs/Encodings
に、OS X El Capitan v10.11 以降は /System/Library/Filesystems/hfs.fs/Contents/Resources/Encodings
に存在しているが、macOS Sierra v10.12 以降では HFS_MacJapanese.kext
を利用することができない。このことは次のコマンドを管理者権限で実行することでわかる。
kextutil -tn /System/Library/Filesystems/hfs.fs/Contents/Resources/Encodings/HFS_MacJapanese.kext
このコマンドを実行すると KEXT の読み込み試験が行われるが、macOS Sierra v10.12 以降では次の内容の警告を出力して終了する。
kxld[com.apple.kext.HFS_MacJapanese]: The following symbols are unresolved for this kext:
kxld[com.apple.kext.HFS_MacJapanese]: _hfs_addconverter
kxld[com.apple.kext.HFS_MacJapanese]: _hfs_remconverter
Link failed (error code 5).
Check library declarations for your kext with kextlibs(8).
このエラーはライブラリなどから HFS の函数が削除されていることを意味しており、macOS Sierra v10.12 以降では HFS_MacJapanese.kext
を利用することができないことを明確に示している。
従って、日本語環境のイメージから文字化けすることなくデータを取り出すには、OS X El Capitan までの OS を用意するか MacJapanese を変換する仕組みが必要である。ここでは OS X El Capitan までの OS を利用してコマンドなどで逐次処理する方法を紹介する。
新しい OS しかない場合は文字化け前提でマウントしてから Ruby などのスクリプトで変換するか、Apple が用意している File Name Encoding Repair Utility.app を利用するといいだろう。
以上の問題に加え、アラビア語 HFS_MacArabic.kext
が macOS Catalina v10.15 から削除されるなど、サポート終了に伴ってデータも減ってきている。実際、macOS Catalina からは HFS イメージのマウントもできなくなってしまったため、データ移行が必要であることは明らかである。
HFS のファイル名は MacJapanese でエンコードされているが、その修正は少し面倒である。macOS がストレージを認識する際に MacJapanese を UTF-8 に変換してしまうからである。
例えば「あ」は MacJapanese では Shift_JIS と共通で 0x82A0 とエンコードされているが、macOS はその文字を UTF-8 で認識して「dž」と出力し 0xC387E280A0 とエンコードする。
「Ç」は UTF-8 では 0xC387 であり Unicode では U+00C7 である。「†」は UTF-8 では 0xE280A0 であり Unicode では U+2020 である。
この挙動はかなり奇妙であり、0x82A0 を直接 UTF-8 と解釈したわけでも、Unicode と解釈したわけでもないようである。しかも面白いことに 2 バイト文字の筈が 5 バイトに増えてしまっている。
他の平仮名も 4 バイトに増えたりしているようである。このことは HFS に記録されているファイル名は 1 バイト単位で記録されており、それを多バイト文字に解釈していると考えることができる。その仕組みを考えるにあたり、Apple が公開している HFS の実装が参考になる。
hfs_japanese/hfs_japanese.kmodproj/hfs_japanese.c
には、次のようにある。
int
MacJapaneseToUnicode(Str31 hfs_str, UniChar *uni_str, UniCharCount maxCharLen, UniCharCount *usedCharLen)
{
UInt32 processedChars;
processedChars = __CFFromMacJapanese(kCFStringEncodingUseCanonical | kCFStringEncodingUseHFSPlusCanonical,
&hfs_str[1],
hfs_str[0],
uni_str,
maxCharLen,
usedCharLen);
if (processedChars == (UInt32)hfs_str[0])
return (0);
else
return (-1);
}
int
UnicodeToMacJapanese(UniChar *uni_str, UniCharCount unicodeChars, Str31 hfs_str)
{
UniCharCount srcCharsUsed;
UInt32 usedByteLen = 0;
srcCharsUsed = __CFToMacJapanese(kCFStringEncodingComposeCombinings | kCFStringEncodingUseHFSPlusCanonical,
uni_str,
unicodeChars,
(UInt8*)&hfs_str[1],
sizeof(Str31) - 1,
&usedByteLen);
hfs_str[0] = usedByteLen;
if (srcCharsUsed == unicodeChars)
return (0);
else
return (-1);
}
__private_extern__ int
hfs_japanese_start(kmod_info_t *ki, void *data)
{
int result;
result = hfs_addconverter(ki->id, kCFStringEncodingMacJapanese,
MacJapaneseToUnicode, UnicodeToMacJapanese);
return (result == 0 ? KERN_SUCCESS : KERN_FAILURE);
}
__private_extern__ int
hfs_japanese_stop(kmod_info_t *ki, void *data)
{
int result;
result = hfs_remconverter(ki->id, kCFStringEncodingMacJapanese);
return (result == 0 ? KERN_SUCCESS : KERN_FAILURE);
}
文字列を処理する型はそれぞれ利用される形態に応じた実装が存在している。Str31
型は HFS のファイル名で用いられるもので、HFS+ では Unicode に対応した HFSUniStr255
型を用いる。UniChar
型は Core Foundation で御馴染みの CFString
クラスにおける実装で、その中身は Unicode である。
-hfs_japanese_start
や -hfs_japanese_stop
を呼び出している箇所を見ると、函数ポインタを設定することでエンコードの変更に対応していると見られる。
-MacJapaneseToUnicode
や -UnicodeToMacJapanese
は hfs_japanese/hfs_japanese.kmodproj/JapaneseConverter.c
における実装によってエンコード MacJapanese から Unicode へ変換・逆変換する。
-__CFFromMacJapanese
を呼び出す際に HFS の文字列ポインタから &hfs_str[1]
というポインタを文字列として、hfs_str[0]
を文字列の長さとして渡している。これは文字列の先頭にその文字列の長さを記載する Pascal 文字列と呼ばれる実装で、HFS ではファイル名は Pascal 文字列で記録されることになっていると云う。
更にその先の -__CFFromMacJapaneseCore
などを読み進めてみると、多バイト文字の「あ」の場合は -ShiftJISToJIS0208
によって 0x2422 に変換される。これは当時 JIS X 0208 を参照していたようである。最終的には埋め込み表の __CFFromJIS0208CharMap
によって 0x2422 から 0x3042 に変換される。0x3042 は「あ」の Unicode である U+3042 と一致している。
ここまで見てきても「あ」が「dž」と文字化けする理由ははっきりとしない。UTF-8 の表現「dž」は JIS X 0208 とも一致しないバイト列になっているからであり、この過程ではそもそも文字列のバイト数が増える実装にはなっていない。
これらの疑問を解消するきっかけは hfs_encodings/hfs_encodings.c
を参照することで与えられる。HFS のエンコードを変更する際に -hfs_addconverter
や -hfs_remconverter
を呼び出している。これらは初期化ルーチンにおいて次のように呼び出されている。
void
hfs_converterinit(void)
{
SLIST_INIT(&hfs_encoding_list);
encodinglst_lck_grp_attr= lck_grp_attr_alloc_init();
encodinglst_lck_grp = lck_grp_alloc_init("cnode_hash", encodinglst_lck_grp_attr);
encodinglst_lck_attr = lck_attr_alloc_init();
lck_mtx_init(&encodinglst_mutex, encodinglst_lck_grp, encodinglst_lck_attr);
/*
* add resident MacRoman converter and take a reference
* since its always "loaded". MacRoman is the default converter
* for HFS standard volumes.
*
* Only do this if we are actually supporting HFS standard
* volumes. The converter is not used on configurations
* that do not support HFS standard.
*/
hfs_addconverter(0, kTextEncodingMacRoman, mac_roman_to_unicode, unicode_to_mac_roman);
SLIST_FIRST(&hfs_encoding_list)->refcount++;
}
-mac_roman_to_unicode
と -unicode_to_mac_roman
は HFS において文字コードに MacRoman を選んだ場合の変換用の函数であり、HFS の初期設定は MacRoman であったから、文字化けしている場合はこの函数が使用されているとわかる。
注目すべきはエンコードの処理方法を変更する -hfs_addconverter
や -hfs_remconverter
の呼び方と実装である。MacJapanese に切り替える場合と MacRoman の場合とを比べても特別なことは何もしていない。
従って「あ」が「dž」と文字化けする場合、その仕組みとして次のものを仮定することができる。macOS は HFS のファイル名を解釈する場合に MacRoman であると解釈する。システム上のファイルパスは Unicode で処理されるため、MacRoman は Unicode に一度変換される。Unicode は必要に応じて UTF-8 に変換され、我々が目にすることになる。
このように考えると、MacRoman では「Ç」が 0x82 と「†」が 0xA0 とエンコードされるため、繫げると 0x82A0 である。これは MacJapanese の「あ」の 0x82A0 となることから説明がついた。従って次のように処理する。
#!/usr/bin/ruby -Ku
fallbackTable = {
"\u2318" => "\x11".force_encoding("MacRoman"),
"\u2713" => "\x12".force_encoding("MacRoman"),
"\u2666" => "\x13".force_encoding("MacRoman"),
#"\uF8FF" => "\x14".force_encoding("MacRoman"),
"\u03A9" => "\xBD".force_encoding("MacRoman"),
"\u20AC" => "\xDB".force_encoding("MacRoman"),
"\uF8FF" => "\xF0".force_encoding("MacRoman")
}
string = "dž"
string.encode!("MacRoman", "UTF-8", {:fallback => fallbackTable})
string.force_encoding("SJIS")
string.encode!("UTF-8", "SJIS")
print string
このサンプルコードは Ruby で書かれており、コードは UTF-8 で保存されることを予想している。このコードを実行すると「あ」と出力される筈である。初めの "UTF-8" は HFS の NFD などの正規化処理を反映した "UTF-8-MAC" を使用した方がいい可能性があるが詳細は確認していない。
半角片仮名のファイル名などを変換すればわかるが、Ruby の実装では一部の文字、「 (U+F8FF)」など Mac OS 以外では表示が難しい字などは UTF-8 から MacRoman に変換できないので、明示的に変換表を与えて解決している。
変換表において 「 (U+F8FF)」は二つ存在し MacRoman には 0x14 ないし 0xF0 へ変換されるとされているが、ここでは 0xF0 への変換を採用した。不具合があれば 0x14 の方を採用すればいいだろう。
また SJIS 即ち Shift_JIS を指定しているが、実際には MacJapanese を正しく変換できないので注意が必要である。MacJapanese を処理する場合は Ruby Gem の mac_japanese が必要である。
value = MacJapanese.to_utf8(value.force_encoding("ASCII-8BIT"))
などとすると良いだろう。詳しくは次説で触れる。
文字化けの問題は古い環境で利用していた NAS のストレージにおいてもみられる問題である。NAS の場合は、Linux 系 OS で処理されていることが多く、HD は ext2 など macOS で取り扱えないフォーマットになっていることが多い。
NAS の HD を取り出してイメージファイルを作成し、仮想環境で構築した Linux 系 OS に接続して NFS で macOS と共有してデータを取り出す方法が考えられる。macOS とわざわざ共有する理由は resource fork が失われてしまう問題があるためである。
/Volumes/NFS
の MacJapanese を UTF-8 化して出力する Ruby のサンプルコードを以下に示した。Ruby では Encoding::MacJapanese が定義されているが変換テーブルは用意されていないため、MacJapanese を処理できる Ruby Gem の mac_japanese が必要である。
この Gem をシステムにインストールせず .gem を .gz とみなして展開しスクリプトと同じディレクトリに配置しても使える様にもなっている。適宜書き換えて使用するといいだろう。
#!/usr/bin/ruby -Ku
sharedTarget = "/Volumes/NFS"
class String
$:.unshift File.dirname(__FILE__)
require "mac_japanese"
def UTF8StringAsEscapedMacJapaneseEncoding(sequence = ':')
sequenceCode = sequence.ord
string = ""
i = 0
j = 0
while i < length
code = self[i].ord
if code == sequenceCode then
string[j] = self[i+1,2].to_i(16).chr
i = i + 2
else string[j] = code.chr end
i = i + 1
j = j + 1
end
# return string.encode(Encoding::UTF_8, Encoding::Shift_JIS)
return MacJapanese.to_utf8(string)
end
def stringByResolvingEscapesInPath(sequence = ':')
components = split("/")
components.map!{|component| component.UTF8StringAsEscapedMacJapaneseEncoding(sequence).split("/").join(":") }
return components.join("/")
end
def stringByResolvingEscapesInPathOfDeletedLastPathComponent(sequence = ':')
return stringByDeletingLastPathComponent.stringByResolvingEscapesInPath(sequence)
end
def stringByResolvingEscapesInPathOfLastPathComponent(sequence = ':')
return lastPathComponent.stringByResolvingEscapesInPath(sequence)
end
def stringByAppendingPathComponent(component)
return [ self, component ].join("/")
end
def stringByDeletingLastPathComponent
return File.dirname(self)
end
def lastPathComponent
return File.basename(self)
end
end
directoryPaths = [ sharedTarget ]
while ! directoryPaths.length.zero?
directoryPath = directoryPaths.pop
#p directoryPath.stringByResolvingEscapesInPathOfLastPathComponent
Dir.glob("#{directoryPath}/*"){|filePath|
if FileTest.directory?(filePath)
directoryPaths.push(filePath)
else
unless filePath.stringByResolvingEscapesInPath == filePath
print filePath.stringByResolvingEscapesInPath
end
end
}
end
このコードではファイル名が NAS のシステムによって適宜エスケープされている場合を想定している。手元の NAS では ext2 で使用できない文字は全て所謂 Quoted-printable の亜種として実装された方式でエスケープされていた。 Quoted-printable では "=" を使うところを ":" とする実装である。Mac OS ではファイル名に "/" が使われていた場合にディレクトリと区別するため ":" へ置換されるようになっている。この置換は ":" が Mac OS のディレクトリの区切りとして使われていた名残であり、実際に AppleScript では HFS 形式のパスとして古式ゆかしく使用できる。 ":" は予約文字となっており、ファイル名に使うことはできないため、エスケープに使用できると考えられたのであろう。この仕様が NAS の制御システムのものか、NAS のファイル共有プロトコルの仕様かはわからない。 この NAS では、喩えば「空」という漢字は Shift_JIS では 0x8bf3 に該当するので、ファイル名としては「:8b:f3」として処理された。「品」という漢字は Shift_JIS では 0x8a77 であるが、0x77 は ASCII コードの「w」に等しいので、ファイル名としては「:8aw」として処理された。 .DS_Store などの "." で始まるファイルは「:2e」とエスケープされている一方で、.jpg など拡張子の "." はエスケープされていないなど、挙動にも僅かに違いがある。resource fork と区別している可能性がある。 また "/" は ":" へと置換することなく「:2f」とエスケープされて保管されているため、"/" を ":" に戻す処理も追加している。なおこのエスケープ手法は CAP (Columbia AppleTalk Package) と呼ばれる AppleTalk の UNIX 実装に由来するものらしく、CAP Encoding と呼ばれている。
HFS イメージを Finder でクリックしてマウントする際、OS X Yosemite v10.10 からは文字コードが自動判定されずしばしば文字化けするようになった。 OS X Yosemite では Finder でエンコーディングを修正することもできたが、OS X El Capitan v10.11 からはその選択肢も選べなくなった。 Finder では選べないが、OS X El Capitan でも HFS_MacJapanese.kext を使用することはできる。文字コードを明示してマウントする場合はコマンドで次のようにする。
hdiutil attach -nomount backup.dmg
mount_hfs -e Japanese -u 501 -g 20 -m 755 -o nodev -o noowners -o nosuid -o rdonly /dev/disk1 /Volumes/HFSData
macOS では attach
によりストレージと接続し、mount
によってストレージをマウントする流れをとる。
attach
だけでマウントすることも可能であるが、文字コードを指定するために -nomount
オプションによってマウントを抑制している。
hdiutil
コマンドで attach
するとストレージの BSD 名が決定されて出力される。mount_hfs
コマンドは KEXT を読み込むために管理者権限で実行する必要があり、
ここでは BSD 名が /dev/disk1
であった場合に /Volumes/HFSData
へマウントする際のコマンドを示した。/Volumes/HFSData
は適宜 mkdir
コマンドなどで作成する必要性がある。
-e
オプションは文字コードを指定しているもので、Arabic, ChineseSimp, ChineseTrad, Croatian, Cyrillic, Greek, Hebrew, Icelandic, Japanese, Korean, Roman, Romanian, Thai, Turkish
が選べる。デフォルトは Roman
である。
-u, -g
オプション で UID を 501、GID を 20 に指定しているが、ログインしているユーザのものが良い。UID と GID は id
コマンドで確認できる。
-o rdonly
オプションは読み取り専用を指定するもので、UDZO 形式など読み取り専用の場合に指定が必要である。もし指定しないと mount_hfs: Permission denied
でマウントに失敗する。
mount_hfs
コマンドは HFS パーティションを明示する必要性があるので、このイメージが複数のパーティションを有していると mount_hfs: error on mount(): error = -1.; mount_hfs: Invalid argument
といった警告が出て、マウントに失敗する。
attach
した際の出力内容に注意を払うようにし、喩えば次の様に出力されていた場合は Apple_HFS
とある /dev/disk1s3
に対してマウントするようにする。
/dev/disk1 Apple_partition_scheme
/dev/disk1s1 Apple_partition_map
/dev/disk1s3 Apple_HFS
ストレージを使用し終わった時は、次のコマンドを使うと umount
によってマウントを解除し、detach
によりストレージを取り外すことができる。
attach
する場合と同様に detach
だけで直接マウントを解除して取り外すことも可能である。/Volumes/HFSData
は不要な場合は適宜削除する必要性があるが、hdiutil
コマンドによって削除されることもある。
umount /Volumes/HFSData
hdiutil detach /dev/disk1
文字化けせずに HFS イメージをマウントできたら、いよいよ HFS+ イメージへ変換することを考える。
Disk Utility.app や hdiutil
コマンドにはフォルダやストレージから新規イメージファイルを作る機能が存在するが、この機能は resource fork を完全にコピーするものではない。
従って、まずここでは書き込み可能な空の HFS+ イメージを作成し、そこにデータを追加、HFS+ イメージを読み取り専用に変換する段階を踏むことにする。空の HFS+ イメージは次のように作成する。
hdiutil create -sectors 446000 -fs HFS+ -volname "Macintosh Data" backup-temporary.dmg
ここではセクタ数を 230 MB の MO で使われていた 446000、ボリューム名を Macintosh Data とした。前節でマウントした backup.dmg と同じものに揃えたい場合は hdiutil imageinfo backup.dmg
で調べることができる。
セクタ数は Sector Count:
、ボリューム名は partition-filesystems: HFS:
として出力される。ただしボリューム名が日本語の場合は MacJapanese なので文字化けする。
そこから HFS+ イメージにデータを複製するには次のコマンドを順次実行する。
mkdir /Volumes/HFSPlusData
hdiutil attach backup-temporary.dmg -nobrowse -mountpoint /Volumes/HFSPlusData
rm -r /Volumes/HFSPlusData/.Trashes
scp -rpE /Volumes/HFSData/ /Volumes/HFSPlusData
touch -r /Volumes/HFSData /Volumes/HFSPlusData
SetFile -d "01/01/1999 00:00:00" /Volumes/HFSPlusData
hdiutil detach /Volumes/HFSPlusData
rm -r /Volumes/HFSPlusData
hdiutil detach /Volumes/HFSData
backup-temporary.dmg をマウントする際に -nobrowse
オプションを指定すると Finder 上でストレージとしては認識されなくなる。
Finder 上でストレージとして認識されると .fseventsd
という FSEvents 用の隠しフォルダが作成されるため、それを回避している。またマウントした時点で必ずゴミ箱が作成されるので消去している。
ここでは scp
コマンドを使用しているが、macOS の scp
コマンドは -E
オプションによって resource fork や ACL といったファイル属性を複製することができる。
かつては Xcode.app に附属する Command Line Tools の CpMac
コマンドなどが必要であったが、現在の macOS の cp
コマンドもまたファイル属性の複製に対応しているので、scp
コマンドでなくとも良い。
touch
コマンドによってストレージの変更日、Command Line Tools の SetFile
コマンドによってストレージの作成日を揃えている。
SetFile
コマンドで設定する作成日は、ここでは平成11年1月1日午前零時としているが、Command Line Tools の GetFileInfo
コマンドを使用すると created:
として作成日を取得できるので、そのまま指定すれば良い。
これらのコマンドによって backup-temporary.dmg には必要なファイルが全て複製されている。最後に読み取り専用の HFS+ イメージとして UDZO 形式の backup-hfsplus.dmg を作成して作業は終了である。
hdiutil convert backup-temporary.dmg -format UDZO -o backup-hfsplus.dmg
必要なければ backup-temporary.dmg を削除すれば良い。backup.dmg もまた HFS フォーマットながら Classic 環境では認識できないので、Classic 環境用には Rdxx 形式のイメージを残しておき、作業完了後には HFS イメージの backup.dmg は削除すると良いだろう。
HFS+ イメージは Mac OS 8.1 以降の Classic 環境であっても使用することができるが、最新の macOS で作成した HFS+ イメージはしばしば Classic 環境で文字化けする。 文字化けした HFS+ に Classic 環境で日本語ファイルを付け足したとしても macOS 側では文字化けすることはない。現在、調査中である。