Skip to content

Commit 1da824b

Browse files
author
Chandan Babu R
committed
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: Darrick J. Wong <[email protected]> Signed-off-by: Chandan Babu R <[email protected]> * 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
2 parents 0d2dd38 + 327ed70 commit 1da824b

25 files changed

+2764
-145
lines changed

fs/xfs/libxfs/xfs_attr.c

Lines changed: 52 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -948,40 +948,67 @@ xfs_attr_lookup(
948948
return error;
949949
}
950950

951+
int
952+
xfs_attr_add_fork(
953+
struct xfs_inode *ip, /* incore inode pointer */
954+
int size, /* space new attribute needs */
955+
int rsvd) /* xact may use reserved blks */
956+
{
957+
struct xfs_mount *mp = ip->i_mount;
958+
struct xfs_trans *tp; /* transaction pointer */
959+
unsigned int blks; /* space reservation */
960+
int error; /* error return value */
961+
962+
ASSERT(!XFS_NOT_DQATTACHED(mp, ip));
963+
964+
blks = XFS_ADDAFORK_SPACE_RES(mp);
965+
966+
error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_addafork, blks, 0,
967+
rsvd, &tp);
968+
if (error)
969+
return error;
970+
971+
if (xfs_inode_has_attr_fork(ip))
972+
goto trans_cancel;
973+
974+
error = xfs_bmap_add_attrfork(tp, ip, size, rsvd);
975+
if (error)
976+
goto trans_cancel;
977+
978+
error = xfs_trans_commit(tp);
979+
xfs_iunlock(ip, XFS_ILOCK_EXCL);
980+
return error;
981+
982+
trans_cancel:
983+
xfs_trans_cancel(tp);
984+
xfs_iunlock(ip, XFS_ILOCK_EXCL);
985+
return error;
986+
}
987+
988+
/*
989+
* Make a change to the xattr structure.
990+
*
991+
* The caller must have initialized @args, attached dquots, and must not hold
992+
* any ILOCKs. Reserved data blocks may be used if @rsvd is set.
993+
*
994+
* Returns -EEXIST for XFS_ATTRUPDATE_CREATE if the name already exists.
995+
* Returns -ENOATTR for XFS_ATTRUPDATE_REMOVE if the name does not exist.
996+
* Returns 0 on success, or a negative errno if something else went wrong.
997+
*/
951998
int
952999
xfs_attr_set(
9531000
struct xfs_da_args *args,
954-
enum xfs_attr_update op)
1001+
enum xfs_attr_update op,
1002+
bool rsvd)
9551003
{
9561004
struct xfs_inode *dp = args->dp;
9571005
struct xfs_mount *mp = dp->i_mount;
9581006
struct xfs_trans_res tres;
959-
bool rsvd = (args->attr_filter & XFS_ATTR_ROOT);
9601007
int error, local;
9611008
int rmt_blks = 0;
9621009
unsigned int total;
9631010

964-
if (xfs_is_shutdown(dp->i_mount))
965-
return -EIO;
966-
967-
error = xfs_qm_dqattach(dp);
968-
if (error)
969-
return error;
970-
971-
if (!args->owner)
972-
args->owner = args->dp->i_ino;
973-
args->geo = mp->m_attr_geo;
974-
args->whichfork = XFS_ATTR_FORK;
975-
xfs_attr_sethash(args);
976-
977-
/*
978-
* We have no control over the attribute names that userspace passes us
979-
* to remove, so we have to allow the name lookup prior to attribute
980-
* removal to fail as well. Preserve the logged flag, since we need
981-
* to pass that through to the logging code.
982-
*/
983-
args->op_flags = XFS_DA_OP_OKNOENT |
984-
(args->op_flags & XFS_DA_OP_LOGGED);
1011+
ASSERT(!args->trans);
9851012

9861013
switch (op) {
9871014
case XFS_ATTRUPDATE_UPSERT:
@@ -999,7 +1026,7 @@ xfs_attr_set(
9991026
xfs_attr_sf_entsize_byname(args->namelen,
10001027
args->valuelen);
10011028

1002-
error = xfs_bmap_add_attrfork(dp, sf_size, rsvd);
1029+
error = xfs_attr_add_fork(dp, sf_size, rsvd);
10031030
if (error)
10041031
return error;
10051032
}
@@ -1076,6 +1103,7 @@ xfs_attr_set(
10761103
error = xfs_trans_commit(args->trans);
10771104
out_unlock:
10781105
xfs_iunlock(dp, XFS_ILOCK_EXCL);
1106+
args->trans = NULL;
10791107
return error;
10801108

10811109
out_trans_cancel:

fs/xfs/libxfs/xfs_attr.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ enum xfs_attr_update {
558558
XFS_ATTRUPDATE_REPLACE, /* set value, fail if attr does not exist */
559559
};
560560

561-
int xfs_attr_set(struct xfs_da_args *args, enum xfs_attr_update op);
561+
int xfs_attr_set(struct xfs_da_args *args, enum xfs_attr_update op, bool rsvd);
562562
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
563563
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
564564
bool xfs_attr_check_namespace(unsigned int attr_flags);
@@ -648,5 +648,6 @@ int __init xfs_attr_intent_init_cache(void);
648648
void xfs_attr_intent_destroy_cache(void);
649649

650650
int xfs_attr_sf_totsize(struct xfs_inode *dp);
651+
int xfs_attr_add_fork(struct xfs_inode *ip, int size, int rsvd);
651652

652653
#endif /* __XFS_ATTR_H__ */

fs/xfs/libxfs/xfs_bmap.c

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,40 +1025,29 @@ xfs_bmap_set_attrforkoff(
10251025
}
10261026

10271027
/*
1028-
* Convert inode from non-attributed to attributed.
1029-
* Must not be in a transaction, ip must not be locked.
1028+
* Convert inode from non-attributed to attributed. Caller must hold the
1029+
* ILOCK_EXCL and the file cannot have an attr fork.
10301030
*/
10311031
int /* error code */
10321032
xfs_bmap_add_attrfork(
1033-
xfs_inode_t *ip, /* incore inode pointer */
1033+
struct xfs_trans *tp,
1034+
struct xfs_inode *ip, /* incore inode pointer */
10341035
int size, /* space new attribute needs */
10351036
int rsvd) /* xact may use reserved blks */
10361037
{
1037-
xfs_mount_t *mp; /* mount structure */
1038-
xfs_trans_t *tp; /* transaction pointer */
1039-
int blks; /* space reservation */
1038+
struct xfs_mount *mp = tp->t_mountp;
10401039
int version = 1; /* superblock attr version */
10411040
int logflags; /* logging flags */
10421041
int error; /* error return value */
10431042

1044-
ASSERT(xfs_inode_has_attr_fork(ip) == 0);
1045-
1046-
mp = ip->i_mount;
1043+
xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
10471044
ASSERT(!XFS_NOT_DQATTACHED(mp, ip));
1048-
1049-
blks = XFS_ADDAFORK_SPACE_RES(mp);
1050-
1051-
error = xfs_trans_alloc_inode(ip, &M_RES(mp)->tr_addafork, blks, 0,
1052-
rsvd, &tp);
1053-
if (error)
1054-
return error;
1055-
if (xfs_inode_has_attr_fork(ip))
1056-
goto trans_cancel;
1045+
ASSERT(!xfs_inode_has_attr_fork(ip));
10571046

10581047
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
10591048
error = xfs_bmap_set_attrforkoff(ip, size, &version);
10601049
if (error)
1061-
goto trans_cancel;
1050+
return error;
10621051

10631052
xfs_ifork_init_attr(ip, XFS_DINODE_FMT_EXTENTS, 0);
10641053
logflags = 0;
@@ -1079,7 +1068,7 @@ xfs_bmap_add_attrfork(
10791068
if (logflags)
10801069
xfs_trans_log_inode(tp, ip, logflags);
10811070
if (error)
1082-
goto trans_cancel;
1071+
return error;
10831072
if (!xfs_has_attr(mp) ||
10841073
(!xfs_has_attr2(mp) && version == 2)) {
10851074
bool log_sb = false;
@@ -1098,14 +1087,7 @@ xfs_bmap_add_attrfork(
10981087
xfs_log_sb(tp);
10991088
}
11001089

1101-
error = xfs_trans_commit(tp);
1102-
xfs_iunlock(ip, XFS_ILOCK_EXCL);
1103-
return error;
1104-
1105-
trans_cancel:
1106-
xfs_trans_cancel(tp);
1107-
xfs_iunlock(ip, XFS_ILOCK_EXCL);
1108-
return error;
1090+
return 0;
11091091
}
11101092

11111093
/*

fs/xfs/libxfs/xfs_bmap.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ int xfs_bmap_longest_free_extent(struct xfs_perag *pag,
176176
void xfs_trim_extent(struct xfs_bmbt_irec *irec, xfs_fileoff_t bno,
177177
xfs_filblks_t len);
178178
unsigned int xfs_bmap_compute_attr_offset(struct xfs_mount *mp);
179-
int xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd);
179+
int xfs_bmap_add_attrfork(struct xfs_trans *tp, struct xfs_inode *ip,
180+
int size, int rsvd);
180181
void xfs_bmap_local_to_extents_empty(struct xfs_trans *tp,
181182
struct xfs_inode *ip, int whichfork);
182183
int xfs_bmap_local_to_extents(struct xfs_trans *tp, struct xfs_inode *ip,

fs/xfs/libxfs/xfs_dir2.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ int
434434
xfs_dir_removename(
435435
struct xfs_trans *tp,
436436
struct xfs_inode *dp,
437-
struct xfs_name *name,
437+
const struct xfs_name *name,
438438
xfs_ino_t ino,
439439
xfs_extlen_t total) /* bmap's total block count */
440440
{

fs/xfs/libxfs/xfs_dir2.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
5858
const struct xfs_name *name, xfs_ino_t *inum,
5959
struct xfs_name *ci_name);
6060
extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
61-
struct xfs_name *name, xfs_ino_t ino,
61+
const struct xfs_name *name, xfs_ino_t ino,
6262
xfs_extlen_t tot);
6363
extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
6464
const struct xfs_name *name, xfs_ino_t inum,

fs/xfs/libxfs/xfs_parent.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,67 @@ xfs_parent_lookup(
313313
xfs_parent_da_args_init(scratch, tp, pptr, ip, ip->i_ino, parent_name);
314314
return xfs_attr_get_ilocked(scratch);
315315
}
316+
317+
/* Sanity-check a parent pointer before we try to perform repairs. */
318+
static inline bool
319+
xfs_parent_sanity_check(
320+
struct xfs_mount *mp,
321+
const struct xfs_name *parent_name,
322+
const struct xfs_parent_rec *pptr)
323+
{
324+
if (!xfs_parent_namecheck(XFS_ATTR_PARENT, parent_name->name,
325+
parent_name->len))
326+
return false;
327+
328+
if (!xfs_parent_valuecheck(mp, pptr, sizeof(*pptr)))
329+
return false;
330+
331+
return true;
332+
}
333+
334+
335+
/*
336+
* Attach the parent pointer (@parent_name -> @pptr) to @ip immediately.
337+
* Caller must not have a transaction or hold the ILOCK. This is for
338+
* specialized repair functions only. The scratchpad need not be initialized.
339+
*/
340+
int
341+
xfs_parent_set(
342+
struct xfs_inode *ip,
343+
xfs_ino_t owner,
344+
const struct xfs_name *parent_name,
345+
struct xfs_parent_rec *pptr,
346+
struct xfs_da_args *scratch)
347+
{
348+
if (!xfs_parent_sanity_check(ip->i_mount, parent_name, pptr)) {
349+
ASSERT(0);
350+
return -EFSCORRUPTED;
351+
}
352+
353+
memset(scratch, 0, sizeof(struct xfs_da_args));
354+
xfs_parent_da_args_init(scratch, NULL, pptr, ip, owner, parent_name);
355+
return xfs_attr_set(scratch, XFS_ATTRUPDATE_CREATE, false);
356+
}
357+
358+
/*
359+
* Remove the parent pointer (@parent_name -> @pptr) from @ip immediately.
360+
* Caller must not have a transaction or hold the ILOCK. This is for
361+
* specialized repair functions only. The scratchpad need not be initialized.
362+
*/
363+
int
364+
xfs_parent_unset(
365+
struct xfs_inode *ip,
366+
xfs_ino_t owner,
367+
const struct xfs_name *parent_name,
368+
struct xfs_parent_rec *pptr,
369+
struct xfs_da_args *scratch)
370+
{
371+
if (!xfs_parent_sanity_check(ip->i_mount, parent_name, pptr)) {
372+
ASSERT(0);
373+
return -EFSCORRUPTED;
374+
}
375+
376+
memset(scratch, 0, sizeof(struct xfs_da_args));
377+
xfs_parent_da_args_init(scratch, NULL, pptr, ip, owner, parent_name);
378+
return xfs_attr_set(scratch, XFS_ATTRUPDATE_REMOVE, false);
379+
}

fs/xfs/libxfs/xfs_parent.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,11 @@ int xfs_parent_from_attr(struct xfs_mount *mp, unsigned int attr_flags,
100100
int xfs_parent_lookup(struct xfs_trans *tp, struct xfs_inode *ip,
101101
const struct xfs_name *name, struct xfs_parent_rec *pptr,
102102
struct xfs_da_args *scratch);
103+
int xfs_parent_set(struct xfs_inode *ip, xfs_ino_t owner,
104+
const struct xfs_name *name, struct xfs_parent_rec *pptr,
105+
struct xfs_da_args *scratch);
106+
int xfs_parent_unset(struct xfs_inode *ip, xfs_ino_t owner,
107+
const struct xfs_name *name, struct xfs_parent_rec *pptr,
108+
struct xfs_da_args *scratch);
103109

104110
#endif /* __XFS_PARENT_H__ */

fs/xfs/scrub/attr.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ xchk_xattr(
675675
* iteration, which doesn't really follow the usual buffer
676676
* locking order.
677677
*/
678-
error = xchk_xattr_walk(sc, sc->ip, xchk_xattr_actor, NULL);
678+
error = xchk_xattr_walk(sc, sc->ip, xchk_xattr_actor, NULL, NULL);
679679
if (!xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error))
680680
return error;
681681

0 commit comments

Comments
 (0)