Commit 47d83c19 authored by Chandan Babu R's avatar Chandan Babu R

Merge tag 'pptrs-6.10_2024-04-23' of...

Merge tag 'pptrs-6.10_2024-04-23' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-6.10-mergeC

xfs: Parent Pointers

This is the latest parent pointer attributes for xfs.  The goal of this
patch set is to add a parent pointer attribute to each inode.  The
attribute name containing the parent inode, generation, and directory
offset, while the  attribute value contains the file name.  This feature
will enable future optimizations for online scrub, shrink, nfs handles,
verity, or any other feature that could make use of quickly deriving an
inodes path from the mount point.

Directory parent pointers are stored as namespaced extended attributes
of a file.  Because parent pointers are an indivisible tuple of
(dirent_name, parent_ino, parent_gen) we cannot use the usual attr name
lookup functions to find a parent pointer.  This is solvable by
introducing a new lookup mode that checks both the name and the value of
the xattr.

Therefore, introduce this new name-value lookup mode that's gated on the
XFS_ATTR_PARENT namespace.  This requires the introduction of new
opcodes for the extended attribute update log intent items, which
actually means that parent pointers (itself an INCOMPAT feature) does
not depend on the LOGGED_XATTRS log incompat feature bit.

To reduce collisions on the dirent names of parent pointers, introduce a
new attr hash mode that is the dir2 namehash of the dirent name xor'd
with the parent inode number.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Signed-off-by: default avatarChandan Babu R <chandanbabu@kernel.org>

* tag 'pptrs-6.10_2024-04-23' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux:
  xfs: enable parent pointers
  xfs: drop compatibility minimum log size computations for reflink
  xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res
  xfs: add a incompat feature bit for parent pointers
  xfs: don't remove the attr fork when parent pointers are enabled
  xfs: add parent pointer ioctls
  xfs: split out handle management helpers a bit
  xfs: move handle ioctl code to xfs_handle.c
  xfs: pass the attr value to put_listent when possible
  xfs: don't return XFS_ATTR_PARENT attributes via listxattr
  xfs: Add parent pointers to xfs_cross_rename
  xfs: Add parent pointers to rename
  xfs: remove parent pointers in unlink
  xfs: add parent attributes to symlink
  xfs: add parent attributes to link
  xfs: parent pointer attribute creation
  xfs: create a hashname function for parent pointers
  xfs: extend transaction reservations for parent attributes
  xfs: add parent pointer validator functions
  xfs: Expose init_xattrs in xfs_create_tmpfile
  xfs: record inode generation in xattr update log intent items
  xfs: create attr log item opcodes and formats for parent pointers
  xfs: refactor xfs_is_using_logged_xattrs checks in attr item recovery
  xfs: allow xattr matching on name and value for parent pointers
  xfs: define parent pointer ondisk extended attribute format
  xfs: add parent pointer support to attribute code
  xfs: create a separate hashname function for extended attributes
  xfs: move xfs_attr_defer_add to xfs_attr_item.c
  xfs: check the flags earlier in xfs_attr_match
  xfs: rearrange xfs_attr_match parameters
parents d7d02f75 67ac7091
......@@ -42,6 +42,7 @@ xfs-y += $(addprefix libxfs/, \
xfs_inode_buf.o \
xfs_log_rlimit.o \
xfs_ag_resv.o \
xfs_parent.o \
xfs_rmap.o \
xfs_rmap_btree.o \
xfs_refcount.o \
......@@ -50,6 +51,7 @@ xfs-y += $(addprefix libxfs/, \
xfs_symlink_remote.o \
xfs_trans_inode.o \
xfs_trans_resv.o \
xfs_trans_space.o \
xfs_types.o \
)
# xfs_rtbitmap is shared with libxfs
......@@ -76,6 +78,7 @@ xfs-y += xfs_aops.o \
xfs_fsmap.o \
xfs_fsops.o \
xfs_globals.o \
xfs_handle.o \
xfs_health.o \
xfs_icache.o \
xfs_ioctl.o \
......
......@@ -26,6 +26,7 @@
#include "xfs_trace.h"
#include "xfs_attr_item.h"
#include "xfs_xattr.h"
#include "xfs_parent.h"
struct kmem_cache *xfs_attr_intent_cache;
......@@ -280,7 +281,7 @@ xfs_attr_get(
args->owner = args->dp->i_ino;
args->geo = args->dp->i_mount->m_attr_geo;
args->whichfork = XFS_ATTR_FORK;
args->hashval = xfs_da_hashname(args->name, args->namelen);
xfs_attr_sethash(args);
/* Entirely possible to look up a name which doesn't exist */
args->op_flags = XFS_DA_OP_OKNOENT;
......@@ -415,6 +416,50 @@ xfs_attr_sf_addname(
return error;
}
/* Compute the hash value for a user/root/secure extended attribute */
xfs_dahash_t
xfs_attr_hashname(
const uint8_t *name,
int namelen)
{
return xfs_da_hashname(name, namelen);
}
/* Compute the hash value for any extended attribute from any namespace. */
xfs_dahash_t
xfs_attr_hashval(
struct xfs_mount *mp,
unsigned int attr_flags,
const uint8_t *name,
int namelen,
const void *value,
int valuelen)
{
ASSERT(xfs_attr_check_namespace(attr_flags));
if (attr_flags & XFS_ATTR_PARENT)
return xfs_parent_hashattr(mp, name, namelen, value, valuelen);
return xfs_attr_hashname(name, namelen);
}
/*
* PPTR_REPLACE operations require the caller to set the old and new names and
* values explicitly. Update the canonical fields to the new name and value
* here now that the removal phase has finished.
*/
static void
xfs_attr_update_pptr_replace_args(
struct xfs_da_args *args)
{
ASSERT(args->new_namelen > 0);
args->name = args->new_name;
args->namelen = args->new_namelen;
args->value = args->new_value;
args->valuelen = args->new_valuelen;
xfs_attr_sethash(args);
}
/*
* Handle the state change on completion of a multi-state attr operation.
*
......@@ -435,6 +480,8 @@ xfs_attr_complete_op(
if (!(args->op_flags & XFS_DA_OP_REPLACE))
replace_state = XFS_DAS_DONE;
else if (xfs_attr_intent_op(attr) == XFS_ATTRI_OP_FLAGS_PPTR_REPLACE)
xfs_attr_update_pptr_replace_args(args);
args->op_flags &= ~XFS_DA_OP_REPLACE;
args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
......@@ -901,37 +948,6 @@ xfs_attr_lookup(
return error;
}
static void
xfs_attr_defer_add(
struct xfs_da_args *args,
unsigned int op_flags)
{
struct xfs_attr_intent *new;
new = kmem_cache_zalloc(xfs_attr_intent_cache,
GFP_KERNEL | __GFP_NOFAIL);
new->xattri_op_flags = op_flags;
new->xattri_da_args = args;
switch (op_flags) {
case XFS_ATTRI_OP_FLAGS_SET:
new->xattri_dela_state = xfs_attr_init_add_state(args);
break;
case XFS_ATTRI_OP_FLAGS_REPLACE:
new->xattri_dela_state = xfs_attr_init_replace_state(args);
break;
case XFS_ATTRI_OP_FLAGS_REMOVE:
new->xattri_dela_state = xfs_attr_init_remove_state(args);
break;
default:
ASSERT(0);
}
xfs_defer_add(args->trans, &new->xattri_list, &xfs_attr_defer_type);
trace_xfs_attr_defer_add(new->xattri_dela_state, args->dp);
}
int
xfs_attr_set(
struct xfs_da_args *args,
......@@ -956,7 +972,7 @@ xfs_attr_set(
args->owner = args->dp->i_ino;
args->geo = mp->m_attr_geo;
args->whichfork = XFS_ATTR_FORK;
args->hashval = xfs_da_hashname(args->name, args->namelen);
xfs_attr_sethash(args);
/*
* We have no control over the attribute names that userspace passes us
......@@ -1021,14 +1037,14 @@ xfs_attr_set(
case -EEXIST:
if (op == XFS_ATTRUPDATE_REMOVE) {
/* if no value, we are performing a remove operation */
xfs_attr_defer_add(args, XFS_ATTRI_OP_FLAGS_REMOVE);
xfs_attr_defer_add(args, XFS_ATTR_DEFER_REMOVE);
break;
}
/* Pure create fails if the attr already exists */
if (op == XFS_ATTRUPDATE_CREATE)
goto out_trans_cancel;
xfs_attr_defer_add(args, XFS_ATTRI_OP_FLAGS_REPLACE);
xfs_attr_defer_add(args, XFS_ATTR_DEFER_REPLACE);
break;
case -ENOATTR:
/* Can't remove what isn't there. */
......@@ -1038,7 +1054,7 @@ xfs_attr_set(
/* Pure replace fails if no existing attr to replace. */
if (op == XFS_ATTRUPDATE_REPLACE)
goto out_trans_cancel;
xfs_attr_defer_add(args, XFS_ATTRI_OP_FLAGS_SET);
xfs_attr_defer_add(args, XFS_ATTR_DEFER_SET);
break;
default:
goto out_trans_cancel;
......@@ -1556,6 +1572,10 @@ xfs_attr_namecheck(
if (length >= MAXNAMELEN)
return false;
/* Parent pointers have their own validation. */
if (attr_flags & XFS_ATTR_PARENT)
return xfs_parent_namecheck(attr_flags, name, length);
/* There shouldn't be any nulls here */
return !memchr(name, 0, length);
}
......
......@@ -47,8 +47,9 @@ struct xfs_attrlist_cursor_kern {
/* void; state communicated via *context */
typedef void (*put_listent_func_t)(struct xfs_attr_list_context *, int,
unsigned char *, int, int);
typedef void (*put_listent_func_t)(struct xfs_attr_list_context *context,
int flags, unsigned char *name, int namelen, void *value,
int valuelen);
struct xfs_attr_list_context {
struct xfs_trans *tp;
......@@ -510,8 +511,8 @@ struct xfs_attr_intent {
struct xfs_da_args *xattri_da_args;
/*
* Shared buffer containing the attr name and value so that the logging
* code can share large memory buffers between log items.
* Shared buffer containing the attr name, new name, and value so that
* the logging code can share large memory buffers between log items.
*/
struct xfs_attri_log_nameval *xattri_nameval;
......@@ -628,6 +629,20 @@ xfs_attr_init_replace_state(struct xfs_da_args *args)
return xfs_attr_init_add_state(args);
}
xfs_dahash_t xfs_attr_hashname(const uint8_t *name, int namelen);
xfs_dahash_t xfs_attr_hashval(struct xfs_mount *mp, unsigned int attr_flags,
const uint8_t *name, int namelen, const void *value,
int valuelen);
/* Set the hash value for any extended attribute from any namespace. */
static inline void xfs_attr_sethash(struct xfs_da_args *args)
{
args->hashval = xfs_attr_hashval(args->dp->i_mount, args->attr_filter,
args->name, args->namelen,
args->value, args->valuelen);
}
extern struct kmem_cache *xfs_attr_intent_cache;
int __init xfs_attr_intent_init_cache(void);
void xfs_attr_intent_destroy_cache(void);
......
......@@ -507,28 +507,57 @@ xfs_attr3_leaf_read(
* INCOMPLETE flag will not be set in attr->attr_filter, but rather
* XFS_DA_OP_RECOVERY will be set in args->op_flags.
*/
static inline unsigned int xfs_attr_match_mask(const struct xfs_da_args *args)
{
if (args->op_flags & XFS_DA_OP_RECOVERY)
return XFS_ATTR_NSP_ONDISK_MASK;
return XFS_ATTR_NSP_ONDISK_MASK | XFS_ATTR_INCOMPLETE;
}
static inline bool
xfs_attr_parent_match(
const struct xfs_da_args *args,
const void *value,
unsigned int valuelen)
{
ASSERT(args->value != NULL);
/* Parent pointers do not use remote values */
if (!value)
return false;
/*
* The only value we support is a parent rec. However, we'll accept
* any valuelen so that offline repair can delete ATTR_PARENT values
* that are not parent pointers.
*/
if (valuelen != args->valuelen)
return false;
return memcmp(args->value, value, valuelen) == 0;
}
static bool
xfs_attr_match(
struct xfs_da_args *args,
uint8_t namelen,
unsigned char *name,
int flags)
unsigned int attr_flags,
const unsigned char *name,
unsigned int namelen,
const void *value,
unsigned int valuelen)
{
unsigned int mask = xfs_attr_match_mask(args);
if (args->namelen != namelen)
return false;
if ((args->attr_filter & mask) != (attr_flags & mask))
return false;
if (memcmp(args->name, name, namelen) != 0)
return false;
/* Recovery ignores the INCOMPLETE flag. */
if ((args->op_flags & XFS_DA_OP_RECOVERY) &&
args->attr_filter == (flags & XFS_ATTR_NSP_ONDISK_MASK))
return true;
if (attr_flags & XFS_ATTR_PARENT)
return xfs_attr_parent_match(args, value, valuelen);
/* All remaining matches need to be filtered by INCOMPLETE state. */
if (args->attr_filter !=
(flags & (XFS_ATTR_NSP_ONDISK_MASK | XFS_ATTR_INCOMPLETE)))
return false;
return true;
}
......@@ -538,6 +567,13 @@ xfs_attr_copy_value(
unsigned char *value,
int valuelen)
{
/*
* Parent pointer lookups require the caller to specify the name and
* value, so don't copy anything.
*/
if (args->attr_filter & XFS_ATTR_PARENT)
return 0;
/*
* No copy if all we have to do is get the length
*/
......@@ -746,8 +782,9 @@ xfs_attr_sf_findname(
for (sfe = xfs_attr_sf_firstentry(sf);
sfe < xfs_attr_sf_endptr(sf);
sfe = xfs_attr_sf_nextentry(sfe)) {
if (xfs_attr_match(args, sfe->namelen, sfe->nameval,
sfe->flags))
if (xfs_attr_match(args, sfe->flags, sfe->nameval,
sfe->namelen, &sfe->nameval[sfe->namelen],
sfe->valuelen))
return sfe;
}
......@@ -854,7 +891,8 @@ xfs_attr_sf_removename(
*/
if (totsize == sizeof(struct xfs_attr_sf_hdr) && xfs_has_attr2(mp) &&
(dp->i_df.if_format != XFS_DINODE_FMT_BTREE) &&
!(args->op_flags & (XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE))) {
!(args->op_flags & (XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE)) &&
!xfs_has_parent(mp)) {
xfs_attr_fork_remove(dp, args->trans);
} else {
xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
......@@ -863,7 +901,8 @@ xfs_attr_sf_removename(
ASSERT(totsize > sizeof(struct xfs_attr_sf_hdr) ||
(args->op_flags & XFS_DA_OP_ADDNAME) ||
!xfs_has_attr2(mp) ||
dp->i_df.if_format == XFS_DINODE_FMT_BTREE);
dp->i_df.if_format == XFS_DINODE_FMT_BTREE ||
xfs_has_parent(mp));
xfs_trans_log_inode(args->trans, dp,
XFS_ILOG_CORE | XFS_ILOG_ADATA);
}
......@@ -947,14 +986,13 @@ xfs_attr_shortform_to_leaf(
nargs.namelen = sfe->namelen;
nargs.value = &sfe->nameval[nargs.namelen];
nargs.valuelen = sfe->valuelen;
nargs.hashval = xfs_da_hashname(sfe->nameval,
sfe->namelen);
nargs.attr_filter = sfe->flags & XFS_ATTR_NSP_ONDISK_MASK;
if (!xfs_attr_check_namespace(sfe->flags)) {
xfs_da_mark_sick(args);
error = -EFSCORRUPTED;
goto out;
}
xfs_attr_sethash(&nargs);
error = xfs_attr3_leaf_lookup_int(bp, &nargs); /* set a->index */
ASSERT(error == -ENOATTR);
error = xfs_attr3_leaf_add(bp, &nargs);
......@@ -2443,18 +2481,23 @@ xfs_attr3_leaf_lookup_int(
*/
if (entry->flags & XFS_ATTR_LOCAL) {
name_loc = xfs_attr3_leaf_name_local(leaf, probe);
if (!xfs_attr_match(args, name_loc->namelen,
name_loc->nameval, entry->flags))
if (!xfs_attr_match(args, entry->flags,
name_loc->nameval, name_loc->namelen,
&name_loc->nameval[name_loc->namelen],
be16_to_cpu(name_loc->valuelen)))
continue;
args->index = probe;
return -EEXIST;
} else {
unsigned int valuelen;
name_rmt = xfs_attr3_leaf_name_remote(leaf, probe);
if (!xfs_attr_match(args, name_rmt->namelen,
name_rmt->name, entry->flags))
valuelen = be32_to_cpu(name_rmt->valuelen);
if (!xfs_attr_match(args, entry->flags, name_rmt->name,
name_rmt->namelen, NULL, valuelen))
continue;
args->index = probe;
args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen);
args->rmtvaluelen = valuelen;
args->rmtblkno = be32_to_cpu(name_rmt->valueblk);
args->rmtblkcnt = xfs_attr3_rmt_blocks(
args->dp->i_mount,
......
......@@ -16,6 +16,7 @@ typedef struct xfs_attr_sf_sort {
uint8_t flags; /* flags bits (see xfs_attr_leaf.h) */
xfs_dahash_t hash; /* this entry's hash value */
unsigned char *name; /* name value, pointer into buffer */
void *value;
} xfs_attr_sf_sort_t;
#define XFS_ATTR_SF_ENTSIZE_MAX /* max space for name&value */ \
......
......@@ -55,7 +55,9 @@ enum xfs_dacmp {
typedef struct xfs_da_args {
struct xfs_da_geometry *geo; /* da block geometry */
const uint8_t *name; /* string (maybe not NULL terminated) */
const uint8_t *new_name; /* new attr name */
void *value; /* set of bytes (maybe contain NULLs) */
void *new_value; /* new xattr value (may contain NULLs) */
struct xfs_inode *dp; /* directory inode to manipulate */
struct xfs_trans *trans; /* current trans (changes over time) */
......@@ -63,10 +65,12 @@ typedef struct xfs_da_args {
xfs_ino_t owner; /* inode that owns the dir/attr data */
int valuelen; /* length of value */
int new_valuelen; /* length of new_value */
uint8_t filetype; /* filetype of inode for directories */
uint8_t op_flags; /* operation flags */
uint8_t attr_filter; /* XFS_ATTR_{ROOT,SECURE,INCOMPLETE} */
short namelen; /* length of string (maybe no NULL) */
short new_namelen; /* length of new attr name */
xfs_dahash_t hashval; /* hash value of name */
xfs_extlen_t total; /* total blocks needed, for 1st bmap */
int whichfork; /* data or attribute fork */
......
......@@ -714,13 +714,20 @@ struct xfs_attr3_leafblock {
#define XFS_ATTR_LOCAL_BIT 0 /* attr is stored locally */
#define XFS_ATTR_ROOT_BIT 1 /* limit access to trusted attrs */
#define XFS_ATTR_SECURE_BIT 2 /* limit access to secure attrs */
#define XFS_ATTR_PARENT_BIT 3 /* parent pointer attrs */
#define XFS_ATTR_INCOMPLETE_BIT 7 /* attr in middle of create/delete */
#define XFS_ATTR_LOCAL (1u << XFS_ATTR_LOCAL_BIT)
#define XFS_ATTR_ROOT (1u << XFS_ATTR_ROOT_BIT)
#define XFS_ATTR_SECURE (1u << XFS_ATTR_SECURE_BIT)
#define XFS_ATTR_PARENT (1u << XFS_ATTR_PARENT_BIT)
#define XFS_ATTR_INCOMPLETE (1u << XFS_ATTR_INCOMPLETE_BIT)
#define XFS_ATTR_NSP_ONDISK_MASK (XFS_ATTR_ROOT | XFS_ATTR_SECURE)
#define XFS_ATTR_NSP_ONDISK_MASK (XFS_ATTR_ROOT | \
XFS_ATTR_SECURE | \
XFS_ATTR_PARENT)
/* Private attr namespaces not exposed to userspace */
#define XFS_ATTR_PRIVATE_NSP_MASK (XFS_ATTR_PARENT)
#define XFS_ATTR_ONDISK_MASK (XFS_ATTR_NSP_ONDISK_MASK | \
XFS_ATTR_LOCAL | \
......@@ -729,7 +736,8 @@ struct xfs_attr3_leafblock {
#define XFS_ATTR_NAMESPACE_STR \
{ XFS_ATTR_LOCAL, "local" }, \
{ XFS_ATTR_ROOT, "root" }, \
{ XFS_ATTR_SECURE, "secure" }
{ XFS_ATTR_SECURE, "secure" }, \
{ XFS_ATTR_PARENT, "parent" }
/*
* Alignment for namelist and valuelist entries (since they are mixed
......@@ -885,4 +893,17 @@ static inline unsigned int xfs_dir2_dirblock_bytes(struct xfs_sb *sbp)
xfs_failaddr_t xfs_da3_blkinfo_verify(struct xfs_buf *bp,
struct xfs_da3_blkinfo *hdr3);
/*
* Parent pointer attribute format definition
*
* The xattr name contains the dirent name.
* The xattr value encodes the parent inode number and generation to ease
* opening parents by handle.
* The xattr hashval is xfs_dir2_namehash() ^ p_ino
*/
struct xfs_parent_rec {
__be64 p_ino;
__be32 p_gen;
} __packed;
#endif /* __XFS_DA_FORMAT_H__ */
......@@ -374,6 +374,7 @@ xfs_sb_has_ro_compat_feature(
#define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */
#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */
#define XFS_SB_FEAT_INCOMPAT_EXCHRANGE (1 << 6) /* exchangerange supported */
#define XFS_SB_FEAT_INCOMPAT_PARENT (1 << 7) /* parent pointers */
#define XFS_SB_FEAT_INCOMPAT_ALL \
(XFS_SB_FEAT_INCOMPAT_FTYPE | \
XFS_SB_FEAT_INCOMPAT_SPINODES | \
......@@ -381,7 +382,8 @@ xfs_sb_has_ro_compat_feature(
XFS_SB_FEAT_INCOMPAT_BIGTIME | \
XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | \
XFS_SB_FEAT_INCOMPAT_NREXT64 | \
XFS_SB_FEAT_INCOMPAT_EXCHRANGE)
XFS_SB_FEAT_INCOMPAT_EXCHRANGE | \
XFS_SB_FEAT_INCOMPAT_PARENT)
#define XFS_SB_FEAT_INCOMPAT_UNKNOWN ~XFS_SB_FEAT_INCOMPAT_ALL
static inline bool
......
......@@ -240,6 +240,7 @@ typedef struct xfs_fsop_resblks {
#define XFS_FSOP_GEOM_FLAGS_INOBTCNT (1 << 22) /* inobt btree counter */
#define XFS_FSOP_GEOM_FLAGS_NREXT64 (1 << 23) /* large extent counters */
#define XFS_FSOP_GEOM_FLAGS_EXCHANGE_RANGE (1 << 24) /* exchange range */
#define XFS_FSOP_GEOM_FLAGS_PARENT (1 << 25) /* linux parent pointers */
/*
* Minimum and maximum sizes need for growth checks.
......@@ -633,7 +634,9 @@ typedef struct xfs_fsop_attrmulti_handlereq {
/*
* per machine unique filesystem identifier types.
*/
typedef struct { __u32 val[2]; } xfs_fsid_t; /* file system id type */
typedef struct xfs_fsid {
__u32 val[2]; /* file system id type */
} xfs_fsid_t;
typedef struct xfs_fid {
__u16 fid_len; /* length of remainder */
......@@ -814,6 +817,78 @@ struct xfs_exchange_range {
XFS_EXCHANGE_RANGE_DRY_RUN | \
XFS_EXCHANGE_RANGE_FILE1_WRITTEN)
/* Iterating parent pointers of files. */
/* target was the root directory */
#define XFS_GETPARENTS_OFLAG_ROOT (1U << 0)
/* Cursor is done iterating pptrs */
#define XFS_GETPARENTS_OFLAG_DONE (1U << 1)
#define XFS_GETPARENTS_OFLAGS_ALL (XFS_GETPARENTS_OFLAG_ROOT | \
XFS_GETPARENTS_OFLAG_DONE)
#define XFS_GETPARENTS_IFLAGS_ALL (0)
struct xfs_getparents_rec {
struct xfs_handle gpr_parent; /* Handle to parent */
__u32 gpr_reclen; /* Length of entire record */
__u32 gpr_reserved; /* zero */
char gpr_name[]; /* Null-terminated filename */
};
/* Iterate through this file's directory parent pointers */
struct xfs_getparents {
/*
* Structure to track progress in iterating the parent pointers.
* Must be initialized to zeroes before the first ioctl call, and
* not touched by callers after that.
*/
struct xfs_attrlist_cursor gp_cursor;
/* Input flags: XFS_GETPARENTS_IFLAG* */
__u16 gp_iflags;
/* Output flags: XFS_GETPARENTS_OFLAG* */
__u16 gp_oflags;
/* Size of the gp_buffer in bytes */
__u32 gp_bufsize;
/* Must be set to zero */
__u64 gp_reserved;
/* Pointer to a buffer in which to place xfs_getparents_rec */
__u64 gp_buffer;
};
static inline struct xfs_getparents_rec *
xfs_getparents_first_rec(struct xfs_getparents *gp)
{
return (struct xfs_getparents_rec *)(uintptr_t)gp->gp_buffer;
}
static inline struct xfs_getparents_rec *
xfs_getparents_next_rec(struct xfs_getparents *gp,
struct xfs_getparents_rec *gpr)
{
void *next = ((void *)gpr + gpr->gpr_reclen);
void *end = (void *)(uintptr_t)(gp->gp_buffer + gp->gp_bufsize);
if (next >= end)
return NULL;
return next;
}
/* Iterate through this file handle's directory parent pointers. */
struct xfs_getparents_by_handle {
/* Handle to file whose parents we want. */
struct xfs_handle gph_handle;
struct xfs_getparents gph_request;
};
/*
* ioctl commands that are used by Linux filesystems
*/
......@@ -849,6 +924,8 @@ struct xfs_exchange_range {
/* XFS_IOC_GETFSMAP ------ hoisted 59 */
#define XFS_IOC_SCRUB_METADATA _IOWR('X', 60, struct xfs_scrub_metadata)
#define XFS_IOC_AG_GEOMETRY _IOWR('X', 61, struct xfs_ag_geometry)
#define XFS_IOC_GETPARENTS _IOWR('X', 62, struct xfs_getparents)
#define XFS_IOC_GETPARENTS_BY_HANDLE _IOWR('X', 63, struct xfs_getparents_by_handle)
/*
* ioctl commands that replace IRIX syssgi()'s
......
......@@ -115,11 +115,13 @@ struct xfs_unmount_log_format {
#define XLOG_REG_TYPE_BUD_FORMAT 26
#define XLOG_REG_TYPE_ATTRI_FORMAT 27
#define XLOG_REG_TYPE_ATTRD_FORMAT 28
#define XLOG_REG_TYPE_ATTR_NAME 29
#define XLOG_REG_TYPE_ATTR_NAME 29
#define XLOG_REG_TYPE_ATTR_VALUE 30
#define XLOG_REG_TYPE_XMI_FORMAT 31
#define XLOG_REG_TYPE_XMD_FORMAT 32
#define XLOG_REG_TYPE_MAX 32
#define XLOG_REG_TYPE_ATTR_NEWNAME 33
#define XLOG_REG_TYPE_ATTR_NEWVALUE 34
#define XLOG_REG_TYPE_MAX 34
/*
* Flags to log operation header
......@@ -1026,6 +1028,9 @@ struct xfs_icreate_log {
#define XFS_ATTRI_OP_FLAGS_SET 1 /* Set the attribute */
#define XFS_ATTRI_OP_FLAGS_REMOVE 2 /* Remove the attribute */
#define XFS_ATTRI_OP_FLAGS_REPLACE 3 /* Replace the attribute */
#define XFS_ATTRI_OP_FLAGS_PPTR_SET 4 /* Set parent pointer */
#define XFS_ATTRI_OP_FLAGS_PPTR_REMOVE 5 /* Remove parent pointer */
#define XFS_ATTRI_OP_FLAGS_PPTR_REPLACE 6 /* Replace parent pointer */
#define XFS_ATTRI_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */
/*
......@@ -1034,6 +1039,7 @@ struct xfs_icreate_log {
*/
#define XFS_ATTRI_FILTER_MASK (XFS_ATTR_ROOT | \
XFS_ATTR_SECURE | \
XFS_ATTR_PARENT | \
XFS_ATTR_INCOMPLETE)
/*
......@@ -1043,11 +1049,22 @@ struct xfs_icreate_log {
struct xfs_attri_log_format {
uint16_t alfi_type; /* attri log item type */
uint16_t alfi_size; /* size of this item */
uint32_t __pad; /* pad to 64 bit aligned */
uint32_t alfi_igen; /* generation of alfi_ino for pptr ops */
uint64_t alfi_id; /* attri identifier */
uint64_t alfi_ino; /* the inode for this attr operation */
uint32_t alfi_op_flags; /* marks the op as a set or remove */
uint32_t alfi_name_len; /* attr name length */
union {
uint32_t alfi_name_len; /* attr name length */
struct {
/*
* For PPTR_REPLACE, these are the lengths of the old
* and new attr names. The new and old values must
* have the same length.
*/
uint16_t alfi_old_name_len;
uint16_t alfi_new_name_len;
};
};
uint32_t alfi_value_len; /* attr value length */
uint32_t alfi_attr_filter;/* attr filter flags */
};
......
......@@ -16,6 +16,34 @@
#include "xfs_bmap_btree.h"
#include "xfs_trace.h"
/*
* Shortly after enabling the large extents count feature in 2023, longstanding
* bugs were found in the code that computes the minimum log size. Luckily,
* the bugs resulted in over-estimates of that size, so there's no impact to
* existing users. However, we don't want to reduce the minimum log size
* because that can create the situation where a newer mkfs writes a new
* filesystem that an older kernel won't mount.
*
* Several years prior, we also discovered that the transaction reservations
* for rmap and reflink operations were unnecessarily large. That was fixed,
* but the minimum log size computation was left alone to avoid the
* compatibility problems noted above. Fix that too.
*
* Therefore, we only may correct the computation starting with filesystem
* features that didn't exist in 2023. In other words, only turn this on if
* the filesystem has parent pointers.
*
* This function can be called before the XFS_HAS_* flags have been set up,
* (e.g. mkfs) so we must check the ondisk superblock.
*/
static inline bool
xfs_want_minlogsize_fixes(
struct xfs_sb *sb)
{
return xfs_sb_is_v5(sb) &&
xfs_sb_has_incompat_feature(sb, XFS_SB_FEAT_INCOMPAT_PARENT);
}
/*
* Calculate the maximum length in bytes that would be required for a local
* attribute value as large attributes out of line are not logged.
......@@ -31,6 +59,15 @@ xfs_log_calc_max_attrsetm_res(
MAXNAMELEN - 1;
nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK);
nblks += XFS_B_TO_FSB(mp, size);
/*
* If the feature set is new enough, correct a unit conversion error in
* the xattr transaction reservation code that resulted in oversized
* minimum log size computations.
*/
if (xfs_want_minlogsize_fixes(&mp->m_sb))
size = XFS_B_TO_FSB(mp, size);
nblks += XFS_NEXTENTADD_SPACE_RES(mp, size, XFS_ATTR_FORK);
return M_RES(mp)->tr_attrsetm.tr_logres +
......@@ -48,6 +85,15 @@ xfs_log_calc_trans_resv_for_minlogblocks(
{
unsigned int rmap_maxlevels = mp->m_rmap_maxlevels;
/*
* If the feature set is new enough, drop the oversized minimum log
* size computation introduced by the original reflink code.
*/
if (xfs_want_minlogsize_fixes(&mp->m_sb)) {
xfs_trans_resv_calc(mp, resv);
return;
}
/*
* In the early days of rmap+reflink, we always set the rmap maxlevels
* to 9 even if the AG was small enough that it would never grow to
......
......@@ -119,6 +119,7 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_OFFSET(xfs_dir2_sf_entry_t, offset, 1);
XFS_CHECK_OFFSET(xfs_dir2_sf_entry_t, name, 3);
XFS_CHECK_STRUCT_SIZE(xfs_dir2_sf_hdr_t, 10);
XFS_CHECK_STRUCT_SIZE(struct xfs_parent_rec, 12);
/* log structures */
XFS_CHECK_STRUCT_SIZE(struct xfs_buf_log_format, 88);
......@@ -155,6 +156,11 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_OFFSET(struct xfs_efi_log_format_32, efi_extents, 16);
XFS_CHECK_OFFSET(struct xfs_efi_log_format_64, efi_extents, 16);
/* parent pointer ioctls */
XFS_CHECK_STRUCT_SIZE(struct xfs_getparents_rec, 32);
XFS_CHECK_STRUCT_SIZE(struct xfs_getparents, 40);
XFS_CHECK_STRUCT_SIZE(struct xfs_getparents_by_handle, 64);
/*
* The v5 superblock format extended several v4 header structures with
* additional data. While new fields are only accessible on v5
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022-2024 Oracle.
* All rights reserved.
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_format.h"
#include "xfs_da_format.h"
#include "xfs_log_format.h"
#include "xfs_shared.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_inode.h"
#include "xfs_error.h"
#include "xfs_trace.h"
#include "xfs_trans.h"
#include "xfs_da_btree.h"
#include "xfs_attr.h"
#include "xfs_dir2.h"
#include "xfs_dir2_priv.h"
#include "xfs_attr_sf.h"
#include "xfs_bmap.h"
#include "xfs_defer.h"
#include "xfs_log.h"
#include "xfs_xattr.h"
#include "xfs_parent.h"
#include "xfs_trans_space.h"
#include "xfs_attr_item.h"
#include "xfs_health.h"
struct kmem_cache *xfs_parent_args_cache;
/*
* Parent pointer attribute handling.
*
* Because the attribute name is a filename component, it will never be longer
* than 255 bytes and must not contain nulls or slashes. These are roughly the
* same constraints that apply to attribute names.
*
* The attribute value must always be a struct xfs_parent_rec. This means the
* attribute will never be in remote format because 12 bytes is nowhere near
* xfs_attr_leaf_entsize_local_max() (~75% of block size).
*
* Creating a new parent attribute will always create a new attribute - there
* should never, ever be an existing attribute in the tree for a new inode.
* ENOSPC behavior is problematic - creating the inode without the parent
* pointer is effectively a corruption, so we allow parent attribute creation
* to dip into the reserve block pool to avoid unexpected ENOSPC errors from
* occurring.
*/
/* Return true if parent pointer attr name is valid. */
bool
xfs_parent_namecheck(
unsigned int attr_flags,
const void *name,
size_t length)
{
/*
* Parent pointers always use logged operations, so there should never
* be incomplete xattrs.
*/
if (attr_flags & XFS_ATTR_INCOMPLETE)
return false;
return xfs_dir2_namecheck(name, length);
}
/* Return true if parent pointer attr value is valid. */
bool
xfs_parent_valuecheck(
struct xfs_mount *mp,
const void *value,
size_t valuelen)
{
const struct xfs_parent_rec *rec = value;
if (!xfs_has_parent(mp))
return false;
/* The xattr value must be a parent record. */
if (valuelen != sizeof(struct xfs_parent_rec))
return false;
/* The parent record must be local. */
if (value == NULL)
return false;
/* The parent inumber must be valid. */
if (!xfs_verify_dir_ino(mp, be64_to_cpu(rec->p_ino)))
return false;
return true;
}
/* Compute the attribute name hash for a parent pointer. */
xfs_dahash_t
xfs_parent_hashval(
struct xfs_mount *mp,
const uint8_t *name,
int namelen,
xfs_ino_t parent_ino)
{
struct xfs_name xname = {
.name = name,
.len = namelen,
};
/*
* Use the same dirent name hash as would be used on the directory, but
* mix in the parent inode number to avoid collisions on hardlinked
* files with identical names but different parents.
*/
return xfs_dir2_hashname(mp, &xname) ^
upper_32_bits(parent_ino) ^ lower_32_bits(parent_ino);
}
/* Compute the attribute name hash from the xattr components. */
xfs_dahash_t
xfs_parent_hashattr(
struct xfs_mount *mp,
const uint8_t *name,
int namelen,
const void *value,
int valuelen)
{
const struct xfs_parent_rec *rec = value;
/* Requires a local attr value in xfs_parent_rec format */
if (valuelen != sizeof(struct xfs_parent_rec)) {
ASSERT(valuelen == sizeof(struct xfs_parent_rec));
return 0;
}
if (!value) {
ASSERT(value != NULL);
return 0;
}
return xfs_parent_hashval(mp, name, namelen, be64_to_cpu(rec->p_ino));
}
/*
* Initialize the parent pointer arguments structure. Caller must have zeroed
* the contents of @args. @tp is only required for updates.
*/
static void
xfs_parent_da_args_init(
struct xfs_da_args *args,
struct xfs_trans *tp,
struct xfs_parent_rec *rec,
struct xfs_inode *child,
xfs_ino_t owner,
const struct xfs_name *parent_name)
{
args->geo = child->i_mount->m_attr_geo;
args->whichfork = XFS_ATTR_FORK;
args->attr_filter = XFS_ATTR_PARENT;
args->op_flags = XFS_DA_OP_LOGGED | XFS_DA_OP_OKNOENT;
args->trans = tp;
args->dp = child;
args->owner = owner;
args->name = parent_name->name;
args->namelen = parent_name->len;
args->value = rec;
args->valuelen = sizeof(struct xfs_parent_rec);
xfs_attr_sethash(args);
}
/* Make sure the incore state is ready for a parent pointer query/update. */
static inline int
xfs_parent_iread_extents(
struct xfs_trans *tp,
struct xfs_inode *child)
{
/* Parent pointers require that the attr fork must exist. */
if (XFS_IS_CORRUPT(child->i_mount, !xfs_inode_has_attr_fork(child))) {
xfs_inode_mark_sick(child, XFS_SICK_INO_PARENT);
return -EFSCORRUPTED;
}
return xfs_iread_extents(tp, child, XFS_ATTR_FORK);
}
/* Add a parent pointer to reflect a dirent addition. */
int
xfs_parent_addname(
struct xfs_trans *tp,
struct xfs_parent_args *ppargs,
struct xfs_inode *dp,
const struct xfs_name *parent_name,
struct xfs_inode *child)
{
int error;
error = xfs_parent_iread_extents(tp, child);
if (error)
return error;
xfs_inode_to_parent_rec(&ppargs->rec, dp);
xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
child->i_ino, parent_name);
xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_SET);
return 0;
}
/* Remove a parent pointer to reflect a dirent removal. */
int
xfs_parent_removename(
struct xfs_trans *tp,
struct xfs_parent_args *ppargs,
struct xfs_inode *dp,
const struct xfs_name *parent_name,
struct xfs_inode *child)
{
int error;
error = xfs_parent_iread_extents(tp, child);
if (error)
return error;
xfs_inode_to_parent_rec(&ppargs->rec, dp);
xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
child->i_ino, parent_name);
xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REMOVE);
return 0;
}
/* Replace one parent pointer with another to reflect a rename. */
int
xfs_parent_replacename(
struct xfs_trans *tp,
struct xfs_parent_args *ppargs,
struct xfs_inode *old_dp,
const struct xfs_name *old_name,
struct xfs_inode *new_dp,
const struct xfs_name *new_name,
struct xfs_inode *child)
{
int error;
error = xfs_parent_iread_extents(tp, child);
if (error)
return error;
xfs_inode_to_parent_rec(&ppargs->rec, old_dp);
xfs_parent_da_args_init(&ppargs->args, tp, &ppargs->rec, child,
child->i_ino, old_name);
xfs_inode_to_parent_rec(&ppargs->new_rec, new_dp);
ppargs->args.new_name = new_name->name;
ppargs->args.new_namelen = new_name->len;
ppargs->args.new_value = &ppargs->new_rec;
ppargs->args.new_valuelen = sizeof(struct xfs_parent_rec);
xfs_attr_defer_add(&ppargs->args, XFS_ATTR_DEFER_REPLACE);
return 0;
}
/*
* Extract parent pointer information from any parent pointer xattr into
* @parent_ino/gen. The last two parameters can be NULL pointers.
*
* Returns 0 if this is not a parent pointer xattr at all; or -EFSCORRUPTED for
* garbage.
*/
int
xfs_parent_from_attr(
struct xfs_mount *mp,
unsigned int attr_flags,
const unsigned char *name,
unsigned int namelen,
const void *value,
unsigned int valuelen,
xfs_ino_t *parent_ino,
uint32_t *parent_gen)
{
const struct xfs_parent_rec *rec = value;
ASSERT(attr_flags & XFS_ATTR_PARENT);
if (!xfs_parent_namecheck(attr_flags, name, namelen))
return -EFSCORRUPTED;
if (!xfs_parent_valuecheck(mp, value, valuelen))
return -EFSCORRUPTED;
if (parent_ino)
*parent_ino = be64_to_cpu(rec->p_ino);
if (parent_gen)
*parent_gen = be32_to_cpu(rec->p_gen);
return 0;
}
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022-2024 Oracle.
* All Rights Reserved.
*/
#ifndef __XFS_PARENT_H__
#define __XFS_PARENT_H__
/* Metadata validators */
bool xfs_parent_namecheck(unsigned int attr_flags, const void *name,
size_t length);
bool xfs_parent_valuecheck(struct xfs_mount *mp, const void *value,
size_t valuelen);
xfs_dahash_t xfs_parent_hashval(struct xfs_mount *mp, const uint8_t *name,
int namelen, xfs_ino_t parent_ino);
xfs_dahash_t xfs_parent_hashattr(struct xfs_mount *mp, const uint8_t *name,
int namelen, const void *value, int valuelen);
/* Initializes a xfs_parent_rec to be stored as an attribute name. */
static inline void
xfs_parent_rec_init(
struct xfs_parent_rec *rec,
xfs_ino_t ino,
uint32_t gen)
{
rec->p_ino = cpu_to_be64(ino);
rec->p_gen = cpu_to_be32(gen);
}
/* Initializes a xfs_parent_rec to be stored as an attribute name. */
static inline void
xfs_inode_to_parent_rec(
struct xfs_parent_rec *rec,
const struct xfs_inode *dp)
{
xfs_parent_rec_init(rec, dp->i_ino, VFS_IC(dp)->i_generation);
}
extern struct kmem_cache *xfs_parent_args_cache;
/*
* Parent pointer information needed to pass around the deferred xattr update
* machinery.
*/
struct xfs_parent_args {
struct xfs_parent_rec rec;
struct xfs_parent_rec new_rec;
struct xfs_da_args args;
};
/*
* Start a parent pointer update by allocating the context object we need to
* perform a parent pointer update.
*/
static inline int
xfs_parent_start(
struct xfs_mount *mp,
struct xfs_parent_args **ppargsp)
{
if (!xfs_has_parent(mp)) {
*ppargsp = NULL;
return 0;
}
*ppargsp = kmem_cache_zalloc(xfs_parent_args_cache, GFP_KERNEL);
if (!*ppargsp)
return -ENOMEM;
return 0;
}
/* Finish a parent pointer update by freeing the context object. */
static inline void
xfs_parent_finish(
struct xfs_mount *mp,
struct xfs_parent_args *ppargs)
{
if (ppargs)
kmem_cache_free(xfs_parent_args_cache, ppargs);
}
int xfs_parent_addname(struct xfs_trans *tp, struct xfs_parent_args *ppargs,
struct xfs_inode *dp, const struct xfs_name *parent_name,
struct xfs_inode *child);
int xfs_parent_removename(struct xfs_trans *tp, struct xfs_parent_args *ppargs,
struct xfs_inode *dp, const struct xfs_name *parent_name,
struct xfs_inode *child);
int xfs_parent_replacename(struct xfs_trans *tp,
struct xfs_parent_args *ppargs,
struct xfs_inode *old_dp, const struct xfs_name *old_name,
struct xfs_inode *new_dp, const struct xfs_name *new_name,
struct xfs_inode *child);
int xfs_parent_from_attr(struct xfs_mount *mp, unsigned int attr_flags,
const unsigned char *name, unsigned int namelen,
const void *value, unsigned int valuelen,
xfs_ino_t *parent_ino, uint32_t *parent_gen);
#endif /* __XFS_PARENT_H__ */
......@@ -178,6 +178,8 @@ xfs_sb_version_to_features(
features |= XFS_FEAT_NREXT64;
if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_EXCHRANGE)
features |= XFS_FEAT_EXCHANGE_RANGE;
if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_PARENT)
features |= XFS_FEAT_PARENT;
return features;
}
......@@ -1254,6 +1256,8 @@ xfs_fs_geometry(
geo->flags |= XFS_FSOP_GEOM_FLAGS_BIGTIME;
if (xfs_has_inobtcounts(mp))
geo->flags |= XFS_FSOP_GEOM_FLAGS_INOBTCNT;
if (xfs_has_parent(mp))
geo->flags |= XFS_FSOP_GEOM_FLAGS_PARENT;
if (xfs_has_sector(mp)) {
geo->flags |= XFS_FSOP_GEOM_FLAGS_SECTOR;
geo->logsectsize = sbp->sb_logsectsize;
......
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000,2005 Silicon Graphics, Inc.
* All Rights Reserved.
*/
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_da_format.h"
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_da_btree.h"
#include "xfs_bmap_btree.h"
#include "xfs_trans_space.h"
/* Calculate the disk space required to add a parent pointer. */
unsigned int
xfs_parent_calc_space_res(
struct xfs_mount *mp,
unsigned int namelen)
{
/*
* Parent pointers are always the first attr in an attr tree, and never
* larger than a block
*/
return XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK) +
XFS_NEXTENTADD_SPACE_RES(mp, namelen, XFS_ATTR_FORK);
}
unsigned int
xfs_create_space_res(
struct xfs_mount *mp,
unsigned int namelen)
{
unsigned int ret;
ret = XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp, namelen);
if (xfs_has_parent(mp))
ret += xfs_parent_calc_space_res(mp, namelen);
return ret;
}
unsigned int
xfs_mkdir_space_res(
struct xfs_mount *mp,
unsigned int namelen)
{
return xfs_create_space_res(mp, namelen);
}
unsigned int
xfs_link_space_res(
struct xfs_mount *mp,
unsigned int namelen)
{
unsigned int ret;
ret = XFS_DIRENTER_SPACE_RES(mp, namelen);
if (xfs_has_parent(mp))
ret += xfs_parent_calc_space_res(mp, namelen);
return ret;
}
unsigned int
xfs_symlink_space_res(
struct xfs_mount *mp,
unsigned int namelen,
unsigned int fsblocks)
{
unsigned int ret;
ret = XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp, namelen) +
fsblocks;
if (xfs_has_parent(mp))
ret += xfs_parent_calc_space_res(mp, namelen);
return ret;
}
unsigned int
xfs_remove_space_res(
struct xfs_mount *mp,
unsigned int namelen)
{
unsigned int ret = XFS_DIRREMOVE_SPACE_RES(mp);
if (xfs_has_parent(mp))
ret += xfs_parent_calc_space_res(mp, namelen);
return ret;
}
unsigned int
xfs_rename_space_res(
struct xfs_mount *mp,
unsigned int src_namelen,
bool target_exists,
unsigned int target_namelen,
bool has_whiteout)
{
unsigned int ret;
ret = XFS_DIRREMOVE_SPACE_RES(mp) +
XFS_DIRENTER_SPACE_RES(mp, target_namelen);
if (xfs_has_parent(mp)) {
if (has_whiteout)
ret += xfs_parent_calc_space_res(mp, src_namelen);
ret += 2 * xfs_parent_calc_space_res(mp, target_namelen);
}
if (target_exists)
ret += xfs_parent_calc_space_res(mp, target_namelen);
return ret;
}
......@@ -80,31 +80,32 @@
/* This macro is not used - see inline code in xfs_attr_set */
#define XFS_ATTRSET_SPACE_RES(mp, v) \
(XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK) + XFS_B_TO_FSB(mp, v))
#define XFS_CREATE_SPACE_RES(mp,nl) \
(XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_DIOSTRAT_SPACE_RES(mp, v) \
(XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK) + (v))
#define XFS_GROWFS_SPACE_RES(mp) \
(2 * (mp)->m_alloc_maxlevels)
#define XFS_GROWFSRT_SPACE_RES(mp,b) \
((b) + XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK))
#define XFS_LINK_SPACE_RES(mp,nl) \
XFS_DIRENTER_SPACE_RES(mp,nl)
#define XFS_MKDIR_SPACE_RES(mp,nl) \
(XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_QM_DQALLOC_SPACE_RES(mp) \
(XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK) + \
XFS_DQUOT_CLUSTER_SIZE_FSB)
#define XFS_QM_QINOCREATE_SPACE_RES(mp) \
XFS_IALLOC_SPACE_RES(mp)
#define XFS_REMOVE_SPACE_RES(mp) \
XFS_DIRREMOVE_SPACE_RES(mp)
#define XFS_RENAME_SPACE_RES(mp,nl) \
(XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_SYMLINK_SPACE_RES(mp,nl,b) \
(XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl) + (b))
#define XFS_IFREE_SPACE_RES(mp) \
(xfs_has_finobt(mp) ? M_IGEO(mp)->inobt_maxlevels : 0)
unsigned int xfs_parent_calc_space_res(struct xfs_mount *mp,
unsigned int namelen);
unsigned int xfs_create_space_res(struct xfs_mount *mp, unsigned int namelen);
unsigned int xfs_mkdir_space_res(struct xfs_mount *mp, unsigned int namelen);
unsigned int xfs_link_space_res(struct xfs_mount *mp, unsigned int namelen);
unsigned int xfs_symlink_space_res(struct xfs_mount *mp, unsigned int namelen,
unsigned int fsblocks);
unsigned int xfs_remove_space_res(struct xfs_mount *mp, unsigned int namelen);
unsigned int xfs_rename_space_res(struct xfs_mount *mp,
unsigned int src_namelen, bool target_exists,
unsigned int target_namelen, bool has_whiteout);
#endif /* __XFS_TRANS_SPACE_H__ */
......@@ -179,7 +179,6 @@ xchk_xattr_actor(
.dp = ip,
.name = name,
.namelen = namelen,
.hashval = xfs_da_hashname(name, namelen),
.trans = sc->tp,
.valuelen = valuelen,
.owner = ip->i_ino,
......@@ -230,6 +229,7 @@ xchk_xattr_actor(
args.value = ab->value;
xfs_attr_sethash(&args);
error = xfs_attr_get_ilocked(&args);
/* ENODATA means the hash lookup failed and the attr is bad */
if (error == -ENODATA)
......@@ -525,7 +525,10 @@ xchk_xattr_rec(
xchk_da_set_corrupt(ds, level);
goto out;
}
calc_hash = xfs_da_hashname(lentry->nameval, lentry->namelen);
calc_hash = xfs_attr_hashval(mp, ent->flags, lentry->nameval,
lentry->namelen,
lentry->nameval + lentry->namelen,
be16_to_cpu(lentry->valuelen));
} else {
rentry = (struct xfs_attr_leaf_name_remote *)
(((char *)bp->b_addr) + nameidx);
......@@ -533,7 +536,13 @@ xchk_xattr_rec(
xchk_da_set_corrupt(ds, level);
goto out;
}
calc_hash = xfs_da_hashname(rentry->name, rentry->namelen);
if (ent->flags & XFS_ATTR_PARENT) {
xchk_da_set_corrupt(ds, level);
goto out;
}
calc_hash = xfs_attr_hashval(mp, ent->flags, rentry->name,
rentry->namelen, NULL,
be32_to_cpu(rentry->valuelen));
}
if (calc_hash != hash)
xchk_da_set_corrupt(ds, level);
......
......@@ -704,7 +704,7 @@ xrep_dir_replay_update(
uint resblks;
int error;
resblks = XFS_LINK_SPACE_RES(mp, xname->len);
resblks = xfs_link_space_res(mp, xname->len);
error = xchk_trans_alloc(rd->sc, resblks);
if (error)
return error;
......
......@@ -326,9 +326,10 @@ xrep_adoption_trans_alloc(
/* Compute the worst case space reservation that we need. */
adopt->sc = sc;
adopt->orphanage_blkres = XFS_LINK_SPACE_RES(mp, MAXNAMELEN);
adopt->orphanage_blkres = xfs_link_space_res(mp, MAXNAMELEN);
if (S_ISDIR(VFS_I(sc->ip)->i_mode))
child_blkres = XFS_RENAME_SPACE_RES(mp, xfs_name_dotdot.len);
child_blkres = xfs_rename_space_res(mp, 0, false,
xfs_name_dotdot.len, false);
adopt->child_blkres = child_blkres;
/*
......
......@@ -171,7 +171,8 @@ xrep_parent_reset_dotdot(
* Reserve more space just in case we have to expand the dir. We're
* allowed to exceed quota to repair inconsistent metadata.
*/
spaceres = XFS_RENAME_SPACE_RES(sc->mp, xfs_name_dotdot.len);
spaceres = xfs_rename_space_res(sc->mp, 0, false, xfs_name_dotdot.len,
false);
error = xfs_trans_reserve_more_inode(sc->tp, sc->ip, spaceres, 0,
true);
if (error)
......
......@@ -421,7 +421,7 @@ xrep_symlink_rebuild(
* unlikely.
*/
fs_blocks = xfs_symlink_blocks(sc->mp, target_len);
resblks = XFS_SYMLINK_SPACE_RES(sc->mp, target_len, fs_blocks);
resblks = xfs_symlink_space_res(sc->mp, target_len, fs_blocks);
error = xfs_trans_reserve_quota_nblks(sc->tp, sc->tempip, resblks, 0,
true);
if (error)
......
......@@ -71,7 +71,7 @@ xrep_tempfile_create(
return error;
if (is_dir) {
resblks = XFS_MKDIR_SPACE_RES(mp, 0);
resblks = xfs_mkdir_space_res(mp, 0);
tres = &M_RES(mp)->tr_mkdir;
} else {
resblks = XFS_IALLOC_SPACE_RES(mp);
......
This diff is collapsed.
......@@ -13,7 +13,9 @@ struct kmem_zone;
struct xfs_attri_log_nameval {
struct xfs_log_iovec name;
struct xfs_log_iovec new_name; /* PPTR_REPLACE only */
struct xfs_log_iovec value;
struct xfs_log_iovec new_value; /* PPTR_REPLACE only */
refcount_t refcount;
/* name and value follow the end of this struct */
......@@ -51,4 +53,12 @@ struct xfs_attrd_log_item {
extern struct kmem_cache *xfs_attri_cache;
extern struct kmem_cache *xfs_attrd_cache;
enum xfs_attr_defer_op {
XFS_ATTR_DEFER_SET,
XFS_ATTR_DEFER_REMOVE,
XFS_ATTR_DEFER_REPLACE,
};
void xfs_attr_defer_add(struct xfs_da_args *args, enum xfs_attr_defer_op op);
#endif /* __XFS_ATTR_ITEM_H__ */
......@@ -92,6 +92,7 @@ xfs_attr_shortform_list(
sfe->flags,
sfe->nameval,
(int)sfe->namelen,
&sfe->nameval[sfe->namelen],
(int)sfe->valuelen);
/*
* Either search callback finished early or
......@@ -135,12 +136,16 @@ xfs_attr_shortform_list(
}
sbp->entno = i;
sbp->hash = xfs_da_hashname(sfe->nameval, sfe->namelen);
sbp->name = sfe->nameval;
sbp->namelen = sfe->namelen;
/* These are bytes, and both on-disk, don't endian-flip */
sbp->value = &sfe->nameval[sfe->namelen],
sbp->valuelen = sfe->valuelen;
sbp->flags = sfe->flags;
sbp->hash = xfs_attr_hashval(dp->i_mount, sfe->flags,
sfe->nameval, sfe->namelen,
sfe->nameval + sfe->namelen,
sfe->valuelen);
sfe = xfs_attr_sf_nextentry(sfe);
sbp++;
nsbuf++;
......@@ -189,6 +194,7 @@ xfs_attr_shortform_list(
sbp->flags,
sbp->name,
sbp->namelen,
sbp->value,
sbp->valuelen);
if (context->seen_enough)
break;
......@@ -476,6 +482,7 @@ xfs_attr3_leaf_list_int(
*/
for (; i < ichdr.count; entry++, i++) {
char *name;
void *value;
int namelen, valuelen;
if (be32_to_cpu(entry->hashval) != cursor->hashval) {
......@@ -493,6 +500,7 @@ xfs_attr3_leaf_list_int(
name_loc = xfs_attr3_leaf_name_local(leaf, i);
name = name_loc->nameval;
namelen = name_loc->namelen;
value = &name_loc->nameval[name_loc->namelen];
valuelen = be16_to_cpu(name_loc->valuelen);
} else {
xfs_attr_leaf_name_remote_t *name_rmt;
......@@ -500,6 +508,7 @@ xfs_attr3_leaf_list_int(
name_rmt = xfs_attr3_leaf_name_remote(leaf, i);
name = name_rmt->name;
namelen = name_rmt->namelen;
value = NULL;
valuelen = be32_to_cpu(name_rmt->valuelen);
}
......@@ -510,7 +519,7 @@ xfs_attr3_leaf_list_int(
return -EFSCORRUPTED;
}
context->put_listent(context, entry->flags,
name, namelen, valuelen);
name, namelen, value, valuelen);
if (context->seen_enough)
break;
cursor->offset++;
......
......@@ -102,7 +102,7 @@ xfs_fs_encode_fh(
return fileid_type;
}
STATIC struct inode *
struct inode *
xfs_nfs_get_inode(
struct super_block *sb,
u64 ino,
......
......@@ -57,4 +57,6 @@ struct xfs_fid64 {
/* This flag goes on the wire. Don't play with it. */
#define XFS_FILEID_TYPE_64FLAG 0x80 /* NFS fileid has 64bit inodes */
struct inode *xfs_nfs_get_inode(struct super_block *sb, u64 ino, u32 gen);
#endif /* __XFS_EXPORT_H__ */
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2000-2005 Silicon Graphics, Inc.
* Copyright (c) 2022-2024 Oracle.
* All rights reserved.
*/
#ifndef __XFS_HANDLE_H__
#define __XFS_HANDLE_H__
int xfs_attrlist_by_handle(struct file *parfilp,
struct xfs_fsop_attrlist_handlereq __user *p);
int xfs_attrmulti_by_handle(struct file *parfilp, void __user *arg);
int xfs_find_handle(unsigned int cmd, struct xfs_fsop_handlereq *hreq);
int xfs_open_by_handle(struct file *parfilp, struct xfs_fsop_handlereq *hreq);
int xfs_readlink_by_handle(struct file *parfilp,
struct xfs_fsop_handlereq *hreq);
int xfs_ioc_attrmulti_one(struct file *parfilp, struct inode *inode,
uint32_t opcode, void __user *uname, void __user *value,
uint32_t *len, uint32_t flags);
int xfs_ioc_attr_list(struct xfs_inode *dp, void __user *ubuf,
size_t bufsize, int flags,
struct xfs_attrlist_cursor __user *ucursor);
struct dentry *xfs_handle_to_dentry(struct file *parfilp, void __user *uhandle,
u32 hlen);
int xfs_ioc_getparents(struct file *file, struct xfs_getparents __user *arg);
int xfs_ioc_getparents_by_handle(struct file *file,
struct xfs_getparents_by_handle __user *arg);
#endif /* __XFS_HANDLE_H__ */
This diff is collapsed.
......@@ -522,7 +522,7 @@ int xfs_create(struct mnt_idmap *idmap,
umode_t mode, dev_t rdev, bool need_xattr,
struct xfs_inode **ipp);
int xfs_create_tmpfile(struct mnt_idmap *idmap,
struct xfs_inode *dp, umode_t mode,
struct xfs_inode *dp, umode_t mode, bool init_xattrs,
struct xfs_inode **ipp);
int xfs_remove(struct xfs_inode *dp, struct xfs_name *name,
struct xfs_inode *ip);
......
This diff is collapsed.
......@@ -14,34 +14,6 @@ int
xfs_ioc_swapext(
xfs_swapext_t *sxp);
extern int
xfs_find_handle(
unsigned int cmd,
xfs_fsop_handlereq_t *hreq);
extern int
xfs_open_by_handle(
struct file *parfilp,
xfs_fsop_handlereq_t *hreq);
extern int
xfs_readlink_by_handle(
struct file *parfilp,
xfs_fsop_handlereq_t *hreq);
int xfs_ioc_attrmulti_one(struct file *parfilp, struct inode *inode,
uint32_t opcode, void __user *uname, void __user *value,
uint32_t *len, uint32_t flags);
int xfs_ioc_attr_list(struct xfs_inode *dp, void __user *ubuf,
size_t bufsize, int flags,
struct xfs_attrlist_cursor __user *ucursor);
extern struct dentry *
xfs_handle_to_dentry(
struct file *parfilp,
void __user *uhandle,
u32 hlen);
extern int
xfs_fileattr_get(
struct dentry *dentry,
......
......@@ -24,6 +24,7 @@
#include "xfs_ioctl32.h"
#include "xfs_trace.h"
#include "xfs_sb.h"
#include "xfs_handle.h"
#define _NATIVE_IOC(cmd, type) \
_IOC(_IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd), sizeof(type))
......
......@@ -157,6 +157,8 @@ xfs_create_need_xattr(
if (dir->i_sb->s_security)
return true;
#endif
if (xfs_has_parent(XFS_I(dir)->i_mount))
return true;
return false;
}
......@@ -201,7 +203,18 @@ xfs_generic_create(
xfs_create_need_xattr(dir, default_acl, acl),
&ip);
} else {
error = xfs_create_tmpfile(idmap, XFS_I(dir), mode, &ip);
bool init_xattrs = false;
/*
* If this temporary file will be linkable, set up the file
* with an attr fork to receive a parent pointer.
*/
if (!(tmpfile->f_flags & O_EXCL) &&
xfs_has_parent(XFS_I(dir)->i_mount))
init_xattrs = true;
error = xfs_create_tmpfile(idmap, XFS_I(dir), mode,
init_xattrs, &ip);
}
if (unlikely(error))
goto out_free_acl;
......
......@@ -44,6 +44,7 @@
#include "xfs_dahash_test.h"
#include "xfs_rtbitmap.h"
#include "xfs_exchmaps_item.h"
#include "xfs_parent.h"
#include "scrub/stats.h"
#include "scrub/rcbag_btree.h"
......@@ -1745,6 +1746,10 @@ xfs_fs_fill_super(
xfs_warn(mp,
"EXPERIMENTAL exchange-range feature enabled. Use at your own risk!");
if (xfs_has_parent(mp))
xfs_warn(mp,
"EXPERIMENTAL parent pointer feature enabled. Use at your own risk!");
error = xfs_mountfs(mp);
if (error)
goto out_filestream_unmount;
......@@ -2211,8 +2216,16 @@ xfs_init_caches(void)
if (!xfs_xmi_cache)
goto out_destroy_xmd_cache;
xfs_parent_args_cache = kmem_cache_create("xfs_parent_args",
sizeof(struct xfs_parent_args),
0, 0, NULL);
if (!xfs_parent_args_cache)
goto out_destroy_xmi_cache;
return 0;
out_destroy_xmi_cache:
kmem_cache_destroy(xfs_xmi_cache);
out_destroy_xmd_cache:
kmem_cache_destroy(xfs_xmd_cache);
out_destroy_iul_cache:
......@@ -2273,6 +2286,7 @@ xfs_destroy_caches(void)
* destroy caches.
*/
rcu_barrier();
kmem_cache_destroy(xfs_parent_args_cache);
kmem_cache_destroy(xfs_xmd_cache);
kmem_cache_destroy(xfs_xmi_cache);
kmem_cache_destroy(xfs_iunlink_cache);
......
......@@ -25,6 +25,8 @@
#include "xfs_error.h"
#include "xfs_health.h"
#include "xfs_symlink_remote.h"
#include "xfs_parent.h"
#include "xfs_defer.h"
int
xfs_readlink(
......@@ -100,6 +102,7 @@ xfs_symlink(
struct xfs_dquot *pdqp = NULL;
uint resblks;
xfs_ino_t ino;
struct xfs_parent_args *ppargs;
*ipp = NULL;
......@@ -130,18 +133,24 @@ xfs_symlink(
/*
* The symlink will fit into the inode data fork?
* There can't be any attributes so we get the whole variable part.
* If there are no parent pointers, then there wont't be any attributes.
* So we get the whole variable part, and do not need to reserve extra
* blocks. Otherwise, we need to reserve the blocks.
*/
if (pathlen <= XFS_LITINO(mp))
if (pathlen <= XFS_LITINO(mp) && !xfs_has_parent(mp))
fs_blocks = 0;
else
fs_blocks = xfs_symlink_blocks(mp, pathlen);
resblks = XFS_SYMLINK_SPACE_RES(mp, link_name->len, fs_blocks);
resblks = xfs_symlink_space_res(mp, link_name->len, fs_blocks);
error = xfs_parent_start(mp, &ppargs);
if (error)
goto out_release_dquots;
error = xfs_trans_alloc_icreate(mp, &M_RES(mp)->tr_symlink, udqp, gdqp,
pdqp, resblks, &tp);
if (error)
goto out_release_dquots;
goto out_parent;
xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
unlock_dp_on_error = true;
......@@ -161,7 +170,7 @@ xfs_symlink(
if (!error)
error = xfs_init_new_inode(idmap, tp, dp, ino,
S_IFLNK | (mode & ~S_IFMT), 1, 0, prid,
false, &ip);
xfs_has_parent(mp), &ip);
if (error)
goto out_trans_cancel;
......@@ -195,6 +204,14 @@ xfs_symlink(
goto out_trans_cancel;
xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
/* Add parent pointer for the new symlink. */
if (ppargs) {
error = xfs_parent_addname(tp, ppargs, dp, link_name, ip);
if (error)
goto out_trans_cancel;
}
xfs_dir_update_hook(dp, ip, 1, link_name);
/*
......@@ -216,6 +233,7 @@ xfs_symlink(
*ipp = ip;
xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_iunlock(dp, XFS_ILOCK_EXCL);
xfs_parent_finish(mp, ppargs);
return 0;
out_trans_cancel:
......@@ -231,6 +249,8 @@ xfs_symlink(
xfs_finish_inode_setup(ip);
xfs_irele(ip);
}
out_parent:
xfs_parent_finish(mp, ppargs);
out_release_dquots:
xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp);
......
......@@ -41,6 +41,7 @@
#include "xfs_bmap.h"
#include "xfs_exchmaps.h"
#include "xfs_exchrange.h"
#include "xfs_parent.h"
/*
* We include this last to have the helpers above available for the trace
......
......@@ -87,11 +87,15 @@ struct xfs_bmap_intent;
struct xfs_exchmaps_intent;
struct xfs_exchmaps_req;
struct xfs_exchrange;
struct xfs_getparents;
struct xfs_parent_irec;
struct xfs_attrlist_cursor_kern;
#define XFS_ATTR_FILTER_FLAGS \
{ XFS_ATTR_ROOT, "ROOT" }, \
{ XFS_ATTR_SECURE, "SECURE" }, \
{ XFS_ATTR_INCOMPLETE, "INCOMPLETE" }
{ XFS_ATTR_INCOMPLETE, "INCOMPLETE" }, \
{ XFS_ATTR_PARENT, "PARENT" }
DECLARE_EVENT_CLASS(xfs_attr_list_class,
TP_PROTO(struct xfs_attr_list_context *ctx),
......@@ -5095,6 +5099,95 @@ TRACE_EVENT(xfs_exchmaps_delta_nextents,
__entry->d_nexts1, __entry->d_nexts2)
);
DECLARE_EVENT_CLASS(xfs_getparents_rec_class,
TP_PROTO(struct xfs_inode *ip, const struct xfs_getparents *ppi,
const struct xfs_attr_list_context *context,
const struct xfs_getparents_rec *pptr),
TP_ARGS(ip, ppi, context, pptr),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(unsigned int, firstu)
__field(unsigned short, reclen)
__field(unsigned int, bufsize)
__field(xfs_ino_t, parent_ino)
__field(unsigned int, parent_gen)
__string(name, pptr->gpr_name)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
__entry->firstu = context->firstu;
__entry->reclen = pptr->gpr_reclen;
__entry->bufsize = ppi->gp_bufsize;
__entry->parent_ino = pptr->gpr_parent.ha_fid.fid_ino;
__entry->parent_gen = pptr->gpr_parent.ha_fid.fid_gen;
__assign_str(name, pptr->gpr_name);
),
TP_printk("dev %d:%d ino 0x%llx firstu %u reclen %u bufsize %u parent_ino 0x%llx parent_gen 0x%x name '%s'",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->firstu,
__entry->reclen,
__entry->bufsize,
__entry->parent_ino,
__entry->parent_gen,
__get_str(name))
)
#define DEFINE_XFS_GETPARENTS_REC_EVENT(name) \
DEFINE_EVENT(xfs_getparents_rec_class, name, \
TP_PROTO(struct xfs_inode *ip, const struct xfs_getparents *ppi, \
const struct xfs_attr_list_context *context, \
const struct xfs_getparents_rec *pptr), \
TP_ARGS(ip, ppi, context, pptr))
DEFINE_XFS_GETPARENTS_REC_EVENT(xfs_getparents_put_listent);
DEFINE_XFS_GETPARENTS_REC_EVENT(xfs_getparents_expand_lastrec);
DECLARE_EVENT_CLASS(xfs_getparents_class,
TP_PROTO(struct xfs_inode *ip, const struct xfs_getparents *ppi,
const struct xfs_attrlist_cursor_kern *cur),
TP_ARGS(ip, ppi, cur),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(unsigned short, iflags)
__field(unsigned short, oflags)
__field(unsigned int, bufsize)
__field(unsigned int, hashval)
__field(unsigned int, blkno)
__field(unsigned int, offset)
__field(int, initted)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
__entry->iflags = ppi->gp_iflags;
__entry->oflags = ppi->gp_oflags;
__entry->bufsize = ppi->gp_bufsize;
__entry->hashval = cur->hashval;
__entry->blkno = cur->blkno;
__entry->offset = cur->offset;
__entry->initted = cur->initted;
),
TP_printk("dev %d:%d ino 0x%llx iflags 0x%x oflags 0x%x bufsize %u cur_init? %d hashval 0x%x blkno %u offset %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->iflags,
__entry->oflags,
__entry->bufsize,
__entry->initted,
__entry->hashval,
__entry->blkno,
__entry->offset)
)
#define DEFINE_XFS_GETPARENTS_EVENT(name) \
DEFINE_EVENT(xfs_getparents_class, name, \
TP_PROTO(struct xfs_inode *ip, const struct xfs_getparents *ppi, \
const struct xfs_attrlist_cursor_kern *cur), \
TP_ARGS(ip, ppi, cur))
DEFINE_XFS_GETPARENTS_EVENT(xfs_getparents_begin);
DEFINE_XFS_GETPARENTS_EVENT(xfs_getparents_end);
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH
......
......@@ -222,6 +222,7 @@ xfs_xattr_put_listent(
int flags,
unsigned char *name,
int namelen,
void *value,
int valuelen)
{
char *prefix;
......@@ -229,6 +230,10 @@ xfs_xattr_put_listent(
ASSERT(context->count >= 0);
/* Don't expose private xattr namespaces. */
if (flags & XFS_ATTR_PRIVATE_NSP_MASK)
return;
if (flags & XFS_ATTR_ROOT) {
#ifdef CONFIG_XFS_POSIX_ACL
if (namelen == SGI_ACL_FILE_SIZE &&
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment