EPERM al formatear un dispositivo de bloque en un sistema de archivos FUSE

EPERM al formatear un dispositivo de bloque en un sistema de archivos FUSE

TL;DR

El intento de formatear un dispositivo de bloque en mi sistema de archivos FUSE falla EPERMen la openllamada al sistema. Los permisos están establecidos en777y los mensajes de correo electrónico necesarios ioctlse eliminan, pero no se imprimen registros desde el controlador FUSE.

Fondo

Estoy escribiendo un programa para crear imágenes de discos virtuales. Uno de mis criterios es que debe poder ejecutarse sin acceso de superusuario, lo que significa que no puedo montar dispositivos loopback, cambiar propietarios de archivos o incluso editar./etc/fuse.conf. Por esta razón, mi enfoque resulta bastante prolijo. Específicamente, para formatear las distintas particiones del disco, me gustaría poder utilizar herramientas del sistema, porque eso me brinda una gama mucho mayor de sistemas de archivos posibles. Esto implica exponer las distintas particiones del disco virtual como dispositivos de bloque al sistema. Sin embargo, todos los métodos posibles que he encontrado han requerido nbddispositivos s o loopback. Ambos requieren acceso de superusuario.

Implementando FUSE yo mismo

Sin embargo, implementar dispositivos de bloque en FUSE no sólo es posible, sino que también es compatible. Desafortunadamente, no pude encontrar mucha documentación sobre el tema y como estoy haciendo todo esto en Rust, el mundo de la documentación para esto es aún más escaso.

He implementado los siguientes métodos FUSE:

  • init
  • lookup
  • getattr
  • open
  • read
  • write
  • readdir
  • ioctl
    • BLKGETSIZE
    • BLKFLSBUF
    • BLKSSZGET

Puedo enumerar el contenido del sistema de archivos y obtener información de directorio/archivo. Estoy ignorando deliberadamente los métodos que crean o modifican recursos, ya que esto se hace a través del proceso de compilación.

El error

Como mencioné, obtengoPermiso denegado( EPERM) error. straceRealizar la mkfsllamada muestra que es la openllamada al dispositivo de bloque la que falla en el lado del kernel.straceResultado completo.

execve("/usr/sbin/mkfs.fat", ["mkfs.fat", "out/partitions/EFI"], 0x7ffd42f64ab8 /* 76 vars */) = 0

    --- snip ---

openat(AT_FDCWD, "out/partitions/EFI", O_RDWR|O_EXCL) = -1 EACCES (Permission denied)
write(2, "mkfs.fat: unable to open out/par"..., 63mkfs.fat: unable to open out/partitions/EFI: Permission denied
) = 63
exit_group(1)                           = ?

Para mayor claridad, mi estructura de directorios se ve así:

out
├── minimal.qcow2 [raw disk image] (shadows minimal.qcow2 [qcow2 file] with qemu-storage-daemon)
├── partitions
│   ├── EFI [Block device]
│   └── System [Block device]
└── qemu-monitor.sock [UNIX domain socket]

Por supuesto, existen funciones de registro que rastrean cada método. Veo registros cuando enumero las particiones, pero no cuando formateo.

Como mencioné, encontré muy poca documentación sobre lo que realmente podría estar causando este error.

Más ideas

Gracias a las ideas de @orenkishon, encontré algunos detalles más que simplemente me desconciertan.

  1. Encontré algunas opciones en fuserlas que eran interesantes:

    • MountOption::Dev Habilitar caracteres especiales y dispositivos de bloqueo
    • MountOption::DefaultPermission Habilitar la verificación de permisos en el kernel
    • MountOption::RW Sistema de archivos de lectura y escritura(aparentemente no es una opción predeterminada)

    Desafortunadamente, ninguna combinación de las cuales resolvió mi problema.

  2. Las funciones de registro no se llaman inmediatamente. Parecen estar vinculados a algún tipo de operación de lavado. Puedo ejecutar el mkfs.fatcomando, ver uno o dos registros, volver a mi IDE y ver aparecer una página con registros.

    Esto puede deberse al hecho de que el directorio donde estoy generando los archivos está dentro del directorio del proyecto, por lo que es visible para el IDE, pero me parece muy inusual.

  3. El registro en la accessfunción nunca es visible, pero en la statfsfunción sí, pero solo si mkfsse llama desde fuera del outdirectorio.yes la primera de todas mkfslas llamadas.

    project > cd ./out
    project/out > mkfs.fat partitions/EFI
    mkfs.fat 4.2 (2021-01-31)
    mkfs.fat: unable to open partitions/EFI: Permission denied
    
    # No logs
    
    project > mkfs.fat out/partitions/EFI
    mkfs.fat 4.2 (2021-01-31)
    mkfs.fat: unable to open out/partitions/EFI: Permission denied
    
    # No logs
    project > cargo run ...
    project > mkfs.fat out/partitions
    mkfs.fat 4.2 (2021-01-31)
    mkfs.fat: unable to open out/partitions/EFI: Permission denied
    
    # Logs appear after switching to IDE
    
  1. Hoy veo este mensaje de registro por primera vez:
[2024-04-21T16:58:24Z DEBUG fuser::mnt::fuse_pure] fusermount: 
[2024-04-21T16:58:24Z DEBUG fuser::mnt::fuse_pure] fusermount: fusermount3: unsafe option dev ignored

Hay un MountOption::Devseparado, que supuestamente agrega soporte para dispositivos de bloques y caracteres. Sin embargo, parece que no puedo explicar por qué se rechaza. Tenía la esperanza de poder utilizar una versión parcheada, libfuse3pero parece que no.

Información adicional que puede ser útil.

Especificaciones del sistema

Copiado directamente de la Información del sistema de KDE

  • Sistema operativo: Kubuntu 23.10
  • Versión plasma de KDE: 5.27.8
  • Versión de marcos KDE: 5.110.0
  • Versión Qt: 5.15.10
  • Versión del kernel: 6.5.0-28-genérico (64 bits)
  • Plataforma gráfica: Wayland
  • Procesadores: 32 × Intel® Core™ i9-13900 de 13.ª generación
  • Memoria: 31,1 GiB de RAM
  • Procesador gráfico: AMD Radeon RX 7900 XT
  • Fabricante: ASUS

Las operaciones de escritura genéricas también fallan

Una sugerencia fue verificar si mkfsfalla en caso de que fat32no sea compatible con el dispositivo de bloqueo. Sin embargo, este no parece ser el caso, ya que formatear con cualquier otro sistema de archivos produce los mismos resultados.

También lo estoy usando mkfscomo plataforma de prueba porque actualmente no conozco ninguna otra utilidad del sistema disponible que escriba para bloquear dispositivos directamente, y mkfses algo que tengo intención de usar de todos modos.

Malas noticias :(

Mientras leía las páginas de manual me encontréeste párrafoque hizo que mi corazón se hundiera:

Se admiten la mayoría de las opciones de montaje genéricas descritas en mount (ro, rw, suid, nosuid, dev, nodev, exec, noexec, atime, noatime, sync, async, dirsync). Los sistemas de archivos se montan con nodev,nosuid de forma predeterminada, que solo puede ser anulado por un usuario privilegiado.

Entonces parece que esto no es posible. Aún así, cualquier idea aquí, cualquier mínima esperanza, sería muy apreciada.

Respuesta1

Si entiendo correctamente, expones un dispositivo de bloque a través de tu sistema de archivos FUSE, que no funciona por razones de seguridad.

En los sistemas tipo Unix, los archivos son mucho más que simples archivos. Permitir que un usuario sin privilegios cree dispositivos de bloque arbitrarios es problemático porque, al crear un dispositivo de bloque correspondiente al dispositivo rootfs, el sistema puede verse comprometido.

Quizás quieras echar un vistazo a lo que fakeroothace el programa. Enlaza todas libclas funciones utilizadas por el programa que ejecuta. Cada vez que rootse realiza una operación que requiere acceso (como crear un dispositivo, por ejemplo), la operación se registra. Posteriormente fakerootfingirá la existencia del archivo prohibido.

Como solo desea exponer las particiones, puede adoptar un enfoque similar y falsificar algunos de los ioctlcorreos electrónicos en su dispositivo falso, cuando en realidad es solo un archivo normal. No estoy seguro de que esto ayude, pero es bueno tenerlo en cuenta.

información relacionada