From: Gou Hao gouhao@uniontech.com
uniontech inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I40JRR CVE: NA
--------------
xattr-test.c can guarantee the basic functions of extended attributes.
Signed-off-by: Gou Hao gouhao@uniontech.com --- tools/testing/selftests/eulerfs/Makefile | 6 + tools/testing/selftests/eulerfs/xattr-test.c | 562 +++++++++++++++++++ 2 files changed, 568 insertions(+) create mode 100644 tools/testing/selftests/eulerfs/Makefile create mode 100644 tools/testing/selftests/eulerfs/xattr-test.c
diff --git a/tools/testing/selftests/eulerfs/Makefile b/tools/testing/selftests/eulerfs/Makefile new file mode 100644 index 000000000000..e5cb6133fdee --- /dev/null +++ b/tools/testing/selftests/eulerfs/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# Makefile for eulerfs selftests + +TEST_GEN_FILES += xattr-test + +include ../lib.mk diff --git a/tools/testing/selftests/eulerfs/xattr-test.c b/tools/testing/selftests/eulerfs/xattr-test.c new file mode 100644 index 000000000000..e85d7de757c7 --- /dev/null +++ b/tools/testing/selftests/eulerfs/xattr-test.c @@ -0,0 +1,562 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + * SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd. + * + * Gou Hao gouhao@unionntech.com + */ + +/* + * Selftests for eulerfs xattr + */ +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> + +#include <sys/xattr.h> +#include <sys/stat.h> +#include <sys/user.h> + +#include <time.h> + +#define BUF_LEN 256 + +#define test_dbg(format, args...) printf("%s: " format, __func__, ##args) + +#define XATTR_PREFIX "user." +#define XATTR_NAME(s) (XATTR_PREFIX#s) +char filename[BUF_LEN], value_buf[BUF_LEN]; +char str255[256], str256[257], str_page[PAGE_SIZE + 1]; + +struct test_data { + char *name; + char *value; + int val_size; + int expect; +}; + +int setup(void) +{ + int fd; + + unlink(filename); + + fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644); + if (fd < 0) { + test_dbg("Create file error!\n"); + return 1; + } + close(fd); + + return 0; +} + +#define print_result_cond(index, cond) \ + test_dbg("case %d %s.\n", (index), (cond) ? "pass" : "failed") + +static inline void print_result(int index, int expect, int real) +{ + if (real == expect) + test_dbg("case %d pass.\n", index); + else + test_dbg("case %d failed: expect=%d, real=%d.\n", index, expect, real); +} + +static inline void fill_value(char *value, int max_size) +{ + int i; + + for (i = 0; i < max_size; i++) + value[i] = 'a'; +} + +static void create_big_buf(void) +{ + int prefix_len = strlen(XATTR_PREFIX); + + sprintf(str255, "%s", XATTR_PREFIX); + fill_value(str255 + prefix_len, 255 - prefix_len); + str255[255] = 0; + + sprintf(str256, "%s", XATTR_PREFIX); + fill_value(str256 + prefix_len, 256 - prefix_len); + str256[256] = 0; + + sprintf(str_page, "%s", XATTR_PREFIX); + fill_value(str_page + prefix_len, PAGE_SIZE - prefix_len); + str_page[PAGE_SIZE] = 0; +} + +void test_setxattr(void) +{ + int ret, i, len; + struct test_data cases[] = { + {NULL, NULL, 0, EFAULT}, + {XATTR_NAME(t1), NULL, 0, 0}, + {NULL, "aaa", 3, EFAULT}, // 3 + {XATTR_NAME(t1), "a", 1, EEXIST}, + {XATTR_NAME(t2), "ab", 2, 0}, + {str255, "a", 1, 0}, // 6 + {str256, "a", 1, ERANGE}, + {XATTR_NAME(t3), str255, 255, 0}, + {XATTR_NAME(t4), str256, 256, 0}, // 9 + {XATTR_NAME(t5), str_page, PAGE_SIZE, ENOSPC}, + {XATTR_NAME(a5), "ab", 2, 0}, + {XATTR_NAME(abbbb5), "ab", 2, 0}, //12 + }; + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + len = sizeof(cases) / sizeof(struct test_data); + for (i = 0; i < len; i++) { + errno = 0; + ret = setxattr(filename, cases[i].name, cases[i].value, + cases[i].val_size, XATTR_CREATE); + print_result(i + 1, cases[i].expect, errno); + } +} + +void test_getxattr(void) +{ + int ret, i, len; + char *test_name = XATTR_NAME(getxattr1); + char *val = "test-value"; + int val_len = strlen(val); + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + setxattr(filename, test_name, val, + val_len, XATTR_CREATE); + + struct test_data cases[] = { + {NULL, NULL, 0, EFAULT}, + {NULL, "aaa", 3, EFAULT}, + {test_name, NULL, 0, val_len}, // 3 + {test_name, value_buf, BUF_LEN, val_len}, + }; + + len = sizeof(cases) / sizeof(struct test_data); + for (i = 0; i < len; i++) { + errno = 0; + ret = getxattr(filename, cases[i].name, + cases[i].value, cases[i].val_size); + if (i == 2) { + print_result(i + 1, cases[i].expect, ret); + } else if (i == 3) { + print_result(i + 1, cases[i].expect, ret); + print_result(i + 1, 0, strcmp(cases[i].value, val)); + } else { + print_result(i + 1, cases[i].expect, errno); + } + + } +} + +int find_name_index(struct test_data *cases, int len, char *target) +{ + int i; + + if (!cases || !target || len <= 0) + return -1; + + for (i = 0; i < len; i++) { + if (!strcmp(cases[i].name, target)) + return i; + } + return -1; +} + +void test_listxattr(void) +{ + int len, list_len; + char *list_value; + int name_index, ret, i; + int count = 0; + + struct test_data cases[] = { + {XATTR_NAME(listxattr-name1), "listxattr-value1", strlen("listxattr-value1"), 0}, + {XATTR_NAME(listxattr-name2), "listxattr-value2", strlen("listxattr-value2"), 0}, + {XATTR_NAME(listxattr-name3), "listxattr-value3", strlen("listxattr-value3"), 0}, + {XATTR_NAME(listxattr-name4), "listxattr-value4", strlen("listxattr-value4"), 0}, + }; + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + len = sizeof(cases) / sizeof(struct test_data); + for (i = 0; i < len; i++) { + ret = setxattr(filename, cases[i].name, cases[i].value, + cases[i].val_size, XATTR_CREATE); + if (ret) { + test_dbg("create xattr FAILED!!\n"); + return; + } + } + + list_len = listxattr(filename, NULL, 0); + if (list_len <= 0) { + if (list_len == 0) + test_dbg("EMPTY!!\n"); + else + test_dbg("call listxattr1 FAILED, error: %s\n", + strerror(errno)); + return; + } + + list_value = (char *) malloc(list_len * sizeof(char)); + if (!list_value) { + test_dbg("malloc FAILED, error: %s\n", + strerror(errno)); + return; + } + list_len = listxattr(filename, list_value, list_len); + if (list_len <= 0) { + test_dbg("call listxattr2 FAILED, error: %s\n", + strerror(errno)); + goto out; + } + for (i = 0; i < list_len; ) { + ret = getxattr(filename, list_value + i, value_buf, BUF_LEN); + if (ret < 0) { + test_dbg("getxattr error: %s\n", strerror(errno)); + goto out; + } + value_buf[ret] = 0; + + name_index = find_name_index(cases, len, list_value + i); + + if (name_index < 0) { + test_dbg("listxattr error: find name index failed\n"); + goto out; + } + print_result(++count, 0, strcmp(value_buf, cases[name_index].value)); + + i += strlen(list_value + i) + 1; + } + +out: + free(list_value); +} + +void test_removexattr(void) +{ + int len, list_len; + char *list_value; + int name_index, ret, i; + int count = 1; + struct test_data cases[] = { + {XATTR_NAME(removexattr-name1), "removexattr-value1", + strlen("removexattr-value1"), 0}, + {XATTR_NAME(removexattr-name2), "removexattr-valuee2", + strlen("removexattr-value2"), 0}, + }; + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + ret = setxattr(filename, cases[0].name, cases[0].value, + cases[0].val_size, XATTR_CREATE); + if (ret) { + test_dbg("create xattr FAILED!!\n"); + return; + } + + list_len = listxattr(filename, NULL, 0); + print_result(count++, strlen(cases[0].name), list_len - 1); + + + ret = removexattr(filename, cases[0].name); + print_result(count++, 0, ret); + + list_len = listxattr(filename, NULL, 0); + print_result(count++, 0, list_len); // case3 + + len = sizeof(cases) / sizeof(struct test_data); + for (i = 0; i < len; i++) { + ret = setxattr(filename, cases[i].name, cases[i].value, + cases[i].val_size, XATTR_CREATE); + if (ret) { + test_dbg("create xattr FAILED!!\n"); + return; + } + } + + + ret = removexattr(filename, cases[0].name); + print_result(count++, 0, ret); + + + list_len = listxattr(filename, NULL, 0); + print_result(count++, strlen(cases[1].name), list_len - 1); + +} + +void test_replacexattr(void) +{ + int ret, i, len; + + char *test_name = XATTR_NAME(replacexattr); + char *val = "test-value"; + int val_len = strlen(val); + + struct test_data cases[] = { + {NULL, NULL, 0, EFAULT}, + {test_name, NULL, 0, 0}, + {test_name, "aaa", 3, 0}, // 3 + {test_name, "aaab", 4, 0}, + {test_name, "aa", 2, 0}, + {test_name, str_page, PAGE_SIZE, ENOSPC}, //6 + {"user.nodata", "haha", 4, ENODATA}, + }; + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + ret = setxattr(filename, test_name, val, + val_len, XATTR_CREATE); + if (ret) { + test_dbg("create attr failed!\n"); + return; + } + + ret = setxattr(filename, XATTR_NAME(replacexattr-tt), "test-stub", + strlen("test-stub"), XATTR_CREATE); + if (ret) { + test_dbg("create attr failed!\n"); + return; + } + + len = sizeof(cases) / sizeof(struct test_data); + for (i = 0; i < len; i++) { + errno = 0; + ret = setxattr(filename, cases[i].name, cases[i].value, + cases[i].val_size, XATTR_REPLACE); + print_result(i + 1, cases[i].expect, errno); + } +} + +void test_setxattr_full(void) +{ + char name_buf[BUF_LEN]; + char *value = "testvalue"; + int value_len = strlen(value); + int i = 1, ret; + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + while (1) { + sprintf(name_buf, "%ssetxattr_full%d", XATTR_PREFIX, i); + errno = 0; + ret = setxattr(filename, name_buf, value, + value_len, XATTR_CREATE); + if (ret) { + test_dbg("create error: i=%d, errno=%d, errstr=%s\n", + i, errno, strerror(errno)); + break; + } + i++; + } +} + +void test_full2empty(void) +{ + char name_buf[BUF_LEN]; + char *value = "testvalue"; + int value_len = strlen(value); + int i = 1, count, ret; + + if (setup()) { + test_dbg("setup failed!\n"); + return; + } + + while (1) { + sprintf(name_buf, "%stest_full2empty%d", XATTR_PREFIX, i); + errno = 0; + ret = setxattr(filename, name_buf, value, + value_len, XATTR_CREATE); + if (ret) + break; + i++; + } + count = i; + + for (i = 1; i < count; i++) { + sprintf(name_buf, "%stest_full2empty%d", XATTR_PREFIX, i); + errno = 0; + ret = removexattr(filename, name_buf); + if (ret) { + test_dbg("create error: i=%d, errno=%d, errstr=%s\n", + i, errno, strerror(errno)); + break; + } + } + if (i == count) + print_result(1, count, i); +} + +char name_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIGHLMNOPQRSTUVWXYZ0123456789"; + + +char *create_random_name(int *out_len) +{ +#define MAX_NAME_LEN 255 + int len = 0, i; + char *ret = NULL; + int name_chars_len = strlen(name_chars); + + len = rand() % (MAX_NAME_LEN) + 1; + ret = malloc(sizeof(char) * len + 1); + if (!ret) + return NULL; + sprintf(ret, XATTR_PREFIX); + for (i = strlen(XATTR_PREFIX); i < len; i++) + ret[i] = name_chars[rand() % name_chars_len]; + ret[i] = 0; + if (out_len) + *out_len = len; + return ret; +} + +char *create_random_value(int *out_len) +{ +#define MAX_VALUE_LEN PAGE_SIZE + int len, i; + char *ret; + + len = rand() % (MAX_NAME_LEN) + 1; + ret = malloc(sizeof(char) * len); + if (!ret) + return NULL; + for (i = 0; i < len; i++) + ret[i] = rand() % 256; + if (out_len) + *out_len = len; + return ret; +} +void test_random_case(void) +{ +#define NUM 100 + int i, op, name_len, value_len; + char *name = NULL, *value = NULL; + char tmpvalue[PAGE_SIZE]; + int ret, count = 1; + + test_dbg("......\n"); + srand(time(NULL)); + for (i = 0; i < NUM; i++, count++) { + memset(tmpvalue, 0, PAGE_SIZE); + op = rand() % 3; + + name = create_random_name(&name_len); + value = create_random_value(&value_len); + + if (!name || !value) + goto free; + switch (op) { + case 0: // create + ret = setxattr(filename, name, value, + value_len, XATTR_CREATE); + if (!ret) { + ret = getxattr(filename, name, tmpvalue, PAGE_SIZE); + print_result_cond(count, ret == value_len + && !memcmp(tmpvalue, value, value_len)); + } + break; + + case 1: // remove + ret = removexattr(filename, name); + if (!ret) { + ret = getxattr(filename, name, tmpvalue, PAGE_SIZE); + print_result_cond(count, !ret && errno == ENODATA); + } + + break; + + case 2: // replace + ret = setxattr(filename, name, value, + value_len, XATTR_REPLACE); + if (!ret) { + ret = getxattr(filename, name, tmpvalue, PAGE_SIZE); + print_result_cond(count, ret == value_len + && !memcmp(tmpvalue, value, value_len)); + } + break; + + default: + break; + } +free: + free(name); + free(value); + } + +} + +int main(int argc, char *argv[]) +{ + + char value[BUF_LEN], *list_value, *p; + int list_len; + int i, ret; + struct stat file_info; + struct timespec start, end; + + void (*test_fun[])(void) = { + test_setxattr, + test_getxattr, + test_listxattr, + test_removexattr, + test_replacexattr, + test_setxattr_full, + test_full2empty, + test_random_case, + }; + + if (argc < 2) { + test_dbg("Need file system folder!\n"); + return 1; + } + + ret = stat(argv[1], &file_info); + if (ret || !S_ISDIR(file_info.st_mode)) { + test_dbg("Bad folder!\n"); + return 1; + } + + printf("PAGE_SIZE=%ld\n", PAGE_SIZE); + + snprintf(filename, BUF_LEN, "%s/test", argv[1]); + + create_big_buf(); + + list_len = sizeof(test_fun) / sizeof(void *); + for (i = 0; i < list_len; i++) { + start.tv_sec = start.tv_nsec = 0; + end.tv_sec = end.tv_nsec = 0; + clock_gettime(CLOCK_REALTIME, &start); + test_fun[i](); + clock_gettime(CLOCK_REALTIME, &end); + printf("exec time: %lds.%ldns\n", + end.tv_sec-start.tv_sec, + end.tv_nsec-start.tv_nsec); + } + return 0; +}