From d177418748dc81279b080edd50cc6b4d3ed8f450 Mon Sep 17 00:00:00 2001 From: Brian Behlendorf Date: Tue, 1 May 2018 16:50:41 -0400 Subject: [PATCH] RHEL 7.5 compat: FMODE_KABI_ITERATE As of RHEL 7.5 the mainline fops.iterate() method was added to the file_operations structure and is correctly detected by the configure script. Normally this is what we want, but in order to maintain KABI compatibility the RHEL change additionally does the following: * Requires that callers intending to use this extended interface set the FMODE_KABI_ITERATE flag on the file structure when opening the directory. * Adds the fops.iterate() method to the end of the structure, without removing fops.readdir(). This change updates the configure check to ignore the RHEL 7.5+ variant of fops.iterate() when detected. Instead fallback to the fops.readdir() interface which will be available. Finally, add the 'zpl_' prefix to the directory context wrappers to avoid colliding with the kernel provided symbols when both the fops.iterate() and fops.readdir() are provided by the kernel. Signed-off-by: Brian Behlendorf Issue #7460 --- config/kernel-vfs-iterate.m4 | 21 +++++++++++++++----- include/sys/zfs_vnops.h | 2 +- include/sys/zpl.h | 27 ++++++++++++++++---------- module/zfs/zfs_vnops.c | 4 ++-- module/zfs/zpl_ctldir.c | 37 +++++++++++++++++++----------------- module/zfs/zpl_file.c | 9 +++++---- 6 files changed, 61 insertions(+), 39 deletions(-) diff --git a/config/kernel-vfs-iterate.m4 b/config/kernel-vfs-iterate.m4 index 7b1599e13269..5de901d4462e 100644 --- a/config/kernel-vfs-iterate.m4 +++ b/config/kernel-vfs-iterate.m4 @@ -23,16 +23,27 @@ AC_DEFUN([ZFS_AC_KERNEL_VFS_ITERATE], [ dnl # dnl # 3.11 API change dnl # + dnl # RHEL 7.5 compatibility; the fops.iterate() method was + dnl # added to the file_operations structure but in order to + dnl # maintain KABI compatibility all callers must set + dnl # FMODE_KABI_ITERATE which is checked in iterate_dir(). + dnl # When detected ignore this interface and fallback to + dnl # to using fops.readdir() to retain KABI compatibility. + dnl # AC_MSG_CHECKING([whether fops->iterate() is available]) ZFS_LINUX_TRY_COMPILE([ #include - int iterate(struct file *filp, struct dir_context * context) - { return 0; } + int iterate(struct file *filp, + struct dir_context *context) { return 0; } static const struct file_operations fops __attribute__ ((unused)) = { .iterate = iterate, }; + + #if defined(FMODE_KABI_ITERATE) + #error "RHEL 7.5, FMODE_KABI_ITERATE interface" + #endif ],[ ],[ AC_MSG_RESULT(yes) @@ -44,8 +55,8 @@ AC_DEFUN([ZFS_AC_KERNEL_VFS_ITERATE], [ AC_MSG_CHECKING([whether fops->readdir() is available]) ZFS_LINUX_TRY_COMPILE([ #include - int readdir(struct file *filp, void *entry, filldir_t func) - { return 0; } + int readdir(struct file *filp, void *entry, + filldir_t func) { return 0; } static const struct file_operations fops __attribute__ ((unused)) = { @@ -57,7 +68,7 @@ AC_DEFUN([ZFS_AC_KERNEL_VFS_ITERATE], [ AC_DEFINE(HAVE_VFS_READDIR, 1, [fops->readdir() is available]) ],[ - AC_MSG_ERROR(no; file a bug report with ZFSOnLinux) + AC_MSG_ERROR(no; file a bug report with ZoL) ]) ]) ]) diff --git a/include/sys/zfs_vnops.h b/include/sys/zfs_vnops.h index f2f4d13f4ed2..767cba10da2b 100644 --- a/include/sys/zfs_vnops.h +++ b/include/sys/zfs_vnops.h @@ -54,7 +54,7 @@ extern int zfs_mkdir(struct inode *dip, char *dirname, vattr_t *vap, struct inode **ipp, cred_t *cr, int flags, vsecattr_t *vsecp); extern int zfs_rmdir(struct inode *dip, char *name, struct inode *cwd, cred_t *cr, int flags); -extern int zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr); +extern int zfs_readdir(struct inode *ip, zpl_dir_context_t *ctx, cred_t *cr); extern int zfs_fsync(struct inode *ip, int syncflag, cred_t *cr); extern int zfs_getattr(struct inode *ip, vattr_t *vap, int flag, cred_t *cr); extern int zfs_getattr_fast(struct inode *ip, struct kstat *sp); diff --git a/include/sys/zpl.h b/include/sys/zpl.h index 1f97f2511a37..65ed43136035 100644 --- a/include/sys/zpl.h +++ b/include/sys/zpl.h @@ -125,56 +125,63 @@ extern const struct inode_operations zpl_ops_shares; #if defined(HAVE_VFS_ITERATE) || defined(HAVE_VFS_ITERATE_SHARED) -#define DIR_CONTEXT_INIT(_dirent, _actor, _pos) { \ +#define ZPL_DIR_CONTEXT_INIT(_dirent, _actor, _pos) { \ .actor = _actor, \ .pos = _pos, \ } +typedef struct dir_context zpl_dir_context_t; + +#define zpl_dir_emit dir_emit +#define zpl_dir_emit_dot dir_emit_dot +#define zpl_dir_emit_dotdot dir_emit_dotdot +#define zpl_dir_emit_dots dir_emit_dots + #else -typedef struct dir_context { +typedef struct zpl_dir_context { void *dirent; const filldir_t actor; loff_t pos; -} dir_context_t; +} zpl_dir_context_t; -#define DIR_CONTEXT_INIT(_dirent, _actor, _pos) { \ +#define ZPL_DIR_CONTEXT_INIT(_dirent, _actor, _pos) { \ .dirent = _dirent, \ .actor = _actor, \ .pos = _pos, \ } static inline bool -dir_emit(struct dir_context *ctx, const char *name, int namelen, +zpl_dir_emit(zpl_dir_context_t *ctx, const char *name, int namelen, uint64_t ino, unsigned type) { return (!ctx->actor(ctx->dirent, name, namelen, ctx->pos, ino, type)); } static inline bool -dir_emit_dot(struct file *file, struct dir_context *ctx) +zpl_dir_emit_dot(struct file *file, zpl_dir_context_t *ctx) { return (ctx->actor(ctx->dirent, ".", 1, ctx->pos, file_inode(file)->i_ino, DT_DIR) == 0); } static inline bool -dir_emit_dotdot(struct file *file, struct dir_context *ctx) +zpl_dir_emit_dotdot(struct file *file, zpl_dir_context_t *ctx) { return (ctx->actor(ctx->dirent, "..", 2, ctx->pos, parent_ino(file_dentry(file)), DT_DIR) == 0); } static inline bool -dir_emit_dots(struct file *file, struct dir_context *ctx) +zpl_dir_emit_dots(struct file *file, zpl_dir_context_t *ctx) { if (ctx->pos == 0) { - if (!dir_emit_dot(file, ctx)) + if (!zpl_dir_emit_dot(file, ctx)) return (false); ctx->pos = 1; } if (ctx->pos == 1) { - if (!dir_emit_dotdot(file, ctx)) + if (!zpl_dir_emit_dotdot(file, ctx)) return (false); ctx->pos = 2; } diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c index a2d7d7b24da6..686d4a47ea49 100644 --- a/module/zfs/zfs_vnops.c +++ b/module/zfs/zfs_vnops.c @@ -2261,7 +2261,7 @@ zfs_rmdir(struct inode *dip, char *name, struct inode *cwd, cred_t *cr, */ /* ARGSUSED */ int -zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr) +zfs_readdir(struct inode *ip, zpl_dir_context_t *ctx, cred_t *cr) { znode_t *zp = ITOZ(ip); zfsvfs_t *zfsvfs = ITOZSB(ip); @@ -2366,7 +2366,7 @@ zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr) type = ZFS_DIRENT_TYPE(zap.za_first_integer); } - done = !dir_emit(ctx, zap.za_name, strlen(zap.za_name), + done = !zpl_dir_emit(ctx, zap.za_name, strlen(zap.za_name), objnum, type); if (done) break; diff --git a/module/zfs/zpl_ctldir.c b/module/zfs/zpl_ctldir.c index 1c5fb34e656a..6df367b8176a 100644 --- a/module/zfs/zpl_ctldir.c +++ b/module/zfs/zpl_ctldir.c @@ -50,27 +50,27 @@ zpl_common_open(struct inode *ip, struct file *filp) * Get root directory contents. */ static int -zpl_root_iterate(struct file *filp, struct dir_context *ctx) +zpl_root_iterate(struct file *filp, zpl_dir_context_t *ctx) { zfsvfs_t *zfsvfs = ITOZSB(file_inode(filp)); int error = 0; ZFS_ENTER(zfsvfs); - if (!dir_emit_dots(filp, ctx)) + if (!zpl_dir_emit_dots(filp, ctx)) goto out; if (ctx->pos == 2) { - if (!dir_emit(ctx, ZFS_SNAPDIR_NAME, strlen(ZFS_SNAPDIR_NAME), - ZFSCTL_INO_SNAPDIR, DT_DIR)) + if (!zpl_dir_emit(ctx, ZFS_SNAPDIR_NAME, + strlen(ZFS_SNAPDIR_NAME), ZFSCTL_INO_SNAPDIR, DT_DIR)) goto out; ctx->pos++; } if (ctx->pos == 3) { - if (!dir_emit(ctx, ZFS_SHAREDIR_NAME, strlen(ZFS_SHAREDIR_NAME), - ZFSCTL_INO_SHARES, DT_DIR)) + if (!zpl_dir_emit(ctx, ZFS_SHAREDIR_NAME, + strlen(ZFS_SHAREDIR_NAME), ZFSCTL_INO_SHARES, DT_DIR)) goto out; ctx->pos++; @@ -85,7 +85,8 @@ zpl_root_iterate(struct file *filp, struct dir_context *ctx) static int zpl_root_readdir(struct file *filp, void *dirent, filldir_t filldir) { - struct dir_context ctx = DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos); + zpl_dir_context_t ctx = + ZPL_DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos); int error; error = zpl_root_iterate(filp, &ctx); @@ -93,7 +94,7 @@ zpl_root_readdir(struct file *filp, void *dirent, filldir_t filldir) return (error); } -#endif /* HAVE_VFS_ITERATE */ +#endif /* !HAVE_VFS_ITERATE && !HAVE_VFS_ITERATE_SHARED */ /* * Get root directory attributes. @@ -248,7 +249,7 @@ zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry, } static int -zpl_snapdir_iterate(struct file *filp, struct dir_context *ctx) +zpl_snapdir_iterate(struct file *filp, zpl_dir_context_t *ctx) { zfsvfs_t *zfsvfs = ITOZSB(file_inode(filp)); fstrans_cookie_t cookie; @@ -260,7 +261,7 @@ zpl_snapdir_iterate(struct file *filp, struct dir_context *ctx) ZFS_ENTER(zfsvfs); cookie = spl_fstrans_mark(); - if (!dir_emit_dots(filp, ctx)) + if (!zpl_dir_emit_dots(filp, ctx)) goto out; pos = ctx->pos; @@ -272,7 +273,7 @@ zpl_snapdir_iterate(struct file *filp, struct dir_context *ctx) if (error) goto out; - if (!dir_emit(ctx, snapname, strlen(snapname), + if (!zpl_dir_emit(ctx, snapname, strlen(snapname), ZFSCTL_INO_SHARES - id, DT_DIR)) goto out; @@ -292,7 +293,8 @@ zpl_snapdir_iterate(struct file *filp, struct dir_context *ctx) static int zpl_snapdir_readdir(struct file *filp, void *dirent, filldir_t filldir) { - struct dir_context ctx = DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos); + zpl_dir_context_t ctx = + ZPL_DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos); int error; error = zpl_snapdir_iterate(filp, &ctx); @@ -300,7 +302,7 @@ zpl_snapdir_readdir(struct file *filp, void *dirent, filldir_t filldir) return (error); } -#endif /* HAVE_VFS_ITERATE */ +#endif /* !HAVE_VFS_ITERATE && !HAVE_VFS_ITERATE_SHARED */ static int zpl_snapdir_rename2(struct inode *sdip, struct dentry *sdentry, @@ -463,7 +465,7 @@ zpl_shares_lookup(struct inode *dip, struct dentry *dentry, } static int -zpl_shares_iterate(struct file *filp, struct dir_context *ctx) +zpl_shares_iterate(struct file *filp, zpl_dir_context_t *ctx) { fstrans_cookie_t cookie; cred_t *cr = CRED(); @@ -475,7 +477,7 @@ zpl_shares_iterate(struct file *filp, struct dir_context *ctx) cookie = spl_fstrans_mark(); if (zfsvfs->z_shares_dir == 0) { - dir_emit_dots(filp, ctx); + zpl_dir_emit_dots(filp, ctx); goto out; } @@ -500,7 +502,8 @@ zpl_shares_iterate(struct file *filp, struct dir_context *ctx) static int zpl_shares_readdir(struct file *filp, void *dirent, filldir_t filldir) { - struct dir_context ctx = DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos); + zpl_dir_context_t ctx = + ZPL_DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos); int error; error = zpl_shares_iterate(filp, &ctx); @@ -508,7 +511,7 @@ zpl_shares_readdir(struct file *filp, void *dirent, filldir_t filldir) return (error); } -#endif /* HAVE_VFS_ITERATE */ +#endif /* !HAVE_VFS_ITERATE && !HAVE_VFS_ITERATE_SHARED */ /* ARGSUSED */ static int diff --git a/module/zfs/zpl_file.c b/module/zfs/zpl_file.c index 1c5f5e409637..e03a0481cff0 100644 --- a/module/zfs/zpl_file.c +++ b/module/zfs/zpl_file.c @@ -76,7 +76,7 @@ zpl_release(struct inode *ip, struct file *filp) } static int -zpl_iterate(struct file *filp, struct dir_context *ctx) +zpl_iterate(struct file *filp, zpl_dir_context_t *ctx) { cred_t *cr = CRED(); int error; @@ -96,7 +96,8 @@ zpl_iterate(struct file *filp, struct dir_context *ctx) static int zpl_readdir(struct file *filp, void *dirent, filldir_t filldir) { - struct dir_context ctx = DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos); + zpl_dir_context_t ctx = + ZPL_DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos); int error; error = zpl_iterate(filp, &ctx); @@ -104,7 +105,7 @@ zpl_readdir(struct file *filp, void *dirent, filldir_t filldir) return (error); } -#endif /* HAVE_VFS_ITERATE */ +#endif /* !HAVE_VFS_ITERATE && !HAVE_VFS_ITERATE_SHARED */ #if defined(HAVE_FSYNC_WITH_DENTRY) /* @@ -965,7 +966,7 @@ const struct file_operations zpl_file_operations = { const struct file_operations zpl_dir_file_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, -#ifdef HAVE_VFS_ITERATE_SHARED +#if defined(HAVE_VFS_ITERATE_SHARED) .iterate_shared = zpl_iterate, #elif defined(HAVE_VFS_ITERATE) .iterate = zpl_iterate,