mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
selftests/fuse: add fuse readdir caching test
This new test will check the caching behaviour using combinations of two opendir flags: FOPEN_KEEP_CACHE and FOPEN_CACHE_DIR. Signed-off-by: Luis Henriques <luis@igalia.com> Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
parent
3e14f079d7
commit
1428be3d84
|
|
@ -5,3 +5,4 @@ test_syncfs
|
|||
write_extend_eof_test
|
||||
fuse_acl_cache_test
|
||||
fuse_symlink_cache_test
|
||||
fuse_readdir_cache_test
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ TEST_GEN_PROGS := fusectl_test test_syncfs
|
|||
TEST_GEN_PROGS += write_extend_eof_test
|
||||
TEST_GEN_PROGS += fuse_acl_cache_test
|
||||
TEST_GEN_PROGS += fuse_symlink_cache_test
|
||||
TEST_GEN_PROGS += fuse_readdir_cache_test
|
||||
TEST_GEN_FILES := fuse_mnt
|
||||
|
||||
include ../../lib.mk
|
||||
|
|
@ -30,5 +31,6 @@ $(OUTPUT)/fuse_mnt: LDLIBS += $(VAR_LDLIBS)
|
|||
|
||||
$(OUTPUT)/fuse_acl_cache_test: fuse_common.c fuse_acl_cache_test.c
|
||||
$(OUTPUT)/fuse_symlink_cache_test: fuse_common.c fuse_symlink_cache_test.c
|
||||
$(OUTPUT)/fuse_readdir_cache_test: fuse_common.c fuse_readdir_cache_test.c
|
||||
|
||||
EXTRA_CLEAN := fuse_common.o
|
||||
|
|
|
|||
|
|
@ -0,0 +1,273 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Simple filesystem to test FUSE readdir cache
|
||||
*
|
||||
* It will simply perform readdir operations on a directory checking how many
|
||||
* times a request is sent to user-space using all the possible caching
|
||||
* combination setting (FOPEN_KEEP_CACHE and FOPEN_CACHE_DIR flags).
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "kselftest_harness.h"
|
||||
|
||||
#include "fuse_common.h"
|
||||
|
||||
#define DIRNAME "mydir"
|
||||
#define FILENAME "myfile"
|
||||
|
||||
#define DIR_INO 42
|
||||
#define FILE_INO 43
|
||||
#define DOT_INO 40
|
||||
#define DOTDOT_INO 41
|
||||
|
||||
#define TIMEOUT 86400.0f
|
||||
|
||||
struct test_state {
|
||||
pthread_mutex_t lock;
|
||||
time_t mtime;
|
||||
int readdir_counter;
|
||||
bool cache_readdir;
|
||||
bool keep_cache;
|
||||
} test_state = {
|
||||
.lock = PTHREAD_MUTEX_INITIALIZER,
|
||||
};
|
||||
|
||||
static void fs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
|
||||
{
|
||||
struct fuse_entry_param e = {};
|
||||
|
||||
if (parent != FUSE_ROOT_ID || strcmp(name, DIRNAME) != 0) {
|
||||
fuse_reply_err(req, ENOENT);
|
||||
return;
|
||||
}
|
||||
|
||||
e.ino = DIR_INO;
|
||||
e.attr.st_mode = S_IFDIR | 0755;
|
||||
e.attr.st_nlink = 1;
|
||||
pthread_mutex_lock(&test_state.lock);
|
||||
e.attr.st_mtime = test_state.mtime;
|
||||
pthread_mutex_unlock(&test_state.lock);
|
||||
e.attr_timeout = TIMEOUT;
|
||||
e.entry_timeout = TIMEOUT;
|
||||
|
||||
fuse_reply_entry(req, &e);
|
||||
}
|
||||
|
||||
static int fill_stat(fuse_ino_t ino, struct stat *st)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
st->st_ino = ino;
|
||||
pthread_mutex_lock(&test_state.lock);
|
||||
st->st_mtime = test_state.mtime;
|
||||
pthread_mutex_unlock(&test_state.lock);
|
||||
|
||||
switch (ino) {
|
||||
case FUSE_ROOT_ID:
|
||||
st->st_mode = S_IFDIR | 0755;
|
||||
st->st_nlink = 2;
|
||||
break;
|
||||
case DOT_INO:
|
||||
case DOTDOT_INO:
|
||||
case DIR_INO:
|
||||
st->st_mode = S_IFDIR | 0755;
|
||||
st->st_nlink = 1;
|
||||
break;
|
||||
case FILE_INO:
|
||||
st->st_mode = S_IFREG | 0444;
|
||||
st->st_nlink = 1;
|
||||
break;
|
||||
default:
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fs_getattr(fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
struct stat st = {};
|
||||
|
||||
if (fill_stat(ino, &st) < 0)
|
||||
fuse_reply_err(req, ENOENT);
|
||||
else
|
||||
fuse_reply_attr(req, &st, TIMEOUT);
|
||||
}
|
||||
|
||||
static void fs_opendir(fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
pthread_mutex_lock(&test_state.lock);
|
||||
fi->keep_cache = test_state.keep_cache;
|
||||
fi->cache_readdir = test_state.cache_readdir;
|
||||
pthread_mutex_unlock(&test_state.lock);
|
||||
fuse_reply_open(req, fi);
|
||||
}
|
||||
|
||||
static void fs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
{
|
||||
struct stat st = {};
|
||||
char buf[1024];
|
||||
char *pbuf;
|
||||
size_t rem = size;
|
||||
size_t sz;
|
||||
int nextoff = 0;
|
||||
|
||||
if (ino != DIR_INO) {
|
||||
fuse_reply_err(req, ENOTDIR);
|
||||
return;
|
||||
}
|
||||
if (offset) {
|
||||
fuse_reply_buf(req, NULL, 0);
|
||||
return;
|
||||
}
|
||||
pbuf = buf;
|
||||
fill_stat(DOT_INO, &st);
|
||||
sz = fuse_add_direntry(req, pbuf, rem, ".", &st, nextoff++);
|
||||
rem -= sz;
|
||||
pbuf += sz;
|
||||
fill_stat(DOTDOT_INO, &st);
|
||||
sz = fuse_add_direntry(req, pbuf, rem, "..", &st, nextoff++);
|
||||
rem -= sz;
|
||||
pbuf += sz;
|
||||
fill_stat(FILE_INO, &st);
|
||||
sz = fuse_add_direntry(req, pbuf, rem, FILENAME, &st, nextoff++);
|
||||
rem -= sz;
|
||||
|
||||
fuse_reply_buf(req, buf, size - rem);
|
||||
|
||||
pthread_mutex_lock(&test_state.lock);
|
||||
test_state.readdir_counter++;
|
||||
pthread_mutex_unlock(&test_state.lock);
|
||||
}
|
||||
|
||||
static const struct fuse_lowlevel_ops fs_ops = {
|
||||
.lookup = fs_lookup,
|
||||
.getattr = fs_getattr,
|
||||
.opendir = fs_opendir,
|
||||
.readdir = fs_readdir,
|
||||
};
|
||||
|
||||
FIXTURE(readdir_cache)
|
||||
{
|
||||
struct fuse_common_ctx ctx;
|
||||
};
|
||||
|
||||
FIXTURE_VARIANT(readdir_cache)
|
||||
{
|
||||
bool cache_readdir;
|
||||
bool keep_cache;
|
||||
};
|
||||
FIXTURE_VARIANT_ADD(readdir_cache, nocache)
|
||||
{
|
||||
.cache_readdir = false,
|
||||
.keep_cache = false,
|
||||
};
|
||||
FIXTURE_VARIANT_ADD(readdir_cache, cache_readdir)
|
||||
{
|
||||
.cache_readdir = true,
|
||||
.keep_cache = false,
|
||||
};
|
||||
FIXTURE_VARIANT_ADD(readdir_cache, keep_cache)
|
||||
{
|
||||
.cache_readdir = false,
|
||||
.keep_cache = true,
|
||||
};
|
||||
FIXTURE_VARIANT_ADD(readdir_cache, cache)
|
||||
{
|
||||
.cache_readdir = true,
|
||||
.keep_cache = true,
|
||||
};
|
||||
|
||||
FIXTURE_SETUP(readdir_cache)
|
||||
{
|
||||
char err[MAX_ERR_MSG];
|
||||
|
||||
pthread_mutex_lock(&test_state.lock);
|
||||
test_state.mtime = time(NULL);
|
||||
test_state.readdir_counter = 0;
|
||||
test_state.cache_readdir = variant->cache_readdir;
|
||||
test_state.keep_cache = variant->keep_cache;
|
||||
pthread_mutex_unlock(&test_state.lock);
|
||||
|
||||
if (fs_setup(&fs_ops, &self->ctx, err))
|
||||
SKIP(return, err);
|
||||
}
|
||||
|
||||
FIXTURE_TEARDOWN(readdir_cache)
|
||||
{
|
||||
fs_teardown(&self->ctx);
|
||||
}
|
||||
|
||||
TEST_F(readdir_cache, test_readdir_cache)
|
||||
{
|
||||
struct dirent *dentry;
|
||||
DIR *dir;
|
||||
char pathname[PATH_MAX];
|
||||
int total_counter, rewind_counter;
|
||||
int dentrycount;
|
||||
|
||||
sprintf(pathname, "%s/%s", self->ctx.mountpoint, DIRNAME);
|
||||
|
||||
dir = opendir(pathname);
|
||||
if (dir == NULL)
|
||||
TH_LOG("opendir(): %s", strerror(errno));
|
||||
ASSERT_NE(dir, NULL);
|
||||
|
||||
errno = 0;
|
||||
dentrycount = 0;
|
||||
while ((dentry = readdir(dir)))
|
||||
dentrycount++;
|
||||
ASSERT_EQ(errno, 0);
|
||||
ASSERT_EQ(dentrycount, 3);
|
||||
|
||||
rewinddir(dir);
|
||||
errno = 0;
|
||||
dentrycount = 0;
|
||||
while ((dentry = readdir(dir)))
|
||||
dentrycount++;
|
||||
ASSERT_EQ(errno, 0);
|
||||
ASSERT_EQ(dentrycount, 3);
|
||||
|
||||
ASSERT_EQ(closedir(dir), 0);
|
||||
|
||||
pthread_mutex_lock(&test_state.lock);
|
||||
rewind_counter = test_state.readdir_counter;
|
||||
pthread_mutex_unlock(&test_state.lock);
|
||||
|
||||
dir = opendir(pathname);
|
||||
if (dir == NULL)
|
||||
TH_LOG("opendir(): %s", strerror(errno));
|
||||
ASSERT_NE(dir, NULL);
|
||||
|
||||
errno = 0;
|
||||
dentrycount = 0;
|
||||
while ((dentry = readdir(dir)))
|
||||
dentrycount++;
|
||||
ASSERT_EQ(errno, 0);
|
||||
ASSERT_EQ(dentrycount, 3);
|
||||
|
||||
ASSERT_EQ(closedir(dir), 0);
|
||||
|
||||
pthread_mutex_lock(&test_state.lock);
|
||||
total_counter = test_state.readdir_counter;
|
||||
pthread_mutex_unlock(&test_state.lock);
|
||||
|
||||
if (!variant->cache_readdir) {
|
||||
ASSERT_EQ(rewind_counter, 2);
|
||||
ASSERT_EQ(total_counter, 3);
|
||||
} else if (!variant->keep_cache) {
|
||||
ASSERT_EQ(rewind_counter, 1);
|
||||
ASSERT_EQ(total_counter, 2);
|
||||
} else {
|
||||
ASSERT_EQ(rewind_counter, 1);
|
||||
ASSERT_EQ(total_counter, 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
Loading…
Reference in New Issue
Block a user