É porque propagate_one()
chama copy_tree()
sem CL_COPY_MNT_NS_FILE
. Nesse caso, se a raiz da árvore for uma montagem de um arquivo NS, copy_tree()
falhará com o erro EINVAL
. O termo "um arquivo NS" significa um dos arquivos /proc/*/ns/mnt
.
Lendo mais, noto que se a raiz da árvore não é um arquivo NS, mas uma das montagens filho é, ela é excluída da propagação (da mesma forma que uma montagem não localizável é).
Veja commit 4ce5d2b1a8fd, vfs: não copie montagens de bind de /proc/<pid>/ns/mnt
entre namespaces
Exemplo de um arquivo NS sendo silenciosamente ignorado durante a propagação
# mount --make-shared /tmp
# cd /tmp
# mkdir private_mnt
# mount --bind private_mnt private_mnt
# mount --make-private private_mnt
# touch private_mnt/child_ns
# unshare --mount=private_mnt/child_ns --propagation=shared ls -l /proc/self/ns/mnt
lrwxrwxrwx. 1 root root 0 Oct 7 18:25 /proc/self/ns/mnt -> 'mnt:[4026532807]'
# findmnt | grep /tmp
├─/tmp tmpfs tmpfs ...
│ ├─/tmp/private_mnt tmpfs[/private_mnt] tmpfs ...
│ │ └─/tmp/private_mnt/child_ns nsfs[mnt:[4026532807]] nsfs ...
Vamos criar uma montagem normal para comparação
# mkdir private_mnt/child_mnt
# mount --bind private_mnt/child_mnt private_mnt/child_mnt
Agora tente propagar tudo. (Crie uma montagem de ligação recursiva de private_mnt
dentro de /tmp
. /tmp
é uma montagem compartilhada).
# mkdir shared_mnt
# mount --rbind private_mnt shared_mnt
# findmnt | grep /tmp/shared_mnt
│ └─/tmp/shared_mnt tmpfs[/private_mnt] tmpfs ...
│ ├─/tmp/shared_mnt/child_ns nsfs[mnt:[4026532809]] nsfs ...
│ └─/tmp/shared_mnt/child_mnt tmpfs[/private_mnt/child_mnt] tmpfs ...
# nsenter --mount=/tmp/private_mnt/child_ns findmnt|grep /tmp/shared_mnt
│ └─/tmp/shared_mnt tmpfs[/private_mnt] tmpfs ...
│ └─/tmp/shared_mnt/child_mnt tmpfs[/private_mnt/child_mnt] tmpfs ...
Código do kernel
Aqui estão os extratos da versão atual do código, que foi adicionado no commit vinculado acima.
static int propagate_one(struct mount *m)
{
...
/* Notice when we are propagating across user namespaces */
if (m->mnt_ns->user_ns != user_ns)
type |= CL_UNPRIVILEGED;
child = copy_tree(last_source, last_source->mnt.mnt_root, type);
if (IS_ERR(child))
return PTR_ERR(child);
struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
int flag)
{
struct mount *res, *p, *q, *r, *parent;
if (!(flag & CL_COPY_UNBINDABLE) && IS_MNT_UNBINDABLE(mnt))
return ERR_PTR(-EINVAL);
if (!(flag & CL_COPY_MNT_NS_FILE) && is_mnt_ns_file(dentry))
return ERR_PTR(-EINVAL);