日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

Golang之文件系统事件监听

發布時間:2024/1/21 windows 48 coder
生活随笔 收集整理的這篇文章主要介紹了 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之文件系统事件监听的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。