Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/tests/sys/kern/inotify_test.c
39536 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2025 Klara, Inc.
5
*/
6
7
#include <sys/capsicum.h>
8
#include <sys/filio.h>
9
#include <sys/inotify.h>
10
#include <sys/ioccom.h>
11
#include <sys/mount.h>
12
#include <sys/socket.h>
13
#include <sys/stat.h>
14
#include <sys/sysctl.h>
15
#include <sys/un.h>
16
17
#include <dirent.h>
18
#include <errno.h>
19
#include <fcntl.h>
20
#include <limits.h>
21
#include <mntopts.h>
22
#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <unistd.h>
26
27
#include <atf-c.h>
28
29
static const char *
30
ev2name(int event)
31
{
32
switch (event) {
33
case IN_ACCESS:
34
return ("IN_ACCESS");
35
case IN_ATTRIB:
36
return ("IN_ATTRIB");
37
case IN_CLOSE_WRITE:
38
return ("IN_CLOSE_WRITE");
39
case IN_CLOSE_NOWRITE:
40
return ("IN_CLOSE_NOWRITE");
41
case IN_CREATE:
42
return ("IN_CREATE");
43
case IN_DELETE:
44
return ("IN_DELETE");
45
case IN_DELETE_SELF:
46
return ("IN_DELETE_SELF");
47
case IN_MODIFY:
48
return ("IN_MODIFY");
49
case IN_MOVE_SELF:
50
return ("IN_MOVE_SELF");
51
case IN_MOVED_FROM:
52
return ("IN_MOVED_FROM");
53
case IN_MOVED_TO:
54
return ("IN_MOVED_TO");
55
case IN_OPEN:
56
return ("IN_OPEN");
57
default:
58
return (NULL);
59
}
60
}
61
62
static void
63
close_checked(int fd)
64
{
65
ATF_REQUIRE(close(fd) == 0);
66
}
67
68
/*
69
* Make sure that no other events are pending, and close the inotify descriptor.
70
*/
71
static void
72
close_inotify(int fd)
73
{
74
int n;
75
76
ATF_REQUIRE(ioctl(fd, FIONREAD, &n) == 0);
77
ATF_REQUIRE(n == 0);
78
close_checked(fd);
79
}
80
81
static uint32_t
82
consume_event_cookie(int ifd, int wd, unsigned int event, unsigned int flags,
83
const char *name)
84
{
85
struct inotify_event *ev;
86
size_t evsz, namelen;
87
ssize_t n;
88
uint32_t cookie;
89
90
/* Only read one record. */
91
namelen = name == NULL ? 0 : strlen(name);
92
evsz = sizeof(*ev) + _IN_NAMESIZE(namelen);
93
ev = malloc(evsz);
94
ATF_REQUIRE(ev != NULL);
95
96
n = read(ifd, ev, evsz);
97
ATF_REQUIRE_MSG(n >= 0, "failed to read event %s", ev2name(event));
98
ATF_REQUIRE((size_t)n >= sizeof(*ev));
99
ATF_REQUIRE((size_t)n == sizeof(*ev) + ev->len);
100
ATF_REQUIRE((size_t)n == evsz);
101
102
ATF_REQUIRE_MSG((ev->mask & IN_ALL_EVENTS) == event,
103
"expected event %#x, got %#x", event, ev->mask);
104
ATF_REQUIRE_MSG((ev->mask & _IN_ALL_RETFLAGS) == flags,
105
"expected flags %#x, got %#x", flags, ev->mask);
106
ATF_REQUIRE_MSG(ev->wd == wd,
107
"expected wd %d, got %d", wd, ev->wd);
108
ATF_REQUIRE_MSG(name == NULL || strcmp(name, ev->name) == 0,
109
"expected name '%s', got '%s'", name, ev->name);
110
cookie = ev->cookie;
111
if ((ev->mask & (IN_MOVED_FROM | IN_MOVED_TO)) == 0)
112
ATF_REQUIRE(cookie == 0);
113
free(ev);
114
return (cookie);
115
}
116
117
/*
118
* Read an event from the inotify file descriptor and check that it
119
* matches the expected values.
120
*/
121
static void
122
consume_event(int ifd, int wd, unsigned int event, unsigned int flags,
123
const char *name)
124
{
125
(void)consume_event_cookie(ifd, wd, event, flags, name);
126
}
127
128
static int
129
inotify(int flags)
130
{
131
int ifd;
132
133
ifd = inotify_init1(flags);
134
ATF_REQUIRE(ifd != -1);
135
return (ifd);
136
}
137
138
static void
139
mount_nullfs(char *dir, char *src)
140
{
141
struct iovec *iov;
142
char errmsg[1024];
143
int error, iovlen;
144
145
iov = NULL;
146
iovlen = 0;
147
148
build_iovec(&iov, &iovlen, "fstype", "nullfs", (size_t)-1);
149
build_iovec(&iov, &iovlen, "fspath", dir, (size_t)-1);
150
build_iovec(&iov, &iovlen, "target", src, (size_t)-1);
151
build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
152
153
errmsg[0] = '\0';
154
error = nmount(iov, iovlen, 0);
155
ATF_REQUIRE_MSG(error == 0,
156
"mount nullfs %s %s: %s", src, dir,
157
errmsg[0] == '\0' ? strerror(errno) : errmsg);
158
159
free_iovec(&iov, &iovlen);
160
}
161
162
static void
163
mount_tmpfs(const char *dir)
164
{
165
struct iovec *iov;
166
char errmsg[1024];
167
int error, iovlen;
168
169
iov = NULL;
170
iovlen = 0;
171
172
build_iovec(&iov, &iovlen, "fstype", "tmpfs", (size_t)-1);
173
build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, dir),
174
(size_t)-1);
175
build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
176
177
errmsg[0] = '\0';
178
error = nmount(iov, iovlen, 0);
179
ATF_REQUIRE_MSG(error == 0,
180
"mount tmpfs %s: %s", dir,
181
errmsg[0] == '\0' ? strerror(errno) : errmsg);
182
183
free_iovec(&iov, &iovlen);
184
}
185
186
static int
187
watch_file(int ifd, int events, char *path)
188
{
189
int fd, wd;
190
191
strncpy(path, "test.XXXXXX", PATH_MAX);
192
fd = mkstemp(path);
193
ATF_REQUIRE(fd != -1);
194
close_checked(fd);
195
196
wd = inotify_add_watch(ifd, path, events);
197
ATF_REQUIRE(wd != -1);
198
199
return (wd);
200
}
201
202
static int
203
watch_dir(int ifd, int events, char *path)
204
{
205
char *p;
206
int wd;
207
208
strlcpy(path, "test.XXXXXX", PATH_MAX);
209
p = mkdtemp(path);
210
ATF_REQUIRE(p == path);
211
212
wd = inotify_add_watch(ifd, path, events);
213
ATF_REQUIRE(wd != -1);
214
215
return (wd);
216
}
217
218
/*
219
* Verify that Capsicum restrictions are applied as expected.
220
*/
221
ATF_TC_WITHOUT_HEAD(inotify_capsicum);
222
ATF_TC_BODY(inotify_capsicum, tc)
223
{
224
int error, dfd, ifd, wd;
225
226
ifd = inotify(IN_NONBLOCK);
227
ATF_REQUIRE(ifd != -1);
228
229
dfd = open(".", O_RDONLY | O_DIRECTORY);
230
ATF_REQUIRE(dfd != -1);
231
232
error = mkdirat(dfd, "testdir", 0755);
233
ATF_REQUIRE(error == 0);
234
235
error = cap_enter();
236
ATF_REQUIRE(error == 0);
237
238
/*
239
* Plain inotify_add_watch() is disallowed.
240
*/
241
wd = inotify_add_watch(ifd, ".", IN_DELETE_SELF);
242
ATF_REQUIRE_ERRNO(ECAPMODE, wd == -1);
243
wd = inotify_add_watch_at(ifd, dfd, "testdir", IN_DELETE_SELF);
244
ATF_REQUIRE(wd >= 0);
245
246
/*
247
* Generate a record and consume it.
248
*/
249
error = unlinkat(dfd, "testdir", AT_REMOVEDIR);
250
ATF_REQUIRE(error == 0);
251
consume_event(ifd, wd, IN_DELETE_SELF, IN_ISDIR, NULL);
252
consume_event(ifd, wd, 0, IN_IGNORED, NULL);
253
254
close_checked(dfd);
255
close_inotify(ifd);
256
}
257
258
/*
259
* Make sure that duplicate, back-to-back events are coalesced.
260
*/
261
ATF_TC_WITHOUT_HEAD(inotify_coalesce);
262
ATF_TC_BODY(inotify_coalesce, tc)
263
{
264
char file[PATH_MAX], path[PATH_MAX];
265
int fd, fd1, ifd, n, wd;
266
267
ifd = inotify(IN_NONBLOCK);
268
269
/* Create a directory and watch it. */
270
wd = watch_dir(ifd, IN_OPEN, path);
271
/* Create a file in the directory and open it. */
272
snprintf(file, sizeof(file), "%s/file", path);
273
fd = open(file, O_RDWR | O_CREAT, 0644);
274
ATF_REQUIRE(fd != -1);
275
close_checked(fd);
276
fd = open(file, O_RDWR);
277
ATF_REQUIRE(fd != -1);
278
fd1 = open(file, O_RDONLY);
279
ATF_REQUIRE(fd1 != -1);
280
close_checked(fd1);
281
close_checked(fd);
282
283
consume_event(ifd, wd, IN_OPEN, 0, "file");
284
ATF_REQUIRE(ioctl(ifd, FIONREAD, &n) == 0);
285
ATF_REQUIRE(n == 0);
286
287
close_inotify(ifd);
288
}
289
290
/*
291
* Check handling of IN_MASK_CREATE.
292
*/
293
ATF_TC_WITHOUT_HEAD(inotify_mask_create);
294
ATF_TC_BODY(inotify_mask_create, tc)
295
{
296
char path[PATH_MAX];
297
int ifd, wd, wd1;
298
299
ifd = inotify(IN_NONBLOCK);
300
301
/* Create a directory and watch it. */
302
wd = watch_dir(ifd, IN_CREATE, path);
303
/* Updating the watch with IN_MASK_CREATE should result in an error. */
304
wd1 = inotify_add_watch(ifd, path, IN_MODIFY | IN_MASK_CREATE);
305
ATF_REQUIRE_ERRNO(EEXIST, wd1 == -1);
306
/* It's an error to specify IN_MASK_ADD with IN_MASK_CREATE. */
307
wd1 = inotify_add_watch(ifd, path, IN_MODIFY | IN_MASK_ADD |
308
IN_MASK_CREATE);
309
ATF_REQUIRE_ERRNO(EINVAL, wd1 == -1);
310
/* Updating the watch without IN_MASK_CREATE should work. */
311
wd1 = inotify_add_watch(ifd, path, IN_MODIFY);
312
ATF_REQUIRE(wd1 != -1);
313
ATF_REQUIRE_EQ(wd, wd1);
314
315
close_inotify(ifd);
316
}
317
318
/*
319
* Make sure that inotify cooperates with nullfs: if a lower vnode is the
320
* subject of an event, the upper vnode should be notified, and if the upper
321
* vnode is the subject of an event, the lower vnode should be notified.
322
*/
323
ATF_TC_WITH_CLEANUP(inotify_nullfs);
324
ATF_TC_HEAD(inotify_nullfs, tc)
325
{
326
atf_tc_set_md_var(tc, "require.user", "root");
327
}
328
ATF_TC_BODY(inotify_nullfs, tc)
329
{
330
char path[PATH_MAX], *p;
331
int dfd, error, fd, ifd, mask, wd;
332
333
mask = IN_CREATE | IN_OPEN;
334
335
ifd = inotify(IN_NONBLOCK);
336
337
strlcpy(path, "./test.XXXXXX", sizeof(path));
338
p = mkdtemp(path);
339
ATF_REQUIRE(p == path);
340
341
error = mkdir("./mnt", 0755);
342
ATF_REQUIRE(error == 0);
343
344
/* Mount the testdir onto ./mnt. */
345
mount_nullfs("./mnt", path);
346
347
wd = inotify_add_watch(ifd, "./mnt", mask);
348
ATF_REQUIRE(wd != -1);
349
350
/* Create a file in the lower directory and open it. */
351
dfd = open(path, O_RDONLY | O_DIRECTORY);
352
ATF_REQUIRE(dfd != -1);
353
fd = openat(dfd, "file", O_RDWR | O_CREAT, 0644);
354
close_checked(fd);
355
close_checked(dfd);
356
357
/* We should see events via the nullfs mount. */
358
consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
359
consume_event(ifd, wd, IN_CREATE, 0, "file");
360
consume_event(ifd, wd, IN_OPEN, 0, "file");
361
362
error = inotify_rm_watch(ifd, wd);
363
ATF_REQUIRE(error == 0);
364
consume_event(ifd, wd, 0, IN_IGNORED, NULL);
365
366
/* Watch the lower directory. */
367
wd = inotify_add_watch(ifd, path, mask);
368
ATF_REQUIRE(wd != -1);
369
/* ... and create a file in the upper directory and open it. */
370
dfd = open("./mnt", O_RDONLY | O_DIRECTORY);
371
ATF_REQUIRE(dfd != -1);
372
fd = openat(dfd, "file2", O_RDWR | O_CREAT, 0644);
373
ATF_REQUIRE(fd != -1);
374
close_checked(fd);
375
close_checked(dfd);
376
377
/* We should see events via the lower directory. */
378
consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
379
consume_event(ifd, wd, IN_CREATE, 0, "file2");
380
consume_event(ifd, wd, IN_OPEN, 0, "file2");
381
382
close_inotify(ifd);
383
}
384
ATF_TC_CLEANUP(inotify_nullfs, tc)
385
{
386
int error;
387
388
error = unmount("./mnt", 0);
389
if (error != 0) {
390
perror("unmount");
391
exit(1);
392
}
393
}
394
395
/*
396
* Make sure that exceeding max_events pending events results in an overflow
397
* event.
398
*/
399
ATF_TC_WITHOUT_HEAD(inotify_queue_overflow);
400
ATF_TC_BODY(inotify_queue_overflow, tc)
401
{
402
char path[PATH_MAX];
403
size_t size;
404
int error, dfd, ifd, max, wd;
405
406
size = sizeof(max);
407
error = sysctlbyname("vfs.inotify.max_queued_events", &max, &size, NULL,
408
0);
409
ATF_REQUIRE(error == 0);
410
411
ifd = inotify(IN_NONBLOCK);
412
413
/* Create a directory and watch it for file creation events. */
414
wd = watch_dir(ifd, IN_CREATE, path);
415
dfd = open(path, O_DIRECTORY);
416
ATF_REQUIRE(dfd != -1);
417
/* Generate max+1 file creation events. */
418
for (int i = 0; i < max + 1; i++) {
419
char name[NAME_MAX];
420
int fd;
421
422
(void)snprintf(name, sizeof(name), "file%d", i);
423
fd = openat(dfd, name, O_CREAT | O_RDWR, 0644);
424
ATF_REQUIRE(fd != -1);
425
close_checked(fd);
426
}
427
428
/*
429
* Read our events. We should see files 0..max-1 and then an overflow
430
* event.
431
*/
432
for (int i = 0; i < max; i++) {
433
char name[NAME_MAX];
434
435
(void)snprintf(name, sizeof(name), "file%d", i);
436
consume_event(ifd, wd, IN_CREATE, 0, name);
437
}
438
439
/* Look for an overflow event. */
440
consume_event(ifd, -1, 0, IN_Q_OVERFLOW, NULL);
441
442
close_checked(dfd);
443
close_inotify(ifd);
444
}
445
446
ATF_TC_WITHOUT_HEAD(inotify_event_access_file);
447
ATF_TC_BODY(inotify_event_access_file, tc)
448
{
449
char path[PATH_MAX], buf[16];
450
off_t nb;
451
ssize_t n;
452
int error, fd, fd1, ifd, s[2], wd;
453
454
ifd = inotify(IN_NONBLOCK);
455
456
wd = watch_file(ifd, IN_ACCESS, path);
457
458
fd = open(path, O_RDWR);
459
n = write(fd, "test", 4);
460
ATF_REQUIRE(n == 4);
461
462
/* A simple read(2) should generate an access. */
463
ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
464
n = read(fd, buf, sizeof(buf));
465
ATF_REQUIRE(n == 4);
466
ATF_REQUIRE(memcmp(buf, "test", 4) == 0);
467
consume_event(ifd, wd, IN_ACCESS, 0, NULL);
468
469
/* copy_file_range(2) should as well. */
470
ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0);
471
fd1 = open("sink", O_RDWR | O_CREAT, 0644);
472
ATF_REQUIRE(fd1 != -1);
473
n = copy_file_range(fd, NULL, fd1, NULL, 4, 0);
474
ATF_REQUIRE(n == 4);
475
close_checked(fd1);
476
consume_event(ifd, wd, IN_ACCESS, 0, NULL);
477
478
/* As should sendfile(2). */
479
error = socketpair(AF_UNIX, SOCK_STREAM, 0, s);
480
ATF_REQUIRE(error == 0);
481
error = sendfile(fd, s[0], 0, 4, NULL, &nb, 0);
482
ATF_REQUIRE(error == 0);
483
ATF_REQUIRE(nb == 4);
484
consume_event(ifd, wd, IN_ACCESS, 0, NULL);
485
close_checked(s[0]);
486
close_checked(s[1]);
487
488
close_checked(fd);
489
490
close_inotify(ifd);
491
}
492
493
ATF_TC_WITHOUT_HEAD(inotify_event_access_dir);
494
ATF_TC_BODY(inotify_event_access_dir, tc)
495
{
496
char root[PATH_MAX], path[PATH_MAX];
497
struct dirent *ent;
498
DIR *dir;
499
int error, ifd, wd;
500
501
ifd = inotify(IN_NONBLOCK);
502
503
wd = watch_dir(ifd, IN_ACCESS, root);
504
snprintf(path, sizeof(path), "%s/dir", root);
505
error = mkdir(path, 0755);
506
ATF_REQUIRE(error == 0);
507
508
/* Read an entry and generate an access. */
509
dir = opendir(path);
510
ATF_REQUIRE(dir != NULL);
511
ent = readdir(dir);
512
ATF_REQUIRE(ent != NULL);
513
ATF_REQUIRE(strcmp(ent->d_name, ".") == 0 ||
514
strcmp(ent->d_name, "..") == 0);
515
ATF_REQUIRE(closedir(dir) == 0);
516
consume_event(ifd, wd, IN_ACCESS, IN_ISDIR, "dir");
517
518
/*
519
* Reading the watched directory should generate an access event.
520
* This is contrary to Linux's inotify man page, which states that
521
* IN_ACCESS is only generated for accesses to objects in a watched
522
* directory.
523
*/
524
dir = opendir(root);
525
ATF_REQUIRE(dir != NULL);
526
ent = readdir(dir);
527
ATF_REQUIRE(ent != NULL);
528
ATF_REQUIRE(strcmp(ent->d_name, ".") == 0 ||
529
strcmp(ent->d_name, "..") == 0);
530
ATF_REQUIRE(closedir(dir) == 0);
531
consume_event(ifd, wd, IN_ACCESS, IN_ISDIR, NULL);
532
533
close_inotify(ifd);
534
}
535
536
ATF_TC_WITHOUT_HEAD(inotify_event_attrib);
537
ATF_TC_BODY(inotify_event_attrib, tc)
538
{
539
char path[PATH_MAX];
540
int error, ifd, fd, wd;
541
542
ifd = inotify(IN_NONBLOCK);
543
544
wd = watch_file(ifd, IN_ATTRIB, path);
545
546
fd = open(path, O_RDWR);
547
ATF_REQUIRE(fd != -1);
548
error = fchmod(fd, 0600);
549
ATF_REQUIRE(error == 0);
550
consume_event(ifd, wd, IN_ATTRIB, 0, NULL);
551
552
error = fchown(fd, getuid(), getgid());
553
ATF_REQUIRE(error == 0);
554
consume_event(ifd, wd, IN_ATTRIB, 0, NULL);
555
556
close_checked(fd);
557
close_inotify(ifd);
558
}
559
560
ATF_TC_WITHOUT_HEAD(inotify_event_close_nowrite);
561
ATF_TC_BODY(inotify_event_close_nowrite, tc)
562
{
563
char file[PATH_MAX], file1[PATH_MAX], dir[PATH_MAX];
564
int ifd, fd, wd1, wd2;
565
566
ifd = inotify(IN_NONBLOCK);
567
568
wd1 = watch_dir(ifd, IN_CLOSE_NOWRITE, dir);
569
wd2 = watch_file(ifd, IN_CLOSE_NOWRITE | IN_CLOSE_WRITE, file);
570
571
fd = open(dir, O_DIRECTORY);
572
ATF_REQUIRE(fd != -1);
573
close_checked(fd);
574
consume_event(ifd, wd1, IN_CLOSE_NOWRITE, IN_ISDIR, NULL);
575
576
fd = open(file, O_RDONLY);
577
ATF_REQUIRE(fd != -1);
578
close_checked(fd);
579
consume_event(ifd, wd2, IN_CLOSE_NOWRITE, 0, NULL);
580
581
snprintf(file1, sizeof(file1), "%s/file", dir);
582
fd = open(file1, O_RDONLY | O_CREAT, 0644);
583
ATF_REQUIRE(fd != -1);
584
close_checked(fd);
585
consume_event(ifd, wd1, IN_CLOSE_NOWRITE, 0, "file");
586
587
close_inotify(ifd);
588
}
589
590
ATF_TC_WITHOUT_HEAD(inotify_event_close_write);
591
ATF_TC_BODY(inotify_event_close_write, tc)
592
{
593
char path[PATH_MAX];
594
int ifd, fd, wd;
595
596
ifd = inotify(IN_NONBLOCK);
597
598
wd = watch_file(ifd, IN_CLOSE_NOWRITE | IN_CLOSE_WRITE, path);
599
600
fd = open(path, O_RDWR);
601
ATF_REQUIRE(fd != -1);
602
close_checked(fd);
603
consume_event(ifd, wd, IN_CLOSE_WRITE, 0, NULL);
604
605
close_inotify(ifd);
606
}
607
608
/* Verify that various operations in a directory generate IN_CREATE events. */
609
ATF_TC_WITHOUT_HEAD(inotify_event_create);
610
ATF_TC_BODY(inotify_event_create, tc)
611
{
612
struct sockaddr_un sun;
613
char path[PATH_MAX], path1[PATH_MAX], root[PATH_MAX];
614
ssize_t n;
615
int error, ifd, ifd1, fd, s, wd, wd1;
616
char b;
617
618
ifd = inotify(IN_NONBLOCK);
619
620
wd = watch_dir(ifd, IN_CREATE, root);
621
622
/* Regular file. */
623
snprintf(path, sizeof(path), "%s/file", root);
624
fd = open(path, O_RDWR | O_CREAT, 0644);
625
ATF_REQUIRE(fd != -1);
626
/*
627
* Make sure we get an event triggered by the fd used to create the
628
* file.
629
*/
630
ifd1 = inotify(IN_NONBLOCK);
631
wd1 = inotify_add_watch(ifd1, root, IN_MODIFY);
632
b = 42;
633
n = write(fd, &b, sizeof(b));
634
ATF_REQUIRE(n == sizeof(b));
635
close_checked(fd);
636
consume_event(ifd, wd, IN_CREATE, 0, "file");
637
consume_event(ifd1, wd1, IN_MODIFY, 0, "file");
638
close_inotify(ifd1);
639
640
/* Hard link. */
641
snprintf(path1, sizeof(path1), "%s/link", root);
642
error = link(path, path1);
643
ATF_REQUIRE(error == 0);
644
consume_event(ifd, wd, IN_CREATE, 0, "link");
645
646
/* Directory. */
647
snprintf(path, sizeof(path), "%s/dir", root);
648
error = mkdir(path, 0755);
649
ATF_REQUIRE(error == 0);
650
consume_event(ifd, wd, IN_CREATE, IN_ISDIR, "dir");
651
652
/* Symbolic link. */
653
snprintf(path1, sizeof(path1), "%s/symlink", root);
654
error = symlink(path, path1);
655
ATF_REQUIRE(error == 0);
656
consume_event(ifd, wd, IN_CREATE, 0, "symlink");
657
658
/* FIFO. */
659
snprintf(path, sizeof(path), "%s/fifo", root);
660
error = mkfifo(path, 0644);
661
ATF_REQUIRE(error == 0);
662
consume_event(ifd, wd, IN_CREATE, 0, "fifo");
663
664
/* Binding a socket. */
665
s = socket(AF_UNIX, SOCK_STREAM, 0);
666
memset(&sun, 0, sizeof(sun));
667
sun.sun_family = AF_UNIX;
668
sun.sun_len = sizeof(sun);
669
snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/socket", root);
670
error = bind(s, (struct sockaddr *)&sun, sizeof(sun));
671
ATF_REQUIRE(error == 0);
672
close_checked(s);
673
consume_event(ifd, wd, IN_CREATE, 0, "socket");
674
675
close_inotify(ifd);
676
}
677
678
ATF_TC_WITHOUT_HEAD(inotify_event_delete);
679
ATF_TC_BODY(inotify_event_delete, tc)
680
{
681
char root[PATH_MAX], path[PATH_MAX], file[PATH_MAX];
682
int error, fd, ifd, wd, wd2;
683
684
ifd = inotify(IN_NONBLOCK);
685
686
wd = watch_dir(ifd, IN_DELETE | IN_DELETE_SELF, root);
687
688
snprintf(path, sizeof(path), "%s/file", root);
689
fd = open(path, O_RDWR | O_CREAT, 0644);
690
ATF_REQUIRE(fd != -1);
691
error = unlink(path);
692
ATF_REQUIRE(error == 0);
693
consume_event(ifd, wd, IN_DELETE, 0, "file");
694
close_checked(fd);
695
696
/*
697
* Make sure that renaming over a file generates a delete event when and
698
* only when that file is watched.
699
*/
700
fd = open(path, O_RDWR | O_CREAT, 0644);
701
ATF_REQUIRE(fd != -1);
702
close_checked(fd);
703
wd2 = inotify_add_watch(ifd, path, IN_DELETE | IN_DELETE_SELF);
704
ATF_REQUIRE(wd2 != -1);
705
snprintf(file, sizeof(file), "%s/file2", root);
706
fd = open(file, O_RDWR | O_CREAT, 0644);
707
ATF_REQUIRE(fd != -1);
708
close_checked(fd);
709
error = rename(file, path);
710
ATF_REQUIRE(error == 0);
711
consume_event(ifd, wd2, IN_DELETE_SELF, 0, NULL);
712
consume_event(ifd, wd2, 0, IN_IGNORED, NULL);
713
714
error = unlink(path);
715
ATF_REQUIRE(error == 0);
716
consume_event(ifd, wd, IN_DELETE, 0, "file");
717
error = rmdir(root);
718
ATF_REQUIRE(error == 0);
719
consume_event(ifd, wd, IN_DELETE_SELF, IN_ISDIR, NULL);
720
consume_event(ifd, wd, 0, IN_IGNORED, NULL);
721
722
close_inotify(ifd);
723
}
724
725
ATF_TC_WITHOUT_HEAD(inotify_event_move);
726
ATF_TC_BODY(inotify_event_move, tc)
727
{
728
char dir1[PATH_MAX], dir2[PATH_MAX], path1[PATH_MAX], path2[PATH_MAX];
729
char path3[PATH_MAX];
730
int error, ifd, fd, wd1, wd2, wd3;
731
uint32_t cookie1, cookie2;
732
733
ifd = inotify(IN_NONBLOCK);
734
735
wd1 = watch_dir(ifd, IN_MOVE | IN_MOVE_SELF, dir1);
736
wd2 = watch_dir(ifd, IN_MOVE | IN_MOVE_SELF, dir2);
737
738
snprintf(path1, sizeof(path1), "%s/file", dir1);
739
fd = open(path1, O_RDWR | O_CREAT, 0644);
740
ATF_REQUIRE(fd != -1);
741
close_checked(fd);
742
snprintf(path2, sizeof(path2), "%s/file2", dir2);
743
error = rename(path1, path2);
744
ATF_REQUIRE(error == 0);
745
cookie1 = consume_event_cookie(ifd, wd1, IN_MOVED_FROM, 0, "file");
746
cookie2 = consume_event_cookie(ifd, wd2, IN_MOVED_TO, 0, "file2");
747
ATF_REQUIRE_MSG(cookie1 == cookie2,
748
"expected cookie %u, got %u", cookie1, cookie2);
749
750
snprintf(path2, sizeof(path2), "%s/dir", dir2);
751
error = rename(dir1, path2);
752
ATF_REQUIRE(error == 0);
753
consume_event(ifd, wd1, IN_MOVE_SELF, IN_ISDIR, NULL);
754
consume_event(ifd, wd2, IN_MOVED_TO, IN_ISDIR, "dir");
755
756
wd3 = watch_file(ifd, IN_MOVE_SELF, path3);
757
error = rename(path3, "foo");
758
ATF_REQUIRE(error == 0);
759
consume_event(ifd, wd3, IN_MOVE_SELF, 0, NULL);
760
761
close_inotify(ifd);
762
}
763
764
ATF_TC_WITHOUT_HEAD(inotify_event_open);
765
ATF_TC_BODY(inotify_event_open, tc)
766
{
767
char root[PATH_MAX], path[PATH_MAX];
768
int error, ifd, fd, wd;
769
770
ifd = inotify(IN_NONBLOCK);
771
772
wd = watch_dir(ifd, IN_OPEN, root);
773
774
snprintf(path, sizeof(path), "%s/file", root);
775
fd = open(path, O_RDWR | O_CREAT, 0644);
776
ATF_REQUIRE(fd != -1);
777
close_checked(fd);
778
consume_event(ifd, wd, IN_OPEN, 0, "file");
779
780
fd = open(path, O_PATH);
781
ATF_REQUIRE(fd != -1);
782
close_checked(fd);
783
consume_event(ifd, wd, IN_OPEN, 0, "file");
784
785
fd = open(root, O_DIRECTORY);
786
ATF_REQUIRE(fd != -1);
787
close_checked(fd);
788
consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
789
790
snprintf(path, sizeof(path), "%s/fifo", root);
791
error = mkfifo(path, 0644);
792
ATF_REQUIRE(error == 0);
793
fd = open(path, O_RDWR);
794
ATF_REQUIRE(fd != -1);
795
close_checked(fd);
796
consume_event(ifd, wd, IN_OPEN, 0, "fifo");
797
798
close_inotify(ifd);
799
}
800
801
ATF_TC_WITH_CLEANUP(inotify_event_unmount);
802
ATF_TC_HEAD(inotify_event_unmount, tc)
803
{
804
atf_tc_set_md_var(tc, "require.user", "root");
805
}
806
ATF_TC_BODY(inotify_event_unmount, tc)
807
{
808
int error, fd, ifd, wd;
809
810
ifd = inotify(IN_NONBLOCK);
811
812
error = mkdir("./root", 0755);
813
ATF_REQUIRE(error == 0);
814
815
mount_tmpfs("./root");
816
817
error = mkdir("./root/dir", 0755);
818
ATF_REQUIRE(error == 0);
819
wd = inotify_add_watch(ifd, "./root/dir", IN_OPEN);
820
ATF_REQUIRE(wd >= 0);
821
822
fd = open("./root/dir", O_RDONLY | O_DIRECTORY);
823
ATF_REQUIRE(fd != -1);
824
consume_event(ifd, wd, IN_OPEN, IN_ISDIR, NULL);
825
close_checked(fd);
826
827
/* A regular unmount should fail, as inotify holds a vnode reference. */
828
error = unmount("./root", 0);
829
ATF_REQUIRE_ERRNO(EBUSY, error == -1);
830
error = unmount("./root", MNT_FORCE);
831
ATF_REQUIRE_MSG(error == 0,
832
"unmounting ./root failed: %s", strerror(errno));
833
834
consume_event(ifd, wd, 0, IN_UNMOUNT, NULL);
835
consume_event(ifd, wd, 0, IN_IGNORED, NULL);
836
837
close_inotify(ifd);
838
}
839
ATF_TC_CLEANUP(inotify_event_unmount, tc)
840
{
841
(void)unmount("./root", MNT_FORCE);
842
}
843
844
ATF_TP_ADD_TCS(tp)
845
{
846
/* Tests for the inotify syscalls. */
847
ATF_TP_ADD_TC(tp, inotify_capsicum);
848
ATF_TP_ADD_TC(tp, inotify_coalesce);
849
ATF_TP_ADD_TC(tp, inotify_mask_create);
850
ATF_TP_ADD_TC(tp, inotify_nullfs);
851
ATF_TP_ADD_TC(tp, inotify_queue_overflow);
852
/* Tests for the various inotify event types. */
853
ATF_TP_ADD_TC(tp, inotify_event_access_file);
854
ATF_TP_ADD_TC(tp, inotify_event_access_dir);
855
ATF_TP_ADD_TC(tp, inotify_event_attrib);
856
ATF_TP_ADD_TC(tp, inotify_event_close_nowrite);
857
ATF_TP_ADD_TC(tp, inotify_event_close_write);
858
ATF_TP_ADD_TC(tp, inotify_event_create);
859
ATF_TP_ADD_TC(tp, inotify_event_delete);
860
ATF_TP_ADD_TC(tp, inotify_event_move);
861
ATF_TP_ADD_TC(tp, inotify_event_open);
862
ATF_TP_ADD_TC(tp, inotify_event_unmount);
863
return (atf_no_error());
864
}
865
866