Created
December 10, 2021 10:29
-
-
Save giuseppe/cd5ae0d9a0299fd9eb80cdf41994acc9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* io_uring_chcon.c | |
* | |
* Copyright (C) 2021 Giuseppe Scrivano <[email protected]> | |
* io_uring_chcon is free software; you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation; either version 2 of the License, or | |
* (at your option) any later version. | |
* | |
* io_uring_chcon is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with crun. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <fcntl.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/xattr.h> | |
#include <unistd.h> | |
#include <dirent.h> | |
#include <liburing.h> | |
#define XATTR_NAME "security.selinux" | |
#define QUEUE_DEPTH 4096 | |
struct context_s { | |
struct io_uring *ring; | |
const char *value; | |
size_t value_len; | |
size_t infly; | |
size_t reqs; | |
}; | |
static int do_submit_queue(struct context_s *ctx) | |
{ | |
int r; | |
r = io_uring_submit(ctx->ring); | |
if (r < 0) { | |
fprintf(stderr, "io_uring_submit: %m\n"); | |
return r; | |
} | |
return 0; | |
} | |
static inline int handle_completion(struct context_s *ctx, struct io_uring_cqe *cqe) | |
{ | |
char *name; | |
int res; | |
ctx->infly--; | |
name = io_uring_cqe_get_data(cqe); | |
res = cqe->res; | |
if (res < 0) { | |
errno = -res; | |
fprintf(stderr, "setxattr (%s): %m\n", name); | |
} | |
io_uring_cqe_seen(ctx->ring, cqe); | |
free(name); | |
return res; | |
} | |
static int do_peek(struct context_s *ctx) | |
{ | |
struct io_uring_cqe *cqe; | |
int r; | |
/* Cleanup as many entries as possible. */ | |
while ((r = io_uring_peek_cqe(ctx->ring, &cqe)) >= 0) { | |
r = handle_completion(ctx, cqe); | |
if (r < 0) | |
return r; | |
} | |
return 0; | |
} | |
static int do_handle(struct context_s *ctx, size_t how_many) | |
{ | |
struct io_uring_cqe *cqe; | |
int r; | |
while (how_many--) { | |
r = io_uring_wait_cqe(ctx->ring, &cqe); | |
if (r < 0) { | |
fprintf(stderr, "io_uring_wait_cqe: ret=%d: %m\n", r); | |
return r; | |
} | |
r = handle_completion(ctx, cqe); | |
if (r < 0) | |
return r; | |
} | |
r = do_peek(ctx); | |
if (r < 0) { | |
return r; | |
} | |
return 0; | |
} | |
static int do_flush(struct context_s *ctx) | |
{ | |
int r; | |
r = do_submit_queue(ctx); | |
if (r < 0) { | |
return r; | |
} | |
return do_handle(ctx, ctx->infly); | |
} | |
static int do_setxattr(struct context_s *ctx, char *path) | |
{ | |
struct io_uring_sqe *sqe; | |
int r; | |
r = do_peek(ctx); | |
if (r < 0) { | |
free(path); | |
return r; | |
} | |
sqe = io_uring_get_sqe(ctx->ring); | |
if (!sqe) { | |
if (ctx->infly == 0) { | |
fprintf(stderr, "io_uring_get_sqe: %m\n"); | |
free(path); | |
return -1; | |
} | |
r = do_submit_queue(ctx); | |
if (r < 0) { | |
return r; | |
} | |
r = do_handle(ctx, 1); | |
if (r < 0) { | |
free(path); | |
return r; | |
} | |
sqe = io_uring_get_sqe(ctx->ring); | |
if (!sqe) { | |
fprintf(stderr, "io_uring_get_sqe: %m\n"); | |
free(path); | |
return -1; | |
} | |
} | |
ctx->infly++; | |
io_uring_prep_setxattr(sqe, XATTR_NAME, ctx->value, path, 0, ctx->value_len); | |
io_uring_sqe_set_data(sqe, path); | |
io_uring_sqe_set_flags(sqe, IOSQE_ASYNC); | |
/* Queue a few requests before notifying the kernel. */ | |
if (++ctx->reqs % 128 == 0) { | |
r = do_submit_queue(ctx); | |
if (r < 0) { | |
return r; | |
} | |
} | |
return 0; | |
} | |
static int recurse(struct context_s *ctx, char *cwd, size_t cwd_len) | |
{ | |
struct io_uring *ring = ctx->ring; | |
size_t val_len = ctx->value_len; | |
const char *val = ctx->value; | |
struct dirent *de; | |
DIR *d; | |
int r = 0; | |
d = opendir(cwd); | |
if (d == NULL) { | |
fprintf(stderr, "opendir `%s`: %m\n", cwd); | |
return -1; | |
} | |
for (de = readdir(d); de; de = readdir(d)) { | |
size_t name_len; | |
size_t path_len; | |
char *path; | |
if (strcmp(de->d_name, ".") == 0 | |
|| strcmp(de->d_name, "..") == 0) | |
continue; | |
name_len = strlen(de->d_name); | |
path = malloc(cwd_len + name_len + 2); | |
if (!path) | |
return -ENOMEM; | |
path_len = cwd_len + name_len + 1; | |
memcpy(path, cwd, cwd_len); | |
path[cwd_len] = '/'; | |
memcpy(path + cwd_len + 1, de->d_name, name_len + 1); | |
switch (de->d_type) { | |
case DT_LNK: | |
/* io_uring doesn't currently support lsetxattr. */ | |
r = lsetxattr(path, XATTR_NAME, | |
ctx->value, ctx->value_len, 0); | |
if (r < 0) { | |
fprintf(stderr, "lsetxattr(%s): %m", path); | |
return r; | |
} | |
break; | |
case DT_DIR: | |
r = recurse(ctx, path, path_len); | |
if (r < 0) | |
return r; | |
r = do_setxattr(ctx, path); | |
if (r < 0) | |
return r; | |
break; | |
default: | |
r = do_setxattr(ctx, path); | |
if (r < 0) | |
return r; | |
break; | |
} | |
} | |
r = closedir(d); | |
if (r < 0) | |
return r; | |
return r; | |
} | |
static int set_and_recurse(struct context_s *ctx, char *path) | |
{ | |
int r; | |
r = recurse(ctx, path, strlen(path)); | |
if (r < 0) | |
return r; | |
return do_setxattr(ctx, path); | |
} | |
int main(int argc, char **argv) | |
{ | |
struct context_s ctx; | |
struct io_uring ring; | |
int rc = 0, ret; | |
char *it, *path; | |
memset(&ctx, 0, sizeof(ctx)); | |
if (argc != 3) { | |
fprintf(stderr, "Usage $CMD $PATH $LABEL\n"); | |
exit(EXIT_FAILURE); | |
} | |
ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0); | |
if (ret) { | |
fprintf(stderr, "child: ring setup failed: %d\n", ret); | |
exit(EXIT_FAILURE); | |
} | |
if (argv[1][0] == '\0') { | |
fprintf(stderr, "invalid path: `%s`\n", argv[1]); | |
exit(EXIT_FAILURE); | |
} | |
it = argv[1] + strlen(argv[1]) - 1; | |
while (*it == '/') | |
*it-- = '\0'; | |
if (*it == '\0') { | |
fprintf(stderr, "invalid path: `%s`\n", argv[1]); | |
exit(EXIT_FAILURE); | |
} | |
path = strdup(argv[1]); | |
if (!path) { | |
fprintf(stderr, "strdup: %m"); | |
exit(EXIT_FAILURE); | |
} | |
ctx.ring = ˚ | |
ctx.value = argv[2]; | |
ctx.value_len = strlen(argv[2]); | |
rc = set_and_recurse(&ctx, path); | |
if (rc < 0) | |
exit(EXIT_FAILURE); | |
rc = do_flush(&ctx); | |
io_uring_queue_exit(&ring); | |
exit(rc ? EXIT_FAILURE : EXIT_SUCCESS); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment