diff options
author | Richard Lau <riclau@uk.ibm.com> | 2019-04-13 02:29:01 +0100 |
---|---|---|
committer | Richard Lau <riclau@uk.ibm.com> | 2019-04-16 13:59:43 -0400 |
commit | 2400cbcf7c6b3c35c96b199676cb8a577f6e701a (patch) | |
tree | 514e71b8c1328aedd3c9e8e6a2307786344e149d /src/node_file.cc | |
parent | 96e46d37c458ab3d6cf1148a471cfd1aac7aafe6 (diff) | |
download | android-node-v8-2400cbcf7c6b3c35c96b199676cb8a577f6e701a.tar.gz android-node-v8-2400cbcf7c6b3c35c96b199676cb8a577f6e701a.tar.bz2 android-node-v8-2400cbcf7c6b3c35c96b199676cb8a577f6e701a.zip |
fs: fix infinite loop with async recursive mkdir on Windows
If `file` is a file then on Windows `mkdir` on `file/a` returns an
`ENOENT` error while on POSIX the equivalent returns `ENOTDIR`. On the
POSIX systems `ENOTDIR` would break out of the loop but on Windows the
`ENOENT` would strip off the `a` and attempt to make `file` as a
directory. This would return `EEXIST` but the code wasn't detecting
that the existing path was a file and attempted to make `file/a` again.
PR-URL: https://github.com/nodejs/node/pull/27207
Fixes: https://github.com/nodejs/node/issues/27198
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
Reviewed-By: Ben Coe <bencoe@gmail.com>
Diffstat (limited to 'src/node_file.cc')
-rw-r--r-- | src/node_file.cc | 48 |
1 files changed, 32 insertions, 16 deletions
diff --git a/src/node_file.cc b/src/node_file.cc index e1acf82408..5d3b48cfa4 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -1270,8 +1270,15 @@ int MKDirpSync(uv_loop_t* loop, } default: uv_fs_req_cleanup(req); + int orig_err = err; err = uv_fs_stat(loop, req, next_path.c_str(), nullptr); - if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) return UV_EEXIST; + if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) { + uv_fs_req_cleanup(req); + if (orig_err == UV_EEXIST && continuation_data.paths.size() > 0) { + return UV_ENOTDIR; + } + return UV_EEXIST; + } if (err < 0) return err; break; } @@ -1338,23 +1345,32 @@ int MKDirpAsync(uv_loop_t* loop, break; } default: - if (err == UV_EEXIST && - req_wrap->continuation_data->paths.size() > 0) { - uv_fs_req_cleanup(req); - MKDirpAsync(loop, req, path.c_str(), - req_wrap->continuation_data->mode, nullptr); - } else { + uv_fs_req_cleanup(req); + // Stash err for use in the callback. + req->data = reinterpret_cast<void*>(static_cast<intptr_t>(err)); + int err = uv_fs_stat(loop, req, path.c_str(), + uv_fs_callback_t{[](uv_fs_t* req) { + FSReqBase* req_wrap = FSReqBase::from_req(req); + int err = req->result; + if (reinterpret_cast<intptr_t>(req->data) == UV_EEXIST && + req_wrap->continuation_data->paths.size() > 0) { + if (err == 0 && S_ISDIR(req->statbuf.st_mode)) { + Environment* env = req_wrap->env(); + uv_loop_t* loop = env->event_loop(); + std::string path = req->path; + uv_fs_req_cleanup(req); + MKDirpAsync(loop, req, path.c_str(), + req_wrap->continuation_data->mode, nullptr); + return; + } + err = UV_ENOTDIR; + } // verify that the path pointed to is actually a directory. + if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) err = UV_EEXIST; uv_fs_req_cleanup(req); - int err = uv_fs_stat(loop, req, path.c_str(), - uv_fs_callback_t{[](uv_fs_t* req) { - FSReqBase* req_wrap = FSReqBase::from_req(req); - int err = req->result; - if (err == 0 && !S_ISDIR(req->statbuf.st_mode)) err = UV_EEXIST; - req_wrap->continuation_data->Done(err); - }}); - if (err < 0) req_wrap->continuation_data->Done(err); - } + req_wrap->continuation_data->Done(err); + }}); + if (err < 0) req_wrap->continuation_data->Done(err); break; } break; |