diff --git a/Makefile b/Makefile
index 725e5caa88bd..273a1f0e3b7c 100644
--- a/Makefile
+++ b/Makefile
@@ -183,6 +183,7 @@ objects = \
 	kernel-shared/extent_io.o	\
 	kernel-shared/file-item.o	\
 	kernel-shared/file.o	\
+	kernel-shared/fs_types.o	\
 	kernel-shared/free-space-cache.o	\
 	kernel-shared/free-space-tree.o	\
 	kernel-shared/inode-item.o	\
diff --git a/check/main.c b/check/main.c
index 6cf8016670f4..8dfb6ba2a8e8 100644
--- a/check/main.c
+++ b/check/main.c
@@ -835,7 +835,7 @@ static void maybe_free_inode_rec(struct cache_tree *inode_cache,
 	if (!rec->found_inode_item)
 		return;
 
-	filetype = imode_to_type(rec->imode);
+	filetype = btrfs_inode_type(rec->imode);
 	list_for_each_entry_safe(backref, tmp, &rec->backrefs, list) {
 		if (backref->found_dir_item && backref->found_dir_index) {
 			if (backref->filetype != filetype)
@@ -2142,7 +2142,7 @@ static int add_missing_dir_index(struct btrfs_root *root,
 	disk_key.offset = 0;
 
 	btrfs_set_dir_item_key(leaf, dir_item, &disk_key);
-	btrfs_set_dir_flags(leaf, dir_item, imode_to_type(rec->imode));
+	btrfs_set_dir_flags(leaf, dir_item, btrfs_inode_type(rec->imode));
 	btrfs_set_dir_data_len(leaf, dir_item, 0);
 	btrfs_set_dir_name_len(leaf, dir_item, backref->namelen);
 	name_ptr = (unsigned long)(dir_item + 1);
@@ -2329,7 +2329,7 @@ static int repair_inode_backrefs(struct btrfs_root *root,
 			ret = btrfs_insert_dir_item(trans, root, backref->name,
 						    backref->namelen,
 						    backref->dir, &location,
-						    imode_to_type(rec->imode),
+						    btrfs_inode_type(rec->imode),
 						    backref->index);
 			BUG_ON(ret);
 			btrfs_commit_transaction(trans, root);
@@ -2363,7 +2363,7 @@ static int find_file_type(struct inode_record *rec, u8 *type)
 
 	/* For inode item recovered case */
 	if (rec->found_inode_item) {
-		*type = imode_to_type(rec->imode);
+		*type = btrfs_inode_type(rec->imode);
 		return 0;
 	}
 
@@ -2622,7 +2622,7 @@ static int repair_inode_no_item(struct btrfs_trans_handle *trans,
 	}
 
 	ret = btrfs_new_inode(trans, root, rec->ino,
-			      mode | btrfs_type_to_imode(filetype));
+			      mode | fs_ftype_to_umode(filetype));
 	if (ret < 0)
 		goto out;
 
@@ -2634,7 +2634,7 @@ static int repair_inode_no_item(struct btrfs_trans_handle *trans,
 	 * We just fill the record and return
 	 */
 	rec->found_dir_item = 1;
-	rec->imode = mode | btrfs_type_to_imode(filetype);
+	rec->imode = mode | fs_ftype_to_umode(filetype);
 	rec->nlink = 0;
 	rec->errors &= ~I_ERR_NO_INODE_ITEM;
 	/* Ensure the inode_nlinks repair function will be called */
diff --git a/check/mode-common.c b/check/mode-common.c
index 7afd9ed96fd2..34e5267bfd8c 100644
--- a/check/mode-common.c
+++ b/check/mode-common.c
@@ -773,7 +773,7 @@ static int find_file_type_dir_index(struct btrfs_root *root, u64 ino, u64 dirid,
 	if (name_len != len || memcmp(namebuf, name, len))
 		goto out;
 	found = true;
-	*imode_ret = btrfs_type_to_imode(filetype);
+	*imode_ret = fs_ftype_to_umode(filetype);
 out:
 	btrfs_release_path(&path);
 	if (!found && !ret)
@@ -832,7 +832,7 @@ static int find_file_type_dir_item(struct btrfs_root *root, u64 ino, u64 dirid,
 				   (unsigned long)(di + 1), len);
 		if (name_len != len || memcmp(namebuf, name, len))
 			continue;
-		*imode_ret = btrfs_type_to_imode(filetype);
+		*imode_ret = fs_ftype_to_umode(filetype);
 		found = true;
 		goto out;
 	}
diff --git a/check/mode-common.h b/check/mode-common.h
index 894bbbb8141b..83296baf4a99 100644
--- a/check/mode-common.h
+++ b/check/mode-common.h
@@ -86,23 +86,6 @@ extern int check_data_csum;
 extern struct btrfs_fs_info *gfs_info;
 extern struct cache_tree *roots_info_cache;
 
-static inline u8 imode_to_type(u32 imode)
-{
-#define S_SHIFT 12
-	static unsigned char btrfs_type_by_mode[S_IFMT >> S_SHIFT] = {
-		[S_IFREG >> S_SHIFT]	= BTRFS_FT_REG_FILE,
-		[S_IFDIR >> S_SHIFT]	= BTRFS_FT_DIR,
-		[S_IFCHR >> S_SHIFT]	= BTRFS_FT_CHRDEV,
-		[S_IFBLK >> S_SHIFT]	= BTRFS_FT_BLKDEV,
-		[S_IFIFO >> S_SHIFT]	= BTRFS_FT_FIFO,
-		[S_IFSOCK >> S_SHIFT]	= BTRFS_FT_SOCK,
-		[S_IFLNK >> S_SHIFT]	= BTRFS_FT_SYMLINK,
-	};
-
-	return btrfs_type_by_mode[(imode & S_IFMT) >> S_SHIFT];
-#undef S_SHIFT
-}
-
 static inline bool fs_root_objectid(u64 objectid)
 {
 	if (objectid == BTRFS_TREE_RELOC_OBJECTID ||
@@ -167,21 +150,6 @@ static inline bool is_valid_imode(u32 imode)
 
 int recow_extent_buffer(struct btrfs_root *root, struct extent_buffer *eb);
 
-static inline u32 btrfs_type_to_imode(u8 type)
-{
-	static u32 imode_by_btrfs_type[] = {
-		[BTRFS_FT_REG_FILE]	= S_IFREG,
-		[BTRFS_FT_DIR]		= S_IFDIR,
-		[BTRFS_FT_CHRDEV]	= S_IFCHR,
-		[BTRFS_FT_BLKDEV]	= S_IFBLK,
-		[BTRFS_FT_FIFO]		= S_IFIFO,
-		[BTRFS_FT_SOCK]		= S_IFSOCK,
-		[BTRFS_FT_SYMLINK]	= S_IFLNK,
-	};
-
-	return imode_by_btrfs_type[(type)];
-}
-
 int get_extent_item_generation(u64 bytenr, u64 *gen_ret);
 
 /*
diff --git a/check/mode-lowmem.c b/check/mode-lowmem.c
index 3b2807cc5de9..dd05fa47384f 100644
--- a/check/mode-lowmem.c
+++ b/check/mode-lowmem.c
@@ -1200,19 +1200,19 @@ next:
 	key.type = BTRFS_DIR_INDEX_KEY;
 	key.offset = index;
 	tmp_err |= find_dir_item(root, &key, &location, namebuf, len,
-			    imode_to_type(mode));
+				btrfs_inode_type(mode));
 
 	/* Find related dir_item */
 	key.objectid = ref_key->offset;
 	key.type = BTRFS_DIR_ITEM_KEY;
 	key.offset = btrfs_name_hash(namebuf, len);
 	tmp_err |= find_dir_item(root, &key, &location, namebuf, len,
-			    imode_to_type(mode));
+				 btrfs_inode_type(mode));
 end:
 	if (tmp_err && opt_check_repair) {
 		ret = repair_ternary_lowmem(root, ref_key->offset,
 					    ref_key->objectid, index, namebuf,
-					    name_len, imode_to_type(mode),
+					    name_len, btrfs_inode_type(mode),
 					    tmp_err);
 		if (!ret) {
 			need_research = 1;
@@ -1220,7 +1220,7 @@ end:
 		}
 	}
 	print_inode_ref_err(root, ref_key, index, namebuf, name_len,
-			    imode_to_type(mode), tmp_err);
+			    btrfs_inode_type(mode), tmp_err);
 	err |= tmp_err;
 	len = sizeof(*ref) + name_len;
 	ref = (struct btrfs_inode_ref *)((char *)ref + len);
@@ -1782,7 +1782,7 @@ begin:
 		ii = btrfs_item_ptr(path->nodes[0], path->slots[0],
 				    struct btrfs_inode_item);
 		mode = btrfs_inode_mode(path->nodes[0], ii);
-		if (imode_to_type(mode) != filetype) {
+		if (btrfs_inode_type(mode) != filetype) {
 			tmp_err |= INODE_ITEM_MISMATCH;
 			goto next;
 		}
@@ -1813,7 +1813,7 @@ next:
 		if (tmp_err && opt_check_repair) {
 			ret = repair_dir_item(root, di_key,
 					      location.objectid, index,
-					      imode_to_type(mode), namebuf,
+					      btrfs_inode_type(mode), namebuf,
 					      name_len, tmp_err);
 			if (ret != tmp_err) {
 				need_research = 1;
@@ -2635,7 +2635,7 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path)
 	nbytes = btrfs_inode_nbytes(node, ii);
 	mode = btrfs_inode_mode(node, ii);
 	flags = btrfs_inode_flags(node, ii);
-	dir = imode_to_type(mode) == BTRFS_FT_DIR;
+	dir = btrfs_inode_type(mode) == BTRFS_FT_DIR;
 	nlink = btrfs_inode_nlink(node, ii);
 	generation = btrfs_inode_generation(node, ii);
 	transid = btrfs_inode_transid(node, ii);
@@ -2729,7 +2729,7 @@ static int check_inode_item(struct btrfs_root *root, struct btrfs_path *path)
 			if (!dir) {
 				warning("root %llu INODE[%llu] mode %u shouldn't have DIR_INDEX[%llu %llu]",
 					root->objectid,	inode_id,
-					imode_to_type(mode), key.objectid,
+					btrfs_inode_type(mode), key.objectid,
 					key.offset);
 			}
 			if (is_orphan && key.type == BTRFS_DIR_INDEX_KEY)
@@ -2772,7 +2772,7 @@ out:
 
 		if ((nlink != 1 || refs != 1) && opt_check_repair) {
 			ret = repair_inode_nlinks_lowmem(root, path, inode_id,
-				namebuf, name_len, refs, imode_to_type(mode),
+				namebuf, name_len, refs, btrfs_inode_type(mode),
 				&nlink);
 		}
 
@@ -2808,7 +2808,7 @@ out:
 			if (opt_check_repair)
 				ret = repair_inode_nlinks_lowmem(root, path,
 					 inode_id, namebuf, name_len, refs,
-					 imode_to_type(mode), &nlink);
+					 btrfs_inode_type(mode), &nlink);
 			if (!opt_check_repair || ret) {
 				err |= LINK_COUNT_ERROR;
 				error(
@@ -5192,7 +5192,7 @@ static int check_fs_first_inode(struct btrfs_root *root)
 		ii = btrfs_item_ptr(path.nodes[0], path.slots[0],
 				    struct btrfs_inode_item);
 		mode = btrfs_inode_mode(path.nodes[0], ii);
-		if (imode_to_type(mode) != BTRFS_FT_DIR)
+		if (btrfs_inode_type(mode) != BTRFS_FT_DIR)
 			err |= INODE_ITEM_MISMATCH;
 	}
 
diff --git a/include/kerncompat.h b/include/kerncompat.h
index 18b474f6aa41..a7b075a4ff80 100644
--- a/include/kerncompat.h
+++ b/include/kerncompat.h
@@ -179,6 +179,7 @@ typedef long long s64;
 typedef int s32;
 #endif
 
+typedef unsigned short umode_t;
 typedef u64 sector_t;
 
 struct vma_shared { int prio_tree_node; };
diff --git a/kernel-shared/ctree.h b/kernel-shared/ctree.h
index bcf11d870061..2df8166970be 100644
--- a/kernel-shared/ctree.h
+++ b/kernel-shared/ctree.h
@@ -31,6 +31,7 @@
 #include "kernel-shared/accessors.h"
 #include "kernel-shared/extent-io-tree.h"
 #include "kernel-shared/locking.h"
+#include "kernel-shared/fs_types.h"
 #include "crypto/crc32c.h"
 #include "common/extent-cache.h"
 
@@ -1213,6 +1214,11 @@ static inline int is_fstree(u64 rootid)
 void btrfs_uuid_to_key(const u8 *uuid, struct btrfs_key *key);
 
 /* inode.c */
+static inline u8 btrfs_inode_type(u32 inode_mode)
+{
+	return fs_umode_to_ftype(inode_mode);
+}
+
 int check_dir_conflict(struct btrfs_root *root, char *name, int namelen,
 		u64 dir, u64 index);
 int btrfs_new_inode(struct btrfs_trans_handle *trans, struct btrfs_root *root,
diff --git a/kernel-shared/fs_types.c b/kernel-shared/fs_types.c
new file mode 100644
index 000000000000..193d7ee80d93
--- /dev/null
+++ b/kernel-shared/fs_types.c
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Cross-ported from kernel fs/fs_types.c, with the following changes:
+ *
+ * - Add new ftype to imode converter.
+ *   This is btrfs-progs specific, as in kernel we always have a inode
+ *   to grab it's mode.
+ *   But in progs we need do convert from umode to ftype for verification.
+ */
+
+#include "kernel-shared/fs_types.h"
+
+/*
+ * dirent file type to fs on-disk file type conversion
+ * Values not initialized explicitly are FT_UNKNOWN (0).
+ */
+static const unsigned char fs_ftype_by_dtype[DT_MAX] = {
+	[DT_REG]	= FT_REG_FILE,
+	[DT_DIR]	= FT_DIR,
+	[DT_LNK]	= FT_SYMLINK,
+	[DT_CHR]	= FT_CHRDEV,
+	[DT_BLK]	= FT_BLKDEV,
+	[DT_FIFO]	= FT_FIFO,
+	[DT_SOCK]	= FT_SOCK,
+};
+
+/**
+ * fs_umode_to_ftype() - file mode to on-disk file type.
+ * @mode: The file mode to convert.
+ *
+ * This function converts the file mode value to the on-disk file type (FT_*).
+ *
+ * Context: Any context.
+ * Return:
+ * * FT_UNKNOWN		- Unknown type
+ * * FT_REG_FILE	- Regular file
+ * * FT_DIR		- Directory
+ * * FT_CHRDEV		- Character device
+ * * FT_BLKDEV		- Block device
+ * * FT_FIFO		- FIFO
+ * * FT_SOCK		- Local-domain socket
+ * * FT_SYMLINK		- Symbolic link
+ */
+unsigned char fs_umode_to_ftype(umode_t mode)
+{
+	return fs_ftype_by_dtype[S_DT(mode)];
+}
+
+umode_t fs_ftype_to_umode(unsigned char ftype)
+{
+	static u32 imode_by_ftype[] = {
+		[FT_REG_FILE]     = S_IFREG,
+		[FT_DIR]          = S_IFDIR,
+		[FT_CHRDEV]       = S_IFCHR,
+		[FT_BLKDEV]       = S_IFBLK,
+		[FT_FIFO]         = S_IFIFO,
+		[FT_SOCK]         = S_IFSOCK,
+		[FT_SYMLINK]      = S_IFLNK,
+       };
+       return imode_by_ftype[(ftype)];
+}
diff --git a/kernel-shared/fs_types.h b/kernel-shared/fs_types.h
new file mode 100644
index 000000000000..bdac8318413b
--- /dev/null
+++ b/kernel-shared/fs_types.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_FS_TYPES_H
+#define _LINUX_FS_TYPES_H
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include "kerncompat.h"
+
+/*
+ * This is cross-ported from kernel include/linux/fs_types.h, changes are:
+ *
+ * - New ftype to umode convertor
+ * - Use glibc's dirent.h to replace the DT_* declarations
+ */
+
+/*
+ * This is a header for the common implementation of dirent
+ * to fs on-disk file type conversion.  Although the fs on-disk
+ * bits are specific to every file system, in practice, many
+ * file systems use the exact same on-disk format to describe
+ * the lower 3 file type bits that represent the 7 POSIX file
+ * types.
+ *
+ * It is important to note that the definitions in this
+ * header MUST NOT change. This would break both the
+ * userspace ABI and the on-disk format of filesystems
+ * using this code.
+ *
+ * All those file systems can use this generic code for the
+ * conversions.
+ */
+
+/*
+ * struct dirent file types
+ * exposed to user via getdents(2), readdir(3)
+ *
+ * These match bits 12..15 of stat.st_mode
+ * (ie "(i_mode >> 12) & 15").
+ */
+#define S_DT_SHIFT	12
+#define S_DT(mode)	(((mode) & S_IFMT) >> S_DT_SHIFT)
+#define S_DT_MASK	(S_IFMT >> S_DT_SHIFT)
+
+/* these are defined by POSIX and also present in glibc's dirent.h */
+/*
+#define DT_UNKNOWN	0
+#define DT_FIFO		1
+#define DT_CHR		2
+#define DT_DIR		4
+#define DT_BLK		6
+#define DT_REG		8
+#define DT_LNK		10
+#define DT_SOCK		12
+#define DT_WHT		14
+*/
+
+#define DT_MAX		(S_DT_MASK + 1) /* 16 */
+
+/*
+ * fs on-disk file types.
+ * Only the low 3 bits are used for the POSIX file types.
+ * Other bits are reserved for fs private use.
+ * These definitions are shared and used by multiple filesystems,
+ * and MUST NOT change under any circumstances.
+ *
+ * Note that no fs currently stores the whiteout type on-disk,
+ * so whiteout dirents are exposed to user as DT_CHR.
+ */
+#define FT_UNKNOWN	0
+#define FT_REG_FILE	1
+#define FT_DIR		2
+#define FT_CHRDEV	3
+#define FT_BLKDEV	4
+#define FT_FIFO		5
+#define FT_SOCK		6
+#define FT_SYMLINK	7
+
+#define FT_MAX		8
+
+/*
+ * declarations for helper functions, accompanying implementation
+ * is in fs/fs_types.c
+ */
+extern unsigned char fs_umode_to_ftype(umode_t mode);
+extern umode_t fs_ftype_to_umode(unsigned char ftype);
+
+#endif
diff --git a/kernel-shared/inode.c b/kernel-shared/inode.c
index 3d420787c8f9..1893e48001af 100644
--- a/kernel-shared/inode.c
+++ b/kernel-shared/inode.c
@@ -33,9 +33,19 @@
 #include "kernel-shared/ctree.h"
 #include "kernel-shared/transaction.h"
 #include "kernel-shared/disk-io.h"
+#include "kernel-shared/fs_types.h"
 #include "common/messages.h"
 #include "common/internal.h"
 
+static_assert(BTRFS_FT_UNKNOWN == FT_UNKNOWN);
+static_assert(BTRFS_FT_REG_FILE == FT_REG_FILE);
+static_assert(BTRFS_FT_DIR == FT_DIR);
+static_assert(BTRFS_FT_CHRDEV == FT_CHRDEV);
+static_assert(BTRFS_FT_BLKDEV == FT_BLKDEV);
+static_assert(BTRFS_FT_FIFO == FT_FIFO);
+static_assert(BTRFS_FT_SOCK == FT_SOCK);
+static_assert(BTRFS_FT_SYMLINK == FT_SYMLINK);
+
 /*
  * Find a free inode index for later btrfs_add_link().
  * Currently just search from the largest dir_index and +1.
