Skip to content
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

Implement path_link for Windows. #1199

Merged
merged 5 commits into from
Apr 3, 2020

Conversation

marmistrz
Copy link
Contributor

@marmistrz marmistrz commented Mar 1, 2020

This is probably the last missing syscall for Windows!

This PR implements path_link for Windows and adds a non-strict version of the path_link integration test.

I'm unsure about the error handling in path_link. MSDN doesn't say much about possible error codes for either CreateHardLinkA or CreateSymbolicLinkA. I mostly copied over the error conversion from path_symlink, but I'm not sure if it's correct. In particular, it's unclear to me what the purpose of strip_trailing_slashes_and_concatenate is.

path_symlink will now also detect an attempt to create a dangling symlink and return ENOTSUP. (is this the correct return code)?

Currently the non-strictness of the test consists of:

  1. we use a separate subdirectories subdir, subdir2, subdir3 for each test stage. This is due to the fact Windows will not remove the directory and won't allow to create a directory with the same name until the previous one has been deleted. I don't see any way of circumventing it, because the application may still try to access the directory through the unclosed file descriptor.
  2. path_link will return EACCES instead of EPERM when trying to create a link to a subdirectory. This violates the POSIX spec. We could manually check if the source path is a directory in case of ERROR_ACCESS_DENIED but this would cost us an extra syscall.
  3. Tests for dangling symlinks or symlink loops have been disabled. Alternatively, we could check if the attempt to create a dangling symlink returns ENOTSUP, but this doesn't make much sense while 1&2 are an issue.

Let me know what you think.

Btw. @kubkon, according to this stackoverflow post Mac OS X 10.5+ permits hard links to directories, which our tests expect to fail.


Notes about links and symlinks under Windows:

  • creating a symlink requires administrative privileges (SeCreateSymbolicLinkPrivilege). On Windows 10 this requirement may be removed, but this requires enabling developer mode
  • Windows distinguishes between file and directory symlinks
  • It's possible to create a dangling symlink, but the type (file/directory) has to be specified upon creation. The behavior in case of type mismatch is inconsistent. Precisely, suppose that a dangling file symlink is created foo -> bar and later, a directory bar is created. Then:
    • under msys64 bash, cd foo succeeds and the directory view is the same when access either directly or through the symlink
    • under cmd (both windowed and as a child process from msys64 bash). cd foo fails with The directory name is invalid

@marmistrz marmistrz added wasi:impl Issues pertaining to WASI implementation in Wasmtime wasi:tests Issues pertaining to WASI tests in Wasmtime labels Mar 1, 2020
@marmistrz
Copy link
Contributor Author

marmistrz commented Mar 2, 2020

The test failure seems unrelated to this change. (some cranelift/codegen issues)

@peterhuene
Copy link
Member

It's hard to see because we output so much junk with --nocapture, but the new test does appear to be failing in CI:

thread 'main' panicked at 'removing a subdirectory: Error { code: 55, message: "Directory not empty." }', src/libcore/result.rs:1188:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
test wasi_tests::path_link_nonstrict ... FAILED

@marmistrz marmistrz force-pushed the path_link branch 3 times, most recently from 5a7b9c6 to 94a29ca Compare March 4, 2020 17:12
@marmistrz
Copy link
Contributor Author

You're right. Unfortunately, I can't reproduce the error locally, the tests always succeed. I'm running the tests on Windows 10, they succeed both when run through msys64 ssh and windowed cmd (as administrator). I'm using cargo test --features test_programs --package test-programs but the full CI command succeeds too.

Could you try and see if you can reproduce it?

@peterhuene
Copy link
Member

I'll see if I can reproduce it.

@peterhuene
Copy link
Member

I'm unable to reproduce the failure locally as well. I tried rerunning the GitHub actions jobs, but that apparently is broken (seems to checkout the wasmtime repo and then try to checkout your HEAD, but it obviously isn't in the history 🤷‍♂).

The expect message of removing a subdirectory only appears in path_link which should still be ignored, so I'm not sure how we saw that in the log.

To restart CI, let's rebase this branch off of master and push it up.

@marmistrz
Copy link
Contributor Author

marmistrz commented Mar 4, 2020

I rebased upon latest master. The message is coming from path_link_nonstrict.

@kubkon
Copy link
Member

kubkon commented Mar 4, 2020

Oh, strip_trailing_slashes_and_concatenate is there to correctly account for the case where old_path is a file, and new_path contains a trailing slash. On POSIX (and in WASI), as far as I remember, if new_path exists on the host but without the trailing slash, we expect in this case an EEXIST error code. On Windows, this is not respected in the same way, hence why the additional check.

@kubkon
Copy link
Member

kubkon commented Mar 4, 2020

As far as mapping of error codes is concerned, yeah, MSDN is not very instructive. When I was figuring out path_symlink and path_rename, it was mostly the case of trial and error unfortunately.

@kubkon
Copy link
Member

kubkon commented Mar 4, 2020

Also, thanks for the link! I'll need to investigate MacOS a bit further I think!

Comment on lines 549 to 552
} else if !old_path.exists() {
// attempt to create a dangling symlink
warn!("Dangling symlink or symlink loops unsupported on Windows");
Err(Error::ENOTSUP)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a good choice of errno value here, but I'd like @sunfishcode to double check that though.

@marmistrz marmistrz force-pushed the path_link branch 5 times, most recently from 22a3a24 to 43195ad Compare March 6, 2020 10:28
@marmistrz
Copy link
Contributor Author

I tried to integrate those two tests into one and it appears that creating dangling symlinks doesn't fail when it should. I'll need to see why.

The previous version of the branch is available here: https://github.com/marmistrz/wasmtime/tree/path_link_old

@github-actions github-actions bot added the wasi Issues pertaining to WASI label Mar 11, 2020
@github-actions
Copy link

Subscribe to Label Action

This issue or pull request has been labeled: "wasi", "wasi:impl", "wasi:tests"

Users Subscribed to "wasi"

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

@marmistrz marmistrz force-pushed the path_link branch 3 times, most recently from d05a9f0 to adf86e4 Compare March 25, 2020 15:23
@marmistrz
Copy link
Contributor Author

Good news! I have managed to isolate the cause of the mysterious CI failures.

It appears that after calling fd_fdstat_get on a file the file will not properly be removed (internally, path_unlink_file uses fs::remove_file). This would manifest itself in two ways: (1) path_remove_directory would fail due to the directory (2) if we skipped that part of the test, an attempt to create another file with the same name would fail due, due to permission having been denied

Support for fd_fdstat_get on Windows was introduced in #557. @peterhuene, since the call was implemented by you, could you take this over from me at this point? I also suggest we disable the checks using fd_fdstat_get until it's figured out.

The change is mostly ready. I'm still not sure if the ERROR_ACCESS_DENIED conversion is still needed, but this is basically discussed in #1359. Should it be the only blocker, I'm fine with removing it for now and possibly adding it back if we decide it's needed in #1359.

@marmistrz marmistrz requested review from peterhuene and kubkon March 25, 2020 15:29
@peterhuene
Copy link
Member

I don't particularly see how the problem lies with fd_fdstat_get. I double checked and it's not inadvertently leaking any handles.

I do see how the test was missing calls to fd_close before the attempts to unlink the files and directories, which is required on Windows.

If we uncomment the fd_fdstat_get calls with the rest of the changes, the test will still fail? I can't explain that, but I'll look into it.

@marmistrz
Copy link
Contributor Author

marmistrz commented Mar 25, 2020

If we uncomment the following lines: https://github.com/bytecodealliance/wasmtime/pull/1199/files#diff-6242b29603dfbc39ebbd9f3e58dde615R98-R102
the tests will fail in the CI (but not locally). I have absolutely no idea why this could happen, I've checked the source too.

@kubkon
Copy link
Member

kubkon commented Apr 1, 2020

@peterhuene @marmistrz did you manage to work out what the problem is? If not, what do you think the next steps should be both wrt the problem and this PR?

@peterhuene
Copy link
Member

I haven't yet determined the problem and I have no educated guesses since the commented out lines should have absolutely no impact on handle reference counts that might keep things from being deleted properly.

@marmistrz when you get a chance, can you uncomment out the problematic lines and push it up for CI to fail again? I'd like to investigate the log just a little more.

@marmistrz
Copy link
Contributor Author

I thought about merging this PR as-is and letting @peterhuene figure out what's wrong with fdstat in a subsequent PR. But for now, I'll add a comment uncommenting the problematic lines.

@peterhuene
Copy link
Member

So it looks like it is passing Windows CI with the uncommented lines. Perhaps the other fix to properly close the handles before unlinking was what actually resolved the issue?

I think we can leave it uncommented and remove the preceding comments about the lines being a problem. I'll go ahead with a review.

Copy link
Member

@peterhuene peterhuene left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! Just some minor feedback before we merge.

crates/test-programs/wasi-tests/src/bin/path_link.rs Outdated Show resolved Hide resolved
crates/test-programs/wasi-tests/src/bin/path_link.rs Outdated Show resolved Hide resolved
crates/test-programs/wasi-tests/src/bin/path_link.rs Outdated Show resolved Hide resolved
Some(code) => {
debug!("path_link at fs::hard_link error code={:?}", code);
match code as u32 {
// TODO is this needed at all????
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this TODO comment actionable or can it be removed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm removing this mapping, because there is no clear reason why it's needed, just as in #1359.

@marmistrz
Copy link
Contributor Author

So it looks like it is passing Windows CI with the uncommented lines. Perhaps the other fix to properly close the handles before unlinking was what actually resolved the issue?

Absolutely no idea. Perhaps some cleanup I did after removing the fdstats interfered. Anyway, I'm happy that this mysterious failure is gone. I will push out the review changes in several steps.

I'm still unsure about point 2 in non-strictness:

2\. `path_link` will return `EACCES` instead of `EPERM` when trying to create a link to a subdirectory. This violates the POSIX spec. We could manually check if the source path is a directory in case of `ERROR_ACCESS_DENIED` but this would cost us an extra syscall.

Should I correct this in the error mappings or do we prefer to avoid the extra syscall?

@kubkon
Copy link
Member

kubkon commented Apr 2, 2020

So it looks like it is passing Windows CI with the uncommented lines. Perhaps the other fix to properly close the handles before unlinking was what actually resolved the issue?

Absolutely no idea. Perhaps some cleanup I did after removing the fdstats interfered. Anyway, I'm happy that this mysterious failure is gone. I will push out the review changes in several steps.

I'm still unsure about point 2 in non-strictness:

2\. `path_link` will return `EACCES` instead of `EPERM` when trying to create a link to a subdirectory. This violates the POSIX spec. We could manually check if the source path is a directory in case of `ERROR_ACCESS_DENIED` but this would cost us an extra syscall.

Should I correct this in the error mappings or do we prefer to avoid the extra syscall?

I think we should be fine either way. We should be fine with an additional syscall since we already do that in path::symlink anyway.

@marmistrz
Copy link
Contributor Author

So it looks like it is passing Windows CI with the uncommented lines. Perhaps the other fix to properly close the handles before unlinking was what actually resolved the issue?

Absolutely no idea. Perhaps some cleanup I did after removing the fdstats interfered. Anyway, I'm happy that this mysterious failure is gone. I will push out the review changes in several steps.
I'm still unsure about point 2 in non-strictness:

2\. `path_link` will return `EACCES` instead of `EPERM` when trying to create a link to a subdirectory. This violates the POSIX spec. We could manually check if the source path is a directory in case of `ERROR_ACCESS_DENIED` but this would cost us an extra syscall.

Should I correct this in the error mappings or do we prefer to avoid the extra syscall?

I think we should be fine either way. We should be fine with an additional syscall since we already do that in path::symlink anyway.

@peterhuene and what do you think?

@peterhuene
Copy link
Member

I'm 👍 on what you now have.

@marmistrz
Copy link
Contributor Author

Awesome! Then, unless anything comes up, I'm merging the PR tomorrow evening CET.

@marmistrz marmistrz merged commit f6e3ab0 into bytecodealliance:master Apr 3, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wasi:impl Issues pertaining to WASI implementation in Wasmtime wasi:tests Issues pertaining to WASI tests in Wasmtime wasi Issues pertaining to WASI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants