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