-
-
Notifications
You must be signed in to change notification settings - Fork 33.8k
fs: fix infinite loop with async recursive mkdir on Windows #27207
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
a074e58 to
0fc9534
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
src/node_file.cc
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Would it be better to move UV_EEXIST to a case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd still need to duplicate the logic for when continuation_data.paths.size() == 0 (which is the same logic as the rest of the default case).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that we now have logic that checks whether or not a path is a file before calling MKDirpAsync, do we need to check continuation_data.paths.size() > 0; seems like a reasonable safety to have in place, but wonder if you could think of a situation where we'd hit this edge-case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We haven't done the check the very first time MKDirpAsync is called from MKDir. So if uv_fs_mkdir() fails on the very first iteration (continuation_data.paths.size() == 0) we want to return UV_EEXIST.
bcoe
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it might be wroth breaking the logic that performs the uv_fs_stat into its own helper method.
Kind of stinks that we need to perform one more system level call to shim the difference between Windows and Linux, is this something that could be eventually addressed in libuv?
test/parallel/test-fs-mkdir.js
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tests look great 👍
src/node_file.cc
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is getting chunky enough, I wonder if we could break it out into a helper method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactored a bit to make the code more compact. PTAL.
src/node_file.cc
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that we now have logic that checks whether or not a path is a file before calling MKDirpAsync, do we need to check continuation_data.paths.size() > 0; seems like a reasonable safety to have in place, but wonder if you could think of a situation where we'd hit this edge-case.
|
@richardlau great work, thanks for getting to this patch so fast. |
3c236d1 to
3066011
Compare
I've refactored a bit so the code is much more compact now. PTAL.
Sounds unlikely based on #18014 (comment) but cc @nodejs/libuv anyway. For this PR the Windows call that Lines 851 to 855 in f698386
Both _wmkdir (Windows) and mkdir (Posix) return EEXIST if the path already exists. The difference is how it treats earlier parts of the path that don't exist -- Posix will return ENOTDIR if an earlier part of the path is not a directory but Windows does not and the only way to shim that would be to walk up the path (which we're already doing in the recursive MKDir implementation).
|
|
Tests are LGTM |
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: nodejs#27207 Fixes: nodejs#27198 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Ben Coe <[email protected]>
Replace try-catch blocks in tests with `assert.rejects()` and `assert.throws()`. PR-URL: nodejs#27207 Fixes: nodejs#27198 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Ben Coe <[email protected]>
bdfe5ce to
c6c37e9
Compare
|
Landed in 96e46d3...c6c37e9. |
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: #27207 Fixes: #27198 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Ben Coe <[email protected]>
Replace try-catch blocks in tests with `assert.rejects()` and `assert.throws()`. PR-URL: #27207 Fixes: #27198 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Ben Coe <[email protected]>
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: #27207 Fixes: #27198 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Ben Coe <[email protected]>
Replace try-catch blocks in tests with `assert.rejects()` and `assert.throws()`. PR-URL: #27207 Fixes: #27198 Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Sakthipriyan Vairamani <[email protected]> Reviewed-By: Ben Coe <[email protected]>
If
fileis a file then on Windowsmkdironfile/areturns anENOENTerror while on POSIX the equivalent returnsENOTDIR. On thePOSIX systems
ENOTDIRwould break out of the loop but on Windows theENOENTwould strip off theaand attempt to makefileas adirectory. This would return
EEXISTbut the code wasn't detectingthat the existing path was a file and attempted to make
file/aagain.Fixes: #27198
cc @nodejs/fs @bcoe
Checklist
make -j4 test(UNIX), orvcbuild test(Windows) passes