EPERM при форматировании блочного устройства в файловой системе FUSE

EPERM при форматировании блочного устройства в файловой системе FUSE

TL;DR

Попытка форматирования блочного устройства в моей файловой системе FUSE завершается ошибкой EPERMпри openсистемном вызове. Разрешения установлены на777и необходимые ioctls заглушены, но журналы не выводятся из обработчика FUSE.

Фон

Я пишу программу для создания виртуальных образов дисков. Одним из моих критериев является то, что она должна работать с нулевым доступом суперпользователя, то есть я не могу монтировать устройства loopback, менять владельцев файлов или даже редактировать/etc/fuse.conf. По этой причине мой подход оказывается довольно длинным. В частности, для форматирования различных разделов на диске я хотел бы иметь возможность использовать системные инструменты, поскольку это дает мне гораздо больший диапазон возможных файловых систем. Это подразумевает предоставление различных разделов на VDisk в качестве блочных устройств для системы. Однако все возможные методы, которые я нашел, требовали либо nbds, либо устройств loopback. Оба из которых требуют доступа суперпользователя.

Реализую FUSE самостоятельно

Однако реализация блочных устройств в FUSE не только возможна, но и поддерживается. К сожалению, мне не удалось найти много документации по этому вопросу, а поскольку я делаю все это в Rust, документация по этому вопросу еще более скудна.

Я реализовал следующие методы FUSE:

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

Я могу вывести список содержимого файловой системы и получить информацию о каталогах/файлах. Я намеренно игнорирую методы, которые создают или изменяют ресурсы, поскольку это делается в процессе сборки.

Ошибка

Как уже упоминалось, я получаюдоступ запрещен( EPERM) ошибка. straceПовторный mkfsвызов показывает, что openна стороне ядра происходит сбой вызова блочного устройства.Полный straceрезультат.

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)                           = ?

Для ясности моя структура каталогов выглядит так:

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]

Конечно, есть функции журналирования, отслеживающие каждый метод. Я вижу журналы при перечислении разделов, но не при форматировании.

Как я уже упоминал, я нашел очень мало документации о том, что на самом деле может быть причиной этой ошибки.

Дальнейшие идеи

Благодаря информации от @orenkishon я обнаружил еще несколько деталей, которые меня просто озадачивают.

  1. Я нашел несколько fuserинтересных вариантов:

    • MountOption::Dev Включить специальные символы и заблокировать устройства
    • MountOption::DefaultPermission Включить проверку разрешений в ядре
    • MountOption::RW Файловая система чтения-записи(видимо, это не опция по умолчанию)

    К сожалению, ни одно из этих решений не решило мою проблему.

  2. Функции журнала не вызываются немедленно. Похоже, они привязаны к какой-то операции очистки. Я могу запустить команду mkfs.fat, увидеть один или два журнала, переключиться обратно в IDE и увидеть, как появляется страница журналов.

    Это может быть связано с тем, что каталог, в котором я генерирую файлы, находится в каталоге проекта, поэтому он виден IDE, но мне это кажется очень необычным.

  3. Журнал в accessфункции никогда не виден, но в statfsфункции виден, но только если mkfsвызывается извне outкаталогаиэто первый из всех mkfsзвонков.

    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. Сегодня я впервые вижу это сообщение в журнале:
[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

Есть MountOption::Devsepcified, который якобы добавляет поддержку блочных и символьных устройств. Однако я не могу объяснить, почему его отклоняют. Я надеялся, что смогу использовать пропатченную версию, libfuse3но, похоже, нет.

Дополнительная информация, которая может быть полезна

Характеристики системы

Скопировано непосредственно из системной информации KDE

  • Операционная система: Kubuntu 23.10
  • Версия KDE Plasma: 5.27.8
  • Версия фреймворков KDE: 5.110.0
  • Версия Qt: 5.15.10
  • Версия ядра: 6.5.0-28-generic (64-бит)
  • Графическая платформа: Wayland
  • Процессоры: 32 × Intel® Core™ i9-13900 13-го поколения
  • Память: 31,1 ГиБ ОЗУ
  • Графический процессор: AMD Radeon RX 7900 XT
  • Производитель: ASUS

Операции общей записи также не работают

Было предложено проверить, mkfsне срабатывает ли в случае, если fat32не поддерживает блочное устройство. Однако, похоже, это не так, поскольку форматирование с помощью любой другой файловой системы дает те же результаты.

Я также использую его mkfsв качестве платформы для тестирования, поскольку в настоящее время не знаю других легкодоступных системных утилит, которые напрямую записывают данные на блочные устройства, а mkfsя в любом случае собираюсь использовать именно их.

Плохие новости :(

Читая man-страницы, я наткнулся наэтот абзацчто заставило мое сердце замереть:

Поддерживается большинство общих параметров монтирования, описанных в mount (ro, rw, suid, nosuid, dev, nodev, exec, noexec, atime, noatime, sync, async, dirsync). Файловые системы монтируются с nodev,nosuid по умолчанию, что может быть переопределено только привилегированным пользователем.

Так что, похоже, это невозможно. Тем не менее, любые идеи здесь — любая слабая надежда — были бы весьма признательны.

решение1

Если я правильно понимаю, вы раскрываете блочное устройство через файловую систему FUSE, что не работает по соображениям безопасности.

В Unix-подобных системах файлы — это гораздо больше, чем просто файлы. Позволить непривилегированному пользователю создавать произвольные блочные устройства проблематично, поскольку, создав блочное устройство, соответствующее устройству rootfs, можно скомпрометировать систему.

Вы можете захотеть взглянуть на то, что fakerootделает программа. Она перехватывает каждую libcфункцию, используемую программой, которую вы запускаете под ней. Всякий раз, когда rootвыполняется операция, требующая доступа (например, создание устройства), операция записывается. Позже fakerootбудет подделывать существование запрещенного файла.

Поскольку вы просто хотите раскрыть разделы, вы можете использовать аналогичный подход и подделать некоторые из ioctls на вашем поддельном устройстве, в то время как на самом деле это просто обычный файл. Я не уверен, что это поможет, но хорошо иметь это в виду.

Связанный контент