Como alocar atomicamente um dispositivo de loop?

11

Estou escrevendo alguns scripts de shell para lidar com algumas coisas de imagem de disco e preciso usar dispositivos de loop para acessar algumas imagens de disco. No entanto, não tenho certeza de como alocar corretamente um dispositivo de loop sem expor meu programa a uma condição de corrida.

Eu sei que posso usar losetup -fpara obter o próximo dispositivo de loop não alocado e alocar esse dispositivo de loop da seguinte maneira:

ld=$(losetup -f)
sudo losetup $ld myfile.img
dostuffwith $ld

No entanto, no caso em que desejo executar várias instâncias do programa ao mesmo tempo, esse é quase um exemplo de condição de corrida e isso me incomoda bastante. Se eu tivesse várias instâncias desse programa em execução ou outros programas tentando também obter um dispositivo de loop, cada processo poderá não ser capaz de alocar o dispositivo de loop antes da próxima chamada losetup -f, caso em que ambos os processos pensariam que o mesmo loop dispositivo está disponível, mas apenas um pode obtê-lo.

Eu poderia usar a sincronização externa para isso, mas gostaria de (se possível) evitar complexidade adicional. Além disso, outros programas que usam dispositivos de loop provavelmente não respeitariam a sincronização que eu possa criar.

Como posso evitar essa possível condição de corrida? Idealmente, eu gostaria de poder descobrir e vincular o dispositivo de loop atomicamente, por exemplo, com um comando como:

ld=$(sudo losetup -f myfile.img)
dostuffwith $ld

No entanto, quando faço isso, $ldnão é atribuído ao caminho do dispositivo de loop e a sudosaída, como em, sudo ld=$(losetup -f myfile.img)dá erros de permissão.

AJMansfield
fonte

Respostas:

13

Esse é um problema clássico de simultaneidade: ao alocar um recurso, você precisa determinar atomicamente que o recurso está livre e reservá-lo; caso contrário, outro processo poderá reservar o recurso entre o horário em que você verifica se ele está livre e o tempo em que o reserva.

Use losetupo modo de alocação automática ( -f) e passe a --showopção para imprimir o caminho do dispositivo de loop.

ld=$(sudo losetup --show -f /tmp/1m)

Esta opção está presente no util-linux desde a versão 2.13 ( adicionada inicialmente como-s , mas --showfoi suportada em todas as versões lançadas e as versões recentes abandonaram o -snome da opção). Infelizmente a versão do BusyBox não a possui.

A versão 3.1 do kernel do Linux introduziu um método para executar a operação de alocação de dispositivo de loop diretamente no kernel, por meio do novo /dev/loop-controldispositivo. Este método é suportado apenas desde o util-linux 2.21. Com o kernel <3.1 ou util-linux <2.21, o losetupprograma enumera as entradas do dispositivo de loop para reservar uma. Não consigo ver uma condição de corrida no código; deve ser seguro, mas pode ter uma pequena janela durante a qual informará incorretamente que todos os dispositivos estão alocados, mesmo que esse não seja o caso.

Gilles
fonte
Para que serve </dev/tty?
Stéphane Chazelas
11
A última vez que tentei, até losetup --find --showcorridas. for i in {1..100}; do losetup -f -s $i & donenão me deu 100 dispositivos de loop. Os dispositivos de loop são incomuns o suficiente para que não importem normalmente; se isso acontecer, sua única opção é fazer seus próprios bloqueios e / ou verificar se o dispositivo de loop correto foi criado como uma reflexão tardia.
Frostschutz 25/05
O @frostschutz losetuppode falhar (por exemplo, porque você ficou sem entradas de loop), mas se ele relatar um nome de dispositivo, esse será o dispositivo que ele alocou com sucesso. As versões anteriores tiveram um bug que causou a gravação de um nome de dispositivo, mesmo que a alocação tenha falhado? Vejo no código fonte que a interface para alocação no kernel existe apenas desde o kernel 3.1. Talvez seja um bug na interface mais antiga que requer que o losetuputilitário faça a pesquisa?
Gilles
Oh, parece realmente estar corrigido em versões mais recentes. O util-linux-2.26.2 parece funcionar, o util-linux-2.24.1 imprime várias vezes /dev/loop14e tal, e o dispositivo pode estar ausente no final. Talvez a correção é cortesia de /dev/loop-control, costumava basta olhar para /proc/partitions...
frostschutz
@frostschutz Desde que o util-linux 2.21, losetupusa /dev/loop-controlse estiver presente, e isso não parece ter uma condição de corrida: a alocação ocorre no kernel e imprimir o caminho do dispositivo é a última coisa que o utilitário faz.
Gilles
5

Eu descobri. Embora eu não tenha certeza de como é o problema da permissão, eu posso filmar primeiro e depois perguntar assim:

sudo losetup -f myfile.img
ld=$(losetup -j myfile.img | grep -o "/dev/loop[0-9]*")
dostuffwith $ld
AJMansfield
fonte
2

Você poderia usar flock:

  tryagain=1
  while [[ $tryagain -ne 0 ]]; do
    ld=`losetup -f`
    flock -n $ld -c "losetup $ld myfile.img"
    tryagain=$?
  done

A idéia aqui é que você tente e flocko arquivo do dispositivo de loop; se outra instância do mesmo script o adquirir primeiro, ele chamará losetup $ld myfile.imge flockretornará 0. Para o script que perde a corrida, losetupnão será chamado e flockretornará 1, fazendo com que o loop se repita.

Para mais veja man flock.

Cachinhos Dourados
fonte
0

Se tudo o que você deseja fazer com a imagem como dispositivo de loopback é montá-la como um sistema de arquivos e trabalhar com o conteúdo, o mountcomando pode cuidar disso automaticamente.

mount -o loop myfile.img /tmp/mountpoint
Random832
fonte
Na verdade, no meu caso, em particular, não quero que ele seja montado - estou usando-o como um dispositivo de bloco e não um sistema de arquivos.
AJMansfield 25/05
@AJMansfield Além de montá-lo, o que você pode fazer com um dispositivo de bloco que não pode fazer com um arquivo?
Random832
Você não pode formatá-lo como espaço de troca em uma configuração de btrfs de disco completo. Eu pretendia usar o arquivo para espaço de troca, uma vez que o btrfs não suporta as operações necessárias para arquivos de troca e não posso ter uma partição de troca real com uma configuração de btrfs em disco completo.
AJMansfield 25/05