--- /dev/null
+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)
+ ])
+
+ ])
+])
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"
#include <sys/cred.h>
#include <sys/fcntl.h>
#include <sys/pathname.h>
+#include <sys/zpl.h>
#ifdef __cplusplus
extern "C" {
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);
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 */
*/
/* 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);
uint8_t prefetch;
int done = 0;
uint64_t parent;
+ loff_t *pos = &(ctx->pos);
ZFS_ENTER(zsb);
ZFS_VERIFY_ZP(zp);
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) {
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);
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.
*/
.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 = {
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);
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)
.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
+
};
/*
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,
.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
+
};
/*
}
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,
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
#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>
} 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);
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 */
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);
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);
}