erofs-utils: fix cross-device submounts

Use device ID and inode number to identify hardlinks
rather than inode number only.

Link: https://lore.kernel.org/r/20201204175642.3231-1-hsiangkao@aol.com
Fixes: a17497f0844a ("erofs-utils: introduce inode operations")
Reviewed-by: Li Guifu <bluce.lee@aliyun.com>
Signed-off-by: Gao Xiang <hsiangkao@aol.com>
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index bf13c16..ac5b270 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -112,7 +112,12 @@
 struct erofs_inode {
 	struct list_head i_hash, i_subdirs, i_xattrs;
 
-	unsigned int flags;
+	union {
+		/* (erofsfuse) runtime flags */
+		unsigned int flags;
+		/* (mkfs.erofs) device ID containing source file */
+		u32 dev;
+	};
 	unsigned int i_count;
 	struct erofs_inode *i_parent;
 
diff --git a/lib/inode.c b/lib/inode.c
index 1cf813d..618eb28 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -35,7 +35,7 @@
 	[S_IFLNK >> S_SHIFT]  = EROFS_FT_SYMLINK,
 };
 
-#define NR_INODE_HASHTABLE	64
+#define NR_INODE_HASHTABLE	16384
 
 struct list_head inode_hashtable[NR_INODE_HASHTABLE];
 
@@ -54,14 +54,14 @@
 }
 
 /* get the inode from the (source) inode # */
-struct erofs_inode *erofs_iget(ino_t ino)
+struct erofs_inode *erofs_iget(dev_t dev, ino_t ino)
 {
 	struct list_head *head =
-		&inode_hashtable[ino % NR_INODE_HASHTABLE];
+		&inode_hashtable[(ino ^ dev) % NR_INODE_HASHTABLE];
 	struct erofs_inode *inode;
 
 	list_for_each_entry(inode, head, i_hash)
-		if (inode->i_ino[1] == ino)
+		if (inode->i_ino[1] == ino && inode->dev == dev)
 			return erofs_igrab(inode);
 	return NULL;
 }
@@ -764,6 +764,7 @@
 	strncpy(inode->i_srcpath, path, sizeof(inode->i_srcpath) - 1);
 	inode->i_srcpath[sizeof(inode->i_srcpath) - 1] = '\0';
 
+	inode->dev = st->st_dev;
 	inode->i_ino[1] = st->st_ino;
 
 	if (erofs_should_use_inode_extended(inode)) {
@@ -778,7 +779,8 @@
 	}
 
 	list_add(&inode->i_hash,
-		 &inode_hashtable[st->st_ino % NR_INODE_HASHTABLE]);
+		 &inode_hashtable[(st->st_ino ^ st->st_dev) %
+				  NR_INODE_HASHTABLE]);
 	return 0;
 }
 
@@ -829,7 +831,7 @@
 	 * since hard-link directory isn't allowed.
 	 */
 	if (!S_ISDIR(st.st_mode)) {
-		inode = erofs_iget(st.st_ino);
+		inode = erofs_iget(st.st_dev, st.st_ino);
 		if (inode)
 			return inode;
 	}