[Mac OS X] Sanbox下のファイルアクセス実装方法

# はじめに
本文書は、[Container Directories and File System Access](https://developer.apple.com/library/mac/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW4) in App Sandbox Design Guideの(不完全な)翻訳です。Mac OS XにてSandbox対応アプリを開発する上で必要な情報になります。
作成日: 2016/01/14
修正日: 2019/11/18 一部翻訳を追加

# Container Directories and File System Access
サンドボックスが有効な時、アプリケーションがアクセスできるのは次のディレクトリに制限されます。

<table>
<tr>
<th>種類</th><th>説明</th>
</tr>
<tr>
<td>App container directory</td>
<td>OSがアプリケーション専用に割り当てるデータ領域です。領域はユーザごとに個別に準備されます。</td>
</tr>
<tr>
<td>App group container directories</td>
<td>許可されたアプリケーションで共有します。複数指定可能です</td>
</tr>
<tr>
<td>User-specified files</td>
<td>ユーザが明示的にファイル選択する、あるいはドラッグすることでアクセス許可されます。</td>
</tr>
<tr>
<td>Related items</td>
<td>上で指定されたファイルと拡張子が異なるファイルにアクセスできます。これにより関連性の高いファイルや、フォーマットが異なる同一コンテンツのファイルにアクセスできます。</td>
</tr>
<tr>
<td>Temporary directories, command-line tool directories, specific world-readable locations</td>
<td>その他明示的に定義されたディレクトリに対して様々な制約下でのアクセスを定義できます</td>
</tr>
</table>
次節から各々の詳細について説明します。

## App Container Directory
* アプリケーション専用のホームディレクトリは、[NSHomeDirectory](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/index.html#//apple_ref/c/func/NSHomeDirectory)関数を呼び出すことで取得できます。
* 上のディレクトリ、あるいはそのサブディレクトリについては、自由に読み書き可能です。
* OSXのPosixレイヤ以上のパス検索APIは、アプリケーション専用の特定のパスを参照します。
* ほとんどのパス検索APIはアプリケーションのコンテナディレクトリからの相対パスに対して機能します。例えば、アプリケーション固有のライブラリは、[NSLibraryDirectory](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/index.html#//apple_ref/c/econst/NSLibraryDirectory)なる定数で定義され、その内部に`Application Support`ディレクトリと、`Preferences`サブディレクトリを含みます。
* 幾つかのディレクトリはコンテナディレクトリ外に存在します。例えば、[NSTemporaryDirectory](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/index.html#//apple_ref/c/func/NSTemporaryDirectory)は、コンテナ外に位置しますが、アプリケーションから自由に読み書きできます。なお、sandboxが有効な場合も無効な場合もコードは同じに出来ます。
* オペレーティングシステム(OSX)は、コンテナディレクトリとアプリケーションを、アプリケーションのシグネチャで紐付けます。
* コンテナディレクトリ以下のファイルは、ユーザが直接するためのものではありません。その場所は、ユーザから隠されています。同ディレクトリにはキャッシュやデータベース、あるいはアプリケーション固有のファイルを格納します。

アプリケーション固有のコードシグネチャが、他のアプリからの不正アクセスからアプリを守ります。コンテナ情報を再利用する場合は、アプリのバージョンアップ時には、既存のシグネチャを使ってください。コンテナディレクトリは、アプリケーションの初回の起動時にOSによって作成されます。

## The Application Group Container Directory
翻訳中

## Powerbox and File System Access Outside of Your Container
上記コンテナ以外の領域に、アプリがアクセスする方法は下記の3通りです:

* ユーザによる明確な指示
* [Entitlements and System Resource Access](https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW1)で説明する特定のファイルの配置情報
* 他アプリからの読み出しを前提としたファイル群を含んだディレクトリ

ユーザの指示によるサンドボックスの拡張を、macOSのセキュリティ技術では、PowerBoxと呼びます。PowerBoxには特別はAPIはありません。PowerBoxを使用するためには、[NSOpenPanel](https://developer.apple.com/documentation/appkit/nsopenpanel)もしくは、[NSSavePanel](https://developer.apple.com/documentation/appkit/nssavepanel)を使用します。PowerBoxを使用するか否かは、xcodeのentitlementの設定で指定します。[Enabling User-Selected File Access in Entitlement Key Reference](https://developer.apple.com/library/archive/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html#//apple_ref/doc/uid/TP40011195-CH4-SW6)を見て下さい。

これらのOpen/Saveのダイアログは、AppKitではなく、PowerBoxが取り扱います。Sandbox化に当たって、これらダイアログを使用するための既存のコードを変更する必要はありません。

PowerBoxの特徴は、そのアクセス領域が、プログラムではなくユーザによって拡張される点にあります。ダイアログによって、サンドボックス外のディレクトリが指定されると、以降アプリケーションは同ディレクトリにアクセス可能になります。

上記に加えて、下記のディレクトリはsandbox下でもアクセス可能です:

* インプットメソッドへの接続
* サービスメニューで選択されたサービスへのアクセス
* 「Open Recent」メニューで選ばれたファイル
* ユーザによるコピー&ペースト
* 下記のディレクトリ下のファイルの読み出し
 * /bin, /sbin, /usr/bin, /usr/lib, /usr/sbin, /usr/share, /System
* [NSTemporaryDirectory](https://developer.apple.com/library/archive/documentation/LegacyTechnologies/WebObjects/WebObjects_3.5/Reference/Frameworks/ObjC/Foundation/Functions/FoundationFunctions/Description.html#//apple_ref/c/func/NSTemporaryDirectory)の呼び出しで得られたファイル

## Related Items
翻訳中

## Open and Save Dialog Behavior with App Sandbox
[NSOpenPanel](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSOpenPanel_Class/index.html#//apple_ref/occ/cl/NSOpenPanel)メソッドと、[NSSavePanel](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSSavePanel_Class/index.html#//apple_ref/occ/cl/NSSavePanel)は、Sandboxがイネーブルの場合とそうでない場合で挙動が異なります。Sanboxがイネーブルの場合、以下の処理が不可能です:

* `ok:`メソッドを使って、OKボタンを押した状態にすること。
* [NSOpenSavePanelDelegate](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOpenSavePanelDelegate_Protocol/index.html#//apple_ref/occ/intf/NSOpenSavePanelDelegate)デリゲートの`panel:userEnteredFilename:confirmed:`メソッドを用いて、ユーザが選択したファイルのパスを書き換えること

Sandbox対応のため、`NSOpenPanel`と`NSSavePanel`は、そのサブクラスのメソッドの一部を継承していません。コンパイラは継承していないメソッドの検出ができないため、これらを呼び出した場合は、実行時に例外が発生します。

# Security-Scoped Bookmarks and Persistent Resource Access
コンテナディレクトリ外のファイルにアクセスするためには、先に述べたユーザの明示的なファイル指定が必要ですが、アプリケーションを再起動すると、アクセス許可が失われてしまいます(ただし、アプリケーションがファイルを開いている状態で、そのアプリケーションが強制終了された場合、該当するファイルはアプリケーション再起動時にオープンできます。)

OSX v10.7.3以降のシステムでは、security-scoped bookmarksなるセキュリティ機構が導入されました。これによりアクセス許可をアプリケーション終了後も保持することができます。

以下にその活用例を挙げます:

* ユーザがダウンロードしたファイル、処理中のファイルあるいは生成したファイルへのアクセス
* ユーザが指定した画像ファイルのアクセス
* ドキュメント中に記載された別ファイルへのアクセス

## Two Distinct Types of Security-Scoped Bookmark
*Security-scoped bookmarks*は、Mac OS X v10.7.3から利用可能です。次の2つのユースケースをサポートします。

* *app-scoped bookmark* : ユーザが指定したファイル/フォルダの永続的なアクセスをサポートします。ユーザが指定するファイル/フォルダの最初のアクセスは、`NSOpenPanel`によって行われます。ユーザが指定したファイルの置かれているフォルダを記録する(通常[NSUserDefaults](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/index.html#//apple_ref/occ/cl/NSUserDefaults)の1つのプロパティとして記録します)事で、app-scoped bookmarkを実装することができます。
* *document-scoped bookmark* : 例えば、ビデオ編集ソフトのプロジェクトファイルには、外部のファイルへの参照が含まれています。これは、プロジェクトファイルがそれら外部ファイルへの永続的アクセスをするための、*Security-scoped bookmarks*を持たなければならないことを意味します。

## Using Security-Scoped Bookmarks
*Security-scoped bookmarks*を使用するためのステップを以下に挙げます。
1. Xcodeのプロジェクトにて、 *Security-scoped bookmarks*を使うための制約(entitlement)を設定してください。
2. ユーザがOpenPanelやSavePanelで指定したファイルのうち、永続化が必要なものについては、URLからブックマークを生成します。
3. アプリケーションが再起動した場合などに、上のステップで保存したブックマークを取り出し、Securty scoped URLを取り出します
4. ステップ3で取得した　URLをもとにファイルを読み込みます。
5. ファイルの読み込みが終わったら、アクセス権を返還してください
6. アプリケーション再起動時には、ステップ3から開始してください。