| // SPDX-License-Identifier: GPL-2.0-or-later | |
| /* | |
| * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. | |
| */ | |
| #include <linux/slab.h> | |
| #include <linux/unaligned.h> | |
| #include <linux/buffer_head.h> | |
| #include <linux/blkdev.h> | |
| #include "exfat_raw.h" | |
| #include "exfat_fs.h" | |
| static int exfat_mirror_bh(struct super_block *sb, struct buffer_head *bh) | |
| { | |
| struct buffer_head *c_bh; | |
| struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
| sector_t sec = bh->b_blocknr; | |
| sector_t sec2; | |
| int err = 0; | |
| if (sbi->FAT2_start_sector != sbi->FAT1_start_sector) { | |
| sec2 = sec - sbi->FAT1_start_sector + sbi->FAT2_start_sector; | |
| c_bh = sb_getblk(sb, sec2); | |
| if (!c_bh) | |
| return -ENOMEM; | |
| memcpy(c_bh->b_data, bh->b_data, sb->s_blocksize); | |
| err = exfat_update_bh(c_bh, sb->s_flags & SB_SYNCHRONOUS); | |
| brelse(c_bh); | |
| } | |
| return err; | |
| } | |
| static int exfat_end_bh(struct super_block *sb, struct buffer_head *bh) | |
| { | |
| int err; | |
| err = exfat_update_bh(bh, sb->s_flags & SB_SYNCHRONOUS); | |
| if (!err) | |
| err = exfat_mirror_bh(sb, bh); | |
| brelse(bh); | |
| return err; | |
| } | |
| static int __exfat_ent_get(struct super_block *sb, unsigned int loc, | |
| unsigned int *content, struct buffer_head **cache) | |
| { | |
| unsigned int off; | |
| sector_t sec; | |
| struct buffer_head *bh = *cache; | |
| sec = FAT_ENT_OFFSET_SECTOR(sb, loc); | |
| off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc); | |
| if (!bh || bh->b_blocknr != sec || !buffer_uptodate(bh)) { | |
| brelse(bh); | |
| bh = sb_bread(sb, sec); | |
| *cache = bh; | |
| if (unlikely(!bh)) | |
| return -EIO; | |
| } | |
| *content = le32_to_cpu(*(__le32 *)(&bh->b_data[off])); | |
| /* remap reserved clusters to simplify code */ | |
| if (*content > EXFAT_BAD_CLUSTER) | |
| *content = EXFAT_EOF_CLUSTER; | |
| return 0; | |
| } | |
| static int __exfat_ent_set(struct super_block *sb, unsigned int loc, | |
| unsigned int content, struct buffer_head **cache) | |
| { | |
| sector_t sec; | |
| __le32 *fat_entry; | |
| struct buffer_head *bh = cache ? *cache : NULL; | |
| unsigned int off; | |
| sec = FAT_ENT_OFFSET_SECTOR(sb, loc); | |
| off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc); | |
| if (!bh || bh->b_blocknr != sec || !buffer_uptodate(bh)) { | |
| if (bh) | |
| exfat_end_bh(sb, bh); | |
| bh = sb_bread(sb, sec); | |
| if (cache) | |
| *cache = bh; | |
| if (unlikely(!bh)) | |
| return -EIO; | |
| } | |
| fat_entry = (__le32 *)&(bh->b_data[off]); | |
| *fat_entry = cpu_to_le32(content); | |
| if (!cache) | |
| exfat_end_bh(sb, bh); | |
| return 0; | |
| } | |
| int exfat_ent_set(struct super_block *sb, unsigned int loc, | |
| unsigned int content) | |
| { | |
| return __exfat_ent_set(sb, loc, content, NULL); | |
| } | |
| /* | |
| * Caller must release the buffer_head if no error return. | |
| */ | |
| int exfat_ent_get(struct super_block *sb, unsigned int loc, | |
| unsigned int *content, struct buffer_head **cache) | |
| { | |
| struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
| if (!is_valid_cluster(sbi, loc)) { | |
| exfat_fs_error_ratelimit(sb, | |
| "invalid access to FAT (entry 0x%08x)", | |
| loc); | |
| goto err; | |
| } | |
| if (unlikely(__exfat_ent_get(sb, loc, content, cache))) { | |
| exfat_fs_error_ratelimit(sb, | |
| "failed to access to FAT (entry 0x%08x)", | |
| loc); | |
| goto err; | |
| } | |
| if (unlikely(*content == EXFAT_FREE_CLUSTER)) { | |
| exfat_fs_error_ratelimit(sb, | |
| "invalid access to FAT free cluster (entry 0x%08x)", | |
| loc); | |
| goto err; | |
| } | |
| if (unlikely(*content == EXFAT_BAD_CLUSTER)) { | |
| exfat_fs_error_ratelimit(sb, | |
| "invalid access to FAT bad cluster (entry 0x%08x)", | |
| loc); | |
| goto err; | |
| } | |
| if (*content != EXFAT_EOF_CLUSTER && !is_valid_cluster(sbi, *content)) { | |
| exfat_fs_error_ratelimit(sb, | |
| "invalid access to FAT (entry 0x%08x) bogus content (0x%08x)", | |
| loc, *content); | |
| goto err; | |
| } | |
| return 0; | |
| err: | |
| /* Avoid double release */ | |
| brelse(*cache); | |
| *cache = NULL; | |
| return -EIO; | |
| } | |
| int exfat_blk_readahead(struct super_block *sb, sector_t sec, | |
| sector_t *ra, blkcnt_t *ra_cnt, sector_t end) | |
| { | |
| struct blk_plug plug; | |
| if (sec < *ra) | |
| return 0; | |
| *ra += *ra_cnt; | |
| /* No blocks left (or only the last block), skip readahead. */ | |
| if (*ra >= end) | |
| return 0; | |
| *ra_cnt = min(end - *ra + 1, EXFAT_BLK_RA_SIZE(sb)); | |
| if (*ra_cnt == 0) { | |
| /* Move 'ra' to the end to disable readahead. */ | |
| *ra = end; | |
| return 0; | |
| } | |
| blk_start_plug(&plug); | |
| for (unsigned int i = 0; i < *ra_cnt; i++) | |
| sb_breadahead(sb, *ra + i); | |
| blk_finish_plug(&plug); | |
| return 0; | |
| } | |
| int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain, | |
| unsigned int len) | |
| { | |
| struct buffer_head *bh = NULL; | |
| sector_t sec, end, ra; | |
| blkcnt_t ra_cnt = 0; | |
| if (!len) | |
| return 0; | |
| ra = FAT_ENT_OFFSET_SECTOR(sb, chain); | |
| end = FAT_ENT_OFFSET_SECTOR(sb, chain + len - 1); | |
| while (len > 1) { | |
| sec = FAT_ENT_OFFSET_SECTOR(sb, chain); | |
| exfat_blk_readahead(sb, sec, &ra, &ra_cnt, end); | |
| if (__exfat_ent_set(sb, chain, chain + 1, &bh)) | |
| return -EIO; | |
| chain++; | |
| len--; | |
| } | |
| if (__exfat_ent_set(sb, chain, EXFAT_EOF_CLUSTER, &bh)) | |
| return -EIO; | |
| exfat_end_bh(sb, bh); | |
| return 0; | |
| } | |
| static inline void exfat_discard_cluster(struct super_block *sb, | |
| unsigned int clu, unsigned int num_clusters) | |
| { | |
| int ret; | |
| struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
| ret = sb_issue_discard(sb, exfat_cluster_to_sector(sbi, clu), | |
| sbi->sect_per_clus * num_clusters, GFP_NOFS, 0); | |
| if (ret == -EOPNOTSUPP) { | |
| exfat_err(sb, "discard not supported by device, disabling"); | |
| sbi->options.discard = 0; | |
| } | |
| } | |
| /* This function must be called with bitmap_lock held */ | |
| static int __exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain) | |
| { | |
| struct super_block *sb = inode->i_sb; | |
| struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
| int cur_cmap_i, next_cmap_i; | |
| unsigned int num_clusters = 0; | |
| unsigned int clu; | |
| /* invalid cluster number */ | |
| if (p_chain->dir == EXFAT_FREE_CLUSTER || | |
| p_chain->dir == EXFAT_EOF_CLUSTER || | |
| p_chain->dir < EXFAT_FIRST_CLUSTER) | |
| return 0; | |
| /* no cluster to truncate */ | |
| if (p_chain->size == 0) | |
| return 0; | |
| /* check cluster validation */ | |
| if (!is_valid_cluster(sbi, p_chain->dir)) { | |
| exfat_err(sb, "invalid start cluster (%u)", p_chain->dir); | |
| return -EIO; | |
| } | |
| clu = p_chain->dir; | |
| cur_cmap_i = next_cmap_i = | |
| BITMAP_OFFSET_SECTOR_INDEX(sb, CLUSTER_TO_BITMAP_ENT(clu)); | |
| if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { | |
| int err; | |
| unsigned int last_cluster = p_chain->dir + p_chain->size - 1; | |
| do { | |
| bool sync = false; | |
| if (clu < last_cluster) | |
| next_cmap_i = | |
| BITMAP_OFFSET_SECTOR_INDEX(sb, CLUSTER_TO_BITMAP_ENT(clu+1)); | |
| /* flush bitmap only if index would be changed or for last cluster */ | |
| if (clu == last_cluster || cur_cmap_i != next_cmap_i) { | |
| sync = true; | |
| cur_cmap_i = next_cmap_i; | |
| } | |
| err = exfat_clear_bitmap(sb, clu, (sync && IS_DIRSYNC(inode))); | |
| if (err) | |
| break; | |
| clu++; | |
| num_clusters++; | |
| } while (num_clusters < p_chain->size); | |
| if (sbi->options.discard) | |
| exfat_discard_cluster(sb, p_chain->dir, p_chain->size); | |
| } else { | |
| unsigned int nr_clu = 1; | |
| do { | |
| bool sync = false; | |
| unsigned int n_clu = clu; | |
| int err = exfat_get_next_cluster(sb, &n_clu); | |
| if (err || n_clu == EXFAT_EOF_CLUSTER) | |
| sync = true; | |
| else | |
| next_cmap_i = | |
| BITMAP_OFFSET_SECTOR_INDEX(sb, CLUSTER_TO_BITMAP_ENT(n_clu)); | |
| if (cur_cmap_i != next_cmap_i) { | |
| sync = true; | |
| cur_cmap_i = next_cmap_i; | |
| } | |
| if (exfat_clear_bitmap(sb, clu, (sync && IS_DIRSYNC(inode)))) | |
| break; | |
| if (sbi->options.discard) { | |
| if (n_clu == clu + 1) | |
| nr_clu++; | |
| else { | |
| exfat_discard_cluster(sb, clu - nr_clu + 1, nr_clu); | |
| nr_clu = 1; | |
| } | |
| } | |
| clu = n_clu; | |
| num_clusters++; | |
| if (err) | |
| break; | |
| if (num_clusters >= sbi->num_clusters - EXFAT_FIRST_CLUSTER) { | |
| /* | |
| * The cluster chain includes a loop, scan the | |
| * bitmap to get the number of used clusters. | |
| */ | |
| exfat_count_used_clusters(sb, &sbi->used_clusters); | |
| return 0; | |
| } | |
| } while (clu != EXFAT_EOF_CLUSTER); | |
| } | |
| sbi->used_clusters -= num_clusters; | |
| return 0; | |
| } | |
| int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain) | |
| { | |
| int ret = 0; | |
| mutex_lock(&EXFAT_SB(inode->i_sb)->bitmap_lock); | |
| ret = __exfat_free_cluster(inode, p_chain); | |
| mutex_unlock(&EXFAT_SB(inode->i_sb)->bitmap_lock); | |
| return ret; | |
| } | |
| int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain, | |
| unsigned int *ret_clu) | |
| { | |
| struct buffer_head *bh = NULL; | |
| unsigned int clu, next; | |
| unsigned int count = 0; | |
| next = p_chain->dir; | |
| if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { | |
| *ret_clu = next + p_chain->size - 1; | |
| return 0; | |
| } | |
| do { | |
| count++; | |
| clu = next; | |
| if (exfat_ent_get(sb, clu, &next, &bh)) | |
| return -EIO; | |
| } while (next != EXFAT_EOF_CLUSTER && count <= p_chain->size); | |
| brelse(bh); | |
| if (p_chain->size != count) { | |
| exfat_fs_error(sb, | |
| "bogus directory size (clus : ondisk(%d) != counted(%d))", | |
| p_chain->size, count); | |
| return -EIO; | |
| } | |
| *ret_clu = clu; | |
| return 0; | |
| } | |
| int exfat_zeroed_cluster(struct inode *dir, unsigned int clu) | |
| { | |
| struct super_block *sb = dir->i_sb; | |
| struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
| struct buffer_head *bh; | |
| sector_t blknr, last_blknr, i; | |
| blknr = exfat_cluster_to_sector(sbi, clu); | |
| last_blknr = blknr + sbi->sect_per_clus; | |
| if (last_blknr > sbi->num_sectors && sbi->num_sectors > 0) { | |
| exfat_fs_error_ratelimit(sb, | |
| "%s: out of range(sect:%llu len:%u)", | |
| __func__, (unsigned long long)blknr, | |
| sbi->sect_per_clus); | |
| return -EIO; | |
| } | |
| /* Zeroing the unused blocks on this cluster */ | |
| for (i = blknr; i < last_blknr; i++) { | |
| bh = sb_getblk(sb, i); | |
| if (!bh) | |
| return -ENOMEM; | |
| memset(bh->b_data, 0, sb->s_blocksize); | |
| set_buffer_uptodate(bh); | |
| mark_buffer_dirty(bh); | |
| brelse(bh); | |
| } | |
| if (IS_DIRSYNC(dir)) | |
| return sync_blockdev_range(sb->s_bdev, | |
| exfat_block_to_bytes(sb, blknr), | |
| exfat_block_to_bytes(sb, last_blknr) - 1); | |
| return 0; | |
| } | |
| int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, | |
| struct exfat_chain *p_chain, bool sync_bmap, bool contig) | |
| { | |
| int ret = -ENOSPC; | |
| unsigned int total_cnt; | |
| unsigned int hint_clu, new_clu, last_clu = EXFAT_EOF_CLUSTER; | |
| struct super_block *sb = inode->i_sb; | |
| struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
| total_cnt = EXFAT_DATA_CLUSTER_COUNT(sbi); | |
| if (unlikely(total_cnt < sbi->used_clusters)) { | |
| exfat_fs_error_ratelimit(sb, | |
| "%s: invalid used clusters(t:%u,u:%u)\n", | |
| __func__, total_cnt, sbi->used_clusters); | |
| return -EIO; | |
| } | |
| if (num_alloc > total_cnt - sbi->used_clusters) | |
| return -ENOSPC; | |
| mutex_lock(&sbi->bitmap_lock); | |
| hint_clu = p_chain->dir; | |
| /* find new cluster */ | |
| if (hint_clu == EXFAT_EOF_CLUSTER) { | |
| if (sbi->clu_srch_ptr < EXFAT_FIRST_CLUSTER) { | |
| exfat_err(sb, "sbi->clu_srch_ptr is invalid (%u)", | |
| sbi->clu_srch_ptr); | |
| sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER; | |
| } | |
| hint_clu = exfat_find_free_bitmap(sb, sbi->clu_srch_ptr); | |
| if (hint_clu == EXFAT_EOF_CLUSTER) { | |
| ret = -ENOSPC; | |
| goto unlock; | |
| } | |
| } | |
| /* check cluster validation */ | |
| if (!is_valid_cluster(sbi, hint_clu)) { | |
| if (hint_clu != sbi->num_clusters) | |
| exfat_err(sb, "hint_cluster is invalid (%u), rewind to the first cluster", | |
| hint_clu); | |
| hint_clu = EXFAT_FIRST_CLUSTER; | |
| p_chain->flags = ALLOC_FAT_CHAIN; | |
| } | |
| p_chain->dir = EXFAT_EOF_CLUSTER; | |
| while ((new_clu = exfat_find_free_bitmap(sb, hint_clu)) != | |
| EXFAT_EOF_CLUSTER) { | |
| if (new_clu != hint_clu) { | |
| if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { | |
| if (exfat_chain_cont_cluster(sb, p_chain->dir, | |
| p_chain->size)) { | |
| ret = -EIO; | |
| goto free_cluster; | |
| } | |
| p_chain->flags = ALLOC_FAT_CHAIN; | |
| } | |
| if (contig && p_chain->size > 0) { | |
| hint_clu = last_clu; | |
| goto done; | |
| } | |
| } | |
| /* update allocation bitmap */ | |
| if (exfat_set_bitmap(sb, new_clu, sync_bmap)) { | |
| ret = -EIO; | |
| goto free_cluster; | |
| } | |
| /* update FAT table */ | |
| if (p_chain->flags == ALLOC_FAT_CHAIN) { | |
| if (exfat_ent_set(sb, new_clu, EXFAT_EOF_CLUSTER)) { | |
| ret = -EIO; | |
| goto free_cluster; | |
| } | |
| } | |
| if (p_chain->dir == EXFAT_EOF_CLUSTER) { | |
| p_chain->dir = new_clu; | |
| } else if (p_chain->flags == ALLOC_FAT_CHAIN) { | |
| if (exfat_ent_set(sb, last_clu, new_clu)) { | |
| ret = -EIO; | |
| goto free_cluster; | |
| } | |
| } | |
| p_chain->size++; | |
| last_clu = new_clu; | |
| if (p_chain->size == num_alloc) { | |
| done: | |
| sbi->clu_srch_ptr = hint_clu; | |
| sbi->used_clusters += p_chain->size; | |
| mutex_unlock(&sbi->bitmap_lock); | |
| return 0; | |
| } | |
| hint_clu = new_clu + 1; | |
| if (hint_clu >= sbi->num_clusters) { | |
| hint_clu = EXFAT_FIRST_CLUSTER; | |
| if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { | |
| if (exfat_chain_cont_cluster(sb, p_chain->dir, | |
| p_chain->size)) { | |
| ret = -EIO; | |
| goto free_cluster; | |
| } | |
| p_chain->flags = ALLOC_FAT_CHAIN; | |
| } | |
| } | |
| } | |
| free_cluster: | |
| __exfat_free_cluster(inode, p_chain); | |
| unlock: | |
| mutex_unlock(&sbi->bitmap_lock); | |
| return ret; | |
| } | |
| int exfat_count_num_clusters(struct super_block *sb, | |
| struct exfat_chain *p_chain, unsigned int *ret_count) | |
| { | |
| unsigned int i, count; | |
| unsigned int clu; | |
| struct exfat_sb_info *sbi = EXFAT_SB(sb); | |
| struct buffer_head *bh = NULL; | |
| if (!p_chain->dir || p_chain->dir == EXFAT_EOF_CLUSTER) { | |
| *ret_count = 0; | |
| return 0; | |
| } | |
| if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { | |
| *ret_count = p_chain->size; | |
| return 0; | |
| } | |
| clu = p_chain->dir; | |
| count = 0; | |
| for (i = EXFAT_FIRST_CLUSTER; i < sbi->num_clusters; i++) { | |
| count++; | |
| if (exfat_ent_get(sb, clu, &clu, &bh)) | |
| return -EIO; | |
| if (clu == EXFAT_EOF_CLUSTER) | |
| break; | |
| } | |
| brelse(bh); | |
| *ret_count = count; | |
| /* | |
| * since exfat_count_used_clusters() is not called, sbi->used_clusters | |
| * cannot be used here. | |
| */ | |
| if (unlikely(i == sbi->num_clusters && clu != EXFAT_EOF_CLUSTER)) { | |
| exfat_fs_error(sb, "The cluster chain has a loop"); | |
| return -EIO; | |
| } | |
| return 0; | |
| } |