11static int descriptor_surface(
void) {
12 int failed = 0, group = -1, duplicate = -1, enabled;
13 struct stat state, copy;
14 char path[96], target[96];
17 duplicate = dup(group);
18 CHECK(duplicate >= 0 && !fstat(group, &state) && !fstat(duplicate, ©));
19 CHECK(state.st_ino && state.st_ino == copy.st_ino && state.st_nlink == 1 && state.st_size == 0);
20 CHECK((state.st_mode & 0777) == 0600);
21 CHECK((fcntl(group, F_GETFL) & (O_ACCMODE | O_NONBLOCK)) == (O_RDWR | O_NONBLOCK));
22 CHECK((fcntl(group, F_GETFD) & FD_CLOEXEC) && !(fcntl(duplicate, F_GETFD) & FD_CLOEXEC));
24 CHECK(!ioctl(duplicate, FIONBIO, &enabled) && !(fcntl(group, F_GETFL) & O_NONBLOCK));
26 CHECK(!ioctl(group, FIONBIO, &enabled) && (fcntl(duplicate, F_GETFL) & O_NONBLOCK));
27 CHECK(!ioctl(duplicate, FIOCLEX) && (fcntl(duplicate, F_GETFD) & FD_CLOEXEC));
28 CHECK(!ioctl(duplicate, FIONCLEX) && !(fcntl(duplicate, F_GETFD) & FD_CLOEXEC));
29 CHECK(lseek(group, 17, SEEK_SET) == 0 && lseek(group, -2, SEEK_CUR) == 0);
31 CHECK(lseek(group, 0, 123) == -1 && errno == EINVAL);
33 CHECK(write(group,
"x", 1) == -1 && errno == EINVAL);
35 CHECK(ioctl(group, 0x7fff, NULL) == -1 && errno == ENOTTY);
36 snprintf(path,
sizeof(path),
"/proc/self/fd/%d", group);
37 ssize_t length = readlink(path, target,
sizeof(target));
38 CHECK(length == (ssize_t)strlen(
"anon_inode:[fanotify]"));
39 CHECK(!memcmp(target,
"anon_inode:[fanotify]", (
size_t)length));
48static int queued_transport(
void) {
49 int failed = 0, group = -1, duplicate = -1, received = -1, sockets[2] = {-1, -1};
50 struct fh_file file = {.fd = -1};
51 CHECK(!fh_create(&file));
53 CHECK(group >= 0 && !fh_mark(group, &file, FAN_MARK_ADD, FAN_MODIFY));
54 duplicate = dup(group);
55 CHECK(duplicate >= 0 && !socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
56 CHECK(!fh_send_fd(sockets[0], duplicate));
59 CHECK(!close(duplicate));
61 CHECK(!fh_modify(&file));
62 received = fh_receive_fd(sockets[1]);
63 CHECK(received >= 0 && (fcntl(received, F_GETFD) & FD_CLOEXEC));
64 CHECK(!fh_event(received, &file, FAN_MODIFY, getpid()));
65 CHECK(!fh_modify(&file));
66 CHECK(!unlink(file.path));
68 CHECK(!fh_event(received, &file, FAN_MODIFY, getpid()));
70 CHECK(open_by_handle_at(file.fd, (
struct file_handle*)&file.handle, O_RDONLY) == -1 &&
72 CHECK(!fh_modify(&file) && fh_readable(received, 0) == 0);
80 for (
int n = 0; n < 2; ++n)
87int fh_exec(
int argc,
char** argv) {
91 int kept = atoi(argv[2]), dropped = atoi(argv[3]);
92 if (fcntl(kept, F_GETFD) < 0)
95 if (fcntl(dropped, F_GETFD) != -1 || errno != EBADF)
97 struct fh_file file = {.fd = -1};
98 file.fd = open(argv[4], O_RDWR | O_CLOEXEC);
99 if (file.fd < 0 || fh_export(file.fd, &file.handle, &file.mount_id))
101 int result = fh_event(kept, &file, FAN_MODIFY, getppid()) || fh_modify(&file) ||
102 fh_event(kept, &file, FAN_MODIFY, getpid()) ||
103 fh_mark(kept, &file, FAN_MARK_ADD, FAN_ATTRIB);
105 return result ? 13 : 0;
108static int inherited_exec(
void) {
109 int failed = 0, group = -1, kept = -1;
111 struct fh_file file = {.fd = -1};
112 CHECK(!fh_create(&file));
114 CHECK(group >= 0 && !fh_mark(group, &file, FAN_MARK_ADD, FAN_MODIFY));
116 CHECK(kept >= 0 && !fh_modify(&file));
120 char keep_text[32], drop_text[32];
121 snprintf(keep_text,
sizeof(keep_text),
"%d", kept);
122 snprintf(drop_text,
sizeof(drop_text),
"%d", group);
123 execl(FH_APP, FH_APP,
"fanotify-exec", keep_text, drop_text, file.path, (
char*)NULL);
126 CHECK(!fh_reap(child, 12000));
128 CHECK(fh_readable(group, 0) == 0);
129 CHECK(!fchmod(file.fd, 0644));
130 CHECK(!fh_event(group, &file, FAN_ATTRIB, getpid()));
133 CHECK(!fh_modify(&file) && !fh_event(group, &file, FAN_MODIFY, getpid()));
144int fh_lifetime(
void) {
145 return descriptor_surface() || queued_transport() || inherited_exec();