jfs: fix readdir cookie incompatibility with NFSv4
authorDave Kleikamp <dave.kleikamp@oracle.com>
Thu, 15 Aug 2013 20:36:49 +0000 (15:36 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 8 Sep 2013 04:58:13 +0000 (21:58 -0700)
commit 44512449c0ab368889dd13ae0031fba74ee7e1d2 upstream.

NFSv4 reserves readdir cookie values 0-2 for special entries (. and ..),
but jfs allows a value of 2 for a non-special entry. This incompatibility
can result in the nfs client reporting a readdir loop.

This patch doesn't change the value stored internally, but adds one to
the value exposed to the iterate method.

Signed-off-by: Dave Kleikamp <dave.kleikamp@oracle.com>
[bwh: Backported to 3.2:
 - Adjust context
 - s/ctx->pos/filp->f_pos/]
Tested-by: Christian Kujau <lists@nerdbynature.de>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/jfs/jfs_dtree.c

index 9197a1b..b6f17c0 100644 (file)
@@ -3047,6 +3047,14 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 
                dir_index = (u32) filp->f_pos;
 
+               /*
+                * NFSv4 reserves cookies 1 and 2 for . and .. so we add
+                * the value we return to the vfs is one greater than the
+                * one we use internally.
+                */
+               if (dir_index)
+                       dir_index--;
+
                if (dir_index > 1) {
                        struct dir_table_slot dirtab_slot;
 
@@ -3086,7 +3094,7 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                        if (p->header.flag & BT_INTERNAL) {
                                jfs_err("jfs_readdir: bad index table");
                                DT_PUTPAGE(mp);
-                               filp->f_pos = -1;
+                               filp->f_pos = DIREND;
                                return 0;
                        }
                } else {
@@ -3094,7 +3102,7 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                                /*
                                 * self "."
                                 */
-                               filp->f_pos = 0;
+                               filp->f_pos = 1;
                                if (filldir(dirent, ".", 1, 0, ip->i_ino,
                                            DT_DIR))
                                        return 0;
@@ -3102,7 +3110,7 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                        /*
                         * parent ".."
                         */
-                       filp->f_pos = 1;
+                       filp->f_pos = 2;
                        if (filldir(dirent, "..", 2, 1, PARENT(ip), DT_DIR))
                                return 0;
 
@@ -3123,24 +3131,25 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                /*
                 * Legacy filesystem - OS/2 & Linux JFS < 0.3.6
                 *
-                * pn = index = 0:      First entry "."
-                * pn = 0; index = 1:   Second entry ".."
+                * pn = 0; index = 1:   First entry "."
+                * pn = 0; index = 2:   Second entry ".."
                 * pn > 0:              Real entries, pn=1 -> leftmost page
                 * pn = index = -1:     No more entries
                 */
                dtpos = filp->f_pos;
-               if (dtpos == 0) {
+               if (dtpos < 2) {
                        /* build "." entry */
 
+                       filp->f_pos = 1;
                        if (filldir(dirent, ".", 1, filp->f_pos, ip->i_ino,
                                    DT_DIR))
                                return 0;
-                       dtoffset->index = 1;
+                       dtoffset->index = 2;
                        filp->f_pos = dtpos;
                }
 
                if (dtoffset->pn == 0) {
-                       if (dtoffset->index == 1) {
+                       if (dtoffset->index == 2) {
                                /* build ".." entry */
 
                                if (filldir(dirent, "..", 2, filp->f_pos,
@@ -3233,6 +3242,12 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                                        }
                                        jfs_dirent->position = unique_pos++;
                                }
+                               /*
+                                * We add 1 to the index because we may
+                                * use a value of 2 internally, and NFSv4
+                                * doesn't like that.
+                                */
+                               jfs_dirent->position++;
                        } else {
                                jfs_dirent->position = dtpos;
                                len = min(d_namleft, DTLHDRDATALEN_LEGACY);