Commit 0313dd8f authored by Chandan Babu R's avatar Chandan Babu R

Merge tag 'repair-symlink-6.10_2024-04-15' of...

Merge tag 'repair-symlink-6.10_2024-04-15' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-6.10-mergeA

xfs: online repair of symbolic links

The patches in this set adds the ability to repair the target buffer of
a symbolic link, using the same salvage, rebuild, and swap strategy used
everywhere else.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Signed-off-by: default avatarChandan Babu R <chandanbabu@kernel.org>

* tag 'repair-symlink-6.10_2024-04-15' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux:
  xfs: online repair of symbolic links
  xfs: pass the owner to xfs_symlink_write_target
  xfs: expose xfs_bmap_local_to_extents for online repair
parents 067d3f71 2651923d
...@@ -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 \
) )
......
...@@ -779,7 +779,7 @@ xfs_bmap_local_to_extents_empty( ...@@ -779,7 +779,7 @@ xfs_bmap_local_to_extents_empty(
} }
STATIC int /* error */ int /* error */
xfs_bmap_local_to_extents( xfs_bmap_local_to_extents(
xfs_trans_t *tp, /* transaction pointer */ xfs_trans_t *tp, /* transaction pointer */
xfs_inode_t *ip, /* incore inode pointer */ xfs_inode_t *ip, /* incore inode pointer */
...@@ -789,7 +789,8 @@ xfs_bmap_local_to_extents( ...@@ -789,7 +789,8 @@ xfs_bmap_local_to_extents(
void (*init_fn)(struct xfs_trans *tp, void (*init_fn)(struct xfs_trans *tp,
struct xfs_buf *bp, struct xfs_buf *bp,
struct xfs_inode *ip, struct xfs_inode *ip,
struct xfs_ifork *ifp)) struct xfs_ifork *ifp, void *priv),
void *priv)
{ {
int error = 0; int error = 0;
int flags; /* logging flags returned */ int flags; /* logging flags returned */
...@@ -850,7 +851,7 @@ xfs_bmap_local_to_extents( ...@@ -850,7 +851,7 @@ xfs_bmap_local_to_extents(
* log here. Note that init_fn must also set the buffer log item type * log here. Note that init_fn must also set the buffer log item type
* correctly. * correctly.
*/ */
init_fn(tp, bp, ip, ifp); init_fn(tp, bp, ip, ifp, priv);
/* account for the change in fork size */ /* account for the change in fork size */
xfs_idata_realloc(ip, -ifp->if_bytes, whichfork); xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
...@@ -982,8 +983,8 @@ xfs_bmap_add_attrfork_local( ...@@ -982,8 +983,8 @@ xfs_bmap_add_attrfork_local(
if (S_ISLNK(VFS_I(ip)->i_mode)) if (S_ISLNK(VFS_I(ip)->i_mode))
return xfs_bmap_local_to_extents(tp, ip, 1, flags, return xfs_bmap_local_to_extents(tp, ip, 1, flags,
XFS_DATA_FORK, XFS_DATA_FORK, xfs_symlink_local_to_remote,
xfs_symlink_local_to_remote); NULL);
/* should only be called for types that support local format data */ /* should only be called for types that support local format data */
ASSERT(0); ASSERT(0);
......
...@@ -179,6 +179,12 @@ unsigned int xfs_bmap_compute_attr_offset(struct xfs_mount *mp); ...@@ -179,6 +179,12 @@ 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_inode *ip, int size, int rsvd);
void xfs_bmap_local_to_extents_empty(struct xfs_trans *tp, void xfs_bmap_local_to_extents_empty(struct xfs_trans *tp,
struct xfs_inode *ip, int whichfork); struct xfs_inode *ip, int whichfork);
int xfs_bmap_local_to_extents(struct xfs_trans *tp, struct xfs_inode *ip,
xfs_extlen_t total, int *logflagsp, int whichfork,
void (*init_fn)(struct xfs_trans *tp, struct xfs_buf *bp,
struct xfs_inode *ip, struct xfs_ifork *ifp,
void *priv),
void *priv);
void xfs_bmap_compute_maxlevels(struct xfs_mount *mp, int whichfork); void xfs_bmap_compute_maxlevels(struct xfs_mount *mp, int whichfork);
int xfs_bmap_first_unused(struct xfs_trans *tp, struct xfs_inode *ip, int xfs_bmap_first_unused(struct xfs_trans *tp, struct xfs_inode *ip,
xfs_extlen_t len, xfs_fileoff_t *unused, int whichfork); xfs_extlen_t len, xfs_fileoff_t *unused, int whichfork);
......
...@@ -169,7 +169,8 @@ xfs_symlink_local_to_remote( ...@@ -169,7 +169,8 @@ xfs_symlink_local_to_remote(
struct xfs_trans *tp, struct xfs_trans *tp,
struct xfs_buf *bp, struct xfs_buf *bp,
struct xfs_inode *ip, struct xfs_inode *ip,
struct xfs_ifork *ifp) struct xfs_ifork *ifp,
void *priv)
{ {
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
char *buf; char *buf;
...@@ -310,6 +311,7 @@ int ...@@ -310,6 +311,7 @@ int
xfs_symlink_write_target( xfs_symlink_write_target(
struct xfs_trans *tp, struct xfs_trans *tp,
struct xfs_inode *ip, struct xfs_inode *ip,
xfs_ino_t owner,
const char *target_path, const char *target_path,
int pathlen, int pathlen,
xfs_fsblock_t fs_blocks, xfs_fsblock_t fs_blocks,
...@@ -364,8 +366,7 @@ xfs_symlink_write_target( ...@@ -364,8 +366,7 @@ xfs_symlink_write_target(
byte_cnt = min(byte_cnt, pathlen); byte_cnt = min(byte_cnt, pathlen);
buf = bp->b_addr; buf = bp->b_addr;
buf += xfs_symlink_hdr_set(mp, ip->i_ino, offset, byte_cnt, buf += xfs_symlink_hdr_set(mp, owner, offset, byte_cnt, bp);
bp);
memcpy(buf, cur_chunk, byte_cnt); memcpy(buf, cur_chunk, byte_cnt);
......
...@@ -16,12 +16,13 @@ int xfs_symlink_hdr_set(struct xfs_mount *mp, xfs_ino_t ino, uint32_t offset, ...@@ -16,12 +16,13 @@ int xfs_symlink_hdr_set(struct xfs_mount *mp, xfs_ino_t ino, uint32_t offset,
bool xfs_symlink_hdr_ok(xfs_ino_t ino, uint32_t offset, bool xfs_symlink_hdr_ok(xfs_ino_t ino, uint32_t offset,
uint32_t size, struct xfs_buf *bp); uint32_t size, struct xfs_buf *bp);
void xfs_symlink_local_to_remote(struct xfs_trans *tp, struct xfs_buf *bp, void xfs_symlink_local_to_remote(struct xfs_trans *tp, struct xfs_buf *bp,
struct xfs_inode *ip, struct xfs_ifork *ifp); struct xfs_inode *ip, struct xfs_ifork *ifp,
void *priv);
xfs_failaddr_t xfs_symlink_shortform_verify(void *sfp, int64_t size); xfs_failaddr_t xfs_symlink_shortform_verify(void *sfp, int64_t size);
int xfs_symlink_remote_read(struct xfs_inode *ip, char *link); int xfs_symlink_remote_read(struct xfs_inode *ip, char *link);
int xfs_symlink_write_target(struct xfs_trans *tp, struct xfs_inode *ip, int xfs_symlink_write_target(struct xfs_trans *tp, struct xfs_inode *ip,
const char *target_path, int pathlen, xfs_fsblock_t fs_blocks, xfs_ino_t owner, const char *target_path, int pathlen,
uint resblks); xfs_fsblock_t fs_blocks, uint resblks);
int xfs_symlink_remote_truncate(struct xfs_trans *tp, struct xfs_inode *ip); int xfs_symlink_remote_truncate(struct xfs_trans *tp, struct xfs_inode *ip);
#endif /* __XFS_SYMLINK_REMOTE_H */ #endif /* __XFS_SYMLINK_REMOTE_H */
...@@ -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 */
......
...@@ -181,8 +181,8 @@ xfs_symlink( ...@@ -181,8 +181,8 @@ xfs_symlink(
xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp); xfs_qm_vop_create_dqattach(tp, ip, udqp, gdqp, pdqp);
resblks -= XFS_IALLOC_SPACE_RES(mp); resblks -= XFS_IALLOC_SPACE_RES(mp);
error = xfs_symlink_write_target(tp, ip, target_path, pathlen, error = xfs_symlink_write_target(tp, ip, ip->i_ino, target_path,
fs_blocks, resblks); pathlen, fs_blocks, resblks);
if (error) if (error)
goto out_trans_cancel; goto out_trans_cancel;
resblks -= fs_blocks; resblks -= fs_blocks;
......
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