Exemple #1
0
int main(int argc, char** argv) {
	int fan_fd, len;
	char buf[sizeof(struct fanotify_event_metadata)*1024];
	struct fanotify_event_metadata *metadata;
        
	if (argc != 3) {
		printf("Usage: %s <root-dir> <procfs>\n", argv[0]);
		return 1;
	}
        procfs = argv[2];

        fan_fd = fanotify_init(0, 0);
	if (fan_fd == -1) {
		perror("fanotify_init");
		return 1;
	}

        fanotify_mark(fan_fd, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_ACCESS, AT_FDCWD, argv[1]);
        if (fan_fd == -1) {
		perror("fanotify_mark");
                return 1;
        }
	while (1) {
		len = read(fan_fd, buf, sizeof(buf));
		metadata = (struct fanotify_event_metadata*)&buf;
		while (FAN_EVENT_OK(metadata, len)) {
			print_filename(metadata->fd);
			close(metadata->fd);
			metadata = FAN_EVENT_NEXT(metadata, len);
		}
	}
}
void
GonkDiskSpaceWatcher::OnFileCanReadWithoutBlocking(int aFd)
{
  struct fanotify_event_metadata* fem = nullptr;
  char buf[4096];
  struct statfs sfs;
  int32_t len, rc;

  do {
    len = read(aFd, buf, sizeof(buf));
  } while(len == -1 && errno == EINTR);

  // Bail out if the file is busy.
  if (len < 0 && errno == ETXTBSY) {
    return;
  }

  // We should get an exact multiple of fanotify_event_metadata
  if (len <= 0 || (len % FAN_EVENT_METADATA_LEN != 0)) {
    MOZ_CRASH("About to crash: fanotify_event_metadata read error.");
  }

  fem = reinterpret_cast<fanotify_event_metadata *>(buf);

  while (FAN_EVENT_OK(fem, len)) {
    rc = fstatfs(fem->fd, &sfs);
    if (rc < 0) {
      NS_WARNING("Unable to stat fan_notify fd");
    } else {
      bool firstRun = mFreeSpace == UINT64_MAX;
      mFreeSpace = sfs.f_bavail * sfs.f_bsize;
      // We change from full <-> free depending on the free space and the
      // low and high thresholds.
      // Once we are in 'full' mode we send updates for all size changes with
      // a minimum of time between messages or when we cross a size change
      // threshold.
      if (firstRun) {
        mIsDiskFull = mFreeSpace <= mLowThreshold;
        // Always notify the current state at first run.
        NotifyUpdate();
      } else if (!mIsDiskFull && (mFreeSpace <= mLowThreshold)) {
        mIsDiskFull = true;
        NotifyUpdate();
      } else if (mIsDiskFull && (mFreeSpace > mHighThreshold)) {
        mIsDiskFull = false;
        NotifyUpdate();
      } else if (mIsDiskFull) {
        if (mTimeout < TimeStamp::Now() - mLastTimestamp ||
            mSizeDelta < llabs(mFreeSpace - mLastFreeSpace)) {
          NotifyUpdate();
        }
      }
    }
    close(fem->fd);
    fem = FAN_EVENT_NEXT(fem, len);
  }
}
Exemple #3
0
/* Read all available fanotify events from the file descriptor 'fd' */
static void handle_events(int fd) {
    const struct fanotify_event_metadata *metadata;
    struct fanotify_event_metadata buf[200];
    ssize_t len;
    char path[PATH_MAX];
    ssize_t path_len;
    char procfd_path[PATH_MAX];
    struct fanotify_response response;

    /* Loop while events can be read from fanotify file descriptor */
    for(;;) {

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

        /* Check if end of available data reached */
        if (len <= 0)
            break;

        /* Point to the first event in the buffer */
        metadata = buf;

        /* Loop over all events in the buffer */
        while (FAN_EVENT_OK(metadata, len)) {

            /* Check that run-time and compile-time structures match */
            if (metadata->vers != FANOTIFY_METADATA_VERSION) {
                fprintf(stderr,
                        "Mismatch of fanotify metadata version.\n");
                exit(EXIT_FAILURE);
            }

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

                /* Handle open permission event */
                if (metadata->mask & FAN_OPEN_PERM) {
                    printf("FAN_OPEN_PERM: ");

                    /* Allow file to be opened */
                    response.fd = metadata->fd;
                    response.response = FAN_ALLOW;
                    write(fd, &response,
                          sizeof(struct fanotify_response));
                }

                /* Handle closing of writable file event */
                if (metadata->mask & FAN_CLOSE_WRITE)
                    printf("FAN_CLOSE_WRITE: ");

                /* Retrieve and print pathname of the accessed file */
                snprintf(procfd_path, sizeof(procfd_path),
                         "/proc/self/fd/%d", metadata->fd);
                path_len = readlink(procfd_path, path,
                                    sizeof(path) - 1);
                if (path_len == -1) {
                    perror("readlink");
                    exit(EXIT_FAILURE);
                }

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

                /* Close the file descriptor of the event */
                close(metadata->fd);
            }

            /* Advance to next event */
            metadata = FAN_EVENT_NEXT(metadata, len);
        }
    }
}
Exemple #4
0
/**
 * fanotify main loop
 * @todo simplify code (CCN is 12 already !)
 */
void fanotify_loop(main_struct_t *main_struct)
{
    struct pollfd fds[FD_POLL_MAX];
    struct signalfd_siginfo fdsi;
    char buffer[FANOTIFY_BUFFER_SIZE];
    ssize_t length = 0;
    struct fanotify_event_metadata *fe_mdata = NULL;
    GSList *dir_list_utf8 = NULL;


    gint signal_fd = 0;
    gint fanotify_fd = 0;

    if (main_struct != NULL)
        {
            signal_fd = main_struct->signal_fd;
            fanotify_fd = main_struct->fanotify_fd;


            /* Setup polling */
            fds[FD_POLL_SIGNAL].fd = signal_fd;
            fds[FD_POLL_SIGNAL].events = POLLIN;
            fds[FD_POLL_FANOTIFY].fd = fanotify_fd;
            fds[FD_POLL_FANOTIFY].events = POLLIN;

            dir_list_utf8 = transform_to_utf8_casefold(main_struct->opt->dirname_list);

            while (1)
                {
                    /* Block until there is something to be read */
                    if (poll(fds, FD_POLL_MAX, -1) < 0)
                        {
                            print_error(__FILE__, __LINE__, _("Couldn't poll(): '%s'\n"), strerror(errno));
                        }

                    /* Signal received ? */
                    if (fds[FD_POLL_SIGNAL].revents & POLLIN)
                        {
                            if (read(fds[FD_POLL_SIGNAL].fd, &fdsi, sizeof(fdsi)) != sizeof(fdsi))
                                {
                                    print_error(__FILE__, __LINE__, _("Couldn't read signal, wrong size read\n"));
                                }

                            /* Break loop if we got SIGINT or SIGTERM */
                            if (fdsi.ssi_signo == SIGINT || fdsi.ssi_signo == SIGTERM)
                                {
                                    stop_fanotify(main_struct->opt, main_struct->fanotify_fd);
                                    free_list(dir_list_utf8);
                                    break;
                                }

                            print_error(__FILE__, __LINE__, _("Received unexpected signal\n"));
                        }


                    /* fanotify event received ? */
                    if (fds[FD_POLL_FANOTIFY].revents & POLLIN)
                        {
                            /* Read from the FD. It will read all events available up to
                             * the given buffer size. */
                            if ((length = read(fds[FD_POLL_FANOTIFY].fd, buffer, FANOTIFY_BUFFER_SIZE)) > 0)
                                {
                                    fe_mdata = (struct fanotify_event_metadata *) buffer;

                                    while (FAN_EVENT_OK(fe_mdata, length))
                                        {
                                            event_process(main_struct, fe_mdata, dir_list_utf8);

                                            if (fe_mdata->fd > 0)
                                                {
                                                    close(fe_mdata->fd);
                                                }

                                            fe_mdata = FAN_EVENT_NEXT(fe_mdata, length);
                                        }
                                }
                        }
                }

        }

}
Exemple #5
0
void *onas_fan_th(void *arg)
{
	struct thrarg *tharg = (struct thrarg *) arg;
	sigset_t sigset;
        struct sigaction act;
	const struct optstruct *pt;
	short int scan;
	int sizelimit = 0, extinfo;
	STATBUF sb;
        uint64_t fan_mask = FAN_EVENT_ON_CHILD | FAN_CLOSE;
        fd_set rfds;
	char buf[4096];
	ssize_t bread;
	struct fanotify_event_metadata *fmd;
	char fname[1024];
	int ret, len;
	char err[128];

	pthread_attr_t ddd_attr;
	struct ddd_thrarg *ddd_tharg = NULL;

	ddd_pid = 0;

    /* ignore all signals except SIGUSR1 */
    sigfillset(&sigset);
    sigdelset(&sigset, SIGUSR1);
    /* The behavior of a process is undefined after it ignores a 
     * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
    sigdelset(&sigset, SIGFPE);
    sigdelset(&sigset, SIGILL);
    sigdelset(&sigset, SIGSEGV);
#ifdef SIGBUS    
    sigdelset(&sigset, SIGBUS);
#endif
    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = onas_fan_exit;
    sigfillset(&(act.sa_mask));
    sigaction(SIGUSR1, &act, NULL);
    sigaction(SIGSEGV, &act, NULL);

    /* Initialize fanotify */
    onas_fan_fd = fanotify_init(FAN_CLASS_CONTENT | FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS, O_LARGEFILE | O_RDONLY);
    if(onas_fan_fd < 0) {
	logg("!ScanOnAccess: fanotify_init failed: %s\n", cli_strerror(errno, err, sizeof(err)));
	if(errno == EPERM)
	    logg("ScanOnAccess: clamd must be started by root\n");
	return NULL;
    }

    if (!tharg) {
	logg("!Unable to start on-access scanner. Bad thread args.\n");
	return NULL;
    }


    if (optget(tharg->opts, "OnAccessPrevention")->enabled && !optget(tharg->opts, "OnAccessMountPath")->enabled) {
	    logg("ScanOnAccess: preventing access attempts on malicious files.\n");
	    fan_mask |= FAN_ACCESS_PERM | FAN_OPEN_PERM;
    } else {
	    logg("ScanOnAccess: notifying only for access attempts.\n");
	    fan_mask |= FAN_ACCESS | FAN_OPEN;
    }

    if ((pt = optget(tharg->opts, "OnAccessMountPath"))->enabled) {
	    while(pt) {
		    if(fanotify_mark(onas_fan_fd, FAN_MARK_ADD | FAN_MARK_MOUNT, fan_mask, onas_fan_fd, pt->strarg) != 0) {
			    logg("!ScanOnAccess: Can't include mountpoint '%s'\n", pt->strarg);
			    return NULL;
		    } else
			    logg("ScanOnAccess: Protecting '%s' and rest of mount.\n", pt->strarg);
		    pt = (struct optstruct *) pt->nextarg;
	    }

    } else if (!optget(tharg->opts, "OnAccessDisableDDD")->enabled) {
	    do {
		    if(pthread_attr_init(&ddd_attr)) break;
		    pthread_attr_setdetachstate(&ddd_attr, PTHREAD_CREATE_JOINABLE);

		    if(!(ddd_tharg = (struct ddd_thrarg *) malloc(sizeof(struct ddd_thrarg)))) break;

		    ddd_tharg->fan_fd = onas_fan_fd;
		    ddd_tharg->fan_mask = fan_mask;
		    ddd_tharg->opts = tharg->opts;
		    ddd_tharg->engine = tharg->engine;
		    ddd_tharg->options = tharg->options;

		    if(!pthread_create(&ddd_pid, &ddd_attr, onas_ddd_th, ddd_tharg)) break;

		    free(ddd_tharg);
		    ddd_tharg=NULL;
	    } while(0);
	    if (!ddd_tharg) logg("!Unable to start dynamic directory determination.\n");

    } else {
	    if((pt = optget(tharg->opts, "OnAccessIncludePath"))->enabled) {
		    while(pt) {
			    if(fanotify_mark(onas_fan_fd, FAN_MARK_ADD, fan_mask, onas_fan_fd, pt->strarg) != 0) {
				    logg("!ScanOnAccess: Can't include path '%s'\n", pt->strarg);
				    return NULL;
			    } else
				    logg("ScanOnAccess: Protecting directory '%s'\n", pt->strarg);
			    pt = (struct optstruct *) pt->nextarg;
		    }
	    } else {
		    logg("!ScanOnAccess: Please specify at least one path with OnAccessIncludePath\n");
		    return NULL;
	    }
    }

    /* Load other options. */
    sizelimit = optget(tharg->opts, "OnAccessMaxFileSize")->numarg;
    if(sizelimit)
	logg("ScanOnAccess: Max file size limited to %d bytes\n", sizelimit);
    else
	logg("ScanOnAccess: File size limit disabled\n");

    extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled;

    FD_ZERO(&rfds);
    FD_SET(onas_fan_fd, &rfds);
    do {
	if (reload) sleep(1);
        ret = select(onas_fan_fd + 1, &rfds, NULL, NULL, NULL);
    } while((ret == -1 && errno == EINTR) || reload);


    time_t start = time(NULL) - 30;
    while(((bread = read(onas_fan_fd, buf, sizeof(buf))) > 0) || errno == EOVERFLOW) {

	if (errno == EOVERFLOW) {
		if (time(NULL) - start >= 30) {
			logg("!ScanOnAccess: Internal error (failed to read data) ... %s\n", strerror(errno));
			logg("!ScanOnAccess: File too large for fanotify ... recovering and continuing scans...\n");
			start = time(NULL);
		}

		errno = 0;
		continue;
	}

	fmd = (struct fanotify_event_metadata *) buf;
	while(FAN_EVENT_OK(fmd, bread)) {
	    scan = 1;
	    if(fmd->fd >= 0) {
		sprintf(fname, "/proc/self/fd/%d", fmd->fd);
		len = readlink(fname, fname, sizeof(fname) - 1);
		if(len == -1) {
		    close(fmd->fd);
		    logg("!ScanOnAccess: Internal error (readlink() failed)\n");
		    return NULL;
		}
		fname[len] = 0;

		if(onas_fan_checkowner(fmd->pid, tharg->opts)) {
		    scan = 0;
		    logg("*ScanOnAccess: %s skipped (excluded UID)\n", fname);
		}

		if(sizelimit) {
		    if(FSTAT(fmd->fd, &sb) != 0 || sb.st_size > sizelimit) {
			scan = 0;
			/* logg("*ScanOnAccess: %s skipped (size > %d)\n", fname, sizelimit); */
		    }
		}

		if(onas_fan_scanfile(onas_fan_fd, fname, fmd, scan, extinfo, tharg) == -1) {
		    close(fmd->fd);
		    return NULL;
		}

		if(close(fmd->fd) == -1) {
		    printf("!ScanOnAccess: Internal error (close(%d) failed)\n", fmd->fd);
		    close(fmd->fd);
		    return NULL;
		}
	    }
	    fmd = FAN_EVENT_NEXT(fmd, bread);
	}
	do {
	    if (reload) sleep(1);
	    ret = select(onas_fan_fd + 1, &rfds, NULL, NULL, NULL);
	} while((ret == -1 && errno == EINTR) || reload);
    }

    if(bread < 0)
	logg("!ScanOnAccess: Internal error (failed to read data) ... %s\n", strerror(errno));

    return NULL;
}
void ScannerThread::handleFanotifyEvent()
{
    char buf[(FAN_EVENT_METADATA_LEN / 2) * 3];
    ssize_t len;
    int fanotifyfd = m_fanotifyfd;


    setState(ScannerThread::BEFORE_READ);
    len = ::read(fanotifyfd, buf, sizeof(buf));
    setState(ScannerThread::AFTER_READ);
    if (len <= 0)
    {
        // nothing actually there - maybe another thread got it
        if (errno != EINTR && errno != EAGAIN)
        {
            PRINT(m_thread<<" no event or error: " << len << " (" << errno <<" "<<strerror(errno)<< ")");
        }
        return;
    }

    struct fanotify_event_metadata* metadata = reinterpret_cast<struct fanotify_event_metadata*>(buf);

    for (; FAN_EVENT_OK(metadata, len); metadata = FAN_EVENT_NEXT(metadata, len))
    {
        if (metadata->vers < 2)
        {
            PRINT("fanotify kernel version too old");
            throw "FIXME"; // TODO: Throw proper exception
        }

        if (metadata->vers != FANOTIFY_METADATA_VERSION)
        {
            // TODO?
            PRINT("fanotify wrong protocol version " << metadata->vers);
            throw "FIXME2"; // TODO: Throw proper exception
        }

        if ((metadata->mask & FAN_ALL_PERM_EVENTS) == 0)
        {
            close(metadata->fd);
            continue;
        }

        std::string path = getPath(metadata->fd);

        //~ {
            //~ struct stat fstatbuf;
            //~ int ret = ::fstat(metadata->fd, &fstatbuf);
            //~ if (ret < 0)
            //~ {
                //~ PRINT("Failed to fstat for "<<metadata->pid <<" "<<path);
            //~ }
        //~ }

        {
            struct fanotify_response response_struct;
            ssize_t ret;

            response_struct.fd = metadata->fd;
            response_struct.response = FAN_ALLOW;

            PRINT(m_thread<< " Responding to fanotify event for "<<metadata->pid << " "<<path);
            setState(ScannerThread::BEFORE_WRITE);
            ret = ::write(fanotifyfd, &response_struct, sizeof(response_struct));
            setState(ScannerThread::AFTER_WRITE);
            if (ret != sizeof(response_struct))
            {
                PRINT(m_thread<<" response error " << ret << " (" << errno <<" "<<strerror(errno)<< ")");
            }
        }


        {
            unsigned int flags = FAN_MARK_ADD | FAN_MARK_IGNORED_MASK;
            int ret = fanotify_mark(fanotifyfd,flags, FAN_OPEN_PERM, metadata->fd, NULL);
            if (ret < 0)
            {
                PRINT(m_thread<<" adding cache mark failed: " << ret);
            }
        }

        if (metadata->fd >= 0)
        {
            ::close(metadata->fd);
        }
    }
}
Exemple #7
0
void *fan_th(void *arg)
{
	struct thrarg *tharg = (struct thrarg *) arg;
	sigset_t sigset;
        struct sigaction act;
	const struct optstruct *pt;
	short int scan;
	int sizelimit = 0, extinfo;
	STATBUF sb;
        uint64_t fan_mask = FAN_ACCESS | FAN_EVENT_ON_CHILD;
	int fan_fd;
        fd_set rfds;
	char buf[4096];
	ssize_t bread;
	struct fanotify_event_metadata *fmd;
	char fname[1024];
	int ret, len;
	char err[128];

    /* ignore all signals except SIGUSR1 */
    sigfillset(&sigset);
    sigdelset(&sigset, SIGUSR1);
    /* The behavior of a process is undefined after it ignores a 
     * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
    sigdelset(&sigset, SIGFPE);
    sigdelset(&sigset, SIGILL);
    sigdelset(&sigset, SIGSEGV);
#ifdef SIGBUS    
    sigdelset(&sigset, SIGBUS);
#endif
    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = fan_exit;
    sigfillset(&(act.sa_mask));
    sigaction(SIGUSR1, &act, NULL);
    sigaction(SIGSEGV, &act, NULL);

    fan_fd = fanotify_init(0, O_RDONLY);
    if(fan_fd < 0) {
	logg("!ScanOnAccess: fanotify_init failed: %s\n", cli_strerror(errno, err, sizeof(err)));
	if(errno == EPERM)
	    logg("ScanOnAccess: clamd must be started by root\n");
	return NULL;
    }

    if((pt = optget(tharg->opts, "OnAccessIncludePath"))->enabled) {
	while(pt) {
	    if(fanotify_mark(fan_fd, FAN_MARK_ADD, fan_mask, fan_fd, pt->strarg) != 0) {
		logg("!ScanOnAccess: Can't include path '%s'\n", pt->strarg);
		return NULL;
	    } else
		logg("ScanOnAccess: Protecting directory '%s'\n", pt->strarg);
	    pt = (struct optstruct *) pt->nextarg;
	}
    } else {
	logg("!ScanOnAccess: Please specify at least one path with OnAccessIncludePath\n");
	return NULL;
    }

    if((pt = optget(tharg->opts, "OnAccessExcludePath"))->enabled) {
	while(pt) {
            if(fanotify_mark(fan_fd, FAN_MARK_REMOVE, fan_mask, fan_fd, pt->strarg) != 0) {
		logg("!ScanOnAccess: Can't exclude path %s\n", pt->strarg);
		return NULL;
	    } else
		logg("ScanOnAccess: Excluded path %s\n", pt->strarg);
	    pt = (struct optstruct *) pt->nextarg;
	}
    }

    sizelimit = optget(tharg->opts, "OnAccessMaxFileSize")->numarg;
    if(sizelimit)
	logg("ScanOnAccess: Max file size limited to %d bytes\n", sizelimit);
    else
	logg("ScanOnAccess: File size limit disabled\n");

    extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled;

    FD_ZERO(&rfds);
    FD_SET(fan_fd, &rfds);
    do {
        ret = select(fan_fd + 1, &rfds, NULL, NULL, NULL);
    } while(ret == -1 && errno == EINTR);

    while((bread = read(fan_fd, buf, sizeof(buf))) > 0) {
	fmd = (struct fanotify_event_metadata *) buf;
	while(FAN_EVENT_OK(fmd, bread)) {
	    scan = 1;
	    if(fmd->fd >= 0) {
		sprintf(fname, "/proc/self/fd/%d", fmd->fd);
		len = readlink(fname, fname, sizeof(fname) - 1);
		if(len == -1) {
		    close(fmd->fd);
		    logg("!ScanOnAccess: Internal error (readlink() failed)\n");
		    return NULL;
		}
		fname[len] = 0;

		if(fan_checkowner(fmd->pid, tharg->opts)) {
		    scan = 0;
		    logg("*ScanOnAccess: %s skipped (excluded UID)\n", fname);
		}

		if(sizelimit) {
		    if(FSTAT(fmd->fd, &sb) != 0 || sb.st_size > sizelimit) {
			scan = 0;
			/* logg("*ScanOnAccess: %s skipped (size > %d)\n", fname, sizelimit); */
		    }
		}

		if(fan_scanfile(fan_fd, fname, fmd, scan, extinfo, tharg) == -1) {
		    close(fmd->fd);
		    return NULL;
		}

		if(close(fmd->fd) == -1) {
		    printf("!ScanOnAccess: Internal error (close(%d) failed)\n", fmd->fd);
		    close(fmd->fd);
		    return NULL;
		}
	    }
	    fmd = FAN_EVENT_NEXT(fmd, bread);
	}
	do {
	    ret = select(fan_fd + 1, &rfds, NULL, NULL, NULL);
	} while(ret == -1 && errno == EINTR);
    }

    if(bread < 0)
	logg("!ScanOnAccess: Internal error (failed to read data)\n");

    return NULL;
}
Exemple #8
0
/* ファイルディスクリプター '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, (void *) &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(struct fanotify_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);
        }
    }
}