Em resumo: se você vincular a montar um arquivo /tmp/a
na parte superior de /tmp/b
em um novo namespace de montagem, mas o inode de /tmp/b
mudar no namespace pai, a montagem da ligação desaparecerá no namespace filho. Estou tentando entender o porquê.
mount (8) não expõe a capacidade de ligar arquivos individuais (apenas diretórios), portanto, a reprodução requer um executável adicional que possa emitir o syscall mount (2) necessário. Aqui está um exemplo simples (referido como bmount
abaixo):
#include <sys/mount.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("requires exactly 2 args\n");
return 1;
}
int err = mount(argv[1], argv[2], "none", MS_BIND, NULL);
if (err == 0) {
return 0;
} else {
printf("mount error (%d): %s\n", errno, strerror(errno));
return 1;
}
}
Configure o caso de teste:
# echo a > /tmp/a; echo b > /tmp/b; echo c > /tmp/c;
# ls -ldi /tmp/a /tmp/b /tmp/c
11403315 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/a
11403422 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/b
11403452 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/c
Agora, em um shell separado:
# unshare -m /bin/bash
# bmount /tmp/a /tmp/b
# ls -ldi /tmp/a /tmp/b /tmp/c
11403315 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/a
11403315 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/b
11403452 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/c
# cat /tmp/b
a
# grep "\/tmp\/" /proc/self/mounts
[redacted] /tmp/b ext4 rw,relatime,errors=remount-ro,data=ordered 0 0
No shell original:
# mv /tmp/c /tmp/b
# ls -ldi /tmp/a /tmp/b /tmp/c
ls: cannot access '/tmp/c': No such file or directory
11403315 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/a
11403452 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/b
No unshare
shell:
# ls -ldi /tmp/a /tmp/b /tmp/c
ls: cannot access '/tmp/c': No such file or directory
11403315 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/a
11403452 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/b
# cat /tmp/b
c
# grep "\/tmp\/" /proc/self/mounts
#
A montagem da ligação desapareceu silenciosamente, e o arquivo /tmp/b
do sistema de arquivos subjacente agora está visível dentro do namespace.
Eu encontrei um artigo do lwn.net que descreve uma mudança na semântica aqui: antes de 2013,%% do comando mv
rename(2)
no ponto de montagem falharia com EBUSY
, mas o comportamento foi alterado para que fosse bem-sucedido e, em seguida, a montagem seria removida. O commit do kernel relevante parece ser 8ed936b5671 .
As perguntas que tenho são:
- Por que a montagem é removida em qualquer mudança de inode? É apenas um detalhe de implementação do sistema de montagem, em que o ponto de montagem é identificado por um dentário em vez de um caminho simples?
- Existe uma maneira de fazer montagens de bind que sejam menos "frágeis" no sentido de que elas não podem ser substituídas ou removidas por operações do sistema de arquivos fora de seu namespace?
Um caso em que isso é relevante na prática é ip-netns (8) ; ip netns exec
funciona pela montagem de ligação /etc/netns/${NAMESPACE}/resolv.conf
na parte superior de /etc/resolv.conf
. Se o inode de /etc/resolv.conf
for alterado por resolvconf (8) ou systemd-resolved, o /etc/resolv.conf
atualizado será visível para o processo em execução dentro do namespace.