
TL;DR
El intento de formatear un dispositivo de bloque en mi sistema de archivos FUSE falla EPERM
en la open
llamada al sistema. Los permisos están establecidos en777y los mensajes de correo electrónico necesarios ioctl
se 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 nbd
dispositivos 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. strace
Realizar la mkfs
llamada muestra que es la open
llamada al dispositivo de bloque la que falla en el lado del kernel.strace
Resultado 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.
Encontré algunas opciones en
fuser
las que eran interesantes:MountOption::Dev
Habilitar caracteres especiales y dispositivos de bloqueoMountOption::DefaultPermission
Habilitar la verificación de permisos en el kernelMountOption::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.
Las funciones de registro no se llaman inmediatamente. Parecen estar vinculados a algún tipo de operación de lavado. Puedo ejecutar el
mkfs.fat
comando, 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.
El registro en la
access
función nunca es visible, pero en lastatfs
función sí, pero solo simkfs
se llama desde fuera delout
directorio.yes la primera de todasmkfs
las 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
- 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::Dev
separado, 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, libfuse3
pero 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 mkfs
falla en caso de que fat32
no 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 mkfs
como plataforma de prueba porque actualmente no conozco ninguna otra utilidad del sistema disponible que escriba para bloquear dispositivos directamente, y mkfs
es 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 fakeroot
hace el programa. Enlaza todas libc
las funciones utilizadas por el programa que ejecuta. Cada vez que root
se realiza una operación que requiere acceso (como crear un dispositivo, por ejemplo), la operación se registra. Posteriormente fakeroot
fingirá la existencia del archivo prohibido.
Como solo desea exponer las particiones, puede adoptar un enfoque similar y falsificar algunos de los ioctl
correos 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.