Por que mount não respeita a opção somente leitura para montagens de ligação?

35

No meu sistema Arch Linux (Linux Kernel 3.14.2), as montagens de ligação não respeitam a opção somente leitura

# mkdir test
# mount --bind -o ro test/ /mnt
# touch /mnt/foo

cria o arquivo /mnt/foo. A entrada relevante em /proc/mountsé

/dev/sda2 /mnt ext4 rw,noatime,data=ordered 0 0

As opções de montagem não correspondem às minhas opções solicitadas, mas correspondem ao comportamento de leitura / gravação da montagem de ligação e às opções usadas para montar originalmente /dev/sda2no/

/dev/sda2 / ext4 rw,noatime,data=ordered 0 0

Se, no entanto, eu remontar a montagem, ela respeitará a opção somente leitura

# mount --bind -o remount,ro test/ /mnt
# touch /mnt/bar
touch: cannot touch ‘/mnt/bar’: Read-only file system

e a entrada relevante em /proc/mounts/

/dev/sda2 /mnt ext4 ro,relatime,data=ordered 0 0

parece com o que eu poderia esperar (embora, na verdade, eu esperasse ver o caminho completo do testdiretório). A entrada /proc/mounts/para a montagem original de /dev/sda2/on /também não é alterada e permanece de leitura / gravação

/dev/sda2 / ext4 rw,noatime,data=ordered 0 0

Esse comportamento e a solução alternativa são conhecidos desde pelo menos 2008 e estão documentados na página de manual domount

Observe que as opções de montagem do sistema de arquivos permanecerão as mesmas do ponto de montagem original e não poderão ser alteradas passando a opção -o junto com --bind / - rbind. As opções de montagem podem ser alteradas por um comando de remontagem separado

Nem todas as distribuições se comportam da mesma forma. O Arch parece silenciosamente falhar em respeitar as opções, enquanto o Debian gera um aviso quando o mount bind não obtém o mount somente leitura

mount: warning: /mnt seems to be mounted read-write.

Há relatos de que esse comportamento foi "corrigido" no Debian Lenny e Squeeze, embora não pareça ser uma correção universal nem ainda funcione no Debian Wheezy. Qual é a dificuldade associada ao fazer com que a montagem de ligação respeite a opção somente leitura na montagem inicial?

StrongBad
fonte
Você tem um / etc / mtab?
precisa saber é o seguinte
Consulte também thread.gmane.org/gmane.linux.utilities.util-linux-ng/2979 e uma solução alternativa usando mount -t bindum script auxiliar em bugs.launchpad.net/ubuntu/+source/mountall/+bug/519380
Stéphane Chazelas
@ECarterYoung sim eu tenho um /etc/mtab. Após a montagem inicial, a entrada diz que a montagem é rw e após a remontagem diz ro, portanto está relatando o estado da montagem corretamente. É apenas o comando mount que falha.
StrongBad
3
Eu testei em duas máquinas Debian testing / unstable, uma rodando um kernel Debian e outra rodando um kernel.org, e nenhuma delas funciona mount --bind -o ro, ambas emitem uma mensagem mount: warning: «mountpoint» seems to be mounted read-write.. Parece que o Debian caiu ou perdeu o patch em algum momento ... funciona, no entanto.
Derobert
2
@StrongBad Testou isso conforme solicitado e também não funciona.
Derobert

Respostas:

21

Montagem de ligação é apenas ... bem ... uma montagem de ligação. Ou seja, não é uma nova montaria. Ele apenas "vincula" / "expõe" / "considera" um subdiretório como um novo ponto de montagem. Como tal, não pode alterar os parâmetros de montagem. É por isso que você está recebendo reclamações:

# mount /mnt/1/lala /mnt/2 -o bind,ro
mount: warning: /mnt/2 seems to be mounted read-write.

Mas, como você disse, uma montagem de ligação normal funciona:

# mount /mnt/1/lala /mnt/2 -o bind

E então um ro remount também funciona:

# mount /mnt/1/lala /mnt/2 -o bind,remount,ro 

No entanto, o que acontece é que você está alterando a montagem inteira e não apenas essa montagem de ligação. Se você der uma olhada em / proc / mounts, verá que a montagem de ligação e a montagem original mudam para somente leitura:

/dev/loop0 /mnt/1 ext2 ro,relatime,errors=continue,user_xattr,acl 0 0
/dev/loop0 /mnt/2 ext2 ro,relatime,errors=continue,user_xattr,acl 0 0

Então, o que você está fazendo é como alterar a montagem inicial para uma montagem somente leitura e, em seguida, fazer uma montagem de ligação que, obviamente, será somente leitura.

ATUALIZAÇÃO 20-07-2016:

O seguinte é verdadeiro para os kernels 4.5, mas não para os kernels 4.3 (isso está errado. Consulte a atualização nº 2 abaixo):

O kernel possui dois sinalizadores que controlam somente leitura:

  • A MS_READONLY: Indicando se a montagem é somente leitura
  • O MNT_READONLY: indicando se o "usuário" deseja que ele seja somente leitura

Em um kernel 4.5, fazer um mount -o bind,rorealmente fará o truque. Por exemplo, isto:

# mkdir /tmp/test
# mkdir /tmp/test/a /tmp/test/b
# mount -t tmpfs none /tmp/test/a
# mkdir /tmp/test/a/d
# mount -o bind,ro /tmp/test/a/d /tmp/test/b

criará uma montagem de ligação somente leitura de /tmp/test/a/dpara /tmp/test/b, que será visível /proc/mountscomo:

none /tmp/test/a tmpfs rw,relatime 0 0
none /tmp/test/b tmpfs ro,relatime 0 0

Uma visão mais detalhada é visível /proc/self/mountinfo, o que leva em consideração a visão do usuário (namespace). As linhas relevantes serão estas:

363 74 0:49 / /tmp/test/a rw,relatime shared:273 - tmpfs none rw
368 74 0:49 /d /tmp/test/b ro,relatime shared:273 - tmpfs none rw

Onde na segunda linha, você pode ver que diz ambos ro( MNT_READONLY) e rw( !MS_READONLY).

O resultado final é este:

# echo a > /tmp/test/a/d/f
# echo a > /tmp/test/b/f
-su: /tmp/test/b/f: Read-only file system

ATUALIZAÇÃO 20-07-2016 # 2:

Um pouco mais detalhadamente mostra que o comportamento de fato depende da versão do libmount, que faz parte do util-linux. O suporte para isso foi adicionado com este commit e foi lançado com a versão 2.27:

confirmar 9ac77b8a78452eab0612523d27fee52159f5016a
Autor: Karel Zak 
Data: segunda-feira, 17 de agosto 11:54:26 2015 +0200

    libmount: adicione suporte para "bind, ro"

    Agora é necessário usar duas chamadas de montagem (8) para criar um somente leitura
    montagem:

      mount / foo / bar -o bind
      mount / bar -o remontar, ro, vincular

    Este patch permite especificar "bind, ro" e a remontagem é feita
    automaticamente pelo libmount pelo syscall mount (2) adicional. Não é
    atômica, é claro.

    Assinado por: Karel Zak 

que também fornece a solução alternativa. O comportamento pode ser visto usando strace em uma montagem mais antiga e mais recente:

Velho:

mount("/tmp/test/a/d", "/tmp/test/b", 0x222e240, MS_MGC_VAL|MS_RDONLY|MS_BIND, NULL) = 0 <0.000681>

Novo:

mount("/tmp/test/a/d", "/tmp/test/b", 0x1a8ee90, MS_MGC_VAL|MS_RDONLY|MS_BIND, NULL) = 0 <0.011492>
mount("none", "/tmp/test/b", NULL, MS_RDONLY|MS_REMOUNT|MS_BIND, NULL) = 0 <0.006281>

Conclusão:

Para alcançar o resultado desejado, é necessário executar dois comandos (como o @Thomas já disse):

mount SRC DST -o bind
mount DST -o remount,ro,bind

As versões mais recentes do mount (util-linux> = 2.27) fazem isso automaticamente quando se executa

mount SRC DST -o bind,ro
V13
fonte
3
Sim mas não. No IIRC, existe algum suporte no kernel para diferentes pontos de montagem (não sistemas de arquivos) para ter opções diferentes. O Debian costumava ter um patch que mount -o bind,rocriava uma visão somente leitura de um sistema de arquivos de leitura e gravação (mas ele não parece mais estar presente no chiado).
Gilles 'SO- stop be evil'
Não vejo como isso contradiz o exposto. Hacks podem permitir todo tipo de coisas, incluindo coisas que não fazem muito sentido. Atualmente, a remontagem somente leitura no kernel 3.14 é finalmente tratada por esta chamada: mnt_make_readonly (real_mount (mnt)), que como você pode ver usa real_mount (), portanto praticamente afeta a montagem real e faz com que as montagens de bind reflitam o novo (somente leitura) sinalizador de montagem. Pelo menos esse é o meu entendimento.
V13
Portanto, isso seria uma conseqüência do patch "spread struct mount" (especificamente esse commit ), aparecendo pela primeira vez no kernel 3.3. Você sabe se as consequências desse patch foram discutidas no lkml ou no lwn?
Gilles 'SO- stop be evil'
7
mount --bind /tmp/ /mnt/tmp/; mount -o remount,bind,ro /mnt/tmp/... então touch /tmp/aestá tudo bem, mas touch /mnt/tmp/btouch: cannot touch ‘/mnt/tmp/b’: Read-only file system. Isso funciona tanto no Debian 3.13 quanto no kernel.org 3.14.2. Portanto, não muda apenas a montagem inteira. Pelo menos não com kernels recentes.
Derobert
11
Presumivelmente, a afirmação de que uma "montagem de ligação é apenas ... bem ... uma montagem de ligação". é realmente importante, mas não significa nada para mim. Também não entendo por que ele funciona pela segunda vez com a opção remontar.
StrongBad
9

A solução adequada é realmente montá-lo duas vezes. Na linha de comando:

mount -t none -o bind /source/dir /destination/dir
mount -t none -o bind,remount,ro /source/dir /destination/dir

Em /etc/fstab:

/source/dir            /destination/dir    none  bind            0 0
/source/dir            /destination/dir    none  remount,bind,ro 0 0

O manual ( man mount) afirma assim:

   The bind mounts.
          Since Linux 2.4.0 it is possible to remount part of the file hierarchy somewhere else. The call is
                 mount --bind olddir newdir
   [...]
          Note that the filesystem mount options will remain the same as those on the original mount point, and cannot be changed  by  passing  the  -o  option
          along with --bind/--rbind. The mount options can be changed by a separate remount command, for example:
          .
                 mount --bind olddir newdir
                 mount -o remount,ro newdir
          .
          Note  that  behavior  of  the remount operation depends on the /etc/mtab file. The first command stores the 'bind' flag to the /etc/mtab file and the
          second command reads the flag from the file.  If you have a system without the /etc/mtab file or if you explicitly define source and target  for  the
          remount command (then mount(8) does not read /etc/mtab), then you have to use bind flag (or option) for the remount command too. For example:
          .
                 mount --bind olddir newdir
                 mount -o remount,ro,bind olddir newdir
Thomas
fonte
Isso parece funcionar com pelo menos o Ubuntu 14.04 LTS e o kernel 3.19.0-51-lowlatency. Agradável!
Mikko Rantalainen
0

Você está perguntando da perspectiva da mount(8)linha de comando (aceitável neste site). Esse comando foi discutido nas outras respostas e, em alguns casos, abstrai a segunda mount(2)chamada de sistema necessária .

Mas por que a segunda chamada do sistema é necessária? Por que uma única mount(2)chamada não pode criar a montagem de ligação somente leitura?

A mount(2)página de manual explica que existem, como outros já apontaram, dois conjuntos de sinalizadores sendo configurados:

  • Os sinalizadores subjacentes do sistema de arquivos
  • Os sinalizadores de ponto de montagem do VFS

Diz:

Desde o Linux 2.6.16, MS_RDONLYpode ser definido ou limpo com base no ponto de montagem, bem como no sistema de arquivos subjacente. O sistema de arquivos montado será gravável apenas se nem o sistema de arquivos nem o ponto de montagem forem sinalizados como somente leitura.

E em relação a MS_REMOUNT:

Desde o Linux 2.6.26, esse sinalizador pode ser usado MS_BINDpara modificar apenas os sinalizadores por ponto de montagem. Isso é particularmente útil para definir ou limpar o sinalizador "somente leitura" em um ponto de montagem sem alterar o sistema de arquivos subjacente. Especificando mountflags como:

      MS_REMOUNT | MS_BIND | MS_RDONLY

fará acesso através deste ponto de montagem somente leitura, sem afetar outros pontos de montagem.

Acho que o problema surgiu quando as montagens de ligação foram introduzidas pela primeira vez:

Se mountflags incluir MS_BIND(disponível desde o Linux 2.4), execute uma montagem de ligação. ... Os bits restantes no argumento mountflags também são ignorados, com exceção de MS_REC. (A montagem de ligação possui as mesmas opções de montagem que o ponto de montagem subjacente.)

Parece que, em vez de usar MS_BIND | MS_REMOUNTcomo sinal para definir apenas os sinalizadores VFS, eles poderiam ter escolhido exceto (e aceitar) MS_RDONLYjunto com o inicial MS_BINDe aplicá-lo ao ponto de montagem.

Portanto, devido à semântica um tanto estranha da mount(2)chamada do sistema:

  • A primeira chamada cria a montagem de ligação e todos os outros sinalizadores são ignorados
  • A segunda chamada (com remontagem) define os sinalizadores do ponto de montagem como somente leitura
Jonathon Reinhart
fonte