Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (c) 2000-2006 Silicon Graphics, Inc.
  4 * All Rights Reserved.
  5 */
  6#include "xfs.h"
  7#include "xfs_fs.h"
  8#include "xfs_shared.h"
  9#include "xfs_format.h"
 10#include "xfs_log_format.h"
 11#include "xfs_trans_resv.h"
 12#include "xfs_mount.h"
 13#include "xfs_inode.h"
 14#include "xfs_trans.h"
 15#include "xfs_inode_item.h"
 16#include "xfs_trace.h"
 17#include "xfs_trans_priv.h"
 18#include "xfs_buf_item.h"
 19#include "xfs_log.h"
 20#include "xfs_error.h"
 21#include "xfs_log_priv.h"
 22#include "xfs_log_recover.h"
 23#include "xfs_icache.h"
 24#include "xfs_bmap_btree.h"
 25
 26STATIC void
 27xlog_recover_inode_ra_pass2(
 28	struct xlog                     *log,
 29	struct xlog_recover_item        *item)
 30{
 31	if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) {
 32		struct xfs_inode_log_format	*ilfp = item->ri_buf[0].i_addr;
 33
 34		xlog_buf_readahead(log, ilfp->ilf_blkno, ilfp->ilf_len,
 35				   &xfs_inode_buf_ra_ops);
 36	} else {
 37		struct xfs_inode_log_format_32	*ilfp = item->ri_buf[0].i_addr;
 38
 39		xlog_buf_readahead(log, ilfp->ilf_blkno, ilfp->ilf_len,
 40				   &xfs_inode_buf_ra_ops);
 41	}
 42}
 43
 44/*
 45 * Inode fork owner changes
 46 *
 47 * If we have been told that we have to reparent the inode fork, it's because an
 48 * extent swap operation on a CRC enabled filesystem has been done and we are
 49 * replaying it. We need to walk the BMBT of the appropriate fork and change the
 50 * owners of it.
 51 *
 52 * The complexity here is that we don't have an inode context to work with, so
 53 * after we've replayed the inode we need to instantiate one.  This is where the
 54 * fun begins.
 55 *
 56 * We are in the middle of log recovery, so we can't run transactions. That
 57 * means we cannot use cache coherent inode instantiation via xfs_iget(), as
 58 * that will result in the corresponding iput() running the inode through
 59 * xfs_inactive(). If we've just replayed an inode core that changes the link
 60 * count to zero (i.e. it's been unlinked), then xfs_inactive() will run
 61 * transactions (bad!).
 62 *
 63 * So, to avoid this, we instantiate an inode directly from the inode core we've
 64 * just recovered. We have the buffer still locked, and all we really need to
 65 * instantiate is the inode core and the forks being modified. We can do this
 66 * manually, then run the inode btree owner change, and then tear down the
 67 * xfs_inode without having to run any transactions at all.
 68 *
 69 * Also, because we don't have a transaction context available here but need to
 70 * gather all the buffers we modify for writeback so we pass the buffer_list
 71 * instead for the operation to use.
 72 */
 73
 74STATIC int
 75xfs_recover_inode_owner_change(
 76	struct xfs_mount	*mp,
 77	struct xfs_dinode	*dip,
 78	struct xfs_inode_log_format *in_f,
 79	struct list_head	*buffer_list)
 80{
 81	struct xfs_inode	*ip;
 82	int			error;
 83
 84	ASSERT(in_f->ilf_fields & (XFS_ILOG_DOWNER|XFS_ILOG_AOWNER));
 85
 86	ip = xfs_inode_alloc(mp, in_f->ilf_ino);
 87	if (!ip)
 88		return -ENOMEM;
 89
 90	/* instantiate the inode */
 91	ASSERT(dip->di_version >= 3);
 92
 93	error = xfs_inode_from_disk(ip, dip);
 94	if (error)
 95		goto out_free_ip;
 96
 97	if (in_f->ilf_fields & XFS_ILOG_DOWNER) {
 98		ASSERT(in_f->ilf_fields & XFS_ILOG_DBROOT);
 99		error = xfs_bmbt_change_owner(NULL, ip, XFS_DATA_FORK,
100					      ip->i_ino, buffer_list);
101		if (error)
102			goto out_free_ip;
103	}
104
105	if (in_f->ilf_fields & XFS_ILOG_AOWNER) {
106		ASSERT(in_f->ilf_fields & XFS_ILOG_ABROOT);
107		error = xfs_bmbt_change_owner(NULL, ip, XFS_ATTR_FORK,
108					      ip->i_ino, buffer_list);
109		if (error)
110			goto out_free_ip;
111	}
112
113out_free_ip:
114	xfs_inode_free(ip);
115	return error;
116}
117
118STATIC int
119xlog_recover_inode_commit_pass2(
120	struct xlog			*log,
121	struct list_head		*buffer_list,
122	struct xlog_recover_item	*item,
123	xfs_lsn_t			current_lsn)
124{
125	struct xfs_inode_log_format	*in_f;
126	struct xfs_mount		*mp = log->l_mp;
127	struct xfs_buf			*bp;
128	struct xfs_dinode		*dip;
129	int				len;
130	char				*src;
131	char				*dest;
132	int				error;
133	int				attr_index;
134	uint				fields;
135	struct xfs_log_dinode		*ldip;
136	uint				isize;
137	int				need_free = 0;
138
139	if (item->ri_buf[0].i_len == sizeof(struct xfs_inode_log_format)) {
140		in_f = item->ri_buf[0].i_addr;
141	} else {
142		in_f = kmem_alloc(sizeof(struct xfs_inode_log_format), 0);
143		need_free = 1;
144		error = xfs_inode_item_format_convert(&item->ri_buf[0], in_f);
145		if (error)
146			goto error;
147	}
148
149	/*
150	 * Inode buffers can be freed, look out for it,
151	 * and do not replay the inode.
152	 */
153	if (xlog_is_buffer_cancelled(log, in_f->ilf_blkno, in_f->ilf_len)) {
154		error = 0;
155		trace_xfs_log_recover_inode_cancel(log, in_f);
156		goto error;
157	}
158	trace_xfs_log_recover_inode_recover(log, in_f);
159
160	error = xfs_buf_read(mp->m_ddev_targp, in_f->ilf_blkno, in_f->ilf_len,
161			0, &bp, &xfs_inode_buf_ops);
162	if (error)
163		goto error;
164	ASSERT(in_f->ilf_fields & XFS_ILOG_CORE);
165	dip = xfs_buf_offset(bp, in_f->ilf_boffset);
166
167	/*
168	 * Make sure the place we're flushing out to really looks
169	 * like an inode!
170	 */
171	if (XFS_IS_CORRUPT(mp, !xfs_verify_magic16(bp, dip->di_magic))) {
172		xfs_alert(mp,
173	"%s: Bad inode magic number, dip = "PTR_FMT", dino bp = "PTR_FMT", ino = %Ld",
174			__func__, dip, bp, in_f->ilf_ino);
175		error = -EFSCORRUPTED;
176		goto out_release;
177	}
178	ldip = item->ri_buf[1].i_addr;
179	if (XFS_IS_CORRUPT(mp, ldip->di_magic != XFS_DINODE_MAGIC)) {
180		xfs_alert(mp,
181			"%s: Bad inode log record, rec ptr "PTR_FMT", ino %Ld",
182			__func__, item, in_f->ilf_ino);
183		error = -EFSCORRUPTED;
184		goto out_release;
185	}
186
187	/*
188	 * If the inode has an LSN in it, recover the inode only if it's less
189	 * than the lsn of the transaction we are replaying. Note: we still
190	 * need to replay an owner change even though the inode is more recent
191	 * than the transaction as there is no guarantee that all the btree
192	 * blocks are more recent than this transaction, too.
193	 */
194	if (dip->di_version >= 3) {
195		xfs_lsn_t	lsn = be64_to_cpu(dip->di_lsn);
196
197		if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) {
198			trace_xfs_log_recover_inode_skip(log, in_f);
199			error = 0;
200			goto out_owner_change;
201		}
202	}
203
204	/*
205	 * di_flushiter is only valid for v1/2 inodes. All changes for v3 inodes
206	 * are transactional and if ordering is necessary we can determine that
207	 * more accurately by the LSN field in the V3 inode core. Don't trust
208	 * the inode versions we might be changing them here - use the
209	 * superblock flag to determine whether we need to look at di_flushiter
210	 * to skip replay when the on disk inode is newer than the log one
211	 */
212	if (!xfs_sb_version_has_v3inode(&mp->m_sb) &&
213	    ldip->di_flushiter < be16_to_cpu(dip->di_flushiter)) {
214		/*
215		 * Deal with the wrap case, DI_MAX_FLUSH is less
216		 * than smaller numbers
217		 */
218		if (be16_to_cpu(dip->di_flushiter) == DI_MAX_FLUSH &&
219		    ldip->di_flushiter < (DI_MAX_FLUSH >> 1)) {
220			/* do nothing */
221		} else {
222			trace_xfs_log_recover_inode_skip(log, in_f);
223			error = 0;
224			goto out_release;
225		}
226	}
227
228	/* Take the opportunity to reset the flush iteration count */
229	ldip->di_flushiter = 0;
230
231	if (unlikely(S_ISREG(ldip->di_mode))) {
232		if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) &&
233		    (ldip->di_format != XFS_DINODE_FMT_BTREE)) {
234			XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(3)",
235					 XFS_ERRLEVEL_LOW, mp, ldip,
236					 sizeof(*ldip));
237			xfs_alert(mp,
238		"%s: Bad regular inode log record, rec ptr "PTR_FMT", "
239		"ino ptr = "PTR_FMT", ino bp = "PTR_FMT", ino %Ld",
240				__func__, item, dip, bp, in_f->ilf_ino);
241			error = -EFSCORRUPTED;
242			goto out_release;
243		}
244	} else if (unlikely(S_ISDIR(ldip->di_mode))) {
245		if ((ldip->di_format != XFS_DINODE_FMT_EXTENTS) &&
246		    (ldip->di_format != XFS_DINODE_FMT_BTREE) &&
247		    (ldip->di_format != XFS_DINODE_FMT_LOCAL)) {
248			XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(4)",
249					     XFS_ERRLEVEL_LOW, mp, ldip,
250					     sizeof(*ldip));
251			xfs_alert(mp,
252		"%s: Bad dir inode log record, rec ptr "PTR_FMT", "
253		"ino ptr = "PTR_FMT", ino bp = "PTR_FMT", ino %Ld",
254				__func__, item, dip, bp, in_f->ilf_ino);
255			error = -EFSCORRUPTED;
256			goto out_release;
257		}
258	}
259	if (unlikely(ldip->di_nextents + ldip->di_anextents > ldip->di_nblocks)){
260		XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(5)",
261				     XFS_ERRLEVEL_LOW, mp, ldip,
262				     sizeof(*ldip));
263		xfs_alert(mp,
264	"%s: Bad inode log record, rec ptr "PTR_FMT", dino ptr "PTR_FMT", "
265	"dino bp "PTR_FMT", ino %Ld, total extents = %d, nblocks = %Ld",
266			__func__, item, dip, bp, in_f->ilf_ino,
267			ldip->di_nextents + ldip->di_anextents,
268			ldip->di_nblocks);
269		error = -EFSCORRUPTED;
270		goto out_release;
271	}
272	if (unlikely(ldip->di_forkoff > mp->m_sb.sb_inodesize)) {
273		XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(6)",
274				     XFS_ERRLEVEL_LOW, mp, ldip,
275				     sizeof(*ldip));
276		xfs_alert(mp,
277	"%s: Bad inode log record, rec ptr "PTR_FMT", dino ptr "PTR_FMT", "
278	"dino bp "PTR_FMT", ino %Ld, forkoff 0x%x", __func__,
279			item, dip, bp, in_f->ilf_ino, ldip->di_forkoff);
280		error = -EFSCORRUPTED;
281		goto out_release;
282	}
283	isize = xfs_log_dinode_size(mp);
284	if (unlikely(item->ri_buf[1].i_len > isize)) {
285		XFS_CORRUPTION_ERROR("xlog_recover_inode_pass2(7)",
286				     XFS_ERRLEVEL_LOW, mp, ldip,
287				     sizeof(*ldip));
288		xfs_alert(mp,
289			"%s: Bad inode log record length %d, rec ptr "PTR_FMT,
290			__func__, item->ri_buf[1].i_len, item);
291		error = -EFSCORRUPTED;
292		goto out_release;
293	}
294
295	/* recover the log dinode inode into the on disk inode */
296	xfs_log_dinode_to_disk(ldip, dip);
297
298	fields = in_f->ilf_fields;
299	if (fields & XFS_ILOG_DEV)
300		xfs_dinode_put_rdev(dip, in_f->ilf_u.ilfu_rdev);
301
302	if (in_f->ilf_size == 2)
303		goto out_owner_change;
304	len = item->ri_buf[2].i_len;
305	src = item->ri_buf[2].i_addr;
306	ASSERT(in_f->ilf_size <= 4);
307	ASSERT((in_f->ilf_size == 3) || (fields & XFS_ILOG_AFORK));
308	ASSERT(!(fields & XFS_ILOG_DFORK) ||
309	       (len == in_f->ilf_dsize));
310
311	switch (fields & XFS_ILOG_DFORK) {
312	case XFS_ILOG_DDATA:
313	case XFS_ILOG_DEXT:
314		memcpy(XFS_DFORK_DPTR(dip), src, len);
315		break;
316
317	case XFS_ILOG_DBROOT:
318		xfs_bmbt_to_bmdr(mp, (struct xfs_btree_block *)src, len,
319				 (struct xfs_bmdr_block *)XFS_DFORK_DPTR(dip),
320				 XFS_DFORK_DSIZE(dip, mp));
321		break;
322
323	default:
324		/*
325		 * There are no data fork flags set.
326		 */
327		ASSERT((fields & XFS_ILOG_DFORK) == 0);
328		break;
329	}
330
331	/*
332	 * If we logged any attribute data, recover it.  There may or
333	 * may not have been any other non-core data logged in this
334	 * transaction.
335	 */
336	if (in_f->ilf_fields & XFS_ILOG_AFORK) {
337		if (in_f->ilf_fields & XFS_ILOG_DFORK) {
338			attr_index = 3;
339		} else {
340			attr_index = 2;
341		}
342		len = item->ri_buf[attr_index].i_len;
343		src = item->ri_buf[attr_index].i_addr;
344		ASSERT(len == in_f->ilf_asize);
345
346		switch (in_f->ilf_fields & XFS_ILOG_AFORK) {
347		case XFS_ILOG_ADATA:
348		case XFS_ILOG_AEXT:
349			dest = XFS_DFORK_APTR(dip);
350			ASSERT(len <= XFS_DFORK_ASIZE(dip, mp));
351			memcpy(dest, src, len);
352			break;
353
354		case XFS_ILOG_ABROOT:
355			dest = XFS_DFORK_APTR(dip);
356			xfs_bmbt_to_bmdr(mp, (struct xfs_btree_block *)src,
357					 len, (struct xfs_bmdr_block *)dest,
358					 XFS_DFORK_ASIZE(dip, mp));
359			break;
360
361		default:
362			xfs_warn(log->l_mp, "%s: Invalid flag", __func__);
363			ASSERT(0);
364			error = -EFSCORRUPTED;
365			goto out_release;
366		}
367	}
368
369out_owner_change:
370	/* Recover the swapext owner change unless inode has been deleted */
371	if ((in_f->ilf_fields & (XFS_ILOG_DOWNER|XFS_ILOG_AOWNER)) &&
372	    (dip->di_mode != 0))
373		error = xfs_recover_inode_owner_change(mp, dip, in_f,
374						       buffer_list);
375	/* re-generate the checksum. */
376	xfs_dinode_calc_crc(log->l_mp, dip);
377
378	ASSERT(bp->b_mount == mp);
379	bp->b_flags |= _XBF_LOGRECOVERY;
380	xfs_buf_delwri_queue(bp, buffer_list);
381
382out_release:
383	xfs_buf_relse(bp);
384error:
385	if (need_free)
386		kmem_free(in_f);
387	return error;
388}
389
390const struct xlog_recover_item_ops xlog_inode_item_ops = {
391	.item_type		= XFS_LI_INODE,
392	.ra_pass2		= xlog_recover_inode_ra_pass2,
393	.commit_pass2		= xlog_recover_inode_commit_pass2,
394};