Skip to content

Commit 77ede5f

Browse files
author
Darrick J. Wong
committed
xfs: walk directory parent pointers to determine backref count
If the filesystem has parent pointers enabled, walk the parent pointers of subdirectories to determine the true backref count. In theory each subdir should have a single parent reachable via dotdot, but in the case of (corrupt) subdirs with multiple parents, we need to keep the link counts high enough that the directory loop detector will be able to correct the multiple parents problems. Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent 8ad3453 commit 77ede5f

File tree

6 files changed

+177
-1
lines changed

6 files changed

+177
-1
lines changed

fs/xfs/scrub/common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ static inline bool xchk_skip_xref(struct xfs_scrub_metadata *sm)
212212
}
213213

214214
bool xchk_dir_looks_zapped(struct xfs_inode *dp);
215+
bool xchk_pptr_looks_zapped(struct xfs_inode *ip);
215216

216217
#ifdef CONFIG_XFS_ONLINE_REPAIR
217218
/* Decide if a repair is required. */

fs/xfs/scrub/nlinks.c

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "xfs_dir2.h"
1919
#include "xfs_dir2_priv.h"
2020
#include "xfs_ag.h"
21+
#include "xfs_parent.h"
2122
#include "scrub/scrub.h"
2223
#include "scrub/common.h"
2324
#include "scrub/repair.h"
@@ -29,6 +30,7 @@
2930
#include "scrub/trace.h"
3031
#include "scrub/readdir.h"
3132
#include "scrub/tempfile.h"
33+
#include "scrub/listxattr.h"
3234

3335
/*
3436
* Live Inode Link Count Checking
@@ -272,12 +274,17 @@ xchk_nlinks_collect_dirent(
272274
* number of parents of the root directory.
273275
*
274276
* Otherwise, increment the number of backrefs pointing back to ino.
277+
*
278+
* If the filesystem has parent pointers, we walk the pptrs to
279+
* determine the backref count.
275280
*/
276281
if (dotdot) {
277282
if (dp == sc->mp->m_rootip)
278283
error = xchk_nlinks_update_incore(xnc, ino, 1, 0, 0);
279-
else
284+
else if (!xfs_has_parent(sc->mp))
280285
error = xchk_nlinks_update_incore(xnc, ino, 0, 1, 0);
286+
else
287+
error = 0;
281288
if (error)
282289
goto out_unlock;
283290
}
@@ -314,6 +321,61 @@ xchk_nlinks_collect_dirent(
314321
return error;
315322
}
316323

324+
/* Bump the backref count for the inode referenced by this parent pointer. */
325+
STATIC int
326+
xchk_nlinks_collect_pptr(
327+
struct xfs_scrub *sc,
328+
struct xfs_inode *ip,
329+
unsigned int attr_flags,
330+
const unsigned char *name,
331+
unsigned int namelen,
332+
const void *value,
333+
unsigned int valuelen,
334+
void *priv)
335+
{
336+
struct xfs_name xname = {
337+
.name = name,
338+
.len = namelen,
339+
};
340+
struct xchk_nlink_ctrs *xnc = priv;
341+
const struct xfs_parent_rec *pptr_rec = value;
342+
xfs_ino_t parent_ino;
343+
int error;
344+
345+
/* Update the shadow link counts if we haven't already failed. */
346+
347+
if (xchk_iscan_aborted(&xnc->collect_iscan)) {
348+
error = -ECANCELED;
349+
goto out_incomplete;
350+
}
351+
352+
if (!(attr_flags & XFS_ATTR_PARENT))
353+
return 0;
354+
355+
error = xfs_parent_from_attr(sc->mp, attr_flags, name, namelen, value,
356+
valuelen, &parent_ino, NULL);
357+
if (error)
358+
return error;
359+
360+
trace_xchk_nlinks_collect_pptr(sc->mp, ip, &xname, pptr_rec);
361+
362+
mutex_lock(&xnc->lock);
363+
364+
error = xchk_nlinks_update_incore(xnc, parent_ino, 0, 1, 0);
365+
if (error)
366+
goto out_unlock;
367+
368+
mutex_unlock(&xnc->lock);
369+
return 0;
370+
371+
out_unlock:
372+
mutex_unlock(&xnc->lock);
373+
xchk_iscan_abort(&xnc->collect_iscan);
374+
out_incomplete:
375+
xchk_set_incomplete(sc);
376+
return error;
377+
}
378+
317379
/* Walk a directory to bump the observed link counts of the children. */
318380
STATIC int
319381
xchk_nlinks_collect_dir(
@@ -360,6 +422,27 @@ xchk_nlinks_collect_dir(
360422
if (error)
361423
goto out_abort;
362424

425+
/* Walk the parent pointers to get real backref counts. */
426+
if (xfs_has_parent(sc->mp)) {
427+
/*
428+
* If the extended attributes look as though they has been
429+
* zapped by the inode record repair code, we cannot scan for
430+
* parent pointers.
431+
*/
432+
if (xchk_pptr_looks_zapped(dp)) {
433+
error = -EBUSY;
434+
goto out_unlock;
435+
}
436+
437+
error = xchk_xattr_walk(sc, dp, xchk_nlinks_collect_pptr, xnc);
438+
if (error == -ECANCELED) {
439+
error = 0;
440+
goto out_unlock;
441+
}
442+
if (error)
443+
goto out_abort;
444+
}
445+
363446
xchk_iscan_mark_visited(&xnc->collect_iscan, dp);
364447
goto out_unlock;
365448

fs/xfs/scrub/nlinks_repair.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include "xfs_ialloc.h"
1919
#include "xfs_sb.h"
2020
#include "xfs_ag.h"
21+
#include "xfs_dir2.h"
22+
#include "xfs_parent.h"
2123
#include "scrub/scrub.h"
2224
#include "scrub/common.h"
2325
#include "scrub/repair.h"

fs/xfs/scrub/parent.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,3 +873,64 @@ xchk_parent(
873873

874874
return error;
875875
}
876+
877+
/*
878+
* Decide if this file's extended attributes (and therefore its parent
879+
* pointers) have been zapped to satisfy the inode and ifork verifiers.
880+
* Checking and repairing should be postponed until the extended attribute
881+
* structure is fixed.
882+
*/
883+
bool
884+
xchk_pptr_looks_zapped(
885+
struct xfs_inode *ip)
886+
{
887+
struct xfs_mount *mp = ip->i_mount;
888+
struct inode *inode = VFS_I(ip);
889+
890+
ASSERT(xfs_has_parent(mp));
891+
892+
/*
893+
* Temporary files that cannot be linked into the directory tree do not
894+
* have attr forks because they cannot ever have parents.
895+
*/
896+
if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE))
897+
return false;
898+
899+
/*
900+
* Directory tree roots do not have parents, so the expected outcome
901+
* of a parent pointer scan is always the empty set. It's safe to scan
902+
* them even if the attr fork was zapped.
903+
*/
904+
if (ip == mp->m_rootip)
905+
return false;
906+
907+
/*
908+
* Metadata inodes are all rooted in the superblock and do not have
909+
* any parents. Hence the attr fork will not be initialized, but
910+
* there are no parent pointers that might have been zapped.
911+
*/
912+
if (xfs_is_metadata_inode(ip))
913+
return false;
914+
915+
/*
916+
* Linked and linkable non-rootdir files should always have an
917+
* attribute fork because that is where parent pointers are
918+
* stored. If the fork is absent, something is amiss.
919+
*/
920+
if (!xfs_inode_has_attr_fork(ip))
921+
return true;
922+
923+
/* Repair zapped this file's attr fork a short time ago */
924+
if (xfs_ifork_zapped(ip, XFS_ATTR_FORK))
925+
return true;
926+
927+
/*
928+
* If the dinode repair found a bad attr fork, it will reset the fork
929+
* to extents format with zero records and wait for the bmapbta
930+
* scrubber to reconstruct the block mappings. The extended attribute
931+
* structure always contain some content when parent pointers are
932+
* enabled, so this is a clear sign of a zapped attr fork.
933+
*/
934+
return ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS &&
935+
ip->i_af.if_nextents == 0;
936+
}

fs/xfs/scrub/trace.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "xfs_da_format.h"
2020
#include "xfs_dir2.h"
2121
#include "xfs_rmap.h"
22+
#include "xfs_parent.h"
2223
#include "scrub/scrub.h"
2324
#include "scrub/xfile.h"
2425
#include "scrub/xfarray.h"

fs/xfs/scrub/trace.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ struct xchk_iscan;
2626
struct xchk_nlink;
2727
struct xchk_fscounters;
2828
struct xfs_rmap_update_params;
29+
struct xfs_parent_rec;
2930

3031
/*
3132
* ftrace's __print_symbolic requires that all enum values be wrapped in the
@@ -1363,6 +1364,33 @@ TRACE_EVENT(xchk_nlinks_collect_dirent,
13631364
__get_str(name))
13641365
);
13651366

1367+
TRACE_EVENT(xchk_nlinks_collect_pptr,
1368+
TP_PROTO(struct xfs_mount *mp, struct xfs_inode *dp,
1369+
const struct xfs_name *name,
1370+
const struct xfs_parent_rec *pptr),
1371+
TP_ARGS(mp, dp, name, pptr),
1372+
TP_STRUCT__entry(
1373+
__field(dev_t, dev)
1374+
__field(xfs_ino_t, dir)
1375+
__field(xfs_ino_t, ino)
1376+
__field(unsigned int, namelen)
1377+
__dynamic_array(char, name, name->len)
1378+
),
1379+
TP_fast_assign(
1380+
__entry->dev = mp->m_super->s_dev;
1381+
__entry->dir = dp->i_ino;
1382+
__entry->ino = be64_to_cpu(pptr->p_ino);
1383+
__entry->namelen = name->len;
1384+
memcpy(__get_str(name), name->name, name->len);
1385+
),
1386+
TP_printk("dev %d:%d dir 0x%llx -> ino 0x%llx name '%.*s'",
1387+
MAJOR(__entry->dev), MINOR(__entry->dev),
1388+
__entry->dir,
1389+
__entry->ino,
1390+
__entry->namelen,
1391+
__get_str(name))
1392+
);
1393+
13661394
TRACE_EVENT(xchk_nlinks_collect_metafile,
13671395
TP_PROTO(struct xfs_mount *mp, xfs_ino_t ino),
13681396
TP_ARGS(mp, ino),

0 commit comments

Comments
 (0)