Linux Audio

Check our new training course

In-person Linux kernel drivers training

Jun 16-20, 2025
Register
Loading...
Note: File does not exist in v6.13.7.
  1/*
  2 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
  3 * Copyright (c) 2013 Red Hat, Inc.
  4 * All Rights Reserved.
  5 *
  6 * This program is free software; you can redistribute it and/or
  7 * modify it under the terms of the GNU General Public License as
  8 * published by the Free Software Foundation.
  9 *
 10 * This program is distributed in the hope that it would be useful,
 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13 * GNU General Public License for more details.
 14 *
 15 * You should have received a copy of the GNU General Public License
 16 * along with this program; if not, write the Free Software Foundation,
 17 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 18 */
 19#include "xfs.h"
 20#include "xfs_fs.h"
 21#include "xfs_shared.h"
 22#include "xfs_format.h"
 23#include "xfs_log_format.h"
 24#include "xfs_trans_resv.h"
 25#include "xfs_bit.h"
 26#include "xfs_sb.h"
 27#include "xfs_ag.h"
 28#include "xfs_mount.h"
 29#include "xfs_da_format.h"
 30#include "xfs_da_btree.h"
 31#include "xfs_inode.h"
 32#include "xfs_alloc.h"
 33#include "xfs_trans.h"
 34#include "xfs_inode_item.h"
 35#include "xfs_bmap.h"
 36#include "xfs_bmap_util.h"
 37#include "xfs_attr.h"
 38#include "xfs_attr_leaf.h"
 39#include "xfs_attr_remote.h"
 40#include "xfs_trans_space.h"
 41#include "xfs_trace.h"
 42#include "xfs_cksum.h"
 43#include "xfs_buf_item.h"
 44#include "xfs_error.h"
 45
 46#define ATTR_RMTVALUE_MAPSIZE	1	/* # of map entries at once */
 47
 48/*
 49 * Each contiguous block has a header, so it is not just a simple attribute
 50 * length to FSB conversion.
 51 */
 52int
 53xfs_attr3_rmt_blocks(
 54	struct xfs_mount *mp,
 55	int		attrlen)
 56{
 57	if (xfs_sb_version_hascrc(&mp->m_sb)) {
 58		int buflen = XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize);
 59		return (attrlen + buflen - 1) / buflen;
 60	}
 61	return XFS_B_TO_FSB(mp, attrlen);
 62}
 63
 64/*
 65 * Checking of the remote attribute header is split into two parts. The verifier
 66 * does CRC, location and bounds checking, the unpacking function checks the
 67 * attribute parameters and owner.
 68 */
 69static bool
 70xfs_attr3_rmt_hdr_ok(
 71	struct xfs_mount	*mp,
 72	void			*ptr,
 73	xfs_ino_t		ino,
 74	uint32_t		offset,
 75	uint32_t		size,
 76	xfs_daddr_t		bno)
 77{
 78	struct xfs_attr3_rmt_hdr *rmt = ptr;
 79
 80	if (bno != be64_to_cpu(rmt->rm_blkno))
 81		return false;
 82	if (offset != be32_to_cpu(rmt->rm_offset))
 83		return false;
 84	if (size != be32_to_cpu(rmt->rm_bytes))
 85		return false;
 86	if (ino != be64_to_cpu(rmt->rm_owner))
 87		return false;
 88
 89	/* ok */
 90	return true;
 91}
 92
 93static bool
 94xfs_attr3_rmt_verify(
 95	struct xfs_mount	*mp,
 96	void			*ptr,
 97	int			fsbsize,
 98	xfs_daddr_t		bno)
 99{
100	struct xfs_attr3_rmt_hdr *rmt = ptr;
101
102	if (!xfs_sb_version_hascrc(&mp->m_sb))
103		return false;
104	if (rmt->rm_magic != cpu_to_be32(XFS_ATTR3_RMT_MAGIC))
105		return false;
106	if (!uuid_equal(&rmt->rm_uuid, &mp->m_sb.sb_uuid))
107		return false;
108	if (be64_to_cpu(rmt->rm_blkno) != bno)
109		return false;
110	if (be32_to_cpu(rmt->rm_bytes) > fsbsize - sizeof(*rmt))
111		return false;
112	if (be32_to_cpu(rmt->rm_offset) +
113				be32_to_cpu(rmt->rm_bytes) > XATTR_SIZE_MAX)
114		return false;
115	if (rmt->rm_owner == 0)
116		return false;
117
118	return true;
119}
120
121static void
122xfs_attr3_rmt_read_verify(
123	struct xfs_buf	*bp)
124{
125	struct xfs_mount *mp = bp->b_target->bt_mount;
126	char		*ptr;
127	int		len;
128	xfs_daddr_t	bno;
129
130	/* no verification of non-crc buffers */
131	if (!xfs_sb_version_hascrc(&mp->m_sb))
132		return;
133
134	ptr = bp->b_addr;
135	bno = bp->b_bn;
136	len = BBTOB(bp->b_length);
137	ASSERT(len >= XFS_LBSIZE(mp));
138
139	while (len > 0) {
140		if (!xfs_verify_cksum(ptr, XFS_LBSIZE(mp),
141				      XFS_ATTR3_RMT_CRC_OFF)) {
142			xfs_buf_ioerror(bp, EFSBADCRC);
143			break;
144		}
145		if (!xfs_attr3_rmt_verify(mp, ptr, XFS_LBSIZE(mp), bno)) {
146			xfs_buf_ioerror(bp, EFSCORRUPTED);
147			break;
148		}
149		len -= XFS_LBSIZE(mp);
150		ptr += XFS_LBSIZE(mp);
151		bno += mp->m_bsize;
152	}
153
154	if (bp->b_error)
155		xfs_verifier_error(bp);
156	else
157		ASSERT(len == 0);
158}
159
160static void
161xfs_attr3_rmt_write_verify(
162	struct xfs_buf	*bp)
163{
164	struct xfs_mount *mp = bp->b_target->bt_mount;
165	struct xfs_buf_log_item	*bip = bp->b_fspriv;
166	char		*ptr;
167	int		len;
168	xfs_daddr_t	bno;
169
170	/* no verification of non-crc buffers */
171	if (!xfs_sb_version_hascrc(&mp->m_sb))
172		return;
173
174	ptr = bp->b_addr;
175	bno = bp->b_bn;
176	len = BBTOB(bp->b_length);
177	ASSERT(len >= XFS_LBSIZE(mp));
178
179	while (len > 0) {
180		if (!xfs_attr3_rmt_verify(mp, ptr, XFS_LBSIZE(mp), bno)) {
181			xfs_buf_ioerror(bp, EFSCORRUPTED);
182			xfs_verifier_error(bp);
183			return;
184		}
185		if (bip) {
186			struct xfs_attr3_rmt_hdr *rmt;
187
188			rmt = (struct xfs_attr3_rmt_hdr *)ptr;
189			rmt->rm_lsn = cpu_to_be64(bip->bli_item.li_lsn);
190		}
191		xfs_update_cksum(ptr, XFS_LBSIZE(mp), XFS_ATTR3_RMT_CRC_OFF);
192
193		len -= XFS_LBSIZE(mp);
194		ptr += XFS_LBSIZE(mp);
195		bno += mp->m_bsize;
196	}
197	ASSERT(len == 0);
198}
199
200const struct xfs_buf_ops xfs_attr3_rmt_buf_ops = {
201	.verify_read = xfs_attr3_rmt_read_verify,
202	.verify_write = xfs_attr3_rmt_write_verify,
203};
204
205STATIC int
206xfs_attr3_rmt_hdr_set(
207	struct xfs_mount	*mp,
208	void			*ptr,
209	xfs_ino_t		ino,
210	uint32_t		offset,
211	uint32_t		size,
212	xfs_daddr_t		bno)
213{
214	struct xfs_attr3_rmt_hdr *rmt = ptr;
215
216	if (!xfs_sb_version_hascrc(&mp->m_sb))
217		return 0;
218
219	rmt->rm_magic = cpu_to_be32(XFS_ATTR3_RMT_MAGIC);
220	rmt->rm_offset = cpu_to_be32(offset);
221	rmt->rm_bytes = cpu_to_be32(size);
222	uuid_copy(&rmt->rm_uuid, &mp->m_sb.sb_uuid);
223	rmt->rm_owner = cpu_to_be64(ino);
224	rmt->rm_blkno = cpu_to_be64(bno);
225
226	return sizeof(struct xfs_attr3_rmt_hdr);
227}
228
229/*
230 * Helper functions to copy attribute data in and out of the one disk extents
231 */
232STATIC int
233xfs_attr_rmtval_copyout(
234	struct xfs_mount *mp,
235	struct xfs_buf	*bp,
236	xfs_ino_t	ino,
237	int		*offset,
238	int		*valuelen,
239	__uint8_t	**dst)
240{
241	char		*src = bp->b_addr;
242	xfs_daddr_t	bno = bp->b_bn;
243	int		len = BBTOB(bp->b_length);
244
245	ASSERT(len >= XFS_LBSIZE(mp));
246
247	while (len > 0 && *valuelen > 0) {
248		int hdr_size = 0;
249		int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, XFS_LBSIZE(mp));
250
251		byte_cnt = min(*valuelen, byte_cnt);
252
253		if (xfs_sb_version_hascrc(&mp->m_sb)) {
254			if (!xfs_attr3_rmt_hdr_ok(mp, src, ino, *offset,
255						  byte_cnt, bno)) {
256				xfs_alert(mp,
257"remote attribute header mismatch bno/off/len/owner (0x%llx/0x%x/Ox%x/0x%llx)",
258					bno, *offset, byte_cnt, ino);
259				return EFSCORRUPTED;
260			}
261			hdr_size = sizeof(struct xfs_attr3_rmt_hdr);
262		}
263
264		memcpy(*dst, src + hdr_size, byte_cnt);
265
266		/* roll buffer forwards */
267		len -= XFS_LBSIZE(mp);
268		src += XFS_LBSIZE(mp);
269		bno += mp->m_bsize;
270
271		/* roll attribute data forwards */
272		*valuelen -= byte_cnt;
273		*dst += byte_cnt;
274		*offset += byte_cnt;
275	}
276	return 0;
277}
278
279STATIC void
280xfs_attr_rmtval_copyin(
281	struct xfs_mount *mp,
282	struct xfs_buf	*bp,
283	xfs_ino_t	ino,
284	int		*offset,
285	int		*valuelen,
286	__uint8_t	**src)
287{
288	char		*dst = bp->b_addr;
289	xfs_daddr_t	bno = bp->b_bn;
290	int		len = BBTOB(bp->b_length);
291
292	ASSERT(len >= XFS_LBSIZE(mp));
293
294	while (len > 0 && *valuelen > 0) {
295		int hdr_size;
296		int byte_cnt = XFS_ATTR3_RMT_BUF_SPACE(mp, XFS_LBSIZE(mp));
297
298		byte_cnt = min(*valuelen, byte_cnt);
299		hdr_size = xfs_attr3_rmt_hdr_set(mp, dst, ino, *offset,
300						 byte_cnt, bno);
301
302		memcpy(dst + hdr_size, *src, byte_cnt);
303
304		/*
305		 * If this is the last block, zero the remainder of it.
306		 * Check that we are actually the last block, too.
307		 */
308		if (byte_cnt + hdr_size < XFS_LBSIZE(mp)) {
309			ASSERT(*valuelen - byte_cnt == 0);
310			ASSERT(len == XFS_LBSIZE(mp));
311			memset(dst + hdr_size + byte_cnt, 0,
312					XFS_LBSIZE(mp) - hdr_size - byte_cnt);
313		}
314
315		/* roll buffer forwards */
316		len -= XFS_LBSIZE(mp);
317		dst += XFS_LBSIZE(mp);
318		bno += mp->m_bsize;
319
320		/* roll attribute data forwards */
321		*valuelen -= byte_cnt;
322		*src += byte_cnt;
323		*offset += byte_cnt;
324	}
325}
326
327/*
328 * Read the value associated with an attribute from the out-of-line buffer
329 * that we stored it in.
330 */
331int
332xfs_attr_rmtval_get(
333	struct xfs_da_args	*args)
334{
335	struct xfs_bmbt_irec	map[ATTR_RMTVALUE_MAPSIZE];
336	struct xfs_mount	*mp = args->dp->i_mount;
337	struct xfs_buf		*bp;
338	xfs_dablk_t		lblkno = args->rmtblkno;
339	__uint8_t		*dst = args->value;
340	int			valuelen;
341	int			nmap;
342	int			error;
343	int			blkcnt = args->rmtblkcnt;
344	int			i;
345	int			offset = 0;
346
347	trace_xfs_attr_rmtval_get(args);
348
349	ASSERT(!(args->flags & ATTR_KERNOVAL));
350	ASSERT(args->rmtvaluelen == args->valuelen);
351
352	valuelen = args->rmtvaluelen;
353	while (valuelen > 0) {
354		nmap = ATTR_RMTVALUE_MAPSIZE;
355		error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno,
356				       blkcnt, map, &nmap,
357				       XFS_BMAPI_ATTRFORK);
358		if (error)
359			return error;
360		ASSERT(nmap >= 1);
361
362		for (i = 0; (i < nmap) && (valuelen > 0); i++) {
363			xfs_daddr_t	dblkno;
364			int		dblkcnt;
365
366			ASSERT((map[i].br_startblock != DELAYSTARTBLOCK) &&
367			       (map[i].br_startblock != HOLESTARTBLOCK));
368			dblkno = XFS_FSB_TO_DADDR(mp, map[i].br_startblock);
369			dblkcnt = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
370			error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp,
371						   dblkno, dblkcnt, 0, &bp,
372						   &xfs_attr3_rmt_buf_ops);
373			if (error)
374				return error;
375
376			error = xfs_attr_rmtval_copyout(mp, bp, args->dp->i_ino,
377							&offset, &valuelen,
378							&dst);
379			xfs_buf_relse(bp);
380			if (error)
381				return error;
382
383			/* roll attribute extent map forwards */
384			lblkno += map[i].br_blockcount;
385			blkcnt -= map[i].br_blockcount;
386		}
387	}
388	ASSERT(valuelen == 0);
389	return 0;
390}
391
392/*
393 * Write the value associated with an attribute into the out-of-line buffer
394 * that we have defined for it.
395 */
396int
397xfs_attr_rmtval_set(
398	struct xfs_da_args	*args)
399{
400	struct xfs_inode	*dp = args->dp;
401	struct xfs_mount	*mp = dp->i_mount;
402	struct xfs_bmbt_irec	map;
403	xfs_dablk_t		lblkno;
404	xfs_fileoff_t		lfileoff = 0;
405	__uint8_t		*src = args->value;
406	int			blkcnt;
407	int			valuelen;
408	int			nmap;
409	int			error;
410	int			offset = 0;
411
412	trace_xfs_attr_rmtval_set(args);
413
414	/*
415	 * Find a "hole" in the attribute address space large enough for
416	 * us to drop the new attribute's value into. Because CRC enable
417	 * attributes have headers, we can't just do a straight byte to FSB
418	 * conversion and have to take the header space into account.
419	 */
420	blkcnt = xfs_attr3_rmt_blocks(mp, args->rmtvaluelen);
421	error = xfs_bmap_first_unused(args->trans, args->dp, blkcnt, &lfileoff,
422						   XFS_ATTR_FORK);
423	if (error)
424		return error;
425
426	args->rmtblkno = lblkno = (xfs_dablk_t)lfileoff;
427	args->rmtblkcnt = blkcnt;
428
429	/*
430	 * Roll through the "value", allocating blocks on disk as required.
431	 */
432	while (blkcnt > 0) {
433		int	committed;
434
435		/*
436		 * Allocate a single extent, up to the size of the value.
437		 */
438		xfs_bmap_init(args->flist, args->firstblock);
439		nmap = 1;
440		error = xfs_bmapi_write(args->trans, dp, (xfs_fileoff_t)lblkno,
441				  blkcnt,
442				  XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
443				  args->firstblock, args->total, &map, &nmap,
444				  args->flist);
445		if (!error) {
446			error = xfs_bmap_finish(&args->trans, args->flist,
447						&committed);
448		}
449		if (error) {
450			ASSERT(committed);
451			args->trans = NULL;
452			xfs_bmap_cancel(args->flist);
453			return(error);
454		}
455
456		/*
457		 * bmap_finish() may have committed the last trans and started
458		 * a new one.  We need the inode to be in all transactions.
459		 */
460		if (committed)
461			xfs_trans_ijoin(args->trans, dp, 0);
462
463		ASSERT(nmap == 1);
464		ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
465		       (map.br_startblock != HOLESTARTBLOCK));
466		lblkno += map.br_blockcount;
467		blkcnt -= map.br_blockcount;
468
469		/*
470		 * Start the next trans in the chain.
471		 */
472		error = xfs_trans_roll(&args->trans, dp);
473		if (error)
474			return (error);
475	}
476
477	/*
478	 * Roll through the "value", copying the attribute value to the
479	 * already-allocated blocks.  Blocks are written synchronously
480	 * so that we can know they are all on disk before we turn off
481	 * the INCOMPLETE flag.
482	 */
483	lblkno = args->rmtblkno;
484	blkcnt = args->rmtblkcnt;
485	valuelen = args->rmtvaluelen;
486	while (valuelen > 0) {
487		struct xfs_buf	*bp;
488		xfs_daddr_t	dblkno;
489		int		dblkcnt;
490
491		ASSERT(blkcnt > 0);
492
493		xfs_bmap_init(args->flist, args->firstblock);
494		nmap = 1;
495		error = xfs_bmapi_read(dp, (xfs_fileoff_t)lblkno,
496				       blkcnt, &map, &nmap,
497				       XFS_BMAPI_ATTRFORK);
498		if (error)
499			return(error);
500		ASSERT(nmap == 1);
501		ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
502		       (map.br_startblock != HOLESTARTBLOCK));
503
504		dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
505		dblkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
506
507		bp = xfs_buf_get(mp->m_ddev_targp, dblkno, dblkcnt, 0);
508		if (!bp)
509			return ENOMEM;
510		bp->b_ops = &xfs_attr3_rmt_buf_ops;
511
512		xfs_attr_rmtval_copyin(mp, bp, args->dp->i_ino, &offset,
513				       &valuelen, &src);
514
515		error = xfs_bwrite(bp);	/* GROT: NOTE: synchronous write */
516		xfs_buf_relse(bp);
517		if (error)
518			return error;
519
520
521		/* roll attribute extent map forwards */
522		lblkno += map.br_blockcount;
523		blkcnt -= map.br_blockcount;
524	}
525	ASSERT(valuelen == 0);
526	return 0;
527}
528
529/*
530 * Remove the value associated with an attribute by deleting the
531 * out-of-line buffer that it is stored on.
532 */
533int
534xfs_attr_rmtval_remove(
535	struct xfs_da_args	*args)
536{
537	struct xfs_mount	*mp = args->dp->i_mount;
538	xfs_dablk_t		lblkno;
539	int			blkcnt;
540	int			error;
541	int			done;
542
543	trace_xfs_attr_rmtval_remove(args);
544
545	/*
546	 * Roll through the "value", invalidating the attribute value's blocks.
547	 */
548	lblkno = args->rmtblkno;
549	blkcnt = args->rmtblkcnt;
550	while (blkcnt > 0) {
551		struct xfs_bmbt_irec	map;
552		struct xfs_buf		*bp;
553		xfs_daddr_t		dblkno;
554		int			dblkcnt;
555		int			nmap;
556
557		/*
558		 * Try to remember where we decided to put the value.
559		 */
560		nmap = 1;
561		error = xfs_bmapi_read(args->dp, (xfs_fileoff_t)lblkno,
562				       blkcnt, &map, &nmap, XFS_BMAPI_ATTRFORK);
563		if (error)
564			return(error);
565		ASSERT(nmap == 1);
566		ASSERT((map.br_startblock != DELAYSTARTBLOCK) &&
567		       (map.br_startblock != HOLESTARTBLOCK));
568
569		dblkno = XFS_FSB_TO_DADDR(mp, map.br_startblock),
570		dblkcnt = XFS_FSB_TO_BB(mp, map.br_blockcount);
571
572		/*
573		 * If the "remote" value is in the cache, remove it.
574		 */
575		bp = xfs_incore(mp->m_ddev_targp, dblkno, dblkcnt, XBF_TRYLOCK);
576		if (bp) {
577			xfs_buf_stale(bp);
578			xfs_buf_relse(bp);
579			bp = NULL;
580		}
581
582		lblkno += map.br_blockcount;
583		blkcnt -= map.br_blockcount;
584	}
585
586	/*
587	 * Keep de-allocating extents until the remote-value region is gone.
588	 */
589	lblkno = args->rmtblkno;
590	blkcnt = args->rmtblkcnt;
591	done = 0;
592	while (!done) {
593		int committed;
594
595		xfs_bmap_init(args->flist, args->firstblock);
596		error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt,
597				    XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA,
598				    1, args->firstblock, args->flist,
599				    &done);
600		if (!error) {
601			error = xfs_bmap_finish(&args->trans, args->flist,
602						&committed);
603		}
604		if (error) {
605			ASSERT(committed);
606			args->trans = NULL;
607			xfs_bmap_cancel(args->flist);
608			return error;
609		}
610
611		/*
612		 * bmap_finish() may have committed the last trans and started
613		 * a new one.  We need the inode to be in all transactions.
614		 */
615		if (committed)
616			xfs_trans_ijoin(args->trans, args->dp, 0);
617
618		/*
619		 * Close out trans and start the next one in the chain.
620		 */
621		error = xfs_trans_roll(&args->trans, args->dp);
622		if (error)
623			return (error);
624	}
625	return(0);
626}