fuse - файловая система в пространстве пользователя (FUSE)
СИНТАКСИС
#include <linux/fuse.h>
ОПИСАНИЕ
Это устройство является основным интерфейсом между драйвером файловой системы FUSE и пользовательским процессом, который хочет предоставить файловую систему (в дальнейшем в этом руководстве этот процесс называется «демон файловой системы»). Эта страница руководства предназначена для тех, кто заинтересован в понимании самого интерфейса ядра. Разработчикам файловой системы FUSE может быть полезно использовать библиотеку в пространстве пользователя, такую как libfuse, которая абстрагирует низкоуровневый интерфейс.
По своей сути, FUSE — это простая клиент-серверная архитектура, в которой ядро Linux является клиентом, а демон — сервером. Получив файловый дескриптор этого устройства, демон может читать запросы из этого файлового дескриптора и отправлять ответы, записывая их в этот дескриптор. Важно отметить, что файловый дескриптор связан с уникальной файловой системой FUSE. В частности, открытие второй копии этого устройства не позволит получить доступ к ресурсам, созданным через первый файловый дескриптор (и наоборот).
Базовый протокол
Каждое сообщение, которое читает демон, начинается с заголовка, описываемого следующей структурой:
struct fuse_in_header {
uint32_t len; /* Общий размер данных,
включая этот заголовок */
uint32_t opcode; /* Тип операции (см. ниже) */
uint64_t unique; /* Уникальный идентификатор для этого запроса */
uint64_t nodeid; /* Идентификатор объекта файловой системы, с которым выполняется операция */
uint32_t uid; /* UID запрашивающего процесса */
uint32_t gid; /* GID запрашивающего процесса */
uint32_t pid; /* PID запрашивающего процесса */
uint32_t padding;
};
За заголовком следует переменная часть данных (которая может быть пустой), специфичная для запрошенной операции (запрошенная операция указывается кодом операции).
Затем демон должен обработать запрос и — при необходимости — отправить ответ, выполнив запись в файловый дескриптор. Почти все операции требуют ответа; если это не так, это указано в документации. Все ответы должны начинаться со следующего заголовка:
struct fuse_out_header {
uint32_t len; /* Общий размер данных, записанных в файловый дескриптор */
int32_t error; /* Любая возникшая ошибка (0, если ошибок нет) */
uint64_t unique; /* Значение из
соответствующего запроса */
};
За этим заголовком также следуют (потенциально пустые) переменные данные, в зависимости от выполненного запроса. Однако, если ответ является ответом об ошибке (то есть установлено значение error), то дополнительные данные не должны отправляться, независимо от запроса.
Обмен сообщениями
В этом разделе должна содержаться документация для каждого сообщения в протоколе. Эта страница руководства в настоящее время неполна, поэтому не все сообщения документированы. Для каждого сообщения сначала приводится структура, отправляемая ядром, а затем — описание семантики сообщения.
FUSE_INIT
struct fuse_init_in {
uint32_t major;
uint32_t minor;
uint32_t max_readahead; /* Начиная с версии протокола 7.6 */
uint32_t flags; /* Начиная с версии протокола 7.6 */
};
Это первый запрос, отправляемый ядром демону. Он используется для согласования версии протокола и других параметров файловой системы. Обратите внимание, что версия протокола может влиять на структуру любого элемента в протоколе (включая эту структуру). Таким образом, демон должен запоминать согласованную версию и флаги для каждой сессии. На момент написания этой страницы руководства самая высокая поддерживаемая версия протокола ядра — 7.26.
Пользователи должны знать, что описания в этой странице руководства могут быть неполными или неточными для более старых или новых версий протокола.
Ответ на этот запрос имеет следующий формат:
struct fuse_init_out {
uint32_t major;
uint32_t minor;
uint32_t max_readahead; /* Начиная с версии 7.6 */
uint32_t flags; /* Начиная с версии 7.6; некоторые биты флагов были введены позже */
uint16_t max_background; /* Начиная с версии 7.13 */
uint16_t congestion_threshold; /* Начиная с версии 7.13 */
uint32_t max_write; /* Начиная с версии 7.5 */
uint32_t time_gran; /* Начиная с версии 7.6 */
uint32_t unused[9];
};
Если основная версия, поддерживаемая ядром, больше, чем поддерживаемая демоном, ответ должен состоять только из uint32_t major (после обычной шапки), что указывает на самую большую основную версию, поддерживаемую демоном. Затем ядро отправит новый запрос FUSE_INIT, соответствующий более старой версии. В обратном случае демон должен тихо вернуться к основной версии ядра.
Согласованная вспомогательная версия считается минимальной из вспомогательных версий, предоставляемых демоном и ядром, и обе стороны должны использовать протокол, соответствующий этой вспомогательной версии.
FUSE_GETATTR
struct fuse_getattr_in {
uint32_t getattr_flags;
uint32_t dummy;
uint64_t fh; /* Устанавливается только если
(getattr_flags & FUSE_GETATTR_FH)
};
Запрошенная операция — вычисление атрибутов, которые должны быть возвращены функцией [stat]({filename}../../stat)(2) и аналогичными операциями для данного объекта файловой системы. Объект, для которого должны быть вычислены атрибуты, указывается либо с помощью header->nodeid, либо, если установлен флаг FUSE_GETATTR_FH, с помощью дескриптора файла fh. Последний случай операции аналогичен fstat(2).
В целях повышения производительности эти атрибуты могут быть кэшированы в ядре в течение определенного периода времени. Пока не истечет время кэширования, атрибуты будут извлекаться из кэша, и дополнительные запросы FUSE_GETATTR не будут выполняться.
Вычисленные атрибуты и запрошенный тайм-аут кэша должны быть возвращены в следующей структуре:
struct fuse_attr_out {
/* Длительность кэширования атрибутов (в секундах и наносекундах) */
uint64_t attr_valid;
uint32_t attr_valid_nsec;
uint32_t dummy;
struct fuse_attr {
uint64_t ino;
uint64_t size;
uint64_t blocks;
uint64_t atime;
uint64_t mtime;
uint64_t ctime;
uint32_t atimensec;
uint32_t mtimensec;
uint32_t ctimensec;
uint32_t mode;
uint32_t nlink;
uint32_t uid;
uint32_t gid;
uint32_t rdev;
uint32_t blksize;
uint32_t padding;
} attr;
};
FUSE_ACCESS
struct fuse_access_in {
uint32_t mask;
uint32_t padding;
};
Если не используется опция монтирования default_permissions, этот запрос может использоваться для проверки разрешений. Данные ответа не требуются, но ошибки могут быть указаны обычным способом путем установки поля error в заголовке ответа (в частности, ошибки отказа в доступе могут быть указаны путем возврата -EACCES).
FUSE_OPEN
FUSE_OPENDIR
struct fuse_open_in {
uint32_t flags; /* Флаги, переданные в open(2) */
uint32_t unused;
};
Запрошенная операция — открыть узел, указанный в header->nodeid. Точная семантика этого действия будет зависеть от реализуемой файловой системы. Однако, по крайней мере, файловая система должна проверить, являются ли запрошенные флаги допустимыми для указанного ресурса, а затем отправить ответ в следующем формате:
struct fuse_open_out {
uint64_t fh;
uint32_t open_flags;
uint32_t padding;
};
Поле fh — непрозрачный идентификатор, который ядро будет использовать для ссылки на этот ресурс. Поле open_flags — битовая маска, указывающая свойства этого дескриптора файла для ядра:
FOPEN_DIRECT_IO Обходить кэш страниц для этого открытого файла.
FOPEN_KEEP_CACHE Не инвалидировать кэш данных при открытии.
FOPEN_NONSEEKABLE Файл нельзя перемещать.
FUSE_READ
FUSE_READDIR
struct fuse_read_in {
uint64_t fh;
uint64_t offset;
uint32_t size;
uint32_t read_flags;
uint64_t lock_owner;
uint32_t flags;
uint32_t padding;
};
Запрошенное действие — прочитать до size байт файла или каталога, начиная с offset. Байты должны быть возвращены непосредственно после обычного заголовка ответа.
FUSE_INTERRUPT
struct fuse_interrupt_in {
uint64_t unique;
};
Запрошенное действие — отменить ожидающую операцию, указанную в unique. Этот запрос не требует ответа. Однако, получение этого сообщения само по себе не отменяет указанную операцию. Ядро по-прежнему будет ожидать ответа на эту операцию (например, ошибку EINTR или короткое чтение). Для данной операции может быть выдано не более одного запроса FUSE_INTERRUPT. После выдачи этого запроса ядро будет ждать завершения указанного запроса, не допуская прерываний.
FUSE_LOOKUP
Сразу после заголовка следует имя файла, которое необходимо найти в каталоге, указанном в header->nodeid. Ожидаемый ответ имеет следующий вид:
struct fuse_entry_out {
uint64_t nodeid; /* Идентификатор узла */
uint64_t generation; /* Поколение узла */
uint64_t entry_valid;
uint64_t attr_valid;
uint32_t entry_valid_nsec;
uint32_t attr_valid_nsec;
struct fuse_attr attr;
};
Комбинация nodeid и generation должна быть уникальной в течение всего времени существования файловой системы.
Интерпретация тайм-аутов и attr аналогична FUSE_GETATTR.
FUSE_FLUSH
struct fuse_flush_in {
uint64_t fh;
uint32_t unused;
uint32_t padding;
uint64_t lock_owner;
};
Запрашиваемое действие — сбросить все ожидающие изменения для указанного дескриптора файла. Ожидаемые данные ответа отсутствуют. Однако пустой ответ все равно должен быть отправлен после завершения операции сброса.
FUSE_RELEASE
FUSE_RELEASEDIR
struct fuse_release_in {
uint64_t fh;
uint32_t flags;
uint32_t release_flags;
uint64_t lock_owner;
};
Это обратные операции к FUSE_OPEN и FUSE_OPENDIR соответственно. Демон теперь может освободить любые ресурсы, связанные с дескриптором файла fh, поскольку ядро больше не будет ссылаться на него. Нет данных ответа, связанных с этим запросом, но ответ все равно должен быть отправлен после полного завершения запроса.
FUSE_STATFS
Эта операция реализует statfs(2) для этой файловой системы. Нет входных данных, связанных с этим запросом. Ожидаемые данные ответа имеют следующую структуру:
struct fuse_kstatfs {
uint64_t blocks;
uint64_t bfree;
uint64_t bavail;
uint64_t files;
uint64_t ffree;
uint32_t bsize;
uint32_t namelen;
uint32_t frsize;
uint32_t padding;
uint32_t spare[6];
};
struct fuse_statfs_out {
struct fuse_kstatfs st;
};
Для интерпретации этих полей см. statfs(2).
ОШИБКИ
E2BIG Возвращается из операций read(2), когда запрос ядра слишком велик для предоставленного буфера, и запрос был FUSE_SETXATTR.
EINVAL Возвращается из write(2), если проверка ответа не удалась. Не все ошибки в ответах будут обнаружены этой проверкой. Однако основные ошибки, такие как короткие ответы или неправильное уникальное значение, обнаруживаются.
EIO Возвращается из операций read(2), когда запрос ядра слишком велик для предоставленного буфера.
Примечание: существует множество способов, которыми неправильное использование этих интерфейсов может привести к тому, что операции с файлами и каталогами в предоставленной файловой системе завершатся с ошибкой EIO. Среди возможных неправильных способов использования:
изменение mode и S_IFMT для inode, который ранее был передан ядру; или
отправка ядру ответов, которые меньше, чем ожидалось ядром.
ENODEV Возвращается из операций read(2) и write(2), если файловая система FUSE была отмонтирована.
EPERM Возвращается из операций с файловым дескриптором /dev/fuse, который не был смонтирован.
СТАНДАРТЫ
Linux.
ЗАМЕЧАНИЯ
Следующие сообщения еще не задокументированы в этой странице руководства:
FUSE_BATCH_FORGET
FUSE_BMAP
FUSE_CREATE
FUSE_DESTROY
FUSE_FALLOCATE
FUSE_FORGET
FUSE_FSYNC
FUSE_FSYNCDIR
FUSE_GETLK
FUSE_GETXATTR
FUSE_IOCTL
FUSE_LINK
FUSE_LISTXATTR
FUSE_LSEEK
FUSE_MKDIR
FUSE_MKNOD
FUSE_NOTIFY_REPLY
FUSE_POLL
FUSE_READDIRPLUS
FUSE_READLINK
FUSE_REMOVEXATTR
FUSE_RENAME
FUSE_RENAME2
FUSE_RMDIR
FUSE_SETATTR
FUSE_SETLK
FUSE_SETLKW
FUSE_SYMLINK
FUSE_UNLINK
FUSE_WRITE
ССЫЛКИ
fusermount(1), mount.fuse(8)