Add -p switch to "zpool get"
[zfs.git] / module / zfs / zpl_ctldir.c
index 6c742c9..1bb646f 100644 (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)
-                       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.
  */
@@ -143,7 +112,11 @@ zpl_root_getattr(struct vfsmount *mnt, struct dentry *dentry,
 }
 
 static struct dentry *
+#ifdef HAVE_LOOKUP_NAMEIDATA
 zpl_root_lookup(struct inode *dip, struct dentry *dentry, struct nameidata *nd)
+#else
+zpl_root_lookup(struct inode *dip, struct dentry *dentry, unsigned int flags)
+#endif
 {
        cred_t *cr = CRED();
        struct inode *ip;
@@ -171,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 = {
@@ -179,12 +156,78 @@ const struct inode_operations zpl_ops_root = {
        .getattr        = zpl_root_getattr,
 };
 
+#ifdef HAVE_AUTOMOUNT
+static struct vfsmount *
+zpl_snapdir_automount(struct path *path)
+{
+       struct dentry *dentry = path->dentry;
+       int error;
+
+       /*
+        * We must briefly disable automounts for this dentry because the
+        * user space mount utility will trigger another lookup on this
+        * directory.  That will result in zpl_snapdir_automount() being
+        * called repeatedly.  The DCACHE_NEED_AUTOMOUNT flag can be
+        * safely reset once the mount completes.
+        */
+       dentry->d_flags &= ~DCACHE_NEED_AUTOMOUNT;
+       error = -zfsctl_mount_snapshot(path, 0);
+       dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
+       if (error)
+               return ERR_PTR(error);
+
+       /*
+        * Rather than returning the new vfsmount for the snapshot we must
+        * return NULL to indicate a mount collision.  This is done because
+        * the user space mount calls do_add_mount() which adds the vfsmount
+        * to the name space.  If we returned the new mount here it would be
+        * added again to the vfsmount list resulting in list corruption.
+        */
+       return (NULL);
+}
+#endif /* HAVE_AUTOMOUNT */
+
+/*
+ * Revalidate any dentry in the snapshot directory on lookup, since a snapshot
+ * having the same name have been created or destroyed since it was cached.
+ */
+static int
+#ifdef HAVE_D_REVALIDATE_NAMEIDATA
+zpl_snapdir_revalidate(struct dentry *dentry, struct nameidata *i)
+#else
+zpl_snapdir_revalidate(struct dentry *dentry, unsigned int flags)
+#endif
+{
+       return 0;
+}
+
+dentry_operations_t zpl_dops_snapdirs = {
+/*
+ * Auto mounting of snapshots is only supported for 2.6.37 and
+ * newer kernels.  Prior to this kernel the ops->follow_link()
+ * callback was used as a hack to trigger the mount.  The
+ * resulting vfsmount was then explicitly grafted in to the
+ * name space.  While it might be possible to add compatibility
+ * code to accomplish this it would require considerable care.
+ */
+#ifdef HAVE_AUTOMOUNT
+       .d_automount    = zpl_snapdir_automount,
+#endif /* HAVE_AUTOMOUNT */
+       .d_revalidate   = zpl_snapdir_revalidate,
+};
+
 static struct dentry *
+#ifdef HAVE_LOOKUP_NAMEIDATA
 zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
     struct nameidata *nd)
+#else
+zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
+    unsigned int flags)
+#endif
+
 {
        cred_t *cr = CRED();
-       struct inode *ip;
+       struct inode *ip = NULL;
        int error;
 
        crhold(cr);
@@ -193,72 +236,39 @@ zpl_snapdir_lookup(struct inode *dip, struct dentry *dentry,
        ASSERT3S(error, <=, 0);
        crfree(cr);
 
-       if (error) {
-               if (error == -ENOENT)
-                       return d_splice_alias(NULL, dentry);
-               else
-                       return ERR_PTR(error);
-       }
+       if (error && error != -ENOENT)
+               return ERR_PTR(error);
 
-       /*
-        * Auto mounting of snapshots is only supported for 2.6.37 and
-        * newer kernels.  Prior to this kernel the ops->follow_link()
-        * callback was used as a hack to trigger the mount.  The
-        * resulting vfsmount was then explicitly grafted in to the
-        * name space.  While it might be possible to add compatibility
-        * code to accomplish this it would require considerable care.
-        */
-#ifdef HAVE_AUTOMOUNT
-       dentry->d_op = &zpl_dops_snapdirs;
-#endif /* HAVE_AUTOMOUNT */
+       ASSERT(error == 0 || ip == NULL);
+       d_clear_d_op(dentry);
+       d_set_d_op(dentry, &zpl_dops_snapdirs);
 
        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);
@@ -269,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)
@@ -300,7 +324,7 @@ zpl_snapdir_rmdir(struct inode *dip, struct dentry *dentry)
 }
 
 static int
-zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, int mode)
+zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, zpl_umode_t mode)
 {
        cred_t *cr = CRED();
        vattr_t *vap;
@@ -309,13 +333,12 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, int mode)
 
        crhold(cr);
        vap = kmem_zalloc(sizeof(vattr_t), KM_SLEEP);
-       zpl_vap_init(vap, dip, dentry, mode | S_IFDIR, cr);
+       zpl_vap_init(vap, dip, mode | S_IFDIR, cr);
 
        error = -zfsctl_snapdir_mkdir(dip, dname(dentry), vap, &ip, cr, 0);
        if (error == 0) {
-#ifdef HAVE_AUTOMOUNT
-               dentry->d_op = &zpl_dops_snapdirs;
-#endif /* HAVE_AUTOMOUNT */
+               d_clear_d_op(dentry);
+               d_set_d_op(dentry, &zpl_dops_snapdirs);
                d_instantiate(dentry, ip);
        }
 
@@ -326,37 +349,6 @@ zpl_snapdir_mkdir(struct inode *dip, struct dentry *dentry, int mode)
        return (error);
 }
 
-#ifdef HAVE_AUTOMOUNT
-static struct vfsmount *
-zpl_snapdir_automount(struct path *path)
-{
-       struct dentry *dentry = path->dentry;
-       int error;
-
-       /*
-        * We must briefly disable automounts for this dentry because the
-        * user space mount utility will trigger another lookup on this
-        * directory.  That will result in zpl_snapdir_automount() being
-        * called repeatedly.  The DCACHE_NEED_AUTOMOUNT flag can be
-        * safely reset once the mount completes.
-        */
-       dentry->d_flags &= ~DCACHE_NEED_AUTOMOUNT;
-       error = -zfsctl_mount_snapshot(path, 0);
-       dentry->d_flags |= DCACHE_NEED_AUTOMOUNT;
-       if (error)
-               return ERR_PTR(error);
-
-       /*
-        * Rather than returning the new vfsmount for the snapshot we must
-        * return NULL to indicate a mount collision.  This is done because
-        * the user space mount calls do_add_mount() which adds the vfsmount
-        * to the name space.  If we returned the new mount here it would be
-        * added again to the vfsmount list resulting in list corruption.
-        */
-       return (NULL);
-}
-#endif /* HAVE_AUTOMOUNT */
-
 /*
  * Get snapshot directory attributes.
  */
@@ -387,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
+
 };
 
 /*
@@ -403,15 +400,14 @@ const struct inode_operations zpl_ops_snapdir = {
        .mkdir          = zpl_snapdir_mkdir,
 };
 
-#ifdef HAVE_AUTOMOUNT
-const struct dentry_operations zpl_dops_snapdirs = {
-       .d_automount    = zpl_snapdir_automount,
-};
-#endif /* HAVE_AUTOMOUNT */
-
 static struct dentry *
+#ifdef HAVE_LOOKUP_NAMEIDATA
 zpl_shares_lookup(struct inode *dip, struct dentry *dentry,
     struct nameidata *nd)
+#else
+zpl_shares_lookup(struct inode *dip, struct dentry *dentry,
+    unsigned int flags)
+#endif
 {
        cred_t *cr = CRED();
        struct inode *ip = NULL;
@@ -433,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,
@@ -507,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
+
 };
 
 /*