Golang之文件系统事件监听

这篇具有很好参考价值的文章主要介绍了Golang之文件系统事件监听。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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之文件系统事件监听文章来源地址https://www.toymoban.com/news/detail-801310.html

到了这里,关于Golang之文件系统事件监听的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【JavaScript】事件监听:表单事件(下篇)

    目录 八、keydown: 当用户按下键盘上的任意键时触发。 九、keyup: 当用户释放键盘上的键时触发。 十、keypress: 当用户按下键盘上的字符键时触发。 十一、focusin: 当表单元素或其子元素获得焦点时触发。 十二、focusout: 当表单元素或其子元素失去焦点时触发。 十三、cut: 当用户

    2024年01月24日
    浏览(44)
  • python 监听键盘事件和鼠标事件

    键盘监听: python有一个很强大的键盘监听库,那就是 keyboard 。他的父类库 pynput 可以实现鼠标监听 可以自行下载 pip install keyboard | pip install pynput 代码参考

    2024年02月13日
    浏览(42)
  • 详解如何在Golang中监听多个channel

    这篇文章主要为大家详细介绍了如何在Golang中实现监听多个channel,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下    我们可以使用select来同时监听多个goroutine。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package main import (   

    2024年02月13日
    浏览(88)
  • 微信小程序input监听键盘完成事件、回车事件

    微信小程序中,输入框输入内容,需要输入完成后点击键盘完成事件后触发搜索 bindconfirm=“事件名称”

    2024年02月14日
    浏览(47)
  • 前端监听键盘事件

    需求就如题所示 在react中监听如下: keydown事件是键盘的相关事件,我们主动去监听一下,然后别忘了去销毁, 然后有个写法: 在useEffect中使用return返回了一个清除监听的函数,实践了一下return的执行时机,发现是在页面切走的时候,也就是说,当前的组件被销毁的时候执

    2024年02月12日
    浏览(36)
  • 微信小程序——事件监听

    微信小程序是一种轻量级的应用程序,它在移动设备上提供了丰富的用户体验。在开发微信小程序时,事件监听是一项重要的技术,它允许开发者捕捉和处理用户的各种操作。本文将介绍微信小程序事件监听的概念、用法和一些实用示例。 在微信小程序中,事件监听是一种机

    2024年02月08日
    浏览(42)
  • C#全局监听键盘事件

    C#如何获取键盘和鼠标处于空闲状态下的时间  可以通过windows api 函数 GetLastInputInfo或者全局钩子HOOK来实现 用 GetLastInputInfo 写(键鼠空闲15分钟自动弹出一个页面) 新建windows 应用程序项目 用HOOK钩子来实现 创建钩子类 调用 方法

    2024年02月11日
    浏览(40)
  • vue鼠标悬停事件监听

    开发框架为 vue2.x 需求是这样的:页面在鼠标悬停(不动)n秒之后,页面进行相应的事件。 比如在我的需求下,是鼠标悬停15秒之后,页面上三个数据弹窗轮询展示。 我的思路中 涉及到了三个变量 polling: 是 轮询的时候的一个计时器 timeCount: 是 判断鼠标是否移动的一个控

    2023年04月12日
    浏览(46)
  • js监听鼠标滚轮事件

    通过原生js监听鼠标滚轮事件 通过jquery实现鼠标滚轮事件,若ie8无法实现,降低jquery版本

    2024年02月16日
    浏览(52)
  • vue监听滚动条事件

    2024年02月11日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包