Android Studio 利用中に直面した問題についてメッチャ深追いしたのでその結果を載せておく。Samba 周りの話はその手の人には常識なのかもしれない。
問題
Windows 版 Android Studio (ver. 3.1) で Samba をマウントしたディレクトリ (Z:\AndroidStudioProjects 等のドライブレターを割り当てたパス) にプロジェクトを作成しようとすると、 Everyone に Writable 権限がない場合に失敗する
- そもそも ネットワークドライブにプロジェクトを作成するのは非推奨(だが仕方がない時もある!)
- メッセージ: Could not ensure the target project location exists and is accessible: (パス名) Please try to specify another path.
原因
- Android Studio はプロジェクトフォルダ作成直後に書き込み権限をチェックするが、Windows のネットワークフォルダの書き込み権限は正しくチェックできないことがままあり、書き込み権限無しと誤判定される
- これは、リモートのユーザーID (SID) とローカルのユーザーID (SID) を単純比較しているために起こるように見える
- ドメインユーザーでマウントしていたり、仮想マシンを複製した場合などで、ローカルとリモートの SID が一致する場合には正しく判定できる
- Samba は素のままでは SID が idmap_tdb という方法で生成されるためうまくいかない。
- 特に
security = ads
の場合は、 winbind をインストールしidmap_ad
を有効にすれば、AD と一致した SID を返すようになる (後述)
- 特に
修正方法: Samba ドライブのファイル所有者の SID を正しく設定する
Samba が報告する SID が AD の SID と一致しないために起こっている場合、Samba 側に winbind を入れて idmap_ad
を有効にすればいけた。
winbind を入れたあとは smbd
, nmbd
を再起動し、 net cache flush
してキャッシュをフラッシュすること。
以下は自分の環境でうまくいったもの。
- AD の DC (Windows Server 2016) をセットアップ
- 「ユーザーとコンピューター」「表示」「拡張機能」を有効に
- ユーザーのプロパティの「属性エディター」タブでユーザーの LDAP 属性を編集できるようになるので、 uidNumber など UNIX 属性を追加しておく
uidNumber, loginShell, unixHomeDirectory, primaryGroupID
- 参考: Missing Unix Attributes tab in ADUC on Windows 10 and Windows Server 2016
- Linux サーバー側 (ubuntu) 参考 Setting up Samba as a Domain Member
- Kerberos クライアントの設定 (後述 krb5.conf)
- sssd の設定
- smb.conf を書く
- DC に ubuntu を登録
net ads join -U administrator
- smbd, nmbd, winbind を起動
- DC に ubuntu を登録
/etc/krb5.conf
[logging]
default = FILE:/var/log/krb5.log
[libdefaults]
default_realm = EARL.TEA.LOCALDOMAIN
kdc_timesync = 1
ccache_type = 4
forwardable = true
proxiable = true
[realms]
EARL.TEA.LOCALDOMAIN = {
kdc = win-n74eqau7up2.earl.tea.localdomain
admin_server = win-n74eqau7up2.earl.tea.localdomain
default_domain = EARL.TEA.LOCALDOMAIN
}
[domain_realm]
.earl.tea.localdomain = EARL.TEA.LOCALDOMAIN
earl.tea.localdomain = EARL.TEA.LOCALDOMAIN
/etc/sssd/sssd.conf
-
ldap_id_mapping = false
にすれば LDAP 側の uidNumber が ローカルの uid にマップされるようになる。 - 設定を変えた場合は
sss_cache -E
でキャッシュをクリアする - 参考: Integrating with a Windows server using the AD provider (公式)
- 参考: SSSD and Active Directory (Ubuntu Server guide)
[sssd]
services = nss, pam
config_file_version = 2
domains = EARL.TEA.LOCALDOMAIN
[domain/EARL.TEA.LOCALDOMAIN]
id_provider = ad
access_provider = ad
ldap_id_mapping = false
/etc/nsswitch.conf
sssd を入れたら勝手に書き換わっていた。
passwd: compat sss
group: compat sss
shadow: compat sss
gshadow: files
hosts: files dns
networks: files
protocols: db files
services: db files sss
ethers: db files
rpc: db files
netgroup: nis sss
sudoers: files sss
/etc/samba/smb.conf
- 再起動:
sudo systemctl restart smbd.service nmbd.service winbind.service
- 参考: smb.conf manpage
- 参考: Idmap config ad (Samba wiki)
- 参考: Setting up Samba as a Domain Member
[global]
workgroup = EARL
client signing = yes
client use spnego = yes
kerberos method = secrets and keytab
realm = EARL.TEA.LOCALDOMAIN
security = ads
dos charset = cp932
unix charset = utf-8
log file = /var/log/samba/log.%m
ldap ssl = no
idmap config EARL:backend = ad
idmap config EARL:schema_mode = rfc2307
idmap config EARL:range = 1001-20000
[homes]
comment = Home Directories
browseable = Yes
read only = No
回避方法: パーミッションを world writeble に
事前に空のフォルダを作成し、 Everyone に対して書き込み権限を与える。
- Samba ネットワークドライブを公開している Unix 側 では
chmod 777 パス
で良い - マウントしている Windows 側 ではフォルダのプロパティで変更するか、次のようにする
-
icacls パス /grant everyone:(F)
(フォルダに対するフルコントロール) -
icacls パス /grant everyone:(IO)(OI)(CI)(F)
(フルコントロールを子孫フォルダーと子孫ファイルでも継承させる)- これを半自動化するスクリプトを後に示す
スクリプト化
エンドユーザーにコマンドラインを叩かせるのは運営上厄介なので次のようなスクリプトを作った。
# Z:\AndroidStudioProjects\ 以下に プロジェクトフォルダを作成するスクリプト (2018/4)
# Android Studio が Z:\ にプロジェクトを作成するには Everyone に書き込み権限がないとうまくいかないため.
# (プロジェクト作成ウィザードの Finish ボタンをクリックした時にエラー)
$WORKSPACE_DIR = "Z:\AndroidStudioProjects"
function Read-InputBoxDialog([string]$Message, [string]$WindowTitle, [string]$DefaultText)
{
Add-Type -AssemblyName Microsoft.VisualBasic
return [Microsoft.VisualBasic.Interaction]::InputBox($Message, $WindowTitle, $DefaultText)
}
function MakeDirAndGrantEveryone([string]$path)
{
Z:
New-Item $path -itemType Directory -Force
icacls $path /grant "everyone:(IO)(OI)(CI)(F)"
icacls $path /grant "everyone:(F)"
}
function msg([string] $text)
{
Write-Host $text -ForegroundColor Cyan
}
Z:
New-Item $WORKSPACE_DIR -itemType Directory -Force
$newProjectName = Read-InputBoxDialog -Message "新しいプロジェクトの名前を入力してください (スペース不可)。"
if( $newProjectName -ne "" ) {
$path = Join-Path $WORKSPACE_DIR $newProjectName
MakeDirAndGrantEveryone( $path )
echo ""
echo "******************************************************************************"
echo "*"
echo "* Android Studio を起動し、"
echo "*"
msg "* $path"
echo "*"
echo "* にプロジェクトを作成してください。"
echo "*"
echo "******************************************************************************"
echo ""
pause
}
ソースを追った、より細かい話
- Android Studio はプロジェクトの作成直後に Java の Files.isWritable を呼んでフォルダへの書き込み権限をチェックする。 NewProjectModel.java:225, FileSystemFileOp.java:174
- Samba のネットワークドライブに対して Files.isWritable は 書き込み権限があっても false を返す 参考 OpenJDK issue
- Java の Files.isWritable は Windows API の AccessCheck 関数を呼ぶ (sun.nio.fs.WindowsFileSystemProvider.java, sun.nio.fs.WindowsSecurity.java) が、これも false を返すようだ
- Android Studio 同梱の Open JDK (JRE) 8 と、最新版の Oracle Java で動作確認
各ソフトウェア提供元などの議論
- Oracle, OpenJDK:
- OpenJDK にバグ JDK-8154915 がポストされているが no fix でクローズされそうな見込み
- Stackoverflow に Files.isWritable の議論とその回答 がある
- Samba: 2003 年に それらしい投稿 を発見したがリプライなし
- Google: それらしい議論なし
- Microsoft: AuthzAccessCheck を使うサンプルコードが GetEffectiveRightsFromAcl function のページに掲載されている。 AuthzAccessCheck は サーバーから取得したファイルの security descriptor の各 ACE (権限リスト) を、 現在のユーザー (を表す AUTHZ_CLIENT_CONTEXT_HANDLE) でチェックするだけ。ドメインにログオンしている状態でこれがうまくいっていないということは、 Samba が渡してくる ACE の SID が現在のユーザーの SID と合致していない可能性がある? → そうだった。
- NON-solution 1: How to check Access Rights - Ruminations
- NON-solution 2: The Windows Access Control Model: Part 2