781 lines
15 KiB
C
781 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright 2021 Google LLC
|
|
*/
|
|
|
|
#include "test_fuse.h"
|
|
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <gelf.h>
|
|
#include <libelf.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/mount.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/statfs.h>
|
|
#include <sys/xattr.h>
|
|
|
|
#include <linux/unistd.h>
|
|
|
|
#include <include/uapi/linux/fuse.h>
|
|
#include <include/uapi/linux/bpf.h>
|
|
|
|
struct _test_options test_options;
|
|
|
|
struct s s(const char *s1)
|
|
{
|
|
struct s s = {0};
|
|
|
|
if (!s1)
|
|
return s;
|
|
|
|
s.s = malloc(strlen(s1) + 1);
|
|
if (!s.s)
|
|
return s;
|
|
|
|
strcpy(s.s, s1);
|
|
return s;
|
|
}
|
|
|
|
struct s sn(const char *s1, const char *s2)
|
|
{
|
|
struct s s = {0};
|
|
|
|
if (!s1)
|
|
return s;
|
|
|
|
s.s = malloc(s2 - s1 + 1);
|
|
if (!s.s)
|
|
return s;
|
|
|
|
strncpy(s.s, s1, s2 - s1);
|
|
s.s[s2 - s1] = 0;
|
|
return s;
|
|
}
|
|
|
|
int s_cmp(struct s s1, struct s s2)
|
|
{
|
|
int result = -1;
|
|
|
|
if (!s1.s || !s2.s)
|
|
goto out;
|
|
result = strcmp(s1.s, s2.s);
|
|
out:
|
|
free(s1.s);
|
|
free(s2.s);
|
|
return result;
|
|
}
|
|
|
|
struct s s_cat(struct s s1, struct s s2)
|
|
{
|
|
struct s s = {0};
|
|
|
|
if (!s1.s || !s2.s)
|
|
goto out;
|
|
|
|
s.s = malloc(strlen(s1.s) + strlen(s2.s) + 1);
|
|
if (!s.s)
|
|
goto out;
|
|
|
|
strcpy(s.s, s1.s);
|
|
strcat(s.s, s2.s);
|
|
out:
|
|
free(s1.s);
|
|
free(s2.s);
|
|
return s;
|
|
}
|
|
|
|
struct s s_splitleft(struct s s1, char c)
|
|
{
|
|
struct s s = {0};
|
|
char *split;
|
|
|
|
if (!s1.s)
|
|
return s;
|
|
|
|
split = strchr(s1.s, c);
|
|
if (split)
|
|
s = sn(s1.s, split);
|
|
|
|
free(s1.s);
|
|
return s;
|
|
}
|
|
|
|
struct s s_splitright(struct s s1, char c)
|
|
{
|
|
struct s s2 = {0};
|
|
char *split;
|
|
|
|
if (!s1.s)
|
|
return s2;
|
|
|
|
split = strchr(s1.s, c);
|
|
if (split)
|
|
s2 = s(split + 1);
|
|
|
|
free(s1.s);
|
|
return s2;
|
|
}
|
|
|
|
struct s s_word(struct s s1, char c, size_t n)
|
|
{
|
|
while (n--)
|
|
s1 = s_splitright(s1, c);
|
|
return s_splitleft(s1, c);
|
|
}
|
|
|
|
struct s s_path(struct s s1, struct s s2)
|
|
{
|
|
return s_cat(s_cat(s1, s("/")), s2);
|
|
}
|
|
|
|
struct s s_pathn(size_t n, struct s s1, ...)
|
|
{
|
|
va_list argp;
|
|
|
|
va_start(argp, s1);
|
|
while (--n)
|
|
s1 = s_path(s1, va_arg(argp, struct s));
|
|
va_end(argp);
|
|
return s1;
|
|
}
|
|
|
|
int s_link(struct s src_pathname, struct s dst_pathname)
|
|
{
|
|
int res;
|
|
|
|
if (src_pathname.s && dst_pathname.s) {
|
|
res = link(src_pathname.s, dst_pathname.s);
|
|
} else {
|
|
res = -1;
|
|
errno = ENOMEM;
|
|
}
|
|
|
|
free(src_pathname.s);
|
|
free(dst_pathname.s);
|
|
return res;
|
|
}
|
|
|
|
int s_symlink(struct s src_pathname, struct s dst_pathname)
|
|
{
|
|
int res;
|
|
|
|
if (src_pathname.s && dst_pathname.s) {
|
|
res = symlink(src_pathname.s, dst_pathname.s);
|
|
} else {
|
|
res = -1;
|
|
errno = ENOMEM;
|
|
}
|
|
|
|
free(src_pathname.s);
|
|
free(dst_pathname.s);
|
|
return res;
|
|
}
|
|
|
|
|
|
int s_mkdir(struct s pathname, mode_t mode)
|
|
{
|
|
int res;
|
|
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
res = mkdir(pathname.s, mode);
|
|
free(pathname.s);
|
|
return res;
|
|
}
|
|
|
|
int s_rmdir(struct s pathname)
|
|
{
|
|
int res;
|
|
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
res = rmdir(pathname.s);
|
|
free(pathname.s);
|
|
return res;
|
|
}
|
|
|
|
int s_unlink(struct s pathname)
|
|
{
|
|
int res;
|
|
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
res = unlink(pathname.s);
|
|
free(pathname.s);
|
|
return res;
|
|
}
|
|
|
|
int s_open(struct s pathname, int flags, ...)
|
|
{
|
|
va_list ap;
|
|
int res;
|
|
|
|
va_start(ap, flags);
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
if (flags & (O_CREAT | O_TMPFILE))
|
|
res = open(pathname.s, flags, va_arg(ap, mode_t));
|
|
else
|
|
res = open(pathname.s, flags);
|
|
|
|
free(pathname.s);
|
|
va_end(ap);
|
|
return res;
|
|
}
|
|
|
|
int s_openat(int dirfd, struct s pathname, int flags, ...)
|
|
{
|
|
va_list ap;
|
|
int res;
|
|
|
|
va_start(ap, flags);
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
if (flags & (O_CREAT | O_TMPFILE))
|
|
res = openat(dirfd, pathname.s, flags, va_arg(ap, mode_t));
|
|
else
|
|
res = openat(dirfd, pathname.s, flags);
|
|
|
|
free(pathname.s);
|
|
va_end(ap);
|
|
return res;
|
|
}
|
|
|
|
int s_creat(struct s pathname, mode_t mode)
|
|
{
|
|
int res;
|
|
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
res = open(pathname.s, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
|
|
free(pathname.s);
|
|
return res;
|
|
}
|
|
|
|
int s_mkfifo(struct s pathname, mode_t mode)
|
|
{
|
|
int res;
|
|
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
res = mknod(pathname.s, S_IFIFO | mode, 0);
|
|
free(pathname.s);
|
|
return res;
|
|
}
|
|
|
|
int s_stat(struct s pathname, struct stat *st)
|
|
{
|
|
int res;
|
|
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
res = stat(pathname.s, st);
|
|
free(pathname.s);
|
|
return res;
|
|
}
|
|
|
|
int s_statfs(struct s pathname, struct statfs *st)
|
|
{
|
|
int res;
|
|
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
res = statfs(pathname.s, st);
|
|
free(pathname.s);
|
|
return res;
|
|
}
|
|
|
|
DIR *s_opendir(struct s pathname)
|
|
{
|
|
DIR *res;
|
|
|
|
res = opendir(pathname.s);
|
|
free(pathname.s);
|
|
return res;
|
|
}
|
|
|
|
int s_getxattr(struct s pathname, const char name[], void *value, size_t size,
|
|
ssize_t *ret_size)
|
|
{
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
*ret_size = getxattr(pathname.s, name, value, size);
|
|
free(pathname.s);
|
|
return *ret_size >= 0 ? 0 : -1;
|
|
}
|
|
|
|
int s_listxattr(struct s pathname, void *list, size_t size, ssize_t *ret_size)
|
|
{
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
*ret_size = listxattr(pathname.s, list, size);
|
|
free(pathname.s);
|
|
return *ret_size >= 0 ? 0 : -1;
|
|
}
|
|
|
|
int s_setxattr(struct s pathname, const char name[], const void *value, size_t size, int flags)
|
|
{
|
|
int res;
|
|
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
res = setxattr(pathname.s, name, value, size, flags);
|
|
free(pathname.s);
|
|
return res;
|
|
}
|
|
|
|
int s_removexattr(struct s pathname, const char name[])
|
|
{
|
|
int res;
|
|
|
|
if (!pathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
res = removexattr(pathname.s, name);
|
|
free(pathname.s);
|
|
return res;
|
|
}
|
|
|
|
int s_rename(struct s oldpathname, struct s newpathname)
|
|
{
|
|
int res;
|
|
|
|
if (!oldpathname.s || !newpathname.s) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
res = rename(oldpathname.s, newpathname.s);
|
|
free(oldpathname.s);
|
|
free(newpathname.s);
|
|
return res;
|
|
}
|
|
|
|
int s_fuse_attr(struct s pathname, struct fuse_attr *fuse_attr_out)
|
|
{
|
|
|
|
struct stat st;
|
|
int result = TEST_FAILURE;
|
|
|
|
TESTSYSCALL(s_stat(pathname, &st));
|
|
|
|
fuse_attr_out->ino = st.st_ino;
|
|
fuse_attr_out->mode = st.st_mode;
|
|
fuse_attr_out->nlink = st.st_nlink;
|
|
fuse_attr_out->uid = st.st_uid;
|
|
fuse_attr_out->gid = st.st_gid;
|
|
fuse_attr_out->rdev = st.st_rdev;
|
|
fuse_attr_out->size = st.st_size;
|
|
fuse_attr_out->blksize = st.st_blksize;
|
|
fuse_attr_out->blocks = st.st_blocks;
|
|
fuse_attr_out->atime = st.st_atime;
|
|
fuse_attr_out->mtime = st.st_mtime;
|
|
fuse_attr_out->ctime = st.st_ctime;
|
|
fuse_attr_out->atimensec = UINT32_MAX;
|
|
fuse_attr_out->mtimensec = UINT32_MAX;
|
|
fuse_attr_out->ctimensec = UINT32_MAX;
|
|
|
|
result = TEST_SUCCESS;
|
|
out:
|
|
return result;
|
|
}
|
|
|
|
struct s tracing_folder(void)
|
|
{
|
|
struct s trace = {0};
|
|
FILE *mounts = NULL;
|
|
char *line = NULL;
|
|
size_t size = 0;
|
|
|
|
TEST(mounts = fopen("/proc/mounts", "re"), mounts);
|
|
while (getline(&line, &size, mounts) != -1) {
|
|
if (!s_cmp(s_word(sn(line, line + size), ' ', 2),
|
|
s("tracefs"))) {
|
|
trace = s_word(sn(line, line + size), ' ', 1);
|
|
break;
|
|
}
|
|
|
|
if (!s_cmp(s_word(sn(line, line + size), ' ', 2), s("debugfs")))
|
|
trace = s_path(s_word(sn(line, line + size), ' ', 1),
|
|
s("tracing"));
|
|
}
|
|
|
|
out:
|
|
free(line);
|
|
fclose(mounts);
|
|
return trace;
|
|
}
|
|
|
|
int tracing_on(void)
|
|
{
|
|
int result = TEST_FAILURE;
|
|
int tracing_on = -1;
|
|
|
|
TEST(tracing_on = s_open(s_path(tracing_folder(), s("tracing_on")),
|
|
O_WRONLY | O_CLOEXEC),
|
|
tracing_on != -1);
|
|
TESTEQUAL(write(tracing_on, "1", 1), 1);
|
|
result = TEST_SUCCESS;
|
|
out:
|
|
close(tracing_on);
|
|
return result;
|
|
}
|
|
|
|
char *concat_file_name(const char *dir, const char *file)
|
|
{
|
|
char full_name[FILENAME_MAX] = "";
|
|
|
|
if (snprintf(full_name, ARRAY_SIZE(full_name), "%s/%s", dir, file) < 0)
|
|
return NULL;
|
|
return strdup(full_name);
|
|
}
|
|
|
|
char *setup_mount_dir(const char *name)
|
|
{
|
|
struct stat st;
|
|
char *current_dir = getcwd(NULL, 0);
|
|
char *mount_dir = concat_file_name(current_dir, name);
|
|
|
|
free(current_dir);
|
|
if (stat(mount_dir, &st) == 0) {
|
|
if (S_ISDIR(st.st_mode))
|
|
return mount_dir;
|
|
|
|
ksft_print_msg("%s is a file, not a dir.\n", mount_dir);
|
|
return NULL;
|
|
}
|
|
|
|
if (mkdir(mount_dir, 0777)) {
|
|
ksft_print_msg("Can't create mount dir.");
|
|
return NULL;
|
|
}
|
|
|
|
return mount_dir;
|
|
}
|
|
|
|
int delete_dir_tree(const char *dir_path, bool remove_root)
|
|
{
|
|
DIR *dir = NULL;
|
|
struct dirent *dp;
|
|
int result = 0;
|
|
|
|
dir = opendir(dir_path);
|
|
if (!dir) {
|
|
result = -errno;
|
|
goto out;
|
|
}
|
|
|
|
while ((dp = readdir(dir))) {
|
|
char *full_path;
|
|
|
|
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
|
|
continue;
|
|
|
|
full_path = concat_file_name(dir_path, dp->d_name);
|
|
if (dp->d_type == DT_DIR)
|
|
result = delete_dir_tree(full_path, true);
|
|
else
|
|
result = unlink(full_path);
|
|
free(full_path);
|
|
if (result)
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
if (dir)
|
|
closedir(dir);
|
|
if (!result && remove_root)
|
|
rmdir(dir_path);
|
|
return result;
|
|
}
|
|
|
|
static int mount_fuse_maybe_init(const char *mount_dir, int bpf_fd, int dir_fd,
|
|
int *fuse_dev_ptr, bool init)
|
|
{
|
|
int result = TEST_FAILURE;
|
|
int fuse_dev = -1;
|
|
char options[FILENAME_MAX];
|
|
uint8_t bytes_in[FUSE_MIN_READ_BUFFER];
|
|
uint8_t bytes_out[FUSE_MIN_READ_BUFFER];
|
|
|
|
DECL_FUSE_IN(init);
|
|
|
|
TEST(fuse_dev = open("/dev/fuse", O_RDWR | O_CLOEXEC), fuse_dev != -1);
|
|
snprintf(options, FILENAME_MAX, "fd=%d,user_id=0,group_id=0,rootmode=0040000",
|
|
fuse_dev);
|
|
if (bpf_fd != -1)
|
|
snprintf(options + strlen(options),
|
|
sizeof(options) - strlen(options),
|
|
",root_bpf=%d", bpf_fd);
|
|
if (dir_fd != -1)
|
|
snprintf(options + strlen(options),
|
|
sizeof(options) - strlen(options),
|
|
",root_dir=%d", dir_fd);
|
|
TESTSYSCALL(mount("ABC", mount_dir, "fuse", 0, options));
|
|
|
|
if (init) {
|
|
TESTFUSEIN(FUSE_INIT, init_in);
|
|
TESTEQUAL(init_in->major, FUSE_KERNEL_VERSION);
|
|
TESTEQUAL(init_in->minor, FUSE_KERNEL_MINOR_VERSION);
|
|
TESTFUSEOUT1(fuse_init_out, ((struct fuse_init_out) {
|
|
.major = FUSE_KERNEL_VERSION,
|
|
.minor = FUSE_KERNEL_MINOR_VERSION,
|
|
.max_readahead = 4096,
|
|
.flags = 0,
|
|
.max_background = 0,
|
|
.congestion_threshold = 0,
|
|
.max_write = 4096,
|
|
.time_gran = 1000,
|
|
.max_pages = 12,
|
|
.map_alignment = 4096,
|
|
}));
|
|
}
|
|
|
|
*fuse_dev_ptr = fuse_dev;
|
|
fuse_dev = -1;
|
|
result = TEST_SUCCESS;
|
|
out:
|
|
close(fuse_dev);
|
|
return result;
|
|
}
|
|
|
|
int mount_fuse(const char *mount_dir, int bpf_fd, int dir_fd, int *fuse_dev_ptr)
|
|
{
|
|
return mount_fuse_maybe_init(mount_dir, bpf_fd, dir_fd, fuse_dev_ptr,
|
|
true);
|
|
}
|
|
|
|
int mount_fuse_no_init(const char *mount_dir, int bpf_fd, int dir_fd,
|
|
int *fuse_dev_ptr)
|
|
{
|
|
return mount_fuse_maybe_init(mount_dir, bpf_fd, dir_fd, fuse_dev_ptr,
|
|
false);
|
|
}
|
|
|
|
struct fuse_bpf_map {
|
|
unsigned int map_type;
|
|
size_t key_size;
|
|
size_t value_size;
|
|
unsigned int max_entries;
|
|
};
|
|
|
|
static int install_maps(Elf_Data *maps, int maps_index, Elf *elf,
|
|
Elf_Data *symbols, int symbol_index,
|
|
struct map_relocation **mr, size_t *map_count)
|
|
{
|
|
int result = TEST_FAILURE;
|
|
int i;
|
|
GElf_Sym symbol;
|
|
|
|
TESTNE((void *)symbols, NULL);
|
|
|
|
for (i = 0; i < symbols->d_size / sizeof(symbol); ++i) {
|
|
TESTNE((void *)gelf_getsym(symbols, i, &symbol), 0);
|
|
if (symbol.st_shndx == maps_index) {
|
|
struct fuse_bpf_map *map;
|
|
union bpf_attr attr;
|
|
int map_fd;
|
|
|
|
map = (struct fuse_bpf_map *)
|
|
((char *)maps->d_buf + symbol.st_value);
|
|
|
|
attr = (union bpf_attr) {
|
|
.map_type = map->map_type,
|
|
.key_size = map->key_size,
|
|
.value_size = map->value_size,
|
|
.max_entries = map->max_entries,
|
|
};
|
|
|
|
TEST(*mr = realloc(*mr, ++*map_count *
|
|
sizeof(struct fuse_bpf_map)),
|
|
*mr);
|
|
TEST(map_fd = syscall(__NR_bpf, BPF_MAP_CREATE,
|
|
&attr, sizeof(attr)),
|
|
map_fd != -1);
|
|
(*mr)[*map_count - 1] = (struct map_relocation) {
|
|
.name = strdup(elf_strptr(elf, symbol_index,
|
|
symbol.st_name)),
|
|
.fd = map_fd,
|
|
.value = symbol.st_value,
|
|
};
|
|
}
|
|
}
|
|
|
|
result = TEST_SUCCESS;
|
|
out:
|
|
return result;
|
|
}
|
|
|
|
static inline int relocate_maps(GElf_Shdr *rel_header, Elf_Data *rel_data,
|
|
Elf_Data *prog_data, Elf_Data *symbol_data,
|
|
struct map_relocation *map_relocations,
|
|
size_t map_count)
|
|
{
|
|
int result = TEST_FAILURE;
|
|
int i;
|
|
struct bpf_insn *insns = (struct bpf_insn *) prog_data->d_buf;
|
|
|
|
for (i = 0; i < rel_header->sh_size / rel_header->sh_entsize; ++i) {
|
|
GElf_Sym sym;
|
|
GElf_Rel rel;
|
|
unsigned int insn_idx;
|
|
int map_idx;
|
|
|
|
gelf_getrel(rel_data, i, &rel);
|
|
insn_idx = rel.r_offset / sizeof(struct bpf_insn);
|
|
insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
|
|
|
|
gelf_getsym(symbol_data, GELF_R_SYM(rel.r_info), &sym);
|
|
for (map_idx = 0; map_idx < map_count; map_idx++) {
|
|
if (map_relocations[map_idx].value == sym.st_value) {
|
|
insns[insn_idx].imm =
|
|
map_relocations[map_idx].fd;
|
|
break;
|
|
}
|
|
}
|
|
TESTNE(map_idx, map_count);
|
|
}
|
|
|
|
result = TEST_SUCCESS;
|
|
out:
|
|
return result;
|
|
}
|
|
|
|
int install_elf_bpf(const char *file, const char *section, int *fd,
|
|
struct map_relocation **map_relocations, size_t *map_count)
|
|
{
|
|
int result = TEST_FAILURE;
|
|
char path[PATH_MAX] = {};
|
|
char *last_slash;
|
|
int filter_fd = -1;
|
|
union bpf_attr bpf_attr;
|
|
static char log[1 << 20];
|
|
Elf *elf = NULL;
|
|
GElf_Ehdr ehdr;
|
|
Elf_Data *data_prog = NULL, *data_maps = NULL, *data_symbols = NULL;
|
|
int maps_index, symbol_index, prog_index;
|
|
int i;
|
|
|
|
TESTNE(readlink("/proc/self/exe", path, PATH_MAX), -1);
|
|
TEST(last_slash = strrchr(path, '/'), last_slash);
|
|
strcpy(last_slash + 1, file);
|
|
TEST(filter_fd = open(path, O_RDONLY | O_CLOEXEC), filter_fd != -1);
|
|
TESTNE(elf_version(EV_CURRENT), EV_NONE);
|
|
TEST(elf = elf_begin(filter_fd, ELF_C_READ, NULL), elf);
|
|
TESTEQUAL((void *) gelf_getehdr(elf, &ehdr), &ehdr);
|
|
for (i = 1; i < ehdr.e_shnum; i++) {
|
|
char *shname;
|
|
GElf_Shdr shdr;
|
|
Elf_Scn *scn;
|
|
|
|
TEST(scn = elf_getscn(elf, i), scn);
|
|
TESTEQUAL((void *)gelf_getshdr(scn, &shdr), &shdr);
|
|
TEST(shname = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name),
|
|
shname);
|
|
|
|
if (!strcmp(shname, "maps")) {
|
|
TEST(data_maps = elf_getdata(scn, 0), data_maps);
|
|
maps_index = i;
|
|
} else if (shdr.sh_type == SHT_SYMTAB) {
|
|
TEST(data_symbols = elf_getdata(scn, 0), data_symbols);
|
|
symbol_index = shdr.sh_link;
|
|
} else if (!strcmp(shname, section)) {
|
|
TEST(data_prog = elf_getdata(scn, 0), data_prog);
|
|
prog_index = i;
|
|
}
|
|
}
|
|
TESTNE((void *) data_prog, NULL);
|
|
|
|
if (data_maps)
|
|
TESTEQUAL(install_maps(data_maps, maps_index, elf,
|
|
data_symbols, symbol_index,
|
|
map_relocations, map_count), 0);
|
|
|
|
/* Now relocate maps */
|
|
for (i = 1; i < ehdr.e_shnum; i++) {
|
|
GElf_Shdr rel_header;
|
|
Elf_Scn *scn;
|
|
Elf_Data *rel_data;
|
|
|
|
TEST(scn = elf_getscn(elf, i), scn);
|
|
TESTEQUAL((void *)gelf_getshdr(scn, &rel_header),
|
|
&rel_header);
|
|
if (rel_header.sh_type != SHT_REL)
|
|
continue;
|
|
TEST(rel_data = elf_getdata(scn, 0), rel_data);
|
|
|
|
if (rel_header.sh_info != prog_index)
|
|
continue;
|
|
TESTEQUAL(relocate_maps(&rel_header, rel_data,
|
|
data_prog, data_symbols,
|
|
*map_relocations, *map_count),
|
|
0);
|
|
}
|
|
|
|
bpf_attr = (union bpf_attr) {
|
|
.prog_type = BPF_PROG_TYPE_FUSE,
|
|
.insn_cnt = data_prog->d_size / 8,
|
|
.insns = ptr_to_u64(data_prog->d_buf),
|
|
.license = ptr_to_u64("GPL"),
|
|
.log_buf = test_options.verbose ? ptr_to_u64(log) : 0,
|
|
.log_size = test_options.verbose ? sizeof(log) : 0,
|
|
.log_level = test_options.verbose ? 2 : 0,
|
|
};
|
|
*fd = syscall(__NR_bpf, BPF_PROG_LOAD, &bpf_attr, sizeof(bpf_attr));
|
|
if (test_options.verbose)
|
|
ksft_print_msg("%s\n", log);
|
|
if (*fd == -1 && errno == ENOSPC)
|
|
ksft_print_msg("bpf log size too small!\n");
|
|
TESTNE(*fd, -1);
|
|
|
|
result = TEST_SUCCESS;
|
|
out:
|
|
close(filter_fd);
|
|
return result;
|
|
}
|
|
|
|
|