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

unistd: adding linux(glibc)/freebsd close_range. #2525

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

devnexen
Copy link
Contributor

@devnexen devnexen commented Oct 22, 2024

Allows to close a range of file descriptors, can set close-on-exec on these and/or unsharing (as having its own file descriptors table
after fork for the calling process).

What does this PR do

Checklist:

  • I have read CONTRIBUTING.md
  • I have written necessary tests and rustdoc comments
  • A change log has been added if this PR modifies nix's API

@SteveLauC
Copy link
Member

I restarted the Linux/glibc CI as the error is not related to the PR

@devnexen devnexen marked this pull request as ready for review October 23, 2024 05:13
@devnexen devnexen marked this pull request as draft October 23, 2024 05:39
@devnexen
Copy link
Contributor Author

FreeBSD issue will be fixed once this PR is merged and released.

Allows to close a range of file descriptors, can set close-on-exec
on these and/or unsharing (as having its own file descriptors table
 after fork for the calling process).
@devnexen devnexen marked this pull request as ready for review November 10, 2024 17:39
Copy link
Member

@SteveLauC SteveLauC left a comment

Choose a reason for hiding this comment

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

Hi, I have left some comments.

About this interface, there was an attempt to add it to Nix, #2153, based on the discussion there, this function should be marked unsafe given that it is easy to incur a double close. And, I think it should take raw file descriptors, a common use case is to close all the file descriptors: close_range(0, u32::MAX, 0), using I/O-safe types makes the interface hard to use in this case. Your thoughts on this?

src/unistd.rs Outdated
#[cfg(all(target_os = "linux", target_env = "gnu"))]
/// Unshare the file descriptors range before closing them
CLOSE_RANGE_UNSHARE;
/// Set the close-on-exec flag on the file descriptors range
Copy link
Member

Choose a reason for hiding this comment

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

Let's be explicit:

Suggested change
/// Set the close-on-exec flag on the file descriptors range
/// Set the close-on-exec flag on the file descriptors range instead of closing them.

src/unistd.rs Outdated
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
pub struct CloseRangeFlags : c_uint {
#[cfg(all(target_os = "linux", target_env = "gnu"))]
/// Unshare the file descriptors range before closing them
Copy link
Member

Choose a reason for hiding this comment

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

Considering that the doc in the man page is kinda misleading, let's fix it on our side:

Suggested change
/// Unshare the file descriptors range before closing them
/// Unshare the file descriptor table, then close the file descriptors specified in the range.

src/unistd.rs Outdated
libc_bitflags! {
/// Options for close_range()
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
pub struct CloseRangeFlags : c_uint {
Copy link
Member

Choose a reason for hiding this comment

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

This is interesting, libc defines these 2 constants as unsigned integers. 🤔

src/unistd.rs Outdated

cfg_if! {
if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
libc::syscall(libc::SYS_close_range, fdbegin.as_fd().as_raw_fd() as u32, fdlast.as_fd().as_raw_fd() as u32, flags.bits() as i32)
Copy link
Member

Choose a reason for hiding this comment

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

glibc added the wrapper in 2.34, and the libc crate has it exposed, any reason why we are using a raw syscall here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Originally I call the wrapper but I think it is due to the CI when there is old glibc

Copy link
Member

Choose a reason for hiding this comment

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

Makes sense, some CIs are still using Ubuntu 20.04 due to this issue, which has glibc 2.31 😮‍💨, perhaps we can try bumping them to see if we are lucky now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we can do that if we do not need to bother supporting older releases out in the field. I may come back to it later.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, if we use glibc wrapper, then using this interface would require glibc 2.34 or above. We are still not lucky regarding that test: #2533, perhaps we should disable the test under QEMU then 😪

src/unistd.rs Outdated
all(target_os = "linux", target_env = "gnu"),
target_os = "freebsd"
))]
pub fn close_range<F: std::os::fd::AsFd>(fdbegin: F, fdlast: F, flags: CloseRangeFlags) -> Result<Option<c_int>> {
Copy link
Member

Choose a reason for hiding this comment

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

I am curious why it returns a Result<Option<c_int>>, and the reason behind the code related to Errno. From the man page, it returns -1 on error, and 0 on success, looks like it is just a normal syscall👀

Copy link
Member

@asomers asomers left a comment

Choose a reason for hiding this comment

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

I agree with @SteveLauC on all points. Additionally, the "fix tests" commit appears to be unrelated to this PR. If it is indeed unrelated, then please open a separate PR for it, and use a more descriptive commit message.

tempfile3.write_all(CONTENTS).unwrap();
tempfile2.write_all(CONTENTS).unwrap();
tempfile1.write_all(CONTENTS).unwrap();
let areclosed = close_range(tempfile1, tempfile3, CloseRangeFlags::CLOSE_RANGE_CLOEXEC);
Copy link
Member

Choose a reason for hiding this comment

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

In a multithreaded program, which this is, there is no guarantee that tempfile3's file descriptor will be higher than tempfile1's.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants