Skip to content

Commit a6fe6f8

Browse files
committed
Add tests
- Add tests for each of the Action types
1 parent 58334aa commit a6fe6f8

File tree

2 files changed

+286
-23
lines changed

2 files changed

+286
-23
lines changed

fanotify_event_types.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ const (
1212
// FileModified event when a file is modified
1313
FileModified Action = unix.FAN_MODIFY
1414

15-
// FileClosed event when a file is closed
15+
// FileClosedAfterWrite event when a file is closed
16+
FileClosedAfterWrite Action = unix.FAN_CLOSE_WRITE
17+
18+
// FileClosedWithNoWrite event when a file is closed without writing
19+
FileClosedWithNoWrite Action = unix.FAN_CLOSE_NOWRITE
20+
21+
// FileClosed event when a file is closed after write or no write
1622
FileClosed Action = unix.FAN_CLOSE_WRITE | unix.FAN_CLOSE_NOWRITE
1723

1824
// FileOpened event when a file is opened

fanotify_test.go

Lines changed: 279 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ import (
1212
"golang.org/x/sys/unix"
1313
)
1414

15+
//
16+
// TestWithCapSysAdm* tests require CAP_SYS_ADM privilege.
17+
// Run tests with sudo or as root -
18+
// sudo go test -v
19+
1520
func TestNewListenerInvalidFlagClassContent(t *testing.T) {
1621
var invalidFlag uint
1722
var eventFlags uint
@@ -42,33 +47,25 @@ func TestNewListenerValidFlags(t *testing.T) {
4247
assert.NotNil(t, l)
4348
}
4449

45-
// func testKernelVersion(t *testing.T) {
46-
// maj, min, patch, err := kernelVersion()
47-
// assert.Equal(t, maj, 5)
48-
// assert.Equal(t, min, 15)
49-
// assert.Equal(t, patch, 0)
50-
// assert.Nil(t, err)
51-
// }
52-
53-
func catFile(filename string) (int, error) {
54-
cmd := exec.Command("cat", filename)
55-
err := cmd.Run()
56-
if err != nil {
57-
return 0, err
50+
func runAsCmd(args ...string) (int, error) {
51+
var cmd *exec.Cmd
52+
if len(args) == 0 {
53+
return 0, errors.New("missing command name")
54+
}
55+
cmdName := args[0]
56+
cmdArgs := args[1:]
57+
if len(args) == 1 {
58+
cmd = exec.Command(cmdName)
59+
} else {
60+
cmd = exec.Command(cmdName, cmdArgs...)
5861
}
59-
return cmd.Process.Pid, nil
60-
}
61-
62-
func modifyFile(filename string) (int, error) {
63-
cmd := exec.Command("touch", "-m", filename)
6462
err := cmd.Run()
6563
if err != nil {
6664
return 0, err
6765
}
6866
return cmd.Process.Pid, nil
6967
}
7068

71-
// TestWithCapSysAdmFanotifyFileAccessed requires CAP_SYS_ADM privilege.
7269
func TestWithCapSysAdmFanotifyFileAccessed(t *testing.T) {
7370
l, err := NewListener("/", 4096, true)
7471
assert.Nil(t, err)
@@ -85,7 +82,7 @@ func TestWithCapSysAdmFanotifyFileAccessed(t *testing.T) {
8582
err = os.WriteFile(testFile, data, 0666)
8683
assert.Nil(t, err)
8784
t.Logf("Test file created %s", testFile)
88-
pid, err := catFile(testFile)
85+
pid, err := runAsCmd("cat", testFile)
8986
assert.Nil(t, err)
9087
select {
9188
case <-time.After(100 * time.Millisecond):
@@ -98,7 +95,6 @@ func TestWithCapSysAdmFanotifyFileAccessed(t *testing.T) {
9895
}
9996
}
10097

101-
// TestWithCapSysAdmFanotifyFileModified requires CAP_SYS_ADM privilege.
10298
func TestWithCapSysAdmFanotifyFileModified(t *testing.T) {
10399
l, err := NewListener("/", 4096, true)
104100
assert.Nil(t, err)
@@ -115,7 +111,7 @@ func TestWithCapSysAdmFanotifyFileModified(t *testing.T) {
115111
l.AddWatch(watchDir, action)
116112
go l.Start()
117113
defer l.Stop()
118-
pid, err := modifyFile(testFile)
114+
pid, err := runAsCmd("touch", "-m", testFile)
119115
assert.Nil(t, err)
120116
select {
121117
case <-time.After(100 * time.Millisecond):
@@ -127,3 +123,264 @@ func TestWithCapSysAdmFanotifyFileModified(t *testing.T) {
127123
assert.True(t, isModifed)
128124
}
129125
}
126+
127+
func TestWithCapSysAdmFanotifyFileClosed(t *testing.T) {
128+
l, err := NewListener("/", 4096, true)
129+
assert.Nil(t, err)
130+
assert.NotNil(t, l)
131+
watchDir := t.TempDir()
132+
133+
t.Logf("Watch Directory: %s", watchDir)
134+
data := []byte("test data...")
135+
testFile := fmt.Sprintf("%s/test.dat", watchDir)
136+
err = os.WriteFile(testFile, data, 0666)
137+
assert.Nil(t, err)
138+
t.Logf("Test file created %s", testFile)
139+
action := FileClosed
140+
l.AddWatch(watchDir, action)
141+
go l.Start()
142+
defer l.Stop()
143+
pid, err := runAsCmd("cat", testFile)
144+
assert.Nil(t, err)
145+
select {
146+
case <-time.After(100 * time.Millisecond):
147+
t.Error("Timeout Error: FileClosed event not received")
148+
case event := <-l.Events:
149+
assert.Equal(t, fmt.Sprintf("%s/%s", event.Path, event.FileName), testFile)
150+
assert.Equal(t, event.Pid, pid)
151+
closeNoWrite := (event.Mask & FileClosedWithNoWrite) == FileClosedWithNoWrite
152+
assert.True(t, closeNoWrite)
153+
}
154+
}
155+
156+
func TestWithCapSysAdmFanotifyFileOpen(t *testing.T) {
157+
l, err := NewListener("/", 4096, true)
158+
assert.Nil(t, err)
159+
assert.NotNil(t, l)
160+
watchDir := t.TempDir()
161+
162+
t.Logf("Watch Directory: %s", watchDir)
163+
data := []byte("test data...")
164+
testFile := fmt.Sprintf("%s/test.dat", watchDir)
165+
err = os.WriteFile(testFile, data, 0666)
166+
assert.Nil(t, err)
167+
t.Logf("Test file created %s", testFile)
168+
action := FileOpened
169+
l.AddWatch(watchDir, action)
170+
go l.Start()
171+
defer l.Stop()
172+
pid, err := runAsCmd("cat", testFile)
173+
assert.Nil(t, err)
174+
select {
175+
case <-time.After(100 * time.Millisecond):
176+
t.Error("Timeout Error: FileOpened event not received")
177+
case event := <-l.Events:
178+
assert.Equal(t, fmt.Sprintf("%s/%s", event.Path, event.FileName), testFile)
179+
assert.Equal(t, event.Pid, pid)
180+
opened := (event.Mask & FileOpened) == FileOpened
181+
assert.True(t, opened)
182+
}
183+
}
184+
185+
func TestWithCapSysAdmFanotifyFileOrDirectoryOpen(t *testing.T) {
186+
l, err := NewListener("/", 4096, true)
187+
assert.Nil(t, err)
188+
assert.NotNil(t, l)
189+
watchDir := t.TempDir()
190+
191+
t.Logf("Watch Directory: %s", watchDir)
192+
action := FileOrDirectoryOpened
193+
l.AddWatch(watchDir, action)
194+
go l.Start()
195+
defer l.Stop()
196+
pid, err := runAsCmd("ls", watchDir)
197+
assert.Nil(t, err)
198+
assert.NotEqual(t, pid, 0)
199+
select {
200+
case <-time.After(100 * time.Millisecond):
201+
t.Error("Timeout Error: FileOrDirectoryOpened event not received")
202+
case event := <-l.Events:
203+
assert.Equal(t, fmt.Sprintf("%s/%s", event.Path, event.FileName), fmt.Sprintf("%s/%s", watchDir, "."))
204+
assert.Equal(t, event.Pid, pid)
205+
opened := (event.Mask & FileOpened) == FileOpened
206+
assert.True(t, opened)
207+
}
208+
}
209+
210+
func TestWithCapSysAdmFanotifyFileOpenForExec(t *testing.T) {
211+
l, err := NewListener("/", 4096, true)
212+
assert.Nil(t, err)
213+
assert.NotNil(t, l)
214+
watchDir := t.TempDir()
215+
216+
t.Logf("Watch Directory: %s", watchDir)
217+
data := []byte(`
218+
#!/bin/bash
219+
220+
echo "test shell script"
221+
exit 0
222+
`)
223+
testFile := fmt.Sprintf("%s/test.sh", watchDir)
224+
err = os.WriteFile(testFile, data, 0755)
225+
assert.Nil(t, err)
226+
t.Logf("Test shell script created %s", testFile)
227+
action := FileOpenedForExec
228+
l.AddWatch(watchDir, action)
229+
go l.Start()
230+
defer l.Stop()
231+
pid, err := runAsCmd("bash", "-c", testFile)
232+
assert.Nil(t, err)
233+
select {
234+
case <-time.After(100 * time.Millisecond):
235+
t.Error("Timeout Error: FileOpenedForExec event not received")
236+
case event := <-l.Events:
237+
assert.Equal(t, fmt.Sprintf("%s/%s", event.Path, event.FileName), testFile)
238+
assert.Equal(t, event.Pid, pid)
239+
openedForExec := (event.Mask & FileOpenedForExec) == FileOpenedForExec
240+
assert.True(t, openedForExec)
241+
}
242+
}
243+
244+
func TestWithCapSysAdmFanotifyFileAttribChanged(t *testing.T) {
245+
l, err := NewListener("/", 4096, true)
246+
assert.Nil(t, err)
247+
assert.NotNil(t, l)
248+
watchDir := t.TempDir()
249+
250+
t.Logf("Watch Directory: %s", watchDir)
251+
data := []byte(`
252+
#!/bin/bash
253+
254+
echo "test shell script"
255+
exit 0
256+
`)
257+
testFile := fmt.Sprintf("%s/test.sh", watchDir)
258+
err = os.WriteFile(testFile, data, 0666)
259+
assert.Nil(t, err)
260+
t.Logf("Test shell script created %s", testFile)
261+
action := FileAttribChanged
262+
l.AddWatch(watchDir, action)
263+
go l.Start()
264+
defer l.Stop()
265+
pid, err := runAsCmd("chmod", "+x", testFile)
266+
assert.Nil(t, err)
267+
select {
268+
case <-time.After(100 * time.Millisecond):
269+
t.Error("Timeout Error: FileOpenedForExec event not received")
270+
case event := <-l.Events:
271+
assert.Equal(t, fmt.Sprintf("%s/%s", event.Path, event.FileName), testFile)
272+
assert.Equal(t, event.Pid, pid)
273+
openedForExec := (event.Mask & FileAttribChanged) == FileAttribChanged
274+
assert.True(t, openedForExec)
275+
}
276+
}
277+
278+
func TestWithCapSysAdmFanotifyFileCreated(t *testing.T) {
279+
l, err := NewListener("/", 4096, true)
280+
assert.Nil(t, err)
281+
assert.NotNil(t, l)
282+
watchDir := t.TempDir()
283+
284+
t.Logf("Watch Directory: %s", watchDir)
285+
action := FileCreated
286+
l.AddWatch(watchDir, action)
287+
go l.Start()
288+
defer l.Stop()
289+
testFile := fmt.Sprintf("%s/test.txt", watchDir)
290+
pid, err := runAsCmd("touch", testFile)
291+
assert.Nil(t, err)
292+
select {
293+
case <-time.After(100 * time.Millisecond):
294+
t.Error("Timeout Error: FileOpenedForExec event not received")
295+
case event := <-l.Events:
296+
assert.Equal(t, fmt.Sprintf("%s/%s", event.Path, event.FileName), testFile)
297+
assert.Equal(t, event.Pid, pid)
298+
created := (event.Mask & FileCreated) == FileCreated
299+
assert.True(t, created)
300+
}
301+
}
302+
303+
func TestWithCapSysAdmFanotifyFileOrDirectoryCreated(t *testing.T) {
304+
l, err := NewListener("/", 4096, true)
305+
assert.Nil(t, err)
306+
assert.NotNil(t, l)
307+
watchDir := t.TempDir()
308+
309+
t.Logf("Watch Directory: %s", watchDir)
310+
action := FileOrDirCreated
311+
l.AddWatch(watchDir, action)
312+
go l.Start()
313+
defer l.Stop()
314+
testDir := fmt.Sprintf("%s/testdir", watchDir)
315+
pid, err := runAsCmd("mkdir", testDir)
316+
assert.Nil(t, err)
317+
select {
318+
case <-time.After(100 * time.Millisecond):
319+
t.Error("Timeout Error: FileOpenedForExec event not received")
320+
case event := <-l.Events:
321+
assert.Equal(t, fmt.Sprintf("%s/%s", event.Path, event.FileName), testDir)
322+
assert.Equal(t, event.Pid, pid)
323+
created := (event.Mask & FileCreated) == FileCreated
324+
assert.True(t, created)
325+
}
326+
}
327+
328+
func TestWithCapSysAdmFanotifyFileDeleted(t *testing.T) {
329+
330+
l, err := NewListener("/", 4096, true)
331+
assert.Nil(t, err)
332+
assert.NotNil(t, l)
333+
334+
watchDir := t.TempDir()
335+
testFile := fmt.Sprintf("%s/test.txt", watchDir)
336+
pid, err := runAsCmd("touch", testFile)
337+
assert.Nil(t, err)
338+
339+
t.Logf("Watch Directory: %s", watchDir)
340+
action := FileDeleted
341+
l.AddWatch(watchDir, action)
342+
go l.Start()
343+
defer l.Stop()
344+
345+
pid, err = runAsCmd("rm", "-f", testFile)
346+
assert.Nil(t, err)
347+
select {
348+
case <-time.After(100 * time.Millisecond):
349+
t.Error("Timeout Error: FileOpenedForExec event not received")
350+
case event := <-l.Events:
351+
assert.Equal(t, fmt.Sprintf("%s/%s", event.Path, event.FileName), testFile)
352+
assert.Equal(t, event.Pid, pid)
353+
deleted := (event.Mask & FileDeleted) == FileDeleted
354+
assert.True(t, deleted)
355+
}
356+
}
357+
358+
func TestWithCapSysAdmFanotifyFileOrDirDeleted(t *testing.T) {
359+
360+
l, err := NewListener("/", 4096, true)
361+
assert.Nil(t, err)
362+
assert.NotNil(t, l)
363+
364+
watchDir := t.TempDir()
365+
testDir := fmt.Sprintf("%s/testdir", watchDir)
366+
pid, err := runAsCmd("mkdir", testDir)
367+
assert.Nil(t, err)
368+
369+
t.Logf("Watch Directory: %s", watchDir)
370+
action := FileOrDirDeleted
371+
l.AddWatch(watchDir, action)
372+
go l.Start()
373+
defer l.Stop()
374+
375+
pid, err = runAsCmd("rm", "-rf", testDir)
376+
assert.Nil(t, err)
377+
select {
378+
case <-time.After(100 * time.Millisecond):
379+
t.Error("Timeout Error: FileOpenedForExec event not received")
380+
case event := <-l.Events:
381+
assert.Equal(t, fmt.Sprintf("%s/%s", event.Path, event.FileName), testDir)
382+
assert.Equal(t, event.Pid, pid)
383+
deleted := (event.Mask & FileDeleted) == FileDeleted
384+
assert.True(t, deleted)
385+
}
386+
}

0 commit comments

Comments
 (0)