fuse - Périphérique Filesystem in Userspace (FUSE)
SYNOPSIS
#include <linux/fuse.h>
DESCRIPTION
Ce périphérique est l’interface principale entre le pilote de système de fichiers FUSE et un processus utilisateur qui souhaite fournir le système de fichiers (désigné dans le reste de cette page de manuel comme le démon de système de fichiers). Cette page de manuel est destinée à ceux qui souhaitent comprendre l’interface du noyau elle-même. Ceux qui implémentent un système de fichiers FUSE peuvent souhaiter utiliser une bibliothèque d’espace utilisateur telle que libfuse qui abstrait l’interface de bas niveau.
Au fond, FUSE est un simple protocole client-serveur, dans lequel le noyau Linux est le client et le démon est le serveur. Après avoir obtenu un descripteur de fichier pour ce périphérique, le démon peut lire les requêtes à partir de ce descripteur de fichier et doit écrire les réponses. Il est important de noter qu’un descripteur de fichier est associé à un système de fichiers FUSE unique. En particulier, l’ouverture d’une deuxième copie de ce périphérique ne permettra pas d’accéder aux ressources créées via le premier descripteur de fichier (et vice versa).
Le protocole de base
Chaque message lu par le démon commence par un en-tête décrit par la structure suivante :
struct fuse_in_header {
uint32_t len; /* Taille totale des données,
y compris cet en-tête */
uint32_t opcode; /* Le type d’opération (voir ci-dessous) */
uint64_t unique; /* Un identifiant unique pour cette requête */
uint64_t nodeid; /* ID de l’objet de système de fichiers
sur lequel l’opération est effectuée */
uint32_t uid; /* UID du processus demandeur */
uint32_t gid; /* GID du processus demandeur */
uint32_t pid; /* PID du processus demandeur */
uint32_t padding;
};
L’en-tête est suivi d’une partie de données de taille variable (qui peut être vide) spécifique à l’opération demandée (l’opération demandée est indiquée par opcode).
Le démon doit ensuite traiter la requête et, le cas échéant, envoyer une réponse en effectuant une écriture sur le descripteur de fichier. Presque toutes les opérations nécessitent une réponse ; si ce n’est pas le cas, cela est documenté ci-dessous.
Toutes les réponses doivent commencer par l’en-tête suivant :
struct fuse_out_header {
uint32_t len; /* Taille totale des données écrites dans
le descripteur de fichier */
int32_t error; /* Toute erreur qui s’est produite (0 si aucune) */
uint64_t unique; /* La valeur de la requête
correspondante */
};
Cet en-tête est également suivi de données de taille variable (potentiellement vides) en fonction de la requête exécutée. Cependant, si la réponse est une réponse d’erreur (c’est-à-dire que l’erreur est définie), aucune donnée supplémentaire ne doit être envoyée, quelle que soit la requête.
Messages échangés
Cette section doit contenir la documentation de chaque message du protocole. Cette page de manuel est actuellement incomplète, donc tous les messages ne sont pas documentés. Pour chaque message, la structure envoyée par le noyau est d'abord donnée, suivie d'une description de la sémantique du message.
FUSE_INIT
struct fuse_init_in {
uint32_t major;
uint32_t minor;
uint32_t max_readahead; /* Depuis la version du protocole v7.6 */
uint32_t flags; /* Depuis la version du protocole v7.6 */
};
Ceci est la première requête envoyée par le noyau au démon. Elle est utilisée pour négocier la version du protocole et d'autres paramètres du système de fichiers. Notez que la version du protocole peut affecter la disposition de toute structure dans le protocole (y compris cette structure). Le démon doit donc se souvenir de la version et des indicateurs négociés pour chaque session. Au moment de la rédaction de cette page de manuel, la version du protocole du noyau la plus élevée prise en charge est 7.26.
Les utilisateurs doivent être conscients que les descriptions de cette page de manuel peuvent être incomplètes ou incorrectes pour les versions de protocole plus anciennes ou plus récentes.
La réponse à cette requête a le format suivant :
struct fuse_init_out {
uint32_t major;
uint32_t minor;
uint32_t max_readahead; /* Depuis v7.6 */
uint32_t flags; /* Depuis v7.6 ; certains bits d’indicateur ont été introduits ultérieurement */
uint16_t max_background; /* Depuis v7.13 */
uint16_t congestion_threshold; /* Depuis v7.13 */
uint32_t max_write; /* Depuis v7.5 */
uint32_t time_gran; /* Depuis v7.6 */
uint32_t unused[9];
};
Si la version principale prise en charge par le noyau est supérieure à celle prise en charge par le démon, la réponse ne doit contenir que uint32_t major (en suivant l’en-tête habituel), indiquant la version principale la plus élevée prise en charge par le démon. Le noyau émettra alors une nouvelle requête FUSE_INIT conforme à la version la plus ancienne. Dans le cas inverse, le démon doit revenir silencieusement à la version principale du noyau.
La version secondaire négociée est considérée comme le minimum des versions secondaires fournies par le démon et par le noyau, et les deux parties doivent utiliser le protocole correspondant à cette version secondaire.
FUSE_GETATTR
struct fuse_getattr_in {
uint32_t getattr_flags;
uint32_t dummy;
uint64_t fh; /* Défini uniquement si
(getattr_flags & FUSE_GETATTR_FH)
};
L’opération demandée est de calculer les attributs à renvoyer par stat(2) et des opérations similaires pour l’objet de système de fichiers donné. L’objet pour lequel les attributs doivent être calculés est indiqué soit par header->nodeid, soit, si l’indicateur FUSE_GETATTR_FH est défini, par le descripteur de fichier fh. Ce dernier cas d’opération est analogue à fstat(2).
Pour des raisons de performances, ces attributs peuvent être mis en cache dans le noyau pendant une durée spécifiée. Tant que le délai d’expiration du cache n’est pas dépassé, les attributs seront extraits du cache et ne provoqueront pas de requêtes FUSE_GETATTR supplémentaires.
Les attributs calculés et la durée de validité du cache demandée doivent ensuite être renvoyés dans la structure suivante :
struct fuse_attr_out {
/* Durée de validité de l’attribut (secondes + nanosecondes) */
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;
};
Si l’option de montage default_permissions n’est pas utilisée, cette requête peut être utilisée pour la vérification des autorisations. Aucune donnée de réponse n’est attendue, mais des erreurs peuvent être indiquées comme d’habitude en définissant le champ d’erreur dans l’en-tête de la réponse (en particulier, les erreurs d’accès refusé peuvent être indiquées en renvoyant -EACCES).
FUSE_OPEN
FUSE_OPENDIR
struct fuse_open_in {
uint32_t flags; /* Les drapeaux qui ont été passés à la fonction open(2) */
uint32_t unused;
};
L’opération demandée consiste à ouvrir le nœud indiqué par header->nodeid. La sémantique exacte de ce que cela signifie dépendra du système de fichiers mis en œuvre. Cependant, au minimum, le système de fichiers doit valider que les drapeaux demandés sont valides pour la ressource indiquée, puis envoyer une réponse avec le format suivant :
struct fuse_open_out {
uint64_t fh;
uint32_t open_flags;
uint32_t padding;
};
Le champ fh est un identifiant opaque que le noyau utilisera pour faire référence à cette ressource. Le champ open_flags est un masque de bits de n’importe lequel des drapeaux qui indiquent les propriétés de ce descripteur de fichier pour le noyau :
FOPEN_DIRECT_IO Contourner le cache de pages pour ce fichier ouvert.
FOPEN_KEEP_CACHE Ne pas invalider le cache de données lors de l’ouverture.
FOPEN_NONSEEKABLE Le fichier n’est pas accessible en lecture/écriture.
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;
};
L’action demandée est de lire jusqu’à size octets du fichier ou du répertoire, en commençant à offset. Les octets doivent être renvoyés directement après l’en-tête de réponse habituel.
FUSE_INTERRUPT
struct fuse_interrupt_in {
uint64_t unique;
};
L’action demandée est d’annuler l’opération en attente indiquée par unique. Cette requête ne nécessite pas de réponse. Cependant, la réception de ce message n’annule pas en soi l’opération indiquée. Le noyau attendra toujours une réponse à ladite opération (par exemple, une erreur EINTR ou une lecture partielle). Au maximum, une requête FUSE_INTERRUPT sera émise pour une opération donnée. Après avoir émis ladite opération, le noyau attendra de manière ininterrompue la fin de l’opération indiquée.
FUSE_LOOKUP
Juste après l’en-tête se trouve le nom de fichier à rechercher dans le répertoire indiqué par header->nodeid. La réponse attendue est de la forme :
struct fuse_entry_out {
uint64_t nodeid; /* ID de nœud */
uint64_t generation; /* Génération de nœud */
uint64_t entry_valid;
uint64_t attr_valid;
uint32_t entry_valid_nsec;
uint32_t attr_valid_nsec;
struct fuse_attr attr;
};
La combinaison de nodeid et generation doit être unique pendant la durée de vie du système de fichiers.
L’interprétation des délais d’attente et de attr est la même que pour FUSE_GETATTR.
FUSE_FLUSH
struct fuse_flush_in {
uint64_t fh;
uint32_t unused;
uint32_t padding;
uint64_t lock_owner;
};
L’action demandée est de vider les modifications en attente pour le descripteur de fichier indiqué. Aucune donnée de réponse n’est attendue. Cependant, un message de réponse vide doit tout de même être émis une fois l’opération de vidage terminée.
FUSE_RELEASE
FUSE_RELEASEDIR
struct fuse_release_in {
uint64_t fh;
uint32_t flags;
uint32_t release_flags;
uint64_t lock_owner;
};
Ce sont les opérations inverses de FUSE_OPEN et FUSE_OPENDIR respectivement. Le démon peut maintenant libérer toutes les ressources associées au descripteur de fichier fh, car le noyau ne s’y référera plus. Il n’y a pas de données de réponse associées à cette requête, mais une réponse doit tout de même être émise une fois que la requête a été complètement traitée.
FUSE_STATFS
Cette opération implémente statfs(2) pour ce système de fichiers. Il n’y a pas de données d’entrée associées à cette requête. Les données de réponse attendues ont la structure suivante :
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;
};
Pour l’interprétation de ces champs, voir statfs(2).
ERREURS
E2BIG Renvoie des opérations read(2) lorsque la requête du noyau est trop grande pour le tampon fourni et que la requête était FUSE_SETXATTR.
EINVAL Renvoie des opérations write(2) si la validation de la réponse échoue. Toutes les erreurs dans les réponses ne sont pas détectées par cette validation. Cependant, les erreurs de base, telles qu’une réponse trop courte ou une valeur unique incorrecte, sont détectées.
EIO Renvoie des opérations read(2) lorsque la requête du noyau est trop grande pour le tampon fourni.
Remarque : il existe différentes manières dont une utilisation incorrecte de ces interfaces peut entraîner l’échec des opérations sur les fichiers et les répertoires du système de fichiers fourni avec EIO. Parmi les utilisations incorrectes possibles, citons :
la modification du mode et de S_IFMT pour un inode qui a déjà été signalé au noyau ; ou
la fourniture de réponses au noyau qui sont plus courtes que ce que le noyau attendait.
ENODEV Renvoie des opérations read(2) et write(2) si le système de fichiers FUSE a été démonté.
EPERM Renvoie des opérations sur un descripteur de fichier /dev/fuse qui n’a pas été monté.
NORMES
Linux.
REMARQUES
Les messages suivants ne sont pas encore documentés dans cette page de manuel :
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
VOIR AUSSI
fusermount(1), mount.fuse(8)