xref: /linux/fs/exfat/iomap.c (revision 3d5e48944e824bddc20d7b874e784f7b279636fe)
182a81a73SNamjae Jeon // SPDX-License-Identifier: GPL-2.0-or-later
282a81a73SNamjae Jeon /*
382a81a73SNamjae Jeon  * iomap callack functions
482a81a73SNamjae Jeon  *
582a81a73SNamjae Jeon  * Copyright (C) 2026 Namjae Jeon <linkinjeon@kernel.org>
682a81a73SNamjae Jeon  */
782a81a73SNamjae Jeon 
882a81a73SNamjae Jeon #include <linux/iomap.h>
982a81a73SNamjae Jeon #include <linux/pagemap.h>
1082a81a73SNamjae Jeon 
1182a81a73SNamjae Jeon #include "exfat_raw.h"
1282a81a73SNamjae Jeon #include "exfat_fs.h"
1382a81a73SNamjae Jeon #include "iomap.h"
1482a81a73SNamjae Jeon 
15867b9c96SNamjae Jeon /*
16867b9c96SNamjae Jeon  * exfat_file_write_dio_end_io - Direct I/O write completion handler
17867b9c96SNamjae Jeon  *
18867b9c96SNamjae Jeon  * Updates i_size if the write extended the file. Called from the dio layer
19867b9c96SNamjae Jeon  * after I/O completion.
20867b9c96SNamjae Jeon  */
21867b9c96SNamjae Jeon static int exfat_file_write_dio_end_io(struct kiocb *iocb, ssize_t size,
22867b9c96SNamjae Jeon 		int error, unsigned int flags)
23867b9c96SNamjae Jeon {
24867b9c96SNamjae Jeon 	struct inode *inode = file_inode(iocb->ki_filp);
25867b9c96SNamjae Jeon 
26867b9c96SNamjae Jeon 	if (error)
27867b9c96SNamjae Jeon 		return error;
28867b9c96SNamjae Jeon 
29867b9c96SNamjae Jeon 	if (size && i_size_read(inode) < iocb->ki_pos + size) {
30867b9c96SNamjae Jeon 		i_size_write(inode, iocb->ki_pos + size);
31867b9c96SNamjae Jeon 		mark_inode_dirty(inode);
32867b9c96SNamjae Jeon 	}
33867b9c96SNamjae Jeon 
34867b9c96SNamjae Jeon 	return 0;
35867b9c96SNamjae Jeon }
36867b9c96SNamjae Jeon 
37867b9c96SNamjae Jeon const struct iomap_dio_ops exfat_write_dio_ops = {
38867b9c96SNamjae Jeon 	.end_io		= exfat_file_write_dio_end_io,
39867b9c96SNamjae Jeon };
40867b9c96SNamjae Jeon 
4182a81a73SNamjae Jeon static int __exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
4282a81a73SNamjae Jeon 		unsigned int flags, struct iomap *iomap, bool may_alloc)
4382a81a73SNamjae Jeon {
4482a81a73SNamjae Jeon 	struct super_block *sb = inode->i_sb;
4582a81a73SNamjae Jeon 	struct exfat_sb_info *sbi = EXFAT_SB(sb);
4682a81a73SNamjae Jeon 	struct exfat_inode_info *ei = EXFAT_I(inode);
4782a81a73SNamjae Jeon 	unsigned int cluster, num_clusters;
4882a81a73SNamjae Jeon 	loff_t cluster_offset, cluster_length;
4982a81a73SNamjae Jeon 	int err;
5082a81a73SNamjae Jeon 	bool balloc = false;
5182a81a73SNamjae Jeon 
5282a81a73SNamjae Jeon 	if (!may_alloc) {
5382a81a73SNamjae Jeon 		/* Completely beyond EOF. Treat as hole */
5482a81a73SNamjae Jeon 		if (i_size_read(inode) <= offset) {
5582a81a73SNamjae Jeon 			iomap->type = IOMAP_HOLE;
5682a81a73SNamjae Jeon 			iomap->addr = IOMAP_NULL_ADDR;
5782a81a73SNamjae Jeon 			iomap->offset = offset;
5882a81a73SNamjae Jeon 			iomap->length = length;
5982a81a73SNamjae Jeon 			return 0;
6082a81a73SNamjae Jeon 		}
6182a81a73SNamjae Jeon 
6282a81a73SNamjae Jeon 		/* Clamp length if the requested range goes beyond i_size */
6382a81a73SNamjae Jeon 		if (offset + length > i_size_read(inode))
6482a81a73SNamjae Jeon 			length = round_up(i_size_read(inode),
6582a81a73SNamjae Jeon 					  i_blocksize(inode)) - offset;
6682a81a73SNamjae Jeon 	}
6782a81a73SNamjae Jeon 
6882a81a73SNamjae Jeon 	num_clusters = exfat_bytes_to_cluster_round_up(sbi,
6982a81a73SNamjae Jeon 			offset + length) - exfat_bytes_to_cluster(sbi, offset);
7082a81a73SNamjae Jeon 
7182a81a73SNamjae Jeon 	mutex_lock(&sbi->s_lock);
7282a81a73SNamjae Jeon 	iomap->bdev = inode->i_sb->s_bdev;
7382a81a73SNamjae Jeon 	iomap->offset = offset;
7482a81a73SNamjae Jeon 
7582a81a73SNamjae Jeon 	err = exfat_map_cluster(inode, exfat_bytes_to_cluster(sbi, offset),
7682a81a73SNamjae Jeon 			&cluster, &num_clusters, may_alloc, &balloc);
7782a81a73SNamjae Jeon 	if (err)
7882a81a73SNamjae Jeon 		goto out;
7982a81a73SNamjae Jeon 
8082a81a73SNamjae Jeon 	cluster_offset = exfat_cluster_offset(sbi, offset);
8182a81a73SNamjae Jeon 	cluster_length = exfat_cluster_to_bytes(sbi, num_clusters);
8282a81a73SNamjae Jeon 
8382a81a73SNamjae Jeon 	iomap->length = min_t(loff_t, length, cluster_length - cluster_offset);
8482a81a73SNamjae Jeon 	iomap->addr = exfat_cluster_to_phys_bytes(sbi, cluster) + cluster_offset;
8582a81a73SNamjae Jeon 	iomap->type = IOMAP_MAPPED;
8682a81a73SNamjae Jeon 	iomap->flags = IOMAP_F_MERGED;
8782a81a73SNamjae Jeon 
8882a81a73SNamjae Jeon 	if (may_alloc || flags & IOMAP_ZERO) {
8982a81a73SNamjae Jeon 		if (balloc)
9082a81a73SNamjae Jeon 			iomap->flags |= IOMAP_F_NEW;
9182a81a73SNamjae Jeon 		else if (iomap->offset + iomap->length >= ei->valid_size) {
9282a81a73SNamjae Jeon 			/*
9382a81a73SNamjae Jeon 			 * This is a write that starts at or extends beyond
9482a81a73SNamjae Jeon 			 * the current valid_size. The region between the old
9582a81a73SNamjae Jeon 			 * valid_size and the end of this write needs to be
9682a81a73SNamjae Jeon 			 * zeroed in the page cache to prevent stale data
9782a81a73SNamjae Jeon 			 * exposure (see IOMAP_F_ZERO_TAIL handling in
9882a81a73SNamjae Jeon 			 * __iomap_write_begin()).
9982a81a73SNamjae Jeon 			 */
10082a81a73SNamjae Jeon 			iomap->flags |= IOMAP_F_ZERO_TAIL;
10182a81a73SNamjae Jeon 		}
10282a81a73SNamjae Jeon 	} else {
103b4b7fe2cSNamjae Jeon 		/*
104b4b7fe2cSNamjae Jeon 		 * valid_size is tracked in byte granularity and
105b4b7fe2cSNamjae Jeon 		 * marks the exact boundary between valid data and
106b4b7fe2cSNamjae Jeon 		 * holes (or unwritten space).
107b4b7fe2cSNamjae Jeon 		 *
108b4b7fe2cSNamjae Jeon 		 * When IOMAP_REPORT is set (used by lseek(SEEK_HOLE)
109b4b7fe2cSNamjae Jeon 		 * and SEEK_DATA), we return IOMAP_HOLE. This allows
110b4b7fe2cSNamjae Jeon 		 * iomap_seek_hole_iter() to directly return the
111b4b7fe2cSNamjae Jeon 		 * precise byte position.
112b4b7fe2cSNamjae Jeon 		 *
113b4b7fe2cSNamjae Jeon 		 * For normal I/O paths (without IOMAP_REPORT) we
114b4b7fe2cSNamjae Jeon 		 * return IOMAP_UNWRITTEN so the write path can
115b4b7fe2cSNamjae Jeon 		 * distinguish it from a real hole.
116b4b7fe2cSNamjae Jeon 		 */
11782a81a73SNamjae Jeon 		if (offset >= ei->valid_size) {
118b4b7fe2cSNamjae Jeon 			iomap->type = flags & IOMAP_REPORT ?
119b4b7fe2cSNamjae Jeon 				IOMAP_HOLE : IOMAP_UNWRITTEN;
12082a81a73SNamjae Jeon 		} else if (offset + iomap->length > ei->valid_size) {
121b4b7fe2cSNamjae Jeon 			if (flags & IOMAP_REPORT) {
122b4b7fe2cSNamjae Jeon 				/*
123b4b7fe2cSNamjae Jeon 				 * For SEEK_HOLE/SEEK_DATA, clip the length
124b4b7fe2cSNamjae Jeon 				 * to the exact byte boundary (valid_size).
125b4b7fe2cSNamjae Jeon 				 * This ensures the caller gets the precise
126b4b7fe2cSNamjae Jeon 				 * hole position in byte units.
127b4b7fe2cSNamjae Jeon 				 */
128b4b7fe2cSNamjae Jeon 				iomap->length = ei->valid_size - iomap->offset;
129b4b7fe2cSNamjae Jeon 			} else
13082a81a73SNamjae Jeon 				iomap->length = round_up(ei->valid_size,
13182a81a73SNamjae Jeon 							 i_blocksize(inode)) -
13282a81a73SNamjae Jeon 								iomap->offset;
13382a81a73SNamjae Jeon 		}
13482a81a73SNamjae Jeon 	}
13582a81a73SNamjae Jeon 
13682a81a73SNamjae Jeon 	iomap->flags |= IOMAP_F_MERGED;
13782a81a73SNamjae Jeon out:
13882a81a73SNamjae Jeon 	mutex_unlock(&sbi->s_lock);
13982a81a73SNamjae Jeon 	return err;
14082a81a73SNamjae Jeon }
14182a81a73SNamjae Jeon 
14282a81a73SNamjae Jeon static int exfat_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
14382a81a73SNamjae Jeon 		unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
14482a81a73SNamjae Jeon {
14582a81a73SNamjae Jeon 	return __exfat_iomap_begin(inode, offset, length, flags, iomap, false);
14682a81a73SNamjae Jeon }
14782a81a73SNamjae Jeon 
14882a81a73SNamjae Jeon static int exfat_write_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
14982a81a73SNamjae Jeon 		unsigned int flags, struct iomap *iomap, struct iomap *srcmap)
15082a81a73SNamjae Jeon {
15182a81a73SNamjae Jeon 	return __exfat_iomap_begin(inode, offset, length, flags, iomap, true);
15282a81a73SNamjae Jeon }
15382a81a73SNamjae Jeon 
15482a81a73SNamjae Jeon const struct iomap_ops exfat_iomap_ops = {
15582a81a73SNamjae Jeon 	.iomap_begin = exfat_iomap_begin,
15682a81a73SNamjae Jeon };
15782a81a73SNamjae Jeon 
15882a81a73SNamjae Jeon /*
15982a81a73SNamjae Jeon  * exfat_write_iomap_end - Update the state after write
16082a81a73SNamjae Jeon  *
16182a81a73SNamjae Jeon  * Extends ->valid_size to cover the newly written range.
16282a81a73SNamjae Jeon  * Marks the inode dirty if metadata was changed.
16382a81a73SNamjae Jeon  */
16482a81a73SNamjae Jeon static int exfat_write_iomap_end(struct inode *inode, loff_t pos, loff_t length,
16582a81a73SNamjae Jeon 		ssize_t written, unsigned int flags, struct iomap *iomap)
16682a81a73SNamjae Jeon {
16782a81a73SNamjae Jeon 	struct exfat_inode_info *ei = EXFAT_I(inode);
16882a81a73SNamjae Jeon 	bool dirtied = false;
16982a81a73SNamjae Jeon 	loff_t end;
17082a81a73SNamjae Jeon 
17182a81a73SNamjae Jeon 	if (!written)
17282a81a73SNamjae Jeon 		return 0;
17382a81a73SNamjae Jeon 
17482a81a73SNamjae Jeon 	end = pos + written;
17582a81a73SNamjae Jeon 
17682a81a73SNamjae Jeon 	if (ei->valid_size < end) {
17782a81a73SNamjae Jeon 		ei->valid_size = end;
17882a81a73SNamjae Jeon 		if (ei->zeroed_size < end)
17982a81a73SNamjae Jeon 			ei->zeroed_size = end;
18082a81a73SNamjae Jeon 		dirtied = true;
18182a81a73SNamjae Jeon 	}
18282a81a73SNamjae Jeon 
18382a81a73SNamjae Jeon 	if (dirtied || iomap->flags & IOMAP_F_SIZE_CHANGED)
18482a81a73SNamjae Jeon 		mark_inode_dirty(inode);
18582a81a73SNamjae Jeon 
18682a81a73SNamjae Jeon 	return written;
18782a81a73SNamjae Jeon }
18882a81a73SNamjae Jeon 
18982a81a73SNamjae Jeon const struct iomap_ops exfat_write_iomap_ops = {
19082a81a73SNamjae Jeon 	.iomap_begin	= exfat_write_iomap_begin,
19182a81a73SNamjae Jeon 	.iomap_end	= exfat_write_iomap_end,
19282a81a73SNamjae Jeon };
19382a81a73SNamjae Jeon 
19482a81a73SNamjae Jeon /*
19582a81a73SNamjae Jeon  * exfat_writeback_range - Map folio during writeback
19682a81a73SNamjae Jeon  *
19782a81a73SNamjae Jeon  * Called for each folio during writeback. If the folio falls outside the
19882a81a73SNamjae Jeon  * current iomap, remaps by calling read_iomap_begin.
19982a81a73SNamjae Jeon  */
20082a81a73SNamjae Jeon static ssize_t exfat_writeback_range(struct iomap_writepage_ctx *wpc,
20182a81a73SNamjae Jeon 		struct folio *folio, u64 offset, unsigned int len, u64 end_pos)
20282a81a73SNamjae Jeon {
20382a81a73SNamjae Jeon 	if (offset < wpc->iomap.offset ||
20482a81a73SNamjae Jeon 	    offset >= wpc->iomap.offset + wpc->iomap.length) {
20582a81a73SNamjae Jeon 		int error;
20682a81a73SNamjae Jeon 
20782a81a73SNamjae Jeon 		error = __exfat_iomap_begin(wpc->inode, offset, len,
20882a81a73SNamjae Jeon 				0, &wpc->iomap, false);
20982a81a73SNamjae Jeon 		if (error)
21082a81a73SNamjae Jeon 			return error;
21182a81a73SNamjae Jeon 	}
21282a81a73SNamjae Jeon 
21382a81a73SNamjae Jeon 	return iomap_add_to_ioend(wpc, folio, offset, end_pos, len);
21482a81a73SNamjae Jeon }
21582a81a73SNamjae Jeon 
21682a81a73SNamjae Jeon const struct iomap_writeback_ops exfat_writeback_ops = {
21782a81a73SNamjae Jeon 	.writeback_range	= exfat_writeback_range,
21882a81a73SNamjae Jeon 	.writeback_submit	= iomap_ioend_writeback_submit,
21982a81a73SNamjae Jeon };
22082a81a73SNamjae Jeon 
22182a81a73SNamjae Jeon /**
22282a81a73SNamjae Jeon  * exfat_iomap_read_end_io - iomap read bio completion handler for exFAT
22382a81a73SNamjae Jeon  * @bio: bio that has completed reading
22482a81a73SNamjae Jeon  *
22582a81a73SNamjae Jeon  * exfat_iomap_begin() rounds up MAPPED extents to the block boundary of
22682a81a73SNamjae Jeon  * valid_size. This ensures that any subsequent blocks are treated as
22782a81a73SNamjae Jeon  * IOMAP_UNWRITTEN, but it also causes the "straddle block" containing
22882a81a73SNamjae Jeon  * valid_size to be read from disk. The disk data beyond valid_size in
22982a81a73SNamjae Jeon  * this block is stale and must be zeroed to prevent data leakage.
23082a81a73SNamjae Jeon  */
23182a81a73SNamjae Jeon static void exfat_iomap_read_end_io(struct bio *bio)
23282a81a73SNamjae Jeon {
23382a81a73SNamjae Jeon 	int error = blk_status_to_errno(bio->bi_status);
23482a81a73SNamjae Jeon 	struct folio_iter iter;
23582a81a73SNamjae Jeon 
23682a81a73SNamjae Jeon 	bio_for_each_folio_all(iter, bio) {
23782a81a73SNamjae Jeon 		struct folio *folio = iter.folio;
23882a81a73SNamjae Jeon 		struct exfat_inode_info *ei = EXFAT_I(folio->mapping->host);
23982a81a73SNamjae Jeon 		s64 valid_size;
24082a81a73SNamjae Jeon 		loff_t pos = folio_pos(folio);
24182a81a73SNamjae Jeon 
24282a81a73SNamjae Jeon 		valid_size = ei->valid_size;
24382a81a73SNamjae Jeon 		if (pos + iter.offset < valid_size &&
24482a81a73SNamjae Jeon 		    pos + iter.offset + iter.length > valid_size)
24582a81a73SNamjae Jeon 			folio_zero_segment(folio, offset_in_folio(folio, valid_size),
24682a81a73SNamjae Jeon 					   iter.offset + iter.length);
24782a81a73SNamjae Jeon 
24882a81a73SNamjae Jeon 		iomap_finish_folio_read(folio, iter.offset, iter.length, error);
24982a81a73SNamjae Jeon 	}
25082a81a73SNamjae Jeon 	bio_put(bio);
25182a81a73SNamjae Jeon }
25282a81a73SNamjae Jeon 
25382a81a73SNamjae Jeon static void exfat_iomap_bio_submit_read(const struct iomap_iter *iter,
25482a81a73SNamjae Jeon 		struct iomap_read_folio_ctx *ctx)
25582a81a73SNamjae Jeon {
256*044472d5SChristoph Hellwig 	iomap_bio_submit_read_endio(iter, ctx, exfat_iomap_read_end_io);
25782a81a73SNamjae Jeon }
25882a81a73SNamjae Jeon 
25982a81a73SNamjae Jeon const struct iomap_read_ops exfat_iomap_bio_read_ops = {
26082a81a73SNamjae Jeon 	.read_folio_range	= iomap_bio_read_folio_range,
26182a81a73SNamjae Jeon 	.submit_read		= exfat_iomap_bio_submit_read,
26282a81a73SNamjae Jeon };
26303a43677SJan Polensky 
26403a43677SJan Polensky int exfat_iomap_swap_activate(struct swap_info_struct *sis,
26503a43677SJan Polensky 			       struct file *file, sector_t *span)
26603a43677SJan Polensky {
26703a43677SJan Polensky 	return iomap_swapfile_activate(sis, file, span, &exfat_iomap_ops);
26803a43677SJan Polensky }
269