[SMB3] Fake an inode number when server returns inode number zero for the root

Message ID CAH2r5mtres9bDqbjXWyNiTDB23XDyG_dtHeixpZgR6UhnJ-DDA@mail.gmail.com
State New
Headers show
Series
  • [SMB3] Fake an inode number when server returns inode number zero for the root
Related show

Commit Message

Steve French March 31, 2018, 11:17 p.m.
Some servers return inode number zero for the root (ok for all other
inodes), if so
we need to create a non-zero hash instead for it and use that.

Comments

Steve French April 1, 2018, 9:49 p.m. | #1
Slightly updated version (shorter by a couple lines)

On Sat, Mar 31, 2018 at 6:17 PM, Steve French <smfrench@gmail.com> wrote:
> Some servers return inode number zero for the root (ok for all other
> inodes), if so
> we need to create a non-zero hash instead for it and use that.
>
>
>
> --
> Thanks,
>
> Steve
Pavel Shilovsky April 2, 2018, 5:43 p.m. | #2
2018-04-01 14:49 GMT-07:00 Steve French <smfrench@gmail.com>:
> Slightly updated version (shorter by a couple lines)
>
> On Sat, Mar 31, 2018 at 6:17 PM, Steve French <smfrench@gmail.com> wrote:
>> Some servers return inode number zero for the root (ok for all other
>> inodes), if so
>> we need to create a non-zero hash instead for it and use that.
>>
>>
>>
>> --
>> Thanks,
>>
>> Steve
>
>
>
> --
> Thanks,
>
> Steve

Please include the prefix "CIFS:" to patch names, [CIFS] is being
omitted in the title when you apply a patch to git.

Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>

--
Best regards,
Pavel Shilovsky
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Steve French April 2, 2018, 6:03 p.m. | #3
On Mon, Apr 2, 2018 at 12:43 PM, Pavel Shilovsky <piastryyy@gmail.com> wrote:
> 2018-04-01 14:49 GMT-07:00 Steve French <smfrench@gmail.com>:
>> Slightly updated version (shorter by a couple lines)
>>
>> On Sat, Mar 31, 2018 at 6:17 PM, Steve French <smfrench@gmail.com> wrote:
>>> Some servers return inode number zero for the root (ok for all other
>>> inodes), if so
>>> we need to create a non-zero hash instead for it and use that.
>>>
<...>
> Please include the prefix "CIFS:" to patch names, [CIFS] is being
> omitted in the title when you apply a patch to git.
>
> Reviewed-by: Pavel Shilovsky <pshilov@microsoft.com>
>
> --
> Best regards,
> Pavel Shilovsky

Yep - you are right.  It was getting stripped out.  Fixed and added
your reviewed by.

Patch

From 773dc281d36640be88a43194d8f3b799436999fe Mon Sep 17 00:00:00 2001
From: Steve French <stfrench@microsoft.com>
Date: Sat, 31 Mar 2018 18:13:38 -0500
Subject: [PATCH] [SMB3] Fix root directory when server returns inode number of
 zero

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>
---
 fs/cifs/cifsglob.h |  2 ++
 fs/cifs/inode.c    | 35 +++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+)

diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 3e3f86841372..4c6953705eb0 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1240,6 +1240,7 @@  struct cifsInodeInfo {
 #define CIFS_INO_DELETE_PENDING		  (3) /* delete pending on server */
 #define CIFS_INO_INVALID_MAPPING	  (4) /* pagecache is invalid */
 #define CIFS_INO_LOCK			  (5) /* lock bit for synchronization */
+#define CIFS_FAKE_ROOT_INODE_NUM	  (6) /* inode has fake inode number */
 	unsigned long flags;
 	spinlock_t writers_lock;
 	unsigned int writers;		/* Number of writers on this inode */
@@ -1470,6 +1471,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;
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index c98a852180b1..2f97f4f75d7d 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -707,6 +707,18 @@  cifs_get_file_info(struct file *filp)
 	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 =  1125899906842597; /* 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;
@@ -987,6 +1020,8 @@  cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
 		cifs_fattr_to_inode(inode, fattr);
 		if (sb->s_flags & SB_NOATIME)
 			inode->i_flags |= S_NOATIME | S_NOCMTIME;
+		if (fattr->cf_flags & CIFS_FATTR_FAKE_ROOT_INO)
+			set_bit(CIFS_FAKE_ROOT_INODE_NUM, &CIFS_I(inode)->flags);
 		if (inode->i_state & I_NEW) {
 			inode->i_ino = hash;
 #ifdef CONFIG_CIFS_FSCACHE
-- 
2.14.1