fanotify(7)

NOM
fanotify – Surveiller les événements des systèmes de fichiers

DESCRIPTION
L’interface de programmation fanotify permet la notification et l’interception des événements du système de fichiers. La recherche de virus et la gestion de stockage hiérarchisé font partie des cas d’utilisation. Dans l’interface originelle seul un ensemble limité d’événements était pris en charge. En particulier, les événements de création, de suppression ou de déplacement n’étaient pas pris en charge. La prise en charge de ces évènements a été ajoutée dans Linux 5.1. Consultez inotify(7) pour plus de précisions sur l’interface qui ne notifiait pas ces évènements avant Linux 5.1.

La capacité de surveiller tous les objets d’un système de fichiers monté, la capacité de décider des droits d’accès et la possibilité de lire ou modifier les fichiers avant qu’ils ne soient accédés par d’autres applications font partie des capacités supplémentaires à celles de l’interface de programmation inotify(7).

Les appels système suivants sont utilisés avec cette interface de programmation : fanotify_init(2), fanotify_mark(2), read(2), write(2) et close(2).

  fanotify_init(), fanotify_mark() et groupes de notification
L’appel système fanotify_init(2) crée et initialise un groupe de notifications fanotify et renvoie un descripteur de fichier le référençant.

Un groupe de notifications fanotify est un objet interne au noyau qui contient une liste de fichiers, répertoires et points de montage pour lesquels des événements seront créés.

Pour chaque entrée dans un groupe de notifications fanotify, deux masques binaires sont présents : le masque mark et le masque ignore. Le masque mark définit les activités de fichier pour lesquelles un événement doit être créé. Le masque ignore définit les activités pour lesquelles aucun événement ne doit être créé. Avoir ces deux types de masque permet à un point de montage ou à un répertoire d’être marqué pour recevoir des événements, tout en ignorant en même temps les événements pour des objets spécifiques dans ce point de montage ou répertoire.

L’appel système fanotify_mark(2) ajoute un fichier, répertoire ou point de montage à un groupe de notifications et indique les événements qui doivent être signalés (ou ignorés), ou supprime ou modifie une telle entrée.

Le masque ignore peut servir pour un cache de fichier. Les événements intéressants pour un cache de fichier sont la modification et la fermeture d’un fichier. Ainsi, le répertoire ou point de montage en cache va être marqué pour recevoir ces événements. Après la réception du premier événement informant qu’un fichier a été modifié, l’entrée correspondante du cache sera désactivée. Aucun autre événement de modification pour ce fichier ne sera utile jusqu’à sa fermeture. Ainsi, l’événement de modification peut être ajouté au masque ignore. Lors de la réception d’un événement de fermeture, l’événement de modification peut être supprimé du masque ignore et l’entrée de cache de fichier peut être mise à jour.

Les entrées des groupes de notification fanotify font référence aux fichiers et répertoires à l’aide de leur numéro d’inœud et aux montages à l’aide de leur identifiant de montage. Si les fichiers ou répertoires sont renommés ou déplacés dans le même montage, les entrées correspondantes survivent. Si les fichiers ou répertoires sont supprimés ou déplacés dans un autre montage ou si les montages sont démontés, les entrées correspondantes sont supprimées.

  La file d’événements
Comme les événements surviennent sur les objets de système de fichiers surveillés par un groupe de notifications, le système fanotify génère les événements qui sont collectés dans une file. Ces événements peuvent être lus (en utilisant read(2) ou similaire) à partir du descripteur de fichier fanotify renvoyé par fanotify_init(2).

Deux types d’événements sont créés : les événements de notification et les événements de permission. Les événements de notification sont surtout informatifs et ne nécessitent pas d’action à prendre par l’application qui les reçoit à part pour la fermeture du descripteur de fichier valable passé dans l’événement (voir ci-dessous). Les événements de permission sont des demandes à l’application qui les reçoit pour décider si les droits d’accès à un fichier doivent être attribués. Pour ces événements, le destinataire doit écrire une réponse qui décide d’attribuer l’accès ou non.

Un événement est supprimé de la file d’événements du groupe fanotify quand il a été lu. Les événements de permission qui ont été lus sont gardés dans une liste interne du groupe fanotify jusqu’à ce qu’une décision d’attribution de droits ait été prise en écrivant dans le descripteur de fichier fanotify ou que le descripteur de fichier fanotify soit fermé.

  Lecture d’événements fanotify
Appeler read(2) pour le descripteur de fichier renvoyé par fanotify_init(2) bloque (si l’attribut FAN_NONBLOCK n’est pas indiqué dans l’appel de fanotify_init(2)) jusqu’à ce qu’un événement de fichier survienne ou que l’appel soit interrompu par un signal (consultez signal(7)).

Après un read(2) réussi, le tampon de lecture contient une ou plus des structures suivantes :

    struct fanotify_event_metadata {
        __u32 event_len;
        __u8 vers;
        __u8 reserved;
        __u16 metadata_len;
        __aligned_u64 mask;
        __s32 fd;
        __s32 pid;
    };

Les enregistrements d’information sont des pièces d’information supplémentaires qui peuvent être fournies en même temps que la structure générique, fanotify_event_metadata. Les flag passés à fanotify_init(2) ont une influence sur le type d’enregistrement d’information qui peut être renvoyé pour l’évènement. Par exemple, si un groupe de notifications est initialisé avec FAN_REPORT_FID ou FAN_REPORT_DIR_FID, les écouteurs d’évènement pourront s’attendre aussi à recevoir une structure fanotify_event_info_fid en même temps que la structure fanotify_event_metadata par laquelle les gestionnaires de fichier sont utilisés pour identifier des objets de système de fichiers plutôt que des descripteurs de fichier. Les enregistrements d’information peuvent être aussi empilés, signifiant qu’utiliser les divers flag FAN_REPORT_* en même temps que l’un ou l’autre est pris en charge. Dans de tels cas, plusieurs enregistrements d’information peuvent être renvoyés pour un évènement en même temps que la structure générique fanotify_event_metadata. Par exemple, si un groupe de notifications est initialisé avec FAN_REPORT_TARGET_FID et FAN_REPORT_PIDFD, un écouteur d’évènement peut s’attendre à recevoir jusqu’à deux enregistrements d’information fanotify_event_info_fid et un enregistrement d’information fanotify_event_info_pidfd en même temps que la structure générique fanotify_event_metadata. Notablement, fanotify ne fournit aucune garantie sur l’ordre des enregistrements d’information quand un groupe de notifications est initialisé avec une configuration basée sur l’empilage. Chaque enregistrement d’information a une structure imbriquée de type fanotify_event_info_header. Il est impératif pour les écouteurs d’évènement d’inspecter le champ info_type de cette structure pour déterminer le type d’enregistrement d’information qui a été reçu pour un évènement donné.

Dans le cas d’un groupe fanotify qui identifie des objets de système de fichiers par des gestionnaires de fichiers, les écouteurs d’événement espèrent recevoir un ou plusieurs objets d’enregistrement d’informations ci-dessous en même temps que la structure générique fanotify_event_metadata dans le tampon de lecture :

    struct fanotify_event_info_fid {
        struct fanotify_event_info_header hdr;
        __kernel_fsid_t fsid;
        unsigned char handle[];
    };

Dans le cas où un groupe fanotify est initialisé avec FAN_REPORT_PIDFD, les écouteurs d’évènement doivent s’attendre à recevoir l’objet d’enregistrement d’information ci-dessous en même temps que la structure fanotify_event_metadata dans le tampon de lecture :

    struct fanotify_event_info_pidfd {
            struct fanotify_event_info_header hdr;
            __s32 pidfd;
    };

Dans le cas d’un évènement FAN_FS_ERROR, un enregistrement supplémentaire d’information décrivant l’erreur qui s’est produite est renvoyé en même temps que la structure fanotify_event_metadata générique dans le tampon de lecture. Cette structure est définie comme suit :

    struct fanotify_event_info_error {
        struct fanotify_event_info_header hdr;
        __s32 error;
        __u32 error_count;
    };

Tous les enregistrements d’information contiennent une structure imbriquée de type fanotify_event_info_header. Cette structure contient des méta-informations sur l’enregistrement d’information qui a pu être renvoyé en même temps que la structure fanotify_event_metadata générique. Cette structure est définie comme suit :

    struct fanotify_event_info_header {
    	__u8 info_type;
    	__u8 pad;
    	__u16 len;
    };

Pour des raisons de performances, une grande taille de tampon (par exemple 4096 octets) est conseillée pour que plusieurs événements puissent être récupérés en une seule lecture.

La valeur de retour de read(2) est le nombre d’octets placés dans le tampon, ou -1 en cas d’erreur (mais consultez BOGUES).

Les membres de la structure fanotify_event_metadata sont les suivants.

event_len
    This is the size of the data for the current event and the offset to the next event in the buffer. Unless the group identifies filesystem objects by file handles, the value of event_len is always FAN_EVENT_METADATA_LEN. For a group that identifies filesystem objects by file handles, event_len also includes the variable size file identifier records.

vers
    Ce champ contient un numéro de version pour la structure. Il doit être comparé à FANOTIFY_METADATA_VERSION pour vérifier que les structures renvoyées pendant l’exécution correspondent aux structures définies à la compilation. En cas d’erreur de correspondance, l’application devrait arrêter d’essayer d’utiliser le descripteur de fichier fanotify.

reserved
    Ce champ n’est pas utilisé.

metadata_len
    This is the size of the structure. The field was introduced to facilitate the implementation of optional headers per event type. No such optional headers exist in the current implementation.

mask
    C’est un masque binaire décrivant l’événement (voir ci-dessous).

fd  C’est un descripteur de fichier ouvert pour l’objet actuellement accédé ou FAN_NOFD si un dépassement de file est survenu. Avec un groupe fanotify qui identifie les objets de système d’exploitation par des gestionnaires de fichiers, les applications doivent escompter que cette valeur soit FAN_NOFD pour chaque évènement qu’elles reçoivent. Le descripteur de fichier peut être utilisé pour accéder au contenu du fichier ou répertoire surveillé. L’application qui lit est responsable de la fermeture de ce descripteur de fichier.

    Lors d’un appel de fanotify_init(2), l’appelant pourrait indiquer (à l’aide de l’argument event_f_flags) plusieurs attributs d’état de fichier à définir dans la description de fichier ouverte qui correspond à ce descripteur de fichier. De plus, l’attribut d’état de fichier FMODE_NONOTIFY (interne au noyau) est défini dans la description de fichier ouverte. L’attribut supprime la création d’événement fanotify. Ainsi, quand le destinataire de l’événement fanotify accède au fichier ou répertoire notifié en utilisant ce descripteur de fichier, aucun événement supplémentaire n’est créé.

pid Si l’attribut FAN_REPORT_TID était réglé dans fanotify_init(2), c’est l’identifiant (TID) du thread qui a provoqué cet évènement. Sinon, c’est le PID du processus qui a provoqué cet évènement.

Un programme écoutant les événements fanotify peut comparer ce PID au PID renvoyé par getpid(2) pour déterminer si l’événement est provoqué par l’écoutant lui-même ou par un autre processus accédant au fichier.

Le masque binaire mask indique les événements survenus pour un seul objet de système de fichiers. Plusieurs bits pourraient être définis dans ce masque si plus d’un événement est survenu pour l’objet de système de fichiers surveillé. En particulier, les événements consécutifs pour le même objet de système de fichiers et originaires du même processus pourraient être fusionnés dans un seul événement, mais deux événements de permission ne sont jamais fusionnés dans une entrée de file.

Les bits pouvant apparaître dans mask sont les suivants.

FAN_ACCESS
    Un fichier ou un répertoire (mais consultez BOGUES) a été accédé (en lecture).

FAN_OPEN
    Un fichier ou un répertoire a été ouvert.

FAN_OPEN_EXEC
    Un fichier a été ouvert dans le but d’être exécuté. Consultez NOTES dans fanotify_mark(2) pour plus de détails.

FAN_ATTRIB
    Une métadonnée de fichier ou d’un répertoire a été modifiée.

FAN_CREATE
    Un fichier enfant ou un répertoire a été créé dans le répertoire surveillé.

FAN_DELETE
    Un fichier enfant ou un répertoire a été supprimé dans le répertoire surveillé.

FAN_DELETE_SELF
    Un fichier ou un répertoire a été supprimé.

FAN_FS_ERROR
    Une erreur de système de fichiers a été détectée.

FAN_RENAME
    Un fichier ou un répertoire a été déplacé de ou vers un répertoire parent surveillé.

FAN_MOVED_FROM
    Un fichier ou un répertoire a été déplacé du répertoire surveillé.

FAN_MOVED_TO
    Un fichier ou un répertoire a été déplacé vers un répertoire parent surveillé.

IN_MOVE_SELF
    Un fichier ou un répertoire surveillé a été déplacé.

FAN_MODIFY
    Un fichier a été modifié.

FAN_CLOSE_WRITE
    Un fichier qui était ouvert en écriture (O_WRONLY ou O_RDWR) a été fermé.

FAN_CLOSE_NOWRITE
    Un fichier ou un répertoire, qui était ouvert en lecture seule (O_RDONLY), a été fermé.

FAN_Q_OVERFLOW
    La file d’événements a dépassé la limite du nombre d’évènements. Cette limite peut être écrasée en indiquant l’attribut FAN_UNLIMITED_QUEUE lors de l’appel de fanotify_init(2).

FAN_ACCESS_PERM
    Une application veut lire un fichier ou répertoire, par exemple en utilisant read(2) ou readdir(2). Le lecteur doit écrire une réponse (telle que décrite ci-dessous) qui détermine si le droit d’accès à l’objet de système de fichiers sera attribué.

FAN_OPEN_PERM
    Une application veut ouvrir un fichier ou un répertoire. Le lecteur doit écrire une réponse qui détermine si le droit d’ouvrir l’objet de système de fichiers sera attribué.

FAN_OPEN_EXEC_PERM
    Une application veut ouvrir un fichier pour une exécution. Le lecteur doit écrire une réponse qui détermine si le droit d’ouvrir l’objet de système de fichiers sera attribué. Consultez NOTES dans fanotify_mark(2) pour plus de détails.

Pour vérifier tous les événements fermés, le masque binaire suivant pourrait être utilisé :

FAN_CLOSE
    Un fichier a été fermé. C’est un synonyme de :

        FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE

Pour vérifier tous les événements de déplacement, le masque binaire suivant pourrait être utilisé :

FAN_MOVE
    Un fichier ou un répertoire a été déplacé. C’est un synonyme de :

        FAN_MOVED_FROM | FAN_MOVED_TO

Les bits suivants peuvent apparaître dans mask seulement conjointement avec d’autres bits de type d’évènement :

FAN_ONDIR
    Les évènements décrits dans le mask se sont déroulés dans un objet de répertoire. Le rapport d’évènements dans des répertoires requiert le réglage de cet attribut dans le masque mark. Consultez fanotify_mark(2) pour plus de détails. L’attribut FAN_ONDIR est rapporté dans un masque d’évènement seulement si le groupe fanotify identifie les objets de système d’exploitation avec des gestionnaires de fichiers.

Les enregistrements d’information qui sont fournis en même temps que la structure fanotify_event_metadata générique contiendront toujours une structure imbriquée de type fanotify_event_info_header. Les membres de fanotify_event_info_header sont les suivants :

info_type
    Valeur entière unique représentant le type d’objet d’enregistrement d’information reçu pour un évènement. Ce champ peut être défini à une des valeurs suivantes : FAN_EVENT_INFO_TYPE_FID, FAN_EVENT_INFO_TYPE_DFID, FAN_EVENT_INFO_TYPE_DFID_NAME ou FAN_EVENT_INFO_TYPE_PIDFD. L’ensemble des valeurs pour ce champ est dépendant des drapeaux qui ont été fournis à fanotify_init(2). Se référer aux détails des champs pour chaque type d’objet d’enregistrement d’information ci-dessous pour appréhender les différents cas dans lesquels les valeurs info_type peuvent être définies.

pad Ce champ n’est actuellement utilisé par aucun type d’objet d’enregistrement d’information et, par conséquent, est défini à zéro.

len La valeur de len est définie à la taille de l’objet d’enregistrement d’information, incluant le fanotify_event_info_header. La taille totale de tous les enregistrements d’information supplémentaires ne peut être supérieure à (event_len - metadata_len).

Les membres de la structure fanotify_event_info_fid sont les suivants.

hdr C’est une structure de type fanotify_event_info_header. Par exemple, quand un descripteur de fichier fanotify est créé en utilisant FAN_REPORT_FID, un enregistrement unique est supposé être attaché à l’évènement avec la valeur de champ info_type de FAN_EVENT_INFO_TYPE_FID. Quand un descripteur de fichier fanotify est créé en utilisant la combinaison de FAN_REPORT_FID et FAN_REPORT_DIR_FID, il peut y avoir deux enregistrements d’informations attachés à l’évènement : un avec une valeur de champ info_type de FAN_EVENT_INFO_TYPE_DFID, identifiant un objet de répertoire parent, et un avec une valeur de champ info_type de FAN_EVENT_INFO_TYPE_FID, identifiant un objet enfant. Il est à remarquer que pour des évènements de modification d’entrée de répertoire FAN_CREATE, FAN_DELETE, FAN_MOVE et FAN_RENAME, un enregistrement d’information identifiant l’objet enfant créé, supprimé ou déplacé est rapporté seulement si un groupe fanotify a été initialisé avec le drapeau FAN_REPORT_TARGET_FID.

fsid
    This is a unique identifier of the filesystem containing the object associated with the event. It is a structure of type __kernel_fsid_t and contains the same value as f_fsid when calling statfs(2). Note that some filesystems (e.g., fuse(4)) report zero fsid. In these cases, it is not possible to use fsid to associate the event with a specific filesystem instance, so monitoring different filesystem instances that report zero fsid with the same fanotify group is not supported.

handle
    This field contains a variable-size structure of type struct file_handle. It is an opaque handle that corresponds to a specified object on a filesystem as returned by name_to_handle_at(2). It can be used to uniquely identify a file on a filesystem and can be passed as an argument to open_by_handle_at(2). If the value of info_type field is FAN_EVENT_INFO_TYPE_DFID_NAME, the file handle is followed by a null terminated string that identifies the created/deleted/moved directory entry name. For other events such as FAN_OPEN, FAN_ATTRIB, FAN_DELETE_SELF, and FAN_MOVE_SELF, if the value of info_type field is FAN_EVENT_INFO_TYPE_FID, the handle identifies the object correlated to the event. If the value of info_type field is FAN_EVENT_INFO_TYPE_DFID, the handle identifies the directory object correlated to the event or the parent directory of a non-directory object correlated to the event. If the value of info_type field is FAN_EVENT_INFO_TYPE_DFID_NAME, the handle identifies the same directory object that would be reported with FAN_EVENT_INFO_TYPE_DFID and the file handle is followed by a null terminated string that identifies the name of a directory entry in that directory, or '.' to identify the directory object itself.

Les membres de la structure fanotify_event_info_pidfd sont les suivants.

hdr C’est une structure de type fanotify_event_info_header. Quand un groupe fanotify est initialisé avec FAN_REPORT_PIDFD, le champ info_type de fanotify_event_info_header est défini à FAN_EVENT_INFO_TYPE_PIDFD.

pidfd
    C’est un descripteur de fichier de processus qui fait référence au processus responsable de la génération de l’évènement. Le descripteur de fichier de processus n’est pas différent de celui qui pourrait être obtenu manuellement si pidfd_open(2) était appelé sur fanotify_event_metadata.pid. Dans cette instance où une erreur pourrait se produire lors de la création de pidfd, un des deux types d’erreur possibles représentés par un entier négatif pourrait être renvoyé dans ce champ pidfd. Dans le cas où le processus responsable de la création de l’évènement s’est terminé avant que l’écouteur d’évènement soit capable de lire des évènements dans la file de notifications, FAN_NOPIDFD est renvoyé. La création de pidfd pour un évènement est seulement faite au moment où les évènements sont lus à partir de la file de notifications. Tous les autres échecs possibles de création de pidfd sont représentés par FAN_EPIDFD. Une fois que l’écouteur d’évènement a traité un évènement et que le pidfd n’est plus nécessaire, celui-ci sera fermé à l’aide de close(2).

Les membres de la structure fanotify_event_info_error sont les suivants.

hdr C’est une structure de type fanotify_event_info_header. Le champ info_type est défini à FAN_EVENT_INFO_TYPE_ERROR.

error
    Ce champ identifie le type d’erreur qui s’est produite.

error_count
    C’est un compteur du nombre d’erreurs supprimées depuis que la dernière erreur a été lue.

Les macros suivantes sont fournies pour itérer sur un tampon contenant les métadonnées d’événement fanotify renvoyées par read(2) à partir d’un descripteur de fichier fanotify.

FAN_EVENT_OK(meta, size)
    This macro checks the remaining size size of the buffer meta against the size of the metadata structure and the event_len field of the first metadata structure in the buffer.

FAN_EVENT_NEXT(meta, size)
    This macro uses the size indicated in the event_len field of the metadata structure pointed to by meta to calculate the address of the next metadata structure that follows meta. size is the number of bytes of metadata that currently remain in the buffer. The macro returns a pointer to the next metadata structure that follows meta, and reduces size by the number of bytes in the metadata structure that has been skipped over (i.e., it subtracts meta->event_len from size).

De plus, il existe :

FAN_EVENT_METADATA_LEN
    Cette macro renvoie la taille (en octet) de la structure fanotify_event_metadata. C’est la taille minimale (et actuellement la seule taille) de métadonnées d’événements.

  Surveiller un descripteur de fichier fanotify pour les événements
Quand un événement fanotify survient, le descripteur de fichier fanotify est indiqué comme lisible lorsque passé à epoll(7), poll(2) ou select(2).

  Traitement des événements de permission
Pour les événements de permission, l’application doit écrire (avec write(2)) une structure de la forme suivante sur le descripteur de fichier fanotify :

    struct fanotify_response {
        __s32 fd;
        __u32 response;
    };

Les membres de cette structure sont les suivants :

fd  C’est le descripteur de fichier de la structure fanotify_event_metadata.

response
    Ce champ indique si les droits doivent être attribués ou non. Cette valeur doit être soit FAN_ALLOW pour permettre l’opération de fichier, soit FAN_DENY pour refuser l’opération de fichier.

Si l’accès est refusé, l’appel de l’application requérante recevra une erreur EPERM. De plus, si le groupe de notifications a été créé avec l’attribut FAN_ENABLE_AUDIT, alors l’attribut FAN_AUDIT peut être défini dans le champ response. Dans ce cas, le sous-système d’audit journalisera l’information à propos de la décision d’accès aux journaux d’audit.

  Supervision des erreurs des systèmes de fichiers
Un seul évènement FAN_FS_ERROR à la fois est stocké par système de fichiers. Les messages d’erreur supplémentaires sont supprimés et comptés dans le champ error_count dans l’enregistrement existant d’évènement FAN_FS_ERROR, mais les détails à propos des erreurs sont perdus.

Les erreurs rapportées par FAN_FS_ERROR sont des erreurs génériques errno, mais tous les types d’erreur ne sont pas rapportés par tous les systèmes de fichiers.

Les erreurs ne concernant pas directement un fichier (c’est-à-dire des corruptions de superbloc) sont rapportées avec un handle non valable. Pour ces erreurs, le handle aura le champ handle_type défini à FILEID_INVALID et la taille de tampon de handle définie à 0.

  Fermeture du descripteur de fichier fanotify
Quand tous les descripteurs de fichier se référant au groupe de notifications fanotify sont fermés, le groupe fanotify est libéré et ses ressources sont libérées pour être réutilisées par le noyau. Lors de l’appel de close(2), les événements de permission restants seront définis à permis.

  /proc interfaces
Le fichier /proc/pid/fdinfo/fd contient des renseignements sur les marques fanotify pour le descripteur de fichier fd du processus pid. Consulter proc(5) pour plus de précisions.

Since Linux 5.13 (and 5.10.220), the following interfaces can be used to control the amount of kernel resources consumed by fanotify:

/proc/sys/fs/fanotify/max_queued_events
    La valeur dans ce fichier est utilisée quand une application appelle fanotify_init(2) pour définir la limite supérieure du nombre d’évènements qui peuvent être mis dans la file d’attente du groupe fanotify correspondant. Les évènements qui font que cette limite est dépassée sont abandonnés, mais un évènement FAN_Q_OVERFLOW est toujours généré. Avant le noyau 5.13 de Linux, la limite codée en dur était de 16384 évènements.

/proc/sys/fs/fanotify/max_user_group
    La valeur dans ce fichier définit la limite supérieure du nombre de groupes fanotify pouvant être créés par ID réel d’utilisateur. Avant le noyau 5.13 de Linux, la limite codée en dur était de 128 groupes par utilisateur.

/proc/sys/fs/fanotify/max_user_marks
    La valeur dans ce fichier définit la limite supérieure du nombre de marques fanotify pouvant être créées par ID réel d’utilisateur. Avant le noyau 5.13 de Linux, la limite codée en dur était de 8192 marques par groupe (pas par utilisateur).

ERREURS
En plus des erreurs habituelles de read(2), les erreurs suivantes peuvent survenir lors de la lecture d’un descripteur de fichier fanotify.

EINVAL
    Le tampon est trop petit pour contenir l’événement.

EMFILE
    La limite par processus du nombre de fichiers ouverts a été atteinte. Consultez la description de RLIMIT_NOFILE dans getrlimit(2).

ENFILE
    La limite du nombre de fichiers ouverts sur le système a été atteinte. Consultez /proc/sys/fs/file-max dans proc(5).

ETXTBSY
    Cette erreur est renvoyée par read(2) si O_RDWR ou O_WRONLY ont été indiqués dans l’argument event_f_flags lors de l’appel fanotify_init(2) et qu’un événement est survenu pour un fichier surveillé actuellement en cours d’exécution.

En plus des erreurs habituelles de write(2), les erreurs suivantes peuvent survenir lors de l’écriture sur un descripteur de fichier fanotify.

EINVAL
    Les droits d’accès fanotify ne sont pas activés dans la configuration du noyau ou la valeur de response dans la structure de réponse n’est pas valable.

ENOENT
    Le descripteur de fichier fd dans la structure de réponse n’est pas valable. Cela pourrait survenir quand une réponse pour l’événement de permission a déjà été écrite.

STANDARDS
Linux.

HISTORIQUE
L’interface de programmation fanotify a été introduite dans Linux 2.6.36 et activée dans Linux 2.6.37. La prise en charge de fdinfo a été ajoutée dans Linux 3.8.

NOTES
L’interface de programmation fanotify n’est disponible que si le noyau a été construit avec l’option de configuration CONFIG_FANOTIFY activée. De plus, le traitement de permission fanotify n’est disponible que si l’option de configuration CONFIG_FANOTIFY_ACCESS_PERMISSIONS est activée.

  Limites et réserves
Fanotify ne signale que les événements déclenchés par un programme en espace utilisateur à l’aide d’une interface de programmation de système de fichiers. Par conséquent, elle n’intercepte pas les événements qui surviennent sur les systèmes de fichiers en réseau.

L'interface fanotify ne signale pas les accès ni les modifications de fichier qui pourraient survenir à cause de mmap(2), msync(2) ou munmap(2).

Les événements pour répertoire ne sont créés que si le répertoire lui-même est ouvert, lu et fermé. Ajouter, supprimer ou modifier les enfants d’un répertoire marqué ne crée pas d’événement pour le répertoire surveillé lui-même.

La surveillance fanotify des répertoires n'est pas récursive : pour surveiller les sous-répertoires, des marques supplémentaires doivent être créées. L’évènement FAN_CREATE peut être utilisé pour détecter quand un sous-répertoire a été créé dans un répertoire marqué. Une marque supplémentaire doit être définie dans le sous-répertoire nouvellement créé. Cette approche crée une situation de compétition, parce qu’elle peut perdre les évènements qui se produisent dans le nouveau sous-répertoire avant qu’une marque soit ajoutée dans ce sous-répertoire. La surveillance des montages offre la capacité de surveiller un arbre entier de répertoires sans ce problème de chronologie. La surveillance de système de fichiers offre la capacité de surveiller tout montage d’une instance de système de fichiers sans situation de compétition.

La file d'événements peut déborder. Dans ce cas, les événements sont perdus.

BOGUES
Avant Linux 3.19, fallocate(2) ne créait pas d'événement fanotify. Depuis Linux 3.19, les appels à fallocate(2) créent des événements FAN_MODIFY.

Dans Linux 3.17, les bogues suivants existent :

-   Dans Linux, un objet du système de fichiers pourrait être accessible par de multiples chemins. Par exemple, une partie d'un système de fichiers pourrait être remontée avec l'option --bind de mount(8). Un écoutant ayant marqué un montage ne sera notifié que des événements déclenchés pour un objet du système de fichiers utilisant le même montage. Tout autre événement passera inaperçu.

-   Quand un événement est créé, aucune vérification n’est effectuée pour voir si l’identifiant utilisateur du processus recevant a le droit de lire ou écrire le fichier avant de passer un descripteur de fichier pour ce fichier. Cela pose un risque de sécurité quand la capacité CAP_SYS_ADMIN est définie pour un programme exécuté par les utilisateurs ordinaires.

-   If a call to read(2) processes multiple events from the fanotify queue and an error occurs, the return value will be the total size of the events successfully copied to the user-space buffer before the error occurred. The return value will not be -1, and errno will not be set. Thus, the reading application has no way to detect the error.

EXEMPLES
Les deux programmes ci-dessous montrent l’utilisation de l’API de fanotify.

  Exemple de programme : fanotify_example.c
Le programme suivant montre l’utilisation de l’interface de programmation fanotify avec les informations d’évènements d’objet passées sous la forme d’un descripteur de fichier. Il marque le point de montage passé en argument de ligne de commande et attend les événements de type FAN_OPEN_PERM et FAN_CLOSE_WRITE. Quand un événement de permission survient, une réponse FAN_ALLOW est donnée.

La sortie suivante de session d’interpréteur de commande montre un exemple de l’exécution de ce programme. Cette session concerne l’édition du fichier /home/utilisateur/temp/notes. Avant d’ouvrir le fichier, un événement FAN_OPEN_PERM est survenu. Après la fermeture du fichier, un événement FAN_CLOSE_WRITE est survenu. L’exécution du programme se termine quand l’utilisateur appuie sur la touche Entrée.

    # ./fanotify_exemple /home
    Appuyer sur la touche Entrée pour quitter.
    En écoute d’événements.
    FAN_OPEN_PERM : Fichier /home/utilisateur/temp/notes
    FAN_CLOSE_WRITE : Fichier /home/utilisateur/temp/notes
    Écoute des évènements arrêtée.

  Source du programme : fanotify_example.c
    #define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
    #include <errno.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <poll.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/fanotify.h>
    #include <unistd.h>

    /* Read all available fanotify events from the file descriptor 'fd'. */

    static void
    handle_events(int fd)
    {
        const struct fanotify_event_metadata *metadata;
        struct fanotify_event_metadata buf[200];
        ssize_t size;
        char path[PATH_MAX];
        ssize_t path_len;
        char procfd_path[PATH_MAX];
        struct fanotify_response response;

        /* Loop while events can be read from fanotify file descriptor. */

        for (;;) {

            /* Read some events. */

            size = read(fd, buf, sizeof(buf));
            if (size == -1 && errno != EAGAIN) {
                perror("read");
                exit(EXIT_FAILURE);
            }

            /* Check if end of available data reached. */

            if (size <= 0)
                break;

            /* Point to the first event in the buffer. */

            metadata = buf;

            /* Loop over all events in the buffer. */

            while (FAN_EVENT_OK(metadata, size)) {

                /* Check that run-time and compile-time structures match. */

                if (metadata->vers != FANOTIFY_METADATA_VERSION) {
                    fprintf(stderr,
                            "Mismatch of fanotify metadata version.\n");
                    exit(EXIT_FAILURE);
                }

                /* metadata->fd contains either FAN_NOFD, indicating a
                   queue overflow, or a file descriptor (a nonnegative
                   integer). Here, we simply ignore queue overflow. */

                if (metadata->fd >= 0) {

                    /* Handle open permission event. */

                    if (metadata->mask & FAN_OPEN_PERM) {
                        printf("FAN_OPEN_PERM: ");

                        /* Allow file to be opened. */

                        response.fd = metadata->fd;
                        response.response = FAN_ALLOW;
                        write(fd, &response, sizeof(response));
                    }

                    /* Handle closing of writable file event. */

                    if (metadata->mask & FAN_CLOSE_WRITE)
                        printf("FAN_CLOSE_WRITE: ");

                    /* Retrieve and print pathname of the accessed file. */

                    snprintf(procfd_path, sizeof(procfd_path),
                             "/proc/self/fd/%d", metadata->fd);
                    path_len = readlink(procfd_path, path,
                                        sizeof(path) - 1);
                    if (path_len == -1) {
                        perror("readlink");
                        exit(EXIT_FAILURE);
                    }

                    path[path_len] = '\0';
                    printf("File %s\n", path);

                    /* Close the file descriptor of the event. */

                    close(metadata->fd);
                }

                /* Advance to next event. */

                metadata = FAN_EVENT_NEXT(metadata, size);
            }
        }
    }

    int
    main(int argc, char *argv[])
    {
        char buf;
        int fd, poll_num;
        nfds_t nfds;
        struct pollfd fds[2];

        /* Check mount point is supplied. */

        if (argc != 2) {
            fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
            exit(EXIT_FAILURE);
        }

        printf("Press enter key to terminate.\n");

        /* Create the file descriptor for accessing the fanotify API. */

        fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
                           O_RDONLY | O_LARGEFILE);
        if (fd == -1) {
            perror("fanotify_init");
            exit(EXIT_FAILURE);
        }

        /* Mark the mount for:
           - permission events before opening files
           - notification events after closing a write-enabled
             file descriptor. */

        if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
                          FAN_OPEN_PERM | FAN_CLOSE_WRITE, AT_FDCWD,
                          argv[1]) == -1) {
            perror("fanotify_mark");
            exit(EXIT_FAILURE);
        }

        /* Prepare for polling. */

        nfds = 2;

        fds[0].fd = STDIN_FILENO;       /* Console input */
        fds[0].events = POLLIN;

        fds[1].fd = fd;                 /* Fanotify input */
        fds[1].events = POLLIN;

        /* This is the loop to wait for incoming events. */

        printf("Listening for events.\n");

        while (1) {
            poll_num = poll(fds, nfds, -1);
            if (poll_num == -1) {
                if (errno == EINTR)     /* Interrupted by a signal */
                    continue;           /* Restart poll() */

                perror("poll");         /* Unexpected error */
                exit(EXIT_FAILURE);
            }

            if (poll_num > 0) {
                if (fds[0].revents & POLLIN) {

                    /* Console input is available: empty stdin and quit. */

                    while (read(STDIN_FILENO, &buf, 1) > 0 && buf != '\n')
                        continue;
                    break;
                }

                if (fds[1].revents & POLLIN) {

                    /* Fanotify events are available. */

                    handle_events(fd);
                }
            }
        }

        printf("Listening for events stopped.\n");
        exit(EXIT_SUCCESS);
    }

  Exemple de programme : fanotify_fid.c
Le second programme est un exemple d’utilisation de fanotify avec un groupe qui identifie des objets avec des gestionnaires de fichier. Le programme marque l’objet de système de fichiers qui est passé comme argument de ligne de commande et attend jusqu’à ce qu’un évènement de type FAN_CREATE se produise. Le masque d’évènement indique quel type d’objet de système de fichiers, soit un fichier ou un répertoire, est créé. Une fois que tous les évènements du tampon ont été lus et traités de la manière appropriée, le programme se termine.

Les sessions d’interpréteur de commande suivantes montrent deux invocations différentes avec des actions différentes réalisées sur l’objet désiré.

La première session montre une marque placée sur /home/utilisateur. Cela est suivi par la création d’un fichier normal, /home/utilisateur/fichiertest.txt. Cela aboutit à un évènement FAN_CREATE généré et rapporté à l’objet de répertoire surveillé parent du fichier et à la création du nom de fichier. L’exécution du programme se termine une fois que tous les évènements capturés du tampon ont été traités.

    # ./fanotify_fid /home/user
    Écoute de tous les évènements.
    FAN_CREATE (fichier créé) :
            Répertoire /home/utilisateur a été modifié.
            Entrée « fichiertest.txt » n’est pas un sous-répertoire.
    Tous les évènements ont été traités avec succès. Fin du programme.

    $ touch /home/user/fichiertest.txt              # Dans un autre terminal

La première session montre une marque placée sur /home/utilisateur. C’est suivi par la création d’un répertoire, /home/utilisateur/réptest. Cette action spécifique aboutit à la génération d’un évènement FAN_CREATE et est rapporté avec l’attribut FAN_ONDIR défini et avec la création du nom de répertoire.

    # ./fanotify_fid /home/user
    Écoute de tous les évènements.
    FAN_CREATE | FAN_ONDIR (sous_répertoire créé) :
            Répertoire /home/utilisateur a été modifié.
            Entrée « réptest » est un sous-répertoire.
    Tous les évènements ont été traités avec succès. Fin du programme.

    $ mkdir -p /home/user/réptest          # Dans un autre terminal

  Source du programme : fanotify_fid.c
    #define _GNU_SOURCE
    #include <errno.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/fanotify.h>
    #include <unistd.h>

    #define BUF_SIZE 256

    int
    main(int argc, char *argv[])
    {
        int fd, ret, event_fd, mount_fd;
        ssize_t size, path_len;
        char path[PATH_MAX];
        char procfd_path[PATH_MAX];
        char events_buf[BUF_SIZE];
        struct file_handle *file_handle;
        struct fanotify_event_metadata *metadata;
        struct fanotify_event_info_fid *fid;
        const char *file_name;
        struct stat sb;

        if (argc != 2) {
            fprintf(stderr, "Invalid number of command line arguments.\n");
            exit(EXIT_FAILURE);
        }

        mount_fd = open(argv[1], O_DIRECTORY | O_RDONLY);
        if (mount_fd == -1) {
            perror(argv[1]);
            exit(EXIT_FAILURE);
        }

        /* Create an fanotify file descriptor with FAN_REPORT_DFID_NAME as
           a flag so that program can receive fid events with directory
           entry name. */

        fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_DFID_NAME, 0);
        if (fd == -1) {
            perror("fanotify_init");
            exit(EXIT_FAILURE);
        }

        /* Place a mark on the filesystem object supplied in argv[1]. */

        ret = fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_ONLYDIR,
                            FAN_CREATE | FAN_ONDIR,
                            AT_FDCWD, argv[1]);
        if (ret == -1) {
            perror("fanotify_mark");
            exit(EXIT_FAILURE);
        }

        printf("Listening for events.\n");

        /* Read events from the event queue into a buffer. */

        size = read(fd, events_buf, sizeof(events_buf));
        if (size == -1 && errno != EAGAIN) {
            perror("read");
            exit(EXIT_FAILURE);
        }

        /* Process all events within the buffer. */

        for (metadata = (struct fanotify_event_metadata *) events_buf;
                FAN_EVENT_OK(metadata, size);
                metadata = FAN_EVENT_NEXT(metadata, size)) {
            fid = (struct fanotify_event_info_fid *) (metadata + 1);
            file_handle = (struct file_handle *) fid->handle;

            /* Ensure that the event info is of the correct type. */

            if (fid->hdr.info_type == FAN_EVENT_INFO_TYPE_FID ||
                fid->hdr.info_type == FAN_EVENT_INFO_TYPE_DFID) {
                file_name = NULL;
            } else if (fid->hdr.info_type == FAN_EVENT_INFO_TYPE_DFID_NAME) {
                file_name = file_handle->f_handle +
                            file_handle->handle_bytes;
            } else {
                fprintf(stderr, "Received unexpected event info type.\n");
                exit(EXIT_FAILURE);
            }

            if (metadata->mask == FAN_CREATE)
                printf("FAN_CREATE (file created):\n");

            if (metadata->mask == (FAN_CREATE | FAN_ONDIR))
                printf("FAN_CREATE | FAN_ONDIR (subdirectory created):\n");

    	/* metadata->fd is set to FAN_NOFD when the group identifies
    	   objects by file handles.  To obtain a file descriptor for
    	   the file object corresponding to an event you can use the
    	   struct file_handle that's provided within the
    	   fanotify_event_info_fid in conjunction with the
    	   open_by_handle_at(2) system call.  A check for ESTALE is
    	   done to accommodate for the situation where the file handle
    	   for the object was deleted prior to this system call. */

            event_fd = open_by_handle_at(mount_fd, file_handle, O_RDONLY);
            if (event_fd == -1) {
                if (errno == ESTALE) {
                    printf("File handle is no longer valid. "
                            "File has been deleted\n");
                    continue;
                } else {
                    perror("open_by_handle_at");
                    exit(EXIT_FAILURE);
                }
            }

            snprintf(procfd_path, sizeof(procfd_path), "/proc/self/fd/%d",
                    event_fd);

            /* Retrieve and print the path of the modified dentry. */

            path_len = readlink(procfd_path, path, sizeof(path) - 1);
            if (path_len == -1) {
                perror("readlink");
                exit(EXIT_FAILURE);
            }

            path[path_len] = '\0';
            printf("\tDirectory '%s' has been modified.\n", path);

            if (file_name) {
                ret = fstatat(event_fd, file_name, &sb, 0);
                if (ret == -1) {
                    if (errno != ENOENT) {
                        perror("fstatat");
                        exit(EXIT_FAILURE);
                    }
                    printf("\tEntry '%s' does not exist.\n", file_name);
                } else if ((sb.st_mode & S_IFMT) == S_IFDIR) {
                    printf("\tEntry '%s' is a subdirectory.\n", file_name);
                } else {
                    printf("\tEntry '%s' is not a subdirectory.\n",
                            file_name);
                }
            }

            /* Close associated file descriptor for this event. */

            close(event_fd);
        }

        printf("All events processed successfully. Program exiting.\n");
        exit(EXIT_SUCCESS);
    }

VOIR AUSSI
fanotify_init(2), fanotify_mark(2), inotify(7)

TRADUCTION
La traduction française de cette page de manuel a été créée par Christophe Blaess <https://www.blaess.fr/christophe/>, Stéphan Rafin <stephan.rafin@laposte.net>, Thierry Vignaud <tvignaud@mandriva.com>, François Micaux, Alain Portal <aportal@univ-montp2.fr>, Jean-Philippe Guérard <fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centraliens.net>, Florentin Duneau <fduneau@gmail.com>, Simon Paillard <simon.paillard@resel.enst-bretagne.fr>, Denis Barbier <barbier@debian.org>, David Prévot <david@tilapin.org> et Jean-Paul Guillonneau <guillonneau.jeanpaul@free.fr>

Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 (https://www.gnu.org/licenses/gpl-3.0.html) concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.

Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à debian-l10n-french@lists.debian.org.