Golang之文件系统事件监听

基本介绍

文件系统事件是指文件系统相关的各种操作和状态变化,当一个应用层的进程操作文件或目录时,会触发system call,内核的notification子系统可以守在那里,把该进程对文件的操作上报给应用层的监听进程。这些事件可以包括文件和目录的创建、修改、删除和文件权限的更改等。

Linux中常用的有两种机制能够监听这些文件事件,分别为inotify和fanotify。

inotify和fanotify最大的区别就是fanotify能够监听到是哪个进程对文件或目录进行操作,并且能够阻止该操作。

fanotify

fanotify:Linux 2.6.37版本引入,能够通知用户哪个进程触发了哪些事件,并且能够对其进行干预。

Golang中fanotify有两个函数:

func FanotifyInit(flags uint, event_f_flags uint) (fd int, err error)
func FanotifyMark(fd int, flags uint, mask uint64, dirFd int, pathname string) (err error)

函数介绍

  • func FanotifyInit(flags uint, event_f_flags uint) (fd int, err error)

    该函数初始化了一个新的fanotify事件组,并返回与该组关联的事件队列的文件描述符,文件描述符用来调用FanotifyMark函数,以指定应该为其创建fanotify事件的文件、目录、挂载或文件系统,通过读取文件描述符来接收这些事件。

    flags参数包含一个多位字段,用于定义监听应用程序的通知类型,可选的值有:

    FAN_CLASS_CONTENT            = 0x4   适用于需要访问已经包含最终内容的文件的事件监听器
    FAN_CLASS_NOTIF = 0x0 默认值,不需要指定,只用于监听,不访问文件内容
    FAN_CLASS_PRE_CONTENT = 0x8 适用于需要在文件包含最终数据之前访问文件的事件监听器*/
    FAN_CLOEXEC = 0x1 如果在程序运行时打开了一个文件描述符,并且在调用时没有关闭,那么新程序中仍然能够使用该文件描述符,设置这个字段,可以确保调用时关闭文件描述符
    FAN_NONBLOCK = 0x2 为文件描述符启用非阻塞标志,读取文件描述符时不会被阻塞
    FAN_UNLIMITED_MARKS = 0x20 取消对每个用户的通知标记数量的限制
    FAN_UNLIMITED_QUEUE = 0x10 删除对事件队列中事件数量的限制
    FAN_REPORT_DFID_NAME = 0xc00 这是(FAN_REPORT_DIR_FID|FAN_REPORT_NAME)的同义词
    FAN_REPORT_DFID_NAME_TARGET = 0x1e00 这是(FAN_REPORT_DFID_NAME|FAN_REPORT_FID|FAN_REPORT_TARGET_FID)的同义词
    FAN_REPORT_DIR_FID = 0x400 Linux 5.9后的功能,使用此标志初始化的通知组的事件将包含与事件相关的目录对象的附加信息
    FAN_REPORT_FID = 0x200 Linux 5.1后的功能,使用此标志初始化的通知组的事件将包含相关的底层文件系统对象的附加信息
    FAN_REPORT_NAME = 0x800 Linux 5.9后的功能,使用此标志初始化的通知组的事件将包含与事件相关的目录条目名称的附加信息
    FAN_REPORT_PIDFD = 0x80 Linux 5.15后的功能,使用此标志初始化的事件将包含一个附加的信息记录
    FAN_REPORT_TARGET_FID = 0x1000 Linux 5.17后的功能,使用此标志初始化的通知组的事件将包含与目录条目修改事件相关的子节点的附加信息
    FAN_REPORT_TID = 0x100 Linux 4.20后的功能,报告线程ID(TID)而不是进程ID(PID)
    FAN_ENABLE_AUDIT = 0x40 Linux 4.15后的功能,启用生成权限事件执行的访问中介的审计日志记录

    event_f_flags参数定义了文件描述符状态,可选的值有:

    O_RDONLY               = 0x0     只读
    O_RDWR = 0x2 读写
    O_WRONLY = 0x1 只写
    O_LARGEFILE = 0x0 启用对超过2gb的文件的支持。在32位系统上,
    O_CLOEXEC = 0x80000 Linux 3.18后的功能为文件描述符启用close-on-exec标志
    这些也是可以的O_APPEND,O_DSYNC,O_NOATIME,O_NONBLOCK,O_SYNC
  • func FanotifyMark(fd int, flags uint, mask uint64, dirFd int, pathname string) (err error)

    该函数在文件系统对象上添加、删除或修改fanotify标记,调用者必须对要标记的文件系统对象具有读权限。

    fd参数是由FanotifyInit函数返回的文件描述符。

    flags参数是描述要执行的操作,可选的值有:

    FAN_MARK_ADD                                = 0x1
    FAN_MARK_DONT_FOLLOW = 0x4
    FAN_MARK_EVICTABLE = 0x200 Linux 5.19后的功能
    FAN_MARK_FILESYSTEM = 0x100 Linux 4.20后的功能
    FAN_MARK_FLUSH = 0x80
    FAN_MARK_IGNORE = 0x400 Linux 6.0后的功能
    FAN_MARK_IGNORED_MASK = 0x20
    FAN_MARK_IGNORED_SURV_MODIFY = 0x40
    FAN_MARK_IGNORE_SURV = 0x440
    FAN_MARK_INODE = 0x0
    FAN_MARK_MOUNT = 0x10
    FAN_MARK_ONLYDIR = 0x8
    FAN_MARK_REMOVE = 0x2

    mask参数定义了应该监听哪些事件或者忽略哪些事件,可选的值有:

    FAN_ACCESS                                  = 0x1
    FAN_ACCESS_PERM = 0x20000
    FAN_MODIFY = 0x2
    FAN_CLOSE = 0x18
    FAN_CLOSE_NOWRITE = 0x10
    FAN_CLOSE_WRITE = 0x8
    FAN_OPEN = 0x20
    FAN_OPEN_EXEC = 0x1000 Linux 5.0后的功能
    FAN_OPEN_EXEC_PERM = 0x40000 Linux 5.0后的功能
    FAN_OPEN_PERM = 0x10000
    FAN_ATTRIB = 0x4 Linux 5.1后的功能
    FAN_CREATE = 0x100 Linux 5.1后的功能
    FAN_DELETE = 0x200 Linux 5.1后的功能
    FAN_DELETE_SELF = 0x400 Linux 5.1后的功能
    FAN_FS_ERROR = 0x8000 Linux 5.16后的功能
    FAN_MOVE = 0xc0
    FAN_MOVED_FROM = 0x40 Linux 5.1后的功能
    FAN_MOVED_TO = 0x80 Linux 5.1后的功能
    FAN_MOVE_SELF = 0x800 Linux 5.1后的功能
    FAN_RENAME = 0x10000000 Linux 5.17后的功能
    FAN_ONDIR = 0x40000000
    FAN_EVENT_ON_CHILD = 0x8000000

    要标记的文件系统对象由文件描述符dirFd和pathname中指定的路径名决定

    • 如果pathname为空,则由dirFd确定
    • 如果pathname为空,并且dirFd的值为AT_FDCWD,监听当前工作目录
    • 如果pathname是绝对路径,dirFd被忽略
    • 如果pathname是相对路径,并且dirFd不是AT_FDCWD,监听pathname相对于dirFd目录的路径
    • 如果pathname是相对路径,并且dirFd为AT_FDCWD,监听pathname相对于当前目录的路径

示例

package main

import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"log"
"os"
"path/filepath"
"unsafe" "golang.org/x/sys/unix"
) func handle_perm(initFd int, fanfd int32) error {
fd := unix.FanotifyResponse{
Fd: fanfd,
Response: uint32(unix.FAN_DENY),
}
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.LittleEndian, fd)
if err != nil {
log.Println(err)
}
ret, err := unix.Write(initFd, buf.Bytes())
if err != nil {
log.Println("handle_perm:err", err)
}
if ret < 0 {
return err
}
return nil
}
func main() {
path := "/root/testapp2/"
name := filepath.Clean(path)
initFd, err := unix.FanotifyInit(unix.FAN_CLOEXEC|unix.FAN_NONBLOCK|unix.FAN_CLASS_PRE_CONTENT, unix.O_RDONLY)
if err != nil {
log.Panicln("FanotifyInit err : ", err)
}
inotifyFile := os.NewFile(uintptr(initFd), "")
if initFd == -1 {
log.Println("fanFd err", err)
}
defer unix.Close(initFd)
mask := uint64(unix.FAN_EVENT_ON_CHILD | unix.FAN_OPEN_PERM)
err = unix.FanotifyMark(initFd, unix.FAN_MARK_ADD, mask, unix.AT_FDCWD, name)
if err != nil {
log.Panicln("FanotifyMark err : ", err)
}
fmt.Println("start:")
fmt.Println("监控目录:", name)
var (
buf [unix.FAN_EVENT_METADATA_LEN * 4096]byte
)
for {
n, err := inotifyFile.Read(buf[:])
if err != nil {
continue
}
if n < unix.FAN_EVENT_METADATA_LEN {
if n == 0 {
err = io.EOF
} else if n < 0 {
err = errors.New("notify: short ")
} else {
err = errors.New("notify: short read in readEvents()")
}
continue
}
var offset int
for offset <= int(n-unix.FAN_EVENT_METADATA_LEN) {
var (
raw = (*unix.FanotifyEventMetadata)(unsafe.Pointer(&buf[offset]))
pid = int32(raw.Pid)
event_len = uint32(raw.Event_len)
fd = int32(raw.Fd)
)
fdPath := fmt.Sprintf("/proc/self/fd/%d", fd)
f, err := os.Readlink(fdPath)
if err != nil {
log.Println(err)
} else {
fmt.Println("fdpath:", f)
}
proName := fmt.Sprintf("/proc/%d/comm", pid)
pN, err := os.ReadFile(proName)
if err != nil {
log.Println(err)
continue
}
if err := handle_perm(initFd, fd); err != nil {
continue
}
fmt.Printf("阻止程序: %v", string(pN))
offset += int(unix.FAN_EVENT_METADATA_LEN + event_len)
}
}
}

示例代码能够拒绝程序打开该目录下文件

Golang之文件系统事件监听的更多相关文章

  1. Java中用得比较顺手的事件监听

    第一次听说监听是三年前,做一个webGIS的项目,当时对Listener的印象就是个"监视器",监视着界面的一举一动,一有动静就触发对应的响应. 一.概述 通过对界面的某一或某些操 ...

  2. 4.JAVA之GUI编程事件监听机制

    事件监听机制的特点: 1.事件源 2.事件 3.监听器 4.事件处理 事件源:就是awt包或者swing包中的那些图形用户界面组件.(如:按钮) 事件:每一个事件源都有自己特点有的对应事件和共性事件. ...

  3. Node.js 教程 05 - EventEmitter(事件监听/发射器 )

    目录: 前言 Node.js事件驱动介绍 Node.js事件 注册并发射自定义Node.js事件 EventEmitter介绍 EventEmitter常用的API error事件 继承EventEm ...

  4. .NET事件监听机制的局限与扩展

    .NET中把“事件”看作一个基本的编程概念,并提供了非常优美的语法支持,对比如下C#和Java代码可以看出两种语言设计思想之间的差异. // C#someButton.Click += OnSomeB ...

  5. 让 select 的 option 标签支持事件监听(如复制操作)

    这标题,让option支持事件监听,应该不难的呀,有什么好讲的? 其实还是有的,默认在浏览器代码是无法直接对option标签进行操作的,不仅包括JS事件监听,还是CSS样式设置 查了一些资料,姑且认为 ...

  6. [JS]笔记12之事件机制--事件冒泡和捕获--事件监听--阻止事件传播

    -->事件冒泡和捕获-->事件监听-->阻止事件传播 一.事件冒泡和捕获 1.概念:当给子元素和父元素定义了相同的事件,比如都定义了onclick事件,点击子元素时,父元素的oncl ...

  7. [No00006A]Js的addEventListener()及attachEvent()区别分析【js中的事件监听】

    1.添加时间监听: Chrom中: addEventListener的使用方式: target.addEventListener(type, listener, useCapture); target ...

  8. java 事件监听 - 鼠标

    java 事件监听 - 鼠标 //事件监听 //鼠标事件监听 //鼠标事件监听有两个实现接口 //1.MouseListener 普通的鼠标操作 //2.MouseMotionListener 鼠标的 ...

  9. java 事件监听 - 键盘

    java 事件监听 - 键盘 //事件监听 //键盘事件监听,写了一个小案例,按上下左右,改变圆形的位置,圆形可以移动 import java.awt.*; import javax.swing.*; ...

  10. java 事件监听 - 控件

    java 事件监听 //事件监听 //事件监听,写了一个小案例,点击按钮改变面板的颜色. import java.awt.*; import javax.swing.*; import java.aw ...

随机推荐

  1. SQL模糊查询语法思考

    模糊查询 sql语句: SELECT 字段 FROM 表 WHERE 某字段 Like 条件 % :表示任意0个或多个字符.可匹配任意类型和长度的字符,有些情况下若是中文,请使用两个百分号(%%)表示 ...

  2. 论文阅读:2023_Semantic Hearing: Programming Acoustic Scenes with Binaural Hearables

    论文地址:语义听觉:用双耳可听器编程声学场景 论文代码:https://semantichearing.cs.washington.edu/ 引用格式:Veluri B, Itani M, Chan ...

  3. Java在指定路径下执行cmd命令的方法

    目前状态:毕业设计ing 背景: 做毕设时,由于需要将python的运行效果展示出来,所以使用了Java写了一个前端的界面.但是在使用Java对python的脚本进行调用时就尴尬了,出错-- 这里也许 ...

  4. C和C++练习

    要点: 1.数组 2.冒泡排序BubbleSort 3.带指针的结构体(malloc,free) 4.字符串操作(拷贝.逆序.比较) 5.格式化输出printf,sprintf 6.格式化输入,sca ...

  5. 【Datahub系列教程】Datahub入门必学——DatahubCLI之Docker命令详解

    大家好,我是独孤风,今天的元数据管理平台Datahub的系列教程,我们来聊一下Datahub CLI.也就是Datahub的客户端. 我们在安装和使用Datahub 的过程中遇到了很多问题. 如何安装 ...

  6. Swagger配置类

    Swagger配置类 package com.guoba.servicebase.config; import com.google.common.base.Predicates; import or ...

  7. Python笔记三之闭包与装饰器

    本文首发于公众号:Hunter后端 原文链接:Python笔记三之闭包与装饰器 这一篇笔记介绍 Python 里面的装饰器. 在介绍装饰器前,首先提出这样一个需求,我想统计某个函数的执行时间,假设这个 ...

  8. BUUCTF Web CyberPunk WriteUp

    想直接查看payload的点这里 前言 二次注入(Second-Order Injection)是指攻击者在应用程序中注入恶意数据,然后在稍后的操作或不同的上下文中再次使用该恶意数据,导致安全漏洞.它 ...

  9. Android学习--Intent

    Intent : Intent 是一个动作的完整描述,一种运行时的绑定机制,Intent中包含对Intent有兴趣的组件信息,如对动作的产生组件.接受组件和传递的数据信息.Android根据此Inte ...

  10. JavaFx FXML入门(五)

    JavaFx FXML入门(五) JavaFX 从入门入门到入土系列 JavaFx的FXML类似安卓中的视图文件,可以添加样式,添加css,添加id然后在java代码中绑定点击事件.可以使用工具编辑: ...