aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/exfat/exfat_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/exfat/exfat_core.c')
-rw-r--r--drivers/staging/exfat/exfat_core.c2582
1 files changed, 0 insertions, 2582 deletions
diff --git a/drivers/staging/exfat/exfat_core.c b/drivers/staging/exfat/exfat_core.c
deleted file mode 100644
index 07b460d01334..000000000000
--- a/drivers/staging/exfat/exfat_core.c
+++ /dev/null
@@ -1,2582 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd.
- */
-
-#include <linux/types.h>
-#include <linux/buffer_head.h>
-#include <linux/fs.h>
-#include <linux/mutex.h>
-#include <linux/blkdev.h>
-#include <linux/slab.h>
-#include "exfat.h"
-
-static void __set_sb_dirty(struct super_block *sb)
-{
- struct exfat_sb_info *sbi = EXFAT_SB(sb);
-
- sbi->s_dirt = 1;
-}
-
-static u8 name_buf[MAX_PATH_LENGTH * MAX_CHARSET_SIZE];
-
-static u8 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 u8 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 */
-};
-
-#define BITMAP_LOC(v) ((v) >> 3)
-#define BITMAP_SHIFT(v) ((v) & 0x07)
-
-static inline s32 exfat_bitmap_test(u8 *bitmap, int i)
-{
- u8 data;
-
- data = bitmap[BITMAP_LOC(i)];
- if ((data >> BITMAP_SHIFT(i)) & 0x01)
- return 1;
- return 0;
-}
-
-static inline void exfat_bitmap_set(u8 *bitmap, int i)
-{
- bitmap[BITMAP_LOC(i)] |= (0x01 << BITMAP_SHIFT(i));
-}
-
-static inline void exfat_bitmap_clear(u8 *bitmap, int i)
-{
- bitmap[BITMAP_LOC(i)] &= ~(0x01 << BITMAP_SHIFT(i));
-}
-
-/*
- * File System Management Functions
- */
-
-void fs_set_vol_flags(struct super_block *sb, u32 new_flag)
-{
- struct pbr_sector_t *p_pbr;
- struct bpbex_t *p_bpb;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- if (p_fs->vol_flag == new_flag)
- return;
-
- p_fs->vol_flag = new_flag;
-
- if (!p_fs->pbr_bh) {
- if (sector_read(sb, p_fs->PBR_sector,
- &p_fs->pbr_bh, 1) != 0)
- return;
- }
-
- p_pbr = (struct pbr_sector_t *)p_fs->pbr_bh->b_data;
- p_bpb = (struct bpbex_t *)p_pbr->bpb;
- SET16(p_bpb->vol_flags, (u16)new_flag);
-
- /* XXX duyoung
- * what can we do here? (cuz fs_set_vol_flags() is void)
- */
- if ((new_flag == VOL_DIRTY) && (!buffer_dirty(p_fs->pbr_bh)))
- sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 1);
- else
- sector_write(sb, p_fs->PBR_sector, p_fs->pbr_bh, 0);
-}
-
-void fs_error(struct super_block *sb)
-{
- struct exfat_mount_options *opts = &EXFAT_SB(sb)->options;
-
- if (opts->errors == EXFAT_ERRORS_PANIC) {
- panic("[EXFAT] Filesystem panic from previous error\n");
- } else if ((opts->errors == EXFAT_ERRORS_RO) && !sb_rdonly(sb)) {
- sb->s_flags |= SB_RDONLY;
- pr_err("[EXFAT] Filesystem has been set read-only\n");
- }
-}
-
-/*
- * Cluster Management Functions
- */
-
-static s32 clear_cluster(struct super_block *sb, u32 clu)
-{
- sector_t s, n;
- s32 ret = 0;
- struct buffer_head *tmp_bh = NULL;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
-
- if (clu == CLUSTER_32(0)) { /* FAT16 root_dir */
- s = p_fs->root_start_sector;
- n = p_fs->data_start_sector;
- } else {
- s = START_SECTOR(clu);
- n = s + p_fs->sectors_per_clu;
- }
-
- for (; s < n; s++) {
- ret = sector_read(sb, s, &tmp_bh, 0);
- if (ret != 0)
- return ret;
-
- memset((char *)tmp_bh->b_data, 0x0, p_bd->sector_size);
- ret = sector_write(sb, s, tmp_bh, 0);
- if (ret != 0)
- break;
- }
-
- brelse(tmp_bh);
- return ret;
-}
-
-static s32 set_alloc_bitmap(struct super_block *sb, u32 clu)
-{
- int i, b;
- sector_t sector;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
-
- i = clu >> (p_bd->sector_size_bits + 3);
- b = clu & ((p_bd->sector_size << 3) - 1);
-
- sector = START_SECTOR(p_fs->map_clu) + i;
-
- exfat_bitmap_set((u8 *)p_fs->vol_amap[i]->b_data, b);
-
- return sector_write(sb, sector, p_fs->vol_amap[i], 0);
-}
-
-static s32 clr_alloc_bitmap(struct super_block *sb, u32 clu)
-{
- int i, b;
- sector_t sector;
-#ifdef CONFIG_STAGING_EXFAT_DISCARD
- struct exfat_sb_info *sbi = EXFAT_SB(sb);
- struct exfat_mount_options *opts = &sbi->options;
- int ret;
-#endif /* CONFIG_STAGING_EXFAT_DISCARD */
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
-
- i = clu >> (p_bd->sector_size_bits + 3);
- b = clu & ((p_bd->sector_size << 3) - 1);
-
- sector = START_SECTOR(p_fs->map_clu) + i;
-
- exfat_bitmap_clear((u8 *)p_fs->vol_amap[i]->b_data, b);
-
-#ifdef CONFIG_STAGING_EXFAT_DISCARD
- if (opts->discard) {
- ret = sb_issue_discard(sb, START_SECTOR(clu),
- (1 << p_fs->sectors_per_clu_bits),
- GFP_NOFS, 0);
- if (ret == -EOPNOTSUPP) {
- pr_warn("discard not supported by device, disabling");
- opts->discard = 0;
- } else {
- return ret;
- }
- }
-#endif /* CONFIG_STAGING_EXFAT_DISCARD */
-
- return sector_write(sb, sector, p_fs->vol_amap[i], 0);
-}
-
-static u32 test_alloc_bitmap(struct super_block *sb, u32 clu)
-{
- int i, map_i, map_b;
- u32 clu_base, clu_free;
- u8 k, clu_mask;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
-
- clu_base = (clu & ~(0x7)) + 2;
- clu_mask = (1 << (clu - clu_base + 2)) - 1;
-
- map_i = clu >> (p_bd->sector_size_bits + 3);
- map_b = (clu >> 3) & p_bd->sector_size_mask;
-
- for (i = 2; i < p_fs->num_clusters; i += 8) {
- k = *(((u8 *)p_fs->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 < p_fs->num_clusters)
- return clu_free;
- }
- clu_base += 8;
-
- if (((++map_b) >= p_bd->sector_size) ||
- (clu_base >= p_fs->num_clusters)) {
- if ((++map_i) >= p_fs->map_sectors) {
- clu_base = 2;
- map_i = 0;
- }
- map_b = 0;
- }
- }
-
- return CLUSTER_32(~0);
-}
-
-s32 exfat_alloc_cluster(struct super_block *sb, s32 num_alloc,
- struct chain_t *p_chain)
-{
- s32 num_clusters = 0;
- u32 hint_clu, new_clu, last_clu = CLUSTER_32(~0);
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- hint_clu = p_chain->dir;
- if (hint_clu == CLUSTER_32(~0)) {
- hint_clu = test_alloc_bitmap(sb, p_fs->clu_srch_ptr - 2);
- if (hint_clu == CLUSTER_32(~0))
- return 0;
- } else if (hint_clu >= p_fs->num_clusters) {
- hint_clu = 2;
- p_chain->flags = 0x01;
- }
-
- __set_sb_dirty(sb);
-
- p_chain->dir = CLUSTER_32(~0);
-
- while ((new_clu = test_alloc_bitmap(sb, hint_clu - 2)) != CLUSTER_32(~0)) {
- if (new_clu != hint_clu) {
- if (p_chain->flags == 0x03) {
- exfat_chain_cont_cluster(sb, p_chain->dir,
- num_clusters);
- p_chain->flags = 0x01;
- }
- }
-
- if (set_alloc_bitmap(sb, new_clu - 2) != 0)
- return -EIO;
-
- num_clusters++;
-
- if (p_chain->flags == 0x01) {
- if (exfat_fat_write(sb, new_clu, CLUSTER_32(~0)) < 0)
- return -EIO;
- }
-
- if (p_chain->dir == CLUSTER_32(~0)) {
- p_chain->dir = new_clu;
- } else {
- if (p_chain->flags == 0x01) {
- if (exfat_fat_write(sb, last_clu, new_clu) < 0)
- return -EIO;
- }
- }
- last_clu = new_clu;
-
- if ((--num_alloc) == 0) {
- p_fs->clu_srch_ptr = hint_clu;
- if (p_fs->used_clusters != UINT_MAX)
- p_fs->used_clusters += num_clusters;
-
- p_chain->size += num_clusters;
- return num_clusters;
- }
-
- hint_clu = new_clu + 1;
- if (hint_clu >= p_fs->num_clusters) {
- hint_clu = 2;
-
- if (p_chain->flags == 0x03) {
- exfat_chain_cont_cluster(sb, p_chain->dir,
- num_clusters);
- p_chain->flags = 0x01;
- }
- }
- }
-
- p_fs->clu_srch_ptr = hint_clu;
- if (p_fs->used_clusters != UINT_MAX)
- p_fs->used_clusters += num_clusters;
-
- p_chain->size += num_clusters;
- return num_clusters;
-}
-
-void exfat_free_cluster(struct super_block *sb, struct chain_t *p_chain,
- s32 do_relse)
-{
- s32 num_clusters = 0;
- u32 clu;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- int i;
- sector_t sector;
-
- if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0)))
- return;
-
- if (p_chain->size <= 0) {
- pr_err("[EXFAT] free_cluster : skip free-req clu:%u, because of zero-size truncation\n",
- p_chain->dir);
- return;
- }
-
- __set_sb_dirty(sb);
- clu = p_chain->dir;
-
- if (p_chain->flags == 0x03) {
- do {
- if (do_relse) {
- sector = START_SECTOR(clu);
- for (i = 0; i < p_fs->sectors_per_clu; i++)
- exfat_buf_release(sb, sector + i);
- }
-
- if (clr_alloc_bitmap(sb, clu - 2) != 0)
- break;
- clu++;
-
- num_clusters++;
- } while (num_clusters < p_chain->size);
- } else {
- do {
- if (p_fs->dev_ejected)
- break;
-
- if (do_relse) {
- sector = START_SECTOR(clu);
- for (i = 0; i < p_fs->sectors_per_clu; i++)
- exfat_buf_release(sb, sector + i);
- }
-
- if (clr_alloc_bitmap(sb, clu - 2) != 0)
- break;
-
- if (exfat_fat_read(sb, clu, &clu) == -1)
- break;
- num_clusters++;
- } while ((clu != CLUSTER_32(0)) && (clu != CLUSTER_32(~0)));
- }
-
- if (p_fs->used_clusters != UINT_MAX)
- p_fs->used_clusters -= num_clusters;
-}
-
-static u32 find_last_cluster(struct super_block *sb, struct chain_t *p_chain)
-{
- u32 clu, next;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- clu = p_chain->dir;
-
- if (p_chain->flags == 0x03) {
- clu += p_chain->size - 1;
- } else {
- while ((exfat_fat_read(sb, clu, &next) == 0) &&
- (next != CLUSTER_32(~0))) {
- if (p_fs->dev_ejected)
- break;
- clu = next;
- }
- }
-
- return clu;
-}
-
-s32 count_num_clusters(struct super_block *sb, struct chain_t *p_chain)
-{
- int i, count = 0;
- u32 clu;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- if ((p_chain->dir == CLUSTER_32(0)) || (p_chain->dir == CLUSTER_32(~0)))
- return 0;
-
- clu = p_chain->dir;
-
- if (p_chain->flags == 0x03) {
- count = p_chain->size;
- } else {
- for (i = 2; i < p_fs->num_clusters; i++) {
- count++;
- if (exfat_fat_read(sb, clu, &clu) != 0)
- return 0;
- if (clu == CLUSTER_32(~0))
- break;
- }
- }
-
- return count;
-}
-
-s32 exfat_count_used_clusters(struct super_block *sb)
-{
- int i, map_i, map_b, count = 0;
- u8 k;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
-
- map_i = 0;
- map_b = 0;
-
- for (i = 2; i < p_fs->num_clusters; i += 8) {
- k = *(((u8 *)p_fs->vol_amap[map_i]->b_data) + map_b);
- count += used_bit[k];
-
- if ((++map_b) >= p_bd->sector_size) {
- map_i++;
- map_b = 0;
- }
- }
-
- return count;
-}
-
-void exfat_chain_cont_cluster(struct super_block *sb, u32 chain, s32 len)
-{
- if (len == 0)
- return;
-
- while (len > 1) {
- if (exfat_fat_write(sb, chain, chain + 1) < 0)
- break;
- chain++;
- len--;
- }
- exfat_fat_write(sb, chain, CLUSTER_32(~0));
-}
-
-/*
- * Allocation Bitmap Management Functions
- */
-
-s32 load_alloc_bitmap(struct super_block *sb)
-{
- int i, j, ret;
- u32 map_size;
- u32 type;
- sector_t sector;
- struct chain_t clu;
- struct bmap_dentry_t *ep;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
-
- clu.dir = p_fs->root_dir;
- clu.flags = 0x01;
-
- while (clu.dir != CLUSTER_32(~0)) {
- if (p_fs->dev_ejected)
- break;
-
- for (i = 0; i < p_fs->dentries_per_clu; i++) {
- ep = (struct bmap_dentry_t *)get_entry_in_dir(sb, &clu,
- i, NULL);
- if (!ep)
- return -ENOENT;
-
- type = exfat_get_entry_type((struct dentry_t *)ep);
-
- if (type == TYPE_UNUSED)
- break;
- if (type != TYPE_BITMAP)
- continue;
-
- if (ep->flags == 0x0) {
- p_fs->map_clu = GET32_A(ep->start_clu);
- map_size = (u32)GET64_A(ep->size);
-
- p_fs->map_sectors = ((map_size - 1) >> p_bd->sector_size_bits) + 1;
-
- p_fs->vol_amap = kmalloc_array(p_fs->map_sectors,
- sizeof(struct buffer_head *),
- GFP_KERNEL);
- if (!p_fs->vol_amap)
- return -ENOMEM;
-
- sector = START_SECTOR(p_fs->map_clu);
-
- for (j = 0; j < p_fs->map_sectors; j++) {
- p_fs->vol_amap[j] = NULL;
- ret = sector_read(sb, sector + j, &p_fs->vol_amap[j], 1);
- if (ret != 0) {
- /* release all buffers and free vol_amap */
- i = 0;
- while (i < j)
- brelse(p_fs->vol_amap[i++]);
-
- kfree(p_fs->vol_amap);
- p_fs->vol_amap = NULL;
- return ret;
- }
- }
-
- p_fs->pbr_bh = NULL;
- return 0;
- }
- }
-
- if (exfat_fat_read(sb, clu.dir, &clu.dir) != 0)
- return -EIO;
- }
-
- return -EFSCORRUPTED;
-}
-
-void free_alloc_bitmap(struct super_block *sb)
-{
- int i;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- brelse(p_fs->pbr_bh);
-
- for (i = 0; i < p_fs->map_sectors; i++)
- __brelse(p_fs->vol_amap[i]);
-
- kfree(p_fs->vol_amap);
- p_fs->vol_amap = NULL;
-}
-
-void sync_alloc_bitmap(struct super_block *sb)
-{
- int i;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- if (!p_fs->vol_amap)
- return;
-
- for (i = 0; i < p_fs->map_sectors; i++)
- sync_dirty_buffer(p_fs->vol_amap[i]);
-}
-
-/*
- * Upcase table Management Functions
- */
-static s32 __load_upcase_table(struct super_block *sb, sector_t sector,
- u32 num_sectors, u32 utbl_checksum)
-{
- int i, ret = -EINVAL;
- u32 j;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
- struct buffer_head *tmp_bh = NULL;
- sector_t end_sector = num_sectors + sector;
-
- bool skip = false;
- u32 index = 0;
- u16 uni = 0;
- u16 **upcase_table;
-
- u32 checksum = 0;
-
- upcase_table = kmalloc_array(UTBL_COL_COUNT, sizeof(u16 *), GFP_KERNEL);
- p_fs->vol_utbl = upcase_table;
- if (!upcase_table)
- return -ENOMEM;
- memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *));
-
- while (sector < end_sector) {
- ret = sector_read(sb, sector, &tmp_bh, 1);
- if (ret != 0) {
- pr_debug("sector read (0x%llX)fail\n",
- (unsigned long long)sector);
- goto error;
- }
- sector++;
-
- for (i = 0; i < p_bd->sector_size && index <= 0xFFFF; i += 2) {
- uni = GET16(((u8 *)tmp_bh->b_data) + i);
-
- checksum = ((checksum & 1) ? 0x80000000 : 0) +
- (checksum >> 1) + *(((u8 *)tmp_bh->b_data) +
- i);
- checksum = ((checksum & 1) ? 0x80000000 : 0) +
- (checksum >> 1) + *(((u8 *)tmp_bh->b_data) +
- (i + 1));
-
- if (skip) {
- pr_debug("skip from 0x%X ", index);
- index += uni;
- pr_debug("to 0x%X (amount of 0x%X)\n",
- index, uni);
- skip = false;
- } else if (uni == index) {
- index++;
- } else if (uni == 0xFFFF) {
- skip = true;
- } else { /* uni != index , uni != 0xFFFF */
- u16 col_index = get_col_index(index);
-
- if (!upcase_table[col_index]) {
- pr_debug("alloc = 0x%X\n", col_index);
- upcase_table[col_index] = kmalloc_array(UTBL_ROW_COUNT,
- sizeof(u16), GFP_KERNEL);
- if (!upcase_table[col_index]) {
- ret = -ENOMEM;
- goto error;
- }
-
- for (j = 0; j < UTBL_ROW_COUNT; j++)
- upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j;
- }
-
- upcase_table[col_index][get_row_index(index)] = uni;
- index++;
- }
- }
- }
- if (index >= 0xFFFF && utbl_checksum == checksum) {
- if (tmp_bh)
- brelse(tmp_bh);
- return 0;
- }
- ret = -EINVAL;
-error:
- if (tmp_bh)
- brelse(tmp_bh);
- free_upcase_table(sb);
- return ret;
-}
-
-static s32 __load_default_upcase_table(struct super_block *sb)
-{
- int i, ret = -EINVAL;
- u32 j;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- bool skip = false;
- u32 index = 0;
- u16 uni = 0;
- u16 **upcase_table;
-
- upcase_table = kmalloc_array(UTBL_COL_COUNT, sizeof(u16 *), GFP_KERNEL);
- p_fs->vol_utbl = upcase_table;
- if (!upcase_table)
- return -ENOMEM;
- memset(upcase_table, 0, UTBL_COL_COUNT * sizeof(u16 *));
-
- for (i = 0; index <= 0xFFFF && i < NUM_UPCASE * 2; i += 2) {
- uni = GET16(uni_upcase + i);
- if (skip) {
- pr_debug("skip from 0x%X ", index);
- index += uni;
- pr_debug("to 0x%X (amount of 0x%X)\n", index, uni);
- skip = false;
- } else if (uni == index) {
- index++;
- } else if (uni == 0xFFFF) {
- skip = true;
- } else { /* uni != index , uni != 0xFFFF */
- u16 col_index = get_col_index(index);
-
- if (!upcase_table[col_index]) {
- pr_debug("alloc = 0x%X\n", col_index);
- upcase_table[col_index] = kmalloc_array(UTBL_ROW_COUNT,
- sizeof(u16),
- GFP_KERNEL);
- if (!upcase_table[col_index]) {
- ret = -ENOMEM;
- goto error;
- }
-
- for (j = 0; j < UTBL_ROW_COUNT; j++)
- upcase_table[col_index][j] = (col_index << LOW_INDEX_BIT) | j;
- }
-
- upcase_table[col_index][get_row_index(index)] = uni;
- index++;
- }
- }
-
- if (index >= 0xFFFF)
- return 0;
-
-error:
- /* FATAL error: default upcase table has error */
- free_upcase_table(sb);
- return ret;
-}
-
-s32 load_upcase_table(struct super_block *sb)
-{
- int i;
- u32 tbl_clu, tbl_size;
- sector_t sector;
- u32 type, num_sectors;
- struct chain_t clu;
- struct case_dentry_t *ep;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
-
- clu.dir = p_fs->root_dir;
- clu.flags = 0x01;
-
- if (p_fs->dev_ejected)
- return -EIO;
-
- while (clu.dir != CLUSTER_32(~0)) {
- for (i = 0; i < p_fs->dentries_per_clu; i++) {
- ep = (struct case_dentry_t *)get_entry_in_dir(sb, &clu,
- i, NULL);
- if (!ep)
- return -ENOENT;
-
- type = exfat_get_entry_type((struct dentry_t *)ep);
-
- if (type == TYPE_UNUSED)
- break;
- if (type != TYPE_UPCASE)
- continue;
-
- tbl_clu = GET32_A(ep->start_clu);
- tbl_size = (u32)GET64_A(ep->size);
-
- sector = START_SECTOR(tbl_clu);
- num_sectors = ((tbl_size - 1) >> p_bd->sector_size_bits) + 1;
- if (__load_upcase_table(sb, sector, num_sectors,
- GET32_A(ep->checksum)) != 0)
- break;
- return 0;
- }
- if (exfat_fat_read(sb, clu.dir, &clu.dir) != 0)
- return -EIO;
- }
- /* load default upcase table */
- return __load_default_upcase_table(sb);
-}
-
-void free_upcase_table(struct super_block *sb)
-{
- u32 i;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- u16 **upcase_table;
-
- upcase_table = p_fs->vol_utbl;
- for (i = 0; i < UTBL_COL_COUNT; i++)
- kfree(upcase_table[i]);
-
- kfree(p_fs->vol_utbl);
- p_fs->vol_utbl = NULL;
-}
-
-/*
- * Directory Entry Management Functions
- */
-
-u32 exfat_get_entry_type(struct dentry_t *p_entry)
-{
- struct file_dentry_t *ep = (struct file_dentry_t *)p_entry;
-
- if (ep->type == 0x0) {
- return TYPE_UNUSED;
- } else if (ep->type < 0x80) {
- return TYPE_DELETED;
- } else if (ep->type == 0x80) {
- return TYPE_INVALID;
- } else if (ep->type < 0xA0) {
- if (ep->type == 0x81) {
- return TYPE_BITMAP;
- } else if (ep->type == 0x82) {
- return TYPE_UPCASE;
- } else if (ep->type == 0x83) {
- return TYPE_VOLUME;
- } else if (ep->type == 0x85) {
- if (GET16_A(ep->attr) & ATTR_SUBDIR)
- return TYPE_DIR;
- else
- return TYPE_FILE;
- }
- return TYPE_CRITICAL_PRI;
- } else if (ep->type < 0xC0) {
- if (ep->type == 0xA0)
- return TYPE_GUID;
- else if (ep->type == 0xA1)
- return TYPE_PADDING;
- else if (ep->type == 0xA2)
- return TYPE_ACLTAB;
- return TYPE_BENIGN_PRI;
- } else if (ep->type < 0xE0) {
- if (ep->type == 0xC0)
- return TYPE_STREAM;
- else if (ep->type == 0xC1)
- return TYPE_EXTEND;
- else if (ep->type == 0xC2)
- return TYPE_ACL;
- return TYPE_CRITICAL_SEC;
- }
-
- return TYPE_BENIGN_SEC;
-}
-
-static void exfat_set_entry_type(struct dentry_t *p_entry, u32 type)
-{
- struct file_dentry_t *ep = (struct file_dentry_t *)p_entry;
-
- if (type == TYPE_UNUSED) {
- ep->type = 0x0;
- } else if (type == TYPE_DELETED) {
- ep->type &= ~0x80;
- } else if (type == TYPE_STREAM) {
- ep->type = 0xC0;
- } else if (type == TYPE_EXTEND) {
- ep->type = 0xC1;
- } else if (type == TYPE_BITMAP) {
- ep->type = 0x81;
- } else if (type == TYPE_UPCASE) {
- ep->type = 0x82;
- } else if (type == TYPE_VOLUME) {
- ep->type = 0x83;
- } else if (type == TYPE_DIR) {
- ep->type = 0x85;
- SET16_A(ep->attr, ATTR_SUBDIR);
- } else if (type == TYPE_FILE) {
- ep->type = 0x85;
- SET16_A(ep->attr, ATTR_ARCHIVE);
- } else if (type == TYPE_SYMLINK) {
- ep->type = 0x85;
- SET16_A(ep->attr, ATTR_ARCHIVE | ATTR_SYMLINK);
- }
-}
-
-u32 exfat_get_entry_attr(struct dentry_t *p_entry)
-{
- struct file_dentry_t *ep = (struct file_dentry_t *)p_entry;
-
- return (u32)GET16_A(ep->attr);
-}
-
-void exfat_set_entry_attr(struct dentry_t *p_entry, u32 attr)
-{
- struct file_dentry_t *ep = (struct file_dentry_t *)p_entry;
-
- SET16_A(ep->attr, (u16)attr);
-}
-
-u8 exfat_get_entry_flag(struct dentry_t *p_entry)
-{
- struct strm_dentry_t *ep = (struct strm_dentry_t *)p_entry;
-
- return ep->flags;
-}
-
-void exfat_set_entry_flag(struct dentry_t *p_entry, u8 flags)
-{
- struct strm_dentry_t *ep = (struct strm_dentry_t *)p_entry;
-
- ep->flags = flags;
-}
-
-u32 exfat_get_entry_clu0(struct dentry_t *p_entry)
-{
- struct strm_dentry_t *ep = (struct strm_dentry_t *)p_entry;
-
- return GET32_A(ep->start_clu);
-}
-
-void exfat_set_entry_clu0(struct dentry_t *p_entry, u32 start_clu)
-{
- struct strm_dentry_t *ep = (struct strm_dentry_t *)p_entry;
-
- SET32_A(ep->start_clu, start_clu);
-}
-
-u64 exfat_get_entry_size(struct dentry_t *p_entry)
-{
- struct strm_dentry_t *ep = (struct strm_dentry_t *)p_entry;
-
- return GET64_A(ep->valid_size);
-}
-
-void exfat_set_entry_size(struct dentry_t *p_entry, u64 size)
-{
- struct strm_dentry_t *ep = (struct strm_dentry_t *)p_entry;
-
- SET64_A(ep->valid_size, size);
- SET64_A(ep->size, size);
-}
-
-void exfat_get_entry_time(struct dentry_t *p_entry, struct timestamp_t *tp,
- u8 mode)
-{
- u16 t = 0x00, d = 0x21;
- struct file_dentry_t *ep = (struct file_dentry_t *)p_entry;
-
- switch (mode) {
- case TM_CREATE:
- t = GET16_A(ep->create_time);
- d = GET16_A(ep->create_date);
- break;
- case TM_MODIFY:
- t = GET16_A(ep->modify_time);
- d = GET16_A(ep->modify_date);
- break;
- case TM_ACCESS:
- t = GET16_A(ep->access_time);
- d = GET16_A(ep->access_date);
- break;
- }
-
- tp->sec = (t & 0x001F) << 1;
- tp->min = (t >> 5) & 0x003F;
- tp->hour = (t >> 11);
- tp->day = (d & 0x001F);
- tp->mon = (d >> 5) & 0x000F;
- tp->year = (d >> 9);
-}
-
-void exfat_set_entry_time(struct dentry_t *p_entry, struct timestamp_t *tp,
- u8 mode)
-{
- u16 t, d;
- struct file_dentry_t *ep = (struct file_dentry_t *)p_entry;
-
- t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1);
- d = (tp->year << 9) | (tp->mon << 5) | tp->day;
-
- switch (mode) {
- case TM_CREATE:
- SET16_A(ep->create_time, t);
- SET16_A(ep->create_date, d);
- break;
- case TM_MODIFY:
- SET16_A(ep->modify_time, t);
- SET16_A(ep->modify_date, d);
- break;
- case TM_ACCESS:
- SET16_A(ep->access_time, t);
- SET16_A(ep->access_date, d);
- break;
- }
-}
-
-static void init_file_entry(struct file_dentry_t *ep, u32 type)
-{
- struct timestamp_t tm, *tp;
-
- exfat_set_entry_type((struct dentry_t *)ep, type);
-
- tp = tm_current(&tm);
- exfat_set_entry_time((struct dentry_t *)ep, tp, TM_CREATE);
- exfat_set_entry_time((struct dentry_t *)ep, tp, TM_MODIFY);
- exfat_set_entry_time((struct dentry_t *)ep, tp, TM_ACCESS);
- ep->create_time_ms = 0;
- ep->modify_time_ms = 0;
- ep->access_time_ms = 0;
-}
-
-static void init_strm_entry(struct strm_dentry_t *ep, u8 flags, u32 start_clu, u64 size)
-{
- exfat_set_entry_type((struct dentry_t *)ep, TYPE_STREAM);
- ep->flags = flags;
- SET32_A(ep->start_clu, start_clu);
- SET64_A(ep->valid_size, size);
- SET64_A(ep->size, size);
-}
-
-static void init_name_entry(struct name_dentry_t *ep, u16 *uniname)
-{
- int i;
-
- exfat_set_entry_type((struct dentry_t *)ep, TYPE_EXTEND);
- ep->flags = 0x0;
-
- for (i = 0; i < 30; i++, i++) {
- SET16_A(ep->unicode_0_14 + i, *uniname);
- if (*uniname == 0x0)
- break;
- uniname++;
- }
-}
-
-static s32 exfat_init_dir_entry(struct super_block *sb, struct chain_t *p_dir,
- s32 entry, u32 type, u32 start_clu, u64 size)
-{
- sector_t sector;
- u8 flags;
- struct file_dentry_t *file_ep;
- struct strm_dentry_t *strm_ep;
-
- flags = (type == TYPE_FILE) ? 0x01 : 0x03;
-
- /* we cannot use get_entry_set_in_dir here because file ep is not initialized yet */
- file_ep = (struct file_dentry_t *)get_entry_in_dir(sb, p_dir, entry,
- &sector);
- if (!file_ep)
- return -ENOENT;
-
- strm_ep = (struct strm_dentry_t *)get_entry_in_dir(sb, p_dir, entry + 1,
- &sector);
- if (!strm_ep)
- return -ENOENT;
-
- init_file_entry(file_ep, type);
- exfat_buf_modify(sb, sector);
-
- init_strm_entry(strm_ep, flags, start_clu, size);
- exfat_buf_modify(sb, sector);
-
- return 0;
-}
-
-static s32 exfat_init_ext_entry(struct super_block *sb, struct chain_t *p_dir,
- s32 entry, s32 num_entries,
- struct uni_name_t *p_uniname,
- struct dos_name_t *p_dosname)
-{
- int i;
- sector_t sector;
- u16 *uniname = p_uniname->name;
- struct file_dentry_t *file_ep;
- struct strm_dentry_t *strm_ep;
- struct name_dentry_t *name_ep;
-
- file_ep = (struct file_dentry_t *)get_entry_in_dir(sb, p_dir, entry,
- &sector);
- if (!file_ep)
- return -ENOENT;
-
- file_ep->num_ext = (u8)(num_entries - 1);
- exfat_buf_modify(sb, sector);
-
- strm_ep = (struct strm_dentry_t *)get_entry_in_dir(sb, p_dir, entry + 1,
- &sector);
- if (!strm_ep)
- return -ENOENT;
-
- strm_ep->name_len = p_uniname->name_len;
- SET16_A(strm_ep->name_hash, p_uniname->name_hash);
- exfat_buf_modify(sb, sector);
-
- for (i = 2; i < num_entries; i++) {
- name_ep = (struct name_dentry_t *)get_entry_in_dir(sb, p_dir,
- entry + i,
- &sector);
- if (!name_ep)
- return -ENOENT;
-
- init_name_entry(name_ep, uniname);
- exfat_buf_modify(sb, sector);
- uniname += 15;
- }
-
- update_dir_checksum(sb, p_dir, entry);
-
- return 0;
-}
-
-void exfat_delete_dir_entry(struct super_block *sb, struct chain_t *p_dir,
- s32 entry, s32 order, s32 num_entries)
-{
- int i;
- sector_t sector;
- struct dentry_t *ep;
-
- for (i = order; i < num_entries; i++) {
- ep = get_entry_in_dir(sb, p_dir, entry + i, &sector);
- if (!ep)
- return;
-
- exfat_set_entry_type(ep, TYPE_DELETED);
- exfat_buf_modify(sb, sector);
- }
-}
-
-void update_dir_checksum(struct super_block *sb, struct chain_t *p_dir,
- s32 entry)
-{
- int i, num_entries;
- sector_t sector;
- u16 chksum;
- struct file_dentry_t *file_ep;
- struct dentry_t *ep;
-
- file_ep = (struct file_dentry_t *)get_entry_in_dir(sb, p_dir, entry,
- &sector);
- if (!file_ep)
- return;
-
- exfat_buf_lock(sb, sector);
-
- num_entries = (s32)file_ep->num_ext + 1;
- chksum = calc_checksum_2byte((void *)file_ep, DENTRY_SIZE, 0,
- CS_DIR_ENTRY);
-
- for (i = 1; i < num_entries; i++) {
- ep = get_entry_in_dir(sb, p_dir, entry + i, NULL);
- if (!ep) {
- exfat_buf_unlock(sb, sector);
- return;
- }
-
- chksum = calc_checksum_2byte((void *)ep, DENTRY_SIZE, chksum,
- CS_DEFAULT);
- }
-
- SET16_A(file_ep->checksum, chksum);
- exfat_buf_modify(sb, sector);
- exfat_buf_unlock(sb, sector);
-}
-
-static s32 __write_partial_entries_in_entry_set(struct super_block *sb,
- struct entry_set_cache_t *es,
- sector_t sec, s32 off, u32 count)
-{
- s32 num_entries, buf_off = (off - es->offset);
- u32 remaining_byte_in_sector, copy_entries;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
- u32 clu;
- u8 *buf, *esbuf = (u8 *)&es->__buf;
-
- pr_debug("%s entered es %p sec %llu off %d count %d\n",
- __func__, es, (unsigned long long)sec, off, count);
- num_entries = count;
-
- while (num_entries) {
- /* white per sector base */
- remaining_byte_in_sector = (1 << p_bd->sector_size_bits) - off;
- copy_entries = min_t(s32,
- remaining_byte_in_sector >> DENTRY_SIZE_BITS,
- num_entries);
- buf = exfat_buf_getblk(sb, sec);
- if (!buf)
- goto err_out;
- pr_debug("es->buf %p buf_off %u\n", esbuf, buf_off);
- pr_debug("copying %d entries from %p to sector %llu\n",
- copy_entries, (esbuf + buf_off),
- (unsigned long long)sec);
- memcpy(buf + off, esbuf + buf_off,
- copy_entries << DENTRY_SIZE_BITS);
- exfat_buf_modify(sb, sec);
- num_entries -= copy_entries;
-
- if (num_entries) {
- /* get next sector */
- if (IS_LAST_SECTOR_IN_CLUSTER(sec)) {
- clu = GET_CLUSTER_FROM_SECTOR(sec);
- if (es->alloc_flag == 0x03) {
- clu++;
- } else {
- if (exfat_fat_read(sb, clu, &clu) == -1)
- goto err_out;
- }
- sec = START_SECTOR(clu);
- } else {
- sec++;
- }
- off = 0;
- buf_off += copy_entries << DENTRY_SIZE_BITS;
- }
- }
-
- pr_debug("%s exited successfully\n", __func__);
- return 0;
-err_out:
- pr_debug("%s failed\n", __func__);
- return -EINVAL;
-}
-
-/* write back all entries in entry set */
-static s32 write_whole_entry_set(struct super_block *sb, struct entry_set_cache_t *es)
-{
- return __write_partial_entries_in_entry_set(sb, es, es->sector,
- es->offset,
- es->num_entries);
-}
-
-void update_dir_checksum_with_entry_set(struct super_block *sb,
- struct entry_set_cache_t *es)
-{
- struct dentry_t *ep;
- u16 chksum = 0;
- s32 chksum_type = CS_DIR_ENTRY, i;
-
- ep = (struct dentry_t *)&es->__buf;
- for (i = 0; i < es->num_entries; i++) {
- pr_debug("%s ep %p\n", __func__, ep);
- chksum = calc_checksum_2byte((void *)ep, DENTRY_SIZE, chksum,
- chksum_type);
- ep++;
- chksum_type = CS_DEFAULT;
- }
-
- ep = (struct dentry_t *)&es->__buf;
- SET16_A(((struct file_dentry_t *)ep)->checksum, chksum);
- write_whole_entry_set(sb, es);
-}
-
-static s32 _walk_fat_chain(struct super_block *sb, struct chain_t *p_dir,
- s32 byte_offset, u32 *clu)
-{
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- s32 clu_offset;
- u32 cur_clu;
-
- clu_offset = byte_offset >> p_fs->cluster_size_bits;
- cur_clu = p_dir->dir;
-
- if (p_dir->flags == 0x03) {
- cur_clu += clu_offset;
- } else {
- while (clu_offset > 0) {
- if (exfat_fat_read(sb, cur_clu, &cur_clu) == -1)
- return -EIO;
- clu_offset--;
- }
- }
-
- if (clu)
- *clu = cur_clu;
- return 0;
-}
-
-static s32 find_location(struct super_block *sb, struct chain_t *p_dir, s32 entry,
- sector_t *sector, s32 *offset)
-{
- s32 off, ret;
- u32 clu = 0;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
-
- off = entry << DENTRY_SIZE_BITS;
-
- if (p_dir->dir == CLUSTER_32(0)) { /* FAT16 root_dir */
- *offset = off & p_bd->sector_size_mask;
- *sector = off >> p_bd->sector_size_bits;
- *sector += p_fs->root_start_sector;
- } else {
- ret = _walk_fat_chain(sb, p_dir, off, &clu);
- if (ret != 0)
- return ret;
-
- /* byte offset in cluster */
- off &= p_fs->cluster_size - 1;
-
- /* byte offset in sector */
- *offset = off & p_bd->sector_size_mask;
-
- /* sector offset in cluster */
- *sector = off >> p_bd->sector_size_bits;
- *sector += START_SECTOR(clu);
- }
- return 0;
-}
-
-struct dentry_t *get_entry_in_dir(struct super_block *sb, struct chain_t *p_dir,
- s32 entry, sector_t *sector)
-{
- s32 off;
- sector_t sec;
- u8 *buf;
-
- if (find_location(sb, p_dir, entry, &sec, &off) != 0)
- return NULL;
-
- buf = exfat_buf_getblk(sb, sec);
-
- if (!buf)
- return NULL;
-
- if (sector)
- *sector = sec;
- return (struct dentry_t *)(buf + off);
-}
-
-/* returns a set of dentries for a file or dir.
- * Note that this is a copy (dump) of dentries so that user should call write_entry_set()
- * 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.
- * out:
- * file_ep: will point the first dentry(= file dentry) on success
- * return:
- * pointer of entry set on success,
- * NULL on failure.
- */
-
-#define ES_MODE_STARTED 0
-#define ES_MODE_GET_FILE_ENTRY 1
-#define ES_MODE_GET_STRM_ENTRY 2
-#define ES_MODE_GET_NAME_ENTRY 3
-#define ES_MODE_GET_CRITICAL_SEC_ENTRY 4
-struct entry_set_cache_t *get_entry_set_in_dir(struct super_block *sb,
- struct chain_t *p_dir, s32 entry,
- u32 type,
- struct dentry_t **file_ep)
-{
- s32 off, ret, byte_offset;
- u32 clu = 0;
- sector_t sec;
- u32 entry_type;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
- struct entry_set_cache_t *es = NULL;
- struct dentry_t *ep, *pos;
- u8 *buf;
- u8 num_entries;
- s32 mode = ES_MODE_STARTED;
- size_t bufsize;
-
- pr_debug("%s entered p_dir dir %u flags %x size %d\n",
- __func__, p_dir->dir, p_dir->flags, p_dir->size);
-
- byte_offset = entry << DENTRY_SIZE_BITS;
- ret = _walk_fat_chain(sb, p_dir, byte_offset, &clu);
- if (ret != 0)
- return NULL;
-
- /* byte offset in cluster */
- byte_offset &= p_fs->cluster_size - 1;
-
- /* byte offset in sector */
- off = byte_offset & p_bd->sector_size_mask;
-
- /* sector offset in cluster */
- sec = byte_offset >> p_bd->sector_size_bits;
- sec += START_SECTOR(clu);
-
- buf = exfat_buf_getblk(sb, sec);
- if (!buf)
- goto err_out;
-
- ep = (struct dentry_t *)(buf + off);
- entry_type = exfat_get_entry_type(ep);
-
- if ((entry_type != TYPE_FILE) && (entry_type != TYPE_DIR))
- goto err_out;
-
- if (type == ES_ALL_ENTRIES)
- num_entries = ((struct file_dentry_t *)ep)->num_ext + 1;
- else
- num_entries = type;
-
- bufsize = offsetof(struct entry_set_cache_t, __buf) + (num_entries) *
- sizeof(struct dentry_t);
- pr_debug("%s: trying to kmalloc %zx bytes for %d entries\n", __func__,
- bufsize, num_entries);
- es = kmalloc(bufsize, GFP_KERNEL);
- if (!es)
- goto err_out;
-
- es->num_entries = num_entries;
- es->sector = sec;
- es->offset = off;
- es->alloc_flag = p_dir->flags;
-
- pos = (struct dentry_t *)&es->__buf;
-
- while (num_entries) {
- /*
- * instead of copying whole sector, we will check every entry.
- * this will provide minimum stablity and consistency.
- */
- entry_type = exfat_get_entry_type(ep);
-
- if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED))
- goto err_out;
-
- switch (mode) {
- case ES_MODE_STARTED:
- if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR))
- mode = ES_MODE_GET_FILE_ENTRY;
- else
- goto err_out;
- break;
- case ES_MODE_GET_FILE_ENTRY:
- if (entry_type == TYPE_STREAM)
- mode = ES_MODE_GET_STRM_ENTRY;
- else
- goto err_out;
- break;
- case ES_MODE_GET_STRM_ENTRY:
- if (entry_type == TYPE_EXTEND)
- mode = ES_MODE_GET_NAME_ENTRY;
- else
- goto err_out;
- break;
- case ES_MODE_GET_NAME_ENTRY:
- if (entry_type == TYPE_EXTEND)
- break;
- else if (entry_type == TYPE_STREAM)
- goto err_out;
- else if (entry_type & TYPE_CRITICAL_SEC)
- mode = ES_MODE_GET_CRITICAL_SEC_ENTRY;
- else
- goto err_out;
- break;
- case ES_MODE_GET_CRITICAL_SEC_ENTRY:
- if ((entry_type == TYPE_EXTEND) ||
- (entry_type == TYPE_STREAM))
- goto err_out;
- else if ((entry_type & TYPE_CRITICAL_SEC) !=
- TYPE_CRITICAL_SEC)
- goto err_out;
- break;
- }
-
- memcpy(pos, ep, sizeof(struct dentry_t));
-
- if (--num_entries == 0)
- break;
-
- if (((off + DENTRY_SIZE) & p_bd->sector_size_mask) <
- (off & p_bd->sector_size_mask)) {
- /* get the next sector */
- if (IS_LAST_SECTOR_IN_CLUSTER(sec)) {
- if (es->alloc_flag == 0x03) {
- clu++;
- } else {
- if (exfat_fat_read(sb, clu, &clu) == -1)
- goto err_out;
- }
- sec = START_SECTOR(clu);
- } else {
- sec++;
- }
- buf = exfat_buf_getblk(sb, sec);
- if (!buf)
- goto err_out;
- off = 0;
- ep = (struct dentry_t *)(buf);
- } else {
- ep++;
- off += DENTRY_SIZE;
- }
- pos++;
- }
-
- if (file_ep)
- *file_ep = (struct dentry_t *)&es->__buf;
-
- pr_debug("%s exiting es %p sec %llu offset %d flags %d, num_entries %u buf ptr %p\n",
- __func__, es, (unsigned long long)es->sector, es->offset,
- es->alloc_flag, es->num_entries, &es->__buf);
- return es;
-err_out:
- pr_debug("%s exited NULL (es %p)\n", __func__, es);
- kfree(es);
- return NULL;
-}
-
-void release_entry_set(struct entry_set_cache_t *es)
-{
- pr_debug("%s es=%p\n", __func__, es);
- kfree(es);
-}
-
-/* search EMPTY CONTINUOUS "num_entries" entries */
-static s32 search_deleted_or_unused_entry(struct super_block *sb,
- struct chain_t *p_dir,
- s32 num_entries)
-{
- int i, dentry, num_empty = 0;
- s32 dentries_per_clu;
- u32 type;
- struct chain_t clu;
- struct dentry_t *ep;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
- dentries_per_clu = p_fs->dentries_in_root;
- else
- dentries_per_clu = p_fs->dentries_per_clu;
-
- if (p_fs->hint_uentry.dir == p_dir->dir) {
- if (p_fs->hint_uentry.entry == -1)
- return -1;
-
- clu.dir = p_fs->hint_uentry.clu.dir;
- clu.size = p_fs->hint_uentry.clu.size;
- clu.flags = p_fs->hint_uentry.clu.flags;
-
- dentry = p_fs->hint_uentry.entry;
- } else {
- p_fs->hint_uentry.entry = -1;
-
- clu.dir = p_dir->dir;
- clu.size = p_dir->size;
- clu.flags = p_dir->flags;
-
- dentry = 0;
- }
-
- while (clu.dir != CLUSTER_32(~0)) {
- if (p_fs->dev_ejected)
- break;
-
- if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
- i = dentry % dentries_per_clu;
- else
- i = dentry & (dentries_per_clu - 1);
-
- for (; i < dentries_per_clu; i++, dentry++) {
- ep = get_entry_in_dir(sb, &clu, i, NULL);
- if (!ep)
- return -1;
-
- type = exfat_get_entry_type(ep);
-
- if (type == TYPE_UNUSED) {
- num_empty++;
- if (p_fs->hint_uentry.entry == -1) {
- p_fs->hint_uentry.dir = p_dir->dir;
- p_fs->hint_uentry.entry = dentry;
-
- p_fs->hint_uentry.clu.dir = clu.dir;
- p_fs->hint_uentry.clu.size = clu.size;
- p_fs->hint_uentry.clu.flags = clu.flags;
- }
- } else if (type == TYPE_DELETED) {
- num_empty++;
- } else {
- num_empty = 0;
- }
-
- if (num_empty >= num_entries) {
- p_fs->hint_uentry.dir = CLUSTER_32(~0);
- p_fs->hint_uentry.entry = -1;
-
- if (p_fs->vol_type == EXFAT)
- return dentry - (num_entries - 1);
- else
- return dentry;
- }
- }
-
- if (p_dir->dir == CLUSTER_32(0))
- break; /* FAT16 root_dir */
-
- if (clu.flags == 0x03) {
- if ((--clu.size) > 0)
- clu.dir++;
- else
- clu.dir = CLUSTER_32(~0);
- } else {
- if (exfat_fat_read(sb, clu.dir, &clu.dir) != 0)
- return -1;
- }
- }
-
- return -1;
-}
-
-static s32 find_empty_entry(struct inode *inode, struct chain_t *p_dir, s32 num_entries)
-{
- s32 ret, dentry;
- u32 last_clu;
- sector_t sector;
- u64 size = 0;
- struct chain_t clu;
- struct dentry_t *ep = NULL;
- struct super_block *sb = inode->i_sb;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct file_id_t *fid = &(EXFAT_I(inode)->fid);
-
- if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
- return search_deleted_or_unused_entry(sb, p_dir, num_entries);
-
- while ((dentry = search_deleted_or_unused_entry(sb, p_dir, num_entries)) < 0) {
- if (p_fs->dev_ejected)
- break;
-
- if (p_dir->dir != p_fs->root_dir)
- size = i_size_read(inode);
-
- last_clu = find_last_cluster(sb, p_dir);
- clu.dir = last_clu + 1;
- clu.size = 0;
- clu.flags = p_dir->flags;
-
- /* (1) allocate a cluster */
- ret = exfat_alloc_cluster(sb, 1, &clu);
- if (ret < 1)
- return -EIO;
-
- if (clear_cluster(sb, clu.dir) != 0)
- return -EIO;
-
- /* (2) append to the FAT chain */
- if (clu.flags != p_dir->flags) {
- exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size);
- p_dir->flags = 0x01;
- p_fs->hint_uentry.clu.flags = 0x01;
- }
- if (clu.flags == 0x01)
- if (exfat_fat_write(sb, last_clu, clu.dir) < 0)
- return -EIO;
-
- if (p_fs->hint_uentry.entry == -1) {
- p_fs->hint_uentry.dir = p_dir->dir;
- p_fs->hint_uentry.entry = p_dir->size << (p_fs->cluster_size_bits - DENTRY_SIZE_BITS);
-
- p_fs->hint_uentry.clu.dir = clu.dir;
- p_fs->hint_uentry.clu.size = 0;
- p_fs->hint_uentry.clu.flags = clu.flags;
- }
- p_fs->hint_uentry.clu.size++;
- p_dir->size++;
-
- /* (3) update the directory entry */
- if (p_dir->dir != p_fs->root_dir) {
- size += p_fs->cluster_size;
-
- ep = get_entry_in_dir(sb, &fid->dir,
- fid->entry + 1, &sector);
- if (!ep)
- return -ENOENT;
- exfat_set_entry_size(ep, size);
- exfat_set_entry_flag(ep, p_dir->flags);
- exfat_buf_modify(sb, sector);
-
- update_dir_checksum(sb, &fid->dir,
- fid->entry);
- }
-
- i_size_write(inode, i_size_read(inode) + p_fs->cluster_size);
- EXFAT_I(inode)->mmu_private += p_fs->cluster_size;
- EXFAT_I(inode)->fid.size += p_fs->cluster_size;
- EXFAT_I(inode)->fid.flags = p_dir->flags;
- inode->i_blocks += 1 << (p_fs->cluster_size_bits - 9);
- }
-
- return dentry;
-}
-
-static s32 extract_uni_name_from_name_entry(struct name_dentry_t *ep, u16 *uniname,
- s32 order)
-{
- int i, len = 0;
-
- for (i = 0; i < 30; i += 2) {
- *uniname = GET16_A(ep->unicode_0_14 + i);
- if (*uniname == 0x0)
- return len;
- uniname++;
- len++;
- }
-
- *uniname = 0x0;
- return len;
-}
-
-/* return values of exfat_find_dir_entry()
- * >= 0 : return dir entiry position with the name in dir
- * -1 : (root dir, ".") it is the root dir itself
- * -2 : entry with the name does not exist
- */
-s32 exfat_find_dir_entry(struct super_block *sb, struct chain_t *p_dir,
- struct uni_name_t *p_uniname, s32 num_entries,
- struct dos_name_t *p_dosname, u32 type)
-{
- int i = 0, dentry = 0, num_ext_entries = 0, len, step;
- s32 order = 0;
- bool is_feasible_entry = false;
- s32 dentries_per_clu, num_empty = 0;
- u32 entry_type;
- u16 entry_uniname[16], *uniname = NULL, unichar;
- struct chain_t clu;
- struct dentry_t *ep;
- struct file_dentry_t *file_ep;
- struct strm_dentry_t *strm_ep;
- struct name_dentry_t *name_ep;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- if (p_dir->dir == p_fs->root_dir) {
- if ((!nls_uniname_cmp(sb, p_uniname->name,
- (u16 *)UNI_CUR_DIR_NAME)) ||
- (!nls_uniname_cmp(sb, p_uniname->name,
- (u16 *)UNI_PAR_DIR_NAME)))
- return -1; // special case, root directory itself
- }
-
- if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
- dentries_per_clu = p_fs->dentries_in_root;
- else
- dentries_per_clu = p_fs->dentries_per_clu;
-
- clu.dir = p_dir->dir;
- clu.size = p_dir->size;
- clu.flags = p_dir->flags;
-
- p_fs->hint_uentry.dir = p_dir->dir;
- p_fs->hint_uentry.entry = -1;
-
- while (clu.dir != CLUSTER_32(~0)) {
- if (p_fs->dev_ejected)
- break;
-
- while (i < dentries_per_clu) {
- ep = get_entry_in_dir(sb, &clu, i, NULL);
- if (!ep)
- return -2;
-
- entry_type = exfat_get_entry_type(ep);
- step = 1;
-
- if ((entry_type == TYPE_UNUSED) || (entry_type == TYPE_DELETED)) {
- is_feasible_entry = false;
-
- if (p_fs->hint_uentry.entry == -1) {
- num_empty++;
-
- if (num_empty == 1) {
- p_fs->hint_uentry.clu.dir = clu.dir;
- p_fs->hint_uentry.clu.size = clu.size;
- p_fs->hint_uentry.clu.flags = clu.flags;
- }
- if ((num_empty >= num_entries) || (entry_type == TYPE_UNUSED))
- p_fs->hint_uentry.entry = dentry - (num_empty - 1);
- }
-
- if (entry_type == TYPE_UNUSED)
- return -2;
- } else {
- num_empty = 0;
-
- if ((entry_type == TYPE_FILE) || (entry_type == TYPE_DIR)) {
- file_ep = (struct file_dentry_t *)ep;
- if ((type == TYPE_ALL) || (type == entry_type)) {
- num_ext_entries = file_ep->num_ext;
- is_feasible_entry = true;
- } else {
- is_feasible_entry = false;
- step = file_ep->num_ext + 1;
- }
- } else if (entry_type == TYPE_STREAM) {
- if (is_feasible_entry) {
- strm_ep = (struct strm_dentry_t *)ep;
- if (p_uniname->name_hash == GET16_A(strm_ep->name_hash) &&
- p_uniname->name_len == strm_ep->name_len) {
- order = 1;
- } else {
- is_feasible_entry = false;
- step = num_ext_entries;
- }
- }
- } else if (entry_type == TYPE_EXTEND) {
- if (is_feasible_entry) {
- name_ep = (struct name_dentry_t *)ep;
-
- if ((++order) == 2)
- uniname = p_uniname->name;
- else
- uniname += 15;
-
- len = extract_uni_name_from_name_entry(name_ep,
- entry_uniname, order);
-
- unichar = *(uniname + len);
- *(uniname + len) = 0x0;
-
- if (nls_uniname_cmp(sb, uniname, entry_uniname)) {
- is_feasible_entry = false;
- step = num_ext_entries - order + 1;
- } else if (order == num_ext_entries) {
- p_fs->hint_uentry.dir = CLUSTER_32(~0);
- p_fs->hint_uentry.entry = -1;
- return dentry - (num_ext_entries);
- }
-
- *(uniname + len) = unichar;
- }
- } else {
- is_feasible_entry = false;
- }
- }
-
- i += step;
- dentry += step;
- }
-
- i -= dentries_per_clu;
-
- if (p_dir->dir == CLUSTER_32(0))
- break; /* FAT16 root_dir */
-
- if (clu.flags == 0x03) {
- if ((--clu.size) > 0)
- clu.dir++;
- else
- clu.dir = CLUSTER_32(~0);
- } else {
- if (exfat_fat_read(sb, clu.dir, &clu.dir) != 0)
- return -2;
- }
- }
-
- return -2;
-}
-
-s32 exfat_count_ext_entries(struct super_block *sb, struct chain_t *p_dir,
- s32 entry, struct dentry_t *p_entry)
-{
- int i, count = 0;
- u32 type;
- struct file_dentry_t *file_ep = (struct file_dentry_t *)p_entry;
- struct dentry_t *ext_ep;
-
- for (i = 0, entry++; i < file_ep->num_ext; i++, entry++) {
- ext_ep = get_entry_in_dir(sb, p_dir, entry, NULL);
- if (!ext_ep)
- return -1;
-
- type = exfat_get_entry_type(ext_ep);
- if ((type == TYPE_EXTEND) || (type == TYPE_STREAM))
- count++;
- else
- return count;
- }
-
- return count;
-}
-
-s32 count_dos_name_entries(struct super_block *sb, struct chain_t *p_dir,
- u32 type)
-{
- int i, count = 0;
- s32 dentries_per_clu;
- u32 entry_type;
- struct chain_t clu;
- struct dentry_t *ep;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
- dentries_per_clu = p_fs->dentries_in_root;
- else
- dentries_per_clu = p_fs->dentries_per_clu;
-
- clu.dir = p_dir->dir;
- clu.size = p_dir->size;
- clu.flags = p_dir->flags;
-
- while (clu.dir != CLUSTER_32(~0)) {
- if (p_fs->dev_ejected)
- break;
-
- for (i = 0; i < dentries_per_clu; i++) {
- ep = get_entry_in_dir(sb, &clu, i, NULL);
- if (!ep)
- return -ENOENT;
-
- entry_type = exfat_get_entry_type(ep);
-
- if (entry_type == TYPE_UNUSED)
- return count;
- if (!(type & TYPE_CRITICAL_PRI) &&
- !(type & TYPE_BENIGN_PRI))
- continue;
-
- if ((type == TYPE_ALL) || (type == entry_type))
- count++;
- }
-
- if (p_dir->dir == CLUSTER_32(0))
- break; /* FAT16 root_dir */
-
- if (clu.flags == 0x03) {
- if ((--clu.size) > 0)
- clu.dir++;
- else
- clu.dir = CLUSTER_32(~0);
- } else {
- if (exfat_fat_read(sb, clu.dir, &clu.dir) != 0)
- return -EIO;
- }
- }
-
- return count;
-}
-
-bool is_dir_empty(struct super_block *sb, struct chain_t *p_dir)
-{
- int i, count = 0;
- s32 dentries_per_clu;
- u32 type;
- struct chain_t clu;
- struct dentry_t *ep;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
- dentries_per_clu = p_fs->dentries_in_root;
- else
- dentries_per_clu = p_fs->dentries_per_clu;
-
- clu.dir = p_dir->dir;
- clu.size = p_dir->size;
- clu.flags = p_dir->flags;
-
- while (clu.dir != CLUSTER_32(~0)) {
- if (p_fs->dev_ejected)
- break;
-
- for (i = 0; i < dentries_per_clu; i++) {
- ep = get_entry_in_dir(sb, &clu, i, NULL);
- if (!ep)
- break;
-
- type = exfat_get_entry_type(ep);
-
- if (type == TYPE_UNUSED)
- return true;
- if ((type != TYPE_FILE) && (type != TYPE_DIR))
- continue;
-
- if (p_dir->dir == CLUSTER_32(0)) /* FAT16 root_dir */
- return false;
-
- if (p_fs->vol_type == EXFAT)
- return false;
- if ((p_dir->dir == p_fs->root_dir) || ((++count) > 2))
- return false;
- }
-
- if (p_dir->dir == CLUSTER_32(0))
- break; /* FAT16 root_dir */
-
- if (clu.flags == 0x03) {
- if ((--clu.size) > 0)
- clu.dir++;
- else
- clu.dir = CLUSTER_32(~0);
- }
- if (exfat_fat_read(sb, clu.dir, &clu.dir) != 0)
- break;
- }
-
- return true;
-}
-
-/*
- * Name Conversion Functions
- */
-
-/* input : dir, uni_name
- * output : num_of_entry, dos_name(format : aaaaaa~1.bbb)
- */
-s32 get_num_entries_and_dos_name(struct super_block *sb, struct chain_t *p_dir,
- struct uni_name_t *p_uniname, s32 *entries,
- struct dos_name_t *p_dosname)
-{
- s32 num_entries;
-
- num_entries = exfat_calc_num_entries(p_uniname);
- if (num_entries == 0)
- return -EINVAL;
-
- *entries = num_entries;
-
- return 0;
-}
-
-void exfat_get_uni_name_from_ext_entry(struct super_block *sb,
- struct chain_t *p_dir, s32 entry,
- u16 *uniname)
-{
- int i;
- struct dentry_t *ep;
- struct entry_set_cache_t *es;
-
- es = get_entry_set_in_dir(sb, p_dir, entry, ES_ALL_ENTRIES, &ep);
- if (!es || es->num_entries < 3) {
- if (es)
- release_entry_set(es);
- return;
- }
-
- ep += 2;
-
- /*
- * 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++, ep++) {
- if (exfat_get_entry_type(ep) == TYPE_EXTEND)
- extract_uni_name_from_name_entry((struct name_dentry_t *)
- ep, uniname, i);
- else
- goto out;
- uniname += 15;
- }
-
-out:
- release_entry_set(es);
-}
-
-s32 exfat_calc_num_entries(struct uni_name_t *p_uniname)
-{
- s32 len;
-
- len = p_uniname->name_len;
- if (len == 0)
- return 0;
-
- /* 1 file entry + 1 stream entry + name entries */
- return (len - 1) / 15 + 3;
-}
-
-u16 calc_checksum_2byte(void *data, s32 len, u16 chksum, s32 type)
-{
- int i;
- u8 *c = (u8 *)data;
-
- switch (type) {
- case CS_DIR_ENTRY:
- for (i = 0; i < len; i++, c++) {
- if ((i == 2) || (i == 3))
- continue;
- chksum = (((chksum & 1) << 15) |
- ((chksum & 0xFFFE) >> 1)) + (u16)*c;
- }
- break;
- default
- :
- for (i = 0; i < len; i++, c++)
- chksum = (((chksum & 1) << 15) |
- ((chksum & 0xFFFE) >> 1)) + (u16)*c;
- }
-
- return chksum;
-}
-
-/*
- * Name Resolution Functions
- */
-
-/* return values of resolve_path()
- * > 0 : return the length of the path
- * < 0 : return error
- */
-s32 resolve_path(struct inode *inode, char *path, struct chain_t *p_dir,
- struct uni_name_t *p_uniname)
-{
- bool lossy = false;
- struct super_block *sb = inode->i_sb;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct file_id_t *fid = &(EXFAT_I(inode)->fid);
-
- if (strscpy(name_buf, path, sizeof(name_buf)) < 0)
- return -EINVAL;
-
- nls_cstring_to_uniname(sb, p_uniname, name_buf, &lossy);
- if (lossy)
- return -EINVAL;
-
- fid->size = i_size_read(inode);
-
- p_dir->dir = fid->start_clu;
- p_dir->size = (s32)(fid->size >> p_fs->cluster_size_bits);
- p_dir->flags = fid->flags;
-
- return 0;
-}
-
-s32 exfat_mount(struct super_block *sb, struct pbr_sector_t *p_pbr)
-{
- struct bpbex_t *p_bpb = (struct bpbex_t *)p_pbr->bpb;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
- struct bd_info_t *p_bd = &(EXFAT_SB(sb)->bd_info);
-
- if (p_bpb->num_fats == 0)
- return -EFSCORRUPTED;
-
- p_fs->sectors_per_clu = 1 << p_bpb->sectors_per_clu_bits;
- p_fs->sectors_per_clu_bits = p_bpb->sectors_per_clu_bits;
- p_fs->cluster_size_bits = p_fs->sectors_per_clu_bits +
- p_bd->sector_size_bits;
- p_fs->cluster_size = 1 << p_fs->cluster_size_bits;
-
- p_fs->num_FAT_sectors = GET32(p_bpb->fat_length);
-
- p_fs->FAT1_start_sector = p_fs->PBR_sector + GET32(p_bpb->fat_offset);
- if (p_bpb->num_fats == 1)
- p_fs->FAT2_start_sector = p_fs->FAT1_start_sector;
- else
- p_fs->FAT2_start_sector = p_fs->FAT1_start_sector +
- p_fs->num_FAT_sectors;
-
- p_fs->root_start_sector = p_fs->PBR_sector + GET32(p_bpb->clu_offset);
- p_fs->data_start_sector = p_fs->root_start_sector;
-
- p_fs->num_sectors = GET64(p_bpb->vol_length);
- p_fs->num_clusters = GET32(p_bpb->clu_count) + 2;
- /* because the cluster index starts with 2 */
-
- p_fs->vol_type = EXFAT;
- p_fs->vol_id = GET32(p_bpb->vol_serial);
-
- p_fs->root_dir = GET32(p_bpb->root_cluster);
- p_fs->dentries_in_root = 0;
- p_fs->dentries_per_clu = 1 << (p_fs->cluster_size_bits -
- DENTRY_SIZE_BITS);
-
- p_fs->vol_flag = (u32)GET16(p_bpb->vol_flags);
- p_fs->clu_srch_ptr = 2;
- p_fs->used_clusters = UINT_MAX;
-
- return 0;
-}
-
-s32 create_dir(struct inode *inode, struct chain_t *p_dir,
- struct uni_name_t *p_uniname, struct file_id_t *fid)
-{
- s32 ret, dentry, num_entries;
- u64 size;
- struct chain_t clu;
- struct dos_name_t dos_name;
- struct super_block *sb = inode->i_sb;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries,
- &dos_name);
- if (ret)
- return ret;
-
- /* find_empty_entry must be called before alloc_cluster */
- dentry = find_empty_entry(inode, p_dir, num_entries);
- if (dentry < 0)
- return -ENOSPC;
-
- clu.dir = CLUSTER_32(~0);
- clu.size = 0;
- clu.flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01;
-
- /* (1) allocate a cluster */
- ret = exfat_alloc_cluster(sb, 1, &clu);
- if (ret < 0)
- return ret;
- else if (ret == 0)
- return -ENOSPC;
-
- ret = clear_cluster(sb, clu.dir);
- if (ret != 0)
- return ret;
-
- size = p_fs->cluster_size;
-
- /* (2) update the directory entry */
- /* make sub-dir entry in parent directory */
- ret = exfat_init_dir_entry(sb, p_dir, dentry, TYPE_DIR, clu.dir,
- size);
- if (ret != 0)
- return ret;
-
- ret = exfat_init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname,
- &dos_name);
- if (ret != 0)
- return ret;
-
- fid->dir.dir = p_dir->dir;
- fid->dir.size = p_dir->size;
- fid->dir.flags = p_dir->flags;
- fid->entry = dentry;
-
- fid->attr = ATTR_SUBDIR;
- fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01;
- fid->size = size;
- fid->start_clu = clu.dir;
-
- fid->type = TYPE_DIR;
- fid->rwoffset = 0;
- fid->hint_last_off = -1;
-
- return 0;
-}
-
-s32 create_file(struct inode *inode, struct chain_t *p_dir,
- struct uni_name_t *p_uniname, u8 mode, struct file_id_t *fid)
-{
- s32 ret, dentry, num_entries;
- struct dos_name_t dos_name;
- struct super_block *sb = inode->i_sb;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname, &num_entries,
- &dos_name);
- if (ret)
- return ret;
-
- /* find_empty_entry must be called before alloc_cluster() */
- dentry = find_empty_entry(inode, p_dir, num_entries);
- if (dentry < 0)
- return -ENOSPC;
-
- /* (1) 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(sb, p_dir, dentry, TYPE_FILE | mode,
- CLUSTER_32(0), 0);
- if (ret != 0)
- return ret;
-
- ret = exfat_init_ext_entry(sb, p_dir, dentry, num_entries, p_uniname,
- &dos_name);
- if (ret != 0)
- return ret;
-
- fid->dir.dir = p_dir->dir;
- fid->dir.size = p_dir->size;
- fid->dir.flags = p_dir->flags;
- fid->entry = dentry;
-
- fid->attr = ATTR_ARCHIVE | mode;
- fid->flags = (p_fs->vol_type == EXFAT) ? 0x03 : 0x01;
- fid->size = 0;
- fid->start_clu = CLUSTER_32(~0);
-
- fid->type = TYPE_FILE;
- fid->rwoffset = 0;
- fid->hint_last_off = -1;
-
- return 0;
-}
-
-void remove_file(struct inode *inode, struct chain_t *p_dir, s32 entry)
-{
- s32 num_entries;
- sector_t sector;
- struct dentry_t *ep;
- struct super_block *sb = inode->i_sb;
-
- ep = get_entry_in_dir(sb, p_dir, entry, &sector);
- if (!ep)
- return;
-
- exfat_buf_lock(sb, sector);
-
- /* exfat_buf_lock() before call count_ext_entries() */
- num_entries = exfat_count_ext_entries(sb, p_dir, entry, ep);
- if (num_entries < 0) {
- exfat_buf_unlock(sb, sector);
- return;
- }
- num_entries++;
-
- exfat_buf_unlock(sb, sector);
-
- /* (1) update the directory entry */
- exfat_delete_dir_entry(sb, p_dir, entry, 0, num_entries);
-}
-
-s32 exfat_rename_file(struct inode *inode, struct chain_t *p_dir, s32 oldentry,
- struct uni_name_t *p_uniname, struct file_id_t *fid)
-{
- s32 ret, newentry = -1, num_old_entries, num_new_entries;
- sector_t sector_old, sector_new;
- struct dos_name_t dos_name;
- struct dentry_t *epold, *epnew;
- struct super_block *sb = inode->i_sb;
-
- epold = get_entry_in_dir(sb, p_dir, oldentry, &sector_old);
- if (!epold)
- return -ENOENT;
-
- exfat_buf_lock(sb, sector_old);
-
- /* exfat_buf_lock() before call count_ext_entries() */
- num_old_entries = exfat_count_ext_entries(sb, p_dir, oldentry,
- epold);
- if (num_old_entries < 0) {
- exfat_buf_unlock(sb, sector_old);
- return -ENOENT;
- }
- num_old_entries++;
-
- ret = get_num_entries_and_dos_name(sb, p_dir, p_uniname,
- &num_new_entries, &dos_name);
- if (ret) {
- exfat_buf_unlock(sb, sector_old);
- return ret;
- }
-
- if (num_old_entries < num_new_entries) {
- newentry = find_empty_entry(inode, p_dir, num_new_entries);
- if (newentry < 0) {
- exfat_buf_unlock(sb, sector_old);
- return -ENOSPC;
- }
-
- epnew = get_entry_in_dir(sb, p_dir, newentry, &sector_new);
- if (!epnew) {
- exfat_buf_unlock(sb, sector_old);
- return -ENOENT;
- }
-
- memcpy((void *)epnew, (void *)epold, DENTRY_SIZE);
- if (exfat_get_entry_type(epnew) == TYPE_FILE) {
- exfat_set_entry_attr(epnew,
- exfat_get_entry_attr(epnew) |
- ATTR_ARCHIVE);
- fid->attr |= ATTR_ARCHIVE;
- }
- exfat_buf_modify(sb, sector_new);
- exfat_buf_unlock(sb, sector_old);
-
- epold = get_entry_in_dir(sb, p_dir, oldentry + 1,
- &sector_old);
- exfat_buf_lock(sb, sector_old);
- epnew = get_entry_in_dir(sb, p_dir, newentry + 1,
- &sector_new);
-
- if (!epold || !epnew) {
- exfat_buf_unlock(sb, sector_old);
- return -ENOENT;
- }
-
- memcpy((void *)epnew, (void *)epold, DENTRY_SIZE);
- exfat_buf_modify(sb, sector_new);
- exfat_buf_unlock(sb, sector_old);
-
- ret = exfat_init_ext_entry(sb, p_dir, newentry,
- num_new_entries, p_uniname,
- &dos_name);
- if (ret != 0)
- return ret;
-
- exfat_delete_dir_entry(sb, p_dir, oldentry, 0,
- num_old_entries);
- fid->entry = newentry;
- } else {
- if (exfat_get_entry_type(epold) == TYPE_FILE) {
- exfat_set_entry_attr(epold,
- exfat_get_entry_attr(epold) |
- ATTR_ARCHIVE);
- fid->attr |= ATTR_ARCHIVE;
- }
- exfat_buf_modify(sb, sector_old);
- exfat_buf_unlock(sb, sector_old);
-
- ret = exfat_init_ext_entry(sb, p_dir, oldentry,
- num_new_entries, p_uniname,
- &dos_name);
- if (ret != 0)
- return ret;
-
- exfat_delete_dir_entry(sb, p_dir, oldentry, num_new_entries,
- num_old_entries);
- }
-
- return 0;
-}
-
-s32 move_file(struct inode *inode, struct chain_t *p_olddir, s32 oldentry,
- struct chain_t *p_newdir, struct uni_name_t *p_uniname,
- struct file_id_t *fid)
-{
- s32 ret, newentry, num_new_entries, num_old_entries;
- sector_t sector_mov, sector_new;
- struct dos_name_t dos_name;
- struct dentry_t *epmov, *epnew;
- struct super_block *sb = inode->i_sb;
-
- epmov = get_entry_in_dir(sb, p_olddir, oldentry, &sector_mov);
- if (!epmov)
- return -ENOENT;
-
- /* check if the source and target directory is the same */
- if (exfat_get_entry_type(epmov) == TYPE_DIR &&
- exfat_get_entry_clu0(epmov) == p_newdir->dir)
- return -EINVAL;
-
- exfat_buf_lock(sb, sector_mov);
-
- /* exfat_buf_lock() before call count_ext_entries() */
- num_old_entries = exfat_count_ext_entries(sb, p_olddir, oldentry,
- epmov);
- if (num_old_entries < 0) {
- exfat_buf_unlock(sb, sector_mov);
- return -ENOENT;
- }
- num_old_entries++;
-
- ret = get_num_entries_and_dos_name(sb, p_newdir, p_uniname,
- &num_new_entries, &dos_name);
- if (ret) {
- exfat_buf_unlock(sb, sector_mov);
- return ret;
- }
-
- newentry = find_empty_entry(inode, p_newdir, num_new_entries);
- if (newentry < 0) {
- exfat_buf_unlock(sb, sector_mov);
- return -ENOSPC;
- }
-
- epnew = get_entry_in_dir(sb, p_newdir, newentry, &sector_new);
- if (!epnew) {
- exfat_buf_unlock(sb, sector_mov);
- return -ENOENT;
- }
-
- memcpy((void *)epnew, (void *)epmov, DENTRY_SIZE);
- if (exfat_get_entry_type(epnew) == TYPE_FILE) {
- exfat_set_entry_attr(epnew, exfat_get_entry_attr(epnew) |
- ATTR_ARCHIVE);
- fid->attr |= ATTR_ARCHIVE;
- }
- exfat_buf_modify(sb, sector_new);
- exfat_buf_unlock(sb, sector_mov);
-
- epmov = get_entry_in_dir(sb, p_olddir, oldentry + 1,
- &sector_mov);
- exfat_buf_lock(sb, sector_mov);
- epnew = get_entry_in_dir(sb, p_newdir, newentry + 1,
- &sector_new);
- if (!epmov || !epnew) {
- exfat_buf_unlock(sb, sector_mov);
- return -ENOENT;
- }
-
- memcpy((void *)epnew, (void *)epmov, DENTRY_SIZE);
- exfat_buf_modify(sb, sector_new);
- exfat_buf_unlock(sb, sector_mov);
-
- ret = exfat_init_ext_entry(sb, p_newdir, newentry, num_new_entries,
- p_uniname, &dos_name);
- if (ret != 0)
- return ret;
-
- exfat_delete_dir_entry(sb, p_olddir, oldentry, 0, num_old_entries);
-
- fid->dir.dir = p_newdir->dir;
- fid->dir.size = p_newdir->size;
- fid->dir.flags = p_newdir->flags;
-
- fid->entry = newentry;
-
- return 0;
-}
-
-/*
- * Sector Read/Write Functions
- */
-
-int sector_read(struct super_block *sb, sector_t sec, struct buffer_head **bh,
- bool read)
-{
- s32 ret = -EIO;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- if ((sec >= (p_fs->PBR_sector + p_fs->num_sectors)) &&
- (p_fs->num_sectors > 0)) {
- pr_err("[EXFAT] %s: out of range error! (sec = %llu)\n",
- __func__, (unsigned long long)sec);
- fs_error(sb);
- return ret;
- }
-
- if (!p_fs->dev_ejected) {
- ret = exfat_bdev_read(sb, sec, bh, 1, read);
- if (ret != 0)
- p_fs->dev_ejected = 1;
- }
-
- return ret;
-}
-
-int sector_write(struct super_block *sb, sector_t sec, struct buffer_head *bh,
- bool sync)
-{
- s32 ret = -EIO;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- if (sec >= (p_fs->PBR_sector + p_fs->num_sectors) &&
- (p_fs->num_sectors > 0)) {
- pr_err("[EXFAT] %s: out of range error! (sec = %llu)\n",
- __func__, (unsigned long long)sec);
- fs_error(sb);
- return ret;
- }
-
- if (!bh) {
- pr_err("[EXFAT] %s: bh is NULL!\n", __func__);
- fs_error(sb);
- return ret;
- }
-
- if (!p_fs->dev_ejected) {
- ret = exfat_bdev_write(sb, sec, bh, 1, sync);
- if (ret != 0)
- p_fs->dev_ejected = 1;
- }
-
- return ret;
-}
-
-int multi_sector_read(struct super_block *sb, sector_t sec,
- struct buffer_head **bh, s32 num_secs, bool read)
-{
- s32 ret = -EIO;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- if (((sec + num_secs) > (p_fs->PBR_sector + p_fs->num_sectors)) &&
- (p_fs->num_sectors > 0)) {
- pr_err("[EXFAT] %s: out of range error! (sec = %llu, num_secs = %d)\n",
- __func__, (unsigned long long)sec, num_secs);
- fs_error(sb);
- return ret;
- }
-
- if (!p_fs->dev_ejected) {
- ret = exfat_bdev_read(sb, sec, bh, num_secs, read);
- if (ret != 0)
- p_fs->dev_ejected = 1;
- }
-
- return ret;
-}
-
-int multi_sector_write(struct super_block *sb, sector_t sec,
- struct buffer_head *bh, s32 num_secs, bool sync)
-{
- s32 ret = -EIO;
- struct fs_info_t *p_fs = &(EXFAT_SB(sb)->fs_info);
-
- if ((sec + num_secs) > (p_fs->PBR_sector + p_fs->num_sectors) &&
- (p_fs->num_sectors > 0)) {
- pr_err("[EXFAT] %s: out of range error! (sec = %llu, num_secs = %d)\n",
- __func__, (unsigned long long)sec, num_secs);
- fs_error(sb);
- return ret;
- }
- if (!bh) {
- pr_err("[EXFAT] %s: bh is NULL!\n", __func__);
- fs_error(sb);
- return ret;
- }
-
- if (!p_fs->dev_ejected) {
- ret = exfat_bdev_write(sb, sec, bh, num_secs, sync);
- if (ret != 0)
- p_fs->dev_ejected = 1;
- }
-
- return ret;
-}