NFS v4ではUTF-8ファイルのみ使用でき、EUCファイル名は使用できない

むかしからSolarisサーバを使っているユーザのとこにNetAppを入れることになった。

どうやらEUC-JPファイル名のものがあるらしいのでNetAppのvolume language を ja_v1で作成してから検証してみたところ、Solaris10環境からNFSマウントして書き込もうとすると「引数が正しくありません」というエラーとなり書き込みが出来ない。

しばらくあれやこれや悩んだ結果、「NFS v4で許されているファイル名は UTF-8 記述のものだけ」という仕様上の問題であることが判明した。(RFC7530 Network File System (NFS) Version 4 Protocol の 12.4. String Encoding に記載)

このため、EUC-JPのファイル名を使いたい場合は、NFS ver3でマウントすることが必須、ということが分かった。

検証過程

まず、Solaris10環境から、NFS ver4で /mnt4 にマウントし、NFS ver3で /mnt3 にマウントした。

# mount 172.17.44.103:/eucvol /mnt4
# mount -o vers=3 172.17.44.103:/eucvol /mnt3
# df -h|grep mnt
mnttab                   0K     0K     0K     0%    /etc/mnttab
172.17.44.103:/eucvol   1.9G   808K   1.9G     1%    /mnt4
172.17.44.103:/eucvol   1.9G   808K   1.9G     1%    /mnt3
# mount|grep /mnt
/etc/mnttab on mnttab read/write/setuid/devices/rstchown/dev=4c80001 on (金)  8 月 12 15:30:07 2022
/mnt4 on 172.17.44.103:/eucvol remote/read/write/setuid/devices/rstchown/xattr/dev=4f40004 on (金)  8月 12 15:33:31 2022
/mnt3 on 172.17.44.103:/eucvol remote/read/write/setuid/devices/rstchown/vers=3/xattr/dev=4f40005 on (金)  8月 12 15:33:38 2022
#

この状態でそれぞれを確認すると、下記の様になっている。

# ls -l /mnt3
合計 8
-rw-r--r--   1 root     root           6  8月 10日  14:37 test.txt
-rw-r--r--   1 root     root           0  8月 10日  14:51 てすと
drwxr-xr-x   2 root     other       4096  8月 10日  14:35 表計算
# ls -l /mnt4
合計 8
-rw-r--r--   1 root     root           6  8月 10日  14:37 test.txt
-rw-r--r--   1 root     root           0  8月 10日  14:51 礒窿
・rwxr-xr-x   2 root     daemon      4096  8月 10日  14:35 茵 ・
・
#

NFS ver4でマウントしている /mnt4 の方は日本語文字(EUC-JP)が正常に表示できていない。

次に /tmp/てすと2 のコピーを試みる

# cp  /tmp/てすと2 /mnt4
cp: /mnt4/てすと2 を作成できません: 引数が正しくありません。
# LANG=C
# export LANG
# date
Fri Aug 12 15:37:39 JST 2022
# cp  /tmp/てすと2 /mnt4
cp: cannot create /mnt4/てすと2: Invalid argument
#

NFS ver4でマウントしている領域へのコピーは「引数が正しくありません」、英語メッセージの場合は「Invalid argument」で失敗する。

対して、NFS ver3領域へのコピーは問題なく実施できる。

# LANG=ja
# export LANG
# date
2022年08月12日 (金) 15時39分11秒 JST
# cp /tmp/てすと2 /mnt3
# ls -l /mnt3
合計 8
-rw-r--r--   1 root     root           6  8月 10日  14:37 test.txt
-rw-r--r--   1 root     root           0  8月 10日  14:51 てすと
-rw-r--r--   1 root     root           8  8月 12日  15:39 てすと2
drwxr-xr-x   2 root     other       4096  8月 10日  14:35 表計算
#

なお、この領域をWindows側から見た場合はこのように正常に表示されている。

ちなみに・・・ONTAP CLIからvolume languageコマンドを実行してみたところ、標準権限(admin)とadvanced権限とで表示される内容が違う、というのが興味深かったです。

netapp101::> volume language -vserver netapp103 -volume eucvol

Volume language is ja_v1 (Japanese euc-j) for netapp103:eucvol

netapp101::> set adv

Warning: These advanced commands are potentially dangerous; use them only when directed to do so by NetApp personnel.
Do you want to continue? {y|n}: y

netapp101::*> volume language -vserver netapp103 -volume eucvol

Volume language is ja_v1 (Japanese euc-j) for netapp103:eucvol

Translation Versions
        OEM Character set is cp932_v1|cp932_v1|Wed Jan 22 23:18:24 UTC 2003 for netapp103:eucvol

        NFS Character set is eucj_v1|eucj_v1|Wed Jan 22 00:46:42 UTC 2003 for netapp103:eucvol

netapp101::*>

Linux上からIPv6アドレス指定でsshするときにはインタフェース名をつける

Linux上からIPv6アドレスを指定してsshしようとしたら「port 22: Invalid argument」と言われてしまった。

[root@centos8 ~]# ssh root@fe80::250:56ff:fe8f:aba9
ssh: connect to host fe80::250:56ff:fe8f:aba9 port 22: Invalid argument
[root@centos8 ~]#

きちんとpingでは応答あるのに

[root@centos8 ~]# ping -6 fe80::250:56ff:fe8f:aba9 -c 5
PING fe80::250:56ff:fe8f:aba9(fe80::250:56ff:fe8f:aba9) 56 data bytes
64 bytes from fe80::250:56ff:fe8f:aba9%ens192: icmp_seq=1 ttl=255 time=0.246 ms
64 bytes from fe80::250:56ff:fe8f:aba9%ens192: icmp_seq=2 ttl=255 time=0.269 ms
64 bytes from fe80::250:56ff:fe8f:aba9%ens192: icmp_seq=3 ttl=255 time=0.250 ms
64 bytes from fe80::250:56ff:fe8f:aba9%ens192: icmp_seq=4 ttl=255 time=0.303 ms
64 bytes from fe80::250:56ff:fe8f:aba9%ens192: icmp_seq=5 ttl=255 time=0.316 ms

--- fe80::250:56ff:fe8f:aba9 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 135ms
rtt min/avg/max/mdev = 0.246/0.276/0.316/0.035 ms
[root@centos8 ~]#

なんでかと思ったら、sshコマンドでは、sshコマンドを実行したホストのどのインタフェースを使ってアクセスするのかを明示的に指定する必要があったようだ。

[root@centos8 ~]# ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:9c:2a:d1 brd ff:ff:ff:ff:ff:ff
    inet 172.17.44.49/16 brd 172.17.255.255 scope global noprefixroute ens192
       valid_lft forever preferred_lft forever
    inet6 xxxx:xxx:xxxx:4700:2788:3aad:4bfa:6715/64 scope global dynamic noprefixroute
       valid_lft 2172237sec preferred_lft 185037sec
    inet6 fe80::2c85:a948:cd5:d5e1/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
[root@centos8 ~]#

つかうネットワークインタフェースは「ens192」なので、IPv6アドレスの後ろに「%ens192」をつけて実行

[root@centos8 ~]# ssh  root@fe80::250:56ff:fe8f:aba9%ens192
Password:
Last login: Wed Apr  7 16:24:37 2021 from fe80::2c85:a948
Oracle Corporation      SunOS 5.11      11.3    September 2015
root@solaris11:~# exit
logout
Connection to fe80::250:56ff:fe8f:aba9%ens192 closed.
[root@centos8 ~]#

問題無く接続できました。

Solaris11でホスト名/IPアドレスを変更する

Solaris10だったら以下のファイルあたりの変更でやってた

/etc/nodename
/etc/hostname.ネットワークインタフェース名
/etc/hosts
/etc/resolv.conf
/etc/nsswitch.conf

Solaris11だとファイルを編集するのではなく、コマンドで変更する形になる

資料:Oracle® Solaris 10 から Oracle Solaris 11.3 への移行「Oracle Solaris 11 でのネットワークの構成

現在のホスト名確認

# hostname

ホスト名変更

# svccfg -s svc:/system/identity:node setprop config/nodename=ホスト名
# svcadm refresh svc:/system/identity:node
# svcadm restart svc:/system/identity:node
# vi /etc/hosts で関係するホスト名を修正
# reboot

refreshのところは「svcadm refresh node」でも大丈夫説がある模様

ネットワーク設定確認

ネタ元 IPv4 インタフェースの構成

# ipadm show-addr
# ipadm show-if
# route -p show
# netstat -rn
# dladm show-phys
# dladm show-link

ネットワーク設定変更

現在値を消して、新規作成となるが2種類手順がある

アドレス設定のみ削除するもの

# ipadm delete-addr net?/v4
# ipadm create-addr -T static -a local=<IPアドレス>/<プレフィックス> net?/v4

ネットワーク定義全体を削除するもの

# ipadm delete-ip net?
# ipadm create-ip net?
# ipadm create-addr -T static -a local=<IPアドレス>/<プレフィックス> net?/v4

デフォルトゲートウェイ設定

# route -p add default <IPアドレス>

DNS設定確認

#  svccfg -s network/dns/client
svc:network/dns/client> listprop config
<略>
svc:network/dns/client> quit
#

DNS設定変更

(ネタ元 DNS クライアントを有効にする方法 )

#  svccfg -s network/dns/client
svc:/network/dns/client> setprop config/search = astring: ("<ドメイン名>" "<ドメイン名>")
svc:/network/dns/client> setprop config/nameserver = net_address: (<IPアドレス> <IPアドレス>)
svc:/network/dns/client> setprop config/options = timeout:1 
svc:/network/dns/client> select network/dns/client:default
svc:/network/dns/client:default> refresh 
svc:/network/dns/client> quit
#
# svcadm enable network/dns/client 

nsswitchの設定を確認する場合

# cat /etc/nsswitch.conf
# svccfg -s system/name-service/switch 
svc:/system/name-service/switch> listprop config
<略>
svc:/system/name-service/switch> quit
#

nsswitchを変更する場合の例

# svccfg -s system/name-service/switch 
svc:/system/name-service/switch> setprop config/host = astring: "dns files [TRYAGAIN=0]" 
svc:/system/name-service/switch> select system/name-service/switch:default 
svc:/system/name-service/switch:default> refresh 
svc:/system/name-service/switch:default> quit
# svcadm enable system/name-service/switch 
# svcadm refresh name-service/switch

Oracle公式に書いてあるDNSサーバ設定手順例

# svccfg -s dns/client setprop config/nameserver=net_address: 192.168.1.1
# svccfg -s dns/client setprop config/domain = astring: "foohost.org"
# svccfg -s name-service/switch setprop config/host = astring: "files dns"
# svcadm refresh name-service/switch
# svcadm refresh dns/client

rootユーザのログイン拒否問題

telnet は /etc/default/login の「CONSOLE=/dev/console」をコメントにする

sshは/etc/ssh/sshd_configに「PermitRootLogin yes」を設定して「svcadm restart ssh」

1つのNICに複数のIPアドレスをつける

eth0:1 というような感じで1つのNICに複数のIPアドレスをつけるやり方

# ipadm create-addr -T static -a local=xxx.xxx.xxx.xxx/24 net0/newif
#

ifconfig -aで見ると、net0:1 というデバイスが作られています。

削除する場合は以下

# ipadm delete-addr net0/newif
#

2段階のNFSマウントをする方法

直接アクセスできないネットワークにあるNFSサーバをNFSでマウントすることはできないか試行錯誤してみた。

普通にCentOS7やSolaris11からやってみたところ、NFSマウントした領域のNFS exportでの公開はnfsd側から「Cannot export /mnt, possibly unsupported filesystem or fsid= required」とか、「Invalid filesystem」とか言われて設定できない。

これはuser-spaceで動作するnfsdを使えば回避できるんじゃないかと探してみた結果、unfs3というものを発見。ソースコードは https://github.com/unfs3/unfs3

Linux,FreeBSD,Solaris,AIX,Irix,MacOSXで動く以外に、Windows上でも制限ありで動作するとのこと。

Windows上で動かした場合は、unfsdが使用するWindowsユーザを1つ割り当てる形になるので、NFS経由のアクセスは全てそのWindowsユーザがアクセスしている、という扱いになるようだ。

あと、このunfs3はNFS ver3のみ使え、NFS v4やNFS v2でのアクセスには対応していない。また、NFS v3でもREADDIRPLUS(属性付きディレクトリの読み取り)周りは実装していないとのこと。

READDIRPLUSはOracle/Solarisのドキュメントによればlsコマンドなどでディレクトリ内のファイル一覧を表示させる動作を高速化するためのものなので、まぁ、なくてもなんとかなる感じのもの。

属性付きディレクトリの読み取り
NFS バージョン 3 では、READDIRPLUS と呼ばれる操作があります。たとえば、ls や ls -l などの、大部分の READDIR が READDIRPLUS コールとして発行されます。バージョン 3 で ls -l コマンドを実行すると、ディレクトリ内の名前リストと共に、ファイルハンドルと属性が返されます。バージョン 2 では、名前が最初に返され、ファイルハンドルと属性を取得するには、続いてサーバーを呼び出す必要があります。
バージョン 3 の READDIRPLUS 操作の利点は、ファイルごとに GETATTR 要求を送信する必要がないため時間が短縮され、ls と ls -l の速度が同程度になることです。

要件は満たせそうなので、とりあえずテスト用CentOS7環境でunfs3を動作させてみる。

準備

環境をインストール

# yum install git
# yum groupinstall "開発ツール"

コンパイル

まず、ソースコードの入手

$ git clone https://github.com/unfs3/unfs3.git

READMEにあるとおりbootstrap&configureを実行

$ cd unfs3/unfs3
$ ./bootstrap
$ ./configure

そしてmake

$ make
for i in Config ; do (cd $i && make all) || exit; done
make[1]: ディレクトリ `/root/unfs3/Config' に入ります
gcc -g -O2 -Wall -W -I.. -I. -I..   -c -o lex.yy.o lex.yy.c
gcc -g -O2 -Wall -W -I.. -I. -I..   -c -o y.tab.o y.tab.c
ar crs lib.a lex.yy.o y.tab.o
make[1]: ディレクトリ `/root/unfs3/Config' から出ます
gcc -g -O2 -Wall -W  -D_GNU_SOURCE -I.   -c -o afsgettimes.o afsgettimes.c
gcc -g -O2 -Wall -W  -D_GNU_SOURCE -I.   -c -o afssupport.o afssupport.c
gcc -g -O2 -Wall -W  -D_GNU_SOURCE -I.   -c -o attr.o attr.c
attr.c: 関数 ‘get_free_bad_dir_entry’ 内:
attr.c:550:5: エラー: ‘for’ ループ初期化宣言は C99 モード内でのみ許可されてい ます
     for (int i = 0;i < BAD_DIR_CACHE_SIZE;i++) {
     ^
attr.c:550:5: 備考: オプション -std=c99 または -std=gnu99 をコードコンパイル時に使用してください
attr.c: 関数 ‘find_bad_dir_entry’ 内:
attr.c:573:5: エラー: ‘for’ ループ初期化宣言は C99 モード内でのみ許可されてい ます
     for (int i = 0;i < BAD_DIR_CACHE_SIZE;i++) {
     ^
make: *** [attr.o] エラー 1
$

エラーとなってしまいます。

これはコンパイル時のオプションに「-std=c99」を指定するようにして解決

$ export CPPFLAGS="-std=c99";./configure
$ make
<略>
$

インストールと設定

普通にmake installすると/usr/local以下にインストールされます。

# make install
/usr/bin/install -c -d /usr/local/sbin
/usr/bin/install -c -d /usr/local/share/man/man7
/usr/bin/install -c -d /usr/local/share/man/man8
/usr/bin/install -c unfsd /usr/local/sbin/unfsd
/usr/bin/install -c -m 644 ./Extras/tags.7 /usr/local/share/man/man7/tags.7
/usr/bin/install -c -m 644 ./unfsd.8       /usr/local/share/man/man8/unfsd.8
#

NFSで公開するディレクトリの設定は、普通のnfsdと同じく /etc/exports ファイルを使用。「-e」オプションで別のファイルを指定することも可能です。

注意点としては、Linuxだとホスト名指定に「*」とnetgroupが使用できず、ログに「unfsd[20479]: syntax error in ‘/etc/exports’, exporting nothing」といった出力が出てしまうという点です。

「*」については「0.0.0.0/0」で代替できます。

/etc/exports ファイルを編集した場合、変更にはexportfsコマンドは使用できません。

unfsdに対してHUPシグナルを送ることで反映されます。(kill -HUP unfsdのPID)

unfsdの起動は「/usr/local/sbin/unfsd」の実行、停止はunfsdへのTERMシグナル送信(kill -TERM unfsdのPID)です。

Solaris 11をNISクライアントにする

Solaris 環境からのNFS v4アクセスを確認するため、VMware上にSolaris 11をインストールした。

NISによるユーザIDの共通化も検証するため、NISクライアントの設定を行った。

NISクライアントにする設定は「domainname NISドメイン名」でNISのドメイン名を設定したあと「ypinit -c」で終わり。

つぎは/etc/nsswitch.confを編集して…と思ってvi /etc/nsswitch.confで開いてみると、手動で編集するな、という記載が…

マニュアルを探すと「Oracle Solaris 11.1 でのネームサービスおよびディレクトリサービスの作業」の「ネームサービススイッチの管理」があった。

「データベースのソースを切り替える方法」にある「svccfg -s system/name-service/switch」で入ったあと「listprop config」で現在の設定値を確認できる。

root@solaris11:~# svccfg -s system/name-service/switch
svc:/system/name-service/switch> listprop config
config                      application
config/value_authorization astring     solaris.smf.value.name-service.switch
config/default             astring     files
config/host                astring     "files dns"
config/printer             astring     "user files"
svc:/system/name-service/switch> quit
root@solaris11:~#

nsswitch.conf にあるいろんなエントリのうち、hostsとprinterしかない。

じゃぁ、手動で設定すればいいのかと「setprop config/passwd=”files nis”」と実行してみるとエラー。

じゃぁ、と「setprop config/password=”files nis”」に変えてみると設定成功

「setprop config/group=”files nis”」も実行。

svc:/system/name-service/switch> setprop config/password="files nis"
svc:/system/name-service/switch> setprop config/group="files nis"
svc:/system/name-service/switch> listprop config
config                      application
config/value_authorization astring     solaris.smf.value.name-service.switch
config/default             astring     files
config/host                astring     "files dns"
config/printer             astring     "user files"
config/password            astring     "files nis"
config/group               astring     "files nis"
svc:/system/name-service/switch>

「quit」で抜けて、「svcadm refresh name-service/switch」を実行するとシステムに反映されて、ls -l でみた場合の結果に反映された。

また、値が反映されると/etc/nsswitch.confの内容も書き換わる。


NISの解除方法

NIS クライアントサービスの無効化より

root@solaris11:~# svcadm disable network/nis/domain
root@solaris11:~# svcadm disable network/nis/client
root@solaris11:~# reboot