/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2023 Red Hat GmbH * Author: Alice Frosi */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "common/gluten.h" #include "operations.h" #include "testutil.h" struct args_write_file { char *file; char *t; ssize_t size; }; static long nr; static void write_arg(struct op_call *call, int i, long v) { ck_write_gluten(gluten, call->args[i], v); } static void write_file(char *file, char *t, ssize_t size) { int fd; fd = open(file, O_CREAT | O_RDWR, S_IWUSR | S_IRUSR); ck_assert_int_ge(fd, -1); write(fd, t, size); close(fd); } static int write_file_get_fd(char *file, char *t, ssize_t size) { int fd; write_file(file, t, size); fd = open(file, O_RDONLY, S_IWUSR); unlink(file); return fd; } static int write_file_clone(void *a) { struct args_write_file *args = (struct args_write_file *)a; write_file(args->file, args->t, args->size); install_single_syscall(SYS_getpid); getpid(); return 0; } static void set_ns_to_none(struct op_call *call) { for (unsigned int i = 0; i < NS_NUM; i++) call->context.ns[i].type = NS_NONE; } static void setup_ns(struct args_write_file *args) { at = mmap(NULL, sizeof(struct args_target), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); at->nr = __NR_getpid; at->target = write_file_clone; at->tclone = (void *)args; at->ns[NS_MOUNT] = true; setup(); } START_TEST(test_with_read) { char test_file[] = "/tmp/test.txt"; char t[PATH_MAX] = "Hello Test"; struct op_call call = { { OFFSET_RO_DATA, 0 }, { { OFFSET_DATA, 0 }, { OFFSET_DATA, sizeof(long) }, { OFFSET_DATA, sizeof(long) * 2 }, }, .has_ret = true, .ret = { OFFSET_DATA, sizeof(long) * 3 }, }; char buf[PATH_MAX]; long count, ret; int fd; fd = write_file_get_fd(test_file, t, sizeof(t)); count = sizeof(buf); for (unsigned int i = 0; i < NS_NUM; i++) call.context.ns[i].type = NS_NONE; nr = SYS_read; ck_write_gluten(gluten, call.nr, nr); write_arg(&call, 0, (long)fd); write_arg(&call, 1, (long)&buf); write_arg(&call, 2, (long)count); nr = SYS_read; ck_assert_int_eq(op_call(&req, notifyfd, &gluten, &call), 0); ck_read_gluten(gluten, call.ret, ret); ck_assert_msg(ret == count, "expect ret %ld to be %ld", ret, count); ck_assert_str_eq(t, buf); } END_TEST START_TEST(test_with_getppid) { struct op_call call = { .nr = { OFFSET_RO_DATA, 0 }, .has_ret = true, .ret = { OFFSET_DATA, 0 }, }; long pid = (long)getpid(); int ret = -1; for (unsigned int i = 0; i < NS_NUM; i++) call.context.ns[i].type = NS_NONE; nr = SYS_getppid; ck_write_gluten(gluten, call.nr, nr); ck_assert_int_eq(op_call(&req, notifyfd, &gluten, &call), 0); ck_read_gluten(gluten, call.ret, ret); ck_assert_msg(ret == pid, "expect ret %d to be equal to %ld", ret, pid); } END_TEST START_TEST(test_with_open_read_ns) { char test_file[] = "/tmp/test.txt"; char t[PATH_MAX] = "Hello Test"; struct args_write_file args = { test_file, t, sizeof(t) }; struct op_call *call; struct op ops[] = { { OP_CALL, { .call = { { OFFSET_RO_DATA, 0 }, /* open */ { { OFFSET_DATA, 0 }, { OFFSET_DATA, sizeof(long) }, { OFFSET_DATA, sizeof(long) * 2 }, }, .has_ret = true, .ret = { OFFSET_DATA, sizeof(long) * 3 } } } }, { OP_CALL, { .call = { { OFFSET_RO_DATA, sizeof(long) }, /* read */ { { OFFSET_DATA, sizeof(long) * 3 }, /* ret of the previous call*/ { OFFSET_DATA, sizeof(long) * 5 }, { OFFSET_DATA, sizeof(long) * 6 }, }, .has_ret = true, .ret = { OFFSET_DATA, sizeof(long) * 7 } } } }, { OP_END, OP_EMPTY }, }; int flags = O_RDWR; char buf[PATH_MAX]; long count, rcount; setup_ns(&args); /* Copy and configure op_calls */ set_ns_to_none(&ops[0].op.call); set_ns_to_none(&ops[1].op.call); nr = SYS_open; call = &ops[0].op.call; ck_write_gluten(gluten, call->nr, nr); write_arg(call, 0, (long)&test_file); write_arg(call, 1, (long)flags); call->context.ns[NS_MOUNT].type = NS_SPEC_TARGET; nr = SYS_read; call = &ops[1].op.call; count = sizeof(buf); ck_write_gluten(gluten, call->nr, nr); write_arg(call, 1, (long)&buf); write_arg(call, 2, (long)count); call->context.ns[NS_MOUNT].type = NS_SPEC_TARGET; write_instr(gluten, ops); ck_assert_int_eq(eval(&gluten, &req, notifyfd), 0); ck_read_gluten(gluten, ops[1].op.call.ret, rcount); ck_assert_msg(rcount == count, "expect ret %ld to be %ld", rcount, count); ck_assert_str_eq(t, buf); } END_TEST Suite *op_call_suite(void) { Suite *s; TCase *tsimple, *tread, *treadns; int timeout = 30; s = suite_create("Perform ops call"); tsimple = tcase_create("getppid"); tcase_add_test(tsimple, test_with_getppid); suite_add_tcase(s, tsimple); tread = tcase_create("read"); tcase_add_test(tread, test_with_read); suite_add_tcase(s, tread); treadns = tcase_create("read ns"); tcase_add_checked_fixture(treadns, NULL, teardown); tcase_set_timeout(treadns, timeout); tcase_add_test(treadns, test_with_open_read_ns); suite_add_tcase(s, treadns); return s; } int main(void) { int no_failed = 0; Suite *s; SRunner *runner; s = op_call_suite(); runner = srunner_create(s); srunner_run_all(runner, CK_VERBOSE); no_failed = srunner_ntests_failed(runner); srunner_free(runner); return (no_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }