Windows の Node.js で存在しないファイルを fs.readFileSync で読み取ろうとすると ENOENT が返ってくる。けど ENOENT は POSIX で定義されてるエラーコードであって、Windows のものではない。どこかで正規化されてるのか? という疑問が出てきたので調べてみた。
答え
Node.js の公式ドキュメントの error.errno の説明に答えが書いてあった。
https://nodejs.org/api/errors.html#errorerrno
On Windows the error number provided by the system will be normalized by libuv.
どうも libuv でエラーコードの正規化がされてるらしい。確かに libuv のコードを見ると、int uv_translate_sys_error(int sys_errno) 関数の中にエラーコードを正規化するコードが書かれていた。
// https://github.com/libuv/libuv/blob/12d1ed1380c59c5ec27503cf149833de6f0e6bb0/src/win/error.c#L134-L144 より int uv_translate_sys_error(int sys_errno) { // ... switch (sys_errno) { // ... case ERROR_BAD_PATHNAME: return UV_ENOENT; case ERROR_DIRECTORY: return UV_ENOENT; case ERROR_ENVVAR_NOT_FOUND: return UV_ENOENT; case ERROR_FILE_NOT_FOUND: return UV_ENOENT; case ERROR_INVALID_NAME: return UV_ENOENT; case ERROR_INVALID_DRIVE: return UV_ENOENT; case ERROR_INVALID_REPARSE_DATA: return UV_ENOENT; case ERROR_MOD_NOT_FOUND: return UV_ENOENT; case ERROR_PATH_NOT_FOUND: return UV_ENOENT; case WSAHOST_NOT_FOUND: return UV_ENOENT; case WSANO_DATA: return UV_ENOENT; // ... } // ... }
あれでも libuv ってイベントループを実装するためのライブラリじゃなかったっけ? fs.readFileSync は同期的な API でイベントループ関係ない気がするんだけど。 同期的な API も libuv に依存してるの?
答え2
...という疑問を持ったので調べてみたら、どうも libuv にはファイルシステムに関する機能もあるようだった。README の「Feature highlights」を見ると、「File system events」「Child processes」「Thread pool」など色々書かれてる。
libuv のコードを見てみると、Windows 向けに fs__read という関数が実装されてて、ここでファイルの読み取りやらエラーコードの正規化をしていることがわかる。
- https://github.com/libuv/libuv/blob/15f29dc08fe72cd189002f1b8ae22fd82264deef/src/win/fs.c#L588C6-L588C14
- https://github.com/libuv/libuv/blob/15f29dc08fe72cd189002f1b8ae22fd82264deef/src/win/fs.c#L654
- https://github.com/libuv/libuv/blob/15f29dc08fe72cd189002f1b8ae22fd82264deef/src/win/fs.c#L79
そしてこの fs__read 関数が fs.readFileSync の内部で呼ばれている、という訳。
- https://github.com/nodejs/node/blob/dbe45b767b0bb9c984206edd4ad0b5658d7c2fe4/lib/fs.js#L457
- https://github.com/nodejs/node/blob/dbe45b767b0bb9c984206edd4ad0b5658d7c2fe4/lib/fs.js#L411
- https://github.com/nodejs/node/blob/dbe45b767b0bb9c984206edd4ad0b5658d7c2fe4/lib/fs.js#L734
- https://github.com/nodejs/node/blob/dbe45b767b0bb9c984206edd4ad0b5658d7c2fe4/src/node_file.cc#L2659
同期的な API の中でも libuv の API が呼び出されてるの知らなかったので、勉強になった。