3809 lines
95 KiB
Plaintext
3809 lines
95 KiB
Plaintext
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 *)®ion, 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, ®ion, 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( ®ion, 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,
|