smb3: Fix root directory when server returns inode number of zero
authorSteve French <stfrench@microsoft.com>
Sat, 31 Mar 2018 23:13:38 +0000 (18:13 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 24 Apr 2018 07:36:26 +0000 (09:36 +0200)
commit 7ea884c77e5c97f1e0a1a422d961d27f78ca2745 upstream.

Some servers return inode number zero for the root directory, which
causes ls to display incorrect data (missing "." and "..").

If the server returns zero for the inode number of the root directory,
fake an inode number for it.

Signed-off-by: Steve French <smfrench@gmail.com>
Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
CC: Stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/cifs/cifsglob.h
fs/cifs/inode.c

index e185b2853eab7b1116dafc7ca8aeeb6d09b10687..6b6496820a21dee0ee1bcb6dd9c8155def7ef309 100644 (file)
@@ -1449,6 +1449,7 @@ struct dfs_info3_param {
 #define CIFS_FATTR_NEED_REVAL          0x4
 #define CIFS_FATTR_INO_COLLISION       0x8
 #define CIFS_FATTR_UNKNOWN_NLINK       0x10
+#define CIFS_FATTR_FAKE_ROOT_INO       0x20
 
 struct cifs_fattr {
        u32             cf_flags;
index 7c732cb4416411e597f2e1a4af96fd8bf7e49beb..0c7b7e2a0919a9a8c219543f5febe79c9dd6195b 100644 (file)
@@ -707,6 +707,18 @@ cgfi_exit:
        return rc;
 }
 
+/* Simple function to return a 64 bit hash of string.  Rarely called */
+static __u64 simple_hashstr(const char *str)
+{
+       const __u64 hash_mult =  1125899906842597L; /* a big enough prime */
+       __u64 hash = 0;
+
+       while (*str)
+               hash = (hash + (__u64) *str++) * hash_mult;
+
+       return hash;
+}
+
 int
 cifs_get_inode_info(struct inode **inode, const char *full_path,
                    FILE_ALL_INFO *data, struct super_block *sb, int xid,
@@ -816,6 +828,14 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
                                                 tmprc);
                                        fattr.cf_uniqueid = iunique(sb, ROOT_I);
                                        cifs_autodisable_serverino(cifs_sb);
+                               } else if ((fattr.cf_uniqueid == 0) &&
+                                               strlen(full_path) == 0) {
+                                       /* some servers ret bad root ino ie 0 */
+                                       cifs_dbg(FYI, "Invalid (0) inodenum\n");
+                                       fattr.cf_flags |=
+                                               CIFS_FATTR_FAKE_ROOT_INO;
+                                       fattr.cf_uniqueid =
+                                               simple_hashstr(tcon->treeName);
                                }
                        }
                } else
@@ -832,6 +852,16 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
                                &fattr.cf_uniqueid, data);
                        if (tmprc)
                                fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
+                       else if ((fattr.cf_uniqueid == 0) &&
+                                       strlen(full_path) == 0) {
+                               /*
+                                * Reuse existing root inode num since
+                                * inum zero for root causes ls of . and .. to
+                                * not be returned
+                                */
+                               cifs_dbg(FYI, "Srv ret 0 inode num for root\n");
+                               fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
+                       }
                } else
                        fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
        }
@@ -893,6 +923,9 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
        }
 
 cgii_exit:
+       if ((*inode) && ((*inode)->i_ino == 0))
+               cifs_dbg(FYI, "inode number of zero returned\n");
+
        kfree(buf);
        cifs_put_tlink(tlink);
        return rc;