diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl
index a85f407..35aeb58 100644
--- a/arch/x86/syscalls/syscall_32.tbl
+++ b/arch/x86/syscalls/syscall_32.tbl
@@ -358,4 +358,5 @@
 349	i386	kcmp			sys_kcmp
 350	i386	finit_module		sys_finit_module
 351	i386	xstat			sys_xstat
-352	i386	fxstat			sys_fxstat
\ No newline at end of file
+352	i386	fxstat			sys_fxstat
+353	i386	xgetdents		sys_xgetdents
diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl
index d9bac28..8b68501 100644
--- a/arch/x86/syscalls/syscall_64.tbl
+++ b/arch/x86/syscalls/syscall_64.tbl
@@ -322,6 +322,7 @@
 313	common	finit_module		sys_finit_module
 314	common 	xstat			sys_xstat
 315	common 	fxstat			sys_fxstat
+316	common	xgetdents		sys_xgetdents
 
 #
 # x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c
index 7179478..f4697a6 100644
--- a/fs/gfs2/dir.c
+++ b/fs/gfs2/dir.c
@@ -78,9 +78,6 @@
 
 #define MAX_RA_BLOCKS 32 /* max read-ahead blocks */
 
-#define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1)
-#define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1))
-
 struct qstr gfs2_qdot __read_mostly;
 struct qstr gfs2_qdotdot __read_mostly;
 
@@ -1159,17 +1156,13 @@ out_kfree:
  *   lt: returns -1
  *   eq: returns 0
  */
-
-static int compare_dents(const void *a, const void *b)
+int compare_dents_i(const struct gfs2_dirent *dent_a,
+		  const struct gfs2_dirent *dent_b)
 {
-	const struct gfs2_dirent *dent_a, *dent_b;
 	u32 hash_a, hash_b;
 	int ret = 0;
 
-	dent_a = *(const struct gfs2_dirent **)a;
 	hash_a = be32_to_cpu(dent_a->de_hash);
-
-	dent_b = *(const struct gfs2_dirent **)b;
 	hash_b = be32_to_cpu(dent_b->de_hash);
 
 	if (hash_a > hash_b)
@@ -1191,28 +1184,43 @@ static int compare_dents(const void *a, const void *b)
 	return ret;
 }
 
+static int compare_dents(const void *a, const void *b)
+{
+	return compare_dents_i(*(const struct gfs2_dirent **)a,
+			       *(const struct gfs2_dirent **)b);
+}
+
+int process_dent(const struct gfs2_dirent *dent, loff_t off, void *opaque,
+		 filldir_t filler)
+{
+	return filler(opaque, (const char *)(dent + 1),
+		       be16_to_cpu(dent->de_name_len),
+		       off, be64_to_cpu(dent->de_inum.no_addr),
+		       be16_to_cpu(dent->de_type));
+}
+
 /**
- * do_filldir_main - read out directory entries
- * @dip: The GFS2 inode
- * @offset: The offset in the file to read from
- * @opaque: opaque data to pass to filldir
- * @filldir: The function to pass entries to
- * @darr: an array of struct gfs2_dirent pointers to read
+ * foreach_dent - run through list of dents and call pd_fn, keeping in
+ *                mind hash collisions
+ * @offset : The offset in the file to read from
+ * @opaque : opaque data to pass to pd_fn
+ * @filldir: The filler function to psas to pd_fn
+ * @darr   : an array of struct gfs2_dirent pointers to read
  * @entries: the number of entries in darr
- * @copied: pointer to int that's non-zero if a entry has been copied out
+ * @copied : pointer to int that's non-zero if a entry has been copied out
+ * @pd_fn  : The function to process a dentry
  *
  * Jump through some hoops to make sure that if there are hash collsions,
  * they are read out at the beginning of a buffer.  We want to minimize
  * the possibility that they will fall into different readdir buffers or
  * that someone will want to seek to that location.
  *
- * Returns: errno, >0 on exception from filldir
+ * Returns: errno, >0 on exception from pd_fn
  */
 
-static int do_filldir_main(struct gfs2_inode *dip, u64 *offset,
-			   void *opaque, filldir_t filldir,
-			   const struct gfs2_dirent **darr, u32 entries,
-			   int *copied)
+int foreach_dent(u64 *offset, void *opaque, filldir_t filldir,
+		 const struct gfs2_dirent **darr, u32 entries,
+		 int *copied, process_dent_t pd_fn)
 {
 	const struct gfs2_dirent *dent, *dent_next;
 	u64 off, off_next;
@@ -1220,8 +1228,6 @@ static int do_filldir_main(struct gfs2_inode *dip, u64 *offset,
 	int run = 0;
 	int error = 0;
 
-	sort(darr, entries, sizeof(struct gfs2_dirent *), compare_dents, NULL);
-
 	dent_next = darr[0];
 	off_next = be32_to_cpu(dent_next->de_hash);
 	off_next = gfs2_disk_hash2offset(off_next);
@@ -1250,26 +1256,48 @@ static int do_filldir_main(struct gfs2_inode *dip, u64 *offset,
 				continue;
 			*offset = off;
 		}
-
-		error = filldir(opaque, (const char *)(dent + 1),
-				be16_to_cpu(dent->de_name_len),
-				off, be64_to_cpu(dent->de_inum.no_addr),
-				be16_to_cpu(dent->de_type));
+		error = pd_fn(dent, off, opaque, filldir);
 		if (error)
 			return 1;
 
 		*copied = 1;
 	}
-
 	/* Increment the *offset by one, so the next time we come into the
 	   do_filldir fxn, we get the next entry instead of the last one in the
 	   current leaf */
 
 	(*offset)++;
-
 	return 0;
 }
 
+/**
+ * do_filldir_main - read out directory entries
+ * @dip: The GFS2 inode
+ * @offset: The offset in the file to read from
+ * @opaque: opaque data to pass to filldir
+ * @filldir: The function to pass entries to, if NULL call xreaddir_collect_dents
+ * @darr: an array of struct gfs2_dirent pointers to read
+ * @entries: the number of entries in darr
+ * @copied: pointer to int that's non-zero if a entry has been copied out
+ *
+ * Returns: 0 on success and non-zero on exception
+ */
+
+static int do_filldir_main(struct gfs2_inode *dip, u64 *offset,
+			   void *opaque, filldir_t filldir,
+			   const struct gfs2_dirent **darr, u32 entries,
+			   int *copied)
+{
+	sort(darr, entries, sizeof(struct gfs2_dirent *), compare_dents, NULL);
+
+	/* 
+	 * If filldir is NULL, we got here through xreaddir and need to
+	 * call the appropriate handler
+	 */
+	return foreach_dent(offset, opaque, filldir, darr, entries, copied, 
+			    filldir ? process_dent : gfs2_xrdir_collect_dents);
+}
+
 static void *gfs2_alloc_sort_buffer(unsigned size)
 {
 	void *ptr = NULL;
diff --git a/fs/gfs2/dir.h b/fs/gfs2/dir.h
index 98c960b..3bb919b 100644
--- a/fs/gfs2/dir.h
+++ b/fs/gfs2/dir.h
@@ -12,11 +12,71 @@
 
 #include <linux/dcache.h>
 #include <linux/crc32.h>
+#include "util.h"
+
+#define gfs2_disk_hash2offset(h) (((u64)(h)) >> 1)
+#define gfs2_dir_offset2hash(p) ((u32)(((u64)(p)) << 1))
 
 struct inode;
 struct gfs2_inode;
 struct gfs2_inum;
 
+struct gfs2_xd_xattr {
+	unsigned int   xa_reclen;
+	void          *xa_vb_value_ptr;
+	unsigned long  xa_value_len;
+	unsigned int   xa_keylen;
+	char           __pad[7];
+	char           xa_keyname[1];
+};
+
+struct gfs2_xdirent {
+	u64                      x_ino;
+	char                     x_type;
+	struct kstat             x_kstat;
+	unsigned int             x_xattr_count;
+	void                    *x_vb_xattr_arr_ptr;
+	unsigned int             x_namelen;
+	char                     x_name[1];
+};
+
+/*
+ * readdir ctx
+ */
+struct gfs2_rddir_ctx {
+	unsigned int          rc_flags;
+	unsigned long         rc_count;
+	void                 *rc_next_dent;
+	void                **rc_vb_dptrs;
+	struct vbuf           rc_dirents;
+	struct vbuf           rc_xattr_keys;
+	struct vbuf           rc_xattr_values;
+};
+
+struct xreaddir_ctx {
+	int                   xc_err;
+	unsigned int          xc_entries;
+	unsigned int          xc_bufsize;
+	unsigned int          xc_flags;
+	unsigned int          xc_mask;
+	size_t                xc_ubufsize;
+	unsigned int          xc_ubuf_ent_cap;
+	struct gfs2_inode    *xc_dip;
+	char                 *xc_buf;
+	char                 *xc_buftop;
+	struct xdirent      **xc_xdents;
+	void                 *xc_opaque;
+};
+
+typedef int (*process_dent_t)(const struct gfs2_dirent *, loff_t, void *, filldir_t);
+
+extern int compare_dents_i(const struct gfs2_dirent *dent_a,
+			   const struct gfs2_dirent *dent_b);
+extern int xreaddir_collect_dents(void *opaque, u64 *offset, 
+		      const struct gfs2_dirent **darr, u32 entries);
+extern int foreach_dent(u64 *offset, void *opaque, filldir_t filldir,
+			const struct gfs2_dirent **darr, u32 entries,
+			int *copied, process_dent_t pd_fn);
 extern struct inode *gfs2_dir_search(struct inode *dir,
 				     const struct qstr *filename);
 extern int gfs2_dir_check(struct inode *dir, const struct qstr *filename,
@@ -66,4 +126,7 @@ static inline void gfs2_qstr2dirent(const struct qstr *name, u16 reclen, struct
 extern struct qstr gfs2_qdot;
 extern struct qstr gfs2_qdotdot;
 
+extern int gfs2_xrdir_collect_dents(const struct gfs2_dirent *dent, loff_t off,
+				       void *opaque, filldir_t filldir);
+
 #endif /* __DIR_DOT_H__ */
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 06b7092..558a312 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -16,6 +16,9 @@
 #include <linux/blkdev.h>
 #include <linux/mm.h>
 #include <linux/mount.h>
+#include <linux/stat.h>
+#include <linux/sort.h>
+#include <linux/xattr.h>
 #include <linux/fs.h>
 #include <linux/gfs2_ondisk.h>
 #include <linux/falloc.h>
@@ -39,6 +42,7 @@
 #include "rgrp.h"
 #include "trans.h"
 #include "util.h"
+#include "xattr.h"
 
 /**
  * gfs2_llseek - seek to a location in a file
@@ -113,6 +117,352 @@ static int gfs2_readdir(struct file *file, void *dirent, filldir_t filldir)
 	return error;
 }
 
+static int gfs2_dirent_dot_or_dotdot(const struct gfs2_dirent *dent)
+{
+	const char *name = (char *)(dent + 1);
+
+	if (be16_to_cpu(dent->de_type) == DT_DIR) {
+		if (be16_to_cpu(dent->de_name_len) == 1 && name[0] == '.')
+			return 1;
+		if (be16_to_cpu(dent->de_name_len) == 2 &&
+		    strncmp(name, "..", 2) == 0)
+			return 1;
+	}
+	return 0;   
+}
+
+/*
+ * Add a gfs2_dirent to the xreaddir context
+ */
+int gfs2_xrdir_collect_dents(const struct gfs2_dirent *dent, loff_t off,
+				       void *opaque, filldir_t filldir)
+{
+	struct gfs2_rddir_ctx *rc = opaque;
+	struct gfs2_xdirent *x;
+	u64 x_ino;
+	char x_type;
+	unsigned int x_xattr_count, x_namelen;
+	const void *nullptr = NULL;
+
+	if (gfs2_dirent_dot_or_dotdot(dent))
+		goto out;
+
+	if (rc->rc_next_dent == NULL)
+		rc->rc_next_dent = rc->rc_dirents.v_ptr;
+
+	/* Copy the dirent contents */
+	x_ino = be64_to_cpu(dent->de_inum.no_addr);
+	x_type = be16_to_cpu(dent->de_type);
+	x_xattr_count = 0;
+	x_namelen = be16_to_cpu(dent->de_name_len);
+
+	x = rc->rc_next_dent;
+
+	vp_write(&rc->rc_dirents, &x->x_ino, &x_ino, sizeof(x->x_ino));
+	vp_write(&rc->rc_dirents, &x->x_type, &x_type, sizeof(x->x_type));
+	vp_write(&rc->rc_dirents, &x->x_xattr_count, &x_xattr_count, sizeof(x->x_xattr_count));
+	vp_write(&rc->rc_dirents, &x->x_vb_xattr_arr_ptr, &nullptr, sizeof(x->x_vb_xattr_arr_ptr));
+	vp_write(&rc->rc_dirents, &x->x_namelen, &x_namelen, sizeof(x->x_namelen));
+	vp_write(&rc->rc_dirents, &x->x_name, (char*)(dent + 1), x_namelen);
+
+	rc->rc_next_dent = x->x_name + x_namelen;
+	rc->rc_count++;
+out:
+	return 0;
+}
+
+static int gfs2_xrdir_create_dptrs(struct gfs2_rddir_ctx *rc)
+{
+	int error = -ENOMEM, i;
+	unsigned int namelen;
+	struct gfs2_xdirent *x = NULL;
+
+	BUG_ON(rc->rc_vb_dptrs || rc->rc_count == 0);
+
+	/* allocate the dirent pointers */
+	rc->rc_vb_dptrs = kmalloc(sizeof(struct gfs2_xdirent *) * rc->rc_count, GFP_KERNEL);
+	if (rc->rc_vb_dptrs == NULL)
+		goto out;
+
+	for (i=0; i<rc->rc_count; i++) {
+		if (!x)
+			x = rc->rc_dirents.v_ptr;
+		rc->rc_vb_dptrs[i] = x;
+		vp_read(&rc->rc_dirents, &namelen, &x->x_namelen, sizeof(x->x_namelen));
+		/* reclen is sizeof(struct gfs2_xdirent) + x_namelen.
+		 * see struct gfs2_xdirent for more info */
+		x = (void *)x->x_name + namelen;
+	}
+out:
+	return error;
+}
+
+static int gfs2_xrdir_collect_xstat(struct gfs2_rddir_ctx *rc, struct gfs2_xdirent *x,
+				    struct gfs2_inode *ip)
+{
+	struct kstat st;
+
+	gfs2_getattr_i(ip, &st);
+	vp_write(&rc->rc_dirents, &x->x_kstat, &st, sizeof(struct kstat));
+
+	return 0;
+}
+
+static int gfs2_xrdir_collect_extra_info(struct gfs2_rddir_ctx *rc, struct gfs2_inode *dip)
+{
+	int error = 0, i;
+
+	/* First sort the dptrs in block order */
+	/* Don't know how to sort yet using vector pages */
+
+	/* Lookup all the inodes for stat info */
+	for (i=0; i<rc->rc_count; i++) {
+		struct gfs2_xdirent *x_vb_p = rc->rc_vb_dptrs[i];
+		u64 ino;
+		struct inode *inode;
+		struct gfs2_inode *ip;
+		struct gfs2_holder gh;
+
+		vp_read(&rc->rc_dirents, &ino, &x_vb_p->x_ino, sizeof(x_vb_p->x_ino));
+
+		inode = gfs2_lookup_by_inum(GFS2_SB(&dip->i_inode), ino, NULL, GFS2_BLKST_DINODE);
+		if (IS_ERR(inode)) {
+			error = -1;
+			break;
+		}
+		ip = GFS2_I(inode);
+		error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
+		if (error) {
+			iput(inode);
+			break;
+		}
+
+		gfs2_xrdir_collect_xstat(rc, x_vb_p, ip);
+		if (ip->i_eattr)
+			error = gfs2_xrdir_collect_xattrs(rc, x_vb_p, ip, XSTAT_ALL_XATTRS);
+
+		gfs2_glock_dq_uninit(&gh);
+		iput(inode);
+	}
+	return error;
+}
+
+static size_t gfs2_xrdir_to_user(struct gfs2_rddir_ctx *rc, void __user *buf, size_t count)
+{
+	size_t ret = -EINVAL, bytes = 0, bytes_bef = 0;
+	int i, skip = 1, written = 0, attrcount;
+	struct gfs2_xdirent x, *x_vb_p;
+	struct gfs2_xd_xattr xdx, *xdx_vb_p;
+	struct linux_xdirent __user *lxd = buf;
+	struct xdirent_blob __user *xblob;
+	struct xdirent_xattr __user *xx;
+	char *tempbuf = NULL;
+
+	tempbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!tempbuf) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	for (i=0; i<rc->rc_count; i++) {
+		if (skip && (rc->rc_vb_dptrs[i] != rc->rc_next_dent))
+			continue;
+		skip = 0;
+		x_vb_p = rc->rc_vb_dptrs[i];
+
+		vp_read(&rc->rc_dirents, &x, x_vb_p, sizeof(struct gfs2_xdirent));
+
+		if ((count - bytes) < sizeof(struct linux_xdirent))
+			goto overflow;
+
+		if (__put_user(x.x_ino, &lxd->xd_ino))
+			goto out;
+		if (__put_user(x.x_type, &lxd->xd_type))
+			goto out;
+		if (__put_user(0, &lxd->xd_off))
+			goto out;
+
+		ret = xstat_set_result(&x.x_kstat, &lxd->xd_stat);
+		if (ret)
+			goto out;
+
+		xblob = &lxd->xd_blob;
+		ret = -EINVAL;
+
+		if (__put_user(x.x_xattr_count, &xblob->xb_xattr_count))
+			goto out;
+
+		/* copied all the fixed size fields */
+		bytes += sizeof(struct linux_xdirent);
+
+		/* copy all the variable length fields */
+		if ((count - bytes) < x.x_namelen)
+			goto overflow;
+
+		vp_read(&rc->rc_dirents, tempbuf, x_vb_p->x_name, x.x_namelen);
+
+		if (copy_to_user(xblob->xb_blob, tempbuf, x.x_namelen))
+			goto out;
+		if (__put_user(0, xblob->xb_blob + x.x_namelen))
+			goto out;
+
+		bytes += x.x_namelen;
+
+		xx = (struct xdirent_xattr __user *)(xblob->xb_blob + x.x_namelen + 1);
+		attrcount = 0;
+		xdx_vb_p = x.x_vb_xattr_arr_ptr;
+
+		while (attrcount < x.x_xattr_count) {
+			vp_read(&rc->rc_xattr_keys, &xdx, xdx_vb_p, sizeof(struct gfs2_xd_xattr));
+
+			if ((count - bytes) < 
+			    (sizeof(struct xdirent_xattr) + xdx.xa_keylen + xdx.xa_value_len))
+				goto overflow;
+
+			if (__put_user(xdx.xa_value_len, &xx->xa_value_len))
+				goto out;
+
+			vp_read(&rc->rc_xattr_keys, tempbuf, xdx_vb_p->xa_keyname, xdx.xa_keylen);
+
+			if (copy_to_user(xx->xa_name_val, tempbuf, xdx.xa_keylen))
+				goto out;
+			if (__put_user(0, xx->xa_name_val + xdx.xa_keylen))
+				goto out;
+
+			vp_read(&rc->rc_xattr_values, tempbuf, xdx.xa_vb_value_ptr, xdx.xa_value_len);
+
+			if (copy_to_user(xx->xa_name_val + xdx.xa_keylen + 1, tempbuf, xdx.xa_value_len))
+				goto out;
+
+			xx = (struct xdirent_xattr __user *)
+				((char *)xx + sizeof(xx->xa_value_len)
+				 + xdx.xa_keylen + 1 + xdx.xa_value_len);
+			xdx_vb_p = (void*) xdx_vb_p + xdx.xa_reclen;
+
+			bytes += sizeof(struct xdirent_xattr) + xdx.xa_keylen + xdx.xa_value_len;
+			attrcount++;
+		}
+
+		if (__put_user(bytes - bytes_bef, &lxd->xd_reclen))
+			goto out;
+
+		lxd = (void *)lxd + (bytes - bytes_bef);
+		rc->rc_next_dent = rc->rc_vb_dptrs[i+1];
+		written = 1;
+		bytes_bef = bytes;
+	}
+overflow:
+	if (!written && !skip) {
+		ret = -EOVERFLOW;
+		goto out;
+	}
+	ret = bytes_bef;
+out:
+	if (tempbuf)
+		kfree(tempbuf);
+	return ret;
+}
+
+/**
+ * gfs2_xreaddir - GFS2's implementation of xreaddir functionality
+ * @file  : The directory to xreaddir
+ * @flags : flags used by xstat
+ * @mask  : field mask for xstat and xattrs
+ * @count : size of user buffer; needed to estimate # of dents to process
+ * @opaque: vfs state to pass back with the filler fn
+ * @filler: The vfs filler function that will write to the user buffer
+ *
+ * Collect extended information (xstat, xattrs) about the dents in the
+ * given directory and pass them to filler(). When filler() signals that
+ * the user buffer is full, adjust file offset to next unread dent and
+ * return.
+ *
+ * Returns: 0 if successful and -ve errno if an exception occurred.
+ */
+#define RC_FL_ALLOCATED 1
+#define RC_FL_UPDATED   2
+
+static size_t gfs2_xreaddir(struct file *file, unsigned int flags, unsigned int mask,
+			    void __user *buf, size_t count)
+{
+	struct gfs2_rddir_ctx *rc = ((struct gfs2_file *)file->private_data)->f_rdctx;
+	size_t error = 0;
+	loff_t offset;
+
+	if (!(rc->rc_flags & RC_FL_UPDATED)) {
+		struct inode *dir = file->f_mapping->host;
+		struct gfs2_inode *dip = GFS2_I(dir);
+		struct gfs2_holder d_gh;
+
+		gfs2_holder_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh);
+		error = gfs2_glock_nq(&d_gh);
+		if (error) {
+			gfs2_holder_uninit(&d_gh);
+			goto out;
+		}
+
+		error = gfs2_dir_read(dir, &offset, rc, NULL, &file->f_ra);
+		if (error < 0 || rc->rc_count == 0)
+			goto out;
+
+		error = gfs2_xrdir_create_dptrs(rc);
+		if (error < 0)
+			goto out;
+
+		error = gfs2_xrdir_collect_extra_info(rc, dip);
+		if (error < 0)
+			goto out;
+
+		gfs2_glock_dq_uninit(&d_gh);
+		rc->rc_next_dent = rc->rc_vb_dptrs[0];
+		rc->rc_flags |= RC_FL_UPDATED;
+	}
+
+	error = gfs2_xrdir_to_user(rc, buf, count);
+out:
+	return error;
+}
+
+void gfs2_rddir_ctx_uninit(struct gfs2_file *fp)
+{
+	struct gfs2_rddir_ctx *rc;
+
+	if (!fp || !fp->f_rdctx)
+		return;
+
+	rc = fp->f_rdctx;
+	kfree(rc->rc_vb_dptrs);
+	vp_uninit(&rc->rc_xattr_values);
+	vp_uninit(&rc->rc_xattr_keys);
+	vp_uninit(&rc->rc_dirents);
+	kfree(rc);
+	fp->f_rdctx = NULL;
+}
+
+int gfs2_rddir_ctx_init(struct gfs2_file *fp)
+{
+	struct gfs2_rddir_ctx *rc;
+	if (!fp)
+		return -EINVAL;
+
+	BUG_ON(fp->f_rdctx != NULL);
+
+	rc = kzalloc(sizeof(struct gfs2_rddir_ctx), GFP_KERNEL);
+	if (rc == NULL)
+		return -ENOMEM;
+
+	if (vp_init(&rc->rc_dirents, 1) ||
+	    vp_init(&rc->rc_xattr_keys, 1) ||
+	    vp_init(&rc->rc_xattr_values, 1)) {
+		gfs2_rddir_ctx_uninit(fp);
+		return -ENOMEM;
+	}
+	rc->rc_flags |= RC_FL_ALLOCATED;
+	fp->f_rdctx = rc;
+
+	return 0;
+}
+
 /**
  * fsflags_cvt
  * @table: A table of 32 u32 flags
@@ -549,8 +899,13 @@ static int gfs2_open(struct inode *inode, struct file *file)
 		return -ENOMEM;
 
 	mutex_init(&fp->f_fl_mutex);
-
 	gfs2_assert_warn(GFS2_SB(inode), !file->private_data);
+
+	if (S_ISDIR(ip->i_inode.i_mode)) {
+		error = gfs2_rddir_ctx_init(fp);
+		if (error)
+			goto fail;
+	}
 	file->private_data = fp;
 
 	if (S_ISREG(ip->i_inode.i_mode)) {
@@ -590,6 +945,9 @@ static int gfs2_release(struct inode *inode, struct file *file)
 {
 	struct gfs2_inode *ip = GFS2_I(inode);
 
+	if (S_ISDIR(ip->i_inode.i_mode))
+		gfs2_rddir_ctx_uninit((struct gfs2_file *)file->private_data);
+
 	kfree(file->private_data);
 	file->private_data = NULL;
 
@@ -1039,6 +1397,7 @@ const struct file_operations gfs2_file_fops = {
 
 const struct file_operations gfs2_dir_fops = {
 	.readdir	= gfs2_readdir,
+	.xreaddir       = gfs2_xreaddir,
 	.unlocked_ioctl	= gfs2_ioctl,
 	.open		= gfs2_open,
 	.release	= gfs2_release,
@@ -1069,10 +1428,10 @@ const struct file_operations gfs2_file_fops_nolock = {
 
 const struct file_operations gfs2_dir_fops_nolock = {
 	.readdir	= gfs2_readdir,
+	.xreaddir       = gfs2_xreaddir,
 	.unlocked_ioctl	= gfs2_ioctl,
 	.open		= gfs2_open,
 	.release	= gfs2_release,
 	.fsync		= gfs2_fsync,
 	.llseek		= default_llseek,
 };
-
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 19750bc..05e5c39 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -381,6 +381,7 @@ static inline struct gfs2_sbd *GFS2_SB(const struct inode *inode)
 struct gfs2_file {
 	struct mutex f_fl_mutex;
 	struct gfs2_holder f_fl_gh;
+	struct gfs2_rddir_ctx *f_rdctx;
 };
 
 struct gfs2_revoke_replay {
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index db048a8..b4528b8 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -1705,7 +1705,8 @@ static int gfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
 		unlock = 1;
 	}
 
-	generic_fillattr(inode, stat);
+	gfs2_getattr_i(ip, stat);
+
 	if (unlock)
 		gfs2_glock_dq_uninit(&gh);
 
diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h
index c53c747..9559c10 100644
--- a/fs/gfs2/inode.h
+++ b/fs/gfs2/inode.h
@@ -93,6 +93,11 @@ err:
 	return -EIO;
 }
 
+static inline void gfs2_getattr_i(struct gfs2_inode *ip, struct kstat *stat)
+{
+	generic_fillattr(&ip->i_inode, stat);
+}
+
 extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type, 
 				       u64 no_addr, u64 no_formal_ino,
 				       int non_block);
diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c
index f00d7c5..1453eac 100644
--- a/fs/gfs2/util.c
+++ b/fs/gfs2/util.c
@@ -285,3 +285,253 @@ void gfs2_icbit_munge(struct gfs2_sbd *sdp, unsigned char **bitmap,
 		bitmap[c][o] &= ~(1 << b);
 }
 
+/*
+ * Fast vector-of-pages backed buffer
+ */
+
+static int vp_alloc_pages(struct vp_ctx *vpx, int start, int end)
+{
+	int i;
+
+	for (i=start; i<end; i++) {
+		vpx->vp_pages[i] = alloc_page(GFP_KERNEL | GFP_NOFS);
+		if (vpx->vp_pages[i] == NULL)
+			goto free;
+	}
+	return 0;
+free:
+	for (i=start; i<end; i++)
+		if (vpx->vp_pages[i]) {
+			__free_page(vpx->vp_pages[i]);
+			vpx->vp_pages[i] = NULL;
+		}
+	return -ENOMEM;
+}
+
+static void vp_free_pages(struct vp_ctx *vpx)
+{
+	int i;
+
+	for (i=0; i<vpx->vp_size; i++)
+		if (vpx->vp_pages[i]) {
+			__free_page(vpx->vp_pages[i]);
+			vpx->vp_pages[i] = NULL;
+		}
+}
+
+static int vp_extend(struct vp_ctx *vpx, int size)
+{
+	/* first make room for more pointers */
+
+	if (size <= 0)
+		return -EINVAL;
+
+	vpx->vp_pages = krealloc(vpx->vp_pages,
+				 sizeof(struct page *) * (vpx->vp_size + size),
+				 GFP_KERNEL);
+	if (vpx->vp_pages == NULL)
+		goto out;
+
+	/* Zero out the new pointers and allocate pages*/
+	memset(&vpx->vp_pages[vpx->vp_size], 0, sizeof(struct page *) * size);
+	if (vp_alloc_pages(vpx, vpx->vp_size, vpx->vp_size + size))
+		goto out;
+
+	vpx->vp_size += size;
+	return 0;
+out:
+	return -ENOMEM;
+}
+
+int vp_init(struct vbuf *vb, int init_cap)
+{
+	int cap, err = -ENOMEM;
+	struct vp_ctx *vpx;
+
+	init_cap = DIV_ROUND_UP(init_cap, PAGE_SIZE);
+	cap = init_cap > 0 ? init_cap : 1;
+
+	vpx = kmalloc(sizeof(struct vp_ctx), GFP_KERNEL);
+	if (vpx == NULL)
+		goto out;
+
+	vpx->vp_magic = VP_MAGIC;
+	vpx->vp_size = cap;
+	vpx->vp_pages = kzalloc(sizeof(struct page *) * cap, GFP_KERNEL);
+	if (vpx->vp_pages == NULL)
+		goto free;
+
+	if (vp_alloc_pages(vpx, 0, cap))
+		goto free_all;
+
+	vpx->vp_baseptr = vpx->vp_top = page_address(vpx->vp_pages[0]);
+	vb->v_ptr = vpx->vp_baseptr;
+	vb->v_opaque = vpx;
+
+	err = 0;
+	goto out;
+
+free_all:
+	vp_free_pages(vpx);
+	kfree(vpx->vp_pages);
+free:
+	kfree(vpx);
+	vpx = NULL;
+out:
+	return err;
+}
+
+void vp_uninit(struct vbuf *vb)
+{
+	struct vp_ctx *vpx;
+
+	if (!vb || !vb->v_opaque)
+		return;
+
+	vpx = vb->v_opaque;
+	if (vpx->vp_magic != VP_MAGIC)
+		return;
+
+	vp_free_pages(vpx);
+	kfree(vpx->vp_pages);
+	kfree(vpx);
+	vb->v_ptr = vb->v_opaque = NULL;
+}
+
+static int vp_rw_pages(struct vp_ctx *vpx, void *to, void *from, size_t count, int what)
+{
+	int pg_ind, pg_off, bytes, rw = 0;
+
+	while (count > 0) {
+		pg_ind = what == VP_READ ? VP_PAGE_INDEX(vpx, from) : VP_PAGE_INDEX(vpx, to);
+		pg_off = what == VP_READ ? VP_PAGE_OFFSET(vpx, from) : VP_PAGE_OFFSET(vpx, to);
+		bytes = what == VP_READ ? VP_PAGE_BYTES_LEFT(vpx, from) : VP_PAGE_BYTES_LEFT(vpx, to);
+		bytes = bytes > count ? count : bytes;
+
+		if (what == VP_READ)
+			memcpy(to, VP_PAGE_PTR(vpx, pg_ind, pg_off), bytes);
+		else {
+			memcpy(VP_PAGE_PTR(vpx, pg_ind, pg_off), from, bytes);
+			if ((to + count) > vpx->vp_top)
+				vpx->vp_top = to + count;
+		}
+		to += bytes;
+		from += bytes;
+		rw += bytes;
+		count -= bytes;
+	}
+	return rw;
+}
+
+struct vp_ctx* vp_get_vpx(struct vbuf *vb)
+{
+	struct vp_ctx *vpx = NULL;
+
+	if (!vb || !vb->v_opaque)
+		goto out;
+
+	vpx = vb->v_opaque;
+	if (vpx->vp_magic != VP_MAGIC) {
+		vpx = NULL;
+		goto out;
+	}
+out:
+	return vpx;
+}
+
+int vp_read(struct vbuf *vb, void *to, void *from, size_t count)
+{
+	struct vp_ctx *vpx;
+	void *buf_end = NULL;
+	int err = -EINVAL;
+
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return err;
+
+	buf_end = vpx->vp_baseptr + PAGE_SIZE * vpx->vp_size;
+	if (count <= 0 || (from + count > buf_end))
+		return err;
+
+	return vp_rw_pages(vpx, to, from, count, VP_READ);
+}
+
+int vp_write_i(struct vp_ctx *vpx, void *to, void *from, size_t count)
+{
+	int err = -EINVAL;
+	void *buf_end = NULL;
+
+	/* Check to see if the write will fall within limits */
+	buf_end = vpx->vp_baseptr + PAGE_SIZE * vpx->vp_size;
+	if (to < vpx->vp_baseptr ||
+	    (to + count) > (buf_end + PAGE_SIZE * VP_MAX_ALLOC_STEP))
+		return err;
+
+	if ((to + count) > buf_end) {
+		err = vp_extend(vpx, DIV_ROUND_UP(to + count - buf_end, PAGE_SIZE));
+		if (err)
+			return err;
+	}
+
+	return vp_rw_pages(vpx, to, from, count, VP_WRITE);
+}
+
+int vp_write(struct vbuf *vb, void *to, void *from, size_t count)
+{
+	struct vp_ctx *vpx;
+	int err = -EINVAL;
+
+	if (count <= 0 || count > VP_MAX_WRITE)
+		return err;
+
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return err;
+
+	return vp_write_i(vpx, to, from, count);
+}
+
+int vp_append(struct vbuf *vb, void *from, size_t count)
+{
+	struct vp_ctx *vpx;
+	int err = -EINVAL;
+
+	if (count <= 0 || count > VP_MAX_WRITE)
+		return err;
+
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return err;
+	
+	return vp_write_i(vpx, vpx->vp_top, from, count);
+}
+
+int vp_memset(struct vbuf *vb, void *to, char c, size_t count)
+{
+	struct vp_ctx *vpx;
+	int err = -EINVAL, i;
+
+	if (count <=0 || count > VP_MAX_WRITE)
+		return err;
+
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return err;
+
+	for (i=0; i<count; i++) {
+		err = vp_write_i(vpx, to + i, &c, 1);
+		if (err != 1)
+			break;
+	}
+	return err;
+}
+
+void* vp_get_top(struct vbuf *vb)
+{
+	struct vp_ctx *vpx;
+	vpx = vp_get_vpx(vb);
+	if (!vpx)
+		return NULL;
+	else
+		return vpx->vp_top;
+}
diff --git a/fs/gfs2/util.h b/fs/gfs2/util.h
index 8053573..9397f32 100644
--- a/fs/gfs2/util.h
+++ b/fs/gfs2/util.h
@@ -11,6 +11,7 @@
 #define __UTIL_DOT_H__
 
 #include <linux/mempool.h>
+#include <linux/slab.h>
 
 #include "incore.h"
 
@@ -168,5 +169,40 @@ void gfs2_icbit_munge(struct gfs2_sbd *sdp, unsigned char **bitmap,
 		      unsigned int bit, int new_value);
 int gfs2_lm_withdraw(struct gfs2_sbd *sdp, char *fmt, ...);
 
+/*
+ * Fast buffer backed by vector of pages.
+ * Always allocate one page at a time
+ */
+struct vbuf {
+	void    *v_ptr;
+	void    *v_opaque;
+};
+
+struct vp_ctx {
+	unsigned int    vp_magic;
+	void           *vp_baseptr;
+	void           *vp_top;
+	unsigned long   vp_size; /* size in pages (for bytes, * PAGE_SIZE) */
+	struct page   **vp_pages;
+};
+
+#define VP_MAGIC           0xfabd1cc5
+#define VP_MAX_WRITE       (PAGE_SIZE * 1024)
+#define VP_MAX_ALLOC_STEP  128 /* Can only alloc these many pages at a time */
+
+#define VP_READ    1
+#define VP_WRITE   2
+
+#define VP_PAGE_INDEX(vpx, a)      (((char*)a - (char*)vpx->vp_baseptr) / PAGE_SIZE)
+#define VP_PAGE_OFFSET(vpx, a)     (((char*)a - (char*)vpx->vp_baseptr) % PAGE_SIZE)
+#define VP_PAGE_BYTES_LEFT(vpx, a) (PAGE_SIZE - VP_PAGE_OFFSET(vpx, a))
+#define VP_PAGE_PTR(vpx, ind, off) (page_address(vpx->vp_pages[ind]) + off)
+
+int vp_init(struct vbuf *vb, int init_cap);
+void vp_uninit(struct vbuf *vb);
+void* vp_get_top(struct vbuf *vb);
+int vp_read(struct vbuf *vb, void *to, void *from, size_t count);
+int vp_write(struct vbuf *vb, void *to, void *from, size_t count);
+int vp_append(struct vbuf *vb, void *from, size_t count);
+int vp_memset(struct vbuf *vb, void *to, char c, size_t count);
 #endif /* __UTIL_DOT_H__ */
-
diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c
index cbb46c2..8e4432a 100644
--- a/fs/gfs2/xattr.c
+++ b/fs/gfs2/xattr.c
@@ -11,6 +11,7 @@
 #include <linux/spinlock.h>
 #include <linux/completion.h>
 #include <linux/buffer_head.h>
+#include <linux/sort.h>
 #include <linux/xattr.h>
 #include <linux/gfs2_ondisk.h>
 #include <asm/uaccess.h>
@@ -18,6 +19,7 @@
 #include "gfs2.h"
 #include "incore.h"
 #include "acl.h"
+#include "dir.h"
 #include "xattr.h"
 #include "glock.h"
 #include "inode.h"
@@ -73,10 +75,11 @@ static int ea_check_size(struct gfs2_sbd *sdp, unsigned int nsize, size_t dsize)
 
 typedef int (*ea_call_t) (struct gfs2_inode *ip, struct buffer_head *bh,
 			  struct gfs2_ea_header *ea,
-			  struct gfs2_ea_header *prev, void *private);
+			  struct gfs2_ea_header *prev, void *private,
+			  unsigned int mask);
 
 static int ea_foreach_i(struct gfs2_inode *ip, struct buffer_head *bh,
-			ea_call_t ea_call, void *data)
+			ea_call_t ea_call, void *data, unsigned int mask)
 {
 	struct gfs2_ea_header *ea, *prev = NULL;
 	int error = 0;
@@ -93,7 +96,7 @@ static int ea_foreach_i(struct gfs2_inode *ip, struct buffer_head *bh,
 		if (!GFS2_EATYPE_VALID(ea->ea_type))
 			goto fail;
 
-		error = ea_call(ip, bh, ea, prev, data);
+		error = ea_call(ip, bh, ea, prev, data, mask);
 		if (error)
 			return error;
 
@@ -112,7 +115,8 @@ fail:
 	return -EIO;
 }
 
-static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
+static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data,
+		      unsigned int mask)
 {
 	struct buffer_head *bh, *eabh;
 	__be64 *eablk, *end;
@@ -123,7 +127,7 @@ static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
 		return error;
 
 	if (!(ip->i_diskflags & GFS2_DIF_EA_INDIRECT)) {
-		error = ea_foreach_i(ip, bh, ea_call, data);
+		error = ea_foreach_i(ip, bh, ea_call, data, mask);
 		goto out;
 	}
 
@@ -145,7 +149,7 @@ static int ea_foreach(struct gfs2_inode *ip, ea_call_t ea_call, void *data)
 		error = gfs2_meta_read(ip->i_gl, bn, DIO_WAIT, &eabh);
 		if (error)
 			break;
-		error = ea_foreach_i(ip, eabh, ea_call, data);
+		error = ea_foreach_i(ip, eabh, ea_call, data, mask);
 		brelse(eabh);
 		if (error)
 			break;
@@ -164,7 +168,7 @@ struct ea_find {
 
 static int ea_find_i(struct gfs2_inode *ip, struct buffer_head *bh,
 		     struct gfs2_ea_header *ea, struct gfs2_ea_header *prev,
-		     void *private)
+		     void *private, unsigned mask)
 {
 	struct ea_find *ef = private;
 
@@ -199,7 +203,7 @@ static int gfs2_ea_find(struct gfs2_inode *ip, int type, const char *name,
 
 	memset(el, 0, sizeof(struct gfs2_ea_location));
 
-	error = ea_foreach(ip, ea_find_i, &ef);
+	error = ea_foreach(ip, ea_find_i, &ef, 0);
 	if (error > 0)
 		return 0;
 
@@ -223,7 +227,8 @@ static int gfs2_ea_find(struct gfs2_inode *ip, int type, const char *name,
 
 static int ea_dealloc_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh,
 				struct gfs2_ea_header *ea,
-				struct gfs2_ea_header *prev, void *private)
+				struct gfs2_ea_header *prev, void *private,
+				unsigned int mask)
 {
 	int *leave = private;
 	struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
@@ -335,7 +340,7 @@ static int ea_remove_unstuffed(struct gfs2_inode *ip, struct buffer_head *bh,
 	if (error)
 		goto out_alloc;
 
-	error = ea_dealloc_unstuffed(ip, bh, ea, prev, (leave) ? &error : NULL);
+	error = ea_dealloc_unstuffed(ip, bh, ea, prev, (leave) ? &error : NULL, 0);
 
 	gfs2_quota_unhold(ip);
 out_alloc:
@@ -361,9 +366,26 @@ static inline unsigned int gfs2_ea_strlen(struct gfs2_ea_header *ea)
 	}
 }
 
+static __inline__ int ea_prefix(struct gfs2_ea_header *ea, char *buf, int size)
+{
+	BUG_ON(size < 9);
+	switch (ea->ea_type) {
+	case GFS2_EATYPE_USR:
+		strncpy(buf, "user.", 5);
+		return 5;
+	case GFS2_EATYPE_SYS:
+		strncpy(buf, "system.", 7);
+		return 7;
+	case GFS2_EATYPE_SECURITY:
+		strncpy(buf, "security.", 9);
+		return 9;
+	}
+	return 0;
+}
+
 static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh,
 		     struct gfs2_ea_header *ea, struct gfs2_ea_header *prev,
-		     void *private)
+		     void *private, unsigned int mask)
 {
 	struct ea_list *ei = private;
 	struct gfs2_ea_request *er = ei->ei_er;
@@ -373,28 +395,14 @@ static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh,
 		return 0;
 
 	if (er->er_data_len) {
-		char *prefix = NULL;
+		char prefix[9];
 		unsigned int l = 0;
 		char c = 0;
 
 		if (ei->ei_size + ea_size > er->er_data_len)
 			return -ERANGE;
 
-		switch (ea->ea_type) {
-		case GFS2_EATYPE_USR:
-			prefix = "user.";
-			l = 5;
-			break;
-		case GFS2_EATYPE_SYS:
-			prefix = "system.";
-			l = 7;
-			break;
-		case GFS2_EATYPE_SECURITY:
-			prefix = "security.";
-			l = 9;
-			break;
-		}
-
+		l = ea_prefix(ea, prefix, 9);
 		BUG_ON(l == 0);
 
 		memcpy(er->er_data + ei->ei_size, prefix, l);
@@ -408,6 +416,93 @@ static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh,
 	return 0;
 }
 
+static inline int xattr_requested(char type, unsigned int mask)
+{
+	if ((type == GFS2_EATYPE_USR) && (mask & XSTAT_XATTR_USER))
+		return 1;
+	if ((type == GFS2_EATYPE_SYS) && (mask & XSTAT_XATTR_SYSTEM))
+		return 1;
+	if ((type == GFS2_EATYPE_SECURITY) && (mask & XSTAT_XATTR_SECURITY))
+		return 1;
+	return 0;
+}
+
+struct gfs2_xdir_ctx_bndle {
+	struct gfs2_rddir_ctx *xcb_rc;
+	struct gfs2_xdirent   *xcb_xd;
+};
+
+static int ea_xdirent_list_i(struct gfs2_inode *ip, struct buffer_head *bh,
+			     struct gfs2_ea_header *ea, struct gfs2_ea_header *prev,
+			     void *private, unsigned int mask)
+{
+	struct gfs2_xdir_ctx_bndle *bundle = private;
+	struct gfs2_rddir_ctx *rc = bundle->xcb_rc;
+	struct gfs2_xdirent *x = bundle->xcb_xd;
+	struct gfs2_xd_xattr *xtr;
+	char prefix[9];
+	unsigned int l = 0, xtr_count;
+	unsigned int namlen, reclen;
+	void *p;
+
+	if (!xattr_requested(ea->ea_type, mask))
+		return 0;
+
+	if (ea->ea_type == GFS2_EATYPE_UNUSED)
+		return 0;
+
+	l = ea_prefix(ea, prefix, 9);
+	BUG_ON(l == 0);
+
+	xtr = vp_get_top(&rc->rc_xattr_keys);
+	vp_memset(&rc->rc_xattr_keys, xtr, 0, sizeof(struct gfs2_xd_xattr));
+
+	/* if mask says don't do values, skip the following lines */
+	if (GFS2_EA_DATA_LEN(ea) > 0) {
+		unsigned long len = GFS2_EA_DATA_LEN(ea);
+		void *valptr = vp_get_top(&rc->rc_xattr_values);
+
+		vp_write(&rc->rc_xattr_keys, &xtr->xa_value_len,
+			 &len, sizeof(xtr->xa_value_len));
+		vp_write(&rc->rc_xattr_keys, &xtr->xa_vb_value_ptr, &valptr, sizeof(void*));
+		vp_read(&rc->rc_xattr_keys, &p, &xtr->xa_vb_value_ptr, sizeof(void*));
+		vp_append(&rc->rc_xattr_values, GFS2_EA2DATA(ea), len);
+	}
+
+	namlen = l + ea->ea_name_len;
+	vp_write(&rc->rc_xattr_keys, &xtr->xa_keylen, &namlen, sizeof(xtr->xa_keylen));
+	vp_write(&rc->rc_xattr_keys, xtr->xa_keyname, &prefix, l);
+	vp_write(&rc->rc_xattr_keys, xtr->xa_keyname + l, GFS2_EA2NAME(ea), namlen);
+
+	/* gfs2_xd_xattr.xa_keyname[1] has an extra byte */
+	reclen = (xtr->xa_keyname + l + namlen) - (char *)xtr;
+	vp_write(&rc->rc_xattr_keys, &xtr->xa_reclen, &reclen, sizeof(xtr->xa_reclen));
+
+	vp_read(&rc->rc_dirents, &xtr_count, &x->x_xattr_count, sizeof(x->x_xattr_count));
+	xtr_count++;
+	vp_write(&rc->rc_dirents, &x->x_xattr_count, &xtr_count, sizeof(x->x_xattr_count));
+
+	return 0;
+}
+
+int gfs2_xrdir_collect_xattrs(struct gfs2_rddir_ctx *rc, struct gfs2_xdirent *x_vb_p,
+			      struct gfs2_inode *ip, unsigned int mask)
+{
+	int error = 0;
+	struct gfs2_xdir_ctx_bndle bundle;
+	struct gfs2_xdirent *xtop;
+
+	bundle.xcb_rc = rc;
+	bundle.xcb_xd = x_vb_p;
+
+	xtop = vp_get_top(&rc->rc_xattr_keys);
+	vp_write(&rc->rc_dirents, &x_vb_p->x_vb_xattr_arr_ptr, &xtop, sizeof(struct gfs2_xd_xattr*));
+
+	error = ea_foreach(ip, ea_xdirent_list_i, &bundle, mask);
+
+	return error;
+}
+
 /**
  * gfs2_listxattr - List gfs2 extended attributes
  * @dentry: The dentry whose inode we are interested in
@@ -437,7 +532,7 @@ ssize_t gfs2_listxattr(struct dentry *dentry, char *buffer, size_t size)
 	if (ip->i_eattr) {
 		struct ea_list ei = { .ei_er = &er, .ei_size = 0 };
 
-		error = ea_foreach(ip, ea_list_i, &ei);
+		error = ea_foreach(ip, ea_list_i, &ei, 0);
 		if (!error)
 			error = ei.ei_size;
 	}
@@ -918,7 +1013,7 @@ static int ea_set_simple_alloc(struct gfs2_inode *ip,
 
 static int ea_set_simple(struct gfs2_inode *ip, struct buffer_head *bh,
 			 struct gfs2_ea_header *ea, struct gfs2_ea_header *prev,
-			 void *private)
+			 void *private, unsigned int mask)
 {
 	struct ea_set *es = private;
 	unsigned int size;
@@ -1055,7 +1150,7 @@ static int ea_set_i(struct gfs2_inode *ip, int type, const char *name,
 	es.es_er = &er;
 	es.es_el = el;
 
-	error = ea_foreach(ip, ea_set_simple, &es);
+	error = ea_foreach(ip, ea_set_simple, &es, 0);
 	if (error > 0)
 		return 0;
 	if (error)
@@ -1465,7 +1560,7 @@ int gfs2_ea_dealloc(struct gfs2_inode *ip)
 	if (error)
 		return error;
 
-	error = ea_foreach(ip, ea_dealloc_unstuffed, NULL);
+	error = ea_foreach(ip, ea_dealloc_unstuffed, NULL, 0);
 	if (error)
 		goto out_quota;
 
diff --git a/fs/gfs2/xattr.h b/fs/gfs2/xattr.h
index d392f83..1cebebf 100644
--- a/fs/gfs2/xattr.h
+++ b/fs/gfs2/xattr.h
@@ -10,6 +10,8 @@
 #ifndef __EATTR_DOT_H__
 #define __EATTR_DOT_H__
 
+#include "dir.h"
+
 struct gfs2_inode;
 struct iattr;
 
@@ -56,6 +58,8 @@ struct gfs2_ea_location {
 extern int __gfs2_xattr_set(struct inode *inode, const char *name,
 			    const void *value, size_t size,
 			    int flags, int type);
+extern int gfs2_xrdir_collect_xattrs(struct gfs2_rddir_ctx *rc, struct gfs2_xdirent *x,
+			      struct gfs2_inode *ip, unsigned int mask);
 extern ssize_t gfs2_listxattr(struct dentry *dentry, char *buffer, size_t size);
 extern int gfs2_ea_dealloc(struct gfs2_inode *ip);
 
diff --git a/fs/readdir.c b/fs/readdir.c
index 5e69ef5..a3482d2 100644
--- a/fs/readdir.c
+++ b/fs/readdir.c
@@ -143,6 +143,105 @@ struct getdents_callback {
 	int error;
 };
 
+static size_t get_xdirent_size(const struct xdirent_info *xinfo)
+{
+	int i;
+	/* Start with the base size */
+	size_t size = sizeof(struct linux_xdirent);
+	/* Account for the various variable length fields
+	 * starting with dirent name. Note that one trailing
+	 * '\0' is already accounted for by the xd_blob field
+	 * in struct linux_xdirent
+	 */
+	size += xinfo->xi_namelen;
+	if (!xinfo->xi_xattr_count)
+		return size; /* No xattrs to worry about */
+	for (i=0; i<xinfo->xi_xattr_count; i++) {
+		struct xattr *xattr = xinfo->xi_xattrs[i];
+		size += sizeof(size_t); /* for xdirent_xattr.xa_value_len */
+		size += strlen(xattr->name) + 1;
+		size += xattr->value_len;
+	}
+	return size;
+}
+
+static int xfill_variables(struct linux_xdirent __user *lxd,
+			   const struct xdirent_info *xinfo)
+{
+	int error = -EINVAL, i;
+	struct xdirent_blob __user *xblob;
+	struct xdirent_xattr __user *xx;
+
+	xblob = &lxd->xd_blob;
+
+	if (__put_user(xinfo->xi_xattr_count, &xblob->xb_xattr_count))
+		goto out;
+
+	if (copy_to_user(xblob->xb_blob, xinfo->xi_name, xinfo->xi_namelen))
+		goto out;
+	if (__put_user(0, xblob->xb_blob + xinfo->xi_namelen))
+		goto out;
+
+	xx = (struct xdirent_xattr __user *)(xblob->xb_blob + xinfo->xi_namelen + 1);
+
+	for (i=0; i< xinfo->xi_xattr_count; i++) {
+		struct xattr *xattr = xinfo->xi_xattrs[i];
+		int namlen = strlen(xattr->name);
+
+		if (__put_user(xattr->value_len, &xx->xa_value_len))
+			goto out;
+		if (copy_to_user(xx->xa_name_val, xattr->name, namlen))
+			goto out;
+		if (__put_user(0, xx->xa_name_val + namlen))
+			goto out;
+		if (copy_to_user(xx->xa_name_val + namlen + 1, xattr->value,
+				 xattr->value_len))
+			goto out;
+		xx = (struct xdirent_xattr __user *)
+			((char *)xx + sizeof(xx->xa_value_len)
+			 + namlen + 1 + xattr->value_len);
+	}
+	error = 0;
+out:
+	return error;
+}
+
+static int xfilldir(void *__opaque, const struct xdirent_info *xinfo)
+{
+	struct xdirent_buf *xbuf = (struct xdirent_buf *) __opaque;
+	struct linux_xdirent __user *lxd;
+	int error = -EOVERFLOW;
+	size_t entsize;
+
+	/* Before proceeding any further, check that the dirent will
+	 * fit in its entirety in the remaining space of the user
+	 * buffer
+	 */
+	entsize = get_xdirent_size(xinfo);
+	if ((xbuf->xb_count - xbuf->xb_used) < entsize)
+		goto out;
+
+	error = -EINVAL;
+	lxd = (struct linux_xdirent __user *)(xbuf->xb_buf + xbuf->xb_used);
+	if (__put_user(xinfo->xi_ino, &lxd->xd_ino))
+		goto out;
+	if (__put_user(xinfo->xi_type, &lxd->xd_type))
+		goto out;
+	if (__put_user(xinfo->xi_offset, &lxd->xd_off))
+		goto out;
+	if (__put_user(entsize, &lxd->xd_reclen))
+		goto out;
+	error = xstat_set_result((struct kstat *)&xinfo->xi_stat, &lxd->xd_stat);
+	if (error)
+		goto out;
+	error = xfill_variables(lxd, xinfo);
+	if (error)
+		goto out;
+	xbuf->xb_used += entsize;
+out:
+	return error;
+}
+
 static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
 		   u64 ino, unsigned int d_type)
 {
@@ -220,6 +319,93 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
 	return error;
 }
 
+SYSCALL_DEFINE5(xgetdents, unsigned int, fd, unsigned, flags, unsigned int, mask, 
+		void __user *, buf, unsigned int, count)
+{
+	struct file *file;
+	struct inode *inode;
+	int fput_needed;
+	int error = -ENOTDIR;
+
+	if (!access_ok(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+
+	file = fget_light(fd, &fput_needed);
+	if (!file)
+		return -EBADF;
+
+	inode = file->f_path.dentry->d_inode;
+
+	if (!file->f_op || !file->f_op->xreaddir)
+		goto out;
+
+	error = security_file_permission(file, MAY_READ);
+	if (error)
+		goto out;
+
+	error = mutex_lock_killable(&inode->i_mutex);
+	if (error)
+		goto out;
+
+	error = -ENOENT;
+	if (!IS_DEADDIR(inode)) {
+		error = file->f_op->xreaddir(file, flags, mask, buf, count);
+		file_accessed(file);
+	}
+	mutex_unlock(&inode->i_mutex);
+out:
+	fput_light(file, fput_needed);
+	return error;
+}
+
+#if 0
+SYSCALL_DEFINE5(xgetdents, unsigned int, fd, unsigned, flags, unsigned int, mask, 
+		void __user *, buf, unsigned int, count)
+{
+	struct file *file;
+	struct inode *inode;
+	int fput_needed;
+	int error = -ENOTDIR;
+
+	if (!access_ok(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+
+	file = fget_light(fd, &fput_needed);
+	if (!file)
+		return -EBADF;
+
+	inode = file->f_path.dentry->d_inode;
+
+	if (!file->f_op || !file->f_op->xreaddir)
+		goto out;
+
+	error = security_file_permission(file, MAY_READ);
+	if (error)
+		goto out;
+
+	error = mutex_lock_killable(&inode->i_mutex);
+	if (error)
+		goto out;
+
+	error = -ENOENT;
+	if (!IS_DEADDIR(inode)) {
+		struct xdirent_buf xbuf;
+		xbuf.xb_buf = buf;
+		xbuf.xb_used = 0;
+		xbuf.xb_count = count;
+		error = file->f_op->xreaddir(file, flags, mask, count, &xbuf, xfilldir);
+		if (!error || error == -EOVERFLOW)
+			error = xbuf.xb_used;
+
+		file_accessed(file);
+	}
+	mutex_unlock(&inode->i_mutex);
+out:
+	fput_light(file, fput_needed);
+	return error;
+}
+#endif
+
 struct getdents_callback64 {
 	struct linux_dirent64 __user * current_dir;
 	struct linux_dirent64 __user * previous;
diff --git a/fs/stat.c b/fs/stat.c
index 5303f40..60376d3 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -635,7 +635,7 @@ static int xstat_get_params(unsigned int mask, struct xstat __user *buffer,
  * Otherwise we copy the extended stats to userspace and return the amount of
  * data written into the buffer (or -EFAULT).
  */
-static long xstat_set_result(struct kstat *stat, struct xstat __user *buffer)
+long xstat_set_result(struct kstat *stat, struct xstat __user *buffer)
 {
 	u32 mask = stat->result_mask, gran = stat->tv_granularity;
 
@@ -685,6 +685,8 @@ static long xstat_set_result(struct kstat *stat, struct xstat __user *buffer)
 	return 0;
 }
 
+EXPORT_SYMBOL(xstat_set_result);
+
 /*
  * System call to get extended stats by path
  */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 44c6c84..35f1418 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1505,6 +1505,8 @@ int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags);
  * to have different dirent layouts depending on the binary type.
  */
 typedef int (*filldir_t)(void *, const char *, int, loff_t, u64, unsigned);
+typedef int (*xfilldir_t)(void *, const struct xdirent_info *);
+
 struct block_device_operations;
 
 /* These macros are for out of kernel modules to test that
@@ -1521,6 +1523,10 @@ struct file_operations {
 	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 	int (*readdir) (struct file *, void *, filldir_t);
+	size_t (*xreaddir) (struct file *, unsigned int, unsigned int, void __user *, size_t);
+#if 0
+	int (*xreaddir) (struct file *, unsigned int, unsigned int, size_t, void *, xfilldir_t);
+#endif
 	unsigned int (*poll) (struct file *, struct poll_table_struct *);
 	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
 	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
diff --git a/include/linux/list_bl.h b/include/linux/list_bl.h
index 31f9d75..2eb8855 100644
--- a/include/linux/list_bl.h
+++ b/include/linux/list_bl.h
@@ -125,6 +125,11 @@ static inline void hlist_bl_unlock(struct hlist_bl_head *b)
 	__bit_spin_unlock(0, (unsigned long *)b);
 }
 
+static inline bool hlist_bl_is_locked(struct hlist_bl_head *b)
+{
+	return bit_spin_is_locked(0, (unsigned long *)b);
+}
+
 /**
  * hlist_bl_for_each_entry	- iterate over list of given type
  * @tpos:	the type * to use as a loop cursor.
diff --git a/include/linux/rculist_bl.h b/include/linux/rculist_bl.h
index cf1244f..4f216c5 100644
--- a/include/linux/rculist_bl.h
+++ b/include/linux/rculist_bl.h
@@ -20,7 +20,7 @@ static inline void hlist_bl_set_first_rcu(struct hlist_bl_head *h,
 static inline struct hlist_bl_node *hlist_bl_first_rcu(struct hlist_bl_head *h)
 {
 	return (struct hlist_bl_node *)
-		((unsigned long)rcu_dereference(h->first) & ~LIST_BL_LOCKMASK);
+		((unsigned long)rcu_dereference_check(h->first, hlist_bl_is_locked(h)) & ~LIST_BL_LOCKMASK);
 }
 
 /**
diff --git a/include/linux/stat.h b/include/linux/stat.h
index 552e047..e18d107 100644
--- a/include/linux/stat.h
+++ b/include/linux/stat.h
@@ -18,6 +18,7 @@
 #include <linux/types.h>
 #include <linux/time.h>
 #include <linux/uidgid.h>
+#include <linux/xattr.h>
 
 struct kstat {
 	u32             query_flags;                /* operational flags */
@@ -46,4 +47,23 @@ struct kstat {
 	unsigned char   volume_id[16];              /* volume identifier */
 };
 
+long xstat_set_result(struct kstat *stat, struct xstat __user *buffer);
+
+struct xdirent_buf {
+	void __user *xb_buf;
+	size_t       xb_count;
+	size_t       xb_used;
+};
+
+struct xdirent_info {
+	const char    *xi_name;
+	int            xi_namelen;
+	u64            xi_ino;
+	u64            xi_offset;
+	char           xi_type;
+	unsigned int   xi_xattr_count;
+	struct kstat   xi_stat;
+	struct xattr **xi_xattrs;
+};
+
 #endif
diff --git a/include/uapi/linux/stat.h b/include/uapi/linux/stat.h
index 2907352..44a5aae 100644
--- a/include/uapi/linux/stat.h
+++ b/include/uapi/linux/stat.h
@@ -90,6 +90,13 @@
 #define XSTAT_VOLUME_ID        0x00008000U     /* want/got st_volume_id */
 #define XSTAT_ALL_STATS        0x0000ffffU     /* all supported stats */
 
+/* xattr request flags */
+#define XSTAT_XATTR_USER       0x00010000U     /* user.* xattrs */
+#define XSTAT_XATTR_SYSTEM     0x00020000U     /* system.* xattrs */
+#define XSTAT_XATTR_SECURITY   0x00040000U     /* security.* xattrs */
+#define XSTAT_XATTR_POSIX_ACL  0x00080000U     /* posix acl xattrs */
+#define XSTAT_ALL_XATTRS       0x00ff0000U     /* all xattrs */
+
 /*
  * Extended stat structures
  */
@@ -152,4 +159,29 @@ struct xstat {
 #define XSTAT_INFO_SYSTEM              0x00001000U /* File is marked system (DOS+) */
 #define XSTAT_INFO_ARCHIVE             0x00002000U /* File is marked archive (DOS+) */
 
+struct xdirent_xattr {
+	size_t       xa_value_len;   /* length of value field */
+	char         xa_name_val[1]; /* name/value pair, name is NULL terminated and 
+				      * value is xa_value_len in length */
+};
+
+/*
+ * xb_blob: first contains NULL terminated name, followed by struct xdirent_xattr
+ * objects packed together.
+ */
+struct xdirent_blob {
+	unsigned int    xb_xattr_count;
+	char            xb_blob[1]; /* contains variable length data like
+				     * NULL-terminated name, xattrs etc */
+};
+
+struct linux_xdirent {
+	unsigned long        xd_ino;
+	char                 xd_type;
+	unsigned long        xd_off;
+	struct xstat         xd_stat;
+	unsigned long        xd_reclen;
+	struct xdirent_blob  xd_blob;
+};
+
 #endif /* _UAPI_LINUX_STAT_H */
