fanotify

FANOTIFY(7)               Manuel du programmeur Linux              FANOTIFY(7)



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

DESCRIPTION
       Lâinterface de programmation fanotify permet dâêtre notifié et
       dâintercepter les événements de systèmes de fichiers. La recherche
       de virus et la gestion de stockage hiérarchisé font partie des cas
       dâutilisation. Actuellement, seul un ensemble limité dâévénements
       est pris en charge. En particulier, les événements de création,
       suppression ou plus ne sont pas pris en charge (consultez inotify(7)
       pour plus de précisions sur une interface de programmation qui permet
       dâêtre notifié de ces événements).

       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é par
       dâautres applications font partie des capacités supplémentaires Ã
       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 montages 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, les entrées correspondantes
       survivent. Si les fichiers ou répertoires sont supprimés ou si les
       montages sont démontés, les entrées correspondante 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âaction qui les reçoit à  part pour la fermeture du
       descripteur de fichier 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 une lecture (avec read(2)) réussie, le tampon de lecture
       contient une ou plusieurs des structures suivantes :

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

       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 seul 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 champs 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. Dans
              lâimplémentation actuelle, la valeur dâevent_len est toujours
              FAN_EVENT_METADATA_LEN. Cependant, lâinterface de programmation
              est conçue pour permettre de renvoyer des structures de taille
              variable à  lâavenir.

       vers   Ce champ contient un numéro de version pour la structure. Il
              doit être comparé à FANOTIFY_METADATA_VERSION pour vérifier
              que la structure renvoyée pendant lâexécution correspond 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énements. 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. 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és en utilisant ce descripteur de
              fichier, aucun événement supplémentaire nâest créé.

       pid    Câest lâidentifiant de processus qui a provoqué lâé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 de 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 originaire 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_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
              Soit un fichier, soit 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 de 16384 entrées.
              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é.

       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

       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é 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 y a :

       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 suivant sur le descripteur de
       fichier fanotify :

           struct fanotify_response {
               __s32 fd;
               __u32 response;
           };

       Les champs 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 dâapplication faisant la demande
       recevra une erreur EPERM.

   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és pour être réutilisés par le noyau.
       Lors de lâappel de close(2), les événements de permission restants
       seront définis à permis.

   /proc/[pid]/fdinfo
       Le fichier /proc/[pid]/fdinfo/[fd] contient des renseignements sur les
       marques fanotify pour le descripteur de fichier fd du processus pid.
       Consultez le fichier Documentation/filesystems/proc.txt des sources du
       noyau pour plus de précisions.

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énements 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é écrit.

VERSIONS
       Lâinterface de programmation fanotify a été introduite dans la
       version 2.6.36 du noyau Linux et activée dans la version 2.6.37. La
       prise en charge de fdinfo a été ajoutée dans la version 3.8.

CONFORMITÃ
       Lâinterface de programmation fanotify est spécifique à  Linux.

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épertoires 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 (mais remarquez que lâinterface de programmation
       fanotify ne fournit aucun moyen de détecter quand un répertoire a
       été créé dans un répertoire marqué, ce qui rend difficile la
       surveillance récursive). Les montages surveillés offrent la capacité
       de surveiller une arborescence complète de répertoires.

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

BOGUES
       Dans Linux 3.15, les bogues suivants existent :

       -  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.

EXEMPLE
       Le programme suivant montre lâutilisation de lâinterface de
       programmation fanotify. Il marque le point de montage passé en
       argument de ligne de commande et attend les événements de type
       FAN_PERM_OPEN et FAN_CLOSE_WRITE. Quand un événement de permission
       survient, une réponse FAN_ALLOW est donnée.

       La sortie suivante a été enregistrée lors de la modification 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.

   Exemple de sortie
           # ./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

           Arrêt de lâécoute dâévénements.

   Source du programme
       /* Nécessaire pour obtenir la définition de O_LARGEFILE */
       #define _GNU_SOURCE

       #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 à partir 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;

           /* Boucler tant que les événements peuvent être lus à partir du
              descripteur de fichier fanotify */

           for(;;) {

               /* Lire certains événements */

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

               /* Vérifier si la fin des données disponibles est atteinte */

               if (len <= 0)
                   break;

               /* Pointer vers le premier événement du tampon */

               metadata = buf;

               /* Boucler sur tous les événements du tampon */

               while (FAN_EVENT_OK(metadata, len)) {

                   /* Vérifier que les structures au moment de lâexécution et
                      de la compilation correspondent */

                   if (metadata->vers != FANOTIFY_METADATA_VERSION) {
                       fprintf(stderr,
                   "Non correspondance de version de métadonnées fanotify.\n");
                       exit(EXIT_FAILURE);
                   }

                   /* metadata->fd contient soit FAN_NOFD, indiquant un
                      dépassement de file, soit un descripteur de fichier
                      (un entier positif).
                      Ici, le dépassement de file est simplement ignoré. */

                   if (metadata->fd >= 0) {

                       /* Traiter lâévénement de permission dâouverture */

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

                           /* Permettre dâouvrir le fichier */

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

                       /* Traiter lâévénement de fermeture de fichier ouvert
                          en écriture */

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

                       /* Récupérer et afficher le 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("Fichier %s\n", path);

                       /* Fermer le descripteur de fichier de lâévénement */

                       close(metadata->fd);
                   }

                   /* Avancer 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érifier quâun point de montage est fourni */

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

           printf("Appuyer sur la touche Entrée pour quitter.\n");

           /* Créer le descripteur de fichier pour accéder à  lâinterface de
              programmation fanotify */

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

           /* Marquer le montage pour :
              - les événements de permission avant dâouvrir les fichiers ;
              - les événements de notification après fermeture de descripteur
                de fichier ouvert en écriture. */

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

           /* Préparer pour la scrutation (polling) */

           nfds = 2;

           /* Entrée de console */

           fds[0].fd = STDIN_FILENO;
           fds[0].events = POLLIN;

           /* Entrée de fanotify */

           fds[1].fd = fd;
           fds[1].events = POLLIN;

           /* Boucle en attente dâarrivée dâévénements */

           printf("En écoute dâévénements.\n");

           while (1) {
               poll_num = poll(fds, nfds, -1);
               if (poll_num == -1) {
                   if (errno == EINTR)     /* Interrompu 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 :
                          vider lâentrée standard et quitter */

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

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

                       /* Des événements fanotify sont disponibles */

                       handle_events(fd);
                   }
               }
           }

           printf("Arrêt de lâécoute dâévénements.\n");
           exit(EXIT_SUCCESS);
       }

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

COLOPHON
       Cette page fait partie de la publication 3.70 du projet man-pages
       Linux. Une description du projet et des instructions pour signaler des
       anomalies peuvent être trouvées à l'adresse
       http://www.kernel.org/doc/man-pages/.

TRADUCTION
       Depuis 2010, cette traduction est maintenue à l'aide de l'outil po4a
       <http://po4a.alioth.debian.org/> par l'équipe de traduction
       francophone au sein du projet perkamon
       <http://perkamon.alioth.debian.org/>.

       Veuillez signaler toute erreur de traduction en écrivant Ã
       <perkamon-fr@traduc.org>.

       Vous pouvez toujours avoir accès à la version anglaise de ce document
       en utilisant la commande « LC_ALL=C man <section> <page_de_man> ».



Linux                             21 mai 2014                      FANOTIFY(7)