Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | // SPDX-License-Identifier: GPL-2.0 /* * Copyright Intel Corporation, 2023 * * Author: Chao Peng <chao.p.peng@linux.intel.com> */ #define _GNU_SOURCE #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <stdio.h> #include <fcntl.h> #include <linux/bitmap.h> #include <linux/falloc.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include "test_util.h" #include "kvm_util_base.h" static void test_file_read_write(int fd) { char buf[64]; TEST_ASSERT(read(fd, buf, sizeof(buf)) < 0, "read on a guest_mem fd should fail"); TEST_ASSERT(write(fd, buf, sizeof(buf)) < 0, "write on a guest_mem fd should fail"); TEST_ASSERT(pread(fd, buf, sizeof(buf), 0) < 0, "pread on a guest_mem fd should fail"); TEST_ASSERT(pwrite(fd, buf, sizeof(buf), 0) < 0, "pwrite on a guest_mem fd should fail"); } static void test_mmap(int fd, size_t page_size) { char *mem; mem = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); TEST_ASSERT_EQ(mem, MAP_FAILED); } static void test_file_size(int fd, size_t page_size, size_t total_size) { struct stat sb; int ret; ret = fstat(fd, &sb); TEST_ASSERT(!ret, "fstat should succeed"); TEST_ASSERT_EQ(sb.st_size, total_size); TEST_ASSERT_EQ(sb.st_blksize, page_size); } static void test_fallocate(int fd, size_t page_size, size_t total_size) { int ret; ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, 0, total_size); TEST_ASSERT(!ret, "fallocate with aligned offset and size should succeed"); ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, page_size - 1, page_size); TEST_ASSERT(ret, "fallocate with unaligned offset should fail"); ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, total_size, page_size); TEST_ASSERT(ret, "fallocate beginning at total_size should fail"); ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, total_size + page_size, page_size); TEST_ASSERT(ret, "fallocate beginning after total_size should fail"); ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, total_size, page_size); TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) at total_size should succeed"); ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, total_size + page_size, page_size); TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) after total_size should succeed"); ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, page_size, page_size - 1); TEST_ASSERT(ret, "fallocate with unaligned size should fail"); ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, page_size, page_size); TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) with aligned offset and size should succeed"); ret = fallocate(fd, FALLOC_FL_KEEP_SIZE, page_size, page_size); TEST_ASSERT(!ret, "fallocate to restore punched hole should succeed"); } static void test_invalid_punch_hole(int fd, size_t page_size, size_t total_size) { struct { off_t offset; off_t len; } testcases[] = { {0, 1}, {0, page_size - 1}, {0, page_size + 1}, {1, 1}, {1, page_size - 1}, {1, page_size}, {1, page_size + 1}, {page_size, 1}, {page_size, page_size - 1}, {page_size, page_size + 1}, }; int ret, i; for (i = 0; i < ARRAY_SIZE(testcases); i++) { ret = fallocate(fd, FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE, testcases[i].offset, testcases[i].len); TEST_ASSERT(ret == -1 && errno == EINVAL, "PUNCH_HOLE with !PAGE_SIZE offset (%lx) and/or length (%lx) should fail", testcases[i].offset, testcases[i].len); } } static void test_create_guest_memfd_invalid(struct kvm_vm *vm) { size_t page_size = getpagesize(); uint64_t flag; size_t size; int fd; for (size = 1; size < page_size; size++) { fd = __vm_create_guest_memfd(vm, size, 0); TEST_ASSERT(fd == -1 && errno == EINVAL, "guest_memfd() with non-page-aligned page size '0x%lx' should fail with EINVAL", size); } for (flag = 0; flag; flag <<= 1) { fd = __vm_create_guest_memfd(vm, page_size, flag); TEST_ASSERT(fd == -1 && errno == EINVAL, "guest_memfd() with flag '0x%lx' should fail with EINVAL", flag); } } static void test_create_guest_memfd_multiple(struct kvm_vm *vm) { int fd1, fd2, ret; struct stat st1, st2; fd1 = __vm_create_guest_memfd(vm, 4096, 0); TEST_ASSERT(fd1 != -1, "memfd creation should succeed"); ret = fstat(fd1, &st1); TEST_ASSERT(ret != -1, "memfd fstat should succeed"); TEST_ASSERT(st1.st_size == 4096, "memfd st_size should match requested size"); fd2 = __vm_create_guest_memfd(vm, 8192, 0); TEST_ASSERT(fd2 != -1, "memfd creation should succeed"); ret = fstat(fd2, &st2); TEST_ASSERT(ret != -1, "memfd fstat should succeed"); TEST_ASSERT(st2.st_size == 8192, "second memfd st_size should match requested size"); ret = fstat(fd1, &st1); TEST_ASSERT(ret != -1, "memfd fstat should succeed"); TEST_ASSERT(st1.st_size == 4096, "first memfd st_size should still match requested size"); TEST_ASSERT(st1.st_ino != st2.st_ino, "different memfd should have different inode numbers"); close(fd2); close(fd1); } int main(int argc, char *argv[]) { size_t page_size; size_t total_size; int fd; struct kvm_vm *vm; TEST_REQUIRE(kvm_has_cap(KVM_CAP_GUEST_MEMFD)); page_size = getpagesize(); total_size = page_size * 4; vm = vm_create_barebones(); test_create_guest_memfd_invalid(vm); test_create_guest_memfd_multiple(vm); fd = vm_create_guest_memfd(vm, total_size, 0); test_file_read_write(fd); test_mmap(fd, page_size); test_file_size(fd, page_size, total_size); test_fallocate(fd, page_size, total_size); test_invalid_punch_hole(fd, page_size, total_size); close(fd); } |