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"
|
2020-02-27 09:30:33 +08:00
|
|
|
#include "xfs_da_format.h"
|
|
|
|
#include "xfs_da_btree.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include <linux/mount.h>
|
|
|
|
#include <linux/namei.h>
|
2021-04-07 20:36:43 +08:00
|
|
|
#include <linux/fileattr.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
}
|
|
|
|
|
2020-02-27 09:30:38 +08:00
|
|
|
/*
|
|
|
|
* Format an attribute and copy it out to the user's buffer.
|
|
|
|
* Take care to check values and protect against them changing later,
|
|
|
|
* we may be reading them directly out of a user buffer.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xfs_ioc_attr_put_listent(
|
|
|
|
struct xfs_attr_list_context *context,
|
|
|
|
int flags,
|
|
|
|
unsigned char *name,
|
|
|
|
int namelen,
|
|
|
|
int valuelen)
|
|
|
|
{
|
|
|
|
struct xfs_attrlist *alist = context->buffer;
|
|
|
|
struct xfs_attrlist_ent *aep;
|
|
|
|
int arraytop;
|
|
|
|
|
|
|
|
ASSERT(!context->seen_enough);
|
|
|
|
ASSERT(context->count >= 0);
|
|
|
|
ASSERT(context->count < (ATTR_MAX_VALUELEN/8));
|
|
|
|
ASSERT(context->firstu >= sizeof(*alist));
|
|
|
|
ASSERT(context->firstu <= context->bufsize);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only list entries in the right namespace.
|
|
|
|
*/
|
2020-02-27 09:30:42 +08:00
|
|
|
if (context->attr_filter != (flags & XFS_ATTR_NSP_ONDISK_MASK))
|
2020-02-27 09:30:38 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
arraytop = sizeof(*alist) +
|
|
|
|
context->count * sizeof(alist->al_offset[0]);
|
|
|
|
|
|
|
|
/* decrement by the actual bytes used by the attr */
|
|
|
|
context->firstu -= round_up(offsetof(struct xfs_attrlist_ent, a_name) +
|
|
|
|
namelen + 1, sizeof(uint32_t));
|
|
|
|
if (context->firstu < arraytop) {
|
|
|
|
trace_xfs_attr_list_full(context);
|
|
|
|
alist->al_more = 1;
|
|
|
|
context->seen_enough = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aep = context->buffer + context->firstu;
|
|
|
|
aep->a_valuelen = valuelen;
|
|
|
|
memcpy(aep->a_name, name, namelen);
|
|
|
|
aep->a_name[namelen] = 0;
|
|
|
|
alist->al_offset[context->count++] = context->firstu;
|
|
|
|
alist->al_count = context->count;
|
|
|
|
trace_xfs_attr_list_add(context);
|
|
|
|
}
|
|
|
|
|
2020-02-27 09:30:42 +08:00
|
|
|
static unsigned int
|
|
|
|
xfs_attr_filter(
|
|
|
|
u32 ioc_flags)
|
|
|
|
{
|
|
|
|
if (ioc_flags & XFS_IOC_ATTR_ROOT)
|
|
|
|
return XFS_ATTR_ROOT;
|
|
|
|
if (ioc_flags & XFS_IOC_ATTR_SECURE)
|
|
|
|
return XFS_ATTR_SECURE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
xfs_attr_flags(
|
|
|
|
u32 ioc_flags)
|
|
|
|
{
|
|
|
|
if (ioc_flags & XFS_IOC_ATTR_CREATE)
|
|
|
|
return XATTR_CREATE;
|
|
|
|
if (ioc_flags & XFS_IOC_ATTR_REPLACE)
|
|
|
|
return XATTR_REPLACE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-02-27 09:30:38 +08:00
|
|
|
int
|
|
|
|
xfs_ioc_attr_list(
|
|
|
|
struct xfs_inode *dp,
|
2020-02-27 09:30:40 +08:00
|
|
|
void __user *ubuf,
|
2020-02-27 09:30:38 +08:00
|
|
|
int bufsize,
|
|
|
|
int flags,
|
2020-02-27 09:30:41 +08:00
|
|
|
struct xfs_attrlist_cursor __user *ucursor)
|
2020-02-27 09:30:38 +08:00
|
|
|
{
|
2020-02-27 09:30:43 +08:00
|
|
|
struct xfs_attr_list_context context = { };
|
2020-02-27 09:30:38 +08:00
|
|
|
struct xfs_attrlist *alist;
|
2020-02-27 09:30:40 +08:00
|
|
|
void *buffer;
|
2020-02-27 09:30:38 +08:00
|
|
|
int error;
|
|
|
|
|
2020-02-27 09:30:40 +08:00
|
|
|
if (bufsize < sizeof(struct xfs_attrlist) ||
|
|
|
|
bufsize > XFS_XATTR_LIST_MAX)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reject flags, only allow namespaces.
|
|
|
|
*/
|
2020-02-27 09:30:42 +08:00
|
|
|
if (flags & ~(XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))
|
2020-02-27 09:30:40 +08:00
|
|
|
return -EINVAL;
|
2020-02-27 09:30:42 +08:00
|
|
|
if (flags == (XFS_IOC_ATTR_ROOT | XFS_IOC_ATTR_SECURE))
|
2020-02-27 09:30:40 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2020-02-27 09:30:38 +08:00
|
|
|
/*
|
|
|
|
* Validate the cursor.
|
|
|
|
*/
|
2020-02-27 09:30:43 +08:00
|
|
|
if (copy_from_user(&context.cursor, ucursor, sizeof(context.cursor)))
|
2020-02-27 09:30:41 +08:00
|
|
|
return -EFAULT;
|
2020-02-27 09:30:43 +08:00
|
|
|
if (context.cursor.pad1 || context.cursor.pad2)
|
2020-02-27 09:30:38 +08:00
|
|
|
return -EINVAL;
|
2020-02-27 09:30:43 +08:00
|
|
|
if (!context.cursor.initted &&
|
|
|
|
(context.cursor.hashval || context.cursor.blkno ||
|
|
|
|
context.cursor.offset))
|
2020-02-27 09:30:38 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2020-09-02 02:47:12 +08:00
|
|
|
buffer = kvzalloc(bufsize, GFP_KERNEL);
|
2020-02-27 09:30:40 +08:00
|
|
|
if (!buffer)
|
|
|
|
return -ENOMEM;
|
2020-02-27 09:30:38 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the output buffer.
|
|
|
|
*/
|
|
|
|
context.dp = dp;
|
|
|
|
context.resynch = 1;
|
2020-02-27 09:30:42 +08:00
|
|
|
context.attr_filter = xfs_attr_filter(flags);
|
2020-02-27 09:30:38 +08:00
|
|
|
context.buffer = buffer;
|
2020-02-27 09:30:44 +08:00
|
|
|
context.bufsize = round_down(bufsize, sizeof(uint32_t));
|
2020-02-27 09:30:38 +08:00
|
|
|
context.firstu = context.bufsize;
|
|
|
|
context.put_listent = xfs_ioc_attr_put_listent;
|
|
|
|
|
|
|
|
alist = context.buffer;
|
|
|
|
alist->al_count = 0;
|
|
|
|
alist->al_more = 0;
|
|
|
|
alist->al_offset[0] = context.bufsize;
|
|
|
|
|
2020-02-27 09:30:39 +08:00
|
|
|
error = xfs_attr_list(&context);
|
2020-02-27 09:30:40 +08:00
|
|
|
if (error)
|
|
|
|
goto out_free;
|
|
|
|
|
2020-02-27 09:30:41 +08:00
|
|
|
if (copy_to_user(ubuf, buffer, bufsize) ||
|
2020-02-27 09:30:43 +08:00
|
|
|
copy_to_user(ucursor, &context.cursor, sizeof(context.cursor)))
|
2020-02-27 09:30:40 +08:00
|
|
|
error = -EFAULT;
|
|
|
|
out_free:
|
|
|
|
kmem_free(buffer);
|
2020-02-27 09:30:38 +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,
|
2020-02-27 09:30:41 +08:00
|
|
|
struct xfs_fsop_attrlist_handlereq __user *p)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2020-02-27 09:30:41 +08:00
|
|
|
struct xfs_fsop_attrlist_handlereq al_hreq;
|
2009-01-19 09:02:57 +08:00
|
|
|
struct dentry *dentry;
|
2020-02-27 09:30:41 +08:00
|
|
|
int error = -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EPERM;
|
2020-02-27 09:30:41 +08:00
|
|
|
if (copy_from_user(&al_hreq, p, sizeof(al_hreq)))
|
2014-06-22 13:04:54 +08:00
|
|
|
return -EFAULT;
|
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
|
|
|
|
2020-02-27 09:30:40 +08:00
|
|
|
error = xfs_ioc_attr_list(XFS_I(d_inode(dentry)), al_hreq.buffer,
|
2020-02-27 09:30:41 +08:00
|
|
|
al_hreq.buflen, al_hreq.flags, &p->pos);
|
2009-01-19 09:02:57 +08:00
|
|
|
dput(dentry);
|
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2020-02-27 09:30:31 +08:00
|
|
|
static 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
|
|
|
{
|
2020-02-27 09:30:34 +08:00
|
|
|
struct xfs_da_args args = {
|
|
|
|
.dp = XFS_I(inode),
|
2020-02-27 09:30:42 +08:00
|
|
|
.attr_filter = xfs_attr_filter(flags),
|
|
|
|
.attr_flags = xfs_attr_flags(flags),
|
2020-02-27 09:30:34 +08:00
|
|
|
.name = name,
|
|
|
|
.namelen = strlen(name),
|
|
|
|
.valuelen = *len,
|
|
|
|
};
|
|
|
|
int error;
|
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;
|
2020-02-27 09:30:34 +08:00
|
|
|
|
|
|
|
error = xfs_attr_get(&args);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (error)
|
|
|
|
goto out_kfree;
|
|
|
|
|
2020-02-27 09:30:34 +08:00
|
|
|
*len = args.valuelen;
|
|
|
|
if (copy_to_user(ubuf, args.value, args.valuelen))
|
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:
|
2020-02-27 09:30:34 +08:00
|
|
|
kmem_free(args.value);
|
2005-04-17 06:20:36 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2020-02-27 09:30:31 +08:00
|
|
|
static 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
|
|
|
{
|
2020-02-27 09:30:33 +08:00
|
|
|
struct xfs_da_args args = {
|
|
|
|
.dp = XFS_I(inode),
|
2020-02-27 09:30:42 +08:00
|
|
|
.attr_filter = xfs_attr_filter(flags),
|
|
|
|
.attr_flags = xfs_attr_flags(flags),
|
2020-02-27 09:30:33 +08:00
|
|
|
.name = name,
|
|
|
|
.namelen = strlen(name),
|
|
|
|
};
|
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;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2020-02-27 09:30:30 +08:00
|
|
|
if (ubuf) {
|
|
|
|
if (len > XFS_XATTR_SIZE_MAX)
|
|
|
|
return -EINVAL;
|
2020-02-27 09:30:33 +08:00
|
|
|
args.value = memdup_user(ubuf, len);
|
|
|
|
if (IS_ERR(args.value))
|
|
|
|
return PTR_ERR(args.value);
|
|
|
|
args.valuelen = len;
|
2020-02-27 09:30:30 +08:00
|
|
|
}
|
2008-04-22 15:34:31 +08:00
|
|
|
|
2020-02-27 09:30:33 +08:00
|
|
|
error = xfs_attr_set(&args);
|
2020-02-27 09:30:42 +08:00
|
|
|
if (!error && (flags & XFS_IOC_ATTR_ROOT))
|
2020-02-27 09:30:41 +08:00
|
|
|
xfs_forget_acl(inode, name);
|
2020-02-27 09:30:33 +08:00
|
|
|
kfree(args.value);
|
2015-11-03 09:53:54 +08:00
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2020-02-27 09:30:31 +08:00
|
|
|
int
|
|
|
|
xfs_ioc_attrmulti_one(
|
|
|
|
struct file *parfilp,
|
|
|
|
struct inode *inode,
|
|
|
|
uint32_t opcode,
|
|
|
|
void __user *uname,
|
|
|
|
void __user *value,
|
|
|
|
uint32_t *len,
|
|
|
|
uint32_t flags)
|
|
|
|
{
|
|
|
|
unsigned char *name;
|
|
|
|
int error;
|
|
|
|
|
2020-02-27 09:30:42 +08:00
|
|
|
if ((flags & XFS_IOC_ATTR_ROOT) && (flags & XFS_IOC_ATTR_SECURE))
|
2020-02-27 09:30:31 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
name = strndup_user(uname, MAXNAMELEN);
|
|
|
|
if (IS_ERR(name))
|
|
|
|
return PTR_ERR(name);
|
|
|
|
|
|
|
|
switch (opcode) {
|
|
|
|
case ATTR_OP_GET:
|
|
|
|
error = xfs_attrmulti_attr_get(inode, name, value, len, flags);
|
|
|
|
break;
|
|
|
|
case ATTR_OP_REMOVE:
|
|
|
|
value = NULL;
|
|
|
|
*len = 0;
|
2021-04-21 06:54:36 +08:00
|
|
|
fallthrough;
|
2020-02-27 09:30:31 +08:00
|
|
|
case ATTR_OP_SET:
|
|
|
|
error = mnt_want_write_file(parfilp);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
error = xfs_attrmulti_attr_set(inode, name, value, *len, flags);
|
|
|
|
mnt_drop_write_file(parfilp);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(name);
|
|
|
|
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;
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
error = 0;
|
|
|
|
for (i = 0; i < am_hreq.opcount; i++) {
|
2020-02-27 09:30:31 +08:00
|
|
|
ops[i].am_error = xfs_ioc_attrmulti_one(parfilp,
|
|
|
|
d_inode(dentry), ops[i].am_opcode,
|
|
|
|
ops[i].am_attrname, ops[i].am_attrvalue,
|
|
|
|
&ops[i].am_length, ops[i].am_flags);
|
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(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;
|
2021-01-21 21:19:58 +08:00
|
|
|
error = xfs_vn_setattr_size(file_mnt_user_ns(filp), 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(
|
2021-01-21 21:19:58 +08:00
|
|
|
struct file *file,
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int cmd,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
2021-01-21 21:19:58 +08:00
|
|
|
struct xfs_mount *mp = XFS_I(file_inode(file))->i_mount;
|
2019-07-03 00:39:40 +08:00
|
|
|
struct xfs_fsop_bulkreq bulkreq;
|
|
|
|
struct xfs_ibulk breq = {
|
|
|
|
.mp = mp,
|
2021-01-21 21:19:58 +08:00
|
|
|
.mnt_userns = file_mnt_user_ns(file),
|
2019-07-03 00:39:40 +08:00
|
|
|
.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;
|
|
|
|
|
2021-08-19 09:46:53 +08:00
|
|
|
if (xfs_is_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(
|
2021-01-21 21:19:58 +08:00
|
|
|
struct file *file,
|
2019-07-04 11:36:27 +08:00
|
|
|
unsigned int cmd,
|
|
|
|
struct xfs_bulkstat_req __user *arg)
|
|
|
|
{
|
2021-01-21 21:19:58 +08:00
|
|
|
struct xfs_mount *mp = XFS_I(file_inode(file))->i_mount;
|
2019-07-04 11:36:27 +08:00
|
|
|
struct xfs_bulk_ireq hdr;
|
|
|
|
struct xfs_ibulk breq = {
|
|
|
|
.mp = mp,
|
2021-01-21 21:19:58 +08:00
|
|
|
.mnt_userns = file_mnt_user_ns(file),
|
2019-07-04 11:36:27 +08:00
|
|
|
};
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
2021-08-19 09:46:53 +08:00
|
|
|
if (xfs_is_shutdown(mp))
|
2019-07-04 11:36:27 +08:00
|
|
|
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;
|
|
|
|
|
2021-08-19 09:46:53 +08:00
|
|
|
if (xfs_is_shutdown(mp))
|
2019-07-04 11:36:28 +08:00
|
|
|
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
|
|
|
|
2021-08-19 09:46:54 +08:00
|
|
|
xfs_fs_geometry(mp, &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.
|
|
|
|
*/
|
|
|
|
|
2019-07-01 23:25:35 +08:00
|
|
|
static void
|
|
|
|
xfs_fill_fsxattr(
|
|
|
|
struct xfs_inode *ip,
|
2021-04-07 20:36:43 +08:00
|
|
|
int whichfork,
|
|
|
|
struct fileattr *fa)
|
2007-10-11 15:47:00 +08:00
|
|
|
{
|
2021-03-30 02:11:42 +08:00
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
2021-04-07 20:36:43 +08:00
|
|
|
struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork);
|
2020-05-19 01:27:22 +08:00
|
|
|
|
2021-04-07 20:36:43 +08:00
|
|
|
fileattr_fill_xflags(fa, xfs_ip2xflags(ip));
|
2021-03-30 02:11:42 +08:00
|
|
|
|
2021-07-13 03:58:51 +08:00
|
|
|
if (ip->i_diflags & XFS_DIFLAG_EXTSIZE) {
|
|
|
|
fa->fsx_extsize = XFS_FSB_TO_B(mp, ip->i_extsize);
|
|
|
|
} else if (ip->i_diflags & XFS_DIFLAG_EXTSZINHERIT) {
|
|
|
|
/*
|
|
|
|
* Don't let a misaligned extent size hint on a directory
|
|
|
|
* escape to userspace if it won't pass the setattr checks
|
|
|
|
* later.
|
|
|
|
*/
|
|
|
|
if ((ip->i_diflags & XFS_DIFLAG_RTINHERIT) &&
|
|
|
|
ip->i_extsize % mp->m_sb.sb_rextsize > 0) {
|
|
|
|
fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE |
|
|
|
|
FS_XFLAG_EXTSZINHERIT);
|
|
|
|
fa->fsx_extsize = 0;
|
|
|
|
} else {
|
|
|
|
fa->fsx_extsize = XFS_FSB_TO_B(mp, ip->i_extsize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-30 02:11:45 +08:00
|
|
|
if (ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE)
|
2021-03-30 02:11:43 +08:00
|
|
|
fa->fsx_cowextsize = XFS_FSB_TO_B(mp, ip->i_cowextsize);
|
2021-03-30 02:11:39 +08:00
|
|
|
fa->fsx_projid = ip->i_projid;
|
2021-04-14 02:15:12 +08:00
|
|
|
if (ifp && !xfs_need_iread_extents(ifp))
|
2020-05-19 01:27:22 +08:00
|
|
|
fa->fsx_nextents = xfs_iext_count(ifp);
|
|
|
|
else
|
|
|
|
fa->fsx_nextents = xfs_ifork_nextents(ifp);
|
2019-07-01 23:25:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
STATIC int
|
2021-04-07 20:36:43 +08:00
|
|
|
xfs_ioc_fsgetxattra(
|
2019-07-01 23:25:35 +08:00
|
|
|
xfs_inode_t *ip,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
2021-04-07 20:36:43 +08:00
|
|
|
struct fileattr fa;
|
2019-07-01 23:25:35 +08:00
|
|
|
|
|
|
|
xfs_ilock(ip, XFS_ILOCK_SHARED);
|
2021-04-07 20:36:43 +08:00
|
|
|
xfs_fill_fsxattr(ip, XFS_ATTR_FORK, &fa);
|
|
|
|
xfs_iunlock(ip, XFS_ILOCK_SHARED);
|
|
|
|
|
|
|
|
return copy_fsxattr_to_user(&fa, arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
xfs_fileattr_get(
|
|
|
|
struct dentry *dentry,
|
|
|
|
struct fileattr *fa)
|
|
|
|
{
|
|
|
|
struct xfs_inode *ip = XFS_I(d_inode(dentry));
|
|
|
|
|
|
|
|
if (d_is_special(dentry))
|
|
|
|
return -ENOTTY;
|
|
|
|
|
|
|
|
xfs_ilock(ip, XFS_ILOCK_SHARED);
|
|
|
|
xfs_fill_fsxattr(ip, XFS_DATA_FORK, fa);
|
2007-10-11 15:47:00 +08:00
|
|
|
xfs_iunlock(ip, XFS_ILOCK_SHARED);
|
|
|
|
|
|
|
|
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 =
|
2021-03-30 02:11:44 +08:00
|
|
|
(ip->i_diflags & XFS_DIFLAG_PREALLOC);
|
2017-09-02 23:21:20 +08:00
|
|
|
|
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 =
|
2021-03-30 02:11:45 +08:00
|
|
|
(ip->i_diflags2 & (XFS_DIFLAG2_REFLINK |
|
|
|
|
XFS_DIFLAG2_BIGTIME));
|
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
|
|
|
}
|
|
|
|
|
2015-02-02 07:14:25 +08:00
|
|
|
static int
|
|
|
|
xfs_ioctl_setattr_xflags(
|
|
|
|
struct xfs_trans *tp,
|
|
|
|
struct xfs_inode *ip,
|
2021-04-07 20:36:43 +08:00
|
|
|
struct fileattr *fa)
|
2015-02-02 07:14:25 +08:00
|
|
|
{
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
2021-03-30 02:11:45 +08:00
|
|
|
uint64_t i_flags2;
|
2015-02-02 07:14:25 +08:00
|
|
|
|
|
|
|
/* Can't change realtime flag if any extents are allocated. */
|
2020-05-19 01:27:22 +08:00
|
|
|
if ((ip->i_df.if_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 ||
|
2021-03-30 02:11:41 +08:00
|
|
|
(ip->i_extsize % mp->m_sb.sb_rextsize))
|
2015-02-02 07:14:25 +08:00
|
|
|
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))
|
2021-03-30 02:11:45 +08:00
|
|
|
ip->i_diflags2 &= ~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. */
|
2021-03-30 02:11:45 +08:00
|
|
|
i_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
|
2021-08-19 09:46:37 +08:00
|
|
|
if (i_flags2 && !xfs_has_v3inodes(mp))
|
2017-09-02 23:21:20 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2021-03-30 02:11:44 +08:00
|
|
|
ip->i_diflags = xfs_flags2diflags(ip, fa->fsx_xflags);
|
2021-03-30 02:11:45 +08:00
|
|
|
ip->i_diflags2 = i_flags2;
|
2017-09-02 23:21:20 +08:00
|
|
|
|
2020-05-05 00:02:43 +08:00
|
|
|
xfs_diflags_to_iflags(ip, false);
|
2015-02-02 07:14:25 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-04-30 22:41:38 +08:00
|
|
|
static void
|
|
|
|
xfs_ioctl_setattr_prepare_dax(
|
2016-03-01 06:41:33 +08:00
|
|
|
struct xfs_inode *ip,
|
2021-04-07 20:36:43 +08:00
|
|
|
struct fileattr *fa)
|
2016-03-01 06:41:33 +08:00
|
|
|
{
|
2020-04-30 22:41:38 +08:00
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
|
|
|
struct inode *inode = VFS_I(ip);
|
2016-03-01 06:41:33 +08:00
|
|
|
|
2018-06-01 06:07:47 +08:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
2020-04-30 22:41:38 +08:00
|
|
|
return;
|
2016-03-01 06:41:33 +08:00
|
|
|
|
2021-08-19 09:46:52 +08:00
|
|
|
if (xfs_has_dax_always(mp) || xfs_has_dax_never(mp))
|
2020-04-30 22:41:38 +08:00
|
|
|
return;
|
2016-03-01 06:41:33 +08:00
|
|
|
|
2020-04-30 22:41:38 +08:00
|
|
|
if (((fa->fsx_xflags & FS_XFLAG_DAX) &&
|
2021-03-30 02:11:45 +08:00
|
|
|
!(ip->i_diflags2 & XFS_DIFLAG2_DAX)) ||
|
2020-04-30 22:41:38 +08:00
|
|
|
(!(fa->fsx_xflags & FS_XFLAG_DAX) &&
|
2021-03-30 02:11:45 +08:00
|
|
|
(ip->i_diflags2 & XFS_DIFLAG2_DAX)))
|
2020-04-30 22:41:38 +08:00
|
|
|
d_mark_dontcache(inode);
|
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.
|
|
|
|
*/
|
|
|
|
static struct xfs_trans *
|
|
|
|
xfs_ioctl_setattr_get_trans(
|
2021-04-07 20:36:43 +08:00
|
|
|
struct xfs_inode *ip,
|
2021-01-30 03:32:09 +08:00
|
|
|
struct xfs_dquot *pdqp)
|
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
|
|
|
|
2021-08-19 09:46:52 +08:00
|
|
|
if (xfs_is_readonly(mp))
|
2021-01-30 03:32:09 +08:00
|
|
|
goto out_error;
|
2016-03-01 06:41:33 +08:00
|
|
|
error = -EIO;
|
2021-08-19 09:46:53 +08:00
|
|
|
if (xfs_is_shutdown(mp))
|
2021-01-30 03:32:09 +08:00
|
|
|
goto out_error;
|
2015-02-02 07:15:35 +08:00
|
|
|
|
2021-01-30 03:32:09 +08:00
|
|
|
error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp,
|
|
|
|
capable(CAP_FOWNER), &tp);
|
2015-02-02 07:15:35 +08:00
|
|
|
if (error)
|
2021-01-30 03:32:09 +08:00
|
|
|
goto out_error;
|
2015-02-02 07:15:35 +08:00
|
|
|
|
2021-08-19 09:46:52 +08:00
|
|
|
if (xfs_has_wsync(mp))
|
2015-02-02 07:15:35 +08:00
|
|
|
xfs_trans_set_sync(tp);
|
|
|
|
|
|
|
|
return tp;
|
|
|
|
|
2021-01-30 03:32:09 +08:00
|
|
|
out_error:
|
2015-02-02 07:15:35 +08:00
|
|
|
return ERR_PTR(error);
|
|
|
|
}
|
|
|
|
|
2015-02-02 07:26:26 +08:00
|
|
|
/*
|
2021-05-13 03:49:19 +08:00
|
|
|
* Validate a proposed extent size hint. For regular files, the hint can only
|
|
|
|
* be changed if no extents are allocated.
|
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,
|
2021-04-07 20:36:43 +08:00
|
|
|
struct fileattr *fa)
|
2015-02-02 07:22:20 +08:00
|
|
|
{
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
2021-05-13 03:49:19 +08:00
|
|
|
xfs_failaddr_t failaddr;
|
|
|
|
uint16_t new_diflags;
|
2015-02-02 07:26:26 +08:00
|
|
|
|
2021-04-07 20:36:43 +08:00
|
|
|
if (!fa->fsx_valid)
|
|
|
|
return 0;
|
|
|
|
|
2020-05-19 01:27:22 +08:00
|
|
|
if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_df.if_nextents &&
|
2021-05-13 03:49:19 +08:00
|
|
|
XFS_FSB_TO_B(mp, ip->i_extsize) != fa->fsx_extsize)
|
2015-02-02 07:22:20 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2021-05-13 03:49:19 +08:00
|
|
|
if (fa->fsx_extsize & mp->m_blockmask)
|
2019-07-01 23:25:36 +08:00
|
|
|
return -EINVAL;
|
2015-02-02 07:22:20 +08:00
|
|
|
|
2021-05-13 03:49:19 +08:00
|
|
|
new_diflags = xfs_flags2diflags(ip, fa->fsx_xflags);
|
2015-02-02 07:26:26 +08:00
|
|
|
|
xfs: validate extsz hints against rt extent size when rtinherit is set
The RTINHERIT bit can be set on a directory so that newly created
regular files will have the REALTIME bit set to store their data on the
realtime volume. If an extent size hint (and EXTSZINHERIT) are set on
the directory, the hint will also be copied into the new file.
As pointed out in previous patches, for realtime files we require the
extent size hint be an integer multiple of the realtime extent, but we
don't perform the same validation on a directory with both RTINHERIT and
EXTSZINHERIT set, even though the only use-case of that combination is
to propagate extent size hints into new realtime files. This leads to
inode corruption errors when the bad values are propagated.
Because there may be existing filesystems with such a configuration, we
cannot simply amend the inode verifier to trip on these directories and
call it a day because that will cause previously "working" filesystems
to start throwing errors abruptly. Note that it's valid to have
directories with rtinherit set even if there is no realtime volume, in
which case the problem does not manifest because rtinherit is ignored if
there's no realtime device; and it's possible that someone set the flag,
crashed, repaired the filesystem (which clears the hint on the realtime
file) and continued.
Therefore, mitigate this issue in several ways: First, if we try to
write out an inode with both rtinherit/extszinherit set and an unaligned
extent size hint, turn off the hint to correct the error. Second, if
someone tries to misconfigure a directory via the fssetxattr ioctl, fail
the ioctl. Third, reverify both extent size hint values when we
propagate heritable inode attributes from parent to child, to prevent
misconfigurations from spreading.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
2021-05-13 03:51:26 +08:00
|
|
|
/*
|
2021-07-13 03:58:50 +08:00
|
|
|
* Inode verifiers do not check that the extent size hint is an integer
|
|
|
|
* multiple of the rt extent size on a directory with both rtinherit
|
|
|
|
* and extszinherit flags set. Don't let sysadmins misconfigure
|
|
|
|
* directories.
|
xfs: validate extsz hints against rt extent size when rtinherit is set
The RTINHERIT bit can be set on a directory so that newly created
regular files will have the REALTIME bit set to store their data on the
realtime volume. If an extent size hint (and EXTSZINHERIT) are set on
the directory, the hint will also be copied into the new file.
As pointed out in previous patches, for realtime files we require the
extent size hint be an integer multiple of the realtime extent, but we
don't perform the same validation on a directory with both RTINHERIT and
EXTSZINHERIT set, even though the only use-case of that combination is
to propagate extent size hints into new realtime files. This leads to
inode corruption errors when the bad values are propagated.
Because there may be existing filesystems with such a configuration, we
cannot simply amend the inode verifier to trip on these directories and
call it a day because that will cause previously "working" filesystems
to start throwing errors abruptly. Note that it's valid to have
directories with rtinherit set even if there is no realtime volume, in
which case the problem does not manifest because rtinherit is ignored if
there's no realtime device; and it's possible that someone set the flag,
crashed, repaired the filesystem (which clears the hint on the realtime
file) and continued.
Therefore, mitigate this issue in several ways: First, if we try to
write out an inode with both rtinherit/extszinherit set and an unaligned
extent size hint, turn off the hint to correct the error. Second, if
someone tries to misconfigure a directory via the fssetxattr ioctl, fail
the ioctl. Third, reverify both extent size hint values when we
propagate heritable inode attributes from parent to child, to prevent
misconfigurations from spreading.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
2021-05-13 03:51:26 +08:00
|
|
|
*/
|
|
|
|
if ((new_diflags & XFS_DIFLAG_RTINHERIT) &&
|
|
|
|
(new_diflags & XFS_DIFLAG_EXTSZINHERIT)) {
|
|
|
|
unsigned int rtextsize_bytes;
|
|
|
|
|
|
|
|
rtextsize_bytes = XFS_FSB_TO_B(mp, mp->m_sb.sb_rextsize);
|
|
|
|
if (fa->fsx_extsize % rtextsize_bytes)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-05-13 03:49:19 +08:00
|
|
|
failaddr = xfs_inode_validate_extsize(ip->i_mount,
|
|
|
|
XFS_B_TO_FSB(mp, fa->fsx_extsize),
|
|
|
|
VFS_I(ip)->i_mode, new_diflags);
|
|
|
|
return failaddr != NULL ? -EINVAL : 0;
|
2015-02-02 07:22:20 +08:00
|
|
|
}
|
|
|
|
|
2016-10-04 00:11:43 +08:00
|
|
|
static int
|
|
|
|
xfs_ioctl_setattr_check_cowextsize(
|
|
|
|
struct xfs_inode *ip,
|
2021-04-07 20:36:43 +08:00
|
|
|
struct fileattr *fa)
|
2016-10-04 00:11:43 +08:00
|
|
|
{
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
2021-05-13 03:49:19 +08:00
|
|
|
xfs_failaddr_t failaddr;
|
|
|
|
uint64_t new_diflags2;
|
|
|
|
uint16_t new_diflags;
|
2016-10-04 00:11:43 +08:00
|
|
|
|
2021-04-07 20:36:43 +08:00
|
|
|
if (!fa->fsx_valid)
|
|
|
|
return 0;
|
|
|
|
|
2021-05-13 03:49:19 +08:00
|
|
|
if (fa->fsx_cowextsize & mp->m_blockmask)
|
2019-07-01 23:25:36 +08:00
|
|
|
return -EINVAL;
|
2016-10-04 00:11:43 +08:00
|
|
|
|
2021-05-13 03:49:19 +08:00
|
|
|
new_diflags = xfs_flags2diflags(ip, fa->fsx_xflags);
|
|
|
|
new_diflags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
|
2016-10-04 00:11:43 +08:00
|
|
|
|
2021-05-13 03:49:19 +08:00
|
|
|
failaddr = xfs_inode_validate_cowextsize(ip->i_mount,
|
|
|
|
XFS_B_TO_FSB(mp, fa->fsx_cowextsize),
|
|
|
|
VFS_I(ip)->i_mode, new_diflags, new_diflags2);
|
|
|
|
return failaddr != NULL ? -EINVAL : 0;
|
2016-10-04 00:11:43 +08:00
|
|
|
}
|
|
|
|
|
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,
|
2021-04-07 20:36:43 +08:00
|
|
|
struct fileattr *fa)
|
2015-02-02 07:22:53 +08:00
|
|
|
{
|
2021-04-07 20:36:43 +08:00
|
|
|
if (!fa->fsx_valid)
|
|
|
|
return 0;
|
|
|
|
|
2021-08-19 09:46:37 +08:00
|
|
|
/* Disallow 32bit project ids if 32bit IDs are not enabled. */
|
2017-06-17 02:00:05 +08:00
|
|
|
if (fa->fsx_projid > (uint16_t)-1 &&
|
2021-08-19 09:46:37 +08:00
|
|
|
!xfs_has_projid32(ip->i_mount))
|
2015-02-02 07:22:53 +08:00
|
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
|
|
}
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2021-04-07 20:36:43 +08:00
|
|
|
int
|
|
|
|
xfs_fileattr_set(
|
|
|
|
struct user_namespace *mnt_userns,
|
|
|
|
struct dentry *dentry,
|
|
|
|
struct fileattr *fa)
|
2008-07-18 15:13:20 +08:00
|
|
|
{
|
2021-04-07 20:36:43 +08:00
|
|
|
struct xfs_inode *ip = XFS_I(d_inode(dentry));
|
2008-07-18 15:13:20 +08:00
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
|
|
|
struct xfs_trans *tp;
|
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;
|
2021-01-30 09:14:33 +08:00
|
|
|
int error;
|
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
|
|
|
|
2021-04-07 20:36:43 +08:00
|
|
|
if (d_is_special(dentry))
|
|
|
|
return -ENOTTY;
|
|
|
|
|
|
|
|
if (!fa->fsx_valid) {
|
|
|
|
if (fa->flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL |
|
|
|
|
FS_NOATIME_FL | FS_NODUMP_FL |
|
|
|
|
FS_SYNC_FL | FS_DAX_FL | FS_PROJINHERIT_FL))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2021-01-30 09:14:33 +08:00
|
|
|
error = xfs_ioctl_setattr_check_projid(ip, fa);
|
|
|
|
if (error)
|
|
|
|
return error;
|
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.
|
|
|
|
*/
|
2021-04-07 20:36:43 +08:00
|
|
|
if (fa->fsx_valid && XFS_IS_QUOTA_ON(mp)) {
|
2021-01-30 09:14:33 +08:00
|
|
|
error = xfs_qm_vop_dqalloc(ip, VFS_I(ip)->i_uid,
|
2020-02-22 00:31:27 +08:00
|
|
|
VFS_I(ip)->i_gid, fa->fsx_projid,
|
2020-04-23 12:54:29 +08:00
|
|
|
XFS_QMOPT_PQUOTA, NULL, NULL, &pdqp);
|
2021-01-30 09:14:33 +08:00
|
|
|
if (error)
|
|
|
|
return error;
|
2008-07-18 15:13:20 +08:00
|
|
|
}
|
|
|
|
|
2020-04-30 22:41:38 +08:00
|
|
|
xfs_ioctl_setattr_prepare_dax(ip, fa);
|
2016-03-01 06:41:33 +08:00
|
|
|
|
2021-04-07 20:36:43 +08:00
|
|
|
tp = xfs_ioctl_setattr_get_trans(ip, pdqp);
|
2015-02-02 07:15:35 +08:00
|
|
|
if (IS_ERR(tp)) {
|
2021-01-30 09:14:33 +08:00
|
|
|
error = PTR_ERR(tp);
|
2015-02-02 07:15:35 +08:00
|
|
|
goto error_free_dquots;
|
2008-07-18 15:13:20 +08:00
|
|
|
}
|
|
|
|
|
2021-01-30 09:14:33 +08:00
|
|
|
error = xfs_ioctl_setattr_check_extsize(ip, fa);
|
|
|
|
if (error)
|
2015-02-02 07:22:20 +08:00
|
|
|
goto error_trans_cancel;
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2021-01-30 09:14:33 +08:00
|
|
|
error = xfs_ioctl_setattr_check_cowextsize(ip, fa);
|
|
|
|
if (error)
|
2016-10-04 00:11:43 +08:00
|
|
|
goto error_trans_cancel;
|
|
|
|
|
2021-01-30 09:14:33 +08:00
|
|
|
error = xfs_ioctl_setattr_xflags(tp, ip, fa);
|
|
|
|
if (error)
|
2015-02-02 07:22:20 +08:00
|
|
|
goto error_trans_cancel;
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2021-04-07 20:36:43 +08:00
|
|
|
if (!fa->fsx_valid)
|
|
|
|
goto skip_xattr;
|
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)) &&
|
2021-01-21 21:19:58 +08:00
|
|
|
!capable_wrt_inode_uidgid(mnt_userns, 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 */
|
2021-03-30 02:11:39 +08:00
|
|
|
if (ip->i_projid != fa->fsx_projid) {
|
2021-08-07 02:05:37 +08:00
|
|
|
if (XFS_IS_PQUOTA_ON(mp)) {
|
2015-02-02 07:16:25 +08:00
|
|
|
olddquot = xfs_qm_vop_chown(tp, ip,
|
|
|
|
&ip->i_pdquot, pdqp);
|
|
|
|
}
|
2021-03-30 02:11:39 +08:00
|
|
|
ip->i_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.
|
|
|
|
*/
|
2021-03-30 02:11:44 +08:00
|
|
|
if (ip->i_diflags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT))
|
2021-03-30 02:11:43 +08:00
|
|
|
ip->i_extsize = XFS_B_TO_FSB(mp, fa->fsx_extsize);
|
2016-10-04 00:11:43 +08:00
|
|
|
else
|
2021-03-30 02:11:41 +08:00
|
|
|
ip->i_extsize = 0;
|
2021-03-30 02:11:43 +08:00
|
|
|
|
2021-08-19 09:46:37 +08:00
|
|
|
if (xfs_has_v3inodes(mp)) {
|
2021-03-30 02:11:45 +08:00
|
|
|
if (ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE)
|
2021-03-30 02:11:43 +08:00
|
|
|
ip->i_cowextsize = XFS_B_TO_FSB(mp, fa->fsx_cowextsize);
|
|
|
|
else
|
|
|
|
ip->i_cowextsize = 0;
|
|
|
|
}
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2021-04-07 20:36:43 +08:00
|
|
|
skip_xattr:
|
2021-01-30 09:14:33 +08:00
|
|
|
error = 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);
|
2013-07-11 13:00:40 +08:00
|
|
|
xfs_qm_dqrele(pdqp);
|
2008-07-18 15:13:20 +08:00
|
|
|
|
2021-01-30 09:14:33 +08:00
|
|
|
return error;
|
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:
|
2013-07-11 13:00:40 +08:00
|
|
|
xfs_qm_dqrele(pdqp);
|
2021-01-30 09:14:33 +08:00
|
|
|
return error;
|
2008-07-18 15:13:20 +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;
|
2021-04-21 06:54:36 +08:00
|
|
|
fallthrough;
|
2017-10-18 05:16:19 +08:00
|
|
|
case XFS_IOC_GETBMAP:
|
|
|
|
/* 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
|
|
|
|
2020-09-02 02:47:12 +08:00
|
|
|
buf = kvzalloc(bmx.bmv_count * sizeof(*buf), GFP_KERNEL);
|
2017-10-18 05:16:19 +08:00
|
|
|
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
|
|
|
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
|
|
|
{
|
|
|
|
struct xfs_fsmap_head xhead = {0};
|
|
|
|
struct fsmap_head head;
|
2020-10-02 01:56:07 +08:00
|
|
|
struct fsmap *recs;
|
|
|
|
unsigned int count;
|
|
|
|
__u32 last_flags = 0;
|
|
|
|
bool done = false;
|
2017-03-29 05:56:37 +08:00
|
|
|
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;
|
|
|
|
|
2020-10-02 01:56:07 +08:00
|
|
|
/*
|
|
|
|
* Use an internal memory buffer so that we don't have to copy fsmap
|
|
|
|
* data to userspace while holding locks. Start by trying to allocate
|
|
|
|
* up to 128k for the buffer, but fall back to a single page if needed.
|
|
|
|
*/
|
|
|
|
count = min_t(unsigned int, head.fmh_count,
|
|
|
|
131072 / sizeof(struct fsmap));
|
|
|
|
recs = kvzalloc(count * sizeof(struct fsmap), GFP_KERNEL);
|
|
|
|
if (!recs) {
|
|
|
|
count = min_t(unsigned int, head.fmh_count,
|
|
|
|
PAGE_SIZE / sizeof(struct fsmap));
|
|
|
|
recs = kvzalloc(count * sizeof(struct fsmap), GFP_KERNEL);
|
|
|
|
if (!recs)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2017-03-29 05:56:37 +08:00
|
|
|
xhead.fmh_iflags = head.fmh_iflags;
|
|
|
|
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]);
|
|
|
|
|
2020-10-02 01:56:07 +08:00
|
|
|
head.fmh_entries = 0;
|
|
|
|
do {
|
|
|
|
struct fsmap __user *user_recs;
|
|
|
|
struct fsmap *last_rec;
|
|
|
|
|
|
|
|
user_recs = &arg->fmh_recs[head.fmh_entries];
|
|
|
|
xhead.fmh_entries = 0;
|
|
|
|
xhead.fmh_count = min_t(unsigned int, count,
|
|
|
|
head.fmh_count - head.fmh_entries);
|
|
|
|
|
|
|
|
/* Run query, record how many entries we got. */
|
|
|
|
error = xfs_getfsmap(ip->i_mount, &xhead, recs);
|
|
|
|
switch (error) {
|
|
|
|
case 0:
|
|
|
|
/*
|
|
|
|
* There are no more records in the result set. Copy
|
|
|
|
* whatever we got to userspace and break out.
|
|
|
|
*/
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
case -ECANCELED:
|
|
|
|
/*
|
|
|
|
* The internal memory buffer is full. Copy whatever
|
|
|
|
* records we got to userspace and go again if we have
|
|
|
|
* not yet filled the userspace buffer.
|
|
|
|
*/
|
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
head.fmh_entries += xhead.fmh_entries;
|
|
|
|
head.fmh_oflags = xhead.fmh_oflags;
|
2017-03-29 05:56:37 +08:00
|
|
|
|
2020-10-02 01:56:07 +08:00
|
|
|
/*
|
|
|
|
* If the caller wanted a record count or there aren't any
|
|
|
|
* new records to return, we're done.
|
|
|
|
*/
|
|
|
|
if (head.fmh_count == 0 || xhead.fmh_entries == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Copy all the records we got out to userspace. */
|
|
|
|
if (copy_to_user(user_recs, recs,
|
|
|
|
xhead.fmh_entries * sizeof(struct fsmap))) {
|
|
|
|
error = -EFAULT;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remember the last record flags we copied to userspace. */
|
|
|
|
last_rec = &recs[xhead.fmh_entries - 1];
|
|
|
|
last_flags = last_rec->fmr_flags;
|
|
|
|
|
|
|
|
/* Set up the low key for the next iteration. */
|
|
|
|
xfs_fsmap_to_internal(&xhead.fmh_keys[0], last_rec);
|
|
|
|
trace_xfs_getfsmap_low_key(ip->i_mount, &xhead.fmh_keys[0]);
|
|
|
|
} while (!done && head.fmh_entries < head.fmh_count);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there are no more records in the query result set and we're not
|
|
|
|
* in counting mode, mark the last record returned with the LAST flag.
|
|
|
|
*/
|
|
|
|
if (done && head.fmh_count > 0 && head.fmh_entries > 0) {
|
|
|
|
struct fsmap __user *user_rec;
|
|
|
|
|
|
|
|
last_flags |= FMR_OF_LAST;
|
|
|
|
user_rec = &arg->fmh_recs[head.fmh_entries - 1];
|
|
|
|
|
|
|
|
if (copy_to_user(&user_rec->fmr_flags, &last_flags,
|
|
|
|
sizeof(last_flags))) {
|
|
|
|
error = -EFAULT;
|
|
|
|
goto out_free;
|
|
|
|
}
|
2017-03-29 05:56:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* copy back header */
|
2020-10-02 01:56:07 +08:00
|
|
|
if (copy_to_user(arg, &head, sizeof(struct fsmap_head))) {
|
|
|
|
error = -EFAULT;
|
|
|
|
goto out_free;
|
|
|
|
}
|
2017-03-29 05:56:37 +08:00
|
|
|
|
2020-10-02 01:56:07 +08:00
|
|
|
out_free:
|
|
|
|
kmem_free(recs);
|
|
|
|
return error;
|
2017-03-29 05:56:37 +08:00
|
|
|
}
|
|
|
|
|
2017-10-18 12:37:34 +08:00
|
|
|
STATIC int
|
|
|
|
xfs_ioc_scrub_metadata(
|
2021-03-23 00:51:51 +08:00
|
|
|
struct file *file,
|
2017-10-18 12:37:34 +08:00
|
|
|
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;
|
|
|
|
|
2021-03-23 00:51:51 +08:00
|
|
|
error = xfs_scrub_metadata(file, &scrub);
|
2017-10-18 12:37:34 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-08-19 09:46:53 +08:00
|
|
|
if (xfs_is_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;
|
|
|
|
}
|
|
|
|
|
2020-05-22 04:08:46 +08:00
|
|
|
static inline int
|
|
|
|
xfs_fs_eofblocks_from_user(
|
|
|
|
struct xfs_fs_eofblocks *src,
|
2021-06-08 00:34:51 +08:00
|
|
|
struct xfs_icwalk *dst)
|
2020-05-22 04:08:46 +08:00
|
|
|
{
|
|
|
|
if (src->eof_version != XFS_EOFBLOCKS_VERSION)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (src->eof_flags & ~XFS_EOF_FLAGS_VALID)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (memchr_inv(&src->pad32, 0, sizeof(src->pad32)) ||
|
|
|
|
memchr_inv(src->pad64, 0, sizeof(src->pad64)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2021-06-08 00:34:51 +08:00
|
|
|
dst->icw_flags = 0;
|
2021-06-08 00:34:51 +08:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_SYNC)
|
2021-06-08 00:34:51 +08:00
|
|
|
dst->icw_flags |= XFS_ICWALK_FLAG_SYNC;
|
2021-06-08 00:34:51 +08:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_UID)
|
2021-06-08 00:34:51 +08:00
|
|
|
dst->icw_flags |= XFS_ICWALK_FLAG_UID;
|
2021-06-08 00:34:51 +08:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_GID)
|
2021-06-08 00:34:51 +08:00
|
|
|
dst->icw_flags |= XFS_ICWALK_FLAG_GID;
|
2021-06-08 00:34:51 +08:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_PRID)
|
2021-06-08 00:34:51 +08:00
|
|
|
dst->icw_flags |= XFS_ICWALK_FLAG_PRID;
|
2021-06-08 00:34:51 +08:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_MINFILESIZE)
|
2021-06-08 00:34:51 +08:00
|
|
|
dst->icw_flags |= XFS_ICWALK_FLAG_MINFILESIZE;
|
2021-06-08 00:34:51 +08:00
|
|
|
|
2021-06-08 00:34:51 +08:00
|
|
|
dst->icw_prid = src->eof_prid;
|
|
|
|
dst->icw_min_file_size = src->eof_min_file_size;
|
2020-05-22 04:08:46 +08:00
|
|
|
|
2021-06-08 00:34:51 +08:00
|
|
|
dst->icw_uid = INVALID_UID;
|
2020-05-22 04:08:46 +08:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_UID) {
|
2021-06-08 00:34:51 +08:00
|
|
|
dst->icw_uid = make_kuid(current_user_ns(), src->eof_uid);
|
|
|
|
if (!uid_valid(dst->icw_uid))
|
2020-05-22 04:08:46 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-06-08 00:34:51 +08:00
|
|
|
dst->icw_gid = INVALID_GID;
|
2020-05-22 04:08:46 +08:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_GID) {
|
2021-06-08 00:34:51 +08:00
|
|
|
dst->icw_gid = make_kgid(current_user_ns(), src->eof_gid);
|
|
|
|
if (!gid_valid(dst->icw_gid))
|
2020-05-22 04:08:46 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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:
|
2021-01-21 21:19:58 +08:00
|
|
|
return xfs_ioc_fsbulkstat(filp, cmd, arg);
|
2008-04-18 09:44:03 +08:00
|
|
|
|
2019-07-04 11:36:27 +08:00
|
|
|
case XFS_IOC_BULKSTAT:
|
2021-01-21 21:19:58 +08:00
|
|
|
return xfs_ioc_bulkstat(filp, 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_FSGETXATTRA:
|
2021-04-07 20:36:43 +08:00
|
|
|
return xfs_ioc_fsgetxattra(ip, 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:
|
2021-03-23 00:51:51 +08:00
|
|
|
return xfs_ioc_scrub_metadata(filp, arg);
|
2017-10-18 12:37:34 +08:00
|
|
|
|
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;
|
|
|
|
|
2021-08-19 09:46:52 +08:00
|
|
|
if (xfs_is_readonly(mp))
|
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: {
|
2021-02-03 10:24:06 +08:00
|
|
|
struct xfs_growfs_data in;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
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: {
|
2021-02-03 10:24:06 +08:00
|
|
|
struct xfs_growfs_log in;
|
2008-04-18 09:44:03 +08:00
|
|
|
|
|
|
|
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: {
|
2021-06-08 00:34:51 +08:00
|
|
|
struct xfs_fs_eofblocks eofb;
|
|
|
|
struct xfs_icwalk icw;
|
2012-11-08 01:21:12 +08:00
|
|
|
|
2013-08-16 02:08:03 +08:00
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
2021-08-19 09:46:52 +08:00
|
|
|
if (xfs_is_readonly(mp))
|
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
|
|
|
|
2021-06-08 00:34:51 +08:00
|
|
|
error = xfs_fs_eofblocks_from_user(&eofb, &icw);
|
2013-08-16 02:08:02 +08:00
|
|
|
if (error)
|
2014-06-25 12:58:08 +08:00
|
|
|
return error;
|
2012-11-08 01:21:12 +08:00
|
|
|
|
2021-06-08 00:34:51 +08:00
|
|
|
trace_xfs_ioc_free_eofblocks(mp, &icw, _RET_IP_);
|
2021-01-23 08:48:38 +08:00
|
|
|
|
2020-04-13 04:11:10 +08:00
|
|
|
sb_start_write(mp->m_super);
|
2021-06-08 00:34:51 +08:00
|
|
|
error = xfs_blockgc_free_space(mp, &icw);
|
2020-04-13 04:11:10 +08:00
|
|
|
sb_end_write(mp->m_super);
|
|
|
|
return error;
|
2012-11-08 01:21:12 +08:00
|
|
|
}
|
|
|
|
|
2008-04-18 09:44:03 +08:00
|
|
|
default:
|
|
|
|
return -ENOTTY;
|
|
|
|
}
|
|
|
|
}
|