[v4,6/7] ext2fs: add EXT4_FEATURE_INCOMPAT_64INODE support

Message ID 20180504070923.45140-7-c17828@cray.com
State Changes Requested
Headers show
Series
  • 64bit inode e2fsprogs support
Related show

Commit Message

c17828 May 4, 2018, 7:09 a.m.
From: Artem Blagodarenko <artem.blagodarenko@gmail.com>

Inodes count and free inodes count should be 64 bit long.
This patch also changes s_inodes_count* to 64 bit

Lustre-bug: https://jira.hpdd.intel.com/browse/LU-9309
Signed-off-by: Artem Blagodarenko <artem.blagodarenko@gmail.com>
---
 debugfs/debugfs.c           |  2 +-
 debugfs/set_fields.c        |  3 ++-
 e2fsck/dirinfo.c            | 28 ++++++++++++++--------------
 e2fsck/e2fsck.h             | 22 +++++++++++-----------
 e2fsck/pass2.c              | 11 ++++++++---
 e2fsck/pass3.c              |  2 +-
 e2fsck/problem.c            |  5 +++++
 e2fsck/problem.h            |  5 ++++-
 e2fsck/super.c              |  1 +
 lib/e2p/feature.c           |  2 ++
 lib/ext2fs/ext2_fs.h        | 15 +++++++++++++--
 lib/ext2fs/ext2fs.h         | 14 ++++++++++----
 lib/ext2fs/swapfs.c         |  6 ++++++
 lib/ext2fs/tst_super_size.c |  8 +++++++-
 misc/fuse2fs.c              |  8 ++++----
 misc/mke2fs.c               | 22 ++++++++++++++--------
 misc/tune2fs.c              |  3 ++-
 17 files changed, 105 insertions(+), 52 deletions(-)

Comments

Andreas Dilger May 4, 2018, 9:37 a.m. | #1
On May 4, 2018, at 1:09 AM, c17828 <artem.blagodarenko@gmail.com> wrote:
> 
> From: Artem Blagodarenko <artem.blagodarenko@gmail.com>
> 
> Inodes count and free inodes count should be 64 bit long.
> This patch also changes s_inodes_count* to 64 bit
> 
> Lustre-bug: https://jira.hpdd.intel.com/browse/LU-9309
> Signed-off-by: Artem Blagodarenko <artem.blagodarenko@gmail.com>
> 
> extern void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks);
> diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
> index a8ccc465..ef0b97c0 100644
> --- a/e2fsck/pass2.c
> +++ b/e2fsck/pass2.c
> @@ -179,7 +179,12 @@ void e2fsck_pass2(e2fsck_t ctx)
> 
> 	if (ext2fs_has_feature_dir_index(fs->super))
> 		ext2fs_dblist_sort2(fs->dblist, special_dir_block_cmp);
> -
> +	if (ext2fs_has_feature_inode64(ctx->fs->super) &&
> +	    !ext2fs_has_feature_dirdata(ctx->fs->super)) {
> +		if (fix_problem(ctx, PR_2_FIX_DIRDATA_FEATURE, &cd.pctx))
> +			ctx->fs->super->s_feature_incompat |=
> +				EXT4_FEATURE_INCOMPAT_DIRDATA;

This should use:
			 ext2fs_set_feature_dirdata(ctx->fs->super);

> diff --git a/e2fsck/problem.c b/e2fsck/problem.c
> index 2a86d528..d8620b93 100644
> --- a/e2fsck/problem.c
> +++ b/e2fsck/problem.c
> @@ -1676,6 +1676,11 @@ static struct e2fsck_problem problem_table[] = {
> 	  N_("@E dirdata length set incorrectly.\n"),
> 	  PROMPT_CLEAR, PR_PREEN_OK },
> 
> +	/* dirdata feature is needed for inode64 */
> +	{ PR_2_FIX_DIRDATA_FEATURE,
> +	  N_("@E ino64 feature without dirdata.\n"),

This should use "inode64" since that is what the feature is named in
lib/e2p/feature.c.

> diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c
> index b7f6c1d2..fd77dedd 100644
> --- a/lib/e2p/feature.c
> +++ b/lib/e2p/feature.c
> @@ -105,6 +105,8 @@ static struct feature feature_list[] = {
> 			"inline_data"},
> 	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_ENCRYPT,
> 			"encrypt"},
> +	{	E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_INODE64,
> +			"inode64"},
> 	{	0, 0, 0 },
> };
> 

> diff --git a/misc/mke2fs.c b/misc/mke2fs.c
> index 8edb7546..2878aadc 100644
> --- a/misc/mke2fs.c
> +++ b/misc/mke2fs.c
> 
> @@ -2457,13 +2458,15 @@ profile_error:
> 	if (num_inodes == 0) {
> 		unsigned long long n;
> 		n = ext2fs_blocks_count(&fs_param) * blocksize / inode_ratio;
> -		if (n > MAX_32_NUM) {
> -			if (ext2fs_has_feature_64bit(&fs_param))
> +		if (n > MAX_32_NUM && !ext2fs_has_feature_inode64(&fs_param)) {
> +			if (ext2fs_has_feature_64bit(&fs_param)) {
> 				num_inodes = MAX_32_NUM;
> +			}
> 			else {

(style) prefer "} else {"

Strangely, I don't see any changes in this patch that would affect the
ABI compatibility?  Looking at lib/ext2fs/ext2fs_*() interfaces that
take an ext2fs_ino_t there are quite a few places that need to be fixed.
Basically, any piece of code that is using ext2fs_get_inodes_count()
and friends to handle an inode number or "ext2fs_ino_t" needs to be
examined.

That will likely need at least one or two more patches before this feature
could be enabled and considered working.  One of the thoughts that Ted had
was to have a test mode for e2fsprogs (and also the kernel) where the high
word of the inode number is set to some constant on initial access for
filesystems with < 2^32 inodes, and then any time it is used the high word
is checked to ensure the high word has not been dropped.  That would ensure
this code is actually working with 64-bit inode numbers, since I suspect
that it isn't really working at this time.

Cheers, Andreas

Patch

diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 4a533b53..a80cf668 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -829,7 +829,7 @@  void internal_dump_inode(FILE *out, const char *prefix,
 	else if (LINUX_S_ISFIFO(inode->i_mode)) i_type = "FIFO";
 	else if (LINUX_S_ISSOCK(inode->i_mode)) i_type = "socket";
 	else i_type = "bad type";
-	fprintf(out, "%sInode: %u   Type: %s    ", prefix, inode_num, i_type);
+	fprintf(out, "%sInode: %lu   Type: %s    ", prefix, inode_num, i_type);
 	fprintf(out, "%sMode:  0%03o   Flags: 0x%x\n",
 		prefix, inode->i_mode & 07777, inode->i_flags);
 	if (is_large_inode && large_inode->i_extra_isize >= 24) {
diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index 8dfbba9c..bfab7ff8 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -79,7 +79,8 @@  static errcode_t parse_mmp_clear(struct field_set_info *info, char *field,
 #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
 
 static struct field_set_info super_fields[] = {
-	{ "inodes_count", &set_sb.s_inodes_count, NULL, 4, parse_uint },
+	{ "inodes_count", &set_sb.s_inodes_count, &set_sb.s_inodes_count_hi,
+		4, parse_uint },
 	{ "blocks_count", &set_sb.s_blocks_count, &set_sb.s_blocks_count_hi,
 		4, parse_uint },
 	{ "r_blocks_count", &set_sb.s_r_blocks_count,
diff --git a/e2fsck/dirinfo.c b/e2fsck/dirinfo.c
index b29f7e92..e8f1f26d 100644
--- a/e2fsck/dirinfo.c
+++ b/e2fsck/dirinfo.c
@@ -35,8 +35,8 @@  struct dir_info_iter {
 };
 
 struct dir_info_ent {
-	ext2_ino_t		dotdot;	/* Parent according to '..' */
-	ext2_ino_t		parent; /* Parent according to treewalk */
+	ext2_ino64_t		dotdot;	/* Parent according to '..' */
+	ext2_ino64_t		parent; /* Parent according to treewalk */
 };
 
 
@@ -201,7 +201,7 @@  void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
  * get_dir_info() --- given an inode number, try to find the directory
  * information entry for it.
  */
-static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
+static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino64_t ino)
 {
 	struct dir_info_db	*db = ctx->dir_info;
 	int			low, high, mid;
@@ -210,7 +210,7 @@  static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
 		return 0;
 
 #ifdef DIRINFO_DEBUG
-	printf("e2fsck_get_dir_info %d...", ino);
+	printf("e2fsck_get_dir_info %ld...", ino);
 #endif
 
 #ifdef CONFIG_TDB
@@ -220,7 +220,7 @@  static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
 		struct dir_info_ent	*buf;
 
 		key.dptr = (unsigned char *) &ino;
-		key.dsize = sizeof(ext2_ino_t);
+		key.dsize = sizeof(ext2_ino64_t);
 
 		data = tdb_fetch(db->tdb, key);
 		if (!data.dptr) {
@@ -235,7 +235,7 @@  static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
 		ret_dir_info.dotdot = buf->dotdot;
 		ret_dir_info.parent = buf->parent;
 #ifdef DIRINFO_DEBUG
-		printf("(%d,%d,%d)\n", ino, buf->dotdot, buf->parent);
+		printf("(%ld,%d,%d)\n", ino, buf->dotdot, buf->parent);
 #endif
 		free(data.dptr);
 		return &ret_dir_info;
@@ -422,8 +422,8 @@  struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, struct dir_info_iter *iter)
  * This function only sets the parent pointer, and requires that
  * dirinfo structure has already been created.
  */
-int e2fsck_dir_info_set_parent(e2fsck_t ctx, ext2_ino_t ino,
-			       ext2_ino_t parent)
+int e2fsck_dir_info_set_parent(e2fsck_t ctx, ext2_ino64_t ino,
+			       ext2_ino64_t parent)
 {
 	struct dir_info *p;
 
@@ -439,8 +439,8 @@  int e2fsck_dir_info_set_parent(e2fsck_t ctx, ext2_ino_t ino,
  * This function only sets the dot dot pointer, and requires that
  * dirinfo structure has already been created.
  */
-int e2fsck_dir_info_set_dotdot(e2fsck_t ctx, ext2_ino_t ino,
-			       ext2_ino_t dotdot)
+int e2fsck_dir_info_set_dotdot(e2fsck_t ctx, ext2_ino64_t ino,
+			       ext2_ino64_t dotdot)
 {
 	struct dir_info *p;
 
@@ -456,8 +456,8 @@  int e2fsck_dir_info_set_dotdot(e2fsck_t ctx, ext2_ino_t ino,
  * This function only sets the parent pointer, and requires that
  * dirinfo structure has already been created.
  */
-int e2fsck_dir_info_get_parent(e2fsck_t ctx, ext2_ino_t ino,
-			       ext2_ino_t *parent)
+int e2fsck_dir_info_get_parent(e2fsck_t ctx, ext2_ino64_t ino,
+			       ext2_ino64_t *parent)
 {
 	struct dir_info *p;
 
@@ -472,8 +472,8 @@  int e2fsck_dir_info_get_parent(e2fsck_t ctx, ext2_ino_t ino,
  * This function only sets the dot dot pointer, and requires that
  * dirinfo structure has already been created.
  */
-int e2fsck_dir_info_get_dotdot(e2fsck_t ctx, ext2_ino_t ino,
-			       ext2_ino_t *dotdot)
+int e2fsck_dir_info_get_dotdot(e2fsck_t ctx, ext2_ino64_t ino,
+			       ext2_ino64_t *dotdot)
 {
 	struct dir_info *p;
 
diff --git a/e2fsck/e2fsck.h b/e2fsck/e2fsck.h
index 9eee4bc2..2e038fbe 100644
--- a/e2fsck/e2fsck.h
+++ b/e2fsck/e2fsck.h
@@ -93,9 +93,9 @@ 
  * directory information.
  */
 struct dir_info {
-	ext2_ino_t		ino;	/* Inode number */
-	ext2_ino_t		dotdot;	/* Parent according to '..' */
-	ext2_ino_t		parent; /* Parent according to treewalk */
+	ext2_ino64_t		ino;	/* Inode number */
+	ext2_ino64_t		dotdot;	/* Parent according to '..' */
+	ext2_ino64_t		parent; /* Parent according to treewalk */
 };
 
 
@@ -460,14 +460,14 @@  extern struct dir_info_iter *e2fsck_dir_info_iter_begin(e2fsck_t ctx);
 extern struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx,
 					     struct dir_info_iter *);
 extern void e2fsck_dir_info_iter_end(e2fsck_t ctx, struct dir_info_iter *);
-extern int e2fsck_dir_info_set_parent(e2fsck_t ctx, ext2_ino_t ino,
-				      ext2_ino_t parent);
-extern int e2fsck_dir_info_set_dotdot(e2fsck_t ctx, ext2_ino_t ino,
-				      ext2_ino_t dotdot);
-extern int e2fsck_dir_info_get_parent(e2fsck_t ctx, ext2_ino_t ino,
-				      ext2_ino_t *parent);
-extern int e2fsck_dir_info_get_dotdot(e2fsck_t ctx, ext2_ino_t ino,
-				      ext2_ino_t *dotdot);
+extern int e2fsck_dir_info_set_parent(e2fsck_t ctx, ext2_ino64_t ino,
+				      ext2_ino64_t parent);
+extern int e2fsck_dir_info_set_dotdot(e2fsck_t ctx, ext2_ino64_t ino,
+				      ext2_ino64_t dotdot);
+extern int e2fsck_dir_info_get_parent(e2fsck_t ctx, ext2_ino64_t ino,
+				      ext2_ino64_t *parent);
+extern int e2fsck_dir_info_get_dotdot(e2fsck_t ctx, ext2_ino64_t ino,
+				      ext2_ino64_t *dotdot);
 
 /* dx_dirinfo.c */
 extern void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks);
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c
index a8ccc465..ef0b97c0 100644
--- a/e2fsck/pass2.c
+++ b/e2fsck/pass2.c
@@ -179,7 +179,12 @@  void e2fsck_pass2(e2fsck_t ctx)
 
 	if (ext2fs_has_feature_dir_index(fs->super))
 		ext2fs_dblist_sort2(fs->dblist, special_dir_block_cmp);
-
+	if (ext2fs_has_feature_inode64(ctx->fs->super) &&
+	    !ext2fs_has_feature_dirdata(ctx->fs->super)) {
+		if (fix_problem(ctx, PR_2_FIX_DIRDATA_FEATURE, &cd.pctx))
+			ctx->fs->super->s_feature_incompat |=
+				EXT4_FEATURE_INCOMPAT_DIRDATA;
+	}
 	check_dir_func = cd.ra_entries ? check_dir_block2 : check_dir_block;
 	cd.pctx.errcode = ext2fs_dblist_iterate2(fs->dblist, check_dir_func,
 						 &cd);
@@ -995,8 +1000,8 @@  static int check_dir_block(ext2_filsys fs,
 	int			dot_state;
 	unsigned int		rec_len;
 	blk64_t			block_nr = db->blk;
-	ext2_ino_t 		ino = db->ino;
-	ext2_ino_t 		subdir_parent;
+	ext2_ino64_t		ino = db->ino;
+	ext2_ino64_t		subdir_parent;
 	__u16			links;
 	struct check_dir_struct	*cd;
 	char			*buf, *ibuf;
diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c
index 4a777213..b6bee3f8 100644
--- a/e2fsck/pass3.c
+++ b/e2fsck/pass3.c
@@ -284,7 +284,7 @@  static int check_directory(e2fsck_t ctx, ext2_ino_t dir,
 			   struct problem_context *pctx)
 {
 	ext2_filsys 	fs = ctx->fs;
-	ext2_ino_t	ino = dir, parent;
+	ext2_ino64_t	ino = dir, parent;
 	int		loop_pass = 0, parent_count = 0;
 
 	while (1) {
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index 2a86d528..d8620b93 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -1676,6 +1676,11 @@  static struct e2fsck_problem problem_table[] = {
 	  N_("@E dirdata length set incorrectly.\n"),
 	  PROMPT_CLEAR, PR_PREEN_OK },
 
+	/* dirdata feature is needed for inode64 */
+	{ PR_2_FIX_DIRDATA_FEATURE,
+	  N_("@E ino64 feature without dirdata.\n"),
+	  PROMPT_FIX, PR_PREEN_OK },
+
 	/* Pass 3 errors */
 
 	/* Pass 3: Checking directory connectivity */
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index 05214840..c847063e 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -13,7 +13,7 @@  typedef __u32 problem_t;
 
 struct problem_context {
 	errcode_t	errcode;
-	ext2_ino_t ino, ino2, dir;
+	ext2_ino64_t ino, ino2, dir;
 	struct ext2_inode *inode;
 	struct ext2_dir_entry *dirent;
 	blk64_t	blk, blk2;
@@ -1007,6 +1007,9 @@  struct problem_context {
 /* Entry dirdata length set incorrectly */
 #define PR_2_CLEAR_DIRDATA		0x020051
 
+/* inode64 feature without dirdata */
+#define PR_2_FIX_DIRDATA_FEATURE	0x020052
+
 /*
  * Pass 3 errors
  */
diff --git a/e2fsck/super.c b/e2fsck/super.c
index 20d6190c..37e5dbfc 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -1048,6 +1048,7 @@  int check_backup_super_block(e2fsck_t ctx)
 		    SUPER_DIFFERENT(s_blocks_count) ||
 		    SUPER_DIFFERENT(s_blocks_count_hi) ||
 		    SUPER_DIFFERENT(s_inodes_count) ||
+		    SUPER_DIFFERENT(s_inodes_count_hi) ||
 		    memcmp(fs->super->s_uuid, backup_sb->s_uuid,
 			   sizeof(fs->super->s_uuid)))
 			ret = 1;
diff --git a/lib/e2p/feature.c b/lib/e2p/feature.c
index b7f6c1d2..fd77dedd 100644
--- a/lib/e2p/feature.c
+++ b/lib/e2p/feature.c
@@ -105,6 +105,8 @@  static struct feature feature_list[] = {
 			"inline_data"},
 	{       E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_ENCRYPT,
 			"encrypt"},
+	{	E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_INODE64,
+			"inode64"},
 	{	0, 0, 0 },
 };
 
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 4681a216..9ce48321 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -737,7 +737,13 @@  struct ext2_super_block {
 	__le32	s_lpf_ino;		/* Location of the lost+found inode */
 	__le32  s_prj_quota_inum;	/* inode for tracking project quota */
 	__le32	s_checksum_seed;	/* crc32c(orig_uuid) if csum_seed set */
-	__le32	s_reserved[98];		/* Padding to the end of the block */
+	__le32	s_inodes_count_hi;	/* high part of inode count */
+	__le32	s_free_inodes_count_hi;	/* Free inodes count */
+	__le32	s_prj_quota_inum_hi;	/* high part of project quota inode */
+	__le32  s_last_orphan_hi;       /* high part of last orphan */
+	__le32  s_first_error_ino_hi;   /* high part of first error ino */
+	__le32  s_last_error_ino_hi;    /* high part of last error ino */
+	__le32	s_reserved[92];		/* Padding to the end of the block */
 	__u32	s_checksum;		/* crc32c(superblock) */
 };
 
@@ -827,6 +833,8 @@  struct ext2_super_block {
 #define EXT4_FEATURE_INCOMPAT_LARGEDIR		0x4000 /* >2GB or 3-lvl htree */
 #define EXT4_FEATURE_INCOMPAT_INLINE_DATA	0x8000 /* data in inode */
 #define EXT4_FEATURE_INCOMPAT_ENCRYPT		0x10000
+#define EXT4_FEATURE_INCOMPAT_INODE64		0x20000
+
 
 #define EXT4_FEATURE_COMPAT_FUNCS(name, ver, flagname) \
 static inline int ext2fs_has_feature_##name(struct ext2_super_block *sb) \
@@ -918,13 +926,16 @@  EXT4_FEATURE_INCOMPAT_FUNCS(csum_seed,		4, CSUM_SEED)
 EXT4_FEATURE_INCOMPAT_FUNCS(largedir,		4, LARGEDIR)
 EXT4_FEATURE_INCOMPAT_FUNCS(inline_data,	4, INLINE_DATA)
 EXT4_FEATURE_INCOMPAT_FUNCS(encrypt,		4, ENCRYPT)
+EXT4_FEATURE_INCOMPAT_FUNCS(inode64,		4, INODE64)
+
 
 #define EXT2_FEATURE_COMPAT_SUPP	0
 #define EXT2_FEATURE_INCOMPAT_SUPP    (EXT2_FEATURE_INCOMPAT_FILETYPE| \
 				       EXT4_FEATURE_INCOMPAT_MMP| \
 				       EXT4_FEATURE_INCOMPAT_LARGEDIR| \
 				       EXT4_FEATURE_INCOMPAT_EA_INODE| \
-				       EXT4_FEATURE_INCOMPAT_DIRDATA)
+				       EXT4_FEATURE_INCOMPAT_DIRDATA \
+				       EXT4_FEATURE_INCOMPAT_INODE64)
 #define EXT2_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
 					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
 					 EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 2a546acc..246cd52a 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -74,6 +74,7 @@  extern "C" {
 #endif /* EXT2_FLAT_INCLUDES */
 
 typedef __u32 __bitwise		ext2_ino_t;
+typedef __u64 __bitwise		ext2_ino64_t;
 typedef __u32 __bitwise		blk_t;
 typedef __u64 __bitwise		blk64_t;
 typedef __u32 __bitwise		dgrp_t;
@@ -601,6 +602,7 @@  typedef struct ext2_icount *ext2_icount_t;
 					 EXT4_FEATURE_INCOMPAT_FLEX_BG|\
 					 EXT4_FEATURE_INCOMPAT_EA_INODE|\
 					 EXT4_FEATURE_INCOMPAT_DIRDATA|\
+					 EXT4_FEATURE_INCOMPAT_INODE64|\
 					 EXT4_LIB_INCOMPAT_MMP|\
 					 EXT4_FEATURE_INCOMPAT_64BIT|\
 					 EXT4_FEATURE_INCOMPAT_INLINE_DATA|\
@@ -2062,14 +2064,18 @@  ext2fs_const_inode(const struct ext2_inode_large * large_inode)
  * ext2fs_get_last_error_ino
  */
 #define EXT2FS_SB_VALUES(name) \
-static inline unsigned long ext2fs_get_##name(struct ext2_super_block *sb) \
+static inline ext2_ino64_t ext2fs_get_##name(struct ext2_super_block *sb) \
 { \
-	unsigned long value = sb->s_##name; \
-	return value; \
+	ext2_ino64_t inodes_count = sb->s_##name; \
+	if (ext2fs_has_feature_inode64(sb)) \
+		inodes_count |= (ext2_ino64_t)sb->s_##name##_hi << 32; \
+	return inodes_count; \
 } \
 static inline void ext2fs_set_##name(struct ext2_super_block *sb,\
-				   unsigned long val) \
+				   ext2_ino64_t val) \
 { \
+	if (ext2fs_has_feature_inode64(sb)) \
+		sb->s_##name##_hi = (__u32)(val >> 32); \
 	sb->s_##name = val; \
 }
 EXT2FS_SB_VALUES(inodes_count)
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index c1da8509..4d98286e 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -83,6 +83,12 @@  void ext2fs_swap_super(struct ext2_super_block * sb)
 	sb->s_usr_quota_inum = ext2fs_swab32(sb->s_usr_quota_inum);
 	sb->s_grp_quota_inum = ext2fs_swab32(sb->s_grp_quota_inum);
 	sb->s_overhead_blocks = ext2fs_swab32(sb->s_overhead_blocks);
+	sb->s_inodes_count_hi = ext2fs_swab32(sb->s_inodes_count_hi);
+	sb->s_free_inodes_count_hi = ext2fs_swab32(sb->s_free_inodes_count_hi);
+	sb->s_last_orphan_hi = ext2fs_swab32(sb->s_last_orphan_hi);
+	sb->s_prj_quota_inum_hi = ext2fs_swab32(sb->s_prj_quota_inum_hi);
+	sb->s_first_error_ino_hi = ext2fs_swab32(sb->s_first_error_ino_hi);
+	sb->s_last_error_ino_hi = ext2fs_swab32(sb->s_last_error_ino_hi);
 	sb->s_checksum = ext2fs_swab32(sb->s_checksum);
 
 	for (i=0; i < 4; i++)
diff --git a/lib/ext2fs/tst_super_size.c b/lib/ext2fs/tst_super_size.c
index 0adac411..137081dd 100644
--- a/lib/ext2fs/tst_super_size.c
+++ b/lib/ext2fs/tst_super_size.c
@@ -142,7 +142,13 @@  int main(int argc, char **argv)
 	check_field(s_lpf_ino, 4);
 	check_field(s_prj_quota_inum, 4);
 	check_field(s_checksum_seed, 4);
-	check_field(s_reserved, 98 * 4);
+	check_field(s_inodes_count_hi, 4);
+	check_field(s_free_inodes_count_hi, 4);
+	check_field(s_prj_quota_inum_hi, 4);
+	check_field(s_last_orphan_hi, 4);
+	check_field(s_first_error_ino_hi, 4);
+	check_field(s_last_error_ino_hi, 4);
+	check_field(s_reserved, 92 * 4);
 	check_field(s_checksum, 4);
 	do_field("Superblock end", 0, 0, cur_offset, 1024);
 #endif
diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c
index 24023400..47f48bdf 100644
--- a/misc/fuse2fs.c
+++ b/misc/fuse2fs.c
@@ -299,7 +299,7 @@  out:
 #define FUSE2FS_FILE_MAGIC	(0xEF53DEAFUL)
 struct fuse2fs_file_handle {
 	unsigned long magic;
-	ext2_ino_t ino;
+	ext2_ino64_t ino;
 	int open_flags;
 };
 
@@ -328,7 +328,7 @@  struct fuse2fs {
 	return translate_error(global_fs, 0, EXT2_ET_BAD_MAGIC); \
 } while (0)
 
-static int __translate_error(ext2_filsys fs, errcode_t err, ext2_ino_t ino,
+static int __translate_error(ext2_filsys fs, errcode_t err, ext2_ino64_t ino,
 			     const char *file, int line);
 #define translate_error(fs, ino, err) __translate_error((fs), (err), (ino), \
 			__FILE__, __LINE__)
@@ -3866,7 +3866,7 @@  out:
 	return ret;
 }
 
-static int __translate_error(ext2_filsys fs, errcode_t err, ext2_ino_t ino,
+static int __translate_error(ext2_filsys fs, errcode_t err, ext2_ino64_t ino,
 			     const char *file, int line)
 {
 	struct timespec now;
@@ -3940,7 +3940,7 @@  no_translation:
 		return ret;
 
 	if (ino)
-		fprintf(ff->err_fp, "FUSE2FS (%s): %s (inode #%d) at %s:%d.\n",
+		fprintf(ff->err_fp, "FUSE2FS (%s): %s (inode #%ld) at %s:%d.\n",
 			fs->device_name ? fs->device_name : "???",
 			error_message(err), ino, file, line);
 	else
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 8edb7546..2878aadc 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -1089,7 +1089,8 @@  static __u32 ok_features[3] = {
 		EXT4_FEATURE_INCOMPAT_INLINE_DATA|
 		EXT4_FEATURE_INCOMPAT_ENCRYPT |
 		EXT4_FEATURE_INCOMPAT_CSUM_SEED |
-		EXT4_FEATURE_INCOMPAT_LARGEDIR,
+		EXT4_FEATURE_INCOMPAT_LARGEDIR|
+		EXT4_FEATURE_INCOMPAT_INODE64,
 	/* R/O compat */
 	EXT2_FEATURE_RO_COMPAT_LARGE_FILE|
 		EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
@@ -2457,13 +2458,15 @@  profile_error:
 	if (num_inodes == 0) {
 		unsigned long long n;
 		n = ext2fs_blocks_count(&fs_param) * blocksize / inode_ratio;
-		if (n > MAX_32_NUM) {
-			if (ext2fs_has_feature_64bit(&fs_param))
+		if (n > MAX_32_NUM && !ext2fs_has_feature_inode64(&fs_param)) {
+			if (ext2fs_has_feature_64bit(&fs_param)) {
 				num_inodes = MAX_32_NUM;
+			}
 			else {
 				com_err(program_name, 0,
-					_("too many inodes (%llu), raise "
-					  "inode ratio?"), n);
+					_("too many inodes (%llu), raise inode"
+					"ratio or enable dirdata and inode64?"),
+					num_inodes);
 				exit(1);
 			}
 		}
@@ -2476,10 +2479,13 @@  profile_error:
 	/*
 	 * Calculate number of inodes based on the inode ratio
 	 */
-	fs_param.s_inodes_count = num_inodes ? num_inodes :
-		(ext2fs_blocks_count(&fs_param) * blocksize) / inode_ratio;
+	if (num_inodes == 0)
+		num_inodes = (ext2fs_blocks_count(&fs_param) * blocksize) /
+			inode_ratio;
+
+	ext2fs_set_inodes_count(&fs_param, num_inodes);
 
-	if ((((unsigned long long)fs_param.s_inodes_count) *
+	if ((ext2fs_get_inodes_count(&fs_param) *
 	     (inode_size ? inode_size : EXT2_GOOD_OLD_INODE_SIZE)) >=
 	    ((ext2fs_blocks_count(&fs_param)) *
 	     EXT2_BLOCK_SIZE(&fs_param))) {
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 421c1d98..3538ab9c 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -161,7 +161,8 @@  static __u32 ok_features[3] = {
 		EXT4_FEATURE_INCOMPAT_64BIT |
 		EXT4_FEATURE_INCOMPAT_ENCRYPT |
 		EXT4_FEATURE_INCOMPAT_CSUM_SEED |
-		EXT4_FEATURE_INCOMPAT_LARGEDIR,
+		EXT4_FEATURE_INCOMPAT_LARGEDIR |
+		EXT4_FEATURE_INCOMPAT_INODE64,
 	/* R/O compat */
 	EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
 		EXT4_FEATURE_RO_COMPAT_HUGE_FILE|