FUSE(4)

ИМЯ
fuse - устройство файловой системы в пользовательском пространстве (FUSE)

ОБЗОР
#include <linux/fuse.h>

ОПИСАНИЕ

Это устройство является основным интерфейсом между драйвером файловой системы FUSE и процессом в пользовательском пространстве, который будет предоставлять файловую систему (далее здесь называется службой файловой системы (filesystem daemon)). Данная справочная страница предназначена тому, кто заинтересован в понимании интерфейса самого ядра. Тем, кто реализует файловую систему FUSE, важнее описание библиотеки пользовательского пространства такой как libfuse, которая скрывает низкоуровневый интерфейс.

В основе FUSE лежит простой клиент-серверный протокол, в котором ядро Linux является клиентом, а служба — сервером. После получения файлового дескриптора этого устройства, служба может читать запросы с помощью read(2) из этого файлового дескриптора и писать ответы с помощью write(2). Важно отметить, что данный файловый дескриптор, связанный с файловой системой FUSE, является индивидуальным. В частности, открытие второй копии этого устройства не даст доступ к ресурсам, созданным через первый файловый дескриптор (и наоборот).

  Основной протокол
Каждое сообщение, читаемое службой, начинается с заголовка следующей структуры:

    struct fuse_in_header {
        uint32_t len;       /* общая длина данных,
                               включая этот заголовок */
        uint32_t opcode;    /* тип операции (смотрите ниже) */
        uint64_t unique;    /* уникальный идентификатор этого запроса */
        uint64_t nodeid;    /* ID объекта файловой системы, с
                               которым выполняется действие */
        uint32_t uid;       /* UID запрашивающего процесса */
        uint32_t gid;       /* GID запрашивающего процесса */
        uint32_t pid;       /* PID запрашивающего процесса */
        uint32_t padding;
    };

После заголовка располагаются данные произвольной длины (их может и не быть), используемые вместе с запрашиваемой операцией (запрашиваемая операция указывается в opcode).

Служба обрабатывает запрос и, если нужен ответ (почти все операции требуют ответа; если нет, то это описано далее), выполняет запись с помощью write(2) в файловый дескриптор. Все ответы должны начинаться со следующего заголовка:

    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; /* начиная с протокола v7.6 */
            uint32_t flags;         /* начиная с протокола v7.6 */
        };

    Это первый запрос, посылаемый ядром службе. Он используется для согласования версии протокола и других параметров файловой системы. Заметим, что версия протокола может влиять на любую структуру протокола (включая эту). Служба должна запоминать согласованную версию и флаги для каждого сеанса. На момент написания этой справочной страницы наибольшей версией поддерживаемого ядром протокола была 7.26.

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

    Ответ на этот запрос имеет следующий формат:

        struct fuse_init_out {
            uint32_t major;
            uint32_t minor;
            uint32_t max_readahead;   /* Начиная с v7.6 */
            uint32_t flags;           /* Начиная с v7.6; некоторые биты флагов были введены позднее */
            uint16_t max_background;  /* Начиная с v7.13 */
            uint16_t congestion_threshold;  /* Начиная с v7.13 */
            uint32_t max_write;       /* Начиная с v7.5 */
            uint32_t time_gran;       /* Начиная с v7.6 */
            uint32_t unused[9];
        };

    Если главная (major) версия, поддерживаемая ядром, больше чем поддерживаемая службой, то ответ должен содержать только uint32_t major (как обычный заголовок), показывающий наибольшую главную версию, поддерживаемую службой. После этого ядро создаст новый запрос FUSE_INIT, соответствующий более старой версии. В противном случае (то есть, если версия службы новее), служба должна переключиться на работу с главной версией ядра.

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

FUSE_GETATTR

        struct fuse_getattr_in {
            uint32_t getattr_flags;
            uint32_t dummy;
            uint64_t fh;      /* задаётся, только если
                                 (getattr_flags & FUSE_GETATTR_FH)
        };

    Операция запроса вычисления атрибутов, возвращаемых stat(2) и подобными операциями для указанного объекта файловой системы. Объект, для которого вычисляются атрибуты, задаётся в виде header->nodeid или, если указан флаг FUSE_GETATTR_FH, в виде файлового описателя (handle) 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. Точна семантика будет зависеть от реализации в файловой системе. Однако, по крайней мере, файловая система должна проверить что запрашиваемые flags корректны для указанного ресурса и затем послать ответ в следующем формате:

        struct fuse_open_out {
            uint64_t fh;
            uint32_t open_flags;
            uint32_t padding;
        };

    Поле fh представляет собой закрытый (opaque) идентификатор, который ядро будет использовать для ссылки на этот ресурс. Поле 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;            /* идентификатор inode */
            uint64_t generation;        /* поколение inode */
            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;
        };

    По этому запросу сбрасываются (flush) все ожидающие изменения для указанного файлового описателя. Данных в ответе не ожидается. Однако, всё равно требуется ответное сообщения после завершения операции сброса.

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), если ответ не прошёл тест на правильность. При этом не все ошибки в ответах будут пойманы. Однако обнаруживаются простые ошибки, такие как короткие ответы или некорректное значение unique.

EIO Возвращается из операции read(2), когда запросы ядра слишком велики для предоставляемого буфера.

    Замечание: Есть несколько вариантов, в которых неправильное использование этих интерфейсов с предоставляемыми файловой системой файлами и каталогами может завершиться ошибкой EIO. Например:

    *   изменение mode & S_IFMT для иноды, о которой ранее было сообщено ядру; или

    *   выдача более коротких ответов ядру, чем оно ожидает.

ENODEV
    Возвращается из операций read(2) и write(2), если файловая система FUSE была размонтирована.

EPERM
    Возвращается из операций над файловым дескриптором /dev/fuse, если он не был смонтирован.

СООТВЕТСТВИЕ СТАНДАРТАМ
Файловая система 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)