18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2004, OGAWA Hirofumi
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
78c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
88c2ecf20Sopenharmony_ci#include "fat.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cistruct fatent_operations {
118c2ecf20Sopenharmony_ci	void (*ent_blocknr)(struct super_block *, int, int *, sector_t *);
128c2ecf20Sopenharmony_ci	void (*ent_set_ptr)(struct fat_entry *, int);
138c2ecf20Sopenharmony_ci	int (*ent_bread)(struct super_block *, struct fat_entry *,
148c2ecf20Sopenharmony_ci			 int, sector_t);
158c2ecf20Sopenharmony_ci	int (*ent_get)(struct fat_entry *);
168c2ecf20Sopenharmony_ci	void (*ent_put)(struct fat_entry *, int);
178c2ecf20Sopenharmony_ci	int (*ent_next)(struct fat_entry *);
188c2ecf20Sopenharmony_ci};
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(fat12_entry_lock);
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic void fat12_ent_blocknr(struct super_block *sb, int entry,
238c2ecf20Sopenharmony_ci			      int *offset, sector_t *blocknr)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
268c2ecf20Sopenharmony_ci	int bytes = entry + (entry >> 1);
278c2ecf20Sopenharmony_ci	WARN_ON(!fat_valid_entry(sbi, entry));
288c2ecf20Sopenharmony_ci	*offset = bytes & (sb->s_blocksize - 1);
298c2ecf20Sopenharmony_ci	*blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits);
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic void fat_ent_blocknr(struct super_block *sb, int entry,
338c2ecf20Sopenharmony_ci			    int *offset, sector_t *blocknr)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
368c2ecf20Sopenharmony_ci	int bytes = (entry << sbi->fatent_shift);
378c2ecf20Sopenharmony_ci	WARN_ON(!fat_valid_entry(sbi, entry));
388c2ecf20Sopenharmony_ci	*offset = bytes & (sb->s_blocksize - 1);
398c2ecf20Sopenharmony_ci	*blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits);
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void fat12_ent_set_ptr(struct fat_entry *fatent, int offset)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct buffer_head **bhs = fatent->bhs;
458c2ecf20Sopenharmony_ci	if (fatent->nr_bhs == 1) {
468c2ecf20Sopenharmony_ci		WARN_ON(offset >= (bhs[0]->b_size - 1));
478c2ecf20Sopenharmony_ci		fatent->u.ent12_p[0] = bhs[0]->b_data + offset;
488c2ecf20Sopenharmony_ci		fatent->u.ent12_p[1] = bhs[0]->b_data + (offset + 1);
498c2ecf20Sopenharmony_ci	} else {
508c2ecf20Sopenharmony_ci		WARN_ON(offset != (bhs[0]->b_size - 1));
518c2ecf20Sopenharmony_ci		fatent->u.ent12_p[0] = bhs[0]->b_data + offset;
528c2ecf20Sopenharmony_ci		fatent->u.ent12_p[1] = bhs[1]->b_data;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic void fat16_ent_set_ptr(struct fat_entry *fatent, int offset)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	WARN_ON(offset & (2 - 1));
598c2ecf20Sopenharmony_ci	fatent->u.ent16_p = (__le16 *)(fatent->bhs[0]->b_data + offset);
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void fat32_ent_set_ptr(struct fat_entry *fatent, int offset)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	WARN_ON(offset & (4 - 1));
658c2ecf20Sopenharmony_ci	fatent->u.ent32_p = (__le32 *)(fatent->bhs[0]->b_data + offset);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent,
698c2ecf20Sopenharmony_ci			   int offset, sector_t blocknr)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct buffer_head **bhs = fatent->bhs;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
748c2ecf20Sopenharmony_ci	fatent->fat_inode = MSDOS_SB(sb)->fat_inode;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	bhs[0] = sb_bread(sb, blocknr);
778c2ecf20Sopenharmony_ci	if (!bhs[0])
788c2ecf20Sopenharmony_ci		goto err;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if ((offset + 1) < sb->s_blocksize)
818c2ecf20Sopenharmony_ci		fatent->nr_bhs = 1;
828c2ecf20Sopenharmony_ci	else {
838c2ecf20Sopenharmony_ci		/* This entry is block boundary, it needs the next block */
848c2ecf20Sopenharmony_ci		blocknr++;
858c2ecf20Sopenharmony_ci		bhs[1] = sb_bread(sb, blocknr);
868c2ecf20Sopenharmony_ci		if (!bhs[1])
878c2ecf20Sopenharmony_ci			goto err_brelse;
888c2ecf20Sopenharmony_ci		fatent->nr_bhs = 2;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci	fat12_ent_set_ptr(fatent, offset);
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cierr_brelse:
948c2ecf20Sopenharmony_ci	brelse(bhs[0]);
958c2ecf20Sopenharmony_cierr:
968c2ecf20Sopenharmony_ci	fat_msg_ratelimit(sb, KERN_ERR, "FAT read failed (blocknr %llu)",
978c2ecf20Sopenharmony_ci			  (llu)blocknr);
988c2ecf20Sopenharmony_ci	return -EIO;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent,
1028c2ecf20Sopenharmony_ci			 int offset, sector_t blocknr)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
1078c2ecf20Sopenharmony_ci	fatent->fat_inode = MSDOS_SB(sb)->fat_inode;
1088c2ecf20Sopenharmony_ci	fatent->bhs[0] = sb_bread(sb, blocknr);
1098c2ecf20Sopenharmony_ci	if (!fatent->bhs[0]) {
1108c2ecf20Sopenharmony_ci		fat_msg_ratelimit(sb, KERN_ERR, "FAT read failed (blocknr %llu)",
1118c2ecf20Sopenharmony_ci				  (llu)blocknr);
1128c2ecf20Sopenharmony_ci		return -EIO;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci	fatent->nr_bhs = 1;
1158c2ecf20Sopenharmony_ci	ops->ent_set_ptr(fatent, offset);
1168c2ecf20Sopenharmony_ci	return 0;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic int fat12_ent_get(struct fat_entry *fatent)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	u8 **ent12_p = fatent->u.ent12_p;
1228c2ecf20Sopenharmony_ci	int next;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	spin_lock(&fat12_entry_lock);
1258c2ecf20Sopenharmony_ci	if (fatent->entry & 1)
1268c2ecf20Sopenharmony_ci		next = (*ent12_p[0] >> 4) | (*ent12_p[1] << 4);
1278c2ecf20Sopenharmony_ci	else
1288c2ecf20Sopenharmony_ci		next = (*ent12_p[1] << 8) | *ent12_p[0];
1298c2ecf20Sopenharmony_ci	spin_unlock(&fat12_entry_lock);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	next &= 0x0fff;
1328c2ecf20Sopenharmony_ci	if (next >= BAD_FAT12)
1338c2ecf20Sopenharmony_ci		next = FAT_ENT_EOF;
1348c2ecf20Sopenharmony_ci	return next;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic int fat16_ent_get(struct fat_entry *fatent)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	int next = le16_to_cpu(*fatent->u.ent16_p);
1408c2ecf20Sopenharmony_ci	WARN_ON((unsigned long)fatent->u.ent16_p & (2 - 1));
1418c2ecf20Sopenharmony_ci	if (next >= BAD_FAT16)
1428c2ecf20Sopenharmony_ci		next = FAT_ENT_EOF;
1438c2ecf20Sopenharmony_ci	return next;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int fat32_ent_get(struct fat_entry *fatent)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	int next = le32_to_cpu(*fatent->u.ent32_p) & 0x0fffffff;
1498c2ecf20Sopenharmony_ci	WARN_ON((unsigned long)fatent->u.ent32_p & (4 - 1));
1508c2ecf20Sopenharmony_ci	if (next >= BAD_FAT32)
1518c2ecf20Sopenharmony_ci		next = FAT_ENT_EOF;
1528c2ecf20Sopenharmony_ci	return next;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic void fat12_ent_put(struct fat_entry *fatent, int new)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	u8 **ent12_p = fatent->u.ent12_p;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (new == FAT_ENT_EOF)
1608c2ecf20Sopenharmony_ci		new = EOF_FAT12;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	spin_lock(&fat12_entry_lock);
1638c2ecf20Sopenharmony_ci	if (fatent->entry & 1) {
1648c2ecf20Sopenharmony_ci		*ent12_p[0] = (new << 4) | (*ent12_p[0] & 0x0f);
1658c2ecf20Sopenharmony_ci		*ent12_p[1] = new >> 4;
1668c2ecf20Sopenharmony_ci	} else {
1678c2ecf20Sopenharmony_ci		*ent12_p[0] = new & 0xff;
1688c2ecf20Sopenharmony_ci		*ent12_p[1] = (*ent12_p[1] & 0xf0) | (new >> 8);
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci	spin_unlock(&fat12_entry_lock);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
1738c2ecf20Sopenharmony_ci	if (fatent->nr_bhs == 2)
1748c2ecf20Sopenharmony_ci		mark_buffer_dirty_inode(fatent->bhs[1], fatent->fat_inode);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic void fat16_ent_put(struct fat_entry *fatent, int new)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	if (new == FAT_ENT_EOF)
1808c2ecf20Sopenharmony_ci		new = EOF_FAT16;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	*fatent->u.ent16_p = cpu_to_le16(new);
1838c2ecf20Sopenharmony_ci	mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic void fat32_ent_put(struct fat_entry *fatent, int new)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	WARN_ON(new & 0xf0000000);
1898c2ecf20Sopenharmony_ci	new |= le32_to_cpu(*fatent->u.ent32_p) & ~0x0fffffff;
1908c2ecf20Sopenharmony_ci	*fatent->u.ent32_p = cpu_to_le32(new);
1918c2ecf20Sopenharmony_ci	mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic int fat12_ent_next(struct fat_entry *fatent)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	u8 **ent12_p = fatent->u.ent12_p;
1978c2ecf20Sopenharmony_ci	struct buffer_head **bhs = fatent->bhs;
1988c2ecf20Sopenharmony_ci	u8 *nextp = ent12_p[1] + 1 + (fatent->entry & 1);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	fatent->entry++;
2018c2ecf20Sopenharmony_ci	if (fatent->nr_bhs == 1) {
2028c2ecf20Sopenharmony_ci		WARN_ON(ent12_p[0] > (u8 *)(bhs[0]->b_data +
2038c2ecf20Sopenharmony_ci							(bhs[0]->b_size - 2)));
2048c2ecf20Sopenharmony_ci		WARN_ON(ent12_p[1] > (u8 *)(bhs[0]->b_data +
2058c2ecf20Sopenharmony_ci							(bhs[0]->b_size - 1)));
2068c2ecf20Sopenharmony_ci		if (nextp < (u8 *)(bhs[0]->b_data + (bhs[0]->b_size - 1))) {
2078c2ecf20Sopenharmony_ci			ent12_p[0] = nextp - 1;
2088c2ecf20Sopenharmony_ci			ent12_p[1] = nextp;
2098c2ecf20Sopenharmony_ci			return 1;
2108c2ecf20Sopenharmony_ci		}
2118c2ecf20Sopenharmony_ci	} else {
2128c2ecf20Sopenharmony_ci		WARN_ON(ent12_p[0] != (u8 *)(bhs[0]->b_data +
2138c2ecf20Sopenharmony_ci							(bhs[0]->b_size - 1)));
2148c2ecf20Sopenharmony_ci		WARN_ON(ent12_p[1] != (u8 *)bhs[1]->b_data);
2158c2ecf20Sopenharmony_ci		ent12_p[0] = nextp - 1;
2168c2ecf20Sopenharmony_ci		ent12_p[1] = nextp;
2178c2ecf20Sopenharmony_ci		brelse(bhs[0]);
2188c2ecf20Sopenharmony_ci		bhs[0] = bhs[1];
2198c2ecf20Sopenharmony_ci		fatent->nr_bhs = 1;
2208c2ecf20Sopenharmony_ci		return 1;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci	ent12_p[0] = NULL;
2238c2ecf20Sopenharmony_ci	ent12_p[1] = NULL;
2248c2ecf20Sopenharmony_ci	return 0;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic int fat16_ent_next(struct fat_entry *fatent)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	const struct buffer_head *bh = fatent->bhs[0];
2308c2ecf20Sopenharmony_ci	fatent->entry++;
2318c2ecf20Sopenharmony_ci	if (fatent->u.ent16_p < (__le16 *)(bh->b_data + (bh->b_size - 2))) {
2328c2ecf20Sopenharmony_ci		fatent->u.ent16_p++;
2338c2ecf20Sopenharmony_ci		return 1;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci	fatent->u.ent16_p = NULL;
2368c2ecf20Sopenharmony_ci	return 0;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic int fat32_ent_next(struct fat_entry *fatent)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	const struct buffer_head *bh = fatent->bhs[0];
2428c2ecf20Sopenharmony_ci	fatent->entry++;
2438c2ecf20Sopenharmony_ci	if (fatent->u.ent32_p < (__le32 *)(bh->b_data + (bh->b_size - 4))) {
2448c2ecf20Sopenharmony_ci		fatent->u.ent32_p++;
2458c2ecf20Sopenharmony_ci		return 1;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci	fatent->u.ent32_p = NULL;
2488c2ecf20Sopenharmony_ci	return 0;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic const struct fatent_operations fat12_ops = {
2528c2ecf20Sopenharmony_ci	.ent_blocknr	= fat12_ent_blocknr,
2538c2ecf20Sopenharmony_ci	.ent_set_ptr	= fat12_ent_set_ptr,
2548c2ecf20Sopenharmony_ci	.ent_bread	= fat12_ent_bread,
2558c2ecf20Sopenharmony_ci	.ent_get	= fat12_ent_get,
2568c2ecf20Sopenharmony_ci	.ent_put	= fat12_ent_put,
2578c2ecf20Sopenharmony_ci	.ent_next	= fat12_ent_next,
2588c2ecf20Sopenharmony_ci};
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic const struct fatent_operations fat16_ops = {
2618c2ecf20Sopenharmony_ci	.ent_blocknr	= fat_ent_blocknr,
2628c2ecf20Sopenharmony_ci	.ent_set_ptr	= fat16_ent_set_ptr,
2638c2ecf20Sopenharmony_ci	.ent_bread	= fat_ent_bread,
2648c2ecf20Sopenharmony_ci	.ent_get	= fat16_ent_get,
2658c2ecf20Sopenharmony_ci	.ent_put	= fat16_ent_put,
2668c2ecf20Sopenharmony_ci	.ent_next	= fat16_ent_next,
2678c2ecf20Sopenharmony_ci};
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic const struct fatent_operations fat32_ops = {
2708c2ecf20Sopenharmony_ci	.ent_blocknr	= fat_ent_blocknr,
2718c2ecf20Sopenharmony_ci	.ent_set_ptr	= fat32_ent_set_ptr,
2728c2ecf20Sopenharmony_ci	.ent_bread	= fat_ent_bread,
2738c2ecf20Sopenharmony_ci	.ent_get	= fat32_ent_get,
2748c2ecf20Sopenharmony_ci	.ent_put	= fat32_ent_put,
2758c2ecf20Sopenharmony_ci	.ent_next	= fat32_ent_next,
2768c2ecf20Sopenharmony_ci};
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic inline void lock_fat(struct msdos_sb_info *sbi)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	mutex_lock(&sbi->fat_lock);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic inline void unlock_fat(struct msdos_sb_info *sbi)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	mutex_unlock(&sbi->fat_lock);
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_civoid fat_ent_access_init(struct super_block *sb)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	mutex_init(&sbi->fat_lock);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (is_fat32(sbi)) {
2958c2ecf20Sopenharmony_ci		sbi->fatent_shift = 2;
2968c2ecf20Sopenharmony_ci		sbi->fatent_ops = &fat32_ops;
2978c2ecf20Sopenharmony_ci	} else if (is_fat16(sbi)) {
2988c2ecf20Sopenharmony_ci		sbi->fatent_shift = 1;
2998c2ecf20Sopenharmony_ci		sbi->fatent_ops = &fat16_ops;
3008c2ecf20Sopenharmony_ci	} else if (is_fat12(sbi)) {
3018c2ecf20Sopenharmony_ci		sbi->fatent_shift = -1;
3028c2ecf20Sopenharmony_ci		sbi->fatent_ops = &fat12_ops;
3038c2ecf20Sopenharmony_ci	} else {
3048c2ecf20Sopenharmony_ci		fat_fs_error(sb, "invalid FAT variant, %u bits", sbi->fat_bits);
3058c2ecf20Sopenharmony_ci	}
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic void mark_fsinfo_dirty(struct super_block *sb)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (sb_rdonly(sb) || !is_fat32(sbi))
3138c2ecf20Sopenharmony_ci		return;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	__mark_inode_dirty(sbi->fsinfo_inode, I_DIRTY_SYNC);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic inline int fat_ent_update_ptr(struct super_block *sb,
3198c2ecf20Sopenharmony_ci				     struct fat_entry *fatent,
3208c2ecf20Sopenharmony_ci				     int offset, sector_t blocknr)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
3238c2ecf20Sopenharmony_ci	const struct fatent_operations *ops = sbi->fatent_ops;
3248c2ecf20Sopenharmony_ci	struct buffer_head **bhs = fatent->bhs;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	/* Is this fatent's blocks including this entry? */
3278c2ecf20Sopenharmony_ci	if (!fatent->nr_bhs || bhs[0]->b_blocknr != blocknr)
3288c2ecf20Sopenharmony_ci		return 0;
3298c2ecf20Sopenharmony_ci	if (is_fat12(sbi)) {
3308c2ecf20Sopenharmony_ci		if ((offset + 1) < sb->s_blocksize) {
3318c2ecf20Sopenharmony_ci			/* This entry is on bhs[0]. */
3328c2ecf20Sopenharmony_ci			if (fatent->nr_bhs == 2) {
3338c2ecf20Sopenharmony_ci				brelse(bhs[1]);
3348c2ecf20Sopenharmony_ci				fatent->nr_bhs = 1;
3358c2ecf20Sopenharmony_ci			}
3368c2ecf20Sopenharmony_ci		} else {
3378c2ecf20Sopenharmony_ci			/* This entry needs the next block. */
3388c2ecf20Sopenharmony_ci			if (fatent->nr_bhs != 2)
3398c2ecf20Sopenharmony_ci				return 0;
3408c2ecf20Sopenharmony_ci			if (bhs[1]->b_blocknr != (blocknr + 1))
3418c2ecf20Sopenharmony_ci				return 0;
3428c2ecf20Sopenharmony_ci		}
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci	ops->ent_set_ptr(fatent, offset);
3458c2ecf20Sopenharmony_ci	return 1;
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ciint fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
3518c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
3528c2ecf20Sopenharmony_ci	const struct fatent_operations *ops = sbi->fatent_ops;
3538c2ecf20Sopenharmony_ci	int err, offset;
3548c2ecf20Sopenharmony_ci	sector_t blocknr;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (!fat_valid_entry(sbi, entry)) {
3578c2ecf20Sopenharmony_ci		fatent_brelse(fatent);
3588c2ecf20Sopenharmony_ci		fat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", entry);
3598c2ecf20Sopenharmony_ci		return -EIO;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	fatent_set_entry(fatent, entry);
3638c2ecf20Sopenharmony_ci	ops->ent_blocknr(sb, entry, &offset, &blocknr);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (!fat_ent_update_ptr(sb, fatent, offset, blocknr)) {
3668c2ecf20Sopenharmony_ci		fatent_brelse(fatent);
3678c2ecf20Sopenharmony_ci		err = ops->ent_bread(sb, fatent, offset, blocknr);
3688c2ecf20Sopenharmony_ci		if (err)
3698c2ecf20Sopenharmony_ci			return err;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci	return ops->ent_get(fatent);
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci/* FIXME: We can write the blocks as more big chunk. */
3758c2ecf20Sopenharmony_cistatic int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs,
3768c2ecf20Sopenharmony_ci			  int nr_bhs)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
3798c2ecf20Sopenharmony_ci	struct buffer_head *c_bh;
3808c2ecf20Sopenharmony_ci	int err, n, copy;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	err = 0;
3838c2ecf20Sopenharmony_ci	for (copy = 1; copy < sbi->fats; copy++) {
3848c2ecf20Sopenharmony_ci		sector_t backup_fat = sbi->fat_length * copy;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		for (n = 0; n < nr_bhs; n++) {
3878c2ecf20Sopenharmony_ci			c_bh = sb_getblk(sb, backup_fat + bhs[n]->b_blocknr);
3888c2ecf20Sopenharmony_ci			if (!c_bh) {
3898c2ecf20Sopenharmony_ci				err = -ENOMEM;
3908c2ecf20Sopenharmony_ci				goto error;
3918c2ecf20Sopenharmony_ci			}
3928c2ecf20Sopenharmony_ci			/* Avoid race with userspace read via bdev */
3938c2ecf20Sopenharmony_ci			lock_buffer(c_bh);
3948c2ecf20Sopenharmony_ci			memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize);
3958c2ecf20Sopenharmony_ci			set_buffer_uptodate(c_bh);
3968c2ecf20Sopenharmony_ci			unlock_buffer(c_bh);
3978c2ecf20Sopenharmony_ci			mark_buffer_dirty_inode(c_bh, sbi->fat_inode);
3988c2ecf20Sopenharmony_ci			if (sb->s_flags & SB_SYNCHRONOUS)
3998c2ecf20Sopenharmony_ci				err = sync_dirty_buffer(c_bh);
4008c2ecf20Sopenharmony_ci			brelse(c_bh);
4018c2ecf20Sopenharmony_ci			if (err)
4028c2ecf20Sopenharmony_ci				goto error;
4038c2ecf20Sopenharmony_ci		}
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_cierror:
4068c2ecf20Sopenharmony_ci	return err;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ciint fat_ent_write(struct inode *inode, struct fat_entry *fatent,
4108c2ecf20Sopenharmony_ci		  int new, int wait)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
4138c2ecf20Sopenharmony_ci	const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
4148c2ecf20Sopenharmony_ci	int err;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	ops->ent_put(fatent, new);
4178c2ecf20Sopenharmony_ci	if (wait) {
4188c2ecf20Sopenharmony_ci		err = fat_sync_bhs(fatent->bhs, fatent->nr_bhs);
4198c2ecf20Sopenharmony_ci		if (err)
4208c2ecf20Sopenharmony_ci			return err;
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci	return fat_mirror_bhs(sb, fatent->bhs, fatent->nr_bhs);
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic inline int fat_ent_next(struct msdos_sb_info *sbi,
4268c2ecf20Sopenharmony_ci			       struct fat_entry *fatent)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	if (sbi->fatent_ops->ent_next(fatent)) {
4298c2ecf20Sopenharmony_ci		if (fatent->entry < sbi->max_cluster)
4308c2ecf20Sopenharmony_ci			return 1;
4318c2ecf20Sopenharmony_ci	}
4328c2ecf20Sopenharmony_ci	return 0;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic inline int fat_ent_read_block(struct super_block *sb,
4368c2ecf20Sopenharmony_ci				     struct fat_entry *fatent)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
4398c2ecf20Sopenharmony_ci	sector_t blocknr;
4408c2ecf20Sopenharmony_ci	int offset;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	fatent_brelse(fatent);
4438c2ecf20Sopenharmony_ci	ops->ent_blocknr(sb, fatent->entry, &offset, &blocknr);
4448c2ecf20Sopenharmony_ci	return ops->ent_bread(sb, fatent, offset, blocknr);
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic void fat_collect_bhs(struct buffer_head **bhs, int *nr_bhs,
4488c2ecf20Sopenharmony_ci			    struct fat_entry *fatent)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	int n, i;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	for (n = 0; n < fatent->nr_bhs; n++) {
4538c2ecf20Sopenharmony_ci		for (i = 0; i < *nr_bhs; i++) {
4548c2ecf20Sopenharmony_ci			if (fatent->bhs[n] == bhs[i])
4558c2ecf20Sopenharmony_ci				break;
4568c2ecf20Sopenharmony_ci		}
4578c2ecf20Sopenharmony_ci		if (i == *nr_bhs) {
4588c2ecf20Sopenharmony_ci			get_bh(fatent->bhs[n]);
4598c2ecf20Sopenharmony_ci			bhs[i] = fatent->bhs[n];
4608c2ecf20Sopenharmony_ci			(*nr_bhs)++;
4618c2ecf20Sopenharmony_ci		}
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ciint fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
4688c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
4698c2ecf20Sopenharmony_ci	const struct fatent_operations *ops = sbi->fatent_ops;
4708c2ecf20Sopenharmony_ci	struct fat_entry fatent, prev_ent;
4718c2ecf20Sopenharmony_ci	struct buffer_head *bhs[MAX_BUF_PER_PAGE];
4728c2ecf20Sopenharmony_ci	int i, count, err, nr_bhs, idx_clus;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	BUG_ON(nr_cluster > (MAX_BUF_PER_PAGE / 2));	/* fixed limit */
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	lock_fat(sbi);
4778c2ecf20Sopenharmony_ci	if (sbi->free_clusters != -1 && sbi->free_clus_valid &&
4788c2ecf20Sopenharmony_ci	    sbi->free_clusters < nr_cluster) {
4798c2ecf20Sopenharmony_ci		unlock_fat(sbi);
4808c2ecf20Sopenharmony_ci		return -ENOSPC;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	err = nr_bhs = idx_clus = 0;
4848c2ecf20Sopenharmony_ci	count = FAT_START_ENT;
4858c2ecf20Sopenharmony_ci	fatent_init(&prev_ent);
4868c2ecf20Sopenharmony_ci	fatent_init(&fatent);
4878c2ecf20Sopenharmony_ci	fatent_set_entry(&fatent, sbi->prev_free + 1);
4888c2ecf20Sopenharmony_ci	while (count < sbi->max_cluster) {
4898c2ecf20Sopenharmony_ci		if (fatent.entry >= sbi->max_cluster)
4908c2ecf20Sopenharmony_ci			fatent.entry = FAT_START_ENT;
4918c2ecf20Sopenharmony_ci		fatent_set_entry(&fatent, fatent.entry);
4928c2ecf20Sopenharmony_ci		err = fat_ent_read_block(sb, &fatent);
4938c2ecf20Sopenharmony_ci		if (err)
4948c2ecf20Sopenharmony_ci			goto out;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci		/* Find the free entries in a block */
4978c2ecf20Sopenharmony_ci		do {
4988c2ecf20Sopenharmony_ci			if (ops->ent_get(&fatent) == FAT_ENT_FREE) {
4998c2ecf20Sopenharmony_ci				int entry = fatent.entry;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci				/* make the cluster chain */
5028c2ecf20Sopenharmony_ci				ops->ent_put(&fatent, FAT_ENT_EOF);
5038c2ecf20Sopenharmony_ci				if (prev_ent.nr_bhs)
5048c2ecf20Sopenharmony_ci					ops->ent_put(&prev_ent, entry);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci				fat_collect_bhs(bhs, &nr_bhs, &fatent);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci				sbi->prev_free = entry;
5098c2ecf20Sopenharmony_ci				if (sbi->free_clusters != -1)
5108c2ecf20Sopenharmony_ci					sbi->free_clusters--;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci				cluster[idx_clus] = entry;
5138c2ecf20Sopenharmony_ci				idx_clus++;
5148c2ecf20Sopenharmony_ci				if (idx_clus == nr_cluster)
5158c2ecf20Sopenharmony_ci					goto out;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci				/*
5188c2ecf20Sopenharmony_ci				 * fat_collect_bhs() gets ref-count of bhs,
5198c2ecf20Sopenharmony_ci				 * so we can still use the prev_ent.
5208c2ecf20Sopenharmony_ci				 */
5218c2ecf20Sopenharmony_ci				prev_ent = fatent;
5228c2ecf20Sopenharmony_ci			}
5238c2ecf20Sopenharmony_ci			count++;
5248c2ecf20Sopenharmony_ci			if (count == sbi->max_cluster)
5258c2ecf20Sopenharmony_ci				break;
5268c2ecf20Sopenharmony_ci		} while (fat_ent_next(sbi, &fatent));
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	/* Couldn't allocate the free entries */
5308c2ecf20Sopenharmony_ci	sbi->free_clusters = 0;
5318c2ecf20Sopenharmony_ci	sbi->free_clus_valid = 1;
5328c2ecf20Sopenharmony_ci	err = -ENOSPC;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ciout:
5358c2ecf20Sopenharmony_ci	unlock_fat(sbi);
5368c2ecf20Sopenharmony_ci	mark_fsinfo_dirty(sb);
5378c2ecf20Sopenharmony_ci	fatent_brelse(&fatent);
5388c2ecf20Sopenharmony_ci	if (!err) {
5398c2ecf20Sopenharmony_ci		if (inode_needs_sync(inode))
5408c2ecf20Sopenharmony_ci			err = fat_sync_bhs(bhs, nr_bhs);
5418c2ecf20Sopenharmony_ci		if (!err)
5428c2ecf20Sopenharmony_ci			err = fat_mirror_bhs(sb, bhs, nr_bhs);
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci	for (i = 0; i < nr_bhs; i++)
5458c2ecf20Sopenharmony_ci		brelse(bhs[i]);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	if (err && idx_clus)
5488c2ecf20Sopenharmony_ci		fat_free_clusters(inode, cluster[0]);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	return err;
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ciint fat_free_clusters(struct inode *inode, int cluster)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
5568c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
5578c2ecf20Sopenharmony_ci	const struct fatent_operations *ops = sbi->fatent_ops;
5588c2ecf20Sopenharmony_ci	struct fat_entry fatent;
5598c2ecf20Sopenharmony_ci	struct buffer_head *bhs[MAX_BUF_PER_PAGE];
5608c2ecf20Sopenharmony_ci	int i, err, nr_bhs;
5618c2ecf20Sopenharmony_ci	int first_cl = cluster, dirty_fsinfo = 0;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	nr_bhs = 0;
5648c2ecf20Sopenharmony_ci	fatent_init(&fatent);
5658c2ecf20Sopenharmony_ci	lock_fat(sbi);
5668c2ecf20Sopenharmony_ci	do {
5678c2ecf20Sopenharmony_ci		cluster = fat_ent_read(inode, &fatent, cluster);
5688c2ecf20Sopenharmony_ci		if (cluster < 0) {
5698c2ecf20Sopenharmony_ci			err = cluster;
5708c2ecf20Sopenharmony_ci			goto error;
5718c2ecf20Sopenharmony_ci		} else if (cluster == FAT_ENT_FREE) {
5728c2ecf20Sopenharmony_ci			fat_fs_error(sb, "%s: deleting FAT entry beyond EOF",
5738c2ecf20Sopenharmony_ci				     __func__);
5748c2ecf20Sopenharmony_ci			err = -EIO;
5758c2ecf20Sopenharmony_ci			goto error;
5768c2ecf20Sopenharmony_ci		}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci		if (sbi->options.discard) {
5798c2ecf20Sopenharmony_ci			/*
5808c2ecf20Sopenharmony_ci			 * Issue discard for the sectors we no longer
5818c2ecf20Sopenharmony_ci			 * care about, batching contiguous clusters
5828c2ecf20Sopenharmony_ci			 * into one request
5838c2ecf20Sopenharmony_ci			 */
5848c2ecf20Sopenharmony_ci			if (cluster != fatent.entry + 1) {
5858c2ecf20Sopenharmony_ci				int nr_clus = fatent.entry - first_cl + 1;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci				sb_issue_discard(sb,
5888c2ecf20Sopenharmony_ci					fat_clus_to_blknr(sbi, first_cl),
5898c2ecf20Sopenharmony_ci					nr_clus * sbi->sec_per_clus,
5908c2ecf20Sopenharmony_ci					GFP_NOFS, 0);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci				first_cl = cluster;
5938c2ecf20Sopenharmony_ci			}
5948c2ecf20Sopenharmony_ci		}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci		ops->ent_put(&fatent, FAT_ENT_FREE);
5978c2ecf20Sopenharmony_ci		if (sbi->free_clusters != -1) {
5988c2ecf20Sopenharmony_ci			sbi->free_clusters++;
5998c2ecf20Sopenharmony_ci			dirty_fsinfo = 1;
6008c2ecf20Sopenharmony_ci		}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci		if (nr_bhs + fatent.nr_bhs > MAX_BUF_PER_PAGE) {
6038c2ecf20Sopenharmony_ci			if (sb->s_flags & SB_SYNCHRONOUS) {
6048c2ecf20Sopenharmony_ci				err = fat_sync_bhs(bhs, nr_bhs);
6058c2ecf20Sopenharmony_ci				if (err)
6068c2ecf20Sopenharmony_ci					goto error;
6078c2ecf20Sopenharmony_ci			}
6088c2ecf20Sopenharmony_ci			err = fat_mirror_bhs(sb, bhs, nr_bhs);
6098c2ecf20Sopenharmony_ci			if (err)
6108c2ecf20Sopenharmony_ci				goto error;
6118c2ecf20Sopenharmony_ci			for (i = 0; i < nr_bhs; i++)
6128c2ecf20Sopenharmony_ci				brelse(bhs[i]);
6138c2ecf20Sopenharmony_ci			nr_bhs = 0;
6148c2ecf20Sopenharmony_ci		}
6158c2ecf20Sopenharmony_ci		fat_collect_bhs(bhs, &nr_bhs, &fatent);
6168c2ecf20Sopenharmony_ci	} while (cluster != FAT_ENT_EOF);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	if (sb->s_flags & SB_SYNCHRONOUS) {
6198c2ecf20Sopenharmony_ci		err = fat_sync_bhs(bhs, nr_bhs);
6208c2ecf20Sopenharmony_ci		if (err)
6218c2ecf20Sopenharmony_ci			goto error;
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci	err = fat_mirror_bhs(sb, bhs, nr_bhs);
6248c2ecf20Sopenharmony_cierror:
6258c2ecf20Sopenharmony_ci	fatent_brelse(&fatent);
6268c2ecf20Sopenharmony_ci	for (i = 0; i < nr_bhs; i++)
6278c2ecf20Sopenharmony_ci		brelse(bhs[i]);
6288c2ecf20Sopenharmony_ci	unlock_fat(sbi);
6298c2ecf20Sopenharmony_ci	if (dirty_fsinfo)
6308c2ecf20Sopenharmony_ci		mark_fsinfo_dirty(sb);
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	return err;
6338c2ecf20Sopenharmony_ci}
6348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_free_clusters);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistruct fatent_ra {
6378c2ecf20Sopenharmony_ci	sector_t cur;
6388c2ecf20Sopenharmony_ci	sector_t limit;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	unsigned int ra_blocks;
6418c2ecf20Sopenharmony_ci	sector_t ra_advance;
6428c2ecf20Sopenharmony_ci	sector_t ra_next;
6438c2ecf20Sopenharmony_ci	sector_t ra_limit;
6448c2ecf20Sopenharmony_ci};
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic void fat_ra_init(struct super_block *sb, struct fatent_ra *ra,
6478c2ecf20Sopenharmony_ci			struct fat_entry *fatent, int ent_limit)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
6508c2ecf20Sopenharmony_ci	const struct fatent_operations *ops = sbi->fatent_ops;
6518c2ecf20Sopenharmony_ci	sector_t blocknr, block_end;
6528c2ecf20Sopenharmony_ci	int offset;
6538c2ecf20Sopenharmony_ci	/*
6548c2ecf20Sopenharmony_ci	 * This is the sequential read, so ra_pages * 2 (but try to
6558c2ecf20Sopenharmony_ci	 * align the optimal hardware IO size).
6568c2ecf20Sopenharmony_ci	 * [BTW, 128kb covers the whole sectors for FAT12 and FAT16]
6578c2ecf20Sopenharmony_ci	 */
6588c2ecf20Sopenharmony_ci	unsigned long ra_pages = sb->s_bdi->ra_pages;
6598c2ecf20Sopenharmony_ci	unsigned int reada_blocks;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	if (fatent->entry >= ent_limit)
6628c2ecf20Sopenharmony_ci		return;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	if (ra_pages > sb->s_bdi->io_pages)
6658c2ecf20Sopenharmony_ci		ra_pages = rounddown(ra_pages, sb->s_bdi->io_pages);
6668c2ecf20Sopenharmony_ci	reada_blocks = ra_pages << (PAGE_SHIFT - sb->s_blocksize_bits + 1);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/* Initialize the range for sequential read */
6698c2ecf20Sopenharmony_ci	ops->ent_blocknr(sb, fatent->entry, &offset, &blocknr);
6708c2ecf20Sopenharmony_ci	ops->ent_blocknr(sb, ent_limit - 1, &offset, &block_end);
6718c2ecf20Sopenharmony_ci	ra->cur = 0;
6728c2ecf20Sopenharmony_ci	ra->limit = (block_end + 1) - blocknr;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	/* Advancing the window at half size */
6758c2ecf20Sopenharmony_ci	ra->ra_blocks = reada_blocks >> 1;
6768c2ecf20Sopenharmony_ci	ra->ra_advance = ra->cur;
6778c2ecf20Sopenharmony_ci	ra->ra_next = ra->cur;
6788c2ecf20Sopenharmony_ci	ra->ra_limit = ra->cur + min_t(sector_t, reada_blocks, ra->limit);
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci/* Assuming to be called before reading a new block (increments ->cur). */
6828c2ecf20Sopenharmony_cistatic void fat_ent_reada(struct super_block *sb, struct fatent_ra *ra,
6838c2ecf20Sopenharmony_ci			  struct fat_entry *fatent)
6848c2ecf20Sopenharmony_ci{
6858c2ecf20Sopenharmony_ci	if (ra->ra_next >= ra->ra_limit)
6868c2ecf20Sopenharmony_ci		return;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	if (ra->cur >= ra->ra_advance) {
6898c2ecf20Sopenharmony_ci		struct msdos_sb_info *sbi = MSDOS_SB(sb);
6908c2ecf20Sopenharmony_ci		const struct fatent_operations *ops = sbi->fatent_ops;
6918c2ecf20Sopenharmony_ci		struct blk_plug plug;
6928c2ecf20Sopenharmony_ci		sector_t blocknr, diff;
6938c2ecf20Sopenharmony_ci		int offset;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci		ops->ent_blocknr(sb, fatent->entry, &offset, &blocknr);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci		diff = blocknr - ra->cur;
6988c2ecf20Sopenharmony_ci		blk_start_plug(&plug);
6998c2ecf20Sopenharmony_ci		/*
7008c2ecf20Sopenharmony_ci		 * FIXME: we would want to directly use the bio with
7018c2ecf20Sopenharmony_ci		 * pages to reduce the number of segments.
7028c2ecf20Sopenharmony_ci		 */
7038c2ecf20Sopenharmony_ci		for (; ra->ra_next < ra->ra_limit; ra->ra_next++)
7048c2ecf20Sopenharmony_ci			sb_breadahead(sb, ra->ra_next + diff);
7058c2ecf20Sopenharmony_ci		blk_finish_plug(&plug);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci		/* Advance the readahead window */
7088c2ecf20Sopenharmony_ci		ra->ra_advance += ra->ra_blocks;
7098c2ecf20Sopenharmony_ci		ra->ra_limit += min_t(sector_t,
7108c2ecf20Sopenharmony_ci				      ra->ra_blocks, ra->limit - ra->ra_limit);
7118c2ecf20Sopenharmony_ci	}
7128c2ecf20Sopenharmony_ci	ra->cur++;
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ciint fat_count_free_clusters(struct super_block *sb)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
7188c2ecf20Sopenharmony_ci	const struct fatent_operations *ops = sbi->fatent_ops;
7198c2ecf20Sopenharmony_ci	struct fat_entry fatent;
7208c2ecf20Sopenharmony_ci	struct fatent_ra fatent_ra;
7218c2ecf20Sopenharmony_ci	int err = 0, free;
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	lock_fat(sbi);
7248c2ecf20Sopenharmony_ci	if (sbi->free_clusters != -1 && sbi->free_clus_valid)
7258c2ecf20Sopenharmony_ci		goto out;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	free = 0;
7288c2ecf20Sopenharmony_ci	fatent_init(&fatent);
7298c2ecf20Sopenharmony_ci	fatent_set_entry(&fatent, FAT_START_ENT);
7308c2ecf20Sopenharmony_ci	fat_ra_init(sb, &fatent_ra, &fatent, sbi->max_cluster);
7318c2ecf20Sopenharmony_ci	while (fatent.entry < sbi->max_cluster) {
7328c2ecf20Sopenharmony_ci		/* readahead of fat blocks */
7338c2ecf20Sopenharmony_ci		fat_ent_reada(sb, &fatent_ra, &fatent);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci		err = fat_ent_read_block(sb, &fatent);
7368c2ecf20Sopenharmony_ci		if (err)
7378c2ecf20Sopenharmony_ci			goto out;
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci		do {
7408c2ecf20Sopenharmony_ci			if (ops->ent_get(&fatent) == FAT_ENT_FREE)
7418c2ecf20Sopenharmony_ci				free++;
7428c2ecf20Sopenharmony_ci		} while (fat_ent_next(sbi, &fatent));
7438c2ecf20Sopenharmony_ci		cond_resched();
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci	sbi->free_clusters = free;
7468c2ecf20Sopenharmony_ci	sbi->free_clus_valid = 1;
7478c2ecf20Sopenharmony_ci	mark_fsinfo_dirty(sb);
7488c2ecf20Sopenharmony_ci	fatent_brelse(&fatent);
7498c2ecf20Sopenharmony_ciout:
7508c2ecf20Sopenharmony_ci	unlock_fat(sbi);
7518c2ecf20Sopenharmony_ci	return err;
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic int fat_trim_clusters(struct super_block *sb, u32 clus, u32 nr_clus)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
7578c2ecf20Sopenharmony_ci	return sb_issue_discard(sb, fat_clus_to_blknr(sbi, clus),
7588c2ecf20Sopenharmony_ci				nr_clus * sbi->sec_per_clus, GFP_NOFS, 0);
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ciint fat_trim_fs(struct inode *inode, struct fstrim_range *range)
7628c2ecf20Sopenharmony_ci{
7638c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
7648c2ecf20Sopenharmony_ci	struct msdos_sb_info *sbi = MSDOS_SB(sb);
7658c2ecf20Sopenharmony_ci	const struct fatent_operations *ops = sbi->fatent_ops;
7668c2ecf20Sopenharmony_ci	struct fat_entry fatent;
7678c2ecf20Sopenharmony_ci	struct fatent_ra fatent_ra;
7688c2ecf20Sopenharmony_ci	u64 ent_start, ent_end, minlen, trimmed = 0;
7698c2ecf20Sopenharmony_ci	u32 free = 0;
7708c2ecf20Sopenharmony_ci	int err = 0;
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	/*
7738c2ecf20Sopenharmony_ci	 * FAT data is organized as clusters, trim at the granulary of cluster.
7748c2ecf20Sopenharmony_ci	 *
7758c2ecf20Sopenharmony_ci	 * fstrim_range is in byte, convert vaules to cluster index.
7768c2ecf20Sopenharmony_ci	 * Treat sectors before data region as all used, not to trim them.
7778c2ecf20Sopenharmony_ci	 */
7788c2ecf20Sopenharmony_ci	ent_start = max_t(u64, range->start>>sbi->cluster_bits, FAT_START_ENT);
7798c2ecf20Sopenharmony_ci	ent_end = ent_start + (range->len >> sbi->cluster_bits) - 1;
7808c2ecf20Sopenharmony_ci	minlen = range->minlen >> sbi->cluster_bits;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (ent_start >= sbi->max_cluster || range->len < sbi->cluster_size)
7838c2ecf20Sopenharmony_ci		return -EINVAL;
7848c2ecf20Sopenharmony_ci	if (ent_end >= sbi->max_cluster)
7858c2ecf20Sopenharmony_ci		ent_end = sbi->max_cluster - 1;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	fatent_init(&fatent);
7888c2ecf20Sopenharmony_ci	lock_fat(sbi);
7898c2ecf20Sopenharmony_ci	fatent_set_entry(&fatent, ent_start);
7908c2ecf20Sopenharmony_ci	fat_ra_init(sb, &fatent_ra, &fatent, ent_end + 1);
7918c2ecf20Sopenharmony_ci	while (fatent.entry <= ent_end) {
7928c2ecf20Sopenharmony_ci		/* readahead of fat blocks */
7938c2ecf20Sopenharmony_ci		fat_ent_reada(sb, &fatent_ra, &fatent);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci		err = fat_ent_read_block(sb, &fatent);
7968c2ecf20Sopenharmony_ci		if (err)
7978c2ecf20Sopenharmony_ci			goto error;
7988c2ecf20Sopenharmony_ci		do {
7998c2ecf20Sopenharmony_ci			if (ops->ent_get(&fatent) == FAT_ENT_FREE) {
8008c2ecf20Sopenharmony_ci				free++;
8018c2ecf20Sopenharmony_ci			} else if (free) {
8028c2ecf20Sopenharmony_ci				if (free >= minlen) {
8038c2ecf20Sopenharmony_ci					u32 clus = fatent.entry - free;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci					err = fat_trim_clusters(sb, clus, free);
8068c2ecf20Sopenharmony_ci					if (err && err != -EOPNOTSUPP)
8078c2ecf20Sopenharmony_ci						goto error;
8088c2ecf20Sopenharmony_ci					if (!err)
8098c2ecf20Sopenharmony_ci						trimmed += free;
8108c2ecf20Sopenharmony_ci					err = 0;
8118c2ecf20Sopenharmony_ci				}
8128c2ecf20Sopenharmony_ci				free = 0;
8138c2ecf20Sopenharmony_ci			}
8148c2ecf20Sopenharmony_ci		} while (fat_ent_next(sbi, &fatent) && fatent.entry <= ent_end);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci		if (fatal_signal_pending(current)) {
8178c2ecf20Sopenharmony_ci			err = -ERESTARTSYS;
8188c2ecf20Sopenharmony_ci			goto error;
8198c2ecf20Sopenharmony_ci		}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci		if (need_resched()) {
8228c2ecf20Sopenharmony_ci			fatent_brelse(&fatent);
8238c2ecf20Sopenharmony_ci			unlock_fat(sbi);
8248c2ecf20Sopenharmony_ci			cond_resched();
8258c2ecf20Sopenharmony_ci			lock_fat(sbi);
8268c2ecf20Sopenharmony_ci		}
8278c2ecf20Sopenharmony_ci	}
8288c2ecf20Sopenharmony_ci	/* handle scenario when tail entries are all free */
8298c2ecf20Sopenharmony_ci	if (free && free >= minlen) {
8308c2ecf20Sopenharmony_ci		u32 clus = fatent.entry - free;
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci		err = fat_trim_clusters(sb, clus, free);
8338c2ecf20Sopenharmony_ci		if (err && err != -EOPNOTSUPP)
8348c2ecf20Sopenharmony_ci			goto error;
8358c2ecf20Sopenharmony_ci		if (!err)
8368c2ecf20Sopenharmony_ci			trimmed += free;
8378c2ecf20Sopenharmony_ci		err = 0;
8388c2ecf20Sopenharmony_ci	}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_cierror:
8418c2ecf20Sopenharmony_ci	fatent_brelse(&fatent);
8428c2ecf20Sopenharmony_ci	unlock_fat(sbi);
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	range->len = trimmed << sbi->cluster_bits;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	return err;
8478c2ecf20Sopenharmony_ci}
848