Description
The kernel API for openat2
is designed to be extensible but the API binding provided by rustix is done in a way that would result in API breakage if a new field was added to openat2
in the future. Ideally, the API would look something more like (idk if AsRef<Openat2How>
or Into<Openat2How>
is more preferable):
#[non_exhaustive]
#[derive(Clone, Debug, Default)]
pub struct Openat2How {
// NOTE: This is actually a u64 but OFlags is i32. There have been
// discussions about making openat2-only flags before so maybe this should
// be O2Flags or something.
pub flags: OFlags,
pub mode: Mode,
pub resolve: ResolveFlags,
}
pub fn openat2<Fd: AsFd, P: Arg>(dirfd: Fd, path: P, how: &Openat2How) -> Result<OwnedFd>
Sadly, the most ergonomic way of instantiating Openat2How
wouldn't work:
let how = Openat2How {
flags: ...,
resolve: ...,
..Default::default()
};
because #[non_exhaustive]
blocks that too. But they could at least do:
let mut how = Openat2How::default();
how.flags = ...;
how.resolve = ...;
And then rustix
would use std::mem::size_of::<Openat2How>()
as the size argument to the syscall. This would allow for future extensions to be added to Openat2How
transparently without causing breakages for Rust users -- allowing us to provide the same backward-compatibility that C users of openat2
get.
Because of this limitation, I can't switch my last syscall wrapper in libpathrs from raw libc
calls to rustix
because I sometimes test extensions in my Rust code and the current API doesn't let you express extensions.
Since changing this would be a breakage and would require a new minor bump for rustix, I'm opening an issue before sending a PR for this. If this API would a bit too ugly to use for most people, then maybe we could make it openat2_raw
or something?
Activity
sunfishcode commentedon Oct 17, 2024
Rustix tends to avoid "just pass the bits through" interfaces, because one of rustix's big goals is safe APIs.
openat2
in rustix is currently declared as a safe function. If it were extensible with arbitrary fields, and those field ever contained raw pointers or raw file descriptors, then it would become unsound. I don't know how likely it is that the Linux developers would ever add pointers or file descriptors toopen_how
, but they don't guarantee that they won't, and it doesn't seem implausible.sunfishcode commentedon Oct 18, 2024
Oh, rereading this, I think I may have misunderstood your request here. Perhaps you're not asking to expose the raw Linux
open_how
bits; perhaps you're asking for rustix to have something like its ownOpenHow
type which is#[non_exhautive]
so that rustix can add new features over time without API breaks?I looked a little at libpathrs and don't see a place where
openat2
is exposed in the public API, so I'm not quite clear on how this would work in practice. Would you mind going into a little more detail of how you'd want to use this?cyphar commentedon Oct 20, 2024
We don't expose it in libpathrs, this is mainly a request as a user of
rustix
.However (as the author of
openat2
), there are quite a few extra features we plan to add and it would be nice if Rust programs could make use of them without having to wait forrustix
to have a SemVer-incompatibility version bump when a new field is added. I don't know what the long-term plan is for rustix (will there ever be a1.0
or will it be likelibc
where it is going to forever be non-1.0
?) but making it difficult to use neweropenat2
features (having to wait for a0.39
or0.40
release because of back-compat issues) would make me hesitant about using the rustixopenat2
wrappers.This would also allow libraries that expose
Openat2How
to update to use neweropenat2
features withrustix
without breaking their users and allow their users to make use of newer features fairly easily (though in the Rust community it seems that re-exporting types directly is something most people avoid due to risks of breakage when updating dependencies, so in practice this might not actually be that important).sunfishcode commentedon Jan 30, 2025
Taking another look here.
Rustix does do semver bumps occasionally; in fact, I'm preparing to do one soon here. However, it does them relatively infrequently at this point. So yes, it is good to think about extensibility.
The easy way to do extensibility for
openat2
is to just add new functions whenever we need to add new arguments to them. If Linux adds newopenat2
features, we could add anopenat2_more
, and we could add all the new arguments to it, without breaking semver. That's nice and simple, and it's tempting to wonder if that's a sufficient strategy here.(If there are a lot of new arguments, then maybe it makes sense to have a struct anyway, for ergonomics reasons, but we'll figure that out when we get there.)
One thing I want to ask about though is this:
It looks like an
OpenAt2How
struct as you sketched above would still require modifying rustix in order to add new features, so if I understand what you're saying here, it still wouldn't support extensions. The only way to allow extensions without changing rustix would be to have some form of opaque data flowing through, but that's a tricky question due to the possibility of the raw data containing raw pointers or fds.