Commit 2651923d authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: online repair of symbolic links

If a symbolic link target looks bad, try to sift through the rubble to
find as much of the target buffer that we can, and stage a new target
(short or remote format as needed) in a temporary file and use the
atomic extent swapping mechanism to commit the results.  In the worst
case, we replace the target with an overly long filename that cannot
possibly resolve.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent ea8214c3
...@@ -213,6 +213,7 @@ xfs-y += $(addprefix scrub/, \ ...@@ -213,6 +213,7 @@ xfs-y += $(addprefix scrub/, \
refcount_repair.o \ refcount_repair.o \
repair.o \ repair.o \
rmap_repair.o \ rmap_repair.o \
symlink_repair.o \
tempfile.o \ tempfile.o \
xfblob.o \ xfblob.o \
) )
......
...@@ -94,6 +94,7 @@ int xrep_setup_xattr(struct xfs_scrub *sc); ...@@ -94,6 +94,7 @@ int xrep_setup_xattr(struct xfs_scrub *sc);
int xrep_setup_directory(struct xfs_scrub *sc); int xrep_setup_directory(struct xfs_scrub *sc);
int xrep_setup_parent(struct xfs_scrub *sc); int xrep_setup_parent(struct xfs_scrub *sc);
int xrep_setup_nlinks(struct xfs_scrub *sc); int xrep_setup_nlinks(struct xfs_scrub *sc);
int xrep_setup_symlink(struct xfs_scrub *sc, unsigned int *resblks);
/* Repair setup functions */ /* Repair setup functions */
int xrep_setup_ag_allocbt(struct xfs_scrub *sc); int xrep_setup_ag_allocbt(struct xfs_scrub *sc);
...@@ -130,6 +131,7 @@ int xrep_fscounters(struct xfs_scrub *sc); ...@@ -130,6 +131,7 @@ int xrep_fscounters(struct xfs_scrub *sc);
int xrep_xattr(struct xfs_scrub *sc); int xrep_xattr(struct xfs_scrub *sc);
int xrep_directory(struct xfs_scrub *sc); int xrep_directory(struct xfs_scrub *sc);
int xrep_parent(struct xfs_scrub *sc); int xrep_parent(struct xfs_scrub *sc);
int xrep_symlink(struct xfs_scrub *sc);
#ifdef CONFIG_XFS_RT #ifdef CONFIG_XFS_RT
int xrep_rtbitmap(struct xfs_scrub *sc); int xrep_rtbitmap(struct xfs_scrub *sc);
...@@ -206,6 +208,11 @@ xrep_setup_nothing( ...@@ -206,6 +208,11 @@ xrep_setup_nothing(
#define xrep_setup_inode(sc, imap) ((void)0) #define xrep_setup_inode(sc, imap) ((void)0)
static inline int xrep_setup_symlink(struct xfs_scrub *sc, unsigned int *x)
{
return 0;
}
#define xrep_revalidate_allocbt (NULL) #define xrep_revalidate_allocbt (NULL)
#define xrep_revalidate_iallocbt (NULL) #define xrep_revalidate_iallocbt (NULL)
...@@ -231,6 +238,7 @@ xrep_setup_nothing( ...@@ -231,6 +238,7 @@ xrep_setup_nothing(
#define xrep_xattr xrep_notsupported #define xrep_xattr xrep_notsupported
#define xrep_directory xrep_notsupported #define xrep_directory xrep_notsupported
#define xrep_parent xrep_notsupported #define xrep_parent xrep_notsupported
#define xrep_symlink xrep_notsupported
#endif /* CONFIG_XFS_ONLINE_REPAIR */ #endif /* CONFIG_XFS_ONLINE_REPAIR */
......
...@@ -339,7 +339,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { ...@@ -339,7 +339,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
.type = ST_INODE, .type = ST_INODE,
.setup = xchk_setup_symlink, .setup = xchk_setup_symlink,
.scrub = xchk_symlink, .scrub = xchk_symlink,
.repair = xrep_notsupported, .repair = xrep_symlink,
}, },
[XFS_SCRUB_TYPE_PARENT] = { /* parent pointers */ [XFS_SCRUB_TYPE_PARENT] = { /* parent pointers */
.type = ST_INODE, .type = ST_INODE,
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "xfs_trans_resv.h" #include "xfs_trans_resv.h"
#include "xfs_mount.h" #include "xfs_mount.h"
#include "xfs_log_format.h" #include "xfs_log_format.h"
#include "xfs_trans.h"
#include "xfs_inode.h" #include "xfs_inode.h"
#include "xfs_symlink.h" #include "xfs_symlink.h"
#include "xfs_health.h" #include "xfs_health.h"
...@@ -17,18 +18,28 @@ ...@@ -17,18 +18,28 @@
#include "scrub/scrub.h" #include "scrub/scrub.h"
#include "scrub/common.h" #include "scrub/common.h"
#include "scrub/health.h" #include "scrub/health.h"
#include "scrub/repair.h"
/* Set us up to scrub a symbolic link. */ /* Set us up to scrub a symbolic link. */
int int
xchk_setup_symlink( xchk_setup_symlink(
struct xfs_scrub *sc) struct xfs_scrub *sc)
{ {
unsigned int resblks = 0;
int error;
/* Allocate the buffer without the inode lock held. */ /* Allocate the buffer without the inode lock held. */
sc->buf = kvzalloc(XFS_SYMLINK_MAXLEN + 1, XCHK_GFP_FLAGS); sc->buf = kvzalloc(XFS_SYMLINK_MAXLEN + 1, XCHK_GFP_FLAGS);
if (!sc->buf) if (!sc->buf)
return -ENOMEM; return -ENOMEM;
return xchk_setup_inode_contents(sc, 0); if (xchk_could_repair(sc)) {
error = xrep_setup_symlink(sc, &resblks);
if (error)
return error;
}
return xchk_setup_inode_contents(sc, resblks);
} }
/* Symbolic links. */ /* Symbolic links. */
......
This diff is collapsed.
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "xfs_exchrange.h" #include "xfs_exchrange.h"
#include "xfs_exchmaps.h" #include "xfs_exchmaps.h"
#include "xfs_defer.h" #include "xfs_defer.h"
#include "xfs_symlink_remote.h"
#include "scrub/scrub.h" #include "scrub/scrub.h"
#include "scrub/common.h" #include "scrub/common.h"
#include "scrub/repair.h" #include "scrub/repair.h"
...@@ -109,6 +110,18 @@ xrep_tempfile_create( ...@@ -109,6 +110,18 @@ xrep_tempfile_create(
error = xfs_dir_init(tp, sc->tempip, dp); error = xfs_dir_init(tp, sc->tempip, dp);
if (error) if (error)
goto out_trans_cancel; goto out_trans_cancel;
} else if (S_ISLNK(VFS_I(sc->tempip)->i_mode)) {
/*
* Initialize the temporary symlink with a meaningless target
* that won't trip the verifiers. Repair must rewrite the
* target with meaningful content before swapping with the file
* being repaired. A single-byte target will not write a
* remote target block, so the owner is irrelevant.
*/
error = xfs_symlink_write_target(tp, sc->tempip,
sc->tempip->i_ino, ".", 1, 0, 0);
if (error)
goto out_trans_cancel;
} }
/* /*
......
...@@ -2711,6 +2711,52 @@ DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_check_alias); ...@@ -2711,6 +2711,52 @@ DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_check_alias);
DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_check_dentry); DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_check_dentry);
DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_invalidate_child); DEFINE_REPAIR_DENTRY_EVENT(xrep_adoption_invalidate_child);
TRACE_EVENT(xrep_symlink_salvage_target,
TP_PROTO(struct xfs_inode *ip, char *target, unsigned int targetlen),
TP_ARGS(ip, target, targetlen),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(unsigned int, targetlen)
__dynamic_array(char, target, targetlen + 1)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
__entry->targetlen = targetlen;
memcpy(__get_str(target), target, targetlen);
__get_str(target)[targetlen] = 0;
),
TP_printk("dev %d:%d ip 0x%llx target '%.*s'",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->targetlen,
__get_str(target))
);
DECLARE_EVENT_CLASS(xrep_symlink_class,
TP_PROTO(struct xfs_inode *ip),
TP_ARGS(ip),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
),
TP_printk("dev %d:%d ip 0x%llx",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino)
);
#define DEFINE_XREP_SYMLINK_EVENT(name) \
DEFINE_EVENT(xrep_symlink_class, name, \
TP_PROTO(struct xfs_inode *ip), \
TP_ARGS(ip))
DEFINE_XREP_SYMLINK_EVENT(xrep_symlink_rebuild);
DEFINE_XREP_SYMLINK_EVENT(xrep_symlink_reset_fork);
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */ #endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
#endif /* _TRACE_XFS_SCRUB_TRACE_H */ #endif /* _TRACE_XFS_SCRUB_TRACE_H */
......
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