fanotify
Table des matières
Retour à l'index
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
-
C’est la taille des données pour l’événement actuel et la position du
prochain événement dans le tampon. À moins que le groupe identifie des
objets du système de fichiers par des gestionnaires de fichiers, la valeur
d’event_len est toujours FAN_EVENT_METADATA_LEN. Pour un groupe qui
identifie les objets du système de fichiers par des gestionnaires de
fichiers, event_len inclut aussi des enregistrements d’identificateur de
fichier de taille variable.
- 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
-
C’est la taille de la structure. Le champ a été introduit pour faciliter
l’implémentation d’en-têtes facultatifs par type d’événement. Aucun en-tête
facultatif n’existe dans l’implémentation actuelle.
- 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
-
C’est un identifiant unique du système de fichiers contenant l’objet associé
avec l’évènement. C’est une structure de type __kernel_fsid_t et elle
contient la même valeur que f_fsid lors de l’appel statfs(2).
- handle
-
Ce champ contient une structure de taille variable de type struct file_handle. C’est un gestionnaire opaque qui correspond à un objet précis
de système de fichiers comme renvoyé par name_to_handle_at(2). Il peut
être utilisé uniquement pour identifier un fichier d’un système de fichiers
et peut être passé comme argument à open_by_handle_at(2). Si la valeur du
champ info_type est FAN_EVENT_INFO_TYPE_DFID_NAME, le gestionnaire de
fichiers est suivi d’un octet NULL final qui identifie le nom d’entrée de
répertoire créée/supprimée/déplacée. Pour les autres évènements tels que
FAN_OPEN, FAN_ATTRIB, FAN_DELETE_SELF et FAN_MOVE_SELF, si la
valeur du champ info_type est FAN_EVENT_INFO_TYPE_FID, le handle
identifie l’objet corrélé à l’évènement. Si la valeur du champ info_type
est FAN_EVENT_INFO_TYPE_DFID, le handle identifie l’objet de
répertoire corrélé à l’évènement ou le répertoire parent d’un objet
non-répertoire corrélé à l’évènement. Si la valeur du champ info_type est
FAN_EVENT_INFO_TYPE_DFID_NAME, le handle identifie le même objet de
répertoire qui aurait été rapporté avec FAN_EVENT_INFO_TYPE_DFID et le
gestionnaire de fichier est suivi d’un octet NULL final qui identifie le nom
de l’entrée de répertoire dans ce répertoire ou « . » pour identifier
l’objet du répertoire.
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, len)
-
Cette macro compare la taille restante len du tampon meta à la taille
de la structure de métadonnées et au champ event_len de la première
structure de métadonnées du tampon.
- FAN_EVENT_NEXT(meta, len)
-
Cette macro utilise la taille indiquée dans le champ event_len de la
structure de métadonnées pointée par meta pour calculer l’adresse de la
prochaine structure de métadonnées qui suit meta. len est le nombre
d’octets de métadonnées qui restent actuellement dans le tampon. La macro
renvoie un pointeur vers la prochaine structure de métadonnées qui suit
meta et réduit len du nombre d’octets dans la structure de métadonnées
qui a été sautée (c’est-à-dire qu’elle soustrait meta->event_len de
len).
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.
Depuis Linux 5.13, les interfaces suivantes peuvent être utilisées pour
contrôler la quantité de ressources du noyau utilisées par 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.
- -
-
Si un appel de read(2) traite plusieurs événements de la file fanotify et
qu’une erreur survient, la valeur de retour sera la taille totale des
événements copiés correctement dans le tampon d’espace utilisateur avant que
l’erreur ne survienne. La valeur de retour ne sera pas -1 et errno ne
sera pas définie. Ainsi, l’application lisant n’a aucun moyen de détecter
l’erreur.
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 /* Nécessaire pour obtenir la définition de O_LARGEFILE */
#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>
/* Lire tous les évènements fanotify disponibles du descripteur de fichier « fd ». */
static void
handle_events(int fd)
{
const struct fanotify_event_metadata *metadata;
struct fanotify_event_metadata buf[200];
ssize_t len;
char path[PATH_MAX];
ssize_t path_len;
char procfd_path[PATH_MAX];
struct fanotify_response response;
/* Bouclage tant que des évènements peuvent être lus du descripteur de fichier. */
for (;;) {
/* Lecture de quelques évènements. */
len = read(fd, buf, sizeof(buf));
if (len == -1 && errno != EAGAIN) {
perror("read");
exit(EXIT_FAILURE);
}
/* Vérification que la fin des données disponibles soit atteinte. */
if (len <= 0)
break;
/* Pointage vers le premier évènement dans le tampon. */
metadata = buf;
/* Bouclage sur tous les évènements du tampon. */
while (FAN_EVENT_OK(metadata, len)) {
/* Vérification que les structures d’exécution et de compilation correspondent. */
if (metadata->vers != FANOTIFY_METADATA_VERSION) {
fprintf(stderr,
"Mauvaise correspondance des versions de métadonnées de fanotify.\en");
exit(EXIT_FAILURE);
}
/* metadata->fd contient soit FAN_NOFD, indiquant un
débordement de file ou un descripteur de fichier (un entier
non négatif). Ici, le débordement est simplement ignoré. */
if (metadata->fd >= 0) {
/* Gestion de la permission d’ouvrir. */
if (metadata->mask & FAN_OPEN_PERM) {
printf("FAN_OPEN_PERM: ");
/* Autorisation d’ouvrir le fichier. */
response.fd = metadata->fd;
response.response = FAN_ALLOW;
write(fd, &response, sizeof(response));
}
/* Gestion de la fermeture d’évènement de fichier éditable. */
if (metadata->mask & FAN_CLOSE_WRITE)
printf("FAN_CLOSE_WRITE: ");
/* Récupération et affichage du chemin du fichier accédé. */
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);
/* Fermeture du descripteur de fichier de l’évènement. */
close(metadata->fd);
}
/* Avance jusqu’au prochain évènement. */
metadata = FAN_EVENT_NEXT(metadata, len);
}
}
}
int
main(int argc, char *argv[])
{
char buf;
int fd, poll_num;
nfds_t nfds;
struct pollfd fds[2];
/* Vérification que le point de montage soit fourni. */
if (argc != 2) {
fprintf(stderr, "Utilisation : %s MOUNT\n", argv[0]);
exit(EXIT_FAILURE);
}
printf("Presser la touche Entrée pour terminer.\n");
/* Création d’un descripteur de fichier pour accéder à l’API de fanotify. */
fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
O_RDONLY | O_LARGEFILE);
if (fd == -1) {
perror("fanotify_init");
exit(EXIT_FAILURE);
}
/* Marque du montage pour :
– évènements de permission avant ouvertures de fichier
– évènements de notification après la fermeture
d’un descripteur de fichier éditable. */
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);
}
/* Préparation pour sondage. */
nfds = 2;
fds[0].fd = STDIN_FILENO; /* Entrée de console */
fds[0].events = POLLIN;
fds[1].fd = fd; /* Entrée de fanotify */
fds[1].events = POLLIN;
/* Ceci est une boucle pour attendre de futurs évènements. */
printf("Écoute d’évènements.\n");
while (1) {
poll_num = poll(fds, nfds, -1);
if (poll_num == -1) {
if (errno == EINTR) /* Interruption par un signal */
continue; /* Redémarrage de poll() */
perror("poll"); /* Erreur inattendue */
exit(EXIT_FAILURE);
}
if (poll_num > 0) {
if (fds[0].revents & POLLIN) {
/* Entrée de console disponible : stdin vide et quitter. */
while (read(STDIN_FILENO, &buf, 1) > 0 && buf != '\n')
continue;
break;
}
if (fds[1].revents & POLLIN) {
/* Évènements de fanotify disponibles. */
handle_events(fd);
}
}
}
printf("Écoute d’évènements arrêtée.\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 len, 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, "nombre d’arguments de ligne de commande non valable.\n");
exit(EXIT_FAILURE);
}
mount_fd = open(argv[1], O_DIRECTORY | O_RDONLY);
if (mount_fd == -1) {
perror(argv[1]);
exit(EXIT_FAILURE);
}
/* Création d’un descripteur de fichier avec FAN_REPORT_DFID_NAME
comme drapeau pour que le programme puisse recevoir des évènements
fid avec un nom d’entrée de répertoire. */
fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_DFID_NAME, 0);
if (fd == -1) {
perror("fanotify_init");
exit(EXIT_FAILURE);
}
/* Placement d’une marque sur l’objet de système de fichiers fourni dans 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("Écoute d’évènements.\n");
/* Lecture d’évènements de la file d’évènements d’un tampon. */
len = read(fd, events_buf, sizeof(events_buf));
if (len == -1 && errno != EAGAIN) {
perror("read");
exit(EXIT_FAILURE);
}
/* Traitement de tous les évènements du tampon. */
for (metadata = (struct fanotify_event_metadata *) events_buf;
FAN_EVENT_OK(metadata, len);
metadata = FAN_EVENT_NEXT(metadata, len)) {
fid = (struct fanotify_event_info_fid *) (metadata + 1);
file_handle = (struct file_handle *) fid->handle;
/* Vérification que l’information d’évènement soit du bon 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, "Type inattendu d’information d’évènement.\n");
exit(EXIT_FAILURE);
}
if (metadata->mask == FAN_CREATE)
printf("FAN_CREATE (fichier créé :\n");
if (metadata->mask == (FAN_CREATE | FAN_ONDIR))
printf("FAN_CREATE | FAN_ONDIR (sous-répertoire créé) :\n");
/* metadata->fd est défini à FAN_NOFD quand le groupe identifie
des objets par des gestionnaires de fichier. Pour obtenir un descripteur
de fichier pour l’objet de fichier correspondant à un évènement, la structure
file_handle qui est fournie dans fanotify_event_info_fid peut être utilisée
en conjonction avec l’appel système open_by_handle_at(2). Une vérification
pour ESTALE est faite pour répondre à la situation où le gestionnaire de
l’objet a été supprimé avant cet appel système. */
event_fd = open_by_handle_at(mount_fd, file_handle, O_RDONLY);
if (event_fd == -1) {
if (errno == ESTALE) {
printf("Gestionnaire de fichier plus valable. "
"Le fichier a été supprimé\n");
continuation;
} else {
perror("open_by_handle_at");
exit(EXIT_FAILURE);
}
}
snprintf(procfd_path, sizeof(procfd_path), "/proc/self/fd/%d",
event_fd);
/* Récupération et affichage du chemin d’entrée de répertoire modifiée. */
path_len = readlink(procfd_path, path, sizeof(path) - 1);
if (path_len == -1) {
perror("readlink");
exit(EXIT_FAILURE);
}
path[path_len] = '\0';
printf("\tRépertoire « %s » a été modifié.\n", chemin);
if (file_name) {
ret = fstatat(event_fd, file_name, &sb, 0);
if (ret == -1) {
if (errno != ENOENT) {
perror("fstatat");
exit(EXIT_FAILURE);
}
printf("\tEntée « %s » n’existe pas.\n", fichier);
} else if ((sb.st_mode & S_IFMT) == S_IFDIR) {
printf("\tEntrée « %s » est un sous-répertoire.\n", fichier);
} else {
printf("\tEntrée « %s » n’est pas un répertoire.\n",
fichier);
}
}
/* Fermeture du descripteur de fichier associé à cet évènement. */
close(event_fd);
}
printf("Tous les évènements ont été traités avec succès. Fin du programme.\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
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 à
Index
- NOM
-
- DESCRIPTION
-
- fanotify_init(), fanotify_mark() et groupes de notification
-
- La file d’événements
-
- Lecture d’événements fanotify
-
- Surveiller un descripteur de fichier fanotify pour les événements
-
- Traitement des événements de permission
-
- Supervision des erreurs des systèmes de fichiers
-
- Fermeture du descripteur de fichier fanotify
-
- /proc interfaces
-
- ERREURS
-
- STANDARDS
-
- HISTORIQUE
-
- NOTES
-
- Limites et réserves
-
- BOGUES
-
- EXEMPLES
-
- Exemple de programme : fanotify_example.c
-
- Source du programme : fanotify_example.c
-
- Exemple de programme : fanotify_fid.c
-
- Source du programme : fanotify_fid.c
-
- VOIR AUSSI
-
- TRADUCTION
-
This document was created by
man2html,
using the manual pages.
Time: 05:06:36 GMT, September 19, 2025