Provided by: manpages-ja_0.5.0.0.20221215+dfsg-1_all bug

名前

       fanotify - ファイルシステムイベントを監視する

説明

       The  fanotify  API provides notification and interception of filesystem events.  Use cases
       include virus scanning and hierarchical storage management.  In the original fanotify API,
       only  a  limited  set  of  events  was supported.  In particular, there was no support for
       create, delete, and move events.  The support for those events was  added  in  Linux  5.1.
       (See inotify(7)  for details of an API that did notify those events pre Linux 5.1.)

       inotify(7) API と比較して追加されている機能としては、 マウントされたファイルシステムの全オ
       ブジェクトを監視する機能、 アクセス許可の判定を行う機能、 他のアプリケーションによるアクセ
       スの前にファイルを読み出したり変更したりする機能がある。

       この  API では以下のシステムコールを使用する: fanotify_init(2), fanotify_mark(2), read(2),
       write(2), close(2)。

   fanotify_init(), fanotify_mark() と通知グループ
       fanotify_init(2) システムコールは fanotify 通知グループを作成、初期化し、 この通知グループ
       を参照するファイルディスクリプターを返す。

       fanotify 通知グループはカーネル内部のオブジェクトで、 イベントが作成されるファイル、ディレ
       クトリ、ファイルシステム、マウントポイントのリストを保持する。

       fanotify 通知グループの各エントリーには 2 つのビットマスクがある。 mark マスクと ignore マ
       スクである。  mark マスクはどのファイル操作についてイベントを作成するかを定義する。 ignore
       マスクはどの操作についてイベントを作成しないかを定義する。 これらの 2 種類のマスクがあるこ
       とで、  ファイルシステム、マウントポイント、ディレクトリに対してイベントの受信を mark して
       おきつつ、  同時にそのマウントポイントやディレクトリ配下の特定のオブジェクトに対するイベン
       トを無視する、 といったことができる。

       fanotify_mark(2)  システムコールは、ファイル、ディレクトリ、ファイルシステム、マウントポイ
       ントを通知グループに追加し、 どのイベントを報告 (もしくは無視) するかを指定する。 また、こ
       のようなエントリーの削除、変更も行う。

       ignore マスクの考えられる使用方法はファイルキャッシュに対してである。 ファイルキャッシュに
       関して興味のあるイベントは、ファイルの変更とファイルのクローズである。  それゆえ、  キャッ
       シュされたディレクトリやマウントポイントは、    これらのイベントを受信するようにマークされ
       る。 ファイルが変更されたという最初のイベントを受信した後は、 対応するキャッシュエントリー
       は無効化される。 そのファイルがクローズされるまでは、 このファイルに対する変更イベントは興
       味のない情報となる。 したがって、 変更イベントを ignore マスクに追加することができる。  ク
       ローズイベントを受信すると、 変更イベントを ignore イベントから削除し、 ファイルキャッシュ
       エントリーを更新することができる。

       The entries in the fanotify notification groups refer to files and directories  via  their
       inode  number  and  to  mounts via their mount ID.  If files or directories are renamed or
       moved within the same mount, the respective entries survive.  If files or directories  are
       deleted  or  moved  to  another  mount  or  if  filesystems  or  mounts are unmounted, the
       corresponding entries are deleted.

   イベントキュー
       通知グループにより監視されているファイルシステムオブジェクトでイベントが発生すると、
       fanotify  システムはイベントを生成し、 そのイベントはキューにまとめられる。 これらのイベン
       トは、 fanotify_init(2) が返した fanotify ファイルディスクリプターから (read(2) などを使っ
       て) 読み出すことができる。

       Two   types   of   events  are  generated:  notification  events  and  permission  events.
       Notification events are merely informative and require  no  action  to  be  taken  by  the
       receiving  application with one exception: if a valid file descriptor is provided within a
       generic event, the file descriptor must be closed.  Permission events are requests to  the
       receiving  application  to  decide  whether permission for a file access shall be granted.
       For these events, the recipient must write a response  which  decides  whether  access  is
       granted or not.

       イベントは、 読み出されると、 fanotify グループのイベントキューから削除される。 読み出され
       たアクセス許可イベントは、 fanotify ファイルディスクリプターにアクセス許可の判定が書き込ま
       れるか、 fanotify ファイルディスクリプターがクローズされるまで、 fanotify グループの内部の
       リストに保持される。

   fanotify イベントの読み出し
       fanotify_init(2)    が返したファイルディスクリプターに対する    read(2)     を呼び出しは、
       (fanotify_init(2) の呼び出しでフラグ FAN_NONBLOCK を指定しなかった場合) ファイルイベントが
       起こるか、呼び出しがシグナルによって割り込まれる (signal(7) 参照) まで停止する。

       The use of  one  of  the  flags  FAN_REPORT_FID,  FAN_REPORT_DIR_FID  in  fanotify_init(2)
       influences what data structures are returned to the event listener for each event.  Events
       reported to a group initialized with one of these flags will use file handles to  identify
       filesystem objects instead of file descriptors.

       read(2) が成功すると、
              読み出しバッファーには以下の構造体が 1 つ以上格納される。

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

       In  case  of  an  fanotify  group  that identifies filesystem objects by file handles, you
       should also expect to receive one or more additional information records of the  structure
       detailed  below  following  the  generic fanotify_event_metadata structure within the read
       buffer:

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

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

       性能上の理由から、複数のイベントを一度の read(2)  で取得できるように大きめのバッファーサイ
       ズ (例えば 4096 バイト) を使用することを推奨する。

       read(2)  の返り値はバッファーに格納されたバイト数である。 エラーの場合は -1 が返される (た
       だし、バグも参照)。

       fanotify_event_metadata 構造体のフィールドは以下のとおりである。

       event_len
              This is the length 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
              length file identifier records.

       vers   このフィールドには構造体のバージョン番号が入る。 実行時に返された構造体がコンパイル
              時の構造体と一致しているかを検査するには、  この値を FANOTIFY_METADATA_VERSION を比
              較すること。 一致しない場合、 アプリケーションはその fanotify  ファイルディスクリプ
              ターを使用するのを諦めるべきである。

       reserved
              このフィールドは使用されない。

       metadata_len
              この構造体の長さである。  このフィールドは、 イベント種別単位のオプションヘッダーの
              実装を扱うために導入された。   現在の実装ではこのようなオプションヘッダーは存在しな
              い。

       mask   イベントを示すビットマスクである (下記参照)

       fd     This  is  an  open  file descriptor for the object being accessed, or FAN_NOFD if a
              queue overflow occurred.  With an fanotify group that identifies filesystem objects
              by  file  handles,  applications should expect this value to be set to FAN_NOFD for
              each event that is received.  The  file  descriptor  can  be  used  to  access  the
              contents   of  the  monitored  file  or  directory.   The  reading  application  is
              responsible for closing this file descriptor.

              fanotify_init(2) を呼び出す際、  呼び出し元はこのファイルディスクリプターに対応する
              オープンファイル記述にセットされた様々なファイル状態フラグを  (event_f_flags 引数を
              使って) 指定することができる。 さらに、 (カーネル内部の) FMODE_NONOTIFY  ファイル状
              態フラグがオープンファイル記述にセットされる。 このフラグは fanotify イベントの生成
              を抑制する。 したがって、 fanotify イベントの受信者がこのファイルディスクリプターを
              使って通知されたファイルやディレクトリにアクセスした際に、 これ以上イベントが作成さ
              れなくなる。

       pid    If flag FAN_REPORT_TID was set in fanotify_init(2), this is the TID of  the  thread
              that  caused  the  event.   Otherwise,  this the PID of the process that caused the
              event.

       fanotify イベントを監視しているプログラムは、 この PID を getpid(2) が返す PID  と比較する
       ことで、 イベントが監視しているプログラム自身から発生したかどうか、 別のプロセスによるファ
       イルアクセスにより発生したか、を判定できる。

       mask のビットマスクは、1  つのファイルシステムオブジェクトに対してどのイベントが発生したか
       を示す。 監視対象のファイルシステムオブジェクトに複数のイベントが発生した場合は、 このマス
       クに複数のビットがセットされることがある。 特に、 同じファイルシステムオブジェクトに対する
       連続するイベントが同じプロセスから生成された場合には、  一つのイベントにまとめられることが
       ある。 例外として、 2 つのアクセス許可イベントが一つのキューエントリーにまとめられることは
       決してない。

       mask でセットされている可能性のあるビットは以下のとおりである。

       FAN_ACCESS
              ファイルやディレクトリがアクセスされた (読み出しが行われた) (ただし、「バグ」の節も
              参照)。

       FAN_OPEN
              ファイルやディレクトリがオープンされた。

       FAN_OPEN_EXEC
              A file was opened with the intent to be executed.  See  NOTES  in  fanotify_mark(2)
              for additional details.

       FAN_ATTRIB
              ファイルかディレクトリのメタデータが変更された。

       FAN_CREATE
              A child file or directory was created in a watched parent.

       FAN_DELETE
              A child file or directory was deleted in a watched parent.

       FAN_DELETE_SELF
              A watched file or directory was deleted.

       FAN_MOVED_FROM
              A file or directory has been moved from a watched parent directory.

       FAN_MOVED_TO
              A file or directory has been moved to a watched parent directory.

       FAN_MOVE_SELF
              監視対象のディレクトリやファイルが移動された。

       FAN_MODIFY
              ファイルが変更された。

       FAN_CLOSE_WRITE
              書き込み用 (O_WRONLYO_RDWR) にオープンされたファイルがクローズされた。

       FAN_CLOSE_NOWRITE
              読み出し用 (O_RDONLY) にオープンされたファイルがクローズされた。

       FAN_Q_OVERFLOW
              イベントキューが  16384 エントリーの上限を超過した。 この上限は fanotify_init(2) 呼
              び出し時に FAN_UNLIMITED_QUEUE フラグを指定することで上書きできる。

       FAN_ACCESS_PERM
              アプリケーションが例えば read(2) や readdir(2) などを使ってファイルやディレクトリを
              読み出そうとした。  このイベントを読み出したプログラムは、 そのファイルシステムオブ
              ジェクトへのアクセス許可を承認するかを判定し (下記で説明するとおり)  応答を書き込ま
              なければならない。

       FAN_OPEN_PERM
              アプリケーションがファイルやディレクトリをオープンしようとした。 このイベントを読み
              出したプログラムは、 そのファイルシステムオブジェクトのオープンを承認するかを判定し
              (下記で説明するとおり) 応答を書き込まなければならない。

       FAN_OPEN_EXEC_PERM
              An  application  wants  to  open  a  file  for  execution.  The reader must write a
              response that determines whether the permission to open the filesystem  object  for
              execution shall be granted.  See NOTES in fanotify_mark(2) for additional details.

       クローズイベントを確認するために以下のビットマスクを使うことができる。

       FAN_CLOSE
              ファイルがクローズされた。 以下の同義語である。

                  FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE

       移動イベントを確認するために以下のビットマスクを使うことができる。

       FAN_MOVE
              ファイルやディレクトリが移動された。 以下の同義語である。

                  FAN_MOVED_FROM | FAN_MOVED_TO

       The following bits may appear in mask only in conjunction with other event type bits:

       FAN_ONDIR
              The  events  described  in the mask have occurred on a directory object.  Reporting
              events  on  directories  requires  setting  this  flag  in  the  mark  mask.    See
              fanotify_mark(2)   for  additional  details.   The FAN_ONDIR flag is reported in an
              event mask only if  the  fanotify  group  identifies  filesystem  objects  by  file
              handles.

       fanotify_event_info_fid 構造体のフィールドは以下のとおりである。

       hdr    This  is  a  structure  of type fanotify_event_info_header.  It is a generic header
              that contains  information  used  to  describe  an  additional  information  record
              attached  to  the  event.  For example, when an fanotify file descriptor is created
              using FAN_REPORT_FID, a single information record is expected to be attached to the
              event with info_type field value of FAN_EVENT_INFO_TYPE_FID.  When an fanotify file
              descriptor   is   created   using   the   combination   of    FAN_REPORT_FID    and
              FAN_REPORT_DIR_FID, there may be two information records attached to the event: one
              with info_type  field  value  of  FAN_EVENT_INFO_TYPE_DFID,  identifying  a  parent
              directory  object,  and  one with info_type field value of FAN_EVENT_INFO_TYPE_FID,
              identifying a non-directory object.  The fanotify_event_info_header contains a  len
              field.  The value of len is the size of the additional information record including
              the  fanotify_event_info_header  itself.   The  total  size   of   all   additional
              information records is not expected to be bigger than ( event_len - metadata_len ).

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

       file_handle
              This  is  a  variable length 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).   Note  that  for  the
              directory  entry  modification  events  FAN_CREATE,  FAN_DELETE,  and FAN_MOVE, the
              file_handle identifies the modified directory  and  not  the  created/deleted/moved
              child  object.   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  file_handle  identifies the object correlated to the
              event.   If  the  value  of  info_type  field  is   FAN_EVENT_INFO_TYPE_DFID,   the
              file_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 file_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.

       fanotify ファイルディスクリプターからの read(2) が返した fanotify イベントメタデータを含む
       バッファーに対して繰り返しを行うため、 以下のマクロが提供されている。

       FAN_EVENT_OK(meta, len)
              このマクロは、  バッファー  meta の残りの長さ len を、 メタデータ構造体の長さとバッ
              ファーの最初のメタデータ構造体の event_len フィールドと比較して検査する。

       FAN_EVENT_NEXT(meta, len)
              This macro uses the length  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.  len 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 len by the number of bytes in the metadata structure
              that has been skipped over (i.e., it subtracts meta->event_len from len).

       また、 以下のマクロも用意されている。

       FAN_EVENT_METADATA_LEN
              このマクロは  fanotify_event_metadata 構造体の (バイト単位の) サイズを返す。 返され
              る値はイベントメタデータの最小値である (現在のところ、これが唯一のサイズである)。

   fanotify ファイルディスクリプターのイベントを監視する
       fanotify イベントが発生すると、 epoll(7), poll(2), select(2) に fanotify  ファイルディスク
       リプターが渡された場合には、そのファイルディスクリプターが読み出し可能であると通知される。

   アクセス許可イベントの取り扱い
       アクセス許可イベントでは、  アプリケーションは以下の形式の構造体を fanotify ファイルディス
       クリプターに write(2) しなければならない。

           struct fanotify_response {
               __s32 fd;
               __u32 response;
           };

       この構造体のフィールドは以下のとおりである。

       fd     このフィールドは fanotify_event_metadata 構造体で返されたファイルディスクリプターで
              ある。

       response
              このフィールドはアクセス許可を承認するかどうかを示す。 値は、このファイル操作を許可
              する FAN_ALLOW か、 このファイル操作を拒否する FAN_DENY  のいずれかでなければならな
              い。

       アクセスを拒否した場合、 アクセスを要求したアプリケーションは EPERM エラーを受け取ることに
       なる。

   fanotify ファイルディスクリプターのクローズ
       fanotify        通知グループを参照するすべてのファイルディスクリプターがクローズされると、
       fanotify    グループは解放され、    カーネルが再利用できるようにそのリソースは解放される。
       close(2) の際に、 処理中であったアクセス許可イベントには許可が設定される。

   /proc/[pid]/fdinfo
       ファイル /proc/[pid]/fdinfo/[fd] には、  プロセス  pid  のファイルディスクリプター  fd  の
       fanotify マークに関する情報が格納される。 詳細は proc(5) を参照。

エラー

       通常の  read(2) のエラーに加え、 fanotify ファイルディスクリプターから読み出しを行った際に
       以下のエラーが発生することがある。

       EINVAL バッファーがイベントを保持するには小さすぎる。

       EMFILE オープンしたファイル数のプロセス毎の上限に達した。 getrlimit(2) の RLIMIT_NOFILE の
              説明を参照。

       ENFILE オープンされたファイルの総数のシステム全体の上限に達した。        proc(5)        の
              /proc/sys/fs/file-max を参照。

       ETXTBSY
              fanotify_init(2) の呼び出し時に O_RDWRO_WRONLYevent_f_flags  引数に指定され
              ており、  現在実行中の監視対象のファイルに対してイベントが発生した際に、 このエラー
              が read(2) から返される。

       通常の write(2) のエラーに加え、 fanotify ファイルディスクリプターに書き込みを行った際に以
       下のエラーが発生することがある。

       EINVAL fanotify  アクセス許可がカーネルの設定で有効になっていない。  応答構造体の response
              値が無効である。

       ENOENT 応答構造体のファイルディスクリプター fd が無効である。 このエラーはアクセス許可イベ
              ントに対する応答がすでに書き込まれている際に発生する。

バージョン

       fanotify  API は Linux カーネルのバージョン 2.6.36 で導入され、 バージョン 2.6.37 で有効に
       された。 fdinfo のサポートはバージョン 3.8 で追加された。

準拠

       fanotify API は Linux 独自のものである。

注意

       fanotify API が利用できるのは、 カーネルが CONFIG_FANOTIFY  設定オプションを有効にして作成
       されている場合だけである。      また、     fanotify     アクセス許可の処理が利用できるのは
       CONFIG_FANOTIFY_ACCESS_PERMISSIONS 設定オプションが有効になっている場合だけである。

   制限と警告
       fanotify が報告するのはユーザー空間プログラムがファイルシステム API 経由で行ったイベントだ
       けである。  その結果、 fanotify ではネットワークファイルシステム上で発生したリモートイベン
       トは捕捉できない。

       inotify API は mmap(2), msync(2), munmap(2) により起こったファイルのアクセスと変更を報告し
       ない。

       ディレクトリのイベントは、ディレクトリ自身がオープン、読み出し、クローズされた場合にしか作
       成されない。  マークされたディレクトリでの子要素の追加、削除、変更では、監視対象のディレク
       トリ自身へのイベントは作成されない。

       Fanotify  monitoring  of  directories  is not recursive: to monitor subdirectories under a
       directory, additional marks must be  created.   The  FAN_CREATE  event  can  be  used  for
       detecting  when  a  subdirectory has been created under a marked directory.  An additional
       mark must then be set on the newly created subdirectory.  This approach is  racy,  because
       it  can  lose events that occurred inside the newly created subdirectory, before a mark is
       added on that subdirectory.  Monitoring mounts offers the capability to  monitor  a  whole
       directory  tree  in  a  race-free manner.  Monitoring filesystems offers the capability to
       monitor changes made from any mount of a filesystem instance in a race-free manner.

       ベントキューはオーバーフローすることがある。 この場合、 イベントは失われる。

バグ

       Before Linux 3.19, fallocate(2)  did not generate  fanotify  events.   Since  Linux  3.19,
       calls to fallocate(2)  generate FAN_MODIFY events.

       Linux 3.17 時点では、 以下のバグが存在する。

       *  Linux   では、ファイルシステムオブジェクトは複数のパスでアクセス可能である。   例えば、
          ファイルシステムの一部は mount(8) の --bind  オプションを使って再マウントされることがあ
          る。  マークされたマウントの監視者は、 同じマウントを使ったファイルオブジェクトについて
          のみイベント通知を受ける。 それ以外のイベントは通知されない。

       *  イベントが生成された際に、 そのファイルのファイルディスクリプターを渡す前に、  イベント
          を受信するプロセスのユーザー  ID がそのファイルに対する読み出し/書き込み許可があるかの
          確認は行われない。 非特権ユーザーによって実行されたプログラムに CAP_SYS_ADMIN  ケーパビ
          リティーがセットされている場合には、 このことはセキュリティーリスクとなる。

       *  read(2) の呼び出しが fanotify キューから複数のイベントを処理している際に、 エラーが発生
          した場合、 返り値はエラーが発生する前までにユーザー空間バッファーに正常にコピーされたイ
          ベントの合計長となる。  返り値は -1 にならず、 errno もセットされない。 したがって、 読
          み出しを行うアプリケーションではエラーを検出する方法はない。

       The two example programs below demonstrate the usage of the fanotify API.

   Example program: fanotify_example.c
       The first program is an example of fanotify being used with its event  object  information
       passed  in  the  form of a file descriptor.  The program marks the mount point passed as a
       command-line argument and waits for events  of  type  FAN_OPEN_PERM  and  FAN_CLOSE_WRITE.
       When a permission event occurs, a FAN_ALLOW response is given.

       The  following  shell  session  shows  an  example  of running this program.  This session
       involved  editing  the  file  /home/user/temp/notes.   Before  the  file  was  opened,   a
       FAN_OPEN_PERM  event  occurred.   After  the  file  was  closed,  a  FAN_CLOSE_WRITE event
       occurred.  Execution of the program ends when the user presses the ENTER key.

           # ./fanotify_example /home
           Press enter key to terminate.
           Listening for events.
           FAN_OPEN_PERM: File /home/user/temp/notes
           FAN_CLOSE_WRITE: File /home/user/temp/notes

           Listening for events stopped.

   プログラムのソース: fanotify_example.c

       #define _GNU_SOURCE     /* 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>

       /* ファイルディスクリプター 'fd' から読み出しできる全 fanotify イベントを読み出す */

       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;

           /* fanotify ファイルディスクリプターからイベントが読み出せる間はループする */

           for (;;) {

               /* イベントを読み出す */

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

               /* 読み出せるデータの最後に達しているかチェックする */

               if (len <= 0)
                   break;

               /* バッファーの最初のイベントを参照する */

               metadata = buf;

               /* バッファー内の全イベントを処理する */

               while (FAN_EVENT_OK(metadata, len)) {

                   /* 実行時とコンパイル時の構造体が一致するか確認する */

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

                   /* metadata->fd には、キューのオーバーフローを示す FAN_NOFD か、
                      ファイルディスクリプター (負でない整数) のいずれかが入っている。
                      ここではキューのオーバーフローは無視している。 */

                   if (metadata->fd >= 0) {

                       /* オープン許可イベントを処理する */

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

                           /* ファイルのオープンを許可する */

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

                       /* 書き込み可能ファイルのクローズイベントを処理する */

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

                       /* アクセスされたファイルのパス名を取得し表示する */

                       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(metadata->fd);
                   }

                   /* 次のイベントに進む */

                   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];

           /* マウントポイントが指定されたか確認する */

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

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

           /* 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);
           }

           /* 指定されたマウントに対して以下を監視するようにマークを付ける:
              - ファイルのオープン前のアクセス許可イベント
              - 書き込み可能なファイルディスクリプターのクローズ後の
                通知イベント */

           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);
           }

           /* ポーリングの準備 */

           nfds = 2;

           /* コンソールの入力 */

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

           /* fanotify の入力 */

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

           /* イベントの発生を待つループ */

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

           while (1) {
               poll_num = poll(fds, nfds, -1);
               if (poll_num == -1) {
                   if (errno == EINTR)     /* シグナルに割り込まれた場合 */
                       continue;           /* poll() を再開する */

                   perror("poll");         /* 予期しないエラー */
                   exit(EXIT_FAILURE);
               }

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

                       /* コンソールからの入力がある場合: 空の標準入力であれば終了 */

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

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

                       /* fanotify イベントがある場合 */

                       handle_events(fd);
                   }
               }
           }

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

   Example program: fanotify_fid.c
       The second program is an example of fanotify being  used  with  a  group  that  identifies
       objects  by  file  handles.   The  program marks the filesystem object that is passed as a
       command-line argument and waits until an event of type FAN_CREATE has occurred.  The event
       mask  indicates  which type of filesystem object—either a file or a directory—was created.
       Once all events have been read from the buffer  and  processed  accordingly,  the  program
       simply terminates.

       The  following  shell  sessions  show  two  different  invocations  of  this program, with
       different actions performed on a watched object.

       The first session shows a mark being placed  on  /home/user.   This  is  followed  by  the
       creation  of  a regular file, /home/user/testfile.txt.  This results in a FAN_CREATE event
       being generated and reported against the file's parent watched directory object  and  with
       the  created file name.  Program execution ends once all events captured within the buffer
       have been processed.

           # ./fanotify_fid /home/user
           Listening for events.
           FAN_CREATE (file created):
                   Directory /home/user has been modified.
                   Entry 'testfile.txt' is not a subdirectory.
           All events processed successfully. Program exiting.

           $ touch /home/user/testfile.txt              # In another terminal

       The second session shows a mark being placed on  /home/user.   This  is  followed  by  the
       creation of a directory, /home/user/testdir.  This specific action results in a FAN_CREATE
       event being generated and is reported with the FAN_ONDIR flag set  and  with  the  created
       directory name.

           # ./fanotify_fid /home/user
           Listening for events.
           FAN_CREATE | FAN_ONDIR (subdirectory created):
                   Directory /home/user has been modified.
                   Entry 'testdir' is a subdirectory.
           All events processed successfully. Program exiting.

           $ mkdir -p /home/user/testdir          # In another terminal

   プログラムのソース: 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, "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 */

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

               /* バッファー内の全イベントを処理する */

           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;

               /* 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);

                       /* 変更された 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);
       }

関連項目

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

この文書について

       この man ページは Linux man-pages プロジェクトのリリース 5.10 の一部である。プロジェクトの
       説明とバグ報告に関する情報は https://www.kernel.org/doc/man-pages/ に書かれている。