2018-06-06 02:42:14 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2005-11-02 03:58:39 +00:00
|
|
|
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
|
|
|
|
* All Rights Reserved.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
#include "xfs.h"
|
|
|
|
#include "xfs_fs.h"
|
2013-10-22 23:36:05 +00:00
|
|
|
#include "xfs_shared.h"
|
2013-10-22 23:50:10 +00:00
|
|
|
#include "xfs_format.h"
|
|
|
|
#include "xfs_log_format.h"
|
|
|
|
#include "xfs_trans_resv.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
#include "xfs_mount.h"
|
|
|
|
#include "xfs_inode.h"
|
|
|
|
#include "xfs_rtalloc.h"
|
2019-07-02 16:39:40 +00:00
|
|
|
#include "xfs_iwalk.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
#include "xfs_itable.h"
|
2005-11-02 03:38:42 +00:00
|
|
|
#include "xfs_error.h"
|
xfs: separate out initial attr_set states
We current use XFS_DAS_UNINIT for several steps in the attr_set
state machine. We use it for setting shortform xattrs, converting
from shortform to leaf, leaf add, leaf-to-node and leaf add. All of
these things are essentially known before we start the state machine
iterating, so we really should separate them out:
XFS_DAS_SF_ADD:
- tries to do a shortform add
- on success -> done
- on ENOSPC converts to leaf, -> XFS_DAS_LEAF_ADD
- on error, dies.
XFS_DAS_LEAF_ADD:
- tries to do leaf add
- on success:
- inline attr -> done
- remote xattr || REPLACE -> XFS_DAS_FOUND_LBLK
- on ENOSPC converts to node, -> XFS_DAS_NODE_ADD
- on error, dies
XFS_DAS_NODE_ADD:
- tries to do node add
- on success:
- inline attr -> done
- remote xattr || REPLACE -> XFS_DAS_FOUND_NBLK
- on error, dies
This makes it easier to understand how the state machine starts
up and sets us up on the path to further state machine
simplifications.
This also converts the DAS state tracepoints to use strings rather
than numbers, as converting between enums and numbers requires
manual counting rather than just reading the name.
This also introduces a XFS_DAS_DONE state so that we can trace
successful operation completions easily.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Allison Henderson<allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2022-05-12 05:12:52 +00:00
|
|
|
#include "xfs_da_format.h"
|
|
|
|
#include "xfs_da_btree.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
#include "xfs_attr.h"
|
2005-11-02 03:38:42 +00:00
|
|
|
#include "xfs_bmap.h"
|
2013-08-12 10:49:42 +00:00
|
|
|
#include "xfs_bmap_util.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
#include "xfs_fsops.h"
|
2011-01-07 13:02:04 +00:00
|
|
|
#include "xfs_discard.h"
|
2008-07-18 07:13:20 +00:00
|
|
|
#include "xfs_quota.h"
|
2009-12-14 23:14:59 +00:00
|
|
|
#include "xfs_trace.h"
|
2012-11-07 17:21:12 +00:00
|
|
|
#include "xfs_icache.h"
|
2013-10-22 23:51:50 +00:00
|
|
|
#include "xfs_trans.h"
|
2017-03-28 21:56:37 +00:00
|
|
|
#include "xfs_btree.h"
|
|
|
|
#include <linux/fsmap.h>
|
|
|
|
#include "xfs_fsmap.h"
|
2017-10-18 04:37:34 +00:00
|
|
|
#include "scrub/xfs_scrub.h"
|
2018-01-08 18:51:27 +00:00
|
|
|
#include "xfs_sb.h"
|
2019-04-12 14:41:17 +00:00
|
|
|
#include "xfs_ag.h"
|
2019-04-12 14:41:17 +00:00
|
|
|
#include "xfs_health.h"
|
2019-10-25 05:26:27 +00:00
|
|
|
#include "xfs_reflink.h"
|
2019-11-07 01:19:33 +00:00
|
|
|
#include "xfs_ioctl.h"
|
2024-04-22 16:47:55 +00:00
|
|
|
#include "xfs_xattr.h"
|
2023-10-16 16:34:39 +00:00
|
|
|
#include "xfs_rtbitmap.h"
|
2024-04-15 21:54:09 +00:00
|
|
|
#include "xfs_file.h"
|
2024-04-15 21:54:14 +00:00
|
|
|
#include "xfs_exchrange.h"
|
2024-04-22 16:47:54 +00:00
|
|
|
#include "xfs_handle.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
#include <linux/mount.h>
|
2021-04-07 12:36:43 +00:00
|
|
|
#include <linux/fileattr.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-07-02 16:39:40 +00:00
|
|
|
/* Return 0 on success or positive error */
|
|
|
|
int
|
2019-07-04 03:36:26 +00:00
|
|
|
xfs_fsbulkstat_one_fmt(
|
2019-07-04 03:36:26 +00:00
|
|
|
struct xfs_ibulk *breq,
|
|
|
|
const struct xfs_bulkstat *bstat)
|
2019-07-02 16:39:40 +00:00
|
|
|
{
|
2019-07-04 03:36:26 +00:00
|
|
|
struct xfs_bstat bs1;
|
|
|
|
|
|
|
|
xfs_bulkstat_to_bstat(breq->mp, &bs1, bstat);
|
|
|
|
if (copy_to_user(breq->ubuffer, &bs1, sizeof(bs1)))
|
2019-07-02 16:39:40 +00:00
|
|
|
return -EFAULT;
|
|
|
|
return xfs_ibulk_advance(breq, sizeof(struct xfs_bstat));
|
|
|
|
}
|
|
|
|
|
2019-07-02 16:39:43 +00:00
|
|
|
int
|
2019-07-04 03:36:26 +00:00
|
|
|
xfs_fsinumbers_fmt(
|
2019-07-04 03:36:27 +00:00
|
|
|
struct xfs_ibulk *breq,
|
|
|
|
const struct xfs_inumbers *igrp)
|
2019-07-02 16:39:43 +00:00
|
|
|
{
|
2019-07-04 03:36:27 +00:00
|
|
|
struct xfs_inogrp ig1;
|
|
|
|
|
|
|
|
xfs_inumbers_to_inogrp(&ig1, igrp);
|
|
|
|
if (copy_to_user(breq->ubuffer, &ig1, sizeof(struct xfs_inogrp)))
|
2019-07-02 16:39:43 +00:00
|
|
|
return -EFAULT;
|
|
|
|
return xfs_ibulk_advance(breq, sizeof(struct xfs_inogrp));
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
STATIC int
|
2019-07-04 03:36:26 +00:00
|
|
|
xfs_ioc_fsbulkstat(
|
2021-01-21 13:19:58 +00:00
|
|
|
struct file *file,
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned int cmd,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
2021-01-21 13:19:58 +00:00
|
|
|
struct xfs_mount *mp = XFS_I(file_inode(file))->i_mount;
|
2019-07-02 16:39:40 +00:00
|
|
|
struct xfs_fsop_bulkreq bulkreq;
|
|
|
|
struct xfs_ibulk breq = {
|
|
|
|
.mp = mp,
|
2023-01-13 11:49:30 +00:00
|
|
|
.idmap = file_mnt_idmap(file),
|
2019-07-02 16:39:40 +00:00
|
|
|
.ocount = 0,
|
|
|
|
};
|
|
|
|
xfs_ino_t lastino;
|
2005-04-16 22:20:36 +00: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 01:46:53 +00:00
|
|
|
if (xfs_is_shutdown(mp))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EIO;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-07-04 03:36:25 +00:00
|
|
|
if (copy_from_user(&bulkreq, arg, sizeof(struct xfs_fsop_bulkreq)))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-07-02 16:39:40 +00:00
|
|
|
if (copy_from_user(&lastino, bulkreq.lastip, sizeof(__s64)))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-07-02 16:39:40 +00:00
|
|
|
if (bulkreq.icount <= 0)
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-11-23 05:30:32 +00:00
|
|
|
if (bulkreq.ubuffer == NULL)
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EINVAL;
|
2007-11-23 05:30:32 +00:00
|
|
|
|
2019-07-02 16:39:40 +00: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-02 16:39:43 +00:00
|
|
|
breq.startino = lastino ? lastino + 1 : 0;
|
2019-07-04 03:36:26 +00:00
|
|
|
error = xfs_inumbers(&breq, xfs_fsinumbers_fmt);
|
2019-07-02 16:39:43 +00:00
|
|
|
lastino = breq.startino - 1;
|
2019-07-02 16:39:40 +00:00
|
|
|
} else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE) {
|
|
|
|
breq.startino = lastino;
|
|
|
|
breq.icount = 1;
|
2019-07-04 03:36:26 +00:00
|
|
|
error = xfs_bulkstat_one(&breq, xfs_fsbulkstat_one_fmt);
|
2019-07-02 16:39:40 +00:00
|
|
|
} else { /* XFS_IOC_FSBULKSTAT */
|
|
|
|
breq.startino = lastino ? lastino + 1 : 0;
|
2019-07-04 03:36:26 +00:00
|
|
|
error = xfs_bulkstat(&breq, xfs_fsbulkstat_one_fmt);
|
2019-07-02 16:39:40 +00:00
|
|
|
lastino = breq.startino - 1;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (error)
|
2014-06-25 04:58:08 +00:00
|
|
|
return error;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-07-02 16:39:39 +00:00
|
|
|
if (bulkreq.lastip != NULL &&
|
2019-07-02 16:39:40 +00:00
|
|
|
copy_to_user(bulkreq.lastip, &lastino, sizeof(xfs_ino_t)))
|
2019-07-02 16:39:39 +00:00
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-07-02 16:39:39 +00:00
|
|
|
if (bulkreq.ocount != NULL &&
|
2019-07-02 16:39:40 +00:00
|
|
|
copy_to_user(bulkreq.ocount, &breq.ocount, sizeof(__s32)))
|
2019-07-02 16:39:39 +00:00
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-04 03:36:27 +00: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-28 21:37:57 +00:00
|
|
|
* should proceed; -ECANCELED if there's nothing to do; or the usual
|
2019-07-04 03:36:27 +00:00
|
|
|
* negative error code.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
xfs_bulk_ireq_setup(
|
|
|
|
struct xfs_mount *mp,
|
2022-12-26 18:11:19 +00:00
|
|
|
const struct xfs_bulk_ireq *hdr,
|
2019-07-04 03:36:27 +00:00
|
|
|
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 03:36:28 +00:00
|
|
|
breq->flags = 0;
|
|
|
|
|
2019-07-04 03:36:29 +00: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:
|
2022-12-26 18:11:19 +00:00
|
|
|
breq->startino = mp->m_sb.sb_rootino;
|
2019-07-04 03:36:29 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
breq->icount = 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2019-07-04 03:36:28 +00: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-28 21:37:57 +00:00
|
|
|
return -ECANCELED;
|
2019-07-04 03:36:28 +00:00
|
|
|
} else if (hdr->agno)
|
|
|
|
return -EINVAL;
|
2019-07-04 03:36:27 +00: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-28 21:37:57 +00:00
|
|
|
return -ECANCELED;
|
2019-07-04 03:36:27 +00:00
|
|
|
|
2022-03-09 12:58:37 +00:00
|
|
|
if (hdr->flags & XFS_BULK_IREQ_NREXT64)
|
|
|
|
breq->flags |= XFS_IBULK_NREXT64;
|
|
|
|
|
2019-07-04 03:36:27 +00: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 13:19:58 +00:00
|
|
|
struct file *file,
|
2019-07-04 03:36:27 +00:00
|
|
|
unsigned int cmd,
|
|
|
|
struct xfs_bulkstat_req __user *arg)
|
|
|
|
{
|
2021-01-21 13:19:58 +00:00
|
|
|
struct xfs_mount *mp = XFS_I(file_inode(file))->i_mount;
|
2019-07-04 03:36:27 +00:00
|
|
|
struct xfs_bulk_ireq hdr;
|
|
|
|
struct xfs_ibulk breq = {
|
|
|
|
.mp = mp,
|
2023-01-13 11:49:30 +00:00
|
|
|
.idmap = file_mnt_idmap(file),
|
2019-07-04 03:36:27 +00:00
|
|
|
};
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
2021-08-19 01:46:53 +00:00
|
|
|
if (xfs_is_shutdown(mp))
|
2019-07-04 03:36:27 +00: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-28 21:37:57 +00:00
|
|
|
if (error == -ECANCELED)
|
2019-07-04 03:36:27 +00: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 03:36:28 +00: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 01:46:53 +00:00
|
|
|
if (xfs_is_shutdown(mp))
|
2019-07-04 03:36:28 +00: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-28 21:37:57 +00:00
|
|
|
if (error == -ECANCELED)
|
2019-07-04 03:36:28 +00: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-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC int
|
|
|
|
xfs_ioc_fsgeometry(
|
2019-04-12 14:41:16 +00:00
|
|
|
struct xfs_mount *mp,
|
|
|
|
void __user *arg,
|
|
|
|
int struct_version)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2019-04-12 14:41:16 +00:00
|
|
|
struct xfs_fsop_geom fsgeo;
|
|
|
|
size_t len;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-08-19 01:46:54 +00:00
|
|
|
xfs_fs_geometry(mp, &fsgeo, struct_version);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-04-12 14:41:16 +00: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 14:41:17 +00:00
|
|
|
else {
|
|
|
|
xfs_fsop_geom_health(mp, &fsgeo);
|
2019-04-12 14:41:16 +00:00
|
|
|
len = sizeof(fsgeo);
|
2019-04-12 14:41:17 +00:00
|
|
|
}
|
2019-04-12 14:41:16 +00:00
|
|
|
|
|
|
|
if (copy_to_user(arg, &fsgeo, len))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-12 14:41:17 +00:00
|
|
|
STATIC int
|
|
|
|
xfs_ioc_ag_geometry(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
2022-07-07 09:07:09 +00:00
|
|
|
struct xfs_perag *pag;
|
2019-04-12 14:41:17 +00:00
|
|
|
struct xfs_ag_geometry ageo;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (copy_from_user(&ageo, arg, sizeof(ageo)))
|
|
|
|
return -EFAULT;
|
2019-08-30 23:30:22 +00:00
|
|
|
if (ageo.ag_flags)
|
|
|
|
return -EINVAL;
|
|
|
|
if (memchr_inv(&ageo.ag_reserved, 0, sizeof(ageo.ag_reserved)))
|
|
|
|
return -EINVAL;
|
2019-04-12 14:41:17 +00:00
|
|
|
|
2022-07-07 09:07:09 +00:00
|
|
|
pag = xfs_perag_get(mp, ageo.ag_number);
|
|
|
|
if (!pag)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
error = xfs_ag_get_geometry(pag, &ageo);
|
|
|
|
xfs_perag_put(pag);
|
2019-04-12 14:41:17 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &ageo, sizeof(ageo)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Linux extended inode flags interface.
|
|
|
|
*/
|
|
|
|
|
2019-07-01 15:25:35 +00:00
|
|
|
static void
|
|
|
|
xfs_fill_fsxattr(
|
|
|
|
struct xfs_inode *ip,
|
2021-04-07 12:36:43 +00:00
|
|
|
int whichfork,
|
|
|
|
struct fileattr *fa)
|
2007-10-11 07:47:00 +00:00
|
|
|
{
|
2021-03-29 18:11:42 +00:00
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
2022-07-09 17:56:05 +00:00
|
|
|
struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork);
|
2020-05-18 17:27:22 +00:00
|
|
|
|
2021-04-07 12:36:43 +00:00
|
|
|
fileattr_fill_xflags(fa, xfs_ip2xflags(ip));
|
2021-03-29 18:11:42 +00:00
|
|
|
|
2021-07-12 19:58:51 +00: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) &&
|
2023-10-16 16:34:39 +00:00
|
|
|
xfs_extlen_to_rtxmod(mp, ip->i_extsize) > 0) {
|
2021-07-12 19:58:51 +00:00
|
|
|
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-29 18:11:45 +00:00
|
|
|
if (ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE)
|
2021-03-29 18:11:43 +00:00
|
|
|
fa->fsx_cowextsize = XFS_FSB_TO_B(mp, ip->i_cowextsize);
|
2021-03-29 18:11:39 +00:00
|
|
|
fa->fsx_projid = ip->i_projid;
|
2021-04-13 18:15:12 +00:00
|
|
|
if (ifp && !xfs_need_iread_extents(ifp))
|
2020-05-18 17:27:22 +00:00
|
|
|
fa->fsx_nextents = xfs_iext_count(ifp);
|
|
|
|
else
|
|
|
|
fa->fsx_nextents = xfs_ifork_nextents(ifp);
|
2019-07-01 15:25:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
STATIC int
|
2021-04-07 12:36:43 +00:00
|
|
|
xfs_ioc_fsgetxattra(
|
2019-07-01 15:25:35 +00:00
|
|
|
xfs_inode_t *ip,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
2021-04-07 12:36:43 +00:00
|
|
|
struct fileattr fa;
|
2019-07-01 15:25:35 +00:00
|
|
|
|
|
|
|
xfs_ilock(ip, XFS_ILOCK_SHARED);
|
2021-04-07 12:36:43 +00: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 07:47:00 +00:00
|
|
|
xfs_iunlock(ip, XFS_ILOCK_SHARED);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-02-01 23:14:25 +00:00
|
|
|
static int
|
|
|
|
xfs_ioctl_setattr_xflags(
|
|
|
|
struct xfs_trans *tp,
|
|
|
|
struct xfs_inode *ip,
|
2021-04-07 12:36:43 +00:00
|
|
|
struct fileattr *fa)
|
2015-02-01 23:14:25 +00:00
|
|
|
{
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
2023-10-25 14:10:19 +00:00
|
|
|
bool rtflag = (fa->fsx_xflags & FS_XFLAG_REALTIME);
|
2021-03-29 18:11:45 +00:00
|
|
|
uint64_t i_flags2;
|
2015-02-01 23:14:25 +00:00
|
|
|
|
2023-10-25 14:10:19 +00:00
|
|
|
if (rtflag != XFS_IS_REALTIME_INODE(ip)) {
|
|
|
|
/* Can't change realtime flag if any extents are allocated. */
|
2024-10-15 09:45:09 +00:00
|
|
|
if (xfs_inode_has_filedata(ip))
|
2023-10-25 14:10:19 +00:00
|
|
|
return -EINVAL;
|
2024-08-04 21:39:57 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If S_DAX is enabled on this file, we can only switch the
|
|
|
|
* device if both support fsdax. We can't update S_DAX because
|
|
|
|
* there might be other threads walking down the access paths.
|
|
|
|
*/
|
|
|
|
if (IS_DAX(VFS_I(ip)) &&
|
|
|
|
(mp->m_ddev_targp->bt_daxdev == NULL ||
|
|
|
|
(mp->m_rtdev_targp &&
|
|
|
|
mp->m_rtdev_targp->bt_daxdev == NULL)))
|
|
|
|
return -EINVAL;
|
2023-10-25 14:10:19 +00:00
|
|
|
}
|
2015-02-01 23:14:25 +00:00
|
|
|
|
2023-10-25 14:10:19 +00:00
|
|
|
if (rtflag) {
|
|
|
|
/* If realtime flag is set then must have realtime device */
|
2015-02-01 23:14:25 +00:00
|
|
|
if (mp->m_sb.sb_rblocks == 0 || mp->m_sb.sb_rextsize == 0 ||
|
2023-10-16 16:34:39 +00:00
|
|
|
xfs_extlen_to_rtxmod(mp, ip->i_extsize))
|
2015-02-01 23:14:25 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2023-10-25 14:10:19 +00:00
|
|
|
/* Clear reflink if we are actually able to set the rt flag. */
|
|
|
|
if (xfs_is_reflink_inode(ip))
|
|
|
|
ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK;
|
|
|
|
}
|
2016-10-03 16:11:50 +00:00
|
|
|
|
2017-09-02 15:21:20 +00:00
|
|
|
/* diflags2 only valid for v3 inodes. */
|
2021-03-29 18:11:45 +00:00
|
|
|
i_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
|
2021-08-19 01:46:37 +00:00
|
|
|
if (i_flags2 && !xfs_has_v3inodes(mp))
|
2017-09-02 15:21:20 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2021-03-29 18:11:44 +00:00
|
|
|
ip->i_diflags = xfs_flags2diflags(ip, fa->fsx_xflags);
|
2021-03-29 18:11:45 +00:00
|
|
|
ip->i_diflags2 = i_flags2;
|
2017-09-02 15:21:20 +00:00
|
|
|
|
2020-05-04 16:02:43 +00:00
|
|
|
xfs_diflags_to_iflags(ip, false);
|
2023-10-25 14:10:20 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make the stable writes flag match that of the device the inode
|
|
|
|
* resides on when flipping the RT flag.
|
|
|
|
*/
|
|
|
|
if (rtflag != XFS_IS_REALTIME_INODE(ip) && S_ISREG(VFS_I(ip)->i_mode))
|
|
|
|
xfs_update_stable_writes(ip);
|
|
|
|
|
2015-02-01 23:14:25 +00:00
|
|
|
xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
|
|
|
|
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
|
2015-10-12 07:21:22 +00:00
|
|
|
XFS_STATS_INC(mp, xs_ig_attrchg);
|
2015-02-01 23:14:25 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-04-30 14:41:38 +00:00
|
|
|
static void
|
|
|
|
xfs_ioctl_setattr_prepare_dax(
|
2016-02-29 22:41:33 +00:00
|
|
|
struct xfs_inode *ip,
|
2021-04-07 12:36:43 +00:00
|
|
|
struct fileattr *fa)
|
2016-02-29 22:41:33 +00:00
|
|
|
{
|
2020-04-30 14:41:38 +00:00
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
|
|
|
struct inode *inode = VFS_I(ip);
|
2016-02-29 22:41:33 +00:00
|
|
|
|
2018-05-31 22:07:47 +00:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
2020-04-30 14:41:38 +00:00
|
|
|
return;
|
2016-02-29 22:41:33 +00:00
|
|
|
|
2021-08-19 01:46:52 +00:00
|
|
|
if (xfs_has_dax_always(mp) || xfs_has_dax_never(mp))
|
2020-04-30 14:41:38 +00:00
|
|
|
return;
|
2016-02-29 22:41:33 +00:00
|
|
|
|
2020-04-30 14:41:38 +00:00
|
|
|
if (((fa->fsx_xflags & FS_XFLAG_DAX) &&
|
2021-03-29 18:11:45 +00:00
|
|
|
!(ip->i_diflags2 & XFS_DIFLAG2_DAX)) ||
|
2020-04-30 14:41:38 +00:00
|
|
|
(!(fa->fsx_xflags & FS_XFLAG_DAX) &&
|
2021-03-29 18:11:45 +00:00
|
|
|
(ip->i_diflags2 & XFS_DIFLAG2_DAX)))
|
2020-04-30 14:41:38 +00:00
|
|
|
d_mark_dontcache(inode);
|
2015-02-01 23:14:25 +00:00
|
|
|
}
|
|
|
|
|
2015-02-01 23:15:35 +00: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 12:36:43 +00:00
|
|
|
struct xfs_inode *ip,
|
2021-01-29 19:32:09 +00:00
|
|
|
struct xfs_dquot *pdqp)
|
2015-02-01 23:15:35 +00:00
|
|
|
{
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
|
|
|
struct xfs_trans *tp;
|
2016-02-29 22:41:33 +00:00
|
|
|
int error = -EROFS;
|
2015-02-01 23:15:35 +00:00
|
|
|
|
2021-08-19 01:46:52 +00:00
|
|
|
if (xfs_is_readonly(mp))
|
2021-01-29 19:32:09 +00:00
|
|
|
goto out_error;
|
2016-02-29 22:41:33 +00:00
|
|
|
error = -EIO;
|
2021-08-19 01:46:53 +00:00
|
|
|
if (xfs_is_shutdown(mp))
|
2021-01-29 19:32:09 +00:00
|
|
|
goto out_error;
|
2015-02-01 23:15:35 +00:00
|
|
|
|
2021-01-29 19:32:09 +00:00
|
|
|
error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp,
|
2022-02-26 00:18:30 +00:00
|
|
|
has_capability_noaudit(current, CAP_FOWNER), &tp);
|
2015-02-01 23:15:35 +00:00
|
|
|
if (error)
|
2021-01-29 19:32:09 +00:00
|
|
|
goto out_error;
|
2015-02-01 23:15:35 +00:00
|
|
|
|
2021-08-19 01:46:52 +00:00
|
|
|
if (xfs_has_wsync(mp))
|
2015-02-01 23:15:35 +00:00
|
|
|
xfs_trans_set_sync(tp);
|
|
|
|
|
|
|
|
return tp;
|
|
|
|
|
2021-01-29 19:32:09 +00:00
|
|
|
out_error:
|
2015-02-01 23:15:35 +00:00
|
|
|
return ERR_PTR(error);
|
|
|
|
}
|
|
|
|
|
2015-02-01 23:26:26 +00:00
|
|
|
/*
|
2021-05-12 19:49:19 +00:00
|
|
|
* Validate a proposed extent size hint. For regular files, the hint can only
|
|
|
|
* be changed if no extents are allocated.
|
2015-02-01 23:26:26 +00:00
|
|
|
*/
|
2015-02-05 00:13:21 +00:00
|
|
|
static int
|
2015-02-01 23:22:20 +00:00
|
|
|
xfs_ioctl_setattr_check_extsize(
|
|
|
|
struct xfs_inode *ip,
|
2021-04-07 12:36:43 +00:00
|
|
|
struct fileattr *fa)
|
2015-02-01 23:22:20 +00:00
|
|
|
{
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
2021-05-12 19:49:19 +00:00
|
|
|
xfs_failaddr_t failaddr;
|
|
|
|
uint16_t new_diflags;
|
2015-02-01 23:26:26 +00:00
|
|
|
|
2021-04-07 12:36:43 +00:00
|
|
|
if (!fa->fsx_valid)
|
|
|
|
return 0;
|
|
|
|
|
2024-10-15 09:45:09 +00:00
|
|
|
if (S_ISREG(VFS_I(ip)->i_mode) && xfs_inode_has_filedata(ip) &&
|
2021-05-12 19:49:19 +00:00
|
|
|
XFS_FSB_TO_B(mp, ip->i_extsize) != fa->fsx_extsize)
|
2015-02-01 23:22:20 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2021-05-12 19:49:19 +00:00
|
|
|
if (fa->fsx_extsize & mp->m_blockmask)
|
2019-07-01 15:25:36 +00:00
|
|
|
return -EINVAL;
|
2015-02-01 23:22:20 +00:00
|
|
|
|
2021-05-12 19:49:19 +00:00
|
|
|
new_diflags = xfs_flags2diflags(ip, fa->fsx_xflags);
|
2015-02-01 23:26:26 +00: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-12 19:51:26 +00:00
|
|
|
/*
|
2021-07-12 19:58:50 +00: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-12 19:51:26 +00: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-12 19:49:19 +00: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-01 23:22:20 +00:00
|
|
|
}
|
|
|
|
|
2016-10-03 16:11:43 +00:00
|
|
|
static int
|
|
|
|
xfs_ioctl_setattr_check_cowextsize(
|
|
|
|
struct xfs_inode *ip,
|
2021-04-07 12:36:43 +00:00
|
|
|
struct fileattr *fa)
|
2016-10-03 16:11:43 +00:00
|
|
|
{
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
2021-05-12 19:49:19 +00:00
|
|
|
xfs_failaddr_t failaddr;
|
|
|
|
uint64_t new_diflags2;
|
|
|
|
uint16_t new_diflags;
|
2016-10-03 16:11:43 +00:00
|
|
|
|
2021-04-07 12:36:43 +00:00
|
|
|
if (!fa->fsx_valid)
|
|
|
|
return 0;
|
|
|
|
|
2021-05-12 19:49:19 +00:00
|
|
|
if (fa->fsx_cowextsize & mp->m_blockmask)
|
2019-07-01 15:25:36 +00:00
|
|
|
return -EINVAL;
|
2016-10-03 16:11:43 +00:00
|
|
|
|
2021-05-12 19:49:19 +00:00
|
|
|
new_diflags = xfs_flags2diflags(ip, fa->fsx_xflags);
|
|
|
|
new_diflags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
|
2016-10-03 16:11:43 +00:00
|
|
|
|
2021-05-12 19:49:19 +00: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-03 16:11:43 +00:00
|
|
|
}
|
|
|
|
|
2015-02-05 00:13:21 +00:00
|
|
|
static int
|
2015-02-01 23:22:53 +00:00
|
|
|
xfs_ioctl_setattr_check_projid(
|
|
|
|
struct xfs_inode *ip,
|
2021-04-07 12:36:43 +00:00
|
|
|
struct fileattr *fa)
|
2015-02-01 23:22:53 +00:00
|
|
|
{
|
2021-04-07 12:36:43 +00:00
|
|
|
if (!fa->fsx_valid)
|
|
|
|
return 0;
|
|
|
|
|
2021-08-19 01:46:37 +00:00
|
|
|
/* Disallow 32bit project ids if 32bit IDs are not enabled. */
|
2017-06-16 18:00:05 +00:00
|
|
|
if (fa->fsx_projid > (uint16_t)-1 &&
|
2021-08-19 01:46:37 +00:00
|
|
|
!xfs_has_projid32(ip->i_mount))
|
2015-02-01 23:22:53 +00:00
|
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
|
|
}
|
2008-07-18 07:13:20 +00:00
|
|
|
|
2021-04-07 12:36:43 +00:00
|
|
|
int
|
|
|
|
xfs_fileattr_set(
|
2023-01-13 11:49:21 +00:00
|
|
|
struct mnt_idmap *idmap,
|
2021-04-07 12:36:43 +00:00
|
|
|
struct dentry *dentry,
|
|
|
|
struct fileattr *fa)
|
2008-07-18 07:13:20 +00:00
|
|
|
{
|
2021-04-07 12:36:43 +00:00
|
|
|
struct xfs_inode *ip = XFS_I(d_inode(dentry));
|
2008-07-18 07:13:20 +00:00
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
|
|
|
struct xfs_trans *tp;
|
2013-07-11 05:00:40 +00:00
|
|
|
struct xfs_dquot *pdqp = NULL;
|
2008-07-18 07:13:20 +00:00
|
|
|
struct xfs_dquot *olddquot = NULL;
|
2021-01-30 01:14:33 +00:00
|
|
|
int error;
|
2008-07-18 07:13:20 +00:00
|
|
|
|
2010-06-24 01:57:09 +00:00
|
|
|
trace_xfs_ioctl_setattr(ip);
|
2008-07-18 07:13:20 +00:00
|
|
|
|
2021-04-07 12:36:43 +00: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 01:14:33 +00:00
|
|
|
error = xfs_ioctl_setattr_check_projid(ip, fa);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2010-08-26 10:19:43 +00:00
|
|
|
|
2008-07-18 07:13:20 +00: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 12:36:43 +00:00
|
|
|
if (fa->fsx_valid && XFS_IS_QUOTA_ON(mp)) {
|
2021-01-30 01:14:33 +00:00
|
|
|
error = xfs_qm_vop_dqalloc(ip, VFS_I(ip)->i_uid,
|
2020-02-21 16:31:27 +00:00
|
|
|
VFS_I(ip)->i_gid, fa->fsx_projid,
|
2020-04-23 04:54:29 +00:00
|
|
|
XFS_QMOPT_PQUOTA, NULL, NULL, &pdqp);
|
2021-01-30 01:14:33 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
2008-07-18 07:13:20 +00:00
|
|
|
}
|
|
|
|
|
2020-04-30 14:41:38 +00:00
|
|
|
xfs_ioctl_setattr_prepare_dax(ip, fa);
|
2016-02-29 22:41:33 +00:00
|
|
|
|
2021-04-07 12:36:43 +00:00
|
|
|
tp = xfs_ioctl_setattr_get_trans(ip, pdqp);
|
2015-02-01 23:15:35 +00:00
|
|
|
if (IS_ERR(tp)) {
|
2021-01-30 01:14:33 +00:00
|
|
|
error = PTR_ERR(tp);
|
2015-02-01 23:15:35 +00:00
|
|
|
goto error_free_dquots;
|
2008-07-18 07:13:20 +00:00
|
|
|
}
|
|
|
|
|
2021-01-30 01:14:33 +00:00
|
|
|
error = xfs_ioctl_setattr_check_extsize(ip, fa);
|
|
|
|
if (error)
|
2015-02-01 23:22:20 +00:00
|
|
|
goto error_trans_cancel;
|
2008-07-18 07:13:20 +00:00
|
|
|
|
2021-01-30 01:14:33 +00:00
|
|
|
error = xfs_ioctl_setattr_check_cowextsize(ip, fa);
|
|
|
|
if (error)
|
2016-10-03 16:11:43 +00:00
|
|
|
goto error_trans_cancel;
|
|
|
|
|
2021-01-30 01:14:33 +00:00
|
|
|
error = xfs_ioctl_setattr_xflags(tp, ip, fa);
|
|
|
|
if (error)
|
2015-02-01 23:22:20 +00:00
|
|
|
goto error_trans_cancel;
|
2008-07-18 07:13:20 +00:00
|
|
|
|
2021-04-07 12:36:43 +00:00
|
|
|
if (!fa->fsx_valid)
|
|
|
|
goto skip_xattr;
|
2008-07-18 07:13:20 +00:00
|
|
|
/*
|
2015-02-01 23:16:25 +00: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 07:13:20 +00:00
|
|
|
*/
|
|
|
|
|
2016-02-09 05:54:58 +00:00
|
|
|
if ((VFS_I(ip)->i_mode & (S_ISUID|S_ISGID)) &&
|
2023-01-13 11:49:27 +00:00
|
|
|
!capable_wrt_inode_uidgid(idmap, VFS_I(ip), CAP_FSETID))
|
2016-02-09 05:54:58 +00:00
|
|
|
VFS_I(ip)->i_mode &= ~(S_ISUID|S_ISGID);
|
2008-07-18 07:13:20 +00:00
|
|
|
|
2015-02-01 23:16:25 +00:00
|
|
|
/* Change the ownerships and register project quota modifications */
|
2021-03-29 18:11:39 +00:00
|
|
|
if (ip->i_projid != fa->fsx_projid) {
|
2021-08-06 18:05:37 +00:00
|
|
|
if (XFS_IS_PQUOTA_ON(mp)) {
|
2015-02-01 23:16:25 +00:00
|
|
|
olddquot = xfs_qm_vop_chown(tp, ip,
|
|
|
|
&ip->i_pdquot, pdqp);
|
|
|
|
}
|
2021-03-29 18:11:39 +00:00
|
|
|
ip->i_projid = fa->fsx_projid;
|
2008-07-21 06:16:15 +00:00
|
|
|
}
|
2008-07-18 07:13:20 +00:00
|
|
|
|
2014-10-01 23:20:30 +00: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-29 18:11:44 +00:00
|
|
|
if (ip->i_diflags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT))
|
2021-03-29 18:11:43 +00:00
|
|
|
ip->i_extsize = XFS_B_TO_FSB(mp, fa->fsx_extsize);
|
2016-10-03 16:11:43 +00:00
|
|
|
else
|
2021-03-29 18:11:41 +00:00
|
|
|
ip->i_extsize = 0;
|
2021-03-29 18:11:43 +00:00
|
|
|
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_v3inodes(mp)) {
|
2021-03-29 18:11:45 +00:00
|
|
|
if (ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE)
|
2021-03-29 18:11:43 +00:00
|
|
|
ip->i_cowextsize = XFS_B_TO_FSB(mp, fa->fsx_cowextsize);
|
|
|
|
else
|
|
|
|
ip->i_cowextsize = 0;
|
|
|
|
}
|
2008-07-18 07:13:20 +00:00
|
|
|
|
2021-04-07 12:36:43 +00:00
|
|
|
skip_xattr:
|
2021-01-30 01:14:33 +00:00
|
|
|
error = xfs_trans_commit(tp);
|
2008-07-18 07:13:20 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Release any dquot(s) the inode had kept before chown.
|
|
|
|
*/
|
2009-06-08 13:33:32 +00:00
|
|
|
xfs_qm_dqrele(olddquot);
|
2013-07-11 05:00:40 +00:00
|
|
|
xfs_qm_dqrele(pdqp);
|
2008-07-18 07:13:20 +00:00
|
|
|
|
2021-01-30 01:14:33 +00:00
|
|
|
return error;
|
2008-07-18 07:13:20 +00:00
|
|
|
|
2015-02-01 23:22:20 +00:00
|
|
|
error_trans_cancel:
|
2015-06-04 03:47:56 +00:00
|
|
|
xfs_trans_cancel(tp);
|
2015-02-01 23:15:35 +00:00
|
|
|
error_free_dquots:
|
2013-07-11 05:00:40 +00:00
|
|
|
xfs_qm_dqrele(pdqp);
|
2021-01-30 01:14:33 +00:00
|
|
|
return error;
|
2008-07-18 07:13:20 +00:00
|
|
|
}
|
|
|
|
|
2017-10-17 21:16:19 +00:00
|
|
|
static bool
|
|
|
|
xfs_getbmap_format(
|
|
|
|
struct kgetbmap *p,
|
|
|
|
struct getbmapx __user *u,
|
|
|
|
size_t recsize)
|
2008-11-28 03:23:35 +00:00
|
|
|
{
|
2017-10-17 21:16:19 +00: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 03:23:35 +00:00
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
STATIC int
|
|
|
|
xfs_ioc_getbmap(
|
2016-07-20 01:29:35 +00:00
|
|
|
struct file *file,
|
2005-04-16 22:20:36 +00:00
|
|
|
unsigned int cmd,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
2017-04-03 22:17:57 +00:00
|
|
|
struct getbmapx bmx = { 0 };
|
2017-10-17 21:16:19 +00:00
|
|
|
struct kgetbmap *buf;
|
|
|
|
size_t recsize;
|
|
|
|
int error, i;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-10-17 21:16:19 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case XFS_IOC_GETBMAPA:
|
|
|
|
bmx.bmv_iflags = BMV_IF_ATTRFORK;
|
2021-04-20 22:54:36 +00:00
|
|
|
fallthrough;
|
2017-10-17 21:16:19 +00: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 05:04:54 +00:00
|
|
|
return -EINVAL;
|
2017-10-17 21:16:19 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-10-17 21:16:19 +00:00
|
|
|
if (copy_from_user(&bmx, arg, recsize))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (bmx.bmv_count < 2)
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EINVAL;
|
2022-01-24 23:48:31 +00:00
|
|
|
if (bmx.bmv_count >= INT_MAX / recsize)
|
2017-10-17 21:16:19 +00:00
|
|
|
return -ENOMEM;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-09-28 22:33:07 +00:00
|
|
|
buf = kvcalloc(bmx.bmv_count, sizeof(*buf), GFP_KERNEL);
|
2017-10-17 21:16:19 +00:00
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-10-17 21:16:19 +00:00
|
|
|
error = xfs_getbmap(XFS_I(file_inode(file)), &bmx, buf);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (error)
|
2017-10-17 21:16:19 +00:00
|
|
|
goto out_free_buf;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2017-10-17 21:16:19 +00: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-16 22:20:36 +00:00
|
|
|
|
2017-10-17 21:16:19 +00:00
|
|
|
error = 0;
|
|
|
|
out_free_buf:
|
2024-02-16 17:02:30 +00:00
|
|
|
kvfree(buf);
|
2018-11-06 15:50:50 +00:00
|
|
|
return error;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2013-08-12 10:49:48 +00:00
|
|
|
int
|
|
|
|
xfs_ioc_swapext(
|
|
|
|
xfs_swapext_t *sxp)
|
|
|
|
{
|
|
|
|
xfs_inode_t *ip, *tip;
|
|
|
|
|
|
|
|
/* Pull information for the target fd */
|
2024-07-20 01:19:02 +00:00
|
|
|
CLASS(fd, f)((int)sxp->sx_fdtarget);
|
|
|
|
if (fd_empty(f))
|
|
|
|
return -EINVAL;
|
2013-08-12 10:49:48 +00:00
|
|
|
|
2024-05-31 18:12:01 +00:00
|
|
|
if (!(fd_file(f)->f_mode & FMODE_WRITE) ||
|
|
|
|
!(fd_file(f)->f_mode & FMODE_READ) ||
|
2024-07-20 01:19:02 +00:00
|
|
|
(fd_file(f)->f_flags & O_APPEND))
|
|
|
|
return -EBADF;
|
2013-08-12 10:49:48 +00:00
|
|
|
|
2024-07-20 01:19:02 +00:00
|
|
|
CLASS(fd, tmp)((int)sxp->sx_fdtmp);
|
|
|
|
if (fd_empty(tmp))
|
|
|
|
return -EINVAL;
|
2013-08-12 10:49:48 +00:00
|
|
|
|
2024-05-31 18:12:01 +00:00
|
|
|
if (!(fd_file(tmp)->f_mode & FMODE_WRITE) ||
|
|
|
|
!(fd_file(tmp)->f_mode & FMODE_READ) ||
|
2024-07-20 01:19:02 +00:00
|
|
|
(fd_file(tmp)->f_flags & O_APPEND))
|
|
|
|
return -EBADF;
|
2013-08-12 10:49:48 +00:00
|
|
|
|
2024-05-31 18:12:01 +00:00
|
|
|
if (IS_SWAPFILE(file_inode(fd_file(f))) ||
|
2024-07-20 01:19:02 +00:00
|
|
|
IS_SWAPFILE(file_inode(fd_file(tmp))))
|
|
|
|
return -EINVAL;
|
2013-08-12 10:49:48 +00:00
|
|
|
|
2016-07-20 00:30:30 +00: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.
|
|
|
|
*/
|
2024-05-31 18:12:01 +00:00
|
|
|
if (fd_file(f)->f_op != &xfs_file_operations ||
|
2024-07-20 01:19:02 +00:00
|
|
|
fd_file(tmp)->f_op != &xfs_file_operations)
|
|
|
|
return -EINVAL;
|
2016-07-20 00:30:30 +00:00
|
|
|
|
2024-05-31 18:12:01 +00:00
|
|
|
ip = XFS_I(file_inode(fd_file(f)));
|
|
|
|
tip = XFS_I(file_inode(fd_file(tmp)));
|
2013-08-12 10:49:48 +00:00
|
|
|
|
2024-07-20 01:19:02 +00:00
|
|
|
if (ip->i_mount != tip->i_mount)
|
|
|
|
return -EINVAL;
|
2013-08-12 10:49:48 +00:00
|
|
|
|
2024-07-20 01:19:02 +00:00
|
|
|
if (ip->i_ino == tip->i_ino)
|
|
|
|
return -EINVAL;
|
2013-08-12 10:49:48 +00:00
|
|
|
|
2024-07-20 01:19:02 +00:00
|
|
|
if (xfs_is_shutdown(ip->i_mount))
|
|
|
|
return -EIO;
|
2013-08-12 10:49:48 +00:00
|
|
|
|
2024-07-20 01:19:02 +00:00
|
|
|
return xfs_swap_extents(ip, tip, sxp);
|
2013-08-12 10:49:48 +00:00
|
|
|
}
|
|
|
|
|
2018-05-15 20:21:48 +00: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 02:42:45 +00:00
|
|
|
/* 1 larger than sb_fname, so this ensures a trailing NUL char */
|
|
|
|
memset(label, 0, sizeof(label));
|
2018-05-15 20:21:48 +00: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 02:42:45 +00:00
|
|
|
strncpy(label, sbp->sb_fname, XFSLABEL_MAX);
|
2018-05-15 20:21:48 +00: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 02:42:45 +00:00
|
|
|
if (copy_to_user(user_label, label, sizeof(label)))
|
2018-05-15 20:21:48 +00: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 02:42:45 +00:00
|
|
|
memcpy(sbp->sb_fname, label, len);
|
2018-05-15 20:21:48 +00: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-21 20:08:46 +00:00
|
|
|
static inline int
|
|
|
|
xfs_fs_eofblocks_from_user(
|
|
|
|
struct xfs_fs_eofblocks *src,
|
2021-06-07 16:34:51 +00:00
|
|
|
struct xfs_icwalk *dst)
|
2020-05-21 20:08:46 +00: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-07 16:34:51 +00:00
|
|
|
dst->icw_flags = 0;
|
2021-06-07 16:34:51 +00:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_SYNC)
|
2021-06-07 16:34:51 +00:00
|
|
|
dst->icw_flags |= XFS_ICWALK_FLAG_SYNC;
|
2021-06-07 16:34:51 +00:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_UID)
|
2021-06-07 16:34:51 +00:00
|
|
|
dst->icw_flags |= XFS_ICWALK_FLAG_UID;
|
2021-06-07 16:34:51 +00:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_GID)
|
2021-06-07 16:34:51 +00:00
|
|
|
dst->icw_flags |= XFS_ICWALK_FLAG_GID;
|
2021-06-07 16:34:51 +00:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_PRID)
|
2021-06-07 16:34:51 +00:00
|
|
|
dst->icw_flags |= XFS_ICWALK_FLAG_PRID;
|
2021-06-07 16:34:51 +00:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_MINFILESIZE)
|
2021-06-07 16:34:51 +00:00
|
|
|
dst->icw_flags |= XFS_ICWALK_FLAG_MINFILESIZE;
|
2021-06-07 16:34:51 +00:00
|
|
|
|
2021-06-07 16:34:51 +00:00
|
|
|
dst->icw_prid = src->eof_prid;
|
|
|
|
dst->icw_min_file_size = src->eof_min_file_size;
|
2020-05-21 20:08:46 +00:00
|
|
|
|
2021-06-07 16:34:51 +00:00
|
|
|
dst->icw_uid = INVALID_UID;
|
2020-05-21 20:08:46 +00:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_UID) {
|
2021-06-07 16:34:51 +00:00
|
|
|
dst->icw_uid = make_kuid(current_user_ns(), src->eof_uid);
|
|
|
|
if (!uid_valid(dst->icw_uid))
|
2020-05-21 20:08:46 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-06-07 16:34:51 +00:00
|
|
|
dst->icw_gid = INVALID_GID;
|
2020-05-21 20:08:46 +00:00
|
|
|
if (src->eof_flags & XFS_EOF_FLAGS_GID) {
|
2021-06-07 16:34:51 +00:00
|
|
|
dst->icw_gid = make_kgid(current_user_ns(), src->eof_gid);
|
|
|
|
if (!gid_valid(dst->icw_gid))
|
2020-05-21 20:08:46 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-12-04 17:40:54 +00:00
|
|
|
static int
|
|
|
|
xfs_ioctl_getset_resblocks(
|
|
|
|
struct file *filp,
|
|
|
|
unsigned int cmd,
|
|
|
|
void __user *arg)
|
|
|
|
{
|
|
|
|
struct xfs_mount *mp = XFS_I(file_inode(filp))->i_mount;
|
|
|
|
struct xfs_fsop_resblks fsop = { };
|
|
|
|
int error;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
if (cmd == XFS_IOC_SET_RESBLKS) {
|
|
|
|
if (xfs_is_readonly(mp))
|
|
|
|
return -EROFS;
|
|
|
|
|
|
|
|
if (copy_from_user(&fsop, arg, sizeof(fsop)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2023-12-04 17:40:56 +00:00
|
|
|
error = xfs_reserve_blocks(mp, fsop.resblks);
|
2023-12-04 17:40:54 +00:00
|
|
|
mnt_drop_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2023-12-04 17:40:56 +00:00
|
|
|
spin_lock(&mp->m_sb_lock);
|
|
|
|
fsop.resblks = mp->m_resblks;
|
|
|
|
fsop.resblks_avail = mp->m_resblks_avail;
|
|
|
|
spin_unlock(&mp->m_sb_lock);
|
|
|
|
|
2023-12-04 17:40:54 +00:00
|
|
|
if (copy_to_user(arg, &fsop, sizeof(fsop)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-12-04 17:40:55 +00:00
|
|
|
static int
|
|
|
|
xfs_ioctl_fs_counts(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_fsop_counts __user *uarg)
|
|
|
|
{
|
|
|
|
struct xfs_fsop_counts out = {
|
|
|
|
.allocino = percpu_counter_read_positive(&mp->m_icount),
|
|
|
|
.freeino = percpu_counter_read_positive(&mp->m_ifree),
|
|
|
|
.freedata = percpu_counter_read_positive(&mp->m_fdblocks) -
|
|
|
|
xfs_fdblocks_unavailable(mp),
|
|
|
|
.freertx = percpu_counter_read_positive(&mp->m_frextents),
|
|
|
|
};
|
|
|
|
|
|
|
|
if (copy_to_user(uarg, &out, sizeof(out)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-12-21 21:07:38 +00:00
|
|
|
/*
|
|
|
|
* These long-unused ioctls were removed from the official ioctl API in 5.17,
|
|
|
|
* but retain these definitions so that we can log warnings about them.
|
|
|
|
*/
|
|
|
|
#define XFS_IOC_ALLOCSP _IOW ('X', 10, struct xfs_flock64)
|
|
|
|
#define XFS_IOC_FREESP _IOW ('X', 11, struct xfs_flock64)
|
|
|
|
#define XFS_IOC_ALLOCSP64 _IOW ('X', 36, struct xfs_flock64)
|
|
|
|
#define XFS_IOC_FREESP64 _IOW ('X', 37, struct xfs_flock64)
|
|
|
|
|
2008-12-09 09:47:33 +00: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 01:44:03 +00:00
|
|
|
struct file *filp,
|
|
|
|
unsigned int cmd,
|
2008-12-09 09:47:33 +00:00
|
|
|
unsigned long p)
|
2008-04-18 01:44:03 +00:00
|
|
|
{
|
2013-01-23 22:07:38 +00:00
|
|
|
struct inode *inode = file_inode(filp);
|
2008-12-09 09:47:33 +00:00
|
|
|
struct xfs_inode *ip = XFS_I(inode);
|
|
|
|
struct xfs_mount *mp = ip->i_mount;
|
|
|
|
void __user *arg = (void __user *)p;
|
2008-04-18 01:44:03 +00:00
|
|
|
int error;
|
|
|
|
|
2010-06-24 01:57:09 +00:00
|
|
|
trace_xfs_file_ioctl(ip);
|
2008-12-09 09:47:33 +00:00
|
|
|
|
|
|
|
switch (cmd) {
|
2011-01-07 13:02:04 +00:00
|
|
|
case FITRIM:
|
|
|
|
return xfs_ioc_trim(mp, arg);
|
2018-05-15 20:21:48 +00: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 01:44:03 +00:00
|
|
|
case XFS_IOC_ALLOCSP:
|
|
|
|
case XFS_IOC_FREESP:
|
|
|
|
case XFS_IOC_ALLOCSP64:
|
xfs: kill the XFS_IOC_{ALLOC,FREE}SP* ioctls
According to the glibc compat header for Irix 4, these ioctls originated
in April 1991 as a (somewhat clunky) way to preallocate space at the end
of a file on an EFS filesystem. XFS, which was released in Irix 5.3 in
December 1993, picked up these ioctls to maintain compatibility and they
were ported to Linux in the early 2000s.
Recently it was pointed out to me they still lurk in the kernel, even
though the Linux fallocate syscall supplanted the functionality a long
time ago. fstests doesn't seem to include any real functional or stress
tests for these ioctls, which means that the code quality is ... very
questionable. Most notably, it was a stale disk block exposure vector
for 21 years and nobody noticed or complained. As mature programmers
say, "If you're not testing it, it's broken."
Given all that, let's withdraw these ioctls from the XFS userspace API.
Normally we'd set a long deprecation process, but I estimate that there
aren't any real users, so let's trigger a warning in dmesg and return
-ENOTTY.
See: CVE-2021-4155
Augments: 983d8e60f508 ("xfs: map unwritten blocks in XFS_IOC_{ALLOC,FREE}SP just like fallocate")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2022-01-08 01:45:51 +00:00
|
|
|
case XFS_IOC_FREESP64:
|
|
|
|
xfs_warn_once(mp,
|
|
|
|
"%s should use fallocate; XFS_IOC_{ALLOC,FREE}SP ioctl unsupported",
|
|
|
|
current->comm);
|
|
|
|
return -ENOTTY;
|
2008-04-18 01:44:03 +00:00
|
|
|
case XFS_IOC_DIOINFO: {
|
2019-10-25 05:25:39 +00:00
|
|
|
struct xfs_buftarg *target = xfs_inode_buftarg(ip);
|
|
|
|
struct dioattr da;
|
2008-04-18 01:44:03 +00: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-21 22:46:23 +00:00
|
|
|
da.d_mem = da.d_miniosz = target->bt_logical_sectorsize;
|
2008-04-18 01:44:03 +00:00
|
|
|
da.d_maxiosz = INT_MAX & ~(da.d_miniosz - 1);
|
|
|
|
|
|
|
|
if (copy_to_user(arg, &da, sizeof(da)))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2008-04-18 01:44:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_FSBULKSTAT_SINGLE:
|
|
|
|
case XFS_IOC_FSBULKSTAT:
|
|
|
|
case XFS_IOC_FSINUMBERS:
|
2021-01-21 13:19:58 +00:00
|
|
|
return xfs_ioc_fsbulkstat(filp, cmd, arg);
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2019-07-04 03:36:27 +00:00
|
|
|
case XFS_IOC_BULKSTAT:
|
2021-01-21 13:19:58 +00:00
|
|
|
return xfs_ioc_bulkstat(filp, cmd, arg);
|
2019-07-04 03:36:28 +00:00
|
|
|
case XFS_IOC_INUMBERS:
|
|
|
|
return xfs_ioc_inumbers(mp, cmd, arg);
|
2008-04-18 01:44:03 +00:00
|
|
|
|
|
|
|
case XFS_IOC_FSGEOMETRY_V1:
|
2019-04-12 14:41:16 +00:00
|
|
|
return xfs_ioc_fsgeometry(mp, arg, 3);
|
|
|
|
case XFS_IOC_FSGEOMETRY_V4:
|
|
|
|
return xfs_ioc_fsgeometry(mp, arg, 4);
|
2008-04-18 01:44:03 +00:00
|
|
|
case XFS_IOC_FSGEOMETRY:
|
2019-04-12 14:41:16 +00:00
|
|
|
return xfs_ioc_fsgeometry(mp, arg, 5);
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2019-04-12 14:41:17 +00:00
|
|
|
case XFS_IOC_AG_GEOMETRY:
|
|
|
|
return xfs_ioc_ag_geometry(mp, arg);
|
|
|
|
|
2008-04-18 01:44:03 +00:00
|
|
|
case XFS_IOC_GETVERSION:
|
|
|
|
return put_user(inode->i_generation, (int __user *)arg);
|
|
|
|
|
|
|
|
case XFS_IOC_FSGETXATTRA:
|
2021-04-07 12:36:43 +00:00
|
|
|
return xfs_ioc_fsgetxattra(ip, arg);
|
2024-04-22 16:47:55 +00:00
|
|
|
case XFS_IOC_GETPARENTS:
|
|
|
|
return xfs_ioc_getparents(filp, arg);
|
|
|
|
case XFS_IOC_GETPARENTS_BY_HANDLE:
|
|
|
|
return xfs_ioc_getparents_by_handle(filp, arg);
|
2008-04-18 01:44:03 +00:00
|
|
|
case XFS_IOC_GETBMAP:
|
|
|
|
case XFS_IOC_GETBMAPA:
|
|
|
|
case XFS_IOC_GETBMAPX:
|
2017-10-17 21:16:19 +00:00
|
|
|
return xfs_ioc_getbmap(filp, cmd, arg);
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2017-03-28 21:56:37 +00:00
|
|
|
case FS_IOC_GETFSMAP:
|
|
|
|
return xfs_ioc_getfsmap(ip, arg);
|
|
|
|
|
xfs: introduce vectored scrub mode
Introduce a variant on XFS_SCRUB_METADATA that allows for a vectored
mode. The caller specifies the principal metadata object that they want
to scrub (allocation group, inode, etc.) once, followed by an array of
scrub types they want called on that object. The kernel runs the scrub
operations and writes the output flags and errno code to the
corresponding array element.
A new pseudo scrub type BARRIER is introduced to force the kernel to
return to userspace if any corruptions have been found when scrubbing
the previous scrub types in the array. This enables userspace to
schedule, for example, the sequence:
1. data fork
2. barrier
3. directory
If the data fork scrub is clean, then the kernel will perform the
directory scrub. If not, the barrier in 2 will exit back to userspace.
The alternative would have been an interface where userspace passes a
pointer to an empty buffer, and the kernel formats that with
xfs_scrub_vecs that tell userspace what it scrubbed and what the outcome
was. With that the kernel would have to communicate that the buffer
needed to have been at least X size, even though for our cases
XFS_SCRUB_TYPE_NR + 2 would always be enough.
Compared to that, this design keeps all the dependency policy and
ordering logic in userspace where it already resides instead of
duplicating it in the kernel. The downside of that is that it needs the
barrier logic.
When running fstests in "rebuild all metadata after each test" mode, I
observed a 10% reduction in runtime due to fewer transitions across the
system call boundary.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
2024-04-22 16:48:25 +00:00
|
|
|
case XFS_IOC_SCRUBV_METADATA:
|
|
|
|
return xfs_ioc_scrubv_metadata(filp, arg);
|
2017-10-18 04:37:34 +00:00
|
|
|
case XFS_IOC_SCRUB_METADATA:
|
2021-03-22 16:51:51 +00:00
|
|
|
return xfs_ioc_scrub_metadata(filp, arg);
|
2017-10-18 04:37:34 +00:00
|
|
|
|
2008-04-18 01:44:03 +00:00
|
|
|
case XFS_IOC_FD_TO_HANDLE:
|
|
|
|
case XFS_IOC_PATH_TO_HANDLE:
|
2008-11-26 03:20:06 +00:00
|
|
|
case XFS_IOC_PATH_TO_FSHANDLE: {
|
|
|
|
xfs_fsop_handlereq_t hreq;
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2008-11-26 03:20:06 +00:00
|
|
|
if (copy_from_user(&hreq, arg, sizeof(hreq)))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2008-11-26 03:20:06 +00:00
|
|
|
return xfs_find_handle(cmd, &hreq);
|
|
|
|
}
|
|
|
|
case XFS_IOC_OPEN_BY_HANDLE: {
|
|
|
|
xfs_fsop_handlereq_t hreq;
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2008-11-26 03:20:06 +00:00
|
|
|
if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2009-01-19 01:02:57 +00:00
|
|
|
return xfs_open_by_handle(filp, &hreq);
|
2008-11-26 03:20:06 +00:00
|
|
|
}
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2008-11-26 03:20:06 +00:00
|
|
|
case XFS_IOC_READLINK_BY_HANDLE: {
|
|
|
|
xfs_fsop_handlereq_t hreq;
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2008-11-26 03:20:06 +00:00
|
|
|
if (copy_from_user(&hreq, arg, sizeof(xfs_fsop_handlereq_t)))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2009-01-19 01:02:57 +00:00
|
|
|
return xfs_readlink_by_handle(filp, &hreq);
|
2008-11-26 03:20:06 +00:00
|
|
|
}
|
2008-04-18 01:44:03 +00:00
|
|
|
case XFS_IOC_ATTRLIST_BY_HANDLE:
|
2009-01-19 01:02:57 +00:00
|
|
|
return xfs_attrlist_by_handle(filp, arg);
|
2008-04-18 01:44:03 +00:00
|
|
|
|
|
|
|
case XFS_IOC_ATTRMULTI_BY_HANDLE:
|
2009-01-19 01:02:57 +00:00
|
|
|
return xfs_attrmulti_by_handle(filp, arg);
|
2008-04-18 01:44:03 +00:00
|
|
|
|
|
|
|
case XFS_IOC_SWAPEXT: {
|
2008-11-26 03:20:06 +00:00
|
|
|
struct xfs_swapext sxp;
|
|
|
|
|
|
|
|
if (copy_from_user(&sxp, arg, sizeof(xfs_swapext_t)))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2012-06-12 14:20:39 +00:00
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2013-08-12 10:49:48 +00:00
|
|
|
error = xfs_ioc_swapext(&sxp);
|
2012-06-12 14:20:39 +00:00
|
|
|
mnt_drop_write_file(filp);
|
2014-06-25 04:58:08 +00:00
|
|
|
return error;
|
2008-04-18 01:44:03 +00:00
|
|
|
}
|
|
|
|
|
2023-12-04 17:40:55 +00:00
|
|
|
case XFS_IOC_FSCOUNTS:
|
|
|
|
return xfs_ioctl_fs_counts(mp, arg);
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2023-12-04 17:40:54 +00:00
|
|
|
case XFS_IOC_SET_RESBLKS:
|
|
|
|
case XFS_IOC_GET_RESBLKS:
|
|
|
|
return xfs_ioctl_getset_resblocks(filp, cmd, arg);
|
2008-04-18 01:44:03 +00:00
|
|
|
|
|
|
|
case XFS_IOC_FSGROWFSDATA: {
|
2021-02-03 02:24:06 +00:00
|
|
|
struct xfs_growfs_data in;
|
2008-04-18 01:44:03 +00:00
|
|
|
|
|
|
|
if (copy_from_user(&in, arg, sizeof(in)))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2012-06-12 14:20:39 +00:00
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2008-04-18 01:44:03 +00:00
|
|
|
error = xfs_growfs_data(mp, &in);
|
2012-06-12 14:20:39 +00:00
|
|
|
mnt_drop_write_file(filp);
|
2014-06-25 04:58:08 +00:00
|
|
|
return error;
|
2008-04-18 01:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_FSGROWFSLOG: {
|
2021-02-03 02:24:06 +00:00
|
|
|
struct xfs_growfs_log in;
|
2008-04-18 01:44:03 +00:00
|
|
|
|
|
|
|
if (copy_from_user(&in, arg, sizeof(in)))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2012-06-12 14:20:39 +00:00
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2008-04-18 01:44:03 +00:00
|
|
|
error = xfs_growfs_log(mp, &in);
|
2012-06-12 14:20:39 +00:00
|
|
|
mnt_drop_write_file(filp);
|
2014-06-25 04:58:08 +00:00
|
|
|
return error;
|
2008-04-18 01:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_FSGROWFSRT: {
|
|
|
|
xfs_growfs_rt_t in;
|
|
|
|
|
|
|
|
if (copy_from_user(&in, arg, sizeof(in)))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2012-06-12 14:20:39 +00:00
|
|
|
error = mnt_want_write_file(filp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2008-04-18 01:44:03 +00:00
|
|
|
error = xfs_growfs_rt(mp, &in);
|
2012-06-12 14:20:39 +00:00
|
|
|
mnt_drop_write_file(filp);
|
2014-06-25 04:58:08 +00:00
|
|
|
return error;
|
2008-04-18 01:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_GOINGDOWN: {
|
2017-06-16 18:00:05 +00:00
|
|
|
uint32_t in;
|
2008-04-18 01:44:03 +00:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
2017-06-16 18:00:05 +00:00
|
|
|
if (get_user(in, (uint32_t __user *)arg))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2014-06-25 04:58:08 +00:00
|
|
|
return xfs_fs_goingdown(mp, in);
|
2008-04-18 01:44:03 +00: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 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2017-06-21 00:54:46 +00:00
|
|
|
return xfs_errortag_add(mp, in.errtag);
|
2008-04-18 01:44:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case XFS_IOC_ERROR_CLEARALL:
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
2017-06-21 00:54:46 +00:00
|
|
|
return xfs_errortag_clearall(mp);
|
2008-04-18 01:44:03 +00:00
|
|
|
|
2012-11-07 17:21:12 +00:00
|
|
|
case XFS_IOC_FREE_EOFBLOCKS: {
|
2021-06-07 16:34:51 +00:00
|
|
|
struct xfs_fs_eofblocks eofb;
|
|
|
|
struct xfs_icwalk icw;
|
2012-11-07 17:21:12 +00:00
|
|
|
|
2013-08-15 18:08:03 +00:00
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
|
2021-08-19 01:46:52 +00:00
|
|
|
if (xfs_is_readonly(mp))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EROFS;
|
2013-08-15 18:08:03 +00:00
|
|
|
|
2012-11-07 17:21:12 +00:00
|
|
|
if (copy_from_user(&eofb, arg, sizeof(eofb)))
|
2014-06-22 05:04:54 +00:00
|
|
|
return -EFAULT;
|
2012-11-07 17:21:12 +00:00
|
|
|
|
2021-06-07 16:34:51 +00:00
|
|
|
error = xfs_fs_eofblocks_from_user(&eofb, &icw);
|
2013-08-15 18:08:02 +00:00
|
|
|
if (error)
|
2014-06-25 04:58:08 +00:00
|
|
|
return error;
|
2012-11-07 17:21:12 +00:00
|
|
|
|
2021-06-07 16:34:51 +00:00
|
|
|
trace_xfs_ioc_free_eofblocks(mp, &icw, _RET_IP_);
|
2021-01-23 00:48:38 +00:00
|
|
|
|
2020-04-12 20:11:10 +00:00
|
|
|
sb_start_write(mp->m_super);
|
2021-06-07 16:34:51 +00:00
|
|
|
error = xfs_blockgc_free_space(mp, &icw);
|
2020-04-12 20:11:10 +00:00
|
|
|
sb_end_write(mp->m_super);
|
|
|
|
return error;
|
2012-11-07 17:21:12 +00:00
|
|
|
}
|
|
|
|
|
2024-04-15 21:54:14 +00:00
|
|
|
case XFS_IOC_EXCHANGE_RANGE:
|
|
|
|
return xfs_ioc_exchange_range(filp, arg);
|
2024-08-30 22:36:47 +00:00
|
|
|
case XFS_IOC_START_COMMIT:
|
|
|
|
return xfs_ioc_start_commit(filp, arg);
|
|
|
|
case XFS_IOC_COMMIT_RANGE:
|
|
|
|
return xfs_ioc_commit_range(filp, arg);
|
2024-04-15 21:54:14 +00:00
|
|
|
|
2008-04-18 01:44:03 +00:00
|
|
|
default:
|
|
|
|
return -ENOTTY;
|
|
|
|
}
|
|
|
|
}
|