EPERM ao formatar dispositivo de bloco em um sistema de arquivos FUSE

EPERM ao formatar dispositivo de bloco em um sistema de arquivos FUSE

DR

A tentativa de formatar um dispositivo de bloco em meu sistema de arquivos FUSE falha no EPERMsyscall open. As permissões estão definidas para777e os s necessários ioctlsão stubados, mas nenhum log é impresso no manipulador FUSE.

Fundo

Estou escrevendo um programa para criar imagens de disco virtual. Um dos meus critérios é que ele possa ser executado sem acesso de superusuário, o que significa que não posso montar dispositivos de loopback, alterar proprietários de arquivos ou até mesmo editar/etc/fuse.conf. Por esta razão, a minha abordagem acaba por ser bastante prolixa. Especificamente, para formatar as diversas partições do disco, gostaria de poder usar as ferramentas do sistema, porque isso me dá uma gama muito maior de sistemas de arquivos possíveis. Isso envolve expor as diversas partições do VDisk como dispositivos de bloco para o sistema. No entanto, todos os métodos possíveis que encontrei exigem nbddispositivos s ou loopback. Ambos requerem acesso de superusuário.

Implementando o FUSE sozinho

No entanto, a implementação de dispositivos de bloco no FUSE não é apenas possível, mas também suportada. Infelizmente não consegui encontrar muita documentação sobre o assunto e como estou fazendo tudo isso em Rust, o mundo da documentação para isso é ainda mais escasso.

Implementei os seguintes métodos FUSE:

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

Posso listar o conteúdo do sistema de arquivos e obter informações de diretório/arquivo. Estou ignorando deliberadamente os métodos que criam ou modificam recursos, pois isso é feito através do processo de construção.

O erro

Como mencionei, eu recebopermissão negada( EPERM) erro. straceA mkfschamada mostra que é a openchamada para o dispositivo de bloco que falha no lado do 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 maior clareza, minha estrutura de diretórios é semelhante a esta:

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]

Claro, existem funções de registro que rastreiam cada método. Vejo logs ao listar as partições, mas não durante a formatação.

Como mencionei, encontrei muito pouca documentação sobre o que realmente poderia estar causando esse erro.

Mais informações

Graças aos insights de @orenkihon, encontrei mais alguns detalhes que me deixam perplexo.

  1. Encontrei algumas opções fuserinteressantes:

    • MountOption::Dev Habilitar caracteres especiais e bloquear dispositivos
    • MountOption::DefaultPermission Habilite a verificação de permissão no kernel
    • MountOption::RW Sistema de arquivos de leitura e gravação(aparentemente não é uma opção padrão)

    Infelizmente, nenhuma combinação disso resolveu meu problema.

  2. As funções de log não são chamadas imediatamente. Eles parecem estar ligados a algum tipo de operação de descarga. Posso executar o mkfs.fatcomando, ver um ou dois logs, voltar para meu IDE e ver uma página com logs aparecer.

    Isso pode ser devido ao fato de o diretório que estou gerando os arquivos estar dentro do diretório do projeto, portanto é visível para o IDE, mas me parece muito incomum.

  3. O log na accessfunção nunca é visível, mas na statfsfunção é, mas somente se mkfsfor chamado de fora do outdiretórioeé a primeira de todas mkfsas chamadas.

    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. Estou vendo esta mensagem de log pela primeira vez hoje:
[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

Existe um MountOption::Devsepcified, que supostamente adiciona suporte para dispositivos de blocos e caracteres. No entanto, não consigo explicar por que está sendo rejeitado. Eu estava esperançoso de poder usar uma versão corrigida, libfuse3mas parece que não.

Informações extras que podem ser úteis

Especificações do sistema

Copiado diretamente das Informações do Sistema do KDE

  • Sistema operacional: Kubuntu 23.10
  • Versão do KDE Plasma: 5.27.8
  • Versão das estruturas do KDE: 5.110.0
  • Versão Qt: 5.15.10
  • Versão do kernel: 6.5.0-28 genérico (64 bits)
  • Plataforma gráfica: Wayland
  • Processadores: Intel® Core™ i9-13900 de 32 × 13ª geração
  • Memória: 31,1 GiB de RAM
  • Processador gráfico: AMD Radeon RX 7900 XT
  • Fabricante: ASUS

As operações de escrita genérica também falham

Uma sugestão foi verificar se mkfsfalha caso fat32não suporte o dispositivo de bloco. No entanto, este não parece ser o caso, pois a formatação com qualquer outro sistema de arquivos produz os mesmos resultados.

Também estou usando mkfscomo plataforma de teste porque atualmente não conheço nenhum outro utilitário de sistema disponível que grave diretamente para bloquear dispositivos e mkfsé algo que pretendo usar de qualquer maneira.

Más notícias :(

Ao ler as páginas de manual, me depareieste parágrafoque fez meu coração afundar:

A maioria das opções genéricas de montagem descritas em mount são suportadas (ro, rw, suid, nosuid, dev, nodev, exec, noexec, atime, noatime, sync, async, dirsync). Os sistemas de arquivos são montados com nodev,nosuid por padrão, que só pode ser substituído por um usuário privilegiado.

Então parece que isso não é possível. Ainda assim, quaisquer insights aqui - qualquer esperança menor - seriam muito apreciados.

Responder1

Se bem entendi, você expõe um dispositivo de bloco por meio do sistema de arquivos FUSE, que não funciona por motivos de segurança.

Em sistemas do tipo Unix, os arquivos são muito mais do que apenas arquivos. Permitir que um usuário sem privilégios crie dispositivos de bloco arbitrários é problemático porque, ao criar um dispositivo de bloco correspondente ao dispositivo rootfs, o sistema pode ser comprometido.

Você pode querer dar uma olhada no que o programa fakerootfaz. Ele conecta todas libcas funções usadas pelo programa que você executa nele. Sempre que é realizada uma operação que requer rootacesso (como criar um dispositivo, por exemplo), a operação é registrada. Mais tarde fakerootirá falsificar a existência do arquivo proibido.

Como você deseja apenas expor as partições, você pode adotar uma abordagem semelhante e falsificar alguns dos ioctlarquivos em seu dispositivo falso, quando na realidade é apenas um arquivo normal. Não tenho certeza se isso ajudará, mas é bom ter isso em mente.

informação relacionada