mirror of
https://github.com/versity/scoutfs.git
synced 2026-01-10 05:37:25 +00:00
scoutfs: support newer ->iterate readdir
The modern upstream kernel has a ->iterate() readdir file_operattions method which takes a context and calls dir_emit(). We add some kernelcompat helpers to juggle the various function definitions, types, and arguments to support both the old ->readdir(filldir) and the new ->iterate(ctx) interfaces. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -5,3 +5,32 @@
|
||||
#
|
||||
|
||||
ccflags-y += -include $(src)/kernelcompat.h
|
||||
|
||||
#
|
||||
# v3.10-rc6-21-gbb6f619b3a49
|
||||
#
|
||||
# _readdir changes from fop->readdir() to fop->iterate() and from
|
||||
# filldir(dirent) to dir_emit(ctx).
|
||||
#
|
||||
ifneq (,$(shell grep 'iterate.*dir_context' include/linux/fs.h))
|
||||
ccflags-y += -DKC_ITERATE_DIR_CONTEXT
|
||||
endif
|
||||
|
||||
#
|
||||
# v3.10-rc6-23-g5f99f4e79abc
|
||||
#
|
||||
# Helpers including dir_emit_dots() are added in the process of
|
||||
# switching dcache_readdir() from fop->readdir() to fop->iterate()
|
||||
#
|
||||
ifneq (,$(shell grep 'dir_emit_dots' include/linux/fs.h))
|
||||
ccflags-y += -DKC_DIR_EMIT_DOTS
|
||||
endif
|
||||
|
||||
#
|
||||
# RHEL extended the fop struct so to use it we have to set
|
||||
# a flag to indicate that the struct is large enough and
|
||||
# contains the pointer.
|
||||
#
|
||||
ifneq (,$(shell grep 'FMODE_KABI_ITERATE' include/linux/fs.h))
|
||||
ccflags-y += -DKC_FMODE_KABI_ITERATE
|
||||
endif
|
||||
|
||||
@@ -436,28 +436,6 @@ out:
|
||||
return d_splice_alias(inode, dentry);
|
||||
}
|
||||
|
||||
/* this exists upstream so we can just delete it in a forward port */
|
||||
static int dir_emit_dots(struct file *file, void *dirent, filldir_t filldir)
|
||||
{
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct inode *parent = dentry->d_parent->d_inode;
|
||||
|
||||
if (file->f_pos == 0) {
|
||||
if (filldir(dirent, ".", 1, 1, scoutfs_ino(inode), DT_DIR))
|
||||
return 0;
|
||||
file->f_pos = 1;
|
||||
}
|
||||
|
||||
if (file->f_pos == 1) {
|
||||
if (filldir(dirent, "..", 2, 1, scoutfs_ino(parent), DT_DIR))
|
||||
return 0;
|
||||
file->f_pos = 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* readdir simply iterates over the dirent items for the dir inode and
|
||||
* uses their offset as the readdir position.
|
||||
@@ -465,7 +443,8 @@ static int dir_emit_dots(struct file *file, void *dirent, filldir_t filldir)
|
||||
* It will need to be careful not to read past the region of the dirent
|
||||
* hash offset keys that it has access to.
|
||||
*/
|
||||
static int scoutfs_readdir(struct file *file, void *dirent, filldir_t filldir)
|
||||
static int KC_DECLARE_READDIR(scoutfs_readdir, struct file *file,
|
||||
void *dirent, kc_readdir_ctx_t ctx)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct super_block *sb = inode->i_sb;
|
||||
@@ -478,7 +457,7 @@ static int scoutfs_readdir(struct file *file, void *dirent, filldir_t filldir)
|
||||
u64 pos;
|
||||
int ret;
|
||||
|
||||
if (!dir_emit_dots(file, dirent, filldir))
|
||||
if (!kc_dir_emit_dots(file, dirent, ctx))
|
||||
return 0;
|
||||
|
||||
dent = alloc_dirent(SCOUTFS_NAME_LEN);
|
||||
@@ -497,7 +476,7 @@ static int scoutfs_readdir(struct file *file, void *dirent, filldir_t filldir)
|
||||
|
||||
for (;;) {
|
||||
init_dirent_key(&key, SCOUTFS_READDIR_TYPE, scoutfs_ino(inode),
|
||||
file->f_pos, 0);
|
||||
kc_readdir_pos(file, ctx), 0);
|
||||
|
||||
ret = scoutfs_item_next(sb, &key, &last_key, &val, dir_lock);
|
||||
if (ret < 0) {
|
||||
@@ -511,21 +490,24 @@ static int scoutfs_readdir(struct file *file, void *dirent, filldir_t filldir)
|
||||
scoutfs_corruption(sb, SC_DIRENT_READDIR_NAME_LEN,
|
||||
corrupt_dirent_readdir_name_len,
|
||||
"dir_ino %llu pos %llu key "SK_FMT" len %d",
|
||||
scoutfs_ino(inode), file->f_pos,
|
||||
scoutfs_ino(inode),
|
||||
kc_readdir_pos(file, ctx),
|
||||
SK_ARG(&key), name_len);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pos = le64_to_cpu(key.skd_major);
|
||||
kc_readdir_pos(file, ctx) = pos;
|
||||
|
||||
if (filldir(dirent, dent->name, name_len, pos,
|
||||
le64_to_cpu(dent->ino), dentry_type(dent->type))) {
|
||||
if (!kc_dir_emit(ctx, dent, dent->name, name_len, pos,
|
||||
le64_to_cpu(dent->ino),
|
||||
dentry_type(dent->type))) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
file->f_pos = pos + 1;
|
||||
kc_readdir_pos(file, ctx) = pos + 1;
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -1737,8 +1719,20 @@ out_unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef KC_FMODE_KABI_ITERATE
|
||||
/* we only need this to set the iterate flag for kabi :/ */
|
||||
static int scoutfs_dir_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->f_mode |= FMODE_KABI_ITERATE;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct file_operations scoutfs_dir_fops = {
|
||||
.readdir = scoutfs_readdir,
|
||||
.KC_FOP_READDIR = scoutfs_readdir,
|
||||
#ifdef KC_FMODE_KABI_ITERATE
|
||||
.open = scoutfs_dir_open,
|
||||
#endif
|
||||
.unlocked_ioctl = scoutfs_ioctl,
|
||||
.fsync = scoutfs_file_fsync,
|
||||
.llseek = generic_file_llseek,
|
||||
|
||||
@@ -1,4 +1,49 @@
|
||||
#ifndef _SCOUTFS_KERNELCOMPAT_H_
|
||||
#define _SCOUTFS_KERNELCOMPAT_H_
|
||||
|
||||
#ifndef KC_ITERATE_DIR_CONTEXT
|
||||
#include <linux/fs.h>
|
||||
typedef filldir_t kc_readdir_ctx_t;
|
||||
#define KC_DECLARE_READDIR(name, file, dirent, ctx) name(file, dirent, ctx)
|
||||
#define KC_FOP_READDIR readdir
|
||||
#define kc_readdir_pos(filp, ctx) (filp)->f_pos
|
||||
#define kc_dir_emit_dots(file, dirent, ctx) dir_emit_dots(file, dirent, ctx)
|
||||
#define kc_dir_emit(ctx, dentry, name, name_len, pos, ino, dt) \
|
||||
(ctx(dentry, name, name_len, pos, ino, dt) == 0)
|
||||
#else
|
||||
typedef struct dir_context * kc_readdir_ctx_t;
|
||||
#define KC_DECLARE_READDIR(name, file, dirent, ctx) name(file, ctx)
|
||||
#define KC_FOP_READDIR iterate
|
||||
#define kc_readdir_pos(filp, ctx) (ctx)->pos
|
||||
#define kc_dir_emit_dots(file, dirent, ctx) dir_emit_dots(file, ctx)
|
||||
#define kc_dir_emit(ctx, dentry, name, name_len, pos, ino, dt) \
|
||||
dir_emit(ctx, name, name_len, ino, dt)
|
||||
#endif
|
||||
|
||||
#ifndef KC_DIR_EMIT_DOTS
|
||||
/*
|
||||
* Kernels before ->iterate and don't have dir_emit_dots so we give them
|
||||
* one that works with the ->readdir() filldir() method.
|
||||
*/
|
||||
static inline int dir_emit_dots(struct file *file, void *dirent,
|
||||
filldir_t filldir)
|
||||
{
|
||||
if (file->f_pos == 0) {
|
||||
if (filldir(dirent, ".", 1, 1,
|
||||
file->f_path.dentry->d_inode->i_ino, DT_DIR))
|
||||
return 0;
|
||||
file->f_pos = 1;
|
||||
}
|
||||
|
||||
if (file->f_pos == 1) {
|
||||
if (filldir(dirent, "..", 2, 1,
|
||||
parent_ino(file->f_path.dentry), DT_DIR))
|
||||
return 0;
|
||||
file->f_pos = 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user