いけむランド

はてダからやってきました

解説 : Fix extension file permissions on Cygwin in namespace feature

雑に PR を出したので解説をする。

github.com

Ruby の namespace 機能をきちんと触っていなかったなと思ったため、サンプル (↓のブログ) を見ながら Cygwin 環境でいじっていると native extension (.so) を require すると LoadError が発生したので追いかけてみた。

moneyforward-dev.jp

原因は namespace は使用する .so を一時領域にコピーして、それを dlopen でロードするのだが、Cygwin 環境では .so に executable permission bit がセットされていないと失敗することであった。

Ruby 内部の namespace の処理で .so をコピーする時に fread で読み出す → fwrite で書き出すという方法をとっている。

Cygwin 環境ではこれによりコピーされた .soWindows 側のファイルシステムに従った permission を持つため、必ずしもコピー元の .so の permission とは一致しなくなる場合がある。

cygwin.com

Cygwin maps Win32 file ownership and permissions to ACLs by default

今回はここで executable permission bit がセットされていなかった。(umask022 にしていたけど、だめだった。)

そこでコピー後に元の .so の permission と同じ設定にする処理を追加することで、例外が発生することなくコピーされた .sorequire できるようになることを確認した。

ちなみに executable permission bit がセットされていないと dlopen に失敗する原因は「Cygwin DLL の中で dlopen が呼ぶ LoadLibraryWACL での Executable 属性がないと失敗する (ERROR_ACCESS_DENIED)」ためみたいなのだが、どうも公式仕様では明言されていないようで、現在の挙動としてはそうなっているという状態のようである。

sourceware.org

If the library is not executable, the LoadLibrary function returns an "access denied" error. You should have this observed, too.