Refactored docs for std::fs::set_permissions_nofollow + fix BSD-based systems to use fchmodat with AT_SYMLINK_NOFOLLOW flag - #160170
Conversation
| /// * `open` with `O_NOFOLLOW` flag enabled + `fchmod` on WASI. | ||
| /// * `fchmodat` function with the flag `AT_SYMLINK_NOFOLLOW` enabled | ||
| /// on Unix platforms | ||
| /// on Unix platforms. |
There was a problem hiding this comment.
These aren't super necessary but 🤷🏻 doesn't matter too much
There was a problem hiding this comment.
Yeah this is grammatically still odd -- the first two bullets are not actually sentences, but the last one is.
|
@bors r+ rollup |
| /// * `fchmodat` function with the flag `AT_SYMLINK_NOFOLLOW` enabled | ||
| /// on Unix platforms | ||
| /// on Unix platforms. | ||
| /// * The flag `FILE_FLAG_OPEN_REPARSE_POINT` is enabled and then the |
There was a problem hiding this comment.
That flag is enabled where? Should this say "SetFileInformationByHandle with the flag ..." or would that not be right?
There was a problem hiding this comment.
The internal code for Windows' set_permissions_nofollow does the following:
pub fn set_perm_nofollow(p: &WCStr, perm: FilePermissions) -> io::Result<()> {
let mut opts = OpenOptions::new();
opts.access_mode(c::FILE_WRITE_ATTRIBUTES);
// `FILE_FLAG_OPEN_REPARSE_POINT` for no_follow behavior
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);
let file = File::open_native(p, &opts)?;
file.set_permissions(perm)
}It opens the file first with FILE_FLAG_BACKUP_SEMANTICS and FILE_FLAG_OPEN_REPARSE_POINT and then it sets the permissions on the reparse point itself using SetFileInformationByHandle. Enabled is probably a poor choice of word here; at the time, I wasn't too sure how to discuss the opening a file behavior on Windows because it seems like OpenOptions::open doesn't go in depth about platform-specific behavior and the open_native call seem to be doing a lot here:
rust/library/std/src/sys/fs/windows.rs
Lines 345 to 398 in b5be620
There was a problem hiding this comment.
I would say something like
/// This function currently corresponds to the following underlying operations:
/// * WASI: `open` with `O_NOFOLLOW` followed by `fchmod`.
/// * Unix: `fchmodat` with `AT_SYMLINK_NOFOLLOW`.
/// * Windows: `CreateFileW` with `FILE_FLAG_OPEN_REPARSE_POINT` followed
/// by `SetFileInformationByHandle`.
There was a problem hiding this comment.
I took a look at Unix implementation of set_permissions_nofollow again, and I wanted to correct something I said about MacOS/BSD-based systems. Currently, they call fchmodat with no flag provided (0), and I realized that's a mistake I made since I forgot to include them underneath the fchmodat with AT_SYMLINK_NOFOLLOW.
rust/library/std/src/sys/fs/unix.rs
Lines 2038 to 2070 in b5be620
I think the reason why I did that was because CI mentioned compiler errors on libc::AT_SYMLINK_NOFOLLOW not existing on certain platforms (though this was on dist-various-1 so wasn't sure what that tested), so I gated it to linux, but forgot to update it to include MacOS/other BSD-based systems that support AT_SYMLINK_NOFOLLOW.
I have to fix this and include the BSD-based systems in that block. I also verified the behavior on my MacBook and it should definitely change the permission bits, which means the test case that sets a symlink with readonly permission should pass.
|
@bors r- Sorry I have a question :) |
|
This pull request was unapproved. |
|
r? RalfJung since you're mostly reviewing already |
|
|
d24bee2 to
82ff1cb
Compare
std::fs::set_permissions_nofollow + fix BSD-based systems to use fchmodat with AT_SYMLINK_NOFOLLOW flag
82ff1cb to
f0403c2
Compare
| @@ -662,13 +662,10 @@ fn set_get_permissions_nofollows_symlink() { | |||
There was a problem hiding this comment.
Android and Linux share the same kernel, so I highly doubt they will behave different here?
There was a problem hiding this comment.
I'll take it off from this block and we can put up a try job to confirm
| // on symlinks could lead to no effect, so we should expect | ||
| // there being no change to BSD-based systems. | ||
| // on symlinks could lead to no effect, so it's case by case | ||
| // on whether the permissions are set |
There was a problem hiding this comment.
// On these systems, the symlink itself is now marked readonly.
This is meant to replace the entire comment, github is just incapable of making that a suggestion...
| options.open(Path::new(os_str))?.set_permissions(Permissions::from_inner(perm)) | ||
| } | ||
| all(target_os = "linux", not(any(target_os = "espidf", target_os = "horizon"))) => { | ||
| all(any(target_os = "linux", target_os = "macos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonfly", target_os = "android"), not(any(target_os = "espidf", target_os = "horizon"))) => { |
There was a problem hiding this comment.
What is going on here? The not part at the end makes no sense at all...?
| // These platforms do not have `AT_SYMLINK_NOFOLLOW` but support fchmodat, | ||
| // so no flag is set for fchmodat. | ||
| cvt_r(|| unsafe { | ||
| libc::fchmodat(libc::AT_FDCWD, p.as_ptr(), perm.mode, 0) | ||
| }) |
There was a problem hiding this comment.
We promise that we will not follow the symlink. So why is it correct to not set the flag?
There was a problem hiding this comment.
To be honest with you, I wasn't sure what Unix platforms didn't support AT_SYMLINK_NOFOLLOW/O_NOFOLLOW because they didn't support symlinks.
For example, I think in my previous PR someone was commenting that ESPIDF/Horizon OS do not have support O_NOFOLLOW or symlinks, so I figured that their behavior should match with just regularly setting permissions on the file (fchmodat with no flag set). I had the not condition from earlier because I wasn't exhaustively sure which Unix-based platform has no symlinks.
I could try separating the not portion of the second branch into its own branch using fchmodat with no flag set and then have the _ branch do the same thing that lolbinarycat did originally:
use crate::fs::OpenOptions;
use crate::os::unix::fs::OpenOptionsExt;
OpenOptions::new().custom_flags(libc::O_NOFOLLOW).open(path)?.set_permissions(perm)^This does return a different error message than Unsupported on symlinks though.
There was a problem hiding this comment.
I think in my previous PR someone was commenting that ESPIDF/Horizon OS do not have support O_NOFOLLOW or symlinks, so I figured that their behavior should match with just regularly setting permissions on the file (fchmodat with no flag set)
If we are sure this is correct (ping the target maintainers) then we can skip the flag on those targets.
But your code skips the flag on all unknown targets and that doesn't seem good.
OpenOptions::new().custom_flags(libc::O_NOFOLLOW).open(path)?.set_permissions(perm) seems like a good fallback impl.
There was a problem hiding this comment.
The maintainer of the ESP-IDF target here: yes, I confirm ESP-IDF (and likely Horizon) do not support neither fchmodat, nor AT_SYMLINK_NOFOLLOW, nor O_NOFOLLOW so they should fall back to the old code before #158168. The latest code surface of this PR seems to fix that.
There was a problem hiding this comment.
To be clear, this only makes sense because they don't support symlinks at all. We have to guarantee that these operations never follow symlinks.
| /// // or succeed in modifying the permissions of a symlink | ||
| /// // This should result in an error on certain platforms, | ||
| /// // succeed in modifying the permissions of a symlink, | ||
| /// // or do nothing at all. |
There was a problem hiding this comment.
Do we know any platform where it does nothing at all?
There was a problem hiding this comment.
I don't have an exhaustive list. All I read from this MacOS post is that it's possible some systems can't change symbolic link permissions at all, which I interpreted to mean two different things: that fchmodat has no effects on symlinks or it throws an error. I did see this issue here that mentions that certain kernel silently ignored AT_SYMLINK_NOFOLLOW, which change permissions on the target, but that does something instead of no effect and it's an incorrect implementation.
Should I assume that AT_SYMLINK_NOFOLLOW should succeed in setting the permission bits/throw an error if it's not supported on symlinks? I'm unsure if no effects on symlinks and not throwing an error is valid behavior.
There was a problem hiding this comment.
Actually, I'll remove the no effects behavior note. I don't think of it being a valid behavior for this function, and since I can't find any results that tell me what platforms does nothing for fchmodat with AT_SYMLINK_NOFOLLOW
|
The user-facing comments are fine but this is turning into a libs discussion about the implementation. r? libs |
|
💔 Test for 01bf5b1 failed: CI. Failed job:
|
This comment has been minimized.
This comment has been minimized.
|
@bors try jobs=x86_64-msvc-1,dist-various-*,test-various,dist-android (Unsure why aarch64-apple is not working, it worked in the previous PR I made on |
This comment has been minimized.
This comment has been minimized.
Refactored docs for `std::fs::set_permissions_nofollow` + fix BSD-based systems to use fchmodat with AT_SYMLINK_NOFOLLOW flag try-job: x86_64-msvc-1 try-job: dist-various-* try-job: test-various try-job: dist-android
|
I generally just check the latest commit on |
| /// * Linux, BSD-based platforms, Android, QNX: `fchmodat` with `AT_SYMLINK_NOFOLLOW` | ||
| /// with a fallback behavior to use `open` with `O_NOFOLLOW` followed by behavior | ||
| /// denoted in [`fs::set_permissions`] when the former `fchmodat` call errors with `ENOTSUP`[^1]. | ||
| /// * Other Unix-based platforms with symlinks: `open` with `O_NOFOLLOW` followed by behavior | ||
| /// denoted in [`fs::set_permissions`]. | ||
| /// * Other Unix-based platforms without symlinks: `open` followed by behavior | ||
| /// denoted in [`fs::set_permissions`]. | ||
| /// * Windows: `CreateFileW` with `FILE_FLAG_OPEN_REPARSE_POINT` followed | ||
| /// by `SetFileInformationByHandle`. |
There was a problem hiding this comment.
| /// * Linux, BSD-based platforms, Android, QNX: `fchmodat` with `AT_SYMLINK_NOFOLLOW` | |
| /// with a fallback behavior to use `open` with `O_NOFOLLOW` followed by behavior | |
| /// denoted in [`fs::set_permissions`] when the former `fchmodat` call errors with `ENOTSUP`[^1]. | |
| /// * Other Unix-based platforms with symlinks: `open` with `O_NOFOLLOW` followed by behavior | |
| /// denoted in [`fs::set_permissions`]. | |
| /// * Other Unix-based platforms without symlinks: `open` followed by behavior | |
| /// denoted in [`fs::set_permissions`]. | |
| /// * Windows: `CreateFileW` with `FILE_FLAG_OPEN_REPARSE_POINT` followed | |
| /// by `SetFileInformationByHandle`. | |
| /// * Linux, BSD-based platforms, Android, QNX: `fchmodat` with `AT_SYMLINK_NOFOLLOW`. If that is not supported, we fall back to | |
| /// `open` with `O_NOFOLLOW` followed by [`fs::set_permissions`]. | |
| /// * Other Unix-based platforms with symlinks: `open` with `O_NOFOLLOW` followed by [`fs::set_permissions`]. | |
| /// * Other Unix-based platforms without symlinks: `open` followed by [`fs::set_permissions`]. | |
| /// * Windows: `CreateFileW` with `FILE_FLAG_OPEN_REPARSE_POINT` followed | |
| /// by `SetFileInformationByHandle`. |
I think the man page just says "if you see that error code, it means the flag is not supported". |
I agree, that's the only reading that's consistent with the actual behaviour |
Following up on this, because I realized I wasn't very clear: The code currently in the main branch, and this patch, both fail on QNX. The tests treat it as a platform where I've written a fix and tested that it works on our CI here: ferrocene/ferrocene@a450413 . I don't love how long the list of platforms in that @asder8215 Please can you cherry-pick that commit into this PR? (minus the "Test" label because I've confirmed that it works now) |
a8e82f2 to
f4ed7b6
Compare
This comment has been minimized.
This comment has been minimized.
Folks, is there a reasonable chance to get this approved and merged - if not in the next days then till end of month? ^^^ is a bit discouraging. The problem is, the already-merged precursor to this PR broke the ESP-IDF Tier 3 target in a way where STD does not even compile anymore for that target - which this PR addresses. And it's been broken since a month... given that the target is Tier 3, having a usable nightly (because Thanks for the understanding and sorry if I pushed a bit too much. I get it it is good to get this right after the first PR failed, but maybe we should've rolled back the first PR in the meantime. Which is also an option if this takes too long I guess? EDIT: #158168 is the problematic one. |
… on different platforms + fixed BSD-based systems to use fchmodat with AT_SYMLINK_NOFOLLOW
…tored non-BSD-based/non-Linux platforms to use OpenOptions open + set_permissions, and refactored tests accordingly
…ns ENOTSUP (e.g. for Ubuntu 20.04 returns ENOTSUP on non-symlinks + symlinks when using fchmodat with AT_SYMLINK_NOFOLLOW). Update docs accordingly as well and corrected behavior + docs for other Unix platforms with symlinks should return `FilesystemLoop` error instead of `InvalidInput` due to not setting `OpenOptions` with read enabled. Co-authored-by: Rachel Barker <rachel.barker@ferrous-systems.com>
…d fchmodat platform call on fchmodat and every platform falls back to open + fchmod when _res is set to ErrorKind::Unsupported; updated docs to reflect change
f4ed7b6 to
61a29b7
Compare
|
This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
|
I'm with @ivmarkov that something needs to be done about the mistake I made in #158168. I'm concerned about I think either the commit b319483 should be reverted/removed or that this PR should be approved soon. I'm unfamiliar with how libs team handle reversion on nightly features though (is it safe to just pop a commit out of history, and then just push that?). As for this PR, I think all it needs, if no other feedback or review is given, is a try job run on Apple (rebased code should not have changed from the last try job other than tidy formatting). I'll give a try job on Apple after CI turns green. |
|
The feature not working properly is not great but, well, it's a nightly feature so 🤷 . The standard library not building on a tier 3 target is a problem. It sometimes happens but it shouldn't take a month to fix. If you submit a minimal PR that fixes just that we can get that approved fairly quickly I think. |
Thanks. |
|
@bors try jobs=aarch64-apple-* |
This comment has been minimized.
This comment has been minimized.
Refactored docs for `std::fs::set_permissions_nofollow` + fix BSD-based systems to use fchmodat with AT_SYMLINK_NOFOLLOW flag try-job: aarch64-apple-*
View all comments
This PR refactors documentations for
std::fs::set_permissions_nofollowand fixes BSD-based systems + Android to usefchmodatwithAT_SYMLINK_NOFOLLOWflag (instead of no flag set) and refactors all other platforms to defer toOpenOptionswithO_NOFOLLOWbehavior.r? @clarfonthey
Since they looked at the original
set_permissions_nofollowPR I madecc @RalfJung