Date: Thu, 09 Oct 2008 17:11:53 +1100
From: Donald Douwsma <donaldd@sgi.com>
Subject: DMAPI support for xfs
Patch-mainline: Not yet
References: bnc#450658

Acked-by: Jan Kara <jack@suse.cz>

---
 fs/xfs/Kconfig               |   13 
 fs/xfs/Makefile              |    5 
 fs/xfs/dmapi/Makefile        |   28 
 fs/xfs/dmapi/xfs_dm.c        | 3327 +++++++++++++++++++++++++++++++++++++++++++
 fs/xfs/dmapi/xfs_dm.h        |   23 
 fs/xfs/linux-2.6/xfs_file.c  |   76 
 fs/xfs/linux-2.6/xfs_ksyms.c |   92 +
 fs/xfs/linux-2.6/xfs_linux.h |    4 
 fs/xfs/linux-2.6/xfs_super.c |   13 
 fs/xfs/xfs_dmops.c           |   20 
 fs/xfs/xfs_itable.c          |    2 
 fs/xfs/xfs_itable.h          |    5 
 fs/xfs/xfs_mount.h           |    1 
 fs/xfs/xfs_rw.c              |    1 
 fs/xfs/xfs_rw.h              |    5 
 fs/xfs/xfs_vnodeops.c        |    2 
 16 files changed, 3609 insertions(+), 8 deletions(-)

--- a/fs/xfs/Kconfig
+++ b/fs/xfs/Kconfig
@@ -36,6 +36,19 @@ config XFS_QUOTA
 	  with or without the generic quota support enabled (CONFIG_QUOTA) -
 	  they are completely independent subsystems.
 
+config XFS_DMAPI
+	tristate "XFS DMAPI support"
+	depends on XFS_FS
+	select DMAPI
+	help
+	  The Data Management API is a system interface used to implement
+	  the interface defined in the X/Open document:
+	  "Systems Management: Data Storage Management (XDSM) API",
+	  dated February 1997.  This interface is used by hierarchical
+	  storage management systems.
+
+	  If unsure, say N.
+
 config XFS_POSIX_ACL
 	bool "XFS POSIX ACL support"
 	depends on XFS_FS
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -41,6 +41,8 @@ ifeq ($(CONFIG_XFS_QUOTA),y)
 xfs-$(CONFIG_PROC_FS)		+= quota/xfs_qm_stats.o
 endif
 
+obj-$(CONFIG_XFS_DMAPI)         += dmapi/
+
 xfs-$(CONFIG_XFS_RT)		+= xfs_rtalloc.o
 xfs-$(CONFIG_XFS_POSIX_ACL)	+= $(XFS_LINUX)/xfs_acl.o
 xfs-$(CONFIG_PROC_FS)		+= $(XFS_LINUX)/xfs_stats.o
@@ -107,7 +109,8 @@ xfs-y				+= $(addprefix $(XFS_LINUX)/, \
 				   xfs_iops.o \
 				   xfs_super.o \
 				   xfs_sync.o \
-				   xfs_xattr.o)
+				   xfs_xattr.o \
+				   xfs_ksyms.o)
 
 # Objects in support/
 xfs-y				+= $(addprefix support/, \
--- /dev/null
+++ b/fs/xfs/dmapi/Makefile
@@ -0,0 +1,28 @@
+#
+# Copyright (c) 2006 Silicon Graphics, Inc.
+# All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it would be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write the Free Software Foundation,
+# Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#
+
+EXTRA_CFLAGS += -I$(src)/.. -I$(src)/../linux-2.6
+EXTRA_CFLAGS += -I$(srctree)/fs/dmapi
+
+ifeq ($(CONFIG_XFS_DEBUG),y)
+	EXTRA_CFLAGS += -g -DDEBUG
+endif
+
+obj-$(CONFIG_XFS_DMAPI)		+= xfs_dmapi.o
+
+xfs_dmapi-y			+= xfs_dm.o
--- /dev/null
+++ b/fs/xfs/dmapi/xfs_dm.c
@@ -0,0 +1,3327 @@
+/*
+ * Copyright (c) 2000-2006 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_types.h"
+#include "xfs_bit.h"
+#include "xfs_log.h"
+#include "xfs_inum.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_mount.h"
+#include "xfs_da_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_itable.h"
+#include "xfs_bmap.h"
+#include "xfs_rw.h"
+#include "xfs_acl.h"
+#include "xfs_attr.h"
+#include "xfs_attr_leaf.h"
+#include "xfs_inode_item.h"
+#include "xfs_vnodeops.h"
+#include <dmapi.h>
+#include <dmapi_kern.h>
+#include "xfs_dm.h"
+
+#include <linux/mount.h>
+
+#define MAXNAMLEN MAXNAMELEN
+
+#define MIN_DIO_SIZE(mp)		((mp)->m_sb.sb_sectsize)
+#define MAX_DIO_SIZE(mp)		(INT_MAX & ~(MIN_DIO_SIZE(mp) - 1))
+
+static void up_rw_sems(struct inode *ip, int flags)
+{
+	if (flags & DM_FLAGS_IALLOCSEM_WR)
+		up_write(&ip->i_alloc_sem);
+	if (flags & DM_FLAGS_IMUX)
+		mutex_unlock(&ip->i_mutex);
+}
+
+static void down_rw_sems(struct inode *ip, int flags)
+{
+	if (flags & DM_FLAGS_IMUX)
+		mutex_lock(&ip->i_mutex);
+	if (flags & DM_FLAGS_IALLOCSEM_WR)
+		down_write(&ip->i_alloc_sem);
+}
+
+
+/* Structure used to hold the on-disk version of a dm_attrname_t.  All
+   on-disk attribute names start with the 8-byte string "SGI_DMI_".
+*/
+
+typedef struct	{
+	char	dan_chars[DMATTR_PREFIXLEN + DM_ATTR_NAME_SIZE + 1];
+} dm_dkattrname_t;
+
+/* Structure used by xfs_dm_get_bulkall(), used as the "private_data"
+ * that we want xfs_bulkstat to send to our formatter.
+ */
+typedef struct {
+	dm_fsid_t	fsid;
+	void __user	*laststruct;
+	dm_dkattrname_t	attrname;
+} dm_bulkstat_one_t;
+
+/* In the on-disk inode, DMAPI attribute names consist of the user-provided
+   name with the DMATTR_PREFIXSTRING pre-pended.  This string must NEVER be
+   changed!
+*/
+
+static	const	char	dmattr_prefix[DMATTR_PREFIXLEN + 1] = DMATTR_PREFIXSTRING;
+
+static	dm_size_t  dm_min_dio_xfer = 0; /* direct I/O disabled for now */
+
+
+/* See xfs_dm_get_dmattr() for a description of why this is needed. */
+
+#define XFS_BUG_KLUDGE	256	/* max size of an in-inode attribute value */
+
+#define DM_MAX_ATTR_BYTES_ON_DESTROY	256
+
+#define DM_STAT_SIZE(dmtype,namelen)	\
+	(sizeof(dmtype) + sizeof(dm_handle_t) + namelen)
+
+#define DM_STAT_ALIGN		(sizeof(__uint64_t))
+
+/* DMAPI's E2BIG == EA's ERANGE */
+#define DM_EA_XLATE_ERR(err) { if (err == ERANGE) err = E2BIG; }
+
+static inline size_t dm_stat_align(size_t size)
+{
+	return (size + (DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1);
+}
+
+static inline size_t dm_stat_size(size_t namelen)
+{
+	return dm_stat_align(sizeof(dm_stat_t) + sizeof(dm_handle_t) + namelen);
+}
+
+/*
+ *	xfs_dm_send_data_event()
+ *
+ *	Send data event to DMAPI.  Drop IO lock (if specified) before
+ *	the dm_send_data_event() call and reacquire it afterwards.
+ */
+int
+xfs_dm_send_data_event(
+	dm_eventtype_t	event,
+	xfs_inode_t	*ip,
+	xfs_off_t	offset,
+	size_t		length,
+	int		flags,
+	int		*lock_flags)
+{
+	struct inode	*inode = &ip->i_vnode;
+	int		error;
+	uint16_t	dmstate;
+
+	/* Returns positive errors to XFS */
+
+	do {
+		dmstate = ip->i_d.di_dmstate;
+		if (lock_flags)
+			xfs_iunlock(ip, *lock_flags);
+
+		up_rw_sems(inode, flags);
+
+		error = dm_send_data_event(event, inode, DM_RIGHT_NULL,
+				offset, length, flags);
+		error = -error; /* DMAPI returns negative errors */
+
+		down_rw_sems(inode, flags);
+
+		if (lock_flags)
+			xfs_ilock(ip, *lock_flags);
+	} while (!error && (ip->i_d.di_dmstate != dmstate));
+
+	return error;
+}
+
+/*	prohibited_mr_events
+ *
+ *	Return event bits representing any events which cannot have managed
+ *	region events set due to memory mapping of the file.  If the maximum
+ *	protection allowed in any pregion includes PROT_WRITE, and the region
+ *	is shared and not text, then neither READ nor WRITE events can be set.
+ *	Otherwise if the file is memory mapped, no READ event can be set.
+ *
+ */
+STATIC int
+prohibited_mr_events(
+	struct address_space *mapping)
+{
+	int prohibited = (1 << DM_EVENT_READ);
+
+	if (!mapping_mapped(mapping))
+		return 0;
+
+	spin_lock(&mapping->i_mmap_lock);
+	if (mapping_writably_mapped(mapping))
+		prohibited |= (1 << DM_EVENT_WRITE);
+	spin_unlock(&mapping->i_mmap_lock);
+
+	return prohibited;
+}
+
+#ifdef	DEBUG_RIGHTS
+STATIC int
+xfs_vp_to_hexhandle(
+	struct inode	*inode,
+	u_int		type,
+	char		*buffer)
+{
+	dm_handle_t	handle;
+	u_char		*ip;
+	int		length;
+	int		error;
+	int		i;
+
+	/*
+	 * XXX: dm_vp_to_handle doesn't exist.
+	 * 	Looks like this debug code is rather dead.
+	 */
+	if ((error = dm_vp_to_handle(inode, &handle)))
+		return(error);
+
+	if (type == DM_FSYS_OBJ) {	/* a filesystem handle */
+		length = DM_FSHSIZE;
+	} else {
+		length = DM_HSIZE(handle);
+	}
+	for (ip = (u_char *)&handle, i = 0; i < length; i++) {
+		*buffer++ = "0123456789abcdef"[ip[i] >> 4];
+		*buffer++ = "0123456789abcdef"[ip[i] & 0xf];
+	}
+	*buffer = '\0';
+	return(0);
+}
+#endif	/* DEBUG_RIGHTS */
+
+
+
+
+/* Copy in and validate an attribute name from user space.  It should be a
+   string of at least one and at most DM_ATTR_NAME_SIZE characters.  Because
+   the dm_attrname_t structure doesn't provide room for the trailing NULL
+   byte, we just copy in one extra character and then zero it if it
+   happens to be non-NULL.
+*/
+
+STATIC int
+xfs_copyin_attrname(
+	dm_attrname_t	__user *from,	/* dm_attrname_t in user space */
+	dm_dkattrname_t *to)		/* name buffer in kernel space */
+{
+	int error = 0;
+	size_t len;
+
+	strcpy(to->dan_chars, dmattr_prefix);
+
+        len = strnlen_user((char __user *)from, DM_ATTR_NAME_SIZE);
+        if (len == 0)
+            error = EFAULT;
+        else {
+	   if (copy_from_user(&to->dan_chars[DMATTR_PREFIXLEN], from, len))
+		to->dan_chars[sizeof(to->dan_chars) - 1] = '\0';
+	   else if (to->dan_chars[DMATTR_PREFIXLEN] == '\0')
+		error = EINVAL;
+	   else
+		to->dan_chars[DMATTR_PREFIXLEN + len - 1] = '\0';
+        }
+
+	return error;
+}
+
+
+/*
+ * Convert the XFS flags into their DMAPI flag equivalent for export
+ */
+STATIC uint
+_xfs_dic2dmflags(
+	__uint16_t		di_flags)
+{
+	uint			flags = 0;
+
+	if (di_flags & XFS_DIFLAG_ANY) {
+		if (di_flags & XFS_DIFLAG_REALTIME)
+			flags |= DM_XFLAG_REALTIME;
+		if (di_flags & XFS_DIFLAG_PREALLOC)
+			flags |= DM_XFLAG_PREALLOC;
+		if (di_flags & XFS_DIFLAG_IMMUTABLE)
+			flags |= DM_XFLAG_IMMUTABLE;
+		if (di_flags & XFS_DIFLAG_APPEND)
+			flags |= DM_XFLAG_APPEND;
+		if (di_flags & XFS_DIFLAG_SYNC)
+			flags |= DM_XFLAG_SYNC;
+		if (di_flags & XFS_DIFLAG_NOATIME)
+			flags |= DM_XFLAG_NOATIME;
+		if (di_flags & XFS_DIFLAG_NODUMP)
+			flags |= DM_XFLAG_NODUMP;
+	}
+	return flags;
+}
+
+STATIC uint
+xfs_ip2dmflags(
+	xfs_inode_t	*ip)
+{
+	return _xfs_dic2dmflags(ip->i_d.di_flags) |
+			(XFS_IFORK_Q(ip) ? DM_XFLAG_HASATTR : 0);
+}
+
+STATIC uint
+xfs_dic2dmflags(
+	xfs_dinode_t	*dip)
+{
+	return _xfs_dic2dmflags(be16_to_cpu(dip->di_flags)) |
+			(XFS_DFORK_Q(dip) ? DM_XFLAG_HASATTR : 0);
+}
+
+/*
+ * This copies selected fields in an inode into a dm_stat structure.  Because
+ * these fields must return the same values as they would in stat(), the
+ * majority of this code was copied directly from xfs_getattr().  Any future
+ * changes to xfs_gettattr() must also be reflected here.
+ */
+STATIC void
+xfs_dip_to_stat(
+	xfs_mount_t		*mp,
+	xfs_ino_t		ino,
+	xfs_dinode_t		*dip,
+	dm_stat_t		*buf)
+{
+	xfs_dinode_t	*dic = dip;
+
+	/*
+	 * The inode format changed when we moved the link count and
+	 * made it 32 bits long.  If this is an old format inode,
+	 * convert it in memory to look like a new one.  If it gets
+	 * flushed to disk we will convert back before flushing or
+	 * logging it.  We zero out the new projid field and the old link
+	 * count field.  We'll handle clearing the pad field (the remains
+	 * of the old uuid field) when we actually convert the inode to
+	 * the new format. We don't change the version number so that we
+	 * can distinguish this from a real new format inode.
+	 */
+	if (dic->di_version == 1) {
+		buf->dt_nlink = be16_to_cpu(dic->di_onlink);
+		/*buf->dt_xfs_projid = 0;*/
+	} else {
+		buf->dt_nlink = be32_to_cpu(dic->di_nlink);
+		/*buf->dt_xfs_projid = be16_to_cpu(dic->di_projid);*/
+	}
+	buf->dt_ino = ino;
+	buf->dt_dev = new_encode_dev(mp->m_ddev_targp->bt_dev);
+	buf->dt_mode = be16_to_cpu(dic->di_mode);
+	buf->dt_uid = be32_to_cpu(dic->di_uid);
+	buf->dt_gid = be32_to_cpu(dic->di_gid);
+	buf->dt_size = be64_to_cpu(dic->di_size);
+	buf->dt_atime = be32_to_cpu(dic->di_atime.t_sec);
+	buf->dt_mtime = be32_to_cpu(dic->di_mtime.t_sec);
+	buf->dt_ctime = be32_to_cpu(dic->di_ctime.t_sec);
+	buf->dt_xfs_xflags = xfs_dic2dmflags(dip);
+	buf->dt_xfs_extsize =
+		be32_to_cpu(dic->di_extsize) << mp->m_sb.sb_blocklog;
+	buf->dt_xfs_extents = be32_to_cpu(dic->di_nextents);
+	buf->dt_xfs_aextents = be16_to_cpu(dic->di_anextents);
+	buf->dt_xfs_igen = be32_to_cpu(dic->di_gen);
+	buf->dt_xfs_dmstate = be16_to_cpu(dic->di_dmstate);
+
+	switch (dic->di_format) {
+	case XFS_DINODE_FMT_DEV:
+		buf->dt_rdev = xfs_dinode_get_rdev(dic);
+		buf->dt_blksize = BLKDEV_IOSIZE;
+		buf->dt_blocks = 0;
+		break;
+	case XFS_DINODE_FMT_LOCAL:
+	case XFS_DINODE_FMT_UUID:
+		buf->dt_rdev = 0;
+		buf->dt_blksize = mp->m_sb.sb_blocksize;
+		buf->dt_blocks = 0;
+		break;
+	case XFS_DINODE_FMT_EXTENTS:
+	case XFS_DINODE_FMT_BTREE:
+		buf->dt_rdev = 0;
+		buf->dt_blksize = mp->m_sb.sb_blocksize;
+		buf->dt_blocks =
+			XFS_FSB_TO_BB(mp, be64_to_cpu(dic->di_nblocks));
+		break;
+	}
+
+	memset(&buf->dt_pad1, 0, sizeof(buf->dt_pad1));
+	memset(&buf->dt_pad2, 0, sizeof(buf->dt_pad2));
+	memset(&buf->dt_pad3, 0, sizeof(buf->dt_pad3));
+
+	/* Finally fill in the DMAPI specific fields */
+	buf->dt_pers = 0;
+	buf->dt_change = 0;
+	buf->dt_nevents = DM_EVENT_MAX;
+	buf->dt_emask = be32_to_cpu(dic->di_dmevmask);
+	buf->dt_dtime = be32_to_cpu(dic->di_ctime.t_sec);
+	/* Set if one of READ, WRITE or TRUNCATE bits is set in emask */
+	buf->dt_pmanreg = (DMEV_ISSET(DM_EVENT_READ, buf->dt_emask) ||
+			DMEV_ISSET(DM_EVENT_WRITE, buf->dt_emask) ||
+			DMEV_ISSET(DM_EVENT_TRUNCATE, buf->dt_emask)) ? 1 : 0;
+}
+
+/*
+ * Pull out both ondisk and incore fields, incore has preference.
+ * The inode must be kept locked SHARED by the caller.
+ */
+STATIC void
+xfs_ip_to_stat(
+	xfs_mount_t		*mp,
+	xfs_ino_t		ino,
+	xfs_inode_t		*ip,
+	dm_stat_t		*buf)
+{
+	xfs_icdinode_t		*dic = &ip->i_d;
+
+	buf->dt_ino = ino;
+	buf->dt_nlink = dic->di_nlink;
+	/*buf->dt_xfs_projid = dic->di_projid;*/
+	buf->dt_mode = dic->di_mode;
+	buf->dt_uid = dic->di_uid;
+	buf->dt_gid = dic->di_gid;
+	buf->dt_size = XFS_ISIZE(ip);
+	buf->dt_dev = new_encode_dev(mp->m_ddev_targp->bt_dev);
+	buf->dt_atime = VFS_I(ip)->i_atime.tv_sec;
+	buf->dt_mtime = dic->di_mtime.t_sec;
+	buf->dt_ctime = dic->di_ctime.t_sec;
+	buf->dt_xfs_xflags = xfs_ip2dmflags(ip);
+	buf->dt_xfs_extsize = dic->di_extsize << mp->m_sb.sb_blocklog;
+	buf->dt_xfs_extents = dic->di_nextents;
+	buf->dt_xfs_aextents = dic->di_anextents;
+	buf->dt_xfs_igen = dic->di_gen;
+	buf->dt_xfs_dmstate = dic->di_dmstate;
+
+	switch (dic->di_format) {
+	case XFS_DINODE_FMT_DEV:
+		buf->dt_rdev = ip->i_df.if_u2.if_rdev;
+		buf->dt_blksize = BLKDEV_IOSIZE;
+		buf->dt_blocks = 0;
+		break;
+	case XFS_DINODE_FMT_LOCAL:
+	case XFS_DINODE_FMT_UUID:
+		buf->dt_rdev = 0;
+		buf->dt_blksize = mp->m_sb.sb_blocksize;
+		buf->dt_blocks = 0;
+		break;
+	case XFS_DINODE_FMT_EXTENTS:
+	case XFS_DINODE_FMT_BTREE:
+		buf->dt_rdev = 0;
+		buf->dt_blksize = mp->m_sb.sb_blocksize;
+		buf->dt_blocks = XFS_FSB_TO_BB(mp,
+				(dic->di_nblocks + ip->i_delayed_blks));
+		break;
+	}
+
+	memset(&buf->dt_pad1, 0, sizeof(buf->dt_pad1));
+	memset(&buf->dt_pad2, 0, sizeof(buf->dt_pad2));
+	memset(&buf->dt_pad3, 0, sizeof(buf->dt_pad3));
+
+	/* Finally fill in the DMAPI specific fields */
+	buf->dt_pers = 0;
+	buf->dt_change = 0;
+	buf->dt_nevents = DM_EVENT_MAX;
+	buf->dt_emask = dic->di_dmevmask;
+	buf->dt_dtime = dic->di_ctime.t_sec;
+	/* Set if one of READ, WRITE or TRUNCATE bits is set in emask */
+	buf->dt_pmanreg = (DMEV_ISSET(DM_EVENT_READ, buf->dt_emask) ||
+			DMEV_ISSET(DM_EVENT_WRITE, buf->dt_emask) ||
+			DMEV_ISSET(DM_EVENT_TRUNCATE, buf->dt_emask)) ? 1 : 0;
+}
+
+/*
+ * Take the handle and put it at the end of a dm_xstat buffer.
+ * dt_compname is unused in bulkstat - so we zero it out.
+ * Finally, update link in dm_xstat_t to point to next struct.
+ */
+STATIC void
+xfs_dm_handle_to_xstat(
+	dm_xstat_t	*xbuf,
+	size_t		xstat_sz,
+	dm_handle_t	*handle,
+	size_t		handle_sz)
+{
+	dm_stat_t	*sbuf = &xbuf->dx_statinfo;
+
+	memcpy(xbuf + 1, handle, handle_sz);
+	sbuf->dt_handle.vd_offset = (ssize_t) sizeof(dm_xstat_t);
+	sbuf->dt_handle.vd_length = (size_t) DM_HSIZE(*handle);
+	memset(&sbuf->dt_compname, 0, sizeof(dm_vardata_t));
+	sbuf->_link = xstat_sz;
+}
+
+STATIC int
+xfs_dm_bulkall_iget_one(
+	xfs_mount_t	*mp,
+	xfs_ino_t	ino,
+	xfs_daddr_t	bno,
+	int		*value_lenp,
+	dm_xstat_t	*xbuf,
+	u_int		*xstat_szp,
+	char		*attr_name,
+	caddr_t		attr_buf)
+{
+	xfs_inode_t	*ip;
+	dm_handle_t	handle;
+	u_int		xstat_sz = *xstat_szp;
+	int		value_len = *value_lenp;
+	int		error;
+
+	error = xfs_iget(mp, NULL, ino,
+			 XFS_IGET_BULKSTAT, XFS_ILOCK_SHARED, &ip, bno);
+	if (error)
+		return error;
+
+	xfs_ip_to_stat(mp, ino, ip, &xbuf->dx_statinfo);
+	dm_ip_to_handle(&ip->i_vnode, &handle);
+	xfs_dm_handle_to_xstat(xbuf, xstat_sz, &handle, sizeof(handle));
+
+	/* Drop ILOCK_SHARED for call to xfs_attr_get */
+	xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+	memset(&xbuf->dx_attrdata, 0, sizeof(dm_vardata_t));
+	error = xfs_attr_get(ip, attr_name, attr_buf, &value_len, ATTR_ROOT);
+	iput(&ip->i_vnode);
+
+	DM_EA_XLATE_ERR(error);
+	if (error && (error != ENOATTR)) {
+		if (error == E2BIG)
+			error = ENOMEM;
+		return error;
+	}
+
+	/* How much space was in the attr? */
+	if (error != ENOATTR) {
+		xbuf->dx_attrdata.vd_offset = xstat_sz;
+		xbuf->dx_attrdata.vd_length = value_len;
+		xstat_sz += (value_len+(DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1);
+	}
+	*xstat_szp = xbuf->dx_statinfo._link = xstat_sz;
+	*value_lenp = value_len;
+	return 0;
+}
+
+
+STATIC int
+xfs_dm_inline_attr(
+	xfs_mount_t	*mp,
+	xfs_dinode_t	*dip,
+	char		*attr_name,
+	caddr_t		attr_buf,
+	int		*value_lenp)
+{
+	if (dip->di_aformat == XFS_DINODE_FMT_LOCAL) {
+		xfs_attr_shortform_t	*sf;
+		xfs_attr_sf_entry_t	*sfe;
+		unsigned int		namelen = strlen(attr_name);
+		unsigned int		valuelen = *value_lenp;
+		int			i;
+
+		sf = (xfs_attr_shortform_t *)XFS_DFORK_APTR(dip);
+		sfe = &sf->list[0];
+		for (i = 0; i < sf->hdr.count;
+				sfe = XFS_ATTR_SF_NEXTENTRY(sfe), i++) {
+			if (sfe->namelen != namelen)
+				continue;
+			if (!(sfe->flags & XFS_ATTR_ROOT))
+				continue;
+			if (memcmp(attr_name, sfe->nameval, namelen) != 0)
+				continue;
+			if (valuelen < sfe->valuelen)
+				return ERANGE;
+			valuelen = sfe->valuelen;
+			memcpy(attr_buf, &sfe->nameval[namelen], valuelen);
+			*value_lenp = valuelen;
+			return 0;
+		}
+	}
+	*value_lenp = 0;
+	return ENOATTR;
+}
+
+STATIC void
+dm_dip_to_handle(
+	xfs_ino_t	ino,
+	xfs_dinode_t	*dip,
+	dm_fsid_t	*fsid,
+	dm_handle_t	*handlep)
+{
+	dm_fid_t	fid;
+	int		hsize;
+
+	fid.dm_fid_len = sizeof(struct dm_fid) - sizeof(fid.dm_fid_len);
+	fid.dm_fid_pad = 0;
+	fid.dm_fid_ino = ino;
+	fid.dm_fid_gen = be32_to_cpu(dip->di_gen);
+
+	memcpy(&handlep->ha_fsid, fsid, sizeof(*fsid));
+	memcpy(&handlep->ha_fid, &fid, fid.dm_fid_len + sizeof(fid.dm_fid_len));
+	hsize = DM_HSIZE(*handlep);
+	memset((char *)handlep + hsize, 0, sizeof(*handlep) - hsize);
+}
+
+STATIC int
+xfs_dm_bulkall_inline_one(
+	xfs_mount_t	*mp,
+	xfs_ino_t	ino,
+	xfs_dinode_t	*dip,
+	dm_fsid_t	*fsid,
+	int		*value_lenp,
+	dm_xstat_t	*xbuf,
+	u_int		*xstat_szp,
+	char		*attr_name,
+	caddr_t		attr_buf)
+{
+	dm_handle_t	handle;
+	u_int		xstat_sz = *xstat_szp;
+	int		value_len = *value_lenp;
+	int		error;
+
+	if (dip->di_mode == 0)
+		return ENOENT;
+
+	xfs_dip_to_stat(mp, ino, dip, &xbuf->dx_statinfo);
+	dm_dip_to_handle(ino, dip, fsid, &handle);
+	xfs_dm_handle_to_xstat(xbuf, xstat_sz, &handle, sizeof(handle));
+
+	memset(&xbuf->dx_attrdata, 0, sizeof(dm_vardata_t));
+	error = xfs_dm_inline_attr(mp, dip, attr_name, attr_buf, &value_len);
+	DM_EA_XLATE_ERR(error);
+	if (error && (error != ENOATTR)) {
+		if (error == E2BIG)
+			error = ENOMEM;
+		return error;
+	}
+
+	/* How much space was in the attr? */
+	if (error != ENOATTR) {
+		xbuf->dx_attrdata.vd_offset = xstat_sz;
+		xbuf->dx_attrdata.vd_length = value_len;
+		xstat_sz += (value_len+(DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1);
+	}
+	*xstat_szp = xbuf->dx_statinfo._link = xstat_sz;
+	*value_lenp = value_len;
+	return 0;
+}
+
+/*
+ * This is used by dm_get_bulkall().
+ * Given a inumber, it igets the inode and fills the given buffer
+ * with the dm_xstat structure for the file.
+ */
+STATIC int
+xfs_dm_bulkall_one(
+	xfs_mount_t	*mp,		/* mount point for filesystem */
+	xfs_ino_t	ino,		/* inode number to get data for */
+	void		__user *buffer,	/* buffer to place output in */
+	int		ubsize,		/* size of buffer */
+	void		*private_data,	/* my private data */
+	xfs_daddr_t	bno,		/* starting block of inode cluster */
+	int		*ubused,	/* amount of buffer we used */
+	void		*dibuff,	/* on-disk inode buffer */
+	int		*res)		/* bulkstat result code */
+{
+	dm_xstat_t	*xbuf;
+	u_int		xstat_sz;
+	int		error;
+	int		value_len;
+	int		kern_buf_sz;
+	int		attr_buf_sz;
+	caddr_t		attr_buf;
+	void __user	*attr_user_buf;
+	dm_bulkstat_one_t *dmb = (dm_bulkstat_one_t*)private_data;
+
+	/* Returns positive errors to XFS */
+
+	*res = BULKSTAT_RV_NOTHING;
+
+	if (!buffer || xfs_internal_inum(mp, ino))
+		return EINVAL;
+
+	xstat_sz = DM_STAT_SIZE(*xbuf, 0);
+	xstat_sz = (xstat_sz + (DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1);
+	if (xstat_sz > ubsize)
+		return ENOMEM;
+
+	kern_buf_sz = xstat_sz;
+	xbuf = kmem_alloc(kern_buf_sz, KM_SLEEP);
+
+	/* Determine place to drop attr value, and available space. */
+	value_len = ubsize - xstat_sz;
+	if (value_len > ATTR_MAX_VALUELEN)
+		value_len = ATTR_MAX_VALUELEN;
+
+	attr_user_buf = buffer + xstat_sz;
+	attr_buf_sz = value_len;
+	attr_buf = kmem_alloc(attr_buf_sz, KM_SLEEP);
+
+	if (!dibuff)
+		error = xfs_dm_bulkall_iget_one(mp, ino, bno,
+						&value_len, xbuf, &xstat_sz,
+						dmb->attrname.dan_chars,
+						attr_buf);
+	else
+		error = xfs_dm_bulkall_inline_one(mp, ino,
+						  (xfs_dinode_t *)dibuff,
+						  &dmb->fsid,
+						  &value_len, xbuf, &xstat_sz,
+						  dmb->attrname.dan_chars,
+						  attr_buf);
+	if (error)
+		goto out_free_buffers;
+
+	if (copy_to_user(buffer, xbuf, kern_buf_sz)) {
+		error = EFAULT;
+		goto out_free_buffers;
+	}
+	if (copy_to_user(attr_user_buf, attr_buf, value_len)) {
+		error = EFAULT;
+		goto out_free_buffers;
+	}
+
+	kmem_free(attr_buf);
+	kmem_free(xbuf);
+
+	*res = BULKSTAT_RV_DIDONE;
+	if (ubused)
+		*ubused = xstat_sz;
+	dmb->laststruct = buffer;
+	return 0;
+
+ out_free_buffers:
+	kmem_free(attr_buf);
+	kmem_free(xbuf);
+	return error;
+}
+
+/*
+ * Take the handle and put it at the end of a dm_stat buffer.
+ * dt_compname is unused in bulkstat - so we zero it out.
+ * Finally, update link in dm_stat_t to point to next struct.
+ */
+STATIC void
+xfs_dm_handle_to_stat(
+	dm_stat_t	*sbuf,
+	size_t		stat_sz,
+	dm_handle_t	*handle,
+	size_t		handle_sz)
+{
+	memcpy(sbuf + 1, handle, handle_sz);
+	sbuf->dt_handle.vd_offset = (ssize_t) sizeof(dm_stat_t);
+	sbuf->dt_handle.vd_length = (size_t) DM_HSIZE(*handle);
+	memset(&sbuf->dt_compname, 0, sizeof(dm_vardata_t));
+	sbuf->_link = stat_sz;
+}
+
+STATIC int
+xfs_dm_bulkattr_iget_one(
+	xfs_mount_t	*mp,
+	xfs_ino_t	ino,
+	xfs_daddr_t	bno,
+	dm_stat_t	*sbuf,
+	u_int		stat_sz)
+{
+	xfs_inode_t	*ip;
+	dm_handle_t	handle;
+	int		error;
+
+	error = xfs_iget(mp, NULL, ino,
+			 XFS_IGET_BULKSTAT, XFS_ILOCK_SHARED, &ip, bno);
+	if (error)
+		return error;
+
+	xfs_ip_to_stat(mp, ino, ip, sbuf);
+	dm_ip_to_handle(&ip->i_vnode, &handle);
+	xfs_dm_handle_to_stat(sbuf, stat_sz, &handle, sizeof(handle));
+
+	xfs_iput(ip, XFS_ILOCK_SHARED);
+	return 0;
+}
+
+STATIC int
+xfs_dm_bulkattr_inline_one(
+	xfs_mount_t	*mp,
+	xfs_ino_t	ino,
+	xfs_dinode_t	*dip,
+	dm_fsid_t	*fsid,
+	dm_stat_t	*sbuf,
+	u_int		stat_sz)
+{
+	dm_handle_t	handle;
+
+	if (dip->di_mode == 0)
+		return ENOENT;
+	xfs_dip_to_stat(mp, ino, dip, sbuf);
+	dm_dip_to_handle(ino, dip, fsid, &handle);
+	xfs_dm_handle_to_stat(sbuf, stat_sz, &handle, sizeof(handle));
+	return 0;
+}
+
+/*
+ * This is used by dm_get_bulkattr().
+ * Given a inumber, it igets the inode and fills the given buffer
+ * with the dm_stat structure for the file.
+ */
+STATIC int
+xfs_dm_bulkattr_one(
+	xfs_mount_t	*mp,		/* mount point for filesystem */
+	xfs_ino_t	ino,		/* inode number to get data for */
+	void		__user *buffer,	/* buffer to place output in */
+	int		ubsize,		/* size of buffer */
+	void		*private_data,	/* my private data */
+	xfs_daddr_t	bno,		/* starting block of inode cluster */
+	int		*ubused,	/* amount of buffer we used */
+	void		*dibuff,	/* on-disk inode buffer */
+	int		*res)		/* bulkstat result code */
+{
+	dm_stat_t	*sbuf;
+	u_int		stat_sz;
+	int		error;
+	dm_bulkstat_one_t *dmb = (dm_bulkstat_one_t*)private_data;
+
+	/* Returns positive errors to XFS */
+
+	*res = BULKSTAT_RV_NOTHING;
+
+	if (!buffer || xfs_internal_inum(mp, ino))
+		return EINVAL;
+
+	stat_sz = DM_STAT_SIZE(*sbuf, 0);
+	stat_sz = (stat_sz+(DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1);
+	if (stat_sz > ubsize)
+		return ENOMEM;
+
+	sbuf = kmem_alloc(stat_sz, KM_SLEEP);
+
+	if (!dibuff)
+		error = xfs_dm_bulkattr_iget_one(mp, ino, bno, sbuf, stat_sz);
+	else
+		error = xfs_dm_bulkattr_inline_one(mp, ino,
+						   (xfs_dinode_t *)dibuff,
+						   &dmb->fsid, sbuf, stat_sz);
+	if (error)
+		goto out_free_buffer;
+
+	if (copy_to_user(buffer, sbuf, stat_sz)) {
+		error = EFAULT;
+		goto out_free_buffer;
+	}
+
+	kmem_free(sbuf);
+	*res = BULKSTAT_RV_DIDONE;
+	if (ubused)
+		*ubused = stat_sz;
+	dmb->laststruct = buffer;
+	return 0;
+
+ out_free_buffer:
+	kmem_free(sbuf);
+	return error;
+}
+
+/* xfs_dm_f_get_eventlist - return the dm_eventset_t mask for inode ip. */
+
+STATIC int
+xfs_dm_f_get_eventlist(
+	xfs_inode_t	*ip,
+	dm_right_t	right,
+	u_int		nelem,
+	dm_eventset_t	*eventsetp,		/* in kernel space! */
+	u_int		*nelemp)		/* in kernel space! */
+{
+	dm_eventset_t	eventset;
+
+	if (right < DM_RIGHT_SHARED)
+		return(EACCES);
+
+	/* Note that we MUST return a regular file's managed region bits as
+	   part of the mask because dm_get_eventlist is supposed to return the
+	   union of all managed region flags in those bits.  Since we only
+	   support one region, we can just return the bits as they are.	 For
+	   all other object types, the bits will already be zero.  Handy, huh?
+	*/
+
+	eventset = ip->i_d.di_dmevmask;
+
+	/* Now copy the event mask and event count back to the caller.	We
+	   return the lesser of nelem and DM_EVENT_MAX.
+	*/
+
+	if (nelem > DM_EVENT_MAX)
+		nelem = DM_EVENT_MAX;
+	eventset &= (1 << nelem) - 1;
+
+	*eventsetp = eventset;
+	*nelemp = nelem;
+	return(0);
+}
+
+
+/* xfs_dm_f_set_eventlist - update the dm_eventset_t mask in the inode vp.  Only the
+   bits from zero to maxevent-1 are being replaced; higher bits are preserved.
+*/
+
+STATIC int
+xfs_dm_f_set_eventlist(
+	xfs_inode_t	*ip,
+	dm_right_t	right,
+	dm_eventset_t	*eventsetp,	/* in kernel space! */
+	u_int		maxevent)
+{
+	dm_eventset_t	eventset;
+	dm_eventset_t	max_mask;
+	dm_eventset_t	valid_events;
+	xfs_trans_t	*tp;
+	xfs_mount_t	*mp;
+	int		error;
+
+	if (right < DM_RIGHT_EXCL)
+		return(EACCES);
+
+	eventset = *eventsetp;
+	if (maxevent >= sizeof(ip->i_d.di_dmevmask) * NBBY)
+		return(EINVAL);
+	max_mask = (1 << maxevent) - 1;
+
+	if (S_ISDIR(ip->i_d.di_mode)) {
+		valid_events = DM_XFS_VALID_DIRECTORY_EVENTS;
+	} else {	/* file or symlink */
+		valid_events = DM_XFS_VALID_FILE_EVENTS;
+	}
+	if ((eventset & max_mask) & ~valid_events)
+		return(EINVAL);
+
+	/* Adjust the event mask so that the managed region bits will not
+	   be altered.
+	*/
+
+	max_mask &= ~(1 <<DM_EVENT_READ);	/* preserve current MR bits */
+	max_mask &= ~(1 <<DM_EVENT_WRITE);
+	max_mask &= ~(1 <<DM_EVENT_TRUNCATE);
+
+	mp = ip->i_mount;
+	tp = xfs_trans_alloc(mp, XFS_TRANS_SET_DMATTRS);
+	error = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES(mp), 0, 0, 0);
+	if (error) {
+		xfs_trans_cancel(tp, 0);
+		return(error);
+	}
+	xfs_ilock(ip, XFS_ILOCK_EXCL);
+	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+
+	ip->i_d.di_dmevmask = (eventset & max_mask) | (ip->i_d.di_dmevmask & ~max_mask);
+
+	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+	igrab(&ip->i_vnode);
+	xfs_trans_commit(tp, 0);
+
+	return(0);
+}
+
+
+/* xfs_dm_fs_get_eventlist - return the dm_eventset_t mask for filesystem vfsp. */
+
+STATIC int
+xfs_dm_fs_get_eventlist(
+	xfs_mount_t	*mp,
+	dm_right_t	right,
+	u_int		nelem,
+	dm_eventset_t	*eventsetp,		/* in kernel space! */
+	u_int		*nelemp)		/* in kernel space! */
+{
+	dm_eventset_t	eventset;
+
+	if (right < DM_RIGHT_SHARED)
+		return(EACCES);
+
+	eventset = mp->m_dmevmask;
+
+	/* Now copy the event mask and event count back to the caller.	We
+	   return the lesser of nelem and DM_EVENT_MAX.
+	*/
+
+	if (nelem > DM_EVENT_MAX)
+		nelem = DM_EVENT_MAX;
+	eventset &= (1 << nelem) - 1;
+
+	*eventsetp = eventset;
+	*nelemp = nelem;
+	return(0);
+}
+
+
+/* xfs_dm_fs_set_eventlist - update the dm_eventset_t mask in the mount structure for
+   filesystem vfsp.  Only the bits from zero to maxevent-1 are being replaced;
+   higher bits are preserved.
+*/
+
+STATIC int
+xfs_dm_fs_set_eventlist(
+	xfs_mount_t	*mp,
+	dm_right_t	right,
+	dm_eventset_t	*eventsetp,	/* in kernel space! */
+	u_int		maxevent)
+{
+	dm_eventset_t	eventset;
+	dm_eventset_t	max_mask;
+
+	if (right < DM_RIGHT_EXCL)
+		return(EACCES);
+
+	eventset = *eventsetp;
+
+	if (maxevent >= sizeof(mp->m_dmevmask) * NBBY)
+		return(EINVAL);
+	max_mask = (1 << maxevent) - 1;
+
+	if ((eventset & max_mask) & ~DM_XFS_VALID_FS_EVENTS)
+		return(EINVAL);
+
+	mp->m_dmevmask = (eventset & max_mask) | (mp->m_dmevmask & ~max_mask);
+	return(0);
+}
+
+
+/* Code in this routine must exactly match the logic in xfs_diordwr() in
+   order for this to work!
+*/
+
+STATIC int
+xfs_dm_direct_ok(
+	xfs_inode_t	*ip,
+	dm_off_t	off,
+	dm_size_t	len,
+	void		__user *bufp)
+{
+	xfs_mount_t	*mp;
+
+	mp = ip->i_mount;
+
+	/* Realtime files can ONLY do direct I/O. */
+
+	if (XFS_IS_REALTIME_INODE(ip))
+		return(1);
+
+	/* If direct I/O is disabled, or if the request is too small, use
+	   buffered I/O.
+	*/
+
+	if (!dm_min_dio_xfer || len < dm_min_dio_xfer)
+		return(0);
+
+#if 0
+	/* If the request is not well-formed or is too large, use
+	   buffered I/O.
+	*/
+
+	if ((__psint_t)bufp & scache_linemask)	/* if buffer not aligned */
+		return(0);
+	if (off & mp->m_blockmask)		/* if file offset not aligned */
+		return(0);
+	if (len & mp->m_blockmask)		/* if xfer length not aligned */
+		return(0);
+	if (len > ctooff(v.v_maxdmasz - 1))	/* if transfer too large */
+		return(0);
+
+	/* A valid direct I/O candidate. */
+
+	return(1);
+#else
+	return(0);
+#endif
+}
+
+
+/* We need to be able to select various combinations of O_NONBLOCK,
+   O_DIRECT, and O_SYNC, yet we don't have a file descriptor and we don't have
+   the file's pathname.	 All we have is a handle.
+*/
+
+STATIC int
+xfs_dm_rdwr(
+	struct inode	*inode,
+	uint		fflag,
+	mode_t		fmode,
+	dm_off_t	off,
+	dm_size_t	len,
+	void		__user *bufp,
+	int		*rvp)
+{
+	const struct cred *cred = current_cred();
+	xfs_inode_t	*ip = XFS_I(inode);
+	int		error;
+	int		oflags;
+	ssize_t		xfer;
+	struct file	*file;
+	struct dentry	*dentry;
+
+	if ((off < 0) || (off > i_size_read(inode)) || !S_ISREG(inode->i_mode))
+		return EINVAL;
+
+	if (fmode & FMODE_READ) {
+		oflags = O_RDONLY;
+	} else {
+		oflags = O_WRONLY;
+	}
+
+	/*
+	 * Build file descriptor flags and I/O flags.  O_NONBLOCK is needed so
+	 * that we don't block on mandatory file locks. This is an invisible IO,
+	 * don't change the atime.
+	 */
+
+	oflags |= O_LARGEFILE | O_NONBLOCK | O_NOATIME;
+	if (xfs_dm_direct_ok(ip, off, len, bufp))
+		oflags |= O_DIRECT;
+
+	if (fflag & O_SYNC)
+		oflags |= O_SYNC;
+
+	if (inode->i_fop == NULL) {
+		/* no iput; caller did get, and will do put */
+		return EINVAL;
+	}
+
+	igrab(inode);
+
+	dentry = d_obtain_alias(inode);
+	if (dentry == NULL) {
+		iput(inode);
+		return ENOMEM;
+	}
+
+	file = dentry_open(dentry, mntget(ip->i_mount->m_vfsmount), oflags,
+			   cred);
+	if (IS_ERR(file)) {
+		return -PTR_ERR(file);
+	}
+	file->f_mode |= FMODE_NOCMTIME;
+
+	if (fmode & FMODE_READ) {
+		xfer = file->f_op->read(file, bufp, len, (loff_t*)&off);
+	} else {
+		xfer = file->f_op->write(file, bufp, len, (loff_t*)&off);
+	}
+
+	if (xfer >= 0) {
+		*rvp = xfer;
+		error = 0;
+	} else {
+		/* xfs_read/xfs_write return negative error--flip it */
+		error = -(int)xfer;
+	}
+
+	fput(file);
+	return error;
+}
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_clear_inherit(
+	struct inode	*inode,
+	dm_right_t	right,
+	dm_attrname_t	__user *attrnamep)
+{
+	return(-ENOSYS); /* Return negative error to DMAPI */
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_create_by_handle(
+	struct inode	*inode,
+	dm_right_t	right,
+	void		__user *hanp,
+	size_t		hlen,
+	char		__user *cname)
+{
+	return(-ENOSYS); /* Return negative error to DMAPI */
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_downgrade_right(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		type)		/* DM_FSYS_OBJ or zero */
+{
+#ifdef	DEBUG_RIGHTS
+	char		buffer[sizeof(dm_handle_t) * 2 + 1];
+
+	if (!xfs_vp_to_hexhandle(inode, type, buffer)) {
+		printf("dm_downgrade_right: old %d new %d type %d handle %s\n",
+			right, DM_RIGHT_SHARED, type, buffer);
+	} else {
+		printf("dm_downgrade_right: old %d new %d type %d handle "
+			"<INVALID>\n", right, DM_RIGHT_SHARED, type);
+	}
+#endif	/* DEBUG_RIGHTS */
+	return(0);
+}
+
+
+/* Note: xfs_dm_get_allocinfo() makes no attempt to coalesce two adjacent
+   extents when both are of type DM_EXTENT_RES; this is left to the caller.
+   XFS guarantees that there will never be two adjacent DM_EXTENT_HOLE extents.
+
+   In order to provide the caller with all extents in a file including
+   those beyond the file's last byte offset, we have to use the xfs_bmapi()
+   interface.
+*/
+
+STATIC int
+xfs_dm_get_allocinfo_rvp(
+	struct inode	*inode,
+	dm_right_t	right,
+	dm_off_t	__user	*offp,
+	u_int		nelem,
+	dm_extent_t	__user *extentp,
+	u_int		__user *nelemp,
+	int		*rvp)
+{
+	xfs_inode_t	*ip = XFS_I(inode);
+	xfs_mount_t	*mp;		/* file system mount point */
+	xfs_fileoff_t	fsb_offset;
+	xfs_filblks_t	fsb_length;
+	dm_off_t	startoff;
+	int		elem;
+	xfs_bmbt_irec_t *bmp = NULL;
+	u_int		bmpcnt = 50;
+	u_int		bmpsz = sizeof(xfs_bmbt_irec_t) * bmpcnt;
+	int		error = 0;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_SHARED)
+		return(-EACCES);
+
+	if ((inode->i_mode & S_IFMT) != S_IFREG)
+		return(-EINVAL);
+
+	if (copy_from_user( &startoff, offp, sizeof(startoff)))
+		return(-EFAULT);
+
+	mp = ip->i_mount;
+	ASSERT(mp);
+
+	if (startoff > XFS_MAXIOFFSET(mp))
+		return(-EINVAL);
+
+	if (nelem == 0)
+		return(-EINVAL);
+
+	/* Convert the caller's starting offset into filesystem allocation
+	   units as required by xfs_bmapi().  Round the offset down so that
+	   it is sure to be included in the reply.
+	*/
+
+	fsb_offset = XFS_B_TO_FSBT(mp, startoff);
+	fsb_length = XFS_B_TO_FSB(mp, XFS_MAXIOFFSET(mp)) - fsb_offset;
+	elem = 0;
+
+	if (fsb_length)
+		bmp = kmem_alloc(bmpsz, KM_SLEEP);
+
+	while (fsb_length && elem < nelem) {
+		dm_extent_t	extent;
+		xfs_filblks_t	fsb_bias;
+		dm_size_t	bias;
+		int		lock;
+		int		num;
+		int		i;
+
+		/* Compute how many getbmap structures to use on the xfs_bmapi
+		   call.
+		*/
+
+		num = MIN((u_int)(nelem - elem), bmpcnt);
+
+		xfs_ilock(ip, XFS_IOLOCK_SHARED);
+		lock = xfs_ilock_map_shared(ip);
+
+		error = xfs_bmapi(NULL, ip, fsb_offset, fsb_length,
+			XFS_BMAPI_ENTIRE, NULL, 0, bmp, &num, NULL, NULL);
+
+		xfs_iunlock_map_shared(ip, lock);
+		xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+
+		if (error) {
+			error = -error; /* Return negative error to DMAPI */
+			goto finish_out;
+		}
+
+		/* Fill in the caller's extents, adjusting the bias in the
+		   first entry if necessary.
+		*/
+
+		for (i = 0; i < num; i++, extentp++) {
+			bias = startoff - XFS_FSB_TO_B(mp, bmp[i].br_startoff);
+			extent.ex_offset = startoff;
+			extent.ex_length =
+				XFS_FSB_TO_B(mp, bmp[i].br_blockcount) - bias;
+			if (bmp[i].br_startblock == HOLESTARTBLOCK) {
+				extent.ex_type = DM_EXTENT_HOLE;
+			} else {
+				extent.ex_type = DM_EXTENT_RES;
+			}
+			startoff = extent.ex_offset + extent.ex_length;
+
+			if (copy_to_user( extentp, &extent, sizeof(extent))) {
+				error = -EFAULT;
+				goto finish_out;
+			}
+
+			fsb_bias = fsb_offset - bmp[i].br_startoff;
+			fsb_offset += bmp[i].br_blockcount - fsb_bias;
+			fsb_length -= bmp[i].br_blockcount - fsb_bias;
+			elem++;
+		}
+	}
+
+	if (fsb_length == 0) {
+		startoff = 0;
+	}
+	if (copy_to_user( offp, &startoff, sizeof(startoff))) {
+		error = -EFAULT;
+		goto finish_out;
+	}
+
+	if (copy_to_user( nelemp, &elem, sizeof(elem))) {
+		error = -EFAULT;
+		goto finish_out;
+	}
+
+	*rvp = (fsb_length == 0 ? 0 : 1);
+
+finish_out:
+	if (bmp)
+		kmem_free(bmp);
+	return(error);
+}
+
+
+STATIC int
+xfs_dm_zero_xstatinfo_link(
+	dm_xstat_t __user	*dxs)
+{
+	dm_xstat_t		*ldxs;
+	int			error = 0;
+
+	if (!dxs)
+		return 0;
+	ldxs = kmalloc(sizeof(*ldxs), GFP_KERNEL);
+	if (!ldxs)
+		return -ENOMEM;
+	if (copy_from_user(ldxs, dxs, sizeof(*dxs))) {
+		error = -EFAULT;
+	} else {
+		ldxs->dx_statinfo._link = 0;
+		if (copy_to_user(dxs, ldxs, sizeof(*dxs)))
+			error = -EFAULT;
+	}
+	kfree(ldxs);
+	return error;
+}
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_get_bulkall_rvp(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		mask,
+	dm_attrname_t	__user *attrnamep,
+	dm_attrloc_t	__user *locp,
+	size_t		buflen,
+	void		__user *bufp,	/* address of buffer in user space */
+	size_t		__user *rlenp,	/* user space address */
+	int		*rvalp)
+{
+	int		error, done;
+	int		nelems;
+	u_int		statstruct_sz;
+	dm_attrloc_t	loc;
+	xfs_mount_t	*mp = XFS_I(inode)->i_mount;
+	dm_attrname_t	attrname;
+	dm_bulkstat_one_t dmb;
+
+	/* Returns negative errors to DMAPI */
+
+	if (copy_from_user(&attrname, attrnamep, sizeof(attrname)) ||
+	    copy_from_user(&loc, locp, sizeof(loc)))
+		return -EFAULT;
+
+	if (attrname.an_chars[0] == '\0')
+		return(-EINVAL);
+
+	if (right < DM_RIGHT_SHARED)
+		return(-EACCES);
+
+	/* Because we will write directly to the user's buffer, make sure that
+	   the buffer is properly aligned.
+	*/
+
+	if (((unsigned long)bufp & (DM_STAT_ALIGN - 1)) != 0)
+		return(-EFAULT);
+
+	/* Size of the handle is constant for this function.
+	 * If there are no files with attributes, then this will be the
+	 * maximum number of inodes we can get.
+	 */
+
+	statstruct_sz = DM_STAT_SIZE(dm_xstat_t, 0);
+	statstruct_sz = (statstruct_sz+(DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1);
+
+	nelems = buflen / statstruct_sz;
+	if (nelems < 1) {
+		if (put_user( statstruct_sz, rlenp ))
+			return(-EFAULT);
+		return(-E2BIG);
+	}
+
+	/* Build the on-disk version of the attribute name. */
+	strcpy(dmb.attrname.dan_chars, dmattr_prefix);
+	strncpy(&dmb.attrname.dan_chars[DMATTR_PREFIXLEN],
+		attrname.an_chars, DM_ATTR_NAME_SIZE + 1);
+	dmb.attrname.dan_chars[sizeof(dmb.attrname.dan_chars) - 1] = '\0';
+
+	/*
+	 * fill the buffer with dm_xstat_t's
+	 */
+
+	dmb.laststruct = NULL;
+	memcpy(&dmb.fsid, mp->m_fixedfsid, sizeof(dm_fsid_t));
+	error = xfs_bulkstat(mp, (xfs_ino_t *)&loc, &nelems,
+			     xfs_dm_bulkall_one, (void*)&dmb, statstruct_sz,
+			     bufp, BULKSTAT_FG_INLINE, &done);
+	if (error)
+		return(-error); /* Return negative error to DMAPI */
+
+	*rvalp = !done ? 1 : 0;
+
+	if (put_user( statstruct_sz * nelems, rlenp ))
+		return(-EFAULT);
+
+	if (copy_to_user( locp, &loc, sizeof(loc)))
+		return(-EFAULT);
+	/*
+	 *  If we didn't do any, we must not have any more to do.
+	 */
+	if (nelems < 1)
+		return(0);
+	/*
+	 * Set _link in the last struct to zero
+	 */
+	return xfs_dm_zero_xstatinfo_link((dm_xstat_t __user *)dmb.laststruct);
+}
+
+
+STATIC int
+xfs_dm_zero_statinfo_link(
+	dm_stat_t __user	*dxs)
+{
+	dm_stat_t		*ldxs;
+	int			error = 0;
+
+	if (!dxs)
+		return 0;
+	ldxs = kmalloc(sizeof(*ldxs), GFP_KERNEL);
+	if (!ldxs)
+		return -ENOMEM;
+	if (copy_from_user(ldxs, dxs, sizeof(*dxs))) {
+		error = -EFAULT;
+	} else {
+		ldxs->_link = 0;
+		if (copy_to_user(dxs, ldxs, sizeof(*dxs)))
+			error = -EFAULT;
+	}
+	kfree(ldxs);
+	return error;
+}
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_get_bulkattr_rvp(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		mask,
+	dm_attrloc_t	__user *locp,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp,
+	int		*rvalp)
+{
+	int		error, done;
+	int		nelems;
+	u_int		statstruct_sz;
+	dm_attrloc_t	loc;
+	xfs_mount_t	*mp = XFS_I(inode)->i_mount;
+	dm_bulkstat_one_t dmb;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_SHARED)
+		return(-EACCES);
+
+	if (copy_from_user( &loc, locp, sizeof(loc)))
+		return(-EFAULT);
+
+	/* Because we will write directly to the user's buffer, make sure that
+	   the buffer is properly aligned.
+	*/
+
+	if (((unsigned long)bufp & (DM_STAT_ALIGN - 1)) != 0)
+		return(-EFAULT);
+
+	/* size of the handle is constant for this function */
+
+	statstruct_sz = DM_STAT_SIZE(dm_stat_t, 0);
+	statstruct_sz = (statstruct_sz+(DM_STAT_ALIGN-1)) & ~(DM_STAT_ALIGN-1);
+
+	nelems = buflen / statstruct_sz;
+	if (nelems < 1) {
+		if (put_user( statstruct_sz, rlenp ))
+			return(-EFAULT);
+		return(-E2BIG);
+	}
+
+	dmb.laststruct = NULL;
+	memcpy(&dmb.fsid, mp->m_fixedfsid, sizeof(dm_fsid_t));
+	error = xfs_bulkstat(mp, (xfs_ino_t *)&loc, &nelems,
+				xfs_dm_bulkattr_one, (void*)&dmb,
+				statstruct_sz, bufp, BULKSTAT_FG_INLINE, &done);
+	if (error)
+		return(-error); /* Return negative error to DMAPI */
+
+	*rvalp = !done ? 1 : 0;
+
+	if (put_user( statstruct_sz * nelems, rlenp ))
+		return(-EFAULT);
+
+	if (copy_to_user( locp, &loc, sizeof(loc)))
+		return(-EFAULT);
+
+	/*
+	 *  If we didn't do any, we must not have any more to do.
+	 */
+	if (nelems < 1)
+		return(0);
+	/*
+	 * Set _link in the last struct to zero
+	 */
+	return xfs_dm_zero_statinfo_link((dm_stat_t __user *)dmb.laststruct);
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_get_config(
+	struct inode	*inode,
+	dm_right_t	right,
+	dm_config_t	flagname,
+	dm_size_t	__user *retvalp)
+{
+	dm_size_t	retval;
+
+	/* Returns negative errors to DMAPI */
+
+	switch (flagname) {
+	case DM_CONFIG_DTIME_OVERLOAD:
+	case DM_CONFIG_PERS_ATTRIBUTES:
+	case DM_CONFIG_PERS_EVENTS:
+	case DM_CONFIG_PERS_MANAGED_REGIONS:
+	case DM_CONFIG_PUNCH_HOLE:
+	case DM_CONFIG_WILL_RETRY:
+		retval = DM_TRUE;
+		break;
+
+	case DM_CONFIG_CREATE_BY_HANDLE:	/* these will never be done */
+	case DM_CONFIG_LOCK_UPGRADE:
+	case DM_CONFIG_PERS_INHERIT_ATTRIBS:
+		retval = DM_FALSE;
+		break;
+
+	case DM_CONFIG_BULKALL:
+		retval = DM_TRUE;
+		break;
+	case DM_CONFIG_MAX_ATTR_ON_DESTROY:
+		retval = DM_MAX_ATTR_BYTES_ON_DESTROY;
+		break;
+
+	case DM_CONFIG_MAX_ATTRIBUTE_SIZE:
+		retval = ATTR_MAX_VALUELEN;
+		break;
+
+	case DM_CONFIG_MAX_HANDLE_SIZE:
+		retval = DM_MAX_HANDLE_SIZE;
+		break;
+
+	case DM_CONFIG_MAX_MANAGED_REGIONS:
+		retval = 1;
+		break;
+
+	case DM_CONFIG_TOTAL_ATTRIBUTE_SPACE:
+		retval = 0x7fffffff;	/* actually it's unlimited */
+		break;
+
+	default:
+		return(-EINVAL);
+	}
+
+	/* Copy the results back to the user. */
+
+	if (copy_to_user( retvalp, &retval, sizeof(retval)))
+		return(-EFAULT);
+	return(0);
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_get_config_events(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		nelem,
+	dm_eventset_t	__user *eventsetp,
+	u_int		__user *nelemp)
+{
+	dm_eventset_t	eventset;
+
+	/* Returns negative errors to DMAPI */
+
+	if (nelem == 0)
+		return(-EINVAL);
+
+	eventset = DM_XFS_SUPPORTED_EVENTS;
+
+	/* Now copy the event mask and event count back to the caller.	We
+	   return the lesser of nelem and DM_EVENT_MAX.
+	*/
+
+	if (nelem > DM_EVENT_MAX)
+		nelem = DM_EVENT_MAX;
+	eventset &= (1 << nelem) - 1;
+
+	if (copy_to_user( eventsetp, &eventset, sizeof(eventset)))
+		return(-EFAULT);
+
+	if (put_user(nelem, nelemp))
+		return(-EFAULT);
+	return(0);
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_get_destroy_dmattr(
+	struct inode	*inode,
+	dm_right_t	right,
+	dm_attrname_t  *attrnamep,
+	char 		**valuepp,
+	int		*vlenp)
+{
+	dm_dkattrname_t dkattrname;
+	int		alloc_size;
+	int		value_len;
+	char		*value;
+	int		error;
+
+	/* Returns negative errors to DMAPI */
+
+	*vlenp = -1;		/* assume failure by default */
+
+	if (attrnamep->an_chars[0] == '\0')
+		return(-EINVAL);
+
+	/* Build the on-disk version of the attribute name. */
+
+	strcpy(dkattrname.dan_chars, dmattr_prefix);
+	strncpy(&dkattrname.dan_chars[DMATTR_PREFIXLEN],
+		(char *)attrnamep->an_chars, DM_ATTR_NAME_SIZE + 1);
+	dkattrname.dan_chars[sizeof(dkattrname.dan_chars) - 1] = '\0';
+
+	/* xfs_attr_get will not return anything if the buffer is too small,
+	   and we don't know how big to make the buffer, so this may take
+	   two tries to get it right.  The initial try must use a buffer of
+	   at least XFS_BUG_KLUDGE bytes to prevent buffer overflow because
+	   of a bug in XFS.
+	*/
+
+	alloc_size = XFS_BUG_KLUDGE;
+	value = kmalloc(alloc_size, GFP_KERNEL);
+	if (value == NULL)
+		return(-ENOMEM);
+
+	error = xfs_attr_get(XFS_I(inode), dkattrname.dan_chars, value,
+							&value_len, ATTR_ROOT);
+	if (error == ERANGE) {
+		kfree(value);
+		alloc_size = value_len;
+		value = kmalloc(alloc_size, GFP_KERNEL);
+		if (value == NULL)
+			return(-ENOMEM);
+
+		error = xfs_attr_get(XFS_I(inode), dkattrname.dan_chars, value,
+					&value_len, ATTR_ROOT);
+	}
+	if (error) {
+		kfree(value);
+		DM_EA_XLATE_ERR(error);
+		return(-error); /* Return negative error to DMAPI */
+	}
+
+	/* The attribute exists and has a value.  Note that a value_len of
+	   zero is valid!
+	*/
+
+	if (value_len == 0) {
+		kfree(value);
+		*vlenp = 0;
+		return(0);
+	} else if (value_len > DM_MAX_ATTR_BYTES_ON_DESTROY) {
+		char	*value2;
+
+		value2 = kmalloc(DM_MAX_ATTR_BYTES_ON_DESTROY, GFP_KERNEL);
+		if (value2 == NULL) {
+			kfree(value);
+			return(-ENOMEM);
+		}
+		memcpy(value2, value, DM_MAX_ATTR_BYTES_ON_DESTROY);
+		kfree(value);
+		value = value2;
+		value_len = DM_MAX_ATTR_BYTES_ON_DESTROY;
+	}
+	*vlenp = value_len;
+	*valuepp = value;
+	return(0);
+}
+
+/* This code was taken from xfs_fcntl(F_DIOINFO) and modified slightly because
+   we don't have a flags parameter (no open file).
+   Taken from xfs_ioctl(XFS_IOC_DIOINFO) on Linux.
+*/
+
+STATIC int
+xfs_dm_get_dioinfo(
+	struct inode	*inode,
+	dm_right_t	right,
+	dm_dioinfo_t	__user *diop)
+{
+	dm_dioinfo_t	dio;
+	xfs_mount_t	*mp;
+	xfs_inode_t	*ip = XFS_I(inode);
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_SHARED)
+		return(-EACCES);
+
+	mp = ip->i_mount;
+
+	dio.d_miniosz = dio.d_mem = MIN_DIO_SIZE(mp);
+	dio.d_maxiosz = MAX_DIO_SIZE(mp);
+	dio.d_dio_only = DM_FALSE;
+
+	if (copy_to_user(diop, &dio, sizeof(dio)))
+		return(-EFAULT);
+	return(0);
+}
+
+typedef struct dm_readdir_cb {
+	xfs_mount_t		*mp;
+	char __user		*ubuf;
+	dm_stat_t __user	*lastbuf;
+	size_t			spaceleft;
+	size_t			nwritten;
+	int			error;
+	dm_stat_t		kstat;
+} dm_readdir_cb_t;
+
+STATIC int
+dm_filldir(void *__buf, const char *name, int namelen, loff_t offset,
+		u64 ino, unsigned int d_type)
+{
+	dm_readdir_cb_t *cb = __buf;
+	dm_stat_t	*statp = &cb->kstat;
+	size_t		len;
+	int		error;
+	int		needed;
+
+	/*
+	 * Make sure we have enough space.
+	 */
+        needed = dm_stat_size(namelen + 1);
+	if (cb->spaceleft < needed) {
+		cb->spaceleft = 0;
+		return -ENOSPC;
+	}
+
+	error = -EINVAL;
+	if (xfs_internal_inum(cb->mp, ino))
+		goto out_err;
+
+	memset(statp, 0, dm_stat_size(MAXNAMLEN));
+	error = -xfs_dm_bulkattr_iget_one(cb->mp, ino, 0,
+			statp, needed);
+	if (error)
+		goto out_err;
+
+	/*
+	 * On return from bulkstat_one(), stap->_link points
+	 * at the end of the handle in the stat structure.
+	 */
+	statp->dt_compname.vd_offset = statp->_link;
+	statp->dt_compname.vd_length = namelen + 1;
+
+	len = statp->_link;
+
+	/* Word-align the record */
+	statp->_link = dm_stat_align(len + namelen + 1);
+
+	error = -EFAULT;
+	if (copy_to_user(cb->ubuf, statp, len))
+		goto out_err;
+	if (copy_to_user(cb->ubuf + len, name, namelen))
+		goto out_err;
+	if (put_user(0, cb->ubuf + len + namelen))
+		goto out_err;
+
+	cb->lastbuf = (dm_stat_t __user *)cb->ubuf;
+	cb->spaceleft -= statp->_link;
+	cb->nwritten += statp->_link;
+	cb->ubuf += statp->_link;
+
+	return 0;
+
+ out_err:
+	cb->error = error;
+	return error;
+}
+
+/* Returns negative errors to DMAPI */
+STATIC int
+xfs_dm_get_dirattrs_rvp(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		mask,
+	dm_attrloc_t	__user *locp,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp,
+	int		*rvp)
+{
+	xfs_inode_t	*dp = XFS_I(inode);
+	xfs_mount_t	*mp = dp->i_mount;
+	dm_readdir_cb_t	*cb;
+	dm_attrloc_t	loc;
+	int		error;
+
+	if (right < DM_RIGHT_SHARED)
+		return -EACCES;
+
+        /*
+         * Make sure that the buffer is properly aligned.
+         */
+        if (((unsigned long)bufp & (DM_STAT_ALIGN - 1)) != 0)
+                return -EFAULT;
+
+	if (mask & ~(DM_AT_HANDLE|DM_AT_EMASK|DM_AT_PMANR|DM_AT_PATTR|
+		     DM_AT_DTIME|DM_AT_CFLAG|DM_AT_STAT))
+		return -EINVAL;
+
+	if (!S_ISDIR(inode->i_mode))
+		return -EINVAL;
+
+        /*
+         * bufp should be able to fit at least one dm_stat entry including
+         * dt_handle and full size MAXNAMLEN dt_compname.
+         */
+        if (buflen < dm_stat_size(MAXNAMLEN))
+                return -ENOMEM;
+
+	if (copy_from_user(&loc, locp, sizeof(loc)))
+		return -EFAULT;
+
+	cb = kzalloc(sizeof(*cb) + dm_stat_size(MAXNAMLEN), GFP_KERNEL);
+	if (!cb)
+		return -ENOMEM;
+
+	cb->mp = mp;
+	cb->spaceleft = buflen;
+	cb->ubuf = bufp;
+
+	mutex_lock(&inode->i_mutex);
+	error = -ENOENT;
+	if (!IS_DEADDIR(inode)) {
+		error = -xfs_readdir(dp, cb, dp->i_size,
+					 (xfs_off_t *)&loc, dm_filldir);
+	}
+	mutex_unlock(&inode->i_mutex);
+
+	if (error)
+		goto out_kfree;
+	if (cb->error) {
+		error = cb->error;
+		goto out_kfree;
+	}
+
+	error = -EFAULT;
+	if (cb->lastbuf && put_user(0, &cb->lastbuf->_link))
+		goto out_kfree;
+	if (put_user(cb->nwritten, rlenp))
+		goto out_kfree;
+	if (copy_to_user(locp, &loc, sizeof(loc)))
+		goto out_kfree;
+
+	if (cb->nwritten)
+		*rvp = 1;
+	else
+		*rvp = 0;
+	error = 0;
+
+ out_kfree:
+	kfree(cb);
+	return error;
+}
+
+STATIC int
+xfs_dm_get_dmattr(
+	struct inode	*inode,
+	dm_right_t	right,
+	dm_attrname_t	__user *attrnamep,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user  *rlenp)
+{
+	dm_dkattrname_t name;
+	char		*value;
+	int		value_len;
+	int		alloc_size;
+	int		error;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_SHARED)
+		return(-EACCES);
+
+	if ((error = xfs_copyin_attrname(attrnamep, &name)) != 0)
+		return(-error); /* Return negative error to DMAPI */
+
+	/* Allocate a buffer to receive the attribute's value.	We allocate
+	   at least one byte even if the caller specified a buflen of zero.
+	   (A buflen of zero is considered valid.)
+
+	   Allocating a minimum of XFS_BUG_KLUDGE bytes temporarily works
+	   around a bug within XFS in which in-inode attribute values are not
+	   checked to see if they will fit in the buffer before they are
+	   copied.  Since no in-core attribute value can be larger than 256
+	   bytes (an 8-bit size field), we allocate that minimum size here to
+	   prevent buffer overrun in both the kernel's and user's buffers.
+	*/
+
+	alloc_size = buflen;
+	if (alloc_size < XFS_BUG_KLUDGE)
+		alloc_size = XFS_BUG_KLUDGE;
+	if (alloc_size > ATTR_MAX_VALUELEN)
+		alloc_size = ATTR_MAX_VALUELEN;
+	value = kmem_alloc(alloc_size, KM_SLEEP | KM_LARGE);
+
+	/* Get the attribute's value. */
+
+	value_len = alloc_size;		/* in/out parameter */
+
+	error = xfs_attr_get(XFS_I(inode), name.dan_chars, value, &value_len,
+					ATTR_ROOT);
+	DM_EA_XLATE_ERR(error);
+
+	/* DMAPI requires an errno of ENOENT if an attribute does not exist,
+	   so remap ENOATTR here.
+	*/
+
+	if (error == ENOATTR)
+		error = ENOENT;
+	if (!error && value_len > buflen)
+		error = E2BIG;
+	if (!error && copy_to_user(bufp, value, value_len))
+		error = EFAULT;
+	if (!error || error == E2BIG) {
+		if (put_user(value_len, rlenp))
+			error = EFAULT;
+	}
+
+	kmem_free(value);
+	return(-error); /* Return negative error to DMAPI */
+}
+
+STATIC int
+xfs_dm_get_eventlist(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		type,
+	u_int		nelem,
+	dm_eventset_t	*eventsetp,
+	u_int 		*nelemp)
+{
+	int		error;
+	xfs_inode_t	*ip = XFS_I(inode);
+
+	/* Returns negative errors to DMAPI */
+
+	if (type == DM_FSYS_OBJ) {
+		error = xfs_dm_fs_get_eventlist(ip->i_mount, right, nelem,
+			eventsetp, nelemp);
+	} else {
+		error = xfs_dm_f_get_eventlist(ip, right, nelem,
+			eventsetp, nelemp);
+	}
+	return(-error); /* Returns negative error to DMAPI */
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_get_fileattr(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		mask,		/* not used; always return everything */
+	dm_stat_t	__user *statp)
+{
+	dm_stat_t	stat;
+	xfs_inode_t	*ip = XFS_I(inode);
+	xfs_mount_t	*mp;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_SHARED)
+		return(-EACCES);
+
+	/* Find the mount point. */
+
+	mp = ip->i_mount;
+
+	xfs_ilock(ip, XFS_ILOCK_SHARED);
+	xfs_ip_to_stat(mp, ip->i_ino, ip, &stat);
+	xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+	if (copy_to_user( statp, &stat, sizeof(stat)))
+		return(-EFAULT);
+	return(0);
+}
+
+
+/* We currently only support a maximum of one managed region per file, and
+   use the DM_EVENT_READ, DM_EVENT_WRITE, and DM_EVENT_TRUNCATE events in
+   the file's dm_eventset_t event mask to implement the DM_REGION_READ,
+   DM_REGION_WRITE, and DM_REGION_TRUNCATE flags for that single region.
+*/
+
+STATIC int
+xfs_dm_get_region(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		nelem,
+	dm_region_t	__user *regbufp,
+	u_int		__user *nelemp)
+{
+	dm_eventset_t	evmask;
+	dm_region_t	region;
+	xfs_inode_t	*ip = XFS_I(inode);
+	u_int		elem;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_SHARED)
+		return(-EACCES);
+
+	evmask = ip->i_d.di_dmevmask;	/* read the mask "atomically" */
+
+	/* Get the file's current managed region flags out of the
+	   dm_eventset_t mask and use them to build a managed region that
+	   covers the entire file, i.e. set rg_offset and rg_size to zero.
+	*/
+
+	memset((char *)&region, 0, sizeof(region));
+
+	if (evmask & (1 << DM_EVENT_READ))
+		region.rg_flags |= DM_REGION_READ;
+	if (evmask & (1 << DM_EVENT_WRITE))
+		region.rg_flags |= DM_REGION_WRITE;
+	if (evmask & (1 << DM_EVENT_TRUNCATE))
+		region.rg_flags |= DM_REGION_TRUNCATE;
+
+	elem = (region.rg_flags ? 1 : 0);
+
+	if (copy_to_user( nelemp, &elem, sizeof(elem)))
+		return(-EFAULT);
+	if (elem > nelem)
+		return(-E2BIG);
+	if (elem && copy_to_user(regbufp, &region, sizeof(region)))
+		return(-EFAULT);
+	return(0);
+}
+
+
+STATIC int
+xfs_dm_getall_dmattr(
+	struct inode	*inode,
+	dm_right_t	right,
+	size_t		buflen,
+	void		__user *bufp,
+	size_t		__user *rlenp)
+{
+	attrlist_cursor_kern_t cursor;
+	attrlist_t	*attrlist;
+	dm_attrlist_t	__user *ulist;
+	int		*last_link;
+	int		alignment;
+	int		total_size;
+	int		list_size = 8192;	/* should be big enough */
+	int		error;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_SHARED)
+		return(-EACCES);
+
+	/* Verify that the user gave us a buffer that is 4-byte aligned, lock
+	   it down, and work directly within that buffer.  As a side-effect,
+	   values of buflen < sizeof(int) return EINVAL.
+	*/
+
+	alignment = sizeof(int) - 1;
+	if ((((__psint_t)bufp & alignment) != 0) ||
+               !access_ok(VERIFY_WRITE, bufp, buflen)) {
+		return(-EFAULT);
+	}
+	buflen &= ~alignment;		/* round down the alignment */
+
+	/* Initialize all the structures and variables for the main loop. */
+
+	memset(&cursor, 0, sizeof(cursor));
+	attrlist = (attrlist_t *)kmem_alloc(list_size, KM_SLEEP);
+	total_size = 0;
+	ulist = (dm_attrlist_t *)bufp;
+	last_link = NULL;
+
+	/* Use vop_attr_list to get the names of DMAPI attributes, and use
+	   vop_attr_get to get their values.  There is a risk here that the
+	   DMAPI attributes could change between the vop_attr_list and
+	   vop_attr_get calls.	If we can detect it, we return EIO to notify
+	   the user.
+	*/
+
+	do {
+		int	i;
+
+		/* Get a buffer full of attribute names.  If there aren't any
+		   more or if we encounter an error, then finish up.
+		*/
+
+		error = xfs_attr_list(XFS_I(inode), (char *)attrlist, list_size,
+						ATTR_ROOT, &cursor);
+		DM_EA_XLATE_ERR(error);
+
+		if (error || attrlist->al_count == 0)
+			break;
+
+		for (i = 0; i < attrlist->al_count; i++) {
+			attrlist_ent_t	*entry;
+			char		*user_name;
+			int		size_needed;
+			int		value_len;
+
+			/* Skip over all non-DMAPI attributes.	If the
+			   attribute name is too long, we assume it is
+			   non-DMAPI even if it starts with the correct
+			   prefix.
+			*/
+
+			entry = ATTR_ENTRY(attrlist, i);
+			if (strncmp(entry->a_name, dmattr_prefix, DMATTR_PREFIXLEN))
+				continue;
+			user_name = &entry->a_name[DMATTR_PREFIXLEN];
+			if (strlen(user_name) > DM_ATTR_NAME_SIZE)
+				continue;
+
+			/* We have a valid DMAPI attribute to return.  If it
+			   won't fit in the user's buffer, we still need to
+			   keep track of the number of bytes for the user's
+			   next call.
+			*/
+
+
+			size_needed = sizeof(*ulist) + entry->a_valuelen;
+			size_needed = (size_needed + alignment) & ~alignment;
+
+			total_size += size_needed;
+			if (total_size > buflen)
+				continue;
+
+			/* Start by filling in all the fields in the
+			   dm_attrlist_t structure.
+			*/
+
+			strncpy((char *)ulist->al_name.an_chars, user_name,
+				DM_ATTR_NAME_SIZE);
+			ulist->al_data.vd_offset = sizeof(*ulist);
+			ulist->al_data.vd_length = entry->a_valuelen;
+			ulist->_link =	size_needed;
+			last_link = &ulist->_link;
+
+			/* Next read the attribute's value into its correct
+			   location after the dm_attrlist structure.  Any sort
+			   of error indicates that the data is moving under us,
+			   so we return EIO to let the user know.
+			*/
+
+			value_len = entry->a_valuelen;
+
+			error = xfs_attr_get(XFS_I(inode), entry->a_name,
+						(void *)(ulist + 1), &value_len,
+						ATTR_ROOT);
+			DM_EA_XLATE_ERR(error);
+
+			if (error || value_len != entry->a_valuelen) {
+				error = EIO;
+				break;
+			}
+
+			ulist = (dm_attrlist_t *)((char *)ulist + ulist->_link);
+		}
+	} while (!error && attrlist->al_more);
+	if (last_link)
+		*last_link = 0;
+
+	if (!error && total_size > buflen)
+		error = E2BIG;
+	if (!error || error == E2BIG) {
+		if (put_user(total_size, rlenp))
+			error = EFAULT;
+	}
+
+	kmem_free(attrlist);
+	return(-error); /* Return negative error to DMAPI */
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_getall_inherit(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		nelem,
+	dm_inherit_t	__user *inheritbufp,
+	u_int		__user *nelemp)
+{
+	return(-ENOSYS); /* Return negative error to DMAPI */
+}
+
+
+/* Initialize location pointer for subsequent dm_get_dirattrs,
+   dm_get_bulkattr, and dm_get_bulkall calls.  The same initialization must
+   work for inode-based routines (dm_get_dirattrs) and filesystem-based
+   routines (dm_get_bulkattr and dm_get_bulkall).  Filesystem-based functions
+   call this routine using the filesystem's root inode.
+*/
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_init_attrloc(
+	struct inode	*inode,
+	dm_right_t	right,
+	dm_attrloc_t	__user *locp)
+{
+	dm_attrloc_t	loc = 0;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_SHARED)
+		return(-EACCES);
+
+	if (copy_to_user( locp, &loc, sizeof(loc)))
+		return(-EFAULT);
+	return(0);
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_mkdir_by_handle(
+	struct inode	*inode,
+	dm_right_t	right,
+	void		__user *hanp,
+	size_t		hlen,
+	char		__user *cname)
+{
+	return(-ENOSYS); /* Return negative error to DMAPI */
+}
+
+
+/*
+ * Probe and Punch
+ *
+ * Hole punching alignment is based on the underlying device base
+ * allocation size. Because it is not defined in the DMAPI spec, we
+ * can align how we choose here. Round inwards (offset up and length
+ * down) to the block, extent or page size whichever is bigger. Our
+ * DMAPI implementation rounds the hole geometry strictly inwards. If
+ * this is not possible, return EINVAL for both for xfs_dm_probe_hole
+ * and xfs_dm_punch_hole which differs from the DMAPI spec.  Note that
+ * length = 0 is special - it means "punch to EOF" and at that point
+ * we treat the punch as remove everything past offset (including
+ * preallocation past EOF).
+ */
+
+STATIC int
+xfs_dm_round_hole(
+	dm_off_t	offset,
+	dm_size_t	length,
+	dm_size_t	align,
+	xfs_fsize_t	filesize,
+	dm_off_t	*roff,
+	dm_size_t	*rlen)
+{
+
+	dm_off_t	off = offset;
+	dm_size_t	len = length;
+
+	/* Try to round offset up to the nearest boundary */
+	*roff = roundup_64(off, align);
+	if ((*roff >= filesize) || (len && (len < align)))
+		return -EINVAL;
+
+	if ((len == 0) || ((off + len) == filesize)) {
+		/* punch to EOF */
+		*rlen = 0;
+	} else {
+		/* Round length down to the nearest boundary. */
+		ASSERT(len >= align);
+		ASSERT(align > (*roff - off));
+		len -= *roff - off;
+		*rlen = len - do_mod(len, align);
+		if (*rlen == 0)
+			return -EINVAL; /* requested length is too small */
+	}
+#ifdef CONFIG_DMAPI_DEBUG
+	printk("xfs_dm_round_hole: off %lu, len %ld, align %lu, "
+	       "filesize %llu, roff %ld, rlen %ld\n",
+	       offset, length, align, filesize, *roff, *rlen);
+#endif
+	return 0; /* hole geometry successfully rounded */
+}
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_probe_hole(
+	struct inode	*inode,
+	dm_right_t	right,
+	dm_off_t	off,
+	dm_size_t	len,
+	dm_off_t	__user	*roffp,
+	dm_size_t	__user *rlenp)
+{
+	dm_off_t	roff;
+	dm_size_t	rlen;
+	xfs_inode_t	*ip = XFS_I(inode);
+	xfs_mount_t	*mp;
+	uint		lock_flags;
+	xfs_fsize_t	realsize;
+	dm_size_t	align;
+	int		error;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_SHARED)
+		return -EACCES;
+
+	if ((ip->i_d.di_mode & S_IFMT) != S_IFREG)
+		return -EINVAL;
+
+	mp = ip->i_mount;
+	lock_flags = XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL;
+	xfs_ilock(ip, lock_flags);
+	realsize = ip->i_size;
+	xfs_iunlock(ip, lock_flags);
+
+	if ((off + len) > realsize)
+		return -E2BIG;
+
+	align = 1 << mp->m_sb.sb_blocklog;
+
+	error = xfs_dm_round_hole(off, len, align, realsize, &roff, &rlen);
+	if (error)
+		return error;
+
+	if (copy_to_user( roffp, &roff, sizeof(roff)))
+		return -EFAULT;
+	if (copy_to_user( rlenp, &rlen, sizeof(rlen)))
+		return -EFAULT;
+	return(0);
+}
+
+
+STATIC int
+xfs_dm_punch_hole(
+	struct inode	*inode,
+	dm_right_t	right,
+	dm_off_t	off,
+	dm_size_t	len)
+{
+	xfs_flock64_t	bf;
+	int		error = 0;
+	xfs_inode_t	*ip = XFS_I(inode);
+	xfs_mount_t	*mp;
+	dm_size_t	align;
+	xfs_fsize_t	realsize;
+	dm_off_t	roff;
+	dm_size_t	rlen;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_EXCL)
+		return -EACCES;
+
+	/* Make sure there are no leases. */
+	error = break_lease(inode, FMODE_WRITE);
+	if (error)
+		return -EBUSY;
+
+	error = get_write_access(inode);
+	if (error)
+		return -EBUSY;
+
+	mp = ip->i_mount;
+
+	down_rw_sems(inode, DM_SEM_FLAG_WR);
+
+	xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
+	realsize = ip->i_size;
+	xfs_iunlock(ip, XFS_ILOCK_EXCL);
+	align = xfs_get_extsz_hint(ip);
+	if (align == 0)
+		align = 1;
+
+	align <<= mp->m_sb.sb_blocklog;
+
+	if ((off + len) > realsize) {
+		xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+		error = -E2BIG;
+		goto up_and_out;
+	}
+
+	if ((off + len) == realsize)
+		len = 0;
+
+	error = xfs_dm_round_hole(off, len, align, realsize, &roff, &rlen);
+	if (error || (off != roff) || (len != rlen)) {
+		xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+		error = -EINVAL;
+		goto up_and_out;
+	}
+
+	bf.l_type = 0;
+	bf.l_whence = 0;
+	bf.l_start = (xfs_off_t)off;
+	if (len) {
+		bf.l_len = len;
+	}
+	else {
+		/*
+		 * When we are punching to EOF, we have to make sure we punch
+		 * the last partial block that contains EOF. Round up
+		 * the length to make sure we punch the block and not just
+		 * zero it.
+		 */
+		bf.l_len = roundup_64((realsize - off), mp->m_sb.sb_blocksize);
+	}
+
+#ifdef CONFIG_DMAPI_DEBUG
+	printk("xfs_dm_punch_hole: off %lu, len %ld, align %lu\n",
+		off, len, align);
+#endif
+
+	error = xfs_change_file_space(ip, XFS_IOC_UNRESVSP, &bf,
+				(xfs_off_t)off, XFS_ATTR_DMI|XFS_ATTR_NOLOCK);
+
+	/*
+	 * if punching to end of file, kill any blocks past EOF that
+	 * may have been (speculatively) preallocated. No point in
+	 * leaving them around if we are migrating the file....
+	 */
+	if (!error && (len == 0)) {
+		error = xfs_free_eofblocks(mp, ip, XFS_FREE_EOF_NOLOCK);
+	}
+
+	/*
+	 * negate the error for return here as core XFS functions return
+	 * positive error numbers
+	 */
+	if (error)
+		error = -error;
+
+	/* Let threads in send_data_event know we punched the file. */
+	ip->i_d.di_dmstate++;
+	xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+
+up_and_out:
+	up_rw_sems(inode, DM_SEM_FLAG_WR);
+	put_write_access(inode);
+
+	return error;
+}
+
+
+STATIC int
+xfs_dm_read_invis_rvp(
+	struct inode	*inode,
+	dm_right_t	right,
+	dm_off_t	off,
+	dm_size_t	len,
+	void		__user *bufp,
+	int		*rvp)
+{
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_SHARED)
+		return(-EACCES);
+
+	return(-xfs_dm_rdwr(inode, 0, FMODE_READ, off, len, bufp, rvp));
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_release_right(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		type)		/* DM_FSYS_OBJ or zero */
+{
+#ifdef	DEBUG_RIGHTS
+	char		buffer[sizeof(dm_handle_t) * 2 + 1];
+
+	if (!xfs_vp_to_hexhandle(inode, type, buffer)) {
+		printf("dm_release_right: old %d type %d handle %s\n",
+			right, type, buffer);
+	} else {
+		printf("dm_release_right: old %d type %d handle "
+			" <INVALID>\n", right, type);
+	}
+#endif	/* DEBUG_RIGHTS */
+	return(0);
+}
+
+
+STATIC int
+xfs_dm_remove_dmattr(
+	struct inode	*inode,
+	dm_right_t	right,
+	int		setdtime,
+	dm_attrname_t	__user *attrnamep)
+{
+	dm_dkattrname_t name;
+	int		error;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_EXCL)
+		return(-EACCES);
+
+	if ((error = xfs_copyin_attrname(attrnamep, &name)) != 0)
+		return(-error); /* Return negative error to DMAPI */
+
+	/* Remove the attribute from the object. */
+
+	error = xfs_attr_remove(XFS_I(inode), name.dan_chars, setdtime ?
+				ATTR_ROOT : (ATTR_ROOT|ATTR_KERNOTIME));
+	DM_EA_XLATE_ERR(error);
+
+	if (error == ENOATTR)
+		error = ENOENT;
+	return(-error); /* Return negative error to DMAPI */
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_request_right(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		type,		/* DM_FSYS_OBJ or zero */
+	u_int		flags,
+	dm_right_t	newright)
+{
+#ifdef	DEBUG_RIGHTS
+	char		buffer[sizeof(dm_handle_t) * 2 + 1];
+
+	if (!xfs_vp_to_hexhandle(inode, type, buffer)) {
+		printf("dm_request_right: old %d new %d type %d flags 0x%x "
+			"handle %s\n", right, newright, type, flags, buffer);
+	} else {
+		printf("dm_request_right: old %d new %d type %d flags 0x%x "
+			"handle <INVALID>\n", right, newright, type, flags);
+	}
+#endif	/* DEBUG_RIGHTS */
+	return(0);
+}
+
+
+STATIC int
+xfs_dm_set_dmattr(
+	struct inode	*inode,
+	dm_right_t	right,
+	dm_attrname_t	__user *attrnamep,
+	int		setdtime,
+	size_t		buflen,
+	void		__user *bufp)
+{
+	dm_dkattrname_t name;
+	char		*value;
+	int		alloc_size;
+	int		error;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_EXCL)
+		return(-EACCES);
+
+	if ((error = xfs_copyin_attrname(attrnamep, &name)) != 0)
+		return(-error); /* Return negative error to DMAPI */
+	if (buflen > ATTR_MAX_VALUELEN)
+		return(-E2BIG);
+
+	/* Copy in the attribute's value and store the <name,value> pair in
+	   the object.	We allocate a buffer of at least one byte even if the
+	   caller specified a buflen of zero.  (A buflen of zero is considered
+	   valid.)
+	*/
+
+	alloc_size = (buflen == 0) ? 1 : buflen;
+	value = kmem_alloc(alloc_size, KM_SLEEP);
+	if (copy_from_user( value, bufp, buflen)) {
+		error = EFAULT;
+	} else {
+		error = xfs_attr_set(XFS_I(inode), name.dan_chars, value, buflen,
+					setdtime ? ATTR_ROOT :
+					(ATTR_ROOT|ATTR_KERNOTIME));
+		DM_EA_XLATE_ERR(error);
+	}
+	kmem_free(value);
+	return(-error); /* Return negative error to DMAPI */
+}
+
+STATIC int
+xfs_dm_set_eventlist(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		type,
+	dm_eventset_t	*eventsetp,	/* in kernel space! */
+	u_int		maxevent)
+{
+	int		error;
+	xfs_inode_t	*ip = XFS_I(inode);
+
+	/* Returns negative errors to DMAPI */
+
+	if (type == DM_FSYS_OBJ) {
+		error = xfs_dm_fs_set_eventlist(ip->i_mount, right, eventsetp, maxevent);
+	} else {
+		error = xfs_dm_f_set_eventlist(ip, right, eventsetp, maxevent);
+	}
+	return(-error); /* Return negative error to DMAPI */
+}
+
+
+/*
+ *  This turned out not XFS-specific, but leave it here with get_fileattr.
+ */
+
+STATIC int
+xfs_dm_set_fileattr(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		mask,
+	dm_fileattr_t	__user *statp)
+{
+	dm_fileattr_t	stat;
+	struct iattr	iattr;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_EXCL)
+		return(-EACCES);
+
+	if (copy_from_user( &stat, statp, sizeof(stat)))
+		return(-EFAULT);
+
+	iattr.ia_valid = 0;
+
+	if (mask & DM_AT_MODE) {
+		iattr.ia_valid |= ATTR_MODE;
+		iattr.ia_mode = stat.fa_mode;
+	}
+	if (mask & DM_AT_UID) {
+		iattr.ia_valid |= ATTR_UID;
+		iattr.ia_uid = stat.fa_uid;
+	}
+	if (mask & DM_AT_GID) {
+		iattr.ia_valid |= ATTR_GID;
+		iattr.ia_gid = stat.fa_gid;
+	}
+	if (mask & DM_AT_ATIME) {
+		iattr.ia_valid |= ATTR_ATIME;
+		iattr.ia_atime.tv_sec = stat.fa_atime;
+		iattr.ia_atime.tv_nsec = 0;
+                inode->i_atime.tv_sec = stat.fa_atime;
+	}
+	if (mask & DM_AT_MTIME) {
+		iattr.ia_valid |= ATTR_MTIME;
+		iattr.ia_mtime.tv_sec = stat.fa_mtime;
+		iattr.ia_mtime.tv_nsec = 0;
+	}
+	if (mask & DM_AT_CTIME) {
+		iattr.ia_valid |= ATTR_CTIME;
+		iattr.ia_ctime.tv_sec = stat.fa_ctime;
+		iattr.ia_ctime.tv_nsec = 0;
+	}
+
+	/*
+	 * DM_AT_DTIME only takes effect if DM_AT_CTIME is not specified.  We
+	 * overload ctime to also act as dtime, i.e. DM_CONFIG_DTIME_OVERLOAD.
+	 */
+	if ((mask & DM_AT_DTIME) && !(mask & DM_AT_CTIME)) {
+		iattr.ia_valid |= ATTR_CTIME;
+		iattr.ia_ctime.tv_sec = stat.fa_dtime;
+		iattr.ia_ctime.tv_nsec = 0;
+	}
+	if (mask & DM_AT_SIZE) {
+		iattr.ia_valid |= ATTR_SIZE;
+		iattr.ia_size = stat.fa_size;
+	}
+
+	return -xfs_setattr(XFS_I(inode), &iattr, XFS_ATTR_DMI);
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_set_inherit(
+	struct inode	*inode,
+	dm_right_t	right,
+	dm_attrname_t	__user *attrnamep,
+	mode_t		mode)
+{
+	return(-ENOSYS); /* Return negative error to DMAPI */
+}
+
+
+STATIC int
+xfs_dm_set_region(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		nelem,
+	dm_region_t	__user *regbufp,
+	dm_boolean_t	__user *exactflagp)
+{
+	xfs_inode_t	*ip = XFS_I(inode);
+	xfs_trans_t	*tp;
+	xfs_mount_t	*mp;
+	dm_region_t	region;
+	dm_eventset_t	new_mask;
+	dm_eventset_t	mr_mask;
+	int		error;
+	u_int		exactflag;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_EXCL)
+		return(-EACCES);
+
+	/* If the caller gave us more than one dm_region_t structure, complain.
+	   (He has to call dm_get_config() to find out what our limit is.)
+	*/
+
+	if (nelem > 1)
+		return(-E2BIG);
+
+	/* If the user provided a dm_region_t structure, then copy it in,
+	   validate it, and convert its flags to the corresponding bits in a
+	   dm_set_eventlist() event mask.  A call with zero regions is
+	   equivalent to clearing all region flags.
+	*/
+
+	new_mask = 0;
+	if (nelem == 1) {
+		if (copy_from_user( &region, regbufp, sizeof(region)))
+			return(-EFAULT);
+
+		if (region.rg_flags & ~(DM_REGION_READ|DM_REGION_WRITE|DM_REGION_TRUNCATE))
+			return(-EINVAL);
+		if (region.rg_flags & DM_REGION_READ)
+			new_mask |= 1 << DM_EVENT_READ;
+		if (region.rg_flags & DM_REGION_WRITE)
+			new_mask |= 1 << DM_EVENT_WRITE;
+		if (region.rg_flags & DM_REGION_TRUNCATE)
+			new_mask |= 1 << DM_EVENT_TRUNCATE;
+	}
+	mr_mask = (1 << DM_EVENT_READ) | (1 << DM_EVENT_WRITE) | (1 << DM_EVENT_TRUNCATE);
+
+	/* Get the file's existing event mask, clear the old managed region
+	   bits, add in the new ones, and update the file's mask.
+	*/
+
+	if (new_mask & prohibited_mr_events(inode->i_mapping)) {
+		/* If the change is simply to remove the READ
+		 * bit, then that's always okay.  Otherwise, it's busy.
+		 */
+		dm_eventset_t m1;
+		m1 = ip->i_d.di_dmevmask & ((1 << DM_EVENT_WRITE) | (1 << DM_EVENT_TRUNCATE));
+		if (m1 != new_mask) {
+			return -EBUSY;
+		}
+	}
+
+	mp = ip->i_mount;
+	tp = xfs_trans_alloc(mp, XFS_TRANS_SET_DMATTRS);
+	error = xfs_trans_reserve(tp, 0, XFS_ICHANGE_LOG_RES (mp), 0, 0, 0);
+	if (error) {
+		xfs_trans_cancel(tp, 0);
+		return(-error); /* Return negative error to DMAPI */
+	}
+	xfs_ilock(ip, XFS_ILOCK_EXCL);
+	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+
+	ip->i_d.di_dmevmask = (ip->i_d.di_dmevmask & ~mr_mask) | new_mask;
+
+	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+	igrab(inode);
+	xfs_trans_commit(tp, 0);
+
+	/* Return the proper value for *exactflagp depending upon whether or not
+	   we "changed" the user's managed region.  In other words, if the user
+	   specified a non-zero value for either rg_offset or rg_size, we
+	   round each of those values back to zero.
+	*/
+
+	if (nelem && (region.rg_offset || region.rg_size)) {
+		exactflag = DM_FALSE;	/* user region was changed */
+	} else {
+		exactflag = DM_TRUE;	/* user region was unchanged */
+	}
+	if (copy_to_user( exactflagp, &exactflag, sizeof(exactflag)))
+		return(-EFAULT);
+	return(0);
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_symlink_by_handle(
+	struct inode	*inode,
+	dm_right_t	right,
+	void __user	*hanp,
+	size_t		hlen,
+	char		__user *cname,
+	char		__user *path)
+{
+	return(-ENOSYS); /* Return negative errors to DMAPI */
+}
+
+
+/*
+ * xfs_dm_sync_by_handle needs to do the same thing as sys_fsync()
+ */
+STATIC int
+xfs_dm_sync_by_handle(
+	struct inode	*inode,
+	dm_right_t	right)
+{
+	int		err, ret;
+	xfs_inode_t	*ip = XFS_I(inode);
+
+	/* Returns negative errors to DMAPI */
+	if (right < DM_RIGHT_EXCL)
+		return(-EACCES);
+
+	/* We need to protect against concurrent writers.. */
+	ret = filemap_fdatawrite(inode->i_mapping);
+	down_rw_sems(inode, DM_FLAGS_IMUX);
+	err = -xfs_fsync(ip);
+	if (!ret)
+		ret = err;
+	up_rw_sems(inode, DM_FLAGS_IMUX);
+	err = filemap_fdatawait(inode->i_mapping);
+	if (!ret)
+		ret = err;
+	xfs_iflags_clear(ip, XFS_ITRUNCATED);
+	return ret;
+}
+
+
+/* ARGSUSED */
+STATIC int
+xfs_dm_upgrade_right(
+	struct inode	*inode,
+	dm_right_t	right,
+	u_int		type)		/* DM_FSYS_OBJ or zero */
+{
+#ifdef	DEBUG_RIGHTS
+	char		buffer[sizeof(dm_handle_t) * 2 + 1];
+
+	if (!xfs_vp_to_hexhandle(inode, type, buffer)) {
+		printf("dm_upgrade_right: old %d new %d type %d handle %s\n",
+			right, DM_RIGHT_EXCL, type, buffer);
+	} else {
+		printf("dm_upgrade_right: old %d new %d type %d handle "
+			"<INVALID>\n", right, DM_RIGHT_EXCL, type);
+	}
+#endif	/* DEBUG_RIGHTS */
+	return(0);
+}
+
+
+STATIC int
+xfs_dm_write_invis_rvp(
+	struct inode	*inode,
+	dm_right_t	right,
+	int		flags,
+	dm_off_t	off,
+	dm_size_t	len,
+	void __user	*bufp,
+	int		*rvp)
+{
+	int		fflag = 0;
+
+	/* Returns negative errors to DMAPI */
+
+	if (right < DM_RIGHT_EXCL)
+		return(-EACCES);
+
+	if (flags & DM_WRITE_SYNC)
+		fflag |= O_SYNC;
+	return(-xfs_dm_rdwr(inode, fflag, FMODE_WRITE, off, len, bufp, rvp));
+}
+
+
+STATIC void
+xfs_dm_obj_ref_hold(
+	struct inode	*inode)
+{
+	igrab(inode);
+}
+
+
+static fsys_function_vector_t	xfs_fsys_vector[DM_FSYS_MAX];
+
+
+STATIC int
+xfs_dm_get_dmapiops(
+	struct super_block	*sb,
+	void			*addr)
+{
+	static	int		initialized = 0;
+	dm_fcntl_vector_t	*vecrq;
+	fsys_function_vector_t	*vecp;
+	int			i = 0;
+
+	vecrq = (dm_fcntl_vector_t *)addr;
+	vecrq->count =
+		sizeof(xfs_fsys_vector) / sizeof(xfs_fsys_vector[0]);
+	vecrq->vecp = xfs_fsys_vector;
+	if (initialized)
+		return(0);
+	vecrq->code_level = DM_CLVL_XOPEN;
+	vecp = xfs_fsys_vector;
+
+	vecp[i].func_no = DM_FSYS_CLEAR_INHERIT;
+	vecp[i++].u_fc.clear_inherit = xfs_dm_clear_inherit;
+	vecp[i].func_no = DM_FSYS_CREATE_BY_HANDLE;
+	vecp[i++].u_fc.create_by_handle = xfs_dm_create_by_handle;
+	vecp[i].func_no = DM_FSYS_DOWNGRADE_RIGHT;
+	vecp[i++].u_fc.downgrade_right = xfs_dm_downgrade_right;
+	vecp[i].func_no = DM_FSYS_GET_ALLOCINFO_RVP;
+	vecp[i++].u_fc.get_allocinfo_rvp = xfs_dm_get_allocinfo_rvp;
+	vecp[i].func_no = DM_FSYS_GET_BULKALL_RVP;
+	vecp[i++].u_fc.get_bulkall_rvp = xfs_dm_get_bulkall_rvp;
+	vecp[i].func_no = DM_FSYS_GET_BULKATTR_RVP;
+	vecp[i++].u_fc.get_bulkattr_rvp = xfs_dm_get_bulkattr_rvp;
+	vecp[i].func_no = DM_FSYS_GET_CONFIG;
+	vecp[i++].u_fc.get_config = xfs_dm_get_config;
+	vecp[i].func_no = DM_FSYS_GET_CONFIG_EVENTS;
+	vecp[i++].u_fc.get_config_events = xfs_dm_get_config_events;
+	vecp[i].func_no = DM_FSYS_GET_DESTROY_DMATTR;
+	vecp[i++].u_fc.get_destroy_dmattr = xfs_dm_get_destroy_dmattr;
+	vecp[i].func_no = DM_FSYS_GET_DIOINFO;
+	vecp[i++].u_fc.get_dioinfo = xfs_dm_get_dioinfo;
+	vecp[i].func_no = DM_FSYS_GET_DIRATTRS_RVP;
+	vecp[i++].u_fc.get_dirattrs_rvp = xfs_dm_get_dirattrs_rvp;
+	vecp[i].func_no = DM_FSYS_GET_DMATTR;
+	vecp[i++].u_fc.get_dmattr = xfs_dm_get_dmattr;
+	vecp[i].func_no = DM_FSYS_GET_EVENTLIST;
+	vecp[i++].u_fc.get_eventlist = xfs_dm_get_eventlist;
+	vecp[i].func_no = DM_FSYS_GET_FILEATTR;
+	vecp[i++].u_fc.get_fileattr = xfs_dm_get_fileattr;
+	vecp[i].func_no = DM_FSYS_GET_REGION;
+	vecp[i++].u_fc.get_region = xfs_dm_get_region;
+	vecp[i].func_no = DM_FSYS_GETALL_DMATTR;
+	vecp[i++].u_fc.getall_dmattr = xfs_dm_getall_dmattr;
+	vecp[i].func_no = DM_FSYS_GETALL_INHERIT;
+	vecp[i++].u_fc.getall_inherit = xfs_dm_getall_inherit;
+	vecp[i].func_no = DM_FSYS_INIT_ATTRLOC;
+	vecp[i++].u_fc.init_attrloc = xfs_dm_init_attrloc;
+	vecp[i].func_no = DM_FSYS_MKDIR_BY_HANDLE;
+	vecp[i++].u_fc.mkdir_by_handle = xfs_dm_mkdir_by_handle;
+	vecp[i].func_no = DM_FSYS_PROBE_HOLE;
+	vecp[i++].u_fc.probe_hole = xfs_dm_probe_hole;
+	vecp[i].func_no = DM_FSYS_PUNCH_HOLE;
+	vecp[i++].u_fc.punch_hole = xfs_dm_punch_hole;
+	vecp[i].func_no = DM_FSYS_READ_INVIS_RVP;
+	vecp[i++].u_fc.read_invis_rvp = xfs_dm_read_invis_rvp;
+	vecp[i].func_no = DM_FSYS_RELEASE_RIGHT;
+	vecp[i++].u_fc.release_right = xfs_dm_release_right;
+	vecp[i].func_no = DM_FSYS_REMOVE_DMATTR;
+	vecp[i++].u_fc.remove_dmattr = xfs_dm_remove_dmattr;
+	vecp[i].func_no = DM_FSYS_REQUEST_RIGHT;
+	vecp[i++].u_fc.request_right = xfs_dm_request_right;
+	vecp[i].func_no = DM_FSYS_SET_DMATTR;
+	vecp[i++].u_fc.set_dmattr = xfs_dm_set_dmattr;
+	vecp[i].func_no = DM_FSYS_SET_EVENTLIST;
+	vecp[i++].u_fc.set_eventlist = xfs_dm_set_eventlist;
+	vecp[i].func_no = DM_FSYS_SET_FILEATTR;
+	vecp[i++].u_fc.set_fileattr = xfs_dm_set_fileattr;
+	vecp[i].func_no = DM_FSYS_SET_INHERIT;
+	vecp[i++].u_fc.set_inherit = xfs_dm_set_inherit;
+	vecp[i].func_no = DM_FSYS_SET_REGION;
+	vecp[i++].u_fc.set_region = xfs_dm_set_region;
+	vecp[i].func_no = DM_FSYS_SYMLINK_BY_HANDLE;
+	vecp[i++].u_fc.symlink_by_handle = xfs_dm_symlink_by_handle;
+	vecp[i].func_no = DM_FSYS_SYNC_BY_HANDLE;
+	vecp[i++].u_fc.sync_by_handle = xfs_dm_sync_by_handle;
+	vecp[i].func_no = DM_FSYS_UPGRADE_RIGHT;
+	vecp[i++].u_fc.upgrade_right = xfs_dm_upgrade_right;
+	vecp[i].func_no = DM_FSYS_WRITE_INVIS_RVP;
+	vecp[i++].u_fc.write_invis_rvp = xfs_dm_write_invis_rvp;
+	vecp[i].func_no = DM_FSYS_OBJ_REF_HOLD;
+	vecp[i++].u_fc.obj_ref_hold = xfs_dm_obj_ref_hold;
+
+	return(0);
+}
+
+
+/*	xfs_dm_send_mmap_event - send events needed for memory mapping a file.
+ *
+ *	This is a workaround called for files that are about to be
+ *	mapped.	 DMAPI events are not being generated at a low enough level
+ *	in the kernel for page reads/writes to generate the correct events.
+ *	So for memory-mapped files we generate read  or write events for the
+ *	whole byte range being mapped.	If the mmap call can never cause a
+ *	write to the file, then only a read event is sent.
+ *
+ *	Code elsewhere prevents adding managed regions to a file while it
+ *	is still mapped.
+ */
+
+STATIC int
+xfs_dm_send_mmap_event(
+	struct vm_area_struct *vma,
+	unsigned int	wantflag)
+{
+	xfs_inode_t	*ip;
+	int		error = 0;
+	dm_eventtype_t	max_event = DM_EVENT_READ;
+	xfs_fsize_t	filesize;
+	xfs_off_t	length, end_of_area, evsize, offset;
+	int		iolock;
+
+	if (!vma->vm_file)
+		return 0;
+
+	ip = XFS_I(vma->vm_file->f_dentry->d_inode);
+
+	if (!S_ISREG(vma->vm_file->f_dentry->d_inode->i_mode) ||
+	    !(ip->i_mount->m_flags & XFS_MOUNT_DMAPI))
+		return 0;
+
+	/* If they specifically asked for 'read', then give it to them.
+	 * Otherwise, see if it's possible to give them 'write'.
+	 */
+	if( wantflag & VM_READ ){
+		max_event = DM_EVENT_READ;
+	}
+	else if( ! (vma->vm_flags & VM_DENYWRITE) ) {
+		if((wantflag & VM_WRITE) || (vma->vm_flags & VM_WRITE))
+			max_event = DM_EVENT_WRITE;
+	}
+
+	if( (wantflag & VM_WRITE) && (max_event != DM_EVENT_WRITE) ){
+		return -EACCES;
+	}
+
+	/* Figure out how much of the file is being requested by the user. */
+	offset = 0; /* beginning of file, for now */
+	length = 0; /* whole file, for now */
+
+	filesize = ip->i_new_size;
+	if (filesize < ip->i_size) {
+		filesize = ip->i_size;
+	}
+
+	/* Set first byte number beyond the map area. */
+
+	if (length) {
+		end_of_area = offset + length;
+		if (end_of_area > filesize)
+			end_of_area = filesize;
+	} else {
+		end_of_area = filesize;
+	}
+
+	/* Set the real amount being mapped. */
+	evsize = end_of_area - offset;
+	if (evsize < 0)
+		evsize = 0;
+
+	if (max_event == DM_EVENT_READ)
+		iolock = XFS_IOLOCK_SHARED;
+	else
+		iolock = XFS_IOLOCK_EXCL;
+
+	xfs_ilock(ip, iolock);
+	/* If write possible, try a DMAPI write event */
+	if (max_event == DM_EVENT_WRITE && DM_EVENT_ENABLED(ip, max_event)) {
+		error = xfs_dm_send_data_event(max_event, ip, offset,
+					       evsize, 0, &iolock);
+		goto out_unlock;
+	}
+
+	/* Try a read event if max_event was != DM_EVENT_WRITE or if it
+	 * was DM_EVENT_WRITE but the WRITE event was not enabled.
+	 */
+	if (DM_EVENT_ENABLED(ip, DM_EVENT_READ)) {
+		error = xfs_dm_send_data_event(DM_EVENT_READ, ip, offset,
+					       evsize, 0, &iolock);
+	}
+out_unlock:
+	xfs_iunlock(ip, iolock);
+	return -error;
+}
+
+
+STATIC int
+xfs_dm_send_destroy_event(
+	xfs_inode_t	*ip,
+	dm_right_t	vp_right)	/* always DM_RIGHT_NULL */
+{
+	/* Returns positive errors to XFS */
+	return -dm_send_destroy_event(&ip->i_vnode, vp_right);
+}
+
+
+STATIC int
+xfs_dm_send_namesp_event(
+	dm_eventtype_t	event,
+	struct xfs_mount *mp,
+	xfs_inode_t	*ip1,
+	dm_right_t	vp1_right,
+	xfs_inode_t	*ip2,
+	dm_right_t	vp2_right,
+	const char	*name1,
+	const char	*name2,
+	mode_t		mode,
+	int		retcode,
+	int		flags)
+{
+	/* Returns positive errors to XFS */
+	return -dm_send_namesp_event(event, mp ? mp->m_super : NULL,
+				    &ip1->i_vnode, vp1_right,
+				    ip2 ? &ip2->i_vnode : NULL, vp2_right,
+				    name1, name2,
+				    mode, retcode, flags);
+}
+
+STATIC int
+xfs_dm_send_mount_event(
+	struct xfs_mount	*mp,
+	dm_right_t		root_right,
+	char			*mtpt,
+	char			*fsname)
+{
+	return dm_send_mount_event(mp->m_super, root_right,
+			NULL, DM_RIGHT_NULL,
+			mp->m_rootip ? VFS_I(mp->m_rootip) : NULL,
+			DM_RIGHT_NULL, mtpt, fsname);
+}
+
+STATIC void
+xfs_dm_send_unmount_event(
+	struct xfs_mount *mp,
+	xfs_inode_t	*ip,		/* NULL if unmount successful */
+	dm_right_t	vfsp_right,
+	mode_t		mode,
+	int		retcode,	/* errno, if unmount failed */
+	int		flags)
+{
+	dm_send_unmount_event(mp->m_super, ip ? &ip->i_vnode : NULL,
+			      vfsp_right, mode, retcode, flags);
+}
+
+
+/*
+ * Data migration operations accessed by the rest of XFS.
+ * When DMAPI support is configured in, this vector is used.
+ */
+
+xfs_dmops_t	xfs_dmcore_xfs = {
+	.xfs_send_data		= xfs_dm_send_data_event,
+	.xfs_send_mmap		= xfs_dm_send_mmap_event,
+	.xfs_send_destroy	= xfs_dm_send_destroy_event,
+	.xfs_send_namesp	= xfs_dm_send_namesp_event,
+	.xfs_send_mount		= xfs_dm_send_mount_event,
+	.xfs_send_unmount	= xfs_dm_send_unmount_event,
+};
+EXPORT_SYMBOL(xfs_dmcore_xfs);
+
+STATIC int
+xfs_dm_fh_to_inode(
+	struct super_block	*sb,
+	struct inode		**inode,
+	dm_fid_t		*dmfid)
+{
+	xfs_mount_t		*mp = XFS_M(sb);
+	xfs_inode_t		*ip;
+	xfs_ino_t		ino;
+	unsigned int		igen;
+	int			error;
+
+	*inode = NULL;
+
+	if (!dmfid->dm_fid_len) {
+		/* filesystem handle */
+		*inode = igrab(&mp->m_rootip->i_vnode);
+		if (!*inode)
+			return -ENOENT;
+		return 0;
+	}
+
+	if (dmfid->dm_fid_len != sizeof(*dmfid) - sizeof(dmfid->dm_fid_len))
+		return -EINVAL;
+
+	ino  = dmfid->dm_fid_ino;
+	igen = dmfid->dm_fid_gen;
+
+	/* fail requests for ino 0 gracefully. */
+	if (ino == 0)
+		return -ESTALE;
+
+	error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_SHARED, &ip, 0);
+	if (error)
+		return -error;
+	if (!ip)
+		return -EIO;
+
+	if (!ip->i_d.di_mode || ip->i_d.di_gen != igen) {
+		xfs_iput_new(ip, XFS_ILOCK_SHARED);
+		return -ENOENT;
+	}
+
+	*inode = &ip->i_vnode;
+	xfs_iunlock(ip, XFS_ILOCK_SHARED);
+	return 0;
+}
+
+STATIC int
+xfs_dm_inode_to_fh(
+	struct inode		*inode,
+	dm_fid_t		*dmfid,
+	dm_fsid_t		*dmfsid)
+{
+	xfs_inode_t		*ip = XFS_I(inode);
+
+	/* Returns negative errors to DMAPI */
+
+	if (ip->i_mount->m_fixedfsid == NULL)
+		return -EINVAL;
+
+	dmfid->dm_fid_len = sizeof(dm_fid_t) - sizeof(dmfid->dm_fid_len);
+	dmfid->dm_fid_pad = 0;
+	/*
+	 * use memcpy because the inode is a long long and there's no
+	 * assurance that dmfid->dm_fid_ino is properly aligned.
+	 */
+	memcpy(&dmfid->dm_fid_ino, &ip->i_ino, sizeof(dmfid->dm_fid_ino));
+	dmfid->dm_fid_gen = ip->i_d.di_gen;
+
+	memcpy(dmfsid, ip->i_mount->m_fixedfsid, sizeof(*dmfsid));
+	return 0;
+}
+
+STATIC void
+xfs_dm_get_fsid(
+	struct super_block	*sb,
+	dm_fsid_t		*fsid)
+{
+	memcpy(fsid, XFS_M(sb)->m_fixedfsid, sizeof(*fsid));
+}
+
+/*
+ * Filesystem operations accessed by the DMAPI core.
+ */
+static struct filesystem_dmapi_operations xfs_dmapiops = {
+	.get_fsys_vector	= xfs_dm_get_dmapiops,
+	.fh_to_inode		= xfs_dm_fh_to_inode,
+	.inode_to_fh		= xfs_dm_inode_to_fh,
+	.get_fsid		= xfs_dm_get_fsid,
+};
+
+static int __init
+xfs_dm_init(void)
+{
+	printk(KERN_INFO "SGI XFS Data Management API subsystem\n");
+
+	dmapi_register(&xfs_fs_type, &xfs_dmapiops);
+	return 0;
+}
+
+static void __exit
+xfs_dm_exit(void)
+{
+	dmapi_unregister(&xfs_fs_type);
+}
+
+MODULE_AUTHOR("Silicon Graphics, Inc.");
+MODULE_DESCRIPTION("SGI XFS dmapi subsystem");
+MODULE_LICENSE("GPL");
+
+module_init(xfs_dm_init);
+module_exit(xfs_dm_exit);
--- /dev/null
+++ b/fs/xfs/dmapi/xfs_dm.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2006 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#ifndef __XFS_DM_H__
+#define __XFS_DM_H__
+
+extern struct file_system_type xfs_fs_type;
+
+#endif /* __XFS_DM_H__ */
--- a/fs/xfs/linux-2.6/xfs_file.c
+++ b/fs/xfs/linux-2.6/xfs_file.c
@@ -47,6 +47,9 @@
 #include <linux/dcache.h>
 
 static const struct vm_operations_struct xfs_file_vm_ops;
+#ifdef HAVE_DMAPI
+static struct vm_operations_struct xfs_dmapi_file_vm_ops;
+#endif
 
 /*
  *	xfs_iozero
@@ -938,6 +941,23 @@ xfs_file_release(
 	return -xfs_release(XFS_I(inode));
 }
 
+#ifdef HAVE_DMAPI
+STATIC int
+xfs_vm_fault(
+	struct vm_area_struct	*vma,
+	struct vm_fault	*vmf)
+{
+	struct inode	*inode = vma->vm_file->f_path.dentry->d_inode;
+	struct xfs_mount *mp = XFS_M(inode->i_sb);
+
+	ASSERT_ALWAYS(mp->m_flags & XFS_MOUNT_DMAPI);
+
+	if (XFS_SEND_MMAP(mp, vma, 0))
+		return VM_FAULT_SIGBUS;
+	return filemap_fault(vma, vmf);
+}
+#endif /* HAVE_DMAPI */
+
 STATIC int
 xfs_file_readdir(
 	struct file	*filp,
@@ -978,10 +998,56 @@ xfs_file_mmap(
 	vma->vm_ops = &xfs_file_vm_ops;
 	vma->vm_flags |= VM_CAN_NONLINEAR;
 
+#ifdef HAVE_DMAPI
+	if (XFS_M(filp->f_path.dentry->d_inode->i_sb)->m_flags & XFS_MOUNT_DMAPI)
+		vma->vm_ops = &xfs_dmapi_file_vm_ops;
+#endif /* HAVE_DMAPI */
+
 	file_accessed(filp);
 	return 0;
 }
 
+#ifdef HAVE_DMAPI
+#ifdef HAVE_VMOP_MPROTECT
+STATIC int
+xfs_vm_mprotect(
+	struct vm_area_struct *vma,
+	unsigned int	newflags)
+{
+	struct inode	*inode = vma->vm_file->f_path.dentry->d_inode;
+	struct xfs_mount *mp = XFS_M(inode->i_sb);
+	int		error = 0;
+
+	if (mp->m_flags & XFS_MOUNT_DMAPI) {
+		if ((vma->vm_flags & VM_MAYSHARE) &&
+		    (newflags & VM_WRITE) && !(vma->vm_flags & VM_WRITE))
+			error = XFS_SEND_MMAP(mp, vma, VM_WRITE);
+	}
+	return error;
+}
+#endif /* HAVE_VMOP_MPROTECT */
+#endif /* HAVE_DMAPI */
+
+#ifdef HAVE_FOP_OPEN_EXEC
+/* If the user is attempting to execute a file that is offline then
+ * we have to trigger a DMAPI READ event before the file is marked as busy
+ * otherwise the invisible I/O will not be able to write to the file to bring
+ * it back online.
+ */
+STATIC int
+xfs_file_open_exec(
+	struct inode	*inode)
+{
+	struct xfs_mount *mp = XFS_M(inode->i_sb);
+	struct xfs_inode *ip = XFS_I(inode);
+
+	if (unlikely(mp->m_flags & XFS_MOUNT_DMAPI) &&
+	             DM_EVENT_ENABLED(ip, DM_EVENT_READ))
+		return -XFS_SEND_DATA(mp, DM_EVENT_READ, ip, 0, 0, 0, NULL);
+	return 0;
+}
+#endif /* HAVE_FOP_OPEN_EXEC */
+
 /*
  * mmap()d file has taken write protection fault and is being made
  * writable. We can set the page state up correctly for a writable
@@ -1033,3 +1099,13 @@ static const struct vm_operations_struct
 	.fault		= filemap_fault,
 	.page_mkwrite	= xfs_vm_page_mkwrite,
 };
+
+#ifdef HAVE_DMAPI
+static struct vm_operations_struct xfs_dmapi_file_vm_ops = {
+	.fault		= xfs_vm_fault,
+	.page_mkwrite	= xfs_vm_page_mkwrite,
+#ifdef HAVE_VMOP_MPROTECT
+	.mprotect	= xfs_vm_mprotect,
+#endif
+};
+#endif /* HAVE_DMAPI */
--- /dev/null
+++ b/fs/xfs/linux-2.6/xfs_ksyms.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2004-2008 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_bit.h"
+#include "xfs_buf.h"
+#include "xfs_log.h"
+#include "xfs_inum.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_ag.h"
+#include "xfs_dir2.h"
+#include "xfs_alloc.h"
+#include "xfs_dmapi.h"
+#include "xfs_quota.h"
+#include "xfs_mount.h"
+#include "xfs_da_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_alloc_btree.h"
+#include "xfs_ialloc_btree.h"
+#include "xfs_dir2_sf.h"
+#include "xfs_attr_sf.h"
+#include "xfs_dinode.h"
+#include "xfs_inode.h"
+#include "xfs_btree.h"
+#include "xfs_ialloc.h"
+#include "xfs_bmap.h"
+#include "xfs_rtalloc.h"
+#include "xfs_error.h"
+#include "xfs_itable.h"
+#include "xfs_rw.h"
+#include "xfs_dir2_data.h"
+#include "xfs_dir2_leaf.h"
+#include "xfs_dir2_block.h"
+#include "xfs_dir2_node.h"
+#include "xfs_acl.h"
+#include "xfs_attr.h"
+#include "xfs_attr_leaf.h"
+#include "xfs_inode_item.h"
+#include "xfs_buf_item.h"
+#include "xfs_extfree_item.h"
+#include "xfs_log_priv.h"
+#include "xfs_trans_priv.h"
+#include "xfs_trans_space.h"
+#include "xfs_utils.h"
+#include "xfs_iomap.h"
+#include "xfs_filestream.h"
+#include "xfs_vnodeops.h"
+
+EXPORT_SYMBOL(xfs_iunlock);
+EXPORT_SYMBOL(xfs_attr_remove);
+EXPORT_SYMBOL(xfs_iunlock_map_shared);
+EXPORT_SYMBOL(xfs_iget);
+EXPORT_SYMBOL(xfs_bmapi);
+EXPORT_SYMBOL(xfs_internal_inum);
+EXPORT_SYMBOL(xfs_attr_set);
+EXPORT_SYMBOL(xfs_trans_reserve);
+EXPORT_SYMBOL(xfs_trans_ijoin);
+EXPORT_SYMBOL(xfs_free_eofblocks);
+EXPORT_SYMBOL(kmem_free);
+EXPORT_SYMBOL(_xfs_trans_commit);
+EXPORT_SYMBOL(xfs_ilock);
+EXPORT_SYMBOL(xfs_attr_get);
+EXPORT_SYMBOL(xfs_readdir);
+EXPORT_SYMBOL(xfs_setattr);
+EXPORT_SYMBOL(xfs_trans_alloc);
+EXPORT_SYMBOL(xfs_trans_cancel);
+EXPORT_SYMBOL(xfs_fsync);
+EXPORT_SYMBOL(xfs_iput_new);
+EXPORT_SYMBOL(xfs_bulkstat);
+EXPORT_SYMBOL(xfs_ilock_map_shared);
+EXPORT_SYMBOL(xfs_iput);
+EXPORT_SYMBOL(xfs_trans_log_inode);
+EXPORT_SYMBOL(xfs_attr_list);
+EXPORT_SYMBOL(kmem_alloc);
+EXPORT_SYMBOL(xfs_change_file_space);
--- a/fs/xfs/linux-2.6/xfs_linux.h
+++ b/fs/xfs/linux-2.6/xfs_linux.h
@@ -160,6 +160,10 @@
 #define xfs_itruncate_data(ip, off)	\
 	(-vmtruncate(VFS_I(ip), (off)))
 
+#undef HAVE_DMAPI
+#if defined(CONFIG_XFS_DMAPI) || defined(CONFIG_XFS_DMAPI_MODULE)
+#define HAVE_DMAPI
+#endif
 
 /* Move the kernel do_div definition off to one side */
 
--- a/fs/xfs/linux-2.6/xfs_super.c
+++ b/fs/xfs/linux-2.6/xfs_super.c
@@ -1670,8 +1670,16 @@ xfs_fs_get_sb(
 	void			*data,
 	struct vfsmount		*mnt)
 {
-	return get_sb_bdev(fs_type, flags, dev_name, data, xfs_fs_fill_super,
+	int error;
+
+	error = get_sb_bdev(fs_type, flags, dev_name, data, xfs_fs_fill_super,
 			   mnt);
+	if (!error) {
+		xfs_mount_t *mp = XFS_M(mnt->mnt_sb);
+		mp->m_vfsmount = mnt;
+	}
+
+	return error;
 }
 
 static const struct super_operations xfs_super_operations = {
@@ -1689,13 +1697,14 @@ static const struct super_operations xfs
 	.show_options		= xfs_fs_show_options,
 };
 
-static struct file_system_type xfs_fs_type = {
+struct file_system_type xfs_fs_type = {
 	.owner			= THIS_MODULE,
 	.name			= "xfs",
 	.get_sb			= xfs_fs_get_sb,
 	.kill_sb		= kill_block_super,
 	.fs_flags		= FS_REQUIRES_DEV,
 };
+EXPORT_SYMBOL(xfs_fs_type);
 
 STATIC int __init
 xfs_init_zones(void)
--- a/fs/xfs/xfs_dmops.c
+++ b/fs/xfs/xfs_dmops.c
@@ -40,9 +40,21 @@ int
 xfs_dmops_get(struct xfs_mount *mp)
 {
 	if (mp->m_flags & XFS_MOUNT_DMAPI) {
-		cmn_err(CE_WARN,
-			"XFS: dmapi support not available in this kernel.");
-		return EINVAL;
+		struct xfs_dmops *ops;
+
+		ops = symbol_get(xfs_dmcore_xfs);
+		if (!ops) {
+			request_module("xfs_dmapi");
+			ops = symbol_get(xfs_dmcore_xfs);
+		}
+
+		if (!ops) {
+			cmn_err(CE_WARN, "XFS: no dmapi support available.");
+			return EINVAL;
+		}
+		mp->m_dm_ops = ops;
+	} else {
+		mp->m_dm_ops = &xfs_dmcore_stub;
 	}
 
 	mp->m_dm_ops = &xfs_dmcore_stub;
@@ -52,4 +64,6 @@ xfs_dmops_get(struct xfs_mount *mp)
 void
 xfs_dmops_put(struct xfs_mount *mp)
 {
+	if (mp->m_dm_ops != &xfs_dmcore_stub)
+		symbol_put(xfs_dmcore_xfs);
 }
--- a/fs/xfs/xfs_itable.c
+++ b/fs/xfs/xfs_itable.c
@@ -39,7 +39,7 @@
 #include "xfs_error.h"
 #include "xfs_btree.h"
 
-STATIC int
+int
 xfs_internal_inum(
 	xfs_mount_t	*mp,
 	xfs_ino_t	ino)
--- a/fs/xfs/xfs_itable.h
+++ b/fs/xfs/xfs_itable.h
@@ -99,6 +99,11 @@ xfs_bulkstat_one(
 	void			*dibuff,
 	int			*stat);
 
+int
+xfs_internal_inum(
+       xfs_mount_t             *mp,
+       xfs_ino_t               ino);
+
 typedef int (*inumbers_fmt_pf)(
 	void			__user *ubuffer, /* buffer to write to */
 	const xfs_inogrp_t	*buffer,	/* buffer to read from */
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -259,6 +259,7 @@ typedef struct xfs_mount {
 	__int64_t		m_update_flags;	/* sb flags we need to update
 						   on the next remount,rw */
	struct list_head	m_mplist;	/* inode shrinker mount list */
+	struct vfsmount         *m_vfsmount;
 } xfs_mount_t;
 
 /*
--- a/fs/xfs/xfs_rw.c
+++ b/fs/xfs/xfs_rw.c
@@ -202,3 +202,4 @@ xfs_get_extsz_hint(
 
 	return extsz;
 }
+EXPORT_SYMBOL(xfs_get_extsz_hint);
--- a/fs/xfs/xfs_rw.h
+++ b/fs/xfs/xfs_rw.h
@@ -45,5 +45,10 @@ extern int xfs_read_buf(struct xfs_mount
 extern void xfs_ioerror_alert(char *func, struct xfs_mount *mp,
 				xfs_buf_t *bp, xfs_daddr_t blkno);
 extern xfs_extlen_t xfs_get_extsz_hint(struct xfs_inode *ip);
+/*
+ * Prototypes for functions in xfs_vnodeops.c.
+ */
+extern int xfs_free_eofblocks(struct xfs_mount *mp, struct xfs_inode *ip,
+                       int flags);
 
 #endif /* __XFS_RW_H__ */
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -593,7 +593,7 @@ xfs_readlink(
  * when the link count isn't zero and by xfs_dm_punch_hole() when
  * punching a hole to EOF.
  */
-STATIC int
+int
 xfs_free_eofblocks(
 	xfs_mount_t	*mp,
 	xfs_inode_t	*ip,
