Commit 1da824b0 authored by Chandan Babu R's avatar Chandan Babu R

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

Merge tag 'repair-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: online repair for parent pointers

This series implements online repair for directory parent pointer
metadata.  The checking half is fairly straightforward -- for each
outgoing directory link (forward or backwards), grab the inode at the
other end, and confirm that there's a corresponding link.  If we can't
grab an inode or lock it, we'll save that link for a slower loop that
cycles all the locks, confirms the continued existence of the link, and
rechecks the link if it's actually still there.

Repairs are a bit more involved -- for directories, we walk the entire
filesystem to rebuild the dirents from parent pointer information.
Parent pointer repairs do the same walk but rebuild the pptrs from the
dirent information, but with the added twist that it duplicates all the
xattrs so that it can use the atomic extent swapping code to commit the
repairs atomically.

This introduces an added twist to the xattr repair code -- we use dirent
hooks to detect a colliding update to the pptr data while we're not
holding the ILOCKs; if one is detected, we restart the xattr salvaging
process but this time hold all the ILOCKs until the end of the scan.

For offline repair, the phase6 directory connectivity scan generates an
index of all the expected parent pointers in the filesystem.  Then it
walks each file and compares the parent pointers attached to that file
against the index generated, and resyncs the results as necessary.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Signed-off-by: default avatarChandan Babu R <chandanbabu@kernel.org>

* tag 'repair-pptrs-6.10_2024-04-23' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux:
  xfs: inode repair should ensure there's an attr fork to store parent pointers
  xfs: repair link count of nondirectories after rebuilding parent pointers
  xfs: adapt the orphanage code to handle parent pointers
  xfs: actually rebuild the parent pointer xattrs
  xfs: add a per-leaf block callback to xchk_xattr_walk
  xfs: split xfs_bmap_add_attrfork into two pieces
  xfs: remove pointless unlocked assertion
  xfs: implement live updates for parent pointer repairs
  xfs: repair directory parent pointers by scanning for dirents
  xfs: replay unlocked parent pointer updates that accrue during xattr repair
  xfs: implement live updates for directory repairs
  xfs: repair directories by scanning directory parent pointers
  xfs: add raw parent pointer apis to support repair
  xfs: salvage parent pointers when rebuilding xattr structures
  xfs: make the reserved block permission flag explicit in xfs_attr_set
  xfs: remove some boilerplate from xfs_attr_set
parents 0d2dd382 327ed702
......@@ -948,40 +948,67 @@ xfs_attr_lookup(
return error;
}
int
xfs_attr_add_fork(
struct xfs_inode *ip, /* incore inode pointer */
int size, /* space new attribute needs */
int rsvd) /* xact may use reserved blks */
{
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp; /* transaction pointer */
unsigned int blks; /* space reservation */
int error; /* error return value */
ASSERT(!XFS_NOT_DQATTACHED(mp, ip));
blks = XFS_ADDAFORK_SPACE_RES(mp);
error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_addafork, blks, 0,
rsvd, &tp);
if (error)
return error;
if (xfs_inode_has_attr_fork(ip))
goto trans_cancel;
error = xfs_bmap_add_attrfork(tp, ip, size, rsvd);
if (error)
goto trans_cancel;
error = xfs_trans_commit(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
return error;
trans_cancel:
xfs_trans_cancel(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
return error;
}
/*
* Make a change to the xattr structure.
*
* The caller must have initialized @args, attached dquots, and must not hold
* any ILOCKs. Reserved data blocks may be used if @rsvd is set.
*
* Returns -EEXIST for XFS_ATTRUPDATE_CREATE if the name already exists.
* Returns -ENOATTR for XFS_ATTRUPDATE_REMOVE if the name does not exist.
* Returns 0 on success, or a negative errno if something else went wrong.
*/
int
xfs_attr_set(
struct xfs_da_args *args,
enum xfs_attr_update op)
enum xfs_attr_update op,
bool rsvd)
{
struct xfs_inode *dp = args->dp;
struct xfs_mount *mp = dp->i_mount;
struct xfs_trans_res tres;
bool rsvd = (args->attr_filter & XFS_ATTR_ROOT);
int error, local;
int rmt_blks = 0;
unsigned int total;
if (xfs_is_shutdown(dp->i_mount))
return -EIO;
error = xfs_qm_dqattach(dp);
if (error)
return error;
if (!args->owner)
args->owner = args->dp->i_ino;
args->geo = mp->m_attr_geo;
args->whichfork = XFS_ATTR_FORK;
xfs_attr_sethash(args);
/*
* 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. Preserve the logged flag, since we need
* to pass that through to the logging code.
*/
args->op_flags = XFS_DA_OP_OKNOENT |
(args->op_flags & XFS_DA_OP_LOGGED);
ASSERT(!args->trans);
switch (op) {
case XFS_ATTRUPDATE_UPSERT:
......@@ -999,7 +1026,7 @@ xfs_attr_set(
xfs_attr_sf_entsize_byname(args->namelen,
args->valuelen);
error = xfs_bmap_add_attrfork(dp, sf_size, rsvd);
error = xfs_attr_add_fork(dp, sf_size, rsvd);
if (error)
return error;
}
......@@ -1076,6 +1103,7 @@ xfs_attr_set(
error = xfs_trans_commit(args->trans);
out_unlock:
xfs_iunlock(dp, XFS_ILOCK_EXCL);
args->trans = NULL;
return error;
out_trans_cancel:
......
......@@ -558,7 +558,7 @@ enum xfs_attr_update {
XFS_ATTRUPDATE_REPLACE, /* set value, fail if attr does not exist */
};
int xfs_attr_set(struct xfs_da_args *args, enum xfs_attr_update op);
int xfs_attr_set(struct xfs_da_args *args, enum xfs_attr_update op, bool rsvd);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
bool xfs_attr_check_namespace(unsigned int attr_flags);
......@@ -648,5 +648,6 @@ int __init xfs_attr_intent_init_cache(void);
void xfs_attr_intent_destroy_cache(void);
int xfs_attr_sf_totsize(struct xfs_inode *dp);
int xfs_attr_add_fork(struct xfs_inode *ip, int size, int rsvd);
#endif /* __XFS_ATTR_H__ */
......@@ -1025,40 +1025,29 @@ xfs_bmap_set_attrforkoff(
}
/*
* Convert inode from non-attributed to attributed.
* Must not be in a transaction, ip must not be locked.
* Convert inode from non-attributed to attributed. Caller must hold the
* ILOCK_EXCL and the file cannot have an attr fork.
*/
int /* error code */
xfs_bmap_add_attrfork(
xfs_inode_t *ip, /* incore inode pointer */
struct xfs_trans *tp,
struct xfs_inode *ip, /* incore inode pointer */
int size, /* space new attribute needs */
int rsvd) /* xact may use reserved blks */
{
xfs_mount_t *mp; /* mount structure */
xfs_trans_t *tp; /* transaction pointer */
int blks; /* space reservation */
struct xfs_mount *mp = tp->t_mountp;
int version = 1; /* superblock attr version */
int logflags; /* logging flags */
int error; /* error return value */
ASSERT(xfs_inode_has_attr_fork(ip) == 0);
mp = ip->i_mount;
xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
ASSERT(!XFS_NOT_DQATTACHED(mp, ip));
blks = XFS_ADDAFORK_SPACE_RES(mp);
error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_addafork, blks, 0,
rsvd, &tp);
if (error)
return error;
if (xfs_inode_has_attr_fork(ip))
goto trans_cancel;
ASSERT(!xfs_inode_has_attr_fork(ip));
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
error = xfs_bmap_set_attrforkoff(ip, size, &version);
if (error)
goto trans_cancel;
return error;
xfs_ifork_init_attr(ip, XFS_DINODE_FMT_EXTENTS, 0);
logflags = 0;
......@@ -1079,7 +1068,7 @@ xfs_bmap_add_attrfork(
if (logflags)
xfs_trans_log_inode(tp, ip, logflags);
if (error)
goto trans_cancel;
return error;
if (!xfs_has_attr(mp) ||
(!xfs_has_attr2(mp) && version == 2)) {
bool log_sb = false;
......@@ -1098,14 +1087,7 @@ xfs_bmap_add_attrfork(
xfs_log_sb(tp);
}
error = xfs_trans_commit(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
return error;
trans_cancel:
xfs_trans_cancel(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
return error;
return 0;
}
/*
......
......@@ -176,7 +176,8 @@ int xfs_bmap_longest_free_extent(struct xfs_perag *pag,
void xfs_trim_extent(struct xfs_bmbt_irec *irec, xfs_fileoff_t bno,
xfs_filblks_t len);
unsigned int xfs_bmap_compute_attr_offset(struct xfs_mount *mp);
int xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd);
int xfs_bmap_add_attrfork(struct xfs_trans *tp, struct xfs_inode *ip,
int size, int rsvd);
void xfs_bmap_local_to_extents_empty(struct xfs_trans *tp,
struct xfs_inode *ip, int whichfork);
int xfs_bmap_local_to_extents(struct xfs_trans *tp, struct xfs_inode *ip,
......
......@@ -434,7 +434,7 @@ int
xfs_dir_removename(
struct xfs_trans *tp,
struct xfs_inode *dp,
struct xfs_name *name,
const struct xfs_name *name,
xfs_ino_t ino,
xfs_extlen_t total) /* bmap's total block count */
{
......
......@@ -58,7 +58,7 @@ extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
const struct xfs_name *name, xfs_ino_t *inum,
struct xfs_name *ci_name);
extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
struct xfs_name *name, xfs_ino_t ino,
const struct xfs_name *name, xfs_ino_t ino,
xfs_extlen_t tot);
extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
const struct xfs_name *name, xfs_ino_t inum,
......
......@@ -313,3 +313,67 @@ xfs_parent_lookup(
xfs_parent_da_args_init(scratch, tp, pptr, ip, ip->i_ino, parent_name);
return xfs_attr_get_ilocked(scratch);
}
/* Sanity-check a parent pointer before we try to perform repairs. */
static inline bool
xfs_parent_sanity_check(
struct xfs_mount *mp,
const struct xfs_name *parent_name,
const struct xfs_parent_rec *pptr)
{
if (!xfs_parent_namecheck(XFS_ATTR_PARENT, parent_name->name,
parent_name->len))
return false;
if (!xfs_parent_valuecheck(mp, pptr, sizeof(*pptr)))
return false;
return true;
}
/*
* Attach the parent pointer (@parent_name -> @pptr) to @ip immediately.
* Caller must not have a transaction or hold the ILOCK. This is for
* specialized repair functions only. The scratchpad need not be initialized.
*/
int
xfs_parent_set(
struct xfs_inode *ip,
xfs_ino_t owner,
const struct xfs_name *parent_name,
struct xfs_parent_rec *pptr,
struct xfs_da_args *scratch)
{
if (!xfs_parent_sanity_check(ip->i_mount, parent_name, pptr)) {
ASSERT(0);
return -EFSCORRUPTED;
}
memset(scratch, 0, sizeof(struct xfs_da_args));
xfs_parent_da_args_init(scratch, NULL, pptr, ip, owner, parent_name);
return xfs_attr_set(scratch, XFS_ATTRUPDATE_CREATE, false);
}
/*
* Remove the parent pointer (@parent_name -> @pptr) from @ip immediately.
* Caller must not have a transaction or hold the ILOCK. This is for
* specialized repair functions only. The scratchpad need not be initialized.
*/
int
xfs_parent_unset(
struct xfs_inode *ip,
xfs_ino_t owner,
const struct xfs_name *parent_name,
struct xfs_parent_rec *pptr,
struct xfs_da_args *scratch)
{
if (!xfs_parent_sanity_check(ip->i_mount, parent_name, pptr)) {
ASSERT(0);
return -EFSCORRUPTED;
}
memset(scratch, 0, sizeof(struct xfs_da_args));
xfs_parent_da_args_init(scratch, NULL, pptr, ip, owner, parent_name);
return xfs_attr_set(scratch, XFS_ATTRUPDATE_REMOVE, false);
}
......@@ -100,5 +100,11 @@ int xfs_parent_from_attr(struct xfs_mount *mp, unsigned int attr_flags,
int xfs_parent_lookup(struct xfs_trans *tp, struct xfs_inode *ip,
const struct xfs_name *name, struct xfs_parent_rec *pptr,
struct xfs_da_args *scratch);
int xfs_parent_set(struct xfs_inode *ip, xfs_ino_t owner,
const struct xfs_name *name, struct xfs_parent_rec *pptr,
struct xfs_da_args *scratch);
int xfs_parent_unset(struct xfs_inode *ip, xfs_ino_t owner,
const struct xfs_name *name, struct xfs_parent_rec *pptr,
struct xfs_da_args *scratch);
#endif /* __XFS_PARENT_H__ */
......@@ -675,7 +675,7 @@ xchk_xattr(
* iteration, which doesn't really follow the usual buffer
* locking order.
*/
error = xchk_xattr_walk(sc, sc->ip, xchk_xattr_actor, NULL);
error = xchk_xattr_walk(sc, sc->ip, xchk_xattr_actor, NULL, NULL);
if (!xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error))
return error;
......
This diff is collapsed.
......@@ -6,6 +6,10 @@
#ifndef __XFS_SCRUB_ATTR_REPAIR_H__
#define __XFS_SCRUB_ATTR_REPAIR_H__
struct xrep_tempexch;
int xrep_xattr_swap(struct xfs_scrub *sc, struct xrep_tempexch *tx);
int xrep_xattr_reset_fork(struct xfs_scrub *sc);
int xrep_xattr_reset_tempfile_fork(struct xfs_scrub *sc);
#endif /* __XFS_SCRUB_ATTR_REPAIR_H__ */
This diff is collapsed.
......@@ -24,6 +24,7 @@
#include "xfs_trans_space.h"
#include "xfs_health.h"
#include "xfs_exchmaps.h"
#include "xfs_parent.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
......@@ -33,6 +34,7 @@
#include "scrub/findparent.h"
#include "scrub/readdir.h"
#include "scrub/tempfile.h"
#include "scrub/listxattr.h"
/*
* Finding the Parent of a Directory
......@@ -238,9 +240,10 @@ xrep_findparent_live_update(
* will be called when there is a dotdot update for the inode being repaired.
*/
int
xrep_findparent_scan_start(
__xrep_findparent_scan_start(
struct xfs_scrub *sc,
struct xrep_parent_scan_info *pscan)
struct xrep_parent_scan_info *pscan,
notifier_fn_t custom_fn)
{
int error;
......@@ -262,7 +265,10 @@ xrep_findparent_scan_start(
* ILOCK, which means that any in-progress inode updates will finish
* before we can scan the inode.
*/
xfs_dir_hook_setup(&pscan->dhook, xrep_findparent_live_update);
if (custom_fn)
xfs_dir_hook_setup(&pscan->dhook, custom_fn);
else
xfs_dir_hook_setup(&pscan->dhook, xrep_findparent_live_update);
error = xfs_dir_hook_add(sc->mp, &pscan->dhook);
if (error)
goto out_iscan;
......
......@@ -24,8 +24,14 @@ struct xrep_parent_scan_info {
bool lookup_parent;
};
int xrep_findparent_scan_start(struct xfs_scrub *sc,
struct xrep_parent_scan_info *pscan);
int __xrep_findparent_scan_start(struct xfs_scrub *sc,
struct xrep_parent_scan_info *pscan,
notifier_fn_t custom_fn);
static inline int xrep_findparent_scan_start(struct xfs_scrub *sc,
struct xrep_parent_scan_info *pscan)
{
return __xrep_findparent_scan_start(sc, pscan, NULL);
}
int xrep_findparent_scan(struct xrep_parent_scan_info *pscan);
void xrep_findparent_scan_teardown(struct xrep_parent_scan_info *pscan);
......
......@@ -1736,6 +1736,44 @@ xrep_inode_extsize(
}
}
/* Ensure this file has an attr fork if it needs to hold a parent pointer. */
STATIC int
xrep_inode_pptr(
struct xfs_scrub *sc)
{
struct xfs_mount *mp = sc->mp;
struct xfs_inode *ip = sc->ip;
struct inode *inode = VFS_I(ip);
if (!xfs_has_parent(mp))
return 0;
/*
* Unlinked inodes that cannot be added to the directory tree will not
* have a parent pointer.
*/
if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE))
return 0;
/* The root directory doesn't have a parent pointer. */
if (ip == mp->m_rootip)
return 0;
/*
* Metadata inodes are rooted in the superblock and do not have any
* parents.
*/
if (xfs_is_metadata_inode(ip))
return 0;
/* Inode already has an attr fork; no further work possible here. */
if (xfs_inode_has_attr_fork(ip))
return 0;
return xfs_bmap_add_attrfork(sc->tp, ip,
sizeof(struct xfs_attr_sf_hdr), true);
}
/* Fix any irregularities in an inode that the verifiers don't catch. */
STATIC int
xrep_inode_problems(
......@@ -1744,6 +1782,9 @@ xrep_inode_problems(
int error;
error = xrep_inode_blockcounts(sc);
if (error)
return error;
error = xrep_inode_pptr(sc);
if (error)
return error;
xrep_inode_timestamps(sc->ip);
......
......@@ -221,6 +221,7 @@ xchk_xattr_walk_node(
struct xfs_scrub *sc,
struct xfs_inode *ip,
xchk_xattr_fn attr_fn,
xchk_xattrleaf_fn leaf_fn,
void *priv)
{
struct xfs_attr3_icleaf_hdr leafhdr;
......@@ -252,6 +253,12 @@ xchk_xattr_walk_node(
xfs_trans_brelse(sc->tp, leaf_bp);
if (leaf_fn) {
error = leaf_fn(sc, priv);
if (error)
goto out_bitmap;
}
/* Make sure we haven't seen this new leaf already. */
len = 1;
if (xdab_bitmap_test(&seen_dablks, leafhdr.forw, &len)) {
......@@ -288,6 +295,7 @@ xchk_xattr_walk(
struct xfs_scrub *sc,
struct xfs_inode *ip,
xchk_xattr_fn attr_fn,
xchk_xattrleaf_fn leaf_fn,
void *priv)
{
int error;
......@@ -308,5 +316,5 @@ xchk_xattr_walk(
if (xfs_attr_is_leaf(ip))
return xchk_xattr_walk_leaf(sc, ip, attr_fn, priv);
return xchk_xattr_walk_node(sc, ip, attr_fn, priv);
return xchk_xattr_walk_node(sc, ip, attr_fn, leaf_fn, priv);
}
......@@ -11,7 +11,9 @@ typedef int (*xchk_xattr_fn)(struct xfs_scrub *sc, struct xfs_inode *ip,
unsigned int namelen, const void *value, unsigned int valuelen,
void *priv);
typedef int (*xchk_xattrleaf_fn)(struct xfs_scrub *sc, void *priv);
int xchk_xattr_walk(struct xfs_scrub *sc, struct xfs_inode *ip,
xchk_xattr_fn attr_fn, void *priv);
xchk_xattr_fn attr_fn, xchk_xattrleaf_fn leaf_fn, void *priv);
#endif /* __XFS_SCRUB_LISTXATTR_H__ */
......@@ -434,7 +434,8 @@ xchk_nlinks_collect_dir(
goto out_unlock;
}
error = xchk_xattr_walk(sc, dp, xchk_nlinks_collect_pptr, xnc);
error = xchk_xattr_walk(sc, dp, xchk_nlinks_collect_pptr, NULL,
xnc);
if (error == -ECANCELED) {
error = 0;
goto out_unlock;
......
......@@ -19,6 +19,8 @@
#include "xfs_icache.h"
#include "xfs_bmap.h"
#include "xfs_bmap_btree.h"
#include "xfs_parent.h"
#include "xfs_attr_sf.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/repair.h"
......@@ -330,6 +332,8 @@ xrep_adoption_trans_alloc(
if (S_ISDIR(VFS_I(sc->ip)->i_mode))
child_blkres = xfs_rename_space_res(mp, 0, false,
xfs_name_dotdot.len, false);
if (xfs_has_parent(mp))
child_blkres += XFS_ADDAFORK_SPACE_RES(mp);
adopt->child_blkres = child_blkres;
/*
......@@ -503,6 +507,19 @@ xrep_adoption_zap_dcache(
dput(d_orphanage);
}
/*
* If we have to add an attr fork ahead of a parent pointer update, how much
* space should we ask for?
*/
static inline int
xrep_adoption_attr_sizeof(
const struct xrep_adoption *adopt)
{
return sizeof(struct xfs_attr_sf_hdr) +
xfs_attr_sf_entsize_byname(sizeof(struct xfs_parent_rec),
adopt->xname->len);
}
/*
* Move the current file to the orphanage under the computed name.
*
......@@ -524,6 +541,19 @@ xrep_adoption_move(
if (error)
return error;
/*
* If this filesystem has parent pointers, ensure that the file being
* moved to the orphanage has an attribute fork. This is required
* because the parent pointer code does not itself add attr forks.
*/
if (!xfs_inode_has_attr_fork(sc->ip) && xfs_has_parent(sc->mp)) {
int sf_size = xrep_adoption_attr_sizeof(adopt);
error = xfs_bmap_add_attrfork(sc->tp, sc->ip, sf_size, true);
if (error)
return error;
}
/* Create the new name in the orphanage. */
error = xfs_dir_createname(sc->tp, sc->orphanage, adopt->xname,
sc->ip->i_ino, adopt->orphanage_blkres);
......@@ -548,6 +578,14 @@ xrep_adoption_move(
return error;
}
/* Add a parent pointer from the file back to the lost+found. */
if (xfs_has_parent(sc->mp)) {
error = xfs_parent_addname(sc->tp, &adopt->ppargs,
sc->orphanage, adopt->xname, sc->ip);
if (error)
return error;
}
/*
* Notify dirent hooks that we moved the file to /lost+found, and
* finish all the deferred work so that we know the adoption is fully
......
......@@ -54,6 +54,9 @@ struct xrep_adoption {
/* Name used for the adoption. */
struct xfs_name *xname;
/* Parent pointer context tracking */
struct xfs_parent_args ppargs;
/* Block reservations for orphanage and child (if directory). */
unsigned int orphanage_blkres;
unsigned int child_blkres;
......
......@@ -317,7 +317,7 @@ xchk_parent_pptr_and_dotdot(
return 0;
/* Otherwise, walk the pptrs again, and check. */
error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_dotdot, pp);
error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_dotdot, NULL, pp);
if (error == -ECANCELED) {
/* Found a parent pointer that matches dotdot. */
return 0;
......@@ -699,7 +699,8 @@ xchk_parent_count_pptrs(
*/
if (pp->need_revalidate) {
pp->pptrs_found = 0;
error = xchk_xattr_walk(sc, sc->ip, xchk_parent_count_pptr, pp);
error = xchk_xattr_walk(sc, sc->ip, xchk_parent_count_pptr,
NULL, pp);
if (error == -EFSCORRUPTED) {
/* Found a bad parent pointer */
xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
......@@ -758,7 +759,7 @@ xchk_parent_pptr(
if (error)
goto out_entries;
error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_attr, pp);
error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_attr, NULL, pp);
if (error == -ECANCELED) {
error = 0;
goto out_names;
......
This diff is collapsed.
......@@ -19,6 +19,8 @@
#include "xfs_rmap.h"
#include "xfs_exchrange.h"
#include "xfs_exchmaps.h"
#include "xfs_dir2.h"
#include "xfs_parent.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
......
......@@ -2539,6 +2539,46 @@ DEFINE_EVENT(xrep_xattr_salvage_class, name, \
TP_ARGS(ip, flags, name, namelen, valuelen))
DEFINE_XREP_XATTR_SALVAGE_EVENT(xrep_xattr_salvage_rec);
DEFINE_XREP_XATTR_SALVAGE_EVENT(xrep_xattr_insert_rec);
DEFINE_XREP_XATTR_SALVAGE_EVENT(xrep_parent_stash_xattr);
DEFINE_XREP_XATTR_SALVAGE_EVENT(xrep_parent_insert_xattr);
DECLARE_EVENT_CLASS(xrep_pptr_salvage_class,
TP_PROTO(struct xfs_inode *ip, unsigned int flags, const void *name,
unsigned int namelen, const void *value, unsigned int valuelen),
TP_ARGS(ip, flags, name, namelen, value, valuelen),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(xfs_ino_t, parent_ino)
__field(unsigned int, parent_gen)
__field(unsigned int, namelen)
__dynamic_array(char, name, namelen)
),
TP_fast_assign(
const struct xfs_parent_rec *rec = value;
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
__entry->parent_ino = be64_to_cpu(rec->p_ino);
__entry->parent_gen = be32_to_cpu(rec->p_gen);
__entry->namelen = namelen;
memcpy(__get_str(name), name, namelen);
),
TP_printk("dev %d:%d ino 0x%llx parent_ino 0x%llx parent_gen 0x%x name '%.*s'",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->parent_ino,
__entry->parent_gen,
__entry->namelen,
__get_str(name))
)
#define DEFINE_XREP_PPTR_SALVAGE_EVENT(name) \
DEFINE_EVENT(xrep_pptr_salvage_class, name, \
TP_PROTO(struct xfs_inode *ip, unsigned int flags, const void *name, \
unsigned int namelen, const void *value, unsigned int valuelen), \
TP_ARGS(ip, flags, name, namelen, value, valuelen))
DEFINE_XREP_PPTR_SALVAGE_EVENT(xrep_xattr_salvage_pptr);
DEFINE_XREP_PPTR_SALVAGE_EVENT(xrep_xattr_insert_pptr);
TRACE_EVENT(xrep_xattr_class,
TP_PROTO(struct xfs_inode *ip, struct xfs_inode *arg_ip),
......@@ -2564,6 +2604,43 @@ DEFINE_EVENT(xrep_xattr_class, name, \
TP_ARGS(ip, arg_ip))
DEFINE_XREP_XATTR_EVENT(xrep_xattr_rebuild_tree);
DEFINE_XREP_XATTR_EVENT(xrep_xattr_reset_fork);
DEFINE_XREP_XATTR_EVENT(xrep_xattr_full_reset);
DECLARE_EVENT_CLASS(xrep_xattr_pptr_scan_class,
TP_PROTO(struct xfs_inode *ip, const struct xfs_inode *dp,
const struct xfs_name *name),
TP_ARGS(ip, dp, name),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(xfs_ino_t, parent_ino)
__field(unsigned int, parent_gen)
__field(unsigned int, namelen)
__dynamic_array(char, name, name->len)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
__entry->parent_ino = dp->i_ino;
__entry->parent_gen = VFS_IC(dp)->i_generation;
__entry->namelen = name->len;
memcpy(__get_str(name), name->name, name->len);
),
TP_printk("dev %d:%d ino 0x%llx parent_ino 0x%llx parent_gen 0x%x name '%.*s'",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->parent_ino,
__entry->parent_gen,
__entry->namelen,
__get_str(name))
)
#define DEFINE_XREP_XATTR_PPTR_SCAN_EVENT(name) \
DEFINE_EVENT(xrep_xattr_pptr_scan_class, name, \
TP_PROTO(struct xfs_inode *ip, const struct xfs_inode *dp, \
const struct xfs_name *name), \
TP_ARGS(ip, dp, name))
DEFINE_XREP_XATTR_PPTR_SCAN_EVENT(xrep_xattr_stash_parentadd);
DEFINE_XREP_XATTR_PPTR_SCAN_EVENT(xrep_xattr_stash_parentremove);
TRACE_EVENT(xrep_dir_recover_dirblock,
TP_PROTO(struct xfs_inode *dp, xfs_dablk_t dabno, uint32_t magic,
......@@ -2654,6 +2731,8 @@ DEFINE_XREP_DIRENT_EVENT(xrep_dir_salvage_entry);
DEFINE_XREP_DIRENT_EVENT(xrep_dir_stash_createname);
DEFINE_XREP_DIRENT_EVENT(xrep_dir_replay_createname);
DEFINE_XREP_DIRENT_EVENT(xrep_adoption_reparent);
DEFINE_XREP_DIRENT_EVENT(xrep_dir_stash_removename);
DEFINE_XREP_DIRENT_EVENT(xrep_dir_replay_removename);
DECLARE_EVENT_CLASS(xrep_adoption_class,
TP_PROTO(struct xfs_inode *dp, struct xfs_inode *ip, bool moved),
......@@ -2708,6 +2787,80 @@ DEFINE_XREP_PARENT_SALVAGE_EVENT(xrep_dir_salvaged_parent);
DEFINE_XREP_PARENT_SALVAGE_EVENT(xrep_findparent_dirent);
DEFINE_XREP_PARENT_SALVAGE_EVENT(xrep_findparent_from_dcache);
DECLARE_EVENT_CLASS(xrep_pptr_class,
TP_PROTO(struct xfs_inode *ip, const struct xfs_name *name,
const struct xfs_parent_rec *pptr),
TP_ARGS(ip, name, pptr),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(xfs_ino_t, parent_ino)
__field(unsigned int, parent_gen)
__field(unsigned int, namelen)
__dynamic_array(char, name, name->len)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
__entry->parent_ino = be64_to_cpu(pptr->p_ino);
__entry->parent_gen = be32_to_cpu(pptr->p_gen);
__entry->namelen = name->len;
memcpy(__get_str(name), name->name, name->len);
),
TP_printk("dev %d:%d ino 0x%llx parent_ino 0x%llx parent_gen 0x%x name '%.*s'",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->parent_ino,
__entry->parent_gen,
__entry->namelen,
__get_str(name))
)
#define DEFINE_XREP_PPTR_EVENT(name) \
DEFINE_EVENT(xrep_pptr_class, name, \
TP_PROTO(struct xfs_inode *ip, const struct xfs_name *name, \
const struct xfs_parent_rec *pptr), \
TP_ARGS(ip, name, pptr))
DEFINE_XREP_PPTR_EVENT(xrep_xattr_replay_parentadd);
DEFINE_XREP_PPTR_EVENT(xrep_xattr_replay_parentremove);
DEFINE_XREP_PPTR_EVENT(xrep_parent_replay_parentadd);
DEFINE_XREP_PPTR_EVENT(xrep_parent_replay_parentremove);
DECLARE_EVENT_CLASS(xrep_pptr_scan_class,
TP_PROTO(struct xfs_inode *ip, const struct xfs_inode *dp,
const struct xfs_name *name),
TP_ARGS(ip, dp, name),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(xfs_ino_t, parent_ino)
__field(unsigned int, parent_gen)
__field(unsigned int, namelen)
__dynamic_array(char, name, name->len)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
__entry->parent_ino = dp->i_ino;
__entry->parent_gen = VFS_IC(dp)->i_generation;
__entry->namelen = name->len;
memcpy(__get_str(name), name->name, name->len);
),
TP_printk("dev %d:%d ino 0x%llx parent_ino 0x%llx parent_gen 0x%x name '%.*s'",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->parent_ino,
__entry->parent_gen,
__entry->namelen,
__get_str(name))
)
#define DEFINE_XREP_PPTR_SCAN_EVENT(name) \
DEFINE_EVENT(xrep_pptr_scan_class, name, \
TP_PROTO(struct xfs_inode *ip, const struct xfs_inode *dp, \
const struct xfs_name *name), \
TP_ARGS(ip, dp, name))
DEFINE_XREP_PPTR_SCAN_EVENT(xrep_parent_stash_parentadd);
DEFINE_XREP_PPTR_SCAN_EVENT(xrep_parent_stash_parentremove);
TRACE_EVENT(xrep_nlinks_set_record,
TP_PROTO(struct xfs_mount *mp, xfs_ino_t ino,
const struct xchk_nlink *obs),
......
......@@ -17,6 +17,7 @@
#include "xfs_acl.h"
#include "xfs_log.h"
#include "xfs_xattr.h"
#include "xfs_quota.h"
#include <linux/posix_acl_xattr.h>
......@@ -70,7 +71,9 @@ xfs_attr_want_log_assist(
/*
* Set or remove an xattr, having grabbed the appropriate logging resources
* prior to calling libxfs.
* 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.
*/
int
xfs_attr_change(
......@@ -80,7 +83,19 @@ xfs_attr_change(
struct xfs_mount *mp = args->dp->i_mount;
int error;
ASSERT(!(args->op_flags & XFS_DA_OP_LOGGED));
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;
if (xfs_attr_want_log_assist(mp)) {
error = xfs_attr_grab_log_assist(mp);
......@@ -90,7 +105,12 @@ xfs_attr_change(
args->op_flags |= XFS_DA_OP_LOGGED;
}
return xfs_attr_set(args, op);
args->owner = args->dp->i_ino;
args->geo = mp->m_attr_geo;
args->whichfork = XFS_ATTR_FORK;
xfs_attr_sethash(args);
return xfs_attr_set(args, op, args->attr_filter & XFS_ATTR_ROOT);
}
......
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