Linux 3.11 compat: fops->iterate()

Commit torvalds/linux@2233f31aad
replaced ->readdir() with ->iterate() in struct file_operations.
All filesystems must now use the new ->iterate method.

To handle this the code was reworked to use the new ->iterate
interface.  Care was taken to keep the majority of changes
confined to the ZPL layer which is already Linux specific.
However, minor changes were required to the common zfs_readdir()
function.

Compatibility with older kernels was accomplished by adding
versions of the trivial dir_emit* helper functions.  Also the
various *_readdir() functions were reworked in to wrappers
which create a dir_context structure to pass to the new
*_iterate() functions.

Unfortunately, the new dir_emit* functions prevent us from
passing a private pointer to the filldir function.  The xattr
directory code leveraged this ability through zfs_readdir()
to generate the list of xattr names.  Since we can no longer
use zfs_readdir() a simplified zpl_xattr_readdir() function
was added to perform the same task.

Signed-off-by: Richard Yao <ryao@cs.stonybrook.edu>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #1653
Issue #1591
This commit is contained in:
Richard Yao
2013-08-07 08:53:45 -04:00
committed by Brian Behlendorf
parent 34e143323e
commit 0f37d0c8be
8 changed files with 262 additions and 126 deletions
+6 -6
View File
@@ -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) {
+91 -106
View File
@@ -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)
if (!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))
goto out;
filp->f_pos++;
/* fall-thru */
case 1:
error = filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR);
if (error)
ctx->pos++;
}
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 */
case 2:
error = filldir(dirent, ZFS_SNAPDIR_NAME,
strlen(ZFS_SNAPDIR_NAME), 2, ZFSCTL_INO_SNAPDIR, DT_DIR);
if (error)
goto out;
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)
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 (!dir_emit_dots(filp, ctx))
goto out;
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 */
case 1:
error = filldir(dirent, "..", 2, 1, parent_ino(dentry), DT_DIR);
if (error)
if (!dir_emit(ctx, snapname, strlen(snapname),
ZFSCTL_INO_SHARES - id, DT_DIR))
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;
}
}
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
};
/*
+20 -3
View File
@@ -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
+40 -9
View File
@@ -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);
}