summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/kernel-vfs-iterate.m443
-rw-r--r--config/kernel.m41
-rw-r--r--include/sys/zfs_vnops.h4
-rw-r--r--include/sys/zpl.h59
-rw-r--r--module/zfs/zfs_vnops.c12
-rw-r--r--module/zfs/zpl_ctldir.c193
-rw-r--r--module/zfs/zpl_file.c23
-rw-r--r--module/zfs/zpl_xattr.c49
8 files changed, 260 insertions, 124 deletions
diff --git a/config/kernel-vfs-iterate.m4 b/config/kernel-vfs-iterate.m4
new file mode 100644
index 000000000..c2c65622c
--- /dev/null
+++ b/config/kernel-vfs-iterate.m4
@@ -0,0 +1,43 @@
+dnl #
+dnl # 3.11 API change
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_VFS_ITERATE], [
+ AC_MSG_CHECKING([whether fops->iterate() is available])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/fs.h>
+ int iterate(struct file *filp, struct dir_context * context)
+ { return 0; }
+
+ static const struct file_operations fops
+ __attribute__ ((unused)) = {
+ .iterate = iterate,
+ };
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_VFS_ITERATE, 1,
+ [fops->iterate() is available])
+ ],[
+ AC_MSG_RESULT(no)
+
+ AC_MSG_CHECKING([whether fops->readdir() is available])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/fs.h>
+ int readdir(struct file *filp, void *entry, filldir_t func)
+ { return 0; }
+
+ static const struct file_operations fops
+ __attribute__ ((unused)) = {
+ .readdir = readdir,
+ };
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_VFS_READDIR, 1,
+ [fops->readdir() is available])
+ ],[
+ AC_MSG_ERROR(no; file a bug report with ZFSOnLinux)
+ ])
+
+ ])
+])
diff --git a/config/kernel.m4 b/config/kernel.m4
index 8742bc51f..74ce22ce6 100644
--- a/config/kernel.m4
+++ b/config/kernel.m4
@@ -80,6 +80,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
ZFS_AC_KERNEL_ELEVATOR_CHANGE
ZFS_AC_KERNEL_5ARG_SGET
ZFS_AC_KERNEL_LSEEK_EXECUTE
+ ZFS_AC_KERNEL_VFS_ITERATE
AS_IF([test "$LINUX_OBJ" != "$LINUX"], [
KERNELMAKE_PARAMS="$KERNELMAKE_PARAMS O=$LINUX_OBJ"
diff --git a/include/sys/zfs_vnops.h b/include/sys/zfs_vnops.h
index 75f7c128b..c9fecf8ba 100644
--- a/include/sys/zfs_vnops.h
+++ b/include/sys/zfs_vnops.h
@@ -31,6 +31,7 @@
#include <sys/cred.h>
#include <sys/fcntl.h>
#include <sys/pathname.h>
+#include <sys/zpl.h>
#ifdef __cplusplus
extern "C" {
@@ -51,8 +52,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, void *dirent, filldir_t filldir,
- loff_t *pos, cred_t *cr);
+extern int zfs_readdir(struct inode *ip, struct dir_context *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 61a57ef29..89cf8240c 100644
--- a/include/sys/zpl.h
+++ b/include/sys/zpl.h
@@ -89,4 +89,63 @@ extern const struct inode_operations zpl_ops_snapdirs;
extern const struct file_operations zpl_fops_shares;
extern const struct inode_operations zpl_ops_shares;
+#ifdef HAVE_VFS_ITERATE
+
+#define DIR_CONTEXT_INIT(_dirent, _actor, _pos) { \
+ .actor = _actor, \
+ .pos = _pos, \
+}
+
+#else
+
+typedef struct dir_context {
+ void *dirent;
+ const filldir_t actor;
+ loff_t pos;
+} dir_context_t;
+
+#define 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,
+ uint64_t ino, unsigned type)
+{
+ return ctx->actor(ctx->dirent, name, namelen, ctx->pos, ino, type) == 0;
+}
+
+static inline bool
+dir_emit_dot(struct file *file, struct dir_context *ctx)
+{
+ return ctx->actor(ctx->dirent, ".", 1, ctx->pos,
+ file->f_path.dentry->d_inode->i_ino, DT_DIR) == 0;
+}
+
+static inline bool
+dir_emit_dotdot(struct file *file, struct dir_context *ctx)
+{
+ return ctx->actor(ctx->dirent, "..", 2, ctx->pos,
+ parent_ino(file->f_path.dentry), DT_DIR) == 0;
+}
+
+static inline bool
+dir_emit_dots(struct file *file, struct dir_context *ctx)
+{
+ if (ctx->pos == 0) {
+ if (!dir_emit_dot(file, ctx))
+ return false;
+ ctx->pos = 1;
+ }
+ if (ctx->pos == 1) {
+ if (!dir_emit_dotdot(file, ctx))
+ return false;
+ ctx->pos = 2;
+ }
+ return true;
+}
+#endif /* HAVE_VFS_ITERATE */
+
#endif /* _SYS_ZPL_H */
diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
index db5d3856a..876d44b35 100644
--- a/module/zfs/zfs_vnops.c
+++ b/module/zfs/zfs_vnops.c
@@ -1997,8 +1997,7 @@ EXPORT_SYMBOL(zfs_rmdir);
*/
/* ARGSUSED */
int
-zfs_readdir(struct inode *ip, void *dirent, filldir_t filldir,
- loff_t *pos, cred_t *cr)
+zfs_readdir(struct inode *ip, struct dir_context *ctx, cred_t *cr)
{
znode_t *zp = ITOZ(ip);
zfs_sb_t *zsb = ITOZSB(ip);
@@ -2010,6 +2009,7 @@ zfs_readdir(struct inode *ip, void *dirent, filldir_t filldir,
uint8_t prefetch;
int done = 0;
uint64_t parent;
+ loff_t *pos = &(ctx->pos);
ZFS_ENTER(zsb);
ZFS_VERIFY_ZP(zp);
@@ -2098,11 +2098,11 @@ zfs_readdir(struct inode *ip, void *dirent, filldir_t filldir,
objnum = ZFS_DIRENT_OBJ(zap.za_first_integer);
}
- done = filldir(dirent, zap.za_name, strlen(zap.za_name),
- *pos, objnum, ZFS_DIRENT_TYPE(zap.za_first_integer));
- if (done) {
+
+ done = !dir_emit(ctx, zap.za_name, strlen(zap.za_name),
+ objnum, ZFS_DIRENT_TYPE(zap.za_first_integer));
+ if (done)
break;
- }
/* Prefetch znode */
if (prefetch) {
diff --git a/module/zfs/zpl_ctldir.c b/module/zfs/zpl_ctldir.c
index 089701707..1bb646fdf 100644
--- a/module/zfs/zpl_ctldir.c
+++ b/module/zfs/zpl_ctldir.c
@@ -46,79 +46,34 @@ zpl_common_open(struct inode *ip, struct file *filp)
return generic_file_open(ip, filp);
}
-static int
-zpl_common_readdir(struct file *filp, void *dirent, filldir_t filldir)
-{
- struct dentry *dentry = filp->f_path.dentry;
- struct inode *ip = dentry->d_inode;
- int error = 0;
-
- switch (filp->f_pos) {
- case 0:
- error = filldir(dirent, ".", 1, 0, ip->i_ino, DT_DIR);
- if (error)
- break;
-
- filp->f_pos++;
- /* fall-thru */
- case 1:
- error = filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR);
- if (error)
- break;
-
- filp->f_pos++;
- /* fall-thru */
- default:
- break;
- }
-
- return (error);
-}
-
/*
* Get root directory contents.
*/
static int
-zpl_root_readdir(struct file *filp, void *dirent, filldir_t filldir)
+zpl_root_iterate(struct file *filp, struct dir_context *ctx)
{
- struct dentry *dentry = filp->f_path.dentry;
- struct inode *ip = dentry->d_inode;
- zfs_sb_t *zsb = ITOZSB(ip);
+ zfs_sb_t *zsb = ITOZSB(filp->f_path.dentry->d_inode);
int error = 0;
ZFS_ENTER(zsb);
- switch (filp->f_pos) {
- case 0:
- error = filldir(dirent, ".", 1, 0, ip->i_ino, DT_DIR);
- if (error)
- goto out;
+ if (!dir_emit_dots(filp, ctx))
+ goto out;
- filp->f_pos++;
- /* fall-thru */
- case 1:
- error = filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR);
- if (error)
+ if (ctx->pos == 2) {
+ if (!dir_emit(ctx, ZFS_SNAPDIR_NAME, strlen(ZFS_SNAPDIR_NAME),
+ ZFSCTL_INO_SNAPDIR, DT_DIR))
goto out;
- filp->f_pos++;
- /* fall-thru */
- case 2:
- error = filldir(dirent, ZFS_SNAPDIR_NAME,
- strlen(ZFS_SNAPDIR_NAME), 2, ZFSCTL_INO_SNAPDIR, DT_DIR);
- if (error)
- goto out;
+ ctx->pos++;
+ }
- filp->f_pos++;
- /* fall-thru */
- case 3:
- error = filldir(dirent, ZFS_SHAREDIR_NAME,
- strlen(ZFS_SHAREDIR_NAME), 3, ZFSCTL_INO_SHARES, DT_DIR);
- if (error)
+ if (ctx->pos == 3) {
+ if (!dir_emit(ctx, ZFS_SHAREDIR_NAME, strlen(ZFS_SHAREDIR_NAME),
+ ZFSCTL_INO_SHARES, DT_DIR))
goto out;
- filp->f_pos++;
- /* fall-thru */
+ ctx->pos++;
}
out:
ZFS_EXIT(zsb);
@@ -126,6 +81,20 @@ out:
return (error);
}
+#if !defined(HAVE_VFS_ITERATE)
+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);
+ int error;
+
+ error = zpl_root_iterate(filp, &ctx);
+ filp->f_pos = ctx.pos;
+
+ return (error);
+}
+#endif /* HAVE_VFS_ITERATE */
+
/*
* Get root directory attributes.
*/
@@ -175,7 +144,11 @@ const struct file_operations zpl_fops_root = {
.open = zpl_common_open,
.llseek = generic_file_llseek,
.read = generic_read_dir,
+#ifdef HAVE_VFS_ITERATE
+ .iterate = zpl_root_iterate,
+#else
.readdir = zpl_root_readdir,
+#endif
};
const struct inode_operations zpl_ops_root = {
@@ -273,50 +246,29 @@ zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
return d_splice_alias(ip, dentry);
}
-/* ARGSUSED */
static int
-zpl_snapdir_readdir(struct file *filp, void *dirent, filldir_t filldir)
+zpl_snapdir_iterate(struct file *filp, struct dir_context *ctx)
{
- struct dentry *dentry = filp->f_path.dentry;
- struct inode *dip = dentry->d_inode;
- zfs_sb_t *zsb = ITOZSB(dip);
+ zfs_sb_t *zsb = ITOZSB(filp->f_path.dentry->d_inode);
char snapname[MAXNAMELEN];
- uint64_t id, cookie;
boolean_t case_conflict;
+ uint64_t id;
int error = 0;
ZFS_ENTER(zsb);
- cookie = filp->f_pos;
- switch (filp->f_pos) {
- case 0:
- error = filldir(dirent, ".", 1, 0, dip->i_ino, DT_DIR);
- if (error)
- goto out;
+ if (!dir_emit_dots(filp, ctx))
+ goto out;
- filp->f_pos++;
- /* fall-thru */
- case 1:
- error = filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR);
+ while (error == 0) {
+ error = -dmu_snapshot_list_next(zsb->z_os, MAXNAMELEN,
+ snapname, &id, &(ctx->pos), &case_conflict);
if (error)
goto out;
- filp->f_pos++;
- /* fall-thru */
- default:
- while (error == 0) {
- error = -dmu_snapshot_list_next(zsb->z_os, MAXNAMELEN,
- snapname, &id, &cookie, &case_conflict);
- if (error)
- goto out;
-
- error = filldir(dirent, snapname, strlen(snapname),
- filp->f_pos, ZFSCTL_INO_SHARES - id, DT_DIR);
- if (error)
- goto out;
-
- filp->f_pos = cookie;
- }
+ if (!dir_emit(ctx, snapname, strlen(snapname),
+ ZFSCTL_INO_SHARES - id, DT_DIR))
+ goto out;
}
out:
ZFS_EXIT(zsb);
@@ -327,6 +279,20 @@ out:
return (error);
}
+#if !defined(HAVE_VFS_ITERATE)
+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);
+ int error;
+
+ error = zpl_snapdir_iterate(filp, &ctx);
+ filp->f_pos = ctx.pos;
+
+ return (error);
+}
+#endif /* HAVE_VFS_ITERATE */
+
int
zpl_snapdir_rename(struct inode *sdip, struct dentry *sdentry,
struct inode *tdip, struct dentry *tdentry)
@@ -413,7 +379,12 @@ const struct file_operations zpl_fops_snapdir = {
.open = zpl_common_open,
.llseek = generic_file_llseek,
.read = generic_read_dir,
+#ifdef HAVE_VFS_ITERATE
+ .iterate = zpl_snapdir_iterate,
+#else
.readdir = zpl_snapdir_readdir,
+#endif
+
};
/*
@@ -458,42 +429,51 @@ zpl_shares_lookup(struct inode *dip, struct dentry *dentry,
return d_splice_alias(ip, dentry);
}
-/* ARGSUSED */
static int
-zpl_shares_readdir(struct file *filp, void *dirent, filldir_t filldir)
+zpl_shares_iterate(struct file *filp, struct dir_context *ctx)
{
cred_t *cr = CRED();
- struct dentry *dentry = filp->f_path.dentry;
- struct inode *ip = dentry->d_inode;
- zfs_sb_t *zsb = ITOZSB(ip);
+ zfs_sb_t *zsb = ITOZSB(filp->f_path.dentry->d_inode);
znode_t *dzp;
- int error;
+ int error = 0;
ZFS_ENTER(zsb);
if (zsb->z_shares_dir == 0) {
- error = zpl_common_readdir(filp, dirent, filldir);
- ZFS_EXIT(zsb);
- return (error);
+ dir_emit_dots(filp, ctx);
+ goto out;
}
error = -zfs_zget(zsb, zsb->z_shares_dir, &dzp);
- if (error) {
- ZFS_EXIT(zsb);
- return (error);
- }
+ if (error)
+ goto out;
crhold(cr);
- error = -zfs_readdir(ZTOI(dzp), dirent, filldir, &filp->f_pos, cr);
+ error = -zfs_readdir(ZTOI(dzp), ctx, cr);
crfree(cr);
iput(ZTOI(dzp));
+out:
ZFS_EXIT(zsb);
ASSERT3S(error, <=, 0);
return (error);
}
+#if !defined(HAVE_VFS_ITERATE)
+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);
+ int error;
+
+ error = zpl_shares_iterate(filp, &ctx);
+ filp->f_pos = ctx.pos;
+
+ return (error);
+}
+#endif /* HAVE_VFS_ITERATE */
+
/* ARGSUSED */
static int
zpl_shares_getattr(struct vfsmount *mnt, struct dentry *dentry,
@@ -532,7 +512,12 @@ const struct file_operations zpl_fops_shares = {
.open = zpl_common_open,
.llseek = generic_file_llseek,
.read = generic_read_dir,
+#ifdef HAVE_VFS_ITERATE
+ .iterate = zpl_shares_iterate,
+#else
.readdir = zpl_shares_readdir,
+#endif
+
};
/*
diff --git a/module/zfs/zpl_file.c b/module/zfs/zpl_file.c
index 299589193..6598c1779 100644
--- a/module/zfs/zpl_file.c
+++ b/module/zfs/zpl_file.c
@@ -64,21 +64,34 @@ zpl_release(struct inode *ip, struct file *filp)
}
static int
-zpl_readdir(struct file *filp, void *dirent, filldir_t filldir)
+zpl_iterate(struct file *filp, struct dir_context *ctx)
{
struct dentry *dentry = filp->f_path.dentry;
cred_t *cr = CRED();
int error;
crhold(cr);
- error = -zfs_readdir(dentry->d_inode, dirent, filldir,
- &filp->f_pos, cr);
+ error = -zfs_readdir(dentry->d_inode, ctx, cr);
crfree(cr);
ASSERT3S(error, <=, 0);
return (error);
}
+#if !defined(HAVE_VFS_ITERATE)
+static int
+zpl_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+ struct dir_context ctx = DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos);
+ int error;
+
+ error = zpl_iterate(filp, &ctx);
+ filp->f_pos = ctx.pos;
+
+ return (error);
+}
+#endif /* HAVE_VFS_ITERATE */
+
#if defined(HAVE_FSYNC_WITH_DENTRY)
/*
* Linux 2.6.x - 2.6.34 API,
@@ -506,7 +519,11 @@ 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
+ .iterate = zpl_iterate,
+#else
.readdir = zpl_readdir,
+#endif
.fsync = zpl_fsync,
.unlocked_ioctl = zpl_ioctl,
#ifdef CONFIG_COMPAT
diff --git a/module/zfs/zpl_xattr.c b/module/zfs/zpl_xattr.c
index eb2c00dfb..dca1ad668 100644
--- a/module/zfs/zpl_xattr.c
+++ b/module/zfs/zpl_xattr.c
@@ -80,6 +80,7 @@
#include <sys/zfs_vfsops.h>
#include <sys/zfs_vnops.h>
#include <sys/zfs_znode.h>
+#include <sys/zap.h>
#include <sys/vfs.h>
#include <sys/zpl.h>
@@ -91,11 +92,8 @@ typedef struct xattr_filldir {
} xattr_filldir_t;
static int
-zpl_xattr_filldir(void *arg, const char *name, int name_len,
- loff_t offset, uint64_t objnum, unsigned int d_type)
+zpl_xattr_filldir(xattr_filldir_t *xf, const char *name, int name_len)
{
- xattr_filldir_t *xf = arg;
-
if (!strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
if (!(ITOZSB(xf->inode)->z_flags & ZSB_XATTR))
return (0);
@@ -118,12 +116,46 @@ zpl_xattr_filldir(void *arg, const char *name, int name_len,
return (0);
}
+/*
+ * Read as many directory entry names as will fit in to the provided buffer,
+ * or when no buffer is provided calculate the required buffer size.
+ */
+int
+zpl_xattr_readdir(struct inode *dxip, xattr_filldir_t *xf)
+{
+ zap_cursor_t zc;
+ zap_attribute_t zap;
+ int error;
+
+ zap_cursor_init(&zc, ITOZSB(dxip)->z_os, ITOZ(dxip)->z_id);
+
+ while ((error = -zap_cursor_retrieve(&zc, &zap)) == 0) {
+
+ if (zap.za_integer_length != 8 || zap.za_num_integers != 1) {
+ error = -ENXIO;
+ break;
+ }
+
+ error = zpl_xattr_filldir(xf, zap.za_name, strlen(zap.za_name));
+ if (error)
+ break;
+
+ zap_cursor_advance(&zc);
+ }
+
+ zap_cursor_fini(&zc);
+
+ if (error == -ENOENT)
+ error = 0;
+
+ return (error);
+}
+
static ssize_t
zpl_xattr_list_dir(xattr_filldir_t *xf, cred_t *cr)
{
struct inode *ip = xf->inode;
struct inode *dxip = NULL;
- loff_t pos = 3; /* skip '.', '..', and '.zfs' entries. */
int error;
/* Lookup the xattr directory */
@@ -135,8 +167,7 @@ zpl_xattr_list_dir(xattr_filldir_t *xf, cred_t *cr)
return (error);
}
- /* Fill provided buffer via zpl_zattr_filldir helper */
- error = -zfs_readdir(dxip, (void *)xf, zpl_xattr_filldir, &pos, cr);
+ error = zpl_xattr_readdir(dxip, xf);
iput(dxip);
return (error);
@@ -162,8 +193,8 @@ zpl_xattr_list_sa(xattr_filldir_t *xf)
while ((nvp = nvlist_next_nvpair(zp->z_xattr_cached, nvp)) != NULL) {
ASSERT3U(nvpair_type(nvp), ==, DATA_TYPE_BYTE_ARRAY);
- error = zpl_xattr_filldir((void *)xf, nvpair_name(nvp),
- strlen(nvpair_name(nvp)), 0, 0, 0);
+ error = zpl_xattr_filldir(xf, nvpair_name(nvp),
+ strlen(nvpair_name(nvp)));
if (error)
return (error);
}