¿Cómo asignar atómicamente un dispositivo de bucle?

11

Estoy escribiendo algunos scripts de shell para manejar algunas cosas de imagen de disco, y necesito usar dispositivos de bucle para acceder a algunas imágenes de disco. Sin embargo, no estoy seguro de cómo asignar correctamente un dispositivo de bucle sin exponer mi programa a una condición de carrera.

Sé que puedo usar losetup -fpara obtener el siguiente dispositivo de bucle sin asignar, y luego asignar ese dispositivo de bucle de esta manera:

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

Sin embargo, en el caso en que quiero ejecutar varias instancias del programa al mismo tiempo, este es casi un ejemplo de libro de texto de una condición de carrera, y eso me molesta bastante. Si tuviera varias instancias de este programa ejecutándose, u otros programas que también intentan obtener un dispositivo de bucle, entonces cada proceso podría no ser capaz de asignar el dispositivo de bucle antes de que la próxima llamada losetup -f, en cuyo caso ambos procesos pensarían que el mismo bucle el dispositivo está disponible, pero solo uno puede obtenerlo.

Podría usar sincronización externa para esto, pero me gustaría (si es posible) evitar una complejidad adicional. Además, otros programas que usan dispositivos de bucle probablemente no respetarían la sincronización que se me ocurra.

¿Cómo puedo evitar esta posible condición de carrera? Idealmente, me gustaría poder descubrir y enlazar el dispositivo de bucle atómicamente, por ejemplo con un comando como:

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

Sin embargo, cuando hago eso, $ldno se asigna a la ruta del dispositivo de bucle, y moverlo sudohacia afuera, como en sudo ld=$(losetup -f myfile.img)da errores de permiso.

AJMansfield
fuente

Respuestas:

13

Este es un problema clásico en la concurrencia: al asignar un recurso, debe determinar atómicamente que el recurso es gratuito y reservarlo; de lo contrario, otro proceso podría reservar el recurso entre el momento en que verifica que es gratuito y el momento en que lo reserva.

Utilice losetupel modo de asignación automática ( -f) y pase la --showopción para que imprima la ruta del dispositivo de bucle.

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

Esta opción ha estado presente en util-linux desde la versión 2.13 ( inicialmente agregada como-s , pero --showha sido admitida en todas las versiones publicadas y las versiones recientes han eliminado el -snombre de la opción). Lamentablemente, la versión de BusyBox no lo tiene.

La versión 3.1 del kernel de Linux introdujo un método para realizar la operación de asignación de dispositivos de bucle directamente en el kernel, a través del nuevo /dev/loop-controldispositivo. Este método solo es compatible desde util-linux 2.21. Con kernel <3.1 o util-linux <2.21, el losetupprograma enumera las entradas del dispositivo de bucle para reservar una. Sin embargo, no puedo ver una condición de carrera en el código; debería ser seguro, pero podría tener una pequeña ventana durante la cual informará incorrectamente que todos los dispositivos están asignados, aunque este no sea el caso.

Gilles 'SO- deja de ser malvado'
fuente
¿Qué es la </dev/ttyde?
Stéphane Chazelas
1
La última vez que lo intenté, incluso losetup --find --showcarreras.for i in {1..100}; do losetup -f -s $i & doneno me dio 100 dispositivos de bucle. Los dispositivos de bucle son poco comunes para que no importe normalmente; si lo hace, su única opción es hacer sus propios bloqueos y / o verificar que el dispositivo de bucle correcto se haya creado como una ocurrencia tardía.
frostschutz
@frostschutz losetuppuede fallar (por ejemplo, porque se ha quedado sin entradas de bucle), pero si informa un nombre de dispositivo, ese es el dispositivo que asignó con éxito. ¿Las versiones anteriores tenían un error que causaba que escribiera un nombre de dispositivo aunque la asignación había fallado? Veo en el código fuente que la interfaz para la asignación en el núcleo solo existe desde el núcleo 3.1, ¿es quizás un error con la interfaz anterior que requiere ellosetup utilidad realice la búsqueda?
Gilles 'SO- deja de ser malvado'
Oh, parece estar solucionado en versiones más recientes. util-linux-2.26.2 parece funcionar, util-linux-2.24.1 imprime repetidamente/dev/loop14 y tal y el dispositivo puede faltar por completo al final. Tal vez la solución es cortesía /dev/loop-control, solía mirar /proc/partitions...
frostschutz
@frostschutz Desde util-linux 2.21, losetupusa /dev/loop-controlsi está presente, y no parece que pueda tener una condición de carrera: la asignación ocurre en el kernel e imprimir la ruta del dispositivo es lo último que hace la utilidad.
Gilles 'SO- deja de ser malvado'
6

Me lo imaginé. Si bien no estoy seguro de cómo es el problema con el permiso, puedo disparar primero y preguntar más tarde de esta manera:

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

Podrías usar flock:

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

La idea aquí es que intentes y flock el archivo del dispositivo de bucle; si otra instancia del mismo script lo adquiere primero, llamará losetup $ld myfile.imgy flockdevolverá 0. Para el script que pierde la carrera, losetupno se llamará y flockdevolverá 1, haciendo que el ciclo se repita.

Para más ver man flock.

encerrada dorada
fuente
0

Si todo lo que desea hacer con la imagen como dispositivo de bucle invertido es montarla como un sistema de archivos y trabajar con los contenidos, el mountcomando puede encargarse de esto automáticamente.

mount -o loop myfile.img /tmp/mountpoint
Aleatorio832
fuente
En realidad, en mi caso, en particular quiero que no esté montado, lo estoy usando como un dispositivo de bloque, no como un sistema de archivos.
AJMansfield
@AJMansfield Aparte de montarlo, ¿qué puede hacer con un dispositivo de bloque que no puede hacer con un archivo?
Random832
No puede formatearlo como espacio de intercambio en una configuración btrfs de disco completo. Tenía la intención de usar el archivo para el espacio de intercambio, ya que btrfs no admite las operaciones necesarias para los archivos de intercambio, y no puedo tener una partición de intercambio real con una configuración de btrfs de disco completo.
AJMansfield