Skip to content

mount: control namespace switching with --namespace-stage - #4029

Open
cgoesche wants to merge 8 commits into
util-linux:masterfrom
cgoesche:mount_ns_stage_option
Open

mount: control namespace switching with --namespace-stage#4029
cgoesche wants to merge 8 commits into
util-linux:masterfrom
cgoesche:mount_ns_stage_option

Conversation

@cgoesche

@cgoesche cgoesche commented Feb 10, 2026

Copy link
Copy Markdown
Collaborator

This new option allows a user to control at which stage of
the mount process libmount should switch to the target
namespace defined with '--namespace'. The usefulness of this
feature is stressed in situations where users wish to mount a
filesystem from one namespace into the other, e.g. dynamically
mount a filesystem into a running container.

@cgoesche cgoesche changed the title Mount ns stage option mount: control namespace switching with --namespace-stage Feb 10, 2026
@cgoesche

cgoesche commented Feb 10, 2026

Copy link
Copy Markdown
Collaborator Author

TODOs:

  • add function description to new routines
  • improve namespace stage flags (maybe?)
  • add tests
  • add debug message in ns_switch_appropriate()

@martinetd martinetd left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for tagging me in the issue (and for the implementation itself!)

Haven't had more than a very quick look, will do a more proper review when time allows

Comment thread bash-completion/mount Outdated
Comment thread sys-utils/mount.c Outdated
Comment thread sys-utils/mount.8.adoc

*prepare*: During the mount preparation stage, i.e. fstab parsing, mount options preparation, source/target path canonicalization, file system type guessing and mount helper preparation.

*attach*: After the *prepare* stage and before the file system is mounted.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I've successfully used attach to bind something from outside a container to inside it

Comment thread sys-utils/mount.8.adoc
*--namespace-stage* _stage_::
Control at which stage of the mount process libmount should switch to the target namespace defined by *-N*.
+
Valid values for _stage_ are:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

These keywords (prepare/attach) are backed by the implementation so they aren't wrong, but I'm not sure they're user-friendly: would source/dest be easier to understand? (as in, "enter namespace when evaluating the source device", "enter namespace when evaluating the mount destination")

I'm honestly not convinced myself as that fails to conveys nuances around fstab, and anyone who'd consider using this can take 5 minutes to understand, so I'm fine with the current words.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good points! Note that the current names are subject to change, as I first focused on the actual functionality rather than the semantics. Maybe we need to partition the stages even further, I'm thinking prep-source, prep-target, etc., but Im not sure.

Comment thread sys-utils/mount.8.adoc
+
*all*: Switch to the target namespaces in all stages.

*prepare*: During the mount preparation stage, i.e. fstab parsing, mount options preparation, source/target path canonicalization, file system type guessing and mount helper preparation.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

wow, I made systemd abort trying this one:

Feb 10 15:51:22 xx systemd[1]: Assertion 'path_is_absolute(p)' failed at src/basic/chase.c:648, function chase(). Aborting.
Feb 10 15:51:22 xx systemd[1]: Caught <ABRT>, from our own process.

I ran ./mount -N <pid> --namespace-stage=prepare --bind /foo . (only crashed the first time, /foo exists in the target namespace)

I'll need to take some time to play with that one. I would have expected the target namespace's /foo to be mounted locally but while it succeeded I didn't see anything change...
Using a directory other than ., mount fails with mount point does not exist, whether the path exists locally or in the dest namespace so I'm not sure how to use that one.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

(systemd bug made me curious, it was fixed in v259 here: systemd/systemd@e1f3d79 -- I'm still not quite sure what the mount command actually did though, I don't see anything in /proc/mounts that is odd...)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Could you run the same command with LIBMOUNT_DEBUG=all please :D ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think when using . as target, mount will just canonicalize that to /. I ran the same command and when I inspect the mounts with findmnt /, I can see the source tmpfs[/foo] for target /.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Indeed, looks like it was transformed into / from the LIBMOUNT_DEBUG log -- unfortunately(?) that call fails on this machine so I'll try again from work during work hours tomorrow to understand what happened...

And I guess I've also earned the right to figure out what stage we'd need to mount a directory from a container into the host.. :)
Will try to have a look by next week

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should probably split this thread, but...

  • for namespace-stage attach, it's interesting, I can make this work with bwrap as you did but not with the same sequence using podman run --pid=host (pid=host for simplicity finding the child pid but it's the same without) ... Looking at LIBMOUNT_DEBUG it now fails with EACCES but I'm sure I had ENOENT on open_tree() in the target namespace earlier, I don't get it... hmm it works if I run podman as root, something about user namespaces? but then I don't see why bwrap is any different here... Well, likely not a mount problem, so I guess it's fine? :|
$ podman run --pid=host -ti --rm docker.io/alpine
/ # echo $$
47642
# from other shell
# LIBMOUNT_DEBUG=all ./mount -N 47642  --namespace-stage=prepare --bind /tmp /mnt
...
47727: libmount:     HOOK: [0x7fc4f3e6cc00]:  BIND/MOVE
47727: libmount:      CXT: [0x2ed48850]:  alloc '__mount' data
47727: libmount:      CXT: [0x2ed48850]: open_tree(path=/tmp clone)
47727: libmount:      CXT: syscall 'open_tree' [failed: Permission denied]
47727: libmount:     HOOK: [0x7fc4f3e6cc00]: init fs/tree failed [errno=13 Permission denied]
47727: libmount:     HOOK: [0x7fc4f3e6cc00]: prepare mount done [rc=-13]
47727: libmount:      CXT: [0x2ed48850]: <--- stage:prep [rc=-13 status=-13]
47727: libmount:      CXT: [0x2ed48850]: mount: preparing failed
  • for ., it resolves to / with --namespace but I'd expect it to resolve to $PWD if the directory refers to a path within the current namespace (and, sure, why not / as it does right now or just error out for . inside the target namespace - using . here doesn't make sense to me)
    This works with e.g.
mount --bind /tmp .
ls $PWD # accessing . is "beneath" the mount so need to use $PWD to check
# or
mount --bind . /mnt
ls /mnt
# or even
mount -N <pid> --namespace-stage attach --bind . /mnt
ls /mnt # inside target namespace

So it's surprising that mount -N <pid> --namespace-stage prepare --bind /whatever . doesn't behave similarly with . as $PWD-in-command-namespace.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

So, I tried something similar to the bwrap method with podman and I can successfully mount a filesystem from inside the container to the outside and vice versa.

1. Create the container

Shell 1

$ podman run --pid=host -ti --rm docker.io/alpine
/ # mkdir /tmp/container
/ # echo bar > /tmp/container/foo
/ # echo $$
316355

2. Mount file system from Container -> Host

Shell 2 on Host

$ mkdir /tmp/outside
$ sudo ./mount --namespace 316355 --namespace-stage prepare --bind /tmp/container /tmp/outside/
$ cat /tmp/outside/foo
bar
$ findmnt /tmp/outside
TARGET SOURCE   FSTYPE OPTIONS
/tmp/outside
       overlay[/tmp/container]
                overla rw,relatime,context="system_u:object_r:container_file_t:....

3. Mount file system from Host -> Container

Shell 2 on Host

$ sudo umount /tmp/outside
$ echo b > /tmp/outside/a
$ sudo ./mount --namespace 316355 --namespace-stage attach --bind /tmp/outside /tmp/container

Shell 1 in Container

/ # cat /tmp/container/a
b

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Note, I ran podman only as a standard user.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks, this is the same as what I did so I'll check a bit more when time allows, but as said above I think it's not a mount problem so we can move forward about this one

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry for the delay, for the first point (mount failing with EACCES) the problem seems to be that on my machine podman as user is using fuse.overlayfs, and accessing the fuse mount as a different user (even root) fails as such -- so that confirms there's no problem with mount on this... Well, I guess the error message could mention Permission denied without probing with LIBMOUNT_DEBUG, but that's orthogonal to this PR, so I think we're good here :)
(Anyway, I think podman now normally doesn't use fuse even as normal user, so that's probably something odd with my setup (perhaps leftovers from upgrading over an old setup that used it...))

That leaves the second point -- I still think . for destination in mount-stage=prepare context ought to be local to the mount process, or if that's difficult making it error out altogether. I don't see where making it behave like / with --namespace would make sense anywhere.

The routines added in this patch help to control the switch
to a given target namespace, that is, at what stages of the
mount process a switch is appropriate.

The desired stages are defined in a bitmask of MNT_NS_STAGE*
flags, stored in the struct libmnt_ns_stage, which also
contains the current stage (member: curr_stage). This current
stage has to be set in specific places, as it is compared to
the stage mask to determine if a switch is appropriate or not.

Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
…ately

Setting the current namespace stage in appropriate places
ensures that the check before the switch is accurate.

Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
… flag

This can allow higher level users to specify namespace stage flags with
their respective names. As of now valid names are:

- "all" = MNT_NS_STAGE_ALL
- "prepare" = MNT_NS_STAGE_PREP
- "attach" = MNT_NS_STAGE_ATTACH

Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
This new option allows a user to control at which stage of
the mount process libmount should switch to the target
namespace defined with '--namespace'. The usefulness of this
feature is stressed in situations where users wish to mount
a filesystem from outside of the targeted namespace into it,
e.g. to mount a filesystem into a container.

Closes: util-linux#3884
Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
Signed-off-by: Christian Goeschel Ndjomouo <cgoesc2@wgu.edu>
@cgoesche
cgoesche force-pushed the mount_ns_stage_option branch from 643e38d to 119cfee Compare February 18, 2026 00:09
Comment thread libmount/src/context.c
* is appropriate for the current stage
*/
if (mnt_context_get_target_ns(cxt)) {
int rc = ns_switch_appropriate(cxt);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi, I'm looking for some way to manipulate container mounts and found this PR. Thanks for your work!

This changes the semantics of mnt_context_switch_ns() (think of mnt_context_switch_origin_ns()). Can we just let the callers call ns_switch_appropriate() instead?

@karelzak

Copy link
Copy Markdown
Collaborator

Please note that I am currently focusing on stabilizing the new release. I will work on this PR later.

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.

4 participants