Golang之文件系统事件监听

打印 上一主题 下一主题

主题 897|帖子 897|积分 2691

Golang之文件系统事件监听

基本介绍

文件系统事件是指文件系统相关的各种操作和状态变化,当一个应用层的进程操作文件或目录时,会触发system call,内核的notification子系统可以守在那里,把该进程对文件的操作上报给应用层的监听进程。这些事件可以包括文件和目录的创建、修改、删除和文件权限的更改等。
Linux中常用的有两种机制能够监听这些文件事件,分别为inotify和fanotify。
inotify和fanotify最大的区别就是fanotify能够监听到是哪个进程对文件或目录进行操作,并且能够阻止该操作。
fanotify

fanotify:Linux 2.6.37版本引入,能够通知用户哪个进程触发了哪些事件,并且能够对其进行干预。
Golang中fanotify有两个函数:
  1. func FanotifyInit(flags uint, event_f_flags uint) (fd int, err error)
  2. 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参数包含一个多位字段,用于定义监听应用程序的通知类型,可选的值有:
    1. FAN_CLASS_CONTENT            = 0x4   适用于需要访问已经包含最终内容的文件的事件监听器
    2. FAN_CLASS_NOTIF              = 0x0   默认值,不需要指定,只用于监听,不访问文件内容
    3. FAN_CLASS_PRE_CONTENT        = 0x8   适用于需要在文件包含最终数据之前访问文件的事件监听器*/
    4. FAN_CLOEXEC                  = 0x1   如果在程序运行时打开了一个文件描述符,并且在调用时没有关闭,那么新程序中仍然能够使用该文件描述符,设置这个字段,可以确保调用时关闭文件描述符
    5. FAN_NONBLOCK                 = 0x2   为文件描述符启用非阻塞标志,读取文件描述符时不会被阻塞
    6. FAN_UNLIMITED_MARKS          = 0x20   取消对每个用户的通知标记数量的限制
    7. FAN_UNLIMITED_QUEUE          = 0x10   删除对事件队列中事件数量的限制
    8. FAN_REPORT_DFID_NAME         = 0xc00  这是(FAN_REPORT_DIR_FID|FAN_REPORT_NAME)的同义词
    9. FAN_REPORT_DFID_NAME_TARGET  = 0x1e00 这是(FAN_REPORT_DFID_NAME|FAN_REPORT_FID|FAN_REPORT_TARGET_FID)的同义词
    10. FAN_REPORT_DIR_FID           = 0x400  Linux 5.9后的功能,使用此标志初始化的通知组的事件将包含与事件相关的目录对象的附加信息
    11. FAN_REPORT_FID               = 0x200  Linux 5.1后的功能,使用此标志初始化的通知组的事件将包含相关的底层文件系统对象的附加信息
    12. FAN_REPORT_NAME              = 0x800  Linux 5.9后的功能,使用此标志初始化的通知组的事件将包含与事件相关的目录条目名称的附加信息
    13. FAN_REPORT_PIDFD             = 0x80   Linux 5.15后的功能,使用此标志初始化的事件将包含一个附加的信息记录
    14. FAN_REPORT_TARGET_FID        = 0x1000 Linux 5.17后的功能,使用此标志初始化的通知组的事件将包含与目录条目修改事件相关的子节点的附加信息
    15. FAN_REPORT_TID               = 0x100  Linux 4.20后的功能,报告线程ID(TID)而不是进程ID(PID)
    16. FAN_ENABLE_AUDIT             = 0x40   Linux 4.15后的功能,启用生成权限事件执行的访问中介的审计日志记录
    复制代码
    event_f_flags参数定义了文件描述符状态,可选的值有:
    1. O_RDONLY               = 0x0     只读
    2. O_RDWR                 = 0x2     读写
    3. O_WRONLY               = 0x1     只写
    4. O_LARGEFILE            = 0x0     启用对超过2gb的文件的支持。在32位系统上,
    5. O_CLOEXEC              = 0x80000 Linux 3.18后的功能为文件描述符启用close-on-exec标志
    6. 这些也是可以的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参数是描述要执行的操作,可选的值有:
    1. FAN_MARK_ADD                                = 0x1
    2. FAN_MARK_DONT_FOLLOW                        = 0x4
    3. FAN_MARK_EVICTABLE                          = 0x200 Linux 5.19后的功能
    4. FAN_MARK_FILESYSTEM                         = 0x100 Linux 4.20后的功能
    5. FAN_MARK_FLUSH                              = 0x80
    6. FAN_MARK_IGNORE                             = 0x400 Linux 6.0后的功能
    7. FAN_MARK_IGNORED_MASK                       = 0x20
    8. FAN_MARK_IGNORED_SURV_MODIFY                = 0x40
    9. FAN_MARK_IGNORE_SURV                        = 0x440
    10. FAN_MARK_INODE                              = 0x0
    11. FAN_MARK_MOUNT                              = 0x10
    12. FAN_MARK_ONLYDIR                            = 0x8
    13. FAN_MARK_REMOVE                             = 0x2
    复制代码
    mask参数定义了应该监听哪些事件或者忽略哪些事件,可选的值有:
    1. FAN_ACCESS                                  = 0x1
    2. FAN_ACCESS_PERM                             = 0x20000
    3. FAN_MODIFY                                  = 0x2
    4. FAN_CLOSE                                   = 0x18
    5. FAN_CLOSE_NOWRITE                           = 0x10
    6. FAN_CLOSE_WRITE                             = 0x8
    7. FAN_OPEN                                    = 0x20
    8. FAN_OPEN_EXEC                               = 0x1000     Linux 5.0后的功能
    9. FAN_OPEN_EXEC_PERM                          = 0x40000    Linux 5.0后的功能
    10. FAN_OPEN_PERM                               = 0x10000
    11. FAN_ATTRIB                                  = 0x4        Linux 5.1后的功能
    12. FAN_CREATE                                  = 0x100      Linux 5.1后的功能
    13. FAN_DELETE                                  = 0x200      Linux 5.1后的功能
    14. FAN_DELETE_SELF                             = 0x400      Linux 5.1后的功能
    15. FAN_FS_ERROR                                = 0x8000     Linux 5.16后的功能
    16. FAN_MOVE                                    = 0xc0
    17. FAN_MOVED_FROM                              = 0x40       Linux 5.1后的功能
    18. FAN_MOVED_TO                                = 0x80       Linux 5.1后的功能
    19. FAN_MOVE_SELF                               = 0x800      Linux 5.1后的功能
    20. FAN_RENAME                                  = 0x10000000 Linux 5.17后的功能
    21. FAN_ONDIR                                   = 0x40000000
    22. 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相对于当前目录的路径

示例

[code]package mainimport (        "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
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

海哥

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表