A documentação pivot_root () antecipou o recurso de namespaces de montagem?

1

pivot_root() moves the root filesystem of the calling process to the directory put_old and makes new_root the new root filesystem of the calling process.

The typical use of pivot_root() is during system startup, when the system mounts a temporary root filesystem (e.g., an initrd), then mounts the real root filesystem, and eventually turns the latter into the current root of all relevant processes or threads.

pivot_root() may or may not change the current root and the current working directory of any processes or threads which use the old root directory. The caller of pivot_root() must ensure that processes with root or current working directory at the old root operate correctly in either case. An easy way to ensure this is to change their root and current working directory to new_root before invoking pivot_root().

The paragraph above is intentionally vague because the implementation of pivot_root() may change in the future. At the time of writing, pivot_root() changes root and current working directory of each process or thread to new_root if they point to the old root directory. This is necessary in order to prevent kernel threads from keeping the old root directory busy with their root and current working directory, even if they never access the filesystem in any way. In the future, there may be a mechanism for kernel threads to explicitly relinquish any access to the filesystem, such that this fairly intrusive mechanism can be removed from pivot_root().

...

BUGS

pivot_root() should not have to change root and current working direc‐ tory of all other processes in the system.

Some of the more obscure uses of pivot_root() may quickly lead to insanity.

- man pivot_root , man pages do Linux 4.15

Estou trabalhando em um caso em que há vários processos em execução quando pivot_root () é chamado.

A página de manual não parece muito clara sobre como as possíveis implementações de pivot_root () podem lidar com o caso com vários processos. Digamos que temos dois processos, S (ystemd) e P (lymouth). Atualmente, P e S mudam seu diretório raiz e de trabalho para new_root e, em seguida, S chama pivot_root (). Com a implementação atual, isso funciona bem.

Digamos que S e P "alterem seu diretório raiz" antes de pivot_root (), usando chroot (). Mas, como nos diz man chroot , é possível deixar uma jaula chroot () se você for root ( mkdir foo; chroot foo; cd ..; chroot . ). Parece claro que os processos têm duas raízes associadas:

  1. seu chroot atual
  2. a raiz do namespace de montagem

Depois de pivot_root (), S deve observar que a raiz de seu namespace de montagem é igual ao seu chroot atual. Porque se houvesse um sistema de arquivos raiz mais profundo para o qual ele pudesse escapar em um ponto futuro, esse sistema de arquivos raiz estaria ocupado e não poderia ser desmontado. Eu acho que permitir que o sistema de arquivos raiz antigo seja desmontado foi o objetivo principal do pivot_root (). (Observe que, com base nisso, /sbin/pivot_root é praticamente inútil, a menos que você assuma a implementação atual de pivot_root ()).

Atualmente, P observa a mesma coisa - porque está no mesmo namespace de montagem que S.

Parece que a implementação alternativa de pivot_root () colocaria o processo de chamada em um novo namespace de montagem alterado. Essa é uma leitura válida?

Acredito que o pivot_root () original anteceda os namespaces de montagem. Sabemos se esse plano para uma implementação alternativa de pivot_root () antecipou a necessidade de alguns dos recursos dos namespaces de montagem ou se esse requisito foi negligenciado?

(Observo que os namespaces de montagem também soam muito como um "mecanismo para que os encadeamentos do kernel abram explicitamente qualquer acesso        para o sistema de arquivos ", por exemplo, os encadeamentos do kernel poderiam fazer o equivalente a pivot_root () em um tmpfs vazio).

    
por sourcejedi 17.06.2018 / 00:51

1 resposta

1

Não. OMI isto não é muito claro, mas há uma leitura muito mais consistente e correta.

A parte essencial de pivot_root (), que deve ser a mesma em qualquer implementação, é:

pivot_root() moves the root filesystem of the calling process to the directory put_old and makes new_root the new root filesystem of the calling process.

A parte essencial de pivot_root () não está limitada apenas ao processo de chamada. A operação descrita nesta citação funciona no namespace de montagem do processo de chamada. Isso afetará a exibição de todos os processos no mesmo namespace de montagem.

Suponho que a página do manual tente definir isso falando sobre o "sistema de arquivos raiz" aqui, ao passo que quando ele quiser se referir ao chroot atual, ele diz "raiz" por conta própria. Eu acho que usa esses termos de forma consistente, mas até que eu entendi os detalhes do pivot_root (), isso não era nada claro para mim.

Considere o efeito que a mudança essencial tem em um segundo processo - ou thread de kernel - cujo diretório de trabalho era o antigo sistema de arquivos raiz. Seu diretório atual ainda será o antigo sistema de arquivos raiz. Isso manterá o ponto de montagem /put_old ocupado e, portanto, não será possível desmontar o sistema de arquivos raiz antigo.

Se você controlar esse segundo processo, resolva isso, conforme a página do manual, configurando seu diretório de trabalho como new_root antes de pivot_root () ser chamado. Depois que pivot_root () é chamado, seu diretório atual ainda será o novo sistema de arquivos raiz.

Portanto, o processo S (ystemd) foi configurado para sinalizar o processo P (lymouth), para alterar o diretório de trabalho antes que o S chame pivot_root (). Sem problemas. Mas também temos threads do kernel, que começam em / . A implementação atual do pivot_root () cuida dos threads do kernel para nós; é equivalente a configurar os diretórios de trabalho dos encadeamentos do kernel e qualquer outro processo para new_root antes da parte essencial de pivot_root ().

Exceto, a implementação atual do pivot_root () somente altera o diretório de trabalho de um processo se o diretório de trabalho antigo for / . Então é bem fácil ver a diferença que isso faz:

$ unshare -rm
# cd /tmp    # work in a subdir instead of '/', and pivot_root() will not change it
# /bin/pwd
/tmp
# mount --bind /new-root /new-root
# pivot_root /new-root /new-root/mnt
# /bin/pwd
/mnt/tmp    # see below: if pivot_root had not updated our current chroot, this would show /tmp

v.s.

$ unshare -rm
# cd /
# /bin/pwd
/
# ls -lid .
2 dr-xr-xr-x. 19 nfsnobody nfsnobody 4096 Jun 13 01:17 .
# ls -lid /newroot
6424395 dr-xr-xr-x. 20 nfsnobody nfsnobody 4096 May 10 12:53 /new-root
# mount --bind /new-root /new-root
# pivot_root /new-root /new-root/mnt
# /bin/pwd
/
# ls -lid .
6424395 dr-xr-xr-x. 20 nobody nobody 4096 May 10 12:53 .
# ls -lid /
6424395 dr-xr-xr-x. 20 nobody nobody 4096 May 10 12:53 /
# ls -lid /mnt
2 dr-xr-xr-x. 19 nobody nobody 4096 Jun 13 01:17 /mnt

Agora eu entendo o que está acontecendo com o diretório de trabalho, acho mais fácil entender o que está acontecendo com o chroot (). O chroot atual do processo que chama pivot_root () pode ser uma referência ao sistema de arquivos raiz original, assim como seu diretório de trabalho atual pode ser.

Note, se você fizer chdir () + pivot_root () mas esqueceu de chroot (), seu diretório atual estará fora do seu chroot atual. Quando seu diretório atual está fora de seu chroot atual, as coisas ficam bastante confusas. Você provavelmente não deseja executar seu programa nesse estado.

# cd /
# python
>>> import os
>>> os.chroot("/newroot")
>>> os.system("/bin/pwd")
(unreachable)/
0
>>> os.getcwd()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 2] No such file or directory
>>> os.system("ls -l ./proc/self/cwd")
lrwxrwxrwx. 1 root root 0 Jun 17 13:46 ./proc/self/cwd -> /
0
>>> os.system("ls -lid ./proc/self/cwd/")
2 dr-xr-xr-x. 19 root root 4096 Jun 13 01:17 ./proc/self/cwd/
0
>>> os.system("ls -lid /")
6424395 dr-xr-xr-x. 20 root root 4096 May 10 12:53 /
0

POSIX não especifica o resultado de pwd ou getcwd () nesta situação :). POSIX não dá nenhum aviso de que você pode obter um erro "Nenhum arquivo ou diretório" (ENOENT) de getcwd (). Manpages do Linux apontam esse erro como possível, se o diretório de trabalho foi desvinculado (por exemplo, com rm ). Eu acho que esse é um bom paralelo.

    
por 17.06.2018 / 13:50