From 18f89a5b11f88efceb1b5fc50bd628b6bfa34c14 Mon Sep 17 00:00:00 2001 From: Sorgelig Date: Mon, 30 Aug 2021 22:52:23 +0800 Subject: [PATCH] Remove original exFAT driver. --- fs/exfat/Kconfig | 22 - fs/exfat/Makefile | 8 - fs/exfat/balloc.c | 346 ----------- fs/exfat/cache.c | 314 ---------- fs/exfat/dir.c | 1203 ----------------------------------- fs/exfat/exfat_fs.h | 529 ---------------- fs/exfat/exfat_raw.h | 168 ----- fs/exfat/fatent.c | 476 -------------- fs/exfat/file.c | 435 ------------- fs/exfat/inode.c | 659 -------------------- fs/exfat/misc.c | 205 ------ fs/exfat/namei.c | 1416 ------------------------------------------ fs/exfat/nls.c | 807 ------------------------ fs/exfat/super.c | 841 ------------------------- 14 files changed, 7429 deletions(-) delete mode 100644 fs/exfat/Kconfig delete mode 100644 fs/exfat/Makefile delete mode 100644 fs/exfat/balloc.c delete mode 100644 fs/exfat/cache.c delete mode 100644 fs/exfat/dir.c delete mode 100644 fs/exfat/exfat_fs.h delete mode 100644 fs/exfat/exfat_raw.h delete mode 100644 fs/exfat/fatent.c delete mode 100644 fs/exfat/file.c delete mode 100644 fs/exfat/inode.c delete mode 100644 fs/exfat/misc.c delete mode 100644 fs/exfat/namei.c delete mode 100644 fs/exfat/nls.c delete mode 100644 fs/exfat/super.c diff --git a/fs/exfat/Kconfig b/fs/exfat/Kconfig deleted file mode 100644 index 5a65071b5..000000000 --- a/fs/exfat/Kconfig +++ /dev/null @@ -1,22 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later - -config EXFAT_FS - tristate "exFAT filesystem support" - select NLS - help - This allows you to mount devices formatted with the exFAT file system. - exFAT is typically used on SD-Cards or USB sticks. - - To compile this as a module, choose M here: the module will be called - exfat. - -config EXFAT_DEFAULT_IOCHARSET - string "Default iocharset for exFAT" - default "utf8" - depends on EXFAT_FS - help - Set this to the default input/output character set to use for - converting between the encoding that is used for user visible - filenames and the UTF-16 character encoding that the exFAT - filesystem uses. This can be overridden with the "iocharset" mount - option for the exFAT filesystems. diff --git a/fs/exfat/Makefile b/fs/exfat/Makefile deleted file mode 100644 index ed51926a4..000000000 --- a/fs/exfat/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-or-later -# -# Makefile for the linux exFAT filesystem support. -# -obj-$(CONFIG_EXFAT_FS) += exfat.o - -exfat-y := inode.o namei.o dir.o super.o fatent.o cache.o nls.o misc.o \ - file.o balloc.o diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c deleted file mode 100644 index cc5cffc4a..000000000 --- a/fs/exfat/balloc.c +++ /dev/null @@ -1,346 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include - -#include "exfat_raw.h" -#include "exfat_fs.h" - -static const unsigned char free_bit[] = { - 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/* 0 ~ 19*/ - 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,/* 20 ~ 39*/ - 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/* 40 ~ 59*/ - 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/* 60 ~ 79*/ - 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2,/* 80 ~ 99*/ - 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,/*100 ~ 119*/ - 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*120 ~ 139*/ - 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,/*140 ~ 159*/ - 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/*160 ~ 179*/ - 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3,/*180 ~ 199*/ - 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*200 ~ 219*/ - 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/*220 ~ 239*/ - 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /*240 ~ 254*/ -}; - -static const unsigned char used_bit[] = { - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,/* 0 ~ 19*/ - 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,/* 20 ~ 39*/ - 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,/* 40 ~ 59*/ - 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,/* 60 ~ 79*/ - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,/* 80 ~ 99*/ - 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,/*100 ~ 119*/ - 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,/*120 ~ 139*/ - 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,/*140 ~ 159*/ - 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,/*160 ~ 179*/ - 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,/*180 ~ 199*/ - 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,/*200 ~ 219*/ - 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,/*220 ~ 239*/ - 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /*240 ~ 255*/ -}; - -/* - * Allocation Bitmap Management Functions - */ -static int exfat_allocate_bitmap(struct super_block *sb, - struct exfat_dentry *ep) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - long long map_size; - unsigned int i, need_map_size; - sector_t sector; - - sbi->map_clu = le32_to_cpu(ep->dentry.bitmap.start_clu); - map_size = le64_to_cpu(ep->dentry.bitmap.size); - need_map_size = ((EXFAT_DATA_CLUSTER_COUNT(sbi) - 1) / BITS_PER_BYTE) - + 1; - if (need_map_size != map_size) { - exfat_err(sb, "bogus allocation bitmap size(need : %u, cur : %lld)", - need_map_size, map_size); - /* - * Only allowed when bogus allocation - * bitmap size is large - */ - if (need_map_size > map_size) - return -EIO; - } - sbi->map_sectors = ((need_map_size - 1) >> - (sb->s_blocksize_bits)) + 1; - sbi->vol_amap = kmalloc_array(sbi->map_sectors, - sizeof(struct buffer_head *), GFP_KERNEL); - if (!sbi->vol_amap) - return -ENOMEM; - - sector = exfat_cluster_to_sector(sbi, sbi->map_clu); - for (i = 0; i < sbi->map_sectors; i++) { - sbi->vol_amap[i] = sb_bread(sb, sector + i); - if (!sbi->vol_amap[i]) { - /* release all buffers and free vol_amap */ - int j = 0; - - while (j < i) - brelse(sbi->vol_amap[j++]); - - kfree(sbi->vol_amap); - sbi->vol_amap = NULL; - return -EIO; - } - } - - return 0; -} - -int exfat_load_bitmap(struct super_block *sb) -{ - unsigned int i, type; - struct exfat_chain clu; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - - exfat_chain_set(&clu, sbi->root_dir, 0, ALLOC_FAT_CHAIN); - while (clu.dir != EXFAT_EOF_CLUSTER) { - for (i = 0; i < sbi->dentries_per_clu; i++) { - struct exfat_dentry *ep; - struct buffer_head *bh; - - ep = exfat_get_dentry(sb, &clu, i, &bh, NULL); - if (!ep) - return -EIO; - - type = exfat_get_entry_type(ep); - if (type == TYPE_UNUSED) - break; - if (type != TYPE_BITMAP) - continue; - if (ep->dentry.bitmap.flags == 0x0) { - int err; - - err = exfat_allocate_bitmap(sb, ep); - brelse(bh); - return err; - } - brelse(bh); - } - - if (exfat_get_next_cluster(sb, &clu.dir)) - return -EIO; - } - - return -EINVAL; -} - -void exfat_free_bitmap(struct exfat_sb_info *sbi) -{ - int i; - - for (i = 0; i < sbi->map_sectors; i++) - __brelse(sbi->vol_amap[i]); - - kfree(sbi->vol_amap); -} - -int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync) -{ - int i, b; - unsigned int ent_idx; - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - - WARN_ON(clu < EXFAT_FIRST_CLUSTER); - ent_idx = CLUSTER_TO_BITMAP_ENT(clu); - i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx); - b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx); - - set_bit_le(b, sbi->vol_amap[i]->b_data); - exfat_update_bh(sbi->vol_amap[i], sync); - return 0; -} - -void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync) -{ - int i, b; - unsigned int ent_idx; - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_mount_options *opts = &sbi->options; - - WARN_ON(clu < EXFAT_FIRST_CLUSTER); - ent_idx = CLUSTER_TO_BITMAP_ENT(clu); - i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx); - b = BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent_idx); - - clear_bit_le(b, sbi->vol_amap[i]->b_data); - exfat_update_bh(sbi->vol_amap[i], sync); - - if (opts->discard) { - int ret_discard; - - ret_discard = sb_issue_discard(sb, - exfat_cluster_to_sector(sbi, clu), - (1 << sbi->sect_per_clus_bits), GFP_NOFS, 0); - - if (ret_discard == -EOPNOTSUPP) { - exfat_err(sb, "discard not supported by device, disabling"); - opts->discard = 0; - } - } -} - -/* - * If the value of "clu" is 0, it means cluster 2 which is the first cluster of - * the cluster heap. - */ -unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu) -{ - unsigned int i, map_i, map_b, ent_idx; - unsigned int clu_base, clu_free; - unsigned char k, clu_mask; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - - WARN_ON(clu < EXFAT_FIRST_CLUSTER); - ent_idx = CLUSTER_TO_BITMAP_ENT(clu); - clu_base = BITMAP_ENT_TO_CLUSTER(ent_idx & ~(BITS_PER_BYTE_MASK)); - clu_mask = IGNORED_BITS_REMAINED(clu, clu_base); - - map_i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx); - map_b = BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent_idx); - - for (i = EXFAT_FIRST_CLUSTER; i < sbi->num_clusters; - i += BITS_PER_BYTE) { - k = *(sbi->vol_amap[map_i]->b_data + map_b); - if (clu_mask > 0) { - k |= clu_mask; - clu_mask = 0; - } - if (k < 0xFF) { - clu_free = clu_base + free_bit[k]; - if (clu_free < sbi->num_clusters) - return clu_free; - } - clu_base += BITS_PER_BYTE; - - if (++map_b >= sb->s_blocksize || - clu_base >= sbi->num_clusters) { - if (++map_i >= sbi->map_sectors) { - clu_base = EXFAT_FIRST_CLUSTER; - map_i = 0; - } - map_b = 0; - } - } - - return EXFAT_EOF_CLUSTER; -} - -int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - unsigned int count = 0; - unsigned int i, map_i = 0, map_b = 0; - unsigned int total_clus = EXFAT_DATA_CLUSTER_COUNT(sbi); - unsigned int last_mask = total_clus & BITS_PER_BYTE_MASK; - unsigned char clu_bits; - const unsigned char last_bit_mask[] = {0, 0b00000001, 0b00000011, - 0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111}; - - total_clus &= ~last_mask; - for (i = 0; i < total_clus; i += BITS_PER_BYTE) { - clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b); - count += used_bit[clu_bits]; - if (++map_b >= (unsigned int)sb->s_blocksize) { - map_i++; - map_b = 0; - } - } - - if (last_mask) { - clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b); - clu_bits &= last_bit_mask[last_mask]; - count += used_bit[clu_bits]; - } - - *ret_count = count; - return 0; -} - -int exfat_trim_fs(struct inode *inode, struct fstrim_range *range) -{ - unsigned int trim_begin, trim_end, count, next_free_clu; - u64 clu_start, clu_end, trim_minlen, trimmed_total = 0; - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - int err = 0; - - clu_start = max_t(u64, range->start >> sbi->cluster_size_bits, - EXFAT_FIRST_CLUSTER); - clu_end = clu_start + (range->len >> sbi->cluster_size_bits) - 1; - trim_minlen = range->minlen >> sbi->cluster_size_bits; - - if (clu_start >= sbi->num_clusters || range->len < sbi->cluster_size) - return -EINVAL; - - if (clu_end >= sbi->num_clusters) - clu_end = sbi->num_clusters - 1; - - mutex_lock(&sbi->bitmap_lock); - - trim_begin = trim_end = exfat_find_free_bitmap(sb, clu_start); - if (trim_begin == EXFAT_EOF_CLUSTER) - goto unlock; - - next_free_clu = exfat_find_free_bitmap(sb, trim_end + 1); - if (next_free_clu == EXFAT_EOF_CLUSTER) - goto unlock; - - do { - if (next_free_clu == trim_end + 1) { - /* extend trim range for continuous free cluster */ - trim_end++; - } else { - /* trim current range if it's larger than trim_minlen */ - count = trim_end - trim_begin + 1; - if (count >= trim_minlen) { - err = sb_issue_discard(sb, - exfat_cluster_to_sector(sbi, trim_begin), - count * sbi->sect_per_clus, GFP_NOFS, 0); - if (err) - goto unlock; - - trimmed_total += count; - } - - /* set next start point of the free hole */ - trim_begin = trim_end = next_free_clu; - } - - if (next_free_clu >= clu_end) - break; - - if (fatal_signal_pending(current)) { - err = -ERESTARTSYS; - goto unlock; - } - - next_free_clu = exfat_find_free_bitmap(sb, next_free_clu + 1); - } while (next_free_clu != EXFAT_EOF_CLUSTER && - next_free_clu > trim_end); - - /* try to trim remainder */ - count = trim_end - trim_begin + 1; - if (count >= trim_minlen) { - err = sb_issue_discard(sb, exfat_cluster_to_sector(sbi, trim_begin), - count * sbi->sect_per_clus, GFP_NOFS, 0); - if (err) - goto unlock; - - trimmed_total += count; - } - -unlock: - mutex_unlock(&sbi->bitmap_lock); - range->len = trimmed_total << sbi->cluster_size_bits; - - return err; -} diff --git a/fs/exfat/cache.c b/fs/exfat/cache.c deleted file mode 100644 index 5a2f119b7..000000000 --- a/fs/exfat/cache.c +++ /dev/null @@ -1,314 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * linux/fs/fat/cache.c - * - * Written 1992,1993 by Werner Almesberger - * - * Mar 1999. AV. Changed cache, so that it uses the starting cluster instead - * of inode number. - * May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers. - * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include - -#include "exfat_raw.h" -#include "exfat_fs.h" - -#define EXFAT_MAX_CACHE 16 - -struct exfat_cache { - struct list_head cache_list; - unsigned int nr_contig; /* number of contiguous clusters */ - unsigned int fcluster; /* cluster number in the file. */ - unsigned int dcluster; /* cluster number on disk. */ -}; - -struct exfat_cache_id { - unsigned int id; - unsigned int nr_contig; - unsigned int fcluster; - unsigned int dcluster; -}; - -static struct kmem_cache *exfat_cachep; - -static void exfat_cache_init_once(void *c) -{ - struct exfat_cache *cache = (struct exfat_cache *)c; - - INIT_LIST_HEAD(&cache->cache_list); -} - -int exfat_cache_init(void) -{ - exfat_cachep = kmem_cache_create("exfat_cache", - sizeof(struct exfat_cache), - 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, - exfat_cache_init_once); - if (!exfat_cachep) - return -ENOMEM; - return 0; -} - -void exfat_cache_shutdown(void) -{ - if (!exfat_cachep) - return; - kmem_cache_destroy(exfat_cachep); -} - -static inline struct exfat_cache *exfat_cache_alloc(void) -{ - return kmem_cache_alloc(exfat_cachep, GFP_NOFS); -} - -static inline void exfat_cache_free(struct exfat_cache *cache) -{ - WARN_ON(!list_empty(&cache->cache_list)); - kmem_cache_free(exfat_cachep, cache); -} - -static inline void exfat_cache_update_lru(struct inode *inode, - struct exfat_cache *cache) -{ - struct exfat_inode_info *ei = EXFAT_I(inode); - - if (ei->cache_lru.next != &cache->cache_list) - list_move(&cache->cache_list, &ei->cache_lru); -} - -static unsigned int exfat_cache_lookup(struct inode *inode, - unsigned int fclus, struct exfat_cache_id *cid, - unsigned int *cached_fclus, unsigned int *cached_dclus) -{ - struct exfat_inode_info *ei = EXFAT_I(inode); - static struct exfat_cache nohit = { .fcluster = 0, }; - struct exfat_cache *hit = &nohit, *p; - unsigned int offset = EXFAT_EOF_CLUSTER; - - spin_lock(&ei->cache_lru_lock); - list_for_each_entry(p, &ei->cache_lru, cache_list) { - /* Find the cache of "fclus" or nearest cache. */ - if (p->fcluster <= fclus && hit->fcluster < p->fcluster) { - hit = p; - if (hit->fcluster + hit->nr_contig < fclus) { - offset = hit->nr_contig; - } else { - offset = fclus - hit->fcluster; - break; - } - } - } - if (hit != &nohit) { - exfat_cache_update_lru(inode, hit); - - cid->id = ei->cache_valid_id; - cid->nr_contig = hit->nr_contig; - cid->fcluster = hit->fcluster; - cid->dcluster = hit->dcluster; - *cached_fclus = cid->fcluster + offset; - *cached_dclus = cid->dcluster + offset; - } - spin_unlock(&ei->cache_lru_lock); - - return offset; -} - -static struct exfat_cache *exfat_cache_merge(struct inode *inode, - struct exfat_cache_id *new) -{ - struct exfat_inode_info *ei = EXFAT_I(inode); - struct exfat_cache *p; - - list_for_each_entry(p, &ei->cache_lru, cache_list) { - /* Find the same part as "new" in cluster-chain. */ - if (p->fcluster == new->fcluster) { - if (new->nr_contig > p->nr_contig) - p->nr_contig = new->nr_contig; - return p; - } - } - return NULL; -} - -static void exfat_cache_add(struct inode *inode, - struct exfat_cache_id *new) -{ - struct exfat_inode_info *ei = EXFAT_I(inode); - struct exfat_cache *cache, *tmp; - - if (new->fcluster == EXFAT_EOF_CLUSTER) /* dummy cache */ - return; - - spin_lock(&ei->cache_lru_lock); - if (new->id != EXFAT_CACHE_VALID && - new->id != ei->cache_valid_id) - goto unlock; /* this cache was invalidated */ - - cache = exfat_cache_merge(inode, new); - if (cache == NULL) { - if (ei->nr_caches < EXFAT_MAX_CACHE) { - ei->nr_caches++; - spin_unlock(&ei->cache_lru_lock); - - tmp = exfat_cache_alloc(); - if (!tmp) { - spin_lock(&ei->cache_lru_lock); - ei->nr_caches--; - spin_unlock(&ei->cache_lru_lock); - return; - } - - spin_lock(&ei->cache_lru_lock); - cache = exfat_cache_merge(inode, new); - if (cache != NULL) { - ei->nr_caches--; - exfat_cache_free(tmp); - goto out_update_lru; - } - cache = tmp; - } else { - struct list_head *p = ei->cache_lru.prev; - - cache = list_entry(p, - struct exfat_cache, cache_list); - } - cache->fcluster = new->fcluster; - cache->dcluster = new->dcluster; - cache->nr_contig = new->nr_contig; - } -out_update_lru: - exfat_cache_update_lru(inode, cache); -unlock: - spin_unlock(&ei->cache_lru_lock); -} - -/* - * Cache invalidation occurs rarely, thus the LRU chain is not updated. It - * fixes itself after a while. - */ -static void __exfat_cache_inval_inode(struct inode *inode) -{ - struct exfat_inode_info *ei = EXFAT_I(inode); - struct exfat_cache *cache; - - while (!list_empty(&ei->cache_lru)) { - cache = list_entry(ei->cache_lru.next, - struct exfat_cache, cache_list); - list_del_init(&cache->cache_list); - ei->nr_caches--; - exfat_cache_free(cache); - } - /* Update. The copy of caches before this id is discarded. */ - ei->cache_valid_id++; - if (ei->cache_valid_id == EXFAT_CACHE_VALID) - ei->cache_valid_id++; -} - -void exfat_cache_inval_inode(struct inode *inode) -{ - struct exfat_inode_info *ei = EXFAT_I(inode); - - spin_lock(&ei->cache_lru_lock); - __exfat_cache_inval_inode(inode); - spin_unlock(&ei->cache_lru_lock); -} - -static inline int cache_contiguous(struct exfat_cache_id *cid, - unsigned int dclus) -{ - cid->nr_contig++; - return cid->dcluster + cid->nr_contig == dclus; -} - -static inline void cache_init(struct exfat_cache_id *cid, - unsigned int fclus, unsigned int dclus) -{ - cid->id = EXFAT_CACHE_VALID; - cid->fcluster = fclus; - cid->dcluster = dclus; - cid->nr_contig = 0; -} - -int exfat_get_cluster(struct inode *inode, unsigned int cluster, - unsigned int *fclus, unsigned int *dclus, - unsigned int *last_dclus, int allow_eof) -{ - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - unsigned int limit = sbi->num_clusters; - struct exfat_inode_info *ei = EXFAT_I(inode); - struct exfat_cache_id cid; - unsigned int content; - - if (ei->start_clu == EXFAT_FREE_CLUSTER) { - exfat_fs_error(sb, - "invalid access to exfat cache (entry 0x%08x)", - ei->start_clu); - return -EIO; - } - - *fclus = 0; - *dclus = ei->start_clu; - *last_dclus = *dclus; - - /* - * Don`t use exfat_cache if zero offset or non-cluster allocation - */ - if (cluster == 0 || *dclus == EXFAT_EOF_CLUSTER) - return 0; - - cache_init(&cid, EXFAT_EOF_CLUSTER, EXFAT_EOF_CLUSTER); - - if (exfat_cache_lookup(inode, cluster, &cid, fclus, dclus) == - EXFAT_EOF_CLUSTER) { - /* - * dummy, always not contiguous - * This is reinitialized by cache_init(), later. - */ - WARN_ON(cid.id != EXFAT_CACHE_VALID || - cid.fcluster != EXFAT_EOF_CLUSTER || - cid.dcluster != EXFAT_EOF_CLUSTER || - cid.nr_contig != 0); - } - - if (*fclus == cluster) - return 0; - - while (*fclus < cluster) { - /* prevent the infinite loop of cluster chain */ - if (*fclus > limit) { - exfat_fs_error(sb, - "detected the cluster chain loop (i_pos %u)", - (*fclus)); - return -EIO; - } - - if (exfat_ent_get(sb, *dclus, &content)) - return -EIO; - - *last_dclus = *dclus; - *dclus = content; - (*fclus)++; - - if (content == EXFAT_EOF_CLUSTER) { - if (!allow_eof) { - exfat_fs_error(sb, - "invalid cluster chain (i_pos %u, last_clus 0x%08x is EOF)", - *fclus, (*last_dclus)); - return -EIO; - } - - break; - } - - if (!cache_contiguous(&cid, *dclus)) - cache_init(&cid, *fclus, *dclus); - } - - exfat_cache_add(inode, &cid); - return 0; -} diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c deleted file mode 100644 index cb1c0d8c1..000000000 --- a/fs/exfat/dir.c +++ /dev/null @@ -1,1203 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include - -#include "exfat_raw.h" -#include "exfat_fs.h" - -static int exfat_extract_uni_name(struct exfat_dentry *ep, - unsigned short *uniname) -{ - int i, len = 0; - - for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) { - *uniname = le16_to_cpu(ep->dentry.name.unicode_0_14[i]); - if (*uniname == 0x0) - return len; - uniname++; - len++; - } - - *uniname = 0x0; - return len; - -} - -static void exfat_get_uniname_from_ext_entry(struct super_block *sb, - struct exfat_chain *p_dir, int entry, unsigned short *uniname) -{ - int i; - struct exfat_entry_set_cache *es; - - es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES); - if (!es) - return; - - /* - * First entry : file entry - * Second entry : stream-extension entry - * Third entry : first file-name entry - * So, the index of first file-name dentry should start from 2. - */ - for (i = 2; i < es->num_entries; i++) { - struct exfat_dentry *ep = exfat_get_dentry_cached(es, i); - - /* end of name entry */ - if (exfat_get_entry_type(ep) != TYPE_EXTEND) - break; - - exfat_extract_uni_name(ep, uniname); - uniname += EXFAT_FILE_NAME_LEN; - } - - exfat_free_dentry_set(es, false); -} - -/* read a directory entry from the opened directory */ -static int exfat_readdir(struct inode *inode, loff_t *cpos, struct exfat_dir_entry *dir_entry) -{ - int i, dentries_per_clu, dentries_per_clu_bits = 0, num_ext; - unsigned int type, clu_offset, max_dentries; - sector_t sector; - struct exfat_chain dir, clu; - struct exfat_uni_name uni_name; - struct exfat_dentry *ep; - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_inode_info *ei = EXFAT_I(inode); - unsigned int dentry = EXFAT_B_TO_DEN(*cpos) & 0xFFFFFFFF; - struct buffer_head *bh; - - /* check if the given file ID is opened */ - if (ei->type != TYPE_DIR) - return -EPERM; - - if (ei->entry == -1) - exfat_chain_set(&dir, sbi->root_dir, 0, ALLOC_FAT_CHAIN); - else - exfat_chain_set(&dir, ei->start_clu, - EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); - - dentries_per_clu = sbi->dentries_per_clu; - dentries_per_clu_bits = ilog2(dentries_per_clu); - max_dentries = (unsigned int)min_t(u64, MAX_EXFAT_DENTRIES, - (u64)sbi->num_clusters << dentries_per_clu_bits); - - clu_offset = dentry >> dentries_per_clu_bits; - exfat_chain_dup(&clu, &dir); - - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - clu.dir += clu_offset; - clu.size -= clu_offset; - } else { - /* hint_information */ - if (clu_offset > 0 && ei->hint_bmap.off != EXFAT_EOF_CLUSTER && - ei->hint_bmap.off > 0 && clu_offset >= ei->hint_bmap.off) { - clu_offset -= ei->hint_bmap.off; - clu.dir = ei->hint_bmap.clu; - } - - while (clu_offset > 0) { - if (exfat_get_next_cluster(sb, &(clu.dir))) - return -EIO; - - clu_offset--; - } - } - - while (clu.dir != EXFAT_EOF_CLUSTER && dentry < max_dentries) { - i = dentry & (dentries_per_clu - 1); - - for ( ; i < dentries_per_clu; i++, dentry++) { - ep = exfat_get_dentry(sb, &clu, i, &bh, §or); - if (!ep) - return -EIO; - - type = exfat_get_entry_type(ep); - if (type == TYPE_UNUSED) { - brelse(bh); - break; - } - - if (type != TYPE_FILE && type != TYPE_DIR) { - brelse(bh); - continue; - } - - num_ext = ep->dentry.file.num_ext; - dir_entry->attr = le16_to_cpu(ep->dentry.file.attr); - exfat_get_entry_time(sbi, &dir_entry->crtime, - ep->dentry.file.create_tz, - ep->dentry.file.create_time, - ep->dentry.file.create_date, - ep->dentry.file.create_time_cs); - exfat_get_entry_time(sbi, &dir_entry->mtime, - ep->dentry.file.modify_tz, - ep->dentry.file.modify_time, - ep->dentry.file.modify_date, - ep->dentry.file.modify_time_cs); - exfat_get_entry_time(sbi, &dir_entry->atime, - ep->dentry.file.access_tz, - ep->dentry.file.access_time, - ep->dentry.file.access_date, - 0); - - *uni_name.name = 0x0; - exfat_get_uniname_from_ext_entry(sb, &clu, i, - uni_name.name); - exfat_utf16_to_nls(sb, &uni_name, - dir_entry->namebuf.lfn, - dir_entry->namebuf.lfnbuf_len); - brelse(bh); - - ep = exfat_get_dentry(sb, &clu, i + 1, &bh, NULL); - if (!ep) - return -EIO; - dir_entry->size = - le64_to_cpu(ep->dentry.stream.valid_size); - dir_entry->entry = dentry; - brelse(bh); - - ei->hint_bmap.off = dentry >> dentries_per_clu_bits; - ei->hint_bmap.clu = clu.dir; - - *cpos = EXFAT_DEN_TO_B(dentry + 1 + num_ext); - return 0; - } - - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - if (--clu.size > 0) - clu.dir++; - else - clu.dir = EXFAT_EOF_CLUSTER; - } else { - if (exfat_get_next_cluster(sb, &(clu.dir))) - return -EIO; - } - } - - dir_entry->namebuf.lfn[0] = '\0'; - *cpos = EXFAT_DEN_TO_B(dentry); - return 0; -} - -static void exfat_init_namebuf(struct exfat_dentry_namebuf *nb) -{ - nb->lfn = NULL; - nb->lfnbuf_len = 0; -} - -static int exfat_alloc_namebuf(struct exfat_dentry_namebuf *nb) -{ - nb->lfn = __getname(); - if (!nb->lfn) - return -ENOMEM; - nb->lfnbuf_len = MAX_VFSNAME_BUF_SIZE; - return 0; -} - -static void exfat_free_namebuf(struct exfat_dentry_namebuf *nb) -{ - if (!nb->lfn) - return; - - __putname(nb->lfn); - exfat_init_namebuf(nb); -} - -/* skip iterating emit_dots when dir is empty */ -#define ITER_POS_FILLED_DOTS (2) -static int exfat_iterate(struct file *filp, struct dir_context *ctx) -{ - struct inode *inode = filp->f_path.dentry->d_inode; - struct super_block *sb = inode->i_sb; - struct inode *tmp; - struct exfat_dir_entry de; - struct exfat_dentry_namebuf *nb = &(de.namebuf); - struct exfat_inode_info *ei = EXFAT_I(inode); - unsigned long inum; - loff_t cpos, i_pos; - int err = 0, fake_offset = 0; - - exfat_init_namebuf(nb); - mutex_lock(&EXFAT_SB(sb)->s_lock); - - cpos = ctx->pos; - if (!dir_emit_dots(filp, ctx)) - goto unlock; - - if (ctx->pos == ITER_POS_FILLED_DOTS) { - cpos = 0; - fake_offset = 1; - } - - if (cpos & (DENTRY_SIZE - 1)) { - err = -ENOENT; - goto unlock; - } - - /* name buffer should be allocated before use */ - err = exfat_alloc_namebuf(nb); - if (err) - goto unlock; -get_new: - if (ei->flags == ALLOC_NO_FAT_CHAIN && cpos >= i_size_read(inode)) - goto end_of_dir; - - err = exfat_readdir(inode, &cpos, &de); - if (err) { - /* - * At least we tried to read a sector. Move cpos to next sector - * position (should be aligned). - */ - if (err == -EIO) { - cpos += 1 << (sb->s_blocksize_bits); - cpos &= ~(sb->s_blocksize - 1); - } - - err = -EIO; - goto end_of_dir; - } - - if (!nb->lfn[0]) - goto end_of_dir; - - i_pos = ((loff_t)ei->start_clu << 32) | (de.entry & 0xffffffff); - tmp = exfat_iget(sb, i_pos); - if (tmp) { - inum = tmp->i_ino; - iput(tmp); - } else { - inum = iunique(sb, EXFAT_ROOT_INO); - } - - /* - * Before calling dir_emit(), sb_lock should be released. - * Because page fault can occur in dir_emit() when the size - * of buffer given from user is larger than one page size. - */ - mutex_unlock(&EXFAT_SB(sb)->s_lock); - if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum, - (de.attr & ATTR_SUBDIR) ? DT_DIR : DT_REG)) - goto out_unlocked; - mutex_lock(&EXFAT_SB(sb)->s_lock); - ctx->pos = cpos; - goto get_new; - -end_of_dir: - if (!cpos && fake_offset) - cpos = ITER_POS_FILLED_DOTS; - ctx->pos = cpos; -unlock: - mutex_unlock(&EXFAT_SB(sb)->s_lock); -out_unlocked: - /* - * To improve performance, free namebuf after unlock sb_lock. - * If namebuf is not allocated, this function do nothing - */ - exfat_free_namebuf(nb); - return err; -} - -const struct file_operations exfat_dir_operations = { - .llseek = generic_file_llseek, - .read = generic_read_dir, - .iterate = exfat_iterate, - .unlocked_ioctl = exfat_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = exfat_compat_ioctl, -#endif - .fsync = exfat_file_fsync, -}; - -int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu) -{ - int ret; - - exfat_chain_set(clu, EXFAT_EOF_CLUSTER, 0, ALLOC_NO_FAT_CHAIN); - - ret = exfat_alloc_cluster(inode, 1, clu, IS_DIRSYNC(inode)); - if (ret) - return ret; - - return exfat_zeroed_cluster(inode, clu->dir); -} - -int exfat_calc_num_entries(struct exfat_uni_name *p_uniname) -{ - int len; - - len = p_uniname->name_len; - if (len == 0) - return -EINVAL; - - /* 1 file entry + 1 stream entry + name entries */ - return ((len - 1) / EXFAT_FILE_NAME_LEN + 3); -} - -unsigned int exfat_get_entry_type(struct exfat_dentry *ep) -{ - if (ep->type == EXFAT_UNUSED) - return TYPE_UNUSED; - if (IS_EXFAT_DELETED(ep->type)) - return TYPE_DELETED; - if (ep->type == EXFAT_INVAL) - return TYPE_INVALID; - if (IS_EXFAT_CRITICAL_PRI(ep->type)) { - if (ep->type == EXFAT_BITMAP) - return TYPE_BITMAP; - if (ep->type == EXFAT_UPCASE) - return TYPE_UPCASE; - if (ep->type == EXFAT_VOLUME) - return TYPE_VOLUME; - if (ep->type == EXFAT_FILE) { - if (le16_to_cpu(ep->dentry.file.attr) & ATTR_SUBDIR) - return TYPE_DIR; - return TYPE_FILE; - } - return TYPE_CRITICAL_PRI; - } - if (IS_EXFAT_BENIGN_PRI(ep->type)) { - if (ep->type == EXFAT_GUID) - return TYPE_GUID; - if (ep->type == EXFAT_PADDING) - return TYPE_PADDING; - if (ep->type == EXFAT_ACLTAB) - return TYPE_ACLTAB; - return TYPE_BENIGN_PRI; - } - if (IS_EXFAT_CRITICAL_SEC(ep->type)) { - if (ep->type == EXFAT_STREAM) - return TYPE_STREAM; - if (ep->type == EXFAT_NAME) - return TYPE_EXTEND; - if (ep->type == EXFAT_ACL) - return TYPE_ACL; - return TYPE_CRITICAL_SEC; - } - return TYPE_BENIGN_SEC; -} - -static void exfat_set_entry_type(struct exfat_dentry *ep, unsigned int type) -{ - if (type == TYPE_UNUSED) { - ep->type = EXFAT_UNUSED; - } else if (type == TYPE_DELETED) { - ep->type &= EXFAT_DELETE; - } else if (type == TYPE_STREAM) { - ep->type = EXFAT_STREAM; - } else if (type == TYPE_EXTEND) { - ep->type = EXFAT_NAME; - } else if (type == TYPE_BITMAP) { - ep->type = EXFAT_BITMAP; - } else if (type == TYPE_UPCASE) { - ep->type = EXFAT_UPCASE; - } else if (type == TYPE_VOLUME) { - ep->type = EXFAT_VOLUME; - } else if (type == TYPE_DIR) { - ep->type = EXFAT_FILE; - ep->dentry.file.attr = cpu_to_le16(ATTR_SUBDIR); - } else if (type == TYPE_FILE) { - ep->type = EXFAT_FILE; - ep->dentry.file.attr = cpu_to_le16(ATTR_ARCHIVE); - } -} - -static void exfat_init_stream_entry(struct exfat_dentry *ep, - unsigned char flags, unsigned int start_clu, - unsigned long long size) -{ - exfat_set_entry_type(ep, TYPE_STREAM); - ep->dentry.stream.flags = flags; - ep->dentry.stream.start_clu = cpu_to_le32(start_clu); - ep->dentry.stream.valid_size = cpu_to_le64(size); - ep->dentry.stream.size = cpu_to_le64(size); -} - -static void exfat_init_name_entry(struct exfat_dentry *ep, - unsigned short *uniname) -{ - int i; - - exfat_set_entry_type(ep, TYPE_EXTEND); - ep->dentry.name.flags = 0x0; - - for (i = 0; i < EXFAT_FILE_NAME_LEN; i++) { - if (*uniname != 0x0) { - ep->dentry.name.unicode_0_14[i] = cpu_to_le16(*uniname); - uniname++; - } else { - ep->dentry.name.unicode_0_14[i] = 0x0; - } - } -} - -int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir, - int entry, unsigned int type, unsigned int start_clu, - unsigned long long size) -{ - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct timespec64 ts = current_time(inode); - sector_t sector; - struct exfat_dentry *ep; - struct buffer_head *bh; - - /* - * We cannot use exfat_get_dentry_set here because file ep is not - * initialized yet. - */ - ep = exfat_get_dentry(sb, p_dir, entry, &bh, §or); - if (!ep) - return -EIO; - - exfat_set_entry_type(ep, type); - exfat_set_entry_time(sbi, &ts, - &ep->dentry.file.create_tz, - &ep->dentry.file.create_time, - &ep->dentry.file.create_date, - &ep->dentry.file.create_time_cs); - exfat_set_entry_time(sbi, &ts, - &ep->dentry.file.modify_tz, - &ep->dentry.file.modify_time, - &ep->dentry.file.modify_date, - &ep->dentry.file.modify_time_cs); - exfat_set_entry_time(sbi, &ts, - &ep->dentry.file.access_tz, - &ep->dentry.file.access_time, - &ep->dentry.file.access_date, - NULL); - - exfat_update_bh(bh, IS_DIRSYNC(inode)); - brelse(bh); - - ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh, §or); - if (!ep) - return -EIO; - - exfat_init_stream_entry(ep, - (type == TYPE_FILE) ? ALLOC_FAT_CHAIN : ALLOC_NO_FAT_CHAIN, - start_clu, size); - exfat_update_bh(bh, IS_DIRSYNC(inode)); - brelse(bh); - - return 0; -} - -int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir, - int entry) -{ - struct super_block *sb = inode->i_sb; - int ret = 0; - int i, num_entries; - sector_t sector; - u16 chksum; - struct exfat_dentry *ep, *fep; - struct buffer_head *fbh, *bh; - - fep = exfat_get_dentry(sb, p_dir, entry, &fbh, §or); - if (!fep) - return -EIO; - - num_entries = fep->dentry.file.num_ext + 1; - chksum = exfat_calc_chksum16(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY); - - for (i = 1; i < num_entries; i++) { - ep = exfat_get_dentry(sb, p_dir, entry + i, &bh, NULL); - if (!ep) { - ret = -EIO; - goto release_fbh; - } - chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum, - CS_DEFAULT); - brelse(bh); - } - - fep->dentry.file.checksum = cpu_to_le16(chksum); - exfat_update_bh(fbh, IS_DIRSYNC(inode)); -release_fbh: - brelse(fbh); - return ret; -} - -int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir, - int entry, int num_entries, struct exfat_uni_name *p_uniname) -{ - struct super_block *sb = inode->i_sb; - int i; - sector_t sector; - unsigned short *uniname = p_uniname->name; - struct exfat_dentry *ep; - struct buffer_head *bh; - int sync = IS_DIRSYNC(inode); - - ep = exfat_get_dentry(sb, p_dir, entry, &bh, §or); - if (!ep) - return -EIO; - - ep->dentry.file.num_ext = (unsigned char)(num_entries - 1); - exfat_update_bh(bh, sync); - brelse(bh); - - ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh, §or); - if (!ep) - return -EIO; - - ep->dentry.stream.name_len = p_uniname->name_len; - ep->dentry.stream.name_hash = cpu_to_le16(p_uniname->name_hash); - exfat_update_bh(bh, sync); - brelse(bh); - - for (i = EXFAT_FIRST_CLUSTER; i < num_entries; i++) { - ep = exfat_get_dentry(sb, p_dir, entry + i, &bh, §or); - if (!ep) - return -EIO; - - exfat_init_name_entry(ep, uniname); - exfat_update_bh(bh, sync); - brelse(bh); - uniname += EXFAT_FILE_NAME_LEN; - } - - exfat_update_dir_chksum(inode, p_dir, entry); - return 0; -} - -int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir, - int entry, int order, int num_entries) -{ - struct super_block *sb = inode->i_sb; - int i; - sector_t sector; - struct exfat_dentry *ep; - struct buffer_head *bh; - - for (i = order; i < num_entries; i++) { - ep = exfat_get_dentry(sb, p_dir, entry + i, &bh, §or); - if (!ep) - return -EIO; - - exfat_set_entry_type(ep, TYPE_DELETED); - exfat_update_bh(bh, IS_DIRSYNC(inode)); - brelse(bh); - } - - return 0; -} - -void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es) -{ - int chksum_type = CS_DIR_ENTRY, i; - unsigned short chksum = 0; - struct exfat_dentry *ep; - - for (i = 0; i < es->num_entries; i++) { - ep = exfat_get_dentry_cached(es, i); - chksum = exfat_calc_chksum16(ep, DENTRY_SIZE, chksum, - chksum_type); - chksum_type = CS_DEFAULT; - } - ep = exfat_get_dentry_cached(es, 0); - ep->dentry.file.checksum = cpu_to_le16(chksum); - es->modified = true; -} - -int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync) -{ - int i, err = 0; - - if (es->modified) - err = exfat_update_bhs(es->bh, es->num_bh, sync); - - for (i = 0; i < es->num_bh; i++) - if (err) - bforget(es->bh[i]); - else - brelse(es->bh[i]); - kfree(es); - return err; -} - -static int exfat_walk_fat_chain(struct super_block *sb, - struct exfat_chain *p_dir, unsigned int byte_offset, - unsigned int *clu) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - unsigned int clu_offset; - unsigned int cur_clu; - - clu_offset = EXFAT_B_TO_CLU(byte_offset, sbi); - cur_clu = p_dir->dir; - - if (p_dir->flags == ALLOC_NO_FAT_CHAIN) { - cur_clu += clu_offset; - } else { - while (clu_offset > 0) { - if (exfat_get_next_cluster(sb, &cur_clu)) - return -EIO; - if (cur_clu == EXFAT_EOF_CLUSTER) { - exfat_fs_error(sb, - "invalid dentry access beyond EOF (clu : %u, eidx : %d)", - p_dir->dir, - EXFAT_B_TO_DEN(byte_offset)); - return -EIO; - } - clu_offset--; - } - } - - *clu = cur_clu; - return 0; -} - -int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir, - int entry, sector_t *sector, int *offset) -{ - int ret; - unsigned int off, clu = 0; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - - off = EXFAT_DEN_TO_B(entry); - - ret = exfat_walk_fat_chain(sb, p_dir, off, &clu); - if (ret) - return ret; - - /* byte offset in cluster */ - off = EXFAT_CLU_OFFSET(off, sbi); - - /* byte offset in sector */ - *offset = EXFAT_BLK_OFFSET(off, sb); - - /* sector offset in cluster */ - *sector = EXFAT_B_TO_BLK(off, sb); - *sector += exfat_cluster_to_sector(sbi, clu); - return 0; -} - -#define EXFAT_MAX_RA_SIZE (128*1024) -static int exfat_dir_readahead(struct super_block *sb, sector_t sec) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct buffer_head *bh; - unsigned int max_ra_count = EXFAT_MAX_RA_SIZE >> sb->s_blocksize_bits; - unsigned int page_ra_count = PAGE_SIZE >> sb->s_blocksize_bits; - unsigned int adj_ra_count = max(sbi->sect_per_clus, page_ra_count); - unsigned int ra_count = min(adj_ra_count, max_ra_count); - - /* Read-ahead is not required */ - if (sbi->sect_per_clus == 1) - return 0; - - if (sec < sbi->data_start_sector) { - exfat_err(sb, "requested sector is invalid(sect:%llu, root:%llu)", - (unsigned long long)sec, sbi->data_start_sector); - return -EIO; - } - - /* Not sector aligned with ra_count, resize ra_count to page size */ - if ((sec - sbi->data_start_sector) & (ra_count - 1)) - ra_count = page_ra_count; - - bh = sb_find_get_block(sb, sec); - if (!bh || !buffer_uptodate(bh)) { - unsigned int i; - - for (i = 0; i < ra_count; i++) - sb_breadahead(sb, (sector_t)(sec + i)); - } - brelse(bh); - return 0; -} - -struct exfat_dentry *exfat_get_dentry(struct super_block *sb, - struct exfat_chain *p_dir, int entry, struct buffer_head **bh, - sector_t *sector) -{ - unsigned int dentries_per_page = EXFAT_B_TO_DEN(PAGE_SIZE); - int off; - sector_t sec; - - if (p_dir->dir == DIR_DELETED) { - exfat_err(sb, "abnormal access to deleted dentry"); - return NULL; - } - - if (exfat_find_location(sb, p_dir, entry, &sec, &off)) - return NULL; - - if (p_dir->dir != EXFAT_FREE_CLUSTER && - !(entry & (dentries_per_page - 1))) - exfat_dir_readahead(sb, sec); - - *bh = sb_bread(sb, sec); - if (!*bh) - return NULL; - - if (sector) - *sector = sec; - return (struct exfat_dentry *)((*bh)->b_data + off); -} - -enum exfat_validate_dentry_mode { - ES_MODE_STARTED, - ES_MODE_GET_FILE_ENTRY, - ES_MODE_GET_STRM_ENTRY, - ES_MODE_GET_NAME_ENTRY, - ES_MODE_GET_CRITICAL_SEC_ENTRY, -}; - -static bool exfat_validate_entry(unsigned int type, - enum exfat_validate_dentry_mode *mode) -{ - if (type == TYPE_UNUSED || type == TYPE_DELETED) - return false; - - switch (*mode) { - case ES_MODE_STARTED: - if (type != TYPE_FILE && type != TYPE_DIR) - return false; - *mode = ES_MODE_GET_FILE_ENTRY; - return true; - case ES_MODE_GET_FILE_ENTRY: - if (type != TYPE_STREAM) - return false; - *mode = ES_MODE_GET_STRM_ENTRY; - return true; - case ES_MODE_GET_STRM_ENTRY: - if (type != TYPE_EXTEND) - return false; - *mode = ES_MODE_GET_NAME_ENTRY; - return true; - case ES_MODE_GET_NAME_ENTRY: - if (type == TYPE_STREAM) - return false; - if (type != TYPE_EXTEND) { - if (!(type & TYPE_CRITICAL_SEC)) - return false; - *mode = ES_MODE_GET_CRITICAL_SEC_ENTRY; - } - return true; - case ES_MODE_GET_CRITICAL_SEC_ENTRY: - if (type == TYPE_EXTEND || type == TYPE_STREAM) - return false; - if ((type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC) - return false; - return true; - default: - WARN_ON_ONCE(1); - return false; - } -} - -struct exfat_dentry *exfat_get_dentry_cached( - struct exfat_entry_set_cache *es, int num) -{ - int off = es->start_off + num * DENTRY_SIZE; - struct buffer_head *bh = es->bh[EXFAT_B_TO_BLK(off, es->sb)]; - char *p = bh->b_data + EXFAT_BLK_OFFSET(off, es->sb); - - return (struct exfat_dentry *)p; -} - -/* - * Returns a set of dentries for a file or dir. - * - * Note It provides a direct pointer to bh->data via exfat_get_dentry_cached(). - * User should call exfat_get_dentry_set() after setting 'modified' to apply - * changes made in this entry set to the real device. - * - * in: - * sb+p_dir+entry: indicates a file/dir - * type: specifies how many dentries should be included. - * return: - * pointer of entry set on success, - * NULL on failure. - */ -struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb, - struct exfat_chain *p_dir, int entry, unsigned int type) -{ - int ret, i, num_bh; - unsigned int off, byte_offset, clu = 0; - sector_t sec; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_entry_set_cache *es; - struct exfat_dentry *ep; - int num_entries; - enum exfat_validate_dentry_mode mode = ES_MODE_STARTED; - struct buffer_head *bh; - - if (p_dir->dir == DIR_DELETED) { - exfat_err(sb, "access to deleted dentry"); - return NULL; - } - - byte_offset = EXFAT_DEN_TO_B(entry); - ret = exfat_walk_fat_chain(sb, p_dir, byte_offset, &clu); - if (ret) - return NULL; - - es = kzalloc(sizeof(*es), GFP_KERNEL); - if (!es) - return NULL; - es->sb = sb; - es->modified = false; - - /* byte offset in cluster */ - byte_offset = EXFAT_CLU_OFFSET(byte_offset, sbi); - - /* byte offset in sector */ - off = EXFAT_BLK_OFFSET(byte_offset, sb); - es->start_off = off; - - /* sector offset in cluster */ - sec = EXFAT_B_TO_BLK(byte_offset, sb); - sec += exfat_cluster_to_sector(sbi, clu); - - bh = sb_bread(sb, sec); - if (!bh) - goto free_es; - es->bh[es->num_bh++] = bh; - - ep = exfat_get_dentry_cached(es, 0); - if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode)) - goto free_es; - - num_entries = type == ES_ALL_ENTRIES ? - ep->dentry.file.num_ext + 1 : type; - es->num_entries = num_entries; - - num_bh = EXFAT_B_TO_BLK_ROUND_UP(off + num_entries * DENTRY_SIZE, sb); - for (i = 1; i < num_bh; i++) { - /* get the next sector */ - if (exfat_is_last_sector_in_cluster(sbi, sec)) { - if (p_dir->flags == ALLOC_NO_FAT_CHAIN) - clu++; - else if (exfat_get_next_cluster(sb, &clu)) - goto free_es; - sec = exfat_cluster_to_sector(sbi, clu); - } else { - sec++; - } - - bh = sb_bread(sb, sec); - if (!bh) - goto free_es; - es->bh[es->num_bh++] = bh; - } - - /* validiate cached dentries */ - for (i = 1; i < num_entries; i++) { - ep = exfat_get_dentry_cached(es, i); - if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode)) - goto free_es; - } - return es; - -free_es: - exfat_free_dentry_set(es, false); - return NULL; -} - -enum { - DIRENT_STEP_FILE, - DIRENT_STEP_STRM, - DIRENT_STEP_NAME, - DIRENT_STEP_SECD, -}; - -/* - * @ei: inode info of parent directory - * @p_dir: directory structure of parent directory - * @num_entries:entry size of p_uniname - * @hint_opt: If p_uniname is found, filled with optimized dir/entry - * for traversing cluster chain. - * @return: - * >= 0: file directory entry position where the name exists - * -ENOENT: entry with the name does not exist - * -EIO: I/O error - */ -int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, - struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname, - int num_entries, unsigned int type, struct exfat_hint *hint_opt) -{ - int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len; - int order, step, name_len = 0; - int dentries_per_clu, num_empty = 0; - unsigned int entry_type; - unsigned short *uniname = NULL; - struct exfat_chain clu; - struct exfat_hint *hint_stat = &ei->hint_stat; - struct exfat_hint_femp candi_empty; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - - dentries_per_clu = sbi->dentries_per_clu; - - exfat_chain_dup(&clu, p_dir); - - if (hint_stat->eidx) { - clu.dir = hint_stat->clu; - dentry = hint_stat->eidx; - end_eidx = dentry; - } - - candi_empty.eidx = EXFAT_HINT_NONE; -rewind: - order = 0; - step = DIRENT_STEP_FILE; - while (clu.dir != EXFAT_EOF_CLUSTER) { - i = dentry & (dentries_per_clu - 1); - for (; i < dentries_per_clu; i++, dentry++) { - struct exfat_dentry *ep; - struct buffer_head *bh; - - if (rewind && dentry == end_eidx) - goto not_found; - - ep = exfat_get_dentry(sb, &clu, i, &bh, NULL); - if (!ep) - return -EIO; - - entry_type = exfat_get_entry_type(ep); - - if (entry_type == TYPE_UNUSED || - entry_type == TYPE_DELETED) { - step = DIRENT_STEP_FILE; - - num_empty++; - if (candi_empty.eidx == EXFAT_HINT_NONE && - num_empty == 1) { - exfat_chain_set(&candi_empty.cur, - clu.dir, clu.size, clu.flags); - } - - if (candi_empty.eidx == EXFAT_HINT_NONE && - num_empty >= num_entries) { - candi_empty.eidx = - dentry - (num_empty - 1); - WARN_ON(candi_empty.eidx < 0); - candi_empty.count = num_empty; - - if (ei->hint_femp.eidx == - EXFAT_HINT_NONE || - candi_empty.eidx <= - ei->hint_femp.eidx) - ei->hint_femp = candi_empty; - } - - brelse(bh); - if (entry_type == TYPE_UNUSED) - goto not_found; - continue; - } - - num_empty = 0; - candi_empty.eidx = EXFAT_HINT_NONE; - - if (entry_type == TYPE_FILE || entry_type == TYPE_DIR) { - step = DIRENT_STEP_FILE; - hint_opt->clu = clu.dir; - hint_opt->eidx = i; - if (type == TYPE_ALL || type == entry_type) { - num_ext = ep->dentry.file.num_ext; - step = DIRENT_STEP_STRM; - } - brelse(bh); - continue; - } - - if (entry_type == TYPE_STREAM) { - u16 name_hash; - - if (step != DIRENT_STEP_STRM) { - step = DIRENT_STEP_FILE; - brelse(bh); - continue; - } - step = DIRENT_STEP_FILE; - name_hash = le16_to_cpu( - ep->dentry.stream.name_hash); - if (p_uniname->name_hash == name_hash && - p_uniname->name_len == - ep->dentry.stream.name_len) { - step = DIRENT_STEP_NAME; - order = 1; - name_len = 0; - } - brelse(bh); - continue; - } - - brelse(bh); - if (entry_type == TYPE_EXTEND) { - unsigned short entry_uniname[16], unichar; - - if (step != DIRENT_STEP_NAME) { - step = DIRENT_STEP_FILE; - continue; - } - - if (++order == 2) - uniname = p_uniname->name; - else - uniname += EXFAT_FILE_NAME_LEN; - - len = exfat_extract_uni_name(ep, entry_uniname); - name_len += len; - - unichar = *(uniname+len); - *(uniname+len) = 0x0; - - if (exfat_uniname_ncmp(sb, uniname, - entry_uniname, len)) { - step = DIRENT_STEP_FILE; - } else if (p_uniname->name_len == name_len) { - if (order == num_ext) - goto found; - step = DIRENT_STEP_SECD; - } - - *(uniname+len) = unichar; - continue; - } - - if (entry_type & - (TYPE_CRITICAL_SEC | TYPE_BENIGN_SEC)) { - if (step == DIRENT_STEP_SECD) { - if (++order == num_ext) - goto found; - continue; - } - } - step = DIRENT_STEP_FILE; - } - - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - if (--clu.size > 0) - clu.dir++; - else - clu.dir = EXFAT_EOF_CLUSTER; - } else { - if (exfat_get_next_cluster(sb, &clu.dir)) - return -EIO; - } - } - -not_found: - /* - * We started at not 0 index,so we should try to find target - * from 0 index to the index we started at. - */ - if (!rewind && end_eidx) { - rewind = 1; - dentry = 0; - clu.dir = p_dir->dir; - /* reset empty hint */ - num_empty = 0; - candi_empty.eidx = EXFAT_HINT_NONE; - goto rewind; - } - - /* initialized hint_stat */ - hint_stat->clu = p_dir->dir; - hint_stat->eidx = 0; - return -ENOENT; - -found: - /* next dentry we'll find is out of this cluster */ - if (!((dentry + 1) & (dentries_per_clu - 1))) { - int ret = 0; - - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - if (--clu.size > 0) - clu.dir++; - else - clu.dir = EXFAT_EOF_CLUSTER; - } else { - ret = exfat_get_next_cluster(sb, &clu.dir); - } - - if (ret || clu.dir == EXFAT_EOF_CLUSTER) { - /* just initialized hint_stat */ - hint_stat->clu = p_dir->dir; - hint_stat->eidx = 0; - return (dentry - num_ext); - } - } - - hint_stat->clu = clu.dir; - hint_stat->eidx = dentry + 1; - return dentry - num_ext; -} - -int exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir, - int entry, struct exfat_dentry *ep) -{ - int i, count = 0; - unsigned int type; - struct exfat_dentry *ext_ep; - struct buffer_head *bh; - - for (i = 0, entry++; i < ep->dentry.file.num_ext; i++, entry++) { - ext_ep = exfat_get_dentry(sb, p_dir, entry, &bh, NULL); - if (!ext_ep) - return -EIO; - - type = exfat_get_entry_type(ext_ep); - brelse(bh); - if (type == TYPE_EXTEND || type == TYPE_STREAM) - count++; - else - break; - } - return count; -} - -int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir) -{ - int i, count = 0; - int dentries_per_clu; - unsigned int entry_type; - struct exfat_chain clu; - struct exfat_dentry *ep; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct buffer_head *bh; - - dentries_per_clu = sbi->dentries_per_clu; - - exfat_chain_dup(&clu, p_dir); - - while (clu.dir != EXFAT_EOF_CLUSTER) { - for (i = 0; i < dentries_per_clu; i++) { - ep = exfat_get_dentry(sb, &clu, i, &bh, NULL); - if (!ep) - return -EIO; - entry_type = exfat_get_entry_type(ep); - brelse(bh); - - if (entry_type == TYPE_UNUSED) - return count; - if (entry_type != TYPE_DIR) - continue; - count++; - } - - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - if (--clu.size > 0) - clu.dir++; - else - clu.dir = EXFAT_EOF_CLUSTER; - } else { - if (exfat_get_next_cluster(sb, &(clu.dir))) - return -EIO; - } - } - - return count; -} diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h deleted file mode 100644 index 1d6da6115..000000000 --- a/fs/exfat/exfat_fs.h +++ /dev/null @@ -1,529 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. - */ - -#ifndef _EXFAT_FS_H -#define _EXFAT_FS_H - -#include -#include -#include - -#define EXFAT_SUPER_MAGIC 0x2011BAB0UL -#define EXFAT_ROOT_INO 1 - -#define EXFAT_CLUSTERS_UNTRACKED (~0u) - -/* - * exfat error flags - */ -enum exfat_error_mode { - EXFAT_ERRORS_CONT, /* ignore error and continue */ - EXFAT_ERRORS_PANIC, /* panic on error */ - EXFAT_ERRORS_RO, /* remount r/o on error */ -}; - -/* - * exfat nls lossy flag - */ -enum { - NLS_NAME_NO_LOSSY, /* no lossy */ - NLS_NAME_LOSSY, /* just detected incorrect filename(s) */ - NLS_NAME_OVERLEN, /* the length is over than its limit */ -}; - -#define EXFAT_HASH_BITS 8 -#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS) - -/* - * Type Definitions - */ -#define ES_2_ENTRIES 2 -#define ES_ALL_ENTRIES 0 - -#define DIR_DELETED 0xFFFF0321 - -/* type values */ -#define TYPE_UNUSED 0x0000 -#define TYPE_DELETED 0x0001 -#define TYPE_INVALID 0x0002 -#define TYPE_CRITICAL_PRI 0x0100 -#define TYPE_BITMAP 0x0101 -#define TYPE_UPCASE 0x0102 -#define TYPE_VOLUME 0x0103 -#define TYPE_DIR 0x0104 -#define TYPE_FILE 0x011F -#define TYPE_CRITICAL_SEC 0x0200 -#define TYPE_STREAM 0x0201 -#define TYPE_EXTEND 0x0202 -#define TYPE_ACL 0x0203 -#define TYPE_BENIGN_PRI 0x0400 -#define TYPE_GUID 0x0401 -#define TYPE_PADDING 0x0402 -#define TYPE_ACLTAB 0x0403 -#define TYPE_BENIGN_SEC 0x0800 -#define TYPE_ALL 0x0FFF - -#define MAX_CHARSET_SIZE 6 /* max size of multi-byte character */ -#define MAX_NAME_LENGTH 255 /* max len of file name excluding NULL */ -#define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH + 1) * MAX_CHARSET_SIZE) - -/* Enough size to hold 256 dentry (even 512 Byte sector) */ -#define DIR_CACHE_SIZE (256*sizeof(struct exfat_dentry)/512+1) - -#define EXFAT_HINT_NONE -1 -#define EXFAT_MIN_SUBDIR 2 - -/* - * helpers for cluster size to byte conversion. - */ -#define EXFAT_CLU_TO_B(b, sbi) ((b) << (sbi)->cluster_size_bits) -#define EXFAT_B_TO_CLU(b, sbi) ((b) >> (sbi)->cluster_size_bits) -#define EXFAT_B_TO_CLU_ROUND_UP(b, sbi) \ - (((b - 1) >> (sbi)->cluster_size_bits) + 1) -#define EXFAT_CLU_OFFSET(off, sbi) ((off) & ((sbi)->cluster_size - 1)) - -/* - * helpers for block size to byte conversion. - */ -#define EXFAT_BLK_TO_B(b, sb) ((b) << (sb)->s_blocksize_bits) -#define EXFAT_B_TO_BLK(b, sb) ((b) >> (sb)->s_blocksize_bits) -#define EXFAT_B_TO_BLK_ROUND_UP(b, sb) \ - (((b - 1) >> (sb)->s_blocksize_bits) + 1) -#define EXFAT_BLK_OFFSET(off, sb) ((off) & ((sb)->s_blocksize - 1)) - -/* - * helpers for block size to dentry size conversion. - */ -#define EXFAT_B_TO_DEN_IDX(b, sbi) \ - ((b) << ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS)) -#define EXFAT_B_TO_DEN(b) ((b) >> DENTRY_SIZE_BITS) -#define EXFAT_DEN_TO_B(b) ((b) << DENTRY_SIZE_BITS) - -/* - * helpers for fat entry. - */ -#define FAT_ENT_SIZE (4) -#define FAT_ENT_SIZE_BITS (2) -#define FAT_ENT_OFFSET_SECTOR(sb, loc) (EXFAT_SB(sb)->FAT1_start_sector + \ - (((u64)loc << FAT_ENT_SIZE_BITS) >> sb->s_blocksize_bits)) -#define FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc) \ - ((loc << FAT_ENT_SIZE_BITS) & (sb->s_blocksize - 1)) - -/* - * helpers for bitmap. - */ -#define CLUSTER_TO_BITMAP_ENT(clu) ((clu) - EXFAT_RESERVED_CLUSTERS) -#define BITMAP_ENT_TO_CLUSTER(ent) ((ent) + EXFAT_RESERVED_CLUSTERS) -#define BITS_PER_SECTOR(sb) ((sb)->s_blocksize * BITS_PER_BYTE) -#define BITS_PER_SECTOR_MASK(sb) (BITS_PER_SECTOR(sb) - 1) -#define BITMAP_OFFSET_SECTOR_INDEX(sb, ent) \ - ((ent / BITS_PER_BYTE) >> (sb)->s_blocksize_bits) -#define BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent) (ent & BITS_PER_SECTOR_MASK(sb)) -#define BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent) \ - ((ent / BITS_PER_BYTE) & ((sb)->s_blocksize - 1)) -#define BITS_PER_BYTE_MASK 0x7 -#define IGNORED_BITS_REMAINED(clu, clu_base) ((1 << ((clu) - (clu_base))) - 1) - -struct exfat_dentry_namebuf { - char *lfn; - int lfnbuf_len; /* usually MAX_UNINAME_BUF_SIZE */ -}; - -/* unicode name structure */ -struct exfat_uni_name { - /* +3 for null and for converting */ - unsigned short name[MAX_NAME_LENGTH + 3]; - u16 name_hash; - unsigned char name_len; -}; - -/* directory structure */ -struct exfat_chain { - unsigned int dir; - unsigned int size; - unsigned char flags; -}; - -/* first empty entry hint information */ -struct exfat_hint_femp { - /* entry index of a directory */ - int eidx; - /* count of continuous empty entry */ - int count; - /* the cluster that first empty slot exists in */ - struct exfat_chain cur; -}; - -/* hint structure */ -struct exfat_hint { - unsigned int clu; - union { - unsigned int off; /* cluster offset */ - int eidx; /* entry index */ - }; -}; - -struct exfat_entry_set_cache { - struct super_block *sb; - bool modified; - unsigned int start_off; - int num_bh; - struct buffer_head *bh[DIR_CACHE_SIZE]; - unsigned int num_entries; -}; - -struct exfat_dir_entry { - struct exfat_chain dir; - int entry; - unsigned int type; - unsigned int start_clu; - unsigned char flags; - unsigned short attr; - loff_t size; - unsigned int num_subdirs; - struct timespec64 atime; - struct timespec64 mtime; - struct timespec64 crtime; - struct exfat_dentry_namebuf namebuf; -}; - -/* - * exfat mount in-memory data - */ -struct exfat_mount_options { - kuid_t fs_uid; - kgid_t fs_gid; - unsigned short fs_fmask; - unsigned short fs_dmask; - /* permission for setting the [am]time */ - unsigned short allow_utime; - /* charset for filename input/display */ - char *iocharset; - /* on error: continue, panic, remount-ro */ - enum exfat_error_mode errors; - unsigned utf8:1, /* Use of UTF-8 character set */ - discard:1; /* Issue discard requests on deletions */ - int time_offset; /* Offset of timestamps from UTC (in minutes) */ -}; - -/* - * EXFAT file system superblock in-memory data - */ -struct exfat_sb_info { - unsigned long long num_sectors; /* num of sectors in volume */ - unsigned int num_clusters; /* num of clusters in volume */ - unsigned int cluster_size; /* cluster size in bytes */ - unsigned int cluster_size_bits; - unsigned int sect_per_clus; /* cluster size in sectors */ - unsigned int sect_per_clus_bits; - unsigned long long FAT1_start_sector; /* FAT1 start sector */ - unsigned long long FAT2_start_sector; /* FAT2 start sector */ - unsigned long long data_start_sector; /* data area start sector */ - unsigned int num_FAT_sectors; /* num of FAT sectors */ - unsigned int root_dir; /* root dir cluster */ - unsigned int dentries_per_clu; /* num of dentries per cluster */ - unsigned int vol_flags; /* volume flags */ - unsigned int vol_flags_persistent; /* volume flags to retain */ - struct buffer_head *boot_bh; /* buffer_head of BOOT sector */ - - unsigned int map_clu; /* allocation bitmap start cluster */ - unsigned int map_sectors; /* num of allocation bitmap sectors */ - struct buffer_head **vol_amap; /* allocation bitmap */ - - unsigned short *vol_utbl; /* upcase table */ - - unsigned int clu_srch_ptr; /* cluster search pointer */ - unsigned int used_clusters; /* number of used clusters */ - - struct mutex s_lock; /* superblock lock */ - struct mutex bitmap_lock; /* bitmap lock */ - struct exfat_mount_options options; - struct nls_table *nls_io; /* Charset used for input and display */ - struct ratelimit_state ratelimit; - - spinlock_t inode_hash_lock; - struct hlist_head inode_hashtable[EXFAT_HASH_SIZE]; - - struct rcu_head rcu; -}; - -#define EXFAT_CACHE_VALID 0 - -/* - * EXFAT file system inode in-memory data - */ -struct exfat_inode_info { - struct exfat_chain dir; - int entry; - unsigned int type; - unsigned short attr; - unsigned int start_clu; - unsigned char flags; - /* - * the copy of low 32bit of i_version to check - * the validation of hint_stat. - */ - unsigned int version; - - /* hint for cluster last accessed */ - struct exfat_hint hint_bmap; - /* hint for entry index we try to lookup next time */ - struct exfat_hint hint_stat; - /* hint for first empty entry */ - struct exfat_hint_femp hint_femp; - - spinlock_t cache_lru_lock; - struct list_head cache_lru; - int nr_caches; - /* for avoiding the race between alloc and free */ - unsigned int cache_valid_id; - - /* - * NOTE: i_size_ondisk is 64bits, so must hold ->inode_lock to access. - * physically allocated size. - */ - loff_t i_size_ondisk; - /* block-aligned i_size (used in cont_write_begin) */ - loff_t i_size_aligned; - /* on-disk position of directory entry or 0 */ - loff_t i_pos; - /* hash by i_location */ - struct hlist_node i_hash_fat; - /* protect bmap against truncate */ - struct rw_semaphore truncate_lock; - struct inode vfs_inode; - /* File creation time */ - struct timespec64 i_crtime; -}; - -static inline struct exfat_sb_info *EXFAT_SB(struct super_block *sb) -{ - return sb->s_fs_info; -} - -static inline struct exfat_inode_info *EXFAT_I(struct inode *inode) -{ - return container_of(inode, struct exfat_inode_info, vfs_inode); -} - -/* - * If ->i_mode can't hold 0222 (i.e. ATTR_RO), we use ->i_attrs to - * save ATTR_RO instead of ->i_mode. - * - * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only - * bit, it's just used as flag for app. - */ -static inline int exfat_mode_can_hold_ro(struct inode *inode) -{ - struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); - - if (S_ISDIR(inode->i_mode)) - return 0; - - if ((~sbi->options.fs_fmask) & 0222) - return 1; - return 0; -} - -/* Convert attribute bits and a mask to the UNIX mode. */ -static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi, - unsigned short attr, mode_t mode) -{ - if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR)) - mode &= ~0222; - - if (attr & ATTR_SUBDIR) - return (mode & ~sbi->options.fs_dmask) | S_IFDIR; - - return (mode & ~sbi->options.fs_fmask) | S_IFREG; -} - -/* Return the FAT attribute byte for this inode */ -static inline unsigned short exfat_make_attr(struct inode *inode) -{ - unsigned short attr = EXFAT_I(inode)->attr; - - if (S_ISDIR(inode->i_mode)) - attr |= ATTR_SUBDIR; - if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & 0222)) - attr |= ATTR_READONLY; - return attr; -} - -static inline void exfat_save_attr(struct inode *inode, unsigned short attr) -{ - if (exfat_mode_can_hold_ro(inode)) - EXFAT_I(inode)->attr = attr & (ATTR_RWMASK | ATTR_READONLY); - else - EXFAT_I(inode)->attr = attr & ATTR_RWMASK; -} - -static inline bool exfat_is_last_sector_in_cluster(struct exfat_sb_info *sbi, - sector_t sec) -{ - return ((sec - sbi->data_start_sector + 1) & - ((1 << sbi->sect_per_clus_bits) - 1)) == 0; -} - -static inline sector_t exfat_cluster_to_sector(struct exfat_sb_info *sbi, - unsigned int clus) -{ - return ((sector_t)(clus - EXFAT_RESERVED_CLUSTERS) << sbi->sect_per_clus_bits) + - sbi->data_start_sector; -} - -static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi, - sector_t sec) -{ - return ((sec - sbi->data_start_sector) >> sbi->sect_per_clus_bits) + - EXFAT_RESERVED_CLUSTERS; -} - -/* super.c */ -int exfat_set_volume_dirty(struct super_block *sb); -int exfat_clear_volume_dirty(struct super_block *sb); - -/* fatent.c */ -#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu) - -int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, - struct exfat_chain *p_chain, bool sync_bmap); -int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain); -int exfat_ent_get(struct super_block *sb, unsigned int loc, - unsigned int *content); -int exfat_ent_set(struct super_block *sb, unsigned int loc, - unsigned int content); -int exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir, - int entry, struct exfat_dentry *p_entry); -int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain, - unsigned int len); -int exfat_zeroed_cluster(struct inode *dir, unsigned int clu); -int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain, - unsigned int *ret_clu); -int exfat_count_num_clusters(struct super_block *sb, - struct exfat_chain *p_chain, unsigned int *ret_count); - -/* balloc.c */ -int exfat_load_bitmap(struct super_block *sb); -void exfat_free_bitmap(struct exfat_sb_info *sbi); -int exfat_set_bitmap(struct inode *inode, unsigned int clu, bool sync); -void exfat_clear_bitmap(struct inode *inode, unsigned int clu, bool sync); -unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu); -int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count); -int exfat_trim_fs(struct inode *inode, struct fstrim_range *range); - -/* file.c */ -extern const struct file_operations exfat_file_operations; -int __exfat_truncate(struct inode *inode, loff_t new_size); -void exfat_truncate(struct inode *inode, loff_t size); -int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, - struct iattr *attr); -int exfat_getattr(struct user_namespace *mnt_userns, const struct path *path, - struct kstat *stat, unsigned int request_mask, - unsigned int query_flags); -int exfat_file_fsync(struct file *file, loff_t start, loff_t end, int datasync); -long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); -long exfat_compat_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg); - -/* namei.c */ -extern const struct dentry_operations exfat_dentry_ops; -extern const struct dentry_operations exfat_utf8_dentry_ops; - -/* cache.c */ -int exfat_cache_init(void); -void exfat_cache_shutdown(void); -void exfat_cache_inval_inode(struct inode *inode); -int exfat_get_cluster(struct inode *inode, unsigned int cluster, - unsigned int *fclus, unsigned int *dclus, - unsigned int *last_dclus, int allow_eof); - -/* dir.c */ -extern const struct inode_operations exfat_dir_inode_operations; -extern const struct file_operations exfat_dir_operations; -unsigned int exfat_get_entry_type(struct exfat_dentry *p_entry); -int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir, - int entry, unsigned int type, unsigned int start_clu, - unsigned long long size); -int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir, - int entry, int num_entries, struct exfat_uni_name *p_uniname); -int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir, - int entry, int order, int num_entries); -int exfat_update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir, - int entry); -void exfat_update_dir_chksum_with_entry_set(struct exfat_entry_set_cache *es); -int exfat_calc_num_entries(struct exfat_uni_name *p_uniname); -int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, - struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname, - int num_entries, unsigned int type, struct exfat_hint *hint_opt); -int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu); -int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir, - int entry, sector_t *sector, int *offset); -struct exfat_dentry *exfat_get_dentry(struct super_block *sb, - struct exfat_chain *p_dir, int entry, struct buffer_head **bh, - sector_t *sector); -struct exfat_dentry *exfat_get_dentry_cached(struct exfat_entry_set_cache *es, - int num); -struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb, - struct exfat_chain *p_dir, int entry, unsigned int type); -int exfat_free_dentry_set(struct exfat_entry_set_cache *es, int sync); -int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir); - -/* inode.c */ -extern const struct inode_operations exfat_file_inode_operations; -void exfat_sync_inode(struct inode *inode); -struct inode *exfat_build_inode(struct super_block *sb, - struct exfat_dir_entry *info, loff_t i_pos); -void exfat_hash_inode(struct inode *inode, loff_t i_pos); -void exfat_unhash_inode(struct inode *inode); -struct inode *exfat_iget(struct super_block *sb, loff_t i_pos); -int exfat_write_inode(struct inode *inode, struct writeback_control *wbc); -void exfat_evict_inode(struct inode *inode); -int exfat_block_truncate_page(struct inode *inode, loff_t from); - -/* exfat/nls.c */ -unsigned short exfat_toupper(struct super_block *sb, unsigned short a); -int exfat_uniname_ncmp(struct super_block *sb, unsigned short *a, - unsigned short *b, unsigned int len); -int exfat_utf16_to_nls(struct super_block *sb, - struct exfat_uni_name *uniname, unsigned char *p_cstring, - int len); -int exfat_nls_to_utf16(struct super_block *sb, - const unsigned char *p_cstring, const int len, - struct exfat_uni_name *uniname, int *p_lossy); -int exfat_create_upcase_table(struct super_block *sb); -void exfat_free_upcase_table(struct exfat_sb_info *sbi); - -/* exfat/misc.c */ -void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) - __printf(3, 4) __cold; -#define exfat_fs_error(sb, fmt, args...) \ - __exfat_fs_error(sb, 1, fmt, ## args) -#define exfat_fs_error_ratelimit(sb, fmt, args...) \ - __exfat_fs_error(sb, __ratelimit(&EXFAT_SB(sb)->ratelimit), \ - fmt, ## args) -void exfat_msg(struct super_block *sb, const char *lv, const char *fmt, ...) - __printf(3, 4) __cold; -#define exfat_err(sb, fmt, ...) \ - exfat_msg(sb, KERN_ERR, fmt, ##__VA_ARGS__) -#define exfat_warn(sb, fmt, ...) \ - exfat_msg(sb, KERN_WARNING, fmt, ##__VA_ARGS__) -#define exfat_info(sb, fmt, ...) \ - exfat_msg(sb, KERN_INFO, fmt, ##__VA_ARGS__) - -void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts, - u8 tz, __le16 time, __le16 date, u8 time_cs); -void exfat_truncate_atime(struct timespec64 *ts); -void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts, - u8 *tz, __le16 *time, __le16 *date, u8 *time_cs); -u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type); -u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type); -void exfat_update_bh(struct buffer_head *bh, int sync); -int exfat_update_bhs(struct buffer_head **bhs, int nr_bhs, int sync); -void exfat_chain_set(struct exfat_chain *ec, unsigned int dir, - unsigned int size, unsigned char flags); -void exfat_chain_dup(struct exfat_chain *dup, struct exfat_chain *ec); - -#endif /* !_EXFAT_FS_H */ diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h deleted file mode 100644 index 7f39b1c64..000000000 --- a/fs/exfat/exfat_raw.h +++ /dev/null @@ -1,168 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. - */ - -#ifndef _EXFAT_RAW_H -#define _EXFAT_RAW_H - -#include - -#define BOOT_SIGNATURE 0xAA55 -#define EXBOOT_SIGNATURE 0xAA550000 -#define STR_EXFAT "EXFAT " /* size should be 8 */ - -#define EXFAT_MAX_FILE_LEN 255 - -#define VOLUME_DIRTY 0x0002 -#define MEDIA_FAILURE 0x0004 - -#define EXFAT_EOF_CLUSTER 0xFFFFFFFFu -#define EXFAT_BAD_CLUSTER 0xFFFFFFF7u -#define EXFAT_FREE_CLUSTER 0 -/* Cluster 0, 1 are reserved, the first cluster is 2 in the cluster heap. */ -#define EXFAT_RESERVED_CLUSTERS 2 -#define EXFAT_FIRST_CLUSTER 2 -#define EXFAT_DATA_CLUSTER_COUNT(sbi) \ - ((sbi)->num_clusters - EXFAT_RESERVED_CLUSTERS) - -/* AllocationPossible and NoFatChain field in GeneralSecondaryFlags Field */ -#define ALLOC_FAT_CHAIN 0x01 -#define ALLOC_NO_FAT_CHAIN 0x03 - -#define DENTRY_SIZE 32 /* directory entry size */ -#define DENTRY_SIZE_BITS 5 -/* exFAT allows 8388608(256MB) directory entries */ -#define MAX_EXFAT_DENTRIES 8388608 - -/* dentry types */ -#define EXFAT_UNUSED 0x00 /* end of directory */ -#define EXFAT_DELETE (~0x80) -#define IS_EXFAT_DELETED(x) ((x) < 0x80) /* deleted file (0x01~0x7F) */ -#define EXFAT_INVAL 0x80 /* invalid value */ -#define EXFAT_BITMAP 0x81 /* allocation bitmap */ -#define EXFAT_UPCASE 0x82 /* upcase table */ -#define EXFAT_VOLUME 0x83 /* volume label */ -#define EXFAT_FILE 0x85 /* file or dir */ -#define EXFAT_GUID 0xA0 -#define EXFAT_PADDING 0xA1 -#define EXFAT_ACLTAB 0xA2 -#define EXFAT_STREAM 0xC0 /* stream entry */ -#define EXFAT_NAME 0xC1 /* file name entry */ -#define EXFAT_ACL 0xC2 /* stream entry */ - -#define IS_EXFAT_CRITICAL_PRI(x) (x < 0xA0) -#define IS_EXFAT_BENIGN_PRI(x) (x < 0xC0) -#define IS_EXFAT_CRITICAL_SEC(x) (x < 0xE0) - -/* checksum types */ -#define CS_DIR_ENTRY 0 -#define CS_BOOT_SECTOR 1 -#define CS_DEFAULT 2 - -/* file attributes */ -#define ATTR_READONLY 0x0001 -#define ATTR_HIDDEN 0x0002 -#define ATTR_SYSTEM 0x0004 -#define ATTR_VOLUME 0x0008 -#define ATTR_SUBDIR 0x0010 -#define ATTR_ARCHIVE 0x0020 - -#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \ - ATTR_SUBDIR | ATTR_ARCHIVE) - -#define BOOTSEC_JUMP_BOOT_LEN 3 -#define BOOTSEC_FS_NAME_LEN 8 -#define BOOTSEC_OLDBPB_LEN 53 - -#define EXFAT_FILE_NAME_LEN 15 - -#define EXFAT_MIN_SECT_SIZE_BITS 9 -#define EXFAT_MAX_SECT_SIZE_BITS 12 -#define EXFAT_MAX_SECT_PER_CLUS_BITS(x) (25 - (x)->sect_size_bits) - -/* EXFAT: Main and Backup Boot Sector (512 bytes) */ -struct boot_sector { - __u8 jmp_boot[BOOTSEC_JUMP_BOOT_LEN]; - __u8 fs_name[BOOTSEC_FS_NAME_LEN]; - __u8 must_be_zero[BOOTSEC_OLDBPB_LEN]; - __le64 partition_offset; - __le64 vol_length; - __le32 fat_offset; - __le32 fat_length; - __le32 clu_offset; - __le32 clu_count; - __le32 root_cluster; - __le32 vol_serial; - __u8 fs_revision[2]; - __le16 vol_flags; - __u8 sect_size_bits; - __u8 sect_per_clus_bits; - __u8 num_fats; - __u8 drv_sel; - __u8 percent_in_use; - __u8 reserved[7]; - __u8 boot_code[390]; - __le16 signature; -} __packed; - -struct exfat_dentry { - __u8 type; - union { - struct { - __u8 num_ext; - __le16 checksum; - __le16 attr; - __le16 reserved1; - __le16 create_time; - __le16 create_date; - __le16 modify_time; - __le16 modify_date; - __le16 access_time; - __le16 access_date; - __u8 create_time_cs; - __u8 modify_time_cs; - __u8 create_tz; - __u8 modify_tz; - __u8 access_tz; - __u8 reserved2[7]; - } __packed file; /* file directory entry */ - struct { - __u8 flags; - __u8 reserved1; - __u8 name_len; - __le16 name_hash; - __le16 reserved2; - __le64 valid_size; - __le32 reserved3; - __le32 start_clu; - __le64 size; - } __packed stream; /* stream extension directory entry */ - struct { - __u8 flags; - __le16 unicode_0_14[EXFAT_FILE_NAME_LEN]; - } __packed name; /* file name directory entry */ - struct { - __u8 flags; - __u8 reserved[18]; - __le32 start_clu; - __le64 size; - } __packed bitmap; /* allocation bitmap directory entry */ - struct { - __u8 reserved1[3]; - __le32 checksum; - __u8 reserved2[12]; - __le32 start_clu; - __le64 size; - } __packed upcase; /* up-case table directory entry */ - } __packed dentry; -} __packed; - -#define EXFAT_TZ_VALID (1 << 7) - -/* Jan 1 GMT 00:00:00 1980 */ -#define EXFAT_MIN_TIMESTAMP_SECS 315532800LL -/* Dec 31 GMT 23:59:59 2107 */ -#define EXFAT_MAX_TIMESTAMP_SECS 4354819199LL - -#endif /* !_EXFAT_RAW_H */ diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c deleted file mode 100644 index e949e5634..000000000 --- a/fs/exfat/fatent.c +++ /dev/null @@ -1,476 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include - -#include "exfat_raw.h" -#include "exfat_fs.h" - -static int exfat_mirror_bh(struct super_block *sb, sector_t sec, - struct buffer_head *bh) -{ - struct buffer_head *c_bh; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - 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); - set_buffer_uptodate(c_bh); - mark_buffer_dirty(c_bh); - if (sb->s_flags & SB_SYNCHRONOUS) - err = sync_dirty_buffer(c_bh); - brelse(c_bh); - } - - return err; -} - -static int __exfat_ent_get(struct super_block *sb, unsigned int loc, - unsigned int *content) -{ - unsigned int off; - sector_t sec; - struct buffer_head *bh; - - sec = FAT_ENT_OFFSET_SECTOR(sb, loc); - off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc); - - bh = sb_bread(sb, sec); - if (!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; - - brelse(bh); - return 0; -} - -int exfat_ent_set(struct super_block *sb, unsigned int loc, - unsigned int content) -{ - unsigned int off; - sector_t sec; - __le32 *fat_entry; - struct buffer_head *bh; - - sec = FAT_ENT_OFFSET_SECTOR(sb, loc); - off = FAT_ENT_OFFSET_BYTE_IN_SECTOR(sb, loc); - - bh = sb_bread(sb, sec); - if (!bh) - return -EIO; - - fat_entry = (__le32 *)&(bh->b_data[off]); - *fat_entry = cpu_to_le32(content); - exfat_update_bh(bh, sb->s_flags & SB_SYNCHRONOUS); - exfat_mirror_bh(sb, sec, bh); - brelse(bh); - return 0; -} - -static inline bool is_valid_cluster(struct exfat_sb_info *sbi, - unsigned int clus) -{ - if (clus < EXFAT_FIRST_CLUSTER || sbi->num_clusters <= clus) - return false; - return true; -} - -int exfat_ent_get(struct super_block *sb, unsigned int loc, - unsigned int *content) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - int err; - - if (!is_valid_cluster(sbi, loc)) { - exfat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", - loc); - return -EIO; - } - - err = __exfat_ent_get(sb, loc, content); - if (err) { - exfat_fs_error(sb, - "failed to access to FAT (entry 0x%08x, err:%d)", - loc, err); - return err; - } - - if (*content == EXFAT_FREE_CLUSTER) { - exfat_fs_error(sb, - "invalid access to FAT free cluster (entry 0x%08x)", - loc); - return -EIO; - } - - if (*content == EXFAT_BAD_CLUSTER) { - exfat_fs_error(sb, - "invalid access to FAT bad cluster (entry 0x%08x)", - loc); - return -EIO; - } - - if (*content != EXFAT_EOF_CLUSTER && !is_valid_cluster(sbi, *content)) { - exfat_fs_error(sb, - "invalid access to FAT (entry 0x%08x) bogus content (0x%08x)", - loc, *content); - return -EIO; - } - - return 0; -} - -int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain, - unsigned int len) -{ - if (!len) - return 0; - - while (len > 1) { - if (exfat_ent_set(sb, chain, chain + 1)) - return -EIO; - chain++; - len--; - } - - if (exfat_ent_set(sb, chain, EXFAT_EOF_CLUSTER)) - return -EIO; - return 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) { - 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; - } - - exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode))); - clu++; - num_clusters++; - } while (num_clusters < p_chain->size); - } else { - 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; - } - - exfat_clear_bitmap(inode, clu, (sync && IS_DIRSYNC(inode))); - clu = n_clu; - num_clusters++; - - if (err) - goto dec_used_clus; - } while (clu != EXFAT_EOF_CLUSTER); - } - -dec_used_clus: - 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) -{ - 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)) - return -EIO; - } while (next != EXFAT_EOF_CLUSTER); - - 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 *bhs[MAX_BUF_PER_PAGE]; - int nr_bhs = MAX_BUF_PER_PAGE; - sector_t blknr, last_blknr; - int err, i, n; - - 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 */ - while (blknr < last_blknr) { - for (n = 0; n < nr_bhs && blknr < last_blknr; n++, blknr++) { - bhs[n] = sb_getblk(sb, blknr); - if (!bhs[n]) { - err = -ENOMEM; - goto release_bhs; - } - memset(bhs[n]->b_data, 0, sb->s_blocksize); - } - - err = exfat_update_bhs(bhs, n, IS_DIRSYNC(dir)); - if (err) - goto release_bhs; - - for (i = 0; i < n; i++) - brelse(bhs[i]); - } - return 0; - -release_bhs: - exfat_err(sb, "failed zeroed sect %llu\n", (unsigned long long)blknr); - for (i = 0; i < n; i++) - bforget(bhs[i]); - return err; -} - -int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, - struct exfat_chain *p_chain, bool sync_bmap) -{ - int ret = -ENOSPC; - unsigned int num_clusters = 0, 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)\n", - 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)) { - exfat_err(sb, "hint_cluster is invalid (%u)", - hint_clu); - hint_clu = EXFAT_FIRST_CLUSTER; - if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { - if (exfat_chain_cont_cluster(sb, p_chain->dir, - num_clusters)) { - ret = -EIO; - goto unlock; - } - 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 && - p_chain->flags == ALLOC_NO_FAT_CHAIN) { - if (exfat_chain_cont_cluster(sb, p_chain->dir, - num_clusters)) { - ret = -EIO; - goto free_cluster; - } - p_chain->flags = ALLOC_FAT_CHAIN; - } - - /* update allocation bitmap */ - if (exfat_set_bitmap(inode, new_clu, sync_bmap)) { - ret = -EIO; - goto free_cluster; - } - - num_clusters++; - - /* 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; - } - } - last_clu = new_clu; - - if (--num_alloc == 0) { - sbi->clu_srch_ptr = hint_clu; - sbi->used_clusters += num_clusters; - - p_chain->size += num_clusters; - 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, - num_clusters)) { - ret = -EIO; - goto free_cluster; - } - p_chain->flags = ALLOC_FAT_CHAIN; - } - } - } -free_cluster: - if (num_clusters) - __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); - - 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)) - return -EIO; - if (clu == EXFAT_EOF_CLUSTER) - break; - } - - *ret_count = count; - return 0; -} diff --git a/fs/exfat/file.c b/fs/exfat/file.c deleted file mode 100644 index 6af0191b6..000000000 --- a/fs/exfat/file.c +++ /dev/null @@ -1,435 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include - -#include "exfat_raw.h" -#include "exfat_fs.h" - -static int exfat_cont_expand(struct inode *inode, loff_t size) -{ - struct address_space *mapping = inode->i_mapping; - loff_t start = i_size_read(inode), count = size - i_size_read(inode); - int err, err2; - - err = generic_cont_expand_simple(inode, size); - if (err) - return err; - - inode->i_ctime = inode->i_mtime = current_time(inode); - mark_inode_dirty(inode); - - if (!IS_SYNC(inode)) - return 0; - - err = filemap_fdatawrite_range(mapping, start, start + count - 1); - err2 = sync_mapping_buffers(mapping); - if (!err) - err = err2; - err2 = write_inode_now(inode, 1); - if (!err) - err = err2; - if (err) - return err; - - return filemap_fdatawait_range(mapping, start, start + count - 1); -} - -static bool exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode) -{ - mode_t allow_utime = sbi->options.allow_utime; - - if (!uid_eq(current_fsuid(), inode->i_uid)) { - if (in_group_p(inode->i_gid)) - allow_utime >>= 3; - if (allow_utime & MAY_WRITE) - return true; - } - - /* use a default check */ - return false; -} - -static int exfat_sanitize_mode(const struct exfat_sb_info *sbi, - struct inode *inode, umode_t *mode_ptr) -{ - mode_t i_mode, mask, perm; - - i_mode = inode->i_mode; - - mask = (S_ISREG(i_mode) || S_ISLNK(i_mode)) ? - sbi->options.fs_fmask : sbi->options.fs_dmask; - perm = *mode_ptr & ~(S_IFMT | mask); - - /* Of the r and x bits, all (subject to umask) must be present.*/ - if ((perm & 0555) != (i_mode & 0555)) - return -EPERM; - - if (exfat_mode_can_hold_ro(inode)) { - /* - * Of the w bits, either all (subject to umask) or none must - * be present. - */ - if ((perm & 0222) && ((perm & 0222) != (0222 & ~mask))) - return -EPERM; - } else { - /* - * If exfat_mode_can_hold_ro(inode) is false, can't change - * w bits. - */ - if ((perm & 0222) != (0222 & ~mask)) - return -EPERM; - } - - *mode_ptr &= S_IFMT | perm; - - return 0; -} - -/* resize the file length */ -int __exfat_truncate(struct inode *inode, loff_t new_size) -{ - unsigned int num_clusters_new, num_clusters_phys; - unsigned int last_clu = EXFAT_FREE_CLUSTER; - struct exfat_chain clu; - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_inode_info *ei = EXFAT_I(inode); - int evict = (ei->dir.dir == DIR_DELETED) ? 1 : 0; - - /* check if the given file ID is opened */ - if (ei->type != TYPE_FILE && ei->type != TYPE_DIR) - return -EPERM; - - exfat_set_volume_dirty(sb); - - num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi); - num_clusters_phys = - EXFAT_B_TO_CLU_ROUND_UP(EXFAT_I(inode)->i_size_ondisk, sbi); - - exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags); - - if (new_size > 0) { - /* - * Truncate FAT chain num_clusters after the first cluster - * num_clusters = min(new, phys); - */ - unsigned int num_clusters = - min(num_clusters_new, num_clusters_phys); - - /* - * Follow FAT chain - * (defensive coding - works fine even with corrupted FAT table - */ - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - clu.dir += num_clusters; - clu.size -= num_clusters; - } else { - while (num_clusters > 0) { - last_clu = clu.dir; - if (exfat_get_next_cluster(sb, &(clu.dir))) - return -EIO; - - num_clusters--; - clu.size--; - } - } - } else { - ei->flags = ALLOC_NO_FAT_CHAIN; - ei->start_clu = EXFAT_EOF_CLUSTER; - } - - i_size_write(inode, new_size); - - if (ei->type == TYPE_FILE) - ei->attr |= ATTR_ARCHIVE; - - /* update the directory entry */ - if (!evict) { - struct timespec64 ts; - struct exfat_dentry *ep, *ep2; - struct exfat_entry_set_cache *es; - int err; - - es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, - ES_ALL_ENTRIES); - if (!es) - return -EIO; - ep = exfat_get_dentry_cached(es, 0); - ep2 = exfat_get_dentry_cached(es, 1); - - ts = current_time(inode); - exfat_set_entry_time(sbi, &ts, - &ep->dentry.file.modify_tz, - &ep->dentry.file.modify_time, - &ep->dentry.file.modify_date, - &ep->dentry.file.modify_time_cs); - ep->dentry.file.attr = cpu_to_le16(ei->attr); - - /* File size should be zero if there is no cluster allocated */ - if (ei->start_clu == EXFAT_EOF_CLUSTER) { - ep2->dentry.stream.valid_size = 0; - ep2->dentry.stream.size = 0; - } else { - ep2->dentry.stream.valid_size = cpu_to_le64(new_size); - ep2->dentry.stream.size = ep2->dentry.stream.valid_size; - } - - if (new_size == 0) { - /* Any directory can not be truncated to zero */ - WARN_ON(ei->type != TYPE_FILE); - - ep2->dentry.stream.flags = ALLOC_FAT_CHAIN; - ep2->dentry.stream.start_clu = EXFAT_FREE_CLUSTER; - } - - exfat_update_dir_chksum_with_entry_set(es); - err = exfat_free_dentry_set(es, inode_needs_sync(inode)); - if (err) - return err; - } - - /* cut off from the FAT chain */ - if (ei->flags == ALLOC_FAT_CHAIN && last_clu != EXFAT_FREE_CLUSTER && - last_clu != EXFAT_EOF_CLUSTER) { - if (exfat_ent_set(sb, last_clu, EXFAT_EOF_CLUSTER)) - return -EIO; - } - - /* invalidate cache and free the clusters */ - /* clear exfat cache */ - exfat_cache_inval_inode(inode); - - /* hint information */ - ei->hint_bmap.off = EXFAT_EOF_CLUSTER; - ei->hint_bmap.clu = EXFAT_EOF_CLUSTER; - - /* hint_stat will be used if this is directory. */ - ei->hint_stat.eidx = 0; - ei->hint_stat.clu = ei->start_clu; - ei->hint_femp.eidx = EXFAT_HINT_NONE; - - /* free the clusters */ - if (exfat_free_cluster(inode, &clu)) - return -EIO; - - exfat_clear_volume_dirty(sb); - - return 0; -} - -void exfat_truncate(struct inode *inode, loff_t size) -{ - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - unsigned int blocksize = i_blocksize(inode); - loff_t aligned_size; - int err; - - mutex_lock(&sbi->s_lock); - if (EXFAT_I(inode)->start_clu == 0) { - /* - * Empty start_clu != ~0 (not allocated) - */ - exfat_fs_error(sb, "tried to truncate zeroed cluster."); - goto write_size; - } - - err = __exfat_truncate(inode, i_size_read(inode)); - if (err) - goto write_size; - - inode->i_ctime = inode->i_mtime = current_time(inode); - if (IS_DIRSYNC(inode)) - exfat_sync_inode(inode); - else - mark_inode_dirty(inode); - - inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) & - ~(sbi->cluster_size - 1)) >> inode->i_blkbits; -write_size: - aligned_size = i_size_read(inode); - if (aligned_size & (blocksize - 1)) { - aligned_size |= (blocksize - 1); - aligned_size++; - } - - if (EXFAT_I(inode)->i_size_ondisk > i_size_read(inode)) - EXFAT_I(inode)->i_size_ondisk = aligned_size; - - if (EXFAT_I(inode)->i_size_aligned > i_size_read(inode)) - EXFAT_I(inode)->i_size_aligned = aligned_size; - mutex_unlock(&sbi->s_lock); -} - -int exfat_getattr(struct user_namespace *mnt_uerns, const struct path *path, - struct kstat *stat, unsigned int request_mask, - unsigned int query_flags) -{ - struct inode *inode = d_backing_inode(path->dentry); - struct exfat_inode_info *ei = EXFAT_I(inode); - - generic_fillattr(&init_user_ns, inode, stat); - exfat_truncate_atime(&stat->atime); - stat->result_mask |= STATX_BTIME; - stat->btime.tv_sec = ei->i_crtime.tv_sec; - stat->btime.tv_nsec = ei->i_crtime.tv_nsec; - stat->blksize = EXFAT_SB(inode->i_sb)->cluster_size; - return 0; -} - -int exfat_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, - struct iattr *attr) -{ - struct exfat_sb_info *sbi = EXFAT_SB(dentry->d_sb); - struct inode *inode = dentry->d_inode; - unsigned int ia_valid; - int error; - - if ((attr->ia_valid & ATTR_SIZE) && - attr->ia_size > i_size_read(inode)) { - error = exfat_cont_expand(inode, attr->ia_size); - if (error || attr->ia_valid == ATTR_SIZE) - return error; - attr->ia_valid &= ~ATTR_SIZE; - } - - /* Check for setting the inode time. */ - ia_valid = attr->ia_valid; - if ((ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) && - exfat_allow_set_time(sbi, inode)) { - attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET | - ATTR_TIMES_SET); - } - - error = setattr_prepare(&init_user_ns, dentry, attr); - attr->ia_valid = ia_valid; - if (error) - goto out; - - if (((attr->ia_valid & ATTR_UID) && - !uid_eq(attr->ia_uid, sbi->options.fs_uid)) || - ((attr->ia_valid & ATTR_GID) && - !gid_eq(attr->ia_gid, sbi->options.fs_gid)) || - ((attr->ia_valid & ATTR_MODE) && - (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | 0777)))) { - error = -EPERM; - goto out; - } - - /* - * We don't return -EPERM here. Yes, strange, but this is too - * old behavior. - */ - if (attr->ia_valid & ATTR_MODE) { - if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0) - attr->ia_valid &= ~ATTR_MODE; - } - - if (attr->ia_valid & ATTR_SIZE) { - error = exfat_block_truncate_page(inode, attr->ia_size); - if (error) - goto out; - - down_write(&EXFAT_I(inode)->truncate_lock); - truncate_setsize(inode, attr->ia_size); - exfat_truncate(inode, attr->ia_size); - up_write(&EXFAT_I(inode)->truncate_lock); - } - - setattr_copy(&init_user_ns, inode, attr); - exfat_truncate_atime(&inode->i_atime); - mark_inode_dirty(inode); - -out: - return error; -} - -static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg) -{ - struct request_queue *q = bdev_get_queue(inode->i_sb->s_bdev); - struct fstrim_range range; - int ret = 0; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (!blk_queue_discard(q)) - return -EOPNOTSUPP; - - if (copy_from_user(&range, (struct fstrim_range __user *)arg, sizeof(range))) - return -EFAULT; - - range.minlen = max_t(unsigned int, range.minlen, - q->limits.discard_granularity); - - ret = exfat_trim_fs(inode, &range); - if (ret < 0) - return ret; - - if (copy_to_user((struct fstrim_range __user *)arg, &range, sizeof(range))) - return -EFAULT; - - return 0; -} - -long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct inode *inode = file_inode(filp); - - switch (cmd) { - case FITRIM: - return exfat_ioctl_fitrim(inode, arg); - default: - return -ENOTTY; - } -} - -#ifdef CONFIG_COMPAT -long exfat_compat_ioctl(struct file *filp, unsigned int cmd, - unsigned long arg) -{ - return exfat_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); -} -#endif - -int exfat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) -{ - struct inode *inode = filp->f_mapping->host; - int err; - - err = __generic_file_fsync(filp, start, end, datasync); - if (err) - return err; - - err = sync_blockdev(inode->i_sb->s_bdev); - if (err) - return err; - - return blkdev_issue_flush(inode->i_sb->s_bdev); -} - -const struct file_operations exfat_file_operations = { - .llseek = generic_file_llseek, - .read_iter = generic_file_read_iter, - .write_iter = generic_file_write_iter, - .unlocked_ioctl = exfat_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = exfat_compat_ioctl, -#endif - .mmap = generic_file_mmap, - .fsync = exfat_file_fsync, - .splice_read = generic_file_splice_read, - .splice_write = iter_file_splice_write, -}; - -const struct inode_operations exfat_file_inode_operations = { - .setattr = exfat_setattr, - .getattr = exfat_getattr, -}; diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c deleted file mode 100644 index ca37d4344..000000000 --- a/fs/exfat/inode.c +++ /dev/null @@ -1,659 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "exfat_raw.h" -#include "exfat_fs.h" - -static int __exfat_write_inode(struct inode *inode, int sync) -{ - unsigned long long on_disk_size; - struct exfat_dentry *ep, *ep2; - struct exfat_entry_set_cache *es = NULL; - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_inode_info *ei = EXFAT_I(inode); - bool is_dir = (ei->type == TYPE_DIR) ? true : false; - - if (inode->i_ino == EXFAT_ROOT_INO) - return 0; - - /* - * If the indode is already unlinked, there is no need for updating it. - */ - if (ei->dir.dir == DIR_DELETED) - return 0; - - if (is_dir && ei->dir.dir == sbi->root_dir && ei->entry == -1) - return 0; - - exfat_set_volume_dirty(sb); - - /* get the directory entry of given file or directory */ - es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES); - if (!es) - return -EIO; - ep = exfat_get_dentry_cached(es, 0); - ep2 = exfat_get_dentry_cached(es, 1); - - ep->dentry.file.attr = cpu_to_le16(exfat_make_attr(inode)); - - /* set FILE_INFO structure using the acquired struct exfat_dentry */ - exfat_set_entry_time(sbi, &ei->i_crtime, - &ep->dentry.file.create_tz, - &ep->dentry.file.create_time, - &ep->dentry.file.create_date, - &ep->dentry.file.create_time_cs); - exfat_set_entry_time(sbi, &inode->i_mtime, - &ep->dentry.file.modify_tz, - &ep->dentry.file.modify_time, - &ep->dentry.file.modify_date, - &ep->dentry.file.modify_time_cs); - exfat_set_entry_time(sbi, &inode->i_atime, - &ep->dentry.file.access_tz, - &ep->dentry.file.access_time, - &ep->dentry.file.access_date, - NULL); - - /* File size should be zero if there is no cluster allocated */ - on_disk_size = i_size_read(inode); - - if (ei->start_clu == EXFAT_EOF_CLUSTER) - on_disk_size = 0; - - ep2->dentry.stream.valid_size = cpu_to_le64(on_disk_size); - ep2->dentry.stream.size = ep2->dentry.stream.valid_size; - - exfat_update_dir_chksum_with_entry_set(es); - return exfat_free_dentry_set(es, sync); -} - -int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) -{ - int ret; - - mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock); - ret = __exfat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL); - mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock); - - return ret; -} - -void exfat_sync_inode(struct inode *inode) -{ - lockdep_assert_held(&EXFAT_SB(inode->i_sb)->s_lock); - __exfat_write_inode(inode, 1); -} - -/* - * Input: inode, (logical) clu_offset, target allocation area - * Output: errcode, cluster number - * *clu = (~0), if it's unable to allocate a new cluster - */ -static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, - unsigned int *clu, int create) -{ - int ret, modified = false; - unsigned int last_clu; - struct exfat_chain new_clu; - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_inode_info *ei = EXFAT_I(inode); - unsigned int local_clu_offset = clu_offset; - unsigned int num_to_be_allocated = 0, num_clusters = 0; - - if (EXFAT_I(inode)->i_size_ondisk > 0) - num_clusters = - EXFAT_B_TO_CLU_ROUND_UP(EXFAT_I(inode)->i_size_ondisk, - sbi); - - if (clu_offset >= num_clusters) - num_to_be_allocated = clu_offset - num_clusters + 1; - - if (!create && (num_to_be_allocated > 0)) { - *clu = EXFAT_EOF_CLUSTER; - return 0; - } - - *clu = last_clu = ei->start_clu; - - if (ei->flags == ALLOC_NO_FAT_CHAIN) { - if (clu_offset > 0 && *clu != EXFAT_EOF_CLUSTER) { - last_clu += clu_offset - 1; - - if (clu_offset == num_clusters) - *clu = EXFAT_EOF_CLUSTER; - else - *clu += clu_offset; - } - } else if (ei->type == TYPE_FILE) { - unsigned int fclus = 0; - int err = exfat_get_cluster(inode, clu_offset, - &fclus, clu, &last_clu, 1); - if (err) - return -EIO; - - clu_offset -= fclus; - } else { - /* hint information */ - if (clu_offset > 0 && ei->hint_bmap.off != EXFAT_EOF_CLUSTER && - ei->hint_bmap.off > 0 && clu_offset >= ei->hint_bmap.off) { - clu_offset -= ei->hint_bmap.off; - /* hint_bmap.clu should be valid */ - WARN_ON(ei->hint_bmap.clu < 2); - *clu = ei->hint_bmap.clu; - } - - while (clu_offset > 0 && *clu != EXFAT_EOF_CLUSTER) { - last_clu = *clu; - if (exfat_get_next_cluster(sb, clu)) - return -EIO; - clu_offset--; - } - } - - if (*clu == EXFAT_EOF_CLUSTER) { - exfat_set_volume_dirty(sb); - - new_clu.dir = (last_clu == EXFAT_EOF_CLUSTER) ? - EXFAT_EOF_CLUSTER : last_clu + 1; - new_clu.size = 0; - new_clu.flags = ei->flags; - - /* allocate a cluster */ - if (num_to_be_allocated < 1) { - /* Broken FAT (i_sze > allocated FAT) */ - exfat_fs_error(sb, "broken FAT chain."); - return -EIO; - } - - ret = exfat_alloc_cluster(inode, num_to_be_allocated, &new_clu, - inode_needs_sync(inode)); - if (ret) - return ret; - - if (new_clu.dir == EXFAT_EOF_CLUSTER || - new_clu.dir == EXFAT_FREE_CLUSTER) { - exfat_fs_error(sb, - "bogus cluster new allocated (last_clu : %u, new_clu : %u)", - last_clu, new_clu.dir); - return -EIO; - } - - /* append to the FAT chain */ - if (last_clu == EXFAT_EOF_CLUSTER) { - if (new_clu.flags == ALLOC_FAT_CHAIN) - ei->flags = ALLOC_FAT_CHAIN; - ei->start_clu = new_clu.dir; - modified = true; - } else { - if (new_clu.flags != ei->flags) { - /* no-fat-chain bit is disabled, - * so fat-chain should be synced with - * alloc-bitmap - */ - exfat_chain_cont_cluster(sb, ei->start_clu, - num_clusters); - ei->flags = ALLOC_FAT_CHAIN; - modified = true; - } - if (new_clu.flags == ALLOC_FAT_CHAIN) - if (exfat_ent_set(sb, last_clu, new_clu.dir)) - return -EIO; - } - - num_clusters += num_to_be_allocated; - *clu = new_clu.dir; - - if (ei->dir.dir != DIR_DELETED && modified) { - struct exfat_dentry *ep; - struct exfat_entry_set_cache *es; - int err; - - es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, - ES_ALL_ENTRIES); - if (!es) - return -EIO; - /* get stream entry */ - ep = exfat_get_dentry_cached(es, 1); - - /* update directory entry */ - ep->dentry.stream.flags = ei->flags; - ep->dentry.stream.start_clu = - cpu_to_le32(ei->start_clu); - ep->dentry.stream.valid_size = - cpu_to_le64(i_size_read(inode)); - ep->dentry.stream.size = - ep->dentry.stream.valid_size; - - exfat_update_dir_chksum_with_entry_set(es); - err = exfat_free_dentry_set(es, inode_needs_sync(inode)); - if (err) - return err; - } /* end of if != DIR_DELETED */ - - inode->i_blocks += - num_to_be_allocated << sbi->sect_per_clus_bits; - - /* - * Move *clu pointer along FAT chains (hole care) because the - * caller of this function expect *clu to be the last cluster. - * This only works when num_to_be_allocated >= 2, - * *clu = (the first cluster of the allocated chain) => - * (the last cluster of ...) - */ - if (ei->flags == ALLOC_NO_FAT_CHAIN) { - *clu += num_to_be_allocated - 1; - } else { - while (num_to_be_allocated > 1) { - if (exfat_get_next_cluster(sb, clu)) - return -EIO; - num_to_be_allocated--; - } - } - - } - - /* hint information */ - ei->hint_bmap.off = local_clu_offset; - ei->hint_bmap.clu = *clu; - - return 0; -} - -static int exfat_map_new_buffer(struct exfat_inode_info *ei, - struct buffer_head *bh, loff_t pos) -{ - if (buffer_delay(bh) && pos > ei->i_size_aligned) - return -EIO; - set_buffer_new(bh); - - /* - * Adjust i_size_aligned if i_size_ondisk is bigger than it. - */ - if (ei->i_size_ondisk > ei->i_size_aligned) - ei->i_size_aligned = ei->i_size_ondisk; - return 0; -} - -static int exfat_get_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create) -{ - struct exfat_inode_info *ei = EXFAT_I(inode); - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; - int err = 0; - unsigned long mapped_blocks = 0; - unsigned int cluster, sec_offset; - sector_t last_block; - sector_t phys = 0; - loff_t pos; - - mutex_lock(&sbi->s_lock); - last_block = EXFAT_B_TO_BLK_ROUND_UP(i_size_read(inode), sb); - if (iblock >= last_block && !create) - goto done; - - /* Is this block already allocated? */ - err = exfat_map_cluster(inode, iblock >> sbi->sect_per_clus_bits, - &cluster, create); - if (err) { - if (err != -ENOSPC) - exfat_fs_error_ratelimit(sb, - "failed to bmap (inode : %p iblock : %llu, err : %d)", - inode, (unsigned long long)iblock, err); - goto unlock_ret; - } - - if (cluster == EXFAT_EOF_CLUSTER) - goto done; - - /* sector offset in cluster */ - sec_offset = iblock & (sbi->sect_per_clus - 1); - - phys = exfat_cluster_to_sector(sbi, cluster) + sec_offset; - mapped_blocks = sbi->sect_per_clus - sec_offset; - max_blocks = min(mapped_blocks, max_blocks); - - /* Treat newly added block / cluster */ - if (iblock < last_block) - create = 0; - - if (create || buffer_delay(bh_result)) { - pos = EXFAT_BLK_TO_B((iblock + 1), sb); - if (ei->i_size_ondisk < pos) - ei->i_size_ondisk = pos; - } - - if (create) { - err = exfat_map_new_buffer(ei, bh_result, pos); - if (err) { - exfat_fs_error(sb, - "requested for bmap out of range(pos : (%llu) > i_size_aligned(%llu)\n", - pos, ei->i_size_aligned); - goto unlock_ret; - } - } - - if (buffer_delay(bh_result)) - clear_buffer_delay(bh_result); - map_bh(bh_result, sb, phys); -done: - bh_result->b_size = EXFAT_BLK_TO_B(max_blocks, sb); -unlock_ret: - mutex_unlock(&sbi->s_lock); - return err; -} - -static int exfat_readpage(struct file *file, struct page *page) -{ - return mpage_readpage(page, exfat_get_block); -} - -static void exfat_readahead(struct readahead_control *rac) -{ - mpage_readahead(rac, exfat_get_block); -} - -static int exfat_writepage(struct page *page, struct writeback_control *wbc) -{ - return block_write_full_page(page, exfat_get_block, wbc); -} - -static int exfat_writepages(struct address_space *mapping, - struct writeback_control *wbc) -{ - return mpage_writepages(mapping, wbc, exfat_get_block); -} - -static void exfat_write_failed(struct address_space *mapping, loff_t to) -{ - struct inode *inode = mapping->host; - - if (to > i_size_read(inode)) { - truncate_pagecache(inode, i_size_read(inode)); - exfat_truncate(inode, EXFAT_I(inode)->i_size_aligned); - } -} - -static int exfat_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned int len, unsigned int flags, - struct page **pagep, void **fsdata) -{ - int ret; - - *pagep = NULL; - ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, - exfat_get_block, - &EXFAT_I(mapping->host)->i_size_ondisk); - - if (ret < 0) - exfat_write_failed(mapping, pos+len); - - return ret; -} - -static int exfat_write_end(struct file *file, struct address_space *mapping, - loff_t pos, unsigned int len, unsigned int copied, - struct page *pagep, void *fsdata) -{ - struct inode *inode = mapping->host; - struct exfat_inode_info *ei = EXFAT_I(inode); - int err; - - err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); - - if (EXFAT_I(inode)->i_size_aligned < i_size_read(inode)) { - exfat_fs_error(inode->i_sb, - "invalid size(size(%llu) > aligned(%llu)\n", - i_size_read(inode), EXFAT_I(inode)->i_size_aligned); - return -EIO; - } - - if (err < len) - exfat_write_failed(mapping, pos+len); - - if (!(err < 0) && !(ei->attr & ATTR_ARCHIVE)) { - inode->i_mtime = inode->i_ctime = current_time(inode); - ei->attr |= ATTR_ARCHIVE; - mark_inode_dirty(inode); - } - - return err; -} - -static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter) -{ - struct address_space *mapping = iocb->ki_filp->f_mapping; - struct inode *inode = mapping->host; - loff_t size = iocb->ki_pos + iov_iter_count(iter); - int rw = iov_iter_rw(iter); - ssize_t ret; - - if (rw == WRITE) { - /* - * FIXME: blockdev_direct_IO() doesn't use ->write_begin(), - * so we need to update the ->i_size_aligned to block boundary. - * - * But we must fill the remaining area or hole by nul for - * updating ->i_size_aligned - * - * Return 0, and fallback to normal buffered write. - */ - if (EXFAT_I(inode)->i_size_aligned < size) - return 0; - } - - /* - * Need to use the DIO_LOCKING for avoiding the race - * condition of exfat_get_block() and ->truncate(). - */ - ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block); - if (ret < 0 && (rw & WRITE)) - exfat_write_failed(mapping, size); - return ret; -} - -static sector_t exfat_aop_bmap(struct address_space *mapping, sector_t block) -{ - sector_t blocknr; - - /* exfat_get_cluster() assumes the requested blocknr isn't truncated. */ - down_read(&EXFAT_I(mapping->host)->truncate_lock); - blocknr = generic_block_bmap(mapping, block, exfat_get_block); - up_read(&EXFAT_I(mapping->host)->truncate_lock); - return blocknr; -} - -/* - * exfat_block_truncate_page() zeroes out a mapping from file offset `from' - * up to the end of the block which corresponds to `from'. - * This is required during truncate to physically zeroout the tail end - * of that block so it doesn't yield old data if the file is later grown. - * Also, avoid causing failure from fsx for cases of "data past EOF" - */ -int exfat_block_truncate_page(struct inode *inode, loff_t from) -{ - return block_truncate_page(inode->i_mapping, from, exfat_get_block); -} - -static const struct address_space_operations exfat_aops = { - .set_page_dirty = __set_page_dirty_buffers, - .readpage = exfat_readpage, - .readahead = exfat_readahead, - .writepage = exfat_writepage, - .writepages = exfat_writepages, - .write_begin = exfat_write_begin, - .write_end = exfat_write_end, - .direct_IO = exfat_direct_IO, - .bmap = exfat_aop_bmap -}; - -static inline unsigned long exfat_hash(loff_t i_pos) -{ - return hash_32(i_pos, EXFAT_HASH_BITS); -} - -void exfat_hash_inode(struct inode *inode, loff_t i_pos) -{ - struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); - struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); - - spin_lock(&sbi->inode_hash_lock); - EXFAT_I(inode)->i_pos = i_pos; - hlist_add_head(&EXFAT_I(inode)->i_hash_fat, head); - spin_unlock(&sbi->inode_hash_lock); -} - -void exfat_unhash_inode(struct inode *inode) -{ - struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); - - spin_lock(&sbi->inode_hash_lock); - hlist_del_init(&EXFAT_I(inode)->i_hash_fat); - EXFAT_I(inode)->i_pos = 0; - spin_unlock(&sbi->inode_hash_lock); -} - -struct inode *exfat_iget(struct super_block *sb, loff_t i_pos) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_inode_info *info; - struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); - struct inode *inode = NULL; - - spin_lock(&sbi->inode_hash_lock); - hlist_for_each_entry(info, head, i_hash_fat) { - WARN_ON(info->vfs_inode.i_sb != sb); - - if (i_pos != info->i_pos) - continue; - inode = igrab(&info->vfs_inode); - if (inode) - break; - } - spin_unlock(&sbi->inode_hash_lock); - return inode; -} - -/* doesn't deal with root inode */ -static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info) -{ - struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); - struct exfat_inode_info *ei = EXFAT_I(inode); - loff_t size = info->size; - - ei->dir = info->dir; - ei->entry = info->entry; - ei->attr = info->attr; - ei->start_clu = info->start_clu; - ei->flags = info->flags; - ei->type = info->type; - - ei->version = 0; - ei->hint_stat.eidx = 0; - ei->hint_stat.clu = info->start_clu; - ei->hint_femp.eidx = EXFAT_HINT_NONE; - ei->hint_bmap.off = EXFAT_EOF_CLUSTER; - ei->i_pos = 0; - - inode->i_uid = sbi->options.fs_uid; - inode->i_gid = sbi->options.fs_gid; - inode_inc_iversion(inode); - inode->i_generation = prandom_u32(); - - if (info->attr & ATTR_SUBDIR) { /* directory */ - inode->i_generation &= ~1; - inode->i_mode = exfat_make_mode(sbi, info->attr, 0777); - inode->i_op = &exfat_dir_inode_operations; - inode->i_fop = &exfat_dir_operations; - set_nlink(inode, info->num_subdirs); - } else { /* regular file */ - inode->i_generation |= 1; - inode->i_mode = exfat_make_mode(sbi, info->attr, 0777); - inode->i_op = &exfat_file_inode_operations; - inode->i_fop = &exfat_file_operations; - inode->i_mapping->a_ops = &exfat_aops; - inode->i_mapping->nrpages = 0; - } - - i_size_write(inode, size); - - /* ondisk and aligned size should be aligned with block size */ - if (size & (inode->i_sb->s_blocksize - 1)) { - size |= (inode->i_sb->s_blocksize - 1); - size++; - } - - ei->i_size_aligned = size; - ei->i_size_ondisk = size; - - exfat_save_attr(inode, info->attr); - - inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) & - ~(sbi->cluster_size - 1)) >> inode->i_blkbits; - inode->i_mtime = info->mtime; - inode->i_ctime = info->mtime; - ei->i_crtime = info->crtime; - inode->i_atime = info->atime; - - return 0; -} - -struct inode *exfat_build_inode(struct super_block *sb, - struct exfat_dir_entry *info, loff_t i_pos) -{ - struct inode *inode; - int err; - - inode = exfat_iget(sb, i_pos); - if (inode) - goto out; - inode = new_inode(sb); - if (!inode) { - inode = ERR_PTR(-ENOMEM); - goto out; - } - inode->i_ino = iunique(sb, EXFAT_ROOT_INO); - inode_set_iversion(inode, 1); - err = exfat_fill_inode(inode, info); - if (err) { - iput(inode); - inode = ERR_PTR(err); - goto out; - } - exfat_hash_inode(inode, i_pos); - insert_inode_hash(inode); -out: - return inode; -} - -void exfat_evict_inode(struct inode *inode) -{ - truncate_inode_pages(&inode->i_data, 0); - - if (!inode->i_nlink) { - i_size_write(inode, 0); - mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock); - __exfat_truncate(inode, 0); - mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock); - } - - invalidate_inode_buffers(inode); - clear_inode(inode); - exfat_cache_inval_inode(inode); - exfat_unhash_inode(inode); -} diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c deleted file mode 100644 index d34e61932..000000000 --- a/fs/exfat/misc.c +++ /dev/null @@ -1,205 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Written 1992,1993 by Werner Almesberger - * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 - * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) - * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include - -#include "exfat_raw.h" -#include "exfat_fs.h" - -/* - * exfat_fs_error reports a file system problem that might indicate fa data - * corruption/inconsistency. Depending on 'errors' mount option the - * panic() is called, or error message is printed FAT and nothing is done, - * or filesystem is remounted read-only (default behavior). - * In case the file system is remounted read-only, it can be made writable - * again by remounting it. - */ -void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) -{ - struct exfat_mount_options *opts = &EXFAT_SB(sb)->options; - va_list args; - struct va_format vaf; - - if (report) { - va_start(args, fmt); - vaf.fmt = fmt; - vaf.va = &args; - exfat_err(sb, "error, %pV", &vaf); - va_end(args); - } - - if (opts->errors == EXFAT_ERRORS_PANIC) { - panic("exFAT-fs (%s): fs panic from previous error\n", - sb->s_id); - } else if (opts->errors == EXFAT_ERRORS_RO && !sb_rdonly(sb)) { - sb->s_flags |= SB_RDONLY; - exfat_err(sb, "Filesystem has been set read-only"); - } -} - -/* - * exfat_msg() - print preformated EXFAT specific messages. - * All logs except what uses exfat_fs_error() should be written by exfat_msg() - */ -void exfat_msg(struct super_block *sb, const char *level, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - va_start(args, fmt); - vaf.fmt = fmt; - vaf.va = &args; - /* level means KERN_ pacility level */ - printk("%sexFAT-fs (%s): %pV\n", level, sb->s_id, &vaf); - va_end(args); -} - -#define SECS_PER_MIN (60) -#define TIMEZONE_SEC(x) ((x) * 15 * SECS_PER_MIN) - -static void exfat_adjust_tz(struct timespec64 *ts, u8 tz_off) -{ - if (tz_off <= 0x3F) - ts->tv_sec -= TIMEZONE_SEC(tz_off); - else /* 0x40 <= (tz_off & 0x7F) <=0x7F */ - ts->tv_sec += TIMEZONE_SEC(0x80 - tz_off); -} - -/* Convert a EXFAT time/date pair to a UNIX date (seconds since 1 1 70). */ -void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts, - u8 tz, __le16 time, __le16 date, u8 time_cs) -{ - u16 t = le16_to_cpu(time); - u16 d = le16_to_cpu(date); - - ts->tv_sec = mktime64(1980 + (d >> 9), d >> 5 & 0x000F, d & 0x001F, - t >> 11, (t >> 5) & 0x003F, (t & 0x001F) << 1); - - - /* time_cs field represent 0 ~ 199cs(1990 ms) */ - if (time_cs) { - ts->tv_sec += time_cs / 100; - ts->tv_nsec = (time_cs % 100) * 10 * NSEC_PER_MSEC; - } else - ts->tv_nsec = 0; - - if (tz & EXFAT_TZ_VALID) - /* Adjust timezone to UTC0. */ - exfat_adjust_tz(ts, tz & ~EXFAT_TZ_VALID); - else - /* Convert from local time to UTC using time_offset. */ - ts->tv_sec -= sbi->options.time_offset * SECS_PER_MIN; -} - -/* Convert linear UNIX date to a EXFAT time/date pair. */ -void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts, - u8 *tz, __le16 *time, __le16 *date, u8 *time_cs) -{ - struct tm tm; - u16 t, d; - - time64_to_tm(ts->tv_sec, 0, &tm); - t = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1); - d = ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday; - - *time = cpu_to_le16(t); - *date = cpu_to_le16(d); - - /* time_cs field represent 0 ~ 199cs(1990 ms) */ - if (time_cs) - *time_cs = (tm.tm_sec & 1) * 100 + - ts->tv_nsec / (10 * NSEC_PER_MSEC); - - /* - * Record 00h value for OffsetFromUtc field and 1 value for OffsetValid - * to indicate that local time and UTC are the same. - */ - *tz = EXFAT_TZ_VALID; -} - -/* - * The timestamp for access_time has double seconds granularity. - * (There is no 10msIncrement field for access_time unlike create/modify_time) - * atime also has only a 2-second resolution. - */ -void exfat_truncate_atime(struct timespec64 *ts) -{ - ts->tv_sec = round_down(ts->tv_sec, 2); - ts->tv_nsec = 0; -} - -u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type) -{ - int i; - u8 *c = (u8 *)data; - - for (i = 0; i < len; i++, c++) { - if (unlikely(type == CS_DIR_ENTRY && (i == 2 || i == 3))) - continue; - chksum = ((chksum << 15) | (chksum >> 1)) + *c; - } - return chksum; -} - -u32 exfat_calc_chksum32(void *data, int len, u32 chksum, int type) -{ - int i; - u8 *c = (u8 *)data; - - for (i = 0; i < len; i++, c++) { - if (unlikely(type == CS_BOOT_SECTOR && - (i == 106 || i == 107 || i == 112))) - continue; - chksum = ((chksum << 31) | (chksum >> 1)) + *c; - } - return chksum; -} - -void exfat_update_bh(struct buffer_head *bh, int sync) -{ - set_buffer_uptodate(bh); - mark_buffer_dirty(bh); - - if (sync) - sync_dirty_buffer(bh); -} - -int exfat_update_bhs(struct buffer_head **bhs, int nr_bhs, int sync) -{ - int i, err = 0; - - for (i = 0; i < nr_bhs; i++) { - set_buffer_uptodate(bhs[i]); - mark_buffer_dirty(bhs[i]); - if (sync) - write_dirty_buffer(bhs[i], 0); - } - - for (i = 0; i < nr_bhs && sync; i++) { - wait_on_buffer(bhs[i]); - if (!err && !buffer_uptodate(bhs[i])) - err = -EIO; - } - return err; -} - -void exfat_chain_set(struct exfat_chain *ec, unsigned int dir, - unsigned int size, unsigned char flags) -{ - ec->dir = dir; - ec->size = size; - ec->flags = flags; -} - -void exfat_chain_dup(struct exfat_chain *dup, struct exfat_chain *ec) -{ - return exfat_chain_set(dup, ec->dir, ec->size, ec->flags); -} diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c deleted file mode 100644 index 24b41103d..000000000 --- a/fs/exfat/namei.c +++ /dev/null @@ -1,1416 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include - -#include "exfat_raw.h" -#include "exfat_fs.h" - -static inline unsigned long exfat_d_version(struct dentry *dentry) -{ - return (unsigned long) dentry->d_fsdata; -} - -static inline void exfat_d_version_set(struct dentry *dentry, - unsigned long version) -{ - dentry->d_fsdata = (void *) version; -} - -/* - * If new entry was created in the parent, it could create the 8.3 alias (the - * shortname of logname). So, the parent may have the negative-dentry which - * matches the created 8.3 alias. - * - * If it happened, the negative dentry isn't actually negative anymore. So, - * drop it. - */ -static int exfat_d_revalidate(struct dentry *dentry, unsigned int flags) -{ - int ret; - - if (flags & LOOKUP_RCU) - return -ECHILD; - - /* - * This is not negative dentry. Always valid. - * - * Note, rename() to existing directory entry will have ->d_inode, and - * will use existing name which isn't specified name by user. - * - * We may be able to drop this positive dentry here. But dropping - * positive dentry isn't good idea. So it's unsupported like - * rename("filename", "FILENAME") for now. - */ - if (d_really_is_positive(dentry)) - return 1; - - /* - * Drop the negative dentry, in order to make sure to use the case - * sensitive name which is specified by user if this is for creation. - */ - if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) - return 0; - - spin_lock(&dentry->d_lock); - ret = inode_eq_iversion(d_inode(dentry->d_parent), - exfat_d_version(dentry)); - spin_unlock(&dentry->d_lock); - return ret; -} - -/* returns the length of a struct qstr, ignoring trailing dots */ -static unsigned int exfat_striptail_len(unsigned int len, const char *name) -{ - while (len && name[len - 1] == '.') - len--; - return len; -} - -/* - * Compute the hash for the exfat name corresponding to the dentry. If the name - * is invalid, we leave the hash code unchanged so that the existing dentry can - * be used. The exfat fs routines will return ENOENT or EINVAL as appropriate. - */ -static int exfat_d_hash(const struct dentry *dentry, struct qstr *qstr) -{ - struct super_block *sb = dentry->d_sb; - struct nls_table *t = EXFAT_SB(sb)->nls_io; - const unsigned char *name = qstr->name; - unsigned int len = exfat_striptail_len(qstr->len, qstr->name); - unsigned long hash = init_name_hash(dentry); - int i, charlen; - wchar_t c; - - for (i = 0; i < len; i += charlen) { - charlen = t->char2uni(&name[i], len - i, &c); - if (charlen < 0) - return charlen; - hash = partial_name_hash(exfat_toupper(sb, c), hash); - } - - qstr->hash = end_name_hash(hash); - return 0; -} - -static int exfat_d_cmp(const struct dentry *dentry, unsigned int len, - const char *str, const struct qstr *name) -{ - struct super_block *sb = dentry->d_sb; - struct nls_table *t = EXFAT_SB(sb)->nls_io; - unsigned int alen = exfat_striptail_len(name->len, name->name); - unsigned int blen = exfat_striptail_len(len, str); - wchar_t c1, c2; - int charlen, i; - - if (alen != blen) - return 1; - - for (i = 0; i < len; i += charlen) { - charlen = t->char2uni(&name->name[i], alen - i, &c1); - if (charlen < 0) - return 1; - if (charlen != t->char2uni(&str[i], blen - i, &c2)) - return 1; - - if (exfat_toupper(sb, c1) != exfat_toupper(sb, c2)) - return 1; - } - - return 0; -} - -const struct dentry_operations exfat_dentry_ops = { - .d_revalidate = exfat_d_revalidate, - .d_hash = exfat_d_hash, - .d_compare = exfat_d_cmp, -}; - -static int exfat_utf8_d_hash(const struct dentry *dentry, struct qstr *qstr) -{ - struct super_block *sb = dentry->d_sb; - const unsigned char *name = qstr->name; - unsigned int len = exfat_striptail_len(qstr->len, qstr->name); - unsigned long hash = init_name_hash(dentry); - int i, charlen; - unicode_t u; - - for (i = 0; i < len; i += charlen) { - charlen = utf8_to_utf32(&name[i], len - i, &u); - if (charlen < 0) - return charlen; - - /* - * exfat_toupper() works only for code points up to the U+FFFF. - */ - hash = partial_name_hash(u <= 0xFFFF ? exfat_toupper(sb, u) : u, - hash); - } - - qstr->hash = end_name_hash(hash); - return 0; -} - -static int exfat_utf8_d_cmp(const struct dentry *dentry, unsigned int len, - const char *str, const struct qstr *name) -{ - struct super_block *sb = dentry->d_sb; - unsigned int alen = exfat_striptail_len(name->len, name->name); - unsigned int blen = exfat_striptail_len(len, str); - unicode_t u_a, u_b; - int charlen, i; - - if (alen != blen) - return 1; - - for (i = 0; i < alen; i += charlen) { - charlen = utf8_to_utf32(&name->name[i], alen - i, &u_a); - if (charlen < 0) - return 1; - if (charlen != utf8_to_utf32(&str[i], blen - i, &u_b)) - return 1; - - if (u_a <= 0xFFFF && u_b <= 0xFFFF) { - if (exfat_toupper(sb, u_a) != exfat_toupper(sb, u_b)) - return 1; - } else { - if (u_a != u_b) - return 1; - } - } - - return 0; -} - -const struct dentry_operations exfat_utf8_dentry_ops = { - .d_revalidate = exfat_d_revalidate, - .d_hash = exfat_utf8_d_hash, - .d_compare = exfat_utf8_d_cmp, -}; - -/* used only in search empty_slot() */ -#define CNT_UNUSED_NOHIT (-1) -#define CNT_UNUSED_HIT (-2) -/* search EMPTY CONTINUOUS "num_entries" entries */ -static int exfat_search_empty_slot(struct super_block *sb, - struct exfat_hint_femp *hint_femp, struct exfat_chain *p_dir, - int num_entries) -{ - int i, dentry, num_empty = 0; - int dentries_per_clu; - unsigned int type; - struct exfat_chain clu; - struct exfat_dentry *ep; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct buffer_head *bh; - - dentries_per_clu = sbi->dentries_per_clu; - - if (hint_femp->eidx != EXFAT_HINT_NONE) { - dentry = hint_femp->eidx; - if (num_entries <= hint_femp->count) { - hint_femp->eidx = EXFAT_HINT_NONE; - return dentry; - } - - exfat_chain_dup(&clu, &hint_femp->cur); - } else { - exfat_chain_dup(&clu, p_dir); - dentry = 0; - } - - while (clu.dir != EXFAT_EOF_CLUSTER) { - i = dentry & (dentries_per_clu - 1); - - for (; i < dentries_per_clu; i++, dentry++) { - ep = exfat_get_dentry(sb, &clu, i, &bh, NULL); - if (!ep) - return -EIO; - type = exfat_get_entry_type(ep); - brelse(bh); - - if (type == TYPE_UNUSED || type == TYPE_DELETED) { - num_empty++; - if (hint_femp->eidx == EXFAT_HINT_NONE) { - hint_femp->eidx = dentry; - hint_femp->count = CNT_UNUSED_NOHIT; - exfat_chain_set(&hint_femp->cur, - clu.dir, clu.size, clu.flags); - } - - if (type == TYPE_UNUSED && - hint_femp->count != CNT_UNUSED_HIT) - hint_femp->count = CNT_UNUSED_HIT; - } else { - if (hint_femp->eidx != EXFAT_HINT_NONE && - hint_femp->count == CNT_UNUSED_HIT) { - /* unused empty group means - * an empty group which includes - * unused dentry - */ - exfat_fs_error(sb, - "found bogus dentry(%d) beyond unused empty group(%d) (start_clu : %u, cur_clu : %u)", - dentry, hint_femp->eidx, - p_dir->dir, clu.dir); - return -EIO; - } - - num_empty = 0; - hint_femp->eidx = EXFAT_HINT_NONE; - } - - if (num_empty >= num_entries) { - /* found and invalidate hint_femp */ - hint_femp->eidx = EXFAT_HINT_NONE; - return (dentry - (num_entries - 1)); - } - } - - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - if (--clu.size > 0) - clu.dir++; - else - clu.dir = EXFAT_EOF_CLUSTER; - } else { - if (exfat_get_next_cluster(sb, &clu.dir)) - return -EIO; - } - } - - return -ENOSPC; -} - -static int exfat_check_max_dentries(struct inode *inode) -{ - if (EXFAT_B_TO_DEN(i_size_read(inode)) >= MAX_EXFAT_DENTRIES) { - /* - * exFAT spec allows a dir to grow up to 8388608(256MB) - * dentries - */ - return -ENOSPC; - } - return 0; -} - -/* find empty directory entry. - * if there isn't any empty slot, expand cluster chain. - */ -static int exfat_find_empty_entry(struct inode *inode, - struct exfat_chain *p_dir, int num_entries) -{ - int dentry; - unsigned int ret, last_clu; - sector_t sector; - loff_t size = 0; - struct exfat_chain clu; - struct exfat_dentry *ep = NULL; - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_inode_info *ei = EXFAT_I(inode); - struct exfat_hint_femp hint_femp; - - hint_femp.eidx = EXFAT_HINT_NONE; - - if (ei->hint_femp.eidx != EXFAT_HINT_NONE) { - hint_femp = ei->hint_femp; - ei->hint_femp.eidx = EXFAT_HINT_NONE; - } - - while ((dentry = exfat_search_empty_slot(sb, &hint_femp, p_dir, - num_entries)) < 0) { - if (dentry == -EIO) - break; - - if (exfat_check_max_dentries(inode)) - return -ENOSPC; - - /* we trust p_dir->size regardless of FAT type */ - if (exfat_find_last_cluster(sb, p_dir, &last_clu)) - return -EIO; - - /* - * Allocate new cluster to this directory - */ - exfat_chain_set(&clu, last_clu + 1, 0, p_dir->flags); - - /* allocate a cluster */ - ret = exfat_alloc_cluster(inode, 1, &clu, IS_DIRSYNC(inode)); - if (ret) - return ret; - - if (exfat_zeroed_cluster(inode, clu.dir)) - return -EIO; - - /* append to the FAT chain */ - if (clu.flags != p_dir->flags) { - /* no-fat-chain bit is disabled, - * so fat-chain should be synced with alloc-bitmap - */ - exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size); - p_dir->flags = ALLOC_FAT_CHAIN; - hint_femp.cur.flags = ALLOC_FAT_CHAIN; - } - - if (clu.flags == ALLOC_FAT_CHAIN) - if (exfat_ent_set(sb, last_clu, clu.dir)) - return -EIO; - - if (hint_femp.eidx == EXFAT_HINT_NONE) { - /* the special case that new dentry - * should be allocated from the start of new cluster - */ - hint_femp.eidx = EXFAT_B_TO_DEN_IDX(p_dir->size, sbi); - hint_femp.count = sbi->dentries_per_clu; - - exfat_chain_set(&hint_femp.cur, clu.dir, 0, clu.flags); - } - hint_femp.cur.size++; - p_dir->size++; - size = EXFAT_CLU_TO_B(p_dir->size, sbi); - - /* update the directory entry */ - if (p_dir->dir != sbi->root_dir) { - struct buffer_head *bh; - - ep = exfat_get_dentry(sb, - &(ei->dir), ei->entry + 1, &bh, §or); - if (!ep) - return -EIO; - - ep->dentry.stream.valid_size = cpu_to_le64(size); - ep->dentry.stream.size = ep->dentry.stream.valid_size; - ep->dentry.stream.flags = p_dir->flags; - exfat_update_bh(bh, IS_DIRSYNC(inode)); - brelse(bh); - if (exfat_update_dir_chksum(inode, &(ei->dir), - ei->entry)) - return -EIO; - } - - /* directory inode should be updated in here */ - i_size_write(inode, size); - EXFAT_I(inode)->i_size_ondisk += sbi->cluster_size; - EXFAT_I(inode)->i_size_aligned += sbi->cluster_size; - EXFAT_I(inode)->flags = p_dir->flags; - inode->i_blocks += 1 << sbi->sect_per_clus_bits; - } - - return dentry; -} - -/* - * Name Resolution Functions : - * Zero if it was successful; otherwise nonzero. - */ -static int __exfat_resolve_path(struct inode *inode, const unsigned char *path, - struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname, - int lookup) -{ - int namelen; - int lossy = NLS_NAME_NO_LOSSY; - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_inode_info *ei = EXFAT_I(inode); - - /* strip all trailing periods */ - namelen = exfat_striptail_len(strlen(path), path); - if (!namelen) - return -ENOENT; - - if (strlen(path) > (MAX_NAME_LENGTH * MAX_CHARSET_SIZE)) - return -ENAMETOOLONG; - - /* - * strip all leading spaces : - * "MS windows 7" supports leading spaces. - * So we should skip this preprocessing for compatibility. - */ - - /* file name conversion : - * If lookup case, we allow bad-name for compatibility. - */ - namelen = exfat_nls_to_utf16(sb, path, namelen, p_uniname, - &lossy); - if (namelen < 0) - return namelen; /* return error value */ - - if ((lossy && !lookup) || !namelen) - return -EINVAL; - - exfat_chain_set(p_dir, ei->start_clu, - EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); - - return 0; -} - -static inline int exfat_resolve_path(struct inode *inode, - const unsigned char *path, struct exfat_chain *dir, - struct exfat_uni_name *uni) -{ - return __exfat_resolve_path(inode, path, dir, uni, 0); -} - -static inline int exfat_resolve_path_for_lookup(struct inode *inode, - const unsigned char *path, struct exfat_chain *dir, - struct exfat_uni_name *uni) -{ - return __exfat_resolve_path(inode, path, dir, uni, 1); -} - -static inline loff_t exfat_make_i_pos(struct exfat_dir_entry *info) -{ - return ((loff_t) info->dir.dir << 32) | (info->entry & 0xffffffff); -} - -static int exfat_add_entry(struct inode *inode, const char *path, - struct exfat_chain *p_dir, unsigned int type, - struct exfat_dir_entry *info) -{ - int ret, dentry, num_entries; - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_uni_name uniname; - struct exfat_chain clu; - int clu_size = 0; - unsigned int start_clu = EXFAT_FREE_CLUSTER; - - ret = exfat_resolve_path(inode, path, p_dir, &uniname); - if (ret) - goto out; - - num_entries = exfat_calc_num_entries(&uniname); - if (num_entries < 0) { - ret = num_entries; - goto out; - } - - /* exfat_find_empty_entry must be called before alloc_cluster() */ - dentry = exfat_find_empty_entry(inode, p_dir, num_entries); - if (dentry < 0) { - ret = dentry; /* -EIO or -ENOSPC */ - goto out; - } - - if (type == TYPE_DIR) { - ret = exfat_alloc_new_dir(inode, &clu); - if (ret) - goto out; - start_clu = clu.dir; - clu_size = sbi->cluster_size; - } - - /* update the directory entry */ - /* fill the dos name directory entry information of the created file. - * the first cluster is not determined yet. (0) - */ - ret = exfat_init_dir_entry(inode, p_dir, dentry, type, - start_clu, clu_size); - if (ret) - goto out; - - ret = exfat_init_ext_entry(inode, p_dir, dentry, num_entries, &uniname); - if (ret) - goto out; - - info->dir = *p_dir; - info->entry = dentry; - info->flags = ALLOC_NO_FAT_CHAIN; - info->type = type; - - if (type == TYPE_FILE) { - info->attr = ATTR_ARCHIVE; - info->start_clu = EXFAT_EOF_CLUSTER; - info->size = 0; - info->num_subdirs = 0; - } else { - info->attr = ATTR_SUBDIR; - info->start_clu = start_clu; - info->size = clu_size; - info->num_subdirs = EXFAT_MIN_SUBDIR; - } - memset(&info->crtime, 0, sizeof(info->crtime)); - memset(&info->mtime, 0, sizeof(info->mtime)); - memset(&info->atime, 0, sizeof(info->atime)); -out: - return ret; -} - -static int exfat_create(struct user_namespace *mnt_userns, struct inode *dir, - struct dentry *dentry, umode_t mode, bool excl) -{ - struct super_block *sb = dir->i_sb; - struct inode *inode; - struct exfat_chain cdir; - struct exfat_dir_entry info; - loff_t i_pos; - int err; - - mutex_lock(&EXFAT_SB(sb)->s_lock); - exfat_set_volume_dirty(sb); - err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE, - &info); - exfat_clear_volume_dirty(sb); - if (err) - goto unlock; - - inode_inc_iversion(dir); - dir->i_ctime = dir->i_mtime = current_time(dir); - if (IS_DIRSYNC(dir)) - exfat_sync_inode(dir); - else - mark_inode_dirty(dir); - - i_pos = exfat_make_i_pos(&info); - inode = exfat_build_inode(sb, &info, i_pos); - err = PTR_ERR_OR_ZERO(inode); - if (err) - goto unlock; - - inode_inc_iversion(inode); - inode->i_mtime = inode->i_atime = inode->i_ctime = - EXFAT_I(inode)->i_crtime = current_time(inode); - exfat_truncate_atime(&inode->i_atime); - /* timestamp is already written, so mark_inode_dirty() is unneeded. */ - - d_instantiate(dentry, inode); -unlock: - mutex_unlock(&EXFAT_SB(sb)->s_lock); - return err; -} - -/* lookup a file */ -static int exfat_find(struct inode *dir, struct qstr *qname, - struct exfat_dir_entry *info) -{ - int ret, dentry, num_entries, count; - struct exfat_chain cdir; - struct exfat_uni_name uni_name; - struct super_block *sb = dir->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_inode_info *ei = EXFAT_I(dir); - struct exfat_dentry *ep, *ep2; - struct exfat_entry_set_cache *es; - /* for optimized dir & entry to prevent long traverse of cluster chain */ - struct exfat_hint hint_opt; - - if (qname->len == 0) - return -ENOENT; - - /* check the validity of directory name in the given pathname */ - ret = exfat_resolve_path_for_lookup(dir, qname->name, &cdir, &uni_name); - if (ret) - return ret; - - num_entries = exfat_calc_num_entries(&uni_name); - if (num_entries < 0) - return num_entries; - - /* check the validation of hint_stat and initialize it if required */ - if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) { - ei->hint_stat.clu = cdir.dir; - ei->hint_stat.eidx = 0; - ei->version = (inode_peek_iversion_raw(dir) & 0xffffffff); - ei->hint_femp.eidx = EXFAT_HINT_NONE; - } - - /* search the file name for directories */ - dentry = exfat_find_dir_entry(sb, ei, &cdir, &uni_name, - num_entries, TYPE_ALL, &hint_opt); - - if (dentry < 0) - return dentry; /* -error value */ - - info->dir = cdir; - info->entry = dentry; - info->num_subdirs = 0; - - /* adjust cdir to the optimized value */ - cdir.dir = hint_opt.clu; - if (cdir.flags & ALLOC_NO_FAT_CHAIN) - cdir.size -= dentry / sbi->dentries_per_clu; - dentry = hint_opt.eidx; - es = exfat_get_dentry_set(sb, &cdir, dentry, ES_2_ENTRIES); - if (!es) - return -EIO; - ep = exfat_get_dentry_cached(es, 0); - ep2 = exfat_get_dentry_cached(es, 1); - - info->type = exfat_get_entry_type(ep); - info->attr = le16_to_cpu(ep->dentry.file.attr); - info->size = le64_to_cpu(ep2->dentry.stream.valid_size); - if ((info->type == TYPE_FILE) && (info->size == 0)) { - info->flags = ALLOC_NO_FAT_CHAIN; - info->start_clu = EXFAT_EOF_CLUSTER; - } else { - info->flags = ep2->dentry.stream.flags; - info->start_clu = - le32_to_cpu(ep2->dentry.stream.start_clu); - } - - exfat_get_entry_time(sbi, &info->crtime, - ep->dentry.file.create_tz, - ep->dentry.file.create_time, - ep->dentry.file.create_date, - ep->dentry.file.create_time_cs); - exfat_get_entry_time(sbi, &info->mtime, - ep->dentry.file.modify_tz, - ep->dentry.file.modify_time, - ep->dentry.file.modify_date, - ep->dentry.file.modify_time_cs); - exfat_get_entry_time(sbi, &info->atime, - ep->dentry.file.access_tz, - ep->dentry.file.access_time, - ep->dentry.file.access_date, - 0); - exfat_free_dentry_set(es, false); - - if (ei->start_clu == EXFAT_FREE_CLUSTER) { - exfat_fs_error(sb, - "non-zero size file starts with zero cluster (size : %llu, p_dir : %u, entry : 0x%08x)", - i_size_read(dir), ei->dir.dir, ei->entry); - return -EIO; - } - - if (info->type == TYPE_DIR) { - exfat_chain_set(&cdir, info->start_clu, - EXFAT_B_TO_CLU(info->size, sbi), info->flags); - count = exfat_count_dir_entries(sb, &cdir); - if (count < 0) - return -EIO; - - info->num_subdirs = count + EXFAT_MIN_SUBDIR; - } - return 0; -} - -static int exfat_d_anon_disconn(struct dentry *dentry) -{ - return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED); -} - -static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, - unsigned int flags) -{ - struct super_block *sb = dir->i_sb; - struct inode *inode; - struct dentry *alias; - struct exfat_dir_entry info; - int err; - loff_t i_pos; - mode_t i_mode; - - mutex_lock(&EXFAT_SB(sb)->s_lock); - err = exfat_find(dir, &dentry->d_name, &info); - if (err) { - if (err == -ENOENT) { - inode = NULL; - goto out; - } - goto unlock; - } - - i_pos = exfat_make_i_pos(&info); - inode = exfat_build_inode(sb, &info, i_pos); - err = PTR_ERR_OR_ZERO(inode); - if (err) - goto unlock; - - i_mode = inode->i_mode; - alias = d_find_alias(inode); - - /* - * Checking "alias->d_parent == dentry->d_parent" to make sure - * FS is not corrupted (especially double linked dir). - */ - if (alias && alias->d_parent == dentry->d_parent && - !exfat_d_anon_disconn(alias)) { - - /* - * Unhashed alias is able to exist because of revalidate() - * called by lookup_fast. You can easily make this status - * by calling create and lookup concurrently - * In such case, we reuse an alias instead of new dentry - */ - if (d_unhashed(alias)) { - WARN_ON(alias->d_name.hash_len != - dentry->d_name.hash_len); - exfat_info(sb, "rehashed a dentry(%p) in read lookup", - alias); - d_drop(dentry); - d_rehash(alias); - } else if (!S_ISDIR(i_mode)) { - /* - * This inode has non anonymous-DCACHE_DISCONNECTED - * dentry. This means, the user did ->lookup() by an - * another name (longname vs 8.3 alias of it) in past. - * - * Switch to new one for reason of locality if possible. - */ - d_move(alias, dentry); - } - iput(inode); - mutex_unlock(&EXFAT_SB(sb)->s_lock); - return alias; - } - dput(alias); -out: - mutex_unlock(&EXFAT_SB(sb)->s_lock); - if (!inode) - exfat_d_version_set(dentry, inode_query_iversion(dir)); - - return d_splice_alias(inode, dentry); -unlock: - mutex_unlock(&EXFAT_SB(sb)->s_lock); - return ERR_PTR(err); -} - -/* remove an entry, BUT don't truncate */ -static int exfat_unlink(struct inode *dir, struct dentry *dentry) -{ - struct exfat_chain cdir; - struct exfat_dentry *ep; - struct super_block *sb = dir->i_sb; - struct inode *inode = dentry->d_inode; - struct exfat_inode_info *ei = EXFAT_I(inode); - struct buffer_head *bh; - sector_t sector; - int num_entries, entry, err = 0; - - mutex_lock(&EXFAT_SB(sb)->s_lock); - exfat_chain_dup(&cdir, &ei->dir); - entry = ei->entry; - if (ei->dir.dir == DIR_DELETED) { - exfat_err(sb, "abnormal access to deleted dentry"); - err = -ENOENT; - goto unlock; - } - - ep = exfat_get_dentry(sb, &cdir, entry, &bh, §or); - if (!ep) { - err = -EIO; - goto unlock; - } - num_entries = exfat_count_ext_entries(sb, &cdir, entry, ep); - if (num_entries < 0) { - err = -EIO; - brelse(bh); - goto unlock; - } - num_entries++; - brelse(bh); - - exfat_set_volume_dirty(sb); - /* update the directory entry */ - if (exfat_remove_entries(dir, &cdir, entry, 0, num_entries)) { - err = -EIO; - goto unlock; - } - - /* This doesn't modify ei */ - ei->dir.dir = DIR_DELETED; - exfat_clear_volume_dirty(sb); - - inode_inc_iversion(dir); - dir->i_mtime = dir->i_atime = current_time(dir); - exfat_truncate_atime(&dir->i_atime); - if (IS_DIRSYNC(dir)) - exfat_sync_inode(dir); - else - mark_inode_dirty(dir); - - clear_nlink(inode); - inode->i_mtime = inode->i_atime = current_time(inode); - exfat_truncate_atime(&inode->i_atime); - exfat_unhash_inode(inode); - exfat_d_version_set(dentry, inode_query_iversion(dir)); -unlock: - mutex_unlock(&EXFAT_SB(sb)->s_lock); - return err; -} - -static int exfat_mkdir(struct user_namespace *mnt_userns, struct inode *dir, - struct dentry *dentry, umode_t mode) -{ - struct super_block *sb = dir->i_sb; - struct inode *inode; - struct exfat_dir_entry info; - struct exfat_chain cdir; - loff_t i_pos; - int err; - - mutex_lock(&EXFAT_SB(sb)->s_lock); - exfat_set_volume_dirty(sb); - err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR, - &info); - exfat_clear_volume_dirty(sb); - if (err) - goto unlock; - - inode_inc_iversion(dir); - dir->i_ctime = dir->i_mtime = current_time(dir); - if (IS_DIRSYNC(dir)) - exfat_sync_inode(dir); - else - mark_inode_dirty(dir); - inc_nlink(dir); - - i_pos = exfat_make_i_pos(&info); - inode = exfat_build_inode(sb, &info, i_pos); - err = PTR_ERR_OR_ZERO(inode); - if (err) - goto unlock; - - inode_inc_iversion(inode); - inode->i_mtime = inode->i_atime = inode->i_ctime = - EXFAT_I(inode)->i_crtime = current_time(inode); - exfat_truncate_atime(&inode->i_atime); - /* timestamp is already written, so mark_inode_dirty() is unneeded. */ - - d_instantiate(dentry, inode); - -unlock: - mutex_unlock(&EXFAT_SB(sb)->s_lock); - return err; -} - -static int exfat_check_dir_empty(struct super_block *sb, - struct exfat_chain *p_dir) -{ - int i, dentries_per_clu; - unsigned int type; - struct exfat_chain clu; - struct exfat_dentry *ep; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct buffer_head *bh; - - dentries_per_clu = sbi->dentries_per_clu; - - exfat_chain_dup(&clu, p_dir); - - while (clu.dir != EXFAT_EOF_CLUSTER) { - for (i = 0; i < dentries_per_clu; i++) { - ep = exfat_get_dentry(sb, &clu, i, &bh, NULL); - if (!ep) - return -EIO; - type = exfat_get_entry_type(ep); - brelse(bh); - if (type == TYPE_UNUSED) - return 0; - - if (type != TYPE_FILE && type != TYPE_DIR) - continue; - - return -ENOTEMPTY; - } - - if (clu.flags == ALLOC_NO_FAT_CHAIN) { - if (--clu.size > 0) - clu.dir++; - else - clu.dir = EXFAT_EOF_CLUSTER; - } else { - if (exfat_get_next_cluster(sb, &(clu.dir))) - return -EIO; - } - } - - return 0; -} - -static int exfat_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - struct exfat_dentry *ep; - struct exfat_chain cdir, clu_to_free; - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_inode_info *ei = EXFAT_I(inode); - struct buffer_head *bh; - sector_t sector; - int num_entries, entry, err; - - mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock); - - exfat_chain_dup(&cdir, &ei->dir); - entry = ei->entry; - - if (ei->dir.dir == DIR_DELETED) { - exfat_err(sb, "abnormal access to deleted dentry"); - err = -ENOENT; - goto unlock; - } - - exfat_chain_set(&clu_to_free, ei->start_clu, - EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi), ei->flags); - - err = exfat_check_dir_empty(sb, &clu_to_free); - if (err) { - if (err == -EIO) - exfat_err(sb, "failed to exfat_check_dir_empty : err(%d)", - err); - goto unlock; - } - - ep = exfat_get_dentry(sb, &cdir, entry, &bh, §or); - if (!ep) { - err = -EIO; - goto unlock; - } - - num_entries = exfat_count_ext_entries(sb, &cdir, entry, ep); - if (num_entries < 0) { - err = -EIO; - brelse(bh); - goto unlock; - } - num_entries++; - brelse(bh); - - exfat_set_volume_dirty(sb); - err = exfat_remove_entries(dir, &cdir, entry, 0, num_entries); - if (err) { - exfat_err(sb, "failed to exfat_remove_entries : err(%d)", err); - goto unlock; - } - ei->dir.dir = DIR_DELETED; - exfat_clear_volume_dirty(sb); - - inode_inc_iversion(dir); - dir->i_mtime = dir->i_atime = current_time(dir); - exfat_truncate_atime(&dir->i_atime); - if (IS_DIRSYNC(dir)) - exfat_sync_inode(dir); - else - mark_inode_dirty(dir); - drop_nlink(dir); - - clear_nlink(inode); - inode->i_mtime = inode->i_atime = current_time(inode); - exfat_truncate_atime(&inode->i_atime); - exfat_unhash_inode(inode); - exfat_d_version_set(dentry, inode_query_iversion(dir)); -unlock: - mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock); - return err; -} - -static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir, - int oldentry, struct exfat_uni_name *p_uniname, - struct exfat_inode_info *ei) -{ - int ret, num_old_entries, num_new_entries; - sector_t sector_old, sector_new; - struct exfat_dentry *epold, *epnew; - struct super_block *sb = inode->i_sb; - struct buffer_head *new_bh, *old_bh; - int sync = IS_DIRSYNC(inode); - - epold = exfat_get_dentry(sb, p_dir, oldentry, &old_bh, §or_old); - if (!epold) - return -EIO; - - num_old_entries = exfat_count_ext_entries(sb, p_dir, oldentry, epold); - if (num_old_entries < 0) - return -EIO; - num_old_entries++; - - num_new_entries = exfat_calc_num_entries(p_uniname); - if (num_new_entries < 0) - return num_new_entries; - - if (num_old_entries < num_new_entries) { - int newentry; - - newentry = - exfat_find_empty_entry(inode, p_dir, num_new_entries); - if (newentry < 0) - return newentry; /* -EIO or -ENOSPC */ - - epnew = exfat_get_dentry(sb, p_dir, newentry, &new_bh, - §or_new); - if (!epnew) - return -EIO; - - *epnew = *epold; - if (exfat_get_entry_type(epnew) == TYPE_FILE) { - epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE); - ei->attr |= ATTR_ARCHIVE; - } - exfat_update_bh(new_bh, sync); - brelse(old_bh); - brelse(new_bh); - - epold = exfat_get_dentry(sb, p_dir, oldentry + 1, &old_bh, - §or_old); - if (!epold) - return -EIO; - epnew = exfat_get_dentry(sb, p_dir, newentry + 1, &new_bh, - §or_new); - if (!epnew) { - brelse(old_bh); - return -EIO; - } - - *epnew = *epold; - exfat_update_bh(new_bh, sync); - brelse(old_bh); - brelse(new_bh); - - ret = exfat_init_ext_entry(inode, p_dir, newentry, - num_new_entries, p_uniname); - if (ret) - return ret; - - exfat_remove_entries(inode, p_dir, oldentry, 0, - num_old_entries); - ei->entry = newentry; - } else { - if (exfat_get_entry_type(epold) == TYPE_FILE) { - epold->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE); - ei->attr |= ATTR_ARCHIVE; - } - exfat_update_bh(old_bh, sync); - brelse(old_bh); - ret = exfat_init_ext_entry(inode, p_dir, oldentry, - num_new_entries, p_uniname); - if (ret) - return ret; - - exfat_remove_entries(inode, p_dir, oldentry, num_new_entries, - num_old_entries); - } - return 0; -} - -static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir, - int oldentry, struct exfat_chain *p_newdir, - struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei) -{ - int ret, newentry, num_new_entries, num_old_entries; - sector_t sector_mov, sector_new; - struct exfat_dentry *epmov, *epnew; - struct super_block *sb = inode->i_sb; - struct buffer_head *mov_bh, *new_bh; - - epmov = exfat_get_dentry(sb, p_olddir, oldentry, &mov_bh, §or_mov); - if (!epmov) - return -EIO; - - num_old_entries = exfat_count_ext_entries(sb, p_olddir, oldentry, - epmov); - if (num_old_entries < 0) - return -EIO; - num_old_entries++; - - num_new_entries = exfat_calc_num_entries(p_uniname); - if (num_new_entries < 0) - return num_new_entries; - - newentry = exfat_find_empty_entry(inode, p_newdir, num_new_entries); - if (newentry < 0) - return newentry; /* -EIO or -ENOSPC */ - - epnew = exfat_get_dentry(sb, p_newdir, newentry, &new_bh, §or_new); - if (!epnew) - return -EIO; - - *epnew = *epmov; - if (exfat_get_entry_type(epnew) == TYPE_FILE) { - epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE); - ei->attr |= ATTR_ARCHIVE; - } - exfat_update_bh(new_bh, IS_DIRSYNC(inode)); - brelse(mov_bh); - brelse(new_bh); - - epmov = exfat_get_dentry(sb, p_olddir, oldentry + 1, &mov_bh, - §or_mov); - if (!epmov) - return -EIO; - epnew = exfat_get_dentry(sb, p_newdir, newentry + 1, &new_bh, - §or_new); - if (!epnew) { - brelse(mov_bh); - return -EIO; - } - - *epnew = *epmov; - exfat_update_bh(new_bh, IS_DIRSYNC(inode)); - brelse(mov_bh); - brelse(new_bh); - - ret = exfat_init_ext_entry(inode, p_newdir, newentry, num_new_entries, - p_uniname); - if (ret) - return ret; - - exfat_remove_entries(inode, p_olddir, oldentry, 0, num_old_entries); - - exfat_chain_set(&ei->dir, p_newdir->dir, p_newdir->size, - p_newdir->flags); - - ei->entry = newentry; - return 0; -} - -static void exfat_update_parent_info(struct exfat_inode_info *ei, - struct inode *parent_inode) -{ - struct exfat_sb_info *sbi = EXFAT_SB(parent_inode->i_sb); - struct exfat_inode_info *parent_ei = EXFAT_I(parent_inode); - loff_t parent_isize = i_size_read(parent_inode); - - /* - * the problem that struct exfat_inode_info caches wrong parent info. - * - * because of flag-mismatch of ei->dir, - * there is abnormal traversing cluster chain. - */ - if (unlikely(parent_ei->flags != ei->dir.flags || - parent_isize != EXFAT_CLU_TO_B(ei->dir.size, sbi) || - parent_ei->start_clu != ei->dir.dir)) { - exfat_chain_set(&ei->dir, parent_ei->start_clu, - EXFAT_B_TO_CLU_ROUND_UP(parent_isize, sbi), - parent_ei->flags); - } -} - -/* rename or move a old file into a new file */ -static int __exfat_rename(struct inode *old_parent_inode, - struct exfat_inode_info *ei, struct inode *new_parent_inode, - struct dentry *new_dentry) -{ - int ret; - int dentry; - struct exfat_chain olddir, newdir; - struct exfat_chain *p_dir = NULL; - struct exfat_uni_name uni_name; - struct exfat_dentry *ep; - struct super_block *sb = old_parent_inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - const unsigned char *new_path = new_dentry->d_name.name; - struct inode *new_inode = new_dentry->d_inode; - int num_entries; - struct exfat_inode_info *new_ei = NULL; - unsigned int new_entry_type = TYPE_UNUSED; - int new_entry = 0; - struct buffer_head *old_bh, *new_bh = NULL; - - /* check the validity of pointer parameters */ - if (new_path == NULL || strlen(new_path) == 0) - return -EINVAL; - - if (ei->dir.dir == DIR_DELETED) { - exfat_err(sb, "abnormal access to deleted source dentry"); - return -ENOENT; - } - - exfat_update_parent_info(ei, old_parent_inode); - - exfat_chain_dup(&olddir, &ei->dir); - dentry = ei->entry; - - ep = exfat_get_dentry(sb, &olddir, dentry, &old_bh, NULL); - if (!ep) { - ret = -EIO; - goto out; - } - brelse(old_bh); - - /* check whether new dir is existing directory and empty */ - if (new_inode) { - ret = -EIO; - new_ei = EXFAT_I(new_inode); - - if (new_ei->dir.dir == DIR_DELETED) { - exfat_err(sb, "abnormal access to deleted target dentry"); - goto out; - } - - exfat_update_parent_info(new_ei, new_parent_inode); - - p_dir = &(new_ei->dir); - new_entry = new_ei->entry; - ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh, NULL); - if (!ep) - goto out; - - new_entry_type = exfat_get_entry_type(ep); - brelse(new_bh); - - /* if new_inode exists, update ei */ - if (new_entry_type == TYPE_DIR) { - struct exfat_chain new_clu; - - new_clu.dir = new_ei->start_clu; - new_clu.size = - EXFAT_B_TO_CLU_ROUND_UP(i_size_read(new_inode), - sbi); - new_clu.flags = new_ei->flags; - - ret = exfat_check_dir_empty(sb, &new_clu); - if (ret) - goto out; - } - } - - /* check the validity of directory name in the given new pathname */ - ret = exfat_resolve_path(new_parent_inode, new_path, &newdir, - &uni_name); - if (ret) - goto out; - - exfat_set_volume_dirty(sb); - - if (olddir.dir == newdir.dir) - ret = exfat_rename_file(new_parent_inode, &olddir, dentry, - &uni_name, ei); - else - ret = exfat_move_file(new_parent_inode, &olddir, dentry, - &newdir, &uni_name, ei); - - if (!ret && new_inode) { - /* delete entries of new_dir */ - ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh, NULL); - if (!ep) { - ret = -EIO; - goto del_out; - } - - num_entries = exfat_count_ext_entries(sb, p_dir, new_entry, ep); - if (num_entries < 0) { - ret = -EIO; - goto del_out; - } - brelse(new_bh); - - if (exfat_remove_entries(new_inode, p_dir, new_entry, 0, - num_entries + 1)) { - ret = -EIO; - goto del_out; - } - - /* Free the clusters if new_inode is a dir(as if exfat_rmdir) */ - if (new_entry_type == TYPE_DIR) { - /* new_ei, new_clu_to_free */ - struct exfat_chain new_clu_to_free; - - exfat_chain_set(&new_clu_to_free, new_ei->start_clu, - EXFAT_B_TO_CLU_ROUND_UP(i_size_read(new_inode), - sbi), new_ei->flags); - - if (exfat_free_cluster(new_inode, &new_clu_to_free)) { - /* just set I/O error only */ - ret = -EIO; - } - - i_size_write(new_inode, 0); - new_ei->start_clu = EXFAT_EOF_CLUSTER; - new_ei->flags = ALLOC_NO_FAT_CHAIN; - } -del_out: - /* Update new_inode ei - * Prevent syncing removed new_inode - * (new_ei is already initialized above code ("if (new_inode)") - */ - new_ei->dir.dir = DIR_DELETED; - } - exfat_clear_volume_dirty(sb); -out: - return ret; -} - -static int exfat_rename(struct user_namespace *mnt_userns, - struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry, - unsigned int flags) -{ - struct inode *old_inode, *new_inode; - struct super_block *sb = old_dir->i_sb; - loff_t i_pos; - int err; - - /* - * The VFS already checks for existence, so for local filesystems - * the RENAME_NOREPLACE implementation is equivalent to plain rename. - * Don't support any other flags - */ - if (flags & ~RENAME_NOREPLACE) - return -EINVAL; - - mutex_lock(&EXFAT_SB(sb)->s_lock); - old_inode = old_dentry->d_inode; - new_inode = new_dentry->d_inode; - - err = __exfat_rename(old_dir, EXFAT_I(old_inode), new_dir, new_dentry); - if (err) - goto unlock; - - inode_inc_iversion(new_dir); - new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = - EXFAT_I(new_dir)->i_crtime = current_time(new_dir); - exfat_truncate_atime(&new_dir->i_atime); - if (IS_DIRSYNC(new_dir)) - exfat_sync_inode(new_dir); - else - mark_inode_dirty(new_dir); - - i_pos = ((loff_t)EXFAT_I(old_inode)->dir.dir << 32) | - (EXFAT_I(old_inode)->entry & 0xffffffff); - exfat_unhash_inode(old_inode); - exfat_hash_inode(old_inode, i_pos); - if (IS_DIRSYNC(new_dir)) - exfat_sync_inode(old_inode); - else - mark_inode_dirty(old_inode); - - if (S_ISDIR(old_inode->i_mode) && old_dir != new_dir) { - drop_nlink(old_dir); - if (!new_inode) - inc_nlink(new_dir); - } - - inode_inc_iversion(old_dir); - old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir); - if (IS_DIRSYNC(old_dir)) - exfat_sync_inode(old_dir); - else - mark_inode_dirty(old_dir); - - if (new_inode) { - exfat_unhash_inode(new_inode); - - /* skip drop_nlink if new_inode already has been dropped */ - if (new_inode->i_nlink) { - drop_nlink(new_inode); - if (S_ISDIR(new_inode->i_mode)) - drop_nlink(new_inode); - } else { - exfat_warn(sb, "abnormal access to an inode dropped"); - WARN_ON(new_inode->i_nlink == 0); - } - new_inode->i_ctime = EXFAT_I(new_inode)->i_crtime = - current_time(new_inode); - } - -unlock: - mutex_unlock(&EXFAT_SB(sb)->s_lock); - return err; -} - -const struct inode_operations exfat_dir_inode_operations = { - .create = exfat_create, - .lookup = exfat_lookup, - .unlink = exfat_unlink, - .mkdir = exfat_mkdir, - .rmdir = exfat_rmdir, - .rename = exfat_rename, - .setattr = exfat_setattr, - .getattr = exfat_getattr, -}; diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c deleted file mode 100644 index 314d5407a..000000000 --- a/fs/exfat/nls.c +++ /dev/null @@ -1,807 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include - -#include "exfat_raw.h" -#include "exfat_fs.h" - -/* Upcase table macro */ -#define EXFAT_NUM_UPCASE (2918) -#define UTBL_COUNT (0x10000) - -/* - * Upcase table in compressed format (7.2.5.1 Recommended Up-case Table - * in exfat specification, See: - * https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification). - */ -static const unsigned short uni_def_upcase[EXFAT_NUM_UPCASE] = { - 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, - 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, - 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, - 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, - 0x0060, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, - 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - 0x0058, 0x0059, 0x005a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, - 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, - 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, - 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, - 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, - 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, - 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, - 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, - 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, - 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, - 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, - 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, - 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, - 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, - 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, - 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00f7, - 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178, - 0x0100, 0x0100, 0x0102, 0x0102, 0x0104, 0x0104, 0x0106, 0x0106, - 0x0108, 0x0108, 0x010a, 0x010a, 0x010c, 0x010c, 0x010e, 0x010e, - 0x0110, 0x0110, 0x0112, 0x0112, 0x0114, 0x0114, 0x0116, 0x0116, - 0x0118, 0x0118, 0x011a, 0x011a, 0x011c, 0x011c, 0x011e, 0x011e, - 0x0120, 0x0120, 0x0122, 0x0122, 0x0124, 0x0124, 0x0126, 0x0126, - 0x0128, 0x0128, 0x012a, 0x012a, 0x012c, 0x012c, 0x012e, 0x012e, - 0x0130, 0x0131, 0x0132, 0x0132, 0x0134, 0x0134, 0x0136, 0x0136, - 0x0138, 0x0139, 0x0139, 0x013b, 0x013b, 0x013d, 0x013d, 0x013f, - 0x013f, 0x0141, 0x0141, 0x0143, 0x0143, 0x0145, 0x0145, 0x0147, - 0x0147, 0x0149, 0x014a, 0x014a, 0x014c, 0x014c, 0x014e, 0x014e, - 0x0150, 0x0150, 0x0152, 0x0152, 0x0154, 0x0154, 0x0156, 0x0156, - 0x0158, 0x0158, 0x015a, 0x015a, 0x015c, 0x015c, 0x015e, 0x015e, - 0x0160, 0x0160, 0x0162, 0x0162, 0x0164, 0x0164, 0x0166, 0x0166, - 0x0168, 0x0168, 0x016a, 0x016a, 0x016c, 0x016c, 0x016e, 0x016e, - 0x0170, 0x0170, 0x0172, 0x0172, 0x0174, 0x0174, 0x0176, 0x0176, - 0x0178, 0x0179, 0x0179, 0x017b, 0x017b, 0x017d, 0x017d, 0x017f, - 0x0243, 0x0181, 0x0182, 0x0182, 0x0184, 0x0184, 0x0186, 0x0187, - 0x0187, 0x0189, 0x018a, 0x018b, 0x018b, 0x018d, 0x018e, 0x018f, - 0x0190, 0x0191, 0x0191, 0x0193, 0x0194, 0x01f6, 0x0196, 0x0197, - 0x0198, 0x0198, 0x023d, 0x019b, 0x019c, 0x019d, 0x0220, 0x019f, - 0x01a0, 0x01a0, 0x01a2, 0x01a2, 0x01a4, 0x01a4, 0x01a6, 0x01a7, - 0x01a7, 0x01a9, 0x01aa, 0x01ab, 0x01ac, 0x01ac, 0x01ae, 0x01af, - 0x01af, 0x01b1, 0x01b2, 0x01b3, 0x01b3, 0x01b5, 0x01b5, 0x01b7, - 0x01b8, 0x01b8, 0x01ba, 0x01bb, 0x01bc, 0x01bc, 0x01be, 0x01f7, - 0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x01c4, 0x01c5, 0x01c4, 0x01c7, - 0x01c8, 0x01c7, 0x01ca, 0x01cb, 0x01ca, 0x01cd, 0x01cd, 0x01cf, - 0x01cf, 0x01d1, 0x01d1, 0x01d3, 0x01d3, 0x01d5, 0x01d5, 0x01d7, - 0x01d7, 0x01d9, 0x01d9, 0x01db, 0x01db, 0x018e, 0x01de, 0x01de, - 0x01e0, 0x01e0, 0x01e2, 0x01e2, 0x01e4, 0x01e4, 0x01e6, 0x01e6, - 0x01e8, 0x01e8, 0x01ea, 0x01ea, 0x01ec, 0x01ec, 0x01ee, 0x01ee, - 0x01f0, 0x01f1, 0x01f2, 0x01f1, 0x01f4, 0x01f4, 0x01f6, 0x01f7, - 0x01f8, 0x01f8, 0x01fa, 0x01fa, 0x01fc, 0x01fc, 0x01fe, 0x01fe, - 0x0200, 0x0200, 0x0202, 0x0202, 0x0204, 0x0204, 0x0206, 0x0206, - 0x0208, 0x0208, 0x020a, 0x020a, 0x020c, 0x020c, 0x020e, 0x020e, - 0x0210, 0x0210, 0x0212, 0x0212, 0x0214, 0x0214, 0x0216, 0x0216, - 0x0218, 0x0218, 0x021a, 0x021a, 0x021c, 0x021c, 0x021e, 0x021e, - 0x0220, 0x0221, 0x0222, 0x0222, 0x0224, 0x0224, 0x0226, 0x0226, - 0x0228, 0x0228, 0x022a, 0x022a, 0x022c, 0x022c, 0x022e, 0x022e, - 0x0230, 0x0230, 0x0232, 0x0232, 0x0234, 0x0235, 0x0236, 0x0237, - 0x0238, 0x0239, 0x2c65, 0x023b, 0x023b, 0x023d, 0x2c66, 0x023f, - 0x0240, 0x0241, 0x0241, 0x0243, 0x0244, 0x0245, 0x0246, 0x0246, - 0x0248, 0x0248, 0x024a, 0x024a, 0x024c, 0x024c, 0x024e, 0x024e, - 0x0250, 0x0251, 0x0252, 0x0181, 0x0186, 0x0255, 0x0189, 0x018a, - 0x0258, 0x018f, 0x025a, 0x0190, 0x025c, 0x025d, 0x025e, 0x025f, - 0x0193, 0x0261, 0x0262, 0x0194, 0x0264, 0x0265, 0x0266, 0x0267, - 0x0197, 0x0196, 0x026a, 0x2c62, 0x026c, 0x026d, 0x026e, 0x019c, - 0x0270, 0x0271, 0x019d, 0x0273, 0x0274, 0x019f, 0x0276, 0x0277, - 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x2c64, 0x027e, 0x027f, - 0x01a6, 0x0281, 0x0282, 0x01a9, 0x0284, 0x0285, 0x0286, 0x0287, - 0x01ae, 0x0244, 0x01b1, 0x01b2, 0x0245, 0x028d, 0x028e, 0x028f, - 0x0290, 0x0291, 0x01b7, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, - 0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f, - 0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7, - 0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af, - 0x02b0, 0x02b1, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x02b6, 0x02b7, - 0x02b8, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf, - 0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7, - 0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf, - 0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7, - 0x02d8, 0x02d9, 0x02da, 0x02db, 0x02dc, 0x02dd, 0x02de, 0x02df, - 0x02e0, 0x02e1, 0x02e2, 0x02e3, 0x02e4, 0x02e5, 0x02e6, 0x02e7, - 0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef, - 0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7, - 0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff, - 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, - 0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f, - 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, - 0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f, - 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, - 0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f, - 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, - 0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f, - 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, - 0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f, - 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, - 0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f, - 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, - 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f, - 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377, - 0x0378, 0x0379, 0x037a, 0x03fd, 0x03fe, 0x03ff, 0x037e, 0x037f, - 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387, - 0x0388, 0x0389, 0x038a, 0x038b, 0x038c, 0x038d, 0x038e, 0x038f, - 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, - 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, - 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x0386, 0x0388, 0x0389, 0x038a, - 0x03b0, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, - 0x03a0, 0x03a1, 0x03a3, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, - 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x038c, 0x038e, 0x038f, 0x03cf, - 0x03d0, 0x03d1, 0x03d2, 0x03d3, 0x03d4, 0x03d5, 0x03d6, 0x03d7, - 0x03d8, 0x03d8, 0x03da, 0x03da, 0x03dc, 0x03dc, 0x03de, 0x03de, - 0x03e0, 0x03e0, 0x03e2, 0x03e2, 0x03e4, 0x03e4, 0x03e6, 0x03e6, - 0x03e8, 0x03e8, 0x03ea, 0x03ea, 0x03ec, 0x03ec, 0x03ee, 0x03ee, - 0x03f0, 0x03f1, 0x03f9, 0x03f3, 0x03f4, 0x03f5, 0x03f6, 0x03f7, - 0x03f7, 0x03f9, 0x03fa, 0x03fa, 0x03fc, 0x03fd, 0x03fe, 0x03ff, - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, - 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f, - 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, - 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, - 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, - 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, - 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, - 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, - 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, - 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, - 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, - 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f, - 0x0460, 0x0460, 0x0462, 0x0462, 0x0464, 0x0464, 0x0466, 0x0466, - 0x0468, 0x0468, 0x046a, 0x046a, 0x046c, 0x046c, 0x046e, 0x046e, - 0x0470, 0x0470, 0x0472, 0x0472, 0x0474, 0x0474, 0x0476, 0x0476, - 0x0478, 0x0478, 0x047a, 0x047a, 0x047c, 0x047c, 0x047e, 0x047e, - 0x0480, 0x0480, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, - 0x0488, 0x0489, 0x048a, 0x048a, 0x048c, 0x048c, 0x048e, 0x048e, - 0x0490, 0x0490, 0x0492, 0x0492, 0x0494, 0x0494, 0x0496, 0x0496, - 0x0498, 0x0498, 0x049a, 0x049a, 0x049c, 0x049c, 0x049e, 0x049e, - 0x04a0, 0x04a0, 0x04a2, 0x04a2, 0x04a4, 0x04a4, 0x04a6, 0x04a6, - 0x04a8, 0x04a8, 0x04aa, 0x04aa, 0x04ac, 0x04ac, 0x04ae, 0x04ae, - 0x04b0, 0x04b0, 0x04b2, 0x04b2, 0x04b4, 0x04b4, 0x04b6, 0x04b6, - 0x04b8, 0x04b8, 0x04ba, 0x04ba, 0x04bc, 0x04bc, 0x04be, 0x04be, - 0x04c0, 0x04c1, 0x04c1, 0x04c3, 0x04c3, 0x04c5, 0x04c5, 0x04c7, - 0x04c7, 0x04c9, 0x04c9, 0x04cb, 0x04cb, 0x04cd, 0x04cd, 0x04c0, - 0x04d0, 0x04d0, 0x04d2, 0x04d2, 0x04d4, 0x04d4, 0x04d6, 0x04d6, - 0x04d8, 0x04d8, 0x04da, 0x04da, 0x04dc, 0x04dc, 0x04de, 0x04de, - 0x04e0, 0x04e0, 0x04e2, 0x04e2, 0x04e4, 0x04e4, 0x04e6, 0x04e6, - 0x04e8, 0x04e8, 0x04ea, 0x04ea, 0x04ec, 0x04ec, 0x04ee, 0x04ee, - 0x04f0, 0x04f0, 0x04f2, 0x04f2, 0x04f4, 0x04f4, 0x04f6, 0x04f6, - 0x04f8, 0x04f8, 0x04fa, 0x04fa, 0x04fc, 0x04fc, 0x04fe, 0x04fe, - 0x0500, 0x0500, 0x0502, 0x0502, 0x0504, 0x0504, 0x0506, 0x0506, - 0x0508, 0x0508, 0x050a, 0x050a, 0x050c, 0x050c, 0x050e, 0x050e, - 0x0510, 0x0510, 0x0512, 0x0512, 0x0514, 0x0515, 0x0516, 0x0517, - 0x0518, 0x0519, 0x051a, 0x051b, 0x051c, 0x051d, 0x051e, 0x051f, - 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, - 0x0528, 0x0529, 0x052a, 0x052b, 0x052c, 0x052d, 0x052e, 0x052f, - 0x0530, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, - 0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f, - 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, - 0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f, - 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0557, - 0x0558, 0x0559, 0x055a, 0x055b, 0x055c, 0x055d, 0x055e, 0x055f, - 0x0560, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, - 0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f, - 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, - 0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f, - 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0xffff, - 0x17f6, 0x2c63, 0x1d7e, 0x1d7f, 0x1d80, 0x1d81, 0x1d82, 0x1d83, - 0x1d84, 0x1d85, 0x1d86, 0x1d87, 0x1d88, 0x1d89, 0x1d8a, 0x1d8b, - 0x1d8c, 0x1d8d, 0x1d8e, 0x1d8f, 0x1d90, 0x1d91, 0x1d92, 0x1d93, - 0x1d94, 0x1d95, 0x1d96, 0x1d97, 0x1d98, 0x1d99, 0x1d9a, 0x1d9b, - 0x1d9c, 0x1d9d, 0x1d9e, 0x1d9f, 0x1da0, 0x1da1, 0x1da2, 0x1da3, - 0x1da4, 0x1da5, 0x1da6, 0x1da7, 0x1da8, 0x1da9, 0x1daa, 0x1dab, - 0x1dac, 0x1dad, 0x1dae, 0x1daf, 0x1db0, 0x1db1, 0x1db2, 0x1db3, - 0x1db4, 0x1db5, 0x1db6, 0x1db7, 0x1db8, 0x1db9, 0x1dba, 0x1dbb, - 0x1dbc, 0x1dbd, 0x1dbe, 0x1dbf, 0x1dc0, 0x1dc1, 0x1dc2, 0x1dc3, - 0x1dc4, 0x1dc5, 0x1dc6, 0x1dc7, 0x1dc8, 0x1dc9, 0x1dca, 0x1dcb, - 0x1dcc, 0x1dcd, 0x1dce, 0x1dcf, 0x1dd0, 0x1dd1, 0x1dd2, 0x1dd3, - 0x1dd4, 0x1dd5, 0x1dd6, 0x1dd7, 0x1dd8, 0x1dd9, 0x1dda, 0x1ddb, - 0x1ddc, 0x1ddd, 0x1dde, 0x1ddf, 0x1de0, 0x1de1, 0x1de2, 0x1de3, - 0x1de4, 0x1de5, 0x1de6, 0x1de7, 0x1de8, 0x1de9, 0x1dea, 0x1deb, - 0x1dec, 0x1ded, 0x1dee, 0x1def, 0x1df0, 0x1df1, 0x1df2, 0x1df3, - 0x1df4, 0x1df5, 0x1df6, 0x1df7, 0x1df8, 0x1df9, 0x1dfa, 0x1dfb, - 0x1dfc, 0x1dfd, 0x1dfe, 0x1dff, 0x1e00, 0x1e00, 0x1e02, 0x1e02, - 0x1e04, 0x1e04, 0x1e06, 0x1e06, 0x1e08, 0x1e08, 0x1e0a, 0x1e0a, - 0x1e0c, 0x1e0c, 0x1e0e, 0x1e0e, 0x1e10, 0x1e10, 0x1e12, 0x1e12, - 0x1e14, 0x1e14, 0x1e16, 0x1e16, 0x1e18, 0x1e18, 0x1e1a, 0x1e1a, - 0x1e1c, 0x1e1c, 0x1e1e, 0x1e1e, 0x1e20, 0x1e20, 0x1e22, 0x1e22, - 0x1e24, 0x1e24, 0x1e26, 0x1e26, 0x1e28, 0x1e28, 0x1e2a, 0x1e2a, - 0x1e2c, 0x1e2c, 0x1e2e, 0x1e2e, 0x1e30, 0x1e30, 0x1e32, 0x1e32, - 0x1e34, 0x1e34, 0x1e36, 0x1e36, 0x1e38, 0x1e38, 0x1e3a, 0x1e3a, - 0x1e3c, 0x1e3c, 0x1e3e, 0x1e3e, 0x1e40, 0x1e40, 0x1e42, 0x1e42, - 0x1e44, 0x1e44, 0x1e46, 0x1e46, 0x1e48, 0x1e48, 0x1e4a, 0x1e4a, - 0x1e4c, 0x1e4c, 0x1e4e, 0x1e4e, 0x1e50, 0x1e50, 0x1e52, 0x1e52, - 0x1e54, 0x1e54, 0x1e56, 0x1e56, 0x1e58, 0x1e58, 0x1e5a, 0x1e5a, - 0x1e5c, 0x1e5c, 0x1e5e, 0x1e5e, 0x1e60, 0x1e60, 0x1e62, 0x1e62, - 0x1e64, 0x1e64, 0x1e66, 0x1e66, 0x1e68, 0x1e68, 0x1e6a, 0x1e6a, - 0x1e6c, 0x1e6c, 0x1e6e, 0x1e6e, 0x1e70, 0x1e70, 0x1e72, 0x1e72, - 0x1e74, 0x1e74, 0x1e76, 0x1e76, 0x1e78, 0x1e78, 0x1e7a, 0x1e7a, - 0x1e7c, 0x1e7c, 0x1e7e, 0x1e7e, 0x1e80, 0x1e80, 0x1e82, 0x1e82, - 0x1e84, 0x1e84, 0x1e86, 0x1e86, 0x1e88, 0x1e88, 0x1e8a, 0x1e8a, - 0x1e8c, 0x1e8c, 0x1e8e, 0x1e8e, 0x1e90, 0x1e90, 0x1e92, 0x1e92, - 0x1e94, 0x1e94, 0x1e96, 0x1e97, 0x1e98, 0x1e99, 0x1e9a, 0x1e9b, - 0x1e9c, 0x1e9d, 0x1e9e, 0x1e9f, 0x1ea0, 0x1ea0, 0x1ea2, 0x1ea2, - 0x1ea4, 0x1ea4, 0x1ea6, 0x1ea6, 0x1ea8, 0x1ea8, 0x1eaa, 0x1eaa, - 0x1eac, 0x1eac, 0x1eae, 0x1eae, 0x1eb0, 0x1eb0, 0x1eb2, 0x1eb2, - 0x1eb4, 0x1eb4, 0x1eb6, 0x1eb6, 0x1eb8, 0x1eb8, 0x1eba, 0x1eba, - 0x1ebc, 0x1ebc, 0x1ebe, 0x1ebe, 0x1ec0, 0x1ec0, 0x1ec2, 0x1ec2, - 0x1ec4, 0x1ec4, 0x1ec6, 0x1ec6, 0x1ec8, 0x1ec8, 0x1eca, 0x1eca, - 0x1ecc, 0x1ecc, 0x1ece, 0x1ece, 0x1ed0, 0x1ed0, 0x1ed2, 0x1ed2, - 0x1ed4, 0x1ed4, 0x1ed6, 0x1ed6, 0x1ed8, 0x1ed8, 0x1eda, 0x1eda, - 0x1edc, 0x1edc, 0x1ede, 0x1ede, 0x1ee0, 0x1ee0, 0x1ee2, 0x1ee2, - 0x1ee4, 0x1ee4, 0x1ee6, 0x1ee6, 0x1ee8, 0x1ee8, 0x1eea, 0x1eea, - 0x1eec, 0x1eec, 0x1eee, 0x1eee, 0x1ef0, 0x1ef0, 0x1ef2, 0x1ef2, - 0x1ef4, 0x1ef4, 0x1ef6, 0x1ef6, 0x1ef8, 0x1ef8, 0x1efa, 0x1efb, - 0x1efc, 0x1efd, 0x1efe, 0x1eff, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, - 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, - 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, - 0x1f1c, 0x1f1d, 0x1f16, 0x1f17, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, - 0x1f1c, 0x1f1d, 0x1f1e, 0x1f1f, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, - 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, - 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, - 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, - 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, - 0x1f4c, 0x1f4d, 0x1f46, 0x1f47, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, - 0x1f4c, 0x1f4d, 0x1f4e, 0x1f4f, 0x1f50, 0x1f59, 0x1f52, 0x1f5b, - 0x1f54, 0x1f5d, 0x1f56, 0x1f5f, 0x1f58, 0x1f59, 0x1f5a, 0x1f5b, - 0x1f5c, 0x1f5d, 0x1f5e, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, - 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, - 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, - 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, - 0x1ffa, 0x1ffb, 0x1f7e, 0x1f7f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, - 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, - 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, - 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, - 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, - 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, - 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fb8, 0x1fb9, 0x1fb2, 0x1fbc, - 0x1fb4, 0x1fb5, 0x1fb6, 0x1fb7, 0x1fb8, 0x1fb9, 0x1fba, 0x1fbb, - 0x1fbc, 0x1fbd, 0x1fbe, 0x1fbf, 0x1fc0, 0x1fc1, 0x1fc2, 0x1fc3, - 0x1fc4, 0x1fc5, 0x1fc6, 0x1fc7, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, - 0x1fc3, 0x1fcd, 0x1fce, 0x1fcf, 0x1fd8, 0x1fd9, 0x1fd2, 0x1fd3, - 0x1fd4, 0x1fd5, 0x1fd6, 0x1fd7, 0x1fd8, 0x1fd9, 0x1fda, 0x1fdb, - 0x1fdc, 0x1fdd, 0x1fde, 0x1fdf, 0x1fe8, 0x1fe9, 0x1fe2, 0x1fe3, - 0x1fe4, 0x1fec, 0x1fe6, 0x1fe7, 0x1fe8, 0x1fe9, 0x1fea, 0x1feb, - 0x1fec, 0x1fed, 0x1fee, 0x1fef, 0x1ff0, 0x1ff1, 0x1ff2, 0x1ff3, - 0x1ff4, 0x1ff5, 0x1ff6, 0x1ff7, 0x1ff8, 0x1ff9, 0x1ffa, 0x1ffb, - 0x1ff3, 0x1ffd, 0x1ffe, 0x1fff, 0x2000, 0x2001, 0x2002, 0x2003, - 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200a, 0x200b, - 0x200c, 0x200d, 0x200e, 0x200f, 0x2010, 0x2011, 0x2012, 0x2013, - 0x2014, 0x2015, 0x2016, 0x2017, 0x2018, 0x2019, 0x201a, 0x201b, - 0x201c, 0x201d, 0x201e, 0x201f, 0x2020, 0x2021, 0x2022, 0x2023, - 0x2024, 0x2025, 0x2026, 0x2027, 0x2028, 0x2029, 0x202a, 0x202b, - 0x202c, 0x202d, 0x202e, 0x202f, 0x2030, 0x2031, 0x2032, 0x2033, - 0x2034, 0x2035, 0x2036, 0x2037, 0x2038, 0x2039, 0x203a, 0x203b, - 0x203c, 0x203d, 0x203e, 0x203f, 0x2040, 0x2041, 0x2042, 0x2043, - 0x2044, 0x2045, 0x2046, 0x2047, 0x2048, 0x2049, 0x204a, 0x204b, - 0x204c, 0x204d, 0x204e, 0x204f, 0x2050, 0x2051, 0x2052, 0x2053, - 0x2054, 0x2055, 0x2056, 0x2057, 0x2058, 0x2059, 0x205a, 0x205b, - 0x205c, 0x205d, 0x205e, 0x205f, 0x2060, 0x2061, 0x2062, 0x2063, - 0x2064, 0x2065, 0x2066, 0x2067, 0x2068, 0x2069, 0x206a, 0x206b, - 0x206c, 0x206d, 0x206e, 0x206f, 0x2070, 0x2071, 0x2072, 0x2073, - 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x207a, 0x207b, - 0x207c, 0x207d, 0x207e, 0x207f, 0x2080, 0x2081, 0x2082, 0x2083, - 0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0x208a, 0x208b, - 0x208c, 0x208d, 0x208e, 0x208f, 0x2090, 0x2091, 0x2092, 0x2093, - 0x2094, 0x2095, 0x2096, 0x2097, 0x2098, 0x2099, 0x209a, 0x209b, - 0x209c, 0x209d, 0x209e, 0x209f, 0x20a0, 0x20a1, 0x20a2, 0x20a3, - 0x20a4, 0x20a5, 0x20a6, 0x20a7, 0x20a8, 0x20a9, 0x20aa, 0x20ab, - 0x20ac, 0x20ad, 0x20ae, 0x20af, 0x20b0, 0x20b1, 0x20b2, 0x20b3, - 0x20b4, 0x20b5, 0x20b6, 0x20b7, 0x20b8, 0x20b9, 0x20ba, 0x20bb, - 0x20bc, 0x20bd, 0x20be, 0x20bf, 0x20c0, 0x20c1, 0x20c2, 0x20c3, - 0x20c4, 0x20c5, 0x20c6, 0x20c7, 0x20c8, 0x20c9, 0x20ca, 0x20cb, - 0x20cc, 0x20cd, 0x20ce, 0x20cf, 0x20d0, 0x20d1, 0x20d2, 0x20d3, - 0x20d4, 0x20d5, 0x20d6, 0x20d7, 0x20d8, 0x20d9, 0x20da, 0x20db, - 0x20dc, 0x20dd, 0x20de, 0x20df, 0x20e0, 0x20e1, 0x20e2, 0x20e3, - 0x20e4, 0x20e5, 0x20e6, 0x20e7, 0x20e8, 0x20e9, 0x20ea, 0x20eb, - 0x20ec, 0x20ed, 0x20ee, 0x20ef, 0x20f0, 0x20f1, 0x20f2, 0x20f3, - 0x20f4, 0x20f5, 0x20f6, 0x20f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, - 0x20fc, 0x20fd, 0x20fe, 0x20ff, 0x2100, 0x2101, 0x2102, 0x2103, - 0x2104, 0x2105, 0x2106, 0x2107, 0x2108, 0x2109, 0x210a, 0x210b, - 0x210c, 0x210d, 0x210e, 0x210f, 0x2110, 0x2111, 0x2112, 0x2113, - 0x2114, 0x2115, 0x2116, 0x2117, 0x2118, 0x2119, 0x211a, 0x211b, - 0x211c, 0x211d, 0x211e, 0x211f, 0x2120, 0x2121, 0x2122, 0x2123, - 0x2124, 0x2125, 0x2126, 0x2127, 0x2128, 0x2129, 0x212a, 0x212b, - 0x212c, 0x212d, 0x212e, 0x212f, 0x2130, 0x2131, 0x2132, 0x2133, - 0x2134, 0x2135, 0x2136, 0x2137, 0x2138, 0x2139, 0x213a, 0x213b, - 0x213c, 0x213d, 0x213e, 0x213f, 0x2140, 0x2141, 0x2142, 0x2143, - 0x2144, 0x2145, 0x2146, 0x2147, 0x2148, 0x2149, 0x214a, 0x214b, - 0x214c, 0x214d, 0x2132, 0x214f, 0x2150, 0x2151, 0x2152, 0x2153, - 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, 0x215b, - 0x215c, 0x215d, 0x215e, 0x215f, 0x2160, 0x2161, 0x2162, 0x2163, - 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b, - 0x216c, 0x216d, 0x216e, 0x216f, 0x2160, 0x2161, 0x2162, 0x2163, - 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b, - 0x216c, 0x216d, 0x216e, 0x216f, 0x2180, 0x2181, 0x2182, 0x2183, - 0x2183, 0xffff, 0x034b, 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, - 0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, - 0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, - 0x24cb, 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0xffff, 0x0746, 0x2c00, - 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x2c08, - 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x2c10, - 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, 0x2c18, - 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, 0x2c20, - 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, 0x2c28, - 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x2c5f, 0x2c60, - 0x2c60, 0x2c62, 0x2c63, 0x2c64, 0x2c65, 0x2c66, 0x2c67, 0x2c67, - 0x2c69, 0x2c69, 0x2c6b, 0x2c6b, 0x2c6d, 0x2c6e, 0x2c6f, 0x2c70, - 0x2c71, 0x2c72, 0x2c73, 0x2c74, 0x2c75, 0x2c75, 0x2c77, 0x2c78, - 0x2c79, 0x2c7a, 0x2c7b, 0x2c7c, 0x2c7d, 0x2c7e, 0x2c7f, 0x2c80, - 0x2c80, 0x2c82, 0x2c82, 0x2c84, 0x2c84, 0x2c86, 0x2c86, 0x2c88, - 0x2c88, 0x2c8a, 0x2c8a, 0x2c8c, 0x2c8c, 0x2c8e, 0x2c8e, 0x2c90, - 0x2c90, 0x2c92, 0x2c92, 0x2c94, 0x2c94, 0x2c96, 0x2c96, 0x2c98, - 0x2c98, 0x2c9a, 0x2c9a, 0x2c9c, 0x2c9c, 0x2c9e, 0x2c9e, 0x2ca0, - 0x2ca0, 0x2ca2, 0x2ca2, 0x2ca4, 0x2ca4, 0x2ca6, 0x2ca6, 0x2ca8, - 0x2ca8, 0x2caa, 0x2caa, 0x2cac, 0x2cac, 0x2cae, 0x2cae, 0x2cb0, - 0x2cb0, 0x2cb2, 0x2cb2, 0x2cb4, 0x2cb4, 0x2cb6, 0x2cb6, 0x2cb8, - 0x2cb8, 0x2cba, 0x2cba, 0x2cbc, 0x2cbc, 0x2cbe, 0x2cbe, 0x2cc0, - 0x2cc0, 0x2cc2, 0x2cc2, 0x2cc4, 0x2cc4, 0x2cc6, 0x2cc6, 0x2cc8, - 0x2cc8, 0x2cca, 0x2cca, 0x2ccc, 0x2ccc, 0x2cce, 0x2cce, 0x2cd0, - 0x2cd0, 0x2cd2, 0x2cd2, 0x2cd4, 0x2cd4, 0x2cd6, 0x2cd6, 0x2cd8, - 0x2cd8, 0x2cda, 0x2cda, 0x2cdc, 0x2cdc, 0x2cde, 0x2cde, 0x2ce0, - 0x2ce0, 0x2ce2, 0x2ce2, 0x2ce4, 0x2ce5, 0x2ce6, 0x2ce7, 0x2ce8, - 0x2ce9, 0x2cea, 0x2ceb, 0x2cec, 0x2ced, 0x2cee, 0x2cef, 0x2cf0, - 0x2cf1, 0x2cf2, 0x2cf3, 0x2cf4, 0x2cf5, 0x2cf6, 0x2cf7, 0x2cf8, - 0x2cf9, 0x2cfa, 0x2cfb, 0x2cfc, 0x2cfd, 0x2cfe, 0x2cff, 0x10a0, - 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8, - 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0, - 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8, - 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, 0x10c0, - 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0xffff, 0xd21b, 0xff21, - 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29, - 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31, - 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39, - 0xff3a, 0xff5b, 0xff5c, 0xff5d, 0xff5e, 0xff5f, 0xff60, 0xff61, - 0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67, 0xff68, 0xff69, - 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f, 0xff70, 0xff71, - 0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77, 0xff78, 0xff79, - 0xff7a, 0xff7b, 0xff7c, 0xff7d, 0xff7e, 0xff7f, 0xff80, 0xff81, - 0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87, 0xff88, 0xff89, - 0xff8a, 0xff8b, 0xff8c, 0xff8d, 0xff8e, 0xff8f, 0xff90, 0xff91, - 0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97, 0xff98, 0xff99, - 0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f, 0xffa0, 0xffa1, - 0xffa2, 0xffa3, 0xffa4, 0xffa5, 0xffa6, 0xffa7, 0xffa8, 0xffa9, - 0xffaa, 0xffab, 0xffac, 0xffad, 0xffae, 0xffaf, 0xffb0, 0xffb1, - 0xffb2, 0xffb3, 0xffb4, 0xffb5, 0xffb6, 0xffb7, 0xffb8, 0xffb9, - 0xffba, 0xffbb, 0xffbc, 0xffbd, 0xffbe, 0xffbf, 0xffc0, 0xffc1, - 0xffc2, 0xffc3, 0xffc4, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9, - 0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf, 0xffd0, 0xffd1, - 0xffd2, 0xffd3, 0xffd4, 0xffd5, 0xffd6, 0xffd7, 0xffd8, 0xffd9, - 0xffda, 0xffdb, 0xffdc, 0xffdd, 0xffde, 0xffdf, 0xffe0, 0xffe1, - 0xffe2, 0xffe3, 0xffe4, 0xffe5, 0xffe6, 0xffe7, 0xffe8, 0xffe9, - 0xffea, 0xffeb, 0xffec, 0xffed, 0xffee, 0xffef, 0xfff0, 0xfff1, - 0xfff2, 0xfff3, 0xfff4, 0xfff5, 0xfff6, 0xfff7, 0xfff8, 0xfff9, - 0xfffa, 0xfffb, 0xfffc, 0xfffd, 0xfffe, 0xffff, -}; - -/* - * Allow full-width illegal characters : - * "MS windows 7" supports full-width-invalid-name-characters. - * So we should check half-width-invalid-name-characters(ASCII) only - * for compatibility. - * - * " * / : < > ? \ | - */ -static unsigned short bad_uni_chars[] = { - 0x0022, 0x002A, 0x002F, 0x003A, - 0x003C, 0x003E, 0x003F, 0x005C, 0x007C, - 0 -}; - -static int exfat_convert_char_to_ucs2(struct nls_table *nls, - const unsigned char *ch, int ch_len, unsigned short *ucs2, - int *lossy) -{ - int len; - - *ucs2 = 0x0; - - if (ch[0] < 0x80) { - *ucs2 = ch[0]; - return 1; - } - - len = nls->char2uni(ch, ch_len, ucs2); - if (len < 0) { - /* conversion failed */ - if (lossy != NULL) - *lossy |= NLS_NAME_LOSSY; - *ucs2 = '_'; - return 1; - } - return len; -} - -static int exfat_convert_ucs2_to_char(struct nls_table *nls, - unsigned short ucs2, unsigned char *ch, int *lossy) -{ - int len; - - ch[0] = 0x0; - - if (ucs2 < 0x0080) { - ch[0] = ucs2; - return 1; - } - - len = nls->uni2char(ucs2, ch, MAX_CHARSET_SIZE); - if (len < 0) { - /* conversion failed */ - if (lossy != NULL) - *lossy |= NLS_NAME_LOSSY; - ch[0] = '_'; - return 1; - } - return len; -} - -unsigned short exfat_toupper(struct super_block *sb, unsigned short a) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - - return sbi->vol_utbl[a] ? sbi->vol_utbl[a] : a; -} - -static unsigned short *exfat_wstrchr(unsigned short *str, unsigned short wchar) -{ - while (*str) { - if (*(str++) == wchar) - return str; - } - return NULL; -} - -int exfat_uniname_ncmp(struct super_block *sb, unsigned short *a, - unsigned short *b, unsigned int len) -{ - int i; - - for (i = 0; i < len; i++, a++, b++) - if (exfat_toupper(sb, *a) != exfat_toupper(sb, *b)) - return 1; - return 0; -} - -static int exfat_utf16_to_utf8(struct super_block *sb, - struct exfat_uni_name *p_uniname, unsigned char *p_cstring, - int buflen) -{ - int len; - const unsigned short *uniname = p_uniname->name; - - /* always len >= 0 */ - len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN, - p_cstring, buflen); - p_cstring[len] = '\0'; - return len; -} - -static int exfat_utf8_to_utf16(struct super_block *sb, - const unsigned char *p_cstring, const int len, - struct exfat_uni_name *p_uniname, int *p_lossy) -{ - int i, unilen, lossy = NLS_NAME_NO_LOSSY; - __le16 upname[MAX_NAME_LENGTH + 1]; - unsigned short *uniname = p_uniname->name; - - WARN_ON(!len); - - unilen = utf8s_to_utf16s(p_cstring, len, UTF16_HOST_ENDIAN, - (wchar_t *)uniname, MAX_NAME_LENGTH + 2); - if (unilen < 0) { - exfat_err(sb, "failed to %s (err : %d) nls len : %d", - __func__, unilen, len); - return unilen; - } - - if (unilen > MAX_NAME_LENGTH) { - exfat_err(sb, "failed to %s (estr:ENAMETOOLONG) nls len : %d, unilen : %d > %d", - __func__, len, unilen, MAX_NAME_LENGTH); - return -ENAMETOOLONG; - } - - for (i = 0; i < unilen; i++) { - if (*uniname < 0x0020 || - exfat_wstrchr(bad_uni_chars, *uniname)) - lossy |= NLS_NAME_LOSSY; - - upname[i] = cpu_to_le16(exfat_toupper(sb, *uniname)); - uniname++; - } - - *uniname = '\0'; - p_uniname->name_len = unilen; - p_uniname->name_hash = exfat_calc_chksum16(upname, unilen << 1, 0, - CS_DEFAULT); - - if (p_lossy) - *p_lossy = lossy; - return unilen; -} - -#define SURROGATE_MASK 0xfffff800 -#define SURROGATE_PAIR 0x0000d800 -#define SURROGATE_LOW 0x00000400 - -static int __exfat_utf16_to_nls(struct super_block *sb, - struct exfat_uni_name *p_uniname, unsigned char *p_cstring, - int buflen) -{ - int i, j, len, out_len = 0; - unsigned char buf[MAX_CHARSET_SIZE]; - const unsigned short *uniname = p_uniname->name; - struct nls_table *nls = EXFAT_SB(sb)->nls_io; - - i = 0; - while (i < MAX_NAME_LENGTH && out_len < (buflen - 1)) { - if (*uniname == '\0') - break; - if ((*uniname & SURROGATE_MASK) != SURROGATE_PAIR) { - len = exfat_convert_ucs2_to_char(nls, *uniname, buf, - NULL); - } else { - /* Process UTF-16 surrogate pair as one character */ - if (!(*uniname & SURROGATE_LOW) && - i+1 < MAX_NAME_LENGTH && - (*(uniname+1) & SURROGATE_MASK) == SURROGATE_PAIR && - (*(uniname+1) & SURROGATE_LOW)) { - uniname++; - i++; - } - - /* - * UTF-16 surrogate pair encodes code points above - * U+FFFF. Code points above U+FFFF are not supported - * by kernel NLS framework therefore use replacement - * character - */ - len = 1; - buf[0] = '_'; - } - - if (out_len + len >= buflen) - len = buflen - 1 - out_len; - out_len += len; - - if (len > 1) { - for (j = 0; j < len; j++) - *p_cstring++ = buf[j]; - } else { /* len == 1 */ - *p_cstring++ = *buf; - } - - uniname++; - i++; - } - - *p_cstring = '\0'; - return out_len; -} - -static int exfat_nls_to_ucs2(struct super_block *sb, - const unsigned char *p_cstring, const int len, - struct exfat_uni_name *p_uniname, int *p_lossy) -{ - int i = 0, unilen = 0, lossy = NLS_NAME_NO_LOSSY; - __le16 upname[MAX_NAME_LENGTH + 1]; - unsigned short *uniname = p_uniname->name; - struct nls_table *nls = EXFAT_SB(sb)->nls_io; - - WARN_ON(!len); - - while (unilen < MAX_NAME_LENGTH && i < len) { - i += exfat_convert_char_to_ucs2(nls, p_cstring + i, len - i, - uniname, &lossy); - - if (*uniname < 0x0020 || - exfat_wstrchr(bad_uni_chars, *uniname)) - lossy |= NLS_NAME_LOSSY; - - upname[unilen] = cpu_to_le16(exfat_toupper(sb, *uniname)); - uniname++; - unilen++; - } - - if (p_cstring[i] != '\0') - lossy |= NLS_NAME_OVERLEN; - - *uniname = '\0'; - p_uniname->name_len = unilen; - p_uniname->name_hash = exfat_calc_chksum16(upname, unilen << 1, 0, - CS_DEFAULT); - - if (p_lossy) - *p_lossy = lossy; - return unilen; -} - -int exfat_utf16_to_nls(struct super_block *sb, struct exfat_uni_name *uniname, - unsigned char *p_cstring, int buflen) -{ - if (EXFAT_SB(sb)->options.utf8) - return exfat_utf16_to_utf8(sb, uniname, p_cstring, - buflen); - return __exfat_utf16_to_nls(sb, uniname, p_cstring, buflen); -} - -int exfat_nls_to_utf16(struct super_block *sb, const unsigned char *p_cstring, - const int len, struct exfat_uni_name *uniname, int *p_lossy) -{ - if (EXFAT_SB(sb)->options.utf8) - return exfat_utf8_to_utf16(sb, p_cstring, len, - uniname, p_lossy); - return exfat_nls_to_ucs2(sb, p_cstring, len, uniname, p_lossy); -} - -static int exfat_load_upcase_table(struct super_block *sb, - sector_t sector, unsigned long long num_sectors, - unsigned int utbl_checksum) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - unsigned int sect_size = sb->s_blocksize; - unsigned int i, index = 0; - u32 chksum = 0; - int ret; - unsigned char skip = false; - unsigned short *upcase_table; - - upcase_table = kvcalloc(UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL); - if (!upcase_table) - return -ENOMEM; - - sbi->vol_utbl = upcase_table; - num_sectors += sector; - - while (sector < num_sectors) { - struct buffer_head *bh; - - bh = sb_bread(sb, sector); - if (!bh) { - exfat_err(sb, "failed to read sector(0x%llx)\n", - (unsigned long long)sector); - ret = -EIO; - goto free_table; - } - sector++; - for (i = 0; i < sect_size && index <= 0xFFFF; i += 2) { - unsigned short uni = get_unaligned_le16(bh->b_data + i); - - if (skip) { - index += uni; - skip = false; - } else if (uni == index) { - index++; - } else if (uni == 0xFFFF) { - skip = true; - } else { /* uni != index , uni != 0xFFFF */ - upcase_table[index] = uni; - index++; - } - } - chksum = exfat_calc_chksum32(bh->b_data, i, chksum, CS_DEFAULT); - brelse(bh); - } - - if (index >= 0xFFFF && utbl_checksum == chksum) - return 0; - - exfat_err(sb, "failed to load upcase table (idx : 0x%08x, chksum : 0x%08x, utbl_chksum : 0x%08x)", - index, chksum, utbl_checksum); - ret = -EINVAL; -free_table: - exfat_free_upcase_table(sbi); - return ret; -} - -static int exfat_load_default_upcase_table(struct super_block *sb) -{ - int i, ret = -EIO; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - unsigned char skip = false; - unsigned short uni = 0, *upcase_table; - unsigned int index = 0; - - upcase_table = kvcalloc(UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL); - if (!upcase_table) - return -ENOMEM; - - sbi->vol_utbl = upcase_table; - - for (i = 0; index <= 0xFFFF && i < EXFAT_NUM_UPCASE; i++) { - uni = uni_def_upcase[i]; - if (skip) { - index += uni; - skip = false; - } else if (uni == index) { - index++; - } else if (uni == 0xFFFF) { - skip = true; - } else { - upcase_table[index] = uni; - index++; - } - } - - if (index >= 0xFFFF) - return 0; - - /* FATAL error: default upcase table has error */ - exfat_free_upcase_table(sbi); - return ret; -} - -int exfat_create_upcase_table(struct super_block *sb) -{ - int i, ret; - unsigned int tbl_clu, type; - sector_t sector; - unsigned long long tbl_size, num_sectors; - unsigned char blksize_bits = sb->s_blocksize_bits; - struct exfat_chain clu; - struct exfat_dentry *ep; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct buffer_head *bh; - - clu.dir = sbi->root_dir; - clu.flags = ALLOC_FAT_CHAIN; - - while (clu.dir != EXFAT_EOF_CLUSTER) { - for (i = 0; i < sbi->dentries_per_clu; i++) { - ep = exfat_get_dentry(sb, &clu, i, &bh, NULL); - if (!ep) - return -EIO; - - type = exfat_get_entry_type(ep); - if (type == TYPE_UNUSED) { - brelse(bh); - break; - } - - if (type != TYPE_UPCASE) { - brelse(bh); - continue; - } - - tbl_clu = le32_to_cpu(ep->dentry.upcase.start_clu); - tbl_size = le64_to_cpu(ep->dentry.upcase.size); - - sector = exfat_cluster_to_sector(sbi, tbl_clu); - num_sectors = ((tbl_size - 1) >> blksize_bits) + 1; - ret = exfat_load_upcase_table(sb, sector, num_sectors, - le32_to_cpu(ep->dentry.upcase.checksum)); - - brelse(bh); - if (ret && ret != -EIO) - goto load_default; - - /* load successfully */ - return ret; - } - - if (exfat_get_next_cluster(sb, &(clu.dir))) - return -EIO; - } - -load_default: - /* load default upcase table */ - return exfat_load_default_upcase_table(sb); -} - -void exfat_free_upcase_table(struct exfat_sb_info *sbi) -{ - kvfree(sbi->vol_utbl); -} diff --git a/fs/exfat/super.c b/fs/exfat/super.c deleted file mode 100644 index 5539ffc20..000000000 --- a/fs/exfat/super.c +++ /dev/null @@ -1,841 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "exfat_raw.h" -#include "exfat_fs.h" - -static char exfat_default_iocharset[] = CONFIG_EXFAT_DEFAULT_IOCHARSET; -static struct kmem_cache *exfat_inode_cachep; - -static void exfat_free_iocharset(struct exfat_sb_info *sbi) -{ - if (sbi->options.iocharset != exfat_default_iocharset) - kfree(sbi->options.iocharset); -} - -static void exfat_delayed_free(struct rcu_head *p) -{ - struct exfat_sb_info *sbi = container_of(p, struct exfat_sb_info, rcu); - - unload_nls(sbi->nls_io); - exfat_free_iocharset(sbi); - exfat_free_upcase_table(sbi); - kfree(sbi); -} - -static void exfat_put_super(struct super_block *sb) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - - mutex_lock(&sbi->s_lock); - exfat_free_bitmap(sbi); - brelse(sbi->boot_bh); - mutex_unlock(&sbi->s_lock); - - call_rcu(&sbi->rcu, exfat_delayed_free); -} - -static int exfat_sync_fs(struct super_block *sb, int wait) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - int err = 0; - - if (!wait) - return 0; - - /* If there are some dirty buffers in the bdev inode */ - mutex_lock(&sbi->s_lock); - sync_blockdev(sb->s_bdev); - if (exfat_clear_volume_dirty(sb)) - err = -EIO; - mutex_unlock(&sbi->s_lock); - return err; -} - -static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - struct super_block *sb = dentry->d_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - unsigned long long id = huge_encode_dev(sb->s_bdev->bd_dev); - - if (sbi->used_clusters == EXFAT_CLUSTERS_UNTRACKED) { - mutex_lock(&sbi->s_lock); - if (exfat_count_used_clusters(sb, &sbi->used_clusters)) { - mutex_unlock(&sbi->s_lock); - return -EIO; - } - mutex_unlock(&sbi->s_lock); - } - - buf->f_type = sb->s_magic; - buf->f_bsize = sbi->cluster_size; - buf->f_blocks = sbi->num_clusters - 2; /* clu 0 & 1 */ - buf->f_bfree = buf->f_blocks - sbi->used_clusters; - buf->f_bavail = buf->f_bfree; - buf->f_fsid = u64_to_fsid(id); - /* Unicode utf16 255 characters */ - buf->f_namelen = EXFAT_MAX_FILE_LEN * NLS_MAX_CHARSET_SIZE; - return 0; -} - -static int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flags) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct boot_sector *p_boot = (struct boot_sector *)sbi->boot_bh->b_data; - bool sync; - - /* retain persistent-flags */ - new_flags |= sbi->vol_flags_persistent; - - /* flags are not changed */ - if (sbi->vol_flags == new_flags) - return 0; - - sbi->vol_flags = new_flags; - - /* skip updating volume dirty flag, - * if this volume has been mounted with read-only - */ - if (sb_rdonly(sb)) - return 0; - - p_boot->vol_flags = cpu_to_le16(new_flags); - - if ((new_flags & VOLUME_DIRTY) && !buffer_dirty(sbi->boot_bh)) - sync = true; - else - sync = false; - - set_buffer_uptodate(sbi->boot_bh); - mark_buffer_dirty(sbi->boot_bh); - - if (sync) - sync_dirty_buffer(sbi->boot_bh); - return 0; -} - -int exfat_set_volume_dirty(struct super_block *sb) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - - return exfat_set_vol_flags(sb, sbi->vol_flags | VOLUME_DIRTY); -} - -int exfat_clear_volume_dirty(struct super_block *sb) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - - return exfat_set_vol_flags(sb, sbi->vol_flags & ~VOLUME_DIRTY); -} - -static int exfat_show_options(struct seq_file *m, struct dentry *root) -{ - struct super_block *sb = root->d_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_mount_options *opts = &sbi->options; - - /* Show partition info */ - if (!uid_eq(opts->fs_uid, GLOBAL_ROOT_UID)) - seq_printf(m, ",uid=%u", - from_kuid_munged(&init_user_ns, opts->fs_uid)); - if (!gid_eq(opts->fs_gid, GLOBAL_ROOT_GID)) - seq_printf(m, ",gid=%u", - from_kgid_munged(&init_user_ns, opts->fs_gid)); - seq_printf(m, ",fmask=%04o,dmask=%04o", opts->fs_fmask, opts->fs_dmask); - if (opts->allow_utime) - seq_printf(m, ",allow_utime=%04o", opts->allow_utime); - if (opts->utf8) - seq_puts(m, ",iocharset=utf8"); - else if (sbi->nls_io) - seq_printf(m, ",iocharset=%s", sbi->nls_io->charset); - if (opts->errors == EXFAT_ERRORS_CONT) - seq_puts(m, ",errors=continue"); - else if (opts->errors == EXFAT_ERRORS_PANIC) - seq_puts(m, ",errors=panic"); - else - seq_puts(m, ",errors=remount-ro"); - if (opts->discard) - seq_puts(m, ",discard"); - if (opts->time_offset) - seq_printf(m, ",time_offset=%d", opts->time_offset); - return 0; -} - -static struct inode *exfat_alloc_inode(struct super_block *sb) -{ - struct exfat_inode_info *ei; - - ei = kmem_cache_alloc(exfat_inode_cachep, GFP_NOFS); - if (!ei) - return NULL; - - init_rwsem(&ei->truncate_lock); - return &ei->vfs_inode; -} - -static void exfat_free_inode(struct inode *inode) -{ - kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode)); -} - -static const struct super_operations exfat_sops = { - .alloc_inode = exfat_alloc_inode, - .free_inode = exfat_free_inode, - .write_inode = exfat_write_inode, - .evict_inode = exfat_evict_inode, - .put_super = exfat_put_super, - .sync_fs = exfat_sync_fs, - .statfs = exfat_statfs, - .show_options = exfat_show_options, -}; - -enum { - Opt_uid, - Opt_gid, - Opt_umask, - Opt_dmask, - Opt_fmask, - Opt_allow_utime, - Opt_charset, - Opt_errors, - Opt_discard, - Opt_time_offset, - - /* Deprecated options */ - Opt_utf8, - Opt_debug, - Opt_namecase, - Opt_codepage, -}; - -static const struct constant_table exfat_param_enums[] = { - { "continue", EXFAT_ERRORS_CONT }, - { "panic", EXFAT_ERRORS_PANIC }, - { "remount-ro", EXFAT_ERRORS_RO }, - {} -}; - -static const struct fs_parameter_spec exfat_parameters[] = { - fsparam_u32("uid", Opt_uid), - fsparam_u32("gid", Opt_gid), - fsparam_u32oct("umask", Opt_umask), - fsparam_u32oct("dmask", Opt_dmask), - fsparam_u32oct("fmask", Opt_fmask), - fsparam_u32oct("allow_utime", Opt_allow_utime), - fsparam_string("iocharset", Opt_charset), - fsparam_enum("errors", Opt_errors, exfat_param_enums), - fsparam_flag("discard", Opt_discard), - fsparam_s32("time_offset", Opt_time_offset), - __fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated, - NULL), - __fsparam(NULL, "debug", Opt_debug, fs_param_deprecated, - NULL), - __fsparam(fs_param_is_u32, "namecase", Opt_namecase, - fs_param_deprecated, NULL), - __fsparam(fs_param_is_u32, "codepage", Opt_codepage, - fs_param_deprecated, NULL), - {} -}; - -static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param) -{ - struct exfat_sb_info *sbi = fc->s_fs_info; - struct exfat_mount_options *opts = &sbi->options; - struct fs_parse_result result; - int opt; - - opt = fs_parse(fc, exfat_parameters, param, &result); - if (opt < 0) - return opt; - - switch (opt) { - case Opt_uid: - opts->fs_uid = make_kuid(current_user_ns(), result.uint_32); - break; - case Opt_gid: - opts->fs_gid = make_kgid(current_user_ns(), result.uint_32); - break; - case Opt_umask: - opts->fs_fmask = result.uint_32; - opts->fs_dmask = result.uint_32; - break; - case Opt_dmask: - opts->fs_dmask = result.uint_32; - break; - case Opt_fmask: - opts->fs_fmask = result.uint_32; - break; - case Opt_allow_utime: - opts->allow_utime = result.uint_32 & 0022; - break; - case Opt_charset: - exfat_free_iocharset(sbi); - opts->iocharset = param->string; - param->string = NULL; - break; - case Opt_errors: - opts->errors = result.uint_32; - break; - case Opt_discard: - opts->discard = 1; - break; - case Opt_time_offset: - /* - * Make the limit 24 just in case someone invents something - * unusual. - */ - if (result.int_32 < -24 * 60 || result.int_32 > 24 * 60) - return -EINVAL; - opts->time_offset = result.int_32; - break; - case Opt_utf8: - case Opt_debug: - case Opt_namecase: - case Opt_codepage: - break; - default: - return -EINVAL; - } - - return 0; -} - -static void exfat_hash_init(struct super_block *sb) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - int i; - - spin_lock_init(&sbi->inode_hash_lock); - for (i = 0; i < EXFAT_HASH_SIZE; i++) - INIT_HLIST_HEAD(&sbi->inode_hashtable[i]); -} - -static int exfat_read_root(struct inode *inode) -{ - struct super_block *sb = inode->i_sb; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - struct exfat_inode_info *ei = EXFAT_I(inode); - struct exfat_chain cdir; - int num_subdirs, num_clu = 0; - - exfat_chain_set(&ei->dir, sbi->root_dir, 0, ALLOC_FAT_CHAIN); - ei->entry = -1; - ei->start_clu = sbi->root_dir; - ei->flags = ALLOC_FAT_CHAIN; - ei->type = TYPE_DIR; - ei->version = 0; - ei->hint_bmap.off = EXFAT_EOF_CLUSTER; - ei->hint_stat.eidx = 0; - ei->hint_stat.clu = sbi->root_dir; - ei->hint_femp.eidx = EXFAT_HINT_NONE; - - exfat_chain_set(&cdir, sbi->root_dir, 0, ALLOC_FAT_CHAIN); - if (exfat_count_num_clusters(sb, &cdir, &num_clu)) - return -EIO; - i_size_write(inode, num_clu << sbi->cluster_size_bits); - - num_subdirs = exfat_count_dir_entries(sb, &cdir); - if (num_subdirs < 0) - return -EIO; - set_nlink(inode, num_subdirs + EXFAT_MIN_SUBDIR); - - inode->i_uid = sbi->options.fs_uid; - inode->i_gid = sbi->options.fs_gid; - inode_inc_iversion(inode); - inode->i_generation = 0; - inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, 0777); - inode->i_op = &exfat_dir_inode_operations; - inode->i_fop = &exfat_dir_operations; - - inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) - & ~(sbi->cluster_size - 1)) >> inode->i_blkbits; - EXFAT_I(inode)->i_pos = ((loff_t)sbi->root_dir << 32) | 0xffffffff; - EXFAT_I(inode)->i_size_aligned = i_size_read(inode); - EXFAT_I(inode)->i_size_ondisk = i_size_read(inode); - - exfat_save_attr(inode, ATTR_SUBDIR); - inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime = - current_time(inode); - exfat_truncate_atime(&inode->i_atime); - return 0; -} - -static int exfat_calibrate_blocksize(struct super_block *sb, int logical_sect) -{ - struct exfat_sb_info *sbi = EXFAT_SB(sb); - - if (!is_power_of_2(logical_sect)) { - exfat_err(sb, "bogus logical sector size %u", logical_sect); - return -EIO; - } - - if (logical_sect < sb->s_blocksize) { - exfat_err(sb, "logical sector size too small for device (logical sector size = %u)", - logical_sect); - return -EIO; - } - - if (logical_sect > sb->s_blocksize) { - brelse(sbi->boot_bh); - sbi->boot_bh = NULL; - - if (!sb_set_blocksize(sb, logical_sect)) { - exfat_err(sb, "unable to set blocksize %u", - logical_sect); - return -EIO; - } - sbi->boot_bh = sb_bread(sb, 0); - if (!sbi->boot_bh) { - exfat_err(sb, "unable to read boot sector (logical sector size = %lu)", - sb->s_blocksize); - return -EIO; - } - } - return 0; -} - -static int exfat_read_boot_sector(struct super_block *sb) -{ - struct boot_sector *p_boot; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - - /* set block size to read super block */ - sb_min_blocksize(sb, 512); - - /* read boot sector */ - sbi->boot_bh = sb_bread(sb, 0); - if (!sbi->boot_bh) { - exfat_err(sb, "unable to read boot sector"); - return -EIO; - } - p_boot = (struct boot_sector *)sbi->boot_bh->b_data; - - /* check the validity of BOOT */ - if (le16_to_cpu((p_boot->signature)) != BOOT_SIGNATURE) { - exfat_err(sb, "invalid boot record signature"); - return -EINVAL; - } - - if (memcmp(p_boot->fs_name, STR_EXFAT, BOOTSEC_FS_NAME_LEN)) { - exfat_err(sb, "invalid fs_name"); /* fs_name may unprintable */ - return -EINVAL; - } - - /* - * must_be_zero field must be filled with zero to prevent mounting - * from FAT volume. - */ - if (memchr_inv(p_boot->must_be_zero, 0, sizeof(p_boot->must_be_zero))) - return -EINVAL; - - if (p_boot->num_fats != 1 && p_boot->num_fats != 2) { - exfat_err(sb, "bogus number of FAT structure"); - return -EINVAL; - } - - /* - * sect_size_bits could be at least 9 and at most 12. - */ - if (p_boot->sect_size_bits < EXFAT_MIN_SECT_SIZE_BITS || - p_boot->sect_size_bits > EXFAT_MAX_SECT_SIZE_BITS) { - exfat_err(sb, "bogus sector size bits : %u\n", - p_boot->sect_size_bits); - return -EINVAL; - } - - /* - * sect_per_clus_bits could be at least 0 and at most 25 - sect_size_bits. - */ - if (p_boot->sect_per_clus_bits > EXFAT_MAX_SECT_PER_CLUS_BITS(p_boot)) { - exfat_err(sb, "bogus sectors bits per cluster : %u\n", - p_boot->sect_per_clus_bits); - return -EINVAL; - } - - sbi->sect_per_clus = 1 << p_boot->sect_per_clus_bits; - sbi->sect_per_clus_bits = p_boot->sect_per_clus_bits; - sbi->cluster_size_bits = p_boot->sect_per_clus_bits + - p_boot->sect_size_bits; - sbi->cluster_size = 1 << sbi->cluster_size_bits; - sbi->num_FAT_sectors = le32_to_cpu(p_boot->fat_length); - sbi->FAT1_start_sector = le32_to_cpu(p_boot->fat_offset); - sbi->FAT2_start_sector = le32_to_cpu(p_boot->fat_offset); - if (p_boot->num_fats == 2) - sbi->FAT2_start_sector += sbi->num_FAT_sectors; - sbi->data_start_sector = le32_to_cpu(p_boot->clu_offset); - sbi->num_sectors = le64_to_cpu(p_boot->vol_length); - /* because the cluster index starts with 2 */ - sbi->num_clusters = le32_to_cpu(p_boot->clu_count) + - EXFAT_RESERVED_CLUSTERS; - - sbi->root_dir = le32_to_cpu(p_boot->root_cluster); - sbi->dentries_per_clu = 1 << - (sbi->cluster_size_bits - DENTRY_SIZE_BITS); - - sbi->vol_flags = le16_to_cpu(p_boot->vol_flags); - sbi->vol_flags_persistent = sbi->vol_flags & (VOLUME_DIRTY | MEDIA_FAILURE); - sbi->clu_srch_ptr = EXFAT_FIRST_CLUSTER; - sbi->used_clusters = EXFAT_CLUSTERS_UNTRACKED; - - /* check consistencies */ - if ((u64)sbi->num_FAT_sectors << p_boot->sect_size_bits < - (u64)sbi->num_clusters * 4) { - exfat_err(sb, "bogus fat length"); - return -EINVAL; - } - - if (sbi->data_start_sector < - (u64)sbi->FAT1_start_sector + - (u64)sbi->num_FAT_sectors * p_boot->num_fats) { - exfat_err(sb, "bogus data start sector"); - return -EINVAL; - } - - if (sbi->vol_flags & VOLUME_DIRTY) - exfat_warn(sb, "Volume was not properly unmounted. Some data may be corrupt. Please run fsck."); - if (sbi->vol_flags & MEDIA_FAILURE) - exfat_warn(sb, "Medium has reported failures. Some data may be lost."); - - /* exFAT file size is limited by a disk volume size */ - sb->s_maxbytes = (u64)(sbi->num_clusters - EXFAT_RESERVED_CLUSTERS) << - sbi->cluster_size_bits; - - /* check logical sector size */ - if (exfat_calibrate_blocksize(sb, 1 << p_boot->sect_size_bits)) - return -EIO; - - return 0; -} - -static int exfat_verify_boot_region(struct super_block *sb) -{ - struct buffer_head *bh = NULL; - u32 chksum = 0; - __le32 *p_sig, *p_chksum; - int sn, i; - - /* read boot sector sub-regions */ - for (sn = 0; sn < 11; sn++) { - bh = sb_bread(sb, sn); - if (!bh) - return -EIO; - - if (sn != 0 && sn <= 8) { - /* extended boot sector sub-regions */ - p_sig = (__le32 *)&bh->b_data[sb->s_blocksize - 4]; - if (le32_to_cpu(*p_sig) != EXBOOT_SIGNATURE) - exfat_warn(sb, "Invalid exboot-signature(sector = %d): 0x%08x", - sn, le32_to_cpu(*p_sig)); - } - - chksum = exfat_calc_chksum32(bh->b_data, sb->s_blocksize, - chksum, sn ? CS_DEFAULT : CS_BOOT_SECTOR); - brelse(bh); - } - - /* boot checksum sub-regions */ - bh = sb_bread(sb, sn); - if (!bh) - return -EIO; - - for (i = 0; i < sb->s_blocksize; i += sizeof(u32)) { - p_chksum = (__le32 *)&bh->b_data[i]; - if (le32_to_cpu(*p_chksum) != chksum) { - exfat_err(sb, "Invalid boot checksum (boot checksum : 0x%08x, checksum : 0x%08x)", - le32_to_cpu(*p_chksum), chksum); - brelse(bh); - return -EINVAL; - } - } - brelse(bh); - return 0; -} - -/* mount the file system volume */ -static int __exfat_fill_super(struct super_block *sb) -{ - int ret; - struct exfat_sb_info *sbi = EXFAT_SB(sb); - - ret = exfat_read_boot_sector(sb); - if (ret) { - exfat_err(sb, "failed to read boot sector"); - goto free_bh; - } - - ret = exfat_verify_boot_region(sb); - if (ret) { - exfat_err(sb, "invalid boot region"); - goto free_bh; - } - - ret = exfat_create_upcase_table(sb); - if (ret) { - exfat_err(sb, "failed to load upcase table"); - goto free_bh; - } - - ret = exfat_load_bitmap(sb); - if (ret) { - exfat_err(sb, "failed to load alloc-bitmap"); - goto free_upcase_table; - } - - ret = exfat_count_used_clusters(sb, &sbi->used_clusters); - if (ret) { - exfat_err(sb, "failed to scan clusters"); - goto free_alloc_bitmap; - } - - return 0; - -free_alloc_bitmap: - exfat_free_bitmap(sbi); -free_upcase_table: - exfat_free_upcase_table(sbi); -free_bh: - brelse(sbi->boot_bh); - return ret; -} - -static int exfat_fill_super(struct super_block *sb, struct fs_context *fc) -{ - struct exfat_sb_info *sbi = sb->s_fs_info; - struct exfat_mount_options *opts = &sbi->options; - struct inode *root_inode; - int err; - - if (opts->allow_utime == (unsigned short)-1) - opts->allow_utime = ~opts->fs_dmask & 0022; - - if (opts->discard) { - struct request_queue *q = bdev_get_queue(sb->s_bdev); - - if (!blk_queue_discard(q)) { - exfat_warn(sb, "mounting with \"discard\" option, but the device does not support discard"); - opts->discard = 0; - } - } - - sb->s_flags |= SB_NODIRATIME; - sb->s_magic = EXFAT_SUPER_MAGIC; - sb->s_op = &exfat_sops; - - sb->s_time_gran = 10 * NSEC_PER_MSEC; - sb->s_time_min = EXFAT_MIN_TIMESTAMP_SECS; - sb->s_time_max = EXFAT_MAX_TIMESTAMP_SECS; - - err = __exfat_fill_super(sb); - if (err) { - exfat_err(sb, "failed to recognize exfat type"); - goto check_nls_io; - } - - /* set up enough so that it can read an inode */ - exfat_hash_init(sb); - - if (!strcmp(sbi->options.iocharset, "utf8")) - opts->utf8 = 1; - else { - sbi->nls_io = load_nls(sbi->options.iocharset); - if (!sbi->nls_io) { - exfat_err(sb, "IO charset %s not found", - sbi->options.iocharset); - err = -EINVAL; - goto free_table; - } - } - - if (sbi->options.utf8) - sb->s_d_op = &exfat_utf8_dentry_ops; - else - sb->s_d_op = &exfat_dentry_ops; - - root_inode = new_inode(sb); - if (!root_inode) { - exfat_err(sb, "failed to allocate root inode"); - err = -ENOMEM; - goto free_table; - } - - root_inode->i_ino = EXFAT_ROOT_INO; - inode_set_iversion(root_inode, 1); - err = exfat_read_root(root_inode); - if (err) { - exfat_err(sb, "failed to initialize root inode"); - goto put_inode; - } - - exfat_hash_inode(root_inode, EXFAT_I(root_inode)->i_pos); - insert_inode_hash(root_inode); - - sb->s_root = d_make_root(root_inode); - if (!sb->s_root) { - exfat_err(sb, "failed to get the root dentry"); - err = -ENOMEM; - goto free_table; - } - - return 0; - -put_inode: - iput(root_inode); - sb->s_root = NULL; - -free_table: - exfat_free_upcase_table(sbi); - exfat_free_bitmap(sbi); - brelse(sbi->boot_bh); - -check_nls_io: - unload_nls(sbi->nls_io); - exfat_free_iocharset(sbi); - sb->s_fs_info = NULL; - kfree(sbi); - return err; -} - -static int exfat_get_tree(struct fs_context *fc) -{ - return get_tree_bdev(fc, exfat_fill_super); -} - -static void exfat_free(struct fs_context *fc) -{ - struct exfat_sb_info *sbi = fc->s_fs_info; - - if (sbi) { - exfat_free_iocharset(sbi); - kfree(sbi); - } -} - -static int exfat_reconfigure(struct fs_context *fc) -{ - fc->sb_flags |= SB_NODIRATIME; - - /* volume flag will be updated in exfat_sync_fs */ - sync_filesystem(fc->root->d_sb); - return 0; -} - -static const struct fs_context_operations exfat_context_ops = { - .parse_param = exfat_parse_param, - .get_tree = exfat_get_tree, - .free = exfat_free, - .reconfigure = exfat_reconfigure, -}; - -static int exfat_init_fs_context(struct fs_context *fc) -{ - struct exfat_sb_info *sbi; - - sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL); - if (!sbi) - return -ENOMEM; - - mutex_init(&sbi->s_lock); - mutex_init(&sbi->bitmap_lock); - ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL, - DEFAULT_RATELIMIT_BURST); - - sbi->options.fs_uid = current_uid(); - sbi->options.fs_gid = current_gid(); - sbi->options.fs_fmask = current->fs->umask; - sbi->options.fs_dmask = current->fs->umask; - sbi->options.allow_utime = -1; - sbi->options.iocharset = exfat_default_iocharset; - sbi->options.errors = EXFAT_ERRORS_RO; - - fc->s_fs_info = sbi; - fc->ops = &exfat_context_ops; - return 0; -} - -static struct file_system_type exfat_fs_type = { - .owner = THIS_MODULE, - .name = "exfat", - .init_fs_context = exfat_init_fs_context, - .parameters = exfat_parameters, - .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV, -}; - -static void exfat_inode_init_once(void *foo) -{ - struct exfat_inode_info *ei = (struct exfat_inode_info *)foo; - - spin_lock_init(&ei->cache_lru_lock); - ei->nr_caches = 0; - ei->cache_valid_id = EXFAT_CACHE_VALID + 1; - INIT_LIST_HEAD(&ei->cache_lru); - INIT_HLIST_NODE(&ei->i_hash_fat); - inode_init_once(&ei->vfs_inode); -} - -static int __init init_exfat_fs(void) -{ - int err; - - err = exfat_cache_init(); - if (err) - return err; - - exfat_inode_cachep = kmem_cache_create("exfat_inode_cache", - sizeof(struct exfat_inode_info), - 0, SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, - exfat_inode_init_once); - if (!exfat_inode_cachep) { - err = -ENOMEM; - goto shutdown_cache; - } - - err = register_filesystem(&exfat_fs_type); - if (err) - goto destroy_cache; - - return 0; - -destroy_cache: - kmem_cache_destroy(exfat_inode_cachep); -shutdown_cache: - exfat_cache_shutdown(); - return err; -} - -static void __exit exit_exfat_fs(void) -{ - /* - * Make sure all delayed rcu free inodes are flushed before we - * destroy cache. - */ - rcu_barrier(); - kmem_cache_destroy(exfat_inode_cachep); - unregister_filesystem(&exfat_fs_type); - exfat_cache_shutdown(); -} - -module_init(init_exfat_fs); -module_exit(exit_exfat_fs); - -MODULE_ALIAS_FS("exfat"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("exFAT filesystem support"); -MODULE_AUTHOR("Samsung Electronics Co., Ltd.");