mizdra's blog

ぽよぐらみんぐ

Node.js におけるファイル読み取りエラーのクロスプラットフォーム対応の仕組み

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 という関数が実装されてて、ここでファイルの読み取りやらエラーコードの正規化をしていることがわかる。

そしてこの fs__read 関数が fs.readFileSync の内部で呼ばれている、という訳。

同期的な API の中でも libuv の API が呼び出されてるの知らなかったので、勉強になった。

ポケットモンスター・ポケモン・Pokémon・は任天堂・クリーチャーズ・ゲームフリークの登録商標です.

当ブログは @mizdra 個人により運営されており, 株式会社ポケモン及びその関連会社とは一切関係ありません.