162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2004, OGAWA Hirofumi 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/blkdev.h> 762306a36Sopenharmony_ci#include <linux/sched/signal.h> 862306a36Sopenharmony_ci#include <linux/backing-dev-defs.h> 962306a36Sopenharmony_ci#include "fat.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistruct fatent_operations { 1262306a36Sopenharmony_ci void (*ent_blocknr)(struct super_block *, int, int *, sector_t *); 1362306a36Sopenharmony_ci void (*ent_set_ptr)(struct fat_entry *, int); 1462306a36Sopenharmony_ci int (*ent_bread)(struct super_block *, struct fat_entry *, 1562306a36Sopenharmony_ci int, sector_t); 1662306a36Sopenharmony_ci int (*ent_get)(struct fat_entry *); 1762306a36Sopenharmony_ci void (*ent_put)(struct fat_entry *, int); 1862306a36Sopenharmony_ci int (*ent_next)(struct fat_entry *); 1962306a36Sopenharmony_ci}; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(fat12_entry_lock); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void fat12_ent_blocknr(struct super_block *sb, int entry, 2462306a36Sopenharmony_ci int *offset, sector_t *blocknr) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 2762306a36Sopenharmony_ci int bytes = entry + (entry >> 1); 2862306a36Sopenharmony_ci WARN_ON(!fat_valid_entry(sbi, entry)); 2962306a36Sopenharmony_ci *offset = bytes & (sb->s_blocksize - 1); 3062306a36Sopenharmony_ci *blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits); 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic void fat_ent_blocknr(struct super_block *sb, int entry, 3462306a36Sopenharmony_ci int *offset, sector_t *blocknr) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 3762306a36Sopenharmony_ci int bytes = (entry << sbi->fatent_shift); 3862306a36Sopenharmony_ci WARN_ON(!fat_valid_entry(sbi, entry)); 3962306a36Sopenharmony_ci *offset = bytes & (sb->s_blocksize - 1); 4062306a36Sopenharmony_ci *blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void fat12_ent_set_ptr(struct fat_entry *fatent, int offset) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct buffer_head **bhs = fatent->bhs; 4662306a36Sopenharmony_ci if (fatent->nr_bhs == 1) { 4762306a36Sopenharmony_ci WARN_ON(offset >= (bhs[0]->b_size - 1)); 4862306a36Sopenharmony_ci fatent->u.ent12_p[0] = bhs[0]->b_data + offset; 4962306a36Sopenharmony_ci fatent->u.ent12_p[1] = bhs[0]->b_data + (offset + 1); 5062306a36Sopenharmony_ci } else { 5162306a36Sopenharmony_ci WARN_ON(offset != (bhs[0]->b_size - 1)); 5262306a36Sopenharmony_ci fatent->u.ent12_p[0] = bhs[0]->b_data + offset; 5362306a36Sopenharmony_ci fatent->u.ent12_p[1] = bhs[1]->b_data; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void fat16_ent_set_ptr(struct fat_entry *fatent, int offset) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci WARN_ON(offset & (2 - 1)); 6062306a36Sopenharmony_ci fatent->u.ent16_p = (__le16 *)(fatent->bhs[0]->b_data + offset); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void fat32_ent_set_ptr(struct fat_entry *fatent, int offset) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci WARN_ON(offset & (4 - 1)); 6662306a36Sopenharmony_ci fatent->u.ent32_p = (__le32 *)(fatent->bhs[0]->b_data + offset); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent, 7062306a36Sopenharmony_ci int offset, sector_t blocknr) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct buffer_head **bhs = fatent->bhs; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci WARN_ON(blocknr < MSDOS_SB(sb)->fat_start); 7562306a36Sopenharmony_ci fatent->fat_inode = MSDOS_SB(sb)->fat_inode; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci bhs[0] = sb_bread(sb, blocknr); 7862306a36Sopenharmony_ci if (!bhs[0]) 7962306a36Sopenharmony_ci goto err; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if ((offset + 1) < sb->s_blocksize) 8262306a36Sopenharmony_ci fatent->nr_bhs = 1; 8362306a36Sopenharmony_ci else { 8462306a36Sopenharmony_ci /* This entry is block boundary, it needs the next block */ 8562306a36Sopenharmony_ci blocknr++; 8662306a36Sopenharmony_ci bhs[1] = sb_bread(sb, blocknr); 8762306a36Sopenharmony_ci if (!bhs[1]) 8862306a36Sopenharmony_ci goto err_brelse; 8962306a36Sopenharmony_ci fatent->nr_bhs = 2; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci fat12_ent_set_ptr(fatent, offset); 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cierr_brelse: 9562306a36Sopenharmony_ci brelse(bhs[0]); 9662306a36Sopenharmony_cierr: 9762306a36Sopenharmony_ci fat_msg_ratelimit(sb, KERN_ERR, "FAT read failed (blocknr %llu)", 9862306a36Sopenharmony_ci (llu)blocknr); 9962306a36Sopenharmony_ci return -EIO; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent, 10362306a36Sopenharmony_ci int offset, sector_t blocknr) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci WARN_ON(blocknr < MSDOS_SB(sb)->fat_start); 10862306a36Sopenharmony_ci fatent->fat_inode = MSDOS_SB(sb)->fat_inode; 10962306a36Sopenharmony_ci fatent->bhs[0] = sb_bread(sb, blocknr); 11062306a36Sopenharmony_ci if (!fatent->bhs[0]) { 11162306a36Sopenharmony_ci fat_msg_ratelimit(sb, KERN_ERR, "FAT read failed (blocknr %llu)", 11262306a36Sopenharmony_ci (llu)blocknr); 11362306a36Sopenharmony_ci return -EIO; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci fatent->nr_bhs = 1; 11662306a36Sopenharmony_ci ops->ent_set_ptr(fatent, offset); 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic int fat12_ent_get(struct fat_entry *fatent) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci u8 **ent12_p = fatent->u.ent12_p; 12362306a36Sopenharmony_ci int next; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci spin_lock(&fat12_entry_lock); 12662306a36Sopenharmony_ci if (fatent->entry & 1) 12762306a36Sopenharmony_ci next = (*ent12_p[0] >> 4) | (*ent12_p[1] << 4); 12862306a36Sopenharmony_ci else 12962306a36Sopenharmony_ci next = (*ent12_p[1] << 8) | *ent12_p[0]; 13062306a36Sopenharmony_ci spin_unlock(&fat12_entry_lock); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci next &= 0x0fff; 13362306a36Sopenharmony_ci if (next >= BAD_FAT12) 13462306a36Sopenharmony_ci next = FAT_ENT_EOF; 13562306a36Sopenharmony_ci return next; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int fat16_ent_get(struct fat_entry *fatent) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci int next = le16_to_cpu(*fatent->u.ent16_p); 14162306a36Sopenharmony_ci WARN_ON((unsigned long)fatent->u.ent16_p & (2 - 1)); 14262306a36Sopenharmony_ci if (next >= BAD_FAT16) 14362306a36Sopenharmony_ci next = FAT_ENT_EOF; 14462306a36Sopenharmony_ci return next; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int fat32_ent_get(struct fat_entry *fatent) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci int next = le32_to_cpu(*fatent->u.ent32_p) & 0x0fffffff; 15062306a36Sopenharmony_ci WARN_ON((unsigned long)fatent->u.ent32_p & (4 - 1)); 15162306a36Sopenharmony_ci if (next >= BAD_FAT32) 15262306a36Sopenharmony_ci next = FAT_ENT_EOF; 15362306a36Sopenharmony_ci return next; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic void fat12_ent_put(struct fat_entry *fatent, int new) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci u8 **ent12_p = fatent->u.ent12_p; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (new == FAT_ENT_EOF) 16162306a36Sopenharmony_ci new = EOF_FAT12; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci spin_lock(&fat12_entry_lock); 16462306a36Sopenharmony_ci if (fatent->entry & 1) { 16562306a36Sopenharmony_ci *ent12_p[0] = (new << 4) | (*ent12_p[0] & 0x0f); 16662306a36Sopenharmony_ci *ent12_p[1] = new >> 4; 16762306a36Sopenharmony_ci } else { 16862306a36Sopenharmony_ci *ent12_p[0] = new & 0xff; 16962306a36Sopenharmony_ci *ent12_p[1] = (*ent12_p[1] & 0xf0) | (new >> 8); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci spin_unlock(&fat12_entry_lock); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode); 17462306a36Sopenharmony_ci if (fatent->nr_bhs == 2) 17562306a36Sopenharmony_ci mark_buffer_dirty_inode(fatent->bhs[1], fatent->fat_inode); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void fat16_ent_put(struct fat_entry *fatent, int new) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci if (new == FAT_ENT_EOF) 18162306a36Sopenharmony_ci new = EOF_FAT16; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci *fatent->u.ent16_p = cpu_to_le16(new); 18462306a36Sopenharmony_ci mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void fat32_ent_put(struct fat_entry *fatent, int new) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci WARN_ON(new & 0xf0000000); 19062306a36Sopenharmony_ci new |= le32_to_cpu(*fatent->u.ent32_p) & ~0x0fffffff; 19162306a36Sopenharmony_ci *fatent->u.ent32_p = cpu_to_le32(new); 19262306a36Sopenharmony_ci mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int fat12_ent_next(struct fat_entry *fatent) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci u8 **ent12_p = fatent->u.ent12_p; 19862306a36Sopenharmony_ci struct buffer_head **bhs = fatent->bhs; 19962306a36Sopenharmony_ci u8 *nextp = ent12_p[1] + 1 + (fatent->entry & 1); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci fatent->entry++; 20262306a36Sopenharmony_ci if (fatent->nr_bhs == 1) { 20362306a36Sopenharmony_ci WARN_ON(ent12_p[0] > (u8 *)(bhs[0]->b_data + 20462306a36Sopenharmony_ci (bhs[0]->b_size - 2))); 20562306a36Sopenharmony_ci WARN_ON(ent12_p[1] > (u8 *)(bhs[0]->b_data + 20662306a36Sopenharmony_ci (bhs[0]->b_size - 1))); 20762306a36Sopenharmony_ci if (nextp < (u8 *)(bhs[0]->b_data + (bhs[0]->b_size - 1))) { 20862306a36Sopenharmony_ci ent12_p[0] = nextp - 1; 20962306a36Sopenharmony_ci ent12_p[1] = nextp; 21062306a36Sopenharmony_ci return 1; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci } else { 21362306a36Sopenharmony_ci WARN_ON(ent12_p[0] != (u8 *)(bhs[0]->b_data + 21462306a36Sopenharmony_ci (bhs[0]->b_size - 1))); 21562306a36Sopenharmony_ci WARN_ON(ent12_p[1] != (u8 *)bhs[1]->b_data); 21662306a36Sopenharmony_ci ent12_p[0] = nextp - 1; 21762306a36Sopenharmony_ci ent12_p[1] = nextp; 21862306a36Sopenharmony_ci brelse(bhs[0]); 21962306a36Sopenharmony_ci bhs[0] = bhs[1]; 22062306a36Sopenharmony_ci fatent->nr_bhs = 1; 22162306a36Sopenharmony_ci return 1; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci ent12_p[0] = NULL; 22462306a36Sopenharmony_ci ent12_p[1] = NULL; 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int fat16_ent_next(struct fat_entry *fatent) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci const struct buffer_head *bh = fatent->bhs[0]; 23162306a36Sopenharmony_ci fatent->entry++; 23262306a36Sopenharmony_ci if (fatent->u.ent16_p < (__le16 *)(bh->b_data + (bh->b_size - 2))) { 23362306a36Sopenharmony_ci fatent->u.ent16_p++; 23462306a36Sopenharmony_ci return 1; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci fatent->u.ent16_p = NULL; 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic int fat32_ent_next(struct fat_entry *fatent) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci const struct buffer_head *bh = fatent->bhs[0]; 24362306a36Sopenharmony_ci fatent->entry++; 24462306a36Sopenharmony_ci if (fatent->u.ent32_p < (__le32 *)(bh->b_data + (bh->b_size - 4))) { 24562306a36Sopenharmony_ci fatent->u.ent32_p++; 24662306a36Sopenharmony_ci return 1; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci fatent->u.ent32_p = NULL; 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic const struct fatent_operations fat12_ops = { 25362306a36Sopenharmony_ci .ent_blocknr = fat12_ent_blocknr, 25462306a36Sopenharmony_ci .ent_set_ptr = fat12_ent_set_ptr, 25562306a36Sopenharmony_ci .ent_bread = fat12_ent_bread, 25662306a36Sopenharmony_ci .ent_get = fat12_ent_get, 25762306a36Sopenharmony_ci .ent_put = fat12_ent_put, 25862306a36Sopenharmony_ci .ent_next = fat12_ent_next, 25962306a36Sopenharmony_ci}; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic const struct fatent_operations fat16_ops = { 26262306a36Sopenharmony_ci .ent_blocknr = fat_ent_blocknr, 26362306a36Sopenharmony_ci .ent_set_ptr = fat16_ent_set_ptr, 26462306a36Sopenharmony_ci .ent_bread = fat_ent_bread, 26562306a36Sopenharmony_ci .ent_get = fat16_ent_get, 26662306a36Sopenharmony_ci .ent_put = fat16_ent_put, 26762306a36Sopenharmony_ci .ent_next = fat16_ent_next, 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic const struct fatent_operations fat32_ops = { 27162306a36Sopenharmony_ci .ent_blocknr = fat_ent_blocknr, 27262306a36Sopenharmony_ci .ent_set_ptr = fat32_ent_set_ptr, 27362306a36Sopenharmony_ci .ent_bread = fat_ent_bread, 27462306a36Sopenharmony_ci .ent_get = fat32_ent_get, 27562306a36Sopenharmony_ci .ent_put = fat32_ent_put, 27662306a36Sopenharmony_ci .ent_next = fat32_ent_next, 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic inline void lock_fat(struct msdos_sb_info *sbi) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci mutex_lock(&sbi->fat_lock); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic inline void unlock_fat(struct msdos_sb_info *sbi) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci mutex_unlock(&sbi->fat_lock); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_civoid fat_ent_access_init(struct super_block *sb) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci mutex_init(&sbi->fat_lock); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (is_fat32(sbi)) { 29662306a36Sopenharmony_ci sbi->fatent_shift = 2; 29762306a36Sopenharmony_ci sbi->fatent_ops = &fat32_ops; 29862306a36Sopenharmony_ci } else if (is_fat16(sbi)) { 29962306a36Sopenharmony_ci sbi->fatent_shift = 1; 30062306a36Sopenharmony_ci sbi->fatent_ops = &fat16_ops; 30162306a36Sopenharmony_ci } else if (is_fat12(sbi)) { 30262306a36Sopenharmony_ci sbi->fatent_shift = -1; 30362306a36Sopenharmony_ci sbi->fatent_ops = &fat12_ops; 30462306a36Sopenharmony_ci } else { 30562306a36Sopenharmony_ci fat_fs_error(sb, "invalid FAT variant, %u bits", sbi->fat_bits); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void mark_fsinfo_dirty(struct super_block *sb) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (sb_rdonly(sb) || !is_fat32(sbi)) 31462306a36Sopenharmony_ci return; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci __mark_inode_dirty(sbi->fsinfo_inode, I_DIRTY_SYNC); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic inline int fat_ent_update_ptr(struct super_block *sb, 32062306a36Sopenharmony_ci struct fat_entry *fatent, 32162306a36Sopenharmony_ci int offset, sector_t blocknr) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 32462306a36Sopenharmony_ci const struct fatent_operations *ops = sbi->fatent_ops; 32562306a36Sopenharmony_ci struct buffer_head **bhs = fatent->bhs; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* Is this fatent's blocks including this entry? */ 32862306a36Sopenharmony_ci if (!fatent->nr_bhs || bhs[0]->b_blocknr != blocknr) 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci if (is_fat12(sbi)) { 33162306a36Sopenharmony_ci if ((offset + 1) < sb->s_blocksize) { 33262306a36Sopenharmony_ci /* This entry is on bhs[0]. */ 33362306a36Sopenharmony_ci if (fatent->nr_bhs == 2) { 33462306a36Sopenharmony_ci brelse(bhs[1]); 33562306a36Sopenharmony_ci fatent->nr_bhs = 1; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci } else { 33862306a36Sopenharmony_ci /* This entry needs the next block. */ 33962306a36Sopenharmony_ci if (fatent->nr_bhs != 2) 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci if (bhs[1]->b_blocknr != (blocknr + 1)) 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci ops->ent_set_ptr(fatent, offset); 34662306a36Sopenharmony_ci return 1; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ciint fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 35262306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); 35362306a36Sopenharmony_ci const struct fatent_operations *ops = sbi->fatent_ops; 35462306a36Sopenharmony_ci int err, offset; 35562306a36Sopenharmony_ci sector_t blocknr; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (!fat_valid_entry(sbi, entry)) { 35862306a36Sopenharmony_ci fatent_brelse(fatent); 35962306a36Sopenharmony_ci fat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", entry); 36062306a36Sopenharmony_ci return -EIO; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci fatent_set_entry(fatent, entry); 36462306a36Sopenharmony_ci ops->ent_blocknr(sb, entry, &offset, &blocknr); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (!fat_ent_update_ptr(sb, fatent, offset, blocknr)) { 36762306a36Sopenharmony_ci fatent_brelse(fatent); 36862306a36Sopenharmony_ci err = ops->ent_bread(sb, fatent, offset, blocknr); 36962306a36Sopenharmony_ci if (err) 37062306a36Sopenharmony_ci return err; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci return ops->ent_get(fatent); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* FIXME: We can write the blocks as more big chunk. */ 37662306a36Sopenharmony_cistatic int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs, 37762306a36Sopenharmony_ci int nr_bhs) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 38062306a36Sopenharmony_ci struct buffer_head *c_bh; 38162306a36Sopenharmony_ci int err, n, copy; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci err = 0; 38462306a36Sopenharmony_ci for (copy = 1; copy < sbi->fats; copy++) { 38562306a36Sopenharmony_ci sector_t backup_fat = sbi->fat_length * copy; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci for (n = 0; n < nr_bhs; n++) { 38862306a36Sopenharmony_ci c_bh = sb_getblk(sb, backup_fat + bhs[n]->b_blocknr); 38962306a36Sopenharmony_ci if (!c_bh) { 39062306a36Sopenharmony_ci err = -ENOMEM; 39162306a36Sopenharmony_ci goto error; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci /* Avoid race with userspace read via bdev */ 39462306a36Sopenharmony_ci lock_buffer(c_bh); 39562306a36Sopenharmony_ci memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize); 39662306a36Sopenharmony_ci set_buffer_uptodate(c_bh); 39762306a36Sopenharmony_ci unlock_buffer(c_bh); 39862306a36Sopenharmony_ci mark_buffer_dirty_inode(c_bh, sbi->fat_inode); 39962306a36Sopenharmony_ci if (sb->s_flags & SB_SYNCHRONOUS) 40062306a36Sopenharmony_ci err = sync_dirty_buffer(c_bh); 40162306a36Sopenharmony_ci brelse(c_bh); 40262306a36Sopenharmony_ci if (err) 40362306a36Sopenharmony_ci goto error; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_cierror: 40762306a36Sopenharmony_ci return err; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ciint fat_ent_write(struct inode *inode, struct fat_entry *fatent, 41162306a36Sopenharmony_ci int new, int wait) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 41462306a36Sopenharmony_ci const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; 41562306a36Sopenharmony_ci int err; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci ops->ent_put(fatent, new); 41862306a36Sopenharmony_ci if (wait) { 41962306a36Sopenharmony_ci err = fat_sync_bhs(fatent->bhs, fatent->nr_bhs); 42062306a36Sopenharmony_ci if (err) 42162306a36Sopenharmony_ci return err; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci return fat_mirror_bhs(sb, fatent->bhs, fatent->nr_bhs); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic inline int fat_ent_next(struct msdos_sb_info *sbi, 42762306a36Sopenharmony_ci struct fat_entry *fatent) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci if (sbi->fatent_ops->ent_next(fatent)) { 43062306a36Sopenharmony_ci if (fatent->entry < sbi->max_cluster) 43162306a36Sopenharmony_ci return 1; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci return 0; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic inline int fat_ent_read_block(struct super_block *sb, 43762306a36Sopenharmony_ci struct fat_entry *fatent) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; 44062306a36Sopenharmony_ci sector_t blocknr; 44162306a36Sopenharmony_ci int offset; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci fatent_brelse(fatent); 44462306a36Sopenharmony_ci ops->ent_blocknr(sb, fatent->entry, &offset, &blocknr); 44562306a36Sopenharmony_ci return ops->ent_bread(sb, fatent, offset, blocknr); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic void fat_collect_bhs(struct buffer_head **bhs, int *nr_bhs, 44962306a36Sopenharmony_ci struct fat_entry *fatent) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci int n, i; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci for (n = 0; n < fatent->nr_bhs; n++) { 45462306a36Sopenharmony_ci for (i = 0; i < *nr_bhs; i++) { 45562306a36Sopenharmony_ci if (fatent->bhs[n] == bhs[i]) 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci if (i == *nr_bhs) { 45962306a36Sopenharmony_ci get_bh(fatent->bhs[n]); 46062306a36Sopenharmony_ci bhs[i] = fatent->bhs[n]; 46162306a36Sopenharmony_ci (*nr_bhs)++; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ciint fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 46962306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 47062306a36Sopenharmony_ci const struct fatent_operations *ops = sbi->fatent_ops; 47162306a36Sopenharmony_ci struct fat_entry fatent, prev_ent; 47262306a36Sopenharmony_ci struct buffer_head *bhs[MAX_BUF_PER_PAGE]; 47362306a36Sopenharmony_ci int i, count, err, nr_bhs, idx_clus; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci BUG_ON(nr_cluster > (MAX_BUF_PER_PAGE / 2)); /* fixed limit */ 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci lock_fat(sbi); 47862306a36Sopenharmony_ci if (sbi->free_clusters != -1 && sbi->free_clus_valid && 47962306a36Sopenharmony_ci sbi->free_clusters < nr_cluster) { 48062306a36Sopenharmony_ci unlock_fat(sbi); 48162306a36Sopenharmony_ci return -ENOSPC; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci err = nr_bhs = idx_clus = 0; 48562306a36Sopenharmony_ci count = FAT_START_ENT; 48662306a36Sopenharmony_ci fatent_init(&prev_ent); 48762306a36Sopenharmony_ci fatent_init(&fatent); 48862306a36Sopenharmony_ci fatent_set_entry(&fatent, sbi->prev_free + 1); 48962306a36Sopenharmony_ci while (count < sbi->max_cluster) { 49062306a36Sopenharmony_ci if (fatent.entry >= sbi->max_cluster) 49162306a36Sopenharmony_ci fatent.entry = FAT_START_ENT; 49262306a36Sopenharmony_ci fatent_set_entry(&fatent, fatent.entry); 49362306a36Sopenharmony_ci err = fat_ent_read_block(sb, &fatent); 49462306a36Sopenharmony_ci if (err) 49562306a36Sopenharmony_ci goto out; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* Find the free entries in a block */ 49862306a36Sopenharmony_ci do { 49962306a36Sopenharmony_ci if (ops->ent_get(&fatent) == FAT_ENT_FREE) { 50062306a36Sopenharmony_ci int entry = fatent.entry; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci /* make the cluster chain */ 50362306a36Sopenharmony_ci ops->ent_put(&fatent, FAT_ENT_EOF); 50462306a36Sopenharmony_ci if (prev_ent.nr_bhs) 50562306a36Sopenharmony_ci ops->ent_put(&prev_ent, entry); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci fat_collect_bhs(bhs, &nr_bhs, &fatent); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci sbi->prev_free = entry; 51062306a36Sopenharmony_ci if (sbi->free_clusters != -1) 51162306a36Sopenharmony_ci sbi->free_clusters--; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci cluster[idx_clus] = entry; 51462306a36Sopenharmony_ci idx_clus++; 51562306a36Sopenharmony_ci if (idx_clus == nr_cluster) 51662306a36Sopenharmony_ci goto out; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* 51962306a36Sopenharmony_ci * fat_collect_bhs() gets ref-count of bhs, 52062306a36Sopenharmony_ci * so we can still use the prev_ent. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci prev_ent = fatent; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci count++; 52562306a36Sopenharmony_ci if (count == sbi->max_cluster) 52662306a36Sopenharmony_ci break; 52762306a36Sopenharmony_ci } while (fat_ent_next(sbi, &fatent)); 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* Couldn't allocate the free entries */ 53162306a36Sopenharmony_ci sbi->free_clusters = 0; 53262306a36Sopenharmony_ci sbi->free_clus_valid = 1; 53362306a36Sopenharmony_ci err = -ENOSPC; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ciout: 53662306a36Sopenharmony_ci unlock_fat(sbi); 53762306a36Sopenharmony_ci mark_fsinfo_dirty(sb); 53862306a36Sopenharmony_ci fatent_brelse(&fatent); 53962306a36Sopenharmony_ci if (!err) { 54062306a36Sopenharmony_ci if (inode_needs_sync(inode)) 54162306a36Sopenharmony_ci err = fat_sync_bhs(bhs, nr_bhs); 54262306a36Sopenharmony_ci if (!err) 54362306a36Sopenharmony_ci err = fat_mirror_bhs(sb, bhs, nr_bhs); 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci for (i = 0; i < nr_bhs; i++) 54662306a36Sopenharmony_ci brelse(bhs[i]); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (err && idx_clus) 54962306a36Sopenharmony_ci fat_free_clusters(inode, cluster[0]); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return err; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ciint fat_free_clusters(struct inode *inode, int cluster) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 55762306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 55862306a36Sopenharmony_ci const struct fatent_operations *ops = sbi->fatent_ops; 55962306a36Sopenharmony_ci struct fat_entry fatent; 56062306a36Sopenharmony_ci struct buffer_head *bhs[MAX_BUF_PER_PAGE]; 56162306a36Sopenharmony_ci int i, err, nr_bhs; 56262306a36Sopenharmony_ci int first_cl = cluster, dirty_fsinfo = 0; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci nr_bhs = 0; 56562306a36Sopenharmony_ci fatent_init(&fatent); 56662306a36Sopenharmony_ci lock_fat(sbi); 56762306a36Sopenharmony_ci do { 56862306a36Sopenharmony_ci cluster = fat_ent_read(inode, &fatent, cluster); 56962306a36Sopenharmony_ci if (cluster < 0) { 57062306a36Sopenharmony_ci err = cluster; 57162306a36Sopenharmony_ci goto error; 57262306a36Sopenharmony_ci } else if (cluster == FAT_ENT_FREE) { 57362306a36Sopenharmony_ci fat_fs_error(sb, "%s: deleting FAT entry beyond EOF", 57462306a36Sopenharmony_ci __func__); 57562306a36Sopenharmony_ci err = -EIO; 57662306a36Sopenharmony_ci goto error; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (sbi->options.discard) { 58062306a36Sopenharmony_ci /* 58162306a36Sopenharmony_ci * Issue discard for the sectors we no longer 58262306a36Sopenharmony_ci * care about, batching contiguous clusters 58362306a36Sopenharmony_ci * into one request 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci if (cluster != fatent.entry + 1) { 58662306a36Sopenharmony_ci int nr_clus = fatent.entry - first_cl + 1; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci sb_issue_discard(sb, 58962306a36Sopenharmony_ci fat_clus_to_blknr(sbi, first_cl), 59062306a36Sopenharmony_ci nr_clus * sbi->sec_per_clus, 59162306a36Sopenharmony_ci GFP_NOFS, 0); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci first_cl = cluster; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci ops->ent_put(&fatent, FAT_ENT_FREE); 59862306a36Sopenharmony_ci if (sbi->free_clusters != -1) { 59962306a36Sopenharmony_ci sbi->free_clusters++; 60062306a36Sopenharmony_ci dirty_fsinfo = 1; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (nr_bhs + fatent.nr_bhs > MAX_BUF_PER_PAGE) { 60462306a36Sopenharmony_ci if (sb->s_flags & SB_SYNCHRONOUS) { 60562306a36Sopenharmony_ci err = fat_sync_bhs(bhs, nr_bhs); 60662306a36Sopenharmony_ci if (err) 60762306a36Sopenharmony_ci goto error; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci err = fat_mirror_bhs(sb, bhs, nr_bhs); 61062306a36Sopenharmony_ci if (err) 61162306a36Sopenharmony_ci goto error; 61262306a36Sopenharmony_ci for (i = 0; i < nr_bhs; i++) 61362306a36Sopenharmony_ci brelse(bhs[i]); 61462306a36Sopenharmony_ci nr_bhs = 0; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci fat_collect_bhs(bhs, &nr_bhs, &fatent); 61762306a36Sopenharmony_ci } while (cluster != FAT_ENT_EOF); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (sb->s_flags & SB_SYNCHRONOUS) { 62062306a36Sopenharmony_ci err = fat_sync_bhs(bhs, nr_bhs); 62162306a36Sopenharmony_ci if (err) 62262306a36Sopenharmony_ci goto error; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci err = fat_mirror_bhs(sb, bhs, nr_bhs); 62562306a36Sopenharmony_cierror: 62662306a36Sopenharmony_ci fatent_brelse(&fatent); 62762306a36Sopenharmony_ci for (i = 0; i < nr_bhs; i++) 62862306a36Sopenharmony_ci brelse(bhs[i]); 62962306a36Sopenharmony_ci unlock_fat(sbi); 63062306a36Sopenharmony_ci if (dirty_fsinfo) 63162306a36Sopenharmony_ci mark_fsinfo_dirty(sb); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci return err; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_free_clusters); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistruct fatent_ra { 63862306a36Sopenharmony_ci sector_t cur; 63962306a36Sopenharmony_ci sector_t limit; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci unsigned int ra_blocks; 64262306a36Sopenharmony_ci sector_t ra_advance; 64362306a36Sopenharmony_ci sector_t ra_next; 64462306a36Sopenharmony_ci sector_t ra_limit; 64562306a36Sopenharmony_ci}; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic void fat_ra_init(struct super_block *sb, struct fatent_ra *ra, 64862306a36Sopenharmony_ci struct fat_entry *fatent, int ent_limit) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 65162306a36Sopenharmony_ci const struct fatent_operations *ops = sbi->fatent_ops; 65262306a36Sopenharmony_ci sector_t blocknr, block_end; 65362306a36Sopenharmony_ci int offset; 65462306a36Sopenharmony_ci /* 65562306a36Sopenharmony_ci * This is the sequential read, so ra_pages * 2 (but try to 65662306a36Sopenharmony_ci * align the optimal hardware IO size). 65762306a36Sopenharmony_ci * [BTW, 128kb covers the whole sectors for FAT12 and FAT16] 65862306a36Sopenharmony_ci */ 65962306a36Sopenharmony_ci unsigned long ra_pages = sb->s_bdi->ra_pages; 66062306a36Sopenharmony_ci unsigned int reada_blocks; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (fatent->entry >= ent_limit) 66362306a36Sopenharmony_ci return; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (ra_pages > sb->s_bdi->io_pages) 66662306a36Sopenharmony_ci ra_pages = rounddown(ra_pages, sb->s_bdi->io_pages); 66762306a36Sopenharmony_ci reada_blocks = ra_pages << (PAGE_SHIFT - sb->s_blocksize_bits + 1); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* Initialize the range for sequential read */ 67062306a36Sopenharmony_ci ops->ent_blocknr(sb, fatent->entry, &offset, &blocknr); 67162306a36Sopenharmony_ci ops->ent_blocknr(sb, ent_limit - 1, &offset, &block_end); 67262306a36Sopenharmony_ci ra->cur = 0; 67362306a36Sopenharmony_ci ra->limit = (block_end + 1) - blocknr; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* Advancing the window at half size */ 67662306a36Sopenharmony_ci ra->ra_blocks = reada_blocks >> 1; 67762306a36Sopenharmony_ci ra->ra_advance = ra->cur; 67862306a36Sopenharmony_ci ra->ra_next = ra->cur; 67962306a36Sopenharmony_ci ra->ra_limit = ra->cur + min_t(sector_t, reada_blocks, ra->limit); 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci/* Assuming to be called before reading a new block (increments ->cur). */ 68362306a36Sopenharmony_cistatic void fat_ent_reada(struct super_block *sb, struct fatent_ra *ra, 68462306a36Sopenharmony_ci struct fat_entry *fatent) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci if (ra->ra_next >= ra->ra_limit) 68762306a36Sopenharmony_ci return; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (ra->cur >= ra->ra_advance) { 69062306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 69162306a36Sopenharmony_ci const struct fatent_operations *ops = sbi->fatent_ops; 69262306a36Sopenharmony_ci struct blk_plug plug; 69362306a36Sopenharmony_ci sector_t blocknr, diff; 69462306a36Sopenharmony_ci int offset; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci ops->ent_blocknr(sb, fatent->entry, &offset, &blocknr); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci diff = blocknr - ra->cur; 69962306a36Sopenharmony_ci blk_start_plug(&plug); 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * FIXME: we would want to directly use the bio with 70262306a36Sopenharmony_ci * pages to reduce the number of segments. 70362306a36Sopenharmony_ci */ 70462306a36Sopenharmony_ci for (; ra->ra_next < ra->ra_limit; ra->ra_next++) 70562306a36Sopenharmony_ci sb_breadahead(sb, ra->ra_next + diff); 70662306a36Sopenharmony_ci blk_finish_plug(&plug); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* Advance the readahead window */ 70962306a36Sopenharmony_ci ra->ra_advance += ra->ra_blocks; 71062306a36Sopenharmony_ci ra->ra_limit += min_t(sector_t, 71162306a36Sopenharmony_ci ra->ra_blocks, ra->limit - ra->ra_limit); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci ra->cur++; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ciint fat_count_free_clusters(struct super_block *sb) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 71962306a36Sopenharmony_ci const struct fatent_operations *ops = sbi->fatent_ops; 72062306a36Sopenharmony_ci struct fat_entry fatent; 72162306a36Sopenharmony_ci struct fatent_ra fatent_ra; 72262306a36Sopenharmony_ci int err = 0, free; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci lock_fat(sbi); 72562306a36Sopenharmony_ci if (sbi->free_clusters != -1 && sbi->free_clus_valid) 72662306a36Sopenharmony_ci goto out; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci free = 0; 72962306a36Sopenharmony_ci fatent_init(&fatent); 73062306a36Sopenharmony_ci fatent_set_entry(&fatent, FAT_START_ENT); 73162306a36Sopenharmony_ci fat_ra_init(sb, &fatent_ra, &fatent, sbi->max_cluster); 73262306a36Sopenharmony_ci while (fatent.entry < sbi->max_cluster) { 73362306a36Sopenharmony_ci /* readahead of fat blocks */ 73462306a36Sopenharmony_ci fat_ent_reada(sb, &fatent_ra, &fatent); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci err = fat_ent_read_block(sb, &fatent); 73762306a36Sopenharmony_ci if (err) 73862306a36Sopenharmony_ci goto out; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci do { 74162306a36Sopenharmony_ci if (ops->ent_get(&fatent) == FAT_ENT_FREE) 74262306a36Sopenharmony_ci free++; 74362306a36Sopenharmony_ci } while (fat_ent_next(sbi, &fatent)); 74462306a36Sopenharmony_ci cond_resched(); 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci sbi->free_clusters = free; 74762306a36Sopenharmony_ci sbi->free_clus_valid = 1; 74862306a36Sopenharmony_ci mark_fsinfo_dirty(sb); 74962306a36Sopenharmony_ci fatent_brelse(&fatent); 75062306a36Sopenharmony_ciout: 75162306a36Sopenharmony_ci unlock_fat(sbi); 75262306a36Sopenharmony_ci return err; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int fat_trim_clusters(struct super_block *sb, u32 clus, u32 nr_clus) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 75862306a36Sopenharmony_ci return sb_issue_discard(sb, fat_clus_to_blknr(sbi, clus), 75962306a36Sopenharmony_ci nr_clus * sbi->sec_per_clus, GFP_NOFS, 0); 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ciint fat_trim_fs(struct inode *inode, struct fstrim_range *range) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 76562306a36Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 76662306a36Sopenharmony_ci const struct fatent_operations *ops = sbi->fatent_ops; 76762306a36Sopenharmony_ci struct fat_entry fatent; 76862306a36Sopenharmony_ci struct fatent_ra fatent_ra; 76962306a36Sopenharmony_ci u64 ent_start, ent_end, minlen, trimmed = 0; 77062306a36Sopenharmony_ci u32 free = 0; 77162306a36Sopenharmony_ci int err = 0; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* 77462306a36Sopenharmony_ci * FAT data is organized as clusters, trim at the granulary of cluster. 77562306a36Sopenharmony_ci * 77662306a36Sopenharmony_ci * fstrim_range is in byte, convert values to cluster index. 77762306a36Sopenharmony_ci * Treat sectors before data region as all used, not to trim them. 77862306a36Sopenharmony_ci */ 77962306a36Sopenharmony_ci ent_start = max_t(u64, range->start>>sbi->cluster_bits, FAT_START_ENT); 78062306a36Sopenharmony_ci ent_end = ent_start + (range->len >> sbi->cluster_bits) - 1; 78162306a36Sopenharmony_ci minlen = range->minlen >> sbi->cluster_bits; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci if (ent_start >= sbi->max_cluster || range->len < sbi->cluster_size) 78462306a36Sopenharmony_ci return -EINVAL; 78562306a36Sopenharmony_ci if (ent_end >= sbi->max_cluster) 78662306a36Sopenharmony_ci ent_end = sbi->max_cluster - 1; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci fatent_init(&fatent); 78962306a36Sopenharmony_ci lock_fat(sbi); 79062306a36Sopenharmony_ci fatent_set_entry(&fatent, ent_start); 79162306a36Sopenharmony_ci fat_ra_init(sb, &fatent_ra, &fatent, ent_end + 1); 79262306a36Sopenharmony_ci while (fatent.entry <= ent_end) { 79362306a36Sopenharmony_ci /* readahead of fat blocks */ 79462306a36Sopenharmony_ci fat_ent_reada(sb, &fatent_ra, &fatent); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci err = fat_ent_read_block(sb, &fatent); 79762306a36Sopenharmony_ci if (err) 79862306a36Sopenharmony_ci goto error; 79962306a36Sopenharmony_ci do { 80062306a36Sopenharmony_ci if (ops->ent_get(&fatent) == FAT_ENT_FREE) { 80162306a36Sopenharmony_ci free++; 80262306a36Sopenharmony_ci } else if (free) { 80362306a36Sopenharmony_ci if (free >= minlen) { 80462306a36Sopenharmony_ci u32 clus = fatent.entry - free; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci err = fat_trim_clusters(sb, clus, free); 80762306a36Sopenharmony_ci if (err && err != -EOPNOTSUPP) 80862306a36Sopenharmony_ci goto error; 80962306a36Sopenharmony_ci if (!err) 81062306a36Sopenharmony_ci trimmed += free; 81162306a36Sopenharmony_ci err = 0; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci free = 0; 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci } while (fat_ent_next(sbi, &fatent) && fatent.entry <= ent_end); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (fatal_signal_pending(current)) { 81862306a36Sopenharmony_ci err = -ERESTARTSYS; 81962306a36Sopenharmony_ci goto error; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci if (need_resched()) { 82362306a36Sopenharmony_ci fatent_brelse(&fatent); 82462306a36Sopenharmony_ci unlock_fat(sbi); 82562306a36Sopenharmony_ci cond_resched(); 82662306a36Sopenharmony_ci lock_fat(sbi); 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci /* handle scenario when tail entries are all free */ 83062306a36Sopenharmony_ci if (free && free >= minlen) { 83162306a36Sopenharmony_ci u32 clus = fatent.entry - free; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci err = fat_trim_clusters(sb, clus, free); 83462306a36Sopenharmony_ci if (err && err != -EOPNOTSUPP) 83562306a36Sopenharmony_ci goto error; 83662306a36Sopenharmony_ci if (!err) 83762306a36Sopenharmony_ci trimmed += free; 83862306a36Sopenharmony_ci err = 0; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cierror: 84262306a36Sopenharmony_ci fatent_brelse(&fatent); 84362306a36Sopenharmony_ci unlock_fat(sbi); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci range->len = trimmed << sbi->cluster_bits; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return err; 84862306a36Sopenharmony_ci} 849