Both mount calls in the code you show need changes. In the first call, when creating the mount, you shouldn't specify the
MS_SHARED flag; this is triggering your
EINVAL error. Instead, just create the mount without that flag (i.e., the
flags argument should be 0). This will create a new mount point with a default propagation type. That type is either
MS_SHARED if the parent mount also has shared propagation, or otherwise the propagation type is
MS_PRIVATE. (For more details, see the NOTES section in the
mount_namespaces(7) manual page.)
In the second
mount() call, you don't need the
MS_REMOUNT flag, and in fact when you use that flag, the
MS_PRIVATE flag is ignored. See the mount(2) manual page (in particular, note the words "with the tests being conducted in the order listed here"):
A call to mount() performs one of a number of general types of
operation, depending on the bits specified in mountflags. The
choice of which operation to perform is determined by testing the
bits set in mountflags, with the tests being conducted in the
order listed here:
* Remount an existing mount: mountflags includes MS_REMOUNT.
* Create a bind mount: mountflags includes MS_BIND.
* Change the propagation type of an existing mount: mountflags
includes one of MS_SHARED, MS_PRIVATE, MS_SLAVE, or MS_UNBIND‐
* Move an existing mount to a new location: mountflags includes
* Create a new mount: mountflags includes none of the above
Just remove the
MS_REMOUNT flag in the second
mount() call and you should obtain the result that (I think) you want.
I'm not absolutely sure of the reasons why this two-step process is required, but I suspect the reason is limitations in the design
mount() API. That API has steadily had pieces added to it over the years (see the quoted text above for evidence of that), and by the time the mount propagation feature was added (around 2005), this was probably the only backwards compatible way to do it.