Index: linux-stage/fs/ext4/dir.c =================================================================== --- linux-stage.orig/fs/ext4/dir.c 2011-04-19 01:02:34.000000000 +0800 +++ linux-stage/fs/ext4/dir.c 2011-04-19 01:24:36.000000000 +0800 @@ -242,22 +242,50 @@ return ret; } +static inline int is_32bit_api(void) +{ +#ifdef HAVE_IS_COMPAT_TASK + return is_compat_task(); +#else + return (BITS_PER_LONG == 32); +#endif +} + /* * 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. + * Up layer (OSD) should specify O_32BITHASH or O_64BITHASH explicitly. + * On the other hand, we allow ldiskfs to be mounted directly on both 32-bit + * and 64-bit nodes, under such case, neither O_32BITHASH nor O_64BITHASH is + * specified. */ -#define hash2pos(major, minor) (major >> 1) -#define pos2maj_hash(pos) ((pos << 1) & 0xffffffff) -#define pos2min_hash(pos) (0) +static inline loff_t hash2pos(struct file *filp, __u32 major, __u32 minor) +{ + if ((filp->f_flags & O_32BITHASH) || + (!(filp->f_flags & O_64BITHASH) && is_32bit_api())) + return (major >> 1); + else + return (((__u64)(major >> 1) << 32) | (__u64)minor); +} + +static inline __u32 pos2maj_hash(struct file *filp, loff_t pos) +{ + if ((filp->f_flags & O_32BITHASH) || + (!(filp->f_flags & O_64BITHASH) && is_32bit_api())) + return ((pos << 1) & 0xffffffff); + else + return (((pos >> 32) << 1) & 0xffffffff); +} + +static inline __u32 pos2min_hash(struct file *filp, loff_t pos) +{ + if ((filp->f_flags & O_32BITHASH) || + (!(filp->f_flags & O_64BITHASH) && is_32bit_api())) + return (0); + else + return (pos & 0xffffffff); +} /* * This structure holds the nodes of the red-black tree used to store @@ -318,15 +346,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 file *filp, 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(filp, pos); + p->curr_minor_hash = pos2min_hash(filp, pos); return p; } @@ -422,7 +451,7 @@ "null fname?!?\n"); return 0; } - curr_pos = hash2pos(fname->hash, fname->minor_hash); + curr_pos = hash2pos(filp, fname->hash, fname->minor_hash); while (fname) { error = filldir(dirent, fname->name, fname->name_len, curr_pos, @@ -447,7 +476,7 @@ int ret; if (!info) { - info = ext4_htree_create_dir_info(filp->f_pos); + info = ext4_htree_create_dir_info(filp, filp->f_pos); if (!info) return -ENOMEM; filp->private_data = info; @@ -461,8 +490,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(filp, filp->f_pos); + info->curr_minor_hash = pos2min_hash(filp, filp->f_pos); } /* Index: linux-stage/fs/ext4/ext4.h =================================================================== --- linux-stage.orig/fs/ext4/ext4.h 2011-04-19 01:02:34.000000000 +0800 +++ linux-stage/fs/ext4/ext4.h 2011-04-19 01:02:34.000000000 +0800 @@ -55,6 +55,14 @@ #define ext4_debug(f, a...) do {} while (0) #endif +#ifndef O_32BITHASH +# define O_32BITHASH 0x10000000 +#endif + +#ifndef O_64BITHASH +# define O_64BITHASH 0x20000000 +#endif + #define HAVE_DISK_INODE_VERSION /* data type for block offset of block group */