brintos

brintos / linux-shallow public Read only

0
0
Text · 13.6 KiB · d015a86 Raw
510 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) 2018-2024 Oracle.  All Rights Reserved.4 * Author: Darrick J. Wong <djwong@kernel.org>5 */6#include "xfs.h"7#include "xfs_fs.h"8#include "xfs_shared.h"9#include "xfs_format.h"10#include "xfs_trans_resv.h"11#include "xfs_mount.h"12#include "xfs_defer.h"13#include "xfs_btree.h"14#include "xfs_bit.h"15#include "xfs_log_format.h"16#include "xfs_trans.h"17#include "xfs_sb.h"18#include "xfs_inode.h"19#include "xfs_inode_fork.h"20#include "xfs_symlink.h"21#include "xfs_bmap.h"22#include "xfs_quota.h"23#include "xfs_da_format.h"24#include "xfs_da_btree.h"25#include "xfs_bmap_btree.h"26#include "xfs_trans_space.h"27#include "xfs_symlink_remote.h"28#include "xfs_exchmaps.h"29#include "xfs_exchrange.h"30#include "xfs_health.h"31#include "scrub/xfs_scrub.h"32#include "scrub/scrub.h"33#include "scrub/common.h"34#include "scrub/trace.h"35#include "scrub/repair.h"36#include "scrub/tempfile.h"37#include "scrub/tempexch.h"38#include "scrub/reap.h"39 40/*41 * Symbolic Link Repair42 * ====================43 *44 * We repair symbolic links by reading whatever target data we can find, up to45 * the first NULL byte.  If the recovered target strlen matches i_size, then46 * we rewrite the target.  In all other cases, we replace the target with an47 * overly long string that cannot possibly resolve.  The new target is written48 * into a private hidden temporary file, and then a file contents exchange49 * commits the new symlink target to the file being repaired.50 */51 52/* Set us up to repair the symlink file. */53int54xrep_setup_symlink(55	struct xfs_scrub	*sc,56	unsigned int		*resblks)57{58	struct xfs_mount	*mp = sc->mp;59	unsigned long long	blocks;60	int			error;61 62	error = xrep_tempfile_create(sc, S_IFLNK);63	if (error)64		return error;65 66	/*67	 * If we're doing a repair, we reserve enough blocks to write out a68	 * completely new symlink file, plus twice as many blocks as we would69	 * need if we can only allocate one block per data fork mapping.  This70	 * should cover the preallocation of the temporary file and exchanging71	 * the extent mappings.72	 *73	 * We cannot use xfs_exchmaps_estimate because we have not yet74	 * constructed the replacement symlink and therefore do not know how75	 * many extents it will use.  By the time we do, we will have a dirty76	 * transaction (which we cannot drop because we cannot drop the77	 * symlink ILOCK) and cannot ask for more reservation.78	 */79	blocks = xfs_symlink_blocks(sc->mp, XFS_SYMLINK_MAXLEN);80	blocks += xfs_bmbt_calc_size(mp, blocks) * 2;81	if (blocks > UINT_MAX)82		return -EOPNOTSUPP;83 84	*resblks += blocks;85	return 0;86}87 88/*89 * Try to salvage the pathname from remote blocks.  Returns the number of bytes90 * salvaged or a negative errno.91 */92STATIC ssize_t93xrep_symlink_salvage_remote(94	struct xfs_scrub	*sc)95{96	struct xfs_bmbt_irec	mval[XFS_SYMLINK_MAPS];97	struct xfs_inode	*ip = sc->ip;98	struct xfs_buf		*bp;99	char			*target_buf = sc->buf;100	xfs_failaddr_t		fa;101	xfs_filblks_t		fsblocks;102	xfs_daddr_t		d;103	loff_t			len;104	loff_t			offset = 0;105	unsigned int		byte_cnt;106	bool			magic_ok;107	bool			hdr_ok;108	int			n;109	int			nmaps = XFS_SYMLINK_MAPS;110	int			error;111 112	/* We'll only read until the buffer is full. */113	len = min_t(loff_t, ip->i_disk_size, XFS_SYMLINK_MAXLEN);114	fsblocks = xfs_symlink_blocks(sc->mp, len);115	error = xfs_bmapi_read(ip, 0, fsblocks, mval, &nmaps, 0);116	if (error)117		return error;118 119	for (n = 0; n < nmaps; n++) {120		struct xfs_dsymlink_hdr	*dsl;121 122		d = XFS_FSB_TO_DADDR(sc->mp, mval[n].br_startblock);123 124		/* Read the rmt block.  We'll run the verifiers manually. */125		error = xfs_trans_read_buf(sc->mp, sc->tp, sc->mp->m_ddev_targp,126				d, XFS_FSB_TO_BB(sc->mp, mval[n].br_blockcount),127				0, &bp, NULL);128		if (error)129			return error;130		bp->b_ops = &xfs_symlink_buf_ops;131 132		/* How many bytes do we expect to get out of this buffer? */133		byte_cnt = XFS_FSB_TO_B(sc->mp, mval[n].br_blockcount);134		byte_cnt = XFS_SYMLINK_BUF_SPACE(sc->mp, byte_cnt);135		byte_cnt = min_t(unsigned int, byte_cnt, len);136 137		/*138		 * See if the verifiers accept this block.  We're willing to139		 * salvage if the if the offset/byte/ino are ok and either the140		 * verifier passed or the magic is ok.  Anything else and we141		 * stop dead in our tracks.142		 */143		fa = bp->b_ops->verify_struct(bp);144		dsl = bp->b_addr;145		magic_ok = dsl->sl_magic == cpu_to_be32(XFS_SYMLINK_MAGIC);146		hdr_ok = xfs_symlink_hdr_ok(ip->i_ino, offset, byte_cnt, bp);147		if (!hdr_ok || (fa != NULL && !magic_ok))148			break;149 150		memcpy(target_buf + offset, dsl + 1, byte_cnt);151 152		len -= byte_cnt;153		offset += byte_cnt;154	}155	return offset;156}157 158/*159 * Try to salvage an inline symlink's contents.  Returns the number of bytes160 * salvaged or a negative errno.161 */162STATIC ssize_t163xrep_symlink_salvage_inline(164	struct xfs_scrub	*sc)165{166	struct xfs_inode	*ip = sc->ip;167	char			*target_buf = sc->buf;168	char			*old_target;169	struct xfs_ifork	*ifp;170	unsigned int		nr;171 172	ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK);173	if (!ifp->if_data)174		return 0;175 176	/*177	 * If inode repair zapped the link target, pretend that we didn't find178	 * any bytes at all so that we can replace the (now totally lost) link179	 * target with a warning message.180	 */181	old_target = ifp->if_data;182	if (xfs_inode_has_sickness(sc->ip, XFS_SICK_INO_SYMLINK_ZAPPED) &&183	    sc->ip->i_disk_size == 1 && old_target[0] == '?')184		return 0;185 186	nr = min(XFS_SYMLINK_MAXLEN, xfs_inode_data_fork_size(ip));187	strncpy(target_buf, ifp->if_data, nr);188	return nr;189}190 191#define DUMMY_TARGET \192	"The target of this symbolic link could not be recovered at all and " \193	"has been replaced with this explanatory message.  To avoid " \194	"accidentally pointing to an existing file path, this message is " \195	"longer than the maximum supported file name length.  That is an " \196	"acceptable length for a symlink target on XFS but will produce " \197	"File Name Too Long errors if resolved."198 199/* Salvage whatever we can of the target. */200STATIC int201xrep_symlink_salvage(202	struct xfs_scrub	*sc)203{204	char			*target_buf = sc->buf;205	ssize_t			buflen = 0;206 207	BUILD_BUG_ON(sizeof(DUMMY_TARGET) - 1 <= NAME_MAX);208 209	/*210	 * Salvage the target if there weren't any corruption problems observed211	 * while scanning it.212	 */213	if (!(sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {214		if (sc->ip->i_df.if_format == XFS_DINODE_FMT_LOCAL)215			buflen = xrep_symlink_salvage_inline(sc);216		else217			buflen = xrep_symlink_salvage_remote(sc);218		if (buflen < 0)219			return buflen;220 221		/*222		 * NULL-terminate the buffer because the ondisk target does not223		 * do that for us.  If salvage didn't find the exact amount of224		 * data that we expected to find, don't salvage anything.225		 */226		target_buf[buflen] = 0;227		if (strlen(target_buf) != sc->ip->i_disk_size)228			buflen = 0;229	}230 231	/*232	 * Change an empty target into a dummy target and clear the symlink233	 * target zapped flag.234	 */235	if (buflen == 0) {236		sc->sick_mask |= XFS_SICK_INO_SYMLINK_ZAPPED;237		sprintf(target_buf, DUMMY_TARGET);238	}239 240	trace_xrep_symlink_salvage_target(sc->ip, target_buf,241			strlen(target_buf));242	return 0;243}244 245STATIC void246xrep_symlink_local_to_remote(247	struct xfs_trans	*tp,248	struct xfs_buf		*bp,249	struct xfs_inode	*ip,250	struct xfs_ifork	*ifp,251	void			*priv)252{253	struct xfs_scrub	*sc = priv;254	struct xfs_dsymlink_hdr	*dsl = bp->b_addr;255 256	xfs_symlink_local_to_remote(tp, bp, ip, ifp, NULL);257 258	if (!xfs_has_crc(sc->mp))259		return;260 261	dsl->sl_owner = cpu_to_be64(sc->ip->i_ino);262	xfs_trans_log_buf(tp, bp, 0,263			  sizeof(struct xfs_dsymlink_hdr) + ifp->if_bytes - 1);264}265 266/*267 * Prepare both links' data forks for an exchange.  Promote the tempfile from268 * local format to extents format, and if the file being repaired has a short269 * format data fork, turn it into an empty extent list.270 */271STATIC int272xrep_symlink_swap_prep(273	struct xfs_scrub	*sc,274	bool			temp_local,275	bool			ip_local)276{277	int			error;278 279	/*280	 * If the temp link is in shortform format, convert that to a remote281	 * target so that we can use the atomic mapping exchange.282	 */283	if (temp_local) {284		int		logflags = XFS_ILOG_CORE;285 286		error = xfs_bmap_local_to_extents(sc->tp, sc->tempip, 1,287				&logflags, XFS_DATA_FORK,288				xrep_symlink_local_to_remote,289				sc);290		if (error)291			return error;292 293		xfs_trans_log_inode(sc->tp, sc->ip, 0);294 295		error = xfs_defer_finish(&sc->tp);296		if (error)297			return error;298	}299 300	/*301	 * If the file being repaired had a shortform data fork, convert that302	 * to an empty extent list in preparation for the atomic mapping303	 * exchange.304	 */305	if (ip_local) {306		struct xfs_ifork	*ifp;307 308		ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK);309		xfs_idestroy_fork(ifp);310		ifp->if_format = XFS_DINODE_FMT_EXTENTS;311		ifp->if_nextents = 0;312		ifp->if_bytes = 0;313		ifp->if_data = NULL;314		ifp->if_height = 0;315 316		xfs_trans_log_inode(sc->tp, sc->ip,317				XFS_ILOG_CORE | XFS_ILOG_DDATA);318	}319 320	return 0;321}322 323/* Exchange the temporary symlink's data fork with the one being repaired. */324STATIC int325xrep_symlink_swap(326	struct xfs_scrub	*sc)327{328	struct xrep_tempexch	*tx = sc->buf;329	bool			ip_local, temp_local;330	int			error;331 332	ip_local = sc->ip->i_df.if_format == XFS_DINODE_FMT_LOCAL;333	temp_local = sc->tempip->i_df.if_format == XFS_DINODE_FMT_LOCAL;334 335	/*336	 * If the both links have a local format data fork and the rebuilt337	 * remote data would fit in the repaired file's data fork, copy the338	 * contents from the tempfile and declare ourselves done.339	 */340	if (ip_local && temp_local &&341	    sc->tempip->i_disk_size <= xfs_inode_data_fork_size(sc->ip)) {342		xrep_tempfile_copyout_local(sc, XFS_DATA_FORK);343		return 0;344	}345 346	/* Otherwise, make sure both data forks are in block-mapping mode. */347	error = xrep_symlink_swap_prep(sc, temp_local, ip_local);348	if (error)349		return error;350 351	return xrep_tempexch_contents(sc, tx);352}353 354/*355 * Free all the remote blocks and reset the data fork.  The caller must join356 * the inode to the transaction.  This function returns with the inode joined357 * to a clean scrub transaction.358 */359STATIC int360xrep_symlink_reset_fork(361	struct xfs_scrub	*sc)362{363	struct xfs_ifork	*ifp = xfs_ifork_ptr(sc->tempip, XFS_DATA_FORK);364	int			error;365 366	/* Unmap all the remote target buffers. */367	if (xfs_ifork_has_extents(ifp)) {368		error = xrep_reap_ifork(sc, sc->tempip, XFS_DATA_FORK);369		if (error)370			return error;371	}372 373	trace_xrep_symlink_reset_fork(sc->tempip);374 375	/* Reset the temp symlink target to dummy content. */376	xfs_idestroy_fork(ifp);377	return xfs_symlink_write_target(sc->tp, sc->tempip, sc->tempip->i_ino,378			"?", 1, 0, 0);379}380 381/*382 * Reinitialize a link target.  Caller must ensure the inode is joined to383 * the transaction.384 */385STATIC int386xrep_symlink_rebuild(387	struct xfs_scrub	*sc)388{389	struct xrep_tempexch	*tx;390	char			*target_buf = sc->buf;391	xfs_fsblock_t		fs_blocks;392	unsigned int		target_len;393	unsigned int		resblks;394	int			error;395 396	/* How many blocks do we need? */397	target_len = strlen(target_buf);398	ASSERT(target_len != 0);399	if (target_len == 0 || target_len > XFS_SYMLINK_MAXLEN)400		return -EFSCORRUPTED;401 402	trace_xrep_symlink_rebuild(sc->ip);403 404	/*405	 * In preparation to write the new symlink target to the temporary406	 * file, drop the ILOCK of the file being repaired (it shouldn't be407	 * joined) and take the ILOCK of the temporary file.408	 *409	 * The VFS does not take the IOLOCK while reading a symlink (and new410	 * symlinks are hidden with INEW until they've been written) so it's411	 * possible that a readlink() could see the old corrupted contents412	 * while we're doing this.413	 */414	xchk_iunlock(sc, XFS_ILOCK_EXCL);415	xrep_tempfile_ilock(sc);416	xfs_trans_ijoin(sc->tp, sc->tempip, 0);417 418	/*419	 * Reserve resources to reinitialize the target.  We're allowed to420	 * exceed file quota to repair inconsistent metadata, though this is421	 * unlikely.422	 */423	fs_blocks = xfs_symlink_blocks(sc->mp, target_len);424	resblks = xfs_symlink_space_res(sc->mp, target_len, fs_blocks);425	error = xfs_trans_reserve_quota_nblks(sc->tp, sc->tempip, resblks, 0,426			true);427	if (error)428		return error;429 430	/* Erase the dummy target set up by the tempfile initialization. */431	xfs_idestroy_fork(&sc->tempip->i_df);432	sc->tempip->i_df.if_bytes = 0;433	sc->tempip->i_df.if_format = XFS_DINODE_FMT_EXTENTS;434 435	/* Write the salvaged target to the temporary link. */436	error = xfs_symlink_write_target(sc->tp, sc->tempip, sc->ip->i_ino,437			target_buf, target_len, fs_blocks, resblks);438	if (error)439		return error;440 441	/*442	 * Commit the repair transaction so that we can use the atomic mapping443	 * exchange functions to compute the correct block reservations and444	 * re-lock the inodes.445	 */446	target_buf = NULL;447	error = xrep_trans_commit(sc);448	if (error)449		return error;450 451	/* Last chance to abort before we start committing fixes. */452	if (xchk_should_terminate(sc, &error))453		return error;454 455	xrep_tempfile_iunlock(sc);456 457	/*458	 * We're done with the temporary buffer, so we can reuse it for the459	 * tempfile contents exchange information.460	 */461	tx = sc->buf;462	error = xrep_tempexch_trans_alloc(sc, XFS_DATA_FORK, tx);463	if (error)464		return error;465 466	/*467	 * Exchange the temp link's data fork with the file being repaired.468	 * This recreates the transaction and takes the ILOCKs of the file469	 * being repaired and the temporary file.470	 */471	error = xrep_symlink_swap(sc);472	if (error)473		return error;474 475	/*476	 * Release the old symlink blocks and reset the data fork of the temp477	 * link to an empty shortform link.  This is the last repair action we478	 * perform on the symlink, so we don't need to clean the transaction.479	 */480	return xrep_symlink_reset_fork(sc);481}482 483/* Repair a symbolic link. */484int485xrep_symlink(486	struct xfs_scrub	*sc)487{488	int			error;489 490	/* The rmapbt is required to reap the old data fork. */491	if (!xfs_has_rmapbt(sc->mp))492		return -EOPNOTSUPP;493	/* We require atomic file exchange range to rebuild anything. */494	if (!xfs_has_exchange_range(sc->mp))495		return -EOPNOTSUPP;496 497	ASSERT(sc->ilock_flags & XFS_ILOCK_EXCL);498 499	error = xrep_symlink_salvage(sc);500	if (error)501		return error;502 503	/* Now reset the target. */504	error = xrep_symlink_rebuild(sc);505	if (error)506		return error;507 508	return xrep_trans_commit(sc);509}510