linux下c++使用系统调用fanotify进行文件监控

1、链接

2、主要结构体及其含义

  • fanotify_event_metadata
//具体可以在linux手册上找到解释
struct fanotify_event_metadata {
               __u32 event_len;			//当前事件的长度
               __u8 vers;				//保存该字段的版本号
               __u8 reserved;			//保留字段
               __u16 metadata_len;		//结构的长度
               __aligned_u64 mask;		//描述事件的掩码
               __s32 fd;				//被访问对象的打开文件描述符
               __s32 pid;				//如果在fanotify_init中设置FAN_REPORT_TID,则为导致事件的线程TID或PID
           };
  • mntent
struct mntent {
	char *mnt_fsname; /* 挂载的文件系统的名字 */
	char *mnt_dir; /* 挂载点 */
	char *mnt_type; /* 文件系统类型:ufs、nfs 等 */
	char *mnt_opts; /* 选项,以逗号为分隔符 */
	int mnt_freq; /* Dump 的频率(以天为单位) */
    int mnt_passno; /* fsck检查的次序 */
};
  • stat
struct stat {
        _dev_t     st_dev;        //文件所在磁盘驱动器号
        _ino_t     st_ino;        //inode,FAT、NTFS文件系统无意义
        unsigned short st_mode;   //文件、文件夹的标志
        short      st_nlink;      //非NTFS系统上通常为1
        short      st_uid;        //UNIX系统上为userid,windows上为0
        short      st_gid;        //UNIX系统上为groupid,windows上为0
        _dev_t     st_rdev;       //驱动器号,与st_dev相同
        _off_t     st_size;       //文件字节数
        time_t st_atime;          //上次访问时间
        time_t st_mtime;          //上次修改时间
        time_t st_ctime;          //创建时间
        };
  • fanotify_event_info_fid
struct fanotify_event_info_fid {
               struct fanotify_event_info_header hdr;
               __kernel_fsid_t fsid;
               unsigned char file_handle[0];
           };
  • fanotify_response
struct fanotify_response {
               __s32 fd;
               __u32 response;
           };

3、主要函数解释

linux手册:fanotify_init(unsigned int flags, unsigned int event_f_flags)

#include <fcntl.h>
#include <sys/fanotify.h>

int fanotify_init(unsigned int flags, unsigned int event_f_flags);
  • flags:(只能指定一个)
    • FAN_CLASS_PRE_CONTENT:
      • 该值允许接收通知文件的事件已被访问,如果有可以访问文件。适用于事件监听器需要在包含最终数据之前访问文件。 分层存储可能使用此通知类。
      • This value allows the receipt of events notifying that a file has been accessed and events for permission decisions if a file may be accessed. It is intended for event listeners that need to access files before they contain their final data. This notification class might be used by hierarchical storage managers, for example.
    • FAN_CLASS_CONTENT:
      • 该值允许接收通知已访问文件的事件,以及是否可以访问文件的权限决定事件。它适用于 需要在文件已经包含其最终内容时访问文件的事件侦听器。 例如,恶意软件检测程序可能会使用此通知类。
      • This value allows the receipt of events notifying that a file has been accessed and events for permission decisions if a file may be accessed. It is intended for event listeners that need to access files when they already contain their final content. This notification class might be used by malware detection programs, for example.
    • FAN_CLASS_NOTIF:
      • 这是默认值。不需要指定。该值仅允许接收通知已访问文件的事件。无法在访问文件之前做出权限决定。
      • This is the default value. It does not need to be specified. This value only allows the receipt of events notifying that a file has been accessed. Permission decisions before the file is accessed are not possible.
  • event_f_flags:
    • O_RDONLY:
      • 仅读
    • O_WRONLY:
      • 仅写
    • O_RDWR:
      • 读写

linux手册: int fanotify_mark(int fanotify_fd, unsigned int flags, uint64_t mask, int dirfd, const char *pathname);

#include <sys/fanotify.h>

int fanotify_mark(int fanotify_fd, unsigned int flags, uint64_t mask, int dirfd, const char *pathname);

4、编程实现(代码尚待优化)

  • 头文件
#include <unistd.h>
#include <linux/fanotify.h>
#include <sys/sysmacros.h>
#include <stdio.h>

#include "HString.h"

#define OPERATION_MV_STRING						L"mv"
#define OPERATION_NAUTILUS_STRING				L"nautilus"
  • mask2str
/*
 * 将描述事件的掩码转换为str
 */
static string mask2str (uint64_t mask)
{
	string str_mask;

	if(mask & FAN_OPEN)
	{
		str_mask += "[FAN_OPEN]";
	}

	if (mask & FAN_ACCESS)
	{
		str_mask += "[FAN_ACCESS]";
	}

	if (mask & FAN_MODIFY)
	{
		str_mask += "[FAN_MODIFY]";
	}

	if (mask & FAN_CLOSE_NOWRITE)
	{
		str_mask += "[FAN_CLOSE_NOWRITE]";
	}

	if (mask & FAN_CLOSE_WRITE)
	{
		str_mask += "[FAN_CLOSE_WRITE]";
	}

	return str_mask;
}
  • reset_flags
/*
 * 重置标志
 */
bool reset_flags(int m_fan_fd){
	if (m_fan_fd < 0){
		return false;
	}
	int err;
	int res;
	FILE* mounts;
	struct mntent* mount;
	//先清空
	fanotify_mark (m_fan_fd, FAN_MARK_FLUSH|FAN_MARK_MOUNT, FAN_ACCESS|FAN_MODIFY|FAN_OPEN|FAN_CLOSE|FAN_ONDIR|FAN_EVENT_ON_CHILD, AT_FDCWD, ".");
	res = fanotify_mark (m_fan_fd, FAN_MARK_ADD|FAN_MARK_MOUNT, FAN_ACCESS|FAN_MODIFY|FAN_OPEN|FAN_CLOSE|FAN_ONDIR|FAN_EVENT_ON_CHILD, AT_FDCWD, ".");
	if (res < 0) {
		err = errno;
		return false;
	}
    mounts = setmntent ("/proc/self/mounts", "r");
    if (mounts == NULL) {
		return false;
    }
    while ((mount = getmntent (mounts)) != NULL) {
        if (access (mount->mnt_fsname, F_OK) != 0) {
            continue;
        }
        res = fanotify_mark (m_fan_fd, FAN_MARK_ADD|FAN_MARK_MOUNT, FAN_ACCESS|FAN_MODIFY|FAN_OPEN|FAN_CLOSE|FAN_ONDIR|FAN_EVENT_ON_CHILD, AT_FDCWD, mount->mnt_dir);
    }
    endmntent (mounts);
}
  • deal_event
/*
 * 处理事件
 */
int deal_event(struct fanotify_event_metadata *data)
{
	int fd;
	ssize_t len = 0;
	static char printbuf[100];
	static char procname[100];
	static char pathname[PATH_MAX];
	struct stat st;
	
	//获取进程名
	snprintf (printbuf, sizeof (printbuf), "/proc/%i/comm", data->pid);
	fd = open (printbuf, O_RDONLY);
	if (fd >= 0) {
		len = read (fd, procname, sizeof (procname));
		while (len > 0 && procname[len-1] == '\n'){
			len--;
		}
	}
	if (fd >= 0){
		unistd::close (fd);
	}
	if (len > 0){
		procname[len] = '\0';
	} else {
		strcpy (procname, "unknown");//名称未知,直接返回,问题是会漏掉一些操作
	}
	//获取路径名称
	snprintf (printbuf, sizeof (printbuf), "/proc/self/fd/%i", data->fd);
	len = readlink (printbuf, pathname, sizeof (pathname));
	if (len < 0) {
		if (fstat (data->fd, &st) < 0) {
			//fstat error
			return 0;
		}
		snprintf (pathname, sizeof (pathname), "device %i:%i inode %ld\n", major (st.st_dev), minor (st.st_dev), st.st_ino);
	}else {
		pathname[len] = '\0';
	}
	HString strPathName = pathname; 
	HString strProcName = procname;
	//监测文件修改:
	vector<HString> vt_str;
	strPathName.get_vt_str_by_sep_trim_and_del_empty(vt_str, " ");
	HString temp=vt_str[0];
	if(temp.get_suffix()!="txt"&& temp.get_suffix()!="docx"&&
		temp.get_suffix()!="doc"&& temp.get_suffix()!="pdf"){
		std::cout<<"no choose"<<strPathName.get_str()<<std::endl;
		return 0;
	}
	if (data->mask & FAN_MODIFY){
        // 事件处理
		std::cout<<"process name: "<<strProcName.get_str()<<", pathname: "<< pathname << ", did: " << mask2str(data->mask) << std::endl;
		return 0;
	}
}
  • mian
int main(){
    int fd = 0;
    int res = 0;
    struct fanotify_event_metadata *data = NULL;
    int err;
    void *buffer = NULL;
    err = posix_memalign (&buffer, 4096, 4096);
	fd = fanotify_init(FAN_CLASS_CONTENT,O_RDWR);
    reset_flags(fd);
	while (1){
        res = read(fd,buffer,4096);
        if(res==0){
            break;
        }
        data = (struct fanotify_event_metadata *) buffer;
        while (res>0){
            deal_event(data);
			unistd::close (data->fd);
            data = FAN_EVENT_NEXT (data, res);
        }
	}
}

5、感谢

  • 首先感谢老师带我参加这次项目,有机会接触外面的世界。
  • 其次感谢公司同事(袁某)的帮助,以及许多封装好的头文件。

6、参考

https://blog.csdn.net/qq_36573828/article/details/99815868

https://blog.csdn.net/daiyudong2020/article/details/51695502

猜你喜欢

转载自blog.csdn.net/xieyipeng1998/article/details/107772485
今日推荐