File: [Development] / xfs-linux / linux-2.6 / xfs_ioctl.c (download)
Revision 1.37, Thu May 17 02:58:46 2001 UTC (16 years, 5 months ago) by lord
Branch: MAIN
Changes since 1.36: +42 -0
lines
Add new FREEZE and THAW ioctl calls. The FREEZE call will pause all
activity in the filesystem which causes modifications, and flush all
state out to disk. The thaw ioctl allows modifications again.
|
/*
* Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 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.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
*/
#include <xfs.h>
#include <xfs_fsops.h>
#include <xfs_dfrag.h>
#include <linux/xfs_iops.h>
#include <linux/locks.h>
#include <linux/smp_lock.h>
#include <linux/dcache.h>
/*
* The "open_by_handle() & readlink_by_handle() ioctl's
* require 'root', or 'SysAdmin' capabilies.
* To change to a simple 'permission' test,
* uncomment the following #define.
*/
/* #define PERMISSIONS_BY_USER */
int
xfs_getattr(
bhv_desc_t *bdp,
vattr_t *vap,
int flags,
cred_t *credp);
int
xfs_setattr(
bhv_desc_t *bdp,
vattr_t *vap,
int flags,
cred_t *credp);
int
xfs_set_dmattrs (
bhv_desc_t *bdp,
u_int evmask,
u_int16_t state,
cred_t *credp);
int
xfs_get_uiosize(
xfs_mount_t *mp,
xfs_inode_t *ip,
struct biosize *bs,
cred_t *credp);
int
xfs_set_uiosize(
xfs_mount_t *mp,
xfs_inode_t *ip,
uint flags,
int read_iosizelog,
int write_iosizelog,
cred_t *credp);
int
xfs_change_file_space(
bhv_desc_t *bdp,
int cmd,
xfs_flock64_t *bf,
xfs_off_t offset,
cred_t *credp,
int attr_flags);
/*
* xfs_find_handle: map from userspace xfs_fsop_handlereq structure to
* a file or fs handle - used for XFS_IOC_PATH_TO_FSHANDLE,
* XFS_IOC_PATH_TO_HANDLE and XFS_IOC_FD_TO_HANDLE.
*/
int
xfs_find_handle(
unsigned int cmd,
unsigned long arg)
{
int hsize;
xfs_handle_t handle;
xfs_fsop_handlereq_t hreq;
struct inode *inode;
struct vnode *vp;
int only_fsid;
/* read handle request from user */
if (copy_from_user(&hreq, (struct xfs_fsop_handlereq *)arg,
sizeof(struct xfs_fsop_handlereq)))
return -XFS_ERROR(EFAULT);
/* zero our handle */
bzero((char *)&handle, sizeof(handle));
/* now we need the inode in question */
only_fsid=0;
switch (cmd) {
case XFS_IOC_PATH_TO_FSHANDLE:
only_fsid=1;
/* fallthrough */
case XFS_IOC_PATH_TO_HANDLE: {
struct nameidata nd;
char *path;
int error;
/* we need the path */
path = getname(hreq.path);
if (IS_ERR(path))
return PTR_ERR(path);
/* traverse the path */
error = 0;
if (path_init(path, LOOKUP_POSITIVE, &nd))
error = path_walk(path, &nd);
putname(path);
if (error)
return error;
ASSERT(nd.dentry);
ASSERT(nd.dentry->d_inode);
inode = igrab(nd.dentry->d_inode);
path_release(&nd);
break;
}
case XFS_IOC_FD_TO_HANDLE: {
struct file * file;
file = fget(hreq.fd);
if (!file)
return -EBADF;
ASSERT(file->f_dentry);
ASSERT(file->f_dentry->d_inode);
inode = igrab(file->f_dentry->d_inode);
fput(file);
break;
}
default:
ASSERT(0);
return -XFS_ERROR(EINVAL);
}
/* we need the vnode */
vp = LINVFS_GET_VP(inode);
if (!vp || !vp->v_vfsp->vfs_altfsid) {
/* we're not in XFS anymore, Toto */
iput(inode);
return -XFS_ERROR(EINVAL);
}
if (vp->v_type != VREG && vp->v_type != VDIR && vp->v_type != VLNK) {
iput(inode);
return -XFS_ERROR(EBADF);
}
/* now we can grab the fsid */
memcpy(&handle.ha_fsid, vp->v_vfsp->vfs_altfsid, sizeof(xfs_fsid_t));
hsize = sizeof(xfs_fsid_t);
if (!only_fsid) {
xfs_inode_t *ip;
bhv_desc_t *bhv;
int lock_mode;
/* we need to get access to the xfs_inode to read the generation */
VN_BHV_READ_LOCK(&(vp)->v_bh);
bhv = VNODE_TO_FIRST_BHV(vp);
ASSERT(bhv);
ip = XFS_BHVTOI(bhv);
ASSERT(ip);
lock_mode = xfs_ilock_map_shared(ip);
/* fill in fid section of handle from inode */
handle.ha_fid.xfs_fid_len = sizeof(xfs_fid_t) -
sizeof(handle.ha_fid.xfs_fid_len);
handle.ha_fid.xfs_fid_pad = 0;
handle.ha_fid.xfs_fid_gen = ip->i_d.di_gen;
handle.ha_fid.xfs_fid_ino = ip->i_ino;
xfs_iunlock_map_shared(ip, lock_mode);
VN_BHV_READ_UNLOCK(&(vp)->v_bh);
hsize = XFS_HSIZE(handle);
}
/* now copy our handle into the user buffer & write out the size */
if (copy_to_user((xfs_handle_t *)hreq.ohandle, &handle, hsize) ||
copy_to_user(hreq.ohandlen, &hsize, sizeof(__s32))) {
iput(inode);
return -XFS_ERROR(EFAULT);
}
iput(inode);
return 0;
}
int
xfs_open_by_handle(
unsigned int cmd,
unsigned long arg,
struct file *parfilp,
struct inode *parinode,
vfs_t *vfsp,
xfs_mount_t *mp)
{
int error;
int new_fd;
int permflag;
__u32 igen;
struct dentry *dentry = NULL;
struct file *filp = NULL;
struct inode *inode = NULL;
struct list_head *lp;
void *hanp;
size_t hlen;
ino_t ino;
xfs_fid_t *xfid;
xfs_handle_t *handlep;
xfs_handle_t handle;
xfs_fsop_handlereq_t hreq;
xfs_inode_t *ip;
#ifndef PERMISSIONS_BY_USER
/*
* Only allow Sys Admin capable users.
*/
if (! capable(CAP_SYS_ADMIN))
return -EPERM;
#endif /* ! PERMISSIONS_BY_USER */
/*
* Only allow handle opens under a directory.
*/
if ( !S_ISDIR(parinode->i_mode) )
return -ENOTDIR;
/*
* Copy the handle down from the user and validate
* that it looks to be in the correct format.
*/
if (copy_from_user(&hreq, (struct xfs_fsop_handlereq *)arg,
sizeof(struct xfs_fsop_handlereq)))
return -XFS_ERROR(EFAULT);
hanp = hreq.ihandle;
hlen = hreq.ihandlen;
handlep = &handle;
/*
* gethandle(hanp, hlen, &handle)
*/
if (hlen < sizeof(handlep->ha_fsid) || hlen > sizeof(*handlep))
return -XFS_ERROR(EINVAL);
if (copy_from_user(handlep, hanp, hlen))
return -XFS_ERROR(EFAULT);
if (hlen < sizeof(*handlep))
bzero(((char *)handlep) + hlen,
sizeof(*handlep) - hlen);
if (hlen > sizeof(handlep->ha_fsid)) {
if ( handlep->ha_fid.xfs_fid_len !=
(hlen - sizeof(handlep->ha_fsid)
- sizeof(handlep->ha_fid.xfs_fid_len))
|| handlep->ha_fid.xfs_fid_pad) {
return -XFS_ERROR(EINVAL);
}
}
/*
* Crack the handle, obtain the inode # & generation #
*/
xfid = (struct xfs_fid *)&handlep->ha_fid;
if (xfid->xfs_fid_len == sizeof(*xfid) - sizeof(xfid->xfs_fid_len)) {
ino = xfid->xfs_fid_ino;
igen = xfid->xfs_fid_gen;
} else {
/*
* Invalid. Since handles can be created in user
* space and passed in, this is not cause for a panic.
*/
return -XFS_ERROR(EINVAL);
}
/*
* Get the XFS inode, building a vnode to go with it.
*/
error = xfs_iget(mp, NULL, ino, XFS_ILOCK_SHARED, &ip, 0);
if (error)
return -error;
if (ip == NULL)
return -XFS_ERROR(EIO);
if (ip->i_d.di_mode == 0 || ip->i_d.di_gen != igen) {
xfs_iput(ip, XFS_ILOCK_SHARED);
return -XFS_ERROR(ENOENT);
}
inode = XFS_ITOV(ip)->v_inode;
xfs_iunlock(ip, XFS_ILOCK_SHARED);
/*
* Restrict handle operations to directories & regular files.
*/
if (! (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)) ) {
iput(inode);
return -XFS_ERROR(EINVAL);
}
/*
* Put open permission in namei format.
*/
permflag = hreq.oflags;
if ((permflag+1) & O_ACCMODE)
permflag++;
if (permflag & O_TRUNC)
permflag |= 2;
/*
* Can't write directories.
*/
if ( S_ISDIR(inode->i_mode) && (permflag & FMODE_WRITE)) {
iput(inode);
return -XFS_ERROR(EISDIR);
}
/*
* Create new_fd
*/
if ((new_fd = get_unused_fd()) < 0) {
iput(inode);
return new_fd;
}
/* now to find a dentry.
* If possible, get a well-connected one
*/
spin_lock(&dcache_lock);
for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) {
dentry = list_entry(lp,struct dentry, d_alias);
if (! (dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) {
dget_locked(dentry);
spin_unlock(&dcache_lock);
iput(inode);
goto found;
}
}
spin_unlock(&dcache_lock);
/*
* ELSE didn't find dentry Create anonymous dcache entry.
*/
dentry = d_alloc_root(inode);
if (dentry == NULL) {
iput(inode);
put_unused_fd(new_fd);
return -XFS_ERROR(ENOMEM);
}
/*
* Keep nfsd happy.
*/
dentry->d_flags |= DCACHE_NFSD_DISCONNECTED;
/*
* Make sure dput can find this dcache entry.
*/
d_rehash(dentry);
found:
/*
* Make sure umount returns an EBUSY on umounts while this file is open.
*/
mntget(parfilp->f_vfsmnt);
/*
* Create file pointer.
*/
filp = dentry_open(dentry, parfilp->f_vfsmnt, hreq.oflags);
if (IS_ERR(filp)) {
dput(dentry);
put_unused_fd(new_fd);
return -XFS_ERROR(-PTR_ERR(filp));
}
#ifdef PERMISSIONS_BY_USER
/*
* Do permission/write checks
*/
permflag = 0;
if (filp->f_mode & FMODE_READ)
permflag |= MAY_READ;
if (filp->f_mode & FMODE_WRITE)
permflag |= MAY_WRITE;
if (error = permission(inode, permflag)) {
put_unused_fd(new_fd);
fput(filp);
return -XFS_ERROR(-error);
}
#endif /* PERMISSIONS_BY_USER */
fd_install(new_fd, filp);
return new_fd;
}
int
xfs_readlink_by_handle(
unsigned int cmd,
unsigned long arg,
struct file *parfilp,
struct inode *parinode,
vfs_t *vfsp,
xfs_mount_t *mp)
{
int error;
int rlsize;
__u32 igen;
struct iovec aiov;
struct uio auio;
void *hanp;
size_t hlen;
vnode_t *vp = NULL;
xfs_fid_t *xfid;
xfs_handle_t *handlep;
xfs_handle_t handle;
xfs_ino_t ino;
xfs_inode_t *ip = NULL;
xfs_fsop_handlereq_t hreq;
__u32 olen;
#ifndef PERMISSIONS_BY_USER
/*
* Only allow Sys Admin capable users.
*/
if (! capable(CAP_SYS_ADMIN))
return -EPERM;
#endif /* ! PERMISSIONS_BY_USER */
/*
* Only allow handle opens under a directory.
*/
if ( !S_ISDIR(parinode->i_mode) )
return -ENOTDIR;
/*
* Copy the handle down from the user and validate
* that it looks to be in the correct format.
*/
if (copy_from_user(&hreq, (struct xfs_fsop_handlereq *)arg,
sizeof(struct xfs_fsop_handlereq)))
return -XFS_ERROR(EFAULT);
hanp = hreq.ihandle;
hlen = hreq.ihandlen;
handlep = &handle;
/*
* gethandle(hanp, hlen, &handle)
*/
if (hlen < sizeof(handlep->ha_fsid) || hlen > sizeof(*handlep))
return -XFS_ERROR(EINVAL);
if (copy_from_user(handlep, hanp, hlen))
return -XFS_ERROR(EFAULT);
if (hlen < sizeof(*handlep))
bzero(((char *)handlep) + hlen,
sizeof(*handlep) - hlen);
if (hlen > sizeof(handlep->ha_fsid)) {
if ( handlep->ha_fid.xfs_fid_len !=
(hlen - sizeof(handlep->ha_fsid)
- sizeof(handlep->ha_fid.xfs_fid_len))
|| handlep->ha_fid.xfs_fid_pad) {
return -XFS_ERROR(EINVAL);
}
}
/*
* Crack the handle, obtain the inode # & generation #
*/
/*
* handle_to_vp(&handle)
* VFS_VGET (vfsp, &vp, &handlep->ha_fid, error)
* bdp, **vp, fidp;
*/
xfid = (struct xfs_fid *)&handlep->ha_fid;
if (xfid->xfs_fid_len == sizeof(*xfid) - sizeof(xfid->xfs_fid_len)) {
ino = xfid->xfs_fid_ino;
igen = xfid->xfs_fid_gen;
} else {
/*
* Invalid. Since handles can be created in user
* space and passed in via gethandle(), this is not
* cause for a panic.
*/
return -XFS_ERROR(EINVAL);
}
/*
* Get the XFS inode, building a vnode to go with it.
*/
error = xfs_iget(mp, NULL, ino, XFS_ILOCK_SHARED, &ip, 0);
if (error)
return -error;
if (ip == NULL)
return -XFS_ERROR(EIO);
if (ip->i_d.di_mode == 0 || ip->i_d.di_gen != igen) {
xfs_iput(ip, XFS_ILOCK_SHARED);
return -XFS_ERROR(ENOENT);
}
vp = XFS_ITOV(ip);
xfs_iunlock(ip, XFS_ILOCK_SHARED);
/*
* Restrict handle operations to symlinks.
*/
if (vp->v_type != VLNK) {
VN_RELE(vp);
return -XFS_ERROR(EINVAL);
}
aiov.iov_base = hreq.ohandle;
if (copy_from_user(&olen, hreq.ohandlen, sizeof(__u32)))
return -XFS_ERROR(EFAULT);
aiov.iov_len = olen;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_fmode = FINVIS;
auio.uio_offset = 0;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_resid = olen;
VOP_READLINK(vp, &auio, get_current_cred(), error);
VN_RELE(vp);
rlsize = olen - auio.uio_resid;
return rlsize;
}
int
xfs_attrctl_by_handle(
unsigned int cmd,
unsigned long arg,
struct file *parfilp,
struct inode *parinode,
vfs_t *vfsp,
xfs_mount_t *mp)
{
int error;
xfs_fsop_attr_handlereq_t attr_hreq;
xfs_fsop_handlereq_t hreq;
attr_op_t *ops;
void *hanp;
size_t hlen;
xfs_handle_t handle;
xfs_handle_t *handlep;
xfs_fid_t *xfid;
xfs_ino_t xinode;
xfs_inode_t *xip = NULL;
__u32 xigen;
vnode_t *vp = NULL;
struct inode *inode = NULL;
#ifndef PERMISSIONS_BY_USER
/*
* Only allow Sys Admin capable users.
*/
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
#endif /* !PERMISSIONS_BY_USER */
/*
* Only allow handle opens under a directory.
*/
if (!S_ISDIR(parinode->i_mode))
return -ENOTDIR;
/*
* Copy the compound attribute/handle structure down.
*/
if (copy_from_user(&attr_hreq, (struct xfs_fsop_attr_handlereq *)arg,
sizeof(struct xfs_fsop_attr_handlereq)))
return -XFS_ERROR(EFAULT);
/*
* Now copy the handle down from the user and validate
* that it looks to be in the correct format.
*/
if (copy_from_user(&hreq, (struct xfs_fsop_handlereq *)attr_hreq.hreq,
sizeof(struct xfs_fsop_handlereq)))
return -XFS_ERROR(EFAULT);
if (!attr_hreq.ops)
return -XFS_ERROR(EINVAL);
hanp = hreq.ihandle;
hlen = hreq.ihandlen;
handlep = &handle;
if (hlen < sizeof(handlep->ha_fsid) || hlen > sizeof(*handlep))
return -XFS_ERROR(EINVAL);
if (copy_from_user(handlep, hanp, hlen))
return -XFS_ERROR(EFAULT);
if (hlen < sizeof(*handlep))
bzero(((char *)handlep) + hlen, sizeof(*handlep) - hlen);
if (hlen > sizeof(handlep->ha_fsid)) {
if (handlep->ha_fid.xfs_fid_len !=
(hlen - sizeof(handlep->ha_fsid)
- sizeof(handlep->ha_fid.xfs_fid_len))
|| handlep->ha_fid.xfs_fid_pad) {
return -XFS_ERROR(EINVAL);
}
}
/*
* Crack the handle, obtain the inode # & generation #
*/
xfid = (struct xfs_fid *)&handlep->ha_fid;
if (xfid->xfs_fid_len == sizeof(*xfid) - sizeof(xfid->xfs_fid_len)) {
xinode = xfid->xfs_fid_ino;
xigen = xfid->xfs_fid_gen;
} else {
/*
* Invalid. Since handles can be created in user
* space and passed in via gethandle(), this is not
* cause for a panic.
*/
return -XFS_ERROR(EINVAL);
}
/* get the xfs inode */
error = xfs_iget(mp, NULL, xinode, XFS_ILOCK_SHARED, &xip, 0);
if (error)
return -error;
if (xip == NULL)
return -XFS_ERROR(EIO);
if (xip->i_d.di_mode == 0 || xip->i_d.di_gen != xigen) {
xfs_iput(xip, XFS_ILOCK_SHARED);
return -XFS_ERROR(ENOENT);
}
/* get vnode and linux inode */
vp = XFS_ITOV(xip);
inode = LINVFS_GET_IP(vp);
xfs_iunlock(xip, XFS_ILOCK_SHARED);
/* Copyin and hand off to VFS */
lock_kernel();
error = -EINVAL;
/* allocate space for attribute ops */
ops = (attr_op_t *) kmalloc(attr_hreq.count * sizeof(attr_op_t), GFP_KERNEL);
if (!ops) {
error = -ENOMEM;
goto unlock;
}
if (copy_from_user(ops, attr_hreq.ops, attr_hreq.count * sizeof(attr_op_t)) != 0) {
error = -EFAULT;
goto free_mem;
}
UPDATE_ATIME(inode);
/* call through to the vfs: note we know this is an XFS inode */
error = inode->i_op->attrctl(inode, ops, attr_hreq.count);
VN_RELE(vp);
if (copy_to_user(attr_hreq.ops, ops, attr_hreq.count * sizeof(attr_op_t)) != 0) {
error = -EFAULT;
goto free_mem;
}
free_mem:
kfree(ops);
unlock:
unlock_kernel();
return error;
}
int
xfs_ioctl(
bhv_desc_t *bdp,
struct inode *inode,
struct file *filp,
unsigned int cmd,
unsigned long arg)
{
int error;
struct biosize bs;
struct dioattr da;
struct fsdmidata dmi;
struct fsxattr fa;
cred_t cred; /* Temporary cred workaround */
vattr_t va;
vnode_t *vp;
vfs_t *vfsp;
xfs_fsop_geom_t fsgeo;
xfs_flock64_t bf;
xfs_inode_t *ip;
xfs_mount_t *mp;
vp = LINVFS_GET_VN_ADDRESS(inode);
ASSERT(vp);
vn_trace_entry(vp, "xfs_ioctl", (inst_t *)__return_address);
ip = XFS_BHVTOI(bdp);
mp = ip->i_mount;
vfsp = LINVFS_GET_VFS(inode->i_sb);
ASSERT(vfsp);
switch (cmd) {
case XFS_IOC_ALLOCSP:
case XFS_IOC_FREESP:
case XFS_IOC_RESVSP:
case XFS_IOC_UNRESVSP:
case XFS_IOC_ALLOCSP64:
case XFS_IOC_FREESP64:
case XFS_IOC_RESVSP64:
case XFS_IOC_UNRESVSP64: {
int attr_flags;
if (filp->f_flags & O_RDONLY)
return -XFS_ERROR(EBADF);
if (vp->v_type != VREG)
return -XFS_ERROR(EINVAL);
if (copy_from_user(&bf, (xfs_flock64_t *)arg,
sizeof(xfs_flock64_t)))
return -XFS_ERROR(EFAULT);
attr_flags = (filp->f_flags & (O_NDELAY|O_NONBLOCK))
? ATTR_NONBLOCK : 0;
if (filp->f_flags & O_INVISIBLE)
attr_flags |= ATTR_DMI;
error = xfs_change_file_space(bdp, cmd, &bf, filp->f_pos,
&cred, attr_flags);
if (error)
return -error;
return 0;
}
case XFS_IOC_DIOINFO:
da.d_miniosz = mp->m_sb.sb_blocksize;
da.d_mem = 512;
/*
* this only really needs to be BBSIZE.
* it is set to the file system block size to
* avoid having to do block zeroing on short writes.
*/
da.d_maxiosz = XFS_FSB_TO_B(mp,
XFS_B_TO_FSBT(mp, pagebuf_max_direct()));
if (copy_to_user((struct dioattr *)arg, &da,
sizeof(struct dioattr)))
return -XFS_ERROR(EFAULT);
return 0;
case XFS_IOC_FSBULKSTAT_SINGLE:
case XFS_IOC_FSBULKSTAT: {
int count; /* # of records returned */
xfs_ino_t inlast; /* last inode number */
int done; /* = 1 if there are more
* stats to get and if
* bulkstat should be called
* again.
* This is unused in syssgi
* but used in dmi */
xfs_fsop_bulkreq_t bulkreq;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (copy_from_user(&bulkreq, (xfs_fsop_bulkreq_t *)arg,
sizeof(xfs_fsop_bulkreq_t)))
return -XFS_ERROR(EFAULT);
if (copy_from_user(&inlast, (__s64 *)bulkreq.lastip,
sizeof(__s64)))
return -XFS_ERROR(EFAULT);
if ((count = bulkreq.icount) <= 0)
return -XFS_ERROR(EINVAL);
if (cmd == XFS_IOC_FSBULKSTAT_SINGLE) {
error = xfs_bulkstat_single(mp, &inlast,
bulkreq.ubuffer, &done);
} else {
if (count == 1 && inlast != 0) {
inlast++;
error = xfs_bulkstat_single(mp, &inlast,
bulkreq.ubuffer, &done);
} else {
error = xfs_bulkstat(mp, NULL, &inlast, &count,
(bulkstat_one_pf)xfs_bulkstat_one,
sizeof(xfs_bstat_t), bulkreq.ubuffer,
BULKSTAT_FG_QUICK, &done);
}
}
if (error)
return -error;
if (bulkreq.ocount != NULL) {
if (copy_to_user((xfs_ino_t *)bulkreq.lastip, &inlast,
sizeof(xfs_ino_t)))
return -XFS_ERROR(EFAULT);
if (copy_to_user((__s32 *)bulkreq.ocount, &count,
sizeof(count)))
return -XFS_ERROR(EFAULT);
}
return 0;
}
case XFS_IOC_FSGEOMETRY:
error = xfs_fs_geometry(mp, &fsgeo, 3);
if (error)
return -error;
if (copy_to_user((xfs_fsop_geom_t *)arg, &fsgeo,
sizeof(xfs_fsop_geom_t)))
return -XFS_ERROR(EFAULT);
return 0;
case XFS_IOC_FSGETXATTR:
va.va_mask = AT_XFLAGS|AT_EXTSIZE|AT_NEXTENTS;
error = xfs_getattr(bdp, &va, 0, &cred);
if (error)
return -error;
fa.fsx_xflags = va.va_xflags;
fa.fsx_extsize = va.va_extsize;
fa.fsx_nextents = va.va_nextents;
if (copy_to_user((struct fsxattr *)arg, &fa,
sizeof(struct fsxattr)))
return -XFS_ERROR(EFAULT);
return 0;
case XFS_IOC_FSSETXATTR: {
int attr_flags;
if (copy_from_user(&fa, (struct fsxattr *)arg,
sizeof(struct fsxattr)))
return -XFS_ERROR(EFAULT);
va.va_mask = AT_XFLAGS | AT_EXTSIZE;
va.va_xflags = fa.fsx_xflags;
va.va_extsize = fa.fsx_extsize;
attr_flags = (filp->f_flags & (O_NDELAY|O_NONBLOCK) )
? ATTR_NONBLOCK : 0;
error = xfs_setattr(bdp, &va, attr_flags, &cred);
if (error)
return -error;
return 0;
}
case XFS_IOC_FSGETXATTRA:
va.va_mask = AT_XFLAGS|AT_EXTSIZE|AT_ANEXTENTS;
error = xfs_getattr(bdp, &va, 0, &cred);
if (error)
return -error;
fa.fsx_xflags = va.va_xflags;
fa.fsx_extsize = va.va_extsize;
fa.fsx_nextents = va.va_anextents;
if (copy_to_user((struct fsxattr *)arg, &fa,
sizeof(struct fsxattr)))
return -XFS_ERROR(EFAULT);
return 0;
case XFS_IOC_GETBIOSIZE:
error = xfs_get_uiosize(mp, ip, &bs, &cred);
if (error)
return -error;
if (copy_to_user((struct biosize *)arg, &bs,
sizeof(struct biosize)))
return -XFS_ERROR(EFAULT);
return 0;
case XFS_IOC_SETBIOSIZE:
if (copy_from_user(&bs, (struct biosize *)arg,
sizeof(struct biosize)))
return -XFS_ERROR(EFAULT);
error = xfs_set_uiosize(mp, ip, bs.biosz_flags, bs.biosz_read,
bs.biosz_write, &cred);
if (error)
return -error;
return 0;
case XFS_IOC_FSSETDM:
if (copy_from_user(&dmi, (struct fsdmidata *)arg,
sizeof(struct fsdmidata)))
return -XFS_ERROR(EFAULT);
error = xfs_set_dmattrs(bdp, dmi.fsd_dmevmask, dmi.fsd_dmstate,
&cred);
if (error)
return -error;
return 0;
case XFS_IOC_GETBMAP:
case XFS_IOC_GETBMAPA: {
struct getbmap bm;
int iflags;
if (copy_from_user(&bm, (struct getbmap *)arg,
sizeof(struct getbmap)))
return -XFS_ERROR(EFAULT);
if (bm.bmv_count < 2)
return -XFS_ERROR(EINVAL);
iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
if (filp->f_flags & O_INVISIBLE)
iflags |= BMV_IF_NO_DMAPI_READ;
error = xfs_getbmap(bdp, &bm,
(struct getbmap *)arg + 1, iflags);
if (error)
return -error;
if (copy_to_user((struct getbmap *)arg, &bm, sizeof(bm)))
return -XFS_ERROR(EFAULT);
return 0;
}
case XFS_IOC_GETBMAPX: {
struct getbmapx bmx;
struct getbmap bm;
int iflags;
if (copy_from_user(&bmx, (struct getbmapx *)arg,
sizeof(struct getbmapx)))
return -XFS_ERROR(EFAULT);
if (bmx.bmv_count < 2)
return -XFS_ERROR(EINVAL);
/*
* Map input getbmapx structure to a getbmap
* structure for xfs_getbmap.
*/
GETBMAP_CONVERT(bmx, bm);
iflags = bmx.bmv_iflags;
if (iflags & (~BMV_IF_VALID))
return -XFS_ERROR(EINVAL);
iflags |= BMV_IF_EXTENDED;
error = xfs_getbmap(bdp, &bm,
(struct getbmapx *)arg + 1, iflags);
if (error)
return -error;
GETBMAP_CONVERT(bm, bmx);
if (copy_to_user((struct getbmapx *)arg, &bmx, sizeof(bmx)))
return -XFS_ERROR(EFAULT);
return 0;
}
case XFS_IOC_PATH_TO_FSHANDLE:
case XFS_IOC_FD_TO_HANDLE:
case XFS_IOC_PATH_TO_HANDLE: {
/*
* XFS_IOC_PATH_TO_FSHANDLE
* returns fs handle for a mount point or path within
* that mount point
* XFS_IOC_FD_TO_HANDLE
* returns full handle for a FD opened in user space
* XFS_IOC_PATH_TO_HANDLE
* returns full handle for a path
*/
return xfs_find_handle(cmd, arg);
}
case XFS_IOC_OPEN_BY_HANDLE: {
return xfs_open_by_handle(cmd, arg, filp, inode, vfsp, mp);
}
case XFS_IOC_READLINK_BY_HANDLE: {
return xfs_readlink_by_handle(cmd, arg, filp, inode, vfsp, mp);
}
case XFS_IOC_ATTRCTL_BY_HANDLE: {
return xfs_attrctl_by_handle(cmd, arg, filp, inode, vfsp, mp);
}
case XFS_IOC_SWAPEXT: {
error = xfs_swapext((struct xfs_swapext *)arg);
if (error)
return -error;
return 0;
}
case XFS_IOC_GETFSUUID: {
if (copy_to_user((char *)arg, (char *)&mp->m_sb.sb_uuid,
sizeof(uuid_t)))
return -XFS_ERROR(EFAULT);
return 0;
}
case XFS_IOC_FSCOUNTS: {
xfs_fsop_counts_t out;
error = xfs_fs_counts(mp, &out);
if (error)
return -error;
if (copy_to_user((char *)arg, &out, sizeof(xfs_fsop_counts_t)))
return -XFS_ERROR(EFAULT);
return 0;
}
case XFS_IOC_SET_RESBLKS: {
xfs_fsop_resblks_t inout;
__uint64_t in;
error = copy_from_user(&inout, (char *)arg, sizeof(xfs_fsop_resblks_t));
if (error)
return -XFS_ERROR(EFAULT);
/* input parameter is passed in resblks field of structure */
in=inout.resblks;
error = xfs_reserve_blocks(mp, &in, &inout);
if (copy_to_user((char *)arg, &inout, sizeof(xfs_fsop_resblks_t)))
return -XFS_ERROR(EFAULT);
return 0;
}
case XFS_IOC_GET_RESBLKS: {
xfs_fsop_resblks_t out;
error = xfs_reserve_blocks(mp, NULL, &out);
if (error)
return -error;
if (copy_to_user((char *)arg, &out, sizeof(xfs_fsop_resblks_t)))
return -XFS_ERROR(EFAULT);
return 0;
}
case XFS_IOC_FSGROWFSDATA: {
xfs_growfs_data_t in;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (copy_from_user(&in, (char *)arg, sizeof(xfs_growfs_data_t)))
return -XFS_ERROR(EFAULT);
error = xfs_growfs_data(mp, &in);
if (error)
return -error;
return 0;
}
case XFS_IOC_FSGROWFSLOG: {
xfs_growfs_log_t in;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (copy_from_user(&in, (char *)arg, sizeof(xfs_growfs_log_t)))
return -XFS_ERROR(EFAULT);
error = xfs_growfs_log(mp, &in);
if (error)
return -error;
return 0;
}
case XFS_IOC_FSGROWFSRT: {
xfs_growfs_rt_t in;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (copy_from_user(&in, (char *)arg, sizeof(xfs_growfs_rt_t)))
return -XFS_ERROR(EFAULT);
error = xfs_growfs_rt(mp, &in);
if (error)
return -error;
return 0;
}
case XFS_IOC_FREEZE: {
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
/* Stop new writers */
xfs_start_freeze(mp, XFS_FREEZE_WRITE);
/* Flush delalloc and delwri data */
VFS_SYNC(vfsp, SYNC_DELWRI|SYNC_WAIT, sys_cred, error);
/* Pause transaction subsystem */
xfs_start_freeze(mp, XFS_FREEZE_TRANS);
/* Flush log to disk */
xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE|XFS_LOG_SYNC);
/* Flush any remaining inodes into buffers */
VFS_SYNC(vfsp, SYNC_BDFLUSH|SYNC_ATTR, sys_cred, error);
/* Push all the buffers out to disk */
xfs_binval(mp->m_ddev_targ);
if (mp->m_rtdev != NODEV) {
xfs_binval(mp->m_rtdev_targ);
}
/* Push the superblock and write an unmount record */
xfs_log_unmount_write(mp);
xfs_unmountfs_writesb(mp);
return 0;
}
case XFS_IOC_THAW: {
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
xfs_unmountfs_writesb(mp);
xfs_finish_freeze(mp);
return 0;
}
#if (defined(DEBUG) || defined(INDUCE_IO_ERROR))
case XFS_IOC_ERROR_INJECTION: {
xfs_error_injection_t in;
error = copy_from_user(&in, (char *)arg, sizeof(xfs_error_injection_t));
if (error) {
return -error;
}
error = xfs_errortag_add(in.errtag, mp);
if (error) {
return -error;
}
return 0;
}
case XFS_IOC_ERROR_CLEARALL: {
error = xfs_errortag_clearall(mp);
return -error;
}
#endif /* DEBUG || INDUCE_IO_ERROR */
default:
return -EINVAL;
}
}