/
fsnotify_linux.go
135 lines (113 loc) · 2.66 KB
/
fsnotify_linux.go
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package fsnotify
import (
"bufio"
"encoding/binary"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"golang.org/x/sys/unix"
)
type Watcher struct {
fd_ int
rwb_ io.ReadWriter
}
// Procfs constants.
const (
ProcFsFd = "/proc/self/fd"
ProcFsFdInfo = "/proc/self/fdinfo"
)
func New() (*Watcher, error) {
fd, err := unix.FanotifyInit(unix.FAN_CLOEXEC|unix.FAN_CLASS_NOTIF|unix.FAN_NONBLOCK, unix.O_RDWR|unix.O_LARGEFILE)
if err != nil {
return nil, err
}
file := os.NewFile(uintptr(fd), "")
return &Watcher{
fd_: fd,
rwb_: bufio.NewReadWriter(bufio.NewReader(file), bufio.NewWriter(file)),
}, nil
}
func (w *Watcher) Mark(fileName string, act Action) error {
switch act {
case MarkAdd:
return unix.FanotifyMark(w.fd_, unix.FAN_MARK_ADD, unix.FAN_CLOSE_WRITE, unix.AT_FDCWD, fileName)
default:
return fmt.Errorf("not support act: %v", act)
}
}
func (w *Watcher) Wait() (Event, error) {
var (
metaData unix.FanotifyEventMetadata
err error
event Event
)
if err = binary.Read(w.rwb_, binary.LittleEndian, &metaData); err != nil {
return event, fmt.Errorf("fanotify read event error: %w", err)
}
if metaData.Vers != unix.FANOTIFY_METADATA_VERSION {
if err = unix.Close(int(metaData.Fd)); err != nil {
return event, fmt.Errorf("fanotify failed to close event fd: %w", err)
}
return event, fmt.Errorf("fanotify wrong metadata version")
}
event.Name_, err = getPath(metaData.Fd)
if err != nil {
return event, err
}
event.Op_ = newOp(metaData.Mask)
return event, nil
}
func (w *Watcher) ResponseAllow(ev *unix.FanotifyEventMetadata) error {
if err := binary.Write(
w.rwb_,
binary.LittleEndian,
&unix.FanotifyResponse{
Fd: ev.Fd,
Response: unix.FAN_ALLOW,
},
); err != nil {
return fmt.Errorf("fanotify: response error, %w", err)
}
return nil
}
// ResponseDeny sends a deny message back to fanotify, used for permission checks.
func (w *Watcher) ResponseDeny(ev *unix.FanotifyEventMetadata) error {
if err := binary.Write(
w.rwb_,
binary.LittleEndian,
&unix.FanotifyResponse{
Fd: ev.Fd,
Response: unix.FAN_DENY,
},
); err != nil {
return fmt.Errorf("fanotify: response error, %w", err)
}
return nil
}
func (w *Watcher) Close() error {
return unix.Close(w.fd_)
}
func newOp(mask uint64) Op {
if mask&unix.FAN_CLOSE_WRITE == unix.FAN_CLOSE_WRITE {
return Write
}
return Create
}
// getPath returns path to file for FD inside event metadata.
func getPath(fd int32) (string, error) {
path, err := os.Readlink(
filepath.Join(
ProcFsFd,
strconv.FormatUint(
uint64(fd),
10,
),
),
)
if err != nil {
return "", fmt.Errorf("fanotify get path error: %w", err)
}
return path, nil
}