2018-06-06 10:42:14 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
2005-11-02 11:58:39 +08:00
|
|
|
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
|
|
|
* All Rights Reserved.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
#include "xfs.h"
|
|
|
|
#include "xfs_fs.h"
|
2013-10-23 07:36:05 +08:00
|
|
|
#include "xfs_shared.h"
|
2013-10-23 07:50:10 +08:00
|
|
|
#include "xfs_format.h"
|
|
|
|
#include "xfs_log_format.h"
|
|
|
|
#include "xfs_trans_resv.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
#include "xfs_mount.h"
|
|
|
|
#include "xfs_inode.h"
|
|
|
|
#include "xfs_rtalloc.h"
|
2019-07-03 00:39:40 +08:00
|
|
|
#include "xfs_iwalk.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
#include "xfs_itable.h"
|
2005-11-02 11:38:42 +08:00
|
|
|
#include "xfs_error.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
#include "xfs_attr.h"
|
2005-11-02 11:38:42 +08:00
|
|
|
#include "xfs_bmap.h"
|
2013-08-12 18:49:42 +08:00
|
|
|
#include "xfs_bmap_util.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
#include "xfs_fsops.h"
|
2011-01-07 21:02:04 +08:00
|
|
|
#include "xfs_discard.h"
|
2008-07-18 15:13:20 +08:00
|
|
|
#include "xfs_quota.h"
|
2009-01-19 09:02:57 +08:00
|
|
|
#include "xfs_export.h"
|
2009-12-15 07:14:59 +08:00
|
|
|
#include "xfs_trace.h"
|
2012-11-08 01:21:12 +08:00
|
|
|
#include "xfs_icache.h"
|
2013-10-23 07:51:50 +08:00
|
|
|
#include "xfs_trans.h"
|
2015-11-03 09:56:17 +08:00
|
|
|
#include "xfs_acl.h"
|
2017-03-29 05:56:37 +08:00
|
|
|
#include "xfs_btree.h"
|
|
|
|
#include <linux/fsmap.h>
|
|
|
|
#include "xfs_fsmap.h"
|
2017-10-18 12:37:34 +08:00
|
|
|
#include "scrub/xfs_scrub.h"
|
2018-01-09 02:51:27 +08:00
|
|
|
#include "xfs_sb.h"
|
2019-04-12 22:41:17 +08:00
|
|
|
#include "xfs_ag.h"
|
2019-04-12 22:41:17 +08:00
|
|
|
#include "xfs_health.h"
|
2019-10-25 13:26:27 +08:00
|
|
|
#include "xfs_reflink.h"
|
2019-11-07 09:19:33 +08:00
|
|
|
#include "xfs_ioctl.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <linux/mount.h>
|
|
|
|
#include <linux/namei.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* xfs_find_handle maps from userspace xfs_fsop_handlereq structure to
|
|
|
|
* a file or fs 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
|
|
|
|
*/
|
2008-11-26 11:20:08 +08:00
|
|
|
int
|
2005-04-17 06:20:36 +08:00
|
|
|
xfs_find_handle(
|
|
|
|
unsigned int cmd,
|
2008-11-26 11:20:06 +08:00
|
|
|
xfs_fsop_handlereq_t *hreq)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int hsize;
|
|
|
|
xfs_handle_t handle;
|
|
|
|
struct inode *inode;
|
2013-09-02 18:49:36 +08:00
|
|
|
struct fd f = {NULL};
|
2009-02-09 04:51:14 +08:00
|
|
|
struct path path;
|
2012-08-29 00:52:22 +08:00
|
|
|
int error;
|
2009-02-09 04:51:14 +08:00
|
|
|
struct xfs_inode *ip;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-02-09 04:51:14 +08:00
|
|
|
if (cmd == XFS_IOC_FD_TO_HANDLE) {
|
2012-08-29 00:52:22 +08:00
|
|
|
f = fdget(hreq->fd);
|
|
|
|
if (!f.file)
|
2009-02-09 04:51:14 +08:00
|
|
|
return -EBADF;
|
2013-01-24 06:07:38 +08:00
|
|
|
inode = file_inode(f.file);
|
2009-02-09 04:51:14 +08:00
|
|
|
} else {
|
2019-07-15 04:42:44 +08:00
|
|
|
error = user_path_at(AT_FDCWD, hreq->path, 0, &path);
|
2009-02-09 04:51:14 +08:00
|
|
|
if (error)
|
|
|
|
return error;
|
2015-03-18 06:25:59 +08:00
|
|
|
inode = d_inode(path.dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2009-02-09 04:51:14 +08:00
|
|
|
ip = XFS_I(inode);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can only generate handles for inodes residing on a XFS filesystem,
|
|
|
|
* and only for regular files, directories or symbolic links.
|
|
|
|
*/
|
|
|
|
error = -EINVAL;
|
|
|
|
if (inode->i_sb->s_magic != XFS_SB_MAGIC)
|
|
|
|
goto out_put;
|
|
|
|
|
|
|
|
error = -EBADF;
|
|
|
|
if (!S_ISREG(inode->i_mode) &&
|
|
|
|
!S_ISDIR(inode->i_mode) &&
|
|
|
|
!S_ISLNK(inode->i_mode))
|
|
|
|
goto out_put;
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(&handle.ha_fsid, ip->i_mount->m_fixedfsid, sizeof(xfs_fsid_t));
|
|
|
|
|
|
|
|
if (cmd == XFS_IOC_PATH_TO_FSHANDLE) {
|
|
|
|
/*
|
|
|
|
* This handle only contains an fsid, zero the rest.
|
|
|
|
*/
|
|
|
|
memset(&handle.ha_fid, 0, sizeof(handle.ha_fid));
|
|
|
|
hsize = sizeof(xfs_fsid_t);
|
|
|
|
} else {
|
2007-09-14 13:22:37 +08:00
|
|
|
handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
|
|
|
|
sizeof(handle.ha_fid.fid_len);
|
|
|
|
handle.ha_fid.fid_pad = 0;
|
2016-02-09 13:54:58 +08:00
|
|
|
handle.ha_fid.fid_gen = inode->i_generation;
|
2007-09-14 13:22:37 +08:00
|
|
|
handle.ha_fid.fid_ino = ip->i_ino;
|
2017-06-15 12:30:44 +08:00
|
|
|
hsize = sizeof(xfs_handle_t);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-02-09 04:51:14 +08:00
|
|
|
error = -EFAULT;
|
2008-11-26 11:20:06 +08:00
|
|
|
if (copy_to_user(hreq->ohandle, &handle, hsize) ||
|
2009-02-09 04:51:14 +08:00
|
|
|
copy_to_user(hreq->ohandlen, &hsize, sizeof(__s32)))
|
|
|
|
goto out_put;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-02-09 04:51:14 +08:00
|
|
|
error = 0;
|
|
|
|
|
|
|
|
out_put:
|
|
|
|
if (cmd == XFS_IOC_FD_TO_HANDLE)
|
2012-08-29 00:52:22 +08:00
|
|
|
fdput(f);
|
2009-02-09 04:51:14 +08:00
|
|
|
else
|
|
|
|
path_put(&path);
|
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-01-19 09:02:57 +08:00
|
|
|
* No need to do permission checks on the various pathname components
|
|
|
|
* as the handle operations are privileged.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
STATIC int
|
2009-01-19 09:02:57 +08:00
|
|
|
xfs_handle_acceptable(
|
|
|
|
void *context,
|
|
|
|
struct dentry *dentry)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert userspace handle data into a dentry.
|
|
|
|
*/
|
|
|
|
struct dentry *
|
|
|
|
xfs_handle_to_dentry(
|
|
|
|
struct file *parfilp,
|
|
|
|
void __user *uhandle,
|
|
|
|
u32 hlen)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
xfs_handle_t handle;
|
2009-01-19 09:02:57 +08:00
|
|
|
struct xfs_fid64 fid;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Only allow handle opens under a directory.
|
|
|
|
*/
|
2013-01-24 06:07:38 +08:00
|
|
|
if (!S_ISDIR(file_inode(parfilp)->i_mode))
|
2009-01-19 09:02:57 +08:00
|
|
|
return ERR_PTR(-ENOTDIR);
|
|
|
|
|
|
|
|
if (hlen != sizeof(xfs_handle_t))
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
if (copy_from_user(&handle, uhandle, hlen))
|
|
|
|
return ERR_PTR(-EFAULT);
|
|
|
|
if (handle.ha_fid.fid_len !=
|
|
|
|
sizeof(handle.ha_fid) - sizeof(handle.ha_fid.fid_len))
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
|
|
memset(&fid, 0, sizeof(struct fid));
|
|
|
|
fid.ino = handle.ha_fid.fid_ino;
|
|
|
|
fid.gen = handle.ha_fid.fid_gen;
|
|
|
|
|
|
|
|
return exportfs_decode_fh(parfilp->f_path.mnt, (struct fid *)&fid, 3,
|
|
|
|
FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG,
|
|
|
|
xfs_handle_acceptable, NULL);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-01-19 09:02:57 +08:00
|
|
|
STATIC struct dentry *
|
|
|
|
xfs_handlereq_to_dentry(
|
|
|
|
struct file *parfilp,
|
|
|
|
xfs_fsop_handlereq_t *hreq)
|
|
|
|
{
|
|
|
|
return xfs_handle_to_dentry(parfilp, hreq->ihandle, hreq->ihandlen);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-11-26 11:20:08 +08:00
|
|
|
int
|
2005-04-17 06:20:36 +08:00
|
|
|
xfs_open_by_handle(
|
|
|
|
struct file *parfilp,
|
2009-01-19 09:02:57 +08:00
|
|
|
xfs_fsop_handlereq_t *hreq)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-11-14 07:39:22 +08:00
|
|
|
const struct cred *cred = current_cred();
|
2005-04-17 06:20:36 +08:00
|
|
|
int error;
|
2009-01-19 09:02:57 +08:00
|
|
|
int fd;
|
2005-04-17 06:20:36 +08:00
|
|
|
int permflag;
|
|
|
|
struct file *filp;
|
|
|
|
struct inode *inode;
|
|
|
|
struct dentry *dentry;
|
2012-03-22 13:15:06 +08:00
|
|
|
fmode_t fmode;
|
2012-06-27 01:58:53 +08:00
|
|
|
struct path path;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EPERM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-01-19 09:02:57 +08:00
|
|
|
dentry = xfs_handlereq_to_dentry(parfilp, hreq);
|
|
|
|
if (IS_ERR(dentry))
|
|
|
|
return PTR_ERR(dentry);
|
2015-03-18 06:25:59 +08:00
|
|
|
inode = d_inode(dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Restrict xfs_open_by_handle to directories & regular files. */
|
|
|
|
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) {
|
2014-06-22 13:04:54 +08:00
|
|
|
error = -EPERM;
|
2009-01-19 09:02:57 +08:00
|
|
|
goto out_dput;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#if BITS_PER_LONG != 32
|
2008-11-26 11:20:06 +08:00
|
|
|
hreq->oflags |= O_LARGEFILE;
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
2009-01-19 09:02:57 +08:00
|
|
|
|
2008-11-26 11:20:06 +08:00
|
|
|
permflag = hreq->oflags;
|
2012-03-22 13:15:06 +08:00
|
|
|
fmode = OPEN_FMODE(permflag);
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((!(permflag & O_APPEND) || (permflag & O_TRUNC)) &&
|
2012-03-22 13:15:06 +08:00
|
|
|
(fmode & FMODE_WRITE) && IS_APPEND(inode)) {
|
2014-06-22 13:04:54 +08:00
|
|
|
error = -EPERM;
|
2009-01-19 09:02:57 +08:00
|
|
|
goto out_dput;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-03-22 13:15:06 +08:00
|
|
|
if ((fmode & FMODE_WRITE) && IS_IMMUTABLE(inode)) {
|
2016-08-02 19:58:28 +08:00
|
|
|
error = -EPERM;
|
2009-01-19 09:02:57 +08:00
|
|
|
goto out_dput;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Can't write directories. */
|
2012-03-22 13:15:06 +08:00
|
|
|
if (S_ISDIR(inode->i_mode) && (fmode & FMODE_WRITE)) {
|
2014-06-22 13:04:54 +08:00
|
|
|
error = -EISDIR;
|
2009-01-19 09:02:57 +08:00
|
|
|
goto out_dput;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-07-03 00:39:34 +08:00
|
|
|
fd = get_unused_fd_flags(0);
|
2009-01-19 09:02:57 +08:00
|
|
|
if (fd < 0) {
|
|
|
|
error = fd;
|
|
|
|
goto out_dput;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-06-27 01:58:53 +08:00
|
|
|
path.mnt = parfilp->f_path.mnt;
|
|
|
|
path.dentry = dentry;
|
|
|
|
filp = dentry_open(&path, hreq->oflags, cred);
|
|
|
|
dput(dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (IS_ERR(filp)) {
|
2009-01-19 09:02:57 +08:00
|
|
|
put_unused_fd(fd);
|
|
|
|
return PTR_ERR(filp);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-12-09 17:47:33 +08:00
|
|
|
|
2011-07-26 08:54:24 +08:00
|
|
|
if (S_ISREG(inode->i_mode)) {
|
2006-11-11 15:04:47 +08:00
|
|
|
filp->f_flags |= O_NOATIME;
|
2008-12-09 17:47:33 +08:00
|
|
|
filp->f_mode |= FMODE_NOCMTIME;
|
2006-11-11 15:04:47 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-01-19 09:02:57 +08:00
|
|
|
fd_install(fd, filp);
|
|
|
|
return fd;
|
|
|
|
|
|
|
|
out_dput:
|
|
|
|
dput(dentry);
|
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-11-26 11:20:08 +08:00
|
|
|
int
|
2005-04-17 06:20:36 +08:00
|
|
|
xfs_readlink_by_handle(
|
2009-01-19 09:02:57 +08:00
|
|
|
struct file *parfilp,
|
|
|
|
xfs_fsop_handlereq_t *hreq)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-01-19 09:02:57 +08:00
|
|
|
struct dentry *dentry;
|
2005-04-17 06:20:36 +08:00
|
|
|
__u32 olen;
|
2007-08-28 11:59:03 +08:00
|
|
|
int error;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EPERM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-01-19 09:02:57 +08:00
|
|
|
dentry = xfs_handlereq_to_dentry(parfilp, hreq);
|
|
|
|
if (IS_ERR(dentry))
|
|
|
|
return PTR_ERR(dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Restrict this handle operation to symlinks only. */
|
2016-12-09 23:45:04 +08:00
|
|
|
if (!d_is_symlink(dentry)) {
|
2014-06-22 13:04:54 +08:00
|
|
|
error = -EINVAL;
|
2009-01-19 09:02:57 +08:00
|
|
|
goto out_dput;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-11-26 11:20:06 +08:00
|
|
|
if (copy_from_user(&olen, hreq->ohandlen, sizeof(__u32))) {
|
2014-06-22 13:04:54 +08:00
|
|
|
error = -EFAULT;
|
2009-01-19 09:02:57 +08:00
|
|
|
goto out_dput;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2016-12-09 23:45:04 +08:00
|
|
|
error = vfs_readlink(dentry, hreq->ohandle, olen);
|
2006-06-09 15:00:52 +08:00
|
|
|
|
2009-01-19 09:02:57 +08:00
|
|
|
out_dput:
|
|
|
|
dput(dentry);
|
2007-08-28 11:59:03 +08:00
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
STATIC int
|
|
|
|
xfs_attrlist_by_handle(
|
2009-01-19 09:02:57 +08:00
|
|
|
struct file *parfilp,
|
|
|
|
void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-01-19 09:02:57 +08:00
|
|
|
int error = -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
attrlist_cursor_kern_t *cursor;
|
2016-08-03 08:58:53 +08:00
|
|
|
struct xfs_fsop_attrlist_handlereq __user *p = arg;
|
2005-04-17 06:20:36 +08:00
|
|
|
xfs_fsop_attrlist_handlereq_t al_hreq;
|
2009-01-19 09:02:57 +08:00
|
|
|
struct dentry *dentry;
|
2005-04-17 06:20:36 +08:00
|
|
|
char *kbuf;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EPERM;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (copy_from_user(&al_hreq, arg, sizeof(xfs_fsop_attrlist_handlereq_t)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2013-11-01 02:00:10 +08:00
|
|
|
if (al_hreq.buflen < sizeof(struct attrlist) ||
|
2015-10-12 13:02:56 +08:00
|
|
|
al_hreq.buflen > XFS_XATTR_LIST_MAX)
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-06-27 11:32:19 +08:00
|
|
|
/*
|
|
|
|
* Reject flags, only allow namespaces.
|
|
|
|
*/
|
|
|
|
if (al_hreq.flags & ~(ATTR_ROOT | ATTR_SECURE))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EINVAL;
|
2008-06-27 11:32:19 +08:00
|
|
|
|
2009-01-19 09:02:57 +08:00
|
|
|
dentry = xfs_handlereq_to_dentry(parfilp, &al_hreq.hreq);
|
|
|
|
if (IS_ERR(dentry))
|
|
|
|
return PTR_ERR(dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-08-27 03:06:22 +08:00
|
|
|
kbuf = kmem_zalloc_large(al_hreq.buflen, 0);
|
2013-09-02 18:53:00 +08:00
|
|
|
if (!kbuf)
|
|
|
|
goto out_dput;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
cursor = (attrlist_cursor_kern_t *)&al_hreq.pos;
|
2015-03-18 06:25:59 +08:00
|
|
|
error = xfs_attr_list(XFS_I(d_inode(dentry)), kbuf, al_hreq.buflen,
|
2007-08-29 08:58:01 +08:00
|
|
|
al_hreq.flags, cursor);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (error)
|
|
|
|
goto out_kfree;
|
|
|
|
|
2016-08-03 08:58:53 +08:00
|
|
|
if (copy_to_user(&p->pos, cursor, sizeof(attrlist_cursor_kern_t))) {
|
|
|
|
error = -EFAULT;
|
|
|
|
goto out_kfree;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (copy_to_user(al_hreq.buffer, kbuf, al_hreq.buflen))
|
|
|
|
error = -EFAULT;
|
|
|
|
|
2013-09-02 18:53:00 +08:00
|
|
|
out_kfree:
|
|
|
|
kmem_free(kbuf);
|
|
|
|
out_dput:
|
2009-01-19 09:02:57 +08:00
|
|
|
dput(dentry);
|
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-11-26 11:20:15 +08:00
|
|
|
int
|
2005-04-17 06:20:36 +08:00
|
|
|
xfs_attrmulti_attr_get(
|
2007-08-29 08:58:01 +08:00
|
|
|
struct inode *inode,
|
2010-01-20 07:47:48 +08:00
|
|
|
unsigned char *name,
|
|
|
|
unsigned char __user *ubuf,
|
2017-06-17 02:00:05 +08:00
|
|
|
uint32_t *len,
|
|
|
|
uint32_t flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-01-20 07:47:48 +08:00
|
|
|
unsigned char *kbuf;
|
2014-06-25 12:58:08 +08:00
|
|
|
int error = -EFAULT;
|
2008-04-22 15:34:31 +08:00
|
|
|
|
2015-10-12 13:03:59 +08:00
|
|
|
if (*len > XFS_XATTR_SIZE_MAX)
|
2014-06-25 12:58:08 +08:00
|
|
|
return -EINVAL;
|
2019-08-27 03:06:22 +08:00
|
|
|
kbuf = kmem_zalloc_large(*len, 0);
|
2013-09-02 18:53:00 +08:00
|
|
|
if (!kbuf)
|
2014-06-25 12:58:08 +08:00
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
xfs: allocate xattr buffer on demand
When doing file lookups and checking for permissions, we end up in
xfs_get_acl() to see if there are any ACLs on the inode. This
requires and xattr lookup, and to do that we have to supply a buffer
large enough to hold an maximum sized xattr.
On workloads were we are accessing a wide range of cache cold files
under memory pressure (e.g. NFS fileservers) we end up spending a
lot of time allocating the buffer. The buffer is 64k in length, so
is a contiguous multi-page allocation, and if that then fails we
fall back to vmalloc(). Hence the allocation here is /expensive/
when we are looking up hundreds of thousands of files a second.
Initial numbers from a bpf trace show average time in xfs_get_acl()
is ~32us, with ~19us of that in the memory allocation. Note these
are average times, so there are going to be affected by the worst
case allocations more than the common fast case...
To avoid this, we could just do a "null" lookup to see if the ACL
xattr exists and then only do the allocation if it exists. This,
however, optimises the path for the "no ACL present" case at the
expense of the "acl present" case. i.e. we can halve the time in
xfs_get_acl() for the no acl case (i.e down to ~10-15us), but that
then increases the ACL case by 30% (i.e. up to 40-45us).
To solve this and speed up both cases, drive the xattr buffer
allocation into the attribute code once we know what the actual
xattr length is. For the no-xattr case, we avoid the allocation
completely, speeding up that case. For the common ACL case, we'll
end up with a fast heap allocation (because it'll be smaller than a
page), and only for the rarer "we have a remote xattr" will we have
a multi-page allocation occur. Hence the common ACL case will be
much faster, too.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
2019-08-30 00:04:10 +08:00
|
|
|
error = xfs_attr_get(XFS_I(inode), name, &kbuf, (int *)len, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (error)
|
|
|
|
goto out_kfree;
|
|
|
|
|
|
|
|
if (copy_to_user(ubuf, kbuf, *len))
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-09-02 18:53:00 +08:00
|
|
|
out_kfree:
|
|
|
|
kmem_free(kbuf);
|
2005-04-17 06:20:36 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2008-11-26 11:20:15 +08:00
|
|
|
int
|
2005-04-17 06:20:36 +08:00
|
|
|
xfs_attrmulti_attr_set(
|
2007-08-29 08:58:01 +08:00
|
|
|
struct inode *inode,
|
2010-01-20 07:47:48 +08:00
|
|
|
unsigned char *name,
|
|
|
|
const unsigned char __user *ubuf,
|
2017-06-17 02:00:05 +08:00
|
|
|
uint32_t len,
|
|
|
|
uint32_t flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-01-20 07:47:48 +08:00
|
|
|
unsigned char *kbuf;
|
2015-11-03 09:53:54 +08:00
|
|
|
int error;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-08-29 08:58:01 +08:00
|
|
|
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
|
2014-06-25 12:58:08 +08:00
|
|
|
return -EPERM;
|
2015-10-12 13:03:59 +08:00
|
|
|
if (len > XFS_XATTR_SIZE_MAX)
|
2014-06-25 12:58:08 +08:00
|
|
|
return -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-04-08 15:08:04 +08:00
|
|
|
kbuf = memdup_user(ubuf, len);
|
|
|
|
if (IS_ERR(kbuf))
|
|
|
|
return PTR_ERR(kbuf);
|
2008-04-22 15:34:31 +08:00
|
|
|
|
2015-11-03 09:53:54 +08:00
|
|
|
error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
|
2015-11-03 09:56:17 +08:00
|
|
|
if (!error)
|
|
|
|
xfs_forget_acl(inode, name, flags);
|
2015-11-03 09:53:54 +08:00
|
|
|
kfree(kbuf);
|
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-11-26 11:20:15 +08:00
|
|
|
int
|
2005-04-17 06:20:36 +08:00
|
|
|
xfs_attrmulti_attr_remove(
|
2007-08-29 08:58:01 +08:00
|
|
|
struct inode *inode,
|
2010-01-20 07:47:48 +08:00
|
|
|
unsigned char *name,
|
2017-06-17 02:00:05 +08:00
|
|
|
uint32_t flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-11-03 09:56:17 +08:00
|
|
|
int error;
|
|
|
|
|
2007-08-29 08:58:01 +08:00
|
|
|
if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
|
2014-06-25 12:58:08 +08:00
|
|
|
return -EPERM;
|
2015-11-03 09:56:17 +08:00
|
|
|
error = xfs_attr_remove(XFS_I(inode), name, flags);
|
|
|
|
if (!error)
|
|
|
|
xfs_forget_acl(inode, name, flags);
|
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
STATIC int
|
|
|
|
xfs_attrmulti_by_handle(
|
2008-02-16 06:37:46 +08:00
|
|
|
struct file *parfilp,
|
2009-01-19 09:02:57 +08:00
|
|
|
void __user *arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int error;
|
|
|
|
xfs_attr_multiop_t *ops;
|
|
|
|
xfs_fsop_attrmulti_handlereq_t am_hreq;
|
2009-01-19 09:02:57 +08:00
|
|
|
struct dentry *dentry;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int i, size;
|
2010-01-20 07:47:48 +08:00
|
|
|
unsigned char *attr_name;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EPERM;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (copy_from_user(&am_hreq, arg, sizeof(xfs_fsop_attrmulti_handlereq_t)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-03-23 06:51:22 +08:00
|
|
|
/* overflow check */
|
|
|
|
if (am_hreq.opcount >= INT_MAX / sizeof(xfs_attr_multiop_t))
|
|
|
|
return -E2BIG;
|
|
|
|
|
2009-01-19 09:02:57 +08:00
|
|
|
dentry = xfs_handlereq_to_dentry(parfilp, &am_hreq.hreq);
|
|
|
|
if (IS_ERR(dentry))
|
|
|
|
return PTR_ERR(dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -E2BIG;
|
2008-06-27 11:32:31 +08:00
|
|
|
size = am_hreq.opcount * sizeof(xfs_attr_multiop_t);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!size || size > 16 * PAGE_SIZE)
|
2009-01-19 09:02:57 +08:00
|
|
|
goto out_dput;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-04-08 15:08:04 +08:00
|
|
|
ops = memdup_user(am_hreq.ops, size);
|
|
|
|
if (IS_ERR(ops)) {
|
2014-06-25 12:58:08 +08:00
|
|
|
error = PTR_ERR(ops);
|
2009-01-19 09:02:57 +08:00
|
|
|
goto out_dput;
|
2009-04-08 15:08:04 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL);
|
|
|
|
if (!attr_name)
|
|
|
|
goto out_kfree_ops;
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
for (i = 0; i < am_hreq.opcount; i++) {
|
2010-01-20 07:47:48 +08:00
|
|
|
ops[i].am_error = strncpy_from_user((char *)attr_name,
|
2005-04-17 06:20:36 +08:00
|
|
|
ops[i].am_attrname, MAXNAMELEN);
|
|
|
|
if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN)
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -ERANGE;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ops[i].am_error < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (ops[i].am_opcode) {
|
|
|
|
case ATTR_OP_GET:
|
2009-01-19 09:02:57 +08:00
|
|
|
ops[i].am_error = xfs_attrmulti_attr_get(
|
2015-03-18 06:25:59 +08:00
|
|
|
d_inode(dentry), attr_name,
|
2009-01-19 09:02:57 +08:00
|
|
|
ops[i].am_attrvalue, &ops[i].am_length,
|
|
|
|
ops[i].am_flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case ATTR_OP_SET:
|
2011-11-24 00:57:51 +08:00
|
|
|
ops[i].am_error = mnt_want_write_file(parfilp);
|
2008-02-16 06:37:46 +08:00
|
|
|
if (ops[i].am_error)
|
|
|
|
break;
|
2009-01-19 09:02:57 +08:00
|
|
|
ops[i].am_error = xfs_attrmulti_attr_set(
|
2015-03-18 06:25:59 +08:00
|
|
|
d_inode(dentry), attr_name,
|
2009-01-19 09:02:57 +08:00
|
|
|
ops[i].am_attrvalue, ops[i].am_length,
|
|
|
|
ops[i].am_flags);
|
2011-12-09 21:06:57 +08:00
|
|
|
mnt_drop_write_file(parfilp);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
case ATTR_OP_REMOVE:
|
2011-11-24 00:57:51 +08:00
|
|
|
ops[i].am_error = mnt_want_write_file(parfilp);
|
2008-02-16 06:37:46 +08:00
|
|
|
if (ops[i].am_error)
|
|
|
|
break;
|
2009-01-19 09:02:57 +08:00
|
|
|
ops[i].am_error = xfs_attrmulti_attr_remove(
|
2015-03-18 06:25:59 +08:00
|
|
|
d_inode(dentry), attr_name,
|
2009-01-19 09:02:57 +08:00
|
|
|
ops[i].am_flags);
|
2011-12-09 21:06:57 +08:00
|
|
|
mnt_drop_write_file(parfilp);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
default:
|
2014-06-25 12:58:08 +08:00
|
|
|
ops[i].am_error = -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (copy_to_user(am_hreq.ops, ops, size))
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
kfree(attr_name);
|
|
|
|
out_kfree_ops:
|
|
|
|
kfree(ops);
|
2009-01-19 09:02:57 +08:00
|
|
|
out_dput:
|
|
|
|
dput(dentry);
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-11-26 11:20:08 +08:00
|
|
|
int
|
2005-04-17 06:20:36 +08:00
|
|
|
xfs_ioc_space(
|
|
|
|
struct file *filp,
|
2008-11-26 11:20:06 +08:00
|
|
|
xfs_flock64_t *bf)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2016-07-20 09:29:35 +08:00
|
|
|
struct inode *inode = file_inode(filp);
|
|
|
|
struct xfs_inode *ip = XFS_I(inode);
|
2013-10-12 15:55:08 +08:00
|
|
|
struct iattr iattr;
|
2019-10-25 13:26:02 +08:00
|
|
|
enum xfs_prealloc_flags flags = XFS_PREALLOC_CLEAR;
|
2018-03-13 05:12:29 +08:00
|
|
|
uint iolock = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
|
2005-04-17 06:20:36 +08:00
|
|
|
int error;
|
|
|
|
|
2006-09-28 08:52:04 +08:00
|
|
|
if (inode->i_flags & (S_IMMUTABLE|S_APPEND))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EPERM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-09-02 14:41:16 +08:00
|
|
|
if (!(filp->f_mode & FMODE_WRITE))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EBADF;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-09-28 08:52:04 +08:00
|
|
|
if (!S_ISREG(inode->i_mode))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-10-25 13:26:27 +08:00
|
|
|
if (xfs_is_always_cow_inode(ip))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2015-02-02 06:53:56 +08:00
|
|
|
if (filp->f_flags & O_DSYNC)
|
|
|
|
flags |= XFS_PREALLOC_SYNC;
|
2016-07-20 09:29:35 +08:00
|
|
|
if (filp->f_mode & FMODE_NOCMTIME)
|
2015-02-02 06:53:56 +08:00
|
|
|
flags |= XFS_PREALLOC_INVISIBLE;
|
|
|
|
|
2012-06-12 22:20:39 +08:00
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2013-10-12 15:55:08 +08:00
|
|
|
|
2015-02-16 08:59:50 +08:00
|
|
|
xfs_ilock(ip, iolock);
|
2018-03-21 05:42:38 +08:00
|
|
|
error = xfs_break_layouts(inode, &iolock, BREAK_UNMAP);
|
2015-02-16 08:59:50 +08:00
|
|
|
if (error)
|
|
|
|
goto out_unlock;
|
xfs: properly serialise fallocate against AIO+DIO
AIO+DIO can extend the file size on IO completion, and it holds
no inode locks while the IO is in flight. Therefore, a race
condition exists in file size updates if we do something like this:
aio-thread fallocate-thread
lock inode
submit IO beyond inode->i_size
unlock inode
.....
lock inode
break layouts
if (off + len > inode->i_size)
new_size = off + len
.....
inode_dio_wait()
<blocks>
.....
completes
inode->i_size updated
inode_dio_done()
....
<wakes>
<does stuff no long beyond EOF>
if (new_size)
xfs_vn_setattr(inode, new_size)
Yup, that attempt to extend the file size in the fallocate code
turns into a truncate - it removes the whatever the aio write
allocated and put to disk, and reduced the inode size back down to
where the fallocate operation ends.
Fundamentally, xfs_file_fallocate() not compatible with racing
AIO+DIO completions, so we need to move the inode_dio_wait() call
up to where the lock the inode and break the layouts.
Secondly, storing the inode size and then using it unchecked without
holding the ILOCK is not safe; we can only do such a thing if we've
locked out and drained all IO and other modification operations,
which we don't do initially in xfs_file_fallocate.
It should be noted that some of the fallocate operations are
compound operations - they are made up of multiple manipulations
that may zero data, and so we may need to flush and invalidate the
file multiple times during an operation. However, we only need to
lock out IO and other space manipulation operations once, as that
lockout is maintained until the entire fallocate operation has been
completed.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
2019-10-30 04:04:32 +08:00
|
|
|
inode_dio_wait(inode);
|
2013-10-12 15:55:08 +08:00
|
|
|
|
|
|
|
switch (bf->l_whence) {
|
|
|
|
case 0: /*SEEK_SET*/
|
|
|
|
break;
|
|
|
|
case 1: /*SEEK_CUR*/
|
|
|
|
bf->l_start += filp->f_pos;
|
|
|
|
break;
|
|
|
|
case 2: /*SEEK_END*/
|
|
|
|
bf->l_start += XFS_ISIZE(ip);
|
|
|
|
break;
|
|
|
|
default:
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -EINVAL;
|
2013-10-12 15:55:08 +08:00
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2019-10-25 13:26:02 +08:00
|
|
|
if (bf->l_start < 0 || bf->l_start > inode->i_sb->s_maxbytes) {
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -EINVAL;
|
2013-10-12 15:55:08 +08:00
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2019-10-25 13:26:02 +08:00
|
|
|
if (bf->l_start > XFS_ISIZE(ip)) {
|
|
|
|
error = xfs_alloc_file_space(ip, XFS_ISIZE(ip),
|
|
|
|
bf->l_start - XFS_ISIZE(ip), 0);
|
|
|
|
if (error)
|
|
|
|
goto out_unlock;
|
2013-10-12 15:55:08 +08:00
|
|
|
}
|
|
|
|
|
2019-10-25 13:26:02 +08:00
|
|
|
iattr.ia_valid = ATTR_SIZE;
|
|
|
|
iattr.ia_size = bf->l_start;
|
|
|
|
error = xfs_vn_setattr_size(file_dentry(filp), &iattr);
|
2013-10-12 15:55:08 +08:00
|
|
|
if (error)
|
|
|
|
goto out_unlock;
|
|
|
|
|
2015-02-02 06:53:56 +08:00
|
|
|
error = xfs_update_prealloc_flags(ip, flags);
|
2013-10-12 15:55:08 +08:00
|
|
|
|
|
|
|
out_unlock:
|
2015-02-16 08:59:50 +08:00
|
|
|
xfs_iunlock(ip, iolock);
|
2012-06-12 22:20:39 +08:00
|
|
|
mnt_drop_write_file(filp);
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2019-07-03 00:39:40 +08:00
|
|
|
/* Return 0 on success or positive error */
|
|
|
|
int
|
2019-07-04 11:36:26 +08:00
|
|
|
xfs_fsbulkstat_one_fmt(
|
2019-07-04 11:36:26 +08:00
|
|
|
struct xfs_ibulk *breq,
|
|
|
|
const struct xfs_bulkstat *bstat)
|
2019-07-03 00:39:40 +08:00
|
|
|
{
|
2019-07-04 11:36:26 +08:00
|
|
|
struct xfs_bstat bs1;
|
|
|
|
|
|
|
|
xfs_bulkstat_to_bstat(breq->mp, &bs1, bstat);
|
|
|
|
if (copy_to_user(breq->ubuffer, &bs1, sizeof(bs1)))
|
2019-07-03 00:39:40 +08:00
|
|
|
return -EFAULT;
|
|
|
|
return xfs_ibulk_advance(breq, sizeof(struct xfs_bstat));
|
|
|
|
}
|
|
|
|
|
2019-07-03 00:39:43 +08:00
|
|
|
int
|
2019-07-04 11:36:26 +08:00
|
|
|
xfs_fsinumbers_fmt(
|
2019-07-04 11:36:27 +08:00
|
|
|
struct xfs_ibulk *breq,
|
|
|
|
const struct xfs_inumbers *igrp)
|
2019-07-03 00:39:43 +08:00
|
|
|
{
|
2019-07-04 11:36:27 +08:00
|
|
|
struct xfs_inogrp ig1;
|
|
|
|
|
|
|
|
xfs_inumbers_to_inogrp(&ig1, igrp);
|
|
|
|
if (copy_to_user(breq->ubuffer, &ig1, sizeof(struct xfs_inogrp)))
|
2019-07-03 00:39:43 +08:00
|
|
|
return -EFAULT;
|
|
|
|
return xfs_ibulk_advance(breq, sizeof(struct xfs_inogrp));
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
STATIC int
|
2019-07-04 11:36:26 +08:00
|
|
|
xfs_ioc_fsbulkstat(
|
2005-04-17 06:20:36 +08:00
|
|
|
xfs_mount_t *mp,
|
|
|
|
unsigned int cmd,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
2019-07-03 00:39:40 +08:00
|
|
|
struct xfs_fsop_bulkreq bulkreq;
|
|
|
|
struct xfs_ibulk breq = {
|
|
|
|
.mp = mp,
|
|
|
|
.ocount = 0,
|
|
|
|
};
|
|
|
|
xfs_ino_t lastino;
|
2005-04-17 06:20:36 +08:00
|
|
|
int error;
|
|
|
|
|
|
|
|
/* done = 1 if there are more stats to get and if bulkstat */
|
|
|
|
/* should be called again (unused here, but used in dmapi) */
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (XFS_FORCED_SHUTDOWN(mp))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EIO;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-07-04 11:36:25 +08:00
|
|
|
if (copy_from_user(&bulkreq, arg, sizeof(struct xfs_fsop_bulkreq)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-07-03 00:39:40 +08:00
|
|
|
if (copy_from_user(&lastino, bulkreq.lastip, sizeof(__s64)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-07-03 00:39:40 +08:00
|
|
|
if (bulkreq.icount <= 0)
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-11-23 13:30:32 +08:00
|
|
|
if (bulkreq.ubuffer == NULL)
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EINVAL;
|
2007-11-23 13:30:32 +08:00
|
|
|
|
2019-07-03 00:39:40 +08:00
|
|
|
breq.ubuffer = bulkreq.ubuffer;
|
|
|
|
breq.icount = bulkreq.icount;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FSBULKSTAT_SINGLE expects that *lastip contains the inode number
|
|
|
|
* that we want to stat. However, FSINUMBERS and FSBULKSTAT expect
|
|
|
|
* that *lastip contains either zero or the number of the last inode to
|
|
|
|
* be examined by the previous call and return results starting with
|
|
|
|
* the next inode after that. The new bulk request back end functions
|
|
|
|
* take the inode to start with, so we have to compute the startino
|
|
|
|
* parameter from lastino to maintain correct function. lastino == 0
|
|
|
|
* is a special case because it has traditionally meant "first inode
|
|
|
|
* in filesystem".
|
|
|
|
*/
|
|
|
|
if (cmd == XFS_IOC_FSINUMBERS) {
|
2019-07-03 00:39:43 +08:00
|
|
|
breq.startino = lastino ? lastino + 1 : 0;
|
2019-07-04 11:36:26 +08:00
|
|
|
error = xfs_inumbers(&breq, xfs_fsinumbers_fmt);
|
2019-07-03 00:39:43 +08:00
|
|
|
lastino = breq.startino - 1;
|
2019-07-03 00:39:40 +08:00
|
|
|
} else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE) {
|
|
|
|
breq.startino = lastino;
|
|
|
|
breq.icount = 1;
|
2019-07-04 11:36:26 +08:00
|
|
|
error = xfs_bulkstat_one(&breq, xfs_fsbulkstat_one_fmt);
|
2019-07-03 00:39:40 +08:00
|
|
|
} else { /* XFS_IOC_FSBULKSTAT */
|
|
|
|
breq.startino = lastino ? lastino + 1 : 0;
|
2019-07-04 11:36:26 +08:00
|
|
|
error = xfs_bulkstat(&breq, xfs_fsbulkstat_one_fmt);
|
2019-07-03 00:39:40 +08:00
|
|
|
lastino = breq.startino - 1;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (error)
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-07-03 00:39:39 +08:00
|
|
|
if (bulkreq.lastip != NULL &&
|
2019-07-03 00:39:40 +08:00
|
|
|
copy_to_user(bulkreq.lastip, &lastino, sizeof(xfs_ino_t)))
|
2019-07-03 00:39:39 +08:00
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-07-03 00:39:39 +08:00
|
|
|
if (bulkreq.ocount != NULL &&
|
2019-07-03 00:39:40 +08:00
|
|
|
copy_to_user(bulkreq.ocount, &breq.ocount, sizeof(__s32)))
|
2019-07-03 00:39:39 +08:00
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-04 11:36:27 +08:00
|
|
|
/* Return 0 on success or positive error */
|
|
|
|
static int
|
|
|
|
xfs_bulkstat_fmt(
|
|
|
|
struct xfs_ibulk *breq,
|
|
|
|
const struct xfs_bulkstat *bstat)
|
|
|
|
{
|
|
|
|
if (copy_to_user(breq->ubuffer, bstat, sizeof(struct xfs_bulkstat)))
|
|
|
|
return -EFAULT;
|
|
|
|
return xfs_ibulk_advance(breq, sizeof(struct xfs_bulkstat));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the incoming bulk request @hdr from userspace and initialize the
|
|
|
|
* internal @breq bulk request appropriately. Returns 0 if the bulk request
|
2019-08-29 05:37:57 +08:00
|
|
|
* should proceed; -ECANCELED if there's nothing to do; or the usual
|
2019-07-04 11:36:27 +08:00
|
|
|
* negative error code.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
xfs_bulk_ireq_setup(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_bulk_ireq *hdr,
|
|
|
|
struct xfs_ibulk *breq,
|
|
|
|
void __user *ubuffer)
|
|
|
|
{
|
|
|
|
if (hdr->icount == 0 ||
|
|
|
|
(hdr->flags & ~XFS_BULK_IREQ_FLAGS_ALL) ||
|
|
|
|
memchr_inv(hdr->reserved, 0, sizeof(hdr->reserved)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
breq->startino = hdr->ino;
|
|
|
|
breq->ubuffer = ubuffer;
|
|
|
|
breq->icount = hdr->icount;
|
|
|
|
breq->ocount = 0;
|
2019-07-04 11:36:28 +08:00
|
|
|
breq->flags = 0;
|
|
|
|
|
2019-07-04 11:36:29 +08:00
|
|
|
/*
|
|
|
|
* The @ino parameter is a special value, so we must look it up here.
|
|
|
|
* We're not allowed to have IREQ_AGNO, and we only return one inode
|
|
|
|
* worth of data.
|
|
|
|
*/
|
|
|
|
if (hdr->flags & XFS_BULK_IREQ_SPECIAL) {
|
|
|
|
if (hdr->flags & XFS_BULK_IREQ_AGNO)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
switch (hdr->ino) {
|
|
|
|
case XFS_BULK_IREQ_SPECIAL_ROOT:
|
|
|
|
hdr->ino = mp->m_sb.sb_rootino;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
breq->icount = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2019-07-04 11:36:28 +08:00
|
|
|
/*
|
|
|
|
* The IREQ_AGNO flag means that we only want results from a given AG.
|
|
|
|
* If @hdr->ino is zero, we start iterating in that AG. If @hdr->ino is
|
|
|
|
* beyond the specified AG then we return no results.
|
|
|
|
*/
|
|
|
|
if (hdr->flags & XFS_BULK_IREQ_AGNO) {
|
|
|
|
if (hdr->agno >= mp->m_sb.sb_agcount)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (breq->startino == 0)
|
|
|
|
breq->startino = XFS_AGINO_TO_INO(mp, hdr->agno, 0);
|
|
|
|
else if (XFS_INO_TO_AGNO(mp, breq->startino) < hdr->agno)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
breq->flags |= XFS_IBULK_SAME_AG;
|
|
|
|
|
|
|
|
/* Asking for an inode past the end of the AG? We're done! */
|
|
|
|
if (XFS_INO_TO_AGNO(mp, breq->startino) > hdr->agno)
|
2019-08-29 05:37:57 +08:00
|
|
|
return -ECANCELED;
|
2019-07-04 11:36:28 +08:00
|
|
|
} else if (hdr->agno)
|
|
|
|
return -EINVAL;
|
2019-07-04 11:36:27 +08:00
|
|
|
|
|
|
|
/* Asking for an inode past the end of the FS? We're done! */
|
|
|
|
if (XFS_INO_TO_AGNO(mp, breq->startino) >= mp->m_sb.sb_agcount)
|
2019-08-29 05:37:57 +08:00
|
|
|
return -ECANCELED;
|
2019-07-04 11:36:27 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Update the userspace bulk request @hdr to reflect the end state of the
|
|
|
|
* internal bulk request @breq.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xfs_bulk_ireq_teardown(
|
|
|
|
struct xfs_bulk_ireq *hdr,
|
|
|
|
struct xfs_ibulk *breq)
|
|
|
|
{
|
|
|
|
hdr->ino = breq->startino;
|
|
|
|
hdr->ocount = breq->ocount;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle the v5 bulkstat ioctl. */
|
|
|
|
STATIC int
|
|
|
|
xfs_ioc_bulkstat(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
unsigned int cmd,
|
|
|
|
struct xfs_bulkstat_req __user *arg)
|
|
|
|
{
|
|
|
|
struct xfs_bulk_ireq hdr;
|
|
|
|
struct xfs_ibulk breq = {
|
|
|
|
.mp = mp,
|
|
|
|
};
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (XFS_FORCED_SHUTDOWN(mp))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (copy_from_user(&hdr, &arg->hdr, sizeof(hdr)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
error = xfs_bulk_ireq_setup(mp, &hdr, &breq, arg->bulkstat);
|
2019-08-29 05:37:57 +08:00
|
|
|
if (error == -ECANCELED)
|
2019-07-04 11:36:27 +08:00
|
|
|
goto out_teardown;
|
|
|
|
if (error < 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
error = xfs_bulkstat(&breq, xfs_bulkstat_fmt);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
out_teardown:
|
|
|
|
xfs_bulk_ireq_teardown(&hdr, &breq);
|
|
|
|
if (copy_to_user(&arg->hdr, &hdr, sizeof(hdr)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-04 11:36:28 +08:00
|
|
|
STATIC int
|
|
|
|
xfs_inumbers_fmt(
|
|
|
|
struct xfs_ibulk *breq,
|
|
|
|
const struct xfs_inumbers *igrp)
|
|
|
|
{
|
|
|
|
if (copy_to_user(breq->ubuffer, igrp, sizeof(struct xfs_inumbers)))
|
|
|
|
return -EFAULT;
|
|
|
|
return xfs_ibulk_advance(breq, sizeof(struct xfs_inumbers));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle the v5 inumbers ioctl. */
|
|
|
|
STATIC int
|
|
|
|
xfs_ioc_inumbers(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
unsigned int cmd,
|
|
|
|
struct xfs_inumbers_req __user *arg)
|
|
|
|
{
|
|
|
|
struct xfs_bulk_ireq hdr;
|
|
|
|
struct xfs_ibulk breq = {
|
|
|
|
.mp = mp,
|
|
|
|
};
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (XFS_FORCED_SHUTDOWN(mp))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (copy_from_user(&hdr, &arg->hdr, sizeof(hdr)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
error = xfs_bulk_ireq_setup(mp, &hdr, &breq, arg->inumbers);
|
2019-08-29 05:37:57 +08:00
|
|
|
if (error == -ECANCELED)
|
2019-07-04 11:36:28 +08:00
|
|
|
goto out_teardown;
|
|
|
|
if (error < 0)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
error = xfs_inumbers(&breq, xfs_inumbers_fmt);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
out_teardown:
|
|
|
|
xfs_bulk_ireq_teardown(&hdr, &breq);
|
|
|
|
if (copy_to_user(&arg->hdr, &hdr, sizeof(hdr)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC int
|
|
|
|
xfs_ioc_fsgeometry(
|
2019-04-12 22:41:16 +08:00
|
|
|
struct xfs_mount *mp,
|
|
|
|
void __user *arg,
|
|
|
|
int struct_version)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2019-04-12 22:41:16 +08:00
|
|
|
struct xfs_fsop_geom fsgeo;
|
|
|
|
size_t len;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-05-02 11:26:30 +08:00
|
|
|
xfs_fs_geometry(&mp->m_sb, &fsgeo, struct_version);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2019-04-12 22:41:16 +08:00
|
|
|
if (struct_version <= 3)
|
|
|
|
len = sizeof(struct xfs_fsop_geom_v1);
|
|
|
|
else if (struct_version == 4)
|
|
|
|
len = sizeof(struct xfs_fsop_geom_v4);
|
2019-04-12 22:41:17 +08:00
|
|
|
else {
|
|
|
|
xfs_fsop_geom_health(mp, &fsgeo);
|
2019-04-12 22:41:16 +08:00
|
|
|
len = sizeof(fsgeo);
|
2019-04-12 22:41:17 +08:00
|
|
|
}
|
2019-04-12 22:41:16 +08:00
|
|
|
|
|
|
|
if (copy_to_user(arg, &fsgeo, len))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-12 22:41:17 +08:00
|
|
|
STATIC int
|
|
|
|
xfs_ioc_ag_geometry(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
|
|
|
struct xfs_ag_geometry ageo;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (copy_from_user(&ageo, arg, sizeof(ageo)))
|
|
|
|
return -EFAULT;
|
2019-08-31 07:30:22 +08:00
|
|
|
if (ageo.ag_flags)
|
|
|
|
return -EINVAL;
|
|
|
|
if (memchr_inv(&ageo.ag_reserved, 0, sizeof(ageo.ag_reserved)))
|
|
|
|
return -EINVAL;
|
2019-04-12 22:41:17 +08:00
|
|
|
|
|
|
|
error = xfs_ag_get_geometry(mp, ageo.ag_number, &ageo);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &ageo, sizeof(ageo)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Linux extended inode flags interface.
|
|
|
|
*/
|
|
|
|
|
|
|
|
STATIC unsigned int
|
|
|
|
xfs_merge_ioc_xflags(
|
|
|
|
unsigned int flags,
|
|
|
|
unsigned int start)
|
|
|
|
{
|
|
|
|
unsigned int xflags = start;
|
|
|
|
|
2007-02-10 15:37:10 +08:00
|
|
|
if (flags & FS_IMMUTABLE_FL)
|
2016-01-04 13:44:15 +08:00
|
|
|
xflags |= FS_XFLAG_IMMUTABLE;
|
2005-04-17 06:20:36 +08:00
|
|
|
else
|
2016-01-04 13:44:15 +08:00
|
|
|
xflags &= ~FS_XFLAG_IMMUTABLE;
|
2007-02-10 15:37:10 +08:00
|
|
|
if (flags & FS_APPEND_FL)
|
2016-01-04 13:44:15 +08:00
|
|
|
xflags |= FS_XFLAG_APPEND;
|
2005-04-17 06:20:36 +08:00
|
|
|
else
|
2016-01-04 13:44:15 +08:00
|
|
|
xflags &= ~FS_XFLAG_APPEND;
|
2007-02-10 15:37:10 +08:00
|
|
|
if (flags & FS_SYNC_FL)
|
2016-01-04 13:44:15 +08:00
|
|
|
xflags |= FS_XFLAG_SYNC;
|
2005-04-17 06:20:36 +08:00
|
|
|
else
|
2016-01-04 13:44:15 +08:00
|
|
|
xflags &= ~FS_XFLAG_SYNC;
|
2007-02-10 15:37:10 +08:00
|
|
|
if (flags & FS_NOATIME_FL)
|
2016-01-04 13:44:15 +08:00
|
|
|
xflags |= FS_XFLAG_NOATIME;
|
2005-04-17 06:20:36 +08:00
|
|
|
else
|
2016-01-04 13:44:15 +08:00
|
|
|
xflags &= ~FS_XFLAG_NOATIME;
|
2007-02-10 15:37:10 +08:00
|
|
|
if (flags & FS_NODUMP_FL)
|
2016-01-04 13:44:15 +08:00
|
|
|
xflags |= FS_XFLAG_NODUMP;
|
2005-04-17 06:20:36 +08:00
|
|
|
else
|
2016-01-04 13:44:15 +08:00
|
|
|
xflags &= ~FS_XFLAG_NODUMP;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return xflags;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC unsigned int
|
|
|
|
xfs_di2lxflags(
|
2017-06-17 02:00:05 +08:00
|
|
|
uint16_t di_flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned int flags = 0;
|
|
|
|
|
|
|
|
if (di_flags & XFS_DIFLAG_IMMUTABLE)
|
2007-02-10 15:37:10 +08:00
|
|
|
flags |= FS_IMMUTABLE_FL;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (di_flags & XFS_DIFLAG_APPEND)
|
2007-02-10 15:37:10 +08:00
|
|
|
flags |= FS_APPEND_FL;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (di_flags & XFS_DIFLAG_SYNC)
|
2007-02-10 15:37:10 +08:00
|
|
|
flags |= FS_SYNC_FL;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (di_flags & XFS_DIFLAG_NOATIME)
|
2007-02-10 15:37:10 +08:00
|
|
|
flags |= FS_NOATIME_FL;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (di_flags & XFS_DIFLAG_NODUMP)
|
2007-02-10 15:37:10 +08:00
|
|
|
flags |= FS_NODUMP_FL;
|
2005-04-17 06:20:36 +08:00
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2019-07-01 23:25:35 +08:00
|
|
|
static void
|
|
|
|
xfs_fill_fsxattr(
|
|
|
|
struct xfs_inode *ip,
|
|
|
|
bool attr,
|
|
|
|
struct fsxattr *fa)
|
2007-10-11 15:47:00 +08:00
|
|
|
{
|
2019-07-01 23:25:35 +08:00
|
|
|
simple_fill_fsxattr(fa, xfs_ip2xflags(ip));
|
|
|
|
fa->fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog;
|
|
|
|
fa->fsx_cowextsize = ip->i_d.di_cowextsize <<
|
2016-10-04 00:11:43 +08:00
|
|
|
ip->i_mount->m_sb.sb_blocklog;
|
2019-11-13 00:22:54 +08:00
|
|
|
fa->fsx_projid = ip->i_d.di_projid;
|
2007-10-11 15:47:00 +08:00
|
|
|
|
|
|
|
if (attr) {
|
|
|
|
if (ip->i_afp) {
|
|
|
|
if (ip->i_afp->if_flags & XFS_IFEXTENTS)
|
2019-07-01 23:25:35 +08:00
|
|
|
fa->fsx_nextents = xfs_iext_count(ip->i_afp);
|
2007-10-11 15:47:00 +08:00
|
|
|
else
|
2019-07-01 23:25:35 +08:00
|
|
|
fa->fsx_nextents = ip->i_d.di_anextents;
|
2007-10-11 15:47:00 +08:00
|
|
|
} else
|
2019-07-01 23:25:35 +08:00
|
|
|
fa->fsx_nextents = 0;
|
2007-10-11 15:47:00 +08:00
|
|
|
} else {
|
|
|
|
if (ip->i_df.if_flags & XFS_IFEXTENTS)
|
2019-07-01 23:25:35 +08:00
|
|
|
fa->fsx_nextents = xfs_iext_count(&ip->i_df);
|
2007-10-11 15:47:00 +08:00
|
|
|
else
|
2019-07-01 23:25:35 +08:00
|
|
|
fa->fsx_nextents = ip->i_d.di_nextents;
|
2007-10-11 15:47:00 +08:00
|
|
|
}
|
2019-07-01 23:25:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
STATIC int
|
|
|
|
xfs_ioc_fsgetxattr(
|
|
|
|
xfs_inode_t *ip,
|
|
|
|
int attr,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
|
|
|
struct fsxattr fa;
|
|
|
|
|
|
|
|
xfs_ilock(ip, XFS_ILOCK_SHARED);
|
|
|
|
xfs_fill_fsxattr(ip, attr, &fa);
|
2007-10-11 15:47:00 +08:00
|
|
|
xfs_iunlock(ip, XFS_ILOCK_SHARED);
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &fa, sizeof(fa)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-02 23:21:20 +08:00
|
|
|
STATIC uint16_t
|
|
|
|
xfs_flags2diflags(
|
2008-07-18 15:13:20 +08:00
|
|
|
struct xfs_inode *ip,
|
|
|
|
unsigned int xflags)
|
|
|
|
{
|
|
|
|
/* can't set PREALLOC this way, just preserve it */
|
2017-09-02 23:21:20 +08:00
|
|
|
uint16_t di_flags =
|
|
|
|
(ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
|
|
|
|
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_IMMUTABLE)
|
2008-07-18 15:13:20 +08:00
|
|
|
di_flags |= XFS_DIFLAG_IMMUTABLE;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_APPEND)
|
2008-07-18 15:13:20 +08:00
|
|
|
di_flags |= XFS_DIFLAG_APPEND;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_SYNC)
|
2008-07-18 15:13:20 +08:00
|
|
|
di_flags |= XFS_DIFLAG_SYNC;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_NOATIME)
|
2008-07-18 15:13:20 +08:00
|
|
|
di_flags |= XFS_DIFLAG_NOATIME;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_NODUMP)
|
2008-07-18 15:13:20 +08:00
|
|
|
di_flags |= XFS_DIFLAG_NODUMP;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_NODEFRAG)
|
2008-07-18 15:13:20 +08:00
|
|
|
di_flags |= XFS_DIFLAG_NODEFRAG;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_FILESTREAM)
|
2008-07-18 15:13:20 +08:00
|
|
|
di_flags |= XFS_DIFLAG_FILESTREAM;
|
2016-02-09 13:54:58 +08:00
|
|
|
if (S_ISDIR(VFS_I(ip)->i_mode)) {
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_RTINHERIT)
|
2008-07-18 15:13:20 +08:00
|
|
|
di_flags |= XFS_DIFLAG_RTINHERIT;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_NOSYMLINKS)
|
2008-07-18 15:13:20 +08:00
|
|
|
di_flags |= XFS_DIFLAG_NOSYMLINKS;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_EXTSZINHERIT)
|
2008-07-18 15:13:20 +08:00
|
|
|
di_flags |= XFS_DIFLAG_EXTSZINHERIT;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_PROJINHERIT)
|
2014-10-02 07:18:40 +08:00
|
|
|
di_flags |= XFS_DIFLAG_PROJINHERIT;
|
2016-02-09 13:54:58 +08:00
|
|
|
} else if (S_ISREG(VFS_I(ip)->i_mode)) {
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_REALTIME)
|
2008-07-18 15:13:20 +08:00
|
|
|
di_flags |= XFS_DIFLAG_REALTIME;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_EXTSIZE)
|
2008-07-18 15:13:20 +08:00
|
|
|
di_flags |= XFS_DIFLAG_EXTSIZE;
|
|
|
|
}
|
2016-01-04 13:44:15 +08:00
|
|
|
|
2017-09-02 23:21:20 +08:00
|
|
|
return di_flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC uint64_t
|
|
|
|
xfs_flags2diflags2(
|
|
|
|
struct xfs_inode *ip,
|
|
|
|
unsigned int xflags)
|
|
|
|
{
|
|
|
|
uint64_t di_flags2 =
|
|
|
|
(ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
|
2016-01-04 13:44:15 +08:00
|
|
|
|
|
|
|
if (xflags & FS_XFLAG_DAX)
|
|
|
|
di_flags2 |= XFS_DIFLAG2_DAX;
|
2016-10-04 00:11:43 +08:00
|
|
|
if (xflags & FS_XFLAG_COWEXTSIZE)
|
|
|
|
di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
|
2016-01-04 13:44:15 +08:00
|
|
|
|
2017-09-02 23:21:20 +08:00
|
|
|
return di_flags2;
|
2008-07-18 15:13:20 +08:00
|
|
|
}
|
|
|
|
|
2008-07-21 14:16:15 +08:00
|
|
|
STATIC void
|
|
|
|
xfs_diflags_to_linux(
|
|
|
|
struct xfs_inode *ip)
|
|
|
|
{
|
2008-08-13 14:00:45 +08:00
|
|
|
struct inode *inode = VFS_I(ip);
|
2008-07-21 14:16:15 +08:00
|
|
|
unsigned int xflags = xfs_ip2xflags(ip);
|
|
|
|
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_IMMUTABLE)
|
2008-07-21 14:16:15 +08:00
|
|
|
inode->i_flags |= S_IMMUTABLE;
|
|
|
|
else
|
|
|
|
inode->i_flags &= ~S_IMMUTABLE;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_APPEND)
|
2008-07-21 14:16:15 +08:00
|
|
|
inode->i_flags |= S_APPEND;
|
|
|
|
else
|
|
|
|
inode->i_flags &= ~S_APPEND;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_SYNC)
|
2008-07-21 14:16:15 +08:00
|
|
|
inode->i_flags |= S_SYNC;
|
|
|
|
else
|
|
|
|
inode->i_flags &= ~S_SYNC;
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_NOATIME)
|
2008-07-21 14:16:15 +08:00
|
|
|
inode->i_flags |= S_NOATIME;
|
|
|
|
else
|
|
|
|
inode->i_flags &= ~S_NOATIME;
|
2017-08-31 00:23:01 +08:00
|
|
|
#if 0 /* disabled until the flag switching races are sorted out */
|
2016-01-04 13:44:15 +08:00
|
|
|
if (xflags & FS_XFLAG_DAX)
|
|
|
|
inode->i_flags |= S_DAX;
|
|
|
|
else
|
|
|
|
inode->i_flags &= ~S_DAX;
|
2017-08-31 00:23:01 +08:00
|
|
|
#endif
|
2008-07-21 14:16:15 +08:00
|
|
|
}
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2015-02-02 07:14:25 +08:00
|
|
|
static int
|
|
|
|
xfs_ioctl_setattr_xflags(
|
|
|
|
struct xfs_trans *tp,
|
|
|
|
struct xfs_inode *ip,
|
|
|
|
struct fsxattr *fa)
|
|
|
|
{
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
2017-09-02 23:21:20 +08:00
|
|
|
uint64_t di_flags2;
|
2015-02-02 07:14:25 +08:00
|
|
|
|
|
|
|
/* Can't change realtime flag if any extents are allocated. */
|
|
|
|
if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
|
2016-01-04 13:44:15 +08:00
|
|
|
XFS_IS_REALTIME_INODE(ip) != (fa->fsx_xflags & FS_XFLAG_REALTIME))
|
2015-02-02 07:14:25 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* If realtime flag is set then must have realtime device */
|
2016-01-04 13:44:15 +08:00
|
|
|
if (fa->fsx_xflags & FS_XFLAG_REALTIME) {
|
2015-02-02 07:14:25 +08:00
|
|
|
if (mp->m_sb.sb_rblocks == 0 || mp->m_sb.sb_rextsize == 0 ||
|
|
|
|
(ip->i_d.di_extsize % mp->m_sb.sb_rextsize))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-10-10 13:49:29 +08:00
|
|
|
/* Clear reflink if we are actually able to set the rt flag. */
|
2016-10-04 00:11:50 +08:00
|
|
|
if ((fa->fsx_xflags & FS_XFLAG_REALTIME) && xfs_is_reflink_inode(ip))
|
2016-10-10 13:49:29 +08:00
|
|
|
ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
|
2016-10-04 00:11:50 +08:00
|
|
|
|
2016-10-04 00:11:50 +08:00
|
|
|
/* Don't allow us to set DAX mode for a reflinked file for now. */
|
|
|
|
if ((fa->fsx_xflags & FS_XFLAG_DAX) && xfs_is_reflink_inode(ip))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2017-09-02 23:21:20 +08:00
|
|
|
/* diflags2 only valid for v3 inodes. */
|
|
|
|
di_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
|
|
|
|
if (di_flags2 && ip->i_d.di_version < 3)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ip->i_d.di_flags = xfs_flags2diflags(ip, fa->fsx_xflags);
|
|
|
|
ip->i_d.di_flags2 = di_flags2;
|
|
|
|
|
2015-02-02 07:14:25 +08:00
|
|
|
xfs_diflags_to_linux(ip);
|
|
|
|
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
|
|
|
|
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
|
2015-10-12 15:21:22 +08:00
|
|
|
XFS_STATS_INC(mp, xs_ig_attrchg);
|
2015-02-02 07:14:25 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-03-01 06:41:33 +08:00
|
|
|
/*
|
|
|
|
* If we are changing DAX flags, we have to ensure the file is clean and any
|
|
|
|
* cached objects in the address space are invalidated and removed. This
|
|
|
|
* requires us to lock out other IO and page faults similar to a truncate
|
|
|
|
* operation. The locks need to be held until the transaction has been committed
|
|
|
|
* so that the cache invalidation is atomic with respect to the DAX flag
|
|
|
|
* manipulation.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
xfs_ioctl_setattr_dax_invalidate(
|
|
|
|
struct xfs_inode *ip,
|
|
|
|
struct fsxattr *fa,
|
|
|
|
int *join_flags)
|
|
|
|
{
|
|
|
|
struct inode *inode = VFS_I(ip);
|
2017-09-19 05:46:03 +08:00
|
|
|
struct super_block *sb = inode->i_sb;
|
2016-03-01 06:41:33 +08:00
|
|
|
int error;
|
|
|
|
|
|
|
|
*join_flags = 0;
|
|
|
|
|
2016-03-01 06:41:33 +08:00
|
|
|
/*
|
|
|
|
* It is only valid to set the DAX flag on regular files and
|
2016-03-01 06:41:33 +08:00
|
|
|
* directories on filesystems where the block size is equal to the page
|
2018-06-01 06:07:47 +08:00
|
|
|
* size. On directories it serves as an inherited hint so we don't
|
|
|
|
* have to check the device for dax support or flush pagecache.
|
2016-03-01 06:41:33 +08:00
|
|
|
*/
|
2016-03-01 06:41:33 +08:00
|
|
|
if (fa->fsx_xflags & FS_XFLAG_DAX) {
|
2019-10-25 13:25:38 +08:00
|
|
|
struct xfs_buftarg *target = xfs_inode_buftarg(ip);
|
|
|
|
|
|
|
|
if (!bdev_dax_supported(target->bt_bdev, sb->s_blocksize))
|
2016-03-01 06:41:33 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2016-03-01 06:41:33 +08:00
|
|
|
|
2016-03-01 06:41:33 +08:00
|
|
|
/* If the DAX state is not changing, we have nothing to do here. */
|
|
|
|
if ((fa->fsx_xflags & FS_XFLAG_DAX) && IS_DAX(inode))
|
|
|
|
return 0;
|
|
|
|
if (!(fa->fsx_xflags & FS_XFLAG_DAX) && !IS_DAX(inode))
|
|
|
|
return 0;
|
|
|
|
|
2018-06-01 06:07:47 +08:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
return 0;
|
|
|
|
|
2016-03-01 06:41:33 +08:00
|
|
|
/* lock, flush and invalidate mapping in preparation for flag change */
|
|
|
|
xfs_ilock(ip, XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL);
|
|
|
|
error = filemap_write_and_wait(inode->i_mapping);
|
|
|
|
if (error)
|
|
|
|
goto out_unlock;
|
|
|
|
error = invalidate_inode_pages2(inode->i_mapping);
|
|
|
|
if (error)
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
*join_flags = XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL;
|
2015-02-02 07:14:25 +08:00
|
|
|
return 0;
|
2016-03-01 06:41:33 +08:00
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
xfs_iunlock(ip, XFS_MMAPLOCK_EXCL | XFS_IOLOCK_EXCL);
|
|
|
|
return error;
|
|
|
|
|
2015-02-02 07:14:25 +08:00
|
|
|
}
|
|
|
|
|
2015-02-02 07:15:35 +08:00
|
|
|
/*
|
|
|
|
* Set up the transaction structure for the setattr operation, checking that we
|
|
|
|
* have permission to do so. On success, return a clean transaction and the
|
|
|
|
* inode locked exclusively ready for further operation specific checks. On
|
|
|
|
* failure, return an error without modifying or locking the inode.
|
2016-03-01 06:41:33 +08:00
|
|
|
*
|
|
|
|
* The inode might already be IO locked on call. If this is the case, it is
|
|
|
|
* indicated in @join_flags and we take full responsibility for ensuring they
|
|
|
|
* are unlocked from now on. Hence if we have an error here, we still have to
|
|
|
|
* unlock them. Otherwise, once they are joined to the transaction, they will
|
|
|
|
* be unlocked on commit/cancel.
|
2015-02-02 07:15:35 +08:00
|
|
|
*/
|
|
|
|
static struct xfs_trans *
|
|
|
|
xfs_ioctl_setattr_get_trans(
|
2016-03-01 06:41:33 +08:00
|
|
|
struct xfs_inode *ip,
|
|
|
|
int join_flags)
|
2015-02-02 07:15:35 +08:00
|
|
|
{
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
|
|
|
struct xfs_trans *tp;
|
2016-03-01 06:41:33 +08:00
|
|
|
int error = -EROFS;
|
2015-02-02 07:15:35 +08:00
|
|
|
|
|
|
|
if (mp->m_flags & XFS_MOUNT_RDONLY)
|
2016-03-01 06:41:33 +08:00
|
|
|
goto out_unlock;
|
|
|
|
error = -EIO;
|
2015-02-02 07:15:35 +08:00
|
|
|
if (XFS_FORCED_SHUTDOWN(mp))
|
2016-03-01 06:41:33 +08:00
|
|
|
goto out_unlock;
|
2015-02-02 07:15:35 +08:00
|
|
|
|
2016-04-06 07:19:55 +08:00
|
|
|
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
|
2015-02-02 07:15:35 +08:00
|
|
|
if (error)
|
2019-04-23 07:28:34 +08:00
|
|
|
goto out_unlock;
|
2015-02-02 07:15:35 +08:00
|
|
|
|
|
|
|
xfs_ilock(ip, XFS_ILOCK_EXCL);
|
2016-03-01 06:41:33 +08:00
|
|
|
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | join_flags);
|
|
|
|
join_flags = 0;
|
2015-02-02 07:15:35 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* CAP_FOWNER overrides the following restrictions:
|
|
|
|
*
|
|
|
|
* The user ID of the calling process must be equal to the file owner
|
|
|
|
* ID, except in cases where the CAP_FSETID capability is applicable.
|
|
|
|
*/
|
|
|
|
if (!inode_owner_or_capable(VFS_I(ip))) {
|
|
|
|
error = -EPERM;
|
|
|
|
goto out_cancel;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mp->m_flags & XFS_MOUNT_WSYNC)
|
|
|
|
xfs_trans_set_sync(tp);
|
|
|
|
|
|
|
|
return tp;
|
|
|
|
|
|
|
|
out_cancel:
|
2015-06-04 11:47:56 +08:00
|
|
|
xfs_trans_cancel(tp);
|
2016-03-01 06:41:33 +08:00
|
|
|
out_unlock:
|
|
|
|
if (join_flags)
|
|
|
|
xfs_iunlock(ip, join_flags);
|
2015-02-02 07:15:35 +08:00
|
|
|
return ERR_PTR(error);
|
|
|
|
}
|
|
|
|
|
2015-02-02 07:26:26 +08:00
|
|
|
/*
|
|
|
|
* extent size hint validation is somewhat cumbersome. Rules are:
|
|
|
|
*
|
|
|
|
* 1. extent size hint is only valid for directories and regular files
|
2016-01-04 13:44:15 +08:00
|
|
|
* 2. FS_XFLAG_EXTSIZE is only valid for regular files
|
|
|
|
* 3. FS_XFLAG_EXTSZINHERIT is only valid for directories.
|
2015-02-02 07:26:26 +08:00
|
|
|
* 4. can only be changed on regular files if no extents are allocated
|
|
|
|
* 5. can be changed on directories at any time
|
|
|
|
* 6. extsize hint of 0 turns off hints, clears inode flags.
|
|
|
|
* 7. Extent size must be a multiple of the appropriate block size.
|
|
|
|
* 8. for non-realtime files, the extent size hint must be limited
|
|
|
|
* to half the AG size to avoid alignment extending the extent beyond the
|
|
|
|
* limits of the AG.
|
2017-10-18 12:37:42 +08:00
|
|
|
*
|
|
|
|
* Please keep this function in sync with xfs_scrub_inode_extsize.
|
2015-02-02 07:26:26 +08:00
|
|
|
*/
|
2015-02-05 08:13:21 +08:00
|
|
|
static int
|
2015-02-02 07:22:20 +08:00
|
|
|
xfs_ioctl_setattr_check_extsize(
|
|
|
|
struct xfs_inode *ip,
|
|
|
|
struct fsxattr *fa)
|
|
|
|
{
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
2019-07-01 23:25:36 +08:00
|
|
|
xfs_extlen_t size;
|
|
|
|
xfs_fsblock_t extsize_fsb;
|
2015-02-02 07:26:26 +08:00
|
|
|
|
2016-02-09 13:54:58 +08:00
|
|
|
if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_d.di_nextents &&
|
2015-02-02 07:22:20 +08:00
|
|
|
((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-07-01 23:25:36 +08:00
|
|
|
if (fa->fsx_extsize == 0)
|
|
|
|
return 0;
|
2015-02-02 07:22:20 +08:00
|
|
|
|
2019-07-01 23:25:36 +08:00
|
|
|
extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize);
|
|
|
|
if (extsize_fsb > MAXEXTLEN)
|
|
|
|
return -EINVAL;
|
2015-02-02 07:22:20 +08:00
|
|
|
|
2019-07-01 23:25:36 +08:00
|
|
|
if (XFS_IS_REALTIME_INODE(ip) ||
|
|
|
|
(fa->fsx_xflags & FS_XFLAG_REALTIME)) {
|
|
|
|
size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog;
|
|
|
|
} else {
|
|
|
|
size = mp->m_sb.sb_blocksize;
|
|
|
|
if (extsize_fsb > mp->m_sb.sb_agblocks / 2)
|
2015-02-02 07:22:20 +08:00
|
|
|
return -EINVAL;
|
2019-07-01 23:25:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (fa->fsx_extsize % size)
|
|
|
|
return -EINVAL;
|
2015-02-02 07:26:26 +08:00
|
|
|
|
2015-02-02 07:22:20 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-10-04 00:11:43 +08:00
|
|
|
/*
|
|
|
|
* CoW extent size hint validation rules are:
|
|
|
|
*
|
|
|
|
* 1. CoW extent size hint can only be set if reflink is enabled on the fs.
|
|
|
|
* The inode does not have to have any shared blocks, but it must be a v3.
|
|
|
|
* 2. FS_XFLAG_COWEXTSIZE is only valid for directories and regular files;
|
|
|
|
* for a directory, the hint is propagated to new files.
|
|
|
|
* 3. Can be changed on files & directories at any time.
|
|
|
|
* 4. CoW extsize hint of 0 turns off hints, clears inode flags.
|
|
|
|
* 5. Extent size must be a multiple of the appropriate block size.
|
|
|
|
* 6. The extent size hint must be limited to half the AG size to avoid
|
|
|
|
* alignment extending the extent beyond the limits of the AG.
|
2017-10-18 12:37:42 +08:00
|
|
|
*
|
|
|
|
* Please keep this function in sync with xfs_scrub_inode_cowextsize.
|
2016-10-04 00:11:43 +08:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
xfs_ioctl_setattr_check_cowextsize(
|
|
|
|
struct xfs_inode *ip,
|
|
|
|
struct fsxattr *fa)
|
|
|
|
{
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
2019-07-01 23:25:36 +08:00
|
|
|
xfs_extlen_t size;
|
|
|
|
xfs_fsblock_t cowextsize_fsb;
|
2016-10-04 00:11:43 +08:00
|
|
|
|
|
|
|
if (!(fa->fsx_xflags & FS_XFLAG_COWEXTSIZE))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!xfs_sb_version_hasreflink(&ip->i_mount->m_sb) ||
|
|
|
|
ip->i_d.di_version != 3)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-07-01 23:25:36 +08:00
|
|
|
if (fa->fsx_cowextsize == 0)
|
|
|
|
return 0;
|
2016-10-04 00:11:43 +08:00
|
|
|
|
2019-07-01 23:25:36 +08:00
|
|
|
cowextsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_cowextsize);
|
|
|
|
if (cowextsize_fsb > MAXEXTLEN)
|
|
|
|
return -EINVAL;
|
2016-10-04 00:11:43 +08:00
|
|
|
|
2019-07-01 23:25:36 +08:00
|
|
|
size = mp->m_sb.sb_blocksize;
|
|
|
|
if (cowextsize_fsb > mp->m_sb.sb_agblocks / 2)
|
|
|
|
return -EINVAL;
|
2016-10-04 00:11:43 +08:00
|
|
|
|
2019-07-01 23:25:36 +08:00
|
|
|
if (fa->fsx_cowextsize % size)
|
|
|
|
return -EINVAL;
|
2016-10-04 00:11:43 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-05 08:13:21 +08:00
|
|
|
static int
|
2015-02-02 07:22:53 +08:00
|
|
|
xfs_ioctl_setattr_check_projid(
|
|
|
|
struct xfs_inode *ip,
|
|
|
|
struct fsxattr *fa)
|
|
|
|
{
|
|
|
|
/* Disallow 32bit project ids if projid32bit feature is not enabled. */
|
2017-06-17 02:00:05 +08:00
|
|
|
if (fa->fsx_projid > (uint16_t)-1 &&
|
2015-02-02 07:22:53 +08:00
|
|
|
!xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb))
|
|
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
|
|
}
|
2008-07-18 15:13:20 +08:00
|
|
|
|
|
|
|
STATIC int
|
|
|
|
xfs_ioctl_setattr(
|
|
|
|
xfs_inode_t *ip,
|
2015-02-02 07:16:25 +08:00
|
|
|
struct fsxattr *fa)
|
2008-07-18 15:13:20 +08:00
|
|
|
{
|
2019-07-01 23:25:35 +08:00
|
|
|
struct fsxattr old_fa;
|
2008-07-18 15:13:20 +08:00
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
|
|
|
struct xfs_trans *tp;
|
2009-06-08 21:33:32 +08:00
|
|
|
struct xfs_dquot *udqp = NULL;
|
2013-07-11 13:00:40 +08:00
|
|
|
struct xfs_dquot *pdqp = NULL;
|
2008-07-18 15:13:20 +08:00
|
|
|
struct xfs_dquot *olddquot = NULL;
|
|
|
|
int code;
|
2016-03-01 06:41:33 +08:00
|
|
|
int join_flags = 0;
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2010-06-24 09:57:09 +08:00
|
|
|
trace_xfs_ioctl_setattr(ip);
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2015-02-02 07:22:53 +08:00
|
|
|
code = xfs_ioctl_setattr_check_projid(ip, fa);
|
|
|
|
if (code)
|
|
|
|
return code;
|
2010-08-26 18:19:43 +08:00
|
|
|
|
2008-07-18 15:13:20 +08:00
|
|
|
/*
|
|
|
|
* If disk quotas is on, we make sure that the dquots do exist on disk,
|
|
|
|
* before we start any other transactions. Trying to do this later
|
|
|
|
* is messy. We don't care to take a readlock to look at the ids
|
|
|
|
* in inode here, because we can't hold it across the trans_reserve.
|
|
|
|
* If the IDs do change before we take the ilock, we're covered
|
|
|
|
* because the i_*dquot fields will get updated anyway.
|
|
|
|
*/
|
2015-02-02 07:16:25 +08:00
|
|
|
if (XFS_IS_QUOTA_ON(mp)) {
|
2009-06-08 21:33:32 +08:00
|
|
|
code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid,
|
2008-07-18 15:13:20 +08:00
|
|
|
ip->i_d.di_gid, fa->fsx_projid,
|
2013-07-11 13:00:40 +08:00
|
|
|
XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp);
|
2008-07-18 15:13:20 +08:00
|
|
|
if (code)
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
2016-03-01 06:41:33 +08:00
|
|
|
/*
|
|
|
|
* Changing DAX config may require inode locking for mapping
|
|
|
|
* invalidation. These need to be held all the way to transaction commit
|
|
|
|
* or cancel time, so need to be passed through to
|
|
|
|
* xfs_ioctl_setattr_get_trans() so it can apply them to the join call
|
|
|
|
* appropriately.
|
|
|
|
*/
|
|
|
|
code = xfs_ioctl_setattr_dax_invalidate(ip, fa, &join_flags);
|
|
|
|
if (code)
|
|
|
|
goto error_free_dquots;
|
|
|
|
|
|
|
|
tp = xfs_ioctl_setattr_get_trans(ip, join_flags);
|
2015-02-02 07:15:35 +08:00
|
|
|
if (IS_ERR(tp)) {
|
|
|
|
code = PTR_ERR(tp);
|
|
|
|
goto error_free_dquots;
|
2008-07-18 15:13:20 +08:00
|
|
|
}
|
|
|
|
|
2015-02-02 07:16:25 +08:00
|
|
|
if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) &&
|
2019-11-13 00:22:54 +08:00
|
|
|
ip->i_d.di_projid != fa->fsx_projid) {
|
2015-02-02 07:16:25 +08:00
|
|
|
code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp,
|
|
|
|
capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0);
|
|
|
|
if (code) /* out of quota */
|
2015-02-02 07:22:20 +08:00
|
|
|
goto error_trans_cancel;
|
2008-07-18 15:13:20 +08:00
|
|
|
}
|
|
|
|
|
2019-07-01 23:25:35 +08:00
|
|
|
xfs_fill_fsxattr(ip, false, &old_fa);
|
|
|
|
code = vfs_ioc_fssetxattr_check(VFS_I(ip), &old_fa, fa);
|
|
|
|
if (code)
|
|
|
|
goto error_trans_cancel;
|
|
|
|
|
2015-02-02 07:22:20 +08:00
|
|
|
code = xfs_ioctl_setattr_check_extsize(ip, fa);
|
|
|
|
if (code)
|
|
|
|
goto error_trans_cancel;
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2016-10-04 00:11:43 +08:00
|
|
|
code = xfs_ioctl_setattr_check_cowextsize(ip, fa);
|
|
|
|
if (code)
|
|
|
|
goto error_trans_cancel;
|
|
|
|
|
2015-02-02 07:14:25 +08:00
|
|
|
code = xfs_ioctl_setattr_xflags(tp, ip, fa);
|
|
|
|
if (code)
|
2015-02-02 07:22:20 +08:00
|
|
|
goto error_trans_cancel;
|
2008-07-18 15:13:20 +08:00
|
|
|
|
|
|
|
/*
|
2015-02-02 07:16:25 +08:00
|
|
|
* Change file ownership. Must be the owner or privileged. CAP_FSETID
|
|
|
|
* overrides the following restrictions:
|
|
|
|
*
|
|
|
|
* The set-user-ID and set-group-ID bits of a file will be cleared upon
|
|
|
|
* successful return from chown()
|
2008-07-18 15:13:20 +08:00
|
|
|
*/
|
|
|
|
|
2016-02-09 13:54:58 +08:00
|
|
|
if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) &&
|
2015-02-02 07:16:25 +08:00
|
|
|
!capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID))
|
2016-02-09 13:54:58 +08:00
|
|
|
VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID);
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2015-02-02 07:16:25 +08:00
|
|
|
/* Change the ownerships and register project quota modifications */
|
2019-11-13 00:22:54 +08:00
|
|
|
if (ip->i_d.di_projid != fa->fsx_projid) {
|
2015-02-02 07:16:25 +08:00
|
|
|
if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) {
|
|
|
|
olddquot = xfs_qm_vop_chown(tp, ip,
|
|
|
|
&ip->i_pdquot, pdqp);
|
|
|
|
}
|
|
|
|
ASSERT(ip->i_d.di_version > 1);
|
2019-11-13 00:22:54 +08:00
|
|
|
ip->i_d.di_projid = fa->fsx_projid;
|
2008-07-21 14:16:15 +08:00
|
|
|
}
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2014-10-02 07:20:30 +08:00
|
|
|
/*
|
|
|
|
* Only set the extent size hint if we've already determined that the
|
|
|
|
* extent size hint should be set on the inode. If no extent size flags
|
|
|
|
* are set on the inode then unconditionally clear the extent size hint.
|
|
|
|
*/
|
2015-02-02 07:16:25 +08:00
|
|
|
if (ip->i_d.di_flags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT))
|
|
|
|
ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
|
|
|
|
else
|
|
|
|
ip->i_d.di_extsize = 0;
|
2016-10-04 00:11:43 +08:00
|
|
|
if (ip->i_d.di_version == 3 &&
|
|
|
|
(ip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE))
|
|
|
|
ip->i_d.di_cowextsize = fa->fsx_cowextsize >>
|
|
|
|
mp->m_sb.sb_blocklog;
|
|
|
|
else
|
|
|
|
ip->i_d.di_cowextsize = 0;
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2015-06-04 11:48:08 +08:00
|
|
|
code = xfs_trans_commit(tp);
|
2008-07-18 15:13:20 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Release any dquot(s) the inode had kept before chown.
|
|
|
|
*/
|
2009-06-08 21:33:32 +08:00
|
|
|
xfs_qm_dqrele(olddquot);
|
|
|
|
xfs_qm_dqrele(udqp);
|
2013-07-11 13:00:40 +08:00
|
|
|
xfs_qm_dqrele(pdqp);
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2010-06-23 16:11:15 +08:00
|
|
|
return code;
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2015-02-02 07:22:20 +08:00
|
|
|
error_trans_cancel:
|
2015-06-04 11:47:56 +08:00
|
|
|
xfs_trans_cancel(tp);
|
2015-02-02 07:15:35 +08:00
|
|
|
error_free_dquots:
|
2009-06-08 21:33:32 +08:00
|
|
|
xfs_qm_dqrele(udqp);
|
2013-07-11 13:00:40 +08:00
|
|
|
xfs_qm_dqrele(pdqp);
|
2008-07-18 15:13:20 +08:00
|
|
|
return code;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
STATIC int
|
2008-04-18 09:44:03 +08:00
|
|
|
xfs_ioc_fssetxattr(
|
2005-04-17 06:20:36 +08:00
|
|
|
xfs_inode_t *ip,
|
|
|
|
struct file *filp,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
|
|
|
struct fsxattr fa;
|
2012-06-12 22:20:39 +08:00
|
|
|
int error;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
if (copy_from_user(&fa, arg, sizeof(fa)))
|
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-06-12 22:20:39 +08:00
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2015-02-02 07:16:25 +08:00
|
|
|
error = xfs_ioctl_setattr(ip, &fa);
|
2012-06-12 22:20:39 +08:00
|
|
|
mnt_drop_write_file(filp);
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2008-04-18 09:44:03 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-18 09:44:03 +08:00
|
|
|
STATIC int
|
|
|
|
xfs_ioc_getxflags(
|
|
|
|
xfs_inode_t *ip,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
|
|
|
unsigned int flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-18 09:44:03 +08:00
|
|
|
flags = xfs_di2lxflags(ip->i_d.di_flags);
|
|
|
|
if (copy_to_user(arg, &flags, sizeof(flags)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-18 09:44:03 +08:00
|
|
|
STATIC int
|
|
|
|
xfs_ioc_setxflags(
|
2015-02-02 07:15:56 +08:00
|
|
|
struct xfs_inode *ip,
|
2008-04-18 09:44:03 +08:00
|
|
|
struct file *filp,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
2015-02-02 07:15:56 +08:00
|
|
|
struct xfs_trans *tp;
|
2008-07-18 15:13:20 +08:00
|
|
|
struct fsxattr fa;
|
2019-07-01 23:25:35 +08:00
|
|
|
struct fsxattr old_fa;
|
2008-04-18 09:44:03 +08:00
|
|
|
unsigned int flags;
|
2016-03-01 06:41:33 +08:00
|
|
|
int join_flags = 0;
|
2015-02-02 07:15:56 +08:00
|
|
|
int error;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-18 09:44:03 +08:00
|
|
|
if (copy_from_user(&flags, arg, sizeof(flags)))
|
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-18 09:44:03 +08:00
|
|
|
if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
|
|
|
|
FS_NOATIME_FL | FS_NODUMP_FL | \
|
|
|
|
FS_SYNC_FL))
|
|
|
|
return -EOPNOTSUPP;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-07-18 15:13:20 +08:00
|
|
|
fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-06-12 22:20:39 +08:00
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2015-02-02 07:15:56 +08:00
|
|
|
|
2016-03-01 06:41:33 +08:00
|
|
|
/*
|
|
|
|
* Changing DAX config may require inode locking for mapping
|
|
|
|
* invalidation. These need to be held all the way to transaction commit
|
|
|
|
* or cancel time, so need to be passed through to
|
|
|
|
* xfs_ioctl_setattr_get_trans() so it can apply them to the join call
|
|
|
|
* appropriately.
|
|
|
|
*/
|
|
|
|
error = xfs_ioctl_setattr_dax_invalidate(ip, &fa, &join_flags);
|
|
|
|
if (error)
|
|
|
|
goto out_drop_write;
|
|
|
|
|
|
|
|
tp = xfs_ioctl_setattr_get_trans(ip, join_flags);
|
2015-02-02 07:15:56 +08:00
|
|
|
if (IS_ERR(tp)) {
|
|
|
|
error = PTR_ERR(tp);
|
|
|
|
goto out_drop_write;
|
|
|
|
}
|
|
|
|
|
2019-07-01 23:25:35 +08:00
|
|
|
xfs_fill_fsxattr(ip, false, &old_fa);
|
|
|
|
error = vfs_ioc_fssetxattr_check(VFS_I(ip), &old_fa, &fa);
|
|
|
|
if (error) {
|
|
|
|
xfs_trans_cancel(tp);
|
|
|
|
goto out_drop_write;
|
|
|
|
}
|
|
|
|
|
2015-02-02 07:15:56 +08:00
|
|
|
error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
|
|
|
|
if (error) {
|
2015-06-04 11:47:56 +08:00
|
|
|
xfs_trans_cancel(tp);
|
2015-02-02 07:15:56 +08:00
|
|
|
goto out_drop_write;
|
|
|
|
}
|
|
|
|
|
2015-06-04 11:48:08 +08:00
|
|
|
error = xfs_trans_commit(tp);
|
2015-02-02 07:15:56 +08:00
|
|
|
out_drop_write:
|
2012-06-12 22:20:39 +08:00
|
|
|
mnt_drop_write_file(filp);
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2017-10-18 05:16:19 +08:00
|
|
|
static bool
|
|
|
|
xfs_getbmap_format(
|
|
|
|
struct kgetbmap *p,
|
|
|
|
struct getbmapx __user *u,
|
|
|
|
size_t recsize)
|
2008-11-28 11:23:35 +08:00
|
|
|
{
|
2017-10-18 05:16:19 +08:00
|
|
|
if (put_user(p->bmv_offset, &u->bmv_offset) ||
|
|
|
|
put_user(p->bmv_block, &u->bmv_block) ||
|
|
|
|
put_user(p->bmv_length, &u->bmv_length) ||
|
|
|
|
put_user(0, &u->bmv_count) ||
|
|
|
|
put_user(0, &u->bmv_entries))
|
|
|
|
return false;
|
|
|
|
if (recsize < sizeof(struct getbmapx))
|
|
|
|
return true;
|
|
|
|
if (put_user(0, &u->bmv_iflags) ||
|
|
|
|
put_user(p->bmv_oflags, &u->bmv_oflags) ||
|
|
|
|
put_user(0, &u->bmv_unused1) ||
|
|
|
|
put_user(0, &u->bmv_unused2))
|
|
|
|
return false;
|
|
|
|
return true;
|
2008-11-28 11:23:35 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
STATIC int
|
|
|
|
xfs_ioc_getbmap(
|
2016-07-20 09:29:35 +08:00
|
|
|
struct file *file,
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int cmd,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
2017-04-04 06:17:57 +08:00
|
|
|
struct getbmapx bmx = { 0 };
|
2017-10-18 05:16:19 +08:00
|
|
|
struct kgetbmap *buf;
|
|
|
|
size_t recsize;
|
|
|
|
int error, i;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-10-18 05:16:19 +08:00
|
|
|
switch (cmd) {
|
|
|
|
case XFS_IOC_GETBMAPA:
|
|
|
|
bmx.bmv_iflags = BMV_IF_ATTRFORK;
|
|
|
|
/*FALLTHRU*/
|
|
|
|
case XFS_IOC_GETBMAP:
|
|
|
|
if (file->f_mode & FMODE_NOCMTIME)
|
|
|
|
bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
|
|
|
|
/* struct getbmap is a strict subset of struct getbmapx. */
|
|
|
|
recsize = sizeof(struct getbmap);
|
|
|
|
break;
|
|
|
|
case XFS_IOC_GETBMAPX:
|
|
|
|
recsize = sizeof(struct getbmapx);
|
|
|
|
break;
|
|
|
|
default:
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EINVAL;
|
2017-10-18 05:16:19 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-10-18 05:16:19 +08:00
|
|
|
if (copy_from_user(&bmx, arg, recsize))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (bmx.bmv_count < 2)
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EINVAL;
|
2017-10-18 05:16:19 +08:00
|
|
|
if (bmx.bmv_count > ULONG_MAX / recsize)
|
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-10-18 05:16:19 +08:00
|
|
|
buf = kmem_zalloc_large(bmx.bmv_count * sizeof(*buf), 0);
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-10-18 05:16:19 +08:00
|
|
|
error = xfs_getbmap(XFS_I(file_inode(file)), &bmx, buf);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (error)
|
2017-10-18 05:16:19 +08:00
|
|
|
goto out_free_buf;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-10-18 05:16:19 +08:00
|
|
|
error = -EFAULT;
|
|
|
|
if (copy_to_user(arg, &bmx, recsize))
|
|
|
|
goto out_free_buf;
|
|
|
|
arg += recsize;
|
|
|
|
|
|
|
|
for (i = 0; i < bmx.bmv_entries; i++) {
|
|
|
|
if (!xfs_getbmap_format(buf + i, arg, recsize))
|
|
|
|
goto out_free_buf;
|
|
|
|
arg += recsize;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-10-18 05:16:19 +08:00
|
|
|
error = 0;
|
|
|
|
out_free_buf:
|
|
|
|
kmem_free(buf);
|
2018-11-06 23:50:50 +08:00
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2017-03-29 05:56:37 +08:00
|
|
|
struct getfsmap_info {
|
|
|
|
struct xfs_mount *mp;
|
2017-04-22 02:24:41 +08:00
|
|
|
struct fsmap_head __user *data;
|
|
|
|
unsigned int idx;
|
2017-03-29 05:56:37 +08:00
|
|
|
__u32 last_flags;
|
|
|
|
};
|
|
|
|
|
|
|
|
STATIC int
|
|
|
|
xfs_getfsmap_format(struct xfs_fsmap *xfm, void *priv)
|
|
|
|
{
|
|
|
|
struct getfsmap_info *info = priv;
|
|
|
|
struct fsmap fm;
|
|
|
|
|
|
|
|
trace_xfs_getfsmap_mapping(info->mp, xfm);
|
|
|
|
|
|
|
|
info->last_flags = xfm->fmr_flags;
|
|
|
|
xfs_fsmap_from_internal(&fm, xfm);
|
2017-04-22 02:24:41 +08:00
|
|
|
if (copy_to_user(&info->data->fmh_recs[info->idx++], &fm,
|
|
|
|
sizeof(struct fsmap)))
|
2017-03-29 05:56:37 +08:00
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC int
|
|
|
|
xfs_ioc_getfsmap(
|
|
|
|
struct xfs_inode *ip,
|
2017-04-22 02:24:41 +08:00
|
|
|
struct fsmap_head __user *arg)
|
2017-03-29 05:56:37 +08:00
|
|
|
{
|
2017-04-22 02:24:40 +08:00
|
|
|
struct getfsmap_info info = { NULL };
|
2017-03-29 05:56:37 +08:00
|
|
|
struct xfs_fsmap_head xhead = {0};
|
|
|
|
struct fsmap_head head;
|
|
|
|
bool aborted = false;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (copy_from_user(&head, arg, sizeof(struct fsmap_head)))
|
|
|
|
return -EFAULT;
|
|
|
|
if (memchr_inv(head.fmh_reserved, 0, sizeof(head.fmh_reserved)) ||
|
|
|
|
memchr_inv(head.fmh_keys[0].fmr_reserved, 0,
|
|
|
|
sizeof(head.fmh_keys[0].fmr_reserved)) ||
|
|
|
|
memchr_inv(head.fmh_keys[1].fmr_reserved, 0,
|
|
|
|
sizeof(head.fmh_keys[1].fmr_reserved)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
xhead.fmh_iflags = head.fmh_iflags;
|
|
|
|
xhead.fmh_count = head.fmh_count;
|
|
|
|
xfs_fsmap_to_internal(&xhead.fmh_keys[0], &head.fmh_keys[0]);
|
|
|
|
xfs_fsmap_to_internal(&xhead.fmh_keys[1], &head.fmh_keys[1]);
|
|
|
|
|
|
|
|
trace_xfs_getfsmap_low_key(ip->i_mount, &xhead.fmh_keys[0]);
|
|
|
|
trace_xfs_getfsmap_high_key(ip->i_mount, &xhead.fmh_keys[1]);
|
|
|
|
|
|
|
|
info.mp = ip->i_mount;
|
2017-04-22 02:24:41 +08:00
|
|
|
info.data = arg;
|
2017-03-29 05:56:37 +08:00
|
|
|
error = xfs_getfsmap(ip->i_mount, &xhead, xfs_getfsmap_format, &info);
|
2019-08-29 05:37:57 +08:00
|
|
|
if (error == -ECANCELED) {
|
2017-03-29 05:56:37 +08:00
|
|
|
error = 0;
|
|
|
|
aborted = true;
|
|
|
|
} else if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
/* If we didn't abort, set the "last" flag in the last fmx */
|
2017-04-24 01:45:21 +08:00
|
|
|
if (!aborted && info.idx) {
|
2017-03-29 05:56:37 +08:00
|
|
|
info.last_flags |= FMR_OF_LAST;
|
2017-04-22 02:24:41 +08:00
|
|
|
if (copy_to_user(&info.data->fmh_recs[info.idx - 1].fmr_flags,
|
|
|
|
&info.last_flags, sizeof(info.last_flags)))
|
2017-03-29 05:56:37 +08:00
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy back header */
|
|
|
|
head.fmh_entries = xhead.fmh_entries;
|
|
|
|
head.fmh_oflags = xhead.fmh_oflags;
|
|
|
|
if (copy_to_user(arg, &head, sizeof(struct fsmap_head)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-18 12:37:34 +08:00
|
|
|
STATIC int
|
|
|
|
xfs_ioc_scrub_metadata(
|
|
|
|
struct xfs_inode *ip,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
|
|
|
struct xfs_scrub_metadata scrub;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (copy_from_user(&scrub, arg, sizeof(scrub)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
error = xfs_scrub_metadata(ip, &scrub);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &scrub, sizeof(scrub)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-12 18:49:48 +08:00
|
|
|
int
|
|
|
|
xfs_ioc_swapext(
|
|
|
|
xfs_swapext_t *sxp)
|
|
|
|
{
|
|
|
|
xfs_inode_t *ip, *tip;
|
|
|
|
struct fd f, tmp;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
/* Pull information for the target fd */
|
|
|
|
f = fdget((int)sxp->sx_fdtarget);
|
|
|
|
if (!f.file) {
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -EINVAL;
|
2013-08-12 18:49:48 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(f.file->f_mode & FMODE_WRITE) ||
|
|
|
|
!(f.file->f_mode & FMODE_READ) ||
|
|
|
|
(f.file->f_flags & O_APPEND)) {
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -EBADF;
|
2013-08-12 18:49:48 +08:00
|
|
|
goto out_put_file;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = fdget((int)sxp->sx_fdtmp);
|
|
|
|
if (!tmp.file) {
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -EINVAL;
|
2013-08-12 18:49:48 +08:00
|
|
|
goto out_put_file;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(tmp.file->f_mode & FMODE_WRITE) ||
|
|
|
|
!(tmp.file->f_mode & FMODE_READ) ||
|
|
|
|
(tmp.file->f_flags & O_APPEND)) {
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -EBADF;
|
2013-08-12 18:49:48 +08:00
|
|
|
goto out_put_tmp_file;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_SWAPFILE(file_inode(f.file)) ||
|
|
|
|
IS_SWAPFILE(file_inode(tmp.file))) {
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -EINVAL;
|
2013-08-12 18:49:48 +08:00
|
|
|
goto out_put_tmp_file;
|
|
|
|
}
|
|
|
|
|
2016-07-20 08:30:30 +08:00
|
|
|
/*
|
|
|
|
* We need to ensure that the fds passed in point to XFS inodes
|
|
|
|
* before we cast and access them as XFS structures as we have no
|
|
|
|
* control over what the user passes us here.
|
|
|
|
*/
|
|
|
|
if (f.file->f_op != &xfs_file_operations ||
|
|
|
|
tmp.file->f_op != &xfs_file_operations) {
|
|
|
|
error = -EINVAL;
|
|
|
|
goto out_put_tmp_file;
|
|
|
|
}
|
|
|
|
|
2013-08-12 18:49:48 +08:00
|
|
|
ip = XFS_I(file_inode(f.file));
|
|
|
|
tip = XFS_I(file_inode(tmp.file));
|
|
|
|
|
|
|
|
if (ip->i_mount != tip->i_mount) {
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -EINVAL;
|
2013-08-12 18:49:48 +08:00
|
|
|
goto out_put_tmp_file;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ip->i_ino == tip->i_ino) {
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -EINVAL;
|
2013-08-12 18:49:48 +08:00
|
|
|
goto out_put_tmp_file;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (XFS_FORCED_SHUTDOWN(ip->i_mount)) {
|
2014-06-25 12:58:08 +08:00
|
|
|
error = -EIO;
|
2013-08-12 18:49:48 +08:00
|
|
|
goto out_put_tmp_file;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = xfs_swap_extents(ip, tip, sxp);
|
|
|
|
|
|
|
|
out_put_tmp_file:
|
|
|
|
fdput(tmp);
|
|
|
|
out_put_file:
|
|
|
|
fdput(f);
|
|
|
|
out:
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2018-05-16 04:21:48 +08:00
|
|
|
static int
|
|
|
|
xfs_ioc_getlabel(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
char __user *user_label)
|
|
|
|
{
|
|
|
|
struct xfs_sb *sbp = &mp->m_sb;
|
|
|
|
char label[XFSLABEL_MAX + 1];
|
|
|
|
|
|
|
|
/* Paranoia */
|
|
|
|
BUILD_BUG_ON(sizeof(sbp->sb_fname) > FSLABEL_MAX);
|
|
|
|
|
xfs: fix string handling in label get/set functions
[sandeen: fix subject, avoid copy-out of uninit data in getlabel]
gcc-8 reports two warnings for the newly added getlabel/setlabel code:
fs/xfs/xfs_ioctl.c: In function 'xfs_ioc_getlabel':
fs/xfs/xfs_ioctl.c:1822:38: error: argument to 'sizeof' in 'strncpy' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess]
strncpy(label, sbp->sb_fname, sizeof(sbp->sb_fname));
^
In function 'strncpy',
inlined from 'xfs_ioc_setlabel' at /git/arm-soc/fs/xfs/xfs_ioctl.c:1863:2,
inlined from 'xfs_file_ioctl' at /git/arm-soc/fs/xfs/xfs_ioctl.c:1918:10:
include/linux/string.h:254:9: error: '__builtin_strncpy' output may be truncated copying 12 bytes from a string of length 12 [-Werror=stringop-truncation]
return __builtin_strncpy(p, q, size);
In both cases, part of the problem is that one of the strncpy()
arguments is a fixed-length character array with zero-padding rather
than a zero-terminated string. In the first one case, we also get an
odd warning about sizeof-pointer-memaccess, which doesn't seem right
(the sizeof is for an array that happens to be the same as the second
strncpy argument).
To work around the bogus warning, I use a plain 'XFSLABEL_MAX' for
the strncpy() length when copying the label in getlabel. For setlabel(),
using memcpy() with the correct length that is already known avoids
the second warning and is slightly simpler.
In a related issue, it appears that we accidentally skip the trailing
\0 when copying a 12-character label back to user space in getlabel().
Using the correct sizeof() argument here copies the extra character.
Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85602
Fixes: f7664b31975b ("xfs: implement online get/set fs label")
Cc: Eric Sandeen <sandeen@redhat.com>
Cc: Martin Sebor <msebor@gmail.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
2018-06-06 10:42:45 +08:00
|
|
|
/* 1 larger than sb_fname, so this ensures a trailing NUL char */
|
|
|
|
memset(label, 0, sizeof(label));
|
2018-05-16 04:21:48 +08:00
|
|
|
spin_lock(&mp->m_sb_lock);
|
xfs: fix string handling in label get/set functions
[sandeen: fix subject, avoid copy-out of uninit data in getlabel]
gcc-8 reports two warnings for the newly added getlabel/setlabel code:
fs/xfs/xfs_ioctl.c: In function 'xfs_ioc_getlabel':
fs/xfs/xfs_ioctl.c:1822:38: error: argument to 'sizeof' in 'strncpy' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess]
strncpy(label, sbp->sb_fname, sizeof(sbp->sb_fname));
^
In function 'strncpy',
inlined from 'xfs_ioc_setlabel' at /git/arm-soc/fs/xfs/xfs_ioctl.c:1863:2,
inlined from 'xfs_file_ioctl' at /git/arm-soc/fs/xfs/xfs_ioctl.c:1918:10:
include/linux/string.h:254:9: error: '__builtin_strncpy' output may be truncated copying 12 bytes from a string of length 12 [-Werror=stringop-truncation]
return __builtin_strncpy(p, q, size);
In both cases, part of the problem is that one of the strncpy()
arguments is a fixed-length character array with zero-padding rather
than a zero-terminated string. In the first one case, we also get an
odd warning about sizeof-pointer-memaccess, which doesn't seem right
(the sizeof is for an array that happens to be the same as the second
strncpy argument).
To work around the bogus warning, I use a plain 'XFSLABEL_MAX' for
the strncpy() length when copying the label in getlabel. For setlabel(),
using memcpy() with the correct length that is already known avoids
the second warning and is slightly simpler.
In a related issue, it appears that we accidentally skip the trailing
\0 when copying a 12-character label back to user space in getlabel().
Using the correct sizeof() argument here copies the extra character.
Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85602
Fixes: f7664b31975b ("xfs: implement online get/set fs label")
Cc: Eric Sandeen <sandeen@redhat.com>
Cc: Martin Sebor <msebor@gmail.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
2018-06-06 10:42:45 +08:00
|
|
|
strncpy(label, sbp->sb_fname, XFSLABEL_MAX);
|
2018-05-16 04:21:48 +08:00
|
|
|
spin_unlock(&mp->m_sb_lock);
|
|
|
|
|
xfs: fix string handling in label get/set functions
[sandeen: fix subject, avoid copy-out of uninit data in getlabel]
gcc-8 reports two warnings for the newly added getlabel/setlabel code:
fs/xfs/xfs_ioctl.c: In function 'xfs_ioc_getlabel':
fs/xfs/xfs_ioctl.c:1822:38: error: argument to 'sizeof' in 'strncpy' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess]
strncpy(label, sbp->sb_fname, sizeof(sbp->sb_fname));
^
In function 'strncpy',
inlined from 'xfs_ioc_setlabel' at /git/arm-soc/fs/xfs/xfs_ioctl.c:1863:2,
inlined from 'xfs_file_ioctl' at /git/arm-soc/fs/xfs/xfs_ioctl.c:1918:10:
include/linux/string.h:254:9: error: '__builtin_strncpy' output may be truncated copying 12 bytes from a string of length 12 [-Werror=stringop-truncation]
return __builtin_strncpy(p, q, size);
In both cases, part of the problem is that one of the strncpy()
arguments is a fixed-length character array with zero-padding rather
than a zero-terminated string. In the first one case, we also get an
odd warning about sizeof-pointer-memaccess, which doesn't seem right
(the sizeof is for an array that happens to be the same as the second
strncpy argument).
To work around the bogus warning, I use a plain 'XFSLABEL_MAX' for
the strncpy() length when copying the label in getlabel. For setlabel(),
using memcpy() with the correct length that is already known avoids
the second warning and is slightly simpler.
In a related issue, it appears that we accidentally skip the trailing
\0 when copying a 12-character label back to user space in getlabel().
Using the correct sizeof() argument here copies the extra character.
Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85602
Fixes: f7664b31975b ("xfs: implement online get/set fs label")
Cc: Eric Sandeen <sandeen@redhat.com>
Cc: Martin Sebor <msebor@gmail.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
2018-06-06 10:42:45 +08:00
|
|
|
if (copy_to_user(user_label, label, sizeof(label)))
|
2018-05-16 04:21:48 +08:00
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
xfs_ioc_setlabel(
|
|
|
|
struct file *filp,
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
char __user *newlabel)
|
|
|
|
{
|
|
|
|
struct xfs_sb *sbp = &mp->m_sb;
|
|
|
|
char label[XFSLABEL_MAX + 1];
|
|
|
|
size_t len;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
/*
|
|
|
|
* The generic ioctl allows up to FSLABEL_MAX chars, but XFS is much
|
|
|
|
* smaller, at 12 bytes. We copy one more to be sure we find the
|
|
|
|
* (required) NULL character to test the incoming label length.
|
|
|
|
* NB: The on disk label doesn't need to be null terminated.
|
|
|
|
*/
|
|
|
|
if (copy_from_user(label, newlabel, XFSLABEL_MAX + 1))
|
|
|
|
return -EFAULT;
|
|
|
|
len = strnlen(label, XFSLABEL_MAX + 1);
|
|
|
|
if (len > sizeof(sbp->sb_fname))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
spin_lock(&mp->m_sb_lock);
|
|
|
|
memset(sbp->sb_fname, 0, sizeof(sbp->sb_fname));
|
xfs: fix string handling in label get/set functions
[sandeen: fix subject, avoid copy-out of uninit data in getlabel]
gcc-8 reports two warnings for the newly added getlabel/setlabel code:
fs/xfs/xfs_ioctl.c: In function 'xfs_ioc_getlabel':
fs/xfs/xfs_ioctl.c:1822:38: error: argument to 'sizeof' in 'strncpy' call is the same expression as the source; did you mean to use the size of the destination? [-Werror=sizeof-pointer-memaccess]
strncpy(label, sbp->sb_fname, sizeof(sbp->sb_fname));
^
In function 'strncpy',
inlined from 'xfs_ioc_setlabel' at /git/arm-soc/fs/xfs/xfs_ioctl.c:1863:2,
inlined from 'xfs_file_ioctl' at /git/arm-soc/fs/xfs/xfs_ioctl.c:1918:10:
include/linux/string.h:254:9: error: '__builtin_strncpy' output may be truncated copying 12 bytes from a string of length 12 [-Werror=stringop-truncation]
return __builtin_strncpy(p, q, size);
In both cases, part of the problem is that one of the strncpy()
arguments is a fixed-length character array with zero-padding rather
than a zero-terminated string. In the first one case, we also get an
odd warning about sizeof-pointer-memaccess, which doesn't seem right
(the sizeof is for an array that happens to be the same as the second
strncpy argument).
To work around the bogus warning, I use a plain 'XFSLABEL_MAX' for
the strncpy() length when copying the label in getlabel. For setlabel(),
using memcpy() with the correct length that is already known avoids
the second warning and is slightly simpler.
In a related issue, it appears that we accidentally skip the trailing
\0 when copying a 12-character label back to user space in getlabel().
Using the correct sizeof() argument here copies the extra character.
Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85602
Fixes: f7664b31975b ("xfs: implement online get/set fs label")
Cc: Eric Sandeen <sandeen@redhat.com>
Cc: Martin Sebor <msebor@gmail.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
2018-06-06 10:42:45 +08:00
|
|
|
memcpy(sbp->sb_fname, label, len);
|
2018-05-16 04:21:48 +08:00
|
|
|
spin_unlock(&mp->m_sb_lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now we do several things to satisfy userspace.
|
|
|
|
* In addition to normal logging of the primary superblock, we also
|
|
|
|
* immediately write these changes to sector zero for the primary, then
|
|
|
|
* update all backup supers (as xfs_db does for a label change), then
|
|
|
|
* invalidate the block device page cache. This is so that any prior
|
|
|
|
* buffered reads from userspace (i.e. from blkid) are invalidated,
|
|
|
|
* and userspace will see the newly-written label.
|
|
|
|
*/
|
|
|
|
error = xfs_sync_sb_buf(mp);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
/*
|
|
|
|
* growfs also updates backup supers so lock against that.
|
|
|
|
*/
|
|
|
|
mutex_lock(&mp->m_growlock);
|
|
|
|
error = xfs_update_secondary_sbs(mp);
|
|
|
|
mutex_unlock(&mp->m_growlock);
|
|
|
|
|
|
|
|
invalidate_bdev(mp->m_ddev_targp->bt_bdev);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mnt_drop_write_file(filp);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2008-12-09 17:47:33 +08:00
|
|
|
/*
|
|
|
|
* Note: some of the ioctl's return positive numbers as a
|
|
|
|
* byte count indicating success, such as readlink_by_handle.
|
|
|
|
* So we don't "sign flip" like most other routines. This means
|
|
|
|
* true errors need to be returned as a negative value.
|
|
|
|
*/
|
|
|
|
long
|
|
|
|
xfs_file_ioctl(
|
2008-04-18 09:44:03 +08:00
|
|
|
struct file *filp,
|
|
|
|
unsigned int cmd,
|
2008-12-09 17:47:33 +08:00
|
|
|
unsigned long p)
|
2008-04-18 09:44:03 +08:00
|
|
|
{
|
2013-01-24 06:07:38 +08:00
|
|
|
struct inode *inode = file_inode(filp);
|
2008-12-09 17:47:33 +08:00
|
|
|
struct xfs_inode *ip = XFS_I(inode);
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
|
|
|
void __user *arg = (void __user *)p;
|
2008-04-18 09:44:03 +08:00
|
|
|
int error;
|
|
|
|
|
2010-06-24 09:57:09 +08:00
|
|
|
trace_xfs_file_ioctl(ip);
|
2008-12-09 17:47:33 +08:00
|
|
|
|
|
|
|
switch (cmd) {
|
2011-01-07 21:02:04 +08:00
|
|
|
case FITRIM:
|
|
|
|
return xfs_ioc_trim(mp, arg);
|
2018-05-16 04:21:48 +08:00
|
|
|
case FS_IOC_GETFSLABEL:
|
|
|
|
return xfs_ioc_getlabel(mp, arg);
|
|
|
|
case FS_IOC_SETFSLABEL:
|
|
|
|
return xfs_ioc_setlabel(filp, mp, arg);
|
2008-04-18 09:44:03 +08:00
|
|
|
case XFS_IOC_ALLOCSP:
|
|
|
|
case XFS_IOC_FREESP:
|
|
|
|
case XFS_IOC_ALLOCSP64:
|
2019-10-25 13:26:02 +08:00
|
|
|
case XFS_IOC_FREESP64: {
|
2008-11-26 11:20:06 +08:00
|
|
|
xfs_flock64_t bf;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2008-11-26 11:20:06 +08:00
|
|
|
if (copy_from_user(&bf, arg, sizeof(bf)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2019-10-25 13:26:02 +08:00
|
|
|
return xfs_ioc_space(filp, &bf);
|
2008-11-26 11:20:06 +08:00
|
|
|
}
|
2008-04-18 09:44:03 +08:00
|
|
|
case XFS_IOC_DIOINFO: {
|
2019-10-25 13:25:39 +08:00
|
|
|
struct xfs_buftarg *target = xfs_inode_buftarg(ip);
|
|
|
|
struct dioattr da;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
xfs: allow logical-sector sized O_DIRECT
Some time ago, mkfs.xfs started picking the storage physical
sector size as the default filesystem "sector size" in order
to avoid RMW costs incurred by doing IOs at logical sector
size alignments.
However, this means that for a filesystem made with i.e.
a 4k sector size on an "advanced format" 4k/512 disk,
512-byte direct IOs are no longer allowed. This means
that XFS has essentially turned this AF drive into a hard
4K device, from the filesystem on up.
XFS's mkfs-specified "sector size" is really just controlling
the minimum size & alignment of filesystem metadata.
There is no real need to tightly couple XFS's minimal
metadata size to the minimum allowed direct IO size;
XFS can continue doing metadata in optimal sizes, but
still allow smaller DIOs for apps which issue them,
for whatever reason.
This patch adds a new field to the xfs_buftarg, so that
we now track 2 sizes:
1) The metadata sector size, which is the minimum unit and
alignment of IO which will be performed by metadata operations.
2) The device logical sector size
The first is used internally by the file system for metadata
alignment and IOs.
The second is used for the minimum allowed direct IO alignment.
This has passed xfstests on filesystems made with 4k sectors,
including when run under the patch I sent to ignore
XFS_IOC_DIOINFO, and issue 512 DIOs anyway. I also directly
tested end of block behavior on preallocated, sparse, and
existing files when we do a 512 IO into a 4k file on a
4k-sector filesystem, to be sure there were no unexpected
behaviors.
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2014-01-22 06:46:23 +08:00
|
|
|
da.d_mem = da.d_miniosz = target->bt_logical_sectorsize;
|
2008-04-18 09:44:03 +08:00
|
|
|
da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &da, sizeof(da)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2008-04-18 09:44:03 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_FSBULKSTAT_SINGLE:
|
|
|
|
case XFS_IOC_FSBULKSTAT:
|
|
|
|
case XFS_IOC_FSINUMBERS:
|
2019-07-04 11:36:26 +08:00
|
|
|
return xfs_ioc_fsbulkstat(mp, cmd, arg);
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2019-07-04 11:36:27 +08:00
|
|
|
case XFS_IOC_BULKSTAT:
|
2008-04-18 09:44:03 +08:00
|
|
|
return xfs_ioc_bulkstat(mp, cmd, arg);
|
2019-07-04 11:36:28 +08:00
|
|
|
case XFS_IOC_INUMBERS:
|
|
|
|
return xfs_ioc_inumbers(mp, cmd, arg);
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
case XFS_IOC_FSGEOMETRY_V1:
|
2019-04-12 22:41:16 +08:00
|
|
|
return xfs_ioc_fsgeometry(mp, arg, 3);
|
|
|
|
case XFS_IOC_FSGEOMETRY_V4:
|
|
|
|
return xfs_ioc_fsgeometry(mp, arg, 4);
|
2008-04-18 09:44:03 +08:00
|
|
|
case XFS_IOC_FSGEOMETRY:
|
2019-04-12 22:41:16 +08:00
|
|
|
return xfs_ioc_fsgeometry(mp, arg, 5);
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2019-04-12 22:41:17 +08:00
|
|
|
case XFS_IOC_AG_GEOMETRY:
|
|
|
|
return xfs_ioc_ag_geometry(mp, arg);
|
|
|
|
|
2008-04-18 09:44:03 +08:00
|
|
|
case XFS_IOC_GETVERSION:
|
|
|
|
return put_user(inode->i_generation, (int __user *)arg);
|
|
|
|
|
|
|
|
case XFS_IOC_FSGETXATTR:
|
|
|
|
return xfs_ioc_fsgetxattr(ip, 0, arg);
|
|
|
|
case XFS_IOC_FSGETXATTRA:
|
|
|
|
return xfs_ioc_fsgetxattr(ip, 1, arg);
|
2008-04-18 10:59:45 +08:00
|
|
|
case XFS_IOC_FSSETXATTR:
|
|
|
|
return xfs_ioc_fssetxattr(ip, filp, arg);
|
2008-04-18 09:44:03 +08:00
|
|
|
case XFS_IOC_GETXFLAGS:
|
2008-04-18 10:59:45 +08:00
|
|
|
return xfs_ioc_getxflags(ip, arg);
|
2008-04-18 09:44:03 +08:00
|
|
|
case XFS_IOC_SETXFLAGS:
|
2008-04-18 10:59:45 +08:00
|
|
|
return xfs_ioc_setxflags(ip, filp, arg);
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
case XFS_IOC_GETBMAP:
|
|
|
|
case XFS_IOC_GETBMAPA:
|
|
|
|
case XFS_IOC_GETBMAPX:
|
2017-10-18 05:16:19 +08:00
|
|
|
return xfs_ioc_getbmap(filp, cmd, arg);
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2017-03-29 05:56:37 +08:00
|
|
|
case FS_IOC_GETFSMAP:
|
|
|
|
return xfs_ioc_getfsmap(ip, arg);
|
|
|
|
|
2017-10-18 12:37:34 +08:00
|
|
|
case XFS_IOC_SCRUB_METADATA:
|
|
|
|
return xfs_ioc_scrub_metadata(ip, arg);
|
|
|
|
|
2008-04-18 09:44:03 +08:00
|
|
|
case XFS_IOC_FD_TO_HANDLE:
|
|
|
|
case XFS_IOC_PATH_TO_HANDLE:
|
2008-11-26 11:20:06 +08:00
|
|
|
case XFS_IOC_PATH_TO_FSHANDLE: {
|
|
|
|
xfs_fsop_handlereq_t hreq;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2008-11-26 11:20:06 +08:00
|
|
|
if (copy_from_user(&hreq, arg, sizeof(hreq)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2008-11-26 11:20:06 +08:00
|
|
|
return xfs_find_handle(cmd, &hreq);
|
|
|
|
}
|
|
|
|
case XFS_IOC_OPEN_BY_HANDLE: {
|
|
|
|
xfs_fsop_handlereq_t hreq;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2008-11-26 11:20:06 +08:00
|
|
|
if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2009-01-19 09:02:57 +08:00
|
|
|
return xfs_open_by_handle(filp, &hreq);
|
2008-11-26 11:20:06 +08:00
|
|
|
}
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2008-11-26 11:20:06 +08:00
|
|
|
case XFS_IOC_READLINK_BY_HANDLE: {
|
|
|
|
xfs_fsop_handlereq_t hreq;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2008-11-26 11:20:06 +08:00
|
|
|
if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2009-01-19 09:02:57 +08:00
|
|
|
return xfs_readlink_by_handle(filp, &hreq);
|
2008-11-26 11:20:06 +08:00
|
|
|
}
|
2008-04-18 09:44:03 +08:00
|
|
|
case XFS_IOC_ATTRLIST_BY_HANDLE:
|
2009-01-19 09:02:57 +08:00
|
|
|
return xfs_attrlist_by_handle(filp, arg);
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
case XFS_IOC_ATTRMULTI_BY_HANDLE:
|
2009-01-19 09:02:57 +08:00
|
|
|
return xfs_attrmulti_by_handle(filp, arg);
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
case XFS_IOC_SWAPEXT: {
|
2008-11-26 11:20:06 +08:00
|
|
|
struct xfs_swapext sxp;
|
|
|
|
|
|
|
|
if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2012-06-12 22:20:39 +08:00
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2013-08-12 18:49:48 +08:00
|
|
|
error = xfs_ioc_swapext(&sxp);
|
2012-06-12 22:20:39 +08:00
|
|
|
mnt_drop_write_file(filp);
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2008-04-18 09:44:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_FSCOUNTS: {
|
|
|
|
xfs_fsop_counts_t out;
|
|
|
|
|
2019-05-02 11:26:30 +08:00
|
|
|
xfs_fs_counts(mp, &out);
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
if (copy_to_user(arg, &out, sizeof(out)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2008-04-18 09:44:03 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_SET_RESBLKS: {
|
|
|
|
xfs_fsop_resblks_t inout;
|
2017-06-17 02:00:05 +08:00
|
|
|
uint64_t in;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
2010-02-06 06:59:53 +08:00
|
|
|
if (mp->m_flags & XFS_MOUNT_RDONLY)
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EROFS;
|
2010-02-06 06:59:53 +08:00
|
|
|
|
2008-04-18 09:44:03 +08:00
|
|
|
if (copy_from_user(&inout, arg, sizeof(inout)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2012-06-12 22:20:39 +08:00
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
2008-04-18 09:44:03 +08:00
|
|
|
/* input parameter is passed in resblks field of structure */
|
|
|
|
in = inout.resblks;
|
|
|
|
error = xfs_reserve_blocks(mp, &in, &inout);
|
2012-06-12 22:20:39 +08:00
|
|
|
mnt_drop_write_file(filp);
|
2008-04-18 09:44:03 +08:00
|
|
|
if (error)
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
if (copy_to_user(arg, &inout, sizeof(inout)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2008-04-18 09:44:03 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_GET_RESBLKS: {
|
|
|
|
xfs_fsop_resblks_t out;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
error = xfs_reserve_blocks(mp, NULL, &out);
|
|
|
|
if (error)
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
if (copy_to_user(arg, &out, sizeof(out)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_FSGROWFSDATA: {
|
|
|
|
xfs_growfs_data_t in;
|
|
|
|
|
|
|
|
if (copy_from_user(&in, arg, sizeof(in)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2012-06-12 22:20:39 +08:00
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2008-04-18 09:44:03 +08:00
|
|
|
error = xfs_growfs_data(mp, &in);
|
2012-06-12 22:20:39 +08:00
|
|
|
mnt_drop_write_file(filp);
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2008-04-18 09:44:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_FSGROWFSLOG: {
|
|
|
|
xfs_growfs_log_t in;
|
|
|
|
|
|
|
|
if (copy_from_user(&in, arg, sizeof(in)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2012-06-12 22:20:39 +08:00
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2008-04-18 09:44:03 +08:00
|
|
|
error = xfs_growfs_log(mp, &in);
|
2012-06-12 22:20:39 +08:00
|
|
|
mnt_drop_write_file(filp);
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2008-04-18 09:44:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_FSGROWFSRT: {
|
|
|
|
xfs_growfs_rt_t in;
|
|
|
|
|
|
|
|
if (copy_from_user(&in, arg, sizeof(in)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2012-06-12 22:20:39 +08:00
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2008-04-18 09:44:03 +08:00
|
|
|
error = xfs_growfs_rt(mp, &in);
|
2012-06-12 22:20:39 +08:00
|
|
|
mnt_drop_write_file(filp);
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2008-04-18 09:44:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_GOINGDOWN: {
|
2017-06-17 02:00:05 +08:00
|
|
|
uint32_t in;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
2017-06-17 02:00:05 +08:00
|
|
|
if (get_user(in, (uint32_t __user *)arg))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2014-06-25 12:58:08 +08:00
|
|
|
return xfs_fs_goingdown(mp, in);
|
2008-04-18 09:44:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_ERROR_INJECTION: {
|
|
|
|
xfs_error_injection_t in;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (copy_from_user(&in, arg, sizeof(in)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2017-06-21 08:54:46 +08:00
|
|
|
return xfs_errortag_add(mp, in.errtag);
|
2008-04-18 09:44:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_ERROR_CLEARALL:
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
2017-06-21 08:54:46 +08:00
|
|
|
return xfs_errortag_clearall(mp);
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2012-11-08 01:21:12 +08:00
|
|
|
case XFS_IOC_FREE_EOFBLOCKS: {
|
2013-08-16 02:08:02 +08:00
|
|
|
struct xfs_fs_eofblocks eofb;
|
|
|
|
struct xfs_eofblocks keofb;
|
2012-11-08 01:21:12 +08:00
|
|
|
|
2013-08-16 02:08:03 +08:00
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (mp->m_flags & XFS_MOUNT_RDONLY)
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EROFS;
|
2013-08-16 02:08:03 +08:00
|
|
|
|
2012-11-08 01:21:12 +08:00
|
|
|
if (copy_from_user(&eofb, arg, sizeof(eofb)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
2012-11-08 01:21:12 +08:00
|
|
|
|
2013-08-16 02:08:02 +08:00
|
|
|
error = xfs_fs_eofblocks_from_user(&eofb, &keofb);
|
|
|
|
if (error)
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2012-11-08 01:21:12 +08:00
|
|
|
|
2014-06-25 12:58:08 +08:00
|
|
|
return xfs_icache_free_eofblocks(mp, &keofb);
|
2012-11-08 01:21:12 +08:00
|
|
|
}
|
|
|
|
|
2008-04-18 09:44:03 +08:00
|
|
|
default:
|
|
|
|
return -ENOTTY;
|
|
|
|
}
|
|
|
|
}
|