path_get refactor and implementation of missing path_ hostcalls on Windows#41
path_get refactor and implementation of missing path_ hostcalls on Windows#41kubkon merged 23 commits intoCraneStation:masterfrom kubkon:path_get_refactor
Conversation
|
OK, so I guess the PR is ready for review and I'd really welcome your feedback here guys. I'm actually kinda stumped with some of this stuff, and especially handling of symlinks on Windows for the reasons mentioned in the PR's description. I've actually gone ahead and created a thin wrapper type, Symlink, around Windows symlinks that essentially tries to imitate *nix symlinks so that self loops and dangling symlinks are possible (otherwise the tests are broken). I'm not sure this is the way we want to go though. In fact, I'd welcome any and all discussion here on the best way to proceed. Finally, if we decide to scrap this PR as-is, I'm OK with that as well. We should still be able to salvage the implementation of at least some of the |
|
@sunfishcode here's a thought on the WASI spec: do you think it would make sense to disallow creation of symlink loops and dangling symlinks in WASI? Additionally, would it also make sense to split This still doesn't address the problem of privileges on Windows: creating symlinks requires that the user has either the necessary privileges or the OS is in developer mode, but I guess it'd clean up the implementation a lot. Anyhow, I'd love to hear your thoughts on all of this! |
|
Without looking too much into the implementation, this overall looks pretty reasonable. I agree that |
|
@sunfishcode did you perhaps have a look at the PR? I'd like to merge it into master before #46, #47 and #54 so as to avoid rebase conflicts and to give @marmistrz ability to enable+add more tests. :-) |
sunfishcode
left a comment
There was a problem hiding this comment.
Looks good to me! path_get is some of the most complex code, and it's nice to have more of it factored out into common code.
src/hostcalls_impl/fs_helpers.rs
Outdated
| ) -> Result<(File, String)> { | ||
| const MAX_SYMLINK_EXPANSIONS: usize = 128; | ||
|
|
||
| if path.contains("\0") { |
There was a problem hiding this comment.
Can this be a character literal instead of a string literal?
There was a problem hiding this comment.
It should be indeed, thanks!
src/hostcalls_impl/fs.rs
Outdated
| let (old_dirfd, old_path) = path_get(old_dirfd, 0, old_path, false)?; | ||
| let (new_dirfd, new_path) = path_get(new_dirfd, 0, new_path, false)?; | ||
|
|
||
| hostcalls_impl::path_link(old_dirfd, new_dirfd, old_path, new_path) |
There was a problem hiding this comment.
This looks like it might be a little error prone, where one could forget to call path_get and in some cases it would still compile and seem to work. Would it make sense to create a struct to replace path_get's tuple return value? Then you could make the hostcalls_impl functions use that struct for their arguments, which would help ensure that path_get has been called. Does that make sense?
There was a problem hiding this comment.
Hmm, that sounds like a good idea. Let me have a play with it and I'll report back to this thread :-)
There was a problem hiding this comment.
OK, I've encapsulated the return value of path_get inside a PathGet struct. Have a look and let me know if this is what you've had in mind :-) Also, I can see several places we could refactor a lot of impl by extending this struct but I don't want to pile onto this PR too much. Instead, we can for sure refactor in smaller steps in subsequent PRs.
|
@sunfishcode if you're happy with the rest of PR as is, I guess I'll go ahead and squash and merge so that we can move forward with other PRs? |
sunfishcode
left a comment
There was a problem hiding this comment.
I'm not yet comfortable with the path_symlink implementation. I made a few comments below, but looking at the big picture, if symlink creation requires special privileges or a special OS mode, it suggests to me we should either:
- remove
path_symlinkfrom WASI, or - document that support for
path_symlinkmay be subject to limitations of the underyling filesystem, and maybe even add a way to query a directory to determine what form of symlinks may be created inside it
My suggestion would be to remove the SYMLINKS singleton here, since it's kind of misleading -- it helps emulate the desired functionality for the program that creates the symlinks, but they're not real and other processes wouldn't see them. I propose it's better to just have path_symlink fail in the cases that Windows doesn't support, and we'll take up the remaining questions at the WASI standards level. How does that sound?
| let target_path: &Path = target.as_ref(); | ||
|
|
||
| if source_path.exists() { | ||
| Err(host::__WASI_EEXIST) |
There was a problem hiding this comment.
Is it necessary to check if the source_path exists up front, or can we defer this and only check it in the non-file non-dir case?
| SymlinkKind::File | ||
| } else if target_path.is_dir() { | ||
| symlink_dir(target_path, source_path).map_err(errno_from_ioerror)?; | ||
| SymlinkKind::Dir |
There was a problem hiding this comment.
How crazy would it be to do this: instead of checking is_file and is_dir, just start by calling symlink_file and if that fails with the right error code, then try symlink_dir, and if that fails, check for the loop/dangling cases?
Yep, sounds good to me! As I said, it's been a roller-coaster ride for me trying to get it to work on Windows. The emulation also made me somewhat uneasy since emulating a dangling symlink would not get committed to the OS until it became solid, which, if the app died prematurely, etc., would potentially never happen, leaving other processes none-the-wiser. Anyhow, is the following plan of action aligned with what you've had in mind:
Those are just some loose thoughts but, in principle, I reckon I should be able to refactor the code to make it work. Also, do you want me to address the comments relating |
|
@sunfishcode OK, so here are the changes as per your suggestion:
Have a look and let me know what you reckon! There is an additional caveat here that I've already mentioned in this PR: Windows does make a distinction between file and directory symlinks. I've adapted the |
|
If |
Cargo.toml
Outdated
| rand = "0.6" | ||
| cfg-if = "0.1.9" | ||
| log = "0.4" | ||
| lazy_static = "1.3.0" |
There was a problem hiding this comment.
This dependency is no longer needed.
sunfishcode
left a comment
There was a problem hiding this comment.
Other than the comment above, this LGTM, so feel free to merge when ready.
|
I don't yet have an opinion about splitting |
Unfortunately, that's very true on Windows. Perhaps this will require some more thought. :-) |
This way, we can re-enable nofollow_errors testcase on Windows also.
This PR moves
path_gethelper out of thesyssubmodule and puts it in platform-independenthostcalls_impl::fs_helpersmodule.It requiresreadlinkatto be implemented which currently is missing on Windows, hence, this PR breaks WASI tutorial on Windows.It also strives at adding missing
path_hostcalls so that we can enable more tests on Windows.TODO:
readlinkatpath_create_directorypath_remove_directorypath_readlinkpath_symlinkpath_unlink_fileEDIT: some basics of the above syscalls are now drafted out. I'm still uneasy about symlink handling on Windows for a couple of reasons:
path_unlink_fileis rather messy as we need to check whether we are dealing with a file or dir symlink and invoke appropriately eitherstd::fs::remove_fileorstd::fs::remove_dirpath_openneeds more love ;-)My current idea for symlinks on Windows is to create a thin, virtual wrapper
Symlink(or similar) which will imitate *nix's functionality thus allowing for symlink loops and dangling symlinks until the symlink is actually made solid (by pointing to an actual resource). Then, theSymlinkshould know whether it points to a file or a dir thus making the cleanup simpler.