EFSをsambaで共有する

## TL;DR

sambaがNFSボリューム内を共有していると気づいたら quotaを確認しにいくよ。
EFSはquotaに対応していないみたいだよ。
応答がないから60秒のタイムアウトまでsambaのファイルコピーとかが止まるよ。
sambaのquotaを確認する仕組みは停止できないっぽいよ。
"get quota command"でquota取得を別コマンドに委ねられるからこれを使うと幸せになれるかも。

## 序章

sambaでとある仕組みを構築していたのだが、今回オンプレからAWSに移行しようということになった。
その際、AWSインフラ担当が、

A担「それFSxじゃ駄目なの？」
私 「FSxでinotify使えるなら(以下略)」
A担「EC2もったいないじゃん」
私 「再設計する暇ないし費用もらってない」
A担「使用量の見積もり面倒だからsambaの部分EFSでもいい？」
私 「EFSってNFSだよね。ファイルロックできたっけか？」
A担「AWSでできない訳ないじゃん くぁwせdrftgyふじこlp」
私 「ああ、ロックとかできそうだね。inotifyもできそう。」
A担「ほれ、使え」
私 「カチャカチャ…… えっと、動くんだけど、samba共有にファイルコピーすると毎回1分くらい待たされるよ。なにこれ。orz」

まあ、単なるファイル共有ならFSxを使った方がGBあたりの単価安いし、容量決まってるならEBSで十分だし、EFSをsambaで共有しようなんて思ったほうが負け、という気はする。でも進むしか無い。頑張れ俺。

## 調査

まず時間を計測。ちょうど60秒。これネットワーク系タイムアウトだろうとあたりをつける。
次にEFSじゃなくてローカルストレージで共有。こちらは待たされない。ということはEFSに原因。
ここまで分かったらあとは…「log level = 10」攻撃。smb.confにこれを付けて大量のログから原因を探る。

```log
[19:47:48.757277] ../../source3/lib/sysquotas_nfs.c:131(sys_get_nfs_quota)
  sys_get_nfs_quota: path[/xxxxxx] bdev[fs-00000000000000000.efs.ap-northeast-1.amazonaws.com:/] qtype[2]
[19:47:48.757285] ../../source3/lib/sysquotas_nfs.c:165(sys_get_nfs_quota)
  sys_get_nfs_quotas: Asking for quota of path 'fs-00000000000000000.efs.ap-northeast-1.amazonaws.com' on host '', rpcprog '100011', rpcvers '1', network 'udp'
[19:48:48.809780] ../../source3/lib/sysquotas_nfs.c:285(sys_get_nfs_quota)
  sys_get_nfs_quotas: finished
```
おお、1分の空白の時間が見つかった。

これでバージョンがすこし違ってもソースコードの該当箇所がだいたい分かるので、探す。

https://github.com/samba-team/samba/blob/master/source3/lib/sysquotas_nfs.c

clnt_create()の返り値がNULLっぽい。まあ何にせよquotaの情報を取りに行ってコケてるっぽい。それにしてもsambaはnfsを検知して対応した動作をするように作られているっぽいことが分かって驚き。そして少し安心。

なんかudpの通信しようとしてるっぽいし、EFSでは無理っぽいし、となるとquotaをチェックしないようにしたい。が、そんなオプション無いっぽい。

ということで「get quota command」を見つけてきた。

## get quota command

> get quota command は Samba が実行されている OS 上で、API が提供されていない場合に限り使用すべきである。このオプションはSambaがquotaをサポートするようにコンパイルされた時に のみ有効である。このパラメーターは、指定されたディレクトリが存在するパーティションについて、指定されたユーザーやグループのクオータ情報を取得するスクリプトへのパスを指定する。
> 
> このスクリプトは三つの引数をとる:
> 
> * ディレクトリ
> * 問い合わせのタイプ
> * ユーザーの uid もしくはグループの gid
> 
> ディレクトリは、実際はほとんど "." である。これは、スクリプト が問い合わせもできるカレントワーキングディレクトリからの相対として 扱われる必要がある。
> 
> 問い合わせのタイプは以下のいずれかの値をとる:
> 
> 1 - ユーザーのクオータ
> 2 - ユーザーのデフォルトのクオータ (uid = -1)
> 3 - グループのクオータ
> 4 - グループのデフォルトのクオータ (gid = -1)
> 
> スクリプトの出力は、以下の要素をスペースで区切った1行の形式で行なわれる:
> 
> 要素1 - クオータフラグ (0 = クオータは無効、 1 = クオータは有効、 2 = クオータは有効で使用を強制されている)
> 要素2 - 現在の使用済ブロック数
> 要素3 - ブロック数のソフトウェア的な上限
> 要素4 - ブロック数のハードウェア的な上限
> 要素5 - 現在の使用済 inode 数
> 要素6 - inode 数のソフトウェア的な上限
> 要素7 - inode 数のハードウェア的な上限
> 要素8(オプション) - ブロックあたりのバイト数(デフォルト1024)
> 
> 既定値: get quota command =
> 
> 例: get quota command = /usr/local/sbin/query_quota

(http://www.samba.gr.jp/project/translation/current/htmldocs/manpages/smb.conf.5.html)

## 対策

シェルスクリプトみたいなので引数を受け取って値を返せばいいらしいが、色々よくわからない。ということで先人の知恵を拝借。

```sh:/usr/local/sbin/samba-get-quota-command
#!/bin/bash

ARGS=( "$@" )
DIRLEN=$(( $# - 2 ))

DIR="${ARGS[@]::${DIRLEN}}"
QUERY=${ARGS[-2]}
UID_OR_GID=${ARGS[-1]}

# optional log to a file
echo "$EUID: quota request of type $QUERY for $UID_OR_GID on $DIR" >> /var/log/samba/quota.log

#This script should print one line as output with spaces between the columns. The printed columns should be:
#    1 - quota flags (0 = no quotas, 1 = quotas enabled, 2 = quotas enabled and enforced)
#    2 - number of currently used blocks
#    3 - the softlimit number of blocks
#    4 - the hardlimit number of blocks
#    5 - currently used number of inodes
#    6 - the softlimit number of inodes
#    7 - the hardlimit number of inodes
#    8 (optional) - the number of bytes in a block(default is 1024)
echo "0 0 0 0 0 0 0"
```

https://bugs.launchpad.net/ubuntu/+source/samba/+bug/1735953

なるほど、必要なのは最後の1行の echo のみだった。このスクリプトを適当に作って置いて、
smb.confのglobalセクションにget quota commandを追加。

```ini
[global]
  get quota command = /usr/local/sbin/samba-get-quota-command
```

ということで、コピーしても1分待たされることもなく、うまく稼働しましたとさ。

## あれ？

/var/log/samba/quota.log に何も出力されてない。呼ばれてない？あれ？