14 return clock_gettime(CLOCK_MONOTONIC, &now) ? -1 : (int64_t)now.tv_sec * 1000000000 + now.tv_nsec;
16void fh_pause(
int milliseconds) {
17 struct timespec pause = {milliseconds / 1000, (milliseconds % 1000) * 1000000};
18 while (nanosleep(&pause, &pause) && errno == EINTR) {
21int fh_wait(
volatile int* flag,
int milliseconds) {
22 int64_t start = fh_now(), deadline = start + (int64_t)milliseconds * 1000000;
23 while (!__atomic_load_n(flag, __ATOMIC_ACQUIRE)) {
24 int64_t now = fh_now();
25 if (start < 0 || now < 0 || now >= deadline)
31int fh_reap(pid_t child,
int milliseconds) {
32 int64_t start = fh_now(), deadline = start + (int64_t)milliseconds * 1000000;
34 while (start >= 0 && (now = fh_now()) >= 0 && now < deadline) {
36 pid_t result = waitpid(child, &status, WNOHANG);
38 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
39 if (result < 0 && errno != EINTR)
44 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
48int fh_read_all(
int fd,
void* buffer,
size_t length) {
49 unsigned char* bytes = buffer;
51 ssize_t amount = read(fd, bytes, length);
52 if (amount < 0 && errno == EINTR)
61int fh_write_all(
int fd,
const void* buffer,
size_t length) {
62 const unsigned char* bytes = buffer;
64 ssize_t amount = write(fd, bytes, length);
65 if (amount < 0 && errno == EINTR)
74int fh_send(
int fd,
char byte) {
75 return fh_write_all(fd, &
byte, 1);
77int fh_readable(
int fd,
int milliseconds) {
78 struct pollfd entry = {.fd = fd, .events = POLLIN};
79 int result = poll(&entry, 1, milliseconds);
80 return result > 0 ? !!(entry.revents & POLLIN) : result;
82int fh_receive(
int fd,
char byte) {
83 if (fh_readable(fd, 5000) != 1)
86 return !fh_read_all(fd, &received, 1) && received ==
byte ? 0 : -1;
88int fh_send_fd(
int socket,
int fd) {
90 struct iovec iov = {&byte, 1};
93 char bytes[CMSG_SPACE(
sizeof(
int))];
95 struct msghdr
message = {.msg_iov = &iov,
97 .msg_control = control.bytes,
98 .msg_controllen =
sizeof(control.bytes)};
99 struct cmsghdr* item = CMSG_FIRSTHDR(&
message);
100 item->cmsg_level = SOL_SOCKET;
101 item->cmsg_type = SCM_RIGHTS;
102 item->cmsg_len = CMSG_LEN(
sizeof(
int));
103 memcpy(CMSG_DATA(item), &fd,
sizeof(fd));
104 return sendmsg(socket, &
message, 0) == 1 ? 0 : -1;
106int fh_receive_fd(
int socket) {
108 struct iovec iov = {&byte, 1};
110 struct cmsghdr align;
111 char bytes[CMSG_SPACE(
sizeof(
int))];
113 struct msghdr
message = {.msg_iov = &iov,
115 .msg_control = control.bytes,
116 .msg_controllen =
sizeof(control.bytes)};
117 if (recvmsg(socket, &
message, MSG_CMSG_CLOEXEC) != 1 ||
message.msg_flags & MSG_CTRUNC)
119 struct cmsghdr* item = CMSG_FIRSTHDR(&
message);
120 if (!item || item->cmsg_level != SOL_SOCKET || item->cmsg_type != SCM_RIGHTS ||
121 item->cmsg_len != CMSG_LEN(
sizeof(
int)))
124 memcpy(&fd, CMSG_DATA(item),
sizeof(fd));
127int fh_export(
int fd,
struct fh_handle* handle,
int* mount_id) {
128 memset(handle, 0,
sizeof(*handle));
129 handle->handle_bytes =
sizeof(handle->bytes);
130 return name_to_handle_at(fd,
"", (
struct file_handle*)handle, mount_id, AT_EMPTY_PATH);
133 return first->handle_bytes <=
sizeof(first->bytes) &&
134 first->handle_bytes == second->handle_bytes && first->handle_type == second->handle_type &&
135 !memcmp(first->bytes, second->bytes, first->handle_bytes);
137int fh_create(
struct fh_file* file) {
138 static unsigned sequence;
139 memset(file, 0,
sizeof(*file));
141 snprintf(file->path,
sizeof(file->path),
"/fanotify-handle-%ld-%u", (
long)getpid(), ++sequence);
142 file->fd = open(file->path, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
143 if (file->fd < 0 || fh_write_all(file->fd,
"0123456789abcdef", 16) ||
144 fh_export(file->fd, &file->handle, &file->mount_id)) {
152void fh_close(
struct fh_file* file) {
160int fh_group(
int nonblock) {
161 return fanotify_init(FAN_REPORT_FID | FAN_CLOEXEC | (nonblock ? FAN_NONBLOCK : 0), O_RDONLY);
163int fh_mark(
int group,
const struct fh_file* file,
unsigned flags, uint64_t mask) {
164 return fanotify_mark(group, flags, mask, file->fd, NULL);
166int fh_modify(
const struct fh_file* file) {
167 return pwrite(file->fd,
"!", 1, 0) == 1 ? 0 : -1;
169size_t fh_record_size(
const struct fh_handle* handle) {
170 return sizeof(
struct fanotify_event_metadata) +
171 ((sizeof(struct fanotify_event_info_fid) + 8 + handle->handle_bytes + 3) & ~(size_t)3);
173int fh_parse(
const void* bytes,
size_t length,
struct fh_record* record) {
174 struct fanotify_event_metadata metadata;
175 if (length <
sizeof(metadata))
177 memcpy(&metadata, bytes,
sizeof(metadata));
178 if (metadata.vers != FANOTIFY_METADATA_VERSION || metadata.event_len != length ||
179 metadata.metadata_len <
sizeof(metadata) || metadata.metadata_len > length ||
180 metadata.fd != FAN_NOFD)
182 memset(record, 0,
sizeof(*record));
183 record->mask = metadata.mask;
184 record->pid = metadata.pid;
185 if (metadata.mask & FAN_Q_OVERFLOW)
186 return length == metadata.metadata_len ? 0 : -1;
187 const unsigned char* cursor = (
const unsigned char*)bytes + metadata.metadata_len;
188 size_t remaining = length - metadata.metadata_len;
189 if (remaining <
sizeof(
struct fanotify_event_info_fid) + 8)
191 struct fanotify_event_info_header info;
192 memcpy(&info, cursor,
sizeof(info));
193 if (info.info_type != FAN_EVENT_INFO_TYPE_FID || info.len != remaining)
195 memcpy(record->fsid, cursor +
sizeof(info),
sizeof(record->fsid));
196 memcpy(&record->handle, cursor +
sizeof(
struct fanotify_event_info_fid), 8);
197 size_t count = record->handle.handle_bytes;
198 if (!count || count >
sizeof(record->handle.bytes) ||
199 sizeof(
struct fanotify_event_info_fid) + 8 + count > remaining)
201 memcpy(record->handle.bytes, cursor +
sizeof(
struct fanotify_event_info_fid) + 8, count);
202 unsigned present = 0;
203 for (
size_t n = 0; n <
sizeof(record->fsid); ++n)
204 present |= record->fsid[n];
205 return present ? 0 : -1;
208 unsigned char bytes[256];
209 size_t length = fh_record_size(shape);
210 if (length >
sizeof(bytes))
212 ssize_t result = read(group, bytes, length);
213 return result > 0 ? fh_parse(bytes, (
size_t)result, record) : -1;
215int fh_event(
int group,
const struct fh_file* file, uint64_t mask, pid_t pid) {
217 int result = fh_take(group, &file->handle, &record);
218 if (result || !fh_equal(&record.handle, &file->handle) || (record.mask & mask) != mask ||
221 "FANOTIFY-HANDLE-CONTRACT: event result=%d expected=%llx/%ld actual=%llx/%d errno=%d\n",
222 result, (
unsigned long long)mask, (
long)pid, (
unsigned long long)record.mask,
228int fh_drain(
int group) {
229 unsigned char bytes[4096];
230 for (
unsigned attempt = 0; attempt < FH_QUEUE_LIMIT + 2; ++attempt) {
231 ssize_t count = read(group, bytes,
sizeof(bytes));
232 if (count < 0 && errno == EAGAIN)