Skip to content

Add mount_setattr #1002

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

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

yujincheng08
Copy link
Contributor

@yujincheng08 yujincheng08 commented Jan 24, 2024

Preliminary implementation of mount_setattr.

A few notes:

  1. I leave the size non-exported. Should we export this?
  2. I intentionally place mount_setattr in misc.rs because I am going to add statmount and listmount syscall once 6.8 releases and libc adds the syscall number.
  3. CI test requires android: add missing syscall constants rust-lang/libc#3558

Fix #975

@yujincheng08 yujincheng08 force-pushed the mount_setattr branch 3 times, most recently from 396991e to 78c5696 Compare January 24, 2024 05:26
#[derive(Debug, Copy, Clone)]
#[allow(missing_docs)]
pub struct MountAttr<'a> {
pub attr_set: MountAttrFlags,
Copy link
Member

Choose a reason for hiding this comment

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

According to Linux's documentation, attr_set is a __u64, while MountAttrFlags is currently a c_uint. Could you add a test testing that the layout matches, following one of the "layouts" tests in the tree?

@sunfishcode
Copy link
Member

I leave the size non-exported. Should we export this?

If you don't have a need for it at this time, then we don't need to. If someone later has a need for it, we can add another function that exposes it.

@yujincheng08 yujincheng08 marked this pull request as draft April 14, 2024 06:04
Copy link
Contributor

@ayosec ayosec left a comment

Choose a reason for hiding this comment

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

I would like to help to finish this pull-request, in case @yujincheng08 can't continue it.

pub attr_set: MountAttrFlags,
pub attr_clr: MountAttrFlags,
pub propagation: MountPropagationFlags,
pub userns_fd: BorrowedFd<'a>,
Copy link
Contributor

Choose a reason for hiding this comment

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

userns_fd field is expected as a __u64, but BorrowedFd is 4 bytes. Maybe it is safer to declare a private struct with the exact same fields of the C API, and translate the MountAttr fields to it before calling mount_setattr.

#[repr(C)]
pub(crate) struct mount_attr {
    pub attr_set: u64,
    pub attr_clr: u64,
    pub propagation: u64,
    pub userns_fd: u64,
}

Also, userns_fd is needed only when attr_set includes MOUNT_ATTR_IDMAP. The field could be declared as an Option<BorrowedFd<'a>>, and send it as -EBADFD to the syscall if it is None.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe it is safer to declare a private struct with the exact same fields of the C API, and translate the MountAttr fields to it before calling mount_setattr.

FWIW: https://codeberg.org/crabjail/lnix/src/commit/092defd573bab18f32c466dd7f774a03236babb6/src/mount/mountfd.rs#L453-L488

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that the way mount_attr is defined in lnix is better than exposing the fields directly.

The usage would be something like this:

mount_setattr(
    dir_fd,
    c"", 
    MountSetattrFlags::RECURSIVE | MountSetattrFlags::EMPTY_PATH,
    MountAttr::new().set_flags(MountAttrFlags::RDONLY),
)?;

@sunfishcode Do you want to take the same approach from lnix?

I can implement the changes if needed.

Copy link
Member

Choose a reason for hiding this comment

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

How is userns_fd exposed in the lnix API?

In general, I'm in favor of encapsulating these fields like this, provided we can do so without restricting useful functionality.

Copy link
Contributor

Choose a reason for hiding this comment

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

How is userns_fd exposed in the lnix API?

It seems that it does not support setting values to userns_fd (there is a TODO: ID-mapped mounts, and userns_fd only appears in the struct definition).

mount_setattr() will not close the file descriptor. I guess that MountAttr should take the ownership of the file descriptor, so it can guarantee that the descriptor is valid when mount_setattr is called, and closed when MountAttr is not needed anymore.

Maybe something like this:

pub struct MountAttr {
    mount_attr: sys::mount_attr,
    userns_fd: Option<OwnedFd>, 
}

impl MountAttr {

    pub fn set_userns_fd(&mut self, userns_fd: OwnedFd) -> &mut Self {
        self.mount_attr.userns_fd = userns_fd.as_raw_fd() as u64;
        self.userns_fd = Some(userns_fd);
        self
    }

    // ...

Choose a reason for hiding this comment

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

It seems to me like the current struct-based approach was done as an implementation detail at the syscall level to allow extensibility in the future. I wouldn't see anything wrong with a simple implementation of this API using only (an admittedly large number of) function parameters: dirfd, pathname, flags, set, clear, propagation, userns.

We could even imagine specialized cases similar to what is done for other mount APIs (like how the mount syscall is exposed also as mount_remount, or there are many typesafe fsconfig variants).

Copy link
Contributor

Choose a reason for hiding this comment

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

It seems to me like the current struct-based approach was done as an implementation detail at the syscall level to allow extensibility in the future. I wouldn't see anything wrong with a simple implementation of this API using only (an admittedly large number of) function parameters: dirfd, pathname, flags, set, clear, propagation, userns.

Well this would mean rustix needs a semver bump or a new function if the kernel adds a new field.

We could even imagine specialized cases similar to what is done for other mount APIs (like how the mount syscall is exposed also as mount_remount, or there are many typesafe fsconfig variants).

There is one difference, the mount functions you listed have an multiplexed interface that got demultiplexed by rustix. mount_setattr isn't a multiplexed syscall (in that form) so splitting would mean you need multiple syscalls instead of one.


Summarizing my thought, we should first decided whether we want extensibility in the future (w/o breaking) or not.

Choose a reason for hiding this comment

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

I see it as a matter of pragmatism: this would be an easy way to resolve this issue in the short term...

Should the API ever change, then there would be a lot of options at that point, including adding a complex API that takes a struct...

Copy link
Contributor

Choose a reason for hiding this comment

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

It seems to me like the current struct-based approach was done as an implementation detail at the syscall level to allow extensibility in the future.

An advantage of using a struct is that it is easier to omit default fields with dedicated builder functions. For example, with the draft in #1002 (comment), an ID-mapped mount could be created with:

use MountSetAttrFlags as Flags;

mount_setattr(
    dir_fd,
    c"", 
    Flags::RECURSIVE | Flags::EMPTY_PATH,
    MountAttr::new_with_idmap(userns),
)?;

Copy link
Contributor

Choose a reason for hiding this comment

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

  • openat2 has a future extensibility struct that rustix decided to inline into the function signature.
  • clone3 has a future extensibility struct but is impossible to be implemented by rustix.
  • mount_setattr, sched_setattr, landlock_create_ruleset, statmount/listmount, perf_event_open have a future extensibility struct and are not yet implemented by rustix.

So we are not dealing with a single syscall but also with the design future syscalls in rustix might follow.

#[repr(C)]
#[derive(Debug, Copy, Clone)]
#[allow(missing_docs)]
pub struct MountAttr<'a> {
Copy link
Contributor

Choose a reason for hiding this comment

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

This struct should be non_exhaustive.

@sunfishcode sunfishcode added the enhancement New feature or request label Mar 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add mount_setattr
5 participants