19 return clock_gettime(CLOCK_MONOTONIC, &time) ? -1
20 : (int64_t)time.tv_sec * 1000000000 + time.tv_nsec;
23int xa_reap(pid_t child,
int milliseconds) {
24 int64_t now = xa_now(), deadline = now + (int64_t)milliseconds * 1000000;
25 while (now >= 0 && (now = xa_now()) >= 0 && now < deadline) {
27 pid_t result = waitpid(child, &status, WNOHANG);
29 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
30 if (result < 0 && errno != EINTR)
32 struct timespec pause = {0, 5000000};
33 nanosleep(&pause, NULL);
36 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
41int xa_write_all(
int fd,
const void* buffer,
size_t length) {
42 const unsigned char* bytes = buffer;
44 ssize_t result = write(fd, bytes, length);
45 if (result < 0 && errno == EINTR)
55int xa_read_all(
int fd,
void* buffer,
size_t length) {
56 unsigned char* bytes = buffer;
58 ssize_t result = read(fd, bytes, length);
59 if (result < 0 && errno == EINTR)
69int xa_send(
int fd,
char byte) {
70 return xa_write_all(fd, &
byte, 1);
73int xa_receive(
int fd,
char expected) {
74 int64_t now = xa_now(), deadline = now + 5000000000;
75 while (now >= 0 && (now = xa_now()) >= 0 && now < deadline) {
76 struct pollfd watch = {.fd = fd, .events = POLLIN};
77 int result = poll(&watch, 1, 100);
78 if (result < 0 && errno == EINTR)
85 if (!xa_read_all(fd, &
byte, 1) &&
byte == expected)
94int xa_create(
struct xa_file* file,
int backend,
int directory) {
95 static unsigned sequence;
96 memset(file, 0,
sizeof(*file));
98 file->backend = backend;
99 file->directory = directory;
100 if (backend == XA_MEMFD) {
105 file->fd = memfd_create(
"xattr-contract", MFD_CLOEXEC | MFD_ALLOW_SEALING);
107 snprintf(file->path,
sizeof(file->path),
"%s/xattr-%ld-%u", backend == XA_RAMFS ?
"/tmp" :
"",
108 (long)getpid(), ++sequence);
109 if (directory && mkdir(file->path, 0700)) {
115 directory ? O_RDONLY | O_DIRECTORY | O_CLOEXEC : O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC,
126 fprintf(stderr,
"XATTR-CONTRACT: create backend=%d directory=%d errno=%d\n", backend, directory,
128 return file->fd < 0 ? -1 : 0;
131int xa_open_alias(
const struct xa_file* file) {
133 return open(file->path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
134 if (file->backend == XA_EXT2) {
136 snprintf(alias,
sizeof(alias),
"%s.alias", file->path);
137 if (link(file->path, alias))
139 int fd = open(alias, O_RDWR | O_CLOEXEC), saved = errno;
149 return file->path[0] ? open(file->path, O_RDWR | O_CLOEXEC) : dup(file->fd);
152void xa_close(
struct xa_file* file) {
165int xa_set(
const struct xa_file* file,
int how,
const char* name,
const void* value,
size_t size,
168 return setxattr(file->path, name, value, size, flags);
170 return lsetxattr(file->path, name, value, size, flags);
171 return fsetxattr(file->fd, name, value, size, flags);
174ssize_t xa_get(
const struct xa_file* file,
int how,
const char* name,
void* value,
size_t size) {
176 return getxattr(file->path, name, value, size);
178 return lgetxattr(file->path, name, value, size);
179 return fgetxattr(file->fd, name, value, size);
182ssize_t xa_list(
const struct xa_file* file,
int how,
char* names,
size_t size) {
184 return listxattr(file->path, names, size);
186 return llistxattr(file->path, names, size);
187 return flistxattr(file->fd, names, size);
190int xa_remove(
const struct xa_file* file,
int how,
const char* name) {
192 return removexattr(file->path, name);
194 return lremovexattr(file->path, name);
195 return fremovexattr(file->fd, name);
198int xa_value(
const struct xa_file* file,
int how,
const char* name,
const void* value,
200 unsigned char* actual = malloc(size + 1);
203 memset(actual, 0xa5, size + 1);
204 int failed = xa_get(file, how, name, NULL, 0) != (ssize_t)size ||
205 xa_get(file, how, name, actual, size + 1) != (ssize_t)size ||
206 (size && memcmp(actual, value, size)) || actual[size] != 0xa5;
208 fprintf(stderr,
"XATTR-CONTRACT: value %s backend=%d how=%d size=%zu errno=%d\n", name,
209 file->backend, how, size, errno);
211 return failed ? -1 : 0;
214int xa_names(
const struct xa_file* file,
int how,
const char*
const* names,
size_t count) {
215 ssize_t length = xa_list(file, how, NULL, 0);
216 if (length < 0 || length > 65536 || count > 128)
218 char* bytes = malloc((
size_t)length + 1);
221 unsigned char seen[128] = {0};
222 bytes[length] = (char)0xa5;
223 int failed = xa_list(file, how, bytes, length) != length || (
unsigned char)bytes[length] != 0xa5;
225 for (
size_t offset = 0; !failed && offset < (size_t)length;) {
226 char* end = memchr(bytes + offset, 0, (
size_t)length - offset);
227 if (!end || end == bytes + offset) {
232 while (index < count && strcmp(bytes + offset, names[index]))
234 if (index == count || seen[index]) {
240 offset = (size_t)(end - bytes) + 1;
242 failed |= found != count;
244 fprintf(stderr,
"XATTR-CONTRACT: list backend=%d how=%d bytes=%zd names=%zu expected=%zu\n",
245 file->backend, how, length, found, count);
247 return failed ? -1 : 0;
250int xa_unprivileged(uid_t uid, gid_t gid,
const gid_t* groups,
size_t count) {
251 if (setgroups(count, groups) || setgid(gid) || setuid(uid))
253 if (geteuid() == uid && getegid() == gid)