8#include <sys/inotify.h>
13static int same_time(
struct timespec first,
struct timespec second) {
14 return first.tv_sec == second.tv_sec && first.tv_nsec == second.tv_nsec;
17static int next_second(time_t previous) {
18 int64_t start = xa_now();
23 if (clock_gettime(CLOCK_REALTIME, &now))
25 if (now.tv_sec > previous)
27 const struct timespec pause = {0, 5000000};
28 nanosleep(&pause, NULL);
29 }
while (xa_now() - start < 3000000000);
34static int event(
int notify,
int watch,
int directory,
int expected) {
37 struct inotify_event received;
39 ssize_t length = read(notify, &received,
sizeof(received));
41 return length == -1 && errno == EAGAIN ? 0 : -1;
42 if (length !=
sizeof(received) || received.wd != watch || received.len || received.cookie ||
43 received.mask != (uint32_t)(IN_ATTRIB | (directory ? IN_ISDIR : 0)))
46 return read(notify, &received,
sizeof(received)) == -1 && errno == EAGAIN ? 0 : -1;
49static int metadata_case(
int backend,
int directory) {
50 int failed = 0, notify = -1, watch = -1;
51 struct xa_file file = {.fd = -1};
52 void* inaccessible = MAP_FAILED;
53 struct stat before, after;
54 static const unsigned char first[] = {0, 1, 0xff, 7};
55 static const unsigned char second[] = {9, 0, 2, 0xfe};
56 CHECK(!xa_create(&file, backend, directory));
57 inaccessible = mmap(NULL, xa_page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
58 CHECK(inaccessible != MAP_FAILED);
60 notify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
62 watch = inotify_add_watch(notify, file.path, IN_ATTRIB | IN_MODIFY | IN_ACCESS);
65 CHECK(!fsetxattr(file.fd,
"user.event", first,
sizeof(first), XATTR_CREATE));
66 CHECK(!event(notify, watch, directory, 1));
67 CHECK(!fstat(file.fd, &before));
69 CHECK(!next_second(before.st_ctim.tv_sec));
70 CHECK(!fsetxattr(file.fd,
"user.event", second,
sizeof(second), XATTR_REPLACE));
71 CHECK(!event(notify, watch, directory, 1));
72 CHECK(!fstat(file.fd, &after));
73 CHECK(after.st_ctim.tv_sec > before.st_ctim.tv_sec);
74 CHECK(same_time(before.st_mtim, after.st_mtim) && same_time(before.st_atim, after.st_atim));
77 CHECK(fsetxattr(file.fd,
"user.event", first,
sizeof(first), XATTR_CREATE) == -1 &&
80 CHECK(fsetxattr(file.fd,
"user.event", inaccessible, 1, 0) == -1 && errno == EFAULT);
82 CHECK(fremovexattr(file.fd,
"user.absent") == -1 && errno == ENODATA);
83 CHECK(!xa_value(&file, XA_FD,
"user.event", second,
sizeof(second)));
84 const char* names[] = {
"user.event"};
85 CHECK(!xa_names(&file, XA_FD, names, 1));
86 CHECK(!fstat(file.fd, &after));
87 CHECK(same_time(before.st_ctim, after.st_ctim) && same_time(before.st_mtim, after.st_mtim) &&
88 same_time(before.st_atim, after.st_atim));
89 CHECK(!event(notify, watch, directory, 0));
90 CHECK(!next_second(before.st_ctim.tv_sec));
91 CHECK(!fremovexattr(file.fd,
"user.event"));
92 CHECK(!event(notify, watch, directory, 1));
93 CHECK(!fstat(file.fd, &after));
94 CHECK(after.st_ctim.tv_sec > before.st_ctim.tv_sec);
95 CHECK(same_time(before.st_mtim, after.st_mtim) && same_time(before.st_atim, after.st_atim));
99 if (inaccessible != MAP_FAILED)
100 munmap(inaccessible, xa_page);
106 for (
int backend = XA_MEMFD; backend <= XA_EXT2; ++backend) {
107 if (metadata_case(backend, 0) || (backend != XA_MEMFD && metadata_case(backend, 1)))