3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Mac で大文字小文字の区別が必要なファイル名を扱う

Last updated at Posted at 2023-10-24

Mac 標準のファイルシステム APFS (Apple File System) のデフォルトは、ファイル名の大文字・小文字を区別せず扱う case-insensitive となっています。一方、Linux のファイルシステムでは大文字・小文字を区別する case-sensitive が標準的1であるため、Linux のファイルを Mac で扱う際に問題となる場合があります。この記事ではディスクイメージを使った解決方法を紹介します。

大文字・小文字が問題になるケース (Linux カーネルの例)

大文字・小文字の区別が特に問題になるは、同じディレクトリ内に大文字・小文字だけが異なるファイルがある場合です。

具体的に自分が困ったのは Linux カーネルのソースコードです。例えば include/uapi/linux/netfilter というディレクトリには、xt_CONNMARK.hxt_connmark.h という大文字・小文字だけが異なる 2 つのファイルが存在しています。

大文字・小文字を区別しないデフォルトの APFS では、これらを同一のファイルとして扱うため、どちらか一方しか同じディレクトリに存在できません。そのため、Linux カーネルのコードを clone した直後からファイルの衝突によってダーティな状態となります。以下はコミット 58720809f527 をデフォルトの APFS 上に clone した直後の結果です。ダーティな状態であるためブランチの切り替えなどの git 操作ができません。

$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   include/uapi/linux/netfilter/xt_CONNMARK.h
	modified:   include/uapi/linux/netfilter/xt_DSCP.h
	modified:   include/uapi/linux/netfilter/xt_MARK.h
	modified:   include/uapi/linux/netfilter/xt_RATEEST.h
	modified:   include/uapi/linux/netfilter/xt_TCPMSS.h
	modified:   include/uapi/linux/netfilter_ipv4/ipt_ECN.h
	modified:   include/uapi/linux/netfilter_ipv4/ipt_TTL.h
	modified:   include/uapi/linux/netfilter_ipv6/ip6t_HL.h
	modified:   net/netfilter/xt_DSCP.c
	modified:   net/netfilter/xt_HL.c
	modified:   net/netfilter/xt_RATEEST.c
	modified:   net/netfilter/xt_TCPMSS.c
	modified:   tools/memory-model/litmus-tests/Z6.0+pooncelock+poonceLock+pombonce.litmus

ディスクイメージを使った解決

APFS には大文字小文字を区別する Case-sensitive APFS というフォーマットが用意されているので、このファイルシステムを使えばこの問題は解決します。既存のディスクのファイルシステムを変更するのは影響が大きいため、ここではディスクイメージを使った方法を紹介します。

まず、大文字小文字を区別する Case-sensitive APFS のディスクイメージを ディスクユーティリティ.app 2 または CLI の hdiutil コマンドで作成します。ここでは hdiutil を使った作成例を紹介します。

次のコマンドは 100 GB の Case-sensitive APFS のディスクイメージを SPARSEBUNDLE 形式で $HOME/src-case.sparsebundle というファイル3に作成する例です。SPARSEBUNDLEスパースファイルの一種であるため、実際に使用するまで容量を消費しません。

hdiutil create -type SPARSEBUNDLE -fs 'Case-sensitive APFS' -size 100g \
  -volname src-case $HOME/src-case.sparsebundle

次にこのファイルをアタッチします。下記の例ではマウント先を $HOME/src-case に設定しています。自分は普段 $HOME/src 以下でソースコードを管理しているので、大文字小文字の区別が必要なリポジトリだけを $HOME/src-case に置き、$HOME/src にシンボリックリンクを貼る運用にしました。

hdiutil attach $HOME/src-case.sparsebundle -mountpoint $HOME/src-case

$HOME/src-case で大文字・小文字が区別されることを確認してみます。

$ touch $HOME/src-case/helloworld.txt
$ touch $HOME/src-case/HELLOWORLD.TXT

大文字・小文字だけが異なるファイルが共存できました。:tada:

$ ls $HOME/src-case/
HELLOWORLD.TXT helloworld.txt

このアタッチ状態は再起動すると失われます。Mac の起動時に自動的にアタッチしたい場合は、Launchd でアタッチコマンドを実行するようにします。以下は自分の設定例です。ユーザごとの launchd の設定を置く ~/Library/LaunchAgents 以下に attach-src-case.plist という名前で保存しました。設定の詳細は man launchd.plist をご覧ください。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>attach-src-case</string>
    <key>ProgramArguments</key>
    <array>
      <string>/bin/bash</string>
      <string>-c</string>
      <string>hdiutil attach $HOME/src-case.sparsebundle -mountpoint $HOME/src-case</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>ProcessType</key>
    <string>Interactive</string>
  </dict>
</plist>

ProcessType を Interactive に設定している理由

Launchd の Job はデフォルトで CPU・I/O にスロットルがあり、ProcessType を指定しない場合(Standard)はこのスロットルによりアタッチに時間がかかります。Interactive を設定するとこの制限がなくなります。詳細は man launchd.plist をご覧ください。

:pencil: 補足: case-insensitive と case-preserving

APFS のデフォルトではファイル名の大文字・小文字を区別せず扱う case-insensitive です。一方、ファイル名としては大文字・小文字は区別されて保存されています。これは case-preserving と呼ばれます。それぞれ別のコンセプトであるため注意が必要です。

例えば heLLoWorLD.TXT という大文字・小文字が混在したファイルを作成・表示すると、APFS のデフォルトでもファイル名としては大文字・小文字が区別され保存されていることがわかります。これが case-preserving です。

$ echo 1 > heLLoWorLD.TXT

$ ls
heLLoWorLD.TXT

一方、大文字小文字だけが異なるファイル名を指定したときに同一のファイルとして扱われる動作が case-insensitive です。

$ echo 1 > heLLoWorLD.TXT
$ echo 2 > HELLOWORLD.TXT

$ ls
heLLoWorLD.TXT

$ cat heLLoWorLD.TXT
2

case-sensitive のファイルシステムの場合は、下記のように別のファイルとして区別されます。

$ echo 1 > heLLoWorLD.TXT
$ echo 2 > HELLOWORLD.TXT

$ ls
HELLOWORLD.TXT heLLoWorLD.TXT

$ cat heLLoWorLD.TXT
1

$ cat HELLOWORLD.TXT
2
  1. ext4, XFS はオプションで case-insensitive に設定可能です。

  2. 日本語表記の場合フォーマットの名前は APFS (大文字/小文字を区別) となります。

  3. SPARSEBUNDLE 形式の場合は実際はディレクトリです。SPARSE の場合は単一ファイルとなります。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?