2018-06-06 02:42:14 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2008-06-23 03:34:09 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2008 Christoph Hellwig.
|
|
|
|
* Portions Copyright (C) 2000-2008 Silicon Graphics, Inc.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "xfs.h"
|
2019-06-29 02:25:35 +00:00
|
|
|
#include "xfs_shared.h"
|
2013-10-22 23:51:50 +00:00
|
|
|
#include "xfs_format.h"
|
2013-08-12 10:49:23 +00:00
|
|
|
#include "xfs_log_format.h"
|
2013-10-14 22:17:51 +00:00
|
|
|
#include "xfs_da_format.h"
|
2021-03-22 16:51:54 +00:00
|
|
|
#include "xfs_trans_resv.h"
|
|
|
|
#include "xfs_mount.h"
|
2008-06-23 03:34:09 +00:00
|
|
|
#include "xfs_inode.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_btree.h"
|
2008-06-23 03:34:09 +00:00
|
|
|
#include "xfs_attr.h"
|
2019-11-07 01:19:33 +00:00
|
|
|
#include "xfs_acl.h"
|
2022-05-27 00:33:29 +00:00
|
|
|
#include "xfs_log.h"
|
|
|
|
#include "xfs_xattr.h"
|
2024-04-22 16:48:06 +00:00
|
|
|
#include "xfs_quota.h"
|
2008-06-23 03:34:09 +00:00
|
|
|
|
|
|
|
#include <linux/posix_acl_xattr.h>
|
|
|
|
|
2022-05-27 00:33:29 +00:00
|
|
|
/*
|
|
|
|
* Get permission to use log-assisted atomic exchange of file extents.
|
xfs: only clear log incompat flags at clean unmount
While reviewing the online fsck patchset, someone spied the
xfs_swapext_can_use_without_log_assistance function and wondered why we
go through this inverted-bitmask dance to avoid setting the
XFS_SB_FEAT_INCOMPAT_LOG_SWAPEXT feature.
(The same principles apply to the logged extended attribute update
feature bit in the since-merged LARP series.)
The reason for this dance is that xfs_add_incompat_log_feature is an
expensive operation -- it forces the log, pushes the AIL, and then if
nobody's beaten us to it, sets the feature bit and issues a synchronous
write of the primary superblock. That could be a one-time cost
amortized over the life of the filesystem, but the log quiesce and cover
operations call xfs_clear_incompat_log_features to remove feature bits
opportunistically. On a moderately loaded filesystem this leads to us
cycling those bits on and off over and over, which hurts performance.
Why do we clear the log incompat bits? Back in ~2020 I think Dave and I
had a conversation on IRC[2] about what the log incompat bits represent.
IIRC in that conversation we decided that the log incompat bits protect
unrecovered log items so that old kernels won't try to recover them and
barf. Since a clean log has no protected log items, we could clear the
bits at cover/quiesce time.
As Dave Chinner pointed out in the thread, clearing log incompat bits at
unmount time has positive effects for golden root disk image generator
setups, since the generator could be running a newer kernel than what
gets written to the golden image -- if there are log incompat fields set
in the golden image that was generated by a newer kernel/OS image
builder then the provisioning host cannot mount the filesystem even
though the log is clean and recovery is unnecessary to mount the
filesystem.
Given that it's expensive to set log incompat bits, we really only want
to do that once per bit per mount. Therefore, I propose that we only
clear log incompat bits as part of writing a clean unmount record. Do
this by adding an operational state flag to the xfs mount that guards
whether or not the feature bit clearing can actually take place.
This eliminates the l_incompat_users rwsem that we use to protect a log
cleaning operation from clearing a feature bit that a frontend thread is
trying to set -- this lock adds another way to fail w.r.t. locking. For
the swapext series, I shard that into multiple locks just to work around
the lockdep complaints, and that's fugly.
Link: https://lore.kernel.org/linux-xfs/20240131230043.GA6180@frogsfrogsfrogs/
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2024-04-15 21:54:06 +00:00
|
|
|
* Callers must not be running any transactions or hold any ILOCKs.
|
2022-05-27 00:33:29 +00:00
|
|
|
*/
|
2022-05-27 00:34:04 +00:00
|
|
|
static inline int
|
2022-05-27 00:33:29 +00:00
|
|
|
xfs_attr_grab_log_assist(
|
|
|
|
struct xfs_mount *mp)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
|
xfs: only clear log incompat flags at clean unmount
While reviewing the online fsck patchset, someone spied the
xfs_swapext_can_use_without_log_assistance function and wondered why we
go through this inverted-bitmask dance to avoid setting the
XFS_SB_FEAT_INCOMPAT_LOG_SWAPEXT feature.
(The same principles apply to the logged extended attribute update
feature bit in the since-merged LARP series.)
The reason for this dance is that xfs_add_incompat_log_feature is an
expensive operation -- it forces the log, pushes the AIL, and then if
nobody's beaten us to it, sets the feature bit and issues a synchronous
write of the primary superblock. That could be a one-time cost
amortized over the life of the filesystem, but the log quiesce and cover
operations call xfs_clear_incompat_log_features to remove feature bits
opportunistically. On a moderately loaded filesystem this leads to us
cycling those bits on and off over and over, which hurts performance.
Why do we clear the log incompat bits? Back in ~2020 I think Dave and I
had a conversation on IRC[2] about what the log incompat bits represent.
IIRC in that conversation we decided that the log incompat bits protect
unrecovered log items so that old kernels won't try to recover them and
barf. Since a clean log has no protected log items, we could clear the
bits at cover/quiesce time.
As Dave Chinner pointed out in the thread, clearing log incompat bits at
unmount time has positive effects for golden root disk image generator
setups, since the generator could be running a newer kernel than what
gets written to the golden image -- if there are log incompat fields set
in the golden image that was generated by a newer kernel/OS image
builder then the provisioning host cannot mount the filesystem even
though the log is clean and recovery is unnecessary to mount the
filesystem.
Given that it's expensive to set log incompat bits, we really only want
to do that once per bit per mount. Therefore, I propose that we only
clear log incompat bits as part of writing a clean unmount record. Do
this by adding an operational state flag to the xfs mount that guards
whether or not the feature bit clearing can actually take place.
This eliminates the l_incompat_users rwsem that we use to protect a log
cleaning operation from clearing a feature bit that a frontend thread is
trying to set -- this lock adds another way to fail w.r.t. locking. For
the swapext series, I shard that into multiple locks just to work around
the lockdep complaints, and that's fugly.
Link: https://lore.kernel.org/linux-xfs/20240131230043.GA6180@frogsfrogsfrogs/
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2024-04-15 21:54:06 +00:00
|
|
|
/* xattr update log intent items are already enabled */
|
2024-04-22 16:47:25 +00:00
|
|
|
if (xfs_is_using_logged_xattrs(mp))
|
2022-05-27 00:33:29 +00:00
|
|
|
return 0;
|
|
|
|
|
2023-09-11 15:39:09 +00:00
|
|
|
/*
|
|
|
|
* Check if the filesystem featureset is new enough to set this log
|
|
|
|
* incompat feature bit. Strictly speaking, the minimum requirement is
|
|
|
|
* a V5 filesystem for the superblock field, but we'll require rmap
|
|
|
|
* or reflink to avoid having to deal with really old kernels.
|
|
|
|
*/
|
xfs: only clear log incompat flags at clean unmount
While reviewing the online fsck patchset, someone spied the
xfs_swapext_can_use_without_log_assistance function and wondered why we
go through this inverted-bitmask dance to avoid setting the
XFS_SB_FEAT_INCOMPAT_LOG_SWAPEXT feature.
(The same principles apply to the logged extended attribute update
feature bit in the since-merged LARP series.)
The reason for this dance is that xfs_add_incompat_log_feature is an
expensive operation -- it forces the log, pushes the AIL, and then if
nobody's beaten us to it, sets the feature bit and issues a synchronous
write of the primary superblock. That could be a one-time cost
amortized over the life of the filesystem, but the log quiesce and cover
operations call xfs_clear_incompat_log_features to remove feature bits
opportunistically. On a moderately loaded filesystem this leads to us
cycling those bits on and off over and over, which hurts performance.
Why do we clear the log incompat bits? Back in ~2020 I think Dave and I
had a conversation on IRC[2] about what the log incompat bits represent.
IIRC in that conversation we decided that the log incompat bits protect
unrecovered log items so that old kernels won't try to recover them and
barf. Since a clean log has no protected log items, we could clear the
bits at cover/quiesce time.
As Dave Chinner pointed out in the thread, clearing log incompat bits at
unmount time has positive effects for golden root disk image generator
setups, since the generator could be running a newer kernel than what
gets written to the golden image -- if there are log incompat fields set
in the golden image that was generated by a newer kernel/OS image
builder then the provisioning host cannot mount the filesystem even
though the log is clean and recovery is unnecessary to mount the
filesystem.
Given that it's expensive to set log incompat bits, we really only want
to do that once per bit per mount. Therefore, I propose that we only
clear log incompat bits as part of writing a clean unmount record. Do
this by adding an operational state flag to the xfs mount that guards
whether or not the feature bit clearing can actually take place.
This eliminates the l_incompat_users rwsem that we use to protect a log
cleaning operation from clearing a feature bit that a frontend thread is
trying to set -- this lock adds another way to fail w.r.t. locking. For
the swapext series, I shard that into multiple locks just to work around
the lockdep complaints, and that's fugly.
Link: https://lore.kernel.org/linux-xfs/20240131230043.GA6180@frogsfrogsfrogs/
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2024-04-15 21:54:06 +00:00
|
|
|
if (!xfs_has_reflink(mp) && !xfs_has_rmapbt(mp))
|
|
|
|
return -EOPNOTSUPP;
|
2023-09-11 15:39:09 +00:00
|
|
|
|
2022-05-27 00:33:29 +00:00
|
|
|
/* Enable log-assisted xattrs. */
|
|
|
|
error = xfs_add_incompat_log_feature(mp,
|
|
|
|
XFS_SB_FEAT_INCOMPAT_LOG_XATTRS);
|
|
|
|
if (error)
|
xfs: only clear log incompat flags at clean unmount
While reviewing the online fsck patchset, someone spied the
xfs_swapext_can_use_without_log_assistance function and wondered why we
go through this inverted-bitmask dance to avoid setting the
XFS_SB_FEAT_INCOMPAT_LOG_SWAPEXT feature.
(The same principles apply to the logged extended attribute update
feature bit in the since-merged LARP series.)
The reason for this dance is that xfs_add_incompat_log_feature is an
expensive operation -- it forces the log, pushes the AIL, and then if
nobody's beaten us to it, sets the feature bit and issues a synchronous
write of the primary superblock. That could be a one-time cost
amortized over the life of the filesystem, but the log quiesce and cover
operations call xfs_clear_incompat_log_features to remove feature bits
opportunistically. On a moderately loaded filesystem this leads to us
cycling those bits on and off over and over, which hurts performance.
Why do we clear the log incompat bits? Back in ~2020 I think Dave and I
had a conversation on IRC[2] about what the log incompat bits represent.
IIRC in that conversation we decided that the log incompat bits protect
unrecovered log items so that old kernels won't try to recover them and
barf. Since a clean log has no protected log items, we could clear the
bits at cover/quiesce time.
As Dave Chinner pointed out in the thread, clearing log incompat bits at
unmount time has positive effects for golden root disk image generator
setups, since the generator could be running a newer kernel than what
gets written to the golden image -- if there are log incompat fields set
in the golden image that was generated by a newer kernel/OS image
builder then the provisioning host cannot mount the filesystem even
though the log is clean and recovery is unnecessary to mount the
filesystem.
Given that it's expensive to set log incompat bits, we really only want
to do that once per bit per mount. Therefore, I propose that we only
clear log incompat bits as part of writing a clean unmount record. Do
this by adding an operational state flag to the xfs mount that guards
whether or not the feature bit clearing can actually take place.
This eliminates the l_incompat_users rwsem that we use to protect a log
cleaning operation from clearing a feature bit that a frontend thread is
trying to set -- this lock adds another way to fail w.r.t. locking. For
the swapext series, I shard that into multiple locks just to work around
the lockdep complaints, and that's fugly.
Link: https://lore.kernel.org/linux-xfs/20240131230043.GA6180@frogsfrogsfrogs/
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2024-04-15 21:54:06 +00:00
|
|
|
return error;
|
2024-04-22 16:47:25 +00:00
|
|
|
xfs_set_using_logged_xattrs(mp);
|
2022-05-27 00:33:29 +00:00
|
|
|
|
|
|
|
xfs_warn_mount(mp, XFS_OPSTATE_WARNED_LARP,
|
|
|
|
"EXPERIMENTAL logged extended attributes feature in use. Use at your own risk!");
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2008-06-23 03:34:09 +00:00
|
|
|
|
xfs: fix TOCTOU race involving the new logged xattrs control knob
I found a race involving the larp control knob, aka the debugging knob
that lets developers enable logging of extended attribute updates:
Thread 1 Thread 2
echo 0 > /sys/fs/xfs/debug/larp
setxattr(REPLACE)
xfs_has_larp (returns false)
xfs_attr_set
echo 1 > /sys/fs/xfs/debug/larp
xfs_attr_defer_replace
xfs_attr_init_replace_state
xfs_has_larp (returns true)
xfs_attr_init_remove_state
<oops, wrong DAS state!>
This isn't a particularly severe problem right now because xattr logging
is only enabled when CONFIG_XFS_DEBUG=y, and developers *should* know
what they're doing.
However, the eventual intent is that callers should be able to ask for
the assistance of the log in persisting xattr updates. This capability
might not be required for /all/ callers, which means that dynamic
control must work correctly. Once an xattr update has decided whether
or not to use logged xattrs, it needs to stay in that mode until the end
of the operation regardless of what subsequent parallel operations might
do.
Therefore, it is an error to continue sampling xfs_globals.larp once
xfs_attr_change has made a decision about larp, and it was not correct
for me to have told Allison that ->create_intent functions can sample
the global log incompat feature bitfield to decide to elide a log item.
Instead, create a new op flag for the xfs_da_args structure, and convert
all other callers of xfs_has_larp and xfs_sb_version_haslogxattrs within
the attr update state machine to look for the operations flag.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2022-06-06 01:51:22 +00:00
|
|
|
static inline bool
|
|
|
|
xfs_attr_want_log_assist(
|
|
|
|
struct xfs_mount *mp)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
/* Logged xattrs require a V5 super for log_incompat */
|
|
|
|
return xfs_has_crc(mp) && xfs_globals.larp;
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-05-27 00:34:04 +00:00
|
|
|
/*
|
|
|
|
* Set or remove an xattr, having grabbed the appropriate logging resources
|
2024-04-22 16:48:06 +00:00
|
|
|
* prior to calling libxfs. Callers of this function are only required to
|
|
|
|
* initialize the inode, attr_filter, name, namelen, value, and valuelen fields
|
|
|
|
* of @args.
|
2022-05-27 00:34:04 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
xfs_attr_change(
|
2024-04-22 16:47:21 +00:00
|
|
|
struct xfs_da_args *args,
|
|
|
|
enum xfs_attr_update op)
|
2022-05-27 00:34:04 +00:00
|
|
|
{
|
|
|
|
struct xfs_mount *mp = args->dp->i_mount;
|
|
|
|
int error;
|
|
|
|
|
2024-04-22 16:48:06 +00:00
|
|
|
if (xfs_is_shutdown(mp))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
error = xfs_qm_dqattach(args->dp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have no control over the attribute names that userspace passes us
|
|
|
|
* to remove, so we have to allow the name lookup prior to attribute
|
|
|
|
* removal to fail as well.
|
|
|
|
*/
|
|
|
|
args->op_flags = XFS_DA_OP_OKNOENT;
|
xfs: fix TOCTOU race involving the new logged xattrs control knob
I found a race involving the larp control knob, aka the debugging knob
that lets developers enable logging of extended attribute updates:
Thread 1 Thread 2
echo 0 > /sys/fs/xfs/debug/larp
setxattr(REPLACE)
xfs_has_larp (returns false)
xfs_attr_set
echo 1 > /sys/fs/xfs/debug/larp
xfs_attr_defer_replace
xfs_attr_init_replace_state
xfs_has_larp (returns true)
xfs_attr_init_remove_state
<oops, wrong DAS state!>
This isn't a particularly severe problem right now because xattr logging
is only enabled when CONFIG_XFS_DEBUG=y, and developers *should* know
what they're doing.
However, the eventual intent is that callers should be able to ask for
the assistance of the log in persisting xattr updates. This capability
might not be required for /all/ callers, which means that dynamic
control must work correctly. Once an xattr update has decided whether
or not to use logged xattrs, it needs to stay in that mode until the end
of the operation regardless of what subsequent parallel operations might
do.
Therefore, it is an error to continue sampling xfs_globals.larp once
xfs_attr_change has made a decision about larp, and it was not correct
for me to have told Allison that ->create_intent functions can sample
the global log incompat feature bitfield to decide to elide a log item.
Instead, create a new op flag for the xfs_da_args structure, and convert
all other callers of xfs_has_larp and xfs_sb_version_haslogxattrs within
the attr update state machine to look for the operations flag.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2022-06-06 01:51:22 +00:00
|
|
|
|
|
|
|
if (xfs_attr_want_log_assist(mp)) {
|
2022-05-27 00:34:04 +00:00
|
|
|
error = xfs_attr_grab_log_assist(mp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
xfs: fix TOCTOU race involving the new logged xattrs control knob
I found a race involving the larp control knob, aka the debugging knob
that lets developers enable logging of extended attribute updates:
Thread 1 Thread 2
echo 0 > /sys/fs/xfs/debug/larp
setxattr(REPLACE)
xfs_has_larp (returns false)
xfs_attr_set
echo 1 > /sys/fs/xfs/debug/larp
xfs_attr_defer_replace
xfs_attr_init_replace_state
xfs_has_larp (returns true)
xfs_attr_init_remove_state
<oops, wrong DAS state!>
This isn't a particularly severe problem right now because xattr logging
is only enabled when CONFIG_XFS_DEBUG=y, and developers *should* know
what they're doing.
However, the eventual intent is that callers should be able to ask for
the assistance of the log in persisting xattr updates. This capability
might not be required for /all/ callers, which means that dynamic
control must work correctly. Once an xattr update has decided whether
or not to use logged xattrs, it needs to stay in that mode until the end
of the operation regardless of what subsequent parallel operations might
do.
Therefore, it is an error to continue sampling xfs_globals.larp once
xfs_attr_change has made a decision about larp, and it was not correct
for me to have told Allison that ->create_intent functions can sample
the global log incompat feature bitfield to decide to elide a log item.
Instead, create a new op flag for the xfs_da_args structure, and convert
all other callers of xfs_has_larp and xfs_sb_version_haslogxattrs within
the attr update state machine to look for the operations flag.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2022-06-06 01:51:22 +00:00
|
|
|
args->op_flags |= XFS_DA_OP_LOGGED;
|
2022-05-27 00:34:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-22 16:48:06 +00:00
|
|
|
args->owner = args->dp->i_ino;
|
|
|
|
args->geo = mp->m_attr_geo;
|
|
|
|
args->whichfork = XFS_ATTR_FORK;
|
|
|
|
xfs_attr_sethash(args);
|
|
|
|
|
2024-07-23 17:26:10 +00:00
|
|
|
/*
|
|
|
|
* Some xattrs must be resistant to allocation failure at ENOSPC, e.g.
|
|
|
|
* creating an inode with ACLs or security attributes requires the
|
|
|
|
* allocation of the xattr holding that information to succeed. Hence
|
|
|
|
* we allow xattrs in the VFS TRUSTED, SYSTEM, POSIX_ACL and SECURITY
|
|
|
|
* (LSM xattr) namespaces to dip into the reserve block pool to allow
|
|
|
|
* manipulation of these xattrs when at ENOSPC. These VFS xattr
|
|
|
|
* namespaces translate to the XFS_ATTR_ROOT and XFS_ATTR_SECURE on-disk
|
|
|
|
* namespaces.
|
|
|
|
*
|
|
|
|
* For most of these cases, these special xattrs will fit in the inode
|
|
|
|
* itself and so consume no extra space or only require temporary extra
|
|
|
|
* space while an overwrite is being made. Hence the use of the reserved
|
|
|
|
* pool is largely to avoid the worst case reservation from preventing
|
|
|
|
* the xattr from being created at ENOSPC.
|
|
|
|
*/
|
|
|
|
return xfs_attr_set(args, op,
|
|
|
|
args->attr_filter & (XFS_ATTR_ROOT | XFS_ATTR_SECURE));
|
2022-05-27 00:34:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-06-23 03:34:09 +00:00
|
|
|
static int
|
2016-04-11 00:48:24 +00:00
|
|
|
xfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused,
|
|
|
|
struct inode *inode, const char *name, void *value, size_t size)
|
2008-06-23 03:34:09 +00:00
|
|
|
{
|
2020-02-27 01:30:34 +00:00
|
|
|
struct xfs_da_args args = {
|
|
|
|
.dp = XFS_I(inode),
|
2020-02-27 01:30:42 +00:00
|
|
|
.attr_filter = handler->flags,
|
2020-02-27 01:30:34 +00:00
|
|
|
.name = name,
|
|
|
|
.namelen = strlen(name),
|
|
|
|
.value = value,
|
|
|
|
.valuelen = size,
|
|
|
|
};
|
|
|
|
int error;
|
2008-06-23 03:34:09 +00:00
|
|
|
|
2023-12-15 18:03:35 +00:00
|
|
|
if (xfs_ifork_zapped(XFS_I(inode), XFS_ATTR_FORK))
|
|
|
|
return -EIO;
|
|
|
|
|
2020-02-27 01:30:34 +00:00
|
|
|
error = xfs_attr_get(&args);
|
2008-06-23 03:34:09 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
2020-02-27 01:30:34 +00:00
|
|
|
return args.valuelen;
|
2008-06-23 03:34:09 +00:00
|
|
|
}
|
|
|
|
|
2024-04-22 16:47:21 +00:00
|
|
|
static inline enum xfs_attr_update
|
|
|
|
xfs_xattr_flags_to_op(
|
2024-04-22 16:47:22 +00:00
|
|
|
int flags,
|
|
|
|
const void *value)
|
2024-04-22 16:47:21 +00:00
|
|
|
{
|
2024-04-22 16:47:22 +00:00
|
|
|
if (!value)
|
|
|
|
return XFS_ATTRUPDATE_REMOVE;
|
2024-04-22 16:47:21 +00:00
|
|
|
if (flags & XATTR_CREATE)
|
|
|
|
return XFS_ATTRUPDATE_CREATE;
|
|
|
|
if (flags & XATTR_REPLACE)
|
|
|
|
return XFS_ATTRUPDATE_REPLACE;
|
2024-04-22 16:47:22 +00:00
|
|
|
return XFS_ATTRUPDATE_UPSERT;
|
2024-04-22 16:47:21 +00:00
|
|
|
}
|
|
|
|
|
2008-06-23 03:34:09 +00:00
|
|
|
static int
|
2021-01-21 13:19:27 +00:00
|
|
|
xfs_xattr_set(const struct xattr_handler *handler,
|
2023-01-13 11:49:23 +00:00
|
|
|
struct mnt_idmap *idmap, struct dentry *unused,
|
2021-01-21 13:19:27 +00:00
|
|
|
struct inode *inode, const char *name, const void *value,
|
|
|
|
size_t size, int flags)
|
2008-06-23 03:34:09 +00:00
|
|
|
{
|
2020-02-27 01:30:33 +00:00
|
|
|
struct xfs_da_args args = {
|
|
|
|
.dp = XFS_I(inode),
|
2020-02-27 01:30:42 +00:00
|
|
|
.attr_filter = handler->flags,
|
2020-02-27 01:30:33 +00:00
|
|
|
.name = name,
|
|
|
|
.namelen = strlen(name),
|
|
|
|
.value = (void *)value,
|
|
|
|
.valuelen = size,
|
|
|
|
};
|
2015-11-03 01:40:59 +00:00
|
|
|
int error;
|
2008-06-23 03:34:09 +00:00
|
|
|
|
2024-04-22 16:47:22 +00:00
|
|
|
error = xfs_attr_change(&args, xfs_xattr_flags_to_op(flags, value));
|
2020-02-27 01:30:42 +00:00
|
|
|
if (!error && (handler->flags & XFS_ATTR_ROOT))
|
2020-02-27 01:30:41 +00:00
|
|
|
xfs_forget_acl(inode, name);
|
2015-11-03 01:40:59 +00:00
|
|
|
return error;
|
2008-06-23 03:34:09 +00:00
|
|
|
}
|
|
|
|
|
2010-05-14 00:53:20 +00:00
|
|
|
static const struct xattr_handler xfs_xattr_user_handler = {
|
2008-06-23 03:34:09 +00:00
|
|
|
.prefix = XATTR_USER_PREFIX,
|
2009-11-13 09:52:56 +00:00
|
|
|
.flags = 0, /* no flags implies user namespace */
|
|
|
|
.get = xfs_xattr_get,
|
|
|
|
.set = xfs_xattr_set,
|
2008-06-23 03:34:09 +00:00
|
|
|
};
|
|
|
|
|
2010-05-14 00:53:20 +00:00
|
|
|
static const struct xattr_handler xfs_xattr_trusted_handler = {
|
2008-06-23 03:34:09 +00:00
|
|
|
.prefix = XATTR_TRUSTED_PREFIX,
|
2020-02-27 01:30:42 +00:00
|
|
|
.flags = XFS_ATTR_ROOT,
|
2009-11-13 09:52:56 +00:00
|
|
|
.get = xfs_xattr_get,
|
|
|
|
.set = xfs_xattr_set,
|
2008-06-23 03:34:09 +00:00
|
|
|
};
|
|
|
|
|
2010-05-14 00:53:20 +00:00
|
|
|
static const struct xattr_handler xfs_xattr_security_handler = {
|
2008-06-23 03:34:09 +00:00
|
|
|
.prefix = XATTR_SECURITY_PREFIX,
|
2020-02-27 01:30:42 +00:00
|
|
|
.flags = XFS_ATTR_SECURE,
|
2009-11-13 09:52:56 +00:00
|
|
|
.get = xfs_xattr_get,
|
|
|
|
.set = xfs_xattr_set,
|
2008-06-23 03:34:09 +00:00
|
|
|
};
|
|
|
|
|
2023-09-30 05:00:30 +00:00
|
|
|
const struct xattr_handler * const xfs_xattr_handlers[] = {
|
2008-06-23 03:34:09 +00:00
|
|
|
&xfs_xattr_user_handler,
|
|
|
|
&xfs_xattr_trusted_handler,
|
|
|
|
&xfs_xattr_security_handler,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2016-12-05 01:32:14 +00:00
|
|
|
static void
|
2015-12-02 13:44:40 +00:00
|
|
|
__xfs_xattr_put_listent(
|
2010-01-19 23:47:48 +00:00
|
|
|
struct xfs_attr_list_context *context,
|
2015-12-02 13:44:40 +00:00
|
|
|
char *prefix,
|
|
|
|
int prefix_len,
|
|
|
|
unsigned char *name,
|
|
|
|
int namelen)
|
2008-06-23 03:34:09 +00:00
|
|
|
{
|
|
|
|
char *offset;
|
|
|
|
int arraytop;
|
|
|
|
|
2019-02-13 19:15:17 +00:00
|
|
|
if (context->count < 0 || context->seen_enough)
|
|
|
|
return;
|
|
|
|
|
2020-02-27 01:30:37 +00:00
|
|
|
if (!context->buffer)
|
2015-12-02 13:44:40 +00:00
|
|
|
goto compute_size;
|
2008-06-23 03:34:09 +00:00
|
|
|
|
|
|
|
arraytop = context->count + prefix_len + namelen + 1;
|
|
|
|
if (arraytop > context->firstu) {
|
|
|
|
context->count = -1; /* insufficient space */
|
2016-09-13 21:40:35 +00:00
|
|
|
context->seen_enough = 1;
|
2016-12-05 01:32:14 +00:00
|
|
|
return;
|
2008-06-23 03:34:09 +00:00
|
|
|
}
|
2020-02-27 01:30:37 +00:00
|
|
|
offset = context->buffer + context->count;
|
2022-11-29 01:24:42 +00:00
|
|
|
memcpy(offset, prefix, prefix_len);
|
2008-06-23 03:34:09 +00:00
|
|
|
offset += prefix_len;
|
2010-01-19 23:47:48 +00:00
|
|
|
strncpy(offset, (char *)name, namelen); /* real name */
|
2008-06-23 03:34:09 +00:00
|
|
|
offset += namelen;
|
|
|
|
*offset = '\0';
|
2015-12-02 13:44:40 +00:00
|
|
|
|
|
|
|
compute_size:
|
2008-06-23 03:34:09 +00:00
|
|
|
context->count += prefix_len + namelen + 1;
|
2016-12-05 01:32:14 +00:00
|
|
|
return;
|
2008-06-23 03:34:09 +00:00
|
|
|
}
|
|
|
|
|
2016-12-05 01:32:14 +00:00
|
|
|
static void
|
2015-12-02 13:44:40 +00:00
|
|
|
xfs_xattr_put_listent(
|
2010-01-19 23:47:48 +00:00
|
|
|
struct xfs_attr_list_context *context,
|
|
|
|
int flags,
|
|
|
|
unsigned char *name,
|
|
|
|
int namelen,
|
2024-04-22 16:47:53 +00:00
|
|
|
void *value,
|
2016-04-05 21:57:32 +00:00
|
|
|
int valuelen)
|
2008-06-23 03:34:09 +00:00
|
|
|
{
|
2015-12-02 13:44:40 +00:00
|
|
|
char *prefix;
|
|
|
|
int prefix_len;
|
2008-06-23 03:34:09 +00:00
|
|
|
|
2015-12-02 13:44:40 +00:00
|
|
|
ASSERT(context->count >= 0);
|
2008-06-23 03:34:09 +00:00
|
|
|
|
2024-04-22 16:47:52 +00:00
|
|
|
/* Don't expose private xattr namespaces. */
|
|
|
|
if (flags & XFS_ATTR_PRIVATE_NSP_MASK)
|
|
|
|
return;
|
|
|
|
|
2015-12-02 13:44:40 +00:00
|
|
|
if (flags & XFS_ATTR_ROOT) {
|
|
|
|
#ifdef CONFIG_XFS_POSIX_ACL
|
|
|
|
if (namelen == SGI_ACL_FILE_SIZE &&
|
|
|
|
strncmp(name, SGI_ACL_FILE,
|
|
|
|
SGI_ACL_FILE_SIZE) == 0) {
|
2016-12-05 01:32:14 +00:00
|
|
|
__xfs_xattr_put_listent(
|
2015-12-02 13:44:40 +00:00
|
|
|
context, XATTR_SYSTEM_PREFIX,
|
|
|
|
XATTR_SYSTEM_PREFIX_LEN,
|
|
|
|
XATTR_POSIX_ACL_ACCESS,
|
|
|
|
strlen(XATTR_POSIX_ACL_ACCESS));
|
|
|
|
} else if (namelen == SGI_ACL_DEFAULT_SIZE &&
|
|
|
|
strncmp(name, SGI_ACL_DEFAULT,
|
|
|
|
SGI_ACL_DEFAULT_SIZE) == 0) {
|
2016-12-05 01:32:14 +00:00
|
|
|
__xfs_xattr_put_listent(
|
2015-12-02 13:44:40 +00:00
|
|
|
context, XATTR_SYSTEM_PREFIX,
|
|
|
|
XATTR_SYSTEM_PREFIX_LEN,
|
|
|
|
XATTR_POSIX_ACL_DEFAULT,
|
|
|
|
strlen(XATTR_POSIX_ACL_DEFAULT));
|
|
|
|
}
|
|
|
|
#endif
|
2008-06-23 03:34:09 +00:00
|
|
|
|
2015-12-02 13:44:40 +00:00
|
|
|
/*
|
|
|
|
* Only show root namespace entries if we are actually allowed to
|
|
|
|
* see them.
|
|
|
|
*/
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2016-12-05 01:32:14 +00:00
|
|
|
return;
|
2015-12-02 13:44:40 +00:00
|
|
|
|
|
|
|
prefix = XATTR_TRUSTED_PREFIX;
|
|
|
|
prefix_len = XATTR_TRUSTED_PREFIX_LEN;
|
|
|
|
} else if (flags & XFS_ATTR_SECURE) {
|
|
|
|
prefix = XATTR_SECURITY_PREFIX;
|
|
|
|
prefix_len = XATTR_SECURITY_PREFIX_LEN;
|
|
|
|
} else {
|
|
|
|
prefix = XATTR_USER_PREFIX;
|
|
|
|
prefix_len = XATTR_USER_PREFIX_LEN;
|
|
|
|
}
|
|
|
|
|
2016-12-05 01:32:14 +00:00
|
|
|
__xfs_xattr_put_listent(context, prefix, prefix_len, name,
|
|
|
|
namelen);
|
|
|
|
return;
|
2008-06-23 03:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t
|
2016-04-05 21:57:18 +00:00
|
|
|
xfs_vn_listxattr(
|
|
|
|
struct dentry *dentry,
|
|
|
|
char *data,
|
|
|
|
size_t size)
|
2008-06-23 03:34:09 +00:00
|
|
|
{
|
|
|
|
struct xfs_attr_list_context context;
|
2016-04-05 21:57:18 +00:00
|
|
|
struct inode *inode = d_inode(dentry);
|
|
|
|
int error;
|
2008-06-23 03:34:09 +00:00
|
|
|
|
2023-12-15 18:03:35 +00:00
|
|
|
if (xfs_ifork_zapped(XFS_I(inode), XFS_ATTR_FORK))
|
|
|
|
return -EIO;
|
|
|
|
|
2008-06-23 03:34:09 +00:00
|
|
|
/*
|
|
|
|
* First read the regular on-disk attributes.
|
|
|
|
*/
|
|
|
|
memset(&context, 0, sizeof(context));
|
|
|
|
context.dp = XFS_I(inode);
|
|
|
|
context.resynch = 1;
|
2020-02-27 01:30:37 +00:00
|
|
|
context.buffer = size ? data : NULL;
|
2008-06-23 03:34:09 +00:00
|
|
|
context.bufsize = size;
|
|
|
|
context.firstu = context.bufsize;
|
2015-12-02 13:44:40 +00:00
|
|
|
context.put_listent = xfs_xattr_put_listent;
|
2008-06-23 03:34:09 +00:00
|
|
|
|
2020-02-27 01:30:39 +00:00
|
|
|
error = xfs_attr_list(&context);
|
2016-04-05 21:57:18 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
2008-06-23 03:34:09 +00:00
|
|
|
if (context.count < 0)
|
|
|
|
return -ERANGE;
|
|
|
|
|
|
|
|
return context.count;
|
|
|
|
}
|