Index: linux-stage/fs/ext4/dir.c =================================================================== --- linux-stage.orig/fs/ext4/dir.c 2011-03-31 10:35:49.000000000 +0800 +++ linux-stage/fs/ext4/dir.c 2011-04-01 09:33:58.706267179 +0800 @@ -249,19 +249,32 @@ /* * These functions convert from the major/minor hash to an f_pos * value. - * - * Currently we only use major hash numer. This is unfortunate, but - * on 32-bit machines, the same VFS interface is used for lseek and - * llseek, so if we use the 64 bit offset, then the 32-bit versions of - * lseek/telldir/seekdir will blow out spectacularly, and from within - * the ext2 low-level routine, we don't know if we're being called by - * a 64-bit version of the system call or the 32-bit version of the - * system call. Worse yet, NFSv2 only allows for a 32-bit readdir - * cookie. Sigh. + * Whether 64-bit or 32-bit hash value is exported as file pos is + * controlled by "64bithash" mount option. */ -#define hash2pos(major, minor) (major >> 1) -#define pos2maj_hash(pos) ((pos << 1) & 0xffffffff) -#define pos2min_hash(pos) (0) +static inline loff_t hash2pos(struct super_block *sb, __u32 major, __u32 minor) +{ + if (test_opt(sb, 64BITHASH)) + return (((__u64)(major >> 1) << 32) | (__u64)minor); + else + return (major >> 1); +} + +static inline __u32 pos2maj_hash(struct super_block *sb, loff_t pos) +{ + if (test_opt(sb, 64BITHASH)) + return (((pos >> 32) << 1) & 0xffffffff); + else + return ((pos << 1) & 0xffffffff); +} + +static inline __u32 pos2min_hash(struct super_block *sb, loff_t pos) +{ + if (test_opt(sb, 64BITHASH)) + return (pos & 0xffffffff); + else + return (0); +} /* * This structure holds the nodes of the red-black tree used to store @@ -322,15 +335,16 @@ } -static struct dir_private_info *ext4_htree_create_dir_info(loff_t pos) +static struct dir_private_info *ext4_htree_create_dir_info( + struct super_block *sb, loff_t pos) { struct dir_private_info *p; p = kzalloc(sizeof(struct dir_private_info), GFP_KERNEL); if (!p) return NULL; - p->curr_hash = pos2maj_hash(pos); - p->curr_minor_hash = pos2min_hash(pos); + p->curr_hash = pos2maj_hash(sb, pos); + p->curr_minor_hash = pos2min_hash(sb, pos); return p; } @@ -426,7 +440,7 @@ "null fname?!?\n"); return 0; } - curr_pos = hash2pos(fname->hash, fname->minor_hash); + curr_pos = hash2pos(sb, fname->hash, fname->minor_hash); while (fname) { error = filldir(dirent, fname->name, fname->name_len, curr_pos, @@ -451,7 +465,7 @@ int ret; if (!info) { - info = ext4_htree_create_dir_info(filp->f_pos); + info = ext4_htree_create_dir_info(inode->i_sb, filp->f_pos); if (!info) return -ENOMEM; filp->private_data = info; @@ -465,8 +479,8 @@ free_rb_tree_fname(&info->root); info->curr_node = NULL; info->extra_fname = NULL; - info->curr_hash = pos2maj_hash(filp->f_pos); - info->curr_minor_hash = pos2min_hash(filp->f_pos); + info->curr_hash = pos2maj_hash(inode->i_sb, filp->f_pos); + info->curr_minor_hash = pos2min_hash(inode->i_sb, filp->f_pos); } /* Index: linux-stage/fs/ext4/ext4.h =================================================================== --- linux-stage.orig/fs/ext4/ext4.h 2011-03-31 10:35:50.000000000 +0800 +++ linux-stage/fs/ext4/ext4.h 2011-04-01 09:33:58.740267284 +0800 @@ -785,6 +785,7 @@ #define EXT4_MOUNT_JOURNAL_CHECKSUM 0x800000 /* Journal checksums */ #define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT 0x1000000 /* Journal Async Commit */ #define EXT4_MOUNT_I_VERSION 0x2000000 /* i_version support */ +#define EXT4_MOUNT_64BITHASH 0x4000000 /* export 64-bit name hash */ #define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */ #define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */ #define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */ Index: linux-stage/fs/ext4/super.c =================================================================== --- linux-stage.orig/fs/ext4/super.c 2011-03-31 10:35:50.000000000 +0800 +++ linux-stage/fs/ext4/super.c 2011-04-01 09:35:00.251453404 +0800 @@ -1540,7 +1540,7 @@ Opt_inode_readahead_blks, Opt_journal_ioprio, Opt_discard, Opt_nodiscard, Opt_mballoc, Opt_bigendian_extents, Opt_force_over_16tb, - Opt_no_mbcache, + Opt_no_mbcache, Opt_64bithash, Opt_extents, Opt_noextents, }; @@ -1614,6 +1614,7 @@ {Opt_discard, "discard"}, {Opt_nodiscard, "nodiscard"}, {Opt_no_mbcache, "no_mbcache"}, + {Opt_64bithash, "64bithash"}, {Opt_extents, "extents"}, {Opt_noextents, "noextents"}, {Opt_err, NULL}, @@ -2092,6 +2093,9 @@ case Opt_no_mbcache: set_opt(sbi->s_mount_opt, NO_MBCACHE); break; + case Opt_64bithash: + set_opt(sbi->s_mount_opt, 64BITHASH); + break; default: ext4_msg(sb, KERN_ERR, "Unrecognized mount option \"%s\" "