USB とポータブルディスクのバックアップ
USB はパワーユーザーのトグルではなく第一級バックエンド。エンジン provider は usb。デスクトップと iOS の既定は path モード。Android は bridge を使う必要がある。OTG が渡すのは content:// URI だけ。POSIX open() は動かない。
iPhone が実際にすること
iOS はマウント済みボリュームを列挙しない(macOS 以外では FileManager.mountedVolumeURLs は空)。アプリは「SanDisk を検出」と出さない。経路は:
- Files に出るまでディスクを挿す。
- Azalea → バックアップストレージ → 新しい USB プロファイル。
- フォルダを選ぶ。アプリが保存するのは生パス文字列ではなく security-scoped bookmark。
- 後のエンジン呼び出しはすべて
withAccess内。そのスコープ外でブックマークを使うと不透明な Rust 権限エラーになる。
2026年2月21日、iPhone 15 + SanDisk Extreme 1 TB:最初の選択のあと、端末をロックすると次回起動は手動更新までボリューム欠落と出した。コピーの飾りではない — 購読できる接続通知を iOS は持たない。
Android が SAF である理由
OpenDocumentTree、それから takePersistableUriPermission。永続化呼び出しを飛ばすと再起動で許可が死ぬ。SafFileBridge は DocumentFile.findFile を歩かない(レベルごとに O(n)。何万枚も止まらせる)。document id の構築を優先する。
ケーブルを抜くと汎用 I/O 失敗ではなく DeviceDetached。ディスク満杯は OutOfSpace。UI は一時停止すべきであり、一件を赤く塗って走査を続けてはいけない。
ディスクに着くもの
<root>/.album/manifest.jsonl は追記のみ。一行目がバージョンヘッダ、それから論理キー、物理パス、バイト、MD5、tombstones。二台目はその台帳を読み、エスケープされた名前を戻して同じフォルダを認領する。.album/ は list に出ない。
exFAT に xattr はない。クラウドバックエンドと同じやり方で MD5 を置くと、USB アップロードごとに ENOTSUP。チェックサムは代わりにマニフェストに住む。
起きないこと
presign_putはない。- APFS にして Android が書くと期待しない。
- 二枚とも Backup という名前のディスクは、フォルダラベルではなく
volumeIdentity()UUID で区別する。